Improve song parsing for Journey FM and Spirit FM, fix app startup issues
This commit is contained in:
1 parent
59689dcbf6
commit
bf7aa42460
5 files changed
+188
-71
No files matched your search
@@ -18,3 +18,126 @@ https://www.amazon.com/s?k=Have%20%20For%20%20You%20Colton%20%20Dixon%20The%20%2
|
||||
Done Tasha Layton - Look What You've
|
||||
https://www.amazon.com/s?k=Done%20Tasha%20%20Layton%20Look%20%20What%20%20You%27ve%20&i=digital-music
|
||||
|
||||
Church Sing Tauren Wells - Let The
|
||||
https://www.amazon.com/s?k=Church%20%20Sing%20Tauren%20%20Wells%20Let%20%20The%20&i=digital-music
|
||||
|
||||
You Danny Gokey - I Got
|
||||
https://www.amazon.com/s?k=You%20Danny%20%20Gokey%20I%20%20Got%20&i=digital-music
|
||||
|
||||
Andrew Ripp - Breakdown
|
||||
https://www.amazon.com/s?k=Andrew%20%20Ripp%20Breakdown&i=digital-music
|
||||
|
||||
You've Done Tasha Layton - Look What
|
||||
https://www.amazon.com/s?k=You%27ve%20%20Done%20Tasha%20%20Layton%20Look%20%20What%20&i=digital-music
|
||||
|
||||
Crowder - Still
|
||||
https://www.amazon.com/s?k=Crowder%20Still&i=digital-music
|
||||
|
||||
Thing About Praise Benjamin William Hastings - That's The
|
||||
https://www.amazon.com/s?k=Thing%20%20About%20%20Praise%20Benjamin%20%20William%20%20Hastings%20That%27s%20%20The%20&i=digital-music
|
||||
|
||||
Love Feat. Bear Rinehar We The Kingdom - Child Of
|
||||
https://www.amazon.com/s?k=Love%20%20Feat.%20%20Bear%20%20Rinehar%20We%20%20The%20%20Kingdom%20Child%20%20Of%20&i=digital-music
|
||||
|
||||
Am They - Scars I
|
||||
https://www.amazon.com/s?k=Am%20%20They%20Scars%20I%20&i=digital-music
|
||||
|
||||
Church Sing Tauren Wells - Let The
|
||||
https://www.amazon.com/s?k=Church%20%20Sing%20Tauren%20%20Wells%20Let%20%20The%20&i=digital-music
|
||||
|
||||
You Danny Gokey - I Got
|
||||
https://www.amazon.com/s?k=You%20Danny%20%20Gokey%20I%20%20Got%20&i=digital-music
|
||||
|
||||
You've Done Tasha Layton - Look What
|
||||
https://www.amazon.com/s?k=You%27ve%20%20Done%20Tasha%20%20Layton%20Look%20%20What%20&i=digital-music
|
||||
|
||||
Am They - Scars I
|
||||
https://www.amazon.com/s?k=Am%20%20They%20Scars%20I%20&i=digital-music
|
||||
|
||||
Church Sing Tauren Wells - Let The
|
||||
https://www.amazon.com/s?k=Church%20%20Sing%20Tauren%20%20Wells%20Let%20%20The%20&i=digital-music
|
||||
|
||||
You Danny Gokey - I Got
|
||||
https://www.amazon.com/s?k=You%20Danny%20%20Gokey%20I%20%20Got%20&i=digital-music
|
||||
|
||||
You've Done Tasha Layton - Look What
|
||||
https://www.amazon.com/s?k=You%27ve%20%20Done%20Tasha%20%20Layton%20Look%20%20What%20&i=digital-music
|
||||
|
||||
Am They - Scars I
|
||||
https://www.amazon.com/s?k=Am%20%20They%20Scars%20I%20&i=digital-music
|
||||
|
||||
Church Sing Tauren Wells - Let The
|
||||
https://www.amazon.com/s?k=Church%20%20Sing%20Tauren%20%20Wells%20Let%20%20The%20&i=digital-music
|
||||
|
||||
You Danny Gokey - I Got
|
||||
https://www.amazon.com/s?k=You%20Danny%20%20Gokey%20I%20%20Got%20&i=digital-music
|
||||
|
||||
You've Done Tasha Layton - Look What
|
||||
https://www.amazon.com/s?k=You%27ve%20%20Done%20Tasha%20%20Layton%20Look%20%20What%20&i=digital-music
|
||||
|
||||
Am They - Scars I
|
||||
https://www.amazon.com/s?k=Am%20%20They%20Scars%20I%20&i=digital-music
|
||||
|
||||
Church Sing Tauren Wells - Let The
|
||||
https://www.amazon.com/s?k=Church%20%20Sing%20Tauren%20%20Wells%20Let%20%20The%20&i=digital-music
|
||||
|
||||
You Danny Gokey - I Got
|
||||
https://www.amazon.com/s?k=You%20Danny%20%20Gokey%20I%20%20Got%20&i=digital-music
|
||||
|
||||
You've Done Tasha Layton - Look What
|
||||
https://www.amazon.com/s?k=You%27ve%20%20Done%20Tasha%20%20Layton%20Look%20%20What%20&i=digital-music
|
||||
|
||||
Am They - Scars I
|
||||
https://www.amazon.com/s?k=Am%20%20They%20Scars%20I%20&i=digital-music
|
||||
|
||||
Church Sing Tauren Wells - Let The
|
||||
https://www.amazon.com/s?k=Church%20%20Sing%20Tauren%20%20Wells%20Let%20%20The%20&i=digital-music
|
||||
|
||||
You Danny Gokey - I Got
|
||||
https://www.amazon.com/s?k=You%20Danny%20%20Gokey%20I%20%20Got%20&i=digital-music
|
||||
|
||||
You've Done Tasha Layton - Look What
|
||||
https://www.amazon.com/s?k=You%27ve%20%20Done%20Tasha%20%20Layton%20Look%20%20What%20&i=digital-music
|
||||
|
||||
Am They - Scars I
|
||||
https://www.amazon.com/s?k=Am%20%20They%20Scars%20I%20&i=digital-music
|
||||
|
||||
Church Sing Tauren Wells - Let The
|
||||
https://www.amazon.com/s?k=Church%20%20Sing%20Tauren%20%20Wells%20Let%20%20The%20&i=digital-music
|
||||
|
||||
You Danny Gokey - I Got
|
||||
https://www.amazon.com/s?k=You%20Danny%20%20Gokey%20I%20%20Got%20&i=digital-music
|
||||
|
||||
You've Done Tasha Layton - Look What
|
||||
https://www.amazon.com/s?k=You%27ve%20%20Done%20Tasha%20%20Layton%20Look%20%20What%20&i=digital-music
|
||||
|
||||
Thing About Praise Benjamin William Hastings - That's The
|
||||
https://www.amazon.com/s?k=Thing%20%20About%20%20Praise%20Benjamin%20%20William%20%20Hastings%20That%27s%20%20The%20&i=digital-music
|
||||
|
||||
Love Feat. Bear Rinehar We The Kingdom - Child Of
|
||||
https://www.amazon.com/s?k=Love%20%20Feat.%20%20Bear%20%20Rinehar%20We%20%20The%20%20Kingdom%20Child%20%20Of%20&i=digital-music
|
||||
|
||||
Am They - Scars I
|
||||
https://www.amazon.com/s?k=Am%20%20They%20Scars%20I%20&i=digital-music
|
||||
|
||||
Church Sing Tauren Wells - Let The
|
||||
https://www.amazon.com/s?k=Church%20%20Sing%20Tauren%20%20Wells%20Let%20%20The%20&i=digital-music
|
||||
|
||||
You Danny Gokey - I Got
|
||||
https://www.amazon.com/s?k=You%20Danny%20%20Gokey%20I%20%20Got%20&i=digital-music
|
||||
|
||||
You've Done Tasha Layton - Look What
|
||||
https://www.amazon.com/s?k=You%27ve%20%20Done%20Tasha%20%20Layton%20Look%20%20What%20&i=digital-music
|
||||
|
||||
Am They - Scars I
|
||||
https://www.amazon.com/s?k=Am%20%20They%20Scars%20I%20&i=digital-music
|
||||
|
||||
Church Sing Tauren Wells - Let The
|
||||
https://www.amazon.com/s?k=Church%20%20Sing%20Tauren%20%20Wells%20Let%20%20The%20&i=digital-music
|
||||
|
||||
You Danny Gokey - I Got
|
||||
https://www.amazon.com/s?k=You%20Danny%20%20Gokey%20I%20%20Got%20&i=digital-music
|
||||
|
||||
You've Done Tasha Layton - Look What
|
||||
https://www.amazon.com/s?k=You%27ve%20%20Done%20Tasha%20%20Layton%20Look%20%20What%20&i=digital-music
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
2025-11-17 12:40:16.417598: Starting Journey FM Playlist app...
|
||||
2025-11-17 12:40:16.417787: DISPLAY set, initializing GUI
|
||||
2025-11-17 12:40:16.553130: Failed to start GUI application: 'MainWindow' object has no attribute 'update_playlist'
|
||||
2025-11-17 12:42:52.626588: Starting Journey FM Playlist app...
|
||||
2025-11-17 12:42:52.626688: DISPLAY set, initializing GUI
|
||||
2025-11-17 12:42:52.761510: App initialized, starting event loop
|
||||
+43
-20
@@ -15,7 +15,7 @@ from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
# GUI imports
|
||||
from PySide6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QPushButton, QTextEdit, QLineEdit, QLabel, QHBoxLayout, QDialog, QListWidget, QListWidgetItem, QTableWidget, QTableWidgetItem, QHeaderView, QCheckBox, QInputDialog, QMessageBox, QProgressBar, QSystemTrayIcon, QMenu, QComboBox, QGroupBox, QFormLayout, QSpinBox, QTextBrowser, QTabWidget, QDialogButtonBox, QSplitter
|
||||
from PySide6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QPushButton, QTextEdit, QLineEdit, QLabel, QHBoxLayout, QDialog, QListWidget, QListWidgetItem, QTableWidget, QTableWidgetItem, QHeaderView, QCheckBox, QInputDialog, QMessageBox, QProgressBar, QSystemTrayIcon, QMenu, QComboBox, QGroupBox, QFormLayout, QSpinBox, QTextBrowser, QTabWidget, QDialogButtonBox, QSplitter, QGridLayout
|
||||
from PySide6.QtCore import QTimer, Qt, QThread, Signal, QSettings, QUrl
|
||||
from PySide6.QtGui import QIcon, QDesktopServices, QFont, QAction
|
||||
import json
|
||||
@@ -23,12 +23,9 @@ import csv
|
||||
from urllib.parse import quote
|
||||
import sqlite3
|
||||
from datetime import datetime
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas
|
||||
from matplotlib.figure import Figure
|
||||
|
||||
# Import our existing functionality
|
||||
from main import scrape_recently_played, create_playlist_in_plex, PLEX_TOKEN, SERVER_IP, PLAYLIST_NAME
|
||||
from main import scrape_recently_played, create_playlist_in_plex, update_playlist, PLEX_TOKEN, SERVER_IP, PLAYLIST_NAME
|
||||
|
||||
class Config:
|
||||
"""Configuration management"""
|
||||
@@ -287,30 +284,37 @@ class MainWindow(QMainWindow):
|
||||
self.last_update_label = QLabel("Last update: Never")
|
||||
status_layout.addWidget(self.last_update_label)
|
||||
|
||||
# Buy list button
|
||||
# Buttons in grid layout
|
||||
button_layout = QGridLayout()
|
||||
|
||||
# Row 1
|
||||
self.update_button = QPushButton("Update Playlist")
|
||||
self.update_button.clicked.connect(self.run_update_playlist)
|
||||
button_layout.addWidget(self.update_button, 0, 0)
|
||||
|
||||
self.buy_list_button = QPushButton("Show Buy List")
|
||||
self.buy_list_button.clicked.connect(self.show_buy_list)
|
||||
status_layout.addWidget(self.buy_list_button)
|
||||
|
||||
# History button
|
||||
button_layout.addWidget(self.buy_list_button, 0, 1)
|
||||
|
||||
# Row 2
|
||||
self.history_button = QPushButton("View History")
|
||||
self.history_button.clicked.connect(self.show_history)
|
||||
status_layout.addWidget(self.history_button)
|
||||
|
||||
# Export button
|
||||
button_layout.addWidget(self.history_button, 1, 0)
|
||||
|
||||
self.export_button = QPushButton("Export Playlist")
|
||||
self.export_button.clicked.connect(self.export_playlist)
|
||||
status_layout.addWidget(self.export_button)
|
||||
|
||||
# Statistics button
|
||||
button_layout.addWidget(self.export_button, 1, 1)
|
||||
|
||||
# Row 3
|
||||
self.stats_button = QPushButton("Statistics")
|
||||
self.stats_button.clicked.connect(self.show_statistics)
|
||||
status_layout.addWidget(self.stats_button)
|
||||
|
||||
# Analytics button
|
||||
button_layout.addWidget(self.stats_button, 2, 0)
|
||||
|
||||
self.analytics_button = QPushButton("Analytics")
|
||||
self.analytics_button.clicked.connect(self.show_analytics)
|
||||
status_layout.addWidget(self.analytics_button)
|
||||
button_layout.addWidget(self.analytics_button, 2, 1)
|
||||
|
||||
status_layout.addLayout(button_layout)
|
||||
|
||||
status_group.setLayout(status_layout)
|
||||
splitter.addWidget(status_group)
|
||||
@@ -333,6 +337,13 @@ class MainWindow(QMainWindow):
|
||||
if not self.config.get('PLEX_TOKEN'):
|
||||
self.show_setup_wizard()
|
||||
|
||||
def run_update_playlist(self):
|
||||
"""Run playlist update in background thread"""
|
||||
import threading
|
||||
self.status_label.setText("Updating playlist...")
|
||||
self.update_button.setEnabled(False)
|
||||
threading.Thread(target=update_playlist, daemon=True).start()
|
||||
|
||||
def set_tray_icon(self, tray_icon):
|
||||
"""Set the tray icon reference"""
|
||||
self.tray_icon = tray_icon
|
||||
@@ -639,6 +650,9 @@ class MainWindow(QMainWindow):
|
||||
import json
|
||||
from datetime import datetime
|
||||
from collections import Counter
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas
|
||||
from matplotlib.figure import Figure
|
||||
|
||||
dialog = QDialog(self)
|
||||
dialog.setWindowTitle("Playlist Analytics")
|
||||
@@ -956,12 +970,16 @@ class SystemTrayApp:
|
||||
|
||||
def main():
|
||||
"""Main application entry point"""
|
||||
with open('app_log.txt', 'a') as log:
|
||||
log.write(f"{datetime.now()}: Starting Journey FM Playlist app...\n")
|
||||
print("Starting Journey FM Playlist app...")
|
||||
try:
|
||||
if not os.environ.get('DISPLAY'):
|
||||
raise Exception("DISPLAY environment variable not set - no graphical display available")
|
||||
|
||||
print("Initializing GUI...")
|
||||
with open('app_log.txt', 'a') as log:
|
||||
log.write(f"{datetime.now()}: DISPLAY set, initializing GUI\n")
|
||||
# Set up high DPI scaling
|
||||
QApplication.setHighDpiScaleFactorRoundingPolicy(
|
||||
Qt.HighDpiScaleFactorRoundingPolicy.PassThrough
|
||||
@@ -970,11 +988,16 @@ def main():
|
||||
# Create and run application
|
||||
tray_app = SystemTrayApp()
|
||||
print("App initialized, starting event loop...")
|
||||
with open('app_log.txt', 'a') as log:
|
||||
log.write(f"{datetime.now()}: App initialized, starting event loop\n")
|
||||
sys.exit(tray_app.run())
|
||||
except Exception as e:
|
||||
print(f"Failed to start GUI application: {e}")
|
||||
error_msg = f"Failed to start GUI application: {e}"
|
||||
print(error_msg)
|
||||
print("Make sure you have a graphical display available (X11/Wayland) and DISPLAY environment variable is set.")
|
||||
print("If running remotely, use X forwarding or a VNC session.")
|
||||
with open('app_log.txt', 'a') as log:
|
||||
log.write(f"{datetime.now()}: {error_msg}\n")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -86,16 +86,19 @@ def scrape_recently_played():
|
||||
|
||||
songs = []
|
||||
# Find elements (div, li) that contain time in their text
|
||||
song_elements = soup.find_all(lambda tag: tag.name in ['div', 'li', 'p'] and re.search(r'\d+:\d+ [AP]M', tag.get_text()))
|
||||
song_elements = soup.find_all(lambda tag: tag.name in ['div', 'li', 'p'] and re.search(r'\d+:\d+', tag.get_text()))
|
||||
for element in song_elements:
|
||||
if source == 'Journey FM':
|
||||
# Journey FM uses <strong> for title
|
||||
spans = element.find_all('span')
|
||||
if len(spans) >= 2:
|
||||
title = spans[0].get_text().strip()
|
||||
artist = spans[1].get_text().strip()
|
||||
else:
|
||||
# Check for strong
|
||||
strong = element.find('strong')
|
||||
if strong:
|
||||
title = strong.get_text().strip()
|
||||
full_text = element.get_text().strip()
|
||||
# Remove the title and time
|
||||
match = re.search(r'(\d+:\d+ [AP]M)', full_text)
|
||||
match = re.search(r'(\d+:\d+(?: [AP]M)?)', full_text)
|
||||
if match:
|
||||
time_part = match.group(1)
|
||||
before_time = full_text.split(time_part)[0].strip()
|
||||
@@ -104,9 +107,9 @@ def scrape_recently_played():
|
||||
artist = full_text.replace(title, '').strip()
|
||||
artist = artist.strip()
|
||||
else:
|
||||
# Fallback
|
||||
# Fallback to text parsing
|
||||
full_text = element.get_text().strip()
|
||||
match = re.search(r'(\d+:\d+ [AP]M)', full_text)
|
||||
match = re.search(r'(\d+:\d+(?: [AP]M)?)', full_text)
|
||||
if match:
|
||||
play_time = match.group(1)
|
||||
before = full_text.split(play_time)[0].strip()
|
||||
@@ -126,29 +129,6 @@ def scrape_recently_played():
|
||||
else:
|
||||
title = ' '.join(parts[:len(parts)//2])
|
||||
artist = ' '.join(parts[len(parts)//2:])
|
||||
else:
|
||||
# Spirit FM uses text with -
|
||||
full_text = element.get_text().strip()
|
||||
match = re.search(r'(\d+:\d+ [AP]M)', full_text)
|
||||
if match:
|
||||
play_time = match.group(1)
|
||||
before = full_text.split(play_time)[0].strip()
|
||||
if ' by ' in before:
|
||||
title, artist = before.split(' by ', 1)
|
||||
title = title.strip()
|
||||
artist = artist.strip()
|
||||
elif ' - ' in before:
|
||||
title, artist = before.split(' - ', 1)
|
||||
title = title.strip()
|
||||
artist = artist.strip()
|
||||
else:
|
||||
parts = re.findall(r'[A-Z][^A-Z]*', before)
|
||||
if len(parts) >= 4:
|
||||
title = ' '.join(parts[:2])
|
||||
artist = ' '.join(parts[2:])
|
||||
else:
|
||||
title = ' '.join(parts[:len(parts)//2])
|
||||
artist = ' '.join(parts[len(parts)//2:])
|
||||
if title and artist:
|
||||
songs.append({'artist': artist, 'title': title, 'source': source}) # Add to all_songs, avoiding duplicates
|
||||
for song in songs:
|
||||
@@ -252,30 +232,24 @@ def create_playlist_in_plex(plex, songs, playlist_name):
|
||||
|
||||
if new_tracks:
|
||||
existing.addItems([ts[0] for ts in new_tracks])
|
||||
print(f"Added {len(new_tracks)} new songs to existing playlist '{playlist_name}':")
|
||||
for track, song in new_tracks:
|
||||
print(f" + '{track.title}' by {track.artist().title} ({song['source']})")
|
||||
# Auto-tag added songs
|
||||
track.addLabel("Journey FM")
|
||||
track.rate(5)
|
||||
added = len(new_tracks)
|
||||
added_songs.extend([f"{track.title} by {track.artist().title} ({song['source']})" for track, song in new_tracks])
|
||||
else:
|
||||
print(f"No new songs to add - all {len(tracks)} matching songs already in playlist.")
|
||||
added = 0
|
||||
except:
|
||||
# Playlist doesn't exist, create it
|
||||
plex.createPlaylist(playlist_name, [ts[0] for ts in tracks])
|
||||
print(f"Created playlist '{playlist_name}' with {len(tracks)} songs:")
|
||||
for track, song in tracks:
|
||||
print(f" + '{track.title}' by {track.artist().title} ({song['source']})")
|
||||
# Auto-tag added songs
|
||||
track.addLabel("Journey FM")
|
||||
track.rate(5)
|
||||
added = len(tracks)
|
||||
added_songs.extend([f"{track.title} by {track.artist().title} ({song['source']})" for track, song in tracks])
|
||||
except Exception as e:
|
||||
print(f"Error with playlist: {e}")
|
||||
added = 0
|
||||
added_songs = []
|
||||
else:
|
||||
@@ -334,7 +308,7 @@ def setup_scheduler(params):
|
||||
subprocess.run(cmd, check=True)
|
||||
print("Scheduler setup completed.")
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error setting up scheduler: {e}")
|
||||
print(f"Scheduler setup failed: {e}")
|
||||
|
||||
def main():
|
||||
global PLEX_TOKEN, SERVER_IP, PLAYLIST_NAME
|
||||
@@ -351,16 +325,12 @@ def main():
|
||||
|
||||
# Log start time
|
||||
start_time = datetime.now()
|
||||
print(f"\n{'='*60}")
|
||||
print(f"Journey FM Playlist Update - {start_time.strftime('%Y-%m-%d %I:%M:%S %p')}")
|
||||
print(f"{'='*60}\n")
|
||||
|
||||
# Initialize history database
|
||||
init_history_db()
|
||||
|
||||
# Scrape songs
|
||||
songs = scrape_recently_played()
|
||||
print(f"Found {len(songs)} recently played songs.")
|
||||
|
||||
# Connect to Plex via MyPlexAccount
|
||||
account = MyPlexAccount(token=PLEX_TOKEN)
|
||||
@@ -377,20 +347,16 @@ def main():
|
||||
break
|
||||
|
||||
if not server_resource:
|
||||
print(f"Server at {SERVER_IP} not found in your claimed resources.")
|
||||
print("Please claim the server: Open http://172.16.16.106:32400/web, sign in with your Plex account, and follow the claim prompts.")
|
||||
return
|
||||
|
||||
plex = server_resource.connect()
|
||||
print(f"Connected to server: {plex.friendlyName}, ID: {plex.machineIdentifier}")
|
||||
|
||||
# Debug: list library sections
|
||||
print(f"Library sections: {[s.title for s in plex.library.sections()]}")
|
||||
|
||||
# Debug: list existing playlists (handle unicode safely)
|
||||
playlists = plex.playlists()
|
||||
playlist_names = [p.title.encode('ascii', 'ignore').decode('ascii') if not p.title.isascii() else p.title for p in playlists]
|
||||
print(f"Existing playlists: {len(playlists)} total")
|
||||
|
||||
# Find matching songs in local library (optional, since Plex search might suffice)
|
||||
# But to filter only those in local library, perhaps skip or adjust
|
||||
@@ -408,7 +374,6 @@ def main():
|
||||
|
||||
# Create Amazon buy list for missing songs
|
||||
if missing:
|
||||
print(f"\n{len(missing)} songs not found in your library. Updating Amazon buy list...")
|
||||
|
||||
# Read existing buy list
|
||||
existing_songs = set()
|
||||
@@ -428,7 +393,7 @@ def main():
|
||||
existing_songs.add((artist.strip(), title.strip()))
|
||||
i += 1
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not read existing buy list: {e}")
|
||||
pass
|
||||
|
||||
# Filter new missing songs
|
||||
new_missing = []
|
||||
@@ -449,8 +414,6 @@ def main():
|
||||
query = urllib.parse.quote(f"{artist} {title}")
|
||||
url = f"https://www.amazon.com/s?k={query}&i=digital-music"
|
||||
f.write(f"{artist} - {title}\n{url}\n\n")
|
||||
print(f" - {artist} - {title}: {url}")
|
||||
print(f"Added {len(new_missing)} new songs to buy list.")
|
||||
else:
|
||||
print("No new songs to add to buy list.")
|
||||
|
||||
@@ -459,8 +422,10 @@ def main():
|
||||
# Log completion
|
||||
end_time = datetime.now()
|
||||
duration = (end_time - start_time).total_seconds()
|
||||
print(f"\nCompleted in {duration:.1f} seconds at {end_time.strftime('%I:%M:%S %p')}")
|
||||
print(f"{'='*60}\n")
|
||||
|
||||
def update_playlist():
|
||||
"""Function to update playlist, callable from GUI"""
|
||||
main()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Binary file not shown.
Reference in new issue
Block a user