forked from Ethanscharlie/Happy-Ripper
Cleaned code
This commit is contained in:
+134
-159
@@ -1,7 +1,7 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from flask import Flask, render_template, request, jsonify
|
||||
from threading import RLock, Thread
|
||||
from threading import Thread
|
||||
import time
|
||||
from yt_dlp import YoutubeDL
|
||||
from yt_dlp.utils import DownloadError, ExtractorError
|
||||
@@ -16,13 +16,19 @@ class ReleaseInfo:
|
||||
artist: str
|
||||
title: str
|
||||
art_url: str
|
||||
is_album: bool
|
||||
is_topic: bool
|
||||
|
||||
|
||||
@dataclass
|
||||
class ReleaseStatus:
|
||||
info: ReleaseInfo | None
|
||||
status: str
|
||||
|
||||
|
||||
class YtdlpErrorCode(Exception):
|
||||
def __init__(self, code):
|
||||
super().__init__(f"Some videos failed with code {code}")
|
||||
self.code = code
|
||||
class RipError(Exception):
|
||||
def __init__(self, msg):
|
||||
super().__init__(msg)
|
||||
|
||||
|
||||
def create_or_wipe_folder(path: str) -> None:
|
||||
@@ -41,21 +47,76 @@ def get_content_json(url: str):
|
||||
|
||||
|
||||
def download_content(path: str, url: str) -> None:
|
||||
with YoutubeDL(
|
||||
{
|
||||
"format": "m4a/bestaudio/best",
|
||||
"postprocessors": [
|
||||
{
|
||||
"key": "FFmpegExtractAudio",
|
||||
"preferredcodec": "m4a",
|
||||
}
|
||||
],
|
||||
"outtmpl": f"{path}/%(title)s.%(ext)s",
|
||||
}
|
||||
) as ydl:
|
||||
error_code = ydl.download([url])
|
||||
if error_code:
|
||||
raise YtdlpErrorCode(error_code)
|
||||
try:
|
||||
with YoutubeDL(
|
||||
{
|
||||
"format": "m4a/bestaudio/best",
|
||||
"postprocessors": [
|
||||
{
|
||||
"key": "FFmpegExtractAudio",
|
||||
"preferredcodec": "m4a",
|
||||
}
|
||||
],
|
||||
"outtmpl": f"{path}/%(title)s.%(ext)s",
|
||||
}
|
||||
) as ydl:
|
||||
error_code = ydl.download([url])
|
||||
if error_code:
|
||||
raise RipError(f"Content download failed ({error_code})")
|
||||
|
||||
except DownloadError:
|
||||
raise RipError("Content Download Failed")
|
||||
|
||||
except ExtractorError:
|
||||
raise RipError("Content Extraction Failed")
|
||||
|
||||
|
||||
def find_release_info(url: str) -> ReleaseInfo:
|
||||
try:
|
||||
info = get_content_json(url)
|
||||
|
||||
except DownloadError:
|
||||
raise RipError("Info Download Failed")
|
||||
|
||||
except ExtractorError:
|
||||
raise RipError("Info Extraction Failed")
|
||||
|
||||
entries = info.get("entries")
|
||||
if entries is None:
|
||||
raise RipError("Entries is none")
|
||||
|
||||
first_entry = next(iter(entries), None)
|
||||
if not first_entry:
|
||||
raise RipError("Entries is empty")
|
||||
|
||||
artist = first_entry.get("uploader")
|
||||
if artist is None:
|
||||
raise RipError("Uploader tag is none")
|
||||
|
||||
is_topic = False
|
||||
if " - Topic" in artist:
|
||||
is_topic = True
|
||||
artist = artist.replace(" - Topic", "")
|
||||
|
||||
title = info.get("title")
|
||||
if title is None:
|
||||
raise RipError("Title tag is none")
|
||||
|
||||
is_album = False
|
||||
if "Album - " in title:
|
||||
is_album = True
|
||||
title = title.replace("Album - ", "")
|
||||
|
||||
thumbnails = info.get("thumbnails")
|
||||
if thumbnails is None:
|
||||
raise RipError("Thumbnails is none")
|
||||
|
||||
art_url = thumbnails[1]["url"]
|
||||
|
||||
if not is_album:
|
||||
raise RipError("Failed, not an album")
|
||||
|
||||
return ReleaseInfo(artist, title, art_url, is_album, is_topic)
|
||||
|
||||
|
||||
def find_tmp_path() -> str:
|
||||
@@ -63,9 +124,7 @@ def find_tmp_path() -> str:
|
||||
return f"{project_root_path}/tmp"
|
||||
|
||||
|
||||
def rip_worker(
|
||||
rip_queue: list[str], status_callback: Callable[[ReleaseInfo],], finish_callback: Callable[[ReleaseInfo],]
|
||||
):
|
||||
def rip_worker(rip_queue: list[str], status_callback: Callable[[ReleaseInfo | str, bool],]):
|
||||
tmp_path = find_tmp_path()
|
||||
|
||||
while True:
|
||||
@@ -73,144 +132,55 @@ def rip_worker(
|
||||
time.sleep(1)
|
||||
continue
|
||||
|
||||
status_callback("Gathering Info", False)
|
||||
|
||||
url = rip_queue.pop(0)
|
||||
create_or_wipe_folder(tmp_path)
|
||||
|
||||
current_url = rip_queue.pop(0)
|
||||
release_info = ReleaseInfo(
|
||||
"",
|
||||
current_url,
|
||||
"https://imgs.search.brave.com/LeS4HHKZ1oz1T15VY5MwiUjWDjLiYKj0vgRABB3D2BY/rs:fit:860:0:0:0/g:ce/aHR0cHM6Ly90My5m/dGNkbi5uZXQvanBn/LzA2Lzg2LzE5LzM0/LzM2MF9GXzY4NjE5/MzQwN19ESFp3amV5/ZEJPUjF0RURrTEF6/d00zdzVrWXN0Unp6/Qi5qcGc",
|
||||
"Starting",
|
||||
)
|
||||
status_callback(release_info)
|
||||
|
||||
is_album = False
|
||||
is_topic = False
|
||||
|
||||
# Info json --------------------------------------------------
|
||||
|
||||
release_info.status = "Gathering Info"
|
||||
status_callback(release_info)
|
||||
|
||||
try:
|
||||
info = get_content_json(current_url)
|
||||
info = find_release_info(url)
|
||||
status_callback(info, False)
|
||||
|
||||
except DownloadError:
|
||||
release_info.status = "Info Download Failed"
|
||||
finish_callback(release_info)
|
||||
continue
|
||||
status_callback("Downloading Content", False)
|
||||
|
||||
except ExtractorError:
|
||||
release_info.status = "Info Extraction Failed"
|
||||
finish_callback(release_info)
|
||||
continue
|
||||
download_content(tmp_path, url)
|
||||
|
||||
entries = info.get("entries")
|
||||
if entries is None:
|
||||
release_info.status = "Entries is none"
|
||||
finish_callback(release_info)
|
||||
continue
|
||||
except RipError as e:
|
||||
status_callback(repr(e), True)
|
||||
|
||||
first_entry = next(iter(entries), None)
|
||||
if not first_entry:
|
||||
release_info.status = "Entries is empty"
|
||||
finish_callback(release_info)
|
||||
continue
|
||||
|
||||
artist = first_entry.get("uploader")
|
||||
if artist is None:
|
||||
release_info.status = "Uploader tag is none"
|
||||
finish_callback(release_info)
|
||||
continue
|
||||
|
||||
if " - Topic" in artist:
|
||||
is_topic = True
|
||||
artist = artist.replace(" - Topic", "")
|
||||
|
||||
release_info.artist = artist
|
||||
|
||||
title = info.get("title")
|
||||
if title is None:
|
||||
release_info.status = "Title tag is none"
|
||||
finish_callback(release_info)
|
||||
continue
|
||||
|
||||
if "Album - " in title:
|
||||
is_album = True
|
||||
title = title.replace("Album - ", "")
|
||||
|
||||
release_info.title = title
|
||||
|
||||
thumbnails = info.get("thumbnails")
|
||||
if thumbnails is None:
|
||||
release_info.status = "Thumbnails is none"
|
||||
finish_callback(release_info)
|
||||
continue
|
||||
|
||||
art_url = thumbnails[1]["url"]
|
||||
release_info.art_url = art_url
|
||||
|
||||
if not is_album:
|
||||
release_info.status = "Failed, not an album"
|
||||
finish_callback(release_info)
|
||||
continue
|
||||
|
||||
release_info.status = "Finished gathering info"
|
||||
status_callback(release_info)
|
||||
|
||||
# Info json --------------------------------------------------
|
||||
|
||||
release_info.status = "Downloading"
|
||||
status_callback(release_info)
|
||||
|
||||
try:
|
||||
download_content(tmp_path, current_url)
|
||||
|
||||
except DownloadError:
|
||||
release_info.status = "Content Download Failed"
|
||||
finish_callback(release_info)
|
||||
continue
|
||||
|
||||
except YtdlpErrorCode:
|
||||
release_info.status = "Content Download Failed with code"
|
||||
finish_callback(release_info)
|
||||
continue
|
||||
|
||||
except ExtractorError:
|
||||
release_info.status = "Content Extraction Failed"
|
||||
finish_callback(release_info)
|
||||
continue
|
||||
|
||||
release_info.status = "Writting Metadata"
|
||||
status_callback(release_info)
|
||||
|
||||
release_info.status = "Finished Successfully"
|
||||
finish_callback(release_info)
|
||||
else:
|
||||
status_callback("Finished Sucessfully", True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
app = Flask(__name__)
|
||||
rip_queue = []
|
||||
finished_queue = []
|
||||
rip_queue: list[str] = []
|
||||
finished_queue: list[ReleaseStatus] = []
|
||||
|
||||
current_release_info = ReleaseInfo("", "", "", "Waiting")
|
||||
current_release_info: ReleaseInfo | None = None
|
||||
current_release_status_message: str = ""
|
||||
|
||||
def get_current_release_info(release_info: ReleaseInfo):
|
||||
global current_release_info
|
||||
current_release_info = release_info
|
||||
|
||||
def get_current_release_info_on_finish(release_info: ReleaseInfo):
|
||||
def get_current_release_status(x: ReleaseInfo | str, finished: bool):
|
||||
global current_release_info
|
||||
global current_release_status_message
|
||||
global finished_queue
|
||||
current_release_info = ReleaseInfo("", "", "", "Waiting")
|
||||
finished_queue.append(release_info)
|
||||
if len(finished_queue) > 30:
|
||||
finished_queue.pop(0)
|
||||
|
||||
ripping_thread = Thread(
|
||||
target=rip_worker, args=(rip_queue, get_current_release_info, get_current_release_info_on_finish), daemon=True
|
||||
).start()
|
||||
if type(x) is ReleaseInfo:
|
||||
current_release_info = x
|
||||
|
||||
elif type(x) is str:
|
||||
current_release_status_message = x
|
||||
|
||||
if finished:
|
||||
finished_queue.append(ReleaseStatus(current_release_info, current_release_status_message))
|
||||
current_release_info = None
|
||||
current_release_status_message = "Waiting"
|
||||
|
||||
if len(finished_queue) > 30:
|
||||
finished_queue.pop(0)
|
||||
|
||||
ripping_thread = Thread(target=rip_worker, args=(rip_queue, get_current_release_status), daemon=True).start()
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
@@ -219,26 +189,31 @@ if __name__ == "__main__":
|
||||
@app.route("/rip", methods=["POST"])
|
||||
def rip():
|
||||
url = request.form.get("url")
|
||||
if url is None:
|
||||
return jsonify({"success": False, "message": "Url is None"})
|
||||
|
||||
rip_queue.append(url)
|
||||
return jsonify({"success": True, "message": f"Added, {url}!"})
|
||||
|
||||
@app.route("/status", methods=["GET"])
|
||||
def status():
|
||||
return jsonify(
|
||||
{
|
||||
"success": True,
|
||||
"queue": rip_queue,
|
||||
"finishedQueue": [
|
||||
{"artist": q.artist, "title": q.title, "status": q.status, "art_url": q.art_url}
|
||||
for q in finished_queue
|
||||
],
|
||||
"currentReleaseInfo": {
|
||||
"artist": current_release_info.artist,
|
||||
"title": current_release_info.title,
|
||||
"status": current_release_info.status,
|
||||
"art_url": current_release_info.art_url,
|
||||
},
|
||||
data = {"success": True, "queue": rip_queue, "finishedQueue": [], "status": current_release_status_message}
|
||||
|
||||
for q in finished_queue:
|
||||
if q.info is None:
|
||||
continue
|
||||
|
||||
data["finishedQueue"].append(
|
||||
{"artist": q.info.artist, "title": q.info.title, "status": q.status, "art_url": q.info.art_url}
|
||||
)
|
||||
|
||||
if current_release_info is not None:
|
||||
data["currentReleaseInfo"] = {
|
||||
"artist": current_release_info.artist,
|
||||
"title": current_release_info.title,
|
||||
"art_url": current_release_info.art_url,
|
||||
}
|
||||
)
|
||||
|
||||
return jsonify(data)
|
||||
|
||||
app.run()
|
||||
|
||||
Reference in New Issue
Block a user