Compare commits

..

148 Commits

Author SHA1 Message Date
Andrew Rabert
4cf53d9f88 Mitigate pull_request_target privilege escalation
Hotfix — replaces pull_request_target with pull_request to stop
granting write permissions and secrets to fork PRs. Some workflows
will break; can be fixed properly later.
2026-02-20 19:10:40 -05:00
crobibero
641a097707 Implement caching for OpenAPI document 2025-11-30 09:04:40 -07:00
Niels van Velzen
6c507b77ae Remove DtoExtensions.AddClientFields (#15638)
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
OpenAPI / OpenAPI - BASE (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (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
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (push) Has been cancelled
2025-11-30 07:22:54 -07:00
renovate[bot]
6ed0ccd37c Update appleboy/ssh-action action to v1.2.4 (#15660)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-30 07:15:08 -07:00
Martín
80e1e42947 Added translation using Weblate (Occitan)
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
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (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 / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (push) Has been cancelled
2025-11-28 20:01:20 +00:00
Niels van Velzen
6ace00eb6a Merge pull request #15227 from kevgrig/issue15226
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (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 / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (push) Has been cancelled
Stale Issue Labeler / Check for stale issues (push) Has been cancelled
Add milliseconds to default console output format
2025-11-27 16:33:38 +01:00
Niels van Velzen
a35ffbf17e Merge pull request #13977 from sususu98/fix/strm-local-subtitle-url
refactor(StreamInfo): reorganize subtitle URL logic and conditions
2025-11-27 16:33:19 +01:00
Niels van Velzen
8c02c3be93 Merge pull request #14824 from CodyEngel/fix-numeric-titles
Fix TV Series parsing containing only numbers.
2025-11-27 16:32:11 +01:00
Niels van Velzen
45669c9b30 Merge pull request #15437 from allmazz/feat/more_file_metadata_tags
Add support for more embedded metadata tags
2025-11-27 16:31:42 +01:00
Niels van Velzen
19c232809e Merge pull request #14950 from nielsvanvelzen/security-remove-has-password
Deprecate HasPassword property on UserDto
2025-11-27 16:31:05 +01:00
Niels van Velzen
301f65af48 Merge pull request #15559 from nielsvanvelzen/disable-legacy-auth
Disable legacy authorization methods by default
2025-11-27 16:30:45 +01:00
Bond-009
082ba58e51 Merge pull request #15630 from jellyfin/renovate/ci-deps
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
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (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 / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (push) Has been cancelled
Update CI dependencies
2025-11-25 18:30:46 +01:00
Bond-009
3b5bdc6bc2 Merge pull request #15246 from JPVenson/feature/AddVersionDisplayStartupUi
Add version to StartupUI
2025-11-25 18:30:27 +01:00
Bond-009
b05e91dba1 Merge pull request #15614 from jellyfin/renovate/polly-monorepo
Update dependency Polly to 8.6.5
2025-11-25 18:26:41 +01:00
renovate[bot]
c7703242e5 Update CI dependencies 2025-11-25 06:39:50 +00:00
Bond-009
21042ad0c2 Merge pull request #15626 from jellyfin/renovate/ci-deps
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (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
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
Update github/codeql-action action to v4.31.5
2025-11-24 19:16:42 +01:00
renovate[bot]
8904551a59 Update github/codeql-action action to v4.31.5 2025-11-24 11:12:00 +00:00
renovate[bot]
cf1ef22367 Update dependency Polly to 8.6.5 2025-11-23 14:03:55 +00:00
rimasx
c08e81c52b Translated using Weblate (Estonian)
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (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 / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / 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/et/
2025-11-23 10:43:48 +00:00
Bond-009
23e66ae1ea Merge pull request #15607 from jellyfin/renovate/z440.atl.core-7.x
Update dependency z440.atl.core to 7.9.0
2025-11-23 10:22:43 +01:00
renovate[bot]
37bbdf3fe7 Update dependency z440.atl.core to 7.9.0 2025-11-22 15:15:12 +00:00
Bond-009
f124223015 Merge pull request #15591 from jellyfin/renovate/actions-checkout-6.x
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - BASE (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (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
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
Update actions/checkout action to v6
2025-11-22 10:22:11 +01:00
Bond-009
9587a9b13c Merge pull request #15566 from jellyfin/renovate/ci-deps
Update github/codeql-action action to v4.31.4
2025-11-22 10:20:48 +01:00
Niels van Velzen
67c67df507 Use async migration 2025-11-20 22:11:55 +01:00
renovate[bot]
569f8cfcfc Update actions/checkout action to v6 2025-11-20 18:58:53 +00:00
Anthony Lavado
aa4ddd139a Add all 10.11 versions to issue template (#15565)
Some checks failed
Tests / run-tests (macos-latest) (push) Has been cancelled
Tests / run-tests (ubuntu-latest) (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (push) Has been cancelled
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
Tests / run-tests (windows-latest) (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (push) Has been cancelled
Stale Issue Labeler / Check for stale issues (push) Has been cancelled
2025-11-18 21:05:43 -05:00
renovate[bot]
8ac97f5471 Update github/codeql-action action to v4.31.4 2025-11-19 01:37:24 +00:00
Bond-009
efabfbc931 Merge pull request #15542 from jellyfin/renovate/ci-deps
Update actions/checkout action to v5.0.1
2025-11-18 22:04:58 +01:00
Bond-009
6b5dc115e8 Merge pull request #15478 from jellyfin/renovate/microsoft
Update Microsoft
2025-11-18 22:01:56 +01:00
Bond-009
2dc0af667e Merge pull request #15477 from jellyfin/renovate/dotnet-monorepo
Update dependency dotnet-ef to v9.0.11
2025-11-18 22:01:49 +01:00
Niels van Velzen
196c243a7d Disable legacy authorization methods by default 2025-11-18 16:17:04 +01:00
Rufis72
55dbff8f30 Translated using Weblate (English (Pirate))
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (push) Has been cancelled
Merge Conflict Labeler / Labeling (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
Project Automation / Project board (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/en@pirate/
2025-11-18 08:19:02 +00:00
theguymadmax
2af43e0131 Backport pull request #15529 from jellyfin/release-10.11.z
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (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
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
Fix movie titles using folder name when NFO saver is enabled

Original-merge: f8e012582a

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:09:14 -05:00
theguymadmax
faf1cea63e Backport pull request #15514 from jellyfin/release-10.11.z
Add 1 minute tolerance for NFO change detection

Original-merge: 6566188e45

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:09:13 -05:00
theguymadmax
7e25089c08 Backport pull request #15508 from jellyfin/release-10.11.z
Fix playlist DateCreated and DateLastMediaAdded not being set

Original-merge: 078f9584ed

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:09:12 -05:00
Iksas
8fa36a38e2 Backport pull request #15502 from jellyfin/release-10.11.z
Fix font extraction for certain transcoding settings

Original-merge: ee34c75386

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:09:10 -05:00
theguymadmax
5b3f29946b Backport pull request #15501 from jellyfin/release-10.11.z
Fix .ignore handling for directories

Original-merge: e8150428b6

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:09:09 -05:00
theguymadmax
c869b5b884 Backport pull request #15493 from jellyfin/release-10.11.z
Remove InheritedTags and update tag filtering logic

Original-merge: 4b38e35bbb

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:09:08 -05:00
CBPJ
a08b6ac266 Backport pull request #15487 from jellyfin/release-10.11.z
Fix gitignore-style not working properly on windows.

Original-merge: 435bb14bb2

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:09:07 -05:00
theguymadmax
4e68a5a078 Backport pull request #15472 from jellyfin/release-10.11.z
Fix series DateLastMediaAdded not updating when new episodes are added

Original-merge: abfbaca336

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:09:06 -05:00
Bond-009
99c68ddd50 Backport pull request #15468 from jellyfin/release-10.11.z
Check if target exists before trying to follow it

Original-merge: 5878b1ffc5

Merged-by: joshuaboniface <joshua@boniface.me>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:09:05 -05:00
Bond-009
d7f628677e Backport pull request #15466 from jellyfin/release-10.11.z
Don't error out when searching for marker files fails

Original-merge: f4a846aa4d

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:09:03 -05:00
theguymadmax
e51680cf56 Backport pull request #15462 from jellyfin/release-10.11.z
Fix NullReferenceException in GetPathProtocol when path is null

Original-merge: 7c1063177f

Merged-by: joshuaboniface <joshua@boniface.me>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:09:02 -05:00
theguymadmax
2e7d7752e9 Backport pull request #15446 from jellyfin/release-10.11.z
Fix AncestorIds not migrating

Original-merge: 177b6464ca

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:09:01 -05:00
IceStormNG
26ac2ccd74 Backport pull request #15441 from jellyfin/release-10.11.z
Fix System.NullReferenceException when people's role is null (10.11.z)

Original-merge: 5a9a8363f4

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

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:08:59 -05:00
theguymadmax
de9e653b73 Backport pull request #15435 from jellyfin/release-10.11.z
Fix search terms using diacritics

Original-merge: 63a3e55297

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:08:58 -05:00
theguymadmax
e34e7a1d0b Backport pull request #15423 from jellyfin/release-10.11.z
Invalidate parent folder's cache on deletion/creation

Original-merge: 49efd68fc7

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:08:57 -05:00
nielsvanvelzen
5a30f108fe Backport pull request #15422 from jellyfin/release-10.11.z
Update branding in Swagger page

Original-merge: d140630208

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:08:56 -05:00
JPVenson
74c9629372 Backport pull request #15413 from jellyfin/release-10.11.z
Fixed missing sort argument

Original-merge: 91c3b1617e

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:08:55 -05:00
theguymadmax
6c5f448787 Backport pull request #15404 from jellyfin/release-10.11.z
Improve season folder parsing

Original-merge: 2e5ced5098

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:08:54 -05:00
Bond-009
f848b8f12c Backport pull request #15390 from jellyfin/release-10.11.z
Don't enforce a minimum amount of free space for the tmp and log dirs

Original-merge: 097cb87f6f

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:08:53 -05:00
theguymadmax
bcec5f2e44 Backport pull request #15381 from jellyfin/release-10.11.z
Fix name filters to use only SortName

Original-merge: 7222910b05

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:08:51 -05:00
theguymadmax
7d05c875f3 Backport pull request #15380 from jellyfin/release-10.11.z
Fix item count display for collapsed items

Original-merge: 8f71922734

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:08:50 -05:00
theguymadmax
c805c5e2b1 Backport pull request #15373 from jellyfin/release-10.11.z
Fix collection grouping in mixed libraries

Original-merge: 13c4517a66

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:08:49 -05:00
evanreichard
c2c4c0adbf Backport pull request #15369 from jellyfin/release-10.11.z
feat(sqlite): add timeout config

Original-merge: c2e5081d64

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:08:48 -05:00
revam
5ea3910af9 Backport pull request #15263 from jellyfin/release-10.11.z
Resolve symlinks for static media source infos

Original-merge: 3b2d64995a

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:08:47 -05:00
theguymadmax
06fb300cff Backport pull request #14955 from jellyfin/release-10.11.z
Fix tmdbid not detected in single movie folder

Original-merge: def5956cd1

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-11-17 14:08:45 -05:00
renovate[bot]
626ab7e00a Update actions/checkout action to v5.0.1 2025-11-17 18:40:00 +00:00
Bond-009
1d140645b0 Merge pull request #15528 from jellyfin/renovate/z440.atl.core-7.x
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (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
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
Update dependency z440.atl.core to 7.8.0
2025-11-17 19:38:20 +01:00
renovate[bot]
52f0c3dd24 Update dependency z440.atl.core to 7.8.0 2025-11-16 17:53:55 +00:00
Bond-009
b8327dbc9f Merge pull request #15480 from jellyfin/renovate/ci-deps
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (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
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
Update CI dependencies
2025-11-16 18:52:54 +01:00
hoanghuy309
d1722936c0 Translated using Weblate (Vietnamese)
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (push) Has been cancelled
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (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 / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / 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/vi/
2025-11-15 06:48:18 +00:00
renovate[bot]
931240a3f5 Update CI dependencies 2025-11-14 01:24:06 +00:00
Grant Alexander
b216a27bfc Translated using Weblate (English (Pirate))
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
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (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
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (push) Has been cancelled
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/en@pirate/
2025-11-12 23:22:20 +00:00
renovate[bot]
8471a67bcd Update Microsoft 2025-11-11 18:42:18 +00:00
renovate[bot]
b8a409195f Update dependency dotnet-ef to v9.0.11 2025-11-11 18:42:10 +00:00
Bond-009
1da67e5e10 Merge pull request #15450 from jellyfin/renovate/fscheck.xunit-3.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
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (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
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (push) Has been cancelled
Update dependency FsCheck.Xunit to 3.3.2
2025-11-09 19:39:59 +01:00
Bond-009
ed1ec7ca6b Merge pull request #15448 from jellyfin/renovate/z440.atl.core-7.x
Update dependency z440.atl.core to 7.7.0
2025-11-09 17:57:55 +01:00
renovate[bot]
3d7a68beb1 Update dependency FsCheck.Xunit to 3.3.2 2025-11-09 15:40:14 +00:00
renovate[bot]
32fc57cf17 Update dependency z440.atl.core to 7.7.0 2025-11-09 10:59:00 +00:00
Bond-009
0598c6eaf6 Merge pull request #15438 from jellyfin/renovate/ci-deps
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (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
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
Update appleboy/ssh-action action to v1.2.3
2025-11-08 17:17:52 +01:00
Andrew
0d7b687da0 Update Jellyfin Server version in issue template (#15398) 2025-11-08 08:30:30 -07:00
renovate[bot]
e69754fd3a Update appleboy/ssh-action action to v1.2.3 2025-11-08 04:18:07 +00:00
Kirill Nikiforov
ac81ddd39a add support for more embedded metadata tags 2025-11-08 02:54:53 +04:00
Diogo Coelho
f693c9d39f Translated using Weblate (Portuguese (Portugal))
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (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
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / 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/pt_PT/
2025-11-06 11:37:15 +00:00
Bond-009
96d72788a1 Merge pull request #15312 from jellyfin/renovate/ci-deps
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (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
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (push) Has been cancelled
Stale Issue Labeler / Check for stale issues (push) Has been cancelled
Update github/codeql-action action to v4.31.2
2025-11-04 21:07:02 +01:00
Bond-009
0d74a95bb8 Merge pull request #15348 from jellyfin/renovate/serilog.sinks.console-6.x
Update dependency Serilog.Sinks.Console to 6.1.1
2025-11-04 21:05:17 +01:00
evanreichard
a7d039b7c6 Backport pull request #15328 from jellyfin/release-10.11.z
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (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
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (push) Has been cancelled
Stale Issue Labeler / Check for stale issues (push) Has been cancelled
fix: in optimistic locking, key off table is locked

Original-merge: b5f0199a25

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Joshua M. Boniface <joshua@boniface.me>
2025-11-02 21:58:46 -05:00
Shadowghost
87b02b1316 Backport pull request #15326 from jellyfin/release-10.11.z
Skip too large extracted season numbers

Original-merge: e7dbb3afec

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Joshua M. Boniface <joshua@boniface.me>
2025-11-02 21:58:45 -05:00
vinnyspb
871de372ff Backport pull request #15325 from jellyfin/release-10.11.z
Update file size when refreshing metadata

Original-merge: f994dd6211

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Joshua M. Boniface <joshua@boniface.me>
2025-11-02 21:58:44 -05:00
crobibero
c9d93b0745 Backport pull request #15322 from jellyfin/release-10.11.z
Fix legacy migration file checks

Original-merge: da254ee968

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Joshua M. Boniface <joshua@boniface.me>
2025-11-02 21:58:43 -05:00
thornbill
1ccd10863e Backport pull request #15254 from jellyfin/release-10.11.z
Update password reset to always return the same response structure

Original-merge: 4ad3141875

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Joshua M. Boniface <joshua@boniface.me>
2025-11-02 21:58:42 -05:00
nyanmisaka
4258df4485 Backport pull request #15247 from jellyfin/release-10.11.z
Ignore initial delay in audio-only containers

Original-merge: 6bf88c049e

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Joshua M. Boniface <joshua@boniface.me>
2025-11-02 21:58:41 -05:00
renovate[bot]
63f06aad94 Update dependency Serilog.Sinks.Console to 6.1.1 2025-11-02 23:14:23 +00:00
Jacky He
ffe82be7a7 Translated using Weblate (Chinese (Traditional Han script, Hong Kong))
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (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
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (push) Has been cancelled
Stale Issue Labeler / Check for stale issues (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/zh_Hant_HK/
2025-10-31 23:39:28 +00:00
Jacky He
23929a3e70 Translated using Weblate (Chinese (Traditional Han script, Hong Kong))
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (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
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / 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/zh_Hant_HK/
2025-10-30 19:34:52 +00:00
renovate[bot]
83d0dbdbcb Update github/codeql-action action to v4.31.2 2025-10-30 18:35:55 +00:00
Battseren Badral
573ce9ceaa Translated using Weblate (Mongolian)
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (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
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/mn/
2025-10-29 22:47:08 +00:00
Jacky He
f21fe9f95e Translated using Weblate (Chinese (Traditional Han script, Hong Kong))
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant_HK/
2025-10-29 22:47:08 +00:00
Battseren Badral
f92eca3efb Translated using Weblate (Mongolian)
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
Stale Issue Labeler / Check for stale issues (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (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
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/mn/
2025-10-29 00:03:00 +00:00
Pascal Wiesmann
7d778d7bef Translated using Weblate (Alemannic)
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (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
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/gsw/
2025-10-28 09:23:31 +00:00
JJBlue
21f65e2e27 Backport pull request #15220 from jellyfin/release-10.11.z
Some checks failed
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
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (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
Skip extracted files in migration if bad timestamp or no access

Original-merge: a305204cfa

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:31 -04:00
theguymadmax
28b0657608 Backport pull request #15217 from jellyfin/release-10.11.z
Normalize paths in database queries

Original-merge: 75f472e6a7

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:30 -04:00
crobibero
a489942454 Backport pull request #15212 from jellyfin/release-10.11.z
Skip invalid database migration

Original-merge: 2966d27c97

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:29 -04:00
Shadowghost
423c2654c0 Backport pull request #15209 from jellyfin/release-10.11.z
Improve symlink handling

Original-merge: e5656af1f2

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:27 -04:00
crobibero
4dc826644d Backport pull request #15197 from jellyfin/release-10.11.z
Filter plugins by id instead of name

Original-merge: 5691eee4f1

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:26 -04:00
crobibero
0f21222a0c Backport pull request #15196 from jellyfin/release-10.11.z
Skip directory entry when restoring from backup

Original-merge: 0e4031ae52

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:25 -04:00
crobibero
570b8b2eb9 Backport pull request #15194 from jellyfin/release-10.11.z
Initialize transcode marker during startup

Original-merge: 81b8b0ca4a

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:24 -04:00
Shadowghost
08fd175f5a Backport pull request #15187 from jellyfin/release-10.11.z
Fix pagination and sorting for folders

Original-merge: 7d1824ea27

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:23 -04:00
gnattu
511b5d9c53 Backport pull request #15177 from jellyfin/release-10.11.z
Make priority class setting more robust

Original-merge: 70c32a26fa

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:22 -04:00
CeruleanRed
6514196e8d Backport pull request #15176 from jellyfin/release-10.11.z
Only save chapters that are within the runtime of the video file

Original-merge: 442af96ed9

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:21 -04:00
crobibero
ed6cb30762 Backport pull request #15170 from jellyfin/release-10.11.z
Clean up BackupService

Original-merge: ac3fa3c376

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:20 -04:00
crobibero
232c0399e2 Backport pull request #15164 from jellyfin/release-10.11.z
Fix XmlOutputFormatter

Original-merge: 2b94bb54aa

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:19 -04:00
nyanmisaka
dbb015441f Backport pull request #15144 from jellyfin/release-10.11.z
Fix videos with cropping metadata are probed as anamorphic

Original-merge: 175ee12bbc

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:18 -04:00
theguymadmax
4c1c160990 Backport pull request #15133 from jellyfin/release-10.11.z
Play selected song first with instant mix

Original-merge: 1520a697ad

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:17 -04:00
MBR-0001
0931d6e4de Backport pull request #15126 from jellyfin/release-10.11.z
Fix Has(Imdb/Tmdb/Tvdb)Id checks

Original-merge: 14b3085ff1

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:15 -04:00
ivanjx
3f2ebc4179 Backport pull request #15113 from jellyfin/release-10.11.z
Add season number fallback for OMDB and TMDB plugins

Original-merge: 618ec4543e

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:14 -04:00
Shadowghost
14e8194581 Backport pull request #15112 from jellyfin/release-10.11.z
Skip extracted files in migration if bad timestamp or no access

Original-merge: 7a1c1cd342

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:13 -04:00
theguymadmax
3c4dc16003 Backport pull request #15102 from jellyfin/release-10.11.z
Make season paths case-insensitive

Original-merge: 305b0fdca3

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:12 -04:00
Bond-009
54d28d9842 Backport pull request #15098 from jellyfin/release-10.11.z
Lower required tmp dir size to 512MiB

Original-merge: 0a6e8146be

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:11 -04:00
theguymadmax
adfa520057 Backport pull request #15087 from jellyfin/release-10.11.z
Optimize WhereReferencedItemMultipleTypes filtering

Original-merge: a5bc4524d8

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:10 -04:00
theguymadmax
5deb69b23f Backport pull request #15083 from jellyfin/release-10.11.z
Fix LiveTV images not saving to database

Original-merge: d738386fe2

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:09 -04:00
nyanmisaka
348b2992d7 Backport pull request #15072 from jellyfin/release-10.11.z
Reject stream copy of HDR10+ video if the client does not support HDR10

Original-merge: a725220c21

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:08 -04:00
gnattu
9f8fb6d588 Backport pull request #15055 from jellyfin/release-10.11.z
Log the message more clear when network manager is not ready

Original-merge: a245605152

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:07 -04:00
Shadowghost
cee16d47cb Backport pull request #15054 from jellyfin/release-10.11.z
Speed-up trickplay migration

Original-merge: ca830d5be7

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:06 -04:00
Shadowghost
9e53f46ad2 Backport pull request #15032 from jellyfin/release-10.11.z
Skip invalid keyframe cache data

Original-merge: f4a53209f4

Merged-by: crobibero <cody@robibe.ro>

Backported-by: Bond_009 <bond.009@outlook.com>
2025-10-27 15:43:04 -04:00
Joshua M. Boniface
53dfcae1a6 Merge pull request #15236 from joshuaboniface/codeowners
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (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
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
Update CODEOWNERS to capture bump_version
2025-10-27 09:10:47 -04:00
JPVenson
81f1cc78b2 Add version to StartupUI 2025-10-27 13:01:52 +00:00
kreaxv
efd659412f Translated using Weblate (Mongolian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/mn/
2025-10-27 09:41:00 +00:00
Joshua M. Boniface
c31ea251c4 Improve handling of .github dir 2025-10-26 22:17:33 -04:00
Joshua M. Boniface
285e7c6c4f Update CODEOWNERS to capture bump_version 2025-10-26 22:07:46 -04:00
Joshua M. Boniface
c274336563 Bump version to 10.12.0 (for real this time)
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (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
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
2025-10-26 21:52:03 -04:00
Joshua M. Boniface
d5fd5dfe6a Fix bump_version to handle spaced filename 2025-10-26 21:51:41 -04:00
Kevin G
42ddcfa565 Add milliseconds to default console output format
Signed-off-by: Kevin G <kevin@myplaceonline.com>
2025-10-26 10:29:29 -05:00
Bond-009
6fa69f9fe5 Merge pull request #15206 from jellyfin/renovate/ci-deps
Some checks failed
Stale Issue Labeler / Check for stale issues (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - BASE (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (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
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
Update danielpalme/ReportGenerator-GitHub-Action action to v5.4.18
2025-10-26 14:50:42 +01:00
Bond-009
0b876365a1 Merge pull request #15205 from jellyfin/renovate/z440.atl.core-7.x
Update dependency z440.atl.core to 7.6.0
2025-10-26 14:39:09 +01:00
Battseren Badral
cdc8325c7b Translated using Weblate (Mongolian)
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (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
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/mn/
2025-10-25 21:55:13 +00:00
renovate[bot]
a6a8e29916 Update danielpalme/ReportGenerator-GitHub-Action action to v5.4.18 2025-10-25 15:32:59 +00:00
renovate[bot]
6fd3847298 Update dependency z440.atl.core to 7.6.0 2025-10-25 15:12:02 +00:00
Bond-009
3ff516a430 Merge pull request #15191 from jellyfin/renovate/ci-deps
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (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
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
Update github/codeql-action action to v4.31.0
2025-10-25 11:23:34 +02:00
Bond-009
d8591840f3 Merge pull request #15192 from jellyfin/renovate/major-github-artifact-actions
Update GitHub Artifact Actions (major)
2025-10-25 11:22:40 +02:00
HanHwanHo
c5affbbf71 Translated using Weblate (Korean)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ko/
2025-10-25 07:46:36 +00:00
renovate[bot]
788f090f27 Update GitHub Artifact Actions 2025-10-24 23:59:16 +00:00
renovate[bot]
0e3b6652b3 Update github/codeql-action action to v4.31.0 2025-10-24 23:59:06 +00:00
desibooklover
d167d59c23 Translated using Weblate (Punjabi)
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (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
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / 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/pa/
2025-10-23 03:50:04 +00:00
desibooklover
f58b4860f7 Translated using Weblate (Hindi)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hi/
2025-10-23 03:50:04 +00:00
Grant Alexander
96b7fc0ac0 Translated using Weblate (Spanish (Mexico))
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
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (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
Project Automation / Project board (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (push) Has been cancelled
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es_MX/
2025-10-21 15:20:51 +00:00
oddife
c8ad861590 Translated using Weblate (Marathi)
Some checks failed
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - BASE (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (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
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/mr/
2025-10-20 21:27:58 +00:00
Jellyfin Release Bot
1a1a24cfff Bump version to 10.12.0
Some checks failed
Stale PR Check / Check PRs with merge conflicts (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
OpenAPI / OpenAPI - HEAD (push) Has been cancelled
OpenAPI / OpenAPI - BASE (push) Has been cancelled
OpenAPI / OpenAPI - Difference (push) Has been cancelled
OpenAPI / OpenAPI - Publish Unstable Spec (push) Has been cancelled
OpenAPI / OpenAPI - Publish Stable Spec (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
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
2025-10-19 20:45:13 -04:00
Niels van Velzen
d43db230fa Add back UpdateUserPassword_Empty_RemoveSetPassword test 2025-10-19 09:45:55 +02:00
Niels van Velzen
0fb6d930e1 Deprecate HasPassword property on UserDto 2025-10-05 11:10:36 +02:00
Cody Engel
2508e8349b update summary docs
Signed-off-by: Cody Engel <cengel815@gmail.com>
2025-09-23 08:22:00 -06:00
Cody Engel
bd9a44ce7d remove explicit ‘-‘ support in series name 2025-09-20 18:00:44 -06:00
Cody Engel
da31d0c6a6 support series that are numeric only
updates SeriesResolver to handle series names that only contain numbers such as 1923.
2025-09-20 14:04:00 -06:00
sususu98
aebabb1580 style: fix return statement indentation in StreamInfo.cs 2025-04-24 14:25:12 +08:00
sususu98
d5402718b7 Merge branch 'jellyfin:master' into fix/strm-local-subtitle-url 2025-04-24 14:18:17 +08:00
sususu98
fd108ff528 Style: Fix indentation in StreamInfo.cs 2025-04-24 14:17:33 +08:00
sususu98
22ce1f25d0 refactor(StreamInfo): reorganize subtitle URL logic and conditions
# Conflicts:
#	MediaBrowser.Model/Dlna/StreamInfo.cs
2025-04-23 18:18:38 +08:00
107 changed files with 915 additions and 614 deletions

View File

@@ -3,7 +3,7 @@
"isRoot": true, "isRoot": true,
"tools": { "tools": {
"dotnet-ef": { "dotnet-ef": {
"version": "9.0.10", "version": "9.0.11",
"commands": [ "commands": [
"dotnet-ef" "dotnet-ef"
] ]

15
.github/CODEOWNERS vendored
View File

@@ -1,4 +1,11 @@
# Joshua must review all changes to deployment and build.sh # Joshua must review all changes to bump_version and any files it touches
.ci/* @joshuaboniface bump_version @joshuaboniface
deployment/* @joshuaboniface .github/ISSUE_TEMPLATE @joshuaboniface
build.sh @joshuaboniface MediaBrowser.Common/MediaBrowser.Common.csproj @joshuaboniface
Jellyfin.Data/Jellyfin.Data.csproj @joshuaboniface
MediaBrowser.Controller/MediaBrowser.Controller.csproj @joshuaboniface
MediaBrowser.Model/MediaBrowser.Model.csproj @joshuaboniface
Emby.Naming/Emby.Naming.csproj @joshuaboniface
src/Jellyfin.Extensions/Jellyfin.Extensions.csproj @joshuaboniface
# Core must approve all changes within the repo config
.github/ @jellyfin/core

View File

@@ -87,7 +87,10 @@ body:
label: Jellyfin Server version label: Jellyfin Server version
description: What version of Jellyfin are you using? description: What version of Jellyfin are you using?
options: options:
- 10.10.0+ - 10.11.3
- 10.11.2
- 10.11.1
- 10.11.0
- Master - Master
- Unstable - Unstable
- Older* - Older*

View File

@@ -20,18 +20,18 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0 uses: actions/setup-dotnet@2016bd2012dba4e32de620c46fe006a3ac9f0602 # v5.0.1
with: with:
dotnet-version: '9.0.x' dotnet-version: '9.0.x'
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@16140ae1a102900babc80a33c44059580f687047 # v4.30.9 uses: github/codeql-action/init@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
queries: +security-extended queries: +security-extended
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@16140ae1a102900babc80a33c44059580f687047 # v4.30.9 uses: github/codeql-action/autobuild@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@16140ae1a102900babc80a33c44059580f687047 # v4.30.9 uses: github/codeql-action/analyze@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5

View File

@@ -1,6 +1,6 @@
name: ABI Compatibility name: ABI Compatibility
on: on:
pull_request_target: pull_request:
permissions: {} permissions: {}
@@ -11,13 +11,13 @@ jobs:
permissions: read-all permissions: read-all
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with: with:
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
repository: ${{ github.event.pull_request.head.repo.full_name }} repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0 uses: actions/setup-dotnet@2016bd2012dba4e32de620c46fe006a3ac9f0602 # v5.0.1
with: with:
dotnet-version: '9.0.x' dotnet-version: '9.0.x'
@@ -26,7 +26,7 @@ jobs:
dotnet build Jellyfin.Server -o ./out dotnet build Jellyfin.Server -o ./out
- name: Upload Head - name: Upload Head
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with: with:
name: abi-head name: abi-head
retention-days: 14 retention-days: 14
@@ -40,14 +40,14 @@ jobs:
permissions: read-all permissions: read-all
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with: with:
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
repository: ${{ github.event.pull_request.head.repo.full_name }} repository: ${{ github.event.pull_request.head.repo.full_name }}
fetch-depth: 0 fetch-depth: 0
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0 uses: actions/setup-dotnet@2016bd2012dba4e32de620c46fe006a3ac9f0602 # v5.0.1
with: with:
dotnet-version: '9.0.x' dotnet-version: '9.0.x'
@@ -65,7 +65,7 @@ jobs:
dotnet build Jellyfin.Server -o ./out dotnet build Jellyfin.Server -o ./out
- name: Upload Head - name: Upload Head
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with: with:
name: abi-base name: abi-base
retention-days: 14 retention-days: 14
@@ -77,7 +77,7 @@ jobs:
pull-requests: write # to create or update comment (peter-evans/create-or-update-comment) pull-requests: write # to create or update comment (peter-evans/create-or-update-comment)
name: ABI - Difference name: ABI - Difference
if: ${{ github.event_name == 'pull_request_target' }} if: ${{ github.event_name == 'pull_request' }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: needs:
- abi-head - abi-head
@@ -85,13 +85,13 @@ jobs:
steps: steps:
- name: Download abi-head - name: Download abi-head
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with: with:
name: abi-head name: abi-head
path: abi-head path: abi-head
- name: Download abi-base - name: Download abi-base
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with: with:
name: abi-base name: abi-base
path: abi-base path: abi-base

View File

@@ -5,7 +5,7 @@ on:
- master - master
tags: tags:
- 'v*' - 'v*'
pull_request_target: pull_request:
permissions: {} permissions: {}
@@ -16,18 +16,18 @@ jobs:
permissions: read-all permissions: read-all
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with: with:
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
repository: ${{ github.event.pull_request.head.repo.full_name }} repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0 uses: actions/setup-dotnet@2016bd2012dba4e32de620c46fe006a3ac9f0602 # v5.0.1
with: with:
dotnet-version: '9.0.x' dotnet-version: '9.0.x'
- name: Generate openapi.json - name: Generate openapi.json
run: dotnet test tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj -c Release --filter "Jellyfin.Server.Integration.Tests.OpenApiSpecTests" run: dotnet test tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj -c Release --filter "Jellyfin.Server.Integration.Tests.OpenApiSpecTests"
- name: Upload openapi.json - name: Upload openapi.json
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with: with:
name: openapi-head name: openapi-head
retention-days: 14 retention-days: 14
@@ -41,7 +41,7 @@ jobs:
permissions: read-all permissions: read-all
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with: with:
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
repository: ${{ github.event.pull_request.head.repo.full_name }} repository: ${{ github.event.pull_request.head.repo.full_name }}
@@ -55,13 +55,13 @@ jobs:
ANCESTOR_REF=$(git merge-base upstream/${{ github.base_ref }} origin/$HEAD_REF) ANCESTOR_REF=$(git merge-base upstream/${{ github.base_ref }} origin/$HEAD_REF)
git checkout --progress --force $ANCESTOR_REF git checkout --progress --force $ANCESTOR_REF
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0 uses: actions/setup-dotnet@2016bd2012dba4e32de620c46fe006a3ac9f0602 # v5.0.1
with: with:
dotnet-version: '9.0.x' dotnet-version: '9.0.x'
- name: Generate openapi.json - name: Generate openapi.json
run: dotnet test tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj -c Release --filter "Jellyfin.Server.Integration.Tests.OpenApiSpecTests" run: dotnet test tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj -c Release --filter "Jellyfin.Server.Integration.Tests.OpenApiSpecTests"
- name: Upload openapi.json - name: Upload openapi.json
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with: with:
name: openapi-base name: openapi-base
retention-days: 14 retention-days: 14
@@ -73,19 +73,19 @@ jobs:
pull-requests: write # to create or update comment (peter-evans/create-or-update-comment) pull-requests: write # to create or update comment (peter-evans/create-or-update-comment)
name: OpenAPI - Difference name: OpenAPI - Difference
if: ${{ github.event_name == 'pull_request_target' }} if: ${{ github.event_name == 'pull_request' }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: needs:
- openapi-head - openapi-head
- openapi-base - openapi-base
steps: steps:
- name: Download openapi-head - name: Download openapi-head
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with: with:
name: openapi-head name: openapi-head
path: openapi-head path: openapi-head
- name: Download openapi-base - name: Download openapi-base
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with: with:
name: openapi-base name: openapi-base
path: openapi-base path: openapi-base
@@ -148,7 +148,7 @@ jobs:
publish-unstable: publish-unstable:
name: OpenAPI - Publish Unstable Spec name: OpenAPI - Publish Unstable Spec
if: ${{ github.event_name != 'pull_request_target' && !startsWith(github.ref, 'refs/tags/v') && contains(github.repository_owner, 'jellyfin') }} if: ${{ github.event_name != 'pull_request' && !startsWith(github.ref, 'refs/tags/v') && contains(github.repository_owner, 'jellyfin') }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: needs:
- openapi-head - openapi-head
@@ -158,7 +158,7 @@ jobs:
run: |- run: |-
echo "JELLYFIN_VERSION=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_ENV echo "JELLYFIN_VERSION=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_ENV
- name: Download openapi-head - name: Download openapi-head
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with: with:
name: openapi-head name: openapi-head
path: openapi-head path: openapi-head
@@ -172,7 +172,7 @@ jobs:
strip_components: 1 strip_components: 1
target: "/srv/incoming/openapi/unstable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}" target: "/srv/incoming/openapi/unstable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}"
- name: Move openapi.json (unstable) into place - name: Move openapi.json (unstable) into place
uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2 uses: appleboy/ssh-action@823bd89e131d8d508129f9443cad5855e9ba96f0 # v1.2.4
with: with:
host: "${{ secrets.REPO_HOST }}" host: "${{ secrets.REPO_HOST }}"
username: "${{ secrets.REPO_USER }}" username: "${{ secrets.REPO_USER }}"
@@ -220,7 +220,7 @@ jobs:
run: |- run: |-
echo "JELLYFIN_VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV echo "JELLYFIN_VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
- name: Download openapi-head - name: Download openapi-head
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with: with:
name: openapi-head name: openapi-head
path: openapi-head path: openapi-head
@@ -234,7 +234,7 @@ jobs:
strip_components: 1 strip_components: 1
target: "/srv/incoming/openapi/stable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}" target: "/srv/incoming/openapi/stable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}"
- name: Move openapi.json (stable) into place - name: Move openapi.json (stable) into place
uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2 uses: appleboy/ssh-action@823bd89e131d8d508129f9443cad5855e9ba96f0 # v1.2.4
with: with:
host: "${{ secrets.REPO_HOST }}" host: "${{ secrets.REPO_HOST }}"
username: "${{ secrets.REPO_USER }}" username: "${{ secrets.REPO_USER }}"

View File

@@ -20,9 +20,9 @@ jobs:
runs-on: "${{ matrix.os }}" runs-on: "${{ matrix.os }}"
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0 - uses: actions/setup-dotnet@2016bd2012dba4e32de620c46fe006a3ac9f0602 # v5.0.1
with: with:
dotnet-version: ${{ env.SDK_VERSION }} dotnet-version: ${{ env.SDK_VERSION }}
@@ -35,7 +35,7 @@ jobs:
--verbosity minimal --verbosity minimal
- name: Merge code coverage results - name: Merge code coverage results
uses: danielpalme/ReportGenerator-GitHub-Action@9870ed167742d546b99962ff815fcc1098355ed8 # v5.4.17 uses: danielpalme/ReportGenerator-GitHub-Action@dcdfb6e704e87df6b2ed0cf123a6c9f69e364869 # v5.5.0
with: with:
reports: "**/coverage.cobertura.xml" reports: "**/coverage.cobertura.xml"
targetdir: "merged/" targetdir: "merged/"

View File

@@ -4,7 +4,7 @@ on:
types: types:
- created - created
- edited - edited
pull_request_target: pull_request:
types: types:
- labeled - labeled
- synchronize - synchronize
@@ -24,7 +24,7 @@ jobs:
reactions: '+1' reactions: '+1'
- name: Checkout the latest code - name: Checkout the latest code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with: with:
token: ${{ secrets.JF_BOT_TOKEN }} token: ${{ secrets.JF_BOT_TOKEN }}
fetch-depth: 0 fetch-depth: 0
@@ -40,11 +40,11 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: pull in script - name: pull in script
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with: with:
repository: jellyfin/jellyfin-triage-script repository: jellyfin/jellyfin-triage-script
- name: install python - name: install python
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with: with:
python-version: '3.14' python-version: '3.14'
cache: 'pip' cache: 'pip'

View File

@@ -10,11 +10,11 @@ jobs:
issues: write issues: write
steps: steps:
- name: pull in script - name: pull in script
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with: with:
repository: jellyfin/jellyfin-triage-script repository: jellyfin/jellyfin-triage-script
- name: install python - name: install python
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with: with:
python-version: '3.14' python-version: '3.14'
cache: 'pip' cache: 'pip'

View File

@@ -4,7 +4,7 @@ on:
push: push:
branches: branches:
- master - master
pull_request_target: pull_request:
issue_comment: issue_comment:
permissions: {} permissions: {}

View File

@@ -4,7 +4,7 @@ on:
push: push:
branches: branches:
- master - master
pull_request_target: pull_request:
issue_comment: issue_comment:
permissions: {} permissions: {}
@@ -16,7 +16,7 @@ jobs:
steps: steps:
- name: Apply label - name: Apply label
uses: eps1lon/actions-label-merge-conflict@1df065ebe6e3310545d4f4c4e862e43bdca146f0 # v3.0.3 uses: eps1lon/actions-label-merge-conflict@1df065ebe6e3310545d4f4c4e862e43bdca146f0 # v3.0.3
if: ${{ github.event_name == 'push' || github.event_name == 'pull_request_target'}} if: ${{ github.event_name == 'push' || github.event_name == 'pull_request'}}
with: with:
dirtyLabel: 'merge conflict' dirtyLabel: 'merge conflict'
commentOnDirty: 'This pull request has merge conflicts. Please resolve the conflicts so the PR can be successfully reviewed and merged.' commentOnDirty: 'This pull request has merge conflicts. Please resolve the conflicts so the PR can be successfully reviewed and merged.'

View File

@@ -33,7 +33,7 @@ jobs:
yq-version: v4.9.8 yq-version: v4.9.8
- name: Checkout Repository - name: Checkout Repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with: with:
ref: ${{ env.TAG_BRANCH }} ref: ${{ env.TAG_BRANCH }}
@@ -66,7 +66,7 @@ jobs:
NEXT_VERSION: ${{ github.event.inputs.NEXT_VERSION }} NEXT_VERSION: ${{ github.event.inputs.NEXT_VERSION }}
steps: steps:
- name: Checkout Repository - name: Checkout Repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with: with:
ref: ${{ env.TAG_BRANCH }} ref: ${{ env.TAG_BRANCH }}

View File

@@ -17,7 +17,7 @@
<PackageVersion Include="Diacritics" Version="4.0.17" /> <PackageVersion Include="Diacritics" Version="4.0.17" />
<PackageVersion Include="DiscUtils.Udf" Version="0.16.13" /> <PackageVersion Include="DiscUtils.Udf" Version="0.16.13" />
<PackageVersion Include="DotNet.Glob" Version="3.1.3" /> <PackageVersion Include="DotNet.Glob" Version="3.1.3" />
<PackageVersion Include="FsCheck.Xunit" Version="3.3.1" /> <PackageVersion Include="FsCheck.Xunit" Version="3.3.2" />
<PackageVersion Include="HarfBuzzSharp.NativeAssets.Linux" Version="8.3.1.1" /> <PackageVersion Include="HarfBuzzSharp.NativeAssets.Linux" Version="8.3.1.1" />
<PackageVersion Include="ICU4N.Transliterator" Version="60.1.0-alpha.356" /> <PackageVersion Include="ICU4N.Transliterator" Version="60.1.0-alpha.356" />
<PackageVersion Include="IDisposableAnalyzers" Version="4.0.8" /> <PackageVersion Include="IDisposableAnalyzers" Version="4.0.8" />
@@ -26,33 +26,33 @@
<PackageVersion Include="libse" Version="4.0.12" /> <PackageVersion Include="libse" Version="4.0.12" />
<PackageVersion Include="LrcParser" Version="2025.623.0" /> <PackageVersion Include="LrcParser" Version="2025.623.0" />
<PackageVersion Include="MetaBrainz.MusicBrainz" Version="6.1.0" /> <PackageVersion Include="MetaBrainz.MusicBrainz" Version="6.1.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="9.0.10" /> <PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="9.0.11" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.10" /> <PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.11" />
<PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="4.14.0" /> <PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="4.14.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.14.0" /> <PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.14.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" /> <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" /> <PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.10" /> <PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.11" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.10" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.11" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.10" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.11" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.10" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.11" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.10" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.11" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.10" /> <PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.11" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.10" /> <PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.11" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.10" /> <PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.11" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.10" /> <PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.11" />
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.10" /> <PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.11" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="9.0.10" /> <PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="9.0.11" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.10" /> <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.11" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.10" /> <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.11" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="9.0.10" /> <PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="9.0.11" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="9.0.10" /> <PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="9.0.11" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.10" /> <PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.11" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.10" /> <PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.11" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.10" /> <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.11" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.10" /> <PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.11" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.10" /> <PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.11" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.0" /> <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageVersion Include="MimeTypes" Version="2.5.2" /> <PackageVersion Include="MimeTypes" Version="2.5.2" />
<PackageVersion Include="Morestachio" Version="5.0.1.631" /> <PackageVersion Include="Morestachio" Version="5.0.1.631" />
<PackageVersion Include="Moq" Version="4.18.4" /> <PackageVersion Include="Moq" Version="4.18.4" />
@@ -62,13 +62,13 @@
<PackageVersion Include="prometheus-net.AspNetCore" Version="8.2.1" /> <PackageVersion Include="prometheus-net.AspNetCore" Version="8.2.1" />
<PackageVersion Include="prometheus-net.DotNetRuntime" Version="4.4.1" /> <PackageVersion Include="prometheus-net.DotNetRuntime" Version="4.4.1" />
<PackageVersion Include="prometheus-net" Version="8.2.1" /> <PackageVersion Include="prometheus-net" Version="8.2.1" />
<PackageVersion Include="Polly" Version="8.6.4" /> <PackageVersion Include="Polly" Version="8.6.5" />
<PackageVersion Include="Serilog.AspNetCore" Version="9.0.0" /> <PackageVersion Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageVersion Include="Serilog.Enrichers.Thread" Version="4.0.0" /> <PackageVersion Include="Serilog.Enrichers.Thread" Version="4.0.0" />
<PackageVersion Include="Serilog.Expressions" Version="5.0.0" /> <PackageVersion Include="Serilog.Expressions" Version="5.0.0" />
<PackageVersion Include="Serilog.Settings.Configuration" Version="9.0.0" /> <PackageVersion Include="Serilog.Settings.Configuration" Version="9.0.0" />
<PackageVersion Include="Serilog.Sinks.Async" Version="2.1.0" /> <PackageVersion Include="Serilog.Sinks.Async" Version="2.1.0" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" /> <PackageVersion Include="Serilog.Sinks.Console" Version="6.1.1" />
<PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" /> <PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageVersion Include="Serilog.Sinks.Graylog" Version="3.1.1" /> <PackageVersion Include="Serilog.Sinks.Graylog" Version="3.1.1" />
<PackageVersion Include="SerilogAnalyzer" Version="0.15.0" /> <PackageVersion Include="SerilogAnalyzer" Version="0.15.0" />
@@ -84,11 +84,11 @@
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.2.3" /> <PackageVersion Include="Swashbuckle.AspNetCore" Version="6.2.3" />
<PackageVersion Include="System.Globalization" Version="4.3.0" /> <PackageVersion Include="System.Globalization" Version="4.3.0" />
<PackageVersion Include="System.Linq.Async" Version="6.0.3" /> <PackageVersion Include="System.Linq.Async" Version="6.0.3" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.10" /> <PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.11" />
<PackageVersion Include="System.Text.Json" Version="9.0.10" /> <PackageVersion Include="System.Text.Json" Version="9.0.11" />
<PackageVersion Include="System.Threading.Tasks.Dataflow" Version="9.0.10" /> <PackageVersion Include="System.Threading.Tasks.Dataflow" Version="9.0.11" />
<PackageVersion Include="TagLibSharp" Version="2.3.0" /> <PackageVersion Include="TagLibSharp" Version="2.3.0" />
<PackageVersion Include="z440.atl.core" Version="7.6.0" /> <PackageVersion Include="z440.atl.core" Version="7.9.0" />
<PackageVersion Include="TMDbLib" Version="2.3.0" /> <PackageVersion Include="TMDbLib" Version="2.3.0" />
<PackageVersion Include="UTF.Unknown" Version="2.6.0" /> <PackageVersion Include="UTF.Unknown" Version="2.6.0" />
<PackageVersion Include="Xunit.Priority" Version="1.1.6" /> <PackageVersion Include="Xunit.Priority" Version="1.1.6" />
@@ -96,4 +96,4 @@
<PackageVersion Include="Xunit.SkippableFact" Version="1.5.23" /> <PackageVersion Include="Xunit.SkippableFact" Version="1.5.23" />
<PackageVersion Include="xunit" Version="2.9.3" /> <PackageVersion Include="xunit" Version="2.9.3" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

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

View File

@@ -10,12 +10,17 @@ namespace Emby.Naming.TV
/// </summary> /// </summary>
public static partial class SeasonPathParser public static partial class SeasonPathParser
{ {
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)*|[tT](?:emporada)*|[kK](?:ausi)*|[Сс](?:езон)*)\s*(?<rightpart>.*)$", RegexOptions.IgnoreCase)]
private static partial Regex ProcessPre(); 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+)(?!\s*[Ee]\d+))(?<rightpart>.*)$", RegexOptions.IgnoreCase)] [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)]
private static partial Regex ProcessPost(); private static partial Regex ProcessPost();
[GeneratedRegex(@"[sS](\d{1,4})(?!\d|[eE]\d)(?=\.|_|-|\[|\]|\s|$)", RegexOptions.None)]
private static partial Regex SeasonPrefix();
/// <summary> /// <summary>
/// Attempts to parse season number from path. /// Attempts to parse season number from path.
/// </summary> /// </summary>
@@ -56,44 +61,34 @@ namespace Emby.Naming.TV
bool supportSpecialAliases, bool supportSpecialAliases,
bool supportNumericSeasonFolders) bool supportNumericSeasonFolders)
{ {
string filename = Path.GetFileName(path); var fileName = Path.GetFileName(path);
filename = Regex.Replace(filename, "[ ._-]", string.Empty);
var seasonPrefixMatch = SeasonPrefix().Match(fileName);
if (seasonPrefixMatch.Success &&
int.TryParse(seasonPrefixMatch.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var val))
{
return (val, true);
}
string filename = CleanNameRegex.Replace(fileName, string.Empty);
if (parentFolderName is not null) if (parentFolderName is not null)
{ {
parentFolderName = Regex.Replace(parentFolderName, "[ ._-]", string.Empty); var cleanParent = CleanNameRegex.Replace(parentFolderName, string.Empty);
filename = filename.Replace(parentFolderName, string.Empty, StringComparison.OrdinalIgnoreCase); filename = filename.Replace(cleanParent, string.Empty, StringComparison.OrdinalIgnoreCase);
} }
if (supportSpecialAliases) if (supportSpecialAliases &&
(filename.Equals("specials", StringComparison.OrdinalIgnoreCase) ||
filename.Equals("extras", StringComparison.OrdinalIgnoreCase)))
{ {
if (string.Equals(filename, "specials", StringComparison.OrdinalIgnoreCase)) return (0, true);
{
return (0, true);
}
if (string.Equals(filename, "extras", StringComparison.OrdinalIgnoreCase))
{
return (0, true);
}
} }
if (supportNumericSeasonFolders) if (supportNumericSeasonFolders &&
int.TryParse(filename, NumberStyles.Integer, CultureInfo.InvariantCulture, out val))
{ {
if (int.TryParse(filename, NumberStyles.Integer, CultureInfo.InvariantCulture, out var val)) return (val, true);
{
return (val, true);
}
}
if (filename.Length > 0 && (filename[0] == 'S' || filename[0] == 's'))
{
var testFilename = filename.AsSpan()[1..];
if (int.TryParse(testFilename, NumberStyles.Integer, CultureInfo.InvariantCulture, out var val))
{
return (val, true);
}
} }
var preMatch = ProcessPre().Match(filename); var preMatch = ProcessPre().Match(filename);
@@ -113,8 +108,10 @@ namespace Emby.Naming.TV
var numberString = match.Groups["seasonnumber"]; var numberString = match.Groups["seasonnumber"];
if (numberString.Success) if (numberString.Success)
{ {
var seasonNumber = int.Parse(numberString.Value, CultureInfo.InvariantCulture); if (int.TryParse(numberString.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var seasonNumber))
return (seasonNumber, true); {
return (seasonNumber, true);
}
} }
return (null, false); return (null, false);

View File

@@ -17,6 +17,13 @@ namespace Emby.Naming.TV
[GeneratedRegex(@"((?<a>[^\._]{2,})[\._]*)|([\._](?<b>[^\._]{2,}))")] [GeneratedRegex(@"((?<a>[^\._]{2,})[\._]*)|([\._](?<b>[^\._]{2,}))")]
private static partial Regex SeriesNameRegex(); private static partial Regex SeriesNameRegex();
/// <summary>
/// Regex that matches titles with year in parentheses. Captures the title (which may be
/// numeric) before the year, i.e. turns "1923 (2022)" into "1923".
/// </summary>
[GeneratedRegex(@"(?<title>.+?)\s*\(\d{4}\)")]
private static partial Regex TitleWithYearRegex();
/// <summary> /// <summary>
/// Resolve information about series from path. /// Resolve information about series from path.
/// </summary> /// </summary>
@@ -27,6 +34,20 @@ namespace Emby.Naming.TV
{ {
string seriesName = Path.GetFileName(path); string seriesName = Path.GetFileName(path);
// First check if the filename matches a title with year pattern (handles numeric titles)
if (!string.IsNullOrEmpty(seriesName))
{
var titleWithYearMatch = TitleWithYearRegex().Match(seriesName);
if (titleWithYearMatch.Success)
{
seriesName = titleWithYearMatch.Groups["title"].Value.Trim();
return new SeriesInfo(path)
{
Name = seriesName
};
}
}
SeriesPathParserResult result = SeriesPathParser.Parse(options, path); SeriesPathParserResult result = SeriesPathParser.Parse(options, path);
if (result.Success) if (result.Success)
{ {

View File

@@ -107,10 +107,20 @@ namespace Emby.Server.Implementations.AppBase
private void CheckOrCreateMarker(string path, string markerName, bool recursive = false) private void CheckOrCreateMarker(string path, string markerName, bool recursive = false)
{ {
var otherMarkers = GetMarkers(path, recursive).FirstOrDefault(e => Path.GetFileName(e) != markerName); string? otherMarkers = null;
try
{
otherMarkers = GetMarkers(path, recursive).FirstOrDefault(e => !Path.GetFileName(e.AsSpan()).Equals(markerName, StringComparison.OrdinalIgnoreCase));
}
catch
{
// Error while checking for marker files, assume none exist and keep going
// TODO: add some logging
}
if (otherMarkers is not null) if (otherMarkers is not null)
{ {
throw new InvalidOperationException($"Exepected to find only {markerName} but found marker for {otherMarkers}."); throw new InvalidOperationException($"Expected to find only {markerName} but found marker for {otherMarkers}.");
} }
var markerPath = Path.Combine(path, markerName); var markerPath = Path.Combine(path, markerName);

View File

@@ -6,6 +6,7 @@ using System.Linq;
using System.Security; using System.Security;
using Jellyfin.Extensions; using Jellyfin.Extensions;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@@ -260,7 +261,7 @@ namespace Emby.Server.Implementations.IO
{ {
try try
{ {
var targetFileInfo = (FileInfo?)fileInfo.ResolveLinkTarget(returnFinalTarget: true); var targetFileInfo = FileSystemHelper.ResolveLinkTarget(fileInfo, returnFinalTarget: true);
if (targetFileInfo is not null) if (targetFileInfo is not null)
{ {
result.Exists = targetFileInfo.Exists; result.Exists = targetFileInfo.Exists;

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.IO; using System.IO;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Resolvers; using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
@@ -11,28 +12,24 @@ namespace Emby.Server.Implementations.Library;
/// </summary> /// </summary>
public class DotIgnoreIgnoreRule : IResolverIgnoreRule public class DotIgnoreIgnoreRule : IResolverIgnoreRule
{ {
private static readonly bool IsWindows = OperatingSystem.IsWindows();
private static FileInfo? FindIgnoreFile(DirectoryInfo directory) private static FileInfo? FindIgnoreFile(DirectoryInfo directory)
{ {
var ignoreFile = new FileInfo(Path.Join(directory.FullName, ".ignore")); for (var current = directory; current is not null; current = current.Parent)
if (ignoreFile.Exists)
{ {
return ignoreFile; var ignorePath = Path.Join(current.FullName, ".ignore");
if (File.Exists(ignorePath))
{
return new FileInfo(ignorePath);
}
} }
var parentDir = directory.Parent; return null;
if (parentDir is null)
{
return null;
}
return FindIgnoreFile(parentDir);
} }
/// <inheritdoc /> /// <inheritdoc />
public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem? parent) public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem? parent) => IsIgnored(fileInfo, parent);
{
return IsIgnored(fileInfo, parent);
}
/// <summary> /// <summary>
/// Checks whether or not the file is ignored. /// Checks whether or not the file is ignored.
@@ -42,65 +39,58 @@ public class DotIgnoreIgnoreRule : IResolverIgnoreRule
/// <returns>True if the file should be ignored.</returns> /// <returns>True if the file should be ignored.</returns>
public static bool IsIgnored(FileSystemMetadata fileInfo, BaseItem? parent) public static bool IsIgnored(FileSystemMetadata fileInfo, BaseItem? parent)
{ {
if (fileInfo.IsDirectory) var searchDirectory = fileInfo.IsDirectory
{ ? new DirectoryInfo(fileInfo.FullName)
var dirIgnoreFile = FindIgnoreFile(new DirectoryInfo(fileInfo.FullName)); : new DirectoryInfo(Path.GetDirectoryName(fileInfo.FullName) ?? string.Empty);
if (dirIgnoreFile is null)
{
return false;
}
// Fast path in case the ignore files isn't a symlink and is empty if (string.IsNullOrEmpty(searchDirectory.FullName))
if (dirIgnoreFile.LinkTarget is null && dirIgnoreFile.Length == 0)
{
return true;
}
// ignore the directory only if the .ignore file is empty
// evaluate individual files otherwise
return string.IsNullOrWhiteSpace(GetFileContent(dirIgnoreFile));
}
var parentDirPath = Path.GetDirectoryName(fileInfo.FullName);
if (string.IsNullOrEmpty(parentDirPath))
{ {
return false; return false;
} }
var folder = new DirectoryInfo(parentDirPath); var ignoreFile = FindIgnoreFile(searchDirectory);
var ignoreFile = FindIgnoreFile(folder);
if (ignoreFile is null) if (ignoreFile is null)
{ {
return false; return false;
} }
string ignoreFileString = GetFileContent(ignoreFile); // Fast path in case the ignore files isn't a symlink and is empty
if (ignoreFile.LinkTarget is null && ignoreFile.Length == 0)
if (string.IsNullOrWhiteSpace(ignoreFileString))
{ {
// Ignore directory if we just have the file // Ignore directory if we just have the file
return true; return true;
} }
// If file has content, base ignoring off the content .gitignore-style rules var content = GetFileContent(ignoreFile);
var ignoreRules = ignoreFileString.Split('\n', StringSplitOptions.RemoveEmptyEntries); return string.IsNullOrWhiteSpace(content)
var ignore = new Ignore.Ignore(); || CheckIgnoreRules(fileInfo.FullName, content, fileInfo.IsDirectory);
ignore.Add(ignoreRules);
return ignore.IsIgnored(fileInfo.FullName);
} }
private static string GetFileContent(FileInfo dirIgnoreFile) private static bool CheckIgnoreRules(string path, string ignoreFileContent, bool isDirectory)
{ {
dirIgnoreFile = (FileInfo?)dirIgnoreFile.ResolveLinkTarget(returnFinalTarget: true) ?? dirIgnoreFile; // If file has content, base ignoring off the content .gitignore-style rules
if (!dirIgnoreFile.Exists) var rules = ignoreFileContent.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
var ignore = new Ignore.Ignore();
ignore.Add(rules);
// Mitigate the problem of the Ignore library not handling Windows paths correctly.
// See https://github.com/jellyfin/jellyfin/issues/15484
var pathToCheck = IsWindows ? path.NormalizePath('/') : path;
// Add trailing slash for directories to match "folder/"
if (isDirectory)
{ {
return string.Empty; pathToCheck = string.Concat(pathToCheck.AsSpan().TrimEnd('/'), "/");
} }
using (var reader = dirIgnoreFile.OpenText()) return ignore.IsIgnored(pathToCheck);
{ }
return reader.ReadToEnd();
} private static string GetFileContent(FileInfo ignoreFile)
{
ignoreFile = FileSystemHelper.ResolveLinkTarget(ignoreFile, returnFinalTarget: true) ?? ignoreFile;
return ignoreFile.Exists
? File.ReadAllText(ignoreFile.FullName)
: string.Empty;
} }
} }

View File

@@ -457,6 +457,12 @@ namespace Emby.Server.Implementations.Library
_cache.TryRemove(child.Id, out _); _cache.TryRemove(child.Id, out _);
} }
if (parent is Folder folder)
{
folder.Children = null;
folder.UserData = null;
}
ReportItemRemoved(item, parent); ReportItemRemoved(item, parent);
} }
@@ -1993,6 +1999,12 @@ namespace Emby.Server.Implementations.Library
RegisterItem(item); RegisterItem(item);
} }
if (parent is Folder folder)
{
folder.Children = null;
folder.UserData = null;
}
if (ItemAdded is not null) if (ItemAdded is not null)
{ {
foreach (var item in items) foreach (var item in items)
@@ -2150,6 +2162,12 @@ namespace Emby.Server.Implementations.Library
_itemRepository.SaveItems(items, cancellationToken); _itemRepository.SaveItems(items, cancellationToken);
if (parent is Folder folder)
{
folder.Children = null;
folder.UserData = null;
}
if (ItemUpdated is not null) if (ItemUpdated is not null)
{ {
foreach (var item in items) foreach (var item in items)

View File

@@ -226,6 +226,11 @@ namespace Emby.Server.Implementations.Library
/// <inheritdoc />> /// <inheritdoc />>
public MediaProtocol GetPathProtocol(string path) public MediaProtocol GetPathProtocol(string path)
{ {
if (string.IsNullOrEmpty(path))
{
return MediaProtocol.File;
}
if (path.StartsWith("Rtsp", StringComparison.OrdinalIgnoreCase)) if (path.StartsWith("Rtsp", StringComparison.OrdinalIgnoreCase))
{ {
return MediaProtocol.Rtsp; return MediaProtocol.Rtsp;

View File

@@ -369,13 +369,16 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
// We need to only look at the name of this actual item (not parents) // We need to only look at the name of this actual item (not parents)
var justName = item.IsInMixedFolder ? Path.GetFileName(item.Path.AsSpan()) : Path.GetFileName(item.ContainingFolderPath.AsSpan()); var justName = item.IsInMixedFolder ? Path.GetFileName(item.Path.AsSpan()) : Path.GetFileName(item.ContainingFolderPath.AsSpan());
if (!justName.IsEmpty) var tmdbid = justName.GetAttributeValue("tmdbid");
// If not in a mixed folder and ID not found in folder path, check filename
if (string.IsNullOrEmpty(tmdbid) && !item.IsInMixedFolder)
{ {
// Check for TMDb id tmdbid = Path.GetFileName(item.Path.AsSpan()).GetAttributeValue("tmdbid");
var tmdbid = justName.GetAttributeValue("tmdbid");
item.TrySetProviderId(MetadataProvider.Tmdb, tmdbid);
} }
item.TrySetProviderId(MetadataProvider.Tmdb, tmdbid);
if (!string.IsNullOrEmpty(item.Path)) if (!string.IsNullOrEmpty(item.Path))
{ {
// Check for IMDb id - we use full media path, as we can assume that this will match in any use case (whether id in parent dir or in file name) // Check for IMDb id - we use full media path, as we can assume that this will match in any use case (whether id in parent dir or in file name)

View File

@@ -30,7 +30,7 @@
"ItemAddedWithName": "{0} fue agregado a la biblioteca", "ItemAddedWithName": "{0} fue agregado a la biblioteca",
"ItemRemovedWithName": "{0} fue removido de la biblioteca", "ItemRemovedWithName": "{0} fue removido de la biblioteca",
"LabelIpAddressValue": "Dirección IP: {0}", "LabelIpAddressValue": "Dirección IP: {0}",
"LabelRunningTimeValue": "Tiempo de reproducción: {0}", "LabelRunningTimeValue": "Tiempo corriendo: {0}",
"Latest": "Recientes", "Latest": "Recientes",
"MessageApplicationUpdated": "El servidor Jellyfin ha sido actualizado", "MessageApplicationUpdated": "El servidor Jellyfin ha sido actualizado",
"MessageApplicationUpdatedTo": "El servidor Jellyfin ha sido actualizado a {0}", "MessageApplicationUpdatedTo": "El servidor Jellyfin ha sido actualizado a {0}",

View File

@@ -137,5 +137,5 @@
"TaskExtractMediaSegmentsDescription": "Eraldab või võtab meediasegmendid MediaSegment'i lubavatest pluginatest.", "TaskExtractMediaSegmentsDescription": "Eraldab või võtab meediasegmendid MediaSegment'i lubavatest pluginatest.",
"TaskMoveTrickplayImages": "Muuda trickplay piltide asukoht", "TaskMoveTrickplayImages": "Muuda trickplay piltide asukoht",
"CleanupUserDataTask": "Puhasta kasutajaandmed", "CleanupUserDataTask": "Puhasta kasutajaandmed",
"CleanupUserDataTaskDescription": "Puhastab kõik kasutajaandmed (vaatamise olek, lemmikute olek jne) meediast, mis pole enam vähemalt 90 päeva saadaval olnud." "CleanupUserDataTaskDescription": "Puhastab kõik kasutajaandmed (vaatamise olek, lemmikute olek jne) meediast, mida pole enam vähemalt 90 päeva saadaval olnud."
} }

View File

@@ -11,7 +11,7 @@
"Collections": "Sammlungen", "Collections": "Sammlungen",
"DeviceOfflineWithName": "{0} wurde getrennt", "DeviceOfflineWithName": "{0} wurde getrennt",
"DeviceOnlineWithName": "{0} ist verbunden", "DeviceOnlineWithName": "{0} ist verbunden",
"FailedLoginAttemptWithUserName": "Fehlgeschlagener Anmeldeversuch von {0}", "FailedLoginAttemptWithUserName": "Fählgschlagene Ameldeversuech vo {0}",
"Favorites": "Favorite", "Favorites": "Favorite",
"Folders": "Ordner", "Folders": "Ordner",
"Genres": "Genre", "Genres": "Genre",

View File

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

View File

@@ -136,5 +136,7 @@
"TaskMoveTrickplayImages": "트릭플레이 이미지 위치 마이그레이션", "TaskMoveTrickplayImages": "트릭플레이 이미지 위치 마이그레이션",
"TaskMoveTrickplayImagesDescription": "추출된 트릭플레이 이미지를 라이브러리 설정에 따라 이동합니다.", "TaskMoveTrickplayImagesDescription": "추출된 트릭플레이 이미지를 라이브러리 설정에 따라 이동합니다.",
"TaskDownloadMissingLyrics": "누락된 가사 다운로드", "TaskDownloadMissingLyrics": "누락된 가사 다운로드",
"TaskDownloadMissingLyricsDescription": "가사 다운로드" "TaskDownloadMissingLyricsDescription": "가사 다운로드",
"CleanupUserDataTask": "사용자 데이터 정리 작업",
"CleanupUserDataTaskDescription": "최소 90일 이상 존재하지 않는 미디어에 대한 사용자 데이터(시청 상태, 즐겨찾기 등)를 정리합니다."
} }

View File

@@ -3,7 +3,7 @@
"HeaderNextUp": "Дараа нь", "HeaderNextUp": "Дараа нь",
"HeaderContinueWatching": "Үргэлжлүүлэн үзэх", "HeaderContinueWatching": "Үргэлжлүүлэн үзэх",
"Songs": "Дуунууд", "Songs": "Дуунууд",
"Playlists": "Playlist-ууд", "Playlists": "Тоглуулах жагсаалтууд",
"Movies": "Кинонууд", "Movies": "Кинонууд",
"Latest": "Сүүлийн үеийн", "Latest": "Сүүлийн үеийн",
"Genres": "Төрлүүд", "Genres": "Төрлүүд",
@@ -71,7 +71,7 @@
"Forced": "Хүчээр", "Forced": "Хүчээр",
"HeaderAlbumArtists": "Цомгийн уран бүтээлчид", "HeaderAlbumArtists": "Цомгийн уран бүтээлчид",
"HeaderFavoriteAlbums": "Дуртай цомгууд", "HeaderFavoriteAlbums": "Дуртай цомгууд",
"HeaderLiveTV": "Шууд", "HeaderLiveTV": "Шууд ТВ",
"HeaderRecordingGroups": "Бичлэгийн бүлгүүд", "HeaderRecordingGroups": "Бичлэгийн бүлгүүд",
"HearingImpaired": "Сонсголын бэрхшээлтэй", "HearingImpaired": "Сонсголын бэрхшээлтэй",
"HomeVideos": "Үндсэн дүрсүүд", "HomeVideos": "Үндсэн дүрсүүд",
@@ -109,7 +109,7 @@
"ScheduledTaskStartedWithName": "{0}-г эхлүүлэв", "ScheduledTaskStartedWithName": "{0}-г эхлүүлэв",
"ServerNameNeedsToBeRestarted": "{0}-г дахин асаана уу", "ServerNameNeedsToBeRestarted": "{0}-г дахин асаана уу",
"Shows": "Шоу", "Shows": "Шоу",
"Sync": "Дахин", "Sync": "Синхрончлох",
"System": "Систем", "System": "Систем",
"TvShows": "ТВ нэвтрүүлгүүд", "TvShows": "ТВ нэвтрүүлгүүд",
"Undefined": "Танисангүй", "Undefined": "Танисангүй",

View File

@@ -132,5 +132,10 @@
"TaskDownloadMissingLyrics": "उपलब्ध नसलेली गीतपट्टी (Lyrics) डाउनलोड करा", "TaskDownloadMissingLyrics": "उपलब्ध नसलेली गीतपट्टी (Lyrics) डाउनलोड करा",
"TaskAudioNormalization": "ऑडिओ सामान्यीकरण", "TaskAudioNormalization": "ऑडिओ सामान्यीकरण",
"TaskAudioNormalizationDescription": "ऑडिओ सामान्यीकरणाचा डाटा स्कॅन करतो.", "TaskAudioNormalizationDescription": "ऑडिओ सामान्यीकरणाचा डाटा स्कॅन करतो.",
"TaskDownloadMissingLyricsDescription": "गाण्यांची गीतपट्टी (Lyrics) डाउनलोड करतो" "TaskDownloadMissingLyricsDescription": "गाण्यांची गीतपट्टी (Lyrics) डाउनलोड करतो",
"TaskExtractMediaSegmentsDescription": "सक्रिय असलेल्या प्लगिनमधून मीडिया विभाग प्राप्त करते.",
"TaskMoveTrickplayImagesDescription": "लायब्ररीच्या सेटिंग्जप्रमाणे आधीपासून अस्तित्वात असलेल्या ट्रिकप्ले फाइल्सचे स्थान बदलते.",
"TaskCleanCollectionsAndPlaylistsDescription": "जे संग्रह आणि प्लेलिस्ट आता अस्तित्वात नाहीत, त्यांमधील घटक हटवते.",
"CleanupUserDataTask": "वापरकर्ता डेटाची स्वच्छता प्रक्रिया",
"CleanupUserDataTaskDescription": "९० दिवसांहून अधिक काळ अनुपस्थित असलेल्या माध्यमांवरील सर्व वापरकर्ता माहिती (जसे पाहण्याची स्थिती, आवडी इ.) हटवते."
} }

View File

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

View File

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

View File

@@ -16,7 +16,7 @@
"Collections": "Barrels", "Collections": "Barrels",
"ItemAddedWithName": "{0} is now with yer treasure", "ItemAddedWithName": "{0} is now with yer treasure",
"Default": "Normal-like", "Default": "Normal-like",
"FailedLoginAttemptWithUserName": "Ye failed to get in, try from {0}", "FailedLoginAttemptWithUserName": "Ye failed to enter from {0}",
"Favorites": "Finest Loot", "Favorites": "Finest Loot",
"ItemRemovedWithName": "{0} was taken from yer treasure", "ItemRemovedWithName": "{0} was taken from yer treasure",
"LabelIpAddressValue": "Ship's coordinates: {0}", "LabelIpAddressValue": "Ship's coordinates: {0}",
@@ -113,5 +113,10 @@
"TaskCleanCache": "Sweep the Cache Chest", "TaskCleanCache": "Sweep the Cache Chest",
"TaskRefreshChapterImages": "Claim chapter portraits", "TaskRefreshChapterImages": "Claim chapter portraits",
"TaskRefreshChapterImagesDescription": "Paints wee portraits fer videos that own chapters.", "TaskRefreshChapterImagesDescription": "Paints wee portraits fer videos that own chapters.",
"TaskRefreshLibrary": "Scan the Treasure Trove" "TaskRefreshLibrary": "Scan the Treasure Trove",
"TasksChannelsCategory": "Channels o' thy Internet",
"TaskRefreshTrickplayImages": "Summon the picture tricks",
"TaskRefreshTrickplayImagesDescription": "Summons picture trick previews for videos in ye enabled book roost",
"TaskUpdatePlugins": "Resummon yer Plugins",
"TaskCleanTranscode": "Swab Ye Transcode Directory"
} }

View File

@@ -5,7 +5,7 @@
"Artists": "Artistas", "Artists": "Artistas",
"AuthenticationSucceededWithUserName": "{0} autenticado com sucesso", "AuthenticationSucceededWithUserName": "{0} autenticado com sucesso",
"Books": "Livros", "Books": "Livros",
"CameraImageUploadedFrom": "Uma nova imagem de câmara foi enviada a partir de {0}", "CameraImageUploadedFrom": "Uma nova imagem da câmara foi enviada a partir de {0}",
"Channels": "Canais", "Channels": "Canais",
"ChapterNameValue": "Capítulo {0}", "ChapterNameValue": "Capítulo {0}",
"Collections": "Coleções", "Collections": "Coleções",

View File

@@ -39,7 +39,7 @@
"TasksMaintenanceCategory": "Bảo Trì", "TasksMaintenanceCategory": "Bảo Trì",
"VersionNumber": "Phiên Bản {0}", "VersionNumber": "Phiên Bản {0}",
"ValueHasBeenAddedToLibrary": "{0} đã được thêm vào thư viện của bạn", "ValueHasBeenAddedToLibrary": "{0} đã được thêm vào thư viện của bạn",
"UserStoppedPlayingItemWithValues": "{0} đã phát xong {1} trên {2}", "UserStoppedPlayingItemWithValues": "{0} đã kết thúc phát {1} trên {2}",
"UserStartedPlayingItemWithValues": "{0} đang phát {1} trên {2}", "UserStartedPlayingItemWithValues": "{0} đang phát {1} trên {2}",
"UserPolicyUpdatedWithName": "Chính sách người dùng đã được cập nhật cho {0}", "UserPolicyUpdatedWithName": "Chính sách người dùng đã được cập nhật cho {0}",
"UserPasswordChangedWithName": "Mật khẩu đã được thay đổi cho người dùng {0}", "UserPasswordChangedWithName": "Mật khẩu đã được thay đổi cho người dùng {0}",

View File

@@ -23,7 +23,7 @@
"HeaderFavoriteShows": "最愛的節目", "HeaderFavoriteShows": "最愛的節目",
"HeaderFavoriteSongs": "最愛的歌曲", "HeaderFavoriteSongs": "最愛的歌曲",
"HeaderLiveTV": "電視直播", "HeaderLiveTV": "電視直播",
"HeaderNextUp": "接著播放", "HeaderNextUp": "繼續觀看",
"HeaderRecordingGroups": "錄製組", "HeaderRecordingGroups": "錄製組",
"HomeVideos": "家庭影片", "HomeVideos": "家庭影片",
"Inherit": "繼承", "Inherit": "繼承",
@@ -127,8 +127,8 @@
"HearingImpaired": "聽力障礙", "HearingImpaired": "聽力障礙",
"TaskRefreshTrickplayImages": "建立 Trickplay 圖像", "TaskRefreshTrickplayImages": "建立 Trickplay 圖像",
"TaskRefreshTrickplayImagesDescription": "為已啟用 Trickplay 的媒體庫內的影片建立 Trickplay 預覽圖。", "TaskRefreshTrickplayImagesDescription": "為已啟用 Trickplay 的媒體庫內的影片建立 Trickplay 預覽圖。",
"TaskExtractMediaSegments": "掃描媒體段落", "TaskExtractMediaSegments": "掃描媒體分段資訊",
"TaskExtractMediaSegmentsDescription": "從MediaSegment中被允許的插件獲取媒體段。", "TaskExtractMediaSegmentsDescription": "從允許MediaSegment 功能的插件獲取媒體段。",
"TaskDownloadMissingLyrics": "下載欠缺歌詞", "TaskDownloadMissingLyrics": "下載欠缺歌詞",
"TaskDownloadMissingLyricsDescription": "下載歌詞", "TaskDownloadMissingLyricsDescription": "下載歌詞",
"TaskCleanCollectionsAndPlaylists": "整理媒體與播放清單", "TaskCleanCollectionsAndPlaylists": "整理媒體與播放清單",
@@ -137,5 +137,6 @@
"TaskCleanCollectionsAndPlaylistsDescription": "從資料庫及播放清單中移除已不存在的項目。", "TaskCleanCollectionsAndPlaylistsDescription": "從資料庫及播放清單中移除已不存在的項目。",
"TaskMoveTrickplayImagesDescription": "根據媒體庫設定移動現有的 Trickplay 檔案。", "TaskMoveTrickplayImagesDescription": "根據媒體庫設定移動現有的 Trickplay 檔案。",
"TaskMoveTrickplayImages": "轉移 Trickplay 影像位置", "TaskMoveTrickplayImages": "轉移 Trickplay 影像位置",
"CleanupUserDataTask": "用戶資料清理工作" "CleanupUserDataTask": "用戶資料清理工作",
"CleanupUserDataTaskDescription": "從用戶數據中清除已經被刪除超過 90 日的媒體相關資料。"
} }

View File

@@ -244,6 +244,7 @@ namespace Emby.Server.Implementations.Playlists
// Update the playlist in the repository // Update the playlist in the repository
playlist.LinkedChildren = [.. playlist.LinkedChildren, .. childrenToAdd]; playlist.LinkedChildren = [.. playlist.LinkedChildren, .. childrenToAdd];
playlist.DateLastMediaAdded = DateTime.UtcNow;
await UpdatePlaylistInternal(playlist).ConfigureAwait(false); await UpdatePlaylistInternal(playlist).ConfigureAwait(false);

View File

@@ -122,7 +122,6 @@ public class ArtistsController : BaseJellyfinApiController
{ {
userId = RequestHelpers.GetUserId(User, userId); userId = RequestHelpers.GetUserId(User, userId);
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
User? user = null; User? user = null;
@@ -326,7 +325,6 @@ public class ArtistsController : BaseJellyfinApiController
{ {
userId = RequestHelpers.GetUserId(User, userId); userId = RequestHelpers.GetUserId(User, userId);
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
User? user = null; User? user = null;
@@ -467,7 +465,7 @@ public class ArtistsController : BaseJellyfinApiController
public ActionResult<BaseItemDto> GetArtistByName([FromRoute, Required] string name, [FromQuery] Guid? userId) public ActionResult<BaseItemDto> GetArtistByName([FromRoute, Required] string name, [FromQuery] Guid? userId)
{ {
userId = RequestHelpers.GetUserId(User, userId); userId = RequestHelpers.GetUserId(User, userId);
var dtoOptions = new DtoOptions().AddClientFields(User); var dtoOptions = new DtoOptions();
var item = _libraryManager.GetArtist(name, dtoOptions); var item = _libraryManager.GetArtist(name, dtoOptions);

View File

@@ -65,7 +65,7 @@ public class CollectionController : BaseJellyfinApiController
UserIds = new[] { userId } UserIds = new[] { userId }
}).ConfigureAwait(false); }).ConfigureAwait(false);
var dtoOptions = new DtoOptions().AddClientFields(User); var dtoOptions = new DtoOptions();
var dto = _dtoService.GetBaseItemDto(item, dtoOptions); var dto = _dtoService.GetBaseItemDto(item, dtoOptions);

View File

@@ -1625,8 +1625,11 @@ public class DynamicHlsController : BaseJellyfinApiController
var useLegacySegmentOption = _mediaEncoder.EncoderVersion < _minFFmpegHlsSegmentOptions; var useLegacySegmentOption = _mediaEncoder.EncoderVersion < _minFFmpegHlsSegmentOptions;
// fMP4 needs this flag to write the audio packet DTS/PTS including the initial delay into MOOF::TRAF::TFDT if (state.VideoStream is not null && state.IsOutputVideo)
hlsArguments += $" {(useLegacySegmentOption ? "-hls_ts_options" : "-hls_segment_options")} movflags=+frag_discont"; {
// fMP4 needs this flag to write the audio packet DTS/PTS including the initial delay into MOOF::TRAF::TFDT
hlsArguments += $" {(useLegacySegmentOption ? "-hls_ts_options" : "-hls_segment_options")} movflags=+frag_discont";
}
segmentFormat = "fmp4" + outputFmp4HeaderArg; segmentFormat = "fmp4" + outputFmp4HeaderArg;
} }

View File

@@ -94,7 +94,6 @@ public class GenresController : BaseJellyfinApiController
{ {
userId = RequestHelpers.GetUserId(User, userId); userId = RequestHelpers.GetUserId(User, userId);
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, false, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, false, imageTypeLimit, enableImageTypes);
User? user = userId.IsNullOrEmpty() User? user = userId.IsNullOrEmpty()
@@ -159,8 +158,7 @@ public class GenresController : BaseJellyfinApiController
public ActionResult<BaseItemDto> GetGenre([FromRoute, Required] string genreName, [FromQuery] Guid? userId) public ActionResult<BaseItemDto> GetGenre([FromRoute, Required] string genreName, [FromQuery] Guid? userId)
{ {
userId = RequestHelpers.GetUserId(User, userId); userId = RequestHelpers.GetUserId(User, userId);
var dtoOptions = new DtoOptions() var dtoOptions = new DtoOptions();
.AddClientFields(User);
Genre? item; Genre? item;
if (genreName.Contains(BaseItem.SlugChar, StringComparison.OrdinalIgnoreCase)) if (genreName.Contains(BaseItem.SlugChar, StringComparison.OrdinalIgnoreCase))

View File

@@ -90,7 +90,6 @@ public class InstantMixController : BaseJellyfinApiController
} }
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions); var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions);
return GetResult(items, user, limit, dtoOptions); return GetResult(items, user, limit, dtoOptions);
@@ -134,7 +133,6 @@ public class InstantMixController : BaseJellyfinApiController
} }
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions); var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions);
return GetResult(items, user, limit, dtoOptions); return GetResult(items, user, limit, dtoOptions);
@@ -178,7 +176,6 @@ public class InstantMixController : BaseJellyfinApiController
} }
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions); var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions);
return GetResult(items, user, limit, dtoOptions); return GetResult(items, user, limit, dtoOptions);
@@ -214,7 +211,6 @@ public class InstantMixController : BaseJellyfinApiController
? null ? null
: _userManager.GetUserById(userId.Value); : _userManager.GetUserById(userId.Value);
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
var items = _musicManager.GetInstantMixFromGenres(new[] { name }, user, dtoOptions); var items = _musicManager.GetInstantMixFromGenres(new[] { name }, user, dtoOptions);
return GetResult(items, user, limit, dtoOptions); return GetResult(items, user, limit, dtoOptions);
@@ -258,7 +254,6 @@ public class InstantMixController : BaseJellyfinApiController
} }
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions); var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions);
return GetResult(items, user, limit, dtoOptions); return GetResult(items, user, limit, dtoOptions);
@@ -302,7 +297,6 @@ public class InstantMixController : BaseJellyfinApiController
} }
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions); var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions);
return GetResult(items, user, limit, dtoOptions); return GetResult(items, user, limit, dtoOptions);
@@ -385,7 +379,6 @@ public class InstantMixController : BaseJellyfinApiController
} }
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions); var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions);
return GetResult(items, user, limit, dtoOptions); return GetResult(items, user, limit, dtoOptions);

View File

@@ -268,7 +268,6 @@ public class ItemsController : BaseJellyfinApiController
} }
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
if (includeItemTypes.Length == 1 if (includeItemTypes.Length == 1
@@ -849,7 +848,6 @@ public class ItemsController : BaseJellyfinApiController
var parentIdGuid = parentId ?? Guid.Empty; var parentIdGuid = parentId ?? Guid.Empty;
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
var ancestorIds = Array.Empty<Guid>(); var ancestorIds = Array.Empty<Guid>();

View File

@@ -187,7 +187,7 @@ public class LibraryController : BaseJellyfinApiController
item = parent; item = parent;
} }
var dtoOptions = new DtoOptions().AddClientFields(User); var dtoOptions = new DtoOptions();
var items = themeItems var items = themeItems
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)) .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item))
.ToArray(); .ToArray();
@@ -260,7 +260,7 @@ public class LibraryController : BaseJellyfinApiController
item = parent; item = parent;
} }
var dtoOptions = new DtoOptions().AddClientFields(User); var dtoOptions = new DtoOptions();
var items = themeItems var items = themeItems
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)) .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item))
.ToArray(); .ToArray();
@@ -496,7 +496,7 @@ public class LibraryController : BaseJellyfinApiController
var baseItemDtos = new List<BaseItemDto>(); var baseItemDtos = new List<BaseItemDto>();
var dtoOptions = new DtoOptions().AddClientFields(User); var dtoOptions = new DtoOptions();
BaseItem? parent = item.GetParent(); BaseItem? parent = item.GetParent();
while (parent is not null) while (parent is not null)
@@ -556,7 +556,7 @@ public class LibraryController : BaseJellyfinApiController
items = items.Where(i => i.IsHidden == val).ToList(); items = items.Where(i => i.IsHidden == val).ToList();
} }
var dtoOptions = new DtoOptions().AddClientFields(User); var dtoOptions = new DtoOptions();
var resultArray = _dtoService.GetBaseItemDtos(items, dtoOptions); var resultArray = _dtoService.GetBaseItemDtos(items, dtoOptions);
return new QueryResult<BaseItemDto>(resultArray); return new QueryResult<BaseItemDto>(resultArray);
} }
@@ -747,8 +747,7 @@ public class LibraryController : BaseJellyfinApiController
return new QueryResult<BaseItemDto>(); return new QueryResult<BaseItemDto>();
} }
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields };
.AddClientFields(User);
var program = item as IHasProgramAttributes; var program = item as IHasProgramAttributes;
bool? isMovie = item is Movie || (program is not null && program.IsMovie) || item is Trailer; bool? isMovie = item is Movie || (program is not null && program.IsMovie) || item is Trailer;

View File

@@ -170,7 +170,6 @@ public class LiveTvController : BaseJellyfinApiController
{ {
userId = RequestHelpers.GetUserId(User, userId); userId = RequestHelpers.GetUserId(User, userId);
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
var channelResult = _liveTvManager.GetInternalChannels( var channelResult = _liveTvManager.GetInternalChannels(
@@ -242,8 +241,7 @@ public class LiveTvController : BaseJellyfinApiController
return NotFound(); return NotFound();
} }
var dtoOptions = new DtoOptions() var dtoOptions = new DtoOptions();
.AddClientFields(User);
return _dtoService.GetBaseItemDto(item, dtoOptions, user); return _dtoService.GetBaseItemDto(item, dtoOptions, user);
} }
@@ -297,7 +295,6 @@ public class LiveTvController : BaseJellyfinApiController
{ {
userId = RequestHelpers.GetUserId(User, userId); userId = RequestHelpers.GetUserId(User, userId);
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
return await _liveTvManager.GetRecordingsAsync( return await _liveTvManager.GetRecordingsAsync(
@@ -444,8 +441,7 @@ public class LiveTvController : BaseJellyfinApiController
return NotFound(); return NotFound();
} }
var dtoOptions = new DtoOptions() var dtoOptions = new DtoOptions();
.AddClientFields(User);
return _dtoService.GetBaseItemDto(item, dtoOptions, user); return _dtoService.GetBaseItemDto(item, dtoOptions, user);
} }
@@ -635,7 +631,6 @@ public class LiveTvController : BaseJellyfinApiController
} }
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
return await _liveTvManager.GetPrograms(query, dtoOptions, CancellationToken.None).ConfigureAwait(false); return await _liveTvManager.GetPrograms(query, dtoOptions, CancellationToken.None).ConfigureAwait(false);
} }
@@ -690,7 +685,6 @@ public class LiveTvController : BaseJellyfinApiController
} }
var dtoOptions = new DtoOptions { Fields = body.Fields ?? [] } var dtoOptions = new DtoOptions { Fields = body.Fields ?? [] }
.AddClientFields(User)
.AddAdditionalDtoOptions(body.EnableImages, body.EnableUserData, body.ImageTypeLimit, body.EnableImageTypes ?? []); .AddAdditionalDtoOptions(body.EnableImages, body.EnableUserData, body.ImageTypeLimit, body.EnableImageTypes ?? []);
return await _liveTvManager.GetPrograms(query, dtoOptions, CancellationToken.None).ConfigureAwait(false); return await _liveTvManager.GetPrograms(query, dtoOptions, CancellationToken.None).ConfigureAwait(false);
} }
@@ -760,7 +754,6 @@ public class LiveTvController : BaseJellyfinApiController
}; };
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
return await _liveTvManager.GetRecommendedProgramsAsync(query, dtoOptions, CancellationToken.None).ConfigureAwait(false); return await _liveTvManager.GetRecommendedProgramsAsync(query, dtoOptions, CancellationToken.None).ConfigureAwait(false);
} }

View File

@@ -74,8 +74,7 @@ public class MoviesController : BaseJellyfinApiController
var user = userId.IsNullOrEmpty() var user = userId.IsNullOrEmpty()
? null ? null
: _userManager.GetUserById(userId.Value); : _userManager.GetUserById(userId.Value);
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields };
.AddClientFields(User);
var categories = new List<RecommendationDto>(); var categories = new List<RecommendationDto>();

View File

@@ -94,7 +94,6 @@ public class MusicGenresController : BaseJellyfinApiController
{ {
userId = RequestHelpers.GetUserId(User, userId); userId = RequestHelpers.GetUserId(User, userId);
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, false, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, false, imageTypeLimit, enableImageTypes);
User? user = userId.IsNullOrEmpty() User? user = userId.IsNullOrEmpty()
@@ -148,7 +147,7 @@ public class MusicGenresController : BaseJellyfinApiController
public ActionResult<BaseItemDto> GetMusicGenre([FromRoute, Required] string genreName, [FromQuery] Guid? userId) public ActionResult<BaseItemDto> GetMusicGenre([FromRoute, Required] string genreName, [FromQuery] Guid? userId)
{ {
userId = RequestHelpers.GetUserId(User, userId); userId = RequestHelpers.GetUserId(User, userId);
var dtoOptions = new DtoOptions().AddClientFields(User); var dtoOptions = new DtoOptions();
MusicGenre? item; MusicGenre? item;

View File

@@ -81,7 +81,6 @@ public class PersonsController : BaseJellyfinApiController
{ {
userId = RequestHelpers.GetUserId(User, userId); userId = RequestHelpers.GetUserId(User, userId);
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
User? user = userId.IsNullOrEmpty() User? user = userId.IsNullOrEmpty()
@@ -121,8 +120,7 @@ public class PersonsController : BaseJellyfinApiController
public ActionResult<BaseItemDto> GetPerson([FromRoute, Required] string name, [FromQuery] Guid? userId) public ActionResult<BaseItemDto> GetPerson([FromRoute, Required] string name, [FromQuery] Guid? userId)
{ {
userId = RequestHelpers.GetUserId(User, userId); userId = RequestHelpers.GetUserId(User, userId);
var dtoOptions = new DtoOptions() var dtoOptions = new DtoOptions();
.AddClientFields(User);
var item = _libraryManager.GetPerson(name); var item = _libraryManager.GetPerson(name);
if (item is null) if (item is null)

View File

@@ -548,7 +548,6 @@ public class PlaylistsController : BaseJellyfinApiController
} }
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
var dtos = _dtoService.GetBaseItemDtos(items.Select(i => i.Item2).ToList(), dtoOptions, user); var dtos = _dtoService.GetBaseItemDtos(items.Select(i => i.Item2).ToList(), dtoOptions, user);

View File

@@ -89,7 +89,6 @@ public class StudiosController : BaseJellyfinApiController
{ {
userId = RequestHelpers.GetUserId(User, userId); userId = RequestHelpers.GetUserId(User, userId);
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
User? user = userId.IsNullOrEmpty() User? user = userId.IsNullOrEmpty()
@@ -142,7 +141,7 @@ public class StudiosController : BaseJellyfinApiController
public ActionResult<BaseItemDto> GetStudio([FromRoute, Required] string name, [FromQuery] Guid? userId) public ActionResult<BaseItemDto> GetStudio([FromRoute, Required] string name, [FromQuery] Guid? userId)
{ {
userId = RequestHelpers.GetUserId(User, userId); userId = RequestHelpers.GetUserId(User, userId);
var dtoOptions = new DtoOptions().AddClientFields(User); var dtoOptions = new DtoOptions();
var item = _libraryManager.GetStudio(name); var item = _libraryManager.GetStudio(name);
if (!userId.IsNullOrEmpty()) if (!userId.IsNullOrEmpty())

View File

@@ -77,7 +77,7 @@ public class SuggestionsController : BaseJellyfinApiController
user = _userManager.GetUserById(requestUserId); user = _userManager.GetUserById(requestUserId);
} }
var dtoOptions = new DtoOptions().AddClientFields(User); var dtoOptions = new DtoOptions();
var result = _libraryManager.GetItemsResult(new InternalItemsQuery(user) var result = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
{ {
OrderBy = new[] { (ItemSortBy.Random, SortOrder.Descending) }, OrderBy = new[] { (ItemSortBy.Random, SortOrder.Descending) },

View File

@@ -99,7 +99,6 @@ public class TvShowsController : BaseJellyfinApiController
} }
var options = new DtoOptions { Fields = fields } var options = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
var result = _tvSeriesManager.GetNextUp( var result = _tvSeriesManager.GetNextUp(
@@ -161,7 +160,6 @@ public class TvShowsController : BaseJellyfinApiController
var parentIdGuid = parentId ?? Guid.Empty; var parentIdGuid = parentId ?? Guid.Empty;
var options = new DtoOptions { Fields = fields } var options = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user) var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
@@ -231,7 +229,6 @@ public class TvShowsController : BaseJellyfinApiController
List<BaseItem> episodes; List<BaseItem> episodes;
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
var shouldIncludeMissingEpisodes = (user is not null && user.DisplayMissingEpisodes) || User.GetIsApiKey(); var shouldIncludeMissingEpisodes = (user is not null && user.DisplayMissingEpisodes) || User.GetIsApiKey();
@@ -360,7 +357,6 @@ public class TvShowsController : BaseJellyfinApiController
}); });
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
var returnItems = _dtoService.GetBaseItemDtos(seasons, dtoOptions, user); var returnItems = _dtoService.GetBaseItemDtos(seasons, dtoOptions, user);

View File

@@ -94,7 +94,7 @@ public class UserLibraryController : BaseJellyfinApiController
await RefreshItemOnDemandIfNeeded(item).ConfigureAwait(false); await RefreshItemOnDemandIfNeeded(item).ConfigureAwait(false);
var dtoOptions = new DtoOptions().AddClientFields(User); var dtoOptions = new DtoOptions();
return _dtoService.GetBaseItemDto(item, dtoOptions, user); return _dtoService.GetBaseItemDto(item, dtoOptions, user);
} }
@@ -133,7 +133,7 @@ public class UserLibraryController : BaseJellyfinApiController
} }
var item = _libraryManager.GetUserRootFolder(); var item = _libraryManager.GetUserRootFolder();
var dtoOptions = new DtoOptions().AddClientFields(User); var dtoOptions = new DtoOptions();
return _dtoService.GetBaseItemDto(item, dtoOptions, user); return _dtoService.GetBaseItemDto(item, dtoOptions, user);
} }
@@ -180,7 +180,7 @@ public class UserLibraryController : BaseJellyfinApiController
} }
var items = await _libraryManager.GetIntros(item, user).ConfigureAwait(false); var items = await _libraryManager.GetIntros(item, user).ConfigureAwait(false);
var dtoOptions = new DtoOptions().AddClientFields(User); var dtoOptions = new DtoOptions();
var dtos = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray(); var dtos = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray();
return new QueryResult<BaseItemDto>(dtos); return new QueryResult<BaseItemDto>(dtos);
@@ -422,7 +422,7 @@ public class UserLibraryController : BaseJellyfinApiController
return NotFound(); return NotFound();
} }
var dtoOptions = new DtoOptions().AddClientFields(User); var dtoOptions = new DtoOptions();
if (item is IHasTrailers hasTrailers) if (item is IHasTrailers hasTrailers)
{ {
var trailers = hasTrailers.LocalTrailers; var trailers = hasTrailers.LocalTrailers;
@@ -478,7 +478,7 @@ public class UserLibraryController : BaseJellyfinApiController
return NotFound(); return NotFound();
} }
var dtoOptions = new DtoOptions().AddClientFields(User); var dtoOptions = new DtoOptions();
return Ok(item return Ok(item
.GetExtras() .GetExtras()
@@ -549,7 +549,6 @@ public class UserLibraryController : BaseJellyfinApiController
} }
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
var list = _userViewManager.GetLatestItems( var list = _userViewManager.GetLatestItems(

View File

@@ -86,7 +86,7 @@ public class UserViewsController : BaseJellyfinApiController
var folders = _userViewManager.GetUserViews(query); var folders = _userViewManager.GetUserViews(query);
var dtoOptions = new DtoOptions().AddClientFields(User); var dtoOptions = new DtoOptions();
dtoOptions.Fields = [..dtoOptions.Fields, ItemFields.PrimaryImageAspectRatio, ItemFields.DisplayPreferencesId]; dtoOptions.Fields = [..dtoOptions.Fields, ItemFields.PrimaryImageAspectRatio, ItemFields.DisplayPreferencesId];
var dtos = Array.ConvertAll(folders, i => _dtoService.GetBaseItemDto(i, dtoOptions, user)); var dtos = Array.ConvertAll(folders, i => _dtoService.GetBaseItemDto(i, dtoOptions, user));

View File

@@ -111,7 +111,6 @@ public class VideosController : BaseJellyfinApiController
} }
var dtoOptions = new DtoOptions(); var dtoOptions = new DtoOptions();
dtoOptions = dtoOptions.AddClientFields(User);
BaseItemDto[] items; BaseItemDto[] items;
if (item is Video video) if (item is Video video)

View File

@@ -89,7 +89,6 @@ public class YearsController : BaseJellyfinApiController
{ {
userId = RequestHelpers.GetUserId(User, userId); userId = RequestHelpers.GetUserId(User, userId);
var dtoOptions = new DtoOptions { Fields = fields } var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
User? user = userId.IsNullOrEmpty() User? user = userId.IsNullOrEmpty()
@@ -182,8 +181,7 @@ public class YearsController : BaseJellyfinApiController
return NotFound(); return NotFound();
} }
var dtoOptions = new DtoOptions() var dtoOptions = new DtoOptions();
.AddClientFields(User);
if (!userId.IsNullOrEmpty()) if (!userId.IsNullOrEmpty())
{ {

View File

@@ -1,10 +1,6 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Security.Claims;
using Jellyfin.Extensions;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
namespace Jellyfin.Api.Extensions; namespace Jellyfin.Api.Extensions;
@@ -13,55 +9,6 @@ namespace Jellyfin.Api.Extensions;
/// </summary> /// </summary>
public static class DtoExtensions public static class DtoExtensions
{ {
/// <summary>
/// Add additional fields depending on client.
/// </summary>
/// <remarks>
/// Use in place of GetDtoOptions.
/// Legacy order: 2.
/// </remarks>
/// <param name="dtoOptions">DtoOptions object.</param>
/// <param name="user">Current claims principal.</param>
/// <returns>Modified DtoOptions object.</returns>
internal static DtoOptions AddClientFields(
this DtoOptions dtoOptions, ClaimsPrincipal user)
{
string? client = user.GetClient();
// No client in claim
if (string.IsNullOrEmpty(client))
{
return dtoOptions;
}
if (!dtoOptions.ContainsField(ItemFields.RecursiveItemCount))
{
if (client.Contains("kodi", StringComparison.OrdinalIgnoreCase) ||
client.Contains("wmc", StringComparison.OrdinalIgnoreCase) ||
client.Contains("media center", StringComparison.OrdinalIgnoreCase) ||
client.Contains("classic", StringComparison.OrdinalIgnoreCase))
{
dtoOptions.Fields = [..dtoOptions.Fields, ItemFields.RecursiveItemCount];
}
}
if (!dtoOptions.ContainsField(ItemFields.ChildCount))
{
if (client.Contains("kodi", StringComparison.OrdinalIgnoreCase) ||
client.Contains("wmc", StringComparison.OrdinalIgnoreCase) ||
client.Contains("media center", StringComparison.OrdinalIgnoreCase) ||
client.Contains("classic", StringComparison.OrdinalIgnoreCase) ||
client.Contains("roku", StringComparison.OrdinalIgnoreCase) ||
client.Contains("samsung", StringComparison.OrdinalIgnoreCase) ||
client.Contains("androidtv", StringComparison.OrdinalIgnoreCase))
{
dtoOptions.Fields = [..dtoOptions.Fields, ItemFields.ChildCount];
}
}
return dtoOptions;
}
/// <summary> /// <summary>
/// Add additional DtoOptions. /// Add additional DtoOptions.
/// </summary> /// </summary>

View File

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

View File

@@ -275,6 +275,7 @@ public sealed class BaseItemRepository
} }
dbQuery = ApplyQueryPaging(dbQuery, filter); dbQuery = ApplyQueryPaging(dbQuery, filter);
dbQuery = ApplyNavigations(dbQuery, filter);
result.Items = dbQuery.AsEnumerable().Where(e => e is not null).Select(w => DeserializeBaseItem(w, filter.SkipDeserialization)).ToArray(); result.Items = dbQuery.AsEnumerable().Where(e => e is not null).Select(w => DeserializeBaseItem(w, filter.SkipDeserialization)).ToArray();
result.StartIndex = filter.StartIndex ?? 0; result.StartIndex = filter.StartIndex ?? 0;
@@ -294,6 +295,7 @@ public sealed class BaseItemRepository
dbQuery = ApplyGroupingFilter(context, dbQuery, filter); dbQuery = ApplyGroupingFilter(context, dbQuery, filter);
dbQuery = ApplyQueryPaging(dbQuery, filter); dbQuery = ApplyQueryPaging(dbQuery, filter);
dbQuery = ApplyNavigations(dbQuery, filter);
return dbQuery.AsEnumerable().Where(e => e is not null).Select(w => DeserializeBaseItem(w, filter.SkipDeserialization)).ToArray(); return dbQuery.AsEnumerable().Where(e => e is not null).Select(w => DeserializeBaseItem(w, filter.SkipDeserialization)).ToArray();
} }
@@ -337,6 +339,8 @@ public sealed class BaseItemRepository
mainquery = ApplyGroupingFilter(context, mainquery, filter); mainquery = ApplyGroupingFilter(context, mainquery, filter);
mainquery = ApplyQueryPaging(mainquery, filter); mainquery = ApplyQueryPaging(mainquery, filter);
mainquery = ApplyNavigations(mainquery, filter);
return mainquery.AsEnumerable().Where(e => e is not null).Select(w => DeserializeBaseItem(w, filter.SkipDeserialization)).ToArray(); return mainquery.AsEnumerable().Where(e => e is not null).Select(w => DeserializeBaseItem(w, filter.SkipDeserialization)).ToArray();
} }
@@ -399,9 +403,7 @@ public sealed class BaseItemRepository
dbQuery = dbQuery.Distinct(); dbQuery = dbQuery.Distinct();
} }
dbQuery = ApplyOrder(dbQuery, filter); dbQuery = ApplyOrder(dbQuery, filter, context);
dbQuery = ApplyNavigations(dbQuery, filter);
return dbQuery; return dbQuery;
} }
@@ -446,6 +448,7 @@ public sealed class BaseItemRepository
dbQuery = TranslateQuery(dbQuery, context, filter); dbQuery = TranslateQuery(dbQuery, context, filter);
dbQuery = ApplyGroupingFilter(context, dbQuery, filter); dbQuery = ApplyGroupingFilter(context, dbQuery, filter);
dbQuery = ApplyQueryPaging(dbQuery, filter); dbQuery = ApplyQueryPaging(dbQuery, filter);
dbQuery = ApplyNavigations(dbQuery, filter);
return dbQuery; return dbQuery;
} }
@@ -1252,7 +1255,7 @@ public sealed class BaseItemRepository
.AsSingleQuery() .AsSingleQuery()
.Where(e => masterQuery.Contains(e.Id)); .Where(e => masterQuery.Contains(e.Id));
query = ApplyOrder(query, filter); query = ApplyOrder(query, filter, context);
var result = new QueryResult<(BaseItemDto, ItemCounts?)>(); var result = new QueryResult<(BaseItemDto, ItemCounts?)>();
if (filter.EnableTotalRecordCount) if (filter.EnableTotalRecordCount)
@@ -1518,7 +1521,7 @@ public sealed class BaseItemRepository
|| query.IncludeItemTypes.Contains(BaseItemKind.Season); || query.IncludeItemTypes.Contains(BaseItemKind.Season);
} }
private IQueryable<BaseItemEntity> ApplyOrder(IQueryable<BaseItemEntity> query, InternalItemsQuery filter) private IQueryable<BaseItemEntity> ApplyOrder(IQueryable<BaseItemEntity> query, InternalItemsQuery filter, JellyfinDbContext context)
{ {
var orderBy = filter.OrderBy; var orderBy = filter.OrderBy;
var hasSearch = !string.IsNullOrEmpty(filter.SearchTerm); var hasSearch = !string.IsNullOrEmpty(filter.SearchTerm);
@@ -1537,7 +1540,7 @@ public sealed class BaseItemRepository
var firstOrdering = orderBy.FirstOrDefault(); var firstOrdering = orderBy.FirstOrDefault();
if (firstOrdering != default) if (firstOrdering != default)
{ {
var expression = OrderMapper.MapOrderByField(firstOrdering.OrderBy, filter); var expression = OrderMapper.MapOrderByField(firstOrdering.OrderBy, filter, context);
if (firstOrdering.SortOrder == SortOrder.Ascending) if (firstOrdering.SortOrder == SortOrder.Ascending)
{ {
orderedQuery = query.OrderBy(expression); orderedQuery = query.OrderBy(expression);
@@ -1562,7 +1565,7 @@ public sealed class BaseItemRepository
foreach (var item in orderBy.Skip(1)) foreach (var item in orderBy.Skip(1))
{ {
var expression = OrderMapper.MapOrderByField(item.OrderBy, filter); var expression = OrderMapper.MapOrderByField(item.OrderBy, filter, context);
if (item.SortOrder == SortOrder.Ascending) if (item.SortOrder == SortOrder.Ascending)
{ {
orderedQuery = orderedQuery!.ThenBy(expression); orderedQuery = orderedQuery!.ThenBy(expression);
@@ -1701,15 +1704,16 @@ public sealed class BaseItemRepository
if (!string.IsNullOrEmpty(filter.SearchTerm)) if (!string.IsNullOrEmpty(filter.SearchTerm))
{ {
var searchTerm = filter.SearchTerm.ToLower(); var cleanedSearchTerm = GetCleanValue(filter.SearchTerm);
if (SearchWildcardTerms.Any(f => searchTerm.Contains(f))) var originalSearchTerm = filter.SearchTerm.ToLower();
if (SearchWildcardTerms.Any(f => cleanedSearchTerm.Contains(f)))
{ {
searchTerm = $"%{searchTerm.Trim('%')}%"; cleanedSearchTerm = $"%{cleanedSearchTerm.Trim('%')}%";
baseQuery = baseQuery.Where(e => EF.Functions.Like(e.CleanName!.ToLower(), searchTerm) || (e.OriginalTitle != null && EF.Functions.Like(e.OriginalTitle.ToLower(), searchTerm))); baseQuery = baseQuery.Where(e => EF.Functions.Like(e.CleanName!, cleanedSearchTerm) || (e.OriginalTitle != null && EF.Functions.Like(e.OriginalTitle.ToLower(), originalSearchTerm)));
} }
else else
{ {
baseQuery = baseQuery.Where(e => e.CleanName!.ToLower().Contains(searchTerm) || (e.OriginalTitle != null && e.OriginalTitle.ToLower().Contains(searchTerm))); baseQuery = baseQuery.Where(e => e.CleanName!.Contains(cleanedSearchTerm) || (e.OriginalTitle != null && e.OriginalTitle.ToLower().Contains(originalSearchTerm)));
} }
} }
@@ -1944,19 +1948,20 @@ public sealed class BaseItemRepository
if (!string.IsNullOrWhiteSpace(filter.NameStartsWith)) if (!string.IsNullOrWhiteSpace(filter.NameStartsWith))
{ {
baseQuery = baseQuery.Where(e => e.SortName!.StartsWith(filter.NameStartsWith)); var startsWithLower = filter.NameStartsWith.ToLowerInvariant();
baseQuery = baseQuery.Where(e => e.SortName!.StartsWith(startsWithLower));
} }
if (!string.IsNullOrWhiteSpace(filter.NameStartsWithOrGreater)) if (!string.IsNullOrWhiteSpace(filter.NameStartsWithOrGreater))
{ {
// i hate this var startsOrGreaterLower = filter.NameStartsWithOrGreater.ToLowerInvariant();
baseQuery = baseQuery.Where(e => e.SortName!.FirstOrDefault() > filter.NameStartsWithOrGreater[0] || e.Name!.FirstOrDefault() > filter.NameStartsWithOrGreater[0]); baseQuery = baseQuery.Where(e => e.SortName!.CompareTo(startsOrGreaterLower) >= 0);
} }
if (!string.IsNullOrWhiteSpace(filter.NameLessThan)) if (!string.IsNullOrWhiteSpace(filter.NameLessThan))
{ {
// i hate this var lessThanLower = filter.NameLessThan.ToLowerInvariant();
baseQuery = baseQuery.Where(e => e.SortName!.FirstOrDefault() < filter.NameLessThan[0] || e.Name!.FirstOrDefault() < filter.NameLessThan[0]); baseQuery = baseQuery.Where(e => e.SortName!.CompareTo(lessThanLower ) < 0);
} }
if (filter.ImageTypes.Length > 0) if (filter.ImageTypes.Length > 0)
@@ -2415,39 +2420,34 @@ public sealed class BaseItemRepository
if (filter.ExcludeInheritedTags.Length > 0) if (filter.ExcludeInheritedTags.Length > 0)
{ {
baseQuery = baseQuery baseQuery = baseQuery.Where(e =>
.Where(e => !e.ItemValues!.Where(w => w.ItemValue.Type == ItemValueType.InheritedTags || w.ItemValue.Type == ItemValueType.Tags) !e.ItemValues!.Any(f => f.ItemValue.Type == ItemValueType.Tags && filter.ExcludeInheritedTags.Contains(f.ItemValue.CleanValue))
.Any(f => filter.ExcludeInheritedTags.Contains(f.ItemValue.CleanValue))); && (e.Type != _itemTypeLookup.BaseItemKindNames[BaseItemKind.Episode] || !e.SeriesId.HasValue ||
!context.ItemValuesMap.Any(f => f.ItemId == e.SeriesId.Value && f.ItemValue.Type == ItemValueType.Tags && filter.ExcludeInheritedTags.Contains(f.ItemValue.CleanValue))));
} }
if (filter.IncludeInheritedTags.Length > 0) if (filter.IncludeInheritedTags.Length > 0)
{ {
// Episodes do not store inherit tags from their parents in the database, and the tag may be still required by the client. // For seasons and episodes, we also need to check the parent series' tags.
// In addition to the tags for the episodes themselves, we need to manually query its parent (the season)'s tags as well. if (includeTypes.Any(t => t == BaseItemKind.Episode || t == BaseItemKind.Season))
if (includeTypes.Length == 1 && includeTypes.FirstOrDefault() is BaseItemKind.Episode)
{ {
baseQuery = baseQuery baseQuery = baseQuery.Where(e =>
.Where(e => e.ItemValues!.Where(f => f.ItemValue.Type == ItemValueType.InheritedTags || f.ItemValue.Type == ItemValueType.Tags) e.ItemValues!.Any(f => f.ItemValue.Type == ItemValueType.Tags && filter.IncludeInheritedTags.Contains(f.ItemValue.CleanValue))
.Any(f => filter.IncludeInheritedTags.Contains(f.ItemValue.CleanValue)) || (e.SeriesId.HasValue && context.ItemValuesMap.Any(f => f.ItemId == e.SeriesId.Value && f.ItemValue.Type == ItemValueType.Tags && filter.IncludeInheritedTags.Contains(f.ItemValue.CleanValue))));
||
(e.ParentId.HasValue && context.ItemValuesMap.Where(w => w.ItemId == e.ParentId.Value && (w.ItemValue.Type == ItemValueType.InheritedTags || w.ItemValue.Type == ItemValueType.Tags))
.Any(f => filter.IncludeInheritedTags.Contains(f.ItemValue.CleanValue))));
} }
// A playlist should be accessible to its owner regardless of allowed tags. // A playlist should be accessible to its owner regardless of allowed tags.
else if (includeTypes.Length == 1 && includeTypes.FirstOrDefault() is BaseItemKind.Playlist) else if (includeTypes.Length == 1 && includeTypes.FirstOrDefault() is BaseItemKind.Playlist)
{ {
baseQuery = baseQuery baseQuery = baseQuery.Where(e =>
.Where(e => e.ItemValues!.Where(f => f.ItemValue.Type == ItemValueType.InheritedTags || f.ItemValue.Type == ItemValueType.Tags) e.ItemValues!.Any(f => f.ItemValue.Type == ItemValueType.Tags && filter.IncludeInheritedTags.Contains(f.ItemValue.CleanValue))
.Any(f => filter.IncludeInheritedTags.Contains(f.ItemValue.CleanValue)) || e.Data!.Contains($"OwnerUserId\":\"{filter.User!.Id:N}\""));
|| e.Data!.Contains($"OwnerUserId\":\"{filter.User!.Id:N}\""));
// d ^^ this is stupid it hate this. // d ^^ this is stupid it hate this.
} }
else else
{ {
baseQuery = baseQuery baseQuery = baseQuery.Where(e =>
.Where(e => e.ItemValues!.Where(f => f.ItemValue.Type == ItemValueType.InheritedTags || f.ItemValue.Type == ItemValueType.Tags) e.ItemValues!.Any(f => f.ItemValue.Type == ItemValueType.Tags && filter.IncludeInheritedTags.Contains(f.ItemValue.CleanValue)));
.Any(f => filter.IncludeInheritedTags.Contains(f.ItemValue.CleanValue)));
} }
} }

View File

@@ -1,7 +1,10 @@
#pragma warning disable RS0030 // Do not use banned APIs
using System; using System;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using Jellyfin.Database.Implementations;
using Jellyfin.Database.Implementations.Entities; using Jellyfin.Database.Implementations.Entities;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@@ -18,39 +21,50 @@ public static class OrderMapper
/// </summary> /// </summary>
/// <param name="sortBy">Item property to sort by.</param> /// <param name="sortBy">Item property to sort by.</param>
/// <param name="query">Context Query.</param> /// <param name="query">Context Query.</param>
/// <param name="jellyfinDbContext">Context.</param>
/// <returns>Func to be executed later for sorting query.</returns> /// <returns>Func to be executed later for sorting query.</returns>
public static Expression<Func<BaseItemEntity, object?>> MapOrderByField(ItemSortBy sortBy, InternalItemsQuery query) public static Expression<Func<BaseItemEntity, object?>> MapOrderByField(ItemSortBy sortBy, InternalItemsQuery query, JellyfinDbContext jellyfinDbContext)
{ {
return sortBy switch return (sortBy, query.User) switch
{ {
ItemSortBy.AirTime => e => e.SortName, // TODO (ItemSortBy.AirTime, _) => e => e.SortName, // TODO
ItemSortBy.Runtime => e => e.RunTimeTicks, (ItemSortBy.Runtime, _) => e => e.RunTimeTicks,
ItemSortBy.Random => e => EF.Functions.Random(), (ItemSortBy.Random, _) => e => EF.Functions.Random(),
ItemSortBy.DatePlayed => e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(query.User!.Id))!.LastPlayedDate, (ItemSortBy.DatePlayed, _) => e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(query.User!.Id))!.LastPlayedDate,
ItemSortBy.PlayCount => e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(query.User!.Id))!.PlayCount, (ItemSortBy.PlayCount, _) => e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(query.User!.Id))!.PlayCount,
ItemSortBy.IsFavoriteOrLiked => e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(query.User!.Id))!.IsFavorite, (ItemSortBy.IsFavoriteOrLiked, _) => e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(query.User!.Id))!.IsFavorite,
ItemSortBy.IsFolder => e => e.IsFolder, (ItemSortBy.IsFolder, _) => e => e.IsFolder,
ItemSortBy.IsPlayed => e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(query.User!.Id))!.Played, (ItemSortBy.IsPlayed, _) => e => e.UserData!.FirstOrDefault(f => f.UserId.Equals(query.User!.Id))!.Played,
ItemSortBy.IsUnplayed => e => !e.UserData!.FirstOrDefault(f => f.UserId.Equals(query.User!.Id))!.Played, (ItemSortBy.IsUnplayed, _) => e => !e.UserData!.FirstOrDefault(f => f.UserId.Equals(query.User!.Id))!.Played,
ItemSortBy.DateLastContentAdded => e => e.DateLastMediaAdded, (ItemSortBy.DateLastContentAdded, _) => e => e.DateLastMediaAdded,
ItemSortBy.Artist => e => e.ItemValues!.Where(f => f.ItemValue.Type == ItemValueType.Artist).Select(f => f.ItemValue.CleanValue).FirstOrDefault(), (ItemSortBy.Artist, _) => e => e.ItemValues!.Where(f => f.ItemValue.Type == ItemValueType.Artist).Select(f => f.ItemValue.CleanValue).FirstOrDefault(),
ItemSortBy.AlbumArtist => e => e.ItemValues!.Where(f => f.ItemValue.Type == ItemValueType.AlbumArtist).Select(f => f.ItemValue.CleanValue).FirstOrDefault(), (ItemSortBy.AlbumArtist, _) => e => e.ItemValues!.Where(f => f.ItemValue.Type == ItemValueType.AlbumArtist).Select(f => f.ItemValue.CleanValue).FirstOrDefault(),
ItemSortBy.Studio => e => e.ItemValues!.Where(f => f.ItemValue.Type == ItemValueType.Studios).Select(f => f.ItemValue.CleanValue).FirstOrDefault(), (ItemSortBy.Studio, _) => e => e.ItemValues!.Where(f => f.ItemValue.Type == ItemValueType.Studios).Select(f => f.ItemValue.CleanValue).FirstOrDefault(),
ItemSortBy.OfficialRating => e => e.InheritedParentalRatingValue, (ItemSortBy.OfficialRating, _) => e => e.InheritedParentalRatingValue,
// ItemSortBy.SeriesDatePlayed => "(Select MAX(LastPlayedDate) from TypedBaseItems B" + GetJoinUserDataText(query) + " where Played=1 and B.SeriesPresentationUniqueKey=A.PresentationUniqueKey)", (ItemSortBy.SeriesSortName, _) => e => e.SeriesName,
ItemSortBy.SeriesSortName => e => e.SeriesName, (ItemSortBy.Album, _) => e => e.Album,
(ItemSortBy.DateCreated, _) => e => e.DateCreated,
(ItemSortBy.PremiereDate, _) => e => (e.PremiereDate ?? (e.ProductionYear.HasValue ? DateTime.MinValue.AddYears(e.ProductionYear.Value - 1) : null)),
(ItemSortBy.StartDate, _) => e => e.StartDate,
(ItemSortBy.Name, _) => e => e.CleanName,
(ItemSortBy.CommunityRating, _) => e => e.CommunityRating,
(ItemSortBy.ProductionYear, _) => e => e.ProductionYear,
(ItemSortBy.CriticRating, _) => e => e.CriticRating,
(ItemSortBy.VideoBitRate, _) => e => e.TotalBitrate,
(ItemSortBy.ParentIndexNumber, _) => e => e.ParentIndexNumber,
(ItemSortBy.IndexNumber, _) => e => e.IndexNumber,
(ItemSortBy.SeriesDatePlayed, not null) => e =>
jellyfinDbContext.BaseItems
.Where(w => w.SeriesPresentationUniqueKey == e.PresentationUniqueKey)
.Join(jellyfinDbContext.UserData.Where(w => w.UserId == query.User.Id && w.Played), f => f.Id, f => f.ItemId, (item, userData) => userData.LastPlayedDate)
.Max(f => f),
(ItemSortBy.SeriesDatePlayed, null) => e => jellyfinDbContext.BaseItems.Where(w => w.SeriesPresentationUniqueKey == e.PresentationUniqueKey)
.Join(jellyfinDbContext.UserData.Where(w => w.Played), f => f.Id, f => f.ItemId, (item, userData) => userData.LastPlayedDate)
.Max(f => f),
// ItemSortBy.SeriesDatePlayed => e => jellyfinDbContext.UserData
// .Where(u => u.Item!.SeriesPresentationUniqueKey == e.PresentationUniqueKey && u.Played)
// .Max(f => f.LastPlayedDate),
// ItemSortBy.AiredEpisodeOrder => "AiredEpisodeOrder", // ItemSortBy.AiredEpisodeOrder => "AiredEpisodeOrder",
ItemSortBy.Album => e => e.Album,
ItemSortBy.DateCreated => e => e.DateCreated,
ItemSortBy.PremiereDate => e => (e.PremiereDate ?? (e.ProductionYear.HasValue ? DateTime.MinValue.AddYears(e.ProductionYear.Value - 1) : null)),
ItemSortBy.StartDate => e => e.StartDate,
ItemSortBy.Name => e => e.CleanName,
ItemSortBy.CommunityRating => e => e.CommunityRating,
ItemSortBy.ProductionYear => e => e.ProductionYear,
ItemSortBy.CriticRating => e => e.CriticRating,
ItemSortBy.VideoBitRate => e => e.TotalBitrate,
ItemSortBy.ParentIndexNumber => e => e.ParentIndexNumber,
ItemSortBy.IndexNumber => e => e.IndexNumber,
_ => e => e.SortName _ => e => e.SortName
}; };
} }

View File

@@ -13,7 +13,6 @@ namespace Jellyfin.Server.Implementations.StorageHelpers;
public static class StorageHelper public static class StorageHelper
{ {
private const long TwoGigabyte = 2_147_483_647L; private const long TwoGigabyte = 2_147_483_647L;
private const long FiveHundredAndTwelveMegaByte = 536_870_911L;
private static readonly string[] _byteHumanizedSuffixes = ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"]; private static readonly string[] _byteHumanizedSuffixes = ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"];
/// <summary> /// <summary>
@@ -24,10 +23,8 @@ public static class StorageHelper
public static void TestCommonPathsForStorageCapacity(IApplicationPaths applicationPaths, ILogger logger) public static void TestCommonPathsForStorageCapacity(IApplicationPaths applicationPaths, ILogger logger)
{ {
TestDataDirectorySize(applicationPaths.DataPath, logger, TwoGigabyte); TestDataDirectorySize(applicationPaths.DataPath, logger, TwoGigabyte);
TestDataDirectorySize(applicationPaths.LogDirectoryPath, logger, FiveHundredAndTwelveMegaByte);
TestDataDirectorySize(applicationPaths.CachePath, logger, TwoGigabyte); TestDataDirectorySize(applicationPaths.CachePath, logger, TwoGigabyte);
TestDataDirectorySize(applicationPaths.ProgramDataPath, logger, TwoGigabyte); TestDataDirectorySize(applicationPaths.ProgramDataPath, logger, TwoGigabyte);
TestDataDirectorySize(applicationPaths.TempDirectory, logger, FiveHundredAndTwelveMegaByte);
} }
/// <summary> /// <summary>

View File

@@ -254,10 +254,10 @@ public class TrickplayManager : ITrickplayManager
} }
// We support video backdrops, but we should not generate trickplay images for them // We support video backdrops, but we should not generate trickplay images for them
var parentDirectory = Directory.GetParent(mediaPath); var parentDirectory = Directory.GetParent(video.Path);
if (parentDirectory is not null && string.Equals(parentDirectory.Name, "backdrops", StringComparison.OrdinalIgnoreCase)) if (parentDirectory is not null && string.Equals(parentDirectory.Name, "backdrops", StringComparison.OrdinalIgnoreCase))
{ {
_logger.LogDebug("Ignoring backdrop media found at {Path} for item {ItemID}", mediaPath, video.Id); _logger.LogDebug("Ignoring backdrop media found at {Path} for item {ItemID}", video.Path, video.Id);
return; return;
} }

View File

@@ -59,7 +59,7 @@ namespace Jellyfin.Server.Implementations.Users
} }
// As long as jellyfin supports password-less users, we need this little block here to accommodate // As long as jellyfin supports password-less users, we need this little block here to accommodate
if (!HasPassword(resolvedUser) && string.IsNullOrEmpty(password)) if (string.IsNullOrEmpty(resolvedUser.Password) && string.IsNullOrEmpty(password))
{ {
return Task.FromResult(new ProviderAuthenticationResult return Task.FromResult(new ProviderAuthenticationResult
{ {
@@ -93,10 +93,6 @@ namespace Jellyfin.Server.Implementations.Users
}); });
} }
/// <inheritdoc />
public bool HasPassword(User user)
=> !string.IsNullOrEmpty(user?.Password);
/// <inheritdoc /> /// <inheritdoc />
public Task ChangePassword(User user, string newPassword) public Task ChangePassword(User user, string newPassword)
{ {

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text.Json; using System.Text.Json;
@@ -92,33 +93,38 @@ namespace Jellyfin.Server.Implementations.Users
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<ForgotPasswordResult> StartForgotPasswordProcess(User user, bool isInNetwork) public async Task<ForgotPasswordResult> StartForgotPasswordProcess(User? user, string enteredUsername, bool isInNetwork)
{ {
byte[] bytes = new byte[4];
RandomNumberGenerator.Fill(bytes);
string pin = BitConverter.ToString(bytes);
DateTime expireTime = DateTime.UtcNow.AddMinutes(30); DateTime expireTime = DateTime.UtcNow.AddMinutes(30);
string filePath = _passwordResetFileBase + user.Id + ".json"; var usernameHash = enteredUsername.ToUpperInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture);
SerializablePasswordReset spr = new SerializablePasswordReset var pinFile = _passwordResetFileBase + usernameHash + ".json";
{
ExpirationDate = expireTime,
Pin = pin,
PinFile = filePath,
UserName = user.Username
};
FileStream fileStream = AsyncFile.Create(filePath); if (user is not null && isInNetwork)
await using (fileStream.ConfigureAwait(false))
{ {
await JsonSerializer.SerializeAsync(fileStream, spr).ConfigureAwait(false); byte[] bytes = new byte[4];
RandomNumberGenerator.Fill(bytes);
string pin = BitConverter.ToString(bytes);
SerializablePasswordReset spr = new SerializablePasswordReset
{
ExpirationDate = expireTime,
Pin = pin,
PinFile = pinFile,
UserName = user.Username
};
FileStream fileStream = AsyncFile.Create(pinFile);
await using (fileStream.ConfigureAwait(false))
{
await JsonSerializer.SerializeAsync(fileStream, spr).ConfigureAwait(false);
}
} }
return new ForgotPasswordResult return new ForgotPasswordResult
{ {
Action = ForgotPasswordAction.PinCode, Action = ForgotPasswordAction.PinCode,
PinExpirationDate = expireTime, PinExpirationDate = expireTime,
PinFile = filePath PinFile = pinFile
}; };
} }

View File

@@ -21,12 +21,6 @@ namespace Jellyfin.Server.Implementations.Users
throw new AuthenticationException("User Account cannot login with this provider. The Normal provider for this user cannot be found"); throw new AuthenticationException("User Account cannot login with this provider. The Normal provider for this user cannot be found");
} }
/// <inheritdoc />
public bool HasPassword(User user)
{
return true;
}
/// <inheritdoc /> /// <inheritdoc />
public Task ChangePassword(User user, string newPassword) public Task ChangePassword(User user, string newPassword)
{ {

View File

@@ -306,15 +306,12 @@ namespace Jellyfin.Server.Implementations.Users
/// <inheritdoc/> /// <inheritdoc/>
public UserDto GetUserDto(User user, string? remoteEndPoint = null) public UserDto GetUserDto(User user, string? remoteEndPoint = null)
{ {
var hasPassword = GetAuthenticationProvider(user).HasPassword(user);
var castReceiverApplications = _serverConfigurationManager.Configuration.CastReceiverApplications; var castReceiverApplications = _serverConfigurationManager.Configuration.CastReceiverApplications;
return new UserDto return new UserDto
{ {
Name = user.Username, Name = user.Username,
Id = user.Id, Id = user.Id,
ServerId = _appHost.SystemId, ServerId = _appHost.SystemId,
HasPassword = hasPassword,
HasConfiguredPassword = hasPassword,
EnableAutoLogin = user.EnableAutoLogin, EnableAutoLogin = user.EnableAutoLogin,
LastLoginDate = user.LastLoginDate, LastLoginDate = user.LastLoginDate,
LastActivityDate = user.LastActivityDate, LastActivityDate = user.LastActivityDate,
@@ -508,23 +505,18 @@ namespace Jellyfin.Server.Implementations.Users
public async Task<ForgotPasswordResult> StartForgotPasswordProcess(string enteredUsername, bool isInNetwork) public async Task<ForgotPasswordResult> StartForgotPasswordProcess(string enteredUsername, bool isInNetwork)
{ {
var user = string.IsNullOrWhiteSpace(enteredUsername) ? null : GetUserByName(enteredUsername); var user = string.IsNullOrWhiteSpace(enteredUsername) ? null : GetUserByName(enteredUsername);
var passwordResetProvider = GetPasswordResetProvider(user);
var result = await passwordResetProvider
.StartForgotPasswordProcess(user, enteredUsername, isInNetwork)
.ConfigureAwait(false);
if (user is not null && isInNetwork) if (user is not null && isInNetwork)
{ {
var passwordResetProvider = GetPasswordResetProvider(user);
var result = await passwordResetProvider
.StartForgotPasswordProcess(user, isInNetwork)
.ConfigureAwait(false);
await UpdateUserAsync(user).ConfigureAwait(false); await UpdateUserAsync(user).ConfigureAwait(false);
return result;
} }
return new ForgotPasswordResult return result;
{
Action = ForgotPasswordAction.InNetworkRequired,
PinFile = string.Empty
};
} }
/// <inheritdoc/> /// <inheritdoc/>
@@ -760,8 +752,13 @@ namespace Jellyfin.Server.Implementations.Users
return GetAuthenticationProviders(user)[0]; return GetAuthenticationProviders(user)[0];
} }
private IPasswordResetProvider GetPasswordResetProvider(User user) private IPasswordResetProvider GetPasswordResetProvider(User? user)
{ {
if (user is null)
{
return _defaultPasswordResetProvider;
}
return GetPasswordResetProviders(user)[0]; return GetPasswordResetProviders(user)[0];
} }

View File

@@ -33,9 +33,11 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors.Infrastructure; using Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen; using Swashbuckle.AspNetCore.SwaggerGen;
using AuthenticationSchemes = Jellyfin.Api.Constants.AuthenticationSchemes; using AuthenticationSchemes = Jellyfin.Api.Constants.AuthenticationSchemes;
@@ -259,7 +261,8 @@ namespace Jellyfin.Server.Extensions
c.OperationFilter<FileRequestFilter>(); c.OperationFilter<FileRequestFilter>();
c.OperationFilter<ParameterObsoleteFilter>(); c.OperationFilter<ParameterObsoleteFilter>();
c.DocumentFilter<AdditionalModelFilter>(); c.DocumentFilter<AdditionalModelFilter>();
}); })
.Replace(ServiceDescriptor.Transient<ISwaggerProvider, CachingOpenApiProvider>());
} }
private static void AddPolicy(this AuthorizationOptions authorizationOptions, string policyName, IAuthorizationRequirement authorizationRequirement) private static void AddPolicy(this AuthorizationOptions authorizationOptions, string policyName, IAuthorizationRequirement authorizationRequirement)

View File

@@ -0,0 +1,89 @@
using System;
using System.Threading;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace Jellyfin.Server.Filters;
/// <summary>
/// OpenApi provider with caching.
/// </summary>
internal sealed class CachingOpenApiProvider : ISwaggerProvider
{
private const string CacheKey = "openapi.json";
private static readonly MemoryCacheEntryOptions _cacheOptions = new() { SlidingExpiration = TimeSpan.FromMinutes(5) };
private static readonly SemaphoreSlim _lock = new(1, 1);
private static readonly TimeSpan _lockTimeout = TimeSpan.FromSeconds(1);
private readonly IMemoryCache _memoryCache;
private readonly SwaggerGenerator _swaggerGenerator;
private readonly SwaggerGeneratorOptions _swaggerGeneratorOptions;
/// <summary>
/// Initializes a new instance of the <see cref="CachingOpenApiProvider"/> class.
/// </summary>
/// <param name="optionsAccessor">The options accessor.</param>
/// <param name="apiDescriptionsProvider">The api descriptions provider.</param>
/// <param name="schemaGenerator">The schema generator.</param>
/// <param name="memoryCache">The memory cache.</param>
public CachingOpenApiProvider(
IOptions<SwaggerGeneratorOptions> optionsAccessor,
IApiDescriptionGroupCollectionProvider apiDescriptionsProvider,
ISchemaGenerator schemaGenerator,
IMemoryCache memoryCache)
{
_swaggerGeneratorOptions = optionsAccessor.Value;
_swaggerGenerator = new SwaggerGenerator(_swaggerGeneratorOptions, apiDescriptionsProvider, schemaGenerator);
_memoryCache = memoryCache;
}
/// <inheritdoc />
public OpenApiDocument GetSwagger(string documentName, string? host = null, string? basePath = null)
{
if (_memoryCache.TryGetValue(CacheKey, out OpenApiDocument? openApiDocument) && openApiDocument is not null)
{
return AdjustDocument(openApiDocument, host, basePath);
}
var acquired = _lock.Wait(_lockTimeout);
try
{
if (_memoryCache.TryGetValue(CacheKey, out openApiDocument) && openApiDocument is not null)
{
return AdjustDocument(openApiDocument, host, basePath);
}
if (!acquired)
{
throw new InvalidOperationException("OpenApi document is generating");
}
openApiDocument = _swaggerGenerator.GetSwagger(documentName);
_memoryCache.Set(CacheKey, openApiDocument, _cacheOptions);
return AdjustDocument(openApiDocument, host, basePath);
}
finally
{
if (acquired)
{
_lock.Release();
}
}
}
private OpenApiDocument AdjustDocument(OpenApiDocument document, string? host, string? basePath)
{
document.Servers = _swaggerGeneratorOptions.Servers.Count != 0
? _swaggerGeneratorOptions.Servers
: string.IsNullOrEmpty(host) && string.IsNullOrEmpty(basePath)
? []
: [new OpenApiServer { Url = $"{host}{basePath}" }];
return document;
}
}

View File

@@ -78,7 +78,7 @@
<None Update="wwwroot\api-docs\swagger\custom.css"> <None Update="wwwroot\api-docs\swagger\custom.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="wwwroot\api-docs\banner-dark.svg"> <None Update="wwwroot\api-docs\jellyfin.svg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="ServerSetupApp/index.mstemplate.html"> <None Update="ServerSetupApp/index.mstemplate.html">

View File

@@ -0,0 +1,32 @@
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Configuration;
namespace Jellyfin.Server.Migrations.Routines;
/// <summary>
/// Migration to disable legacy authorization in the system config.
/// </summary>
[JellyfinMigration("2025-11-18T16:00:00", nameof(DisableLegacyAuthorization))]
public class DisableLegacyAuthorization : IAsyncMigrationRoutine
{
private readonly IServerConfigurationManager _serverConfigurationManager;
/// <summary>
/// Initializes a new instance of the <see cref="DisableLegacyAuthorization"/> class.
/// </summary>
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
public DisableLegacyAuthorization(IServerConfigurationManager serverConfigurationManager)
{
_serverConfigurationManager = serverConfigurationManager;
}
/// <inheritdoc />
public Task PerformAsync(CancellationToken cancellationToken)
{
_serverConfigurationManager.Configuration.EnableLegacyAuthorization = false;
_serverConfigurationManager.SaveConfiguration();
return Task.CompletedTask;
}
}

View File

@@ -71,7 +71,7 @@ namespace Jellyfin.Server.Migrations.Routines
if (row.GetInt32(0) == 0) if (row.GetInt32(0) == 0)
{ {
_logger.LogWarning("Table 'ActivityLog' doesn't exist in {ActivityLogPath}, nothing to migrate", activityLogPath); _logger.LogWarning("Table 'ActivityLog' doesn't exist in {ActivityLogPath}, nothing to migrate", activityLogPath);
break; return;
} }
} }

View File

@@ -50,9 +50,28 @@ namespace Jellyfin.Server.Migrations.Routines
public void Perform() public void Perform()
{ {
var dataPath = _appPaths.DataPath; var dataPath = _appPaths.DataPath;
using (var connection = new SqliteConnection($"Filename={Path.Combine(dataPath, DbFilename)}")) var dbFilePath = Path.Combine(dataPath, DbFilename);
if (!File.Exists(dbFilePath))
{
_logger.LogWarning("{Path} doesn't exist, nothing to migrate", dbFilePath);
return;
}
using (var connection = new SqliteConnection($"Filename={dbFilePath}"))
{ {
connection.Open(); connection.Open();
var tableQuery = connection.Query("SELECT count(*) FROM sqlite_master WHERE type='table' AND name='Tokens';");
foreach (var row in tableQuery)
{
if (row.GetInt32(0) == 0)
{
_logger.LogWarning("Table 'Tokens' doesn't exist in {Path}, nothing to migrate", dbFilePath);
return;
}
}
using var dbContext = _dbProvider.CreateDbContext(); using var dbContext = _dbProvider.CreateDbContext();
var authenticatedDevices = connection.Query("SELECT * FROM Tokens"); var authenticatedDevices = connection.Query("SELECT * FROM Tokens");

View File

@@ -78,9 +78,27 @@ namespace Jellyfin.Server.Migrations.Routines
var displayPrefs = new HashSet<string>(StringComparer.OrdinalIgnoreCase); var displayPrefs = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var customDisplayPrefs = new HashSet<string>(StringComparer.OrdinalIgnoreCase); var customDisplayPrefs = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var dbFilePath = Path.Combine(_paths.DataPath, DbFilename); var dbFilePath = Path.Combine(_paths.DataPath, DbFilename);
if (!File.Exists(dbFilePath))
{
_logger.LogWarning("{Path} doesn't exist, nothing to migrate", dbFilePath);
return;
}
using (var connection = new SqliteConnection($"Filename={dbFilePath}")) using (var connection = new SqliteConnection($"Filename={dbFilePath}"))
{ {
connection.Open(); connection.Open();
var tableQuery = connection.Query("SELECT count(*) FROM sqlite_master WHERE type='table' AND name='userdisplaypreferences';");
foreach (var row in tableQuery)
{
if (row.GetInt32(0) == 0)
{
_logger.LogWarning("Table 'userdisplaypreferences' doesn't exist in {Path}, nothing to migrate", dbFilePath);
return;
}
}
using var dbContext = _provider.CreateDbContext(); using var dbContext = _provider.CreateDbContext();
var results = connection.Query("SELECT * FROM userdisplaypreferences"); var results = connection.Query("SELECT * FROM userdisplaypreferences");

View File

@@ -383,8 +383,6 @@ internal class MigrateLibraryDb : IDatabaseMigrationRoutine
}); });
} }
baseItemIds.Clear();
foreach (var item in peopleCache) foreach (var item in peopleCache)
{ {
operation.JellyfinDbContext.Peoples.Add(item.Value.Person); operation.JellyfinDbContext.Peoples.Add(item.Value.Person);

View File

@@ -75,7 +75,7 @@ public class MigrateUserDb : IMigrationRoutine
if (row.GetInt32(0) == 0) if (row.GetInt32(0) == 0)
{ {
_logger.LogWarning("Table 'LocalUsersv2' doesn't exist in {UserDbPath}, nothing to migrate", userDbPath); _logger.LogWarning("Table 'LocalUsersv2' doesn't exist in {UserDbPath}, nothing to migrate", userDbPath);
break; return;
} }
} }

View File

@@ -11,7 +11,7 @@
{ {
"Name": "Console", "Name": "Console",
"Args": { "Args": {
"outputTemplate": "[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}" "outputTemplate": "[{Timestamp:HH:mm:ss.fff}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}"
} }
}, },
{ {

View File

@@ -249,6 +249,7 @@ public sealed class SetupServer : IDisposable
{ {
{ "isInReportingMode", _isUnhealthy }, { "isInReportingMode", _isUnhealthy },
{ "retryValue", retryAfterValue }, { "retryValue", retryAfterValue },
{ "version", typeof(Emby.Server.Implementations.ApplicationHost).Assembly.GetName().Version! },
{ "logs", startupLogEntries }, { "logs", startupLogEntries },
{ "networkManagerReady", networkManager is not null }, { "networkManagerReady", networkManager is not null },
{ "localNetworkRequest", networkManager is not null && context.Connection.RemoteIpAddress is not null && networkManager.IsInLocalNetwork(context.Connection.RemoteIpAddress) } { "localNetworkRequest", networkManager is not null && context.Connection.RemoteIpAddress is not null && networkManager.IsInLocalNetwork(context.Connection.RemoteIpAddress) }

View File

@@ -173,7 +173,7 @@
<header class="flex-row"> <header class="flex-row">
{{^IF isInReportingMode}} {{^IF isInReportingMode}}
<p>Jellyfin Server still starting. Please wait.</p> <p>Jellyfin Server {{version}} still starting. Please wait.</p>
{{#ELSE}} {{#ELSE}}
<p>Jellyfin Server has encountered an error and was not able to start.</p> <p>Jellyfin Server has encountered an error and was not able to start.</p>
{{/ELSE}} {{/ELSE}}

View File

@@ -1,34 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- ***** BEGIN LICENSE BLOCK *****
- Part of the Jellyfin project (https://jellyfin.media)
-
- All copyright belongs to the Jellyfin contributors; a full list can
- be found in the file CONTRIBUTORS.md
-
- This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
- To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/.
- ***** END LICENSE BLOCK ***** -->
<svg id="banner-dark" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1536 512">
<defs>
<linearGradient id="linear-gradient" x1="110.25" y1="213.3" x2="496.14" y2="436.09" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#aa5cc3"/>
<stop offset="1" stop-color="#00a4dc"/>
</linearGradient>
</defs>
<title>banner-dark</title>
<g id="banner-dark">
<g id="banner-dark-icon">
<path id="inner-shape" d="M261.42,201.62c-20.44,0-86.24,119.29-76.2,139.43s142.48,19.92,152.4,0S281.86,201.63,261.42,201.62Z" fill="url(#linear-gradient)"/>
<path id="outer-shape" d="M261.42,23.3C199.83,23.3,1.57,382.73,31.8,443.43s429.34,60,459.24,0S323,23.3,261.42,23.3ZM411.9,390.76c-19.59,39.33-281.08,39.77-300.9,0S221.1,115.48,261.45,115.48,431.49,351.42,411.9,390.76Z" fill="url(#linear-gradient)"/>
</g>
<g id="jellyfin-light-outlines" style="isolation:isolate" transform="translate(43.8)">
<path d="M556.64,350.75a67,67,0,0,1-22.87-27.47,8.91,8.91,0,0,1-1.49-4.75,7.42,7.42,0,0,1,2.83-5.94,9.25,9.25,0,0,1,6.09-2.38c3.16,0,5.94,1.69,8.31,5.05a48.09,48.09,0,0,0,16.34,20.34,40.59,40.59,0,0,0,24,7.58q20.51,0,33.27-12.62t12.77-33.12V159a8.44,8.44,0,0,1,2.67-6.39,9.56,9.56,0,0,1,6.83-2.52,9,9,0,0,1,6.68,2.52,8.7,8.7,0,0,1,2.53,6.39v138.4a64.7,64.7,0,0,1-8.32,32.67,59,59,0,0,1-23,22.72Q608.62,361,589.9,361A57.21,57.21,0,0,1,556.64,350.75Z" fill="#fff"/>
<path d="M831.66,279.47a8.77,8.77,0,0,1-6.24,2.53H713.16q0,17.82,7.27,31.92a54.91,54.91,0,0,0,20.79,22.28q13.51,8.18,31.93,8.17a54,54,0,0,0,25.54-5.94,52.7,52.7,0,0,0,18.12-15.15,10,10,0,0,1,6.24-2.67,8.14,8.14,0,0,1,7.72,7.72,8.81,8.81,0,0,1-3,6.24,74.7,74.7,0,0,1-23.91,19A65.56,65.56,0,0,1,773.45,361q-22.87,0-40.4-9.8a69.51,69.51,0,0,1-27.32-27.48q-9.79-17.66-9.8-40.83,0-24.36,9.65-42.62t25.69-27.92a65.2,65.2,0,0,1,34.16-9.65A70,70,0,0,1,798.84,211a65.78,65.78,0,0,1,25.39,24.36q9.81,16,10.1,38A8.07,8.07,0,0,1,831.66,279.47ZM733.5,231.8Q718.8,243.68,714.64,266H815.92v-2.38A46.91,46.91,0,0,0,807,240.27a48.47,48.47,0,0,0-18.56-15.15,54,54,0,0,0-23-5.2Q748.2,219.92,733.5,231.8Z" fill="#fff"/>
<path d="M888.24,355.5a8.92,8.92,0,0,1-15.3-6.38v-202a8.91,8.91,0,1,1,17.82,0v202A8.65,8.65,0,0,1,888.24,355.5Z" fill="#fff"/>
<path d="M956.55,355.5a8.92,8.92,0,0,1-15.3-6.38v-202a8.91,8.91,0,1,1,17.82,0v202A8.65,8.65,0,0,1,956.55,355.5Z" fill="#fff"/>
<path d="M1122.86,206.11a8.7,8.7,0,0,1,2.53,6.39v131q0,23.44-9.21,40.09a61.58,61.58,0,0,1-25.54,25.25q-16.34,8.61-36.83,8.61a96.73,96.73,0,0,1-23.31-2.68,61.72,61.72,0,0,1-18-7.12q-6.24-3.87-6.24-8.62a17.94,17.94,0,0,1,.6-3,8.06,8.06,0,0,1,3-4.45,7.49,7.49,0,0,1,4.45-1.49,7.91,7.91,0,0,1,3.56.89q19,10.39,36.24,10.4,24.65,0,39.06-15.44t14.4-42.18V333.38a54.37,54.37,0,0,1-21.38,20,62.55,62.55,0,0,1-30.3,7.58q-25.83,0-39.2-15.45t-13.37-41.87V212.5a8.91,8.91,0,1,1,17.82,0V301q0,21.39,9.36,32.38t29.25,11a48,48,0,0,0,23.32-6.09,49.88,49.88,0,0,0,17.82-16,37.44,37.44,0,0,0,6.68-21.24V212.5a9,9,0,0,1,15.29-6.39Z" fill="#fff"/>
<path d="M1210.18,161.41q-5.21,6.24-5.2,17.23v30.59h33.27a8.19,8.19,0,0,1,5.79,2.38,8.26,8.26,0,0,1,0,11.88,8.22,8.22,0,0,1-5.79,2.37H1205V349.12a8.91,8.91,0,1,1-17.82,0V225.86h-21.68a7.83,7.83,0,0,1-5.94-2.52,8.21,8.21,0,0,1-2.37-5.79,8,8,0,0,1,2.37-6.09,8.33,8.33,0,0,1,5.94-2.23h21.68V178.64q0-18.7,10.84-29t29-10.24a46.1,46.1,0,0,1,15.45,2.52q7.13,2.53,7.12,8.17a8.07,8.07,0,0,1-2.37,5.94,7.37,7.37,0,0,1-5.35,2.37,18.81,18.81,0,0,1-6.53-1.48,42,42,0,0,0-10.4-1.78Q1215.37,155.18,1210.18,161.41ZM1276,180.87c-2.19-1.88-3.27-4.61-3.27-8.17v-3q0-5.34,3.41-8.17t9.36-2.82q11.88,0,11.88,11v3c0,3.56-1,6.29-3.12,8.17s-5.1,2.82-9.06,2.82S1278.14,182.75,1276,180.87Zm15.59,174.63a8.92,8.92,0,0,1-15.3-6.38V212.5a8.91,8.91,0,1,1,17.82,0V349.12A8.65,8.65,0,0,1,1291.56,355.5Z" fill="#fff"/>
<path d="M1452.53,218.88q12.92,16.2,12.92,42.92v87.32a8.4,8.4,0,0,1-2.67,6.38,8.8,8.8,0,0,1-6.24,2.53,8.64,8.64,0,0,1-8.91-8.91V262.69q0-19.31-9.65-31.33t-29.85-12a53.28,53.28,0,0,0-42.77,21.83,36.24,36.24,0,0,0-7.13,21.53v86.43a8.91,8.91,0,1,1-17.82,0V216.06a8.91,8.91,0,1,1,17.82,0V232.4q8-12.77,23-21.24A61.84,61.84,0,0,1,1412,202.7Q1439.61,202.7,1452.53,218.88Z" fill="#fff"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.7 KiB

View File

@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" width="251" height="72" fill="none" viewBox="0 0 251 72">
<g clip-path="url(#a)">
<path fill="url(#b)"
d="M24.212 49.158C22.66 46.042 32.838 27.588 36 27.588c3.167.002 13.323 18.488 11.788 21.57-1.534 3.082-22.025 3.116-23.576 0" />
<path fill="url(#c)" fill-rule="evenodd"
d="M.482 64.995C-4.195 55.605 26.477 0 36 0c9.533 0 40.153 55.713 35.527 64.995s-66.368 9.39-71.045 0m12.254-8.148c3.064 6.152 43.518 6.084 46.548 0 3.03-6.086-17.032-42.586-23.275-42.586S9.671 50.694 12.736 56.847"
clip-rule="evenodd" />
<path fill="#fff"
d="M225.22 56c-.28 0-.42 0-.527-.055a.5.5 0 0 1-.219-.218c-.054-.107-.054-.247-.054-.527V26.8c0-.28 0-.42.054-.527a.5.5 0 0 1 .219-.219c.107-.054.247-.054.527-.054h5.183c.28 0 .42 0 .527.054a.5.5 0 0 1 .218.219c.055.107.055.247.055.527v2.895a7.9 7.9 0 0 1 3.419-3.254q2.261-1.103 5.074-1.103 3.308 0 5.845 1.434a10.1 10.1 0 0 1 4.026 4.026q1.434 2.536 1.434 5.9V55.2c0 .28 0 .42-.055.527a.5.5 0 0 1-.218.218c-.107.055-.247.055-.527.055h-5.625c-.28 0-.42 0-.527-.055a.5.5 0 0 1-.218-.218c-.055-.107-.055-.247-.055-.527V38.408q0-2.978-1.709-4.688-1.654-1.764-4.357-1.764-2.702 0-4.412 1.764-1.654 1.766-1.654 4.688V55.2c0 .28 0 .42-.054.527a.5.5 0 0 1-.219.218c-.107.055-.247.055-.527.055zm-11.54-33.363c-.28 0-.42 0-.527-.055a.5.5 0 0 1-.218-.218c-.055-.107-.055-.247-.055-.527v-6.121c0-.28 0-.42.055-.527a.5.5 0 0 1 .218-.219c.107-.054.247-.054.527-.054h5.624c.28 0 .42 0 .527.054a.5.5 0 0 1 .219.219c.054.107.054.247.054.527v6.12c0 .28 0 .42-.054.528a.5.5 0 0 1-.219.218c-.107.055-.247.055-.527.055zm0 33.363c-.28 0-.42 0-.527-.054a.5.5 0 0 1-.218-.219c-.055-.107-.055-.247-.055-.527V26.8c0-.28 0-.42.055-.527a.5.5 0 0 1 .218-.218c.107-.055.247-.055.527-.055h5.624c.28 0 .42 0 .527.055a.5.5 0 0 1 .219.218c.054.107.054.247.054.527v28.4c0 .28 0 .42-.054.527a.5.5 0 0 1-.219.219c-.107.054-.247.054-.527.054zm-16.712-.054c.107.054.247.054.527.054h5.625c.28 0 .42 0 .526-.054a.5.5 0 0 0 .219-.219c.055-.107.055-.247.055-.527V32.452h5.872c.28 0 .42 0 .527-.054a.5.5 0 0 0 .219-.219c.054-.107.054-.247.054-.527V26.8c0-.28 0-.42-.054-.527a.5.5 0 0 0-.219-.218c-.107-.055-.247-.055-.527-.055h-5.872v-.992q0-2.261 1.323-3.31 1.379-1.102 3.75-1.102.454 0 .939.044c.345.031.518.047.634-.004a.48.48 0 0 0 .241-.22c.061-.111.061-.274.061-.6V15.39c0-.304 0-.457-.061-.589a.7.7 0 0 0-.248-.284c-.122-.078-.261-.097-.537-.136a14.5 14.5 0 0 0-1.966-.126q-5.184 0-8.273 2.812t-3.088 7.942V26H186.53c-.3 0-.451 0-.58.05a.75.75 0 0 0-.296.205c-.091.104-.143.244-.248.526l-7.43 19.9-7.483-19.903c-.105-.28-.158-.42-.249-.524a.75.75 0 0 0-.296-.205c-.129-.049-.279-.049-.578-.049h-5.769c-.394 0-.591 0-.717.083a.5.5 0 0 0-.213.314c-.031.147.041.33.186.697L174.281 56l-.661 1.6q-.883 1.874-2.041 3.033-1.103 1.158-3.584 1.158-.883 0-1.875-.166a13 13 0 0 1-.73-.1c-.389-.066-.584-.099-.709-.053a.47.47 0 0 0-.26.22c-.066.116-.066.298-.066.663v4.329c0 .243 0 .365.045.481a.7.7 0 0 0 .189.266c.095.081.194.116.392.185q.684.24 1.47.351 1.158.22 2.371.22 4.246 0 7.059-2.426 2.867-2.37 4.577-6.728l10.517-26.58h5.72V55.2c0 .28 0 .42.055.527a.5.5 0 0 0 .218.219M154.363 56c-.28 0-.42 0-.527-.054a.5.5 0 0 1-.219-.219c-.054-.107-.054-.247-.054-.527V15.054c0-.28 0-.42.054-.527a.5.5 0 0 1 .219-.219c.107-.054.247-.054.527-.054h5.624c.28 0 .42 0 .527.054a.5.5 0 0 1 .218.219c.055.107.055.247.055.527V55.2c0 .28 0 .42-.055.527a.5.5 0 0 1-.218.219c-.107.054-.247.054-.527.054zm-11.621 0c-.28 0-.42 0-.527-.054a.5.5 0 0 1-.219-.219c-.054-.107-.054-.247-.054-.527V15.054c0-.28 0-.42.054-.527a.5.5 0 0 1 .219-.219c.107-.054.247-.054.527-.054h5.624c.28 0 .42 0 .527.054a.5.5 0 0 1 .219.219c.054.107.054.247.054.527V55.2c0 .28 0 .42-.054.527a.5.5 0 0 1-.219.219c-.107.054-.247.054-.527.054zm-18.132.662q-4.632-.001-8.107-2.096a14.6 14.6 0 0 1-5.404-5.68q-1.93-3.585-1.93-7.942 0-4.522 1.93-7.996 1.985-3.53 5.349-5.57 3.42-2.04 7.61-2.04 4.688 0 7.942 2.04 3.253 1.986 4.963 5.294 1.71 3.309 1.709 7.335 0 .828-.11 1.654-.031.45-.12.841c-.037.165-.055.247-.115.33a.55.55 0 0 1-.208.168c-.095.04-.194.04-.393.04h-21.057q.33 3.309 2.537 5.294 2.205 1.986 5.459 1.985 2.482 0 4.191-1.047a8.2 8.2 0 0 0 2.206-1.986c.241-.316.362-.474.484-.542a.6.6 0 0 1 .352-.083c.139.006.296.083.608.236l4.269 2.094c.239.118.359.176.431.275a.52.52 0 0 1 .098.298c0 .122-.058.231-.172.45q-1.432 2.742-4.526 4.607-3.419 2.04-7.996 2.04m-.552-25.368q-2.702 0-4.687 1.654-1.93 1.6-2.537 4.577h14.118q-.22-2.757-2.151-4.466-1.875-1.765-4.743-1.765M90.801 56c-.28 0-.42 0-.527-.054a.5.5 0 0 1-.218-.218C90 55.62 90 55.48 90 55.2v-5.294c0-.28 0-.42.055-.527a.5.5 0 0 1 .218-.218c.107-.055.247-.055.527-.055h1.572q2.646 0 4.19-1.489 1.6-1.545 1.6-4.08V15.715c0-.28 0-.42.055-.527a.5.5 0 0 1 .218-.219c.107-.054.247-.054.527-.054h5.956c.28 0 .42 0 .527.054a.5.5 0 0 1 .218.219c.055.107.055.247.055.527v27.546q0 3.804-1.655 6.672-1.599 2.868-4.632 4.467-2.979 1.6-7.06 1.6z" />
</g>
<defs>
<linearGradient id="b" x1="12" x2="71.999" y1="30.001" y2="63.002"
gradientUnits="userSpaceOnUse">
<stop stop-color="#aa5cc3" />
<stop offset="1" stop-color="#00a4dc" />
</linearGradient>
<linearGradient id="c" x1="12" x2="71.999" y1="29.999" y2="63.001"
gradientUnits="userSpaceOnUse">
<stop stop-color="#aa5cc3" />
<stop offset="1" stop-color="#00a4dc" />
</linearGradient>
<clipPath id="a">
<path fill="#fff" d="M0 0h251v72H0z" />
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -4,12 +4,14 @@
} }
.topbar-wrapper .link:after { .topbar-wrapper .link:after {
content: url(../banner-dark.svg); content: '';
display: block; display: block;
-moz-box-sizing: border-box; background-image: url(../jellyfin.svg);
background-position: center;
background-repeat: no-repeat;
background-size: contain;
box-sizing: border-box; box-sizing: border-box;
max-width: 100%; width: 220px;
max-height: 100%; height: 40px;
width: 150px;
} }
/* end logo */ /* end logo */

View File

@@ -103,11 +103,11 @@ namespace MediaBrowser.Common.Configuration
void MakeSanityCheckOrThrow(); void MakeSanityCheckOrThrow();
/// <summary> /// <summary>
/// Checks and creates the given path and adds it with a marker file if non existant. /// Checks and creates the given path and adds it with a marker file if non existent.
/// </summary> /// </summary>
/// <param name="path">The path to check.</param> /// <param name="path">The path to check.</param>
/// <param name="markerName">The common marker file name.</param> /// <param name="markerName">The common marker file name.</param>
/// <param name="recursive">Check for other settings paths recursivly.</param> /// <param name="recursive">Check for other settings paths recursively.</param>
void CreateAndCheckMarker(string path, string markerName, bool recursive = false); void CreateAndCheckMarker(string path, string markerName, bool recursive = false);
} }
} }

View File

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

View File

@@ -14,8 +14,6 @@ namespace MediaBrowser.Controller.Authentication
Task<ProviderAuthenticationResult> Authenticate(string username, string password); Task<ProviderAuthenticationResult> Authenticate(string username, string password);
bool HasPassword(User user);
Task ChangePassword(User user, string newPassword); Task ChangePassword(User user, string newPassword);
} }

View File

@@ -1,5 +1,3 @@
#nullable disable
#pragma warning disable CS1591 #pragma warning disable CS1591
using System; using System;
@@ -15,11 +13,12 @@ namespace MediaBrowser.Controller.Authentication
bool IsEnabled { get; } bool IsEnabled { get; }
Task<ForgotPasswordResult> StartForgotPasswordProcess(User user, bool isInNetwork); Task<ForgotPasswordResult> StartForgotPasswordProcess(User? user, string enteredUsername, bool isInNetwork);
Task<PinRedeemResult> RedeemPasswordResetPin(string pin); Task<PinRedeemResult> RedeemPasswordResetPin(string pin);
} }
#nullable disable
public class PasswordPinCreationResult public class PasswordPinCreationResult
{ {
public string PinFile { get; set; } public string PinFile { get; set; }

View File

@@ -24,6 +24,7 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaSegments; using MediaBrowser.Controller.MediaSegments;
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
@@ -1127,6 +1128,15 @@ namespace MediaBrowser.Controller.Entities
var protocol = item.PathProtocol; var protocol = item.PathProtocol;
// Resolve the item path so everywhere we use the media source it will always point to
// the correct path even if symlinks are in use. Calling ResolveLinkTarget on a non-link
// path will return null, so it's safe to check for all paths.
var itemPath = item.Path;
if (protocol is MediaProtocol.File && FileSystemHelper.ResolveLinkTarget(itemPath, returnFinalTarget: true) is { Exists: true } linkInfo)
{
itemPath = linkInfo.FullName;
}
var info = new MediaSourceInfo var info = new MediaSourceInfo
{ {
Id = item.Id.ToString("N", CultureInfo.InvariantCulture), Id = item.Id.ToString("N", CultureInfo.InvariantCulture),
@@ -1134,7 +1144,7 @@ namespace MediaBrowser.Controller.Entities
MediaStreams = MediaSourceManager.GetMediaStreams(item.Id), MediaStreams = MediaSourceManager.GetMediaStreams(item.Id),
MediaAttachments = MediaSourceManager.GetMediaAttachments(item.Id), MediaAttachments = MediaSourceManager.GetMediaAttachments(item.Id),
Name = GetMediaSourceName(item), Name = GetMediaSourceName(item),
Path = enablePathSubstitution ? GetMappedPath(item, item.Path, protocol) : item.Path, Path = enablePathSubstitution ? GetMappedPath(item, itemPath, protocol) : itemPath,
RunTimeTicks = item.RunTimeTicks, RunTimeTicks = item.RunTimeTicks,
Container = item.Container, Container = item.Container,
Size = item.Size, Size = item.Size,

View File

@@ -729,9 +729,7 @@ namespace MediaBrowser.Controller.Entities
query.StartIndex = startIndex; query.StartIndex = startIndex;
} }
var result = PostFilterAndSort(items, query); return PostFilterAndSort(items, query);
result.TotalRecordCount = totalCount;
return result;
} }
if (this is not UserRootFolder if (this is not UserRootFolder
@@ -1001,9 +999,7 @@ namespace MediaBrowser.Controller.Entities
items = GetChildren(user, true, out totalItemCount, childQuery).Where(filter); items = GetChildren(user, true, out totalItemCount, childQuery).Where(filter);
} }
var result = PostFilterAndSort(items, query); return PostFilterAndSort(items, query);
result.TotalRecordCount = totalItemCount;
return result;
} }
protected QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items, InternalItemsQuery query) protected QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items, InternalItemsQuery query)
@@ -1039,7 +1035,15 @@ namespace MediaBrowser.Controller.Entities
items = UserViewBuilder.FilterForAdjacency(items.ToList(), query.AdjacentTo.Value); items = UserViewBuilder.FilterForAdjacency(items.ToList(), query.AdjacentTo.Value);
} }
return UserViewBuilder.SortAndPage(items, null, query, LibraryManager); var filteredItems = items as IReadOnlyList<BaseItem> ?? items.ToList();
var result = UserViewBuilder.SortAndPage(filteredItems, null, query, LibraryManager);
if (query.EnableTotalRecordCount)
{
result.TotalRecordCount = filteredItems.Count;
}
return result;
} }
private static IEnumerable<BaseItem> CollapseBoxSetItemsIfNeeded( private static IEnumerable<BaseItem> CollapseBoxSetItemsIfNeeded(
@@ -1052,12 +1056,49 @@ namespace MediaBrowser.Controller.Entities
{ {
ArgumentNullException.ThrowIfNull(items); ArgumentNullException.ThrowIfNull(items);
if (CollapseBoxSetItems(query, queryParent, user, configurationManager)) if (!CollapseBoxSetItems(query, queryParent, user, configurationManager))
{ {
items = collectionManager.CollapseItemsWithinBoxSets(items, user); return items;
} }
return items; var config = configurationManager.Configuration;
bool collapseMovies = config.EnableGroupingMoviesIntoCollections;
bool collapseSeries = config.EnableGroupingShowsIntoCollections;
if (user is null || (collapseMovies && collapseSeries))
{
return collectionManager.CollapseItemsWithinBoxSets(items, user);
}
if (!collapseMovies && !collapseSeries)
{
return items;
}
var collapsibleItems = new List<BaseItem>();
var remainingItems = new List<BaseItem>();
foreach (var item in items)
{
if ((collapseMovies && item is Movie) || (collapseSeries && item is Series))
{
collapsibleItems.Add(item);
}
else
{
remainingItems.Add(item);
}
}
if (collapsibleItems.Count == 0)
{
return remainingItems;
}
var collapsedItems = collectionManager.CollapseItemsWithinBoxSets(collapsibleItems, user);
return collapsedItems.Concat(remainingItems);
} }
private static bool CollapseBoxSetItems( private static bool CollapseBoxSetItems(
@@ -1088,24 +1129,26 @@ namespace MediaBrowser.Controller.Entities
} }
var param = query.CollapseBoxSetItems; var param = query.CollapseBoxSetItems;
if (param.HasValue)
if (!param.HasValue)
{ {
if (user is not null && query.IncludeItemTypes.Any(type => return param.Value && AllowBoxSetCollapsing(query);
(type == BaseItemKind.Movie && !configurationManager.Configuration.EnableGroupingMoviesIntoCollections) ||
(type == BaseItemKind.Series && !configurationManager.Configuration.EnableGroupingShowsIntoCollections)))
{
return false;
}
if (query.IncludeItemTypes.Length == 0
|| query.IncludeItemTypes.Any(type => type == BaseItemKind.Movie || type == BaseItemKind.Series))
{
param = true;
}
} }
return param.HasValue && param.Value && AllowBoxSetCollapsing(query); var config = configurationManager.Configuration;
bool queryHasMovies = query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(BaseItemKind.Movie);
bool queryHasSeries = query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(BaseItemKind.Series);
bool collapseMovies = config.EnableGroupingMoviesIntoCollections;
bool collapseSeries = config.EnableGroupingShowsIntoCollections;
if (user is not null)
{
bool canCollapse = (queryHasMovies && collapseMovies) || (queryHasSeries && collapseSeries);
return canCollapse && AllowBoxSetCollapsing(query);
}
return (queryHasMovies || queryHasSeries) && AllowBoxSetCollapsing(query);
} }
private static bool AllowBoxSetCollapsing(InternalItemsQuery request) private static bool AllowBoxSetCollapsing(InternalItemsQuery request)

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
@@ -61,4 +62,82 @@ public static class FileSystemHelper
} }
} }
} }
/// <summary>
/// Gets the target of the specified file link.
/// </summary>
/// <remarks>
/// This helper exists because of this upstream runtime issue; https://github.com/dotnet/runtime/issues/92128.
/// </remarks>
/// <param name="linkPath">The path of the file link.</param>
/// <param name="returnFinalTarget">true to follow links to the final target; false to return the immediate next link.</param>
/// <returns>
/// A <see cref="FileInfo"/> if the <paramref name="linkPath"/> is a link, regardless of if the target exists; otherwise, <c>null</c>.
/// </returns>
public static FileInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget = false)
{
// Check if the file exists so the native resolve handler won't throw at us.
if (!File.Exists(linkPath))
{
return null;
}
if (!returnFinalTarget)
{
return File.ResolveLinkTarget(linkPath, returnFinalTarget: false) as FileInfo;
}
if (File.ResolveLinkTarget(linkPath, returnFinalTarget: false) is not FileInfo targetInfo)
{
return null;
}
if (!targetInfo.Exists)
{
return targetInfo;
}
var currentPath = targetInfo.FullName;
var visited = new HashSet<string>(StringComparer.Ordinal) { linkPath, currentPath };
while (File.ResolveLinkTarget(currentPath, returnFinalTarget: false) is FileInfo linkInfo)
{
var targetPath = linkInfo.FullName;
// If an infinite loop is detected, return the file info for the
// first link in the loop we encountered.
if (!visited.Add(targetPath))
{
return new FileInfo(targetPath);
}
targetInfo = linkInfo;
currentPath = targetPath;
// Exit if the target doesn't exist, so the native resolve handler won't throw at us.
if (!targetInfo.Exists)
{
break;
}
}
return targetInfo;
}
/// <summary>
/// Gets the target of the specified file link.
/// </summary>
/// <remarks>
/// This helper exists because of this upstream runtime issue; https://github.com/dotnet/runtime/issues/92128.
/// </remarks>
/// <param name="fileInfo">The file info of the file link.</param>
/// <param name="returnFinalTarget">true to follow links to the final target; false to return the immediate next link.</param>
/// <returns>
/// A <see cref="FileInfo"/> if the <paramref name="fileInfo"/> is a link, regardless of if the target exists; otherwise, <c>null</c>.
/// </returns>
public static FileInfo? ResolveLinkTarget(FileInfo fileInfo, bool returnFinalTarget = false)
{
ArgumentNullException.ThrowIfNull(fileInfo);
return ResolveLinkTarget(fileInfo.FullName, returnFinalTarget);
}
} }

View File

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

View File

@@ -154,11 +154,12 @@ namespace MediaBrowser.MediaEncoding.Probing
info.Name = tags.GetFirstNotNullNorWhiteSpaceValue("title", "title-eng"); info.Name = tags.GetFirstNotNullNorWhiteSpaceValue("title", "title-eng");
info.ForcedSortName = tags.GetFirstNotNullNorWhiteSpaceValue("sort_name", "title-sort", "titlesort"); info.ForcedSortName = tags.GetFirstNotNullNorWhiteSpaceValue("sort_name", "title-sort", "titlesort");
info.Overview = tags.GetFirstNotNullNorWhiteSpaceValue("synopsis", "description", "desc"); info.Overview = tags.GetFirstNotNullNorWhiteSpaceValue("synopsis", "description", "desc", "comment");
info.IndexNumber = FFProbeHelpers.GetDictionaryNumericValue(tags, "episode_sort");
info.ParentIndexNumber = FFProbeHelpers.GetDictionaryNumericValue(tags, "season_number"); info.ParentIndexNumber = FFProbeHelpers.GetDictionaryNumericValue(tags, "season_number");
info.ShowName = tags.GetValueOrDefault("show_name"); info.IndexNumber = FFProbeHelpers.GetDictionaryNumericValue(tags, "episode_sort") ??
FFProbeHelpers.GetDictionaryNumericValue(tags, "episode_id");
info.ShowName = tags.GetValueOrDefault("show_name", "show");
info.ProductionYear = FFProbeHelpers.GetDictionaryNumericValue(tags, "date"); info.ProductionYear = FFProbeHelpers.GetDictionaryNumericValue(tags, "date");
// Several different forms of retail/premiere date // Several different forms of retail/premiere date

View File

@@ -396,7 +396,7 @@ public sealed class TranscodeManager : ITranscodeManager, IDisposable
ArgumentException.ThrowIfNullOrEmpty(_mediaEncoder.EncoderPath); ArgumentException.ThrowIfNullOrEmpty(_mediaEncoder.EncoderPath);
// If subtitles get burned in fonts may need to be extracted from the media file // If subtitles get burned in fonts may need to be extracted from the media file
if (state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode) if (state.SubtitleStream is not null && (state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode || state.BaseRequest.AlwaysBurnInSubtitleWhenTranscoding))
{ {
if (state.MediaSource.VideoType == VideoType.Dvd || state.MediaSource.VideoType == VideoType.BluRay) if (state.MediaSource.VideoType == VideoType.Dvd || state.MediaSource.VideoType == VideoType.BluRay)
{ {

View File

@@ -287,5 +287,5 @@ public class ServerConfiguration : BaseApplicationConfiguration
/// <summary> /// <summary>
/// Gets or sets a value indicating whether old authorization methods are allowed. /// Gets or sets a value indicating whether old authorization methods are allowed.
/// </summary> /// </summary>
public bool EnableLegacyAuthorization { get; set; } = true; public bool EnableLegacyAuthorization { get; set; }
} }

View File

@@ -1250,30 +1250,37 @@ public class StreamInfo
if (info.DeliveryMethod == SubtitleDeliveryMethod.External) if (info.DeliveryMethod == SubtitleDeliveryMethod.External)
{ {
if (MediaSource.Protocol == MediaProtocol.File || !string.Equals(stream.Codec, subtitleProfile.Format, StringComparison.OrdinalIgnoreCase) || !stream.IsExternal) // Default to using the API URL
{ info.Url = string.Format(
info.Url = string.Format( CultureInfo.InvariantCulture,
CultureInfo.InvariantCulture, "{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}",
"{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}", baseUrl,
baseUrl, ItemId,
ItemId, MediaSourceId,
MediaSourceId, stream.Index.ToString(CultureInfo.InvariantCulture),
stream.Index.ToString(CultureInfo.InvariantCulture), startPositionTicks.ToString(CultureInfo.InvariantCulture),
startPositionTicks.ToString(CultureInfo.InvariantCulture), subtitleProfile.Format);
subtitleProfile.Format); info.IsExternalUrl = false; // Default to API URL
if (!string.IsNullOrEmpty(accessToken)) // Check conditions for potentially using the direct path
{ if (stream.IsExternal // Must be external
info.Url += "?ApiKey=" + accessToken; && MediaSource?.Protocol != MediaProtocol.File // Main media must not be a local file
} && string.Equals(stream.Codec, subtitleProfile.Format, StringComparison.OrdinalIgnoreCase) // Format must match (no conversion needed)
&& !string.IsNullOrEmpty(stream.Path) // Path must exist
info.IsExternalUrl = false; && Uri.TryCreate(stream.Path, UriKind.Absolute, out Uri? uriResult) // Path must be an absolute URI
} && (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps)) // Scheme must be HTTP or HTTPS
else
{ {
// All conditions met, override with the direct path
info.Url = stream.Path; info.Url = stream.Path;
info.IsExternalUrl = true; info.IsExternalUrl = true;
} }
// Append ApiKey only if we are using the API URL
if (!info.IsExternalUrl && !string.IsNullOrEmpty(accessToken))
{
// Use "?ApiKey=" as seen in HEAD and other parts of the code
info.Url += "?ApiKey=" + accessToken;
}
} }
return info; return info;

View File

@@ -1,5 +1,6 @@
#nullable disable #nullable disable
using System; using System;
using System.ComponentModel;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Users; using MediaBrowser.Model.Users;
@@ -54,20 +55,22 @@ namespace MediaBrowser.Model.Dto
/// Gets or sets a value indicating whether this instance has password. /// Gets or sets a value indicating whether this instance has password.
/// </summary> /// </summary>
/// <value><c>true</c> if this instance has password; otherwise, <c>false</c>.</value> /// <value><c>true</c> if this instance has password; otherwise, <c>false</c>.</value>
public bool HasPassword { get; set; } [Obsolete("This information is no longer provided")]
public bool? HasPassword { get; set; } = true;
/// <summary> /// <summary>
/// Gets or sets a value indicating whether this instance has configured password. /// Gets or sets a value indicating whether this instance has configured password.
/// </summary> /// </summary>
/// <value><c>true</c> if this instance has configured password; otherwise, <c>false</c>.</value> /// <value><c>true</c> if this instance has configured password; otherwise, <c>false</c>.</value>
public bool HasConfiguredPassword { get; set; } [Obsolete("This is always true")]
public bool? HasConfiguredPassword { get; set; } = true;
/// <summary> /// <summary>
/// Gets or sets a value indicating whether this instance has configured easy password. /// Gets or sets a value indicating whether this instance has configured easy password.
/// </summary> /// </summary>
/// <value><c>true</c> if this instance has configured easy password; otherwise, <c>false</c>.</value> /// <value><c>true</c> if this instance has configured easy password; otherwise, <c>false</c>.</value>
[Obsolete("Easy Password has been replaced with Quick Connect")] [Obsolete("Easy Password has been replaced with Quick Connect")]
public bool HasConfiguredEasyPassword { get; set; } public bool? HasConfiguredEasyPassword { get; set; } = false;
/// <summary> /// <summary>
/// Gets or sets whether async login is enabled or not. /// Gets or sets whether async login is enabled or not.

View File

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

View File

@@ -1,11 +1,15 @@
#pragma warning disable CS1591 #pragma warning disable CS1591
using System;
namespace MediaBrowser.Model.Users namespace MediaBrowser.Model.Users
{ {
public enum ForgotPasswordAction public enum ForgotPasswordAction
{ {
[Obsolete("Returning different actions represents a security concern.")]
ContactAdmin = 0, ContactAdmin = 0,
PinCode = 1, PinCode = 1,
[Obsolete("Returning different actions represents a security concern.")]
InNetworkRequired = 2 InNetworkRequired = 2
} }
} }

View File

@@ -151,7 +151,10 @@ namespace MediaBrowser.Providers.Manager
.ConfigureAwait(false); .ConfigureAwait(false);
updateType |= beforeSaveResult; updateType |= beforeSaveResult;
updateType = await SaveInternal(item, refreshOptions, updateType, isFirstRefresh, requiresRefresh, metadataResult, cancellationToken).ConfigureAwait(false); if (!isFirstRefresh)
{
updateType = await SaveInternal(item, refreshOptions, updateType, isFirstRefresh, requiresRefresh, metadataResult, cancellationToken).ConfigureAwait(false);
}
// Next run metadata providers // Next run metadata providers
if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None) if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None)
@@ -229,6 +232,11 @@ namespace MediaBrowser.Providers.Manager
if (file is not null) if (file is not null)
{ {
item.DateModified = file.LastWriteTimeUtc; item.DateModified = file.LastWriteTimeUtc;
if (!file.IsDirectory)
{
item.Size = file.Length;
}
} }
} }
@@ -312,12 +320,8 @@ namespace MediaBrowser.Providers.Manager
{ {
if (EnableUpdateMetadataFromChildren(item, isFullRefresh, updateType)) if (EnableUpdateMetadataFromChildren(item, isFullRefresh, updateType))
{ {
if (isFullRefresh || updateType > ItemUpdateType.None) var children = GetChildrenForMetadataUpdates(item);
{ updateType = UpdateMetadataFromChildren(item, children, isFullRefresh, updateType);
var children = GetChildrenForMetadataUpdates(item);
updateType = UpdateMetadataFromChildren(item, children, isFullRefresh, updateType);
}
} }
var presentationUniqueKey = item.CreatePresentationUniqueKey(); var presentationUniqueKey = item.CreatePresentationUniqueKey();
@@ -339,7 +343,10 @@ namespace MediaBrowser.Providers.Manager
item.DateModified = info.LastWriteTimeUtc; item.DateModified = info.LastWriteTimeUtc;
if (ServerConfigurationManager.GetMetadataConfiguration().UseFileCreationTimeForDateAdded) if (ServerConfigurationManager.GetMetadataConfiguration().UseFileCreationTimeForDateAdded)
{ {
item.DateCreated = info.CreationTimeUtc; if (info.CreationTimeUtc > DateTime.MinValue)
{
item.DateCreated = info.CreationTimeUtc;
}
} }
if (item is Video video) if (item is Video video)
@@ -357,16 +364,24 @@ namespace MediaBrowser.Providers.Manager
protected virtual bool EnableUpdateMetadataFromChildren(TItemType item, bool isFullRefresh, ItemUpdateType currentUpdateType) protected virtual bool EnableUpdateMetadataFromChildren(TItemType item, bool isFullRefresh, ItemUpdateType currentUpdateType)
{ {
if (isFullRefresh || currentUpdateType > ItemUpdateType.None) if (item is Folder folder)
{ {
if (EnableUpdatingPremiereDateFromChildren || EnableUpdatingGenresFromChildren || EnableUpdatingStudiosFromChildren || EnableUpdatingOfficialRatingFromChildren) if (!isFullRefresh && currentUpdateType == ItemUpdateType.None)
{ {
return true; return folder.SupportsDateLastMediaAdded;
} }
if (item is Folder folder) if (isFullRefresh || currentUpdateType > ItemUpdateType.None)
{ {
return folder.SupportsDateLastMediaAdded || folder.SupportsCumulativeRunTimeTicks; if (EnableUpdatingPremiereDateFromChildren || EnableUpdatingGenresFromChildren || EnableUpdatingStudiosFromChildren || EnableUpdatingOfficialRatingFromChildren)
{
return true;
}
if (folder.SupportsDateLastMediaAdded || folder.SupportsCumulativeRunTimeTicks)
{
return true;
}
} }
} }
@@ -387,36 +402,42 @@ namespace MediaBrowser.Providers.Manager
{ {
var updateType = ItemUpdateType.None; var updateType = ItemUpdateType.None;
if (isFullRefresh || currentUpdateType > ItemUpdateType.None) if (item is Folder folder)
{ {
updateType |= UpdateCumulativeRunTimeTicks(item, children); if (folder.SupportsDateLastMediaAdded)
updateType |= UpdateDateLastMediaAdded(item, children);
// don't update user-changeable metadata for locked items
if (item.IsLocked)
{ {
return updateType; updateType |= UpdateDateLastMediaAdded(item, children);
} }
if (EnableUpdatingPremiereDateFromChildren) if ((isFullRefresh || currentUpdateType > ItemUpdateType.None) && folder.SupportsCumulativeRunTimeTicks)
{ {
updateType |= UpdatePremiereDate(item, children); updateType |= UpdateCumulativeRunTimeTicks(item, children);
} }
}
if (EnableUpdatingGenresFromChildren) if (!(isFullRefresh || currentUpdateType > ItemUpdateType.None) || item.IsLocked)
{ {
updateType |= UpdateGenres(item, children); return updateType;
} }
if (EnableUpdatingStudiosFromChildren) if (EnableUpdatingPremiereDateFromChildren)
{ {
updateType |= UpdateStudios(item, children); updateType |= UpdatePremiereDate(item, children);
} }
if (EnableUpdatingOfficialRatingFromChildren) if (EnableUpdatingGenresFromChildren)
{ {
updateType |= UpdateOfficialRating(item, children); updateType |= UpdateGenres(item, children);
} }
if (EnableUpdatingStudiosFromChildren)
{
updateType |= UpdateStudios(item, children);
}
if (EnableUpdatingOfficialRatingFromChildren)
{
updateType |= UpdateOfficialRating(item, children);
} }
return updateType; return updateType;

View File

@@ -520,7 +520,7 @@ namespace MediaBrowser.Providers.MediaInfo
{ {
Name = person.Name, Name = person.Name,
Type = person.Type, Type = person.Type,
Role = person.Role.Trim() Role = person.Role?.Trim()
}); });
} }
} }

View File

@@ -68,12 +68,15 @@ namespace MediaBrowser.XbmcMetadata.Providers
{ {
var file = GetXmlFile(new ItemInfo(item), directoryService); var file = GetXmlFile(new ItemInfo(item), directoryService);
if (file is null) if (file?.Exists is not true)
{ {
return false; return false;
} }
return file.Exists && _fileSystem.GetLastWriteTimeUtc(file) > item.DateLastSaved; var fileTime = _fileSystem.GetLastWriteTimeUtc(file);
// 1 minute tolerance to avoid detecting our own file writes
return (fileTime - item.DateLastSaved) > TimeSpan.FromMinutes(1);
} }
protected abstract void Fetch(MetadataResult<T> result, string path, CancellationToken cancellationToken); protected abstract void Fetch(MetadataResult<T> result, string path, CancellationToken cancellationToken);

View File

@@ -1,4 +1,4 @@
using System.Reflection; using System.Reflection;
[assembly: AssemblyVersion("10.11.1")] [assembly: AssemblyVersion("10.12.0")]
[assembly: AssemblyFileVersion("10.11.1")] [assembly: AssemblyFileVersion("10.12.0")]

View File

@@ -52,10 +52,14 @@ public class OptimisticLockBehavior : IEntityFrameworkCoreLockingBehavior
_logger = logger; _logger = logger;
_writePolicy = Policy _writePolicy = Policy
.HandleInner<Exception>(e => e.Message.Contains("database is locked", StringComparison.InvariantCultureIgnoreCase)) .HandleInner<Exception>(e =>
e.Message.Contains("database is locked", StringComparison.InvariantCultureIgnoreCase) ||
e.Message.Contains("database table is locked", StringComparison.InvariantCultureIgnoreCase))
.WaitAndRetry(sleepDurations.Length, backoffProvider, RetryHandle); .WaitAndRetry(sleepDurations.Length, backoffProvider, RetryHandle);
_writeAsyncPolicy = Policy _writeAsyncPolicy = Policy
.HandleInner<Exception>(e => e.Message.Contains("database is locked", StringComparison.InvariantCultureIgnoreCase)) .HandleInner<Exception>(e =>
e.Message.Contains("database is locked", StringComparison.InvariantCultureIgnoreCase) ||
e.Message.Contains("database table is locked", StringComparison.InvariantCultureIgnoreCase))
.WaitAndRetryAsync(sleepDurations.Length, backoffProvider, RetryHandle); .WaitAndRetryAsync(sleepDurations.Length, backoffProvider, RetryHandle);
void RetryHandle(Exception exception, TimeSpan timespan, int retryNo, Context context) void RetryHandle(Exception exception, TimeSpan timespan, int retryNo, Context context)

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