Merge pull request #246 from arabcoders/dev
Simplify the codebase and removed all backwards compatibility flags
This commit is contained in:
commit
18fe5409ce
23 changed files with 1306 additions and 1610 deletions
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
|
@ -22,6 +22,7 @@
|
|||
"consoletitle",
|
||||
"cookiesfrombrowser",
|
||||
"copyts",
|
||||
"cronsim",
|
||||
"daterange",
|
||||
"dotenv",
|
||||
"finaldir",
|
||||
|
|
|
|||
554
Pipfile.lock
generated
554
Pipfile.lock
generated
|
|
@ -35,91 +35,91 @@
|
|||
},
|
||||
"aiohttp": {
|
||||
"hashes": [
|
||||
"sha256:04eb541ce1e03edc1e3be1917a0f45ac703e913c21a940111df73a2c2db11d73",
|
||||
"sha256:05582cb2d156ac7506e68b5eac83179faedad74522ed88f88e5861b78740dc0e",
|
||||
"sha256:0a29be28e60e5610d2437b5b2fed61d6f3dcde898b57fb048aa5079271e7f6f3",
|
||||
"sha256:0b2501f1b981e70932b4a552fc9b3c942991c7ae429ea117e8fba57718cdeed0",
|
||||
"sha256:0df3788187559c262922846087e36228b75987f3ae31dd0a1e5ee1034090d42f",
|
||||
"sha256:12c5869e7ddf6b4b1f2109702b3cd7515667b437da90a5a4a50ba1354fe41881",
|
||||
"sha256:14fc03508359334edc76d35b2821832f092c8f092e4b356e74e38419dfe7b6de",
|
||||
"sha256:1a7169ded15505f55a87f8f0812c94c9412623c744227b9e51083a72a48b68a5",
|
||||
"sha256:1c68e41c4d576cd6aa6c6d2eddfb32b2acfb07ebfbb4f9da991da26633a3db1a",
|
||||
"sha256:20412c7cc3720e47a47e63c0005f78c0c2370020f9f4770d7fc0075f397a9fb0",
|
||||
"sha256:22a8107896877212130c58f74e64b77f7007cb03cea8698be317272643602d45",
|
||||
"sha256:28a3d083819741592685762d51d789e6155411277050d08066537c5edc4066e6",
|
||||
"sha256:2b86efe23684b58a88e530c4ab5b20145f102916bbb2d82942cafec7bd36a647",
|
||||
"sha256:2d0b46abee5b5737cb479cc9139b29f010a37b1875ee56d142aefc10686a390b",
|
||||
"sha256:321238a42ed463848f06e291c4bbfb3d15ba5a79221a82c502da3e23d7525d06",
|
||||
"sha256:3a8a0d127c10b8d89e69bbd3430da0f73946d839e65fec00ae48ca7916a31948",
|
||||
"sha256:3a8b0321e40a833e381d127be993b7349d1564b756910b28b5f6588a159afef3",
|
||||
"sha256:3b420d076a46f41ea48e5fcccb996f517af0d406267e31e6716f480a3d50d65c",
|
||||
"sha256:3b512f1de1c688f88dbe1b8bb1283f7fbeb7a2b2b26e743bb2193cbadfa6f307",
|
||||
"sha256:413fe39fd929329f697f41ad67936f379cba06fcd4c462b62e5b0f8061ee4a77",
|
||||
"sha256:41cf0cefd9e7b5c646c2ef529c8335e7eafd326f444cc1cdb0c47b6bc836f9be",
|
||||
"sha256:4848ae31ad44330b30f16c71e4f586cd5402a846b11264c412de99fa768f00f3",
|
||||
"sha256:4b0a200e85da5c966277a402736a96457b882360aa15416bf104ca81e6f5807b",
|
||||
"sha256:4e2e8ef37d4bc110917d038807ee3af82700a93ab2ba5687afae5271b8bc50ff",
|
||||
"sha256:4edcbe34e6dba0136e4cabf7568f5a434d89cc9de5d5155371acda275353d228",
|
||||
"sha256:51ba80d473eb780a329d73ac8afa44aa71dfb521693ccea1dea8b9b5c4df45ce",
|
||||
"sha256:5409a59d5057f2386bb8b8f8bbcfb6e15505cedd8b2445db510563b5d7ea1186",
|
||||
"sha256:572def4aad0a4775af66d5a2b5923c7de0820ecaeeb7987dcbccda2a735a993f",
|
||||
"sha256:599b66582f7276ebefbaa38adf37585e636b6a7a73382eb412f7bc0fc55fb73d",
|
||||
"sha256:59a05cdc636431f7ce843c7c2f04772437dd816a5289f16440b19441be6511f1",
|
||||
"sha256:602d4db80daf4497de93cb1ce00b8fc79969c0a7cf5b67bec96fa939268d806a",
|
||||
"sha256:65c75b14ee74e8eeff2886321e76188cbe938d18c85cff349d948430179ad02c",
|
||||
"sha256:69bb252bfdca385ccabfd55f4cd740d421dd8c8ad438ded9637d81c228d0da49",
|
||||
"sha256:6d3986112e34eaa36e280dc8286b9dd4cc1a5bcf328a7f147453e188f6fe148f",
|
||||
"sha256:6dd9766da617855f7e85f27d2bf9a565ace04ba7c387323cd3e651ac4329db91",
|
||||
"sha256:70ab0f61c1a73d3e0342cedd9a7321425c27a7067bebeeacd509f96695b875fc",
|
||||
"sha256:749f1eb10e51dbbcdba9df2ef457ec060554842eea4d23874a3e26495f9e87b1",
|
||||
"sha256:781c8bd423dcc4641298c8c5a2a125c8b1c31e11f828e8d35c1d3a722af4c15a",
|
||||
"sha256:7e7abe865504f41b10777ac162c727af14e9f4db9262e3ed8254179053f63e6d",
|
||||
"sha256:7f2dadece8b85596ac3ab1ec04b00694bdd62abc31e5618f524648d18d9dd7fa",
|
||||
"sha256:86135c32d06927339c8c5e64f96e4eee8825d928374b9b71a3c42379d7437058",
|
||||
"sha256:8778620396e554b758b59773ab29c03b55047841d8894c5e335f12bfc45ebd28",
|
||||
"sha256:87f0e003fb4dd5810c7fbf47a1239eaa34cd929ef160e0a54c570883125c4831",
|
||||
"sha256:8aa5c68e1e68fff7cd3142288101deb4316b51f03d50c92de6ea5ce646e6c71f",
|
||||
"sha256:8d14e274828561db91e4178f0057a915f3af1757b94c2ca283cb34cbb6e00b50",
|
||||
"sha256:8d1dd75aa4d855c7debaf1ef830ff2dfcc33f893c7db0af2423ee761ebffd22b",
|
||||
"sha256:92007c89a8cb7be35befa2732b0b32bf3a394c1b22ef2dff0ef12537d98a7bda",
|
||||
"sha256:92868f6512714efd4a6d6cb2bfc4903b997b36b97baea85f744229f18d12755e",
|
||||
"sha256:948abc8952aff63de7b2c83bfe3f211c727da3a33c3a5866a0e2cf1ee1aa950f",
|
||||
"sha256:95d7787f2bcbf7cb46823036a8d64ccfbc2ffc7d52016b4044d901abceeba3db",
|
||||
"sha256:997b57e38aa7dc6caab843c5e042ab557bc83a2f91b7bd302e3c3aebbb9042a1",
|
||||
"sha256:99b8bbfc8111826aa8363442c0fc1f5751456b008737ff053570f06a151650b3",
|
||||
"sha256:9e73fa341d8b308bb799cf0ab6f55fc0461d27a9fa3e4582755a3d81a6af8c09",
|
||||
"sha256:a0d2c04a623ab83963576548ce098baf711a18e2c32c542b62322a0b4584b990",
|
||||
"sha256:a40087b82f83bd671cbeb5f582c233d196e9653220404a798798bfc0ee189fff",
|
||||
"sha256:ad1f2fb9fe9b585ea4b436d6e998e71b50d2b087b694ab277b30e060c434e5db",
|
||||
"sha256:b05774864c87210c531b48dfeb2f7659407c2dda8643104fb4ae5e2c311d12d9",
|
||||
"sha256:b41693b7388324b80f9acfabd479bd1c84f0bc7e8f17bab4ecd9675e9ff9c734",
|
||||
"sha256:b42dbd097abb44b3f1156b4bf978ec5853840802d6eee2784857be11ee82c6a0",
|
||||
"sha256:b4e7c7ec4146a94a307ca4f112802a8e26d969018fabed526efc340d21d3e7d0",
|
||||
"sha256:b59d096b5537ec7c85954cb97d821aae35cfccce3357a2cafe85660cc6295628",
|
||||
"sha256:b9c60d1de973ca94af02053d9b5111c4fbf97158e139b14f1be68337be267be6",
|
||||
"sha256:bccd2cb7aa5a3bfada72681bdb91637094d81639e116eac368f8b3874620a654",
|
||||
"sha256:c32593ead1a8c6aabd58f9d7ee706e48beac796bb0cb71d6b60f2c1056f0a65f",
|
||||
"sha256:c7571f99525c76a6280f5fe8e194eeb8cb4da55586c3c61c59c33a33f10cfce7",
|
||||
"sha256:c8b2df9feac55043759aa89f722a967d977d80f8b5865a4153fc41c93b957efc",
|
||||
"sha256:ca9f835cdfedcb3f5947304e85b8ca3ace31eef6346d8027a97f4de5fb687534",
|
||||
"sha256:cc9253069158d57e27d47a8453d8a2c5a370dc461374111b5184cf2f147a3cc3",
|
||||
"sha256:ced66c5c6ad5bcaf9be54560398654779ec1c3695f1a9cf0ae5e3606694a000a",
|
||||
"sha256:d173c0ac508a2175f7c9a115a50db5fd3e35190d96fdd1a17f9cb10a6ab09aa1",
|
||||
"sha256:d6edc538c7480fa0a3b2bdd705f8010062d74700198da55d16498e1b49549b9c",
|
||||
"sha256:daf20d9c3b12ae0fdf15ed92235e190f8284945563c4b8ad95b2d7a31f331cd3",
|
||||
"sha256:dc311634f6f28661a76cbc1c28ecf3b3a70a8edd67b69288ab7ca91058eb5a33",
|
||||
"sha256:e2bc827c01f75803de77b134afdbf74fa74b62970eafdf190f3244931d7a5c0d",
|
||||
"sha256:e365034c5cf6cf74f57420b57682ea79e19eb29033399dd3f40de4d0171998fa",
|
||||
"sha256:e906da0f2bcbf9b26cc2b144929e88cb3bf943dd1942b4e5af066056875c7618",
|
||||
"sha256:e9faafa74dbb906b2b6f3eb9942352e9e9db8d583ffed4be618a89bd71a4e914",
|
||||
"sha256:ec6cd1954ca2bbf0970f531a628da1b1338f594bf5da7e361e19ba163ecc4f3b",
|
||||
"sha256:f296d637a50bb15fb6a229fbb0eb053080e703b53dbfe55b1e4bb1c5ed25d325",
|
||||
"sha256:f30fc72daf85486cdcdfc3f5e0aea9255493ef499e31582b34abadbfaafb0965",
|
||||
"sha256:fe846f0a98aa9913c2852b630cd39b4098f296e0907dd05f6c7b30d911afa4c3"
|
||||
"sha256:03ce9d2f01aef26cd6aaba2f330273c2364237db2f499b93c3e9f2e249f83cd2",
|
||||
"sha256:05eea49d598c4ece6f285e00464de206f838b48ff21938d5aa9c394e115945b9",
|
||||
"sha256:078b1ce274f967951b42a65d5b7ec115b7886343a5271f2eed330458ea87bb48",
|
||||
"sha256:0987dcf32e4c47f22634d32e4b0ffbc368bbcf2b33b408cd1a3d2dc0a6a5cd34",
|
||||
"sha256:0c7eba0f90e27ec4af64db051f35387fa17128e6eeb58ee0f2318f2627168cc2",
|
||||
"sha256:0e97c1e55f6931f07ecaf53aef8352def8386adfd0cd3caa6429cc40e886d6a9",
|
||||
"sha256:0ec98c22030ea2a430cb11afddda7d4737b7e5c236c704f0d7d15504978598f7",
|
||||
"sha256:18733fa6bbe44698ff96138c1f1d682bbdf0846250a42c25c108eed328fef0d4",
|
||||
"sha256:1bee0f9e2d4088b57243d63afcb06256bd2d9ff683080f51e74fa790c8cfedfd",
|
||||
"sha256:204f6695b47a7d130ddf6680158920825d0d32202a870e0bc56a2ec637935b1a",
|
||||
"sha256:20dda85988a4f506bf5a457b416b238e32ab944a2deb878ddf0af92df9254a9b",
|
||||
"sha256:2241d862dc6a3c0c2662a6934e79078d3a1e51a76c0dca5d65b30f3debee6c9b",
|
||||
"sha256:23397670f3739b6f3c4019da8226190f6cce5ab2008b664ed96a6d1f0fe7f069",
|
||||
"sha256:23857adc919b64bba8a4db0eccb24e53fcaf85633e690ef1581c5562ed58cae7",
|
||||
"sha256:269d145c593a65f78fb9a64dece90341561ddb2e91a96d42681132b2f706c42a",
|
||||
"sha256:291f324f49ecede693dfb4820a412d1388cb10a2214ab60028252b505e105d6f",
|
||||
"sha256:29cce2a7e009494e675018c0b1819a133befbab8629c797276c5d793bbdf1138",
|
||||
"sha256:357355c9d51c8b12bbc7de43b27ce4b51f14cce050e00b5a87d0d5527d779395",
|
||||
"sha256:36a490f1ebe0b982366314c77f02258f87bd5d9bd362839dc6a24188447f37eb",
|
||||
"sha256:379882ab6a40e6e0879ad8e84dca74ddbadff94af4f314b59b7da89c8463a669",
|
||||
"sha256:38368a32530dcdeccfa47544cf66724118d9cc8a889c057e116723ab62994380",
|
||||
"sha256:3cfd9f4aeaec4a75a0b4986a9977ac0a09b3d87ae83415e4b461e86715c80897",
|
||||
"sha256:41f82df6f0f895f0f843bc86762bea45b4d0fe876de49239ffc89d2365426399",
|
||||
"sha256:430f9707f0c1239a92bff7769b0db185ef400278dc63c89f88ed1bd5153aab7a",
|
||||
"sha256:433e7388b3063bba462b3362641988270b087a9ccae22390364f86b37a480c17",
|
||||
"sha256:43625253e3dc018d34867b70909149f15f29eac0382802afe027f2fbf17bcb9c",
|
||||
"sha256:437eee9e057a7907b11e4af2b18df56b6c795b28e0a3ac250691936cf6bf40eb",
|
||||
"sha256:45d8c22c91bdb7417bd4f5119242dbd2e2140c0e9de47342c765f127f897eb57",
|
||||
"sha256:48d790d05c76b6cb93a1259b44d3f0019b61d507f8ebf0d49da3ef5ac858b05d",
|
||||
"sha256:4b03093d4140d926965d23497a059ec59c8c07e602d2489ce5fb990f3a897db4",
|
||||
"sha256:4b85486e8914d4e778343f5322834aada678eaf4c5315e50d41d9b74817ff97b",
|
||||
"sha256:558de86eef9a886e43c6ae5b75cecdce81203da5832d19d11da8de417967d478",
|
||||
"sha256:576b56a34d26ea8a8f0e1a30b8a069ba4ab121fb8eb796d1b89fedda7c74c553",
|
||||
"sha256:5a61df20fa77792e83307e266f76790f7cb67980dd476948948de212ee7ec64c",
|
||||
"sha256:5b5edd482ff0a8585b3f4e8b3681819447324166a43a5588800a5bca340dbf27",
|
||||
"sha256:5bd37d615cd26d09321bd0168305f8508778712cf38aeffeed550274fb48a2ee",
|
||||
"sha256:63f8d6106566f98fcfde7a643c9da52d90679b8592dea76c4adfc3cd5d06d22c",
|
||||
"sha256:65eb40e74e3126cba185da7a78815cf3a30140932193831b3bfd73c79965c723",
|
||||
"sha256:6fd82d0b3f73c59c80dade0ca8e0342de1ee261e147140ade65a465be670e83c",
|
||||
"sha256:71ed71d9431e770550aab27a77ef9d30d33ce6f558ab7818be7205ae6aca635d",
|
||||
"sha256:73a7f6283634dd30f93b9a67c414c00517869478b50361c503535e075fa07eaf",
|
||||
"sha256:74afb637cd06760afe0aa55a3ce82178ef4c950be65935add8f3809f701f36ca",
|
||||
"sha256:79a08d675167c50f41d106d67bbcbc9e86e1b43d305c4b9f982d5c656a94a9ba",
|
||||
"sha256:7ac49c32901489343b4dab064ab520f6b879a03fb4f9667c84620b66f07bed69",
|
||||
"sha256:7cf4b2b5a0f7a738ecd759eaeaef800fc7c57683b7be9d8a43fcb86ca62701b4",
|
||||
"sha256:7e20bd6aa51a5209c9131395e20ce126e8e317c0cf78a8180f026b4d73f018f6",
|
||||
"sha256:825ec92391e3e4ddda74de79ed0f8b443e9b412a0c9c82618ca2163abd875df5",
|
||||
"sha256:82ddf7f642b9c0b08063f3cf4e2818b22901bce8ebad05c232d9e295e77436a0",
|
||||
"sha256:84370ff70c1677ee0c4db40fe2baee6ffc72e9d32def3ff18739c1390c62329f",
|
||||
"sha256:85d73479b79172e7d667b466c170ca6097a334c09ecd83c95c210546031251b5",
|
||||
"sha256:8b4d8d78fbd5290204dcf43957a2184acd5cee358f203f24a3a97f7d7984eeb7",
|
||||
"sha256:8c6e39f0bb2418f839841f92b3cd64077ff5166d724c984ab450ca08d5e51d92",
|
||||
"sha256:94c552a4864ed292dadf1d341213374284a3af72e49bea02e70ce6f07cb37004",
|
||||
"sha256:9b97fae7f75a0b666ce4281627856d1b1a85477f26c2e8b266292e770c17df36",
|
||||
"sha256:a0361cafb50b185356a5f346c169aea1d14783df99e6da714d626b104586e0b5",
|
||||
"sha256:a3739daa52c0cff42f1c40f63b2fe818fcf41019d84c80a7add3224207a7060f",
|
||||
"sha256:a3d706afcc808f6add4208dfa13f911fd93c2a3dab6be484fee4fd0602a0867e",
|
||||
"sha256:a449c48f5b02c0c14f5310881558ca861bff8e00b1f79be4cf6a53f638464c30",
|
||||
"sha256:a50b86eb9cf74fa5b6f1386e08e1520dcbe83d7dfd4c8bf6f2ca72b03d42e79f",
|
||||
"sha256:a6d6d86443580f846ec9cf60f899b7cace34411f2ff5c95db5970047195e5bfa",
|
||||
"sha256:ae3a5d9f2fbe736fec7d24be25c57aa78c2d78d96540439ea68a8abbed9906fc",
|
||||
"sha256:b9b9a1e592ac8fcc4584baea240e41f77415e0de98932fdf19565aa3b6a02d0b",
|
||||
"sha256:be11989cbc0728f81c0d022cef140ef8adb20d3012ad8f0ac61853bef571eb52",
|
||||
"sha256:c28cbae1ce76dc48d0fcccb045ac21d00dcc1b306bb745910cf35585ce89404e",
|
||||
"sha256:c2de66177e087999568638c02639cf0248011b5c7fca69b006947153c534fcab",
|
||||
"sha256:c37aa3eb8eb92f3793f0c1e73f212a76cbc8d363e9990df54b5b7099f75ce740",
|
||||
"sha256:c930064b79cc0eb63678e376b819d546b0e2360264cd7544c32119496f646f35",
|
||||
"sha256:c982b2cbd7b8f4b31e9faf2de09e22b060a6cd0a693f20892dda41a8fb0f46ef",
|
||||
"sha256:d8370d31e6d8ecccd97cd791c466d0acb56527df51b0c105d7ea54c7fcc0f75b",
|
||||
"sha256:d99981304065f4ea407dd7495f74f8b8c10f0e26733f8a47dc174ece73744d14",
|
||||
"sha256:d9c8417a24063f35b526f8bf14f6f4bdea6f3f49850457337b6ea928901adbbc",
|
||||
"sha256:e13767424425bb607931a0b9e703b95d2693650011ef8f0166b4cd80066b66b9",
|
||||
"sha256:e792131352418dde7b0c598e217e89ecf6a26889f46f35f910e5544ffdebf3ae",
|
||||
"sha256:eba325409a0f990f9b43ed18916cbf5b9779bc4c979b8887c444e7be9c38ccca",
|
||||
"sha256:ec3dd04138bd30e6a3403dbd3ab5a5ccfb501597c5a95196cd816936ed55b777",
|
||||
"sha256:edcddb97574402ff7481bc6f70819ba863e77b0be58a840ed5f59d52d2f20a71",
|
||||
"sha256:f00e7540a60460fbeaffff4a7dcf72fe8b710f8280a542a4d798273787c64e72",
|
||||
"sha256:f1b6c639750bf2a228957e25fcab7a7ff11987c543d70bf73223369f0d7bdb27",
|
||||
"sha256:f1e0369f0dc8c895e718ce37147f56d46142d37596be183ab7a34192c5e6e4c5",
|
||||
"sha256:f30e6980ec5d6ad815a233e19e39fe27ea94b1081c31c8aa1df1b629da3737b8",
|
||||
"sha256:fc270fe480e7e425c45054543f58510fe649f70b77f514171909bbfe585105c0"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_version >= '3.9'",
|
||||
"version": "==3.11.14"
|
||||
"version": "==3.11.15"
|
||||
},
|
||||
"aiosignal": {
|
||||
"hashes": [
|
||||
|
|
@ -625,101 +625,101 @@
|
|||
},
|
||||
"multidict": {
|
||||
"hashes": [
|
||||
"sha256:0085b0afb2446e57050140240a8595846ed64d1cbd26cef936bfab3192c673b8",
|
||||
"sha256:042028348dc5a1f2be6c666437042a98a5d24cee50380f4c0902215e5ec41844",
|
||||
"sha256:05fefbc3cddc4e36da209a5e49f1094bbece9a581faa7f3589201fd95df40e5d",
|
||||
"sha256:063be88bd684782a0715641de853e1e58a2f25b76388538bd62d974777ce9bc2",
|
||||
"sha256:07bfa8bc649783e703263f783f73e27fef8cd37baaad4389816cf6a133141331",
|
||||
"sha256:08549895e6a799bd551cf276f6e59820aa084f0f90665c0f03dd3a50db5d3c48",
|
||||
"sha256:095a2eabe8c43041d3e6c2cb8287a257b5f1801c2d6ebd1dd877424f1e89cf29",
|
||||
"sha256:0b183a959fb88ad1be201de2c4bdf52fa8e46e6c185d76201286a97b6f5ee65c",
|
||||
"sha256:0c383d28857f66f5aebe3e91d6cf498da73af75fbd51cedbe1adfb85e90c0460",
|
||||
"sha256:0d57a01a2a9fa00234aace434d8c131f0ac6e0ac6ef131eda5962d7e79edfb5b",
|
||||
"sha256:0dc25a3293c50744796e87048de5e68996104d86d940bb24bc3ec31df281b191",
|
||||
"sha256:0e5a644e50ef9fb87878d4d57907f03a12410d2aa3b93b3acdf90a741df52c49",
|
||||
"sha256:0f249badb360b0b4d694307ad40f811f83df4da8cef7b68e429e4eea939e49dd",
|
||||
"sha256:0f74f2fc51555f4b037ef278efc29a870d327053aba5cb7d86ae572426c7cccc",
|
||||
"sha256:125dd82b40f8c06d08d87b3510beaccb88afac94e9ed4a6f6c71362dc7dbb04b",
|
||||
"sha256:13551d0e2d7201f0959725a6a769b6f7b9019a168ed96006479c9ac33fe4096b",
|
||||
"sha256:14ed9ed1bfedd72a877807c71113deac292bf485159a29025dfdc524c326f3e1",
|
||||
"sha256:163f4604e76639f728d127293d24c3e208b445b463168af3d031b92b0998bb90",
|
||||
"sha256:19e2819b0b468174de25c0ceed766606a07cedeab132383f1e83b9a4e96ccb4f",
|
||||
"sha256:1e2a2193d3aa5cbf5758f6d5680a52aa848e0cf611da324f71e5e48a9695cc86",
|
||||
"sha256:1f3c099d3899b14e1ce52262eb82a5f5cb92157bb5106bf627b618c090a0eadc",
|
||||
"sha256:214207dcc7a6221d9942f23797fe89144128a71c03632bf713d918db99bd36de",
|
||||
"sha256:2325105e16d434749e1be8022f942876a936f9bece4ec41ae244e3d7fae42aaf",
|
||||
"sha256:2529ddbdaa424b2c6c2eb668ea684dd6b75b839d0ad4b21aad60c168269478d7",
|
||||
"sha256:256d431fe4583c5f1e0f2e9c4d9c22f3a04ae96009b8cfa096da3a8723db0a16",
|
||||
"sha256:25bb96338512e2f46f615a2bb7c6012fe92a4a5ebd353e5020836a7e33120349",
|
||||
"sha256:2e87f1926e91855ae61769ba3e3f7315120788c099677e0842e697b0bfb659f2",
|
||||
"sha256:2fc6af8e39f7496047c7876314f4317736eac82bf85b54c7c76cf1a6f8e35d98",
|
||||
"sha256:3157126b028c074951839233647bd0e30df77ef1fedd801b48bdcad242a60f4e",
|
||||
"sha256:32c9b4878f48be3e75808ea7e499d6223b1eea6d54c487a66bc10a1871e3dc6a",
|
||||
"sha256:32ed748ff9ac682eae7859790d3044b50e3076c7d80e17a44239683769ff485e",
|
||||
"sha256:3501621d5e86f1a88521ea65d5cad0a0834c77b26f193747615b7c911e5422d2",
|
||||
"sha256:437c33561edb6eb504b5a30203daf81d4a9b727e167e78b0854d9a4e18e8950b",
|
||||
"sha256:48d39b1824b8d6ea7de878ef6226efbe0773f9c64333e1125e0efcfdd18a24c7",
|
||||
"sha256:4ac3fcf9a2d369bd075b2c2965544036a27ccd277fc3c04f708338cc57533081",
|
||||
"sha256:4ccfd74957ef53fa7380aaa1c961f523d582cd5e85a620880ffabd407f8202c0",
|
||||
"sha256:52b05e21ff05729fbea9bc20b3a791c3c11da61649ff64cce8257c82a020466d",
|
||||
"sha256:5389445f0173c197f4a3613713b5fb3f3879df1ded2a1a2e4bc4b5b9c5441b7e",
|
||||
"sha256:5c5e7d2e300d5cb3b2693b6d60d3e8c8e7dd4ebe27cd17c9cb57020cac0acb80",
|
||||
"sha256:5d26547423e5e71dcc562c4acdc134b900640a39abd9066d7326a7cc2324c530",
|
||||
"sha256:5dd7106d064d05896ce28c97da3f46caa442fe5a43bc26dfb258e90853b39b44",
|
||||
"sha256:5f8cb1329f42fadfb40d6211e5ff568d71ab49be36e759345f91c69d1033d633",
|
||||
"sha256:61d5541f27533f803a941d3a3f8a3d10ed48c12cf918f557efcbf3cd04ef265c",
|
||||
"sha256:639556758c36093b35e2e368ca485dada6afc2bd6a1b1207d85ea6dfc3deab27",
|
||||
"sha256:641cf2e3447c9ecff2f7aa6e9eee9eaa286ea65d57b014543a4911ff2799d08a",
|
||||
"sha256:6aed763b6a1b28c46c055692836879328f0b334a6d61572ee4113a5d0c859872",
|
||||
"sha256:6e2a2d6749e1ff2c9c76a72c6530d5baa601205b14e441e6d98011000f47a7ac",
|
||||
"sha256:7243c5a6523c5cfeca76e063efa5f6a656d1d74c8b1fc64b2cd1e84e507f7e2a",
|
||||
"sha256:76b34c12b013d813e6cb325e6bd4f9c984db27758b16085926bbe7ceeaace626",
|
||||
"sha256:781b5dd1db18c9e9eacc419027b0acb5073bdec9de1675c0be25ceb10e2ad133",
|
||||
"sha256:7c611345bbe7cb44aabb877cb94b63e86f2d0db03e382667dbd037866d44b4f8",
|
||||
"sha256:83b78c680d4b15d33042d330c2fa31813ca3974197bddb3836a5c635a5fd013f",
|
||||
"sha256:84e87a7d75fa36839a3a432286d719975362d230c70ebfa0948549cc38bd5b46",
|
||||
"sha256:89b3857652183b8206a891168af47bac10b970d275bba1f6ee46565a758c078d",
|
||||
"sha256:8cd1a0644ccaf27e9d2f6d9c9474faabee21f0578fe85225cc5af9a61e1653df",
|
||||
"sha256:8de4d42dffd5ced9117af2ce66ba8722402541a3aa98ffdf78dde92badb68932",
|
||||
"sha256:94a7bb972178a8bfc4055db80c51efd24baefaced5e51c59b0d598a004e8305d",
|
||||
"sha256:98aa8325c7f47183b45588af9c434533196e241be0a4e4ae2190b06d17675c02",
|
||||
"sha256:9e658d1373c424457ddf6d55ec1db93c280b8579276bebd1f72f113072df8a5d",
|
||||
"sha256:9f49585f4abadd2283034fc605961f40c638635bc60f5162276fec075f2e37a4",
|
||||
"sha256:9f6cad071960ba1914fa231677d21b1b4a3acdcce463cee41ea30bc82e6040cf",
|
||||
"sha256:a0cc398350ef31167e03f3ca7c19313d4e40a662adcb98a88755e4e861170bdd",
|
||||
"sha256:a1133414b771619aa3c3000701c11b2e4624a7f492f12f256aedde97c28331a2",
|
||||
"sha256:a33273a541f1e1a8219b2a4ed2de355848ecc0254264915b9290c8d2de1c74e1",
|
||||
"sha256:a3c0ff89fe40a152e77b191b83282c9664357dce3004032d42e68c514ceff27e",
|
||||
"sha256:a49994481b99cd7dedde07f2e7e93b1d86c01c0fca1c32aded18f10695ae17eb",
|
||||
"sha256:abf5b17bc0cf626a8a497d89ac691308dbd825d2ac372aa990b1ca114e470151",
|
||||
"sha256:ac380cacdd3b183338ba63a144a34e9044520a6fb30c58aa14077157a033c13e",
|
||||
"sha256:ad81012b24b88aad4c70b2cbc2dad84018783221b7f923e926f4690ff8569da3",
|
||||
"sha256:b2c00ad31fbc2cbac85d7d0fcf90853b2ca2e69d825a2d3f3edb842ef1544a2c",
|
||||
"sha256:b4c153863dd6569f6511845922c53e39c8d61f6e81f228ad5443e690fca403de",
|
||||
"sha256:b4f3d66dd0354b79761481fc15bdafaba0b9d9076f1f42cc9ce10d7fcbda205a",
|
||||
"sha256:b99aac6bb2c37db336fa03a39b40ed4ef2818bf2dfb9441458165ebe88b793af",
|
||||
"sha256:b9f6392d98c0bd70676ae41474e2eecf4c7150cb419237a41f8f96043fcb81d1",
|
||||
"sha256:c537da54ce4ff7c15e78ab1292e5799d0d43a2108e006578a57f531866f64025",
|
||||
"sha256:ca23db5fb195b5ef4fd1f77ce26cadefdf13dba71dab14dadd29b34d457d7c44",
|
||||
"sha256:cc826b9a8176e686b67aa60fd6c6a7047b0461cae5591ea1dc73d28f72332a8a",
|
||||
"sha256:cca83a629f77402cfadd58352e394d79a61c8015f1694b83ab72237ec3941f88",
|
||||
"sha256:cf8d370b2fea27fb300825ec3984334f7dd54a581bde6456799ba3776915a656",
|
||||
"sha256:d1175b0e0d6037fab207f05774a176d71210ebd40b1c51f480a04b65ec5c786d",
|
||||
"sha256:d1996ee1330e245cd3aeda0887b4409e3930524c27642b046e4fae88ffa66c5e",
|
||||
"sha256:d5a36953389f35f0a4e88dc796048829a2f467c9197265504593f0e420571547",
|
||||
"sha256:da51d8928ad8b4244926fe862ba1795f0b6e68ed8c42cd2f822d435db9c2a8f4",
|
||||
"sha256:e16e7297f29a544f49340012d6fc08cf14de0ab361c9eb7529f6a57a30cbfda1",
|
||||
"sha256:e25b11a0417475f093d0f0809a149aff3943c2c56da50fdf2c3c88d57fe3dfbd",
|
||||
"sha256:e4371591e621579cb6da8401e4ea405b33ff25a755874a3567c4075ca63d56e2",
|
||||
"sha256:e653d36b1bf48fa78c7fcebb5fa679342e025121ace8c87ab05c1cefd33b34fc",
|
||||
"sha256:e7d91a230c7f8af86c904a5a992b8c064b66330544693fd6759c3d6162382ecf",
|
||||
"sha256:e851e6363d0dbe515d8de81fd544a2c956fdec6f8a049739562286727d4a00c3",
|
||||
"sha256:ef7d48207926edbf8b16b336f779c557dd8f5a33035a85db9c4b0febb0706817",
|
||||
"sha256:f7716f7e7138252d88607228ce40be22660d6608d20fd365d596e7ca0738e019",
|
||||
"sha256:facaf11f21f3a4c51b62931feb13310e6fe3475f85e20d9c9fdce0d2ea561b87"
|
||||
"sha256:02a239ad4e35d3c033e701bc8b7beb05013fc6617b13948497cf50e9e9c12f7f",
|
||||
"sha256:091f3dfe63e9f48ae6c7da8d638e8d8e050ff11e348f69bafc20e65d11f7aa48",
|
||||
"sha256:0a1a5f50ccc57a317db68582411ce6aca076ee3bc386290b416a5783e807afa1",
|
||||
"sha256:0ba7d12ff77cadb59519aba674cac3a0b16d0fc5abaa3ef79b5ff3806e52e991",
|
||||
"sha256:0c4a6ec469c30f4162745d6485b778432c497032b8a2cb3a0332eea9d3b6aef6",
|
||||
"sha256:150cc33d5d972c1bd7b0e5c446cccfa4dc6b8e185ef54dab3327db101149f797",
|
||||
"sha256:162af9c2e0743465a47e2b32d9b0a7c260b7843629b5e6f0a8a92819b5a40d27",
|
||||
"sha256:168a02178b7e980899f3475ff008436eaab035d50e39cb8f7de641bbe9bbc3a6",
|
||||
"sha256:1aa6e1a605c602c119a22368f7d3b9dfb102a7cd69edae5a89e55da34fd64c2b",
|
||||
"sha256:1b293804f4b53be6297bc2afdeaf15aff76c1b6be69f3a3da785143eebdfb656",
|
||||
"sha256:1c8836280abc310ea6260dac93126645d21284885008c317c2ac4281a90f1398",
|
||||
"sha256:2264957a0e0fb92d8d538a27738178c48628c65ef0ec6b068d64d2e5d11cc00d",
|
||||
"sha256:257bd1540b7c32982b17e86f024985b9c591da8542a3fb350ed7dad42d725998",
|
||||
"sha256:299070f503a9a99e4af38d07da784ced28377cc62b678084b9e2e48fa51c57d3",
|
||||
"sha256:2a83896925e3a2ddec736c48255572e6f15a95339024c02b800dab28e63461a3",
|
||||
"sha256:2cef02d3878804909283549bc10d4789a14c610fcd286f17cd94a195b21fe469",
|
||||
"sha256:2cf3e0781febf9f093eff3eca2d6dd7954ef2969ff46f6cd95173a4db8397fd8",
|
||||
"sha256:2e87a8635a7db577617b50bd2f2080744ed20e556750b97e4f9988e6d20d3941",
|
||||
"sha256:31c68e103b253643cf9b4704f5d655997c116626b20ef3bb953992b7f447485d",
|
||||
"sha256:34ef116022119d3053ecce8ba62109c8b54a650a537b183e79de1db4078894a8",
|
||||
"sha256:38e60e400e07ba678a755cc89b09955a49acb0188d47aee2829ed4efc1ccb053",
|
||||
"sha256:3ac6ea86ac161198653dba8f0af047d7b718318ba6d6bf4c719bc288ac21017b",
|
||||
"sha256:3ad737617d38c55c0b72cd3ea01bf5cbab0d75123de5e52c39643ddc6465cb5f",
|
||||
"sha256:3ae6ea8e610143348199c1db9fea5392f6b11a0e37ac8e8e7cae9c408625c5d0",
|
||||
"sha256:3bcb8cdfeb08cef0138d696e52ec08fffaf009ef4b1c7c5a40340af672bd9b60",
|
||||
"sha256:3d783be54d076843f58bf061fdaf1071789fb924fb35a0eb84dbc2c8b68499a2",
|
||||
"sha256:3da30fb560b37cede31306b890c982213a23fa7335d368cdc16cf7034170860b",
|
||||
"sha256:430cb9ee0cf2d5a2fa91e78f306544352009dbda9ebb4bd04c5785e67fc04953",
|
||||
"sha256:43ad68da1385738a1e96a188029f092e88381352e697a0687784191c1d45845b",
|
||||
"sha256:478d29a270785fc58632c9d1e0f59f595d3fb06c5e63ac117136dba2d0e5f4f9",
|
||||
"sha256:4c3da8aa4b28f83382f0dee288d030eb0fa212df9632dc2fe4e4fc2a8e2577a4",
|
||||
"sha256:560af476d03256a65bd4ff05c83e7869ec3c2b40579d4ed1ac74075ef19e02bf",
|
||||
"sha256:56225de73d69f5ee6b783648eb1936e1bbe874a529cb1e15d64038904c54efb2",
|
||||
"sha256:5737f40ffb1926b8bf38d32fdac48a27c441b757f1bf44cbaa100dafef049a01",
|
||||
"sha256:5a1996d963016e6486b6a672f64f868e6b4e7e9e2caf710994df11b04790903e",
|
||||
"sha256:5a922601cb94f427dd96eec3a7c776537ce7490f2beb69e88a81859e537343e4",
|
||||
"sha256:5bd70d75b4bbfc0a26fdbfc72e260b70090637020b0d7d27eb26c7ae5edf16da",
|
||||
"sha256:5e0db6ea39ac96ae2ccc582291633ee55ae93c9cc6dca1f7ab4dd9f0b7e08f8a",
|
||||
"sha256:5eb2a8415a891564054447a327a7ef7ec90e69e0e63d85d1ffb03f82e102c740",
|
||||
"sha256:6193564d51d02a4aa6fc60bb40f04b65840e70e124282a7337cd94a9ed1b0b81",
|
||||
"sha256:65e763769cec2fa9c79b70555e3f2a7903747d127ab14ec9eaa5f00763bb4152",
|
||||
"sha256:66c108d8e02da2febb0aa7d7002e14c4a0571460993c9edf8249393cdae7eeef",
|
||||
"sha256:67caf9435b1f0115042cbc37e9d60475891b2d9b2a711ade0876580da2a5e0df",
|
||||
"sha256:6989ea9326f9a9171edc920a459003b1deffc6cb48605b8f2610846c7fbd201a",
|
||||
"sha256:69983ee3e649e41dc41ddea68f6c2ab64d7f19fcab4e21a72a1accf6482c5605",
|
||||
"sha256:6b25953a1d6a97746becbd663b49e3b436a5001c995a62662d65835a2ba996a7",
|
||||
"sha256:6b7e30e4246c2cd5e6c2c907486d235e4f3e8a39979744e9e0b8090629c62da4",
|
||||
"sha256:6be1182387fb9a95fd8a8cbfab2f2b61d61ea0ab4699780d4300ea207c8f8d71",
|
||||
"sha256:6befa3c621caf26f5affc6121bf863b8f049f862a7a10aabb0d19cce34c6e363",
|
||||
"sha256:6fbe184451480c17f1f8dac160c9f3f6d243010fdb8416de4d3d7ee69ea65aa4",
|
||||
"sha256:7074f10bf324e4d38208e1bcc2b6abd87901675b1a567507035ff1e7403378c0",
|
||||
"sha256:71a8ce430f6c341725aefc0031626c484e0858fd95d1bf3625e0d27919d8edca",
|
||||
"sha256:71d92e846541669ae1d11e00906c408c1dc9890196e13f11d4d62bd235ac9ddb",
|
||||
"sha256:72574693fad34d075a54fa771984a1a8809a9eff880b731152078e81c7d8c80f",
|
||||
"sha256:72a1159144e4ad7d152b2c3f75bed8c9b60ad06e9da322e1965981f8371c0dee",
|
||||
"sha256:72d15a61b76fa4b814390c21defd772893d1152157e3297e6afb328ef32d1711",
|
||||
"sha256:74e45386528db2d9346618276e8746507ec6ccb91512c5f02d2049ccafb0576e",
|
||||
"sha256:7594756e9607e5ffbc16c7f3b1715c0f7224a82ac389f0087fa5faaa65fb96e2",
|
||||
"sha256:767ed12b1662fac32d84eb43707e4f778b12554fc12ce3c8f7793a02111a9e32",
|
||||
"sha256:77e0406dc247851a1e250fdcfd46d13cdecc2ac5b8d1d038dea216da2e7fa9b7",
|
||||
"sha256:79658460926a1c48b2ac10196a673cc7d81857dc268ca593a66fd661ed1b77ea",
|
||||
"sha256:7bf0d43f2c56b600c6895c25a420e4b46c6a5acf91d6c9a6300713496f70e63b",
|
||||
"sha256:80935f26af0eec490c4e52381a28f39b08c2bc4ef4562448890027e4a4cfa3a4",
|
||||
"sha256:82bd76a12588bd2bb27965aaf9fcff2258ffb03c9a290368288ed4571ed539ab",
|
||||
"sha256:864684b36e0488715aac531785abe7514e872bfe83ecc768828e9ddaadeed320",
|
||||
"sha256:883c07b404229e98d30b1ef64f07d4e3422d431ecd727c2ebba8549f70b2ba16",
|
||||
"sha256:88548dec2f6f69e547252fe3113bf1b3b08c0879f5b44633927be07ed18c5cc0",
|
||||
"sha256:8895999e0de5ddddddd146bb1bfc0316af808580637fea158873b84785f7a9e9",
|
||||
"sha256:8ab4ea5f49166b990411c522c1f5f901014932ead15a463616ec93e10fff2c05",
|
||||
"sha256:904895323a59dd50387cddd1337c3eac48ec0fe6cb4d2e4106dc1a3b80091727",
|
||||
"sha256:90c3c60985e7da13e44aeaaf2e1c10fe1b7825788a18c82b0f9eaeb6c4e9d9c6",
|
||||
"sha256:9584441b7a603a12aa382adf8c093ddc5a22328a108dabc6a4a112fa5b4facae",
|
||||
"sha256:9ca652d9c6f68535537d75502b549ed0ca07fa6d3908f84f29f92148ec7310f2",
|
||||
"sha256:9efe23b3d0671eeb8b1e70791612538dcdc1b39275563ca78f338aa0ce9619d2",
|
||||
"sha256:a466c14574152b7caed5d76f1e46ae2963d33f5b0eb2dd915fa33870183b0f26",
|
||||
"sha256:a48fd39ba69f399f39b911885469bdfedd1de4a095146211e39e8b5db0e3a937",
|
||||
"sha256:a716344c6e92551bdf842b999c7d7b34a13e32facf3e6c5942690c9883d45e3a",
|
||||
"sha256:b6df1b707adab4858dfaa74877f60f07d9d6be01a57015cf4e62aa8f00f3576b",
|
||||
"sha256:b7d3053742a9a559dda8598a52e0c1bcbd18258cc199cba52137ce8c8e92c537",
|
||||
"sha256:b95d96a0640decaa24cd9cf386fd4d8a96c973aafa42dd9c65609f9f0d66cc34",
|
||||
"sha256:bc15e81587b652bbeba9d4d7d3707bcaaa1d7b4aab2024c14be477662003a211",
|
||||
"sha256:be034aa2841c57ea43a55bc123d8f3f41cc2d3d102a22390c863791ba6bf23f1",
|
||||
"sha256:c5223ab45639ce93c7233d518f6c3199422b49dbd0ebfb1d7917b5da2636712e",
|
||||
"sha256:d9c2b1ca98e5454b78cd434f29fc33eb8f8a2f343efc5f975225d92070b9f7f6",
|
||||
"sha256:db61c6ae9ee7268dc69a078ea22deaaf861350ad2e4c194c70798b8ec9789131",
|
||||
"sha256:dc4e82c5db4e65703c842b0947699dd958a7262a8b854d1c19bbfe2d27be9333",
|
||||
"sha256:e18db3b38ac30d8cc1ddbdfc7f6d3d814c1abb8936c57bd1c09c5da02873c8c4",
|
||||
"sha256:e31697466812ff1cdabd7a9db7fb7f37d319e58e4c8a2ce1065012c1a5cfebde",
|
||||
"sha256:eeca775446b00826ec8ca34eed8c902c6a1ae7dc42d78094b1f73ecd38d1dcf8",
|
||||
"sha256:fa476e5f763f59270bda050695e22078b2461dc14bc62a0d27756c5906bca86c",
|
||||
"sha256:fb3269a3641b868ab7856c3cd939782ee1c802fa120b470b597286d2f9e93aee",
|
||||
"sha256:fd289ef26241bc2c0b7b047417a33786790bf2a01f6c92d9688eb2693c9116df"
|
||||
],
|
||||
"markers": "python_version >= '3.9'",
|
||||
"version": "==6.2.0"
|
||||
"version": "==6.3.0"
|
||||
},
|
||||
"mutagen": {
|
||||
"hashes": [
|
||||
|
|
@ -732,107 +732,107 @@
|
|||
},
|
||||
"propcache": {
|
||||
"hashes": [
|
||||
"sha256:02df07041e0820cacc8f739510078f2aadcfd3fc57eaeeb16d5ded85c872c89e",
|
||||
"sha256:03acd9ff19021bd0567582ac88f821b66883e158274183b9e5586f678984f8fe",
|
||||
"sha256:03c091bb752349402f23ee43bb2bff6bd80ccab7c9df6b88ad4322258d6960fc",
|
||||
"sha256:07700939b2cbd67bfb3b76a12e1412405d71019df00ca5697ce75e5ef789d829",
|
||||
"sha256:0c3e893c4464ebd751b44ae76c12c5f5c1e4f6cbd6fbf67e3783cd93ad221863",
|
||||
"sha256:119e244ab40f70a98c91906d4c1f4c5f2e68bd0b14e7ab0a06922038fae8a20f",
|
||||
"sha256:11ae6a8a01b8a4dc79093b5d3ca2c8a4436f5ee251a9840d7790dccbd96cb649",
|
||||
"sha256:15010f29fbed80e711db272909a074dc79858c6d28e2915704cfc487a8ac89c6",
|
||||
"sha256:19d36bb351ad5554ff20f2ae75f88ce205b0748c38b146c75628577020351e3c",
|
||||
"sha256:1c8f7d896a16da9455f882870a507567d4f58c53504dc2d4b1e1d386dfe4588a",
|
||||
"sha256:2383a17385d9800b6eb5855c2f05ee550f803878f344f58b6e194de08b96352c",
|
||||
"sha256:24c04f8fbf60094c531667b8207acbae54146661657a1b1be6d3ca7773b7a545",
|
||||
"sha256:2578541776769b500bada3f8a4eeaf944530516b6e90c089aa368266ed70c49e",
|
||||
"sha256:26a67e5c04e3119594d8cfae517f4b9330c395df07ea65eab16f3d559b7068fe",
|
||||
"sha256:2b975528998de037dfbc10144b8aed9b8dd5a99ec547f14d1cb7c5665a43f075",
|
||||
"sha256:2d15bc27163cd4df433e75f546b9ac31c1ba7b0b128bfb1b90df19082466ff57",
|
||||
"sha256:2d913d36bdaf368637b4f88d554fb9cb9d53d6920b9c5563846555938d5450bf",
|
||||
"sha256:3302c5287e504d23bb0e64d2a921d1eb4a03fb93a0a0aa3b53de059f5a5d737d",
|
||||
"sha256:36ca5e9a21822cc1746023e88f5c0af6fce3af3b85d4520efb1ce4221bed75cc",
|
||||
"sha256:3b812b3cb6caacd072276ac0492d249f210006c57726b6484a1e1805b3cfeea0",
|
||||
"sha256:3c6ec957025bf32b15cbc6b67afe233c65b30005e4c55fe5768e4bb518d712f1",
|
||||
"sha256:41de3da5458edd5678b0f6ff66691507f9885f5fe6a0fb99a5d10d10c0fd2d64",
|
||||
"sha256:42924dc0c9d73e49908e35bbdec87adedd651ea24c53c29cac103ede0ea1d340",
|
||||
"sha256:4544699674faf66fb6b4473a1518ae4999c1b614f0b8297b1cef96bac25381db",
|
||||
"sha256:46ed02532cb66612d42ae5c3929b5e98ae330ea0f3900bc66ec5f4862069519b",
|
||||
"sha256:49ea05212a529c2caffe411e25a59308b07d6e10bf2505d77da72891f9a05641",
|
||||
"sha256:4fa0e7c9c3cf7c276d4f6ab9af8adddc127d04e0fcabede315904d2ff76db626",
|
||||
"sha256:507c5357a8d8b4593b97fb669c50598f4e6cccbbf77e22fa9598aba78292b4d7",
|
||||
"sha256:549722908de62aa0b47a78b90531c022fa6e139f9166be634f667ff45632cc92",
|
||||
"sha256:58e6d2a5a7cb3e5f166fd58e71e9a4ff504be9dc61b88167e75f835da5764d07",
|
||||
"sha256:5a16167118677d94bb48bfcd91e420088854eb0737b76ec374b91498fb77a70e",
|
||||
"sha256:5d62c4f6706bff5d8a52fd51fec6069bef69e7202ed481486c0bc3874912c787",
|
||||
"sha256:5fa159dcee5dba00c1def3231c249cf261185189205073bde13797e57dd7540a",
|
||||
"sha256:6032231d4a5abd67c7f71168fd64a47b6b451fbcb91c8397c2f7610e67683810",
|
||||
"sha256:63f26258a163c34542c24808f03d734b338da66ba91f410a703e505c8485791d",
|
||||
"sha256:65a37714b8ad9aba5780325228598a5b16c47ba0f8aeb3dc0514701e4413d7c0",
|
||||
"sha256:67054e47c01b7b349b94ed0840ccae075449503cf1fdd0a1fdd98ab5ddc2667b",
|
||||
"sha256:67dda3c7325691c2081510e92c561f465ba61b975f481735aefdfc845d2cd043",
|
||||
"sha256:6985a593417cdbc94c7f9c3403747335e450c1599da1647a5af76539672464d3",
|
||||
"sha256:6a1948df1bb1d56b5e7b0553c0fa04fd0e320997ae99689488201f19fa90d2e7",
|
||||
"sha256:6b5b7fd6ee7b54e01759f2044f936dcf7dea6e7585f35490f7ca0420fe723c0d",
|
||||
"sha256:6c929916cbdb540d3407c66f19f73387f43e7c12fa318a66f64ac99da601bcdf",
|
||||
"sha256:6f4d7a7c0aff92e8354cceca6fe223973ddf08401047920df0fcb24be2bd5138",
|
||||
"sha256:728af36011bb5d344c4fe4af79cfe186729efb649d2f8b395d1572fb088a996c",
|
||||
"sha256:742840d1d0438eb7ea4280f3347598f507a199a35a08294afdcc560c3739989d",
|
||||
"sha256:75e872573220d1ee2305b35c9813626e620768248425f58798413e9c39741f46",
|
||||
"sha256:794c3dd744fad478b6232289c866c25406ecdfc47e294618bdf1697e69bd64a6",
|
||||
"sha256:7c0fdbdf6983526e269e5a8d53b7ae3622dd6998468821d660d0daf72779aefa",
|
||||
"sha256:7c5f5290799a3f6539cc5e6f474c3e5c5fbeba74a5e1e5be75587746a940d51e",
|
||||
"sha256:7c6e7e4f9167fddc438cd653d826f2222222564daed4116a02a184b464d3ef05",
|
||||
"sha256:7cedd25e5f678f7738da38037435b340694ab34d424938041aa630d8bac42663",
|
||||
"sha256:7e2e068a83552ddf7a39a99488bcba05ac13454fb205c847674da0352602082f",
|
||||
"sha256:8319293e85feadbbfe2150a5659dbc2ebc4afdeaf7d98936fb9a2f2ba0d4c35c",
|
||||
"sha256:8526b0941ec5a40220fc4dfde76aed58808e2b309c03e9fa8e2260083ef7157f",
|
||||
"sha256:8884ba1a0fe7210b775106b25850f5e5a9dc3c840d1ae9924ee6ea2eb3acbfe7",
|
||||
"sha256:8cb625bcb5add899cb8ba7bf716ec1d3e8f7cdea9b0713fa99eadf73b6d4986f",
|
||||
"sha256:8d663fd71491dde7dfdfc899d13a067a94198e90695b4321084c6e450743b8c7",
|
||||
"sha256:8ee1983728964d6070ab443399c476de93d5d741f71e8f6e7880a065f878e0b9",
|
||||
"sha256:997e7b8f173a391987df40f3b52c423e5850be6f6df0dcfb5376365440b56667",
|
||||
"sha256:9be90eebc9842a93ef8335291f57b3b7488ac24f70df96a6034a13cb58e6ff86",
|
||||
"sha256:9ddd49258610499aab83b4f5b61b32e11fce873586282a0e972e5ab3bcadee51",
|
||||
"sha256:9ecde3671e62eeb99e977f5221abcf40c208f69b5eb986b061ccec317c82ebd0",
|
||||
"sha256:9ff4e9ecb6e4b363430edf2c6e50173a63e0820e549918adef70515f87ced19a",
|
||||
"sha256:a254537b9b696ede293bfdbc0a65200e8e4507bc9f37831e2a0318a9b333c85c",
|
||||
"sha256:a2b9bf8c79b660d0ca1ad95e587818c30ccdb11f787657458d6f26a1ea18c568",
|
||||
"sha256:a61a68d630e812b67b5bf097ab84e2cd79b48c792857dc10ba8a223f5b06a2af",
|
||||
"sha256:a7080b0159ce05f179cfac592cda1a82898ca9cd097dacf8ea20ae33474fbb25",
|
||||
"sha256:a8fd93de4e1d278046345f49e2238cdb298589325849b2645d4a94c53faeffc5",
|
||||
"sha256:a94ffc66738da99232ddffcf7910e0f69e2bbe3a0802e54426dbf0714e1c2ffe",
|
||||
"sha256:aa806bbc13eac1ab6291ed21ecd2dd426063ca5417dd507e6be58de20e58dfcf",
|
||||
"sha256:b0c1a133d42c6fc1f5fbcf5c91331657a1ff822e87989bf4a6e2e39b818d0ee9",
|
||||
"sha256:b58229a844931bca61b3a20efd2be2a2acb4ad1622fc026504309a6883686fbf",
|
||||
"sha256:bb2f144c6d98bb5cbc94adeb0447cfd4c0f991341baa68eee3f3b0c9c0e83767",
|
||||
"sha256:be90c94570840939fecedf99fa72839aed70b0ced449b415c85e01ae67422c90",
|
||||
"sha256:bf0d9a171908f32d54f651648c7290397b8792f4303821c42a74e7805bfb813c",
|
||||
"sha256:bf15fc0b45914d9d1b706f7c9c4f66f2b7b053e9517e40123e137e8ca8958b3d",
|
||||
"sha256:bf4298f366ca7e1ad1d21bbb58300a6985015909964077afd37559084590c929",
|
||||
"sha256:c441c841e82c5ba7a85ad25986014be8d7849c3cfbdb6004541873505929a74e",
|
||||
"sha256:cacea77ef7a2195f04f9279297684955e3d1ae4241092ff0cfcef532bb7a1c32",
|
||||
"sha256:cd54895e4ae7d32f1e3dd91261df46ee7483a735017dc6f987904f194aa5fd14",
|
||||
"sha256:d1323cd04d6e92150bcc79d0174ce347ed4b349d748b9358fd2e497b121e03c8",
|
||||
"sha256:d383bf5e045d7f9d239b38e6acadd7b7fdf6c0087259a84ae3475d18e9a2ae8b",
|
||||
"sha256:d3e7420211f5a65a54675fd860ea04173cde60a7cc20ccfbafcccd155225f8bc",
|
||||
"sha256:d8074c5dd61c8a3e915fa8fc04754fa55cfa5978200d2daa1e2d4294c1f136aa",
|
||||
"sha256:df03cd88f95b1b99052b52b1bb92173229d7a674df0ab06d2b25765ee8404bce",
|
||||
"sha256:e45377d5d6fefe1677da2a2c07b024a6dac782088e37c0b1efea4cfe2b1be19b",
|
||||
"sha256:e53d19c2bf7d0d1e6998a7e693c7e87300dd971808e6618964621ccd0e01fe4e",
|
||||
"sha256:e560fd75aaf3e5693b91bcaddd8b314f4d57e99aef8a6c6dc692f935cc1e6bbf",
|
||||
"sha256:ec5060592d83454e8063e487696ac3783cc48c9a329498bafae0d972bc7816c9",
|
||||
"sha256:ecc2920630283e0783c22e2ac94427f8cca29a04cfdf331467d4f661f4072dac",
|
||||
"sha256:ed7161bccab7696a473fe7ddb619c1d75963732b37da4618ba12e60899fefe4f",
|
||||
"sha256:ee0bd3a7b2e184e88d25c9baa6a9dc609ba25b76daae942edfb14499ac7ec374",
|
||||
"sha256:ee25f1ac091def37c4b59d192bbe3a206298feeb89132a470325bf76ad122a1e",
|
||||
"sha256:efa44f64c37cc30c9f05932c740a8b40ce359f51882c70883cc95feac842da4d",
|
||||
"sha256:f47d52fd9b2ac418c4890aad2f6d21a6b96183c98021f0a48497a904199f006e",
|
||||
"sha256:f857034dc68d5ceb30fb60afb6ff2103087aea10a01b613985610e007053a121",
|
||||
"sha256:fb91d20fa2d3b13deea98a690534697742029f4fb83673a3501ae6e3746508b5",
|
||||
"sha256:fddb8870bdb83456a489ab67c6b3040a8d5a55069aa6f72f9d872235fbc52f54"
|
||||
"sha256:050b571b2e96ec942898f8eb46ea4bfbb19bd5502424747e83badc2d4a99a44e",
|
||||
"sha256:05543250deac8e61084234d5fc54f8ebd254e8f2b39a16b1dce48904f45b744b",
|
||||
"sha256:069e7212890b0bcf9b2be0a03afb0c2d5161d91e1bf51569a64f629acc7defbf",
|
||||
"sha256:09400e98545c998d57d10035ff623266927cb784d13dd2b31fd33b8a5316b85b",
|
||||
"sha256:0c3c3a203c375b08fd06a20da3cf7aac293b834b6f4f4db71190e8422750cca5",
|
||||
"sha256:0c86e7ceea56376216eba345aa1fc6a8a6b27ac236181f840d1d7e6a1ea9ba5c",
|
||||
"sha256:0fbe94666e62ebe36cd652f5fc012abfbc2342de99b523f8267a678e4dfdee3c",
|
||||
"sha256:17d1c688a443355234f3c031349da69444be052613483f3e4158eef751abcd8a",
|
||||
"sha256:19a06db789a4bd896ee91ebc50d059e23b3639c25d58eb35be3ca1cbe967c3bf",
|
||||
"sha256:1c5c7ab7f2bb3f573d1cb921993006ba2d39e8621019dffb1c5bc94cdbae81e8",
|
||||
"sha256:1eb34d90aac9bfbced9a58b266f8946cb5935869ff01b164573a7634d39fbcb5",
|
||||
"sha256:1f6cc0ad7b4560e5637eb2c994e97b4fa41ba8226069c9277eb5ea7101845b42",
|
||||
"sha256:27c6ac6aa9fc7bc662f594ef380707494cb42c22786a558d95fcdedb9aa5d035",
|
||||
"sha256:2d219b0dbabe75e15e581fc1ae796109b07c8ba7d25b9ae8d650da582bed01b0",
|
||||
"sha256:2fce1df66915909ff6c824bbb5eb403d2d15f98f1518e583074671a30fe0c21e",
|
||||
"sha256:319fa8765bfd6a265e5fa661547556da381e53274bc05094fc9ea50da51bfd46",
|
||||
"sha256:359e81a949a7619802eb601d66d37072b79b79c2505e6d3fd8b945538411400d",
|
||||
"sha256:3a02a28095b5e63128bcae98eb59025924f121f048a62393db682f049bf4ac24",
|
||||
"sha256:3e19ea4ea0bf46179f8a3652ac1426e6dcbaf577ce4b4f65be581e237340420d",
|
||||
"sha256:3e584b6d388aeb0001d6d5c2bd86b26304adde6d9bb9bfa9c4889805021b96de",
|
||||
"sha256:40d980c33765359098837527e18eddefc9a24cea5b45e078a7f3bb5b032c6ecf",
|
||||
"sha256:4114c4ada8f3181af20808bedb250da6bae56660e4b8dfd9cd95d4549c0962f7",
|
||||
"sha256:43593c6772aa12abc3af7784bff4a41ffa921608dd38b77cf1dfd7f5c4e71371",
|
||||
"sha256:47ef24aa6511e388e9894ec16f0fbf3313a53ee68402bc428744a367ec55b833",
|
||||
"sha256:4cf9e93a81979f1424f1a3d155213dc928f1069d697e4353edb8a5eba67c6259",
|
||||
"sha256:4d0dfdd9a2ebc77b869a0b04423591ea8823f791293b527dc1bb896c1d6f1136",
|
||||
"sha256:563f9d8c03ad645597b8d010ef4e9eab359faeb11a0a2ac9f7b4bc8c28ebef25",
|
||||
"sha256:58aa11f4ca8b60113d4b8e32d37e7e78bd8af4d1a5b5cb4979ed856a45e62005",
|
||||
"sha256:5a0a9898fdb99bf11786265468571e628ba60af80dc3f6eb89a3545540c6b0ef",
|
||||
"sha256:5aed8d8308215089c0734a2af4f2e95eeb360660184ad3912686c181e500b2e7",
|
||||
"sha256:5b9145c35cc87313b5fd480144f8078716007656093d23059e8993d3a8fa730f",
|
||||
"sha256:5cb5918253912e088edbf023788de539219718d3b10aef334476b62d2b53de53",
|
||||
"sha256:5cdb0f3e1eb6dfc9965d19734d8f9c481b294b5274337a8cb5cb01b462dcb7e0",
|
||||
"sha256:5ced33d827625d0a589e831126ccb4f5c29dfdf6766cac441d23995a65825dcb",
|
||||
"sha256:603f1fe4144420374f1a69b907494c3acbc867a581c2d49d4175b0de7cc64566",
|
||||
"sha256:61014615c1274df8da5991a1e5da85a3ccb00c2d4701ac6f3383afd3ca47ab0a",
|
||||
"sha256:64a956dff37080b352c1c40b2966b09defb014347043e740d420ca1eb7c9b908",
|
||||
"sha256:668ddddc9f3075af019f784456267eb504cb77c2c4bd46cc8402d723b4d200bf",
|
||||
"sha256:6d8e309ff9a0503ef70dc9a0ebd3e69cf7b3894c9ae2ae81fc10943c37762458",
|
||||
"sha256:6f173bbfe976105aaa890b712d1759de339d8a7cef2fc0a1714cc1a1e1c47f64",
|
||||
"sha256:71ebe3fe42656a2328ab08933d420df5f3ab121772eef78f2dc63624157f0ed9",
|
||||
"sha256:730178f476ef03d3d4d255f0c9fa186cb1d13fd33ffe89d39f2cda4da90ceb71",
|
||||
"sha256:7d2d5a0028d920738372630870e7d9644ce437142197f8c827194fca404bf03b",
|
||||
"sha256:7f30241577d2fef2602113b70ef7231bf4c69a97e04693bde08ddab913ba0ce5",
|
||||
"sha256:813fbb8b6aea2fc9659815e585e548fe706d6f663fa73dff59a1677d4595a037",
|
||||
"sha256:82de5da8c8893056603ac2d6a89eb8b4df49abf1a7c19d536984c8dd63f481d5",
|
||||
"sha256:83be47aa4e35b87c106fc0c84c0fc069d3f9b9b06d3c494cd404ec6747544894",
|
||||
"sha256:8638f99dca15b9dff328fb6273e09f03d1c50d9b6512f3b65a4154588a7595fe",
|
||||
"sha256:87380fb1f3089d2a0b8b00f006ed12bd41bd858fabfa7330c954c70f50ed8757",
|
||||
"sha256:88c423efef9d7a59dae0614eaed718449c09a5ac79a5f224a8b9664d603f04a3",
|
||||
"sha256:89498dd49c2f9a026ee057965cdf8192e5ae070ce7d7a7bd4b66a8e257d0c976",
|
||||
"sha256:8a17583515a04358b034e241f952f1715243482fc2c2945fd99a1b03a0bd77d6",
|
||||
"sha256:916cd229b0150129d645ec51614d38129ee74c03293a9f3f17537be0029a9641",
|
||||
"sha256:9532ea0b26a401264b1365146c440a6d78269ed41f83f23818d4b79497aeabe7",
|
||||
"sha256:967a8eec513dbe08330f10137eacb427b2ca52118769e82ebcfcab0fba92a649",
|
||||
"sha256:975af16f406ce48f1333ec5e912fe11064605d5c5b3f6746969077cc3adeb120",
|
||||
"sha256:9979643ffc69b799d50d3a7b72b5164a2e97e117009d7af6dfdd2ab906cb72cd",
|
||||
"sha256:9a8ecf38de50a7f518c21568c80f985e776397b902f1ce0b01f799aba1608b40",
|
||||
"sha256:9cec3239c85ed15bfaded997773fdad9fb5662b0a7cbc854a43f291eb183179e",
|
||||
"sha256:9e64e948ab41411958670f1093c0a57acfdc3bee5cf5b935671bbd5313bcf229",
|
||||
"sha256:9f64d91b751df77931336b5ff7bafbe8845c5770b06630e27acd5dbb71e1931c",
|
||||
"sha256:a0ab8cf8cdd2194f8ff979a43ab43049b1df0b37aa64ab7eca04ac14429baeb7",
|
||||
"sha256:a110205022d077da24e60b3df8bcee73971be9575dec5573dd17ae5d81751111",
|
||||
"sha256:a34aa3a1abc50740be6ac0ab9d594e274f59960d3ad253cd318af76b996dd654",
|
||||
"sha256:a444192f20f5ce8a5e52761a031b90f5ea6288b1eef42ad4c7e64fef33540b8f",
|
||||
"sha256:a461959ead5b38e2581998700b26346b78cd98540b5524796c175722f18b0294",
|
||||
"sha256:a75801768bbe65499495660b777e018cbe90c7980f07f8aa57d6be79ea6f71da",
|
||||
"sha256:aa8efd8c5adc5a2c9d3b952815ff8f7710cefdcaf5f2c36d26aff51aeca2f12f",
|
||||
"sha256:aca63103895c7d960a5b9b044a83f544b233c95e0dcff114389d64d762017af7",
|
||||
"sha256:b0313e8b923b3814d1c4a524c93dfecea5f39fa95601f6a9b1ac96cd66f89ea0",
|
||||
"sha256:b23c11c2c9e6d4e7300c92e022046ad09b91fd00e36e83c44483df4afa990073",
|
||||
"sha256:b303b194c2e6f171cfddf8b8ba30baefccf03d36a4d9cab7fd0bb68ba476a3d7",
|
||||
"sha256:b655032b202028a582d27aeedc2e813299f82cb232f969f87a4fde491a233f11",
|
||||
"sha256:bd39c92e4c8f6cbf5f08257d6360123af72af9f4da75a690bef50da77362d25f",
|
||||
"sha256:bef100c88d8692864651b5f98e871fb090bd65c8a41a1cb0ff2322db39c96c27",
|
||||
"sha256:c2fe5c910f6007e716a06d269608d307b4f36e7babee5f36533722660e8c4a70",
|
||||
"sha256:c66d8ccbc902ad548312b96ed8d5d266d0d2c6d006fd0f66323e9d8f2dd49be7",
|
||||
"sha256:cd6a55f65241c551eb53f8cf4d2f4af33512c39da5d9777694e9d9c60872f519",
|
||||
"sha256:d249609e547c04d190e820d0d4c8ca03ed4582bcf8e4e160a6969ddfb57b62e5",
|
||||
"sha256:d4e89cde74154c7b5957f87a355bb9c8ec929c167b59c83d90654ea36aeb6180",
|
||||
"sha256:dc1915ec523b3b494933b5424980831b636fe483d7d543f7afb7b3bf00f0c10f",
|
||||
"sha256:e1c4d24b804b3a87e9350f79e2371a705a188d292fd310e663483af6ee6718ee",
|
||||
"sha256:e474fc718e73ba5ec5180358aa07f6aded0ff5f2abe700e3115c37d75c947e18",
|
||||
"sha256:e4fe2a6d5ce975c117a6bb1e8ccda772d1e7029c1cca1acd209f91d30fa72815",
|
||||
"sha256:e7fb9a84c9abbf2b2683fa3e7b0d7da4d8ecf139a1c635732a8bda29c5214b0e",
|
||||
"sha256:e861ad82892408487be144906a368ddbe2dc6297074ade2d892341b35c59844a",
|
||||
"sha256:ec314cde7314d2dd0510c6787326bbffcbdc317ecee6b7401ce218b3099075a7",
|
||||
"sha256:ed5f6d2edbf349bd8d630e81f474d33d6ae5d07760c44d33cd808e2f5c8f4ae6",
|
||||
"sha256:ef2e4e91fb3945769e14ce82ed53007195e616a63aa43b40fb7ebaaf907c8d4c",
|
||||
"sha256:f011f104db880f4e2166bcdcf7f58250f7a465bc6b068dc84c824a3d4a5c94dc",
|
||||
"sha256:f1528ec4374617a7a753f90f20e2f551121bb558fcb35926f99e3c42367164b8",
|
||||
"sha256:f27785888d2fdd918bc36de8b8739f2d6c791399552333721b58193f68ea3e98",
|
||||
"sha256:f35c7070eeec2cdaac6fd3fe245226ed2a6292d3ee8c938e5bb645b434c5f256",
|
||||
"sha256:f3bbecd2f34d0e6d3c543fdb3b15d6b60dd69970c2b4c822379e5ec8f6f621d5",
|
||||
"sha256:f6f1324db48f001c2ca26a25fa25af60711e09b9aaf4b28488602776f4f9a744",
|
||||
"sha256:f78eb8422acc93d7b69964012ad7048764bb45a54ba7a39bb9e146c72ea29723",
|
||||
"sha256:fb6e0faf8cb6b4beea5d6ed7b5a578254c6d7df54c36ccd3d8b3eb00d6770277",
|
||||
"sha256:feccd282de1f6322f56f6845bf1207a537227812f0a9bf5571df52bb418d79d5"
|
||||
],
|
||||
"markers": "python_version >= '3.9'",
|
||||
"version": "==0.3.0"
|
||||
"version": "==0.3.1"
|
||||
},
|
||||
"pycparser": {
|
||||
"hashes": [
|
||||
|
|
@ -1187,11 +1187,11 @@
|
|||
},
|
||||
"typing-extensions": {
|
||||
"hashes": [
|
||||
"sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d",
|
||||
"sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"
|
||||
"sha256:0a4ac55a5820789d87e297727d229866c9650f6521b64206413c4fbada24d95b",
|
||||
"sha256:c8dd92cc0d6425a97c18fbb9d1954e5ff92c1ca881a309c45f06ebc0b79058e5"
|
||||
],
|
||||
"markers": "python_version < '3.13'",
|
||||
"version": "==4.12.2"
|
||||
"version": "==4.13.0"
|
||||
},
|
||||
"tzlocal": {
|
||||
"hashes": [
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ from sqlite3 import Connection
|
|||
from .config import Config
|
||||
from .Download import Download
|
||||
from .ItemDTO import ItemDTO
|
||||
from .Utils import clean_item
|
||||
|
||||
|
||||
class DataStore:
|
||||
|
|
@ -68,7 +69,7 @@ class DataStore:
|
|||
|
||||
for row in cursor:
|
||||
rowDate = datetime.strptime(row["created_at"], "%Y-%m-%d %H:%M:%S") # noqa: DTZ007
|
||||
data: dict = json.loads(row["data"])
|
||||
data, _ = clean_item(json.loads(row["data"]), keys=ItemDTO.removed_fields())
|
||||
key: str = data.pop("_id")
|
||||
item: ItemDTO = ItemDTO(**data)
|
||||
item._id = key
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@ class Download:
|
|||
temp_dir: str = None
|
||||
template: str = None
|
||||
template_chapter: str = None
|
||||
ytdl_opts: dict = None
|
||||
info: ItemDTO = None
|
||||
default_ytdl_opts: dict = None
|
||||
debug: bool = False
|
||||
|
|
@ -92,7 +91,6 @@ class Download:
|
|||
self.template = info.template
|
||||
self.template_chapter = info.template_chapter
|
||||
self.preset = info.preset
|
||||
self.ytdl_opts = info.config if info.config else {}
|
||||
self.info = info
|
||||
self.id = info._id
|
||||
self.default_ytdl_opts = config.ytdl_options
|
||||
|
|
@ -147,7 +145,7 @@ class Download:
|
|||
YTDLPOpts.get_instance()
|
||||
.preset(self.preset)
|
||||
.add({"break_on_existing": True})
|
||||
.add(self.ytdl_opts, from_user=True)
|
||||
.add_cli(self.info.cli, from_user=True)
|
||||
.add(
|
||||
{
|
||||
"color": "no_color",
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import asyncio
|
||||
import functools
|
||||
import json
|
||||
import glob
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
import uuid
|
||||
from datetime import UTC, datetime
|
||||
from email.utils import formatdate
|
||||
from pathlib import Path
|
||||
from sqlite3 import Connection
|
||||
|
||||
import anyio
|
||||
|
|
@ -18,7 +19,7 @@ from .config import Config
|
|||
from .DataStore import DataStore
|
||||
from .Download import Download
|
||||
from .Events import EventBus, Events
|
||||
from .ItemDTO import ItemDTO
|
||||
from .ItemDTO import Item, ItemDTO
|
||||
from .Presets import Presets
|
||||
from .Singleton import Singleton
|
||||
from .Utils import arg_converter, calc_download_path, extract_info, is_downloaded
|
||||
|
|
@ -168,45 +169,23 @@ class DownloadQueue(metaclass=Singleton):
|
|||
except Exception as e:
|
||||
LOG.error(f"Failed to cancel downloads. {e!s}")
|
||||
|
||||
async def __add_entry(
|
||||
self,
|
||||
entry: dict,
|
||||
preset: str,
|
||||
folder: str,
|
||||
config: dict | None = None,
|
||||
cookies: str = "",
|
||||
template: str = "",
|
||||
extras: dict | None = None,
|
||||
cli: str = "",
|
||||
already=None,
|
||||
):
|
||||
async def __add_entry(self, entry: dict, item: Item, already=None):
|
||||
"""
|
||||
Add an entry to the download queue.
|
||||
|
||||
Args:
|
||||
entry (dict): The entry to add to the download queue.
|
||||
preset (str): The preset to use for the download.
|
||||
folder (str): The folder to save the download to.
|
||||
config (dict): The yt-dlp configuration to use for the download.
|
||||
cookies (str): The cookies to use for the download.
|
||||
template (str): The output template to use for the download.
|
||||
extras (dict): The extra information to add to the download.
|
||||
cli (str): The yt-dlp command line options to use for the download.
|
||||
item (Item): The item to add to the download queue.
|
||||
already (set): The set of already downloaded items.
|
||||
|
||||
Returns:
|
||||
dict: The status of the operation.
|
||||
|
||||
"""
|
||||
if not config:
|
||||
config = {}
|
||||
|
||||
if not extras:
|
||||
extras = {}
|
||||
else:
|
||||
for key in extras.copy():
|
||||
if item.has_extras():
|
||||
for key in item.extras.copy():
|
||||
if not str(key).startswith("playlist"):
|
||||
extras.pop(key, None)
|
||||
item.extras.pop(key, None)
|
||||
|
||||
if not entry:
|
||||
return {"status": "error", "msg": "Invalid/empty data was given."}
|
||||
|
|
@ -239,13 +218,7 @@ class DownloadQueue(metaclass=Singleton):
|
|||
results.append(
|
||||
await self.__add_entry(
|
||||
entry=etr,
|
||||
preset=preset,
|
||||
folder=folder,
|
||||
config=config,
|
||||
cookies=cookies,
|
||||
template=template,
|
||||
extras=extras,
|
||||
cli=cli,
|
||||
item=item.new_with(url=etr.get("url") or etr.get("webpage_url")),
|
||||
already=already,
|
||||
)
|
||||
)
|
||||
|
|
@ -291,7 +264,7 @@ class DownloadQueue(metaclass=Singleton):
|
|||
is_live = bool(entry.get("is_live") or live_in or entry.get("live_status") in live_status)
|
||||
|
||||
try:
|
||||
download_dir = calc_download_path(base_path=self.config.download_path, folder=folder)
|
||||
download_dir = calc_download_path(base_path=self.config.download_path, folder=item.folder)
|
||||
except Exception as e:
|
||||
LOG.exception(e)
|
||||
return {"status": "error", "msg": str(e)}
|
||||
|
|
@ -299,31 +272,30 @@ class DownloadQueue(metaclass=Singleton):
|
|||
fields: tuple = ("uploader", "channel", "thumbnail")
|
||||
for field in fields:
|
||||
if entry.get(field):
|
||||
extras[field] = entry.get(field)
|
||||
item.extras[field] = entry.get(field)
|
||||
|
||||
for key in entry:
|
||||
if isinstance(key, str) and key.startswith("playlist") and entry.get(key):
|
||||
extras[key] = entry.get(key)
|
||||
item.extras[key] = entry.get(key)
|
||||
|
||||
dl = ItemDTO(
|
||||
id=str(entry.get("id")),
|
||||
title=str(entry.get("title")),
|
||||
url=str(entry.get("webpage_url") or entry.get("url")),
|
||||
preset=preset,
|
||||
folder=folder,
|
||||
preset=item.preset,
|
||||
folder=item.folder,
|
||||
download_dir=download_dir,
|
||||
temp_dir=self.config.temp_path,
|
||||
cookies=cookies,
|
||||
config=config,
|
||||
template=template if template else self.config.output_template,
|
||||
cookies=item.cookies,
|
||||
template=item.template if item.template else self.config.output_template,
|
||||
template_chapter=self.config.output_template_chapter,
|
||||
datetime=formatdate(time.time()),
|
||||
error=error,
|
||||
is_live=is_live,
|
||||
live_in=live_in,
|
||||
options=options,
|
||||
cli=cli,
|
||||
extras=extras,
|
||||
cli=item.cli,
|
||||
extras=item.extras,
|
||||
)
|
||||
|
||||
dlInfo: Download = Download(info=dl, info_dict=entry)
|
||||
|
|
@ -347,44 +319,16 @@ class DownloadQueue(metaclass=Singleton):
|
|||
return {"status": "ok"}
|
||||
|
||||
if eventType.startswith("url"):
|
||||
return await self.add(
|
||||
url=str(entry.get("url")),
|
||||
preset=preset,
|
||||
folder=folder,
|
||||
config=config,
|
||||
cookies=cookies,
|
||||
template=template,
|
||||
extras=extras,
|
||||
cli=cli,
|
||||
already=already,
|
||||
)
|
||||
return await self.add(item=item.new_with(url=entry.get("url")), already=already)
|
||||
|
||||
return {"status": "error", "msg": f'Unsupported resource "{eventType}"'}
|
||||
|
||||
async def add(
|
||||
self,
|
||||
url: str,
|
||||
preset: str,
|
||||
folder: str,
|
||||
config: dict | None = None,
|
||||
cookies: str = "",
|
||||
template: str = "",
|
||||
cli: str = "",
|
||||
extras: dict | None = None,
|
||||
already=None,
|
||||
):
|
||||
async def add(self, item: Item, already: set | None = None):
|
||||
"""
|
||||
Add an item to the download queue.
|
||||
|
||||
Args:
|
||||
url (str): The url to be added to the queue.
|
||||
preset (str): The preset to be used for the download.
|
||||
folder (str): The folder to save the download to.
|
||||
config (dict): The yt-dlp config to be used for the download.
|
||||
cookies (str): The cookies to be used for the download.
|
||||
template (str): The template to be used for the download.
|
||||
cli (str): The yt-dlp cli options to be used for the download.
|
||||
extras (dict): Extra data to be added to the download
|
||||
item (Item): The item to be added to the queue.
|
||||
already (set): Set of already downloaded items.
|
||||
|
||||
Returns:
|
||||
|
|
@ -392,62 +336,47 @@ class DownloadQueue(metaclass=Singleton):
|
|||
{ "status": "text" }
|
||||
|
||||
"""
|
||||
_preset = Presets.get_instance().get(name=preset)
|
||||
_preset = Presets.get_instance().get(item.preset)
|
||||
|
||||
config = config if config else {}
|
||||
if cli:
|
||||
if item.has_cli():
|
||||
try:
|
||||
config = arg_converter(args=cli, level=True)
|
||||
arg_converter(args=item.cli, level=True)
|
||||
except Exception as e:
|
||||
LOG.error(f"Invalid cli options '{cli}'. {e!s}")
|
||||
return {"status": "error", "msg": f"Invalid cli options '{cli}'. {e!s}"}
|
||||
|
||||
folder = str(folder) if folder else ""
|
||||
if not extras:
|
||||
extras = {}
|
||||
LOG.error(f"Invalid cli options '{item.cli}'. {e!s}")
|
||||
return {"status": "error", "msg": f"Invalid cli options '{item.cli}'. {e!s}"}
|
||||
|
||||
if _preset:
|
||||
if _preset.folder and not folder:
|
||||
folder = _preset.folder
|
||||
if _preset.folder and not item.folder:
|
||||
item.folder = _preset.folder
|
||||
|
||||
if _preset.template and not template:
|
||||
template = _preset.template
|
||||
if _preset.template and not item.template:
|
||||
item.template = _preset.template
|
||||
|
||||
if _preset.cookies and not cookies:
|
||||
cookies = _preset.cookies
|
||||
if _preset.cookies and not item.cookies:
|
||||
item.cookies = _preset.cookies
|
||||
|
||||
filePath = calc_download_path(base_path=self.config.download_path, folder=folder)
|
||||
yt_conf = {}
|
||||
cookie_file = os.path.join(self.config.temp_path, f"c_{uuid.uuid4().hex}.txt")
|
||||
|
||||
LOG.info(
|
||||
f"Adding 'URL: {url}' to 'Folder: {filePath}' with 'Preset: {preset}' 'Naming: {template}', 'Cookies: {len(cookies)}/chars' 'CLI: {cli}' 'Extras: {extras}'."
|
||||
)
|
||||
|
||||
if isinstance(config, str):
|
||||
try:
|
||||
config = json.loads(config)
|
||||
except Exception as e:
|
||||
LOG.error(f"Unable to load '{config=}'. {e!s}")
|
||||
return {"status": "error", "msg": f"Failed to parse json yt-dlp config. {e!s}"}
|
||||
LOG.info(f"Adding '{item.__repr__()}'.")
|
||||
|
||||
already = set() if already is None else already
|
||||
|
||||
if url in already:
|
||||
LOG.warning(f"Recursion detected with url '{url}' skipping.")
|
||||
if item.url in already:
|
||||
LOG.warning(f"Recursion detected with url '{item.url}' skipping.")
|
||||
return {"status": "ok"}
|
||||
|
||||
already.add(url)
|
||||
already.add(item.url)
|
||||
|
||||
try:
|
||||
downloaded, id_dict = self._is_downloaded(url)
|
||||
downloaded, id_dict = self._is_downloaded(item.url)
|
||||
if downloaded is True and id_dict:
|
||||
message = f"This url with ID '{id_dict.get('id')}' has been downloaded already and recorded in archive."
|
||||
LOG.info(message)
|
||||
return {"status": "error", "msg": message}
|
||||
|
||||
started = time.perf_counter()
|
||||
LOG.debug(f"extract_info: checking {url=}")
|
||||
LOG.debug(f"extract_info: checking '{item.url}'.")
|
||||
|
||||
logs = []
|
||||
|
||||
|
|
@ -456,16 +385,18 @@ class DownloadQueue(metaclass=Singleton):
|
|||
"func": lambda _, msg: logs.append(msg),
|
||||
"level": logging.WARNING,
|
||||
},
|
||||
**YTDLPOpts.get_instance().preset(name=preset).add(config=config, from_user=True).get_all(),
|
||||
**YTDLPOpts.get_instance().preset(name=item.preset).add_cli(args=item.cli, from_user=True).get_all(),
|
||||
}
|
||||
|
||||
if cookies:
|
||||
if item.cookies:
|
||||
try:
|
||||
async with await anyio.open_file(cookie_file, "w") as f:
|
||||
await f.write(cookies)
|
||||
await f.write(item.cookies)
|
||||
yt_conf["cookiefile"] = f.name
|
||||
except ValueError as e:
|
||||
LOG.error(f"Failed to create cookie file for '{self.info.id}: {self.info.title}'. '{e!s}'.")
|
||||
msg = f"Failed to create cookie file for '{self.info.id}: {self.info.title}'. '{e!s}'."
|
||||
LOG.error(msg)
|
||||
return {"status": "error", "msg": msg}
|
||||
|
||||
entry = await asyncio.wait_for(
|
||||
fut=asyncio.get_running_loop().run_in_executor(
|
||||
|
|
@ -473,7 +404,7 @@ class DownloadQueue(metaclass=Singleton):
|
|||
functools.partial(
|
||||
extract_info,
|
||||
config=yt_conf,
|
||||
url=url,
|
||||
url=item.url,
|
||||
debug=bool(self.config.ytdl_debug),
|
||||
no_archive=False,
|
||||
follow_redirect=True,
|
||||
|
|
@ -486,7 +417,7 @@ class DownloadQueue(metaclass=Singleton):
|
|||
return {"status": "error", "msg": "Unable to extract info." + "\n".join(logs)}
|
||||
|
||||
LOG.debug(
|
||||
f"extract_info: for 'URL: {url}' is done in '{time.perf_counter() - started}'. Length: '{len(entry)}'."
|
||||
f"extract_info: for 'URL: {item.url}' is done in '{time.perf_counter() - started}'. Length: '{len(entry)}/keys'."
|
||||
)
|
||||
except yt_dlp.utils.ExistingVideoReached as exc:
|
||||
LOG.error(f"Video has been downloaded already and recorded in archive.log file. '{exc!s}'.")
|
||||
|
|
@ -508,17 +439,7 @@ class DownloadQueue(metaclass=Singleton):
|
|||
except Exception as e:
|
||||
LOG.error(f"Failed to remove cookie file '{yt_conf['cookiefile']}'. {e!s}")
|
||||
|
||||
return await self.__add_entry(
|
||||
entry=entry,
|
||||
preset=preset,
|
||||
folder=folder,
|
||||
config=config,
|
||||
cookies=cookies,
|
||||
template=template,
|
||||
already=already,
|
||||
extras=extras,
|
||||
cli=cli,
|
||||
)
|
||||
return await self.__add_entry(entry=entry, item=item, already=already)
|
||||
|
||||
async def cancel(self, ids: list[str]) -> dict[str, str]:
|
||||
"""
|
||||
|
|
@ -588,7 +509,7 @@ class DownloadQueue(metaclass=Singleton):
|
|||
continue
|
||||
|
||||
itemRef: str = f"{id=} {item.info.id=} {item.info.title=}"
|
||||
fileDeleted: bool = False
|
||||
removed_files = 0
|
||||
filename: str = ""
|
||||
|
||||
if remove_file and self.config.remove_files and "finished" == item.info.status:
|
||||
|
|
@ -602,9 +523,13 @@ class DownloadQueue(metaclass=Singleton):
|
|||
folder=filename,
|
||||
create_path=False,
|
||||
)
|
||||
if realFile and os.path.exists(realFile):
|
||||
os.remove(realFile)
|
||||
fileDeleted = True
|
||||
rf = Path(realFile)
|
||||
if rf.is_file() and rf.exists():
|
||||
for f in rf.parent.glob(f"{glob.escape(rf.stem)}.*"):
|
||||
if f.is_file() and f.exists() and not f.name.startswith("."):
|
||||
removed_files += 1
|
||||
LOG.debug(f"Removing '{itemRef}' local file '{f.name}'.")
|
||||
os.remove(f)
|
||||
else:
|
||||
LOG.warning(f"Failed to remove '{itemRef}' local file '{filename}'. File not found.")
|
||||
except Exception as e:
|
||||
|
|
@ -614,8 +539,8 @@ class DownloadQueue(metaclass=Singleton):
|
|||
await self._notify.emit(Events.CLEARED, data=item.info.serialize())
|
||||
|
||||
msg = f"Deleted completed download '{itemRef}'."
|
||||
if fileDeleted and filename:
|
||||
msg += f" and removed local file '{filename}'."
|
||||
if removed_files > 0:
|
||||
msg += f" and removed '{removed_files}' local files."
|
||||
|
||||
LOG.info(msg=msg)
|
||||
status[id] = "ok"
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ from .DownloadQueue import DownloadQueue
|
|||
from .encoder import Encoder
|
||||
from .Events import EventBus, Events, message
|
||||
from .ffprobe import ffprobe
|
||||
from .ItemDTO import Item
|
||||
from .M3u8 import M3u8
|
||||
from .Notifications import Notification, NotificationEvents
|
||||
from .Playlist import Playlist
|
||||
|
|
@ -475,7 +476,8 @@ class HttpAPI(Common):
|
|||
err = err.split("\n")[-1] if "\n" in err else err
|
||||
err = err.replace("main.py: error: ", "").strip().capitalize()
|
||||
return web.json_response(
|
||||
data={"error": f"Failed to command line arguments for yt-dlp. '{err}'."}, status=web.HTTPBadRequest.status_code
|
||||
data={"error": f"Failed to parse command arguments for yt-dlp. '{err}'."},
|
||||
status=web.HTTPBadRequest.status_code,
|
||||
)
|
||||
|
||||
@route("GET", "api/yt-dlp/url/info")
|
||||
|
|
@ -501,7 +503,7 @@ class HttpAPI(Common):
|
|||
|
||||
preset = request.query.get("preset")
|
||||
if preset:
|
||||
exists = Presets.get_instance().get(name=preset)
|
||||
exists = Presets.get_instance().get(preset)
|
||||
if not exists:
|
||||
return web.json_response(
|
||||
data={"status": False, "message": f"Preset '{preset}' does not exist."},
|
||||
|
|
@ -674,7 +676,7 @@ class HttpAPI(Common):
|
|||
|
||||
preset = request.query.get("preset")
|
||||
if preset:
|
||||
exists = Presets.get_instance().get(name=preset)
|
||||
exists = Presets.get_instance().get(preset)
|
||||
if not exists:
|
||||
return web.json_response(
|
||||
data={"status": False, "message": f"Preset '{preset}' does not exist."},
|
||||
|
|
@ -683,7 +685,7 @@ class HttpAPI(Common):
|
|||
data["preset"] = preset
|
||||
|
||||
try:
|
||||
status = await self.add(**self.format_item(data))
|
||||
status = await self.add(item=Item.format(data))
|
||||
except ValueError as e:
|
||||
return web.json_response(data={"status": False, "message": str(e)}, status=web.HTTPBadRequest.status_code)
|
||||
|
||||
|
|
@ -710,15 +712,16 @@ class HttpAPI(Common):
|
|||
if isinstance(data, dict):
|
||||
data = [data]
|
||||
|
||||
items = []
|
||||
for item in data:
|
||||
try:
|
||||
self.format_item(item)
|
||||
items.append(Item.format(item))
|
||||
except ValueError as e:
|
||||
return web.json_response(data={"error": str(e), "data": item}, status=web.HTTPBadRequest.status_code)
|
||||
|
||||
return web.json_response(
|
||||
data=await asyncio.wait_for(
|
||||
fut=asyncio.gather(*[self.add(**self.format_item(item)) for item in data]),
|
||||
fut=asyncio.gather(*[self.add(item=item) for item in items]),
|
||||
timeout=None,
|
||||
),
|
||||
status=web.HTTPOk.status_code,
|
||||
|
|
@ -790,18 +793,6 @@ class HttpAPI(Common):
|
|||
if not item.get("id", None) or not validate_uuid(item.get("id"), version=4):
|
||||
item["id"] = str(uuid.uuid4())
|
||||
|
||||
if not item.get("args", None) or str(item.get("args")).strip() == "":
|
||||
item["args"] = {}
|
||||
|
||||
if item.get("args", None) and isinstance(item.get("args"), str):
|
||||
item["args"] = json.loads(item.get("args"))
|
||||
|
||||
if not item.get("postprocessors", None) or str(item.get("postprocessors")).strip() == "":
|
||||
item["postprocessors"] = []
|
||||
|
||||
if item.get("postprocessors", None) and isinstance(item.get("postprocessors"), str):
|
||||
item["postprocessors"] = json.loads(item.get("postprocessors"))
|
||||
|
||||
try:
|
||||
cls.validate(item)
|
||||
except ValueError as e:
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ from .config import Config
|
|||
from .DownloadQueue import DownloadQueue
|
||||
from .encoder import Encoder
|
||||
from .Events import Event, EventBus, Events, error
|
||||
from .ItemDTO import Item
|
||||
from .Presets import Presets
|
||||
from .Utils import is_downloaded
|
||||
|
||||
|
|
@ -79,7 +80,7 @@ class HttpSocket(Common):
|
|||
|
||||
self._notify.subscribe(
|
||||
Events.ADD_URL,
|
||||
lambda data, _, **kwargs: self.add(**self.format_item(data.data)), # noqa: ARG005
|
||||
lambda data, _, **kwargs: self.add(item=Item.format(data.data)), # noqa: ARG005
|
||||
f"{__class__.__name__}.socket_add_url",
|
||||
)
|
||||
|
||||
|
|
@ -188,14 +189,16 @@ class HttpSocket(Common):
|
|||
return
|
||||
|
||||
try:
|
||||
item = self.format_item(data)
|
||||
await self._notify.emit(
|
||||
event=Events.STATUS,
|
||||
data=await self.add(item=Item.format(data)),
|
||||
to=sid,
|
||||
)
|
||||
except ValueError as e:
|
||||
LOG.exception(e)
|
||||
await self._notify.emit(Events.ERROR, data=error(str(e)), to=sid)
|
||||
return
|
||||
|
||||
status = await self.add(**item)
|
||||
await self._notify.emit(Events.STATUS, data=status, to=sid)
|
||||
|
||||
@ws_event
|
||||
async def item_cancel(self, sid: str, id: str):
|
||||
if not id:
|
||||
|
|
|
|||
|
|
@ -1,10 +1,171 @@
|
|||
import json
|
||||
import logging
|
||||
import time
|
||||
import uuid
|
||||
from dataclasses import dataclass, field
|
||||
from email.utils import formatdate
|
||||
from typing import Any
|
||||
|
||||
LOG = logging.getLogger("ItemDTO")
|
||||
|
||||
|
||||
@dataclass(kw_only=True)
|
||||
class Item:
|
||||
"""
|
||||
This class represents a single download request for data transfer purposes.
|
||||
"""
|
||||
|
||||
url: str
|
||||
"""The URL of the item to be downloaded."""
|
||||
|
||||
preset: str = field(default_factory=lambda: Item._default_preset())
|
||||
"""The preset to be used for the download."""
|
||||
|
||||
folder: str = ""
|
||||
"""The folder to save the download to."""
|
||||
|
||||
cookies: str = ""
|
||||
"""The cookies to be used for the download."""
|
||||
|
||||
template: str = ""
|
||||
"""The template to be used for the download."""
|
||||
|
||||
cli: str = ""
|
||||
"""The yt-dlp cli options to be used for the download."""
|
||||
|
||||
extras: dict = field(default_factory=dict)
|
||||
"""Extra data to be added to the download."""
|
||||
|
||||
def serialize(self) -> dict:
|
||||
return self.__dict__.copy()
|
||||
|
||||
def json(self) -> str:
|
||||
return json.dumps(self.serialize(), default=lambda o: o.__dict__, sort_keys=True, indent=4)
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
return self.__dict__.get(key, default)
|
||||
|
||||
def has_extras(self) -> bool:
|
||||
"""
|
||||
Check if the item has any extras data associated with it.
|
||||
|
||||
Returns:
|
||||
bool: True if the item has extras, False otherwise.
|
||||
|
||||
"""
|
||||
return self.extras and len(self.extras) > 0
|
||||
|
||||
def has_cli(self) -> bool:
|
||||
"""
|
||||
Check if the item has any yt-dlp cli options associated with it.
|
||||
|
||||
Returns:
|
||||
bool: True if the item has yt-dlp cli options, False otherwise.
|
||||
|
||||
"""
|
||||
return self.cli and len(self.cli) > 2
|
||||
|
||||
def _default_preset() -> str:
|
||||
from .config import Config
|
||||
|
||||
return Config.get_instance().default_preset
|
||||
|
||||
def new_with(self, **kwargs) -> "Item":
|
||||
"""
|
||||
Create a new instance of Item with the given parameters.
|
||||
|
||||
Args:
|
||||
**kwargs: The parameters to be used for creating the new instance.
|
||||
|
||||
Returns:
|
||||
Item: A new instance of Item with the given parameters.
|
||||
|
||||
"""
|
||||
return Item.format({**self.serialize(), **kwargs})
|
||||
|
||||
def __repr__(self):
|
||||
from .config import Config
|
||||
from .Utils import calc_download_path, strip_newline
|
||||
|
||||
data = {}
|
||||
for k, v in self.serialize().items():
|
||||
if not v:
|
||||
continue
|
||||
|
||||
if k == "cli":
|
||||
data[k] = strip_newline(v)
|
||||
elif k == "extras":
|
||||
data[k] = f"{len(v)} items"
|
||||
elif k == "cookies":
|
||||
data[k] = f"{len(v)}/chars"
|
||||
elif k == "folder":
|
||||
data[k] = calc_download_path(base_path=Config.get_instance().download_path, folder=v)
|
||||
else:
|
||||
data[k] = v
|
||||
|
||||
items = "".join(f'{k}="{v}", ' for k, v in data.items() if v)
|
||||
return f"Item({items.strip(', ')})"
|
||||
|
||||
@staticmethod
|
||||
def format(item: dict) -> "Item":
|
||||
"""
|
||||
Format the item to be added to the download queue.
|
||||
|
||||
Args:
|
||||
item (dict): The item to be formatted.
|
||||
|
||||
Raises:
|
||||
ValueError: If the url is not provided.
|
||||
ValueError: If the yt-cli command line arguments are not valid.
|
||||
|
||||
Returns:
|
||||
Item: The formatted item.
|
||||
|
||||
"""
|
||||
url: str = item.get("url")
|
||||
|
||||
if not url or not isinstance(url, str):
|
||||
msg = "url param is required."
|
||||
raise ValueError(msg)
|
||||
|
||||
data = {
|
||||
"url": url,
|
||||
}
|
||||
|
||||
preset = item.get("preset")
|
||||
if preset and isinstance(preset, str) and preset != Item._default_preset():
|
||||
data["preset"] = preset
|
||||
|
||||
if item.get("folder") and isinstance(item.get("folder"), str):
|
||||
data["folder"] = item.get("folder")
|
||||
|
||||
if item.get("cookies") and isinstance(item.get("cookies"), str):
|
||||
data["cookies"] = item.get("cookies")
|
||||
|
||||
if item.get("template") and isinstance(item.get("template"), str):
|
||||
data["template"] = item.get("template")
|
||||
|
||||
extras = item.get("extras")
|
||||
if extras and isinstance(extras, dict) and len(extras) > 0:
|
||||
data["extras"] = extras
|
||||
|
||||
cli = item.get("cli")
|
||||
if cli and len(cli) > 2:
|
||||
from .Utils import arg_converter
|
||||
|
||||
try:
|
||||
removed_options = []
|
||||
arg_converter(args=cli, level=True, removed_options=removed_options)
|
||||
if len(removed_options) > 0:
|
||||
LOG.warning("Removed the following options '%s' for '%s'.", ", ".join(removed_options), url)
|
||||
|
||||
data["cli"] = cli
|
||||
except Exception as e:
|
||||
msg = f"Failed to parse yt-dlp cli options. {e!s}"
|
||||
raise ValueError(msg) from e
|
||||
|
||||
return Item(**data)
|
||||
|
||||
|
||||
@dataclass(kw_only=True)
|
||||
class ItemDTO:
|
||||
|
|
@ -49,45 +210,8 @@ class ItemDTO:
|
|||
speed: str | None = None
|
||||
eta: str | None = None
|
||||
|
||||
# @Deprecated: These fields are deprecated and will be removed in the future.
|
||||
thumbnail: str | None = None
|
||||
ytdlp_cookies: str | None = None
|
||||
ytdlp_config: dict = field(default_factory=dict)
|
||||
output_template: str | None = None
|
||||
output_template_chapter: str | None = None
|
||||
config: dict = field(default_factory=dict)
|
||||
|
||||
def serialize(self) -> dict:
|
||||
deprecated: tuple = (
|
||||
"thumbnail",
|
||||
"quality",
|
||||
"format",
|
||||
"ytdlp_cookies",
|
||||
"ytdlp_config",
|
||||
"output_template",
|
||||
"output_template_chapter",
|
||||
"config",
|
||||
)
|
||||
|
||||
if self.thumbnail and "thumbnail" not in self.extras:
|
||||
self.extras["thumbnail"] = self.thumbnail
|
||||
|
||||
mapper: dict = {
|
||||
"cookies": "ytdlp_cookies",
|
||||
"config": "ytdlp_config",
|
||||
"template": "output_template",
|
||||
"template_chapter": "output_template_chapter",
|
||||
}
|
||||
|
||||
for k, v in mapper.items():
|
||||
if not self.get(k) and self.get(v):
|
||||
self.__dict__[k] = self.get(v)
|
||||
|
||||
dump = self.__dict__.copy()
|
||||
for f in deprecated:
|
||||
dump.pop(f, None)
|
||||
|
||||
return dump
|
||||
return self.__dict__.copy()
|
||||
|
||||
def json(self) -> str:
|
||||
return json.dumps(self.serialize(), default=lambda o: o.__dict__, sort_keys=True, indent=4)
|
||||
|
|
@ -97,3 +221,17 @@ class ItemDTO:
|
|||
|
||||
def get_id(self) -> str:
|
||||
return self._id
|
||||
|
||||
@staticmethod
|
||||
def removed_fields() -> tuple:
|
||||
"""Fields that once existed but are no longer used."""
|
||||
return (
|
||||
"thumbnail",
|
||||
"quality",
|
||||
"format",
|
||||
"ytdlp_cookies",
|
||||
"ytdlp_config",
|
||||
"output_template",
|
||||
"output_template_chapter",
|
||||
"config",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -141,9 +141,6 @@ class Notification(metaclass=Singleton):
|
|||
except Exception:
|
||||
pass
|
||||
|
||||
if os.path.getsize(self._file) > 10:
|
||||
self.load()
|
||||
|
||||
@staticmethod
|
||||
def get_instance() -> "Notification":
|
||||
if Notification._instance is None:
|
||||
|
|
@ -371,7 +368,9 @@ class Notification(metaclass=Singleton):
|
|||
if "form" == target.request.type.lower():
|
||||
reqBody["data"]["data"] = self._encoder.encode(reqBody["data"]["data"])
|
||||
|
||||
logging.getLogger("httpx").setLevel(logging.WARNING)
|
||||
if not self._debug:
|
||||
logging.getLogger("httpx").setLevel(logging.WARNING)
|
||||
|
||||
response = await self._client.request(**reqBody)
|
||||
|
||||
respData = {"url": target.request.url, "status": response.status_code, "text": response.text}
|
||||
|
|
@ -404,7 +403,7 @@ class Notification(metaclass=Singleton):
|
|||
data[k] = [self._deep_unpack(i) for i in v]
|
||||
if isinstance(v, datetime):
|
||||
data[k] = v.isoformat()
|
||||
if isinstance(v, ItemDTO):
|
||||
if isinstance(v, object) and hasattr(v, "serialize"):
|
||||
data[k] = v.serialize()
|
||||
|
||||
return data
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ from .config import Config
|
|||
from .encoder import Encoder
|
||||
from .Events import EventBus, Events
|
||||
from .Singleton import Singleton
|
||||
from .Utils import arg_converter, clean_item
|
||||
|
||||
LOG = logging.getLogger("presets")
|
||||
|
||||
|
|
@ -26,12 +27,6 @@ class Preset:
|
|||
format: str
|
||||
"""The format of the preset."""
|
||||
|
||||
args: dict[str, list[str] | bool] = field(default_factory=dict)
|
||||
"""The arguments of the preset."""
|
||||
|
||||
postprocessors: list = field(default_factory=list)
|
||||
"""The postprocessors of the preset."""
|
||||
|
||||
folder: str = ""
|
||||
"""The default download folder to use if non is given."""
|
||||
|
||||
|
|
@ -83,13 +78,19 @@ class Presets(metaclass=Singleton):
|
|||
pass
|
||||
|
||||
with open(os.path.join(os.path.dirname(__file__), "presets.json")) as f:
|
||||
self._default_presets = [Preset(**preset) for preset in json.load(f)]
|
||||
for i, preset in enumerate(json.load(f)):
|
||||
try:
|
||||
self.validate(preset)
|
||||
self._default_presets.append(Preset(**preset))
|
||||
except Exception as e:
|
||||
LOG.error(f"Failed to parse default preset '{i}'. '{e!s}'.")
|
||||
continue
|
||||
|
||||
EventBus.get_instance().subscribe(
|
||||
Events.PRESETS_ADD,
|
||||
lambda data, _, **kwargs: self.add(**data.data), # noqa: ARG005
|
||||
f"{__class__.__name__}.save",
|
||||
)
|
||||
def event_handler(_, __):
|
||||
msg = "Not implemented"
|
||||
raise Exception(msg)
|
||||
|
||||
EventBus.get_instance().subscribe(Events.PRESETS_ADD, event_handler, f"{__class__.__name__}.save")
|
||||
|
||||
@staticmethod
|
||||
def get_instance() -> "Presets":
|
||||
|
|
@ -150,22 +151,27 @@ class Presets(metaclass=Singleton):
|
|||
LOG.info(f"No presets were defined in '{self._file}'.")
|
||||
return self
|
||||
|
||||
needSaving = False
|
||||
need_save = False
|
||||
|
||||
for i, preset in enumerate(presets):
|
||||
try:
|
||||
if "id" not in preset:
|
||||
preset["id"] = str(uuid.uuid4())
|
||||
needSaving = True
|
||||
need_save = True
|
||||
|
||||
preset, preset_status = clean_item(preset, keys=("args", "postprocessors"))
|
||||
preset = Preset(**preset)
|
||||
|
||||
if preset_status:
|
||||
need_save = True
|
||||
|
||||
self._presets.append(preset)
|
||||
except Exception as e:
|
||||
LOG.error(f"Failed to parse preset at list position '{i}'. '{e!s}'.")
|
||||
continue
|
||||
|
||||
if needSaving:
|
||||
LOG.info("Saving presets due to missing ids.")
|
||||
if need_save:
|
||||
LOG.info("Saving presets due to format, or id change.")
|
||||
self.save(self._presets)
|
||||
|
||||
return self
|
||||
|
|
@ -198,7 +204,7 @@ class Presets(metaclass=Singleton):
|
|||
"""
|
||||
if not isinstance(preset, dict):
|
||||
if not isinstance(preset, Preset):
|
||||
msg = "Invalid preset type."
|
||||
msg = f"Invalid preset type. Was expecting a (Preset|dict), but got '{type(preset).__name__}'."
|
||||
raise ValueError(msg) # noqa: TRY004
|
||||
|
||||
preset = preset.serialize()
|
||||
|
|
@ -215,18 +221,8 @@ class Presets(metaclass=Singleton):
|
|||
msg = "No format found."
|
||||
raise ValueError(msg)
|
||||
|
||||
if preset.get("args") and not isinstance(preset.get("args"), dict):
|
||||
msg = "Invalid args type. expected dict."
|
||||
raise ValueError(msg)
|
||||
|
||||
if preset.get("postprocessors") and not isinstance(preset.get("postprocessors"), list):
|
||||
msg = "Invalid postprocessors type. expected list."
|
||||
raise ValueError(msg)
|
||||
|
||||
if preset.get("cli"):
|
||||
try:
|
||||
from .Utils import arg_converter
|
||||
|
||||
arg_converter(args=preset.get("cli"))
|
||||
except Exception as e:
|
||||
msg = f"Invalid cli options. '{e!s}'."
|
||||
|
|
@ -270,26 +266,24 @@ class Presets(metaclass=Singleton):
|
|||
|
||||
return self
|
||||
|
||||
def get(self, id: str | None = None, name: str | None = None) -> Preset | None:
|
||||
def get(self, id_or_name: str) -> Preset | None:
|
||||
"""
|
||||
Get the preset by id or name.
|
||||
|
||||
Args:
|
||||
id (str|None): The id of the preset.
|
||||
name (str|None): The name of the preset.
|
||||
id_or_name (str): The id or name of the preset.
|
||||
|
||||
Returns:
|
||||
Preset|None: The preset if found, None otherwise.
|
||||
|
||||
"""
|
||||
if not id and not name:
|
||||
if not id_or_name:
|
||||
return None
|
||||
|
||||
for preset in self.get_all():
|
||||
if preset.id == id:
|
||||
return preset
|
||||
if id_or_name not in (preset.id, preset.name):
|
||||
continue
|
||||
|
||||
if preset.name == name:
|
||||
return preset
|
||||
return preset
|
||||
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ from .encoder import Encoder
|
|||
from .Events import EventBus, Events, error, info, success
|
||||
from .Scheduler import Scheduler
|
||||
from .Singleton import Singleton
|
||||
from .Utils import clean_item
|
||||
|
||||
LOG = logging.getLogger("tasks")
|
||||
|
||||
|
|
@ -140,13 +141,13 @@ class Tasks(metaclass=Singleton):
|
|||
LOG.info(f"No tasks were defined in '{self._file}'.")
|
||||
return self
|
||||
|
||||
need_update = False
|
||||
need_save = False
|
||||
for i, task in enumerate(tasks):
|
||||
try:
|
||||
task, task_status = self.clean_task(task)
|
||||
task, task_status = clean_item(task, keys=("cookies", "config"))
|
||||
task = Task(**task)
|
||||
if task_status:
|
||||
need_update = True
|
||||
need_save = True
|
||||
except Exception as e:
|
||||
LOG.error(f"Failed to parse task at list position '{i}'. '{e!s}'.")
|
||||
continue
|
||||
|
|
@ -155,12 +156,20 @@ class Tasks(metaclass=Singleton):
|
|||
self._scheduler.add(timer=task.timer, func=self._runner, args=(task,), id=task.id)
|
||||
self._tasks.append(task)
|
||||
|
||||
LOG.info(f"Task '{i}: {task.name}' queued to be executed every '{task.timer}'.")
|
||||
try:
|
||||
from cronsim import CronSim
|
||||
|
||||
cs = CronSim(task.timer, datetime.now(UTC))
|
||||
schedule_time = cs.explain()
|
||||
except Exception:
|
||||
schedule_time = task.timer
|
||||
|
||||
LOG.info(f"Task '{i}: {task.name}' queued to be executed '{schedule_time}'.")
|
||||
except Exception as e:
|
||||
LOG.exception(e)
|
||||
LOG.error(f"Failed to queue task '{i}: {task.name}'. '{e!s}'.")
|
||||
|
||||
if need_update:
|
||||
if need_save:
|
||||
LOG.info("Updating tasks file to remove old keys.")
|
||||
self.save(self.get_all())
|
||||
|
||||
|
|
@ -223,6 +232,7 @@ class Tasks(metaclass=Singleton):
|
|||
if task.get("cli"):
|
||||
try:
|
||||
from .Utils import arg_converter
|
||||
|
||||
arg_converter(args=task.get("cli"))
|
||||
except Exception as e:
|
||||
msg = f"Invalid cli options. '{e!s}'."
|
||||
|
|
@ -266,27 +276,6 @@ class Tasks(metaclass=Singleton):
|
|||
|
||||
return self
|
||||
|
||||
def clean_task(self, task: dict) -> tuple[dict, bool]:
|
||||
"""
|
||||
Clean the task from old keys.
|
||||
|
||||
Args:
|
||||
task (dict): The task to clean.
|
||||
|
||||
Returns:
|
||||
tuple[dict, bool]: The cleaned task and a status if the task was cleaned.
|
||||
|
||||
"""
|
||||
status = False
|
||||
removedKeys = ["cookies", "config"]
|
||||
|
||||
for key in removedKeys:
|
||||
if key in task:
|
||||
status = True
|
||||
task.pop(key)
|
||||
|
||||
return task, status
|
||||
|
||||
async def _runner(self, task: Task):
|
||||
"""
|
||||
Run the task.
|
||||
|
|
|
|||
|
|
@ -522,8 +522,6 @@ def arg_converter(
|
|||
|
||||
bad_options.update(item.items())
|
||||
|
||||
LOG.debug("Removed %i the following options: '%s'.", level, ", ".join(bad_options.values()))
|
||||
|
||||
for key in diff.copy():
|
||||
if key not in bad_options:
|
||||
continue
|
||||
|
|
@ -889,3 +887,60 @@ def get_files(base_path: str, dir: str | None = None):
|
|||
)
|
||||
|
||||
return contents
|
||||
|
||||
|
||||
def clean_item(item: dict, keys: list | tuple) -> tuple[dict, bool]:
|
||||
"""
|
||||
Remove given keys from a dictionary.
|
||||
This function modifies the dictionary in place and returns a tuple
|
||||
containing the modified dictionary and a boolean indicating
|
||||
whether any keys were removed.
|
||||
|
||||
Args:
|
||||
item (dict): The item to clean.
|
||||
keys (list|tuple): The keys to remove.
|
||||
|
||||
Returns:
|
||||
tuple[dict, bool]: The cleaned item and a the status of cleaning operation.
|
||||
|
||||
Raises:
|
||||
TypeError: If item is not a dictionary or keys is not a list or tuple.
|
||||
|
||||
"""
|
||||
status = False
|
||||
|
||||
if not isinstance(item, dict):
|
||||
msg = "Item must be a dictionary."
|
||||
raise TypeError(msg)
|
||||
|
||||
if not isinstance(keys, list | tuple):
|
||||
msg = "Keys must be a list or tuple."
|
||||
raise TypeError(msg)
|
||||
|
||||
for key in keys:
|
||||
if key not in item:
|
||||
continue
|
||||
|
||||
status = True
|
||||
item.pop(key)
|
||||
|
||||
return item, status
|
||||
|
||||
|
||||
def strip_newline(string: str) -> str:
|
||||
"""
|
||||
Remove newlines from a string.
|
||||
|
||||
Args:
|
||||
string (str): The string to process.
|
||||
|
||||
Returns:
|
||||
str: The string without newlines.
|
||||
|
||||
"""
|
||||
if not string:
|
||||
return ""
|
||||
|
||||
res = re.sub(r"(\r\n|\r|\n)", " ", string)
|
||||
|
||||
return res.strip() if res else ""
|
||||
|
|
|
|||
|
|
@ -36,6 +36,30 @@ class YTDLPOpts(metaclass=Singleton):
|
|||
|
||||
return YTDLPOpts._instance
|
||||
|
||||
def add_cli(self, args: str, from_user: int | bool = False) -> "YTDLPOpts":
|
||||
"""
|
||||
Prase and add yt-dlp cli options to the item options.
|
||||
|
||||
Args:
|
||||
args (str): The cli options to add
|
||||
from_user (bool): If the options are from the user
|
||||
|
||||
Returns:
|
||||
YTDLPOpts: The instance of the class
|
||||
|
||||
"""
|
||||
if not args or len(args) < 2 or not isinstance(args, str):
|
||||
return self
|
||||
|
||||
removed_options = []
|
||||
|
||||
self._item_opts.update(arg_converter(args=args, level=from_user, removed_options=removed_options))
|
||||
|
||||
if len(removed_options) > 0:
|
||||
LOG.warning("Removed the following options: '%s'.", ", ".join(removed_options))
|
||||
|
||||
return self
|
||||
|
||||
def add(self, config: dict, from_user: bool = False) -> "YTDLPOpts":
|
||||
"""
|
||||
Add the options to the item options.
|
||||
|
|
@ -78,7 +102,7 @@ class YTDLPOpts(metaclass=Singleton):
|
|||
YTDLPOpts: The instance of the class
|
||||
|
||||
"""
|
||||
preset = Presets.get_instance().get(name=name)
|
||||
preset = Presets.get_instance().get(name)
|
||||
if not preset or "default" == name:
|
||||
return self
|
||||
|
||||
|
|
@ -118,25 +142,6 @@ class YTDLPOpts(metaclass=Singleton):
|
|||
"temp": self._config.temp_path,
|
||||
}
|
||||
|
||||
# @Deprecated - To be removed in future versions.
|
||||
if not preset.cli:
|
||||
if preset.postprocessors and isinstance(preset.postprocessors, list) and len(preset.postprocessors) > 0:
|
||||
self._preset_opts["postprocessors"] = preset.postprocessors
|
||||
|
||||
if preset.args and isinstance(preset.args, dict) and len(preset.args) > 0:
|
||||
bad_options = {k: v for d in REMOVE_KEYS for k, v in d.items()}
|
||||
removed_options = []
|
||||
for key, value in preset.args.items():
|
||||
if key in bad_options:
|
||||
removed_options.append(bad_options[key])
|
||||
continue
|
||||
self._preset_opts[key] = value
|
||||
|
||||
if len(removed_options) > 0:
|
||||
LOG.warning(
|
||||
"Removed the following options '%s' from '%s' args.", ", ".join(removed_options), preset.name
|
||||
)
|
||||
|
||||
return self
|
||||
|
||||
def get_all(self, keep: bool = False) -> dict:
|
||||
|
|
@ -166,25 +171,4 @@ class YTDLPOpts(metaclass=Singleton):
|
|||
if "format" in data and data["format"] in ["not_set", "default"]:
|
||||
data["format"] = None
|
||||
|
||||
# @Deprecated - To be removed in future versions, All the checks below this line are deprecated.
|
||||
if "daterange" in data and isinstance(data["daterange"], dict):
|
||||
from yt_dlp.utils import DateRange
|
||||
|
||||
data["daterange"] = DateRange(data["daterange"]["start"], data["daterange"]["end"])
|
||||
|
||||
if "impersonate" in data and isinstance(data["impersonate"], str):
|
||||
from yt_dlp.networking.impersonate import ImpersonateTarget
|
||||
|
||||
data["impersonate"] = ImpersonateTarget.from_str(data["impersonate"])
|
||||
|
||||
if (
|
||||
"match_filter" in data
|
||||
and isinstance(data["match_filter"], dict)
|
||||
and "filters" in data["match_filter"]
|
||||
and len(data["match_filter"]["filters"]) > 0
|
||||
):
|
||||
from yt_dlp.utils import match_filter_func
|
||||
|
||||
data["match_filter"] = match_filter_func(data["match_filter"]["filters"])
|
||||
|
||||
return data
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import json
|
||||
import logging
|
||||
|
||||
from .config import Config
|
||||
from .DownloadQueue import DownloadQueue
|
||||
from .encoder import Encoder
|
||||
from .Utils import arg_converter
|
||||
from .ItemDTO import Item
|
||||
|
||||
LOG = logging.getLogger("common")
|
||||
|
||||
|
|
@ -27,105 +26,16 @@ class Common:
|
|||
config = config or Config.get_instance()
|
||||
self.default_preset = config.default_preset
|
||||
|
||||
async def add(
|
||||
self,
|
||||
url: str,
|
||||
preset: str,
|
||||
folder: str,
|
||||
cookies: str,
|
||||
# @deprecated: config: dict,
|
||||
config: dict,
|
||||
template: str,
|
||||
extras: dict | None = None,
|
||||
cli: str = "",
|
||||
) -> dict[str, str]:
|
||||
async def add(self, item: Item) -> dict[str, str]:
|
||||
"""
|
||||
Add an item to the download queue.
|
||||
|
||||
Args:
|
||||
url (str): The url to be added to the queue.
|
||||
preset (str): The preset to be used for the download.
|
||||
folder (str): The folder to save the download to.
|
||||
cookies (str): The cookies to be used for the download.
|
||||
config (dict): The yt-dlp config to be used for the download.
|
||||
template (str): The template to be used for the download.
|
||||
extras (dict): Extra data to be added to the download
|
||||
cli (str): The yt-dlp cli options to be used for the download.
|
||||
item (Item): The item to be added to the queue.
|
||||
|
||||
Returns:
|
||||
dict[str, str]: The status of the download.
|
||||
{ "status": "text" }
|
||||
|
||||
"""
|
||||
if cookies and isinstance(cookies, dict):
|
||||
cookies = self.encoder.encode(cookies)
|
||||
|
||||
return await self.queue.add(
|
||||
url=url,
|
||||
preset=preset if preset else self.config.default_preset,
|
||||
folder=folder,
|
||||
cookies=cookies,
|
||||
config=config if isinstance(config, dict) else {},
|
||||
template=template,
|
||||
cli=cli,
|
||||
extras=extras,
|
||||
)
|
||||
|
||||
def format_item(self, item: dict) -> dict:
|
||||
"""
|
||||
Format the item to be added to the download queue.
|
||||
|
||||
Args:
|
||||
item (dict): The item to be formatted.
|
||||
|
||||
Raises:
|
||||
ValueError: If the url is not provided.
|
||||
ValueError: If the yt-dlp config is not a valid json.
|
||||
|
||||
Returns:
|
||||
dict: The formatted item
|
||||
|
||||
"""
|
||||
url: str = item.get("url")
|
||||
|
||||
if not url:
|
||||
msg = "url param is required."
|
||||
raise ValueError(msg)
|
||||
|
||||
preset: str = str(item.get("preset", self.config.default_preset))
|
||||
folder: str = str(item.get("folder")) if item.get("folder") else ""
|
||||
cookies: str = str(item.get("cookies")) if item.get("cookies") else ""
|
||||
template: str = str(item.get("template")) if item.get("template") else ""
|
||||
extras = item.get("extras", {})
|
||||
|
||||
config = item.get("config")
|
||||
if isinstance(config, str) and config:
|
||||
try:
|
||||
config = json.loads(config)
|
||||
except Exception as e:
|
||||
msg = f"Failed to parse json yt-dlp config for '{url}'. {e!s}"
|
||||
raise ValueError(msg) from e
|
||||
|
||||
cli = item.get("cli")
|
||||
if cli and len(cli) > 2:
|
||||
try:
|
||||
removed_options = []
|
||||
arg_converter(args=cli, level=True, removed_options=removed_options)
|
||||
if len(removed_options) > 0:
|
||||
LOG.warning("Removed the following options '%s'.", ", ".join(removed_options))
|
||||
|
||||
config = {}
|
||||
except Exception as e:
|
||||
msg = f"Failed to parse yt-dlp cli options. {e!s}"
|
||||
raise ValueError(msg) from e
|
||||
|
||||
return {
|
||||
"url": url,
|
||||
"preset": preset,
|
||||
"folder": folder,
|
||||
"cookies": cookies,
|
||||
"config": config if isinstance(config, dict) else {},
|
||||
"template": template,
|
||||
"cli": cli if isinstance(cli, str) else "",
|
||||
"extras": extras if isinstance(extras, dict) else {},
|
||||
}
|
||||
return await self.queue.add(item=item)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import coloredlogs
|
|||
from dotenv import load_dotenv
|
||||
from yt_dlp.version import __version__ as YTDLP_VERSION
|
||||
|
||||
from .Utils import REMOVE_KEYS, arg_converter, load_file, merge_dict
|
||||
from .Utils import arg_converter
|
||||
from .version import APP_VERSION
|
||||
|
||||
|
||||
|
|
@ -321,11 +321,10 @@ class Config:
|
|||
except Exception as e:
|
||||
LOG.error(f"Error starting debugpy server at '0.0.0.0:{self.debugpy_port}'. {e}")
|
||||
|
||||
ytdlp_cli: str = os.path.join(self.config_path, "ytdlp.cli")
|
||||
optsFile: str = os.path.join(self.config_path, "ytdlp.json")
|
||||
if os.path.exists(ytdlp_cli) and os.path.getsize(ytdlp_cli) > 2:
|
||||
LOG.info(f"Loading yt-dlp custom options from '{ytdlp_cli}'.")
|
||||
with open(ytdlp_cli) as f:
|
||||
opts_file: str = os.path.join(self.config_path, "ytdlp.cli")
|
||||
if os.path.exists(opts_file) and os.path.getsize(opts_file) > 2:
|
||||
LOG.info(f"Loading yt-dlp custom options from '{opts_file}'.")
|
||||
with open(opts_file) as f:
|
||||
ytdlp_cli_opts = f.read().strip()
|
||||
if ytdlp_cli_opts:
|
||||
try:
|
||||
|
|
@ -343,41 +342,15 @@ class Config:
|
|||
|
||||
if len(removed_options) > 0:
|
||||
LOG.warning(
|
||||
"Removed the following options: '%s' from '%s'", ", ".join(removed_options), ytdlp_cli
|
||||
"Removed the following options: '%s' from '%s'", ", ".join(removed_options), opts_file
|
||||
)
|
||||
except Exception as e:
|
||||
msg = f"Failed to parse yt-dlp cli options from '{ytdlp_cli}'. '{e!s}'."
|
||||
msg = f"Failed to parse yt-dlp cli options from '{opts_file}'. '{e!s}'."
|
||||
raise ValueError(msg) from e
|
||||
else:
|
||||
LOG.warning(f"Empty yt-dlp custom options file '{ytdlp_cli}'.")
|
||||
# @Deprecated - To be removed in future versions.
|
||||
elif os.path.exists(optsFile) and os.path.getsize(optsFile) > 5:
|
||||
LOG.warning("The JSON ytdlp.json options file is deprecated, please use 'ytdlp.cli' file instead.")
|
||||
LOG.info(f"Loading yt-dlp custom options from '{optsFile}'.")
|
||||
|
||||
(opts, status, error) = load_file(optsFile, dict)
|
||||
if not status:
|
||||
LOG.error(f"Could not load yt-dlp custom options from '{optsFile}'. {error}")
|
||||
sys.exit(1)
|
||||
if isinstance(opts, dict):
|
||||
bad_options = {k: v for d in REMOVE_KEYS for k, v in d.items()}
|
||||
removed_options = []
|
||||
for key in opts.copy():
|
||||
if key not in bad_options:
|
||||
continue
|
||||
|
||||
removed_options.append(bad_options[key])
|
||||
opts.pop(key, None)
|
||||
|
||||
if len(removed_options) > 0:
|
||||
LOG.warning(
|
||||
"Removed the following options: '%s' from 'ytdlp.json' file.", ", ".join(removed_options)
|
||||
)
|
||||
self.ytdl_options = merge_dict(self.ytdl_options, opts)
|
||||
else:
|
||||
LOG.error(f"Invalid yt-dlp custom options file '{optsFile}'.")
|
||||
LOG.warning(f"Empty yt-dlp custom options file '{opts_file}'.")
|
||||
else:
|
||||
LOG.info(f"No yt-dlp custom options found at '{ytdlp_cli}'.")
|
||||
LOG.info(f"No yt-dlp custom options found at '{opts_file}'.")
|
||||
|
||||
self.ytdl_options["socket_timeout"] = self.socket_timeout
|
||||
|
||||
|
|
|
|||
|
|
@ -5,31 +5,39 @@
|
|||
<div class="box">
|
||||
<div class="columns is-multiline is-mobile">
|
||||
<div class="column is-12">
|
||||
<div class="control has-icons-left">
|
||||
<label class="label is-inline is-unselectable" for="url">
|
||||
<span class="icon"><i class="fa-solid fa-link" /></span>
|
||||
URLs
|
||||
</label>
|
||||
<div class="control">
|
||||
<input type="text" class="input" id="url" placeholder="Video or playlist link"
|
||||
:disabled="!socket.isConnected || addInProgress" v-model="url">
|
||||
<span class="icon is-small is-left">
|
||||
<i class="fa-solid fa-link" />
|
||||
</span>
|
||||
</div>
|
||||
<span class="help">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
<span>You can add multiple URLs separated by a comma <code>,</code>.</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="column is-4-tablet is-12-mobile" v-if="!config.app.basic_mode">
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
<a href="#" class="button is-static">Preset</a>
|
||||
<a href="#" class="button is-static">
|
||||
<span class="icon"><i class="fa-solid fa-sliders" /></span>
|
||||
<span>Preset</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="control is-expanded">
|
||||
<div class="select is-fullwidth">
|
||||
<select id="preset" class="is-fullwidth"
|
||||
:disabled="!socket.isConnected || addInProgress || hasFormatInConfig" v-model="selectedPreset"
|
||||
v-tooltip.bottom="hasFormatInConfig ? 'Presets are disabled. Format key is present in the Command arguments for yt-dlp.' : ''">
|
||||
<optgroup label="Default presets">
|
||||
<option v-for="item in filter_presets(true)" :key="item.name" :value="item.name">
|
||||
<optgroup label="Custom presets" v-if="config?.presets.filter(p => !p?.default).length > 0">
|
||||
<option v-for="item in filter_presets(false)" :key="item.name" :value="item.name">
|
||||
{{ item.name }}
|
||||
</option>
|
||||
</optgroup>
|
||||
<optgroup label="Custom presets" v-if="config?.presets.filter(p => !p?.default).length > 0">
|
||||
<option v-for="item in filter_presets(false)" :key="item.name" :value="item.name">
|
||||
<optgroup label="Default presets">
|
||||
<option v-for="item in filter_presets(true)" :key="item.name" :value="item.name">
|
||||
{{ item.name }}
|
||||
</option>
|
||||
</optgroup>
|
||||
|
|
@ -41,7 +49,10 @@
|
|||
<div class="column is-6-tablet is-12-mobile" v-if="!config.app.basic_mode">
|
||||
<div class="field has-addons" v-tooltip="'Folder relative to ' + config.app.download_path">
|
||||
<div class="control">
|
||||
<a href="#" class="button is-static">Save in</a>
|
||||
<a href="#" class="button is-static">
|
||||
<span class="icon"><i class="fa-solid fa-folder" /></span>
|
||||
<span>Save in</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="control is-expanded">
|
||||
<input type="text" class="input is-fullwidth" id="path" v-model="downloadPath" placeholder="Default"
|
||||
|
|
@ -67,17 +78,17 @@
|
|||
</div>
|
||||
<div class="columns is-multiline is-mobile" v-if="showAdvanced && !config.app.basic_mode">
|
||||
|
||||
<div class="column is-6-tablet is-12-mobile">
|
||||
<div class="column is-12">
|
||||
<div class="field">
|
||||
<label class="label is-inline" for="output_format"
|
||||
v-tooltip="'Default Format: ' + config.app.output_template">
|
||||
<label class="label is-inline is-unselectable" for="output_format"
|
||||
v-tooltip="'Default: ' + config.app.output_template">
|
||||
<span class="icon"><i class="fa-solid fa-file" /></span>
|
||||
Output Template
|
||||
</label>
|
||||
<div class="control has-icons-left">
|
||||
<div class="control">
|
||||
<input type="text" class="input" v-model="output_template" id="output_format"
|
||||
:disabled="!socket.isConnected || addInProgress"
|
||||
placeholder="Uses default output template naming if empty.">
|
||||
<span class="icon is-small is-left"><i class="fa-solid fa-file" /></span>
|
||||
</div>
|
||||
<span class="help">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
|
|
@ -87,16 +98,16 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-6-tablet is-12-mobile">
|
||||
<div class="column is-6-tablet is-4-mobile">
|
||||
<div class="field">
|
||||
<label class="label is-inline" for="cli_options">
|
||||
<label class="label is-inline is-unselectable" for="cli_options">
|
||||
<span class="icon"><i class="fa-solid fa-terminal" /></span>
|
||||
Command arguments for yt-dlp
|
||||
</label>
|
||||
<div class="control has-icons-left">
|
||||
<input type="text" class="input" v-model="ytdlp_cli" id="cli_options"
|
||||
<div class="control">
|
||||
<textarea class="textarea is-pre" v-model="ytdlp_cli" id="cli_options"
|
||||
:disabled="!socket.isConnected || addInProgress"
|
||||
placeholder="command options to use, e.g. --no-embed-metadata --no-embed-thumbnail">
|
||||
<span class="icon is-small is-left"><i class="fa-solid fa-terminal" /></span>
|
||||
placeholder="command options to use, e.g. --no-embed-metadata --no-embed-thumbnail" />
|
||||
</div>
|
||||
|
||||
<span class="help">
|
||||
|
|
@ -110,9 +121,10 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-12">
|
||||
<div class="column is-6-tablet is-4-mobile">
|
||||
<div class="field">
|
||||
<label class="label is-inline" for="ytdlpCookies" v-tooltip="'Netscape HTTP Cookie format'">
|
||||
<label class="label is-inline is-unselectable" for="ytdlpCookies">
|
||||
<span class="icon"><i class="fa-solid fa-cookie" /></span>
|
||||
Cookies
|
||||
</label>
|
||||
<div class="control">
|
||||
|
|
@ -206,8 +218,9 @@ const resetConfig = () => {
|
|||
return
|
||||
}
|
||||
|
||||
selectedPreset.value = config.app.default_preset.value
|
||||
selectedPreset.value = config.app.default_preset
|
||||
ytdlpCookies.value = ''
|
||||
ytdlp_cli.value = ''
|
||||
output_template.value = null
|
||||
url.value = null
|
||||
downloadPath.value = null
|
||||
|
|
@ -277,9 +290,8 @@ const hasFormatInConfig = computed(() => {
|
|||
return false
|
||||
}
|
||||
|
||||
return /(?<!\w)(-f|--format)(=|:)?(?!\w)/.test(ytdlp_cli.value)
|
||||
return /(?<!\S)(-f|--format)(=|\s)(\S+)/.test(ytdlp_cli.value)
|
||||
})
|
||||
|
||||
const filter_presets = (flag = true) => config.presets.filter(item => item.default === flag)
|
||||
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -27,13 +27,13 @@
|
|||
|
||||
<div class="column is-12" v-if="showImport || !reference">
|
||||
<label class="label is-inline" for="import_string">
|
||||
<span class="icon"><i class="fa-solid fa-file-import" /></span>
|
||||
Import string
|
||||
</label>
|
||||
|
||||
<div class="field has-addons">
|
||||
<div class="control has-icons-left is-expanded">
|
||||
<div class="control is-expanded">
|
||||
<input type="text" class="input" id="import_string" v-model="import_string" autocomplete="off">
|
||||
<span class="icon is-small is-left"><i class="fa-solid fa-t" /></span>
|
||||
</div>
|
||||
|
||||
<div class="control">
|
||||
|
|
@ -51,31 +51,35 @@
|
|||
|
||||
<div class="column is-6-tablet is-12-mobile">
|
||||
<div class="field">
|
||||
<label class="label is-inline" for="name" v-text="'Name'" />
|
||||
<div class="control has-icons-left">
|
||||
<label class="label is-inline" for="name">
|
||||
<span class="icon"><i class="fa-solid fa-tag" /></span>
|
||||
Name
|
||||
</label>
|
||||
<div class="control">
|
||||
<input type="text" class="input" id="name" v-model="form.name" :disabled="addInProgress">
|
||||
<span class="icon is-small is-left"><i class="fa-solid fa-n" /></span>
|
||||
</div>
|
||||
<span class="help">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
<span>The name to refers to this custom settings.</span>
|
||||
<span>The name to refers to this preset of settings.</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-6-tablet is-12-mobile">
|
||||
<div class="field">
|
||||
<label class="label is-inline" for="format" v-text="'Format'" />
|
||||
<div class="control has-icons-left">
|
||||
<label class="label is-inline" for="format">
|
||||
<span class="icon"><i class="fa-solid fa-f" /></span>
|
||||
Format
|
||||
</label>
|
||||
<div class="control">
|
||||
<input type="text" class="input" id="format" v-model="form.format" :disabled="addInProgress">
|
||||
<span class="icon is-small is-left"><i class="fa-solid fa-f" /></span>
|
||||
</div>
|
||||
<span class="help">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
<span>The yt-dlp <code>[--format, -f]</code> video format code. see <NuxtLink
|
||||
href="https://github.com/yt-dlp/yt-dlp?tab=readme-ov-file#format-selection" target="blank">this
|
||||
url</NuxtLink> for more info.</span>. Note, as this key is required, you can set the value to
|
||||
<code>default</code> to let <code>yt-dlp</code> choose the best format.
|
||||
page</NuxtLink> for more info. Note, as this key is required, you can set the value to
|
||||
<code>default</code> to let <code>yt-dlp</code> choose the best format.</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -83,12 +87,12 @@
|
|||
<div class="column is-6-tablet is-12-mobile">
|
||||
<div class="field">
|
||||
<label class="label is-inline" for="folder">
|
||||
<span class="icon"><i class="fa-solid fa-folder" /></span>
|
||||
Default Download path
|
||||
</label>
|
||||
<div class="control has-icons-left">
|
||||
<div class="control">
|
||||
<input type="text" class="input" id="folder" placeholder="Leave empty to use default download path"
|
||||
v-model="form.folder" :disabled="addInProgress" list="folders">
|
||||
<span class="icon is-small is-left"><i class="fa-solid fa-folder" /></span>
|
||||
</div>
|
||||
<span class="help">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
|
|
@ -101,19 +105,19 @@
|
|||
<div class="column is-6-tablet is-12-mobile">
|
||||
<div class="field">
|
||||
<label class="label is-inline" for="output_template">
|
||||
<span class="icon"><i class="fa-solid fa-file" /></span>
|
||||
Default Output template
|
||||
</label>
|
||||
<div class="control has-icons-left">
|
||||
<div class="control">
|
||||
<input type="text" class="input" id="output_template" :disabled="addInProgress"
|
||||
placeholder="Leave empty to use default template." v-model="form.template">
|
||||
<span class="icon is-small is-left"><i class="fa-solid fa-file" /></span>
|
||||
</div>
|
||||
<span class="help">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
<span>Use this output template if non are given with URL. if not set, it will defaults to
|
||||
<code>{{ config.app.output_template }}</code>.
|
||||
For more information <NuxtLink href="https://github.com/yt-dlp/yt-dlp#output-template"
|
||||
target="_blank">visit this url</NuxtLink>.
|
||||
For more information visit <NuxtLink href="https://github.com/yt-dlp/yt-dlp#output-template"
|
||||
target="_blank">this page</NuxtLink>.
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -122,11 +126,12 @@
|
|||
<div class="column is-12">
|
||||
<div class="field">
|
||||
<label class="label is-inline" for="cli_options">
|
||||
<span class="icon"><i class="fa-solid fa-terminal" /></span>
|
||||
Command arguments for yt-dlp
|
||||
</label>
|
||||
<div class="control">
|
||||
<input type="text" class="input" v-model="form.cli" id="cli_options" :disabled="addInProgress"
|
||||
placeholder="command options to use, e.g. --no-embed-metadata --no-embed-thumbnail">
|
||||
<textarea class="textarea is-pre" v-model="form.cli" id="cli_options" :disabled="addInProgress"
|
||||
placeholder="command options to use, e.g. --no-embed-metadata --no-embed-thumbnail" />
|
||||
</div>
|
||||
<span class="help">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
|
|
@ -141,8 +146,10 @@
|
|||
|
||||
<div class="column is-12">
|
||||
<div class="field">
|
||||
<label class="label is-inline" for="cookies"
|
||||
v-tooltip="'Netscape HTTP Cookie format.'">Cookies</label>
|
||||
<label class="label is-inline" for="cookies" v-tooltip="'Netscape HTTP Cookie format.'">
|
||||
<span class="icon"><i class="fa-solid fa-cookie" /></span>
|
||||
Cookies
|
||||
</label>
|
||||
<div class="control">
|
||||
<textarea class="textarea is-pre" id="cookies" v-model="form.cookies" :disabled="addInProgress"
|
||||
placeholder="Leave empty to use default cookies" />
|
||||
|
|
@ -157,56 +164,7 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-12" v-if="has_data(form?.args) || has_data(form?.postprocessors)">
|
||||
<Message title="Deprecation Warning" class="is-background-warning-80 has-text-dark"
|
||||
icon="fas fa-exclamation-circle">
|
||||
<ul>
|
||||
<li>
|
||||
The <code>JSON yt-dlp config</code> and <code>JSON yt-dlp Post-Processors</code> fields are
|
||||
deprecated and will be removed in the future. Please use the <b>Command arguments for yt-dlp</b>
|
||||
field instead. The deprecated fields will still be working for now but they will stop, we suggest
|
||||
that you migrate to the new field. as soon as possible to avoid any issues. No support will be
|
||||
given for the deprecated fields.
|
||||
</li>
|
||||
<li>
|
||||
If both fields are set, the <b>Command arguments for yt-dlp</b> field will take precedence over
|
||||
the deprecated fields. and when you click save it will remove the deprecated fields.
|
||||
</li>
|
||||
</ul>
|
||||
</Message>
|
||||
</div>
|
||||
<div class="column is-6-tablet is-12-mobile" v-if="has_data(form?.args)">
|
||||
<div class="field">
|
||||
<label class="label is-inline" for="args" v-tooltip="'Extends current global yt-dlp config. (JSON)'">
|
||||
JSON yt-dlp config <span class="has-text-danger">(DEPRECATED)</span>
|
||||
</label>
|
||||
<div class="control">
|
||||
<textarea class="textarea" id="args" v-model="form.args" :disabled="addInProgress"
|
||||
placeholder="{}" />
|
||||
</div>
|
||||
<span class="help has-text-danger">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
<span>Deprecated, use <b>Command arguments for yt-dlp</b> field instead. </span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-6-tablet is-12-mobile" v-if="has_data(form?.postprocessors)">
|
||||
<div class="field">
|
||||
<label class="label is-inline" for="postprocessors"
|
||||
v-tooltip="'Things to do after download is done.'">
|
||||
JSON yt-dlp Post-Processors <span class="has-text-danger">(DEPRECATED)</span>
|
||||
</label>
|
||||
<div class="control">
|
||||
<textarea class="textarea" id="postprocessors" v-model="form.postprocessors"
|
||||
:disabled="addInProgress" placeholder="[]" />
|
||||
</div>
|
||||
<span class="help has-text-danger">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
<span>Deprecated, use <b>Command arguments for yt-dlp</b> field instead. </span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -319,55 +277,11 @@ const checkInfo = async () => {
|
|||
return;
|
||||
}
|
||||
|
||||
if (form?.cli && '' !== form.cli) {
|
||||
if ((has_data(copy?.args) || has_data(copy?.postprocessors))) {
|
||||
if (false === confirm('cli options are set, this will remove the JSON yt-dlp config and Post-Processors. Are you sure?')) {
|
||||
toast.warning('User cancelled the operation.')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (copy?.args) {
|
||||
delete copy.args
|
||||
}
|
||||
|
||||
if (copy?.postprocessors) {
|
||||
delete copy.postprocessors
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (typeof copy.args === 'object') {
|
||||
copy.args = JSON.stringify(copy.args, null, 2);
|
||||
}
|
||||
|
||||
if (typeof copy.postprocessors === 'object') {
|
||||
copy.postprocessors = JSON.stringify(copy.postprocessors, null, 2);
|
||||
}
|
||||
|
||||
if (copy?.args) {
|
||||
try {
|
||||
copy.args = JSON.parse(copy.args)
|
||||
} catch (e) {
|
||||
toast.error(`Invalid JSON yt-dlp config. ${e.message}`)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (copy?.postprocessors) {
|
||||
try {
|
||||
copy.postprocessors = JSON.parse(copy.postprocessors)
|
||||
} catch (e) {
|
||||
toast.error(`Invalid JSON yt-dlp Post-Processors. ${e.message}`)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// trim all fields in copy only if they are strings
|
||||
for (const key in copy) {
|
||||
if (typeof copy[key] === 'string') {
|
||||
copy[key] = copy[key].trim()
|
||||
if (typeof copy[key] !== 'string') {
|
||||
continue
|
||||
}
|
||||
copy[key] = copy[key].trim()
|
||||
}
|
||||
|
||||
emitter('submit', { reference: toRaw(props.reference), preset: toRaw(copy) });
|
||||
|
|
@ -417,18 +331,13 @@ const importItem = async () => {
|
|||
try {
|
||||
const item = JSON.parse(val)
|
||||
|
||||
console.log(item)
|
||||
|
||||
if ('preset' !== item._type) {
|
||||
toast.error(`Invalid import string. Expected type 'preset', got '${item._type}'.`)
|
||||
import_string.value = ''
|
||||
if (item?._type || 'preset' !== item._type) {
|
||||
toast.error(`Invalid import string. Expected type 'preset', got '${item._type ?? 'unknown'}'.`)
|
||||
return
|
||||
}
|
||||
|
||||
if (form.format || form.cli) {
|
||||
if (false === confirm('This will overwrite the current form fields. Are you sure?')) {
|
||||
return
|
||||
}
|
||||
if ((form.format || form.cli) && false === confirm('This will overwrite the current data. Are you sure?')) {
|
||||
return
|
||||
}
|
||||
|
||||
if (item.name) {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,10 @@
|
|||
Dark
|
||||
</label>
|
||||
</div>
|
||||
<p class="help">Select the color scheme for the WebUI.</p>
|
||||
<p class="help">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
<span>Select the color scheme for the WebUI.</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
|
|
@ -34,7 +37,10 @@
|
|||
<input id="random_bg" type="checkbox" class="switch is-success" v-model="bg_enable">
|
||||
<label for="random_bg" class="is-unselectable"> Enable</label>
|
||||
</div>
|
||||
<p class="help">Use random background image from your media backends.</p>
|
||||
<p class="help">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
<span>Use random background image.</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
|
|
@ -45,7 +51,10 @@
|
|||
<input id="random_bg_opacity" style="width: 100%" type="range" v-model="bg_opacity" min="0.50" max="1.00"
|
||||
step="0.05">
|
||||
</div>
|
||||
<p class="help">How visible the background image should be.</p>
|
||||
<p class="help">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
<span>How visible the background image should be.</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field" v-if="bg_enable">
|
||||
|
|
@ -61,7 +70,10 @@
|
|||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<p class="help">Change the displayed picture.</p>
|
||||
<p class="help">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
<span>Change the displayed picture.</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -28,13 +28,13 @@
|
|||
|
||||
<div class="column is-12" v-if="showImport || !reference">
|
||||
<label class="label is-inline" for="import_string">
|
||||
<span class="icon"><i class="fa-solid fa-file-import" /></span>
|
||||
Import string
|
||||
</label>
|
||||
|
||||
<div class="field has-addons">
|
||||
<div class="control has-icons-left is-expanded">
|
||||
<div class="control is-expanded">
|
||||
<input type="text" class="input" id="import_string" v-model="import_string" autocomplete="off">
|
||||
<span class="icon is-small is-left"><i class="fa-solid fa-t" /></span>
|
||||
</div>
|
||||
|
||||
<div class="control">
|
||||
|
|
@ -52,21 +52,27 @@
|
|||
|
||||
<div class="column is-6-tablet is-12-mobile">
|
||||
<div class="field">
|
||||
<label class="label is-inline" for="name" v-text="'Name'" />
|
||||
<label class="label is-inline" for="name">
|
||||
<span class="icon"><i class="fa-solid fa-user" /></span>
|
||||
Name
|
||||
</label>
|
||||
<div class="control has-icons-left">
|
||||
<input type="text" class="input" id="name" v-model="form.name" :disabled="addInProgress">
|
||||
<span class="icon is-small is-left"><i class="fa-solid fa-user" /></span>
|
||||
</div>
|
||||
<span class="help">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
<span>Task name is used to identify the task in the task list and in logs.</span>
|
||||
<span>The name is used to identify this specific task.</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-6-tablet is-12-mobile">
|
||||
<div class="field">
|
||||
<label class="label is-inline" for="url" v-text="'URL'" />
|
||||
<label class="label is-inline" for="url">
|
||||
<span class="icon"><i class="fa-solid fa-link" /></span>
|
||||
URL
|
||||
</label>
|
||||
<div class="control has-icons-left">
|
||||
<input type="url" class="input" id="url" v-model="form.url" :disabled="addInProgress">
|
||||
<span class="icon is-small is-left"><i class="fa-solid fa-link" /></span>
|
||||
|
|
@ -80,8 +86,11 @@
|
|||
|
||||
<div class="column is-6-tablet is-12-mobile">
|
||||
<div class="field">
|
||||
<label class="label is-inline" for="preset">Preset</label>
|
||||
<div class="control has-icons-left">
|
||||
<label class="label is-inline" for="preset">
|
||||
<span class="icon"><i class="fa-solid fa-sliders" /></span>
|
||||
Preset
|
||||
</label>
|
||||
<div class="control">
|
||||
<div class="select is-fullwidth">
|
||||
<select id="preset" class="is-fullwidth" v-model="form.preset"
|
||||
:disabled="addInProgress || hasFormatInConfig"
|
||||
|
|
@ -98,7 +107,6 @@
|
|||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
<span class="icon is-small is-left"><i class="fa-solid fa-tv" /></span>
|
||||
</div>
|
||||
<span class="help">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
|
|
@ -113,12 +121,12 @@
|
|||
<div class="column is-6-tablet is-12-mobile">
|
||||
<div class="field">
|
||||
<label class="label is-inline" for="timer">
|
||||
<span class="icon"><i class="fa-solid fa-clock" /></span>
|
||||
CRON expression timer.
|
||||
</label>
|
||||
<div class="control has-icons-left">
|
||||
<div class="control">
|
||||
<input type="text" class="input" id="timer" placeholder="leave empty to run once every hour."
|
||||
v-model="form.timer" :disabled="addInProgress">
|
||||
<span class="icon is-small is-left"><i class="fa-solid fa-clock" /></span>
|
||||
</div>
|
||||
<span class="help">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
|
|
@ -135,12 +143,12 @@
|
|||
<div class="column is-6-tablet is-12-mobile">
|
||||
<div class="field">
|
||||
<label class="label is-inline" for="folder">
|
||||
Download path
|
||||
<span class="icon"><i class="fa-solid fa-folder" /></span>
|
||||
Save in
|
||||
</label>
|
||||
<div class="control has-icons-left">
|
||||
<div class="control">
|
||||
<input type="text" class="input" id="folder" placeholder="Leave empty to use default download path"
|
||||
v-model="form.folder" :disabled="addInProgress" list="folders">
|
||||
<span class="icon is-small is-left"><i class="fa-solid fa-folder" /></span>
|
||||
</div>
|
||||
<span class="help">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
|
|
@ -153,12 +161,12 @@
|
|||
<div class="column is-6-tablet is-12-mobile">
|
||||
<div class="field">
|
||||
<label class="label is-inline" for="output_template">
|
||||
<span class="icon"><i class="fa-solid fa-file" /></span>
|
||||
Output template
|
||||
</label>
|
||||
<div class="control has-icons-left">
|
||||
<div class="control">
|
||||
<input type="text" class="input" id="output_template" :disabled="addInProgress"
|
||||
placeholder="Leave empty to use default template." v-model="form.template">
|
||||
<span class="icon is-small is-left"><i class="fa-solid fa-file" /></span>
|
||||
</div>
|
||||
<span class="help">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
|
|
@ -174,11 +182,13 @@
|
|||
<div class="column is-12">
|
||||
<div class="field">
|
||||
<label class="label is-inline" for="cli_options">
|
||||
<span class="icon"><i class="fa-solid fa-terminal" /></span>
|
||||
Command arguments for yt-dlp
|
||||
</label>
|
||||
<div class="control">
|
||||
<input type="text" class="input" v-model="form.cli" id="cli_options" :disabled="addInProgress"
|
||||
placeholder="command options to use, e.g. --no-embed-metadata --no-embed-thumbnail">
|
||||
<textarea type="text" class="textarea is-pre" v-model="form.cli" id="cli_options"
|
||||
:disabled="addInProgress"
|
||||
placeholder="command options to use, e.g. --no-embed-metadata --no-embed-thumbnail" />
|
||||
</div>
|
||||
<span class="help">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
|
|
@ -389,7 +399,7 @@ const hasFormatInConfig = computed(() => {
|
|||
return false
|
||||
}
|
||||
|
||||
return /(?<!\w)(-f|--format)(=|:)?(?!\w)/.test(form.cli)
|
||||
return /(?<!\S)(-f|--format)(=|\s)(\S+)/.test(form.cli)
|
||||
})
|
||||
|
||||
const filter_presets = (flag = true) => config.presets.filter(item => item.default === flag)
|
||||
|
|
|
|||
|
|
@ -154,6 +154,16 @@ onUnmounted(() => {
|
|||
if (title.value) {
|
||||
window.document.title = 'YTPTube'
|
||||
}
|
||||
if (video.value) {
|
||||
try {
|
||||
video.value.pause()
|
||||
video.value.querySelectorAll("source").forEach(source => source.removeAttribute("src"))
|
||||
video.value.load()
|
||||
}
|
||||
catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const prepareVideoPlayer = () => {
|
||||
|
|
|
|||
|
|
@ -12,17 +12,17 @@
|
|||
"web-types": "./web-types.json",
|
||||
"dependencies": {
|
||||
"@pinia/nuxt": "^0.10.1",
|
||||
"@sentry/nuxt": "^9.9.0",
|
||||
"@sentry/nuxt": "^9.10.1",
|
||||
"@vueuse/core": "^13.0.0",
|
||||
"@vueuse/nuxt": "^13.0.0",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
"cron-parser": "^5.0.6",
|
||||
"cronstrue": "^2.49.0",
|
||||
"cron-parser": "^5.1.0",
|
||||
"cronstrue": "^2.57.0",
|
||||
"floating-vue": "^5.2.2",
|
||||
"hls.js": "^1.4.12",
|
||||
"hls.js": "^1.6.0",
|
||||
"moment": "^2.30.1",
|
||||
"nuxt": "^3.16.1",
|
||||
"nuxt": "^3.16.2",
|
||||
"pinia": "^3.0.1",
|
||||
"socket.io-client": "^4.7.2",
|
||||
"vue": "^3.4",
|
||||
|
|
|
|||
|
|
@ -71,15 +71,6 @@ div.is-centered {
|
|||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
<template v-if="has_data(item?.args) || has_data(item.postprocessors)">
|
||||
<p class="has-text-danger is-5 has-text-bold">
|
||||
<span class="icon"><i class="fa-solid fa-triangle-exclamation" /></span>
|
||||
<span>The preset is using deprecated options. It is recommended to update the preset to use the
|
||||
new options. It will cease to work in future versions.</span>
|
||||
</p>
|
||||
<hr>
|
||||
</template>
|
||||
|
||||
<p class="is-text-overflow"
|
||||
v-if="item?.format && false === ['default', 'not_set'].includes(item.format)">
|
||||
<span class="icon"><i class="fa-solid fa-f" /></span>
|
||||
|
|
@ -307,11 +298,6 @@ const exportItem = item => {
|
|||
}
|
||||
})
|
||||
|
||||
if (has_data(data?.args) || has_data(data?.postprocessors)) {
|
||||
toast.error("v1.0 presets are no longer supported target for export. Please update your preset.")
|
||||
return
|
||||
}
|
||||
|
||||
let userData = {}
|
||||
|
||||
for (const key of Object.keys(data)) {
|
||||
|
|
|
|||
1204
ui/yarn.lock
1204
ui/yarn.lock
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue