diff --git a/journeyfm/history_service.py b/journeyfm/history_service.py index b47ebdb..76b4613 100644 --- a/journeyfm/history_service.py +++ b/journeyfm/history_service.py @@ -11,6 +11,7 @@ HISTORY_COLUMNS = { "duplicate_count": "INTEGER DEFAULT 0", "skipped_count": "INTEGER DEFAULT 0", "station_breakdown": "TEXT DEFAULT '[]'", + "scraped_songs": "TEXT DEFAULT '[]'", "skipped_songs": "TEXT DEFAULT '[]'", "error_message": "TEXT DEFAULT ''", } @@ -37,6 +38,10 @@ def init_history_db(db_path=None): for column_name, definition in HISTORY_COLUMNS.items(): if column_name not in existing_columns: cursor.execute(f"ALTER TABLE history ADD COLUMN {column_name} {definition}") + + # store scraped song detail for per-song/per-station analysis + if 'scraped_songs' not in existing_columns: + cursor.execute("ALTER TABLE history ADD COLUMN scraped_songs TEXT DEFAULT '[]'") conn.commit() conn.close() @@ -50,8 +55,8 @@ def save_history_entry(result, db_path=None): INSERT INTO history ( date, added_count, added_songs, missing_count, missing_songs, status, scraped_count, matched_count, duplicate_count, skipped_count, - station_breakdown, skipped_songs, error_message - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + station_breakdown, scraped_songs, skipped_songs, error_message + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( datetime.now().isoformat(), @@ -65,6 +70,7 @@ def save_history_entry(result, db_path=None): result.get("duplicate_count", 0), result.get("skipped_count", 0), json.dumps(result.get("station_breakdown", [])), + json.dumps(result.get("scraped_songs", [])), json.dumps(result.get("skipped_songs", [])), result.get("error_message", ""), ), diff --git a/journeyfm/update_service.py b/journeyfm/update_service.py index b96c57b..a84864c 100644 --- a/journeyfm/update_service.py +++ b/journeyfm/update_service.py @@ -117,6 +117,8 @@ def run_update_job(config=None, dry_run=False, persist_history=True, write_buy_l songs = scrape_result["songs"] result["station_breakdown"] = scrape_result["station_results"] result["scraped_count"] = len(songs) + # Keep scraped songs for history analytics (song play counts per station) + result["scraped_songs"] = songs try: plex = connect_to_plex_server(token, server_ip) diff --git a/journeyfm/web_service.py b/journeyfm/web_service.py index 5816ff3..df68ffc 100644 --- a/journeyfm/web_service.py +++ b/journeyfm/web_service.py @@ -27,6 +27,7 @@ def load_recent_stats(db_path=None): "last_attempted": None, "last_success": None, "station_counts": {}, + "song_counts": {}, } if not Path(db_path).exists(): @@ -52,16 +53,28 @@ def load_recent_stats(db_path=None): stats["last_attempted"] = last_row[0] stats["last_success"] = last_row[1] - c.execute("SELECT station_breakdown FROM history") + c.execute("SELECT station_breakdown, scraped_songs FROM history") station_rows = c.fetchall() - for (station_json,) in station_rows: + for station_json, scraped_songs_json in station_rows: try: for station in json.loads(station_json or "[]"): if station.get("success"): key = station.get("display_name") or station.get("station") or "Unknown" stats["station_counts"][key] = stats["station_counts"].get(key, 0) + int(station.get("scraped_count", 0)) except Exception: - continue + pass + + try: + songs = json.loads(scraped_songs_json or "[]") + for song in songs: + station_name = song.get("source", "Unknown") + title = song.get("title", "?") + artist = song.get("artist", "?") + key = f"{artist} - {title}" + stats["song_counts"].setdefault(station_name, {}) + stats["song_counts"][station_name][key] = stats["song_counts"][station_name].get(key, 0) + 1 + except Exception: + pass finally: conn.close() @@ -75,6 +88,14 @@ def render_dashboard_html(stats): ) overall_count = sum(stats["station_counts"].values()) + # Build a flattened station-song ranking table for display + top_song_rows = [] + for station_name, songs in sorted(stats.get("song_counts", {}).items()): + sorted_songs = sorted(songs.items(), key=lambda kv: kv[1], reverse=True) + for song_name, count in sorted_songs[:10]: + top_song_rows.append(f"
| Station | Song | Count |
|---|