import logging import os import re import string from typing import List from loguru import logger as log import streamlit as st import yt_dlp from pytube import Search logger = logging.getLogger("pytube") logger.setLevel(logging.ERROR) def _sanitize_filename(filename): safe_chars = "-_.() %s%s" % ( re.escape(string.ascii_letters), re.escape(string.digits), ) safe_filename = re.sub(f"[^{safe_chars}]", "_", filename) return safe_filename.strip() @st.cache_data(show_spinner=False) def download_audio_from_youtube(url, output_path): if not os.path.exists(output_path): os.makedirs(output_path) with yt_dlp.YoutubeDL({"quiet": True}) as ydl: info_dict = ydl.extract_info(url, download=False) if info_dict.get("duration", 0) > 360: st.error("Song is too long. Please use a song no longer than 6 minutes.") return video_title = info_dict.get("title", None) video_title = _sanitize_filename(video_title) ydl_opts = { "format": "bestaudio/best", "postprocessors": [ { "key": "FFmpegExtractAudio", "preferredcodec": "mp3", "preferredquality": "192", } ], "outtmpl": os.path.join(output_path, video_title), "quiet": True, } with yt_dlp.YoutubeDL(ydl_opts) as ydl: ydl.download([url]) return f"{video_title}.mp3" @st.cache_data(show_spinner=False, max_entries=10) def query_youtube(query: str) -> Search: return Search(query) def search_youtube(query: str, limit=5) -> List: log.info(f"{query}") if len(query) > 3: 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[:limit]] else: video_options = [] else: video_options = [] st.session_state.video_options = video_options return video_options def get_youtube_url(title: str) -> str: video = st.session_state.search_results[st.session_state.video_options.index(title)] return video.embed_url def check_if_is_youtube_url(url: str) -> bool: return url.startswith("http")