Compare commits

...

42 Commits

Author SHA1 Message Date
Joshua M. Boniface a2c0799489 Merge commit from fork
Remove server side processing for profile picture and splash screen
2025-08-13 18:08:43 -04:00
evan314159 ad133eb6b9 Fix AlbumArtistIds filter to use correct ItemValueType (#14641) 2025-08-13 05:52:54 -06:00
Alex Collado 50180adc53 Change Spanish; Latin code acording to BCP 47 specification (#14639) 2025-08-13 07:33:58 +02:00
Cody Robibero bd94ca3071 Merge pull request #14634 from crobibero/itemname-counts 2025-08-12 20:20:54 -06:00
Bond-009 869b4f8bbf Merge pull request #14636 from jellyfin/renovate/ci-deps
Update github/codeql-action action to v3.29.9
2025-08-12 23:22:55 +02:00
renovate[bot] a4d856360b Update github/codeql-action action to v3.29.9 2025-08-12 15:49:37 +00:00
Cody Robibero beca405ad4 Refactor to pull item counts in a single query 2025-08-11 21:06:04 -06:00
renovate[bot] c0be325b89 Update actions/checkout action to v5 (#14631)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-11 13:06:47 -06:00
renovate[bot] dea500b26b Update dependency UTF.Unknown to 2.6.0 (#14599)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Bond_009 <bond.009@outlook.com>
2025-08-11 13:06:24 -06:00
evan314159 47634e731a Refactor query from EXISTS to JOIN to avoid API timeouts with large libraries (#14557) 2025-08-11 13:05:13 -06:00
Bond-009 cd1d11366e Merge pull request #14615 from jellyfin/renovate/z440.atl.core-7.x
Update dependency z440.atl.core to 7.3.0
2025-08-11 18:25:32 +02:00
Bond-009 76dfaead8b Merge pull request #14612 from jellyfin/renovate/ci-deps
Update CI dependencies
2025-08-11 18:06:01 +02:00
Evan 5eef85f027 move new TaggedItemCounts to its own file 2025-08-11 20:49:06 +08:00
renovate[bot] e6a7530ced Update CI dependencies 2025-08-11 11:22:48 +00:00
renovate[bot] 00be664b9e Update dependency z440.atl.core to 7.3.0 2025-08-10 15:15:27 +00:00
Thunderstrike116 e1d0f7d1e5 Translated using Weblate (Greek)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/el/
2025-08-10 13:57:29 +00:00
Evan 0a4ff3f3c0 Fix GetBaseItemDto to return related item counts via SQL count
For API call /Items/{item id} GetBaseItemDto will return the counts of related items e.g. artists, albums, songs.  GetBaseItemDto currently does this by calling GetTaggedItems which retrieves the objects into memory to count them.  Replace with SQL count.

Fixes:
This should be an improvement for any large libraries, but especially large music libraries.  Example:

Request Library -> Genres -> any very popular genre in your large library, e.g. Classical
Number of albums = 1552, songs = 23515, ...

- Before change: Try to retrieve 1552 albums, 23515 songs, ... in memory, API never returns, database on fire
- After change: API returns in 367ms and Genre view opens with 200 albums in 2 seconds

I verified the numbers returned are correct but note that there is a bug somewhere else in Jellyfin that is setting TopParentId to NULL for a large portion of my MusicArtists, which causes them to not be counted by the existing GetCount().  This is not related to this change, also happens with the existing code, and does not seem to affect the Web UI.

Includes Cory's changes in:
- https://github.com/jellyfin/jellyfin/pull/14610#issuecomment-3172211468
- https://github.com/jellyfin/jellyfin/pull/14610#issuecomment-3172239154
2025-08-10 18:02:17 +08:00
NarohC 21f214b1a6 Translated using Weblate (Spanish (Mexico))
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es_MX/
2025-08-10 07:38:46 +00:00
Tim Eisele 0650666497 Always save images (#14600) 2025-08-09 08:24:26 -06:00
Bond-009 877899dcc2 Merge pull request #14583 from jellyfin/renovate/dotnet-monorepo
Update dependency dotnet-ef to 9.0.8
2025-08-08 19:41:37 +02:00
Bond-009 bf2f8ec633 Merge pull request #14584 from jellyfin/renovate/microsoft
Update Microsoft to 9.0.8
2025-08-08 19:41:16 +02:00
Bond-009 2eff03b03e Merge pull request #14606 from jellyfin/renovate/ci-deps
Update github/codeql-action action to v3.29.8
2025-08-08 19:26:18 +02:00
renovate[bot] 103932e4fb Update github/codeql-action action to v3.29.8 2025-08-08 14:31:59 +00:00
Niels van Velzen 2b94b3b5f6 Merge pull request #14597 from jellyfin/renovate/svg.skia-3.x
Update dependency Svg.Skia to 3.0.5
2025-08-07 22:05:37 +02:00
Niels van Velzen 64032e8656 Merge pull request #14604 from jellyfin/renovate/ci-deps
Update github/codeql-action action to v3.29.6
2025-08-07 22:04:24 +02:00
renovate[bot] 329ce8d4c2 Update Microsoft to 9.0.8 2025-08-07 19:51:21 +00:00
renovate[bot] 2a7c924904 Update github/codeql-action action to v3.29.6 2025-08-07 19:51:15 +00:00
renovate[bot] 72664a68bc Update dependency Svg.Skia to 3.0.5 2025-08-07 19:51:12 +00:00
renovate[bot] 3ec123b616 Update dependency dotnet-ef to 9.0.8 2025-08-07 19:51:05 +00:00
Niels van Velzen 376220661b Merge pull request #14593 from Bond-009/revert
Revert "Merge pull request #13604 from Jxiced/master"
2025-08-07 21:49:59 +02:00
Lotine Guille 9e88121647 Translated using Weblate (English (Pirate))
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/en@pirate/
2025-08-07 01:15:12 +00:00
Bond_009 c7c7b30d28 Revert "Merge pull request #13604 from Jxiced/master"
This reverts commit ce78af2ed4, reversing
changes made to db7465e83d.
2025-08-06 15:49:40 +02:00
Bond-009 601ce4c3b1 Merge pull request #14590 from jellyfin/renovate/major-github-artifact-actions
Update actions/download-artifact action to v5
2025-08-06 15:45:12 +02:00
Nick fcc7f53e81 Translated using Weblate (Uzbek)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/uz/
2025-08-06 05:11:07 +00:00
newton181 e3acf08acc Translated using Weblate (Spanish (Latin America))
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es_419/
2025-08-06 05:11:07 +00:00
renovate[bot] c60139a32c Update actions/download-artifact action to v5 2025-08-05 22:10:49 +00:00
Niels van Velzen 6d4efe6523 Translated using Weblate (Norwegian Nynorsk)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nn/
2025-08-05 05:28:20 +00:00
Erick Marx 43a955dded Translated using Weblate (Portuguese)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt/
2025-08-04 13:27:28 +00:00
JPVenson 7320e10329 readd Format for splashscreen and user image 2025-07-27 23:12:40 +00:00
JPVenson 5b544bf1ed Remove even obsoleter code 2025-07-27 21:17:20 +00:00
JPVenson 1a1d9b2404 Remove obsolete code 2025-07-27 21:16:45 +00:00
JPVenson 96a05276a6 Removed all server side processing options from public endpoints for image gen 2025-07-27 21:15:18 +00:00
29 changed files with 352 additions and 241 deletions
+1 -1
View File
@@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "9.0.7",
"version": "9.0.8",
"commands": [
"dotnet-ef"
]
+4 -4
View File
@@ -20,18 +20,18 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup .NET
uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1
with:
dotnet-version: '9.0.x'
- name: Initialize CodeQL
uses: github/codeql-action/init@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5
uses: github/codeql-action/init@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.9
with:
languages: ${{ matrix.language }}
queries: +security-extended
- name: Autobuild
uses: github/codeql-action/autobuild@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5
uses: github/codeql-action/autobuild@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.9
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5
uses: github/codeql-action/analyze@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.9
+4 -4
View File
@@ -11,7 +11,7 @@ jobs:
permissions: read-all
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ github.event.pull_request.head.sha }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
@@ -40,7 +40,7 @@ jobs:
permissions: read-all
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ github.event.pull_request.head.sha }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
@@ -85,13 +85,13 @@ jobs:
steps:
- name: Download abi-head
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
name: abi-head
path: abi-head
- name: Download abi-base
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
name: abi-base
path: abi-base
+6 -6
View File
@@ -16,7 +16,7 @@ jobs:
permissions: read-all
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ github.event.pull_request.head.sha }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
@@ -41,7 +41,7 @@ jobs:
permissions: read-all
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ github.event.pull_request.head.sha }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
@@ -80,12 +80,12 @@ jobs:
- openapi-base
steps:
- name: Download openapi-head
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
name: openapi-head
path: openapi-head
- name: Download openapi-base
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
name: openapi-base
path: openapi-base
@@ -158,7 +158,7 @@ jobs:
run: |-
echo "JELLYFIN_VERSION=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_ENV
- name: Download openapi-head
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
name: openapi-head
path: openapi-head
@@ -220,7 +220,7 @@ jobs:
run: |-
echo "JELLYFIN_VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
- name: Download openapi-head
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
name: openapi-head
path: openapi-head
+2 -2
View File
@@ -20,7 +20,7 @@ jobs:
runs-on: "${{ matrix.os }}"
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1
with:
@@ -35,7 +35,7 @@ jobs:
--verbosity minimal
- name: Merge code coverage results
uses: danielpalme/ReportGenerator-GitHub-Action@c1dd332d00304c5aa5d506aab698a5224a8fa24e # 5.4.11
uses: danielpalme/ReportGenerator-GitHub-Action@c4c5175a441c6603ec614f5084386dabe0e2295b # v5.4.12
with:
reports: "**/coverage.cobertura.xml"
targetdir: "merged/"
+2 -2
View File
@@ -24,7 +24,7 @@ jobs:
reactions: '+1'
- name: Checkout the latest code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
token: ${{ secrets.JF_BOT_TOKEN }}
fetch-depth: 0
@@ -40,7 +40,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: pull in script
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
repository: jellyfin/jellyfin-triage-script
- name: install python
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
issues: write
steps:
- name: pull in script
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
repository: jellyfin/jellyfin-triage-script
- name: install python
+2 -2
View File
@@ -33,7 +33,7 @@ jobs:
yq-version: v4.9.8
- name: Checkout Repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ env.TAG_BRANCH }}
@@ -66,7 +66,7 @@ jobs:
NEXT_VERSION: ${{ github.event.inputs.NEXT_VERSION }}
steps:
- name: Checkout Repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ env.TAG_BRANCH }}
-1
View File
@@ -197,7 +197,6 @@
- [Kenneth Cochran](https://github.com/kennethcochran)
- [benedikt257](https://github.com/benedikt257)
- [revam](https://github.com/revam)
- [Jxiced](https://github.com/Jxiced)
- [allesmi](https://github.com/allesmi)
- [ThunderClapLP](https://github.com/ThunderClapLP)
- [Shoham Peller](https://github.com/spellr)
+28 -28
View File
@@ -26,29 +26,29 @@
<PackageVersion Include="libse" Version="4.0.12" />
<PackageVersion Include="LrcParser" Version="2025.623.0" />
<PackageVersion Include="MetaBrainz.MusicBrainz" Version="6.1.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="9.0.7" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.7" />
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="9.0.8" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.8" />
<PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="4.14.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.7" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.7" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.7" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.7" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.7" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.8" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.8" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.8" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.8" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.8" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageVersion Include="MimeTypes" Version="2.5.2" />
<PackageVersion Include="Morestachio" Version="5.0.1.631" />
@@ -76,18 +76,18 @@
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="3.116.1" />
<PackageVersion Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
<PackageVersion Include="Svg.Skia" Version="3.0.4" />
<PackageVersion Include="Svg.Skia" Version="3.0.5" />
<PackageVersion Include="Swashbuckle.AspNetCore.ReDoc" Version="6.5.0" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.2.3" />
<PackageVersion Include="System.Globalization" Version="4.3.0" />
<PackageVersion Include="System.Linq.Async" Version="6.0.3" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.7" />
<PackageVersion Include="System.Text.Json" Version="9.0.7" />
<PackageVersion Include="System.Threading.Tasks.Dataflow" Version="9.0.7" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.8" />
<PackageVersion Include="System.Text.Json" Version="9.0.8" />
<PackageVersion Include="System.Threading.Tasks.Dataflow" Version="9.0.8" />
<PackageVersion Include="TagLibSharp" Version="2.3.0" />
<PackageVersion Include="z440.atl.core" Version="7.2.0" />
<PackageVersion Include="z440.atl.core" Version="7.3.0" />
<PackageVersion Include="TMDbLib" Version="2.2.0" />
<PackageVersion Include="UTF.Unknown" Version="2.5.1" />
<PackageVersion Include="UTF.Unknown" Version="2.6.0" />
<PackageVersion Include="Xunit.Priority" Version="1.1.6" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
<PackageVersion Include="Xunit.SkippableFact" Version="1.5.23" />
+132 -37
View File
@@ -1,6 +1,7 @@
#pragma warning disable CS1591
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
@@ -37,6 +38,77 @@ namespace Emby.Server.Implementations.Dto
{
public class DtoService : IDtoService
{
private static readonly FrozenDictionary<BaseItemKind, BaseItemKind[]> _relatedItemKinds = new Dictionary<BaseItemKind, BaseItemKind[]>
{
{
BaseItemKind.Genre, [
BaseItemKind.Audio,
BaseItemKind.Episode,
BaseItemKind.Movie,
BaseItemKind.LiveTvProgram,
BaseItemKind.MusicAlbum,
BaseItemKind.MusicArtist,
BaseItemKind.MusicVideo,
BaseItemKind.Series,
BaseItemKind.Trailer
]
},
{
BaseItemKind.MusicArtist, [
BaseItemKind.Audio,
BaseItemKind.MusicAlbum,
BaseItemKind.MusicVideo
]
},
{
BaseItemKind.MusicGenre, [
BaseItemKind.Audio,
BaseItemKind.MusicAlbum,
BaseItemKind.MusicArtist,
BaseItemKind.MusicVideo
]
},
{
BaseItemKind.Person, [
BaseItemKind.Audio,
BaseItemKind.Episode,
BaseItemKind.Movie,
BaseItemKind.LiveTvProgram,
BaseItemKind.MusicAlbum,
BaseItemKind.MusicArtist,
BaseItemKind.MusicVideo,
BaseItemKind.Series,
BaseItemKind.Trailer
]
},
{
BaseItemKind.Studio, [
BaseItemKind.Audio,
BaseItemKind.Episode,
BaseItemKind.Movie,
BaseItemKind.LiveTvProgram,
BaseItemKind.MusicAlbum,
BaseItemKind.MusicArtist,
BaseItemKind.MusicVideo,
BaseItemKind.Series,
BaseItemKind.Trailer
]
},
{
BaseItemKind.Year, [
BaseItemKind.Audio,
BaseItemKind.Episode,
BaseItemKind.Movie,
BaseItemKind.LiveTvProgram,
BaseItemKind.MusicAlbum,
BaseItemKind.MusicArtist,
BaseItemKind.MusicVideo,
BaseItemKind.Series,
BaseItemKind.Trailer
]
}
}.ToFrozenDictionary();
private readonly ILogger<DtoService> _logger;
private readonly ILibraryManager _libraryManager;
private readonly IUserDataManager _userDataRepository;
@@ -102,21 +174,9 @@ namespace Emby.Server.Implementations.Dto
(programTuples ??= []).Add((item, dto));
}
if (item is IItemByName byName)
if (options.ContainsField(ItemFields.ItemCounts))
{
if (options.ContainsField(ItemFields.ItemCounts))
{
var libraryItems = byName.GetTaggedItems(new InternalItemsQuery(user)
{
Recursive = true,
DtoOptions = new DtoOptions(false)
{
EnableImages = false
}
});
SetItemByNameInfo(item, dto, libraryItems);
}
SetItemByNameInfo(dto, user);
}
returnItems[index] = dto;
@@ -147,34 +207,14 @@ namespace Emby.Server.Implementations.Dto
LivetvManager.AddInfoToProgramDto(new[] { (item, dto) }, options.Fields, user).GetAwaiter().GetResult();
}
if (item is IItemByName itemByName
&& options.ContainsField(ItemFields.ItemCounts))
if (options.ContainsField(ItemFields.ItemCounts))
{
SetItemByNameInfo(
item,
dto,
GetTaggedItems(
itemByName,
user,
new DtoOptions(false)
{
EnableImages = false
}));
SetItemByNameInfo(dto, user);
}
return dto;
}
private static IReadOnlyList<BaseItem> GetTaggedItems(IItemByName byName, User? user, DtoOptions options)
{
return byName.GetTaggedItems(
new InternalItemsQuery(user)
{
Recursive = true,
DtoOptions = options
});
}
private BaseItemDto GetBaseItemDtoInternal(BaseItem item, DtoOptions options, User? user = null, BaseItem? owner = null)
{
var dto = new BaseItemDto
@@ -315,11 +355,15 @@ namespace Emby.Server.Implementations.Dto
}
/// <inheritdoc />
/// TODO refactor this to use the new SetItemByNameInfo.
/// Some callers already have the counts extracted so no reason to retrieve them again.
public BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List<BaseItem>? taggedItems, User? user = null)
{
var dto = GetBaseItemDtoInternal(item, options, user);
if (taggedItems is not null && options.ContainsField(ItemFields.ItemCounts))
if (options.ContainsField(ItemFields.ItemCounts)
&& taggedItems is not null
&& taggedItems.Count != 0)
{
SetItemByNameInfo(item, dto, taggedItems);
}
@@ -327,6 +371,57 @@ namespace Emby.Server.Implementations.Dto
return dto;
}
private void SetItemByNameInfo(BaseItemDto dto, User? user)
{
if (!_relatedItemKinds.TryGetValue(dto.Type, out var relatedItemKinds))
{
return;
}
var query = new InternalItemsQuery(user)
{
Recursive = true,
DtoOptions = new DtoOptions(false) { EnableImages = false },
IncludeItemTypes = relatedItemKinds
};
switch (dto.Type)
{
case BaseItemKind.Genre:
case BaseItemKind.MusicGenre:
query.GenreIds = [dto.Id];
break;
case BaseItemKind.MusicArtist:
query.ArtistIds = [dto.Id];
break;
case BaseItemKind.Person:
query.PersonIds = [dto.Id];
break;
case BaseItemKind.Studio:
query.StudioIds = [dto.Id];
break;
case BaseItemKind.Year
when int.TryParse(dto.Name, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year):
query.Years = [year];
break;
default:
return;
}
var counts = _libraryManager.GetItemCounts(query);
dto.AlbumCount = counts.AlbumCount;
dto.ArtistCount = counts.ArtistCount;
dto.EpisodeCount = counts.EpisodeCount;
dto.MovieCount = counts.MovieCount;
dto.MusicVideoCount = counts.MusicVideoCount;
dto.ProgramCount = counts.ProgramCount;
dto.SeriesCount = counts.SeriesCount;
dto.SongCount = counts.SongCount;
dto.TrailerCount = counts.TrailerCount;
dto.ChildCount = counts.TotalItemCount();
}
private static void SetItemByNameInfo(BaseItem item, BaseItemDto dto, IReadOnlyList<BaseItem> taggedItems)
{
if (item is MusicArtist)
@@ -1389,6 +1389,25 @@ namespace Emby.Server.Implementations.Library
return _itemRepository.GetCount(query);
}
public ItemCounts GetItemCounts(InternalItemsQuery query)
{
if (query.Recursive && !query.ParentId.IsEmpty())
{
var parent = GetItemById(query.ParentId);
if (parent is not null)
{
SetTopParentIdsOrAncestors(query, [parent]);
}
}
if (query.User is not null)
{
AddUserToQuery(query, query.User);
}
return _itemRepository.GetItemCounts(query);
}
public IReadOnlyList<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents)
{
SetTopParentIdsOrAncestors(query, parents);
@@ -1981,8 +2000,6 @@ namespace Emby.Server.Implementations.Library
return;
}
var anyChange = false;
foreach (var img in outdated)
{
var image = img;
@@ -2014,7 +2031,6 @@ namespace Emby.Server.Implementations.Library
try
{
size = _imageProcessor.GetImageDimensions(item, image);
anyChange = image.Width != size.Width || image.Height != size.Height;
image.Width = size.Width;
image.Height = size.Height;
}
@@ -2022,7 +2038,6 @@ namespace Emby.Server.Implementations.Library
{
_logger.LogError(ex, "Cannot get image dimensions for {ImagePath}", image.Path);
size = default;
anyChange = image.Width != size.Width || image.Height != size.Height;
image.Width = 0;
image.Height = 0;
}
@@ -2030,20 +2045,17 @@ namespace Emby.Server.Implementations.Library
try
{
var blurhash = _imageProcessor.GetImageBlurHash(image.Path, size);
anyChange = anyChange || !blurhash.Equals(image.BlurHash, StringComparison.Ordinal);
image.BlurHash = blurhash;
}
catch (Exception ex)
{
_logger.LogError(ex, "Cannot compute blurhash for {ImagePath}", image.Path);
anyChange = anyChange || !string.IsNullOrEmpty(image.BlurHash);
image.BlurHash = string.Empty;
}
try
{
var modifiedDate = _fileSystem.GetLastWriteTimeUtc(image.Path);
anyChange = anyChange || modifiedDate != image.DateModified;
image.DateModified = modifiedDate;
}
catch (Exception ex)
@@ -2052,10 +2064,7 @@ namespace Emby.Server.Implementations.Library
}
}
if (anyChange)
{
_itemRepository.SaveImages(item);
}
_itemRepository.SaveImages(item);
RegisterItem(item);
}
@@ -136,5 +136,7 @@
"TaskMoveTrickplayImagesDescription": "Μετακινεί τα υπάρχοντα αρχεία trickplay σύμφωνα με τις ρυθμίσεις της βιβλιοθήκης.",
"TaskDownloadMissingLyricsDescription": "Κατεβάζει στίχους για τραγούδια",
"TaskExtractMediaSegments": "Σάρωση τμημάτων πολυμέσων",
"TaskExtractMediaSegmentsDescription": "Εξάγει ή βρίσκει τμήματα πολυμέσων από επεκτάσεις που χρησιμοποιούν το MediaSegment."
"TaskExtractMediaSegmentsDescription": "Εξάγει ή βρίσκει τμήματα πολυμέσων από επεκτάσεις που χρησιμοποιούν το MediaSegment.",
"CleanupUserDataTaskDescription": "Καθαρίζει όλα τα δεδομένα χρήστη (κατάσταση παρακολούθησης, κατάσταση αγαπημένων κ.λπ.) από πολυμέσα που δεν υπάρχουν πλέον για τουλάχιστον 90 ημέρες.",
"CleanupUserDataTask": "Εργασία εκκαθάρισης δεδομένων χρήστη"
}
@@ -136,5 +136,6 @@
"TaskExtractMediaSegments": "Escaneo de segmentos de medios",
"TaskExtractMediaSegmentsDescription": "Extrae u obtiene segmentos de medios de plugins habilitados para MediaSegment.",
"TaskMoveTrickplayImages": "Migrar la ubicación de la imagen de Trickplay",
"TaskMoveTrickplayImagesDescription": "Mueve archivos de trickplay existentes según la configuración de la biblioteca."
"TaskMoveTrickplayImagesDescription": "Mueve archivos de trickplay existentes según la configuración de la biblioteca.",
"CleanupUserDataTask": "Tarea de limpieza de los datos del usuario"
}
@@ -135,5 +135,7 @@
"TaskExtractMediaSegmentsDescription": "Extrae u obtiene segmentos de medios de complementos habilitados para MediaSegment.",
"TaskMoveTrickplayImagesDescription": "Mueve archivos de trickplay existentes según la configuración de la biblioteca.",
"TaskExtractMediaSegments": "Escaneo de segmentos de medios",
"TaskMoveTrickplayImages": "Migrar la ubicación de la imagen de Trickplay"
"TaskMoveTrickplayImages": "Migrar la ubicación de la imagen de Trickplay",
"CleanupUserDataTask": "Tarea de limpieza de datos de usuario",
"CleanupUserDataTaskDescription": "Limpia todos los datos de usuario (estado de visualización, favoritos, etc.) que no están presentes en la biblioteca por al menos 90 días."
}
@@ -23,7 +23,7 @@
"Genres": "Sjangrar",
"Folders": "Mapper",
"Favorites": "Favorittar",
"FailedLoginAttemptWithUserName": "https://betpro-dealers.com/",
"FailedLoginAttemptWithUserName": "Mislukka påloggingsforsøk frå {0}",
"DeviceOnlineWithName": "{0} er tilkopla",
"DeviceOfflineWithName": "{0} har kopla frå",
"Collections": "Samlingar",
@@ -116,7 +116,7 @@
"TaskCleanActivityLogDescription": "Sletter aktivitetslogginnlegg som er eldre enn den konfigurerte alderen.",
"TaskCleanActivityLog": "Slett aktivitetslogg",
"Undefined": "Udefinert",
"Forced": "https://betpro-dealers.com/",
"Forced": "Tvungen",
"Default": "Standard",
"External": "Ekstern",
"HearingImpaired": "Nedsett høyrsel",
@@ -32,5 +32,14 @@
"HeaderFavoriteShows": "Treasured Tales",
"ChapterNameValue": "Piece {0}",
"HeaderFavoriteSongs": "Treasured Chimes",
"HeaderNextUp": "Incoming"
"HeaderNextUp": "Incoming",
"HeaderLiveTV": "Scrying Glass",
"HearingImpaired": "Hard o' Hearing",
"LabelRunningTimeValue": "Journey duration: {0}",
"MessageApplicationUpdated": "Yer Map of the Seas has been scribbled",
"HomeVideos": "Yer Onboard Booty",
"MixedContent": "Jumbled loot",
"Music": "Tunes",
"NameInstallFailed": "Ye couldn't bring {0} aboard yer ship",
"MessageApplicationUpdatedTo": "Yer Map of the Seas has been scribbled with {0}"
}
@@ -135,5 +135,7 @@
"TaskMoveTrickplayImagesDescription": "Move os ficheiros trickplay existentes de acordo com as definições da mediateca.",
"TaskExtractMediaSegments": "Analisar segmentos de multimédia",
"TaskExtractMediaSegmentsDescription": "Extrai ou obtém segmentos de multimédia a partir de plugins com suporte para MediaSegment.",
"TaskMoveTrickplayImages": "Migrar a localização da imagem do Trickplay"
"TaskMoveTrickplayImages": "Migrar a localização da imagem do Trickplay",
"CleanupUserDataTask": "Task de limpeza de dados do usuário",
"CleanupUserDataTaskDescription": "Remove todos os dados do usuário (progresso, favoritos etc) de mídias que não estão presentes há pelo menos 90 dias."
}
@@ -110,5 +110,6 @@
"TaskCleanCache": "Kesh katalogini tozalash",
"TaskRefreshChapterImages": "Sahnadan tasvirini chiqarish",
"TaskRefreshChapterImagesDescription": "Sahnalarni o'z ichiga olgan videolar uchun eskizlarni yaratadi.",
"TaskRefreshLibrary": "Media kutubxonangizni skanerlash"
"TaskRefreshLibrary": "Media kutubxonangizni skanerlash",
"TaskCleanLogsDescription": "{0} kundan eski log fayllarni o'chiradi."
}
@@ -402,7 +402,7 @@ sog|||Sogdian|sogdien
som||so|Somali|somali
son|||Songhai languages|songhai, langues
sot||st|Sotho, Southern|sotho du Sud
spa||es-mx|Spanish; Latin|espagnol; Latin
spa||es-419|Spanish; Latin|espagnol; Latin
spa||es|Spanish; Castilian|espagnol; castillan
sqi|alb|sq|Albanian|albanais
srd||sc|Sardinian|sarde
+28 -109
View File
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
@@ -1458,19 +1459,6 @@ public class ImageController : BaseJellyfinApiController
/// <param name="userId">User id.</param>
/// <param name="tag">Optional. Supply the cache tag from the item object to receive strong caching headers.</param>
/// <param name="format">Determines the output format of the image - original,gif,jpg,png.</param>
/// <param name="maxWidth">The maximum image width to return.</param>
/// <param name="maxHeight">The maximum image height to return.</param>
/// <param name="percentPlayed">Optional. Percent to render for the percent played overlay.</param>
/// <param name="unplayedCount">Optional. Unplayed count overlay to render.</param>
/// <param name="width">The fixed image width to return.</param>
/// <param name="height">The fixed image height to return.</param>
/// <param name="quality">Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases.</param>
/// <param name="fillWidth">Width of box to fill.</param>
/// <param name="fillHeight">Height of box to fill.</param>
/// <param name="blur">Optional. Blur image.</param>
/// <param name="backgroundColor">Optional. Apply a background color for transparent images.</param>
/// <param name="foregroundLayer">Optional. Apply a foreground layer on top of the image.</param>
/// <param name="imageIndex">Image index.</param>
/// <response code="200">Image stream returned.</response>
/// <response code="400">User id not provided.</response>
/// <response code="404">Item not found.</response>
@@ -1487,20 +1475,7 @@ public class ImageController : BaseJellyfinApiController
public async Task<ActionResult> GetUserImage(
[FromQuery] Guid? userId,
[FromQuery] string? tag,
[FromQuery] ImageFormat? format,
[FromQuery] int? maxWidth,
[FromQuery] int? maxHeight,
[FromQuery] double? percentPlayed,
[FromQuery] int? unplayedCount,
[FromQuery] int? width,
[FromQuery] int? height,
[FromQuery] int? quality,
[FromQuery] int? fillWidth,
[FromQuery] int? fillHeight,
[FromQuery] int? blur,
[FromQuery] string? backgroundColor,
[FromQuery] string? foregroundLayer,
[FromQuery] int? imageIndex)
[FromQuery] ImageFormat? format)
{
var requestUserId = userId ?? User.GetUserId();
if (requestUserId.IsEmpty())
@@ -1521,34 +1496,24 @@ public class ImageController : BaseJellyfinApiController
DateModified = user.ProfileImage.LastModified
};
if (width.HasValue)
{
info.Width = width.Value;
}
if (height.HasValue)
{
info.Height = height.Value;
}
return await GetImageInternal(
user.Id,
ImageType.Profile,
imageIndex,
null,
tag,
format,
maxWidth,
maxHeight,
percentPlayed,
unplayedCount,
width,
height,
quality,
fillWidth,
fillHeight,
blur,
backgroundColor,
foregroundLayer,
null,
null,
null,
null,
null,
null,
90,
null,
null,
null,
null,
null,
null,
info)
.ConfigureAwait(false);
@@ -1608,20 +1573,7 @@ public class ImageController : BaseJellyfinApiController
=> GetUserImage(
userId,
tag,
format,
maxWidth,
maxHeight,
percentPlayed,
unplayedCount,
width,
height,
quality,
fillWidth,
fillHeight,
blur,
backgroundColor,
foregroundLayer,
imageIndex);
format);
/// <summary>
/// Get user profile image.
@@ -1677,36 +1629,13 @@ public class ImageController : BaseJellyfinApiController
=> GetUserImage(
userId,
tag,
format,
maxWidth,
maxHeight,
percentPlayed,
unplayedCount,
width,
height,
quality,
fillWidth,
fillHeight,
blur,
backgroundColor,
foregroundLayer,
imageIndex);
format);
/// <summary>
/// Generates or gets the splashscreen.
/// </summary>
/// <param name="tag">Supply the cache tag from the item object to receive strong caching headers.</param>
/// <param name="format">Determines the output format of the image - original,gif,jpg,png.</param>
/// <param name="maxWidth">The maximum image width to return.</param>
/// <param name="maxHeight">The maximum image height to return.</param>
/// <param name="width">The fixed image width to return.</param>
/// <param name="height">The fixed image height to return.</param>
/// <param name="fillWidth">Width of box to fill.</param>
/// <param name="fillHeight">Height of box to fill.</param>
/// <param name="blur">Blur image.</param>
/// <param name="backgroundColor">Apply a background color for transparent images.</param>
/// <param name="foregroundLayer">Apply a foreground layer on top of the image.</param>
/// <param name="quality">Quality setting, from 0-100.</param>
/// <response code="200">Splashscreen returned successfully.</response>
/// <returns>The splashscreen.</returns>
[HttpGet("Branding/Splashscreen")]
@@ -1714,17 +1643,7 @@ public class ImageController : BaseJellyfinApiController
[ProducesImageFile]
public async Task<ActionResult> GetSplashscreen(
[FromQuery] string? tag,
[FromQuery] ImageFormat? format,
[FromQuery] int? maxWidth,
[FromQuery] int? maxHeight,
[FromQuery] int? width,
[FromQuery] int? height,
[FromQuery] int? fillWidth,
[FromQuery] int? fillHeight,
[FromQuery] int? blur,
[FromQuery] string? backgroundColor,
[FromQuery] string? foregroundLayer,
[FromQuery, Range(0, 100)] int quality = 90)
[FromQuery] ImageFormat? format)
{
var brandingOptions = _serverConfigurationManager.GetConfiguration<BrandingOptions>("branding");
var isAdmin = User.IsInRole(Constants.UserRoles.Administrator);
@@ -1763,16 +1682,16 @@ public class ImageController : BaseJellyfinApiController
{
Path = splashscreenPath
},
Height = height,
MaxHeight = maxHeight,
MaxWidth = maxWidth,
FillHeight = fillHeight,
FillWidth = fillWidth,
Quality = quality,
Width = width,
Blur = blur,
BackgroundColor = backgroundColor,
ForegroundLayer = foregroundLayer,
Height = null,
MaxHeight = null,
MaxWidth = null,
FillHeight = null,
FillWidth = null,
Quality = 90,
Width = null,
Blur = null,
BackgroundColor = null,
ForegroundLayer = null,
SupportedOutputFormats = outputFormats
};
@@ -1,4 +1,3 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
@@ -132,16 +131,16 @@ public class StartupController : BaseJellyfinApiController
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<ActionResult> UpdateStartupUser([FromBody] StartupUserDto startupUserDto)
{
ArgumentNullException.ThrowIfNull(startupUserDto.Name);
_userManager.ThrowIfInvalidUsername(startupUserDto.Name);
var user = _userManager.Users.First();
if (string.IsNullOrWhiteSpace(startupUserDto.Password))
{
return BadRequest("Password must not be empty");
}
user.Username = startupUserDto.Name;
if (startupUserDto.Name is not null)
{
user.Username = startupUserDto.Name;
}
await _userManager.UpdateUserAsync(user).ConfigureAwait(false);
@@ -457,6 +457,66 @@ public sealed class BaseItemRepository
return dbQuery.Count();
}
/// <inheritdoc />
public ItemCounts GetItemCounts(InternalItemsQuery filter)
{
ArgumentNullException.ThrowIfNull(filter);
// Hack for right now since we currently don't support filtering out these duplicates within a query
PrepareFilterQuery(filter);
using var context = _dbProvider.CreateDbContext();
var dbQuery = TranslateQuery(context.BaseItems.AsNoTracking(), context, filter);
var counts = dbQuery
.GroupBy(x => x.Type)
.Select(x => new { x.Key, Count = x.Count() })
.AsEnumerable();
var lookup = _itemTypeLookup.BaseItemKindNames;
var result = new ItemCounts();
foreach (var count in counts)
{
if (string.Equals(count.Key, lookup[BaseItemKind.MusicAlbum], StringComparison.Ordinal))
{
result.AlbumCount = count.Count;
}
else if (string.Equals(count.Key, lookup[BaseItemKind.MusicArtist], StringComparison.Ordinal))
{
result.ArtistCount = count.Count;
}
else if (string.Equals(count.Key, lookup[BaseItemKind.Episode], StringComparison.Ordinal))
{
result.EpisodeCount = count.Count;
}
else if (string.Equals(count.Key, lookup[BaseItemKind.Movie], StringComparison.Ordinal))
{
result.MovieCount = count.Count;
}
else if (string.Equals(count.Key, lookup[BaseItemKind.MusicVideo], StringComparison.Ordinal))
{
result.MusicVideoCount = count.Count;
}
else if (string.Equals(count.Key, lookup[BaseItemKind.LiveTvProgram], StringComparison.Ordinal))
{
result.ProgramCount = count.Count;
}
else if (string.Equals(count.Key, lookup[BaseItemKind.Series], StringComparison.Ordinal))
{
result.SeriesCount = count.Count;
}
else if (string.Equals(count.Key, lookup[BaseItemKind.Audio], StringComparison.Ordinal))
{
result.SongCount = count.Count;
}
else if (string.Equals(count.Key, lookup[BaseItemKind.Trailer], StringComparison.Ordinal))
{
result.TrailerCount = count.Count;
}
}
return result;
}
#pragma warning disable CA1307 // Specify StringComparison for clarity
/// <summary>
/// Gets the type.
@@ -1115,13 +1175,18 @@ public sealed class BaseItemRepository
IsSeries = filter.IsSeries
});
var itemValuesQuery = context.ItemValues
.Where(f => itemValueTypes.Contains(f.Type))
.SelectMany(f => f.BaseItemsMap!, (f, w) => new { f, w })
.Join(
innerQueryFilter,
fw => fw.w.ItemId,
g => g.Id,
(fw, g) => fw.f.CleanValue);
var innerQuery = PrepareItemQuery(context, filter)
.Where(e => e.Type == returnType)
.Where(e => context.ItemValues!
.Where(f => itemValueTypes.Contains(f.Type))
.Where(f => innerQueryFilter.Any(g => f.BaseItemsMap!.Any(w => w.ItemId == g.Id)))
.Select(f => f.CleanValue)
.Contains(e.CleanName));
.Where(e => itemValuesQuery.Contains(e.CleanName));
var outerQueryFilter = new InternalItemsQuery(filter.User)
{
@@ -1902,7 +1967,7 @@ public sealed class BaseItemRepository
if (filter.AlbumArtistIds.Length > 0)
{
baseQuery = baseQuery.WhereReferencedItem(context, ItemValueType.Artist, filter.AlbumArtistIds);
baseQuery = baseQuery.WhereReferencedItem(context, ItemValueType.AlbumArtist, filter.AlbumArtistIds);
}
if (filter.ContributingArtistIds.Length > 0)
@@ -744,8 +744,7 @@ namespace Jellyfin.Server.Implementations.Users
_users[user.Id] = user;
}
/// <inheritdoc/>
public void ThrowIfInvalidUsername(string name)
internal static void ThrowIfInvalidUsername(string name)
{
if (!string.IsNullOrWhiteSpace(name) && ValidUsernameRegex().IsMatch(name))
{
@@ -630,6 +630,8 @@ namespace MediaBrowser.Controller.Library
int GetCount(InternalItemsQuery query);
ItemCounts GetItemCounts(InternalItemsQuery query);
Task RunMetadataSavers(BaseItem item, ItemUpdateType updateReason);
BaseItem GetParentItem(Guid? parentId, Guid? userId);
@@ -33,12 +33,6 @@ namespace MediaBrowser.Controller.Library
/// <value>The users ids.</value>
IEnumerable<Guid> UsersIds { get; }
/// <summary>
/// Checks if the user's username is valid.
/// </summary>
/// <param name="name">The user's username.</param>
void ThrowIfInvalidUsername(string name);
/// <summary>
/// Initializes the user manager and ensures that a user exists.
/// </summary>
@@ -84,6 +84,8 @@ public interface IItemRepository
int GetCount(InternalItemsQuery filter);
ItemCounts GetItemCounts(InternalItemsQuery filter);
QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetGenres(InternalItemsQuery filter);
QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetMusicGenres(InternalItemsQuery filter);
@@ -171,14 +171,15 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
using (var stream = await GetStream(fileInfo.Path, fileInfo.Protocol, cancellationToken).ConfigureAwait(false))
{
var result = CharsetDetector.DetectFromStream(stream).Detected;
var result = await CharsetDetector.DetectFromStreamAsync(stream, cancellationToken).ConfigureAwait(false);
var detected = result.Detected;
stream.Position = 0;
if (result is not null)
if (detected is not null)
{
_logger.LogDebug("charset {CharSet} detected for {Path}", result.EncodingName, fileInfo.Path);
_logger.LogDebug("charset {CharSet} detected for {Path}", detected.EncodingName, fileInfo.Path);
using var reader = new StreamReader(stream, result.Encoding);
using var reader = new StreamReader(stream, detected.Encoding);
var text = await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false);
return new MemoryStream(Encoding.UTF8.GetBytes(text));
@@ -938,7 +939,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles
using (var stream = await GetStream(path, mediaSource.Protocol, cancellationToken).ConfigureAwait(false))
{
var charset = CharsetDetector.DetectFromStream(stream).Detected?.EncodingName ?? string.Empty;
var result = await CharsetDetector.DetectFromStreamAsync(stream, cancellationToken).ConfigureAwait(false);
var charset = result.Detected?.EncodingName ?? string.Empty;
// UTF16 is automatically converted to UTF8 by FFmpeg, do not specify a character encoding
if ((path.EndsWith(".ass", StringComparison.Ordinal) || path.EndsWith(".ssa", StringComparison.Ordinal) || path.EndsWith(".srt", StringComparison.Ordinal))
+9
View File
@@ -76,5 +76,14 @@ namespace MediaBrowser.Model.Dto
/// </summary>
/// <value>The item count.</value>
public int ItemCount { get; set; }
/// <summary>
/// Adds all counts.
/// </summary>
/// <returns>The total of the counts.</returns>
public int TotalItemCount()
{
return MovieCount + SeriesCount + EpisodeCount + ArtistCount + ProgramCount + TrailerCount + SongCount + AlbumCount + MusicVideoCount + BoxSetCount + BookCount;
}
}
}