Merge pull request #41 from arabcoders/dev

initial code to support adding tasks via WebGUI
This commit is contained in:
Abdulmohsen 2023-12-15 16:51:09 +03:00 committed by GitHub
commit 41d09d2753
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 611 additions and 220 deletions

View file

@ -12,8 +12,6 @@ on:
- info - info
- warning - warning
- debug - debug
schedule:
- cron: "0 0 * * *"
jobs: jobs:
delete-builds: delete-builds:

3
.vscode/launch.json vendored
View file

@ -33,7 +33,8 @@
"YTP_DOWNLOAD_PATH": "${workspaceFolder}/var/downloads", "YTP_DOWNLOAD_PATH": "${workspaceFolder}/var/downloads",
"YTP_TEMP_PATH": "${workspaceFolder}/var/tmp", "YTP_TEMP_PATH": "${workspaceFolder}/var/tmp",
"YTP_YTDL_OPTIONS_FILE": "${workspaceFolder}/var/config/ytdlp.json", "YTP_YTDL_OPTIONS_FILE": "${workspaceFolder}/var/config/ytdlp.json",
"YTP_URL_HOST": "http://localhost:8081" "YTP_URL_HOST": "http://localhost:8081",
"YTP_YTDL_DEBUG": "true",
} }
} }
] ]

View file

@ -29,7 +29,7 @@ ENV YTP_TEMP_PATH=/tmp
ENV YTP_DOWNLOAD_PATH=/downloads ENV YTP_DOWNLOAD_PATH=/downloads
RUN mkdir /config /downloads && ln -snf /usr/share/zoneinfo/${TZ} /etc/localtime && echo ${TZ} > /etc/timezone && \ RUN mkdir /config /downloads && ln -snf /usr/share/zoneinfo/${TZ} /etc/localtime && echo ${TZ} > /etc/timezone && \
apk add --update --no-cache bash ffmpeg aria2 coreutils curl shadow sqlite tzdata && \ apk add --update --no-cache bash ffmpeg mkvtoolnix patch aria2 coreutils curl shadow sqlite tzdata && \
useradd -u ${USER_ID:-1000} -U -d /app -s /bin/bash app && \ useradd -u ${USER_ID:-1000} -U -d /app -s /bin/bash app && \
rm -rf /var/cache/apk/* rm -rf /var/cache/apk/*

View file

@ -9,7 +9,6 @@ aiohttp = "*"
yt-dlp = "*" yt-dlp = "*"
caribou = "*" caribou = "*"
coloredlogs = "*" coloredlogs = "*"
ffprobe-python = "*"
aiocron = "*" aiocron = "*"
[dev-packages] [dev-packages]

332
Pipfile.lock generated
View file

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "9256047e3c0597d835039eadb3c589948874236a04f2392e5293ee7dd29084bc" "sha256": "fe1facc749dd399988b1a97d60121af298edf76c14b60ceff6aa398114b15030"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -361,80 +361,88 @@
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.0.1" "version": "==2.0.1"
}, },
"ffprobe-python": {
"hashes": [
"sha256:b02576228f6da0b0bb973e15e5dadb132013a4ded202dabb89ac77779e9634d6",
"sha256:b1d3e69e65e0814e28575f63a47b836a0b7cc8f1ffedd62d27152d9e4488214a"
],
"index": "pypi",
"version": "==1.0.3"
},
"frozenlist": { "frozenlist": {
"hashes": [ "hashes": [
"sha256:007df07a6e3eb3e33e9a1fe6a9db7af152bbd8a185f9aaa6ece10a3529e3e1c6", "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7",
"sha256:008eb8b31b3ea6896da16c38c1b136cb9fec9e249e77f6211d479db79a4eaf01", "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98",
"sha256:09163bdf0b2907454042edb19f887c6d33806adc71fbd54afc14908bfdc22251", "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad",
"sha256:0c7c1b47859ee2cac3846fde1c1dc0f15da6cec5a0e5c72d101e0f83dcb67ff9", "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5",
"sha256:0e5c8764c7829343d919cc2dfc587a8db01c4f70a4ebbc49abde5d4b158b007b", "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae",
"sha256:10ff5faaa22786315ef57097a279b833ecab1a0bfb07d604c9cbb1c4cdc2ed87", "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e",
"sha256:17ae5cd0f333f94f2e03aaf140bb762c64783935cc764ff9c82dff626089bebf", "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a",
"sha256:19488c57c12d4e8095a922f328df3f179c820c212940a498623ed39160bc3c2f", "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701",
"sha256:1a0848b52815006ea6596c395f87449f693dc419061cc21e970f139d466dc0a0", "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d",
"sha256:1e78fb68cf9c1a6aa4a9a12e960a5c9dfbdb89b3695197aa7064705662515de2", "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6",
"sha256:261b9f5d17cac914531331ff1b1d452125bf5daa05faf73b71d935485b0c510b", "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6",
"sha256:2b8bcf994563466db019fab287ff390fffbfdb4f905fc77bc1c1d604b1c689cc", "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106",
"sha256:38461d02d66de17455072c9ba981d35f1d2a73024bee7790ac2f9e361ef1cd0c", "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75",
"sha256:490132667476f6781b4c9458298b0c1cddf237488abd228b0b3650e5ecba7467", "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868",
"sha256:491e014f5c43656da08958808588cc6c016847b4360e327a62cb308c791bd2d9", "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a",
"sha256:515e1abc578dd3b275d6a5114030b1330ba044ffba03f94091842852f806f1c1", "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0",
"sha256:556de4430ce324c836789fa4560ca62d1591d2538b8ceb0b4f68fb7b2384a27a", "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1",
"sha256:5833593c25ac59ede40ed4de6d67eb42928cca97f26feea219f21d0ed0959b79", "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826",
"sha256:6221d84d463fb110bdd7619b69cb43878a11d51cbb9394ae3105d082d5199167", "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec",
"sha256:6918d49b1f90821e93069682c06ffde41829c346c66b721e65a5c62b4bab0300", "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6",
"sha256:6c38721585f285203e4b4132a352eb3daa19121a035f3182e08e437cface44bf", "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950",
"sha256:71932b597f9895f011f47f17d6428252fc728ba2ae6024e13c3398a087c2cdea", "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19",
"sha256:7211ef110a9194b6042449431e08c4d80c0481e5891e58d429df5899690511c2", "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0",
"sha256:764226ceef3125e53ea2cb275000e309c0aa5464d43bd72abd661e27fffc26ab", "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8",
"sha256:7645a8e814a3ee34a89c4a372011dcd817964ce8cb273c8ed6119d706e9613e3", "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a",
"sha256:76d4711f6f6d08551a7e9ef28c722f4a50dd0fc204c56b4bcd95c6cc05ce6fbb", "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09",
"sha256:7f4f399d28478d1f604c2ff9119907af9726aed73680e5ed1ca634d377abb087", "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86",
"sha256:88f7bc0fcca81f985f78dd0fa68d2c75abf8272b1f5c323ea4a01a4d7a614efc", "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c",
"sha256:8d0edd6b1c7fb94922bf569c9b092ee187a83f03fb1a63076e7774b60f9481a8", "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5",
"sha256:901289d524fdd571be1c7be054f48b1f88ce8dddcbdf1ec698b27d4b8b9e5d62", "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b",
"sha256:93ea75c050c5bb3d98016b4ba2497851eadf0ac154d88a67d7a6816206f6fa7f", "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b",
"sha256:981b9ab5a0a3178ff413bca62526bb784249421c24ad7381e39d67981be2c326", "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d",
"sha256:9ac08e601308e41eb533f232dbf6b7e4cea762f9f84f6357136eed926c15d12c", "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0",
"sha256:a02eb8ab2b8f200179b5f62b59757685ae9987996ae549ccf30f983f40602431", "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea",
"sha256:a0c6da9aee33ff0b1a451e867da0c1f47408112b3391dd43133838339e410963", "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776",
"sha256:a6c8097e01886188e5be3e6b14e94ab365f384736aa1fca6a0b9e35bd4a30bc7", "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a",
"sha256:aa384489fefeb62321b238e64c07ef48398fe80f9e1e6afeff22e140e0850eef", "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897",
"sha256:ad2a9eb6d9839ae241701d0918f54c51365a51407fd80f6b8289e2dfca977cc3", "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7",
"sha256:b206646d176a007466358aa21d85cd8600a415c67c9bd15403336c331a10d956", "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09",
"sha256:b826d97e4276750beca7c8f0f1a4938892697a6bcd8ec8217b3312dad6982781", "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9",
"sha256:b89ac9768b82205936771f8d2eb3ce88503b1556324c9f903e7156669f521472", "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe",
"sha256:bd7bd3b3830247580de99c99ea2a01416dfc3c34471ca1298bccabf86d0ff4dc", "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd",
"sha256:bdf1847068c362f16b353163391210269e4f0569a3c166bc6a9f74ccbfc7e839", "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742",
"sha256:c11b0746f5d946fecf750428a95f3e9ebe792c1ee3b1e96eeba145dc631a9672", "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09",
"sha256:c5374b80521d3d3f2ec5572e05adc94601985cc526fb276d0c8574a6d749f1b3", "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0",
"sha256:ca265542ca427bf97aed183c1676e2a9c66942e822b14dc6e5f42e038f92a503", "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932",
"sha256:ce31ae3e19f3c902de379cf1323d90c649425b86de7bbdf82871b8a2a0615f3d", "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1",
"sha256:ceb6ec0a10c65540421e20ebd29083c50e6d1143278746a4ef6bcf6153171eb8", "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a",
"sha256:d081f13b095d74b67d550de04df1c756831f3b83dc9881c38985834387487f1b", "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49",
"sha256:d5655a942f5f5d2c9ed93d72148226d75369b4f6952680211972a33e59b1dfdc", "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d",
"sha256:d5a32087d720c608f42caed0ef36d2b3ea61a9d09ee59a5142d6070da9041b8f", "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7",
"sha256:d6484756b12f40003c6128bfcc3fa9f0d49a687e171186c2d85ec82e3758c559", "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480",
"sha256:dd65632acaf0d47608190a71bfe46b209719bf2beb59507db08ccdbe712f969b", "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89",
"sha256:de343e75f40e972bae1ef6090267f8260c1446a1695e77096db6cfa25e759a95", "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e",
"sha256:e29cda763f752553fa14c68fb2195150bfab22b352572cb36c43c47bedba70eb", "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b",
"sha256:e41f3de4df3e80de75845d3e743b3f1c4c8613c3997a912dbf0229fc61a8b963", "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82",
"sha256:e66d2a64d44d50d2543405fb183a21f76b3b5fd16f130f5c99187c3fb4e64919", "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb",
"sha256:e74b0506fa5aa5598ac6a975a12aa8928cbb58e1f5ac8360792ef15de1aa848f", "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068",
"sha256:f0ed05f5079c708fe74bf9027e95125334b6978bf07fd5ab923e9e55e5fbb9d3", "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8",
"sha256:f61e2dc5ad442c52b4887f1fdc112f97caeff4d9e6ebe78879364ac59f1663e1", "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b",
"sha256:fec520865f42e5c7f050c2a79038897b1c7d1595e907a9e08e3353293ffc948e" "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb",
"sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2",
"sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11",
"sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b",
"sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc",
"sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0",
"sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497",
"sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17",
"sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0",
"sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2",
"sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439",
"sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5",
"sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac",
"sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825",
"sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887",
"sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced",
"sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"
], ],
"markers": "python_version >= '3.8'", "markers": "python_version >= '3.8'",
"version": "==1.4.0" "version": "==1.4.1"
}, },
"h11": { "h11": {
"hashes": [ "hashes": [
@ -745,99 +753,99 @@
}, },
"yarl": { "yarl": {
"hashes": [ "hashes": [
"sha256:09c19e5f4404574fcfb736efecf75844ffe8610606f3fccc35a1515b8b6712c4", "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51",
"sha256:0ab5baaea8450f4a3e241ef17e3d129b2143e38a685036b075976b9c415ea3eb", "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce",
"sha256:0d155a092bf0ebf4a9f6f3b7a650dc5d9a5bbb585ef83a52ed36ba46f55cc39d", "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559",
"sha256:126638ab961633f0940a06e1c9d59919003ef212a15869708dcb7305f91a6732", "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0",
"sha256:1a0a4f3aaa18580038cfa52a7183c8ffbbe7d727fe581300817efc1e96d1b0e9", "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81",
"sha256:1d93461e2cf76c4796355494f15ffcb50a3c198cc2d601ad8d6a96219a10c363", "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc",
"sha256:26a1a8443091c7fbc17b84a0d9f38de34b8423b459fb853e6c8cdfab0eacf613", "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4",
"sha256:271d63396460b6607b588555ea27a1a02b717ca2e3f2cf53bdde4013d7790929", "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c",
"sha256:28a108cb92ce6cf867690a962372996ca332d8cda0210c5ad487fe996e76b8bb", "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130",
"sha256:29beac86f33d6c7ab1d79bd0213aa7aed2d2f555386856bb3056d5fdd9dab279", "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136",
"sha256:2c757f64afe53a422e45e3e399e1e3cf82b7a2f244796ce80d8ca53e16a49b9f", "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e",
"sha256:2dad8166d41ebd1f76ce107cf6a31e39801aee3844a54a90af23278b072f1ccf", "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec",
"sha256:2dc72e891672343b99db6d497024bf8b985537ad6c393359dc5227ef653b2f17", "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7",
"sha256:2f3c8822bc8fb4a347a192dd6a28a25d7f0ea3262e826d7d4ef9cc99cd06d07e", "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1",
"sha256:32435d134414e01d937cd9d6cc56e8413a8d4741dea36af5840c7750f04d16ab", "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455",
"sha256:3cfa4dbe17b2e6fca1414e9c3bcc216f6930cb18ea7646e7d0d52792ac196808", "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099",
"sha256:3d5434b34100b504aabae75f0622ebb85defffe7b64ad8f52b8b30ec6ef6e4b9", "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129",
"sha256:4003f380dac50328c85e85416aca6985536812c082387255c35292cb4b41707e", "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10",
"sha256:44e91a669c43f03964f672c5a234ae0d7a4d49c9b85d1baa93dec28afa28ffbd", "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142",
"sha256:4a14907b597ec55740f63e52d7fee0e9ee09d5b9d57a4f399a7423268e457b57", "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98",
"sha256:4ce77d289f8d40905c054b63f29851ecbfd026ef4ba5c371a158cfe6f623663e", "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa",
"sha256:4d6d74a97e898c1c2df80339aa423234ad9ea2052f66366cef1e80448798c13d", "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7",
"sha256:51382c72dd5377861b573bd55dcf680df54cea84147c8648b15ac507fbef984d", "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525",
"sha256:525cd69eff44833b01f8ef39aa33a9cc53a99ff7f9d76a6ef6a9fb758f54d0ff", "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c",
"sha256:53ec65f7eee8655bebb1f6f1607760d123c3c115a324b443df4f916383482a67", "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9",
"sha256:5f74b015c99a5eac5ae589de27a1201418a5d9d460e89ccb3366015c6153e60a", "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c",
"sha256:6280353940f7e5e2efaaabd686193e61351e966cc02f401761c4d87f48c89ea4", "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8",
"sha256:632c7aeb99df718765adf58eacb9acb9cbc555e075da849c1378ef4d18bf536a", "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b",
"sha256:6465d36381af057d0fab4e0f24ef0e80ba61f03fe43e6eeccbe0056e74aadc70", "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf",
"sha256:66a6dbf6ca7d2db03cc61cafe1ee6be838ce0fbc97781881a22a58a7c5efef42", "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23",
"sha256:6d350388ba1129bc867c6af1cd17da2b197dff0d2801036d2d7d83c2d771a682", "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd",
"sha256:7217234b10c64b52cc39a8d82550342ae2e45be34f5bff02b890b8c452eb48d7", "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27",
"sha256:721ee3fc292f0d069a04016ef2c3a25595d48c5b8ddc6029be46f6158d129c92", "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f",
"sha256:72a57b41a0920b9a220125081c1e191b88a4cdec13bf9d0649e382a822705c65", "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece",
"sha256:73cc83f918b69110813a7d95024266072d987b903a623ecae673d1e71579d566", "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434",
"sha256:778df71c8d0c8c9f1b378624b26431ca80041660d7be7c3f724b2c7a6e65d0d6", "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec",
"sha256:79e1df60f7c2b148722fb6cafebffe1acd95fd8b5fd77795f56247edaf326752", "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff",
"sha256:7c86d0d0919952d05df880a1889a4f0aeb6868e98961c090e335671dea5c0361", "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78",
"sha256:7eaf13af79950142ab2bbb8362f8d8d935be9aaf8df1df89c86c3231e4ff238a", "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d",
"sha256:828235a2a169160ee73a2fcfb8a000709edf09d7511fccf203465c3d5acc59e4", "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863",
"sha256:8535e111a064f3bdd94c0ed443105934d6f005adad68dd13ce50a488a0ad1bf3", "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53",
"sha256:88d2c3cc4b2f46d1ba73d81c51ec0e486f59cc51165ea4f789677f91a303a9a7", "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31",
"sha256:8a2538806be846ea25e90c28786136932ec385c7ff3bc1148e45125984783dc6", "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15",
"sha256:8dab30b21bd6fb17c3f4684868c7e6a9e8468078db00f599fb1c14e324b10fca", "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5",
"sha256:8f18a7832ff85dfcd77871fe677b169b1bc60c021978c90c3bb14f727596e0ae", "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b",
"sha256:946db4511b2d815979d733ac6a961f47e20a29c297be0d55b6d4b77ee4b298f6", "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57",
"sha256:96758e56dceb8a70f8a5cff1e452daaeff07d1cc9f11e9b0c951330f0a2396a7", "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3",
"sha256:9a172c3d5447b7da1680a1a2d6ecdf6f87a319d21d52729f45ec938a7006d5d8", "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1",
"sha256:9a5211de242754b5e612557bca701f39f8b1a9408dff73c6db623f22d20f470e", "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f",
"sha256:9df9a0d4c5624790a0dea2e02e3b1b3c69aed14bcb8650e19606d9df3719e87d", "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad",
"sha256:aa4643635f26052401750bd54db911b6342eb1a9ac3e74f0f8b58a25d61dfe41", "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c",
"sha256:aed37db837ecb5962469fad448aaae0f0ee94ffce2062cf2eb9aed13328b5196", "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7",
"sha256:af52725c7c39b0ee655befbbab5b9a1b209e01bb39128dce0db226a10014aacc", "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2",
"sha256:b0b8c06afcf2bac5a50b37f64efbde978b7f9dc88842ce9729c020dc71fae4ce", "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b",
"sha256:b61e64b06c3640feab73fa4ff9cb64bd8182de52e5dc13038e01cfe674ebc321", "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2",
"sha256:b7831566595fe88ba17ea80e4b61c0eb599f84c85acaa14bf04dd90319a45b90", "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b",
"sha256:b8bc5b87a65a4e64bc83385c05145ea901b613d0d3a434d434b55511b6ab0067", "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9",
"sha256:b8d51817cf4b8d545963ec65ff06c1b92e5765aa98831678d0e2240b6e9fd281", "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be",
"sha256:b9f9cafaf031c34d95c1528c16b2fa07b710e6056b3c4e2e34e9317072da5d1a", "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e",
"sha256:bb72d2a94481e7dc7a0c522673db288f31849800d6ce2435317376a345728225", "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984",
"sha256:c25ec06e4241e162f5d1f57c370f4078797ade95c9208bd0c60f484834f09c96", "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4",
"sha256:c405d482c320a88ab53dcbd98d6d6f32ada074f2d965d6e9bf2d823158fa97de", "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074",
"sha256:c4472fe53ebf541113e533971bd8c32728debc4c6d8cc177f2bff31d011ec17e", "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2",
"sha256:c4b1efb11a8acd13246ffb0bee888dd0e8eb057f8bf30112e3e21e421eb82d4a", "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392",
"sha256:c5f3faeb8100a43adf3e7925d556801d14b5816a0ac9e75e22948e787feec642", "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91",
"sha256:c6f034386e5550b5dc8ded90b5e2ff7db21f0f5c7de37b6efc5dac046eb19c10", "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541",
"sha256:c99ddaddb2fbe04953b84d1651149a0d85214780e4d0ee824e610ab549d98d92", "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf",
"sha256:ca6b66f69e30f6e180d52f14d91ac854b8119553b524e0e28d5291a724f0f423", "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572",
"sha256:cccdc02e46d2bd7cb5f38f8cc3d9db0d24951abd082b2f242c9e9f59c0ab2af3", "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66",
"sha256:cd49a908cb6d387fc26acee8b7d9fcc9bbf8e1aca890c0b2fdfd706057546080", "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575",
"sha256:cf7a4e8de7f1092829caef66fd90eaf3710bc5efd322a816d5677b7664893c93", "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14",
"sha256:cfd77e8e5cafba3fb584e0f4b935a59216f352b73d4987be3af51f43a862c403", "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5",
"sha256:d34c4f80956227f2686ddea5b3585e109c2733e2d4ef12eb1b8b4e84f09a2ab6", "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1",
"sha256:d61a0ca95503867d4d627517bcfdc28a8468c3f1b0b06c626f30dd759d3999fd", "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e",
"sha256:d81657b23e0edb84b37167e98aefb04ae16cbc5352770057893bd222cdc6e45f", "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551",
"sha256:d92d897cb4b4bf915fbeb5e604c7911021a8456f0964f3b8ebbe7f9188b9eabb", "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17",
"sha256:dd318e6b75ca80bff0b22b302f83a8ee41c62b8ac662ddb49f67ec97e799885d", "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead",
"sha256:dd952b9c64f3b21aedd09b8fe958e4931864dba69926d8a90c90d36ac4e28c9a", "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0",
"sha256:e0e7e83f31e23c5d00ff618045ddc5e916f9e613d33c5a5823bc0b0a0feb522f", "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe",
"sha256:e0f17d1df951336a02afc8270c03c0c6e60d1f9996fcbd43a4ce6be81de0bd9d", "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234",
"sha256:e2a16ef5fa2382af83bef4a18c1b3bcb4284c4732906aa69422cf09df9c59f1f", "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0",
"sha256:e36021db54b8a0475805acc1d6c4bca5d9f52c3825ad29ae2d398a9d530ddb88", "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7",
"sha256:e73db54c967eb75037c178a54445c5a4e7461b5203b27c45ef656a81787c0c1b", "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34",
"sha256:e741bd48e6a417bdfbae02e088f60018286d6c141639359fb8df017a3b69415a", "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42",
"sha256:f7271d6bd8838c49ba8ae647fc06469137e1c161a7ef97d778b72904d9b68696", "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385",
"sha256:fc391e3941045fd0987c77484b2799adffd08e4b6735c4ee5f054366a2e1551d", "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78",
"sha256:fc94441bcf9cb8c59f51f23193316afefbf3ff858460cb47b5758bf66a14d130", "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be",
"sha256:fe34befb8c765b8ce562f0200afda3578f8abb159c76de3ab354c80b72244c41", "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958",
"sha256:fe8080b4f25dfc44a86bedd14bc4f9d469dfc6456e6f3c5d9077e81a5fedfba7", "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749",
"sha256:ff34cb09a332832d1cf38acd0f604c068665192c6107a439a92abfd8acf90fe2" "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"
], ],
"markers": "python_version >= '3.7'", "markers": "python_version >= '3.7'",
"version": "==1.9.3" "version": "==1.9.4"
}, },
"yt-dlp": { "yt-dlp": {
"hashes": [ "hashes": [

View file

@ -41,7 +41,7 @@ docker run -d --name ytptube -p 8081:8081 -v ./config:/config:rw -v ./downloads:
## Run using docker-compose ## Run using docker-compose
```yaml ```yaml
version: "3" version: "3.9"
services: services:
ytptube: ytptube:
user: "1000:1000" user: "1000:1000"
@ -51,8 +51,10 @@ services:
ports: ports:
- "8081:8081" - "8081:8081"
volumes: volumes:
- ./config:/config - ./config:/config:rw
- ./downloads:/downloads - ./downloads:/downloads:rw
tmpfs:
- /tmp
``` ```
## Configuration via environment variables ## Configuration via environment variables

View file

@ -107,9 +107,21 @@ class Main:
f'YTPTube v{self.config.version} - listening on http://{self.config.host}:{self.config.port}'), f'YTPTube v{self.config.version} - listening on http://{self.config.host}:{self.config.port}'),
) )
async def connect(self, sid, environ): async def connect(self, sid, _):
await self.sio.emit('all', self.serializer.encode(self.dqueue.get()), to=sid) data: dict = {
await self.sio.emit('configuration', self.serializer.encode(self.config), to=sid) **self.dqueue.get(),
"config": self.config,
"tasks": [],
}
if os.path.exists(os.path.join(self.config.config_path, 'tasks.json')):
try:
with open(os.path.join(self.config.config_path, 'tasks.json'), 'r') as f:
data['tasks'] = json.load(f)
except Exception as e:
pass
await self.sio.emit('initial_data', self.serializer.encode(data), to=sid)
def addTasks(self): def addTasks(self):
tasks_file: str = os.path.join(self.config.config_path, 'tasks.json') tasks_file: str = os.path.join(self.config.config_path, 'tasks.json')
@ -118,8 +130,13 @@ class Main:
f'No tasks file found at {tasks_file}. Skipping Tasks.') f'No tasks file found at {tasks_file}. Skipping Tasks.')
return return
with open(tasks_file, 'r') as f: try:
tasks = json.load(f) with open(tasks_file, 'r') as f:
tasks = json.load(f)
except Exception as e:
logging.error(
f'Could not load tasks file [{tasks_file}]. Error message [{str(e)}]. Skipping Tasks.')
return
for task in tasks: for task in tasks:
if not task.get('url'): if not task.get('url'):
@ -188,6 +205,22 @@ class Main:
return web.Response(text=self.serializer.encode(status)) return web.Response(text=self.serializer.encode(status))
@self.routes.get(self.config.url_prefix + 'tasks')
async def tasks(_: Request) -> Response:
tasks_file: str = os.path.join(
self.config.config_path, 'tasks.json')
if not os.path.exists(tasks_file):
return web.json_response({"error": "No tasks defined."}, status=404)
try:
with open(tasks_file, 'r') as f:
tasks = json.load(f)
except Exception as e:
return web.json_response({"error": str(e)}, status=500)
return web.json_response(tasks)
@self.routes.post(self.config.url_prefix + 'add_batch') @self.routes.post(self.config.url_prefix + 'add_batch')
async def add_batch(request: Request) -> Response: async def add_batch(request: Request) -> Response:
status = {} status = {}

View file

@ -5,6 +5,7 @@ import re
import sys import sys
import coloredlogs import coloredlogs
class Config: class Config:
config_path: str = '.' config_path: str = '.'
download_path: str = '.' download_path: str = '.'

View file

@ -104,9 +104,6 @@ class Download:
if self.debug: if self.debug:
params['verbose'] = True params['verbose'] = True
params['logger'] = logging.getLogger('YTPTube-ytdl')
else:
params['quiet'] = True
if self.info.ytdlp_cookies: if self.info.ytdlp_cookies:
try: try:
@ -122,8 +119,8 @@ class Download:
logging.error( logging.error(
f'Invalid cookies: was provided for {self.info.title} - {str(e)}') f'Invalid cookies: was provided for {self.info.title} - {str(e)}')
logging.debug( logging.info(
f'Downloading {self.info._id} {self.info.title}... {params}') f'Downloading {self.info._id=} {self.info.title=}... {params=}')
ret = yt_dlp.YoutubeDL(params=params).download([self.info.url]) ret = yt_dlp.YoutubeDL(params=params).download([self.info.url])
self.status_queue.put( self.status_queue.put(

View file

@ -4,7 +4,7 @@ import math
import os import os
from urllib.parse import quote from urllib.parse import quote
from src.Utils import calcDownloadPath from src.Utils import calcDownloadPath
from ffprobe import FFProbe from src.ffprobe import FFProbe
from src.Config import Config from src.Config import Config
@ -31,14 +31,15 @@ class M3u8:
if not os.path.exists(realFile): if not os.path.exists(realFile):
raise Exception(f"File {realFile} does not exist.") raise Exception(f"File {realFile} does not exist.")
metadata = FFProbe(realFile) try:
ffprobe = FFProbe(realFile)
except UnicodeDecodeError as e:
pass
# video duration. if not 'duration' in ffprobe.metadata:
if not 'Duration' in metadata.metadata:
raise Exception(f"Unable to get {realFile} duration.") raise Exception(f"Unable to get {realFile} duration.")
duration: float = self.parseDuration(metadata.metadata.get('Duration')) duration: float = float(ffprobe.metadata.get('duration'))
m3u8 = "#EXTM3U\n" m3u8 = "#EXTM3U\n"
m3u8 += "#EXT-X-VERSION:3\n" m3u8 += "#EXT-X-VERSION:3\n"
@ -52,11 +53,11 @@ class M3u8:
segmentParams = { segmentParams = {
} }
for stream in metadata.streams: for stream in ffprobe.streams:
if stream.codec_type == 'video': if stream.is_video():
if stream.codec_name not in self.ok_vcodecs: if stream.codec_name not in self.ok_vcodecs:
segmentParams['vc'] = 1 segmentParams['vc'] = 1
if stream.codec_type == 'audio': if stream.is_audio():
if stream.codec_name not in self.ok_acodecs: if stream.codec_name not in self.ok_acodecs:
segmentParams['ac'] = 1 segmentParams['ac'] = 1

View file

@ -65,6 +65,8 @@ class Segments:
ffmpegCmd.append('-i') ffmpegCmd.append('-i')
ffmpegCmd.append(f'file:{tmpFile}') ffmpegCmd.append(f'file:{tmpFile}')
ffmpegCmd.append('-map_metadata')
ffmpegCmd.append('-1')
ffmpegCmd.append('-pix_fmt') ffmpegCmd.append('-pix_fmt')
ffmpegCmd.append('yuv420p') ffmpegCmd.append('yuv420p')

View file

@ -191,6 +191,8 @@ def mergeDict(source: dict, destination: dict) -> dict:
source=value, source=value,
destination=destination_copy.setdefault(key, {}) destination=destination_copy.setdefault(key, {})
) )
elif isinstance(value, list) and isinstance(destination_key_value, list):
destination_copy[key] = destination_key_value + value
else: else:
destination_copy[key] = value destination_copy[key] = value
@ -202,6 +204,7 @@ def mergeConfig(config: dict, new_config: dict) -> dict:
ignored_keys: tuple = ( ignored_keys: tuple = (
'cookiefile', 'cookiefile',
'download_archive'
'paths', 'paths',
'outtmpl', 'outtmpl',
'progress_hooks', 'progress_hooks',

247
app/src/ffprobe.py Normal file
View file

@ -0,0 +1,247 @@
"""
Python wrapper for ffprobe command line tool. ffprobe must exist in the path.
"""
import functools
import json
import operator
import os
import pipes
import re
import subprocess
class FFProbeError(Exception):
pass
class FFStream:
"""
An object representation of an individual stream in a multimedia file.
"""
def __init__(self, json_data: dict):
for key, val in json_data.items():
setattr(self, key, val)
try:
self.__dict__['framerate'] = round(
functools.reduce(
operator.truediv, map(int, self.__dict__.get(
'avg_frame_rate', '').split('/'))
)
)
except ValueError:
self.__dict__['framerate'] = None
except ZeroDivisionError:
self.__dict__['framerate'] = 0
def __repr__(self):
if not 'codec_long_name' in self.__dict__:
self.codec_long_name = self.__dict__.get('codec_name', '')
if self.is_video():
return f"<Stream: #{self.index} [{self.codec_type}] {self.codec_long_name}, {self.framerate}, ({self.width}x{self.height})>"
if self.is_audio():
return f"<Stream: #{self.index} [{self.codec_type}] {self.codec_long_name}, channels: {self.channels} ({self.channel_layout}), " \
"{sample_rate}Hz> "
if self.is_subtitle() or self.is_attachment():
return f"<Stream: #{self.index} [{self.codec_type}] {self.codec_long_name}>"
return f"<Stream: #{self.index} [{self.codec_type}]>"
def is_audio(self):
"""
Is this stream labelled as an audio stream?
"""
return self.__dict__.get('codec_type', None) == 'audio'
def is_video(self):
"""
Is the stream labelled as a video stream.
"""
return self.__dict__.get('codec_type', None) == 'video'
def is_subtitle(self):
"""
Is the stream labelled as a subtitle stream.
"""
return self.__dict__.get('codec_type', None) == 'subtitle'
def is_attachment(self):
"""
Is the stream labelled as a attachment stream.
"""
return self.__dict__.get('codec_type', None) == 'attachment'
def frame_size(self):
"""
Returns the pixel frame size as an integer tuple (width,height) if the stream is a video stream.
Returns None if it is not a video stream.
"""
size = None
if self.is_video():
width = self.__dict__['width']
height = self.__dict__['height']
if width and height:
try:
size = (int(width), int(height))
except ValueError:
raise FFProbeError(
"None integer size {}:{}".format(width, height))
else:
return None
return size
def pixel_format(self):
"""
Returns a string representing the pixel format of the video stream. e.g. yuv420p.
Returns none is it is not a video stream.
"""
return self.__dict__.get('pix_fmt', None)
def frames(self):
"""
Returns the length of a video stream in frames. Returns 0 if not a video stream.
"""
if self.is_video() or self.is_audio():
try:
frame_count = int(self.__dict__.get('nb_frames', ''))
except ValueError:
raise FFProbeError('None integer frame count')
else:
frame_count = 0
return frame_count
def duration_seconds(self):
"""
Returns the runtime duration of the video stream as a floating point number of seconds.
Returns 0.0 if not a video stream.
"""
if self.is_video() or self.is_audio():
try:
duration = float(self.__dict__.get('duration', ''))
except ValueError:
raise FFProbeError('None numeric duration')
else:
duration = 0.0
return duration
def language(self):
"""
Returns language tag of stream. e.g. eng
"""
return self.__dict__.get('TAG:language', None)
def codec(self):
"""
Returns a string representation of the stream codec.
"""
return self.__dict__.get('codec_name', None)
def codec_description(self):
"""
Returns a long representation of the stream codec.
"""
return self.__dict__.get('codec_long_name', None)
def codec_tag(self):
"""
Returns a short representative tag of the stream codec.
"""
return self.__dict__.get('codec_tag_string', None)
def bit_rate(self):
"""
Returns bit_rate as an integer in bps
"""
try:
return int(self.__dict__.get('bit_rate', ''))
except ValueError:
raise FFProbeError('None integer bit_rate')
class FFProbe:
"""
FFProbe wraps the ffprobe command and pulls the data into an object form::
metadata=FFProbe('multimedia-file.mov')
"""
audio: list[FFStream] = []
attachment: list[FFStream] = []
streams: list[FFStream] = []
subtitle: list[FFStream] = []
video: list[FFStream] = []
metadata: dict = {}
path_to_video: str = ''
def __init__(self, path_to_video):
self.path_to_video = path_to_video
try:
with open(os.devnull, 'w') as tempf:
subprocess.check_call(
["ffprobe", "-h"], stdout=tempf, stderr=tempf)
except FileNotFoundError:
raise IOError('ffprobe not found.')
if os.path.isfile(self.path_to_video):
cmd: list = [
"ffprobe -v quiet -of json -show_format -show_streams " +
pipes.quote(self.path_to_video)
]
p = subprocess.Popen(
args=cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=True
)
p.wait()
data, err = p.communicate()
if p.returncode == 0:
parsed: dict = json.loads(data.decode('utf-8'))
else:
raise FFProbeError(f"FFProbe error: {err}")
stream = False
self.streams = []
self.video = []
self.audio = []
self.subtitle = []
self.attachment = []
for stream in parsed['streams'] if 'streams' in parsed else []:
self.streams.append(FFStream(stream))
self.metadata = parsed['format'] if 'format' in parsed else {}
for stream in self.streams:
if stream.is_audio():
self.audio.append(stream)
elif stream.is_video():
self.video.append(stream)
elif stream.is_subtitle():
self.subtitle.append(stream)
elif stream.is_attachment():
self.attachment.append(stream)
else:
raise IOError('No such media file ' + self.path_to_video)
def __repr__(self):
return "<FFprobe: {metadata}, {video}, {audio}, {subtitle}, {attachment}>".format(**vars(self))
if __name__ == '__main__':
import sys
if len(sys.argv) < 2:
print("Usage: ffprobe.py <path to media file>")
sys.exit(1)
ff = FFProbe(sys.argv[1])
print(ff)

View file

@ -1,6 +1,7 @@
<template> <template>
<PageHeader :config="config" @toggleForm="addForm = !addForm" /> <PageHeader :config="config" @toggleForm="addForm = !addForm" @toggleTasks="showTasks = !showTasks" />
<formAdd v-if="addForm" :config="config" @addItem="addItem" /> <formAdd v-if="addForm" :config="config" @addItem="addItem" />
<pageTasks v-if="showTasks" :tasks="config.tasks" @removeTask="deleteTask" />
<DownloadingList :config="config" :queue="downloading" @deleteItem="deleteItem" /> <DownloadingList :config="config" :queue="downloading" @deleteItem="deleteItem" />
<PageCompleted :config="config" :completed="completed" @deleteItem="deleteItem" @addItem="addItem" <PageCompleted :config="config" :completed="completed" @deleteItem="deleteItem" @addItem="addItem"
@playItem="playItem" /> @playItem="playItem" />
@ -20,6 +21,7 @@
import { onMounted, reactive, ref } from 'vue' import { onMounted, reactive, ref } from 'vue'
import PageHeader from './components/Page-Header' import PageHeader from './components/Page-Header'
import formAdd from './components/Form-Add' import formAdd from './components/Form-Add'
import pageTasks from './components/Page-Tasks'
import DownloadingList from './components/Page-Downloading' import DownloadingList from './components/Page-Downloading'
import PageCompleted from './components/Page-Completed' import PageCompleted from './components/Page-Completed'
import PageFooter from './components/Page-Footer' import PageFooter from './components/Page-Footer'
@ -34,12 +36,14 @@ const bus = useEventBus('item_added');
const config = reactive({ const config = reactive({
isConnected: false, isConnected: false,
app: {}, app: {},
tasks: [],
}); });
const downloading = reactive({}); const downloading = reactive({});
const completed = reactive({}); const completed = reactive({});
const video_link = ref(''); const video_link = ref('');
const addForm = useStorage('addForm', true) const addForm = useStorage('addForm', true)
const showTasks = useStorage('showTasks', false)
onMounted(() => { onMounted(() => {
const socket = io(process.env.VUE_APP_BASE_URL, { const socket = io(process.env.VUE_APP_BASE_URL, {
@ -49,19 +53,17 @@ onMounted(() => {
socket.on('connect', () => config.isConnected = true); socket.on('connect', () => config.isConnected = true);
socket.on('disconnect', () => config.isConnected = false); socket.on('disconnect', () => config.isConnected = false);
socket.on('all', stream => { socket.on('initial_data', stream => {
const initialData = JSON.parse(stream); const initialData = JSON.parse(stream);
for (const key in initialData) { config.app = initialData['config'];
const element = initialData[key]; config.tasks = initialData['tasks'];
if (key === 'queue') {
for (const id in element) { for (const id in initialData['queue']) {
downloading[id] = element[id]; downloading[id] = initialData['queue'][id];
} }
} else if (key === 'done') {
for (const id in element) { for (const id in initialData['done']) {
completed[id] = element[id]; completed[id] = initialData['done'][id];
}
}
} }
}); });
@ -104,10 +106,6 @@ onMounted(() => {
data.deleting = dl?.deleting; data.deleting = dl?.deleting;
downloading[data._id] = data; downloading[data._id] = data;
}); });
socket.on('configuration', stream => {
config.app = JSON.parse(stream);
});
}); });
const deleteItem = (type, item) => { const deleteItem = (type, item) => {
@ -172,6 +170,14 @@ const playItem = (item) => {
video_link.value = config.app.url_host + config.app.url_prefix + baseDir + encodeURIComponent(item.filename); video_link.value = config.app.url_host + config.app.url_prefix + baseDir + encodeURIComponent(item.filename);
}; };
const deleteTask = (indexNumber, item) => {
if (!confirm(`Are you sure you want to delete this task ${item?.name || item.url}?`)) {
return;
}
config.tasks = config.tasks.filter((_, index) => {
return index !== indexNumber;
});
}
</script> </script>

View file

@ -1,26 +1,22 @@
<template> <template>
<nav class="navbar is-mobile is-dark"> <nav class="navbar is-mobile is-dark">
<div class="navbar-brand pl-5"> <div class="navbar-brand pl-5">
<a class="navbar-item" href="#"> <a class="navbar-item has-tooltip-bottom" :class="config.isConnected ? 'has-text-success' : 'has-text-danger'"
:data-tooltip="config.isConnected ? 'Connected' : 'Connecting'" href="javascript:void(0);">
<b>YTPTube</b> <b>YTPTube</b>
</a> </a>
</div> </div>
<div class="navbar-end"> <div class="navbar-end">
<div class="navbar-item">
<button class="button is-dark has-tooltip-bottom" :data-tooltip="config.isConnected ? 'Connected' : 'Connecting'">
<span class="icon-text" :class="config.isConnected ? 'has-text-success' : 'has-text-danger'">
<span class="icon">
<font-awesome-icon :icon="config.isConnected ? 'fa-solid fa-wifi' : 'fa-solid fa-signal'" />
</span>
</span>
</button>
</div>
<div class="navbar-item"> <div class="navbar-item">
<button data-tooltip="Show/Hide Add Form" class="button is-dark has-tooltip-bottom" @click="$emit('toggleForm')"> <button data-tooltip="Show/Hide Add Form" class="button is-dark has-tooltip-bottom" @click="$emit('toggleForm')">
<font-awesome-icon icon="fa-solid fa-plus" /> <font-awesome-icon icon="fa-solid fa-plus" />
</button> </button>
</div> </div>
<div class="navbar-item">
<button data-tooltip="Show/Hide Tasks" class="button is-dark has-tooltip-bottom" @click="$emit('toggleTasks')">
<font-awesome-icon icon="fa-solid fa-tasks" />
</button>
</div>
<div class="navbar-item"> <div class="navbar-item">
<button data-tooltip="Switch to Light theme" class="button is-dark has-tooltip-bottom" <button data-tooltip="Switch to Light theme" class="button is-dark has-tooltip-bottom"
@click="selectedTheme = 'light'" v-if="selectedTheme == 'dark'">🌞</button> @click="selectedTheme = 'light'" v-if="selectedTheme == 'dark'">🌞</button>
@ -37,7 +33,7 @@ import { useStorage } from '@vueuse/core'
const selectedTheme = useStorage('theme', (() => window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')()); const selectedTheme = useStorage('theme', (() => window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')());
defineEmits(['toggleForm']) defineEmits(['toggleForm', 'toggleTasks'])
defineProps({ defineProps({
config: { config: {
@ -115,6 +111,7 @@ watch(selectedTheme, (value) => {
display: flex; display: flex;
padding: 0; padding: 0;
} }
.navbar-menu { .navbar-menu {
flex-grow: 1; flex-grow: 1;
flex-shrink: 0; flex-shrink: 0;

View file

@ -0,0 +1,96 @@
<template>
<div class="mt-3 is-unselectable">
<p class="title is-3">
<span class="icon-text">
<span class="icon">
<font-awesome-icon icon="fa-solid fa-tasks" />
</span>
<span>Tasks</span>
</span>
</p>
<p class="subtitle is-4 has-text-danger">
This section is not working yet.
</p>
</div>
<div class="columns is-multiline">
<div class="column is-12 has-text-right">
<button class="button is-primary" @click="$emit('task_new')">
<span class="icon-text">
<span class="icon">
<font-awesome-icon icon="fa-solid fa-plus" />
</span>
<span>New Task</span>
</span>
</button>
</div>
<div class="column is-12">
<div class="table-container">
<table class="table is-bordered is-narrow is-hoverable is-striped is-fullwidth is-fixed">
<thead class="has-text-centered">
<tr>
<th class="has-text-centered" width="20%">Name</th>
<th class="has-text-centered" width="50%">URL</th>
<th class="has-text-centered" width="20%">Timer</th>
<th class="has-text-centered" width="10%">Options</th>
</tr>
</thead>
<tbody>
<tr v-for="item, key in tasks" :key="key">
<td class="is-text-overflow has-text-centered" v-if="item.name">{{ item.name }}</td>
<td class="has-text-centered" v-else>Not set</td>
<td class="is-text-overflow">{{ item.url }}</td>
<td class="has-text-centered" v-if="item.timer">{{ item.timer }}</td>
<td class="has-text-centered" v-else>once an hour</td>
<td class="has-text-centered">
<div class="field is-grouped">
<div class="control is-expanded">
<button class="button is-small is-danger" @click="$emit('removeTask', key, item)"
data-tooltip="Remove task">
<span class="icon">
<font-awesome-icon icon="fa-solid fa-trash" />
</span>
</button>
</div>
<div class="control is-expanded">
<button class="button is-small is-info" data-tooltip="Modify task">
<span class="icon">
<font-awesome-icon icon="fa-solid fa-cog" />
</span>
</button>
</div>
</div>
</td>
</tr>
<tr v-if="tasks.length < 1">
<td colspan="4" class="has-text-centered has-text-danger">
No tasks are defined.
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue'
defineEmits(['task_new', 'task_add', 'removeTask', 'tasks_updated'])
defineProps({
tasks: {
type: Array,
required: true
}
})
</script>
<style scoped>
table.is-fixed {
table-layout: fixed;
}
</style>

View file

@ -5,7 +5,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import { import {
faCog, faTrash, faLink, faPlus, faTrashCan, faCircleXmark, faCircleCheck, faRotateRight, faDownload, faUpRightFromSquare, faCog, faTrash, faLink, faPlus, faTrashCan, faCircleXmark, faCircleCheck, faRotateRight, faDownload, faUpRightFromSquare,
faSpinner, faWifi, faSignal, faArrowUp, faArrowDown, faSpinner, faArrowUp, faArrowDown, faTasks
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { faSquare, faSquareCheck } from '@fortawesome/free-regular-svg-icons' import { faSquare, faSquareCheck } from '@fortawesome/free-regular-svg-icons'
@ -17,7 +17,7 @@ import './assets/css/style.css'
import '@creativebulma/bulma-tooltip/dist/bulma-tooltip.min.css'; import '@creativebulma/bulma-tooltip/dist/bulma-tooltip.min.css';
library.add(faCog, faTrash, faLink, faPlus, faTrashCan, faCircleXmark, faCircleCheck, faRotateRight, faDownload, faUpRightFromSquare, library.add(faCog, faTrash, faLink, faPlus, faTrashCan, faCircleXmark, faCircleCheck, faRotateRight, faDownload, faUpRightFromSquare,
faSquare, faSquareCheck, faSpinner, faWifi, faSignal, faArrowUp, faArrowDown,) faSquare, faSquareCheck, faSpinner, faArrowUp, faArrowDown, faTasks,)
const app = createApp(App); const app = createApp(App);
app.config.globalProperties.capitalize = s => s && s[0].toUpperCase() + s.slice(1); app.config.globalProperties.capitalize = s => s && s[0].toUpperCase() + s.slice(1);