import os import gradio as gr import spotipy import uvicorn from urllib import request from fastapi import FastAPI from starlette.middleware.sessions import SessionMiddleware from starlette.responses import HTMLResponse, RedirectResponse from starlette.requests import Request as SRequest from fastapi.responses import HTMLResponse from fastapi.responses import RedirectResponse from src.playlist_builder import * from src.evolutionary_alrogithm import Population, simple_mutation, simple_crossover, THUMBS_UP, THUMBS_DOWN, ADD_TO_PLAYLIST from src.spotipy_utils import get_auth_manager NUM_SONGS = 9 DEFAULT_MUTATION_SIZE = 0.25 SONG_CHOICES = [THUMBS_UP, THUMBS_DOWN, ADD_TO_PLAYLIST] OPTIONS_TITLE = """# 🧬 Options""" APP_HEADER = """# Evolutionary Playlist Builder""" EXPLORATION_HEADER = """## Rate of Exploration""" PLAYLIST_HEADER = """## Current Playlist""" FAST_API_KEY = os.environ["FAST_API_KEY"] app = FastAPI() app.add_middleware(SessionMiddleware, secret_key=FAST_API_KEY) @app.get('/', response_class=HTMLResponse) async def homepage(request: SRequest): auth_manager = get_auth_manager() # Need auth manager to generate Spotify API authentication url. auth_url = auth_manager.get_authorize_url() return "Login to Spotify" def initialize_playlist_builder(): demo = gr.Blocks() with demo: gr.Markdown(APP_HEADER) with gr.Row(): # Initialize app attributes do_crossover = gr.State(False) do_mutation = gr.State(False) population = Population(NUM_SONGS, DEFAULT_MUTATION_SIZE, mutation_function=simple_mutation, crossover_function=simple_crossover) population_state = gr.State(population) # Generate blank traversal visualization. initial_traversal_history = population.generate_traversal_viz() # Layout first column. with gr.Column(): gr.Markdown( f""" This app is still in development mode so if you want the ability to add a playlist to your library, email carterward4@gmail.com with your name, spotify email, and the subject as PLAYLIST ACCESS REQUEST. """) options = gr.Box(visible=False) with options: gr.Markdown(OPTIONS_TITLE) crossover_radio = gr.Radio(choices=CROSSOVER_OPTIONS, value=None, label="Crossover?") # radio button for crossover options. mutation_radio = gr.Radio(choices=MUTATION_OPTIONS, value=None, label="Mutate?") # radio button for mutation options. gr.Markdown(EXPLORATION_HEADER, visible=False) mutation_size_slider = gr.Slider(minimum=0.0, maximum=1.0, value=DEFAULT_MUTATION_SIZE , interactive=True, show_label=False, visible=False) # slider controlling mutation slide. Values restricted between 0 and 1. history_block = gr.Plot(initial_traversal_history, label="Search History") # Plot block for blank viz. gr.Markdown(PLAYLIST_HEADER) playlist_display = gr.TextArea(value=population.get_playlist_block_value(), max_lines=10000, show_label=False) # Blank playlist display. playlist_name = gr.Textbox(interactive=True, label="Enter the name for this playlist!") # Auth. URL entry. add_playlist = gr.Button(value="Add Playlist to Spotify Library") # tracking_viz_button = gr.Button(value="Generate Direction Tracking Viz") # Button to add to playlist playlist_added_message = gr.Markdown(ADDED_TO_PLAYLIST, visible=False) # Build grid layout with initial song art and options. index_blocks = [] name_blocks = [] image_blocks = [] dropdown_blocks = [] preview_blocks = [] with gr.Column(): # Init seed options seed_option = gr.Radio(SEED_OPTIONS, value=SEED_OPTIONS[-1], label="Seed generation?", show_label=True) seed_options_container = gr.Box(visible=False) with seed_options_container: seed_track_search = gr.Textbox(value = None, interactive=True, label="Enter the track name!", show_label=True) seed_artist_search = gr.Textbox(value = None, interactive=True, label="Enter the artist name!", show_label=True) search_button = gr.Button(value = "Search for seed!") search_results = gr.Dropdown(label="Search result options", interactive=True) # Init population grid. pop_container = gr.Box(visible=False) with pop_container: for row in range(0, population.size, 3): # Iterate over track rows. with gr.Row(): for col in range(3): # Iterate over track columns. with gr.Box(): track_index = row + col if track_index < len(population.pop): current_song = population.pop[track_index] song_preview = current_song.get_preview_url() track_image = gr.Image(value=current_song.get_image_url(), show_label=False, visible=True).style(height=175, width=175) track_title = gr.Markdown(value=f"{current_song.name} by {current_song.artist}", show_label=False) track_dropdown = gr.Dropdown(SONG_CHOICES, label = track_index, show_label=False) else: track_image = None song_preview = None track_image = gr.Image(value=None, show_label=False, visible=True).style(height=175, width=175) track_title = gr.Markdown(value=f"", show_label=False, visible=True) track_dropdown = gr.Dropdown(SONG_CHOICES, label = track_index, show_label=False, visible=True) index_blocks.append(gr.TextArea(value=track_index, visible=False)) # Init index blocks to keep track of everything. # Render initial images. image_blocks.append(track_image) # Add names. name_blocks.append(track_title) # Dropdown for song options. dropdown_blocks.append(track_dropdown) # Add song preview if it exists. if song_preview: preview_blocks.append(gr.Audio(value=song_preview, show_label=False)) else: preview_blocks.append(gr.Audio(value=song_preview, show_label=False, visible=False)) with gr.Box(): next_generation = gr.Button(value="Next Generation").style(full_width=True) # Mapping change functions to class methods. crossover_radio.change( fn=set_crossover_option, inputs=[crossover_radio], outputs=[do_crossover] ) mutation_radio.change( fn=set_mutation_option, inputs=[population_state, do_crossover, mutation_radio], outputs=[mutation_size_slider, crossover_radio, do_crossover, do_mutation, population_state] ) mutation_size_slider.change( fn=update_mutation_size, inputs=[population_state, mutation_size_slider], outputs=[population_state] ) seed_option.change( fn=change_seed_option, inputs=[seed_option, population_state], outputs=[seed_options_container, options, seed_track_search, seed_artist_search, search_results, population_state] ) search_button.click( fn=search_for_seed, inputs=[population_state, seed_artist_search, seed_track_search], outputs=[population_state, search_results] ) search_results.change( fn=set_pop_seed, inputs=[population_state, search_results], outputs=[population_state] ) # Set function to hangle change in dropdown value for each track. for index_block, dropdown_block in zip(index_blocks, dropdown_blocks): dropdown_block.change( fn=set_track_state, inputs=[population_state, index_block, dropdown_block], outputs=[population_state] ) # Set function to generate next generation of tracks. next_generation.click( fn=get_next_generation, inputs=[population_state, do_mutation, do_crossover], outputs=[pop_container, options, population_state, playlist_display, *image_blocks, *name_blocks, *dropdown_blocks, *preview_blocks, history_block] ) add_playlist.click( fn=add_playlist_to_spotify, inputs=[population_state, playlist_name], outputs=[playlist_added_message] ) # tracking_viz_button.click( # fn=generate_population_direction_tracking_viz, # inputs=[population_state] # ) return demo gradio_app = initialize_playlist_builder() playlist_app = gr.mount_gradio_app(app, gradio_app, "/gradio", gradio_api_url="http://localhost:7860/") uvicorn.run(app, host="0.0.0.0", port=7860)