From 0bf96f0213b8b8d12fdee78141ea6d592aad2f43 Mon Sep 17 00:00:00 2001 From: fchinembiri Date: Fri, 10 Jul 2026 20:40:39 +0200 Subject: [PATCH] fix(musicseerr): implement session-based conversion triggering and polling in Tubidy scraper --- apps/musicseerr/tasks.py | 68 ++++++++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 13 deletions(-) diff --git a/apps/musicseerr/tasks.py b/apps/musicseerr/tasks.py index 1f49211..fe3c90f 100644 --- a/apps/musicseerr/tasks.py +++ b/apps/musicseerr/tasks.py @@ -148,6 +148,7 @@ def search_tubidy(query: str, proxy_url: str) -> list: def get_tubidy_download_link(content_id: str, proxy_url: str) -> str: """ Queries watch.php for the given content ID to find the direct MP3 download URL. + Supports initiating and polling the conversion process if the video is not yet processed. """ proxies = {"http": proxy_url, "https": proxy_url} if proxy_url else None headers = { @@ -166,23 +167,64 @@ def get_tubidy_download_link(content_id: str, proxy_url: str) -> str: logger.info(f"Fetching watch.php for content ID '{content_id}'") try: - response = cf_requests.get(watch_url, headers=headers, params=params, proxies=proxies, impersonate="chrome", timeout=15) + session = cf_requests.Session() + if proxies: + session.proxies = proxies + session.headers.update(headers) + + response = session.get(watch_url, params=params, impersonate="chrome", timeout=15) response.raise_for_status() - soup = BeautifulSoup(response.text, "html.parser") - for li in soup.find_all("li", class_=lambda x: x and "list-group-item" in x and "big" in x): - a_tag = li.find("a") - if a_tag: + def extract_link(html: str) -> str: + soup = BeautifulSoup(html, "html.parser") + # 1. Search in list-group-item big tags + for li in soup.find_all("li", class_=lambda x: x and "list-group-item" in x and "big" in x): + a_tag = li.find("a") + if a_tag: + text = a_tag.text.lower() + href = a_tag.get("href") + if href and "download" in text: + return urljoin(watch_url, href) + # 2. General fallback search for direct download/mp3 links + for a_tag in soup.find_all("a", href=True): text = a_tag.text.lower() - href = a_tag.get("href") - if href and "download" in text: - return href - - # Fallback to general scraping of the watch.php page if structure differs + href = a_tag["href"] + if "download" in text and "act=process" not in href and "act=down" not in href: + return urljoin(watch_url, href) + if (".mp3" in href.lower() or "video.mp3.tubidy.cool" in href.lower()) and "act=process" not in href: + return urljoin(watch_url, href) + return "" + + link = extract_link(response.text) + if link: + return link + + # If no link found, check if there is a process link + soup = BeautifulSoup(response.text, "html.parser") + process_link = None for a_tag in soup.find_all("a", href=True): - text = a_tag.text.lower() - if "download" in text and "act=process" not in a_tag["href"]: - return a_tag["href"] + if "act=process" in a_tag["href"]: + process_link = urljoin(watch_url, a_tag["href"]) + break + + if process_link: + logger.info(f"Triggering Tubidy processing step for content ID '{content_id}' via {process_link}") + proc_resp = session.get(process_link, impersonate="chrome", timeout=15) + proc_resp.raise_for_status() + + # Wait for conversion (typically takes 5-10s on Tubidy server) + import time + wait_time = 10 + logger.info(f"Waiting {wait_time} seconds for conversion to finish...") + time.sleep(wait_time) + + # Re-fetch the download page + response = session.get(watch_url, params=params, impersonate="chrome", timeout=15) + response.raise_for_status() + link = extract_link(response.text) + if link: + return link + except Exception as e: logger.error(f"Failed to fetch download link for {content_id}: {e}")