Fabio Grasso commited on
Commit
dc66c98
1 Parent(s): 6d8379b

feat: replace st_searchbox with text_input and selectbox, limit query youtube (#5)

Browse files

Remove st_searchbox cause instability.
Use simple input box to search on YouTube and avoid search on every input changes.
Limit output results to 5.

app/pages/Karaoke.py CHANGED
@@ -2,7 +2,6 @@ from pathlib import Path
2
 
3
  import streamlit as st
4
  from streamlit_player import st_player
5
- from streamlit_searchbox import st_searchbox
6
 
7
  from service.youtube import (
8
  get_youtube_url,
@@ -30,6 +29,7 @@ sess = st.session_state
30
 
31
 
32
  def show_karaoke(pathname):
 
33
  cols = st.columns([1, 1, 3, 1])
34
  with cols[1]:
35
  sess.delay = st.slider(
@@ -65,13 +65,17 @@ def show_karaoke(pathname):
65
  )
66
  with st.columns([1, 4, 1])[1]:
67
  if events.name == "onPlay":
68
- st.session_state.player_restart = True
69
  log.info(f"Play Karaoke - {sess.selected_value}")
70
 
71
- elif events.name == "onProgress" and events.data["playedSeconds"] > 0:
72
- if st.session_state.player_restart:
 
 
 
 
73
  sess.tot_delay = sess.delay + events.data["playedSeconds"]
74
- st.session_state.player_restart = False
75
  st_player(
76
  sess.url + f"&t={sess.tot_delay}s",
77
  **{
@@ -88,51 +92,75 @@ def show_karaoke(pathname):
88
  )
89
 
90
 
 
 
 
 
 
 
91
  def body():
92
  st.markdown(
93
  "<h4><center>Play karaoke removing the vocals of your favorite song <center></h4>",
94
  unsafe_allow_html=True,
95
  )
96
  yt_cols = st.columns([1, 3, 2, 1])
 
97
  with yt_cols[1]:
98
- selected_value = st_searchbox(
99
- search_youtube,
100
- label=None,
101
  placeholder="Search on YouTube by name...",
102
- clear_on_submit=True,
103
- key="yt_searchbox",
104
  )
105
- if selected_value is not None and selected_value in sess.video_options:
106
- sess.random_song = None
 
 
 
 
 
 
 
 
 
 
 
107
 
108
- if selected_value != sess.selected_value: # New song selected
109
- sess.executed = False
 
110
 
111
- sess.selected_value = selected_value
112
- sess.url = get_youtube_url(selected_value)
113
 
114
- with yt_cols[2]:
115
- if st.button("🎲 Random song", use_container_width=True):
116
- sess.last_dir, sess.url = get_random_song()
117
- sess.selected_value = sess.last_dir
118
- sess.random_song = True
119
- sess.video_options = []
120
- sess.executed = False
121
 
122
- if sess.url is not None:
 
 
 
 
 
 
 
 
 
 
123
  player_cols = st.columns([2, 2, 1, 1], gap="medium")
124
  with player_cols[1]:
125
- player = st.empty()
126
- streamlit_player(
127
- player,
128
- sess.url,
129
- height=200,
130
- is_active=False,
131
- muted=False,
132
- start=0,
133
- key="yt_player",
134
- events=["onProgress"],
135
- )
136
 
137
  # Separate vocals
138
  cols_before_sep = st.columns([2, 4, 2])
@@ -144,6 +172,7 @@ def body():
144
  use_container_width=True,
145
  )
146
  if execute or sess.executed:
 
147
  execute_button.empty()
148
  player.empty()
149
  if execute:
@@ -156,11 +185,15 @@ def body():
156
  sess.filename = download_audio_from_youtube(sess.url, in_path)
157
  if sess.filename is None:
158
  st.stop()
159
- sess.url = None
160
  filename = sess.filename
161
  song = load_audio_segment(in_path / filename, filename.split(".")[-1])
162
  song.export(in_path / filename, format=filename.split(".")[-1])
163
  model, device = load_model(pretrained_model="baseline.pth")
 
 
 
 
 
164
  separate(
165
  input=in_path / filename,
166
  model=model,
@@ -171,11 +204,12 @@ def body():
171
  selected_value = None
172
  sess.last_dir = ".".join(sess.filename.split(".")[:-1])
173
  sess.executed = True
 
174
  else:
175
  sess.executed = True
176
 
177
- if sess.executed:
178
- show_karaoke(out_path / "vocal_remover" / sess.last_dir / "no_vocals.mp3")
179
 
180
 
181
  if __name__ == "__main__":
 
2
 
3
  import streamlit as st
4
  from streamlit_player import st_player
 
5
 
6
  from service.youtube import (
7
  get_youtube_url,
 
29
 
30
 
31
  def show_karaoke(pathname):
32
+ st.session_state.karaoke = True
33
  cols = st.columns([1, 1, 3, 1])
34
  with cols[1]:
35
  sess.delay = st.slider(
 
65
  )
66
  with st.columns([1, 4, 1])[1]:
67
  if events.name == "onPlay":
68
+ sess.player_restart = True
69
  log.info(f"Play Karaoke - {sess.selected_value}")
70
 
71
+ elif (
72
+ events.name == "onProgress"
73
+ and events.data["playedSeconds"] > 0
74
+ and events.data["played"] < 1
75
+ ):
76
+ if sess.player_restart:
77
  sess.tot_delay = sess.delay + events.data["playedSeconds"]
78
+ sess.player_restart = False
79
  st_player(
80
  sess.url + f"&t={sess.tot_delay}s",
81
  **{
 
92
  )
93
 
94
 
95
+ def reset_karaoke():
96
+ sess.karaoke = False
97
+ sess.url = None
98
+ sess.executed = False
99
+
100
+
101
  def body():
102
  st.markdown(
103
  "<h4><center>Play karaoke removing the vocals of your favorite song <center></h4>",
104
  unsafe_allow_html=True,
105
  )
106
  yt_cols = st.columns([1, 3, 2, 1])
107
+ selected_value = None
108
  with yt_cols[1]:
109
+ input_search = st.text_input(
110
+ label="Search a song on YouTube",
111
+ label_visibility="collapsed",
112
  placeholder="Search on YouTube by name...",
113
+ key="yt_input_search",
114
+ on_change=reset_karaoke,
115
  )
116
+ if not sess.get("karaoke", False):
117
+ radio_selection = st.empty()
118
+ if input_search != "" and input_search != sess.get("input_search", ""):
119
+ sess.input_search = input_search
120
+ with st.spinner("Searching on YouTube..."):
121
+ sess.options = search_youtube(input_search)
122
+ if sess.get("options", []) != []:
123
+ selected_value = radio_selection.selectbox(
124
+ label="**⬇️ Select a title and see the video preview**",
125
+ index=len(sess.options),
126
+ options=sess.options + [""],
127
+ key="yt_radio",
128
+ )
129
 
130
+ if not sess.get("karaoke", False):
131
+ if selected_value is not None and selected_value in sess.video_options:
132
+ sess.random_song = None
133
 
134
+ if selected_value != sess.selected_value: # New song selected
135
+ sess.executed = False
136
 
137
+ sess.selected_value = selected_value
138
+ sess.url = get_youtube_url(selected_value)
 
 
 
 
 
139
 
140
+ if selected_value is None or selected_value == "":
141
+ with yt_cols[2]:
142
+ if st.button("🎲 Random song", use_container_width=True):
143
+ sess.last_dir, sess.url = get_random_song()
144
+ sess.selected_value = sess.last_dir
145
+ sess.random_song = True
146
+ sess.video_options = []
147
+ sess.executed = False
148
+ radio_selection.empty()
149
+
150
+ if sess.url is not None and not sess.get("karaoke", False):
151
  player_cols = st.columns([2, 2, 1, 1], gap="medium")
152
  with player_cols[1]:
153
+ with st.spinner("Loading video preview..."):
154
+ player = st.empty()
155
+ streamlit_player(
156
+ player,
157
+ sess.url,
158
+ height=200,
159
+ is_active=False,
160
+ muted=False,
161
+ start=0,
162
+ key="yt_player",
163
+ )
164
 
165
  # Separate vocals
166
  cols_before_sep = st.columns([2, 4, 2])
 
172
  use_container_width=True,
173
  )
174
  if execute or sess.executed:
175
+ radio_selection.empty()
176
  execute_button.empty()
177
  player.empty()
178
  if execute:
 
185
  sess.filename = download_audio_from_youtube(sess.url, in_path)
186
  if sess.filename is None:
187
  st.stop()
 
188
  filename = sess.filename
189
  song = load_audio_segment(in_path / filename, filename.split(".")[-1])
190
  song.export(in_path / filename, format=filename.split(".")[-1])
191
  model, device = load_model(pretrained_model="baseline.pth")
192
+ cancel_button = st.empty()
193
+ if cancel_button.button(
194
+ "Cancel", use_container_width=True, type="secondary"
195
+ ):
196
+ st.experimental_rerun()
197
  separate(
198
  input=in_path / filename,
199
  model=model,
 
204
  selected_value = None
205
  sess.last_dir = ".".join(sess.filename.split(".")[:-1])
206
  sess.executed = True
207
+ cancel_button.empty()
208
  else:
209
  sess.executed = True
210
 
211
+ if sess.executed:
212
+ show_karaoke(out_path / "vocal_remover" / sess.last_dir / "no_vocals.mp3")
213
 
214
 
215
  if __name__ == "__main__":
app/service/youtube.py CHANGED
@@ -2,7 +2,6 @@ import logging
2
  import os
3
  import re
4
  import string
5
- import time
6
  from typing import List
7
 
8
  import streamlit as st
@@ -51,18 +50,17 @@ def download_audio_from_youtube(url, output_path):
51
  return f"{video_title}.mp3"
52
 
53
 
54
- @st.cache_data(show_spinner=False)
55
  def query_youtube(query: str) -> Search:
56
  return Search(query)
57
 
58
 
59
- def search_youtube(query: str) -> List:
60
  if len(query) > 3:
61
- time.sleep(0.5)
62
  search = query_youtube(query + " lyrics")
63
  st.session_state.search_results = search.results
64
  if "search_results" in st.session_state and st.session_state.search_results is not None:
65
- video_options = [video.title for video in st.session_state.search_results]
66
  else:
67
  video_options = []
68
  else:
 
2
  import os
3
  import re
4
  import string
 
5
  from typing import List
6
 
7
  import streamlit as st
 
50
  return f"{video_title}.mp3"
51
 
52
 
53
+ @st.cache_data(show_spinner=False, max_entries=10)
54
  def query_youtube(query: str) -> Search:
55
  return Search(query)
56
 
57
 
58
+ def search_youtube(query: str, limit=5) -> List:
59
  if len(query) > 3:
 
60
  search = query_youtube(query + " lyrics")
61
  st.session_state.search_results = search.results
62
  if "search_results" in st.session_state and st.session_state.search_results is not None:
63
+ video_options = [video.title for video in st.session_state.search_results[:limit]]
64
  else:
65
  video_options = []
66
  else:
requirements.in CHANGED
@@ -4,7 +4,6 @@ pandas==1.5.3
4
  pydub==0.25.1
5
  pytube==12.1.3
6
  streamlit-player==0.1.5
7
- streamlit-searchbox==0.1.2
8
  yt-dlp==2023.7.6
9
  matplotlib==3.7.1
10
  librosa==0.10.0.post2
 
4
  pydub==0.25.1
5
  pytube==12.1.3
6
  streamlit-player==0.1.5
 
7
  yt-dlp==2023.7.6
8
  matplotlib==3.7.1
9
  librosa==0.10.0.post2
requirements.txt CHANGED
@@ -2,7 +2,7 @@
2
  # This file is autogenerated by pip-compile with Python 3.10
3
  # by the following command:
4
  #
5
- # pip-compile --output-file=requirements.txt --resolver=backtracking requirements.in
6
  #
7
  altair==4.2.2
8
  # via streamlit
@@ -30,7 +30,7 @@ cffi==1.15.1
30
  # via soundfile
31
  charset-normalizer==3.2.0
32
  # via requests
33
- click==8.1.4
34
  # via streamlit
35
  cloudpickle==2.2.1
36
  # via submitit
@@ -56,7 +56,7 @@ entrypoints==0.4
56
  # via altair
57
  filelock==3.12.2
58
  # via torch
59
- fonttools==4.40.0
60
  # via matplotlib
61
  gitdb==4.0.10
62
  # via gitpython
@@ -77,7 +77,7 @@ joblib==1.3.1
77
  # via
78
  # librosa
79
  # scikit-learn
80
- jsonschema==4.18.0
81
  # via altair
82
  jsonschema-specifications==2023.6.1
83
  # via jsonschema
@@ -227,20 +227,17 @@ streamlit==1.22.0
227
  # stqdm
228
  # streamlit-option-menu
229
  # streamlit-player
230
- # streamlit-searchbox
231
  streamlit-option-menu==0.3.6
232
  # via -r requirements.in
233
  streamlit-player==0.1.5
234
  # via -r requirements.in
235
- streamlit-searchbox==0.1.2
236
- # via -r requirements.in
237
  submitit==1.4.5
238
  # via dora-search
239
  sympy==1.12
240
  # via torch
241
  tenacity==8.2.2
242
  # via streamlit
243
- threadpoolctl==3.1.0
244
  # via scikit-learn
245
  toml==0.10.2
246
  # via streamlit
@@ -283,5 +280,5 @@ websockets==11.0.3
283
  # via yt-dlp
284
  yt-dlp==2023.7.6
285
  # via -r requirements.in
286
- zipp==3.16.0
287
  # via importlib-metadata
 
2
  # This file is autogenerated by pip-compile with Python 3.10
3
  # by the following command:
4
  #
5
+ # pip-compile --output-file=requirements.txt requirements.in
6
  #
7
  altair==4.2.2
8
  # via streamlit
 
30
  # via soundfile
31
  charset-normalizer==3.2.0
32
  # via requests
33
+ click==8.1.5
34
  # via streamlit
35
  cloudpickle==2.2.1
36
  # via submitit
 
56
  # via altair
57
  filelock==3.12.2
58
  # via torch
59
+ fonttools==4.41.0
60
  # via matplotlib
61
  gitdb==4.0.10
62
  # via gitpython
 
77
  # via
78
  # librosa
79
  # scikit-learn
80
+ jsonschema==4.18.3
81
  # via altair
82
  jsonschema-specifications==2023.6.1
83
  # via jsonschema
 
227
  # stqdm
228
  # streamlit-option-menu
229
  # streamlit-player
 
230
  streamlit-option-menu==0.3.6
231
  # via -r requirements.in
232
  streamlit-player==0.1.5
233
  # via -r requirements.in
 
 
234
  submitit==1.4.5
235
  # via dora-search
236
  sympy==1.12
237
  # via torch
238
  tenacity==8.2.2
239
  # via streamlit
240
+ threadpoolctl==3.2.0
241
  # via scikit-learn
242
  toml==0.10.2
243
  # via streamlit
 
280
  # via yt-dlp
281
  yt-dlp==2023.7.6
282
  # via -r requirements.in
283
+ zipp==3.16.2
284
  # via importlib-metadata