diff --git a/Pipfile b/Pipfile index b603d39233..24c985922d 100644 --- a/Pipfile +++ b/Pipfile @@ -6,6 +6,7 @@ verify_ssl = true [dev-packages] [packages] +annotated-types = {version="==0.7.0", python_version=">= '3.8'"} apluggy = "==0.9.4" attrs = "==23.2.0" cattrs = {version="==23.2.3", python_version=">='3.7'"} @@ -14,6 +15,7 @@ charset-normalizer = "==3.3.2" click = "==8.1.7" decorator = "==5.1.1" deprecated = "==1.2.14" +greenlet = {version="==3.0.3", markers="python_version < '3.13' and platform_machine == 'aarch64' or (platform_machine == 'ppc64le' or (platform_machine == 'x86_64' or (platform_machine == 'amd64' or (platform_machine == 'AMD64' or (platform_machine == 'win32' or platform_machine == 'WIN32')))))"} humanize = "==4.10.0" idna = {version="==3.7", python_version=">='3.5'"} inquirerpy = "==0.3.4" @@ -25,6 +27,8 @@ platformdirs = {version="==4.2.2", python_version=">='3.7'"} plexapi = "==4.15.15" pluggy = "==1.5.0" prompt-toolkit = "==3.0.47" +pydantic = {version="==2.8.2", python_version=">= '3.8'"} +pydantic-core = {version="==2.20.1", python_version=">= '3.8'"} pygments = "==2.18.0" python-dotenv = "==1.0.1" python-git-info = "==0.8.3" @@ -36,6 +40,8 @@ requests-cache = "==1.2.1" requests-oauthlib = {version="==2.0.0", python_version=">='3.4'"} rich = "==13.7.1" six = {version="==1.16.0", python_version=">='3.4'"} +sqlalchemy = {version="==2.0.31", python_version=">= '3.7'"} +sqlmodel = {version="==0.0.19", python_version=">= '3.7'"} tqdm = "==4.66.4" typing-extensions = {version="==4.12.2", python_version=">= '3.8'"} url-normalize = {version="==1.4.3", python_version=">='3.6'"} diff --git a/Pipfile.lock b/Pipfile.lock index 2b4bb21397..32e4dfac22 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "34a4eb2ca07cc65addce6d57fd560c5ea3cd2ed118df271b5e861f0ec0b49904" + "sha256": "eab06a99dc67f57b73ad820af285ee010ef6067601108fada16fe99d29ac007a" }, "pipfile-spec": 6, "requires": { @@ -16,6 +16,14 @@ ] }, "default": { + "annotated-types": { + "hashes": [ + "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", + "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" + ], + "markers": "python_version >= '3.8'", + "version": "==0.7.0" + }, "apluggy": { "hashes": [ "sha256:ecfcd7cad1c484281cd528281104c53657297f8ab8e723f26c2f396a407c8d0c", @@ -179,6 +187,70 @@ "markers": "python_version < '3.11'", "version": "==1.2.1" }, + "greenlet": { + "hashes": [ + "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67", + "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6", + "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257", + "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4", + "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676", + "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61", + "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc", + "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca", + "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7", + "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728", + "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305", + "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6", + "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379", + "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414", + "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04", + "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a", + "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf", + "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491", + "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559", + "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e", + "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274", + "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb", + "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b", + "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9", + "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b", + "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be", + "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506", + "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405", + "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113", + "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f", + "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5", + "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230", + "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d", + "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f", + "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a", + "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e", + "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61", + "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6", + "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d", + "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71", + "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22", + "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2", + "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3", + "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067", + "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc", + "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881", + "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3", + "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e", + "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac", + "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53", + "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0", + "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b", + "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83", + "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41", + "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c", + "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf", + "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da", + "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33" + ], + "markers": "python_version < '3.13' and platform_machine == 'aarch64' or (platform_machine == 'ppc64le' or (platform_machine == 'x86_64' or (platform_machine == 'amd64' or (platform_machine == 'AMD64' or (platform_machine == 'win32' or platform_machine == 'WIN32')))))", + "version": "==3.0.3" + }, "humanize": { "hashes": [ "sha256:06b6eb0293e4b85e8d385397c5868926820db32b9b654b932f57fa41c23c9978", @@ -273,6 +345,109 @@ "markers": "python_full_version >= '3.7.0'", "version": "==3.0.47" }, + "pydantic": { + "hashes": [ + "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a", + "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8" + ], + "markers": "python_version >= '3.8'", + "version": "==2.8.2" + }, + "pydantic-core": { + "hashes": [ + "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d", + "sha256:04024d270cf63f586ad41fff13fde4311c4fc13ea74676962c876d9577bcc78f", + "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686", + "sha256:084659fac3c83fd674596612aeff6041a18402f1e1bc19ca39e417d554468482", + "sha256:10d4204d8ca33146e761c79f83cc861df20e7ae9f6487ca290a97702daf56006", + "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83", + "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6", + "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88", + "sha256:177f55a886d74f1808763976ac4efd29b7ed15c69f4d838bbd74d9d09cf6fa86", + "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a", + "sha256:1eedfeb6089ed3fad42e81a67755846ad4dcc14d73698c120a82e4ccf0f1f9f6", + "sha256:225b67a1f6d602de0ce7f6c1c3ae89a4aa25d3de9be857999e9124f15dab486a", + "sha256:242b8feb3c493ab78be289c034a1f659e8826e2233786e36f2893a950a719bb6", + "sha256:254ec27fdb5b1ee60684f91683be95e5133c994cc54e86a0b0963afa25c8f8a6", + "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43", + "sha256:26ab812fa0c845df815e506be30337e2df27e88399b985d0bb4e3ecfe72df31c", + "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4", + "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e", + "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203", + "sha256:2aafc5a503855ea5885559eae883978c9b6d8c8993d67766ee73d82e841300dd", + "sha256:2d036c7187b9422ae5b262badb87a20a49eb6c5238b2004e96d4da1231badef1", + "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24", + "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc", + "sha256:38cf1c40a921d05c5edc61a785c0ddb4bed67827069f535d794ce6bcded919fc", + "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3", + "sha256:3c5ebac750d9d5f2706654c638c041635c385596caf68f81342011ddfa1e5598", + "sha256:3d482efec8b7dc6bfaedc0f166b2ce349df0011f5d2f1f25537ced4cfc34fd98", + "sha256:407653af5617f0757261ae249d3fba09504d7a71ab36ac057c938572d1bc9331", + "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2", + "sha256:41e81317dd6a0127cabce83c0c9c3fbecceae981c8391e6f1dec88a77c8a569a", + "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6", + "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688", + "sha256:4745f4ac52cc6686390c40eaa01d48b18997cb130833154801a442323cc78f91", + "sha256:4868f6bd7c9d98904b748a2653031fc9c2f85b6237009d475b1008bfaeb0a5aa", + "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b", + "sha256:4dd484681c15e6b9a977c785a345d3e378d72678fd5f1f3c0509608da24f2ac0", + "sha256:4f2790949cf385d985a31984907fecb3896999329103df4e4983a4a41e13e840", + "sha256:512ecfbefef6dac7bc5eaaf46177b2de58cdf7acac8793fe033b24ece0b9566c", + "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd", + "sha256:53e431da3fc53360db73eedf6f7124d1076e1b4ee4276b36fb25514544ceb4a3", + "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231", + "sha256:5b5ff4911aea936a47d9376fd3ab17e970cc543d1b68921886e7f64bd28308d1", + "sha256:5d41e6daee2813ecceea8eda38062d69e280b39df793f5a942fa515b8ed67953", + "sha256:5e999ba8dd90e93d57410c5e67ebb67ffcaadcea0ad973240fdfd3a135506250", + "sha256:5f239eb799a2081495ea659d8d4a43a8f42cd1fe9ff2e7e436295c38a10c286a", + "sha256:635fee4e041ab9c479e31edda27fcf966ea9614fff1317e280d99eb3e5ab6fe2", + "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20", + "sha256:6b507132dcfc0dea440cce23ee2182c0ce7aba7054576efc65634f080dbe9434", + "sha256:6b9d9bb600328a1ce523ab4f454859e9d439150abb0906c5a1983c146580ebab", + "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703", + "sha256:77bf3ac639c1ff567ae3b47f8d4cc3dc20f9966a2a6dd2311dcc055d3d04fb8a", + "sha256:784c1214cb6dd1e3b15dd8b91b9a53852aed16671cc3fbe4786f4f1db07089e2", + "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac", + "sha256:7ed1b0132f24beeec5a78b67d9388656d03e6a7c837394f99257e2d55b461611", + "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121", + "sha256:964faa8a861d2664f0c7ab0c181af0bea66098b1919439815ca8803ef136fc4e", + "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b", + "sha256:9ebfef07dbe1d93efb94b4700f2d278494e9162565a54f124c404a5656d7ff09", + "sha256:a45f84b09ac9c3d35dfcf6a27fd0634d30d183205230a0ebe8373a0e8cfa0906", + "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9", + "sha256:a6d511cc297ff0883bc3708b465ff82d7560193169a8b93260f74ecb0a5e08a7", + "sha256:a8ad4c766d3f33ba8fd692f9aa297c9058970530a32c728a2c4bfd2616d3358b", + "sha256:aa2f457b4af386254372dfa78a2eda2563680d982422641a85f271c859df1987", + "sha256:b03f7941783b4c4a26051846dea594628b38f6940a2fdc0df00b221aed39314c", + "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b", + "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e", + "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237", + "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1", + "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19", + "sha256:c6514f963b023aeee506678a1cf821fe31159b925c4b76fe2afa94cc70b3222b", + "sha256:c693e916709c2465b02ca0ad7b387c4f8423d1db7b4649c551f27a529181c5ad", + "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0", + "sha256:d02a72df14dfdbaf228424573a07af10637bd490f0901cee872c4f434a735b94", + "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312", + "sha256:d2b27e6af28f07e2f195552b37d7d66b150adbaa39a6d327766ffd695799780f", + "sha256:d2fe69c5434391727efa54b47a1e7986bb0186e72a41b203df8f5b0a19a4f669", + "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1", + "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe", + "sha256:e0bbdd76ce9aa5d4209d65f2b27fc6e5ef1312ae6c5333c26db3f5ade53a1e99", + "sha256:e7c4ea22b6739b162c9ecaaa41d718dfad48a244909fe7ef4b54c0b530effc5a", + "sha256:e93e1a4b4b33daed65d781a57a522ff153dcf748dee70b40c7258c5861e1768a", + "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52", + "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c", + "sha256:eaad4ff2de1c3823fddf82f41121bdf453d922e9a238642b1dedb33c4e4f98ad", + "sha256:f1f62b2413c3a0e846c3b838b2ecd6c7a19ec6793b2a522745b0869e37ab5bc1", + "sha256:f6d6cff3538391e8486a431569b77921adfcdef14eb18fbf19b7c0a5294d4e6a", + "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f", + "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a", + "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27" + ], + "markers": "python_version >= '3.8'", + "version": "==2.20.1" + }, "pygments": { "hashes": [ "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", @@ -413,6 +588,70 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' and python_version >= '3.4'", "version": "==1.16.0" }, + "sqlalchemy": { + "hashes": [ + "sha256:0b0f658414ee4e4b8cbcd4a9bb0fd743c5eeb81fc858ca517217a8013d282c96", + "sha256:2196208432deebdfe3b22185d46b08f00ac9d7b01284e168c212919891289396", + "sha256:23b9fbb2f5dd9e630db70fbe47d963c7779e9c81830869bd7d137c2dc1ad05fb", + "sha256:26a6a9837589c42b16693cf7bf836f5d42218f44d198f9343dd71d3164ceeeac", + "sha256:2a21c97efcbb9f255d5c12a96ae14da873233597dfd00a3a0c4ce5b3e5e79704", + "sha256:2e2c38c2a4c5c634fe6c3c58a789712719fa1bf9b9d6ff5ebfce9a9e5b89c1ca", + "sha256:2fc47dc6185a83c8100b37acda27658fe4dbd33b7d5e7324111f6521008ab4fe", + "sha256:2fd17e3bb8058359fa61248c52c7b09a97cf3c820e54207a50af529876451808", + "sha256:352b2770097f41bff6029b280c0e03b217c2dcaddc40726f8f53ed58d8a85da4", + "sha256:3b74570d99126992d4b0f91fb87c586a574a5872651185de8297c6f90055ae42", + "sha256:3cb8a66b167b033ec72c3812ffc8441d4e9f5f78f5e31e54dcd4c90a4ca5bebc", + "sha256:3f9faef422cfbb8fd53716cd14ba95e2ef655400235c3dfad1b5f467ba179c8c", + "sha256:4b600e9a212ed59355813becbcf282cfda5c93678e15c25a0ef896b354423238", + "sha256:501ff052229cb79dd4c49c402f6cb03b5a40ae4771efc8bb2bfac9f6c3d3508f", + "sha256:56d51ae825d20d604583f82c9527d285e9e6d14f9a5516463d9705dab20c3740", + "sha256:597fec37c382a5442ffd471f66ce12d07d91b281fd474289356b1a0041bdf31d", + "sha256:5a48ac4d359f058474fadc2115f78a5cdac9988d4f99eae44917f36aa1476327", + "sha256:5b6cf796d9fcc9b37011d3f9936189b3c8074a02a4ed0c0fbbc126772c31a6d4", + "sha256:66f63278db425838b3c2b1c596654b31939427016ba030e951b292e32b99553e", + "sha256:69f3e3c08867a8e4856e92d7afb618b95cdee18e0bc1647b77599722c9a28911", + "sha256:6e2622844551945db81c26a02f27d94145b561f9d4b0c39ce7bfd2fda5776dac", + "sha256:6f77c4f042ad493cb8595e2f503c7a4fe44cd7bd59c7582fd6d78d7e7b8ec52c", + "sha256:74afabeeff415e35525bf7a4ecdab015f00e06456166a2eba7590e49f8db940e", + "sha256:750900a471d39a7eeba57580b11983030517a1f512c2cb287d5ad0fcf3aebd58", + "sha256:78fe11dbe37d92667c2c6e74379f75746dc947ee505555a0197cfba9a6d4f1a4", + "sha256:79a40771363c5e9f3a77f0e28b3302801db08040928146e6808b5b7a40749c88", + "sha256:7bd112be780928c7f493c1a192cd8c5fc2a2a7b52b790bc5a84203fb4381c6be", + "sha256:8a41514c1a779e2aa9a19f67aaadeb5cbddf0b2b508843fcd7bafdf4c6864005", + "sha256:9f2bee229715b6366f86a95d497c347c22ddffa2c7c96143b59a2aa5cc9eebbc", + "sha256:9fea3d0884e82d1e33226935dac990b967bef21315cbcc894605db3441347443", + "sha256:afb6dde6c11ea4525318e279cd93c8734b795ac8bb5dda0eedd9ebaca7fa23f1", + "sha256:b607489dd4a54de56984a0c7656247504bd5523d9d0ba799aef59d4add009484", + "sha256:b6e22630e89f0e8c12332b2b4c282cb01cf4da0d26795b7eae16702a608e7ca1", + "sha256:b9c01990d9015df2c6f818aa8f4297d42ee71c9502026bb074e713d496e26b67", + "sha256:bd15026f77420eb2b324dcb93551ad9c5f22fab2c150c286ef1dc1160f110203", + "sha256:c06fb43a51ccdff3b4006aafee9fcf15f63f23c580675f7734245ceb6b6a9e05", + "sha256:c76c81c52e1e08f12f4b6a07af2b96b9b15ea67ccdd40ae17019f1c373faa227", + "sha256:ccaf1b0c90435b6e430f5dd30a5aede4764942a695552eb3a4ab74ed63c5b8d3", + "sha256:cd1591329333daf94467e699e11015d9c944f44c94d2091f4ac493ced0119449", + "sha256:cd5b94d4819c0c89280b7c6109c7b788a576084bf0a480ae17c227b0bc41e109", + "sha256:d337bf94052856d1b330d5fcad44582a30c532a2463776e1651bd3294ee7e58b", + "sha256:dc251477eae03c20fae8db9c1c23ea2ebc47331bcd73927cdcaecd02af98d3c3", + "sha256:dc6d69f8829712a4fd799d2ac8d79bdeff651c2301b081fd5d3fe697bd5b4ab9", + "sha256:f2a213c1b699d3f5768a7272de720387ae0122f1becf0901ed6eaa1abd1baf6c", + "sha256:f3ad7f221d8a69d32d197e5968d798217a4feebe30144986af71ada8c548e9fa", + "sha256:f43e93057cf52a227eda401251c72b6fbe4756f35fa6bfebb5d73b86881e59b0", + "sha256:f68470edd70c3ac3b6cd5c2a22a8daf18415203ca1b036aaeb9b0fb6f54e8298", + "sha256:fa4b1af3e619b5b0b435e333f3967612db06351217c58bfb50cee5f003db2a5a", + "sha256:fc6b14e8602f59c6ba893980bea96571dd0ed83d8ebb9c4479d9ed5425d562e9" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.31" + }, + "sqlmodel": { + "hashes": [ + "sha256:6c8125d4101970d031e9aae970b20cbeaf44149989f8366d939f4ab21aab8763", + "sha256:95449b0b48a40a3eecf0a629fa5735b9dfc8a5574a91090d24ca17f02246ad96" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==0.0.19" + }, "tqdm": { "hashes": [ "sha256:b75ca56b413b030bc3f00af51fd2c1a1a5eac6a0c1cca83cbb37a5c52abce644", diff --git a/plextraktsync/db/SyncDatabase.py b/plextraktsync/db/SyncDatabase.py new file mode 100644 index 0000000000..ccf35fa84f --- /dev/null +++ b/plextraktsync/db/SyncDatabase.py @@ -0,0 +1,52 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from sqlmodel import Session, select + +from plextraktsync.db.models.SyncRecord import SyncRecord + +if TYPE_CHECKING: + from sqlalchemy.future import Engine + + from plextraktsync.media import Media + + +class SyncDatabase: + def __init__(self, engine: Engine): + self.engine = engine + + def find_by_id(self, media_type: str, trakt_id: int): + with Session(self.engine) as session: + statement = ( + select(SyncRecord) + .where(SyncRecord.media_type == media_type) + .where(SyncRecord.trakt_id == trakt_id) + ) + return session.exec(statement).first() + + def insert(self, record: SyncRecord): + pass + + def update(self, m: Media): + record = self.find_by_id(m.type, m.trakt_id) + if not record: + record = SyncRecord( + media_type=m.type, + trakt_id=m.trakt_id, + # plex_timestamp_watched=m.watched_on_plex, + seen_on_plex_sync=m.watched_on_plex, + # trakt_timestamp_watched=m.watched_on_trakt, + seen_on_trakt_sync=m.watched_on_trakt, + ) + # if record.plex_timestamp_watched != m.watched_on_plex: + # record.plex_timestamp_watched = m.watched_on_plex + if record.seen_on_plex_sync != m.watched_on_plex: + record.seen_on_plex_sync = m.watched_on_plex + # if record.trakt_timestamp_watched != m.watched_on_trakt: + # record.trakt_timestamp_watched = m.watched_on_trakt + if record.seen_on_trakt_sync != m.watched_on_trakt: + record.seen_on_trakt_sync = m.watched_on_trakt + with Session(self.engine) as session: + session.add(record) + session.commit() diff --git a/plextraktsync/db/models/SyncRecord.py b/plextraktsync/db/models/SyncRecord.py new file mode 100644 index 0000000000..1e154dd1ba --- /dev/null +++ b/plextraktsync/db/models/SyncRecord.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from datetime import datetime +from typing import Optional + +from sqlmodel import Field, SQLModel, Column, DateTime, func, text + + +class SyncRecord(SQLModel, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + media_type: str + trakt_id: int + created_at: datetime = Field( + sa_column_kwargs={ + "server_default": text("CURRENT_TIMESTAMP"), + } + ) + updated_at: Optional[datetime] = Field( + sa_column=Column(DateTime(), onupdate=func.now()) + ) + plex_timestamp_watched: Optional[datetime] + seen_on_plex_sync: bool + trakt_timestamp_watched: Optional[datetime] + seen_on_trakt_sync: bool diff --git a/plextraktsync/db/models/__init__.py b/plextraktsync/db/models/__init__.py new file mode 100644 index 0000000000..1b183efbe4 --- /dev/null +++ b/plextraktsync/db/models/__init__.py @@ -0,0 +1 @@ +from .SyncRecord import SyncRecord # noqa: F401 diff --git a/plextraktsync/sync/Sync.py b/plextraktsync/sync/Sync.py index 9c36b0c58a..b47fa67fb4 100644 --- a/plextraktsync/sync/Sync.py +++ b/plextraktsync/sync/Sync.py @@ -11,16 +11,18 @@ from plextraktsync.plan.Walker import Walker from plextraktsync.plex.PlexApi import PlexApi from plextraktsync.trakt.TraktApi import TraktApi + from plextraktsync.db.SyncDatabase import SyncDatabase class Sync: logger = logging.getLogger(__name__) - def __init__(self, config: SyncConfig, plex: PlexApi, trakt: TraktApi): + def __init__(self, config: SyncConfig, plex: PlexApi, trakt: TraktApi, sync_state: SyncDatabase): self.config = config self.plex = plex self.trakt = trakt self.walker = None + self.sync_state = sync_state @cached_property def trakt_lists(self): @@ -47,9 +49,11 @@ async def sync(self, walker: Walker, dry_run=False): if self.config.need_library_walk: async for movie in walker.find_movies(): + self.sync_state.update(movie) await pm.ahook.walk_movie(movie=movie, dry_run=dry_run) async for episode in walker.find_episodes(): + self.sync_state.update(episode) await pm.ahook.walk_episode(episode=episode, dry_run=dry_run) await pm.ahook.fini(walker=walker, dry_run=dry_run) diff --git a/plextraktsync/util/Factory.py b/plextraktsync/util/Factory.py index e066fd9833..939cf448e9 100644 --- a/plextraktsync/util/Factory.py +++ b/plextraktsync/util/Factory.py @@ -140,7 +140,7 @@ def sync(self): plex = self.plex_api trakt = self.trakt_api - return Sync(self.sync_config, plex, trakt) + return Sync(self.sync_config, plex, trakt, self.sync_database) @cached_property def progressbar(self): @@ -213,6 +213,24 @@ def watch_state_updater(self): config=self.config, ) + @cached_property + def sync_engine(self): + from os.path import join + + from sqlmodel import SQLModel, create_engine + + from plextraktsync.path import cache_dir + + db_path = join(cache_dir, "sync.sqlite") + engine = create_engine(f"sqlite:///{db_path}", echo=False) + + # Import all models for metadata.create_all + import plextraktsync.db.models # noqa: F401 + + SQLModel.metadata.create_all(engine) + + return engine + @cached_property def logging(self): import logging @@ -300,6 +318,12 @@ def sync_config(self): return SyncConfig(self.config, self.server_config) + @cached_property + def sync_database(self): + from plextraktsync.db.SyncDatabase import SyncDatabase + + return SyncDatabase(self.sync_engine) + @cached_property def queue(self): from plextraktsync.queue.BackgroundTask import BackgroundTask diff --git a/requirements.txt b/requirements.txt index aa10370ccc..016ff756d1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ -i https://pypi.org/simple +annotated-types==0.7.0; python_version >= '3.8' apluggy==0.9.4 attrs==23.2.0; python_version >= '3.7' cattrs==23.2.3; python_version >= '3.7' and python_version >= '3.8' @@ -8,6 +9,7 @@ click==8.1.7 decorator==5.1.1 deprecated==1.2.14 exceptiongroup==1.2.1; python_version < '3.11' +greenlet==3.0.3; python_version < '3.13' and platform_machine == 'aarch64' or (platform_machine == 'ppc64le' or (platform_machine == 'x86_64' or (platform_machine == 'amd64' or (platform_machine == 'AMD64' or (platform_machine == 'win32' or platform_machine == 'WIN32'))))) humanize==4.10.0; python_version >= '3.8' idna==3.7; python_version >= '3.5' inquirerpy==0.3.4 @@ -19,6 +21,8 @@ platformdirs==4.2.2; python_version >= '3.7' and python_version >= '3.8' plexapi==4.15.15; python_version >= '3.8' pluggy==1.5.0 prompt-toolkit==3.0.47; python_full_version >= '3.7.0' +pydantic==2.8.2; python_version >= '3.8' +pydantic-core==2.20.1; python_version >= '3.8' pygments==2.18.0 python-dotenv==1.0.1 python-git-info==0.8.3 @@ -30,6 +34,8 @@ requests-cache==1.2.1; python_version >= '3.8' requests-oauthlib==2.0.0; python_version >= '3.4' rich==13.7.1 six==1.16.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' and python_version >= '3.4' +sqlalchemy==2.0.31; python_version >= '3.7' +sqlmodel==0.0.19; python_version >= '3.7' tqdm==4.66.4 types-decorator==5.1.8.20240310; python_version >= '3.8' typing-extensions==4.12.2; python_version >= '3.8'