Compare commits

..

288 Commits

Author SHA1 Message Date
renovate[bot]
8435dfb779 Update Microsoft to 10.0.8 2026-05-12 22:46:52 +00:00
Bond-009
e9942c3857 Merge pull request #16831 from Bond-009/netVersion
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
Tests / run-tests (macos-latest) (push) Has been cancelled
Tests / run-tests (ubuntu-latest) (push) Has been cancelled
Tests / run-tests (windows-latest) (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Artifact (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Stable Spec (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
Reference correct .NET version in README
2026-05-12 20:01:27 +02:00
Bond_009
04ecf77d97 Reference correct .NET version in README 2026-05-12 18:35:37 +02:00
nyanmisaka
b9ee9b0660 Backport pull request #16819 from jellyfin/release-10.11.z
Fix rate control in av1_amf encoder

Original-merge: a023b9c88d

Merged-by: Bond-009 <bond.009@outlook.com>

Backported-by: Bond_009 <bond.009@outlook.com>
2026-05-12 12:26:13 -04:00
Bond-009
27a3ccb7e4 consolidate OpenAPI categories and deprecate startup routes (#16757) 2026-05-12 18:13:11 +02:00
Tim Eisele
a9865367d8 Safeguard against invalid GUIDs (#16813)
Safeguard against invalid GUIDs
2026-05-12 18:12:54 +02:00
José Fonseca
c169184e01 Translated using Weblate (Portuguese)
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
Tests / run-tests (macos-latest) (push) Has been cancelled
Tests / run-tests (ubuntu-latest) (push) Has been cancelled
Tests / run-tests (windows-latest) (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Artifact (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Stable Spec (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt/
2026-05-11 20:13:48 +00:00
José Fonseca
5bcea608c5 Translated using Weblate (Portuguese (Portugal))
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt_PT/
2026-05-11 20:13:48 +00:00
Milo Ivir
22bf421be6 Translated using Weblate (Croatian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hr/
2026-05-11 20:13:48 +00:00
GolanGitHub
6d3a7b6f69 Translated using Weblate (Spanish)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es/
2026-05-11 20:13:48 +00:00
Bond-009
406aaabefd Use SortName when sorting by name (#16804)
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
Tests / run-tests (macos-latest) (push) Has been cancelled
Tests / run-tests (ubuntu-latest) (push) Has been cancelled
Tests / run-tests (windows-latest) (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Artifact (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Stable Spec (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
Stale Issue Labeler / Check for stale issues (push) Has been cancelled
* Use SortName when sorting by name

* Preserve ordering in item values query
2026-05-11 18:22:26 +02:00
Aindriú Mac Giolla Eoin
ef64f19eac Translated using Weblate (Irish)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ga/
2026-05-11 14:04:44 +00:00
Anrijs Vitolins
2800ae3b8a Translated using Weblate (Latvian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/lv/
2026-05-11 14:04:44 +00:00
Vincenzo Reale
7ead333f6e Translated using Weblate (Italian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/it/
2026-05-11 14:04:44 +00:00
Szilki077
9be1246749 Translated using Weblate (Hungarian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hu/
2026-05-11 14:04:44 +00:00
alxhu
2229851689 Translated using Weblate (German)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/de/
2026-05-11 14:04:43 +00:00
BitToby
21f12a1ad0 Suppress CA2213 false positive on ApplicationHost._pluginManager (#2149) (#16792)
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
Tests / run-tests (macos-latest) (push) Has been cancelled
Tests / run-tests (ubuntu-latest) (push) Has been cancelled
Tests / run-tests (windows-latest) (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Artifact (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Stable Spec (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
2026-05-11 06:29:05 +02:00
renovate[bot]
be0f0b9761 Update danielpalme/ReportGenerator-GitHub-Action action to v5.5.10 (#16820) 2026-05-11 06:28:43 +02:00
Tim Eisele
f24709f11c Print warning on invalid Subnets in Network/Proxy configuration (#16793)
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
Tests / run-tests (macos-latest) (push) Has been cancelled
Tests / run-tests (ubuntu-latest) (push) Has been cancelled
Tests / run-tests (windows-latest) (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Artifact (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Stable Spec (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
Print warning on invalid Subnets in Network/Proxy configuration
2026-05-10 20:34:26 +02:00
Bond-009
4f238ca9b3 Merge pull request #16803 from nyanmisaka/new-profile-condition-video-rotation
Add videoRotation profile condition
2026-05-10 20:32:40 +02:00
Bond-009
42870986a8 Merge pull request #16807 from Shadowghost/fix-artist-duplicates
Fix artist duplicates
2026-05-10 20:28:36 +02:00
Bas
82120732ca Translated using Weblate (Dutch)
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
Tests / run-tests (macos-latest) (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
Tests / run-tests (ubuntu-latest) (push) Has been cancelled
Tests / run-tests (windows-latest) (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Artifact (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Stable Spec (push) Has been cancelled
Stale Issue Labeler / Check for stale issues (push) Has been cancelled
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nl/
2026-05-09 13:53:22 +00:00
lednurb
9a3dfec151 Translated using Weblate (Dutch)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nl/
2026-05-09 13:53:22 +00:00
Shadowghost
02835c6144 Add People Deduplication 2026-05-09 11:18:05 +02:00
nyanmisaka
6d6dee9492 Add tests for videoRotation profile condition
Signed-off-by: nyanmisaka <nst799610810@gmail.com>
2026-05-09 15:20:29 +08:00
nyanmisaka
fc251265d9 Check videoRotation in video stream copy
Signed-off-by: nyanmisaka <nst799610810@gmail.com>
2026-05-09 15:20:29 +08:00
nyanmisaka
5cb6ac521a Add videoRotation profile condition
Signed-off-by: nyanmisaka <nst799610810@gmail.com>
2026-05-09 15:20:29 +08:00
Bond-009
4dd19b990b Merge pull request #16290 from benbenmoss/fix/io-excemption-from-strm
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
Tests / run-tests (macos-latest) (push) Has been cancelled
Tests / run-tests (ubuntu-latest) (push) Has been cancelled
Tests / run-tests (windows-latest) (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Artifact (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Stable Spec (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
IOException with STRM HTTP URLs
2026-05-09 08:19:41 +02:00
Bond-009
efa502a65f Merge pull request #16798 from Shadowghost/fix-segment-provider
Fix Segment Provider Configuration
2026-05-09 08:17:02 +02:00
Shadowghost
b7b405dc83 Fix artist duplicates 2026-05-09 02:07:26 +02:00
Shadowghost
149649a6cf Preserve ordering in item values query 2026-05-09 02:06:01 +02:00
Fjuro
169745fddb Translated using Weblate (Czech)
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
Tests / run-tests (macos-latest) (push) Has been cancelled
Tests / run-tests (ubuntu-latest) (push) Has been cancelled
Tests / run-tests (windows-latest) (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Artifact (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Stable Spec (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
Stale Issue Labeler / Check for stale issues (push) Has been cancelled
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/cs/
2026-05-08 22:52:08 +00:00
Translation expert
c4c521719e Translated using Weblate (Arabic)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ar/
2026-05-08 20:31:00 +00:00
Shadowghost
e9cad048e9 Use SortName when sorting by name 2026-05-08 20:34:36 +02:00
Bond-009
de64a69c7a Merge pull request #14847 from tjwalkr3/warnings-5
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
Tests / run-tests (macos-latest) (push) Has been cancelled
Tests / run-tests (ubuntu-latest) (push) Has been cancelled
Tests / run-tests (windows-latest) (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Artifact (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Stable Spec (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
Fix CA1051 and CA1815 warnings, Change public fields to auto properties
2026-05-08 17:55:25 +02:00
Bond-009
c328dbef10 Update github/codeql-action action to v4.35.4 (#16796)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-08 17:51:47 +02:00
dkanada
8437866ffa consolidate OpenAPI categories and deprecate startup routes 2026-05-08 12:53:26 +09:00
Shadowghost
b84bd34c67 Fix Segment Provider Configuration 2026-05-08 01:05:14 +02:00
Erik W
82b51a60a5 Translated using Weblate (Swedish)
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
Tests / run-tests (macos-latest) (push) Has been cancelled
Tests / run-tests (ubuntu-latest) (push) Has been cancelled
Tests / run-tests (windows-latest) (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Artifact (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Stable Spec (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
Stale Issue Labeler / Check for stale issues (push) Has been cancelled
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sv/
2026-05-07 21:29:41 +00:00
Kityn
5cdfb9bfac Translated using Weblate (Polish)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pl/
2026-05-07 21:29:38 +00:00
Bas
6c59f9a03d Translated using Weblate (Dutch)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nl/
2026-05-07 19:59:14 +00:00
Erik W
e1e18e8da0 Add OriginalLanguage as option to PreferredAudioLanguage (#12579)
* Add OriginalLanguage as option to PreferredAudioLanguage

* Support for multiple original languages

* Add original audio stream indicator

* Fetch OriginalLanguage from TMDB

* Adapt to EFCore refactor

* Fix PlayDefaultAudioTrack OriginalLanguage behavior

* Fix better PlayDefaultAudioTrack OriginalLanguage behavior

* Add comment to ItemFields

* Improved PlayDefaultAudioTrack behavior

* Add migration for original language

* Use sting.Equals for string comparisons

* Always set dto OriginalLanguage

* Remove OriginalLanguage from ItemFields

---------

Co-authored-by: Lampan-git <lampan-git@users.noreply.github.com>
2026-05-07 20:07:23 +02:00
renovate[bot]
63a078b720 Update github/codeql-action action to v4.35.4 2026-05-07 18:05:21 +00:00
theguymadmax
d636b82e83 Allow tmdb as an alias for tmdbid provider id (#16433)
Allow tmdb as an alias for tmdbid provider id
2026-05-07 20:04:28 +02:00
Tim Eisele
bc074b5283 Switch to new version scheme (#16758)
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
Tests / run-tests (macos-latest) (push) Has been cancelled
Tests / run-tests (ubuntu-latest) (push) Has been cancelled
Tests / run-tests (windows-latest) (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Artifact (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Stable Spec (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
Stale Issue Labeler / Check for stale issues (push) Has been cancelled
2026-05-06 17:41:34 -04:00
dkanada
3876a0ad3d fix person type exclusion in api response (#16784) 2026-05-06 17:39:45 -04:00
Marc Brooks
a629080c89 Fix MusicBrainz test. (#16789) 2026-05-06 17:38:56 -04:00
Bond-009
2fbb821581 Merge pull request #16472 from IDisposable/feature/season-provider-id-from-path
Parse provider IDs from season and episode folder/file names
2026-05-06 20:49:28 +02:00
Bond-009
33ed52b8ee Merge branch 'master' into feature/season-provider-id-from-path 2026-05-06 20:49:19 +02:00
Bond-009
d1ab428476 Merge pull request #16321 from WizardOfYendor1/fix/livetv-consumer-leak-negative-position-ticks
Fix live stream consumer leak on negative PositionTicks
2026-05-06 20:46:13 +02:00
Bond-009
142b89eab5 Merge pull request #16319 from JPVenson/feat/MigrationStartupSwitch
Add startup mode to migrate or seed the database on cmd
2026-05-06 20:38:53 +02:00
Bond-009
1bbbc1c823 Merge pull request #16328 from Shadowghost/rating-fix
Fix Canadian rating and fallback to unrated if we have a CountryCode but no matching rating
2026-05-06 20:33:58 +02:00
Niels van Velzen
a8f361f8c0 Merge pull request #16788 from theguymadmax/do-more-like-no2
Remove DigitalOcean from sponsors section
2026-05-06 19:08:43 +02:00
Niels van Velzen
c0593281ff Merge pull request #16466 from PERSONALPANCHIWIRIS/fix/issue#16308_community_rating_not_updating
Fix #16308: Community ratings not updating after changing .nfo file.
2026-05-06 19:08:19 +02:00
Niels van Velzen
d648aba881 Merge pull request #16611 from LmanTW/master
Ignore season directories with no video for TV Shows
2026-05-06 19:08:05 +02:00
Niels van Velzen
44d5954205 Merge pull request #16783 from Shadowghost/fix-people-without-item-id
Fix unique people response for query if no item ID is supplied
2026-05-06 19:07:13 +02:00
Niels van Velzen
10c42d70ca Merge pull request #16780 from Shadowghost/fix-muscibrainz-dispose
Move MusicBrainz Query client to plugin instance
2026-05-06 19:07:03 +02:00
Niels van Velzen
da88a06ede Merge pull request #16779 from Shadowghost/fix-subtitle-save
Fix subtitle save path
2026-05-06 19:07:00 +02:00
Bond-009
842a5efdcf Merge pull request #16782 from JPVenson/backport/15368
Fix UserManager after EFcore refactor (backport #15368)
2026-05-06 18:02:30 +02:00
Bond-009
e84fd95bcc Merge pull request #16246 from lcorbasson/lcorbasson-patch-1
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
Tests / run-tests (macos-latest) (push) Has been cancelled
Tests / run-tests (ubuntu-latest) (push) Has been cancelled
Tests / run-tests (windows-latest) (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Artifact (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Stable Spec (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
Add the filename to exceptions in DeserializeFromFile()
2026-05-06 17:41:20 +02:00
Bond-009
28546f535c Merge pull request #16646 from Biosias/cz-sk-season-parse
Add different Slovak and Czech translation of Season for parsing
2026-05-06 17:40:47 +02:00
Bond-009
e50aeb914a Merge pull request #16322 from poytiis/remove-ToList-calls
Remove unnecessary ToList calls in TrickplayManager
2026-05-06 17:40:12 +02:00
Bond-009
087e745e7a Merge pull request #16778 from Shadowghost/fix-rewatch
Fix rewatch query
2026-05-06 17:37:13 +02:00
theguymadmax
c8c890b9e5 Remove DigitalOcean from sponsors section 2026-05-06 08:21:23 -04:00
Shadowghost
4b8be6bc91 Fix unique people response for query if no item ID is supplied 2026-05-05 20:21:44 +02:00
JPVenson
ec054f6a34 Backport changes from #15368 2026-05-05 17:57:27 +00:00
Shadowghost
d3e6079d38 Move MusicBrainz Query client to plugin instance 2026-05-05 17:43:37 +02:00
Shadowghost
9ade1fb8f6 Fix subtitle save path 2026-05-05 17:32:58 +02:00
Shadowghost
8a43b1c784 Fix rewatch query 2026-05-05 17:31:19 +02:00
Niels van Velzen
4178e0ebaf Merge pull request #16220 from Shadowghost/epg-fixes
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
Tests / run-tests (macos-latest) (push) Has been cancelled
Tests / run-tests (ubuntu-latest) (push) Has been cancelled
Tests / run-tests (windows-latest) (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Artifact (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Stable Spec (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Stale Issue Labeler / Check for stale issues (push) Has been cancelled
Fix EPG issues
2026-05-05 15:53:19 +02:00
Niels van Velzen
064fd8c5c0 Merge pull request #16756 from Shadowghost/artist-speedup
Speed-up LatestItems for Music
2026-05-05 15:49:02 +02:00
Niels van Velzen
7be1350205 Merge pull request #16769 from llaforest/fix/playback-progress-null-overwrite
Guard against null-overwrite of saved audio/subtitle track selections
2026-05-05 15:48:53 +02:00
Niels van Velzen
b227f3e85b Merge pull request #16777 from gabeluci/fix-directoryservice-sort
Fix use of thread-unsafe List<T>.Sort()
2026-05-05 15:48:46 +02:00
Joshua M. Boniface
fd6badf096 Merge pull request #16754 from nielsvanvelzen/keep-legacy-auth
Keep legacy authorization enabled
2026-05-05 09:46:38 -04:00
samurato
7e4727fff8 Translated using Weblate (Nepali)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ne/
2026-05-05 12:31:14 +00:00
Gabriel Luci
6f2e42c20c Fix use of thread-unsafe List<T>.Sort() 2026-05-04 23:46:00 -04:00
Shadowghost
d4f91ab5ca Fixup 2026-05-04 23:48:09 +02:00
Shadowghost
0d58c773f9 Fix review comments 2026-05-04 21:42:31 +02:00
Shadowghost
6be96100c7 Fix review and CodeQL comments 2026-05-04 21:33:10 +02:00
Shadowghost
57c0fcd674 Merge remote-tracking branch 'upstream/master' into epg-fixes 2026-05-04 21:26:26 +02:00
llaforest
6ea2f05497 Guard against null-overwrite of saved audio/subtitle track selections
Some clients omit AudioStreamIndex or SubtitleStreamIndex in playback progress reports and it causes previously saved track selections to be erased.
Add .HasValue checks so only explicit track changes are persisted.
2026-05-04 13:26:08 -04:00
Niels van Velzen
ec04313317 Merge pull request #16478 from Shadowghost/cachdir.tag-support
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
Tests / run-tests (macos-latest) (push) Has been cancelled
Tests / run-tests (ubuntu-latest) (push) Has been cancelled
Tests / run-tests (windows-latest) (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Artifact (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Stable Spec (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
Stale Issue Labeler / Check for stale issues (push) Has been cancelled
Add CACHEDIR.tag support
2026-05-04 19:12:40 +02:00
Niels van Velzen
d87e9f9622 Merge pull request #16695 from ExpctING/fix_dummy_chapter
Fix dummy chapter handling for videos with a single chapter or short duration.
2026-05-04 18:02:16 +02:00
Niels van Velzen
10de1ce8fe Merge pull request #16272 from jellyfin/renovate/morestachio-5.x
Update dependency Morestachio to 5.0.1.670
2026-05-04 18:01:11 +02:00
Niels van Velzen
5d5ae271a5 Merge pull request #16702 from itz4blitz/blitz/issue-13197-nfo-season
Honor episode NFO season during metadata merge
2026-05-04 18:00:50 +02:00
Niels van Velzen
d707a9dba1 Merge pull request #16704 from dyld-w/fix/keyframe-duration-overshoot
Fix keyframe duration overshoot breaking playback
2026-05-04 17:59:59 +02:00
Niels van Velzen
d359d2f7a8 Merge pull request #16166 from Shadowghost/ignore-caching
Implement ignore rule caching
2026-05-04 17:59:48 +02:00
Niels van Velzen
ba268cc3fb Merge pull request #16761 from Shadowghost/fix-recursive-collection-folder
Fix Playlist and Boxset query, save and count performance
2026-05-04 17:58:57 +02:00
Niels van Velzen
dcba6c3659 Merge pull request #16616 from dkanada/fix-person-limit
fix person TotalRecordCount when limit is applied
2026-05-04 17:58:27 +02:00
Niels van Velzen
57821e4cde Merge pull request #16475 from Shadowghost/fix-triple-digit-episode-numbers
Fix triple digit episode number handling
2026-05-04 17:58:13 +02:00
Niels van Velzen
b7d0301099 Merge pull request #16747 from MBR-0001/fix-ffprobe-subs
Fix FFProbeVideoInfo downloading subtitles without considering internal streams
2026-05-04 17:57:49 +02:00
Shadowghost
2365cea626 Only consider Album creation date 2026-05-04 15:13:00 +02:00
Shadowghost
fa65a392b0 Fix Playlist and Boxset query and count perf 2026-05-04 10:25:02 +02:00
dkanada
ec990be12a fix person TotalRecordCount when limit is applied 2026-05-04 12:06:11 +09:00
Shadowghost
0f6bab03eb Fix Sonar comments 2026-05-04 02:16:00 +02:00
Shadowghost
88cad2ad1a Speed-up LatestItems for Music 2026-05-04 02:00:38 +02:00
Shadowghost
d20c775daf Implement ignore rule caching 2026-05-03 23:35:33 +02:00
Niels van Velzen
b8e25b49b3 Keep legacy authorization enabled 2026-05-03 22:20:24 +02:00
Weblate
622947e374 Update translation files
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
Tests / run-tests (macos-latest) (push) Has been cancelled
Tests / run-tests (ubuntu-latest) (push) Has been cancelled
Tests / run-tests (windows-latest) (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Artifact (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Stable Spec (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
Stale Issue Labeler / Check for stale issues (push) Has been cancelled
Updated by "Cleanup translation files" hook in Weblate.

Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/
2026-05-03 19:56:43 +00:00
Niels van Velzen
6e22075a63 Merge pull request #16062 from Shadowghost/perf-rebased
Query Performance Improvements
2026-05-03 21:56:34 +02:00
Shadowghost
d68d0fa962 Merge remote-tracking branch 'upstream/master' into perf-rebased 2026-05-03 13:31:23 +02:00
Shadowghost
00b08c0b32 Omit BoxSet related materialization 2026-05-03 13:26:30 +02:00
Shadowghost
0183127d2a Apply review suggestion 2026-05-03 13:20:12 +02:00
nyanmisaka
d9ced0d639 Use strict QSV CPB size for less powerful H.264 decoder
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
Tests / run-tests (macos-latest) (push) Has been cancelled
Tests / run-tests (ubuntu-latest) (push) Has been cancelled
Tests / run-tests (windows-latest) (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Artifact (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Stable Spec (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
Signed-off-by: nyanmisaka <nst799610810@gmail.com>
2026-05-03 12:25:33 +02:00
Seven Rats
f5f75ed2e1 feat/audiobook_chapters (#16518)
feat/audiobook_chapters
2026-05-03 12:18:20 +02:00
Bond-009
df6f706c2f Merge pull request #16744 from nyanmisaka/skip-ssa-to-ass-conv
Avoid SSA to ASS conversion and loss of styles
2026-05-03 12:17:12 +02:00
Bond-009
3aed429120 Merge pull request #16746 from jellyfin/renovate/ci-deps
Update danielpalme/ReportGenerator-GitHub-Action action to v5.5.9
2026-05-03 12:16:58 +02:00
Bond-009
f9012b6411 Merge pull request #16739 from tcsenpai/fix-symmetric-skip-defaults
fix: use symmetric 15s default for skip forward/backward lengths
2026-05-03 12:07:30 +02:00
Bas
6293e7a3c9 Translated using Weblate (Dutch)
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
Tests / run-tests (macos-latest) (push) Has been cancelled
Tests / run-tests (ubuntu-latest) (push) Has been cancelled
Tests / run-tests (windows-latest) (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Artifact (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Stable Spec (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nl/
2026-05-03 05:52:08 +00:00
Bate Mite
9404fa2b27 Translated using Weblate (Macedonian)
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
Tests / run-tests (macos-latest) (push) Has been cancelled
Tests / run-tests (ubuntu-latest) (push) Has been cancelled
Tests / run-tests (windows-latest) (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Artifact (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Stable Spec (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
Stale Issue Labeler / Check for stale issues (push) Has been cancelled
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/mk/
2026-05-02 20:38:02 +00:00
MBR#0001
19b756a507 Fix FFProbeVideoInfo downloading subtitles without considering internal streams
Currently "Skip if the video already contains embedded subtitles" and "Skip if the default audio track matches the download language" are ignored because the internal tracks are not loaded before downloading

This method is also triggered by the missing subtitles task (whenever a subtitle is downloaded) so if there are multiple languages configured, after first one is downloaded (valid) it runs it for other languages which might be internal
2026-05-02 21:07:13 +02:00
renovate[bot]
4e94c3e28b Update danielpalme/ReportGenerator-GitHub-Action action to v5.5.9 2026-05-02 18:49:55 +00:00
ExpctING
127d924c5b fix
Co-authored-by: Copilot <copilot@github.com>
2026-05-03 02:06:13 +08:00
Bond-009
8150a51238 Merge pull request #16738 from jellyfin/renovate/ci-deps
Update CI dependencies
2026-05-02 17:30:45 +02:00
renovate[bot]
fd80a9d916 Update CI dependencies 2026-05-02 14:52:19 +00:00
nyanmisaka
e75f7f1b28 Avoid SSA to ASS conversion and loss of styles
Signed-off-by: nyanmisaka <nst799610810@gmail.com>
2026-05-02 21:36:34 +08:00
Hassan Alabdulaal
f7bfad8673 Translated using Weblate (Arabic)
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
Tests / run-tests (macos-latest) (push) Has been cancelled
Tests / run-tests (ubuntu-latest) (push) Has been cancelled
Tests / run-tests (windows-latest) (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Artifact (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Stable Spec (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ar/
2026-05-02 11:37:43 +00:00
Hassan Alabdulaal
3690d0bf86 Translated using Weblate (Arabic)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ar/
2026-05-02 10:12:21 +00:00
Hassan Alabdulaal
4e257364b6 Added translation using Weblate (Arabic (Saudi Arabia)) 2026-05-02 08:24:04 +00:00
tcsenpai
58e7ff7f9d Use symmetric 15s default for skip forward/backward lengths
Previously the default skip forward was 30s while skip backward was 10s.
This asymmetry is unexpected for most users and causes a poor UX,
especially on mobile/TV clients that rely on these server-side defaults.

Both values now default to 15000ms (15s), keeping a single consistent
behaviour until the user explicitly customizes them.
2026-05-01 22:09:26 +02:00
Shadowghost
68ab585894 Merge remote-tracking branch 'upstream/master' into epg-fixes 2026-04-29 22:18:47 +02:00
Bond-009
105492ac28 Merge pull request #16690 from jellyfin/renovate/diacritics-4.x
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
Stale Issue Labeler / Check for stale issues (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
Tests / run-tests (macos-latest) (push) Has been cancelled
Tests / run-tests (ubuntu-latest) (push) Has been cancelled
Tests / run-tests (windows-latest) (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Artifact (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Stable Spec (push) Has been cancelled
Update dependency Diacritics to 4.1.8
2026-04-29 21:40:43 +02:00
gnattu
bb12b122c3 Backport pull request #16718 from jellyfin/release-10.11.z
Allow HDR10 for VPP tonemapping

Original-merge: 938c043596

Merged-by: Bond-009 <bond.009@outlook.com>

Backported-by: Bond_009 <bond.009@outlook.com>
2026-04-29 15:39:34 -04:00
Bond-009
6d15f693b5 Merge pull request #16715 from dkanada/obsolete-endpoints
hide HLS controllers and deprecate unused endpoints
2026-04-29 21:36:58 +02:00
Bond-009
e12fbe08a4 Merge pull request #16725 from jellyfin/renovate/microsoft
Update dependency Microsoft.NET.Test.Sdk to 18.5.1
2026-04-29 21:34:56 +02:00
renovate[bot]
e9af1588f2 Update dependency Microsoft.NET.Test.Sdk to 18.5.1 2026-04-28 18:32:08 +00:00
Bond-009
b733857da2 Merge pull request #16672 from dwandw/fix-ipv6-prefixes-not-recognized-as-proxy
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
Tests / run-tests (macos-latest) (push) Has been cancelled
Tests / run-tests (ubuntu-latest) (push) Has been cancelled
Tests / run-tests (windows-latest) (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Artifact (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI Publish / OpenAPI - Publish Stable Spec (push) Has been cancelled
Stale Issue Labeler / Check for stale issues (push) Has been cancelled
Fix IPv6 prefixes not recognized as proxy
2026-04-28 20:21:07 +02:00
Bond-009
44a5c6b3dd Merge pull request #16710 from jellyfin/renovate/ci-deps
Update danielpalme/ReportGenerator-GitHub-Action action to v5.5.7
2026-04-28 20:03:16 +02:00
renovate[bot]
035d8f06cc Update danielpalme/ReportGenerator-GitHub-Action action to v5.5.7 2026-04-27 20:36:46 +00:00
dkanada
c59d3bb21a hide HLS controllers and update obsolete endpoints 2026-04-27 21:45:40 +09:00
dwandw
9962fbbe2e fix: IPv6 prefixes not recognized as proxy https://github.com/jellyfin/jellyfin/issues/15710 2026-04-27 07:13:27 +00:00
Shadowghost
070e2b2d0c Apply review suggestions 2026-04-26 22:30:52 +02:00
Shadowghost
755ef1f942 Fix BoxSet library visibility 2026-04-26 18:53:17 +02:00
Shadowghost
a1f3da1819 Reduce correlated EXISTS queries 2026-04-26 18:53:17 +02:00
Shadowghost
d19449e6a5 Use AsNoTracking() when only reading 2026-04-26 18:53:17 +02:00
Shadowghost
fc866a64e0 Remove unnecessary materializations 2026-04-26 18:53:06 +02:00
Dylan Dellett-Wion
9f5f18d2db Add test for keyframe duration overshoot clamping 2026-04-26 00:24:15 -04:00
Dylan Dellett-Wion
8044156df5 Clamp keyframe duration overshoot instead of throwing in HLS playlist generation 2026-04-26 00:22:34 -04:00
itz4blitz
f9a7cd7457 Honor episode NFO season during metadata merge
Path-derived season numbers could win over explicit provider metadata during episode refresh, causing episodes to appear in the wrong season. Prefer provider-supplied season numbers in the provider merge phases without letting later backfill merges clobber them.
2026-04-25 18:38:07 -04:00
ExpctING
1dd8541ed5 fix ci failed
Co-authored-by: Copilot <copilot@github.com>
2026-04-25 21:10:47 +08:00
ExpctING
d86a4d6815 fix chapter average calculation to avoid division by zero error.
Co-authored-by: Copilot <copilot@github.com>
2026-04-25 16:38:21 +08:00
ExpctING
857e730168 Fix dummy chapter handling for videos with a single chapter. 2026-04-25 03:30:55 +08:00
renovate[bot]
1691940c3f Update dependency Diacritics to 4.1.8 2026-04-23 23:46:18 +00:00
renovate[bot]
44957300ab Update dependency Morestachio to 5.0.1.670 2026-04-21 18:06:10 +00:00
Francisco Ernesto Planas Pestana
c483619928 Fix jellyfin#16308: Community ratings not updating after changing .nfo file.
When "replace all metadata" was issued on a film, with the Web metadata
scrapers and "save to local metadata" disabled, after changing the
.nfo file, 'Community rating' was not updated in the server, remaining
the cached value.

Fixed by adding, in the .nfo parser, an option for
'communityrating' along with value conformity verifiers.

Validation tests were added.
2026-04-21 10:45:23 +01:00
Shadowghost
b717754ed8 Merge remote-tracking branch 'upstream/master' into epg-fixes 2026-04-20 08:16:45 +02:00
Shadowghost
f806ae4018 Fix too many SQL variables error on large libraries 2026-04-19 10:27:47 +02:00
Shadowghost
8a1ad14faf Fix review comment 2026-04-19 10:27:23 +02:00
Shadowghost
e71bb7e904 Merge remote-tracking branch 'upstream/master' into perf-rebased 2026-04-19 10:23:34 +02:00
Biosias
31e8e197cf Add differen Slovak and Czech translation of Season for parsing 2026-04-16 13:14:59 +02:00
Shadowghost
5b4882c102 More generic collection folder filter handling 2026-04-12 18:46:35 +02:00
Shadowghost
60e01e1f22 Apply review suggestions 2026-04-11 18:00:41 +02:00
Shadowghost
e0f50f504a Merge remote-tracking branch 'upstream/master' into epg-fixes 2026-04-11 17:48:00 +02:00
Shadowghost
d8bbb4dfe8 Fix filters 2026-04-11 17:42:27 +02:00
LmanTW
3ef7ada736 Ignore season directories with no video for TV Shows 2026-04-10 18:59:59 +08:00
Shadowghost
24a0df9a39 Merge remote-tracking branch 'upstream/master' into perf-rebased 2026-04-07 21:36:07 +02:00
Tim Eisele
e44821e8f6 Update MediaBrowser.Controller/Entities/BaseItem.cs
Co-authored-by: Bond-009 <bond.009@outlook.com>
2026-04-06 12:39:05 +02:00
Shadowghost
99ad70fbc8 Wrap method parameters 2026-04-01 18:01:25 +02:00
Shadowghost
6fdfc6a61b Fix version filters 2026-03-30 21:04:59 +02:00
Shadowghost
d5f4c624e3 Do not return alternate versions by default 2026-03-30 18:56:31 +02:00
Tim Eisele
a12736a0ce Apply suggestions from code review
Co-authored-by: theguymadmax <theguymadmax@proton.me>
2026-03-29 10:28:15 +02:00
WizardOfYendor1
3a4dff8cc4 Fix live stream consumer leak when PositionTicks is negative
When a stalled HLS client sends ReportPlaybackStopped with a negative
PositionTicks (e.g. HLS.js reporting position on buffer stall), the
ArgumentOutOfRangeException was thrown before GetSession was called,
causing CloseLiveStreamIfNeededAsync to never run. This left the
ILiveStream.ConsumerCount permanently incremented, permanently holding
a tuner open with no active viewer.

Fix by acquiring the session and performing live stream cleanup before
throwing the validation exception, so tuner resources are always freed
even when playback reporting fails with invalid position data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 20:11:26 -04:00
Shadowghost
4fe3abdc0e Delete file-based playlists if empty/removed 2026-03-26 19:37:57 +01:00
Shadowghost
1219c5ec3b Add CACHEDIR.tag support 2026-03-26 13:14:42 +01:00
Shadowghost
2147f57df5 Fix triple digit episode number handling 2026-03-26 11:44:35 +01:00
Tim Eisele
f793acc1aa Update Emby.Server.Implementations/Localization/LocalizationManager.cs
Co-authored-by: theguymadmax <theguymadmax@proton.me>
2026-03-26 10:06:50 +01:00
Marc Brooks
aa96ff42e6 Parse provider IDs from season and episode folder/file names
Season and episode directories/files can now include provider ID
attributes in their names (e.g. "Season 01 [tvdbid=22222]" or
"Show S01E01 [tmdbid=99999].mkv"), consistent with the existing
behavior for series folders.

Supported providers: imdbid, tvdbid, tvmazeid, tmdbid.

Adds TmdbSeasonExternalId and TmdbEpisodeExternalId so that
the TMDB season and episode IDs are surfaced in the metadata editor.

Seasons do not have their own IMDb IDs, so we don't support imdbid parsing
in SeasonResolver. Instead, generate IMDb season URLs via
ImdbExternalUrlProvider using the parent series' IMDb ID and the
season number, matching the IMDb URL format:
imdb.com/title/{seriesId}/episodes/?season={N}

Add tests for the ExternalUrlProviders.
2026-03-25 18:47:40 -05:00
Francisco Ernesto Planas Pestana
e065015d6d Fix #16308: Community ratings not updating after changing .nfo file.
When "replace all metadata" was issued on a film, with the Web metadata
scrapers and "save to local metadata" disabled, after changing the
.nfo file, 'Community rating' was not updated in the server, remaining
the cached value.

Fixed by changing the flag on the 'MergeData' call
(line 809 - MetadataService.cs) to use the option defined by the
user and not the erroneous absolute 'false' value.

Also, in the .nfo parser an option for 'communityrating' was added
along with value conformity verifiers.

Validation tests were added.
2026-03-25 14:07:53 +00:00
Shadowghost
ebc15d3e27 Handle removed alternates 2026-03-14 20:26:40 +01:00
Shadowghost
4b48cad6f7 Don't throw if path is missing 2026-03-14 19:57:18 +01:00
Shadowghost
27d54c5b1c Fix IsResumable and IsPlayed filter 2026-03-13 22:46:42 +01:00
Shadowghost
d218303b93 Merge remote-tracking branch 'upstream/master' into perf-rebased 2026-03-13 09:33:09 +01:00
Shadowghost
bcd5c33ecd Merge remote-tracking branch 'upstream/master' into perf-rebased 2026-03-11 19:36:37 +01:00
Shadowghost
352b6c91f8 Safeguard against empty list 2026-03-11 15:39:04 +01:00
Shadowghost
a8a029de73 Fix DeleteItemsUnsafeFast usage 2026-03-11 09:16:35 +01:00
Shadowghost
ba722b4517 Optimize Search and NextUp queries 2026-03-08 15:26:35 +01:00
Shadowghost
1d8bdcc411 Fix played check in next episode logic 2026-03-07 21:24:10 +01:00
Shadowghost
e6e7f2a692 Fix batch delete of items with same userdata keys 2026-03-07 21:23:35 +01:00
Shadowghost
5882006ee7 Optimize migrations 2026-03-07 21:23:16 +01:00
Shadowghost
ea1c1d0468 Optimize grouping query performance 2026-03-07 20:14:04 +01:00
Shadowghost
077fa89717 Split BaseItemRepository and IItemRepository 2026-03-07 20:12:42 +01:00
Shadowghost
268f23f39a Fix trickplay generation for parts and alt versions 2026-03-06 00:16:04 +01:00
Shadowghost
744c5539d8 Fix review comments 2026-03-05 22:54:26 +01:00
Shadowghost
f5b2e0b8f9 Ensure parts use base type 2026-03-05 21:38:10 +01:00
Shadowghost
59c360aea7 Fix OrderMapper 2026-03-05 19:43:32 +01:00
Shadowghost
3da726463d Fix Liked and Favorite filters 2026-03-03 22:27:33 +01:00
Shadowghost
8c0898738d Fix stacking 2026-03-03 21:45:28 +01:00
Shadowghost
d5fb6f99ef Refresh rating levels 2026-03-02 09:14:34 +01:00
Shadowghost
3d4e4c4572 If we have a country code in the rating, treat as unrated if the country does not have the rating 2026-03-02 09:14:23 +01:00
Shadowghost
11e16df596 Fix Canadian ratings 2026-03-02 09:04:40 +01:00
JPVenson
febfd7f94a Stop server immediately on migration only mode 2026-03-01 15:52:22 +00:00
Teemu Pöytäniemi
8271568677 Add poytiis to CONTRIBUTORS.md 2026-03-01 16:35:24 +02:00
Teemu Pöytäniemi
b22c8882d6 Remove unnecessary ToList calls 2026-03-01 16:28:55 +02:00
JPVenson
63c4fc297a Update naming 2026-03-01 13:12:51 +00:00
JPVenson
e70eaf8bc1 Add startup mode to migrate or seed the database on cmd 2026-03-01 13:10:32 +00:00
Shadowghost
116a036d56 Fix promotion 2026-02-28 17:08:32 +01:00
Shadowghost
8ee4f951fe Defer primary deletion if we replace primary until after item creation 2026-02-28 11:31:52 +01:00
Shadowghost
457c53da6f Decouple demotion detection from deletion 2026-02-27 21:22:47 +01:00
Shadowghost
46ffe0af9c Fix version promotion if multiple versions change 2026-02-27 20:07:09 +01:00
Shadowghost
826e21ecc8 Fix LinkedAlternativeVersion validation 2026-02-27 13:51:51 +01:00
Shadowghost
885b45838c Enable Extras for BD and DVD folders 2026-02-27 13:30:23 +01:00
Shadowghost
bd6bf6ee3c Merge remote-tracking branch 'upstream/master' into perf-rebased 2026-02-27 13:27:57 +01:00
Shadowghost
2d0d497961 Update saved metadata on primary change 2026-02-25 21:03:46 +01:00
Shadowghost
4bd9dbe910 Fix LinkedChildren rerouting on primary version demotion 2026-02-25 20:53:29 +01:00
Shadowghost
b7da5c1860 Apply review suggestions 2026-02-25 14:51:53 +01:00
Shadowghost
9aa69eded9 Fix collection handling of multiple version movie 2026-02-25 08:51:26 +01:00
Shadowghost
b5f5b02787 Fix special features filter 2026-02-24 21:35:50 +01:00
ghis@am.com
41d2070008 adding strm & http ignore to extract timestamp 2026-02-24 10:58:47 +11:00
Shadowghost
61ff36d761 Optimize SeriesDatePlayed ordering 2026-02-23 23:44:15 +01:00
Shadowghost
100d6bb38c Gracefully handle empty listingId 2026-02-23 21:17:52 +01:00
Shadowghost
66c11231b2 Fix promotion logic 2026-02-23 20:25:55 +01:00
Shadowghost
34c1e45bc2 Fixup 2026-02-23 17:01:38 +01:00
Shadowghost
ea0641b659 Fix version promotion 2026-02-23 09:05:43 +01:00
Shadowghost
d63b2b2657 Apply review suggestion 2026-02-22 12:37:14 +01:00
Shadowghost
6ce5f9dfd5 Cleanup folder duplicates of series 2026-02-22 12:34:58 +01:00
Shadowghost
ed43ad0968 Persistence 2026-02-22 11:32:55 +01:00
Shadowghost
27396bffc6 Handle 5002, 5003 and add caches 2026-02-22 11:14:15 +01:00
Shadowghost
d156e04c9a Fix Skipping 2026-02-21 22:56:53 +01:00
Shadowghost
5541653f73 Return Name instead of CleanName for legacy filters 2026-02-21 22:51:46 +01:00
Shadowghost
ae5420d4ae Handle linkedChildren in GetPlayedAndTotalCountBatch and optimize filter 2026-02-21 22:38:07 +01:00
Shadowghost
0f1a6fe4c2 Fix IsPlayed filter and setting BoxSet played/unplayed 2026-02-21 19:58:00 +01:00
Shadowghost
97340edf02 Fix image failure response handling in batch endpoint 2026-02-21 16:47:08 +01:00
Shadowghost
c4c3e9ea4d Fix batch requests 2026-02-21 11:40:18 +01:00
Shadowghost
561e78efb4 Apply review suggestions 2026-02-20 21:21:25 +01:00
Shadowghost
ff0a64ecb9 Merge remote-tracking branch 'upstream/master' into perf-rebased 2026-02-20 21:17:55 +01:00
Shadowghost
679664ca28 Add early returns 2026-02-20 15:14:03 +01:00
Shadowghost
b0eec00e1c Properly handle SD internal error codes 2026-02-20 14:58:12 +01:00
Shadowghost
e49d71707c Fix EPG issues 2026-02-20 14:35:56 +01:00
Shadowghost
3a090a5716 Optimize item count calculation for named items 2026-02-18 20:28:42 +01:00
Shadowghost
f96c399e62 Fix cleanup for BDMV 2026-02-17 22:59:03 +01:00
Shadowghost
0f75518287 Enforce permissions on BoxSets 2026-02-16 18:50:11 +01:00
Loïc CORBASSON
893188ab28 Add the filename to the exception's trace to facilitate error resolution (see #12254, #9508, ...) 2026-02-16 17:23:52 +01:00
Shadowghost
de32e2eb6f Fix library performance 2026-02-16 10:50:18 +01:00
Shadowghost
84962cbc94 Cleanup orphan items 2026-02-16 10:06:16 +01:00
Shadowghost
ba356638e8 Fix Extra refresh 2026-02-15 23:13:01 +01:00
Shadowghost
3439d3c017 Migrate PrimaryVersionId to GUID and fix assignment 2026-02-15 23:12:52 +01:00
Shadowghost
837c7d4ed3 Try fixing orphan versions again 2026-02-15 19:49:30 +01:00
Shadowghost
4ce03ffa21 Fix orphaned alt version cleanup 2026-02-15 17:34:06 +01:00
Shadowghost
50cabcd99d Remove split queries 2026-02-15 16:54:54 +01:00
Shadowghost
9730aaac57 Fix ordering of cleanup in migration 2026-02-15 10:32:53 +01:00
Shadowghost
340bcafd3d Merge remote-tracking branch 'upstream/master' into perf-rebased 2026-02-15 10:14:41 +01:00
Shadowghost
f5c9a4a476 Add cleanup for orphaned versions 2026-02-15 10:14:30 +01:00
Shadowghost
edec464306 Handle episode versions in LinkedChild migration 2026-02-09 18:25:07 +01:00
Shadowghost
5dcec831f3 Fix naming filter when collapsing into boxsets 2026-02-09 09:13:31 +01:00
Shadowghost
737abe6f3a Fix version order in migration 2026-02-09 09:13:05 +01:00
Shadowghost
edc6caf255 Cleanup and fix version ordering 2026-02-09 00:45:48 +01:00
Shadowghost
0a99a78ddc Fix random sorting 2026-02-08 21:52:08 +01:00
Shadowghost
71594b4a9a Fix multiple version resolution 2026-02-08 17:22:52 +01:00
Shadowghost
bb6c3b4eec Fix BoxSet collapse handling and deletion 2026-02-07 21:17:01 +01:00
Shadowghost
2420ece5fe Fix version resolution and scan handling 2026-02-07 19:01:37 +01:00
Shadowghost
00dd84035e Merge remote-tracking branch 'upstream/master' into perf-rebased 2026-02-07 14:06:53 +01:00
Shadowghost
f5d966fcc3 Remove Collection and Playlist cleanup task 2026-02-07 09:37:42 +01:00
Shadowghost
98d7c8d59f Make sure we deduplicate LinkedChildren 2026-02-07 09:37:42 +01:00
Shadowghost
268d88a5fb Optimize Collection Grouping, NextUp and Latest queries 2026-02-07 01:38:06 +01:00
Shadowghost
8ddc35a1ce Optimize Indices 2026-02-07 00:57:15 +01:00
Shadowghost
46ad25f47d Fix NextUp 2026-02-05 19:57:39 +01:00
Shadowghost
0c46004cd9 Optimize accessibility checks 2026-02-05 19:57:39 +01:00
Shadowghost
a0346fe5b7 Fix multiple version handling 2026-02-05 01:41:07 +01:00
Shadowghost
aedd2b04a2 Fix alternative Versions and prevent over fetching 2026-02-04 19:19:31 +01:00
Shadowghost
98b561d62c Revert RefreshMetadata changes 2026-02-01 16:54:59 +01:00
Shadowghost
d6a8fa1485 Fix part removal and double series refresh 2026-02-01 14:16:51 +01:00
Shadowghost
042385599f Fix orderBy warnings 2026-02-01 12:22:27 +01:00
Shadowghost
09a729effe Fix tag checks 2026-01-31 23:45:38 +01:00
Shadowghost
2789532aa8 Optimize Validator and Filter Performance 2026-01-31 19:22:04 +01:00
Shadowghost
694db80d4c Reroute on version removal 2026-01-31 19:21:36 +01:00
Shadowghost
a650148dfd Merge remote-tracking branch 'upstream/master' into perf-rebased 2026-01-30 21:12:06 +01:00
Shadowghost
17e8759a52 Apply review suggestions 2026-01-26 16:52:26 +01:00
Shadowghost
327ace1d30 Fix GetMediaSourceName_Valid test 2026-01-19 12:54:35 +01:00
Shadowghost
95a301dc43 Optimization and search test fixes 2026-01-19 12:39:17 +01:00
Shadowghost
342846e4fc Add ImageInfo index 2026-01-18 19:48:46 +01:00
Shadowghost
99440f8432 Fix EFcore readme 2026-01-18 19:48:46 +01:00
Shadowghost
2086ac7dd2 Don't use raw SQL 2026-01-18 19:48:46 +01:00
Shadowghost
4a1012fd22 FIx folder refreshes resetting LinkedChildren 2026-01-18 19:48:46 +01:00
Shadowghost
89427af41c Fixes after rebase 2026-01-18 19:48:46 +01:00
Shadowghost
5996c4afce Complete LinkedChildren integration and batch DTO optimizations
This commit integrates remaining performance changes:

- Add batch user data fetching in DtoService to reduce N+1 queries
- Add GetNextUpEpisodesBatch in TVSeriesManager for efficient batch retrieval
- Update Video/Movie/BoxSet to use LibraryManager for alternate versions
- Transition LinkedChild to use ItemId instead of Path (obsolete Path/LibraryItemId)
- Update providers and controllers for LinkedChildren-based references
- Add NextUpEpisodeBatchResult for batched episode queries
- Integrate IDescendantQueryProvider in SqliteDatabaseProvider
2026-01-18 19:48:46 +01:00
Shadowghost
dfa78590c2 Add OwnerId fix migration and library options event
- Add FixIncorrectOwnerIdRelationships migration routine
- Add LibraryOptionsUpdatedEventArgs for library options changes
2026-01-18 19:47:02 +01:00
Shadowghost
912a963a2b Add folder-aware filter extensions and descendant query provider
- Add FolderAwareFilterExtensions for LinkedChildren-based filtering
- Add IDescendantQueryProvider interface for database-specific queries
- Add MatchCriteria classes for folder filtering
- Add SqliteDescendantQueryProvider implementation
2026-01-18 19:47:02 +01:00
Shadowghost
f260585917 Add LinkedChildren data migration routine
Migrates existing LinkedChildren data from JSON-serialized Data column
to the new relational LinkedChildren table for boxsets, playlists,
and video alternate versions.
2026-01-18 19:47:02 +01:00
Shadowghost
22d8a00716 Add optimized indexes for UserData and latest items queries 2026-01-18 19:47:02 +01:00
Shadowghost
c350fd0f40 Remove ExtraIds column and use OwnerId relationship for extras
- Remove ExtraIds property from BaseItemEntity and BaseItem
- Update RefreshExtras to query via OwnerId instead of cached ExtraIds
- Update GetExtras methods to query database via OwnerIds filter
- Add OwnerIds and ExtraTypes filter support to InternalItemsQuery
- Add filter handling in BaseItemRepository for new query options
- Update HasSpecialFeature/HasTrailer filters to use Extras relationship
- Add CleanupOrphanedExtras migration routine
- Add database migration to drop ExtraIds column
2026-01-18 19:47:02 +01:00
Shadowghost
139d23ddc2 Normalize OwnerId to GUID and add performance indexes
- Change OwnerId from string to Guid for proper foreign key relationships
- Add Owner/Extras navigation properties for extras relationship
- Add indexes on OwnerId and ExtraType columns for efficient queries
- Add optimized composite indexes for latest items queries sorted by DateCreated
- Update BaseItemRepository and migration to handle new Guid type
2026-01-18 19:46:37 +01:00
Shadowghost
cc2ccd1bf3 Add LinkedChildren database table for normalized relationships
Introduces a new database table to store linked child relationships for
boxsets, playlists, and video alternate versions. This replaces the
JSON-serialized Data column approach with a proper relational structure.

- Add LinkedChildEntity and LinkedChildType enum
- Add entity configuration with proper foreign keys
- Add EF Core migration for SQLite
2026-01-18 19:46:37 +01:00
Shadowghost
1491494bcb Add early tag check exit and enhance search ordering
- BaseItem: Skip GetInheritedTags() call for users without tag
  restrictions, improving visibility check performance
- BaseItem: Only fetch parents once in visibility chec
- OrderMapper: Include OriginalTitle in search relevance scoring
  for better matching of foreign content
2026-01-18 19:46:37 +01:00
Shadowghost
0b77f97048 Optimize latest items grouping in UserViewManager
- Use dictionary lookup for O(1) container grouping instead of O(n) FirstOrDefault searches
- Add optimized path for movies in GetLatestItemList
- Reduce query limit multiplier from 5x to 2x
- Update to collection expression syntax
2026-01-18 19:45:39 +01:00
Shadowghost
d3d4d37e82 Simplify UserDataManager and remove unused private methods
Removes unused private GetUserData and GetUserDataInternal methods.
Moves GetUserDataBatch to be an abstract interface method rather than
having a default implementation for clarity.
2026-01-18 19:45:39 +01:00
Shadowghost
1c1447362e Skip metadata refresh for already-indexed items during validation
Optimizes the validation process by checking if items already exist in
the database before attempting to refresh metadata.
2026-01-18 19:45:39 +01:00
Thomas Jones
a014cb538e Fixed 3 different CA1051 warnings. Changed them to auto properties and rearranged the members to fit styling rules.
Co-authored-by: Derpipose <90276123+Derpipose@users.noreply.github.com>
2025-09-23 14:18:15 -06:00
Thomas Jones
378ba937b6 Added in pragma warning disable for CA1815 warning.
Co-authored-by: Derpipose <90276123+Derpipose@users.noreply.github.com>
2025-09-23 13:17:52 -06:00
357 changed files with 37224 additions and 5412 deletions

View File

@@ -32,13 +32,13 @@ jobs:
dotnet-version: '10.0.x'
- name: Initialize CodeQL
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
uses: github/codeql-action/init@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
with:
languages: ${{ matrix.language }}
queries: +security-extended
- name: Autobuild
uses: github/codeql-action/autobuild@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
uses: github/codeql-action/autobuild@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
uses: github/codeql-action/analyze@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4

View File

@@ -35,7 +35,7 @@ jobs:
--verbosity minimal
- name: Merge code coverage results
uses: danielpalme/ReportGenerator-GitHub-Action@ee3806a36b8b2eb9594cb3e5fae045af7e5ead10 # v5.5.6
uses: danielpalme/ReportGenerator-GitHub-Action@049f7ec958c672fd31d5cc1cb01622dc8d2e23ab # v5.5.10
with:
reports: "**/coverage.cobertura.xml"
targetdir: "merged/"

View File

@@ -118,6 +118,7 @@
- [Phlogi](https://github.com/Phlogi)
- [pjeanjean](https://github.com/pjeanjean)
- [ploughpuff](https://github.com/ploughpuff)
- [poytiis](https://github.com/poytiis)
- [pR0Ps](https://github.com/pR0Ps)
- [PrplHaz4](https://github.com/PrplHaz4)
- [RazeLighter777](https://github.com/RazeLighter777)
@@ -228,6 +229,8 @@
- [MarcoCoreDuo](https://github.com/MarcoCoreDuo)
- [LiHRaM](https://github.com/LiHRaM)
- [MSalman5230](https://github.com/MSalman5230)
- [dwandw](https://github.com/dwandw)
- [Lampan-git](https://github.com/Lampan-git)
# Emby Contributors

View File

@@ -14,11 +14,11 @@
<PackageVersion Include="BlurHashSharp" Version="1.4.0-pre.1" />
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="coverlet.collector" Version="10.0.0" />
<PackageVersion Include="Diacritics" Version="4.1.4" />
<PackageVersion Include="Diacritics" Version="4.1.8" />
<PackageVersion Include="DiscUtils.Udf" Version="0.16.13" />
<PackageVersion Include="DotNet.Glob" Version="3.1.3" />
<PackageVersion Include="FsCheck.Xunit.v3" Version="3.3.3" />
<PackageVersion Include="HarfBuzzSharp.NativeAssets.Linux" Version="8.3.1.3" />
<PackageVersion Include="HarfBuzzSharp.NativeAssets.Linux" Version="8.3.1.1" />
<PackageVersion Include="ICU4N.Transliterator" Version="60.1.0-alpha.356" />
<PackageVersion Include="IDisposableAnalyzers" Version="4.0.8" />
<PackageVersion Include="Ignore" Version="0.2.1" />
@@ -26,30 +26,30 @@
<PackageVersion Include="libse" Version="4.0.12" />
<PackageVersion Include="LrcParser" Version="2025.623.0" />
<PackageVersion Include="MetaBrainz.MusicBrainz" Version="8.0.1" />
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="10.0.7" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.7" />
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="10.0.8" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.8" />
<PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="4.14.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="5.3.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="5.3.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="5.3.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="10.0.7" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.7" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.7" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.7" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.7" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="10.0.7" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.5.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="10.0.8" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.8" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.8" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.8" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="10.0.8" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
<PackageVersion Include="MimeTypes" Version="2.5.2" />
<PackageVersion Include="Morestachio" Version="5.0.1.631" />
<PackageVersion Include="Morestachio" Version="5.0.1.670" />
<PackageVersion Include="Moq" Version="4.18.4" />
<PackageVersion Include="NEbml" Version="1.1.0.5" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
@@ -69,15 +69,15 @@
<PackageVersion Include="SerilogAnalyzer" Version="0.15.0" />
<PackageVersion Include="SharpFuzz" Version="2.2.0" />
<!-- Pinned to 3.116.1 because https://github.com/jellyfin/jellyfin/pull/14255 -->
<PackageVersion Include="SkiaSharp" Version="[3.119.2]" />
<PackageVersion Include="SkiaSharp.HarfBuzz" Version="[3.119.2]" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="[3.119.2]" />
<PackageVersion Include="SkiaSharp" Version="[3.116.1]" />
<PackageVersion Include="SkiaSharp.HarfBuzz" Version="[3.116.1]" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="[3.116.1]" />
<PackageVersion Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
<PackageVersion Include="Svg.Skia" Version="3.4.1" />
<PackageVersion Include="Swashbuckle.AspNetCore.ReDoc" Version="10.1.7" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="10.1.7" />
<PackageVersion Include="System.Text.Json" Version="10.0.7" />
<PackageVersion Include="System.Text.Json" Version="10.0.8" />
<PackageVersion Include="TagLibSharp" Version="2.3.0" />
<PackageVersion Include="z440.atl.core" Version="7.13.0" />
<PackageVersion Include="TMDbLib" Version="3.0.0" />

View File

@@ -1,3 +1,5 @@
#pragma warning disable CA1815
namespace Emby.Naming.AudioBook
{
/// <summary>

View File

@@ -379,6 +379,14 @@ namespace Emby.Naming.Common
IsNamed = true
},
// "Name - 101.mkv", "Name - 101 [720p].mkv", "Name - 101 (2020).mkv"
// Handles absolute episode numbers with hyphen delimiter (common in anime)
// Without brackets (bracketed version handled above)
new EpisodeExpression(@".*[\\\/](?<seriesname>[^\\\/]+?)[\s_]+-[\s_]+(?<epnumber>[0-9]+)[\s_]*(?:\[.*?\]|\(.*?\))*[\s_]*(?:\.\w+)?$")
{
IsNamed = true
},
// /server/anything_102.mp4
// /server/james.corden.2017.04.20.anne.hathaway.720p.hdtv.x264-crooks.mkv
// /server/anything_1996.11.14.mp4

View File

@@ -36,7 +36,7 @@
<PropertyGroup>
<Authors>Jellyfin Contributors</Authors>
<PackageId>Jellyfin.Naming</PackageId>
<VersionPrefix>10.12.0</VersionPrefix>
<VersionPrefix>12.0.0</VersionPrefix>
<RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
</PropertyGroup>

View File

@@ -12,10 +12,10 @@ namespace Emby.Naming.TV
{
private static readonly Regex CleanNameRegex = new(@"[ ._\-\[\]]", RegexOptions.Compiled);
[GeneratedRegex(@"^\s*((?<seasonnumber>(?>\d+))(?:st|nd|rd|th|\.)*(?!\s*[Ee]\d+))\s*(?:[[]*|[]*|[sS](?:eason|æson|aison|taffel|eries|tagione|äsong|eizoen|easong|ezon|ezona|ezóna|ezonul)*|[tT](?:emporada)*|[kK](?:ausi)*|[Сс](?:езон)*)\s*(?<rightpart>.*)$", RegexOptions.IgnoreCase)]
[GeneratedRegex(@"^\s*((?<seasonnumber>(?>\d+))(?:st|nd|rd|th|\.)*(?!\s*[Ee]\d+))\s*(?:[[]*|[]*|[sS](?:eason|æson|aison|taffel|eries|tagione|äsong|eizoen|easong|ezon|ezona|ezóna|ezonul|érie|éria|erie|eria)*|[tT](?:emporada)*|[kK](?:ausi)*|[Сс](?:езон)*)\s*(?<rightpart>.*)$", RegexOptions.IgnoreCase)]
private static partial Regex ProcessPre();
[GeneratedRegex(@"^\s*(?:[[]*|[]*|[sS](?:eason|æson|aison|taffel|eries|tagione|äsong|eizoen|easong|ezon|ezona|ezóna|ezonul)*|[tT](?:emporada)*|[kK](?:ausi)*|[Сс](?:езон)*)\s*(?<seasonnumber>\d+?)(?=\d{3,4}p|[^\d]|$)(?!\s*[Ee]\d)(?<rightpart>.*)$", RegexOptions.IgnoreCase)]
[GeneratedRegex(@"^\s*(?:[[]*|[]*|[sS](?:eason|æson|aison|taffel|eries|tagione|äsong|eizoen|easong|ezon|ezona|ezóna|ezonul|érie|éria|erie|eria)*|[tT](?:emporada)*|[kK](?:ausi)*|[Сс](?:езон)*)\s*(?<seasonnumber>\d+?)(?=\d{3,4}p|[^\d]|$)(?!\s*[Ee]\d)(?<rightpart>.*)$", RegexOptions.IgnoreCase)]
private static partial Regex ProcessPost();
[GeneratedRegex(@"[sS](\d{1,4})(?!\d|[eE]\d)(?=\.|_|-|\[|\]|\s|$)", RegexOptions.None)]

View File

@@ -1,3 +1,5 @@
#pragma warning disable CA1815
namespace Emby.Naming.Video
{
/// <summary>

View File

@@ -90,6 +90,7 @@ namespace Emby.Server.Implementations.AppBase
CreateAndCheckMarker(ProgramDataPath, "data");
CreateAndCheckMarker(CachePath, "cache");
CreateAndCheckMarker(DataPath, "data");
CreateCacheDirTag(CachePath);
}
/// <inheritdoc />
@@ -100,6 +101,26 @@ namespace Emby.Server.Implementations.AppBase
CheckOrCreateMarker(path, $".jellyfin-{markerName}", recursive);
}
/// <summary>
/// Creates a CACHEDIR.TAG file in the specified directory per the Cache Directory Tagging specification.
/// This signals to backup tools (e.g. Restic, Borg) that the directory contains cached data
/// and can be excluded from backups.
/// </summary>
/// <param name="path">The cache directory path.</param>
internal static void CreateCacheDirTag(string path)
{
var tagPath = Path.Combine(path, "CACHEDIR.TAG");
if (!File.Exists(tagPath))
{
File.WriteAllText(
tagPath,
"Signature: 8a477f597d28d172789f06886806bc55\n"
+ "# This file is a cache directory tag created by Jellyfin.\n"
+ "# For information about cache directory tags, see:\n"
+ "#\thttps://bford.info/cachedir/\n");
}
}
private IEnumerable<string> GetMarkers(string path, bool recursive = false)
{
return Directory.EnumerateFiles(path, ".jellyfin-*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);

View File

@@ -228,6 +228,7 @@ namespace Emby.Server.Implementations.AppBase
Logger.LogInformation("Setting cache path: {Path}", cachePath);
((BaseApplicationPaths)CommonApplicationPaths).CachePath = cachePath;
CommonApplicationPaths.CreateAndCheckMarker(((BaseApplicationPaths)CommonApplicationPaths).CachePath, "cache");
BaseApplicationPaths.CreateCacheDirTag(cachePath);
}
/// <summary>

View File

@@ -166,8 +166,6 @@ namespace Emby.Server.Implementations
ConfigurationManager.Configuration,
ApplicationPaths.PluginsPath,
ApplicationVersion);
_disposableParts.Add(_pluginManager);
}
/// <summary>
@@ -507,7 +505,13 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IUserDataManager, UserDataManager>();
serviceCollection.AddSingleton<IItemRepository, BaseItemRepository>();
serviceCollection.AddSingleton<BaseItemRepository>();
serviceCollection.AddSingleton<IItemRepository>(sp => sp.GetRequiredService<BaseItemRepository>());
serviceCollection.AddSingleton<IItemQueryHelpers>(sp => sp.GetRequiredService<BaseItemRepository>());
serviceCollection.AddSingleton<IItemPersistenceService, ItemPersistenceService>();
serviceCollection.AddSingleton<INextUpService, NextUpService>();
serviceCollection.AddSingleton<IItemCountService, ItemCountService>();
serviceCollection.AddSingleton<ILinkedChildrenService, LinkedChildrenService>();
serviceCollection.AddSingleton<IPeopleRepository, PeopleRepository>();
serviceCollection.AddSingleton<IChapterRepository, ChapterRepository>();
serviceCollection.AddSingleton<IMediaAttachmentRepository, MediaAttachmentRepository>();
@@ -530,6 +534,7 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IMusicManager, MusicManager>();
serviceCollection.AddSingleton<ILibraryMonitor, LibraryMonitor>();
serviceCollection.AddSingleton<DotIgnoreIgnoreRule>();
serviceCollection.AddSingleton<ISearchEngine, SearchEngine>();
@@ -641,6 +646,7 @@ namespace Emby.Server.Implementations
BaseItem.ConfigurationManager = ConfigurationManager;
BaseItem.FileSystem = Resolve<IFileSystem>();
BaseItem.ItemRepository = Resolve<IItemRepository>();
BaseItem.ItemCountService = Resolve<IItemCountService>();
BaseItem.LibraryManager = Resolve<ILibraryManager>();
BaseItem.LocalizationManager = Resolve<ILocalizationManager>();
BaseItem.Logger = Resolve<ILogger<BaseItem>>();
@@ -1006,6 +1012,8 @@ namespace Emby.Server.Implementations
}
_disposableParts.Clear();
_pluginManager?.Dispose();
}
_disposed = true;

View File

@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Jellyfin.Extensions;
using MediaBrowser.Controller.Chapters;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
@@ -128,7 +129,7 @@ public class ChapterManager : IChapterManager
var averageChapterDuration = GetAverageDurationBetweenChapters(chapters);
var threshold = TimeSpan.FromSeconds(1).Ticks;
if (averageChapterDuration < threshold)
if (chapters.Count >= 2 && averageChapterDuration < threshold)
{
_logger.LogInformation("Skipping chapter image extraction for {Video} as the average chapter duration {AverageDuration} was lower than the minimum threshold {Threshold}", video.Name, averageChapterDuration, threshold);
extractImages = false;
@@ -232,12 +233,22 @@ public class ChapterManager : IChapterManager
}
/// <inheritdoc />
public void SaveChapters(Video video, IReadOnlyList<ChapterInfo> chapters)
public bool Supports(BaseItem item)
=> item is Video or Audio;
/// <inheritdoc />
public void SaveChapters(BaseItem item, IReadOnlyList<ChapterInfo> chapters)
{
// Remove any chapters that are outside of the runtime of the video
var validChapters = chapters.Where(c => c.StartPositionTicks < video.RunTimeTicks).ToList();
_chapterRepository.SaveChapters(video.Id, validChapters);
}
if (!Supports(item))
{
_logger.LogWarning("Attempted to save chapters for unsupported item type {Type}: {Name} ({Id})", item.GetType().Name, item.Name, item.Id);
return;
}
// Remove any chapters that are outside of the runtime of the item
var validChapters = chapters.Where(c => c.StartPositionTicks < item.RunTimeTicks).ToList();
_chapterRepository.SaveChapters(item.Id, validChapters);
}
/// <inheritdoc />
public ChapterInfo? GetChapter(Guid baseItemId, int index)

View File

@@ -272,7 +272,7 @@ namespace Emby.Server.Implementations.Collections
{
var childItem = _libraryManager.GetItemById(guidId);
var child = collection.LinkedChildren.FirstOrDefault(i => (i.ItemId.HasValue && i.ItemId.Value.Equals(guidId)) || (childItem is not null && string.Equals(childItem.Path, i.Path, StringComparison.OrdinalIgnoreCase)));
var child = collection.LinkedChildren.FirstOrDefault(i => i.ItemId.HasValue && i.ItemId.Value.Equals(guidId));
if (child is null)
{
@@ -342,7 +342,7 @@ namespace Emby.Server.Implementations.Collections
// this is kind of a performance hack because only Video has alternate versions that should be in a box set?
if (item is Video video)
{
foreach (var childId in video.GetLocalAlternateVersionIds())
foreach (var childId in _libraryManager.GetLocalAlternateVersionIds(video))
{
if (!results.ContainsKey(childId))
{

View File

@@ -5,10 +5,12 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using Jellyfin.Database.Implementations;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Playlists;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
@@ -35,7 +37,11 @@ public class CleanDatabaseScheduledTask : ILibraryPostScanTask
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
await CleanDeadItems(cancellationToken, progress).ConfigureAwait(false);
var deadItemsProgress = new Progress<double>(val => progress.Report(val * 0.8));
await CleanDeadItems(cancellationToken, deadItemsProgress).ConfigureAwait(false);
var playlistProgress = new Progress<double>(val => progress.Report(80 + (val * 0.2)));
await CleanOrphanedFilePlaylistsAsync(cancellationToken, playlistProgress).ConfigureAwait(false);
}
private async Task CleanDeadItems(CancellationToken cancellationToken, IProgress<double> progress)
@@ -116,4 +122,32 @@ public class CleanDatabaseScheduledTask : ILibraryPostScanTask
progress.Report(100);
}
private async Task CleanOrphanedFilePlaylistsAsync(CancellationToken cancellationToken, IProgress<double> progress)
{
var playlists = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = [BaseItemKind.Playlist],
Recursive = true
}).OfType<Playlist>().ToList();
var numComplete = 0;
var numItems = Math.Max(playlists.Count, 1);
foreach (var playlist in playlists)
{
cancellationToken.ThrowIfCancellationRequested();
if (playlist.IsFile && !File.Exists(playlist.Path))
{
_logger.LogInformation("Removing file-based playlist {Name} because source file {Path} no longer exists", playlist.Name, playlist.Path);
_libraryManager.DeleteItem(playlist, new DeleteOptions { DeleteFileLocation = false });
}
numComplete++;
progress.Report((double)numComplete / numItems * 100);
}
progress.Report(100);
}
}

View File

@@ -153,17 +153,102 @@ namespace Emby.Server.Implementations.Dto
private ILiveTvManager LivetvManager => _livetvManagerFactory.Value;
/// <inheritdoc />
public IReadOnlyList<BaseItemDto> GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, User? user = null, BaseItem? owner = null)
public IReadOnlyList<BaseItemDto> GetBaseItemDtos(
IReadOnlyList<BaseItem> items,
DtoOptions options,
User? user = null,
BaseItem? owner = null,
bool skipVisibilityCheck = false)
{
var accessibleItems = user is null ? items : items.Where(x => x.IsVisible(user)).ToList();
var accessibleItems = skipVisibilityCheck || user is null ? items : items.Where(x => x.IsVisible(user)).ToList();
var returnItems = new BaseItemDto[accessibleItems.Count];
List<(BaseItem, BaseItemDto)>? programTuples = null;
List<(BaseItemDto, LiveTvChannel)>? channelTuples = null;
// Batch-fetch user data for all items
Dictionary<Guid, UserItemData>? userDataBatch = null;
if (user is not null && options.EnableUserData)
{
userDataBatch = _userDataRepository.GetUserDataBatch(accessibleItems, user);
}
// Pre-compute collection folders once to avoid N+1 queries in CanDelete
List<Folder>? allCollectionFolders = null;
if (user is not null && options.ContainsField(ItemFields.CanDelete))
{
allCollectionFolders = _libraryManager.GetUserRootFolder().Children.OfType<Folder>().ToList();
}
// Batch-fetch child counts for all folders to avoid N+1 queries
Dictionary<Guid, int>? childCountBatch = null;
if (options.ContainsField(ItemFields.ChildCount))
{
var folderIds = accessibleItems.OfType<Folder>().Select(f => f.Id).ToList();
if (folderIds.Count > 0)
{
childCountBatch = _libraryManager.GetChildCountBatch(folderIds, user?.Id);
}
}
// Batch-fetch played/total counts for all folders to avoid N+1 queries
Dictionary<Guid, (int Played, int Total)>? playedCountBatch = null;
if (user is not null && options.EnableUserData)
{
var folderIds = accessibleItems.OfType<Folder>()
.Where(f => f.SupportsUserDataFromChildren && (f.SupportsPlayedStatus || options.ContainsField(ItemFields.RecursiveItemCount)))
.Select(f => f.Id).ToList();
if (folderIds.Count > 0)
{
playedCountBatch = _libraryManager.GetPlayedAndTotalCountBatch(folderIds, user);
}
}
// Batch-fetch MusicArtist lookups across all items to avoid N+1 queries.
IReadOnlyDictionary<string, MusicArtist[]>? artistsBatch = null;
var artistNames = new HashSet<string>(StringComparer.Ordinal);
foreach (var item in accessibleItems)
{
if (item is IHasArtist hasArtist)
{
foreach (var name in hasArtist.Artists)
{
if (!string.IsNullOrWhiteSpace(name))
{
artistNames.Add(name);
}
}
}
if (item is IHasAlbumArtist hasAlbumArtist)
{
foreach (var name in hasAlbumArtist.AlbumArtists)
{
if (!string.IsNullOrWhiteSpace(name))
{
artistNames.Add(name);
}
}
}
}
if (artistNames.Count > 0)
{
artistsBatch = _libraryManager.GetArtists(artistNames.ToArray());
}
for (int index = 0; index < accessibleItems.Count; index++)
{
var item = accessibleItems[index];
var dto = GetBaseItemDtoInternal(item, options, user, owner);
var dto = GetBaseItemDtoInternal(
item,
options,
user,
owner,
userDataBatch?.GetValueOrDefault(item.Id),
allCollectionFolders,
childCountBatch,
playedCountBatch,
artistsBatch);
if (item is LiveTvChannel tvChannel)
{
@@ -197,7 +282,7 @@ namespace Emby.Server.Implementations.Dto
public BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User? user = null, BaseItem? owner = null)
{
var dto = GetBaseItemDtoInternal(item, options, user, owner);
var dto = GetBaseItemDtoInternal(item, options, user, owner, null);
if (item is LiveTvChannel tvChannel)
{
LivetvManager.AddChannelInfo(new[] { (dto, tvChannel) }, options, user);
@@ -215,7 +300,16 @@ namespace Emby.Server.Implementations.Dto
return dto;
}
private BaseItemDto GetBaseItemDtoInternal(BaseItem item, DtoOptions options, User? user = null, BaseItem? owner = null)
private BaseItemDto GetBaseItemDtoInternal(
BaseItem item,
DtoOptions options,
User? user = null,
BaseItem? owner = null,
UserItemData? userData = null,
List<Folder>? allCollectionFolders = null,
Dictionary<Guid, int>? childCountBatch = null,
Dictionary<Guid, (int Played, int Total)>? playedCountBatch = null,
IReadOnlyDictionary<string, MusicArtist[]>? artistsBatch = null)
{
var dto = new BaseItemDto
{
@@ -252,7 +346,14 @@ namespace Emby.Server.Implementations.Dto
if (user is not null)
{
AttachUserSpecificInfo(dto, item, user, options);
AttachUserSpecificInfo(
dto,
item,
user,
options,
userData,
childCountBatch,
playedCountBatch);
}
if (item is IHasMediaSources
@@ -268,13 +369,15 @@ namespace Emby.Server.Implementations.Dto
AttachStudios(dto, item);
}
AttachBasicFields(dto, item, owner, options);
AttachBasicFields(dto, item, owner, options, artistsBatch);
if (options.ContainsField(ItemFields.CanDelete))
{
dto.CanDelete = user is null
? item.CanDelete()
: item.CanDelete(user);
: allCollectionFolders is not null
? item.CanDelete(user, allCollectionFolders)
: item.CanDelete(user);
}
if (options.ContainsField(ItemFields.CanDownload))
@@ -378,37 +481,7 @@ namespace Emby.Server.Implementations.Dto
return;
}
var query = new InternalItemsQuery(user)
{
Recursive = true,
DtoOptions = new DtoOptions(false) { EnableImages = false },
IncludeItemTypes = relatedItemKinds
};
switch (dto.Type)
{
case BaseItemKind.Genre:
case BaseItemKind.MusicGenre:
query.GenreIds = [dto.Id];
break;
case BaseItemKind.MusicArtist:
query.ArtistIds = [dto.Id];
break;
case BaseItemKind.Person:
query.PersonIds = [dto.Id];
break;
case BaseItemKind.Studio:
query.StudioIds = [dto.Id];
break;
case BaseItemKind.Year
when int.TryParse(dto.Name, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year):
query.Years = [year];
break;
default:
return;
}
var counts = _libraryManager.GetItemCounts(query);
var counts = _libraryManager.GetItemCountsForNameItem(dto.Type, dto.Id, relatedItemKinds, user);
dto.AlbumCount = counts.AlbumCount;
dto.ArtistCount = counts.ArtistCount;
@@ -458,7 +531,14 @@ namespace Emby.Server.Implementations.Dto
/// <summary>
/// Attaches the user specific info.
/// </summary>
private void AttachUserSpecificInfo(BaseItemDto dto, BaseItem item, User user, DtoOptions options)
private void AttachUserSpecificInfo(
BaseItemDto dto,
BaseItem item,
User user,
DtoOptions options,
UserItemData? userData = null,
Dictionary<Guid, int>? childCountBatch = null,
Dictionary<Guid, (int Played, int Total)>? playedCountBatch = null)
{
if (item.IsFolder)
{
@@ -466,7 +546,19 @@ namespace Emby.Server.Implementations.Dto
if (options.EnableUserData)
{
dto.UserData = _userDataRepository.GetUserDataDto(item, dto, user, options);
if (userData is not null)
{
// Use pre-fetched user data
dto.UserData = GetUserItemDataDto(userData, item.Id);
(int Played, int Total)? precomputed = playedCountBatch is not null
&& playedCountBatch.TryGetValue(item.Id, out var counts) ? counts : null;
item.FillUserDataDtoValues(dto.UserData, userData, dto, user, options, precomputed);
}
else
{
// Fall back to individual fetch
dto.UserData = _userDataRepository.GetUserDataDto(item, dto, user, options);
}
}
if (!dto.ChildCount.HasValue && item.SourceType == SourceType.Library)
@@ -485,7 +577,7 @@ namespace Emby.Server.Implementations.Dto
if (options.ContainsField(ItemFields.ChildCount))
{
dto.ChildCount ??= GetChildCount(folder, user);
dto.ChildCount ??= GetChildCount(folder, user, childCountBatch);
}
}
@@ -503,7 +595,17 @@ namespace Emby.Server.Implementations.Dto
{
if (options.EnableUserData)
{
dto.UserData = _userDataRepository.GetUserDataDto(item, user);
if (userData is not null)
{
// Use pre-fetched user data
dto.UserData = GetUserItemDataDto(userData, item.Id);
item.FillUserDataDtoValues(dto.UserData, userData, dto, user, options);
}
else
{
// Fall back to individual fetch
dto.UserData = _userDataRepository.GetUserDataDto(item, user);
}
}
}
@@ -513,7 +615,25 @@ namespace Emby.Server.Implementations.Dto
}
}
private static int GetChildCount(Folder folder, User user)
private static UserItemDataDto GetUserItemDataDto(UserItemData data, Guid itemId)
{
ArgumentNullException.ThrowIfNull(data);
return new UserItemDataDto
{
IsFavorite = data.IsFavorite,
Likes = data.Likes,
PlaybackPositionTicks = data.PlaybackPositionTicks,
PlayCount = data.PlayCount,
Rating = data.Rating,
Played = data.Played,
LastPlayedDate = data.LastPlayedDate,
ItemId = itemId,
Key = data.Key
};
}
private static int GetChildCount(Folder folder, User user, Dictionary<Guid, int>? childCountBatch)
{
// Right now this is too slow to calculate for top level folders on a per-user basis
// Just return something so that apps that are expecting a value won't think the folders are empty
@@ -522,6 +642,13 @@ namespace Emby.Server.Implementations.Dto
return Random.Shared.Next(1, 10);
}
// Use pre-fetched batch data if available
if (childCountBatch is not null && childCountBatch.TryGetValue(folder.Id, out var count))
{
return count;
}
// Fall back to individual query for special cases (Series, Season, etc.)
return folder.GetChildCount(user);
}
@@ -815,7 +942,8 @@ namespace Emby.Server.Implementations.Dto
/// <param name="item">The item.</param>
/// <param name="owner">The owner.</param>
/// <param name="options">The options.</param>
private void AttachBasicFields(BaseItemDto dto, BaseItem item, BaseItem? owner, DtoOptions options)
/// <param name="artistsBatch">Optional pre-fetched artist lookup shared across a batch of items.</param>
private void AttachBasicFields(BaseItemDto dto, BaseItem item, BaseItem? owner, DtoOptions options, IReadOnlyDictionary<string, MusicArtist[]>? artistsBatch = null)
{
if (options.ContainsField(ItemFields.DateCreated))
{
@@ -939,6 +1067,8 @@ namespace Emby.Server.Implementations.Dto
dto.OriginalTitle = item.OriginalTitle;
}
dto.OriginalLanguage = item.OriginalLanguage;
if (options.ContainsField(ItemFields.ParentId))
{
dto.ParentId = item.DisplayParentId;
@@ -1060,7 +1190,8 @@ namespace Emby.Server.Implementations.Dto
// Include artists that are not in the database yet, e.g., just added via metadata editor
// var foundArtists = artistItems.Items.Select(i => i.Item1.Name).ToList();
var artistsLookup = _libraryManager.GetArtists([.. hasArtist.Artists.Where(e => !string.IsNullOrWhiteSpace(e))]);
var artistsLookup = artistsBatch
?? _libraryManager.GetArtists([.. hasArtist.Artists.Where(e => !string.IsNullOrWhiteSpace(e))]);
dto.ArtistItems = hasArtist.Artists
.Where(name => !string.IsNullOrWhiteSpace(name))
@@ -1094,7 +1225,8 @@ namespace Emby.Server.Implementations.Dto
// })
// .ToList();
var albumArtistsLookup = _libraryManager.GetArtists([.. hasAlbumArtist.AlbumArtists.Where(e => !string.IsNullOrWhiteSpace(e))]);
var albumArtistsLookup = artistsBatch
?? _libraryManager.GetArtists([.. hasAlbumArtist.AlbumArtists.Where(e => !string.IsNullOrWhiteSpace(e))]);
dto.AlbumArtists = hasAlbumArtist.AlbumArtists
.Where(name => !string.IsNullOrWhiteSpace(name))
@@ -1132,11 +1264,6 @@ namespace Emby.Server.Implementations.Dto
}
}
if (options.ContainsField(ItemFields.Chapters))
{
dto.Chapters = _chapterManager.GetChapters(item.Id).ToList();
}
if (options.ContainsField(ItemFields.Trickplay))
{
var trickplay = _trickplayManager.GetTrickplayManifest(item).GetAwaiter().GetResult();
@@ -1150,6 +1277,11 @@ namespace Emby.Server.Implementations.Dto
dto.ExtraType = video.ExtraType;
}
if (options.ContainsField(ItemFields.Chapters))
{
dto.Chapters = _chapterManager.GetChapters(item.Id).ToList();
}
if (options.ContainsField(ItemFields.MediaStreams))
{
// Add VideoInfo

View File

@@ -21,6 +21,7 @@ namespace Emby.Server.Implementations.IO
private readonly ILibraryManager _libraryManager;
private readonly IServerConfigurationManager _configurationManager;
private readonly IFileSystem _fileSystem;
private readonly DotIgnoreIgnoreRule _dotIgnoreIgnoreRule;
/// <summary>
/// The file system watchers.
@@ -47,17 +48,20 @@ namespace Emby.Server.Implementations.IO
/// <param name="configurationManager">The configuration manager.</param>
/// <param name="fileSystem">The filesystem.</param>
/// <param name="appLifetime">The <see cref="IHostApplicationLifetime"/>.</param>
/// <param name="dotIgnoreIgnoreRule">The .ignore rule handler.</param>
public LibraryMonitor(
ILogger<LibraryMonitor> logger,
ILibraryManager libraryManager,
IServerConfigurationManager configurationManager,
IFileSystem fileSystem,
IHostApplicationLifetime appLifetime)
IHostApplicationLifetime appLifetime,
DotIgnoreIgnoreRule dotIgnoreIgnoreRule)
{
_libraryManager = libraryManager;
_logger = logger;
_configurationManager = configurationManager;
_fileSystem = fileSystem;
_dotIgnoreIgnoreRule = dotIgnoreIgnoreRule;
appLifetime.ApplicationStarted.Register(Start);
appLifetime.ApplicationStopping.Register(Stop);
@@ -354,7 +358,7 @@ namespace Emby.Server.Implementations.IO
}
var fileInfo = _fileSystem.GetFileSystemInfo(path);
if (DotIgnoreIgnoreRule.IsIgnored(fileInfo, null))
if (_dotIgnoreIgnoreRule.ShouldIgnore(fileInfo, null))
{
return;
}

View File

@@ -586,6 +586,12 @@ namespace Emby.Server.Implementations.IO
/// <inheritdoc />
public virtual IEnumerable<FileSystemMetadata> GetFiles(string path, string searchPattern, IReadOnlyList<string>? extensions, bool enableCaseSensitiveExtensions, bool recursive = false)
{
if (!Directory.Exists(path))
{
_logger.LogWarning("Directory does not exist: {Path}", path);
return [];
}
var enumerationOptions = GetEnumerationOptions(recursive);
// On linux and macOS the search pattern is case-sensitive

View File

@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using BitFaster.Caching.Lru;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Resolvers;
@@ -15,22 +17,36 @@ public class DotIgnoreIgnoreRule : IResolverIgnoreRule
{
private static readonly bool IsWindows = OperatingSystem.IsWindows();
private static FileInfo? FindIgnoreFile(DirectoryInfo directory)
{
for (var current = directory; current is not null; current = current.Parent)
{
var ignorePath = Path.Join(current.FullName, ".ignore");
if (File.Exists(ignorePath))
{
return new FileInfo(ignorePath);
}
}
private readonly FastConcurrentLru<string, IgnoreFileCacheEntry> _directoryCache;
private readonly FastConcurrentLru<string, ParsedIgnoreCacheEntry> _rulesCache;
return null;
/// <summary>
/// Initializes a new instance of the <see cref="DotIgnoreIgnoreRule"/> class.
/// </summary>
public DotIgnoreIgnoreRule()
{
var cacheSize = Math.Max(100, Environment.ProcessorCount * 100);
_directoryCache = new FastConcurrentLru<string, IgnoreFileCacheEntry>(
Environment.ProcessorCount,
cacheSize,
StringComparer.Ordinal);
_rulesCache = new FastConcurrentLru<string, ParsedIgnoreCacheEntry>(
Environment.ProcessorCount,
Math.Max(32, cacheSize / 4),
StringComparer.Ordinal);
}
/// <inheritdoc />
public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem? parent) => IsIgnored(fileInfo, parent);
public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem? parent) => IsIgnoredInternal(fileInfo, parent);
/// <summary>
/// Clears the directory lookup cache. The parsed rules cache is not cleared
/// as it validates file modification time on each access.
/// </summary>
public void ClearDirectoryCache()
{
_directoryCache.Clear();
}
/// <summary>
/// Checks whether or not the file is ignored.
@@ -38,40 +54,38 @@ public class DotIgnoreIgnoreRule : IResolverIgnoreRule
/// <param name="fileInfo">The file information.</param>
/// <param name="parent">The parent BaseItem.</param>
/// <returns>True if the file should be ignored.</returns>
public static bool IsIgnored(FileSystemMetadata fileInfo, BaseItem? parent)
public bool IsIgnoredInternal(FileSystemMetadata fileInfo, BaseItem? parent)
{
var searchDirectory = fileInfo.IsDirectory
? new DirectoryInfo(fileInfo.FullName)
: new DirectoryInfo(Path.GetDirectoryName(fileInfo.FullName) ?? string.Empty);
? fileInfo.FullName
: Path.GetDirectoryName(fileInfo.FullName);
if (string.IsNullOrEmpty(searchDirectory.FullName))
if (string.IsNullOrEmpty(searchDirectory))
{
return false;
}
var ignoreFile = FindIgnoreFile(searchDirectory);
var ignoreFile = FindIgnoreFileCached(searchDirectory);
if (ignoreFile is null)
{
return false;
}
// Fast path in case the ignore files isn't a symlink and is empty
if (ignoreFile.LinkTarget is null && ignoreFile.Length == 0)
var parsedEntry = GetParsedRules(ignoreFile);
if (parsedEntry is null)
{
// File was deleted after we cached the path - clear the directory cache entry and return false
_directoryCache.TryRemove(searchDirectory, out _);
return false;
}
// Empty file means ignore everything
if (parsedEntry.IsEmpty)
{
// Ignore directory if we just have the file
return true;
}
var content = GetFileContent(ignoreFile);
return string.IsNullOrWhiteSpace(content)
|| CheckIgnoreRules(fileInfo.FullName, content, fileInfo.IsDirectory);
}
private static bool CheckIgnoreRules(string path, string ignoreFileContent, bool isDirectory)
{
// If file has content, base ignoring off the content .gitignore-style rules
var rules = ignoreFileContent.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
return CheckIgnoreRules(path, rules, isDirectory);
return parsedEntry.Rules.IsIgnored(GetPathToCheck(fileInfo.FullName, fileInfo.IsDirectory));
}
/// <summary>
@@ -117,8 +131,8 @@ public class DotIgnoreIgnoreRule : IResolverIgnoreRule
return true;
}
// Mitigate the problem of the Ignore library not handling Windows paths correctly.
// See https://github.com/jellyfin/jellyfin/issues/15484
// Mitigate the problem of the Ignore library not handling Windows paths correctly.
// See https://github.com/jellyfin/jellyfin/issues/15484
var pathToCheck = normalizePath ? path.NormalizePath('/') : path;
// Add trailing slash for directories to match "folder/"
@@ -130,11 +144,196 @@ public class DotIgnoreIgnoreRule : IResolverIgnoreRule
return ignore.IsIgnored(pathToCheck);
}
private static string GetFileContent(FileInfo ignoreFile)
private FileInfo? FindIgnoreFileCached(string directory)
{
ignoreFile = FileSystemHelper.ResolveLinkTarget(ignoreFile, returnFinalTarget: true) ?? ignoreFile;
return ignoreFile.Exists
? File.ReadAllText(ignoreFile.FullName)
: string.Empty;
// Check if we have a cached result for this directory
if (_directoryCache.TryGet(directory, out var cached))
{
return cached.IgnoreFileDirectory is null
? null
: new FileInfo(Path.Join(cached.IgnoreFileDirectory, ".ignore"));
}
DirectoryInfo startDir;
try
{
startDir = new DirectoryInfo(directory);
}
catch (ArgumentException)
{
return null;
}
// Walk up the directory tree to find .ignore file using DirectoryInfo.Parent
var checkedDirs = new List<string> { directory };
for (var current = startDir; current is not null; current = current.Parent)
{
var currentPath = current.FullName;
// Check if this intermediate directory is cached
if (current != startDir && _directoryCache.TryGet(currentPath, out var parentCached))
{
// Cache the result for all directories we checked
var entry = new IgnoreFileCacheEntry(parentCached.IgnoreFileDirectory);
foreach (var dir in checkedDirs)
{
_directoryCache.AddOrUpdate(dir, entry);
}
return parentCached.IgnoreFileDirectory is null
? null
: new FileInfo(Path.Join(parentCached.IgnoreFileDirectory, ".ignore"));
}
var ignoreFile = new FileInfo(Path.Join(currentPath, ".ignore"));
if (ignoreFile.Exists)
{
// Cache for all directories we checked
var entry = new IgnoreFileCacheEntry(currentPath);
foreach (var dir in checkedDirs)
{
_directoryCache.AddOrUpdate(dir, entry);
}
return ignoreFile;
}
if (current != startDir)
{
checkedDirs.Add(currentPath);
}
}
// No .ignore file found - cache null result for all directories
var nullEntry = new IgnoreFileCacheEntry((string?)null);
foreach (var dir in checkedDirs)
{
_directoryCache.AddOrUpdate(dir, nullEntry);
}
return null;
}
private ParsedIgnoreCacheEntry? GetParsedRules(FileInfo ignoreFile)
{
if (!ignoreFile.Exists)
{
_rulesCache.TryRemove(ignoreFile.FullName, out _);
return null;
}
var lastModified = ignoreFile.LastWriteTimeUtc;
var fileLength = ignoreFile.Length;
var key = ignoreFile.FullName;
// Check cache
if (_rulesCache.TryGet(key, out var cached))
{
if (cached.FileLastModified == lastModified && cached.FileLength == fileLength)
{
return cached;
}
// Stale - need to reparse
_rulesCache.TryRemove(key, out _);
}
// Parse the file
var parsedEntry = ParseIgnoreFile(ignoreFile, lastModified, fileLength);
_rulesCache.AddOrUpdate(key, parsedEntry);
return parsedEntry;
}
private static ParsedIgnoreCacheEntry ParseIgnoreFile(FileInfo ignoreFile, DateTime lastModified, long fileLength)
{
if (ignoreFile.LinkTarget is null && fileLength == 0)
{
return new ParsedIgnoreCacheEntry
{
Rules = new Ignore.Ignore(),
FileLastModified = lastModified,
FileLength = fileLength,
IsEmpty = true
};
}
// Resolve symlinks
var resolvedFile = FileSystemHelper.ResolveLinkTarget(ignoreFile, returnFinalTarget: true) ?? ignoreFile;
if (!resolvedFile.Exists)
{
return new ParsedIgnoreCacheEntry
{
Rules = new Ignore.Ignore(),
FileLastModified = lastModified,
FileLength = fileLength,
IsEmpty = true
};
}
var content = File.ReadAllText(resolvedFile.FullName);
if (string.IsNullOrWhiteSpace(content))
{
return new ParsedIgnoreCacheEntry
{
Rules = new Ignore.Ignore(),
FileLastModified = lastModified,
FileLength = fileLength,
IsEmpty = true
};
}
var rules = content.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
var ignore = new Ignore.Ignore();
var validRulesAdded = 0;
foreach (var rule in rules)
{
try
{
ignore.Add(rule);
validRulesAdded++;
}
catch (RegexParseException)
{
// Ignore invalid patterns
}
}
// No valid rules means treat as empty (ignore all)
return new ParsedIgnoreCacheEntry
{
Rules = ignore,
FileLastModified = lastModified,
FileLength = fileLength,
IsEmpty = validRulesAdded == 0
};
}
private static string GetPathToCheck(string path, bool isDirectory)
{
// Normalize Windows paths
var pathToCheck = IsWindows ? path.NormalizePath('/') : path;
// Add trailing slash for directories to match "folder/"
if (isDirectory)
{
pathToCheck = string.Concat(pathToCheck.AsSpan().TrimEnd('/'), "/");
}
return pathToCheck;
}
private readonly record struct IgnoreFileCacheEntry(string? IgnoreFileDirectory);
private sealed class ParsedIgnoreCacheEntry
{
public required Ignore.Ignore Rules { get; init; }
public required DateTime FileLastModified { get; init; }
public required long FileLength { get; init; }
public required bool IsEmpty { get; init; }
}
}

View File

@@ -30,18 +30,17 @@ using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.MediaSegments;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Controller.Trickplay;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@@ -77,12 +76,17 @@ namespace Emby.Server.Implementations.Library
private readonly IMediaEncoder _mediaEncoder;
private readonly IFileSystem _fileSystem;
private readonly IItemRepository _itemRepository;
private readonly IItemPersistenceService _persistenceService;
private readonly INextUpService _nextUpService;
private readonly IItemCountService _countService;
private readonly ILinkedChildrenService _linkedChildrenService;
private readonly IImageProcessor _imageProcessor;
private readonly NamingOptions _namingOptions;
private readonly IPeopleRepository _peopleRepository;
private readonly ExtraResolver _extraResolver;
private readonly IPathManager _pathManager;
private readonly FastConcurrentLru<Guid, BaseItem> _cache;
private readonly DotIgnoreIgnoreRule _dotIgnoreIgnoreRule;
/// <summary>
/// The _root folder sync lock.
@@ -115,11 +119,16 @@ namespace Emby.Server.Implementations.Library
/// <param name="userViewManagerFactory">The user view manager.</param>
/// <param name="mediaEncoder">The media encoder.</param>
/// <param name="itemRepository">The item repository.</param>
/// <param name="persistenceService">The item persistence service.</param>
/// <param name="nextUpService">The next up service.</param>
/// <param name="countService">The item count service.</param>
/// <param name="linkedChildrenService">The linked children service.</param>
/// <param name="imageProcessor">The image processor.</param>
/// <param name="namingOptions">The naming options.</param>
/// <param name="directoryService">The directory service.</param>
/// <param name="peopleRepository">The people repository.</param>
/// <param name="pathManager">The path manager.</param>
/// <param name="dotIgnoreIgnoreRule">The .ignore rule handler.</param>
public LibraryManager(
IServerApplicationHost appHost,
ILoggerFactory loggerFactory,
@@ -133,11 +142,16 @@ namespace Emby.Server.Implementations.Library
Lazy<IUserViewManager> userViewManagerFactory,
IMediaEncoder mediaEncoder,
IItemRepository itemRepository,
IItemPersistenceService persistenceService,
INextUpService nextUpService,
IItemCountService countService,
ILinkedChildrenService linkedChildrenService,
IImageProcessor imageProcessor,
NamingOptions namingOptions,
IDirectoryService directoryService,
IPeopleRepository peopleRepository,
IPathManager pathManager)
IPathManager pathManager,
DotIgnoreIgnoreRule dotIgnoreIgnoreRule)
{
_appHost = appHost;
_logger = loggerFactory.CreateLogger<LibraryManager>();
@@ -151,6 +165,10 @@ namespace Emby.Server.Implementations.Library
_userViewManagerFactory = userViewManagerFactory;
_mediaEncoder = mediaEncoder;
_itemRepository = itemRepository;
_persistenceService = persistenceService;
_nextUpService = nextUpService;
_countService = countService;
_linkedChildrenService = linkedChildrenService;
_imageProcessor = imageProcessor;
_cache = new FastConcurrentLru<Guid, BaseItem>(_configurationManager.Configuration.CacheSize);
@@ -158,6 +176,7 @@ namespace Emby.Server.Implementations.Library
_namingOptions = namingOptions;
_peopleRepository = peopleRepository;
_pathManager = pathManager;
_dotIgnoreIgnoreRule = dotIgnoreIgnoreRule;
_extraResolver = new ExtraResolver(loggerFactory.CreateLogger<ExtraResolver>(), namingOptions, directoryService);
_configurationManager.ConfigurationUpdated += ConfigurationUpdated;
@@ -327,9 +346,17 @@ namespace Emby.Server.Implementations.Library
DeleteItem(item, options, parent, notifyParentItem);
}
public void DeleteItemsUnsafeFast(IEnumerable<BaseItem> items)
public void DeleteItemsUnsafeFast(IReadOnlyCollection<BaseItem> items, bool deleteSourceFiles = false)
{
var pathMaps = items.Select(e => (Item: e, InternalPath: GetInternalMetadataPaths(e), DeletePaths: e.GetDeletePaths())).ToArray();
if (items.Count == 0)
{
return;
}
var pathMaps = items.Select(e =>
(Item: e,
InternalPath: GetInternalMetadataPaths(e),
DeletePaths: deleteSourceFiles ? e.GetDeletePaths() : [])).ToArray();
foreach (var (item, internalPaths, pathsToDelete) in pathMaps)
{
@@ -363,7 +390,7 @@ namespace Emby.Server.Implementations.Library
}
}
_itemRepository.DeleteItem([.. pathMaps.Select(f => f.Item.Id)]);
_persistenceService.DeleteItem([.. pathMaps.Select(f => f.Item.Id)]);
}
public void DeleteItem(BaseItem item, DeleteOptions options, BaseItem parent, bool notifyParentItem)
@@ -406,6 +433,99 @@ namespace Emby.Server.Implementations.Library
item.Id);
}
// If deleting a primary version video, clear PrimaryVersionId from alternate versions
// OwnerId check: items with OwnerId set are alternate versions or extras, not primaries
if (item is Video video && !video.PrimaryVersionId.HasValue && video.OwnerId.IsEmpty())
{
var localAlternateIds = GetLocalAlternateVersionIds(video).ToHashSet();
var allAlternateVersions = localAlternateIds
.Concat(GetLinkedAlternateVersions(video).Select(v => v.Id))
.Distinct()
.Select(id => GetItemById(id))
.OfType<Video>()
.ToList();
// Partition alternates by whether their files still exist on disk
var alternateVersions = new List<Video>();
var missingAlternates = new List<Video>();
foreach (var alt in allAlternateVersions)
{
if (!string.IsNullOrEmpty(alt.Path) && !_fileSystem.FileExists(alt.Path))
{
missingAlternates.Add(alt);
}
else
{
alternateVersions.Add(alt);
}
}
// Delete alternates whose files no longer exist to avoid ghost items.
// Clear PrimaryVersionId first so DeleteItem doesn't try to update the primary being deleted.
foreach (var missing in missingAlternates)
{
_logger.LogInformation(
"Deleting missing alternate version {Name} ({Path})",
missing.Name ?? "Unknown name",
missing.Path ?? string.Empty);
missing.SetPrimaryVersionId(null);
missing.OwnerId = Guid.Empty;
missing.LocalAlternateVersions = [];
missing.LinkedAlternateVersions = [];
DeleteItem(missing, new DeleteOptions { DeleteFileLocation = false }, false);
}
if (alternateVersions.Count > 0)
{
_logger.LogInformation(
"Clearing PrimaryVersionId from {Count} alternate versions of {Name}",
alternateVersions.Count,
item.Name ?? "Unknown name");
// Promote the first alternate version to be the new primary
var newPrimary = alternateVersions[0];
newPrimary.SetPrimaryVersionId(null);
newPrimary.OwnerId = Guid.Empty;
// Transfer alternate version arrays from old primary to new primary
// so UpdateToRepositoryAsync creates correct LinkedChildren entries
newPrimary.LocalAlternateVersions = video.LocalAlternateVersions
.Where(p => !string.Equals(p, newPrimary.Path, StringComparison.OrdinalIgnoreCase))
.ToArray();
newPrimary.LinkedAlternateVersions = video.LinkedAlternateVersions
.Where(lc => !lc.ItemId.HasValue || !lc.ItemId.Value.Equals(newPrimary.Id))
.ToArray();
newPrimary.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).GetAwaiter().GetResult();
// Re-route playlist/collection references from deleted primary to new primary
RerouteLinkedChildReferencesAsync(video.Id, newPrimary.Id).GetAwaiter().GetResult();
// Update remaining alternates to point to new primary
foreach (var alternate in alternateVersions.Skip(1))
{
alternate.SetPrimaryVersionId(newPrimary.Id);
// Only set OwnerId for local alternates; linked alternates are independent items
alternate.OwnerId = localAlternateIds.Contains(alternate.Id) ? newPrimary.Id : Guid.Empty;
alternate.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).GetAwaiter().GetResult();
}
}
}
else if (item is Video alternateVideo && alternateVideo.PrimaryVersionId.HasValue)
{
// If deleting an alternate version, re-route references to its primary
RerouteLinkedChildReferencesAsync(alternateVideo.Id, alternateVideo.PrimaryVersionId.Value).GetAwaiter().GetResult();
// Remove deleted alternate from primary's LinkedAlternateVersions
if (GetItemById(alternateVideo.PrimaryVersionId.Value) is Video primaryVideo)
{
primaryVideo.LinkedAlternateVersions = primaryVideo.LinkedAlternateVersions
.Where(lc => !lc.ItemId.HasValue || !lc.ItemId.Value.Equals(alternateVideo.Id))
.ToArray();
primaryVideo.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).GetAwaiter().GetResult();
}
}
var children = item.IsFolder
? ((Folder)item).GetRecursiveChildren(false)
: [];
@@ -450,7 +570,7 @@ namespace Emby.Server.Implementations.Library
item.SetParent(null);
_itemRepository.DeleteItem([item.Id, .. children.Select(f => f.Id)]);
_persistenceService.DeleteItem([item.Id, .. children.Select(f => f.Id)]);
_cache.TryRemove(item.Id, out _);
foreach (var child in children)
{
@@ -576,6 +696,9 @@ namespace Emby.Server.Implementations.Library
// Trickplay
list.Add(_pathManager.GetTrickplayDirectory(video));
// Chapter Images
list.Add(_pathManager.GetChapterImageFolderPath(video));
// Subtitles and attachments
foreach (var mediaSource in item.GetMediaSources(false))
{
@@ -657,8 +780,63 @@ namespace Emby.Server.Implementations.Library
return key.GetMD5();
}
public BaseItem? ResolvePath(FileSystemMetadata fileInfo, Folder? parent = null, IDirectoryService? directoryService = null)
=> ResolvePath(fileInfo, directoryService ?? new DirectoryService(_fileSystem), null, parent);
public BaseItem? ResolvePath(
FileSystemMetadata fileInfo,
Folder? parent = null,
IDirectoryService? directoryService = null,
CollectionType? collectionType = null)
=> ResolvePath(fileInfo, directoryService ?? new DirectoryService(_fileSystem), null, parent, collectionType);
/// <inheritdoc />
public Video? ResolveAlternateVersion(string path, Type expectedVideoType, Folder? parent, CollectionType? collectionType)
{
// Clean up any existing item saved with wrong type (e.g. Video instead of Movie).
// This happens when items were previously resolved without proper type context
// in mixed-content libraries where collectionType is null.
var expectedId = GetNewItemId(path, expectedVideoType);
if (expectedVideoType != typeof(Video))
{
var wrongTypeId = GetNewItemId(path, typeof(Video));
if (!wrongTypeId.Equals(expectedId) && GetItemById(wrongTypeId) is Video wrongTypeItem)
{
_logger.LogInformation(
"Removing alternate version with wrong type {WrongType}, expected {ExpectedType}: {Path}",
wrongTypeItem.GetType().Name,
expectedVideoType.Name,
path);
DeleteItem(wrongTypeItem, new DeleteOptions { DeleteFileLocation = false });
}
}
var resolved = ResolvePath(
_fileSystem.GetFileSystemInfo(path),
parent,
collectionType: collectionType) as Video;
if (resolved is null)
{
return null;
}
// Ensure the alternate version has the same concrete type as the primary video.
// ResolvePath may return a generic Video for files in mixed-content libraries
// where collectionType is null, even though the primary is a Movie/Episode/etc.
if (resolved.GetType() != expectedVideoType)
{
if (Activator.CreateInstance(expectedVideoType) is Video correctVideo)
{
correctVideo.Path = resolved.Path;
correctVideo.Name = resolved.Name;
correctVideo.VideoType = resolved.VideoType;
correctVideo.ProductionYear = resolved.ProductionYear;
correctVideo.ExtraType = resolved.ExtraType;
resolved = correctVideo;
}
}
resolved.Id = expectedId;
return resolved;
}
private BaseItem? ResolvePath(
FileSystemMetadata fileInfo,
@@ -1041,7 +1219,7 @@ namespace Emby.Server.Implementations.Library
public IReadOnlyDictionary<string, MusicArtist[]> GetArtists(IReadOnlyList<string> names)
{
return _itemRepository.FindArtists(names);
return _linkedChildrenService.FindArtists(names);
}
public MusicArtist GetArtist(string name, DtoOptions options)
@@ -1131,6 +1309,7 @@ namespace Emby.Server.Implementations.Library
public async Task ValidateMediaLibraryInternal(IProgress<double> progress, CancellationToken cancellationToken)
{
IsScanRunning = true;
ClearIgnoreRuleCache();
LibraryMonitor.Stop();
try
@@ -1139,6 +1318,7 @@ namespace Emby.Server.Implementations.Library
}
finally
{
ClearIgnoreRuleCache();
LibraryMonitor.Start();
IsScanRunning = false;
}
@@ -1146,6 +1326,7 @@ namespace Emby.Server.Implementations.Library
public async Task ValidateTopLibraryFolders(CancellationToken cancellationToken, bool removeRoot = false)
{
ClearIgnoreRuleCache();
RootFolder.Children = null;
await RootFolder.RefreshMetadata(cancellationToken).ConfigureAwait(false);
@@ -1186,8 +1367,16 @@ namespace Emby.Server.Implementations.Library
if (toDelete.Count > 0)
{
_itemRepository.DeleteItem(toDelete.ToArray());
_persistenceService.DeleteItem(toDelete.ToArray());
}
ClearIgnoreRuleCache();
}
/// <inheritdoc />
public void ClearIgnoreRuleCache()
{
_dotIgnoreIgnoreRule.ClearDirectoryCache();
}
private async Task PerformLibraryValidation(IProgress<double> progress, CancellationToken cancellationToken)
@@ -1262,7 +1451,7 @@ namespace Emby.Server.Implementations.Library
progress.Report(percent * 100);
}
_itemRepository.UpdateInheritedValues();
_persistenceService.UpdateInheritedValues();
progress.Report(100);
}
@@ -1421,14 +1610,7 @@ namespace Emby.Server.Implementations.Library
AddUserToQuery(query, query.User, allowExternalContent);
}
var itemList = _itemRepository.GetItemList(query);
var user = query.User;
if (user is not null)
{
return itemList.Where(i => i.IsVisible(user)).ToList();
}
return itemList;
return _itemRepository.GetItemList(query);
}
public IReadOnlyList<BaseItem> GetItemList(InternalItemsQuery query)
@@ -1452,7 +1634,7 @@ namespace Emby.Server.Implementations.Library
AddUserToQuery(query, query.User);
}
return _itemRepository.GetCount(query);
return _countService.GetCount(query);
}
public ItemCounts GetItemCounts(InternalItemsQuery query)
@@ -1471,7 +1653,30 @@ namespace Emby.Server.Implementations.Library
AddUserToQuery(query, query.User);
}
return _itemRepository.GetItemCounts(query);
return _countService.GetItemCounts(query);
}
/// <inheritdoc/>
public ItemCounts GetItemCountsForNameItem(BaseItemKind kind, Guid id, BaseItemKind[] relatedItemKinds, User? user)
{
var query = new InternalItemsQuery(user);
if (user is not null)
{
AddUserToQuery(query, user);
}
return _countService.GetItemCountsForNameItem(kind, id, relatedItemKinds, query);
}
public Dictionary<Guid, int> GetChildCountBatch(IReadOnlyList<Guid> parentIds, Guid? userId)
{
return _countService.GetChildCountBatch(parentIds, userId);
}
/// <inheritdoc/>
public Dictionary<Guid, (int Played, int Total)> GetPlayedAndTotalCountBatch(IReadOnlyList<Guid> folderIds, User user)
{
return _countService.GetPlayedAndTotalCountBatch(folderIds, user);
}
public IReadOnlyList<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents)
@@ -1516,7 +1721,17 @@ namespace Emby.Server.Implementations.Library
}
}
return _itemRepository.GetNextUpSeriesKeys(query, dateCutoff);
return _nextUpService.GetNextUpSeriesKeys(query, dateCutoff);
}
/// <inheritdoc />
public IReadOnlyDictionary<string, MediaBrowser.Controller.Persistence.NextUpEpisodeBatchResult> GetNextUpEpisodesBatch(
InternalItemsQuery query,
IReadOnlyList<string> seriesKeys,
bool includeSpecials,
bool includeWatchedForRewatching)
{
return _nextUpService.GetNextUpEpisodesBatch(query, seriesKeys, includeSpecials, includeWatchedForRewatching);
}
public QueryResult<BaseItem> QueryItems(InternalItemsQuery query)
@@ -1683,6 +1898,25 @@ namespace Emby.Server.Implementations.Library
query.TopParentIds = [Guid.NewGuid()];
}
}
else if (parents.Count == 1 && parents.First() is Folder folder
&& (folder is Playlist || folder is BoxSet)
&& folder.LinkedChildren.Length > 0)
{
// Playlists and BoxSets store their contents in LinkedChildren and never
// populate AncestorIds for those items, so a recursive AncestorIds query
// would return zero rows. Resolve to the linked child IDs up front and
// route through the existing indexed ItemIds filter.
query.ItemIds = folder.LinkedChildren
.Where(lc => lc.ItemId.HasValue && !lc.ItemId.Value.IsEmpty())
.Select(lc => lc.ItemId!.Value)
.ToArray();
// Empty linked-children should still return empty rather than scanning everything.
if (query.ItemIds.Length == 0)
{
query.ItemIds = [Guid.NewGuid()];
}
}
else
{
// We need to be able to query from any arbitrary ancestor up the tree
@@ -1700,6 +1934,11 @@ namespace Emby.Server.Implementations.Library
private void AddUserToQuery(InternalItemsQuery query, User user, bool allowExternalContent = true)
{
if (query.User is null)
{
query.SetUser(user);
}
if (query.AncestorIds.Length == 0 &&
query.ParentId.IsEmpty() &&
query.ChannelIds.Count == 0 &&
@@ -1725,6 +1964,15 @@ namespace Emby.Server.Implementations.Library
}
}
/// <inheritdoc/>
public void ConfigureUserAccess(InternalItemsQuery query, User user)
{
ArgumentNullException.ThrowIfNull(query);
ArgumentNullException.ThrowIfNull(user);
AddUserToQuery(query, user);
}
private IEnumerable<Guid> GetTopParentIdsForQuery(BaseItem item, User? user)
{
if (item is UserView view)
@@ -1889,6 +2137,44 @@ namespace Emby.Server.Implementations.Library
return video;
}
/// <inheritdoc />
public IEnumerable<Guid> GetLocalAlternateVersionIds(Video video)
{
ArgumentNullException.ThrowIfNull(video);
var linkedIds = _linkedChildrenService.GetLinkedChildrenIds(video.Id, (int)MediaBrowser.Controller.Entities.LinkedChildType.LocalAlternateVersion);
if (linkedIds.Count > 0)
{
return linkedIds;
}
return [];
}
/// <inheritdoc />
public IEnumerable<Video> GetLinkedAlternateVersions(Video video)
{
ArgumentNullException.ThrowIfNull(video);
var linkedIds = _linkedChildrenService.GetLinkedChildrenIds(video.Id, (int)MediaBrowser.Controller.Entities.LinkedChildType.LinkedAlternateVersion);
if (linkedIds.Count > 0)
{
return linkedIds
.Select(id => GetItemById(id))
.Where(i => i is not null)
.OfType<Video>()
.OrderBy(i => i.SortName);
}
return [];
}
/// <inheritdoc />
public void UpsertLinkedChild(Guid parentId, Guid childId, MediaBrowser.Controller.Entities.LinkedChildType childType)
{
_linkedChildrenService.UpsertLinkedChild(parentId, childId, childType);
}
/// <inheritdoc />
public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User? user, IEnumerable<ItemSortBy> sortBy, SortOrder sortOrder)
{
@@ -1993,9 +2279,44 @@ namespace Emby.Server.Implementations.Library
/// <inheritdoc />
public void CreateItems(IReadOnlyList<BaseItem> items, BaseItem? parent, CancellationToken cancellationToken)
{
_itemRepository.SaveItems(items, cancellationToken);
// Resolve and add any local alternate version items that don't exist yet
// This ensures they exist in the database when LinkedChildren are processed
var allItems = new List<BaseItem>(items);
var parentFolder = parent as Folder;
var parentCollectionType = parent is not null ? GetTopFolderContentType(parent) : null;
foreach (var item in items)
{
if (item is Video video && video.LocalAlternateVersions.Length > 0)
{
var videoType = video.GetType();
foreach (var path in video.LocalAlternateVersions)
{
if (string.IsNullOrEmpty(path))
{
continue;
}
// Use the primary video's type for ID calculation to ensure consistency
var altId = GetNewItemId(path, videoType);
if (GetItemById(altId) is null && !allItems.Any(i => i.Id.Equals(altId)))
{
// Alternate version doesn't exist, resolve and create it
// ensuring it has the same type as the primary video
var altVideo = ResolveAlternateVersion(path, videoType, parentFolder, parentCollectionType);
if (altVideo is not null)
{
altVideo.OwnerId = video.Id;
altVideo.SetPrimaryVersionId(video.Id);
allItems.Add(altVideo);
}
}
}
}
}
_persistenceService.SaveItems(allItems, cancellationToken);
foreach (var item in allItems)
{
RegisterItem(item);
}
@@ -2144,7 +2465,7 @@ namespace Emby.Server.Implementations.Library
item.ValidateImages();
await _itemRepository.SaveImagesAsync(item).ConfigureAwait(false);
await _persistenceService.SaveImagesAsync(item).ConfigureAwait(false);
RegisterItem(item);
}
@@ -2161,7 +2482,50 @@ namespace Emby.Server.Implementations.Library
item.DateLastSaved = DateTime.UtcNow;
}
_itemRepository.SaveItems(items, cancellationToken);
// Resolve and add any local alternate version items that don't exist yet
// This ensures they exist in the database when LinkedChildren are processed
var allItems = new List<BaseItem>(items);
var parentFolder = parent as Folder;
var parentCollectionType = GetTopFolderContentType(parent);
foreach (var item in items)
{
if (item is Video video && video.LocalAlternateVersions.Length > 0)
{
var videoType = video.GetType();
foreach (var path in video.LocalAlternateVersions)
{
if (string.IsNullOrEmpty(path))
{
continue;
}
// Use the primary video's type for ID calculation to ensure consistency
var altId = GetNewItemId(path, videoType);
if (GetItemById(altId) is null && !allItems.Any(i => i.Id.Equals(altId)))
{
// Alternate version doesn't exist, resolve and create it
// ensuring it has the same type as the primary video
var altVideo = ResolveAlternateVersion(path, videoType, parentFolder, parentCollectionType);
if (altVideo is not null)
{
altVideo.OwnerId = video.Id;
altVideo.SetPrimaryVersionId(video.Id);
allItems.Add(altVideo);
}
}
}
}
}
_persistenceService.SaveItems(allItems, cancellationToken);
foreach (var item in allItems)
{
if (!items.Contains(item))
{
RegisterItem(item);
}
}
if (parent is Folder folder)
{
@@ -2205,7 +2569,7 @@ namespace Emby.Server.Implementations.Library
/// <inheritdoc />
public async Task ReattachUserDataAsync(BaseItem item, CancellationToken cancellationToken)
{
await _itemRepository.ReattachUserDataAsync(item, cancellationToken).ConfigureAwait(false);
await _persistenceService.ReattachUserDataAsync(item, cancellationToken).ConfigureAwait(false);
}
public async Task RunMetadataSavers(BaseItem item, ItemUpdateType updateReason)
@@ -2833,8 +3197,9 @@ namespace Emby.Server.Implementations.Library
public IEnumerable<BaseItem> FindExtras(BaseItem owner, IReadOnlyList<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
{
// Apply .ignore rules
var filtered = fileSystemChildren.Where(c => !DotIgnoreIgnoreRule.IsIgnored(c, owner)).ToList();
var ownerVideoInfo = VideoResolver.Resolve(owner.Path, owner.IsFolder, _namingOptions, libraryRoot: owner.ContainingFolderPath);
var filtered = fileSystemChildren.Where(c => !_dotIgnoreIgnoreRule.ShouldIgnore(c, owner)).ToList();
var isFolder = owner.IsFolder || (owner is Video video && (video.VideoType == VideoType.BluRay || video.VideoType == VideoType.Dvd));
var ownerVideoInfo = VideoResolver.Resolve(owner.Path, isFolder, _namingOptions, libraryRoot: owner.ContainingFolderPath);
if (ownerVideoInfo is null)
{
yield break;
@@ -2896,10 +3261,16 @@ namespace Emby.Server.Implementations.Library
extra.ExtraType = extraType;
}
extra.ParentId = Guid.Empty;
extra.OwnerId = owner.Id;
extra.IsInMixedFolder = isInMixedFolder;
return extra;
// Only return items that are actual extras (have ExtraType set)
// Note: OwnerId and ParentId are set by RefreshExtras, not here,
// so that RefreshExtras can detect when they need updating and set ForceSave.
if (extra.ExtraType is not null)
{
extra.IsInMixedFolder = isInMixedFolder;
return extra;
}
return null;
}
}
@@ -2918,7 +3289,7 @@ namespace Emby.Server.Implementations.Library
public IReadOnlyList<PersonInfo> GetPeople(InternalPeopleQuery query)
{
return _peopleRepository.GetPeople(query);
return _peopleRepository.GetPeople(query).Items;
}
public IReadOnlyList<PersonInfo> GetPeople(BaseItem item)
@@ -2939,24 +3310,33 @@ namespace Emby.Server.Implementations.Library
return [];
}
public IReadOnlyList<Person> GetPeopleItems(InternalPeopleQuery query)
public QueryResult<BaseItem> GetPeopleItems(InternalPeopleQuery query)
{
return _peopleRepository.GetPeopleNames(query)
.Select(i =>
var queryResult = _peopleRepository.GetPeople(query);
var baseItems = queryResult.Items.Select(i =>
{
try
{
return GetPerson(i.Name);
}
catch (Exception ex)
{
_logger.LogError(ex, "error retrieving BaseItem for person: {0}", i.Name);
return null;
}
})
.Where(i => i is not null)
.Where(i => query.User is null || i!.IsVisible(query.User))
.OfType<BaseItem>()
.ToList()
.AsReadOnly();
return new QueryResult<BaseItem>
{
try
{
return GetPerson(i);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting person");
return null;
}
})
.Where(i => i is not null)
.Where(i => query.User is null || i!.IsVisible(query.User))
.ToList()!; // null values are filtered out
StartIndex = queryResult.StartIndex,
TotalRecordCount = queryResult.TotalRecordCount,
Items = baseItems,
};
}
public IReadOnlyList<string> GetPeopleNames(InternalPeopleQuery query)
@@ -3385,5 +3765,40 @@ namespace Emby.Server.Implementations.Library
_fileSystem.CreateShortcut(lnk, _appHost.ReverseVirtualPath(path));
RemoveContentTypeOverrides(path);
}
/// <inheritdoc />
public async Task RerouteLinkedChildReferencesAsync(Guid fromChildId, Guid toChildId)
{
var affectedParentIds = _linkedChildrenService.RerouteLinkedChildren(fromChildId, toChildId);
// Update in-memory LinkedChildren and re-save metadata (NFO) for affected parents
foreach (var parentId in affectedParentIds)
{
if (GetItemById(parentId) is Folder parent)
{
foreach (var lc in parent.LinkedChildren)
{
if (lc.ItemId.HasValue && lc.ItemId.Value.Equals(fromChildId))
{
lc.ItemId = toChildId;
}
}
await RunMetadataSavers(parent, ItemUpdateType.MetadataEdit).ConfigureAwait(false);
}
}
}
/// <inheritdoc />
public QueryFiltersLegacy GetQueryFiltersLegacy(InternalItemsQuery query)
{
if (query.User is not null)
{
AddUserToQuery(query, query.User);
}
SetTopParentOrAncestorIds(query);
return _itemRepository.GetQueryFiltersLegacy(query);
}
}
}

View File

@@ -23,6 +23,7 @@ using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding;
@@ -423,7 +424,7 @@ namespace Emby.Server.Implementations.Library
MediaStreamSelector.SetSubtitleStreamScores(source.MediaStreams, preferredSubs, user.SubtitleMode, audioLanguage);
}
private void SetDefaultAudioStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection)
private void SetDefaultAudioStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection, string originalLanguage)
{
if (userData is not null && userData.AudioStreamIndex.HasValue && user.RememberAudioSelections && allowRememberingSelection)
{
@@ -437,7 +438,42 @@ namespace Emby.Server.Implementations.Library
}
}
var preferredAudio = NormalizeLanguage(user.AudioLanguagePreference);
if (string.Equals(user.AudioLanguagePreference, "OriginalLanguage", StringComparison.OrdinalIgnoreCase))
{
originalLanguage = !string.IsNullOrWhiteSpace(originalLanguage)
? originalLanguage.Split(',').FirstOrDefault()
: null;
if (user.PlayDefaultAudioTrack)
{
source.DefaultAudioStreamIndex = MediaStreamSelector.GetDefaultAudioStreamIndex(
source.MediaStreams,
NormalizeLanguage(originalLanguage),
user.PlayDefaultAudioTrack);
return;
}
var originalIndex = source.MediaStreams.FindIndex(i => i.Type == MediaStreamType.Audio && i.IsOriginal);
if (!string.IsNullOrWhiteSpace(originalLanguage) && originalIndex != -1)
{
var mediaLanguageOriginal = source.MediaStreams[originalIndex].Language;
if (NormalizeLanguage(mediaLanguageOriginal).Contains(NormalizeLanguage(originalLanguage).FirstOrDefault()))
{
source.DefaultAudioStreamIndex = originalIndex;
return;
}
}
else if (originalIndex != -1)
{
source.DefaultAudioStreamIndex = originalIndex;
return;
}
}
var preferredAudio = string.Equals(user.AudioLanguagePreference, "OriginalLanguage", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(originalLanguage)
? NormalizeLanguage(originalLanguage)
: NormalizeLanguage(user.AudioLanguagePreference);
source.DefaultAudioStreamIndex = MediaStreamSelector.GetDefaultAudioStreamIndex(source.MediaStreams, preferredAudio, user.PlayDefaultAudioTrack);
if (user.PlayDefaultAudioTrack)
@@ -462,7 +498,19 @@ namespace Emby.Server.Implementations.Library
var allowRememberingSelection = item is null || item.EnableRememberingTrackSelections;
SetDefaultAudioStreamIndex(source, userData, user, allowRememberingSelection);
var originalLanguage = item?.OriginalLanguage ?? item switch
{
Episode episode => episode.Series.OriginalLanguage,
Video video => video.GetOwner() switch
{
Episode ownerEpisode => ownerEpisode.OriginalLanguage ?? ownerEpisode.Series.OriginalLanguage,
BaseItem owner => owner.OriginalLanguage,
null => null
},
_ => null
};
SetDefaultAudioStreamIndex(source, userData, user, allowRememberingSelection, originalLanguage);
SetDefaultSubtitleStreamIndex(source, userData, user, allowRememberingSelection);
}
else if (mediaType == MediaType.Audio)

View File

@@ -70,6 +70,16 @@ namespace Emby.Server.Implementations.Library
return match ? imdbId.ToString() : null;
}
// Allow tmdb as an alias for tmdbid
if (attribute.Equals("tmdbid", StringComparison.OrdinalIgnoreCase))
{
var tmdbValue = str.GetAttributeValue("tmdb");
if (tmdbValue is not null)
{
return tmdbValue;
}
}
return null;
}

View File

@@ -6,6 +6,7 @@ using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.IO;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library;
@@ -14,18 +15,22 @@ namespace Emby.Server.Implementations.Library;
/// </summary>
public class PathManager : IPathManager
{
private readonly ILogger<PathManager> _logger;
private readonly IServerConfigurationManager _config;
private readonly IApplicationPaths _appPaths;
/// <summary>
/// Initializes a new instance of the <see cref="PathManager"/> class.
/// </summary>
/// <param name="logger">The logger.</param>
/// <param name="config">The server configuration manager.</param>
/// <param name="appPaths">The application paths.</param>
public PathManager(
ILogger<PathManager> logger,
IServerConfigurationManager config,
IApplicationPaths appPaths)
{
_logger = logger;
_config = config;
_appPaths = appPaths;
}
@@ -35,31 +40,43 @@ public class PathManager : IPathManager
private string AttachmentCachePath => Path.Combine(_appPaths.DataPath, "attachments");
/// <inheritdoc />
public string GetAttachmentPath(string mediaSourceId, string fileName)
public string? GetAttachmentPath(string mediaSourceId, string fileName)
{
return Path.Combine(GetAttachmentFolderPath(mediaSourceId), fileName);
var folder = GetAttachmentFolderPath(mediaSourceId);
return folder is null ? null : Path.Combine(folder, fileName);
}
/// <inheritdoc />
public string GetAttachmentFolderPath(string mediaSourceId)
public string? GetAttachmentFolderPath(string mediaSourceId)
{
var id = Guid.Parse(mediaSourceId).ToString("D", CultureInfo.InvariantCulture).AsSpan();
if (!Guid.TryParse(mediaSourceId, out var parsed))
{
_logger.LogDebug("MediaSource Id '{MediaSourceId}' is not a GUID; no on-disk attachment folder.", mediaSourceId);
return null;
}
var id = parsed.ToString("D", CultureInfo.InvariantCulture).AsSpan();
return Path.Join(AttachmentCachePath, id[..2], id);
}
/// <inheritdoc />
public string GetSubtitleFolderPath(string mediaSourceId)
public string? GetSubtitleFolderPath(string mediaSourceId)
{
var id = Guid.Parse(mediaSourceId).ToString("D", CultureInfo.InvariantCulture).AsSpan();
if (!Guid.TryParse(mediaSourceId, out var parsed))
{
_logger.LogDebug("MediaSource Id '{MediaSourceId}' is not a GUID; no on-disk subtitle folder.", mediaSourceId);
return null;
}
var id = parsed.ToString("D", CultureInfo.InvariantCulture).AsSpan();
return Path.Join(SubtitleCachePath, id[..2], id);
}
/// <inheritdoc />
public string GetSubtitlePath(string mediaSourceId, int streamIndex, string extension)
public string? GetSubtitlePath(string mediaSourceId, int streamIndex, string extension)
{
return Path.Combine(GetSubtitleFolderPath(mediaSourceId), streamIndex.ToString(CultureInfo.InvariantCulture) + extension);
var folder = GetSubtitleFolderPath(mediaSourceId);
return folder is null ? null : Path.Combine(folder, streamIndex.ToString(CultureInfo.InvariantCulture) + extension);
}
/// <inheritdoc />
@@ -90,12 +107,23 @@ public class PathManager : IPathManager
public IReadOnlyList<string> GetExtractedDataPaths(BaseItem item)
{
var mediaSourceId = item.Id.ToString("N", CultureInfo.InvariantCulture);
return [
GetAttachmentFolderPath(mediaSourceId),
GetSubtitleFolderPath(mediaSourceId),
GetTrickplayDirectory(item, false),
GetTrickplayDirectory(item, true),
GetChapterImageFolderPath(item)
];
List<string> paths = [];
var attachmentFolder = GetAttachmentFolderPath(mediaSourceId);
if (attachmentFolder is not null)
{
paths.Add(attachmentFolder);
}
var subtitleFolder = GetSubtitleFolderPath(mediaSourceId);
if (subtitleFolder is not null)
{
paths.Add(subtitleFolder);
}
paths.Add(GetTrickplayDirectory(item, false));
paths.Add(GetTrickplayDirectory(item, true));
paths.Add(GetChapterImageFolderPath(item));
return paths;
}
}

View File

@@ -16,7 +16,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books
{
public class BookResolver : ItemResolver<Book>
{
private readonly string[] _validExtensions = { ".azw", ".azw3", ".cb7", ".cbr", ".cbt", ".cbz", ".epub", ".mobi", ".pdf", ".m4b", ".m4a", ".aac", ".flac", ".mp3", ".opus" };
private readonly string[] _validExtensions = { ".azw", ".azw3", ".cb7", ".cbr", ".cbt", ".cbz", ".epub", ".mobi", ".pdf" };
protected override Book Resolve(ItemResolveArgs args)
{

View File

@@ -1,8 +1,10 @@
#nullable disable
using System;
using System.IO;
using System.Linq;
using Emby.Naming.Common;
using Emby.Server.Implementations.Library;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
@@ -81,10 +83,34 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
episode.ParentIndexNumber = 1;
}
SetProviderIdFromPath(episode, args.Path);
return episode;
}
return null;
}
/// <summary>
/// Sets provider ids from the episode file name.
/// </summary>
/// <param name="item">The episode.</param>
/// <param name="path">The episode file path.</param>
private static void SetProviderIdFromPath(Episode item, string path)
{
var justName = Path.GetFileNameWithoutExtension(path.AsSpan());
var imdbId = justName.GetAttributeValue("imdbid");
item.TrySetProviderId(MetadataProvider.Imdb, imdbId);
var tvdbId = justName.GetAttributeValue("tvdbid");
item.TrySetProviderId(MetadataProvider.Tvdb, tvdbId);
var tvmazeId = justName.GetAttributeValue("tvmazeid");
item.TrySetProviderId(MetadataProvider.TvMaze, tvmazeId);
var tmdbId = justName.GetAttributeValue("tmdbid");
item.TrySetProviderId(MetadataProvider.Tmdb, tmdbId);
}
}
}

View File

@@ -1,10 +1,15 @@
#nullable disable
using System;
using System.Globalization;
using System.IO;
using System.Linq;
using Emby.Naming.Common;
using Emby.Naming.TV;
using Emby.Server.Implementations.Library;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
using Microsoft.Extensions.Logging;
@@ -77,6 +82,14 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
return null;
}
var hasAnyVideo = Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories)
.Any(file => _namingOptions.VideoFileExtensions.Contains(Path.GetExtension(file)));
if (!hasAnyVideo)
{
return null;
}
}
if (season.IndexNumber.HasValue && string.IsNullOrEmpty(season.Name))
@@ -91,10 +104,31 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
args.LibraryOptions.PreferredMetadataLanguage);
}
SetProviderIdFromPath(season, path);
return season;
}
return null;
}
/// <summary>
/// Sets provider ids from the season folder name.
/// </summary>
/// <param name="item">The season.</param>
/// <param name="path">The season folder path.</param>
private static void SetProviderIdFromPath(Season item, string path)
{
var justName = Path.GetFileName(path.AsSpan());
var tvdbId = justName.GetAttributeValue("tvdbid");
item.TrySetProviderId(MetadataProvider.Tvdb, tvdbId);
var tvmazeId = justName.GetAttributeValue("tvmazeid");
item.TrySetProviderId(MetadataProvider.TvMaze, tvmazeId);
var tmdbId = justName.GetAttributeValue("tmdbid");
item.TrySetProviderId(MetadataProvider.Tmdb, tmdbId);
}
}
}

View File

@@ -177,53 +177,74 @@ namespace Emby.Server.Implementations.Library
};
}
private UserItemData? GetUserData(User user, Guid itemId, List<string> keys)
/// <inheritdoc />
public Dictionary<Guid, UserItemData> GetUserDataBatch(IReadOnlyList<BaseItem> items, User user)
{
var cacheKey = GetCacheKey(user.InternalId, itemId);
var result = new Dictionary<Guid, UserItemData>(items.Count);
var itemsNeedingQuery = new List<(BaseItem Item, List<string> Keys)>();
if (_cache.TryGet(cacheKey, out var data))
foreach (var item in items)
{
return data;
}
data = GetUserDataInternal(user.Id, itemId, keys);
if (data is null)
{
return new UserItemData()
var cacheKey = GetCacheKey(user.InternalId, item.Id);
if (_cache.TryGet(cacheKey, out var cachedData))
{
Key = keys[0],
};
}
return _cache.GetOrAdd(cacheKey, _ => data);
}
private UserItemData? GetUserDataInternal(Guid userId, Guid itemId, List<string> keys)
{
if (keys.Count == 0)
{
return null;
}
using var context = _repository.CreateDbContext();
var userData = context.UserData.AsNoTracking().Where(e => e.ItemId == itemId && keys.Contains(e.CustomDataKey) && e.UserId.Equals(userId)).ToArray();
if (userData.Length > 0)
{
var directDataReference = userData.FirstOrDefault(e => e.CustomDataKey == itemId.ToString("N"));
if (directDataReference is not null)
{
return Map(directDataReference);
result[item.Id] = cachedData;
}
else
{
var userData = item.UserData?.Where(e => e.UserId.Equals(user.Id)).Select(Map).FirstOrDefault();
if (userData is not null)
{
result[item.Id] = userData;
_cache.AddOrUpdate(cacheKey, userData);
}
else
{
var keys = item.GetUserDataKeys();
itemsNeedingQuery.Add((item, keys));
}
}
return Map(userData.First());
}
return new UserItemData
if (itemsNeedingQuery.Count == 0)
{
Key = keys.Last()!
};
return result;
}
// Build a single query for all missing items
var allItemIds = itemsNeedingQuery.Select(x => x.Item.Id).ToList();
var allKeys = itemsNeedingQuery.SelectMany(x => x.Keys).Distinct().ToList();
if (allKeys.Count > 0)
{
using var context = _repository.CreateDbContext();
var userDataArray = context.UserData
.AsNoTracking()
.Where(e => e.UserId.Equals(user.Id))
.WhereOneOrMany(allItemIds, e => e.ItemId)
.WhereOneOrMany(allKeys, e => e.CustomDataKey)
.ToArray();
var userDataByItem = userDataArray.GroupBy(e => e.ItemId).ToDictionary(g => g.Key, g => g.ToArray());
foreach (var (item, keys) in itemsNeedingQuery)
{
UserItemData userData;
if (userDataByItem.TryGetValue(item.Id, out var itemUserData) && itemUserData.Length > 0)
{
var directDataReference = itemUserData.FirstOrDefault(e => e.CustomDataKey == item.Id.ToString("N"));
userData = directDataReference is not null ? Map(directDataReference) : Map(itemUserData.First());
}
else
{
userData = new UserItemData { Key = keys.Count > 0 ? keys[0] : string.Empty };
}
result[item.Id] = userData;
var cacheKey = GetCacheKey(user.InternalId, item.Id);
_cache.AddOrUpdate(cacheKey, userData);
}
}
return result;
}
/// <summary>

View File

@@ -59,8 +59,8 @@ namespace Emby.Server.Implementations.Library
var collectionFolder = folder as ICollectionFolder;
var folderViewType = collectionFolder?.CollectionType;
// Playlist library requires special handling because the folder only references user playlists
if (folderViewType == CollectionType.playlists)
// Playlist and BoxSet libraries require special handling because the folder only references linked items
if (folderViewType == CollectionType.playlists || folderViewType == CollectionType.boxsets)
{
var items = folder.GetItemList(new InternalItemsQuery(user)
{
@@ -138,7 +138,7 @@ namespace Emby.Server.Implementations.Library
list = list.Where(i => !user.GetPreferenceValues<Guid>(PreferenceKind.MyMediaExcludes).Contains(i.Id)).ToList();
}
var sorted = _libraryManager.Sort(list, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).ToList();
var sorted = _libraryManager.Sort(list, user, [ItemSortBy.SortName], SortOrder.Ascending).ToList();
var orders = user.GetPreferenceValues<Guid>(PreferenceKind.OrderedViews);
return list
@@ -205,7 +205,7 @@ namespace Emby.Server.Implementations.Library
var libraryItems = GetItemsForLatestItems(request.User, request, options);
var list = new List<Tuple<BaseItem, List<BaseItem>>>();
var containerIndexMap = new Dictionary<Guid, int>();
foreach (var item in libraryItems)
{
// Only grab the index container for media
@@ -213,20 +213,16 @@ namespace Emby.Server.Implementations.Library
if (container is null)
{
list.Add(new Tuple<BaseItem, List<BaseItem>>(null, new List<BaseItem> { item }));
list.Add(new Tuple<BaseItem, List<BaseItem>>(null!, new List<BaseItem> { item }));
}
else if (containerIndexMap.TryGetValue(container.Id, out var existingIndex))
{
list[existingIndex].Item2.Add(item);
}
else
{
var current = list.FirstOrDefault(i => i.Item1 is not null && i.Item1.Id.Equals(container.Id));
if (current is not null)
{
current.Item2.Add(item);
}
else
{
list.Add(new Tuple<BaseItem, List<BaseItem>>(container, new List<BaseItem> { item }));
}
containerIndexMap[container.Id] = list.Count;
list.Add(new Tuple<BaseItem, List<BaseItem>>(container, new List<BaseItem> { item }));
}
if (list.Count >= request.Limit)
@@ -255,7 +251,7 @@ namespace Emby.Server.Implementations.Library
return _channelManager.GetLatestChannelItemsInternal(
new InternalItemsQuery(user)
{
ChannelIds = new[] { parentId },
ChannelIds = [parentId],
IsPlayed = request.IsPlayed,
StartIndex = request.StartIndex,
Limit = request.Limit,
@@ -301,11 +297,11 @@ namespace Emby.Server.Implementations.Library
{
if (hasCollectionType.All(i => i.CollectionType == CollectionType.movies))
{
includeItemTypes = new[] { BaseItemKind.Movie };
includeItemTypes = [BaseItemKind.Movie];
}
else if (hasCollectionType.All(i => i.CollectionType == CollectionType.tvshows))
{
includeItemTypes = new[] { BaseItemKind.Episode };
includeItemTypes = [BaseItemKind.Episode];
}
}
}
@@ -344,29 +340,29 @@ namespace Emby.Server.Implementations.Library
}
var excludeItemTypes = includeItemTypes.Length == 0 && mediaTypes.Length == 0
? new[]
{
?
[
BaseItemKind.Person,
BaseItemKind.Studio,
BaseItemKind.Year,
BaseItemKind.MusicGenre,
BaseItemKind.Genre
}
]
: Array.Empty<BaseItemKind>();
var query = new InternalItemsQuery(user)
{
IncludeItemTypes = includeItemTypes,
OrderBy = new[]
{
OrderBy =
[
(ItemSortBy.DateCreated, SortOrder.Descending),
(ItemSortBy.SortName, SortOrder.Descending),
(ItemSortBy.ProductionYear, SortOrder.Descending)
},
],
IsFolder = includeItemTypes.Length == 0 ? false : null,
ExcludeItemTypes = excludeItemTypes,
IsVirtualItem = false,
Limit = limit * 5,
Limit = limit * 2,
IsPlayed = isPlayed,
DtoOptions = options,
MediaTypes = mediaTypes
@@ -394,6 +390,12 @@ namespace Emby.Server.Implementations.Library
query.Limit = limit;
return _libraryManager.GetLatestItemList(query, parents, CollectionType.music);
}
if (collectionType == CollectionType.movies)
{
query.Limit = limit;
return _libraryManager.GetLatestItemList(query, parents, CollectionType.movies);
}
}
return _libraryManager.GetItemList(query, parents);

View File

@@ -55,25 +55,35 @@ public class ArtistsValidator
IncludeItemTypes = [BaseItemKind.MusicArtist]
}).ToHashSet();
var existingArtists = _libraryManager.GetArtists(names);
var numComplete = 0;
var count = names.Count;
var refreshed = 0;
foreach (var name in names)
{
try
{
var item = _libraryManager.GetArtist(name);
MusicArtist? item = null;
if (existingArtists.TryGetValue(name, out var artists) && artists.Length > 0)
{
item = artists.OrderBy(i => i.IsAccessedByName ? 1 : 0).First();
}
// Fall back to GetArtist if not found (creates new item if needed)
item ??= _libraryManager.GetArtist(name);
var isNew = !existingArtistIds.Contains(item.Id);
var neverRefreshed = item.DateLastRefreshed == default;
if (isNew || neverRefreshed)
{
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
refreshed++;
}
}
catch (OperationCanceledException)
{
// Don't clutter the log
throw;
}
catch (Exception ex)
@@ -89,31 +99,24 @@ public class ArtistsValidator
progress.Report(percent);
}
_logger.LogInformation("Refreshed metadata for {RefreshedCount} new artists out of {TotalCount} total", refreshed, count);
var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = [BaseItemKind.MusicArtist],
IsDeadArtist = true,
IsLocked = false
}).Cast<MusicArtist>().ToList();
}).Cast<MusicArtist>()
.Where(item => item.IsAccessedByName)
.ToList();
foreach (var item in deadEntities)
{
if (!item.IsAccessedByName)
{
continue;
}
_logger.LogInformation("Deleting dead {ItemType} {ItemId} {ItemName}", item.GetType().Name, item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name);
_libraryManager.DeleteItem(
item,
new DeleteOptions
{
DeleteFileLocation = false
},
false);
}
_libraryManager.DeleteItemsUnsafeFast(deadEntities, deleteSourceFiles: true);
progress.Report(100);
}
}

View File

@@ -74,7 +74,7 @@ public class CollectionPostScanTask : ILibraryPostScanTask
foreach (var m in movies)
{
if (m is Movie movie && !string.IsNullOrEmpty(movie.CollectionName))
if (m is Movie movie && !string.IsNullOrEmpty(movie.CollectionName) && !movie.PrimaryVersionId.HasValue)
{
if (collectionNameMoviesMap.TryGetValue(movie.CollectionName, out var movieList))
{

View File

@@ -1,5 +1,6 @@
using System;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
@@ -48,17 +49,40 @@ public class GenresValidator
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
var names = _itemRepo.GetGenreNames();
var existingGenreIds = _libraryManager.GetItemIds(new InternalItemsQuery
{
IncludeItemTypes = [BaseItemKind.Genre]
}).ToHashSet();
var existingGenres = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = [BaseItemKind.Genre]
}).Cast<Genre>()
.GroupBy(g => g.Name, StringComparer.OrdinalIgnoreCase)
.ToDictionary(g => g.Key, g => g.First(), StringComparer.OrdinalIgnoreCase);
var numComplete = 0;
var count = names.Count;
var refreshed = 0;
foreach (var name in names)
{
try
{
var item = _libraryManager.GetGenre(name);
Genre? item = null;
if (existingGenres.TryGetValue(name, out var existingGenre))
{
item = existingGenre;
}
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
// Fall back to GetGenre if not found (creates new item if needed)
item ??= _libraryManager.GetGenre(name);
if (!existingGenreIds.Contains(item.Id))
{
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
refreshed++;
}
}
catch (OperationCanceledException)
{
@@ -78,6 +102,8 @@ public class GenresValidator
progress.Report(percent);
}
_logger.LogInformation("Refreshed metadata for {RefreshedCount} new genres out of {TotalCount} total", refreshed, count);
var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = [BaseItemKind.Genre, BaseItemKind.MusicGenre],
@@ -88,16 +114,10 @@ public class GenresValidator
foreach (var item in deadEntities)
{
_logger.LogInformation("Deleting dead {ItemType} {ItemId} {ItemName}", item.GetType().Name, item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name);
_libraryManager.DeleteItem(
item,
new DeleteOptions
{
DeleteFileLocation = false
},
false);
}
_libraryManager.DeleteItemsUnsafeFast(deadEntities, deleteSourceFiles: true);
progress.Report(100);
}
}

View File

@@ -1,6 +1,9 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using Microsoft.Extensions.Logging;
@@ -45,17 +48,25 @@ public class MusicGenresValidator
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
var names = _itemRepo.GetMusicGenreNames();
var existingMusicGenreIds = _libraryManager.GetItemIds(new InternalItemsQuery
{
IncludeItemTypes = [BaseItemKind.MusicGenre]
}).ToHashSet();
var numComplete = 0;
var count = names.Count;
var refreshed = 0;
foreach (var name in names)
{
try
{
var item = _libraryManager.GetMusicGenre(name);
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
if (!existingMusicGenreIds.Contains(item.Id))
{
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
refreshed++;
}
}
catch (OperationCanceledException)
{
@@ -75,6 +86,8 @@ public class MusicGenresValidator
progress.Report(percent);
}
_logger.LogInformation("Refreshed metadata for {RefreshedCount} new music genres out of {TotalCount} total", refreshed, count);
progress.Report(100);
}
}

View File

@@ -109,7 +109,7 @@ public class PeopleValidator
var i = 0;
foreach (var item in deadEntities.Chunk(500))
{
_libraryManager.DeleteItemsUnsafeFast(item);
_libraryManager.DeleteItemsUnsafeFast(item, true);
subProgress.Report(100f / deadEntities.Count * (i++ * 100));
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
@@ -49,17 +50,40 @@ public class StudiosValidator
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
var names = _itemRepo.GetStudioNames();
var existingStudioIds = _libraryManager.GetItemIds(new InternalItemsQuery
{
IncludeItemTypes = [BaseItemKind.Studio]
}).ToHashSet();
var existingStudios = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = [BaseItemKind.Studio]
}).Cast<Studio>()
.GroupBy(s => s.Name, StringComparer.OrdinalIgnoreCase)
.ToDictionary(g => g.Key, g => g.First(), StringComparer.OrdinalIgnoreCase);
var numComplete = 0;
var count = names.Count;
var refreshed = 0;
foreach (var name in names)
{
try
{
var item = _libraryManager.GetStudio(name);
Studio? item = null;
if (existingStudios.TryGetValue(name, out var existingStudio))
{
item = existingStudio;
}
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
// Fall back to GetStudio if not found (creates new item if needed)
item ??= _libraryManager.GetStudio(name);
if (!existingStudioIds.Contains(item.Id))
{
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
refreshed++;
}
}
catch (OperationCanceledException)
{
@@ -79,6 +103,8 @@ public class StudiosValidator
progress.Report(percent);
}
_logger.LogInformation("Refreshed metadata for {RefreshedCount} new studios out of {TotalCount} total", refreshed, count);
var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = [BaseItemKind.Studio],
@@ -89,16 +115,10 @@ public class StudiosValidator
foreach (var item in deadEntities)
{
_logger.LogInformation("Deleting dead {ItemType} {ItemId} {ItemName}", item.GetType().Name, item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name);
_libraryManager.DeleteItem(
item,
new DeleteOptions
{
DeleteFileLocation = false
},
false);
}
_libraryManager.DeleteItemsUnsafeFast(deadEntities, deleteSourceFiles: true);
progress.Report(100);
}
}

View File

@@ -128,8 +128,6 @@
"TaskRefreshTrickplayImagesDescription": "Skep fopspeel voorskou vir videos in aangeskakelde media versameling.",
"TaskAudioNormalizationDescription": "Skandeer lêers vir oudio-normaliseringsdata.",
"TaskAudioNormalization": "Odio Normalisering",
"TaskCleanCollectionsAndPlaylists": "Maak versamelings en snitlyste skoon",
"TaskCleanCollectionsAndPlaylistsDescription": "Verwyder items uit versamelings en snitlyste wat nie meer bestaan nie.",
"TaskDownloadMissingLyrics": "Laai tekorte lirieke af",
"TaskDownloadMissingLyricsDescription": "Laai lirieke af vir liedjies",
"TaskExtractMediaSegments": "Media Segment Skandeer",

View File

@@ -1,141 +1,140 @@
{
"Albums": "ألبومات",
"AppDeviceValues": "تطبيق: {0}, جهاز: {1}",
"Application": "تطبيق",
"Artists": "فنانون",
"AuthenticationSucceededWithUserName": "نجحت عملية التوثيق بـ {0}",
"Albums": "الألبومات",
"AppDeviceValues": "التطبيق: {0}، الجهاز: {1}",
"Application": "التطبيق",
"Artists": "الفنانون",
"AuthenticationSucceededWithUserName": "تمت مصادقة {0} بنجاح",
"Books": "الكتب",
"CameraImageUploadedFrom": ُفعت صورة الكاميرا الجديدة من {0}",
"CameraImageUploadedFrom": "تم رفع صورة كاميرا جديدة من {0}",
"Channels": "القنوات",
"ChapterNameValue": "الفصل {0}",
"Collections": "مجموعات",
"DeviceOfflineWithName": "قُطِع الاتصال ب{0}",
"Collections": "المجموعات",
"DeviceOfflineWithName": "انقطع اتصال {0}",
"DeviceOnlineWithName": "{0} متصل",
"FailedLoginAttemptWithUserName": "محاولة تسجيل الدخول فاشلة من {0}",
"FailedLoginAttemptWithUserName": "محاولة تسجيل دخول فاشلة من {0}",
"Favorites": "المفضلة",
"Folders": "المجلدات",
"Genres": "التصنيفات",
"HeaderAlbumArtists": "فناني الألبوم",
"Genres": "الأنواع",
"HeaderAlbumArtists": "فنانو الألبوم",
"HeaderContinueWatching": "متابعة المشاهدة",
"HeaderFavoriteAlbums": "الألبومات المفضلة",
"HeaderFavoriteArtists": "الفنانون المفضلون",
"HeaderFavoriteEpisodes": "الحلقات المفضلة",
"HeaderFavoriteShows": "المسلسلات المفضلة",
"HeaderFavoriteSongs": "الأغاني المفضلة",
"HeaderLiveTV": "التلفاز المباشر",
"HeaderLiveTV": "البث التلفزيوني المباشر",
"HeaderNextUp": "التالي",
"HeaderRecordingGroups": "مجموعات التسجيل",
"HomeVideos": "الفيديوهات الشخصية",
"Inherit": "توريث",
"ItemAddedWithName": "أُضيف {0} للمكتبة",
"ItemRemovedWithName": "أُزيل {0} من المكتبة",
"LabelIpAddressValue": "عنوان الآي بي: {0}",
"HomeVideos": "فيديوهات منزلية",
"Inherit": "وراثة",
"ItemAddedWithName": "تمت إضافة {0} إلى المكتبة",
"ItemRemovedWithName": "تمت إزالة {0} من المكتبة",
"LabelIpAddressValue": "عنوان IP: {0}",
"LabelRunningTimeValue": "مدة التشغيل: {0}",
"Latest": "الأحدث",
"MessageApplicationUpdated": "حُدث خادم Jellyfin",
"MessageApplicationUpdatedTo": "حُدث خادم Jellyfin إلى {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "حُدثت إعدادات الخادم في قسم {0}",
"MessageServerConfigurationUpdated": "حُدثت إعدادات الخادم",
"MessageApplicationUpdated": "تم تحديث خادم Jellyfin",
"MessageApplicationUpdatedTo": "تم تحديث خادم Jellyfin إلى {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "تم تحديث قسم إعدادات الخادم {0}",
"MessageServerConfigurationUpdated": "تم تحديث إعدادات الخادم",
"MixedContent": "محتوى مختلط",
"Movies": "الأفلام",
"Music": "الموسيقى",
"MusicVideos": "الفيديوهات الموسيقية",
"NameInstallFailed": "فشل تثبيت {0}",
"NameSeasonNumber": "الموسم {0}",
"NameSeasonUnknown": "الموسم غير معروف",
"NewVersionIsAvailable": "نسخة جديدة من خادم Jellyfin متوفرة للتحميل.",
"NotificationOptionApplicationUpdateAvailable": "يوجد تحديث للتطبيق",
"NotificationOptionApplicationUpdateInstalled": "نُصب تحديث التطبيق",
"NotificationOptionAudioPlayback": "بدأ تشغيل المقطع الصوتي",
"NotificationOptionAudioPlaybackStopped": "أُوقف تشغيل المقطع الصوتي",
"NotificationOptionCameraImageUploaded": ُفعت صورة الكاميرا",
"NotificationOptionInstallationFailed": "فشل في التثبيت",
"NotificationOptionNewLibraryContent": "أُضيف محتوى جديدا",
"NotificationOptionPluginError": "فشل في الملحق",
"NotificationOptionPluginInstalled": "ثُبتت الملحق",
"NameSeasonUnknown": "موسم غير معروف",
"NewVersionIsAvailable": "يتوفر إصدار جديد من خادم Jellyfin للتنزيل.",
"NotificationOptionApplicationUpdateAvailable": "تحديث التطبيق متاح",
"NotificationOptionApplicationUpdateInstalled": "تم تثبيت تحديث التطبيق",
"NotificationOptionAudioPlayback": "بدأ تشغيل الصوت",
"NotificationOptionAudioPlaybackStopped": "توقف تشغيل الصوت",
"NotificationOptionCameraImageUploaded": "تم رفع صورة كاميرا",
"NotificationOptionInstallationFailed": "فشل التثبيت",
"NotificationOptionNewLibraryContent": "تمت إضافة محتوى جديد",
"NotificationOptionPluginError": "خطأ في الملحق",
"NotificationOptionPluginInstalled": م تثبيت الملحق",
"NotificationOptionPluginUninstalled": "تمت إزالة الملحق",
"NotificationOptionPluginUpdateInstalled": "تم تثبيت تحديثات الملحق",
"NotificationOptionServerRestartRequired": "يجب إعادة تشغيل الخادم",
"NotificationOptionTaskFailed": "فشل في المهمة المجدولة",
"NotificationOptionUserLockedOut": "تم إقفال حساب المستخدم",
"NotificationOptionPluginUpdateInstalled": "تم تحديث الملحق",
"NotificationOptionServerRestartRequired": "مطلوب إعادة تشغيل الخادم",
"NotificationOptionTaskFailed": "فشل المهمة المجدولة",
"NotificationOptionUserLockedOut": "تم قفل حساب المستخدم",
"NotificationOptionVideoPlayback": "بدأ تشغيل الفيديو",
"NotificationOptionVideoPlaybackStopped": م إيقاف تشغيل الفيديو",
"NotificationOptionVideoPlaybackStopped": وقف تشغيل الفيديو",
"Photos": "الصور",
"Playlists": "قوائم التشغيل",
"Plugin": "الملحق",
"PluginInstalledWithName": "تم تثبيت {0}",
"PluginUninstalledWithName": "تمت إزالة {0}",
"PluginUpdatedWithName": "تم تحديث {0}",
"ProviderValue": "المزود: {0}",
"ScheduledTaskFailedWithName": "فشلت العملية {0}",
"ScheduledTaskStartedWithName": م بدء العملية {0}",
"ServerNameNeedsToBeRestarted": "يحتاج {0} لإعادة التشغيل",
"Shows": "العروض",
"ProviderValue": "المزوّد: {0}",
"ScheduledTaskFailedWithName": "فشلت {0}",
"ScheduledTaskStartedWithName": "بدأت {0}",
"ServerNameNeedsToBeRestarted": "يحتاج {0} إلى إعادة التشغيل",
"Shows": "المسلسلات",
"Songs": "الأغاني",
"StartupEmbyServerIsLoading": "يتم تحميل خادم Jellyfin . الرجاء المحاولة بعد قليل.",
"SubtitleDownloadFailureFromForItem": "فشل تحميل الترجمات من {0} ل {1}",
"StartupEmbyServerIsLoading": "يتم الآن تحميل خادم Jellyfin. يرجى المحاولة مرة أخرى بعد قليل.",
"SubtitleDownloadFailureFromForItem": "فشل تنزيل الترجمات من {0} لـ {1}",
"Sync": "مزامنة",
"System": "النظام",
"TvShows": "البرامج التلفزيونية",
"User": "المستخدم",
"UserCreatedWithName": "تم إنشاء المستخدم {0}",
"UserDeletedWithName": "تم حذف المستخدم {0}",
"UserDownloadingItemWithValues": "يقوم {0} بتنزيل {1}",
"UserLockedOutWithName": "تم منع المستخدم {0} من الدخول",
"UserOfflineFromDevice": "تم قطع اتصال {0} من {1}",
"UserOnlineFromDevice": "{0} متصل عبر {1}",
"UserPasswordChangedWithName": "تم تغيير كلمة السر للمستخدم {0}",
"UserPolicyUpdatedWithName": "تم تحديث سياسة المستخدم {0}",
"UserStartedPlayingItemWithValues": "قام {0} ببدء تشغيل {1} على {2}",
"UserStoppedPlayingItemWithValues": "قام {0} بإيقاف تشغيل {1} على {2}",
"ValueHasBeenAddedToLibrary": "تمت اضافت {0} إلى مكتبة الوسائط",
"ValueSpecialEpisodeName": "حلقة خاصه - {0}",
"UserDownloadingItemWithValues": "{0} يقوم بتنزيل {1}",
"UserLockedOutWithName": "تم قفل حساب المستخدم {0}",
"UserOfflineFromDevice": "انقطع اتصال {0} من {1}",
"UserOnlineFromDevice": "{0} متصل من {1}",
"UserPasswordChangedWithName": "تم تغيير كلمة المرور للمستخدم {0}",
"UserPolicyUpdatedWithName": "تم تحديث سياسة المستخدم لـ {0}",
"UserStartedPlayingItemWithValues": "{0} يقوم بتشغيل {1} على {2}",
"UserStoppedPlayingItemWithValues": "أنهى {0} تشغيل {1} على {2}",
"ValueHasBeenAddedToLibrary": "تمت إضافة {0} إلى مكتبة المحتوى الخاصة بك",
"ValueSpecialEpisodeName": "خاص - {0}",
"VersionNumber": "الإصدار {0}",
"TaskCleanCacheDescription": "يحذف الملفات المؤقتة التي لم يعد النظام بحاجة إليها.",
"TaskCleanCache": "حذف الملفات المؤقتة",
"TaskCleanCacheDescription": "يحذف ملفات ذاكرة التخزين المؤقت التي لم يعد النظام بحاجة إليها.",
"TaskCleanCache": "تنظيف مجلد ذاكرة التخزين المؤقت",
"TasksChannelsCategory": "قنوات الإنترنت",
"TasksLibraryCategory": "مكتبة",
"TasksMaintenanceCategory": "صيانة",
"TaskRefreshLibraryDescription": "يفحص مكتبة الوسائط الخاصة بك باحثا عن ملفات جديدة، ومن ثم يُحدث البيانات الوصفية.",
"TaskRefreshLibrary": "افحص مكتبة الوسائط",
"TaskRefreshChapterImagesDescription": ُنشئ صور مصغرة لمقاطع الفيديو التي تحتوي على فصول.",
"TaskRefreshChapterImages": "استخراج صور الفصل",
"TasksApplicationCategory": "تطبيق",
"TaskDownloadMissingSubtitlesDescription": "يبحث في الإنترنت على الترجمات الناقصة استنادا على البيانات الوصفية.",
"TaskDownloadMissingSubtitles": حميل الترجمات الناقصة",
"TaskRefreshChannelsDescription": "يحدث معلومات قنوات الإنترنت.",
"TaskRefreshChannels": "إعادة تحديث القنوات",
"TaskCleanTranscodeDescription": "يحذف ملفات الترميز الأقدم من يوم واحد.",
"TaskCleanTranscode": "حذف ما بمجلد الترميز",
"TaskUpdatePluginsDescription": "تحميل وتثبيت الإضافات التي تم تفعيل التحديث التلقائي لها.",
"TaskUpdatePlugins": "تحديث الإضافات",
"TaskRefreshPeopleDescription": قوم بتحديث البيانات الوصفية للممثلين والمخرجين في مكتبة الوسائط الخاصة بك.",
"TaskRefreshPeople": "إعادة تحميل الأشخاص",
"TaskCleanLogsDescription": "يحذف السجلات الأقدم من {0} يوم.",
"TaskCleanLogs": "حذف مسار السجل",
"TaskCleanActivityLogDescription": "يحذف سجل الأنشطة الأقدم من الوقت الذي تم تحديده.",
"TaskCleanActivityLog": "حذف سجل الأنشطة",
"Default": "افتراضي",
"Undefined": "غير معرف",
"Forced": "ملحقة",
"TaskOptimizeDatabaseDescription": "يضغط قاعدة البيانات ويقتطع المساحة الحرة. تشغيل هذه المهمة بعد فحص المكتبة أو إجراء تغييرات أخرى تتضمن تعديلات في قاعدة البيانات قد تؤدي إلى تحسين الأداء.",
"TasksLibraryCategory": "المكتبة",
"TasksMaintenanceCategory": "الصيانة",
"TaskRefreshLibraryDescription": "يفحص مكتبة المحتوى الخاصة بك بحثاً عن ملفات جديدة ويحدّث البيانات الوصفية.",
"TaskRefreshLibrary": "فحص مكتبة المحتوى",
"TaskRefreshChapterImagesDescription": "ينشئ صوراً مصغرة للفيديوهات التي تحتوي على فصول.",
"TaskRefreshChapterImages": "استخراج صور الفصول",
"TasksApplicationCategory": "التطبيق",
"TaskDownloadMissingSubtitlesDescription": "يبحث في الإنترنت عن الترجمات المفقودة بناءً على إعدادات البيانات الوصفية.",
"TaskDownloadMissingSubtitles": نزيل الترجمات المفقودة",
"TaskRefreshChannelsDescription": "يحدّث معلومات قنوات الإنترنت.",
"TaskRefreshChannels": "تحديث القنوات",
"TaskCleanTranscodeDescription": "يحذف ملفات تحويل الترميز التي مر عليها أكثر من يوم واحد.",
"TaskCleanTranscode": "تنظيف مجلد تحويل الترميز",
"TaskUpdatePluginsDescription": نزّل ويثبّت التحديثات للملحقات المهيأة للتحديث التلقائي.",
"TaskUpdatePlugins": "تحديث الملحقات",
"TaskRefreshPeopleDescription": "يحدّث البيانات الوصفية للممثلين والمخرجين في مكتبة المحتوى الخاصة بك.",
"TaskRefreshPeople": "تحديث الأشخاص",
"TaskCleanLogsDescription": "يحذف ملفات السجل التي يزيد عمرها عن {0} أيام.",
"TaskCleanLogs": "تنظيف مجلد السجلات",
"TaskCleanActivityLogDescription": "يحذف إدخالات سجل النشاط الأقدم من العمر المحدد.",
"TaskCleanActivityLog": "تنظيف سجل النشاط",
"Default": "الافتراضي",
"Undefined": "غير محدد",
"Forced": "إجباري",
"TaskOptimizeDatabaseDescription": "يضغط قاعدة البيانات ويقلل المساحة الحرة. قد يؤدي تشغيل هذه المهمة بعد فحص المكتبة أو إجراء تغييرات أخرى تتضمن تعديلات على قاعدة البيانات إلى تحسين الأداء.",
"TaskOptimizeDatabase": "تحسين قاعدة البيانات",
"TaskKeyframeExtractorDescription": "يستخرج الإطارات الرئيسية من ملفات الفيديو لكي ينشئ قوائم تشغيل بث HTTP المباشر. قد تستمر هذه العملية لوقت طويل.",
"TaskKeyframeExtractor": "مستخرج الإطار الرئيسي",
"TaskKeyframeExtractorDescription": "يستخرج الإطارات الرئيسية من ملفات الفيديو لإنشاء قوائم تشغيل HLS أكثر دقة. قد يستغرق تشغيل هذه المهمة وقتاً طويلاً.",
"TaskKeyframeExtractor": "مستخرج الإطارات الرئيسية",
"External": "خارجي",
"HearingImpaired": "ضعاف السمع",
"TaskRefreshTrickplayImages": "توليد صور المعاينة السريعة",
"TaskRefreshTrickplayImagesDescription": ُولّد معاينات تنقل سريع لمقاطع الفيديو ضمن المكتبات المفعّلة.",
"TaskCleanCollectionsAndPlaylists": "حذف المجموعات وقوائم التشغيل",
"TaskCleanCollectionsAndPlaylistsDescription": "حذف عناصر من المجموعات وقوائم التشغيل التي لم تعد موجودة.",
"TaskAudioNormalization": "تسوية الصوت",
"TaskAudioNormalizationDescription": "مسح الملفات لتطبيع بيانات الصوت.",
"TaskDownloadMissingLyrics": "تنزيل عبارات القصيدة",
"TaskDownloadMissingLyricsDescription": "كلمات",
"TaskExtractMediaSegments": "فحص مقاطع الوسائط",
"TaskExtractMediaSegmentsDescription": ستخرج مقاطع وسائط من إضافات MediaSegment المُفعّلة.",
"TaskMoveTrickplayImages": "تغيير مكان صور المعاينة السريعة",
"TaskMoveTrickplayImagesDescription": "تُنقل ملفات التشغيل السريع الحالية بناءً على إعدادات المكتبة.",
"HearingImpaired": "لضعاف السمع",
"TaskRefreshTrickplayImages": "إنشاء صور معاينات التنقل (Trickplay)",
"TaskRefreshTrickplayImagesDescription": نشئ صور معاينات التنقل السريع للفيديوهات في المكتبات المفعّلة.",
"TaskAudioNormalization": "تطبيع الصوت",
"TaskAudioNormalizationDescription": "يفحص الملفات لجمع بيانات تطبيع الصوت.",
"TaskDownloadMissingLyrics": "تنزيل الكلمات المفقودة",
"TaskDownloadMissingLyricsDescription": "ينزّل الكلمات للأغاني.",
"TaskExtractMediaSegments": "فحص مقاطع المحتوى",
"TaskExtractMediaSegmentsDescription": "يستخرج أو يحصل على مقاطع المحتوى من الملحقات المفعّلة لمقاطع المحتوى (MediaSegment).",
"TaskMoveTrickplayImages": "نقل موقع صور معاينات التنقل",
"TaskMoveTrickplayImagesDescription": نقل ملفات معاينات التنقل الحالية وفقاً لإعدادات المكتبة.",
"CleanupUserDataTask": "مهمة تنظيف بيانات المستخدم",
"CleanupUserDataTaskDescription": "مسح جميع بيانات المستخدم (حالة المشاهدة، والحالة المفضلة وما إلى ذلك) من الوسائط التي لم تعد موجودة لمدة 90 يومًا على الأقل."
"CleanupUserDataTaskDescription": "ينظف جميع بيانات المستخدم (مثل حالة المشاهدة وحالة المفضلة وغيرها) للمحتوى الذي لم يعد موجوداً لمدة 90 يوماً على الأقل.",
"Original": "فريد"
}

View File

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

View File

@@ -126,8 +126,6 @@
"TaskKeyframeExtractorDescription": "Выдае ключавыя кадры з відэафайлаў для стварэння больш дакладных плэй-лістоў HLS. Гэта задача можа выконвацца доўга.",
"TaskRefreshTrickplayImages": "Стварыць выявы Trickplay",
"TaskRefreshTrickplayImagesDescription": "Стварае перадпрагляды відэаролікаў для Trickplay у падключаных бібліятэках.",
"TaskCleanCollectionsAndPlaylists": "Ачысціць калекцыі і плэй-лісты",
"TaskCleanCollectionsAndPlaylistsDescription": "Выдаляе элементы з калекцый і плэй-лістоў, якія больш не існуюць.",
"TaskAudioNormalizationDescription": "Скануе файлы на прадмет нармалізацыі гуку.",
"TaskAudioNormalization": "Нармалізацыя гуку",
"TaskExtractMediaSegmentsDescription": "Выдае або атрымлівае медыясегменты з убудоў з падтрымкай MediaSegment.",

View File

@@ -128,8 +128,6 @@
"TaskRefreshTrickplayImagesDescription": "Създава прегледи на Trickplay за видеа в активирани библиотеки.",
"TaskDownloadMissingLyrics": "Свали липсващи текстове",
"TaskDownloadMissingLyricsDescription": "Свали текстове за песни",
"TaskCleanCollectionsAndPlaylists": "Изчисти колекциите и плейлистите",
"TaskCleanCollectionsAndPlaylistsDescription": "Премахни несъществуващи файлове в колекциите и плейлистите.",
"TaskAudioNormalization": "Нормализиране на звука",
"TaskAudioNormalizationDescription": "Сканирай файловете за нормализация на звука.",
"TaskExtractMediaSegmentsDescription": "Изважда медиини сегменти от MediaSegment плъгини.",

View File

@@ -127,8 +127,6 @@
"TaskRefreshTrickplayImages": "ট্রিকপ্লে ইমেজ তৈরি",
"TaskRefreshTrickplayImagesDescription": "সক্ষম লাইব্রেরিতে ভিডিওর জন্য ট্রিকপ্লে প্রিভিউ তৈরি করে।",
"TaskDownloadMissingLyricsDescription": "গানের জন্য লিরিকস ডাউনলোড করুন",
"TaskCleanCollectionsAndPlaylists": "কালেকশন এবং প্লেলিস্ট পরিষ্কার করুন",
"TaskCleanCollectionsAndPlaylistsDescription": "কালেকশন এবং প্লেলিস্ট থেকে আইটেমগুলি সরিয়ে দেয় যা আর বিদ্যমান নেই।",
"TaskExtractMediaSegments": "মিডিয়া সেগমেন্ট স্ক্যান",
"TaskExtractMediaSegmentsDescription": "মিডিয়া সেগমেন্ট সক্ষম প্লাগইনগুলি থেকে মিডিয়া সেগমেন্ট বের করে বা অর্জন করে।",
"TaskDownloadMissingLyrics": "অনুপস্থিত গান ডাউনলোড করুন",

View File

@@ -130,8 +130,6 @@
"TaskOptimizeDatabaseDescription": "Komprimira bazu podataka i čisti slobodan prostor. Pokretanje ovog zadatka nakon skeniranja biblioteke ili izvođenja drugih promjena koje podrazumijevaju izmjene baze podataka može poboljšati performanse.",
"TaskKeyframeExtractor": "Izvađač ključnih sličica",
"TaskKeyframeExtractorDescription": "Izvlači ključne okvire iz video datoteka kako bi kreirao preciznije HLS playliste. Ovaj zadatak može trajati dugo.",
"TaskCleanCollectionsAndPlaylists": "Očistite kolekcije i playliste",
"TaskCleanCollectionsAndPlaylistsDescription": "Uklanja stavke iz kolekcija i playlista koje više ne postoje.",
"TaskExtractMediaSegments": "Analiza medijskog segmenta",
"TaskExtractMediaSegmentsDescription": "Izvlači ili dobija medijske segmente iz dodataka koji podržavaju MediaSegment.",
"TaskMoveTrickplayImages": "Migracija lokacije slike Trickplay",

View File

@@ -126,8 +126,6 @@
"HearingImpaired": "Discapacitat auditiva",
"TaskRefreshTrickplayImages": "Generació d'imatges de previsualització",
"TaskRefreshTrickplayImagesDescription": "Creació d'imatges de previsualització per a vídeos en les mediateques habilitades.",
"TaskCleanCollectionsAndPlaylistsDescription": "Esborra elements de col·leccions i llistes de reproducció que ja no existeixen.",
"TaskCleanCollectionsAndPlaylists": "Neteja de les col·leccions i llistes de reproducció",
"TaskAudioNormalization": "Estabilització de l'àudio",
"TaskAudioNormalizationDescription": "Escaneja els fitxer per a obtenir dades de normalització de l'àudio.",
"TaskDownloadMissingLyricsDescription": "Descàrrega de les lletres de les cançons",

View File

@@ -126,8 +126,6 @@
"HearingImpaired": "Sluchově postižení",
"TaskRefreshTrickplayImages": "Generovat obrázky pro Trickplay",
"TaskRefreshTrickplayImagesDescription": "Obrázky Trickplay se používají k zobrazení náhledů u videí v knihovnách, kde je to povoleno.",
"TaskCleanCollectionsAndPlaylists": "Pročistit kolekce a seznamy přehrávání",
"TaskCleanCollectionsAndPlaylistsDescription": "Odstraní neexistující položky z kolekcí a seznamů přehrávání.",
"TaskAudioNormalization": "Normalizace zvuku",
"TaskAudioNormalizationDescription": "Skenovat soubory za účelem normalizace zvuku.",
"TaskDownloadMissingLyrics": "Stáhnout chybějící texty k písni",
@@ -137,5 +135,6 @@
"TaskMoveTrickplayImages": "Přesunout úložiště obrázků Trickplay",
"TaskMoveTrickplayImagesDescription": "Přesune existující soubory Trickplay podle nastavení knihovny.",
"CleanupUserDataTaskDescription": "Odstraní všechna uživatelská data (stav zhlédnutí, oblíbené atd.) z médií, které již neexistují více než 90 dní.",
"CleanupUserDataTask": "Pročistit uživatelská data"
"CleanupUserDataTask": "Pročistit uživatelská data",
"Original": "Originál"
}

View File

@@ -130,7 +130,5 @@
"TaskRefreshTrickplayImagesDescription": "Creu rhagolygon Trickplay ar gyfer fideos mewn llyfrgelloedd gweithredol.",
"TaskDownloadMissingLyrics": "Lawrlwytho geiriau coll",
"TaskDownloadMissingLyricsDescription": "Lawrlwytho geiriau caneuon",
"TaskCleanCollectionsAndPlaylists": "Glanhau casgliadau a rhestrau chwarae",
"TaskCleanCollectionsAndPlaylistsDescription": "Dileu eitemau o gasgliadau a rhestrau chwarae sydd ddim yn bodoli bellach.",
"TaskExtractMediaSegments": "Sganio Darnau Cyfryngau"
}

View File

@@ -126,8 +126,6 @@
"HearingImpaired": "Hørehæmmet",
"TaskRefreshTrickplayImages": "Generer trickplay-billeder",
"TaskRefreshTrickplayImagesDescription": "Laver trickplay-billeder for videoer i aktiverede biblioteker.",
"TaskCleanCollectionsAndPlaylists": "Ryd op i samlinger og afspilningslister",
"TaskCleanCollectionsAndPlaylistsDescription": "Fjerner elementer fra samlinger og afspilningslister der ikke eksisterer længere.",
"TaskAudioNormalizationDescription": "Skanner filer for data vedrørende lydnormalisering.",
"TaskAudioNormalization": "Lydnormalisering",
"TaskDownloadMissingLyricsDescription": "Søger på internettet efter manglende sangtekster baseret på metadata-konfigurationen",

View File

@@ -126,8 +126,6 @@
"HearingImpaired": "Hörgeschädigt",
"TaskRefreshTrickplayImages": "Trickplay-Bilder generieren",
"TaskRefreshTrickplayImagesDescription": "Erstellt ein Trickplay-Vorschauen für Videos in aktivierten Bibliotheken.",
"TaskCleanCollectionsAndPlaylists": "Sammlungen und Playlisten aufräumen",
"TaskCleanCollectionsAndPlaylistsDescription": "Löscht nicht mehr vorhandene Einträge aus den Sammlungen und Playlisten.",
"TaskAudioNormalization": "Audio Normalisierung",
"TaskAudioNormalizationDescription": "Durchsucht Dateien nach Audionormalisierungsdaten.",
"TaskDownloadMissingLyricsDescription": "Lädt Songtexte herunter",
@@ -137,5 +135,6 @@
"TaskMoveTrickplayImages": "Verzeichnis für Trickplay-Bilder migrieren",
"TaskMoveTrickplayImagesDescription": "Trickplay-Bilder werden entsprechend der Bibliothekseinstellungen verschoben.",
"CleanupUserDataTask": "Aufgabe zur Bereinigung von Benutzerdaten",
"CleanupUserDataTaskDescription": "Löscht alle Benutzerdaten (Abspielstatus, Favoritenstatus, usw.) von Medien, die seit mindestens 90 Tagen nicht mehr vorhanden sind."
"CleanupUserDataTaskDescription": "Löscht alle Benutzerdaten (Abspielstatus, Favoritenstatus, usw.) von Medien, die seit mindestens 90 Tagen nicht mehr vorhanden sind.",
"Original": "Original"
}

View File

@@ -128,8 +128,6 @@
"TaskRefreshTrickplayImagesDescription": "Δημιουργεί προεπισκοπήσεις trickplay για βίντεο σε ενεργοποιημένες βιβλιοθήκες.",
"TaskAudioNormalization": "Ομοιομορφία ήχου",
"TaskAudioNormalizationDescription": "Ανίχνευση αρχείων για δεδομένα ομοιομορφίας ήχου.",
"TaskCleanCollectionsAndPlaylists": "Καθαρισμός συλλογών και λιστών αναπαραγωγής",
"TaskCleanCollectionsAndPlaylistsDescription": "Αφαιρούνται στοιχεία από τις συλλογές και τις λίστες αναπαραγωγής που δεν υπάρχουν πλέον.",
"TaskMoveTrickplayImages": "Αλλαγή τοποθεσίας εικόνων Trickplay",
"TaskDownloadMissingLyrics": "Λήψη στίχων που λείπουν",
"TaskMoveTrickplayImagesDescription": "Μετακινεί τα υπάρχοντα αρχεία trickplay σύμφωνα με τις ρυθμίσεις της βιβλιοθήκης.",

View File

@@ -126,8 +126,6 @@
"HearingImpaired": "Hearing Impaired",
"TaskRefreshTrickplayImages": "Generate Trickplay Images",
"TaskRefreshTrickplayImagesDescription": "Creates trickplay previews for videos in enabled libraries.",
"TaskCleanCollectionsAndPlaylists": "Clean up collections and playlists",
"TaskCleanCollectionsAndPlaylistsDescription": "Removes items from collections and playlists that no longer exist.",
"TaskAudioNormalization": "Audio Normalisation",
"TaskAudioNormalizationDescription": "Scans files for audio normalisation data.",
"TaskDownloadMissingLyrics": "Download missing lyrics",

View File

@@ -64,6 +64,7 @@
"NotificationOptionUserLockedOut": "User locked out",
"NotificationOptionVideoPlayback": "Video playback started",
"NotificationOptionVideoPlaybackStopped": "Video playback stopped",
"Original": "Original",
"Photos": "Photos",
"Playlists": "Playlists",
"Plugin": "Plugin",
@@ -130,8 +131,6 @@
"TaskOptimizeDatabaseDescription": "Compacts database and truncates free space. Running this task after scanning the library or doing other changes that imply database modifications might improve performance.",
"TaskKeyframeExtractor": "Keyframe Extractor",
"TaskKeyframeExtractorDescription": "Extracts keyframes from video files to create more precise HLS playlists. This task may run for a long time.",
"TaskCleanCollectionsAndPlaylists": "Clean up collections and playlists",
"TaskCleanCollectionsAndPlaylistsDescription": "Removes items from collections and playlists that no longer exist.",
"TaskExtractMediaSegments": "Media Segment Scan",
"TaskExtractMediaSegmentsDescription": "Extracts or obtains media segments from MediaSegment enabled plugins.",
"TaskMoveTrickplayImages": "Migrate Trickplay Image Location",

View File

@@ -128,8 +128,6 @@
"TaskRefreshTrickplayImagesDescription": "Crea vistas previas de reproducción engañosa para videos en bibliotecas habilitadas.",
"TaskAudioNormalization": "Normalización de audio",
"TaskAudioNormalizationDescription": "Escanea archivos en busca de datos de normalización de audio.",
"TaskCleanCollectionsAndPlaylists": "Limpiar colecciones y listas de reproducción",
"TaskCleanCollectionsAndPlaylistsDescription": "Elimina elementos de colecciones y listas de reproducción que ya no existen.",
"TaskDownloadMissingLyrics": "Descargar letra faltante",
"TaskDownloadMissingLyricsDescription": "Descarga letras de canciones",
"TaskExtractMediaSegments": "Escanear Segmentos de Media",

View File

@@ -128,8 +128,6 @@
"TaskRefreshTrickplayImages": "Generar imágenes de la barra de reproducción",
"TaskAudioNormalization": "Normalización de audio",
"TaskAudioNormalizationDescription": "Analiza los archivos para normalizar el audio.",
"TaskCleanCollectionsAndPlaylists": "Limpieza de colecciones y listas de reproducción",
"TaskCleanCollectionsAndPlaylistsDescription": "Quita elementos que ya no existen de colecciones y listas de reproducción.",
"TaskDownloadMissingLyrics": "descargar letras que faltan",
"TaskDownloadMissingLyricsDescription": "Descargar letras de canciones",
"TaskExtractMediaSegments": "Escaneo de segmentos de medios",

View File

@@ -126,8 +126,6 @@
"HearingImpaired": "Discapacidad Auditiva",
"TaskRefreshTrickplayImages": "Generar miniaturas de línea de tiempo",
"TaskRefreshTrickplayImagesDescription": "Crear miniaturas de tiempo para videos en las librerías habilitadas.",
"TaskCleanCollectionsAndPlaylists": "Limpiar colecciones y listas de reproducción",
"TaskCleanCollectionsAndPlaylistsDescription": "Elimina elementos de colecciones y listas de reproducción que ya no existen.",
"TaskAudioNormalization": "Normalización de audio",
"TaskAudioNormalizationDescription": "Escanear archivos para obtener datos de normalización.",
"TaskDownloadMissingLyricsDescription": "Descargar letras para las canciones",
@@ -137,5 +135,6 @@
"TaskExtractMediaSegmentsDescription": "Extrae u obtiene segmentos de medios de plugins habilitados para MediaSegment.",
"TaskMoveTrickplayImages": "Migrar la ubicación de la imagen de Trickplay",
"CleanupUserDataTask": "Tarea de limpieza de datos del usuario",
"CleanupUserDataTaskDescription": "Limpia todos los datos del usuario (estado de visualización, favoritos, etc.) de los medios que ya no están disponibles desde hace al menos 90 días."
"CleanupUserDataTaskDescription": "Limpia todos los datos del usuario (estado de visualización, favoritos, etc.) de los medios que ya no están disponibles desde hace al menos 90 días.",
"Original": "Original"
}

View File

@@ -127,9 +127,7 @@
"TaskRefreshTrickplayImagesDescription": "Crea previsualizaciones para la barra de reproducción en las bibliotecas habilitadas.",
"TaskRefreshTrickplayImages": "Generar imágenes de la barra de reproducción",
"TaskAudioNormalization": "Normalización de audio",
"TaskCleanCollectionsAndPlaylistsDescription": "Quita elementos que ya no existen de colecciones y listas de reproducción.",
"TaskAudioNormalizationDescription": "Analiza los archivos para normalizar el audio.",
"TaskCleanCollectionsAndPlaylists": "Limpieza de colecciones y listas de reproducción",
"TaskDownloadMissingLyrics": "Descargar letra faltante",
"TaskDownloadMissingLyricsDescription": "Descarga letras de canciones",
"TaskExtractMediaSegmentsDescription": "Extrae u obtiene segmentos de medios de complementos habilitados para MediaSegment.",

View File

@@ -41,8 +41,6 @@
"TaskKeyframeExtractorDescription": "Extrae Fotogramas Clave de los archivos de vídeo para crear Listas de Reproducción HLS más precisas. Esta tarea puede durar mucho tiempo.",
"TaskAudioNormalization": "Normalización de audio",
"TaskAudioNormalizationDescription": "Escanear archivos para la normalización de data.",
"TaskCleanCollectionsAndPlaylists": "Limpiar colecciones y listas de reproducción",
"TaskCleanCollectionsAndPlaylistsDescription": "Remover elementos de colecciones y listas de reproducción que no existen.",
"TvShows": "Series de TV",
"UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}",
"TaskRefreshChannels": "Actualizar canales",

View File

@@ -128,8 +128,6 @@
"TaskRefreshTrickplayImagesDescription": "Loob trickplay eelvaated videotele lubatud meediakogudes.",
"TaskAudioNormalization": "Normaliseeri helitugevus",
"TaskAudioNormalizationDescription": "Otsib failidest helitugevuse normaliseerimise teavet.",
"TaskCleanCollectionsAndPlaylistsDescription": "Eemaldab kogumikest ja esitusloenditest üksused, mida enam ei eksisteeri.",
"TaskCleanCollectionsAndPlaylists": "Puhasta kogumikud ja esitusloendid",
"TaskDownloadMissingLyrics": "Hangi puuduvad laulusõnad",
"TaskDownloadMissingLyricsDescription": "Laulusõnade allalaadimine",
"TaskMoveTrickplayImagesDescription": "Liigutab trickplay pildid meediakogu sätete kohaselt.",

View File

@@ -130,8 +130,6 @@
"TaskDownloadMissingLyrics": "Deskargatu falta diren letrak",
"TaskDownloadMissingLyricsDescription": "Deskargatu abestientzako letrak",
"TaskExtractMediaSegments": "Multimedia segmentuen eskaneoa",
"TaskCleanCollectionsAndPlaylistsDescription": "Jada existitzen ez diren bildumak eta erreprodukzio-zerrendak kentzen ditu.",
"TaskCleanCollectionsAndPlaylists": "Garbitu bildumak eta erreprodukzio-zerrendak",
"TaskExtractMediaSegmentsDescription": "Media segmentuak atera edo lortzen ditu MediaSegment gaituta duten pluginetik.",
"TaskMoveTrickplayImages": "Aldatu Trickplay irudien kokalekua",
"TaskMoveTrickplayImagesDescription": "Lehendik dauden trickplay fitxategiak liburutegiaren ezarpenen arabera mugitzen dira.",

View File

@@ -126,8 +126,6 @@
"HearingImpaired": "مشکل شنوایی",
"TaskRefreshTrickplayImages": "تولید تصاویر Trickplay",
"TaskRefreshTrickplayImagesDescription": "تولید پیش‌نمایش های trickplay برای ویدیو های فعال شده در کتابخانه.",
"TaskCleanCollectionsAndPlaylists": "پاکسازی مجموعه ها و لیست پخش",
"TaskCleanCollectionsAndPlaylistsDescription": "موارد را از مجموعه ها و لیست پخش هایی که دیگر وجود ندارند حذف میکند.",
"TaskAudioNormalizationDescription": "بررسی فایل برای داده‌های نرمال کردن صدا.",
"TaskDownloadMissingLyrics": "دانلود متن‌های ناموجود",
"TaskDownloadMissingLyricsDescription": "دانلود متن شعر‌ها",

View File

@@ -126,8 +126,6 @@
"HearingImpaired": "Kuulorajoitteinen",
"TaskRefreshTrickplayImages": "Luo Trickplay-kuvat",
"TaskRefreshTrickplayImagesDescription": "Luo Trickplay-esikatselut käytössä olevien kirjastojen videoista.",
"TaskCleanCollectionsAndPlaylistsDescription": "Poistaa kohteet kokoelmista ja soittolistoista joita ei ole enää olemassa.",
"TaskCleanCollectionsAndPlaylists": "Puhdista kokoelmat ja soittolistat",
"TaskAudioNormalization": "Äänenvoimakkuuden normalisointi",
"TaskAudioNormalizationDescription": "Etsii tiedostoista äänenvoimakkuuden normalisointitietoja.",
"TaskDownloadMissingLyrics": "Lataa puuttuva lyriikka",

View File

@@ -126,8 +126,6 @@
"HearingImpaired": "Malentendants",
"TaskRefreshTrickplayImages": "Générer des images Trickplay",
"TaskRefreshTrickplayImagesDescription": "Crée des aperçus Trickplay pour les vidéos dans les médiathèques activées.",
"TaskCleanCollectionsAndPlaylists": "Nettoyer les collections et les listes de lecture",
"TaskCleanCollectionsAndPlaylistsDescription": "Supprime les éléments des collections et des listes de lecture qui n'existent plus.",
"TaskAudioNormalization": "Normalisation audio",
"TaskAudioNormalizationDescription": "Analyse les fichiers à la recherche de données de normalisation audio.",
"TaskExtractMediaSegments": "Analyse des segments de média",

View File

@@ -126,8 +126,6 @@
"HearingImpaired": "Malentendants",
"TaskRefreshTrickplayImages": "Générer des images Trickplay",
"TaskRefreshTrickplayImagesDescription": "Crée des aperçus Trickplay pour les vidéos dans les médiathèques activées.",
"TaskCleanCollectionsAndPlaylists": "Nettoyer les collections et les listes de lecture",
"TaskCleanCollectionsAndPlaylistsDescription": "Supprime les éléments des collections et des listes de lecture qui n'existent plus.",
"TaskAudioNormalization": "Normalisation audio",
"TaskAudioNormalizationDescription": "Analyse les fichiers à la recherche de données de normalisation audio.",
"TaskDownloadMissingLyricsDescription": "Téléchargement des paroles des chansons",

View File

@@ -29,12 +29,10 @@
"TaskRefreshChannelsDescription": "Athnuachan eolas faoi chainéil idirlín.",
"TaskOptimizeDatabase": "Bunachar sonraí a bharrfheabhsú",
"TaskKeyframeExtractorDescription": "Baintear eochairfhrámaí as comhaid físe chun seinmliostaí HLS níos cruinne a chruthú. Féadfaidh an tasc seo a bheith ar siúl ar feadh i bhfad.",
"TaskCleanCollectionsAndPlaylistsDescription": "Baintear míreanna as bailiúcháin agus seinmliostaí nach ann dóibh a thuilleadh.",
"TaskDownloadMissingLyricsDescription": "Íosluchtaigh liricí do na hamhráin",
"TaskUpdatePluginsDescription": "Íoslódálann agus suiteálann nuashonruithe do bhreiseáin atá cumraithe le nuashonrú go huathoibríoch.",
"TaskDownloadMissingSubtitlesDescription": "Déanann sé cuardach ar an idirlíon le haghaidh fotheidil atá ar iarraidh bunaithe ar chumraíocht meiteashonraí.",
"TaskExtractMediaSegmentsDescription": "Sliocht nó faigheann codanna meán ó bhreiseáin chumasaithe MediaSegment.",
"TaskCleanCollectionsAndPlaylists": "Glan suas bailiúcháin agus seinmliostaí",
"TaskOptimizeDatabaseDescription": "Comhdhlúthaíonn bunachar sonraí agus gearrtar spás saor in aisce. Má ritheann tú an tasc seo tar éis scanadh a dhéanamh ar an leabharlann nó athruithe eile a dhéanamh a thugann le tuiscint gur cheart go bhfeabhsófaí an fheidhmíocht.",
"TaskMoveTrickplayImagesDescription": "Bogtar comhaid trickplay atá ann cheana de réir socruithe na leabharlainne.",
"AppDeviceValues": "Aip: {0}, Gléas: {1}",
@@ -137,5 +135,6 @@
"TaskCleanTranscode": "Eolaire Transcode Glan",
"TaskDownloadMissingSubtitles": "Íosluchtaigh fotheidil ar iarraidh",
"CleanupUserDataTask": "Tasc glantacháin sonraí úsáideora",
"CleanupUserDataTaskDescription": "Glanann sé gach sonraí úsáideora (stádas faire, stádas is fearr leat srl.) ó mheáin nach bhfuil i láthair a thuilleadh ar feadh 90 lá ar a laghad."
"CleanupUserDataTaskDescription": "Glanann sé gach sonraí úsáideora (stádas faire, stádas is fearr leat srl.) ó mheáin nach bhfuil i láthair a thuilleadh ar feadh 90 lá ar a laghad.",
"Original": "Bunaidh"
}

View File

@@ -128,8 +128,6 @@
"TaskRefreshTrickplayImagesDescription": "Crea miniaturas de previsualización para os vídeos nas bibliotecas habilitadas.",
"TaskDownloadMissingLyrics": "Descargar letras que faltan",
"TaskDownloadMissingLyricsDescription": "Descarga as letras das cancións",
"TaskCleanCollectionsAndPlaylists": "Limpar coleccións e listas de reprodución",
"TaskCleanCollectionsAndPlaylistsDescription": "Quita ítems que xa non existen das coleccións e listas de reprodución.",
"TaskExtractMediaSegmentsDescription": "Procura segmentos de medios cos plugins habilitados.",
"TaskExtractMediaSegments": "Escaneo de segmentos de medios",
"TaskMoveTrickplayImages": "Migrar as miniaturas de previsualización a outra ubicación",

View File

@@ -127,9 +127,7 @@
"TaskRefreshTrickplayImages": "יצירת תמונות Trickplay",
"TaskRefreshTrickplayImagesDescription": "יוצר תמונות Trickplay לסרטונים בספריות הפעילות.",
"TaskAudioNormalization": "נרמול שמע",
"TaskCleanCollectionsAndPlaylistsDescription": "מנקה פריטים לא קיימים מאוספים ורשימות השמעה.",
"TaskAudioNormalizationDescription": "מחפש קבצי נורמליזציה של שמע.",
"TaskCleanCollectionsAndPlaylists": "מנקה אוספים ורשימות השמעה",
"TaskDownloadMissingLyrics": "הורדת מילים חסרות",
"TaskDownloadMissingLyricsDescription": "הורדת מילים לשירים",
"TaskMoveTrickplayImages": "העברת מיקום של תמונות Trickplay",

View File

@@ -134,7 +134,5 @@
"TaskExtractMediaSegmentsDescription": "मीडियासेगमेंट सक्षम प्लगइन्स से मीडिया सेगमेंट निकालता है या प्राप्त करता है।",
"TaskMoveTrickplayImages": "ट्रिकप्ले छवि स्थान माइग्रेट करें",
"TaskMoveTrickplayImagesDescription": "लाइब्रेरी सेटिंग्स के अनुसार मौजूदा ट्रिकप्ले फ़ाइलों को स्थानांतरित करता है।",
"TaskCleanCollectionsAndPlaylistsDescription": "संग्रहों और प्लेलिस्टों से उन आइटमों को हटाता है जो अब मौजूद नहीं हैं।",
"TaskCleanCollectionsAndPlaylists": "संग्रह और प्लेलिस्ट साफ़ करें",
"CleanupUserDataTask": "यूज़र डेटा सफाई कार्य"
}

View File

@@ -128,8 +128,6 @@
"TaskRefreshTrickplayImagesDescription": "Stvara preglede brzog pregledavanja za videa u aktiviranim bibliotekama.",
"TaskAudioNormalization": "Normalizacija zvuka",
"TaskAudioNormalizationDescription": "Skenira datoteke u potrazi za podacima o normalizaciji zvuka.",
"TaskCleanCollectionsAndPlaylistsDescription": "Uklanja stavke iz zbirki i popisa za reprodukciju koje više ne postoje.",
"TaskCleanCollectionsAndPlaylists": "Očisti zbirke i popise za reprodukciju",
"TaskExtractMediaSegments": "Skeniranje dijelova medija",
"TaskDownloadMissingLyrics": "Preuzmi tekstove koji nedostaju",
"TaskDownloadMissingLyricsDescription": "Preuzmi tekstove pjesama",
@@ -137,5 +135,6 @@
"TaskMoveTrickplayImages": "Premjesti mjesto slika brzog pregledavanja",
"TaskMoveTrickplayImagesDescription": "Premješta postojeće datoteke brzog pregledavanja u postavke biblioteke.",
"CleanupUserDataTask": "Zadatak čišćenja korisničkih podataka",
"CleanupUserDataTaskDescription": "Briše sve korisničke podatke (stanje gledanja, status favorita itd.) s medija koji više nisu prisutni najmanje 90 dana."
"CleanupUserDataTaskDescription": "Briše sve korisničke podatke (stanje gledanja, status favorita itd.) s medija koji više nisu prisutni najmanje 90 dana.",
"Original": "Original"
}

View File

@@ -39,7 +39,7 @@
"MixedContent": "Vegyes tartalom",
"Movies": "Filmek",
"Music": "Zenék",
"MusicVideos": "Zenei videóklipek",
"MusicVideos": "Zenei videók",
"NameInstallFailed": "{0} sikertelen telepítés",
"NameSeasonNumber": "{0}. évad",
"NameSeasonUnknown": "Ismeretlen évad",
@@ -127,9 +127,7 @@
"TaskRefreshTrickplayImages": "Trickplay képek előállítása",
"TaskRefreshTrickplayImagesDescription": "Trickplay előnézetet készít az engedélyezett könyvtárakban lévő videókhoz.",
"TaskAudioNormalization": "Hangerő-normalizálás",
"TaskCleanCollectionsAndPlaylistsDescription": "Nem létező elemek törlése a gyűjteményekből és lejátszási listákról.",
"TaskAudioNormalizationDescription": "Hangerő-normalizálási adatok keresése.",
"TaskCleanCollectionsAndPlaylists": "Gyűjtemények és lejátszási listák optimalizálása",
"TaskExtractMediaSegments": "Médiaszegmens felismerése",
"TaskDownloadMissingLyrics": "Hiányzó szöveg letöltése",
"TaskDownloadMissingLyricsDescription": "Zenék szövegének letöltése",
@@ -137,5 +135,6 @@
"TaskMoveTrickplayImagesDescription": "A médiatár-beállításoknak megfelelően áthelyezi a meglévő trickplay fájlokat.",
"TaskExtractMediaSegmentsDescription": "Kinyeri vagy megszerzi a médiaszegmenseket a MediaSegment támogatással rendelkező bővítményekből.",
"CleanupUserDataTaskDescription": "Legalább 90 napja nem elérhető médiákhoz kapcsolódó összes felhasználói adat (pl. megtekintési állapot, kedvencek) törlése.",
"CleanupUserDataTask": "Felhasználói adatok tisztítása feladat"
"CleanupUserDataTask": "Felhasználói adatok tisztítása feladat",
"Original": "Eredeti"
}

View File

@@ -128,8 +128,6 @@
"TaskRefreshTrickplayImagesDescription": "Buat pratinjau trickplay untuk video di perpustakaan yang diaktifkan.",
"TaskAudioNormalizationDescription": "Pindai file untuk data normalisasi audio.",
"TaskAudioNormalization": "Normalisasi Audio",
"TaskCleanCollectionsAndPlaylists": "Bersihkan koleksi dan daftar putar",
"TaskCleanCollectionsAndPlaylistsDescription": "Menghapus item dari koleksi dan daftar putar yang sudah tidak ada.",
"TaskDownloadMissingLyricsDescription": "Unduh lirik untuk lagu",
"TaskExtractMediaSegmentsDescription": "Mengekstrak atau memperoleh segmen media dari plugin yang mendukung MediaSegment.",
"TaskMoveTrickplayImagesDescription": "Memindahkan file trickplay yang sudah ada sesuai dengan pengaturan pustaka.",

View File

@@ -128,8 +128,6 @@
"TaskRefreshTrickplayImages": "Búa til hraðspilunarmyndir",
"TaskAudioNormalization": "Hljóðstöðlun",
"TaskAudioNormalizationDescription": "Leitar að hljóðstöðlunargögnum í skrám.",
"TaskCleanCollectionsAndPlaylists": "Hreinsa söfn og spilunarlista",
"TaskCleanCollectionsAndPlaylistsDescription": "Fjarlægir hluti úr söfnum og spilalistum sem eru ekki lengur til.",
"TaskDownloadMissingLyricsDescription": "Sækja söngtexta fyrir lög",
"TaskDownloadMissingLyrics": "Sækja söngtexta sem vantar",
"TaskExtractMediaSegments": "Skönnun efnishluta",

View File

@@ -126,8 +126,6 @@
"HearingImpaired": "Non udenti",
"TaskRefreshTrickplayImages": "Genera immagini Trickplay",
"TaskRefreshTrickplayImagesDescription": "Crea anteprime trickplay per i video nelle librerie abilitate.",
"TaskCleanCollectionsAndPlaylists": "Ripulisci le collezioni e le scalette",
"TaskCleanCollectionsAndPlaylistsDescription": "Rimuove gli elementi dalle collezioni e dalle scalette che non esistono più.",
"TaskAudioNormalization": "Normalizzazione dell'audio",
"TaskAudioNormalizationDescription": "Scansiona i file alla ricerca dei dati per la normalizzazione dell'audio.",
"TaskDownloadMissingLyricsDescription": "Scarica testi per le canzoni",
@@ -137,5 +135,6 @@
"TaskExtractMediaSegmentsDescription": "Estrae o ottiene segmenti multimediali dai plugin abilitati MediaSegment.",
"TaskExtractMediaSegments": "Scansiona Segmento Media",
"CleanupUserDataTask": "Task di pulizia dei dati utente",
"CleanupUserDataTaskDescription": "Pulisce tutti i dati utente (stato di visione, status preferiti, ecc.) dai contenuti non più presenti da almeno 90 giorni."
"CleanupUserDataTaskDescription": "Pulisce tutti i dati utente (stato di visione, status preferiti, ecc.) dai contenuti non più presenti da almeno 90 giorni.",
"Original": "Originale"
}

View File

@@ -126,10 +126,8 @@
"HearingImpaired": "聴覚障害の方",
"TaskRefreshTrickplayImages": "トリックプレー画像を生成",
"TaskRefreshTrickplayImagesDescription": "有効なライブラリ内のビデオをもとにトリックプレーのプレビューを生成します。",
"TaskCleanCollectionsAndPlaylists": "コレクションとプレイリストをクリーンアップ",
"TaskAudioNormalization": "音声の正規化",
"TaskAudioNormalizationDescription": "音声の正規化データのためにファイルをスキャンします。",
"TaskCleanCollectionsAndPlaylistsDescription": "在しなくなったコレクションやプレイリストからアイテムを削除します。",
"TaskDownloadMissingLyricsDescription": "歌詞をダウンロード",
"TaskExtractMediaSegments": "メディアセグメントを読み取る",
"TaskMoveTrickplayImages": "Trickplayの画像を移動",

View File

@@ -130,8 +130,6 @@
"TaskAudioNormalizationDescription": "აანალიზებს ფაილებს აუდიოს ნორმალიზაციისთვის.",
"TaskDownloadMissingLyrics": "მიუწვდომელი ლირიკების ჩამოტვირთვა",
"TaskDownloadMissingLyricsDescription": "ჩამოტვირთავს ამჟამად ბიბლიოთეკაში არარსებულ ლირიკებს სიმღერებისთვის",
"TaskCleanCollectionsAndPlaylists": "კოლექციების და დასაკრავი სიების გასუფთავება",
"TaskCleanCollectionsAndPlaylistsDescription": "შლის არარსებულ ერთეულებს კოლექციებიდან და დასაკრავი სიებიდან.",
"TaskExtractMediaSegments": "მედია სეგმენტების სკანირება",
"TaskExtractMediaSegmentsDescription": "მედია სეგმენტების სკანირება მხარდაჭერილი მოდულებისთვის.",
"TaskMoveTrickplayImages": "Trickplay სურათების მიგრაცია",

View File

@@ -127,7 +127,5 @@
"TaskUpdatePluginsDescription": "ទាញយក និងដំឡើងបច្ចុប្បន្នភាពសម្រាប់Plugins ដែលត្រូវបាន Config ដើម្បីធ្វើបច្ចុប្បន្នភាពដោយស្វ័យប្រវត្តិ.",
"TaskCleanTranscodeDescription": "លុបឯកសារ Transcode ដែលលើសពីមួយថ្ងៃ.",
"TaskDownloadMissingSubtitlesDescription": "ស្វែងរកតាមអ៊ីនធឺណិត សម្រាប់សាប់ថាយថល ដែលបាត់ដោយផ្អែកលើ metadata.",
"TaskOptimizeDatabase": "ធ្វើឱ្យ Database ប្រសើរឡើង",
"TaskCleanCollectionsAndPlaylistsDescription": "លុបរបស់របរចេញពីបណ្តុំ និងបញ្ជីចាក់ដែលលែងមាន.",
"TaskCleanCollectionsAndPlaylists": "សម្អាតបណ្តុំ និងបញ្ជីចាក់"
"TaskOptimizeDatabase": "ធ្វើឱ្យ Database ប្រសើរឡើង"
}

View File

@@ -129,7 +129,5 @@
"TaskExtractMediaSegments": "ಮಾಧ್ಯಮ ವಿಭಾಗದ ಹುಡುಕು",
"TaskDownloadMissingLyrics": "ಇಲ್ಲದ ಸಾಹಿತ್ಯವನ್ನು ಪಡೆಯಿರಿ",
"TaskAudioNormalization": "ಧ್ವನಿ ಸಾಮಾನ್ಯೀಕರಣ",
"TaskRefreshTrickplayImages": "ಟ್ರಿಕ್‌ಪ್ಲೇ ಚಿತ್ರಗಳನ್ನು ರಚಿಸಿ",
"TaskCleanCollectionsAndPlaylists": "ಸಂಗ್ರಹಗಳು ಮತ್ತು ಪ್ಲೇಪಟ್ಟಿಗಳನ್ನು ಸ್ವಚ್ಛಗೊಳಿಸಿ",
"TaskCleanCollectionsAndPlaylistsDescription": "ಇಲ್ಲದ ಸಂಗ್ರಹಗಳು ಮತ್ತು ಪ್ಲೇಪಟ್ಟಿಗಳಿಂದ ವಸ್ತುಗಳನ್ನು ತೆಗೆದುಹಾಕುತ್ತದೆ."
"TaskRefreshTrickplayImages": "ಟ್ರಿಕ್‌ಪ್ಲೇ ಚಿತ್ರಗಳನ್ನು ರಚಿಸಿ"
}

View File

@@ -124,12 +124,10 @@
"TaskKeyframeExtractor": "키프레임 추출",
"External": "외부",
"HearingImpaired": "청각 장애",
"TaskCleanCollectionsAndPlaylists": "컬렉션과 재생목록 정리",
"TaskAudioNormalization": "오디오의 볼륨 수준을 일정하게 조정",
"TaskAudioNormalizationDescription": "오디오의 볼륨 수준을 일정하게 조정하기 위해 파일을 스캔합니다.",
"TaskRefreshTrickplayImages": "비디오 탐색용 미리보기 썸네일 생성",
"TaskRefreshTrickplayImagesDescription": "활성화된 라이브러리에서 비디오의 트릭플레이 미리보기를 생성합니다.",
"TaskCleanCollectionsAndPlaylistsDescription": "더 이상 존재하지 않는 컬렉션 및 재생 목록에서 항목을 제거합니다.",
"TaskExtractMediaSegments": "미디어 세그먼트 스캔",
"TaskExtractMediaSegmentsDescription": "MediaSegment를 지원하는 플러그인에서 미디어 세그먼트를 추출하거나 가져옵니다.",
"TaskMoveTrickplayImages": "트릭플레이 이미지 위치 마이그레이션",

View File

@@ -128,9 +128,7 @@
"TaskOptimizeDatabaseDescription": "Y hwra kesstrotha ha berrhe efander rydh. Martesen y hwra gwellhe gwryth mar kwre'ta an oberen ma wosa ty dhe arhwilas an lyverva, po neb chanj aral neb a brof chanjyansow selvanylyon.",
"TaskAudioNormalizationDescription": "Y hwra arhwilas restrennow rag manylyon normalheans klewans.",
"TaskRefreshLibraryDescription": "Y hwra arhwilas dha lyverva media rag restrennow nowydh ha disegha metamanylyon.",
"TaskCleanCollectionsAndPlaylists": "Glanhe kuntellow ha rolyow-gwari",
"TaskKeyframeExtractor": "Estennell Framalhwedh",
"TaskCleanCollectionsAndPlaylistsDescription": "Y hwra dilea taklow a-dhyworth kuntellow ha rolyow-gwari na vos na moy.",
"TaskKeyframeExtractorDescription": "Y hwra kuntel framyowalhwedh a-dhyworth restrennow gwydhyowyow rag gul rolyow-gwari HLS moy poran. Martesen y hwra an oberen ma ow ponya rag termyn hir.",
"TaskExtractMediaSegments": "Arhwilas Rann Media",
"TaskExtractMediaSegmentsDescription": "Kavos rannow media a-dhyworth ystynansow gallosegys MediaSegment.",

View File

@@ -104,8 +104,6 @@
"TaskDownloadMissingSubtitles": "Fehlend Ënnertitelen eroflueden",
"TaskOptimizeDatabase": "Datebank optiméieren",
"TaskKeyframeExtractor": "Schlësselbild Extrakter",
"TaskCleanCollectionsAndPlaylists": "Sammlungen a Playlisten botzen",
"TaskCleanCollectionsAndPlaylistsDescription": "Ewechhuele vun Elementer aus Sammlungen a Playlisten, déi net méi existéieren.",
"TaskExtractMediaSegments": "Mediesegment-Scan",
"NewVersionIsAvailable": "Nei Versioun fir Jellyfin Server ass verfügbar.",
"CameraImageUploadedFrom": "En neit Kamera Bild gouf vu {0} eropgelueden",

View File

@@ -126,8 +126,6 @@
"HearingImpaired": "Su klausos sutrikimais",
"TaskRefreshTrickplayImages": "Generuoti Trickplay atvaizdus",
"TaskRefreshTrickplayImagesDescription": "Sukuria trickplay peržiūras vaizdo įrašams įgalintose bibliotekose.",
"TaskCleanCollectionsAndPlaylists": "Išvalo duomenis rinkiniuose ir grojaraščiuose",
"TaskCleanCollectionsAndPlaylistsDescription": "Pašalina neegzistuojančius elementus iš rinkinių ir grojaraščių.",
"TaskAudioNormalization": "Garso normalizavimas",
"TaskAudioNormalizationDescription": "Skenuoja failus, ieškant garso normalizavimo duomenų.",
"TaskExtractMediaSegments": "Medijos segmentų nuskaitymas",

View File

@@ -127,9 +127,7 @@
"TaskRefreshTrickplayImages": "Ģenerēt partīšanas attēlus",
"TaskRefreshTrickplayImagesDescription": "Izveido priekšskatījumus videoklipu pārtīšanai iespējotajās bibliotēkās.",
"TaskAudioNormalization": "Audio normalizācija",
"TaskCleanCollectionsAndPlaylistsDescription": "Noņem vairs neeksistējošus vienumus no kolekcijām un atskaņošanas sarakstiem.",
"TaskAudioNormalizationDescription": "Skanē failus priekš audio normālizācijas informācijas.",
"TaskCleanCollectionsAndPlaylists": "Notīrīt kolekcijas un atskaņošanas sarakstus",
"TaskExtractMediaSegments": "Multivides segmenta skenēšana",
"TaskExtractMediaSegmentsDescription": "Izvelk vai iegūst multivides segmentus no MediaSegment iespējotiem spraudņiem.",
"TaskMoveTrickplayImages": "Trickplay attēlu pārvietošana",
@@ -137,5 +135,6 @@
"TaskDownloadMissingLyrics": "Lejupielādēt trūkstošos vārdus",
"TaskDownloadMissingLyricsDescription": "Lejupielādēt vārdus dziesmām",
"CleanupUserDataTask": "Lietotāju datu tīrīšanas uzdevums",
"CleanupUserDataTaskDescription": "Notīra visus lietotāja datus (skatīšanās stāvokļus, favorītu statusi utt.) no medijiem, kas vairs nav pieejami vismaz 90 dienas."
"CleanupUserDataTaskDescription": "Notīra visus lietotāja datus (skatīšanās stāvokļus, favorītu statusi utt.) no medijiem, kas vairs nav pieejami vismaz 90 dienas.",
"Original": "Oriģināls"
}

View File

@@ -124,13 +124,15 @@
"TaskCleanActivityLog": "Избриши Лог на Активности",
"External": "Надворешен",
"HearingImpaired": "Оштетен слух",
"TaskCleanCollectionsAndPlaylists": "Исчисти ги колекциите и плејлистите",
"TaskAudioNormalizationDescription": "Скенирање датотеки за податоци за нормализација на звукот.",
"TaskDownloadMissingLyrics": "Преземи стихови кои недостасуваат",
"TaskDownloadMissingLyricsDescription": "Преземи стихови/текстови за песни",
"TaskRefreshTrickplayImages": "Генерирај слики за прегледување (Trickplay)",
"TaskAudioNormalization": "Нормализација на звукот",
"TaskRefreshTrickplayImagesDescription": "Креира трикплеј прегледи за видеа во овозможените библиотеки.",
"TaskCleanCollectionsAndPlaylistsDescription": "Отстранува ставки од колекциите и плејлистите што веќе не постојат.",
"TaskExtractMediaSegments": "Скенирање на сегменти на содржина"
"TaskExtractMediaSegments": "Скенирање на сегменти на содржина",
"TaskMoveTrickplayImages": "Мигрирај ја локацијата на сликата од Trickplay",
"TaskMoveTrickplayImagesDescription": "Ги преместува постоечките датотеки за трикплеј според поставките на библиотеката.",
"CleanupUserDataTask": "Задача за чистење на кориснички податоци",
"CleanupUserDataTaskDescription": "Ги чисти сите кориснички податоци (состојба на гледање, статус на омилени итн.) од медиуми што повеќе не се присутни најмалку 90 дена."
}

View File

@@ -124,8 +124,6 @@
"External": "പുറമേയുള്ള",
"TaskKeyframeExtractorDescription": "കൂടുതൽ കൃത്യമായ HLS പ്ലേലിസ്റ്റുകൾ സൃഷ്‌ടിക്കുന്നതിന് വീഡിയോ ഫയലുകളിൽ നിന്ന് കീഫ്രെയിമുകൾ എക്‌സ്‌ട്രാക്‌റ്റ് ചെയ്യുന്നു. ഈ പ്രവർത്തനം പൂർത്തിയാവാൻ കുറച്ചധികം സമയം എടുത്തേക്കാം.",
"TaskKeyframeExtractor": "കീഫ്രെയിം എക്സ്ട്രാക്റ്റർ",
"TaskCleanCollectionsAndPlaylistsDescription": "നിലവിലില്ലാത്ത ശേഖരങ്ങളിൽ നിന്നും പ്ലേലിസ്റ്റുകളിൽ നിന്നും ഇനങ്ങൾ നീക്കംചെയ്യുന്നു.",
"TaskCleanCollectionsAndPlaylists": "ശേഖരങ്ങളും പ്ലേലിസ്റ്റുകളും വൃത്തിയാക്കുക",
"TaskAudioNormalization": "സാധാരണ ശബ്ദ നിലയിലെത്തിലെത്തിക്കുക",
"TaskAudioNormalizationDescription": "സാധാരണ ശബ്ദ നിലയിലെത്തിലെത്തിക്കുന്ന ഡാറ്റയ്ക്കായി ഫയലുകൾ സ്കാൻ ചെയ്യുക.",
"TaskRefreshTrickplayImages": "ട്രിക്ക് പ്ലേ ചിത്രങ്ങൾ സൃഷ്ടിക്കുക",

View File

@@ -27,7 +27,6 @@
"NotificationOptionServerRestartRequired": "Server-г дахин асаана уу",
"NotificationOptionVideoPlaybackStopped": "Бичлэгийг зогсоов",
"UserPasswordChangedWithName": "Хэрэглэгч {0}-н нууц үгийг өөрчиллөө",
"TaskCleanCollectionsAndPlaylists": "Цуглуулга ба тоглуулах жагсаалтыг цэвэрлэх",
"ScheduledTaskFailedWithName": "{0} амжилтгүй",
"StartupEmbyServerIsLoading": "Jellyfin Server ачааллаж байна. Хэсэг хугацааны дараа дахин оролдоно уу.",
"TaskCleanActivityLog": "Үйл ажиллагааны бүртгэлийг цэвэрлэх",
@@ -44,7 +43,6 @@
"NotificationOptionAudioPlayback": "Дууг тоглууллаа",
"TaskRefreshTrickplayImages": "Трикплэй зургуудыг үүсгэх",
"TaskUpdatePlugins": "Plugin-уудыг шинэчлэх",
"TaskCleanCollectionsAndPlaylistsDescription": "Одоо байхгүй болсон зүйлсийг цуглуулга ба тоглуулах жагсаалтаас устгана.",
"TaskAudioNormalization": "Аудиог хэвшүүлэх",
"TaskAudioNormalizationDescription": "Файлуудаас дууны хэвийн хэмжээсийн мэдээллийг шалгана.",
"TaskRefreshTrickplayImagesDescription": "Идэвхжсэн сангуудад байгаа видеонуудын трикплэй урьдчилсан харагдацыг үүсгэнэ.",

View File

@@ -126,7 +126,6 @@
"HearingImpaired": "कर्णबधीर",
"TaskRefreshTrickplayImages": "ट्रिकप्ले प्रतिमा तयार करा",
"TaskRefreshTrickplayImagesDescription": "सक्षम लायब्ररीमधील व्हिडिओंसाठी ट्रिकप्ले पूर्वावलोकन तयार करते.",
"TaskCleanCollectionsAndPlaylists": "संग्रह आणि प्लेलिस्ट व्यवस्थित करा",
"TaskExtractMediaSegments": "मिडिया विभाग तपासणी",
"TaskMoveTrickplayImages": "ट्रिकप्ले प्रतिमेचे स्थान स्थलांतर करा",
"TaskDownloadMissingLyrics": "उपलब्ध नसलेली गीतपट्टी (Lyrics) डाउनलोड करा",
@@ -135,7 +134,6 @@
"TaskDownloadMissingLyricsDescription": "गाण्यांची गीतपट्टी (Lyrics) डाउनलोड करतो",
"TaskExtractMediaSegmentsDescription": "सक्रिय असलेल्या प्लगिनमधून मीडिया विभाग प्राप्त करते.",
"TaskMoveTrickplayImagesDescription": "लायब्ररीच्या सेटिंग्जप्रमाणे आधीपासून अस्तित्वात असलेल्या ट्रिकप्ले फाइल्सचे स्थान बदलते.",
"TaskCleanCollectionsAndPlaylistsDescription": "जे संग्रह आणि प्लेलिस्ट आता अस्तित्वात नाहीत, त्यांमधील घटक हटवते.",
"CleanupUserDataTask": "वापरकर्ता डेटाची स्वच्छता प्रक्रिया",
"CleanupUserDataTaskDescription": "९० दिवसांहून अधिक काळ अनुपस्थित असलेल्या माध्यमांवरील सर्व वापरकर्ता माहिती (जसे पाहण्याची स्थिती, आवडी इ.) हटवते."
}

View File

@@ -132,10 +132,8 @@
"TaskDownloadMissingLyrics": "Muat turun lirik yang hilang",
"TaskDownloadMissingLyricsDescription": "Memuat turun lirik-lirik untuk lagu-lagu",
"TaskMoveTrickplayImages": "Alih Lokasi Imej Trickplay",
"TaskCleanCollectionsAndPlaylists": "Bersihkan koleksi dan senarai audio video",
"TaskAudioNormalization": "Normalisasi Audio",
"TaskAudioNormalizationDescription": "Mengimbas fail-fail untuk data normalisasi audio.",
"TaskCleanCollectionsAndPlaylistsDescription": "Mengalih keluar item daripada koleksi dan senarai audio video yang tidak wujud lagi.",
"CleanupUserDataTaskDescription": "Membersihkan semua data pengguna (keadaan tontonan, status kegemaran, dan sebagainya) daripada media yang tidak lagi wujud sekurang-kurangnya selama 90 hari.",
"CleanupUserDataTask": "Tugas pembersihan data pengguna"
}

View File

@@ -128,8 +128,6 @@
"TaskOptimizeDatabase": "Ottimiżża d-database",
"TaskKeyframeExtractor": "Estrattur ta' Keyframes",
"TaskKeyframeExtractorDescription": "Jiġbed il-keyframes mill-fajls tal-videos biex jagħmel playlists HLS aktar preċiżi. Dan it-task jista' jdum żmien twil biex ilesti.",
"TaskCleanCollectionsAndPlaylists": "Naddaf il-kollezzjonijiet u l-playlists",
"TaskCleanCollectionsAndPlaylistsDescription": "Ineħħi oġġetti minn kollezzjonijiet u playlists li m'għadhomx jeżistu.",
"TaskDownloadMissingLyrics": "Niżżel il-lirika nieqsa",
"TaskDownloadMissingLyricsDescription": "Iniżżel il-lirika għal-kanzunetti",
"TaskExtractMediaSegments": "Scan tas-Sezzjoni tal-Midja",

View File

@@ -122,10 +122,8 @@
"AppDeviceValues": "အက်ပ်- {0}၊ စက်- {1}",
"External": "ပြင်ပ",
"TaskKeyframeExtractorDescription": "ပိုမိုတိကျသည့် အိတ်ချ်အယ်လ်အက်စ် အစဉ်လိုက်ပြသမှုများ ဖန်တီးနိုင်ရန်အတွက် ဗီဒီယိုဖိုင်များမှ ကီးဖရိန်များကို ထုတ်နှုတ်ယူမည် ဖြစ်သည်။ ဤလုပ်ဆောင်မှုသည် အချိန်ကြာရှည်နိုင်သည်။",
"TaskCleanCollectionsAndPlaylistsDescription": "စုစည်းမှုများနှင့် အစဉ်လိုက်ပြသမှုများမှ မရှိတော့သည်များကို ဖယ်ရှားမည်။",
"TaskRefreshTrickplayImages": "ထရစ်ခ်ပလေး ပုံများကို ထုတ်မည်",
"TaskKeyframeExtractor": "ကီးဖရိန်များကို ထုတ်နုတ်ခြင်း",
"TaskCleanCollectionsAndPlaylists": "စုစည်းမှုများနှင့် အစဉ်လိုက်ပြသမှုများကို ရှင်းလင်းမည်",
"HearingImpaired": "အကြားအာရုံ ချို့တဲ့သူ",
"TaskDownloadMissingLyrics": "ကျန်နေသောသီချင်းစာသားများအား ဒေါင်းလုတ်ဆွဲပါ",
"TaskDownloadMissingLyricsDescription": "သီချင်းများအတွက် သီချင်းစာသား ဒေါင်းလုတ်ဆွဲပါ"

View File

@@ -126,10 +126,8 @@
"HearingImpaired": "Hørselshemmet",
"TaskRefreshTrickplayImages": "Generer Trickplay bilder",
"TaskRefreshTrickplayImagesDescription": "Oppretter trickplay-forhåndsvisninger for videoer i aktiverte biblioteker.",
"TaskCleanCollectionsAndPlaylists": "Rydd kolleksjoner og spillelister",
"TaskAudioNormalization": "Lydnormalisering",
"TaskAudioNormalizationDescription": "Skan filer for lydnormaliserende data.",
"TaskCleanCollectionsAndPlaylistsDescription": "Fjerner elementer fra kolleksjoner og spillelister som ikke lengere finnes.",
"TaskDownloadMissingLyrics": "Last ned manglende tekster",
"TaskDownloadMissingLyricsDescription": "Last ned sangtekster",
"TaskExtractMediaSegments": "Skann mediasegment",

View File

@@ -45,7 +45,7 @@
"Genres": "विधाहरू",
"Folders": "फोल्डरहरू",
"Favorites": "मनपर्ने",
"FailedLoginAttemptWithUserName": "{0}को लग इन प्रयास असफल",
"FailedLoginAttemptWithUserName": "असफल लग इन प्रयास {0} देखि",
"DeviceOnlineWithName": "{0}को साथ जडित",
"DeviceOfflineWithName": "{0}बाट विच्छेदन भयो",
"Collections": "संग्रह",

View File

@@ -14,7 +14,7 @@
"Favorites": "Favorieten",
"Folders": "Mappen",
"HeaderAlbumArtists": "Albumartiesten",
"HeaderContinueWatching": "Verder kijken",
"HeaderContinueWatching": "Verderkijken",
"HeaderFavoriteAlbums": "Favoriete albums",
"HeaderFavoriteArtists": "Favoriete artiesten",
"HeaderFavoriteEpisodes": "Favoriete afleveringen",
@@ -124,8 +124,6 @@
"HearingImpaired": "Slechthorenden",
"TaskRefreshTrickplayImages": "Trickplay-afbeeldingen genereren",
"TaskRefreshTrickplayImagesDescription": "Creëert trickplay-voorvertoningen voor video's in bibliotheken waarvoor dit is ingeschakeld.",
"TaskCleanCollectionsAndPlaylists": "Collecties en afspeellijsten opruimen",
"TaskCleanCollectionsAndPlaylistsDescription": "Verwijdert niet langer bestaande items uit collecties en afspeellijsten.",
"TaskAudioNormalization": "Geluidsnormalisatie",
"TaskAudioNormalizationDescription": "Scant bestanden op gegevens voor geluidsnormalisatie.",
"TaskDownloadMissingLyrics": "Ontbrekende liedteksten downloaden",
@@ -137,5 +135,6 @@
"CleanupUserDataTaskDescription": "Wist alle gebruikersgegevens (kijkstatus, favorieten, etc.) van media die al minstens 90 dagen niet meer aanwezig zijn.",
"CleanupUserDataTask": "Opruimtaak gebruikersdata",
"Albums": "Albums",
"Genres": "Genres"
"Genres": "Genres",
"Original": "Oorspronkelijk"
}

View File

@@ -131,8 +131,6 @@
"TaskDownloadMissingLyrics": "ਅਧੂਰੇ ਬੋਲ ਡਾਊਨਲੋਡ ਕਰੋ",
"TaskDownloadMissingLyricsDescription": "ਗੀਤਾਂ ਲਈ ਡਾਊਨਲੋਡ ਕਿਤੇ ਬੋਲ",
"TaskKeyframeExtractor": "ਕੀ-ਫ੍ਰੇਮ ਐਕਸਟ੍ਰੈਕਟਰ",
"TaskCleanCollectionsAndPlaylistsDescription": "ਕਲੈਕਸ਼ਨਾਂ ਅਤੇ ਪਲੇਲਿਸਟਾਂ ਵਿੱਚੋਂ ਉਹ ਆਈਟਮ ਹਟਾਉਂਦਾ ਹੈ ਜੋ ਹੁਣ ਮੌਜੂਦ ਨਹੀਂ ਹਨ।",
"TaskCleanCollectionsAndPlaylists": "ਕਲੈਕਸ਼ਨਾਂ ਅਤੇ ਪਲੇਲਿਸਟਾਂ ਨੂੰ ਸਾਫ ਕਰੋ",
"TaskAudioNormalization": "ਆਵਾਜ਼ ਸਧਾਰਣੀਕਰਨ",
"TaskRefreshTrickplayImagesDescription": "ਵੀਡੀਓ ਲਈ ਟ੍ਰਿਕਪਲੇ ਪ੍ਰੀਵਿਊ ਬਣਾਉਂਦਾ ਹੈ (ਜੇ ਲਾਇਬ੍ਰੇਰੀ ਵਿੱਚ ਚੁਣਿਆ ਗਿਆ ਹੈ)।",
"TaskKeyframeExtractorDescription": "ਕੀ-ਫ੍ਰੇਮਜ਼ ਨੂੰ ਵੀਡੀਓ ਫਾਈਲਾਂ ਵਿੱਚੋਂ ਨਿਕਾਲਦਾ ਹੈ ਤਾਂ ਜੋ ਹੋਰ ਜ਼ਿਆਦਾ ਸਟਿਕ ਹੋਣ ਵਾਲੀਆਂ HLS ਪਲੇਲਿਸਟਾਂ ਬਣਾਈਆਂ ਜਾ ਸਕਣ। ਇਹ ਕੰਮ ਲੰਬੇ ਸਮੇਂ ਤੱਕ ਚੱਲ ਸਕਦਾ ਹੈ।",

View File

@@ -126,8 +126,6 @@
"HearingImpaired": "Niedosłyszący",
"TaskRefreshTrickplayImages": "Generuj obrazy Trickplay",
"TaskRefreshTrickplayImagesDescription": "Tworzy podglądy Trickplay dla filmów we włączonych bibliotekach.",
"TaskCleanCollectionsAndPlaylistsDescription": "Usuwa elementy z kolekcji i list odtwarzania, które już nie istnieją.",
"TaskCleanCollectionsAndPlaylists": "Oczyść kolekcje i listy odtwarzania",
"TaskAudioNormalization": "Normalizacja dźwięku",
"TaskAudioNormalizationDescription": "Skanuje pliki w poszukiwaniu danych normalizacji dźwięku.",
"TaskDownloadMissingLyrics": "Pobierz brakujące słowa",
@@ -137,5 +135,6 @@
"TaskExtractMediaSegmentsDescription": "Wyodrębnia lub pobiera segmenty mediów z wtyczek obsługujących MediaSegment.",
"TaskMoveTrickplayImagesDescription": "Przenosi istniejące pliki Trickplay zgodnie z ustawieniami biblioteki.",
"CleanupUserDataTaskDescription": "Usuwa wszystkie dane użytkownika (stan oglądanych, status ulubionych itp.) z mediów, które nie są dostępne od co najmniej 90 dni.",
"CleanupUserDataTask": "Zadanie czyszczenia danych użytkownika"
"CleanupUserDataTask": "Zadanie czyszczenia danych użytkownika",
"Original": "Oryginalny"
}

View File

@@ -126,8 +126,6 @@
"HearingImpaired": "Deficiência Auditiva",
"TaskRefreshTrickplayImages": "Gerar imagens Trickplay",
"TaskRefreshTrickplayImagesDescription": "Cria prévias Trickplay para vídeos em bibliotecas em que o recurso está habilitado.",
"TaskCleanCollectionsAndPlaylists": "Limpe coleções e playlists",
"TaskCleanCollectionsAndPlaylistsDescription": "Remove itens de coleções e playlists que não existem mais.",
"TaskAudioNormalization": "Normalização de áudio",
"TaskAudioNormalizationDescription": "Examina os ficheiros em busca de dados de normalização de áudio.",
"TaskDownloadMissingLyricsDescription": "Baixar letras para músicas",

View File

@@ -126,8 +126,6 @@
"HearingImpaired": "Surdo",
"TaskRefreshTrickplayImages": "Gerar imagens de trickplay",
"TaskRefreshTrickplayImagesDescription": "Cria pré-visualizações de trickplay para vídeos nas bibliotecas ativadas.",
"TaskCleanCollectionsAndPlaylistsDescription": "Remove itens de coleções e listas de reprodução que já não existem.",
"TaskCleanCollectionsAndPlaylists": "Limpar coleções e listas de reprodução",
"TaskAudioNormalizationDescription": "Analisa os ficheiros para obter dados de normalização de áudio.",
"TaskAudioNormalization": "Normalização de áudio",
"TaskExtractMediaSegments": "Analisar segmentos de multimédia",
@@ -137,5 +135,6 @@
"TaskExtractMediaSegmentsDescription": "Extrai ou obtém segmentos de multimédia a partir de plugins com suporte para MediaSegment.",
"TaskMoveTrickplayImagesDescription": "Move os ficheiros trickplay existentes de acordo com as definições da mediateca.",
"CleanupUserDataTaskDescription": "Apaga todos os dados de utilizador (estados de reprodução, favoritos, etc) de arquivos média não presentes há 90 dias ou mais.",
"CleanupUserDataTask": "Limpeza de dados de utilizador"
"CleanupUserDataTask": "Limpeza de dados de utilizador",
"Original": "Original"
}

View File

@@ -126,8 +126,6 @@
"TaskKeyframeExtractorDescription": "Retira frames chave do video para criar listas HLS precisas. Esta tarefa pode correr durante algum tempo.",
"TaskRefreshTrickplayImages": "Gerar imagens de trickplay",
"TaskRefreshTrickplayImagesDescription": "Cria pré-visualizações de trickplay para vídeos nas bibliotecas ativadas.",
"TaskCleanCollectionsAndPlaylistsDescription": "Remove itens de coleções e listas de reprodução que já não existem.",
"TaskCleanCollectionsAndPlaylists": "Limpar coleções e listas de reprodução",
"TaskAudioNormalizationDescription": "Analisa os ficheiros para obter dados de normalização de áudio.",
"TaskAudioNormalization": "Normalização de áudio",
"TaskDownloadMissingLyrics": "Transferir letra em falta",
@@ -137,5 +135,6 @@
"TaskExtractMediaSegmentsDescription": "Extrai ou obtém segmentos de multimédia a partir de plugins com suporte para MediaSegment.",
"TaskMoveTrickplayImages": "Migrar a localização da imagem do Trickplay",
"CleanupUserDataTask": "Task de limpeza de dados do usuário",
"CleanupUserDataTaskDescription": "Remove todos os dados do usuário (progresso, favoritos etc) de mídias que não estão presentes há pelo menos 90 dias."
"CleanupUserDataTaskDescription": "Remove todos os dados do usuário (progresso, favoritos etc) de mídias que não estão presentes há pelo menos 90 dias.",
"Original": "Original"
}

View File

@@ -128,8 +128,6 @@
"TaskRefreshTrickplayImagesDescription": "Generează previzualizările trickplay pentru videourile din librăriile selectate.",
"TaskAudioNormalizationDescription": "Scanează fișiere pentru date necesare normalizării sunetului.",
"TaskAudioNormalization": "Normalizare sunet",
"TaskCleanCollectionsAndPlaylists": "Curăță colecțiile și listele de redare",
"TaskCleanCollectionsAndPlaylistsDescription": "Elimină elementele care nu mai există din colecții și liste de redare.",
"TaskExtractMediaSegments": "Scanează segmentele media",
"TaskMoveTrickplayImagesDescription": "Mută fișierele trickplay existente conform setărilor librăriei.",
"TaskExtractMediaSegmentsDescription": "Extrage sau obține segmentele media de la pluginurile MediaSegment activate.",

View File

@@ -126,8 +126,6 @@
"HearingImpaired": "Для слабослышащих",
"TaskRefreshTrickplayImages": "Сгенерировать изображения для Trickplay",
"TaskRefreshTrickplayImagesDescription": "Создает предпросмотры для Trickplay для видео в библиотеках, где эта функция включена.",
"TaskCleanCollectionsAndPlaylists": "Очистка коллекций и списков воспроизведения",
"TaskCleanCollectionsAndPlaylistsDescription": "Удаляет элементы из коллекций и списков воспроизведения, которые больше не существуют.",
"TaskAudioNormalization": "Нормализация звука",
"TaskAudioNormalizationDescription": "Сканирует файлы на наличие данных о нормализации звука.",
"TaskDownloadMissingLyrics": "Загрузить недостающий текст",

View File

@@ -126,8 +126,6 @@
"HearingImpaired": "Sluchovo postihnutí",
"TaskRefreshTrickplayImages": "Generovanie obrázkov Trickplay",
"TaskRefreshTrickplayImagesDescription": "Vytvára trickplay náhľady pre videá v povolených knižniciach.",
"TaskCleanCollectionsAndPlaylists": "Vyčistiť kolekcie a playlisty",
"TaskCleanCollectionsAndPlaylistsDescription": "Odstráni položky z kolekcií a playlistov, ktoré už neexistujú.",
"TaskAudioNormalization": "Normalizácia zvuku",
"TaskAudioNormalizationDescription": "Skenovať súbory za účelom normalizácie zvuku.",
"TaskExtractMediaSegments": "Skenovanie segmentov médií",

View File

@@ -132,10 +132,8 @@
"TaskMoveTrickplayImages": "Preseli lokacijo Trickplay slik",
"TaskDownloadMissingLyrics": "Prenesi manjkajoča besedila pesmi",
"TaskDownloadMissingLyricsDescription": "Prenesi besedila za pesmi",
"TaskCleanCollectionsAndPlaylists": "Počisti zbirke in sezname predvajanja",
"TaskAudioNormalization": "Normalizacija zvoka",
"TaskAudioNormalizationDescription": "Pregled datotek za podatke o normalizaciji zvoka.",
"TaskCleanCollectionsAndPlaylistsDescription": "Odstrani elemente iz zbirk in seznamov predvajanja, ki ne obstajajo več.",
"CleanupUserDataTask": "Čiščenje uporabniških podatkov",
"CleanupUserDataTaskDescription": "Izbriše vse uporabniške podatke (stanje ogleda, priljubljene itd.) za vsebine, ki že več kot 90 dni niso na voljo."
}

View File

@@ -132,8 +132,6 @@
"TaskMoveTrickplayImagesDescription": "Zhvendos skedarët ekzistues të trickplay sipas cilësimeve të bibliotekës.",
"TaskDownloadMissingLyrics": "Shkarko tekstet e këngëve që mungojnë",
"TaskDownloadMissingLyricsDescription": "Shkarkon tekstet e këngëve",
"TaskCleanCollectionsAndPlaylists": "Pastron koleksionet dhe listat e këngëve",
"TaskCleanCollectionsAndPlaylistsDescription": "Heq elementet nga koleksionet dhe listat e këngëve që nuk ekzistojnë më.",
"TaskAudioNormalization": "Normalizimi i audios",
"TaskAudioNormalizationDescription": "Skannon skedarët për të dhëna të normalizimit të audios.",
"CleanupUserDataTaskDescription": "Pastron të gjitha të dhënat e përdorueseve (gjendja e shikimit, statusi i të preferuarave etj.) nga mediat që nuk janë më të pranishme për të paktën 90 ditë.",

View File

@@ -125,12 +125,10 @@
"TaskKeyframeExtractor": "Екстрактор кључних сличица",
"HearingImpaired": "ослабљен слух",
"TaskAudioNormalization": "Нормализација звука",
"TaskCleanCollectionsAndPlaylists": "Очистите колекције и плејлисте",
"TaskAudioNormalizationDescription": "Скенира датотеке за податке о нормализацији звука.",
"TaskRefreshTrickplayImages": "Направи сличице за визуелно премотавање",
"TaskRefreshTrickplayImagesDescription": "Прављење сличица које помажу код визуелног премотавања видео-снимака.",
"TaskDownloadMissingLyrics": "Преузми стихове који недостају",
"TaskCleanCollectionsAndPlaylistsDescription": "Уклања ставке које више не постоје из колекција и плејлиста.",
"TaskExtractMediaSegments": "Скенирај сегменте медија",
"TaskExtractMediaSegmentsDescription": "Извлачи или добавља сегменте медија у додацима који раде са MediaSegment-ом.",
"TaskMoveTrickplayImagesDescription": "Премешта постојеће сличице за визуелно премотавање сходно подешавањима библиотеке.",

View File

@@ -126,9 +126,7 @@
"HearingImpaired": "Hörselskadad",
"TaskRefreshTrickplayImages": "Generera Trickplay-bilder",
"TaskRefreshTrickplayImagesDescription": "Skapar trickplay-förhandsvisningar för videor i aktiverade bibliotek.",
"TaskCleanCollectionsAndPlaylists": "Rensa upp samlingar och spellistor",
"TaskAudioNormalization": "Ljudnormalisering",
"TaskCleanCollectionsAndPlaylistsDescription": "Tar bort objekt från samlingar och spellistor som inte längre finns.",
"TaskAudioNormalizationDescription": "Skannar filer för ljudnormaliseringsdata.",
"TaskDownloadMissingLyrics": "Ladda ner saknad låttext",
"TaskDownloadMissingLyricsDescription": "Laddar ner låttexter",
@@ -137,5 +135,6 @@
"TaskMoveTrickplayImages": "Migrera platsen för Trickplay-bilder",
"TaskMoveTrickplayImagesDescription": "Flyttar befintliga trickplay-filer enligt bibliotekets inställningar.",
"CleanupUserDataTaskDescription": "Tar bort all användardata (såsom vad du sett, favoriter med mera) för media som inte funnits på enheten på minst 90 dagar.",
"CleanupUserDataTask": "Uppgift för rensning av användardata"
"CleanupUserDataTask": "Uppgift för rensning av användardata",
"Original": "Original"
}

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