Spaces:
Running
Running
from pathlib import Path | |
import streamlit as st | |
from streamlit_player import st_player | |
from service.youtube import ( | |
get_youtube_url, | |
search_youtube, | |
download_audio_from_youtube, | |
) | |
from helpers import ( | |
get_random_song, | |
load_audio_segment, | |
streamlit_player, | |
local_audio, | |
delete_old_files, | |
) | |
from service.vocal_remover.runner import separate, load_model | |
from footer import footer | |
from header import header | |
from loguru import logger as log | |
out_path = Path("/tmp") | |
in_path = Path("/tmp") | |
sess = st.session_state | |
def show_karaoke(pathname): | |
st.session_state.karaoke = True | |
cols = st.columns([1, 1, 3, 1]) | |
with cols[1]: | |
sess.delay = st.slider( | |
label="Adjust video start delay in seconds (higher values anticipate lyrics, need to restart the player)", | |
key="delay_slider", | |
value=2, | |
min_value=0, | |
max_value=5, | |
help="Synchronize youtube player with karaoke audio by adding a delay to the youtube player.", | |
) | |
with cols[2]: | |
events = st_player( | |
local_audio(pathname), | |
**{ | |
"progress_interval": 1000, | |
"playing": False, | |
"muted": False, | |
"light": False, | |
"play_inline": True, | |
"playback_rate": 1, | |
"height": 40, | |
"config": { | |
"start": 0, | |
"forceAudio": True, | |
}, | |
"events": ["onProgress", "onPlay"], | |
}, | |
key="karaoke_player", | |
) | |
st.markdown( | |
"""<center>⬆️ Click on the play button to start karaoke<br>You will see the video with lyrics below ⬇️<center>""", | |
unsafe_allow_html=True, | |
) | |
with st.columns([1, 4, 1])[1]: | |
if events.name == "onPlay": | |
sess.player_restart = True | |
log.info(f"Play Karaoke - {sess.selected_value} - {sess.delay}s delay") | |
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"] | |
sess.player_restart = False | |
st_player( | |
sess.url + f"&t={sess.tot_delay}s", | |
**{ | |
"progress_interval": 1000, | |
"playing": True, | |
"muted": True, | |
"light": False, | |
"play_inline": False, | |
"playback_rate": 1, | |
"height": 250, | |
"events": None, | |
}, | |
key="yt_muted_player", | |
) | |
def reset_karaoke(): | |
sess.karaoke = False | |
sess.url = None | |
sess.executed = False | |
def body(): | |
st.markdown( | |
"<h4><center>Play karaoke removing the vocals of your favorite song <center></h4>", | |
unsafe_allow_html=True, | |
) | |
yt_cols = st.columns([1, 3, 2, 1]) | |
selected_value = None | |
with yt_cols[1]: | |
input_search = st.text_input( | |
label="Search a song on YouTube", | |
label_visibility="collapsed", | |
placeholder="Search on YouTube by name...", | |
key="yt_input_search", | |
on_change=reset_karaoke, | |
) | |
radio_selection = st.empty() | |
if not sess.get("karaoke", False): | |
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 not sess.get("karaoke", False): | |
if selected_value is not None and selected_value in sess.video_options: | |
sess.random_song = None | |
if selected_value != sess.selected_value: # New song selected | |
sess.executed = False | |
sess.selected_value = selected_value | |
sess.url = get_youtube_url(selected_value) | |
if selected_value is None or selected_value == "": | |
with yt_cols[2]: | |
if st.button("🎲 Random song", use_container_width=True): | |
reset_karaoke() | |
sess.last_dir, sess.url = get_random_song() | |
log.info(f"Random song - {sess.last_dir}") | |
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]: | |
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]) | |
with cols_before_sep[1]: | |
execute_button = st.empty() | |
execute = execute_button.button( | |
"Confirm and remove vocals 🎤 🎶", | |
type="primary", | |
use_container_width=True, | |
) | |
if execute or sess.executed: | |
radio_selection.empty() | |
execute_button.empty() | |
player.empty() | |
if execute: | |
sess.executed = False | |
if sess.random_song is None: | |
if not sess.executed: | |
with st.spinner( | |
"Separating vocals from music, it could take a few minutes... Don't close this page!" | |
): | |
log.info(f"Separating vocals from {sess.selected_value}") | |
sess.filename = download_audio_from_youtube(sess.url, in_path) | |
if sess.filename is None: | |
st.stop() | |
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" | |
): | |
log.info(f"Cancel separation of vocals from {filename}") | |
st.experimental_rerun() | |
separate( | |
input=in_path / filename, | |
model=model, | |
device=device, | |
output_dir=out_path, | |
only_no_vocals=True, | |
) | |
selected_value = None | |
sess.last_dir = ".".join(sess.filename.split(".")[:-1]) | |
sess.executed = True | |
cancel_button.empty() | |
log.info(f"Separating Done - {sess.selected_value}") | |
else: | |
sess.executed = True | |
if sess.executed: | |
show_karaoke(out_path / "vocal_remover" / sess.last_dir / "no_vocals.mp3") | |
if __name__ == "__main__": | |
header() | |
body() | |
footer() | |
delete_old_files("/tmp", 60 * 30) | |