diff --git a/.vscode/launch.json b/.vscode/launch.json index e2cd4cf5..114acd41 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -33,8 +33,7 @@ "YTP_DOWNLOAD_PATH": "${workspaceFolder}/var/downloads", "YTP_TEMP_PATH": "${workspaceFolder}/var/tmp", "YTP_YTDL_OPTIONS_FILE": "${workspaceFolder}/var/config/ytdlp.json", - "YTP_URL_HOST": "http://localhost:8081", - "YTP_KEEP_ARCHIVE": "true", + "YTP_URL_HOST": "http://localhost:8081" } } ] diff --git a/app/src/Config.py b/app/src/Config.py index 94b8dd64..500c726c 100644 --- a/app/src/Config.py +++ b/app/src/Config.py @@ -23,6 +23,7 @@ class Config: ytdl_options: dict | str = {} ytdl_options_file: str = '' + ytdl_debug: bool = False host: str = '0.0.0.0' port: int = 8081 @@ -31,7 +32,7 @@ class Config: base_path: str = '' - _boolean_vars: tuple = ('keep_archive') + _boolean_vars: tuple = ('keep_archive', 'ytdl_debug') def __init__(self): baseDefualtPath: str = os.path.dirname(os.path.dirname(__file__)) diff --git a/app/src/DTO/ItemDTO.py b/app/src/DTO/ItemDTO.py index 48c11103..c80a5f64 100644 --- a/app/src/DTO/ItemDTO.py +++ b/app/src/DTO/ItemDTO.py @@ -19,7 +19,7 @@ class ItemDTO: ytdlp_cookies: str = None ytdlp_config: dict = field(default_factory=dict) output_template: str = None - timestamp: int = time.time_ns() + timestamp: float = time.time_ns() is_live: bool = None # yt-dlp injected fields. diff --git a/app/src/DataStore.py b/app/src/DataStore.py index 76107ac7..c14d4f23 100644 --- a/app/src/DataStore.py +++ b/app/src/DataStore.py @@ -50,7 +50,7 @@ class DataStore: with sqlite3.connect(self.db_file) as db: db.row_factory = sqlite3.Row cursor = db.execute( - 'SELECT "id", "data" FROM "history" WHERE "type" = ? ORDER BY "created_at" ASC', + f'SELECT "id", "data" FROM "history" WHERE "type" = ? ORDER BY "created_at" ASC', (self.type,) ) diff --git a/app/src/Download.py b/app/src/Download.py index 321a89bc..74203090 100644 --- a/app/src/Download.py +++ b/app/src/Download.py @@ -22,6 +22,7 @@ class Download: ytdl_opts: dict = None info: ItemDTO = None default_ytdl_opts: dict = None + debug: bool = False _ytdlp_fields: tuple = ( 'tmpfilename', @@ -41,16 +42,19 @@ class Download: download_dir: str, temp_dir: str, output_template_chapter: str, - default_ytdl_opts: dict + default_ytdl_opts: dict, + debug: bool = False ): self.download_dir = download_dir self.temp_dir = temp_dir self.output_template_chapter = output_template_chapter self.output_template = info.output_template self.format = get_format(info.format, info.quality) - self.ytdl_opts = get_opts(info.format, info.quality, info.ytdlp_config if info.ytdlp_config else {}) + self.ytdl_opts = get_opts( + info.format, info.quality, info.ytdlp_config if info.ytdlp_config else {}) self.info = info self.default_ytdl_opts = default_ytdl_opts + self.debug = debug self.canceled = False self.tmpfilename = None @@ -82,7 +86,6 @@ class Download: ) params: dict = { - 'quiet': True, 'no_color': True, 'format': self.format, 'paths': { @@ -99,6 +102,12 @@ class Download: **mergeConfig(self.default_ytdl_opts, self.ytdl_opts), } + if self.debug: + params['verbose'] = True + params['logger'] = logging.getLogger('YTPTube-ytdl') + else: + params['quiet'] = True + if self.info.ytdlp_cookies: try: data = jsonCookie(json.loads(self.info.ytdlp_cookies)) @@ -113,6 +122,8 @@ class Download: logging.error( f'Invalid cookies: was provided for {self.info.title} - {str(e)}') + logging.debug( + f'Downloading {self.info._id} {self.info.title}... {params}') ret = yt_dlp.YoutubeDL(params=params).download([self.info.url]) self.status_queue.put( diff --git a/app/src/DownloadQueue.py b/app/src/DownloadQueue.py index caae61c3..126fe911 100644 --- a/app/src/DownloadQueue.py +++ b/app/src/DownloadQueue.py @@ -8,7 +8,7 @@ from src.Notifier import Notifier from src.Download import Download from src.DTO.ItemDTO import ItemDTO from src.DataStore import DataStore -from src.Utils import ObjectSerializer, calcDownloadPath, ExtractInfo +from src.Utils import ObjectSerializer, calcDownloadPath, ExtractInfo, mergeConfig from datetime import datetime, timezone log = logging.getLogger('DownloadQueue') @@ -129,6 +129,7 @@ class DownloadQueue: temp_dir=self.config.temp_path, output_template_chapter=output_chapter, default_ytdl_opts=self.config.ytdl_options, + debug=bool(self.config.ytdl_debug) ) ) @@ -163,6 +164,8 @@ class DownloadQueue: output_template: str = '', already=None ): + ytdlp_config = ytdlp_config if ytdlp_config else {} + log.info( f'adding {url}: {quality=} {format=} {folder=} {output_template=} {ytdlp_cookies=} {ytdlp_config=}') @@ -173,7 +176,19 @@ class DownloadQueue: else: already.add(url) try: - entry = await asyncio.get_running_loop().run_in_executor(None, ExtractInfo, self.config.ytdl_options, url) + entry = await asyncio.get_running_loop().run_in_executor( + None, + ExtractInfo, + mergeConfig(self.config.ytdl_options, ytdlp_config), + url, + bool(self.config.ytdl_debug) + ) + if not entry: + return { + 'status': 'error', + 'msg': 'No metadata, most likely video has been downloaded before.' if self.config.keep_archive else 'Unable to extract info check logs.' + } + logging.debug(f'entry: extract info says: {entry}') except yt_dlp.utils.YoutubeDLError as exc: return {'status': 'error', 'msg': str(exc)} diff --git a/app/src/Utils.py b/app/src/Utils.py index 4e7bdabc..3fbc0730 100644 --- a/app/src/Utils.py +++ b/app/src/Utils.py @@ -165,9 +165,8 @@ def calcDownloadPath(basePath: str, folder: str = None) -> str: return download_path -def ExtractInfo(config: dict, url: str) -> dict: +def ExtractInfo(config: dict, url: str, debug: bool = False) -> dict: params: dict = { - 'quiet': True, 'no_color': True, 'extract_flat': True, 'skip_download': True, @@ -176,6 +175,12 @@ def ExtractInfo(config: dict, url: str) -> dict: **config, } + if debug: + params['verbose'] = True + params['logger'] = logging.getLogger('YTPTube-ytdl') + else: + params['quiet'] = True + return yt_dlp.YoutubeDL(params=params).extract_info(url, download=False) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 34414034..0f1828b3 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,16 +1,17 @@ { - "name": "frontend", + "name": "YTPTube", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "frontend", + "name": "YTPTube", "version": "0.1.0", "dependencies": { "@vueuse/core": "^10.6.1", "bulma": "^0.9.4", "core-js": "^3.8.3", + "moment": "^2.29.4", "socket.io-client": "^4.7.2", "vue": "^3.2.13", "vue-toastification": "^2.0.0-rc.5" @@ -8021,6 +8022,14 @@ "integrity": "sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==", "dev": true }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "engines": { + "node": "*" + } + }, "node_modules/mrmime": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 5f1dd435..f0349d93 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,6 +12,7 @@ "@vueuse/core": "^10.6.1", "bulma": "^0.9.4", "core-js": "^3.8.3", + "moment": "^2.29.4", "socket.io-client": "^4.7.2", "vue": "^3.2.13", "vue-toastification": "^2.0.0-rc.5" diff --git a/frontend/src/components/Form-Add.vue b/frontend/src/components/Form-Add.vue index 1b4f0a29..58dc4e8e 100644 --- a/frontend/src/components/Form-Add.vue +++ b/frontend/src/components/Form-Add.vue @@ -156,12 +156,13 @@ const selectedQuality = useStorage('selectedQuality', '') const ytdlpConfig = useStorage('ytdlp_config', '') const ytdlpCookies = useStorage('ytdlp_cookies', '') const output_template = useStorage('output_template', null) -const qualities = ref([]) -const url = ref('') -const downloadPath = ref('') -const addInProgress = ref(false) +const downloadPath = useStorage('downloadPath', null) +const url = useStorage('downloadUrl', null) const showAdvanced = useStorage('show_advanced', false) +const qualities = ref([]) +const addInProgress = ref(false) + const updateQualities = () => { for (const key in downloadFormats) { const item = downloadFormats[key]; @@ -190,7 +191,7 @@ const addDownload = () => { url: url.value, format: selectedFormat.value, quality: selectedQuality.value, - path: downloadPath.value, + folder: downloadPath.value, ytdlp_config: ytdlpConfig.value, ytdlp_cookies: ytdlpCookies.value, output_template: output_template.value, @@ -206,6 +207,8 @@ const resetStorage = () => { ytdlpConfig.value = ''; ytdlpCookies.value = ''; output_template.value = null; + url.value = ''; + downloadPath.value = ''; } bus.on((event, data) => { diff --git a/frontend/src/components/Page-Completed.vue b/frontend/src/components/Page-Completed.vue index bb68289b..0462ec5f 100644 --- a/frontend/src/components/Page-Completed.vue +++ b/frontend/src/components/Page-Completed.vue @@ -74,7 +74,7 @@