diff --git a/Pipfile b/Pipfile index a7b4ffe..e944e45 100644 --- a/Pipfile +++ b/Pipfile @@ -47,6 +47,8 @@ parsett = "*" tenacity = "*" ratelimit = "*" qrcode = "*" +scrapeops-scrapy = "*" +scrapeops-python-requests = "*" [dev-packages] pysocks = "*" diff --git a/Pipfile.lock b/Pipfile.lock index b613e13..65c223b 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "1cd404d7a48decb6239cae9c230197eb0437451f506b80cf25c4a745092c726f" + "sha256": "247ef524a84735405140690139bd5d6a2cf3e662163deaaa874cc2f3dc42ad8e" }, "pipfile-spec": 6, "requires": { @@ -41,100 +41,100 @@ }, "aiohttp": { "hashes": [ - "sha256:02d1d6610588bcd743fae827bd6f2e47e0d09b346f230824b4c6fb85c6065f9c", - "sha256:03690541e4cc866eef79626cfa1ef4dd729c5c1408600c8cb9e12e1137eed6ab", - "sha256:0bc059ecbce835630e635879f5f480a742e130d9821fbe3d2f76610a6698ee25", - "sha256:0c21c82df33b264216abffff9f8370f303dab65d8eee3767efbbd2734363f677", - "sha256:1298b854fd31d0567cbb916091be9d3278168064fca88e70b8468875ef9ff7e7", - "sha256:1321658f12b6caffafdc35cfba6c882cb014af86bef4e78c125e7e794dfb927b", - "sha256:143b0026a9dab07a05ad2dd9e46aa859bffdd6348ddc5967b42161168c24f857", - "sha256:16e6a51d8bc96b77f04a6764b4ad03eeef43baa32014fce71e882bd71302c7e4", - "sha256:172ad884bb61ad31ed7beed8be776eb17e7fb423f1c1be836d5cb357a096bf12", - "sha256:17c272cfe7b07a5bb0c6ad3f234e0c336fb53f3bf17840f66bd77b5815ab3d16", - "sha256:1a0ee6c0d590c917f1b9629371fce5f3d3f22c317aa96fbdcce3260754d7ea21", - "sha256:2746d8994ebca1bdc55a1e998feff4e94222da709623bb18f6e5cfec8ec01baf", - "sha256:2914caa46054f3b5ff910468d686742ff8cff54b8a67319d75f5d5945fd0a13d", - "sha256:2bbf94d4a0447705b7775417ca8bb8086cc5482023a6e17cdc8f96d0b1b5aba6", - "sha256:2bd9f3eac515c16c4360a6a00c38119333901b8590fe93c3257a9b536026594d", - "sha256:2c33fa6e10bb7ed262e3ff03cc69d52869514f16558db0626a7c5c61dde3c29f", - "sha256:2d37f4718002863b82c6f391c8efd4d3a817da37030a29e2682a94d2716209de", - "sha256:3668d0c2a4d23fb136a753eba42caa2c0abbd3d9c5c87ee150a716a16c6deec1", - "sha256:36d4fba838be5f083f5490ddd281813b44d69685db910907636bc5dca6322316", - "sha256:40ff5b7660f903dc587ed36ef08a88d46840182d9d4b5694e7607877ced698a1", - "sha256:42775de0ca04f90c10c5c46291535ec08e9bcc4756f1b48f02a0657febe89b10", - "sha256:482c85cf3d429844396d939b22bc2a03849cb9ad33344689ad1c85697bcba33a", - "sha256:4e6cb75f8ddd9c2132d00bc03c9716add57f4beff1263463724f6398b813e7eb", - "sha256:4edc3fd701e2b9a0d605a7b23d3de4ad23137d23fc0dbab726aa71d92f11aaaf", - "sha256:4fd16b30567c5b8e167923be6e027eeae0f20cf2b8a26b98a25115f28ad48ee0", - "sha256:5002a02c17fcfd796d20bac719981d2fca9c006aac0797eb8f430a58e9d12431", - "sha256:51d0a4901b27272ae54e42067bc4b9a90e619a690b4dc43ea5950eb3070afc32", - "sha256:558b3d223fd631ad134d89adea876e7fdb4c93c849ef195049c063ada82b7d08", - "sha256:5c070430fda1a550a1c3a4c2d7281d3b8cfc0c6715f616e40e3332201a253067", - "sha256:5f392ef50e22c31fa49b5a46af7f983fa3f118f3eccb8522063bee8bfa6755f8", - "sha256:60555211a006d26e1a389222e3fab8cd379f28e0fbf7472ee55b16c6c529e3a6", - "sha256:608cecd8d58d285bfd52dbca5b6251ca8d6ea567022c8a0eaae03c2589cd9af9", - "sha256:60ad5b8a7452c0f5645c73d4dad7490afd6119d453d302cd5b72b678a85d6044", - "sha256:63649309da83277f06a15bbdc2a54fbe75efb92caa2c25bb57ca37762789c746", - "sha256:6ebdc3b3714afe1b134b3bbeb5f745eed3ecbcff92ab25d80e4ef299e83a5465", - "sha256:6f3c6648aa123bcd73d6f26607d59967b607b0da8ffcc27d418a4b59f4c98c7c", - "sha256:7003f33f5f7da1eb02f0446b0f8d2ccf57d253ca6c2e7a5732d25889da82b517", - "sha256:776e9f3c9b377fcf097c4a04b241b15691e6662d850168642ff976780609303c", - "sha256:85711eec2d875cd88c7eb40e734c4ca6d9ae477d6f26bd2b5bb4f7f60e41b156", - "sha256:87d1e4185c5d7187684d41ebb50c9aeaaaa06ca1875f4c57593071b0409d2444", - "sha256:8a3f063b41cc06e8d0b3fcbbfc9c05b7420f41287e0cd4f75ce0a1f3d80729e6", - "sha256:8b3fb28a9ac8f2558760d8e637dbf27aef1e8b7f1d221e8669a1074d1a266bb2", - "sha256:8bd9125dd0cc8ebd84bff2be64b10fdba7dc6fd7be431b5eaf67723557de3a31", - "sha256:8be1a65487bdfc285bd5e9baf3208c2132ca92a9b4020e9f27df1b16fab998a9", - "sha256:8cc0d13b4e3b1362d424ce3f4e8c79e1f7247a00d792823ffd640878abf28e56", - "sha256:8d9d10d10ec27c0d46ddaecc3c5598c4db9ce4e6398ca872cdde0525765caa2f", - "sha256:8debb45545ad95b58cc16c3c1cc19ad82cffcb106db12b437885dbee265f0ab5", - "sha256:91aa966858593f64c8a65cdefa3d6dc8fe3c2768b159da84c1ddbbb2c01ab4ef", - "sha256:9331dd34145ff105177855017920dde140b447049cd62bb589de320fd6ddd582", - "sha256:99f9678bf0e2b1b695e8028fedac24ab6770937932eda695815d5a6618c37e04", - "sha256:9fdf5c839bf95fc67be5794c780419edb0dbef776edcfc6c2e5e2ffd5ee755fa", - "sha256:a14e4b672c257a6b94fe934ee62666bacbc8e45b7876f9dd9502d0f0fe69db16", - "sha256:a19caae0d670771ea7854ca30df76f676eb47e0fd9b2ee4392d44708f272122d", - "sha256:a35ed3d03910785f7d9d6f5381f0c24002b2b888b298e6f941b2fc94c5055fcd", - "sha256:a61df62966ce6507aafab24e124e0c3a1cfbe23c59732987fc0fd0d71daa0b88", - "sha256:a6e00c8a92e7663ed2be6fcc08a2997ff06ce73c8080cd0df10cc0321a3168d7", - "sha256:ac3196952c673822ebed8871cf8802e17254fff2a2ed4835d9c045d9b88c5ec7", - "sha256:ac74e794e3aee92ae8f571bfeaa103a141e409863a100ab63a253b1c53b707eb", - "sha256:ad3675c126f2a95bde637d162f8231cff6bc0bc9fbe31bd78075f9ff7921e322", - "sha256:aeebd3061f6f1747c011e1d0b0b5f04f9f54ad1a2ca183e687e7277bef2e0da2", - "sha256:ba1a599255ad6a41022e261e31bc2f6f9355a419575b391f9655c4d9e5df5ff5", - "sha256:bbdb8def5268f3f9cd753a265756f49228a20ed14a480d151df727808b4531dd", - "sha256:c2555e4949c8d8782f18ef20e9d39730d2656e218a6f1a21a4c4c0b56546a02e", - "sha256:c2695c61cf53a5d4345a43d689f37fc0f6d3a2dc520660aec27ec0f06288d1f9", - "sha256:c2b627d3c8982691b06d89d31093cee158c30629fdfebe705a91814d49b554f8", - "sha256:c46131c6112b534b178d4e002abe450a0a29840b61413ac25243f1291613806a", - "sha256:c54dc329cd44f7f7883a9f4baaefe686e8b9662e2c6c184ea15cceee587d8d69", - "sha256:c7d7cafc11d70fdd8801abfc2ff276744ae4cb39d8060b6b542c7e44e5f2cfc2", - "sha256:cb0b2d5d51f96b6cc19e6ab46a7b684be23240426ae951dcdac9639ab111b45e", - "sha256:d15a29424e96fad56dc2f3abed10a89c50c099f97d2416520c7a543e8fddf066", - "sha256:d1f5c9169e26db6a61276008582d945405b8316aae2bb198220466e68114a0f5", - "sha256:d271f770b52e32236d945911b2082f9318e90ff835d45224fa9e28374303f729", - "sha256:d646fdd74c25bbdd4a055414f0fe32896c400f38ffbdfc78c68e62812a9e0257", - "sha256:d6e395c3d1f773cf0651cd3559e25182eb0c03a2777b53b4575d8adc1149c6e9", - "sha256:d7c071235a47d407b0e93aa6262b49422dbe48d7d8566e1158fecc91043dd948", - "sha256:d97273a52d7f89a75b11ec386f786d3da7723d7efae3034b4dda79f6f093edc1", - "sha256:dcf354661f54e6a49193d0b5653a1b011ba856e0b7a76bda2c33e4c6892f34ea", - "sha256:e3e7fabedb3fe06933f47f1538df7b3a8d78e13d7167195f51ca47ee12690373", - "sha256:e525b69ee8a92c146ae5b4da9ecd15e518df4d40003b01b454ad694a27f498b5", - "sha256:e709d6ac598c5416f879bb1bae3fd751366120ac3fa235a01de763537385d036", - "sha256:e83dfefb4f7d285c2d6a07a22268344a97d61579b3e0dce482a5be0251d672ab", - "sha256:e86260b76786c28acf0b5fe31c8dca4c2add95098c709b11e8c35b424ebd4f5b", - "sha256:e883b61b75ca6efc2541fcd52a5c8ccfe288b24d97e20ac08fdf343b8ac672ea", - "sha256:f0a44bb40b6aaa4fb9a5c1ee07880570ecda2065433a96ccff409c9c20c1624a", - "sha256:f82ace0ec57c94aaf5b0e118d4366cff5889097412c75aa14b4fd5fc0c44ee3e", - "sha256:f9ca09414003c0e96a735daa1f071f7d7ed06962ef4fa29ceb6c80d06696d900", - "sha256:fa430b871220dc62572cef9c69b41e0d70fcb9d486a4a207a5de4c1f25d82593", - "sha256:fc262c3df78c8ff6020c782d9ce02e4bcffe4900ad71c0ecdad59943cba54442", - "sha256:fcd546782d03181b0b1d20b43d612429a90a68779659ba8045114b867971ab71", - "sha256:fd4ceeae2fb8cabdd1b71c82bfdd39662473d3433ec95b962200e9e752fb70d0", - "sha256:fec5fac7aea6c060f317f07494961236434928e6f4374e170ef50b3001e14581" + "sha256:007ec22fbc573e5eb2fb7dec4198ef8f6bf2fe4ce20020798b2eb5d0abda6138", + "sha256:00819de9e45d42584bed046314c40ea7e9aea95411b38971082cad449392b08c", + "sha256:01948b1d570f83ee7bbf5a60ea2375a89dfb09fd419170e7f5af029510033d24", + "sha256:038f514fe39e235e9fef6717fbf944057bfa24f9b3db9ee551a7ecf584b5b480", + "sha256:03a42ac7895406220124c88911ebee31ba8b2d24c98507f4a8bf826b2937c7f2", + "sha256:05646ebe6b94cc93407b3bf34b9eb26c20722384d068eb7339de802154d61bc5", + "sha256:0631dd7c9f0822cc61c88586ca76d5b5ada26538097d0f1df510b082bad3411a", + "sha256:0b00807e2605f16e1e198f33a53ce3c4523114059b0c09c337209ae55e3823a8", + "sha256:0e1b370d8007c4ae31ee6db7f9a2fe801a42b146cec80a86766e7ad5c4a259cf", + "sha256:15ecd889a709b0080f02721255b3f80bb261c2293d3c748151274dfea93ac871", + "sha256:1b66ccafef7336a1e1f0e389901f60c1d920102315a56df85e49552308fc0486", + "sha256:1bbb122c557a16fafc10354b9d99ebf2f2808a660d78202f10ba9d50786384b9", + "sha256:1eb89d3d29adaf533588f209768a9c02e44e4baf832b08118749c5fad191781d", + "sha256:258c5dd01afc10015866114e210fb7365f0d02d9d059c3c3415382ab633fcbcb", + "sha256:2609e9ab08474702cc67b7702dbb8a80e392c54613ebe80db7e8dbdb79837c68", + "sha256:274cfa632350225ce3fdeb318c23b4a10ec25c0e2c880eff951a3842cf358ac1", + "sha256:28529e08fde6f12eba8677f5a8608500ed33c086f974de68cc65ab218713a59d", + "sha256:2b606353da03edcc71130b52388d25f9a30a126e04caef1fd637e31683033abd", + "sha256:30ca7c3b94708a9d7ae76ff281b2f47d8eaf2579cd05971b5dc681db8caac6e1", + "sha256:333cf6cf8e65f6a1e06e9eb3e643a0c515bb850d470902274239fea02033e9a8", + "sha256:3455522392fb15ff549d92fbf4b73b559d5e43dc522588f7eb3e54c3f38beee7", + "sha256:362f641f9071e5f3ee6f8e7d37d5ed0d95aae656adf4ef578313ee585b585959", + "sha256:3bcd391d083f636c06a68715e69467963d1f9600f85ef556ea82e9ef25f043f7", + "sha256:3dffb610a30d643983aeb185ce134f97f290f8935f0abccdd32c77bed9388b42", + "sha256:3fe407bf93533a6fa82dece0e74dbcaaf5d684e5a51862887f9eaebe6372cd79", + "sha256:413251f6fcf552a33c981c4709a6bba37b12710982fec8e558ae944bfb2abd38", + "sha256:438cd072f75bb6612f2aca29f8bd7cdf6e35e8f160bc312e49fbecab77c99e3a", + "sha256:4470c73c12cd9109db8277287d11f9dd98f77fc54155fc71a7738a83ffcc8ea8", + "sha256:45c3b868724137f713a38376fef8120c166d1eadd50da1855c112fe97954aed8", + "sha256:486f7aabfa292719a2753c016cc3a8f8172965cabb3ea2e7f7436c7f5a22a151", + "sha256:4f05e9727ce409358baa615dbeb9b969db94324a79b5a5cea45d39bdb01d82e6", + "sha256:50aed5155f819873d23520919e16703fc8925e509abbb1a1491b0087d1cd969e", + "sha256:50edbcad60d8f0e3eccc68da67f37268b5144ecc34d59f27a02f9611c1d4eec7", + "sha256:54ca74df1be3c7ca1cf7f4c971c79c2daf48d9aa65dea1a662ae18926f5bc8ce", + "sha256:578a4b875af3e0daaf1ac6fa983d93e0bbfec3ead753b6d6f33d467100cdc67b", + "sha256:597a079284b7ee65ee102bc3a6ea226a37d2b96d0418cc9047490f231dc09fe8", + "sha256:59bb3c54aa420521dc4ce3cc2c3fe2ad82adf7b09403fa1f48ae45c0cbde6628", + "sha256:5c6a5b8c7926ba5d8545c7dd22961a107526562da31a7a32fa2456baf040939f", + "sha256:64f6c17757251e2b8d885d728b6433d9d970573586a78b78ba8929b0f41d045a", + "sha256:679abe5d3858b33c2cf74faec299fda60ea9de62916e8b67e625d65bf069a3b7", + "sha256:741a46d58677d8c733175d7e5aa618d277cd9d880301a380fd296975a9cdd7bc", + "sha256:7789050d9e5d0c309c706953e5e8876e38662d57d45f936902e176d19f1c58ab", + "sha256:77abf6665ae54000b98b3c742bc6ea1d1fb31c394bcabf8b5d2c1ac3ebfe7f3b", + "sha256:79019094f87c9fb44f8d769e41dbb664d6e8fcfd62f665ccce36762deaa0e911", + "sha256:7b06b7843929e41a94ea09eb1ce3927865387e3e23ebe108e0d0d09b08d25be9", + "sha256:7e338c0523d024fad378b376a79faff37fafb3c001872a618cde1d322400a572", + "sha256:7ea7ffc6d6d6f8a11e6f40091a1040995cdff02cfc9ba4c2f30a516cb2633554", + "sha256:8105fd8a890df77b76dd3054cddf01a879fc13e8af576805d667e0fa0224c35d", + "sha256:84afcdea18eda514c25bc68b9af2a2b1adea7c08899175a51fe7c4fb6d551257", + "sha256:9294bbb581f92770e6ed5c19559e1e99255e4ca604a22c5c6397b2f9dd3ee42c", + "sha256:93429602396f3383a797a2a70e5f1de5df8e35535d7806c9f91df06f297e109b", + "sha256:9627cc1a10c8c409b5822a92d57a77f383b554463d1884008e051c32ab1b3742", + "sha256:998f3bd3cfc95e9424a6acd7840cbdd39e45bc09ef87533c006f94ac47296090", + "sha256:9c72109213eb9d3874f7ac8c0c5fa90e072d678e117d9061c06e30c85b4cf0e6", + "sha256:9fc1500fd2a952c5c8e3b29aaf7e3cc6e27e9cfc0a8819b3bce48cc1b849e4cc", + "sha256:a3f00003de6eba42d6e94fabb4125600d6e484846dbf90ea8e48a800430cc142", + "sha256:a45d85cf20b5e0d0aa5a8dca27cce8eddef3292bc29d72dcad1641f4ed50aa16", + "sha256:a7d8d14fe962153fc681f6366bdec33d4356f98a3e3567782aac1b6e0e40109a", + "sha256:a8fa23fe62c436ccf23ff930149c047f060c7126eae3ccea005f0483f27b2e28", + "sha256:aa6658732517ddabe22c9036479eabce6036655ba87a0224c612e1ae6af2087e", + "sha256:aafc8ee9b742ce75044ae9a4d3e60e3d918d15a4c2e08a6c3c3e38fa59b92d94", + "sha256:ab5a5a0c7a7991d90446a198689c0535be89bbd6b410a1f9a66688f0880ec026", + "sha256:acd48d5b80ee80f9432a165c0ac8cbf9253eaddb6113269a5e18699b33958dbb", + "sha256:ad7593bb24b2ab09e65e8a1d385606f0f47c65b5a2ae6c551db67d6653e78c28", + "sha256:baa42524a82f75303f714108fea528ccacf0386af429b69fff141ffef1c534f9", + "sha256:bdfcf6443637c148c4e1a20c48c566aa694fa5e288d34b20fcdc58507882fed3", + "sha256:be7443669ae9c016b71f402e43208e13ddf00912f47f623ee5994e12fc7d4b3f", + "sha256:c02a30b904282777d872266b87b20ed8cc0d1501855e27f831320f471d54d983", + "sha256:c1277cd707c465cd09572a774559a3cc7c7a28802eb3a2a9472588f062097205", + "sha256:c30a0eafc89d28e7f959281b58198a9fa5e99405f716c0289b7892ca345fe45f", + "sha256:c5ce2ce7c997e1971b7184ee37deb6ea9922ef5163c6ee5aa3c274b05f9e12fa", + "sha256:c823bc3971c44ab93e611ab1a46b1eafeae474c0c844aff4b7474287b75fe49c", + "sha256:ce0cdc074d540265bfeb31336e678b4e37316849d13b308607efa527e981f5c2", + "sha256:d1720b4f14c78a3089562b8875b53e36b51c97c51adc53325a69b79b4b48ebcb", + "sha256:d183cf9c797a5291e8301790ed6d053480ed94070637bfaad914dd38b0981f67", + "sha256:d9010c31cd6fa59438da4e58a7f19e4753f7f264300cd152e7f90d4602449762", + "sha256:d9e5e4a85bdb56d224f412d9c98ae4cbd032cc4f3161818f692cd81766eee65a", + "sha256:da1dee8948d2137bb51fbb8a53cce6b1bcc86003c6b42565f008438b806cccd8", + "sha256:df9270660711670e68803107d55c2b5949c2e0f2e4896da176e1ecfc068b974a", + "sha256:e00e3505cd80440f6c98c6d69269dcc2a119f86ad0a9fd70bccc59504bebd68a", + "sha256:e48d5021a84d341bcaf95c8460b152cfbad770d28e5fe14a768988c461b821bc", + "sha256:e7f8b04d83483577fd9200461b057c9f14ced334dcb053090cea1da9c8321a91", + "sha256:edfe3341033a6b53a5c522c802deb2079eee5cbfbb0af032a55064bd65c73a23", + "sha256:ef9c33cc5cbca35808f6c74be11eb7f5f6b14d2311be84a15b594bd3e58b5527", + "sha256:f2d4324a98062be0525d16f768a03e0bbb3b9fe301ceee99611dc9a7953124e6", + "sha256:f3935f82f6f4a3820270842e90456ebad3af15810cf65932bd24da4463bc0a4c", + "sha256:f614ab0c76397661b90b6851a030004dac502e48260ea10f2441abd2207fbcc7", + "sha256:f7db54c7914cc99d901d93a34704833568d86c20925b2762f9fa779f9cd2e70f", + "sha256:fbc6264158392bad9df19537e872d476f7c57adf718944cc1e4495cbabf38e2a", + "sha256:fe2fb38c2ed905a2582948e2de560675e9dfbee94c6d5ccdb1301c6d0a5bf092", + "sha256:ffe595f10566f8276b76dc3a11ae4bb7eba1aac8ddd75811736a15b0d5311414" ], "markers": "python_version >= '3.8'", - "version": "==3.10.9" + "version": "==3.10.10" }, "aioqbt": { "git": "git+https://github.com/mhdzumair/aioqbt.git", @@ -209,12 +209,12 @@ }, "beanie": { "hashes": [ - "sha256:54016f4ec71ed0ea6ce0c7946a395090c45687f254dbbe1cf06eec608383f790", - "sha256:b45926c01d4a899c519c665c2a5f230990717e99f7fd68172a389ca33e7693b9" + "sha256:2cc6762bdd59b9040dd004ecbc7d4fd5ddd22e52743915e38d1f0f92f276bcaf", + "sha256:a5eee40f1e52214afeb8558c0823d7504856884770c3d56fc3cd5765efb87314" ], "index": "pypi", "markers": "python_version >= '3.7' and python_version < '4.0'", - "version": "==1.26.0" + "version": "==1.27.0" }, "beautifulsoup4": { "hashes": [ @@ -315,99 +315,114 @@ }, "charset-normalizer": { "hashes": [ - "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", - "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", - "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", - "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", - "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", - "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", - "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", - "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", - "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", - "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", - "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", - "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", - "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", - "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", - "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", - "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", - "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", - "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", - "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", - "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", - "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", - "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", - "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", - "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", - "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", - "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", - "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", - "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", - "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", - "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", - "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", - "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", - "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", - "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", - "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", - "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", - "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", - "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", - "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", - "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", - "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", - "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", - "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", - "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", - "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", - "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", - "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", - "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", - "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", - "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", - "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", - "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", - "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", - "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", - "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", - "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", - "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", - "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", - "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", - "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", - "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", - "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", - "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", - "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", - "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", - "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", - "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", - "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", - "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", - "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", - "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", - "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", - "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", - "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", - "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", - "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", - "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", - "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", - "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", - "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", - "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", - "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", - "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", - "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", - "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", - "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", - "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", - "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", - "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", - "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" + "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621", + "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6", + "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8", + "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912", + "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c", + "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b", + "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d", + "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d", + "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95", + "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e", + "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565", + "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", + "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab", + "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be", + "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", + "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907", + "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0", + "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2", + "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62", + "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62", + "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", + "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc", + "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284", + "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca", + "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455", + "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858", + "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b", + "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594", + "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", + "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db", + "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", + "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea", + "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6", + "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", + "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749", + "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7", + "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd", + "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99", + "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242", + "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee", + "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129", + "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2", + "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51", + "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee", + "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8", + "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", + "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613", + "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742", + "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe", + "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3", + "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5", + "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631", + "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7", + "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15", + "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c", + "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea", + "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417", + "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", + "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", + "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca", + "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa", + "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99", + "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149", + "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41", + "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574", + "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0", + "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f", + "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", + "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654", + "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3", + "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19", + "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", + "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578", + "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9", + "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1", + "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51", + "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719", + "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236", + "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a", + "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c", + "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade", + "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944", + "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc", + "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6", + "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6", + "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27", + "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6", + "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2", + "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12", + "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf", + "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", + "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7", + "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf", + "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", + "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b", + "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", + "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03", + "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4", + "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", + "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365", + "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a", + "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748", + "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b", + "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", + "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482" ], "markers": "python_full_version >= '3.7.0'", - "version": "==3.3.2" + "version": "==3.4.0" }, "cinemagoer": { "editable": true, @@ -494,20 +509,20 @@ }, "curl-cffi": { "hashes": [ - "sha256:24b084e5b0046c76a58902c2b985bdb2638d9b16af130b33b521a4c6d59792d6", - "sha256:2caa3cd83ec2cb3ad5729311184324b03db181a0f1ca6a3abd25ba9319562396", - "sha256:3756d31635f62004cc3b2280f9001b03294eacb58b4017f27cc2b645b2251880", - "sha256:3ca68890e839b35134e063d7c57684e14a6bca5f68948f8a689e64569abe5a40", - "sha256:3ec154c1f516364d9c52e6045be78724fc3eef711b6721cc855d32f1ad6a69c5", - "sha256:4671f367671038b14ea2c25f49e27402686c602d0d640ac7d484ac5c1f66b538", - "sha256:83f22b65fddbc46d330e49bd0a44f73112d3f0a10378e8b31d5123df9db9fc72", - "sha256:a4f58b83eab8a77f788444e347a0b4c877cf327ecb60299b5fa01e5c73b294ee", - "sha256:afa1230a4833246f45deb404b13676ac29c37476ec94bae0005dbde85f96be35", - "sha256:ea2ebed4ff052fcd7417cfd20fdab9415eb47ea06ca0c31896971a1c768c9bf3" + "sha256:39e195a13b95bd7e4a1e8eb7804807b4504c462f6886fde01dfe4b7552fd965e", + "sha256:3f9373ce27b20e65fb1e6cbea6e4411edca1f3b4440be68090b262eece728544", + "sha256:43367efb8d48e9997cf7591084ed7529409ad98bb67e284ae152f2a15e4ee68e", + "sha256:890f1b0e1454977ff6bd388d29eb1eafb76bb8ef63d3c8b7539aafd4808a6cec", + "sha256:901012e21af899bdf1278fd9fcee5aad6931ed56e1f5a620ffa90220c9e79f10", + "sha256:9a90bfd0bcc7bf0f30ef6de6f4e612ea1bdc238a48928845532109a0273f6275", + "sha256:a357d3da6aa8ccd41f1e6e422b9ff09ad4cc99b23b8f6299998cbfee6e690f01", + "sha256:b5520bcf6284417e66c82728512e344b50bc0e9d8bd7949923b30558746b49a3", + "sha256:ec6718bc5151b3e0ecc35aaf40078a39cc239405182c63fc95933eb7bff572dd", + "sha256:fe01be42c27667353028ee06802cc32e3936496e645a39175db4cf7bea45bb10" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==0.7.2" + "version": "==0.7.3" }, "defusedxml": { "hashes": [ @@ -537,12 +552,12 @@ }, "dnspython": { "hashes": [ - "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50", - "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc" + "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", + "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1" ], "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==2.6.1" + "markers": "python_version >= '3.9'", + "version": "==2.7.0" }, "dramatiq": { "extras": [ @@ -949,8 +964,8 @@ }, "humanize": { "git": "git+https://github.com/python-humanize/humanize.git", - "markers": "python_version >= '3.8'", - "ref": "19b43d3b8d80959d75097d8e145690be811465bd" + "markers": "python_version >= '3.9'", + "ref": "073a00b4cae648cb79e494eefb0389f5b038ffce" }, "hyperlink": { "hashes": [ @@ -1008,6 +1023,14 @@ "markers": "python_version >= '3.7'", "version": "==1.0.1" }, + "json5": { + "hashes": [ + "sha256:34ed7d834b1341a86987ed52f3f76cd8ee184394906b6e22a1e0deb9ab294e8f", + "sha256:548e41b9be043f9426776f05df8635a00fe06104ea51ed24b67f908856e151ae" + ], + "markers": "python_version >= '3.8'", + "version": "==0.9.25" + }, "jsonschema": { "hashes": [ "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", @@ -1018,11 +1041,11 @@ }, "jsonschema-specifications": { "hashes": [ - "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc", - "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c" + "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272", + "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf" ], - "markers": "python_version >= '3.8'", - "version": "==2023.12.1" + "markers": "python_version >= '3.9'", + "version": "==2024.10.1" }, "lazy-model": { "hashes": [ @@ -1186,69 +1209,70 @@ }, "markupsafe": { "hashes": [ - "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf", - "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff", - "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f", - "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3", - "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532", - "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f", - "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617", - "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df", - "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4", - "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906", - "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f", - "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4", - "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8", - "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371", - "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2", - "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465", - "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52", - "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6", - "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169", - "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad", - "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2", - "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0", - "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029", - "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f", - "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a", - "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced", - "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5", - "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c", - "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf", - "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9", - "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb", - "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad", - "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3", - "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1", - "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46", - "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc", - "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a", - "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee", - "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900", - "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5", - "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea", - "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f", - "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5", - "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e", - "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a", - "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f", - "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50", - "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a", - "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b", - "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4", - "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff", - "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2", - "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46", - "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b", - "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf", - "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5", - "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5", - "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab", - "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd", - "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68" + "sha256:0778de17cff1acaeccc3ff30cd99a3fd5c50fc58ad3d6c0e0c4c58092b859396", + "sha256:0f84af7e813784feb4d5e4ff7db633aba6c8ca64a833f61d8e4eade234ef0c38", + "sha256:17b2aea42a7280db02ac644db1d634ad47dcc96faf38ab304fe26ba2680d359a", + "sha256:242d6860f1fd9191aef5fae22b51c5c19767f93fb9ead4d21924e0bcb17619d8", + "sha256:244dbe463d5fb6d7ce161301a03a6fe744dac9072328ba9fc82289238582697b", + "sha256:26627785a54a947f6d7336ce5963569b5d75614619e75193bdb4e06e21d447ad", + "sha256:2a4b34a8d14649315c4bc26bbfa352663eb51d146e35eef231dd739d54a5430a", + "sha256:2ae99f31f47d849758a687102afdd05bd3d3ff7dbab0a8f1587981b58a76152a", + "sha256:312387403cd40699ab91d50735ea7a507b788091c416dd007eac54434aee51da", + "sha256:3341c043c37d78cc5ae6e3e305e988532b072329639007fd408a476642a89fd6", + "sha256:33d1c36b90e570ba7785dacd1faaf091203d9942bc036118fab8110a401eb1a8", + "sha256:3e683ee4f5d0fa2dde4db77ed8dd8a876686e3fc417655c2ece9a90576905344", + "sha256:3ffb4a8e7d46ed96ae48805746755fadd0909fea2306f93d5d8233ba23dda12a", + "sha256:40621d60d0e58aa573b68ac5e2d6b20d44392878e0bfc159012a5787c4e35bc8", + "sha256:40f1e10d51c92859765522cbd79c5c8989f40f0419614bcdc5015e7b6bf97fc5", + "sha256:45d42d132cff577c92bfba536aefcfea7e26efb975bd455db4e6602f5c9f45e7", + "sha256:48488d999ed50ba8d38c581d67e496f955821dc183883550a6fbc7f1aefdc170", + "sha256:4935dd7883f1d50e2ffecca0aa33dc1946a94c8f3fdafb8df5c330e48f71b132", + "sha256:4c2d64fdba74ad16138300815cfdc6ab2f4647e23ced81f59e940d7d4a1469d9", + "sha256:4c8817557d0de9349109acb38b9dd570b03cc5014e8aabf1cbddc6e81005becd", + "sha256:4ffaaac913c3f7345579db4f33b0020db693f302ca5137f106060316761beea9", + "sha256:5a4cb365cb49b750bdb60b846b0c0bc49ed62e59a76635095a179d440540c346", + "sha256:62fada2c942702ef8952754abfc1a9f7658a4d5460fabe95ac7ec2cbe0d02abc", + "sha256:67c519635a4f64e495c50e3107d9b4075aec33634272b5db1cde839e07367589", + "sha256:6a54c43d3ec4cf2a39f4387ad044221c66a376e58c0d0e971d47c475ba79c6b5", + "sha256:7044312a928a66a4c2a22644147bc61a199c1709712069a344a3fb5cfcf16915", + "sha256:730d86af59e0e43ce277bb83970530dd223bf7f2a838e086b50affa6ec5f9295", + "sha256:800100d45176652ded796134277ecb13640c1a537cad3b8b53da45aa96330453", + "sha256:80fcbf3add8790caddfab6764bde258b5d09aefbe9169c183f88a7410f0f6dea", + "sha256:82b5dba6eb1bcc29cc305a18a3c5365d2af06ee71b123216416f7e20d2a84e5b", + "sha256:852dc840f6d7c985603e60b5deaae1d89c56cb038b577f6b5b8c808c97580f1d", + "sha256:8ad4ad1429cd4f315f32ef263c1342166695fad76c100c5d979c45d5570ed58b", + "sha256:8ae369e84466aa70f3154ee23c1451fda10a8ee1b63923ce76667e3077f2b0c4", + "sha256:93e8248d650e7e9d49e8251f883eed60ecbc0e8ffd6349e18550925e31bd029b", + "sha256:973a371a55ce9ed333a3a0f8e0bcfae9e0d637711534bcb11e130af2ab9334e7", + "sha256:9ba25a71ebf05b9bb0e2ae99f8bc08a07ee8e98c612175087112656ca0f5c8bf", + "sha256:a10860e00ded1dd0a65b83e717af28845bb7bd16d8ace40fe5531491de76b79f", + "sha256:a4792d3b3a6dfafefdf8e937f14906a51bd27025a36f4b188728a73382231d91", + "sha256:a7420ceda262dbb4b8d839a4ec63d61c261e4e77677ed7c66c99f4e7cb5030dd", + "sha256:ad91738f14eb8da0ff82f2acd0098b6257621410dcbd4df20aaa5b4233d75a50", + "sha256:b6a387d61fe41cdf7ea95b38e9af11cfb1a63499af2759444b99185c4ab33f5b", + "sha256:b954093679d5750495725ea6f88409946d69cfb25ea7b4c846eef5044194f583", + "sha256:bbde71a705f8e9e4c3e9e33db69341d040c827c7afa6789b14c6e16776074f5a", + "sha256:beeebf760a9c1f4c07ef6a53465e8cfa776ea6a2021eda0d0417ec41043fe984", + "sha256:c91b394f7601438ff79a4b93d16be92f216adb57d813a78be4446fe0f6bc2d8c", + "sha256:c97ff7fedf56d86bae92fa0a646ce1a0ec7509a7578e1ed238731ba13aabcd1c", + "sha256:cb53e2a99df28eee3b5f4fea166020d3ef9116fdc5764bc5117486e6d1211b25", + "sha256:cbf445eb5628981a80f54087f9acdbf84f9b7d862756110d172993b9a5ae81aa", + "sha256:d06b24c686a34c86c8c1fba923181eae6b10565e4d80bdd7bc1c8e2f11247aa4", + "sha256:d98e66a24497637dd31ccab090b34392dddb1f2f811c4b4cd80c230205c074a3", + "sha256:db15ce28e1e127a0013dfb8ac243a8e392db8c61eae113337536edb28bdc1f97", + "sha256:db842712984e91707437461930e6011e60b39136c7331e971952bb30465bc1a1", + "sha256:e24bfe89c6ac4c31792793ad9f861b8f6dc4546ac6dc8f1c9083c7c4f2b335cd", + "sha256:e81c52638315ff4ac1b533d427f50bc0afc746deb949210bc85f05d4f15fd772", + "sha256:e9393357f19954248b00bed7c56f29a25c930593a77630c719653d51e7669c2a", + "sha256:ee3941769bd2522fe39222206f6dd97ae83c442a94c90f2b7a25d847d40f4729", + "sha256:f31ae06f1328595d762c9a2bf29dafd8621c7d3adc130cbb46278079758779ca", + "sha256:f94190df587738280d544971500b9cafc9b950d32efcb1fba9ac10d84e6aa4e6", + "sha256:fa7d686ed9883f3d664d39d5a8e74d3c5f63e603c2e3ff0abcba23eac6542635", + "sha256:fb532dd9900381d2e8f48172ddc5a59db4c445a11b9fab40b3b786da40d3b56b", + "sha256:fe32482b37b4b00c7a52a07211b479653b7fe4f22b2e481b9a9b099d8a430f2f" ], - "markers": "python_version >= '3.7'", - "version": "==2.1.5" + "markers": "python_version >= '3.9'", + "version": "==3.0.1" }, "motor": { "hashes": [ @@ -1385,7 +1409,7 @@ "pikpakapi": { "git": "git+https://github.com/mhdzumair/PikPakAPI.git", "markers": "python_full_version >= '3.8.3' and python_full_version < '4.0.0'", - "ref": "28b291ad835fdba87e7395dadb08e36b4a90baba" + "ref": "32a6533c945248fe0319997ac90b266ee2fbe8a3" }, "pillow": { "hashes": [ @@ -1496,6 +1520,110 @@ "markers": "python_version >= '3.8'", "version": "==0.21.0" }, + "propcache": { + "hashes": [ + "sha256:00181262b17e517df2cd85656fcd6b4e70946fe62cd625b9d74ac9977b64d8d9", + "sha256:0e53cb83fdd61cbd67202735e6a6687a7b491c8742dfc39c9e01e80354956763", + "sha256:1235c01ddaa80da8235741e80815ce381c5267f96cc49b1477fdcf8c047ef325", + "sha256:140fbf08ab3588b3468932974a9331aff43c0ab8a2ec2c608b6d7d1756dbb6cb", + "sha256:191db28dc6dcd29d1a3e063c3be0b40688ed76434622c53a284e5427565bbd9b", + "sha256:1e41d67757ff4fbc8ef2af99b338bfb955010444b92929e9e55a6d4dcc3c4f09", + "sha256:1ec43d76b9677637a89d6ab86e1fef70d739217fefa208c65352ecf0282be957", + "sha256:20a617c776f520c3875cf4511e0d1db847a076d720714ae35ffe0df3e440be68", + "sha256:218db2a3c297a3768c11a34812e63b3ac1c3234c3a086def9c0fee50d35add1f", + "sha256:22aa8f2272d81d9317ff5756bb108021a056805ce63dd3630e27d042c8092798", + "sha256:25a1f88b471b3bc911d18b935ecb7115dff3a192b6fef46f0bfaf71ff4f12418", + "sha256:25c8d773a62ce0451b020c7b29a35cfbc05de8b291163a7a0f3b7904f27253e6", + "sha256:2a60ad3e2553a74168d275a0ef35e8c0a965448ffbc3b300ab3a5bb9956c2162", + "sha256:2a66df3d4992bc1d725b9aa803e8c5a66c010c65c741ad901e260ece77f58d2f", + "sha256:2ccc28197af5313706511fab3a8b66dcd6da067a1331372c82ea1cb74285e036", + "sha256:2e900bad2a8456d00a113cad8c13343f3b1f327534e3589acc2219729237a2e8", + "sha256:2ee7606193fb267be4b2e3b32714f2d58cad27217638db98a60f9efb5efeccc2", + "sha256:33ac8f098df0585c0b53009f039dfd913b38c1d2edafed0cedcc0c32a05aa110", + "sha256:3444cdba6628accf384e349014084b1cacd866fbb88433cd9d279d90a54e0b23", + "sha256:363ea8cd3c5cb6679f1c2f5f1f9669587361c062e4899fce56758efa928728f8", + "sha256:375a12d7556d462dc64d70475a9ee5982465fbb3d2b364f16b86ba9135793638", + "sha256:388f3217649d6d59292b722d940d4d2e1e6a7003259eb835724092a1cca0203a", + "sha256:3947483a381259c06921612550867b37d22e1df6d6d7e8361264b6d037595f44", + "sha256:39e104da444a34830751715f45ef9fc537475ba21b7f1f5b0f4d71a3b60d7fe2", + "sha256:3c997f8c44ec9b9b0bcbf2d422cc00a1d9b9c681f56efa6ca149a941e5560da2", + "sha256:3dfafb44f7bb35c0c06eda6b2ab4bfd58f02729e7c4045e179f9a861b07c9850", + "sha256:3ebbcf2a07621f29638799828b8d8668c421bfb94c6cb04269130d8de4fb7136", + "sha256:3f88a4095e913f98988f5b338c1d4d5d07dbb0b6bad19892fd447484e483ba6b", + "sha256:439e76255daa0f8151d3cb325f6dd4a3e93043e6403e6491813bcaaaa8733887", + "sha256:4569158070180c3855e9c0791c56be3ceeb192defa2cdf6a3f39e54319e56b89", + "sha256:466c219deee4536fbc83c08d09115249db301550625c7fef1c5563a584c9bc87", + "sha256:4a9d9b4d0a9b38d1c391bb4ad24aa65f306c6f01b512e10a8a34a2dc5675d348", + "sha256:4c7dde9e533c0a49d802b4f3f218fa9ad0a1ce21f2c2eb80d5216565202acab4", + "sha256:53d1bd3f979ed529f0805dd35ddaca330f80a9a6d90bc0121d2ff398f8ed8861", + "sha256:55346705687dbd7ef0d77883ab4f6fabc48232f587925bdaf95219bae072491e", + "sha256:56295eb1e5f3aecd516d91b00cfd8bf3a13991de5a479df9e27dd569ea23959c", + "sha256:56bb5c98f058a41bb58eead194b4db8c05b088c93d94d5161728515bd52b052b", + "sha256:5a5b3bb545ead161be780ee85a2b54fdf7092815995661947812dde94a40f6fb", + "sha256:5f2564ec89058ee7c7989a7b719115bdfe2a2fb8e7a4543b8d1c0cc4cf6478c1", + "sha256:608cce1da6f2672a56b24a015b42db4ac612ee709f3d29f27a00c943d9e851de", + "sha256:63f13bf09cc3336eb04a837490b8f332e0db41da66995c9fd1ba04552e516354", + "sha256:662dd62358bdeaca0aee5761de8727cfd6861432e3bb828dc2a693aa0471a563", + "sha256:676135dcf3262c9c5081cc8f19ad55c8a64e3f7282a21266d05544450bffc3a5", + "sha256:67aeb72e0f482709991aa91345a831d0b707d16b0257e8ef88a2ad246a7280bf", + "sha256:67b69535c870670c9f9b14a75d28baa32221d06f6b6fa6f77a0a13c5a7b0a5b9", + "sha256:682a7c79a2fbf40f5dbb1eb6bfe2cd865376deeac65acf9beb607505dced9e12", + "sha256:6994984550eaf25dd7fc7bd1b700ff45c894149341725bb4edc67f0ffa94efa4", + "sha256:69d3a98eebae99a420d4b28756c8ce6ea5a29291baf2dc9ff9414b42676f61d5", + "sha256:6e2e54267980349b723cff366d1e29b138b9a60fa376664a157a342689553f71", + "sha256:73e4b40ea0eda421b115248d7e79b59214411109a5bc47d0d48e4c73e3b8fcf9", + "sha256:74acd6e291f885678631b7ebc85d2d4aec458dd849b8c841b57ef04047833bed", + "sha256:7665f04d0c7f26ff8bb534e1c65068409bf4687aa2534faf7104d7182debb336", + "sha256:7735e82e3498c27bcb2d17cb65d62c14f1100b71723b68362872bca7d0913d90", + "sha256:77a86c261679ea5f3896ec060be9dc8e365788248cc1e049632a1be682442063", + "sha256:7cf18abf9764746b9c8704774d8b06714bcb0a63641518a3a89c7f85cc02c2ad", + "sha256:83928404adf8fb3d26793665633ea79b7361efa0287dfbd372a7e74311d51ee6", + "sha256:8e40876731f99b6f3c897b66b803c9e1c07a989b366c6b5b475fafd1f7ba3fb8", + "sha256:8f188cfcc64fb1266f4684206c9de0e80f54622c3f22a910cbd200478aeae61e", + "sha256:91997d9cb4a325b60d4e3f20967f8eb08dfcb32b22554d5ef78e6fd1dda743a2", + "sha256:91ee8fc02ca52e24bcb77b234f22afc03288e1dafbb1f88fe24db308910c4ac7", + "sha256:92fe151145a990c22cbccf9ae15cae8ae9eddabfc949a219c9f667877e40853d", + "sha256:945db8ee295d3af9dbdbb698cce9bbc5c59b5c3fe328bbc4387f59a8a35f998d", + "sha256:9517d5e9e0731957468c29dbfd0f976736a0e55afaea843726e887f36fe017df", + "sha256:952e0d9d07609d9c5be361f33b0d6d650cd2bae393aabb11d9b719364521984b", + "sha256:97a58a28bcf63284e8b4d7b460cbee1edaab24634e82059c7b8c09e65284f178", + "sha256:97e48e8875e6c13909c800fa344cd54cc4b2b0db1d5f911f840458a500fde2c2", + "sha256:9e0f07b42d2a50c7dd2d8675d50f7343d998c64008f1da5fef888396b7f84630", + "sha256:a3dc1a4b165283bd865e8f8cb5f0c64c05001e0718ed06250d8cac9bec115b48", + "sha256:a3ebe9a75be7ab0b7da2464a77bb27febcb4fab46a34f9288f39d74833db7f61", + "sha256:a64e32f8bd94c105cc27f42d3b658902b5bcc947ece3c8fe7bc1b05982f60e89", + "sha256:a6ed8db0a556343d566a5c124ee483ae113acc9a557a807d439bcecc44e7dfbb", + "sha256:ad9c9b99b05f163109466638bd30ada1722abb01bbb85c739c50b6dc11f92dc3", + "sha256:b33d7a286c0dc1a15f5fc864cc48ae92a846df287ceac2dd499926c3801054a6", + "sha256:bc092ba439d91df90aea38168e11f75c655880c12782facf5cf9c00f3d42b562", + "sha256:c436130cc779806bdf5d5fae0d848713105472b8566b75ff70048c47d3961c5b", + "sha256:c5869b8fd70b81835a6f187c5fdbe67917a04d7e52b6e7cc4e5fe39d55c39d58", + "sha256:c5ecca8f9bab618340c8e848d340baf68bcd8ad90a8ecd7a4524a81c1764b3db", + "sha256:cfac69017ef97db2438efb854edf24f5a29fd09a536ff3a992b75990720cdc99", + "sha256:d2f0d0f976985f85dfb5f3d685697ef769faa6b71993b46b295cdbbd6be8cc37", + "sha256:d5bed7f9805cc29c780f3aee05de3262ee7ce1f47083cfe9f77471e9d6777e83", + "sha256:d6a21ef516d36909931a2967621eecb256018aeb11fc48656e3257e73e2e247a", + "sha256:d9b6ddac6408194e934002a69bcaadbc88c10b5f38fb9307779d1c629181815d", + "sha256:db47514ffdbd91ccdc7e6f8407aac4ee94cc871b15b577c1c324236b013ddd04", + "sha256:df81779732feb9d01e5d513fad0122efb3d53bbc75f61b2a4f29a020bc985e70", + "sha256:e4a91d44379f45f5e540971d41e4626dacd7f01004826a18cb048e7da7e96544", + "sha256:e63e3e1e0271f374ed489ff5ee73d4b6e7c60710e1f76af5f0e1a6117cd26394", + "sha256:e70fac33e8b4ac63dfc4c956fd7d85a0b1139adcfc0d964ce288b7c527537fea", + "sha256:ecddc221a077a8132cf7c747d5352a15ed763b674c0448d811f408bf803d9ad7", + "sha256:f45eec587dafd4b2d41ac189c2156461ebd0c1082d2fe7013571598abb8505d1", + "sha256:f52a68c21363c45297aca15561812d542f8fc683c85201df0bebe209e349f793", + "sha256:f571aea50ba5623c308aa146eb650eebf7dbe0fd8c5d946e28343cb3b5aad577", + "sha256:f60f0ac7005b9f5a6091009b09a419ace1610e163fa5deaba5ce3484341840e7", + "sha256:f6475a1b2ecb310c98c28d271a30df74f9dd436ee46d09236a6b750a7599ce57", + "sha256:f6d5749fdd33d90e34c2efb174c7e236829147a2713334d708746e94c4bde40d", + "sha256:f902804113e032e2cdf8c71015651c97af6418363bea8d78dc0911d56c335032", + "sha256:fa1076244f54bb76e65e22cb6910365779d5c3d71d1f18b275f1dfc7b0d71b4d", + "sha256:fc2db02409338bf36590aa985a461b2c96fce91f8e7e0f14c50c5fcc4f229016", + "sha256:ffcad6c564fe6b9b8916c1aefbb37a362deebf9394bd2974e9d84232e3e08504" + ], + "markers": "python_version >= '3.8'", + "version": "==0.2.0" + }, "protego": { "hashes": [ "sha256:2fbe8e9b7a7dbc5016a932b14c98d236aad4c29290bbe457b8d2779666ef7a41", @@ -2290,6 +2418,29 @@ "markers": "python_version >= '3.8'", "version": "==0.20.0" }, + "scrapeops-python-logger": { + "hashes": [ + "sha256:faf58d35a6f043e90cd9680c070046f6617ceff725528ab66a98657c7cdef8f3" + ], + "markers": "python_version >= '3.6'", + "version": "==0.4.7" + }, + "scrapeops-python-requests": { + "hashes": [ + "sha256:2c7b6e2ecd7690efff1f0b0f506df1f189f07534bf9f9eb6f5b8e8cf604e5198" + ], + "index": "pypi", + "markers": "python_version >= '3.6'", + "version": "==0.4.7" + }, + "scrapeops-scrapy": { + "hashes": [ + "sha256:42a78c2e48c395895a2145d57ee912dbdf14c675738187fa08c191fb637f6a2a" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==0.5.6" + }, "scrapy": { "hashes": [ "sha256:4be353d6abbb942a9f7e7614ca8b5f3d9037381176ac8d8859c8cac676e74fa0", @@ -2438,6 +2589,14 @@ "markers": "python_version >= '3.8'", "version": "==0.22.1" }, + "tld": { + "hashes": [ + "sha256:93dde5e1c04bdf1844976eae440706379d21f4ab235b73c05d7483e074fb5629", + "sha256:f75b2be080f767ed17c2338a339eaa4fab5792586319ca819119da252f9f3749" + ], + "markers": "python_version >= '3.7' and python_version < '4'", + "version": "==0.13" + }, "tldextract": { "hashes": [ "sha256:4dfc4c277b6b97fa053899fcdb892d2dc27295851ab5fac4e07797b6a21b2e46", @@ -2524,11 +2683,11 @@ "standard" ], "hashes": [ - "sha256:13bc21373d103859f68fe739608e2eb054a816dea79189bc3ca08ea89a275906", - "sha256:cac7be4dd4d891c363cd942160a7b02e69150dcbc7a36be04d5f4af4b17c8ced" + "sha256:adc42d9cac80cf3e51af97c1851648066841e7cfb6993a4ca8de29ac1548ed41", + "sha256:f5167919867b161b7bcaf32646c6a94cdbd4c3aa2eb5c17d36bb9aa5cfd8c493" ], "markers": "python_version >= '3.8'", - "version": "==0.31.0" + "version": "==0.31.1" }, "uvloop": { "hashes": [ @@ -2806,101 +2965,107 @@ }, "yarl": { "hashes": [ - "sha256:08d7148ff11cb8e886d86dadbfd2e466a76d5dd38c7ea8ebd9b0e07946e76e4b", - "sha256:098b870c18f1341786f290b4d699504e18f1cd050ed179af8123fd8232513424", - "sha256:11b3ca8b42a024513adce810385fcabdd682772411d95bbbda3b9ed1a4257644", - "sha256:1891d69a6ba16e89473909665cd355d783a8a31bc84720902c5911dbb6373465", - "sha256:1bbb418f46c7f7355084833051701b2301092e4611d9e392360c3ba2e3e69f88", - "sha256:1d0828e17fa701b557c6eaed5edbd9098eb62d8838344486248489ff233998b8", - "sha256:1d8e3ca29f643dd121f264a7c89f329f0fcb2e4461833f02de6e39fef80f89da", - "sha256:1fa56f34b2236f5192cb5fceba7bbb09620e5337e0b6dfe2ea0ddbd19dd5b154", - "sha256:216a6785f296169ed52cd7dcdc2612f82c20f8c9634bf7446327f50398732a51", - "sha256:22b739f99c7e4787922903f27a892744189482125cc7b95b747f04dd5c83aa9f", - "sha256:2430cf996113abe5aee387d39ee19529327205cda975d2b82c0e7e96e5fdabdc", - "sha256:269c201bbc01d2cbba5b86997a1e0f73ba5e2f471cfa6e226bcaa7fd664b598d", - "sha256:298c1eecfd3257aa16c0cb0bdffb54411e3e831351cd69e6b0739be16b1bdaa8", - "sha256:2a93a4557f7fc74a38ca5a404abb443a242217b91cd0c4840b1ebedaad8919d4", - "sha256:2b2442a415a5f4c55ced0fade7b72123210d579f7d950e0b5527fc598866e62c", - "sha256:2db874dd1d22d4c2c657807562411ffdfabec38ce4c5ce48b4c654be552759dc", - "sha256:309c104ecf67626c033845b860d31594a41343766a46fa58c3309c538a1e22b2", - "sha256:31497aefd68036d8e31bfbacef915826ca2e741dbb97a8d6c7eac66deda3b606", - "sha256:373f16f38721c680316a6a00ae21cc178e3a8ef43c0227f88356a24c5193abd6", - "sha256:396e59b8de7e4d59ff5507fb4322d2329865b909f29a7ed7ca37e63ade7f835c", - "sha256:3bb83a0f12701c0b91112a11148b5217617982e1e466069d0555be9b372f2734", - "sha256:3de86547c820e4f4da4606d1c8ab5765dd633189791f15247706a2eeabc783ae", - "sha256:3fdbf0418489525231723cdb6c79e7738b3cbacbaed2b750cb033e4ea208f220", - "sha256:40c6e73c03a6befb85b72da213638b8aaa80fe4136ec8691560cf98b11b8ae6e", - "sha256:44a4c40a6f84e4d5955b63462a0e2a988f8982fba245cf885ce3be7618f6aa7d", - "sha256:44b07e1690f010c3c01d353b5790ec73b2f59b4eae5b0000593199766b3f7a5c", - "sha256:45d23c4668d4925688e2ea251b53f36a498e9ea860913ce43b52d9605d3d8177", - "sha256:45f209fb4bbfe8630e3d2e2052535ca5b53d4ce2d2026bed4d0637b0416830da", - "sha256:4afdf84610ca44dcffe8b6c22c68f309aff96be55f5ea2fa31c0c225d6b83e23", - "sha256:4feaaa4742517eaceafcbe74595ed335a494c84634d33961214b278126ec1485", - "sha256:576365c9f7469e1f6124d67b001639b77113cfd05e85ce0310f5f318fd02fe85", - "sha256:5820bd4178e6a639b3ef1db8b18500a82ceab6d8b89309e121a6859f56585b05", - "sha256:5989a38ba1281e43e4663931a53fbf356f78a0325251fd6af09dd03b1d676a09", - "sha256:5a9bacedbb99685a75ad033fd4de37129449e69808e50e08034034c0bf063f99", - "sha256:5b66c87da3c6da8f8e8b648878903ca54589038a0b1e08dde2c86d9cd92d4ac9", - "sha256:5c5e32fef09ce101fe14acd0f498232b5710effe13abac14cd95de9c274e689e", - "sha256:658e8449b84b92a4373f99305de042b6bd0d19bf2080c093881e0516557474a5", - "sha256:6a2acde25be0cf9be23a8f6cbd31734536a264723fca860af3ae5e89d771cd71", - "sha256:6a5185ad722ab4dd52d5fb1f30dcc73282eb1ed494906a92d1a228d3f89607b0", - "sha256:6b7f6e699304717fdc265a7e1922561b02a93ceffdaefdc877acaf9b9f3080b8", - "sha256:703b0f584fcf157ef87816a3c0ff868e8c9f3c370009a8b23b56255885528f10", - "sha256:7055bbade838d68af73aea13f8c86588e4bcc00c2235b4b6d6edb0dbd174e246", - "sha256:78f271722423b2d4851cf1f4fa1a1c4833a128d020062721ba35e1a87154a049", - "sha256:7addd26594e588503bdef03908fc207206adac5bd90b6d4bc3e3cf33a829f57d", - "sha256:81bad32c8f8b5897c909bf3468bf601f1b855d12f53b6af0271963ee67fff0d2", - "sha256:82e692fb325013a18a5b73a4fed5a1edaa7c58144dc67ad9ef3d604eccd451ad", - "sha256:84bbcdcf393139f0abc9f642bf03f00cac31010f3034faa03224a9ef0bb74323", - "sha256:86c438ce920e089c8c2388c7dcc8ab30dfe13c09b8af3d306bcabb46a053d6f7", - "sha256:8be8cdfe20787e6a5fcbd010f8066227e2bb9058331a4eccddec6c0db2bb85b2", - "sha256:8c723c91c94a3bc8033dd2696a0f53e5d5f8496186013167bddc3fb5d9df46a3", - "sha256:8ca53632007c69ddcdefe1e8cbc3920dd88825e618153795b57e6ebcc92e752a", - "sha256:8f722f30366474a99745533cc4015b1781ee54b08de73260b2bbe13316079851", - "sha256:942c80a832a79c3707cca46bd12ab8aa58fddb34b1626d42b05aa8f0bcefc206", - "sha256:94a993f976cdcb2dc1b855d8b89b792893220db8862d1a619efa7451817c836b", - "sha256:95c6737f28069153c399d875317f226bbdea939fd48a6349a3b03da6829fb550", - "sha256:9915300fe5a0aa663c01363db37e4ae8e7c15996ebe2c6cce995e7033ff6457f", - "sha256:9a18595e6a2ee0826bf7dfdee823b6ab55c9b70e8f80f8b77c37e694288f5de1", - "sha256:9c8854b9f80693d20cec797d8e48a848c2fb273eb6f2587b57763ccba3f3bd4b", - "sha256:9cec42a20eae8bebf81e9ce23fb0d0c729fc54cf00643eb251ce7c0215ad49fe", - "sha256:9d2e1626be8712333a9f71270366f4a132f476ffbe83b689dd6dc0d114796c74", - "sha256:9d74f3c335cfe9c21ea78988e67f18eb9822f5d31f88b41aec3a1ec5ecd32da5", - "sha256:9fb4134cc6e005b99fa29dbc86f1ea0a298440ab6b07c6b3ee09232a3b48f495", - "sha256:a0ae6637b173d0c40b9c1462e12a7a2000a71a3258fa88756a34c7d38926911c", - "sha256:a31d21089894942f7d9a8df166b495101b7258ff11ae0abec58e32daf8088813", - "sha256:a3442c31c11088e462d44a644a454d48110f0588de830921fd201060ff19612a", - "sha256:ab9524e45ee809a083338a749af3b53cc7efec458c3ad084361c1dbf7aaf82a2", - "sha256:b1481c048fe787f65e34cb06f7d6824376d5d99f1231eae4778bbe5c3831076d", - "sha256:b8c837ab90c455f3ea8e68bee143472ee87828bff19ba19776e16ff961425b57", - "sha256:bbf2c3f04ff50f16404ce70f822cdc59760e5e2d7965905f0e700270feb2bbfc", - "sha256:bbf9c2a589be7414ac4a534d54e4517d03f1cbb142c0041191b729c2fa23f320", - "sha256:bcd5bf4132e6a8d3eb54b8d56885f3d3a38ecd7ecae8426ecf7d9673b270de43", - "sha256:c14c16831b565707149c742d87a6203eb5597f4329278446d5c0ae7a1a43928e", - "sha256:c49f3e379177f4477f929097f7ed4b0622a586b0aa40c07ac8c0f8e40659a1ac", - "sha256:c92b89bffc660f1274779cb6fbb290ec1f90d6dfe14492523a0667f10170de26", - "sha256:cd66152561632ed4b2a9192e7f8e5a1d41e28f58120b4761622e0355f0fe034c", - "sha256:cf1ad338620249f8dd6d4b6a91a69d1f265387df3697ad5dc996305cf6c26fb2", - "sha256:d07b52c8c450f9366c34aa205754355e933922c79135125541daae6cbf31c799", - "sha256:d0d12fe78dcf60efa205e9a63f395b5d343e801cf31e5e1dda0d2c1fb618073d", - "sha256:d4ee1d240b84e2f213565f0ec08caef27a0e657d4c42859809155cf3a29d1735", - "sha256:d959fe96e5c2712c1876d69af0507d98f0b0e8d81bee14cfb3f6737470205419", - "sha256:dcaef817e13eafa547cdfdc5284fe77970b891f731266545aae08d6cce52161e", - "sha256:df4e82e68f43a07735ae70a2d84c0353e58e20add20ec0af611f32cd5ba43fb4", - "sha256:ec8cfe2295f3e5e44c51f57272afbd69414ae629ec7c6b27f5a410efc78b70a0", - "sha256:ec9dd328016d8d25702a24ee274932aebf6be9787ed1c28d021945d264235b3c", - "sha256:ef9b85fa1bc91c4db24407e7c4da93a5822a73dd4513d67b454ca7064e8dc6a3", - "sha256:f3bf60444269345d712838bb11cc4eadaf51ff1a364ae39ce87a5ca8ad3bb2c8", - "sha256:f452cc1436151387d3d50533523291d5f77c6bc7913c116eb985304abdbd9ec9", - "sha256:f7917697bcaa3bc3e83db91aa3a0e448bf5cde43c84b7fc1ae2427d2417c0224", - "sha256:f90575e9fe3aae2c1e686393a9689c724cd00045275407f71771ae5d690ccf38", - "sha256:fb382fd7b4377363cc9f13ba7c819c3c78ed97c36a82f16f3f92f108c787cbbf", - "sha256:fb9f59f3848edf186a76446eb8bcf4c900fe147cb756fbbd730ef43b2e67c6a7", - "sha256:fc2931ac9ce9c61c9968989ec831d3a5e6fcaaff9474e7cfa8de80b7aff5a093" + "sha256:0127bc2ea72c1eaae6808ace661f0edf222f32ffa987d37f2dbb4798288f2656", + "sha256:0358b697abdf1f2d68038bd02ef8ddcc4813835744f79c755f8743aa485585e7", + "sha256:06306c74f0775621a70fa5acd292119bbb6961d1f9a5f3d657a4c8c15b86f7b9", + "sha256:06b5b462cadf59c1df28ffbb0a3971fa16b60cf0c9d59a38bf5679a986d18685", + "sha256:097094a979af7b31520517c59179f6817b8426724343cecbec0eb3af1f8fb6cf", + "sha256:0c791a2d42da20ac568e5c0cc9b8af313188becd203a936ad959b578dafbcebb", + "sha256:1656a8b531a96427f26f498b7d0f19931166ff30e4344eca99bdb27faca14fc5", + "sha256:18614630533ac37ec373bd8035aec8fa4dd9aedac641209c06de7e082622ff77", + "sha256:1e5fa4c4e55cdacef1844f609bc9a02c8cd29c324a71ca1d3ee454701d4bb496", + "sha256:1edaf4171fc1582352ac5d9b2783966fa0f4ff86187279ef2a491613d23b894a", + "sha256:2124c642b8cc9b68e5981e429842dadc32bb850b010cccec9d24236253a19f60", + "sha256:229f222bb47cd7ab225648efd1ae47fe6943f18e4c91bce66471faf09fe33128", + "sha256:2429a651a2191c3fb8c9de21546c6046da539034d51dcb8df52302748004593d", + "sha256:25a4e29ee758596b2a0daffa4814714e9b464077ca862baf78ed0e8698e46b61", + "sha256:27c323b28723faed046f906c70466144c4dd12046a0128a301b29a65cfeff758", + "sha256:2add8ed2acf42398dfaa7dffd32e4d18ffbae341d62c8d4765bd9929336379b5", + "sha256:2bece7fdc13e23db005879b67190db0d397f6ba89c81dc7e3c77e9f5819aff7f", + "sha256:2eafb4e92f72a3b6c27f1d5e921d046e2728850af8887f86857c3fe868a5b5c0", + "sha256:32840ff92c713053591ff0e66845d4e9f4bea8fd5fba3da00f8d92e77722f24e", + "sha256:33896afca6fb4e1988c099534c52823870dfc8730bc6f96a3831f24c1e0ab814", + "sha256:350b468a217d433cbb4482e9414a14dfd360a3d5ab92013175925abb234364cc", + "sha256:38cab8f91b1085f1fd0765d40c46c8f43282f109018d5fcd017c46ac3eaba0cf", + "sha256:3e24a778470f3a9e9c11250d09daf5dea93369bc51aefca6605dbc963737a117", + "sha256:4224bbbc8a2e9b9a3828d36c1bab7458441d7fb9fb3af321eb735732ba8ee89d", + "sha256:4424082edff76fe46ff08851e91865097c0ad780fa79b87063dc5d5b80efc9d6", + "sha256:454707fb16f180984da6338d1f51897f0b8d8c4c2e0592d9d1e9fa02a5bb8218", + "sha256:4b1ab96a1ac91bd1233706d638ade35f663684deaa4e5e5f190858cba044afb9", + "sha256:4c5ff3e7609c214667c7d7e00d5f4f3576fefde47ebcb7e492c015117dafebbf", + "sha256:5107d89c9edec6ee077970a95fb9eeb4776ea8c2337b6a39c0ade9a58f50f3e4", + "sha256:5156c12a97405339ec93facbc7860566db381af2de1bec338195563fb64f37ef", + "sha256:553a1e3537aeeb29c0eb29ef28b80e0e801697fa71d96ac60675b284ff8e582a", + "sha256:5e1cc7823f43781390965c4762b54262cfcf76b6f152e489d00a5a1ac63063e4", + "sha256:5eef9804e65eb292e9c5587e88fe6a27a11f121d358312ac47211e8f42876751", + "sha256:6237637b496bc04819190e724a4e61ff2f251abf432f70cf491b3bc4a3f2f253", + "sha256:627bb5bc4ed3d3ebceb9fb55717cec6cd58bb47fdb5669169ebbc248e9bf156c", + "sha256:676d7356bb30825b7dbdad4fdd7a9feac379d074e5d4a36299767d72857ded42", + "sha256:6960b0d2e713e726cb2914e3051e080b12412f70dcb8731cf7a8fb52c37931bb", + "sha256:6b93a666cd8cfd43f605d1b81a32b9e290bf45c74c2bfd51ba705449c78448c7", + "sha256:6ca160b4c649f0d56daef04751eef4571de46ed4b80f9051a87d090fef32f08e", + "sha256:70ac7893e67a81ed1346ee3e71203ca4b0c3550c005b1d1cf87bc1e61eecd04b", + "sha256:73c4af08e9bb9a9aa7df6c789b05b924b9a0c6a368bb0e418d0b85181b64b631", + "sha256:748dcacc19c69957f7063ea4fb359fa2180735b1a638c81a4a96b86a382a6f29", + "sha256:75d9762f65205a86381298eb9079f27c60b84de0c262e402dcf45c6cbc385234", + "sha256:7711d83dafe52cda16ff2dd205cd83c05e4c06d5aaac596ae2cf7d50d094a530", + "sha256:7aa9f9af452c3e8486a0b88fddd58352e6cea17b691b18861d26e46cf65ffff0", + "sha256:7f713d8f3c4e2eac0d91b741e8ef2e1082022de244685601ec83e899b445d86a", + "sha256:81edbd9bf9f25cd995e6d51c307e1d279587d40b7473e258fef6d5e548560cd2", + "sha256:83363a5789f128618041b9a737c7b146f1965abddf4294b0444591406b437c1e", + "sha256:85e273e59b8b1a5f60a89df82cddeaf918181abd7ae7a2f2f899b68b0c774ff1", + "sha256:8ad2e487824ba4cda87851a371139e255410e45d3bf2e334194789278d709cec", + "sha256:8b7f902f13a230686f01bcff17cd9ba045653069811c8fd5027f0f414b417e2f", + "sha256:8f074a24aa9a6a3d406474ec889ebb5d661f329349068e05e8dfcb3c4be67752", + "sha256:9084d99933824ed8d665f10f4ce62d08fed714e7678d5ff11a8c2c98b2dc18f9", + "sha256:928f7a61c4311f3dd003af19bb779f99683f97a0559b765c80fdb8846dab0452", + "sha256:97fcaf530318369da3cfd6ff52f5ab38daf8cb10ecee9a76efebf8031de09eef", + "sha256:994d27b24b61b1870f3571395c840433faabec5dcd239bd11ff6af7e34234bb6", + "sha256:9ae454916aa3abe28d0ef1c21ca1e8e36a14ccf52183d465dfaccffaa7ed462c", + "sha256:9fac5416c44e8e1d8ea9440096f88e1a7273257f3157184c5c715060e0c448a1", + "sha256:a2fe45c1143eefb680a4589c55e671fabd482a7f8c7791f311ea3bcc20139246", + "sha256:a3f8be3e785009ffa148e66474fea5c787ccb203b3d0bd1f22e1e22f7da0f3b3", + "sha256:a616c2e4b60cb8cdd9eb3b0c6fda4ab5f3e26244b427aaade560dcf63c5754fb", + "sha256:a94c9058c5703c172904103d7b479f7e23dd4e5f8e67b49f6cd256d35ff169cb", + "sha256:b1208f2e081d34832f509cbe311237a0543effe23d60b2fa14c0d3f86e6d1d07", + "sha256:b4b25de7e85ba90b2ff230153123b6b000a7f69c41d84a3a0dc3f878334c8509", + "sha256:bbe72c41cdd55c88b238a8925849fde4069c0cdcdef83f8d967f8f3982659326", + "sha256:c0a86dd3e85c6aa3fc73236eb5cf7ce69dd8ad7abcd23f8ae1126831c8e40c2f", + "sha256:c3b08d9e98d1a15338fcfbd52c02003704322c2d460c9b9be7df08f2952bdce6", + "sha256:c4d9c221cc8e32b14196498679bf2b324bec1d1127c4ba934d98e19298faa661", + "sha256:c4f882e42c6cea89488b9a16919edde8c0b1a98f307c05abdd3dd3bc4368af40", + "sha256:c5cc25cbd9ae01d49ac7b504ef5f3cbdcc8d139f9750dcfa0b80d405b4645cc2", + "sha256:c7f2deac59dc3e0528bdded248e637e789e5111ba1723a8d7a262eb93e133e15", + "sha256:c8b034b60e74fb29064f765851e77e5910055e1c4a3cb75c32eccf2b470fc00f", + "sha256:c9b9159eeeb7cd1c7131dc7f5878454f97a4dc20cd157e6474174ccac448b844", + "sha256:c9c405ca78c70c3599d8956e53be0c9def9c51ad949964a49ad96c79729a5b1a", + "sha256:ceb200918c9bd163bd390cc169b254b23b4be121026b003be93a4f2f5b554b4b", + "sha256:d06040266b5e6512a37b4703684d1798124764b43328254799e9678c588882a6", + "sha256:d3f5e201bd170fb97c643e84df58e221372cd053fbb291ebbd878b165ea5057e", + "sha256:d4aa7cca009817789fd5b8e52e8122f9e85dc580c88b816a93321c00a8acbced", + "sha256:d772ae3c12d3b8629db656050c86ee66924eaa98f7125a889175a59cfaafdb19", + "sha256:d816969b55a970b3accc7f9e4ea8f60043e3f7de96f21c06063d747ffc2f18ba", + "sha256:d885dcdca7bae42bc9a2f6cbf766abcb2a6cc043b1905fc3782c6ea1f74a2b95", + "sha256:db903458a457a53ee0f764ed11c5b5368398e216b442c42dca9d90fbd2bbf31c", + "sha256:dc63bb79e896d6ce6aaf672ac304b54969280e949c45727867fc154a17ec7ab2", + "sha256:dd042e6c3bf36448e3e3ed302b12ce79762480f4aff8e7a167cdf8c35dc93297", + "sha256:ddea4abc4606c10dddb70651b210b7ab5b663148d6d7bc85d76963c923629891", + "sha256:df57f3c3ef760489f2e82192e6c93286c2bc80d6d854ef940e5345ae7153cd4b", + "sha256:e1ddf05eeb422810b1aa919095db0691493442eebbf9cfb0f1e478a7b2fbdf3d", + "sha256:e2e3cb74684ff357e6b3c82dd71031d3c1fd7ee9f9b0a5205e5568c963e074f9", + "sha256:e4f64c8c52dde564bf3251b41d7a6746564b0fc0516cebe9c9e6695224440d22", + "sha256:e4f7efb38331e8327c1cc7fb2a2905a7db03d1a7fdb04706bf6465d0e44d41d4", + "sha256:e61b2019ebb5345510b833c4dd1f4afb1f0c07753f86f184c63836ffc3fb08ba", + "sha256:e7e38bf6e52797084c5c396db5bb519615727e491e9003e2449631457bf77738", + "sha256:eae041f535fe2e57681954f7cccb81854d777ce4c2a87749428ebe6c71c02ec0", + "sha256:eb964d18c01b7a1263a6f07b88d63711fcd564fc429d934279cf12f4b467bf53", + "sha256:ef780f9d480ffb423380abeb4cfcad66ecb8f93526dfa367d322fdad9ec7c25f", + "sha256:efc0430b80ed834c80c99c32946cfc6ee29dfcd7c62ad3c8f15657322ade7942", + "sha256:f2508ee2bad8381b5254eadc35d32fe800d12eb2c63b744183341f3a66e435a7", + "sha256:fee9acd5e39c8611957074dfba06552e430020eea831caf5eb2cea30f10e06bd" ], "markers": "python_version >= '3.8'", - "version": "==1.13.1" + "version": "==1.15.0" }, "zope.event": { "hashes": [ @@ -2912,43 +3077,46 @@ }, "zope.interface": { "hashes": [ - "sha256:01e6e58078ad2799130c14a1d34ec89044ada0e1495329d72ee0407b9ae5100d", - "sha256:064ade95cb54c840647205987c7b557f75d2b2f7d1a84bfab4cf81822ef6e7d1", - "sha256:11fa1382c3efb34abf16becff8cb214b0b2e3144057c90611621f2d186b7e1b7", - "sha256:1bee1b722077d08721005e8da493ef3adf0b7908e0cd85cc7dc836ac117d6f32", - "sha256:1eeeb92cb7d95c45e726e3c1afe7707919370addae7ed14f614e22217a536958", - "sha256:21a207c6b2c58def5011768140861a73f5240f4f39800625072ba84e76c9da0b", - "sha256:2545d6d7aac425d528cd9bf0d9e55fcd47ab7fd15f41a64b1c4bf4c6b24946dc", - "sha256:2c4316a30e216f51acbd9fb318aa5af2e362b716596d82cbb92f9101c8f8d2e7", - "sha256:35062d93bc49bd9b191331c897a96155ffdad10744ab812485b6bad5b588d7e4", - "sha256:382d31d1e68877061daaa6499468e9eb38eb7625d4369b1615ac08d3860fe896", - "sha256:3aa8fcbb0d3c2be1bfd013a0f0acd636f6ed570c287743ae2bbd467ee967154d", - "sha256:3d4b91821305c8d8f6e6207639abcbdaf186db682e521af7855d0bea3047c8ca", - "sha256:3de1d553ce72868b77a7e9d598c9bff6d3816ad2b4cc81c04f9d8914603814f3", - "sha256:3fcdc76d0cde1c09c37b7c6b0f8beba2d857d8417b055d4f47df9c34ec518bdd", - "sha256:5112c530fa8aa2108a3196b9c2f078f5738c1c37cfc716970edc0df0414acda8", - "sha256:53d678bb1c3b784edbfb0adeebfeea6bf479f54da082854406a8f295d36f8386", - "sha256:6195c3c03fef9f87c0dbee0b3b6451df6e056322463cf35bca9a088e564a3c58", - "sha256:6d04b11ea47c9c369d66340dbe51e9031df2a0de97d68f442305ed7625ad6493", - "sha256:6dd647fcd765030638577fe6984284e0ebba1a1008244c8a38824be096e37fe3", - "sha256:799ef7a444aebbad5a145c3b34bff012b54453cddbde3332d47ca07225792ea4", - "sha256:7d92920416f31786bc1b2f34cc4fc4263a35a407425319572cbf96b51e835cd3", - "sha256:7e0c151a6c204f3830237c59ee4770cc346868a7a1af6925e5e38650141a7f05", - "sha256:84f8794bd59ca7d09d8fce43ae1b571be22f52748169d01a13d3ece8394d8b5b", - "sha256:95e5913ec718010dc0e7c215d79a9683b4990e7026828eedfda5268e74e73e11", - "sha256:9b9369671a20b8d039b8e5a1a33abd12e089e319a3383b4cc0bf5c67bd05fe7b", - "sha256:ab985c566a99cc5f73bc2741d93f1ed24a2cc9da3890144d37b9582965aff996", - "sha256:af94e429f9d57b36e71ef4e6865182090648aada0cb2d397ae2b3f7fc478493a", - "sha256:c96b3e6b0d4f6ddfec4e947130ec30bd2c7b19db6aa633777e46c8eecf1d6afd", - "sha256:cd2690d4b08ec9eaf47a85914fe513062b20da78d10d6d789a792c0b20307fb1", - "sha256:d3b7ce6d46fb0e60897d62d1ff370790ce50a57d40a651db91a3dde74f73b738", - "sha256:d976fa7b5faf5396eb18ce6c132c98e05504b52b60784e3401f4ef0b2e66709b", - "sha256:db6237e8fa91ea4f34d7e2d16d74741187e9105a63bbb5686c61fea04cdbacca", - "sha256:ecd32f30f40bfd8511b17666895831a51b532e93fc106bfa97f366589d3e4e0e", - "sha256:f418c88f09c3ba159b95a9d1cfcdbe58f208443abb1f3109f4b9b12fd60b187c" + "sha256:07add15de0cc7e69917f7d286b64d54125c950aeb43efed7a5ea7172f000fbc1", + "sha256:0ac20581fc6cd7c754f6dff0ae06fedb060fa0e9ea6309d8be8b2701d9ea51c4", + "sha256:124149e2d42067b9c6597f4dafdc7a0983d0163868f897b7bb5dc850b14f9a87", + "sha256:27cfb5205d68b12682b6e55ab8424662d96e8ead19550aad0796b08dd2c9a45e", + "sha256:2a29ac607e970b5576547f0e3589ec156e04de17af42839eedcf478450687317", + "sha256:2b6a4924f5bad9fe21d99f66a07da60d75696a136162427951ec3cb223a5570d", + "sha256:2bd9e9f366a5df08ebbdc159f8224904c1c5ce63893984abb76954e6fbe4381a", + "sha256:3bcff5c09d0215f42ba64b49205a278e44413d9bf9fa688fd9e42bfe472b5f4f", + "sha256:3f005869a1a05e368965adb2075f97f8ee9a26c61898a9e52a9764d93774f237", + "sha256:4a00ead2e24c76436e1b457a5132d87f83858330f6c923640b7ef82d668525d1", + "sha256:4af4a12b459a273b0b34679a5c3dc5e34c1847c3dd14a628aa0668e19e638ea2", + "sha256:5501e772aff595e3c54266bc1bfc5858e8f38974ce413a8f1044aae0f32a83a3", + "sha256:5e28ea0bc4b084fc93a483877653a033062435317082cdc6388dec3438309faf", + "sha256:5e956b1fd7f3448dd5e00f273072e73e50dfafcb35e4227e6d5af208075593c9", + "sha256:5fcf379b875c610b5a41bc8a891841533f98de0520287d7f85e25386cd10d3e9", + "sha256:6159e767d224d8f18deff634a1d3722e68d27488c357f62ebeb5f3e2f5288b1f", + "sha256:661d5df403cd3c5b8699ac480fa7f58047a3253b029db690efa0c3cf209993ef", + "sha256:711eebc77f2092c6a8b304bad0b81a6ce3cf5490b25574e7309fbc07d881e3af", + "sha256:80a3c00b35f6170be5454b45abe2719ea65919a2f09e8a6e7b1362312a872cd3", + "sha256:848b6fa92d7c8143646e64124ed46818a0049a24ecc517958c520081fd147685", + "sha256:91b6c30689cfd87c8f264acb2fc16ad6b3c72caba2aec1bf189314cf1a84ca33", + "sha256:9733a9a0f94ef53d7aa64661811b20875b5bc6039034c6e42fb9732170130573", + "sha256:9940d5bc441f887c5f375ec62bcf7e7e495a2d5b1da97de1184a88fb567f06af", + "sha256:9e3e48f3dea21c147e1b10c132016cb79af1159facca9736d231694ef5a740a8", + "sha256:a14c9decf0eb61e0892631271d500c1e306c7b6901c998c7035e194d9150fdd1", + "sha256:a735f82d2e3ed47ca01a20dfc4c779b966b16352650a8036ab3955aad151ed8a", + "sha256:a99240b1d02dc469f6afbe7da1bf617645e60290c272968f4e53feec18d7dce8", + "sha256:b7b25db127db3e6b597c5f74af60309c4ad65acd826f89609662f0dc33a54728", + "sha256:b936d61dbe29572fd2cfe13e30b925e5383bed1aba867692670f5a2a2eb7b4e9", + "sha256:bec001798ab62c3fc5447162bf48496ae9fba02edc295a9e10a0b0c639a6452e", + "sha256:cc8a318162123eddbdf22fcc7b751288ce52e4ad096d3766ff1799244352449d", + "sha256:d0a45b5af9f72c805ee668d1479480ca85169312211bed6ed18c343e39307d5f", + "sha256:e53c291debef523b09e1fe3dffe5f35dde164f1c603d77f770b88a1da34b7ed6", + "sha256:ec1ef1fdb6f014d5886b97e52b16d0f852364f447d2ab0f0c6027765777b6667", + "sha256:ec59fe53db7d32abb96c6d4efeed84aab4a7c38c62d7a901a9b20c09dd936e7a", + "sha256:f245d039f72e6f802902375755846f5de1ee1e14c3e8736c078565599bcab621", + "sha256:ff115ef91c0eeac69cd92daeba36a9d8e14daee445b504eeea2b1c0b55821984" ], "markers": "python_version >= '3.8'", - "version": "==7.0.3" + "version": "==7.1.0" } }, "develop": { @@ -2978,99 +3146,114 @@ }, "charset-normalizer": { "hashes": [ - "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", - "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", - "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", - "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", - "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", - "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", - "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", - "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", - "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", - "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", - "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", - "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", - "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", - "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", - "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", - "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", - "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", - "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", - "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", - "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", - "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", - "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", - "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", - "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", - "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", - "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", - "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", - "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", - "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", - "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", - "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", - "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", - "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", - "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", - "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", - "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", - "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", - "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", - "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", - "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", - "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", - "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", - "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", - "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", - "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", - "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", - "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", - "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", - "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", - "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", - "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", - "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", - "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", - "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", - "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", - "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", - "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", - "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", - "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", - "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", - "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", - "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", - "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", - "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", - "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", - "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", - "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", - "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", - "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", - "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", - "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", - "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", - "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", - "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", - "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", - "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", - "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", - "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", - "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", - "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", - "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", - "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", - "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", - "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", - "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", - "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", - "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", - "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", - "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", - "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" + "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621", + "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6", + "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8", + "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912", + "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c", + "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b", + "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d", + "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d", + "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95", + "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e", + "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565", + "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", + "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab", + "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be", + "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", + "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907", + "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0", + "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2", + "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62", + "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62", + "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", + "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc", + "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284", + "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca", + "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455", + "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858", + "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b", + "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594", + "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", + "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db", + "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", + "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea", + "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6", + "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", + "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749", + "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7", + "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd", + "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99", + "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242", + "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee", + "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129", + "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2", + "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51", + "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee", + "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8", + "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", + "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613", + "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742", + "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe", + "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3", + "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5", + "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631", + "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7", + "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15", + "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c", + "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea", + "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417", + "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", + "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", + "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca", + "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa", + "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99", + "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149", + "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41", + "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574", + "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0", + "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f", + "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", + "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654", + "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3", + "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19", + "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", + "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578", + "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9", + "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1", + "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51", + "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719", + "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236", + "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a", + "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c", + "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade", + "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944", + "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc", + "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6", + "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6", + "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27", + "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6", + "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2", + "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12", + "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf", + "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", + "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7", + "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf", + "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", + "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b", + "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", + "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03", + "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4", + "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", + "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365", + "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a", + "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748", + "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b", + "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", + "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482" ], "markers": "python_full_version >= '3.7.0'", - "version": "==3.3.2" + "version": "==3.4.0" }, "idna": { "hashes": [ diff --git a/api/scheduler.py b/api/scheduler.py index d05f3bc..5daa379 100644 --- a/api/scheduler.py +++ b/api/scheduler.py @@ -1,11 +1,13 @@ from apscheduler.schedulers.asyncio import AsyncIOScheduler from apscheduler.triggers.cron import CronTrigger + from db.config import settings from mediafusion_scrapy.task import run_spider from scrapers.imdb_data import fetch_movie_ids_to_update +from scrapers.prowlarr_feed import run_prowlarr_feed_scraper from scrapers.trackers import update_torrent_seeders from scrapers.tv import validate_tv_streams_in_db -from scrapers.prowlarr_feed import run_prowlarr_feed_scraper +from scrapers.utils import cleanup_expired_scraper_task def setup_scheduler(scheduler: AsyncIOScheduler): @@ -218,3 +220,12 @@ def setup_scheduler(scheduler: AsyncIOScheduler): "crontab_expression": settings.prowlarr_feed_scraper_crontab, }, ) + + scheduler.add_job( + cleanup_expired_scraper_task.send, + CronTrigger.from_crontab(settings.cleanup_expired_scraper_task_crontab), + name="cleanup_expired_scraper_task", + kwargs={ + "crontab_expression": settings.cleanup_expired_scraper_task_crontab, + }, + ) diff --git a/db/config.py b/db/config.py index d1ca4d2..0356449 100644 --- a/db/config.py +++ b/db/config.py @@ -23,10 +23,12 @@ class Settings(BaseSettings): # External Service URLs scraper_proxy_url: str | None = None - zilean_url: str = "http://zilean.zilean:8181" playwright_cdp_url: str = "ws://browserless:3000?blockAds=true&stealth=true" flaresolverr_url: str = "http://flaresolverr:8191/v1" + # External Service API Keys + scrapeops_api_key: str | None = None + # Prowlarr Settings prowlarr_url: str = "http://prowlarr-service:9696" prowlarr_api_key: str | None = None @@ -42,6 +44,10 @@ class Settings(BaseSettings): torrentio_search_interval_days: int = 3 torrentio_url: str = "https://torrentio.strem.fun" + # Zilean Settings + zilean_search_interval_hour: int = 24 + zilean_url: str = "http://zilean.zilean:8181" + # Premiumize Settings premiumize_oauth_client_id: str | None = None premiumize_oauth_client_secret: str | None = None @@ -93,7 +99,7 @@ class Settings(BaseSettings): streamed_scheduler_crontab: str = "*/30 * * * *" disable_streamed_scheduler: bool = False streambtw_scheduler_crontab: str = "*/15 * * * *" - disable_streambtw_scheduler: bool = False + disable_streambtw_scheduler: bool = True dlhd_scheduler_crontab: str = "25 * * * *" disable_dlhd_scheduler: bool = False update_imdb_data_crontab: str = "0 2 * * *" @@ -108,6 +114,7 @@ class Settings(BaseSettings): disable_ufc_tgx_scheduler: bool = False prowlarr_feed_scraper_crontab: str = "0 */3 * * *" disable_prowlarr_feed_scraper: bool = False + cleanup_expired_scraper_task_crontab: str = "0 * * * *" @model_validator(mode="after") def default_poster_host_url(self) -> "Settings": diff --git a/db/models.py b/db/models.py index 41bb9fb..3cd35fa 100644 --- a/db/models.py +++ b/db/models.py @@ -154,6 +154,7 @@ class MediaFusionMetaData(Document): runtime: Optional[str] = None website: Optional[str] = None genres: Optional[list[str]] = Field(default_factory=list) + last_updated_at: datetime = Field(default_factory=datetime.now) class Settings: is_root = True diff --git a/db/schemas.py b/db/schemas.py index 8eb4740..4117b07 100644 --- a/db/schemas.py +++ b/db/schemas.py @@ -109,6 +109,7 @@ class QBittorrentConfig(BaseModel): class MediaFlowConfig(BaseModel): proxy_url: str | None = Field(alias="pu") api_password: str | None = Field(alias="ap") + public_ip: str | None = Field(alias="pip") proxy_live_streams: bool = Field(default=False, alias="pls") proxy_debrid_streams: bool = Field(default=False, alias="pds") diff --git a/deployment/docker-compose/docker-compose.yml b/deployment/docker-compose/docker-compose.yml index cd7e223..d2c3652 100644 --- a/deployment/docker-compose/docker-compose.yml +++ b/deployment/docker-compose/docker-compose.yml @@ -2,7 +2,7 @@ version: '3.8' services: mediafusion: - image: mhdzumair/mediafusion:v4.0.1 + image: mhdzumair/mediafusion:v4.0.2 ports: - "8000:8000" env_file: @@ -41,7 +41,7 @@ services: - "6379:6379" dramatiq-worker: - image: mhdzumair/mediafusion:v4.0.1 + image: mhdzumair/mediafusion:v4.0.2 command: ["pipenv", "run", "dramatiq", "api.task", "-p", "1", "-t", "1", "--queues", "scrapy"] env_file: - .env diff --git a/deployment/k8s/local-deployment.yaml b/deployment/k8s/local-deployment.yaml index 875bae8..a0fc270 100644 --- a/deployment/k8s/local-deployment.yaml +++ b/deployment/k8s/local-deployment.yaml @@ -19,7 +19,7 @@ spec: spec: containers: - name: mediafusion - image: mhdzumair/mediafusion:v4.0.1 + image: mhdzumair/mediafusion:v4.0.2 ports: - containerPort: 8000 resources: @@ -124,7 +124,7 @@ spec: spec: containers: - name: dramatiq-worker - image: mhdzumair/mediafusion:v4.0.1 + image: mhdzumair/mediafusion:v4.0.2 command: ["pipenv", "run", "dramatiq", "api.task", "-p", "1", "-t", "1"] env: - name: HOST_URL diff --git a/kodi/plugin.video.mediafusion/addon.xml b/kodi/plugin.video.mediafusion/addon.xml index 208d683..d7068cf 100644 --- a/kodi/plugin.video.mediafusion/addon.xml +++ b/kodi/plugin.video.mediafusion/addon.xml @@ -1,5 +1,5 @@ - + diff --git a/mediafusion_scrapy/extensions.py b/mediafusion_scrapy/extensions.py new file mode 100644 index 0000000..9e06b35 --- /dev/null +++ b/mediafusion_scrapy/extensions.py @@ -0,0 +1,63 @@ +import time +from datetime import datetime, timedelta + +from scrapy import signals +from scrapy.exceptions import NotConfigured, CloseSpider +from scrapy.utils.log import logger +from twisted.internet import task + + +class InactivityMonitor: + """Monitor spider for inactivity and close if inactive for too long""" + + def __init__(self, crawler, interval=60.0, inactivity_timeout=15): + self.crawler = crawler + self.stats = crawler.stats + self.interval = interval + self.inactivity_timeout = inactivity_timeout + self.task = None + self.last_scraped_time = time.monotonic() + + @classmethod + def from_crawler(cls, crawler): + interval = crawler.settings.getfloat("INACTIVITY_CHECK_INTERVAL", 60.0) + inactivity_timeout = crawler.settings.getint("INACTIVITY_TIMEOUT_MINUTES", 15) + + if not interval or not inactivity_timeout: + raise NotConfigured + + ext = cls(crawler, interval, inactivity_timeout) + + crawler.signals.connect(ext.spider_opened, signal=signals.spider_opened) + crawler.signals.connect(ext.spider_closed, signal=signals.spider_closed) + crawler.signals.connect(ext.item_scraped, signal=signals.item_scraped) + + return ext + + def spider_opened(self, spider): + self.last_scraped_time = time.monotonic() + self.task = task.LoopingCall(self.check_inactivity, spider) + self.task.start(self.interval) + + def check_inactivity(self, spider): + time_since_last_scrape_seconds = time.monotonic() - self.last_scraped_time + time_since_last_scrape = timedelta(seconds=time_since_last_scrape_seconds) + logger.debug( + "Checking inactivity for spider %s, last scraped %s ago", + spider.name, + time_since_last_scrape, + ) + if time_since_last_scrape > timedelta(minutes=self.inactivity_timeout): + msg = f"No items scraped in the last {self.inactivity_timeout} minutes. Closing spider." + logger.info(msg, extra={"spider": spider}) + self.crawler.engine.close_spider( + spider, reason=f"Inactivity timeout: {msg}" + ) + raise CloseSpider(f"Inactivity timeout: {msg}") + + def item_scraped(self, item, response, spider): + self.last_scraped_time = datetime.now() + + def spider_closed(self, spider, reason): + if self.task and self.task.running: + self.task.stop() diff --git a/mediafusion_scrapy/pipelines/live_stream_resolver_pipeline.py b/mediafusion_scrapy/pipelines/live_stream_resolver_pipeline.py index 8a97772..94272db 100644 --- a/mediafusion_scrapy/pipelines/live_stream_resolver_pipeline.py +++ b/mediafusion_scrapy/pipelines/live_stream_resolver_pipeline.py @@ -12,6 +12,7 @@ class LiveStreamResolverPipeline: adapter = ItemAdapter(item) stream_url = adapter.get("stream_url") stream_headers = adapter.get("stream_headers") + response_headers = adapter.get("response_headers", {}) if not stream_headers: referer = adapter.get("referer") stream_headers = {"Referer": referer} if referer else {} @@ -30,7 +31,9 @@ class LiveStreamResolverPipeline: ) ) ) - content_type = response.headers.get("Content-Type", b"").decode().lower() + content_type = response_headers.get( + "Content-Type", response.headers.get("Content-Type", b"").decode().lower() + ) if response.status == 200 and content_type in const.IPTV_VALID_CONTENT_TYPES: stream_headers.update( @@ -49,6 +52,7 @@ class LiveStreamResolverPipeline: "notWebReady": True, "proxyHeaders": { "request": stream_headers, + "response": response_headers, }, }, } diff --git a/mediafusion_scrapy/pipelines/sport_video_parser_pipeline.py b/mediafusion_scrapy/pipelines/sport_video_parser_pipeline.py index 6ffdf95..02a8401 100644 --- a/mediafusion_scrapy/pipelines/sport_video_parser_pipeline.py +++ b/mediafusion_scrapy/pipelines/sport_video_parser_pipeline.py @@ -22,7 +22,7 @@ class SportVideoParserPipeline: def process_item(self, item, spider): adapter = ItemAdapter(item) if "title" not in adapter or "torrent_name" not in adapter: - raise DropItem(f"title not found in item: {item}") + raise DropItem(f"title or torrent_name not found in item: {item}") match = self.title_regex.search(adapter["title"]) or self.title_regex.search( adapter["torrent_name"] diff --git a/mediafusion_scrapy/settings.py b/mediafusion_scrapy/settings.py index 7513d32..dd67e18 100644 --- a/mediafusion_scrapy/settings.py +++ b/mediafusion_scrapy/settings.py @@ -51,16 +51,30 @@ SPIDER_MIDDLEWARES = { # Enable or disable downloader middlewares # See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html + DOWNLOADER_MIDDLEWARES = { "mediafusion_scrapy.middlewares.FlaresolverrMiddleware": 542, "mediafusion_scrapy.middlewares.TooManyRequestsRetryMiddleware": 543, + "scrapy.downloadermiddlewares.retry.RetryMiddleware": 550, } -# Enable or disable extensions -# See https://docs.scrapy.org/en/latest/topics/extensions.html -# EXTENSIONS = { -# "scrapy.extensions.telnet.TelnetConsole": None, -# } +EXTENSIONS = { + "mediafusion_scrapy.extensions.InactivityMonitor": 100, +} + +if settings.scrapeops_api_key: + SCRAPEOPS_API_KEY = settings.scrapeops_api_key + DOWNLOADER_MIDDLEWARES.update( + { + "scrapeops_scrapy.middleware.retry.RetryMiddleware": 550, + "scrapy.downloadermiddlewares.retry.RetryMiddleware": None, + } + ) + EXTENSIONS.update( + { + "scrapeops_scrapy.extension.ScrapeOpsMonitor": 500, # ScrapeOps Monitor + } + ) # Configure item pipelines # See https://docs.scrapy.org/en/latest/topics/item-pipeline.html @@ -114,3 +128,5 @@ RETRY_HTTP_CODES = [ RETRY_TIMES = 5 FLARESOLVERR_URL = settings.flaresolverr_url + +INACTIVITY_TIMEOUT_MINUTES = 15 diff --git a/mediafusion_scrapy/spiders/dlhd.py b/mediafusion_scrapy/spiders/dlhd.py index c848049..3d3a4b1 100644 --- a/mediafusion_scrapy/spiders/dlhd.py +++ b/mediafusion_scrapy/spiders/dlhd.py @@ -96,33 +96,16 @@ class DaddyLiveHDSpider(scrapy.Spider): item_copy.update( { "stream_name": channel["channel_name"], + "stream_url": m3u8_url, "stream_headers": { "Referer": self.referer, "Origin": self.referer.rstrip("/"), }, + "response_headers": { + "Content-Type": "application/vnd.apple.mpegurl", + }, "channel_id": channel["channel_id"], } ) - yield scrapy.Request( - m3u8_url, - self.parse_stream_link, - meta={ - "item": item_copy, - "dont_redirect": True, - "handle_httpstatus_list": [301], - }, - headers={"Referer": self.referer}, - dont_filter=True, - ) - - def parse_stream_link(self, response): - item = response.meta["item"] - stream_url = response.headers.get("Location", b"").decode("utf-8") - if not stream_url: - self.logger.error( - f"Failed to get stream URL for {item['stream_name']} channel_id: {item['channel_id']}" - ) - return - item["stream_url"] = stream_url - yield item + yield item_copy diff --git a/mediafusion_scrapy/spiders/tgx.py b/mediafusion_scrapy/spiders/tgx.py index cbb9ea5..1472ec8 100644 --- a/mediafusion_scrapy/spiders/tgx.py +++ b/mediafusion_scrapy/spiders/tgx.py @@ -327,7 +327,7 @@ class FormulaTgxSpider(TgxSpider): logo_image = "https://i.postimg.cc/Sqf4V8tj/f1logo.png?dl=1" keyword_patterns = re.compile(r"formula[ .+]*[1234e]+", re.IGNORECASE) - scraped_info_hash_key = "formula_tgx_scraped_info_hash3" + scraped_info_hash_key = "formula_tgx_scraped_info_hash" custom_settings = { "ITEM_PIPELINES": { diff --git a/resources/exceptions/content_infringing.mp4 b/resources/exceptions/content_infringing.mp4 new file mode 100644 index 0000000..e651f30 Binary files /dev/null and b/resources/exceptions/content_infringing.mp4 differ diff --git a/resources/exceptions/ip_not_allowed.mp4 b/resources/exceptions/ip_not_allowed.mp4 new file mode 100644 index 0000000..524a350 Binary files /dev/null and b/resources/exceptions/ip_not_allowed.mp4 differ diff --git a/resources/exceptions/too_many_requests.mp4 b/resources/exceptions/too_many_requests.mp4 new file mode 100644 index 0000000..1e027af Binary files /dev/null and b/resources/exceptions/too_many_requests.mp4 differ diff --git a/resources/html/configure.html b/resources/html/configure.html index 1312421..64627ee 100644 --- a/resources/html/configure.html +++ b/resources/html/configure.html @@ -509,7 +509,7 @@
Please enter a valid MediaFlow Proxy URL. @@ -528,6 +528,14 @@ Please enter the MediaFlow API password.
+
+ + + +
diff --git a/resources/js/config_script.js b/resources/js/config_script.js index 02f704b..20129d7 100644 --- a/resources/js/config_script.js +++ b/resources/js/config_script.js @@ -313,6 +313,7 @@ function getUserData() { mediaflowConfig = { proxy_url: document.getElementById('mediaflow_proxy_url').value, api_password: document.getElementById('mediaflow_api_password').value, + mediaflow_public_ip: document.getElementById('mediaflow_public_ip').value, proxy_live_streams: document.getElementById('proxy_live_streams').checked, proxy_debrid_streams: document.getElementById('proxy_debrid_streams').checked }; diff --git a/resources/json/scraper_config.json b/resources/json/scraper_config.json index 1a491cc..fbc4717 100644 --- a/resources/json/scraper_config.json +++ b/resources/json/scraper_config.json @@ -1,6 +1,6 @@ { "start_urls": { - "nowsports": "https://nowmesports.nl/", + "nowsports": "https://nowmesport.com/", "nowmetv": "https://nowmelive.com/", "tamilultra": "https://tamilultra.tv/", "tamilbulb": "https://tamilbulb.tv/", @@ -283,8 +283,9 @@ }, "dlhd": { "schedule_url": "https://dlhd.so/schedule/schedule-generated.json", - "m3u8_base_url": "https://webhdrunns.mizhls.ru/lb/premium{channel_id}/index.m3u8", - "referer": "https://cookiewebplay.xyz/", + "m3u8_base_url": "https://xyzdddd.mizhls.ru/lb/premium{channel_id}/index.m3u8", + "key_url": "https://key.keylocking.ru", + "referer": "https://ilovetoplay.xyz/", "category_mapping": { "Tv Shows": "Other Sports", "Soccer": "Football", diff --git a/resources/manifest.json b/resources/manifest.json index df60db9..f7098fa 100644 --- a/resources/manifest.json +++ b/resources/manifest.json @@ -1,6 +1,6 @@ { "id": "stremio.addons", - "version": "4.0.1", + "version": "4.0.2", "name": "Media Fusion", "contactEmail": "mhdzumair@gmail.com", "description": "Universal Stremio Add-on for Movies, Series, Live TV & Sports Events. Source: https://github.com/mhdzumair/MediaFusion", diff --git a/scrapers/base_scraper.py b/scrapers/base_scraper.py index 47b4aa2..11e1e39 100644 --- a/scrapers/base_scraper.py +++ b/scrapers/base_scraper.py @@ -1,5 +1,6 @@ import abc import logging +import time from datetime import timedelta from functools import wraps from typing import List, Any, Dict @@ -40,7 +41,7 @@ class BaseScraper(abc.ABC): @staticmethod def cache(ttl: int = 3600): """ - Decorator for caching the results of a method. + Decorator for caching the scraping status using Redis Sorted Sets with timestamps. :param ttl: Time to live for the cache in seconds """ @@ -48,12 +49,23 @@ class BaseScraper(abc.ABC): @wraps(func) async def wrapper(self, *args, **kwargs): cache_key = self.get_cache_key(*args, **kwargs) - cached_result = await REDIS_ASYNC_CLIENT.get(cache_key) - if cached_result: - return [] + current_time = int(time.time()) + + # Check if the item has been scraped recently + score = await REDIS_ASYNC_CLIENT.zscore( + self.cache_key_prefix, cache_key + ) + + if score and current_time - score < ttl: + return [] # Item has been scraped recently, no need to scrape again result = await func(self, *args, **kwargs) - await REDIS_ASYNC_CLIENT.set(cache_key, "True", ex=ttl) + + # Mark the item as scraped with the current timestamp + await REDIS_ASYNC_CLIENT.zadd( + self.cache_key_prefix, {cache_key: current_time} + ) + return result return wrapper @@ -145,11 +157,9 @@ class BaseScraper(abc.ABC): :return: Cache key string """ if catalog_type == "movie": - return f"{self.cache_key_prefix}:{catalog_type}:{metadata.id}" + return f"{catalog_type}:{metadata.id}" - return ( - f"{self.cache_key_prefix}:{catalog_type}:{metadata.id}:{season}:{episode}" - ) + return f"{catalog_type}:{metadata.id}:{season}:{episode}" def validate_title_and_year( self, @@ -223,3 +233,11 @@ class BaseScraper(abc.ABC): from db.crud import store_new_torrent_streams await store_new_torrent_streams(streams) + + @staticmethod + async def remove_expired_items(scraper_prefix: str, ttl: int = 3600): + """ + Remove expired items from the cache. + """ + current_time = int(time.time()) + await REDIS_ASYNC_CLIENT.zremrangebyscore(scraper_prefix, 0, current_time - ttl) diff --git a/scrapers/prowlarr.py b/scrapers/prowlarr.py index da80f95..a34da26 100644 --- a/scrapers/prowlarr.py +++ b/scrapers/prowlarr.py @@ -5,6 +5,7 @@ from typing import List, Dict, Any, AsyncGenerator, Literal, AsyncIterable import PTT import dramatiq import httpx +from scrapeops_python_requests.scrapeops_requests import ScrapeOpsRequests from torf import Magnet, MagnetError from db.config import settings @@ -21,7 +22,7 @@ from scrapers.base_scraper import BaseScraper from scrapers.imdb_data import get_episode_by_date, get_season_episodes from utils.network import CircuitBreaker, batch_process_with_circuit_breaker from utils.parser import is_contain_18_plus_keywords -from utils.runtime_const import REDIS_ASYNC_CLIENT +from utils.runtime_const import REDIS_ASYNC_CLIENT, PROWLARR_SEARCH_TTL from utils.torrent import extract_torrent_metadata from utils.wrappers import minimum_run_interval @@ -47,6 +48,10 @@ allowlist_keywords = [ # fmt: on +class MaxProcessLimitReached(Exception): + pass + + class ProwlarrScraper(BaseScraper): MOVIE_SEARCH_QUERY_TEMPLATES = [ "{title} ({year})", # Exact match with year @@ -60,16 +65,17 @@ class ProwlarrScraper(BaseScraper): "{title} S{season:02d}", # Short season search with leading zeros "{title}", # Title-only fallback ] + cache_key_prefix = "prowlarr" def __init__(self): super().__init__( - cache_key_prefix="prowlarr", logger_name=self.__class__.__name__ + cache_key_prefix=self.cache_key_prefix, logger_name=self.__class__.__name__ ) self.base_url = f"{settings.prowlarr_url}/api/v1/search" + self.scrapeops_logger = None + self.scrape_response = None - @BaseScraper.cache( - ttl=int(timedelta(hours=settings.prowlarr_search_interval_hour).total_seconds()) - ) + @BaseScraper.cache(ttl=PROWLARR_SEARCH_TTL) @BaseScraper.rate_limit(calls=5, period=timedelta(seconds=1)) async def scrape_and_parse( self, @@ -80,6 +86,16 @@ class ProwlarrScraper(BaseScraper): ) -> List[TorrentStreams]: results = [] processed_info_hashes: set[str] = set() + job_name = f"{metadata.title}:{metadata.id}" + if catalog_type == "series": + job_name += f":{season}:{episode}" + + if settings.scrapeops_api_key: + self.scrapeops_logger = ScrapeOpsRequests( + scrapeops_api_key=settings.scrapeops_api_key, + spider_name="Prowlarr Scraper", + job_name=job_name, + ) try: async for stream in self._scrape_and_parse( @@ -90,6 +106,11 @@ class ProwlarrScraper(BaseScraper): episode, ): results.append(stream) + if settings.scrapeops_api_key: + self.scrapeops_logger.item_scraped( + item=stream.model_dump(include={"id"}), + response=self.scrape_response, + ) except httpx.ReadTimeout: self.logger.warning("Timeout while fetching search results") except httpx.HTTPStatusError as e: @@ -98,6 +119,9 @@ class ProwlarrScraper(BaseScraper): ) except Exception as e: self.logger.exception(f"An error occurred during scraping: {str(e)}") + finally: + if settings.scrapeops_api_key: + self.scrapeops_logger.logger.close_sdk() self.logger.info( f"Returning {len(results)} scraped streams for {metadata.title}" @@ -260,7 +284,7 @@ class ProwlarrScraper(BaseScraper): if max_process and streams_processed >= max_process: self.logger.info(f"Reached max process limit of {max_process}") - break + raise MaxProcessLimitReached("Max process limit reached") try: async with asyncio.timeout(max_process_time): @@ -279,6 +303,10 @@ class ProwlarrScraper(BaseScraper): f"Stream processing timed out after {max_process_time} seconds. " f"Processed {streams_processed} streams" ) + except MaxProcessLimitReached: + self.logger.info( + f"Stream processing cancelled after reaching max process limit of {max_process}" + ) except Exception as e: self.logger.error(f"An error occurred during stream processing: {e}") self.logger.info( @@ -295,6 +323,7 @@ class ProwlarrScraper(BaseScraper): params=params, timeout=settings.prowlarr_search_query_timeout, ) + self.scrape_response = response response.raise_for_status() return response.json() @@ -655,7 +684,12 @@ class ProwlarrScraper(BaseScraper): redirect_url = response.headers.get("Location") return await self.get_torrent_data(redirect_url, indexer) response.raise_for_status() - return extract_torrent_metadata(response.content, is_parse_ptt=False), True + if response.headers.get("Content-Type") == "application/x-bittorrent": + return ( + extract_torrent_metadata(response.content, is_parse_ptt=False), + True, + ) + return {}, False @staticmethod def parse_title_data(title: str) -> dict: @@ -713,6 +747,13 @@ async def background_movie_title_search( if not metadata: return + if settings.scrapeops_api_key: + scraper.scrapeops_logger = ScrapeOpsRequests( + scrapeops_api_key=settings.scrapeops_api_key, + spider_name="Prowlarr Scraper", + job_name=f"background:{metadata.title}:{metadata.id}", + ) + title_streams_generators = [ scraper.scrape_movie_by_title( processed_info_hashes, @@ -725,6 +766,11 @@ async def background_movie_title_search( try: async for stream in scraper.process_streams(*title_streams_generators): await scraper.store_streams([stream]) + if settings.scrapeops_api_key: + scraper.scrapeops_logger.item_scraped( + item=stream.model_dump(include={"id"}), + response=scraper.scrape_response, + ) except httpx.ReadTimeout: scraper.logger.warning( f"Timeout while fetching search results for movie {metadata.title} ({metadata.year}), retrying later" @@ -738,6 +784,9 @@ async def background_movie_title_search( scraper.logger.error( f"Error fetching search results: {e.response.text}, status code: {e.response.status_code}" ) + finally: + if settings.scrapeops_api_key: + scraper.scrapeops_logger.logger.close_sdk() scraper.logger.info( f"Background title search completed for {metadata.title} ({metadata.year})" @@ -762,6 +811,13 @@ async def background_series_title_search( if not metadata: return + if settings.scrapeops_api_key: + scraper.scrapeops_logger = ScrapeOpsRequests( + scrapeops_api_key=settings.scrapeops_api_key, + spider_name="Prowlarr Scraper", + job_name=f"background:{metadata.title}:{metadata.id}:{season}:{episode}", + ) + title_streams_generators = [ scraper.scrape_series_by_title( processed_info_hashes, @@ -778,6 +834,11 @@ async def background_series_title_search( try: async for stream in scraper.process_streams(*title_streams_generators): await scraper.store_streams([stream]) + if settings.scrapeops_api_key: + scraper.scrapeops_logger.item_scraped( + item=stream.model_dump(include={"id"}), + response=scraper.scrape_response, + ) except httpx.ReadTimeout: scraper.logger.warning( f"Timeout while fetching search results for {metadata.title} S{season}E{episode}, retrying later" @@ -792,6 +853,9 @@ async def background_series_title_search( scraper.logger.error( f"Error fetching search results: {e.response.text}, status code: {e.response.status_code}" ) + finally: + if settings.scrapeops_api_key: + scraper.scrapeops_logger.logger.close_sdk() scraper.logger.info( f"Background title search completed for {metadata.title} S{season}E{episode}" diff --git a/scrapers/prowlarr_feed.py b/scrapers/prowlarr_feed.py index 423119a..7cccf6e 100644 --- a/scrapers/prowlarr_feed.py +++ b/scrapers/prowlarr_feed.py @@ -2,6 +2,7 @@ import logging import dramatiq import httpx +from scrapeops_python_requests.scrapeops_requests import ScrapeOpsRequests from db.config import settings from db.crud import ( @@ -40,6 +41,13 @@ async def mark_item_as_processed(item_id: str): async def scrape_prowlarr_feed(): scraper = ProwlarrScraper() + if settings.scrapeops_api_key: + scraper.scrapeops_logger = ScrapeOpsRequests( + scrapeops_api_key=settings.scrapeops_api_key, + spider_name="Prowlarr Scraper", + job_name="Prowlarr Feed Scraper", + ) + params = { "type": "search", "categories": [2000, 5000, 8000], # Movies, TV, and Other categories @@ -70,6 +78,9 @@ async def scrape_prowlarr_feed(): except Exception as e: logger.exception(f"Error scraping Prowlarr feed: {e}") + finally: + if scraper.scrapeops_logger: + scraper.scrapeops_logger.logger.close_sdk() async def process_feed_item(item: dict, scraper: ProwlarrScraper): @@ -83,6 +94,12 @@ async def process_feed_item(item: dict, scraper: ProwlarrScraper): parsed_title_data = scraper.parse_title_data(item["title"]) if is_contain_18_plus_keywords(item["title"]): logger.warning(f"Item {item['title']} contains black listed keywords") + if settings.scrapeops_api_key: + scraper.scrapeops_logger.logger.item_dropped( + item={"info_hash": item_id}, + response=scraper.scrape_response, + message="Contains blacklisted keywords", + ) return item_id # Determine media type @@ -97,10 +114,22 @@ async def process_feed_item(item: dict, scraper: ProwlarrScraper): logger.warning( f"Category 8000 item {item['title']} does not match expected format" ) + if settings.scrapeops_api_key: + scraper.scrapeops_logger.logger.item_dropped( + item={"info_hash": item_id}, + response=scraper.scrape_response, + message="Does not match expected format", + ) return item_id media_type = "series" if parsed_title_data.get("seasons") else "movie" else: logger.warning(f"Unsupported category {category_ids} for item {item['title']}") + if settings.scrapeops_api_key: + scraper.scrapeops_logger.logger.item_dropped( + item={"info_hash": item_id}, + response=scraper.scrape_response, + message="Unsupported category", + ) return item_id # Fetch or create metadata @@ -111,6 +140,12 @@ async def process_feed_item(item: dict, scraper: ProwlarrScraper): if not metadata: logger.warning(f"Unable to find or create metadata for {item['title']}") + if settings.scrapeops_api_key: + scraper.scrapeops_logger.logger.item_dropped( + item={"info_hash": item_id}, + response=scraper.scrape_response, + message="Unable to find or create metadata", + ) return item_id # Process the stream @@ -119,9 +154,19 @@ async def process_feed_item(item: dict, scraper: ProwlarrScraper): ) if stream: await store_new_torrent_streams([stream]) + if settings.scrapeops_api_key: + scraper.scrapeops_logger.item_scraped( + item={"info_hash": item_id}, response=scraper.scrape_response + ) return item_id else: logger.warning(f"Failed to process stream for {item['title']}") + if settings.scrapeops_api_key: + scraper.scrapeops_logger.logger.item_dropped( + item={"info_hash": item_id}, + response=scraper.scrape_response, + message="Failed to process stream", + ) return item_id @@ -150,6 +195,6 @@ async def search_and_create_metadata(metadata: dict, media_type: str): max_backoff=3600000, priority=50, ) -async def run_prowlarr_feed_scraper(): +async def run_prowlarr_feed_scraper(**kwargs): logger.info("Running Prowlarr feed scraper") await scrape_prowlarr_feed() diff --git a/scrapers/torrent_info.py b/scrapers/torrent_info.py index 05b8a98..317ea4c 100644 --- a/scrapers/torrent_info.py +++ b/scrapers/torrent_info.py @@ -7,9 +7,10 @@ from utils.torrent import get_info_hash_from_magnet async def get_torrent_info(url: str, indexer: str) -> dict: torrent_info = {} + pre_process_func = pre_process_map.get(indexer, get_page_bs4) parse_func = parser_function_map.get(indexer, parse_common_torrents) - soup = await get_page_bs4(url) + soup = await pre_process_func(url) if not soup: return torrent_info @@ -43,7 +44,7 @@ def parse_torrent_downloads(soup, torrent_info, url): info_hash = ( info_hash_span.find_parent("p").text.replace("Infohash:", "").strip() ) - torrent_info["infoHash"] = info_hash.strip() + torrent_info["infoHash"] = info_hash return torrent_info @@ -74,10 +75,8 @@ def parse_badass_torrents(soup, torrent_info, url): torrent_link = soup.find("a", string="Torrent Download") if torrent_link: torrent_info["downloadUrl"] = urljoin(url, torrent_link.get("href")) - torrent_info["magnetUrl"] = None - elif magnet_link: + if magnet_link: torrent_info["magnetUrl"] = magnet_link.get("href") - torrent_info["downloadUrl"] = None return torrent_info @@ -116,6 +115,15 @@ def parse_gktorrent(soup, torrent_info, url): return torrent_info +async def pre_process_therarbg(url): + url = url.replace("?format=json", "") + return await get_page_bs4(url) + + +pre_process_map = { + "TheRARBG": pre_process_therarbg, +} + parser_function_map = { "1337x": parse_1337x, "TheRARBG": parse_common_torrents, diff --git a/scrapers/torrentio.py b/scrapers/torrentio.py index 446497d..c04bbf8 100644 --- a/scrapers/torrentio.py +++ b/scrapers/torrentio.py @@ -5,6 +5,7 @@ from os import path from typing import List, Dict, Any import PTT +from scrapeops_python_requests.scrapeops_requests import ScrapeOpsRequests from tenacity import RetryError from db.config import settings @@ -14,22 +15,21 @@ from utils.parser import ( convert_size_to_bytes, is_contain_18_plus_keywords, ) +from utils.runtime_const import TORRENTIO_SEARCH_TTL from utils.validation_helper import is_video_file -from scrapeops_python_requests.scrapeops_requests import ScrapeOpsRequests - class TorrentioScraper(BaseScraper): + cache_key_prefix = "torrentio" + def __init__(self): super().__init__( - cache_key_prefix="torrentio", logger_name=self.__class__.__name__ + cache_key_prefix=self.cache_key_prefix, logger_name=self.__class__.__name__ ) self.base_url = settings.torrentio_url self.semaphore = asyncio.Semaphore(10) - @BaseScraper.cache( - ttl=int(timedelta(days=settings.torrentio_search_interval_days).total_seconds()) - ) + @BaseScraper.cache(ttl=TORRENTIO_SEARCH_TTL) @BaseScraper.rate_limit(calls=5, period=timedelta(seconds=1)) async def scrape_and_parse( self, @@ -44,14 +44,17 @@ class TorrentioScraper(BaseScraper): url = f"{self.base_url}/stream/{catalog_type}/{metadata.id}:{season}:{episode}.json" job_name += f":{season}:{episode}" - scrapeops_logger = ScrapeOpsRequests( - scrapeops_api_key=settings.scrapeops_api_key, - spider_name="Torrentio Scraper", - job_name=job_name, - ) + scrapeops_logger = None + if settings.scrapeops_api_key: + scrapeops_logger = ScrapeOpsRequests( + scrapeops_api_key=settings.scrapeops_api_key, + spider_name="Torrentio Scraper", + job_name=job_name, + ) try: response = await self.make_request(url) + response.raise_for_status() data = response.json() if not self.validate_response(data): @@ -62,9 +65,10 @@ class TorrentioScraper(BaseScraper): data, metadata, catalog_type, season, episode ) for stream in stream_data: - scrapeops_logger.item_scraped( - item=stream.model_dump(include={"id", "meta_id"}), response=response - ) + if scrapeops_logger: + scrapeops_logger.item_scraped( + item=stream.model_dump(include={"id"}), response=response + ) return stream_data except (ScraperError, RetryError): return [] @@ -72,7 +76,8 @@ class TorrentioScraper(BaseScraper): self.logger.exception(f"Error occurred while fetching {url}: {e}") return [] finally: - scrapeops_logger.logger.close_sdk() + if scrapeops_logger: + scrapeops_logger.logger.close_sdk() def validate_response(self, response: Dict[str, Any]) -> bool: return "streams" in response and isinstance(response["streams"], list) diff --git a/scrapers/utils.py b/scrapers/utils.py index d0fe5e5..64369de 100644 --- a/scrapers/utils.py +++ b/scrapers/utils.py @@ -1,11 +1,19 @@ import asyncio import logging +import dramatiq + from db.config import settings from db.models import TorrentStreams, MediaFusionMetaData +from scrapers.base_scraper import BaseScraper from scrapers.prowlarr import ProwlarrScraper from scrapers.torrentio import TorrentioScraper from scrapers.zilean import ZileanScraper +from utils.runtime_const import ( + ZILEAN_SEARCH_TTL, + TORRENTIO_SEARCH_TTL, + PROWLARR_SEARCH_TTL, +) async def run_scrapers( @@ -46,3 +54,19 @@ async def run_scrapers( unique_streams = set(scraped_streams) logging.info(f"Scraped {len(scraped_streams)} streams for {metadata.title}") return unique_streams + + +@dramatiq.actor( + time_limit=5 * 60 * 1000, # 5 minutes + priority=20, +) +async def cleanup_expired_scraper_task(**kwargs): + await BaseScraper.remove_expired_items( + ProwlarrScraper.cache_key_prefix, PROWLARR_SEARCH_TTL + ) + await BaseScraper.remove_expired_items( + TorrentioScraper.cache_key_prefix, TORRENTIO_SEARCH_TTL + ) + await BaseScraper.remove_expired_items( + ZileanScraper.cache_key_prefix, ZILEAN_SEARCH_TTL + ) diff --git a/scrapers/zilean.py b/scrapers/zilean.py index f8a7357..2970655 100644 --- a/scrapers/zilean.py +++ b/scrapers/zilean.py @@ -3,25 +3,28 @@ from datetime import timedelta from typing import List, Dict, Any import PTT +from httpx import Response from tenacity import RetryError - +from scrapeops_python_requests.scrapeops_requests import ScrapeOpsRequests from db.config import settings from db.models import TorrentStreams, Season, Episode, MediaFusionMetaData from scrapers.base_scraper import BaseScraper, ScraperError from utils.parser import ( is_contain_18_plus_keywords, ) +from utils.runtime_const import ZILEAN_SEARCH_TTL class ZileanScraper(BaseScraper): + cache_key_prefix = "zilean" + def __init__(self): - super().__init__(cache_key_prefix="zilean", logger_name=self.__class__.__name__) - self.base_url = f"{settings.zilean_url}/dmm/search" + super().__init__( + cache_key_prefix=self.cache_key_prefix, logger_name=self.__class__.__name__ + ) self.semaphore = asyncio.Semaphore(10) - @BaseScraper.cache( - ttl=int(timedelta(hours=settings.prowlarr_search_interval_hour).total_seconds()) - ) + @BaseScraper.cache(ttl=ZILEAN_SEARCH_TTL) @BaseScraper.rate_limit(calls=5, period=timedelta(seconds=1)) async def scrape_and_parse( self, @@ -30,22 +33,86 @@ class ZileanScraper(BaseScraper): season: int = None, episode: int = None, ) -> List[TorrentStreams]: - try: - stream_response = await self.make_request( - self.base_url, + job_name = f"{metadata.title}:{metadata.id}" + if catalog_type == "series": + job_name += f":{season}:{episode}" + + scrapeops_logger = None + if settings.scrapeops_api_key: + scrapeops_logger = ScrapeOpsRequests( + scrapeops_api_key=settings.scrapeops_api_key, + spider_name="Zilean Scraper", + job_name=job_name, + ) + + search_task = asyncio.create_task( + self.make_request( + f"{settings.zilean_url}/dmm/search", method="POST", json={"queryText": metadata.title}, timeout=10, ) + ) - stream_data = stream_response.json() - if not self.validate_response(stream_data): - self.logger.warning(f"Invalid response received for {metadata.title}") - return [] + if metadata.type == "movie": + params = { + "Query": metadata.title, + "Year": metadata.year, + } + else: + params = { + "Query": metadata.title, + "Season": season, + "Episode": episode, + } - return await self.parse_response( + filtered_task = asyncio.create_task( + self.make_request( + f"{settings.zilean_url}/dmm/filtered", + method="GET", + params=params, + timeout=10, + ) + ) + + search_response, filtered_response = await asyncio.gather( + search_task, filtered_task, return_exceptions=True + ) + + stream_data = [] + response = None + if isinstance(search_response, Response): + response = search_response + stream_data.extend(search_response.json()) + else: + self.logger.error( + f"Error occurred while search {metadata.title}: {search_response}" + ) + if isinstance(filtered_response, Response): + response = filtered_response + stream_data.extend(filtered_response.json()) + else: + self.logger.error( + f"Error occurred while filtering {metadata.title}: {filtered_response}" + ) + + if not self.validate_response(stream_data): + self.logger.error(f"No valid streams found for {metadata.title}") + if scrapeops_logger: + scrapeops_logger.logger.close_sdk() + return [] + + try: + streams = await self.parse_response( stream_data, metadata, catalog_type, season, episode ) + if scrapeops_logger: + for stream in streams: + scrapeops_logger.item_scraped( + item=stream.model_dump(include={"id"}), + response=response, + ) + return streams except (ScraperError, RetryError): return [] except Exception as e: @@ -53,6 +120,9 @@ class ZileanScraper(BaseScraper): f"Error occurred while fetching {metadata.title}: {e}" ) return [] + finally: + if scrapeops_logger: + scrapeops_logger.logger.close_sdk() async def parse_response( self, diff --git a/streaming_providers/pikpak/utils.py b/streaming_providers/pikpak/utils.py index f02d743..4c5163e 100644 --- a/streaming_providers/pikpak/utils.py +++ b/streaming_providers/pikpak/utils.py @@ -10,6 +10,8 @@ from db.models import TorrentStreams from db.schemas import UserData from streaming_providers.exceptions import ProviderException from streaming_providers.parser import select_file_index_from_torrent +from utils import crypto +from utils.runtime_const import REDIS_ASYNC_CLIENT async def get_torrent_file_by_info_hash( @@ -141,6 +143,22 @@ async def find_file_in_folder_tree( async def initialize_pikpak(user_data: UserData): + cache_key = f"pikpak:{crypto.get_text_hash(user_data.streaming_provider.email + user_data.streaming_provider.password, full_hash=True)}" + if pikpak_encrypted_token := await REDIS_ASYNC_CLIENT.get(cache_key): + pikpak_encoded_token = crypto.decrypt_text( + pikpak_encrypted_token, user_data.streaming_provider.password + ) + pikpak = PikPakApi( + encoded_token=pikpak_encoded_token, + httpx_client_args={ + "transport": httpx.AsyncHTTPTransport(retries=3), + "timeout": 10, + }, + token_refresh_callback=store_pikpak_token_in_cache, + token_refresh_callback_kwargs={"user_data": user_data}, + ) + return pikpak + pikpak = PikPakApi( username=user_data.streaming_provider.email, password=user_data.streaming_provider.password, @@ -148,7 +166,10 @@ async def initialize_pikpak(user_data: UserData): "transport": httpx.AsyncHTTPTransport(retries=3), "timeout": 10, }, + token_refresh_callback=store_pikpak_token_in_cache, + token_refresh_callback_kwargs={"user_data": user_data}, ) + try: await pikpak.login() except PikpakException as error: @@ -166,9 +187,22 @@ async def initialize_pikpak(user_data: UserData): "Failed to connect to PikPak. Please try again later.", "debrid_service_down_error.mp4", ) + + await store_pikpak_token_in_cache(pikpak, user_data) return pikpak +async def store_pikpak_token_in_cache(pikpak: PikPakApi, user_data: UserData): + cache_key = f"pikpak:{crypto.get_text_hash(user_data.streaming_provider.email + user_data.streaming_provider.password, full_hash=True)}" + await REDIS_ASYNC_CLIENT.set( + cache_key, + crypto.encrypt_text( + pikpak.encoded_token, user_data.streaming_provider.password + ), + ex=7 * 24 * 60 * 60, # Store for 7 days + ) + + async def handle_torrent_status( pikpak: PikPakApi, info_hash: str, diff --git a/streaming_providers/realdebrid/client.py b/streaming_providers/realdebrid/client.py index be543e5..6a18954 100644 --- a/streaming_providers/realdebrid/client.py +++ b/streaming_providers/realdebrid/client.py @@ -18,13 +18,26 @@ class RealDebrid(DebridClient): super().__init__(token) def _handle_service_specific_errors(self, error): - if ( - error.response.status_code == 403 - and error.response.json().get("error_code") == 9 - ): - raise ProviderException( - "Real-Debrid Permission denied for free account", "need_premium.mp4" - ) + if error.response.status_code == 403: + error_code = error.response.json().get("error_code") + match error_code: + case 9: + raise ProviderException( + "Real-Debrid Permission denied for free account", + "need_premium.mp4", + ) + case 22: + raise ProviderException( + "IP address not allowed", "ip_not_allowed.mp4" + ) + case 34: + raise ProviderException( + "Too many requests", "too_many_requests.mp4" + ) + case 35: + raise ProviderException( + "Content marked as infringing", "content_infringing.mp4" + ) def _make_request( self, diff --git a/utils/cleanup_torrents.py b/utils/cleanup_torrents.py index 165b892..577b5a7 100644 --- a/utils/cleanup_torrents.py +++ b/utils/cleanup_torrents.py @@ -1,6 +1,7 @@ import asyncio -import sys import logging +import sys +from datetime import datetime, timezone, timedelta from typing import Dict import PTT @@ -12,10 +13,13 @@ from db.models import ( TorrentStreams, MediaFusionSeriesMetaData, MediaFusionMovieMetaData, + MediaFusionMetaData, ) from utils.parser import calculate_max_similarity_ratio, is_contain_18_plus_keywords BATCH_SIZE = 1000 # Adjust the batch size as needed +SKIP_TORRENTS = 0 # Skip the first n torrents +LAST_UPDATED = datetime.now(tz=timezone.utc) - timedelta(days=1) # Configure logging logging.basicConfig( @@ -26,49 +30,63 @@ logger = logging.getLogger(__name__) async def cleanup_torrents(dry_run: bool = True) -> Dict[str, int]: metrics = { - "total_torrents": 0, - "removed_torrents": 0, - "updated_torrents": 0, + "total": 0, + "removed": 0, "no_metadata": 0, "valid": 0, - "ratio_error": 0, - "adult_content": 0, + "ratio": 0, + "adult": 0, "year_mismatch": 0, "removed_non_imdb": 0, + "non_movie": 0, } lock = asyncio.Lock() - total_torrents = await TorrentStreams.count() - progress_bar = tqdm( - range(0, total_torrents, BATCH_SIZE), desc="Processing torrents" + + # Create a cursor for efficient pagination + cursor = TorrentStreams.find(TorrentStreams.updated_at < LAST_UPDATED).sort( + -TorrentStreams.updated_at ) + total_torrents = await cursor.count() + total_torrents = max(0, total_torrents - SKIP_TORRENTS) + + progress_bar = tqdm(total=total_torrents, desc="Processing torrents") torrent_bulk_writer = BulkWriter() movies_bulk_writer = BulkWriter() series_bulk_writer = BulkWriter() - for skip in progress_bar: - torrents = await TorrentStreams.find().skip(skip).limit(BATCH_SIZE).to_list() - tasks = [ - process_torrent( - torrent, - dry_run, - metrics, - lock, - torrent_bulk_writer, - movies_bulk_writer, - series_bulk_writer, - ) - for torrent in torrents - ] - await asyncio.gather(*tasks) - await torrent_bulk_writer.commit() - await movies_bulk_writer.commit() - await series_bulk_writer.commit() - torrent_bulk_writer.operations.clear() - movies_bulk_writer.operations.clear() - series_bulk_writer.operations.clear() + cursor = cursor.skip(SKIP_TORRENTS) + processed_count = 0 + async for torrent in cursor: + await process_torrent( + torrent, + dry_run, + metrics, + lock, + torrent_bulk_writer, + movies_bulk_writer, + series_bulk_writer, + ) + + processed_count += 1 + if processed_count >= BATCH_SIZE: + await torrent_bulk_writer.commit() + await movies_bulk_writer.commit() + await series_bulk_writer.commit() + torrent_bulk_writer.operations.clear() + movies_bulk_writer.operations.clear() + series_bulk_writer.operations.clear() + processed_count = 0 + + progress_bar.update(1) progress_bar.set_postfix(metrics) + # Commit any remaining operations + await torrent_bulk_writer.commit() + await movies_bulk_writer.commit() + await series_bulk_writer.commit() + + progress_bar.close() return metrics @@ -82,20 +100,28 @@ async def process_torrent( series_bulk_writer: BulkWriter, ) -> str: async with lock: - metrics["total_torrents"] += 1 + metrics["total"] += 1 - meta_data = None - if any("series" in catalog for catalog in torrent.catalog): - meta_data = await MediaFusionSeriesMetaData.get(torrent.meta_id) - meta_data_bulk_writer = series_bulk_writer - else: - meta_data = await MediaFusionMovieMetaData.get(torrent.meta_id) - meta_data_bulk_writer = movies_bulk_writer + meta_data = await MediaFusionMetaData.get_motor_collection().find_one( + {"_id": torrent.meta_id} + ) if not meta_data: + await torrent.delete(bulk_writer=torrent_bulk_writer) async with lock: metrics["no_metadata"] += 1 - return "no_metadata" + metrics["removed"] += 1 + logger.debug( + f"No metadata found for torrent: {torrent.torrent_name}, {torrent.meta_id}" + ) + return "removed" + + if meta_data["type"] == "movie": + meta_data = MediaFusionMovieMetaData(**meta_data) + meta_data_bulk_writer = movies_bulk_writer + else: + meta_data = MediaFusionSeriesMetaData(**meta_data) + meta_data_bulk_writer = series_bulk_writer if ( not dry_run @@ -109,16 +135,17 @@ async def process_torrent( await torrent.delete(bulk_writer=torrent_bulk_writer) await meta_data.delete(bulk_writer=meta_data_bulk_writer) async with lock: - metrics["removed_torrents"] += 1 + metrics["removed"] += 1 metrics["removed_non_imdb"] += 1 + logger.debug(f"Removed non-IMDb torrent: {torrent.torrent_name}") return "removed" if is_contain_18_plus_keywords(torrent.torrent_name): if not dry_run: await torrent.delete(bulk_writer=torrent_bulk_writer) async with lock: - metrics["removed_torrents"] += 1 - metrics["adult_content"] += 1 + metrics["removed"] += 1 + metrics["adult"] += 1 logger.debug(f"Removed torrent due to adult content: {torrent.torrent_name}") return "removed" @@ -128,26 +155,41 @@ async def process_torrent( parsed_data.get("title", ""), meta_data.title, meta_data.aka_titles ) expected_ratio = 70 if torrent.source in ["TamilMV", "TamilBlasters"] else 85 - if max_ratio < expected_ratio and torrent.meta_id.startswith("tt"): + if ( + max_ratio < expected_ratio + and torrent.meta_id.startswith("tt") + and not any(x in torrent.catalog for x in ["wwe_tgx", "ufc_tgx"]) + ): if not dry_run: await torrent.delete(bulk_writer=torrent_bulk_writer) async with lock: - metrics["removed_torrents"] += 1 - metrics["ratio_error"] += 1 + metrics["removed"] += 1 + metrics["ratio"] += 1 logger.debug( f"Removed torrent due to low similarity ratio: {parsed_data.get('title')} != {meta_data.title} (ratio: {max_ratio}%) (full title: {torrent.torrent_name})" ) return "removed" - if "year" in parsed_data and torrent.meta_id.startswith("tt"): - if meta_data.type == "movie" and parsed_data["year"] != meta_data.year: + if torrent.meta_id.startswith("tt") and meta_data.type == "movie": + if parsed_data.get("seasons"): if not dry_run: await torrent.delete(bulk_writer=torrent_bulk_writer) async with lock: - metrics["removed_torrents"] += 1 + metrics["removed"] += 1 + metrics["non_movie"] += 1 + logger.debug( + f"Removed non-movie torrent: {torrent.torrent_name}, found season: {parsed_data['seasons']}. Expected movie." + ) + return "removed" + + if meta_data.type == "movie" and parsed_data.get("year") != meta_data.year: + if not dry_run: + await torrent.delete(bulk_writer=torrent_bulk_writer) + async with lock: + metrics["removed"] += 1 metrics["year_mismatch"] += 1 logger.debug( - f"Removed torrent due to year mismatch: {torrent.torrent_name} (torrent year: {parsed_data['year']}, meta year: {meta_data.year})" + f"Removed torrent due to year mismatch: {torrent.torrent_name} ({parsed_data.get('year')} != {meta_data.year}) ({meta_data.id}) ({meta_data.type})" ) return "removed" @@ -163,14 +205,13 @@ async def process_torrent( torrent.audio = ( parsed_data.get("audio")[0] if parsed_data.get("audio") else None ) + torrent.updated_at = datetime.now(tz=timezone.utc) await torrent.save(bulk_writer=torrent_bulk_writer) - async with lock: - metrics["updated_torrents"] += 1 return "updated" - async with lock: - metrics["valid"] += 1 - return "valid" + torrent.updated_at = datetime.now(tz=timezone.utc) + await torrent.save(bulk_writer=torrent_bulk_writer) + return "updated" # Initialize the database connection and start the cleanup process diff --git a/utils/crypto.py b/utils/crypto.py index 1b57ac8..81ef793 100644 --- a/utils/crypto.py +++ b/utils/crypto.py @@ -14,6 +14,32 @@ from db.schemas import UserData from utils.runtime_const import SECRET_KEY +def encrypt_text(text: str, secret_key: str | bytes) -> str: + iv = get_random_bytes(16) + if isinstance(secret_key, str): + secret_key = secret_key.encode("utf-8") + cipher = AES.new(secret_key.ljust(32)[:32], AES.MODE_CBC, iv) + encoded_text = text.encode("utf-8") + encrypted_data = cipher.encrypt( + encoded_text + b"\0" * (16 - len(encoded_text) % 16) + ) + compressed_data = zlib.compress(iv + encrypted_data) + encrypted_str = urlsafe_b64encode(compressed_data).decode("utf-8") + return encrypted_str + + +def decrypt_text(secret_str: str, secret_key: str | bytes) -> str: + decoded_data = urlsafe_b64decode(secret_str) + encrypted_data = zlib.decompress(decoded_data) + iv = encrypted_data[:16] + if isinstance(secret_key, str): + secret_key = secret_key.encode("utf-8") + cipher = AES.new(secret_key.ljust(32)[:32], AES.MODE_CBC, iv) + decrypted_data = cipher.decrypt(encrypted_data[16:]) + decrypted_data = decrypted_data.rstrip(b"\0") + return decrypted_data.decode("utf-8") + + def encrypt_user_data(user_data: UserData) -> str: data = user_data.model_dump_json( exclude_none=True, @@ -22,26 +48,15 @@ def encrypt_user_data(user_data: UserData) -> str: round_trip=True, by_alias=True, ) - dump_data = data.encode("utf-8") - iv = get_random_bytes(16) - cipher = AES.new(SECRET_KEY, AES.MODE_CBC, iv) - encrypted_data = cipher.encrypt(dump_data + b"\0" * (16 - len(dump_data) % 16)) - compressed_data = zlib.compress(iv + encrypted_data) - encrypted_str = urlsafe_b64encode(compressed_data).decode("utf-8") - return encrypted_str + return encrypt_text(data, SECRET_KEY) def decrypt_user_data(secret_str: str | None = None) -> UserData: if not secret_str: return UserData() try: - decoded_data = urlsafe_b64decode(secret_str) - encrypted_data = zlib.decompress(decoded_data) - iv = encrypted_data[:16] - cipher = AES.new(SECRET_KEY, AES.MODE_CBC, iv) - decrypted_data = cipher.decrypt(encrypted_data[16:]) - decrypted_data = decrypted_data.rstrip(b"\0") - user_data = UserData.model_validate_json(decrypted_data.decode("utf-8")) + decrypted_data = decrypt_text(secret_str, SECRET_KEY) + user_data = UserData.model_validate_json(decrypted_data) except Exception: user_data = UserData() return user_data diff --git a/utils/network.py b/utils/network.py index 83efa00..14566a8 100644 --- a/utils/network.py +++ b/utils/network.py @@ -2,7 +2,7 @@ import asyncio import logging from typing import Callable, AsyncGenerator, Any, Tuple from urllib import parse -from urllib.parse import urlencode +from urllib.parse import urlencode, urlparse import httpx from fastapi.requests import Request @@ -169,14 +169,21 @@ def get_client_ip(request: Request) -> str | None: return request.client.host if request.client else "127.0.0.1" -async def get_mediaflow_proxy_public_ip( - mediaflow_proxy_url: str, api_password -) -> str | None: +async def get_mediaflow_proxy_public_ip(mediaflow_config) -> str | None: """ Get the public IP address of the MediaFlow proxy server. """ + if mediaflow_config.public_ip: + return mediaflow_config.public_ip + + parsed_url = urlparse(mediaflow_config.mediaflow_proxy_url) + if PRIVATE_CIDR.match(parsed_url.netloc): + # MediaFlow proxy URL is a private IP address + return None + cache_key = crypto.get_text_hash( - f"{mediaflow_proxy_url}:{api_password}", full_hash=True + f"{mediaflow_config.mediaflow_proxy_url}:{mediaflow_config.api_password}", + full_hash=True, ) if public_ip := await REDIS_ASYNC_CLIENT.getex(cache_key, ex=300): return public_ip.decode() @@ -184,8 +191,8 @@ async def get_mediaflow_proxy_public_ip( try: async with httpx.AsyncClient() as client: response = await client.get( - parse.urljoin(mediaflow_proxy_url, "/proxy/ip"), - params={"api_password": api_password}, + parse.urljoin(mediaflow_config.mediaflow_proxy_url, "/proxy/ip"), + params={"api_password": mediaflow_config.api_password}, timeout=10, ) response.raise_for_status() @@ -211,10 +218,7 @@ async def get_user_public_ip( and user_data.mediaflow_config and user_data.mediaflow_config.proxy_debrid_streams ): - public_ip = await get_mediaflow_proxy_public_ip( - user_data.mediaflow_config.proxy_url, - user_data.mediaflow_config.api_password, - ) + public_ip = await get_mediaflow_proxy_public_ip(user_data.mediaflow_config) if public_ip: return public_ip # Get the user's public IP address diff --git a/utils/parser.py b/utils/parser.py index d299131..7531507 100644 --- a/utils/parser.py +++ b/utils/parser.py @@ -1,7 +1,8 @@ import asyncio import functools import logging -from typing import Optional, List +from datetime import datetime +from typing import Optional, List, Any import math import re @@ -13,6 +14,7 @@ from db.models import TorrentStreams, TVStreams from db.schemas import Stream, UserData from streaming_providers import mapper from utils import const +from utils.config import config_manager from utils.const import STREAMING_PROVIDERS_SHORT_NAMES from utils.network import encode_mediaflow_proxy_url from utils.runtime_const import ADULT_CONTENT_KEYWORDS, TRACKERS @@ -94,13 +96,29 @@ async def filter_and_sort_streams( ) # Step 3: Dynamically sort streams based on user preferences - def dynamic_sort_key(stream): - return tuple( - ( - const.RESOLUTION_RANKING.get(stream.filtered_resolution, 0) - if key == "resolution" - else ( - -min( + def dynamic_sort_key(stream: TorrentStreams) -> tuple: + def key_value(key: str) -> Any: + match key: + case "cached": + return stream.cached or False + case "resolution": + return const.RESOLUTION_RANKING.get(stream.filtered_resolution, 0) + case "quality": + return const.QUALITY_RANKING.get(stream.filtered_quality, 0) + case "size": + return stream.size + case "seeders": + return stream.seeders or 0 + case "created_at": + created_at = stream.created_at + if isinstance(created_at, datetime): + return created_at + elif isinstance(created_at, (int, float)): + return datetime.fromtimestamp(created_at) + else: + return datetime.min + case "language": + return -min( ( user_data.language_sorting.index(lang) for lang in stream.filtered_languages @@ -108,28 +126,22 @@ async def filter_and_sort_streams( ), default=len(user_data.language_sorting), ) - if key == "language" - else ( - const.QUALITY_RANKING.get(stream.filtered_quality, 0) - if key == "quality" - else ( - getattr(stream, key, 0) - if key in stream.model_fields_set - else 0 - ) - ) - ) - ) - for key in user_data.torrent_sorting_priority + case _ if key in stream.model_fields_set: + return getattr(stream, key, 0) + case _: + return 0 + + return tuple(key_value(key) for key in user_data.torrent_sorting_priority) + + try: + dynamically_sorted_streams = sorted( + filtered_streams, key=dynamic_sort_key, reverse=True ) - - def safe_sort_key(stream): - raw_key = dynamic_sort_key(stream) - return tuple(0 if item is None else item for item in raw_key) - - dynamically_sorted_streams = sorted( - filtered_streams, key=safe_sort_key, reverse=True - ) + except (TypeError, Exception): + logging.exception( + f"torrent_sorting_priority: {user_data.torrent_sorting_priority}: sort data: {[dynamic_sort_key(stream) for stream in filtered_streams]}" + ) + dynamically_sorted_streams = filtered_streams # Step 4: Limit streams per resolution based on user preference, after dynamic sorting limited_streams = [] @@ -390,7 +402,7 @@ async def process_stream( stream_url, behavior_hints = stream.url, stream.behaviorHints behavior_hints = behavior_hints if behavior_hints else {} - if stream.drm_key or "dlhd" in stream.source: + if stream.drm_key: if not is_mediaflow_proxy_enabled: return "MEDIAFLOW_NEEDED" stream_url = get_proxy_url(stream, mediaflow_config) @@ -418,7 +430,10 @@ def get_proxy_url(stream: TVStreams, mediaflow_config) -> str: if stream.drm_key: query_params = {"key_id": stream.drm_key_id, "key": stream.drm_key} elif "dlhd" in stream.source: - query_params = {"use_request_proxy": False} + query_params = { + "use_request_proxy": False, + "key_url": config_manager.get_scraper_config("dlhd", "key_url"), + } return encode_mediaflow_proxy_url( mediaflow_config.proxy_url, @@ -426,6 +441,9 @@ def get_proxy_url(stream: TVStreams, mediaflow_config) -> str: stream.url, query_params=query_params, request_headers=stream.behaviorHints.get("proxyHeaders", {}).get("request", {}), + response_headers=stream.behaviorHints.get("proxyHeaders", {}).get( + "response", {} + ), encryption_api_password=mediaflow_config.api_password, ) @@ -460,7 +478,7 @@ async def fetch_downloaded_info_hashes( return downloaded_info_hashes except Exception as error: - logging.error( + logging.exception( f"Failed to fetch downloaded info hashes for {user_data.streaming_provider.service}: {error}" ) pass diff --git a/utils/runtime_const.py b/utils/runtime_const.py index ba1f4dd..5e6992d 100644 --- a/utils/runtime_const.py +++ b/utils/runtime_const.py @@ -1,4 +1,5 @@ import re +from datetime import timedelta from fastapi.templating import Jinja2Templates import redis @@ -41,3 +42,14 @@ REDIS_SYNC_CLIENT: redis.Redis = redis.Redis( REDIS_ASYNC_CLIENT: redis.asyncio.Redis = redis.asyncio.Redis( connection_pool=redis.asyncio.ConnectionPool.from_url(settings.redis_url) ) + + +PROWLARR_SEARCH_TTL = int( + timedelta(hours=settings.prowlarr_search_interval_hour).total_seconds() +) +TORRENTIO_SEARCH_TTL = int( + timedelta(days=settings.torrentio_search_interval_days).total_seconds() +) +ZILEAN_SEARCH_TTL = int( + timedelta(hours=settings.zilean_search_interval_hour).total_seconds() +) diff --git a/utils/torrent.py b/utils/torrent.py index b6750b5..1a6d342 100644 --- a/utils/torrent.py +++ b/utils/torrent.py @@ -80,7 +80,7 @@ def extract_torrent_metadata(content: bytes, is_parse_ptt: bool = True) -> dict: metadata.update(PTT.parse_title(torrent_name, True)) return metadata except Exception as e: - logging.error(f"Error occurred: {e}") + logging.exception(f"Error occurred: {e}") return {} diff --git a/utils/validation_helper.py b/utils/validation_helper.py index f113aa1..46e2a12 100644 --- a/utils/validation_helper.py +++ b/utils/validation_helper.py @@ -4,8 +4,7 @@ import logging from urllib import parse from urllib.parse import urlparse -import aiohttp -from aiohttp import ClientError +import httpx from db import schemas from utils import const @@ -18,14 +17,17 @@ def is_valid_url(url: str) -> bool: async def does_url_exist(url: str) -> bool: - async with aiohttp.ClientSession() as session: + async with httpx.AsyncClient() as client: try: - async with session.head( - url, allow_redirects=True, timeout=10, headers=const.UA_HEADER - ) as response: - logging.info("URL: %s, Status: %s", url, response.status) - return response.status == 200 - except (ClientError, asyncio.TimeoutError) as err: + response = await client.head( + url, timeout=10, headers=const.UA_HEADER, follow_redirects=True + ) + response.raise_for_status() + return response.status_code == 200 + except httpx.HTTPStatusError as err: + logging.error("URL: %s, Status: %s", url, err.response.status_code) + return False + except (httpx.RequestError, httpx.TimeoutException) as err: logging.error("URL: %s, Status: %s", url, err) return False @@ -41,20 +43,29 @@ async def validate_live_stream_url( return False headers = behaviour_hint.get("proxyHeaders", {}).get("request", {}) - async with aiohttp.ClientSession() as session: + async with httpx.AsyncClient() as client: try: - async with session.head( - url, - allow_redirects=True, - headers=headers, - timeout=aiohttp.ClientTimeout(total=20), - ) as response: - content_type = response.headers.get("content-type", "").lower() - is_valid = content_type in const.IPTV_VALID_CONTENT_TYPES - return is_valid - except (ClientError, asyncio.TimeoutError) as err: + response = await client.head( + url, timeout=10, headers=headers, follow_redirects=True + ) + response.raise_for_status() + content_type = ( + behaviour_hint.get("proxyHeaders", {}) + .get("response", {}) + .get("Content-Type", response.headers.get("content-type", "").lower()) + ) + is_valid = content_type in const.IPTV_VALID_CONTENT_TYPES + return is_valid + except ( + httpx.RequestError, + httpx.TimeoutException, + httpx.HTTPStatusError, + ) as err: logging.error(err) return False + except Exception as e: + logging.exception(e) + return False async def validate_m3u8_or_mpd_url_with_cache(url: str, behaviour_hint: dict): @@ -78,13 +89,12 @@ class ValidationError(Exception): async def validate_yt_id(yt_id: str) -> bool: image_url = f"https://img.youtube.com/vi/{yt_id}/mqdefault.jpg" - async with aiohttp.ClientSession() as session: + async with httpx.AsyncClient() as client: try: - async with session.head( - image_url, allow_redirects=True, timeout=aiohttp.ClientTimeout(total=5) - ) as response: - return response.status == 200 - except (ClientError, asyncio.TimeoutError): + response = await client.head(image_url, timeout=10, follow_redirects=True) + response.raise_for_status() + return response.status_code == 200 + except (httpx.HTTPStatusError, httpx.TimeoutException, Exception): return False