Compare commits

...

213 Commits

Author SHA1 Message Date
Cody Robibero
8b4a36d6f7 Merge pull request #6898 from jonas-resch/support-external-audio-files
Add support for external audio files
2021-12-11 12:05:34 -07:00
Cody Robibero
4d1b824651 Merge pull request #6902 from cvium/migrate_networkconfig
Migrate network configuration safely
2021-12-11 12:04:58 -07:00
Cody Robibero
fbc79cb4ce Merge pull request #6965 from bendardenne/patch-1
Add artist to '/' split whitelist
2021-12-11 12:04:39 -07:00
WWWesten
4d446eb614 Translated using Weblate (Esperanto)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/eo/
2021-12-10 02:05:47 -05:00
Benoît Dardenne
de2d292197 Added artist to '/' split whitelist 2021-12-09 16:07:57 +01:00
WWWesten
13ac3e3665 Translated using Weblate (Macedonian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/mk/
2021-12-08 14:05:53 -05:00
WWWesten
5c407dbab6 Translated using Weblate (Romanian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ro/
2021-12-08 14:05:53 -05:00
Weevild
f50b2f7959 Translated using Weblate (Swedish)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sv/
2021-12-08 14:05:53 -05:00
archon eleven
1acb9e1d45 Translated using Weblate (Malay)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ms/
2021-12-08 14:05:53 -05:00
WWWesten
6640f3f782 Translated using Weblate (Malay)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ms/
2021-12-08 14:05:53 -05:00
WWWesten
8aef2cb282 Translated using Weblate (Lithuanian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/lt/
2021-12-08 14:05:53 -05:00
WWWesten
8bd331f5fa Translated using Weblate (Croatian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hr/
2021-12-08 14:05:53 -05:00
Muhammed Aljailane
ee80602a0d Translated using Weblate (Arabic)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ar/
2021-12-08 14:05:53 -05:00
Jonas Resch
03b3f08354 Format code in MediaBrowser.Providers/MediaInfo/AudioResolver.cs
Co-authored-by: Claus Vium <cvium@users.noreply.github.com>
2021-12-08 18:55:28 +01:00
Jonas Resch
65833076db Add "Async" suffix to AddExternalAudio method
Co-authored-by: Claus Vium <cvium@users.noreply.github.com>
2021-12-08 16:49:27 +01:00
Jonas Resch
e18d966874 Add "Async" suffix to AddExternalAudio method
Co-authored-by: Claus Vium <cvium@users.noreply.github.com>
2021-12-08 16:49:20 +01:00
Jonas Resch
4cdb590291 Exclude .strm files when searching for external audio files 2021-12-08 10:18:09 +01:00
Jonas Resch
d47811bdaf Fix wrong ffmpeg map argument due to wrong calculation 2021-12-08 10:17:25 +01:00
Jonas Resch
01a0a4a87c Add audioResolver argument to FFProbeVideoInfo initialization 2021-12-08 10:16:48 +01:00
Jonas Resch
87a6fdf847 Merge branch 'support-external-audio-files' of github.com:jonas-resch/jellyfin into support-external-audio-files 2021-12-08 09:55:16 +01:00
Claus Vium
a327b43ab7 Update MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs 2021-12-07 20:28:51 +01:00
Claus Vium
3f69eeab27 Merge branch 'master' into support-external-audio-files 2021-12-07 20:16:32 +01:00
Claus Vium
dd8b9e9d23 Merge pull request #6953 from matthiasdv/mdv/harden-systemd-service
Add more hardening to systemd service
2021-12-07 19:46:45 +01:00
matthiasdv
3176a4ddd9 add more hardening to systemd service 2021-12-06 22:40:00 +01:00
Bond-009
9cafa2cab4 Merge pull request #6946 from jellyfin/dependabot/nuget/libse-3.6.4 2021-12-06 14:46:10 +01:00
dependabot[bot]
c4c7d7431f Bump libse from 3.6.2 to 3.6.4
Bumps [libse](https://github.com/SubtitleEdit/subtitleedit) from 3.6.2 to 3.6.4.
- [Release notes](https://github.com/SubtitleEdit/subtitleedit/releases)
- [Changelog](https://github.com/SubtitleEdit/subtitleedit/blob/master/Changelog.txt)
- [Commits](https://github.com/SubtitleEdit/subtitleedit/compare/3.6.2...3.6.4)

---
updated-dependencies:
- dependency-name: libse
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-06 13:29:10 +00:00
Bond-009
f2648f3eee Merge pull request #6947 from jellyfin/dependabot/nuget/BDInfo-0.7.6.2 2021-12-06 14:28:17 +01:00
dependabot[bot]
29095ab390 Bump BDInfo from 0.7.6.1 to 0.7.6.2
Bumps [BDInfo](https://github.com/jellyfin/BDInfo) from 0.7.6.1 to 0.7.6.2.
- [Release notes](https://github.com/jellyfin/BDInfo/releases)
- [Commits](https://github.com/jellyfin/BDInfo/commits)

---
updated-dependencies:
- dependency-name: BDInfo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-06 12:01:19 +00:00
WWWesten
b9b3959223 Translated using Weblate (Punjabi)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pa/
2021-12-06 03:45:54 -05:00
WWWesten
9b71bf8cfe Translated using Weblate (Nepali)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ne/
2021-12-06 03:45:54 -05:00
WWWesten
5eff80fb8c Translated using Weblate (Macedonian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/mk/
2021-12-06 03:45:54 -05:00
mikixd586
2d77c729f2 Translated using Weblate (Serbian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sr/
2021-12-06 03:45:54 -05:00
mio2
9d387c542a Translated using Weblate (Japanese)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ja/
2021-12-06 03:45:54 -05:00
Bas Goos
60f4d70cc5 Translated using Weblate (Dutch)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nl/
2021-12-06 03:45:54 -05:00
oxixes
bd48b74d5b Translated using Weblate (Spanish)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es/
2021-12-06 03:45:54 -05:00
Mehyar
838adaea48 Translated using Weblate (Arabic)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ar/
2021-12-06 03:45:54 -05:00
archon eleven
46543ead27 Translated using Weblate (Malay)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ms/
2021-12-06 03:45:54 -05:00
Cody Robibero
707e5bab97 Merge pull request #6941 from holahmeds/mime-type
Use MimeTypes package to determine MIME type
2021-12-04 17:55:24 -07:00
Ahmed Rafiq
cf75f99f0e Update unit test name
Co-authored-by: Claus Vium <cvium@users.noreply.github.com>
2021-12-04 21:14:52 +06:00
Ahmed Rafiq
fa6f6515a6 Update unit test name
Co-authored-by: Claus Vium <cvium@users.noreply.github.com>
2021-12-04 21:14:16 +06:00
Ahmed Rafiq
0e491025ae Refactor MimeTypes.ToExtension()
Co-authored-by: Cody Robibero <cody@robibe.ro>
2021-12-04 21:13:18 +06:00
Ahmed Rafiq
40e05a7993 Update documentation 2021-12-04 21:06:51 +06:00
Ahmed Rafiq
6193fdea69 Use MimeTypes package to determine MIME type
This simplifies the code since we don't have to keep large mappings of extensions and MIME types.

We still keep the ability to override the mappings for:
- filling in entries not present in the package, for e.g. ".azw3"
- picking preferred extensions, for e.g. MimeTypes provides ".conf" as a possible extionsion for "text/plain", and while that is correct, ".txt" would be preferrable
- compatibility reasons
2021-12-04 20:08:16 +06:00
Jonas Resch
ca2d94ee97 Merge branch 'support-external-audio-files' of github.com:jonas-resch/jellyfin into support-external-audio-files 2021-12-03 19:19:53 +01:00
Jonas Resch
99a48554a6 Optimize calculation of external audio stream index in MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
Co-authored-by: Cody Robibero <cody@robibe.ro>
2021-12-03 19:19:22 +01:00
Jonas Resch
120828d8d0 Replace escaped quote string with quote character in MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
Co-authored-by: Cody Robibero <cody@robibe.ro>
2021-12-03 19:18:43 +01:00
Cody Robibero
9cea773d29 Merge pull request #6874 from 1337joe/tmdb-image-size-options 2021-12-03 07:03:02 -07:00
Cody Robibero
a474e4cf3b Merge pull request #6914 from marius-luca-87/dlna 2021-12-02 06:56:07 -07:00
Cody Robibero
b1025121b8 Merge pull request #6929 from Bond-009/jsoncontent 2021-12-02 06:55:28 -07:00
Bond-009
2029076ce8 Merge pull request #6936 from cvium/ffprobeprovider_tweak 2021-12-02 13:42:45 +01:00
cvium
5535b9c01f Reduce allocations 2021-12-02 11:21:59 +01:00
Jonas Resch
180e2dc329 Prevent crashes in specific scenarios 2021-12-01 21:05:43 +01:00
Bond_009
40be86eec0 Use PostAsJsonAsync where possible 2021-12-01 15:58:23 +01:00
Bond_009
beafd6eaab Use JsonContent where possible
Should reduce the # of allocated bytes
2021-12-01 15:40:06 +01:00
hoanghuy309
f6d8c19a7a Translated using Weblate (Vietnamese)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/vi/
2021-11-30 17:48:37 -05:00
snieguzary
2da7777e6d Translated using Weblate (Polish)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pl/
2021-11-30 17:48:37 -05:00
WWWesten
a0df79d8a5 Translated using Weblate (Persian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fa/
2021-11-30 17:48:37 -05:00
Bond-009
01b95cf8e6 Merge pull request #6927 from 1337joe/use-ssl-for-tmdb-images 2021-11-30 23:43:26 +01:00
Jonas Resch
6bbfcf1906 Add documentation to AudioResolver class 2021-11-30 21:05:43 +01:00
Jonas Resch
7b50048020 Add ConfigureAwait true in MediaBrowser.Providers/MediaInfo/AudioResolver.cs
Co-authored-by: Claus Vium <cvium@users.noreply.github.com>
2021-11-30 20:45:47 +01:00
Jonas Resch
a9a53dc657 Add ConfigureAwait true in MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
Co-authored-by: Claus Vium <cvium@users.noreply.github.com>
2021-11-30 20:45:21 +01:00
Jonas Resch
0d8170cedb Move variable in MediaBrowser.Providers/MediaInfo/AudioResolver.cs
Co-authored-by: Claus Vium <cvium@users.noreply.github.com>
2021-11-30 20:44:57 +01:00
Jonas Resch
1a35690834 Don't disable warnings in MediaBrowser.Providers/MediaInfo/AudioResolver.cs
Co-authored-by: Claus Vium <cvium@users.noreply.github.com>
2021-11-30 20:44:16 +01:00
Jonas Resch
c61b9ef05a Fix warning due to new line after opening bracket 2021-11-30 19:52:44 +01:00
Jonas Resch
b5b994b22f Fix compiler warning due to missing EnumeratorCancellation attribute 2021-11-30 19:31:46 +01:00
Jonas Resch
0894a6193f Implement coding standards from 2nd code feedback 2021-11-30 19:31:46 +01:00
Jonas Resch
9d34d6339a Change return type from List<string> to IEnumerable<string> in MediaBrowser.Providers/MediaInfo/AudioResolver.cs
Co-authored-by: Claus Vium <cvium@users.noreply.github.com>
2021-11-30 19:31:46 +01:00
Jonas Resch
bbf1399826 Check language for null or empty instead of only null in MediaBrowser.Providers/MediaInfo/AudioResolver.cs
Co-authored-by: Claus Vium <cvium@users.noreply.github.com>
2021-11-30 19:31:46 +01:00
Jonas Resch
d016d483ae Change return type from Task<List<MediaStream>> to Task<IAsyncEnumerable<MediaStream>> in MediaBrowser.Providers/MediaInfo/AudioResolver.cs
Co-authored-by: Claus Vium <cvium@users.noreply.github.com>
2021-11-30 19:31:46 +01:00
Jonas Resch
61b191d345 Fix indentation in MediaBrowser.Providers/MediaInfo/AudioResolver.cs
If statement which checks if filename of audio and video file match or if audio file starts with video filename

Co-authored-by: Claus Vium <cvium@users.noreply.github.com>
2021-11-30 19:31:46 +01:00
Jonas Resch
9433072f90 Only search in video folder for external audio files
Don't search in video metadata folder since audio files won't be stored there

Co-authored-by: Claus Vium <cvium@users.noreply.github.com>
2021-11-30 19:31:46 +01:00
Jonas Resch
a3c5afa443 Add ConfigureAwait false MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
Co-authored-by: Cody Robibero <cody@robibe.ro>
2021-11-30 19:31:46 +01:00
Jonas Resch
f1862f9b1a Add ConfigureAwait false to MediaBrowser.Providers/MediaInfo/AudioResolver.cs
Co-authored-by: Cody Robibero <cody@robibe.ro>
2021-11-30 19:31:46 +01:00
Jonas Resch
a68e58556c Implement code feedback
- Rewrite AudioResolver
- Use async & await instead of .Result
- Add support for audio containers with multiple audio streams (e.g.
  mka)
- Fix bug when using external subtitle and external audio streams at the
  same time
2021-11-30 19:31:46 +01:00
Jonas Resch
5e91f50c43 Update CONTRIBUTORS.md 2021-11-30 19:31:46 +01:00
Jonas Resch
c1a8385c9c Shorten calculation of audio startIndex in MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
Co-authored-by: Claus Vium <cvium@users.noreply.github.com>
2021-11-30 19:31:46 +01:00
Jonas Resch
9978164438 Add support for external audio files 2021-11-30 19:31:46 +01:00
Marius Luca
eaa003775f - take into account the streams dlnaheaders query parameter set by the DidlBuilder NormalizeDlnaMediaUrl function 2021-11-30 16:09:02 +02:00
Joe Rogers
e778462955 Use SSL for tmdb images 2021-11-30 14:31:32 +01:00
WWWesten
c677b4f6b7 Translated using Weblate (Mongolian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/mn/
2021-11-29 07:05:47 -05:00
WWWesten
20b0499b00 Translated using Weblate (Telugu)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/te/
2021-11-29 07:05:47 -05:00
rimasx
09f2456919 Translated using Weblate (Estonian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/et/
2021-11-29 07:05:47 -05:00
WWWesten
bcd84dedda Translated using Weblate (Pirate)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pr/
2021-11-29 07:05:47 -05:00
WWWesten
2496c5d589 Translated using Weblate (Vietnamese)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/vi/
2021-11-29 07:05:47 -05:00
WWWesten
19c9319d2c Translated using Weblate (Albanian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sq/
2021-11-29 07:05:47 -05:00
WWWesten
6ef9ac6e53 Translated using Weblate (Tamil)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ta/
2021-11-29 07:05:47 -05:00
WontTell
502a5fc932 Translated using Weblate (Spanish (Latin America))
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es_419/
2021-11-29 07:05:47 -05:00
WWWesten
02ce95edaf Translated using Weblate (Serbian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sr/
2021-11-29 07:05:47 -05:00
WWWesten
5addb24e9b Translated using Weblate (Portuguese)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt/
2021-11-29 07:05:47 -05:00
wolong gl
dc27e7618f Translated using Weblate (Chinese (Simplified))
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hans/
2021-11-29 07:05:47 -05:00
Weevild
3e1bbdd3d4 Translated using Weblate (Swedish)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sv/
2021-11-29 07:05:47 -05:00
WWWesten
2ff786fd3d Translated using Weblate (Malay)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ms/
2021-11-29 07:05:47 -05:00
Bond-009
2a09d4244c Merge pull request #6923 from cvium/query_a_bit_less 2021-11-29 13:00:38 +01:00
cvium
beef6f0855 Don't query series twice 2021-11-28 19:56:31 +01:00
Cody Robibero
80c7119537 Merge pull request #6915 from 1337joe/subtitle-parsing-fix 2021-11-27 16:55:51 -07:00
Joe Rogers
1df5b5034b Address suppressed warnings 2021-11-27 20:35:18 +01:00
Joe Rogers
4a20ae6cb4 Allow default/forced tag without setting language 2021-11-27 20:13:21 +01:00
Claus Vium
a87b87825d Update Jellyfin.Server/Migrations/MigrationRunner.cs
Co-authored-by: Cody Robibero <cody@robibe.ro>
2021-11-27 16:01:27 +01:00
Cody Robibero
976e3160b8 Merge pull request #6907 from marius-luca-87/dlna 2021-11-27 07:47:54 -07:00
Claus Vium
ca887518dd Use the Memory overload for ReadAsync (#6865) 2021-11-27 03:23:46 +01:00
Marius Luca
5b5ae1ef52 - enable seek function when direct streaming over DLNA 2021-11-26 18:09:34 +02:00
WWWesten
3eec137100 Translated using Weblate (Spanish (Latin America))
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es_419/
2021-11-26 04:09:26 -05:00
WontTell
402a3797a4 Translated using Weblate (Spanish (Latin America))
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es_419/
2021-11-26 04:09:26 -05:00
cvium
0b871505a6 Remove stray datamember 2021-11-24 14:36:01 +01:00
cvium
0485ff1899 Create a store key constant for network 2021-11-24 13:42:14 +01:00
cvium
69df004b9f Migrate network configuration safely 2021-11-24 13:00:12 +01:00
N4v41
8b9ff893b3 Translated using Weblate (Portuguese)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt/
2021-11-24 06:28:00 -05:00
Pedro Almeida
b3d5383073 Translated using Weblate (Portuguese (Portugal))
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt_PT/
2021-11-24 06:28:00 -05:00
Csaba
9d5f328a09 Translated using Weblate (Hungarian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hu/
2021-11-24 06:27:59 -05:00
WWWesten
f469ee3010 Translated using Weblate (English)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/en/
2021-11-24 06:27:59 -05:00
Bond-009
e30ac91f16 Merge pull request #6900 from ianjazz246/local-time-test 2021-11-24 09:14:22 +01:00
ianjazz246
95183c365a Use local time for RecordingHelperTests 2021-11-23 12:57:05 -08:00
Joe Rogers
0af5e60094 Address review comments
Store null instead of calculating scaled image sizes.
Add endpoint to provide TMDb image size options.
2021-11-22 21:08:07 +01:00
Cody Robibero
3c030740ea Merge pull request #6895 from jellyfin/dependabot/nuget/prometheus-net.AspNetCore-5.0.2
Bump prometheus-net.AspNetCore from 5.0.1 to 5.0.2
2021-11-22 06:50:16 -07:00
dependabot[bot]
bb9ce98379 Bump prometheus-net.AspNetCore from 5.0.1 to 5.0.2
Bumps [prometheus-net.AspNetCore](https://github.com/prometheus-net/prometheus-net) from 5.0.1 to 5.0.2.
- [Release notes](https://github.com/prometheus-net/prometheus-net/releases)
- [Changelog](https://github.com/prometheus-net/prometheus-net/blob/master/History)
- [Commits](https://github.com/prometheus-net/prometheus-net/compare/v5.0.1...v5.0.2)

---
updated-dependencies:
- dependency-name: prometheus-net.AspNetCore
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-22 13:33:18 +00:00
Cody Robibero
02fda265b8 Merge pull request #6894 from jellyfin/dependabot/nuget/prometheus-net-5.0.2
Bump prometheus-net from 5.0.1 to 5.0.2
2021-11-22 06:32:48 -07:00
Cody Robibero
501919df35 Merge pull request #6893 from jellyfin/dependabot/nuget/sharpcompress-0.30.1
Bump sharpcompress from 0.30.0 to 0.30.1
2021-11-22 06:32:35 -07:00
Cody Robibero
107909de30 Merge pull request #6896 from jellyfin/dependabot/nuget/Serilog.Sinks.Console-4.0.1
Bump Serilog.Sinks.Console from 4.0.0 to 4.0.1
2021-11-22 06:32:19 -07:00
dependabot[bot]
052b667d6a Bump Serilog.Sinks.Console from 4.0.0 to 4.0.1
Bumps [Serilog.Sinks.Console](https://github.com/serilog/serilog-sinks-console) from 4.0.0 to 4.0.1.
- [Release notes](https://github.com/serilog/serilog-sinks-console/releases)
- [Commits](https://github.com/serilog/serilog-sinks-console/compare/v4.0.0...v4.0.1)

---
updated-dependencies:
- dependency-name: Serilog.Sinks.Console
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-22 12:01:30 +00:00
dependabot[bot]
e5ea607853 Bump prometheus-net from 5.0.1 to 5.0.2
Bumps [prometheus-net](https://github.com/prometheus-net/prometheus-net) from 5.0.1 to 5.0.2.
- [Release notes](https://github.com/prometheus-net/prometheus-net/releases)
- [Changelog](https://github.com/prometheus-net/prometheus-net/blob/master/History)
- [Commits](https://github.com/prometheus-net/prometheus-net/compare/v5.0.1...v5.0.2)

---
updated-dependencies:
- dependency-name: prometheus-net
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-22 12:01:20 +00:00
dependabot[bot]
223cc8314a Bump sharpcompress from 0.30.0 to 0.30.1
Bumps [sharpcompress](https://github.com/adamhathcock/sharpcompress) from 0.30.0 to 0.30.1.
- [Release notes](https://github.com/adamhathcock/sharpcompress/releases)
- [Commits](https://github.com/adamhathcock/sharpcompress/compare/0.30...0.30.1)

---
updated-dependencies:
- dependency-name: sharpcompress
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-22 12:01:01 +00:00
Claus Vium
39e6658d01 Merge pull request #6879 from crobibero/client-log-cleanup
Remove ClientLog endpoints
2021-11-21 23:57:13 +01:00
Claus Vium
8f051c86f7 Merge pull request #6873 from crobibero/resume-ignore-active
Add ability to exclude active sessions from resumable items
2021-11-21 23:54:17 +01:00
Claus Vium
11419cbbfd Merge pull request #6890 from crobibero/dlna-create
Fix creating DLNA profiles
2021-11-21 20:46:41 +01:00
Cody Robibero
5b7e8a27fb Fix creating DLNA profiles 2021-11-21 11:48:30 -07:00
WWWesten
718e4b8665 Translated using Weblate (Mongolian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/mn/
2021-11-20 14:05:43 -05:00
hoanghuy309
d387d4df92 Translated using Weblate (Vietnamese)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/vi/
2021-11-20 14:05:43 -05:00
WWWesten
1fa845de6e Translated using Weblate (Ukrainian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/uk/
2021-11-20 14:05:43 -05:00
WWWesten
7126f9dffc Translated using Weblate (English (United Kingdom))
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/en_GB/
2021-11-20 14:05:43 -05:00
Cody Robibero
b2b4bd82d7 Merge pull request #6862 from 1337joe/query-instead-of-filtering 2021-11-20 08:50:39 -07:00
Cody Robibero
03c7bcf9c6 Merge pull request #6870 from cvium/fix_omdb_image_provider 2021-11-20 08:50:22 -07:00
Cody Robibero
ea355b4262 Remove ClientLog endpoints 2021-11-20 08:47:05 -07:00
cvium
71a0abe211 Remove unnecessary N/A checks (converter changes them to null) 2021-11-20 08:32:07 +01:00
cvium
d8c3b8e7f8 Don't use AppendFormat 2021-11-20 08:24:52 +01:00
Joe Rogers
88baff5693 Apply suggestions from code review
Co-authored-by: Claus Vium <cvium@users.noreply.github.com>
2021-11-19 22:40:42 +01:00
Joe Rogers
6d3b129666 Add image scaling options for tmdb 2021-11-19 22:21:21 +01:00
Cody Robibero
8295a3be0d Add ability to exclude active sessions from resumable items 2021-11-19 14:07:50 -07:00
WWWesten
17a273d237 Update LocalizationManager.cs (#6839) 2021-11-19 18:00:20 +01:00
Cody Robibero
752bb88445 Merge pull request #6871 from cvium/fix_localapiurl 2021-11-19 09:18:25 -07:00
cvium
a263f3978b Only force scheme and port when HTTPS is disallowed 2021-11-19 16:57:48 +01:00
Cody Robibero
0009e5cc27 Merge pull request #6850 from PetrusZ/collection-validator 2021-11-19 07:54:41 -07:00
cvium
96ea865681 Refactor omdb providers 2021-11-19 15:32:07 +01:00
Cody Robibero
4a7498a0be Merge pull request #6869 from cvium/optimize_childcount
Small optimization to child count field
2021-11-19 05:07:54 -07:00
cvium
e3f0a53f59 Small optimization to child count field 2021-11-19 09:15:06 +01:00
Joe Rogers
9ba7bf96ef Query MediaSourceManager directly in image providers
Add doc comments/minor tweaks to AudioImageProvider
2021-11-18 15:11:50 +01:00
Joe Rogers
bff5ff0cb8 Merge similar tests with Theories 2021-11-18 14:29:22 +01:00
Petrus.Z
acb86066ff Replace library option to AutomaticallyAddToCollection
Signed-off-by: Petrus.Z <silencly07@gmail.com>
2021-11-18 16:00:14 +08:00
Petrus.Z
263bbf897a Reduce one query
Signed-off-by: Petrus.Z <silencly07@gmail.com>
2021-11-18 15:34:35 +08:00
Joe Rogers
97124f5fce Add missed override 2021-11-18 07:42:04 +01:00
Petrus.Z
6565b0cfbe Fix style issues
Signed-off-by: Petrus.Z <silencly07@gmail.com>
2021-11-18 13:33:44 +08:00
Petrus.Z
b635b5a7e3 Paginate movies query
Signed-off-by: Petrus.Z <silencly07@gmail.com>
2021-11-18 13:08:56 +08:00
Petrus.Z
6d74c83ddb Fix issues mentioned in review, except for option name
Signed-off-by: Petrus.Z <silencly07@gmail.com>
2021-11-18 12:07:27 +08:00
Joe Rogers
7cf5767949 Query media streams by type instead of filtering 2021-11-17 22:34:04 +01:00
Cody Robibero
fa366f0099 Merge pull request #6860 from cvium/fix_xml_endless_loops 2021-11-17 08:39:49 -07:00
Petrus.Z
989013c974 Add new line at end of file
Signed-off-by: Petrus.Z <silencly07@gmail.com>
2021-11-17 22:22:52 +08:00
Petrus.Z
c09e999916 Use List<Gid> instead of List<Movie>
Signed-off-by: Petrus.Z <silencly07@gmail.com>
2021-11-17 22:18:59 +08:00
cvium
61b75c82ce Read past empty elements 2021-11-17 14:49:30 +01:00
rimasx
773a4ae1f3 Translated using Weblate (Estonian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/et/
2021-11-17 08:48:37 -05:00
WWWesten
38ac84ad71 Translated using Weblate (Esperanto)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/eo/
2021-11-17 08:48:37 -05:00
WWWesten
054ca6a8fb Translated using Weblate (Kazakh)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/kk/
2021-11-17 08:48:37 -05:00
millallo
3d9f865247 Translated using Weblate (Italian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/it/
2021-11-17 08:48:37 -05:00
Csaba
5cd6fba11a Translated using Weblate (Hungarian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hu/
2021-11-17 08:48:37 -05:00
blob03
f7b18d97a7 Translated using Weblate (French)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fr/
2021-11-17 08:48:37 -05:00
Lukáš Kucharczyk
45e6e2adee Translated using Weblate (Czech)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/cs/
2021-11-17 08:48:37 -05:00
blob03
704c1539a2 Translated using Weblate (English)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/en/
2021-11-17 08:48:37 -05:00
WWWesten
00b293d2e9 Added translation using Weblate (Mongolian) 2021-11-17 08:48:37 -05:00
Petrus.Z
52e9dc66f5 Remove extra blank line
Signed-off-by: Petrus.Z <silencly07@gmail.com>
2021-11-17 21:07:02 +08:00
Petrus.Z
3c8f7d380f Improve performance
Signed-off-by: Petrus.Z <silencly07@gmail.com>
2021-11-17 20:56:14 +08:00
Petrus.Z
0a0ddb0eaf Add AutoCollection option
it can determine whether auto create/add movies to collection

Signed-off-by: Petrus.Z <silencly07@gmail.com>
2021-11-17 14:02:07 +08:00
Cody Robibero
84b8c9c2bc Merge pull request #6857 from Bond-009/warn56 2021-11-16 12:25:54 -07:00
Bond_009
257e1be95f Fix some warnings 2021-11-16 16:31:57 +01:00
rimasx
6003289717 Translated using Weblate (Estonian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/et/
2021-11-16 09:50:35 -05:00
WWWesten
19d285149b Translated using Weblate (Macedonian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/mk/
2021-11-16 09:50:35 -05:00
blob03
33a2e1bd50 Translated using Weblate (French)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fr/
2021-11-16 09:50:35 -05:00
emmanuel billeaud
b8507371d5 Translated using Weblate (French)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fr/
2021-11-16 09:50:35 -05:00
Bond-009
bddded96ff Merge pull request #6856 from cvium/unused_deps 2021-11-16 15:42:32 +01:00
cvium
24024706bf Fix release build 2021-11-16 12:55:35 +01:00
cvium
93fd1c7075 Fix 2021-11-16 12:27:27 +01:00
cvium
5b1b2621ab Fix build 2021-11-16 12:25:46 +01:00
cvium
b50c3852ef Remove unused dependencies 2021-11-16 12:24:17 +01:00
Petrus.Z
1924d0740d Fix style and performance issues mentioned in review
Signed-off-by: Petrus.Z <silencly07@gmail.com>
2021-11-16 19:03:18 +08:00
Petrus.Z
74459ec403 Fix issues mentioned in review
Signed-off-by: Petrus.Z <silencly07@gmail.com>
2021-11-16 11:50:04 +08:00
Cody Robibero
c32a421ea7 Merge pull request #6851 from cvium/remove_references_to_ilibrarymanager 2021-11-15 15:45:49 -07:00
Cody Robibero
4cfe8fe588 Merge pull request #6831 from 1337joe/image-provider-cleanup 2021-11-15 15:45:33 -07:00
Cody Robibero
06c82973c6 Merge pull request #6854 from 1337joe/add-customprefs-set 2021-11-15 15:45:21 -07:00
Claus Vium
f638cd08ea Merge pull request #6852 from Bond-009/warn54
Fix some warnings
2021-11-15 23:35:53 +01:00
Claus Vium
31b87130a2 Merge pull request #6848 from marius-luca-87/dlna
Ensure the proper StartTimeTicks variable is forwarded to the AddDlnaHeaders function
2021-11-15 23:34:34 +01:00
Joe Rogers
ca99a27611 Add setter for CustomPrefs 2021-11-15 22:03:19 +01:00
Petrus.Z
5eb1fde88c Add Collection Validator, create collection based on nfo
Based on nfo's set element, automatically add movie to collection.

Signed-off-by: Petrus.Z <silencly07@gmail.com>
2021-11-16 00:17:14 +08:00
cvium
24679af2e8 Fix comment 2021-11-15 16:01:34 +01:00
Bond_009
474b035d99 Fix some warnings 2021-11-15 15:57:07 +01:00
cvium
4f45c52674 Remove ILibraryManager as a dependency in resolvers etc. 2021-11-15 15:56:02 +01:00
Joe Rogers
f059be8e4d Add logging and fast return 2021-11-15 15:30:43 +01:00
Cody Robibero
03435641c8 Merge pull request #6849 from 1337joe/server-disabled-metadata 2021-11-15 07:25:56 -07:00
Joe Rogers
58be1d7759 Actually check server disabled metadata providers 2021-11-15 14:47:06 +01:00
Marius Luca
4e0edaf544 - ensure the proper StartTimeTicks variable is forwarded to the AddDlnaHeaders function 2021-11-15 15:34:02 +02:00
rimasx
9c74103fbe Translated using Weblate (Estonian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/et/
2021-11-15 07:48:24 -05:00
WWWesten
551c6f02a2 Added translation using Weblate (Assamese) 2021-11-15 07:48:24 -05:00
Bond-009
358cf48506 Merge pull request #6847 from jellyfin/dependabot/nuget/Microsoft.SourceLink.GitHub-1.1.1 2021-11-15 13:31:56 +01:00
Bond-009
331d4ad849 Merge pull request #6846 from jellyfin/dependabot/nuget/Diacritics-3.3.10 2021-11-15 13:31:05 +01:00
dependabot[bot]
bd32cecf7a Bump Microsoft.SourceLink.GitHub from 1.1.0 to 1.1.1
Bumps [Microsoft.SourceLink.GitHub](https://github.com/dotnet/sourcelink) from 1.1.0 to 1.1.1.
- [Release notes](https://github.com/dotnet/sourcelink/releases)
- [Commits](https://github.com/dotnet/sourcelink/compare/1.1.0...1.1.1)

---
updated-dependencies:
- dependency-name: Microsoft.SourceLink.GitHub
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-15 12:01:08 +00:00
dependabot[bot]
c84f2e48b0 Bump Diacritics from 3.3.4 to 3.3.10
Bumps [Diacritics](https://github.com/thomasgalliker/Diacritics.NET) from 3.3.4 to 3.3.10.
- [Release notes](https://github.com/thomasgalliker/Diacritics.NET/releases)
- [Commits](https://github.com/thomasgalliker/Diacritics.NET/commits)

---
updated-dependencies:
- dependency-name: Diacritics
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-15 12:00:50 +00:00
Joe Rogers
370b7f8e12 Handle unexpected case more gracefully 2021-11-14 23:13:45 +01:00
Cody Robibero
4cb649853d Merge pull request #6845 from marius-luca-87/dlna
Flush the XmlWriter before calling the StringBuilder ToString() method
2021-11-14 13:02:46 -07:00
Marius Luca
a774d1fa10 - flush the XmlWriter before calling the StringBuilder ToString() method 2021-11-14 20:46:17 +02:00
Haadiy Rozzaq
5254e74719 Translated using Weblate (Indonesian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/id/
2021-11-14 01:36:37 -05:00
WWWesten
14b5e85461 Translated using Weblate (Belarusian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/be/
2021-11-14 01:36:37 -05:00
WWWesten
25f1cdbcb5 Translated using Weblate (Catalan)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ca/
2021-11-14 01:36:37 -05:00
Cody Robibero
34df1a030b Merge pull request #6818 from Bond-009/password 2021-11-13 16:56:25 -07:00
Joe Rogers
1d729b2b0f Use codec to determine image format 2021-11-12 16:30:30 +01:00
Joe Rogers
f73a7a6ed8 Use ImageFormat instead of string for extension 2021-11-12 16:22:11 +01:00
Joe Rogers
de9bf327c6 Merge similar tests with Theories 2021-11-12 13:44:48 +01:00
Bond_009
5265b3eee7 Replace PBKDF2-SHA1 with PBKDF2-SHA512
This also migrates already created passwords on login

Source for the number of iterations:
https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2
2021-11-10 22:34:54 +01:00
179 changed files with 2473 additions and 2015 deletions

View File

@@ -150,6 +150,7 @@
- [ianjazz246](https://github.com/ianjazz246)
- [peterspenler](https://github.com/peterspenler)
- [MBR-0001](https://github.com/MBR-0001)
- [jonas-resch](https://github.com/jonas-resch)
# Emby Contributors

View File

@@ -395,6 +395,7 @@ namespace Emby.Dlna.ContentDirectory
}
writer.WriteFullEndElement();
writer.Flush();
xmlWriter.WriteElementString("Result", builder.ToString());
}
@@ -484,6 +485,7 @@ namespace Emby.Dlna.ContentDirectory
}
writer.WriteFullEndElement();
writer.Flush();
xmlWriter.WriteElementString("Result", builder.ToString());
}

View File

@@ -350,7 +350,7 @@ namespace Emby.Dlna
Directory.CreateDirectory(systemProfilesPath);
var fileOptions = AsyncFile.WriteOptions;
fileOptions.Mode = FileMode.CreateNew;
fileOptions.Mode = FileMode.Create;
fileOptions.PreallocationSize = length;
using (var fileStream = new FileStream(path, fileOptions))
{

View File

@@ -124,7 +124,7 @@ namespace Emby.Dlna.Main
config);
Current = this;
var netConfig = config.GetConfiguration<NetworkConfiguration>("network");
var netConfig = config.GetConfiguration<NetworkConfiguration>(NetworkConfigurationStore.StoreKey);
_disabled = appHost.ListenWithHttps && netConfig.RequireHttps;
if (_disabled && _config.GetDlnaConfiguration().EnableServer)
@@ -262,7 +262,6 @@ namespace Emby.Dlna.Main
{
_publisher = new SsdpDevicePublisher(
_communicationsServer,
_networkManager,
MediaBrowser.Common.System.OperatingSystem.Name,
Environment.OSVersion.VersionString,
_config.GetDlnaConfiguration().SendOnlyMatchedHost)
@@ -400,7 +399,6 @@ namespace Emby.Dlna.Main
_imageProcessor,
_deviceDiscovery,
_httpClientFactory,
_config,
_userDataManager,
_localization,
_mediaSourceManager,

View File

@@ -11,7 +11,6 @@ using System.Threading.Tasks;
using Jellyfin.Data.Events;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Library;
@@ -35,7 +34,6 @@ namespace Emby.Dlna.PlayTo
private readonly IServerApplicationHost _appHost;
private readonly IImageProcessor _imageProcessor;
private readonly IHttpClientFactory _httpClientFactory;
private readonly IServerConfigurationManager _config;
private readonly IUserDataManager _userDataManager;
private readonly ILocalizationManager _localization;
@@ -47,7 +45,7 @@ namespace Emby.Dlna.PlayTo
private SemaphoreSlim _sessionLock = new SemaphoreSlim(1, 1);
private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
public PlayToManager(ILogger logger, ISessionManager sessionManager, ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IImageProcessor imageProcessor, IDeviceDiscovery deviceDiscovery, IHttpClientFactory httpClientFactory, IServerConfigurationManager config, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder)
public PlayToManager(ILogger logger, ISessionManager sessionManager, ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IImageProcessor imageProcessor, IDeviceDiscovery deviceDiscovery, IHttpClientFactory httpClientFactory, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder)
{
_logger = logger;
_sessionManager = sessionManager;
@@ -58,7 +56,6 @@ namespace Emby.Dlna.PlayTo
_imageProcessor = imageProcessor;
_deviceDiscovery = deviceDiscovery;
_httpClientFactory = httpClientFactory;
_config = config;
_userDataManager = userDataManager;
_localization = localization;
_mediaSourceManager = mediaSourceManager;

View File

@@ -38,7 +38,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
</ItemGroup>
<!-- Code Analyzers-->

View File

@@ -19,6 +19,7 @@ using Emby.Dlna;
using Emby.Dlna.Main;
using Emby.Dlna.Ssdp;
using Emby.Drawing;
using Emby.Naming.Common;
using Emby.Notifications;
using Emby.Photos;
using Emby.Server.Implementations.Archiving;
@@ -312,22 +313,6 @@ namespace Emby.Server.Implementations
? Environment.MachineName
: ConfigurationManager.Configuration.ServerName;
/// <summary>
/// Temporary function to migration network settings out of system.xml and into network.xml.
/// TODO: remove at the point when a fixed migration path has been decided upon.
/// </summary>
private void MigrateNetworkConfiguration()
{
string path = Path.Combine(ConfigurationManager.CommonApplicationPaths.ConfigurationDirectoryPath, "network.xml");
if (!File.Exists(path))
{
var networkSettings = new NetworkConfiguration();
ClassMigrationHelper.CopyProperties(ConfigurationManager.Configuration, networkSettings);
_xmlSerializer.SerializeToFile(networkSettings, path);
Logger.LogDebug("Successfully migrated network settings.");
}
}
public string ExpandVirtualPath(string path)
{
var appPaths = ApplicationPaths;
@@ -512,8 +497,6 @@ namespace Emby.Server.Implementations
ConfigurationManager.AddParts(GetExports<IConfigurationFactory>());
// Have to migrate settings here as migration subsystem not yet initialised.
MigrateNetworkConfiguration();
NetManager = new NetworkManager(ConfigurationManager, LoggerFactory.CreateLogger<NetworkManager>());
// Initialize runtime stat collection
@@ -596,6 +579,7 @@ namespace Emby.Server.Implementations
serviceCollection.AddTransient(provider => new Lazy<IProviderManager>(provider.GetRequiredService<IProviderManager>));
serviceCollection.AddTransient(provider => new Lazy<IUserViewManager>(provider.GetRequiredService<IUserViewManager>));
serviceCollection.AddSingleton<ILibraryManager, LibraryManager>();
serviceCollection.AddSingleton<NamingOptions>();
serviceCollection.AddSingleton<IMusicManager, MusicManager>();
@@ -1126,12 +1110,12 @@ namespace Emby.Server.Implementations
}
/// <inheritdoc/>
public string GetApiUrlForLocalAccess(bool allowHttps)
public string GetApiUrlForLocalAccess(bool allowHttps = true)
{
// With an empty source, the port will be null
string smart = NetManager.GetBindInterface(string.Empty, out _);
var scheme = allowHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp;
var port = allowHttps ? HttpsPort : HttpPort;
var scheme = !allowHttps ? Uri.UriSchemeHttp : null;
int? port = !allowHttps ? HttpPort : null;
return GetLocalApiUrl(smart.Trim('/'), scheme, port);
}

View File

@@ -1075,14 +1075,6 @@ namespace Emby.Server.Implementations.Channels
forceUpdate = true;
}
// was used for status
// if (!string.Equals(item.ExternalEtag ?? string.Empty, info.Etag ?? string.Empty, StringComparison.Ordinal))
// {
// item.ExternalEtag = info.Etag;
// forceUpdate = true;
// _logger.LogDebug("Forcing update due to ExternalEtag {0}", item.Name);
// }
if (!internalChannelId.Equals(item.ChannelId))
{
forceUpdate = true;

View File

@@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Model.Cryptography;
using static MediaBrowser.Common.Cryptography.Constants;
using static MediaBrowser.Model.Cryptography.Constants;
namespace Emby.Server.Implementations.Cryptography
{
@@ -12,10 +14,7 @@ namespace Emby.Server.Implementations.Cryptography
/// </summary>
public class CryptographyProvider : ICryptoProvider
{
// 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
// TODO: remove when not needed for backwards compat
private static readonly HashSet<string> _supportedHashMethods = new HashSet<string>()
{
"MD5",
@@ -35,60 +34,81 @@ namespace Emby.Server.Implementations.Cryptography
};
/// <inheritdoc />
public string DefaultHashMethod => "PBKDF2";
public string DefaultHashMethod => "PBKDF2-SHA512";
/// <inheritdoc />
public IEnumerable<string> GetSupportedHashMethods()
=> _supportedHashMethods;
private byte[] PBKDF2(string method, byte[] bytes, byte[] salt, int iterations)
public PasswordHash CreatePasswordHash(ReadOnlySpan<char> password)
{
// 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)
{
throw new CryptographicException($"Cannot currently use PBKDF2 with requested hash method: {method}");
}
using var r = new Rfc2898DeriveBytes(bytes, salt, iterations);
return r.GetBytes(32);
byte[] salt = GenerateSalt();
return new PasswordHash(
DefaultHashMethod,
Rfc2898DeriveBytes.Pbkdf2(
password,
salt,
DefaultIterations,
HashAlgorithmName.SHA512,
DefaultOutputLength),
salt,
new Dictionary<string, string>
{
{ "iterations", DefaultIterations.ToString(CultureInfo.InvariantCulture) }
});
}
/// <inheritdoc />
public byte[] ComputeHash(string hashMethod, byte[] bytes, byte[] salt)
public bool Verify(PasswordHash hash, ReadOnlySpan<char> password)
{
if (hashMethod == DefaultHashMethod)
if (string.Equals(hash.Id, "PBKDF2", StringComparison.Ordinal))
{
return PBKDF2(hashMethod, bytes, salt, DefaultIterations);
return hash.Hash.SequenceEqual(
Rfc2898DeriveBytes.Pbkdf2(
password,
hash.Salt,
int.Parse(hash.Parameters["iterations"], CultureInfo.InvariantCulture),
HashAlgorithmName.SHA1,
32));
}
if (!_supportedHashMethods.Contains(hashMethod))
if (string.Equals(hash.Id, "PBKDF2-SHA512", StringComparison.Ordinal))
{
throw new CryptographicException($"Requested hash method is not supported: {hashMethod}");
return hash.Hash.SequenceEqual(
Rfc2898DeriveBytes.Pbkdf2(
password,
hash.Salt,
int.Parse(hash.Parameters["iterations"], CultureInfo.InvariantCulture),
HashAlgorithmName.SHA512,
DefaultOutputLength));
}
using var h = HashAlgorithm.Create(hashMethod) ?? throw new ResourceNotFoundException($"Unknown hash method: {hashMethod}.");
if (salt.Length == 0)
if (!_supportedHashMethods.Contains(hash.Id))
{
return h.ComputeHash(bytes);
throw new CryptographicException($"Requested hash method is not supported: {hash.Id}");
}
byte[] salted = new byte[bytes.Length + salt.Length];
using var h = HashAlgorithm.Create(hash.Id) ?? throw new ResourceNotFoundException($"Unknown hash method: {hash.Id}.");
var bytes = Encoding.UTF8.GetBytes(password.ToArray());
if (hash.Salt.Length == 0)
{
return hash.Hash.SequenceEqual(h.ComputeHash(bytes));
}
byte[] salted = new byte[bytes.Length + hash.Salt.Length];
Array.Copy(bytes, salted, bytes.Length);
Array.Copy(salt, 0, salted, bytes.Length, salt.Length);
return h.ComputeHash(salted);
hash.Salt.CopyTo(salted.AsSpan(bytes.Length));
return hash.Hash.SequenceEqual(h.ComputeHash(salted));
}
/// <inheritdoc />
public byte[] ComputeHashWithDefaultMethod(byte[] bytes, byte[] salt)
=> PBKDF2(DefaultHashMethod, bytes, salt, DefaultIterations);
/// <inheritdoc />
public byte[] GenerateSalt()
=> GenerateSalt(DefaultSaltLength);
/// <inheritdoc />
public byte[] GenerateSalt(int length)
=> RandomNumberGenerator.GetBytes(length);
{
var salt = new byte[length];
using var rng = RandomNumberGenerator.Create();
rng.GetNonZeroBytes(salt);
return salt;
}
}
}

View File

@@ -7,7 +7,7 @@ using SQLitePCL.pretty;
namespace Emby.Server.Implementations.Data
{
public class ManagedConnection : IDisposable
public sealed class ManagedConnection : IDisposable
{
private readonly SemaphoreSlim _writeLock;

View File

@@ -370,6 +370,12 @@ namespace Emby.Server.Implementations.Dto
if (item is MusicAlbum || item is Season || item is Playlist)
{
dto.ChildCount = dto.RecursiveItemCount;
var folderChildCount = folder.LinkedChildren.Length;
// The default is an empty array, so we can't reliably use the count when it's empty
if (folderChildCount > 0)
{
dto.ChildCount ??= folderChildCount;
}
}
if (options.ContainsField(ItemFields.ChildCount))

View File

@@ -32,7 +32,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.0" />
<PackageReference Include="Mono.Nat" Version="3.0.2" />
<PackageReference Include="prometheus-net.DotNetRuntime" Version="4.2.2" />
<PackageReference Include="sharpcompress" Version="0.30.0" />
<PackageReference Include="sharpcompress" Version="0.30.1" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="3.1.0" />
<PackageReference Include="DotNet.Glob" Version="3.1.3" />
</ItemGroup>

View File

@@ -27,7 +27,6 @@ namespace Emby.Server.Implementations.EntryPoints
private readonly IServerApplicationHost _appHost;
private readonly ILogger<ExternalPortForwarding> _logger;
private readonly IServerConfigurationManager _config;
private readonly IDeviceDiscovery _deviceDiscovery;
private readonly ConcurrentDictionary<IPEndPoint, byte> _createdRules = new ConcurrentDictionary<IPEndPoint, byte>();
@@ -42,17 +41,14 @@ namespace Emby.Server.Implementations.EntryPoints
/// <param name="logger">The logger.</param>
/// <param name="appHost">The application host.</param>
/// <param name="config">The configuration manager.</param>
/// <param name="deviceDiscovery">The device discovery.</param>
public ExternalPortForwarding(
ILogger<ExternalPortForwarding> logger,
IServerApplicationHost appHost,
IServerConfigurationManager config,
IDeviceDiscovery deviceDiscovery)
IServerConfigurationManager config)
{
_logger = logger;
_appHost = appHost;
_config = config;
_deviceDiscovery = deviceDiscovery;
}
private string GetConfigIdentifier()

View File

@@ -1,5 +1,3 @@
#nullable disable
#pragma warning disable CS1591
using System;
@@ -14,7 +12,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.IO
{
public class FileRefresher : IDisposable
public sealed class FileRefresher : IDisposable
{
private readonly ILogger _logger;
private readonly ILibraryManager _libraryManager;
@@ -22,7 +20,7 @@ namespace Emby.Server.Implementations.IO
private readonly List<string> _affectedPaths = new List<string>();
private readonly object _timerLock = new object();
private Timer _timer;
private Timer? _timer;
private bool _disposed;
public FileRefresher(string path, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ILogger logger)
@@ -36,7 +34,7 @@ namespace Emby.Server.Implementations.IO
AddPath(path);
}
public event EventHandler<EventArgs> Completed;
public event EventHandler<EventArgs>? Completed;
public string Path { get; private set; }
@@ -111,7 +109,7 @@ namespace Emby.Server.Implementations.IO
RestartTimer();
}
private void OnTimerCallback(object state)
private void OnTimerCallback(object? state)
{
List<string> paths;
@@ -127,7 +125,7 @@ namespace Emby.Server.Implementations.IO
try
{
ProcessPathChanges(paths.ToList());
ProcessPathChanges(paths);
}
catch (Exception ex)
{
@@ -137,12 +135,12 @@ namespace Emby.Server.Implementations.IO
private void ProcessPathChanges(List<string> paths)
{
var itemsToRefresh = paths
IEnumerable<BaseItem> itemsToRefresh = paths
.Distinct(StringComparer.OrdinalIgnoreCase)
.Select(GetAffectedBaseItem)
.Where(item => item != null)
.GroupBy(x => x.Id)
.Select(x => x.First());
.GroupBy(x => x!.Id) // Removed null values in the previous .Where()
.Select(x => x.First())!;
foreach (var item in itemsToRefresh)
{
@@ -176,15 +174,15 @@ namespace Emby.Server.Implementations.IO
/// </summary>
/// <param name="path">The path.</param>
/// <returns>BaseItem.</returns>
private BaseItem GetAffectedBaseItem(string path)
private BaseItem? GetAffectedBaseItem(string path)
{
BaseItem item = null;
BaseItem? item = null;
while (item == null && !string.IsNullOrEmpty(path))
{
item = _libraryManager.FindByPath(path, null);
path = System.IO.Path.GetDirectoryName(path);
path = System.IO.Path.GetDirectoryName(path) ?? string.Empty;
}
if (item != null)

View File

@@ -449,12 +449,12 @@ namespace Emby.Server.Implementations.IO
}
var newRefresher = new FileRefresher(path, _configurationManager, _libraryManager, _logger);
newRefresher.Completed += NewRefresher_Completed;
newRefresher.Completed += OnNewRefresherCompleted;
_activeRefreshers.Add(newRefresher);
}
}
private void NewRefresher_Completed(object sender, EventArgs e)
private void OnNewRefresherCompleted(object sender, EventArgs e)
{
var refresher = (FileRefresher)sender;
DisposeRefresher(refresher);
@@ -481,6 +481,7 @@ namespace Emby.Server.Implementations.IO
{
lock (_activeRefreshers)
{
refresher.Completed -= OnNewRefresherCompleted;
refresher.Dispose();
_activeRefreshers.Remove(refresher);
}
@@ -492,6 +493,7 @@ namespace Emby.Server.Implementations.IO
{
foreach (var refresher in _activeRefreshers.ToList())
{
refresher.Completed -= OnNewRefresherCompleted;
refresher.Dispose();
}

View File

@@ -1,8 +1,9 @@
using System;
using System.IO;
using Emby.Naming.Audio;
using Emby.Naming.Common;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.IO;
@@ -13,17 +14,17 @@ namespace Emby.Server.Implementations.Library
/// </summary>
public class CoreResolutionIgnoreRule : IResolverIgnoreRule
{
private readonly ILibraryManager _libraryManager;
private readonly NamingOptions _namingOptions;
private readonly IServerApplicationPaths _serverApplicationPaths;
/// <summary>
/// Initializes a new instance of the <see cref="CoreResolutionIgnoreRule"/> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="namingOptions">The naming options.</param>
/// <param name="serverApplicationPaths">The server application paths.</param>
public CoreResolutionIgnoreRule(ILibraryManager libraryManager, IServerApplicationPaths serverApplicationPaths)
public CoreResolutionIgnoreRule(NamingOptions namingOptions, IServerApplicationPaths serverApplicationPaths)
{
_libraryManager = libraryManager;
_namingOptions = namingOptions;
_serverApplicationPaths = serverApplicationPaths;
}
@@ -78,7 +79,7 @@ namespace Emby.Server.Implementations.Library
{
// Don't resolve these into audio files
if (Path.GetFileNameWithoutExtension(filename.AsSpan()).Equals(BaseItem.ThemeSongFileName, StringComparison.Ordinal)
&& _libraryManager.IsAudioFile(filename))
&& AudioFileParser.IsAudioFile(filename, _namingOptions))
{
return true;
}

View File

@@ -79,6 +79,7 @@ namespace Emby.Server.Implementations.Library
private readonly IFileSystem _fileSystem;
private readonly IItemRepository _itemRepository;
private readonly IImageProcessor _imageProcessor;
private readonly NamingOptions _namingOptions;
/// <summary>
/// The _root folder sync lock.
@@ -88,9 +89,6 @@ namespace Emby.Server.Implementations.Library
private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromHours(24);
private NamingOptions _namingOptions;
private string[] _videoFileExtensions;
/// <summary>
/// The _root folder.
/// </summary>
@@ -116,6 +114,7 @@ namespace Emby.Server.Implementations.Library
/// <param name="itemRepository">The item repository.</param>
/// <param name="imageProcessor">The image processor.</param>
/// <param name="memoryCache">The memory cache.</param>
/// <param name="namingOptions">The naming options.</param>
public LibraryManager(
IServerApplicationHost appHost,
ILogger<LibraryManager> logger,
@@ -130,7 +129,8 @@ namespace Emby.Server.Implementations.Library
IMediaEncoder mediaEncoder,
IItemRepository itemRepository,
IImageProcessor imageProcessor,
IMemoryCache memoryCache)
IMemoryCache memoryCache,
NamingOptions namingOptions)
{
_appHost = appHost;
_logger = logger;
@@ -146,6 +146,7 @@ namespace Emby.Server.Implementations.Library
_itemRepository = itemRepository;
_imageProcessor = imageProcessor;
_memoryCache = memoryCache;
_namingOptions = namingOptions;
_configurationManager.ConfigurationUpdated += ConfigurationUpdated;
@@ -2500,16 +2501,6 @@ namespace Emby.Server.Implementations.Library
return RootFolder;
}
/// <inheritdoc />
public bool IsVideoFile(string path)
{
return VideoResolver.IsVideoFile(path, GetNamingOptions());
}
/// <inheritdoc />
public bool IsAudioFile(string path)
=> AudioFileParser.IsAudioFile(path, GetNamingOptions());
/// <inheritdoc />
public int? GetSeasonNumberFromPath(string path)
=> SeasonPathParser.Parse(path, true, true).SeasonNumber;
@@ -2525,7 +2516,7 @@ namespace Emby.Server.Implementations.Library
isAbsoluteNaming = null;
}
var resolver = new EpisodeResolver(GetNamingOptions());
var resolver = new EpisodeResolver(_namingOptions);
var isFolder = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd;
@@ -2682,21 +2673,9 @@ namespace Emby.Server.Implementations.Library
return changed;
}
/// <inheritdoc />
public NamingOptions GetNamingOptions()
{
if (_namingOptions == null)
{
_namingOptions = new NamingOptions();
_videoFileExtensions = _namingOptions.VideoFileExtensions;
}
return _namingOptions;
}
public ItemLookupInfo ParseName(string name)
{
var namingOptions = GetNamingOptions();
var namingOptions = _namingOptions;
var result = VideoResolver.CleanDateTime(name, namingOptions);
return new ItemLookupInfo
@@ -2708,11 +2687,11 @@ namespace Emby.Server.Implementations.Library
public IEnumerable<Video> FindTrailers(BaseItem owner, List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
{
var namingOptions = GetNamingOptions();
var namingOptions = _namingOptions;
var files = owner.IsInMixedFolder ? new List<FileSystemMetadata>() : fileSystemChildren.Where(i => i.IsDirectory)
.Where(i => string.Equals(i.Name, BaseItem.TrailersFolderName, StringComparison.OrdinalIgnoreCase))
.SelectMany(i => _fileSystem.GetFiles(i.FullName, _videoFileExtensions, false, false))
.SelectMany(i => _fileSystem.GetFiles(i.FullName, namingOptions.VideoFileExtensions, false, false))
.ToList();
var videos = VideoListResolver.Resolve(fileSystemChildren, namingOptions);
@@ -2726,7 +2705,7 @@ namespace Emby.Server.Implementations.Library
var resolvers = new IItemResolver[]
{
new GenericVideoResolver<Trailer>(this)
new GenericVideoResolver<Trailer>(_namingOptions)
};
return ResolvePaths(files, directoryService, null, new LibraryOptions(), null, resolvers)
@@ -2752,11 +2731,11 @@ namespace Emby.Server.Implementations.Library
public IEnumerable<Video> FindExtras(BaseItem owner, List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
{
var namingOptions = GetNamingOptions();
var namingOptions = _namingOptions;
var files = owner.IsInMixedFolder ? new List<FileSystemMetadata>() : fileSystemChildren.Where(i => i.IsDirectory)
.Where(i => BaseItem.AllExtrasTypesFolderNames.ContainsKey(i.Name ?? string.Empty))
.SelectMany(i => _fileSystem.GetFiles(i.FullName, _videoFileExtensions, false, false))
.SelectMany(i => _fileSystem.GetFiles(i.FullName, namingOptions.VideoFileExtensions, false, false))
.ToList();
var videos = VideoListResolver.Resolve(fileSystemChildren, namingOptions);
@@ -2840,7 +2819,7 @@ namespace Emby.Server.Implementations.Library
private void SetExtraTypeFromFilename(Video item)
{
var resolver = new ExtraResolver(GetNamingOptions());
var resolver = new ExtraResolver(_namingOptions);
var result = resolver.GetExtraInfo(item.Path);

View File

@@ -6,7 +6,10 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Emby.Naming.Audio;
using Emby.Naming.AudioBook;
using Emby.Naming.Common;
using Emby.Naming.Video;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
@@ -21,11 +24,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
/// </summary>
public class AudioResolver : ItemResolver<MediaBrowser.Controller.Entities.Audio.Audio>, IMultiItemResolver
{
private readonly ILibraryManager _libraryManager;
private readonly NamingOptions _namingOptions;
public AudioResolver(ILibraryManager libraryManager)
public AudioResolver(NamingOptions namingOptions)
{
_libraryManager = libraryManager;
_namingOptions = namingOptions;
}
/// <summary>
@@ -40,7 +43,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
string collectionType,
IDirectoryService directoryService)
{
var result = ResolveMultipleInternal(parent, files, collectionType, directoryService);
var result = ResolveMultipleInternal(parent, files, collectionType);
if (result != null)
{
@@ -56,12 +59,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
private MultiItemResolverResult ResolveMultipleInternal(
Folder parent,
List<FileSystemMetadata> files,
string collectionType,
IDirectoryService directoryService)
string collectionType)
{
if (string.Equals(collectionType, CollectionType.Books, StringComparison.OrdinalIgnoreCase))
{
return ResolveMultipleAudio<AudioBook>(parent, files, directoryService, false, collectionType, true);
return ResolveMultipleAudio(parent, files, true);
}
return null;
@@ -87,14 +89,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
return null;
}
var files = args.FileSystemChildren
.Where(i => !_libraryManager.IgnoreFile(i, args.Parent))
.ToList();
return FindAudio<AudioBook>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false);
return FindAudioBook(args, false);
}
if (_libraryManager.IsAudioFile(args.Path))
if (AudioFileParser.IsAudioFile(args.Path, _namingOptions))
{
var extension = Path.GetExtension(args.Path);
@@ -107,7 +105,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
var isMixedCollectionType = string.IsNullOrEmpty(collectionType);
// For conflicting extensions, give priority to videos
if (isMixedCollectionType && _libraryManager.IsVideoFile(args.Path))
if (isMixedCollectionType && VideoResolver.IsVideoFile(args.Path, _namingOptions))
{
return null;
}
@@ -141,29 +139,23 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
return null;
}
private T FindAudio<T>(ItemResolveArgs args, string path, Folder parent, List<FileSystemMetadata> fileSystemEntries, IDirectoryService directoryService, string collectionType, bool parseName)
where T : MediaBrowser.Controller.Entities.Audio.Audio, new()
private AudioBook FindAudioBook(ItemResolveArgs args, bool parseName)
{
// TODO: Allow GetMultiDiscMovie in here
const bool supportsMultiVersion = false;
var result = ResolveMultipleAudio(args.Parent, args.GetActualFileSystemChildren(), parseName);
var result = ResolveMultipleAudio<T>(parent, fileSystemEntries, directoryService, supportsMultiVersion, collectionType, parseName) ??
new MultiItemResolverResult();
if (result.Items.Count == 1)
if (result == null || result.Items.Count != 1 || result.Items[0] is not AudioBook item)
{
// If we were supporting this we'd be checking filesFromOtherItems
var item = (T)result.Items[0];
item.IsInMixedFolder = false;
item.Name = Path.GetFileName(item.ContainingFolderPath);
return item;
return null;
}
return null;
// If we were supporting this we'd be checking filesFromOtherItems
item.IsInMixedFolder = false;
item.Name = Path.GetFileName(item.ContainingFolderPath);
return item;
}
private MultiItemResolverResult ResolveMultipleAudio<T>(Folder parent, IEnumerable<FileSystemMetadata> fileSystemEntries, IDirectoryService directoryService, bool suppportMultiEditions, string collectionType, bool parseName)
where T : MediaBrowser.Controller.Entities.Audio.Audio, new()
private MultiItemResolverResult ResolveMultipleAudio(Folder parent, IEnumerable<FileSystemMetadata> fileSystemEntries, bool parseName)
{
var files = new List<FileSystemMetadata>();
var items = new List<BaseItem>();
@@ -176,15 +168,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
{
leftOver.Add(child);
}
else if (!IsIgnored(child.Name))
else
{
files.Add(child);
}
}
var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
var resolver = new AudioBookListResolver(namingOptions);
var resolver = new AudioBookListResolver(_namingOptions);
var resolverResult = resolver.Resolve(files).ToList();
var result = new MultiItemResolverResult
@@ -210,7 +200,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
var firstMedia = resolvedItem.Files[0];
var libraryItem = new T
var libraryItem = new AudioBook
{
Path = firstMedia.Path,
IsInMixedFolder = isInMixedFolder,
@@ -230,12 +220,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
return result;
}
private bool ContainsFile(List<AudioBookInfo> result, FileSystemMetadata file)
private static bool ContainsFile(IEnumerable<AudioBookInfo> result, FileSystemMetadata file)
{
return result.Any(i => ContainsFile(i, file));
}
private bool ContainsFile(AudioBookInfo result, FileSystemMetadata file)
private static bool ContainsFile(AudioBookInfo result, FileSystemMetadata file)
{
return result.Files.Any(i => ContainsFile(i, file)) ||
result.AlternateVersions.Any(i => ContainsFile(i, file)) ||
@@ -246,10 +236,5 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
{
return string.Equals(result.Path, file.FullName, StringComparison.OrdinalIgnoreCase);
}
private static bool IsIgnored(string filename)
{
return false;
}
}
}

View File

@@ -6,6 +6,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Emby.Naming.Audio;
using Emby.Naming.Common;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
@@ -22,20 +23,17 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
public class MusicAlbumResolver : ItemResolver<MusicAlbum>
{
private readonly ILogger<MusicAlbumResolver> _logger;
private readonly IFileSystem _fileSystem;
private readonly ILibraryManager _libraryManager;
private readonly NamingOptions _namingOptions;
/// <summary>
/// Initializes a new instance of the <see cref="MusicAlbumResolver"/> class.
/// </summary>
/// <param name="logger">The logger.</param>
/// <param name="fileSystem">The file system.</param>
/// <param name="libraryManager">The library manager.</param>
public MusicAlbumResolver(ILogger<MusicAlbumResolver> logger, IFileSystem fileSystem, ILibraryManager libraryManager)
/// <param name="namingOptions">The naming options.</param>
public MusicAlbumResolver(ILogger<MusicAlbumResolver> logger, NamingOptions namingOptions)
{
_logger = logger;
_fileSystem = fileSystem;
_libraryManager = libraryManager;
_namingOptions = namingOptions;
}
/// <summary>
@@ -87,7 +85,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
/// <returns><c>true</c> if the provided path points to a music album, <c>false</c> otherwise.</returns>
public bool IsMusicAlbum(string path, IDirectoryService directoryService)
{
return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService, _logger, _fileSystem, _libraryManager);
return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService);
}
/// <summary>
@@ -101,7 +99,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
if (args.IsDirectory)
{
// if (args.Parent is MusicArtist) return true; // saves us from testing children twice
if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService, _logger, _fileSystem, _libraryManager))
if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService))
{
return true;
}
@@ -116,13 +114,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
private bool ContainsMusic(
IEnumerable<FileSystemMetadata> list,
bool allowSubfolders,
IDirectoryService directoryService,
ILogger<MusicAlbumResolver> logger,
IFileSystem fileSystem,
ILibraryManager libraryManager)
IDirectoryService directoryService)
{
// check for audio files before digging down into directories
var foundAudioFile = list.Any(fileSystemInfo => !fileSystemInfo.IsDirectory && libraryManager.IsAudioFile(fileSystemInfo.FullName));
var foundAudioFile = list.Any(fileSystemInfo => !fileSystemInfo.IsDirectory && AudioFileParser.IsAudioFile(fileSystemInfo.FullName, _namingOptions));
if (foundAudioFile)
{
// at least one audio file exists
@@ -137,21 +132,20 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
var discSubfolderCount = 0;
var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
var parser = new AlbumParser(namingOptions);
var parser = new AlbumParser(_namingOptions);
var directories = list.Where(fileSystemInfo => fileSystemInfo.IsDirectory);
var result = Parallel.ForEach(directories, (fileSystemInfo, state) =>
{
var path = fileSystemInfo.FullName;
var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryManager);
var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService);
if (hasMusic)
{
if (parser.IsMultiPart(path))
{
logger.LogDebug("Found multi-disc folder: {Path}", path);
_logger.LogDebug("Found multi-disc folder: {Path}", path);
Interlocked.Increment(ref discSubfolderCount);
}
else

View File

@@ -3,6 +3,7 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Emby.Naming.Common;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
@@ -19,27 +20,19 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
public class MusicArtistResolver : ItemResolver<MusicArtist>
{
private readonly ILogger<MusicAlbumResolver> _logger;
private readonly IFileSystem _fileSystem;
private readonly ILibraryManager _libraryManager;
private readonly IServerConfigurationManager _config;
private NamingOptions _namingOptions;
/// <summary>
/// Initializes a new instance of the <see cref="MusicArtistResolver"/> class.
/// </summary>
/// <param name="logger">The logger for the created <see cref="MusicAlbumResolver"/> instances.</param>
/// <param name="fileSystem">The file system.</param>
/// <param name="libraryManager">The library manager.</param>
/// <param name="config">The configuration manager.</param>
/// <param name="namingOptions">The naming options.</param>
public MusicArtistResolver(
ILogger<MusicAlbumResolver> logger,
IFileSystem fileSystem,
ILibraryManager libraryManager,
IServerConfigurationManager config)
NamingOptions namingOptions)
{
_logger = logger;
_fileSystem = fileSystem;
_libraryManager = libraryManager;
_config = config;
_namingOptions = namingOptions;
}
/// <summary>
@@ -89,7 +82,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
var directoryService = args.DirectoryService;
var albumResolver = new MusicAlbumResolver(_logger, _fileSystem, _libraryManager);
var albumResolver = new MusicAlbumResolver(_logger, _namingOptions);
// If we contain an album assume we are an artist folder
var directories = args.FileSystemChildren.Where(i => i.IsDirectory);

View File

@@ -6,6 +6,7 @@ using System;
using System.IO;
using System.Linq;
using DiscUtils.Udf;
using Emby.Naming.Common;
using Emby.Naming.Video;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -21,12 +22,12 @@ namespace Emby.Server.Implementations.Library.Resolvers
public abstract class BaseVideoResolver<T> : MediaBrowser.Controller.Resolvers.ItemResolver<T>
where T : Video, new()
{
protected BaseVideoResolver(ILibraryManager libraryManager)
protected BaseVideoResolver(NamingOptions namingOptions)
{
LibraryManager = libraryManager;
NamingOptions = namingOptions;
}
protected ILibraryManager LibraryManager { get; }
protected NamingOptions NamingOptions { get; }
/// <summary>
/// Resolves the specified args.
@@ -48,7 +49,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
protected virtual TVideoType ResolveVideo<TVideoType>(ItemResolveArgs args, bool parseName)
where TVideoType : Video, new()
{
var namingOptions = LibraryManager.GetNamingOptions();
var namingOptions = NamingOptions;
// If the path is a file check for a matching extensions
if (args.IsDirectory)
@@ -138,7 +139,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
return null;
}
if (LibraryManager.IsVideoFile(args.Path) || videoInfo.IsStub)
if (VideoResolver.IsVideoFile(args.Path, NamingOptions) || videoInfo.IsStub)
{
var path = args.Path;
@@ -267,7 +268,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
protected void Set3DFormat(Video video)
{
var result = Format3DParser.Parse(video.Path, LibraryManager.GetNamingOptions());
var result = Format3DParser.Parse(video.Path, NamingOptions);
Set3DFormat(video, result.Is3D, result.Format3D);
}

View File

@@ -2,16 +2,16 @@
#pragma warning disable CS1591
using Emby.Naming.Common;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
namespace Emby.Server.Implementations.Library.Resolvers
{
public class GenericVideoResolver<T> : BaseVideoResolver<T>
where T : Video, new()
{
public GenericVideoResolver(ILibraryManager libraryManager)
: base(libraryManager)
public GenericVideoResolver(NamingOptions namingOptions)
: base(namingOptions)
{
}
}

View File

@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Emby.Naming.Common;
using Emby.Naming.Video;
using Jellyfin.Extensions;
using MediaBrowser.Controller.Drawing;
@@ -25,6 +26,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
public class MovieResolver : BaseVideoResolver<Video>, IMultiItemResolver
{
private readonly IImageProcessor _imageProcessor;
private readonly StackResolver _stackResolver;
private string[] _validCollectionTypes = new[]
{
@@ -38,12 +40,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
/// <summary>
/// Initializes a new instance of the <see cref="MovieResolver"/> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="imageProcessor">The image processor.</param>
public MovieResolver(ILibraryManager libraryManager, IImageProcessor imageProcessor)
: base(libraryManager)
/// <param name="namingOptions">The naming options.</param>
public MovieResolver(IImageProcessor imageProcessor, NamingOptions namingOptions)
: base(namingOptions)
{
_imageProcessor = imageProcessor;
_stackResolver = new StackResolver(NamingOptions);
}
/// <summary>
@@ -89,9 +92,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return null;
}
var files = args.FileSystemChildren
.Where(i => !LibraryManager.IgnoreFile(i, args.Parent))
.ToList();
var files = args.GetActualFileSystemChildren().ToList();
if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
{
@@ -258,9 +259,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
}
}
var namingOptions = LibraryManager.GetNamingOptions();
var resolverResult = VideoListResolver.Resolve(files, namingOptions, suppportMultiEditions).ToList();
var resolverResult = VideoListResolver.Resolve(files, NamingOptions, suppportMultiEditions).ToList();
var result = new MultiItemResolverResult
{
@@ -438,7 +437,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
if (result.Items.Count == 1)
{
var videoPath = result.Items[0].Path;
var hasPhotos = photos.Any(i => !PhotoResolver.IsOwnedByResolvedMedia(LibraryManager, videoPath, i.Name));
var hasPhotos = photos.Any(i => !PhotoResolver.IsOwnedByResolvedMedia(videoPath, i.Name));
if (!hasPhotos)
{
@@ -511,9 +510,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return null;
}
var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions();
var result = new StackResolver(namingOptions).ResolveDirectories(folderPaths).ToList();
var result = _stackResolver.ResolveDirectories(folderPaths).ToList();
if (result.Count != 1)
{

View File

@@ -1,6 +1,7 @@
#nullable disable
using System;
using Emby.Naming.Common;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -15,17 +16,17 @@ namespace Emby.Server.Implementations.Library.Resolvers
public class PhotoAlbumResolver : GenericFolderResolver<PhotoAlbum>
{
private readonly IImageProcessor _imageProcessor;
private readonly ILibraryManager _libraryManager;
private readonly NamingOptions _namingOptions;
/// <summary>
/// Initializes a new instance of the <see cref="PhotoAlbumResolver"/> class.
/// </summary>
/// <param name="imageProcessor">The image processor.</param>
/// <param name="libraryManager">The library manager.</param>
public PhotoAlbumResolver(IImageProcessor imageProcessor, ILibraryManager libraryManager)
/// <param name="namingOptions">The naming options.</param>
public PhotoAlbumResolver(IImageProcessor imageProcessor, NamingOptions namingOptions)
{
_imageProcessor = imageProcessor;
_libraryManager = libraryManager;
_namingOptions = namingOptions;
}
/// <inheritdoc />
@@ -73,7 +74,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
foreach (var siblingFile in files)
{
if (PhotoResolver.IsOwnedByMedia(_libraryManager, siblingFile.FullName, filename))
if (PhotoResolver.IsOwnedByMedia(_namingOptions, siblingFile.FullName, filename))
{
ownedByMedia = true;
break;

View File

@@ -6,6 +6,8 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Emby.Naming.Common;
using Emby.Naming.Video;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -16,7 +18,8 @@ namespace Emby.Server.Implementations.Library.Resolvers
public class PhotoResolver : ItemResolver<Photo>
{
private readonly IImageProcessor _imageProcessor;
private readonly ILibraryManager _libraryManager;
private readonly NamingOptions _namingOptions;
private static readonly HashSet<string> _ignoreFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"folder",
@@ -30,10 +33,11 @@ namespace Emby.Server.Implementations.Library.Resolvers
"default"
};
public PhotoResolver(IImageProcessor imageProcessor, ILibraryManager libraryManager)
public PhotoResolver(IImageProcessor imageProcessor, NamingOptions namingOptions)
{
_imageProcessor = imageProcessor;
_libraryManager = libraryManager;
_namingOptions = namingOptions;
}
/// <summary>
@@ -60,7 +64,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
foreach (var file in files)
{
if (IsOwnedByMedia(_libraryManager, file.FullName, filename))
if (IsOwnedByMedia(_namingOptions, file.FullName, filename))
{
return null;
}
@@ -77,17 +81,12 @@ namespace Emby.Server.Implementations.Library.Resolvers
return null;
}
internal static bool IsOwnedByMedia(ILibraryManager libraryManager, string file, string imageFilename)
internal static bool IsOwnedByMedia(NamingOptions namingOptions, string file, string imageFilename)
{
if (libraryManager.IsVideoFile(file))
{
return IsOwnedByResolvedMedia(libraryManager, file, imageFilename);
}
return false;
return VideoResolver.IsVideoFile(file, namingOptions) && IsOwnedByResolvedMedia(file, imageFilename);
}
internal static bool IsOwnedByResolvedMedia(ILibraryManager libraryManager, string file, string imageFilename)
internal static bool IsOwnedByResolvedMedia(string file, string imageFilename)
=> imageFilename.StartsWith(Path.GetFileNameWithoutExtension(file), StringComparison.OrdinalIgnoreCase);
internal static bool IsImageFile(string path, IImageProcessor imageProcessor)

View File

@@ -2,6 +2,7 @@
using System;
using System.Linq;
using Emby.Naming.Common;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
@@ -17,9 +18,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
/// <summary>
/// Initializes a new instance of the <see cref="EpisodeResolver"/> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
public EpisodeResolver(ILibraryManager libraryManager)
: base(libraryManager)
/// <param name="namingOptions">The naming options.</param>
public EpisodeResolver(NamingOptions namingOptions)
: base(namingOptions)
{
}

View File

@@ -1,6 +1,7 @@
#nullable disable
using System.Globalization;
using Emby.Naming.Common;
using Emby.Naming.TV;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
@@ -14,22 +15,22 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
/// </summary>
public class SeasonResolver : GenericFolderResolver<Season>
{
private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localization;
private readonly ILogger<SeasonResolver> _logger;
private readonly NamingOptions _namingOptions;
/// <summary>
/// Initializes a new instance of the <see cref="SeasonResolver"/> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="namingOptions">The naming options.</param>
/// <param name="localization">The localization.</param>
/// <param name="logger">The logger.</param>
public SeasonResolver(
ILibraryManager libraryManager,
NamingOptions namingOptions,
ILocalizationManager localization,
ILogger<SeasonResolver> logger)
{
_libraryManager = libraryManager;
_namingOptions = namingOptions;
_localization = localization;
_logger = logger;
}
@@ -43,7 +44,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
{
if (args.Parent is Series series && args.IsDirectory)
{
var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
var namingOptions = _namingOptions;
var path = args.Path;
@@ -65,18 +66,15 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
var episodeInfo = resolver.Resolve(testPath, true);
if (episodeInfo != null)
if (episodeInfo?.EpisodeNumber != null && episodeInfo.SeasonNumber.HasValue)
{
if (episodeInfo.EpisodeNumber.HasValue && episodeInfo.SeasonNumber.HasValue)
{
_logger.LogDebug(
"Found folder underneath series with episode number: {0}. Season {1}. Episode {2}",
path,
episodeInfo.SeasonNumber.Value,
episodeInfo.EpisodeNumber.Value);
_logger.LogDebug(
"Found folder underneath series with episode number: {0}. Season {1}. Episode {2}",
path,
episodeInfo.SeasonNumber.Value,
episodeInfo.EpisodeNumber.Value);
return null;
}
return null;
}
}

View File

@@ -5,7 +5,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Emby.Naming.Common;
using Emby.Naming.TV;
using Emby.Naming.Video;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers;
@@ -21,17 +26,17 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
public class SeriesResolver : GenericFolderResolver<Series>
{
private readonly ILogger<SeriesResolver> _logger;
private readonly ILibraryManager _libraryManager;
private readonly NamingOptions _namingOptions;
/// <summary>
/// Initializes a new instance of the <see cref="SeriesResolver"/> class.
/// </summary>
/// <param name="logger">The logger.</param>
/// <param name="libraryManager">The library manager.</param>
public SeriesResolver(ILogger<SeriesResolver> logger, ILibraryManager libraryManager)
/// <param name="namingOptions">The naming options.</param>
public SeriesResolver(ILogger<SeriesResolver> logger, NamingOptions namingOptions)
{
_logger = logger;
_libraryManager = libraryManager;
_namingOptions = namingOptions;
}
/// <summary>
@@ -54,12 +59,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
return null;
}
var seriesInfo = Naming.TV.SeriesResolver.Resolve(_libraryManager.GetNamingOptions(), args.Path);
var seriesInfo = Naming.TV.SeriesResolver.Resolve(_namingOptions, args.Path);
var collectionType = args.GetCollectionType();
if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
{
var configuredContentType = _libraryManager.GetConfiguredContentType(args.Path);
// TODO refactor into separate class or something, this is copied from LibraryManager.GetConfiguredContentType
var configuredContentType = args.GetConfiguredContentType();
if (!string.Equals(configuredContentType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
{
return new Series
@@ -91,7 +97,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
return null;
}
if (IsSeriesFolder(args.Path, args.FileSystemChildren, _logger, _libraryManager, false))
if (IsSeriesFolder(args.Path, args.FileSystemChildren, false))
{
return new Series
{
@@ -105,11 +111,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
return null;
}
public static bool IsSeriesFolder(
private bool IsSeriesFolder(
string path,
IEnumerable<FileSystemMetadata> fileSystemChildren,
ILogger<SeriesResolver> logger,
ILibraryManager libraryManager,
bool isTvContentType)
{
foreach (var child in fileSystemChildren)
@@ -118,21 +122,21 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
{
if (IsSeasonFolder(child.FullName, isTvContentType))
{
logger.LogDebug("{Path} is a series because of season folder {Dir}.", path, child.FullName);
_logger.LogDebug("{Path} is a series because of season folder {Dir}.", path, child.FullName);
return true;
}
}
else
{
string fullName = child.FullName;
if (libraryManager.IsVideoFile(fullName))
if (VideoResolver.IsVideoFile(path, _namingOptions))
{
if (isTvContentType)
{
return true;
}
var namingOptions = ((LibraryManager)libraryManager).GetNamingOptions();
var namingOptions = _namingOptions;
var episodeResolver = new Naming.TV.EpisodeResolver(namingOptions);
@@ -145,7 +149,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
}
}
logger.LogDebug("{Path} is not a series folder.", path);
_logger.LogDebug("{Path} is not a series folder.", path);
return false;
}

View File

@@ -0,0 +1,156 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using MediaBrowser.Controller.Collections;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Querying;
using Jellyfin.Data.Enums;
using Microsoft.Extensions.Logging;
using MediaBrowser.Model.Entities;
namespace Emby.Server.Implementations.Library.Validators
{
/// <summary>
/// Class CollectionPostScanTask.
/// </summary>
public class CollectionPostScanTask : ILibraryPostScanTask
{
private readonly ILibraryManager _libraryManager;
private readonly ICollectionManager _collectionManager;
private readonly ILogger<CollectionPostScanTask> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="CollectionPostScanTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="collectionManager">The collection manager.</param>
/// <param name="logger">The logger.</param>
public CollectionPostScanTask(
ILibraryManager libraryManager,
ICollectionManager collectionManager,
ILogger<CollectionPostScanTask> logger)
{
_libraryManager = libraryManager;
_collectionManager = collectionManager;
_logger = logger;
}
/// <summary>
/// Runs the specified progress.
/// </summary>
/// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
var collectionNameMoviesMap = new Dictionary<string, HashSet<Guid>>();
foreach (var library in _libraryManager.RootFolder.Children)
{
if (!_libraryManager.GetLibraryOptions(library).AutomaticallyAddToCollection)
{
continue;
}
var startIndex = 0;
var pagesize = 1000;
while (true)
{
var movies = _libraryManager.GetItemList(new InternalItemsQuery
{
MediaTypes = new string[] { MediaType.Video },
IncludeItemTypes = new[] { nameof(Movie) },
IsVirtualItem = false,
OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) },
Parent = library,
StartIndex = startIndex,
Limit = pagesize,
Recursive = true
});
foreach (var m in movies)
{
if (m is Movie movie && !string.IsNullOrEmpty(movie.CollectionName))
{
if (collectionNameMoviesMap.TryGetValue(movie.CollectionName, out var movieList))
{
movieList.Add(movie.Id);
}
else
{
collectionNameMoviesMap[movie.CollectionName] = new HashSet<Guid> { movie.Id };
}
}
}
if (movies.Count < pagesize)
{
break;
}
startIndex += pagesize;
}
}
var numComplete = 0;
var count = collectionNameMoviesMap.Count;
if (count == 0)
{
progress.Report(100);
return;
}
var boxSets = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = new[] { nameof(BoxSet) },
CollapseBoxSetItems = false,
Recursive = true
});
foreach (var (collectionName, movieIds) in collectionNameMoviesMap)
{
try
{
var boxSet = boxSets.FirstOrDefault(b => b?.Name == collectionName) as BoxSet;
if (boxSet == null)
{
// won't automatically create collection if only one movie in it
if (movieIds.Count >= 2)
{
boxSet = await _collectionManager.CreateCollectionAsync(new CollectionCreationOptions
{
Name = collectionName,
IsLocked = true
});
await _collectionManager.AddToCollectionAsync(boxSet.Id, movieIds);
}
}
else
{
await _collectionManager.AddToCollectionAsync(boxSet.Id, movieIds);
}
numComplete++;
double percent = numComplete;
percent /= count;
percent *= 100;
progress.Report(percent);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error refreshing {CollectionName} with {@MovieIds}", collectionName, movieIds);
}
}
progress.Report(100);
}
}
}

View File

@@ -187,7 +187,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
CultureInfo.InvariantCulture,
"-i \"{0}\" {2} -map_metadata -1 -threads {6} {3}{4}{5} -y \"{1}\"",
inputTempFile,
targetFile.Replace("\"", "\\\""), // Escape quotes in filename
targetFile.Replace("\"", "\\\"", StringComparison.Ordinal), // Escape quotes in filename
videoArgs,
GetAudioArgs(mediaSource),
subtitleArgs,

View File

@@ -9,8 +9,10 @@ using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Net.Http.Headers;
using System.Net.Mime;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Threading;
@@ -20,7 +22,6 @@ using Jellyfin.Extensions;
using Jellyfin.Extensions.Json;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv;
@@ -35,7 +36,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private readonly ILogger<SchedulesDirect> _logger;
private readonly IHttpClientFactory _httpClientFactory;
private readonly SemaphoreSlim _tokenSemaphore = new SemaphoreSlim(1, 1);
private readonly ICryptoProvider _cryptoProvider;
private readonly ConcurrentDictionary<string, NameValuePair> _tokens = new ConcurrentDictionary<string, NameValuePair>();
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
@@ -43,12 +43,10 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public SchedulesDirect(
ILogger<SchedulesDirect> logger,
IHttpClientFactory httpClientFactory,
ICryptoProvider cryptoProvider)
IHttpClientFactory httpClientFactory)
{
_logger = logger;
_httpClientFactory = httpClientFactory;
_cryptoProvider = cryptoProvider;
}
/// <inheritdoc />
@@ -104,11 +102,10 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
};
var requestString = JsonSerializer.Serialize(requestList, _jsonOptions);
_logger.LogDebug("Request string for schedules is: {RequestString}", requestString);
_logger.LogDebug("Request string for schedules is: {@RequestString}", requestList);
using var options = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/schedules");
options.Content = new StringContent(requestString, Encoding.UTF8, MediaTypeNames.Application.Json);
options.Content = JsonContent.Create(requestList, options: _jsonOptions);
options.Headers.TryAddWithoutValidation("token", token);
using var response = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
@@ -124,8 +121,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
programRequestOptions.Headers.TryAddWithoutValidation("token", token);
var programIds = dailySchedules.SelectMany(d => d.Programs.Select(s => s.ProgramId)).Distinct();
programRequestOptions.Content = new ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes(programIds, _jsonOptions));
programRequestOptions.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(MediaTypeNames.Application.Json);
programRequestOptions.Content = JsonContent.Create(programIds, options: _jsonOptions);
using var innerResponse = await Send(programRequestOptions, true, info, cancellationToken).ConfigureAwait(false);
await using var innerResponseStream = await innerResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
@@ -169,12 +165,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings
const double DesiredAspect = 2.0 / 3;
programEntry.PrimaryImage = GetProgramImage(ApiUrl, imagesWithText, true, DesiredAspect) ??
GetProgramImage(ApiUrl, allImages, true, DesiredAspect);
programEntry.PrimaryImage = GetProgramImage(ApiUrl, imagesWithText, DesiredAspect) ??
GetProgramImage(ApiUrl, allImages, DesiredAspect);
const double WideAspect = 16.0 / 9;
programEntry.ThumbImage = GetProgramImage(ApiUrl, imagesWithText, true, WideAspect);
programEntry.ThumbImage = GetProgramImage(ApiUrl, imagesWithText, WideAspect);
// Don't supply the same image twice
if (string.Equals(programEntry.PrimaryImage, programEntry.ThumbImage, StringComparison.Ordinal))
@@ -182,7 +178,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
programEntry.ThumbImage = null;
}
programEntry.BackdropImage = GetProgramImage(ApiUrl, imagesWithoutText, true, WideAspect);
programEntry.BackdropImage = GetProgramImage(ApiUrl, imagesWithoutText, WideAspect);
// programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ??
// GetProgramImage(ApiUrl, data, "Banner-L1", false) ??
@@ -403,7 +399,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return info;
}
private string GetProgramImage(string apiUrl, IEnumerable<ImageDataDto> images, bool returnDefaultImage, double desiredAspect)
private static string GetProgramImage(string apiUrl, IEnumerable<ImageDataDto> images, double desiredAspect)
{
var match = images
.OrderBy(i => Math.Abs(desiredAspect - GetAspectRatio(i)))
@@ -648,7 +644,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
CancellationToken cancellationToken)
{
using var options = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/token");
var hashedPasswordBytes = _cryptoProvider.ComputeHash("SHA1", Encoding.ASCII.GetBytes(password), Array.Empty<byte>());
var hashedPasswordBytes = SHA1.HashData(Encoding.ASCII.GetBytes(password));
// TODO: remove ToLower when Convert.ToHexString supports lowercase
// Schedules Direct requires the hex to be lowercase
string hashedPassword = Convert.ToHexString(hashedPasswordBytes).ToLowerInvariant();

View File

@@ -51,7 +51,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
public async Task<bool> CheckTunerAvailability(IPAddress remoteIp, int tuner, CancellationToken cancellationToken)
{
using var client = new TcpClient();
client.Connect(remoteIp, HdHomeRunPort);
await client.ConnectAsync(remoteIp, HdHomeRunPort).ConfigureAwait(false);
using var stream = client.GetStream();
return await CheckTunerAvailability(stream, tuner, cancellationToken).ConfigureAwait(false);

View File

@@ -283,7 +283,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
// #EXTINF:0,84.0 - VOX Schweiz
if (!string.IsNullOrWhiteSpace(nameInExtInf))
{
var numberIndex = nameInExtInf.IndexOf(' ');
var numberIndex = nameInExtInf.IndexOf(' ', StringComparison.Ordinal);
if (numberIndex > 0)
{
var numberPart = nameInExtInf.Substring(0, numberIndex).Trim(new[] { ' ', '.' });

View File

@@ -11,11 +11,11 @@
"Collections": "التجميعات",
"DeviceOfflineWithName": "قُطِع الاتصال ب{0}",
"DeviceOnlineWithName": "{0} متصل",
"FailedLoginAttemptWithUserName": "عملية تسجيل الدخول فشلت من {0}",
"FailedLoginAttemptWithUserName": "محاولة تسجيل الدخول فشلت من {0}",
"Favorites": "مفضلات",
"Folders": "المجلدات",
"Genres": "التضنيفات",
"HeaderAlbumArtists": "ألبوم الفنان",
"HeaderAlbumArtists": "فناني الألبوم",
"HeaderContinueWatching": "استمر بالمشاهدة",
"HeaderFavoriteAlbums": "الألبومات المفضلة",
"HeaderFavoriteArtists": "الفنانون المفضلون",

View File

@@ -0,0 +1 @@
{}

View File

@@ -1 +1,4 @@
{}
{
"Sync": "Сінхранізацыя",
"Playlists": "Плэйліст"
}

View File

@@ -25,7 +25,7 @@
"HeaderLiveTV": "TV en Directe",
"HeaderNextUp": "A continuació",
"HeaderRecordingGroups": "Grups d'Enregistrament",
"HomeVideos": "Vídeos domèstics",
"HomeVideos": "Vídeos Domèstics",
"Inherit": "Hereta",
"ItemAddedWithName": "{0} ha estat afegit a la biblioteca",
"ItemRemovedWithName": "{0} ha estat eliminat de la biblioteca",
@@ -39,7 +39,7 @@
"MixedContent": "Contingut barrejat",
"Movies": "Pel·lícules",
"Music": "Música",
"MusicVideos": "Vídeos musicals",
"MusicVideos": "Vídeos Musicals",
"NameInstallFailed": "Instalació de {0} fallida",
"NameSeasonNumber": "Temporada {0}",
"NameSeasonUnknown": "Temporada Desconeguda",

View File

@@ -15,7 +15,7 @@
"Favorites": "Oblíbené",
"Folders": "Složky",
"Genres": "Žánry",
"HeaderAlbumArtists": "Album umělce",
"HeaderAlbumArtists": "Umělci alba",
"HeaderContinueWatching": "Pokračovat ve sledování",
"HeaderFavoriteAlbums": "Oblíbená alba",
"HeaderFavoriteArtists": "Oblíbení interpreti",

View File

@@ -15,7 +15,7 @@
"Favorites": "Favourites",
"Folders": "Folders",
"Genres": "Genres",
"HeaderAlbumArtists": "Artist's Album",
"HeaderAlbumArtists": "Album artists",
"HeaderContinueWatching": "Continue Watching",
"HeaderFavoriteAlbums": "Favourite Albums",
"HeaderFavoriteArtists": "Favourite Artists",

View File

@@ -12,12 +12,12 @@
"Default": "Default",
"DeviceOfflineWithName": "{0} has disconnected",
"DeviceOnlineWithName": "{0} is connected",
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
"FailedLoginAttemptWithUserName": "Failed login try from {0}",
"Favorites": "Favorites",
"Folders": "Folders",
"Forced": "Forced",
"Genres": "Genres",
"HeaderAlbumArtists": "Artist's Album",
"HeaderAlbumArtists": "Album artists",
"HeaderContinueWatching": "Continue Watching",
"HeaderFavoriteAlbums": "Favorite Albums",
"HeaderFavoriteArtists": "Favorite Artists",

View File

@@ -11,7 +11,7 @@
"ItemAddedWithName": "{0} aldonis al la plurmediteko",
"HeaderLiveTV": "TV-etero",
"HeaderContinueWatching": "Daŭrigi Spektadon",
"HeaderAlbumArtists": "Albumo de artisto",
"HeaderAlbumArtists": "Artistoj de albumo",
"Folders": "Dosierujoj",
"DeviceOnlineWithName": "{0} estas konektita",
"Default": "Defaŭlte",
@@ -104,7 +104,7 @@
"TaskRefreshChannelsDescription": "Refreŝigas informon pri interretaj kanaloj.",
"TaskDownloadMissingSubtitles": "Elŝuti mankantajn subtekstojn",
"TaskCleanTranscode": "Malplenigi Transkodadan Katalogon",
"TaskRefreshChapterImages": "Eltiri Ĉapitraj Bildojn",
"TaskRefreshChapterImages": "Eltiri Ĉapitrajn Bildojn",
"TaskCleanCache": "Malplenigi Staplan Katalogon",
"TaskCleanActivityLog": "Malplenigi Aktivecan Ĵurnalon",
"PluginUpdatedWithName": "{0} estis ĝisdatigita",

View File

@@ -15,8 +15,8 @@
"Favorites": "Favoritos",
"Folders": "Carpetas",
"Genres": "Géneros",
"HeaderAlbumArtists": "Artista del álbum",
"HeaderContinueWatching": "Continuar viendo",
"HeaderAlbumArtists": "Artistas del álbum",
"HeaderContinueWatching": "Seguir viendo",
"HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderFavoriteArtists": "Artistas favoritos",
"HeaderFavoriteEpisodes": "Episodios favoritos",

View File

@@ -15,7 +15,7 @@
"HeaderFavoriteEpisodes": "Episodios favoritos",
"HeaderFavoriteShows": "Programas favoritos",
"HeaderContinueWatching": "Continuar viendo",
"HeaderAlbumArtists": "Artistas del álbum",
"HeaderAlbumArtists": "Artistas de álbum",
"Genres": "Géneros",
"Folders": "Carpetas",
"Favorites": "Favoritos",
@@ -29,7 +29,7 @@
"TaskRefreshChannelsDescription": "Actualiza la información de canales de Internet.",
"TaskRefreshChannels": "Actualizar canales",
"TaskCleanTranscodeDescription": "Elimina archivos transcodificados que tengan más de un día.",
"TaskCleanTranscode": "Limpiar directorio de transcodificado",
"TaskCleanTranscode": "Limpiar el directorio de transcodificaciones",
"TaskUpdatePluginsDescription": "Descarga e instala actualizaciones para complementos que están configurados para actualizarse automáticamente.",
"TaskUpdatePlugins": "Actualizar complementos",
"TaskRefreshPeopleDescription": "Actualiza metadatos de actores y directores en tu biblioteca de medios.",
@@ -105,7 +105,7 @@
"Inherit": "Heredar",
"HomeVideos": "Videos caseros",
"HeaderRecordingGroups": "Grupos de grabación",
"FailedLoginAttemptWithUserName": "Intento fallido de inicio de sesión desde {0}",
"FailedLoginAttemptWithUserName": "Intento de inicio de sesión fallido desde {0}",
"DeviceOnlineWithName": "{0} está conectado",
"DeviceOfflineWithName": "{0} se ha desconectado",
"ChapterNameValue": "Capítulo {0}",
@@ -114,10 +114,10 @@
"Application": "Aplicación",
"AppDeviceValues": "App: {0}, Dispositivo: {1}",
"TaskCleanActivityLogDescription": "Elimina las entradas del registro de actividad anteriores al periodo configurado.",
"TaskCleanActivityLog": "Limpiar Registro de Actividades",
"TaskCleanActivityLog": "Limpiar registro de actividades",
"Undefined": "Sin definir",
"Forced": "Forzado",
"Default": "Por Defecto",
"TaskOptimizeDatabaseDescription": "Compacta la base de datos y restaura el espacio libre. Ejecutar esta tarea después de actualizar las librerías o realizar otros cambios que impliquen modificar las bases de datos puede mejorar la performance.",
"TaskOptimizeDatabase": "Optimización de base de datos"
"Default": "Por defecto",
"TaskOptimizeDatabaseDescription": "Compacta la base de datos y libera espacio. Ejecutar esta tarea después de escanear la biblioteca o hacer otros cambios que impliquen modificaciones en la base de datos puede mejorar el rendimiento.",
"TaskOptimizeDatabase": "Optimizar base de datos"
}

View File

@@ -32,9 +32,9 @@
"ValueSpecialEpisodeName": "Eriepisood - {0}",
"ValueHasBeenAddedToLibrary": "{0} lisati meediakogusse",
"UserStartedPlayingItemWithValues": "{0} taasesitab {1} serveris {2}",
"UserPasswordChangedWithName": "Kasutaja {0} parooli on muudetud",
"UserLockedOutWithName": "Kasutaja {0} on lukustatud",
"UserDeletedWithName": "Kasutaja {0} on kustutatud",
"UserPasswordChangedWithName": "Kasutaja {0} parool muudeti",
"UserLockedOutWithName": "Kasutaja {0} lukustati",
"UserDeletedWithName": "Kasutaja {0} kustutati",
"UserCreatedWithName": "Kasutaja {0} on loodud",
"ScheduledTaskStartedWithName": "{0} käivitati",
"ProviderValue": "Allikas: {0}",
@@ -54,9 +54,9 @@
"Plugin": "Plugin",
"Playlists": "Pleilistid",
"Photos": "Fotod",
"NotificationOptionVideoPlaybackStopped": "Video taasesitus on peatatud",
"NotificationOptionVideoPlaybackStopped": "Video taasesitus lõppes",
"NotificationOptionVideoPlayback": "Video taasesitus algas",
"NotificationOptionUserLockedOut": "Kasutaja on lukustatud",
"NotificationOptionUserLockedOut": "Kasutaja lukustati",
"NotificationOptionTaskFailed": "Ajastatud ülesanne nurjus",
"NotificationOptionServerRestartRequired": "Vajalik on serveri taaskäivitamine",
"NotificationOptionPluginUpdateInstalled": "Paigaldati plugina uuendus",
@@ -66,7 +66,7 @@
"NotificationOptionNewLibraryContent": "Lisati uut sisu",
"NotificationOptionInstallationFailed": "Paigaldamine nurjus",
"NotificationOptionCameraImageUploaded": "Kaamera pilt on üles laaditud",
"NotificationOptionAudioPlaybackStopped": "Heli taasesitus peatati",
"NotificationOptionAudioPlaybackStopped": "Heli taasesitus lõppes",
"NotificationOptionAudioPlayback": "Heli taasesitus algas",
"NotificationOptionApplicationUpdateInstalled": "Rakenduse uuendus paigaldati",
"NotificationOptionApplicationUpdateAvailable": "Rakenduse uuendus on saadaval",
@@ -97,12 +97,12 @@
"HeaderFavoriteArtists": "Lemmikesitajad",
"HeaderFavoriteAlbums": "Lemmikalbumid",
"HeaderContinueWatching": "Jätka vaatamist",
"HeaderAlbumArtists": "Albumi esitaja",
"HeaderAlbumArtists": "Albumi esitajad",
"Genres": "Žanrid",
"Forced": "Sunnitud",
"Folders": "Kaustad",
"Favorites": "Lemmikud",
"FailedLoginAttemptWithUserName": "Ebaõnnestunud sisselogimiskatse kasutajalt {0}",
"FailedLoginAttemptWithUserName": "{0} - sisselogimine nurjus",
"DeviceOnlineWithName": "{0} on ühendatud",
"DeviceOfflineWithName": "{0} katkestas ühenduse",
"Default": "Vaikimisi",
@@ -114,5 +114,10 @@
"Artists": "Esitajad",
"Application": "Rakendus",
"AppDeviceValues": "Rakendus: {0}, seade: {1}",
"Albums": "Albumid"
"Albums": "Albumid",
"UserOfflineFromDevice": "{0} katkestas ühenduse seadmega {1}",
"SubtitleDownloadFailureFromForItem": "Subtiitrite allalaadimine {0} > {1} nurjus",
"UserPolicyUpdatedWithName": "Kasutaja {0} õigusi värskendati",
"UserStoppedPlayingItemWithValues": "{0} lõpetas {1} taasesituse seadmes {2}",
"UserOnlineFromDevice": "{0} on ühendatud seadmest {1}"
}

View File

@@ -6,7 +6,7 @@
"AuthenticationSucceededWithUserName": "{0} با موفقیت تایید اعتبار شد",
"Books": "کتاب‌ها",
"CameraImageUploadedFrom": "یک عکس جدید از دوربین ارسال شده است {0}",
"Channels": "کانالها",
"Channels": "کانالها",
"ChapterNameValue": "قسمت {0}",
"Collections": "مجموعه‌ها",
"DeviceOfflineWithName": "ارتباط {0} قطع شد",
@@ -37,7 +37,7 @@
"MessageNamedServerConfigurationUpdatedWithValue": "پکربندی بخش {0} سرور بروزرسانی شد",
"MessageServerConfigurationUpdated": "پیکربندی سرور بروزرسانی شد",
"MixedContent": "محتوای مخلوط",
"Movies": "فیلمها",
"Movies": "فیلم ها",
"Music": "موسیقی",
"MusicVideos": "موزیک ویدیوها",
"NameInstallFailed": "{0} نصب با مشکل مواجه شد",

View File

@@ -15,7 +15,7 @@
"Favorites": "Favoris",
"Folders": "Dossiers",
"Genres": "Genres",
"HeaderAlbumArtists": "Artistes de l'album",
"HeaderAlbumArtists": "Artistes d'album",
"HeaderContinueWatching": "Continuer à regarder",
"HeaderFavoriteAlbums": "Albums favoris",
"HeaderFavoriteArtists": "Artistes préférés",

View File

@@ -15,7 +15,7 @@
"Favorites": "Favoriti",
"Folders": "Mape",
"Genres": "Žanrovi",
"HeaderAlbumArtists": "Album od izvođača",
"HeaderAlbumArtists": "Izvođači albuma",
"HeaderContinueWatching": "Nastavi gledati",
"HeaderFavoriteAlbums": "Omiljeni albumi",
"HeaderFavoriteArtists": "Omiljeni izvođači",

View File

@@ -11,11 +11,11 @@
"Collections": "Gyűjtemények",
"DeviceOfflineWithName": "{0} kijelentkezett",
"DeviceOnlineWithName": "{0} belépett",
"FailedLoginAttemptWithUserName": "Sikertelen bejelentkezési kísérlet tőle: {0}",
"FailedLoginAttemptWithUserName": "Sikertelen bejelentkezési kísérlet innen: {0}",
"Favorites": "Kedvencek",
"Folders": "Könyvtárak",
"Genres": "Műfajok",
"HeaderAlbumArtists": "Előadó albumai",
"HeaderAlbumArtists": "Album előadó(k)",
"HeaderContinueWatching": "Megtekintés folytatása",
"HeaderFavoriteAlbums": "Kedvenc albumok",
"HeaderFavoriteArtists": "Kedvenc előadók",

View File

@@ -7,10 +7,10 @@
"MessageApplicationUpdated": "Jellyfin Server sudah diperbarui",
"Latest": "Terbaru",
"LabelIpAddressValue": "Alamat IP: {0}",
"ItemRemovedWithName": "{0} sudah dikeluarkan dari pustaka",
"ItemRemovedWithName": "{0} sudah dihapus dari pustaka",
"ItemAddedWithName": "{0} telah dimasukkan ke dalam pustaka",
"Inherit": "Warisan",
"HomeVideos": "Video Rumah",
"Inherit": "Warisi",
"HomeVideos": "Video Rumahan",
"HeaderRecordingGroups": "Grup Rekaman",
"HeaderNextUp": "Selanjutnya",
"HeaderLiveTV": "TV Live",
@@ -73,7 +73,7 @@
"NotificationOptionCameraImageUploaded": "Gambar kamera terunggah",
"NotificationOptionApplicationUpdateInstalled": "Pembaruan aplikasi terpasang",
"NotificationOptionApplicationUpdateAvailable": "Pembaruan aplikasi tersedia",
"NewVersionIsAvailable": "Versi baru dari Jellyfin Server tersedia untuk diunduh.",
"NewVersionIsAvailable": "Versi baru dari Jellyfin Server sudah tersedia untuk diunduh.",
"NameSeasonUnknown": "Musim tak diketahui",
"NameSeasonNumber": "Musim {0}",
"NameInstallFailed": "{0} penginstalan gagal",
@@ -117,5 +117,7 @@
"TaskCleanActivityLog": "Bersihkan Log Aktivitas",
"Undefined": "Tidak terdefinisi",
"Forced": "Dipaksa",
"Default": "Bawaan"
"Default": "Bawaan",
"TaskOptimizeDatabaseDescription": "Rapihkan basis data dan membersihkan ruang kosong. Menjalankan tugas ini setelah memindai pustaka atau melakukan perubahan lain yang menyiratkan modifikasi basis data dapat meningkatkan kinerja.",
"TaskOptimizeDatabase": "Optimalkan basis data"
}

View File

@@ -15,7 +15,7 @@
"Favorites": "Preferiti",
"Folders": "Cartelle",
"Genres": "Generi",
"HeaderAlbumArtists": "Artisti dell'Album",
"HeaderAlbumArtists": "Artisti dell'album",
"HeaderContinueWatching": "Continua a guardare",
"HeaderFavoriteAlbums": "Album Preferiti",
"HeaderFavoriteArtists": "Artisti Preferiti",

View File

@@ -11,11 +11,11 @@
"Collections": "コレクション",
"DeviceOfflineWithName": "{0} が切断されました",
"DeviceOnlineWithName": "{0} が接続されました",
"FailedLoginAttemptWithUserName": "ログインを試行しましたが {0}によって失敗しました",
"FailedLoginAttemptWithUserName": "ログインを試行しましたが {0} によって失敗しました",
"Favorites": "お気に入り",
"Folders": "フォルダー",
"Genres": "ジャンル",
"HeaderAlbumArtists": "アーティストのアルバム",
"HeaderAlbumArtists": "アルバムアーティスト",
"HeaderContinueWatching": "視聴を続ける",
"HeaderFavoriteAlbums": "お気に入りのアルバム",
"HeaderFavoriteArtists": "お気に入りのアーティスト",

View File

@@ -15,7 +15,7 @@
"Favorites": "Tañdaulylar",
"Folders": "Qaltalar",
"Genres": "Janrlar",
"HeaderAlbumArtists": "Oryndauşynyñ älbomy",
"HeaderAlbumArtists": "Älbom oryndauşylary",
"HeaderContinueWatching": "Qaraudy jalğastyru",
"HeaderFavoriteAlbums": "Tañdauly älbomdar",
"HeaderFavoriteArtists": "Tañdauly oryndauşylar",

View File

@@ -1,7 +1,7 @@
{
"Albums": "Albumai",
"AppDeviceValues": "Programa: {0}, Įrenginys: {1}",
"Application": "Programa",
"Application": "Programėlė",
"Artists": "Atlikėjai",
"AuthenticationSucceededWithUserName": "{0} sėkmingai autentifikuota",
"Books": "Knygos",

View File

@@ -5,7 +5,7 @@
"PluginUninstalledWithName": "{0} беше успешно деинсталирано",
"PluginInstalledWithName": "{0} беше успешно инсталирано",
"Plugin": "Додатоци",
"Playlists": "Листи",
"Playlists": "Плејлисти",
"Photos": "Слики",
"NotificationOptionVideoPlaybackStopped": "Видео стопирано",
"NotificationOptionVideoPlayback": "Видео пуштено",
@@ -50,7 +50,7 @@
"HeaderFavoriteEpisodes": "Омилени Епизоди",
"HeaderFavoriteArtists": "Омилени Изведувачи",
"HeaderFavoriteAlbums": "Омилени Албуми",
"HeaderContinueWatching": "Продолжи со гледање",
"HeaderContinueWatching": "Продолжи со Гледање",
"HeaderAlbumArtists": "Изведувачи од Албуми",
"Genres": "Жанрови",
"Folders": "Папки",
@@ -97,5 +97,8 @@
"TasksChannelsCategory": "Интернет Канали",
"TasksApplicationCategory": "Апликација",
"TasksLibraryCategory": "Библиотека",
"TasksMaintenanceCategory": "Одржување"
"TasksMaintenanceCategory": "Одржување",
"Undefined": "Недефинирано",
"Forced": "Принудно",
"Default": "Зададено"
}

View File

@@ -0,0 +1,14 @@
{
"Books": "Номууд",
"HeaderNextUp": "Дараах",
"HeaderContinueWatching": "Үргэлжлүүлэн үзэх",
"Songs": "Дуунууд",
"Playlists": "Тоглуулах жагсаалт",
"Movies": "Кино",
"Latest": "Сүүлийн үеийн",
"Genres": "Төрөл зүйл",
"Favorites": "Дуртай",
"Collections": "Багц",
"Artists": "Зураачуд",
"Albums": "Цомгууд"
}

View File

@@ -37,9 +37,9 @@
"MessageNamedServerConfigurationUpdatedWithValue": "Konfigurasi pelayan di bahagian {0} telah dikemas kini",
"MessageServerConfigurationUpdated": "Konfigurasi pelayan telah dikemas kini",
"MixedContent": "Kandungan campuran",
"Movies": "Filem",
"Movies": "Filem-filem",
"Music": "Muzik",
"MusicVideos": "Muzik video",
"MusicVideos": "",
"NameInstallFailed": "{0} pemasangan gagal",
"NameSeasonNumber": "Musim {0}",
"NameSeasonUnknown": "Musim Tidak Diketahui",
@@ -53,43 +53,43 @@
"NotificationOptionNewLibraryContent": "Kandungan baru telah ditambah",
"NotificationOptionPluginError": "Kegagalan plugin",
"NotificationOptionPluginInstalled": "Plugin telah dipasang",
"NotificationOptionPluginUninstalled": "Plugin uninstalled",
"NotificationOptionPluginUpdateInstalled": "Plugin update installed",
"NotificationOptionServerRestartRequired": "Server restart required",
"NotificationOptionTaskFailed": "Scheduled task failure",
"NotificationOptionUserLockedOut": "User locked out",
"NotificationOptionVideoPlayback": "Video playback started",
"NotificationOptionPluginUninstalled": "Plugin telah dinyahpasang",
"NotificationOptionPluginUpdateInstalled": "Kemaskini plugin telah dipasang",
"NotificationOptionServerRestartRequired": "",
"NotificationOptionTaskFailed": "Kegagalan tugas berjadual",
"NotificationOptionUserLockedOut": "Pengguna telah dikunci",
"NotificationOptionVideoPlayback": "Ulangmain video bermula",
"NotificationOptionVideoPlaybackStopped": "Ulangmain video dihentikan",
"Photos": "Gambar-gambar",
"Playlists": "Senarai main",
"Plugin": "Plugin",
"PluginInstalledWithName": "{0} was installed",
"PluginUninstalledWithName": "{0} was uninstalled",
"PluginUpdatedWithName": "{0} was updated",
"ProviderValue": "Provider: {0}",
"PluginInstalledWithName": "{0} telah dipasang",
"PluginUninstalledWithName": "{0} telah dinyahpasang",
"PluginUpdatedWithName": "{0} telah dikemaskini",
"ProviderValue": "Pembekal: {0}",
"ScheduledTaskFailedWithName": "{0} gagal",
"ScheduledTaskStartedWithName": "{0} bermula",
"ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
"Shows": "Series",
"ServerNameNeedsToBeRestarted": "{0} perlu di ulangmula",
"Shows": "Tayangan",
"Songs": "Lagu-lagu",
"StartupEmbyServerIsLoading": "Pelayan Jellyfin sedang dimuatkan. Sila cuba sebentar lagi.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "Muat turun sarikata gagal dari {0} untuk {1}",
"Sync": "Sync",
"Sync": "",
"System": "Sistem",
"TvShows": "TV Shows",
"User": "User",
"UserCreatedWithName": "User {0} has been created",
"UserDeletedWithName": "User {0} has been deleted",
"UserDownloadingItemWithValues": "{0} is downloading {1}",
"TvShows": "Tayangan TV",
"User": "Pengguna",
"UserCreatedWithName": "Pengguna {0} telah diwujudkan",
"UserDeletedWithName": "Pengguna {0} telah dipadamkan",
"UserDownloadingItemWithValues": "{0} sedang memuat turun {1}",
"UserLockedOutWithName": "Pengguna {0} telah dikunci",
"UserOfflineFromDevice": "{0} telah terputus dari {1}",
"UserOnlineFromDevice": "{0} berada dalam talian dari {1}",
"UserPasswordChangedWithName": "Kata laluan telah ditukar bagi pengguna {0}",
"UserPolicyUpdatedWithName": "Dasar pengguna telah dikemas kini untuk {0}",
"UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
"UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
"UserStartedPlayingItemWithValues": "{0} sedang dimainkan {1} pada {2}",
"UserStoppedPlayingItemWithValues": "{0} telah tamat dimainkan {1} pada {2}",
"ValueHasBeenAddedToLibrary": "{0} telah ditambah ke media library anda",
"ValueSpecialEpisodeName": "Khas - {0}",
"VersionNumber": "Versi {0}",
"TaskCleanActivityLog": "Log Aktiviti Bersih",

View File

@@ -69,7 +69,7 @@
"UserDeletedWithName": "प्रयोगकर्ता {0} हटाइएको छ",
"UserCreatedWithName": "प्रयोगकर्ता {0} सिर्जना गरिएको छ",
"User": "प्रयोगकर्ता",
"PluginInstalledWithName": "",
"PluginInstalledWithName": "{0} सभएको थियो",
"StartupEmbyServerIsLoading": "Jellyfin सर्भर लोड हुँदैछ। कृपया छिट्टै फेरि प्रयास गर्नुहोस्।",
"Songs": "गीतहरू",
"Shows": "शोहरू",

View File

@@ -15,7 +15,7 @@
"Favorites": "Favorieten",
"Folders": "Mappen",
"Genres": "Genres",
"HeaderAlbumArtists": "Artiests Album",
"HeaderAlbumArtists": "Album Artiesten",
"HeaderContinueWatching": "Kijken hervatten",
"HeaderFavoriteAlbums": "Favoriete albums",
"HeaderFavoriteArtists": "Favoriete artiesten",

View File

@@ -24,7 +24,7 @@
"TasksLibraryCategory": "ਲਾਇਬ੍ਰੇਰੀ",
"TasksMaintenanceCategory": "ਰੱਖ-ਰਖਾਅ",
"VersionNumber": "ਵਰਜਨ {0}",
"ValueSpecialEpisodeName": "ਵਿਸ਼ੇਸ਼ - {0}",
"ValueSpecialEpisodeName": "ਖਾਸ - {0}",
"ValueHasBeenAddedToLibrary": "{0} ਤੁਹਾਡੀ ਮੀਡੀਆ ਲਾਇਬ੍ਰੇਰੀ ਵਿੱਚ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ ਹੈ",
"UserStoppedPlayingItemWithValues": "{0} ਨੇ {2} 'ਤੇ {1} ਖੇਡਣਾ ਪੂਰਾ ਕਰ ਲਿਆ ਹੈ",
"UserStartedPlayingItemWithValues": "{0} {2} 'ਤੇ {1} ਖੇਡ ਰਿਹਾ ਹੈ",
@@ -43,8 +43,8 @@
"Sync": "ਸਿੰਕ",
"SubtitleDownloadFailureFromForItem": "ਉਪਸਿਰਲੇਖ {1} ਲਈ {0} ਤੋਂ ਡਾ toਨਲੋਡ ਕਰਨ ਵਿੱਚ ਅਸਫਲ ਰਹੇ",
"StartupEmbyServerIsLoading": "ਜੈਲੀਫਿਨ ਸਰਵਰ ਲੋਡ ਹੋ ਰਿਹਾ ਹੈ. ਕਿਰਪਾ ਕਰਕੇ ਜਲਦੀ ਹੀ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ.",
"Songs": "ਗਾਣੇ",
"Shows": "ਸ਼ੋਅਜ਼",
"Songs": "ਗਾਣੇ",
"Shows": "ਸ਼ੋਅ",
"ServerNameNeedsToBeRestarted": "{0} ਮੁੜ ਚਾਲੂ ਕਰਨ ਦੀ ਲੋੜ ਹੈ",
"ScheduledTaskStartedWithName": "{0} ਸ਼ੁਰੂ ਹੋਇਆ",
"ScheduledTaskFailedWithName": "{0} ਅਸਫਲ",
@@ -53,7 +53,7 @@
"PluginUninstalledWithName": "{0} ਅਣਇੰਸਟੌਲ ਕੀਤਾ ਗਿਆ ਸੀ",
"PluginInstalledWithName": "{0} ਲਗਾਇਆ ਗਿਆ ਸੀ",
"Plugin": "ਪਲੱਗਇਨ",
"Playlists": "ਪਲੇਲਿਸਟਸ",
"Playlists": "ਪਲੇਸੂਚੀਆਂ",
"Photos": "ਫੋਟੋਆਂ",
"NotificationOptionVideoPlaybackStopped": "ਵੀਡੀਓ ਪਲੇਬੈਕ ਰੋਕਿਆ ਗਿਆ",
"NotificationOptionVideoPlayback": "ਵੀਡੀਓ ਪਲੇਬੈਕ ਸ਼ੁਰੂ ਹੋਇਆ",
@@ -102,13 +102,13 @@
"HeaderAlbumArtists": "ਐਲਬਮ ਕਲਾਕਾਰ",
"Genres": "ਸ਼ੈਲੀਆਂ",
"Forced": "ਮਜਬੂਰ",
"Folders": "ਫੋਲਡਰ",
"Folders": "ਫੋਲਡਰ",
"Favorites": "ਮਨਪਸੰਦ",
"FailedLoginAttemptWithUserName": "ਤੋਂ ਲਾਗਇਨ ਕੋਸ਼ਿਸ਼ ਫੇਲ ਹੋਈ {0}",
"DeviceOnlineWithName": "{0} ਜੁੜਿਆ ਹੋਇਆ ਹੈ",
"DeviceOfflineWithName": "{0} ਡਿਸਕਨੈਕਟ ਹੋ ਗਿਆ ਹੈ",
"Default": "ਮੂਲ",
"Collections": "ਸੰਗ੍ਰਹਿ",
"Default": "ਡਿਫੌਲਟ",
"Collections": "ਸੰਗ੍ਰਹਿ",
"ChapterNameValue": "ਅਧਿਆਇ {0}",
"Channels": "ਚੈਨਲ",
"CameraImageUploadedFrom": "ਤੋਂ ਇੱਕ ਨਵਾਂ ਕੈਮਰਾ ਚਿੱਤਰ ਅਪਲੋਡ ਕੀਤਾ ਗਿਆ ਹੈ {0}",

View File

@@ -15,7 +15,7 @@
"Favorites": "Ulubione",
"Folders": "Foldery",
"Genres": "Gatunki",
"HeaderAlbumArtists": "Album artysty",
"HeaderAlbumArtists": "Wykonawcy albumów",
"HeaderContinueWatching": "Kontynuuj odtwarzanie",
"HeaderFavoriteAlbums": "Ulubione albumy",
"HeaderFavoriteArtists": "Ulubieni wykonawcy",
@@ -47,7 +47,7 @@
"NotificationOptionApplicationUpdateAvailable": "Dostępna aktualizacja aplikacji",
"NotificationOptionApplicationUpdateInstalled": "Zaktualizowano aplikację",
"NotificationOptionAudioPlayback": "Rozpoczęto odtwarzanie muzyki",
"NotificationOptionAudioPlaybackStopped": "Odtwarzane dźwięku zatrzymane",
"NotificationOptionAudioPlaybackStopped": "Odtwarzanie dźwięku zatrzymane",
"NotificationOptionCameraImageUploaded": "Przekazano obraz z urządzenia przenośnego",
"NotificationOptionInstallationFailed": "Nieudana instalacja",
"NotificationOptionNewLibraryContent": "Dodano nową zawartość",
@@ -98,7 +98,7 @@
"TaskRefreshChannels": "Odśwież kanały",
"TaskCleanTranscodeDescription": "Usuwa transkodowane pliki starsze niż 1 dzień.",
"TaskCleanTranscode": "Wyczyść folder transkodowania",
"TaskUpdatePluginsDescription": "Pobiera i instaluje aktualizacje dla pluginów które są skonfigurowane do automatycznej aktualizacji.",
"TaskUpdatePluginsDescription": "Pobiera i instaluje aktualizacje dla pluginów, które są skonfigurowane do automatycznej aktualizacji.",
"TaskUpdatePlugins": "Aktualizuj pluginy",
"TaskRefreshPeopleDescription": "Odświeża metadane o aktorów i reżyserów w Twojej bibliotece mediów.",
"TaskRefreshPeople": "Odśwież obsadę",

View File

@@ -1,5 +1,7 @@
{
"Books": "Libros",
"AuthenticationSucceededWithUserName": "{0} autentificado correctamente",
"Artists": "Artistas"
"Artists": "Artistas",
"Songs": "Shantees",
"Albums": "Ships"
}

View File

@@ -15,7 +15,7 @@
"Favorites": "Favoritos",
"Folders": "Pastas",
"Genres": "Géneros",
"HeaderAlbumArtists": "Artistas do Álbum",
"HeaderAlbumArtists": "Álbum do Artista",
"HeaderContinueWatching": "Continuar a Ver",
"HeaderFavoriteAlbums": "Álbuns Favoritos",
"HeaderFavoriteArtists": "Artistas Favoritos",

View File

@@ -74,7 +74,7 @@
"HeaderFavoriteArtists": "Artiști Favoriți",
"HeaderFavoriteAlbums": "Albume Favorite",
"HeaderContinueWatching": "Vizionează în continuare",
"HeaderAlbumArtists": "Album Artiști",
"HeaderAlbumArtists": "Albume Artiști",
"Genres": "Genuri",
"Folders": "Dosare",
"Favorites": "Favorite",

View File

@@ -74,7 +74,7 @@
"NameSeasonUnknown": "Sezon i panjohur",
"NameSeasonNumber": "Sezoni {0}",
"NameInstallFailed": "Instalimi i {0} dështoi",
"MusicVideos": "Video muzikore",
"MusicVideos": "Video Muzikore",
"Music": "Muzikë",
"Movies": "Filmat",
"MixedContent": "Përmbajtje e përzier",
@@ -96,7 +96,7 @@
"HeaderFavoriteArtists": "Artistët e preferuar",
"HeaderFavoriteAlbums": "Albumet e preferuar",
"HeaderContinueWatching": "Vazhdo të shikosh",
"HeaderAlbumArtists": "Artistët e Albumeve",
"HeaderAlbumArtists": "Artistët e albumeve",
"Genres": "Zhanret",
"Folders": "Skedarët",
"Favorites": "Të preferuarat",

View File

@@ -50,7 +50,7 @@
"NameSeasonUnknown": "Непозната сезона",
"NameSeasonNumber": "Сезона {0}",
"NameInstallFailed": "Инсталација {0} није успела",
"MusicVideos": "Музички спотови",
"MusicVideos": "Музички видео",
"Music": "Музика",
"Movies": "Филмови",
"MixedContent": "Мешовит садржај",
@@ -64,7 +64,7 @@
"ItemRemovedWithName": "{0} уклоњено из библиотеке",
"ItemAddedWithName": "{0} додато у библиотеку",
"Inherit": "Наследи",
"HomeVideos": "Кућни видео",
"HomeVideos": "Кућни Видео",
"HeaderRecordingGroups": "Групе снимања",
"HeaderNextUp": "Следи",
"HeaderLiveTV": "ТВ уживо",
@@ -117,5 +117,6 @@
"TaskCleanActivityLog": "Очисти историју активности",
"Undefined": "Недефинисано",
"Forced": "Принудно",
"Default": "Подразумевано"
"Default": "Подразумевано",
"TaskOptimizeDatabase": "Оптимизуј датабазу"
}

View File

@@ -15,7 +15,7 @@
"Favorites": "Favoriter",
"Folders": "Mappar",
"Genres": "Genrer",
"HeaderAlbumArtists": "Artistens album",
"HeaderAlbumArtists": "Albumsartister",
"HeaderContinueWatching": "Fortsätt kolla",
"HeaderFavoriteAlbums": "Favoritalbum",
"HeaderFavoriteArtists": "Favoritartister",
@@ -96,8 +96,8 @@
"TaskDownloadMissingSubtitles": "Ladda ned saknade undertexter",
"TaskRefreshChannelsDescription": "Uppdaterar information för internetkanaler.",
"TaskRefreshChannels": "Uppdatera kanaler",
"TaskCleanTranscodeDescription": "Raderar transkodningsfiler som är mer än en dag gamla.",
"TaskCleanTranscode": "Töm transkodningskatalog",
"TaskCleanTranscodeDescription": "Raderar omkodningsfiler som är mer än en dag gamla.",
"TaskCleanTranscode": "Töm omkodningskatalog",
"TaskUpdatePluginsDescription": "Laddar ned och installerar uppdateringar till insticksprogram som är konfigurerade att uppdateras automatiskt.",
"TaskUpdatePlugins": "Uppdatera insticksprogram",
"TaskRefreshPeopleDescription": "Uppdaterar metadata för skådespelare och regissörer i ditt mediabibliotek.",

View File

@@ -85,7 +85,7 @@
"HeaderFavoriteArtists": "பிடித்த கலைஞர்கள்",
"HeaderFavoriteAlbums": "பிடித்த ஆல்பங்கள்",
"HeaderContinueWatching": "தொடர்ந்து பார்",
"HeaderAlbumArtists": "இசைக் கலைஞர்கள்",
"HeaderAlbumArtists": "கலைஞரின் ஆல்பம்",
"Genres": "வகைகள்",
"Favorites": "பிடித்தவை",
"ChapterNameValue": "அத்தியாயம் {0}",

View File

@@ -1 +1,23 @@
{}
{
"ValueSpecialEpisodeName": "ప్రత్యేక - {0}",
"Sync": "సమకాలీకరించు",
"Songs": "పాటలు",
"Shows": "ప్రదర్శనలు",
"Playlists": "ప్లేజాబితాలు",
"Photos": "ఫోటోలు",
"MusicVideos": "మ్యూజిక్ వీడియోలు",
"Music": "సంగీతం",
"Movies": "సినిమాలు",
"HeaderContinueWatching": "చూడటం కొనసాగించండి",
"HeaderAlbumArtists": "ఆల్బమ్ కళాకారులు",
"Genres": "శైలులు",
"Forced": "బలవంతంగా",
"Folders": "ఫోల్డర్లు",
"Favorites": "ఇష్టమైనవి",
"Default": "డిఫాల్ట్",
"Collections": "సేకరణలు",
"Channels": "ఛానెల్‌లు",
"Books": "పుస్తకాలు",
"Artists": "కళాకారులు",
"Albums": "ఆల్బమ్‌లు"
}

View File

@@ -16,7 +16,7 @@
"HeaderFavoriteArtists": "Улюблені виконавці",
"HeaderFavoriteAlbums": "Улюблені альбоми",
"HeaderContinueWatching": "Продовжити перегляд",
"HeaderAlbumArtists": "Виконавці альбомів",
"HeaderAlbumArtists": "Виконавці альбому",
"Genres": "Жанри",
"Folders": "Каталоги",
"Favorites": "Улюблені",

View File

@@ -3,7 +3,7 @@
"Favorites": "Yêu Thích",
"Folders": "Thư Mục",
"Genres": "Thể Loại",
"HeaderAlbumArtists": "Album Nghệ sĩ",
"HeaderAlbumArtists": "Album nghệ sĩ",
"HeaderContinueWatching": "Xem Tiếp",
"HeaderLiveTV": "TV Trực Tiếp",
"Movies": "Phim",
@@ -95,7 +95,7 @@
"ItemRemovedWithName": "{0} đã xóa khỏi thư viện",
"ItemAddedWithName": "{0} được thêm vào thư viện",
"Inherit": "Thừa hưởng",
"HomeVideos": "Video nhà",
"HomeVideos": "Video Nhà",
"HeaderRecordingGroups": "Nhóm Ghi Video",
"HeaderNextUp": "Tiếp Theo",
"HeaderFavoriteSongs": "Bài Hát Yêu Thích",
@@ -103,7 +103,7 @@
"HeaderFavoriteEpisodes": "Tập Phim Yêu Thích",
"HeaderFavoriteArtists": "Nghệ Sĩ Yêu Thích",
"HeaderFavoriteAlbums": "Album Ưa Thích",
"FailedLoginAttemptWithUserName": "Nỗ lực đăng nhập thất bại từ {0}",
"FailedLoginAttemptWithUserName": "Đăng nhập không thành công thử từ {0}",
"DeviceOnlineWithName": "{0} đã kết nối",
"DeviceOfflineWithName": "{0} đã ngắt kết nối",
"ChapterNameValue": "Phân Cảnh {0}",

View File

@@ -11,7 +11,7 @@
"Collections": "合集",
"DeviceOfflineWithName": "{0} 已断开",
"DeviceOnlineWithName": "{0} 已连接",
"FailedLoginAttemptWithUserName": "来自 {0} 的失败登入",
"FailedLoginAttemptWithUserName": " {0} 尝试登录失败",
"Favorites": "我的最爱",
"Folders": "文件夹",
"Genres": "风格",

View File

@@ -373,60 +373,75 @@ namespace Emby.Server.Implementations.Localization
public IEnumerable<LocalizationOption> GetLocalizationOptions()
{
yield return new LocalizationOption("Afrikaans", "af");
yield return new LocalizationOption("Arabic", "ar");
yield return new LocalizationOption("Bulgarian (Bulgaria)", "bg-BG");
yield return new LocalizationOption("Catalan", "ca");
yield return new LocalizationOption("Chinese (Hong Kong)", "zh-HK");
yield return new LocalizationOption("Chinese Simplified", "zh-CN");
yield return new LocalizationOption("Chinese Traditional", "zh-TW");
yield return new LocalizationOption("Croatian", "hr");
yield return new LocalizationOption("Czech", "cs");
yield return new LocalizationOption("Danish", "da");
yield return new LocalizationOption("Dutch", "nl");
yield return new LocalizationOption("العربية", "ar");
yield return new LocalizationOption("Беларуская", "be");
yield return new LocalizationOption("Български", "bg-BG");
yield return new LocalizationOption("বাংলা (বাংলাদেশ)", "bn");
yield return new LocalizationOption("Català", "ca");
yield return new LocalizationOption("Čeština", "cs");
yield return new LocalizationOption("Cymraeg", "cy");
yield return new LocalizationOption("Dansk", "da");
yield return new LocalizationOption("Deutsch", "de");
yield return new LocalizationOption("English (United Kingdom)", "en-GB");
yield return new LocalizationOption("English (United States)", "en-US");
yield return new LocalizationOption("English", "en-US");
yield return new LocalizationOption("Ελληνικά", "el");
yield return new LocalizationOption("Esperanto", "eo");
yield return new LocalizationOption("Estonian", "et");
yield return new LocalizationOption("Finnish", "fi");
yield return new LocalizationOption("French", "fr");
yield return new LocalizationOption("French (Canada)", "fr-CA");
yield return new LocalizationOption("German", "de");
yield return new LocalizationOption("Greek", "el");
yield return new LocalizationOption("Hebrew", "he");
yield return new LocalizationOption("Hungarian", "hu");
yield return new LocalizationOption("Icelandic", "is");
yield return new LocalizationOption("Indonesian", "id");
yield return new LocalizationOption("Italian", "it");
yield return new LocalizationOption("Japanese", "ja");
yield return new LocalizationOption("Kazakh", "kk");
yield return new LocalizationOption("Korean", "ko");
yield return new LocalizationOption("Latvian", "lv");
yield return new LocalizationOption("Lithuanian", "lt-LT");
yield return new LocalizationOption("Malay", "ms");
yield return new LocalizationOption("Malayalam", "ml");
yield return new LocalizationOption("Norwegian Bokmål", "nb");
yield return new LocalizationOption("Norwegian Nynorsk", "nn");
yield return new LocalizationOption("Persian", "fa");
yield return new LocalizationOption("Polish", "pl");
yield return new LocalizationOption("Portuguese", "pt");
yield return new LocalizationOption("Portuguese (Brazil)", "pt-BR");
yield return new LocalizationOption("Portuguese (Portugal)", "pt-PT");
yield return new LocalizationOption("Romanian", "ro");
yield return new LocalizationOption("Russian", "ru");
yield return new LocalizationOption("Serbian", "sr");
yield return new LocalizationOption("Slovak", "sk");
yield return new LocalizationOption("Slovenian (Slovenia)", "sl-SI");
yield return new LocalizationOption("Spanish", "es");
yield return new LocalizationOption("Spanish (Argentina)", "es-AR");
yield return new LocalizationOption("Spanish (Latin America)", "es-419");
yield return new LocalizationOption("Spanish (Mexico)", "es-MX");
yield return new LocalizationOption("Swedish", "sv");
yield return new LocalizationOption("Swiss German", "gsw");
yield return new LocalizationOption("Tamil", "ta");
yield return new LocalizationOption("Telugu", "te");
yield return new LocalizationOption("Turkish", "tr");
yield return new LocalizationOption("Español", "es");
yield return new LocalizationOption("Español americano", "es_419");
yield return new LocalizationOption("Español (Argentina)", "es-AR");
yield return new LocalizationOption("Español (Dominicana)", "es_DO");
yield return new LocalizationOption("Español (México)", "es-MX");
yield return new LocalizationOption("Eesti", "et");
yield return new LocalizationOption("فارسی", "fa");
yield return new LocalizationOption("Suomi", "fi");
yield return new LocalizationOption("Filipino", "fil");
yield return new LocalizationOption("Français", "fr");
yield return new LocalizationOption("Français (Canada)", "fr-CA");
yield return new LocalizationOption("Galego", "gl");
yield return new LocalizationOption("Schwiizerdütsch", "gsw");
yield return new LocalizationOption("עִבְרִית", "he");
yield return new LocalizationOption("हिन्दी", "hi");
yield return new LocalizationOption("Hrvatski", "hr");
yield return new LocalizationOption("Magyar", "hu");
yield return new LocalizationOption("Bahasa Indonesia", "id");
yield return new LocalizationOption("Íslenska", "is");
yield return new LocalizationOption("Italiano", "it");
yield return new LocalizationOption("日本語", "ja");
yield return new LocalizationOption("Qazaqşa", "kk");
yield return new LocalizationOption("한국어", "ko");
yield return new LocalizationOption("Lietuvių", "lt");
yield return new LocalizationOption("Latviešu", "lv");
yield return new LocalizationOption("Македонски", "mk");
yield return new LocalizationOption("മലയാളം", "ml");
yield return new LocalizationOption("मराठी", "mr");
yield return new LocalizationOption("Bahasa Melayu", "ms");
yield return new LocalizationOption("Norsk bokmål", "nb");
yield return new LocalizationOption("नेपाली", "ne");
yield return new LocalizationOption("Nederlands", "nl");
yield return new LocalizationOption("Norsk nynorsk", "nn");
yield return new LocalizationOption("ਪੰਜਾਬੀ", "pa");
yield return new LocalizationOption("Polski", "pl");
yield return new LocalizationOption("Pirate", "pr");
yield return new LocalizationOption("Português", "pt");
yield return new LocalizationOption("Português (Brasil)", "pt-BR");
yield return new LocalizationOption("Português (Portugal)", "pt-PT");
yield return new LocalizationOption("Românește", "ro");
yield return new LocalizationOption("Русский", "ru");
yield return new LocalizationOption("Slovenčina", "sk");
yield return new LocalizationOption("Slovenščina", "sl-SI");
yield return new LocalizationOption("Shqip", "sq");
yield return new LocalizationOption("Српски", "sr");
yield return new LocalizationOption("Svenska", "sv");
yield return new LocalizationOption("தமிழ்", "ta");
yield return new LocalizationOption("తెలుగు", "te");
yield return new LocalizationOption("ภาษาไทย", "th");
yield return new LocalizationOption("Türkçe", "tr");
yield return new LocalizationOption("Українська", "uk");
yield return new LocalizationOption("اُردُو", "ur_PK");
yield return new LocalizationOption("Tiếng Việt", "vi");
yield return new LocalizationOption("Ukrainian", "uk");
yield return new LocalizationOption("汉语 (简化字)", "zh-CN");
yield return new LocalizationOption("漢語 (繁体字)", "zh-TW");
yield return new LocalizationOption("廣東話 (香港)", "zh-HK");
}
}
}

View File

@@ -527,7 +527,7 @@ namespace Emby.Server.Implementations.Playlists
var relativeUri = folderUri.MakeRelativeUri(fileAbsoluteUri);
string relativePath = Uri.UnescapeDataString(relativeUri.ToString());
if (fileAbsoluteUri.Scheme.Equals("file", StringComparison.CurrentCultureIgnoreCase))
if (fileAbsoluteUri.Scheme.Equals("file", StringComparison.OrdinalIgnoreCase))
{
relativePath = relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
}

View File

@@ -26,7 +26,7 @@ namespace Emby.Server.Implementations.Sorting
/// <returns>System.Int32.</returns>
public int Compare(BaseItem? x, BaseItem? y)
{
return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase);
return string.Compare(GetValue(x), GetValue(y), StringComparison.OrdinalIgnoreCase);
}
/// <summary>

View File

@@ -25,7 +25,7 @@ namespace Emby.Server.Implementations.Sorting
/// <returns>System.Int32.</returns>
public int Compare(BaseItem? x, BaseItem? y)
{
return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase);
return string.Compare(GetValue(x), GetValue(y), StringComparison.OrdinalIgnoreCase);
}
/// <summary>
@@ -35,9 +35,7 @@ namespace Emby.Server.Implementations.Sorting
/// <returns>System.String.</returns>
private static string? GetValue(BaseItem? x)
{
var audio = x as Audio;
return audio == null ? string.Empty : audio.Album;
return x is Audio audio ? audio.Album : string.Empty;
}
}
}

View File

@@ -17,7 +17,7 @@ namespace Emby.Server.Implementations.Sorting
/// <inheritdoc />
public int Compare(BaseItem? x, BaseItem? y)
{
return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase);
return string.Compare(GetValue(x), GetValue(y), StringComparison.OrdinalIgnoreCase);
}
/// <summary>

View File

@@ -34,7 +34,7 @@ namespace Emby.Server.Implementations.Sorting
throw new ArgumentNullException(nameof(y));
}
return string.Compare(x.Name, y.Name, StringComparison.CurrentCultureIgnoreCase);
return string.Compare(x.Name, y.Name, StringComparison.OrdinalIgnoreCase);
}
}
}

View File

@@ -25,7 +25,7 @@ namespace Emby.Server.Implementations.Sorting
/// <returns>System.Int32.</returns>
public int Compare(BaseItem x, BaseItem y)
{
return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase);
return string.Compare(GetValue(x), GetValue(y), StringComparison.OrdinalIgnoreCase);
}
private static string GetValue(BaseItem item)

View File

@@ -36,7 +36,7 @@ namespace Emby.Server.Implementations.Sorting
throw new ArgumentNullException(nameof(y));
}
return string.Compare(x.SortName, y.SortName, StringComparison.CurrentCultureIgnoreCase);
return string.Compare(x.SortName, y.SortName, StringComparison.OrdinalIgnoreCase);
}
}
}

View File

@@ -58,7 +58,7 @@ namespace Emby.Server.Implementations.Udp
_udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
}
private async Task RespondToV2Message(string messageText, EndPoint endpoint, CancellationToken cancellationToken)
private async Task RespondToV2Message(EndPoint endpoint, CancellationToken cancellationToken)
{
string? localUrl = _config[AddressOverrideConfigKey];
if (string.IsNullOrEmpty(localUrl))
@@ -76,7 +76,7 @@ namespace Emby.Server.Implementations.Udp
try
{
await _udpSocket.SendToAsync(JsonSerializer.SerializeToUtf8Bytes(response), SocketFlags.None, endpoint).ConfigureAwait(false);
await _udpSocket.SendToAsync(JsonSerializer.SerializeToUtf8Bytes(response), SocketFlags.None, endpoint, cancellationToken).ConfigureAwait(false);
}
catch (SocketException ex)
{
@@ -115,7 +115,7 @@ namespace Emby.Server.Implementations.Udp
var text = Encoding.UTF8.GetString(_receiveBuffer, 0, result.ReceivedBytes);
if (text.Contains("who is JellyfinServer?", StringComparison.OrdinalIgnoreCase))
{
await RespondToV2Message(text, result.RemoteEndPoint, cancellationToken).ConfigureAwait(false);
await RespondToV2Message(result.RemoteEndPoint, cancellationToken).ConfigureAwait(false);
}
}
catch (SocketException ex)

View File

@@ -1,4 +1,6 @@
using System;
#pragma warning disable CA1813 // Avoid unsealed attributes
using System;
namespace Jellyfin.Api.Attributes
{

View File

@@ -3,7 +3,7 @@
/// <summary>
/// Produces file attribute of "image/*".
/// </summary>
public class AcceptsImageFileAttribute : AcceptsFileAttribute
public sealed class AcceptsImageFileAttribute : AcceptsFileAttribute
{
private const string ContentType = "image/*";

View File

@@ -7,7 +7,7 @@ namespace Jellyfin.Api.Attributes
/// <summary>
/// Identifies an action that supports the HTTP GET method.
/// </summary>
public class HttpSubscribeAttribute : HttpMethodAttribute
public sealed class HttpSubscribeAttribute : HttpMethodAttribute
{
private static readonly IEnumerable<string> _supportedMethods = new[] { "SUBSCRIBE" };

View File

@@ -7,7 +7,7 @@ namespace Jellyfin.Api.Attributes
/// <summary>
/// Identifies an action that supports the HTTP GET method.
/// </summary>
public class HttpUnsubscribeAttribute : HttpMethodAttribute
public sealed class HttpUnsubscribeAttribute : HttpMethodAttribute
{
private static readonly IEnumerable<string> _supportedMethods = new[] { "UNSUBSCRIBE" };

View File

@@ -6,7 +6,7 @@ namespace Jellyfin.Api.Attributes
/// Attribute to mark a parameter as obsolete.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public class ParameterObsoleteAttribute : Attribute
public sealed class ParameterObsoleteAttribute : Attribute
{
}
}

View File

@@ -3,7 +3,7 @@
/// <summary>
/// Produces file attribute of "image/*".
/// </summary>
public class ProducesAudioFileAttribute : ProducesFileAttribute
public sealed class ProducesAudioFileAttribute : ProducesFileAttribute
{
private const string ContentType = "audio/*";

View File

@@ -1,4 +1,6 @@
using System;
#pragma warning disable CA1813 // Avoid unsealed attributes
using System;
namespace Jellyfin.Api.Attributes
{

View File

@@ -3,7 +3,7 @@
/// <summary>
/// Produces file attribute of "image/*".
/// </summary>
public class ProducesImageFileAttribute : ProducesFileAttribute
public sealed class ProducesImageFileAttribute : ProducesFileAttribute
{
private const string ContentType = "image/*";

View File

@@ -3,7 +3,7 @@
/// <summary>
/// Produces file attribute of "image/*".
/// </summary>
public class ProducesPlaylistFileAttribute : ProducesFileAttribute
public sealed class ProducesPlaylistFileAttribute : ProducesFileAttribute
{
private const string ContentType = "application/x-mpegURL";

View File

@@ -3,7 +3,7 @@
/// <summary>
/// Produces file attribute of "video/*".
/// </summary>
public class ProducesVideoFileAttribute : ProducesFileAttribute
public sealed class ProducesVideoFileAttribute : ProducesFileAttribute
{
private const string ContentType = "video/*";

View File

@@ -1,5 +1,4 @@
using System;
using System.Net.Mime;
using System.Net.Mime;
using System.Threading.Tasks;
using Jellyfin.Api.Attributes;
using Jellyfin.Api.Constants;
@@ -7,7 +6,6 @@ using Jellyfin.Api.Helpers;
using Jellyfin.Api.Models.ClientLogDtos;
using MediaBrowser.Controller.ClientEvent;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.ClientLog;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@@ -37,54 +35,6 @@ namespace Jellyfin.Api.Controllers
_serverConfigurationManager = serverConfigurationManager;
}
/// <summary>
/// Post event from client.
/// </summary>
/// <param name="clientLogEventDto">The client log dto.</param>
/// <response code="204">Event logged.</response>
/// <response code="403">Event logging disabled.</response>
/// <returns>Submission status.</returns>
[HttpPost]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
public ActionResult LogEvent([FromBody] ClientLogEventDto clientLogEventDto)
{
if (!_serverConfigurationManager.Configuration.AllowClientLogUpload)
{
return Forbid();
}
var (clientName, clientVersion, userId, deviceId) = GetRequestInformation();
Log(clientLogEventDto, userId, clientName, clientVersion, deviceId);
return NoContent();
}
/// <summary>
/// Bulk post events from client.
/// </summary>
/// <param name="clientLogEventDtos">The list of client log dtos.</param>
/// <response code="204">All events logged.</response>
/// <response code="403">Event logging disabled.</response>
/// <returns>Submission status.</returns>
[HttpPost("Bulk")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
public ActionResult LogEvents([FromBody] ClientLogEventDto[] clientLogEventDtos)
{
if (!_serverConfigurationManager.Configuration.AllowClientLogUpload)
{
return Forbid();
}
var (clientName, clientVersion, userId, deviceId) = GetRequestInformation();
foreach (var dto in clientLogEventDtos)
{
Log(dto, userId, clientName, clientVersion, deviceId);
}
return NoContent();
}
/// <summary>
/// Upload a document.
/// </summary>
@@ -111,39 +61,20 @@ namespace Jellyfin.Api.Controllers
return StatusCode(StatusCodes.Status413PayloadTooLarge, $"Payload must be less than {MaxDocumentSize:N0} bytes");
}
var (clientName, clientVersion, _, _) = GetRequestInformation();
var (clientName, clientVersion) = GetRequestInformation();
var fileName = await _clientEventLogger.WriteDocumentAsync(clientName, clientVersion, Request.Body)
.ConfigureAwait(false);
return Ok(new ClientLogDocumentResponseDto(fileName));
}
private void Log(
ClientLogEventDto dto,
Guid userId,
string clientName,
string clientVersion,
string deviceId)
{
_clientEventLogger.Log(new ClientLogEvent(
dto.Timestamp,
dto.Level,
userId,
clientName,
clientVersion,
deviceId,
dto.Message));
}
private (string ClientName, string ClientVersion, Guid UserId, string DeviceId) GetRequestInformation()
private (string ClientName, string ClientVersion) GetRequestInformation()
{
var clientName = ClaimHelpers.GetClient(HttpContext.User) ?? "unknown-client";
var clientVersion = ClaimHelpers.GetIsApiKey(HttpContext.User)
? "apikey"
: ClaimHelpers.GetVersion(HttpContext.User) ?? "unknown-version";
var userId = ClaimHelpers.GetUserId(HttpContext.User) ?? Guid.Empty;
var deviceId = ClaimHelpers.GetDeviceId(HttpContext.User) ?? "unknown-device-id";
return (clientName, clientVersion, userId, deviceId);
return (clientName, clientVersion);
}
}
}

View File

@@ -5,8 +5,6 @@ using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Api.Constants;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
@@ -30,7 +28,6 @@ namespace Jellyfin.Api.Controllers
public class ItemLookupController : BaseJellyfinApiController
{
private readonly IProviderManager _providerManager;
private readonly IServerApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem;
private readonly ILibraryManager _libraryManager;
private readonly ILogger<ItemLookupController> _logger;
@@ -39,19 +36,16 @@ namespace Jellyfin.Api.Controllers
/// Initializes a new instance of the <see cref="ItemLookupController"/> class.
/// </summary>
/// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="logger">Instance of the <see cref="ILogger{ItemLookupController}"/> interface.</param>
public ItemLookupController(
IProviderManager providerManager,
IServerConfigurationManager serverConfigurationManager,
IFileSystem fileSystem,
ILibraryManager libraryManager,
ILogger<ItemLookupController> logger)
{
_providerManager = providerManager;
_appPaths = serverConfigurationManager.ApplicationPaths;
_fileSystem = fileSystem;
_libraryManager = libraryManager;
_logger = logger;

View File

@@ -10,6 +10,7 @@ using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
@@ -33,6 +34,7 @@ namespace Jellyfin.Api.Controllers
private readonly ILocalizationManager _localization;
private readonly IDtoService _dtoService;
private readonly ILogger<ItemsController> _logger;
private readonly ISessionManager _sessionManager;
/// <summary>
/// Initializes a new instance of the <see cref="ItemsController"/> class.
@@ -42,18 +44,21 @@ namespace Jellyfin.Api.Controllers
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
/// <param name="dtoService">Instance of the <see cref="IDtoService"/> interface.</param>
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
/// <param name="sessionManager">Instance of the <see cref="ISessionManager"/> interface.</param>
public ItemsController(
IUserManager userManager,
ILibraryManager libraryManager,
ILocalizationManager localization,
IDtoService dtoService,
ILogger<ItemsController> logger)
ILogger<ItemsController> logger,
ISessionManager sessionManager)
{
_userManager = userManager;
_libraryManager = libraryManager;
_localization = localization;
_dtoService = dtoService;
_logger = logger;
_sessionManager = sessionManager;
}
/// <summary>
@@ -763,6 +768,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="includeItemTypes">Optional. If specified, results will be filtered based on the item type. This allows multiple, comma delimited.</param>
/// <param name="enableTotalRecordCount">Optional. Enable the total record count.</param>
/// <param name="enableImages">Optional. Include image information in output.</param>
/// <param name="excludeActiveSessions">Optional. Whether to exclude the currently active sessions.</param>
/// <response code="200">Items returned.</response>
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the items that are resumable.</returns>
[HttpGet("Users/{userId}/Items/Resume")]
@@ -781,7 +787,8 @@ namespace Jellyfin.Api.Controllers
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] excludeItemTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
[FromQuery] bool enableTotalRecordCount = true,
[FromQuery] bool? enableImages = true)
[FromQuery] bool? enableImages = true,
[FromQuery] bool excludeActiveSessions = false)
{
var user = _userManager.GetUserById(userId);
var parentIdGuid = parentId ?? Guid.Empty;
@@ -801,6 +808,15 @@ namespace Jellyfin.Api.Controllers
.ToArray();
}
var excludeItemIds = Array.Empty<Guid>();
if (excludeActiveSessions)
{
excludeItemIds = _sessionManager.Sessions
.Where(s => s.UserId == userId && s.NowPlayingItem != null)
.Select(s => s.NowPlayingItem.Id)
.ToArray();
}
var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
{
OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending) },
@@ -817,7 +833,8 @@ namespace Jellyfin.Api.Controllers
AncestorIds = ancestorIds,
IncludeItemTypes = RequestHelpers.GetItemTypeStrings(includeItemTypes),
ExcludeItemTypes = RequestHelpers.GetItemTypeStrings(excludeItemTypes),
SearchTerm = searchTerm
SearchTerm = searchTerm,
ExcludeItemIds = excludeItemIds
});
var returnItems = _dtoService.GetBaseItemDtos(itemsResult.Items, dtoOptions, user);

View File

@@ -26,7 +26,6 @@ namespace Jellyfin.Api.Controllers
private readonly ILibraryManager _libraryManager;
private readonly IDtoService _dtoService;
private readonly IUserManager _userManager;
private readonly IUserDataManager _userDataManager;
/// <summary>
/// Initializes a new instance of the <see cref="PersonsController"/> class.
@@ -34,17 +33,14 @@ namespace Jellyfin.Api.Controllers
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="dtoService">Instance of the <see cref="IDtoService"/> interface.</param>
/// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
/// <param name="userDataManager">Instance of the <see cref="IUserDataManager"/> interface.</param>
public PersonsController(
ILibraryManager libraryManager,
IDtoService dtoService,
IUserManager userManager,
IUserDataManager userDataManager)
IUserManager userManager)
{
_libraryManager = libraryManager;
_dtoService = dtoService;
_userManager = userManager;
_userDataManager = userDataManager;
}
/// <summary>

View File

@@ -28,7 +28,6 @@ namespace Jellyfin.Api.Controllers
{
private readonly IInstallationManager _installationManager;
private readonly IPluginManager _pluginManager;
private readonly IConfigurationManager _config;
private readonly JsonSerializerOptions _serializerOptions;
/// <summary>
@@ -36,16 +35,13 @@ namespace Jellyfin.Api.Controllers
/// </summary>
/// <param name="installationManager">Instance of the <see cref="IInstallationManager"/> interface.</param>
/// <param name="pluginManager">Instance of the <see cref="IPluginManager"/> interface.</param>
/// <param name="config">Instance of the <see cref="IConfigurationManager"/> interface.</param>
public PluginsController(
IInstallationManager installationManager,
IPluginManager pluginManager,
IConfigurationManager config)
IPluginManager pluginManager)
{
_installationManager = installationManager;
_pluginManager = pluginManager;
_serializerOptions = JsonDefaults.Options;
_config = config;
}
/// <summary>

View File

@@ -30,7 +30,6 @@ namespace Jellyfin.Api.Controllers
{
private readonly IProviderManager _providerManager;
private readonly IServerApplicationPaths _applicationPaths;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILibraryManager _libraryManager;
/// <summary>
@@ -38,17 +37,14 @@ namespace Jellyfin.Api.Controllers
/// </summary>
/// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
/// <param name="applicationPaths">Instance of the <see cref="IServerApplicationPaths"/> interface.</param>
/// <param name="httpClientFactory">Instance of the <see cref="IHttpClientFactory"/> interface.</param>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
public RemoteImageController(
IProviderManager providerManager,
IServerApplicationPaths applicationPaths,
IHttpClientFactory httpClientFactory,
ILibraryManager libraryManager)
{
_providerManager = providerManager;
_applicationPaths = applicationPaths;
_httpClientFactory = httpClientFactory;
_libraryManager = libraryManager;
}

View File

@@ -93,7 +93,7 @@ namespace Jellyfin.Api.Controllers
NetworkConfiguration settings = _config.GetNetworkConfiguration();
settings.EnableRemoteAccess = startupRemoteAccessDto.EnableRemoteAccess;
settings.EnableUPnP = startupRemoteAccessDto.EnableAutomaticPortMapping;
_config.SaveConfiguration("network", settings);
_config.SaveConfiguration(NetworkConfigurationStore.StoreKey, settings);
return NoContent();
}

View File

@@ -451,7 +451,7 @@ namespace Jellyfin.Api.Controllers
if (@static.HasValue && @static.Value && state.DirectStreamProvider != null)
{
StreamingHelpers.AddDlnaHeaders(state, Response.Headers, true, startTimeTicks, Request, _dlnaManager);
StreamingHelpers.AddDlnaHeaders(state, Response.Headers, true, state.Request.StartTimeTicks, Request, _dlnaManager);
var liveStreamInfo = _mediaSourceManager.GetLiveStreamInfo(streamingRequest.LiveStreamId);
if (liveStreamInfo == null)
@@ -467,7 +467,7 @@ namespace Jellyfin.Api.Controllers
// Static remote stream
if (@static.HasValue && @static.Value && state.InputProtocol == MediaProtocol.Http)
{
StreamingHelpers.AddDlnaHeaders(state, Response.Headers, true, startTimeTicks, Request, _dlnaManager);
StreamingHelpers.AddDlnaHeaders(state, Response.Headers, true, state.Request.StartTimeTicks, Request, _dlnaManager);
var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
return await FileStreamResponseHelpers.GetStaticRemoteStreamResult(state, isHeadRequest, httpClient, HttpContext).ConfigureAwait(false);
@@ -484,7 +484,7 @@ namespace Jellyfin.Api.Controllers
var transcodingJob = _transcodingJobHelper.GetTranscodingJob(outputPath, TranscodingJobType.Progressive);
var isTranscodeCached = outputPathExists && transcodingJob != null;
StreamingHelpers.AddDlnaHeaders(state, Response.Headers, (@static.HasValue && @static.Value) || isTranscodeCached, startTimeTicks, Request, _dlnaManager);
StreamingHelpers.AddDlnaHeaders(state, Response.Headers, (@static.HasValue && @static.Value) || isTranscodeCached, state.Request.StartTimeTicks, Request, _dlnaManager);
// Static stream
if (@static.HasValue && @static.Value)

View File

@@ -1,71 +0,0 @@
using System;
using System.Reflection;
namespace Jellyfin.Api.Helpers
{
/// <summary>
/// A static class for copying matching properties from one object to another.
/// TODO: remove at the point when a fixed migration path has been decided upon.
/// </summary>
public static class ClassMigrationHelper
{
/// <summary>
/// Extension for 'Object' that copies the properties to a destination object.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="destination">The destination.</param>
public static void CopyProperties(this object source, object destination)
{
// If any this null throw an exception.
if (source == null || destination == null)
{
throw new ArgumentException("Source or/and Destination Objects are null");
}
// Getting the Types of the objects.
Type typeDest = destination.GetType();
Type typeSrc = source.GetType();
// Iterate the Properties of the source instance and populate them from their destination counterparts.
PropertyInfo[] srcProps = typeSrc.GetProperties();
foreach (PropertyInfo srcProp in srcProps)
{
if (!srcProp.CanRead)
{
continue;
}
var targetProperty = typeDest.GetProperty(srcProp.Name);
if (targetProperty == null)
{
continue;
}
if (!targetProperty.CanWrite)
{
continue;
}
var obj = targetProperty.GetSetMethod(true);
if (obj != null && obj.IsPrivate)
{
continue;
}
var target = targetProperty.GetSetMethod();
if (target != null && (target.Attributes & MethodAttributes.Static) != 0)
{
continue;
}
if (!targetProperty.PropertyType.IsAssignableFrom(srcProp.PropertyType))
{
continue;
}
// Passed all tests, lets set the value.
targetProperty.SetValue(destination, srcProp.GetValue(source, null), null);
}
}
}
}

View File

@@ -17,7 +17,6 @@ namespace Jellyfin.Api.Helpers
private readonly TranscodingJobDto? _job;
private readonly TranscodingJobHelper? _transcodingJobHelper;
private readonly int _timeoutMs;
private int _bytesWritten;
private bool _disposed;
/// <summary>
@@ -71,53 +70,58 @@ namespace Jellyfin.Api.Helpers
/// <inheritdoc />
public override void Flush()
{
_stream.Flush();
// Not supported
}
/// <inheritdoc />
public override int Read(byte[] buffer, int offset, int count)
=> Read(buffer.AsSpan(offset, count));
/// <inheritdoc />
public override int Read(Span<byte> buffer)
{
return _stream.Read(buffer, offset, count);
int totalBytesRead = 0;
var stopwatch = Stopwatch.StartNew();
while (KeepReading(stopwatch.ElapsedMilliseconds))
{
totalBytesRead += _stream.Read(buffer);
if (totalBytesRead > 0)
{
break;
}
Thread.Sleep(50);
}
UpdateBytesWritten(totalBytesRead);
return totalBytesRead;
}
/// <inheritdoc />
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
=> await ReadAsync(buffer.AsMemory(offset, count), cancellationToken).ConfigureAwait(false);
/// <inheritdoc />
public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
int totalBytesRead = 0;
int remainingBytesToRead = count;
var stopwatch = Stopwatch.StartNew();
int newOffset = offset;
while (remainingBytesToRead > 0)
while (KeepReading(stopwatch.ElapsedMilliseconds))
{
cancellationToken.ThrowIfCancellationRequested();
int bytesRead = await _stream.ReadAsync(buffer, newOffset, remainingBytesToRead, cancellationToken).ConfigureAwait(false);
remainingBytesToRead -= bytesRead;
newOffset += bytesRead;
if (bytesRead > 0)
totalBytesRead += await _stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
if (totalBytesRead > 0)
{
_bytesWritten += bytesRead;
totalBytesRead += bytesRead;
if (_job != null)
{
_job.BytesDownloaded = Math.Max(_job.BytesDownloaded ?? _bytesWritten, _bytesWritten);
}
break;
}
else
{
// If the job is null it's a live stream and will require user action to close, but don't keep it open indefinitely
if (_job?.HasExited ?? stopwatch.ElapsedMilliseconds > _timeoutMs)
{
break;
}
await Task.Delay(50, cancellationToken).ConfigureAwait(false);
}
await Task.Delay(50, cancellationToken).ConfigureAwait(false);
}
UpdateBytesWritten(totalBytesRead);
return totalBytesRead;
}
@@ -159,5 +163,19 @@ namespace Jellyfin.Api.Helpers
base.Dispose(disposing);
}
}
private void UpdateBytesWritten(int totalBytesRead)
{
if (_job != null)
{
_job.BytesDownloaded += totalBytesRead;
}
}
private bool KeepReading(long elapsed)
{
// If the job is null it's a live stream and will require user action to close, but don't keep it open indefinitely
return !_job?.HasExited ?? elapsed < _timeoutMs;
}
}
}

Some files were not shown because too many files have changed in this diff Show More