import time import streamlit as st from st_aggrid import AgGrid, GridOptionsBuilder, GridUpdateMode import pandas as pd import Flow from src.Library import * def entity_to_row(entity: Entity): return { "id": entity.id, "mbid": entity.mbid, "Title": entity.title, "PLay Count": entity.play_count, "Resolved": int(entity.resolved_percentage * 100), "AutoScore": entity.auto_score, # "Added": entity.date_added.strftime("%Y-%m-%d") if entity.date_added else None, # "Played": entity.date_last_play.strftime("%Y-%m-%d") if entity.date_last_play else None, # "Released": entity.date_released.strftime("%Y-%m-%d") if entity.date_released else None, "LocalId": str(entity.local_id) if entity.local_id else None, "Path": str(entity.local_path) if entity.local_path else None, } def track_to_row(track: Track): return entity_to_row(track) | { "Album": track.album.title if track.album else "", "Artists": ", ".join(a.title if a.title else "no_arist_name" for a in track.artists), "Lyrics": track.has_lyrics, } def artist_to_row(artist: Artist): return entity_to_row(artist) | { } def album_to_row(album: Album): return entity_to_row(album) | { } class LibraryView: def __init__(self): self.lib : Library = None self.libs : list[Library] = None self.view = None self.lyrics_only = False self.query = None def add_libraries(self, libs : list[Library]): self.libs = libs def render(self): st.set_page_config(layout="wide") with st.sidebar: lib = st.selectbox( "Library", options=[lib.name for lib in self.libs], ) for iter in self.libs: if iter.name == lib: self.lib = iter st.write(f"Showing library '{self.lib.name}'") self.view = st.radio( "View", ["Tree"] ) if self.view == "Tree": self.render_artists_albums_tracks() # elif self.view == "Tracks": # self.render_tracks() # elif self.view == "Playlists": # self.render_playlists() def track_visible( self, track: Track, artist: Artist | None, album: Album | None, ): if self.lyrics_only and not track.has_lyrics: return False if self.query and self.query.lower() not in track.title.lower(): return False if artist and all(a.id != artist.id for a in track.artists): return False if album and (not track.album or track.album.id != album.id): return False return True def render_artists_albums_tracks(self): # --- ARTISTS TABLE --- st.subheader("Artists") artist_rows = [artist_to_row(a) for a in self.lib.artists] df_artists = pd.DataFrame(artist_rows) gb_artists = GridOptionsBuilder.from_dataframe(df_artists) gb_artists.configure_selection(selection_mode="multiple", use_checkbox=True) gb_artists.configure_column("Title", filter="agTextColumnFilter") gb_artists.configure_column("AutoScore", sort="desc") grid_options_artists = gb_artists.build() grid_response_artists = AgGrid( df_artists, gridOptions=grid_options_artists, update_mode=GridUpdateMode.SELECTION_CHANGED, allow_unsafe_jscode=True, enable_enterprise_modules=False, fit_columns_on_grid_load=True, ) selected_rows_df = grid_response_artists.get("selected_rows") selected_artist_titles = selected_rows_df["Title"].tolist() if selected_rows_df is not None and not selected_rows_df.empty else [] selected_artists = [a for a in self.lib.artists if a.title in selected_artist_titles] # --- ALBUMS TABLE --- st.subheader("Albums") if selected_artists: albums_list = [] for artist in selected_artists: albums_list.extend(artist.albums) albums_list = list({al.id: al for al in albums_list}.values()) else: albums_list = self.lib.albums album_rows = [album_to_row(a) for a in albums_list] df_albums = pd.DataFrame(album_rows) gb_albums = GridOptionsBuilder.from_dataframe(df_albums) gb_albums.configure_selection(selection_mode="multiple", use_checkbox=True) gb_albums.configure_column("Title", filter="agTextColumnFilter") gb_albums.configure_column("AutoScore", sort="desc") grid_options_albums = gb_albums.build() grid_response_albums = AgGrid( df_albums, gridOptions=grid_options_albums, update_mode=GridUpdateMode.SELECTION_CHANGED, allow_unsafe_jscode=True, enable_enterprise_modules=False, fit_columns_on_grid_load=True, ) selected_rows_df = grid_response_albums.get("selected_rows") selected_album_titles = selected_rows_df["Title"].tolist() if selected_rows_df is not None and not selected_rows_df.empty else [] selected_albums = [al for al in albums_list if al.title in selected_album_titles] # --- TRACKS TABLE --- st.subheader("Tracks") if selected_albums: tracks_list = [] for album in selected_albums: tracks_list.extend(album.tracks) elif selected_artists: tracks_list = [] for artist in selected_artists: tracks_list.extend([t for t in self.lib.tracks if artist in t.artists]) else: tracks_list = self.lib.tracks track_rows = [track_to_row(t) for t in tracks_list] df_tracks = pd.DataFrame(track_rows) gb_tracks = GridOptionsBuilder.from_dataframe(df_tracks) gb_tracks.configure_selection(selection_mode="multiple", use_checkbox=True) gb_tracks.configure_column("Title", filter="agTextColumnFilter") gb_tracks.configure_column("AutoScore", sort="desc") grid_options_tracks = gb_tracks.build() AgGrid( df_tracks, gridOptions=grid_options_tracks, update_mode=GridUpdateMode.SELECTION_CHANGED, allow_unsafe_jscode=True, enable_enterprise_modules=False, fit_columns_on_grid_load=True, ) def run(): flow = Flow.Flow() # flow.fetch_spotify() # flow.fetch_local() # flow.parse_spotify_library() # flow.parse_local_library() flow.load_libraries() # flow.map_local_to_spotify() # flow.log_stats() gui = LibraryView() gui.add_libraries([flow.spotify_library, flow.itunes_library, flow.local_library]) gui.render() run()