From 677af3712bbf5748277a5d88d85fb0cf719210b9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Sep 2021 19:35:35 +0000 Subject: [PATCH 01/15] Bump axios from 0.21.1 to 0.21.2 in /backend Bumps [axios](https://github.com/axios/axios) from 0.21.1 to 0.21.2. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/master/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v0.21.1...v0.21.2) --- updated-dependencies: - dependency-name: axios dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- backend/package-lock.json | 14 +++++++------- backend/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index f68e7c4..63a7e66 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -316,11 +316,11 @@ "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==" }, "axios": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", - "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.2.tgz", + "integrity": "sha512-87otirqUw3e8CzHTMO+/9kh/FSgXt/eVDvipijwDtEuwbkySWZ9SBm6VEubmJ/kLKEoLQV/POhxXFb66bfekfg==", "requires": { - "follow-redirects": "^1.10.0" + "follow-redirects": "^1.14.0" } }, "backoff": { @@ -1267,9 +1267,9 @@ } }, "follow-redirects": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz", - "integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==" + "version": "1.14.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz", + "integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==" }, "forever-agent": { "version": "0.6.1", diff --git a/backend/package.json b/backend/package.json index 8d4579d..ac96767 100644 --- a/backend/package.json +++ b/backend/package.json @@ -33,7 +33,7 @@ "archiver": "^3.1.1", "async": "^3.1.0", "async-mutex": "^0.3.1", - "axios": "^0.21.1", + "axios": "^0.21.2", "bcryptjs": "^2.4.0", "compression": "^1.7.4", "config": "^3.2.3", From 1e9eec1b553c534e57afdb3290c9ef54dd9ee5a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Sep 2021 19:36:01 +0000 Subject: [PATCH 02/15] Bump electron from 8.2.0 to 9.4.0 Bumps [electron](https://github.com/electron/electron) from 8.2.0 to 9.4.0. - [Release notes](https://github.com/electron/electron/releases) - [Changelog](https://github.com/electron/electron/blob/master/docs/breaking-changes.md) - [Commits](https://github.com/electron/electron/compare/v8.2.0...v9.4.0) Signed-off-by: dependabot[bot] --- package-lock.json | 165 +++++++++++++++++++++------------------------- package.json | 2 +- 2 files changed, 75 insertions(+), 92 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2ac2dfa..cad23bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1622,9 +1622,9 @@ } }, "@electron/get": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.9.0.tgz", - "integrity": "sha512-OBIKtF6ttIJotDXe4KJMUyTBO4xMii+mFjlA8R4CORuD4HvCUaCK3lPjhdTRCvuEv6gzWNbAvd9DNBv0v780lw==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.13.0.tgz", + "integrity": "sha512-+SjZhRuRo+STTO1Fdhzqnv9D2ZhjxXP6egsJ9kiO8dtP68cDx7dFCwWi64dlMQV7sWcfW1OYCW4wviEBzmRsfQ==", "dev": true, "requires": { "debug": "^4.1.1", @@ -1634,7 +1634,7 @@ "global-tunnel-ng": "^2.7.1", "got": "^9.6.0", "progress": "^2.0.3", - "sanitize-filename": "^1.6.2", + "semver": "^6.2.0", "sumchecker": "^3.0.1" }, "dependencies": { @@ -1648,6 +1648,12 @@ "jsonfile": "^4.0.0", "universalify": "^0.1.0" } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true } } }, @@ -3015,9 +3021,9 @@ "dev": true }, "boolean": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.0.1.tgz", - "integrity": "sha512-HRZPIjPcbwAVQvOTxR4YE3o8Xs98NqbbL1iEZDCz7CL8ql0Lt5iOyJFxfnAB0oFs8Oh02F/lLlg30Mexv46LjA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.1.4.tgz", + "integrity": "sha512-3hx0kwU3uzG6ReQ3pnaFQPSktpBw6RHN3/ivDKEuU8g1XSfafowyvDnadjv1xp8IZqhtSukxlwv9bF6FhX8m0w==", "dev": true, "optional": true }, @@ -3298,9 +3304,9 @@ }, "dependencies": { "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, "requires": { "pump": "^3.0.0" @@ -3313,9 +3319,9 @@ "dev": true }, "normalize-url": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", - "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", "dev": true } } @@ -3774,9 +3780,9 @@ } }, "config-chain": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", - "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", "dev": true, "optional": true, "requires": { @@ -4799,9 +4805,9 @@ "dev": true }, "electron": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/electron/-/electron-8.2.0.tgz", - "integrity": "sha512-mnV43gKCrCUMHLmGws/DU/l8LhaxrFD53A4ofwtthdCqOZWGIdk1+eMphiVumXR5a3lC64XVvmXQ2k28i7F/zw==", + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-9.4.0.tgz", + "integrity": "sha512-hOC4q0jkb+UDYZRy8vrZ1IANnq+jznZnbkD62OEo06nU+hIbp2IrwDRBNuSLmQ3cwZMVir0WSIA1qEVK0PkzGA==", "dev": true, "requires": { "@electron/get": "^1.0.1", @@ -5007,9 +5013,9 @@ "dev": true }, "env-paths": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", - "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true }, "err-code": { @@ -6276,34 +6282,37 @@ } }, "global-agent": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-2.1.8.tgz", - "integrity": "sha512-VpBe/rhY6Rw2VDOTszAMNambg+4Qv8j0yiTNDYEXXXxkUNGWLHp8A3ztK4YDBbFNcWF4rgsec6/5gPyryya/+A==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-2.2.0.tgz", + "integrity": "sha512-+20KpaW6DDLqhG7JDiJpD1JvNvb8ts+TNl7BPOYcURqCrXqnN1Vf+XVOrkKJAFPqfX+oEhsdzOj1hLWkBTdNJg==", "dev": true, "optional": true, "requires": { - "boolean": "^3.0.0", - "core-js": "^3.6.4", + "boolean": "^3.0.1", + "core-js": "^3.6.5", "es6-error": "^4.1.1", - "matcher": "^2.1.0", - "roarr": "^2.15.2", - "semver": "^7.1.2", - "serialize-error": "^5.0.0" + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" }, "dependencies": { "core-js": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz", - "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==", + "version": "3.17.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.17.3.tgz", + "integrity": "sha512-lyvajs+wd8N1hXfzob1LdOCCHFU4bGMbqqmLn1Q4QlCpDqWPpGf+p0nj+LNrvDDG33j0hZXw2nsvvVpHysxyNw==", "dev": true, "optional": true }, "semver": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", - "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, - "optional": true + "optional": true, + "requires": { + "lru-cache": "^6.0.0" + } } } }, @@ -6326,9 +6335,9 @@ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" }, "globalthis": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.1.tgz", - "integrity": "sha512-mJPRTc/P39NH/iNG4mXa9aIhNymaQikTrnspeCa2ZuJ+mH2QN/rXwtX3XwKrHqWgUQFbNZKtHM105aHzJalElw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", + "integrity": "sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==", "dev": true, "optional": true, "requires": { @@ -8427,19 +8436,19 @@ } }, "matcher": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/matcher/-/matcher-2.1.0.tgz", - "integrity": "sha512-o+nZr+vtJtgPNklyeUKkkH42OsK8WAfdgaJE2FNxcjLPg+5QbeEoT6vRj8Xq/iv18JlQ9cmKsEu0b94ixWf1YQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", "dev": true, "optional": true, "requires": { - "escape-string-regexp": "^2.0.0" + "escape-string-regexp": "^4.0.0" }, "dependencies": { "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "optional": true } @@ -10662,6 +10671,12 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true + }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -11717,13 +11732,13 @@ } }, "roarr": { - "version": "2.15.2", - "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.2.tgz", - "integrity": "sha512-jmaDhK9CO4YbQAV8zzCnq9vjAqeO489MS5ehZ+rXmFiPFFE6B+S9KYO6prjmLJ5A0zY3QxVlQdrIya7E/azz/Q==", + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", "dev": true, "optional": true, "requires": { - "boolean": "^3.0.0", + "boolean": "^3.0.1", "detect-node": "^2.0.4", "globalthis": "^1.0.1", "json-stringify-safe": "^5.0.1", @@ -11810,15 +11825,6 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, - "sanitize-filename": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", - "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", - "dev": true, - "requires": { - "truncate-utf8-bytes": "^1.0.0" - } - }, "sass": { "version": "1.27.0", "resolved": "https://registry.npmjs.org/sass/-/sass-1.27.0.tgz", @@ -12033,19 +12039,19 @@ } }, "serialize-error": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-5.0.0.tgz", - "integrity": "sha512-/VtpuyzYf82mHYTtI4QKtwHa79vAdU5OQpNPAmE/0UDdlGT0ZxHwC+J6gXkw29wwoVI8fMPsfcVHOwXtUQYYQA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", "dev": true, "optional": true, "requires": { - "type-fest": "^0.8.0" + "type-fest": "^0.13.1" }, "dependencies": { "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", "dev": true, "optional": true } @@ -13403,15 +13409,6 @@ "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true }, - "truncate-utf8-bytes": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", - "integrity": "sha1-QFkjkJWS1W94pYGENLC3hInKXys=", - "dev": true, - "requires": { - "utf8-byte-length": "^1.0.1" - } - }, "ts-md5": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/ts-md5/-/ts-md5-1.2.7.tgz", @@ -13843,14 +13840,6 @@ "dev": true, "requires": { "prepend-http": "^2.0.0" - }, - "dependencies": { - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "dev": true - } } }, "use": { @@ -13865,12 +13854,6 @@ "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=", "dev": true }, - "utf8-byte-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", - "integrity": "sha1-9F8VDExm7uloGGUFq5P8u4rWv2E=", - "dev": true - }, "util": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", diff --git a/package.json b/package.json index ad1f6f9..57b692e 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "@typescript-eslint/eslint-plugin": "^4.29.0", "@typescript-eslint/parser": "^4.29.0", "codelyzer": "^6.0.0", - "electron": "^8.0.1", + "electron": "^9.4.0", "eslint": "^7.32.0", "jasmine-core": "~3.6.0", "jasmine-spec-reporter": "~5.0.0", From bd2443b1e94753a282d972c8a4d3064eaccc2464 Mon Sep 17 00:00:00 2001 From: GlassedSilver Date: Sat, 18 Sep 2021 16:59:49 +0200 Subject: [PATCH 03/15] docker-compose.yml: Use YoutubeDL-Material nightly --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 62c33fc..2d0d73e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,7 +15,7 @@ services: - ./users:/app/users ports: - "8998:17442" - image: tzahi12345/youtubedl-material:latest + image: tzahi12345/youtubedl-material:nightly ytdl-mongo-db: image: mongo ports: From f7e0b3e86b58b8f55383c9f66845b2e217533f50 Mon Sep 17 00:00:00 2001 From: GlassedSilver Date: Sat, 18 Sep 2021 17:22:11 +0200 Subject: [PATCH 04/15] =?UTF-8?q?[DRAFT!]=20Bump=20alpine:=20pinned=20'3.1?= =?UTF-8?q?2'=20=E2=86=92=20'latest'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0be5ff4..d2b9d75 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.12 as frontend +FROM alpine:latest as frontend RUN apk add --no-cache \ npm @@ -15,7 +15,7 @@ RUN ng build --prod #--------------# -FROM alpine:3.12 +FROM alpine:latest ENV UID=1000 \ GID=1000 \ From 176c99f813be95c0524b284b57c5890490b5d1d9 Mon Sep 17 00:00:00 2001 From: GlassedSilver Date: Sat, 18 Sep 2021 17:41:25 +0200 Subject: [PATCH 05/15] Reference host-specific instructions --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 8f6fdde..b452bec 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,10 @@ Alternatively, you can port forward the port specified in the config (defaults t ## Docker +### Host-specific instructions + +If you're on a Synology NAS, unRAID or any other possible special case you can check if there's known issues or instructions both in the issue tracker and in the [Wiki!](https://github.com/Tzahi12345/YoutubeDL-Material/wiki#environment-specific-guideshelp) + ### Setup If you are looking to setup YoutubeDL-Material with Docker, this section is for you. And you're in luck! Docker setup is quite simple. From 33f23c3ca9a04d9e3e5467fb4c664c5e210dcfab Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Sun, 19 Sep 2021 14:24:18 -0400 Subject: [PATCH 06/15] Fixed issue where youtube-dl autoupdates broke if checkExistsWithTimeout failed the first time --- backend/app.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/app.js b/backend/app.js index 173e6ed..7404f3c 100644 --- a/backend/app.js +++ b/backend/app.js @@ -837,7 +837,7 @@ async function checkExistsWithTimeout(filePath, timeout) { fs.access(filePath, fs.constants.R_OK, function (err) { if (!err) { clearTimeout(timer); - watcher.close(); + if (watcher) watcher.close(); resolve(); } }); @@ -847,7 +847,7 @@ async function checkExistsWithTimeout(filePath, timeout) { var watcher = fs.watch(dir, function (eventType, filename) { if (eventType === 'rename' && filename === basename) { clearTimeout(timer); - watcher.close(); + if (watcher) watcher.close(); resolve(); } }); From 759637c1cfa53d1304dd9afd72c221509fe6a278 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Sun, 19 Sep 2021 14:29:12 -0400 Subject: [PATCH 07/15] Fixed issue where per-subscription custom args were not being applied --- backend/subscriptions.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/subscriptions.js b/backend/subscriptions.js index 244cb70..568d06e 100644 --- a/backend/subscriptions.js +++ b/backend/subscriptions.js @@ -329,7 +329,8 @@ function generateOptionsForSubscriptionDownload(sub, user_uid) { selectedHeight: sub.maxQuality && sub.maxQuality !== 'best' ? sub.maxQuality : null, customFileFolderPath: getAppendedBasePath(sub, basePath), customOutput: sub.custom_output ? `${sub.custom_output}` : `${default_output}`, - customArchivePath: path.join(__dirname, basePath, 'archives', sub.name) + customArchivePath: path.join(__dirname, basePath, 'archives', sub.name), + additionalArgs: sub.custom_args } return base_download_options; From a71d9f5c7e1ba7c2ed3c3d3b96c8ee398baadcda Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Sun, 19 Sep 2021 14:44:02 -0400 Subject: [PATCH 08/15] Added tests for arg generation and laid some plumbing for better arg simulation in the UI --- backend/downloader.js | 6 +++--- backend/subscriptions.js | 3 ++- backend/test/tests.js | 14 ++++++++++++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/backend/downloader.js b/backend/downloader.js index ec43b5d..16aaaa8 100644 --- a/backend/downloader.js +++ b/backend/downloader.js @@ -190,7 +190,7 @@ async function collectInfo(download_uid) { options.customFileFolderPath = user_path + path.sep; } - let args = await generateArgs(url, type, options, download['user_uid']); + let args = await exports.generateArgs(url, type, options, download['user_uid']); // get video info prior to download let info = await getVideoInfoByURL(url, args, download_uid); @@ -209,7 +209,7 @@ async function collectInfo(download_uid) { if (category && category['custom_output']) { options.customOutput = category['custom_output']; options.noRelativePath = true; - args = await generateArgs(url, type, options, download['user_uid']); + args = await exports.generateArgs(url, type, options, download['user_uid']); info = await getVideoInfoByURL(url, args, download_uid); } @@ -369,7 +369,7 @@ async function downloadQueuedFile(download_uid) { // helper functions -async function generateArgs(url, type, options, user_uid = null) { +exports.generateArgs = async (url, type, options, user_uid = null) => { const audioFolderPath = config_api.getConfigItem('ytdl_audio_folder_path'); const videoFolderPath = config_api.getConfigItem('ytdl_video_folder_path'); diff --git a/backend/subscriptions.js b/backend/subscriptions.js index 568d06e..90b6873 100644 --- a/backend/subscriptions.js +++ b/backend/subscriptions.js @@ -543,5 +543,6 @@ module.exports = { deleteSubscriptionFile : deleteSubscriptionFile, getVideosForSub : getVideosForSub, initialize : initialize, - updateSubscriptionPropertyMultiple : updateSubscriptionPropertyMultiple + updateSubscriptionPropertyMultiple : updateSubscriptionPropertyMultiple, + generateOptionsForSubscriptionDownload: generateOptionsForSubscriptionDownload } diff --git a/backend/test/tests.js b/backend/test/tests.js index 1824cff..99b79b2 100644 --- a/backend/test/tests.js +++ b/backend/test/tests.js @@ -293,6 +293,7 @@ describe('Downloader', function() { const downloader_api = require('../downloader'); downloader_api.initialize(db_api); const url = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'; + const sub_id = 'dc834388-3454-41bf-a618-e11cb8c7de1c'; const options = { ui_uid: uuid(), user: 'admin' @@ -325,4 +326,17 @@ describe('Downloader', function() { it('Pause file', async function() { }); + + it('Generate args', async function() { + const args = await downloader_api.generateArgs(url, 'video', options); + console.log(args); + }); + + it('Generate args - subscription', async function() { + subscriptions_api.initialize(db_api, logger); + const sub = await subscriptions_api.getSubscription(sub_id); + const sub_options = subscriptions_api.generateOptionsForSubscriptionDownload(sub, 'admin'); + const args = await downloader_api.generateArgs(url, 'video', sub_options, 'admin'); + console.log(args); + }); }); From 27faff054e279cff94e11ca273c0d20f32919cfb Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Sun, 19 Sep 2021 14:56:32 -0400 Subject: [PATCH 09/15] Recent videos component now remembers sort order between page reloads --- .../components/recent-videos/recent-videos.component.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/app/components/recent-videos/recent-videos.component.ts b/src/app/components/recent-videos/recent-videos.component.ts index d6ff044..251c41d 100644 --- a/src/app/components/recent-videos/recent-videos.component.ts +++ b/src/app/components/recent-videos/recent-videos.component.ts @@ -107,6 +107,12 @@ export class RecentVideosComponent implements OnInit { this.fileTypeFilter = cached_file_type_filter; } + const sort_order = localStorage.getItem('recent_videos_sort_order'); + + if (sort_order) { + this.descendingMode = sort_order === 'descending'; + } + this.searchChangedSubject .debounceTime(500) .pipe(distinctUntilChanged() @@ -145,6 +151,7 @@ export class RecentVideosComponent implements OnInit { toggleModeChange() { this.descendingMode = !this.descendingMode; + localStorage.setItem('recent_videos_sort_order', this.descendingMode ? 'descending' : 'ascending'); this.getAllFiles(); } From 58a0dc4afe9c5150c5ab6a3bdbf86967a05b5d22 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Mon, 20 Sep 2021 22:04:46 -0600 Subject: [PATCH 10/15] Version and commit info is now generated during autobuilds and can be viewed in the about dialog Prepared removal of JSON translations from repo to move towards XLIFF-only --- .github/workflows/build.yml | 17 +++++++++++++++++ .github/workflows/docker-release.yml | 17 +++++++++++++++++ .github/workflows/docker.yml | 17 +++++++++++++++++ backend/app.js | 12 ++++++++++++ src/app/app.component.ts | 4 ++++ .../about-dialog/about-dialog.component.html | 11 +++++++++++ .../about-dialog/about-dialog.component.ts | 2 +- src/app/posts.services.ts | 5 +++++ 8 files changed, 84 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8527313..54b21d0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,6 +25,23 @@ jobs: cd backend npm install sudo npm install -g @angular/cli + - name: prepare localization + run: | + sudo npm install -g xliff-to-json + xliff-to-json ./src/assets/i18n + - name: Set hash + id: vars + run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)" + - name: Get current date + id: date + run: echo "::set-output name=date::$(date +'%Y-%m-%d')" + - name: create-json + id: create-json + uses: jsdaniell/create-json@1.1.2 + with: + name: "version.json" + json: '{"type": "autobuild", "tag": "N/A", "commit": "${{ steps.vars.outputs.sha_short }}", "date": "${{ steps.date.outputs.date }}"}' + dir: 'backend/' - name: build run: ng build --prod - name: prepare artifact upload diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index 96ec2c8..aae7fb3 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -13,6 +13,23 @@ jobs: steps: - name: checkout code uses: actions/checkout@v2 + - name: prepare localization + run: | + sudo npm install -g xliff-to-json + xliff-to-json ./src/assets/i18n + - name: Set hash + id: vars + run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)" + - name: Get current date + id: date + run: echo "::set-output name=date::$(date +'%Y-%m-%d')" + - name: create-json + id: create-json + uses: jsdaniell/create-json@1.1.2 + with: + name: "version.json" + json: '{"type": "docker", "tag": "latest", "commit": "${{ steps.vars.outputs.sha_short }}", "date": "${{ steps.date.outputs.date }}"}' + dir: 'backend/' - name: setup platform emulator uses: docker/setup-qemu-action@v1 - name: setup multi-arch docker build diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index c74f29b..9e0049b 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -10,6 +10,23 @@ jobs: steps: - name: checkout code uses: actions/checkout@v2 + - name: prepare localization + run: | + sudo npm install -g xliff-to-json + xliff-to-json ./src/assets/i18n + - name: Set hash + id: vars + run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)" + - name: Get current date + id: date + run: echo "::set-output name=date::$(date +'%Y-%m-%d')" + - name: create-json + id: create-json + uses: jsdaniell/create-json@1.1.2 + with: + name: "version.json" + json: '{"type": "docker", "tag": "nightly", "commit": "${{ steps.vars.outputs.sha_short }}", "date": "${{ steps.date.outputs.date }}"}' + dir: 'backend/' - name: setup platform emulator uses: docker/setup-qemu-action@v1 - name: setup multi-arch docker build diff --git a/backend/app.js b/backend/app.js index 7404f3c..33363d9 100644 --- a/backend/app.js +++ b/backend/app.js @@ -142,6 +142,14 @@ var validDownloadingAgents = [ const subscription_timeouts = {}; +let version_info = null; +if (fs.existsSync('version.json')) { + version_info = fs.readJSONSync('version.json'); + logger.verbose(`Version info: ${JSON.stringify(version_info, null, 2)}`); +} else { + version_info = {'type': 'N/A', 'tag': 'N/A', 'commit': 'N/A', 'date': 'N/A'}; +} + // don't overwrite config if it already happened.. NOT // let alreadyWritten = db.get('configWriteFlag').value(); let writeConfigMode = process.env.write_ytdl_config; @@ -932,6 +940,10 @@ app.post('/api/setConfig', optionalJwt, function(req, res) { } }); +app.get('/api/versionInfo', (req, res) => { + res.send({version_info: version_info}); +}); + app.post('/api/restartServer', optionalJwt, (req, res) => { // delayed by a little bit so that the client gets a response setTimeout(() => {restartServer()}, 100); diff --git a/src/app/app.component.ts b/src/app/app.component.ts index cadd7ca..6780813 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -118,6 +118,10 @@ export class AppComponent implements OnInit, AfterViewInit { } this.postsService.reloadCategories(); + + this.postsService.getVersionInfo().subscribe(res => { + this.postsService.version_info = res['version_info']; + }); } // theme stuff diff --git a/src/app/dialogs/about-dialog/about-dialog.component.html b/src/app/dialogs/about-dialog/about-dialog.component.html index ddf28f0..c869b8d 100644 --- a/src/app/dialogs/about-dialog/about-dialog.component.html +++ b/src/app/dialogs/about-dialog/about-dialog.component.html @@ -21,6 +21,17 @@ done  Update available - {{latestGithubRelease['tag_name']}}. You can update from the settings menu. You are up to date.

+

+ Installation type: {{postsService.version_info.type}} +
+ + Docker tag: {{postsService.version_info.tag}} +
+
+ Commit hash: {{postsService.version_info.commit}} +
+ Build date: {{postsService.version_info.date}} +

Found a bug or have a suggestion? Click here to create an issue!

diff --git a/src/app/dialogs/about-dialog/about-dialog.component.ts b/src/app/dialogs/about-dialog/about-dialog.component.ts index fb8c1fa..826421a 100644 --- a/src/app/dialogs/about-dialog/about-dialog.component.ts +++ b/src/app/dialogs/about-dialog/about-dialog.component.ts @@ -19,7 +19,7 @@ export class AboutDialogComponent implements OnInit { sidepanel_mode = this.postsService.sidepanel_mode; card_size = this.postsService.card_size; - constructor(private postsService: PostsService) { } + constructor(public postsService: PostsService) { } ngOnInit(): void { this.getLatestGithubRelease(); diff --git a/src/app/posts.services.ts b/src/app/posts.services.ts index c0c8b56..22faa70 100644 --- a/src/app/posts.services.ts +++ b/src/app/posts.services.ts @@ -60,6 +60,7 @@ export class PostsService implements CanActivate { categories = null; sidenav = null; locale = isoLangs['en']; + version_info = null; constructor(private http: HttpClient, private router: Router, @Inject(DOCUMENT) private document: Document, public snackBar: MatSnackBar, private titleService: Title) { @@ -453,6 +454,10 @@ export class PostsService implements CanActivate { return this.http.post(this.path + 'clearFinishedDownloads', {}, this.httpOptions); } + getVersionInfo() { + return this.http.get(this.path + 'versionInfo', this.httpOptions); + } + updateServer(tag) { return this.http.post(this.path + 'updateServer', {tag: tag}, this.httpOptions); } From 8aa354ac245c7862ebac238b974e9d245ebc6cdb Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Tue, 21 Sep 2021 20:03:26 -0600 Subject: [PATCH 11/15] Fixed issue where navigating to a sub's video would play all videos from the subscription --- src/app/components/recent-videos/recent-videos.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/recent-videos/recent-videos.component.ts b/src/app/components/recent-videos/recent-videos.component.ts index 251c41d..2645424 100644 --- a/src/app/components/recent-videos/recent-videos.component.ts +++ b/src/app/components/recent-videos/recent-videos.component.ts @@ -202,7 +202,7 @@ export class RecentVideosComponent implements OnInit { } else { // normal subscriptions !new_tab ? this.router.navigate(['/player', {uid: file.uid, - type: file.isAudio ? 'audio' : 'video', sub_id: sub.id}]) + type: file.isAudio ? 'audio' : 'video'}]) : window.open(`/#/player;uid=${file.uid};type=${file.isAudio ? 'audio' : 'video'}`); } } else { From 75fc09ed99a4ffd0ecc0333eb23232db43a90d43 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Tue, 21 Sep 2021 23:51:07 -0600 Subject: [PATCH 12/15] Improved arg simulation -- now uses same method as the actual download Added checkbox for advanced custom args to either replace all args or append --- backend/app.js | 25 +++++- backend/downloader.js | 4 +- src/app/main/main.component.css | 4 +- src/app/main/main.component.html | 25 +++--- src/app/main/main.component.ts | 140 ++++++++++++------------------- src/app/posts.services.ts | 16 +++- 6 files changed, 113 insertions(+), 101 deletions(-) diff --git a/backend/app.js b/backend/app.js index 33363d9..97a29df 100644 --- a/backend/app.js +++ b/backend/app.js @@ -987,8 +987,9 @@ app.post('/api/downloadFile', optionalJwt, async function(req, res) { const url = req.body.url; const type = req.body.type; const user_uid = req.isAuthenticated() ? req.user.uid : null; - var options = { + const options = { customArgs: req.body.customArgs, + additionalArgs: req.body.additionalArgs, customOutput: req.body.customOutput, selectedHeight: req.body.selectedHeight, customQualityConfiguration: req.body.customQualityConfiguration, @@ -996,7 +997,7 @@ app.post('/api/downloadFile', optionalJwt, async function(req, res) { youtubePassword: req.body.youtubePassword, ui_uid: req.body.ui_uid, cropFileSettings: req.body.cropFileSettings - } + }; const download = await downloader_api.createDownload(url, type, options, user_uid); @@ -1012,6 +1013,26 @@ app.post('/api/killAllDownloads', optionalJwt, async function(req, res) { res.send(result_obj); }); +app.post('/api/generateArgs', optionalJwt, async function(req, res) { + const url = req.body.url; + const type = req.body.type; + const user_uid = req.isAuthenticated() ? req.user.uid : null; + const options = { + customArgs: req.body.customArgs, + additionalArgs: req.body.additionalArgs, + customOutput: req.body.customOutput, + selectedHeight: req.body.selectedHeight, + customQualityConfiguration: req.body.customQualityConfiguration, + youtubeUsername: req.body.youtubeUsername, + youtubePassword: req.body.youtubePassword, + ui_uid: req.body.ui_uid, + cropFileSettings: req.body.cropFileSettings + }; + + const args = await downloader_api.generateArgs(url, type, options, user_uid, true); + res.send({args: args}); +}); + // gets all download mp3s app.get('/api/getMp3s', optionalJwt, async function(req, res) { // TODO: simplify diff --git a/backend/downloader.js b/backend/downloader.js index 16aaaa8..1c428ea 100644 --- a/backend/downloader.js +++ b/backend/downloader.js @@ -369,7 +369,7 @@ async function downloadQueuedFile(download_uid) { // helper functions -exports.generateArgs = async (url, type, options, user_uid = null) => { +exports.generateArgs = async (url, type, options, user_uid = null, simulated = false) => { const audioFolderPath = config_api.getConfigItem('ytdl_audio_folder_path'); const videoFolderPath = config_api.getConfigItem('ytdl_video_folder_path'); @@ -510,7 +510,7 @@ exports.generateArgs = async (url, type, options, user_uid = null) => { // filter out incompatible args downloadConfig = filterArgs(downloadConfig, is_audio); - logger.verbose(`youtube-dl args being used: ${downloadConfig.join(',')}`); + if (!simulated) logger.verbose(`youtube-dl args being used: ${downloadConfig.join(',')}`); return downloadConfig; } diff --git a/src/app/main/main.component.css b/src/app/main/main.component.css index e2325f2..5a712ae 100644 --- a/src/app/main/main.component.css +++ b/src/app/main/main.component.css @@ -129,7 +129,9 @@ mat-form-field.mat-form-field { } .edit-button { - margin-left: 10px; + margin-left: 5px; + margin-top: -6px; + margin-bottom: -5px; top: -5px; } diff --git a/src/app/main/main.component.html b/src/app/main/main.component.html index 3bfdc23..cba6565 100644 --- a/src/app/main/main.component.html +++ b/src/app/main/main.component.html @@ -111,8 +111,13 @@ + + + Replace args + + - + No need to include URL, just everything after. Args are delimited using two commas like so: ,, @@ -127,7 +132,7 @@ - + Documentation. Path is relative to the config download path. Don't include extension. @@ -140,13 +145,13 @@ Use authentication - - + +
- - + +
@@ -155,13 +160,13 @@ Crop file - - + +
- - + +
diff --git a/src/app/main/main.component.ts b/src/app/main/main.component.ts index 996d819..37653ba 100644 --- a/src/app/main/main.component.ts +++ b/src/app/main/main.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit, ElementRef, ViewChild, ViewChildren, QueryList } from '@angular/core'; import {PostsService} from '../posts.services'; import {FileCardComponent} from '../file-card/file-card.component'; -import { Observable } from 'rxjs'; +import { Observable, Subject } from 'rxjs'; import {FormControl, Validators} from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; import { MatSnackBar } from '@angular/material/snack-bar'; @@ -9,7 +9,6 @@ import { saveAs } from 'file-saver'; import { YoutubeSearchService, Result } from '../youtube-search.service'; import { Router, ActivatedRoute } from '@angular/router'; import { Platform } from '@angular/cdk/platform'; -import { v4 as uuid } from 'uuid'; import { ArgModifierDialogComponent } from 'app/dialogs/arg-modifier-dialog/arg-modifier-dialog.component'; import { RecentVideosComponent } from 'app/components/recent-videos/recent-videos.component'; @@ -50,6 +49,7 @@ export class MainComponent implements OnInit { customArgsEnabled = false; customArgs = null; customOutputEnabled = false; + replaceArgs = false; customOutput = null; youtubeAuthEnabled = false; youtubeUsername = null; @@ -212,6 +212,7 @@ export class MainComponent implements OnInit { error: false }; + argsChangedSubject: Subject = new Subject(false); simulatedOutput = ''; constructor(public postsService: PostsService, private youtubeSearch: YoutubeSearchService, public snackBar: MatSnackBar, @@ -224,8 +225,6 @@ export class MainComponent implements OnInit { if (this.autoStartDownload) { this.downloadClicked(); } - - setInterval(() => this.getSimulatedOutput(), 1000); } async loadConfig() { @@ -261,6 +260,10 @@ export class MainComponent implements OnInit { this.customOutputEnabled = localStorage.getItem('customOutputEnabled') === 'true'; } + if (localStorage.getItem('replaceArgs') !== null) { + this.replaceArgs = localStorage.getItem('replaceArgs') === 'true'; + } + if (localStorage.getItem('youtubeAuthEnabled') !== null) { this.youtubeAuthEnabled = localStorage.getItem('youtubeAuthEnabled') === 'true'; } @@ -323,6 +326,12 @@ export class MainComponent implements OnInit { this.autoStartDownload = true; } + this.argsChangedSubject + .debounceTime(500) + .subscribe((should_simulate) => { + if (should_simulate) this.getSimulatedOutput(); + }); + this.setCols(); } @@ -412,7 +421,8 @@ export class MainComponent implements OnInit { this.urlError = false; // get common args - const customArgs = (this.customArgsEnabled ? this.customArgs : null); + const customArgs = (this.customArgsEnabled && this.replaceArgs ? this.customArgs : null); + const additionalArgs = (this.customArgsEnabled && !this.replaceArgs ? this.customArgs : null); const customOutput = (this.customOutputEnabled ? this.customOutput : null); const youtubeUsername = (this.youtubeAuthEnabled && this.youtubeUsername ? this.youtubeUsername : null); const youtubePassword = (this.youtubeAuthEnabled && this.youtubePassword ? this.youtubePassword : null); @@ -445,7 +455,7 @@ export class MainComponent implements OnInit { this.downloadingfile = true; this.postsService.downloadFile(this.url, type, (this.selectedQuality === '' ? null : this.selectedQuality), - customQualityConfiguration, customArgs, customOutput, youtubeUsername, youtubePassword, cropFileSettings).subscribe(res => { + customQualityConfiguration, customArgs, additionalArgs, customOutput, youtubeUsername, youtubePassword, cropFileSettings).subscribe(res => { this.current_download = res['download']; this.downloads.push(res['download']); this.download_uids.push(res['download']['uid']); @@ -593,6 +603,7 @@ export class MainComponent implements OnInit { if (str !== this.last_valid_url && this.allowQualitySelect) { // get info this.getURLInfo(str); + this.argsChangedSubject.next(true); } this.last_valid_url = str; } @@ -630,79 +641,44 @@ export class MainComponent implements OnInit { } } - getSimulatedOutput() { - const customArgsExists = this.customArgsEnabled && this.customArgs; - const globalArgsExists = this.globalCustomArgs && this.globalCustomArgs !== ''; - - let full_string_array: string[] = []; - const base_string_array = ['youtube-dl', this.url]; - - if (customArgsExists) { - this.simulatedOutput = base_string_array.join(' ') + ' ' + this.customArgs.split(',,').join(' '); - return this.simulatedOutput; - } - - full_string_array.push(...base_string_array); + argChanged(): void { + this.argsChangedSubject.next(true); + } - const base_path = this.audioOnly ? this.audioFolderPath : this.videoFolderPath; - const ext = this.audioOnly ? '.mp3' : '.mp4'; - // gets output - let output_string_array = ['-o', base_path + '%(title)s' + ext]; - if (this.customOutputEnabled && this.customOutput) { - output_string_array = ['-o', base_path + this.customOutput + ext]; - } - // before pushing output, should check if using an external downloader - if (!this.useDefaultDownloadingAgent && this.customDownloadingAgent === 'aria2c') { - full_string_array.push('--external-downloader', 'aria2c'); - } - // pushes output - full_string_array.push(...output_string_array); + getSimulatedOutput(): void { + // this function should be very similar to downloadFile() + const customArgs = (this.customArgsEnabled && this.replaceArgs ? this.customArgs : null); + const additionalArgs = (this.customArgsEnabled && !this.replaceArgs ? this.customArgs : null); + const customOutput = (this.customOutputEnabled ? this.customOutput : null); + const youtubeUsername = (this.youtubeAuthEnabled && this.youtubeUsername ? this.youtubeUsername : null); + const youtubePassword = (this.youtubeAuthEnabled && this.youtubePassword ? this.youtubePassword : null); - // logic splits into audio and video modes - if (this.audioOnly) { - // adds base audio string - const format_array = []; - const audio_format = this.getSelectedAudioFormat(); - if (audio_format) { - format_array.push('-f', audio_format); - } else if (this.selectedQuality) { - format_array.push('--audio-quality', this.selectedQuality['format_id']); - } + const type = this.audioOnly ? 'audio' : 'video'; - // pushes formats - full_string_array.splice(2, 0, ...format_array); + const customQualityConfiguration = type === 'audio' ? this.getSelectedAudioFormat() : this.getSelectedVideoFormat(); - const additional_params = ['-x', '--audio-format', 'mp3', '--write-info-json', '--print-json']; + let cropFileSettings = null; - full_string_array.push(...additional_params); - } else { - // adds base video string - let format_array = ['-f', 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4']; - const video_format = this.getSelectedVideoFormat(); - if (video_format) { - format_array = ['-f', video_format]; - } else if (this.selectedQuality) { - format_array = [`bestvideo[height=${this.selectedQuality['format_id']}]+bestaudio/best[height=${this.selectedQuality}]`]; + if (this.cropFile) { + cropFileSettings = { + cropFileStart: this.cropFileStart, + cropFileEnd: this.cropFileEnd } - - // pushes formats - full_string_array.splice(2, 0, ...format_array); - - const additional_params = ['--write-info-json', '--print-json']; - - full_string_array.push(...additional_params); - } - - if (this.use_youtubedl_archive) { - full_string_array.push('--download-archive', 'archive.txt'); } - if (globalArgsExists) { - full_string_array = full_string_array.concat(this.globalCustomArgs.split(',,')); - } - - this.simulatedOutput = full_string_array.join(' '); - return this.simulatedOutput; + this.postsService.generateArgs(this.url, type, (this.selectedQuality === '' ? null : this.selectedQuality), + customQualityConfiguration, customArgs, additionalArgs, customOutput, youtubeUsername, youtubePassword, cropFileSettings).subscribe(res => { + const simulated_args = res['args']; + if (simulated_args) { + // hide password if needed + const passwordIndex = simulated_args.indexOf('--password'); + console.log(passwordIndex); + if (passwordIndex !== -1 && passwordIndex !== simulated_args.length - 1) { + simulated_args[passwordIndex + 1] = simulated_args[passwordIndex + 1].replace(/./g, '*'); + } + this.simulatedOutput = `youtube-dl ${this.url} ${simulated_args.join(' ')}`; + } + }); } errorFormats(url) { @@ -746,6 +722,7 @@ export class MainComponent implements OnInit { videoModeChanged(new_val) { this.selectedQuality = ''; localStorage.setItem('audioOnly', new_val.checked.toString()); + this.argsChangedSubject.next(true); } autoplayChanged(new_val) { @@ -754,29 +731,22 @@ export class MainComponent implements OnInit { customArgsEnabledChanged(new_val) { localStorage.setItem('customArgsEnabled', new_val.checked.toString()); - if (new_val.checked === true && this.customOutputEnabled) { - this.customOutputEnabled = false; - localStorage.setItem('customOutputEnabled', 'false'); + this.argsChangedSubject.next(true); + } - this.youtubeAuthEnabled = false; - localStorage.setItem('youtubeAuthEnabled', 'false'); - } + replaceArgsChanged(new_val) { + localStorage.setItem('replaceArgs', new_val.checked.toString()); + this.argsChangedSubject.next(true); } customOutputEnabledChanged(new_val) { localStorage.setItem('customOutputEnabled', new_val.checked.toString()); - if (new_val.checked === true && this.customArgsEnabled) { - this.customArgsEnabled = false; - localStorage.setItem('customArgsEnabled', 'false'); - } + this.argsChangedSubject.next(true); } youtubeAuthEnabledChanged(new_val) { localStorage.setItem('youtubeAuthEnabled', new_val.checked.toString()); - if (new_val.checked === true && this.customArgsEnabled) { - this.customArgsEnabled = false; - localStorage.setItem('customArgsEnabled', 'false'); - } + this.argsChangedSubject.next(true); } getAudioAndVideoFormats(formats) { diff --git a/src/app/posts.services.ts b/src/app/posts.services.ts index 22faa70..bee6500 100644 --- a/src/app/posts.services.ts +++ b/src/app/posts.services.ts @@ -175,11 +175,25 @@ export class PostsService implements CanActivate { } // tslint:disable-next-line: max-line-length - downloadFile(url: string, type: string, selectedQuality: string, customQualityConfiguration: string, customArgs: string = null, customOutput: string = null, youtubeUsername: string = null, youtubePassword: string = null, cropFileSettings = null) { + downloadFile(url: string, type: string, selectedQuality: string, customQualityConfiguration: string, customArgs: string = null, additionalArgs: string = null, customOutput: string = null, youtubeUsername: string = null, youtubePassword: string = null, cropFileSettings = null) { return this.http.post(this.path + 'downloadFile', {url: url, selectedHeight: selectedQuality, customQualityConfiguration: customQualityConfiguration, customArgs: customArgs, + additionalArgs: additionalArgs, + customOutput: customOutput, + youtubeUsername: youtubeUsername, + youtubePassword: youtubePassword, + type: type, + cropFileSettings: cropFileSettings}, this.httpOptions); + } + + generateArgs(url: string, type: string, selectedQuality: string, customQualityConfiguration: string, customArgs: string = null, additionalArgs: string = null, customOutput: string = null, youtubeUsername: string = null, youtubePassword: string = null, cropFileSettings = null) { + return this.http.post(this.path + 'generateArgs', {url: url, + selectedHeight: selectedQuality, + customQualityConfiguration: customQualityConfiguration, + customArgs: customArgs, + additionalArgs: additionalArgs, customOutput: customOutput, youtubeUsername: youtubeUsername, youtubePassword: youtubePassword, From ec7f04552f241518cb542e6ca2dc1a7be770f208 Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Tue, 21 Sep 2021 23:56:04 -0600 Subject: [PATCH 13/15] Fixed mangled Subject initialization in main component --- src/app/main/main.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/main/main.component.ts b/src/app/main/main.component.ts index 37653ba..00e1c61 100644 --- a/src/app/main/main.component.ts +++ b/src/app/main/main.component.ts @@ -212,7 +212,7 @@ export class MainComponent implements OnInit { error: false }; - argsChangedSubject: Subject = new Subject(false); + argsChangedSubject: Subject = new Subject(); simulatedOutput = ''; constructor(public postsService: PostsService, private youtubeSearch: YoutubeSearchService, public snackBar: MatSnackBar, From 562eaa1b9b5c15ac2052eaf953d24b5e58156c5f Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Wed, 22 Sep 2021 19:27:25 -0600 Subject: [PATCH 14/15] Added support for generate NFO files for Kodi Minor UI updates to settings --- backend/appdata/default.json | 3 +- backend/config.js | 3 +- backend/consts.js | 5 ++ backend/db.js | 3 +- backend/downloader.js | 20 +++++ backend/package-lock.json | 73 ++++++++++++++++ backend/package.json | 1 + backend/test/sample.info.json | 1 + backend/test/tests.js | 10 +++ backend/utils.js | 8 +- src/app/settings/settings.component.html | 103 ++++++++++++----------- src/app/settings/settings.component.ts | 26 +++++- 12 files changed, 197 insertions(+), 59 deletions(-) create mode 100644 backend/test/sample.info.json diff --git a/backend/appdata/default.json b/backend/appdata/default.json index 04f76de..44f2f02 100644 --- a/backend/appdata/default.json +++ b/backend/appdata/default.json @@ -33,7 +33,8 @@ "use_twitch_API": false, "twitch_API_key": "", "twitch_auto_download_chat": false, - "use_sponsorblock_API": false + "use_sponsorblock_API": false, + "generate_NFO_files": false }, "Themes": { "default_theme": "default", diff --git a/backend/config.js b/backend/config.js index e50c472..95e364c 100644 --- a/backend/config.js +++ b/backend/config.js @@ -208,7 +208,8 @@ const DEFAULT_CONFIG = { "use_twitch_API": false, "twitch_API_key": "", "twitch_auto_download_chat": false, - "use_sponsorblock_API": false + "use_sponsorblock_API": false, + "generate_NFO_files": false }, "Themes": { "default_theme": "default", diff --git a/backend/consts.js b/backend/consts.js index 1f25021..849fa4c 100644 --- a/backend/consts.js +++ b/backend/consts.js @@ -114,6 +114,11 @@ exports.CONFIG_ITEMS = { 'key': 'ytdl_use_sponsorblock_api', 'path': 'YoutubeDLMaterial.API.use_sponsorblock_API' }, + 'ytdl_generate_nfo_files': { + 'key': 'ytdl_generate_nfo_files', + 'path': 'YoutubeDLMaterial.API.generate_NFO_files' + }, + // Themes 'ytdl_default_theme': { diff --git a/backend/db.js b/backend/db.js index a4c8080..18f9064 100644 --- a/backend/db.js +++ b/backend/db.js @@ -217,8 +217,7 @@ function generateFileObject(file_path, type) { var title = jsonobj.title; var url = jsonobj.webpage_url; var uploader = jsonobj.uploader; - var upload_date = jsonobj.upload_date; - upload_date = upload_date ? `${upload_date.substring(0, 4)}-${upload_date.substring(4, 6)}-${upload_date.substring(6, 8)}` : 'N/A'; + var upload_date = utils.formatDateString(jsonobj.upload_date); var size = stats.size; diff --git a/backend/downloader.js b/backend/downloader.js index 1c428ea..86e6f5e 100644 --- a/backend/downloader.js +++ b/backend/downloader.js @@ -11,6 +11,7 @@ const youtubedl = require('youtube-dl'); const logger = require('./logger'); const config_api = require('./config'); const twitch_api = require('./twitch'); +const { create } = require('xmlbuilder2'); const categories_api = require('./categories'); const utils = require('./utils'); @@ -328,6 +329,10 @@ async function downloadQueuedFile(download_uid) { if (!success) logger.error('Failed to apply ID3 tag to audio file ' + output_json['_filename']); } + if (config_api.getConfigItem('ytdl_generate_nfo_files')) { + exports.generateNFOFile(output_json, `${filepath_no_extension}.nfo`); + } + if (options.cropFileSettings) { await utils.cropFile(full_file_path, options.cropFileSettings.cropFileStart, options.cropFileSettings.cropFileEnd, ext); } @@ -603,4 +608,19 @@ async function checkDownloadPercent(download_uid) { const percent_complete = (sum_size/resulting_file_size * 100).toFixed(2); await db_api.updateRecord('download_queue', {uid: download_uid}, {percent_complete: percent_complete}); }); +} + +exports.generateNFOFile = (info, output_path) => { + const nfo_obj = { + episodedetails: { + title: info['fulltitle'], + episode: info['playlist_index'] ? info['playlist_index'] : undefined, + premiered: utils.formatDateString(info['upload_date']), + plot: `${info['uploader_url']}\n${info['description']}\n${info['playlist_title'] ? info['playlist_title'] : ''}`, + director: info['artist'] ? info['artist'] : info['uploader'] + } + }; + const doc = create(nfo_obj); + const xml = doc.end({ prettyPrint: true }); + fs.writeFileSync(output_path, xml); } \ No newline at end of file diff --git a/backend/package-lock.json b/backend/package-lock.json index fbade26..7c691f8 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -4,6 +4,38 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@oozcitak/dom": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@oozcitak/dom/-/dom-1.15.10.tgz", + "integrity": "sha512-0JT29/LaxVgRcGKvHmSrUTEvZ8BXvZhGl2LASRUgHqDTC1M5g1pLmVv56IYNyt3bG2CUjDkc67wnyZC14pbQrQ==", + "requires": { + "@oozcitak/infra": "1.0.8", + "@oozcitak/url": "1.0.4", + "@oozcitak/util": "8.3.8" + } + }, + "@oozcitak/infra": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@oozcitak/infra/-/infra-1.0.8.tgz", + "integrity": "sha512-JRAUc9VR6IGHOL7OGF+yrvs0LO8SlqGnPAMqyzOuFZPSZSXI7Xf2O9+awQPSMXgIWGtgUf/dA6Hs6X6ySEaWTg==", + "requires": { + "@oozcitak/util": "8.3.8" + } + }, + "@oozcitak/url": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@oozcitak/url/-/url-1.0.4.tgz", + "integrity": "sha512-kDcD8y+y3FCSOvnBI6HJgl00viO/nGbQoCINmQ0h98OhnGITrWR3bOGfwYCthgcrV8AnTJz8MzslTQbC3SOAmw==", + "requires": { + "@oozcitak/infra": "1.0.8", + "@oozcitak/util": "8.3.8" + } + }, + "@oozcitak/util": { + "version": "8.3.8", + "resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-8.3.8.tgz", + "integrity": "sha512-T8TbSnGsxo6TDBJx/Sgv/BlVJL3tshxZP7Aq5R1mSnM5OcHY2dQaxLMu2+E8u3gN0MLOzdjurqN4ZRVuzQycOQ==" + }, "@sindresorhus/is": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", @@ -1117,6 +1149,11 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -3030,6 +3067,11 @@ "memory-pager": "^1.0.2" } }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, "sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", @@ -3593,6 +3635,37 @@ "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" }, + "xmlbuilder2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-3.0.2.tgz", + "integrity": "sha512-h4MUawGY21CTdhV4xm3DG9dgsqyhDkZvVJBx88beqX8wJs3VgyGQgAn5VreHuae6unTQxh115aMK5InCVmOIKw==", + "requires": { + "@oozcitak/dom": "1.15.10", + "@oozcitak/infra": "1.0.8", + "@oozcitak/util": "8.3.8", + "@types/node": "*", + "js-yaml": "3.14.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + } + } + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/backend/package.json b/backend/package.json index c857561..339e94a 100644 --- a/backend/package.json +++ b/backend/package.json @@ -66,6 +66,7 @@ "unzipper": "^0.10.10", "uuidv4": "^6.0.6", "winston": "^3.2.1", + "xmlbuilder2": "^3.0.2", "youtube-dl": "^3.0.2" } } diff --git a/backend/test/sample.info.json b/backend/test/sample.info.json new file mode 100644 index 0000000..8d65c5b --- /dev/null +++ b/backend/test/sample.info.json @@ -0,0 +1 @@ +{"fps": 25, "webpage_url_basename": "watch", "vbr": 3546.159, "age_limit": 0, "_filename": "video\\20091024 - Rick Astley - Never Gonna Give You Up (Official Music Video).mp4", "height": 1080, "description": "Rick Astley's official music video for \u201cNever Gonna Give You Up", "thumbnail": "https://i.ytimg.com/vi_webp/dQw4w9WgXcQ/maxresdefault.webp", "resolution": null, "webpage_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "average_rating": 4.894496, "ext": "mp4", "formats": [{"fps": null, "abr": 46.492, "height": null, "format_id": "249", "url": "https://r2---sn-j5caxvox-cvpe.googlevideo.com/videoplayback?expire=1631616016&ei=sCdAYdCGAZD7kgadtbT4AQ&ip=174.126.238.181&id=o-AB6FjTBkJE3OCIRR6E4MV93s_EQC6Wem5GQY3nsgnbhd&itag=249&source=youtube&requiressl=yes&mh=7c&mm=31%2C29&mn=sn-j5caxvox-cvpe%2Csn-nx5s7n76&ms=au%2Crdu&mv=m&mvi=2&pl=24&initcwndbps=1847500&vprv=1&mime=audio%2Fwebm&ns=flWNw8FESVeF__wRUpmII5wG&gir=yes&clen=1232413&dur=212.061&lmt=1624945854503369&mt=1631594013&fvip=4&keepalive=yes&fexp=24001373%2C24007246&beids=9466585&c=WEB&txp=5511222&n=7iJIhDM_ktV3660j&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRQIhAOQPPrlgzOqNtORq4sWu11c9B7xu7oDsAw2tubKwfknwAiBqNjjMPHEVKlGNnOplfYZ_zuITnJ0PQAicXP1qyYRDWA%3D%3D&sig=AOq0QJ8wRgIhAOBiYeG7Pq0puYb5QjUVfMdTQStw09143tjience7YyJAiEAyK77-fXJ55w7RP2_ML9Gysg9ZrqhjcBzsolQIVHx6I4=", "format": "249 - audio only (tiny)", "filesize": 1232413, "http_headers": {"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.33 Safari/537.36", "Accept-Encoding": "gzip, deflate", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5"}, "asr": 48000, "downloader_options": {"http_chunk_size": 10485760}, "format_note": "tiny", "acodec": "opus", "quality": 0, "container": "webm_dash", "ext": "webm", "protocol": "https", "vcodec": "none", "tbr": 46.492, "width": null}, {"fps": null, "abr": 61.494, "height": null, "format_id": "250", "url": "https://r2---sn-j5caxvox-cvpe.googlevideo.com/videoplayback?expire=1631616016&ei=sCdAYdCGAZD7kgadtbT4AQ&ip=174.126.238.181&id=o-AB6FjTBkJE3OCIRR6E4MV93s_EQC6Wem5GQY3nsgnbhd&itag=250&source=youtube&requiressl=yes&mh=7c&mm=31%2C29&mn=sn-j5caxvox-cvpe%2Csn-nx5s7n76&ms=au%2Crdu&mv=m&mvi=2&pl=24&initcwndbps=1847500&vprv=1&mime=audio%2Fwebm&ns=flWNw8FESVeF__wRUpmII5wG&gir=yes&clen=1630086&dur=212.061&lmt=1624945852595198&mt=1631594013&fvip=4&keepalive=yes&fexp=24001373%2C24007246&beids=9466585&c=WEB&txp=5511222&n=7iJIhDM_ktV3660j&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRQIgGn2dRpsfP4xqu8rwvbRoH6SV9eCoQNr9KhOHIzWHcfwCIQCNXPD8U5zRz8nOD86M7E79GYNf9sR1QYI8Q0vRwh90zQ%3D%3D&sig=AOq0QJ8wRAIgMX4-9T8B0Rk8YxA7umuAzrb7Cf81aYG4PiV3uKTP4zQCIDw876bIcVsfWIO4Lx4qS7txMIkqDsnofhAfcIrAc9-T", "format": "250 - audio only (tiny)", "filesize": 1630086, "http_headers": {"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.33 Safari/537.36", "Accept-Encoding": "gzip, deflate", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5"}, "asr": 48000, "downloader_options": {"http_chunk_size": 10485760}, "format_note": "tiny", "acodec": "opus", "quality": 0, "container": "webm_dash", "ext": "webm", "protocol": "https", "vcodec": "none", "tbr": 61.494, "width": null}, {"fps": null, "abr": 129.51, "height": null, "format_id": "140", "url": "https://r2---sn-j5caxvox-cvpe.googlevideo.com/videoplayback?expire=1631616016&ei=sCdAYdCGAZD7kgadtbT4AQ&ip=174.126.238.181&id=o-AB6FjTBkJE3OCIRR6E4MV93s_EQC6Wem5GQY3nsgnbhd&itag=140&source=youtube&requiressl=yes&mh=7c&mm=31%2C29&mn=sn-j5caxvox-cvpe%2Csn-nx5s7n76&ms=au%2Crdu&mv=m&mvi=2&pl=24&initcwndbps=1847500&vprv=1&mime=audio%2Fmp4&ns=flWNw8FESVeF__wRUpmII5wG&gir=yes&clen=3433514&dur=212.091&lmt=1628122153868652&mt=1631594013&fvip=4&keepalive=yes&fexp=24001373%2C24007246&beids=9466585&c=WEB&txp=5532434&n=7iJIhDM_ktV3660j&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRQIhAIb6WyFCUS1IO9DY2BvkCDbeQpYU54NUMD8wHVUv-CgoAiBBk9HxGnh0qQAJqGRNFHGTtqiNhjBxPQCfHWqDOmzMbg%3D%3D&sig=AOq0QJ8wRAIgUstXqxWkdk_oRE6snyI1OaunA8jmDtA5gFsO__xRSWgCIDN6rrueyDicOohCGgyy-VOLBEHuwtjxpTpEdFrdzLtd", "format": "140 - audio only (tiny)", "filesize": 3433514, "http_headers": {"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.33 Safari/537.36", "Accept-Encoding": "gzip, deflate", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5"}, "asr": 44100, "downloader_options": {"http_chunk_size": 10485760}, "format_note": "tiny", "acodec": "mp4a.40.2", "quality": 0, "container": "m4a_dash", "ext": "m4a", "protocol": "https", "vcodec": "none", "tbr": 129.51, "width": null}, {"fps": null, "abr": 129.689, "height": null, "format_id": "251", "url": "https://r2---sn-j5caxvox-cvpe.googlevideo.com/videoplayback?expire=1631616016&ei=sCdAYdCGAZD7kgadtbT4AQ&ip=174.126.238.181&id=o-AB6FjTBkJE3OCIRR6E4MV93s_EQC6Wem5GQY3nsgnbhd&itag=251&source=youtube&requiressl=yes&mh=7c&mm=31%2C29&mn=sn-j5caxvox-cvpe%2Csn-nx5s7n76&ms=au%2Crdu&mv=m&mvi=2&pl=24&initcwndbps=1847500&vprv=1&mime=audio%2Fwebm&ns=flWNw8FESVeF__wRUpmII5wG&gir=yes&clen=3437753&dur=212.061&lmt=1624945852795821&mt=1631594013&fvip=4&keepalive=yes&fexp=24001373%2C24007246&beids=9466585&c=WEB&txp=5511222&n=7iJIhDM_ktV3660j&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhAO1WGRPqP3fz7DeczBl9uDNWvypP_xmoQapWlTCG66d9AiEAvZrhvA9jWZGnwCDEhntzc9utiYJZq-_PdXBpKlfUKAY%3D&sig=AOq0QJ8wRQIgPP7v5vRzBQVhBUFexSLhOsqNNfttu2TFH4qIctUxqAsCIQDixqdmBoxi72zvNSXdw-VyXAKd6mQfO_GmsvapdAutvA==", "format": "251 - audio only (tiny)", "filesize": 3437753, "http_headers": {"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.33 Safari/537.36", "Accept-Encoding": "gzip, deflate", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5"}, "asr": 48000, "downloader_options": {"http_chunk_size": 10485760}, "format_note": "tiny", "acodec": "opus", "quality": 0, "container": "webm_dash", "ext": "webm", "protocol": "https", "vcodec": "none", "tbr": 129.689, "width": null}, {"fps": 25, "format": "394 - 256x144 (144p)", "height": 144, "format_id": "394", "url": "https://r2---sn-j5caxvox-cvpe.googlevideo.com/videoplayback?expire=1631616016&ei=sCdAYdCGAZD7kgadtbT4AQ&ip=174.126.238.181&id=o-AB6FjTBkJE3OCIRR6E4MV93s_EQC6Wem5GQY3nsgnbhd&itag=394&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C278%2C394%2C395%2C396%2C397%2C398%2C399&source=youtube&requiressl=yes&mh=7c&mm=31%2C29&mn=sn-j5caxvox-cvpe%2Csn-nx5s7n76&ms=au%2Crdu&mv=m&mvi=2&pl=24&initcwndbps=1847500&vprv=1&mime=video%2Fmp4&ns=flWNw8FESVeF__wRUpmII5wG&gir=yes&clen=1806317&dur=212.040&lmt=1624947805339431&mt=1631594013&fvip=4&keepalive=yes&fexp=24001373%2C24007246&beids=9466585&c=WEB&txp=5531432&n=7iJIhDM_ktV3660j&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRAIgMcmJsqnRfEbWlqbPt1EPKCpOF5UzkyaABbAi0SXEwHICIHVGwJ69zGvjLE7SfFLy7c-1L-psZ1M4cHbx0s_FlJEu&sig=AOq0QJ8wRQIgXnlbLnaTYCnHNGSv3NHchQ_uPCQKO-EuJ8le0QekHdsCIQDsHRE_83b11n_QLvDF0qhNgOtpfYgkhKU05RNt_DiwYg==", "vbr": 68.15, "filesize": 1806317, "http_headers": {"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.33 Safari/537.36", "Accept-Encoding": "gzip, deflate", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5"}, "asr": null, "downloader_options": {"http_chunk_size": 10485760}, "format_note": "144p", "acodec": "none", "quality": 0, "container": "mp4_dash", "ext": "mp4", "protocol": "https", "vcodec": "av01.0.00M.08", "tbr": 68.15, "width": 256}, {"fps": 25, "format": "160 - 256x144 (144p)", "height": 144, "format_id": "160", "url": "https://r2---sn-j5caxvox-cvpe.googlevideo.com/videoplayback?expire=1631616016&ei=sCdAYdCGAZD7kgadtbT4AQ&ip=174.126.238.181&id=o-AB6FjTBkJE3OCIRR6E4MV93s_EQC6Wem5GQY3nsgnbhd&itag=160&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C278%2C394%2C395%2C396%2C397%2C398%2C399&source=youtube&requiressl=yes&mh=7c&mm=31%2C29&mn=sn-j5caxvox-cvpe%2Csn-nx5s7n76&ms=au%2Crdu&mv=m&mvi=2&pl=24&initcwndbps=1847500&vprv=1&mime=video%2Fmp4&ns=flWNw8FESVeF__wRUpmII5wG&gir=yes&clen=1902470&dur=212.040&lmt=1628130952822277&mt=1631594013&fvip=4&keepalive=yes&fexp=24001373%2C24007246&beids=9466585&c=WEB&txp=5535434&n=7iJIhDM_ktV3660j&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhANAbvnZihueHQl6DlS0KDCcmD612v91WicDPh0MMooE3AiEApFWRu4aPyDGym8F6QMLxc8yrVTWSSFBaUxjDWrx59FA%3D&sig=AOq0QJ8wRgIhAIKLvUYN8RN1LzNPHRN4Rb3fC0zDev9KlSdeCExTXPosAiEA_yY2AJ69va_G3eTdbkifrdePATiSrgb8PVDdMk-6hYw=", "vbr": 71.777, "filesize": 1902470, "http_headers": {"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.33 Safari/537.36", "Accept-Encoding": "gzip, deflate", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5"}, "asr": null, "downloader_options": {"http_chunk_size": 10485760}, "format_note": "144p", "acodec": "none", "quality": 0, "container": "mp4_dash", "ext": "mp4", "protocol": "https", "vcodec": "avc1.4d400c", "tbr": 71.777, "width": 256}, {"fps": 25, "format": "278 - 256x144 (144p)", "height": 144, "format_id": "278", "url": "https://r2---sn-j5caxvox-cvpe.googlevideo.com/videoplayback?expire=1631616016&ei=sCdAYdCGAZD7kgadtbT4AQ&ip=174.126.238.181&id=o-AB6FjTBkJE3OCIRR6E4MV93s_EQC6Wem5GQY3nsgnbhd&itag=278&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C278%2C394%2C395%2C396%2C397%2C398%2C399&source=youtube&requiressl=yes&mh=7c&mm=31%2C29&mn=sn-j5caxvox-cvpe%2Csn-nx5s7n76&ms=au%2Crdu&mv=m&mvi=2&pl=24&initcwndbps=1847500&vprv=1&mime=video%2Fwebm&ns=flWNw8FESVeF__wRUpmII5wG&gir=yes&clen=2377337&dur=212.040&lmt=1628133100068252&mt=1631594013&fvip=4&keepalive=yes&fexp=24001373%2C24007246&beids=9466585&c=WEB&txp=5535434&n=7iJIhDM_ktV3660j&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRQIgYxpgVafit-zpha31Q8EtJBdHaBpRj1y65ew-cOr0644CIQD8SJ7dYuyflAm0T7T6ny03WB5jOjmHO87TDSwLeAPwZA%3D%3D&sig=AOq0QJ8wRQIgZP8HNivYFUpLZC2a5UHDX26raL9ekwrCP3fNwgST874CIQDuD4D2kg8lxP-4BC8J_x9cP8vGKfwD1w9HVad9TXIbjg==", "vbr": 89.693, "filesize": 2377337, "http_headers": {"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.33 Safari/537.36", "Accept-Encoding": "gzip, deflate", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5"}, "asr": null, "downloader_options": {"http_chunk_size": 10485760}, "format_note": "144p", "acodec": "none", "quality": 0, "container": "webm_dash", "ext": "webm", "protocol": "https", "vcodec": "vp9", "tbr": 89.693, "width": 256}, {"fps": 25, "format": "133 - 426x240 (240p)", "height": 240, "format_id": "133", "url": "https://r2---sn-j5caxvox-cvpe.googlevideo.com/videoplayback?expire=1631616016&ei=sCdAYdCGAZD7kgadtbT4AQ&ip=174.126.238.181&id=o-AB6FjTBkJE3OCIRR6E4MV93s_EQC6Wem5GQY3nsgnbhd&itag=133&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C278%2C394%2C395%2C396%2C397%2C398%2C399&source=youtube&requiressl=yes&mh=7c&mm=31%2C29&mn=sn-j5caxvox-cvpe%2Csn-nx5s7n76&ms=au%2Crdu&mv=m&mvi=2&pl=24&initcwndbps=1847500&vprv=1&mime=video%2Fmp4&ns=flWNw8FESVeF__wRUpmII5wG&gir=yes&clen=3107257&dur=212.040&lmt=1628130952261127&mt=1631594013&fvip=4&keepalive=yes&fexp=24001373%2C24007246&beids=9466585&c=WEB&txp=5535434&n=7iJIhDM_ktV3660j&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRAIgNlYpzWhXOUhMEpm6bv_NUP8flw8_gbDOPuh_BpF1NWkCIA7dMuvigM6xlyoiJa8HxjDx-zeZODadHPUQ7DSxuy-u&sig=AOq0QJ8wRQIgfZ33AlJlf8H6DyxSVCHeZvA7i3QC9giEfW2FL6iXqTACIQCYEK8Rqbt9EgjKbGEks_iWoxXtm6TFLPM1vmx7722hrg==", "vbr": 117.232, "filesize": 3107257, "http_headers": {"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.33 Safari/537.36", "Accept-Encoding": "gzip, deflate", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5"}, "asr": null, "downloader_options": {"http_chunk_size": 10485760}, "format_note": "240p", "acodec": "none", "quality": 1, "container": "mp4_dash", "ext": "mp4", "protocol": "https", "vcodec": "avc1.4d4015", "tbr": 117.232, "width": 426}, {"fps": 25, "format": "395 - 426x240 (240p)", "height": 240, "format_id": "395", "url": "https://r2---sn-j5caxvox-cvpe.googlevideo.com/videoplayback?expire=1631616016&ei=sCdAYdCGAZD7kgadtbT4AQ&ip=174.126.238.181&id=o-AB6FjTBkJE3OCIRR6E4MV93s_EQC6Wem5GQY3nsgnbhd&itag=395&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C278%2C394%2C395%2C396%2C397%2C398%2C399&source=youtube&requiressl=yes&mh=7c&mm=31%2C29&mn=sn-j5caxvox-cvpe%2Csn-nx5s7n76&ms=au%2Crdu&mv=m&mvi=2&pl=24&initcwndbps=1847500&vprv=1&mime=video%2Fmp4&ns=flWNw8FESVeF__wRUpmII5wG&gir=yes&clen=3563582&dur=212.040&lmt=1624947923168885&mt=1631594013&fvip=4&keepalive=yes&fexp=24001373%2C24007246&beids=9466585&c=WEB&txp=5531432&n=7iJIhDM_ktV3660j&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhALb3kDbV5djjgibZrxg1kD6hfnxNHcgVWv0snt354vKHAiEA5UJIuEWmRqTsPtKGbHwcWducF6aUoEaFxKx1Tz5L008%3D&sig=AOq0QJ8wRQIgKib1QlEvGVYibzu3l1BDT63OFgt9Ttv4HCcqpCKebncCIQCGk-H3AdMNUzWU5I7015YbmU_X4g2Es44BBWCzLYUM6w==", "vbr": 134.449, "filesize": 3563582, "http_headers": {"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.33 Safari/537.36", "Accept-Encoding": "gzip, deflate", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5"}, "asr": null, "downloader_options": {"http_chunk_size": 10485760}, "format_note": "240p", "acodec": "none", "quality": 1, "container": "mp4_dash", "ext": "mp4", "protocol": "https", "vcodec": "av01.0.00M.08", "tbr": 134.449, "width": 426}, {"fps": 25, "format": "242 - 426x240 (240p)", "height": 240, "format_id": "242", "url": "https://r2---sn-j5caxvox-cvpe.googlevideo.com/videoplayback?expire=1631616016&ei=sCdAYdCGAZD7kgadtbT4AQ&ip=174.126.238.181&id=o-AB6FjTBkJE3OCIRR6E4MV93s_EQC6Wem5GQY3nsgnbhd&itag=242&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C278%2C394%2C395%2C396%2C397%2C398%2C399&source=youtube&requiressl=yes&mh=7c&mm=31%2C29&mn=sn-j5caxvox-cvpe%2Csn-nx5s7n76&ms=au%2Crdu&mv=m&mvi=2&pl=24&initcwndbps=1847500&vprv=1&mime=video%2Fwebm&ns=flWNw8FESVeF__wRUpmII5wG&gir=yes&clen=4215000&dur=212.040&lmt=1628133101514133&mt=1631594013&fvip=4&keepalive=yes&fexp=24001373%2C24007246&beids=9466585&c=WEB&txp=5535434&n=7iJIhDM_ktV3660j&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhAPDAPJegoPAAF6kQgTebJ5-GdTQHu-dnudbQ9wUfaIWKAiEA4kNlwtfnb1zuhOb7OlIlBInYYdOTFyoI9mE7-yxxbxM%3D&sig=AOq0QJ8wRgIhALxu_H89hpmniExLrH6A073CxYUCJVhiVe0KFK3Skz7gAiEAlnZsUIv_GSPJYDafWKjskYOJkbRY2tTqSVZO3tHiVPg=", "vbr": 159.026, "filesize": 4215000, "http_headers": {"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.33 Safari/537.36", "Accept-Encoding": "gzip, deflate", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5"}, "asr": null, "downloader_options": {"http_chunk_size": 10485760}, "format_note": "240p", "acodec": "none", "quality": 1, "container": "webm_dash", "ext": "webm", "protocol": "https", "vcodec": "vp9", "tbr": 159.026, "width": 426}, {"fps": 25, "format": "134 - 640x360 (360p)", "height": 360, "format_id": "134", "url": "https://r2---sn-j5caxvox-cvpe.googlevideo.com/videoplayback?expire=1631616016&ei=sCdAYdCGAZD7kgadtbT4AQ&ip=174.126.238.181&id=o-AB6FjTBkJE3OCIRR6E4MV93s_EQC6Wem5GQY3nsgnbhd&itag=134&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C278%2C394%2C395%2C396%2C397%2C398%2C399&source=youtube&requiressl=yes&mh=7c&mm=31%2C29&mn=sn-j5caxvox-cvpe%2Csn-nx5s7n76&ms=au%2Crdu&mv=m&mvi=2&pl=24&initcwndbps=1847500&vprv=1&mime=video%2Fmp4&ns=flWNw8FESVeF__wRUpmII5wG&gir=yes&clen=5820414&dur=212.040&lmt=1628130952382425&mt=1631594013&fvip=4&keepalive=yes&fexp=24001373%2C24007246&beids=9466585&c=WEB&txp=5535434&n=7iJIhDM_ktV3660j&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRQIhAOtn2CoLBwcHCnbLpyhRugJMSvb5A3Mt7q0oc2GewlP5AiBSL9UpjE1pdqmlim_prX4T9hURk5K8YDHf24Gvlo29Sw%3D%3D&sig=AOq0QJ8wRAIgGt5L323eYDn6EDy6r2IYX0xGkzGzVMeH7NruficAzMECIGrivYII2PAGKL5Rv6lDc-l53z6PVid6W_8Jco5BYmFB", "vbr": 219.596, "filesize": 5820414, "http_headers": {"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.33 Safari/537.36", "Accept-Encoding": "gzip, deflate", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5"}, "asr": null, "downloader_options": {"http_chunk_size": 10485760}, "format_note": "360p", "acodec": "none", "quality": 2, "container": "mp4_dash", "ext": "mp4", "protocol": "https", "vcodec": "avc1.4d401e", "tbr": 219.596, "width": 640}, {"fps": 25, "format": "396 - 640x360 (360p)", "height": 360, "format_id": "396", "url": "https://r2---sn-j5caxvox-cvpe.googlevideo.com/videoplayback?expire=1631616016&ei=sCdAYdCGAZD7kgadtbT4AQ&ip=174.126.238.181&id=o-AB6FjTBkJE3OCIRR6E4MV93s_EQC6Wem5GQY3nsgnbhd&itag=396&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C278%2C394%2C395%2C396%2C397%2C398%2C399&source=youtube&requiressl=yes&mh=7c&mm=31%2C29&mn=sn-j5caxvox-cvpe%2Csn-nx5s7n76&ms=au%2Crdu&mv=m&mvi=2&pl=24&initcwndbps=1847500&vprv=1&mime=video%2Fmp4&ns=flWNw8FESVeF__wRUpmII5wG&gir=yes&clen=6957752&dur=212.040&lmt=1624947999257981&mt=1631594013&fvip=4&keepalive=yes&fexp=24001373%2C24007246&beids=9466585&c=WEB&txp=5531432&n=7iJIhDM_ktV3660j&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRAIgHlGjZ2Fo06YkUp_59h9RpX89UUj7yF86OMbElBaaDRQCIBIJuyfWeNSKFP3s6BkXIk8Cy3F2mHRO3PRoR5CuacA3&sig=AOq0QJ8wRQIhANp7jIO5PM8sEwGd4-EmADAfLNsIw-mWhZDI0XAmQ666AiAfEAjcDgczL2TMBWQm9xobP6cbCrDVxfVxR_3IbYIYXg==", "vbr": 262.507, "filesize": 6957752, "http_headers": {"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.33 Safari/537.36", "Accept-Encoding": "gzip, deflate", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5"}, "asr": null, "downloader_options": {"http_chunk_size": 10485760}, "format_note": "360p", "acodec": "none", "quality": 2, "container": "mp4_dash", "ext": "mp4", "protocol": "https", "vcodec": "av01.0.01M.08", "tbr": 262.507, "width": 640}, {"fps": 25, "format": "243 - 640x360 (360p)", "height": 360, "format_id": "243", "url": "https://r2---sn-j5caxvox-cvpe.googlevideo.com/videoplayback?expire=1631616016&ei=sCdAYdCGAZD7kgadtbT4AQ&ip=174.126.238.181&id=o-AB6FjTBkJE3OCIRR6E4MV93s_EQC6Wem5GQY3nsgnbhd&itag=243&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C278%2C394%2C395%2C396%2C397%2C398%2C399&source=youtube&requiressl=yes&mh=7c&mm=31%2C29&mn=sn-j5caxvox-cvpe%2Csn-nx5s7n76&ms=au%2Crdu&mv=m&mvi=2&pl=24&initcwndbps=1847500&vprv=1&mime=video%2Fwebm&ns=flWNw8FESVeF__wRUpmII5wG&gir=yes&clen=7257207&dur=212.040&lmt=1628133101141872&mt=1631594013&fvip=4&keepalive=yes&fexp=24001373%2C24007246&beids=9466585&c=WEB&txp=5535434&n=7iJIhDM_ktV3660j&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRQIgdk8qX0zqkEIlabAum60ghDNmMy2W1zWAsnvKpoef1igCIQCWvArsFhNndiLBBq7UEdk2qX9FvYej4tCuDFknrNyyDA%3D%3D&sig=AOq0QJ8wRgIhAN3hiLrHN-nYrc4xRIFk0mcVYxqR5FNRhzcEz8DJyLAuAiEAiIsaYG5R6ZcaTuWfwV4qp3iJqAYPBLX0QkDgNbXhGl4=", "vbr": 273.805, "filesize": 7257207, "http_headers": {"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.33 Safari/537.36", "Accept-Encoding": "gzip, deflate", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5"}, "asr": null, "downloader_options": {"http_chunk_size": 10485760}, "format_note": "360p", "acodec": "none", "quality": 2, "container": "webm_dash", "ext": "webm", "protocol": "https", "vcodec": "vp9", "tbr": 273.805, "width": 640}, {"fps": 25, "format": "135 - 854x480 (480p)", "height": 480, "format_id": "135", "url": "https://r2---sn-j5caxvox-cvpe.googlevideo.com/videoplayback?expire=1631616016&ei=sCdAYdCGAZD7kgadtbT4AQ&ip=174.126.238.181&id=o-AB6FjTBkJE3OCIRR6E4MV93s_EQC6Wem5GQY3nsgnbhd&itag=135&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C278%2C394%2C395%2C396%2C397%2C398%2C399&source=youtube&requiressl=yes&mh=7c&mm=31%2C29&mn=sn-j5caxvox-cvpe%2Csn-nx5s7n76&ms=au%2Crdu&mv=m&mvi=2&pl=24&initcwndbps=1847500&vprv=1&mime=video%2Fmp4&ns=flWNw8FESVeF__wRUpmII5wG&gir=yes&clen=8935471&dur=212.040&lmt=1628130953383927&mt=1631594013&fvip=4&keepalive=yes&fexp=24001373%2C24007246&beids=9466585&c=WEB&txp=5535434&n=7iJIhDM_ktV3660j&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRAIgGK75aA2fI4WwFkPZV42bMbNbkOG6OtuIXjuFNqezr4MCIGst4mxew5OX2AYcbwTj9f0e3PBAS8QGTYs3yDeVbNdT&sig=AOq0QJ8wRgIhALIazj23a4uqcFohSavkQCmmByUR_7DxHo6ohKjOc5yiAiEAoleQpPWYZ8tO0O7gZsT_H4aUuGh_p9F2oNjrx5eLKqQ=", "vbr": 337.123, "filesize": 8935471, "http_headers": {"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.33 Safari/537.36", "Accept-Encoding": "gzip, deflate", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5"}, "asr": null, "downloader_options": {"http_chunk_size": 10485760}, "format_note": "480p", "acodec": "none", "quality": 3, "container": "mp4_dash", "ext": "mp4", "protocol": "https", "vcodec": "avc1.4d401e", "tbr": 337.123, "width": 854}, {"fps": 25, "format": "244 - 854x480 (480p)", "height": 480, "format_id": "244", "url": "https://r2---sn-j5caxvox-cvpe.googlevideo.com/videoplayback?expire=1631616016&ei=sCdAYdCGAZD7kgadtbT4AQ&ip=174.126.238.181&id=o-AB6FjTBkJE3OCIRR6E4MV93s_EQC6Wem5GQY3nsgnbhd&itag=244&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C278%2C394%2C395%2C396%2C397%2C398%2C399&source=youtube&requiressl=yes&mh=7c&mm=31%2C29&mn=sn-j5caxvox-cvpe%2Csn-nx5s7n76&ms=au%2Crdu&mv=m&mvi=2&pl=24&initcwndbps=1847500&vprv=1&mime=video%2Fwebm&ns=flWNw8FESVeF__wRUpmII5wG&gir=yes&clen=10532052&dur=212.040&lmt=1628133102160132&mt=1631594013&fvip=4&keepalive=yes&fexp=24001373%2C24007246&beids=9466585&c=WEB&txp=5535434&n=7iJIhDM_ktV3660j&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhAJKhoFYw7Q-a6k4sgJZO69oCR8yu-_86cfVvZDVnlxcrAiEA6ghEBhCbESj7wH3LcFmHp4dNFEVI_H3dazQKTWPxMnk%3D&sig=AOq0QJ8wRgIhAN3WW-w6rLDTOWF4r6kqvmbOlsgkp0wgxnvHeMJOPa8oAiEA4s9L5_hPFQ9pjUD9VWVjJrYKTT5pzeFfXCD2Cd7tR6s=", "vbr": 397.36, "filesize": 10532052, "http_headers": {"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.33 Safari/537.36", "Accept-Encoding": "gzip, deflate", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5"}, "asr": null, "downloader_options": {"http_chunk_size": 10485760}, "format_note": "480p", "acodec": "none", "quality": 3, "container": "webm_dash", "ext": "webm", "protocol": "https", "vcodec": "vp9", "tbr": 397.36, "width": 854}, {"fps": 25, "format": "397 - 854x480 (480p)", "height": 480, "format_id": "397", "url": "https://r2---sn-j5caxvox-cvpe.googlevideo.com/videoplayback?expire=1631616016&ei=sCdAYdCGAZD7kgadtbT4AQ&ip=174.126.238.181&id=o-AB6FjTBkJE3OCIRR6E4MV93s_EQC6Wem5GQY3nsgnbhd&itag=397&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C278%2C394%2C395%2C396%2C397%2C398%2C399&source=youtube&requiressl=yes&mh=7c&mm=31%2C29&mn=sn-j5caxvox-cvpe%2Csn-nx5s7n76&ms=au%2Crdu&mv=m&mvi=2&pl=24&initcwndbps=1847500&vprv=1&mime=video%2Fmp4&ns=flWNw8FESVeF__wRUpmII5wG&gir=yes&clen=11930795&dur=212.040&lmt=1624948131494790&mt=1631594013&fvip=4&keepalive=yes&fexp=24001373%2C24007246&beids=9466585&c=WEB&txp=5531432&n=7iJIhDM_ktV3660j&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRAIgcoFDjq9zIiJ_ofRzFyQjv_6wRL4EV6mbVDCkwBYN8t0CIFvyQbtU537nm7Iw9-t_e9Scxlcyt5SwbrhtWIiA4zt5&sig=AOq0QJ8wRgIhAPf62Xh3p8Sk1zf3iZTK1zaEoSJz6eft26JJ9VR8N2CPAiEAtZtW4sdAYDDmAqS524Qxy6ZD9BZgvdFZWkTyZwrbkyo=", "vbr": 450.133, "filesize": 11930795, "http_headers": {"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.33 Safari/537.36", "Accept-Encoding": "gzip, deflate", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5"}, "asr": null, "downloader_options": {"http_chunk_size": 10485760}, "format_note": "480p", "acodec": "none", "quality": 3, "container": "mp4_dash", "ext": "mp4", "protocol": "https", "vcodec": "av01.0.04M.08", "tbr": 450.133, "width": 854}, {"fps": 25, "format": "136 - 1280x720 (720p)", "height": 720, "format_id": "136", "url": "https://r2---sn-j5caxvox-cvpe.googlevideo.com/videoplayback?expire=1631616016&ei=sCdAYdCGAZD7kgadtbT4AQ&ip=174.126.238.181&id=o-AB6FjTBkJE3OCIRR6E4MV93s_EQC6Wem5GQY3nsgnbhd&itag=136&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C278%2C394%2C395%2C396%2C397%2C398%2C399&source=youtube&requiressl=yes&mh=7c&mm=31%2C29&mn=sn-j5caxvox-cvpe%2Csn-nx5s7n76&ms=au%2Crdu&mv=m&mvi=2&pl=24&initcwndbps=1847500&vprv=1&mime=video%2Fmp4&ns=flWNw8FESVeF__wRUpmII5wG&gir=yes&clen=17375531&dur=212.040&lmt=1628130952538181&mt=1631594013&fvip=4&keepalive=yes&fexp=24001373%2C24007246&beids=9466585&c=WEB&txp=5535434&n=7iJIhDM_ktV3660j&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRQIhAN1ZFXb_ZOXOofsWiuyAlB_PEmFOXLMiMWlLzcrb33zgAiARNS1RT8FOIEst33KIGloOKR9Y6_fSg0o1s_cr8PrDTQ%3D%3D&sig=AOq0QJ8wRAIgeCl2ZWgWyfopoNoWUVV4Lijvm2aYyFZhH4KZsPLOWb8CICgDOyLogiCIwOIiNz4zamJTtkakt8VDaLhV__Z4b9An", "vbr": 655.556, "filesize": 17375531, "http_headers": {"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.33 Safari/537.36", "Accept-Encoding": "gzip, deflate", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5"}, "asr": null, "downloader_options": {"http_chunk_size": 10485760}, "format_note": "720p", "acodec": "none", "quality": 4, "container": "mp4_dash", "ext": "mp4", "protocol": "https", "vcodec": "avc1.4d401f", "tbr": 655.556, "width": 1280}, {"fps": 25, "format": "247 - 1280x720 (720p)", "height": 720, "format_id": "247", "url": "https://r2---sn-j5caxvox-cvpe.googlevideo.com/videoplayback?expire=1631616016&ei=sCdAYdCGAZD7kgadtbT4AQ&ip=174.126.238.181&id=o-AB6FjTBkJE3OCIRR6E4MV93s_EQC6Wem5GQY3nsgnbhd&itag=247&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C278%2C394%2C395%2C396%2C397%2C398%2C399&source=youtube&requiressl=yes&mh=7c&mm=31%2C29&mn=sn-j5caxvox-cvpe%2Csn-nx5s7n76&ms=au%2Crdu&mv=m&mvi=2&pl=24&initcwndbps=1847500&vprv=1&mime=video%2Fwebm&ns=flWNw8FESVeF__wRUpmII5wG&gir=yes&clen=18530394&dur=212.040&lmt=1628133100666546&mt=1631594013&fvip=4&keepalive=yes&fexp=24001373%2C24007246&beids=9466585&c=WEB&txp=5535434&n=7iJIhDM_ktV3660j&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRQIhAIEWZfOAPFeUw2-xI2l_IvQ2XJG92s_5Yd77n9pgCvISAiByI9uxsyN5xk_PtxDSFF4UG1Q-7OpXrYr3i6jBz880JQ%3D%3D&sig=AOq0QJ8wRgIhAL3l9xAE1mvJmZ5wEGtRO-f_vhHCRtts8Cj-jD1ZpcUqAiEAsKbVkjHRYLtXGbmwUT1qe6medMxHmIYGsLrPTftbuYI=", "vbr": 699.128, "filesize": 18530394, "http_headers": {"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.33 Safari/537.36", "Accept-Encoding": "gzip, deflate", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5"}, "asr": null, "downloader_options": {"http_chunk_size": 10485760}, "format_note": "720p", "acodec": "none", "quality": 4, "container": "webm_dash", "ext": "webm", "protocol": "https", "vcodec": "vp9", "tbr": 699.128, "width": 1280}, {"fps": 25, "format": "398 - 1280x720 (720p)", "height": 720, "format_id": "398", "url": "https://r2---sn-j5caxvox-cvpe.googlevideo.com/videoplayback?expire=1631616016&ei=sCdAYdCGAZD7kgadtbT4AQ&ip=174.126.238.181&id=o-AB6FjTBkJE3OCIRR6E4MV93s_EQC6Wem5GQY3nsgnbhd&itag=398&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C278%2C394%2C395%2C396%2C397%2C398%2C399&source=youtube&requiressl=yes&mh=7c&mm=31%2C29&mn=sn-j5caxvox-cvpe%2Csn-nx5s7n76&ms=au%2Crdu&mv=m&mvi=2&pl=24&initcwndbps=1847500&vprv=1&mime=video%2Fmp4&ns=flWNw8FESVeF__wRUpmII5wG&gir=yes&clen=22755091&dur=212.040&lmt=1624947952304594&mt=1631594013&fvip=4&keepalive=yes&fexp=24001373%2C24007246&beids=9466585&c=WEB&txp=5531432&n=7iJIhDM_ktV3660j&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhAPYMgegprlBtjtqtXJkbsbAeeXS6XNWySYoxae2IvrLKAiEApXXh7mr2C8UMrfXNGaIB--m-4kkSy91IN3muC3Z4t18%3D&sig=AOq0QJ8wRgIhAPm3dPdmeLw7e6muiJwhSDKe1nlkHoQ1WkXcmz6ngRkVAiEAqShfUbwRICZ7TI3pggP-3rWlFlpExlV1ebinn5JLc4k=", "vbr": 858.52, "filesize": 22755091, "http_headers": {"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.33 Safari/537.36", "Accept-Encoding": "gzip, deflate", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5"}, "asr": null, "downloader_options": {"http_chunk_size": 10485760}, "format_note": "720p", "acodec": "none", "quality": 4, "container": "mp4_dash", "ext": "mp4", "protocol": "https", "vcodec": "av01.0.05M.08", "tbr": 858.52, "width": 1280}, {"fps": 25, "format": "399 - 1920x1080 (1080p)", "height": 1080, "format_id": "399", "url": "https://r2---sn-j5caxvox-cvpe.googlevideo.com/videoplayback?expire=1631616016&ei=sCdAYdCGAZD7kgadtbT4AQ&ip=174.126.238.181&id=o-AB6FjTBkJE3OCIRR6E4MV93s_EQC6Wem5GQY3nsgnbhd&itag=399&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C278%2C394%2C395%2C396%2C397%2C398%2C399&source=youtube&requiressl=yes&mh=7c&mm=31%2C29&mn=sn-j5caxvox-cvpe%2Csn-nx5s7n76&ms=au%2Crdu&mv=m&mvi=2&pl=24&initcwndbps=1847500&vprv=1&mime=video%2Fmp4&ns=flWNw8FESVeF__wRUpmII5wG&gir=yes&clen=41772768&dur=212.040&lmt=1624948154710174&mt=1631594013&fvip=4&keepalive=yes&fexp=24001373%2C24007246&beids=9466585&c=WEB&txp=5531432&n=7iJIhDM_ktV3660j&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRAIgcCja1-Lsriw-hfwc2KwX9G4VBYJ1a9p3OGBQYwa3pywCIFMEO6QGSXUQoQomqSYxqUu4HOZHqHnhVUFlaroP-l2h&sig=AOq0QJ8wRQIgIPISNyI_OU4-cpSkzZjvP6oF2w0izlJKe8DCwl9bH5QCIQDHydWqPWx7k9ctYkmtelvOV6Ao_hUwUzeJuMaaXqllkw==", "vbr": 1576.033, "filesize": 41772768, "http_headers": {"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.33 Safari/537.36", "Accept-Encoding": "gzip, deflate", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5"}, "asr": null, "downloader_options": {"http_chunk_size": 10485760}, "format_note": "1080p", "acodec": "none", "quality": 5, "container": "mp4_dash", "ext": "mp4", "protocol": "https", "vcodec": "av01.0.08M.08", "tbr": 1576.033, "width": 1920}, {"fps": 25, "format": "137 - 1920x1080 (1080p)", "height": 1080, "format_id": "137", "url": "https://r2---sn-j5caxvox-cvpe.googlevideo.com/videoplayback?expire=1631616016&ei=sCdAYdCGAZD7kgadtbT4AQ&ip=174.126.238.181&id=o-AB6FjTBkJE3OCIRR6E4MV93s_EQC6Wem5GQY3nsgnbhd&itag=137&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C278%2C394%2C395%2C396%2C397%2C398%2C399&source=youtube&requiressl=yes&mh=7c&mm=31%2C29&mn=sn-j5caxvox-cvpe%2Csn-nx5s7n76&ms=au%2Crdu&mv=m&mvi=2&pl=24&initcwndbps=1847500&vprv=1&mime=video%2Fmp4&ns=flWNw8FESVeF__wRUpmII5wG&gir=yes&clen=93990963&dur=212.040&lmt=1628130818054956&mt=1631594013&fvip=4&keepalive=yes&fexp=24001373%2C24007246&beids=9466585&c=WEB&txp=5535434&n=7iJIhDM_ktV3660j&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRQIgfImvk-1wtFA9tIjgPP0B-zd95cwOlEHMhBvV3aE7zl0CIQCEf3d3oZfoThLk__WJL1AOe1URRgkj_SIcftgH8HG_jg%3D%3D&sig=AOq0QJ8wRgIhAJItHQu2MeVv3to1Z0luBDsircwDXjWzWeBmYRAKDW9rAiEA1iafsfDk5Yp-9zACBxgmxyL_pidNC3mqYV_TKjHcklY=", "vbr": 3546.159, "filesize": 93990963, "http_headers": {"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.33 Safari/537.36", "Accept-Encoding": "gzip, deflate", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5"}, "asr": null, "downloader_options": {"http_chunk_size": 10485760}, "format_note": "1080p", "acodec": "none", "quality": 5, "container": "mp4_dash", "ext": "mp4", "protocol": "https", "vcodec": "avc1.640028", "tbr": 3546.159, "width": 1920}, {"fps": 25, "format": "18 - 640x360 (360p)", "height": 360, "format_id": "18", "url": "https://r2---sn-j5caxvox-cvpe.googlevideo.com/videoplayback?expire=1631616016&ei=sCdAYdCGAZD7kgadtbT4AQ&ip=174.126.238.181&id=o-AB6FjTBkJE3OCIRR6E4MV93s_EQC6Wem5GQY3nsgnbhd&itag=18&source=youtube&requiressl=yes&mh=7c&mm=31%2C29&mn=sn-j5caxvox-cvpe%2Csn-nx5s7n76&ms=au%2Crdu&mv=m&mvi=2&pl=24&initcwndbps=1847500&vprv=1&mime=video%2Fmp4&ns=zMV9mnhHv3UnwW1w1ilGGmgG&gir=yes&clen=16724503&ratebypass=yes&dur=212.091&lmt=1624987724234745&mt=1631594013&fvip=4&fexp=24001373%2C24007246&beids=9466585&c=WEB&txp=5530322&n=EFswOlbBGAoK1nv1&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cratebypass%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRQIhANvKM6JozEQa97eFLe2GBuj7K6UqHz8r0ilGwRnrl5OBAiAHdOlZ3a0nr2sKPzU2cfBNdSgdtRYyYem2sOJCFRRdkg%3D%3D&sig=AOq0QJ8wRQIhAO27Mc8XHMtx364tjalsNLXk8X5zzE11s4ovWD-0cVMwAiB4giGZbFxOvmRb0Eo1OTuExAAWvFfl_IB8vrJXPHMAsQ==", "filesize": 16724503, "http_headers": {"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.33 Safari/537.36", "Accept-Encoding": "gzip, deflate", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-us,en;q=0.5"}, "asr": 44100, "format_note": "360p", "acodec": "mp4a.40.2", "quality": 2, "ext": "mp4", "protocol": "https", "vcodec": "avc1.42001E", "tbr": 630.842, "width": 640}], "id": "dQw4w9WgXcQ", "format_id": "137+251", "uploader": "Rick Astley", "channel_id": "UCuAXFkgsw1L7xaCfnd5JJOw", "acodec": "opus", "thumbnails": [{"height": 94, "resolution": "168x94", "url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLDd2KtelLHaNSXrI9_5K-NvTscKNw", "width": 168, "id": "0"}, {"height": 110, "resolution": "196x110", "url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg?sqp=-oaymwEbCMQBEG5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLBUpEOOWUXWkNyijQuZ4UPzp2BE-w", "width": 196, "id": "1"}, {"height": 138, "resolution": "246x138", "url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg?sqp=-oaymwEcCPYBEIoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBCyhr8AqpJ1SxKVU6SyK5ODJ_IpA", "width": 246, "id": "2"}, {"height": 188, "resolution": "336x188", "url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLB_p0PncTtkrhaNDZtntrE3gKkoYw", "width": 336, "id": "3"}, {"height": 1080, "resolution": "1920x1080", "url": "https://i.ytimg.com/vi_webp/dQw4w9WgXcQ/maxresdefault.webp", "width": 1920, "id": "4"}], "extractor_key": "Youtube", "fulltitle": "Rick Astley - Never Gonna Give You Up (Official Music Video)", "categories": ["Music"], "view_count": 1046140382, "format": "137 - 1920x1080 (1080p)+251 - audio only (tiny)", "is_live": null, "upload_date": "20091024", "track": "Never Gonna Give You Up (7\" Mix)", "extractor": "youtube", "artist": "Rick Astley", "abr": 129.689, "creator": "Rick Astley", "channel": "Rick Astley", "title": "Rick Astley - Never Gonna Give You Up (Official Music Video)", "vcodec": "avc1.640028", "display_id": "dQw4w9WgXcQ", "width": 1920, "alt_title": "Never Gonna Give You Up (7\" Mix)", "channel_url": "https://www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw", "stretched_ratio": null, "uploader_id": "UCuAXFkgsw1L7xaCfnd5JJOw", "like_count": 11756010, "playlist": null, "tags": ["rick astley", "Never Gonna Give You Up", "nggyu", "never gonna give you up lyrics", "rick rolled", "the boys soundtrack", "the boys amazon prime", "Never gonna give you up the boys", "official", "Rick Roll", "music video", "Rick Astley album", "rick astley official", "together forever", "Whenever You Need Somebody", "rickrolled", "WRECK-IT RALPH 2", "Fortnite song", "Fortnite event", "Fortnite dance", "fortnite never gonna give you up", "rick astley never gonna give you up", "rick astley never gonna give you up lyrics"], "duration": 212, "uploader_url": "http://www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw", "dislike_count": 318477, "playlist_index": null} \ No newline at end of file diff --git a/backend/test/tests.js b/backend/test/tests.js index 99b79b2..c52fa13 100644 --- a/backend/test/tests.js +++ b/backend/test/tests.js @@ -339,4 +339,14 @@ describe('Downloader', function() { const args = await downloader_api.generateArgs(url, 'video', sub_options, 'admin'); console.log(args); }); + + it('Generate kodi NFO file', async function() { + const nfo_file_path = './test/sample.nfo'; + if (fs.existsSync(nfo_file_path)) { + fs.unlinkSync(nfo_file_path); + } + const sample_json = fs.readJSONSync('./test/sample.info.json'); + downloader_api.generateNFOFile(sample_json, nfo_file_path); + assert(fs.existsSync(nfo_file_path), true); + }); }); diff --git a/backend/utils.js b/backend/utils.js index 9c07833..137274d 100644 --- a/backend/utils.js +++ b/backend/utils.js @@ -45,8 +45,7 @@ async function getDownloadedFilesByType(basePath, type, full_metadata = false) { files.push(jsonobj); continue; } - var upload_date = jsonobj.upload_date; - upload_date = upload_date ? `${upload_date.substring(0, 4)}-${upload_date.substring(4, 6)}-${upload_date.substring(6, 8)}` : null; + var upload_date = formatDateString(jsonobj.upload_date); var isaudio = type === 'audio'; var file_obj = new File(id, jsonobj.title, jsonobj.thumbnail, isaudio, jsonobj.duration, jsonobj.webpage_url, jsonobj.uploader, @@ -295,6 +294,10 @@ function removeFileExtension(filename) { return filename_parts.join('.'); } +function formatDateString(date_string) { + return date_string ? `${date_string.substring(0, 4)}-${date_string.substring(4, 6)}-${date_string.substring(6, 8)}` : 'N/A'; +} + function createEdgeNGrams(str) { if (str && str.length > 3) { const minGram = 3 @@ -389,6 +392,7 @@ module.exports = { getCurrentDownloader: getCurrentDownloader, recFindByExt: recFindByExt, removeFileExtension: removeFileExtension, + formatDateString: formatDateString, cropFile: cropFile, createEdgeNGrams: createEdgeNGrams, wait: wait, diff --git a/src/app/settings/settings.component.html b/src/app/settings/settings.component.html index e7e0223..74a57dd 100644 --- a/src/app/settings/settings.component.html +++ b/src/app/settings/settings.component.html @@ -1,12 +1,5 @@

Settings

- - - - - + @@ -272,9 +265,12 @@ Also known as a Client ID. Generating a key is easy!
-
+
Use SponsorBlock API
+
+ Generate NFO files +
@@ -423,52 +419,59 @@ - -
-
- Allow user registration + + +
+ Users
- - - - - Internal - - - LDAP - - - -
-
- - - -
-
- - - -
-
- - - -
+ + +
- - - + Allow user registration
-
- - - + + + + + Internal + + + LDAP + + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
- -
- + +
diff --git a/src/app/settings/settings.component.ts b/src/app/settings/settings.component.ts index 483c4db..7d8b4d3 100644 --- a/src/app/settings/settings.component.ts +++ b/src/app/settings/settings.component.ts @@ -12,6 +12,7 @@ import { ConfirmDialogComponent } from 'app/dialogs/confirm-dialog/confirm-dialo import { moveItemInArray, CdkDragDrop } from '@angular/cdk/drag-drop'; import { InputDialogComponent } from 'app/input-dialog/input-dialog.component'; import { EditCategoryDialogComponent } from 'app/dialogs/edit-category-dialog/edit-category-dialog.component'; +import { ActivatedRoute, Router } from '@angular/router'; @Component({ selector: 'app-settings', @@ -38,17 +39,28 @@ export class SettingsComponent implements OnInit { latestGithubRelease = null; CURRENT_VERSION = CURRENT_VERSION - get settingsAreTheSame() { + tabs = ['main', 'downloader', 'extra', 'database', 'advanced', 'users', 'logs']; + tabIndex = 0; + + INDEX_TO_TAB = Object.assign({}, this.tabs); + TAB_TO_INDEX = {}; + + usersTabDisabledTooltip = $localize`You must enable multi-user mode to access this tab.`; + + get settingsAreTheSame(): boolean { this._settingsSame = this.settingsSame() return this._settingsSame; } - set settingsAreTheSame(val) { + set settingsAreTheSame(val: boolean) { this._settingsSame = val; } constructor(public postsService: PostsService, private snackBar: MatSnackBar, private sanitizer: DomSanitizer, - private dialog: MatDialog) { } + private dialog: MatDialog, private router: Router, private route: ActivatedRoute) { + // invert index to tab + Object.keys(this.INDEX_TO_TAB).forEach(key => { this.TAB_TO_INDEX[this.INDEX_TO_TAB[key]] = key; }); + } ngOnInit() { if (this.postsService.initialized) { @@ -66,6 +78,9 @@ export class SettingsComponent implements OnInit { this.generated_bookmarklet_code = this.sanitizer.bypassSecurityTrustUrl(this.generateBookmarkletCode()); this.getLatestGithubRelease(); + + const tab = this.route.snapshot.paramMap.get('tab'); + this.tabIndex = tab && this.TAB_TO_INDEX[tab] ? this.TAB_TO_INDEX[tab] : 0; } getConfig() { @@ -98,6 +113,11 @@ export class SettingsComponent implements OnInit { this.new_config = JSON.parse(JSON.stringify(this.initial_config)); } + tabChanged(event) { + const index = event['index']; + this.router.navigate(['/settings', {tab: this.INDEX_TO_TAB[index]}]); + } + dropCategory(event: CdkDragDrop) { moveItemInArray(this.postsService.categories, event.previousIndex, event.currentIndex); this.postsService.updateCategories(this.postsService.categories).subscribe(res => { From db53a12635572e67f84c8de6947567b309878a6c Mon Sep 17 00:00:00 2001 From: Isaac Abadi Date: Wed, 22 Sep 2021 21:29:15 -0600 Subject: [PATCH 15/15] Added Korean translations and updated source translations file --- src/app/settings/settings.component.ts | 2 +- src/assets/i18n/messages.en.xlf | 747 +++++-- src/assets/i18n/messages.ko.json | 261 +++ src/assets/i18n/messages.ko.xlf | 2634 ++++++++++++++++++++++++ 4 files changed, 3452 insertions(+), 192 deletions(-) create mode 100644 src/assets/i18n/messages.ko.json create mode 100644 src/assets/i18n/messages.ko.xlf diff --git a/src/app/settings/settings.component.ts b/src/app/settings/settings.component.ts index 7d8b4d3..c30d153 100644 --- a/src/app/settings/settings.component.ts +++ b/src/app/settings/settings.component.ts @@ -21,7 +21,7 @@ import { ActivatedRoute, Router } from '@angular/router'; }) export class SettingsComponent implements OnInit { all_locales = isoLangs; - supported_locales = ['en', 'es', 'de', 'fr', 'nl', 'pt', 'it', 'ca', 'cs', 'nb', 'ru', 'zh', 'id', 'en-GB']; + supported_locales = ['en', 'es', 'de', 'fr', 'nl', 'pt', 'it', 'ca', 'cs', 'nb', 'ru', 'zh', 'ko', 'id', 'en-GB']; initialLocale = localStorage.getItem('locale'); initial_config = null; diff --git a/src/assets/i18n/messages.en.xlf b/src/assets/i18n/messages.en.xlf index 99e0f16..da1b3d7 100644 --- a/src/assets/i18n/messages.en.xlf +++ b/src/assets/i18n/messages.en.xlf @@ -26,22 +26,10 @@ src/app/settings/settings.component.html - 75 + 67 Dark mode toggle label - - Settings - - src/app/app.component.html - 28 - - - src/app/settings/settings.component.html - 1 - - Settings menu label - Home @@ -58,7 +46,7 @@ src/app/components/login/login.component.html - 15 + 34 src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html @@ -82,6 +70,25 @@ Navigation menu Downloads Page title + + Settings + + src/app/app.component.html + 49 + + + src/app/settings/settings.component.html + 1 + + Settings menu label + + + Download for has been queued! + + src/app/main/main.component.ts + 469 + + Only Audio @@ -122,13 +129,13 @@ YT search View button for searched video - - Multi-download Mode + + Autoplay src/app/main/main.component.html 70,71 - Multi-download Mode checkbox + Autoplay checkbox Cancel @@ -154,11 +161,19 @@ Use custom args checkbox + + Replace args + + src/app/main/main.component.html + 116,117 + + Replace args + Custom args src/app/main/main.component.html - 115 + 120 src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html @@ -174,7 +189,7 @@ No need to include URL, just everything after. Args are delimited using two commas like so: ,, src/app/main/main.component.html - 118,119 + 123,124 Custom Args input hint @@ -182,7 +197,7 @@ Use custom output src/app/main/main.component.html - 126,127 + 131,132 Use custom output checkbox @@ -190,7 +205,7 @@ Custom output src/app/main/main.component.html - 130 + 135 Custom output placeholder @@ -198,7 +213,7 @@ Documentation src/app/main/main.component.html - 132 + 137 src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html @@ -214,7 +229,7 @@ src/app/settings/settings.component.html - 125 + 117 Youtube-dl output template documentation link @@ -222,7 +237,7 @@ Path is relative to the config download path. Don't include extension. src/app/main/main.component.html - 133 + 138 src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html @@ -242,26 +257,10 @@ Crop file src/app/main/main.component.html - 155,156 + 160,161 Crop video checkbox - - Crop from (seconds) - - src/app/main/main.component.html - 159 - - Crop from placeholder - - - Crop to (seconds) - - src/app/main/main.component.html - 164 - - Crop to placeholder - Simulated command: @@ -274,7 +273,7 @@ Use authentication src/app/main/main.component.html - 140,141 + 145,146 Use authentication checkbox @@ -282,7 +281,7 @@ Username src/app/main/main.component.html - 144 + 149 YT Username placeholder @@ -290,7 +289,7 @@ Password src/app/main/main.component.html - 149 + 154 src/app/dialogs/add-user-dialog/add-user-dialog.component.html @@ -302,6 +301,22 @@ YT Password placeholder + + Crop from (seconds) + + src/app/main/main.component.html + 164 + + Crop from placeholder + + + Crop to (seconds) + + src/app/main/main.component.html + 169 + + Crop to placeholder + Create a playlist @@ -386,7 +401,7 @@ src/app/settings/settings.component.html - 18 + 10 Subscription URL input placeholder @@ -492,6 +507,10 @@ src/app/dialogs/arg-modifier-dialog/arg-modifier-dialog.component.html 84 + + src/app/dialogs/confirm-dialog/confirm-dialog.component.html + 16 + src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html 66 @@ -500,6 +519,18 @@ src/app/dialogs/edit-category-dialog/edit-category-dialog.component.html 54 + + src/app/settings/settings.component.html + 490 + + + src/app/components/downloads/downloads.component.html + 61 + + + src/app/components/downloads/downloads.component.html + 61 + src/app/components/modify-users/modify-users.component.html 61 @@ -586,7 +617,7 @@ src/app/dialogs/about-dialog/about-dialog.component.html - 59 + 70 src/app/dialogs/share-media-dialog/share-media-dialog.component.html @@ -638,7 +669,7 @@ src/app/subscriptions/subscriptions.component.html - 31 + 34 src/app/subscription/subscription/subscription.component.html @@ -802,7 +833,7 @@ src/app/components/login/login.component.html - 35 + 38 Register user button @@ -854,7 +885,7 @@ src/app/settings/settings.component.html - 464 + 487 src/app/components/modify-users/modify-users.component.html @@ -918,6 +949,38 @@ Files search placeholder + + File type + + src/app/components/recent-videos/recent-videos.component.html + 52 + + File type + + + Both + + src/app/components/recent-videos/recent-videos.component.html + 54 + + Both + + + Video only + + src/app/components/recent-videos/recent-videos.component.html + 55 + + Video only + + + Audio only + + src/app/components/recent-videos/recent-videos.component.html + 56 + + Audio only + No videos found. @@ -982,7 +1045,7 @@ src/app/components/unified-file-card/unified-file-card.component.html - 37 + 43 Playlist edit button @@ -998,11 +1061,11 @@ src/app/components/unified-file-card/unified-file-card.component.html - 33 + 39 src/app/components/unified-file-card/unified-file-card.component.html - 39 + 45 Delete playlist @@ -1038,7 +1101,7 @@ src/app/components/unified-file-card/unified-file-card.component.html - 34 + 40 Delete and blacklist video button @@ -1046,7 +1109,7 @@ views src/app/player/player.component.html - 15 + 16 View count label @@ -1134,7 +1197,7 @@ Playlists src/app/subscriptions/subscriptions.component.html - 27 + 30 Subscriptions playlists title @@ -1150,7 +1213,7 @@ You have no channel subscriptions. src/app/subscriptions/subscriptions.component.html - 24 + 27 No channel subscriptions text @@ -1158,7 +1221,7 @@ Name not available. Playlist retrieval in progress. src/app/subscriptions/subscriptions.component.html - 33 + 36 Subscription playlist not available text @@ -1166,15 +1229,22 @@ You have no playlist subscriptions. src/app/subscriptions/subscriptions.component.html - 43 + 46 No playlist subscriptions text + + You must enable multi-user mode to access this tab. + + src/app/settings/settings.component.ts + 48 + + Main src/app/settings/settings.component.html - 12 + 4 Main settings label @@ -1182,7 +1252,7 @@ Downloader src/app/settings/settings.component.html - 102 + 94 Downloader settings label @@ -1190,7 +1260,7 @@ Extra src/app/settings/settings.component.html - 185 + 198 Extra settings label @@ -1198,7 +1268,7 @@ Database src/app/settings/settings.component.html - 284 + 303 Database settings label @@ -1206,47 +1276,27 @@ Advanced src/app/settings/settings.component.html - 320 + 339 Host settings label - - Users - - src/app/settings/settings.component.html - 403 - - - src/app/settings/settings.component.html - 403 - - Users settings label - Logs src/app/settings/settings.component.html - 451 + 476 src/app/settings/settings.component.html - 451 + 476 Logs settings label - - {VAR_SELECT, select, true {Close} false {Cancel} other {otha}} - - src/app/settings/settings.component.html - 467 - - Settings cancel and close button - URL this app will be accessed from, without the port. src/app/settings/settings.component.html - 19 + 11 URL setting input hint @@ -1254,7 +1304,7 @@ Port src/app/settings/settings.component.html - 24 + 16 Port input placeholder @@ -1262,7 +1312,7 @@ The desired port. Default is 17442. src/app/settings/settings.component.html - 25 + 17 Port setting input hint @@ -1270,7 +1320,7 @@ Multi-user mode src/app/settings/settings.component.html - 34 + 26 Multi user mode setting @@ -1278,7 +1328,7 @@ Users base path src/app/settings/settings.component.html - 38 + 30 Users base path placeholder @@ -1286,7 +1336,7 @@ Base path for users and their downloaded videos. src/app/settings/settings.component.html - 39 + 31 Users base path hint @@ -1294,7 +1344,7 @@ Allow subscriptions src/app/settings/settings.component.html - 48 + 40 Allow subscriptions setting @@ -1302,7 +1352,7 @@ Subscriptions base path src/app/settings/settings.component.html - 52 + 44 Subscriptions base path input setting placeholder @@ -1310,7 +1360,7 @@ Base path for videos from your subscribed channels and playlists. It is relative to YTDL-Material's root folder. src/app/settings/settings.component.html - 53 + 45 Subscriptions base path setting input hint @@ -1318,7 +1368,7 @@ Check interval src/app/settings/settings.component.html - 58 + 50 Check interval input setting placeholder @@ -1326,7 +1376,7 @@ Unit is seconds, only include numbers. src/app/settings/settings.component.html - 59 + 51 Check interval setting input hint @@ -1334,7 +1384,7 @@ Sometimes new videos are downloaded before being fully processed. This setting will mean new videos will be checked for a higher quality version the following day. src/app/settings/settings.component.html - 63 + 55 Redownload fresh uploads tooltip @@ -1342,7 +1392,7 @@ Redownload fresh uploads src/app/settings/settings.component.html - 63 + 55 Redownload fresh uploads @@ -1350,7 +1400,7 @@ Theme src/app/settings/settings.component.html - 72 + 64 Theme select label @@ -1358,7 +1408,7 @@ Default src/app/settings/settings.component.html - 74 + 66 Default theme label @@ -1366,7 +1416,7 @@ Allow theme change src/app/settings/settings.component.html - 80 + 72 Allow theme change setting @@ -1374,7 +1424,7 @@ Language src/app/settings/settings.component.html - 89 + 81 Language select label @@ -1382,7 +1432,7 @@ Audio folder path src/app/settings/settings.component.html - 109 + 101 Audio folder path input placeholder @@ -1390,7 +1440,7 @@ Path for audio only downloads. It is relative to YTDL-Material's root folder. src/app/settings/settings.component.html - 110 + 102 Aduio path setting input hint @@ -1398,7 +1448,7 @@ Video folder path src/app/settings/settings.component.html - 116 + 108 Video folder path input placeholder @@ -1406,7 +1456,7 @@ Path for video downloads. It is relative to YTDL-Material's root folder. src/app/settings/settings.component.html - 117 + 109 Video path setting input hint @@ -1414,7 +1464,7 @@ Default file output src/app/settings/settings.component.html - 123 + 115 Default file output placeholder @@ -1422,7 +1472,7 @@ Path is relative to the above download paths. Don't include extension. src/app/settings/settings.component.html - 126 + 118 Custom Output input hint @@ -1430,7 +1480,7 @@ Global custom args src/app/settings/settings.component.html - 133 + 125 Custom args input placeholder @@ -1438,7 +1488,7 @@ Global custom args for downloads on the home page. Args are delimited using two commas like so: ,, src/app/settings/settings.component.html - 134 + 126 Custom args setting input hint @@ -1446,7 +1496,7 @@ Categories src/app/settings/settings.component.html - 144 + 136 Categories @@ -1454,7 +1504,7 @@ With this setting enabled, if a single video matches a category, the entire playlist will receive that category. src/app/settings/settings.component.html - 158 + 150 Allow playlist categorization setting tooltip @@ -1462,7 +1512,7 @@ Allow playlist categorization src/app/settings/settings.component.html - 158 + 150 Allow playlist categorization setting label @@ -1470,7 +1520,7 @@ Use youtube-dl archive src/app/settings/settings.component.html - 166 + 158 Use youtubedl archive setting @@ -1478,7 +1528,7 @@ Include thumbnail src/app/settings/settings.component.html - 170 + 162 Include thumbnail setting @@ -1486,15 +1536,47 @@ Include metadata src/app/settings/settings.component.html - 174 + 166 Include metadata setting + + Max concurrent downloads + + src/app/settings/settings.component.html + 175 + + Max concurrent downloads + + + Limits the amount of downloads that can be simultaneously downloaded. Use -1 for no limit. + + src/app/settings/settings.component.html + 176 + + Max concurrent downloads input hint + + + Download rate limit + + src/app/settings/settings.component.html + 181 + + Download rate limit input placeholder + + + Rate limits your downloads to the specified amount. Ex: 200K + + src/app/settings/settings.component.html + 182 + + Download rate limit input hint + Kill all downloads src/app/settings/settings.component.html - 178 + 191 Kill all downloads button @@ -1502,7 +1584,7 @@ Top title src/app/settings/settings.component.html - 191 + 204 Top title input placeholder @@ -1510,7 +1592,7 @@ File manager enabled src/app/settings/settings.component.html - 196 + 209 File manager enabled setting @@ -1518,7 +1600,7 @@ Downloads manager enabled src/app/settings/settings.component.html - 199 + 212 Downloads manager enabled setting @@ -1526,7 +1608,7 @@ Allow quality select src/app/settings/settings.component.html - 202 + 215 Allow quality seelct setting @@ -1534,23 +1616,23 @@ Download only mode src/app/settings/settings.component.html - 205 + 218 Download only mode setting - - Allow multi-download mode + + Allow autoplay src/app/settings/settings.component.html - 208 + 221 - Allow multi-download mode setting + Allow autoplay setting Enable Public API src/app/settings/settings.component.html - 216 + 229 Enable Public API key setting @@ -1558,7 +1640,7 @@ Public API Key src/app/settings/settings.component.html - 221 + 234 Public API Key setting placeholder @@ -1566,7 +1648,7 @@ View documentation src/app/settings/settings.component.html - 222 + 235 View API docs setting hint @@ -1574,7 +1656,7 @@ This will delete your old API key! src/app/settings/settings.component.html - 226 + 239 delete api key tooltip @@ -1582,7 +1664,7 @@ Generate src/app/settings/settings.component.html - 226 + 239 Generate key button @@ -1590,7 +1672,7 @@ Use YouTube API src/app/settings/settings.component.html - 235 + 248 Use YouTube API setting @@ -1598,7 +1680,7 @@ Youtube API Key src/app/settings/settings.component.html - 239 + 252 Youtube API Key setting placeholder @@ -1606,11 +1688,11 @@ Generating a key is easy! src/app/settings/settings.component.html - 240 + 253 src/app/settings/settings.component.html - 252 + 265 Youtube API Key setting hint @@ -1618,7 +1700,7 @@ Use Twitch API src/app/settings/settings.component.html - 244 + 257 Use Twitch API setting @@ -1626,7 +1708,7 @@ Twitch API Key src/app/settings/settings.component.html - 251 + 264 Twitch API Key setting placeholder @@ -1634,15 +1716,47 @@ Also known as a Client ID. src/app/settings/settings.component.html - 252 + 265 Twitch API Key setting hint AKA preamble + + Enables a button to skip ads when viewing supported videos. + + src/app/settings/settings.component.html + 269 + + SponsorBlock API tooltip + + + Use SponsorBlock API + + src/app/settings/settings.component.html + 269 + + Use SponsorBlock API setting + + + Generates NFO files with every download, primarily used by Kodi. + + src/app/settings/settings.component.html + 272 + + Generate NFO files tooltip + + + Generate NFO files + + src/app/settings/settings.component.html + 272 + + Generate NFO files setting + Auto-download Twitch Chat src/app/settings/settings.component.html - 247 + 260 Auto download Twitch Chat setting @@ -1650,15 +1764,15 @@ Click here src/app/settings/settings.component.html - 262 + 281 src/app/settings/settings.component.html - 268 + 287 src/app/dialogs/about-dialog/about-dialog.component.html - 25 + 36 Chrome ext click here @@ -1666,7 +1780,7 @@ to download the official YoutubeDL-Material Chrome extension manually. src/app/settings/settings.component.html - 262 + 281 Chrome click here suffix @@ -1674,7 +1788,7 @@ You must manually load the extension and modify the extension's settings to set the frontend URL. src/app/settings/settings.component.html - 263 + 282 Chrome setup suffix @@ -1682,7 +1796,7 @@ to install the official YoutubeDL-Material Firefox extension right off the Firefox extensions page. src/app/settings/settings.component.html - 268 + 287 Firefox click here suffix @@ -1690,7 +1804,7 @@ Detailed setup instructions. src/app/settings/settings.component.html - 269 + 288 Firefox setup prefix link @@ -1698,7 +1812,7 @@ Not much is required other than changing the extension's settings to set the frontend URL. src/app/settings/settings.component.html - 269 + 288 Firefox setup suffix @@ -1706,7 +1820,7 @@ Drag the link below to your bookmarks, and you're good to go! Just navigate to the YouTube video you'd like to download, and click the bookmark. src/app/settings/settings.component.html - 274 + 293 Bookmarklet instructions @@ -1714,7 +1828,7 @@ Generate 'audio only' bookmarklet src/app/settings/settings.component.html - 275 + 294 Generate audio only bookmarklet checkbox @@ -1722,7 +1836,7 @@ Database location: src/app/settings/settings.component.html - 290 + 309 Database location label @@ -1730,7 +1844,7 @@ Records per table src/app/settings/settings.component.html - 291 + 310 Records per table label @@ -1738,7 +1852,7 @@ MongoDB Connection String src/app/settings/settings.component.html - 299 + 318 MongoDB Connection String @@ -1746,7 +1860,7 @@ Example: src/app/settings/settings.component.html - 300 + 319 MongoDB Connection String setting hint AKA preamble @@ -1754,7 +1868,7 @@ Test connection string src/app/settings/settings.component.html - 304 + 323 Test connection string button @@ -1762,7 +1876,7 @@ Transfer DB to src/app/settings/settings.component.html - 308 + 327 Transfer DB button @@ -1770,7 +1884,7 @@ Database information could not be retrieved. Check the server logs for more information. src/app/settings/settings.component.html - 312 + 331 Database info not retrieved error message @@ -1778,7 +1892,7 @@ Select a downloader src/app/settings/settings.component.html - 326 + 345 Default downloader select label @@ -1786,7 +1900,7 @@ Use default downloading agent src/app/settings/settings.component.html - 335 + 354 Use default downloading agent setting @@ -1794,7 +1908,7 @@ Select a download agent src/app/settings/settings.component.html - 339 + 358 Custom downloader select label @@ -1802,7 +1916,7 @@ Log Level src/app/settings/settings.component.html - 353 + 372 Log Level label @@ -1810,7 +1924,7 @@ Login expiration src/app/settings/settings.component.html - 365 + 384 Login expiration select label @@ -1818,7 +1932,7 @@ Allow advanced download src/app/settings/settings.component.html - 376 + 395 Allow advanced downloading setting @@ -1826,7 +1940,7 @@ Use Cookies src/app/settings/settings.component.html - 384 + 403 Use cookies setting @@ -1834,7 +1948,7 @@ Set Cookies src/app/settings/settings.component.html - 385 + 404 Set cookies button @@ -1842,15 +1956,23 @@ Restart server src/app/settings/settings.component.html - 397 + 416 Restart server button + + Users + + src/app/settings/settings.component.html + 425 + + Users settings label + Allow user registration src/app/settings/settings.component.html - 407 + 431 Allow registration setting @@ -1858,7 +1980,7 @@ Auth method src/app/settings/settings.component.html - 411 + 435 Auth method select @@ -1866,7 +1988,7 @@ Internal src/app/settings/settings.component.html - 413 + 437 Internal auth method @@ -1874,7 +1996,7 @@ LDAP src/app/settings/settings.component.html - 416 + 440 LDAP auth method @@ -1882,7 +2004,7 @@ LDAP URL src/app/settings/settings.component.html - 423 + 447 LDAP URL @@ -1890,7 +2012,7 @@ Bind DN src/app/settings/settings.component.html - 428 + 452 Bind DN @@ -1898,7 +2020,7 @@ Bind Credentials src/app/settings/settings.component.html - 433 + 457 Bind Credentials @@ -1906,7 +2028,7 @@ Search Base src/app/settings/settings.component.html - 438 + 462 Search Base @@ -1914,7 +2036,7 @@ Search Filter src/app/settings/settings.component.html - 443 + 467 Search Filter @@ -1950,11 +2072,35 @@ Version label + + Installation type: + + src/app/dialogs/about-dialog/about-dialog.component.html + 25 + + Installation type + + + Commit hash: + + src/app/dialogs/about-dialog/about-dialog.component.html + 31 + + Commit hash + + + Build date: + + src/app/dialogs/about-dialog/about-dialog.component.html + 33 + + Build date + Found a bug or have a suggestion? src/app/dialogs/about-dialog/about-dialog.component.html - 25 + 36 About bug prefix @@ -1962,7 +2108,7 @@ to create an issue! src/app/dialogs/about-dialog/about-dialog.component.html - 25 + 36 About bug suffix @@ -1990,6 +2136,14 @@ Update through settings menu hint + + Docker tag: + + src/app/dialogs/about-dialog/about-dialog.component.html + 28 + + Docker tag + Select a version: @@ -2046,35 +2200,230 @@ Share video dialog title - - Session ID: + + Creating download + + src/app/components/downloads/downloads.component.ts + 58 + + + + Getting info + + src/app/components/downloads/downloads.component.ts + 59 + + + + Downloading file + + src/app/components/downloads/downloads.component.ts + 60 + + + + Complete + + src/app/components/downloads/downloads.component.ts + 61 + + + + Clear finished downloads + + src/app/components/downloads/downloads.component.ts + 129 + + + + Would you like to clear your finished downloads? + + src/app/components/downloads/downloads.component.ts + 130 + + + + Clear + + src/app/components/downloads/downloads.component.ts + 131 + + + + Error for + + src/app/components/downloads/downloads.component.ts + 238 + + + + Copy to clipboard + + src/app/components/downloads/downloads.component.ts + 240 + + + + Close + + src/app/components/downloads/downloads.component.ts + 241 + + + + Copied to clipboard! + + src/app/components/downloads/downloads.component.ts + 249 + + + + Date src/app/components/downloads/downloads.component.html - 5 + 7 - Session ID + Date - - Clear all downloads + + Title src/app/components/downloads/downloads.component.html - 18 + 13 - clear all downloads action button + Title - - (current) + + Subscription src/app/components/downloads/downloads.component.html - 6 + 23 + + Subscription + + + Stage + + src/app/components/downloads/downloads.component.html + 36 + + Stage + + + Progress + + src/app/components/downloads/downloads.component.html + 42 + + Progress + + + Actions + + src/app/components/downloads/downloads.component.html + 55 - Current session + Actions + + + Clear + + src/app/components/downloads/downloads.component.html + 68 + + + src/app/components/downloads/downloads.component.html + 68 + + Clear + + + Pause + + src/app/components/downloads/downloads.component.html + 59 + + + src/app/components/downloads/downloads.component.html + 59 + + Pause + + + Resume + + src/app/components/downloads/downloads.component.html + 60 + + + src/app/components/downloads/downloads.component.html + 60 + + Resume + + + Watch content + + src/app/components/downloads/downloads.component.html + 64 + + + src/app/components/downloads/downloads.component.html + 64 + + Watch content + + + Show error + + src/app/components/downloads/downloads.component.html + 65 + + + src/app/components/downloads/downloads.component.html + 65 + + Show error + + + Restart + + src/app/components/downloads/downloads.component.html + 66 + + Restart + + + Pause all downloads + + src/app/components/downloads/downloads.component.html + 83 + + Pause all downloads + + + Resume all downloads + + src/app/components/downloads/downloads.component.html + 84 + + Resume all downloads + + + Clear finished downloads + + src/app/components/downloads/downloads.component.html + 85 + + Clear finished downloads No downloads available! src/app/components/downloads/downloads.component.html - 25 + 90 No downloads label @@ -2322,11 +2671,19 @@ Go to subscription menu item + + Add to playlist + + src/app/components/unified-file-card/unified-file-card.component.html + 26 + + Add to playlist menu item + Delete and redownload src/app/components/unified-file-card/unified-file-card.component.html - 28 + 34 src/app/subscription/subscription-file-card/subscription-file-card.component.html @@ -2338,7 +2695,7 @@ Delete forever src/app/components/unified-file-card/unified-file-card.component.html - 31 + 37 src/app/subscription/subscription-file-card/subscription-file-card.component.html @@ -2362,6 +2719,14 @@ See less + + Skip ad + + src/app/components/skip-ad-button/skip-ad-button.component.html + 1 + + Skip ad button + Length: diff --git a/src/assets/i18n/messages.ko.json b/src/assets/i18n/messages.ko.json new file mode 100644 index 0000000..1fea7ad --- /dev/null +++ b/src/assets/i18n/messages.ko.json @@ -0,0 +1,261 @@ +{ + "004b222ff9ef9dd4771b777950ca1d0e4cd4348a": "대하여", + "994363f08f9fbfa3b3994ff7b35c6904fdff18d8": "프로필", + "adb4562d2dbd3584370e44496969d58c511ecb63": "다크", + "121cc5391cd2a5115bc2b3160379ee5b36cd7716": "설정", + "92eee6be6de0b11c924e3ab27db30257159c0a7c": "홈", + "6765b4c916060f6bc42d9bb69e80377dbcb5e4e9": "로그인", + "357064ca9d9ac859eb618e28e8126fa32be049e2": "구독", + "822fab38216f64e8166d368b59fe756ca39d301b": "다운로드", + "4a9889d36910edc8323d7bab60858ab3da6d91df": "오디오만", + "6a21ba5fb0ac804a525bf9ab168038c3ee88e661": "다운로드", + "a38ae1082fec79ba1f379978337385a539a28e73": "품질", + "4be966a9dcfbc9b54dfcc604b831c0289f847fa4": "URL 이용", + "d3f02f845e62cebd75fde451ab8479d2a8ad784d": "보기", + "96a01fafe135afc58b0f8071a4ab00234495ce18": "복수 다운로드 모드", + "6a3777f913cf3f288664f0632b9f24794fdcc24e": "취소", + "322ed150e02666fe2259c5b4614eac7066f4ffa0": "고급", + "4e4c721129466be9c3862294dc40241b64045998": "사용자 지정 인수 이용", + "ad2f8ac8b7de7945b80c8e424484da94e597125f": "사용자 지정 인수", + "a6911c2157f1b775284bbe9654ce5eb30cf45d7f": "URL을 포함할 필요가 없습니다. 이후의 모든 항목만 포함하면 됩니다. 인수는 다음과 같은 두 개의 쉼표를 사용하여 구분됩니다. : ,,", + "3a92a3443c65a52f37ca7efb8f453b35dbefbf29": "사용자 지정 출력 사용", + "d9c02face477f2f9cdaae318ccee5f89856851fb": "사용자 지정 출력", + "fcfd4675b4c90f08d18d3abede9a9a4dff4cfdc7": "문서", + "19d1ae64d94d28a29b2c57ae8671aace906b5401": "경로는 설정된 다운로드 경로에 상대적입니다. 확장자는 포함하지 마세요.", + "4e1291cb1d579e7b7a1b802e6a8fd16ef7a557fa": "파일 자르기", + "44d007f6f8a2b19f12d85f9e49647b4ac02d7cbe": "자르기 시작지점 (초)", + "661206c3ab91fa81e9d8b40afb29f1866b78432f": "자르기 마무리지점 (초)", + "b7ffe7c6586d6f3f18a9246806a7c7d5538ab43e": "시뮬레이션된 명령:", + "8fad10737d3e3735a6699a4d89cbf6c20f6bb55f": "인증 사용", + "08c74dc9762957593b91f6eb5d65efdfc975bf48": "아이디", + "c32ef07f8803a223a83ed17024b38e8d82292407": "비밀번호", + "17f0ea5d2d7a262b0e875acc70475f102aee84e6": "재생목록 만들기", + "cff1428d10d59d14e45edec3c735a27b5482db59": "제목", + "f61c6867295f3b53d23557021f2f4e0aa1d0b8fc": "종류", + "f0baeb8b69d120073b6d60d34785889b0c3232c8": "오디오", + "2d1ea268a6a9f483dbc2cbfe19bf4256a57a6af4": "동영상", + "f47e2d56dd8a145b2e9599da9730c049d52962a2": "오디오 파일", + "a52dae09be10ca3a65da918533ced3d3f4992238": "동영상", + "a9806cf78ce00eb2613eeca11354a97e033377b8": "재생목록이나 채널 구독", + "801b98c6f02fe3b32f6afa3ee854c99ed83474e6": "URL", + "93efc99ae087fc116de708ecd3ace86ca237cf30": "재생목록이나 채널 URL", + "08f5d0ef937ae17feb1b04aff15ad88911e87baf": "사용자 지정 이름", + "ea30873bd3f0d5e4fb2378eec3f0a1db77634a28": "모든 업로드 된 파일 다운로드", + "d641b8fa5ac5e85114c733b1f7de6976bd091f70": "최고 화질", + "c76a955642714b8949ff3e4b4990864a2e2cac95": "오디오 전용 모드", + "408ca4911457e84a348cecf214f02c69289aa8f1": "스트리밍 전용 모드", + "f432e1a8d6adb12e612127978ce2e0ced933959c": "이것들은 일반적인 인수 뒤에 추가됩니다.", + "98b6ec9ec138186d663e64770267b67334353d63": "사용자 지정 파일 출력", + "d7b35c384aecd25a516200d6921836374613dfe7": "취소", + "d0336848b0c375a1c25ba369b3481ee383217a4f": "구독", + "28a678e9cabf86e44c32594c43fa0e890135c20f": "마지막으로 업로드된 동영상 다운로드", + "e78c0d60ac39787f62c9159646fe0b3c1ed55a1d": "종류:", + "c52db455cca9109ee47e1a612c3f4117c09eb71b": "URL:", + "ca3dbbc7f3e011bffe32a10a3ea45cc84f30ecf1": "아이디:", + "f4e529ae5ffd73001d1ff4bbdeeb0a72e342e5c8": "닫기", + "8efc77bf327659c0fec1f518cf48a98cdcd9dddf": "아카이브 내보내기", + "3042bd3ad8dffcfeca5fd1ae6159fd1047434e95": "구독 취소", + "303e45ffae995c9817e510e38cb969e6bb3adcbf": "(일시정지)", + "a44d86aa1e6c20ced07aca3a7c081d8db9ded1c6": "아카이브:", + "616e206cb4f25bd5885fc35925365e43cf5fb929": "제목:", + "c6eb45d085384903e53ab001a3513d1de6a1dbac": "업로더:", + "109c6f4a5e46efb933612ededfaf52a13178b7e0": "파일 크기:", + "bd630d8669b16e5f264ec4649d9b469fe03e5ff4": "경로:", + "a67e7d843cef735c79d5ef1c8ba4af3e758912bb": "업로드 날짜:", + "0cc1dec590ecd74bef71a865fb364779bc42a749": "카테고리:", + "d9e83ac17026e70ef6e9c0f3240a3b2450367f40": "Youtube-dl 인수 수정", + "7fc1946abe2b40f60059c6cd19975d677095fd19": "시뮬레이션된 새 인수", + "0b71824ae71972f236039bed43f8d2323e8fd570": "인수 추가", + "c8b0e59eb491f2ac7505f0fbab747062e6b32b23": "카테고리로 찾기", + "9eeb91caef5a50256dd87e1c4b7b3e8216479377": "인수 값 이용", + "7de2451ed3fb8d8b847979bd3f0c740b970f167b": "인수 추가", + "b2623aee44b70c9a4ba1fce16c8a593b0a4c7974": "수정", + "25d8ad5eba2ec24e68295a27d6a4bb9b49e3dacd": "인수 값", + "91ecce65f1d23f9419d1c953cd6b7bc7f91c110e": "업데이터", + "b7ff2e2b909c53abe088fe60b9f4b6ac7757247f": "사용자 등록", + "024886ca34a6f309e3e51c2ed849320592c3faaa": "아이디", + "cfc2f436ec2beffb042e7511a73c89c372e86a6c": "등록", + "ebadf946ae90f13ecd0c70f09edbc0f983af8a0f": "새 쿠키 업로드", + "a8b7b9c168fd936a75e500806a8c0d7755ef1198": "참고: 새로운 쿠키를 추가하면 이전 쿠키를 덮어씁니다. 또한 쿠키는 사용자 개인이 아닌 전체에 적용됩니다.", + "98a8a42e5efffe17ab786636ed0139b4c7032d0e": "드래그 앤 드롭", + "4f389e41e4592f7f9bb76abdd8af4afdfb13f4f1": "재생목록 수정", + "52c9a103b812f258bcddc3d90a6e3f46871d25fe": "저장", + "cba36d610ddba59b6dd6fbec77199eabf0ff2de3": "재생할 때 재생목록 섞기", + "5caadefa4143cf6766a621b0f54f91f373a1f164": "콘텐츠 추가", + "33026f57ea65cd9c8a5d917a08083f71a718933a": "기본 순서", + "29376982b1205d9d6ea3d289e8e2f8e1ac2839b1": "순서 거꾸로", + "d02888c485d3aeab6de628508f4a00312a722894": "내 동영상", + "7e892ba15f2c6c17e83510e273b3e10fc32ea016": "검색", + "73423607944a694ce6f9e55cfee329681bb4d9f9": "동영상 없음.", + "3697f8583ea42868aa269489ad366103d94aece7": "수정중", + "07db550ae114d9faad3a0cbb68bcc16ab6cd31fc": "일시정지됨", + "c3b0b86523f1d10e84a71f9b188d54913a11af3b": "카테고리 수정중", + "2489eefea00931942b91f4a1ae109514b591e2e1": "규칙", + "e4eeb9106dbcbc91ca1ac3fb4068915998a70f37": "새로운 규칙 추가", + "792dc6a57f28a1066db283f2e736484f066005fd": "트위치 채팅 다운로드", + "28f86ffd419b869711aa13f5e5ff54be6d70731c": "수정", + "826b25211922a1b46436589233cb6f1a163d89b7": "삭제", + "321e4419a943044e674beb55b8039f42a9761ca5": "정보", + "e684046d73bcee88e82f7ff01e2852789a05fc32": "동영상 수:", + "34504b488c24c27e68089be549f0eeae6ebaf30b": "삭제하고 블랙리스트 추가", + "dad95154dcef3509b8cc705046061fd24994bbb7": "조회수", + "4d8a18b04a1f785ecd8021ac824e0dfd5881dbfc": "성공적으로 다운로드 완료", + "348cc5d553b18e862eb1c1770e5636f6b05ba130": "에러 발생", + "4f8b2bb476981727ab34ed40fde1218361f92c45": "세부사항", + "e9aff8e6df2e2bf6299ea27bb2894c70bc48bd4d": "에러 발생:", + "77b0c73840665945b25bd128709aa64c8f017e1c": "다운로드 시작:", + "08ff9375ec078065bcdd7637b7ea65fce2979266": "다운로드 끝:", + "ad127117f9471612f47d01eae09709da444a36a4": "파일 경로(들):", + "e2319dec5b4ccfb6ed9f55ccabd63650a8fdf547": "구독중", + "807cf11e6ac1cde912496f764c176bdfdd6b7e19": "채널", + "47546e45bbb476baaaad38244db444c427ddc502": "재생목록", + "29b89f751593e1b347eef103891b7a1ff36ec03f": "이름이 유효하지 않음. 채널 검색중.", + "4636cd4a1379c50d471e98786098c4d39e1e82ad": "구독중인 채널이 없습니다.", + "2e0a410652cb07d069f576b61eab32586a18320d": "이름이 유효하지 않음. 플레이리스트 검색중.", + "587b57ced54965d8874c3fd0e9dfedb987e5df04": "구독중인 재생목록이 없습니다.", + "82421c3e46a0453a70c42900eab51d58d79e6599": "메인", + "0ba25ad86a240576c4f20a2fada4722ebba77b1e": "다운로더", + "d5f69691f9f05711633128b5a3db696783266b58": "추가", + "fb324ec7da611c6283caa6fc6257c39a56d6aaf7": "데이터베이스", + "bc2e854e111ecf2bd7db170da5e3c2ed08181d88": "고급", + "4d13a9cd5ed3dcee0eab22cb25198d43886942be": "사용자", + "eb3d5aefff38a814b76da74371cbf02c0789a1ef": "로그", + "fe8fd36dbf5deee1d56564965787a782a66eba44": "{VAR_SELECT, select, true {닫다} false {취소} other {기타}}", + "54c512cca1923ab72faf1a0bd98d3d172469629a": "포트를 제외한 이 앱에 접속할 URL.", + "cb2741a46e3560f6bc6dfd99d385e86b08b26d72": "포트", + "22e8f1d0423a3b784fe40fab187b92c06541b577": "포트 설정. 기본 포트는 17442 입니다.", + "d4477669a560750d2064051a510ef4d7679e2f3e": "복수 사용자 모드", + "2eb03565fcdce7a7a67abc277a936a32fcf51557": "사용자 기본 경로", + "a64505c41150663968e277ec9b3ddaa5f4838798": "사용자와 그들의 동영상 다운로드를 위한 기본 경로.", + "4e3120311801c4acd18de7146add2ee4a4417773": "구독 허용", + "4bee2a4bef2d26d37c9b353c278e24e5cd309ce3": "구독 기본 경로", + "bc9892814ee2d119ae94378c905ea440a249b84a": "구독된 채널과 재생목록에서 나온 영상들을 위한 기본 경로. 경로는 YTDL-Material 루트 폴더 경로에 상대적입니다.", + "5bef4b25ba680da7fff06b86a91b1fc7e6a926e3": "확인 간격", + "0f56a7449b77630c114615395bbda4cab398efd8": "단위는 초이며, 숫자만 넣으세요.", + "13759b09a7f4074ceee8fa2f968f9815fdf63295": "가끔 새 동영상이 최고 화질 처리 전에 다운로드 될 때가 있습니다. 이 설정은 새 동영상이 더 높은 화질의 버전이 있는지 다음 날짜에 확인됨을 의미합니다.", + "3d1a47dc18b7bd8b5d9e1eb44b235ed9c4a2b513": "높은 화질 재다운로드", + "27a56aad79d8b61269ed303f11664cc78bcc2522": "테마", + "ff7cee38a2259526c519f878e71b964f41db4348": "기본", + "7a6bacee4c31cb5c0ac2d24274fb4610d8858602": "테마 변경 허용", + "fe46ccaae902ce974e2441abe752399288298619": "언어", + "ab2756805742e84ad0cc0468f4be2d8aa9f855a5": "오디오 폴더 경로", + "c2c89cdf45d46ea64d2ed2f9ac15dfa4d77e26ca": "오디오 전용 다운로드 경로. 경로는 YTDL-Material 루트 폴더 경로에 상대적입니다.", + "46826331da1949bd6fb74624447057099c9d20cd": "동영상 폴더 경로", + "17c92e6d47a213fa95b5aa344b3f258147123f93": "동영상 다운로드 경로. 경로는 YTDL-Material 루트 폴더 경로에 상대적입니다.", + "cfe829634b1144bc44b6d38cf5584ea65db9804f": "기본 파일 출력", + "1148fd45287ff09955b938756bc302042bcb29c7": "경로는 위의 다운로드 경로에 상대적입니다. 확장자는 포함하지 마세요.", + "ef418d4ece7c844f3a5e431da1aa59bedd88da7b": "전반적으로 적용될 사용자 지정 인수", + "6b995e7130b4d667eaab6c5f61b362ace486d26d": "홈페이지에서의 다운로드에 대해 전반적으로 적용될 사용자 지정 인수. 인수는 다음과 같은 두 개의 쉼표를 사용하여 구분됩니다. : ,,", + "04201f9d27abd7d6f58a4328ab98063ce1072006": "카테고리", + "1f6d3986a970af27f16f8a95ce0dc3033cc90a83": "이 설정을 사용하면, 하나의 동영상이 카테고리와 일치할 경우, 전체 재생목록에 해당 카테고리가 표시됩니다.", + "5da94ccb2301f586af26916e921bdad6d673ab58": "재생목록 카테고리화 허용", + "78e49b7339b4fa7184dd21bcaae107ce9b7076f6": "Youtube-dl 아카이브 사용", + "ffc19f32b1cba0daefc0e5668f89346db1db83ad": "썸네일 포함", + "384de8f8f112c9e6092eb2698706d391553f3e8d": "메타데이터 포함", + "fb35145bfb84521e21b6385363d59221f436a573": "모든 다운로드 종료", + "61f8fd90b5f8cb20c70371feb2ee5e1fac5a9095": "상위 제목", + "78d3531417c0d4ba4c90f0d4ae741edc261ec8df": "파일 매니저 설정됨", + "a5a1be0a5df07de9eec57f5d2a86ed0204b2e75a": "다운로드 매니저 설정됨", + "c33bd5392b39dbed36b8e5a1145163a15d45835f": "화질 선택 허용", + "bda5508e24e0d77debb28bcd9194d8fefb1cfb92": "다운로드 전용 모드", + "09d31c803a7252658694e1e3176b97f5655a3fe3": "복수 다운로드 모드 허용", + "1c4dbce56d96b8974aac24a02f7ab2ee81415014": "오픈 API 허용", + "23bd81dcc30b74d06279a26d7a42e8901c1b124e": "오픈 API 키", + "41016a73d8ad85e6cb26dffa0a8fab9fe8f60d8e": "문서 보기", + "00a94f58d9eb2e3aa561440eabea616d0c937fa2": "이것은 예전 API키를 지울 것입니다!", + "1b258b258b4cc475ceb2871305b61756b0134f4a": "생성", + "d5d7c61349f3b0859336066e6d453fc35d334fe5": "유튜브 API 사용", + "ce10d31febb3d9d60c160750570310f303a22c22": "유튜브 API 키", + "8602e313cdfa7c4cc475ccbe86459fce3c3fd986": "키를 만드는 것은 쉽습니다!", + "d162f9fcd6a7187b391e004f072ab3da8377c47d": "트위치 API 사용", + "8ae23bc4302a479f687f4b20a84c276182e2519c": "트위치 API 키", + "84ffcebac2709ca0785f4a1d5ba274433b5beabc": "클라이언트 ID라고도 알려져 있음.", + "5fb1e0083c9b2a40ac8ae7dcb2618311c291b8b9": "트위치 채팅 자동 다운로드", + "9b3cedfa83c6d7acb3210953289d1be4aab115c7": "이곳을 누르세요", + "7f09776373995003161235c0c8d02b7f91dbc4df": "공식 YoutubeDL-Material 크롬 확장 프로그램을 수동으로 다운로드 하기 위해.", + "5b5296423906ab3371fdb2b5a5aaa83acaa2ee52": "반드시 확장 프로그램을 수동으로 실행하고 확장 프로그램 설정을 수정하여 프론트엔드 URL을 설정해야 합니다.", + "9a2ec6da48771128384887525bdcac992632c863": "파이어폭스 확장 프로그램 페이지에서 바로 공식 YoutubeDL-Material 파이어폭스 확장 프로그램을 설치하기 위해.", + "eb81be6b49e195e5307811d1d08a19259d411f37": "자세한 설정 지침.", + "cb17ff8fe3961cf90f44bee97c88a3f3347a7e55": "프론트엔드 URL을 설정하기 위해 확장 프로그램 설정을 변경하는 것 외에는 필요한 것이 많지 않습니다.", + "61b81b11aad0b9d970ece2fce18405f07eac69c2": "아래 링크를 북마크에 끌어다 놓으시면 됩니다! 이제 그냥 다운로드하고자 하는 유튜브 비디오 페이지에서 북마크를 클릭하면 됩니다.", + "c505d6c5de63cc700f0aaf8a4b31fae9e18024e5": "'오디오 전용' 북마크 생성", + "47955e2cc6986625528b4352034858180d675281": "데이터베이스 위치:", + "9f8de81d44ec2a9a58b97e589b9e3154b3966c60": "테이블당 레코드", + "3913164a51898aac444bf6c7150e46ad5a8a18ad": "몽고DB 연결 문자열", + "5473e36f5102e2ae22ce4c6620cacc40cc98da95": "예시:", + "d54142de169844b014ae913a4056c31495f4a305": "연결 문자열 테스트", + "98e94c9bdac1ca8beb29d73b2e6f7a9e5e035aec": "DB 전환", + "b1c08387975e6feada407c9b5f5f564261b8192b": "데이터베이스 정보를 검색할 수 없습니다. 자세한 내용은 서버 로그를 확인하세요.", + "ec71e08aee647ea4a71fd6b7510c54d84a797ca6": "다운로더 선택", + "5fab47f146b0a4b809dcebf3db9da94df6299ea1": "기본 다운로드 에이전트 사용", + "c776eb4992b6c98f58cd89b20c1ea8ac37888521": "다운로드 에이전트 선택", + "0c43af932e6a4ee85500e28f01b3538b4eb27bc4": "로그 레벨", + "db6c192032f4cab809aad35215f0aa4765761897": "로그인 만료", + "dc3d990391c944d1fbfc7cfb402f7b5e112fb3a8": "고급 다운로드 허용", + "431e5f3a0dde88768d1074baedd65266412b3f02": "쿠키 사용", + "80651a7ad1229ea6613557d3559f702cfa5aecf5": "쿠키 설정", + "635285fa5624d50a408feb7eb564c0db0d3f1ce1": "서버 재시작", + "37224420db54d4bc7696f157b779a7225f03ca9d": "사용자 등록 허용", + "fa548cee6ea11c160a416cac3e6bdec0363883dc": "인증 방법", + "4f56ced9d6b85aeb1d4346433361d47ea72dac1a": "내부", + "e3d7c5f019e79a3235a28ba24df24f11712c7627": "LDAP", + "1db9789b93069861019bd0ccaa5d4706b00afc61": "LDAP URL", + "f50fa6c09c8944aed504f6325f2913ee6c7a296a": "Bind DN", + "080cc6abcba236390fc22e79792d0d3443a3bd2a": "Bind Credentials", + "cfa67d14d84fe0e9fadf251dc51ffc181173b662": "기본 검색", + "e01d54ecc1a0fcf9525a3c100ed8b83d94e61c23": "검색 필터", + "cec82c0a545f37420d55a9b6c45c20546e82f94e": "YoutubeDL-Material에 대하여", + "199c17e5d6a419313af3c325f06dcbb9645ca618": "은(는) 구글의 Material 디자인 요건에 따라 만들어진 오픈소스 유튜브 다운로더 입니다. 당신은 당신이 좋아하는 동영상을 동영상이나 오디오 파일로 원활하게 받을 수 있으며, 심지어 당신이 좋아하는 채널이나 재생목록을 구독해 그들의 새로운 동영상을 지속적으로 업데이트 할 수도 있습니다.", + "bc0ad0ee6630acb7fcb7802ec79f5a0ee943c1a7": "은(는) 광범위한 API, 도커 지원, 현지화 (번역) 지원을 포함한 몇몇 엄청난 기능이 포함되어 있습니다! 아래 깃허브 아이콘을 클릭해 모든 지원되는 기능을 확인해보세요.", + "a45e3b05f0529dc5246d70ef62304c94426d4c81": "설치된 버전:", + "b33536f59b94ec935a16bd6869d836895dc5300c": "버그를 찾았거나 제안하실 사항이 있으신가요?", + "e1f398f38ff1534303d4bb80bd6cece245f24016": "이슈를 생성하기 위해!", + "e22f3a5351944f3a1a10cfc7da6f65dfbe0037fe": "업데이트 확인중...", + "a16e92385b4fd9677bb830a4b796b8b79c113290": "업데이트 가능", + "189b28aaa19b3c51c6111ad039c4fd5e2a22e370": "설정 메뉴에서 업데이트를 할 수 있습니다.", + "1372e61c5bd06100844bd43b98b016aabc468f62": "선택된 버전:", + "1f6d14a780a37a97899dc611881e6bc971268285": "공유 허용", + "6580b6a950d952df847cb3d8e7176720a740adc8": "타임스탬프 사용", + "4f2ed9e71a7c981db3e50ae2fedb28aff2ec4e6c": "초", + "3a6e5a6aa78ca864f6542410c5dafb6334538106": "클립보드에 복사", + "a249a5ae13e0835383885aaf697d2890cc3e53e9": "재생목록 공유", + "94e2674467c7a08a291f9bd97ce694d4e47ffd62": "파일 공유", + "a1ad8b1be9be43b5183bd2c3186d4e19496f2a0b": "세션 아이디:", + "b6c453e0e61faea184bbaf5c5b0a1e164f4de2a2": "모든 다운로드된 항목 지우기", + "eb98135e35af26a9a326ee69bd8ff104d36dd8ec": "(현재)", + "7117fc42f860e86d983bfccfcf2654e5750f3406": "다운로드된 항목 없음!", + "42ff677ec14f111e88bd6cdd30145378e994d1bf": "프로필", + "bb694b49d408265c91c62799c2b3a7e3151c824d": "로그아웃", + "ac9d09de42edca1296371e4d801349c9096ac8de": "UID:", + "a5ed099ffc9e96f6970df843289ade8a7d20ab9f": "생성됨:", + "fa96f2137af0a24e6d6d54c598c0af7d5d5ad344": "로그인하지 않았습니다.", + "a1dbca87b9f36d2b06a5cbcffb5814c4ae9b798a": "관리자 계정 생성", + "2d2adf3ca26a676bca2269295b7455a26fd26980": "기본 관리자 계정이 감지되지 않았습니다. 이것은 'admin'이라는 ID를 가진 관리자 계정을 만들고, 비밀번호를 설정할 것입니다.", + "70a67e04629f6d412db0a12d51820b480788d795": "생성", + "4d92a0395dd66778a931460118626c5794a3fc7a": "사용자 추가", + "b0d7dd8a1b0349622d6e0c6e643e24a9ea0efa1d": "역할 수정", + "746f64ddd9001ac456327cd9a3d5152203a4b93c": "ID", + "52c1447c1ec9570a2a3025c7e566557b8d19ed92": "역할", + "59a8c38db3091a63ac1cb9590188dc3a972acfb3": "액션", + "2bd201aea09e43fbfd3cd15ec0499b6755302329": "사용자 관리", + "95b95a9c79e4fd9ed41f6855e37b3b06af25bcab": "사용자 삭제", + "632e8b20c98e8eec4059a605a4b011bb476137af": "사용자 수정", + "29c97c8e76763bb15b6d515648fa5bd1eb0f7510": "사용자 UID:", + "e70e209561583f360b1e9cefd2cbb1fe434b6229": "새 비밀번호", + "6498fa1b8f563988f769654a75411bb8060134b9": "새 비밀번호 설정", + "544e09cdc99a8978f48521d45f62db0da6dcf742": "기본 역할 사용", + "4f20f2d5a6882190892e58b85f6ccbedfa737952": "네", + "3d3ae7deebc5949b0c1c78b9847886a94321d9fd": "아니오", + "57c6c05d8ebf4ef1180c2705033c044f655bb2c4": "역할 관리", + "5009630cdf32ab4f1c78737b9617b8773512c05a": "줄:", + "8a0bda4c47f10b2423ff183acefbf70d4ab52ea2": "로그 지우기", + "24dc3ecf7ec2c2144910c4f3d38343828be03a4c": "자동으로 생성됨", + "ccf5ea825526ac490974336cb5c24352886abc07": "파일 열기", + "5656a06f17c24b2d7eae9c221567b209743829a9": "새 탭에서 파일 열기", + "a0720c36ee1057e5c54a86591b722485c62d7b1a": "구독중으로 가기", + "94e01842dcee90531caa52e4147f70679bac87fe": "삭제하고 재다운로드", + "2031adb51e07a41844e8ba7704b054e98345c9c1": "영원히 삭제", + "ddc31f2885b1b33a7651963254b0c197f2a64086": "더 보기.", + "56a2a773fbd5a6b9ac2e6b89d29d70a2ed0f3227": "간략히 보기.", + "2054791b822475aeaea95c0119113de3200f5e1c": "길이:" +} \ No newline at end of file diff --git a/src/assets/i18n/messages.ko.xlf b/src/assets/i18n/messages.ko.xlf new file mode 100644 index 0000000..3746351 --- /dev/null +++ b/src/assets/i18n/messages.ko.xlf @@ -0,0 +1,2634 @@ + + + + + + About + 대하여 + + src/app/app.component.html + 32 + + About menu label + + + Profile + 프로필 + + src/app/app.component.html + 19 + + Profile menu label + + + Dark + 다크 + + src/app/app.component.html + 23 + + + src/app/settings/settings.component.html + 75 + + Dark mode toggle label + + + Settings + 설정 + + src/app/app.component.html + 28 + + + src/app/settings/settings.component.html + 1 + + Settings menu label + + + Home + + + src/app/app.component.html + 43 + + Navigation menu Home Page title + + + Login + 로그인 + + src/app/app.component.html + 44 + + + src/app/components/login/login.component.html + 15 + + + src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html + 20 + + Navigation menu Login Page title + + + Subscriptions + 구독 + + src/app/app.component.html + 45 + + Navigation menu Subscriptions Page title + + + Downloads + 다운로드 + + src/app/app.component.html + 46 + + Navigation menu Downloads Page title + + + Only Audio + 오디오만 + + src/app/main/main.component.html + 65,66 + + Only Audio checkbox + + + Download + 다운로드 + + src/app/main/main.component.html + 79,80 + + Main download button + + + Quality + 품질 + + src/app/main/main.component.html + 19,20 + + Quality select label + + + Use URL + URL 이용 + + src/app/main/main.component.html + 51 + + YT search Use URL button for searched video + + + View + 보기 + + src/app/main/main.component.html + 55,56 + + YT search View button for searched video + + + Multi-download Mode + 복수 다운로드 모드 + + src/app/main/main.component.html + 70,71 + + Multi-download Mode checkbox + + + Cancel + 취소 + + src/app/main/main.component.html + 84,85 + + Cancel download button + + + Advanced + 고급 + + src/app/main/main.component.html + 96,97 + + Advanced download mode panel + + + Use custom args + 사용자 지정 인수 이용 + + src/app/main/main.component.html + 110,111 + + Use custom args checkbox + + + Custom args + 사용자 지정 인수 + + src/app/main/main.component.html + 115 + + + src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html + 57 + + + src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html + 44 + + Custom args placeholder + + + No need to include URL, just everything after. Args are delimited using two commas like so: ,, + URL을 포함할 필요가 없습니다. 이후의 모든 항목만 포함하면 됩니다. 인수는 다음과 같은 두 개의 쉼표를 사용하여 구분됩니다. : ,, + + src/app/main/main.component.html + 118,119 + + Custom Args input hint + + + Use custom output + 사용자 지정 출력 사용 + + src/app/main/main.component.html + 126,127 + + Use custom output checkbox + + + Custom output + 사용자 지정 출력 + + src/app/main/main.component.html + 130 + + Custom output placeholder + + + Documentation + 문서 + + src/app/main/main.component.html + 132 + + + src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html + 69 + + + src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html + 56 + + + src/app/dialogs/edit-category-dialog/edit-category-dialog.component.html + 47 + + + src/app/settings/settings.component.html + 125 + + Youtube-dl output template documentation link + + + Path is relative to the config download path. Don't include extension. + 경로는 설정된 다운로드 경로에 상대적입니다. 확장자는 포함하지 마세요. + + src/app/main/main.component.html + 133 + + + src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html + 70 + + + src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html + 57 + + + src/app/dialogs/edit-category-dialog/edit-category-dialog.component.html + 48 + + Custom Output input hint + + + Crop file + 파일 자르기 + + src/app/main/main.component.html + 155,156 + + Crop video checkbox + + + Crop from (seconds) + 자르기 시작지점 (초) + + src/app/main/main.component.html + 159 + + Crop from placeholder + + + Crop to (seconds) + 자르기 마무리지점 (초) + + src/app/main/main.component.html + 164 + + Crop to placeholder + + + Simulated command: + 시뮬레이션된 명령: + + src/app/main/main.component.html + 102,103 + + Simulated command label + + + Use authentication + 인증 사용 + + src/app/main/main.component.html + 140,141 + + Use authentication checkbox + + + Username + 아이디 + + src/app/main/main.component.html + 144 + + YT Username placeholder + + + Password + 비밀번호 + + src/app/main/main.component.html + 149 + + + src/app/dialogs/add-user-dialog/add-user-dialog.component.html + 11 + + + src/app/dialogs/set-default-admin-dialog/set-default-admin-dialog.component.html + 10 + + YT Password placeholder + + + Create a playlist + 재생목록 만들기 + + src/app/create-playlist/create-playlist.component.html + 1 + + Create a playlist dialog title + + + Name + 제목 + + src/app/create-playlist/create-playlist.component.html + 6 + + + src/app/dialogs/modify-playlist/modify-playlist.component.html + 8 + + + src/app/dialogs/edit-category-dialog/edit-category-dialog.component.html + 5 + + Playlist name placeholder + + + Type + 종류 + + src/app/create-playlist/create-playlist.component.html + 11 + + Type select + + + Audio + 오디오 + + src/app/create-playlist/create-playlist.component.html + 12 + + Audio + + + Video + 동영상 + + src/app/create-playlist/create-playlist.component.html + 13 + + Video + + + Audio files + 오디오 파일 + + src/app/create-playlist/create-playlist.component.html + 19 + + Audio files title + + + Videos + 동영상 + + src/app/create-playlist/create-playlist.component.html + 20 + + + src/app/subscription/subscription/subscription.component.html + 29 + + Videos title + + + Subscribe to playlist or channel + 재생목록이나 채널 구독 + + src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html + 1 + + Subscribe dialog title + + + URL + URL + + src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html + 8 + + + src/app/settings/settings.component.html + 18 + + Subscription URL input placeholder + + + The playlist or channel URL + 재생목록이나 채널 URL + + src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html + 9 + + Subscription URL input hint + + + Custom name + 사용자 지정 이름 + + src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html + 19 + + Subscription custom name placeholder + + + Download all uploads + 모든 업로드 된 파일 다운로드 + + src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html + 23 + + + src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html + 10 + + Download all uploads subscription setting + + + Max quality + 최고 화질 + + src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html + 40 + + + src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html + 32 + + Max quality placeholder + + + Audio-only mode + 오디오 전용 모드 + + src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html + 47 + + + src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html + 27 + + Streaming-only mode + + + Streaming-only mode + 스트리밍 전용 모드 + + src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html + 52 + + + src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html + 39 + + Streaming-only mode + + + These are added after the standard args. + 이것들은 일반적인 인수 뒤에 추가됩니다. + + src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html + 60 + + + src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html + 47 + + Custom args hint + + + Custom file output + 사용자 지정 파일 출력 + + src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html + 66 + + + src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html + 53 + + + src/app/dialogs/edit-category-dialog/edit-category-dialog.component.html + 44 + + Subscription custom file output placeholder + + + Cancel + 취소 + + src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html + 79 + + + src/app/dialogs/arg-modifier-dialog/arg-modifier-dialog.component.html + 84 + + + src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html + 66 + + + src/app/dialogs/edit-category-dialog/edit-category-dialog.component.html + 54 + + + src/app/components/modify-users/modify-users.component.html + 61 + + Subscribe cancel button + + + Subscribe + 구독 + + src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html + 81 + + Subscribe button + + + Download videos uploaded in the last + 마지막으로 업로드된 동영상 다운로드 + + src/app/dialogs/subscribe-dialog/subscribe-dialog.component.html + 26 + + + src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html + 13 + + Download time range prefix + + + Type: + 종류: + + src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.html + 5 + + Subscription type property + + + URL: + URL: + + src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.html + 9 + + + src/app/dialogs/video-info-dialog/video-info-dialog.component.html + 9 + + Subscription URL property + + + ID: + 아이디: + + src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.html + 13 + + + src/app/file-card/file-card.component.html + 7 + + + src/app/download-item/download-item.component.html + 4 + + Subscription ID property + + + Close + 닫기 + + src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.html + 23 + + + src/app/dialogs/video-info-dialog/video-info-dialog.component.html + 35 + + + src/app/dialogs/update-progress-dialog/update-progress-dialog.component.html + 17 + + + src/app/dialogs/add-user-dialog/add-user-dialog.component.html + 18 + + + src/app/dialogs/cookies-uploader-dialog/cookies-uploader-dialog.component.html + 40 + + + src/app/dialogs/about-dialog/about-dialog.component.html + 59 + + + src/app/dialogs/share-media-dialog/share-media-dialog.component.html + 29 + + + src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html + 27 + + + src/app/components/manage-user/manage-user.component.html + 30 + + + src/app/components/manage-role/manage-role.component.html + 18 + + Close subscription info button + + + Export Archive + 아카이브 내보내기 + + src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.html + 24 + + Export Archive button + + + Unsubscribe + 구독 취소 + + src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.html + 26 + + Unsubscribe button + + + (Paused) + (일시정지) + + src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.html + 1 + + + src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html + 1 + + + src/app/subscriptions/subscriptions.component.html + 12 + + + src/app/subscriptions/subscriptions.component.html + 31 + + + src/app/subscription/subscription/subscription.component.html + 5 + + Paused suffix + + + Archive: + 아카이브: + + src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.html + 17 + + Subscription ID property + + + Name: + 제목: + + src/app/dialogs/video-info-dialog/video-info-dialog.component.html + 5 + + + src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html + 6 + + Video name property + + + Uploader: + 업로더: + + src/app/dialogs/video-info-dialog/video-info-dialog.component.html + 13 + + Video ID property + + + File size: + 파일 크기: + + src/app/dialogs/video-info-dialog/video-info-dialog.component.html + 17 + + Video file size property + + + Path: + 경로: + + src/app/dialogs/video-info-dialog/video-info-dialog.component.html + 21 + + Video path property + + + Upload Date: + 업로드 날짜: + + src/app/dialogs/video-info-dialog/video-info-dialog.component.html + 25 + + Video upload date property + + + Category: + 카테고리: + + src/app/dialogs/video-info-dialog/video-info-dialog.component.html + 29 + + Category property + + + Modify youtube-dl args + Youtube-dl 인수 수정 + + src/app/dialogs/arg-modifier-dialog/arg-modifier-dialog.component.html + 1 + + Modify args title + + + Simulated new args + 시뮬레이션된 새 인수 + + src/app/dialogs/arg-modifier-dialog/arg-modifier-dialog.component.html + 8 + + Simulated args title + + + Add an arg + 인수 추가 + + src/app/dialogs/arg-modifier-dialog/arg-modifier-dialog.component.html + 34 + + Add arg card title + + + Search by category + 카테고리로 찾기 + + src/app/dialogs/arg-modifier-dialog/arg-modifier-dialog.component.html + 60 + + Search args by category button + + + Use arg value + 인수 값 이용 + + src/app/dialogs/arg-modifier-dialog/arg-modifier-dialog.component.html + 64 + + Use arg value checkbox + + + Add arg + 인수 추가 + + src/app/dialogs/arg-modifier-dialog/arg-modifier-dialog.component.html + 73 + + Search args by category button + + + Modify + 수정 + + src/app/dialogs/arg-modifier-dialog/arg-modifier-dialog.component.html + 85 + + Arg modifier modify button + + + Arg value + 인수 값 + + src/app/dialogs/arg-modifier-dialog/arg-modifier-dialog.component.html + 68 + + Arg value placeholder + + + Updater + 업데이터 + + src/app/dialogs/update-progress-dialog/update-progress-dialog.component.html + 1 + + Update progress dialog title + + + Register a user + 사용자 등록 + + src/app/dialogs/add-user-dialog/add-user-dialog.component.html + 1 + + Register user dialog title + + + User name + 아이디 + + src/app/dialogs/add-user-dialog/add-user-dialog.component.html + 6 + + User name placeholder + + + Register + 등록 + + src/app/dialogs/add-user-dialog/add-user-dialog.component.html + 17 + + + src/app/components/login/login.component.html + 35 + + Register user button + + + Upload new cookies + 새 쿠키 업로드 + + src/app/dialogs/cookies-uploader-dialog/cookies-uploader-dialog.component.html + 1 + + Cookies uploader dialog title + + + NOTE: Uploading new cookies will override your previous cookies. Also note that cookies are instance-wide, not per-user. + 참고: 새로운 쿠키를 추가하면 이전 쿠키를 덮어씁니다. 또한 쿠키는 사용자 개인이 아닌 전체에 적용됩니다. + + src/app/dialogs/cookies-uploader-dialog/cookies-uploader-dialog.component.html + 20 + + Cookies upload warning + + + Drag and Drop + 드래그 앤 드롭 + + src/app/dialogs/cookies-uploader-dialog/cookies-uploader-dialog.component.html + 11 + + Drag and Drop + + + Modify playlist + 재생목록 수정 + + src/app/dialogs/modify-playlist/modify-playlist.component.html + 1 + + Modify playlist dialog title + + + Save + 저장 + + src/app/dialogs/modify-playlist/modify-playlist.component.html + 43 + + + src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html + 68 + + + src/app/dialogs/edit-category-dialog/edit-category-dialog.component.html + 56 + + + src/app/settings/settings.component.html + 464 + + + src/app/components/modify-users/modify-users.component.html + 58 + + Save + + + Randomize order when playing + 재생할 때 재생목록 섞기 + + src/app/dialogs/modify-playlist/modify-playlist.component.html + 13 + + Randomize order when playing checkbox label + + + Add content + 콘텐츠 추가 + + src/app/dialogs/modify-playlist/modify-playlist.component.html + 24 + + Add content + + + Normal order + 기본 순서 + + src/app/dialogs/modify-playlist/modify-playlist.component.html + 18 + + Normal order + + + Reverse order + 순서 거꾸로 + + src/app/dialogs/modify-playlist/modify-playlist.component.html + 19 + + Reverse order + + + My videos + 내 동영상 + + src/app/components/recent-videos/recent-videos.component.html + 20 + + My videos title + + + Search + 검색 + + src/app/components/recent-videos/recent-videos.component.html + 24 + + + src/app/components/modify-users/modify-users.component.html + 7 + + + src/app/subscription/subscription/subscription.component.html + 33 + + Files search placeholder + + + No videos found. + 동영상 없음. + + src/app/components/recent-videos/recent-videos.component.html + 38 + + No videos found + + + Editing + 수정중 + + src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html + 1 + + Edit subscription dialog title prefix + + + Paused + 일시정지됨 + + src/app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html + 7 + + Paused subscription setting + + + Editing category + 카테고리 수정중 + + src/app/dialogs/edit-category-dialog/edit-category-dialog.component.html + 1 + + Editing category dialog title + + + Rules + 규칙 + + src/app/dialogs/edit-category-dialog/edit-category-dialog.component.html + 10 + + Rules + + + Add new rule + 새로운 규칙 추가 + + src/app/dialogs/edit-category-dialog/edit-category-dialog.component.html + 39 + + Add new rule tooltip + + + Download Twitch Chat + 트위치 채팅 다운로드 + + src/app/components/twitch-chat/twitch-chat.component.html + 10 + + Download Twitch Chat button + + + Edit + 수정 + + src/app/file-card/file-card.component.html + 19 + + + src/app/components/unified-file-card/unified-file-card.component.html + 37 + + Playlist edit button + + + Delete + 삭제 + + src/app/file-card/file-card.component.html + 20 + + + src/app/file-card/file-card.component.html + 25 + + + src/app/components/unified-file-card/unified-file-card.component.html + 33 + + + src/app/components/unified-file-card/unified-file-card.component.html + 39 + + Delete playlist + + + Info + 정보 + + src/app/file-card/file-card.component.html + 24 + + + src/app/components/unified-file-card/unified-file-card.component.html + 24 + + + src/app/subscription/subscription-file-card/subscription-file-card.component.html + 7 + + Video info button + + + Count: + 동영상 수: + + src/app/file-card/file-card.component.html + 8 + + Playlist video count + + + Delete and blacklist + 삭제하고 블랙리스트 추가 + + src/app/file-card/file-card.component.html + 26 + + + src/app/components/unified-file-card/unified-file-card.component.html + 34 + + Delete and blacklist video button + + + views + 조회수 + + src/app/player/player.component.html + 15 + + View count label + + + The download was successful + 성공적으로 다운로드 완료 + + src/app/download-item/download-item.component.html + 8 + + + src/app/download-item/download-item.component.html + 8 + + download successful tooltip + + + An error has occurred + 에러 발생 + + src/app/download-item/download-item.component.html + 9 + + + src/app/download-item/download-item.component.html + 9 + + download error tooltip + + + Details + 세부사항 + + src/app/download-item/download-item.component.html + 18 + + Details + + + An error has occurred: + 에러 발생: + + src/app/download-item/download-item.component.html + 27 + + Error label + + + Download start: + 다운로드 시작: + + src/app/download-item/download-item.component.html + 32 + + Download start label + + + Download end: + 다운로드 끝: + + src/app/download-item/download-item.component.html + 35 + + Download end label + + + File path(s): + 파일 경로(들): + + src/app/download-item/download-item.component.html + 38 + + File path(s) label + + + Your subscriptions + 구독중 + + src/app/subscriptions/subscriptions.component.html + 3 + + Subscriptions title + + + Channels + 채널 + + src/app/subscriptions/subscriptions.component.html + 8 + + Subscriptions channels title + + + Playlists + 재생목록 + + src/app/subscriptions/subscriptions.component.html + 27 + + Subscriptions playlists title + + + Name not available. Channel retrieval in progress. + 이름이 유효하지 않음. 채널 검색중. + + src/app/subscriptions/subscriptions.component.html + 14 + + Subscription playlist not available text + + + You have no channel subscriptions. + 구독중인 채널이 없습니다. + + src/app/subscriptions/subscriptions.component.html + 24 + + No channel subscriptions text + + + Name not available. Playlist retrieval in progress. + 이름이 유효하지 않음. 플레이리스트 검색중. + + src/app/subscriptions/subscriptions.component.html + 33 + + Subscription playlist not available text + + + You have no playlist subscriptions. + 구독중인 재생목록이 없습니다. + + src/app/subscriptions/subscriptions.component.html + 43 + + No playlist subscriptions text + + + Main + 메인 + + src/app/settings/settings.component.html + 12 + + Main settings label + + + Downloader + 다운로더 + + src/app/settings/settings.component.html + 102 + + Downloader settings label + + + Extra + 추가 + + src/app/settings/settings.component.html + 185 + + Extra settings label + + + Database + 데이터베이스 + + src/app/settings/settings.component.html + 284 + + Database settings label + + + Advanced + 고급 + + src/app/settings/settings.component.html + 320 + + Host settings label + + + Users + 사용자 + + src/app/settings/settings.component.html + 403 + + + src/app/settings/settings.component.html + 403 + + Users settings label + + + Logs + 로그 + + src/app/settings/settings.component.html + 451 + + + src/app/settings/settings.component.html + 451 + + Logs settings label + + + {VAR_SELECT, select, true {Close} false {Cancel} other {otha}} + {VAR_SELECT, select, true {닫다} false {취소} other {기타}} + + src/app/settings/settings.component.html + 467 + + Settings cancel and close button + + + URL this app will be accessed from, without the port. + 포트를 제외한 이 앱에 접속할 URL. + + src/app/settings/settings.component.html + 19 + + URL setting input hint + + + Port + 포트 + + src/app/settings/settings.component.html + 24 + + Port input placeholder + + + The desired port. Default is 17442. + 포트 설정. 기본 포트는 17442 입니다. + + src/app/settings/settings.component.html + 25 + + Port setting input hint + + + Multi-user mode + 복수 사용자 모드 + + src/app/settings/settings.component.html + 34 + + Multi user mode setting + + + Users base path + 사용자 기본 경로 + + src/app/settings/settings.component.html + 38 + + Users base path placeholder + + + Base path for users and their downloaded videos. + 사용자와 그들의 동영상 다운로드를 위한 기본 경로. + + src/app/settings/settings.component.html + 39 + + Users base path hint + + + Allow subscriptions + 구독 허용 + + src/app/settings/settings.component.html + 48 + + Allow subscriptions setting + + + Subscriptions base path + 구독 기본 경로 + + src/app/settings/settings.component.html + 52 + + Subscriptions base path input setting placeholder + + + Base path for videos from your subscribed channels and playlists. It is relative to YTDL-Material's root folder. + 구독된 채널과 재생목록에서 나온 영상들을 위한 기본 경로. 경로는 YTDL-Material 루트 폴더 경로에 상대적입니다. + + src/app/settings/settings.component.html + 53 + + Subscriptions base path setting input hint + + + Check interval + 확인 간격 + + src/app/settings/settings.component.html + 58 + + Check interval input setting placeholder + + + Unit is seconds, only include numbers. + 단위는 초이며, 숫자만 넣으세요. + + src/app/settings/settings.component.html + 59 + + Check interval setting input hint + + + Sometimes new videos are downloaded before being fully processed. This setting will mean new videos will be checked for a higher quality version the following day. + 가끔 새 동영상이 최고 화질 처리 전에 다운로드 될 때가 있습니다. 이 설정은 새 동영상이 더 높은 화질의 버전이 있는지 다음 날짜에 확인됨을 의미합니다. + + src/app/settings/settings.component.html + 63 + + Redownload fresh uploads tooltip + + + Redownload fresh uploads + 높은 화질 재다운로드 + + src/app/settings/settings.component.html + 63 + + Redownload fresh uploads + + + Theme + 테마 + + src/app/settings/settings.component.html + 72 + + Theme select label + + + Default + 기본 + + src/app/settings/settings.component.html + 74 + + Default theme label + + + Allow theme change + 테마 변경 허용 + + src/app/settings/settings.component.html + 80 + + Allow theme change setting + + + Language + 언어 + + src/app/settings/settings.component.html + 89 + + Language select label + + + Audio folder path + 오디오 폴더 경로 + + src/app/settings/settings.component.html + 109 + + Audio folder path input placeholder + + + Path for audio only downloads. It is relative to YTDL-Material's root folder. + 오디오 전용 다운로드 경로. 경로는 YTDL-Material 루트 폴더 경로에 상대적입니다. + + src/app/settings/settings.component.html + 110 + + Aduio path setting input hint + + + Video folder path + 동영상 폴더 경로 + + src/app/settings/settings.component.html + 116 + + Video folder path input placeholder + + + Path for video downloads. It is relative to YTDL-Material's root folder. + 동영상 다운로드 경로. 경로는 YTDL-Material 루트 폴더 경로에 상대적입니다. + + src/app/settings/settings.component.html + 117 + + Video path setting input hint + + + Default file output + 기본 파일 출력 + + src/app/settings/settings.component.html + 123 + + Default file output placeholder + + + Path is relative to the above download paths. Don't include extension. + 경로는 위의 다운로드 경로에 상대적입니다. 확장자는 포함하지 마세요. + + src/app/settings/settings.component.html + 126 + + Custom Output input hint + + + Global custom args + 전반적으로 적용될 사용자 지정 인수 + + src/app/settings/settings.component.html + 133 + + Custom args input placeholder + + + Global custom args for downloads on the home page. Args are delimited using two commas like so: ,, + 홈페이지에서의 다운로드에 대해 전반적으로 적용될 사용자 지정 인수. 인수는 다음과 같은 두 개의 쉼표를 사용하여 구분됩니다. : ,, + + src/app/settings/settings.component.html + 134 + + Custom args setting input hint + + + Categories + 카테고리 + + src/app/settings/settings.component.html + 144 + + Categories + + + With this setting enabled, if a single video matches a category, the entire playlist will receive that category. + 이 설정을 사용하면, 하나의 동영상이 카테고리와 일치할 경우, 전체 재생목록에 해당 카테고리가 표시됩니다. + + src/app/settings/settings.component.html + 158 + + Allow playlist categorization setting tooltip + + + Allow playlist categorization + 재생목록 카테고리화 허용 + + src/app/settings/settings.component.html + 158 + + Allow playlist categorization setting label + + + Use youtube-dl archive + Youtube-dl 아카이브 사용 + + src/app/settings/settings.component.html + 166 + + Use youtubedl archive setting + + + Include thumbnail + 썸네일 포함 + + src/app/settings/settings.component.html + 170 + + Include thumbnail setting + + + Include metadata + 메타데이터 포함 + + src/app/settings/settings.component.html + 174 + + Include metadata setting + + + Kill all downloads + 모든 다운로드 종료 + + src/app/settings/settings.component.html + 178 + + Kill all downloads button + + + Top title + 상위 제목 + + src/app/settings/settings.component.html + 191 + + Top title input placeholder + + + File manager enabled + 파일 매니저 설정됨 + + src/app/settings/settings.component.html + 196 + + File manager enabled setting + + + Downloads manager enabled + 다운로드 매니저 설정됨 + + src/app/settings/settings.component.html + 199 + + Downloads manager enabled setting + + + Allow quality select + 화질 선택 허용 + + src/app/settings/settings.component.html + 202 + + Allow quality seelct setting + + + Download only mode + 다운로드 전용 모드 + + src/app/settings/settings.component.html + 205 + + Download only mode setting + + + Allow multi-download mode + 복수 다운로드 모드 허용 + + src/app/settings/settings.component.html + 208 + + Allow multi-download mode setting + + + Enable Public API + 오픈 API 허용 + + src/app/settings/settings.component.html + 216 + + Enable Public API key setting + + + Public API Key + 오픈 API 키 + + src/app/settings/settings.component.html + 221 + + Public API Key setting placeholder + + + View documentation + 문서 보기 + + src/app/settings/settings.component.html + 222 + + View API docs setting hint + + + This will delete your old API key! + 이것은 예전 API키를 지울 것입니다! + + src/app/settings/settings.component.html + 226 + + delete api key tooltip + + + Generate + 생성 + + src/app/settings/settings.component.html + 226 + + Generate key button + + + Use YouTube API + 유튜브 API 사용 + + src/app/settings/settings.component.html + 235 + + Use YouTube API setting + + + Youtube API Key + 유튜브 API 키 + + src/app/settings/settings.component.html + 239 + + Youtube API Key setting placeholder + + + Generating a key is easy! + 키를 만드는 것은 쉽습니다! + + src/app/settings/settings.component.html + 240 + + + src/app/settings/settings.component.html + 252 + + Youtube API Key setting hint + + + Use Twitch API + 트위치 API 사용 + + src/app/settings/settings.component.html + 244 + + Use Twitch API setting + + + Twitch API Key + 트위치 API 키 + + src/app/settings/settings.component.html + 251 + + Twitch API Key setting placeholder + + + Also known as a Client ID. + 클라이언트 ID라고도 알려져 있음. + + src/app/settings/settings.component.html + 252 + + Twitch API Key setting hint AKA preamble + + + Auto-download Twitch Chat + 트위치 채팅 자동 다운로드 + + src/app/settings/settings.component.html + 247 + + Auto download Twitch Chat setting + + + Click here + 이곳을 누르세요 + + src/app/settings/settings.component.html + 262 + + + src/app/settings/settings.component.html + 268 + + + src/app/dialogs/about-dialog/about-dialog.component.html + 25 + + Chrome ext click here + + + to download the official YoutubeDL-Material Chrome extension manually. + 공식 YoutubeDL-Material 크롬 확장 프로그램을 수동으로 다운로드 하기 위해. + + src/app/settings/settings.component.html + 262 + + Chrome click here suffix + + + You must manually load the extension and modify the extension's settings to set the frontend URL. + 반드시 확장 프로그램을 수동으로 실행하고 확장 프로그램 설정을 수정하여 프론트엔드 URL을 설정해야 합니다. + + src/app/settings/settings.component.html + 263 + + Chrome setup suffix + + + to install the official YoutubeDL-Material Firefox extension right off the Firefox extensions page. + 파이어폭스 확장 프로그램 페이지에서 바로 공식 YoutubeDL-Material 파이어폭스 확장 프로그램을 설치하기 위해. + + src/app/settings/settings.component.html + 268 + + Firefox click here suffix + + + Detailed setup instructions. + 자세한 설정 지침. + + src/app/settings/settings.component.html + 269 + + Firefox setup prefix link + + + Not much is required other than changing the extension's settings to set the frontend URL. + 프론트엔드 URL을 설정하기 위해 확장 프로그램 설정을 변경하는 것 외에는 필요한 것이 많지 않습니다. + + src/app/settings/settings.component.html + 269 + + Firefox setup suffix + + + Drag the link below to your bookmarks, and you're good to go! Just navigate to the YouTube video you'd like to download, and click the bookmark. + 아래 링크를 북마크에 끌어다 놓으시면 됩니다! 이제 그냥 다운로드하고자 하는 유튜브 비디오 페이지에서 북마크를 클릭하면 됩니다. + + src/app/settings/settings.component.html + 274 + + Bookmarklet instructions + + + Generate 'audio only' bookmarklet + '오디오 전용' 북마크 생성 + + src/app/settings/settings.component.html + 275 + + Generate audio only bookmarklet checkbox + + + Database location: + 데이터베이스 위치: + + src/app/settings/settings.component.html + 290 + + Database location label + + + Records per table + 테이블당 레코드 + + src/app/settings/settings.component.html + 291 + + Records per table label + + + MongoDB Connection String + 몽고DB 연결 문자열 + + src/app/settings/settings.component.html + 299 + + MongoDB Connection String + + + Example: + 예시: + + src/app/settings/settings.component.html + 300 + + MongoDB Connection String setting hint AKA preamble + + + Test connection string + 연결 문자열 테스트 + + src/app/settings/settings.component.html + 304 + + Test connection string button + + + Transfer DB to + DB 전환 + + src/app/settings/settings.component.html + 308 + + Transfer DB button + + + Database information could not be retrieved. Check the server logs for more information. + 데이터베이스 정보를 검색할 수 없습니다. 자세한 내용은 서버 로그를 확인하세요. + + src/app/settings/settings.component.html + 312 + + Database info not retrieved error message + + + Select a downloader + 다운로더 선택 + + src/app/settings/settings.component.html + 326 + + Default downloader select label + + + Use default downloading agent + 기본 다운로드 에이전트 사용 + + src/app/settings/settings.component.html + 335 + + Use default downloading agent setting + + + Select a download agent + 다운로드 에이전트 선택 + + src/app/settings/settings.component.html + 339 + + Custom downloader select label + + + Log Level + 로그 레벨 + + src/app/settings/settings.component.html + 353 + + Log Level label + + + Login expiration + 로그인 만료 + + src/app/settings/settings.component.html + 365 + + Login expiration select label + + + Allow advanced download + 고급 다운로드 허용 + + src/app/settings/settings.component.html + 376 + + Allow advanced downloading setting + + + Use Cookies + 쿠키 사용 + + src/app/settings/settings.component.html + 384 + + Use cookies setting + + + Set Cookies + 쿠키 설정 + + src/app/settings/settings.component.html + 385 + + Set cookies button + + + Restart server + 서버 재시작 + + src/app/settings/settings.component.html + 397 + + Restart server button + + + Allow user registration + 사용자 등록 허용 + + src/app/settings/settings.component.html + 407 + + Allow registration setting + + + Auth method + 인증 방법 + + src/app/settings/settings.component.html + 411 + + Auth method select + + + Internal + 내부 + + src/app/settings/settings.component.html + 413 + + Internal auth method + + + LDAP + LDAP + + src/app/settings/settings.component.html + 416 + + LDAP auth method + + + LDAP URL + LDAP URL + + src/app/settings/settings.component.html + 423 + + LDAP URL + + + Bind DN + Bind DN + + src/app/settings/settings.component.html + 428 + + Bind DN + + + Bind Credentials + Bind Credentials + + src/app/settings/settings.component.html + 433 + + Bind Credentials + + + Search Base + 기본 검색 + + src/app/settings/settings.component.html + 438 + + Search Base + + + Search Filter + 검색 필터 + + src/app/settings/settings.component.html + 443 + + Search Filter + + + About YoutubeDL-Material + YoutubeDL-Material에 대하여 + + src/app/dialogs/about-dialog/about-dialog.component.html + 1 + + About dialog title + + + is an open-source YouTube downloader built under Google's Material Design specifications. You can seamlessly download your favorite videos as video or audio files, and even subscribe to your favorite channels and playlists to keep updated with their new videos. + 은(는) 구글의 Material 디자인 요건에 따라 만들어진 오픈소스 유튜브 다운로더 입니다. 당신은 당신이 좋아하는 동영상을 동영상이나 오디오 파일로 원활하게 받을 수 있으며, 심지어 당신이 좋아하는 채널이나 재생목록을 구독해 그들의 새로운 동영상을 지속적으로 업데이트 할 수도 있습니다. + + src/app/dialogs/about-dialog/about-dialog.component.html + 12 + + About first paragraph + + + has some awesome features included! An extensive API, Docker support, and localization (translation) support. Read up on all the supported features by clicking on the GitHub icon above. + 은(는) 광범위한 API, 도커 지원, 현지화 (번역) 지원을 포함한 몇몇 엄청난 기능이 포함되어 있습니다! 아래 깃허브 아이콘을 클릭해 모든 지원되는 기능을 확인해보세요. + + src/app/dialogs/about-dialog/about-dialog.component.html + 15 + + About second paragraph + + + Installed version: + 설치된 버전: + + src/app/dialogs/about-dialog/about-dialog.component.html + 20 + + Version label + + + Found a bug or have a suggestion? + 버그를 찾았거나 제안하실 사항이 있으신가요? + + src/app/dialogs/about-dialog/about-dialog.component.html + 25 + + About bug prefix + + + to create an issue! + 이슈를 생성하기 위해! + + src/app/dialogs/about-dialog/about-dialog.component.html + 25 + + About bug suffix + + + Checking for updates... + 업데이트 확인중... + + src/app/dialogs/about-dialog/about-dialog.component.html + 20 + + Checking for updates text + + + Update available + 업데이트 가능 + + src/app/dialogs/about-dialog/about-dialog.component.html + 21 + + View latest update + + + You can update from the settings menu. + 설정 메뉴에서 업데이트를 할 수 있습니다. + + src/app/dialogs/about-dialog/about-dialog.component.html + 21 + + Update through settings menu hint + + + Select a version: + 선택된 버전: + + src/app/updater/updater.component.html + 3 + + Select a version + + + Enable sharing + 공유 허용 + + src/app/dialogs/share-media-dialog/share-media-dialog.component.html + 9 + + Enable sharing checkbox + + + Use timestamp + 타임스탬프 사용 + + src/app/dialogs/share-media-dialog/share-media-dialog.component.html + 12 + + Use timestamp + + + Seconds + + + src/app/dialogs/share-media-dialog/share-media-dialog.component.html + 14 + + Seconds + + + Copy to clipboard + 클립보드에 복사 + + src/app/dialogs/share-media-dialog/share-media-dialog.component.html + 23 + + Copy to clipboard button + + + Share playlist + 재생목록 공유 + + src/app/dialogs/share-media-dialog/share-media-dialog.component.html + 2 + + Share playlist dialog title + + + Share file + 파일 공유 + + src/app/dialogs/share-media-dialog/share-media-dialog.component.html + 3 + + Share video dialog title + + + Session ID: + 세션 아이디: + + src/app/components/downloads/downloads.component.html + 5 + + Session ID + + + Clear all downloads + 모든 다운로드된 항목 지우기 + + src/app/components/downloads/downloads.component.html + 18 + + clear all downloads action button + + + (current) + (현재) + + src/app/components/downloads/downloads.component.html + 6 + + Current session + + + No downloads available! + 다운로드된 항목 없음! + + src/app/components/downloads/downloads.component.html + 25 + + No downloads label + + + Your Profile + 프로필 + + src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html + 1 + + User profile dialog title + + + Logout + 로그아웃 + + src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html + 28 + + Logout + + + UID: + UID: + + src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html + 9 + + UID + + + Created: + 생성됨: + + src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html + 12 + + Created + + + You are not logged in. + 로그인하지 않았습니다. + + src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html + 19 + + Not logged in notification + + + Create admin account + 관리자 계정 생성 + + src/app/dialogs/set-default-admin-dialog/set-default-admin-dialog.component.html + 1 + + Create admin account dialog title + + + No default admin account detected. This will create and set the password for an admin account with the user name as 'admin'. + 기본 관리자 계정이 감지되지 않았습니다. 이것은 'admin'이라는 ID를 가진 관리자 계정을 만들고, 비밀번호를 설정할 것입니다. + + src/app/dialogs/set-default-admin-dialog/set-default-admin-dialog.component.html + 5 + + No default admin detected explanation + + + Create + 생성 + + src/app/dialogs/set-default-admin-dialog/set-default-admin-dialog.component.html + 17 + + Create + + + Add Users + 사용자 추가 + + src/app/components/modify-users/modify-users.component.html + 90 + + Add users button + + + Edit Role + 역할 수정 + + src/app/components/modify-users/modify-users.component.html + 95 + + Edit role + + + User name + ID + + src/app/components/modify-users/modify-users.component.html + 17 + + Username users table header + + + Role + 역할 + + src/app/components/modify-users/modify-users.component.html + 35 + + Role users table header + + + Actions + 액션 + + src/app/components/modify-users/modify-users.component.html + 55 + + Actions users table header + + + Manage user + 사용자 관리 + + src/app/components/modify-users/modify-users.component.html + 70 + + + src/app/components/manage-user/manage-user.component.html + 1 + + manage user action button tooltip + + + Delete user + 사용자 삭제 + + src/app/components/modify-users/modify-users.component.html + 73 + + delete user action button tooltip + + + Edit user + 사용자 수정 + + src/app/components/modify-users/modify-users.component.html + 66 + + edit user action button tooltip + + + User UID: + 사용자 UID: + + src/app/components/manage-user/manage-user.component.html + 4 + + User UID + + + New password + 새 비밀번호 + + src/app/components/manage-user/manage-user.component.html + 8 + + New password placeholder + + + Set new password + 새 비밀번호 설정 + + src/app/components/manage-user/manage-user.component.html + 10 + + Set new password + + + Use role default + 기본 역할 사용 + + src/app/components/manage-user/manage-user.component.html + 19 + + Use role default + + + Yes + + + src/app/components/manage-user/manage-user.component.html + 20 + + + src/app/components/manage-role/manage-role.component.html + 9 + + Yes + + + No + 아니오 + + src/app/components/manage-user/manage-user.component.html + 21 + + + src/app/components/manage-role/manage-role.component.html + 10 + + No + + + Manage role + 역할 관리 + + src/app/components/manage-role/manage-role.component.html + 1 + + Manage role dialog title + + + Lines: + 줄: + + src/app/components/logs-viewer/logs-viewer.component.html + 22 + + Label for lines select in logger view + + + Clear logs + 로그 지우기 + + src/app/components/logs-viewer/logs-viewer.component.html + 34 + + Clear logs button + + + Auto-generated + 자동으로 생성됨 + + src/app/components/unified-file-card/unified-file-card.component.html + 5 + + Auto-generated label + + + Open file + 파일 열기 + + src/app/components/unified-file-card/unified-file-card.component.html + 18 + + Open file button + + + Open file in new tab + 새 탭에서 파일 열기 + + src/app/components/unified-file-card/unified-file-card.component.html + 19 + + Open file in new tab + + + Go to subscription + 구독중으로 가기 + + src/app/components/unified-file-card/unified-file-card.component.html + 25 + + Go to subscription menu item + + + Delete and redownload + 삭제하고 재다운로드 + + src/app/components/unified-file-card/unified-file-card.component.html + 28 + + + src/app/subscription/subscription-file-card/subscription-file-card.component.html + 8 + + Delete and redownload subscription video button + + + Delete forever + 영원히 삭제 + + src/app/components/unified-file-card/unified-file-card.component.html + 31 + + + src/app/subscription/subscription-file-card/subscription-file-card.component.html + 9 + + Delete forever subscription video button + + + See more. + 더 보기. + + src/app/components/see-more/see-more.component.html + 5,6 + + See more + + + See less. + 간략히 보기. + + src/app/components/see-more/see-more.component.html + 8,9 + + See less + + + Length: + 길이: + + src/app/subscription/subscription-file-card/subscription-file-card.component.html + 3 + + Video duration label + + + +