Add station selection feature to SetupWizard and scrape function
Co-authored-by: nmemmert <67961292+nmemmert@users.noreply.github.com>
This commit is contained in:
1 parent
d5da0f710e
commit
013edb952f
2 files changed
+111
-66
No files matched your search
+36
-1
@@ -59,6 +59,9 @@ class Config:
|
|||||||
def save_config(self, config):
|
def save_config(self, config):
|
||||||
"""Save config to QSettings"""
|
"""Save config to QSettings"""
|
||||||
for key, value in config.items():
|
for key, value in config.items():
|
||||||
|
# Handle list values (convert to comma-separated string)
|
||||||
|
if isinstance(value, list):
|
||||||
|
value = ','.join(value)
|
||||||
self.set(key, value)
|
self.set(key, value)
|
||||||
|
|
||||||
class SetupWizard(QDialog):
|
class SetupWizard(QDialog):
|
||||||
@@ -109,6 +112,22 @@ class SetupWizard(QDialog):
|
|||||||
form_group.setLayout(form_layout)
|
form_group.setLayout(form_layout)
|
||||||
layout.addWidget(form_group)
|
layout.addWidget(form_group)
|
||||||
|
|
||||||
|
# Station Selection
|
||||||
|
station_group = QGroupBox("Station Selection")
|
||||||
|
station_layout = QVBoxLayout()
|
||||||
|
|
||||||
|
station_layout.addWidget(QLabel("Select which stations to monitor:"))
|
||||||
|
self.journey_fm_checkbox = QCheckBox("Journey FM (myjourneyfm.com)")
|
||||||
|
self.journey_fm_checkbox.setChecked(True)
|
||||||
|
station_layout.addWidget(self.journey_fm_checkbox)
|
||||||
|
|
||||||
|
self.spirit_fm_checkbox = QCheckBox("Spirit FM (spiritfm.com)")
|
||||||
|
self.spirit_fm_checkbox.setChecked(True)
|
||||||
|
station_layout.addWidget(self.spirit_fm_checkbox)
|
||||||
|
|
||||||
|
station_group.setLayout(station_layout)
|
||||||
|
layout.addWidget(station_group)
|
||||||
|
|
||||||
# Scheduling options
|
# Scheduling options
|
||||||
sched_group = QGroupBox("Scheduling")
|
sched_group = QGroupBox("Scheduling")
|
||||||
sched_layout = QVBoxLayout()
|
sched_layout = QVBoxLayout()
|
||||||
@@ -147,14 +166,30 @@ class SetupWizard(QDialog):
|
|||||||
self.server_input.setText(config.get('SERVER_IP', ''))
|
self.server_input.setText(config.get('SERVER_IP', ''))
|
||||||
self.playlist_input.setText(config.get('PLAYLIST_NAME', 'Journey FM Recently Played'))
|
self.playlist_input.setText(config.get('PLAYLIST_NAME', 'Journey FM Recently Played'))
|
||||||
|
|
||||||
|
# Load station selections
|
||||||
|
selected_stations = config.get('SELECTED_STATIONS', 'journey_fm,spirit_fm')
|
||||||
|
if isinstance(selected_stations, str):
|
||||||
|
selected_stations = selected_stations.split(',')
|
||||||
|
else:
|
||||||
|
selected_stations = ['journey_fm', 'spirit_fm']
|
||||||
|
self.journey_fm_checkbox.setChecked('journey_fm' in selected_stations)
|
||||||
|
self.spirit_fm_checkbox.setChecked('spirit_fm' in selected_stations)
|
||||||
|
|
||||||
def get_config(self):
|
def get_config(self):
|
||||||
|
selected_stations = []
|
||||||
|
if self.journey_fm_checkbox.isChecked():
|
||||||
|
selected_stations.append('journey_fm')
|
||||||
|
if self.spirit_fm_checkbox.isChecked():
|
||||||
|
selected_stations.append('spirit_fm')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'PLEX_TOKEN': self.token_input.text().strip(),
|
'PLEX_TOKEN': self.token_input.text().strip(),
|
||||||
'SERVER_IP': self.server_input.text().strip(),
|
'SERVER_IP': self.server_input.text().strip(),
|
||||||
'PLAYLIST_NAME': self.playlist_input.text().strip(),
|
'PLAYLIST_NAME': self.playlist_input.text().strip(),
|
||||||
'AUTO_UPDATE': self.auto_update.isChecked(),
|
'AUTO_UPDATE': self.auto_update.isChecked(),
|
||||||
'UPDATE_INTERVAL': self.interval_spin.value(),
|
'UPDATE_INTERVAL': self.interval_spin.value(),
|
||||||
'UPDATE_UNIT': self.interval_unit.currentText()
|
'UPDATE_UNIT': self.interval_unit.currentText(),
|
||||||
|
'SELECTED_STATIONS': ','.join(selected_stations)
|
||||||
}
|
}
|
||||||
|
|
||||||
class LogViewer(QWidget):
|
class LogViewer(QWidget):
|
||||||
|
|||||||
@@ -32,8 +32,11 @@ PLEX_TOKEN = 'pU-m3HWYUZU6iXJFhJyA' # Your Plex.tv token
|
|||||||
SERVER_IP = '172.16.16.106' # Your local server IP
|
SERVER_IP = '172.16.16.106' # Your local server IP
|
||||||
PLAYLIST_NAME = 'Journey FM Recently Played'
|
PLAYLIST_NAME = 'Journey FM Recently Played'
|
||||||
|
|
||||||
def scrape_recently_played():
|
def scrape_recently_played(selected_stations=None):
|
||||||
"""Scrape recently played songs from Journey FM and Spirit FM"""
|
"""Scrape recently played songs from Journey FM and Spirit FM"""
|
||||||
|
if selected_stations is None:
|
||||||
|
selected_stations = ['journey_fm', 'spirit_fm']
|
||||||
|
|
||||||
options = Options()
|
options = Options()
|
||||||
options.add_argument("--headless=new")
|
options.add_argument("--headless=new")
|
||||||
options.add_argument("--no-sandbox")
|
options.add_argument("--no-sandbox")
|
||||||
@@ -58,80 +61,82 @@ def scrape_recently_played():
|
|||||||
seen = set()
|
seen = set()
|
||||||
|
|
||||||
# Journey FM - uses <strong> for title, normal text for artist
|
# Journey FM - uses <strong> for title, normal text for artist
|
||||||
try:
|
if 'journey_fm' in selected_stations:
|
||||||
print("Scraping Journey FM...")
|
|
||||||
driver.get('https://www.myjourneyfm.com/recently-played/')
|
|
||||||
time.sleep(5)
|
|
||||||
|
|
||||||
# Click "View More" button if exists
|
|
||||||
try:
|
try:
|
||||||
more_button = driver.find_element(By.ID, "moreSongs")
|
print("Scraping Journey FM...")
|
||||||
more_button.click()
|
driver.get('https://www.myjourneyfm.com/recently-played/')
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
soup = BeautifulSoup(driver.page_source, 'html.parser')
|
# Click "View More" button if exists
|
||||||
|
|
||||||
# Find all song items using the proper class structure
|
|
||||||
song_items = soup.find_all('div', class_='rp-item')
|
|
||||||
|
|
||||||
for item in song_items:
|
|
||||||
try:
|
try:
|
||||||
title_elem = item.find('h5', class_='song-title')
|
more_button = driver.find_element(By.ID, "moreSongs")
|
||||||
artist_elem = item.find('p', class_='song-artist')
|
more_button.click()
|
||||||
|
time.sleep(5)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
if title_elem and artist_elem:
|
soup = BeautifulSoup(driver.page_source, 'html.parser')
|
||||||
title = title_elem.get_text().strip()
|
|
||||||
artist = artist_elem.get_text().strip()
|
# Find all song items using the proper class structure
|
||||||
|
song_items = soup.find_all('div', class_='rp-item')
|
||||||
|
|
||||||
|
for item in song_items:
|
||||||
|
try:
|
||||||
|
title_elem = item.find('h5', class_='song-title')
|
||||||
|
artist_elem = item.find('p', class_='song-artist')
|
||||||
|
|
||||||
|
if title_elem and artist_elem:
|
||||||
|
title = title_elem.get_text().strip()
|
||||||
|
artist = artist_elem.get_text().strip()
|
||||||
|
|
||||||
|
if title and artist:
|
||||||
|
key = (title.lower(), artist.lower())
|
||||||
|
if key not in seen:
|
||||||
|
all_songs.append({'title': title, 'artist': artist, 'source': 'Journey FM'})
|
||||||
|
seen.add(key)
|
||||||
|
print(f" Found: {title} by {artist}")
|
||||||
|
except Exception as e:
|
||||||
|
continue
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error scraping Journey FM: {e}")
|
||||||
|
|
||||||
|
# Spirit FM - loads songs from iframe text file
|
||||||
|
if 'spirit_fm' in selected_stations:
|
||||||
|
try:
|
||||||
|
print("Scraping Spirit FM...")
|
||||||
|
driver.get('https://spiritfm.com/ajax/now_playing_history.txt')
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
# Parse the plain text format: "Mon 01:45PM Artist - Title"
|
||||||
|
soup = BeautifulSoup(driver.page_source, 'html.parser')
|
||||||
|
text_content = soup.get_text()
|
||||||
|
|
||||||
|
# Split into lines and parse each
|
||||||
|
for line in text_content.split('\n'):
|
||||||
|
line = line.strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Match pattern: Day HH:MM(AM/PM) Artist - Title
|
||||||
|
match = re.match(r'^\w+\s+\d+:\d+[AP]M\s+(.+?)\s+-\s+(.+)$', line)
|
||||||
|
if match:
|
||||||
|
artist = match.group(1).strip()
|
||||||
|
title = match.group(2).strip()
|
||||||
|
|
||||||
|
# Clean up HTML entities
|
||||||
|
artist = artist.replace('&', '&')
|
||||||
|
title = title.replace('&', '&')
|
||||||
|
|
||||||
if title and artist:
|
if title and artist:
|
||||||
key = (title.lower(), artist.lower())
|
key = (title.lower(), artist.lower())
|
||||||
if key not in seen:
|
if key not in seen:
|
||||||
all_songs.append({'title': title, 'artist': artist, 'source': 'Journey FM'})
|
all_songs.append({'title': title, 'artist': artist, 'source': 'Spirit FM'})
|
||||||
seen.add(key)
|
seen.add(key)
|
||||||
print(f" Found: {title} by {artist}")
|
print(f" Found: {title} by {artist}")
|
||||||
except Exception as e:
|
|
||||||
continue
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error scraping Journey FM: {e}")
|
print(f"Error scraping Spirit FM: {e}")
|
||||||
|
|
||||||
# Spirit FM - loads songs from iframe text file
|
|
||||||
try:
|
|
||||||
print("Scraping Spirit FM...")
|
|
||||||
driver.get('https://spiritfm.com/ajax/now_playing_history.txt')
|
|
||||||
time.sleep(3)
|
|
||||||
|
|
||||||
# Parse the plain text format: "Mon 01:45PM Artist - Title"
|
|
||||||
soup = BeautifulSoup(driver.page_source, 'html.parser')
|
|
||||||
text_content = soup.get_text()
|
|
||||||
|
|
||||||
# Split into lines and parse each
|
|
||||||
for line in text_content.split('\n'):
|
|
||||||
line = line.strip()
|
|
||||||
if not line:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Match pattern: Day HH:MM(AM/PM) Artist - Title
|
|
||||||
match = re.match(r'^\w+\s+\d+:\d+[AP]M\s+(.+?)\s+-\s+(.+)$', line)
|
|
||||||
if match:
|
|
||||||
artist = match.group(1).strip()
|
|
||||||
title = match.group(2).strip()
|
|
||||||
|
|
||||||
# Clean up HTML entities
|
|
||||||
artist = artist.replace('&', '&')
|
|
||||||
title = title.replace('&', '&')
|
|
||||||
|
|
||||||
if title and artist:
|
|
||||||
key = (title.lower(), artist.lower())
|
|
||||||
if key not in seen:
|
|
||||||
all_songs.append({'title': title, 'artist': artist, 'source': 'Spirit FM'})
|
|
||||||
seen.add(key)
|
|
||||||
print(f" Found: {title} by {artist}")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error scraping Spirit FM: {e}")
|
|
||||||
|
|
||||||
driver.quit()
|
driver.quit()
|
||||||
|
|
||||||
@@ -312,12 +317,17 @@ def setup_scheduler(params):
|
|||||||
def main():
|
def main():
|
||||||
global PLEX_TOKEN, SERVER_IP, PLAYLIST_NAME
|
global PLEX_TOKEN, SERVER_IP, PLAYLIST_NAME
|
||||||
# Load or prompt for config
|
# Load or prompt for config
|
||||||
|
selected_stations = ['journey_fm', 'spirit_fm'] # default
|
||||||
if os.path.exists('config.json'):
|
if os.path.exists('config.json'):
|
||||||
with open('config.json', 'r') as f:
|
with open('config.json', 'r') as f:
|
||||||
config = json.load(f)
|
config = json.load(f)
|
||||||
PLEX_TOKEN = config.get('PLEX_TOKEN', PLEX_TOKEN)
|
PLEX_TOKEN = config.get('PLEX_TOKEN', PLEX_TOKEN)
|
||||||
SERVER_IP = config.get('SERVER_IP', SERVER_IP)
|
SERVER_IP = config.get('SERVER_IP', SERVER_IP)
|
||||||
PLAYLIST_NAME = config.get('PLAYLIST_NAME', PLAYLIST_NAME)
|
PLAYLIST_NAME = config.get('PLAYLIST_NAME', PLAYLIST_NAME)
|
||||||
|
# Load selected stations
|
||||||
|
selected_stations_str = config.get('SELECTED_STATIONS', 'journey_fm,spirit_fm')
|
||||||
|
if isinstance(selected_stations_str, str):
|
||||||
|
selected_stations = selected_stations_str.split(',')
|
||||||
else:
|
else:
|
||||||
# If no config file, use defaults (GUI should handle this)
|
# If no config file, use defaults (GUI should handle this)
|
||||||
pass
|
pass
|
||||||
@@ -328,8 +338,8 @@ def main():
|
|||||||
# Initialize history database
|
# Initialize history database
|
||||||
init_history_db()
|
init_history_db()
|
||||||
|
|
||||||
# Scrape songs
|
# Scrape songs with selected stations
|
||||||
songs = scrape_recently_played()
|
songs = scrape_recently_played(selected_stations)
|
||||||
|
|
||||||
# Connect to Plex via MyPlexAccount
|
# Connect to Plex via MyPlexAccount
|
||||||
account = MyPlexAccount(token=PLEX_TOKEN)
|
account = MyPlexAccount(token=PLEX_TOKEN)
|
||||||
|
|||||||
Reference in new issue
Block a user