Fixed type casting for some elements when adding items.

This commit is contained in:
ArabCoders 2025-01-19 18:12:11 +03:00
parent e7cc746ee9
commit 843d5fd726
6 changed files with 68 additions and 56 deletions

View file

@ -95,13 +95,13 @@ class Download:
def _progress_hook(self, data: dict): def _progress_hook(self, data: dict):
dataDict = {k: v for k, v in data.items() if k in self._ytdlp_fields} dataDict = {k: v for k, v in data.items() if k in self._ytdlp_fields}
if "finished" == data.get("status", None) and data.get("info_dict", {}).get("filename", False): if "finished" == data.get("status", None) and data.get("info_dict", {}).get("filename", None):
dataDict["filename"] = data["info_dict"]["filename"] dataDict["filename"] = data["info_dict"]["filename"]
self.status_queue.put({"id": self.id, **dataDict}) self.status_queue.put({"id": self.id, **dataDict})
def _postprocessor_hook(self, data: dict): def _postprocessor_hook(self, data: dict):
if data.get("postprocessor") != "MoveFiles" or data.get("status") != "finished": if "MoveFiles" != data.get("postprocessor") or "finished" != data.get("status"):
return return
if self.debug: if self.debug:
@ -116,24 +116,22 @@ class Download:
def _download(self): def _download(self):
try: try:
params: dict = { params: dict = get_opts(self.preset, mergeConfig(self.default_ytdl_opts, self.ytdl_opts))
"color": "no_color", params.update(
"paths": { {
"home": self.download_dir, "color": "no_color",
"temp": self.tempPath, "paths": {"home": self.download_dir, "temp": self.tempPath},
}, "outtmpl": {"default": self.output_template, "chapter": self.output_template_chapter},
"outtmpl": {"default": self.output_template, "chapter": self.output_template_chapter}, "noprogress": True,
"noprogress": True, "break_on_existing": True,
"break_on_existing": True, "progress_hooks": [self._progress_hook],
"progress_hooks": [self._progress_hook], "postprocessor_hooks": [self._postprocessor_hook],
"postprocessor_hooks": [self._postprocessor_hook], "ignoreerrors": False,
**get_opts(self.preset, mergeConfig(self.default_ytdl_opts, self.ytdl_opts)), }
} )
if "format" not in params and self.default_ytdl_opts.get("format", None): if "format" not in params and self.default_ytdl_opts.get("format", None):
params["format"] = self.default_ytdl_opts["format"] params["format"] = "best"
params["ignoreerrors"] = False
if self.debug: if self.debug:
params["verbose"] = True params["verbose"] = True
@ -151,7 +149,7 @@ class Download:
params["cookiefile"] = f.name params["cookiefile"] = f.name
except ValueError as e: except ValueError as e:
LOG.error(f"Invalid cookies: was provided for {self.info.title} - {str(e)}") LOG.error(f"Invalid cookies: was provided for '{self.info.title}'. '{str(e)}'.")
if self.is_live or self.is_manifestless: if self.is_live or self.is_manifestless:
hasDeletedOptions = False hasDeletedOptions = False
@ -176,7 +174,7 @@ class Download:
cls = yt_dlp.YoutubeDL(params=params) cls = yt_dlp.YoutubeDL(params=params)
if isinstance(self.info_dict, dict) and len(self.info_dict) > 1: if isinstance(self.info_dict, dict) and len(self.info_dict) > 1:
LOG.debug("Downloading using pre-info.") LOG.debug(f"Downloading '{self.info.url}' using pre-info.")
cls.process_ie_result(self.info_dict, download=True) cls.process_ie_result(self.info_dict, download=True)
ret = cls._download_retcode ret = cls._download_retcode
else: else:
@ -297,7 +295,7 @@ class Download:
if self.info.status != "finished" and self.is_live: if self.info.status != "finished" and self.is_live:
LOG.warning( LOG.warning(
f"Keeping live temp folder [{self.tempPath}], as the reported status is not finished [{self.info.status}]." f"Keeping live temp folder '{self.tempPath}', as the reported status is not finished '{self.info.status}'."
) )
return return
@ -310,7 +308,7 @@ class Download:
) )
return return
LOG.info(f"Deleting Temp folder: {self.tempPath}") LOG.info(f"Deleting Temp folder '{self.tempPath}'.")
shutil.rmtree(self.tempPath, ignore_errors=True) shutil.rmtree(self.tempPath, ignore_errors=True)
async def progress_update(self): async def progress_update(self):

View file

@ -80,8 +80,8 @@ class DownloadQueue:
live_in: str | None = None live_in: str | None = None
eventType = entry.get("_type") or "video" eventType = entry.get("_type") or "video"
if eventType == "playlist": if "playlist" == eventType:
entries = entry["entries"] entries = entry.get("entries", [])
playlist_index_digits = len(str(len(entries))) playlist_index_digits = len(str(len(entries)))
results = [] results = []
for i, etr in enumerate(entries, start=1): for i, etr in enumerate(entries, start=1):
@ -106,16 +106,16 @@ class DownloadQueue:
) )
) )
if any(res["status"] == "error" for res in results): if any("error" == res["status"] for res in results):
return { return {
"status": "error", "status": "error",
"msg": ", ".join(res["msg"] for res in results if res["status"] == "error" and "msg" in res), "msg": ", ".join(res["msg"] for res in results if res["status"] == "error" and "msg" in res),
} }
return {"status": "ok"} return {"status": "ok"}
elif (eventType == "video" or eventType.startswith("url")) and "id" in entry and "title" in entry: elif ("video" == eventType or eventType.startswith("url")) and "id" in entry and "title" in entry:
# check if the video is live stream. # check if the video is live stream.
if "live_status" in entry and entry.get("live_status") == "is_upcoming": if "live_status" in entry and "is_upcoming" == entry.get("live_status"):
if "release_timestamp" in entry and entry.get("release_timestamp"): if "release_timestamp" in entry and entry.get("release_timestamp"):
live_in = formatdate(entry.get("release_timestamp")) live_in = formatdate(entry.get("release_timestamp"))
else: else:
@ -230,10 +230,12 @@ class DownloadQueue:
already=None, already=None,
): ):
ytdlp_config = ytdlp_config if ytdlp_config else {} ytdlp_config = ytdlp_config if ytdlp_config else {}
folder = str(folder) folder = str(folder) if folder else ""
filePath = calcDownloadPath(basePath=self.config.download_path, folder=folder)
LOG.info( LOG.info(
f"Adding url '{url}' to folder '{folder}' with the following options 'Preset: {preset}' 'Naming: {output_template}', 'Cookies: {ytdlp_cookies}' 'YTConfig: {ytdlp_config}'." f"Adding 'URL: {url}' to 'Folder: {filePath}' with 'Preset: {preset}' 'Naming: {output_template}', 'Cookies: {ytdlp_cookies}' 'YTConfig: {ytdlp_config}'."
) )
if isinstance(ytdlp_config, str): if isinstance(ytdlp_config, str):
@ -279,13 +281,13 @@ class DownloadQueue:
f"extract_info: for 'URL: {url}' is done in '{time.perf_counter() - started}'. Length: '{len(entry)}'." f"extract_info: for 'URL: {url}' is done in '{time.perf_counter() - started}'. Length: '{len(entry)}'."
) )
except yt_dlp.utils.ExistingVideoReached as exc: except yt_dlp.utils.ExistingVideoReached as exc:
LOG.error(f"Video has been downloaded already and recorded in archive.log file. {str(exc)}") LOG.error(f"Video has been downloaded already and recorded in archive.log file. '{str(exc)}'.")
return {"status": "error", "msg": "Video has been downloaded already and recorded in archive.log file."} return {"status": "error", "msg": "Video has been downloaded already and recorded in archive.log file."}
except yt_dlp.utils.YoutubeDLError as exc: except yt_dlp.utils.YoutubeDLError as exc:
LOG.error(f"YoutubeDLError: Unable to extract info. {str(exc)}") LOG.error(f"YoutubeDLError: Unable to extract info. '{str(exc)}'.")
return {"status": "error", "msg": str(exc)} return {"status": "error", "msg": str(exc)}
except asyncio.exceptions.TimeoutError as exc: except asyncio.exceptions.TimeoutError as exc:
LOG.error(f"TimeoutError: Unable to extract info. {str(exc)}") LOG.error(f"TimeoutError: Unable to extract info. '{str(exc)}'.")
return { return {
"status": "error", "status": "error",
"msg": f"TimeoutError: {self.config.extract_info_timeout}s reached Unable to extract info.", "msg": f"TimeoutError: {self.config.extract_info_timeout}s reached Unable to extract info.",
@ -366,7 +368,7 @@ class DownloadQueue:
os.remove(realFile) os.remove(realFile)
fileDeleted = True fileDeleted = True
else: else:
LOG.warning(f"File '{filename}' does not exist.") LOG.warning(f"Failed to remove '{itemRef}' local file '{filename}'. File not found.")
except Exception as e: except Exception as e:
LOG.error(f"Unable to remove '{itemRef}' local file '{filename}'. {str(e)}") LOG.error(f"Unable to remove '{itemRef}' local file '{filename}'. {str(e)}")
@ -448,8 +450,9 @@ class DownloadQueue:
await asyncio.sleep(1) await asyncio.sleep(1)
async def __downloadFile(self, id: str, entry: Download): async def __downloadFile(self, id: str, entry: Download):
filePath = calcDownloadPath(basePath=self.config.download_path, folder=entry.info.folder)
LOG.info( LOG.info(
f"Downloading 'id: {id}', 'Title: {entry.info.title}', 'URL: {entry.info.url}' to 'Folder: {entry.info.folder}'." f"Downloading 'id: {id}', 'Title: {entry.info.title}', 'URL: {entry.info.url}' to 'Folder: {filePath}'."
) )
try: try:

View file

@ -222,24 +222,32 @@ class HttpAPI(common):
post = await request.json() post = await request.json()
url: str = post.get("url") url: str = post.get("url")
preset: str = post.get("preset", "default")
if not url: if not url:
return web.json_response(data={"error": "url is required."}, status=web.HTTPBadRequest.status_code) return web.json_response(data={"error": "url is required."}, status=web.HTTPBadRequest.status_code)
folder: str = post.get("folder") preset: str = str(post.get("preset", self.config.default_preset))
ytdlp_cookies: str = post.get("ytdlp_cookies") folder: str = str(post.get("folder")) if post.get("folder") else ""
ytdlp_config: dict | None = post.get("ytdlp_config") ytdlp_cookies: str = str(post.get("ytdlp_cookies")) if post.get("ytdlp_cookies") else ""
output_template: str = post.get("output_template") output_template: str = str(post.get("output_template")) if post.get("output_template") else ""
if ytdlp_config is None:
ytdlp_config = {} ytdlp_config = post.get("ytdlp_config")
if isinstance(ytdlp_config, str) and ytdlp_config:
try:
ytdlp_config = json.loads(ytdlp_config)
except Exception as e:
LOG.error(f"Failed to parse json yt-dlp config for '{url}'. {str(e)}")
return web.json_response(
data={"error": f"Failed to parse json yt-dlp config. {str(e)}"},
status=web.HTTPBadRequest.status_code,
)
status = await self.add( status = await self.add(
url=url, url=url,
preset=preset, preset=preset,
folder=folder, folder=folder,
ytdlp_cookies=ytdlp_cookies, ytdlp_cookies=ytdlp_cookies,
ytdlp_config=ytdlp_config, ytdlp_config=ytdlp_config if isinstance(ytdlp_config, dict) else {},
output_template=output_template, output_template=output_template,
) )

View file

@ -1,12 +1,13 @@
import asyncio import asyncio
import errno import errno
import functools import functools
import json
import logging import logging
import os import os
import pty import pty
import shlex import shlex
from datetime import datetime
import time import time
from datetime import datetime
import socketio import socketio
from aiohttp import web from aiohttp import web
@ -161,20 +162,25 @@ class HttpSocket(common):
await self.emitter.warning("No URL provided.", to=sid) await self.emitter.warning("No URL provided.", to=sid)
return return
preset: str = str(data.get("preset", "default")) preset: str = str(data.get("preset", self.config.default_preset))
folder: str = str(data.get("folder")) folder: str = str(data.get("folder")) if data.get("folder") else ""
ytdlp_cookies: str = str(data.get("ytdlp_cookies")) ytdlp_cookies: str = str(data.get("ytdlp_cookies")) if data.get("ytdlp_cookies") else ""
ytdlp_config: dict | None = data.get("ytdlp_config") output_template: str = str(data.get("output_template")) if data.get("output_template") else ""
output_template: str = data.get("output_template")
if ytdlp_config is None: ytdlp_config = data.get("ytdlp_config")
ytdlp_config = {} if isinstance(ytdlp_config, str) and ytdlp_config:
try:
ytdlp_config = json.loads(ytdlp_config)
except Exception as e:
await self.emitter.warning(f"Failed to parse json yt-dlp config. {str(e)}", to=sid)
return
status = await self.add( status = await self.add(
url=url, url=url,
preset=preset, preset=preset,
folder=folder, folder=folder,
ytdlp_cookies=ytdlp_cookies, ytdlp_cookies=ytdlp_cookies,
ytdlp_config=ytdlp_config, ytdlp_config=ytdlp_config if isinstance(ytdlp_config, dict) else {},
output_template=output_template, output_template=output_template,
) )

View file

@ -109,7 +109,7 @@ def getVideoInfo(url: str, ytdlp_opts: dict = None, no_archive: bool = True) ->
return yt_dlp.YoutubeDL().extract_info(url, download=False) return yt_dlp.YoutubeDL().extract_info(url, download=False)
def calcDownloadPath(basePath: str, folder: str = None, createPath: bool = True) -> str: def calcDownloadPath(basePath: str, folder: str|None = None, createPath: bool = True) -> str:
"""Calculates download path and prevents folder traversal. """Calculates download path and prevents folder traversal.
Returns: Returns:

View file

@ -22,9 +22,6 @@ class common:
async def add( async def add(
self, url: str, preset: str, folder: str, ytdlp_cookies: str, ytdlp_config: dict, output_template: str self, url: str, preset: str, folder: str, ytdlp_cookies: str, ytdlp_config: dict, output_template: str
) -> dict[str, str]: ) -> dict[str, str]:
if ytdlp_config is None:
ytdlp_config = {}
if ytdlp_cookies and isinstance(ytdlp_cookies, dict): if ytdlp_cookies and isinstance(ytdlp_cookies, dict):
ytdlp_cookies = self.encoder.encode(ytdlp_cookies) ytdlp_cookies = self.encoder.encode(ytdlp_cookies)
@ -33,7 +30,7 @@ class common:
preset=preset if preset else "default", preset=preset if preset else "default",
folder=folder, folder=folder,
ytdlp_cookies=ytdlp_cookies, ytdlp_cookies=ytdlp_cookies,
ytdlp_config=ytdlp_config, ytdlp_config=ytdlp_config if isinstance(ytdlp_config, dict) else {},
output_template=output_template, output_template=output_template,
) )