Compare commits
880 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e8196fed7c | |||
| 477702fbb9 | |||
| 2216a271bb | |||
| 91cd7d2f6b | |||
| 4e681d25d9 | |||
| 06cc2891de | |||
| 682432f55a | |||
| 7c4cb5ec58 | |||
| 1df73fdeba | |||
| 21ba8a0593 | |||
| 08ed52eb72 | |||
| 99700e1b95 | |||
| 4e0be95368 | |||
| c8a59c8343 | |||
| 2b2a2ed708 | |||
| a827a2fbcc | |||
| f97f6b8061 | |||
| 844ea9d77e | |||
| 71479286e9 | |||
| 28c2ac528d | |||
| 696a36b4a5 | |||
| 5fb4922c6f | |||
| 3738f95871 | |||
| 6797bdec04 | |||
| 764c6d5461 | |||
| 6973182ade | |||
| f62af07381 | |||
| 46c37c0ae8 | |||
| 4ad71766fc | |||
| 9d60cc8c66 | |||
| 4a6243096a | |||
| 10cbdc8e8e | |||
| 4886fc467c | |||
| 8e2827cc39 | |||
| 395d2e4917 | |||
| d31f5229da | |||
| d6622818dc | |||
| ba684d6d3a | |||
| 90c04a4640 | |||
| 06a1e1f166 | |||
| 31ad366aa9 | |||
| 10f33b0273 | |||
| eaa1ac8013 | |||
| ca3bb308b3 | |||
| e790f024c2 | |||
| 250e0c75df | |||
| cde7375049 | |||
| c7fedfbca3 | |||
| f520831025 | |||
| 2f0719a883 | |||
| 34ab99caf1 | |||
| b2f94c0e40 | |||
| 65bff1181a | |||
| efb14f0b58 | |||
| 75a4f04cce | |||
| 007fe34363 | |||
| a7e31ef31f | |||
| eae0c28e6d | |||
| 21950382b9 | |||
| 1af9c047fb | |||
| 2d19bfa7fb | |||
| f5f7de64de | |||
| 754e76a61b | |||
| 09505e0988 | |||
| 67e206fa0f | |||
| 608fd873de | |||
| 05a4161fd3 | |||
| 05040351dc | |||
| d75324afc9 | |||
| 38fcd31917 | |||
| 816d8a0216 | |||
| e37ccd6ec0 | |||
| f27477da26 | |||
| c032a015a4 | |||
| 11e81b035a | |||
| 891a03c038 | |||
| 31aa6c486c | |||
| 1d9133a5e8 | |||
| 3375ca5a8c | |||
| 1596e93cc1 | |||
| 1a540f1cf7 | |||
| 05f5cd1bde | |||
| f0fbd0232c | |||
| 72dd609109 | |||
| 13e94a8b1b | |||
| d9e7883fb5 | |||
| 2d396cb589 | |||
| b56031b9f3 | |||
| fd86b141e2 | |||
| 427a3e9b08 | |||
| 3001f21f8d | |||
| 48b50a22a4 | |||
| 5e8496bc59 | |||
| 2dbc1153e8 | |||
| b07c146fd9 | |||
| cc2edc4d66 | |||
| 524357bfa8 | |||
| 7343e07fe5 | |||
| 6be8624373 | |||
| 740c95d557 | |||
| 31607fbb37 | |||
| 122cba2aa7 | |||
| b44a70ff36 | |||
| 1b03f078b9 | |||
| 4c8f8cf64c | |||
| d623f616fa | |||
| fc8de8aead | |||
| 6480cfcc87 | |||
| e36d424b5f | |||
| f7e7d72688 | |||
| 3474568ce2 | |||
| 89f2dfd78a | |||
| 2c4c56d6d6 | |||
| 087d4153ae | |||
| 86772bd7bd | |||
| 4e2841f0d7 | |||
| 26fe4040bf | |||
| fb7f29de18 | |||
| d18252542d | |||
| 030fcaac15 | |||
| 7ebb043249 | |||
| 598b1c9966 | |||
| cf36aaef2b | |||
| f30af9cd5f | |||
| 5024c52c60 | |||
| e971b62dab | |||
| 4ffec8ad26 | |||
| 69cc5814d8 | |||
| 414a318a0d | |||
| 758e35baba | |||
| 09921a00aa | |||
| 16a3bb2df4 | |||
| 2c684c0231 | |||
| 949a1cce21 | |||
| 0e787f4e9f | |||
| bd31091648 | |||
| 4cd8903abc | |||
| 7b01de8db1 | |||
| f73d8a44df | |||
| 752d65d020 | |||
| c2667f99f4 | |||
| c7e7aa0a61 | |||
| fc79659549 | |||
| e81a6adb95 | |||
| 80aedcd7e2 | |||
| fc28c9237c | |||
| b04200ca68 | |||
| 67fbbcfd12 | |||
| e3dbed1c1a | |||
| 480a6607e2 | |||
| 4a30fee40d | |||
| 7f0fa74467 | |||
| 3d1d27230d | |||
| 9df1506794 | |||
| 2d0844b5db | |||
| 1ee016c997 | |||
| 221389089c | |||
| 59031ee3b8 | |||
| d2e408539e | |||
| a8140cc74b | |||
| d5f080fefb | |||
| 35ff8ec713 | |||
| 764c901cd7 | |||
| f85d45d17f | |||
| 022bd1b8b4 | |||
| 31305af7ff | |||
| dd929d796f | |||
| 11fde02035 | |||
| 3d85014edc | |||
| d125fbc43d | |||
| 427688a0a0 | |||
| 21cc38fcf4 | |||
| ee7bf86e0f | |||
| f8bb7a7ff4 | |||
| 6d3e6d800f | |||
| 208585d3f6 | |||
| bf00dedc7f | |||
| bf43dc00bb | |||
| e64aaebbac | |||
| 1d443d2ff5 | |||
| 6507a9b2ee | |||
| 4cc4d57a78 | |||
| 605bf0e8c3 | |||
| 89e2af6b57 | |||
| b864e9da2a | |||
| f10382a696 | |||
| 297f25cfc2 | |||
| afdef163ea | |||
| 6751560228 | |||
| 2012eb5e11 | |||
| 58068e249a | |||
| 3ddbda9aca | |||
| 1fef8bf266 | |||
| 3c4043199a | |||
| 0220309ea7 | |||
| 497b4f834f | |||
| 715ddbb3b0 | |||
| 0ff038f0a2 | |||
| 7322485a6d | |||
| f77af5f6e4 | |||
| 2324c408ba | |||
| 037cf9e1ee | |||
| 5268553e7f | |||
| 5f7524aca2 | |||
| 86f5221f96 | |||
| ecf85a73ec | |||
| 132ce3ece1 | |||
| 25deca9579 | |||
| 93d15cd969 | |||
| 4b91c9bf66 | |||
| 16adaa64c9 | |||
| 93fe7957fb | |||
| fb96763f65 | |||
| 63342f89d4 | |||
| 47095e6cf8 | |||
| f2062ba19b | |||
| 58061a7295 | |||
| c6c398179a | |||
| e498e47109 | |||
| 40089e2fd9 | |||
| 045a0419f6 | |||
| 80fb3dc2cb | |||
| fcee64df09 | |||
| 757e0194b9 | |||
| 4625592a83 | |||
| e3b844b5aa | |||
| decaffed86 | |||
| 669c48cc8b | |||
| d1fe24ac92 | |||
| 3fa43a1e08 | |||
| 028a98d2c1 | |||
| 0f70a81db3 | |||
| d014a1dc1d | |||
| e04a152ed0 | |||
| 8a680ae324 | |||
| 75996476a7 | |||
| 620d7b560d | |||
| ab9859ecef | |||
| 37ea50a572 | |||
| 5cb3f04389 | |||
| cedf50eeec | |||
| c5fce647de | |||
| 10a0d6bdba | |||
| 0abe57e930 | |||
| 65c0b486aa | |||
| ae0ecc1b10 | |||
| dfff68b2f4 | |||
| e4c5d51860 | |||
| 8c609bc9ce | |||
| f486f5966f | |||
| e91dd14b31 | |||
| 8a53b60912 | |||
| a4b52b7264 | |||
| 5ae0ef0527 | |||
| dfb1d704ed | |||
| ffd6dac03a | |||
| 8f4895e8a5 | |||
| a9302b8b53 | |||
| c31b0b311b | |||
| bba049c987 | |||
| 21c2acc520 | |||
| 394d23a73a | |||
| 276428878e | |||
| 04db0369d4 | |||
| 4ef7eda593 | |||
| 2242c8d793 | |||
| bef665be36 | |||
| bf9f342c13 | |||
| a994edda04 | |||
| 9f6bca3658 | |||
| 2617a49b78 | |||
| 656bffbbb2 | |||
| 8104e739d5 | |||
| 632968dc81 | |||
| ed69e690b8 | |||
| 20775116f7 | |||
| 446f9bf81f | |||
| 913e80fd55 | |||
| 9a4a01fb0e | |||
| df92df7bd6 | |||
| 78742b8e4c | |||
| 942c400c19 | |||
| 5587dd8bfb | |||
| 8d1fc3f984 | |||
| 318e0d4a24 | |||
| bc00617df7 | |||
| 2c26517172 | |||
| 51648a2a21 | |||
| 12df381495 | |||
| 17ca23d73b | |||
| 41df94115f | |||
| 0250204f14 | |||
| 0419deeec4 | |||
| 9020f68ce1 | |||
| 557c4d065d | |||
| 6cc1bd544a | |||
| aba22b92bc | |||
| 040871459b | |||
| 6263b73d9c | |||
| 1cc433eabc | |||
| 5982cdad90 | |||
| d450169964 | |||
| e823c11b46 | |||
| c328417d29 | |||
| c4192f9f8b | |||
| 5368112d90 | |||
| ed07ed44ae | |||
| 9993dafe54 | |||
| 6bdb5debd2 | |||
| 65403747df | |||
| 594b271383 | |||
| 27f9981142 | |||
| 3d3d879b99 | |||
| 53ed6e5e6e | |||
| d0e4e0f600 | |||
| 311caf3fc3 | |||
| 7c13021def | |||
| c8788d83fb | |||
| 331260cf80 | |||
| 58e5931a32 | |||
| 02041fa6f4 | |||
| fcd299965d | |||
| 79d5a53aea | |||
| f1086a72bf | |||
| 95d001a053 | |||
| b7fd68d366 | |||
| c84729a4f4 | |||
| 4951ec9814 | |||
| edba82db37 | |||
| 1d1e6dede9 | |||
| f384822aa5 | |||
| 1ac282b12e | |||
| 588a13377c | |||
| fb1de5a921 | |||
| 71ed840944 | |||
| dab8e15052 | |||
| e47d121985 | |||
| c0b95dbc79 | |||
| 647adc51c8 | |||
| 7668ecf9c9 | |||
| 3769453541 | |||
| 8c2af50170 | |||
| 1e97e4d462 | |||
| 27e7e792b3 | |||
| 7429c07c05 | |||
| 91afaaf8fe | |||
| 333bd2107a | |||
| 25d3d0b731 | |||
| 77addb2283 | |||
| 9651a78b0c | |||
| 9eba31185a | |||
| aea7edf0fa | |||
| 42d4834f63 | |||
| ba78f6a0ff | |||
| feb5b62ad4 | |||
| 5ccba7cf8a | |||
| 848cfc32cc | |||
| 5510e8ebee | |||
| 9c02e99e35 | |||
| 148db8b81a | |||
| e342b7bc71 | |||
| f1c93ae618 | |||
| 4e229ad86b | |||
| e88f079da6 | |||
| 4e8de67aca | |||
| a85488cd20 | |||
| 5a7cca9d1b | |||
| d6c6f3c10c | |||
| 38f52a139e | |||
| 194da8416b | |||
| f3e7bc0573 | |||
| c3fa299acc | |||
| 852460b991 | |||
| 33b67a357f | |||
| 4db31acff9 | |||
| 968e282c90 | |||
| 1731bf7372 | |||
| 0804bed66d | |||
| a0606b5730 | |||
| 9ba6227db4 | |||
| 9bab93262e | |||
| 0f9006c81f | |||
| b3438559cc | |||
| 8e5cccb22c | |||
| 500c0b9cba | |||
| 5054a77dcf | |||
| dac2c98d8a | |||
| 4df3333b71 | |||
| 5262e50fee | |||
| 547d0ecf58 | |||
| 6e07eab247 | |||
| 96b3d37caf | |||
| aafed63c3f | |||
| 2e9a3d45c2 | |||
| e281c79d6f | |||
| 38ec68c488 | |||
| da61998ad6 | |||
| f28dd79fb1 | |||
| c2e57aba27 | |||
| 4a3e42e779 | |||
| 67f399dccf | |||
| eb95b025d4 | |||
| de45cfdd8c | |||
| 47966793c0 | |||
| 0c49079c16 | |||
| d82d6b6aef | |||
| 7ae526da8b | |||
| e79d44d9f1 | |||
| fb7de2f966 | |||
| db54fe4c70 | |||
| 1eb26bdf08 | |||
| cbd0e71c07 | |||
| 2db1826ed8 | |||
| cf4e64f430 | |||
| 9b39404b9a | |||
| 00fe66a38c | |||
| 406af5f086 | |||
| e7e7d96f51 | |||
| 785fa76ac6 | |||
| 6c967c5982 | |||
| f03e279382 | |||
| 1bc2b12ee3 | |||
| 469a17b3ca | |||
| affb8c8673 | |||
| 74aa38acd7 | |||
| d4ded281aa | |||
| c597f0de35 | |||
| 3965f90236 | |||
| 8af4417f8f | |||
| 83948420a4 | |||
| 8c53407a9d | |||
| 4a5c526ccc | |||
| 69a81c0bc2 | |||
| 320707d44c | |||
| 2690ac299b | |||
| 268b099ca8 | |||
| 33171a58b5 | |||
| 73c1cdb32a | |||
| 13bfe5093e | |||
| 99bed9a9c3 | |||
| 8ef41020d9 | |||
| fca226bdfd | |||
| bca7a26ffd | |||
| 60df855b26 | |||
| 44ed037e73 | |||
| 50ce7572b4 | |||
| 74695428fe | |||
| 139807719c | |||
| 1e2050f106 | |||
| a23f04623e | |||
| 1f30a50f4a | |||
| 098de6b050 | |||
| a0d31a49a0 | |||
| 6bbb968b57 | |||
| 6c6e9ca9f2 | |||
| 89d4ce309d | |||
| b43317c5e1 | |||
| 5917e91447 | |||
| 9753a76905 | |||
| d978ae1996 | |||
| 6016a27736 | |||
| 24b76dbed8 | |||
| dab25a0eeb | |||
| 566646ad8b | |||
| 6c1ca6f737 | |||
| aa0c20afd5 | |||
| 46acff4113 | |||
| de5c0bab70 | |||
| 5181427234 | |||
| c79324154f | |||
| 53beebc774 | |||
| ba003e06ef | |||
| 1d631540ac | |||
| 3ed9d32f68 | |||
| 3ecfd1fdd1 | |||
| 24574d4964 | |||
| 512ab8c6aa | |||
| 029bafdf9c | |||
| 0849c4a447 | |||
| 5238ba5d8f | |||
| 6b1a64652f | |||
| 9961c8c459 | |||
| 18418b6892 | |||
| d4bc7b5a5a | |||
| e2d0f67077 | |||
| 5ef63e738d | |||
| 7bbcb455c0 | |||
| 2ad54cd09d | |||
| 7c9803e135 | |||
| 480999e8e6 | |||
| ef17ec700b | |||
| 4b34b0cfea | |||
| 60fc53306d | |||
| 77d4fec6eb | |||
| c6188e26af | |||
| 18717d103a | |||
| 6834b64922 | |||
| 29ff80d69c | |||
| 4a700778e3 | |||
| 56e3063342 | |||
| 781cca0c82 | |||
| eab35890dc | |||
| 967d5deeb7 | |||
| 51ba28bd65 | |||
| a94aeb5c87 | |||
| 13f2783a8e | |||
| 7554f63551 | |||
| 25253cf961 | |||
| 77a5617774 | |||
| f274d024ce | |||
| 395072239d | |||
| e2f6ecaef6 | |||
| 48e7274d37 | |||
| 9f3aa2cead | |||
| bb50363812 | |||
| 9a38e4dc8a | |||
| c607c95e64 | |||
| 18ae107ce4 | |||
| 3f80b16ffa | |||
| 72aa364aa5 | |||
| 0d5fbcb031 | |||
| a35ea49c99 | |||
| c45b6aa53e | |||
| 4b4399fba6 | |||
| c06598635f | |||
| a15098dc00 | |||
| 4811e76860 | |||
| 64a4f259a2 | |||
| bdfd042d70 | |||
| a993420676 | |||
| fb6a901374 | |||
| 86a4d15a32 | |||
| 25c2267a89 | |||
| b86f049e66 | |||
| 12fea24590 | |||
| 26e2ffdd31 | |||
| d9ab654abe | |||
| c44f96b727 | |||
| c5ac36c886 | |||
| ff9a0c7e55 | |||
| 056e19f350 | |||
| f8ba55e202 | |||
| 4ea76f9cdc | |||
| 88f56cd0c4 | |||
| 69ea15f73a | |||
| b7ae044e65 | |||
| d8d237f6f2 | |||
| 18e1d03a89 | |||
| 3947f2315d | |||
| cb9e50b2ea | |||
| e620bb9512 | |||
| 8b04fe7633 | |||
| 2cb747651b | |||
| 46897aab4f | |||
| 892787cb1a | |||
| be77e14db9 | |||
| 34af7501fa | |||
| 183ef34422 | |||
| 637936cb9f | |||
| fc59b0ab77 | |||
| d8b312674d | |||
| ebae7229c1 | |||
| 3df8cda110 | |||
| 43cf11aa35 | |||
| 6d74184cfb | |||
| 21f0a7e020 | |||
| cb6d2cbd2d | |||
| ce51025e7c | |||
| 5a054e5150 | |||
| 98f003f71a | |||
| b9efcace79 | |||
| 1aaa8de1f9 | |||
| 5e2e190f3e | |||
| 828434058f | |||
| dfbf5fc9fa | |||
| 8029cd3ebb | |||
| e970d7a6aa | |||
| 350e795640 | |||
| f5bda652c4 | |||
| e8189cd0f6 | |||
| 18231fedef | |||
| 935c7231eb | |||
| da2c7db0df | |||
| 83d98ac92d | |||
| b997b12d27 | |||
| 6887e790c8 | |||
| 373a1f72bf | |||
| 0d43b06042 | |||
| ced9868357 | |||
| 23c867f946 | |||
| 42c233c74e | |||
| 1f8e74f3a8 | |||
| d6835f8dd6 | |||
| 86940e96d5 | |||
| 75d90c8e4c | |||
| ecbc0538f6 | |||
| 2a26760911 | |||
| 19b6808602 | |||
| c2202be0f8 | |||
| 9729ae52a3 | |||
| 7cc69f30c4 | |||
| e33706ab25 | |||
| 4018b7e2d5 | |||
| 8425d76198 | |||
| 9bf009c4f8 | |||
| 2845e7e101 | |||
| c78298789d | |||
| a6bde0943e | |||
| 7bb8985f11 | |||
| db1ebe2559 | |||
| dbebc4774f | |||
| 0fbc4545d1 | |||
| 8414285b58 | |||
| 5d4bef5478 | |||
| cac3a3e945 | |||
| 7ec42b89a0 | |||
| bca569da42 | |||
| a82303ccd1 | |||
| c720504e39 | |||
| a2dd2ddd55 | |||
| 5835c4b21d | |||
| d8e6808d77 | |||
| 9e58e31de0 | |||
| 77602aff88 | |||
| 1ffd443d5a | |||
| 1dc5a624a7 | |||
| af8f86b3de | |||
| bfc3954995 | |||
| 4ed90d4658 | |||
| 3a6a5baa8e | |||
| 18f0d996c0 | |||
| da2554bd53 | |||
| f2811323c2 | |||
| 3f878d63a5 | |||
| a5882ae162 | |||
| c74028d08d | |||
| 3f13851be5 | |||
| ea446fd4a3 | |||
| 585b5201f1 | |||
| 8d98885cda | |||
| da9418c1b2 | |||
| 9dba930a85 | |||
| 7722cb3ffa | |||
| 3e6819c718 | |||
| 41fb1e5106 | |||
| 64d5ec12e2 | |||
| d409623086 | |||
| 9af28607c9 | |||
| 81a8ebde22 | |||
| 221a95c93c | |||
| 982ac32471 | |||
| d48275a785 | |||
| a4e98a0390 | |||
| 0fbdb79df7 | |||
| 87dc60d4aa | |||
| d6b56dde62 | |||
| 0302144b22 | |||
| 269b94254b | |||
| 2f377e0a0f | |||
| 3b96c78515 | |||
| e150174ece | |||
| 33e069e461 | |||
| 8055b70ab1 | |||
| 593b7327cf | |||
| 9d795adc3e | |||
| 0ff38b6012 | |||
| fe43e279c8 | |||
| d18823ced1 | |||
| 05bbf71b6d | |||
| 8bf88f4cb2 | |||
| db4a72df10 | |||
| 406fb045c2 | |||
| 32992b6143 | |||
| da169dddb5 | |||
| 250f03d2d9 | |||
| d8cb34dbbc | |||
| edf5ee0cc4 | |||
| 8fd9f5b6a4 | |||
| 2f4a00d322 | |||
| 86089ec03a | |||
| 9849c183ac | |||
| 29d6783471 | |||
| 236e21efcb | |||
| 30ec203eff | |||
| bcb32ec6ad | |||
| eb4b705167 | |||
| d6c669a7c8 | |||
| 1b84446831 | |||
| fb256b7aa0 | |||
| 382b8bb509 | |||
| 00234a5ece | |||
| 57cefb432a | |||
| 7be4a8500c | |||
| 74d2698c5f | |||
| 4727f69fc9 | |||
| 1e6c41e333 | |||
| bee8f58265 | |||
| a71040ba1b | |||
| a6a4cd5667 | |||
| 3a5bbcf2a8 | |||
| 449074e73f | |||
| 2fc97212a7 | |||
| f1ef0b0b4c | |||
| 387b4dea25 | |||
| 3014866f65 | |||
| 37985c2e26 | |||
| 139e3c19ee | |||
| da860e6e54 | |||
| ce03662fa7 | |||
| 49923e50db | |||
| c3c52b6682 | |||
| 80281e599d | |||
| 3a88a3c795 | |||
| d6e98f9a50 | |||
| 2bcbffee0c | |||
| 22ffc5aee4 | |||
| ee3a4531a0 | |||
| aff740c596 | |||
| 38d9eeffbe | |||
| d6d9fce898 | |||
| f60ad53393 | |||
| e8461d3317 | |||
| 9dd512df80 | |||
| 5b0bd88892 | |||
| 70c85925af | |||
| e449182641 | |||
| fcfe02ee73 | |||
| b8b650540d | |||
| e1d523ee45 | |||
| ceb8b9f740 | |||
| 8413c56392 | |||
| 88038d9644 | |||
| c846da4f9e | |||
| 546f4cd46f | |||
| e216702bcf | |||
| be89a5e719 | |||
| 84d56976ba | |||
| 0ef2b46106 | |||
| 52294881b1 | |||
| 5e8a2db029 | |||
| 262eefd8db | |||
| 73e8758d84 | |||
| 181de97ce5 | |||
| ae5514afd6 | |||
| 83af2db679 | |||
| 0b3e6548db | |||
| 72beadc74d | |||
| f0e74c2c6b | |||
| d351fa0c1e | |||
| 20033f2275 | |||
| c4c0894b29 | |||
| c4f51e16a5 | |||
| 56dcc45dc0 | |||
| be89d53a9e | |||
| 3ac7531385 | |||
| cb1ff69585 | |||
| a50fb922c5 | |||
| 593c6c071c | |||
| d573f2d671 | |||
| 1ce5939362 | |||
| 4d335d8f13 | |||
| c118f111b6 | |||
| 52e91243e5 | |||
| 9faa68b26f | |||
| aadf7676d1 | |||
| 08ca1337a9 | |||
| 07072d9f7b | |||
| 7e3c45c917 | |||
| 548270772c | |||
| cb7bffc233 | |||
| da2caa2902 | |||
| ab0e851db9 | |||
| 32f393d57f | |||
| 78e4e2ed92 | |||
| 42d5a48491 | |||
| 20dac6d6b8 | |||
| 1cdcace061 | |||
| 95ee3c72e3 | |||
| 66eabcdd39 | |||
| 1d94607a30 | |||
| 1385d89df6 | |||
| 8b073e2ba5 | |||
| 34da7de47d | |||
| 021a1887fb | |||
| 6772ac5603 | |||
| b630e9de82 | |||
| 7774977cdd | |||
| 5ac6d0ae59 | |||
| fa3a8108e5 | |||
| 660f6174b3 | |||
| 8af1e93cd4 | |||
| cabb824f2a | |||
| b4c5ff89fd | |||
| 78324ff797 | |||
| 885a000da7 | |||
| aad34e62ca | |||
| 45d8ace9bb | |||
| 5e4697802f | |||
| f6227e99cc | |||
| ae24d644db | |||
| b982d7c239 | |||
| c713824bf9 | |||
| ea851317e7 | |||
| 8985fb8d58 | |||
| b5e8cce4cf | |||
| 211ae30188 | |||
| e18b89ca27 | |||
| ebd2a30087 | |||
| fd361421b1 | |||
| f7a46c7a56 | |||
| 2a1f6361a5 | |||
| 4519ce26e2 | |||
| 49d9649b8e | |||
| 1ea219bf3f | |||
| d0f2b3a747 | |||
| ffcf6bdd3a | |||
| 795f2c8774 | |||
| f8aff0c51d | |||
| 055e43eda7 | |||
| c8cb908004 | |||
| 8ab08dd041 | |||
| 8487319374 | |||
| 91e99effc9 | |||
| 0e2e731103 | |||
| 6822975fd3 | |||
| 12e4c1c7ae | |||
| 6786dfcabd | |||
| f06b9a14f3 | |||
| 838541b825 | |||
| 1d1d7e8a37 | |||
| d3afa53191 | |||
| 450f246f95 | |||
| b9a111432a | |||
| 581a7fe078 | |||
| a430568082 | |||
| d7c6d16250 | |||
| 3a831994f6 | |||
| dc68d61491 | |||
| a05d803d4c | |||
| b4893b9ac9 | |||
| b0608d26b4 | |||
| 67b1f9f716 | |||
| 39195aae09 | |||
| cc598a86f1 | |||
| ffe79c8982 | |||
| 05ad2e9b3f | |||
| 42abb5a993 | |||
| a4055779f6 | |||
| 1a3543e5a5 | |||
| 42e0b32c7d | |||
| 85a58fd655 | |||
| a709cbdc64 | |||
| 08b63a7c11 | |||
| 51edd5d067 | |||
| ee89236fe8 | |||
| fee42e883c | |||
| cc3422b96b | |||
| 50279be686 | |||
| 0e617933f6 | |||
| f74bfcb343 | |||
| 64b6cfa3dc | |||
| bb056f4b59 | |||
| ce11869a1a | |||
| e3b19c22a7 | |||
| 05fd76c0fa | |||
| 7165868509 | |||
| 4190410c7e | |||
| b673054c8d | |||
| e051ca6ff6 | |||
| d8d6c6f254 | |||
| 2ffab720fb | |||
| 07f163a4c3 | |||
| 883575893b | |||
| d1a0497f55 | |||
| ded9dee22c | |||
| e8f6a61131 | |||
| fd7f420af2 | |||
| eaa6cb0ddc | |||
| 8af256f9c2 | |||
| 9f83ee7b3e |
@@ -0,0 +1,192 @@
|
|||||||
|
name: $(Date:yyyyMMdd)$(Rev:.r)
|
||||||
|
|
||||||
|
variables:
|
||||||
|
- name: TestProjects
|
||||||
|
value: 'Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj'
|
||||||
|
- name: RestoreBuildProjects
|
||||||
|
value: 'Jellyfin.Server/Jellyfin.Server.csproj'
|
||||||
|
|
||||||
|
pr:
|
||||||
|
autoCancel: true
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
batch: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
- job: main_build
|
||||||
|
displayName: Main Build
|
||||||
|
pool:
|
||||||
|
vmImage: ubuntu-16.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
release:
|
||||||
|
BuildConfiguration: Release
|
||||||
|
debug:
|
||||||
|
BuildConfiguration: Debug
|
||||||
|
maxParallel: 2
|
||||||
|
steps:
|
||||||
|
- checkout: self
|
||||||
|
clean: true
|
||||||
|
submodules: true
|
||||||
|
persistCredentials: false
|
||||||
|
|
||||||
|
- task: DotNetCoreCLI@2
|
||||||
|
displayName: Restore
|
||||||
|
inputs:
|
||||||
|
command: restore
|
||||||
|
projects: '$(RestoreBuildProjects)'
|
||||||
|
|
||||||
|
- task: DotNetCoreCLI@2
|
||||||
|
displayName: Build
|
||||||
|
inputs:
|
||||||
|
projects: '$(RestoreBuildProjects)'
|
||||||
|
arguments: '--configuration $(BuildConfiguration)'
|
||||||
|
|
||||||
|
- task: DotNetCoreCLI@2
|
||||||
|
displayName: Test
|
||||||
|
inputs:
|
||||||
|
command: test
|
||||||
|
projects: '$(RestoreBuildProjects)'
|
||||||
|
arguments: '--configuration $(BuildConfiguration)'
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
- task: DotNetCoreCLI@2
|
||||||
|
displayName: Publish
|
||||||
|
inputs:
|
||||||
|
command: publish
|
||||||
|
publishWebProjects: false
|
||||||
|
projects: '$(RestoreBuildProjects)'
|
||||||
|
arguments: '--configuration $(BuildConfiguration) --output $(build.artifactstagingdirectory)'
|
||||||
|
zipAfterPublish: false
|
||||||
|
|
||||||
|
# - task: PublishBuildArtifacts@1
|
||||||
|
# displayName: 'Publish Artifact'
|
||||||
|
# inputs:
|
||||||
|
# PathtoPublish: '$(build.artifactstagingdirectory)'
|
||||||
|
# artifactName: 'jellyfin-build-$(BuildConfiguration)'
|
||||||
|
# zipAfterPublish: true
|
||||||
|
|
||||||
|
- task: PublishBuildArtifacts@1
|
||||||
|
displayName: 'Publish Artifact Naming'
|
||||||
|
condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded())
|
||||||
|
inputs:
|
||||||
|
PathtoPublish: '$(build.artifactstagingdirectory)/Jellyfin.Server/Emby.Naming.dll'
|
||||||
|
artifactName: 'Jellyfin.Naming'
|
||||||
|
|
||||||
|
- task: PublishBuildArtifacts@1
|
||||||
|
displayName: 'Publish Artifact Controller'
|
||||||
|
condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded())
|
||||||
|
inputs:
|
||||||
|
PathtoPublish: '$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Controller.dll'
|
||||||
|
artifactName: 'Jellyfin.Controller'
|
||||||
|
|
||||||
|
- task: PublishBuildArtifacts@1
|
||||||
|
displayName: 'Publish Artifact Model'
|
||||||
|
condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded())
|
||||||
|
inputs:
|
||||||
|
PathtoPublish: '$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Model.dll'
|
||||||
|
artifactName: 'Jellyfin.Model'
|
||||||
|
|
||||||
|
- task: PublishBuildArtifacts@1
|
||||||
|
displayName: 'Publish Artifact Common'
|
||||||
|
condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded())
|
||||||
|
inputs:
|
||||||
|
PathtoPublish: '$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Common.dll'
|
||||||
|
artifactName: 'Jellyfin.Common'
|
||||||
|
|
||||||
|
- job: dotnet_compat
|
||||||
|
displayName: Compatibility Check
|
||||||
|
pool:
|
||||||
|
vmImage: ubuntu-16.04
|
||||||
|
dependsOn: main_build
|
||||||
|
condition: and(succeeded(), variables['System.PullRequest.PullRequestNumber']) # Only execute if the pullrequest numer is defined. (So not for normal CI builds)
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
Naming:
|
||||||
|
NugetPackageName: Jellyfin.Naming
|
||||||
|
AssemblyFileName: Emby.Naming.dll
|
||||||
|
Controller:
|
||||||
|
NugetPackageName: Jellyfin.Controller
|
||||||
|
AssemblyFileName: MediaBrowser.Controller.dll
|
||||||
|
Model:
|
||||||
|
NugetPackageName: Jellyfin.Model
|
||||||
|
AssemblyFileName: MediaBrowser.Model.dll
|
||||||
|
Common:
|
||||||
|
NugetPackageName: Jellyfin.Common
|
||||||
|
AssemblyFileName: MediaBrowser.Common.dll
|
||||||
|
maxParallel: 2
|
||||||
|
steps:
|
||||||
|
- checkout: none
|
||||||
|
|
||||||
|
- task: DownloadBuildArtifacts@0
|
||||||
|
displayName: Download the Reference Assembly Build Artifact
|
||||||
|
inputs:
|
||||||
|
buildType: 'specific' # Options: current, specific
|
||||||
|
project: $(System.TeamProjectId) # Required when buildType == Specific
|
||||||
|
pipeline: $(System.DefinitionId) # Required when buildType == Specific, not sure if this will take a name too
|
||||||
|
#specificBuildWithTriggering: false # Optional
|
||||||
|
buildVersionToDownload: 'latestFromBranch' # Required when buildType == Specific# Options: latest, latestFromBranch, specific
|
||||||
|
allowPartiallySucceededBuilds: false # Optional
|
||||||
|
branchName: '$(System.PullRequest.TargetBranch)' # Required when buildType == Specific && BuildVersionToDownload == LatestFromBranch
|
||||||
|
#buildId: # Required when buildType == Specific && BuildVersionToDownload == Specific
|
||||||
|
#tags: # Optional
|
||||||
|
downloadType: 'single' # Options: single, specific
|
||||||
|
artifactName: '$(NugetPackageName)'# Required when downloadType == Single
|
||||||
|
#itemPattern: '**' # Optional
|
||||||
|
downloadPath: '$(System.ArtifactsDirectory)/current-artifacts'
|
||||||
|
#parallelizationLimit: '8' # Optional
|
||||||
|
|
||||||
|
- task: CopyFiles@2
|
||||||
|
displayName: Copy Nuget Assembly to current-release folder
|
||||||
|
inputs:
|
||||||
|
sourceFolder: $(System.ArtifactsDirectory)/current-artifacts # Optional
|
||||||
|
contents: '**/*.dll'
|
||||||
|
targetFolder: $(System.ArtifactsDirectory)/current-release
|
||||||
|
cleanTargetFolder: true # Optional
|
||||||
|
overWrite: true # Optional
|
||||||
|
flattenFolders: true # Optional
|
||||||
|
|
||||||
|
- task: DownloadBuildArtifacts@0
|
||||||
|
displayName: Download the New Assembly Build Artifact
|
||||||
|
inputs:
|
||||||
|
buildType: 'current' # Options: current, specific
|
||||||
|
allowPartiallySucceededBuilds: false # Optional
|
||||||
|
downloadType: 'single' # Options: single, specific
|
||||||
|
artifactName: '$(NugetPackageName)' # Required when downloadType == Single
|
||||||
|
downloadPath: '$(System.ArtifactsDirectory)/new-artifacts'
|
||||||
|
|
||||||
|
- task: CopyFiles@2
|
||||||
|
displayName: Copy Artifact Assembly to new-release folder
|
||||||
|
inputs:
|
||||||
|
sourceFolder: $(System.ArtifactsDirectory)/new-artifacts # Optional
|
||||||
|
contents: '**/*.dll'
|
||||||
|
targetFolder: $(System.ArtifactsDirectory)/new-release
|
||||||
|
cleanTargetFolder: true # Optional
|
||||||
|
overWrite: true # Optional
|
||||||
|
flattenFolders: true # Optional
|
||||||
|
|
||||||
|
- task: DownloadGitHubRelease@0
|
||||||
|
displayName: Download ABI compatibility check tool from GitHub
|
||||||
|
inputs:
|
||||||
|
connection: Jellyfin GitHub
|
||||||
|
userRepository: EraYaN/dotnet-compatibility
|
||||||
|
defaultVersionType: 'latest' # Options: latest, specificVersion, specificTag
|
||||||
|
#version: # Required when defaultVersionType != Latest
|
||||||
|
itemPattern: '**-ci.zip' # Optional
|
||||||
|
downloadPath: '$(System.ArtifactsDirectory)'
|
||||||
|
|
||||||
|
- task: ExtractFiles@1
|
||||||
|
displayName: Extract ABI compatibility check tool
|
||||||
|
inputs:
|
||||||
|
archiveFilePatterns: '$(System.ArtifactsDirectory)/*-ci.zip'
|
||||||
|
destinationFolder: $(System.ArtifactsDirectory)/tools
|
||||||
|
cleanDestinationFolder: true
|
||||||
|
|
||||||
|
- task: CmdLine@2
|
||||||
|
displayName: Execute ABI compatibility check tool
|
||||||
|
inputs:
|
||||||
|
script: 'dotnet tools/CompatibilityCheckerCoreCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName)'
|
||||||
|
workingDirectory: $(System.ArtifactsDirectory) # Optional
|
||||||
|
#failOnStderr: false # Optional
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
srpm:
|
||||||
|
dnf -y install git
|
||||||
|
git submodule update --init --recursive
|
||||||
|
cd deployment/fedora-package-x64; \
|
||||||
|
./create_tarball.sh; \
|
||||||
|
rpmbuild -bs pkg-src/jellyfin.spec \
|
||||||
|
--define "_sourcedir $$PWD/pkg-src/" \
|
||||||
|
--define "_srcrpmdir $(outdir)"
|
||||||
@@ -8,3 +8,4 @@ README.md
|
|||||||
deployment/*/dist
|
deployment/*/dist
|
||||||
deployment/*/pkg-dist
|
deployment/*/pkg-dist
|
||||||
deployment/collect-dist/
|
deployment/collect-dist/
|
||||||
|
ci/
|
||||||
|
|||||||
+20
-2
@@ -1,12 +1,30 @@
|
|||||||
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
name: build
|
name: build-debug
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: submodules
|
- name: submodules
|
||||||
image: docker:git
|
image: docker:git
|
||||||
commands:
|
commands:
|
||||||
- git submodule update --init --recursive
|
- git submodule update --init --recursive
|
||||||
|
|
||||||
- name: build
|
- name: build
|
||||||
image: microsoft/dotnet:2-sdk
|
image: microsoft/dotnet:2-sdk
|
||||||
commands:
|
commands:
|
||||||
- dotnet publish --configuration release --output /release Jellyfin.Server
|
- dotnet publish "Jellyfin.Server" --configuration Debug --output "../ci/ci-debug"
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
name: build-release
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: submodules
|
||||||
|
image: docker:git
|
||||||
|
commands:
|
||||||
|
- git submodule update --init --recursive
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
image: microsoft/dotnet:2-sdk
|
||||||
|
commands:
|
||||||
|
- dotnet publish "Jellyfin.Server" --configuration Release --output "../ci/ci-release"
|
||||||
|
|
||||||
|
|||||||
+75
-9
@@ -15,6 +15,10 @@ insert_final_newline = true
|
|||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
max_line_length = null
|
max_line_length = null
|
||||||
|
|
||||||
|
# YAML indentation
|
||||||
|
[*.{yml,yaml}]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
# XML indentation
|
# XML indentation
|
||||||
[*.{csproj,xml}]
|
[*.{csproj,xml}]
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
@@ -55,15 +59,77 @@ dotnet_style_prefer_conditional_expression_over_return = true:silent
|
|||||||
###############################
|
###############################
|
||||||
# Naming Conventions #
|
# Naming Conventions #
|
||||||
###############################
|
###############################
|
||||||
# Style Definitions
|
# Style Definitions (From Roslyn)
|
||||||
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
|
|
||||||
# Use PascalCase for constant fields
|
# Non-private static fields are PascalCase
|
||||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
|
dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.severity = suggestion
|
||||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
|
dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non_private_static_fields
|
||||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
|
dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style
|
||||||
dotnet_naming_symbols.constant_fields.applicable_kinds = field
|
|
||||||
dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
|
dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field
|
||||||
dotnet_naming_symbols.constant_fields.required_modifiers = const
|
dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected internal, private protected
|
||||||
|
dotnet_naming_symbols.non_private_static_fields.required_modifiers = static
|
||||||
|
|
||||||
|
dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case
|
||||||
|
|
||||||
|
# Constants are PascalCase
|
||||||
|
dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion
|
||||||
|
dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants
|
||||||
|
dotnet_naming_rule.constants_should_be_pascal_case.style = constant_style
|
||||||
|
|
||||||
|
dotnet_naming_symbols.constants.applicable_kinds = field, local
|
||||||
|
dotnet_naming_symbols.constants.required_modifiers = const
|
||||||
|
|
||||||
|
dotnet_naming_style.constant_style.capitalization = pascal_case
|
||||||
|
|
||||||
|
# Static fields are camelCase and start with s_
|
||||||
|
dotnet_naming_rule.static_fields_should_be_camel_case.severity = suggestion
|
||||||
|
dotnet_naming_rule.static_fields_should_be_camel_case.symbols = static_fields
|
||||||
|
dotnet_naming_rule.static_fields_should_be_camel_case.style = static_field_style
|
||||||
|
|
||||||
|
dotnet_naming_symbols.static_fields.applicable_kinds = field
|
||||||
|
dotnet_naming_symbols.static_fields.required_modifiers = static
|
||||||
|
|
||||||
|
dotnet_naming_style.static_field_style.capitalization = camel_case
|
||||||
|
dotnet_naming_style.static_field_style.required_prefix = _
|
||||||
|
|
||||||
|
# Instance fields are camelCase and start with _
|
||||||
|
dotnet_naming_rule.instance_fields_should_be_camel_case.severity = suggestion
|
||||||
|
dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields
|
||||||
|
dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style
|
||||||
|
|
||||||
|
dotnet_naming_symbols.instance_fields.applicable_kinds = field
|
||||||
|
|
||||||
|
dotnet_naming_style.instance_field_style.capitalization = camel_case
|
||||||
|
dotnet_naming_style.instance_field_style.required_prefix = _
|
||||||
|
|
||||||
|
# Locals and parameters are camelCase
|
||||||
|
dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion
|
||||||
|
dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters
|
||||||
|
dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style
|
||||||
|
|
||||||
|
dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local
|
||||||
|
|
||||||
|
dotnet_naming_style.camel_case_style.capitalization = camel_case
|
||||||
|
|
||||||
|
# Local functions are PascalCase
|
||||||
|
dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion
|
||||||
|
dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions
|
||||||
|
dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style
|
||||||
|
|
||||||
|
dotnet_naming_symbols.local_functions.applicable_kinds = local_function
|
||||||
|
|
||||||
|
dotnet_naming_style.local_function_style.capitalization = pascal_case
|
||||||
|
|
||||||
|
# By default, name items with PascalCase
|
||||||
|
dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion
|
||||||
|
dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members
|
||||||
|
dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style
|
||||||
|
|
||||||
|
dotnet_naming_symbols.all_members.applicable_kinds = *
|
||||||
|
|
||||||
|
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
|
||||||
|
|
||||||
###############################
|
###############################
|
||||||
# C# Coding Conventions #
|
# C# Coding Conventions #
|
||||||
###############################
|
###############################
|
||||||
|
|||||||
@@ -8,28 +8,29 @@ assignees: ''
|
|||||||
---
|
---
|
||||||
|
|
||||||
**Describe the bug**
|
**Describe the bug**
|
||||||
A clear and concise description of what the bug is.
|
<!-- A clear and concise description of what the bug is. -->
|
||||||
|
|
||||||
**To Reproduce**
|
**To Reproduce**
|
||||||
Steps to reproduce the behavior:
|
<!-- Steps to reproduce the behavior: -->
|
||||||
1. Go to '...'
|
1. Go to '...'
|
||||||
2. Click on '....'
|
2. Click on '....'
|
||||||
3. Scroll down to '....'
|
3. Scroll down to '....'
|
||||||
4. See error
|
4. See error
|
||||||
|
|
||||||
**Expected behavior**
|
**Expected behavior**
|
||||||
A clear and concise description of what you expected to happen.
|
<!-- A clear and concise description of what you expected to happen. -->
|
||||||
|
|
||||||
**Logs**
|
**Logs**
|
||||||
Please paste any log errors.
|
<!-- Please paste any log errors. -->
|
||||||
|
|
||||||
**Screenshots**
|
**Screenshots**
|
||||||
If applicable, add screenshots to help explain your problem.
|
<!-- If applicable, add screenshots to help explain your problem. -->
|
||||||
|
|
||||||
**System (please complete the following information):**
|
**System (please complete the following information):**
|
||||||
- OS: [e.g. Docker, Debian, Windows]
|
- OS: [e.g. Docker, Debian, Windows]
|
||||||
- Browser: [e.g. Firefox, Chrome, Safari]
|
- Browser: [e.g. Firefox, Chrome, Safari]
|
||||||
- Jellyfin Version: [e.g. 10.0.1]
|
- Jellyfin Version: [e.g. 10.0.1]
|
||||||
|
- Reverse proxy: [e.g. no, nginx, apache, etc.]
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context about the problem here.
|
<!-- Add any other context about the problem here. -->
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ assignees: ''
|
|||||||
---
|
---
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
**Is your feature request related to a problem? Please describe.**
|
||||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
**Describe the solution you'd like**
|
||||||
A clear and concise description of what you want to happen.
|
<!-- A clear and concise description of what you want to happen. -->
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
**Describe alternatives you've considered**
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context or screenshots about the feature request here.
|
<!-- Add any other context or screenshots about the feature request here. -->
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ assignees: ''
|
|||||||
---
|
---
|
||||||
|
|
||||||
**Describe the feature you'd like**
|
**Describe the feature you'd like**
|
||||||
A clear and concise description of what you want to happen.
|
<!-- A clear and concise description of what you want to happen. -->
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context or screenshots about the feature request here.
|
<!-- Add any other context or screenshots about the feature request here. -->
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
<!--
|
||||||
Ensure your title is short, descriptive, and in the imperative mood (Fix X, Change Y, instead of Fixed X, Changed Y).
|
Ensure your title is short, descriptive, and in the imperative mood (Fix X, Change Y, instead of Fixed X, Changed Y).
|
||||||
For a good inspiration of what to write in commit messages and PRs please review https://chris.beams.io/posts/git-commit/ and our https://jellyfin.readthedocs.io/en/latest/developer-docs/contributing/ page.
|
For a good inspiration of what to write in commit messages and PRs please review https://chris.beams.io/posts/git-commit/ and our https://jellyfin.readthedocs.io/en/latest/developer-docs/contributing/ page.
|
||||||
|
-->
|
||||||
|
|
||||||
**Changes**
|
**Changes**
|
||||||
Describe your changes here in 1-5 sentences.
|
<!-- Describe your changes here in 1-5 sentences. -->
|
||||||
|
|
||||||
**Issues**
|
**Issues**
|
||||||
Tag any issues that this PR solves here.
|
<!-- Tag any issues that this PR solves here.
|
||||||
Fixes #
|
ex. Fixes # -->
|
||||||
|
|||||||
@@ -264,3 +264,8 @@ deployment/**/pkg-dist-tmp/
|
|||||||
deployment/collect-dist/
|
deployment/collect-dist/
|
||||||
|
|
||||||
jellyfin_version.ini
|
jellyfin_version.ini
|
||||||
|
|
||||||
|
ci/
|
||||||
|
|
||||||
|
# Doxygen
|
||||||
|
doc/
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
[submodule "MediaBrowser.WebDashboard/jellyfin-web"]
|
[submodule "MediaBrowser.WebDashboard/jellyfin-web"]
|
||||||
path = MediaBrowser.WebDashboard/jellyfin-web
|
path = MediaBrowser.WebDashboard/jellyfin-web
|
||||||
url = https://github.com/jellyfin/jellyfin-web.git
|
url = https://github.com/jellyfin/jellyfin-web.git
|
||||||
|
branch = .
|
||||||
|
|||||||
+5
-5
@@ -92,7 +92,7 @@ namespace BDInfo
|
|||||||
}
|
}
|
||||||
|
|
||||||
DirectoryRoot =
|
DirectoryRoot =
|
||||||
_fileSystem.GetDirectoryInfo(_fileSystem.GetDirectoryName(DirectoryBDMV.FullName));
|
_fileSystem.GetDirectoryInfo(Path.GetDirectoryName(DirectoryBDMV.FullName));
|
||||||
DirectoryBDJO =
|
DirectoryBDJO =
|
||||||
GetDirectory("BDJO", DirectoryBDMV, 0);
|
GetDirectory("BDJO", DirectoryBDMV, 0);
|
||||||
DirectoryCLIPINF =
|
DirectoryCLIPINF =
|
||||||
@@ -150,7 +150,7 @@ namespace BDInfo
|
|||||||
Is3D = true;
|
Is3D = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_fileSystem.FileExists(Path.Combine(DirectoryRoot.FullName, "FilmIndex.xml")))
|
if (File.Exists(Path.Combine(DirectoryRoot.FullName, "FilmIndex.xml")))
|
||||||
{
|
{
|
||||||
IsDBOX = true;
|
IsDBOX = true;
|
||||||
}
|
}
|
||||||
@@ -165,7 +165,7 @@ namespace BDInfo
|
|||||||
foreach (var file in files)
|
foreach (var file in files)
|
||||||
{
|
{
|
||||||
PlaylistFiles.Add(
|
PlaylistFiles.Add(
|
||||||
file.Name.ToUpper(), new TSPlaylistFile(this, file, _fileSystem));
|
file.Name.ToUpper(), new TSPlaylistFile(this, file));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,7 +185,7 @@ namespace BDInfo
|
|||||||
foreach (var file in files)
|
foreach (var file in files)
|
||||||
{
|
{
|
||||||
StreamClipFiles.Add(
|
StreamClipFiles.Add(
|
||||||
file.Name.ToUpper(), new TSStreamClipFile(file, _fileSystem));
|
file.Name.ToUpper(), new TSStreamClipFile(file));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,7 +345,7 @@ namespace BDInfo
|
|||||||
{
|
{
|
||||||
return dir;
|
return dir;
|
||||||
}
|
}
|
||||||
var parentFolder = _fileSystem.GetDirectoryName(dir.FullName);
|
var parentFolder = Path.GetDirectoryName(dir.FullName);
|
||||||
if (string.IsNullOrEmpty(parentFolder))
|
if (string.IsNullOrEmpty(parentFolder))
|
||||||
{
|
{
|
||||||
dir = null;
|
dir = null;
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ using System.Runtime.InteropServices;
|
|||||||
[assembly: AssemblyDescription("")]
|
[assembly: AssemblyDescription("")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCompany("Jellyfin Project")]
|
[assembly: AssemblyCompany("Jellyfin Project")]
|
||||||
[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")]
|
[assembly: AssemblyProduct("Jellyfin Server")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2016 CinemaSquid. Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")]
|
[assembly: AssemblyCopyright("Copyright © 2016 CinemaSquid. Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: NeutralResourcesLanguage("en")]
|
[assembly: NeutralResourcesLanguage("en")]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//============================================================================
|
//============================================================================
|
||||||
// BDInfo - Blu-ray Video and Audio Analysis Tool
|
// BDInfo - Blu-ray Video and Audio Analysis Tool
|
||||||
// Copyright © 2010 Cinema Squid
|
// Copyright © 2010 Cinema Squid
|
||||||
//
|
//
|
||||||
@@ -28,7 +28,6 @@ namespace BDInfo
|
|||||||
{
|
{
|
||||||
public class TSPlaylistFile
|
public class TSPlaylistFile
|
||||||
{
|
{
|
||||||
private readonly IFileSystem _fileSystem;
|
|
||||||
private FileSystemMetadata FileInfo = null;
|
private FileSystemMetadata FileInfo = null;
|
||||||
public string FileType = null;
|
public string FileType = null;
|
||||||
public bool IsInitialized = false;
|
public bool IsInitialized = false;
|
||||||
@@ -64,21 +63,19 @@ namespace BDInfo
|
|||||||
new List<TSGraphicsStream>();
|
new List<TSGraphicsStream>();
|
||||||
|
|
||||||
public TSPlaylistFile(BDROM bdrom,
|
public TSPlaylistFile(BDROM bdrom,
|
||||||
FileSystemMetadata fileInfo, IFileSystem fileSystem)
|
FileSystemMetadata fileInfo)
|
||||||
{
|
{
|
||||||
BDROM = bdrom;
|
BDROM = bdrom;
|
||||||
FileInfo = fileInfo;
|
FileInfo = fileInfo;
|
||||||
_fileSystem = fileSystem;
|
|
||||||
Name = fileInfo.Name.ToUpper();
|
Name = fileInfo.Name.ToUpper();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TSPlaylistFile(BDROM bdrom,
|
public TSPlaylistFile(BDROM bdrom,
|
||||||
string name,
|
string name,
|
||||||
List<TSStreamClip> clips, IFileSystem fileSystem)
|
List<TSStreamClip> clips)
|
||||||
{
|
{
|
||||||
BDROM = bdrom;
|
BDROM = bdrom;
|
||||||
Name = name;
|
Name = name;
|
||||||
_fileSystem = fileSystem;
|
|
||||||
IsCustom = true;
|
IsCustom = true;
|
||||||
foreach (var clip in clips)
|
foreach (var clip in clips)
|
||||||
{
|
{
|
||||||
@@ -231,7 +228,7 @@ namespace BDInfo
|
|||||||
Streams.Clear();
|
Streams.Clear();
|
||||||
StreamClips.Clear();
|
StreamClips.Clear();
|
||||||
|
|
||||||
fileStream = _fileSystem.OpenRead(FileInfo.FullName);
|
fileStream = File.OpenRead(FileInfo.FullName);
|
||||||
fileReader = new BinaryReader(fileStream);
|
fileReader = new BinaryReader(fileStream);
|
||||||
|
|
||||||
byte[] data = new byte[fileStream.Length];
|
byte[] data = new byte[fileStream.Length];
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//============================================================================
|
//============================================================================
|
||||||
// BDInfo - Blu-ray Video and Audio Analysis Tool
|
// BDInfo - Blu-ray Video and Audio Analysis Tool
|
||||||
// Copyright © 2010 Cinema Squid
|
// Copyright © 2010 Cinema Squid
|
||||||
//
|
//
|
||||||
@@ -28,7 +28,6 @@ namespace BDInfo
|
|||||||
{
|
{
|
||||||
public class TSStreamClipFile
|
public class TSStreamClipFile
|
||||||
{
|
{
|
||||||
private readonly IFileSystem _fileSystem;
|
|
||||||
public FileSystemMetadata FileInfo = null;
|
public FileSystemMetadata FileInfo = null;
|
||||||
public string FileType = null;
|
public string FileType = null;
|
||||||
public bool IsValid = false;
|
public bool IsValid = false;
|
||||||
@@ -37,10 +36,9 @@ namespace BDInfo
|
|||||||
public Dictionary<ushort, TSStream> Streams =
|
public Dictionary<ushort, TSStream> Streams =
|
||||||
new Dictionary<ushort, TSStream>();
|
new Dictionary<ushort, TSStream>();
|
||||||
|
|
||||||
public TSStreamClipFile(FileSystemMetadata fileInfo, IFileSystem fileSystem)
|
public TSStreamClipFile(FileSystemMetadata fileInfo)
|
||||||
{
|
{
|
||||||
FileInfo = fileInfo;
|
FileInfo = fileInfo;
|
||||||
_fileSystem = fileSystem;
|
|
||||||
Name = fileInfo.Name.ToUpper();
|
Name = fileInfo.Name.ToUpper();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +55,7 @@ namespace BDInfo
|
|||||||
#endif
|
#endif
|
||||||
Streams.Clear();
|
Streams.Clear();
|
||||||
|
|
||||||
fileStream = _fileSystem.OpenRead(FileInfo.FullName);
|
fileStream = File.OpenRead(FileInfo.FullName);
|
||||||
fileReader = new BinaryReader(fileStream);
|
fileReader = new BinaryReader(fileStream);
|
||||||
|
|
||||||
byte[] data = new byte[fileStream.Length];
|
byte[] data = new byte[fileStream.Length];
|
||||||
|
|||||||
@@ -15,7 +15,15 @@
|
|||||||
- [cvium](https://github.com/cvium)
|
- [cvium](https://github.com/cvium)
|
||||||
- [wtayl0r](https://github.com/wtayl0r)
|
- [wtayl0r](https://github.com/wtayl0r)
|
||||||
- [TtheCreator](https://github.com/Tthecreator)
|
- [TtheCreator](https://github.com/Tthecreator)
|
||||||
|
- [dkanada](https://github.com/dkanada)
|
||||||
- [LogicalPhallacy](https://github.com/LogicalPhallacy/)
|
- [LogicalPhallacy](https://github.com/LogicalPhallacy/)
|
||||||
|
- [RazeLighter777](https://github.com/RazeLighter777)
|
||||||
|
- [WillWill56](https://github.com/WillWill56)
|
||||||
|
- [Liggy](https://github.com/Liggy)
|
||||||
|
- [fruhnow](https://github.com/fruhnow)
|
||||||
|
- [Lynxy](https://github.com/Lynxy)
|
||||||
|
- [fasheng](https://github.com/fasheng)
|
||||||
|
- [ploughpuff](https://github.com/ploughpuff)
|
||||||
|
|
||||||
# Emby Contributors
|
# Emby Contributors
|
||||||
|
|
||||||
|
|||||||
+24
-27
@@ -1,37 +1,34 @@
|
|||||||
ARG DOTNET_VERSION=2
|
ARG DOTNET_VERSION=2.2
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION} as builder
|
||||||
# Download ffmpeg first to allow quicker rebuild of other layers
|
|
||||||
FROM alpine as ffmpeg
|
|
||||||
ARG FFMPEG_URL=https://www.johnvansickle.com/ffmpeg/old-releases/ffmpeg-4.0.3-64bit-static.tar.xz
|
|
||||||
RUN wget ${FFMPEG_URL} -O - | tar Jxf - \
|
|
||||||
&& mkdir ffmpeg-bin \
|
|
||||||
&& mv ffmpeg*/ffmpeg ffmpeg-bin \
|
|
||||||
&& mv ffmpeg*/ffprobe ffmpeg-bin
|
|
||||||
|
|
||||||
|
|
||||||
FROM microsoft/dotnet:${DOTNET_VERSION}-sdk as builder
|
|
||||||
WORKDIR /repo
|
WORKDIR /repo
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN export DOTNET_CLI_TELEMETRY_OPTOUT=1 \
|
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||||
&& dotnet clean \
|
RUN bash -c "source deployment/common.build.sh && \
|
||||||
&& dotnet publish \
|
build_jellyfin Jellyfin.Server Release linux-x64 /jellyfin"
|
||||||
--configuration release \
|
|
||||||
--output /jellyfin \
|
|
||||||
Jellyfin.Server
|
|
||||||
|
|
||||||
|
|
||||||
FROM microsoft/dotnet:${DOTNET_VERSION}-runtime
|
|
||||||
COPY --from=builder /jellyfin /jellyfin
|
|
||||||
COPY --from=ffmpeg /ffmpeg-bin/* /usr/bin/
|
|
||||||
EXPOSE 8096
|
|
||||||
VOLUME /config /media
|
|
||||||
|
|
||||||
|
FROM jellyfin/ffmpeg as ffmpeg
|
||||||
|
FROM mcr.microsoft.com/dotnet/core/runtime:${DOTNET_VERSION}
|
||||||
# libfontconfig1 is required for Skia
|
# libfontconfig1 is required for Skia
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install --no-install-recommends --no-install-suggests -y \
|
&& apt-get install --no-install-recommends --no-install-suggests -y \
|
||||||
libfontconfig1 \
|
libfontconfig1 \
|
||||||
&& apt-get clean autoclean \
|
&& apt-get clean autoclean \
|
||||||
&& apt-get autoremove \
|
&& apt-get autoremove \
|
||||||
&& rm -rf /var/lib/{apt,dpkg,cache,log}
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
ENTRYPOINT dotnet /jellyfin/jellyfin.dll -programdata /config
|
&& mkdir -p /cache /config /media \
|
||||||
|
&& chmod 777 /cache /config /media
|
||||||
|
COPY --from=ffmpeg / /
|
||||||
|
COPY --from=builder /jellyfin /jellyfin
|
||||||
|
|
||||||
|
ARG JELLYFIN_WEB_VERSION=10.3.2
|
||||||
|
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
|
||||||
|
&& rm -rf /jellyfin/jellyfin-web \
|
||||||
|
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
|
||||||
|
|
||||||
|
EXPOSE 8096
|
||||||
|
VOLUME /cache /config /media
|
||||||
|
ENTRYPOINT dotnet /jellyfin/jellyfin.dll \
|
||||||
|
--datadir /config \
|
||||||
|
--cachedir /cache \
|
||||||
|
--ffmpeg /usr/local/bin/ffmpeg
|
||||||
|
|||||||
+35
-16
@@ -1,24 +1,43 @@
|
|||||||
|
# Requires binfm_misc registration
|
||||||
|
# https://github.com/multiarch/qemu-user-static#binfmt_misc-register
|
||||||
ARG DOTNET_VERSION=3.0
|
ARG DOTNET_VERSION=3.0
|
||||||
|
|
||||||
|
|
||||||
FROM microsoft/dotnet:${DOTNET_VERSION}-sdk-stretch-arm32v7 as builder
|
FROM multiarch/qemu-user-static:x86_64-arm as qemu
|
||||||
|
FROM alpine as qemu_extract
|
||||||
|
COPY --from=qemu /usr/bin qemu-arm-static.tar.gz
|
||||||
|
RUN tar -xzvf qemu-arm-static.tar.gz
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION} as builder
|
||||||
WORKDIR /repo
|
WORKDIR /repo
|
||||||
COPY . .
|
COPY . .
|
||||||
#TODO Remove or update the sed line when we update dotnet version.
|
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||||
RUN export DOTNET_CLI_TELEMETRY_OPTOUT=1 \
|
# TODO Remove or update the sed line when we update dotnet version.
|
||||||
&& find . -type f -exec sed -i 's/netcoreapp2.1/netcoreapp3.0/g' {} \; \
|
RUN find . -type f -exec sed -i 's/netcoreapp2.1/netcoreapp3.0/g' {} \;
|
||||||
&& dotnet clean -maxcpucount:1 \
|
# Discard objs - may cause failures if exists
|
||||||
&& dotnet publish \
|
RUN find . -type d -name obj | xargs -r rm -r
|
||||||
-maxcpucount:1 \
|
# Build
|
||||||
--configuration release \
|
RUN bash -c "source deployment/common.build.sh && \
|
||||||
--output /jellyfin \
|
build_jellyfin Jellyfin.Server Release linux-arm /jellyfin"
|
||||||
Jellyfin.Server
|
|
||||||
|
|
||||||
|
|
||||||
FROM microsoft/dotnet:${DOTNET_VERSION}-runtime-stretch-slim-arm32v7
|
FROM mcr.microsoft.com/dotnet/core/runtime:${DOTNET_VERSION}-stretch-slim-arm32v7
|
||||||
COPY --from=builder /jellyfin /jellyfin
|
COPY --from=qemu_extract qemu-arm-static /usr/bin
|
||||||
EXPOSE 8096
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y ffmpeg
|
&& apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \
|
||||||
VOLUME /config /media
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
ENTRYPOINT dotnet /jellyfin/jellyfin.dll -programdata /config
|
&& mkdir -p /cache /config /media \
|
||||||
|
&& chmod 777 /cache /config /media
|
||||||
|
COPY --from=builder /jellyfin /jellyfin
|
||||||
|
|
||||||
|
ARG JELLYFIN_WEB_VERSION=10.3.2
|
||||||
|
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
|
||||||
|
&& rm -rf /jellyfin/jellyfin-web \
|
||||||
|
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
|
||||||
|
|
||||||
|
EXPOSE 8096
|
||||||
|
VOLUME /cache /config /media
|
||||||
|
ENTRYPOINT dotnet /jellyfin/jellyfin.dll \
|
||||||
|
--datadir /config \
|
||||||
|
--cachedir /cache \
|
||||||
|
--ffmpeg /usr/bin/ffmpeg
|
||||||
|
|||||||
+31
-20
@@ -1,33 +1,44 @@
|
|||||||
# Requires binfm_misc registration for aarch64
|
# Requires binfm_misc registration
|
||||||
# https://github.com/multiarch/qemu-user-static#binfmt_misc-register
|
# https://github.com/multiarch/qemu-user-static#binfmt_misc-register
|
||||||
ARG DOTNET_VERSION=3.0
|
ARG DOTNET_VERSION=3.0
|
||||||
|
|
||||||
|
|
||||||
FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
|
FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
|
||||||
FROM alpine as qemu_extract
|
FROM alpine as qemu_extract
|
||||||
COPY --from=qemu /usr/bin qemu_user_static.tgz
|
COPY --from=qemu /usr/bin qemu-aarch64-static.tar.gz
|
||||||
RUN tar -xzvf qemu_user_static.tgz
|
RUN tar -xzvf qemu-aarch64-static.tar.gz
|
||||||
|
|
||||||
|
|
||||||
FROM microsoft/dotnet:${DOTNET_VERSION}-sdk-stretch-arm64v8 as builder
|
FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION} as builder
|
||||||
COPY --from=qemu_extract qemu-* /usr/bin
|
|
||||||
WORKDIR /repo
|
WORKDIR /repo
|
||||||
COPY . .
|
COPY . .
|
||||||
#TODO Remove or update the sed line when we update dotnet version.
|
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||||
RUN export DOTNET_CLI_TELEMETRY_OPTOUT=1 \
|
# TODO Remove or update the sed line when we update dotnet version.
|
||||||
&& find . -type f -exec sed -i 's/netcoreapp2.1/netcoreapp3.0/g' {} \; \
|
RUN find . -type f -exec sed -i 's/netcoreapp2.1/netcoreapp3.0/g' {} \;
|
||||||
&& dotnet clean \
|
# Discard objs - may cause failures if exists
|
||||||
&& dotnet publish \
|
RUN find . -type d -name obj | xargs -r rm -r
|
||||||
--configuration release \
|
# Build
|
||||||
--output /jellyfin \
|
RUN bash -c "source deployment/common.build.sh && \
|
||||||
Jellyfin.Server
|
build_jellyfin Jellyfin.Server Release linux-arm64 /jellyfin"
|
||||||
|
|
||||||
|
|
||||||
FROM microsoft/dotnet:${DOTNET_VERSION}-runtime-stretch-slim-arm64v8
|
FROM mcr.microsoft.com/dotnet/core/runtime:${DOTNET_VERSION}-stretch-slim-arm64v8
|
||||||
COPY --from=qemu_extract qemu-* /usr/bin
|
COPY --from=qemu_extract qemu-aarch64-static /usr/bin
|
||||||
COPY --from=builder /jellyfin /jellyfin
|
|
||||||
EXPOSE 8096
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y ffmpeg
|
&& apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \
|
||||||
VOLUME /config /media
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
ENTRYPOINT dotnet /jellyfin/jellyfin.dll -programdata /config
|
&& mkdir -p /cache /config /media \
|
||||||
|
&& chmod 777 /cache /config /media
|
||||||
|
COPY --from=builder /jellyfin /jellyfin
|
||||||
|
|
||||||
|
ARG JELLYFIN_WEB_VERSION=10.3.2
|
||||||
|
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
|
||||||
|
&& rm -rf /jellyfin/jellyfin-web \
|
||||||
|
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
|
||||||
|
|
||||||
|
EXPOSE 8096
|
||||||
|
VOLUME /cache /config /media
|
||||||
|
ENTRYPOINT dotnet /jellyfin/jellyfin.dll \
|
||||||
|
--datadir /config \
|
||||||
|
--cachedir /cache \
|
||||||
|
--ffmpeg /usr/bin/ffmpeg
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
namespace DvdLib.Ifo
|
|
||||||
{
|
|
||||||
public enum AudioCodec
|
|
||||||
{
|
|
||||||
AC3 = 0,
|
|
||||||
MPEG1 = 2,
|
|
||||||
MPEG2ext = 3,
|
|
||||||
LPCM = 4,
|
|
||||||
DTS = 6,
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ApplicationMode
|
|
||||||
{
|
|
||||||
Unspecified = 0,
|
|
||||||
Karaoke = 1,
|
|
||||||
Surround = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AudioAttributes
|
|
||||||
{
|
|
||||||
public readonly AudioCodec Codec;
|
|
||||||
public readonly bool MultichannelExtensionPresent;
|
|
||||||
public readonly ApplicationMode Mode;
|
|
||||||
public readonly byte QuantDRC;
|
|
||||||
public readonly byte SampleRate;
|
|
||||||
public readonly byte Channels;
|
|
||||||
public readonly ushort LanguageCode;
|
|
||||||
public readonly byte LanguageExtension;
|
|
||||||
public readonly byte CodeExtension;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MultiChannelExtension
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+8
-8
@@ -26,17 +26,17 @@ namespace DvdLib.Ifo
|
|||||||
|
|
||||||
if (vmgPath == null)
|
if (vmgPath == null)
|
||||||
{
|
{
|
||||||
var allIfos = allFiles.Where(i => string.Equals(i.Extension, ".ifo", StringComparison.OrdinalIgnoreCase));
|
foreach (var ifo in allFiles)
|
||||||
|
|
||||||
foreach (var ifo in allIfos)
|
|
||||||
{
|
{
|
||||||
var num = ifo.Name.Split('_').ElementAtOrDefault(1);
|
if (!string.Equals(ifo.Extension, ".ifo", StringComparison.OrdinalIgnoreCase))
|
||||||
var numbersRead = new List<ushort>();
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(num) && ushort.TryParse(num, out var ifoNumber) && !numbersRead.Contains(ifoNumber))
|
var nums = ifo.Name.Split(new [] { '_' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
if (nums.Length >= 2 && ushort.TryParse(nums[1], out var ifoNumber))
|
||||||
{
|
{
|
||||||
ReadVTS(ifoNumber, ifo.FullName);
|
ReadVTS(ifoNumber, ifo.FullName);
|
||||||
numbersRead.Add(ifoNumber);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,7 +76,7 @@ namespace DvdLib.Ifo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReadVTS(ushort vtsNum, List<FileSystemMetadata> allFiles)
|
private void ReadVTS(ushort vtsNum, IEnumerable<FileSystemMetadata> allFiles)
|
||||||
{
|
{
|
||||||
var filename = string.Format("VTS_{0:00}_0.IFO", vtsNum);
|
var filename = string.Format("VTS_{0:00}_0.IFO", vtsNum);
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace DvdLib.Ifo
|
|
||||||
{
|
|
||||||
public class ProgramChainCommandTable
|
|
||||||
{
|
|
||||||
public readonly ushort LastByteAddress;
|
|
||||||
public readonly List<VirtualMachineCommand> PreCommands;
|
|
||||||
public readonly List<VirtualMachineCommand> PostCommands;
|
|
||||||
public readonly List<VirtualMachineCommand> CellCommands;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class VirtualMachineCommand
|
|
||||||
{
|
|
||||||
public readonly byte[] Command;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -25,13 +25,10 @@ namespace DvdLib.Ifo
|
|||||||
public byte[] SubpictureStreamControl { get; private set; } // 32*4 entries
|
public byte[] SubpictureStreamControl { get; private set; } // 32*4 entries
|
||||||
|
|
||||||
private ushort _nextProgramNumber;
|
private ushort _nextProgramNumber;
|
||||||
public readonly ProgramChain Next;
|
|
||||||
|
|
||||||
private ushort _prevProgramNumber;
|
private ushort _prevProgramNumber;
|
||||||
public readonly ProgramChain Previous;
|
|
||||||
|
|
||||||
private ushort _goupProgramNumber;
|
private ushort _goupProgramNumber;
|
||||||
public readonly ProgramChain Goup; // ?? maybe Group
|
|
||||||
|
|
||||||
public ProgramPlaybackMode PlaybackMode { get; private set; }
|
public ProgramPlaybackMode PlaybackMode { get; private set; }
|
||||||
public uint ProgramCount { get; private set; }
|
public uint ProgramCount { get; private set; }
|
||||||
@@ -40,7 +37,6 @@ namespace DvdLib.Ifo
|
|||||||
public byte[] Palette { get; private set; } // 16*4 entries
|
public byte[] Palette { get; private set; } // 16*4 entries
|
||||||
|
|
||||||
private ushort _commandTableOffset;
|
private ushort _commandTableOffset;
|
||||||
public readonly ProgramChainCommandTable CommandTable;
|
|
||||||
|
|
||||||
private ushort _programMapOffset;
|
private ushort _programMapOffset;
|
||||||
private ushort _cellPlaybackOffset;
|
private ushort _cellPlaybackOffset;
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
namespace DvdLib.Ifo
|
|
||||||
{
|
|
||||||
public enum VideoCodec
|
|
||||||
{
|
|
||||||
MPEG1 = 0,
|
|
||||||
MPEG2 = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum VideoFormat
|
|
||||||
{
|
|
||||||
NTSC = 0,
|
|
||||||
PAL = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum AspectRatio
|
|
||||||
{
|
|
||||||
ar4to3 = 0,
|
|
||||||
ar16to9 = 3
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum FilmMode
|
|
||||||
{
|
|
||||||
None = -1,
|
|
||||||
Camera = 0,
|
|
||||||
Film = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
public class VideoAttributes
|
|
||||||
{
|
|
||||||
public readonly VideoCodec Codec;
|
|
||||||
public readonly VideoFormat Format;
|
|
||||||
public readonly AspectRatio Aspect;
|
|
||||||
public readonly bool AutomaticPanScan;
|
|
||||||
public readonly bool AutomaticLetterBox;
|
|
||||||
public readonly bool Line21CCField1;
|
|
||||||
public readonly bool Line21CCField2;
|
|
||||||
public readonly int Width;
|
|
||||||
public readonly int Height;
|
|
||||||
public readonly bool Letterboxed;
|
|
||||||
public readonly FilmMode FilmMode;
|
|
||||||
|
|
||||||
public VideoAttributes()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,8 +9,8 @@ using System.Runtime.InteropServices;
|
|||||||
[assembly: AssemblyDescription("")]
|
[assembly: AssemblyDescription("")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCompany("Jellyfin Project")]
|
[assembly: AssemblyCompany("Jellyfin Project")]
|
||||||
[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")]
|
[assembly: AssemblyProduct("Jellyfin Server")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")]
|
[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: NeutralResourcesLanguage("en")]
|
[assembly: NeutralResourcesLanguage("en")]
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ namespace Emby.Dlna.Api
|
|||||||
{
|
{
|
||||||
var url = Request.AbsoluteUri;
|
var url = Request.AbsoluteUri;
|
||||||
var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase));
|
var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase));
|
||||||
var xml = _dlnaManager.GetServerDescriptionXml(Request.Headers.ToDictionary(), request.UuId, serverAddress);
|
var xml = _dlnaManager.GetServerDescriptionXml(Request.Headers, request.UuId, serverAddress);
|
||||||
|
|
||||||
var cacheLength = TimeSpan.FromDays(1);
|
var cacheLength = TimeSpan.FromDays(1);
|
||||||
var cacheKey = Request.RawUrl.GetMD5();
|
var cacheKey = Request.RawUrl.GetMD5();
|
||||||
@@ -147,21 +147,21 @@ namespace Emby.Dlna.Api
|
|||||||
|
|
||||||
public object Get(GetContentDirectory request)
|
public object Get(GetContentDirectory request)
|
||||||
{
|
{
|
||||||
var xml = ContentDirectory.GetServiceXml(Request.Headers.ToDictionary());
|
var xml = ContentDirectory.GetServiceXml();
|
||||||
|
|
||||||
return _resultFactory.GetResult(Request, xml, XMLContentType);
|
return _resultFactory.GetResult(Request, xml, XMLContentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Get(GetMediaReceiverRegistrar request)
|
public object Get(GetMediaReceiverRegistrar request)
|
||||||
{
|
{
|
||||||
var xml = MediaReceiverRegistrar.GetServiceXml(Request.Headers.ToDictionary());
|
var xml = MediaReceiverRegistrar.GetServiceXml();
|
||||||
|
|
||||||
return _resultFactory.GetResult(Request, xml, XMLContentType);
|
return _resultFactory.GetResult(Request, xml, XMLContentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Get(GetConnnectionManager request)
|
public object Get(GetConnnectionManager request)
|
||||||
{
|
{
|
||||||
var xml = ConnectionManager.GetServiceXml(Request.Headers.ToDictionary());
|
var xml = ConnectionManager.GetServiceXml();
|
||||||
|
|
||||||
return _resultFactory.GetResult(Request, xml, XMLContentType);
|
return _resultFactory.GetResult(Request, xml, XMLContentType);
|
||||||
}
|
}
|
||||||
@@ -193,7 +193,7 @@ namespace Emby.Dlna.Api
|
|||||||
|
|
||||||
return service.ProcessControlRequest(new ControlRequest
|
return service.ProcessControlRequest(new ControlRequest
|
||||||
{
|
{
|
||||||
Headers = Request.Headers.ToDictionary(),
|
Headers = Request.Headers,
|
||||||
InputXml = requestStream,
|
InputXml = requestStream,
|
||||||
TargetServerUuId = id,
|
TargetServerUuId = id,
|
||||||
RequestedUrl = Request.AbsoluteUri
|
RequestedUrl = Request.AbsoluteUri
|
||||||
@@ -236,7 +236,9 @@ namespace Emby.Dlna.Api
|
|||||||
|
|
||||||
public object Get(GetIcon request)
|
public object Get(GetIcon request)
|
||||||
{
|
{
|
||||||
var contentType = "image/" + Path.GetExtension(request.Filename).TrimStart('.').ToLower();
|
var contentType = "image/" + Path.GetExtension(request.Filename)
|
||||||
|
.TrimStart('.')
|
||||||
|
.ToLowerInvariant();
|
||||||
|
|
||||||
var cacheLength = TimeSpan.FromDays(365);
|
var cacheLength = TimeSpan.FromDays(365);
|
||||||
var cacheKey = Request.RawUrl.GetMD5();
|
var cacheKey = Request.RawUrl.GetMD5();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ namespace Emby.Dlna.Configuration
|
|||||||
public bool EnableServer { get; set; }
|
public bool EnableServer { get; set; }
|
||||||
public bool EnableDebugLog { get; set; }
|
public bool EnableDebugLog { get; set; }
|
||||||
public bool BlastAliveMessages { get; set; }
|
public bool BlastAliveMessages { get; set; }
|
||||||
|
public bool SendOnlyMatchedHost { get; set; }
|
||||||
public int ClientDiscoveryIntervalSeconds { get; set; }
|
public int ClientDiscoveryIntervalSeconds { get; set; }
|
||||||
public int BlastAliveMessageIntervalSeconds { get; set; }
|
public int BlastAliveMessageIntervalSeconds { get; set; }
|
||||||
public string DefaultUserId { get; set; }
|
public string DefaultUserId { get; set; }
|
||||||
@@ -16,6 +17,7 @@ namespace Emby.Dlna.Configuration
|
|||||||
EnablePlayTo = true;
|
EnablePlayTo = true;
|
||||||
EnableServer = true;
|
EnableServer = true;
|
||||||
BlastAliveMessages = true;
|
BlastAliveMessages = true;
|
||||||
|
SendOnlyMatchedHost = true;
|
||||||
ClientDiscoveryIntervalSeconds = 60;
|
ClientDiscoveryIntervalSeconds = 60;
|
||||||
BlastAliveMessageIntervalSeconds = 1800;
|
BlastAliveMessageIntervalSeconds = 1800;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using Emby.Dlna.Service;
|
using Emby.Dlna.Service;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Dlna;
|
using MediaBrowser.Controller.Dlna;
|
||||||
using MediaBrowser.Model.Xml;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Dlna.ConnectionManager
|
namespace Emby.Dlna.ConnectionManager
|
||||||
@@ -13,18 +11,16 @@ namespace Emby.Dlna.ConnectionManager
|
|||||||
private readonly IDlnaManager _dlna;
|
private readonly IDlnaManager _dlna;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IServerConfigurationManager _config;
|
private readonly IServerConfigurationManager _config;
|
||||||
protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory;
|
|
||||||
|
|
||||||
public ConnectionManager(IDlnaManager dlna, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
|
public ConnectionManager(IDlnaManager dlna, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient)
|
||||||
: base(logger, httpClient)
|
: base(logger, httpClient)
|
||||||
{
|
{
|
||||||
_dlna = dlna;
|
_dlna = dlna;
|
||||||
_config = config;
|
_config = config;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
XmlReaderSettingsFactory = xmlReaderSettingsFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetServiceXml(IDictionary<string, string> headers)
|
public string GetServiceXml()
|
||||||
{
|
{
|
||||||
return new ConnectionManagerXmlBuilder().GetXml();
|
return new ConnectionManagerXmlBuilder().GetXml();
|
||||||
}
|
}
|
||||||
@@ -34,7 +30,7 @@ namespace Emby.Dlna.ConnectionManager
|
|||||||
var profile = _dlna.GetProfile(request.Headers) ??
|
var profile = _dlna.GetProfile(request.Headers) ??
|
||||||
_dlna.GetDefaultProfile();
|
_dlna.GetDefaultProfile();
|
||||||
|
|
||||||
return new ControlHandler(_config, _logger, XmlReaderSettingsFactory, profile).ProcessControlRequest(request);
|
return new ControlHandler(_config, _logger, profile).ProcessControlRequest(request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ using Emby.Dlna.Service;
|
|||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Model.Dlna;
|
using MediaBrowser.Model.Dlna;
|
||||||
using MediaBrowser.Model.Xml;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Dlna.ConnectionManager
|
namespace Emby.Dlna.ConnectionManager
|
||||||
@@ -32,7 +31,8 @@ namespace Emby.Dlna.ConnectionManager
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public ControlHandler(IServerConfigurationManager config, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory, DeviceProfile profile) : base(config, logger, xmlReaderSettingsFactory)
|
public ControlHandler(IServerConfigurationManager config, ILogger logger, DeviceProfile profile)
|
||||||
|
: base(config, logger)
|
||||||
{
|
{
|
||||||
_profile = profile;
|
_profile = profile;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ using MediaBrowser.Controller.MediaEncoding;
|
|||||||
using MediaBrowser.Controller.TV;
|
using MediaBrowser.Controller.TV;
|
||||||
using MediaBrowser.Model.Dlna;
|
using MediaBrowser.Model.Dlna;
|
||||||
using MediaBrowser.Model.Globalization;
|
using MediaBrowser.Model.Globalization;
|
||||||
using MediaBrowser.Model.Xml;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Dlna.ContentDirectory
|
namespace Emby.Dlna.ContentDirectory
|
||||||
@@ -28,7 +27,6 @@ namespace Emby.Dlna.ContentDirectory
|
|||||||
private readonly IMediaSourceManager _mediaSourceManager;
|
private readonly IMediaSourceManager _mediaSourceManager;
|
||||||
private readonly IUserViewManager _userViewManager;
|
private readonly IUserViewManager _userViewManager;
|
||||||
private readonly IMediaEncoder _mediaEncoder;
|
private readonly IMediaEncoder _mediaEncoder;
|
||||||
protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory;
|
|
||||||
private readonly ITVSeriesManager _tvSeriesManager;
|
private readonly ITVSeriesManager _tvSeriesManager;
|
||||||
|
|
||||||
public ContentDirectory(IDlnaManager dlna,
|
public ContentDirectory(IDlnaManager dlna,
|
||||||
@@ -38,7 +36,12 @@ namespace Emby.Dlna.ContentDirectory
|
|||||||
IServerConfigurationManager config,
|
IServerConfigurationManager config,
|
||||||
IUserManager userManager,
|
IUserManager userManager,
|
||||||
ILogger logger,
|
ILogger logger,
|
||||||
IHttpClient httpClient, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, IMediaEncoder mediaEncoder, IXmlReaderSettingsFactory xmlReaderSettingsFactory, ITVSeriesManager tvSeriesManager)
|
IHttpClient httpClient,
|
||||||
|
ILocalizationManager localization,
|
||||||
|
IMediaSourceManager mediaSourceManager,
|
||||||
|
IUserViewManager userViewManager,
|
||||||
|
IMediaEncoder mediaEncoder,
|
||||||
|
ITVSeriesManager tvSeriesManager)
|
||||||
: base(logger, httpClient)
|
: base(logger, httpClient)
|
||||||
{
|
{
|
||||||
_dlna = dlna;
|
_dlna = dlna;
|
||||||
@@ -51,7 +54,6 @@ namespace Emby.Dlna.ContentDirectory
|
|||||||
_mediaSourceManager = mediaSourceManager;
|
_mediaSourceManager = mediaSourceManager;
|
||||||
_userViewManager = userViewManager;
|
_userViewManager = userViewManager;
|
||||||
_mediaEncoder = mediaEncoder;
|
_mediaEncoder = mediaEncoder;
|
||||||
XmlReaderSettingsFactory = xmlReaderSettingsFactory;
|
|
||||||
_tvSeriesManager = tvSeriesManager;
|
_tvSeriesManager = tvSeriesManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +67,7 @@ namespace Emby.Dlna.ContentDirectory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetServiceXml(IDictionary<string, string> headers)
|
public string GetServiceXml()
|
||||||
{
|
{
|
||||||
return new ContentDirectoryXmlBuilder().GetXml();
|
return new ContentDirectoryXmlBuilder().GetXml();
|
||||||
}
|
}
|
||||||
@@ -76,7 +78,6 @@ namespace Emby.Dlna.ContentDirectory
|
|||||||
_dlna.GetDefaultProfile();
|
_dlna.GetDefaultProfile();
|
||||||
|
|
||||||
var serverAddress = request.RequestedUrl.Substring(0, request.RequestedUrl.IndexOf("/dlna", StringComparison.OrdinalIgnoreCase));
|
var serverAddress = request.RequestedUrl.Substring(0, request.RequestedUrl.IndexOf("/dlna", StringComparison.OrdinalIgnoreCase));
|
||||||
string accessToken = null;
|
|
||||||
|
|
||||||
var user = GetUser(profile);
|
var user = GetUser(profile);
|
||||||
|
|
||||||
@@ -85,7 +86,7 @@ namespace Emby.Dlna.ContentDirectory
|
|||||||
_libraryManager,
|
_libraryManager,
|
||||||
profile,
|
profile,
|
||||||
serverAddress,
|
serverAddress,
|
||||||
accessToken,
|
null,
|
||||||
_imageProcessor,
|
_imageProcessor,
|
||||||
_userDataManager,
|
_userDataManager,
|
||||||
user,
|
user,
|
||||||
@@ -95,7 +96,6 @@ namespace Emby.Dlna.ContentDirectory
|
|||||||
_mediaSourceManager,
|
_mediaSourceManager,
|
||||||
_userViewManager,
|
_userViewManager,
|
||||||
_mediaEncoder,
|
_mediaEncoder,
|
||||||
XmlReaderSettingsFactory,
|
|
||||||
_tvSeriesManager)
|
_tvSeriesManager)
|
||||||
.ProcessControlRequest(request);
|
.ProcessControlRequest(request);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ using MediaBrowser.Model.Dlna;
|
|||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Globalization;
|
using MediaBrowser.Model.Globalization;
|
||||||
using MediaBrowser.Model.Querying;
|
using MediaBrowser.Model.Querying;
|
||||||
using MediaBrowser.Model.Xml;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Dlna.ContentDirectory
|
namespace Emby.Dlna.ContentDirectory
|
||||||
@@ -51,8 +50,22 @@ namespace Emby.Dlna.ContentDirectory
|
|||||||
|
|
||||||
private readonly DeviceProfile _profile;
|
private readonly DeviceProfile _profile;
|
||||||
|
|
||||||
public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, string accessToken, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, IMediaEncoder mediaEncoder, IXmlReaderSettingsFactory xmlReaderSettingsFactory, ITVSeriesManager tvSeriesManager)
|
public ControlHandler(
|
||||||
: base(config, logger, xmlReaderSettingsFactory)
|
ILogger logger,
|
||||||
|
ILibraryManager libraryManager,
|
||||||
|
DeviceProfile profile,
|
||||||
|
string serverAddress,
|
||||||
|
string accessToken,
|
||||||
|
IImageProcessor imageProcessor,
|
||||||
|
IUserDataManager userDataManager,
|
||||||
|
User user, int systemUpdateId,
|
||||||
|
IServerConfigurationManager config,
|
||||||
|
ILocalizationManager localization,
|
||||||
|
IMediaSourceManager mediaSourceManager,
|
||||||
|
IUserViewManager userViewManager,
|
||||||
|
IMediaEncoder mediaEncoder,
|
||||||
|
ITVSeriesManager tvSeriesManager)
|
||||||
|
: base(config, logger)
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
_userDataManager = userDataManager;
|
_userDataManager = userDataManager;
|
||||||
@@ -63,7 +76,7 @@ namespace Emby.Dlna.ContentDirectory
|
|||||||
_profile = profile;
|
_profile = profile;
|
||||||
_config = config;
|
_config = config;
|
||||||
|
|
||||||
_didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, _logger, libraryManager, mediaEncoder);
|
_didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, _logger, mediaEncoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams)
|
protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams)
|
||||||
@@ -260,7 +273,7 @@ namespace Emby.Dlna.ContentDirectory
|
|||||||
|
|
||||||
if (item.IsDisplayedAsFolder || serverItem.StubType.HasValue)
|
if (item.IsDisplayedAsFolder || serverItem.StubType.HasValue)
|
||||||
{
|
{
|
||||||
var childrenResult = (GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount));
|
var childrenResult = GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount);
|
||||||
|
|
||||||
_didlBuilder.WriteFolderElement(writer, item, serverItem.StubType, null, childrenResult.TotalRecordCount, filter, id);
|
_didlBuilder.WriteFolderElement(writer, item, serverItem.StubType, null, childrenResult.TotalRecordCount, filter, id);
|
||||||
}
|
}
|
||||||
@@ -273,7 +286,7 @@ namespace Emby.Dlna.ContentDirectory
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var childrenResult = (GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount));
|
var childrenResult = GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount);
|
||||||
totalCount = childrenResult.TotalRecordCount;
|
totalCount = childrenResult.TotalRecordCount;
|
||||||
|
|
||||||
provided = childrenResult.Items.Length;
|
provided = childrenResult.Items.Length;
|
||||||
@@ -454,7 +467,7 @@ namespace Emby.Dlna.ContentDirectory
|
|||||||
User = user,
|
User = user,
|
||||||
Recursive = true,
|
Recursive = true,
|
||||||
IsMissing = false,
|
IsMissing = false,
|
||||||
ExcludeItemTypes = new[] { typeof(Game).Name, typeof(Book).Name },
|
ExcludeItemTypes = new[] { typeof(Book).Name },
|
||||||
IsFolder = isFolder,
|
IsFolder = isFolder,
|
||||||
MediaTypes = mediaTypes.ToArray(),
|
MediaTypes = mediaTypes.ToArray(),
|
||||||
DtoOptions = GetDtoOptions()
|
DtoOptions = GetDtoOptions()
|
||||||
@@ -483,27 +496,26 @@ namespace Emby.Dlna.ContentDirectory
|
|||||||
return GetGenreItems(item, Guid.Empty, user, sort, startIndex, limit);
|
return GetGenreItems(item, Guid.Empty, user, sort, startIndex, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stubType.HasValue || stubType.Value != StubType.Folder)
|
if ((!stubType.HasValue || stubType.Value != StubType.Folder)
|
||||||
|
&& item is IHasCollectionType collectionFolder)
|
||||||
{
|
{
|
||||||
var collectionFolder = item as IHasCollectionType;
|
if (string.Equals(CollectionType.Music, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
|
||||||
if (collectionFolder != null && string.Equals(CollectionType.Music, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
{
|
||||||
return GetMusicFolders(item, user, stubType, sort, startIndex, limit);
|
return GetMusicFolders(item, user, stubType, sort, startIndex, limit);
|
||||||
}
|
}
|
||||||
if (collectionFolder != null && string.Equals(CollectionType.Movies, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
|
else if (string.Equals(CollectionType.Movies, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return GetMovieFolders(item, user, stubType, sort, startIndex, limit);
|
return GetMovieFolders(item, user, stubType, sort, startIndex, limit);
|
||||||
}
|
}
|
||||||
if (collectionFolder != null && string.Equals(CollectionType.TvShows, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
|
else if (string.Equals(CollectionType.TvShows, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return GetTvFolders(item, user, stubType, sort, startIndex, limit);
|
return GetTvFolders(item, user, stubType, sort, startIndex, limit);
|
||||||
}
|
}
|
||||||
|
else if (string.Equals(CollectionType.Folders, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
|
||||||
if (collectionFolder != null && string.Equals(CollectionType.Folders, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
{
|
||||||
return GetFolders(item, user, stubType, sort, startIndex, limit);
|
return GetFolders(item, user, stubType, sort, startIndex, limit);
|
||||||
}
|
}
|
||||||
if (collectionFolder != null && string.Equals(CollectionType.LiveTv, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
|
else if (string.Equals(CollectionType.LiveTv, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return GetLiveTvChannels(item, user, stubType, sort, startIndex, limit);
|
return GetLiveTvChannels(item, user, stubType, sort, startIndex, limit);
|
||||||
}
|
}
|
||||||
@@ -524,7 +536,7 @@ namespace Emby.Dlna.ContentDirectory
|
|||||||
Limit = limit,
|
Limit = limit,
|
||||||
StartIndex = startIndex,
|
StartIndex = startIndex,
|
||||||
IsVirtualItem = false,
|
IsVirtualItem = false,
|
||||||
ExcludeItemTypes = new[] { typeof(Game).Name, typeof(Book).Name },
|
ExcludeItemTypes = new[] { typeof(Book).Name },
|
||||||
IsPlaceHolder = false,
|
IsPlaceHolder = false,
|
||||||
DtoOptions = GetDtoOptions()
|
DtoOptions = GetDtoOptions()
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
namespace Emby.Dlna
|
namespace Emby.Dlna
|
||||||
{
|
{
|
||||||
public class ControlRequest
|
public class ControlRequest
|
||||||
{
|
{
|
||||||
public IDictionary<string, string> Headers { get; set; }
|
public IHeaderDictionary Headers { get; set; }
|
||||||
|
|
||||||
public Stream InputXml { get; set; }
|
public Stream InputXml { get; set; }
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ namespace Emby.Dlna
|
|||||||
|
|
||||||
public ControlRequest()
|
public ControlRequest()
|
||||||
{
|
{
|
||||||
Headers = new Dictionary<string, string>();
|
Headers = new HeaderDictionary();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+68
-115
@@ -43,22 +43,30 @@ namespace Emby.Dlna.Didl
|
|||||||
private readonly ILocalizationManager _localization;
|
private readonly ILocalizationManager _localization;
|
||||||
private readonly IMediaSourceManager _mediaSourceManager;
|
private readonly IMediaSourceManager _mediaSourceManager;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly ILibraryManager _libraryManager;
|
|
||||||
private readonly IMediaEncoder _mediaEncoder;
|
private readonly IMediaEncoder _mediaEncoder;
|
||||||
|
|
||||||
public DidlBuilder(DeviceProfile profile, User user, IImageProcessor imageProcessor, string serverAddress, string accessToken, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, ILogger logger, ILibraryManager libraryManager, IMediaEncoder mediaEncoder)
|
public DidlBuilder(
|
||||||
|
DeviceProfile profile,
|
||||||
|
User user,
|
||||||
|
IImageProcessor imageProcessor,
|
||||||
|
string serverAddress,
|
||||||
|
string accessToken,
|
||||||
|
IUserDataManager userDataManager,
|
||||||
|
ILocalizationManager localization,
|
||||||
|
IMediaSourceManager mediaSourceManager,
|
||||||
|
ILogger logger,
|
||||||
|
IMediaEncoder mediaEncoder)
|
||||||
{
|
{
|
||||||
_profile = profile;
|
_profile = profile;
|
||||||
|
_user = user;
|
||||||
_imageProcessor = imageProcessor;
|
_imageProcessor = imageProcessor;
|
||||||
_serverAddress = serverAddress;
|
_serverAddress = serverAddress;
|
||||||
|
_accessToken = accessToken;
|
||||||
_userDataManager = userDataManager;
|
_userDataManager = userDataManager;
|
||||||
_localization = localization;
|
_localization = localization;
|
||||||
_mediaSourceManager = mediaSourceManager;
|
_mediaSourceManager = mediaSourceManager;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_libraryManager = libraryManager;
|
|
||||||
_mediaEncoder = mediaEncoder;
|
_mediaEncoder = mediaEncoder;
|
||||||
_accessToken = accessToken;
|
|
||||||
_user = user;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string NormalizeDlnaMediaUrl(string url)
|
public static string NormalizeDlnaMediaUrl(string url)
|
||||||
@@ -117,7 +125,8 @@ namespace Emby.Dlna.Didl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WriteItemElement(DlnaOptions options,
|
public void WriteItemElement(
|
||||||
|
DlnaOptions options,
|
||||||
XmlWriter writer,
|
XmlWriter writer,
|
||||||
BaseItem item,
|
BaseItem item,
|
||||||
User user,
|
User user,
|
||||||
@@ -232,12 +241,15 @@ namespace Emby.Dlna.Didl
|
|||||||
AddVideoResource(writer, video, deviceId, filter, contentFeature, streamInfo);
|
AddVideoResource(writer, video, deviceId, filter, contentFeature, streamInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
var subtitleProfiles = streamInfo.GetSubtitleProfiles(_mediaEncoder, false, _serverAddress, _accessToken)
|
var subtitleProfiles = streamInfo.GetSubtitleProfiles(_mediaEncoder, false, _serverAddress, _accessToken);
|
||||||
.Where(subtitle => subtitle.DeliveryMethod == SubtitleDeliveryMethod.External)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
foreach (var subtitle in subtitleProfiles)
|
foreach (var subtitle in subtitleProfiles)
|
||||||
{
|
{
|
||||||
|
if (subtitle.DeliveryMethod != SubtitleDeliveryMethod.External)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var subtitleAdded = AddSubtitleElement(writer, subtitle);
|
var subtitleAdded = AddSubtitleElement(writer, subtitle);
|
||||||
|
|
||||||
if (subtitleAdded && _profile.EnableSingleSubtitleLimit)
|
if (subtitleAdded && _profile.EnableSingleSubtitleLimit)
|
||||||
@@ -250,7 +262,8 @@ namespace Emby.Dlna.Didl
|
|||||||
private bool AddSubtitleElement(XmlWriter writer, SubtitleStreamInfo info)
|
private bool AddSubtitleElement(XmlWriter writer, SubtitleStreamInfo info)
|
||||||
{
|
{
|
||||||
var subtitleProfile = _profile.SubtitleProfiles
|
var subtitleProfile = _profile.SubtitleProfiles
|
||||||
.FirstOrDefault(i => string.Equals(info.Format, i.Format, StringComparison.OrdinalIgnoreCase) && i.Method == SubtitleDeliveryMethod.External);
|
.FirstOrDefault(i => string.Equals(info.Format, i.Format, StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& i.Method == SubtitleDeliveryMethod.External);
|
||||||
|
|
||||||
if (subtitleProfile == null)
|
if (subtitleProfile == null)
|
||||||
{
|
{
|
||||||
@@ -265,7 +278,7 @@ namespace Emby.Dlna.Didl
|
|||||||
// <sec:CaptionInfo sec:type="srt">http://192.168.1.3:9999/video.srt</sec:CaptionInfo>
|
// <sec:CaptionInfo sec:type="srt">http://192.168.1.3:9999/video.srt</sec:CaptionInfo>
|
||||||
|
|
||||||
writer.WriteStartElement("sec", "CaptionInfoEx", null);
|
writer.WriteStartElement("sec", "CaptionInfoEx", null);
|
||||||
writer.WriteAttributeString("sec", "type", null, info.Format.ToLower());
|
writer.WriteAttributeString("sec", "type", null, info.Format.ToLowerInvariant());
|
||||||
|
|
||||||
writer.WriteString(info.Url);
|
writer.WriteString(info.Url);
|
||||||
writer.WriteFullEndElement();
|
writer.WriteFullEndElement();
|
||||||
@@ -282,7 +295,7 @@ namespace Emby.Dlna.Didl
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
writer.WriteStartElement(string.Empty, "res", NS_DIDL);
|
writer.WriteStartElement(string.Empty, "res", NS_DIDL);
|
||||||
var protocolInfo = string.Format("http-get:*:text/{0}:*", info.Format.ToLower());
|
var protocolInfo = string.Format("http-get:*:text/{0}:*", info.Format.ToLowerInvariant());
|
||||||
writer.WriteAttributeString("protocolInfo", protocolInfo);
|
writer.WriteAttributeString("protocolInfo", protocolInfo);
|
||||||
|
|
||||||
writer.WriteString(info.Url);
|
writer.WriteString(info.Url);
|
||||||
@@ -387,91 +400,39 @@ namespace Emby.Dlna.Didl
|
|||||||
|
|
||||||
private string GetDisplayName(BaseItem item, StubType? itemStubType, BaseItem context)
|
private string GetDisplayName(BaseItem item, StubType? itemStubType, BaseItem context)
|
||||||
{
|
{
|
||||||
if (itemStubType.HasValue && itemStubType.Value == StubType.Latest)
|
if (itemStubType.HasValue)
|
||||||
{
|
{
|
||||||
return _localization.GetLocalizedString("Latest");
|
switch (itemStubType.Value)
|
||||||
}
|
{
|
||||||
if (itemStubType.HasValue && itemStubType.Value == StubType.Playlists)
|
case StubType.Latest: return _localization.GetLocalizedString("Latest");
|
||||||
{
|
case StubType.Playlists: return _localization.GetLocalizedString("Playlists");
|
||||||
return _localization.GetLocalizedString("Playlists");
|
case StubType.AlbumArtists: return _localization.GetLocalizedString("HeaderAlbumArtists");
|
||||||
}
|
case StubType.Albums: return _localization.GetLocalizedString("Albums");
|
||||||
if (itemStubType.HasValue && itemStubType.Value == StubType.AlbumArtists)
|
case StubType.Artists: return _localization.GetLocalizedString("Artists");
|
||||||
{
|
case StubType.Songs: return _localization.GetLocalizedString("Songs");
|
||||||
return _localization.GetLocalizedString("HeaderAlbumArtists");
|
case StubType.Genres: return _localization.GetLocalizedString("Genres");
|
||||||
}
|
case StubType.FavoriteAlbums: return _localization.GetLocalizedString("HeaderFavoriteAlbums");
|
||||||
if (itemStubType.HasValue && itemStubType.Value == StubType.Albums)
|
case StubType.FavoriteArtists: return _localization.GetLocalizedString("HeaderFavoriteArtists");
|
||||||
{
|
case StubType.FavoriteSongs: return _localization.GetLocalizedString("HeaderFavoriteSongs");
|
||||||
return _localization.GetLocalizedString("Albums");
|
case StubType.ContinueWatching: return _localization.GetLocalizedString("HeaderContinueWatching");
|
||||||
}
|
case StubType.Movies: return _localization.GetLocalizedString("Movies");
|
||||||
if (itemStubType.HasValue && itemStubType.Value == StubType.Artists)
|
case StubType.Collections: return _localization.GetLocalizedString("Collections");
|
||||||
{
|
case StubType.Favorites: return _localization.GetLocalizedString("Favorites");
|
||||||
return _localization.GetLocalizedString("Artists");
|
case StubType.NextUp: return _localization.GetLocalizedString("HeaderNextUp");
|
||||||
}
|
case StubType.FavoriteSeries: return _localization.GetLocalizedString("HeaderFavoriteShows");
|
||||||
if (itemStubType.HasValue && itemStubType.Value == StubType.Songs)
|
case StubType.FavoriteEpisodes: return _localization.GetLocalizedString("HeaderFavoriteEpisodes");
|
||||||
{
|
case StubType.Series: return _localization.GetLocalizedString("Shows");
|
||||||
return _localization.GetLocalizedString("Songs");
|
default: break;
|
||||||
}
|
}
|
||||||
if (itemStubType.HasValue && itemStubType.Value == StubType.Genres)
|
|
||||||
{
|
|
||||||
return _localization.GetLocalizedString("Genres");
|
|
||||||
}
|
|
||||||
if (itemStubType.HasValue && itemStubType.Value == StubType.FavoriteAlbums)
|
|
||||||
{
|
|
||||||
return _localization.GetLocalizedString("HeaderFavoriteAlbums");
|
|
||||||
}
|
|
||||||
if (itemStubType.HasValue && itemStubType.Value == StubType.FavoriteArtists)
|
|
||||||
{
|
|
||||||
return _localization.GetLocalizedString("HeaderFavoriteArtists");
|
|
||||||
}
|
|
||||||
if (itemStubType.HasValue && itemStubType.Value == StubType.FavoriteSongs)
|
|
||||||
{
|
|
||||||
return _localization.GetLocalizedString("HeaderFavoriteSongs");
|
|
||||||
}
|
|
||||||
if (itemStubType.HasValue && itemStubType.Value == StubType.ContinueWatching)
|
|
||||||
{
|
|
||||||
return _localization.GetLocalizedString("HeaderContinueWatching");
|
|
||||||
}
|
|
||||||
if (itemStubType.HasValue && itemStubType.Value == StubType.Movies)
|
|
||||||
{
|
|
||||||
return _localization.GetLocalizedString("Movies");
|
|
||||||
}
|
|
||||||
if (itemStubType.HasValue && itemStubType.Value == StubType.Collections)
|
|
||||||
{
|
|
||||||
return _localization.GetLocalizedString("Collections");
|
|
||||||
}
|
|
||||||
if (itemStubType.HasValue && itemStubType.Value == StubType.Favorites)
|
|
||||||
{
|
|
||||||
return _localization.GetLocalizedString("Favorites");
|
|
||||||
}
|
|
||||||
if (itemStubType.HasValue && itemStubType.Value == StubType.NextUp)
|
|
||||||
{
|
|
||||||
return _localization.GetLocalizedString("HeaderNextUp");
|
|
||||||
}
|
|
||||||
if (itemStubType.HasValue && itemStubType.Value == StubType.FavoriteSeries)
|
|
||||||
{
|
|
||||||
return _localization.GetLocalizedString("HeaderFavoriteShows");
|
|
||||||
}
|
|
||||||
if (itemStubType.HasValue && itemStubType.Value == StubType.FavoriteEpisodes)
|
|
||||||
{
|
|
||||||
return _localization.GetLocalizedString("HeaderFavoriteEpisodes");
|
|
||||||
}
|
|
||||||
if (itemStubType.HasValue && itemStubType.Value == StubType.Series)
|
|
||||||
{
|
|
||||||
return _localization.GetLocalizedString("Shows");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var episode = item as Episode;
|
if (item is Episode episode && context is Season season)
|
||||||
var season = context as Season;
|
|
||||||
|
|
||||||
if (episode != null && season != null)
|
|
||||||
{
|
{
|
||||||
// This is a special embedded within a season
|
// This is a special embedded within a season
|
||||||
if (item.ParentIndexNumber.HasValue && item.ParentIndexNumber.Value == 0)
|
if (item.ParentIndexNumber.HasValue && item.ParentIndexNumber.Value == 0
|
||||||
|
&& season.IndexNumber.HasValue && season.IndexNumber.Value != 0)
|
||||||
{
|
{
|
||||||
if (season.IndexNumber.HasValue && season.IndexNumber.Value != 0)
|
return string.Format(_localization.GetLocalizedString("ValueSpecialEpisodeName"), item.Name);
|
||||||
{
|
|
||||||
return string.Format(_localization.GetLocalizedString("ValueSpecialEpisodeName"), item.Name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.IndexNumber.HasValue)
|
if (item.IndexNumber.HasValue)
|
||||||
@@ -585,10 +546,8 @@ namespace Emby.Dlna.Didl
|
|||||||
|
|
||||||
public static bool IsIdRoot(string id)
|
public static bool IsIdRoot(string id)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(id) ||
|
if (string.IsNullOrWhiteSpace(id)
|
||||||
|
|| string.Equals(id, "0", StringComparison.OrdinalIgnoreCase)
|
||||||
string.Equals(id, "0", StringComparison.OrdinalIgnoreCase)
|
|
||||||
|
|
||||||
// Samsung sometimes uses 1 as root
|
// Samsung sometimes uses 1 as root
|
||||||
|| string.Equals(id, "1", StringComparison.OrdinalIgnoreCase))
|
|| string.Equals(id, "1", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
@@ -808,7 +767,7 @@ namespace Emby.Dlna.Didl
|
|||||||
{
|
{
|
||||||
writer.WriteString(_profile.RequiresPlainFolders ? "object.container.storageFolder" : "object.container.genre.musicGenre");
|
writer.WriteString(_profile.RequiresPlainFolders ? "object.container.storageFolder" : "object.container.genre.musicGenre");
|
||||||
}
|
}
|
||||||
else if (item is Genre || item is GameGenre)
|
else if (item is Genre)
|
||||||
{
|
{
|
||||||
writer.WriteString(_profile.RequiresPlainFolders ? "object.container.storageFolder" : "object.container.genre");
|
writer.WriteString(_profile.RequiresPlainFolders ? "object.container.storageFolder" : "object.container.genre");
|
||||||
}
|
}
|
||||||
@@ -844,7 +803,7 @@ namespace Emby.Dlna.Didl
|
|||||||
// var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase))
|
// var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase))
|
||||||
// ?? PersonType.Actor;
|
// ?? PersonType.Actor;
|
||||||
|
|
||||||
// AddValue(writer, "upnp", type.ToLower(), actor.Name, NS_UPNP);
|
// AddValue(writer, "upnp", type.ToLowerInvariant(), actor.Name, NS_UPNP);
|
||||||
|
|
||||||
// index++;
|
// index++;
|
||||||
|
|
||||||
@@ -859,10 +818,9 @@ namespace Emby.Dlna.Didl
|
|||||||
{
|
{
|
||||||
AddCommonFields(item, itemStubType, context, writer, filter);
|
AddCommonFields(item, itemStubType, context, writer, filter);
|
||||||
|
|
||||||
var hasArtists = item as IHasArtist;
|
|
||||||
var hasAlbumArtists = item as IHasAlbumArtist;
|
var hasAlbumArtists = item as IHasAlbumArtist;
|
||||||
|
|
||||||
if (hasArtists != null)
|
if (item is IHasArtist hasArtists)
|
||||||
{
|
{
|
||||||
foreach (var artist in hasArtists.Artists)
|
foreach (var artist in hasArtists.Artists)
|
||||||
{
|
{
|
||||||
@@ -932,7 +890,7 @@ namespace Emby.Dlna.Didl
|
|||||||
|
|
||||||
private void AddCover(BaseItem item, BaseItem context, StubType? stubType, XmlWriter writer)
|
private void AddCover(BaseItem item, BaseItem context, StubType? stubType, XmlWriter writer)
|
||||||
{
|
{
|
||||||
ImageDownloadInfo imageInfo = GetImageInfo(item);;
|
ImageDownloadInfo imageInfo = GetImageInfo(item);
|
||||||
|
|
||||||
if (imageInfo == null)
|
if (imageInfo == null)
|
||||||
{
|
{
|
||||||
@@ -1088,8 +1046,8 @@ namespace Emby.Dlna.Didl
|
|||||||
//{
|
//{
|
||||||
// var size = _imageProcessor.GetImageSize(imageInfo);
|
// var size = _imageProcessor.GetImageSize(imageInfo);
|
||||||
|
|
||||||
// width = Convert.ToInt32(size.Width);
|
// width = size.Width;
|
||||||
// height = Convert.ToInt32(size.Height);
|
// height = size.Height;
|
||||||
//}
|
//}
|
||||||
//catch
|
//catch
|
||||||
//{
|
//{
|
||||||
@@ -1112,7 +1070,7 @@ namespace Emby.Dlna.Didl
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class ImageDownloadInfo
|
private class ImageDownloadInfo
|
||||||
{
|
{
|
||||||
internal Guid ItemId;
|
internal Guid ItemId;
|
||||||
internal string ImageTag;
|
internal string ImageTag;
|
||||||
@@ -1128,7 +1086,7 @@ namespace Emby.Dlna.Didl
|
|||||||
internal ItemImageInfo ItemImageInfo;
|
internal ItemImageInfo ItemImageInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ImageUrlInfo
|
private class ImageUrlInfo
|
||||||
{
|
{
|
||||||
internal string Url;
|
internal string Url;
|
||||||
|
|
||||||
@@ -1147,7 +1105,7 @@ namespace Emby.Dlna.Didl
|
|||||||
|
|
||||||
if (stubType.HasValue)
|
if (stubType.HasValue)
|
||||||
{
|
{
|
||||||
id = stubType.Value.ToString().ToLower() + "_" + id;
|
id = stubType.Value.ToString().ToLowerInvariant() + "_" + id;
|
||||||
}
|
}
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
@@ -1162,8 +1120,7 @@ namespace Emby.Dlna.Didl
|
|||||||
info.ImageTag,
|
info.ImageTag,
|
||||||
format,
|
format,
|
||||||
maxWidth.ToString(CultureInfo.InvariantCulture),
|
maxWidth.ToString(CultureInfo.InvariantCulture),
|
||||||
maxHeight.ToString(CultureInfo.InvariantCulture)
|
maxHeight.ToString(CultureInfo.InvariantCulture));
|
||||||
);
|
|
||||||
|
|
||||||
var width = info.Width;
|
var width = info.Width;
|
||||||
var height = info.Height;
|
var height = info.Height;
|
||||||
@@ -1172,15 +1129,11 @@ namespace Emby.Dlna.Didl
|
|||||||
|
|
||||||
if (width.HasValue && height.HasValue)
|
if (width.HasValue && height.HasValue)
|
||||||
{
|
{
|
||||||
var newSize = DrawingUtils.Resize(new ImageSize
|
var newSize = DrawingUtils.Resize(
|
||||||
{
|
new ImageDimensions(width.Value, height.Value), 0, 0, maxWidth, maxHeight);
|
||||||
Height = height.Value,
|
|
||||||
Width = width.Value
|
|
||||||
|
|
||||||
}, 0, 0, maxWidth, maxHeight);
|
width = newSize.Width;
|
||||||
|
height = newSize.Height;
|
||||||
width = Convert.ToInt32(newSize.Width);
|
|
||||||
height = Convert.ToInt32(newSize.Height);
|
|
||||||
|
|
||||||
var normalizedFormat = format
|
var normalizedFormat = format
|
||||||
.Replace("jpeg", "jpg", StringComparison.OrdinalIgnoreCase);
|
.Replace("jpeg", "jpg", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|||||||
+30
-27
@@ -2,8 +2,10 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Emby.Dlna.Profiles;
|
using Emby.Dlna.Profiles;
|
||||||
using Emby.Dlna.Server;
|
using Emby.Dlna.Server;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
@@ -14,9 +16,10 @@ using MediaBrowser.Controller.Drawing;
|
|||||||
using MediaBrowser.Model.Dlna;
|
using MediaBrowser.Model.Dlna;
|
||||||
using MediaBrowser.Model.Drawing;
|
using MediaBrowser.Model.Drawing;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.Reflection;
|
|
||||||
using MediaBrowser.Model.Serialization;
|
using MediaBrowser.Model.Serialization;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
|
|
||||||
namespace Emby.Dlna
|
namespace Emby.Dlna
|
||||||
{
|
{
|
||||||
@@ -28,7 +31,7 @@ namespace Emby.Dlna
|
|||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IJsonSerializer _jsonSerializer;
|
private readonly IJsonSerializer _jsonSerializer;
|
||||||
private readonly IServerApplicationHost _appHost;
|
private readonly IServerApplicationHost _appHost;
|
||||||
private readonly IAssemblyInfo _assemblyInfo;
|
private static readonly Assembly _assembly = typeof(DlnaManager).Assembly;
|
||||||
|
|
||||||
private readonly Dictionary<string, Tuple<InternalProfileInfo, DeviceProfile>> _profiles = new Dictionary<string, Tuple<InternalProfileInfo, DeviceProfile>>(StringComparer.Ordinal);
|
private readonly Dictionary<string, Tuple<InternalProfileInfo, DeviceProfile>> _profiles = new Dictionary<string, Tuple<InternalProfileInfo, DeviceProfile>>(StringComparer.Ordinal);
|
||||||
|
|
||||||
@@ -37,7 +40,8 @@ namespace Emby.Dlna
|
|||||||
IFileSystem fileSystem,
|
IFileSystem fileSystem,
|
||||||
IApplicationPaths appPaths,
|
IApplicationPaths appPaths,
|
||||||
ILoggerFactory loggerFactory,
|
ILoggerFactory loggerFactory,
|
||||||
IJsonSerializer jsonSerializer, IServerApplicationHost appHost, IAssemblyInfo assemblyInfo)
|
IJsonSerializer jsonSerializer,
|
||||||
|
IServerApplicationHost appHost)
|
||||||
{
|
{
|
||||||
_xmlSerializer = xmlSerializer;
|
_xmlSerializer = xmlSerializer;
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
@@ -45,14 +49,13 @@ namespace Emby.Dlna
|
|||||||
_logger = loggerFactory.CreateLogger("Dlna");
|
_logger = loggerFactory.CreateLogger("Dlna");
|
||||||
_jsonSerializer = jsonSerializer;
|
_jsonSerializer = jsonSerializer;
|
||||||
_appHost = appHost;
|
_appHost = appHost;
|
||||||
_assemblyInfo = assemblyInfo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InitProfiles()
|
public async Task InitProfilesAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ExtractSystemProfiles();
|
await ExtractSystemProfilesAsync();
|
||||||
LoadProfiles();
|
LoadProfiles();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -202,16 +205,13 @@ namespace Emby.Dlna
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeviceProfile GetProfile(IDictionary<string, string> headers)
|
public DeviceProfile GetProfile(IHeaderDictionary headers)
|
||||||
{
|
{
|
||||||
if (headers == null)
|
if (headers == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(headers));
|
throw new ArgumentNullException(nameof(headers));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to case insensitive
|
|
||||||
headers = new Dictionary<string, string>(headers, StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
var profile = GetProfiles().FirstOrDefault(i => i.Identification != null && IsMatch(headers, i.Identification));
|
var profile = GetProfiles().FirstOrDefault(i => i.Identification != null && IsMatch(headers, i.Identification));
|
||||||
|
|
||||||
if (profile != null)
|
if (profile != null)
|
||||||
@@ -227,12 +227,12 @@ namespace Emby.Dlna
|
|||||||
return profile;
|
return profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsMatch(IDictionary<string, string> headers, DeviceIdentification profileInfo)
|
private bool IsMatch(IHeaderDictionary headers, DeviceIdentification profileInfo)
|
||||||
{
|
{
|
||||||
return profileInfo.Headers.Any(i => IsMatch(headers, i));
|
return profileInfo.Headers.Any(i => IsMatch(headers, i));
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsMatch(IDictionary<string, string> headers, HttpHeaderInfo header)
|
private bool IsMatch(IHeaderDictionary headers, HttpHeaderInfo header)
|
||||||
{
|
{
|
||||||
// Handle invalid user setup
|
// Handle invalid user setup
|
||||||
if (string.IsNullOrEmpty(header.Name))
|
if (string.IsNullOrEmpty(header.Name))
|
||||||
@@ -240,14 +240,14 @@ namespace Emby.Dlna
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (headers.TryGetValue(header.Name, out string value))
|
if (headers.TryGetValue(header.Name, out StringValues value))
|
||||||
{
|
{
|
||||||
switch (header.Match)
|
switch (header.Match)
|
||||||
{
|
{
|
||||||
case HeaderMatchType.Equals:
|
case HeaderMatchType.Equals:
|
||||||
return string.Equals(value, header.Value, StringComparison.OrdinalIgnoreCase);
|
return string.Equals(value, header.Value, StringComparison.OrdinalIgnoreCase);
|
||||||
case HeaderMatchType.Substring:
|
case HeaderMatchType.Substring:
|
||||||
var isMatch = value.IndexOf(header.Value, StringComparison.OrdinalIgnoreCase) != -1;
|
var isMatch = value.ToString().IndexOf(header.Value, StringComparison.OrdinalIgnoreCase) != -1;
|
||||||
//_logger.LogDebug("IsMatch-Substring value: {0} testValue: {1} isMatch: {2}", value, header.Value, isMatch);
|
//_logger.LogDebug("IsMatch-Substring value: {0} testValue: {1} isMatch: {2}", value, header.Value, isMatch);
|
||||||
return isMatch;
|
return isMatch;
|
||||||
case HeaderMatchType.Regex:
|
case HeaderMatchType.Regex:
|
||||||
@@ -300,7 +300,7 @@ namespace Emby.Dlna
|
|||||||
|
|
||||||
profile = ReserializeProfile(tempProfile);
|
profile = ReserializeProfile(tempProfile);
|
||||||
|
|
||||||
profile.Id = path.ToLower().GetMD5().ToString("N");
|
profile.Id = path.ToLowerInvariant().GetMD5().ToString("N");
|
||||||
|
|
||||||
_profiles[path] = new Tuple<InternalProfileInfo, DeviceProfile>(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile);
|
_profiles[path] = new Tuple<InternalProfileInfo, DeviceProfile>(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile);
|
||||||
|
|
||||||
@@ -352,45 +352,48 @@ namespace Emby.Dlna
|
|||||||
|
|
||||||
Info = new DeviceProfileInfo
|
Info = new DeviceProfileInfo
|
||||||
{
|
{
|
||||||
Id = file.FullName.ToLower().GetMD5().ToString("N"),
|
Id = file.FullName.ToLowerInvariant().GetMD5().ToString("N"),
|
||||||
Name = _fileSystem.GetFileNameWithoutExtension(file),
|
Name = _fileSystem.GetFileNameWithoutExtension(file),
|
||||||
Type = type
|
Type = type
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExtractSystemProfiles()
|
private async Task ExtractSystemProfilesAsync()
|
||||||
{
|
{
|
||||||
var namespaceName = GetType().Namespace + ".Profiles.Xml.";
|
var namespaceName = GetType().Namespace + ".Profiles.Xml.";
|
||||||
|
|
||||||
var systemProfilesPath = SystemProfilesPath;
|
var systemProfilesPath = SystemProfilesPath;
|
||||||
|
|
||||||
foreach (var name in _assemblyInfo.GetManifestResourceNames(GetType())
|
foreach (var name in _assembly.GetManifestResourceNames())
|
||||||
.Where(i => i.StartsWith(namespaceName))
|
|
||||||
.ToList())
|
|
||||||
{
|
{
|
||||||
|
if (!name.StartsWith(namespaceName))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var filename = Path.GetFileName(name).Substring(namespaceName.Length);
|
var filename = Path.GetFileName(name).Substring(namespaceName.Length);
|
||||||
|
|
||||||
var path = Path.Combine(systemProfilesPath, filename);
|
var path = Path.Combine(systemProfilesPath, filename);
|
||||||
|
|
||||||
using (var stream = _assemblyInfo.GetManifestResourceStream(GetType(), name))
|
using (var stream = _assembly.GetManifestResourceStream(name))
|
||||||
{
|
{
|
||||||
var fileInfo = _fileSystem.GetFileInfo(path);
|
var fileInfo = _fileSystem.GetFileInfo(path);
|
||||||
|
|
||||||
if (!fileInfo.Exists || fileInfo.Length != stream.Length)
|
if (!fileInfo.Exists || fileInfo.Length != stream.Length)
|
||||||
{
|
{
|
||||||
_fileSystem.CreateDirectory(systemProfilesPath);
|
Directory.CreateDirectory(systemProfilesPath);
|
||||||
|
|
||||||
using (var fileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
|
using (var fileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
|
||||||
{
|
{
|
||||||
stream.CopyTo(fileStream);
|
await stream.CopyToAsync(fileStream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not necessary, but just to make it easy to find
|
// Not necessary, but just to make it easy to find
|
||||||
_fileSystem.CreateDirectory(UserProfilesPath);
|
Directory.CreateDirectory(UserProfilesPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeleteProfile(string id)
|
public void DeleteProfile(string id)
|
||||||
@@ -490,7 +493,7 @@ namespace Emby.Dlna
|
|||||||
internal string Path { get; set; }
|
internal string Path { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetServerDescriptionXml(IDictionary<string, string> headers, string serverUuId, string serverAddress)
|
public string GetServerDescriptionXml(IHeaderDictionary headers, string serverUuId, string serverAddress)
|
||||||
{
|
{
|
||||||
var profile = GetProfile(headers) ??
|
var profile = GetProfile(headers) ??
|
||||||
GetDefaultProfile();
|
GetDefaultProfile();
|
||||||
@@ -506,12 +509,12 @@ namespace Emby.Dlna
|
|||||||
? ImageFormat.Png
|
? ImageFormat.Png
|
||||||
: ImageFormat.Jpg;
|
: ImageFormat.Jpg;
|
||||||
|
|
||||||
var resource = GetType().Namespace + ".Images." + filename.ToLower();
|
var resource = GetType().Namespace + ".Images." + filename.ToLowerInvariant();
|
||||||
|
|
||||||
return new ImageStream
|
return new ImageStream
|
||||||
{
|
{
|
||||||
Format = format,
|
Format = format,
|
||||||
Stream = _assemblyInfo.GetManifestResourceStream(GetType(), resource)
|
Stream = _assembly.GetManifestResourceStream(resource)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,4 +58,9 @@
|
|||||||
<EmbeddedResource Include="Profiles\Xml\Xbox One.xml" />
|
<EmbeddedResource Include="Profiles\Xml\Xbox One.xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="2.2.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Emby.Dlna
|
namespace Emby.Dlna
|
||||||
{
|
{
|
||||||
public interface IUpnpService
|
public interface IUpnpService
|
||||||
@@ -7,9 +5,8 @@ namespace Emby.Dlna
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the content directory XML.
|
/// Gets the content directory XML.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="headers">The headers.</param>
|
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
string GetServiceXml(IDictionary<string, string> headers);
|
string GetServiceXml();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Processes the control request.
|
/// Processes the control request.
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 6.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 13 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.2 KiB |
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Emby.Dlna.PlayTo;
|
using Emby.Dlna.PlayTo;
|
||||||
@@ -20,11 +19,10 @@ using MediaBrowser.Model.Dlna;
|
|||||||
using MediaBrowser.Model.Globalization;
|
using MediaBrowser.Model.Globalization;
|
||||||
using MediaBrowser.Model.Net;
|
using MediaBrowser.Model.Net;
|
||||||
using MediaBrowser.Model.System;
|
using MediaBrowser.Model.System;
|
||||||
using MediaBrowser.Model.Threading;
|
|
||||||
using MediaBrowser.Model.Xml;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Rssdp;
|
using Rssdp;
|
||||||
using Rssdp.Infrastructure;
|
using Rssdp.Infrastructure;
|
||||||
|
using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
|
||||||
|
|
||||||
namespace Emby.Dlna.Main
|
namespace Emby.Dlna.Main
|
||||||
{
|
{
|
||||||
@@ -50,9 +48,7 @@ namespace Emby.Dlna.Main
|
|||||||
|
|
||||||
private SsdpDevicePublisher _Publisher;
|
private SsdpDevicePublisher _Publisher;
|
||||||
|
|
||||||
private readonly ITimerFactory _timerFactory;
|
|
||||||
private readonly ISocketFactory _socketFactory;
|
private readonly ISocketFactory _socketFactory;
|
||||||
private readonly IEnvironmentInfo _environmentInfo;
|
|
||||||
private readonly INetworkManager _networkManager;
|
private readonly INetworkManager _networkManager;
|
||||||
|
|
||||||
private ISsdpCommunicationsServer _communicationsServer;
|
private ISsdpCommunicationsServer _communicationsServer;
|
||||||
@@ -78,11 +74,8 @@ namespace Emby.Dlna.Main
|
|||||||
IDeviceDiscovery deviceDiscovery,
|
IDeviceDiscovery deviceDiscovery,
|
||||||
IMediaEncoder mediaEncoder,
|
IMediaEncoder mediaEncoder,
|
||||||
ISocketFactory socketFactory,
|
ISocketFactory socketFactory,
|
||||||
ITimerFactory timerFactory,
|
|
||||||
IEnvironmentInfo environmentInfo,
|
|
||||||
INetworkManager networkManager,
|
INetworkManager networkManager,
|
||||||
IUserViewManager userViewManager,
|
IUserViewManager userViewManager,
|
||||||
IXmlReaderSettingsFactory xmlReaderSettingsFactory,
|
|
||||||
ITVSeriesManager tvSeriesManager)
|
ITVSeriesManager tvSeriesManager)
|
||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
@@ -99,12 +92,11 @@ namespace Emby.Dlna.Main
|
|||||||
_deviceDiscovery = deviceDiscovery;
|
_deviceDiscovery = deviceDiscovery;
|
||||||
_mediaEncoder = mediaEncoder;
|
_mediaEncoder = mediaEncoder;
|
||||||
_socketFactory = socketFactory;
|
_socketFactory = socketFactory;
|
||||||
_timerFactory = timerFactory;
|
|
||||||
_environmentInfo = environmentInfo;
|
|
||||||
_networkManager = networkManager;
|
_networkManager = networkManager;
|
||||||
_logger = loggerFactory.CreateLogger("Dlna");
|
_logger = loggerFactory.CreateLogger("Dlna");
|
||||||
|
|
||||||
ContentDirectory = new ContentDirectory.ContentDirectory(dlnaManager,
|
ContentDirectory = new ContentDirectory.ContentDirectory(
|
||||||
|
dlnaManager,
|
||||||
userDataManager,
|
userDataManager,
|
||||||
imageProcessor,
|
imageProcessor,
|
||||||
libraryManager,
|
libraryManager,
|
||||||
@@ -116,18 +108,17 @@ namespace Emby.Dlna.Main
|
|||||||
mediaSourceManager,
|
mediaSourceManager,
|
||||||
userViewManager,
|
userViewManager,
|
||||||
mediaEncoder,
|
mediaEncoder,
|
||||||
xmlReaderSettingsFactory,
|
|
||||||
tvSeriesManager);
|
tvSeriesManager);
|
||||||
|
|
||||||
ConnectionManager = new ConnectionManager.ConnectionManager(dlnaManager, config, _logger, httpClient, xmlReaderSettingsFactory);
|
ConnectionManager = new ConnectionManager.ConnectionManager(dlnaManager, config, _logger, httpClient);
|
||||||
|
|
||||||
MediaReceiverRegistrar = new MediaReceiverRegistrar.MediaReceiverRegistrar(_logger, httpClient, config, xmlReaderSettingsFactory);
|
MediaReceiverRegistrar = new MediaReceiverRegistrar.MediaReceiverRegistrar(_logger, httpClient, config);
|
||||||
Current = this;
|
Current = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Run()
|
public async Task RunAsync()
|
||||||
{
|
{
|
||||||
((DlnaManager)_dlnaManager).InitProfiles();
|
await ((DlnaManager)_dlnaManager).InitProfilesAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
ReloadComponents();
|
ReloadComponents();
|
||||||
|
|
||||||
@@ -173,9 +164,10 @@ namespace Emby.Dlna.Main
|
|||||||
{
|
{
|
||||||
if (_communicationsServer == null)
|
if (_communicationsServer == null)
|
||||||
{
|
{
|
||||||
var enableMultiSocketBinding = _environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows;
|
var enableMultiSocketBinding = OperatingSystem.Id == OperatingSystemId.Windows ||
|
||||||
|
OperatingSystem.Id == OperatingSystemId.Linux;
|
||||||
|
|
||||||
_communicationsServer = new SsdpCommunicationsServer(_socketFactory, _networkManager, _logger, enableMultiSocketBinding)
|
_communicationsServer = new SsdpCommunicationsServer(_config, _socketFactory, _networkManager, _logger, enableMultiSocketBinding)
|
||||||
{
|
{
|
||||||
IsShared = true
|
IsShared = true
|
||||||
};
|
};
|
||||||
@@ -233,7 +225,7 @@ namespace Emby.Dlna.Main
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_Publisher = new SsdpDevicePublisher(_communicationsServer, _timerFactory, _environmentInfo.OperatingSystemName, _environmentInfo.OperatingSystemVersion);
|
_Publisher = new SsdpDevicePublisher(_communicationsServer, _networkManager, OperatingSystem.Name, Environment.OSVersion.VersionString, _config.GetDlnaConfiguration().SendOnlyMatchedHost);
|
||||||
_Publisher.LogFunction = LogMessage;
|
_Publisher.LogFunction = LogMessage;
|
||||||
_Publisher.SupportPnpRootDevice = false;
|
_Publisher.SupportPnpRootDevice = false;
|
||||||
|
|
||||||
@@ -249,21 +241,21 @@ namespace Emby.Dlna.Main
|
|||||||
|
|
||||||
private async Task RegisterServerEndpoints()
|
private async Task RegisterServerEndpoints()
|
||||||
{
|
{
|
||||||
var addresses = (await _appHost.GetLocalIpAddresses(CancellationToken.None).ConfigureAwait(false)).ToList();
|
var addresses = await _appHost.GetLocalIpAddresses(CancellationToken.None).ConfigureAwait(false);
|
||||||
|
|
||||||
var udn = CreateUuid(_appHost.SystemId);
|
var udn = CreateUuid(_appHost.SystemId);
|
||||||
|
|
||||||
foreach (var address in addresses)
|
foreach (var address in addresses)
|
||||||
{
|
{
|
||||||
// TODO: Remove this condition on platforms that support it
|
if (address.AddressFamily == IpAddressFamily.InterNetworkV6)
|
||||||
//if (address.AddressFamily == IpAddressFamily.InterNetworkV6)
|
{
|
||||||
//{
|
// Not support IPv6 right now
|
||||||
// continue;
|
continue;
|
||||||
//}
|
}
|
||||||
|
|
||||||
var fullService = "urn:schemas-upnp-org:device:MediaServer:1";
|
var fullService = "urn:schemas-upnp-org:device:MediaServer:1";
|
||||||
|
|
||||||
_logger.LogInformation("Registering publisher for {0} on {1}", fullService, address.ToString());
|
_logger.LogInformation("Registering publisher for {0} on {1}", fullService, address);
|
||||||
|
|
||||||
var descriptorUri = "/dlna/" + udn + "/description.xml";
|
var descriptorUri = "/dlna/" + udn + "/description.xml";
|
||||||
var uri = new Uri(_appHost.GetLocalApiUrl(address) + descriptorUri);
|
var uri = new Uri(_appHost.GetLocalApiUrl(address) + descriptorUri);
|
||||||
@@ -272,6 +264,8 @@ namespace Emby.Dlna.Main
|
|||||||
{
|
{
|
||||||
CacheLifetime = TimeSpan.FromSeconds(1800), //How long SSDP clients can cache this info.
|
CacheLifetime = TimeSpan.FromSeconds(1800), //How long SSDP clients can cache this info.
|
||||||
Location = uri, // Must point to the URL that serves your devices UPnP description document.
|
Location = uri, // Must point to the URL that serves your devices UPnP description document.
|
||||||
|
Address = address,
|
||||||
|
SubnetMask = _networkManager.GetLocalIpSubnetMask(address),
|
||||||
FriendlyName = "Jellyfin",
|
FriendlyName = "Jellyfin",
|
||||||
Manufacturer = "Jellyfin",
|
Manufacturer = "Jellyfin",
|
||||||
ModelName = "Jellyfin Server",
|
ModelName = "Jellyfin Server",
|
||||||
@@ -353,8 +347,7 @@ namespace Emby.Dlna.Main
|
|||||||
_userDataManager,
|
_userDataManager,
|
||||||
_localization,
|
_localization,
|
||||||
_mediaSourceManager,
|
_mediaSourceManager,
|
||||||
_mediaEncoder,
|
_mediaEncoder);
|
||||||
_timerFactory);
|
|
||||||
|
|
||||||
_manager.Start();
|
_manager.Start();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
|||||||
using Emby.Dlna.Service;
|
using Emby.Dlna.Service;
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Model.Xml;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Dlna.MediaReceiverRegistrar
|
namespace Emby.Dlna.MediaReceiverRegistrar
|
||||||
@@ -36,7 +35,8 @@ namespace Emby.Dlna.MediaReceiverRegistrar
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public ControlHandler(IServerConfigurationManager config, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(config, logger, xmlReaderSettingsFactory)
|
public ControlHandler(IServerConfigurationManager config, ILogger logger)
|
||||||
|
: base(config, logger)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using Emby.Dlna.Service;
|
using Emby.Dlna.Service;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Model.Xml;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Dlna.MediaReceiverRegistrar
|
namespace Emby.Dlna.MediaReceiverRegistrar
|
||||||
@@ -10,16 +8,14 @@ namespace Emby.Dlna.MediaReceiverRegistrar
|
|||||||
public class MediaReceiverRegistrar : BaseService, IMediaReceiverRegistrar
|
public class MediaReceiverRegistrar : BaseService, IMediaReceiverRegistrar
|
||||||
{
|
{
|
||||||
private readonly IServerConfigurationManager _config;
|
private readonly IServerConfigurationManager _config;
|
||||||
protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory;
|
|
||||||
|
|
||||||
public MediaReceiverRegistrar(ILogger logger, IHttpClient httpClient, IServerConfigurationManager config, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
|
public MediaReceiverRegistrar(ILogger logger, IHttpClient httpClient, IServerConfigurationManager config)
|
||||||
: base(logger, httpClient)
|
: base(logger, httpClient)
|
||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
XmlReaderSettingsFactory = xmlReaderSettingsFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetServiceXml(IDictionary<string, string> headers)
|
public string GetServiceXml()
|
||||||
{
|
{
|
||||||
return new MediaReceiverRegistrarXmlBuilder().GetXml();
|
return new MediaReceiverRegistrarXmlBuilder().GetXml();
|
||||||
}
|
}
|
||||||
@@ -28,7 +24,7 @@ namespace Emby.Dlna.MediaReceiverRegistrar
|
|||||||
{
|
{
|
||||||
return new ControlHandler(
|
return new ControlHandler(
|
||||||
_config,
|
_config,
|
||||||
Logger, XmlReaderSettingsFactory)
|
Logger)
|
||||||
.ProcessControlRequest(request);
|
.ProcessControlRequest(request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Emby.Dlna.PlayTo
|
|
||||||
{
|
|
||||||
public class CurrentIdEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
public string Id { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+191
-132
@@ -4,13 +4,13 @@ using System.Globalization;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Xml;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using Emby.Dlna.Common;
|
using Emby.Dlna.Common;
|
||||||
using Emby.Dlna.Server;
|
using Emby.Dlna.Server;
|
||||||
using Emby.Dlna.Ssdp;
|
using Emby.Dlna.Ssdp;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Model.Threading;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Dlna.PlayTo
|
namespace Emby.Dlna.PlayTo
|
||||||
@@ -19,7 +19,7 @@ namespace Emby.Dlna.PlayTo
|
|||||||
{
|
{
|
||||||
#region Fields & Properties
|
#region Fields & Properties
|
||||||
|
|
||||||
private ITimer _timer;
|
private Timer _timer;
|
||||||
|
|
||||||
public DeviceInfo Properties { get; set; }
|
public DeviceInfo Properties { get; set; }
|
||||||
|
|
||||||
@@ -40,12 +40,7 @@ namespace Emby.Dlna.PlayTo
|
|||||||
|
|
||||||
public TimeSpan? Duration { get; set; }
|
public TimeSpan? Duration { get; set; }
|
||||||
|
|
||||||
private TimeSpan _position = TimeSpan.FromSeconds(0);
|
public TimeSpan Position { get; set; } = TimeSpan.FromSeconds(0);
|
||||||
public TimeSpan Position
|
|
||||||
{
|
|
||||||
get => _position;
|
|
||||||
set => _position = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TRANSPORTSTATE TransportState { get; private set; }
|
public TRANSPORTSTATE TransportState { get; private set; }
|
||||||
|
|
||||||
@@ -61,24 +56,20 @@ namespace Emby.Dlna.PlayTo
|
|||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IServerConfigurationManager _config;
|
private readonly IServerConfigurationManager _config;
|
||||||
|
|
||||||
public DateTime DateLastActivity { get; private set; }
|
|
||||||
public Action OnDeviceUnavailable { get; set; }
|
public Action OnDeviceUnavailable { get; set; }
|
||||||
|
|
||||||
private readonly ITimerFactory _timerFactory;
|
public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger, IServerConfigurationManager config)
|
||||||
|
|
||||||
public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger, IServerConfigurationManager config, ITimerFactory timerFactory)
|
|
||||||
{
|
{
|
||||||
Properties = deviceProperties;
|
Properties = deviceProperties;
|
||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_config = config;
|
_config = config;
|
||||||
_timerFactory = timerFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Dlna Device.Start");
|
_logger.LogDebug("Dlna Device.Start");
|
||||||
_timer = _timerFactory.Create(TimerCallback, null, 1000, Timeout.Infinite);
|
_timer = new Timer(TimerCallback, null, 1000, Timeout.Infinite);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DateTime _lastVolumeRefresh;
|
private DateTime _lastVolumeRefresh;
|
||||||
@@ -119,7 +110,9 @@ namespace Emby.Dlna.PlayTo
|
|||||||
lock (_timerLock)
|
lock (_timerLock)
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_volumeRefreshActive = true;
|
_volumeRefreshActive = true;
|
||||||
|
|
||||||
@@ -136,7 +129,9 @@ namespace Emby.Dlna.PlayTo
|
|||||||
lock (_timerLock)
|
lock (_timerLock)
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_volumeRefreshActive = false;
|
_volumeRefreshActive = false;
|
||||||
|
|
||||||
@@ -144,11 +139,6 @@ namespace Emby.Dlna.PlayTo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnPlaybackStartedExternally()
|
|
||||||
{
|
|
||||||
RestartTimer(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Commanding
|
#region Commanding
|
||||||
|
|
||||||
public Task VolumeDown(CancellationToken cancellationToken)
|
public Task VolumeDown(CancellationToken cancellationToken)
|
||||||
@@ -333,7 +323,9 @@ namespace Emby.Dlna.PlayTo
|
|||||||
private string CreateDidlMeta(string value)
|
private string CreateDidlMeta(string value)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(value))
|
if (string.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
return DescriptionXmlBuilder.Escape(value);
|
return DescriptionXmlBuilder.Escape(value);
|
||||||
}
|
}
|
||||||
@@ -342,10 +334,11 @@ namespace Emby.Dlna.PlayTo
|
|||||||
{
|
{
|
||||||
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Play");
|
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Play");
|
||||||
if (command == null)
|
if (command == null)
|
||||||
|
{
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
var service = GetAvTransportService();
|
var service = GetAvTransportService();
|
||||||
|
|
||||||
if (service == null)
|
if (service == null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Unable to find service");
|
throw new InvalidOperationException("Unable to find service");
|
||||||
@@ -369,7 +362,9 @@ namespace Emby.Dlna.PlayTo
|
|||||||
|
|
||||||
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Stop");
|
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Stop");
|
||||||
if (command == null)
|
if (command == null)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var service = GetAvTransportService();
|
var service = GetAvTransportService();
|
||||||
|
|
||||||
@@ -385,7 +380,9 @@ namespace Emby.Dlna.PlayTo
|
|||||||
|
|
||||||
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Pause");
|
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Pause");
|
||||||
if (command == null)
|
if (command == null)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var service = GetAvTransportService();
|
var service = GetAvTransportService();
|
||||||
|
|
||||||
@@ -405,7 +402,9 @@ namespace Emby.Dlna.PlayTo
|
|||||||
private async void TimerCallback(object sender)
|
private async void TimerCallback(object sender)
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -425,8 +424,6 @@ namespace Emby.Dlna.PlayTo
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DateLastActivity = DateTime.UtcNow;
|
|
||||||
|
|
||||||
if (transportState.HasValue)
|
if (transportState.HasValue)
|
||||||
{
|
{
|
||||||
// If we're not playing anything no need to get additional data
|
// If we're not playing anything no need to get additional data
|
||||||
@@ -505,7 +502,9 @@ namespace Emby.Dlna.PlayTo
|
|||||||
|
|
||||||
var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetVolume");
|
var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetVolume");
|
||||||
if (command == null)
|
if (command == null)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var service = GetServiceRenderingControl();
|
var service = GetServiceRenderingControl();
|
||||||
|
|
||||||
@@ -518,13 +517,17 @@ namespace Emby.Dlna.PlayTo
|
|||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
if (result == null || result.Document == null)
|
if (result == null || result.Document == null)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var volume = result.Document.Descendants(uPnpNamespaces.RenderingControl + "GetVolumeResponse").Select(i => i.Element("CurrentVolume")).FirstOrDefault(i => i != null);
|
var volume = result.Document.Descendants(uPnpNamespaces.RenderingControl + "GetVolumeResponse").Select(i => i.Element("CurrentVolume")).FirstOrDefault(i => i != null);
|
||||||
var volumeValue = volume == null ? null : volume.Value;
|
var volumeValue = volume?.Value;
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(volumeValue))
|
if (string.IsNullOrWhiteSpace(volumeValue))
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Volume = int.Parse(volumeValue, UsCulture);
|
Volume = int.Parse(volumeValue, UsCulture);
|
||||||
|
|
||||||
@@ -545,7 +548,9 @@ namespace Emby.Dlna.PlayTo
|
|||||||
|
|
||||||
var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMute");
|
var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMute");
|
||||||
if (command == null)
|
if (command == null)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var service = GetServiceRenderingControl();
|
var service = GetServiceRenderingControl();
|
||||||
|
|
||||||
@@ -560,39 +565,44 @@ namespace Emby.Dlna.PlayTo
|
|||||||
if (result == null || result.Document == null)
|
if (result == null || result.Document == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var valueNode = result.Document.Descendants(uPnpNamespaces.RenderingControl + "GetMuteResponse").Select(i => i.Element("CurrentMute")).FirstOrDefault(i => i != null);
|
var valueNode = result.Document.Descendants(uPnpNamespaces.RenderingControl + "GetMuteResponse")
|
||||||
var value = valueNode == null ? null : valueNode.Value;
|
.Select(i => i.Element("CurrentMute"))
|
||||||
|
.FirstOrDefault(i => i != null);
|
||||||
|
|
||||||
IsMuted = string.Equals(value, "1", StringComparison.OrdinalIgnoreCase);
|
IsMuted = string.Equals(valueNode?.Value, "1", StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<TRANSPORTSTATE?> GetTransportInfo(TransportCommands avCommands, CancellationToken cancellationToken)
|
private async Task<TRANSPORTSTATE?> GetTransportInfo(TransportCommands avCommands, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetTransportInfo");
|
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetTransportInfo");
|
||||||
if (command == null)
|
if (command == null)
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var service = GetAvTransportService();
|
var service = GetAvTransportService();
|
||||||
if (service == null)
|
if (service == null)
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType), false)
|
var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType), false)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
if (result == null || result.Document == null)
|
if (result == null || result.Document == null)
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var transportState =
|
var transportState =
|
||||||
result.Document.Descendants(uPnpNamespaces.AvTransport + "GetTransportInfoResponse").Select(i => i.Element("CurrentTransportState")).FirstOrDefault(i => i != null);
|
result.Document.Descendants(uPnpNamespaces.AvTransport + "GetTransportInfoResponse").Select(i => i.Element("CurrentTransportState")).FirstOrDefault(i => i != null);
|
||||||
|
|
||||||
var transportStateValue = transportState == null ? null : transportState.Value;
|
var transportStateValue = transportState == null ? null : transportState.Value;
|
||||||
|
|
||||||
if (transportStateValue != null)
|
if (transportStateValue != null
|
||||||
|
&& Enum.TryParse(transportStateValue, true, out TRANSPORTSTATE state))
|
||||||
{
|
{
|
||||||
if (Enum.TryParse(transportStateValue, true, out TRANSPORTSTATE state))
|
return state;
|
||||||
{
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -602,10 +612,11 @@ namespace Emby.Dlna.PlayTo
|
|||||||
{
|
{
|
||||||
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMediaInfo");
|
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMediaInfo");
|
||||||
if (command == null)
|
if (command == null)
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var service = GetAvTransportService();
|
var service = GetAvTransportService();
|
||||||
|
|
||||||
if (service == null)
|
if (service == null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Unable to find service");
|
throw new InvalidOperationException("Unable to find service");
|
||||||
@@ -617,7 +628,9 @@ namespace Emby.Dlna.PlayTo
|
|||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
if (result == null || result.Document == null)
|
if (result == null || result.Document == null)
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var track = result.Document.Descendants("CurrentURIMetaData").FirstOrDefault();
|
var track = result.Document.Descendants("CurrentURIMetaData").FirstOrDefault();
|
||||||
|
|
||||||
@@ -657,11 +670,13 @@ namespace Emby.Dlna.PlayTo
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Tuple<bool, uBaseObject>> GetPositionInfo(TransportCommands avCommands, CancellationToken cancellationToken)
|
private async Task<(bool, uBaseObject)> GetPositionInfo(TransportCommands avCommands, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetPositionInfo");
|
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetPositionInfo");
|
||||||
if (command == null)
|
if (command == null)
|
||||||
return new Tuple<bool, uBaseObject>(false, null);
|
{
|
||||||
|
return (false, null);
|
||||||
|
}
|
||||||
|
|
||||||
var service = GetAvTransportService();
|
var service = GetAvTransportService();
|
||||||
|
|
||||||
@@ -676,7 +691,9 @@ namespace Emby.Dlna.PlayTo
|
|||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
if (result == null || result.Document == null)
|
if (result == null || result.Document == null)
|
||||||
return new Tuple<bool, uBaseObject>(false, null);
|
{
|
||||||
|
return (false, null);
|
||||||
|
}
|
||||||
|
|
||||||
var trackUriElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackURI")).FirstOrDefault(i => i != null);
|
var trackUriElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackURI")).FirstOrDefault(i => i != null);
|
||||||
var trackUri = trackUriElem == null ? null : trackUriElem.Value;
|
var trackUri = trackUriElem == null ? null : trackUriElem.Value;
|
||||||
@@ -684,8 +701,8 @@ namespace Emby.Dlna.PlayTo
|
|||||||
var durationElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackDuration")).FirstOrDefault(i => i != null);
|
var durationElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackDuration")).FirstOrDefault(i => i != null);
|
||||||
var duration = durationElem == null ? null : durationElem.Value;
|
var duration = durationElem == null ? null : durationElem.Value;
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(duration) &&
|
if (!string.IsNullOrWhiteSpace(duration)
|
||||||
!string.Equals(duration, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase))
|
&& !string.Equals(duration, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
Duration = TimeSpan.Parse(duration, UsCulture);
|
Duration = TimeSpan.Parse(duration, UsCulture);
|
||||||
}
|
}
|
||||||
@@ -707,43 +724,75 @@ namespace Emby.Dlna.PlayTo
|
|||||||
if (track == null)
|
if (track == null)
|
||||||
{
|
{
|
||||||
//If track is null, some vendors do this, use GetMediaInfo instead
|
//If track is null, some vendors do this, use GetMediaInfo instead
|
||||||
return new Tuple<bool, uBaseObject>(true, null);
|
return (true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
var trackString = (string)track;
|
var trackString = (string)track;
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(trackString) || string.Equals(trackString, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase))
|
if (string.IsNullOrWhiteSpace(trackString) || string.Equals(trackString, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return new Tuple<bool, uBaseObject>(true, null);
|
return (true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
XElement uPnpResponse;
|
XElement uPnpResponse = null;
|
||||||
|
|
||||||
// Handle different variations sent back by devices
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
uPnpResponse = XElement.Parse(trackString);
|
uPnpResponse = ParseResponse(trackString);
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// first try to add a root node with a dlna namesapce
|
_logger.LogError(ex, "Uncaught exception while parsing xml");
|
||||||
try
|
}
|
||||||
{
|
|
||||||
uPnpResponse = XElement.Parse("<data xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">" + trackString + "</data>");
|
if (uPnpResponse == null)
|
||||||
uPnpResponse = uPnpResponse.Descendants().First();
|
{
|
||||||
}
|
_logger.LogError("Failed to parse xml: \n {Xml}", trackString);
|
||||||
catch (Exception ex)
|
return (true, null);
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Unable to parse xml {0}", trackString);
|
|
||||||
return new Tuple<bool, uBaseObject>(true, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var e = uPnpResponse.Element(uPnpNamespaces.items);
|
var e = uPnpResponse.Element(uPnpNamespaces.items);
|
||||||
|
|
||||||
var uTrack = CreateUBaseObject(e, trackUri);
|
var uTrack = CreateUBaseObject(e, trackUri);
|
||||||
|
|
||||||
return new Tuple<bool, uBaseObject>(true, uTrack);
|
return (true, uTrack);
|
||||||
|
}
|
||||||
|
|
||||||
|
private XElement ParseResponse(string xml)
|
||||||
|
{
|
||||||
|
// Handle different variations sent back by devices
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return XElement.Parse(xml);
|
||||||
|
}
|
||||||
|
catch (XmlException)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// first try to add a root node with a dlna namesapce
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return XElement.Parse("<data xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">" + xml + "</data>")
|
||||||
|
.Descendants()
|
||||||
|
.First();
|
||||||
|
}
|
||||||
|
catch (XmlException)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// some devices send back invalid xml
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return XElement.Parse(xml.Replace("&", "&"));
|
||||||
|
}
|
||||||
|
catch (XmlException)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static uBaseObject CreateUBaseObject(XElement container, string trackUri)
|
private static uBaseObject CreateUBaseObject(XElement container, string trackUri)
|
||||||
@@ -801,11 +850,9 @@ namespace Emby.Dlna.PlayTo
|
|||||||
|
|
||||||
private async Task<TransportCommands> GetAVProtocolAsync(CancellationToken cancellationToken)
|
private async Task<TransportCommands> GetAVProtocolAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var avCommands = AvCommands;
|
if (AvCommands != null)
|
||||||
|
|
||||||
if (avCommands != null)
|
|
||||||
{
|
{
|
||||||
return avCommands;
|
return AvCommands;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
@@ -825,18 +872,15 @@ namespace Emby.Dlna.PlayTo
|
|||||||
|
|
||||||
var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
|
var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
avCommands = TransportCommands.Create(document);
|
AvCommands = TransportCommands.Create(document);
|
||||||
AvCommands = avCommands;
|
return AvCommands;
|
||||||
return avCommands;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<TransportCommands> GetRenderingProtocolAsync(CancellationToken cancellationToken)
|
private async Task<TransportCommands> GetRenderingProtocolAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var rendererCommands = RendererCommands;
|
if (RendererCommands != null)
|
||||||
|
|
||||||
if (rendererCommands != null)
|
|
||||||
{
|
{
|
||||||
return rendererCommands;
|
return RendererCommands;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
@@ -845,7 +889,6 @@ namespace Emby.Dlna.PlayTo
|
|||||||
}
|
}
|
||||||
|
|
||||||
var avService = GetServiceRenderingControl();
|
var avService = GetServiceRenderingControl();
|
||||||
|
|
||||||
if (avService == null)
|
if (avService == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Device AvService is null");
|
throw new ArgumentException("Device AvService is null");
|
||||||
@@ -857,9 +900,8 @@ namespace Emby.Dlna.PlayTo
|
|||||||
_logger.LogDebug("Dlna Device.GetRenderingProtocolAsync");
|
_logger.LogDebug("Dlna Device.GetRenderingProtocolAsync");
|
||||||
var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
|
var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
rendererCommands = TransportCommands.Create(document);
|
RendererCommands = TransportCommands.Create(document);
|
||||||
RendererCommands = rendererCommands;
|
return RendererCommands;
|
||||||
return rendererCommands;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string NormalizeUrl(string baseUrl, string url)
|
private string NormalizeUrl(string baseUrl, string url)
|
||||||
@@ -871,85 +913,103 @@ namespace Emby.Dlna.PlayTo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!url.Contains("/"))
|
if (!url.Contains("/"))
|
||||||
|
{
|
||||||
url = "/dmr/" + url;
|
url = "/dmr/" + url;
|
||||||
|
}
|
||||||
|
|
||||||
if (!url.StartsWith("/"))
|
if (!url.StartsWith("/"))
|
||||||
|
{
|
||||||
url = "/" + url;
|
url = "/" + url;
|
||||||
|
}
|
||||||
|
|
||||||
return baseUrl + url;
|
return baseUrl + url;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TransportCommands AvCommands
|
private TransportCommands AvCommands { get; set; }
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TransportCommands RendererCommands
|
private TransportCommands RendererCommands { get; set; }
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<Device> CreateuPnpDeviceAsync(Uri url, IHttpClient httpClient, IServerConfigurationManager config, ILogger logger, ITimerFactory timerFactory, CancellationToken cancellationToken)
|
public static async Task<Device> CreateuPnpDeviceAsync(Uri url, IHttpClient httpClient, IServerConfigurationManager config, ILogger logger, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var ssdpHttpClient = new SsdpHttpClient(httpClient, config);
|
var ssdpHttpClient = new SsdpHttpClient(httpClient, config);
|
||||||
|
|
||||||
var document = await ssdpHttpClient.GetDataAsync(url.ToString(), cancellationToken).ConfigureAwait(false);
|
var document = await ssdpHttpClient.GetDataAsync(url.ToString(), cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
var deviceProperties = new DeviceInfo();
|
|
||||||
|
|
||||||
var friendlyNames = new List<string>();
|
var friendlyNames = new List<string>();
|
||||||
|
|
||||||
var name = document.Descendants(uPnpNamespaces.ud.GetName("friendlyName")).FirstOrDefault();
|
var name = document.Descendants(uPnpNamespaces.ud.GetName("friendlyName")).FirstOrDefault();
|
||||||
if (name != null && !string.IsNullOrWhiteSpace(name.Value))
|
if (name != null && !string.IsNullOrWhiteSpace(name.Value))
|
||||||
|
{
|
||||||
friendlyNames.Add(name.Value);
|
friendlyNames.Add(name.Value);
|
||||||
|
}
|
||||||
|
|
||||||
var room = document.Descendants(uPnpNamespaces.ud.GetName("roomName")).FirstOrDefault();
|
var room = document.Descendants(uPnpNamespaces.ud.GetName("roomName")).FirstOrDefault();
|
||||||
if (room != null && !string.IsNullOrWhiteSpace(room.Value))
|
if (room != null && !string.IsNullOrWhiteSpace(room.Value))
|
||||||
|
{
|
||||||
friendlyNames.Add(room.Value);
|
friendlyNames.Add(room.Value);
|
||||||
|
}
|
||||||
|
|
||||||
deviceProperties.Name = string.Join(" ", friendlyNames.ToArray());
|
var deviceProperties = new DeviceInfo()
|
||||||
|
{
|
||||||
|
Name = string.Join(" ", friendlyNames),
|
||||||
|
BaseUrl = string.Format("http://{0}:{1}", url.Host, url.Port)
|
||||||
|
};
|
||||||
|
|
||||||
var model = document.Descendants(uPnpNamespaces.ud.GetName("modelName")).FirstOrDefault();
|
var model = document.Descendants(uPnpNamespaces.ud.GetName("modelName")).FirstOrDefault();
|
||||||
if (model != null)
|
if (model != null)
|
||||||
|
{
|
||||||
deviceProperties.ModelName = model.Value;
|
deviceProperties.ModelName = model.Value;
|
||||||
|
}
|
||||||
|
|
||||||
var modelNumber = document.Descendants(uPnpNamespaces.ud.GetName("modelNumber")).FirstOrDefault();
|
var modelNumber = document.Descendants(uPnpNamespaces.ud.GetName("modelNumber")).FirstOrDefault();
|
||||||
if (modelNumber != null)
|
if (modelNumber != null)
|
||||||
|
{
|
||||||
deviceProperties.ModelNumber = modelNumber.Value;
|
deviceProperties.ModelNumber = modelNumber.Value;
|
||||||
|
}
|
||||||
|
|
||||||
var uuid = document.Descendants(uPnpNamespaces.ud.GetName("UDN")).FirstOrDefault();
|
var uuid = document.Descendants(uPnpNamespaces.ud.GetName("UDN")).FirstOrDefault();
|
||||||
if (uuid != null)
|
if (uuid != null)
|
||||||
|
{
|
||||||
deviceProperties.UUID = uuid.Value;
|
deviceProperties.UUID = uuid.Value;
|
||||||
|
}
|
||||||
|
|
||||||
var manufacturer = document.Descendants(uPnpNamespaces.ud.GetName("manufacturer")).FirstOrDefault();
|
var manufacturer = document.Descendants(uPnpNamespaces.ud.GetName("manufacturer")).FirstOrDefault();
|
||||||
if (manufacturer != null)
|
if (manufacturer != null)
|
||||||
|
{
|
||||||
deviceProperties.Manufacturer = manufacturer.Value;
|
deviceProperties.Manufacturer = manufacturer.Value;
|
||||||
|
}
|
||||||
|
|
||||||
var manufacturerUrl = document.Descendants(uPnpNamespaces.ud.GetName("manufacturerURL")).FirstOrDefault();
|
var manufacturerUrl = document.Descendants(uPnpNamespaces.ud.GetName("manufacturerURL")).FirstOrDefault();
|
||||||
if (manufacturerUrl != null)
|
if (manufacturerUrl != null)
|
||||||
|
{
|
||||||
deviceProperties.ManufacturerUrl = manufacturerUrl.Value;
|
deviceProperties.ManufacturerUrl = manufacturerUrl.Value;
|
||||||
|
}
|
||||||
|
|
||||||
var presentationUrl = document.Descendants(uPnpNamespaces.ud.GetName("presentationURL")).FirstOrDefault();
|
var presentationUrl = document.Descendants(uPnpNamespaces.ud.GetName("presentationURL")).FirstOrDefault();
|
||||||
if (presentationUrl != null)
|
if (presentationUrl != null)
|
||||||
|
{
|
||||||
deviceProperties.PresentationUrl = presentationUrl.Value;
|
deviceProperties.PresentationUrl = presentationUrl.Value;
|
||||||
|
}
|
||||||
|
|
||||||
var modelUrl = document.Descendants(uPnpNamespaces.ud.GetName("modelURL")).FirstOrDefault();
|
var modelUrl = document.Descendants(uPnpNamespaces.ud.GetName("modelURL")).FirstOrDefault();
|
||||||
if (modelUrl != null)
|
if (modelUrl != null)
|
||||||
|
{
|
||||||
deviceProperties.ModelUrl = modelUrl.Value;
|
deviceProperties.ModelUrl = modelUrl.Value;
|
||||||
|
}
|
||||||
|
|
||||||
var serialNumber = document.Descendants(uPnpNamespaces.ud.GetName("serialNumber")).FirstOrDefault();
|
var serialNumber = document.Descendants(uPnpNamespaces.ud.GetName("serialNumber")).FirstOrDefault();
|
||||||
if (serialNumber != null)
|
if (serialNumber != null)
|
||||||
|
{
|
||||||
deviceProperties.SerialNumber = serialNumber.Value;
|
deviceProperties.SerialNumber = serialNumber.Value;
|
||||||
|
}
|
||||||
|
|
||||||
var modelDescription = document.Descendants(uPnpNamespaces.ud.GetName("modelDescription")).FirstOrDefault();
|
var modelDescription = document.Descendants(uPnpNamespaces.ud.GetName("modelDescription")).FirstOrDefault();
|
||||||
if (modelDescription != null)
|
if (modelDescription != null)
|
||||||
|
{
|
||||||
deviceProperties.ModelDescription = modelDescription.Value;
|
deviceProperties.ModelDescription = modelDescription.Value;
|
||||||
|
}
|
||||||
deviceProperties.BaseUrl = string.Format("http://{0}:{1}", url.Host, url.Port);
|
|
||||||
|
|
||||||
var icon = document.Descendants(uPnpNamespaces.ud.GetName("icon")).FirstOrDefault();
|
var icon = document.Descendants(uPnpNamespaces.ud.GetName("icon")).FirstOrDefault();
|
||||||
|
|
||||||
if (icon != null)
|
if (icon != null)
|
||||||
{
|
{
|
||||||
deviceProperties.Icon = CreateIcon(icon);
|
deviceProperties.Icon = CreateIcon(icon);
|
||||||
@@ -958,12 +1018,15 @@ namespace Emby.Dlna.PlayTo
|
|||||||
foreach (var services in document.Descendants(uPnpNamespaces.ud.GetName("serviceList")))
|
foreach (var services in document.Descendants(uPnpNamespaces.ud.GetName("serviceList")))
|
||||||
{
|
{
|
||||||
if (services == null)
|
if (services == null)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var servicesList = services.Descendants(uPnpNamespaces.ud.GetName("service"));
|
var servicesList = services.Descendants(uPnpNamespaces.ud.GetName("service"));
|
||||||
|
|
||||||
if (servicesList == null)
|
if (servicesList == null)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var element in servicesList)
|
foreach (var element in servicesList)
|
||||||
{
|
{
|
||||||
@@ -976,9 +1039,7 @@ namespace Emby.Dlna.PlayTo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var device = new Device(deviceProperties, httpClient, logger, config, timerFactory);
|
return new Device(deviceProperties, httpClient, logger, config);
|
||||||
|
|
||||||
return device;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -1065,75 +1126,73 @@ namespace Emby.Dlna.PlayTo
|
|||||||
|
|
||||||
private void OnPlaybackStart(uBaseObject mediaInfo)
|
private void OnPlaybackStart(uBaseObject mediaInfo)
|
||||||
{
|
{
|
||||||
if (PlaybackStart != null)
|
if (string.IsNullOrWhiteSpace(mediaInfo.Url))
|
||||||
{
|
|
||||||
PlaybackStart.Invoke(this, new PlaybackStartEventArgs
|
|
||||||
{
|
|
||||||
MediaInfo = mediaInfo
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnPlaybackProgress(uBaseObject mediaInfo)
|
|
||||||
{
|
|
||||||
var mediaUrl = mediaInfo.Url;
|
|
||||||
if (string.IsNullOrWhiteSpace(mediaUrl))
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PlaybackProgress != null)
|
PlaybackStart?.Invoke(this, new PlaybackStartEventArgs
|
||||||
{
|
{
|
||||||
PlaybackProgress.Invoke(this, new PlaybackProgressEventArgs
|
MediaInfo = mediaInfo
|
||||||
{
|
});
|
||||||
MediaInfo = mediaInfo
|
}
|
||||||
});
|
|
||||||
|
private void OnPlaybackProgress(uBaseObject mediaInfo)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(mediaInfo.Url))
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PlaybackProgress?.Invoke(this, new PlaybackProgressEventArgs
|
||||||
|
{
|
||||||
|
MediaInfo = mediaInfo
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPlaybackStop(uBaseObject mediaInfo)
|
private void OnPlaybackStop(uBaseObject mediaInfo)
|
||||||
{
|
{
|
||||||
if (PlaybackStopped != null)
|
PlaybackStopped?.Invoke(this, new PlaybackStoppedEventArgs
|
||||||
{
|
{
|
||||||
PlaybackStopped.Invoke(this, new PlaybackStoppedEventArgs
|
MediaInfo = mediaInfo
|
||||||
{
|
});
|
||||||
MediaInfo = mediaInfo
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMediaChanged(uBaseObject old, uBaseObject newMedia)
|
private void OnMediaChanged(uBaseObject old, uBaseObject newMedia)
|
||||||
{
|
{
|
||||||
if (MediaChanged != null)
|
MediaChanged?.Invoke(this, new MediaChangedEventArgs
|
||||||
{
|
{
|
||||||
MediaChanged.Invoke(this, new MediaChangedEventArgs
|
OldMediaInfo = old,
|
||||||
{
|
NewMediaInfo = newMedia
|
||||||
OldMediaInfo = old,
|
});
|
||||||
NewMediaInfo = newMedia
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#region IDisposable
|
#region IDisposable
|
||||||
|
|
||||||
bool _disposed;
|
bool _disposed;
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (!_disposed)
|
Dispose(true);
|
||||||
{
|
GC.SuppressFinalize(this);
|
||||||
_disposed = true;
|
|
||||||
|
|
||||||
DisposeTimer();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DisposeTimer()
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (_timer != null)
|
if (_disposed)
|
||||||
{
|
{
|
||||||
_timer.Dispose();
|
return;
|
||||||
_timer = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_timer?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_timer = null;
|
||||||
|
Properties = null;
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Emby.Dlna.Didl;
|
using Emby.Dlna.Didl;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Controller.Dlna;
|
using MediaBrowser.Controller.Dlna;
|
||||||
using MediaBrowser.Controller.Drawing;
|
using MediaBrowser.Controller.Drawing;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
@@ -17,8 +18,8 @@ using MediaBrowser.Model.Dto;
|
|||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Events;
|
using MediaBrowser.Model.Events;
|
||||||
using MediaBrowser.Model.Globalization;
|
using MediaBrowser.Model.Globalization;
|
||||||
using MediaBrowser.Model.Services;
|
|
||||||
using MediaBrowser.Model.Session;
|
using MediaBrowser.Model.Session;
|
||||||
|
using Microsoft.AspNetCore.WebUtilities;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Dlna.PlayTo
|
namespace Emby.Dlna.PlayTo
|
||||||
@@ -42,30 +43,43 @@ namespace Emby.Dlna.PlayTo
|
|||||||
private readonly IDeviceDiscovery _deviceDiscovery;
|
private readonly IDeviceDiscovery _deviceDiscovery;
|
||||||
private readonly string _serverAddress;
|
private readonly string _serverAddress;
|
||||||
private readonly string _accessToken;
|
private readonly string _accessToken;
|
||||||
private readonly DateTime _creationTime;
|
|
||||||
|
|
||||||
public bool IsSessionActive => !_disposed && _device != null;
|
public bool IsSessionActive => !_disposed && _device != null;
|
||||||
|
|
||||||
public bool SupportsMediaControl => IsSessionActive;
|
public bool SupportsMediaControl => IsSessionActive;
|
||||||
|
|
||||||
public PlayToController(SessionInfo session, ISessionManager sessionManager, ILibraryManager libraryManager, ILogger logger, IDlnaManager dlnaManager, IUserManager userManager, IImageProcessor imageProcessor, string serverAddress, string accessToken, IDeviceDiscovery deviceDiscovery, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IConfigurationManager config, IMediaEncoder mediaEncoder)
|
public PlayToController(
|
||||||
|
SessionInfo session,
|
||||||
|
ISessionManager sessionManager,
|
||||||
|
ILibraryManager libraryManager,
|
||||||
|
ILogger logger,
|
||||||
|
IDlnaManager dlnaManager,
|
||||||
|
IUserManager userManager,
|
||||||
|
IImageProcessor imageProcessor,
|
||||||
|
string serverAddress,
|
||||||
|
string accessToken,
|
||||||
|
IDeviceDiscovery deviceDiscovery,
|
||||||
|
IUserDataManager userDataManager,
|
||||||
|
ILocalizationManager localization,
|
||||||
|
IMediaSourceManager mediaSourceManager,
|
||||||
|
IConfigurationManager config,
|
||||||
|
IMediaEncoder mediaEncoder)
|
||||||
{
|
{
|
||||||
_session = session;
|
_session = session;
|
||||||
_sessionManager = sessionManager;
|
_sessionManager = sessionManager;
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
|
_logger = logger;
|
||||||
_dlnaManager = dlnaManager;
|
_dlnaManager = dlnaManager;
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_imageProcessor = imageProcessor;
|
_imageProcessor = imageProcessor;
|
||||||
_serverAddress = serverAddress;
|
_serverAddress = serverAddress;
|
||||||
|
_accessToken = accessToken;
|
||||||
_deviceDiscovery = deviceDiscovery;
|
_deviceDiscovery = deviceDiscovery;
|
||||||
_userDataManager = userDataManager;
|
_userDataManager = userDataManager;
|
||||||
_localization = localization;
|
_localization = localization;
|
||||||
_mediaSourceManager = mediaSourceManager;
|
_mediaSourceManager = mediaSourceManager;
|
||||||
_config = config;
|
_config = config;
|
||||||
_mediaEncoder = mediaEncoder;
|
_mediaEncoder = mediaEncoder;
|
||||||
_accessToken = accessToken;
|
|
||||||
_logger = logger;
|
|
||||||
_creationTime = DateTime.UtcNow;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Init(Device device)
|
public void Init(Device device)
|
||||||
@@ -88,9 +102,10 @@ namespace Emby.Dlna.PlayTo
|
|||||||
{
|
{
|
||||||
_sessionManager.ReportSessionEnded(_session.Id);
|
_sessionManager.ReportSessionEnded(_session.Id);
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// Could throw if the session is already gone
|
// Could throw if the session is already gone
|
||||||
|
_logger.LogError(ex, "Error reporting the end of session {Id}", _session.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,20 +113,14 @@ namespace Emby.Dlna.PlayTo
|
|||||||
{
|
{
|
||||||
var info = e.Argument;
|
var info = e.Argument;
|
||||||
|
|
||||||
info.Headers.TryGetValue("NTS", out string nts);
|
if (!_disposed
|
||||||
|
&& info.Headers.TryGetValue("USN", out string usn)
|
||||||
if (!info.Headers.TryGetValue("USN", out string usn)) usn = string.Empty;
|
&& usn.IndexOf(_device.Properties.UUID, StringComparison.OrdinalIgnoreCase) != -1
|
||||||
|
&& (usn.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) != -1
|
||||||
if (!info.Headers.TryGetValue("NT", out string nt)) nt = string.Empty;
|
|| (info.Headers.TryGetValue("NT", out string nt)
|
||||||
|
&& nt.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) != -1)))
|
||||||
if (usn.IndexOf(_device.Properties.UUID, StringComparison.OrdinalIgnoreCase) != -1 &&
|
|
||||||
!_disposed)
|
|
||||||
{
|
{
|
||||||
if (usn.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) != -1 ||
|
OnDeviceUnavailable();
|
||||||
nt.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) != -1)
|
|
||||||
{
|
|
||||||
OnDeviceUnavailable();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,9 +383,7 @@ namespace Emby.Dlna.PlayTo
|
|||||||
return _device.IsPaused ? _device.SetPlay(CancellationToken.None) : _device.SetPause(CancellationToken.None);
|
return _device.IsPaused ? _device.SetPlay(CancellationToken.None) : _device.SetPause(CancellationToken.None);
|
||||||
|
|
||||||
case PlaystateCommand.Seek:
|
case PlaystateCommand.Seek:
|
||||||
{
|
return Seek(command.SeekPositionTicks ?? 0);
|
||||||
return Seek(command.SeekPositionTicks ?? 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
case PlaystateCommand.NextTrack:
|
case PlaystateCommand.NextTrack:
|
||||||
return SetPlaylistIndex(_currentPlaylistIndex + 1);
|
return SetPlaylistIndex(_currentPlaylistIndex + 1);
|
||||||
@@ -442,8 +449,7 @@ namespace Emby.Dlna.PlayTo
|
|||||||
var profile = _dlnaManager.GetProfile(deviceInfo.ToDeviceIdentification()) ??
|
var profile = _dlnaManager.GetProfile(deviceInfo.ToDeviceIdentification()) ??
|
||||||
_dlnaManager.GetDefaultProfile();
|
_dlnaManager.GetDefaultProfile();
|
||||||
|
|
||||||
var hasMediaSources = item as IHasMediaSources;
|
var mediaSources = item is IHasMediaSources
|
||||||
var mediaSources = hasMediaSources != null
|
|
||||||
? (_mediaSourceManager.GetStaticMediaSources(item, true, user))
|
? (_mediaSourceManager.GetStaticMediaSources(item, true, user))
|
||||||
: new List<MediaSourceInfo>();
|
: new List<MediaSourceInfo>();
|
||||||
|
|
||||||
@@ -452,7 +458,7 @@ namespace Emby.Dlna.PlayTo
|
|||||||
|
|
||||||
playlistItem.StreamUrl = DidlBuilder.NormalizeDlnaMediaUrl(playlistItem.StreamInfo.ToUrl(_serverAddress, _accessToken));
|
playlistItem.StreamUrl = DidlBuilder.NormalizeDlnaMediaUrl(playlistItem.StreamInfo.ToUrl(_serverAddress, _accessToken));
|
||||||
|
|
||||||
var itemXml = new DidlBuilder(profile, user, _imageProcessor, _serverAddress, _accessToken, _userDataManager, _localization, _mediaSourceManager, _logger, _libraryManager, _mediaEncoder)
|
var itemXml = new DidlBuilder(profile, user, _imageProcessor, _serverAddress, _accessToken, _userDataManager, _localization, _mediaSourceManager, _logger, _mediaEncoder)
|
||||||
.GetItemDidl(_config.GetDlnaConfiguration(), item, user, null, _session.DeviceId, new Filter(), playlistItem.StreamInfo);
|
.GetItemDidl(_config.GetDlnaConfiguration(), item, user, null, _session.DeviceId, new Filter(), playlistItem.StreamInfo);
|
||||||
|
|
||||||
playlistItem.Didl = itemXml;
|
playlistItem.Didl = itemXml;
|
||||||
@@ -601,22 +607,34 @@ namespace Emby.Dlna.PlayTo
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (!_disposed)
|
Dispose(true);
|
||||||
{
|
GC.SuppressFinalize(this);
|
||||||
_disposed = true;
|
|
||||||
|
|
||||||
_device.PlaybackStart -= _device_PlaybackStart;
|
|
||||||
_device.PlaybackProgress -= _device_PlaybackProgress;
|
|
||||||
_device.PlaybackStopped -= _device_PlaybackStopped;
|
|
||||||
_device.MediaChanged -= _device_MediaChanged;
|
|
||||||
//_deviceDiscovery.DeviceLeft -= _deviceDiscovery_DeviceLeft;
|
|
||||||
_device.OnDeviceUnavailable = null;
|
|
||||||
|
|
||||||
_device.Dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_device.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_device.PlaybackStart -= _device_PlaybackStart;
|
||||||
|
_device.PlaybackProgress -= _device_PlaybackProgress;
|
||||||
|
_device.PlaybackStopped -= _device_PlaybackStopped;
|
||||||
|
_device.MediaChanged -= _device_MediaChanged;
|
||||||
|
_deviceDiscovery.DeviceLeft -= _deviceDiscovery_DeviceLeft;
|
||||||
|
_device.OnDeviceUnavailable = null;
|
||||||
|
_device = null;
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
|
||||||
|
|
||||||
private Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
|
private Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
@@ -837,13 +855,13 @@ namespace Emby.Dlna.PlayTo
|
|||||||
if (index == -1) return request;
|
if (index == -1) return request;
|
||||||
|
|
||||||
var query = url.Substring(index + 1);
|
var query = url.Substring(index + 1);
|
||||||
QueryParamCollection values = MyHttpUtility.ParseQueryString(query);
|
Dictionary<string, string> values = QueryHelpers.ParseQuery(query).ToDictionary(kv => kv.Key, kv => kv.Value.ToString());
|
||||||
|
|
||||||
request.DeviceProfileId = values.Get("DeviceProfileId");
|
request.DeviceProfileId = values.GetValueOrDefault("DeviceProfileId");
|
||||||
request.DeviceId = values.Get("DeviceId");
|
request.DeviceId = values.GetValueOrDefault("DeviceId");
|
||||||
request.MediaSourceId = values.Get("MediaSourceId");
|
request.MediaSourceId = values.GetValueOrDefault("MediaSourceId");
|
||||||
request.LiveStreamId = values.Get("LiveStreamId");
|
request.LiveStreamId = values.GetValueOrDefault("LiveStreamId");
|
||||||
request.IsDirectStream = string.Equals("true", values.Get("Static"), StringComparison.OrdinalIgnoreCase);
|
request.IsDirectStream = string.Equals("true", values.GetValueOrDefault("Static"), StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
request.AudioStreamIndex = GetIntValue(values, "AudioStreamIndex");
|
request.AudioStreamIndex = GetIntValue(values, "AudioStreamIndex");
|
||||||
request.SubtitleStreamIndex = GetIntValue(values, "SubtitleStreamIndex");
|
request.SubtitleStreamIndex = GetIntValue(values, "SubtitleStreamIndex");
|
||||||
@@ -857,9 +875,9 @@ namespace Emby.Dlna.PlayTo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int? GetIntValue(QueryParamCollection values, string name)
|
private static int? GetIntValue(IReadOnlyDictionary<string, string> values, string name)
|
||||||
{
|
{
|
||||||
var value = values.Get(name);
|
var value = values.GetValueOrDefault(name);
|
||||||
|
|
||||||
if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
|
if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
|
||||||
{
|
{
|
||||||
@@ -869,9 +887,9 @@ namespace Emby.Dlna.PlayTo
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long GetLongValue(QueryParamCollection values, string name)
|
private static long GetLongValue(IReadOnlyDictionary<string, string> values, string name)
|
||||||
{
|
{
|
||||||
var value = values.Get(name);
|
var value = values.GetValueOrDefault(name);
|
||||||
|
|
||||||
if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
|
if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ using MediaBrowser.Model.Events;
|
|||||||
using MediaBrowser.Model.Globalization;
|
using MediaBrowser.Model.Globalization;
|
||||||
using MediaBrowser.Model.Net;
|
using MediaBrowser.Model.Net;
|
||||||
using MediaBrowser.Model.Session;
|
using MediaBrowser.Model.Session;
|
||||||
using MediaBrowser.Model.Threading;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Dlna.PlayTo
|
namespace Emby.Dlna.PlayTo
|
||||||
@@ -39,13 +38,12 @@ namespace Emby.Dlna.PlayTo
|
|||||||
private readonly IDeviceDiscovery _deviceDiscovery;
|
private readonly IDeviceDiscovery _deviceDiscovery;
|
||||||
private readonly IMediaSourceManager _mediaSourceManager;
|
private readonly IMediaSourceManager _mediaSourceManager;
|
||||||
private readonly IMediaEncoder _mediaEncoder;
|
private readonly IMediaEncoder _mediaEncoder;
|
||||||
private readonly ITimerFactory _timerFactory;
|
|
||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
private SemaphoreSlim _sessionLock = new SemaphoreSlim(1, 1);
|
private SemaphoreSlim _sessionLock = new SemaphoreSlim(1, 1);
|
||||||
private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
|
private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
public PlayToManager(ILogger logger, ISessionManager sessionManager, ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IImageProcessor imageProcessor, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient, IServerConfigurationManager config, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, ITimerFactory timerFactory)
|
public PlayToManager(ILogger logger, ISessionManager sessionManager, ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IImageProcessor imageProcessor, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient, IServerConfigurationManager config, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_sessionManager = sessionManager;
|
_sessionManager = sessionManager;
|
||||||
@@ -61,7 +59,6 @@ namespace Emby.Dlna.PlayTo
|
|||||||
_localization = localization;
|
_localization = localization;
|
||||||
_mediaSourceManager = mediaSourceManager;
|
_mediaSourceManager = mediaSourceManager;
|
||||||
_mediaEncoder = mediaEncoder;
|
_mediaEncoder = mediaEncoder;
|
||||||
_timerFactory = timerFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start()
|
public void Start()
|
||||||
@@ -92,11 +89,6 @@ namespace Emby.Dlna.PlayTo
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_sessionManager.Sessions.Any(i => usn.IndexOf(i.DeviceId, StringComparison.OrdinalIgnoreCase) != -1))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var cancellationToken = _disposeCancellationTokenSource.Token;
|
var cancellationToken = _disposeCancellationTokenSource.Token;
|
||||||
|
|
||||||
await _sessionLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
await _sessionLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||||
@@ -108,6 +100,11 @@ namespace Emby.Dlna.PlayTo
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_sessionManager.Sessions.Any(i => usn.IndexOf(i.DeviceId, StringComparison.OrdinalIgnoreCase) != -1))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await AddDevice(info, location, cancellationToken).ConfigureAwait(false);
|
await AddDevice(info, location, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
@@ -162,17 +159,15 @@ namespace Emby.Dlna.PlayTo
|
|||||||
uuid = location.GetMD5().ToString("N");
|
uuid = location.GetMD5().ToString("N");
|
||||||
}
|
}
|
||||||
|
|
||||||
string deviceName = null;
|
var sessionInfo = _sessionManager.LogSessionActivity("DLNA", _appHost.ApplicationVersion, uuid, null, uri.OriginalString, null);
|
||||||
|
|
||||||
var sessionInfo = _sessionManager.LogSessionActivity("DLNA", _appHost.ApplicationVersion, uuid, deviceName, uri.OriginalString, null);
|
|
||||||
|
|
||||||
var controller = sessionInfo.SessionControllers.OfType<PlayToController>().FirstOrDefault();
|
var controller = sessionInfo.SessionControllers.OfType<PlayToController>().FirstOrDefault();
|
||||||
|
|
||||||
if (controller == null)
|
if (controller == null)
|
||||||
{
|
{
|
||||||
var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _config, _logger, _timerFactory, cancellationToken).ConfigureAwait(false);
|
var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _config, _logger, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
deviceName = device.Properties.Name;
|
string deviceName = device.Properties.Name;
|
||||||
|
|
||||||
_sessionManager.UpdateDeviceName(sessionInfo.Id, deviceName);
|
_sessionManager.UpdateDeviceName(sessionInfo.Id, deviceName);
|
||||||
|
|
||||||
@@ -186,8 +181,6 @@ namespace Emby.Dlna.PlayTo
|
|||||||
serverAddress = _appHost.GetLocalApiUrl(info.LocalIpAddress);
|
serverAddress = _appHost.GetLocalApiUrl(info.LocalIpAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
string accessToken = null;
|
|
||||||
|
|
||||||
controller = new PlayToController(sessionInfo,
|
controller = new PlayToController(sessionInfo,
|
||||||
_sessionManager,
|
_sessionManager,
|
||||||
_libraryManager,
|
_libraryManager,
|
||||||
@@ -196,7 +189,7 @@ namespace Emby.Dlna.PlayTo
|
|||||||
_userManager,
|
_userManager,
|
||||||
_imageProcessor,
|
_imageProcessor,
|
||||||
serverAddress,
|
serverAddress,
|
||||||
accessToken,
|
null,
|
||||||
_deviceDiscovery,
|
_deviceDiscovery,
|
||||||
_userDataManager,
|
_userDataManager,
|
||||||
_localization,
|
_localization,
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ namespace Emby.Dlna.PlayTo
|
|||||||
{
|
{
|
||||||
public class PlaylistItemFactory
|
public class PlaylistItemFactory
|
||||||
{
|
{
|
||||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
|
||||||
|
|
||||||
public PlaylistItem Create(Photo item, DeviceProfile profile)
|
public PlaylistItem Create(Photo item, DeviceProfile profile)
|
||||||
{
|
{
|
||||||
var playlistItem = new PlaylistItem
|
var playlistItem = new PlaylistItem
|
||||||
|
|||||||
@@ -107,12 +107,18 @@ namespace Emby.Dlna.PlayTo
|
|||||||
foreach (var arg in action.ArgumentList)
|
foreach (var arg in action.ArgumentList)
|
||||||
{
|
{
|
||||||
if (arg.Direction == "out")
|
if (arg.Direction == "out")
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (arg.Name == "InstanceID")
|
if (arg.Name == "InstanceID")
|
||||||
|
{
|
||||||
stateString += BuildArgumentXml(arg, "0");
|
stateString += BuildArgumentXml(arg, "0");
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
stateString += BuildArgumentXml(arg, null);
|
stateString += BuildArgumentXml(arg, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return string.Format(CommandBase, action.Name, xmlNamespace, stateString);
|
return string.Format(CommandBase, action.Name, xmlNamespace, stateString);
|
||||||
@@ -125,11 +131,18 @@ namespace Emby.Dlna.PlayTo
|
|||||||
foreach (var arg in action.ArgumentList)
|
foreach (var arg in action.ArgumentList)
|
||||||
{
|
{
|
||||||
if (arg.Direction == "out")
|
if (arg.Direction == "out")
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (arg.Name == "InstanceID")
|
if (arg.Name == "InstanceID")
|
||||||
|
{
|
||||||
stateString += BuildArgumentXml(arg, "0");
|
stateString += BuildArgumentXml(arg, "0");
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
stateString += BuildArgumentXml(arg, value.ToString(), commandParameter);
|
stateString += BuildArgumentXml(arg, value.ToString(), commandParameter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return string.Format(CommandBase, action.Name, xmlNamesapce, stateString);
|
return string.Format(CommandBase, action.Name, xmlNamesapce, stateString);
|
||||||
@@ -142,11 +155,17 @@ namespace Emby.Dlna.PlayTo
|
|||||||
foreach (var arg in action.ArgumentList)
|
foreach (var arg in action.ArgumentList)
|
||||||
{
|
{
|
||||||
if (arg.Name == "InstanceID")
|
if (arg.Name == "InstanceID")
|
||||||
|
{
|
||||||
stateString += BuildArgumentXml(arg, "0");
|
stateString += BuildArgumentXml(arg, "0");
|
||||||
|
}
|
||||||
else if (dictionary.ContainsKey(arg.Name))
|
else if (dictionary.ContainsKey(arg.Name))
|
||||||
|
{
|
||||||
stateString += BuildArgumentXml(arg, dictionary[arg.Name]);
|
stateString += BuildArgumentXml(arg, dictionary[arg.Name]);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
stateString += BuildArgumentXml(arg, value.ToString());
|
stateString += BuildArgumentXml(arg, value.ToString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return string.Format(CommandBase, action.Name, xmlNamesapce, stateString);
|
return string.Format(CommandBase, action.Name, xmlNamesapce, stateString);
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Emby.Dlna.PlayTo
|
|
||||||
{
|
|
||||||
public class TransportStateEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
public TRANSPORTSTATE State { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Xml.Linq;
|
|
||||||
|
|
||||||
namespace Emby.Dlna.PlayTo
|
|
||||||
{
|
|
||||||
public class uParser
|
|
||||||
{
|
|
||||||
public static IList<uBaseObject> ParseBrowseXml(XDocument doc)
|
|
||||||
{
|
|
||||||
if (doc == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("doc");
|
|
||||||
}
|
|
||||||
|
|
||||||
var list = new List<uBaseObject>();
|
|
||||||
|
|
||||||
var document = doc.Document;
|
|
||||||
|
|
||||||
if (document == null)
|
|
||||||
return list;
|
|
||||||
|
|
||||||
var item = (from result in document.Descendants("Result") select result).FirstOrDefault();
|
|
||||||
|
|
||||||
if (item == null)
|
|
||||||
return list;
|
|
||||||
|
|
||||||
var uPnpResponse = XElement.Parse((string)item);
|
|
||||||
|
|
||||||
var uObjects = from container in uPnpResponse.Elements(uPnpNamespaces.containers)
|
|
||||||
select new uParserObject { Element = container };
|
|
||||||
|
|
||||||
var uObjects2 = from container in uPnpResponse.Elements(uPnpNamespaces.items)
|
|
||||||
select new uParserObject { Element = container };
|
|
||||||
|
|
||||||
list.AddRange(uObjects.Concat(uObjects2).Select(CreateObjectFromXML).Where(uObject => uObject != null));
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static uBaseObject CreateObjectFromXML(uParserObject uItem)
|
|
||||||
{
|
|
||||||
return UpnpContainer.Create(uItem.Element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
using System.Xml.Linq;
|
|
||||||
|
|
||||||
namespace Emby.Dlna.PlayTo
|
|
||||||
{
|
|
||||||
public class uParserObject
|
|
||||||
{
|
|
||||||
public XElement Element { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,8 +8,8 @@ using System.Resources;
|
|||||||
[assembly: AssemblyDescription("")]
|
[assembly: AssemblyDescription("")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCompany("Jellyfin Project")]
|
[assembly: AssemblyCompany("Jellyfin Project")]
|
||||||
[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")]
|
[assembly: AssemblyProduct("Jellyfin Server")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")]
|
[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: NeutralResourcesLanguage("en")]
|
[assembly: NeutralResourcesLanguage("en")]
|
||||||
|
|||||||
@@ -107,19 +107,19 @@ namespace Emby.Dlna.Server
|
|||||||
'&'
|
'&'
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly string[] s_escapeStringPairs = new string[]
|
private static readonly string[] s_escapeStringPairs = new[]
|
||||||
{
|
{
|
||||||
"<",
|
"<",
|
||||||
"<",
|
"<",
|
||||||
">",
|
">",
|
||||||
">",
|
">",
|
||||||
"\"",
|
"\"",
|
||||||
""",
|
""",
|
||||||
"'",
|
"'",
|
||||||
"'",
|
"'",
|
||||||
"&",
|
"&",
|
||||||
"&"
|
"&"
|
||||||
};
|
};
|
||||||
|
|
||||||
private static string GetEscapeSequence(char c)
|
private static string GetEscapeSequence(char c)
|
||||||
{
|
{
|
||||||
@@ -133,7 +133,7 @@ namespace Emby.Dlna.Server
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return c.ToString();
|
return c.ToString(CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Replaces invalid XML characters in a string with their valid XML equivalent.</summary>
|
/// <summary>Replaces invalid XML characters in a string with their valid XML equivalent.</summary>
|
||||||
@@ -145,6 +145,7 @@ namespace Emby.Dlna.Server
|
|||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder stringBuilder = null;
|
StringBuilder stringBuilder = null;
|
||||||
int length = str.Length;
|
int length = str.Length;
|
||||||
int num = 0;
|
int num = 0;
|
||||||
@@ -230,9 +231,9 @@ namespace Emby.Dlna.Server
|
|||||||
|
|
||||||
var serverName = new string(characters);
|
var serverName = new string(characters);
|
||||||
|
|
||||||
var name = (_profile.FriendlyName ?? string.Empty).Replace("${HostName}", serverName, StringComparison.OrdinalIgnoreCase);
|
var name = _profile.FriendlyName?.Replace("${HostName}", serverName, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
return name;
|
return name ?? string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AppendIconList(StringBuilder builder)
|
private void AppendIconList(StringBuilder builder)
|
||||||
@@ -295,65 +296,62 @@ namespace Emby.Dlna.Server
|
|||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<DeviceIcon> GetIcons()
|
private IEnumerable<DeviceIcon> GetIcons()
|
||||||
{
|
=> new[]
|
||||||
var list = new List<DeviceIcon>();
|
|
||||||
|
|
||||||
list.Add(new DeviceIcon
|
|
||||||
{
|
{
|
||||||
MimeType = "image/png",
|
new DeviceIcon
|
||||||
Depth = "24",
|
{
|
||||||
Width = 240,
|
MimeType = "image/png",
|
||||||
Height = 240,
|
Depth = "24",
|
||||||
Url = "icons/logo240.png"
|
Width = 240,
|
||||||
});
|
Height = 240,
|
||||||
|
Url = "icons/logo240.png"
|
||||||
|
},
|
||||||
|
|
||||||
list.Add(new DeviceIcon
|
new DeviceIcon
|
||||||
{
|
{
|
||||||
MimeType = "image/jpeg",
|
MimeType = "image/jpeg",
|
||||||
Depth = "24",
|
Depth = "24",
|
||||||
Width = 240,
|
Width = 240,
|
||||||
Height = 240,
|
Height = 240,
|
||||||
Url = "icons/logo240.jpg"
|
Url = "icons/logo240.jpg"
|
||||||
});
|
},
|
||||||
|
|
||||||
list.Add(new DeviceIcon
|
new DeviceIcon
|
||||||
{
|
{
|
||||||
MimeType = "image/png",
|
MimeType = "image/png",
|
||||||
Depth = "24",
|
Depth = "24",
|
||||||
Width = 120,
|
Width = 120,
|
||||||
Height = 120,
|
Height = 120,
|
||||||
Url = "icons/logo120.png"
|
Url = "icons/logo120.png"
|
||||||
});
|
},
|
||||||
|
|
||||||
list.Add(new DeviceIcon
|
new DeviceIcon
|
||||||
{
|
{
|
||||||
MimeType = "image/jpeg",
|
MimeType = "image/jpeg",
|
||||||
Depth = "24",
|
Depth = "24",
|
||||||
Width = 120,
|
Width = 120,
|
||||||
Height = 120,
|
Height = 120,
|
||||||
Url = "icons/logo120.jpg"
|
Url = "icons/logo120.jpg"
|
||||||
});
|
},
|
||||||
|
|
||||||
list.Add(new DeviceIcon
|
new DeviceIcon
|
||||||
{
|
{
|
||||||
MimeType = "image/png",
|
MimeType = "image/png",
|
||||||
Depth = "24",
|
Depth = "24",
|
||||||
Width = 48,
|
Width = 48,
|
||||||
Height = 48,
|
Height = 48,
|
||||||
Url = "icons/logo48.png"
|
Url = "icons/logo48.png"
|
||||||
});
|
},
|
||||||
|
|
||||||
list.Add(new DeviceIcon
|
new DeviceIcon
|
||||||
{
|
{
|
||||||
MimeType = "image/jpeg",
|
MimeType = "image/jpeg",
|
||||||
Depth = "24",
|
Depth = "24",
|
||||||
Width = 48,
|
Width = 48,
|
||||||
Height = 48,
|
Height = 48,
|
||||||
Url = "icons/logo48.jpg"
|
Url = "icons/logo48.jpg"
|
||||||
});
|
}
|
||||||
|
};
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<DeviceService> GetServices()
|
private IEnumerable<DeviceService> GetServices()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ using System.Xml;
|
|||||||
using Emby.Dlna.Didl;
|
using Emby.Dlna.Didl;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Extensions;
|
using MediaBrowser.Controller.Extensions;
|
||||||
using MediaBrowser.Model.Xml;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Dlna.Service
|
namespace Emby.Dlna.Service
|
||||||
@@ -18,13 +17,11 @@ namespace Emby.Dlna.Service
|
|||||||
|
|
||||||
protected readonly IServerConfigurationManager Config;
|
protected readonly IServerConfigurationManager Config;
|
||||||
protected readonly ILogger _logger;
|
protected readonly ILogger _logger;
|
||||||
protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory;
|
|
||||||
|
|
||||||
protected BaseControlHandler(IServerConfigurationManager config, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
|
protected BaseControlHandler(IServerConfigurationManager config, ILogger logger)
|
||||||
{
|
{
|
||||||
Config = config;
|
Config = config;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
XmlReaderSettingsFactory = xmlReaderSettingsFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ControlResponse ProcessControlRequest(ControlRequest request)
|
public ControlResponse ProcessControlRequest(ControlRequest request)
|
||||||
@@ -61,11 +58,13 @@ namespace Emby.Dlna.Service
|
|||||||
|
|
||||||
using (var streamReader = new StreamReader(request.InputXml))
|
using (var streamReader = new StreamReader(request.InputXml))
|
||||||
{
|
{
|
||||||
var readerSettings = XmlReaderSettingsFactory.Create(false);
|
var readerSettings = new XmlReaderSettings()
|
||||||
|
{
|
||||||
readerSettings.CheckCharacters = false;
|
ValidationType = ValidationType.None,
|
||||||
readerSettings.IgnoreProcessingInstructions = true;
|
CheckCharacters = false,
|
||||||
readerSettings.IgnoreComments = true;
|
IgnoreProcessingInstructions = true,
|
||||||
|
IgnoreComments = true
|
||||||
|
};
|
||||||
|
|
||||||
using (var reader = XmlReader.Create(streamReader, readerSettings))
|
using (var reader = XmlReader.Create(streamReader, readerSettings))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ using MediaBrowser.Controller.Configuration;
|
|||||||
using MediaBrowser.Model.Dlna;
|
using MediaBrowser.Model.Dlna;
|
||||||
using MediaBrowser.Model.Events;
|
using MediaBrowser.Model.Events;
|
||||||
using MediaBrowser.Model.Net;
|
using MediaBrowser.Model.Net;
|
||||||
using MediaBrowser.Model.Threading;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Rssdp;
|
using Rssdp;
|
||||||
using Rssdp.Infrastructure;
|
using Rssdp.Infrastructure;
|
||||||
@@ -48,20 +47,17 @@ namespace Emby.Dlna.Ssdp
|
|||||||
|
|
||||||
private SsdpDeviceLocator _deviceLocator;
|
private SsdpDeviceLocator _deviceLocator;
|
||||||
|
|
||||||
private readonly ITimerFactory _timerFactory;
|
|
||||||
private readonly ISocketFactory _socketFactory;
|
private readonly ISocketFactory _socketFactory;
|
||||||
private ISsdpCommunicationsServer _commsServer;
|
private ISsdpCommunicationsServer _commsServer;
|
||||||
|
|
||||||
public DeviceDiscovery(
|
public DeviceDiscovery(
|
||||||
ILoggerFactory loggerFactory,
|
ILoggerFactory loggerFactory,
|
||||||
IServerConfigurationManager config,
|
IServerConfigurationManager config,
|
||||||
ISocketFactory socketFactory,
|
ISocketFactory socketFactory)
|
||||||
ITimerFactory timerFactory)
|
|
||||||
{
|
{
|
||||||
_logger = loggerFactory.CreateLogger(nameof(DeviceDiscovery));
|
_logger = loggerFactory.CreateLogger(nameof(DeviceDiscovery));
|
||||||
_config = config;
|
_config = config;
|
||||||
_socketFactory = socketFactory;
|
_socketFactory = socketFactory;
|
||||||
_timerFactory = timerFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call this method from somewhere in your code to start the search.
|
// Call this method from somewhere in your code to start the search.
|
||||||
@@ -78,7 +74,7 @@ namespace Emby.Dlna.Ssdp
|
|||||||
{
|
{
|
||||||
if (_listenerCount > 0 && _deviceLocator == null)
|
if (_listenerCount > 0 && _deviceLocator == null)
|
||||||
{
|
{
|
||||||
_deviceLocator = new SsdpDeviceLocator(_commsServer, _timerFactory);
|
_deviceLocator = new SsdpDeviceLocator(_commsServer);
|
||||||
|
|
||||||
// (Optional) Set the filter so we only see notifications for devices we care about
|
// (Optional) Set the filter so we only see notifications for devices we care about
|
||||||
// (can be any search target value i.e device type, uuid value etc - any value that appears in the
|
// (can be any search target value i.e device type, uuid value etc - any value that appears in the
|
||||||
|
|||||||
@@ -5,12 +5,6 @@
|
|||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="SkiaSharp" Version="1.68.0" />
|
|
||||||
<PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="1.68.0" />
|
|
||||||
<PackageReference Include="Jellyfin.SkiaSharp.NativeAssets.LinuxArm" Version="1.68.0" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
|
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
|
||||||
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
|
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ using MediaBrowser.Model.Extensions;
|
|||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.Net;
|
using MediaBrowser.Model.Net;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Emby.Drawing
|
namespace Emby.Drawing
|
||||||
{
|
{
|
||||||
@@ -66,7 +65,7 @@ namespace Emby.Drawing
|
|||||||
_appPaths = appPaths;
|
_appPaths = appPaths;
|
||||||
|
|
||||||
ImageEnhancers = Array.Empty<IImageEnhancer>();
|
ImageEnhancers = Array.Empty<IImageEnhancer>();
|
||||||
|
|
||||||
ImageHelper.ImageProcessor = this;
|
ImageHelper.ImageProcessor = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,8 +83,8 @@ namespace Emby.Drawing
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string[] SupportedInputFormats =>
|
public IReadOnlyCollection<string> SupportedInputFormats =>
|
||||||
new string[]
|
new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||||
{
|
{
|
||||||
"tiff",
|
"tiff",
|
||||||
"tif",
|
"tif",
|
||||||
@@ -138,14 +137,14 @@ namespace Emby.Drawing
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImageFormat[] GetSupportedImageOutputFormats()
|
public IReadOnlyCollection<ImageFormat> GetSupportedImageOutputFormats()
|
||||||
{
|
=> _imageEncoder.SupportedOutputFormats;
|
||||||
return _imageEncoder.SupportedOutputFormats;
|
|
||||||
}
|
private static readonly HashSet<string> TransparentImageTypes
|
||||||
|
= new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".png", ".webp", ".gif" };
|
||||||
|
|
||||||
private static readonly string[] TransparentImageTypes = new string[] { ".png", ".webp", ".gif" };
|
|
||||||
public bool SupportsTransparency(string path)
|
public bool SupportsTransparency(string path)
|
||||||
=> TransparentImageTypes.Contains(Path.GetExtension(path).ToLower());
|
=> TransparentImageTypes.Contains(Path.GetExtension(path));
|
||||||
|
|
||||||
public async Task<(string path, string mimeType, DateTime dateModified)> ProcessImage(ImageProcessingOptions options)
|
public async Task<(string path, string mimeType, DateTime dateModified)> ProcessImage(ImageProcessingOptions options)
|
||||||
{
|
{
|
||||||
@@ -168,10 +167,10 @@ namespace Emby.Drawing
|
|||||||
|
|
||||||
string originalImagePath = originalImage.Path;
|
string originalImagePath = originalImage.Path;
|
||||||
DateTime dateModified = originalImage.DateModified;
|
DateTime dateModified = originalImage.DateModified;
|
||||||
ImageSize? originalImageSize = null;
|
ImageDimensions? originalImageSize = null;
|
||||||
if (originalImage.Width > 0 && originalImage.Height > 0)
|
if (originalImage.Width > 0 && originalImage.Height > 0)
|
||||||
{
|
{
|
||||||
originalImageSize = new ImageSize(originalImage.Width, originalImage.Height);
|
originalImageSize = new ImageDimensions(originalImage.Width, originalImage.Height);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_imageEncoder.SupportsImageEncoding)
|
if (!_imageEncoder.SupportsImageEncoding)
|
||||||
@@ -181,6 +180,12 @@ namespace Emby.Drawing
|
|||||||
|
|
||||||
var supportedImageInfo = await GetSupportedImage(originalImagePath, dateModified).ConfigureAwait(false);
|
var supportedImageInfo = await GetSupportedImage(originalImagePath, dateModified).ConfigureAwait(false);
|
||||||
originalImagePath = supportedImageInfo.path;
|
originalImagePath = supportedImageInfo.path;
|
||||||
|
|
||||||
|
if (!File.Exists(originalImagePath))
|
||||||
|
{
|
||||||
|
return (originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
|
||||||
|
}
|
||||||
|
|
||||||
dateModified = supportedImageInfo.dateModified;
|
dateModified = supportedImageInfo.dateModified;
|
||||||
bool requiresTransparency = TransparentImageTypes.Contains(Path.GetExtension(originalImagePath));
|
bool requiresTransparency = TransparentImageTypes.Contains(Path.GetExtension(originalImagePath));
|
||||||
|
|
||||||
@@ -231,7 +236,7 @@ namespace Emby.Drawing
|
|||||||
return (originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
|
return (originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageSize newSize = ImageHelper.GetNewImageSize(options, null);
|
ImageDimensions newSize = ImageHelper.GetNewImageSize(options, null);
|
||||||
int quality = options.Quality;
|
int quality = options.Quality;
|
||||||
|
|
||||||
ImageFormat outputFormat = GetOutputFormat(options.SupportedOutputFormats, requiresTransparency);
|
ImageFormat outputFormat = GetOutputFormat(options.SupportedOutputFormats, requiresTransparency);
|
||||||
@@ -245,7 +250,7 @@ namespace Emby.Drawing
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!_fileSystem.FileExists(cacheFilePath))
|
if (!File.Exists(cacheFilePath))
|
||||||
{
|
{
|
||||||
if (options.CropWhiteSpace && !SupportsTransparency(originalImagePath))
|
if (options.CropWhiteSpace && !SupportsTransparency(originalImagePath))
|
||||||
{
|
{
|
||||||
@@ -262,21 +267,10 @@ namespace Emby.Drawing
|
|||||||
|
|
||||||
return (cacheFilePath, GetMimeType(outputFormat, cacheFilePath), _fileSystem.GetLastWriteTimeUtc(cacheFilePath));
|
return (cacheFilePath, GetMimeType(outputFormat, cacheFilePath), _fileSystem.GetLastWriteTimeUtc(cacheFilePath));
|
||||||
}
|
}
|
||||||
catch (ArgumentOutOfRangeException ex)
|
|
||||||
{
|
|
||||||
// Decoder failed to decode it
|
|
||||||
#if DEBUG
|
|
||||||
_logger.LogError(ex, "Error encoding image");
|
|
||||||
#endif
|
|
||||||
// Just spit out the original file if all the options are default
|
|
||||||
return (originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// If it fails for whatever reason, return the original image
|
// If it fails for whatever reason, return the original image
|
||||||
_logger.LogError(ex, "Error encoding image");
|
_logger.LogError(ex, "Error encoding image");
|
||||||
|
|
||||||
// Just spit out the original file if all the options are default
|
|
||||||
return (originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
|
return (originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -334,7 +328,7 @@ namespace Emby.Drawing
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the cache file path based on a set of parameters
|
/// Gets the cache file path based on a set of parameters
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private string GetCacheFilePath(string originalPath, ImageSize outputSize, int quality, DateTime dateModified, ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, int? blur, string backgroundColor, string foregroundLayer)
|
private string GetCacheFilePath(string originalPath, ImageDimensions outputSize, int quality, DateTime dateModified, ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, int? blur, string backgroundColor, string foregroundLayer)
|
||||||
{
|
{
|
||||||
var filename = originalPath
|
var filename = originalPath
|
||||||
+ "width=" + outputSize.Width
|
+ "width=" + outputSize.Width
|
||||||
@@ -375,29 +369,28 @@ namespace Emby.Drawing
|
|||||||
|
|
||||||
filename += "v=" + Version;
|
filename += "v=" + Version;
|
||||||
|
|
||||||
return GetCachePath(ResizedImageCachePath, filename, "." + format.ToString().ToLower());
|
return GetCachePath(ResizedImageCachePath, filename, "." + format.ToString().ToLowerInvariant());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImageSize GetImageSize(BaseItem item, ItemImageInfo info)
|
public ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info)
|
||||||
=> GetImageSize(item, info, true);
|
=> GetImageDimensions(item, info, true);
|
||||||
|
|
||||||
public ImageSize GetImageSize(BaseItem item, ItemImageInfo info, bool updateItem)
|
public ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info, bool updateItem)
|
||||||
{
|
{
|
||||||
int width = info.Width;
|
int width = info.Width;
|
||||||
int height = info.Height;
|
int height = info.Height;
|
||||||
|
|
||||||
if (height > 0 && width > 0)
|
if (height > 0 && width > 0)
|
||||||
{
|
{
|
||||||
return new ImageSize(width, height);
|
return new ImageDimensions(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
string path = info.Path;
|
string path = info.Path;
|
||||||
_logger.LogInformation("Getting image size for item {ItemType} {Path}", item.GetType().Name, path);
|
_logger.LogInformation("Getting image size for item {ItemType} {Path}", item.GetType().Name, path);
|
||||||
|
|
||||||
var size = GetImageSize(path);
|
ImageDimensions size = GetImageDimensions(path);
|
||||||
|
info.Width = size.Width;
|
||||||
info.Height = Convert.ToInt32(size.Height);
|
info.Height = size.Height;
|
||||||
info.Width = Convert.ToInt32(size.Width);
|
|
||||||
|
|
||||||
if (updateItem)
|
if (updateItem)
|
||||||
{
|
{
|
||||||
@@ -410,20 +403,8 @@ namespace Emby.Drawing
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the size of the image.
|
/// Gets the size of the image.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ImageSize GetImageSize(string path)
|
public ImageDimensions GetImageDimensions(string path)
|
||||||
{
|
=> _imageEncoder.GetImageSize(path);
|
||||||
if (string.IsNullOrEmpty(path))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
using (var s = new SKFileStream(path))
|
|
||||||
using (var codec = SKCodec.Create(s))
|
|
||||||
{
|
|
||||||
var info = codec.Info;
|
|
||||||
return new ImageSize(info.Width, info.Height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the image cache tag.
|
/// Gets the image cache tag.
|
||||||
@@ -495,7 +476,7 @@ namespace Emby.Drawing
|
|||||||
return (originalImagePath, dateModified);
|
return (originalImagePath, dateModified);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_imageEncoder.SupportedInputFormats.Contains(inputFormat, StringComparer.OrdinalIgnoreCase))
|
if (!_imageEncoder.SupportedInputFormats.Contains(inputFormat))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -640,12 +621,12 @@ namespace Emby.Drawing
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Check again in case of contention
|
// Check again in case of contention
|
||||||
if (_fileSystem.FileExists(enhancedImagePath))
|
if (File.Exists(enhancedImagePath))
|
||||||
{
|
{
|
||||||
return (enhancedImagePath, treatmentRequiresTransparency);
|
return (enhancedImagePath, treatmentRequiresTransparency);
|
||||||
}
|
}
|
||||||
|
|
||||||
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(enhancedImagePath));
|
Directory.CreateDirectory(Path.GetDirectoryName(enhancedImagePath));
|
||||||
|
|
||||||
await ExecuteImageEnhancers(supportedEnhancers, originalImagePath, enhancedImagePath, item, imageType, imageIndex).ConfigureAwait(false);
|
await ExecuteImageEnhancers(supportedEnhancers, originalImagePath, enhancedImagePath, item, imageType, imageIndex).ConfigureAwait(false);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using MediaBrowser.Controller.Drawing;
|
using MediaBrowser.Controller.Drawing;
|
||||||
using MediaBrowser.Model.Drawing;
|
using MediaBrowser.Model.Drawing;
|
||||||
|
|
||||||
@@ -6,15 +7,11 @@ namespace Emby.Drawing
|
|||||||
{
|
{
|
||||||
public class NullImageEncoder : IImageEncoder
|
public class NullImageEncoder : IImageEncoder
|
||||||
{
|
{
|
||||||
public string[] SupportedInputFormats =>
|
public IReadOnlyCollection<string> SupportedInputFormats
|
||||||
new[]
|
=> new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "png", "jpeg", "jpg" };
|
||||||
{
|
|
||||||
"png",
|
|
||||||
"jpeg",
|
|
||||||
"jpg"
|
|
||||||
};
|
|
||||||
|
|
||||||
public ImageFormat[] SupportedOutputFormats => new[] { ImageFormat.Jpg, ImageFormat.Png };
|
public IReadOnlyCollection<ImageFormat> SupportedOutputFormats
|
||||||
|
=> new HashSet<ImageFormat>() { ImageFormat.Jpg, ImageFormat.Png };
|
||||||
|
|
||||||
public void CropWhiteSpace(string inputPath, string outputPath)
|
public void CropWhiteSpace(string inputPath, string outputPath)
|
||||||
{
|
{
|
||||||
@@ -37,7 +34,7 @@ namespace Emby.Drawing
|
|||||||
|
|
||||||
public bool SupportsImageEncoding => false;
|
public bool SupportsImageEncoding => false;
|
||||||
|
|
||||||
public ImageSize GetImageSize(string path)
|
public ImageDimensions GetImageSize(string path)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ using System.Runtime.InteropServices;
|
|||||||
[assembly: AssemblyDescription("")]
|
[assembly: AssemblyDescription("")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCompany("Jellyfin Project")]
|
[assembly: AssemblyCompany("Jellyfin Project")]
|
||||||
[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")]
|
[assembly: AssemblyProduct("Jellyfin Server")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")]
|
[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using MediaBrowser.Model.Diagnostics;
|
|||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.System;
|
using MediaBrowser.Model.System;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
|
||||||
|
|
||||||
namespace IsoMounter
|
namespace IsoMounter
|
||||||
{
|
{
|
||||||
@@ -17,9 +18,7 @@ namespace IsoMounter
|
|||||||
|
|
||||||
#region Private Fields
|
#region Private Fields
|
||||||
|
|
||||||
private readonly IEnvironmentInfo EnvironmentInfo;
|
|
||||||
private readonly bool ExecutablesAvailable;
|
private readonly bool ExecutablesAvailable;
|
||||||
private readonly IFileSystem FileSystem;
|
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly string MountCommand;
|
private readonly string MountCommand;
|
||||||
private readonly string MountPointRoot;
|
private readonly string MountPointRoot;
|
||||||
@@ -31,15 +30,12 @@ namespace IsoMounter
|
|||||||
|
|
||||||
#region Constructor(s)
|
#region Constructor(s)
|
||||||
|
|
||||||
public LinuxIsoManager(ILogger logger, IFileSystem fileSystem, IEnvironmentInfo environment, IProcessFactory processFactory)
|
public LinuxIsoManager(ILogger logger, IProcessFactory processFactory)
|
||||||
{
|
{
|
||||||
|
|
||||||
EnvironmentInfo = environment;
|
|
||||||
FileSystem = fileSystem;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
ProcessFactory = processFactory;
|
ProcessFactory = processFactory;
|
||||||
|
|
||||||
MountPointRoot = FileSystem.DirectorySeparatorChar + "tmp" + FileSystem.DirectorySeparatorChar + "Emby";
|
MountPointRoot = Path.DirectorySeparatorChar + "tmp" + Path.DirectorySeparatorChar + "Emby";
|
||||||
|
|
||||||
_logger.LogDebug(
|
_logger.LogDebug(
|
||||||
"[{0}] System PATH is currently set to [{1}].",
|
"[{0}] System PATH is currently set to [{1}].",
|
||||||
@@ -111,7 +107,7 @@ namespace IsoMounter
|
|||||||
public bool CanMount(string path)
|
public bool CanMount(string path)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (EnvironmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Linux)
|
if (OperatingSystem.Id != OperatingSystemId.Linux)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -120,8 +116,8 @@ namespace IsoMounter
|
|||||||
Name,
|
Name,
|
||||||
path,
|
path,
|
||||||
Path.GetExtension(path),
|
Path.GetExtension(path),
|
||||||
EnvironmentInfo.OperatingSystem,
|
OperatingSystem.Name,
|
||||||
ExecutablesAvailable.ToString()
|
ExecutablesAvailable
|
||||||
);
|
);
|
||||||
|
|
||||||
if (ExecutablesAvailable)
|
if (ExecutablesAvailable)
|
||||||
@@ -183,7 +179,7 @@ namespace IsoMounter
|
|||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
"[{0}] Disposing [{1}].",
|
"[{0}] Disposing [{1}].",
|
||||||
Name,
|
Name,
|
||||||
disposing.ToString()
|
disposing
|
||||||
);
|
);
|
||||||
|
|
||||||
if (disposing)
|
if (disposing)
|
||||||
@@ -214,9 +210,9 @@ namespace IsoMounter
|
|||||||
{
|
{
|
||||||
string path = test.Trim();
|
string path = test.Trim();
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(path) && FileSystem.FileExists(path = Path.Combine(path, name)))
|
if (!string.IsNullOrEmpty(path) && File.Exists(path = Path.Combine(path, name)))
|
||||||
{
|
{
|
||||||
return FileSystem.GetFullPath(path);
|
return Path.GetFullPath(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,9 +225,8 @@ namespace IsoMounter
|
|||||||
var uid = getuid();
|
var uid = getuid();
|
||||||
|
|
||||||
_logger.LogDebug(
|
_logger.LogDebug(
|
||||||
"[{0}] Our current UID is [{1}], GetUserId() returned [{2}].",
|
"[{0}] GetUserId() returned [{2}].",
|
||||||
Name,
|
Name,
|
||||||
uid.ToString(),
|
|
||||||
uid
|
uid
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -327,7 +322,7 @@ namespace IsoMounter
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
FileSystem.CreateDirectory(mountPoint);
|
Directory.CreateDirectory(mountPoint);
|
||||||
}
|
}
|
||||||
catch (UnauthorizedAccessException)
|
catch (UnauthorizedAccessException)
|
||||||
{
|
{
|
||||||
@@ -377,7 +372,7 @@ namespace IsoMounter
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
FileSystem.DeleteDirectory(mountPoint, false);
|
Directory.Delete(mountPoint, false);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -455,7 +450,7 @@ namespace IsoMounter
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
FileSystem.DeleteDirectory(mount.MountedPath, false);
|
Directory.Delete(mount.MountedPath, false);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ using System.Runtime.InteropServices;
|
|||||||
[assembly: AssemblyDescription("")]
|
[assembly: AssemblyDescription("")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCompany("Jellyfin Project")]
|
[assembly: AssemblyCompany("Jellyfin Project")]
|
||||||
[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")]
|
[assembly: AssemblyProduct("Jellyfin Server")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")]
|
[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: NeutralResourcesLanguage("en")]
|
[assembly: NeutralResourcesLanguage("en")]
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace Emby.Naming.AudioBook
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var extension = Path.GetExtension(path) ?? string.Empty;
|
var extension = Path.GetExtension(path);
|
||||||
// Check supported extensions
|
// Check supported extensions
|
||||||
if (!_options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
|
if (!_options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ using System.Runtime.InteropServices;
|
|||||||
[assembly: AssemblyDescription("")]
|
[assembly: AssemblyDescription("")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCompany("Jellyfin Project")]
|
[assembly: AssemblyCompany("Jellyfin Project")]
|
||||||
[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")]
|
[assembly: AssemblyProduct("Jellyfin Server")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")]
|
[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: NeutralResourcesLanguage("en")]
|
[assembly: NeutralResourcesLanguage("en")]
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using Emby.Naming.Common;
|
using Emby.Naming.Common;
|
||||||
|
|
||||||
namespace Emby.Naming.TV
|
namespace Emby.Naming.TV
|
||||||
@@ -22,7 +21,9 @@ namespace Emby.Naming.TV
|
|||||||
// There were no failed tests without this block, but to be safe, we can keep it until
|
// There were no failed tests without this block, but to be safe, we can keep it until
|
||||||
// the regex which require file extensions are modified so that they don't need them.
|
// the regex which require file extensions are modified so that they don't need them.
|
||||||
if (IsDirectory)
|
if (IsDirectory)
|
||||||
|
{
|
||||||
path += ".mp4";
|
path += ".mp4";
|
||||||
|
}
|
||||||
|
|
||||||
EpisodePathParserResult result = null;
|
EpisodePathParserResult result = null;
|
||||||
|
|
||||||
@@ -35,6 +36,7 @@ namespace Emby.Naming.TV
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNamed.HasValue)
|
if (isNamed.HasValue)
|
||||||
{
|
{
|
||||||
if (expression.IsNamed != isNamed.Value)
|
if (expression.IsNamed != isNamed.Value)
|
||||||
@@ -42,6 +44,7 @@ namespace Emby.Naming.TV
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isOptimistic.HasValue)
|
if (isOptimistic.HasValue)
|
||||||
{
|
{
|
||||||
if (expression.IsOptimistic != isOptimistic.Value)
|
if (expression.IsOptimistic != isOptimistic.Value)
|
||||||
@@ -191,13 +194,20 @@ namespace Emby.Naming.TV
|
|||||||
|
|
||||||
private void FillAdditional(string path, EpisodePathParserResult info, IEnumerable<EpisodeExpression> expressions)
|
private void FillAdditional(string path, EpisodePathParserResult info, IEnumerable<EpisodeExpression> expressions)
|
||||||
{
|
{
|
||||||
var results = expressions
|
foreach (var i in expressions)
|
||||||
.Where(i => i.IsNamed)
|
|
||||||
.Select(i => Parse(path, i))
|
|
||||||
.Where(i => i.Success);
|
|
||||||
|
|
||||||
foreach (var result in results)
|
|
||||||
{
|
{
|
||||||
|
if (!i.IsNamed)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = Parse(path, i);
|
||||||
|
|
||||||
|
if (!result.Success)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(info.SeriesName))
|
if (string.IsNullOrEmpty(info.SeriesName))
|
||||||
{
|
{
|
||||||
info.SeriesName = result.SeriesName;
|
info.SeriesName = result.SeriesName;
|
||||||
@@ -208,12 +218,10 @@ namespace Emby.Naming.TV
|
|||||||
info.EndingEpsiodeNumber = result.EndingEpsiodeNumber;
|
info.EndingEpsiodeNumber = result.EndingEpsiodeNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(info.SeriesName))
|
if (!string.IsNullOrEmpty(info.SeriesName)
|
||||||
|
&& (!info.EpisodeNumber.HasValue || info.EndingEpsiodeNumber.HasValue))
|
||||||
{
|
{
|
||||||
if (!info.EpisodeNumber.HasValue || info.EndingEpsiodeNumber.HasValue)
|
break;
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,8 +183,7 @@ namespace Emby.Naming.Video
|
|||||||
{
|
{
|
||||||
if (videos.All(i => i.Files.Count == 1 && IsEligibleForMultiVersion(folderName, i.Files[0].Path)))
|
if (videos.All(i => i.Files.Count == 1 && IsEligibleForMultiVersion(folderName, i.Files[0].Path)))
|
||||||
{
|
{
|
||||||
// Enforce the multi-version limit
|
if (HaveSameYear(videos))
|
||||||
if (videos.Count <= 8 && HaveSameYear(videos))
|
|
||||||
{
|
{
|
||||||
var ordered = videos.OrderBy(i => i.Name).ToList();
|
var ordered = videos.OrderBy(i => i.Name).ToList();
|
||||||
|
|
||||||
@@ -200,23 +199,6 @@ namespace Emby.Naming.Video
|
|||||||
}
|
}
|
||||||
|
|
||||||
return videos;
|
return videos;
|
||||||
//foreach (var video in videos.OrderBy(i => i.Name))
|
|
||||||
//{
|
|
||||||
// var match = list
|
|
||||||
// .FirstOrDefault(i => string.Equals(i.Name, video.Name, StringComparison.OrdinalIgnoreCase));
|
|
||||||
|
|
||||||
// if (match != null && video.Files.Count == 1 && match.Files.Count == 1)
|
|
||||||
// {
|
|
||||||
// match.AlternateVersions.Add(video.Files[0]);
|
|
||||||
// match.Extras.AddRange(video.Extras);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// list.Add(video);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
//return list;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool HaveSameYear(List<VideoInfo> videos)
|
private bool HaveSameYear(List<VideoInfo> videos)
|
||||||
@@ -226,17 +208,14 @@ namespace Emby.Naming.Video
|
|||||||
|
|
||||||
private bool IsEligibleForMultiVersion(string folderName, string testFilename)
|
private bool IsEligibleForMultiVersion(string folderName, string testFilename)
|
||||||
{
|
{
|
||||||
testFilename = Path.GetFileNameWithoutExtension(testFilename);
|
testFilename = Path.GetFileNameWithoutExtension(testFilename) ?? string.Empty;
|
||||||
|
|
||||||
if (string.Equals(folderName, testFilename, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (testFilename.StartsWith(folderName, StringComparison.OrdinalIgnoreCase))
|
if (testFilename.StartsWith(folderName, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
testFilename = testFilename.Substring(folderName.Length).Trim();
|
testFilename = testFilename.Substring(folderName.Length).Trim();
|
||||||
return testFilename.StartsWith("-", StringComparison.OrdinalIgnoreCase) || Regex.Replace(testFilename, @"\[([^]]*)\]", "").Trim() == string.Empty;
|
return string.IsNullOrEmpty(testFilename) ||
|
||||||
|
testFilename.StartsWith("-") ||
|
||||||
|
string.IsNullOrWhiteSpace(Regex.Replace(testFilename, @"\[([^]]*)\]", string.Empty)) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -11,111 +11,81 @@ namespace Emby.Notifications
|
|||||||
public class CoreNotificationTypes : INotificationTypeFactory
|
public class CoreNotificationTypes : INotificationTypeFactory
|
||||||
{
|
{
|
||||||
private readonly ILocalizationManager _localization;
|
private readonly ILocalizationManager _localization;
|
||||||
private readonly IServerApplicationHost _appHost;
|
|
||||||
|
|
||||||
public CoreNotificationTypes(ILocalizationManager localization, IServerApplicationHost appHost)
|
public CoreNotificationTypes(ILocalizationManager localization)
|
||||||
{
|
{
|
||||||
_localization = localization;
|
_localization = localization;
|
||||||
_appHost = appHost;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<NotificationTypeInfo> GetNotificationTypes()
|
public IEnumerable<NotificationTypeInfo> GetNotificationTypes()
|
||||||
{
|
{
|
||||||
var knownTypes = new List<NotificationTypeInfo>
|
var knownTypes = new NotificationTypeInfo[]
|
||||||
{
|
{
|
||||||
new NotificationTypeInfo
|
new NotificationTypeInfo
|
||||||
{
|
{
|
||||||
Type = NotificationType.ApplicationUpdateInstalled.ToString()
|
Type = NotificationType.ApplicationUpdateInstalled.ToString()
|
||||||
},
|
},
|
||||||
|
|
||||||
new NotificationTypeInfo
|
new NotificationTypeInfo
|
||||||
{
|
{
|
||||||
Type = NotificationType.InstallationFailed.ToString()
|
Type = NotificationType.InstallationFailed.ToString()
|
||||||
},
|
},
|
||||||
|
|
||||||
new NotificationTypeInfo
|
new NotificationTypeInfo
|
||||||
{
|
{
|
||||||
Type = NotificationType.PluginInstalled.ToString()
|
Type = NotificationType.PluginInstalled.ToString()
|
||||||
},
|
},
|
||||||
|
|
||||||
new NotificationTypeInfo
|
new NotificationTypeInfo
|
||||||
{
|
{
|
||||||
Type = NotificationType.PluginError.ToString()
|
Type = NotificationType.PluginError.ToString()
|
||||||
},
|
},
|
||||||
|
|
||||||
new NotificationTypeInfo
|
new NotificationTypeInfo
|
||||||
{
|
{
|
||||||
Type = NotificationType.PluginUninstalled.ToString()
|
Type = NotificationType.PluginUninstalled.ToString()
|
||||||
},
|
},
|
||||||
|
|
||||||
new NotificationTypeInfo
|
new NotificationTypeInfo
|
||||||
{
|
{
|
||||||
Type = NotificationType.PluginUpdateInstalled.ToString()
|
Type = NotificationType.PluginUpdateInstalled.ToString()
|
||||||
},
|
},
|
||||||
|
|
||||||
new NotificationTypeInfo
|
new NotificationTypeInfo
|
||||||
{
|
{
|
||||||
Type = NotificationType.ServerRestartRequired.ToString()
|
Type = NotificationType.ServerRestartRequired.ToString()
|
||||||
},
|
},
|
||||||
|
|
||||||
new NotificationTypeInfo
|
new NotificationTypeInfo
|
||||||
{
|
{
|
||||||
Type = NotificationType.TaskFailed.ToString()
|
Type = NotificationType.TaskFailed.ToString()
|
||||||
},
|
},
|
||||||
|
|
||||||
new NotificationTypeInfo
|
new NotificationTypeInfo
|
||||||
{
|
{
|
||||||
Type = NotificationType.NewLibraryContent.ToString()
|
Type = NotificationType.NewLibraryContent.ToString()
|
||||||
},
|
},
|
||||||
|
|
||||||
new NotificationTypeInfo
|
new NotificationTypeInfo
|
||||||
{
|
{
|
||||||
Type = NotificationType.AudioPlayback.ToString()
|
Type = NotificationType.AudioPlayback.ToString()
|
||||||
},
|
},
|
||||||
|
|
||||||
new NotificationTypeInfo
|
|
||||||
{
|
|
||||||
Type = NotificationType.GamePlayback.ToString()
|
|
||||||
},
|
|
||||||
|
|
||||||
new NotificationTypeInfo
|
new NotificationTypeInfo
|
||||||
{
|
{
|
||||||
Type = NotificationType.VideoPlayback.ToString()
|
Type = NotificationType.VideoPlayback.ToString()
|
||||||
},
|
},
|
||||||
|
|
||||||
new NotificationTypeInfo
|
new NotificationTypeInfo
|
||||||
{
|
{
|
||||||
Type = NotificationType.AudioPlaybackStopped.ToString()
|
Type = NotificationType.AudioPlaybackStopped.ToString()
|
||||||
},
|
},
|
||||||
|
|
||||||
new NotificationTypeInfo
|
|
||||||
{
|
|
||||||
Type = NotificationType.GamePlaybackStopped.ToString()
|
|
||||||
},
|
|
||||||
|
|
||||||
new NotificationTypeInfo
|
new NotificationTypeInfo
|
||||||
{
|
{
|
||||||
Type = NotificationType.VideoPlaybackStopped.ToString()
|
Type = NotificationType.VideoPlaybackStopped.ToString()
|
||||||
},
|
},
|
||||||
|
|
||||||
new NotificationTypeInfo
|
new NotificationTypeInfo
|
||||||
{
|
{
|
||||||
Type = NotificationType.CameraImageUploaded.ToString()
|
Type = NotificationType.CameraImageUploaded.ToString()
|
||||||
},
|
},
|
||||||
|
|
||||||
new NotificationTypeInfo
|
new NotificationTypeInfo
|
||||||
{
|
{
|
||||||
Type = NotificationType.UserLockedOut.ToString()
|
Type = NotificationType.UserLockedOut.ToString()
|
||||||
}
|
},
|
||||||
};
|
new NotificationTypeInfo
|
||||||
|
|
||||||
if (!_appHost.CanSelfUpdate)
|
|
||||||
{
|
|
||||||
knownTypes.Add(new NotificationTypeInfo
|
|
||||||
{
|
{
|
||||||
Type = NotificationType.ApplicationUpdateAvailable.ToString()
|
Type = NotificationType.ApplicationUpdateAvailable.ToString()
|
||||||
});
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
foreach (var type in knownTypes)
|
foreach (var type in knownTypes)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,22 +5,17 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Common.Updates;
|
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Controller.Devices;
|
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Audio;
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
using MediaBrowser.Controller.Entities.TV;
|
using MediaBrowser.Controller.Entities.TV;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Notifications;
|
using MediaBrowser.Controller.Notifications;
|
||||||
using MediaBrowser.Controller.Plugins;
|
using MediaBrowser.Controller.Plugins;
|
||||||
using MediaBrowser.Controller.Session;
|
|
||||||
using MediaBrowser.Model.Activity;
|
using MediaBrowser.Model.Activity;
|
||||||
using MediaBrowser.Model.Events;
|
using MediaBrowser.Model.Events;
|
||||||
using MediaBrowser.Model.Globalization;
|
using MediaBrowser.Model.Globalization;
|
||||||
using MediaBrowser.Model.Notifications;
|
using MediaBrowser.Model.Notifications;
|
||||||
using MediaBrowser.Model.Tasks;
|
|
||||||
using MediaBrowser.Model.Threading;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Notifications
|
namespace Emby.Notifications
|
||||||
@@ -30,53 +25,50 @@ namespace Emby.Notifications
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class Notifications : IServerEntryPoint
|
public class Notifications : IServerEntryPoint
|
||||||
{
|
{
|
||||||
private readonly IInstallationManager _installationManager;
|
|
||||||
private readonly IUserManager _userManager;
|
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
private readonly ITaskManager _taskManager;
|
|
||||||
private readonly INotificationManager _notificationManager;
|
private readonly INotificationManager _notificationManager;
|
||||||
|
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
private readonly ISessionManager _sessionManager;
|
|
||||||
private readonly IServerApplicationHost _appHost;
|
private readonly IServerApplicationHost _appHost;
|
||||||
private readonly ITimerFactory _timerFactory;
|
|
||||||
|
|
||||||
private ITimer LibraryUpdateTimer { get; set; }
|
private Timer LibraryUpdateTimer { get; set; }
|
||||||
private readonly object _libraryChangedSyncLock = new object();
|
private readonly object _libraryChangedSyncLock = new object();
|
||||||
|
|
||||||
private readonly IConfigurationManager _config;
|
private readonly IConfigurationManager _config;
|
||||||
private readonly IDeviceManager _deviceManager;
|
|
||||||
private readonly ILocalizationManager _localization;
|
private readonly ILocalizationManager _localization;
|
||||||
private readonly IActivityManager _activityManager;
|
private readonly IActivityManager _activityManager;
|
||||||
|
|
||||||
private string[] _coreNotificationTypes;
|
private string[] _coreNotificationTypes;
|
||||||
|
|
||||||
public Notifications(IInstallationManager installationManager, IActivityManager activityManager, ILocalizationManager localization, IUserManager userManager, ILogger logger, ITaskManager taskManager, INotificationManager notificationManager, ILibraryManager libraryManager, ISessionManager sessionManager, IServerApplicationHost appHost, IConfigurationManager config, IDeviceManager deviceManager, ITimerFactory timerFactory)
|
public Notifications(
|
||||||
|
IActivityManager activityManager,
|
||||||
|
ILocalizationManager localization,
|
||||||
|
ILogger logger,
|
||||||
|
INotificationManager notificationManager,
|
||||||
|
ILibraryManager libraryManager,
|
||||||
|
IServerApplicationHost appHost,
|
||||||
|
IConfigurationManager config)
|
||||||
{
|
{
|
||||||
_installationManager = installationManager;
|
|
||||||
_userManager = userManager;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_taskManager = taskManager;
|
|
||||||
_notificationManager = notificationManager;
|
_notificationManager = notificationManager;
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
_sessionManager = sessionManager;
|
|
||||||
_appHost = appHost;
|
_appHost = appHost;
|
||||||
_config = config;
|
_config = config;
|
||||||
_deviceManager = deviceManager;
|
|
||||||
_timerFactory = timerFactory;
|
|
||||||
_localization = localization;
|
_localization = localization;
|
||||||
_activityManager = activityManager;
|
_activityManager = activityManager;
|
||||||
|
|
||||||
_coreNotificationTypes = new CoreNotificationTypes(localization, appHost).GetNotificationTypes().Select(i => i.Type).ToArray();
|
_coreNotificationTypes = new CoreNotificationTypes(localization).GetNotificationTypes().Select(i => i.Type).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Run()
|
public Task RunAsync()
|
||||||
{
|
{
|
||||||
_libraryManager.ItemAdded += _libraryManager_ItemAdded;
|
_libraryManager.ItemAdded += _libraryManager_ItemAdded;
|
||||||
_appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged;
|
_appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged;
|
||||||
_appHost.HasUpdateAvailableChanged += _appHost_HasUpdateAvailableChanged;
|
_appHost.HasUpdateAvailableChanged += _appHost_HasUpdateAvailableChanged;
|
||||||
_activityManager.EntryCreated += _activityManager_EntryCreated;
|
_activityManager.EntryCreated += _activityManager_EntryCreated;
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void _appHost_HasPendingRestartChanged(object sender, EventArgs e)
|
private async void _appHost_HasPendingRestartChanged(object sender, EventArgs e)
|
||||||
@@ -125,10 +117,9 @@ namespace Emby.Notifications
|
|||||||
return _config.GetConfiguration<NotificationOptions>("notifications");
|
return _config.GetConfiguration<NotificationOptions>("notifications");
|
||||||
}
|
}
|
||||||
|
|
||||||
async void _appHost_HasUpdateAvailableChanged(object sender, EventArgs e)
|
private async void _appHost_HasUpdateAvailableChanged(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
// This notification is for users who can't auto-update (aka running as service)
|
if (!_appHost.HasUpdateAvailable)
|
||||||
if (!_appHost.HasUpdateAvailable || _appHost.CanSelfUpdate)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -146,7 +137,7 @@ namespace Emby.Notifications
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<BaseItem> _itemsAdded = new List<BaseItem>();
|
private readonly List<BaseItem> _itemsAdded = new List<BaseItem>();
|
||||||
void _libraryManager_ItemAdded(object sender, ItemChangeEventArgs e)
|
private void _libraryManager_ItemAdded(object sender, ItemChangeEventArgs e)
|
||||||
{
|
{
|
||||||
if (!FilterItem(e.Item))
|
if (!FilterItem(e.Item))
|
||||||
{
|
{
|
||||||
@@ -157,7 +148,7 @@ namespace Emby.Notifications
|
|||||||
{
|
{
|
||||||
if (LibraryUpdateTimer == null)
|
if (LibraryUpdateTimer == null)
|
||||||
{
|
{
|
||||||
LibraryUpdateTimer = _timerFactory.Create(LibraryUpdateTimerCallback, null, 5000,
|
LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, 5000,
|
||||||
Timeout.Infinite);
|
Timeout.Infinite);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ using System.Runtime.InteropServices;
|
|||||||
[assembly: AssemblyDescription("")]
|
[assembly: AssemblyDescription("")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCompany("Jellyfin Project")]
|
[assembly: AssemblyCompany("Jellyfin Project")]
|
||||||
[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")]
|
[assembly: AssemblyProduct("Jellyfin Server")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")]
|
[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: NeutralResourcesLanguage("en")]
|
[assembly: NeutralResourcesLanguage("en")]
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ using MediaBrowser.Controller.Library;
|
|||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Drawing;
|
using MediaBrowser.Model.Drawing;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.IO;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using TagLib;
|
using TagLib;
|
||||||
using TagLib.IFD;
|
using TagLib.IFD;
|
||||||
@@ -21,13 +20,11 @@ namespace Emby.Photos
|
|||||||
public class PhotoProvider : ICustomMetadataProvider<Photo>, IForcedProvider, IHasItemChangeMonitor
|
public class PhotoProvider : ICustomMetadataProvider<Photo>, IForcedProvider, IHasItemChangeMonitor
|
||||||
{
|
{
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IFileSystem _fileSystem;
|
|
||||||
private IImageProcessor _imageProcessor;
|
private IImageProcessor _imageProcessor;
|
||||||
|
|
||||||
public PhotoProvider(ILogger logger, IFileSystem fileSystem, IImageProcessor imageProcessor)
|
public PhotoProvider(ILogger logger, IImageProcessor imageProcessor)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_fileSystem = fileSystem;
|
|
||||||
_imageProcessor = imageProcessor;
|
_imageProcessor = imageProcessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,12 +178,12 @@ namespace Emby.Photos
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var size = _imageProcessor.GetImageSize(item, img, false);
|
var size = _imageProcessor.GetImageDimensions(item, img, false);
|
||||||
|
|
||||||
if (size.Width > 0 && size.Height > 0)
|
if (size.Width > 0 && size.Height > 0)
|
||||||
{
|
{
|
||||||
item.Width = Convert.ToInt32(size.Width);
|
item.Width = size.Width;
|
||||||
item.Height = Convert.ToInt32(size.Height);
|
item.Height = size.Height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ArgumentException)
|
catch (ArgumentException)
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ using System.Runtime.InteropServices;
|
|||||||
[assembly: AssemblyDescription("")]
|
[assembly: AssemblyDescription("")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCompany("Jellyfin Project")]
|
[assembly: AssemblyCompany("Jellyfin Project")]
|
||||||
[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")]
|
[assembly: AssemblyProduct("Jellyfin Server")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")]
|
[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
[assembly: NeutralResourcesLanguage("en")]
|
[assembly: NeutralResourcesLanguage("en")]
|
||||||
|
|||||||
@@ -30,13 +30,10 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
public class ActivityLogEntryPoint : IServerEntryPoint
|
public class ActivityLogEntryPoint : IServerEntryPoint
|
||||||
{
|
{
|
||||||
private readonly IInstallationManager _installationManager;
|
private readonly IInstallationManager _installationManager;
|
||||||
|
|
||||||
//private readonly ILogger _logger;
|
|
||||||
private readonly ISessionManager _sessionManager;
|
private readonly ISessionManager _sessionManager;
|
||||||
private readonly ITaskManager _taskManager;
|
private readonly ITaskManager _taskManager;
|
||||||
private readonly IActivityManager _activityManager;
|
private readonly IActivityManager _activityManager;
|
||||||
private readonly ILocalizationManager _localization;
|
private readonly ILocalizationManager _localization;
|
||||||
|
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
private readonly ISubtitleManager _subManager;
|
private readonly ISubtitleManager _subManager;
|
||||||
private readonly IUserManager _userManager;
|
private readonly IUserManager _userManager;
|
||||||
@@ -59,52 +56,50 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
_deviceManager = deviceManager;
|
_deviceManager = deviceManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Run()
|
public Task RunAsync()
|
||||||
{
|
{
|
||||||
_taskManager.TaskCompleted += _taskManager_TaskCompleted;
|
_taskManager.TaskCompleted += OnTaskCompleted;
|
||||||
|
|
||||||
_installationManager.PluginInstalled += _installationManager_PluginInstalled;
|
_installationManager.PluginInstalled += OnPluginInstalled;
|
||||||
_installationManager.PluginUninstalled += _installationManager_PluginUninstalled;
|
_installationManager.PluginUninstalled += OnPluginUninstalled;
|
||||||
_installationManager.PluginUpdated += _installationManager_PluginUpdated;
|
_installationManager.PluginUpdated += OnPluginUpdated;
|
||||||
_installationManager.PackageInstallationFailed += _installationManager_PackageInstallationFailed;
|
_installationManager.PackageInstallationFailed += OnPackageInstallationFailed;
|
||||||
|
|
||||||
_sessionManager.SessionStarted += _sessionManager_SessionStarted;
|
_sessionManager.SessionStarted += OnSessionStarted;
|
||||||
_sessionManager.AuthenticationFailed += _sessionManager_AuthenticationFailed;
|
_sessionManager.AuthenticationFailed += OnAuthenticationFailed;
|
||||||
_sessionManager.AuthenticationSucceeded += _sessionManager_AuthenticationSucceeded;
|
_sessionManager.AuthenticationSucceeded += OnAuthenticationSucceeded;
|
||||||
_sessionManager.SessionEnded += _sessionManager_SessionEnded;
|
_sessionManager.SessionEnded += OnSessionEnded;
|
||||||
|
|
||||||
_sessionManager.PlaybackStart += _sessionManager_PlaybackStart;
|
_sessionManager.PlaybackStart += OnPlaybackStart;
|
||||||
_sessionManager.PlaybackStopped += _sessionManager_PlaybackStopped;
|
_sessionManager.PlaybackStopped += OnPlaybackStopped;
|
||||||
|
|
||||||
//_subManager.SubtitlesDownloaded += _subManager_SubtitlesDownloaded;
|
_subManager.SubtitleDownloadFailure += OnSubtitleDownloadFailure;
|
||||||
_subManager.SubtitleDownloadFailure += _subManager_SubtitleDownloadFailure;
|
|
||||||
|
|
||||||
_userManager.UserCreated += _userManager_UserCreated;
|
_userManager.UserCreated += OnUserCreated;
|
||||||
_userManager.UserPasswordChanged += _userManager_UserPasswordChanged;
|
_userManager.UserPasswordChanged += OnUserPasswordChanged;
|
||||||
_userManager.UserDeleted += _userManager_UserDeleted;
|
_userManager.UserDeleted += OnUserDeleted;
|
||||||
_userManager.UserPolicyUpdated += _userManager_UserPolicyUpdated;
|
_userManager.UserPolicyUpdated += OnUserPolicyUpdated;
|
||||||
_userManager.UserLockedOut += _userManager_UserLockedOut;
|
_userManager.UserLockedOut += OnUserLockedOut;
|
||||||
|
|
||||||
//_config.ConfigurationUpdated += _config_ConfigurationUpdated;
|
_deviceManager.CameraImageUploaded += OnCameraImageUploaded;
|
||||||
//_config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
|
|
||||||
|
|
||||||
_deviceManager.CameraImageUploaded += _deviceManager_CameraImageUploaded;
|
_appHost.ApplicationUpdated += OnApplicationUpdated;
|
||||||
|
|
||||||
_appHost.ApplicationUpdated += _appHost_ApplicationUpdated;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
async void _deviceManager_CameraImageUploaded(object sender, GenericEventArgs<CameraImageUploadInfo> e)
|
private void OnCameraImageUploaded(object sender, GenericEventArgs<CameraImageUploadInfo> e)
|
||||||
{
|
{
|
||||||
await CreateLogEntry(new ActivityLogEntry
|
CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("CameraImageUploadedFrom"), e.Argument.Device.Name),
|
Name = string.Format(_localization.GetLocalizedString("CameraImageUploadedFrom"), e.Argument.Device.Name),
|
||||||
Type = NotificationType.CameraImageUploaded.ToString()
|
Type = NotificationType.CameraImageUploaded.ToString()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async void _userManager_UserLockedOut(object sender, GenericEventArgs<User> e)
|
private void OnUserLockedOut(object sender, GenericEventArgs<User> e)
|
||||||
{
|
{
|
||||||
await CreateLogEntry(new ActivityLogEntry
|
CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("UserLockedOutWithName"), e.Argument.Name),
|
Name = string.Format(_localization.GetLocalizedString("UserLockedOutWithName"), e.Argument.Name),
|
||||||
Type = NotificationType.UserLockedOut.ToString(),
|
Type = NotificationType.UserLockedOut.ToString(),
|
||||||
@@ -112,9 +107,9 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async void _subManager_SubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e)
|
private void OnSubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e)
|
||||||
{
|
{
|
||||||
await CreateLogEntry(new ActivityLogEntry
|
CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"), e.Provider, Notifications.Notifications.GetItemName(e.Item)),
|
Name = string.Format(_localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"), e.Provider, Notifications.Notifications.GetItemName(e.Item)),
|
||||||
Type = "SubtitleDownloadFailure",
|
Type = "SubtitleDownloadFailure",
|
||||||
@@ -123,7 +118,7 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async void _sessionManager_PlaybackStopped(object sender, PlaybackStopEventArgs e)
|
private void OnPlaybackStopped(object sender, PlaybackStopEventArgs e)
|
||||||
{
|
{
|
||||||
var item = e.MediaInfo;
|
var item = e.MediaInfo;
|
||||||
|
|
||||||
@@ -144,9 +139,9 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var user = e.Users.First();
|
var user = e.Users[0];
|
||||||
|
|
||||||
await CreateLogEntry(new ActivityLogEntry
|
CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("UserStoppedPlayingItemWithValues"), user.Name, GetItemName(item), e.DeviceName),
|
Name = string.Format(_localization.GetLocalizedString("UserStoppedPlayingItemWithValues"), user.Name, GetItemName(item), e.DeviceName),
|
||||||
Type = GetPlaybackStoppedNotificationType(item.MediaType),
|
Type = GetPlaybackStoppedNotificationType(item.MediaType),
|
||||||
@@ -154,7 +149,7 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async void _sessionManager_PlaybackStart(object sender, PlaybackProgressEventArgs e)
|
private void OnPlaybackStart(object sender, PlaybackProgressEventArgs e)
|
||||||
{
|
{
|
||||||
var item = e.MediaInfo;
|
var item = e.MediaInfo;
|
||||||
|
|
||||||
@@ -177,7 +172,7 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
|
|
||||||
var user = e.Users.First();
|
var user = e.Users.First();
|
||||||
|
|
||||||
await CreateLogEntry(new ActivityLogEntry
|
CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("UserStartedPlayingItemWithValues"), user.Name, GetItemName(item), e.DeviceName),
|
Name = string.Format(_localization.GetLocalizedString("UserStartedPlayingItemWithValues"), user.Name, GetItemName(item), e.DeviceName),
|
||||||
Type = GetPlaybackNotificationType(item.MediaType),
|
Type = GetPlaybackNotificationType(item.MediaType),
|
||||||
@@ -208,10 +203,6 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
{
|
{
|
||||||
return NotificationType.AudioPlayback.ToString();
|
return NotificationType.AudioPlayback.ToString();
|
||||||
}
|
}
|
||||||
if (string.Equals(mediaType, MediaType.Game, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return NotificationType.GamePlayback.ToString();
|
|
||||||
}
|
|
||||||
if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return NotificationType.VideoPlayback.ToString();
|
return NotificationType.VideoPlayback.ToString();
|
||||||
@@ -226,10 +217,6 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
{
|
{
|
||||||
return NotificationType.AudioPlaybackStopped.ToString();
|
return NotificationType.AudioPlaybackStopped.ToString();
|
||||||
}
|
}
|
||||||
if (string.Equals(mediaType, MediaType.Game, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return NotificationType.GamePlaybackStopped.ToString();
|
|
||||||
}
|
|
||||||
if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return NotificationType.VideoPlaybackStopped.ToString();
|
return NotificationType.VideoPlaybackStopped.ToString();
|
||||||
@@ -238,7 +225,7 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async void _sessionManager_SessionEnded(object sender, SessionEventArgs e)
|
private void OnSessionEnded(object sender, SessionEventArgs e)
|
||||||
{
|
{
|
||||||
string name;
|
string name;
|
||||||
var session = e.SessionInfo;
|
var session = e.SessionInfo;
|
||||||
@@ -255,7 +242,7 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
name = string.Format(_localization.GetLocalizedString("UserOfflineFromDevice"), session.UserName, session.DeviceName);
|
name = string.Format(_localization.GetLocalizedString("UserOfflineFromDevice"), session.UserName, session.DeviceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
await CreateLogEntry(new ActivityLogEntry
|
CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = name,
|
Name = name,
|
||||||
Type = "SessionEnded",
|
Type = "SessionEnded",
|
||||||
@@ -264,11 +251,11 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async void _sessionManager_AuthenticationSucceeded(object sender, GenericEventArgs<AuthenticationResult> e)
|
private void OnAuthenticationSucceeded(object sender, GenericEventArgs<AuthenticationResult> e)
|
||||||
{
|
{
|
||||||
var user = e.Argument.User;
|
var user = e.Argument.User;
|
||||||
|
|
||||||
await CreateLogEntry(new ActivityLogEntry
|
CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("AuthenticationSucceededWithUserName"), user.Name),
|
Name = string.Format(_localization.GetLocalizedString("AuthenticationSucceededWithUserName"), user.Name),
|
||||||
Type = "AuthenticationSucceeded",
|
Type = "AuthenticationSucceeded",
|
||||||
@@ -277,9 +264,9 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async void _sessionManager_AuthenticationFailed(object sender, GenericEventArgs<AuthenticationRequest> e)
|
private void OnAuthenticationFailed(object sender, GenericEventArgs<AuthenticationRequest> e)
|
||||||
{
|
{
|
||||||
await CreateLogEntry(new ActivityLogEntry
|
CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("FailedLoginAttemptWithUserName"), e.Argument.Username),
|
Name = string.Format(_localization.GetLocalizedString("FailedLoginAttemptWithUserName"), e.Argument.Username),
|
||||||
Type = "AuthenticationFailed",
|
Type = "AuthenticationFailed",
|
||||||
@@ -288,9 +275,9 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async void _appHost_ApplicationUpdated(object sender, GenericEventArgs<PackageVersionInfo> e)
|
private void OnApplicationUpdated(object sender, GenericEventArgs<PackageVersionInfo> e)
|
||||||
{
|
{
|
||||||
await CreateLogEntry(new ActivityLogEntry
|
CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("MessageApplicationUpdatedTo"), e.Argument.versionStr),
|
Name = string.Format(_localization.GetLocalizedString("MessageApplicationUpdatedTo"), e.Argument.versionStr),
|
||||||
Type = NotificationType.ApplicationUpdateInstalled.ToString(),
|
Type = NotificationType.ApplicationUpdateInstalled.ToString(),
|
||||||
@@ -298,27 +285,9 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
|
private void OnUserPolicyUpdated(object sender, GenericEventArgs<User> e)
|
||||||
{
|
{
|
||||||
await CreateLogEntry(new ActivityLogEntry
|
CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
|
||||||
Name = string.Format(_localization.GetLocalizedString("MessageNamedServerConfigurationUpdatedWithValue"), e.Key),
|
|
||||||
Type = "NamedConfigurationUpdated"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async void _config_ConfigurationUpdated(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
await CreateLogEntry(new ActivityLogEntry
|
|
||||||
{
|
|
||||||
Name = _localization.GetLocalizedString("MessageServerConfigurationUpdated"),
|
|
||||||
Type = "ServerConfigurationUpdated"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async void _userManager_UserPolicyUpdated(object sender, GenericEventArgs<User> e)
|
|
||||||
{
|
|
||||||
await CreateLogEntry(new ActivityLogEntry
|
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("UserPolicyUpdatedWithName"), e.Argument.Name),
|
Name = string.Format(_localization.GetLocalizedString("UserPolicyUpdatedWithName"), e.Argument.Name),
|
||||||
Type = "UserPolicyUpdated",
|
Type = "UserPolicyUpdated",
|
||||||
@@ -326,18 +295,18 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async void _userManager_UserDeleted(object sender, GenericEventArgs<User> e)
|
private void OnUserDeleted(object sender, GenericEventArgs<User> e)
|
||||||
{
|
{
|
||||||
await CreateLogEntry(new ActivityLogEntry
|
CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("UserDeletedWithName"), e.Argument.Name),
|
Name = string.Format(_localization.GetLocalizedString("UserDeletedWithName"), e.Argument.Name),
|
||||||
Type = "UserDeleted"
|
Type = "UserDeleted"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async void _userManager_UserPasswordChanged(object sender, GenericEventArgs<User> e)
|
private void OnUserPasswordChanged(object sender, GenericEventArgs<User> e)
|
||||||
{
|
{
|
||||||
await CreateLogEntry(new ActivityLogEntry
|
CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("UserPasswordChangedWithName"), e.Argument.Name),
|
Name = string.Format(_localization.GetLocalizedString("UserPasswordChangedWithName"), e.Argument.Name),
|
||||||
Type = "UserPasswordChanged",
|
Type = "UserPasswordChanged",
|
||||||
@@ -345,9 +314,9 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async void _userManager_UserCreated(object sender, GenericEventArgs<User> e)
|
private void OnUserCreated(object sender, GenericEventArgs<User> e)
|
||||||
{
|
{
|
||||||
await CreateLogEntry(new ActivityLogEntry
|
CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("UserCreatedWithName"), e.Argument.Name),
|
Name = string.Format(_localization.GetLocalizedString("UserCreatedWithName"), e.Argument.Name),
|
||||||
Type = "UserCreated",
|
Type = "UserCreated",
|
||||||
@@ -355,18 +324,7 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async void _subManager_SubtitlesDownloaded(object sender, SubtitleDownloadEventArgs e)
|
private void OnSessionStarted(object sender, SessionEventArgs e)
|
||||||
{
|
|
||||||
await CreateLogEntry(new ActivityLogEntry
|
|
||||||
{
|
|
||||||
Name = string.Format(_localization.GetLocalizedString("SubtitlesDownloadedForItem"), Notifications.Notifications.GetItemName(e.Item)),
|
|
||||||
Type = "SubtitlesDownloaded",
|
|
||||||
ItemId = e.Item.Id.ToString("N"),
|
|
||||||
ShortOverview = string.Format(_localization.GetLocalizedString("ProviderValue"), e.Provider)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async void _sessionManager_SessionStarted(object sender, SessionEventArgs e)
|
|
||||||
{
|
{
|
||||||
string name;
|
string name;
|
||||||
var session = e.SessionInfo;
|
var session = e.SessionInfo;
|
||||||
@@ -383,7 +341,7 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
name = string.Format(_localization.GetLocalizedString("UserOnlineFromDevice"), session.UserName, session.DeviceName);
|
name = string.Format(_localization.GetLocalizedString("UserOnlineFromDevice"), session.UserName, session.DeviceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
await CreateLogEntry(new ActivityLogEntry
|
CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = name,
|
Name = name,
|
||||||
Type = "SessionStarted",
|
Type = "SessionStarted",
|
||||||
@@ -392,9 +350,9 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async void _installationManager_PluginUpdated(object sender, GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>> e)
|
private void OnPluginUpdated(object sender, GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>> e)
|
||||||
{
|
{
|
||||||
await CreateLogEntry(new ActivityLogEntry
|
CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("PluginUpdatedWithName"), e.Argument.Item1.Name),
|
Name = string.Format(_localization.GetLocalizedString("PluginUpdatedWithName"), e.Argument.Item1.Name),
|
||||||
Type = NotificationType.PluginUpdateInstalled.ToString(),
|
Type = NotificationType.PluginUpdateInstalled.ToString(),
|
||||||
@@ -403,18 +361,18 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async void _installationManager_PluginUninstalled(object sender, GenericEventArgs<IPlugin> e)
|
private void OnPluginUninstalled(object sender, GenericEventArgs<IPlugin> e)
|
||||||
{
|
{
|
||||||
await CreateLogEntry(new ActivityLogEntry
|
CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("PluginUninstalledWithName"), e.Argument.Name),
|
Name = string.Format(_localization.GetLocalizedString("PluginUninstalledWithName"), e.Argument.Name),
|
||||||
Type = NotificationType.PluginUninstalled.ToString()
|
Type = NotificationType.PluginUninstalled.ToString()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async void _installationManager_PluginInstalled(object sender, GenericEventArgs<PackageVersionInfo> e)
|
private void OnPluginInstalled(object sender, GenericEventArgs<PackageVersionInfo> e)
|
||||||
{
|
{
|
||||||
await CreateLogEntry(new ActivityLogEntry
|
CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("PluginInstalledWithName"), e.Argument.name),
|
Name = string.Format(_localization.GetLocalizedString("PluginInstalledWithName"), e.Argument.name),
|
||||||
Type = NotificationType.PluginInstalled.ToString(),
|
Type = NotificationType.PluginInstalled.ToString(),
|
||||||
@@ -422,11 +380,11 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async void _installationManager_PackageInstallationFailed(object sender, InstallationFailedEventArgs e)
|
private void OnPackageInstallationFailed(object sender, InstallationFailedEventArgs e)
|
||||||
{
|
{
|
||||||
var installationInfo = e.InstallationInfo;
|
var installationInfo = e.InstallationInfo;
|
||||||
|
|
||||||
await CreateLogEntry(new ActivityLogEntry
|
CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("NameInstallFailed"), installationInfo.Name),
|
Name = string.Format(_localization.GetLocalizedString("NameInstallFailed"), installationInfo.Name),
|
||||||
Type = NotificationType.InstallationFailed.ToString(),
|
Type = NotificationType.InstallationFailed.ToString(),
|
||||||
@@ -435,7 +393,7 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async void _taskManager_TaskCompleted(object sender, TaskCompletionEventArgs e)
|
private void OnTaskCompleted(object sender, TaskCompletionEventArgs e)
|
||||||
{
|
{
|
||||||
var result = e.Result;
|
var result = e.Result;
|
||||||
var task = e.Task;
|
var task = e.Task;
|
||||||
@@ -462,7 +420,7 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
vals.Add(e.Result.LongErrorMessage);
|
vals.Add(e.Result.LongErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
await CreateLogEntry(new ActivityLogEntry
|
CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("ScheduledTaskFailedWithName"), task.Name),
|
Name = string.Format(_localization.GetLocalizedString("ScheduledTaskFailedWithName"), task.Name),
|
||||||
Type = NotificationType.TaskFailed.ToString(),
|
Type = NotificationType.TaskFailed.ToString(),
|
||||||
@@ -473,50 +431,37 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CreateLogEntry(ActivityLogEntry entry)
|
private void CreateLogEntry(ActivityLogEntry entry)
|
||||||
{
|
=> _activityManager.Create(entry);
|
||||||
try
|
|
||||||
{
|
|
||||||
await _activityManager.CreateAsync(entry);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// Logged at lower levels
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_taskManager.TaskCompleted -= _taskManager_TaskCompleted;
|
_taskManager.TaskCompleted -= OnTaskCompleted;
|
||||||
|
|
||||||
_installationManager.PluginInstalled -= _installationManager_PluginInstalled;
|
_installationManager.PluginInstalled -= OnPluginInstalled;
|
||||||
_installationManager.PluginUninstalled -= _installationManager_PluginUninstalled;
|
_installationManager.PluginUninstalled -= OnPluginUninstalled;
|
||||||
_installationManager.PluginUpdated -= _installationManager_PluginUpdated;
|
_installationManager.PluginUpdated -= OnPluginUpdated;
|
||||||
_installationManager.PackageInstallationFailed -= _installationManager_PackageInstallationFailed;
|
_installationManager.PackageInstallationFailed -= OnPackageInstallationFailed;
|
||||||
|
|
||||||
_sessionManager.SessionStarted -= _sessionManager_SessionStarted;
|
_sessionManager.SessionStarted -= OnSessionStarted;
|
||||||
_sessionManager.AuthenticationFailed -= _sessionManager_AuthenticationFailed;
|
_sessionManager.AuthenticationFailed -= OnAuthenticationFailed;
|
||||||
_sessionManager.AuthenticationSucceeded -= _sessionManager_AuthenticationSucceeded;
|
_sessionManager.AuthenticationSucceeded -= OnAuthenticationSucceeded;
|
||||||
_sessionManager.SessionEnded -= _sessionManager_SessionEnded;
|
_sessionManager.SessionEnded -= OnSessionEnded;
|
||||||
|
|
||||||
_sessionManager.PlaybackStart -= _sessionManager_PlaybackStart;
|
_sessionManager.PlaybackStart -= OnPlaybackStart;
|
||||||
_sessionManager.PlaybackStopped -= _sessionManager_PlaybackStopped;
|
_sessionManager.PlaybackStopped -= OnPlaybackStopped;
|
||||||
|
|
||||||
_subManager.SubtitlesDownloaded -= _subManager_SubtitlesDownloaded;
|
_subManager.SubtitleDownloadFailure -= OnSubtitleDownloadFailure;
|
||||||
_subManager.SubtitleDownloadFailure -= _subManager_SubtitleDownloadFailure;
|
|
||||||
|
|
||||||
_userManager.UserCreated -= _userManager_UserCreated;
|
_userManager.UserCreated -= OnUserCreated;
|
||||||
_userManager.UserPasswordChanged -= _userManager_UserPasswordChanged;
|
_userManager.UserPasswordChanged -= OnUserPasswordChanged;
|
||||||
_userManager.UserDeleted -= _userManager_UserDeleted;
|
_userManager.UserDeleted -= OnUserDeleted;
|
||||||
_userManager.UserPolicyUpdated -= _userManager_UserPolicyUpdated;
|
_userManager.UserPolicyUpdated -= OnUserPolicyUpdated;
|
||||||
_userManager.UserLockedOut -= _userManager_UserLockedOut;
|
_userManager.UserLockedOut -= OnUserLockedOut;
|
||||||
|
|
||||||
_config.ConfigurationUpdated -= _config_ConfigurationUpdated;
|
_deviceManager.CameraImageUploaded -= OnCameraImageUploaded;
|
||||||
_config.NamedConfigurationUpdated -= _config_NamedConfigurationUpdated;
|
|
||||||
|
|
||||||
_deviceManager.CameraImageUploaded -= _deviceManager_CameraImageUploaded;
|
_appHost.ApplicationUpdated -= OnApplicationUpdated;
|
||||||
|
|
||||||
_appHost.ApplicationUpdated -= _appHost_ApplicationUpdated;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -538,6 +483,7 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
values.Add(CreateValueString(years, "year"));
|
values.Add(CreateValueString(years, "year"));
|
||||||
days = days % DaysInYear;
|
days = days % DaysInYear;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Number of months
|
// Number of months
|
||||||
if (days >= DaysInMonth)
|
if (days >= DaysInMonth)
|
||||||
{
|
{
|
||||||
@@ -545,25 +491,39 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
values.Add(CreateValueString(months, "month"));
|
values.Add(CreateValueString(months, "month"));
|
||||||
days = days % DaysInMonth;
|
days = days % DaysInMonth;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Number of days
|
// Number of days
|
||||||
if (days >= 1)
|
if (days >= 1)
|
||||||
|
{
|
||||||
values.Add(CreateValueString(days, "day"));
|
values.Add(CreateValueString(days, "day"));
|
||||||
|
}
|
||||||
|
|
||||||
// Number of hours
|
// Number of hours
|
||||||
if (span.Hours >= 1)
|
if (span.Hours >= 1)
|
||||||
|
{
|
||||||
values.Add(CreateValueString(span.Hours, "hour"));
|
values.Add(CreateValueString(span.Hours, "hour"));
|
||||||
|
}
|
||||||
// Number of minutes
|
// Number of minutes
|
||||||
if (span.Minutes >= 1)
|
if (span.Minutes >= 1)
|
||||||
|
{
|
||||||
values.Add(CreateValueString(span.Minutes, "minute"));
|
values.Add(CreateValueString(span.Minutes, "minute"));
|
||||||
|
}
|
||||||
|
|
||||||
// Number of seconds (include when 0 if no other components included)
|
// Number of seconds (include when 0 if no other components included)
|
||||||
if (span.Seconds >= 1 || values.Count == 0)
|
if (span.Seconds >= 1 || values.Count == 0)
|
||||||
|
{
|
||||||
values.Add(CreateValueString(span.Seconds, "second"));
|
values.Add(CreateValueString(span.Seconds, "second"));
|
||||||
|
}
|
||||||
|
|
||||||
// Combine values into string
|
// Combine values into string
|
||||||
var builder = new StringBuilder();
|
var builder = new StringBuilder();
|
||||||
for (int i = 0; i < values.Count; i++)
|
for (int i = 0; i < values.Count; i++)
|
||||||
{
|
{
|
||||||
if (builder.Length > 0)
|
if (builder.Length > 0)
|
||||||
|
{
|
||||||
builder.Append(i == values.Count - 1 ? " and " : ", ");
|
builder.Append(i == values.Count - 1 ? " and " : ", ");
|
||||||
|
}
|
||||||
|
|
||||||
builder.Append(values[i]);
|
builder.Append(values[i]);
|
||||||
}
|
}
|
||||||
// Return result
|
// Return result
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Model.Activity;
|
using MediaBrowser.Model.Activity;
|
||||||
using MediaBrowser.Model.Events;
|
using MediaBrowser.Model.Events;
|
||||||
|
using MediaBrowser.Model.Querying;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Activity
|
namespace Emby.Server.Implementations.Activity
|
||||||
@@ -27,39 +26,26 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CreateAsync(ActivityLogEntry entry)
|
public void Create(ActivityLogEntry entry)
|
||||||
{
|
{
|
||||||
entry.Date = DateTime.UtcNow;
|
entry.Date = DateTime.UtcNow;
|
||||||
|
|
||||||
await _repo.CreateAsync(entry);
|
_repo.Create(entry);
|
||||||
|
|
||||||
EntryCreated?.Invoke(this, new GenericEventArgs<ActivityLogEntry>(entry));
|
EntryCreated?.Invoke(this, new GenericEventArgs<ActivityLogEntry>(entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit)
|
public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit)
|
||||||
{
|
{
|
||||||
var result = _repo.GetActivityLogEntries();
|
var result = _repo.GetActivityLogEntries(minDate, hasUserId, startIndex, limit);
|
||||||
|
|
||||||
if (minDate.HasValue)
|
foreach (var item in result.Items)
|
||||||
{
|
{
|
||||||
result = result.Where(x => x.Date >= minDate.Value);
|
if (item.UserId == Guid.Empty)
|
||||||
}
|
{
|
||||||
if (hasUserId.HasValue)
|
continue;
|
||||||
{
|
}
|
||||||
result = result.Where(x => x.UserId != null && x.UserId != Guid.Empty);
|
|
||||||
}
|
|
||||||
if (startIndex.HasValue)
|
|
||||||
{
|
|
||||||
result = result.Where(x => x.Id >= startIndex.Value);
|
|
||||||
}
|
|
||||||
if (limit.HasValue)
|
|
||||||
{
|
|
||||||
result = result.Take(limit.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add images for each user
|
|
||||||
foreach (var item in result)
|
|
||||||
{
|
|
||||||
var user = _userManager.GetUserById(item.UserId);
|
var user = _userManager.GetUserById(item.UserId);
|
||||||
|
|
||||||
if (user != null)
|
if (user != null)
|
||||||
@@ -69,7 +55,12 @@ namespace Emby.Server.Implementations.Activity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.AsEnumerable();
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit)
|
||||||
|
{
|
||||||
|
return GetActivityLogEntries(minDate, null, startIndex, limit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,310 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using Emby.Server.Implementations.Data;
|
||||||
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Model.Activity;
|
using MediaBrowser.Model.Activity;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using MediaBrowser.Model.IO;
|
||||||
|
using MediaBrowser.Model.Querying;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using SQLitePCL.pretty;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Activity
|
namespace Emby.Server.Implementations.Activity
|
||||||
{
|
{
|
||||||
public class ActivityRepository : DbContext, IActivityRepository
|
public class ActivityRepository : BaseSqliteRepository, IActivityRepository
|
||||||
{
|
{
|
||||||
protected string _dataDirPath;
|
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
||||||
|
protected IFileSystem FileSystem { get; private set; }
|
||||||
|
|
||||||
public DbSet<ActivityLogEntry> ActivityLogs { get; set; }
|
public ActivityRepository(ILoggerFactory loggerFactory, IServerApplicationPaths appPaths, IFileSystem fileSystem)
|
||||||
|
: base(loggerFactory.CreateLogger(nameof(ActivityRepository)))
|
||||||
public ActivityRepository(string dataDirPath)
|
|
||||||
{
|
{
|
||||||
_dataDirPath = dataDirPath;
|
DbFilePath = Path.Combine(appPaths.DataPath, "activitylog.db");
|
||||||
|
FileSystem = fileSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
public void Initialize()
|
||||||
{
|
{
|
||||||
// Ensure the dir exists
|
try
|
||||||
Directory.CreateDirectory(_dataDirPath);
|
{
|
||||||
|
InitializeInternal();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError(ex, "Error loading database file. Will reset and retry.");
|
||||||
|
|
||||||
optionsBuilder.UseSqlite($"Filename={Path.Combine(_dataDirPath, "activitylog.sqlite.db")}");
|
FileSystem.DeleteFile(DbFilePath);
|
||||||
|
|
||||||
|
InitializeInternal();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CreateAsync(ActivityLogEntry entry)
|
private void InitializeInternal()
|
||||||
{
|
{
|
||||||
await ActivityLogs.AddAsync(entry);
|
using (var connection = CreateConnection())
|
||||||
await SaveChangesAsync();
|
{
|
||||||
|
RunDefaultInitialization(connection);
|
||||||
|
|
||||||
|
connection.RunQueries(new[]
|
||||||
|
{
|
||||||
|
"create table if not exists ActivityLog (Id INTEGER PRIMARY KEY, Name TEXT NOT NULL, Overview TEXT, ShortOverview TEXT, Type TEXT NOT NULL, ItemId TEXT, UserId TEXT, DateCreated DATETIME NOT NULL, LogSeverity TEXT NOT NULL)",
|
||||||
|
"drop index if exists idx_ActivityLogEntries"
|
||||||
|
});
|
||||||
|
|
||||||
|
TryMigrate(connection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IQueryable<ActivityLogEntry> GetActivityLogEntries()
|
private void TryMigrate(ManagedConnection connection)
|
||||||
=> ActivityLogs;
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (TableExists(connection, "ActivityLogEntries"))
|
||||||
|
{
|
||||||
|
connection.RunQueries(new[]
|
||||||
|
{
|
||||||
|
"INSERT INTO ActivityLog (Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) SELECT Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity FROM ActivityLogEntries",
|
||||||
|
"drop table if exists ActivityLogEntries"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError(ex, "Error migrating activity log database");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLog";
|
||||||
|
|
||||||
|
public void Create(ActivityLogEntry entry)
|
||||||
|
{
|
||||||
|
if (entry == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
using (WriteLock.Write())
|
||||||
|
using (var connection = CreateConnection())
|
||||||
|
{
|
||||||
|
connection.RunInTransaction(db =>
|
||||||
|
{
|
||||||
|
using (var statement = db.PrepareStatement("insert into ActivityLog (Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) values (@Name, @Overview, @ShortOverview, @Type, @ItemId, @UserId, @DateCreated, @LogSeverity)"))
|
||||||
|
{
|
||||||
|
statement.TryBind("@Name", entry.Name);
|
||||||
|
|
||||||
|
statement.TryBind("@Overview", entry.Overview);
|
||||||
|
statement.TryBind("@ShortOverview", entry.ShortOverview);
|
||||||
|
statement.TryBind("@Type", entry.Type);
|
||||||
|
statement.TryBind("@ItemId", entry.ItemId);
|
||||||
|
|
||||||
|
if (entry.UserId.Equals(Guid.Empty))
|
||||||
|
{
|
||||||
|
statement.TryBindNull("@UserId");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
statement.TryBind("@UserId", entry.UserId.ToString("N"));
|
||||||
|
}
|
||||||
|
|
||||||
|
statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue());
|
||||||
|
statement.TryBind("@LogSeverity", entry.Severity.ToString());
|
||||||
|
|
||||||
|
statement.MoveNext();
|
||||||
|
}
|
||||||
|
}, TransactionMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(ActivityLogEntry entry)
|
||||||
|
{
|
||||||
|
if (entry == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
using (WriteLock.Write())
|
||||||
|
using (var connection = CreateConnection())
|
||||||
|
{
|
||||||
|
connection.RunInTransaction(db =>
|
||||||
|
{
|
||||||
|
using (var statement = db.PrepareStatement("Update ActivityLog set Name=@Name,Overview=@Overview,ShortOverview=@ShortOverview,Type=@Type,ItemId=@ItemId,UserId=@UserId,DateCreated=@DateCreated,LogSeverity=@LogSeverity where Id=@Id"))
|
||||||
|
{
|
||||||
|
statement.TryBind("@Id", entry.Id);
|
||||||
|
|
||||||
|
statement.TryBind("@Name", entry.Name);
|
||||||
|
statement.TryBind("@Overview", entry.Overview);
|
||||||
|
statement.TryBind("@ShortOverview", entry.ShortOverview);
|
||||||
|
statement.TryBind("@Type", entry.Type);
|
||||||
|
statement.TryBind("@ItemId", entry.ItemId);
|
||||||
|
|
||||||
|
if (entry.UserId.Equals(Guid.Empty))
|
||||||
|
{
|
||||||
|
statement.TryBindNull("@UserId");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
statement.TryBind("@UserId", entry.UserId.ToString("N"));
|
||||||
|
}
|
||||||
|
|
||||||
|
statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue());
|
||||||
|
statement.TryBind("@LogSeverity", entry.Severity.ToString());
|
||||||
|
|
||||||
|
statement.MoveNext();
|
||||||
|
}
|
||||||
|
}, TransactionMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit)
|
||||||
|
{
|
||||||
|
using (WriteLock.Read())
|
||||||
|
using (var connection = CreateConnection(true))
|
||||||
|
{
|
||||||
|
var commandText = BaseActivitySelectText;
|
||||||
|
var whereClauses = new List<string>();
|
||||||
|
|
||||||
|
if (minDate.HasValue)
|
||||||
|
{
|
||||||
|
whereClauses.Add("DateCreated>=@DateCreated");
|
||||||
|
}
|
||||||
|
if (hasUserId.HasValue)
|
||||||
|
{
|
||||||
|
if (hasUserId.Value)
|
||||||
|
{
|
||||||
|
whereClauses.Add("UserId not null");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
whereClauses.Add("UserId is null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var whereTextWithoutPaging = whereClauses.Count == 0 ?
|
||||||
|
string.Empty :
|
||||||
|
" where " + string.Join(" AND ", whereClauses.ToArray());
|
||||||
|
|
||||||
|
if (startIndex.HasValue && startIndex.Value > 0)
|
||||||
|
{
|
||||||
|
var pagingWhereText = whereClauses.Count == 0 ?
|
||||||
|
string.Empty :
|
||||||
|
" where " + string.Join(" AND ", whereClauses.ToArray());
|
||||||
|
|
||||||
|
whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM ActivityLog {0} ORDER BY DateCreated DESC LIMIT {1})",
|
||||||
|
pagingWhereText,
|
||||||
|
startIndex.Value.ToString(_usCulture)));
|
||||||
|
}
|
||||||
|
|
||||||
|
var whereText = whereClauses.Count == 0 ?
|
||||||
|
string.Empty :
|
||||||
|
" where " + string.Join(" AND ", whereClauses.ToArray());
|
||||||
|
|
||||||
|
commandText += whereText;
|
||||||
|
|
||||||
|
commandText += " ORDER BY DateCreated DESC";
|
||||||
|
|
||||||
|
if (limit.HasValue)
|
||||||
|
{
|
||||||
|
commandText += " LIMIT " + limit.Value.ToString(_usCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
var statementTexts = new List<string>();
|
||||||
|
statementTexts.Add(commandText);
|
||||||
|
statementTexts.Add("select count (Id) from ActivityLog" + whereTextWithoutPaging);
|
||||||
|
|
||||||
|
return connection.RunInTransaction(db =>
|
||||||
|
{
|
||||||
|
var list = new List<ActivityLogEntry>();
|
||||||
|
var result = new QueryResult<ActivityLogEntry>();
|
||||||
|
|
||||||
|
var statements = PrepareAllSafe(db, statementTexts).ToList();
|
||||||
|
|
||||||
|
using (var statement = statements[0])
|
||||||
|
{
|
||||||
|
if (minDate.HasValue)
|
||||||
|
{
|
||||||
|
statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var row in statement.ExecuteQuery())
|
||||||
|
{
|
||||||
|
list.Add(GetEntry(row));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var statement = statements[1])
|
||||||
|
{
|
||||||
|
if (minDate.HasValue)
|
||||||
|
{
|
||||||
|
statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Items = list.ToArray();
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}, ReadTransactionMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ActivityLogEntry GetEntry(IReadOnlyList<IResultSetValue> reader)
|
||||||
|
{
|
||||||
|
var index = 0;
|
||||||
|
|
||||||
|
var info = new ActivityLogEntry
|
||||||
|
{
|
||||||
|
Id = reader[index].ToInt64()
|
||||||
|
};
|
||||||
|
|
||||||
|
index++;
|
||||||
|
if (reader[index].SQLiteType != SQLiteType.Null)
|
||||||
|
{
|
||||||
|
info.Name = reader[index].ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
if (reader[index].SQLiteType != SQLiteType.Null)
|
||||||
|
{
|
||||||
|
info.Overview = reader[index].ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
if (reader[index].SQLiteType != SQLiteType.Null)
|
||||||
|
{
|
||||||
|
info.ShortOverview = reader[index].ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
if (reader[index].SQLiteType != SQLiteType.Null)
|
||||||
|
{
|
||||||
|
info.Type = reader[index].ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
if (reader[index].SQLiteType != SQLiteType.Null)
|
||||||
|
{
|
||||||
|
info.ItemId = reader[index].ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
if (reader[index].SQLiteType != SQLiteType.Null)
|
||||||
|
{
|
||||||
|
info.UserId = new Guid(reader[index].ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
info.Date = reader[index].ReadDateTime();
|
||||||
|
|
||||||
|
index++;
|
||||||
|
if (reader[index].SQLiteType != SQLiteType.Null)
|
||||||
|
{
|
||||||
|
info.Severity = (LogLevel)Enum.Parse(typeof(LogLevel), reader[index].ToString(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
|
|
||||||
@@ -14,48 +15,52 @@ namespace Emby.Server.Implementations.AppBase
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected BaseApplicationPaths(
|
protected BaseApplicationPaths(
|
||||||
string programDataPath,
|
string programDataPath,
|
||||||
string appFolderPath,
|
string logDirectoryPath,
|
||||||
string logDirectoryPath = null,
|
string configurationDirectoryPath,
|
||||||
string configurationDirectoryPath = null)
|
string cacheDirectoryPath,
|
||||||
|
string webDirectoryPath)
|
||||||
{
|
{
|
||||||
ProgramDataPath = programDataPath;
|
ProgramDataPath = programDataPath;
|
||||||
ProgramSystemPath = appFolderPath;
|
|
||||||
LogDirectoryPath = logDirectoryPath;
|
LogDirectoryPath = logDirectoryPath;
|
||||||
ConfigurationDirectoryPath = configurationDirectoryPath;
|
ConfigurationDirectoryPath = configurationDirectoryPath;
|
||||||
|
CachePath = cacheDirectoryPath;
|
||||||
|
WebPath = webDirectoryPath;
|
||||||
|
|
||||||
|
DataPath = Path.Combine(ProgramDataPath, "data");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the path to the program data folder
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The program data path.</value>
|
||||||
public string ProgramDataPath { get; private set; }
|
public string ProgramDataPath { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the path to the web UI resources folder
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The web UI resources path.</value>
|
||||||
|
public string WebPath { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the path to the system folder
|
/// Gets the path to the system folder
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ProgramSystemPath { get; private set; }
|
public string ProgramSystemPath { get; } = AppContext.BaseDirectory;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The _data directory
|
|
||||||
/// </summary>
|
|
||||||
private string _dataDirectory;
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the folder path to the data directory
|
/// Gets the folder path to the data directory
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The data directory.</value>
|
/// <value>The data directory.</value>
|
||||||
|
private string _dataPath;
|
||||||
public string DataPath
|
public string DataPath
|
||||||
{
|
{
|
||||||
get
|
get => _dataPath;
|
||||||
{
|
private set => _dataPath = Directory.CreateDirectory(value).FullName;
|
||||||
if (_dataDirectory == null)
|
|
||||||
{
|
|
||||||
_dataDirectory = Path.Combine(ProgramDataPath, "data");
|
|
||||||
|
|
||||||
Directory.CreateDirectory(_dataDirectory);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _dataDirectory;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private const string _virtualDataPath = "%AppDataPath%";
|
/// <summary>
|
||||||
public string VirtualDataPath => _virtualDataPath;
|
/// Gets the magic strings used for virtual path manipulation.
|
||||||
|
/// </summary>
|
||||||
|
public string VirtualDataPath { get; } = "%AppDataPath%";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the image cache path.
|
/// Gets the image cache path.
|
||||||
@@ -75,61 +80,17 @@ namespace Emby.Server.Implementations.AppBase
|
|||||||
/// <value>The plugin configurations path.</value>
|
/// <value>The plugin configurations path.</value>
|
||||||
public string PluginConfigurationsPath => Path.Combine(PluginsPath, "configurations");
|
public string PluginConfigurationsPath => Path.Combine(PluginsPath, "configurations");
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the path to where temporary update files will be stored
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The plugin configurations path.</value>
|
|
||||||
public string TempUpdatePath => Path.Combine(ProgramDataPath, "updates");
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The _log directory
|
|
||||||
/// </summary>
|
|
||||||
private string _logDirectoryPath;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the path to the log directory
|
/// Gets the path to the log directory
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The log directory path.</value>
|
/// <value>The log directory path.</value>
|
||||||
public string LogDirectoryPath
|
public string LogDirectoryPath { get; private set; }
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(_logDirectoryPath))
|
|
||||||
{
|
|
||||||
_logDirectoryPath = Path.Combine(ProgramDataPath, "logs");
|
|
||||||
|
|
||||||
Directory.CreateDirectory(_logDirectoryPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _logDirectoryPath;
|
|
||||||
}
|
|
||||||
set => _logDirectoryPath = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The _config directory
|
|
||||||
/// </summary>
|
|
||||||
private string _configurationDirectoryPath;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the path to the application configuration root directory
|
/// Gets the path to the application configuration root directory
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The configuration directory path.</value>
|
/// <value>The configuration directory path.</value>
|
||||||
public string ConfigurationDirectoryPath
|
public string ConfigurationDirectoryPath { get; private set; }
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(_configurationDirectoryPath))
|
|
||||||
{
|
|
||||||
_configurationDirectoryPath = Path.Combine(ProgramDataPath, "config");
|
|
||||||
|
|
||||||
Directory.CreateDirectory(_configurationDirectoryPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _configurationDirectoryPath;
|
|
||||||
}
|
|
||||||
set => _configurationDirectoryPath = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the path to the system configuration file
|
/// Gets the path to the system configuration file
|
||||||
@@ -137,29 +98,11 @@ namespace Emby.Server.Implementations.AppBase
|
|||||||
/// <value>The system configuration file path.</value>
|
/// <value>The system configuration file path.</value>
|
||||||
public string SystemConfigurationFilePath => Path.Combine(ConfigurationDirectoryPath, "system.xml");
|
public string SystemConfigurationFilePath => Path.Combine(ConfigurationDirectoryPath, "system.xml");
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The _cache directory
|
|
||||||
/// </summary>
|
|
||||||
private string _cachePath;
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the folder path to the cache directory
|
/// Gets the folder path to the cache directory
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The cache directory.</value>
|
/// <value>The cache directory.</value>
|
||||||
public string CachePath
|
public string CachePath { get; set; }
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(_cachePath))
|
|
||||||
{
|
|
||||||
_cachePath = Path.Combine(ProgramDataPath, "cache");
|
|
||||||
|
|
||||||
Directory.CreateDirectory(_cachePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _cachePath;
|
|
||||||
}
|
|
||||||
set => _cachePath = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the folder path to the temp directory within the cache folder
|
/// Gets the folder path to the temp directory within the cache folder
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ namespace Emby.Server.Implementations.AppBase
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
// Lazy load
|
// Lazy load
|
||||||
LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationLoaded, ref _configurationSyncLock, () => (BaseApplicationConfiguration)ConfigurationHelper.GetXmlConfiguration(ConfigurationType, CommonApplicationPaths.SystemConfigurationFilePath, XmlSerializer, FileSystem));
|
LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationLoaded, ref _configurationSyncLock, () => (BaseApplicationConfiguration)ConfigurationHelper.GetXmlConfiguration(ConfigurationType, CommonApplicationPaths.SystemConfigurationFilePath, XmlSerializer));
|
||||||
return _configuration;
|
return _configuration;
|
||||||
}
|
}
|
||||||
protected set
|
protected set
|
||||||
@@ -127,7 +127,7 @@ namespace Emby.Server.Implementations.AppBase
|
|||||||
Logger.LogInformation("Saving system configuration");
|
Logger.LogInformation("Saving system configuration");
|
||||||
var path = CommonApplicationPaths.SystemConfigurationFilePath;
|
var path = CommonApplicationPaths.SystemConfigurationFilePath;
|
||||||
|
|
||||||
FileSystem.CreateDirectory(FileSystem.GetDirectoryName(path));
|
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
|
|
||||||
lock (_configurationSyncLock)
|
lock (_configurationSyncLock)
|
||||||
{
|
{
|
||||||
@@ -171,16 +171,29 @@ namespace Emby.Server.Implementations.AppBase
|
|||||||
private void UpdateCachePath()
|
private void UpdateCachePath()
|
||||||
{
|
{
|
||||||
string cachePath;
|
string cachePath;
|
||||||
|
// If the configuration file has no entry (i.e. not set in UI)
|
||||||
if (string.IsNullOrWhiteSpace(CommonConfiguration.CachePath))
|
if (string.IsNullOrWhiteSpace(CommonConfiguration.CachePath))
|
||||||
{
|
{
|
||||||
cachePath = null;
|
// If the current live configuration has no entry (i.e. not set on CLI/envvars, during startup)
|
||||||
|
if (string.IsNullOrWhiteSpace(((BaseApplicationPaths)CommonApplicationPaths).CachePath))
|
||||||
|
{
|
||||||
|
// Set cachePath to a default value under ProgramDataPath
|
||||||
|
cachePath = Path.Combine(((BaseApplicationPaths)CommonApplicationPaths).ProgramDataPath, "cache");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Set cachePath to the existing live value; will require restart if UI value is removed (but not replaced)
|
||||||
|
// TODO: Figure out how to re-grab this from the CLI/envvars while running
|
||||||
|
cachePath = ((BaseApplicationPaths)CommonApplicationPaths).CachePath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cachePath = Path.Combine(CommonConfiguration.CachePath, "cache");
|
// Set cachePath to the new UI-set value
|
||||||
|
cachePath = CommonConfiguration.CachePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.LogInformation("Setting cache path to " + cachePath);
|
||||||
((BaseApplicationPaths)CommonApplicationPaths).CachePath = cachePath;
|
((BaseApplicationPaths)CommonApplicationPaths).CachePath = cachePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,7 +210,7 @@ namespace Emby.Server.Implementations.AppBase
|
|||||||
&& !string.Equals(CommonConfiguration.CachePath ?? string.Empty, newPath))
|
&& !string.Equals(CommonConfiguration.CachePath ?? string.Empty, newPath))
|
||||||
{
|
{
|
||||||
// Validate
|
// Validate
|
||||||
if (!FileSystem.DirectoryExists(newPath))
|
if (!Directory.Exists(newPath))
|
||||||
{
|
{
|
||||||
throw new FileNotFoundException(string.Format("{0} does not exist.", newPath));
|
throw new FileNotFoundException(string.Format("{0} does not exist.", newPath));
|
||||||
}
|
}
|
||||||
@@ -209,8 +222,7 @@ namespace Emby.Server.Implementations.AppBase
|
|||||||
protected void EnsureWriteAccess(string path)
|
protected void EnsureWriteAccess(string path)
|
||||||
{
|
{
|
||||||
var file = Path.Combine(path, Guid.NewGuid().ToString());
|
var file = Path.Combine(path, Guid.NewGuid().ToString());
|
||||||
|
File.WriteAllText(file, string.Empty);
|
||||||
FileSystem.WriteAllText(file, string.Empty);
|
|
||||||
FileSystem.DeleteFile(file);
|
FileSystem.DeleteFile(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +230,7 @@ namespace Emby.Server.Implementations.AppBase
|
|||||||
|
|
||||||
private string GetConfigurationFile(string key)
|
private string GetConfigurationFile(string key)
|
||||||
{
|
{
|
||||||
return Path.Combine(CommonApplicationPaths.ConfigurationDirectoryPath, key.ToLower() + ".xml");
|
return Path.Combine(CommonApplicationPaths.ConfigurationDirectoryPath, key.ToLowerInvariant() + ".xml");
|
||||||
}
|
}
|
||||||
|
|
||||||
public object GetConfiguration(string key)
|
public object GetConfiguration(string key)
|
||||||
@@ -246,14 +258,15 @@ namespace Emby.Server.Implementations.AppBase
|
|||||||
|
|
||||||
private object LoadConfiguration(string path, Type configurationType)
|
private object LoadConfiguration(string path, Type configurationType)
|
||||||
{
|
{
|
||||||
|
if (!File.Exists(path))
|
||||||
|
{
|
||||||
|
return Activator.CreateInstance(configurationType);
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return XmlSerializer.DeserializeFromFile(configurationType, path);
|
return XmlSerializer.DeserializeFromFile(configurationType, path);
|
||||||
}
|
}
|
||||||
catch (FileNotFoundException)
|
|
||||||
{
|
|
||||||
return Activator.CreateInstance(configurationType);
|
|
||||||
}
|
|
||||||
catch (IOException)
|
catch (IOException)
|
||||||
{
|
{
|
||||||
return Activator.CreateInstance(configurationType);
|
return Activator.CreateInstance(configurationType);
|
||||||
@@ -293,7 +306,7 @@ namespace Emby.Server.Implementations.AppBase
|
|||||||
_configurations.AddOrUpdate(key, configuration, (k, v) => configuration);
|
_configurations.AddOrUpdate(key, configuration, (k, v) => configuration);
|
||||||
|
|
||||||
var path = GetConfigurationFile(key);
|
var path = GetConfigurationFile(key);
|
||||||
FileSystem.CreateDirectory(FileSystem.GetDirectoryName(path));
|
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
|
|
||||||
lock (_configurationSyncLock)
|
lock (_configurationSyncLock)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using MediaBrowser.Model.IO;
|
|
||||||
using MediaBrowser.Model.Serialization;
|
using MediaBrowser.Model.Serialization;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.AppBase
|
namespace Emby.Server.Implementations.AppBase
|
||||||
@@ -18,9 +17,8 @@ namespace Emby.Server.Implementations.AppBase
|
|||||||
/// <param name="type">The type.</param>
|
/// <param name="type">The type.</param>
|
||||||
/// <param name="path">The path.</param>
|
/// <param name="path">The path.</param>
|
||||||
/// <param name="xmlSerializer">The XML serializer.</param>
|
/// <param name="xmlSerializer">The XML serializer.</param>
|
||||||
/// <param name="fileSystem">The file system</param>
|
|
||||||
/// <returns>System.Object.</returns>
|
/// <returns>System.Object.</returns>
|
||||||
public static object GetXmlConfiguration(Type type, string path, IXmlSerializer xmlSerializer, IFileSystem fileSystem)
|
public static object GetXmlConfiguration(Type type, string path, IXmlSerializer xmlSerializer)
|
||||||
{
|
{
|
||||||
object configuration;
|
object configuration;
|
||||||
|
|
||||||
@@ -29,7 +27,7 @@ namespace Emby.Server.Implementations.AppBase
|
|||||||
// Use try/catch to avoid the extra file system lookup using File.Exists
|
// Use try/catch to avoid the extra file system lookup using File.Exists
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
buffer = fileSystem.ReadAllBytes(path);
|
buffer = File.ReadAllBytes(path);
|
||||||
|
|
||||||
configuration = xmlSerializer.DeserializeFromBytes(type, buffer);
|
configuration = xmlSerializer.DeserializeFromBytes(type, buffer);
|
||||||
}
|
}
|
||||||
@@ -48,10 +46,10 @@ namespace Emby.Server.Implementations.AppBase
|
|||||||
// If the file didn't exist before, or if something has changed, re-save
|
// If the file didn't exist before, or if something has changed, re-save
|
||||||
if (buffer == null || !buffer.SequenceEqual(newBytes))
|
if (buffer == null || !buffer.SequenceEqual(newBytes))
|
||||||
{
|
{
|
||||||
fileSystem.CreateDirectory(fileSystem.GetDirectoryName(path));
|
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
|
|
||||||
// Save it after load in case we got new items
|
// Save it after load in case we got new items
|
||||||
fileSystem.WriteAllBytes(path, newBytes);
|
File.WriteAllBytes(path, newBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
return configuration;
|
return configuration;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -14,11 +14,9 @@ namespace Emby.Server.Implementations.Archiving
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ZipClient : IZipClient
|
public class ZipClient : IZipClient
|
||||||
{
|
{
|
||||||
private readonly IFileSystem _fileSystem;
|
public ZipClient()
|
||||||
|
|
||||||
public ZipClient(IFileSystem fileSystem)
|
|
||||||
{
|
{
|
||||||
_fileSystem = fileSystem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -29,7 +27,7 @@ namespace Emby.Server.Implementations.Archiving
|
|||||||
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
|
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
|
||||||
public void ExtractAll(string sourceFile, string targetPath, bool overwriteExistingFiles)
|
public void ExtractAll(string sourceFile, string targetPath, bool overwriteExistingFiles)
|
||||||
{
|
{
|
||||||
using (var fileStream = _fileSystem.OpenRead(sourceFile))
|
using (var fileStream = File.OpenRead(sourceFile))
|
||||||
{
|
{
|
||||||
ExtractAll(fileStream, targetPath, overwriteExistingFiles);
|
ExtractAll(fileStream, targetPath, overwriteExistingFiles);
|
||||||
}
|
}
|
||||||
@@ -115,7 +113,7 @@ namespace Emby.Server.Implementations.Archiving
|
|||||||
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
|
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
|
||||||
public void ExtractAllFrom7z(string sourceFile, string targetPath, bool overwriteExistingFiles)
|
public void ExtractAllFrom7z(string sourceFile, string targetPath, bool overwriteExistingFiles)
|
||||||
{
|
{
|
||||||
using (var fileStream = _fileSystem.OpenRead(sourceFile))
|
using (var fileStream = File.OpenRead(sourceFile))
|
||||||
{
|
{
|
||||||
ExtractAllFrom7z(fileStream, targetPath, overwriteExistingFiles);
|
ExtractAllFrom7z(fileStream, targetPath, overwriteExistingFiles);
|
||||||
}
|
}
|
||||||
@@ -155,7 +153,7 @@ namespace Emby.Server.Implementations.Archiving
|
|||||||
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
|
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
|
||||||
public void ExtractAllFromTar(string sourceFile, string targetPath, bool overwriteExistingFiles)
|
public void ExtractAllFromTar(string sourceFile, string targetPath, bool overwriteExistingFiles)
|
||||||
{
|
{
|
||||||
using (var fileStream = _fileSystem.OpenRead(sourceFile))
|
using (var fileStream = File.OpenRead(sourceFile))
|
||||||
{
|
{
|
||||||
ExtractAllFromTar(fileStream, targetPath, overwriteExistingFiles);
|
ExtractAllFromTar(fileStream, targetPath, overwriteExistingFiles);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -243,8 +243,7 @@ namespace Emby.Server.Implementations.Channels
|
|||||||
{
|
{
|
||||||
foreach (var item in returnItems)
|
foreach (var item in returnItems)
|
||||||
{
|
{
|
||||||
var task = RefreshLatestChannelItems(GetChannelProvider(item), CancellationToken.None);
|
RefreshLatestChannelItems(GetChannelProvider(item), CancellationToken.None).GetAwaiter().GetResult();
|
||||||
Task.WaitAll(task);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,9 +302,7 @@ namespace Emby.Server.Implementations.Channels
|
|||||||
}
|
}
|
||||||
|
|
||||||
numComplete++;
|
numComplete++;
|
||||||
double percent = numComplete;
|
double percent = (double)numComplete / allChannelsList.Count;
|
||||||
percent /= allChannelsList.Count;
|
|
||||||
|
|
||||||
progress.Report(100 * percent);
|
progress.Report(100 * percent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,7 +352,7 @@ namespace Emby.Server.Implementations.Channels
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
|
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
|
|
||||||
_jsonSerializer.SerializeToFile(mediaSources, path);
|
_jsonSerializer.SerializeToFile(mediaSources, path);
|
||||||
}
|
}
|
||||||
@@ -658,9 +655,7 @@ namespace Emby.Server.Implementations.Channels
|
|||||||
|
|
||||||
foreach (var item in result.Items)
|
foreach (var item in result.Items)
|
||||||
{
|
{
|
||||||
var folder = item as Folder;
|
if (item is Folder folder)
|
||||||
|
|
||||||
if (folder != null)
|
|
||||||
{
|
{
|
||||||
await GetChannelItemsInternal(new InternalItemsQuery
|
await GetChannelItemsInternal(new InternalItemsQuery
|
||||||
{
|
{
|
||||||
@@ -834,7 +829,7 @@ namespace Emby.Server.Implementations.Channels
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
|
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
|
|
||||||
_jsonSerializer.SerializeToFile(result, path);
|
_jsonSerializer.SerializeToFile(result, path);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,64 +35,52 @@ namespace Emby.Server.Implementations.Channels
|
|||||||
public static string GetUserDistinctValue(User user)
|
public static string GetUserDistinctValue(User user)
|
||||||
{
|
{
|
||||||
var channels = user.Policy.EnabledChannels
|
var channels = user.Policy.EnabledChannels
|
||||||
.OrderBy(i => i)
|
.OrderBy(i => i);
|
||||||
.ToList();
|
|
||||||
|
|
||||||
return string.Join("|", channels.ToArray());
|
return string.Join("|", channels);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CleanDatabase(CancellationToken cancellationToken)
|
private void CleanDatabase(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var installedChannelIds = ((ChannelManager)_channelManager).GetInstalledChannelIds();
|
var installedChannelIds = ((ChannelManager)_channelManager).GetInstalledChannelIds();
|
||||||
|
|
||||||
var databaseIds = _libraryManager.GetItemIds(new InternalItemsQuery
|
var uninstalledChannels = _libraryManager.GetItemList(new InternalItemsQuery
|
||||||
{
|
{
|
||||||
IncludeItemTypes = new[] { typeof(Channel).Name }
|
IncludeItemTypes = new[] { typeof(Channel).Name },
|
||||||
|
ExcludeItemIds = installedChannelIds.ToArray()
|
||||||
});
|
});
|
||||||
|
|
||||||
var invalidIds = databaseIds
|
foreach (var channel in uninstalledChannels)
|
||||||
.Except(installedChannelIds)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
foreach (var id in invalidIds)
|
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
CleanChannel(id, cancellationToken);
|
CleanChannel((Channel)channel, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CleanChannel(Guid id, CancellationToken cancellationToken)
|
private void CleanChannel(Channel channel, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Cleaning channel {0} from database", id);
|
_logger.LogInformation("Cleaning channel {0} from database", channel.Id);
|
||||||
|
|
||||||
// Delete all channel items
|
// Delete all channel items
|
||||||
var allIds = _libraryManager.GetItemIds(new InternalItemsQuery
|
var items = _libraryManager.GetItemList(new InternalItemsQuery
|
||||||
{
|
{
|
||||||
ChannelIds = new[] { id }
|
ChannelIds = new[] { channel.Id }
|
||||||
});
|
});
|
||||||
|
|
||||||
foreach (var deleteId in allIds)
|
foreach (var item in items)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
DeleteItem(deleteId);
|
_libraryManager.DeleteItem(item, new DeleteOptions
|
||||||
|
{
|
||||||
|
DeleteFileLocation = false
|
||||||
|
|
||||||
|
}, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, delete the channel itself
|
// Finally, delete the channel itself
|
||||||
DeleteItem(id);
|
_libraryManager.DeleteItem(channel, new DeleteOptions
|
||||||
}
|
|
||||||
|
|
||||||
private void DeleteItem(Guid id)
|
|
||||||
{
|
|
||||||
var item = _libraryManager.GetItemById(id);
|
|
||||||
|
|
||||||
if (item == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_libraryManager.DeleteItem(item, new DeleteOptions
|
|
||||||
{
|
{
|
||||||
DeleteFileLocation = false
|
DeleteFileLocation = false
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ using Microsoft.Extensions.Logging;
|
|||||||
|
|
||||||
namespace Emby.Server.Implementations.Channels
|
namespace Emby.Server.Implementations.Channels
|
||||||
{
|
{
|
||||||
class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask
|
public class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask
|
||||||
{
|
{
|
||||||
private readonly IChannelManager _channelManager;
|
private readonly IChannelManager _channelManager;
|
||||||
private readonly IUserManager _userManager;
|
private readonly IUserManager _userManager;
|
||||||
|
|||||||
@@ -10,14 +10,18 @@ using MediaBrowser.Controller.Entities.Movies;
|
|||||||
using MediaBrowser.Controller.Entities.TV;
|
using MediaBrowser.Controller.Entities.TV;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Extensions;
|
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Collections
|
namespace Emby.Server.Implementations.Collections
|
||||||
{
|
{
|
||||||
public class CollectionImageProvider : BaseDynamicImageProvider<BoxSet>
|
public class CollectionImageProvider : BaseDynamicImageProvider<BoxSet>
|
||||||
{
|
{
|
||||||
public CollectionImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor) : base(fileSystem, providerManager, applicationPaths, imageProcessor)
|
public CollectionImageProvider(
|
||||||
|
IFileSystem fileSystem,
|
||||||
|
IProviderManager providerManager,
|
||||||
|
IApplicationPaths applicationPaths,
|
||||||
|
IImageProcessor imageProcessor)
|
||||||
|
: base(fileSystem, providerManager, applicationPaths, imageProcessor)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +36,7 @@ namespace Emby.Server.Implementations.Collections
|
|||||||
return base.Supports(item);
|
return base.Supports(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override List<BaseItem> GetItemsWithImages(BaseItem item)
|
protected override IReadOnlyList<BaseItem> GetItemsWithImages(BaseItem item)
|
||||||
{
|
{
|
||||||
var playlist = (BoxSet)item;
|
var playlist = (BoxSet)item;
|
||||||
|
|
||||||
@@ -70,12 +74,13 @@ namespace Emby.Server.Implementations.Collections
|
|||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
.Where(i => i != null)
|
.Where(i => i != null)
|
||||||
.DistinctBy(i => i.Id)
|
.GroupBy(x => x.Id)
|
||||||
|
.Select(x => x.First())
|
||||||
.OrderBy(i => Guid.NewGuid())
|
.OrderBy(i => Guid.NewGuid())
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string CreateImage(BaseItem item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
|
protected override string CreateImage(BaseItem item, IReadOnlyCollection<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
|
||||||
{
|
{
|
||||||
return CreateSingleImage(itemsWithImages, outputPathWithoutExtension, ImageType.Primary);
|
return CreateSingleImage(itemsWithImages, outputPathWithoutExtension, ImageType.Primary);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ namespace Emby.Server.Implementations.Collections
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_fileSystem.CreateDirectory(path);
|
Directory.CreateDirectory(path);
|
||||||
|
|
||||||
var libraryOptions = new LibraryOptions
|
var libraryOptions = new LibraryOptions
|
||||||
{
|
{
|
||||||
@@ -133,7 +133,7 @@ namespace Emby.Server.Implementations.Collections
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_fileSystem.CreateDirectory(path);
|
Directory.CreateDirectory(path);
|
||||||
|
|
||||||
var collection = new BoxSet
|
var collection = new BoxSet
|
||||||
{
|
{
|
||||||
@@ -342,24 +342,22 @@ namespace Emby.Server.Implementations.Collections
|
|||||||
{
|
{
|
||||||
private readonly CollectionManager _collectionManager;
|
private readonly CollectionManager _collectionManager;
|
||||||
private readonly IServerConfigurationManager _config;
|
private readonly IServerConfigurationManager _config;
|
||||||
private readonly IFileSystem _fileSystem;
|
|
||||||
private ILogger _logger;
|
private ILogger _logger;
|
||||||
|
|
||||||
public CollectionManagerEntryPoint(ICollectionManager collectionManager, IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger)
|
public CollectionManagerEntryPoint(ICollectionManager collectionManager, IServerConfigurationManager config, ILogger logger)
|
||||||
{
|
{
|
||||||
_collectionManager = (CollectionManager)collectionManager;
|
_collectionManager = (CollectionManager)collectionManager;
|
||||||
_config = config;
|
_config = config;
|
||||||
_fileSystem = fileSystem;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void Run()
|
public async Task RunAsync()
|
||||||
{
|
{
|
||||||
if (!_config.Configuration.CollectionsUpgraded && _config.Configuration.IsStartupWizardCompleted)
|
if (!_config.Configuration.CollectionsUpgraded && _config.Configuration.IsStartupWizardCompleted)
|
||||||
{
|
{
|
||||||
var path = _collectionManager.GetCollectionsFolderPath();
|
var path = _collectionManager.GetCollectionsFolderPath();
|
||||||
|
|
||||||
if (_fileSystem.DirectoryExists(path))
|
if (Directory.Exists(path))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -74,23 +74,14 @@ namespace Emby.Server.Implementations.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpdateMetadataPath()
|
private void UpdateMetadataPath()
|
||||||
{
|
{
|
||||||
string metadataPath;
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(Configuration.MetadataPath))
|
if (string.IsNullOrWhiteSpace(Configuration.MetadataPath))
|
||||||
{
|
{
|
||||||
metadataPath = GetInternalMetadataPath();
|
((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = Path.Combine(ApplicationPaths.ProgramDataPath, "metadata");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
metadataPath = Path.Combine(Configuration.MetadataPath, "metadata");
|
((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = Configuration.MetadataPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = metadataPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetInternalMetadataPath()
|
|
||||||
{
|
|
||||||
return Path.Combine(ApplicationPaths.ProgramDataPath, "metadata");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -148,7 +139,7 @@ namespace Emby.Server.Implementations.Configuration
|
|||||||
&& !string.Equals(Configuration.CertificatePath ?? string.Empty, newPath))
|
&& !string.Equals(Configuration.CertificatePath ?? string.Empty, newPath))
|
||||||
{
|
{
|
||||||
// Validate
|
// Validate
|
||||||
if (!FileSystem.FileExists(newPath))
|
if (!File.Exists(newPath))
|
||||||
{
|
{
|
||||||
throw new FileNotFoundException(string.Format("Certificate file '{0}' does not exist.", newPath));
|
throw new FileNotFoundException(string.Format("Certificate file '{0}' does not exist.", newPath));
|
||||||
}
|
}
|
||||||
@@ -168,7 +159,7 @@ namespace Emby.Server.Implementations.Configuration
|
|||||||
&& !string.Equals(Configuration.MetadataPath ?? string.Empty, newPath))
|
&& !string.Equals(Configuration.MetadataPath ?? string.Empty, newPath))
|
||||||
{
|
{
|
||||||
// Validate
|
// Validate
|
||||||
if (!FileSystem.DirectoryExists(newPath))
|
if (!Directory.Exists(newPath))
|
||||||
{
|
{
|
||||||
throw new FileNotFoundException(string.Format("{0} does not exist.", newPath));
|
throw new FileNotFoundException(string.Format("{0} does not exist.", newPath));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations
|
||||||
|
{
|
||||||
|
public static class ConfigurationOptions
|
||||||
|
{
|
||||||
|
public static readonly Dictionary<string, string> Configuration = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{"HttpListenerHost:DefaultRedirectPath", "web/index.html"},
|
||||||
|
{"MusicBrainz:BaseUrl", "https://www.musicbrainz.org"}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -8,6 +10,39 @@ namespace Emby.Server.Implementations.Cryptography
|
|||||||
{
|
{
|
||||||
public class CryptographyProvider : ICryptoProvider
|
public class CryptographyProvider : ICryptoProvider
|
||||||
{
|
{
|
||||||
|
private static readonly HashSet<string> _supportedHashMethods = new HashSet<string>()
|
||||||
|
{
|
||||||
|
"MD5",
|
||||||
|
"System.Security.Cryptography.MD5",
|
||||||
|
"SHA",
|
||||||
|
"SHA1",
|
||||||
|
"System.Security.Cryptography.SHA1",
|
||||||
|
"SHA256",
|
||||||
|
"SHA-256",
|
||||||
|
"System.Security.Cryptography.SHA256",
|
||||||
|
"SHA384",
|
||||||
|
"SHA-384",
|
||||||
|
"System.Security.Cryptography.SHA384",
|
||||||
|
"SHA512",
|
||||||
|
"SHA-512",
|
||||||
|
"System.Security.Cryptography.SHA512"
|
||||||
|
};
|
||||||
|
|
||||||
|
public string DefaultHashMethod => "PBKDF2";
|
||||||
|
|
||||||
|
private RandomNumberGenerator _randomNumberGenerator;
|
||||||
|
|
||||||
|
private const int _defaultIterations = 1000;
|
||||||
|
|
||||||
|
public CryptographyProvider()
|
||||||
|
{
|
||||||
|
//FIXME: When we get DotNet Standard 2.1 we need to revisit how we do the crypto
|
||||||
|
//Currently supported hash methods from https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptoconfig?view=netcore-2.1
|
||||||
|
//there might be a better way to autogenerate this list as dotnet updates, but I couldn't find one
|
||||||
|
//Please note the default method of PBKDF2 is not included, it cannot be used to generate hashes cleanly as it is actually a pbkdf with sha1
|
||||||
|
_randomNumberGenerator = RandomNumberGenerator.Create();
|
||||||
|
}
|
||||||
|
|
||||||
public Guid GetMD5(string str)
|
public Guid GetMD5(string str)
|
||||||
{
|
{
|
||||||
return new Guid(ComputeMD5(Encoding.Unicode.GetBytes(str)));
|
return new Guid(ComputeMD5(Encoding.Unicode.GetBytes(str)));
|
||||||
@@ -36,5 +71,98 @@ namespace Emby.Server.Implementations.Cryptography
|
|||||||
return provider.ComputeHash(bytes);
|
return provider.ComputeHash(bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IEnumerable<string> GetSupportedHashMethods()
|
||||||
|
{
|
||||||
|
return _supportedHashMethods;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] PBKDF2(string method, byte[] bytes, byte[] salt, int iterations)
|
||||||
|
{
|
||||||
|
//downgrading for now as we need this library to be dotnetstandard compliant
|
||||||
|
//with this downgrade we'll add a check to make sure we're on the downgrade method at the moment
|
||||||
|
if (method == DefaultHashMethod)
|
||||||
|
{
|
||||||
|
using (var r = new Rfc2898DeriveBytes(bytes, salt, iterations))
|
||||||
|
{
|
||||||
|
return r.GetBytes(32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new CryptographicException($"Cannot currently use PBKDF2 with requested hash method: {method}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] ComputeHash(string hashMethod, byte[] bytes)
|
||||||
|
{
|
||||||
|
return ComputeHash(hashMethod, bytes, Array.Empty<byte>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] ComputeHashWithDefaultMethod(byte[] bytes)
|
||||||
|
{
|
||||||
|
return ComputeHash(DefaultHashMethod, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] ComputeHash(string hashMethod, byte[] bytes, byte[] salt)
|
||||||
|
{
|
||||||
|
if (hashMethod == DefaultHashMethod)
|
||||||
|
{
|
||||||
|
return PBKDF2(hashMethod, bytes, salt, _defaultIterations);
|
||||||
|
}
|
||||||
|
else if (_supportedHashMethods.Contains(hashMethod))
|
||||||
|
{
|
||||||
|
using (var h = HashAlgorithm.Create(hashMethod))
|
||||||
|
{
|
||||||
|
if (salt.Length == 0)
|
||||||
|
{
|
||||||
|
return h.ComputeHash(bytes);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
byte[] salted = new byte[bytes.Length + salt.Length];
|
||||||
|
Array.Copy(bytes, salted, bytes.Length);
|
||||||
|
Array.Copy(salt, 0, salted, bytes.Length, salt.Length);
|
||||||
|
return h.ComputeHash(salted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new CryptographicException($"Requested hash method is not supported: {hashMethod}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] ComputeHashWithDefaultMethod(byte[] bytes, byte[] salt)
|
||||||
|
{
|
||||||
|
return PBKDF2(DefaultHashMethod, bytes, salt, _defaultIterations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] ComputeHash(PasswordHash hash)
|
||||||
|
{
|
||||||
|
int iterations = _defaultIterations;
|
||||||
|
if (!hash.Parameters.ContainsKey("iterations"))
|
||||||
|
{
|
||||||
|
hash.Parameters.Add("iterations", _defaultIterations.ToString(CultureInfo.InvariantCulture));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
iterations = int.Parse(hash.Parameters["iterations"]);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new InvalidDataException($"Couldn't successfully parse iterations value from string: {hash.Parameters["iterations"]}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return PBKDF2(hash.Id, hash.HashBytes, hash.SaltBytes, iterations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] GenerateSalt()
|
||||||
|
{
|
||||||
|
byte[] salt = new byte[64];
|
||||||
|
_randomNumberGenerator.GetBytes(salt);
|
||||||
|
return salt;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ namespace Emby.Server.Implementations.Data
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
db.ExecuteAll(string.Join(";", queries.ToArray()));
|
db.ExecuteAll(string.Join(";", queries));
|
||||||
Logger.LogInformation("PRAGMA synchronous=" + db.Query("PRAGMA synchronous").SelectScalarString().First());
|
Logger.LogInformation("PRAGMA synchronous=" + db.Query("PRAGMA synchronous").SelectScalarString().First());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,23 +232,6 @@ namespace Emby.Server.Implementations.Data
|
|||||||
|
|
||||||
protected virtual int? CacheSize => null;
|
protected virtual int? CacheSize => null;
|
||||||
|
|
||||||
internal static void CheckOk(int rc)
|
|
||||||
{
|
|
||||||
string msg = "";
|
|
||||||
|
|
||||||
if (raw.SQLITE_OK != rc)
|
|
||||||
{
|
|
||||||
throw CreateException((ErrorCode)rc, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static Exception CreateException(ErrorCode rc, string msg)
|
|
||||||
{
|
|
||||||
var exp = new Exception(msg);
|
|
||||||
|
|
||||||
return exp;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
protected void CheckDisposed()
|
protected void CheckDisposed()
|
||||||
{
|
{
|
||||||
@@ -375,13 +358,6 @@ namespace Emby.Server.Implementations.Data
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DummyToken : IDisposable
|
|
||||||
{
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IDisposable Read(this ReaderWriterLockSlim obj)
|
public static IDisposable Read(this ReaderWriterLockSlim obj)
|
||||||
{
|
{
|
||||||
//if (BaseSqliteRepository.ThreadSafeMode > 0)
|
//if (BaseSqliteRepository.ThreadSafeMode > 0)
|
||||||
@@ -390,6 +366,7 @@ namespace Emby.Server.Implementations.Data
|
|||||||
//}
|
//}
|
||||||
return new WriteLockToken(obj);
|
return new WriteLockToken(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IDisposable Write(this ReaderWriterLockSlim obj)
|
public static IDisposable Write(this ReaderWriterLockSlim obj)
|
||||||
{
|
{
|
||||||
//if (BaseSqliteRepository.ThreadSafeMode > 0)
|
//if (BaseSqliteRepository.ThreadSafeMode > 0)
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Common.Configuration;
|
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Persistence;
|
|
||||||
using MediaBrowser.Model.IO;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Data
|
namespace Emby.Server.Implementations.Data
|
||||||
@@ -13,18 +10,12 @@ namespace Emby.Server.Implementations.Data
|
|||||||
public class CleanDatabaseScheduledTask : ILibraryPostScanTask
|
public class CleanDatabaseScheduledTask : ILibraryPostScanTask
|
||||||
{
|
{
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
private readonly IItemRepository _itemRepo;
|
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IFileSystem _fileSystem;
|
|
||||||
private readonly IApplicationPaths _appPaths;
|
|
||||||
|
|
||||||
public CleanDatabaseScheduledTask(ILibraryManager libraryManager, IItemRepository itemRepo, ILogger logger, IFileSystem fileSystem, IApplicationPaths appPaths)
|
public CleanDatabaseScheduledTask(ILibraryManager libraryManager, ILogger logger)
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
_itemRepo = itemRepo;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_fileSystem = fileSystem;
|
|
||||||
_appPaths = appPaths;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
|
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
|
||||||
|
|||||||
@@ -90,9 +90,10 @@ namespace Emby.Server.Implementations.Data
|
|||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(displayPreferences));
|
throw new ArgumentNullException(nameof(displayPreferences));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(displayPreferences.Id))
|
if (string.IsNullOrEmpty(displayPreferences.Id))
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(displayPreferences.Id));
|
throw new ArgumentException("Display preferences has an invalid Id", nameof(displayPreferences));
|
||||||
}
|
}
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -119,9 +119,9 @@ namespace Emby.Server.Implementations.Data
|
|||||||
{
|
{
|
||||||
list.Add(row[0].ReadGuidFromBlob());
|
list.Add(row[0].ReadGuidFromBlob());
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
Logger.LogError(ex, "Error while getting user");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,8 @@ namespace Emby.Server.Implementations.Data
|
|||||||
{
|
{
|
||||||
TryMigrateToLocalUsersTable(connection);
|
TryMigrateToLocalUsersTable(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RemoveEmptyPasswordHashes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +75,38 @@ namespace Emby.Server.Implementations.Data
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RemoveEmptyPasswordHashes()
|
||||||
|
{
|
||||||
|
foreach (var user in RetrieveAllUsers())
|
||||||
|
{
|
||||||
|
// If the user password is the sha1 hash of the empty string, remove it
|
||||||
|
if (!string.Equals(user.Password, "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal)
|
||||||
|
&& !string.Equals(user.Password, "$SHA1$DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
user.Password = null;
|
||||||
|
var serialized = _jsonSerializer.SerializeToBytes(user);
|
||||||
|
|
||||||
|
using (WriteLock.Write())
|
||||||
|
using (var connection = CreateConnection())
|
||||||
|
{
|
||||||
|
connection.RunInTransaction(db =>
|
||||||
|
{
|
||||||
|
using (var statement = db.PrepareStatement("update LocalUsersv2 set data=@data where Id=@InternalId"))
|
||||||
|
{
|
||||||
|
statement.TryBind("@InternalId", user.InternalId);
|
||||||
|
statement.TryBind("@data", serialized);
|
||||||
|
statement.MoveNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
}, TransactionMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Save a user in the repo
|
/// Save a user in the repo
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using MediaBrowser.Model.Reflection;
|
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Data
|
namespace Emby.Server.Implementations.Data
|
||||||
{
|
{
|
||||||
@@ -10,16 +9,13 @@ namespace Emby.Server.Implementations.Data
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class TypeMapper
|
public class TypeMapper
|
||||||
{
|
{
|
||||||
private readonly IAssemblyInfo _assemblyInfo;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This holds all the types in the running assemblies so that we can de-serialize properly when we don't have strong types
|
/// This holds all the types in the running assemblies so that we can de-serialize properly when we don't have strong types
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ConcurrentDictionary<string, Type> _typeMap = new ConcurrentDictionary<string, Type>();
|
private readonly ConcurrentDictionary<string, Type> _typeMap = new ConcurrentDictionary<string, Type>();
|
||||||
|
|
||||||
public TypeMapper(IAssemblyInfo assemblyInfo)
|
public TypeMapper()
|
||||||
{
|
{
|
||||||
_assemblyInfo = assemblyInfo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -45,8 +41,7 @@ namespace Emby.Server.Implementations.Data
|
|||||||
/// <returns>Type.</returns>
|
/// <returns>Type.</returns>
|
||||||
private Type LookupType(string typeName)
|
private Type LookupType(string typeName)
|
||||||
{
|
{
|
||||||
return _assemblyInfo
|
return AppDomain.CurrentDomain.GetAssemblies()
|
||||||
.GetCurrentAssemblies()
|
|
||||||
.Select(a => a.GetType(typeName))
|
.Select(a => a.GetType(typeName))
|
||||||
.FirstOrDefault(t => t != null);
|
.FirstOrDefault(t => t != null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ namespace Emby.Server.Implementations.Devices
|
|||||||
{
|
{
|
||||||
private readonly IApplicationPaths _appPaths;
|
private readonly IApplicationPaths _appPaths;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IFileSystem _fileSystem;
|
|
||||||
|
|
||||||
private readonly object _syncLock = new object();
|
private readonly object _syncLock = new object();
|
||||||
|
|
||||||
@@ -53,11 +52,11 @@ namespace Emby.Server.Implementations.Devices
|
|||||||
{
|
{
|
||||||
var path = CachePath;
|
var path = CachePath;
|
||||||
|
|
||||||
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
|
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
|
|
||||||
lock (_syncLock)
|
lock (_syncLock)
|
||||||
{
|
{
|
||||||
_fileSystem.WriteAllText(path, id, Encoding.UTF8);
|
File.WriteAllText(path, id, Encoding.UTF8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -86,19 +85,10 @@ namespace Emby.Server.Implementations.Devices
|
|||||||
|
|
||||||
private string _id;
|
private string _id;
|
||||||
|
|
||||||
public DeviceId(
|
public DeviceId(IApplicationPaths appPaths, ILoggerFactory loggerFactory)
|
||||||
IApplicationPaths appPaths,
|
|
||||||
ILoggerFactory loggerFactory,
|
|
||||||
IFileSystem fileSystem)
|
|
||||||
{
|
{
|
||||||
if (fileSystem == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(fileSystem));
|
|
||||||
}
|
|
||||||
|
|
||||||
_appPaths = appPaths;
|
_appPaths = appPaths;
|
||||||
_logger = loggerFactory.CreateLogger("SystemId");
|
_logger = loggerFactory.CreateLogger("SystemId");
|
||||||
_fileSystem = fileSystem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Value => _id ?? (_id = GetDeviceId());
|
public string Value => _id ?? (_id = GetDeviceId());
|
||||||
|
|||||||
@@ -34,8 +34,6 @@ namespace Emby.Server.Implementations.Devices
|
|||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
private readonly ILibraryMonitor _libraryMonitor;
|
private readonly ILibraryMonitor _libraryMonitor;
|
||||||
private readonly IServerConfigurationManager _config;
|
private readonly IServerConfigurationManager _config;
|
||||||
private readonly ILogger _logger;
|
|
||||||
private readonly INetworkManager _network;
|
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
private readonly ILocalizationManager _localizationManager;
|
private readonly ILocalizationManager _localizationManager;
|
||||||
|
|
||||||
@@ -55,17 +53,13 @@ namespace Emby.Server.Implementations.Devices
|
|||||||
IUserManager userManager,
|
IUserManager userManager,
|
||||||
IFileSystem fileSystem,
|
IFileSystem fileSystem,
|
||||||
ILibraryMonitor libraryMonitor,
|
ILibraryMonitor libraryMonitor,
|
||||||
IServerConfigurationManager config,
|
IServerConfigurationManager config)
|
||||||
ILoggerFactory loggerFactory,
|
|
||||||
INetworkManager network)
|
|
||||||
{
|
{
|
||||||
_json = json;
|
_json = json;
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
_libraryMonitor = libraryMonitor;
|
_libraryMonitor = libraryMonitor;
|
||||||
_config = config;
|
_config = config;
|
||||||
_logger = loggerFactory.CreateLogger(nameof(DeviceManager));
|
|
||||||
_network = network;
|
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
_localizationManager = localizationManager;
|
_localizationManager = localizationManager;
|
||||||
_authRepo = authRepo;
|
_authRepo = authRepo;
|
||||||
@@ -76,7 +70,7 @@ namespace Emby.Server.Implementations.Devices
|
|||||||
public void SaveCapabilities(string deviceId, ClientCapabilities capabilities)
|
public void SaveCapabilities(string deviceId, ClientCapabilities capabilities)
|
||||||
{
|
{
|
||||||
var path = Path.Combine(GetDevicePath(deviceId), "capabilities.json");
|
var path = Path.Combine(GetDevicePath(deviceId), "capabilities.json");
|
||||||
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
|
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
|
|
||||||
lock (_capabilitiesSyncLock)
|
lock (_capabilitiesSyncLock)
|
||||||
{
|
{
|
||||||
@@ -239,7 +233,7 @@ namespace Emby.Server.Implementations.Devices
|
|||||||
path = Path.Combine(path, file.Name);
|
path = Path.Combine(path, file.Name);
|
||||||
path = Path.ChangeExtension(path, MimeTypes.ToExtension(file.MimeType) ?? "jpg");
|
path = Path.ChangeExtension(path, MimeTypes.ToExtension(file.MimeType) ?? "jpg");
|
||||||
|
|
||||||
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
|
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
|
|
||||||
await EnsureLibraryFolder(uploadPathInfo.Item2, uploadPathInfo.Item3).ConfigureAwait(false);
|
await EnsureLibraryFolder(uploadPathInfo.Item2, uploadPathInfo.Item3).ConfigureAwait(false);
|
||||||
|
|
||||||
@@ -275,7 +269,7 @@ namespace Emby.Server.Implementations.Devices
|
|||||||
private void AddCameraUpload(string deviceId, LocalFileInfo file)
|
private void AddCameraUpload(string deviceId, LocalFileInfo file)
|
||||||
{
|
{
|
||||||
var path = Path.Combine(GetDevicePath(deviceId), "camerauploads.json");
|
var path = Path.Combine(GetDevicePath(deviceId), "camerauploads.json");
|
||||||
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
|
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
|
|
||||||
lock (_cameraUploadSyncLock)
|
lock (_cameraUploadSyncLock)
|
||||||
{
|
{
|
||||||
@@ -317,7 +311,7 @@ namespace Emby.Server.Implementations.Devices
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
_fileSystem.CreateDirectory(path);
|
Directory.CreateDirectory(path);
|
||||||
|
|
||||||
var libraryOptions = new LibraryOptions
|
var libraryOptions = new LibraryOptions
|
||||||
{
|
{
|
||||||
@@ -414,24 +408,22 @@ namespace Emby.Server.Implementations.Devices
|
|||||||
{
|
{
|
||||||
private readonly DeviceManager _deviceManager;
|
private readonly DeviceManager _deviceManager;
|
||||||
private readonly IServerConfigurationManager _config;
|
private readonly IServerConfigurationManager _config;
|
||||||
private readonly IFileSystem _fileSystem;
|
|
||||||
private ILogger _logger;
|
private ILogger _logger;
|
||||||
|
|
||||||
public DeviceManagerEntryPoint(IDeviceManager deviceManager, IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger)
|
public DeviceManagerEntryPoint(IDeviceManager deviceManager, IServerConfigurationManager config, ILogger logger)
|
||||||
{
|
{
|
||||||
_deviceManager = (DeviceManager)deviceManager;
|
_deviceManager = (DeviceManager)deviceManager;
|
||||||
_config = config;
|
_config = config;
|
||||||
_fileSystem = fileSystem;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void Run()
|
public async Task RunAsync()
|
||||||
{
|
{
|
||||||
if (!_config.Configuration.CameraUploadUpgraded && _config.Configuration.IsStartupWizardCompleted)
|
if (!_config.Configuration.CameraUploadUpgraded && _config.Configuration.IsStartupWizardCompleted)
|
||||||
{
|
{
|
||||||
var path = _deviceManager.GetUploadsPath();
|
var path = _deviceManager.GetUploadsPath();
|
||||||
|
|
||||||
if (_fileSystem.DirectoryExists(path))
|
if (Directory.Exists(path))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ namespace Emby.Server.Implementations.Diagnostics
|
|||||||
{
|
{
|
||||||
public class CommonProcess : IProcess
|
public class CommonProcess : IProcess
|
||||||
{
|
{
|
||||||
public event EventHandler Exited;
|
|
||||||
|
|
||||||
private readonly ProcessOptions _options;
|
|
||||||
private readonly Process _process;
|
private readonly Process _process;
|
||||||
|
|
||||||
|
private bool _disposed = false;
|
||||||
|
private bool _hasExited;
|
||||||
|
|
||||||
public CommonProcess(ProcessOptions options)
|
public CommonProcess(ProcessOptions options)
|
||||||
{
|
{
|
||||||
_options = options;
|
StartInfo = options;
|
||||||
|
|
||||||
var startInfo = new ProcessStartInfo
|
var startInfo = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
@@ -27,10 +27,10 @@ namespace Emby.Server.Implementations.Diagnostics
|
|||||||
CreateNoWindow = options.CreateNoWindow,
|
CreateNoWindow = options.CreateNoWindow,
|
||||||
RedirectStandardError = options.RedirectStandardError,
|
RedirectStandardError = options.RedirectStandardError,
|
||||||
RedirectStandardInput = options.RedirectStandardInput,
|
RedirectStandardInput = options.RedirectStandardInput,
|
||||||
RedirectStandardOutput = options.RedirectStandardOutput
|
RedirectStandardOutput = options.RedirectStandardOutput,
|
||||||
|
ErrorDialog = options.ErrorDialog
|
||||||
};
|
};
|
||||||
|
|
||||||
startInfo.ErrorDialog = options.ErrorDialog;
|
|
||||||
|
|
||||||
if (options.IsHidden)
|
if (options.IsHidden)
|
||||||
{
|
{
|
||||||
@@ -45,11 +45,22 @@ namespace Emby.Server.Implementations.Diagnostics
|
|||||||
if (options.EnableRaisingEvents)
|
if (options.EnableRaisingEvents)
|
||||||
{
|
{
|
||||||
_process.EnableRaisingEvents = true;
|
_process.EnableRaisingEvents = true;
|
||||||
_process.Exited += _process_Exited;
|
_process.Exited += OnProcessExited;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _hasExited;
|
public event EventHandler Exited;
|
||||||
|
|
||||||
|
public ProcessOptions StartInfo { get; }
|
||||||
|
|
||||||
|
public StreamWriter StandardInput => _process.StandardInput;
|
||||||
|
|
||||||
|
public StreamReader StandardError => _process.StandardError;
|
||||||
|
|
||||||
|
public StreamReader StandardOutput => _process.StandardOutput;
|
||||||
|
|
||||||
|
public int ExitCode => _process.ExitCode;
|
||||||
|
|
||||||
private bool HasExited
|
private bool HasExited
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -72,25 +83,6 @@ namespace Emby.Server.Implementations.Diagnostics
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void _process_Exited(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
_hasExited = true;
|
|
||||||
if (Exited != null)
|
|
||||||
{
|
|
||||||
Exited(this, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProcessOptions StartInfo => _options;
|
|
||||||
|
|
||||||
public StreamWriter StandardInput => _process.StandardInput;
|
|
||||||
|
|
||||||
public StreamReader StandardError => _process.StandardError;
|
|
||||||
|
|
||||||
public StreamReader StandardOutput => _process.StandardOutput;
|
|
||||||
|
|
||||||
public int ExitCode => _process.ExitCode;
|
|
||||||
|
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
_process.Start();
|
_process.Start();
|
||||||
@@ -105,10 +97,10 @@ namespace Emby.Server.Implementations.Diagnostics
|
|||||||
{
|
{
|
||||||
return _process.WaitForExit(timeMs);
|
return _process.WaitForExit(timeMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<bool> WaitForExitAsync(int timeMs)
|
public Task<bool> WaitForExitAsync(int timeMs)
|
||||||
{
|
{
|
||||||
//Note: For this function to work correctly, the option EnableRisingEvents needs to be set to true.
|
// Note: For this function to work correctly, the option EnableRisingEvents needs to be set to true.
|
||||||
|
|
||||||
if (HasExited)
|
if (HasExited)
|
||||||
{
|
{
|
||||||
@@ -130,7 +122,29 @@ namespace Emby.Server.Implementations.Diagnostics
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_process.Dispose();
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_process?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnProcessExited(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
_hasExited = true;
|
||||||
|
Exited?.Invoke(this, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ using System.Linq;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Common;
|
using MediaBrowser.Common;
|
||||||
using MediaBrowser.Controller.Channels;
|
using MediaBrowser.Controller.Channels;
|
||||||
using MediaBrowser.Controller.Configuration;
|
|
||||||
using MediaBrowser.Controller.Devices;
|
|
||||||
using MediaBrowser.Controller.Drawing;
|
using MediaBrowser.Controller.Drawing;
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
@@ -21,8 +19,6 @@ using MediaBrowser.Controller.Providers;
|
|||||||
using MediaBrowser.Model.Drawing;
|
using MediaBrowser.Model.Drawing;
|
||||||
using MediaBrowser.Model.Dto;
|
using MediaBrowser.Model.Dto;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Extensions;
|
|
||||||
using MediaBrowser.Model.IO;
|
|
||||||
using MediaBrowser.Model.Querying;
|
using MediaBrowser.Model.Querying;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
@@ -36,13 +32,9 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
private readonly IItemRepository _itemRepo;
|
private readonly IItemRepository _itemRepo;
|
||||||
|
|
||||||
private readonly IImageProcessor _imageProcessor;
|
private readonly IImageProcessor _imageProcessor;
|
||||||
private readonly IServerConfigurationManager _config;
|
|
||||||
private readonly IFileSystem _fileSystem;
|
|
||||||
private readonly IProviderManager _providerManager;
|
private readonly IProviderManager _providerManager;
|
||||||
|
|
||||||
private readonly Func<IChannelManager> _channelManagerFactory;
|
|
||||||
private readonly IApplicationHost _appHost;
|
private readonly IApplicationHost _appHost;
|
||||||
private readonly Func<IDeviceManager> _deviceManager;
|
|
||||||
private readonly Func<IMediaSourceManager> _mediaSourceManager;
|
private readonly Func<IMediaSourceManager> _mediaSourceManager;
|
||||||
private readonly Func<ILiveTvManager> _livetvManager;
|
private readonly Func<ILiveTvManager> _livetvManager;
|
||||||
|
|
||||||
@@ -52,12 +44,8 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
IUserDataManager userDataRepository,
|
IUserDataManager userDataRepository,
|
||||||
IItemRepository itemRepo,
|
IItemRepository itemRepo,
|
||||||
IImageProcessor imageProcessor,
|
IImageProcessor imageProcessor,
|
||||||
IServerConfigurationManager config,
|
|
||||||
IFileSystem fileSystem,
|
|
||||||
IProviderManager providerManager,
|
IProviderManager providerManager,
|
||||||
Func<IChannelManager> channelManagerFactory,
|
|
||||||
IApplicationHost appHost,
|
IApplicationHost appHost,
|
||||||
Func<IDeviceManager> deviceManager,
|
|
||||||
Func<IMediaSourceManager> mediaSourceManager,
|
Func<IMediaSourceManager> mediaSourceManager,
|
||||||
Func<ILiveTvManager> livetvManager)
|
Func<ILiveTvManager> livetvManager)
|
||||||
{
|
{
|
||||||
@@ -66,12 +54,8 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
_userDataRepository = userDataRepository;
|
_userDataRepository = userDataRepository;
|
||||||
_itemRepo = itemRepo;
|
_itemRepo = itemRepo;
|
||||||
_imageProcessor = imageProcessor;
|
_imageProcessor = imageProcessor;
|
||||||
_config = config;
|
|
||||||
_fileSystem = fileSystem;
|
|
||||||
_providerManager = providerManager;
|
_providerManager = providerManager;
|
||||||
_channelManagerFactory = channelManagerFactory;
|
|
||||||
_appHost = appHost;
|
_appHost = appHost;
|
||||||
_deviceManager = deviceManager;
|
|
||||||
_mediaSourceManager = mediaSourceManager;
|
_mediaSourceManager = mediaSourceManager;
|
||||||
_livetvManager = livetvManager;
|
_livetvManager = livetvManager;
|
||||||
}
|
}
|
||||||
@@ -95,15 +79,8 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
return GetBaseItemDto(item, options, user, owner);
|
return GetBaseItemDto(item, options, user, owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BaseItemDto[] GetBaseItemDtos(List<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null)
|
public BaseItemDto[] GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null)
|
||||||
{
|
=> GetBaseItemDtos(items, items.Count, options, user, owner);
|
||||||
return GetBaseItemDtos(items, items.Count, options, user, owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BaseItemDto[] GetBaseItemDtos(BaseItem[] items, DtoOptions options, User user = null, BaseItem owner = null)
|
|
||||||
{
|
|
||||||
return GetBaseItemDtos(items, items.Length, options, user, owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BaseItemDto[] GetBaseItemDtos(IEnumerable<BaseItem> items, int itemCount, DtoOptions options, User user = null, BaseItem owner = null)
|
public BaseItemDto[] GetBaseItemDtos(IEnumerable<BaseItem> items, int itemCount, DtoOptions options, User user = null, BaseItem owner = null)
|
||||||
{
|
{
|
||||||
@@ -374,10 +351,6 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
dto.MusicVideoCount = taggedItems.Count(i => i is MusicVideo);
|
dto.MusicVideoCount = taggedItems.Count(i => i is MusicVideo);
|
||||||
dto.SongCount = taggedItems.Count(i => i is Audio);
|
dto.SongCount = taggedItems.Count(i => i is Audio);
|
||||||
}
|
}
|
||||||
else if (item is GameGenre)
|
|
||||||
{
|
|
||||||
dto.GameCount = taggedItems.Count(i => i is Game);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// This populates them all and covers Genre, Person, Studio, Year
|
// This populates them all and covers Genre, Person, Studio, Year
|
||||||
@@ -385,7 +358,6 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
dto.ArtistCount = taggedItems.Count(i => i is MusicArtist);
|
dto.ArtistCount = taggedItems.Count(i => i is MusicArtist);
|
||||||
dto.AlbumCount = taggedItems.Count(i => i is MusicAlbum);
|
dto.AlbumCount = taggedItems.Count(i => i is MusicAlbum);
|
||||||
dto.EpisodeCount = taggedItems.Count(i => i is Episode);
|
dto.EpisodeCount = taggedItems.Count(i => i is Episode);
|
||||||
dto.GameCount = taggedItems.Count(i => i is Game);
|
|
||||||
dto.MovieCount = taggedItems.Count(i => i is Movie);
|
dto.MovieCount = taggedItems.Count(i => i is Movie);
|
||||||
dto.TrailerCount = taggedItems.Count(i => i is Trailer);
|
dto.TrailerCount = taggedItems.Count(i => i is Trailer);
|
||||||
dto.MusicVideoCount = taggedItems.Count(i => i is MusicVideo);
|
dto.MusicVideoCount = taggedItems.Count(i => i is MusicVideo);
|
||||||
@@ -532,17 +504,6 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
dto.Album = item.Album;
|
dto.Album = item.Album;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SetGameProperties(BaseItemDto dto, Game item)
|
|
||||||
{
|
|
||||||
dto.GameSystem = item.GameSystem;
|
|
||||||
dto.MultiPartGameFiles = item.MultiPartGameFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SetGameSystemProperties(BaseItemDto dto, GameSystem item)
|
|
||||||
{
|
|
||||||
dto.GameSystem = item.GameSystemName;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string[] GetImageTags(BaseItem item, List<ItemImageInfo> images)
|
private string[] GetImageTags(BaseItem item, List<ItemImageInfo> images)
|
||||||
{
|
{
|
||||||
return images
|
return images
|
||||||
@@ -636,7 +597,8 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
}
|
}
|
||||||
|
|
||||||
}).Where(i => i != null)
|
}).Where(i => i != null)
|
||||||
.DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
|
.GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.Select(x => x.First())
|
||||||
.ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase);
|
.ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
for (var i = 0; i < people.Count; i++)
|
for (var i = 0; i < people.Count; i++)
|
||||||
@@ -698,11 +660,6 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
return _libraryManager.GetMusicGenreId(name);
|
return _libraryManager.GetMusicGenreId(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (owner is Game || owner is GameSystem)
|
|
||||||
{
|
|
||||||
return _libraryManager.GetGameGenreId(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _libraryManager.GetGenreId(name);
|
return _libraryManager.GetGenreId(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1206,20 +1163,6 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var game = item as Game;
|
|
||||||
|
|
||||||
if (game != null)
|
|
||||||
{
|
|
||||||
SetGameProperties(dto, game);
|
|
||||||
}
|
|
||||||
|
|
||||||
var gameSystem = item as GameSystem;
|
|
||||||
|
|
||||||
if (gameSystem != null)
|
|
||||||
{
|
|
||||||
SetGameSystemProperties(dto, gameSystem);
|
|
||||||
}
|
|
||||||
|
|
||||||
var musicVideo = item as MusicVideo;
|
var musicVideo = item as MusicVideo;
|
||||||
if (musicVideo != null)
|
if (musicVideo != null)
|
||||||
{
|
{
|
||||||
@@ -1428,7 +1371,7 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
|
|
||||||
var supportedEnhancers = _imageProcessor.GetSupportedEnhancers(item, ImageType.Primary);
|
var supportedEnhancers = _imageProcessor.GetSupportedEnhancers(item, ImageType.Primary);
|
||||||
|
|
||||||
ImageSize size;
|
ImageDimensions size;
|
||||||
|
|
||||||
var defaultAspectRatio = item.GetDefaultPrimaryImageAspectRatio();
|
var defaultAspectRatio = item.GetDefaultPrimaryImageAspectRatio();
|
||||||
|
|
||||||
@@ -1439,9 +1382,9 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
return defaultAspectRatio;
|
return defaultAspectRatio;
|
||||||
}
|
}
|
||||||
|
|
||||||
double dummyWidth = 200;
|
int dummyWidth = 200;
|
||||||
double dummyHeight = dummyWidth / defaultAspectRatio;
|
int dummyHeight = Convert.ToInt32(dummyWidth / defaultAspectRatio);
|
||||||
size = new ImageSize(dummyWidth, dummyHeight);
|
size = new ImageDimensions(dummyWidth, dummyHeight);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1452,7 +1395,7 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
size = _imageProcessor.GetImageSize(item, imageInfo);
|
size = _imageProcessor.GetImageDimensions(item, imageInfo);
|
||||||
|
|
||||||
if (size.Width <= 0 || size.Height <= 0)
|
if (size.Width <= 0 || size.Height <= 0)
|
||||||
{
|
{
|
||||||
@@ -1481,7 +1424,7 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
var width = size.Width;
|
var width = size.Width;
|
||||||
var height = size.Height;
|
var height = size.Height;
|
||||||
|
|
||||||
if (width.Equals(0) || height.Equals(0))
|
if (width <= 0 || height <= 0)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Emby.Naming\Emby.Naming.csproj" />
|
<ProjectReference Include="..\Emby.Naming\Emby.Naming.csproj" />
|
||||||
@@ -9,12 +9,10 @@
|
|||||||
<ProjectReference Include="..\MediaBrowser.Providers\MediaBrowser.Providers.csproj" />
|
<ProjectReference Include="..\MediaBrowser.Providers\MediaBrowser.Providers.csproj" />
|
||||||
<ProjectReference Include="..\MediaBrowser.WebDashboard\MediaBrowser.WebDashboard.csproj" />
|
<ProjectReference Include="..\MediaBrowser.WebDashboard\MediaBrowser.WebDashboard.csproj" />
|
||||||
<ProjectReference Include="..\MediaBrowser.XbmcMetadata\MediaBrowser.XbmcMetadata.csproj" />
|
<ProjectReference Include="..\MediaBrowser.XbmcMetadata\MediaBrowser.XbmcMetadata.csproj" />
|
||||||
<ProjectReference Include="..\SocketHttpListener\SocketHttpListener.csproj" />
|
|
||||||
<ProjectReference Include="..\Emby.Dlna\Emby.Dlna.csproj" />
|
<ProjectReference Include="..\Emby.Dlna\Emby.Dlna.csproj" />
|
||||||
<ProjectReference Include="..\Mono.Nat\Mono.Nat.csproj" />
|
<ProjectReference Include="..\Mono.Nat\Mono.Nat.csproj" />
|
||||||
<ProjectReference Include="..\MediaBrowser.Api\MediaBrowser.Api.csproj" />
|
<ProjectReference Include="..\MediaBrowser.Api\MediaBrowser.Api.csproj" />
|
||||||
<ProjectReference Include="..\MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj" />
|
<ProjectReference Include="..\MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj" />
|
||||||
<ProjectReference Include="..\OpenSubtitlesHandler\OpenSubtitlesHandler.csproj" />
|
|
||||||
<ProjectReference Include="..\Emby.Photos\Emby.Photos.csproj" />
|
<ProjectReference Include="..\Emby.Photos\Emby.Photos.csproj" />
|
||||||
<ProjectReference Include="..\Emby.Drawing\Emby.Drawing.csproj" />
|
<ProjectReference Include="..\Emby.Drawing\Emby.Drawing.csproj" />
|
||||||
<ProjectReference Include="..\Emby.XmlTv\Emby.XmlTv\Emby.XmlTv.csproj" />
|
<ProjectReference Include="..\Emby.XmlTv\Emby.XmlTv\Emby.XmlTv.csproj" />
|
||||||
@@ -22,12 +20,20 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Hosting.Server.Abstractions" Version="2.2.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="2.2.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.2.0" />
|
||||||
<PackageReference Include="ServiceStack.Text.Core" Version="5.4.0" />
|
<PackageReference Include="ServiceStack.Text.Core" Version="5.4.0" />
|
||||||
<PackageReference Include="sharpcompress" Version="0.22.0" />
|
<PackageReference Include="sharpcompress" Version="0.22.0" />
|
||||||
<PackageReference Include="SimpleInjector" Version="4.4.2" />
|
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="1.0.0" />
|
||||||
<PackageReference Include="SQLitePCL.pretty.core" Version="1.1.8" />
|
|
||||||
<PackageReference Include="SQLitePCLRaw.core" Version="1.1.11" />
|
|
||||||
<PackageReference Include="UTF.Unknown" Version="1.0.0-beta1" />
|
<PackageReference Include="UTF.Unknown" Version="1.0.0-beta1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
@@ -40,11 +46,26 @@
|
|||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- Code analysers-->
|
||||||
|
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.6.3" />
|
||||||
|
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2" />
|
||||||
|
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
|
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Localization\iso6392.txt" />
|
<EmbeddedResource Include="Localization\iso6392.txt" />
|
||||||
<EmbeddedResource Include="Localization\countries.json" />
|
<EmbeddedResource Include="Localization\countries.json" />
|
||||||
<EmbeddedResource Include="Localization\Core\*.json" />
|
<EmbeddedResource Include="Localization\Core\*.json" />
|
||||||
<EmbeddedResource Include="Localization\Ratings\*.txt" />
|
<EmbeddedResource Include="Localization\Ratings\*.csv" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ using MediaBrowser.Controller.Plugins;
|
|||||||
using MediaBrowser.Controller.Session;
|
using MediaBrowser.Controller.Session;
|
||||||
using MediaBrowser.Model.LiveTv;
|
using MediaBrowser.Model.LiveTv;
|
||||||
using MediaBrowser.Model.Tasks;
|
using MediaBrowser.Model.Tasks;
|
||||||
using MediaBrowser.Model.Threading;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.EntryPoints
|
namespace Emby.Server.Implementations.EntryPoints
|
||||||
@@ -22,11 +21,10 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||||||
private readonly ISessionManager _sessionManager;
|
private readonly ISessionManager _sessionManager;
|
||||||
private readonly IServerConfigurationManager _config;
|
private readonly IServerConfigurationManager _config;
|
||||||
private readonly ILiveTvManager _liveTvManager;
|
private readonly ILiveTvManager _liveTvManager;
|
||||||
private readonly ITimerFactory _timerFactory;
|
|
||||||
|
|
||||||
private ITimer _timer;
|
private Timer _timer;
|
||||||
|
|
||||||
public AutomaticRestartEntryPoint(IServerApplicationHost appHost, ILogger logger, ITaskManager iTaskManager, ISessionManager sessionManager, IServerConfigurationManager config, ILiveTvManager liveTvManager, ITimerFactory timerFactory)
|
public AutomaticRestartEntryPoint(IServerApplicationHost appHost, ILogger logger, ITaskManager iTaskManager, ISessionManager sessionManager, IServerConfigurationManager config, ILiveTvManager liveTvManager)
|
||||||
{
|
{
|
||||||
_appHost = appHost;
|
_appHost = appHost;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -34,15 +32,16 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||||||
_sessionManager = sessionManager;
|
_sessionManager = sessionManager;
|
||||||
_config = config;
|
_config = config;
|
||||||
_liveTvManager = liveTvManager;
|
_liveTvManager = liveTvManager;
|
||||||
_timerFactory = timerFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Run()
|
public Task RunAsync()
|
||||||
{
|
{
|
||||||
if (_appHost.CanSelfRestart)
|
if (_appHost.CanSelfRestart)
|
||||||
{
|
{
|
||||||
_appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged;
|
_appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _appHost_HasPendingRestartChanged(object sender, EventArgs e)
|
void _appHost_HasPendingRestartChanged(object sender, EventArgs e)
|
||||||
@@ -51,7 +50,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||||||
|
|
||||||
if (_appHost.HasPendingRestart)
|
if (_appHost.HasPendingRestart)
|
||||||
{
|
{
|
||||||
_timer = _timerFactory.Create(TimerCallback, null, TimeSpan.FromMinutes(15), TimeSpan.FromMinutes(15));
|
_timer = new Timer(TimerCallback, null, TimeSpan.FromMinutes(15), TimeSpan.FromMinutes(15));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user