diff --git a/app/pages/Karaoke.py b/app/pages/Karaoke.py index fb3df0c..2afc614 100644 --- a/app/pages/Karaoke.py +++ b/app/pages/Karaoke.py @@ -2,7 +2,6 @@ import streamlit as st from streamlit_player import st_player -from streamlit_searchbox import st_searchbox from service.youtube import ( get_youtube_url, @@ -30,6 +29,7 @@ def show_karaoke(pathname): + st.session_state.karaoke = True cols = st.columns([1, 1, 3, 1]) with cols[1]: sess.delay = st.slider( @@ -65,13 +65,17 @@ def show_karaoke(pathname): ) with st.columns([1, 4, 1])[1]: if events.name == "onPlay": - st.session_state.player_restart = True + sess.player_restart = True log.info(f"Play Karaoke - {sess.selected_value}") - elif events.name == "onProgress" and events.data["playedSeconds"] > 0: - if st.session_state.player_restart: + elif ( + events.name == "onProgress" + and events.data["playedSeconds"] > 0 + and events.data["played"] < 1 + ): + if sess.player_restart: sess.tot_delay = sess.delay + events.data["playedSeconds"] - st.session_state.player_restart = False + sess.player_restart = False st_player( sess.url + f"&t={sess.tot_delay}s", **{ @@ -88,51 +92,75 @@ def show_karaoke(pathname): ) +def reset_karaoke(): + sess.karaoke = False + sess.url = None + sess.executed = False + + def body(): st.markdown( "

Play karaoke removing the vocals of your favorite song

", unsafe_allow_html=True, ) yt_cols = st.columns([1, 3, 2, 1]) + selected_value = None with yt_cols[1]: - selected_value = st_searchbox( - search_youtube, - label=None, + input_search = st.text_input( + label="Search a song on YouTube", + label_visibility="collapsed", placeholder="Search on YouTube by name...", - clear_on_submit=True, - key="yt_searchbox", + key="yt_input_search", + on_change=reset_karaoke, ) - if selected_value is not None and selected_value in sess.video_options: - sess.random_song = None + if not sess.get("karaoke", False): + radio_selection = st.empty() + if input_search != "" and input_search != sess.get("input_search", ""): + sess.input_search = input_search + with st.spinner("Searching on YouTube..."): + sess.options = search_youtube(input_search) + if sess.get("options", []) != []: + selected_value = radio_selection.selectbox( + label="**⬇️ Select a title and see the video preview**", + index=len(sess.options), + options=sess.options + [""], + key="yt_radio", + ) - if selected_value != sess.selected_value: # New song selected - sess.executed = False + if not sess.get("karaoke", False): + if selected_value is not None and selected_value in sess.video_options: + sess.random_song = None - sess.selected_value = selected_value - sess.url = get_youtube_url(selected_value) + if selected_value != sess.selected_value: # New song selected + sess.executed = False - with yt_cols[2]: - if st.button("🎲 Random song", use_container_width=True): - sess.last_dir, sess.url = get_random_song() - sess.selected_value = sess.last_dir - sess.random_song = True - sess.video_options = [] - sess.executed = False + sess.selected_value = selected_value + sess.url = get_youtube_url(selected_value) - if sess.url is not None: + if selected_value is None or selected_value == "": + with yt_cols[2]: + if st.button("🎲 Random song", use_container_width=True): + sess.last_dir, sess.url = get_random_song() + sess.selected_value = sess.last_dir + sess.random_song = True + sess.video_options = [] + sess.executed = False + radio_selection.empty() + + if sess.url is not None and not sess.get("karaoke", False): player_cols = st.columns([2, 2, 1, 1], gap="medium") with player_cols[1]: - player = st.empty() - streamlit_player( - player, - sess.url, - height=200, - is_active=False, - muted=False, - start=0, - key="yt_player", - events=["onProgress"], - ) + with st.spinner("Loading video preview..."): + player = st.empty() + streamlit_player( + player, + sess.url, + height=200, + is_active=False, + muted=False, + start=0, + key="yt_player", + ) # Separate vocals cols_before_sep = st.columns([2, 4, 2]) @@ -144,6 +172,7 @@ def body(): use_container_width=True, ) if execute or sess.executed: + radio_selection.empty() execute_button.empty() player.empty() if execute: @@ -156,11 +185,15 @@ def body(): sess.filename = download_audio_from_youtube(sess.url, in_path) if sess.filename is None: st.stop() - sess.url = None filename = sess.filename song = load_audio_segment(in_path / filename, filename.split(".")[-1]) song.export(in_path / filename, format=filename.split(".")[-1]) model, device = load_model(pretrained_model="baseline.pth") + cancel_button = st.empty() + if cancel_button.button( + "Cancel", use_container_width=True, type="secondary" + ): + st.experimental_rerun() separate( input=in_path / filename, model=model, @@ -171,11 +204,12 @@ def body(): selected_value = None sess.last_dir = ".".join(sess.filename.split(".")[:-1]) sess.executed = True + cancel_button.empty() else: sess.executed = True - if sess.executed: - show_karaoke(out_path / "vocal_remover" / sess.last_dir / "no_vocals.mp3") + if sess.executed: + show_karaoke(out_path / "vocal_remover" / sess.last_dir / "no_vocals.mp3") if __name__ == "__main__": diff --git a/app/service/youtube.py b/app/service/youtube.py index 193ff8c..9ff2ebf 100644 --- a/app/service/youtube.py +++ b/app/service/youtube.py @@ -2,7 +2,6 @@ import os import re import string -import time from typing import List import streamlit as st @@ -51,18 +50,17 @@ def download_audio_from_youtube(url, output_path): return f"{video_title}.mp3" -@st.cache_data(show_spinner=False) +@st.cache_data(show_spinner=False, max_entries=10) def query_youtube(query: str) -> Search: return Search(query) -def search_youtube(query: str) -> List: +def search_youtube(query: str, limit=5) -> List: if len(query) > 3: - time.sleep(0.5) search = query_youtube(query + " lyrics") st.session_state.search_results = search.results if "search_results" in st.session_state and st.session_state.search_results is not None: - video_options = [video.title for video in st.session_state.search_results] + video_options = [video.title for video in st.session_state.search_results[:limit]] else: video_options = [] else: diff --git a/requirements.in b/requirements.in index 9b8d34f..dcdcb43 100644 --- a/requirements.in +++ b/requirements.in @@ -4,7 +4,6 @@ pandas==1.5.3 pydub==0.25.1 pytube==12.1.3 streamlit-player==0.1.5 -streamlit-searchbox==0.1.2 yt-dlp==2023.7.6 matplotlib==3.7.1 librosa==0.10.0.post2 diff --git a/requirements.txt b/requirements.txt index 5f51351..8ab3a78 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile with Python 3.10 # by the following command: # -# pip-compile --output-file=requirements.txt --resolver=backtracking requirements.in +# pip-compile --output-file=requirements.txt requirements.in # altair==4.2.2 # via streamlit @@ -30,7 +30,7 @@ cffi==1.15.1 # via soundfile charset-normalizer==3.2.0 # via requests -click==8.1.4 +click==8.1.5 # via streamlit cloudpickle==2.2.1 # via submitit @@ -56,7 +56,7 @@ entrypoints==0.4 # via altair filelock==3.12.2 # via torch -fonttools==4.40.0 +fonttools==4.41.0 # via matplotlib gitdb==4.0.10 # via gitpython @@ -77,7 +77,7 @@ joblib==1.3.1 # via # librosa # scikit-learn -jsonschema==4.18.0 +jsonschema==4.18.3 # via altair jsonschema-specifications==2023.6.1 # via jsonschema @@ -227,20 +227,17 @@ streamlit==1.22.0 # stqdm # streamlit-option-menu # streamlit-player - # streamlit-searchbox streamlit-option-menu==0.3.6 # via -r requirements.in streamlit-player==0.1.5 # via -r requirements.in -streamlit-searchbox==0.1.2 - # via -r requirements.in submitit==1.4.5 # via dora-search sympy==1.12 # via torch tenacity==8.2.2 # via streamlit -threadpoolctl==3.1.0 +threadpoolctl==3.2.0 # via scikit-learn toml==0.10.2 # via streamlit @@ -283,5 +280,5 @@ websockets==11.0.3 # via yt-dlp yt-dlp==2023.7.6 # via -r requirements.in -zipp==3.16.0 +zipp==3.16.2 # via importlib-metadata