forked from Ethanscharlie/Happy-Ripper
Cleaned code
This commit is contained in:
@@ -12,3 +12,4 @@ build/
|
||||
.idea/
|
||||
*.swp
|
||||
*~
|
||||
tmp
|
||||
|
||||
+118
-143
@@ -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,6 +47,7 @@ def get_content_json(url: str):
|
||||
|
||||
|
||||
def download_content(path: str, url: str) -> None:
|
||||
try:
|
||||
with YoutubeDL(
|
||||
{
|
||||
"format": "m4a/bestaudio/best",
|
||||
@@ -55,7 +62,61 @@ def download_content(path: str, url: str) -> None:
|
||||
) as ydl:
|
||||
error_code = ydl.download([url])
|
||||
if error_code:
|
||||
raise YtdlpErrorCode(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 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_info, get_current_release_info_on_finish), daemon=True
|
||||
).start()
|
||||
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()
|
||||
|
||||
@@ -106,8 +106,13 @@
|
||||
const response = await fetch("/status", { method: "GET" });
|
||||
const statusJson = await response.json();
|
||||
|
||||
if ("currentReleaseInfo" in statusJson) {
|
||||
const currentInfo = statusJson.currentReleaseInfo;
|
||||
statusMessage.innerHTML = createCard(currentInfo.artist, currentInfo.title, currentInfo.status, currentInfo.art_url);
|
||||
statusMessage.innerHTML = createCard(currentInfo.artist, currentInfo.title, statusJson.status, currentInfo.art_url);
|
||||
}
|
||||
else {
|
||||
statusMessage.innerHTML = `<p>${statusJson.status}</p>`;
|
||||
}
|
||||
|
||||
queueTableBody.innerHTML = "";
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user