mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-12-06 06:36:25 +00:00
Compare commits
681 Commits
feat/chrom
...
v4.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 25e4978583 | |||
| 457a3c6709 | |||
|
|
ddb9b2926d | ||
|
|
1e5ef4003a | ||
|
|
f246b6ce32 | ||
|
|
b246f43c70 | ||
|
|
fbf660d532 | ||
|
|
db3f03dc6c | ||
|
|
ba16d7873f | ||
|
|
ee33c71dfe | ||
|
|
1eadcb6521 | ||
|
|
b3a9c9dae8 | ||
|
|
c475691939 | ||
|
|
e6ac7e502c | ||
|
|
30581a4ee1 | ||
| a54311c53f | |||
| 0c9711baea | |||
| bffd71fea5 | |||
| 7e6e56a366 | |||
| baa78b9417 | |||
| b28bf5803d | |||
|
|
ee3eed6c23 | ||
| bf831dba45 | |||
| a43d54ee1d | |||
| f536713f04 | |||
| f438e80d3a | |||
| 38cb4c4f28 | |||
| 1178e8fd6c | |||
| e2414f94f3 | |||
| c87001d91e | |||
| 4cf9609153 | |||
| 83877d8dab | |||
| bab97fba5f | |||
| c289161400 | |||
| 7810f626c6 | |||
| 0294f538eb | |||
| ce07cece06 | |||
| 74013cf113 | |||
| 1eb7198e6f | |||
| c24c8914bf | |||
| e5dde472c9 | |||
| 7ccfab4d8b | |||
| 0bc6512bcc | |||
| e70174cb24 | |||
| fe155898bb | |||
| bd48032a50 | |||
| e124113d41 | |||
| 6974ef0163 | |||
| 015bfe983c | |||
| 275a892ee1 | |||
| 0ac4a1daa0 | |||
| 7bf3ba2443 | |||
| cfa12b0fed | |||
| 2f309440cc | |||
| db3d7f1f2e | |||
| d1febd13fd | |||
| 6fbd00a38f | |||
| 948c98f95b | |||
| 4139362677 | |||
| b9932383c6 | |||
| aa4ea2134a | |||
| 48f77c2f7a | |||
| 6567e78c8c | |||
| 32050bcdcd | |||
| 2aa10c9b1f | |||
| 4f9c06c7bd | |||
| b6bb190e69 | |||
| 4135fc5703 | |||
| c1ba51b903 | |||
| a218271001 | |||
| f4442fad0c | |||
| 87fdc1a444 | |||
| eed764c6e0 | |||
| f9a2185748 | |||
| 4f7c449ea7 | |||
| 7dab3fd094 | |||
| 49d225532c | |||
| b2c67e7df4 | |||
| 07259a7635 | |||
| d63c601812 | |||
| ecd1b55fc6 | |||
| 4c955e4115 | |||
| 14319a5c89 | |||
| 0ac388f3eb | |||
| fbf43fb10f | |||
| e0f41be887 | |||
| bd84989454 | |||
| e0b2e8e937 | |||
| f2ebc5659e | |||
| b72553aa11 | |||
| 0ed35e0354 | |||
| 29d846a944 | |||
| 9609fb150a | |||
| 9a5c4ab087 | |||
| f59f9a7ba0 | |||
| 8a925b35dd | |||
| 3e31c51f65 | |||
| 3c959ff532 | |||
| fa6deb7f82 | |||
| b7f5ede7cd | |||
| b2e0363594 | |||
| 73f4187087 | |||
| 1f63991ca0 | |||
| 8ea8d3ff57 | |||
| 070a94d87d | |||
| 22348e1554 | |||
| 0e2950ab69 | |||
| 2b0f6837a8 | |||
| ee4cc6706e | |||
| 948f8694f2 | |||
| 29314d473f | |||
| 5f177e9338 | |||
| d7dd2bd138 | |||
| ba37786038 | |||
| 179b79c926 | |||
| 411054afe9 | |||
| 067eafbbe4 | |||
| 253e256458 | |||
| 238fdf5d40 | |||
| 13ddeaaf0a | |||
| 07afbdaa4b | |||
| 0ff74d331f | |||
| 3eff6228be | |||
| edc6d11824 | |||
| e9aaa184cf | |||
| e8351e960d | |||
| 0ff03fb413 | |||
| ba83edd26c | |||
| eed058c891 | |||
| 48f82a6f13 | |||
| 177391a74c | |||
| 9ea177e2f6 | |||
| ca6a4d8ab5 | |||
| c5a2a05af6 | |||
| 0034f93caa | |||
| d01ef02389 | |||
| e8f2a72516 | |||
| bc66d35497 | |||
| e1d0d2c186 | |||
| e7c878b30a | |||
| 8fb46099d4 | |||
| 72628519a5 | |||
| f7375428b6 | |||
| ead8a44fe0 | |||
| a1aa71e271 | |||
| a72691a81f | |||
| 3be409ec70 | |||
| 9409197766 | |||
| 1a92094eaf | |||
| fa47ccfd85 | |||
| 6b4468bf7d | |||
| b0927b8388 | |||
| 593dc3ca55 | |||
| a124a1a71f | |||
| 0e71242fd3 | |||
| 556b2a2317 | |||
| 4bfd7c348d | |||
| 972007f9e5 | |||
| 9bfd1a78bd | |||
| 05d8332358 | |||
| dd62611588 | |||
| f0d6402529 | |||
| d73a37fbed | |||
| 9084a78420 | |||
| 1769aa45c9 | |||
| 3386263fbe | |||
| 0eb7a58494 | |||
| b995b86cee | |||
| a825912ad3 | |||
| e788dea8a4 | |||
| 7622420f06 | |||
| 66fff153e1 | |||
| a33171ba87 | |||
| 4bb7b4b6c4 | |||
| f8e887c1f2 | |||
| 47dc244a15 | |||
| f872deffb8 | |||
| 5489f601d2 | |||
| d77813d82f | |||
| b0f9d7906a | |||
| eec08d4a3e | |||
| 320c45096b | |||
| 68a83c31be | |||
| 88eb325079 | |||
| b2f4933a5f | |||
| e12a1d369d | |||
| c4f1e420f8 | |||
| 15a4280a36 | |||
| 3e39ef1705 | |||
| 0dd3a6190e | |||
| f03fbd42df | |||
| dfc86e4b96 | |||
| 167e2853f0 | |||
| 1698de332c | |||
| 377d85c7f1 | |||
| 68a3af0b52 | |||
| 5b7bfa79f9 | |||
| d7dee62e97 | |||
| 4368f0cbe5 | |||
| 12f35fefc4 | |||
| 49a1dad51e | |||
| 40c0403962 | |||
| 068e153ae9 | |||
| 42869cb28f | |||
| 56a96540b4 | |||
| 5a6bb57fd5 | |||
| 44521d0d5f | |||
| 43a4a0e6ee | |||
| 1373d0ce26 | |||
| c9c1ac5126 | |||
| d136b98411 | |||
| 7e0e359f46 | |||
| 86427cf6ef | |||
| c7db07f7ba | |||
| d3fbec1a9d | |||
| c621c45695 | |||
| bb456738f0 | |||
| 0112acdd2f | |||
| ae6121f3aa | |||
| 9b606cf9ac | |||
| 234d3c9c7c | |||
| ce4663d512 | |||
| 97de98b89a | |||
| 76d0c53cc1 | |||
| 8b102b083f | |||
| e13f9c6aa8 | |||
| e8b929d4ca | |||
| 034f048883 | |||
| aceb6ee14c | |||
| 7698a1490e | |||
| 59a25fbee9 | |||
| fc598838c4 | |||
| 11712b5b13 | |||
| d2799adad0 | |||
| 872639dab9 | |||
| e4562648ba | |||
| 941137ff51 | |||
| eaec736676 | |||
| 9ba7cb1ba5 | |||
| 2ce8e7f866 | |||
| 5a618a8db2 | |||
| d21e4ffba2 | |||
| 28855046b8 | |||
| eb351f9bed | |||
| 0e0bb17ad9 | |||
| e95f001dd2 | |||
| 0d4b9fe488 | |||
| 4be4fa2c4f | |||
| 8d4da63855 | |||
| 3a9941f69f | |||
| 01486dfbec | |||
| fb2280798a | |||
| 29f874e390 | |||
| dc6bdf7715 | |||
| 6022f5b9d2 | |||
| 30b6d4791f | |||
| e5c185627d | |||
| 573852d852 | |||
| 385c580358 | |||
| ec2b0e0f78 | |||
| c3816b709c | |||
| 4b8528af65 | |||
| 69ba636d74 | |||
| 1b035035ce | |||
| 4dc0d6a97c | |||
| 34e4036f66 | |||
| 1dd3e37a37 | |||
| e79e568a4c | |||
| 1641ba2898 | |||
| 31d9507033 | |||
| 67deef897f | |||
| 3b84161ec5 | |||
| a04bffbec9 | |||
| 7ad1383acb | |||
| 4217d471c4 | |||
| 5f6e96333c | |||
| 7962d3a16f | |||
| de6d831498 | |||
| be076616cc | |||
| 0a38fdd8d3 | |||
| 2fa5587fbf | |||
| 4bfffa579f | |||
| 010fab93d7 | |||
| 6f3eefb611 | |||
| 328b378978 | |||
| 1f518c2d33 | |||
| a1a5e665fb | |||
| 70afa7cc9b | |||
| 5f1ea76cad | |||
| 462aa44473 | |||
| dc0f0df92f | |||
| 0ec22766ba | |||
| 3549f8b2d2 | |||
| 8a3a2fecf5 | |||
| 5c8c7b8804 | |||
| e88d4c2ca7 | |||
| 83a91eea97 | |||
| 13ef0ba14a | |||
| 938ccd9215 | |||
| d394c390f7 | |||
| 4c705a4605 | |||
| 105aa7874f | |||
| 27ab3b0b21 | |||
| 03af604e94 | |||
| 05b8f5b400 | |||
| e8654ca181 | |||
| 68b4e71281 | |||
| b89617d125 | |||
| 70466aba7e | |||
| 5ddfe1ddb2 | |||
| 25418071fe | |||
| a9d4fb769d | |||
| 1faf234255 | |||
| c06afcd56d | |||
| 607b973dbd | |||
| 22e136d9fd | |||
| 7b2f44d19a | |||
| 74c31a0f18 | |||
| fd3c2e5f9b | |||
| be3724c6b1 | |||
| b8e6c21538 | |||
| 60afaf6211 | |||
| 090f305f02 | |||
| 0a30e5022b | |||
| 6733fe9c40 | |||
| 76ce4b8058 | |||
| 36f4bbc7e7 | |||
| 30d52c6061 | |||
| 7018915686 | |||
| a1fb4ce8eb | |||
| 5446dbce83 | |||
| 93b36f1bd4 | |||
| ab12de8287 | |||
| 3cdfc91c93 | |||
| a6c8105d8c | |||
| ca99421624 | |||
| 5c270a0362 | |||
| 19ae15f53f | |||
| 386c6bf268 | |||
| e075306363 | |||
| 9591350c3e | |||
| 45b18eb8e6 | |||
| 0f59798e73 | |||
| 7b56924466 | |||
| 3d30d65184 | |||
| 9bb896e84b | |||
| e0ee364929 | |||
| 8e9cd2d2f3 | |||
| f58597379b | |||
| e6f263b6de | |||
| c69864d0f5 | |||
| ae901b257b | |||
| fdc537d69a | |||
| f12c1053ca | |||
| 6435114384 | |||
| c55c3bca72 | |||
| 83b8627717 | |||
| 95ccceb259 | |||
| 31d8f5f7b9 | |||
|
|
f1660bbc74 | ||
|
|
5d72fe44ab | ||
| 714b7d845a | |||
| 066229eb0e | |||
| 0f96f02df5 | |||
| b753fdd2b0 | |||
| f9df0b8a12 | |||
| db41c55230 | |||
| 58b799edb4 | |||
| 94d3b8676f | |||
| de7aa58195 | |||
| 5d4e251251 | |||
| 45baa804e6 | |||
| 2c49848dd7 | |||
| be499b3085 | |||
| 4fde5fb65f | |||
| 4c84f857e7 | |||
| 1e55b7bf50 | |||
| 479d3e9f07 | |||
| dcf14c6e6f | |||
| 96d5ca0f3c | |||
| 32fa639d4a | |||
| 69760dd5f8 | |||
| 1237a9157c | |||
| fee59833f2 | |||
| 12f685010b | |||
| 6ac8af9946 | |||
| e8d4c128da | |||
| d2a5d4b3ec | |||
| 7797a7bf53 | |||
| e5430a7aed | |||
| 88cd4a3ec2 | |||
| 8650c2d4c8 | |||
| 1734e57d43 | |||
| 28d0fc161b | |||
| 8ebc767b76 | |||
| bbe8a19189 | |||
| 4bffd359b9 | |||
| 08bbfd91fe | |||
| 1e3cd64ec9 | |||
| 663de0e720 | |||
| 7cffd75749 | |||
| 327ea4912a | |||
| efa743a714 | |||
| cd44e8e4c3 | |||
| e185625f4a | |||
| 3e140e4f43 | |||
| 47a22b6540 | |||
| 72e74cea32 | |||
| 4993ae50fe | |||
| eeb111dd9e | |||
| 4bb992fc67 | |||
| 58040f8f49 | |||
| 4f2cefb1be | |||
| 2b33191db7 | |||
| 0969d68adc | |||
| 86533153bf | |||
| 6349763abc | |||
| 67693d6c31 | |||
| 31a757a5cd | |||
| 2c59dddca0 | |||
| 206466f9cc | |||
| 039c644453 | |||
| 5d377654aa | |||
| ff163026c7 | |||
| 1d431ecad6 | |||
| 6a4c2c6aea | |||
| 3f928ad507 | |||
| 735edf1529 | |||
| f49882fb0d | |||
| 6f4936f6be | |||
| e8eb36284b | |||
| cec8400145 | |||
| 22d8ea8215 | |||
| e6a6131a14 | |||
| 92b5e33940 | |||
| 63f7a75394 | |||
| dbe85322ea | |||
| 64d4ee9168 | |||
| f7f40be956 | |||
| f42eaeb953 | |||
| 954909fecd | |||
| 95133deeb0 | |||
| 8ba80e93e3 | |||
| 47c7617d24 | |||
| a5fc5b3753 | |||
| 0b2d8a7e2e | |||
| 5ee0a0044a | |||
| 6e39690d7a | |||
| ca4e9e0052 | |||
| f76ce5dae1 | |||
| 4e4a90b2d2 | |||
| 98f6fda99f | |||
| 9a125e0359 | |||
| 3778b2148c | |||
| 3ab76006af | |||
| c96db3a3dc | |||
| ea52ce1c65 | |||
| f80289aef1 | |||
| 5cdcff0cd0 | |||
| 4c523d56f5 | |||
| 67e4764a72 | |||
| e7ace4d497 | |||
| c6edf4e2cb | |||
| 2939ea0787 | |||
| 64adc63920 | |||
| 33d212bd84 | |||
| 5543bc4c9d | |||
| d106988fd7 | |||
| bc6fdec360 | |||
| a7d8863998 | |||
| a3b2b80c1e | |||
| 0b614ec4b6 | |||
| 0e2c029249 | |||
| deade0010b | |||
| e94fae1d85 | |||
| 259ca2e14e | |||
| e9f9ae5154 | |||
| 3b98263fbc | |||
| ca1d2dd16f | |||
| 9e254e6813 | |||
| 9e250e6129 | |||
| 8f22785d2f | |||
| bb716ab6b6 | |||
| 5acd231292 | |||
| 7ebe0adace | |||
| 9e3337e9c5 | |||
| 298dc9af27 | |||
| 840b0c07ff | |||
| 592c92785f | |||
| d0db4815f1 | |||
| 8cf105a550 | |||
| 9c6055a52e | |||
| a554b7681f | |||
| cbcc2f30b7 | |||
| 565125da67 | |||
| ec8782ad81 | |||
| 7388719cad | |||
| 75fb4b5809 | |||
| 408873b844 | |||
| 31a349704b | |||
| c3b8595cd7 | |||
| 2334afb3eb | |||
| ce9dc48d81 | |||
| 20f7f87072 | |||
| a9ce596381 | |||
| 8ac5f02d93 | |||
| 10b291b4aa | |||
| c894f33c11 | |||
| 1167cfa3a4 | |||
| 541a7c9e2b | |||
| 8b2dd048d3 | |||
| d4d223dbf0 | |||
| 6b9842c9d3 | |||
| e2387158dc | |||
| 89fbb3a3cf | |||
| 55da64a702 | |||
| edff93917e | |||
| 424390417a | |||
| 7427de1bb4 | |||
| 42adf023d9 | |||
| 02214a127c | |||
| 9e98bf3532 | |||
| 86955cf0cb | |||
| bf281820b9 | |||
| 4d0dd674ad | |||
| 973f9cfdc2 | |||
| 13d79c5338 | |||
| dca91feff8 | |||
| 505ebb48bc | |||
| eaa4060f2a | |||
| 5e87a669de | |||
| 9ce8043667 | |||
| edf2941e01 | |||
| da0c1087e9 | |||
| a4f9d5b461 | |||
| ebb3b962a8 | |||
| 38040ba1d3 | |||
| e17b4c6e23 | |||
| 03bd5b4c78 | |||
| ebe2da0309 | |||
| 201892ea7f | |||
| e12ac9b3a4 | |||
| 2b86a469a9 | |||
| dae11994c2 | |||
| 3e53b28a10 | |||
| 4c4e22a0e0 | |||
| b5f8799d0a | |||
| 875c6eaa5e | |||
| a56af56009 | |||
| eee51216b9 | |||
| 73578736bc | |||
| 33db01cbbf | |||
| a716fd1e22 | |||
| 19c3f44d9a | |||
| ae095b3e2c | |||
| eb92eed532 | |||
| 2652e558d2 | |||
| 262a26e3df | |||
|
|
4af4119561 | ||
|
|
04eae360a7 | ||
|
|
a6c645c33f | ||
| 4b7f59e2e9 | |||
| da17fc3e6d | |||
| b62af8d2ae | |||
| ef3e4dc39b | |||
| fbe624ca6d | |||
| 67112a37da | |||
| c0c263c4d7 | |||
| e32c09f48f | |||
| 91aa673dcb | |||
| 95257771a5 | |||
| fa6bb695f3 | |||
| 9f5b5282b1 | |||
| ea53eb9b24 | |||
| 98a0466761 | |||
| e3be74d519 | |||
| fdc6a88317 | |||
| d67bd62393 | |||
|
|
ea986917af | ||
|
|
ffbf4be19e | ||
|
|
2b87aacc58 | ||
|
|
b62b272492 | ||
|
|
e5027cf00d | ||
|
|
e7b4c01f93 | ||
|
|
304951e463 | ||
|
|
e9622edee9 | ||
|
|
80be960f29 | ||
|
|
d2d736a7ec | ||
|
|
341d799207 | ||
|
|
91289c8588 | ||
|
|
bc878a2e90 | ||
|
|
bb4e7b7712 | ||
|
|
dad0a7d30a | ||
|
|
b0ea38b047 | ||
|
|
9c35801196 | ||
|
|
f5338fc8a8 | ||
|
|
aa38b34191 | ||
|
|
01eae3f680 | ||
|
|
6a098b6317 | ||
|
|
e242867ddb | ||
|
|
e11af205f8 | ||
|
|
9dc9431170 | ||
|
|
1e182e0d75 | ||
|
|
a501c8e571 | ||
|
|
95eb703788 | ||
|
|
942f4f1c75 | ||
|
|
8ca120aa6f | ||
|
|
1f33d52429 | ||
|
|
257e78dcaa | ||
|
|
b88cd583d3 | ||
|
|
67da1563be | ||
|
|
4b92b8a38e | ||
|
|
39ae631cf1 | ||
|
|
84b4c998a7 | ||
|
|
e741b5aa6d | ||
|
|
162891ba1d | ||
|
|
1139a726c9 | ||
|
|
b0eb8c3b42 | ||
|
|
7315d5f5a1 | ||
|
|
7a1bde1b73 | ||
|
|
b1b8772717 | ||
|
|
856eaffda6 | ||
|
|
2c16fdad19 | ||
|
|
c79a991024 | ||
|
|
7b8d916685 | ||
|
|
3c447f5708 | ||
|
|
4f5023f745 | ||
|
|
2c5d37083b | ||
|
|
4f6024a473 | ||
|
|
2ac4c434f5 | ||
|
|
eabf5e1faf | ||
|
|
1ee955fbfe | ||
|
|
de06c7f81f | ||
|
|
4347df0b9b | ||
|
|
b951ef5ce4 | ||
|
|
a12f78761d | ||
|
|
894cbb3c9d | ||
|
|
26f9cf646b | ||
|
|
8c28df9517 | ||
|
|
1b76bbf6c2 | ||
|
|
e5b236f51c | ||
|
|
a213c39445 | ||
|
|
be6551888e | ||
|
|
3b29e1a87a | ||
|
|
bb63340555 | ||
|
|
24dddc3075 | ||
|
|
47ca25fe1c | ||
|
|
1cd418991c | ||
|
|
d15c3ed047 | ||
|
|
26e5ce6852 | ||
|
|
f834e08273 | ||
|
|
26e40adb21 | ||
|
|
43ed65bc76 | ||
|
|
1f049952cc | ||
|
|
869c1fbe51 | ||
|
|
5b78146db9 | ||
|
|
fff73ed2b8 | ||
|
|
71a506443f | ||
|
|
2e50be6254 | ||
|
|
ceaafc495d | ||
|
|
67de27578e | ||
|
|
e82e515a23 | ||
|
|
20c740ce07 | ||
|
|
9a9538d14d | ||
|
|
ca50b53bae | ||
|
|
d420c7d0a9 | ||
|
|
4195371efb | ||
|
|
27e309f04d | ||
|
|
b20a4fa149 | ||
|
|
2c4bcb9c39 | ||
|
|
07b41716e8 | ||
|
|
e92e7758e7 | ||
|
|
c64c34a9fb | ||
|
|
a221e6358e | ||
|
|
f40122d5b2 | ||
|
|
a065205ea0 | ||
|
|
856f42ce27 | ||
|
|
016d50e383 | ||
|
|
1507b279f7 | ||
|
|
000f7cbfe1 | ||
|
|
cbbd04efb6 |
@@ -9,6 +9,6 @@ indent_style = tab
|
||||
indent_size = tab
|
||||
smart_tab = true
|
||||
|
||||
[{*.yaml,*.yml}]
|
||||
[{*.yaml,*.yml,*.nix}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
44
.env.example
44
.env.example
@@ -1,6 +1,40 @@
|
||||
TVDB__APIKEY=
|
||||
THEMOVIEDB__APIKEY=
|
||||
AUTHENTICATION_SECRET=
|
||||
POSTGRES_USER=kyoousername
|
||||
POSTGRES_PASSWORD=kyoopassword
|
||||
# Useful config options
|
||||
LIBRARY_ROOT=./video
|
||||
CACHE_ROOT=/tmp/kyoo_cache
|
||||
LIBRARY_LANGUAGES=en
|
||||
|
||||
# A pattern (regex) to ignore video files.
|
||||
LIBRARY_IGNORE_PATTERN=.*/[dD]ownloads?/.*
|
||||
|
||||
# The following two values should be set to a random sequence of characters.
|
||||
# You MUST change thoses when installing kyoo (for security)
|
||||
AUTHENTICATION_SECRET=4c@mraGB!KRfF@kpS8739y9FcHemKxBsqqxLbdR?
|
||||
# You can input multiple api keys separated by a ,
|
||||
KYOO_APIKEYS=t7H5!@4iMNsAaSJQ49pat4jprJgTcF656if#J3
|
||||
|
||||
DEFAULT_PERMISSIONS=overall.read
|
||||
UNLOGGED_PERMISSIONS=overall.read
|
||||
|
||||
THEMOVIEDB_APIKEY=
|
||||
PUBLIC_BACK_URL=http://localhost:5000
|
||||
|
||||
|
||||
|
||||
# Following options are optional and only useful for debugging.
|
||||
|
||||
# To debug the front end, you can set the following to an external backend
|
||||
KYOO_URL=
|
||||
# The library root inside the container.
|
||||
KYOO_LIBRARY_ROOT=/video
|
||||
|
||||
# Database things
|
||||
POSTGRES_USER=KyooUser
|
||||
POSTGRES_PASSWORD=KyooPassword
|
||||
POSTGRES_DB=kyooDB
|
||||
POSTGRES_SERVER=postgres
|
||||
POSTGRES_PORT=5432
|
||||
|
||||
MEILI_HOST="http://meilisearch:7700"
|
||||
MEILI_MASTER_KEY="ghvjkgisbgkbgskegblfqbgjkebbhgwkjfb"
|
||||
|
||||
# vi: ft=sh
|
||||
|
||||
1
.git-blame-ignore-revs
Normal file
1
.git-blame-ignore-revs
Normal file
@@ -0,0 +1 @@
|
||||
7e6e56a366babe17e7891a5897180efbf93c00c5
|
||||
89
.github/workflows/analysis.yml
vendored
89
.github/workflows/analysis.yml
vendored
@@ -1,89 +0,0 @@
|
||||
name: Analysis
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- next
|
||||
pull_request:
|
||||
|
||||
|
||||
jobs:
|
||||
analysis:
|
||||
name: Static Analysis
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
||||
|
||||
- name: Cache SonarCloud packages
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/sonar/cache
|
||||
key: ${{ runner.os }}-sonar
|
||||
restore-keys: ${{ runner.os }}-sonar
|
||||
|
||||
- name: Cache SonarCloud scanner
|
||||
id: cache-sonar-scanner
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.sonar/scanner
|
||||
key: ${{ runner.os }}-sonar-scanner
|
||||
restore-keys: ${{ runner.os }}-sonar-scanner
|
||||
|
||||
- name: Install SonarCloud scanner
|
||||
if: steps.cache-sonar-scanner.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
cd back
|
||||
mkdir -p ~/.sonar/scanner
|
||||
dotnet tool update dotnet-sonarscanner --tool-path ~/.sonar/scanner
|
||||
|
||||
- name: Wait for tests to run (Push)
|
||||
uses: lewagon/wait-on-check-action@master
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
ref: ${{github.ref}}
|
||||
check-name: tests
|
||||
repo-token: ${{secrets.GITHUB_TOKEN}}
|
||||
running-workflow-name: analysis
|
||||
allowed-conclusions: success,skipped,cancelled,neutral,failure
|
||||
- name: Wait for tests to run (PR)
|
||||
uses: lewagon/wait-on-check-action@master
|
||||
if: github.event_name == 'pull_request'
|
||||
with:
|
||||
ref: ${{github.event.pull_request.head.sha}}
|
||||
check-name: tests
|
||||
repo-token: ${{secrets.GITHUB_TOKEN}}
|
||||
running-workflow-name: analysis
|
||||
allowed-conclusions: success,skipped,cancelled,neutral,failure
|
||||
|
||||
- name: Download coverage report
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
with:
|
||||
commit: ${{env.COMMIT_SHA}}
|
||||
workflow: tests.yml
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
|
||||
- name: Build and analyze
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
shell: bash
|
||||
run: |
|
||||
cp -r results.xml/ coverage.xml/ back/
|
||||
cd back
|
||||
find . -name 'coverage.opencover.xml'
|
||||
dotnet build-server shutdown
|
||||
|
||||
~/.sonar/scanner/dotnet-sonarscanner begin \
|
||||
-k:"AnonymusRaccoon_Kyoo" \
|
||||
-o:"anonymus-raccoon" \
|
||||
-d:sonar.login="${{ secrets.SONAR_TOKEN }}" \
|
||||
-d:sonar.host.url="https://sonarcloud.io" \
|
||||
-d:sonar.cs.opencover.reportsPaths="**/coverage.opencover.xml" \
|
||||
-d:sonar.cs.vstest.reportsPaths="**/TestOutputResults.xml"
|
||||
|
||||
dotnet build --no-incremental '-p:SkipTranscoder=true'
|
||||
|
||||
~/.sonar/scanner/dotnet-sonarscanner end -d:sonar.login="${{ secrets.SONAR_TOKEN }}"
|
||||
73
.github/workflows/coding-style.yml
vendored
73
.github/workflows/coding-style.yml
vendored
@@ -1,15 +1,72 @@
|
||||
name: CodingStyle
|
||||
name: Coding Style
|
||||
on: [pull_request, workflow_dispatch]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: "Coding style check"
|
||||
back:
|
||||
name: "Lint Back"
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./back
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Check coding style
|
||||
run: |
|
||||
dotnet tool restore
|
||||
dotnet csharpier . --check
|
||||
|
||||
front:
|
||||
name: "Lint Front"
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./front
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v1
|
||||
|
||||
- name: Find yarn cache
|
||||
id: yarn-cache-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
- name: Restore cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
dotnet-version: 6.0.x
|
||||
- name: Build the app
|
||||
run: cd back && dotnet build -p:CheckCodingStyle=true -p:TreatWarningsAsErrors=true '-p:SkipTranscoder=true'
|
||||
path: ${{ steps.yarn-cache-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: ${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
|
||||
- name: Lint
|
||||
run: yarn lint && yarn format
|
||||
|
||||
scanner:
|
||||
name: "Lint scanner"
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./scanner
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
|
||||
- name: Run black
|
||||
run: |
|
||||
pip install black-with-tabs
|
||||
black . --check
|
||||
|
||||
transcoder:
|
||||
name: "Lint transcoder"
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./transcoder
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Run cargo fmt
|
||||
run: |
|
||||
cargo fmt --check
|
||||
|
||||
32
.github/workflows/docker.yml
vendored
32
.github/workflows/docker.yml
vendored
@@ -4,6 +4,8 @@ on:
|
||||
branches:
|
||||
- master
|
||||
- next
|
||||
tags:
|
||||
- v*
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
@@ -15,11 +17,17 @@ jobs:
|
||||
include:
|
||||
- context: ./back
|
||||
label: back
|
||||
image: ghcr.io/${{github.repository_owner}}/kyoo_back
|
||||
image: zoriya/kyoo_back
|
||||
- context: ./front
|
||||
label: front
|
||||
image: ghcr.io/${{github.repository_owner}}/kyoo_front
|
||||
name: Docker build ${{matrix.label}}
|
||||
image: zoriya/kyoo_front
|
||||
- context: ./scanner
|
||||
label: scanner
|
||||
image: zoriya/kyoo_scanner
|
||||
- context: ./transcoder
|
||||
label: transcoder
|
||||
image: zoriya/kyoo_transcoder
|
||||
name: Build ${{matrix.label}}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
@@ -48,18 +56,28 @@ jobs:
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
if: ${{env.SHOULD_PUSH}}
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{github.repository_owner}}
|
||||
password: ${{secrets.GITHUB_TOKEN}}
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Build and push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: ${{matrix.context}}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
build-args: |
|
||||
VERSION=0.0.0
|
||||
push: ${{env.SHOULD_PUSH}}
|
||||
tags: ${{steps.meta.outputs.tags}}
|
||||
|
||||
- name: Sync README.MD
|
||||
if: ${{env.SHOULD_PUSH}}
|
||||
uses: ms-jpq/sync-dockerhub-readme@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
repository: ${{ matrix.image }}
|
||||
readme: "./README.md"
|
||||
|
||||
32
.github/workflows/documentation.yml
vendored
32
.github/workflows/documentation.yml
vendored
@@ -1,32 +0,0 @@
|
||||
name: Update the documentation
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
Building:
|
||||
runs-on: [ubuntu-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: nikeee/docfx-action@v1.0.0
|
||||
name: Build Documentation
|
||||
with:
|
||||
args: docs/docfx.json
|
||||
- name: Update the docs
|
||||
run: |
|
||||
cd docs/_site
|
||||
sudo chown $(whoami):$(whoami) . -R
|
||||
echo -n docs.kyoo.moe > CNAME
|
||||
git config --global user.email "${GITHUB_ACTOR}@github.com"
|
||||
git config --global user.name "${GITHUB_ACTOR}"
|
||||
git init
|
||||
git add -A
|
||||
git commit -m "Deploying the documentation"
|
||||
git remote add origin https://${GITHUB_ACTOR}:${GITHUB_TOKEN}@${GITHUB_REPO}
|
||||
git checkout -b gh-pages
|
||||
git push --force origin gh-pages
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_REPO: "github.com/${{github.repository_owner}}/Kyoo"
|
||||
69
.github/workflows/native-build.yml
vendored
Normal file
69
.github/workflows/native-build.yml
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
name: Native build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- next
|
||||
tags:
|
||||
- v*
|
||||
|
||||
jobs:
|
||||
update:
|
||||
name: Expo Build
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./front
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Check for EXPO_TOKEN
|
||||
run: |
|
||||
if [ -z "${{ secrets.EXPO_TOKEN }}" ]; then
|
||||
echo "You must provide an EXPO_TOKEN secret linked to this project's Expo account in this repo's secrets. Learn more: https://docs.expo.dev/eas-update/github-actions"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16.x
|
||||
cache: yarn
|
||||
cache-dependency-path: front/yarn.lock
|
||||
|
||||
- name: Setup Expo
|
||||
uses: expo/expo-github-action@v7
|
||||
with:
|
||||
expo-version: latest
|
||||
eas-version: latest
|
||||
token: ${{ secrets.EXPO_TOKEN }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
|
||||
- name: Build Mobile Release
|
||||
run: yarn build:mobile | tee log.txt
|
||||
|
||||
- name: Parse Asset URL
|
||||
id: url
|
||||
run: |
|
||||
ASSET_URL=$(grep -oe 'https://expo.dev/artifacts/eas/.*' log.txt)
|
||||
echo The android url is $ASSET_URL
|
||||
echo "assetUrl=$ASSET_URL" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Download APK Asset
|
||||
run: wget -O kyoo.apk ${{ steps.url.outputs.assetUrl }}
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: kyoo.apk
|
||||
path: ./front/kyoo.apk
|
||||
|
||||
- name: Upload release artifacts
|
||||
uses: Roang-zero1/github-upload-release-artifacts-action@v2
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
args: ./front/kyoo.apk
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
43
.github/workflows/native-update.yml
vendored
Normal file
43
.github/workflows/native-update.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
name: Native update
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
|
||||
jobs:
|
||||
update:
|
||||
name: Expo Update
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./front
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Check for EXPO_TOKEN
|
||||
run: |
|
||||
if [ -z "${{ secrets.EXPO_TOKEN }}" ]; then
|
||||
echo "You must provide an EXPO_TOKEN secret linked to this project's Expo account in this repo's secrets. Learn more: https://docs.expo.dev/eas-update/github-actions"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16.x
|
||||
cache: yarn
|
||||
cache-dependency-path: front/yarn.lock
|
||||
|
||||
- name: Setup Expo
|
||||
uses: expo/expo-github-action@v7
|
||||
with:
|
||||
expo-version: latest
|
||||
eas-version: latest
|
||||
token: ${{ secrets.EXPO_TOKEN }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
|
||||
- name: Publish update
|
||||
run: yarn update
|
||||
36
.github/workflows/release.yml
vendored
36
.github/workflows/release.yml
vendored
@@ -1,36 +0,0 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set commit environment
|
||||
run: echo "COMMIT_SHA=$(git rev-list -n 1 ${{github.ref}})" >> $GITHUB_ENV
|
||||
- name: Wait for builds & tests to finish
|
||||
uses: lewagon/wait-on-check-action@master
|
||||
with:
|
||||
ref: ${{github.ref}}
|
||||
repo-token: ${{secrets.GITHUB_TOKEN}}
|
||||
running-workflow-name: release
|
||||
allowed-conclusions: success,skipped,cancelled,neutral
|
||||
# - name: Public the abstractions to nuget
|
||||
# id: publish_nuget
|
||||
# uses: rohith/publish-nuget@v2
|
||||
# with:
|
||||
# PROJECT_FILE_PATH: Kyoo.Abstractions/Kyoo.Abstractions.csproj
|
||||
# PACKAGE_NAME: Kyoo.Abstractions
|
||||
# VERSION_REGEX: ^\s*<PackageVersion>(.*)<\/PackageVersion>\s*$
|
||||
# NUGET_KEY: ${{secrets.NUGET_API_KEY}}
|
||||
# INCLUDE_SYMBOLS: true
|
||||
- name: Create Release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
generateReleaseNotes: true
|
||||
token: ${{secrets.GITHUB_TOKEN}}
|
||||
21
.github/workflows/robot.yml
vendored
21
.github/workflows/robot.yml
vendored
@@ -8,7 +8,8 @@ on:
|
||||
|
||||
|
||||
jobs:
|
||||
build:
|
||||
test:
|
||||
name: Run Robot Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -16,24 +17,24 @@ jobs:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: Pull images
|
||||
run: docker-compose pull
|
||||
run: |
|
||||
cp .env.example .env
|
||||
docker compose version
|
||||
docker compose pull
|
||||
|
||||
- name: Docker cache
|
||||
uses: satackey/action-docker-layer-caching@v0.0.11
|
||||
continue-on-error: true
|
||||
|
||||
- name: Build the app
|
||||
run: docker-compose build
|
||||
|
||||
- name: Start the service
|
||||
run: |
|
||||
cp .env.example .env
|
||||
docker-compose up -d
|
||||
docker compose up -d back postgres ingress meilisearch # --wait Wait is not available on gha
|
||||
|
||||
- name: Perform healthchecks
|
||||
run: |
|
||||
docker-compose ps -a
|
||||
wget --retry-connrefused --retry-on-http-error=502 http://localhost:8901 #/api/healthcheck
|
||||
docker compose ps -a
|
||||
docker compose logs
|
||||
wget --retry-connrefused --retry-on-http-error=502 http://localhost:8901/api/health || (docker compose logs && exit 1)
|
||||
|
||||
- name: Run robot tests
|
||||
run: |
|
||||
@@ -42,7 +43,7 @@ jobs:
|
||||
|
||||
- name: Show logs
|
||||
if: failure()
|
||||
run: docker-compose logs
|
||||
run: docker compose logs
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
|
||||
7
.github/workflows/tests.yml
vendored
7
.github/workflows/tests.yml
vendored
@@ -8,8 +8,9 @@ on:
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
name: Back tests
|
||||
runs-on: ubuntu-latest
|
||||
container: mcr.microsoft.com/dotnet/sdk:6.0
|
||||
container: mcr.microsoft.com/dotnet/sdk:7.0
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
@@ -29,7 +30,7 @@ jobs:
|
||||
run: |
|
||||
cd back
|
||||
dotnet build '-p:SkipTranscoder=true' -p:CopyLocalLockFileAssemblies=true
|
||||
cp ./out/bin/Kyoo.Abstractions/Debug/net6.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll ./tests/Kyoo.Tests/bin/Debug/net6.0/
|
||||
cp ./out/bin/Kyoo.Abstractions/Debug/net7.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll ./tests/Kyoo.Tests/bin/Debug/net7.0/
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
@@ -37,7 +38,7 @@ jobs:
|
||||
dotnet test --no-build '-p:CollectCoverage=true;CoverletOutputFormat=opencover' --logger "trx;LogFileName=TestOutputResults.xml"
|
||||
env:
|
||||
POSTGRES_HOST: postgres
|
||||
POSTGRES_USERNAME: postgres
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
|
||||
- name: Sanitize coverage output
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
/video
|
||||
.env
|
||||
.venv
|
||||
.idea
|
||||
.vscode
|
||||
log.html
|
||||
|
||||
5
.pg_format
Normal file
5
.pg_format
Normal file
@@ -0,0 +1,5 @@
|
||||
tabs=1
|
||||
function-case=1 #lowercase
|
||||
keyword-case=1
|
||||
type-case=1
|
||||
no-space-function=1
|
||||
@@ -1,4 +1,4 @@
|
||||
# Authors
|
||||
Ordered by the date of the first commit.
|
||||
|
||||
* Zoe Roux ([@AnonymusRaccoon](http://github.com/AnonymusRaccoon))
|
||||
* Zoe Roux ([@zoriya](http://github.com/zoriya))
|
||||
|
||||
@@ -3,21 +3,19 @@
|
||||
Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great.
|
||||
|
||||
Contributions to this project are [released](https://docs.github.com/en/github/site-policy/github-terms-of-service#6-contributions-under-repository-license)
|
||||
to the public under the [GPL-3.0 License](https://github.com/AnonymusRaccoon/Kyoo/blob/master/LICENSE).
|
||||
to the public under the [GPL-3.0 License](https://github.com/zoriya/Kyoo/blob/master/LICENSE).
|
||||
|
||||
## Submitting a pull request
|
||||
|
||||
1. [Fork](https://github.com/AnonymusRaccoon/Kyoo/fork) and clone the repository
|
||||
2. Install the toolstack and/or download precompiled binaries for the transcoder (see [Development & Build](https://github.com/AnonymusRaccoon/Kyoo#development--build))
|
||||
3. Create a new branch: `git checkout -b my-branch-name`
|
||||
4. Make your changes (Please try to follow the same coding style as what has already been made - indent with tabs, align with spaces etc)
|
||||
5. Make sure the tests pass: `dotnet tests`
|
||||
6. Push to your fork and [submit a pull request](https://github.com/AnonymusRaccoon/Kyoo/compare)
|
||||
7. Pat your self on the back and wait for your pull request to be reviewed and merged.
|
||||
1. [Fork](https://github.com/zoriya/Kyoo/fork) and clone the repository
|
||||
2. Create a new branch: `git checkout -b my-branch-name`
|
||||
3. Make your changes (Please try to follow the same coding style as what has already been made - indent with tabs, align with spaces etc).
|
||||
Don't forget to run the auto-formatter.
|
||||
4. Push to your fork and [submit a pull request](https://github.com/zoriya/Kyoo/compare)
|
||||
5. Pat your self on the back and wait for your pull request to be reviewed and merged.
|
||||
|
||||
Here are a few things you can do that will increase the likelihood of your pull request being accepted:
|
||||
|
||||
- Write tests.
|
||||
- Make sure the `README.md` and any other relevant **documentation are kept up-to-date**.
|
||||
- We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option.
|
||||
- Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as **separate pull requests**.
|
||||
|
||||
56
INSTALLING.md
Normal file
56
INSTALLING.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# Installing
|
||||
|
||||
To install Kyoo, you need docker and docker-compose. Those can be installed from here for
|
||||
[Linux](https://docs.docker.com/engine/install/)
|
||||
[Mac](https://docs.docker.com/desktop/install/mac-install/)
|
||||
or [Windows](https://docs.docker.com/desktop/install/windows-install/). Docker is used to run each services of Kyoo in
|
||||
an isolated environment with all the dependencies they need.
|
||||
|
||||
Kyoo also needs 3 files to work properly. Two of them can simply be copy-pasted from this repository, the other needs to be filled in with your configurations.
|
||||
Those files can be put in any directory of your choice.
|
||||
|
||||
Those 3 files are:
|
||||
|
||||
- A `docker-compose.yml` (simply copy docker-compose.prod.yml from [here](https://raw.githubusercontent.com/zoriya/Kyoo/master/docker-compose.prod.yml)).
|
||||
- A `nginx.conf.template` copied from [here](https://raw.githubusercontent.com/zoriya/Kyoo/master/nginx.conf.template).
|
||||
- A `.env` file that you will need to **fill**. Look at the example [.env.example](https://raw.githubusercontent.com/zoriya/Kyoo/master/.env.example)
|
||||
|
||||
> If you want an explanation of what are those files, you can read the following:
|
||||
> The `docker-compose.yml` file describes the different services of Kyoo, where they should be downloaded and their start order. \
|
||||
> The `nignx.conf.template` file describes which service will be called when accessing the URL of Kyoo. \
|
||||
> The `.env` file contains all the configuration options that the services in `docker-compose.yml` will read.
|
||||
|
||||
To retrieve metadata, Kyoo will need to communicate with an external service. For now, that is `the movie database`.
|
||||
For this purpose, you will need to get an API Key. For that, go to [themoviedb.org](https://www.themoviedb.org/) and create an account, then
|
||||
go [here](https://www.themoviedb.org/settings/api) and copy the `API Key (v3 auth)`, paste it after the `THEMOVIEDB_APIKEY=` on the `.env` file.
|
||||
|
||||
The next and last step is actually starting Kyoo. To do that, open a terminal in the same directory as the 3 configurations files
|
||||
and run `docker-compose up -d`.
|
||||
|
||||
Congratulation, everything is now ready to use Kyoo. You can navigate to `http://localhost:8901` on a web browser to see your instance of Kyoo.
|
||||
|
||||
# Installing TLDR
|
||||
|
||||
1. Install docker & docker-compose
|
||||
2. Download the
|
||||
[`docker-compose.yml`](https://raw.githubusercontent.com/zoriya/Kyoo/master/docker-compose.prod.yml),
|
||||
[`nginx.conf.template`](https://raw.githubusercontent.com/zoriya/Kyoo/master/nginx.conf.template) and
|
||||
[`.env`](https://raw.githubusercontent.com/zoriya/Kyoo/master/.env.example) files
|
||||
3. Fill the `.env` file with your configuration options (and an API Key from [themoviedb.org](https://www.themoviedb.org/))
|
||||
4. Run `docker-compose up -d`
|
||||
|
||||
# Updating
|
||||
|
||||
Updating Kyoo is exactly the same as installing it. Get an updated version of the `docker-compose.yml` and `nginx.conf.template` files and
|
||||
unsure that your `.env` contains all the options specified in the updated `.env.example` file.
|
||||
|
||||
After that, you will need to update Kyoo's services. For that, open a terminal in the configuration's directory and run
|
||||
the command `docker-compose pull`. You are now ready to restart Kyoo, you can run `docker-compose up -d`.
|
||||
|
||||
You can also enable automatic updates via an external tool like [watchtower](https://containrrr.dev/watchtower/).
|
||||
TLDR: `docker run -d --name watchtower -e WATCHTOWER_CLEANUP=true -e WATCHTOWER_POLL_INTERVAL=86400 -v /var/run/docker.sock:/var/run/docker.sock containrrr/watchtower`
|
||||
|
||||
# Uninstalling
|
||||
|
||||
To uninstall Kyoo, you need to open a terminal in the configuration's directory and run `docker-compose down`. This will
|
||||
stop Kyoo's services. You can then remove the configuration files.
|
||||
44
PRIVACY.md
Normal file
44
PRIVACY.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Privacy Policy for Kyoo
|
||||
|
||||
Effective Date: 2023-06-15
|
||||
|
||||
Thank you for using Kyoo. This Privacy Policy outlines how we collect, use, and protect your personal information when you use our service. We are committed to ensuring the privacy and security of your data. Please read this policy carefully to understand our practices regarding your information.
|
||||
|
||||
### 1. Information We Collect:
|
||||
|
||||
We collect and store only the email addresses you provide during the registration process. No other personal information is requested or stored.
|
||||
|
||||
### 2. Use of Personal Information:
|
||||
|
||||
We use your email address solely for the purpose of user authentication and login. We do not use, access, or share your email address with any third parties.
|
||||
|
||||
### 3. Data Storage and Security:
|
||||
|
||||
All personal information, limited to email addresses, is stored securely on our self-hosted servers. We take appropriate technical and organizational measures to protect against unauthorized access, alteration, disclosure, or destruction of your personal information.
|
||||
|
||||
### 4. Data Sharing:
|
||||
|
||||
We do not share any personal information, including email addresses, with any third parties. Your data remains confidential and is used only for the purposes stated in this Privacy Policy.
|
||||
|
||||
### 5. Account Deletion:
|
||||
|
||||
You have the right to delete your Kyoo account at any time. To do so, please click on the `Delete your account` button on the account's menu of the app/website. Upon account deletion, all associated personal information, including your email address, will be permanently and irreversibly removed from our servers.
|
||||
|
||||
### 6. Cookies and Tracking Technologies:
|
||||
|
||||
Kyoo does not use any cookies or tracking technologies to collect or store user information.
|
||||
|
||||
### 7. Legal Basis for Processing:
|
||||
|
||||
Our collection and processing of your email address is based on your consent, as it is necessary for the performance of the contract between you and Kyoo.
|
||||
|
||||
### 8. Changes to this Privacy Policy:
|
||||
|
||||
We may update this Privacy Policy from time to time. Any changes will be posted on this page, and the effective date will be indicated at the top of the policy. Please review this policy periodically for any updates.
|
||||
|
||||
### 9. Contact Us:
|
||||
|
||||
If you have any questions, concerns, or requests regarding this Privacy Policy or the processing of your personal information, please contact us at https://github.com/zoriya/kyoo/issues.
|
||||
|
||||
|
||||
By using Kyoo, you acknowledge and agree to the practices described in this Privacy Policy.
|
||||
76
README.md
76
README.md
@@ -1,12 +1,4 @@
|
||||
# <img width="24px" src="./icons/icon-256x256.png" alt="Kyoo"> Kyoo
|
||||
<p>
|
||||
<a href="https://github.com/AnonymusRaccoon/Kyoo/actions/workflows/build.yml"><img src="https://img.shields.io/github/workflow/status/AnonymusRaccoon/Kyoo/Build?style=flat-square" alt="Build status"></a>
|
||||
<a href="https://github.com/AnonymusRaccoon/Kyoo/actions/workflows/tests.yml"><img src="https://img.shields.io/github/workflow/status/AnonymusRaccoon/Kyoo/Testing?label=tests&style=flat-square" alt="Tests status"></a>
|
||||
<a href="https://github.com/users/AnonymusRaccoon/packages/container/package/kyoo"><img src="https://img.shields.io/github/workflow/status/AnonymusRaccoon/Kyoo/Docker?label=docker&style=flat-square" alt="Docker status"/></a>
|
||||
<a href="https://sonarcloud.io/dashboard?id=AnonymusRaccoon_Kyoo"><img src="https://img.shields.io/sonar/tests/AnonymusRaccoon_Kyoo?compact_message&server=https%3A%2F%2Fsonarcloud.io&style=flat-square" alt="Test report"></a>
|
||||
<a href="https://sonarcloud.io/dashboard?id=AnonymusRaccoon_Kyoo"><img src="https://img.shields.io/sonar/coverage/AnonymusRaccoon_Kyoo?server=https%3A%2F%2Fsonarcloud.io&style=flat-square" alt="Coverage"></a>
|
||||
<a href="./LICENSE"><img src="https://img.shields.io/github/license/AnonymusRaccoon/Kyoo?style=flat-square" alt="License"></a>
|
||||
</p>
|
||||
|
||||
Kyoo is an open-source media browser which allow you to stream your movies, tv-shows or anime.
|
||||
It is an alternative to Plex, Emby or Jellyfin.
|
||||
@@ -17,78 +9,28 @@ Feel free to open issues or pull requests, all contribution are welcomed.
|
||||
|
||||
## Getting started
|
||||
|
||||
- [Installation](https://docs.kyoo.moe/start/install.html)
|
||||
- [Setting Up](https://docs.kyoo.moe/start/setting_up.html)
|
||||
- [Api Documentation](https://demo.kyoo.moe/redoc)
|
||||
- [Documentation (work in progress)](https://docs.kyoo.moe)
|
||||
- [Installation](./INSTALLING.md)
|
||||
- [Api Documentation](https://kyoo.zoriya.dev/api/doc)
|
||||
- [Contributing](./CONTRIBUTING.md)
|
||||
|
||||
## Features
|
||||
- Manage your movies, tv-series & anime
|
||||
- Transmux/Transcode files to make them available on every platform
|
||||
- Create smart watchlists that will automatically update when you watch movies/episodes
|
||||
- Search your collection via names, tags or descriptions
|
||||
- Download metadata automatically
|
||||
- Transmux files to make them available on every platform (Transcode coming soon)
|
||||
- Account system with a permission system
|
||||
- Handle subtitles natively with embedded fonts (ass, subrip or vtt)
|
||||
- Entirely free and works without internet (when metadata have already been downloaded)
|
||||
- Works on Linux, Windows, Docker and probably Mac
|
||||
- A powerful plugin system
|
||||
|
||||
## Live Demo
|
||||
|
||||
You can see a live demo with copyright-free movies here: [demo.kyoo.moe](https://demo.kyoo.moe).
|
||||
You can see a live demo with copyright-free movies here: [kyoo.zoriya.dev](https://kyoo.zoriya.dev).
|
||||
Thanks to the [blender studio](https://www.blender.org/about/studio/) for providing open-source movies available for all.
|
||||
|
||||
The demo server is simply created using the following docker compose:
|
||||
|
||||
```yml
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
kyoo:
|
||||
image: ghcr.io/anonymusraccoon/kyoo:master
|
||||
restart: on-failure
|
||||
environment:
|
||||
- KYOO_DATADIR=/var/lib/kyoo
|
||||
- DATABASE__ENABLED=postgres
|
||||
- DATABASE__CONFIGURATIONS__POSTGRES__SERVER=postgres
|
||||
- DATABASE__CONFIGURATIONS__POSTGRES__USER ID=kyoo
|
||||
- DATABASE__CONFIGURATIONS__POSTGRES__PASSWORD=kyooPassword
|
||||
- TVDB__APIKEY=TheTvDbApiKey
|
||||
- THE-MOVIEDB__APIKEY=TheMovieDbApiKey
|
||||
ports:
|
||||
- "80:5000"
|
||||
depends_on:
|
||||
- postgres
|
||||
volumes:
|
||||
- kyoo:/var/lib/kyoo
|
||||
- video:/video
|
||||
postgres:
|
||||
image: "postgres"
|
||||
restart: on-failure
|
||||
environment:
|
||||
- POSTGRES_USER=kyoo
|
||||
- POSTGRES_PASSWORD=kyooPassword
|
||||
volumes:
|
||||
- db:/var/lib/postgresql/data
|
||||
|
||||
volumes:
|
||||
kyoo:
|
||||
video:
|
||||
db:
|
||||
```
|
||||
|
||||
## Screens
|
||||
|
||||

|
||||

|
||||
- - -
|
||||

|
||||

|
||||
- - -
|
||||

|
||||
- - -
|
||||

|
||||
- - -
|
||||

|
||||
- - -
|
||||

|
||||
- - -
|
||||

|
||||

|
||||
|
||||
18
back/.config/dotnet-tools.json
Normal file
18
back/.config/dotnet-tools.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"dotnet-ef": {
|
||||
"version": "7.0.9",
|
||||
"commands": [
|
||||
"dotnet-ef"
|
||||
]
|
||||
},
|
||||
"csharpier": {
|
||||
"version": "0.26.4",
|
||||
"commands": [
|
||||
"dotnet-csharpier"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,11 +11,11 @@ smart_tab = true
|
||||
|
||||
[*.cs]
|
||||
csharp_prefer_braces = false
|
||||
dotnet_diagnostic.IDE0130.severity = none
|
||||
dotnet_diagnostic.IDE0058.severity = none
|
||||
dotnet_diagnostic.IDE0046.severity = none
|
||||
dotnet_diagnostic.CA1848.severity = none
|
||||
dotnet_diagnostic.CA2007.severity = none
|
||||
dotnet_diagnostic.IDE0055.severity = none
|
||||
dotnet_diagnostic.IDE0058.severity = none
|
||||
dotnet_diagnostic.IDE0130.severity = none
|
||||
|
||||
# Sort using and Import directives with System.* appearing first
|
||||
dotnet_sort_system_directives_first = true
|
||||
csharp_using_directive_placement = outside_namespace:warning
|
||||
@@ -43,9 +43,9 @@ dotnet_style_prefer_conditional_expression_over_return = true
|
||||
# Disable strange throw.
|
||||
csharp_style_throw_expression = false:suggestion
|
||||
# Forbid "var" everywhere
|
||||
csharp_style_var_for_built_in_types = false:warning
|
||||
csharp_style_var_when_type_is_apparent = false:warning
|
||||
csharp_style_var_elsewhere = false:warning
|
||||
csharp_style_var_for_built_in_types = false:suggestion
|
||||
csharp_style_var_when_type_is_apparent = false:suggestion
|
||||
csharp_style_var_elsewhere = false:suggestion
|
||||
# Prefer method-like constructs to have a block body
|
||||
csharp_style_expression_bodied_methods = false:none
|
||||
csharp_style_expression_bodied_constructors = false:none
|
||||
@@ -89,6 +89,9 @@ resharper_wrap_before_arrow_with_expressions = true
|
||||
resharper_xmldoc_attribute_indent = align_by_first_attribute
|
||||
resharper_xmldoc_indent_child_elements = RemoveIndent
|
||||
resharper_xmldoc_indent_text = RemoveIndent
|
||||
# Switch on enum
|
||||
dotnet_diagnostic.CS8509.severity=error # missing switch case for named enum value
|
||||
dotnet_diagnostic.CS8524.severity=none # missing switch case for unnamed enum value
|
||||
|
||||
# Waiting for https://github.com/dotnet/roslyn/issues/44596 to get fixed.
|
||||
# file_header_template = Kyoo - A portable and vast media library solution.\nCopyright (c) Kyoo.\n\nSee AUTHORS.md and LICENSE file in the project root for full license information.\n\nKyoo is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\nany later version.\n\nKyoo is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
2
back/.gitignore
vendored
2
back/.gitignore
vendored
@@ -2,6 +2,7 @@ out
|
||||
libtranscoder.so
|
||||
libtranscoder.dylib
|
||||
transcoder.dll
|
||||
kyoo_datadir
|
||||
|
||||
video
|
||||
.env
|
||||
@@ -80,7 +81,6 @@ StyleCopReport.xml
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
|
||||
@@ -1,37 +1,30 @@
|
||||
FROM gcc:latest as transcoder
|
||||
RUN apt-get update && apt-get install -y cmake make libavutil-dev libavcodec-dev libavformat-dev
|
||||
WORKDIR /transcoder
|
||||
COPY src/Kyoo.Transcoder .
|
||||
RUN cmake . && make -j
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0 as builder
|
||||
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:7.0 as builder
|
||||
ARG TARGETARCH
|
||||
WORKDIR /kyoo
|
||||
|
||||
COPY Kyoo.sln ./Kyoo.sln
|
||||
COPY nuget.config ./nuget.config
|
||||
COPY src/Directory.Build.props src/Directory.Build.props
|
||||
COPY src/Kyoo.Authentication/Kyoo.Authentication.csproj src/Kyoo.Authentication/Kyoo.Authentication.csproj
|
||||
COPY src/Kyoo.Database/Kyoo.Database.csproj src/Kyoo.Database/Kyoo.Database.csproj
|
||||
COPY src/Kyoo.Host.Generic/Kyoo.Host.Generic.csproj src/Kyoo.Host.Generic/Kyoo.Host.Generic.csproj
|
||||
COPY src/Kyoo.SqLite/Kyoo.SqLite.csproj src/Kyoo.SqLite/Kyoo.SqLite.csproj
|
||||
COPY src/Kyoo.TheMovieDb/Kyoo.TheMovieDb.csproj src/Kyoo.TheMovieDb/Kyoo.TheMovieDb.csproj
|
||||
COPY src/Kyoo.Abstractions/Kyoo.Abstractions.csproj src/Kyoo.Abstractions/Kyoo.Abstractions.csproj
|
||||
COPY src/Kyoo.Core/Kyoo.Core.csproj src/Kyoo.Core/Kyoo.Core.csproj
|
||||
COPY src/Kyoo.Host.Console/Kyoo.Host.Console.csproj src/Kyoo.Host.Console/Kyoo.Host.Console.csproj
|
||||
COPY src/Kyoo.Host/Kyoo.Host.csproj src/Kyoo.Host/Kyoo.Host.csproj
|
||||
COPY src/Kyoo.Postgresql/Kyoo.Postgresql.csproj src/Kyoo.Postgresql/Kyoo.Postgresql.csproj
|
||||
COPY src/Kyoo.Meilisearch/Kyoo.Meilisearch.csproj src/Kyoo.Meilisearch/Kyoo.Meilisearch.csproj
|
||||
COPY src/Kyoo.Swagger/Kyoo.Swagger.csproj src/Kyoo.Swagger/Kyoo.Swagger.csproj
|
||||
COPY src/Kyoo.TheTvdb/Kyoo.TheTvdb.csproj src/Kyoo.TheTvdb/Kyoo.TheTvdb.csproj
|
||||
COPY tests/Kyoo.Tests/Kyoo.Tests.csproj tests/Kyoo.Tests/Kyoo.Tests.csproj
|
||||
RUN dotnet restore
|
||||
RUN dotnet restore -a $TARGETARCH
|
||||
|
||||
COPY . .
|
||||
ARG VERSION
|
||||
RUN dotnet publish --no-restore -c Release -o /opt/kyoo "-p:Version=${VERSION:-"0.0.0-dev"};SkipTranscoder=true" src/Kyoo.Host.Console
|
||||
RUN dotnet publish -a $TARGETARCH --no-restore -c Release -o /app "-p:Version=${VERSION:-"0.0.0-dev"}" src/Kyoo.Host
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:6.0
|
||||
RUN apt-get update && apt-get install -y libavutil-dev libavcodec-dev libavformat-dev
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:7.0
|
||||
RUN apt-get update && apt-get install -y curl
|
||||
COPY --from=builder /app /app
|
||||
|
||||
WORKDIR /kyoo
|
||||
EXPOSE 5000
|
||||
COPY --from=builder /opt/kyoo /usr/lib/kyoo
|
||||
COPY --from=transcoder /transcoder/libtranscoder.so /usr/lib/kyoo
|
||||
CMD ["/usr/lib/kyoo/Kyoo.Host.Console"]
|
||||
|
||||
# The back can take a long time to start if meilisearch is initializing
|
||||
HEALTHCHECK --interval=5s --retries=15 CMD curl --fail http://localhost:5000/health || exit
|
||||
ENTRYPOINT ["/app/Kyoo.Host"]
|
||||
|
||||
@@ -1,35 +1,23 @@
|
||||
FROM gcc:latest as transcoder
|
||||
RUN apt-get update && apt-get install -y cmake make libavutil-dev libavcodec-dev libavformat-dev
|
||||
WORKDIR /transcoder
|
||||
COPY src/Kyoo.Transcoder .
|
||||
RUN cmake . && make -j
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0
|
||||
RUN apt-get update && apt-get install -y libavutil-dev libavcodec-dev libavformat-dev
|
||||
FROM mcr.microsoft.com/dotnet/sdk:7.0
|
||||
RUN apt-get update && apt-get install -y curl
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
COPY Kyoo.sln ./Kyoo.sln
|
||||
COPY nuget.config ./nuget.config
|
||||
COPY src/Directory.Build.props src/Directory.Build.props
|
||||
COPY src/Kyoo.Authentication/Kyoo.Authentication.csproj src/Kyoo.Authentication/Kyoo.Authentication.csproj
|
||||
COPY src/Kyoo.Database/Kyoo.Database.csproj src/Kyoo.Database/Kyoo.Database.csproj
|
||||
COPY src/Kyoo.Host.Generic/Kyoo.Host.Generic.csproj src/Kyoo.Host.Generic/Kyoo.Host.Generic.csproj
|
||||
COPY src/Kyoo.SqLite/Kyoo.SqLite.csproj src/Kyoo.SqLite/Kyoo.SqLite.csproj
|
||||
COPY src/Kyoo.TheMovieDb/Kyoo.TheMovieDb.csproj src/Kyoo.TheMovieDb/Kyoo.TheMovieDb.csproj
|
||||
COPY src/Kyoo.Abstractions/Kyoo.Abstractions.csproj src/Kyoo.Abstractions/Kyoo.Abstractions.csproj
|
||||
COPY src/Kyoo.Core/Kyoo.Core.csproj src/Kyoo.Core/Kyoo.Core.csproj
|
||||
COPY src/Kyoo.Host.Console/Kyoo.Host.Console.csproj src/Kyoo.Host.Console/Kyoo.Host.Console.csproj
|
||||
COPY src/Kyoo.Host/Kyoo.Host.csproj src/Kyoo.Host/Kyoo.Host.csproj
|
||||
COPY src/Kyoo.Postgresql/Kyoo.Postgresql.csproj src/Kyoo.Postgresql/Kyoo.Postgresql.csproj
|
||||
COPY src/Kyoo.Meilisearch/Kyoo.Meilisearch.csproj src/Kyoo.Meilisearch/Kyoo.Meilisearch.csproj
|
||||
COPY src/Kyoo.Swagger/Kyoo.Swagger.csproj src/Kyoo.Swagger/Kyoo.Swagger.csproj
|
||||
COPY src/Kyoo.TheTvdb/Kyoo.TheTvdb.csproj src/Kyoo.TheTvdb/Kyoo.TheTvdb.csproj
|
||||
COPY tests/Kyoo.Tests/Kyoo.Tests.csproj tests/Kyoo.Tests/Kyoo.Tests.csproj
|
||||
|
||||
RUN dotnet restore
|
||||
|
||||
COPY --from=transcoder /transcoder/libtranscoder.so /usr/lib/
|
||||
|
||||
WORKDIR /kyoo
|
||||
EXPOSE 5000
|
||||
ENV DOTNET_USE_POLLING_FILE_WATCHER 1
|
||||
CMD ["dotnet", "watch", "run", "--no-restore", "--project", "src/Kyoo.Host.Console"]
|
||||
|
||||
# HEALTHCHECK --interval=5s CMD curl --fail http://localhost:5000/health || exit
|
||||
HEALTHCHECK CMD true
|
||||
ENTRYPOINT ["dotnet", "watch", "run", "--no-restore", "--project", "/app/src/Kyoo.Host"]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.CSharp.MaintainabilityRules">
|
||||
<Rule Id="SA1413" Action="None" /> <!-- UseTrailingCommasInMultiLineInitializers -->
|
||||
<Rule Id="SA1414" Action="None" /> <!-- UseTrailingCommasInMultiLineInitializers -->
|
||||
<Rule Id="SA1114" Action="None" /> <!-- UseTrailingCommasInMultiLineInitializers -->
|
||||
</Rules>
|
||||
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.CSharp.OrderingRules">
|
||||
<Rule Id="SA1201" Action="None" /> <!-- ElementsMustAppearInTheCorrectOrder -->
|
||||
@@ -27,18 +28,20 @@
|
||||
<Rule Id="SA1027" Action="None"/> <!-- UseTabsCorrectly (smarts tabs are broken). TODO find a way to enable smart tabs -->
|
||||
</Rules>
|
||||
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.CSharp.LayoutRules">
|
||||
<Rule Id="SA1402" Action="None"/> <!-- SingleClassPerFile -->
|
||||
<Rule Id="SA1649" Action="None"/> <!-- Class name must be filename -->
|
||||
<Rule Id="SA1503" Action="None"/> <!-- BracesMustNotBeOmitted -->
|
||||
<Rule Id="SA1512" Action="None"/> <!-- Let me comment -->
|
||||
<Rule Id="SA1520" Action="None"/> <!-- UseBracesConsistently -->
|
||||
<Rule Id="SA1515" Action="None"/> <!-- SingleLineCommentMustBePrecededByBlankLine -->
|
||||
<Rule Id="SA1513" Action="None"/> <!-- ClosingBraceMustBeFollowedByBlankLine -->
|
||||
</Rules>
|
||||
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.CSharp.DocumentationRules">
|
||||
<Rule Id="SA1600" Action="None" /> <!-- Elements Shuld be Documented -->
|
||||
<Rule Id="SA1602" Action="None" /> <!-- Enums should be documented -->
|
||||
<Rule Id="SA1642" Action="None" /> <!-- ConstructorSummaryDocumentationMustBeginWithStandardText -->
|
||||
<Rule Id="SA1643" Action="None" /> <!-- DestructorSummaryDocumentationMustBeginWithStandardText -->
|
||||
<Rule Id="SA1623" Action="None" /> <!-- PropertySummaryDocumentationMustMatchAccessors -->
|
||||
|
||||
|
||||
|
||||
<Rule Id="SA1629" Action="None" /> <!-- DocumentationTextMustEndWithAPeriod TODO remove this, this is only temporary -->
|
||||
<Rule Id="SA1629" Action="None" /> <!-- DocumentationTextMustEndWithAPeriod -->
|
||||
</Rules>
|
||||
</RuleSet>
|
||||
|
||||
@@ -3,33 +3,19 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kyoo.Core", "src\Kyoo.Core\
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Abstractions", "src\Kyoo.Abstractions\Kyoo.Abstractions.csproj", "{BAB2CAE1-AC28-4509-AA3E-8DC75BD59220}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Database", "src\Kyoo.Database\Kyoo.Database.csproj", "{6F91B645-F785-46BB-9C4F-1EFC83E489B6}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Postgresql", "src\Kyoo.Postgresql\Kyoo.Postgresql.csproj", "{3213C96D-0BF3-460B-A8B5-B9977229408A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Authentication", "src\Kyoo.Authentication\Kyoo.Authentication.csproj", "{7A841335-6523-47DB-9717-80AA7BD943FD}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.SqLite", "src\Kyoo.SqLite\Kyoo.SqLite.csproj", "{6515380E-1E57-42DA-B6E3-E1C8A848818A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.TheTvdb", "src\Kyoo.TheTvdb\Kyoo.TheTvdb.csproj", "{D06BF829-23F5-40F3-A62D-627D9F4B4D6C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.TheMovieDb", "src\Kyoo.TheMovieDb\Kyoo.TheMovieDb.csproj", "{BAB270D4-E0EA-4329-BA65-512FDAB01001}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Tests", "tests\Kyoo.Tests\Kyoo.Tests.csproj", "{0C8AA7EA-E723-4532-852F-35AA4E8AFED5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Host.Console", "src\Kyoo.Host.Console\Kyoo.Host.Console.csproj", "{D8658BEA-8949-45AC-BEBB-A4FFC4F800F5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Swagger", "src\Kyoo.Swagger\Kyoo.Swagger.csproj", "{7D1A7596-73F6-4D35-842E-A5AD9C620596}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{FEAE1B0E-D797-470F-9030-0EF743575ECC}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Providers", "Providers", "{8D28F5EF-0CD7-4697-A2A7-24EC31A48F21}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Host", "src\Kyoo.Host\Kyoo.Host.csproj", "{0938459E-2E2B-457F-8120-7D8CA93866A6}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Databases", "Databases", "{865461CA-EC06-4B42-91CF-8723B0A9BB67}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Hosts", "Hosts", "{C569FF25-7E01-484C-9F72-5B99845AD94B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Host.Generic", "src\Kyoo.Host.Generic\Kyoo.Host.Generic.csproj", "{0938459E-2E2B-457F-8120-7D8CA93866A6}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Meilisearch", "src\Kyoo.Meilisearch\Kyoo.Meilisearch.csproj", "{F8E6018A-FD51-40EB-99FF-A26BA59F2762}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -45,10 +31,6 @@ Global
|
||||
{BAB2CAE1-AC28-4509-AA3E-8DC75BD59220}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BAB2CAE1-AC28-4509-AA3E-8DC75BD59220}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BAB2CAE1-AC28-4509-AA3E-8DC75BD59220}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6F91B645-F785-46BB-9C4F-1EFC83E489B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6F91B645-F785-46BB-9C4F-1EFC83E489B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6F91B645-F785-46BB-9C4F-1EFC83E489B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6F91B645-F785-46BB-9C4F-1EFC83E489B6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3213C96D-0BF3-460B-A8B5-B9977229408A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3213C96D-0BF3-460B-A8B5-B9977229408A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3213C96D-0BF3-460B-A8B5-B9977229408A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@@ -61,14 +43,6 @@ Global
|
||||
{6515380E-1E57-42DA-B6E3-E1C8A848818A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6515380E-1E57-42DA-B6E3-E1C8A848818A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6515380E-1E57-42DA-B6E3-E1C8A848818A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D06BF829-23F5-40F3-A62D-627D9F4B4D6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D06BF829-23F5-40F3-A62D-627D9F4B4D6C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D06BF829-23F5-40F3-A62D-627D9F4B4D6C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D06BF829-23F5-40F3-A62D-627D9F4B4D6C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BAB270D4-E0EA-4329-BA65-512FDAB01001}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BAB270D4-E0EA-4329-BA65-512FDAB01001}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BAB270D4-E0EA-4329-BA65-512FDAB01001}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BAB270D4-E0EA-4329-BA65-512FDAB01001}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0C8AA7EA-E723-4532-852F-35AA4E8AFED5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0C8AA7EA-E723-4532-852F-35AA4E8AFED5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0C8AA7EA-E723-4532-852F-35AA4E8AFED5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@@ -81,10 +55,6 @@ Global
|
||||
{4FF1ECD9-6EEF-4440-B037-A661D78FB04D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4FF1ECD9-6EEF-4440-B037-A661D78FB04D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4FF1ECD9-6EEF-4440-B037-A661D78FB04D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D8658BEA-8949-45AC-BEBB-A4FFC4F800F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D8658BEA-8949-45AC-BEBB-A4FFC4F800F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D8658BEA-8949-45AC-BEBB-A4FFC4F800F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D8658BEA-8949-45AC-BEBB-A4FFC4F800F5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7D1A7596-73F6-4D35-842E-A5AD9C620596}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7D1A7596-73F6-4D35-842E-A5AD9C620596}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7D1A7596-73F6-4D35-842E-A5AD9C620596}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@@ -93,15 +63,12 @@ Global
|
||||
{0938459E-2E2B-457F-8120-7D8CA93866A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0938459E-2E2B-457F-8120-7D8CA93866A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0938459E-2E2B-457F-8120-7D8CA93866A6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F8E6018A-FD51-40EB-99FF-A26BA59F2762}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F8E6018A-FD51-40EB-99FF-A26BA59F2762}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F8E6018A-FD51-40EB-99FF-A26BA59F2762}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F8E6018A-FD51-40EB-99FF-A26BA59F2762}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{0C8AA7EA-E723-4532-852F-35AA4E8AFED5} = {FEAE1B0E-D797-470F-9030-0EF743575ECC}
|
||||
{BAB270D4-E0EA-4329-BA65-512FDAB01001} = {8D28F5EF-0CD7-4697-A2A7-24EC31A48F21}
|
||||
{D06BF829-23F5-40F3-A62D-627D9F4B4D6C} = {8D28F5EF-0CD7-4697-A2A7-24EC31A48F21}
|
||||
{6F91B645-F785-46BB-9C4F-1EFC83E489B6} = {865461CA-EC06-4B42-91CF-8723B0A9BB67}
|
||||
{3213C96D-0BF3-460B-A8B5-B9977229408A} = {865461CA-EC06-4B42-91CF-8723B0A9BB67}
|
||||
{6515380E-1E57-42DA-B6E3-E1C8A848818A} = {865461CA-EC06-4B42-91CF-8723B0A9BB67}
|
||||
{D8658BEA-8949-45AC-BEBB-A4FFC4F800F5} = {C569FF25-7E01-484C-9F72-5B99845AD94B}
|
||||
{0938459E-2E2B-457F-8120-7D8CA93866A6} = {C569FF25-7E01-484C-9F72-5B99845AD94B}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
4
back/ef.rsp
Normal file
4
back/ef.rsp
Normal file
@@ -0,0 +1,4 @@
|
||||
--project
|
||||
src/Kyoo.Postgresql/Kyoo.Postgresql.csproj
|
||||
--msbuildprojectextensionspath
|
||||
out/obj/Kyoo.Postgresql
|
||||
@@ -1,13 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<clear/>
|
||||
<add key="nuget" value="https://api.nuget.org/v3/index.json"/>
|
||||
</packageSources>
|
||||
<disabledPackageSources>
|
||||
<clear/>
|
||||
</disabledPackageSources>
|
||||
<fallbackPackageFolders>
|
||||
<clear/>
|
||||
</fallbackPackageFolders>
|
||||
<packageSources>
|
||||
<clear/>
|
||||
<add key="feedz" value="https://f.feedz.io/zoriya/entityframeworkcore-projectables/nuget/index.json"/>
|
||||
<add key="nuget" value="https://api.nuget.org/v3/index.json"/>
|
||||
</packageSources>
|
||||
<disabledPackageSources>
|
||||
<clear/>
|
||||
</disabledPackageSources>
|
||||
<fallbackPackageFolders>
|
||||
<clear/>
|
||||
</fallbackPackageFolders>
|
||||
</configuration>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<LangVersion>default</LangVersion>
|
||||
<Company>Kyoo</Company>
|
||||
<Authors>Kyoo</Authors>
|
||||
@@ -9,10 +9,10 @@
|
||||
<PackageLicenseExpression>GPL-3.0-or-later</PackageLicenseExpression>
|
||||
<RequireLicenseAcceptance>true</RequireLicenseAcceptance>
|
||||
|
||||
<RepositoryUrl>https://github.com/AnonymusRaccoon/Kyoo</RepositoryUrl>
|
||||
<RepositoryUrl>https://github.com/zoriya/Kyoo</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<PackageProjectUrl>https://github.com/AnonymusRaccoon/Kyoo</PackageProjectUrl>
|
||||
<PackageProjectUrl>https://github.com/zoriya/Kyoo</PackageProjectUrl>
|
||||
|
||||
<PackageVersion>1.0.0</PackageVersion>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
@@ -29,13 +29,6 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<IsWindows Condition="$([MSBuild]::IsOSPlatform('Windows'))">true</IsWindows>
|
||||
<IsOSX Condition="$([MSBuild]::IsOSPlatform('OSX'))">true</IsOSX>
|
||||
<IsLinux Condition="$([MSBuild]::IsOSPlatform('Linux'))">true</IsLinux>
|
||||
|
||||
<!-- This is horrible but the only workarround I found to disable the transcoder copy on the dev dockerfile -->
|
||||
<SkipTranscoder Condition="$(MsBuildThisFileDirectory) == '/app/src/'">true</SkipTranscoder>
|
||||
|
||||
<CheckCodingStyle Condition="$(CheckCodingStyle) == ''">true</CheckCodingStyle>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -44,15 +37,14 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="$(CheckCodingStyle) == true">
|
||||
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.354" PrivateAssets="All" />
|
||||
|
||||
<AdditionalFiles Include="$(MSBuildThisFileDirectory)../stylecop.json" Link="stylecop.json" Visible="false" />
|
||||
<None Include="$(MSBuildThisFileDirectory)../.editorconfig" Link=".editorconfig" Visible="false" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition="$(CheckCodingStyle) == true">
|
||||
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)../Kyoo.ruleset</CodeAnalysisRuleSet>
|
||||
<NoWarn>1591;1305;8618;SYSLIB1045;CS1573</NoWarn>
|
||||
<!-- <AnalysisMode>All</AnalysisMode> -->
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- vim: ft=xml -->
|
||||
</Project>
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace Kyoo.Abstractions.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface that allow one to interact with the host and shutdown or restart the app.
|
||||
/// </summary>
|
||||
public interface IApplication
|
||||
{
|
||||
/// <summary>
|
||||
/// Shutdown the process and stop gracefully.
|
||||
/// </summary>
|
||||
void Shutdown();
|
||||
|
||||
/// <summary>
|
||||
/// Restart Kyoo from scratch, reload plugins, configurations and restart the web server.
|
||||
/// </summary>
|
||||
void Restart();
|
||||
|
||||
/// <summary>
|
||||
/// Get the data directory.
|
||||
/// </summary>
|
||||
/// <returns>Retrieve the data directory where runtime data should be stored.</returns>
|
||||
string GetDataDirectory();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the path of the json configuration file
|
||||
/// (relative to the data directory, see <see cref="GetDataDirectory"/>).
|
||||
/// </summary>
|
||||
/// <returns>The configuration file name.</returns>
|
||||
string GetConfigFile();
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Kyoo.Abstractions.Models;
|
||||
using Kyoo.Abstractions.Models.Exceptions;
|
||||
|
||||
namespace Kyoo.Abstractions.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// A class to ease configuration management. This work WITH Microsoft's package, you can still use IOptions patterns
|
||||
/// to access your options, this manager ease dynamic work and editing.
|
||||
/// It works with <see cref="ConfigurationReference"/>.
|
||||
/// </summary>
|
||||
public interface IConfigurationManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Add an editable configuration to the editable configuration list.
|
||||
/// </summary>
|
||||
/// <param name="path">The root path of the editable configuration. It should not be a nested type.</param>
|
||||
/// <typeparam name="T">The type of the configuration.</typeparam>
|
||||
void AddTyped<T>(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Add an editable configuration to the editable configuration list.
|
||||
/// WARNING: this method allow you to add an unmanaged type. This type won't be editable. This can be used
|
||||
/// for external libraries or variable arguments.
|
||||
/// </summary>
|
||||
/// <param name="path">The root path of the editable configuration. It should not be a nested type.</param>
|
||||
void AddUntyped(string path);
|
||||
|
||||
/// <summary>
|
||||
/// An helper method of <see cref="AddTyped{T}"/> and <see cref="AddUntyped"/>.
|
||||
/// This register a typed value if <paramref name="type"/> is not <c>null</c> and registers an untyped type
|
||||
/// if <paramref name="type"/> is <c>null</c>.
|
||||
/// </summary>
|
||||
/// <param name="path">The root path of the editable configuration. It should not be a nested type.</param>
|
||||
/// <param name="type">The type of the configuration or null.</param>
|
||||
void Register(string path, [CanBeNull] Type type);
|
||||
|
||||
/// <summary>
|
||||
/// Get the value of a setting using it's path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the resource (can be separated by ':' or '__').</param>
|
||||
/// <exception cref="ItemNotFoundException">No setting found at the given path.</exception>
|
||||
/// <returns>The value of the settings (if it's a strongly typed one, the given type is instantiated.</returns>
|
||||
object GetValue(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Get the value of a setting using it's path.
|
||||
/// If your don't need a strongly typed value, see <see cref="GetValue"/>.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the resource (can be separated by ':' or '__').</param>
|
||||
/// <typeparam name="T">A type to strongly type your option.</typeparam>
|
||||
/// <exception cref="InvalidCastException">If your type is not the same as the registered type.</exception>
|
||||
/// <exception cref="ItemNotFoundException">No setting found at the given path.</exception>
|
||||
/// <returns>The value of the settings (if it's a strongly typed one, the given type is instantiated.</returns>
|
||||
T GetValue<T>(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Edit the value of a setting using it's path. Save it to the json file.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the resource (can be separated by ':' or '__').</param>
|
||||
/// <param name="value">The new value of the resource.</param>
|
||||
/// <exception cref="ItemNotFoundException">No setting found at the given path.</exception>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
Task EditValue(string path, object value);
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Kyoo.Abstractions.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Kyoo.Abstractions.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// A service to abstract the file system to allow custom file systems
|
||||
/// (like distant file systems or external providers).
|
||||
/// </summary>
|
||||
public interface IFileSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Used for http queries returning a file. This should be used to return local files
|
||||
/// or proxy them from a distant server.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If no file exists at the given path or if the path is null, a NotFoundResult is returned
|
||||
/// to handle it gracefully.
|
||||
/// </remarks>
|
||||
/// <param name="path">The path of the file.</param>
|
||||
/// <param name="rangeSupport">
|
||||
/// Should the file be downloaded at once or is the client allowed to request only part of the file
|
||||
/// </param>
|
||||
/// <param name="type">
|
||||
/// You can manually specify the content type of your file.
|
||||
/// For example you can force a file to be returned as plain text using <c>text/plain</c>.
|
||||
/// If the type is not specified, it will be deduced automatically (from the extension or by sniffing the file).
|
||||
/// </param>
|
||||
/// <returns>An <see cref="IActionResult"/> representing the file returned.</returns>
|
||||
IActionResult FileResult([CanBeNull] string path, bool rangeSupport = false, string type = null);
|
||||
|
||||
/// <summary>
|
||||
/// Read a file present at <paramref name="path"/>. The reader can be used in an arbitrary context.
|
||||
/// To return files from an http endpoint, use <see cref="FileResult"/>.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file</param>
|
||||
/// <exception cref="FileNotFoundException">If the file could not be found.</exception>
|
||||
/// <returns>A reader to read the file.</returns>
|
||||
Task<Stream> GetReader([NotNull] string path);
|
||||
|
||||
/// <summary>
|
||||
/// Read a file present at <paramref name="path"/>. The reader can be used in an arbitrary context.
|
||||
/// To return files from an http endpoint, use <see cref="FileResult"/>.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file</param>
|
||||
/// <param name="mime">The mime type of the opened file.</param>
|
||||
/// <exception cref="FileNotFoundException">If the file could not be found.</exception>
|
||||
/// <returns>A reader to read the file.</returns>
|
||||
Task<Stream> GetReader([NotNull] string path, AsyncRef<string> mime);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new file at <paramref name="path"></paramref>.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the new file.</param>
|
||||
/// <returns>A writer to write to the new file.</returns>
|
||||
Task<Stream> NewFile([NotNull] string path);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new directory at the given path
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the directory</param>
|
||||
/// <returns>The path of the newly created directory is returned.</returns>
|
||||
Task<string> CreateDirectory([NotNull] string path);
|
||||
|
||||
/// <summary>
|
||||
/// Combine multiple paths.
|
||||
/// </summary>
|
||||
/// <param name="paths">The paths to combine</param>
|
||||
/// <returns>The combined path.</returns>
|
||||
string Combine(params string[] paths);
|
||||
|
||||
/// <summary>
|
||||
/// List files in a directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the directory</param>
|
||||
/// <param name="options">Should the search be recursive or not.</param>
|
||||
/// <returns>A list of files's path.</returns>
|
||||
Task<ICollection<string>> ListFiles([NotNull] string path,
|
||||
SearchOption options = SearchOption.TopDirectoryOnly);
|
||||
|
||||
/// <summary>
|
||||
/// Check if a file exists at the given path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to check</param>
|
||||
/// <returns>True if the path exists, false otherwise</returns>
|
||||
Task<bool> Exists([NotNull] string path);
|
||||
|
||||
/// <summary>
|
||||
/// Get the extra directory of a resource <typeparamref name="T"/>.
|
||||
/// This method is in this system to allow a filesystem to use a different metadata policy for one.
|
||||
/// It can be useful if the filesystem is readonly.
|
||||
/// </summary>
|
||||
/// <param name="resource">The resource to proceed</param>
|
||||
/// <typeparam name="T">The type of the resource.</typeparam>
|
||||
/// <returns>The extra directory of the resource.</returns>
|
||||
Task<string> GetExtraDirectory<T>([NotNull] T resource);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve tracks for a specific episode.
|
||||
/// Subtitles, chapters and fonts should also be extracted and cached when calling this method.
|
||||
/// </summary>
|
||||
/// <param name="episode">The episode to retrieve tracks for.</param>
|
||||
/// <param name="reExtract">Should the cache be invalidated and subtitles and others be re-extracted?</param>
|
||||
/// <returns>The list of tracks available for this episode.</returns>
|
||||
Task<ICollection<Track>> ExtractInfos([NotNull] Episode episode, bool reExtract);
|
||||
|
||||
/// <summary>
|
||||
/// Transmux the selected episode to hls.
|
||||
/// </summary>
|
||||
/// <param name="episode">The episode to transmux.</param>
|
||||
/// <returns>The master file (m3u8) of the transmuxed hls file.</returns>
|
||||
IActionResult Transmux([NotNull] Episode episode);
|
||||
|
||||
// Maybe add options for to select the codec.
|
||||
// IActionResult Transcode(Episode episode);
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Abstractions.Models;
|
||||
using Kyoo.Abstractions.Models.Exceptions;
|
||||
|
||||
namespace Kyoo.Abstractions.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface to identify episodes, shows and metadata based on the episode file.
|
||||
/// </summary>
|
||||
public interface IIdentifier
|
||||
{
|
||||
/// <summary>
|
||||
/// Identify a path and return the parsed metadata.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the episode file to parse.</param>
|
||||
/// <exception cref="IdentificationFailedException">
|
||||
/// The identifier could not work for the given path.
|
||||
/// </exception>
|
||||
/// <returns>
|
||||
/// A tuple of models representing parsed metadata.
|
||||
/// If no metadata could be parsed for a type, null can be returned.
|
||||
/// </returns>
|
||||
Task<(Collection, Show, Season, Episode)> Identify(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Identify an external subtitle or track file from it's path and return the parsed metadata.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the external track file to parse.</param>
|
||||
/// <exception cref="IdentificationFailedException">
|
||||
/// The identifier could not work for the given path.
|
||||
/// </exception>
|
||||
/// <returns>
|
||||
/// The metadata of the track identified.
|
||||
/// </returns>
|
||||
Task<Track> IdentifyTrack(string path);
|
||||
}
|
||||
}
|
||||
@@ -16,14 +16,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Kyoo.Abstractions.Models;
|
||||
using Kyoo.Abstractions.Models.Exceptions;
|
||||
|
||||
namespace Kyoo.Abstractions.Controllers
|
||||
{
|
||||
@@ -32,581 +25,62 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// </summary>
|
||||
public interface ILibraryManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the repository corresponding to the T item.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type you want</typeparam>
|
||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||
/// <returns>The repository corresponding</returns>
|
||||
IRepository<T> GetRepository<T>()
|
||||
where T : class, IResource;
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle libraries.
|
||||
/// </summary>
|
||||
ILibraryRepository LibraryRepository { get; }
|
||||
IRepository<T> Repository<T>()
|
||||
where T : IResource, IQuery;
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle libraries items (a wrapper around shows and collections).
|
||||
/// </summary>
|
||||
ILibraryItemRepository LibraryItemRepository { get; }
|
||||
IRepository<ILibraryItem> LibraryItems { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle new items.
|
||||
/// </summary>
|
||||
IRepository<INews> News { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle watched items.
|
||||
/// </summary>
|
||||
IWatchStatusRepository WatchStatus { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle collections.
|
||||
/// </summary>
|
||||
ICollectionRepository CollectionRepository { get; }
|
||||
IRepository<Collection> Collections { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle shows.
|
||||
/// </summary>
|
||||
IShowRepository ShowRepository { get; }
|
||||
IRepository<Movie> Movies { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle shows.
|
||||
/// </summary>
|
||||
IRepository<Show> Shows { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle seasons.
|
||||
/// </summary>
|
||||
ISeasonRepository SeasonRepository { get; }
|
||||
IRepository<Season> Seasons { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle episodes.
|
||||
/// </summary>
|
||||
IEpisodeRepository EpisodeRepository { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle tracks.
|
||||
/// </summary>
|
||||
ITrackRepository TrackRepository { get; }
|
||||
IRepository<Episode> Episodes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle people.
|
||||
/// </summary>
|
||||
IPeopleRepository PeopleRepository { get; }
|
||||
IRepository<People> People { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle studios.
|
||||
/// </summary>
|
||||
IStudioRepository StudioRepository { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle genres.
|
||||
/// </summary>
|
||||
IGenreRepository GenreRepository { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle providers.
|
||||
/// </summary>
|
||||
IProviderRepository ProviderRepository { get; }
|
||||
IRepository<Studio> Studios { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The repository that handle users.
|
||||
/// </summary>
|
||||
IUserRepository UserRepository { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the resource by it's ID
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the resource</param>
|
||||
/// <typeparam name="T">The type of the resource</typeparam>
|
||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||
/// <returns>The resource found</returns>
|
||||
[ItemNotNull]
|
||||
Task<T> Get<T>(int id)
|
||||
where T : class, IResource;
|
||||
|
||||
/// <summary>
|
||||
/// Get the resource by it's slug
|
||||
/// </summary>
|
||||
/// <param name="slug">The slug of the resource</param>
|
||||
/// <typeparam name="T">The type of the resource</typeparam>
|
||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||
/// <returns>The resource found</returns>
|
||||
[ItemNotNull]
|
||||
Task<T> Get<T>(string slug)
|
||||
where T : class, IResource;
|
||||
|
||||
/// <summary>
|
||||
/// Get the resource by a filter function.
|
||||
/// </summary>
|
||||
/// <param name="where">The filter function.</param>
|
||||
/// <typeparam name="T">The type of the resource</typeparam>
|
||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||
/// <returns>The first resource found that match the where function</returns>
|
||||
[ItemNotNull]
|
||||
Task<T> Get<T>(Expression<Func<T, bool>> where)
|
||||
where T : class, IResource;
|
||||
|
||||
/// <summary>
|
||||
/// Get a season from it's showID and it's seasonNumber
|
||||
/// </summary>
|
||||
/// <param name="showID">The id of the show</param>
|
||||
/// <param name="seasonNumber">The season's number</param>
|
||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||
/// <returns>The season found</returns>
|
||||
[ItemNotNull]
|
||||
Task<Season> Get(int showID, int seasonNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a season from it's show slug and it's seasonNumber
|
||||
/// </summary>
|
||||
/// <param name="showSlug">The slug of the show</param>
|
||||
/// <param name="seasonNumber">The season's number</param>
|
||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||
/// <returns>The season found</returns>
|
||||
[ItemNotNull]
|
||||
Task<Season> Get(string showSlug, int seasonNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a episode from it's showID, it's seasonNumber and it's episode number.
|
||||
/// </summary>
|
||||
/// <param name="showID">The id of the show</param>
|
||||
/// <param name="seasonNumber">The season's number</param>
|
||||
/// <param name="episodeNumber">The episode's number</param>
|
||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||
/// <returns>The episode found</returns>
|
||||
[ItemNotNull]
|
||||
Task<Episode> Get(int showID, int seasonNumber, int episodeNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a episode from it's show slug, it's seasonNumber and it's episode number.
|
||||
/// </summary>
|
||||
/// <param name="showSlug">The slug of the show</param>
|
||||
/// <param name="seasonNumber">The season's number</param>
|
||||
/// <param name="episodeNumber">The episode's number</param>
|
||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||
/// <returns>The episode found</returns>
|
||||
[ItemNotNull]
|
||||
Task<Episode> Get(string showSlug, int seasonNumber, int episodeNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get the resource by it's ID or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the resource</param>
|
||||
/// <typeparam name="T">The type of the resource</typeparam>
|
||||
/// <returns>The resource found</returns>
|
||||
[ItemCanBeNull]
|
||||
Task<T> GetOrDefault<T>(int id)
|
||||
where T : class, IResource;
|
||||
|
||||
/// <summary>
|
||||
/// Get the resource by it's slug or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="slug">The slug of the resource</param>
|
||||
/// <typeparam name="T">The type of the resource</typeparam>
|
||||
/// <returns>The resource found</returns>
|
||||
[ItemCanBeNull]
|
||||
Task<T> GetOrDefault<T>(string slug)
|
||||
where T : class, IResource;
|
||||
|
||||
/// <summary>
|
||||
/// Get the resource by a filter function or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="where">The filter function.</param>
|
||||
/// <param name="sortBy">A custom sort method to handle cases where multiples items match the filters.</param>
|
||||
/// <typeparam name="T">The type of the resource</typeparam>
|
||||
/// <returns>The first resource found that match the where function</returns>
|
||||
[ItemCanBeNull]
|
||||
Task<T> GetOrDefault<T>(Expression<Func<T, bool>> where, Sort<T> sortBy = default)
|
||||
where T : class, IResource;
|
||||
|
||||
/// <summary>
|
||||
/// Get a season from it's showID and it's seasonNumber or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="showID">The id of the show</param>
|
||||
/// <param name="seasonNumber">The season's number</param>
|
||||
/// <returns>The season found</returns>
|
||||
[ItemCanBeNull]
|
||||
Task<Season> GetOrDefault(int showID, int seasonNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a season from it's show slug and it's seasonNumber or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="showSlug">The slug of the show</param>
|
||||
/// <param name="seasonNumber">The season's number</param>
|
||||
/// <returns>The season found</returns>
|
||||
[ItemCanBeNull]
|
||||
Task<Season> GetOrDefault(string showSlug, int seasonNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a episode from it's showID, it's seasonNumber and it's episode number or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="showID">The id of the show</param>
|
||||
/// <param name="seasonNumber">The season's number</param>
|
||||
/// <param name="episodeNumber">The episode's number</param>
|
||||
/// <returns>The episode found</returns>
|
||||
[ItemCanBeNull]
|
||||
Task<Episode> GetOrDefault(int showID, int seasonNumber, int episodeNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a episode from it's show slug, it's seasonNumber and it's episode number or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="showSlug">The slug of the show</param>
|
||||
/// <param name="seasonNumber">The season's number</param>
|
||||
/// <param name="episodeNumber">The episode's number</param>
|
||||
/// <returns>The episode found</returns>
|
||||
[ItemCanBeNull]
|
||||
Task<Episode> GetOrDefault(string showSlug, int seasonNumber, int episodeNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Load a related resource
|
||||
/// </summary>
|
||||
/// <param name="obj">The source object.</param>
|
||||
/// <param name="member">A getter function for the member to load</param>
|
||||
/// <param name="force">
|
||||
/// <c>true</c> if you want to load the relation even if it is not null, <c>false</c> otherwise.
|
||||
/// </param>
|
||||
/// <typeparam name="T">The type of the source object</typeparam>
|
||||
/// <typeparam name="T2">The related resource's type</typeparam>
|
||||
/// <returns>The param <paramref name="obj"/></returns>
|
||||
/// <seealso cref="Load{T,T2}(T, System.Linq.Expressions.Expression{System.Func{T,System.Collections.Generic.ICollection{T2}}}, bool)"/>
|
||||
/// <seealso cref="Load{T}(T, string, bool)"/>
|
||||
/// <seealso cref="Load(IResource, string, bool)"/>
|
||||
Task<T> Load<T, T2>([NotNull] T obj, Expression<Func<T, T2>> member, bool force = false)
|
||||
where T : class, IResource
|
||||
where T2 : class, IResource;
|
||||
|
||||
/// <summary>
|
||||
/// Load a collection of related resource
|
||||
/// </summary>
|
||||
/// <param name="obj">The source object.</param>
|
||||
/// <param name="member">A getter function for the member to load</param>
|
||||
/// <param name="force">
|
||||
/// <c>true</c> if you want to load the relation even if it is not null, <c>false</c> otherwise.
|
||||
/// </param>
|
||||
/// <typeparam name="T">The type of the source object</typeparam>
|
||||
/// <typeparam name="T2">The related resource's type</typeparam>
|
||||
/// <returns>The param <paramref name="obj"/></returns>
|
||||
/// <seealso cref="Load{T,T2}(T, System.Linq.Expressions.Expression{System.Func{T,T2}}, bool)"/>
|
||||
/// <seealso cref="Load{T}(T, string, bool)"/>
|
||||
/// <seealso cref="Load(IResource, string, bool)"/>
|
||||
Task<T> Load<T, T2>([NotNull] T obj, Expression<Func<T, ICollection<T2>>> member, bool force = false)
|
||||
where T : class, IResource
|
||||
where T2 : class;
|
||||
|
||||
/// <summary>
|
||||
/// Load a related resource by it's name
|
||||
/// </summary>
|
||||
/// <param name="obj">The source object.</param>
|
||||
/// <param name="memberName">The name of the resource to load (case sensitive)</param>
|
||||
/// <param name="force">
|
||||
/// <c>true</c> if you want to load the relation even if it is not null, <c>false</c> otherwise.
|
||||
/// </param>
|
||||
/// <typeparam name="T">The type of the source object</typeparam>
|
||||
/// <returns>The param <paramref name="obj"/></returns>
|
||||
/// <seealso cref="Load{T,T2}(T, System.Linq.Expressions.Expression{System.Func{T,T2}}, bool)"/>
|
||||
/// <seealso cref="Load{T,T2}(T, System.Linq.Expressions.Expression{System.Func{T,System.Collections.Generic.ICollection{T2}}}, bool)"/>
|
||||
/// <seealso cref="Load(IResource, string, bool)"/>
|
||||
Task<T> Load<T>([NotNull] T obj, string memberName, bool force = false)
|
||||
where T : class, IResource;
|
||||
|
||||
/// <summary>
|
||||
/// Load a related resource without specifying it's type.
|
||||
/// </summary>
|
||||
/// <param name="obj">The source object.</param>
|
||||
/// <param name="memberName">The name of the resource to load (case sensitive)</param>
|
||||
/// <param name="force">
|
||||
/// <c>true</c> if you want to load the relation even if it is not null, <c>false</c> otherwise.
|
||||
/// </param>
|
||||
/// <seealso cref="Load{T,T2}(T, System.Linq.Expressions.Expression{System.Func{T,T2}}, bool)"/>
|
||||
/// <seealso cref="Load{T,T2}(T, System.Linq.Expressions.Expression{System.Func{T,System.Collections.Generic.ICollection{T2}}}, bool)"/>
|
||||
/// <seealso cref="Load{T}(T, string, bool)"/>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
Task Load([NotNull] IResource obj, string memberName, bool force = false);
|
||||
|
||||
/// <summary>
|
||||
/// Get items (A wrapper around shows or collections) from a library.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the library</param>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">Sort information (sort order and sort by)</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <exception cref="ItemNotFoundException">No library exist with the given ID.</exception>
|
||||
/// <returns>A list of items that match every filters</returns>
|
||||
Task<ICollection<LibraryItem>> GetItemsFromLibrary(int id,
|
||||
Expression<Func<LibraryItem, bool>> where = null,
|
||||
Sort<LibraryItem> sort = default,
|
||||
Pagination limit = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get items (A wrapper around shows or collections) from a library.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the library</param>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">A sort by method</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <exception cref="ItemNotFoundException">No library exist with the given ID.</exception>
|
||||
/// <returns>A list of items that match every filters</returns>
|
||||
Task<ICollection<LibraryItem>> GetItemsFromLibrary(int id,
|
||||
[Optional] Expression<Func<LibraryItem, bool>> where,
|
||||
Expression<Func<LibraryItem, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetItemsFromLibrary(id, where, new Sort<LibraryItem>(sort), limit);
|
||||
|
||||
/// <summary>
|
||||
/// Get items (A wrapper around shows or collections) from a library.
|
||||
/// </summary>
|
||||
/// <param name="slug">The slug of the library</param>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">Sort information (sort order and sort by)</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <exception cref="ItemNotFoundException">No library exist with the given slug.</exception>
|
||||
/// <returns>A list of items that match every filters</returns>
|
||||
Task<ICollection<LibraryItem>> GetItemsFromLibrary(string slug,
|
||||
Expression<Func<LibraryItem, bool>> where = null,
|
||||
Sort<LibraryItem> sort = default,
|
||||
Pagination limit = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get items (A wrapper around shows or collections) from a library.
|
||||
/// </summary>
|
||||
/// <param name="slug">The slug of the library</param>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">A sort by method</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <exception cref="ItemNotFoundException">No library exist with the given slug.</exception>
|
||||
/// <returns>A list of items that match every filters</returns>
|
||||
Task<ICollection<LibraryItem>> GetItemsFromLibrary(string slug,
|
||||
[Optional] Expression<Func<LibraryItem, bool>> where,
|
||||
Expression<Func<LibraryItem, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetItemsFromLibrary(slug, where, new Sort<LibraryItem>(sort), limit);
|
||||
|
||||
/// <summary>
|
||||
/// Get people's roles from a show.
|
||||
/// </summary>
|
||||
/// <param name="showID">The ID of the show</param>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">Sort information (sort order and sort by)</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <exception cref="ItemNotFoundException">No <see cref="Show"/> exist with the given ID.</exception>
|
||||
/// <returns>A list of items that match every filters</returns>
|
||||
Task<ICollection<PeopleRole>> GetPeopleFromShow(int showID,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get people's roles from a show.
|
||||
/// </summary>
|
||||
/// <param name="showID">The ID of the show</param>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">A sort by method</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <exception cref="ItemNotFoundException">No <see cref="Show"/> exist with the given ID.</exception>
|
||||
/// <returns>A list of items that match every filters</returns>
|
||||
Task<ICollection<PeopleRole>> GetPeopleFromShow(int showID,
|
||||
[Optional] Expression<Func<PeopleRole, bool>> where,
|
||||
Expression<Func<PeopleRole, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetPeopleFromShow(showID, where, new Sort<PeopleRole>(sort), limit);
|
||||
|
||||
/// <summary>
|
||||
/// Get people's roles from a show.
|
||||
/// </summary>
|
||||
/// <param name="showSlug">The slug of the show</param>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">Sort information (sort order and sort by)</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <exception cref="ItemNotFoundException">No <see cref="Show"/> exist with the given slug.</exception>
|
||||
/// <returns>A list of items that match every filters</returns>
|
||||
Task<ICollection<PeopleRole>> GetPeopleFromShow(string showSlug,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get people's roles from a show.
|
||||
/// </summary>
|
||||
/// <param name="showSlug">The slug of the show</param>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">A sort by method</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <exception cref="ItemNotFoundException">No <see cref="Show"/> exist with the given slug.</exception>
|
||||
/// <returns>A list of items that match every filters</returns>
|
||||
Task<ICollection<PeopleRole>> GetPeopleFromShow(string showSlug,
|
||||
[Optional] Expression<Func<PeopleRole, bool>> where,
|
||||
Expression<Func<PeopleRole, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetPeopleFromShow(showSlug, where, new Sort<PeopleRole>(sort), limit);
|
||||
|
||||
/// <summary>
|
||||
/// Get people's roles from a person.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the person</param>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">Sort information (sort order and sort by)</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <exception cref="ItemNotFoundException">No <see cref="People"/> exist with the given ID.</exception>
|
||||
/// <returns>A list of items that match every filters</returns>
|
||||
Task<ICollection<PeopleRole>> GetRolesFromPeople(int id,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get people's roles from a person.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the person</param>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">A sort by method</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <exception cref="ItemNotFoundException">No <see cref="People"/> exist with the given ID.</exception>
|
||||
/// <returns>A list of items that match every filters</returns>
|
||||
Task<ICollection<PeopleRole>> GetRolesFromPeople(int id,
|
||||
[Optional] Expression<Func<PeopleRole, bool>> where,
|
||||
Expression<Func<PeopleRole, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetRolesFromPeople(id, where, new Sort<PeopleRole>(sort), limit);
|
||||
|
||||
/// <summary>
|
||||
/// Get people's roles from a person.
|
||||
/// </summary>
|
||||
/// <param name="slug">The slug of the person</param>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">Sort information (sort order and sort by)</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <exception cref="ItemNotFoundException">No <see cref="People"/> exist with the given slug.</exception>
|
||||
/// <returns>A list of items that match every filters</returns>
|
||||
Task<ICollection<PeopleRole>> GetRolesFromPeople(string slug,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get people's roles from a person.
|
||||
/// </summary>
|
||||
/// <param name="slug">The slug of the person</param>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">A sort by method</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <exception cref="ItemNotFoundException">No <see cref="People"/> exist with the given slug.</exception>
|
||||
/// <returns>A list of items that match every filters</returns>
|
||||
Task<ICollection<PeopleRole>> GetRolesFromPeople(string slug,
|
||||
[Optional] Expression<Func<PeopleRole, bool>> where,
|
||||
Expression<Func<PeopleRole, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetRolesFromPeople(slug, where, new Sort<PeopleRole>(sort), limit);
|
||||
|
||||
/// <summary>
|
||||
/// Setup relations between a show, a library and a collection
|
||||
/// </summary>
|
||||
/// <param name="showID">The show's ID to setup relations with</param>
|
||||
/// <param name="libraryID">The library's ID to setup relations with (optional)</param>
|
||||
/// <param name="collectionID">The collection's ID to setup relations with (optional)</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
Task AddShowLink(int showID, int? libraryID, int? collectionID);
|
||||
|
||||
/// <summary>
|
||||
/// Setup relations between a show, a library and a collection
|
||||
/// </summary>
|
||||
/// <param name="show">The show to setup relations with</param>
|
||||
/// <param name="library">The library to setup relations with (optional)</param>
|
||||
/// <param name="collection">The collection to setup relations with (optional)</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
Task AddShowLink([NotNull] Show show, Library library, Collection collection);
|
||||
|
||||
/// <summary>
|
||||
/// Get all resources with filters
|
||||
/// </summary>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">Sort information (sort order and sort by)</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <typeparam name="T">The type of resources to load</typeparam>
|
||||
/// <returns>A list of resources that match every filters</returns>
|
||||
Task<ICollection<T>> GetAll<T>(Expression<Func<T, bool>> where = null,
|
||||
Sort<T> sort = default,
|
||||
Pagination limit = default)
|
||||
where T : class, IResource;
|
||||
|
||||
/// <summary>
|
||||
/// Get all resources with filters
|
||||
/// </summary>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">A sort by function</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <typeparam name="T">The type of resources to load</typeparam>
|
||||
/// <returns>A list of resources that match every filters</returns>
|
||||
Task<ICollection<T>> GetAll<T>([Optional] Expression<Func<T, bool>> where,
|
||||
Expression<Func<T, object>> sort,
|
||||
Pagination limit = default)
|
||||
where T : class, IResource
|
||||
{
|
||||
return GetAll(where, new Sort<T>(sort), limit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the count of resources that match the filter
|
||||
/// </summary>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <typeparam name="T">The type of resources to load</typeparam>
|
||||
/// <returns>A list of resources that match every filters</returns>
|
||||
Task<int> GetCount<T>(Expression<Func<T, bool>> where = null)
|
||||
where T : class, IResource;
|
||||
|
||||
/// <summary>
|
||||
/// Search for a resource
|
||||
/// </summary>
|
||||
/// <param name="query">The search query</param>
|
||||
/// <typeparam name="T">The type of resources</typeparam>
|
||||
/// <returns>A list of 20 items that match the search query</returns>
|
||||
Task<ICollection<T>> Search<T>(string query)
|
||||
where T : class, IResource;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new resource.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to register</param>
|
||||
/// <typeparam name="T">The type of resource</typeparam>
|
||||
/// <returns>The resource registers and completed by database's information (related items and so on)</returns>
|
||||
Task<T> Create<T>([NotNull] T item)
|
||||
where T : class, IResource;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new resource if it does not exist already. If it does, the existing value is returned instead.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to register</param>
|
||||
/// <typeparam name="T">The type of resource</typeparam>
|
||||
/// <returns>The newly created item or the existing value if it existed.</returns>
|
||||
Task<T> CreateIfNotExists<T>([NotNull] T item)
|
||||
where T : class, IResource;
|
||||
|
||||
/// <summary>
|
||||
/// Edit a resource
|
||||
/// </summary>
|
||||
/// <param name="item">The resource to edit, it's ID can't change.</param>
|
||||
/// <param name="resetOld">Should old properties of the resource be discarded or should null values considered as not changed?</param>
|
||||
/// <typeparam name="T">The type of resources</typeparam>
|
||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||
/// <returns>The resource edited and completed by database's information (related items and so on)</returns>
|
||||
Task<T> Edit<T>(T item, bool resetOld)
|
||||
where T : class, IResource;
|
||||
|
||||
/// <summary>
|
||||
/// Delete a resource.
|
||||
/// </summary>
|
||||
/// <param name="item">The resource to delete</param>
|
||||
/// <typeparam name="T">The type of resource to delete</typeparam>
|
||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
Task Delete<T>(T item)
|
||||
where T : class, IResource;
|
||||
|
||||
/// <summary>
|
||||
/// Delete a resource by it's ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the resource to delete</param>
|
||||
/// <typeparam name="T">The type of resource to delete</typeparam>
|
||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
Task Delete<T>(int id)
|
||||
where T : class, IResource;
|
||||
|
||||
/// <summary>
|
||||
/// Delete a resource by it's slug.
|
||||
/// </summary>
|
||||
/// <param name="slug">The slug of the resource to delete</param>
|
||||
/// <typeparam name="T">The type of resource to delete</typeparam>
|
||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
Task Delete<T>(string slug)
|
||||
where T : class, IResource;
|
||||
IRepository<User> Users { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Kyoo.Abstractions.Models;
|
||||
|
||||
namespace Kyoo.Abstractions.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface to automatically retrieve metadata from external providers.
|
||||
/// </summary>
|
||||
public interface IMetadataProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="Provider"/> corresponding to this provider.
|
||||
/// This allow to map metadata to a provider, keep metadata links and
|
||||
/// know witch <see cref="IMetadataProvider"/> is used for a specific <see cref="Library"/>.
|
||||
/// </summary>
|
||||
Provider Provider { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Return a new item with metadata from your provider.
|
||||
/// </summary>
|
||||
/// <param name="item">
|
||||
/// The item to retrieve metadata from. Most of the time, only the name will be available but other
|
||||
/// properties may be filed by other providers before a call to this method. This can allow you to identify
|
||||
/// the collection on your provider.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// You must not use metadata from the given <paramref name="item"/>.
|
||||
/// Merging metadata is the job of Kyoo, a complex <typeparamref name="T"/> is given
|
||||
/// to make a precise search and give you every available properties, not to discard properties.
|
||||
/// </remarks>
|
||||
/// <typeparam name="T">The type of resource to retrieve metadata for.</typeparam>
|
||||
/// <returns>A new <typeparamref name="T"/> containing metadata from your provider or null</returns>
|
||||
[ItemCanBeNull]
|
||||
Task<T> Get<T>([NotNull] T item)
|
||||
where T : class, IResource;
|
||||
|
||||
/// <summary>
|
||||
/// Search for a specific type of items with a given query.
|
||||
/// </summary>
|
||||
/// <param name="query">The search query to use.</param>
|
||||
/// <typeparam name="T">The type of resource to search metadata for.</typeparam>
|
||||
/// <returns>The list of items that could be found on this specific provider.</returns>
|
||||
[ItemNotNull]
|
||||
Task<ICollection<T>> Search<T>(string query)
|
||||
where T : class, IResource;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A special <see cref="IMetadataProvider"/> that merge results.
|
||||
/// This interface exists to specify witch provider to use but it can be used like any other metadata provider.
|
||||
/// </summary>
|
||||
public abstract class AProviderComposite : IMetadataProvider
|
||||
{
|
||||
/// <inheritdoc />
|
||||
[ItemNotNull]
|
||||
public abstract Task<T> Get<T>(T item)
|
||||
where T : class, IResource;
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract Task<ICollection<T>> Search<T>(string query)
|
||||
where T : class, IResource;
|
||||
|
||||
/// <summary>
|
||||
/// Since this is a composite and not a real provider, no metadata is available.
|
||||
/// It is not meant to be stored or selected. This class will handle merge based on what is required.
|
||||
/// </summary>
|
||||
public Provider Provider => null;
|
||||
|
||||
/// <summary>
|
||||
/// Select witch providers to use.
|
||||
/// The <see cref="IMetadataProvider"/> associated with the given <see cref="Provider"/> will be used.
|
||||
/// </summary>
|
||||
/// <param name="providers">The list of providers to use</param>
|
||||
public abstract void UseProviders(IEnumerable<Provider> providers);
|
||||
}
|
||||
}
|
||||
@@ -34,53 +34,16 @@ namespace Kyoo.Abstractions.Controllers
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
|
||||
public interface IPlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// A slug to identify this plugin in queries.
|
||||
/// </summary>
|
||||
string Slug { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the plugin
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The description of this plugin. This will be displayed on the "installed plugins" page.
|
||||
/// </summary>
|
||||
string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <c>true</c> if the plugin should be enabled, <c>false</c> otherwise.
|
||||
/// If a plugin is not enabled, no configure method will be called.
|
||||
/// This allow one to enable a plugin if a specific configuration value is set or if the environment contains
|
||||
/// the right settings.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// By default, a plugin is always enabled. This method can be overriden to change this behavior.
|
||||
/// </remarks>
|
||||
virtual bool Enabled => true;
|
||||
|
||||
/// <summary>
|
||||
/// A list of types that will be available via the IOptions interfaces and will be listed inside
|
||||
/// an IConfiguration.
|
||||
///
|
||||
/// If a field should be loosely typed, <see cref="Dictionary{TKey,TValue}"/> or <c>null</c>
|
||||
/// can be specified.
|
||||
/// WARNING: null means an unmanaged type that won't be editable. This can be used
|
||||
/// for external libraries or variable arguments.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// All use of the configuration must be specified here and not registered elsewhere, if a type is registered
|
||||
/// elsewhere the configuration won't be editable via the <see cref="IConfigurationManager"/> and all values
|
||||
/// will be discarded on edit.
|
||||
/// </remarks>
|
||||
Dictionary<string, Type> Configuration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// An optional configuration step to allow a plugin to change asp net configurations.
|
||||
/// </summary>
|
||||
/// <seealso cref="SA"/>
|
||||
virtual IEnumerable<IStartupAction> ConfigureSteps => ArraySegment<IStartupAction>.Empty;
|
||||
IEnumerable<IStartupAction> ConfigureSteps => ArraySegment<IStartupAction>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// A configure method that will be run on plugin's startup.
|
||||
@@ -101,15 +64,5 @@ namespace Kyoo.Abstractions.Controllers
|
||||
{
|
||||
// Skipped
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An optional function to execute and initialize your plugin.
|
||||
/// It can be used to initialize a database connection, fill initial data or anything.
|
||||
/// </summary>
|
||||
/// <param name="provider">A service provider to request services</param>
|
||||
void Initialize(IServiceProvider provider)
|
||||
{
|
||||
// Skipped
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,10 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Kyoo.Abstractions.Models;
|
||||
using Kyoo.Abstractions.Models.Exceptions;
|
||||
using Kyoo.Abstractions.Models.Utils;
|
||||
|
||||
namespace Kyoo.Abstractions.Controllers
|
||||
{
|
||||
@@ -32,125 +30,181 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The resource's type that this repository manage.</typeparam>
|
||||
public interface IRepository<T> : IBaseRepository
|
||||
where T : class, IResource
|
||||
where T : IResource, IQuery
|
||||
{
|
||||
/// <summary>
|
||||
/// The event handler type for all events of this repository.
|
||||
/// </summary>
|
||||
/// <param name="resource">The resource created/modified/deleted</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
public delegate Task ResourceEventHandler(T resource);
|
||||
|
||||
/// <summary>
|
||||
/// Get a resource from it's ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the resource</param>
|
||||
/// <param name="include">The related fields to include.</param>
|
||||
/// <exception cref="ItemNotFoundException">If the item could not be found.</exception>
|
||||
/// <returns>The resource found</returns>
|
||||
[ItemNotNull]
|
||||
Task<T> Get(int id);
|
||||
Task<T> Get(Guid id, Include<T>? include = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get a resource from it's slug.
|
||||
/// </summary>
|
||||
/// <param name="slug">The slug of the resource</param>
|
||||
/// <param name="include">The related fields to include.</param>
|
||||
/// <exception cref="ItemNotFoundException">If the item could not be found.</exception>
|
||||
/// <returns>The resource found</returns>
|
||||
[ItemNotNull]
|
||||
Task<T> Get(string slug);
|
||||
Task<T> Get(string slug, Include<T>? include = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get the first resource that match the predicate.
|
||||
/// </summary>
|
||||
/// <param name="where">A predicate to filter the resource.</param>
|
||||
/// <param name="filter">A predicate to filter the resource.</param>
|
||||
/// <param name="include">The related fields to include.</param>
|
||||
/// <param name="sortBy">A custom sort method to handle cases where multiples items match the filters.</param>
|
||||
/// <param name="reverse">Reverse the sort.</param>
|
||||
/// <param name="afterId">Select the first element after this id if it was in a list.</param>
|
||||
/// <exception cref="ItemNotFoundException">If the item could not be found.</exception>
|
||||
/// <returns>The resource found</returns>
|
||||
[ItemNotNull]
|
||||
Task<T> Get(Expression<Func<T, bool>> where);
|
||||
Task<T> Get(
|
||||
Filter<T> filter,
|
||||
Include<T>? include = default,
|
||||
Sort<T>? sortBy = default,
|
||||
bool reverse = false,
|
||||
Guid? afterId = default
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Get a resource from it's ID or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the resource</param>
|
||||
/// <param name="include">The related fields to include.</param>
|
||||
/// <returns>The resource found</returns>
|
||||
[ItemCanBeNull]
|
||||
Task<T> GetOrDefault(int id);
|
||||
Task<T?> GetOrDefault(Guid id, Include<T>? include = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get a resource from it's slug or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="slug">The slug of the resource</param>
|
||||
/// <param name="include">The related fields to include.</param>
|
||||
/// <returns>The resource found</returns>
|
||||
[ItemCanBeNull]
|
||||
Task<T> GetOrDefault(string slug);
|
||||
Task<T?> GetOrDefault(string slug, Include<T>? include = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get the first resource that match the predicate or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="where">A predicate to filter the resource.</param>
|
||||
/// <param name="filter">A predicate to filter the resource.</param>
|
||||
/// <param name="include">The related fields to include.</param>
|
||||
/// <param name="sortBy">A custom sort method to handle cases where multiples items match the filters.</param>
|
||||
/// <param name="reverse">Reverse the sort.</param>
|
||||
/// <param name="afterId">Select the first element after this id if it was in a list.</param>
|
||||
/// <returns>The resource found</returns>
|
||||
[ItemCanBeNull]
|
||||
Task<T> GetOrDefault(Expression<Func<T, bool>> where, Sort<T> sortBy = default);
|
||||
Task<T?> GetOrDefault(
|
||||
Filter<T>? filter,
|
||||
Include<T>? include = default,
|
||||
Sort<T>? sortBy = default,
|
||||
bool reverse = false,
|
||||
Guid? afterId = default
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Search for resources.
|
||||
/// Search for resources with the database.
|
||||
/// </summary>
|
||||
/// <param name="query">The query string.</param>
|
||||
/// <param name="include">The related fields to include.</param>
|
||||
/// <returns>A list of resources found</returns>
|
||||
[ItemNotNull]
|
||||
Task<ICollection<T>> Search(string query);
|
||||
Task<ICollection<T>> Search(string query, Include<T>? include = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get every resources that match all filters
|
||||
/// </summary>
|
||||
/// <param name="where">A filter predicate</param>
|
||||
/// <param name="filter">A filter predicate</param>
|
||||
/// <param name="sort">Sort information about the query (sort by, sort order)</param>
|
||||
/// <param name="include">The related fields to include.</param>
|
||||
/// <param name="limit">How pagination should be done (where to start and how many to return)</param>
|
||||
/// <returns>A list of resources that match every filters</returns>
|
||||
[ItemNotNull]
|
||||
Task<ICollection<T>> GetAll(Expression<Func<T, bool>> where = null,
|
||||
Sort<T> sort = default,
|
||||
Pagination limit = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get every resources that match all filters
|
||||
/// </summary>
|
||||
/// <param name="where">A filter predicate</param>
|
||||
/// <param name="sort">A sort by predicate. The order is ascending.</param>
|
||||
/// <param name="limit">How pagination should be done (where to start and how many to return)</param>
|
||||
/// <returns>A list of resources that match every filters</returns>
|
||||
[ItemNotNull]
|
||||
Task<ICollection<T>> GetAll([Optional] Expression<Func<T, bool>> where,
|
||||
Expression<Func<T, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetAll(where, new Sort<T>(sort), limit);
|
||||
Task<ICollection<T>> GetAll(
|
||||
Filter<T>? filter = null,
|
||||
Sort<T>? sort = default,
|
||||
Include<T>? include = default,
|
||||
Pagination? limit = default
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Get the number of resources that match the filter's predicate.
|
||||
/// </summary>
|
||||
/// <param name="where">A filter predicate</param>
|
||||
/// <param name="filter">A filter predicate</param>
|
||||
/// <returns>How many resources matched that filter</returns>
|
||||
Task<int> GetCount(Expression<Func<T, bool>> where = null);
|
||||
Task<int> GetCount(Filter<T>? filter = null);
|
||||
|
||||
/// <summary>
|
||||
/// Map a list of ids to a list of items (keep the order).
|
||||
/// </summary>
|
||||
/// <param name="ids">The list of items id.</param>
|
||||
/// <param name="include">The related fields to include.</param>
|
||||
/// <returns>A list of resources mapped from ids.</returns>
|
||||
Task<ICollection<T>> FromIds(IList<Guid> ids, Include<T>? include = default);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new resource.
|
||||
/// </summary>
|
||||
/// <param name="obj">The item to register</param>
|
||||
/// <returns>The resource registers and completed by database's information (related items and so on)</returns>
|
||||
[ItemNotNull]
|
||||
Task<T> Create([NotNull] T obj);
|
||||
Task<T> Create(T obj);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new resource if it does not exist already. If it does, the existing value is returned instead.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to create</param>
|
||||
/// <returns>The newly created item or the existing value if it existed.</returns>
|
||||
[ItemNotNull]
|
||||
Task<T> CreateIfNotExists([NotNull] T obj);
|
||||
Task<T> CreateIfNotExists(T obj);
|
||||
|
||||
/// <summary>
|
||||
/// Edit a resource
|
||||
/// Called when a resource has been created.
|
||||
/// </summary>
|
||||
static event ResourceEventHandler OnCreated;
|
||||
|
||||
/// <summary>
|
||||
/// Callback that should be called after a resource has been created.
|
||||
/// </summary>
|
||||
/// <param name="obj">The resource newly created.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
protected static Task OnResourceCreated(T obj) =>
|
||||
OnCreated?.Invoke(obj) ?? Task.CompletedTask;
|
||||
|
||||
/// <summary>
|
||||
/// Edit a resource and replace every property
|
||||
/// </summary>
|
||||
/// <param name="edited">The resource to edit, it's ID can't change.</param>
|
||||
/// <param name="resetOld">Should old properties of the resource be discarded or should null values considered as not changed?</param>
|
||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||
/// <returns>The resource edited and completed by database's information (related items and so on)</returns>
|
||||
[ItemNotNull]
|
||||
Task<T> Edit([NotNull] T edited, bool resetOld);
|
||||
Task<T> Edit(T edited);
|
||||
|
||||
/// <summary>
|
||||
/// Edit only specific properties of a resource
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the resource to edit</param>
|
||||
/// <param name="patch">
|
||||
/// A method that will be called when you need to update every properties that you want to
|
||||
/// persist. It can return false to abort the process via an ArgumentException
|
||||
/// </param>
|
||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||
/// <returns>The resource edited and completed by database's information (related items and so on)</returns>
|
||||
Task<T> Patch(Guid id, Func<T, Task<bool>> patch);
|
||||
|
||||
/// <summary>
|
||||
/// Called when a resource has been edited.
|
||||
/// </summary>
|
||||
static event ResourceEventHandler OnEdited;
|
||||
|
||||
/// <summary>
|
||||
/// Callback that should be called after a resource has been edited.
|
||||
/// </summary>
|
||||
/// <param name="obj">The resource newly edited.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
protected static Task OnResourceEdited(T obj) =>
|
||||
OnEdited?.Invoke(obj) ?? Task.CompletedTask;
|
||||
|
||||
/// <summary>
|
||||
/// Delete a resource by it's ID
|
||||
@@ -158,7 +212,7 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// <param name="id">The ID of the resource</param>
|
||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
Task Delete(int id);
|
||||
Task Delete(Guid id);
|
||||
|
||||
/// <summary>
|
||||
/// Delete a resource by it's slug
|
||||
@@ -174,14 +228,27 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// <param name="obj">The resource to delete</param>
|
||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
Task Delete([NotNull] T obj);
|
||||
Task Delete(T obj);
|
||||
|
||||
/// <summary>
|
||||
/// Delete all resources that match the predicate.
|
||||
/// </summary>
|
||||
/// <param name="where">A predicate to filter resources to delete. Every resource that match this will be deleted.</param>
|
||||
/// <param name="filter">A predicate to filter resources to delete. Every resource that match this will be deleted.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
Task DeleteAll([NotNull] Expression<Func<T, bool>> where);
|
||||
Task DeleteAll(Filter<T> filter);
|
||||
|
||||
/// <summary>
|
||||
/// Called when a resource has been edited.
|
||||
/// </summary>
|
||||
static event ResourceEventHandler OnDeleted;
|
||||
|
||||
/// <summary>
|
||||
/// Callback that should be called after a resource has been deleted.
|
||||
/// </summary>
|
||||
/// <param name="obj">The resource newly deleted.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
protected static Task OnResourceDeleted(T obj) =>
|
||||
OnDeleted?.Invoke(obj) ?? Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -194,382 +261,4 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// </summary>
|
||||
Type RepositoryType { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A repository to handle shows.
|
||||
/// </summary>
|
||||
public interface IShowRepository : IRepository<Show>
|
||||
{
|
||||
/// <summary>
|
||||
/// Link a show to a collection and/or a library. The given show is now part of those containers.
|
||||
/// If both a library and a collection are given, the collection is added to the library too.
|
||||
/// </summary>
|
||||
/// <param name="showID">The ID of the show</param>
|
||||
/// <param name="libraryID">The ID of the library (optional)</param>
|
||||
/// <param name="collectionID">The ID of the collection (optional)</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
Task AddShowLink(int showID, int? libraryID, int? collectionID);
|
||||
|
||||
/// <summary>
|
||||
/// Get a show's slug from it's ID.
|
||||
/// </summary>
|
||||
/// <param name="showID">The ID of the show</param>
|
||||
/// <exception cref="ItemNotFoundException">If a show with the given ID is not found.</exception>
|
||||
/// <returns>The show's slug</returns>
|
||||
Task<string> GetSlug(int showID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A repository to handle seasons.
|
||||
/// </summary>
|
||||
public interface ISeasonRepository : IRepository<Season>
|
||||
{
|
||||
/// <summary>
|
||||
/// Get a season from it's showID and it's seasonNumber
|
||||
/// </summary>
|
||||
/// <param name="showID">The id of the show</param>
|
||||
/// <param name="seasonNumber">The season's number</param>
|
||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||
/// <returns>The season found</returns>
|
||||
Task<Season> Get(int showID, int seasonNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a season from it's show slug and it's seasonNumber
|
||||
/// </summary>
|
||||
/// <param name="showSlug">The slug of the show</param>
|
||||
/// <param name="seasonNumber">The season's number</param>
|
||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||
/// <returns>The season found</returns>
|
||||
Task<Season> Get(string showSlug, int seasonNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a season from it's showID and it's seasonNumber or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="showID">The id of the show</param>
|
||||
/// <param name="seasonNumber">The season's number</param>
|
||||
/// <returns>The season found</returns>
|
||||
Task<Season> GetOrDefault(int showID, int seasonNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a season from it's show slug and it's seasonNumber or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="showSlug">The slug of the show</param>
|
||||
/// <param name="seasonNumber">The season's number</param>
|
||||
/// <returns>The season found</returns>
|
||||
Task<Season> GetOrDefault(string showSlug, int seasonNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The repository to handle episodes
|
||||
/// </summary>
|
||||
public interface IEpisodeRepository : IRepository<Episode>
|
||||
{
|
||||
// TODO replace the next methods with extension methods.
|
||||
|
||||
/// <summary>
|
||||
/// Get a episode from it's showID, it's seasonNumber and it's episode number.
|
||||
/// </summary>
|
||||
/// <param name="showID">The id of the show</param>
|
||||
/// <param name="seasonNumber">The season's number</param>
|
||||
/// <param name="episodeNumber">The episode's number</param>
|
||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||
/// <returns>The episode found</returns>
|
||||
Task<Episode> Get(int showID, int seasonNumber, int episodeNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a episode from it's show slug, it's seasonNumber and it's episode number.
|
||||
/// </summary>
|
||||
/// <param name="showSlug">The slug of the show</param>
|
||||
/// <param name="seasonNumber">The season's number</param>
|
||||
/// <param name="episodeNumber">The episode's number</param>
|
||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||
/// <returns>The episode found</returns>
|
||||
Task<Episode> Get(string showSlug, int seasonNumber, int episodeNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a episode from it's showID, it's seasonNumber and it's episode number or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="showID">The id of the show</param>
|
||||
/// <param name="seasonNumber">The season's number</param>
|
||||
/// <param name="episodeNumber">The episode's number</param>
|
||||
/// <returns>The episode found</returns>
|
||||
Task<Episode> GetOrDefault(int showID, int seasonNumber, int episodeNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a episode from it's show slug, it's seasonNumber and it's episode number or null if it is not found.
|
||||
/// </summary>
|
||||
/// <param name="showSlug">The slug of the show</param>
|
||||
/// <param name="seasonNumber">The season's number</param>
|
||||
/// <param name="episodeNumber">The episode's number</param>
|
||||
/// <returns>The episode found</returns>
|
||||
Task<Episode> GetOrDefault(string showSlug, int seasonNumber, int episodeNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a episode from it's showID and it's absolute number.
|
||||
/// </summary>
|
||||
/// <param name="showID">The id of the show</param>
|
||||
/// <param name="absoluteNumber">The episode's absolute number (The episode number does not reset to 1 after the end of a season.</param>
|
||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||
/// <returns>The episode found</returns>
|
||||
Task<Episode> GetAbsolute(int showID, int absoluteNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get a episode from it's showID and it's absolute number.
|
||||
/// </summary>
|
||||
/// <param name="showSlug">The slug of the show</param>
|
||||
/// <param name="absoluteNumber">The episode's absolute number (The episode number does not reset to 1 after the end of a season.</param>
|
||||
/// <exception cref="ItemNotFoundException">If the item is not found</exception>
|
||||
/// <returns>The episode found</returns>
|
||||
Task<Episode> GetAbsolute(string showSlug, int absoluteNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A repository to handle tracks
|
||||
/// </summary>
|
||||
public interface ITrackRepository : IRepository<Track> { }
|
||||
|
||||
/// <summary>
|
||||
/// A repository to handle libraries.
|
||||
/// </summary>
|
||||
public interface ILibraryRepository : IRepository<Library> { }
|
||||
|
||||
/// <summary>
|
||||
/// A repository to handle library items (A wrapper around shows and collections).
|
||||
/// </summary>
|
||||
public interface ILibraryItemRepository : IRepository<LibraryItem>
|
||||
{
|
||||
/// <summary>
|
||||
/// Get items (A wrapper around shows or collections) from a library.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the library</param>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">Sort information (sort order and sort by)</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <exception cref="ItemNotFoundException">No library exist with the given ID.</exception>
|
||||
/// <returns>A list of items that match every filters</returns>
|
||||
public Task<ICollection<LibraryItem>> GetFromLibrary(int id,
|
||||
Expression<Func<LibraryItem, bool>> where = null,
|
||||
Sort<LibraryItem> sort = default,
|
||||
Pagination limit = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get items (A wrapper around shows or collections) from a library.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the library</param>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">A sort by method</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <exception cref="ItemNotFoundException">No library exist with the given ID.</exception>
|
||||
/// <returns>A list of items that match every filters</returns>
|
||||
public Task<ICollection<LibraryItem>> GetFromLibrary(int id,
|
||||
[Optional] Expression<Func<LibraryItem, bool>> where,
|
||||
Expression<Func<LibraryItem, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetFromLibrary(id, where, new Sort<LibraryItem>(sort), limit);
|
||||
|
||||
/// <summary>
|
||||
/// Get items (A wrapper around shows or collections) from a library.
|
||||
/// </summary>
|
||||
/// <param name="slug">The slug of the library</param>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">Sort information (sort order and sort by)</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <exception cref="ItemNotFoundException">No library exist with the given slug.</exception>
|
||||
/// <returns>A list of items that match every filters</returns>
|
||||
public Task<ICollection<LibraryItem>> GetFromLibrary(string slug,
|
||||
Expression<Func<LibraryItem, bool>> where = null,
|
||||
Sort<LibraryItem> sort = default,
|
||||
Pagination limit = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get items (A wrapper around shows or collections) from a library.
|
||||
/// </summary>
|
||||
/// <param name="slug">The slug of the library</param>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">A sort by method</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <exception cref="ItemNotFoundException">No library exist with the given slug.</exception>
|
||||
/// <returns>A list of items that match every filters</returns>
|
||||
public Task<ICollection<LibraryItem>> GetFromLibrary(string slug,
|
||||
[Optional] Expression<Func<LibraryItem, bool>> where,
|
||||
Expression<Func<LibraryItem, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetFromLibrary(slug, where, new Sort<LibraryItem>(sort), limit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A repository for collections
|
||||
/// </summary>
|
||||
public interface ICollectionRepository : IRepository<Collection> { }
|
||||
|
||||
/// <summary>
|
||||
/// A repository for genres.
|
||||
/// </summary>
|
||||
public interface IGenreRepository : IRepository<Genre> { }
|
||||
|
||||
/// <summary>
|
||||
/// A repository for studios.
|
||||
/// </summary>
|
||||
public interface IStudioRepository : IRepository<Studio> { }
|
||||
|
||||
/// <summary>
|
||||
/// A repository for people.
|
||||
/// </summary>
|
||||
public interface IPeopleRepository : IRepository<People>
|
||||
{
|
||||
/// <summary>
|
||||
/// Get people's roles from a show.
|
||||
/// </summary>
|
||||
/// <param name="showID">The ID of the show</param>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">Sort information (sort order and sort by)</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <exception cref="ItemNotFoundException">No <see cref="Show"/> exist with the given ID.</exception>
|
||||
/// <returns>A list of items that match every filters</returns>
|
||||
Task<ICollection<PeopleRole>> GetFromShow(int showID,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get people's roles from a show.
|
||||
/// </summary>
|
||||
/// <param name="showID">The ID of the show</param>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">A sort by method</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <exception cref="ItemNotFoundException">No <see cref="Show"/> exist with the given ID.</exception>
|
||||
/// <returns>A list of items that match every filters</returns>
|
||||
Task<ICollection<PeopleRole>> GetFromShow(int showID,
|
||||
[Optional] Expression<Func<PeopleRole, bool>> where,
|
||||
Expression<Func<PeopleRole, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetFromShow(showID, where, new Sort<PeopleRole>(sort), limit);
|
||||
|
||||
/// <summary>
|
||||
/// Get people's roles from a show.
|
||||
/// </summary>
|
||||
/// <param name="showSlug">The slug of the show</param>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">Sort information (sort order and sort by)</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <exception cref="ItemNotFoundException">No <see cref="Show"/> exist with the given slug.</exception>
|
||||
/// <returns>A list of items that match every filters</returns>
|
||||
Task<ICollection<PeopleRole>> GetFromShow(string showSlug,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get people's roles from a show.
|
||||
/// </summary>
|
||||
/// <param name="showSlug">The slug of the show</param>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">A sort by method</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <exception cref="ItemNotFoundException">No <see cref="Show"/> exist with the given slug.</exception>
|
||||
/// <returns>A list of items that match every filters</returns>
|
||||
Task<ICollection<PeopleRole>> GetFromShow(string showSlug,
|
||||
[Optional] Expression<Func<PeopleRole, bool>> where,
|
||||
Expression<Func<PeopleRole, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetFromShow(showSlug, where, new Sort<PeopleRole>(sort), limit);
|
||||
|
||||
/// <summary>
|
||||
/// Get people's roles from a person.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the person</param>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">Sort information (sort order and sort by)</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <exception cref="ItemNotFoundException">No <see cref="People"/> exist with the given ID.</exception>
|
||||
/// <returns>A list of items that match every filters</returns>
|
||||
Task<ICollection<PeopleRole>> GetFromPeople(int id,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get people's roles from a person.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the person</param>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">A sort by method</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <exception cref="ItemNotFoundException">No <see cref="People"/> exist with the given ID.</exception>
|
||||
/// <returns>A list of items that match every filters</returns>
|
||||
Task<ICollection<PeopleRole>> GetFromPeople(int id,
|
||||
[Optional] Expression<Func<PeopleRole, bool>> where,
|
||||
Expression<Func<PeopleRole, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetFromPeople(id, where, new Sort<PeopleRole>(sort), limit);
|
||||
|
||||
/// <summary>
|
||||
/// Get people's roles from a person.
|
||||
/// </summary>
|
||||
/// <param name="slug">The slug of the person</param>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">Sort information (sort order and sort by)</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <exception cref="ItemNotFoundException">No <see cref="People"/> exist with the given slug.</exception>
|
||||
/// <returns>A list of items that match every filters</returns>
|
||||
Task<ICollection<PeopleRole>> GetFromPeople(string slug,
|
||||
Expression<Func<PeopleRole, bool>> where = null,
|
||||
Sort<PeopleRole> sort = default,
|
||||
Pagination limit = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get people's roles from a person.
|
||||
/// </summary>
|
||||
/// <param name="slug">The slug of the person</param>
|
||||
/// <param name="where">A filter function</param>
|
||||
/// <param name="sort">A sort by method</param>
|
||||
/// <param name="limit">How many items to return and where to start</param>
|
||||
/// <exception cref="ItemNotFoundException">No <see cref="People"/> exist with the given slug.</exception>
|
||||
/// <returns>A list of items that match every filters</returns>
|
||||
Task<ICollection<PeopleRole>> GetFromPeople(string slug,
|
||||
[Optional] Expression<Func<PeopleRole, bool>> where,
|
||||
Expression<Func<PeopleRole, object>> sort,
|
||||
Pagination limit = default
|
||||
) => GetFromPeople(slug, where, new Sort<PeopleRole>(sort), limit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A repository to handle providers.
|
||||
/// </summary>
|
||||
public interface IProviderRepository : IRepository<Provider>
|
||||
{
|
||||
/// <summary>
|
||||
/// Get a list of external ids that match all filters
|
||||
/// </summary>
|
||||
/// <param name="where">A predicate to add arbitrary filter</param>
|
||||
/// <param name="sort">Sort information (sort order and sort by)</param>
|
||||
/// <param name="limit">Pagination information (where to start and how many to get)</param>
|
||||
/// <typeparam name="T">The type of metadata to retrieve</typeparam>
|
||||
/// <returns>A filtered list of external ids.</returns>
|
||||
Task<ICollection<MetadataID>> GetMetadataID<T>(Expression<Func<MetadataID, bool>> where = null,
|
||||
Sort<MetadataID> sort = default,
|
||||
Pagination limit = default)
|
||||
where T : class, IMetadata;
|
||||
|
||||
/// <summary>
|
||||
/// Get a list of external ids that match all filters
|
||||
/// </summary>
|
||||
/// <param name="where">A predicate to add arbitrary filter</param>
|
||||
/// <param name="sort">A sort by expression</param>
|
||||
/// <param name="limit">Pagination information (where to start and how many to get)</param>
|
||||
/// <typeparam name="T">The type of metadata to retrieve</typeparam>
|
||||
/// <returns>A filtered list of external ids.</returns>
|
||||
Task<ICollection<MetadataID>> GetMetadataID<T>([Optional] Expression<Func<MetadataID, bool>> where,
|
||||
Expression<Func<MetadataID, object>> sort,
|
||||
Pagination limit = default
|
||||
)
|
||||
where T : class, IMetadata
|
||||
=> GetMetadataID<T>(where, new Sort<MetadataID>(sort), limit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A repository to handle users.
|
||||
/// </summary>
|
||||
public interface IUserRepository : IRepository<User> { }
|
||||
}
|
||||
|
||||
119
back/src/Kyoo.Abstractions/Controllers/ISearchManager.cs
Normal file
119
back/src/Kyoo.Abstractions/Controllers/ISearchManager.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Abstractions.Models;
|
||||
using Kyoo.Abstractions.Models.Utils;
|
||||
|
||||
namespace Kyoo.Abstractions.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// The service to search items.
|
||||
/// </summary>
|
||||
public interface ISearchManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Search for items.
|
||||
/// </summary>
|
||||
/// <param name="query">The seach query.</param>
|
||||
/// <param name="sortBy">Sort information about the query (sort by, sort order)</param>
|
||||
/// <param name="pagination">How pagination should be done (where to start and how many to return)</param>
|
||||
/// <param name="include">The related fields to include.</param>
|
||||
/// <returns>A list of resources that match every filters</returns>
|
||||
public Task<SearchPage<ILibraryItem>.SearchResult> SearchItems(
|
||||
string? query,
|
||||
Sort<ILibraryItem> sortBy,
|
||||
SearchPagination pagination,
|
||||
Include<ILibraryItem>? include = default
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Search for movies.
|
||||
/// </summary>
|
||||
/// <param name="query">The seach query.</param>
|
||||
/// <param name="sortBy">Sort information about the query (sort by, sort order)</param>
|
||||
/// <param name="pagination">How pagination should be done (where to start and how many to return)</param>
|
||||
/// <param name="include">The related fields to include.</param>
|
||||
/// <returns>A list of resources that match every filters</returns>
|
||||
public Task<SearchPage<Movie>.SearchResult> SearchMovies(
|
||||
string? query,
|
||||
Sort<Movie> sortBy,
|
||||
SearchPagination pagination,
|
||||
Include<Movie>? include = default
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Search for shows.
|
||||
/// </summary>
|
||||
/// <param name="query">The seach query.</param>
|
||||
/// <param name="sortBy">Sort information about the query (sort by, sort order)</param>
|
||||
/// <param name="pagination">How pagination should be done (where to start and how many to return)</param>
|
||||
/// <param name="include">The related fields to include.</param>
|
||||
/// <returns>A list of resources that match every filters</returns>
|
||||
public Task<SearchPage<Show>.SearchResult> SearchShows(
|
||||
string? query,
|
||||
Sort<Show> sortBy,
|
||||
SearchPagination pagination,
|
||||
Include<Show>? include = default
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Search for collections.
|
||||
/// </summary>
|
||||
/// <param name="query">The seach query.</param>
|
||||
/// <param name="sortBy">Sort information about the query (sort by, sort order)</param>
|
||||
/// <param name="pagination">How pagination should be done (where to start and how many to return)</param>
|
||||
/// <param name="include">The related fields to include.</param>
|
||||
/// <returns>A list of resources that match every filters</returns>
|
||||
public Task<SearchPage<Collection>.SearchResult> SearchCollections(
|
||||
string? query,
|
||||
Sort<Collection> sortBy,
|
||||
SearchPagination pagination,
|
||||
Include<Collection>? include = default
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Search for episodes.
|
||||
/// </summary>
|
||||
/// <param name="query">The seach query.</param>
|
||||
/// <param name="sortBy">Sort information about the query (sort by, sort order)</param>
|
||||
/// <param name="pagination">How pagination should be done (where to start and how many to return)</param>
|
||||
/// <param name="include">The related fields to include.</param>
|
||||
/// <returns>A list of resources that match every filters</returns>
|
||||
public Task<SearchPage<Episode>.SearchResult> SearchEpisodes(
|
||||
string? query,
|
||||
Sort<Episode> sortBy,
|
||||
SearchPagination pagination,
|
||||
Include<Episode>? include = default
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Search for studios.
|
||||
/// </summary>
|
||||
/// <param name="query">The seach query.</param>
|
||||
/// <param name="sortBy">Sort information about the query (sort by, sort order)</param>
|
||||
/// <param name="pagination">How pagination should be done (where to start and how many to return)</param>
|
||||
/// <param name="include">The related fields to include.</param>
|
||||
/// <returns>A list of resources that match every filters</returns>
|
||||
public Task<SearchPage<Studio>.SearchResult> SearchStudios(
|
||||
string? query,
|
||||
Sort<Studio> sortBy,
|
||||
SearchPagination pagination,
|
||||
Include<Studio>? include = default
|
||||
);
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Kyoo.Abstractions.Models;
|
||||
using Kyoo.Abstractions.Models.Exceptions;
|
||||
|
||||
namespace Kyoo.Abstractions.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// A common interface that tasks should implement.
|
||||
/// </summary>
|
||||
public interface ITask
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of parameters
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// All parameters that this task as. Every one of them will be given to the run function with a value.
|
||||
/// </returns>
|
||||
public TaskParameters GetParameters();
|
||||
|
||||
/// <summary>
|
||||
/// Start this task.
|
||||
/// </summary>
|
||||
/// <param name="arguments">
|
||||
/// The list of parameters.
|
||||
/// </param>
|
||||
/// <param name="progress">
|
||||
/// The progress reporter. Used to inform the sender the percentage of completion of this task
|
||||
/// .</param>
|
||||
/// <param name="cancellationToken">A token to request the task's cancellation.
|
||||
/// If this task is not cancelled quickly, it might be killed by the runner.
|
||||
/// </param>
|
||||
/// <exception cref="TaskFailedException">
|
||||
/// An exception meaning that the task has failed for handled reasons like invalid arguments,
|
||||
/// invalid environment, missing plugins or failures not related to a default in the code.
|
||||
/// This exception allow the task to display a failure message to the end user while others exceptions
|
||||
/// will be displayed as unhandled exceptions and display a stack trace.
|
||||
/// </exception>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
public Task Run([NotNull] TaskParameters arguments,
|
||||
[NotNull] IProgress<float> progress,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A single task parameter. This struct contains metadata to display and utility functions to get them in the task.
|
||||
/// </summary>
|
||||
/// <remarks>This struct will be used to generate the swagger documentation of the task.</remarks>
|
||||
public record TaskParameter
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of this parameter.
|
||||
/// </summary>
|
||||
public string Name { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The description of this parameter.
|
||||
/// </summary>
|
||||
public string Description { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of this parameter.
|
||||
/// </summary>
|
||||
public Type Type { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Is this parameter required or can it be ignored?
|
||||
/// </summary>
|
||||
public bool IsRequired { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The default value of this object.
|
||||
/// </summary>
|
||||
public object DefaultValue { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The value of the parameter.
|
||||
/// </summary>
|
||||
private object _Value { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new task parameter.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the parameter</param>
|
||||
/// <param name="description">The description of the parameter</param>
|
||||
/// <typeparam name="T">The type of the parameter.</typeparam>
|
||||
/// <returns>A new task parameter.</returns>
|
||||
public static TaskParameter Create<T>(string name, string description)
|
||||
{
|
||||
return new TaskParameter
|
||||
{
|
||||
Name = name,
|
||||
Description = description,
|
||||
Type = typeof(T)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new required task parameter.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the parameter</param>
|
||||
/// <param name="description">The description of the parameter</param>
|
||||
/// <typeparam name="T">The type of the parameter.</typeparam>
|
||||
/// <returns>A new task parameter.</returns>
|
||||
public static TaskParameter CreateRequired<T>(string name, string description)
|
||||
{
|
||||
return new TaskParameter
|
||||
{
|
||||
Name = name,
|
||||
Description = description,
|
||||
Type = typeof(T),
|
||||
IsRequired = true
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a parameter's value to give to a task.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the parameter</param>
|
||||
/// <param name="value">The value of the parameter. It's type will be used as parameter's type.</param>
|
||||
/// <typeparam name="T">The type of the parameter</typeparam>
|
||||
/// <returns>A TaskParameter that can be used as value.</returns>
|
||||
public static TaskParameter CreateValue<T>(string name, T value)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Name = name,
|
||||
Type = typeof(T),
|
||||
_Value = value
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a parameter's value for the current parameter.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to use</param>
|
||||
/// <returns>A new parameter's value for this current parameter</returns>
|
||||
public TaskParameter CreateValue(object value)
|
||||
{
|
||||
return this with { _Value = value };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the value of this parameter. If the value is of the wrong type, it will be converted.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of this parameter</typeparam>
|
||||
/// <returns>The value of this parameter.</returns>
|
||||
public T As<T>()
|
||||
{
|
||||
if (typeof(T) == typeof(object))
|
||||
return (T)_Value;
|
||||
|
||||
if (_Value is IResource resource)
|
||||
{
|
||||
if (typeof(T) == typeof(string))
|
||||
return (T)(object)resource.Slug;
|
||||
if (typeof(T) == typeof(int))
|
||||
return (T)(object)resource.ID;
|
||||
}
|
||||
|
||||
return (T)Convert.ChangeType(_Value, typeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A parameters container implementing an indexer to allow simple usage of parameters.
|
||||
/// </summary>
|
||||
public class TaskParameters : List<TaskParameter>
|
||||
{
|
||||
/// <summary>
|
||||
/// An indexer that return the parameter with the specified name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the task (case sensitive)</param>
|
||||
public TaskParameter this[string name] => this.FirstOrDefault(x => x.Name == name);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new, empty, <see cref="TaskParameters"/>
|
||||
/// </summary>
|
||||
public TaskParameters() { }
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="TaskParameters"/> with an initial parameters content.
|
||||
/// </summary>
|
||||
/// <param name="parameters">The list of parameters</param>
|
||||
public TaskParameters(IEnumerable<TaskParameter> parameters)
|
||||
{
|
||||
AddRange(parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
using Kyoo.Abstractions.Models.Exceptions;
|
||||
|
||||
namespace Kyoo.Abstractions.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// A service to handle long running tasks.
|
||||
/// </summary>
|
||||
/// <remarks>The concurrent number of running tasks is implementation dependent.</remarks>
|
||||
public interface ITaskManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Start a new task (or queue it).
|
||||
/// </summary>
|
||||
/// <param name="taskSlug">
|
||||
/// The slug of the task to run.
|
||||
/// </param>
|
||||
/// <param name="progress">
|
||||
/// A progress reporter to know the percentage of completion of the task.
|
||||
/// </param>
|
||||
/// <param name="arguments">
|
||||
/// A list of arguments to pass to the task. An automatic conversion will be made if arguments to not fit.
|
||||
/// </param>
|
||||
/// <param name="cancellationToken">
|
||||
/// A custom cancellation token for the task.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// If the number of arguments is invalid, if an argument can't be converted or if the task finds the argument
|
||||
/// invalid.
|
||||
/// </exception>
|
||||
/// <exception cref="ItemNotFoundException">
|
||||
/// The task could not be found.
|
||||
/// </exception>
|
||||
void StartTask(string taskSlug,
|
||||
[NotNull] IProgress<float> progress,
|
||||
Dictionary<string, object> arguments = null,
|
||||
CancellationToken? cancellationToken = null);
|
||||
|
||||
/// <summary>
|
||||
/// Start a new task (or queue it).
|
||||
/// </summary>
|
||||
/// <param name="progress">
|
||||
/// A progress reporter to know the percentage of completion of the task.
|
||||
/// </param>
|
||||
/// <param name="arguments">
|
||||
/// A list of arguments to pass to the task. An automatic conversion will be made if arguments to not fit.
|
||||
/// </param>
|
||||
/// <typeparam name="T">
|
||||
/// The type of the task to start.
|
||||
/// </typeparam>
|
||||
/// <param name="cancellationToken">
|
||||
/// A custom cancellation token for the task.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// If the number of arguments is invalid, if an argument can't be converted or if the task finds the argument
|
||||
/// invalid.
|
||||
/// </exception>
|
||||
/// <exception cref="ItemNotFoundException">
|
||||
/// The task could not be found.
|
||||
/// </exception>
|
||||
void StartTask<T>([NotNull] IProgress<float> progress,
|
||||
Dictionary<string, object> arguments = null,
|
||||
CancellationToken? cancellationToken = null)
|
||||
where T : ITask;
|
||||
|
||||
/// <summary>
|
||||
/// Get all currently running tasks
|
||||
/// </summary>
|
||||
/// <returns>A list of currently running tasks.</returns>
|
||||
ICollection<(TaskMetadataAttribute, ITask)> GetRunningTasks();
|
||||
|
||||
/// <summary>
|
||||
/// Get all available tasks
|
||||
/// </summary>
|
||||
/// <returns>A list of every tasks that this instance know.</returns>
|
||||
ICollection<TaskMetadataAttribute> GetAllTasks();
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,10 @@
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Kyoo.Abstractions.Models;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Kyoo.Abstractions.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
@@ -34,22 +35,31 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// <param name="item">
|
||||
/// The item to cache images.
|
||||
/// </param>
|
||||
/// <param name="alwaysDownload">
|
||||
/// <c>true</c> if images should be downloaded even if they already exists locally, <c>false</c> otherwise.
|
||||
/// </param>
|
||||
/// <typeparam name="T">The type of the item</typeparam>
|
||||
/// <returns><c>true</c> if an image has been downloaded, <c>false</c> otherwise.</returns>
|
||||
Task<bool> DownloadImages<T>([NotNull] T item, bool alwaysDownload = false)
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
Task DownloadImages<T>(T item)
|
||||
where T : IThumbnails;
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the local path of an image of the given item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to retrieve the poster from.</param>
|
||||
/// <param name="imageID">The ID of the image. See <see cref="Images"/> for values.</param>
|
||||
/// <param name="image">The ID of the image.</param>
|
||||
/// <param name="quality">The quality of the image</param>
|
||||
/// <typeparam name="T">The type of the item</typeparam>
|
||||
/// <returns>The path of the image for the given resource or null if it does not exists.</returns>
|
||||
Task<string> GetImagePath<T>([NotNull] T item, int imageID)
|
||||
string GetImagePath<T>(T item, string image, ImageQuality quality)
|
||||
where T : IThumbnails;
|
||||
|
||||
/// <summary>
|
||||
/// Delete images associated with the item.
|
||||
/// </summary>
|
||||
/// <param name="item">
|
||||
/// The item with cached images.
|
||||
/// </param>
|
||||
/// <typeparam name="T">The type of the item</typeparam>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
Task DeleteImages<T>(T item)
|
||||
where T : IThumbnails;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Kyoo.Abstractions.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Kyoo.Abstractions.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Transcoder responsible of handling low level video details.
|
||||
/// </summary>
|
||||
public interface ITranscoder
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieve tracks for a specific episode.
|
||||
/// Subtitles, chapters and fonts should also be extracted and cached when calling this method.
|
||||
/// </summary>
|
||||
/// <param name="episode">The episode to retrieve tracks for.</param>
|
||||
/// <param name="reExtract">Should the cache be invalidated and subtitles and others be re-extracted?</param>
|
||||
/// <returns>The list of tracks available for this episode.</returns>
|
||||
Task<ICollection<Track>> ExtractInfos(Episode episode, bool reExtract);
|
||||
|
||||
/// <summary>
|
||||
/// List fonts assosiated with this episode.
|
||||
/// </summary>
|
||||
/// <param name="episode">Th episode to list fonts for.</param>
|
||||
/// <returns>The list of attachements for this epiosode.</returns>
|
||||
Task<ICollection<Font>> ListFonts(Episode episode);
|
||||
|
||||
/// <summary>
|
||||
/// Get the specified font for this episode.
|
||||
/// </summary>
|
||||
/// <param name="episode">The episode to list fonts for.</param>
|
||||
/// <param name="slug">The slug of the specific font to retrive.</param>
|
||||
/// <returns>The <see cref="Font"/> with the given slug or null.</returns>
|
||||
[ItemCanBeNull] Task<Font> GetFont(Episode episode, string slug);
|
||||
|
||||
/// <summary>
|
||||
/// Transmux the selected episode to hls.
|
||||
/// </summary>
|
||||
/// <param name="episode">The episode to transmux.</param>
|
||||
/// <returns>The master file (m3u8) of the transmuxed hls file.</returns>
|
||||
IActionResult Transmux(Episode episode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Abstractions.Models;
|
||||
using Kyoo.Abstractions.Models.Utils;
|
||||
|
||||
namespace Kyoo.Abstractions.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// A local repository to handle watched items
|
||||
/// </summary>
|
||||
public interface IWatchStatusRepository
|
||||
{
|
||||
// /// <summary>
|
||||
// /// The event handler type for all events of this repository.
|
||||
// /// </summary>
|
||||
// /// <param name="resource">The resource created/modified/deleted</param>
|
||||
// /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
// public delegate Task ResourceEventHandler(T resource);
|
||||
|
||||
Task<ICollection<IWatchlist>> GetAll(
|
||||
Filter<IWatchlist>? filter = default,
|
||||
Include<IWatchlist>? include = default,
|
||||
Pagination? limit = default
|
||||
);
|
||||
|
||||
Task<MovieWatchStatus?> GetMovieStatus(Guid movieId, Guid userId);
|
||||
|
||||
Task<MovieWatchStatus?> SetMovieStatus(
|
||||
Guid movieId,
|
||||
Guid userId,
|
||||
WatchStatus status,
|
||||
int? watchedTime
|
||||
);
|
||||
|
||||
Task DeleteMovieStatus(Guid movieId, Guid userId);
|
||||
|
||||
Task<ShowWatchStatus?> GetShowStatus(Guid showId, Guid userId);
|
||||
|
||||
Task<ShowWatchStatus?> SetShowStatus(Guid showId, Guid userId, WatchStatus status);
|
||||
|
||||
Task DeleteShowStatus(Guid showId, Guid userId);
|
||||
|
||||
Task<EpisodeWatchStatus?> GetEpisodeStatus(Guid episodeId, Guid userId);
|
||||
|
||||
/// <param name="watchedTime">Where the user has stopped watching. Only usable if Status
|
||||
/// is <see cref="WatchStatus.Watching"/></param>
|
||||
Task<EpisodeWatchStatus?> SetEpisodeStatus(
|
||||
Guid episodeId,
|
||||
Guid userId,
|
||||
WatchStatus status,
|
||||
int? watchedTime
|
||||
);
|
||||
|
||||
Task DeleteEpisodeStatus(Guid episodeId, Guid userId);
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Kyoo.Abstractions.Controllers
|
||||
@@ -26,8 +25,6 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// A list of constant priorities used for <see cref="IStartupAction"/>'s <see cref="IStartupAction.Priority"/>.
|
||||
/// It also contains helper methods for creating new <see cref="StartupAction"/>.
|
||||
/// </summary>
|
||||
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name",
|
||||
Justification = "StartupAction is nested and the name SA is short to improve readability in plugin's startup.")]
|
||||
public static class SA
|
||||
{
|
||||
/// <summary>
|
||||
@@ -72,8 +69,7 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// <param name="action">The action to run</param>
|
||||
/// <param name="priority">The priority of the new action</param>
|
||||
/// <returns>A new <see cref="StartupAction"/></returns>
|
||||
public static StartupAction New(Action action, int priority)
|
||||
=> new(action, priority);
|
||||
public static StartupAction New(Action action, int priority) => new(action, priority);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="StartupAction"/>.
|
||||
@@ -83,7 +79,7 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// <typeparam name="T">A dependency that this action will use.</typeparam>
|
||||
/// <returns>A new <see cref="StartupAction"/></returns>
|
||||
public static StartupAction<T> New<T>(Action<T> action, int priority)
|
||||
=> new(action, priority);
|
||||
where T : notnull => new(action, priority);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="StartupAction"/>.
|
||||
@@ -94,7 +90,8 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// <typeparam name="T2">A second dependency that this action will use.</typeparam>
|
||||
/// <returns>A new <see cref="StartupAction"/></returns>
|
||||
public static StartupAction<T, T2> New<T, T2>(Action<T, T2> action, int priority)
|
||||
=> new(action, priority);
|
||||
where T : notnull
|
||||
where T2 : notnull => new(action, priority);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="StartupAction"/>.
|
||||
@@ -105,8 +102,13 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// <typeparam name="T2">A second dependency that this action will use.</typeparam>
|
||||
/// <typeparam name="T3">A third dependency that this action will use.</typeparam>
|
||||
/// <returns>A new <see cref="StartupAction"/></returns>
|
||||
public static StartupAction<T, T2, T3> New<T, T2, T3>(Action<T, T2, T3> action, int priority)
|
||||
=> new(action, priority);
|
||||
public static StartupAction<T, T2, T3> New<T, T2, T3>(
|
||||
Action<T, T2, T3> action,
|
||||
int priority
|
||||
)
|
||||
where T : notnull
|
||||
where T2 : notnull
|
||||
where T3 : notnull => new(action, priority);
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="IStartupAction"/> with no dependencies.
|
||||
@@ -144,6 +146,7 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The dependency to use.</typeparam>
|
||||
public class StartupAction<T> : IStartupAction
|
||||
where T : notnull
|
||||
{
|
||||
/// <summary>
|
||||
/// The action to execute at startup.
|
||||
@@ -177,6 +180,8 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// <typeparam name="T">The dependency to use.</typeparam>
|
||||
/// <typeparam name="T2">The second dependency to use.</typeparam>
|
||||
public class StartupAction<T, T2> : IStartupAction
|
||||
where T : notnull
|
||||
where T2 : notnull
|
||||
{
|
||||
/// <summary>
|
||||
/// The action to execute at startup.
|
||||
@@ -200,10 +205,7 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// <inheritdoc />
|
||||
public void Run(IServiceProvider provider)
|
||||
{
|
||||
_action.Invoke(
|
||||
provider.GetRequiredService<T>(),
|
||||
provider.GetRequiredService<T2>()
|
||||
);
|
||||
_action.Invoke(provider.GetRequiredService<T>(), provider.GetRequiredService<T2>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,6 +216,9 @@ namespace Kyoo.Abstractions.Controllers
|
||||
/// <typeparam name="T2">The second dependency to use.</typeparam>
|
||||
/// <typeparam name="T3">The third dependency to use.</typeparam>
|
||||
public class StartupAction<T, T2, T3> : IStartupAction
|
||||
where T : notnull
|
||||
where T2 : notnull
|
||||
where T3 : notnull
|
||||
{
|
||||
/// <summary>
|
||||
/// The action to execute at startup.
|
||||
|
||||
@@ -20,6 +20,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using Kyoo.Abstractions.Models.Exceptions;
|
||||
using Kyoo.Authentication.Models;
|
||||
|
||||
namespace Kyoo.Authentication
|
||||
@@ -39,5 +40,26 @@ namespace Kyoo.Authentication
|
||||
return user.Claims.FirstOrDefault(x => x.Type == Claims.Permissions)?.Value.Split(',')
|
||||
?? Array.Empty<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the id of the current user or null if unlogged or invalid.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>The id of the user or null.</returns>
|
||||
public static Guid? GetId(this ClaimsPrincipal user)
|
||||
{
|
||||
Claim? value = user.FindFirst(Claims.Id);
|
||||
if (Guid.TryParse(value?.Value, out Guid id))
|
||||
return id;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Guid GetIdOrThrow(this ClaimsPrincipal user)
|
||||
{
|
||||
Guid? ret = user.GetId();
|
||||
if (ret == null)
|
||||
throw new UnauthorizedException();
|
||||
return ret.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,14 +3,19 @@
|
||||
<Title>Kyoo.Abstractions</Title>
|
||||
<Description>Base package to create plugins for Kyoo.</Description>
|
||||
<RootNamespace>Kyoo.Abstractions</RootNamespace>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autofac" Version="6.2.0" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2021.2.0" />
|
||||
<PackageReference Include="Autofac" Version="7.1.0" />
|
||||
<PackageReference Include="Dapper" Version="2.1.24" />
|
||||
<PackageReference Include="EntityFrameworkCore.Projectables" Version="4.1.4-prebeta" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2023.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0" />
|
||||
<PackageReference Include="System.ComponentModel.Composition" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Sprache" Version="2.3.1" />
|
||||
<PackageReference Include="System.ComponentModel.Composition" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Kyoo.Abstractions.Models.Attributes
|
||||
{
|
||||
@@ -32,23 +31,21 @@ namespace Kyoo.Abstractions.Models.Attributes
|
||||
/// <summary>
|
||||
/// The public name of this api.
|
||||
/// </summary>
|
||||
[NotNull] public string Name { get; }
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the group in witch this API is. You can also specify a custom sort order using the following
|
||||
/// format: <code>order:name</code>. Everything before the first <c>:</c> will be removed but kept for
|
||||
/// th alphabetical ordering.
|
||||
/// </summary>
|
||||
public string Group { get; set; }
|
||||
public string? Group { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="ApiDefinitionAttribute"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the api that will be used on the documentation page.</param>
|
||||
public ApiDefinitionAttribute([NotNull] string name)
|
||||
public ApiDefinitionAttribute(string name)
|
||||
{
|
||||
if (name == null)
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
|
||||
namespace Kyoo.Abstractions.Models.Attributes
|
||||
{
|
||||
/// <summary>
|
||||
/// An attribute to inform how a <see cref="IFileSystem"/> works.
|
||||
/// </summary>
|
||||
[MetadataAttribute]
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class FileSystemMetadataAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The scheme(s) used to identify this path.
|
||||
/// It can be something like http, https, ftp, file and so on.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If multiples files with the same schemes exists, an exception will be thrown.
|
||||
/// </remarks>
|
||||
public string[] Scheme { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <c>true</c> if the scheme should be removed from the path before calling
|
||||
/// methods of this <see cref="IFileSystem"/>, <c>false</c> otherwise.
|
||||
/// </summary>
|
||||
public bool StripScheme { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="FileSystemMetadataAttribute"/> using the specified schemes.
|
||||
/// </summary>
|
||||
/// <param name="schemes">The schemes to use.</param>
|
||||
public FileSystemMetadataAttribute(string[] schemes)
|
||||
{
|
||||
Scheme = schemes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="FileSystemMetadataAttribute"/> using a dictionary of metadata.
|
||||
/// </summary>
|
||||
/// <param name="metadata">
|
||||
/// The dictionary of metadata. This method expect the dictionary to contain a field
|
||||
/// per property in this attribute, with the same types as the properties of this attribute.
|
||||
/// </param>
|
||||
public FileSystemMetadataAttribute(IDictionary<string, object> metadata)
|
||||
{
|
||||
Scheme = (string[])metadata[nameof(Scheme)];
|
||||
StripScheme = (bool)metadata[nameof(StripScheme)];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,12 +17,11 @@
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
|
||||
namespace Kyoo.Abstractions.Models.Attributes
|
||||
{
|
||||
/// <summary>
|
||||
/// The targeted relation can be loaded via a call to <see cref="ILibraryManager.Load"/>.
|
||||
/// The targeted relation can be loaded.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class LoadableRelationAttribute : Attribute
|
||||
@@ -30,7 +29,13 @@ namespace Kyoo.Abstractions.Models.Attributes
|
||||
/// <summary>
|
||||
/// The name of the field containing the related resource's ID.
|
||||
/// </summary>
|
||||
public string RelationID { get; }
|
||||
public string? RelationID { get; }
|
||||
|
||||
public string? Sql { get; set; }
|
||||
|
||||
public string? On { get; set; }
|
||||
|
||||
public string? Projected { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="LoadableRelationAttribute"/>.
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Kyoo.Abstractions.Models.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// An attribute to inform that this interface is a type union
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Interface)]
|
||||
public class OneOfAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The types this union concist of.
|
||||
/// </summary>
|
||||
public Type[] Types { get; set; }
|
||||
}
|
||||
@@ -32,17 +32,17 @@ namespace Kyoo.Abstractions.Models.Permissions
|
||||
/// <summary>
|
||||
/// The needed permission type.
|
||||
/// </summary>
|
||||
public string Type { get; }
|
||||
public string? Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The needed permission kind.
|
||||
/// </summary>
|
||||
public Kind Kind { get; }
|
||||
public Kind? Kind { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The group of this permission.
|
||||
/// </summary>
|
||||
public Group Group { get; set; }
|
||||
public Group? Group { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ask a permission to run an action.
|
||||
|
||||
@@ -16,26 +16,22 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace Kyoo.Core.Models.Options
|
||||
using System;
|
||||
using Kyoo.Utils;
|
||||
|
||||
namespace Kyoo.Abstractions.Models.Attributes;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class SqlFirstColumnAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for media registering.
|
||||
/// The name of the first column of the element. Used to split multiples
|
||||
/// items on a single sql query. If not specified, it defaults to "Id".
|
||||
/// </summary>
|
||||
public class MediaOptions
|
||||
public string Name { get; set; }
|
||||
|
||||
public SqlFirstColumnAttribute(string name)
|
||||
{
|
||||
/// <summary>
|
||||
/// The path of this options
|
||||
/// </summary>
|
||||
public const string Path = "Media";
|
||||
|
||||
/// <summary>
|
||||
/// A regex for episodes
|
||||
/// </summary>
|
||||
public string[] Regex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A regex for subtitles
|
||||
/// </summary>
|
||||
public string[] SubtitleRegex { get; set; }
|
||||
Name = name.ToSnakeCase();
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
|
||||
namespace Kyoo.Abstractions.Models.Attributes
|
||||
{
|
||||
/// <summary>
|
||||
/// An attribute to inform how a <see cref="IFileSystem"/> works.
|
||||
/// </summary>
|
||||
[MetadataAttribute]
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class TaskMetadataAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The slug of the task, used to start it.
|
||||
/// </summary>
|
||||
public string Slug { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the task that will be displayed to the user.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A quick description of what this task will do.
|
||||
/// </summary>
|
||||
public string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Should this task be automatically run at app startup?
|
||||
/// </summary>
|
||||
public bool RunOnStartup { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The priority of this task. Only used if <see cref="RunOnStartup"/> is true.
|
||||
/// It allow one to specify witch task will be started first as tasked are run on a Priority's descending order.
|
||||
/// </summary>
|
||||
public int Priority { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <c>true</c> if this task should not be displayed to the user, <c>false</c> otherwise.
|
||||
/// </summary>
|
||||
public bool IsHidden { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="TaskMetadataAttribute"/> with the given slug, name and description.
|
||||
/// </summary>
|
||||
/// <param name="slug">The slug of the task, used to start it.</param>
|
||||
/// <param name="name">The name of the task that will be displayed to the user.</param>
|
||||
/// <param name="description">A quick description of what this task will do.</param>
|
||||
public TaskMetadataAttribute(string slug, string name, string description)
|
||||
{
|
||||
Slug = slug;
|
||||
Name = name;
|
||||
Description = description;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="TaskMetadataAttribute"/> using a dictionary of metadata.
|
||||
/// </summary>
|
||||
/// <param name="metadata">
|
||||
/// The dictionary of metadata. This method expect the dictionary to contain a field
|
||||
/// per property in this attribute, with the same types as the properties of this attribute.
|
||||
/// </param>
|
||||
public TaskMetadataAttribute(IDictionary<string, object> metadata)
|
||||
{
|
||||
Slug = (string)metadata[nameof(Slug)];
|
||||
Name = (string)metadata[nameof(Name)];
|
||||
Description = (string)metadata[nameof(Description)];
|
||||
RunOnStartup = (bool)metadata[nameof(RunOnStartup)];
|
||||
Priority = (int)metadata[nameof(Priority)];
|
||||
IsHidden = (bool)metadata[nameof(IsHidden)];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A chapter to split an episode in multiple parts.
|
||||
/// </summary>
|
||||
public class Chapter
|
||||
{
|
||||
/// <summary>
|
||||
/// The start time of the chapter (in second from the start of the episode).
|
||||
/// </summary>
|
||||
public float StartTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The end time of the chapter (in second from the start of the episode).
|
||||
/// </summary>
|
||||
public float EndTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of this chapter. This should be a human-readable name that could be presented to the user.
|
||||
/// There should be well-known chapters name for commonly used chapters.
|
||||
/// For example, use "Opening" for the introduction-song and "Credits" for the end chapter with credits.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="Chapter"/>.
|
||||
/// </summary>
|
||||
/// <param name="startTime">The start time of the chapter (in second)</param>
|
||||
/// <param name="endTime">The end time of the chapter (in second)</param>
|
||||
/// <param name="name">The name of this chapter</param>
|
||||
public Chapter(float startTime, float endTime, string name)
|
||||
{
|
||||
StartTime = startTime;
|
||||
EndTime = endTime;
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using JetBrains.Annotations;
|
||||
using Kyoo.Utils;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A class given information about a strongly typed configuration.
|
||||
/// </summary>
|
||||
public class ConfigurationReference
|
||||
{
|
||||
/// <summary>
|
||||
/// The path of the resource (separated by ':')
|
||||
/// </summary>
|
||||
public string Path { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of the resource.
|
||||
/// </summary>
|
||||
public Type Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="ConfigurationReference"/> using a given path and type.
|
||||
/// This method does not create sub configuration resources. Please see <see cref="CreateReference"/>
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the resource (separated by ':' or "__")</param>
|
||||
/// <param name="type">The type of the resource</param>
|
||||
/// <seealso cref="CreateReference"/>
|
||||
public ConfigurationReference(string path, Type type)
|
||||
{
|
||||
Path = path;
|
||||
Type = type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the list of configuration reference a type has.
|
||||
/// </summary>
|
||||
/// <param name="path">
|
||||
/// The base path of the type (separated by ':' or "__". If empty, it will start at root)
|
||||
/// </param>
|
||||
/// <param name="type">The type of the object</param>
|
||||
/// <returns>The list of configuration reference a type has.</returns>
|
||||
public static IEnumerable<ConfigurationReference> CreateReference(string path, [NotNull] Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException(nameof(type));
|
||||
|
||||
List<ConfigurationReference> ret = new()
|
||||
{
|
||||
new ConfigurationReference(path, type)
|
||||
};
|
||||
|
||||
if (!type.IsClass || type.AssemblyQualifiedName?.StartsWith("System") == true)
|
||||
return ret;
|
||||
|
||||
Type enumerable = Utility.GetGenericDefinition(type, typeof(IEnumerable<>));
|
||||
Type dictionary = Utility.GetGenericDefinition(type, typeof(IDictionary<,>));
|
||||
Type dictionaryKey = dictionary?.GetGenericArguments()[0];
|
||||
|
||||
if (dictionary != null && dictionaryKey == typeof(string))
|
||||
ret.AddRange(CreateReference($"{path}:{type.Name}:*", dictionary.GetGenericArguments()[1]));
|
||||
else if (dictionary != null && dictionaryKey == typeof(int))
|
||||
ret.AddRange(CreateReference($"{path}:{type.Name}:", dictionary.GetGenericArguments()[1]));
|
||||
else if (enumerable != null)
|
||||
ret.AddRange(CreateReference($"{path}:{type.Name}:", enumerable.GetGenericArguments()[0]));
|
||||
else
|
||||
{
|
||||
foreach (PropertyInfo child in type.GetProperties())
|
||||
ret.AddRange(CreateReference($"{path}:{child.Name}", child.PropertyType));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the list of configuration reference a type has.
|
||||
/// </summary>
|
||||
/// <param name="path">
|
||||
/// The base path of the type (separated by ':' or "__". If empty, it will start at root)
|
||||
/// </param>
|
||||
/// <typeparam name="T">The type of the object</typeparam>
|
||||
/// <returns>The list of configuration reference a type has.</returns>
|
||||
public static IEnumerable<ConfigurationReference> CreateReference<T>(string path)
|
||||
{
|
||||
return CreateReference(path, typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a <see cref="ConfigurationReference"/> meaning that the given path is of any type.
|
||||
/// It means that the type can't be edited.
|
||||
/// </summary>
|
||||
/// <param name="path">
|
||||
/// The path that will be untyped (separated by ':' or "__". If empty, it will start at root).
|
||||
/// </param>
|
||||
/// <returns>A configuration reference representing a path of any type.</returns>
|
||||
public static ConfigurationReference CreateUntyped(string path)
|
||||
{
|
||||
return new ConfigurationReference(path, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,19 +28,19 @@ namespace Kyoo.Abstractions.Models.Exceptions
|
||||
public class DuplicatedItemException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a new <see cref="DuplicatedItemException"/> with the default message.
|
||||
/// The existing object.
|
||||
/// </summary>
|
||||
public DuplicatedItemException()
|
||||
: base("Already exists in the database.")
|
||||
{ }
|
||||
public object? Existing { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="DuplicatedItemException"/> with a custom message.
|
||||
/// Create a new <see cref="DuplicatedItemException"/> with the default message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to use</param>
|
||||
public DuplicatedItemException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
/// <param name="existing">The existing object.</param>
|
||||
public DuplicatedItemException(object? existing = null)
|
||||
: base("Already exists in the database.")
|
||||
{
|
||||
Existing = existing;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The serialization constructor.
|
||||
@@ -48,7 +48,6 @@ namespace Kyoo.Abstractions.Models.Exceptions
|
||||
/// <param name="info">Serialization infos</param>
|
||||
/// <param name="context">The serialization context</param>
|
||||
protected DuplicatedItemException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
: base(info, context) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Kyoo.Abstractions.Models.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
/// An exception thrown when a part of the app has a fatal issue.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class HealthException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a new <see cref="HealthException"/> with a custom message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to use.</param>
|
||||
public HealthException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// The serialization constructor
|
||||
/// </summary>
|
||||
/// <param name="info">Serialization infos</param>
|
||||
/// <param name="context">The serialization context</param>
|
||||
protected HealthException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
|
||||
namespace Kyoo.Abstractions.Models.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
/// An exception raised when an <see cref="IIdentifier"/> failed.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class IdentificationFailedException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a new <see cref="IdentificationFailedException"/> with a default message.
|
||||
/// </summary>
|
||||
public IdentificationFailedException()
|
||||
: base("An identification failed.")
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="IdentificationFailedException"/> with a custom message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to use.</param>
|
||||
public IdentificationFailedException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// The serialization constructor
|
||||
/// </summary>
|
||||
/// <param name="info">Serialization infos</param>
|
||||
/// <param name="context">The serialization context</param>
|
||||
protected IdentificationFailedException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
@@ -37,8 +37,7 @@ namespace Kyoo.Abstractions.Models.Exceptions
|
||||
/// </summary>
|
||||
/// <param name="message">The message of the exception</param>
|
||||
public ItemNotFoundException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
: base(message) { }
|
||||
|
||||
/// <summary>
|
||||
/// The serialization constructor
|
||||
@@ -46,7 +45,6 @@ namespace Kyoo.Abstractions.Models.Exceptions
|
||||
/// <param name="info">Serialization infos</param>
|
||||
/// <param name="context">The serialization context</param>
|
||||
protected ItemNotFoundException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
: base(info, context) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
|
||||
namespace Kyoo.Abstractions.Models.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
/// An exception raised when an <see cref="ITask"/> failed.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class TaskFailedException : AggregateException
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a new <see cref="TaskFailedException"/> with a default message.
|
||||
/// </summary>
|
||||
public TaskFailedException()
|
||||
: base("A task failed.")
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="TaskFailedException"/> with a custom message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to use.</param>
|
||||
public TaskFailedException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="TaskFailedException"/> wrapping another exception.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception to wrap.</param>
|
||||
public TaskFailedException(Exception exception)
|
||||
: base(exception)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// The serialization constructor
|
||||
/// </summary>
|
||||
/// <param name="info">Serialization infos</param>
|
||||
/// <param name="context">The serialization context</param>
|
||||
protected TaskFailedException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Kyoo.Abstractions.Models.Exceptions
|
||||
{
|
||||
[Serializable]
|
||||
public class UnauthorizedException : Exception
|
||||
{
|
||||
public UnauthorizedException()
|
||||
: base("User not authenticated or token invalid.") { }
|
||||
|
||||
public UnauthorizedException(string message)
|
||||
: base(message) { }
|
||||
|
||||
protected UnauthorizedException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context) { }
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
using Kyoo.Utils;
|
||||
using PathIO = System.IO.Path;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A font of an <see cref="Episode"/>.
|
||||
/// </summary>
|
||||
public class Font : ILink
|
||||
{
|
||||
/// <summary>
|
||||
/// A human-readable identifier, used in the URL.
|
||||
/// </summary>
|
||||
public string Slug { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the font file (with the extension).
|
||||
/// </summary>
|
||||
public string File { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The format of this font (the extension).
|
||||
/// </summary>
|
||||
public string Format { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The path of the font.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public string Path { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public object Link { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new empty <see cref="Font"/>.
|
||||
/// </summary>
|
||||
public Font() { }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="Font"/> from a path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the font.</param>
|
||||
/// <param name="episodeSlug">The slug of the episode that contains this font.</param>
|
||||
public Font(string path, string episodeSlug)
|
||||
{
|
||||
Slug = Utility.ToSlug(PathIO.GetFileNameWithoutExtension(path));
|
||||
Path = path;
|
||||
File = PathIO.GetFileName(path);
|
||||
Format = PathIO.GetExtension(path).Replace(".", string.Empty);
|
||||
Link = $"/watch/{episodeSlug}/font/{Slug}.{Format}";
|
||||
}
|
||||
}
|
||||
}
|
||||
45
back/src/Kyoo.Abstractions/Models/Genre.cs
Normal file
45
back/src/Kyoo.Abstractions/Models/Genre.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A genre that allow one to specify categories for shows.
|
||||
/// </summary>
|
||||
public enum Genre
|
||||
{
|
||||
Action,
|
||||
Adventure,
|
||||
Animation,
|
||||
Comedy,
|
||||
Crime,
|
||||
Documentary,
|
||||
Drama,
|
||||
Family,
|
||||
Fantasy,
|
||||
History,
|
||||
Horror,
|
||||
Music,
|
||||
Mystery,
|
||||
Romance,
|
||||
ScienceFiction,
|
||||
Thriller,
|
||||
War,
|
||||
Western,
|
||||
}
|
||||
}
|
||||
31
back/src/Kyoo.Abstractions/Models/ILibraryItem.cs
Normal file
31
back/src/Kyoo.Abstractions/Models/ILibraryItem.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Abstractions.Models;
|
||||
|
||||
/// <summary>
|
||||
/// A show, a movie or a collection.
|
||||
/// </summary>
|
||||
[OneOf(Types = new[] { typeof(Show), typeof(Movie), typeof(Collection) })]
|
||||
public interface ILibraryItem : IResource, IThumbnails, IMetadata, IAddedDate, IQuery
|
||||
{
|
||||
static Sort IQuery.DefaultSort => new Sort<ILibraryItem>.By(nameof(Movie.Name));
|
||||
}
|
||||
31
back/src/Kyoo.Abstractions/Models/INews.cs
Normal file
31
back/src/Kyoo.Abstractions/Models/INews.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Abstractions.Models;
|
||||
|
||||
/// <summary>
|
||||
/// A show, a movie or a collection.
|
||||
/// </summary>
|
||||
[OneOf(Types = new[] { typeof(Episode), typeof(Movie) })]
|
||||
public interface INews : IResource, IThumbnails, IMetadata, IAddedDate, IQuery
|
||||
{
|
||||
static Sort IQuery.DefaultSort => new Sort<INews>.By(nameof(AddedDate), true);
|
||||
}
|
||||
27
back/src/Kyoo.Abstractions/Models/IWatchlist.cs
Normal file
27
back/src/Kyoo.Abstractions/Models/IWatchlist.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Abstractions.Models;
|
||||
|
||||
/// <summary>
|
||||
/// A watch list item.
|
||||
/// </summary>
|
||||
[OneOf(Types = new[] { typeof(Show), typeof(Movie) })]
|
||||
public interface IWatchlist : IResource, IThumbnails, IMetadata, IAddedDate { }
|
||||
@@ -1,172 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of item, ether a show, a movie or a collection.
|
||||
/// </summary>
|
||||
public enum ItemType
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="LibraryItem"/> is a <see cref="Show"/>.
|
||||
/// </summary>
|
||||
Show,
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="LibraryItem"/> is a Movie (a <see cref="Show"/> with
|
||||
/// <see cref="Models.Show.IsMovie"/> equals to true).
|
||||
/// </summary>
|
||||
Movie,
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="LibraryItem"/> is a <see cref="Collection"/>.
|
||||
/// </summary>
|
||||
Collection
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A type union between <see cref="Show"/> and <see cref="Collection"/>.
|
||||
/// This is used to list content put inside a library.
|
||||
/// </summary>
|
||||
public class LibraryItem : CustomTypeDescriptor, IResource, IThumbnails
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public int ID { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Slug { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The title of the show or collection.
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The summary of the show or collection.
|
||||
/// </summary>
|
||||
public string Overview { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is this show airing, not aired yet or finished? This is only applicable for shows.
|
||||
/// </summary>
|
||||
public Status? Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The date this show or collection started airing. It can be null if this is unknown.
|
||||
/// </summary>
|
||||
public DateTime? StartAir { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The date this show or collection finished airing.
|
||||
/// It must be after the <see cref="StartAir"/> but can be the same (example: for movies).
|
||||
/// It can also be null if this is unknown.
|
||||
/// </summary>
|
||||
public DateTime? EndAir { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<int, string> Images { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of this item (ether a collection, a show or a movie).
|
||||
/// </summary>
|
||||
public ItemType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new, empty <see cref="LibraryItem"/>.
|
||||
/// </summary>
|
||||
public LibraryItem() { }
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="LibraryItem"/> from a show.
|
||||
/// </summary>
|
||||
/// <param name="show">The show that this library item should represent.</param>
|
||||
public LibraryItem(Show show)
|
||||
{
|
||||
ID = show.ID;
|
||||
Slug = show.Slug;
|
||||
Title = show.Title;
|
||||
Overview = show.Overview;
|
||||
Status = show.Status;
|
||||
StartAir = show.StartAir;
|
||||
EndAir = show.EndAir;
|
||||
Images = show.Images;
|
||||
Type = show.IsMovie ? ItemType.Movie : ItemType.Show;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="LibraryItem"/> from a collection
|
||||
/// </summary>
|
||||
/// <param name="collection">The collection that this library item should represent.</param>
|
||||
public LibraryItem(Collection collection)
|
||||
{
|
||||
ID = -collection.ID;
|
||||
Slug = collection.Slug;
|
||||
Title = collection.Name;
|
||||
Overview = collection.Overview;
|
||||
Status = Models.Status.Unknown;
|
||||
StartAir = null;
|
||||
EndAir = null;
|
||||
Images = collection.Images;
|
||||
Type = ItemType.Collection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An expression to create a <see cref="LibraryItem"/> representing a show.
|
||||
/// </summary>
|
||||
public static Expression<Func<Show, LibraryItem>> FromShow => x => new LibraryItem
|
||||
{
|
||||
ID = x.ID,
|
||||
Slug = x.Slug,
|
||||
Title = x.Title,
|
||||
Overview = x.Overview,
|
||||
Status = x.Status,
|
||||
StartAir = x.StartAir,
|
||||
EndAir = x.EndAir,
|
||||
Images = x.Images,
|
||||
Type = x.IsMovie ? ItemType.Movie : ItemType.Show
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// An expression to create a <see cref="LibraryItem"/> representing a collection.
|
||||
/// </summary>
|
||||
public static Expression<Func<Collection, LibraryItem>> FromCollection => x => new LibraryItem
|
||||
{
|
||||
ID = -x.ID,
|
||||
Slug = x.Slug,
|
||||
Title = x.Name,
|
||||
Overview = x.Overview,
|
||||
Status = Models.Status.Unknown,
|
||||
StartAir = null,
|
||||
EndAir = null,
|
||||
Images = x.Images,
|
||||
Type = ItemType.Collection
|
||||
};
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string GetClassName()
|
||||
{
|
||||
return Type.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,48 +16,21 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// ID and link of an item on an external provider.
|
||||
/// </summary>
|
||||
public class MetadataID
|
||||
public class MetadataId
|
||||
{
|
||||
/// <summary>
|
||||
/// The expression to retrieve the unique ID of a MetadataID. This is an aggregate of the two resources IDs.
|
||||
/// </summary>
|
||||
public static Expression<Func<MetadataID, object>> PrimaryKey
|
||||
{
|
||||
get { return x => new { First = x.ResourceID, Second = x.ProviderID }; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the resource which possess the metadata.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public int ResourceID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the provider.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public int ProviderID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The provider that can do something with this ID.
|
||||
/// </summary>
|
||||
public Provider Provider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the resource on the external provider.
|
||||
/// </summary>
|
||||
public string DataID { get; set; }
|
||||
public string DataId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The URL of the resource on the external provider.
|
||||
/// </summary>
|
||||
public string Link { get; set; }
|
||||
public string? Link { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Kyoo.Utils;
|
||||
@@ -40,10 +39,15 @@ namespace Kyoo.Abstractions.Models
|
||||
/// </summary>
|
||||
public string First { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The link of the previous page.
|
||||
/// </summary>
|
||||
public string? Previous { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The link of the next page.
|
||||
/// </summary>
|
||||
public string Next { get; }
|
||||
public string? Next { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of items in the current page.
|
||||
@@ -60,12 +64,20 @@ namespace Kyoo.Abstractions.Models
|
||||
/// </summary>
|
||||
/// <param name="items">The list of items in the page.</param>
|
||||
/// <param name="this">The link of the current page.</param>
|
||||
/// <param name="previous">The link of the previous page.</param>
|
||||
/// <param name="next">The link of the next page.</param>
|
||||
/// <param name="first">The link of the first page.</param>
|
||||
public Page(ICollection<T> items, string @this, string next, string first)
|
||||
public Page(
|
||||
ICollection<T> items,
|
||||
string @this,
|
||||
string? previous,
|
||||
string? next,
|
||||
string first
|
||||
)
|
||||
{
|
||||
Items = items;
|
||||
This = @this;
|
||||
Previous = previous;
|
||||
Next = next;
|
||||
First = first;
|
||||
}
|
||||
@@ -77,20 +89,22 @@ namespace Kyoo.Abstractions.Models
|
||||
/// <param name="url">The base url of the resources available from this page.</param>
|
||||
/// <param name="query">The list of query strings of the current page</param>
|
||||
/// <param name="limit">The number of items requested for the current page.</param>
|
||||
public Page(ICollection<T> items,
|
||||
string url,
|
||||
Dictionary<string, string> query,
|
||||
int limit)
|
||||
public Page(ICollection<T> items, string url, Dictionary<string, string> query, int limit)
|
||||
{
|
||||
Items = items;
|
||||
This = url + query.ToQueryString();
|
||||
|
||||
if (items.Count > 0 && query.ContainsKey("afterID"))
|
||||
{
|
||||
query["afterID"] = items.First().Id.ToString();
|
||||
query["reverse"] = "true";
|
||||
Previous = url + query.ToQueryString();
|
||||
}
|
||||
query.Remove("reverse");
|
||||
if (items.Count == limit && limit > 0)
|
||||
{
|
||||
query["afterID"] = items.Last().ID.ToString();
|
||||
query["afterID"] = items.Last().Id.ToString();
|
||||
Next = url + query.ToQueryString();
|
||||
}
|
||||
|
||||
query.Remove("afterID");
|
||||
First = url + query.ToQueryString();
|
||||
}
|
||||
|
||||
28
back/src/Kyoo.Abstractions/Models/PartialResource.cs
Normal file
28
back/src/Kyoo.Abstractions/Models/PartialResource.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Kyoo.Models;
|
||||
|
||||
public class PartialResource
|
||||
{
|
||||
public Guid? Id { get; set; }
|
||||
|
||||
public string? Slug { get; set; }
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
/// <summary>
|
||||
@@ -29,10 +31,10 @@ namespace Kyoo.Abstractions.Models
|
||||
public class PeopleRole : IResource
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public int ID { get; set; }
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Slug => ForPeople ? Show.Slug : People.Slug;
|
||||
public string Slug => ForPeople ? Show!.Slug : People.Slug;
|
||||
|
||||
/// <summary>
|
||||
/// Should this role be used as a Show substitute (the value is <c>true</c>) or
|
||||
@@ -43,7 +45,7 @@ namespace Kyoo.Abstractions.Models
|
||||
/// <summary>
|
||||
/// The ID of the People playing the role.
|
||||
/// </summary>
|
||||
public int PeopleID { get; set; }
|
||||
public Guid PeopleID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The people that played this role.
|
||||
@@ -53,12 +55,16 @@ namespace Kyoo.Abstractions.Models
|
||||
/// <summary>
|
||||
/// The ID of the Show where the People playing in.
|
||||
/// </summary>
|
||||
public int ShowID { get; set; }
|
||||
public Guid? ShowID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The show where the People played in.
|
||||
/// </summary>
|
||||
public Show Show { get; set; }
|
||||
public Show? Show { get; set; }
|
||||
|
||||
public Guid? MovieID { get; set; }
|
||||
|
||||
public Movie? Movie { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of work the person has done for the show.
|
||||
|
||||
@@ -16,21 +16,28 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
using Kyoo.Utils;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A class representing collections of <see cref="Show"/>.
|
||||
/// A collection can also be stored in a <see cref="Library"/>.
|
||||
/// </summary>
|
||||
public class Collection : IResource, IMetadata, IThumbnails
|
||||
public class Collection : IQuery, IResource, IMetadata, IThumbnails, IAddedDate, ILibraryItem
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public int ID { get; set; }
|
||||
public static Sort DefaultSort => new Sort<Collection>.By(nameof(Collection.Name));
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[MaxLength(256)]
|
||||
public string Slug { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -38,25 +45,48 @@ namespace Kyoo.Abstractions.Models
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<int, string> Images { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The description of this collection.
|
||||
/// </summary>
|
||||
public string Overview { get; set; }
|
||||
public string? Overview { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public DateTime AddedDate { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image? Poster { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image? Thumbnail { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image? Logo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of movies contained in this collection.
|
||||
/// </summary>
|
||||
[SerializeIgnore]
|
||||
public ICollection<Movie>? Movies { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of shows contained in this collection.
|
||||
/// </summary>
|
||||
[LoadableRelation] public ICollection<Show> Shows { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of libraries that contains this collection.
|
||||
/// </summary>
|
||||
[LoadableRelation] public ICollection<Library> Libraries { get; set; }
|
||||
[SerializeIgnore]
|
||||
public ICollection<Show>? Shows { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[EditableRelation] [LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; }
|
||||
public Dictionary<string, MetadataId> ExternalId { get; set; } = new();
|
||||
|
||||
public Collection() { }
|
||||
|
||||
[JsonConstructor]
|
||||
public Collection(string name)
|
||||
{
|
||||
if (name != null)
|
||||
{
|
||||
Slug = Utility.ToSlug(name);
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,10 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using EntityFrameworkCore.Projectables;
|
||||
using JetBrains.Annotations;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
@@ -28,31 +31,38 @@ namespace Kyoo.Abstractions.Models
|
||||
/// <summary>
|
||||
/// A class to represent a single show's episode.
|
||||
/// </summary>
|
||||
public class Episode : IResource, IMetadata, IThumbnails
|
||||
public class Episode : IQuery, IResource, IMetadata, IThumbnails, IAddedDate, INews
|
||||
{
|
||||
// Use absolute numbers by default and fallback to season/episodes if it does not exists.
|
||||
public static Sort DefaultSort =>
|
||||
new Sort<Episode>.Conglomerate(
|
||||
new Sort<Episode>.By(x => x.AbsoluteNumber),
|
||||
new Sort<Episode>.By(x => x.SeasonNumber),
|
||||
new Sort<Episode>.By(x => x.EpisodeNumber)
|
||||
);
|
||||
|
||||
/// <inheritdoc />
|
||||
public int ID { get; set; }
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[Computed]
|
||||
[MaxLength(256)]
|
||||
public string Slug
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ShowSlug != null || Show != null)
|
||||
return GetSlug(ShowSlug ?? Show.Slug, SeasonNumber, EpisodeNumber, AbsoluteNumber);
|
||||
return ShowID != 0
|
||||
? GetSlug(ShowID.ToString(), SeasonNumber, EpisodeNumber, AbsoluteNumber)
|
||||
: null;
|
||||
if (ShowSlug != null || Show?.Slug != null)
|
||||
return GetSlug(
|
||||
ShowSlug ?? Show!.Slug,
|
||||
SeasonNumber,
|
||||
EpisodeNumber,
|
||||
AbsoluteNumber
|
||||
);
|
||||
return GetSlug(ShowId.ToString(), SeasonNumber, EpisodeNumber, AbsoluteNumber);
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
[NotNull]
|
||||
private set
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
|
||||
Match match = Regex.Match(value, @"(?<show>.+)-s(?<season>\d+)e(?<episode>\d+)");
|
||||
|
||||
if (match.Success)
|
||||
@@ -80,32 +90,34 @@ namespace Kyoo.Abstractions.Models
|
||||
/// <summary>
|
||||
/// The slug of the Show that contain this episode. If this is not set, this episode is ill-formed.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public string ShowSlug { private get; set; }
|
||||
[SerializeIgnore]
|
||||
public string? ShowSlug { private get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the Show containing this episode.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public int ShowID { get; set; }
|
||||
public Guid ShowId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The show that contains this episode. This must be explicitly loaded via a call to <see cref="ILibraryManager.Load"/>.
|
||||
/// The show that contains this episode.
|
||||
/// </summary>
|
||||
[LoadableRelation(nameof(ShowID))] public Show Show { get; set; }
|
||||
[LoadableRelation(nameof(ShowId))]
|
||||
public Show? Show { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the Season containing this episode.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public int? SeasonID { get; set; }
|
||||
public Guid? SeasonId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The season that contains this episode.
|
||||
/// This must be explicitly loaded via a call to <see cref="ILibraryManager.Load"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This can be null if the season is unknown and the episode is only identified
|
||||
/// by it's <see cref="AbsoluteNumber"/>.
|
||||
/// </remarks>
|
||||
[LoadableRelation(nameof(SeasonID))] public Season Season { get; set; }
|
||||
[LoadableRelation(nameof(SeasonId))]
|
||||
public Season? Season { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The season in witch this episode is in.
|
||||
@@ -123,22 +135,24 @@ namespace Kyoo.Abstractions.Models
|
||||
public int? AbsoluteNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The path of the video file for this episode. Any format supported by a <see cref="IFileSystem"/> is allowed.
|
||||
/// The path of the video file for this episode.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public string Path { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<int, string> Images { get; set; }
|
||||
public string Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The title of this episode.
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The overview of this episode.
|
||||
/// </summary>
|
||||
public string Overview { get; set; }
|
||||
public string? Overview { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// How long is this episode? (in minutes)
|
||||
/// </summary>
|
||||
public int Runtime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The release date of this episode. It can be null if unknown.
|
||||
@@ -146,12 +160,124 @@ namespace Kyoo.Abstractions.Models
|
||||
public DateTime? ReleaseDate { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[EditableRelation] [LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; }
|
||||
public DateTime AddedDate { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image? Poster { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image? Thumbnail { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image? Logo { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<string, MetadataId> ExternalId { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// The list of tracks this episode has. This lists video, audio and subtitles available.
|
||||
/// The previous episode that should be seen before viewing this one.
|
||||
/// </summary>
|
||||
[EditableRelation] [LoadableRelation] public ICollection<Track> Tracks { get; set; }
|
||||
[Projectable(UseMemberBody = nameof(_PreviousEpisode), OnlyOnInclude = true)]
|
||||
[LoadableRelation(
|
||||
// language=PostgreSQL
|
||||
Sql = """
|
||||
select
|
||||
pe.* -- Episode as pe
|
||||
from
|
||||
episodes as "pe"
|
||||
where
|
||||
pe.show_id = "this".show_id
|
||||
and (pe.absolute_number < "this".absolute_number
|
||||
or pe.season_number < "this".season_number
|
||||
or (pe.season_number = "this".season_number
|
||||
and e.episode_number < "this".episode_number))
|
||||
order by
|
||||
pe.absolute_number desc nulls last,
|
||||
pe.season_number desc,
|
||||
pe.episode_number desc
|
||||
limit 1
|
||||
"""
|
||||
)]
|
||||
public Episode? PreviousEpisode { get; set; }
|
||||
|
||||
private Episode? _PreviousEpisode =>
|
||||
Show!
|
||||
.Episodes!
|
||||
.OrderBy(x => x.AbsoluteNumber == null)
|
||||
.ThenByDescending(x => x.AbsoluteNumber)
|
||||
.ThenByDescending(x => x.SeasonNumber)
|
||||
.ThenByDescending(x => x.EpisodeNumber)
|
||||
.FirstOrDefault(
|
||||
x =>
|
||||
x.AbsoluteNumber < AbsoluteNumber
|
||||
|| x.SeasonNumber < SeasonNumber
|
||||
|| (x.SeasonNumber == SeasonNumber && x.EpisodeNumber < EpisodeNumber)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// The next episode to watch after this one.
|
||||
/// </summary>
|
||||
[Projectable(UseMemberBody = nameof(_NextEpisode), OnlyOnInclude = true)]
|
||||
[LoadableRelation(
|
||||
// language=PostgreSQL
|
||||
Sql = """
|
||||
select
|
||||
ne.* -- Episode as ne
|
||||
from
|
||||
episodes as "ne"
|
||||
where
|
||||
ne.show_id = "this".show_id
|
||||
and (ne.absolute_number > "this".absolute_number
|
||||
or ne.season_number > "this".season_number
|
||||
or (ne.season_number = "this".season_number
|
||||
and e.episode_number > "this".episode_number))
|
||||
order by
|
||||
ne.absolute_number,
|
||||
ne.season_number,
|
||||
ne.episode_number
|
||||
limit 1
|
||||
"""
|
||||
)]
|
||||
public Episode? NextEpisode { get; set; }
|
||||
|
||||
private Episode? _NextEpisode =>
|
||||
Show!
|
||||
.Episodes!
|
||||
.OrderBy(x => x.AbsoluteNumber)
|
||||
.ThenBy(x => x.SeasonNumber)
|
||||
.ThenBy(x => x.EpisodeNumber)
|
||||
.FirstOrDefault(
|
||||
x =>
|
||||
x.AbsoluteNumber > AbsoluteNumber
|
||||
|| x.SeasonNumber > SeasonNumber
|
||||
|| (x.SeasonNumber == SeasonNumber && x.EpisodeNumber > EpisodeNumber)
|
||||
);
|
||||
|
||||
[SerializeIgnore]
|
||||
public ICollection<EpisodeWatchStatus>? Watched { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Metadata of what an user as started/planned to watch.
|
||||
/// </summary>
|
||||
[Projectable(UseMemberBody = nameof(_WatchStatus), OnlyOnInclude = true)]
|
||||
[LoadableRelation(
|
||||
Sql = "episode_watch_status",
|
||||
On = "episode_id = \"this\".id and \"relation\".user_id = [current_user]"
|
||||
)]
|
||||
public EpisodeWatchStatus? WatchStatus { get; set; }
|
||||
|
||||
// There is a global query filter to filter by user so we just need to do single.
|
||||
private EpisodeWatchStatus? _WatchStatus => Watched!.FirstOrDefault();
|
||||
|
||||
/// <summary>
|
||||
/// Links to watch this episode.
|
||||
/// </summary>
|
||||
public VideoLinks Links =>
|
||||
new()
|
||||
{
|
||||
Direct = $"/video/episode/{Slug}/direct",
|
||||
Hls = $"/video/episode/{Slug}/master.m3u8",
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Get the slug of an episode.
|
||||
@@ -170,14 +296,13 @@ namespace Kyoo.Abstractions.Models
|
||||
/// If you don't know it or this is a movie, use null
|
||||
/// </param>
|
||||
/// <returns>The slug corresponding to the given arguments</returns>
|
||||
/// <exception cref="ArgumentNullException">The given show slug was null.</exception>
|
||||
public static string GetSlug([NotNull] string showSlug,
|
||||
public static string GetSlug(
|
||||
string showSlug,
|
||||
int? seasonNumber,
|
||||
int? episodeNumber,
|
||||
int? absoluteNumber = null)
|
||||
int? absoluteNumber = null
|
||||
)
|
||||
{
|
||||
if (showSlug == null)
|
||||
throw new ArgumentNullException(nameof(showSlug));
|
||||
return seasonNumber switch
|
||||
{
|
||||
null when absoluteNumber == null => showSlug,
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
using Kyoo.Utils;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A genre that allow one to specify categories for shows.
|
||||
/// </summary>
|
||||
public class Genre : IResource
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public int ID { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Slug { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of this genre.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of shows that have this genre.
|
||||
/// </summary>
|
||||
[LoadableRelation] public ICollection<Show> Shows { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new, empty <see cref="Genre"/>.
|
||||
/// </summary>
|
||||
public Genre() { }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="Genre"/> and specify it's <see cref="Name"/>.
|
||||
/// The <see cref="Slug"/> is automatically calculated from it's name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of this genre.</param>
|
||||
public Genre(string name)
|
||||
{
|
||||
Slug = Utility.ToSlug(name);
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,16 +16,18 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface to represent resources that should have a link field in their return values (like videos).
|
||||
/// An interface applied to resources.
|
||||
/// </summary>
|
||||
public interface ILink
|
||||
public interface IAddedDate
|
||||
{
|
||||
/// <summary>
|
||||
/// The link to return, in most cases this should be a string.
|
||||
/// The date at which this resource was added to kyoo.
|
||||
/// </summary>
|
||||
public object Link { get; }
|
||||
public DateTime AddedDate { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -16,11 +16,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
@@ -30,69 +26,8 @@ namespace Kyoo.Abstractions.Models
|
||||
public interface IMetadata
|
||||
{
|
||||
/// <summary>
|
||||
/// The link to metadata providers that this show has. See <see cref="MetadataID"/> for more information.
|
||||
/// The link to metadata providers that this show has. See <see cref="MetadataId"/> for more information.
|
||||
/// </summary>
|
||||
[EditableRelation]
|
||||
[LoadableRelation]
|
||||
public ICollection<MetadataID> ExternalIDs { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A static class containing extensions method for every <see cref="IMetadata"/> class.
|
||||
/// This allow one to use metadata more easily.
|
||||
/// </summary>
|
||||
public static class MetadataExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieve the internal provider's ID of an item using it's provider slug.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method will never return anything if the <see cref="IMetadata.ExternalIDs"/> are not loaded.
|
||||
/// </remarks>
|
||||
/// <param name="self">An instance of <see cref="IMetadata"/> to retrieve the ID from.</param>
|
||||
/// <param name="provider">The slug of the provider</param>
|
||||
/// <returns>The <see cref="MetadataID.DataID"/> field of the asked provider.</returns>
|
||||
[CanBeNull]
|
||||
public static string GetID(this IMetadata self, string provider)
|
||||
{
|
||||
return self.ExternalIDs?.FirstOrDefault(x => x.Provider.Slug == provider)?.DataID;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the internal provider's ID of an item using it's provider slug.
|
||||
/// If the ID could be found, it is converted to the <typeparamref name="T"/> type and <c>true</c> is returned.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method will never succeed if the <see cref="IMetadata.ExternalIDs"/> are not loaded.
|
||||
/// </remarks>
|
||||
/// <param name="self">An instance of <see cref="IMetadata"/> to retrieve the ID from.</param>
|
||||
/// <param name="provider">The slug of the provider</param>
|
||||
/// <param name="id">
|
||||
/// The <see cref="MetadataID.DataID"/> field of the asked provider parsed
|
||||
/// and converted to the <typeparamref name="T"/> type.
|
||||
/// It is only relevant if this method returns <c>true</c>.
|
||||
/// </param>
|
||||
/// <typeparam name="T">The type to convert the <see cref="MetadataID.DataID"/> to.</typeparam>
|
||||
/// <returns><c>true</c> if this method succeeded, <c>false</c> otherwise.</returns>
|
||||
public static bool TryGetID<T>(this IMetadata self, string provider, out T id)
|
||||
{
|
||||
string dataID = self.ExternalIDs?.FirstOrDefault(x => x.Provider.Slug == provider)?.DataID;
|
||||
if (dataID == null)
|
||||
{
|
||||
id = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
id = (T)Convert.ChangeType(dataID, typeof(T));
|
||||
}
|
||||
catch
|
||||
{
|
||||
id = default;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public Dictionary<string, MetadataId> ExternalId { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,11 +19,12 @@
|
||||
using System;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
|
||||
namespace Kyoo.Abstractions.Models.Attributes
|
||||
namespace Kyoo.Abstractions.Models;
|
||||
|
||||
public interface IQuery
|
||||
{
|
||||
/// <summary>
|
||||
/// The targeted relation can be edited via calls to the repository's <see cref="IRepository{T}.Edit"/> method.
|
||||
/// The sorting that will be used when no user defined one is present.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class EditableRelationAttribute : Attribute { }
|
||||
public static virtual Sort DefaultSort => throw new NotImplementedException();
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
@@ -23,7 +25,7 @@ namespace Kyoo.Abstractions.Models
|
||||
/// <summary>
|
||||
/// An interface to represent a resource that can be retrieved from the database.
|
||||
/// </summary>
|
||||
public interface IResource
|
||||
public interface IResource : IQuery
|
||||
{
|
||||
/// <summary>
|
||||
/// A unique ID for this type of resource. This can't be changed and duplicates are not allowed.
|
||||
@@ -32,7 +34,7 @@ namespace Kyoo.Abstractions.Models
|
||||
/// You don't need to specify an ID manually when creating a new resource,
|
||||
/// this field is automatically assigned by the <see cref="IRepository{T}"/>.
|
||||
/// </remarks>
|
||||
public int ID { get; set; }
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A human-readable identifier that can be used instead of an ID.
|
||||
@@ -42,6 +44,7 @@ namespace Kyoo.Abstractions.Models
|
||||
/// There is no setter for a slug since it can be computed from other fields.
|
||||
/// For example, a season slug is {ShowSlug}-s{SeasonNumber}.
|
||||
/// </remarks>
|
||||
[MaxLength(256)]
|
||||
public string Slug { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,12 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Globalization;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
@@ -27,52 +31,97 @@ namespace Kyoo.Abstractions.Models
|
||||
public interface IThumbnails
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of images mapped to a certain index.
|
||||
/// The string value should be a path supported by the <see cref="IFileSystem"/>.
|
||||
/// A poster is a 2/3 format image with the cover of the resource.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// An arbitrary index should not be used, instead use indexes from <see cref="Models.Images"/>
|
||||
/// </remarks>
|
||||
/// <example>{"0": "example.com/dune/poster"}</example>
|
||||
public Dictionary<int, string> Images { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A class containing constant values for images. To be used as index of a <see cref="IThumbnails.Images"/>.
|
||||
/// </summary>
|
||||
public static class Images
|
||||
{
|
||||
/// <summary>
|
||||
/// A poster is a 9/16 format image with the cover of the resource.
|
||||
/// </summary>
|
||||
public const int Poster = 0;
|
||||
public Image? Poster { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A thumbnail is a 16/9 format image, it could ether be used as a background or as a preview but it usually
|
||||
/// is not an official image.
|
||||
/// </summary>
|
||||
public const int Thumbnail = 1;
|
||||
public Image? Thumbnail { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A logo is a small image representing the resource.
|
||||
/// </summary>
|
||||
public const int Logo = 2;
|
||||
public Image? Logo { get; set; }
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ImageConvertor))]
|
||||
[SqlFirstColumn(nameof(Source))]
|
||||
public class Image
|
||||
{
|
||||
/// <summary>
|
||||
/// The original image from another server.
|
||||
/// </summary>
|
||||
public string Source { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A video of a few minutes that tease the content.
|
||||
/// A hash to display as placeholder while the image is loading.
|
||||
/// </summary>
|
||||
public const int Trailer = 3;
|
||||
[MaxLength(32)]
|
||||
public string Blurhash { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the name of an image using it's ID. It is also used by the serializer to retrieve all named images.
|
||||
/// If a plugin adds a new image type, it should add it's value and name here to allow the serializer to add it.
|
||||
/// </summary>
|
||||
public static Dictionary<int, string> ImageName { get; } = new()
|
||||
public Image() { }
|
||||
|
||||
[JsonConstructor]
|
||||
public Image(string source, string? blurhash = null)
|
||||
{
|
||||
[Poster] = nameof(Poster),
|
||||
[Thumbnail] = nameof(Thumbnail),
|
||||
[Logo] = nameof(Logo),
|
||||
[Trailer] = nameof(Trailer)
|
||||
};
|
||||
Source = source;
|
||||
Blurhash = blurhash ?? "000000";
|
||||
}
|
||||
|
||||
public class ImageConvertor : TypeConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
return true;
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(
|
||||
ITypeDescriptorContext? context,
|
||||
CultureInfo? culture,
|
||||
object value
|
||||
)
|
||||
{
|
||||
if (value is not string source)
|
||||
return base.ConvertFrom(context, culture, value)!;
|
||||
return new Image(source);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(
|
||||
ITypeDescriptorContext? context,
|
||||
Type? destinationType
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The quality of an image
|
||||
/// </summary>
|
||||
public enum ImageQuality
|
||||
{
|
||||
/// <summary>
|
||||
/// Small
|
||||
/// </summary>
|
||||
Low,
|
||||
|
||||
/// <summary>
|
||||
/// Medium
|
||||
/// </summary>
|
||||
Medium,
|
||||
|
||||
/// <summary>
|
||||
/// Large
|
||||
/// </summary>
|
||||
High,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A library containing <see cref="Show"/> and <see cref="Collection"/>.
|
||||
/// </summary>
|
||||
public class Library : IResource
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public int ID { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Slug { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of this library.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of paths that this library is responsible for. This is mainly used by the Scan task.
|
||||
/// </summary>
|
||||
public string[] Paths { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of <see cref="Provider"/> used for items in this library.
|
||||
/// </summary>
|
||||
[EditableRelation] [LoadableRelation] public ICollection<Provider> Providers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of shows in this library.
|
||||
/// </summary>
|
||||
[LoadableRelation] public ICollection<Show> Shows { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of collections in this library.
|
||||
/// </summary>
|
||||
[LoadableRelation] public ICollection<Collection> Collections { get; set; }
|
||||
}
|
||||
}
|
||||
200
back/src/Kyoo.Abstractions/Models/Resources/Movie.cs
Normal file
200
back/src/Kyoo.Abstractions/Models/Resources/Movie.cs
Normal file
@@ -0,0 +1,200 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using EntityFrameworkCore.Projectables;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
using Kyoo.Utils;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A series or a movie.
|
||||
/// </summary>
|
||||
public class Movie
|
||||
: IQuery,
|
||||
IResource,
|
||||
IMetadata,
|
||||
IOnMerge,
|
||||
IThumbnails,
|
||||
IAddedDate,
|
||||
ILibraryItem,
|
||||
INews,
|
||||
IWatchlist
|
||||
{
|
||||
public static Sort DefaultSort => new Sort<Movie>.By(x => x.Name);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[MaxLength(256)]
|
||||
public string Slug { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The title of this show.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A catchphrase for this movie.
|
||||
/// </summary>
|
||||
public string? Tagline { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of alternative titles of this show.
|
||||
/// </summary>
|
||||
public string[] Aliases { get; set; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// The path of the movie video file.
|
||||
/// </summary>
|
||||
public string Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The summary of this show.
|
||||
/// </summary>
|
||||
public string? Overview { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of tags that match this movie.
|
||||
/// </summary>
|
||||
public string[] Tags { get; set; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// The list of genres (themes) this show has.
|
||||
/// </summary>
|
||||
public Genre[] Genres { get; set; } = Array.Empty<Genre>();
|
||||
|
||||
/// <summary>
|
||||
/// Is this show airing, not aired yet or finished?
|
||||
/// </summary>
|
||||
public Status Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// How well this item is rated? (from 0 to 100).
|
||||
/// </summary>
|
||||
public int Rating { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// How long is this movie? (in minutes)
|
||||
/// </summary>
|
||||
public int Runtime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The date this movie aired.
|
||||
/// </summary>
|
||||
public DateTime? AirDate { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public DateTime AddedDate { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image? Poster { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image? Thumbnail { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image? Logo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A video of a few minutes that tease the content.
|
||||
/// </summary>
|
||||
public string? Trailer { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<string, MetadataId> ExternalId { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the Studio that made this show.
|
||||
/// </summary>
|
||||
[SerializeIgnore]
|
||||
public Guid? StudioId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Studio that made this show.
|
||||
/// </summary>
|
||||
[LoadableRelation(nameof(StudioId))]
|
||||
public Studio? Studio { get; set; }
|
||||
|
||||
// /// <summary>
|
||||
// /// The list of people that made this show.
|
||||
// /// </summary>
|
||||
// [SerializeIgnore] public ICollection<PeopleRole>? People { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of collections that contains this show.
|
||||
/// </summary>
|
||||
[SerializeIgnore]
|
||||
public ICollection<Collection>? Collections { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Links to watch this movie.
|
||||
/// </summary>
|
||||
public VideoLinks Links =>
|
||||
new()
|
||||
{
|
||||
Direct = $"/video/movie/{Slug}/direct",
|
||||
Hls = $"/video/movie/{Slug}/master.m3u8",
|
||||
};
|
||||
|
||||
[SerializeIgnore]
|
||||
public ICollection<MovieWatchStatus>? Watched { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Metadata of what an user as started/planned to watch.
|
||||
/// </summary>
|
||||
[Projectable(UseMemberBody = nameof(_WatchStatus), OnlyOnInclude = true)]
|
||||
[LoadableRelation(
|
||||
Sql = "movie_watch_status",
|
||||
On = "movie_id = \"this\".id and \"relation\".user_id = [current_user]"
|
||||
)]
|
||||
public MovieWatchStatus? WatchStatus { get; set; }
|
||||
|
||||
// There is a global query filter to filter by user so we just need to do single.
|
||||
private MovieWatchStatus? _WatchStatus => Watched!.FirstOrDefault();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnMerge(object merged)
|
||||
{
|
||||
// if (People != null)
|
||||
// {
|
||||
// foreach (PeopleRole link in People)
|
||||
// link.Movie = this;
|
||||
// }
|
||||
}
|
||||
|
||||
public Movie() { }
|
||||
|
||||
[JsonConstructor]
|
||||
public Movie(string name)
|
||||
{
|
||||
if (name != null)
|
||||
{
|
||||
Slug = Utility.ToSlug(name);
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,20 +16,30 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
using Kyoo.Utils;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// An actor, voice actor, writer, animator, somebody who worked on a <see cref="Show"/>.
|
||||
/// </summary>
|
||||
public class People : IResource, IMetadata, IThumbnails
|
||||
[Table("people")]
|
||||
public class People : IQuery, IResource, IMetadata, IThumbnails
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public int ID { get; set; }
|
||||
public static Sort DefaultSort => new Sort<People>.By(x => x.Name);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[MaxLength(256)]
|
||||
public string Slug { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -38,14 +48,33 @@ namespace Kyoo.Abstractions.Models
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<int, string> Images { get; set; }
|
||||
public Image? Poster { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[EditableRelation] [LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; }
|
||||
public Image? Thumbnail { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image? Logo { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<string, MetadataId> ExternalId { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// The list of roles this person has played in. See <see cref="PeopleRole"/> for more information.
|
||||
/// </summary>
|
||||
[EditableRelation] [LoadableRelation] public ICollection<PeopleRole> Roles { get; set; }
|
||||
[SerializeIgnore]
|
||||
public ICollection<PeopleRole>? Roles { get; set; }
|
||||
|
||||
public People() { }
|
||||
|
||||
[JsonConstructor]
|
||||
public People(string name)
|
||||
{
|
||||
if (name != null)
|
||||
{
|
||||
Slug = Utility.ToSlug(name);
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
using Kyoo.Utils;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// This class contains metadata about <see cref="IMetadataProvider"/>.
|
||||
/// You can have providers even if you don't have the corresponding <see cref="IMetadataProvider"/>.
|
||||
/// </summary>
|
||||
public class Provider : IResource, IThumbnails
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public int ID { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Slug { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of this provider.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<int, string> Images { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of libraries that uses this provider.
|
||||
/// </summary>
|
||||
[LoadableRelation] public ICollection<Library> Libraries { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new, default, <see cref="Provider"/>
|
||||
/// </summary>
|
||||
public Provider() { }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="Provider"/> and specify it's <see cref="Name"/>.
|
||||
/// The <see cref="Slug"/> is automatically calculated from it's name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of this provider.</param>
|
||||
/// <param name="logo">The logo of this provider.</param>
|
||||
public Provider(string name, string logo)
|
||||
{
|
||||
Slug = Utility.ToSlug(name);
|
||||
Name = name;
|
||||
Images = new Dictionary<int, string>
|
||||
{
|
||||
[Models.Images.Logo] = logo
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,10 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text.RegularExpressions;
|
||||
using EntityFrameworkCore.Projectables;
|
||||
using JetBrains.Annotations;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
@@ -28,30 +31,34 @@ namespace Kyoo.Abstractions.Models
|
||||
/// <summary>
|
||||
/// A season of a <see cref="Show"/>.
|
||||
/// </summary>
|
||||
public class Season : IResource, IMetadata, IThumbnails
|
||||
public class Season : IQuery, IResource, IMetadata, IThumbnails, IAddedDate
|
||||
{
|
||||
public static Sort DefaultSort => new Sort<Season>.By(x => x.SeasonNumber);
|
||||
|
||||
/// <inheritdoc />
|
||||
public int ID { get; set; }
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[Computed]
|
||||
[MaxLength(256)]
|
||||
public string Slug
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ShowSlug == null && Show == null)
|
||||
return $"{ShowID}-s{SeasonNumber}";
|
||||
return $"{ShowId}-s{SeasonNumber}";
|
||||
return $"{ShowSlug ?? Show?.Slug}-s{SeasonNumber}";
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
[NotNull]
|
||||
private set
|
||||
{
|
||||
Match match = Regex.Match(value ?? string.Empty, @"(?<show>.+)-s(?<season>\d+)");
|
||||
Match match = Regex.Match(value, @"(?<show>.+)-s(?<season>\d+)");
|
||||
|
||||
if (!match.Success)
|
||||
throw new ArgumentException("Invalid season slug. Format: {showSlug}-s{seasonNumber}");
|
||||
throw new ArgumentException(
|
||||
"Invalid season slug. Format: {showSlug}-s{seasonNumber}"
|
||||
);
|
||||
ShowSlug = match.Groups["show"].Value;
|
||||
SeasonNumber = int.Parse(match.Groups["season"].Value);
|
||||
}
|
||||
@@ -60,18 +67,19 @@ namespace Kyoo.Abstractions.Models
|
||||
/// <summary>
|
||||
/// The slug of the Show that contain this episode. If this is not set, this season is ill-formed.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public string ShowSlug { private get; set; }
|
||||
[SerializeIgnore]
|
||||
public string? ShowSlug { private get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the Show containing this season.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public int ShowID { get; set; }
|
||||
public Guid ShowId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The show that contains this season.
|
||||
/// This must be explicitly loaded via a call to <see cref="ILibraryManager.Load"/>.
|
||||
/// </summary>
|
||||
[LoadableRelation(nameof(ShowID))] public Show Show { get; set; }
|
||||
[LoadableRelation(nameof(ShowId))]
|
||||
public Show? Show { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of this season. This can be set to 0 to indicate specials.
|
||||
@@ -81,32 +89,63 @@ namespace Kyoo.Abstractions.Models
|
||||
/// <summary>
|
||||
/// The title of this season.
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A quick overview of this season.
|
||||
/// </summary>
|
||||
public string Overview { get; set; }
|
||||
public string? Overview { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The starting air date of this season.
|
||||
/// </summary>
|
||||
public DateTime? StartDate { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public DateTime AddedDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ending date of this season.
|
||||
/// </summary>
|
||||
public DateTime? EndDate { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<int, string> Images { get; set; }
|
||||
public Image? Poster { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[EditableRelation] [LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; }
|
||||
public Image? Thumbnail { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image? Logo { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<string, MetadataId> ExternalId { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// The list of episodes that this season contains.
|
||||
/// </summary>
|
||||
[LoadableRelation] public ICollection<Episode> Episodes { get; set; }
|
||||
[SerializeIgnore]
|
||||
public ICollection<Episode>? Episodes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of episodes in this season.
|
||||
/// </summary>
|
||||
[Projectable(UseMemberBody = nameof(_EpisodesCount), OnlyOnInclude = true)]
|
||||
[NotMapped]
|
||||
[LoadableRelation(
|
||||
// language=PostgreSQL
|
||||
Projected = """
|
||||
(
|
||||
select
|
||||
count(*)::int
|
||||
from
|
||||
episodes as e
|
||||
where
|
||||
e.season_id = id) as episodes_count
|
||||
"""
|
||||
)]
|
||||
public int EpisodesCount { get; set; }
|
||||
|
||||
private int _EpisodesCount => Episodes!.Count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,42 +18,68 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using EntityFrameworkCore.Projectables;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
using Kyoo.Utils;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A series or a movie.
|
||||
/// </summary>
|
||||
public class Show : IResource, IMetadata, IOnMerge, IThumbnails
|
||||
public class Show
|
||||
: IQuery,
|
||||
IResource,
|
||||
IMetadata,
|
||||
IOnMerge,
|
||||
IThumbnails,
|
||||
IAddedDate,
|
||||
ILibraryItem,
|
||||
IWatchlist
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public int ID { get; set; }
|
||||
public static Sort DefaultSort => new Sort<Show>.By(x => x.Name);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[MaxLength(256)]
|
||||
public string Slug { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The title of this show.
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A catchphrase for this show.
|
||||
/// </summary>
|
||||
public string? Tagline { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of alternative titles of this show.
|
||||
/// </summary>
|
||||
[EditableRelation] public string[] Aliases { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The path of the root directory of this show.
|
||||
/// This can be any kind of path supported by <see cref="IFileSystem"/>
|
||||
/// </summary>
|
||||
[SerializeIgnore] public string Path { get; set; }
|
||||
public List<string> Aliases { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// The summary of this show.
|
||||
/// </summary>
|
||||
public string Overview { get; set; }
|
||||
public string? Overview { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of tags that match this movie.
|
||||
/// </summary>
|
||||
public List<string> Tags { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// The list of genres (themes) this show has.
|
||||
/// </summary>
|
||||
public List<Genre> Genres { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Is this show airing, not aired yet or finished?
|
||||
@@ -61,11 +87,9 @@ namespace Kyoo.Abstractions.Models
|
||||
public Status Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// An URL to a trailer. This could be any path supported by the <see cref="IFileSystem"/>.
|
||||
/// How well this item is rated? (from 0 to 100).
|
||||
/// </summary>
|
||||
/// TODO for now, this is set to a youtube url. It should be cached and converted to a local file.
|
||||
[Obsolete("Use Images instead of this, this is only kept for the API response.")]
|
||||
public string TrailerUrl => Images?.GetValueOrDefault(Models.Images.Trailer);
|
||||
public int Rating { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The date this show started airing. It can be null if this is unknown.
|
||||
@@ -74,74 +98,145 @@ namespace Kyoo.Abstractions.Models
|
||||
|
||||
/// <summary>
|
||||
/// The date this show finished airing.
|
||||
/// It must be after the <see cref="StartAir"/> but can be the same (example: for movies).
|
||||
/// It can also be null if this is unknown.
|
||||
/// </summary>
|
||||
public DateTime? EndAir { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<int, string> Images { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if this show represent a movie, false otherwise.
|
||||
/// </summary>
|
||||
public bool IsMovie { get; set; }
|
||||
public DateTime AddedDate { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[EditableRelation] [LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; }
|
||||
public Image? Poster { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image? Thumbnail { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Image? Logo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A video of a few minutes that tease the content.
|
||||
/// </summary>
|
||||
public string? Trailer { get; set; }
|
||||
|
||||
[SerializeIgnore]
|
||||
[Column("start_air")]
|
||||
public DateTime? AirDate => StartAir;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<string, MetadataId> ExternalId { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the Studio that made this show.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public int? StudioID { get; set; }
|
||||
[SerializeIgnore]
|
||||
public Guid? StudioId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Studio that made this show.
|
||||
/// This must be explicitly loaded via a call to <see cref="ILibraryManager.Load"/>.
|
||||
/// </summary>
|
||||
[LoadableRelation(nameof(StudioID))] [EditableRelation] public Studio Studio { get; set; }
|
||||
[LoadableRelation(nameof(StudioId))]
|
||||
public Studio? Studio { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of genres (themes) this show has.
|
||||
/// </summary>
|
||||
[LoadableRelation] [EditableRelation] public ICollection<Genre> Genres { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of people that made this show.
|
||||
/// </summary>
|
||||
[LoadableRelation] [EditableRelation] public ICollection<PeopleRole> People { get; set; }
|
||||
// /// <summary>
|
||||
// /// The list of people that made this show.
|
||||
// /// </summary>
|
||||
// [SerializeIgnore] public ICollection<PeopleRole>? People { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The different seasons in this show. If this is a movie, this list is always null or empty.
|
||||
/// </summary>
|
||||
[LoadableRelation] public ICollection<Season> Seasons { get; set; }
|
||||
[SerializeIgnore]
|
||||
public ICollection<Season>? Seasons { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of episodes in this show.
|
||||
/// If this is a movie, there will be a unique episode (with the seasonNumber and episodeNumber set to null).
|
||||
/// Having an episode is necessary to store metadata and tracks.
|
||||
/// </summary>
|
||||
[LoadableRelation] public ICollection<Episode> Episodes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of libraries that contains this show.
|
||||
/// </summary>
|
||||
[LoadableRelation] public ICollection<Library> Libraries { get; set; }
|
||||
[SerializeIgnore]
|
||||
public ICollection<Episode>? Episodes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of collections that contains this show.
|
||||
/// </summary>
|
||||
[LoadableRelation] public ICollection<Collection> Collections { get; set; }
|
||||
[SerializeIgnore]
|
||||
public ICollection<Collection>? Collections { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The first episode of this show.
|
||||
/// </summary>
|
||||
[Projectable(UseMemberBody = nameof(_FirstEpisode), OnlyOnInclude = true)]
|
||||
[LoadableRelation(
|
||||
// language=PostgreSQL
|
||||
Sql = """
|
||||
select
|
||||
fe.* -- Episode as fe
|
||||
from (
|
||||
select
|
||||
e.*,
|
||||
row_number() over (partition by e.show_id order by e.absolute_number, e.season_number, e.episode_number) as number
|
||||
from
|
||||
episodes as e) as "fe"
|
||||
where
|
||||
fe.number <= 1
|
||||
""",
|
||||
On = "show_id = \"this\".id"
|
||||
)]
|
||||
public Episode? FirstEpisode { get; set; }
|
||||
|
||||
private Episode? _FirstEpisode =>
|
||||
Episodes!
|
||||
.OrderBy(x => x.AbsoluteNumber)
|
||||
.ThenBy(x => x.SeasonNumber)
|
||||
.ThenBy(x => x.EpisodeNumber)
|
||||
.FirstOrDefault();
|
||||
|
||||
/// <summary>
|
||||
/// The number of episodes in this show.
|
||||
/// </summary>
|
||||
[Projectable(UseMemberBody = nameof(_EpisodesCount), OnlyOnInclude = true)]
|
||||
[NotMapped]
|
||||
[LoadableRelation(
|
||||
// language=PostgreSQL
|
||||
Projected = """
|
||||
(
|
||||
select
|
||||
count(*)::int
|
||||
from
|
||||
episodes as e
|
||||
where
|
||||
e.show_id = "this".id) as episodes_count
|
||||
"""
|
||||
)]
|
||||
public int EpisodesCount { get; set; }
|
||||
|
||||
private int _EpisodesCount => Episodes!.Count;
|
||||
|
||||
[SerializeIgnore]
|
||||
public ICollection<ShowWatchStatus>? Watched { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Metadata of what an user as started/planned to watch.
|
||||
/// </summary>
|
||||
[Projectable(UseMemberBody = nameof(_WatchStatus), OnlyOnInclude = true)]
|
||||
[LoadableRelation(
|
||||
Sql = "show_watch_status",
|
||||
On = "show_id = \"this\".id and \"relation\".user_id = [current_user]"
|
||||
)]
|
||||
public ShowWatchStatus? WatchStatus { get; set; }
|
||||
|
||||
// There is a global query filter to filter by user so we just need to do single.
|
||||
private ShowWatchStatus? _WatchStatus => Watched!.FirstOrDefault();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnMerge(object merged)
|
||||
{
|
||||
if (People != null)
|
||||
{
|
||||
foreach (PeopleRole link in People)
|
||||
link.Show = this;
|
||||
}
|
||||
|
||||
// if (People != null)
|
||||
// {
|
||||
// foreach (PeopleRole link in People)
|
||||
// link.Show = this;
|
||||
// }
|
||||
if (Seasons != null)
|
||||
{
|
||||
foreach (Season season in Seasons)
|
||||
@@ -154,6 +249,18 @@ namespace Kyoo.Abstractions.Models
|
||||
episode.Show = this;
|
||||
}
|
||||
}
|
||||
|
||||
public Show() { }
|
||||
|
||||
[JsonConstructor]
|
||||
public Show(string name)
|
||||
{
|
||||
if (name != null)
|
||||
{
|
||||
Slug = Utility.ToSlug(name);
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -16,21 +16,28 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
using Kyoo.Utils;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A studio that make shows.
|
||||
/// </summary>
|
||||
public class Studio : IResource, IMetadata
|
||||
public class Studio : IQuery, IResource, IMetadata
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public int ID { get; set; }
|
||||
public static Sort DefaultSort => new Sort<Studio>.By(x => x.Name);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[MaxLength(256)]
|
||||
public string Slug { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -41,10 +48,17 @@ namespace Kyoo.Abstractions.Models
|
||||
/// <summary>
|
||||
/// The list of shows that are made by this studio.
|
||||
/// </summary>
|
||||
[LoadableRelation] public ICollection<Show> Shows { get; set; }
|
||||
[SerializeIgnore]
|
||||
public ICollection<Show>? Shows { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of movies that are made by this studio.
|
||||
/// </summary>
|
||||
[SerializeIgnore]
|
||||
public ICollection<Movie>? Movies { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[EditableRelation] [LoadableRelation] public ICollection<MetadataID> ExternalIDs { get; set; }
|
||||
public Dictionary<string, MetadataId> ExternalId { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Create a new, empty, <see cref="Studio"/>.
|
||||
@@ -55,10 +69,14 @@ namespace Kyoo.Abstractions.Models
|
||||
/// Create a new <see cref="Studio"/> with a specific name, the slug is calculated automatically.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the studio.</param>
|
||||
[JsonConstructor]
|
||||
public Studio(string name)
|
||||
{
|
||||
Slug = Utility.ToSlug(name);
|
||||
Name = name;
|
||||
if (name != null)
|
||||
{
|
||||
Slug = Utility.ToSlug(name);
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,229 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using JetBrains.Annotations;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of available stream types.
|
||||
/// Attachments are only used temporarily by the transcoder but are not stored in a database.
|
||||
/// </summary>
|
||||
public enum StreamType
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of the stream is not known.
|
||||
/// </summary>
|
||||
Unknown = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The stream is a video.
|
||||
/// </summary>
|
||||
Video = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The stream is an audio.
|
||||
/// </summary>
|
||||
Audio = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The stream is a subtitle.
|
||||
/// </summary>
|
||||
Subtitle = 3,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A video, audio or subtitle track for an episode.
|
||||
/// </summary>
|
||||
public class Track : IResource, ILink
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public int ID { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[Computed]
|
||||
public string Slug
|
||||
{
|
||||
get
|
||||
{
|
||||
string type = Type.ToString().ToLowerInvariant();
|
||||
string index = TrackIndex != 0 ? $"-{TrackIndex}" : string.Empty;
|
||||
string episode = _episodeSlug ?? Episode?.Slug ?? EpisodeID.ToString(CultureInfo.InvariantCulture);
|
||||
return $"{episode}.{Language ?? "und"}{index}{(IsForced ? ".forced" : string.Empty)}.{type}";
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
private set
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
Match match = Regex.Match(value,
|
||||
@"(?<ep>[^\.]+)\.(?<lang>\w{0,3})(-(?<index>\d+))?(\.(?<forced>forced))?\.(?<type>\w+)(\.\w*)?");
|
||||
|
||||
if (!match.Success)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Invalid track slug. " +
|
||||
"Format: {episodeSlug}.{language}[-{index}][.forced].{type}[.{extension}]"
|
||||
);
|
||||
}
|
||||
|
||||
_episodeSlug = match.Groups["ep"].Value;
|
||||
Language = match.Groups["lang"].Value;
|
||||
if (Language == "und")
|
||||
Language = null;
|
||||
TrackIndex = match.Groups["index"].Success ? int.Parse(match.Groups["index"].Value, CultureInfo.InvariantCulture) : 0;
|
||||
IsForced = match.Groups["forced"].Success;
|
||||
Type = Enum.Parse<StreamType>(match.Groups["type"].Value, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The title of the stream.
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The language of this stream (as a ISO-639-2 language code)
|
||||
/// </summary>
|
||||
public string Language { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The codec of this stream.
|
||||
/// </summary>
|
||||
public string Codec { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is this stream the default one of it's type?
|
||||
/// </summary>
|
||||
public bool IsDefault { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is this stream tagged as forced?
|
||||
/// </summary>
|
||||
public bool IsForced { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is this track extern to the episode's file?
|
||||
/// </summary>
|
||||
public bool IsExternal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The path of this track.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public string Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of this stream.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public StreamType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the episode that uses this track.
|
||||
/// </summary>
|
||||
[SerializeIgnore] public int EpisodeID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The episode that uses this track.
|
||||
/// </summary>
|
||||
[LoadableRelation(nameof(EpisodeID))]
|
||||
public Episode Episode
|
||||
{
|
||||
get => _episode;
|
||||
set
|
||||
{
|
||||
_episode = value;
|
||||
if (_episode != null)
|
||||
_episodeSlug = _episode.Slug;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The index of this track on the episode.
|
||||
/// </summary>
|
||||
public int TrackIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A user-friendly name for this track. It does not include the track type.
|
||||
/// </summary>
|
||||
public string DisplayName
|
||||
{
|
||||
get
|
||||
{
|
||||
string language = _GetLanguage(Language);
|
||||
|
||||
if (language == null)
|
||||
return $"Unknown (index: {TrackIndex})";
|
||||
CultureInfo info = CultureInfo.GetCultures(CultureTypes.NeutralCultures)
|
||||
.FirstOrDefault(x => x.ThreeLetterISOLanguageName == language);
|
||||
string name = info?.EnglishName ?? language;
|
||||
if (IsForced)
|
||||
name += " Forced";
|
||||
if (IsExternal)
|
||||
name += " (External)";
|
||||
if (Title is { Length: > 1 })
|
||||
name += " - " + Title;
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The slug of the episode that contain this track. If this is not set, this track is ill-formed.
|
||||
/// </summary>
|
||||
[SerializeIgnore] private string _episodeSlug;
|
||||
|
||||
/// <summary>
|
||||
/// The episode that uses this track.
|
||||
/// This is the baking field of <see cref="Episode"/>.
|
||||
/// </summary>
|
||||
[SerializeIgnore] private Episode _episode;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public object Link => Type == StreamType.Subtitle ? $"/subtitle/{Slug}" : null;
|
||||
|
||||
// Converting mkv track language to c# system language tag.
|
||||
private static string _GetLanguage(string mkvLanguage)
|
||||
{
|
||||
// TODO delete this and have a real way to get the language string from the ISO-639-2.
|
||||
return mkvLanguage switch
|
||||
{
|
||||
"fre" => "fra",
|
||||
null => "und",
|
||||
_ => mkvLanguage
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utility method to create a track slug from a incomplete slug (only add the type of the track).
|
||||
/// </summary>
|
||||
/// <param name="baseSlug">The slug to edit</param>
|
||||
/// <param name="type">The new type of this </param>
|
||||
/// <returns>The completed slug.</returns>
|
||||
public static string BuildSlug(string baseSlug, StreamType type)
|
||||
{
|
||||
return baseSlug.EndsWith($".{type}", StringComparison.InvariantCultureIgnoreCase)
|
||||
? baseSlug
|
||||
: $"{baseSlug}.{type.ToString().ToLowerInvariant()}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,20 +16,28 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
using Kyoo.Utils;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A single user of the app.
|
||||
/// </summary>
|
||||
public class User : IResource, IThumbnails
|
||||
public class User : IQuery, IResource, IAddedDate
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public int ID { get; set; }
|
||||
public static Sort DefaultSort => new Sort<User>.By(x => x.Username);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[MaxLength(256)]
|
||||
public string Slug { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -51,27 +59,32 @@ namespace Kyoo.Abstractions.Models
|
||||
/// <summary>
|
||||
/// The list of permissions of the user. The format of this is implementation dependent.
|
||||
/// </summary>
|
||||
public string[] Permissions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Arbitrary extra data that can be used by specific authentication implementations.
|
||||
/// </summary>
|
||||
[SerializeIgnore]
|
||||
public Dictionary<string, string> ExtraData { get; set; }
|
||||
public string[] Permissions { get; set; } = Array.Empty<string>();
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<int, string> Images { get; set; }
|
||||
public DateTime AddedDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of shows the user has finished.
|
||||
/// A logo is a small image representing the resource.
|
||||
/// </summary>
|
||||
[SerializeIgnore]
|
||||
public ICollection<Show> Watched { get; set; }
|
||||
public Image? Logo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of episodes the user is watching (stopped in progress or the next episode of the show)
|
||||
/// </summary>
|
||||
[SerializeIgnore]
|
||||
public ICollection<WatchedEpisode> CurrentlyWatching { get; set; }
|
||||
// /// <summary>
|
||||
// /// The user's watch list.
|
||||
// /// </summary>
|
||||
// // [SerializeIgnore]
|
||||
// // public ICollection<WatchInfo>? Watchlist { get; set; }
|
||||
|
||||
public User() { }
|
||||
|
||||
[JsonConstructor]
|
||||
public User(string username)
|
||||
{
|
||||
if (username != null)
|
||||
{
|
||||
Slug = Utility.ToSlug(username);
|
||||
Username = username;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
239
back/src/Kyoo.Abstractions/Models/Resources/WatchStatus.cs
Normal file
239
back/src/Kyoo.Abstractions/Models/Resources/WatchStatus.cs
Normal file
@@ -0,0 +1,239 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Has the user started watching, is it planned?
|
||||
/// </summary>
|
||||
public enum WatchStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// The user has already watched this.
|
||||
/// </summary>
|
||||
Completed,
|
||||
|
||||
/// <summary>
|
||||
/// The user started watching this but has not finished.
|
||||
/// </summary>
|
||||
Watching,
|
||||
|
||||
/// <summary>
|
||||
/// The user does not plan to continue watching.
|
||||
/// </summary>
|
||||
Droped,
|
||||
|
||||
/// <summary>
|
||||
/// The user has not started watching this but plans to.
|
||||
/// </summary>
|
||||
Planned,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Metadata of what an user as started/planned to watch.
|
||||
/// </summary>
|
||||
[SqlFirstColumn(nameof(UserId))]
|
||||
public class MovieWatchStatus : IAddedDate
|
||||
{
|
||||
/// <summary>
|
||||
/// The ID of the user that started watching this episode.
|
||||
/// </summary>
|
||||
[SerializeIgnore]
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The user that started watching this episode.
|
||||
/// </summary>
|
||||
[SerializeIgnore]
|
||||
public User User { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the movie started.
|
||||
/// </summary>
|
||||
[SerializeIgnore]
|
||||
public Guid MovieId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Movie"/> started.
|
||||
/// </summary>
|
||||
[SerializeIgnore]
|
||||
public Movie Movie { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DateTime AddedDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The date at which this item was played.
|
||||
/// </summary>
|
||||
public DateTime? PlayedDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Has the user started watching, is it planned?
|
||||
/// </summary>
|
||||
public WatchStatus Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Where the player has stopped watching the movie (in seconds).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Null if the status is not Watching.
|
||||
/// </remarks>
|
||||
public int? WatchedTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Where the player has stopped watching the movie (in percentage between 0 and 100).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Null if the status is not Watching.
|
||||
/// </remarks>
|
||||
public int? WatchedPercent { get; set; }
|
||||
}
|
||||
|
||||
[SqlFirstColumn(nameof(UserId))]
|
||||
public class EpisodeWatchStatus : IAddedDate
|
||||
{
|
||||
/// <summary>
|
||||
/// The ID of the user that started watching this episode.
|
||||
/// </summary>
|
||||
[SerializeIgnore]
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The user that started watching this episode.
|
||||
/// </summary>
|
||||
[SerializeIgnore]
|
||||
public User User { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the episode started.
|
||||
/// </summary>
|
||||
[SerializeIgnore]
|
||||
public Guid? EpisodeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Episode"/> started.
|
||||
/// </summary>
|
||||
[SerializeIgnore]
|
||||
public Episode Episode { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DateTime AddedDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The date at which this item was played.
|
||||
/// </summary>
|
||||
public DateTime? PlayedDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Has the user started watching, is it planned?
|
||||
/// </summary>
|
||||
public WatchStatus Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Where the player has stopped watching the episode (in seconds).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Null if the status is not Watching.
|
||||
/// </remarks>
|
||||
public int? WatchedTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Where the player has stopped watching the episode (in percentage between 0 and 100).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Null if the status is not Watching or if the next episode is not started.
|
||||
/// </remarks>
|
||||
public int? WatchedPercent { get; set; }
|
||||
}
|
||||
|
||||
[SqlFirstColumn(nameof(UserId))]
|
||||
public class ShowWatchStatus : IAddedDate
|
||||
{
|
||||
/// <summary>
|
||||
/// The ID of the user that started watching this episode.
|
||||
/// </summary>
|
||||
[SerializeIgnore]
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The user that started watching this episode.
|
||||
/// </summary>
|
||||
[SerializeIgnore]
|
||||
public User User { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the show started.
|
||||
/// </summary>
|
||||
[SerializeIgnore]
|
||||
public Guid ShowId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Show"/> started.
|
||||
/// </summary>
|
||||
[SerializeIgnore]
|
||||
public Show Show { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DateTime AddedDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The date at which this item was played.
|
||||
/// </summary>
|
||||
public DateTime? PlayedDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Has the user started watching, is it planned?
|
||||
/// </summary>
|
||||
public WatchStatus Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of episodes the user has not seen.
|
||||
/// </summary>
|
||||
public int UnseenEpisodesCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the episode started.
|
||||
/// </summary>
|
||||
[SerializeIgnore]
|
||||
public Guid? NextEpisodeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The next <see cref="Episode"/> to watch.
|
||||
/// </summary>
|
||||
public Episode? NextEpisode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Where the player has stopped watching the episode (in seconds).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Null if the status is not Watching or if the next episode is not started.
|
||||
/// </remarks>
|
||||
public int? WatchedTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Where the player has stopped watching the episode (in percentage between 0 and 100).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Null if the status is not Watching or if the next episode is not started.
|
||||
/// </remarks>
|
||||
public int? WatchedPercent { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -16,31 +16,39 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Metadata of episode currently watching by an user
|
||||
/// Results of a search request.
|
||||
/// </summary>
|
||||
public class WatchedEpisode
|
||||
/// <typeparam name="T">The search item's type.</typeparam>
|
||||
public class SearchPage<T> : Page<T>
|
||||
where T : IResource
|
||||
{
|
||||
/// <summary>
|
||||
/// The ID of the user that started watching this episode.
|
||||
/// </summary>
|
||||
public int UserID { get; set; }
|
||||
public SearchPage(
|
||||
SearchResult result,
|
||||
string @this,
|
||||
string? previous,
|
||||
string? next,
|
||||
string first
|
||||
)
|
||||
: base(result.Items, @this, previous, next, first)
|
||||
{
|
||||
Query = result.Query;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the episode started.
|
||||
/// The query of the search request.
|
||||
/// </summary>
|
||||
public int EpisodeID { get; set; }
|
||||
public string? Query { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Episode"/> started.
|
||||
/// </summary>
|
||||
public Episode Episode { get; set; }
|
||||
public class SearchResult
|
||||
{
|
||||
public string? Query { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Where the player has stopped watching the episode (between 0 and 100).
|
||||
/// </summary>
|
||||
public int WatchedPercentage { get; set; }
|
||||
public ICollection<T> Items { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Results of a search request.
|
||||
/// </summary>
|
||||
public class SearchResult
|
||||
{
|
||||
/// <summary>
|
||||
/// The query of the search request.
|
||||
/// </summary>
|
||||
public string Query { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The collections that matched the search.
|
||||
/// </summary>
|
||||
public ICollection<Collection> Collections { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The shows that matched the search.
|
||||
/// </summary>
|
||||
public ICollection<Show> Shows { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The episodes that matched the search.
|
||||
/// </summary>
|
||||
public ICollection<Episode> Episodes { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The people that matched the search.
|
||||
/// </summary>
|
||||
public ICollection<People> People { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The genres that matched the search.
|
||||
/// </summary>
|
||||
public ICollection<Genre> Genres { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The studios that matched the search.
|
||||
/// </summary>
|
||||
public ICollection<Studio> Studios { get; init; }
|
||||
}
|
||||
}
|
||||
371
back/src/Kyoo.Abstractions/Models/Utils/Filter.cs
Normal file
371
back/src/Kyoo.Abstractions/Models/Utils/Filter.cs
Normal file
@@ -0,0 +1,371 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
using Sprache;
|
||||
|
||||
namespace Kyoo.Abstractions.Models.Utils;
|
||||
|
||||
public static class ParseHelper
|
||||
{
|
||||
public static Parser<T> ErrorMessage<T>(this Parser<T> @this, string message) =>
|
||||
input =>
|
||||
{
|
||||
IResult<T> result = @this(input);
|
||||
|
||||
return result.WasSuccessful
|
||||
? result
|
||||
: Result.Failure<T>(result.Remainder, message, result.Expectations);
|
||||
};
|
||||
|
||||
public static Parser<T> Error<T>(string message) =>
|
||||
input =>
|
||||
{
|
||||
return Result.Failure<T>(input, message, Array.Empty<string>());
|
||||
};
|
||||
}
|
||||
|
||||
public abstract record Filter
|
||||
{
|
||||
public static Filter<T>? And<T>(params Filter<T>?[] filters)
|
||||
{
|
||||
return filters
|
||||
.Where(x => x != null)
|
||||
.Aggregate(
|
||||
(Filter<T>?)null,
|
||||
(acc, filter) =>
|
||||
{
|
||||
if (acc == null)
|
||||
return filter;
|
||||
return new Filter<T>.And(acc, filter!);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public static Filter<T>? Or<T>(params Filter<T>?[] filters)
|
||||
{
|
||||
return filters
|
||||
.Where(x => x != null)
|
||||
.Aggregate(
|
||||
(Filter<T>?)null,
|
||||
(acc, filter) =>
|
||||
{
|
||||
if (acc == null)
|
||||
return filter;
|
||||
return new Filter<T>.Or(acc, filter!);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract record Filter<T> : Filter
|
||||
{
|
||||
public record And(Filter<T> First, Filter<T> Second) : Filter<T>;
|
||||
|
||||
public record Or(Filter<T> First, Filter<T> Second) : Filter<T>;
|
||||
|
||||
public record Not(Filter<T> Filter) : Filter<T>;
|
||||
|
||||
public record Eq(string Property, object? Value) : Filter<T>;
|
||||
|
||||
public record Ne(string Property, object? Value) : Filter<T>;
|
||||
|
||||
public record Gt(string Property, object Value) : Filter<T>;
|
||||
|
||||
public record Ge(string Property, object Value) : Filter<T>;
|
||||
|
||||
public record Lt(string Property, object Value) : Filter<T>;
|
||||
|
||||
public record Le(string Property, object Value) : Filter<T>;
|
||||
|
||||
public record Has(string Property, object Value) : Filter<T>;
|
||||
|
||||
/// <summary>
|
||||
/// Internal filter used for keyset paginations to resume random sorts.
|
||||
/// The pseudo sql is md5(seed || table.id) = md5(seed || 'hardCodedId')
|
||||
/// </summary>
|
||||
public record CmpRandom(string cmp, string Seed, Guid ReferenceId) : Filter<T>;
|
||||
|
||||
/// <summary>
|
||||
/// Internal filter used only in EF with hard coded lamdas (used for relations).
|
||||
/// </summary>
|
||||
public record Lambda(Expression<Func<T, bool>> Inner) : Filter<T>;
|
||||
|
||||
public static class FilterParsers
|
||||
{
|
||||
public static readonly Parser<Filter<T>> Filter = Parse
|
||||
.Ref(() => Bracket)
|
||||
.Or(Parse.Ref(() => Not))
|
||||
.Or(Parse.Ref(() => Eq))
|
||||
.Or(Parse.Ref(() => Ne))
|
||||
.Or(Parse.Ref(() => Gt))
|
||||
.Or(Parse.Ref(() => Ge))
|
||||
.Or(Parse.Ref(() => Lt))
|
||||
.Or(Parse.Ref(() => Le))
|
||||
.Or(Parse.Ref(() => Has));
|
||||
|
||||
public static readonly Parser<Filter<T>> CompleteFilter = Parse
|
||||
.Ref(() => Or)
|
||||
.Or(Parse.Ref(() => And))
|
||||
.Or(Filter);
|
||||
|
||||
public static readonly Parser<Filter<T>> Bracket =
|
||||
from open in Parse.Char('(').Token()
|
||||
from filter in CompleteFilter
|
||||
from close in Parse.Char(')').Token()
|
||||
select filter;
|
||||
|
||||
public static readonly Parser<IEnumerable<char>> AndOperator = Parse
|
||||
.IgnoreCase("and")
|
||||
.Or(Parse.String("&&"))
|
||||
.Token();
|
||||
|
||||
public static readonly Parser<IEnumerable<char>> OrOperator = Parse
|
||||
.IgnoreCase("or")
|
||||
.Or(Parse.String("||"))
|
||||
.Token();
|
||||
|
||||
public static readonly Parser<Filter<T>> And = Parse.ChainOperator(
|
||||
AndOperator,
|
||||
Filter,
|
||||
(_, a, b) => new And(a, b)
|
||||
);
|
||||
|
||||
public static readonly Parser<Filter<T>> Or = Parse.ChainOperator(
|
||||
OrOperator,
|
||||
And.Or(Filter),
|
||||
(_, a, b) => new Or(a, b)
|
||||
);
|
||||
|
||||
public static readonly Parser<Filter<T>> Not =
|
||||
from not in Parse.IgnoreCase("not").Or(Parse.String("!")).Token()
|
||||
from filter in CompleteFilter
|
||||
select new Not(filter);
|
||||
|
||||
private static Parser<object> _GetValueParser(Type type)
|
||||
{
|
||||
Type? nullable = Nullable.GetUnderlyingType(type);
|
||||
if (nullable != null)
|
||||
{
|
||||
return from value in _GetValueParser(nullable) select value;
|
||||
}
|
||||
|
||||
if (type == typeof(int))
|
||||
return Parse.Number.Select(x => int.Parse(x) as object);
|
||||
|
||||
if (type == typeof(float))
|
||||
{
|
||||
return from a in Parse.Number
|
||||
from dot in Parse.Char('.')
|
||||
from b in Parse.Number
|
||||
select float.Parse($"{a}.{b}") as object;
|
||||
}
|
||||
|
||||
if (type == typeof(Guid))
|
||||
{
|
||||
return from guid in Parse.Regex(
|
||||
@"[({]?[a-fA-F0-9]{8}[-]?([a-fA-F0-9]{4}[-]?){3}[a-fA-F0-9]{12}[})]?",
|
||||
"Guid"
|
||||
)
|
||||
select Guid.Parse(guid) as object;
|
||||
}
|
||||
|
||||
if (type == typeof(string))
|
||||
{
|
||||
return (
|
||||
from lq in Parse.Char('"').Or(Parse.Char('\''))
|
||||
from str in Parse.AnyChar.Where(x => x is not '"' and not '\'').Many().Text()
|
||||
from rq in Parse.Char('"').Or(Parse.Char('\''))
|
||||
select str
|
||||
).Or(Parse.LetterOrDigit.Many().Text());
|
||||
}
|
||||
|
||||
if (type.IsEnum)
|
||||
{
|
||||
return Parse
|
||||
.LetterOrDigit
|
||||
.Many()
|
||||
.Text()
|
||||
.Then(x =>
|
||||
{
|
||||
if (Enum.TryParse(type, x, true, out object? value))
|
||||
return Parse.Return(value);
|
||||
return ParseHelper.Error<object>($"Invalid enum value. Unexpected {x}");
|
||||
});
|
||||
}
|
||||
|
||||
if (type == typeof(DateTime))
|
||||
{
|
||||
return from year in Parse.Digit.Repeat(4).Text().Select(int.Parse)
|
||||
from yd in Parse.Char('-')
|
||||
from mouth in Parse.Digit.Repeat(2).Text().Select(int.Parse)
|
||||
from md in Parse.Char('-')
|
||||
from day in Parse.Digit.Repeat(2).Text().Select(int.Parse)
|
||||
select new DateTime(year, mouth, day) as object;
|
||||
}
|
||||
|
||||
if (typeof(IEnumerable).IsAssignableFrom(type))
|
||||
return ParseHelper.Error<object>(
|
||||
"Can't filter a list with a default comparator, use the 'has' filter."
|
||||
);
|
||||
return ParseHelper.Error<object>("Unfilterable field found");
|
||||
}
|
||||
|
||||
private static Parser<Filter<T>> _GetOperationParser(
|
||||
Parser<object> op,
|
||||
Func<string, object, Filter<T>> apply,
|
||||
Func<Type, Parser<object?>>? customTypeParser = null
|
||||
)
|
||||
{
|
||||
Parser<string> property = Parse.LetterOrDigit.AtLeastOnce().Text();
|
||||
|
||||
return property.Then(prop =>
|
||||
{
|
||||
Type[] types =
|
||||
typeof(T).GetCustomAttribute<OneOfAttribute>()?.Types ?? new[] { typeof(T) };
|
||||
|
||||
if (string.Equals(prop, "kind", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return from eq in op
|
||||
from val in types
|
||||
.Select(x => Parse.IgnoreCase(x.Name).Text())
|
||||
.Aggregate(
|
||||
null as Parser<string>,
|
||||
(acc, x) => acc == null ? x : Parse.Or(acc, x)
|
||||
)
|
||||
select apply("kind", val);
|
||||
}
|
||||
|
||||
PropertyInfo? propInfo = types
|
||||
.Select(
|
||||
x =>
|
||||
x.GetProperty(
|
||||
prop,
|
||||
BindingFlags.IgnoreCase
|
||||
| BindingFlags.Public
|
||||
| BindingFlags.Instance
|
||||
)
|
||||
)
|
||||
.FirstOrDefault();
|
||||
if (propInfo == null)
|
||||
return ParseHelper.Error<Filter<T>>($"The given filter '{prop}' is invalid.");
|
||||
|
||||
Parser<object?> value =
|
||||
customTypeParser != null
|
||||
? customTypeParser(propInfo.PropertyType)
|
||||
: _GetValueParser(propInfo.PropertyType);
|
||||
|
||||
return from eq in op
|
||||
from val in value
|
||||
select apply(propInfo.Name, val);
|
||||
});
|
||||
}
|
||||
|
||||
public static readonly Parser<Filter<T>> Eq = _GetOperationParser(
|
||||
Parse.IgnoreCase("eq").Or(Parse.String("=")).Token(),
|
||||
(property, value) => new Eq(property, value),
|
||||
(Type type) =>
|
||||
{
|
||||
Type? inner = Nullable.GetUnderlyingType(type);
|
||||
if (inner == null)
|
||||
return _GetValueParser(type);
|
||||
return Parse
|
||||
.String("null")
|
||||
.Token()
|
||||
.Return((object?)null)
|
||||
.Or(_GetValueParser(inner));
|
||||
}
|
||||
);
|
||||
|
||||
public static readonly Parser<Filter<T>> Ne = _GetOperationParser(
|
||||
Parse.IgnoreCase("ne").Or(Parse.String("!=")).Token(),
|
||||
(property, value) => new Ne(property, value),
|
||||
(Type type) =>
|
||||
{
|
||||
Type? inner = Nullable.GetUnderlyingType(type);
|
||||
if (inner == null)
|
||||
return _GetValueParser(type);
|
||||
return Parse
|
||||
.String("null")
|
||||
.Token()
|
||||
.Return((object?)null)
|
||||
.Or(_GetValueParser(inner));
|
||||
}
|
||||
);
|
||||
|
||||
public static readonly Parser<Filter<T>> Gt = _GetOperationParser(
|
||||
Parse.IgnoreCase("gt").Or(Parse.String(">")).Token(),
|
||||
(property, value) => new Gt(property, value)
|
||||
);
|
||||
|
||||
public static readonly Parser<Filter<T>> Ge = _GetOperationParser(
|
||||
Parse.IgnoreCase("ge").Or(Parse.String(">=")).Token(),
|
||||
(property, value) => new Ge(property, value)
|
||||
);
|
||||
|
||||
public static readonly Parser<Filter<T>> Lt = _GetOperationParser(
|
||||
Parse.IgnoreCase("lt").Or(Parse.String("<")).Token(),
|
||||
(property, value) => new Lt(property, value)
|
||||
);
|
||||
|
||||
public static readonly Parser<Filter<T>> Le = _GetOperationParser(
|
||||
Parse.IgnoreCase("le").Or(Parse.String("<=")).Token(),
|
||||
(property, value) => new Le(property, value)
|
||||
);
|
||||
|
||||
public static readonly Parser<Filter<T>> Has = _GetOperationParser(
|
||||
Parse.IgnoreCase("has").Token(),
|
||||
(property, value) => new Has(property, value),
|
||||
(Type type) =>
|
||||
{
|
||||
if (typeof(IEnumerable).IsAssignableFrom(type) && type != typeof(string))
|
||||
return _GetValueParser(
|
||||
type.GetElementType() ?? type.GenericTypeArguments.First()
|
||||
);
|
||||
return ParseHelper.Error<object>("Can't use 'has' on a non-list.");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public static Filter<T>? From(string? filter)
|
||||
{
|
||||
if (filter == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
IResult<Filter<T>> ret = FilterParsers.CompleteFilter.End().TryParse(filter);
|
||||
if (ret.WasSuccessful)
|
||||
return ret.Value;
|
||||
throw new ValidationException(
|
||||
$"Could not parse filter argument: {ret.Message}. Not parsed: {filter[ret.Remainder.Position..]}"
|
||||
);
|
||||
}
|
||||
catch (ParseException ex)
|
||||
{
|
||||
throw new ValidationException($"Could not parse filter argument: {ex.Message}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Kyoo.Abstractions.Models.Utils
|
||||
{
|
||||
@@ -38,18 +37,18 @@ namespace Kyoo.Abstractions.Models.Utils
|
||||
/// <summary>
|
||||
/// The ID of the resource or null if the slug is specified.
|
||||
/// </summary>
|
||||
private readonly int? _id;
|
||||
private readonly Guid? _id;
|
||||
|
||||
/// <summary>
|
||||
/// The slug of the resource or null if the id is specified.
|
||||
/// </summary>
|
||||
private readonly string _slug;
|
||||
private readonly string? _slug;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="Identifier"/> for the given id.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the resource.</param>
|
||||
public Identifier(int id)
|
||||
public Identifier(Guid id)
|
||||
{
|
||||
_id = id;
|
||||
}
|
||||
@@ -58,10 +57,8 @@ namespace Kyoo.Abstractions.Models.Utils
|
||||
/// Create a new <see cref="Identifier"/> for the given slug.
|
||||
/// </summary>
|
||||
/// <param name="slug">The slug of the resource.</param>
|
||||
public Identifier([NotNull] string slug)
|
||||
public Identifier(string slug)
|
||||
{
|
||||
if (slug == null)
|
||||
throw new ArgumentNullException(nameof(slug));
|
||||
_slug = slug;
|
||||
}
|
||||
|
||||
@@ -83,11 +80,9 @@ namespace Kyoo.Abstractions.Models.Utils
|
||||
/// );
|
||||
/// </code>
|
||||
/// </example>
|
||||
public T Match<T>(Func<int, T> idFunc, Func<string, T> slugFunc)
|
||||
public T Match<T>(Func<Guid, T> idFunc, Func<string, T> slugFunc)
|
||||
{
|
||||
return _id.HasValue
|
||||
? idFunc(_id.Value)
|
||||
: slugFunc(_slug);
|
||||
return _id.HasValue ? idFunc(_id.Value) : slugFunc(_slug!);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -102,31 +97,47 @@ namespace Kyoo.Abstractions.Models.Utils
|
||||
/// identifier.Matcher<Season>(x => x.ShowID, x => x.Show.Slug)
|
||||
/// </code>
|
||||
/// </example>
|
||||
public Expression<Func<T, bool>> Matcher<T>(Expression<Func<T, int>> idGetter,
|
||||
Expression<Func<T, string>> slugGetter)
|
||||
public Filter<T> Matcher<T>(
|
||||
Expression<Func<T, Guid>> idGetter,
|
||||
Expression<Func<T, string>> slugGetter
|
||||
)
|
||||
{
|
||||
ConstantExpression self = Expression.Constant(_id.HasValue ? _id.Value : _slug);
|
||||
BinaryExpression equal = Expression.Equal(_id.HasValue ? idGetter.Body : slugGetter.Body, self);
|
||||
ICollection<ParameterExpression> parameters = _id.HasValue ? idGetter.Parameters : slugGetter.Parameters;
|
||||
return Expression.Lambda<Func<T, bool>>(equal, parameters);
|
||||
BinaryExpression equal = Expression.Equal(
|
||||
_id.HasValue ? idGetter.Body : slugGetter.Body,
|
||||
self
|
||||
);
|
||||
ICollection<ParameterExpression> parameters = _id.HasValue
|
||||
? idGetter.Parameters
|
||||
: slugGetter.Parameters;
|
||||
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(equal, parameters);
|
||||
return new Filter<T>.Lambda(lambda);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A matcher overload for nullable IDs. See
|
||||
/// <see cref="Matcher{T}(System.Linq.Expressions.Expression{System.Func{T,int}},System.Linq.Expressions.Expression{System.Func{T,string}})"/>
|
||||
/// <see cref="Matcher{T}(Expression{Func{T,Guid}},Expression{Func{T,string}})"/>
|
||||
/// for more details.
|
||||
/// </summary>
|
||||
/// <param name="idGetter">An expression to retrieve an ID from the type <typeparamref name="T"/>.</param>
|
||||
/// <param name="slugGetter">An expression to retrieve a slug from the type <typeparamref name="T"/>.</param>
|
||||
/// <typeparam name="T">The type to match against this identifier.</typeparam>
|
||||
/// <returns>An expression to match the type <typeparamref name="T"/> to this identifier.</returns>
|
||||
public Expression<Func<T, bool>> Matcher<T>(Expression<Func<T, int?>> idGetter,
|
||||
Expression<Func<T, string>> slugGetter)
|
||||
public Filter<T> Matcher<T>(
|
||||
Expression<Func<T, Guid?>> idGetter,
|
||||
Expression<Func<T, string>> slugGetter
|
||||
)
|
||||
{
|
||||
ConstantExpression self = Expression.Constant(_id.HasValue ? _id.Value : _slug);
|
||||
BinaryExpression equal = Expression.Equal(_id.HasValue ? idGetter.Body : slugGetter.Body, self);
|
||||
ICollection<ParameterExpression> parameters = _id.HasValue ? idGetter.Parameters : slugGetter.Parameters;
|
||||
return Expression.Lambda<Func<T, bool>>(equal, parameters);
|
||||
BinaryExpression equal = Expression.Equal(
|
||||
_id.HasValue ? idGetter.Body : slugGetter.Body,
|
||||
self
|
||||
);
|
||||
ICollection<ParameterExpression> parameters = _id.HasValue
|
||||
? idGetter.Parameters
|
||||
: slugGetter.Parameters;
|
||||
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(equal, parameters);
|
||||
return new Filter<T>.Lambda(lambda);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -138,25 +149,28 @@ namespace Kyoo.Abstractions.Models.Utils
|
||||
/// </returns>
|
||||
public bool IsSame(IResource resource)
|
||||
{
|
||||
return Match(
|
||||
id => resource.ID == id,
|
||||
slug => resource.Slug == slug
|
||||
);
|
||||
return Match(id => resource.Id == id, slug => resource.Slug == slug);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return an expression that return true if this <see cref="Identifier"/> match a given resource.
|
||||
/// Return a filter to get this <see cref="Identifier"/> match a given resource.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of resource to match against.</typeparam>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the given resource match this identifier, <c>false</c> otherwise.
|
||||
/// </returns>
|
||||
public Expression<Func<T, bool>> IsSame<T>()
|
||||
public Filter<T> IsSame<T>()
|
||||
where T : IResource
|
||||
{
|
||||
return _id.HasValue
|
||||
? x => x.ID == _id.Value
|
||||
: x => x.Slug == _slug;
|
||||
? new Filter<T>.Eq("Id", _id.Value)
|
||||
: new Filter<T>.Eq("Slug", _slug!);
|
||||
}
|
||||
|
||||
private Expression<Func<T, bool>> _IsSameExpression<T>()
|
||||
where T : IResource
|
||||
{
|
||||
return _id.HasValue ? x => x.Id == _id.Value : x => x.Slug == _slug;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -166,7 +180,7 @@ namespace Kyoo.Abstractions.Models.Utils
|
||||
/// <typeparam name="T">The type that contain the list to check.</typeparam>
|
||||
/// <typeparam name="T2">The type of resource to check this identifier against.</typeparam>
|
||||
/// <returns>An expression to check if this <see cref="Identifier"/> is contained.</returns>
|
||||
public Expression<Func<T, bool>> IsContainedIn<T, T2>(Expression<Func<T, IEnumerable<T2>>> listGetter)
|
||||
public Filter<T> IsContainedIn<T, T2>(Expression<Func<T, IEnumerable<T2>?>> listGetter)
|
||||
where T2 : IResource
|
||||
{
|
||||
MethodInfo method = typeof(Enumerable)
|
||||
@@ -174,16 +188,23 @@ namespace Kyoo.Abstractions.Models.Utils
|
||||
.Where(x => x.Name == nameof(Enumerable.Any))
|
||||
.FirstOrDefault(x => x.GetParameters().Length == 2)!
|
||||
.MakeGenericMethod(typeof(T2));
|
||||
MethodCallExpression call = Expression.Call(null, method!, listGetter.Body, IsSame<T2>());
|
||||
return Expression.Lambda<Func<T, bool>>(call, listGetter.Parameters);
|
||||
MethodCallExpression call = Expression.Call(
|
||||
null,
|
||||
method,
|
||||
listGetter.Body,
|
||||
_IsSameExpression<T2>()
|
||||
);
|
||||
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(
|
||||
call,
|
||||
listGetter.Parameters
|
||||
);
|
||||
return new Filter<T>.Lambda(lambda);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return _id.HasValue
|
||||
? _id.Value.ToString()
|
||||
: _slug;
|
||||
return _id.HasValue ? _id.Value.ToString() : _slug!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -192,7 +213,7 @@ namespace Kyoo.Abstractions.Models.Utils
|
||||
public class IdentifierConvertor : TypeConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(int) || sourceType == typeof(string))
|
||||
return true;
|
||||
@@ -200,15 +221,17 @@ namespace Kyoo.Abstractions.Models.Utils
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
public override object ConvertFrom(
|
||||
ITypeDescriptorContext? context,
|
||||
CultureInfo? culture,
|
||||
object value
|
||||
)
|
||||
{
|
||||
if (value is int id)
|
||||
if (value is Guid id)
|
||||
return new Identifier(id);
|
||||
if (value is not string slug)
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
return int.TryParse(slug, out id)
|
||||
? new Identifier(id)
|
||||
: new Identifier(slug);
|
||||
return base.ConvertFrom(context, culture, value)!;
|
||||
return Guid.TryParse(slug, out id) ? new Identifier(id) : new Identifier(slug);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
112
back/src/Kyoo.Abstractions/Models/Utils/Include.cs
Normal file
112
back/src/Kyoo.Abstractions/Models/Utils/Include.cs
Normal file
@@ -0,0 +1,112 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Abstractions.Models.Utils;
|
||||
|
||||
public class Include
|
||||
{
|
||||
/// <summary>
|
||||
/// The aditional fields to include in the result.
|
||||
/// </summary>
|
||||
public ICollection<Metadata> Metadatas { get; set; } = ArraySegment<Metadata>.Empty;
|
||||
|
||||
public abstract record Metadata(string Name);
|
||||
|
||||
public record SingleRelation(string Name, Type type, string RelationIdName) : Metadata(Name);
|
||||
|
||||
public record CustomRelation(string Name, Type type, string Sql, string? On, Type Declaring)
|
||||
: Metadata(Name);
|
||||
|
||||
public record ProjectedRelation(string Name, string Sql) : Metadata(Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The aditional fields to include in the result.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type related to the new fields</typeparam>
|
||||
public class Include<T> : Include
|
||||
{
|
||||
/// <summary>
|
||||
/// The aditional fields names to include in the result.
|
||||
/// </summary>
|
||||
public ICollection<string> Fields => Metadatas.Select(x => x.Name).ToList();
|
||||
|
||||
public Include() { }
|
||||
|
||||
public Include(params string[] fields)
|
||||
{
|
||||
Type[] types = typeof(T).GetCustomAttribute<OneOfAttribute>()?.Types ?? new[] { typeof(T) };
|
||||
Metadatas = fields
|
||||
.SelectMany(key =>
|
||||
{
|
||||
var relations = types
|
||||
.Select(
|
||||
x =>
|
||||
x.GetProperty(
|
||||
key,
|
||||
BindingFlags.IgnoreCase
|
||||
| BindingFlags.Public
|
||||
| BindingFlags.Instance
|
||||
)!
|
||||
)
|
||||
.Select(
|
||||
prop => (prop, attr: prop?.GetCustomAttribute<LoadableRelationAttribute>()!)
|
||||
)
|
||||
.Where(x => x.prop != null && x.attr != null)
|
||||
.ToList();
|
||||
if (!relations.Any())
|
||||
throw new ValidationException($"No loadable relation with the name {key}.");
|
||||
return relations
|
||||
.Select(x =>
|
||||
{
|
||||
(PropertyInfo prop, LoadableRelationAttribute attr) = x;
|
||||
|
||||
if (attr.RelationID != null)
|
||||
return new SingleRelation(prop.Name, prop.PropertyType, attr.RelationID)
|
||||
as Metadata;
|
||||
if (attr.Sql != null)
|
||||
return new CustomRelation(
|
||||
prop.Name,
|
||||
prop.PropertyType,
|
||||
attr.Sql,
|
||||
attr.On,
|
||||
prop.DeclaringType!
|
||||
);
|
||||
if (attr.Projected != null)
|
||||
return new ProjectedRelation(prop.Name, attr.Projected);
|
||||
throw new NotImplementedException();
|
||||
})
|
||||
.Distinct();
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public static Include<T> From(string? fields)
|
||||
{
|
||||
if (string.IsNullOrEmpty(fields))
|
||||
return new Include<T>();
|
||||
return new Include<T>(fields.Split(','));
|
||||
}
|
||||
}
|
||||
@@ -16,38 +16,57 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Kyoo.Abstractions.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Information about the pagination. How many items should be displayed and where to start.
|
||||
/// </summary>
|
||||
public readonly struct Pagination
|
||||
public class Pagination
|
||||
{
|
||||
/// <summary>
|
||||
/// The count of items to return.
|
||||
/// </summary>
|
||||
public int Count { get; }
|
||||
public int Limit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Where to start? Using the given sort.
|
||||
/// </summary>
|
||||
public int? AfterID { get; }
|
||||
public Guid? AfterID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should the previous page be returned instead of the next?
|
||||
/// </summary>
|
||||
public bool Reverse { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="Pagination"/> with default values.
|
||||
/// </summary>
|
||||
public Pagination()
|
||||
{
|
||||
Limit = 50;
|
||||
AfterID = null;
|
||||
Reverse = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="Pagination"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="count">Set the <see cref="Count"/> value</param>
|
||||
/// <param name="count">Set the <see cref="Limit"/> value</param>
|
||||
/// <param name="afterID">Set the <see cref="AfterID"/> value. If not specified, it will start from the start</param>
|
||||
public Pagination(int count, int? afterID = null)
|
||||
/// <param name="reverse">Should the previous page be returned instead of the next?</param>
|
||||
public Pagination(int count, Guid? afterID = null, bool reverse = false)
|
||||
{
|
||||
Count = count;
|
||||
Limit = count;
|
||||
AfterID = afterID;
|
||||
Reverse = reverse;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly create a new pagination from a limit number.
|
||||
/// </summary>
|
||||
/// <param name="limit">Set the <see cref="Count"/> value</param>
|
||||
/// <param name="limit">Set the <see cref="Limit"/> value</param>
|
||||
/// <returns>A new <see cref="Pagination"/> instance</returns>
|
||||
public static implicit operator Pagination(int limit) => new(limit);
|
||||
}
|
||||
|
||||
@@ -31,13 +31,13 @@ namespace Kyoo.Abstractions.Models.Utils
|
||||
/// The list of errors that where made in the request.
|
||||
/// </summary>
|
||||
/// <example><c>["InvalidFilter: no field 'startYear' on a collection"]</c></example>
|
||||
[NotNull] public string[] Errors { get; set; }
|
||||
public string[] Errors { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="RequestError"/> with one error.
|
||||
/// </summary>
|
||||
/// <param name="error">The error to specify in the response.</param>
|
||||
public RequestError([NotNull] string error)
|
||||
public RequestError(string error)
|
||||
{
|
||||
if (error == null)
|
||||
throw new ArgumentNullException(nameof(error));
|
||||
@@ -48,10 +48,13 @@ namespace Kyoo.Abstractions.Models.Utils
|
||||
/// Create a new <see cref="RequestError"/> with multiple errors.
|
||||
/// </summary>
|
||||
/// <param name="errors">The errors to specify in the response.</param>
|
||||
public RequestError([NotNull] string[] errors)
|
||||
public RequestError(string[] errors)
|
||||
{
|
||||
if (errors == null || !errors.Any())
|
||||
throw new ArgumentException("Errors must be non null and not empty", nameof(errors));
|
||||
throw new ArgumentException(
|
||||
"Errors must be non null and not empty",
|
||||
nameof(errors)
|
||||
);
|
||||
Errors = errors;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,21 +16,21 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace Kyoo.TheTvdb.Models
|
||||
namespace Kyoo.Abstractions.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// The option containing the api key for the tvdb.
|
||||
/// Information about the pagination. How many items should be displayed and where to start.
|
||||
/// </summary>
|
||||
public class TvdbOption
|
||||
public class SearchPagination
|
||||
{
|
||||
/// <summary>
|
||||
/// The path to get this option from the root configuration.
|
||||
/// The count of items to return.
|
||||
/// </summary>
|
||||
public const string Path = "tvdb";
|
||||
public int Limit { get; set; } = 50;
|
||||
|
||||
/// <summary>
|
||||
/// The api key of the tvdb.
|
||||
/// Where to start? How many items to skip?
|
||||
/// </summary>
|
||||
public string ApiKey { get; set; }
|
||||
public int? Skip { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -17,72 +17,120 @@
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using Kyoo.Abstractions.Models;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
using Kyoo.Utils;
|
||||
|
||||
namespace Kyoo.Abstractions.Controllers
|
||||
{
|
||||
public record Sort;
|
||||
|
||||
/// <summary>
|
||||
/// Information about how a query should be sorted. What factor should decide the sort and in which order.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">For witch type this sort applies</typeparam>
|
||||
public readonly struct Sort<T>
|
||||
public record Sort<T> : Sort
|
||||
where T : IQuery
|
||||
{
|
||||
/// <summary>
|
||||
/// The sort key. This member will be used to sort the results.
|
||||
/// Sort by a specific key
|
||||
/// </summary>
|
||||
public Expression<Func<T, object>> Key { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <param name="Key">The sort keys. This members will be used to sort the results.</param>
|
||||
/// <param name="Desendant">
|
||||
/// If this is set to true, items will be sorted in descend order else, they will be sorted in ascendant order.
|
||||
/// </summary>
|
||||
public bool Descendant { get; }
|
||||
/// </param>
|
||||
public record By(string Key, bool Desendant = false) : Sort<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Sort by a specific key
|
||||
/// </summary>
|
||||
/// <param name="key">The sort keys. This members will be used to sort the results.</param>
|
||||
/// <param name="desendant">
|
||||
/// If this is set to true, items will be sorted in descend order else, they will be sorted in ascendant order.
|
||||
/// </param>
|
||||
public By(Expression<Func<T, object?>> key, bool desendant = false)
|
||||
: this(Utility.GetPropertyName(key), desendant) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="Sort{T}"/> instance.
|
||||
/// Sort by multiple keys.
|
||||
/// </summary>
|
||||
/// <param name="key">The sort key given. It is assigned to <see cref="Key"/>.</param>
|
||||
/// <param name="descendant">Should this be in descendant order? The default is false.</param>
|
||||
/// <exception cref="ArgumentException">If the given key is not a member.</exception>
|
||||
public Sort(Expression<Func<T, object>> key, bool descendant = false)
|
||||
{
|
||||
Key = key;
|
||||
Descendant = descendant;
|
||||
/// <param name="List">The list of keys to sort by.</param>
|
||||
public record Conglomerate(params Sort<T>[] List) : Sort<T>;
|
||||
|
||||
if (!Utility.IsPropertyExpression(Key))
|
||||
throw new ArgumentException("The given sort key is not valid.");
|
||||
/// <summary>Sort randomly items</summary>
|
||||
public record Random(uint Seed) : Sort<T>
|
||||
{
|
||||
public Random()
|
||||
: this(0)
|
||||
{
|
||||
uint seed = BitConverter.ToUInt32(
|
||||
BitConverter.GetBytes(new System.Random().Next(int.MinValue, int.MaxValue)),
|
||||
0
|
||||
);
|
||||
Seed = seed;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The default sort method for the given type.</summary>
|
||||
public record Default : Sort<T>
|
||||
{
|
||||
public void Deconstruct(out Sort<T> value)
|
||||
{
|
||||
value = (Sort<T>)T.DefaultSort;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="Sort{T}"/> instance from a key's name (case insensitive).
|
||||
/// </summary>
|
||||
/// <param name="sortBy">A key name with an optional order specifier. Format: "key:asc", "key:desc" or "key".</param>
|
||||
/// <param name="seed">The random seed.</param>
|
||||
/// <exception cref="ArgumentException">An invalid key or sort specifier as been given.</exception>
|
||||
public Sort(string sortBy)
|
||||
/// <returns>A <see cref="Sort{T}"/> for the given string</returns>
|
||||
public static Sort<T> From(string? sortBy, uint seed)
|
||||
{
|
||||
if (string.IsNullOrEmpty(sortBy))
|
||||
{
|
||||
Key = null;
|
||||
Descendant = false;
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrEmpty(sortBy) || sortBy == "default")
|
||||
return new Default();
|
||||
if (sortBy == "random")
|
||||
return new Random(seed);
|
||||
if (sortBy.Contains(','))
|
||||
return new Conglomerate(sortBy.Split(',').Select(x => From(x, seed)).ToArray());
|
||||
|
||||
if (sortBy.StartsWith("random:"))
|
||||
return new Random(uint.Parse(sortBy["random:".Length..]));
|
||||
|
||||
string key = sortBy.Contains(':') ? sortBy[..sortBy.IndexOf(':')] : sortBy;
|
||||
string order = sortBy.Contains(':') ? sortBy[(sortBy.IndexOf(':') + 1)..] : null;
|
||||
|
||||
ParameterExpression param = Expression.Parameter(typeof(T), "x");
|
||||
MemberExpression property = Expression.Property(param, key);
|
||||
Key = property.Type.IsValueType
|
||||
? Expression.Lambda<Func<T, object>>(Expression.Convert(property, typeof(object)), param)
|
||||
: Expression.Lambda<Func<T, object>>(property, param);
|
||||
|
||||
Descendant = order switch
|
||||
string? order = sortBy.Contains(':') ? sortBy[(sortBy.IndexOf(':') + 1)..] : null;
|
||||
bool desendant = order switch
|
||||
{
|
||||
"desc" => true,
|
||||
"asc" => false,
|
||||
null => false,
|
||||
_ => throw new ArgumentException($"The sort order, if set, should be :asc or :desc but it was :{order}.")
|
||||
_
|
||||
=> throw new ValidationException(
|
||||
$"The sort order, if set, should be :asc or :desc but it was :{order}."
|
||||
)
|
||||
};
|
||||
|
||||
Type[] types =
|
||||
typeof(T).GetCustomAttribute<OneOfAttribute>()?.Types ?? new[] { typeof(T) };
|
||||
PropertyInfo? property = types
|
||||
.Select(
|
||||
x =>
|
||||
x.GetProperty(
|
||||
key,
|
||||
BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance
|
||||
)
|
||||
)
|
||||
.FirstOrDefault(x => x != null);
|
||||
if (property == null)
|
||||
throw new ValidationException("The given sort key is not valid.");
|
||||
return new By(property.Name, desendant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,21 +16,21 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace Kyoo.TheMovieDb.Models
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// The option containing the api key for TheMovieDb.
|
||||
/// The links to see a movie or an episode.
|
||||
/// </summary>
|
||||
public class TheMovieDbOptions
|
||||
public class VideoLinks
|
||||
{
|
||||
/// <summary>
|
||||
/// The path to get this option from the root configuration.
|
||||
/// The direct link to the unprocessed video (pristine quality).
|
||||
/// </summary>
|
||||
public const string Path = "themoviedb";
|
||||
public string Direct { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The api key of TheMovieDb.
|
||||
/// The link to an HLS master playlist containing all qualities available for this video.
|
||||
/// </summary>
|
||||
public string ApiKey { get; set; }
|
||||
public string Hls { get; set; }
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user