Merge remote-tracking branch 'jellyfinorigin/master' into feature/10.10/DetachedMigration

This commit is contained in:
JPVenson 2025-02-21 11:08:09 +00:00
commit 8c0b0d9102
472 changed files with 26419 additions and 9694 deletions

View File

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

View File

@ -1,28 +0,0 @@
{
"name": "Development Jellyfin Server - FFmpeg",
"image":"mcr.microsoft.com/devcontainers/dotnet:8.0-jammy",
// restores nuget packages, installs the dotnet workloads and installs the dev https certificate
"postStartCommand": "dotnet restore; dotnet workload update; dotnet dev-certs https --trust; sudo bash \"./.devcontainer/Dev - Server Ffmpeg/install-ffmpeg.sh\"",
// reads the extensions list and installs them
"postAttachCommand": "cat .vscode/extensions.json | jq -r .recommendations[] | xargs -n 1 code --install-extension",
"features": {
"ghcr.io/devcontainers/features/dotnet:2": {
"version": "none",
"dotnetRuntimeVersions": "8.0",
"aspNetCoreRuntimeVersions": "8.0"
},
"ghcr.io/devcontainers-contrib/features/apt-packages:1": {
"preserve_apt_list": false,
"packages": ["libfontconfig1"]
},
"ghcr.io/devcontainers/features/docker-in-docker:2": {
"dockerDashComposeVersion": "v2"
},
"ghcr.io/devcontainers/features/github-cli:1": {},
"ghcr.io/eitsupi/devcontainer-features/jq-likes:2": {}
},
"hostRequirements": {
"memory": "8gb",
"cpus": 4
}
}

View File

@ -1,15 +1,15 @@
{ {
"name": "Development Jellyfin Server", "name": "Development Jellyfin Server",
"image":"mcr.microsoft.com/devcontainers/dotnet:8.0-jammy", "image":"mcr.microsoft.com/devcontainers/dotnet:9.0-bookworm",
// restores nuget packages, installs the dotnet workloads and installs the dev https certificate // restores nuget packages, installs the dotnet workloads and installs the dev https certificate
"postStartCommand": "dotnet restore; dotnet workload update; dotnet dev-certs https --trust", "postStartCommand": "sudo dotnet restore; sudo dotnet workload update; sudo dotnet dev-certs https --trust; sudo bash \"./.devcontainer/install-ffmpeg.sh\"",
// reads the extensions list and installs them // reads the extensions list and installs them
"postAttachCommand": "cat .vscode/extensions.json | jq -r .recommendations[] | xargs -n 1 code --install-extension", "postAttachCommand": "cat .vscode/extensions.json | jq -r .recommendations[] | xargs -n 1 code --install-extension",
"features": { "features": {
"ghcr.io/devcontainers/features/dotnet:2": { "ghcr.io/devcontainers/features/dotnet:2": {
"version": "none", "version": "none",
"dotnetRuntimeVersions": "8.0", "dotnetRuntimeVersions": "9.0",
"aspNetCoreRuntimeVersions": "8.0" "aspNetCoreRuntimeVersions": "9.0"
}, },
"ghcr.io/devcontainers-contrib/features/apt-packages:1": { "ghcr.io/devcontainers-contrib/features/apt-packages:1": {
"preserve_apt_list": false, "preserve_apt_list": false,

View File

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
## configure the following for a manuall install of a specific version from the repo ## configure the following for a manual install of a specific version from the repo
# wget https://repo.jellyfin.org/releases/server/ubuntu/versions/jellyfin-ffmpeg/6.0.1-1/jellyfin-ffmpeg6_6.0.1-1-jammy_amd64.deb -O ffmpeg.deb # wget https://repo.jellyfin.org/releases/server/ubuntu/versions/jellyfin-ffmpeg/6.0.1-1/jellyfin-ffmpeg6_6.0.1-1-jammy_amd64.deb -O ffmpeg.deb
@ -29,4 +29,4 @@ Signed-By: /etc/apt/keyrings/jellyfin.gpg
EOF EOF
sudo apt update -y sudo apt update -y
sudo apt install jellyfin-ffmpeg6 -y sudo apt install jellyfin-ffmpeg7 -y

View File

@ -192,3 +192,341 @@ csharp_space_between_method_call_empty_parameter_list_parentheses = false
# Wrapping preferences # Wrapping preferences
csharp_preserve_single_line_statements = true csharp_preserve_single_line_statements = true
csharp_preserve_single_line_blocks = true csharp_preserve_single_line_blocks = true
###############################
# C# Analyzer Rules #
###############################
### ERROR #
###########
# error on SA1000: The keyword 'new' should be followed by a space
dotnet_diagnostic.SA1000.severity = error
# error on SA1001: Commas should not be preceded by whitespace
dotnet_diagnostic.SA1001.severity = error
# error on SA1106: Code should not contain empty statements
dotnet_diagnostic.SA1106.severity = error
# error on SA1107: Code should not contain multiple statements on one line
dotnet_diagnostic.SA1107.severity = error
# error on SA1028: Code should not contain trailing whitespace
dotnet_diagnostic.SA1028.severity = error
# error on SA1117: The parameters should all be placed on the same line or each parameter should be placed on its own line
dotnet_diagnostic.SA1117.severity = error
# error on SA1137: Elements should have the same indentation
dotnet_diagnostic.SA1137.severity = error
# error on SA1142: Refer to tuple fields by name
dotnet_diagnostic.SA1142.severity = error
# error on SA1210: Using directives should be ordered alphabetically by the namespaces
dotnet_diagnostic.SA1210.severity = error
# error on SA1316: Tuple element names should use correct casing
dotnet_diagnostic.SA1316.severity = error
# error on SA1414: Tuple types in signatures should have element names
dotnet_diagnostic.SA1414.severity = error
# disable warning SA1513: Closing brace should be followed by blank line
dotnet_diagnostic.SA1513.severity = error
# error on SA1518: File is required to end with a single newline character
dotnet_diagnostic.SA1518.severity = error
# error on SA1629: Documentation text should end with a period
dotnet_diagnostic.SA1629.severity = error
# error on CA1001: Types that own disposable fields should be disposable
dotnet_diagnostic.CA1001.severity = error
# error on CA1012: Abstract types should not have public constructors
dotnet_diagnostic.CA1012.severity = error
# error on CA1063: Implement IDisposable correctly
dotnet_diagnostic.CA1063.severity = error
# error on CA1305: Specify IFormatProvider
dotnet_diagnostic.CA1305.severity = error
# error on CA1307: Specify StringComparison for clarity
dotnet_diagnostic.CA1307.severity = error
# error on CA1309: Use ordinal StringComparison
dotnet_diagnostic.CA1309.severity = error
# error on CA1310: Specify StringComparison for correctness
dotnet_diagnostic.CA1310.severity = error
# error on CA1513: Use 'ObjectDisposedException.ThrowIf' instead of explicitly throwing a new exception instance
dotnet_diagnostic.CA1513.severity = error
# error on CA1725: Parameter names should match base declaration
dotnet_diagnostic.CA1725.severity = error
# error on CA1725: Call async methods when in an async method
dotnet_diagnostic.CA1727.severity = error
# error on CA1813: Avoid unsealed attributes
dotnet_diagnostic.CA1813.severity = error
# error on CA1834: Use 'StringBuilder.Append(char)' instead of 'StringBuilder.Append(string)' when the input is a constant unit string
dotnet_diagnostic.CA1834.severity = error
# error on CA1843: Do not use 'WaitAll' with a single task
dotnet_diagnostic.CA1843.severity = error
# error on CA1845: Use span-based 'string.Concat'
dotnet_diagnostic.CA1845.severity = error
# error on CA1849: Call async methods when in an async method
dotnet_diagnostic.CA1849.severity = error
# error on CA1851: Possible multiple enumerations of IEnumerable collection
dotnet_diagnostic.CA1851.severity = error
# error on CA1854: Prefer a 'TryGetValue' call over a Dictionary indexer access guarded by a 'ContainsKey' check to avoid double lookup
dotnet_diagnostic.CA1854.severity = error
# error on CA1860: Avoid using 'Enumerable.Any()' extension method
dotnet_diagnostic.CA1860.severity = error
# error on CA1862: Use the 'StringComparison' method overloads to perform case-insensitive string comparisons
dotnet_diagnostic.CA1862.severity = error
# error on CA1863: Use 'CompositeFormat'
dotnet_diagnostic.CA1863.severity = error
# error on CA1864: Prefer the 'IDictionary.TryAdd(TKey, TValue)' method
dotnet_diagnostic.CA1864.severity = error
# error on CA1865-CA1867: Use 'string.Method(char)' instead of 'string.Method(string)' for string with single char
dotnet_diagnostic.CA1865.severity = error
dotnet_diagnostic.CA1866.severity = error
dotnet_diagnostic.CA1867.severity = error
# error on CA1868: Unnecessary call to 'Contains' for sets
dotnet_diagnostic.CA1868.severity = error
# error on CA1869: Cache and reuse 'JsonSerializerOptions' instances
dotnet_diagnostic.CA1869.severity = error
# error on CA1870: Use a cached 'SearchValues' instance
dotnet_diagnostic.CA1870.severity = error
# error on CA1871: Do not pass a nullable struct to 'ArgumentNullException.ThrowIfNull'
dotnet_diagnostic.CA1871.severity = error
# error on CA1872: Prefer 'Convert.ToHexString' and 'Convert.ToHexStringLower' over call chains based on 'BitConverter.ToString'
dotnet_diagnostic.CA1872.severity = error
# error on CA2016: Forward the CancellationToken parameter to methods that take one
# or pass in 'CancellationToken.None' explicitly to indicate intentionally not propagating the token
dotnet_diagnostic.CA2016.severity = error
# error on CA2201: Exception type System.Exception is not sufficiently specific
dotnet_diagnostic.CA2201.severity = error
# error on CA2215: Dispose methods should call base class dispose
dotnet_diagnostic.CA2215.severity = error
# error on CA2249: Use 'string.Contains' instead of 'string.IndexOf' to improve readability
dotnet_diagnostic.CA2249.severity = error
# error on CA2254: Template should be a static expression
dotnet_diagnostic.CA2254.severity = error
################
### SUGGESTION #
################
# disable warning CA1014: Mark assemblies with CLSCompliantAttribute
dotnet_diagnostic.CA1014.severity = suggestion
# disable warning CA1024: Use properties where appropriate
dotnet_diagnostic.CA1024.severity = suggestion
# disable warning CA1031: Do not catch general exception types
dotnet_diagnostic.CA1031.severity = suggestion
# disable warning CA1032: Implement standard exception constructors
dotnet_diagnostic.CA1032.severity = suggestion
# disable warning CA1040: Avoid empty interfaces
dotnet_diagnostic.CA1040.severity = suggestion
# disable warning CA1062: Validate arguments of public methods
dotnet_diagnostic.CA1062.severity = suggestion
# TODO: enable when false positives are fixed
# disable warning CA1508: Avoid dead conditional code
dotnet_diagnostic.CA1508.severity = suggestion
# disable warning CA1515: Consider making public types internal
dotnet_diagnostic.CA1515.severity = suggestion
# disable warning CA1716: Identifiers should not match keywords
dotnet_diagnostic.CA1716.severity = suggestion
# disable warning CA1720: Identifiers should not contain type names
dotnet_diagnostic.CA1720.severity = suggestion
# disable warning CA1724: Type names should not match namespaces
dotnet_diagnostic.CA1724.severity = suggestion
# disable warning CA1805: Do not initialize unnecessarily
dotnet_diagnostic.CA1805.severity = suggestion
# disable warning CA1812: internal class that is apparently never instantiated.
# If so, remove the code from the assembly.
# If this class is intended to contain only static members, make it static
dotnet_diagnostic.CA1812.severity = suggestion
# disable warning CA1822: Member does not access instance data and can be marked as static
dotnet_diagnostic.CA1822.severity = suggestion
# CA1859: Use concrete types when possible for improved performance
dotnet_diagnostic.CA1859.severity = suggestion
# TODO: Enable
# CA1861: Prefer 'static readonly' fields over constant array arguments if the called method is called repeatedly and is not mutating the passed array
dotnet_diagnostic.CA1861.severity = suggestion
# disable warning CA2000: Dispose objects before losing scope
dotnet_diagnostic.CA2000.severity = suggestion
# disable warning CA2253: Named placeholders should not be numeric values
dotnet_diagnostic.CA2253.severity = suggestion
# disable warning CA5394: Do not use insecure randomness
dotnet_diagnostic.CA5394.severity = suggestion
# error on CA3003: Review code for file path injection vulnerabilities
dotnet_diagnostic.CA3003.severity = suggestion
# error on CA3006: Review code for process command injection vulnerabilities
dotnet_diagnostic.CA3006.severity = suggestion
###############
### DISABLED #
###############
# disable warning SA1009: Closing parenthesis should be followed by a space.
dotnet_diagnostic.SA1009.severity = none
# disable warning SA1011: Closing square bracket should be followed by a space.
dotnet_diagnostic.SA1011.severity = none
# disable warning SA1101: Prefix local calls with 'this.'
dotnet_diagnostic.SA1101.severity = none
# disable warning SA1108: Block statements should not contain embedded comments
dotnet_diagnostic.SA1108.severity = none
# disable warning SA1118: Parameter must not span multiple lines.
dotnet_diagnostic.SA1118.severity = none
# disable warning SA1128:: Put constructor initializers on their own line
dotnet_diagnostic.SA1128.severity = none
# disable warning SA1130: Use lambda syntax
dotnet_diagnostic.SA1130.severity = none
# disable warning SA1200: 'using' directive must appear within a namespace declaration
dotnet_diagnostic.SA1200.severity = none
# disable warning SA1202: 'public' members must come before 'private' members
dotnet_diagnostic.SA1202.severity = none
# disable warning SA1204: Static members must appear before non-static members
dotnet_diagnostic.SA1204.severity = none
# disable warning SA1309: Fields must not begin with an underscore
dotnet_diagnostic.SA1309.severity = none
# disable warning SA1311: Static readonly fields should begin with upper-case letter
dotnet_diagnostic.SA1311.severity = none
# disable warning SA1413: Use trailing comma in multi-line initializers
dotnet_diagnostic.SA1413.severity = none
# disable warning SA1512: Single-line comments must not be followed by blank line
dotnet_diagnostic.SA1512.severity = none
# disable warning SA1515: Single-line comment should be preceded by blank line
dotnet_diagnostic.SA1515.severity = none
# disable warning SA1600: Elements should be documented
dotnet_diagnostic.SA1600.severity = none
# disable warning SA1601: Partial elements should be documented
dotnet_diagnostic.SA1601.severity = none
# disable warning SA1602: Enumeration items should be documented
dotnet_diagnostic.SA1602.severity = none
# disable warning SA1633: The file header is missing or not located at the top of the file
dotnet_diagnostic.SA1633.severity = none
# disable warning CA1054: Change the type of parameter url from string to System.Uri
dotnet_diagnostic.CA1054.severity = none
# disable warning CA1055: URI return values should not be strings
dotnet_diagnostic.CA1055.severity = none
# disable warning CA1056: URI properties should not be strings
dotnet_diagnostic.CA1056.severity = none
# disable warning CA1303: Do not pass literals as localized parameters
dotnet_diagnostic.CA1303.severity = none
# disable warning CA1308: Normalize strings to uppercase
dotnet_diagnostic.CA1308.severity = none
# disable warning CA1848: Use the LoggerMessage delegates
dotnet_diagnostic.CA1848.severity = none
# disable warning CA2101: Specify marshaling for P/Invoke string arguments
dotnet_diagnostic.CA2101.severity = none
# disable warning CA2234: Pass System.Uri objects instead of strings
dotnet_diagnostic.CA2234.severity = none
# error on RS0030: Do not used banned APIs
dotnet_diagnostic.RS0030.severity = error
# disable warning IDISP001: Dispose created
dotnet_diagnostic.IDISP001.severity = suggestion
# TODO: Enable when false positives are fixed
# disable warning IDISP003: Dispose previous before re-assigning
dotnet_diagnostic.IDISP003.severity = suggestion
# disable warning IDISP004: Don't ignore created IDisposable
dotnet_diagnostic.IDISP004.severity = suggestion
# disable warning IDISP007: Don't dispose injected
dotnet_diagnostic.IDISP007.severity = suggestion
# disable warning IDISP008: Don't assign member with injected and created disposables
dotnet_diagnostic.IDISP008.severity = suggestion
[tests/**.{cs,vb}]
# disable warning SA0001: XML comment analysis is disabled due to project configuration
dotnet_diagnostic.SA0001.severity = none
# disable warning CA1707: Identifiers should not contain underscores
dotnet_diagnostic.CA1707.severity = none
# disable warning CA2007: Consider calling ConfigureAwait on the awaited task
dotnet_diagnostic.CA2007.severity = none
# disable warning CA2234: Pass system uri objects instead of strings
dotnet_diagnostic.CA2234.severity = suggestion
# disable warning xUnit1028: Test methods must have a supported return type.
dotnet_diagnostic.xUnit1028.severity = none
# CA1826: Do not use Enumerable methods on indexable collections
dotnet_diagnostic.CA1826.severity = suggestion

View File

@ -14,7 +14,7 @@ body:
label: "This issue respects the following points:" label: "This issue respects the following points:"
description: All conditions are **required**. Failure to comply with any of these conditions may cause your issue to be closed without comment. description: All conditions are **required**. Failure to comply with any of these conditions may cause your issue to be closed without comment.
options: options:
- label: This is a **bug**, not a question or a configuration issue; Please visit our forum or chat rooms first to troubleshoot with volunteers, before creating a report. The links can be found [here](https://jellyfin.org/contact/). - label: This is a **bug**, not a question or a configuration issue; Please visit our [forum or chat rooms](https://jellyfin.org/contact/) first to troubleshoot with volunteers, before creating a report.
required: true required: true
- label: This issue is **not** already reported on [GitHub](https://github.com/jellyfin/jellyfin/issues?q=is%3Aopen+is%3Aissue) _(I've searched it)_. - label: This issue is **not** already reported on [GitHub](https://github.com/jellyfin/jellyfin/issues?q=is%3Aopen+is%3Aissue) _(I've searched it)_.
required: true required: true
@ -86,7 +86,7 @@ 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.9.11+ - 10.10.0+
- Master - Master
- Unstable - Unstable
- Older* - Older*

View File

@ -22,16 +22,16 @@ jobs:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25 # v4.1.0 uses: actions/setup-dotnet@3951f0dfe7a07e2313ec93c75700083e2005cbab # v4.3.0
with: with:
dotnet-version: '8.0.x' dotnet-version: '9.0.x'
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0 uses: github/codeql-action/init@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
queries: +security-extended queries: +security-extended
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0 uses: github/codeql-action/autobuild@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0 uses: github/codeql-action/analyze@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9

View File

@ -16,12 +16,17 @@ jobs:
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
uses: actions/setup-dotnet@3951f0dfe7a07e2313ec93c75700083e2005cbab # v4.3.0
with:
dotnet-version: '9.0.x'
- name: Build - name: Build
run: | run: |
dotnet build Jellyfin.Server -o ./out dotnet build Jellyfin.Server -o ./out
- name: Upload Head - name: Upload Head
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with: with:
name: abi-head name: abi-head
retention-days: 14 retention-days: 14
@ -41,6 +46,11 @@ jobs:
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
uses: actions/setup-dotnet@3951f0dfe7a07e2313ec93c75700083e2005cbab # v4.3.0
with:
dotnet-version: '9.0.x'
- name: Checkout common ancestor - name: Checkout common ancestor
env: env:
HEAD_REF: ${{ github.head_ref }} HEAD_REF: ${{ github.head_ref }}
@ -55,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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with: with:
name: abi-base name: abi-base
retention-days: 14 retention-days: 14

View File

@ -21,18 +21,18 @@ jobs:
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@3e891b0cb619bf60e2c25674b222b8940e2c1c25 # v4.1.0 uses: actions/setup-dotnet@3951f0dfe7a07e2313ec93c75700083e2005cbab # v4.3.0
with: with:
dotnet-version: '8.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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with: with:
name: openapi-head name: openapi-head
retention-days: 14 retention-days: 14
if-no-files-found: error if-no-files-found: error
path: tests/Jellyfin.Server.Integration.Tests/bin/Release/net8.0/openapi.json path: tests/Jellyfin.Server.Integration.Tests/bin/Release/net9.0/openapi.json
openapi-base: openapi-base:
name: OpenAPI - BASE name: OpenAPI - BASE
@ -55,18 +55,18 @@ 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@3e891b0cb619bf60e2c25674b222b8940e2c1c25 # v4.1.0 uses: actions/setup-dotnet@3951f0dfe7a07e2313ec93c75700083e2005cbab # v4.3.0
with: with:
dotnet-version: '8.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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with: with:
name: openapi-base name: openapi-base
retention-days: 14 retention-days: 14
if-no-files-found: error if-no-files-found: error
path: tests/Jellyfin.Server.Integration.Tests/bin/Release/net8.0/openapi.json path: tests/Jellyfin.Server.Integration.Tests/bin/Release/net9.0/openapi.json
openapi-diff: openapi-diff:
permissions: permissions:
@ -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@25ce8cbbcb08177468c7ff7ec5cbfa236f9341e1 # v1.1.0 uses: appleboy/ssh-action@7eaf76671a0d7eec5d98ee897acda4f968735a17 # v1.2.0
with: with:
host: "${{ secrets.REPO_HOST }}" host: "${{ secrets.REPO_HOST }}"
username: "${{ secrets.REPO_USER }}" username: "${{ secrets.REPO_USER }}"
@ -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@25ce8cbbcb08177468c7ff7ec5cbfa236f9341e1 # v1.1.0 uses: appleboy/ssh-action@7eaf76671a0d7eec5d98ee897acda4f968735a17 # v1.2.0
with: with:
host: "${{ secrets.REPO_HOST }}" host: "${{ secrets.REPO_HOST }}"
username: "${{ secrets.REPO_USER }}" username: "${{ secrets.REPO_USER }}"

View File

@ -9,19 +9,20 @@ on:
pull_request: pull_request:
env: env:
SDK_VERSION: "8.0.x" SDK_VERSION: "9.0.x"
jobs: jobs:
run-tests: run-tests:
strategy: strategy:
matrix: matrix:
os: ["ubuntu-latest", "macos-latest", "windows-latest"] os: ["ubuntu-latest", "macos-latest", "windows-latest"]
fail-fast: false
runs-on: "${{ matrix.os }}" runs-on: "${{ matrix.os }}"
steps: steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25 # v4.1.0 - uses: actions/setup-dotnet@3951f0dfe7a07e2313ec93c75700083e2005cbab # v4.3.0
with: with:
dotnet-version: ${{ env.SDK_VERSION }} dotnet-version: ${{ env.SDK_VERSION }}
@ -34,7 +35,7 @@ jobs:
--verbosity minimal --verbosity minimal
- name: Merge code coverage results - name: Merge code coverage results
uses: danielpalme/ReportGenerator-GitHub-Action@62f9e70ab348d56eee76d446b4db903a85ab0ea8 # v5.3.11 uses: danielpalme/ReportGenerator-GitHub-Action@f1927db1dbfc029b056583ee488832e939447fe6 # v5.4.4
with: with:
reports: "**/coverage.cobertura.xml" reports: "**/coverage.cobertura.xml"
targetdir: "merged/" targetdir: "merged/"

View File

@ -34,94 +34,6 @@ jobs:
env: env:
GITHUB_TOKEN: ${{ secrets.JF_BOT_TOKEN }} GITHUB_TOKEN: ${{ secrets.JF_BOT_TOKEN }}
check-backport:
permissions:
contents: read
name: Check Backport
if: ${{ ( github.event.issue.pull_request && contains(github.event.comment.body, '@jellyfin-bot check backport') ) || github.event.label.name == 'stable backport' || contains(github.event.pull_request.labels.*.name, 'stable backport' ) }}
runs-on: ubuntu-latest
steps:
- name: Notify as seen
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
if: ${{ github.event.comment != null }}
with:
token: ${{ secrets.JF_BOT_TOKEN }}
comment-id: ${{ github.event.comment.id }}
reactions: eyes
- name: Checkout the latest code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
token: ${{ secrets.JF_BOT_TOKEN }}
fetch-depth: 0
- name: Notify as running
id: comment_running
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
if: ${{ github.event.comment != null }}
with:
token: ${{ secrets.JF_BOT_TOKEN }}
issue-number: ${{ github.event.issue.number }}
body: |
Running backport tests...
- name: Perform test backport
id: run_tests
run: |
set +o errexit
git config --global user.name "Jellyfin Bot"
git config --global user.email "team@jellyfin.org"
CURRENT_BRANCH="origin/${GITHUB_HEAD_REF}"
git checkout master
git merge --no-ff ${CURRENT_BRANCH}
MERGE_COMMIT_HASH=$( git log -q -1 | head -1 | awk '{ print $2 }' )
git fetch --all
CURRENT_STABLE=$( git branch -r | grep 'origin/release' | sort -rV | head -1 | awk -F '/' '{ print $NF }' )
stable_branch="Current stable release branch: ${CURRENT_STABLE}"
echo ${stable_branch}
echo ::set-output name=branch::${stable_branch}
git checkout -t origin/${CURRENT_STABLE} -b ${CURRENT_STABLE}
git cherry-pick -sx -m1 ${MERGE_COMMIT_HASH} &>output.txt
retcode=$?
cat output.txt | grep -v 'hint:'
output="$( grep -v 'hint:' output.txt )"
output="${output//'%'/'%25'}"
output="${output//$'\n'/'%0A'}"
output="${output//$'\r'/'%0D'}"
echo ::set-output name=output::$output
exit ${retcode}
- name: Notify with result success
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
if: ${{ github.event.comment != null && success() }}
with:
token: ${{ secrets.JF_BOT_TOKEN }}
comment-id: ${{ steps.comment_running.outputs.comment-id }}
body: |
${{ steps.run_tests.outputs.branch }}
Output from `git cherry-pick`:
---
${{ steps.run_tests.outputs.output }}
reactions: hooray
- name: Notify with result failure
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
if: ${{ github.event.comment != null && failure() }}
with:
token: ${{ secrets.JF_BOT_TOKEN }}
comment-id: ${{ steps.comment_running.outputs.comment-id }}
body: |
${{ steps.run_tests.outputs.branch }}
Output from `git cherry-pick`:
---
${{ steps.run_tests.outputs.output }}
reactions: confused
rename: rename:
name: Rename name: Rename
if: contains(github.event.comment.body, '@jellyfin-bot rename') && github.event.comment.author_association == 'MEMBER' if: contains(github.event.comment.body, '@jellyfin-bot rename') && github.event.comment.author_association == 'MEMBER'
@ -132,7 +44,7 @@ jobs:
with: with:
repository: jellyfin/jellyfin-triage-script repository: jellyfin/jellyfin-triage-script
- name: install python - name: install python
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
with: with:
python-version: '3.12' python-version: '3.12'
cache: 'pip' cache: 'pip'

View File

@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ contains(github.repository, 'jellyfin/') }} if: ${{ contains(github.repository, 'jellyfin/') }}
steps: steps:
- uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0 - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
with: with:
repo-token: ${{ secrets.JF_BOT_TOKEN }} repo-token: ${{ secrets.JF_BOT_TOKEN }}
ascending: true ascending: true

View File

@ -14,7 +14,7 @@ jobs:
with: with:
repository: jellyfin/jellyfin-triage-script repository: jellyfin/jellyfin-triage-script
- name: install python - name: install python
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
with: with:
python-version: '3.12' python-version: '3.12'
cache: 'pip' cache: 'pip'

View File

@ -15,7 +15,7 @@ jobs:
if: ${{ github.repository == 'jellyfin/jellyfin' }} if: ${{ github.repository == 'jellyfin/jellyfin' }}
steps: steps:
- name: Apply label - name: Apply label
uses: eps1lon/actions-label-merge-conflict@1b1b1fcde06a9b3d089f3464c96417961dde1168 # v3.0.2 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_target'}}
with: with:
dirtyLabel: 'merge conflict' dirtyLabel: 'merge conflict'

View File

@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ contains(github.repository, 'jellyfin/') }} if: ${{ contains(github.repository, 'jellyfin/') }}
steps: steps:
- uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0 - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
with: with:
repo-token: ${{ secrets.JF_BOT_TOKEN }} repo-token: ${{ secrets.JF_BOT_TOKEN }}
ascending: true ascending: true

View File

@ -4,7 +4,8 @@
"editorconfig.editorconfig", "editorconfig.editorconfig",
"github.vscode-github-actions", "github.vscode-github-actions",
"ms-dotnettools.vscode-dotnet-runtime", "ms-dotnettools.vscode-dotnet-runtime",
"ms-dotnettools.csdevkit" "ms-dotnettools.csdevkit",
"alexcvzz.vscode-sqlite"
], ],
"unwantedRecommendations": [ "unwantedRecommendations": [

6
.vscode/launch.json vendored
View File

@ -6,7 +6,7 @@
"type": "coreclr", "type": "coreclr",
"request": "launch", "request": "launch",
"preLaunchTask": "build", "preLaunchTask": "build",
"program": "${workspaceFolder}/Jellyfin.Server/bin/Debug/net8.0/jellyfin.dll", "program": "${workspaceFolder}/Jellyfin.Server/bin/Debug/net9.0/jellyfin.dll",
"args": [], "args": [],
"cwd": "${workspaceFolder}/Jellyfin.Server", "cwd": "${workspaceFolder}/Jellyfin.Server",
"console": "internalConsole", "console": "internalConsole",
@ -22,7 +22,7 @@
"type": "coreclr", "type": "coreclr",
"request": "launch", "request": "launch",
"preLaunchTask": "build", "preLaunchTask": "build",
"program": "${workspaceFolder}/Jellyfin.Server/bin/Debug/net8.0/jellyfin.dll", "program": "${workspaceFolder}/Jellyfin.Server/bin/Debug/net9.0/jellyfin.dll",
"args": ["--nowebclient"], "args": ["--nowebclient"],
"cwd": "${workspaceFolder}/Jellyfin.Server", "cwd": "${workspaceFolder}/Jellyfin.Server",
"console": "internalConsole", "console": "internalConsole",
@ -34,7 +34,7 @@
"type": "coreclr", "type": "coreclr",
"request": "launch", "request": "launch",
"preLaunchTask": "build", "preLaunchTask": "build",
"program": "${workspaceFolder}/Jellyfin.Server/bin/Debug/net8.0/jellyfin.dll", "program": "${workspaceFolder}/Jellyfin.Server/bin/Debug/net9.0/jellyfin.dll",
"args": ["--nowebclient", "--ffmpeg", "/usr/lib/jellyfin-ffmpeg/ffmpeg"], "args": ["--nowebclient", "--ffmpeg", "/usr/lib/jellyfin-ffmpeg/ffmpeg"],
"cwd": "${workspaceFolder}/Jellyfin.Server", "cwd": "${workspaceFolder}/Jellyfin.Server",
"console": "internalConsole", "console": "internalConsole",

View File

@ -192,6 +192,9 @@
- [jaina heartles](https://github.com/heartles) - [jaina heartles](https://github.com/heartles)
- [oxixes](https://github.com/oxixes) - [oxixes](https://github.com/oxixes)
- [elfalem](https://github.com/elfalem) - [elfalem](https://github.com/elfalem)
- [Kenneth Cochran](https://github.com/kennethcochran)
- [benedikt257](https://github.com/benedikt257)
- [revam](https://github.com/revam)
# Emby Contributors # Emby Contributors
@ -265,3 +268,4 @@
- [0x25CBFC4F](https://github.com/0x25CBFC4F) - [0x25CBFC4F](https://github.com/0x25CBFC4F)
- [Robert Lützner](https://github.com/rluetzner) - [Robert Lützner](https://github.com/rluetzner)
- [Nathan McCrina](https://github.com/nfmccrina) - [Nathan McCrina](https://github.com/nfmccrina)
- [Martin Reuter](https://github.com/reuterma24)

View File

@ -3,11 +3,11 @@
<PropertyGroup> <PropertyGroup>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)/jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsNotAsErrors>NU1902;NU1903</WarningsNotAsErrors>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

View File

@ -4,88 +4,87 @@
</PropertyGroup> </PropertyGroup>
<!-- Run "dotnet list package (dash,dash)outdated" to see the latest versions of each package.--> <!-- Run "dotnet list package (dash,dash)outdated" to see the latest versions of each package.-->
<ItemGroup Label="Package Dependencies"> <ItemGroup Label="Package Dependencies">
<PackageVersion Include="AsyncKeyedLock" Version="7.0.2" /> <PackageVersion Include="AsyncKeyedLock" Version="7.1.4" />
<PackageVersion Include="AutoFixture.AutoMoq" Version="4.18.1" /> <PackageVersion Include="AutoFixture.AutoMoq" Version="4.18.1" />
<PackageVersion Include="AutoFixture.Xunit2" Version="4.18.1" /> <PackageVersion Include="AutoFixture.Xunit2" Version="4.18.1" />
<PackageVersion Include="AutoFixture" Version="4.18.1" /> <PackageVersion Include="AutoFixture" Version="4.18.1" />
<PackageVersion Include="BDInfo" Version="0.8.0" /> <PackageVersion Include="BDInfo" Version="0.8.0" />
<PackageVersion Include="BlurHashSharp.SkiaSharp" Version="1.3.3" /> <PackageVersion Include="BlurHashSharp.SkiaSharp" Version="1.3.4" />
<PackageVersion Include="BlurHashSharp" Version="1.3.3" /> <PackageVersion Include="BlurHashSharp" Version="1.3.4" />
<PackageVersion Include="CommandLineParser" Version="2.9.1" /> <PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="coverlet.collector" Version="6.0.2" /> <PackageVersion Include="coverlet.collector" Version="6.0.4" />
<PackageVersion Include="Diacritics" Version="3.3.29" /> <PackageVersion Include="Diacritics" Version="3.3.29" />
<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="2.16.6" /> <PackageVersion Include="FsCheck.Xunit" Version="3.1.0" />
<PackageVersion Include="HarfBuzzSharp.NativeAssets.Linux" Version="7.3.0.2" /> <PackageVersion Include="HarfBuzzSharp.NativeAssets.Linux" Version="7.3.0.3" />
<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" />
<PackageVersion Include="Jellyfin.XmlTv" Version="10.8.0" /> <PackageVersion Include="Jellyfin.XmlTv" Version="10.8.0" />
<PackageVersion Include="libse" Version="4.0.8" /> <PackageVersion Include="libse" Version="4.0.10" />
<PackageVersion Include="LrcParser" Version="2024.0728.2" /> <PackageVersion Include="LrcParser" Version="2024.0728.2" />
<PackageVersion Include="MetaBrainz.MusicBrainz" Version="6.1.0" /> <PackageVersion Include="MetaBrainz.MusicBrainz" Version="6.1.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="8.0.10" /> <PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.10" /> <PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.2" />
<PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4" /> <PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="8.0.10" /> <PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.10" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.10" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.10" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.2" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.10" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" /> <PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" /> <PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" /> <PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" /> <PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" /> <PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1" /> <PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" /> <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" /> <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="8.0.10" /> <PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="8.0.10" /> <PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" /> <PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.1" /> <PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2" /> <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.1" /> <PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="8.0.2" /> <PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.2" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.1" /> <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageVersion Include="MimeTypes" Version="2.4.0" /> <PackageVersion Include="MimeTypes" Version="2.5.2" />
<PackageVersion Include="Mono.Nat" Version="3.0.4" />
<PackageVersion Include="Moq" Version="4.18.4" /> <PackageVersion Include="Moq" Version="4.18.4" />
<PackageVersion Include="NEbml" Version="0.11.0" /> <PackageVersion Include="NEbml" Version="0.12.0" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" /> <PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="PlaylistsNET" Version="1.4.1" /> <PackageVersion Include="PlaylistsNET" Version="1.4.1" />
<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="Serilog.AspNetCore" Version="8.0.3" /> <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.Settings.Configuration" Version="8.0.4" /> <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.0.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="6.0.0" /> <PackageVersion Include="Serilog.Sinks.File" Version="6.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" />
<PackageVersion Include="SharpFuzz" Version="2.1.1" /> <PackageVersion Include="SharpFuzz" Version="2.2.0" />
<PackageVersion Include="SkiaSharp" Version="2.88.8" /> <PackageVersion Include="SkiaSharp" Version="2.88.9" />
<PackageVersion Include="SkiaSharp.HarfBuzz" Version="2.88.8" /> <PackageVersion Include="SkiaSharp.HarfBuzz" Version="2.88.9" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.8" /> <PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" />
<PackageVersion Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" /> <PackageVersion Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" /> <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
<PackageVersion Include="Svg.Skia" Version="2.0.0.1" /> <PackageVersion Include="Svg.Skia" Version="2.0.0.4" />
<PackageVersion Include="Swashbuckle.AspNetCore.ReDoc" Version="6.5.0" /> <PackageVersion Include="Swashbuckle.AspNetCore.ReDoc" Version="6.5.0" />
<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.1" /> <PackageVersion Include="System.Linq.Async" Version="6.0.1" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="8.0.0" /> <PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.2" />
<PackageVersion Include="System.Text.Json" Version="8.0.5" /> <PackageVersion Include="System.Text.Json" Version="9.0.2" />
<PackageVersion Include="System.Threading.Tasks.Dataflow" Version="8.0.1" /> <PackageVersion Include="System.Threading.Tasks.Dataflow" Version="9.0.2" />
<PackageVersion Include="TagLibSharp" Version="2.3.0" /> <PackageVersion Include="TagLibSharp" Version="2.3.0" />
<PackageVersion Include="z440.atl.core" Version="6.6.0" /> <PackageVersion Include="z440.atl.core" Version="6.16.0" />
<PackageVersion Include="TMDbLib" Version="2.2.0" /> <PackageVersion Include="TMDbLib" Version="2.2.0" />
<PackageVersion Include="UTF.Unknown" Version="2.5.1" /> <PackageVersion Include="UTF.Unknown" Version="2.5.1" />
<PackageVersion Include="Xunit.Priority" Version="1.1.6" /> <PackageVersion Include="Xunit.Priority" Version="1.1.6" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" /> <PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
<PackageVersion Include="Xunit.SkippableFact" Version="1.4.13" /> <PackageVersion Include="Xunit.SkippableFact" Version="1.5.23" />
<PackageVersion Include="xunit" Version="2.9.2" /> <PackageVersion Include="xunit" Version="2.9.3" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -467,6 +467,14 @@ namespace Emby.Naming.Common
{ {
IsNamed = true IsNamed = true
}, },
// Anime style expression
// "[Group][Series Name][21][1080p][FLAC][HASH]"
// "[Group] Series Name [04][BDRIP]"
new EpisodeExpression(@"(?:\[(?:[^\]]+)\]\s*)?(?<seriesname>\[[^\]]+\]|[^[\]]+)\s*\[(?<epnumber>[0-9]+)\]")
{
IsNamed = true
},
}; };
VideoExtraRules = new[] VideoExtraRules = new[]

View File

@ -6,7 +6,7 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<PublishRepositoryUrl>true</PublishRepositoryUrl> <PublishRepositoryUrl>true</PublishRepositoryUrl>
@ -36,7 +36,7 @@
<PropertyGroup> <PropertyGroup>
<Authors>Jellyfin Contributors</Authors> <Authors>Jellyfin Contributors</Authors>
<PackageId>Jellyfin.Naming</PackageId> <PackageId>Jellyfin.Naming</PackageId>
<VersionPrefix>10.10.0</VersionPrefix> <VersionPrefix>10.11.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

@ -12,7 +12,7 @@ namespace Emby.Naming.TV
/// <summary> /// <summary>
/// Regex that matches strings of at least 2 characters separated by a dot or underscore. /// Regex that matches strings of at least 2 characters separated by a dot or underscore.
/// Used for removing separators between words, i.e turns "The_show" into "The show" while /// Used for removing separators between words, i.e turns "The_show" into "The show" while
/// preserving namings like "S.H.O.W". /// preserving names like "S.H.O.W".
/// </summary> /// </summary>
[GeneratedRegex(@"((?<a>[^\._]{2,})[\._]*)|([\._](?<b>[^\._]{2,}))")] [GeneratedRegex(@"((?<a>[^\._]{2,})[\._]*)|([\._](?<b>[^\._]{2,}))")]
private static partial Regex SeriesNameRegex(); private static partial Regex SeriesNameRegex();

View File

@ -19,7 +19,7 @@
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup> </PropertyGroup>

View File

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events; using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
@ -19,7 +20,7 @@ namespace Emby.Server.Implementations.AppBase
public abstract class BaseConfigurationManager : IConfigurationManager public abstract class BaseConfigurationManager : IConfigurationManager
{ {
private readonly ConcurrentDictionary<string, object> _configurations = new(); private readonly ConcurrentDictionary<string, object> _configurations = new();
private readonly object _configurationSyncLock = new(); private readonly Lock _configurationSyncLock = new();
private ConfigurationStore[] _configurationStores = Array.Empty<ConfigurationStore>(); private ConfigurationStore[] _configurationStores = Array.Empty<ConfigurationStore>();
private IConfigurationFactory[] _configurationFactories = Array.Empty<IConfigurationFactory>(); private IConfigurationFactory[] _configurationFactories = Array.Empty<IConfigurationFactory>();

View File

@ -40,6 +40,7 @@ using Jellyfin.MediaEncoding.Hls.Playlist;
using Jellyfin.Networking.Manager; using Jellyfin.Networking.Manager;
using Jellyfin.Networking.Udp; using Jellyfin.Networking.Udp;
using Jellyfin.Server.Implementations; using Jellyfin.Server.Implementations;
using Jellyfin.Server.Implementations.Item;
using Jellyfin.Server.Implementations.MediaSegments; using Jellyfin.Server.Implementations.MediaSegments;
using MediaBrowser.Common; using MediaBrowser.Common;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
@ -83,7 +84,6 @@ using MediaBrowser.Model.Net;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.System; using MediaBrowser.Model.System;
using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Tasks;
using MediaBrowser.Providers.Chapters;
using MediaBrowser.Providers.Lyric; using MediaBrowser.Providers.Lyric;
using MediaBrowser.Providers.Manager; using MediaBrowser.Providers.Manager;
using MediaBrowser.Providers.Plugins.Tmdb; using MediaBrowser.Providers.Plugins.Tmdb;
@ -268,6 +268,11 @@ namespace Emby.Server.Implementations
public string ExpandVirtualPath(string path) public string ExpandVirtualPath(string path)
{ {
if (path is null)
{
return null;
}
var appPaths = ApplicationPaths; var appPaths = ApplicationPaths;
return path.Replace(appPaths.VirtualDataPath, appPaths.DataPath, StringComparison.OrdinalIgnoreCase) return path.Replace(appPaths.VirtualDataPath, appPaths.DataPath, StringComparison.OrdinalIgnoreCase)
@ -492,10 +497,14 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IBlurayExaminer, BdInfoExaminer>(); serviceCollection.AddSingleton<IBlurayExaminer, BdInfoExaminer>();
serviceCollection.AddSingleton<IUserDataRepository, SqliteUserDataRepository>();
serviceCollection.AddSingleton<IUserDataManager, UserDataManager>(); serviceCollection.AddSingleton<IUserDataManager, UserDataManager>();
serviceCollection.AddSingleton<IItemRepository, SqliteItemRepository>(); serviceCollection.AddSingleton<IItemRepository, BaseItemRepository>();
serviceCollection.AddSingleton<IPeopleRepository, PeopleRepository>();
serviceCollection.AddSingleton<IChapterRepository, ChapterRepository>();
serviceCollection.AddSingleton<IMediaAttachmentRepository, MediaAttachmentRepository>();
serviceCollection.AddSingleton<IMediaStreamRepository, MediaStreamRepository>();
serviceCollection.AddSingleton<IItemTypeLookup, ItemTypeLookup>();
serviceCollection.AddSingleton<IMediaEncoder, MediaBrowser.MediaEncoding.Encoder.MediaEncoder>(); serviceCollection.AddSingleton<IMediaEncoder, MediaBrowser.MediaEncoding.Encoder.MediaEncoder>();
serviceCollection.AddSingleton<EncodingHelper>(); serviceCollection.AddSingleton<EncodingHelper>();
@ -540,8 +549,6 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IUserViewManager, UserViewManager>(); serviceCollection.AddSingleton<IUserViewManager, UserViewManager>();
serviceCollection.AddSingleton<IChapterManager, ChapterManager>();
serviceCollection.AddSingleton<IEncodingManager, MediaEncoder.EncodingManager>(); serviceCollection.AddSingleton<IEncodingManager, MediaEncoder.EncodingManager>();
serviceCollection.AddSingleton<IAuthService, AuthService>(); serviceCollection.AddSingleton<IAuthService, AuthService>();
@ -579,9 +586,6 @@ namespace Emby.Server.Implementations
} }
} }
((SqliteItemRepository)Resolve<IItemRepository>()).Initialize();
((SqliteUserDataRepository)Resolve<IUserDataRepository>()).Initialize();
var localizationManager = (LocalizationManager)Resolve<ILocalizationManager>(); var localizationManager = (LocalizationManager)Resolve<ILocalizationManager>();
await localizationManager.LoadAll().ConfigureAwait(false); await localizationManager.LoadAll().ConfigureAwait(false);
@ -607,7 +611,7 @@ namespace Emby.Server.Implementations
// Don't use an empty string password // Don't use an empty string password
password = string.IsNullOrWhiteSpace(password) ? null : password; password = string.IsNullOrWhiteSpace(password) ? null : password;
var localCert = new X509Certificate2(path, password, X509KeyStorageFlags.UserKeySet); var localCert = X509CertificateLoader.LoadPkcs12FromFile(path, password, X509KeyStorageFlags.UserKeySet);
if (!localCert.HasPrivateKey) if (!localCert.HasPrivateKey)
{ {
Logger.LogError("No private key included in SSL cert {CertificateLocation}.", path); Logger.LogError("No private key included in SSL cert {CertificateLocation}.", path);
@ -635,6 +639,7 @@ namespace Emby.Server.Implementations
BaseItem.ProviderManager = Resolve<IProviderManager>(); BaseItem.ProviderManager = Resolve<IProviderManager>();
BaseItem.LocalizationManager = Resolve<ILocalizationManager>(); BaseItem.LocalizationManager = Resolve<ILocalizationManager>();
BaseItem.ItemRepository = Resolve<IItemRepository>(); BaseItem.ItemRepository = Resolve<IItemRepository>();
BaseItem.ChapterRepository = Resolve<IChapterRepository>();
BaseItem.FileSystem = Resolve<IFileSystem>(); BaseItem.FileSystem = Resolve<IFileSystem>();
BaseItem.UserDataManager = Resolve<IUserDataManager>(); BaseItem.UserDataManager = Resolve<IUserDataManager>();
BaseItem.ChannelManager = Resolve<IChannelManager>(); BaseItem.ChannelManager = Resolve<IChannelManager>();

View File

@ -17,7 +17,6 @@ namespace Emby.Server.Implementations
{ DefaultRedirectKey, "web/" }, { DefaultRedirectKey, "web/" },
{ FfmpegProbeSizeKey, "1G" }, { FfmpegProbeSizeKey, "1G" },
{ FfmpegAnalyzeDurationKey, "200M" }, { FfmpegAnalyzeDurationKey, "200M" },
{ PlaylistsAllowDuplicatesKey, bool.FalseString },
{ BindToUnixSocketKey, bool.FalseString }, { BindToUnixSocketKey, bool.FalseString },
{ SqliteCacheSizeKey, "20000" }, { SqliteCacheSizeKey, "20000" },
{ FfmpegSkipValidationKey, bool.FalseString }, { FfmpegSkipValidationKey, bool.FalseString },

View File

@ -1,269 +0,0 @@
#nullable disable
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Threading;
using Jellyfin.Extensions;
using Microsoft.Data.Sqlite;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Data
{
public abstract class BaseSqliteRepository : IDisposable
{
private bool _disposed = false;
private SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1);
private SqliteConnection _writeConnection;
/// <summary>
/// Initializes a new instance of the <see cref="BaseSqliteRepository"/> class.
/// </summary>
/// <param name="logger">The logger.</param>
protected BaseSqliteRepository(ILogger<BaseSqliteRepository> logger)
{
Logger = logger;
}
/// <summary>
/// Gets or sets the path to the DB file.
/// </summary>
protected string DbFilePath { get; set; }
/// <summary>
/// Gets the logger.
/// </summary>
/// <value>The logger.</value>
protected ILogger<BaseSqliteRepository> Logger { get; }
/// <summary>
/// Gets the cache size.
/// </summary>
/// <value>The cache size or null.</value>
protected virtual int? CacheSize => null;
/// <summary>
/// Gets the locking mode. <see href="https://www.sqlite.org/pragma.html#pragma_locking_mode" />.
/// </summary>
protected virtual string LockingMode => "NORMAL";
/// <summary>
/// Gets the journal mode. <see href="https://www.sqlite.org/pragma.html#pragma_journal_mode" />.
/// </summary>
/// <value>The journal mode.</value>
protected virtual string JournalMode => "WAL";
/// <summary>
/// Gets the journal size limit. <see href="https://www.sqlite.org/pragma.html#pragma_journal_size_limit" />.
/// The default (-1) is overridden to prevent unconstrained WAL size, as reported by users.
/// </summary>
/// <value>The journal size limit.</value>
protected virtual int? JournalSizeLimit => 134_217_728; // 128MiB
/// <summary>
/// Gets the page size.
/// </summary>
/// <value>The page size or null.</value>
protected virtual int? PageSize => null;
/// <summary>
/// Gets the temp store mode.
/// </summary>
/// <value>The temp store mode.</value>
/// <see cref="TempStoreMode"/>
protected virtual TempStoreMode TempStore => TempStoreMode.Memory;
/// <summary>
/// Gets the synchronous mode.
/// </summary>
/// <value>The synchronous mode or null.</value>
/// <see cref="SynchronousMode"/>
protected virtual SynchronousMode? Synchronous => SynchronousMode.Normal;
public virtual void Initialize()
{
// Configuration and pragmas can affect VACUUM so it needs to be last.
using (var connection = GetConnection())
{
connection.Execute("VACUUM");
}
}
protected ManagedConnection GetConnection(bool readOnly = false)
{
if (!readOnly)
{
_writeLock.Wait();
if (_writeConnection is not null)
{
return new ManagedConnection(_writeConnection, _writeLock);
}
var writeConnection = new SqliteConnection($"Filename={DbFilePath};Pooling=False");
writeConnection.Open();
if (CacheSize.HasValue)
{
writeConnection.Execute("PRAGMA cache_size=" + CacheSize.Value);
}
if (!string.IsNullOrWhiteSpace(LockingMode))
{
writeConnection.Execute("PRAGMA locking_mode=" + LockingMode);
}
if (!string.IsNullOrWhiteSpace(JournalMode))
{
writeConnection.Execute("PRAGMA journal_mode=" + JournalMode);
}
if (JournalSizeLimit.HasValue)
{
writeConnection.Execute("PRAGMA journal_size_limit=" + JournalSizeLimit.Value);
}
if (Synchronous.HasValue)
{
writeConnection.Execute("PRAGMA synchronous=" + (int)Synchronous.Value);
}
if (PageSize.HasValue)
{
writeConnection.Execute("PRAGMA page_size=" + PageSize.Value);
}
writeConnection.Execute("PRAGMA temp_store=" + (int)TempStore);
return new ManagedConnection(_writeConnection = writeConnection, _writeLock);
}
var connection = new SqliteConnection($"Filename={DbFilePath};Mode=ReadOnly");
connection.Open();
if (CacheSize.HasValue)
{
connection.Execute("PRAGMA cache_size=" + CacheSize.Value);
}
if (!string.IsNullOrWhiteSpace(LockingMode))
{
connection.Execute("PRAGMA locking_mode=" + LockingMode);
}
if (!string.IsNullOrWhiteSpace(JournalMode))
{
connection.Execute("PRAGMA journal_mode=" + JournalMode);
}
if (JournalSizeLimit.HasValue)
{
connection.Execute("PRAGMA journal_size_limit=" + JournalSizeLimit.Value);
}
if (Synchronous.HasValue)
{
connection.Execute("PRAGMA synchronous=" + (int)Synchronous.Value);
}
if (PageSize.HasValue)
{
connection.Execute("PRAGMA page_size=" + PageSize.Value);
}
connection.Execute("PRAGMA temp_store=" + (int)TempStore);
return new ManagedConnection(connection, null);
}
public SqliteCommand PrepareStatement(ManagedConnection connection, string sql)
{
var command = connection.CreateCommand();
command.CommandText = sql;
return command;
}
protected bool TableExists(ManagedConnection connection, string name)
{
using var statement = PrepareStatement(connection, "select DISTINCT tbl_name from sqlite_master");
foreach (var row in statement.ExecuteQuery())
{
if (string.Equals(name, row.GetString(0), StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
protected List<string> GetColumnNames(ManagedConnection connection, string table)
{
var columnNames = new List<string>();
foreach (var row in connection.Query("PRAGMA table_info(" + table + ")"))
{
if (row.TryGetString(1, out var columnName))
{
columnNames.Add(columnName);
}
}
return columnNames;
}
protected void AddColumn(ManagedConnection connection, string table, string columnName, string type, List<string> existingColumnNames)
{
if (existingColumnNames.Contains(columnName, StringComparison.OrdinalIgnoreCase))
{
return;
}
connection.Execute("alter table " + table + " add column " + columnName + " " + type + " NULL");
}
protected void CheckDisposed()
{
ObjectDisposedException.ThrowIf(_disposed, this);
}
/// <inheritdoc />
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool dispose)
{
if (_disposed)
{
return;
}
if (dispose)
{
_writeLock.Wait();
try
{
_writeConnection.Dispose();
}
finally
{
_writeLock.Release();
}
_writeLock.Dispose();
}
_writeConnection = null;
_writeLock = null;
_disposed = true;
}
}
}

View File

@ -1,10 +1,13 @@
#pragma warning disable CS1591 #pragma warning disable CS1591
using System; using System;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Server.Implementations;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Data namespace Emby.Server.Implementations.Data
@ -13,20 +16,24 @@ namespace Emby.Server.Implementations.Data
{ {
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ILogger<CleanDatabaseScheduledTask> _logger; private readonly ILogger<CleanDatabaseScheduledTask> _logger;
private readonly IDbContextFactory<JellyfinDbContext> _dbProvider;
public CleanDatabaseScheduledTask(ILibraryManager libraryManager, ILogger<CleanDatabaseScheduledTask> logger) public CleanDatabaseScheduledTask(
ILibraryManager libraryManager,
ILogger<CleanDatabaseScheduledTask> logger,
IDbContextFactory<JellyfinDbContext> dbProvider)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
_logger = logger; _logger = logger;
_dbProvider = dbProvider;
} }
public Task Run(IProgress<double> progress, CancellationToken cancellationToken) public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{ {
CleanDeadItems(cancellationToken, progress); await CleanDeadItems(cancellationToken, progress).ConfigureAwait(false);
return Task.CompletedTask;
} }
private void CleanDeadItems(CancellationToken cancellationToken, IProgress<double> progress) private async Task CleanDeadItems(CancellationToken cancellationToken, IProgress<double> progress)
{ {
var itemIds = _libraryManager.GetItemIds(new InternalItemsQuery var itemIds = _libraryManager.GetItemIds(new InternalItemsQuery
{ {
@ -34,7 +41,7 @@ namespace Emby.Server.Implementations.Data
}); });
var numComplete = 0; var numComplete = 0;
var numItems = itemIds.Count; var numItems = itemIds.Count + 1;
_logger.LogDebug("Cleaning {0} items with dead parent links", numItems); _logger.LogDebug("Cleaning {0} items with dead parent links", numItems);
@ -60,6 +67,17 @@ namespace Emby.Server.Implementations.Data
progress.Report(percent * 100); progress.Report(percent * 100);
} }
var context = await _dbProvider.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
await using (context.ConfigureAwait(false))
{
var transaction = await context.Database.BeginTransactionAsync(cancellationToken).ConfigureAwait(false);
await using (transaction.ConfigureAwait(false))
{
await context.ItemValues.Where(e => e.BaseItemsMap!.Count == 0).ExecuteDeleteAsync(cancellationToken).ConfigureAwait(false);
await transaction.CommitAsync(cancellationToken).ConfigureAwait(false);
}
}
progress.Report(100); progress.Report(100);
} }
} }

View File

@ -0,0 +1,64 @@
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Threading.Channels;
using Emby.Server.Implementations.Playlists;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Playlists;
namespace Emby.Server.Implementations.Data;
/// <inheritdoc />
public class ItemTypeLookup : IItemTypeLookup
{
/// <inheritdoc />
public IReadOnlyList<string> MusicGenreTypes { get; } = [
typeof(Audio).FullName!,
typeof(MusicVideo).FullName!,
typeof(MusicAlbum).FullName!,
typeof(MusicArtist).FullName!,
];
/// <inheritdoc />
public IReadOnlyDictionary<BaseItemKind, string> BaseItemKindNames { get; } = new Dictionary<BaseItemKind, string>()
{
{ BaseItemKind.AggregateFolder, typeof(AggregateFolder).FullName! },
{ BaseItemKind.Audio, typeof(Audio).FullName! },
{ BaseItemKind.AudioBook, typeof(AudioBook).FullName! },
{ BaseItemKind.BasePluginFolder, typeof(BasePluginFolder).FullName! },
{ BaseItemKind.Book, typeof(Book).FullName! },
{ BaseItemKind.BoxSet, typeof(BoxSet).FullName! },
{ BaseItemKind.Channel, typeof(Channel).FullName! },
{ BaseItemKind.CollectionFolder, typeof(CollectionFolder).FullName! },
{ BaseItemKind.Episode, typeof(Episode).FullName! },
{ BaseItemKind.Folder, typeof(Folder).FullName! },
{ BaseItemKind.Genre, typeof(Genre).FullName! },
{ BaseItemKind.Movie, typeof(Movie).FullName! },
{ BaseItemKind.LiveTvChannel, typeof(LiveTvChannel).FullName! },
{ BaseItemKind.LiveTvProgram, typeof(LiveTvProgram).FullName! },
{ BaseItemKind.MusicAlbum, typeof(MusicAlbum).FullName! },
{ BaseItemKind.MusicArtist, typeof(MusicArtist).FullName! },
{ BaseItemKind.MusicGenre, typeof(MusicGenre).FullName! },
{ BaseItemKind.MusicVideo, typeof(MusicVideo).FullName! },
{ BaseItemKind.Person, typeof(Person).FullName! },
{ BaseItemKind.Photo, typeof(Photo).FullName! },
{ BaseItemKind.PhotoAlbum, typeof(PhotoAlbum).FullName! },
{ BaseItemKind.Playlist, typeof(Playlist).FullName! },
{ BaseItemKind.PlaylistsFolder, typeof(PlaylistsFolder).FullName! },
{ BaseItemKind.Season, typeof(Season).FullName! },
{ BaseItemKind.Series, typeof(Series).FullName! },
{ BaseItemKind.Studio, typeof(Studio).FullName! },
{ BaseItemKind.Trailer, typeof(Trailer).FullName! },
{ BaseItemKind.TvChannel, typeof(LiveTvChannel).FullName! },
{ BaseItemKind.TvProgram, typeof(LiveTvProgram).FullName! },
{ BaseItemKind.UserRootFolder, typeof(UserRootFolder).FullName! },
{ BaseItemKind.UserView, typeof(UserView).FullName! },
{ BaseItemKind.Video, typeof(Video).FullName! },
{ BaseItemKind.Year, typeof(Year).FullName! }
}.ToFrozenDictionary();
}

View File

@ -1,62 +0,0 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.Data.Sqlite;
namespace Emby.Server.Implementations.Data;
public sealed class ManagedConnection : IDisposable
{
private readonly SemaphoreSlim? _writeLock;
private SqliteConnection _db;
private bool _disposed = false;
public ManagedConnection(SqliteConnection db, SemaphoreSlim? writeLock)
{
_db = db;
_writeLock = writeLock;
}
public SqliteTransaction BeginTransaction()
=> _db.BeginTransaction();
public SqliteCommand CreateCommand()
=> _db.CreateCommand();
public void Execute(string commandText)
=> _db.Execute(commandText);
public SqliteCommand PrepareStatement(string sql)
=> _db.PrepareStatement(sql);
public IEnumerable<SqliteDataReader> Query(string commandText)
=> _db.Query(commandText);
public void Dispose()
{
if (_disposed)
{
return;
}
if (_writeLock is null)
{
// Read connections are managed with an internal pool
_db.Dispose();
}
else
{
// Write lock is managed by BaseSqliteRepository
// Don't dispose here
_writeLock.Release();
}
_db = null!;
_disposed = true;
}
}

View File

@ -127,9 +127,17 @@ namespace Emby.Server.Implementations.Data
return false; return false;
} }
try
{
result = reader.GetGuid(index); result = reader.GetGuid(index);
return true; return true;
} }
catch
{
result = Guid.Empty;
return false;
}
}
public static bool TryGetString(this SqliteDataReader reader, int index, out string result) public static bool TryGetString(this SqliteDataReader reader, int index, out string result)
{ {

File diff suppressed because it is too large Load Diff

View File

@ -1,369 +0,0 @@
#nullable disable
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using Microsoft.Data.Sqlite;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Data
{
public class SqliteUserDataRepository : BaseSqliteRepository, IUserDataRepository
{
private readonly IUserManager _userManager;
public SqliteUserDataRepository(
ILogger<SqliteUserDataRepository> logger,
IServerConfigurationManager config,
IUserManager userManager)
: base(logger)
{
_userManager = userManager;
DbFilePath = Path.Combine(config.ApplicationPaths.DataPath, "library.db");
}
/// <summary>
/// Opens the connection to the database.
/// </summary>
public override void Initialize()
{
base.Initialize();
using (var connection = GetConnection())
{
var userDatasTableExists = TableExists(connection, "UserDatas");
var userDataTableExists = TableExists(connection, "userdata");
var users = userDatasTableExists ? null : _userManager.Users;
using var transaction = connection.BeginTransaction();
connection.Execute(string.Join(
';',
"create table if not exists UserDatas (key nvarchar not null, userId INT not null, rating float null, played bit not null, playCount int not null, isFavorite bit not null, playbackPositionTicks bigint not null, lastPlayedDate datetime null, AudioStreamIndex INT, SubtitleStreamIndex INT)",
"drop index if exists idx_userdata",
"drop index if exists idx_userdata1",
"drop index if exists idx_userdata2",
"drop index if exists userdataindex1",
"drop index if exists userdataindex",
"drop index if exists userdataindex3",
"drop index if exists userdataindex4",
"create unique index if not exists UserDatasIndex1 on UserDatas (key, userId)",
"create index if not exists UserDatasIndex2 on UserDatas (key, userId, played)",
"create index if not exists UserDatasIndex3 on UserDatas (key, userId, playbackPositionTicks)",
"create index if not exists UserDatasIndex4 on UserDatas (key, userId, isFavorite)",
"create index if not exists UserDatasIndex5 on UserDatas (key, userId, lastPlayedDate)"));
if (!userDataTableExists)
{
transaction.Commit();
return;
}
var existingColumnNames = GetColumnNames(connection, "userdata");
AddColumn(connection, "userdata", "InternalUserId", "int", existingColumnNames);
AddColumn(connection, "userdata", "AudioStreamIndex", "int", existingColumnNames);
AddColumn(connection, "userdata", "SubtitleStreamIndex", "int", existingColumnNames);
if (userDatasTableExists)
{
return;
}
ImportUserIds(connection, users);
connection.Execute("INSERT INTO UserDatas (key, userId, rating, played, playCount, isFavorite, playbackPositionTicks, lastPlayedDate, AudioStreamIndex, SubtitleStreamIndex) SELECT key, InternalUserId, rating, played, playCount, isFavorite, playbackPositionTicks, lastPlayedDate, AudioStreamIndex, SubtitleStreamIndex from userdata where InternalUserId not null");
transaction.Commit();
}
}
private void ImportUserIds(ManagedConnection db, IEnumerable<User> users)
{
var userIdsWithUserData = GetAllUserIdsWithUserData(db);
using (var statement = db.PrepareStatement("update userdata set InternalUserId=@InternalUserId where UserId=@UserId"))
{
foreach (var user in users)
{
if (!userIdsWithUserData.Contains(user.Id))
{
continue;
}
statement.TryBind("@UserId", user.Id);
statement.TryBind("@InternalUserId", user.InternalId);
statement.ExecuteNonQuery();
}
}
}
private List<Guid> GetAllUserIdsWithUserData(ManagedConnection db)
{
var list = new List<Guid>();
using (var statement = PrepareStatement(db, "select DISTINCT UserId from UserData where UserId not null"))
{
foreach (var row in statement.ExecuteQuery())
{
try
{
list.Add(row.GetGuid(0));
}
catch (Exception ex)
{
Logger.LogError(ex, "Error while getting user");
}
}
}
return list;
}
/// <inheritdoc />
public void SaveUserData(long userId, string key, UserItemData userData, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(userData);
if (userId <= 0)
{
throw new ArgumentNullException(nameof(userId));
}
ArgumentException.ThrowIfNullOrEmpty(key);
PersistUserData(userId, key, userData, cancellationToken);
}
/// <inheritdoc />
public void SaveAllUserData(long userId, UserItemData[] userData, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(userData);
if (userId <= 0)
{
throw new ArgumentNullException(nameof(userId));
}
PersistAllUserData(userId, userData, cancellationToken);
}
/// <summary>
/// Persists the user data.
/// </summary>
/// <param name="internalUserId">The user id.</param>
/// <param name="key">The key.</param>
/// <param name="userData">The user data.</param>
/// <param name="cancellationToken">The cancellation token.</param>
public void PersistUserData(long internalUserId, string key, UserItemData userData, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
using (var connection = GetConnection())
using (var transaction = connection.BeginTransaction())
{
SaveUserData(connection, internalUserId, key, userData);
transaction.Commit();
}
}
private static void SaveUserData(ManagedConnection db, long internalUserId, string key, UserItemData userData)
{
using (var statement = db.PrepareStatement("replace into UserDatas (key, userId, rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex) values (@key, @userId, @rating,@played,@playCount,@isFavorite,@playbackPositionTicks,@lastPlayedDate,@AudioStreamIndex,@SubtitleStreamIndex)"))
{
statement.TryBind("@userId", internalUserId);
statement.TryBind("@key", key);
if (userData.Rating.HasValue)
{
statement.TryBind("@rating", userData.Rating.Value);
}
else
{
statement.TryBindNull("@rating");
}
statement.TryBind("@played", userData.Played);
statement.TryBind("@playCount", userData.PlayCount);
statement.TryBind("@isFavorite", userData.IsFavorite);
statement.TryBind("@playbackPositionTicks", userData.PlaybackPositionTicks);
if (userData.LastPlayedDate.HasValue)
{
statement.TryBind("@lastPlayedDate", userData.LastPlayedDate.Value.ToDateTimeParamValue());
}
else
{
statement.TryBindNull("@lastPlayedDate");
}
if (userData.AudioStreamIndex.HasValue)
{
statement.TryBind("@AudioStreamIndex", userData.AudioStreamIndex.Value);
}
else
{
statement.TryBindNull("@AudioStreamIndex");
}
if (userData.SubtitleStreamIndex.HasValue)
{
statement.TryBind("@SubtitleStreamIndex", userData.SubtitleStreamIndex.Value);
}
else
{
statement.TryBindNull("@SubtitleStreamIndex");
}
statement.ExecuteNonQuery();
}
}
/// <summary>
/// Persist all user data for the specified user.
/// </summary>
private void PersistAllUserData(long internalUserId, UserItemData[] userDataList, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
using (var connection = GetConnection())
using (var transaction = connection.BeginTransaction())
{
foreach (var userItemData in userDataList)
{
SaveUserData(connection, internalUserId, userItemData.Key, userItemData);
}
transaction.Commit();
}
}
/// <summary>
/// Gets the user data.
/// </summary>
/// <param name="userId">The user id.</param>
/// <param name="key">The key.</param>
/// <returns>Task{UserItemData}.</returns>
/// <exception cref="ArgumentNullException">
/// userId
/// or
/// key.
/// </exception>
public UserItemData GetUserData(long userId, string key)
{
if (userId <= 0)
{
throw new ArgumentNullException(nameof(userId));
}
ArgumentException.ThrowIfNullOrEmpty(key);
using (var connection = GetConnection(true))
{
using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from UserDatas where key =@Key and userId=@UserId"))
{
statement.TryBind("@UserId", userId);
statement.TryBind("@Key", key);
foreach (var row in statement.ExecuteQuery())
{
return ReadRow(row);
}
}
return null;
}
}
public UserItemData GetUserData(long userId, List<string> keys)
{
ArgumentNullException.ThrowIfNull(keys);
if (keys.Count == 0)
{
return null;
}
return GetUserData(userId, keys[0]);
}
/// <summary>
/// Return all user-data associated with the given user.
/// </summary>
/// <param name="userId">The internal user id.</param>
/// <returns>The list of user item data.</returns>
public List<UserItemData> GetAllUserData(long userId)
{
if (userId <= 0)
{
throw new ArgumentNullException(nameof(userId));
}
var list = new List<UserItemData>();
using (var connection = GetConnection())
{
using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from UserDatas where userId=@UserId"))
{
statement.TryBind("@UserId", userId);
foreach (var row in statement.ExecuteQuery())
{
list.Add(ReadRow(row));
}
}
}
return list;
}
/// <summary>
/// Read a row from the specified reader into the provided userData object.
/// </summary>
/// <param name="reader">The list of result set values.</param>
/// <returns>The user item data.</returns>
private UserItemData ReadRow(SqliteDataReader reader)
{
var userData = new UserItemData
{
Key = reader.GetString(0)
};
if (reader.TryGetDouble(2, out var rating))
{
userData.Rating = rating;
}
userData.Played = reader.GetBoolean(3);
userData.PlayCount = reader.GetInt32(4);
userData.IsFavorite = reader.GetBoolean(5);
userData.PlaybackPositionTicks = reader.GetInt64(6);
if (reader.TryReadDateTime(7, out var lastPlayedDate))
{
userData.LastPlayedDate = lastPlayedDate;
}
if (reader.TryGetInt32(8, out var audioStreamIndex))
{
userData.AudioStreamIndex = audioStreamIndex;
}
if (reader.TryGetInt32(9, out var subtitleStreamIndex))
{
userData.SubtitleStreamIndex = subtitleStreamIndex;
}
return userData;
}
}
}

View File

@ -1,30 +0,0 @@
namespace Emby.Server.Implementations.Data;
/// <summary>
/// The disk synchronization mode, controls how aggressively SQLite will write data
/// all the way out to physical storage.
/// </summary>
public enum SynchronousMode
{
/// <summary>
/// SQLite continues without syncing as soon as it has handed data off to the operating system.
/// </summary>
Off = 0,
/// <summary>
/// SQLite database engine will still sync at the most critical moments.
/// </summary>
Normal = 1,
/// <summary>
/// SQLite database engine will use the xSync method of the VFS
/// to ensure that all content is safely written to the disk surface prior to continuing.
/// </summary>
Full = 2,
/// <summary>
/// EXTRA synchronous is like FULL with the addition that the directory containing a rollback journal
/// is synced after that journal is unlinked to commit a transaction in DELETE mode.
/// </summary>
Extra = 3
}

View File

@ -1,23 +0,0 @@
namespace Emby.Server.Implementations.Data;
/// <summary>
/// Storage mode used by temporary database files.
/// </summary>
public enum TempStoreMode
{
/// <summary>
/// The compile-time C preprocessor macro SQLITE_TEMP_STORE
/// is used to determine where temporary tables and indices are stored.
/// </summary>
Default = 0,
/// <summary>
/// Temporary tables and indices are stored in a file.
/// </summary>
File = 1,
/// <summary>
/// Temporary tables and indices are kept in as if they were pure in-memory databases memory.
/// </summary>
Memory = 2
}

View File

@ -4,6 +4,7 @@ using System;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -13,7 +14,7 @@ namespace Emby.Server.Implementations.Devices
{ {
private readonly IApplicationPaths _appPaths; private readonly IApplicationPaths _appPaths;
private readonly ILogger<DeviceId> _logger; private readonly ILogger<DeviceId> _logger;
private readonly object _syncLock = new object(); private readonly Lock _syncLock = new();
private string? _id; private string? _id;

View File

@ -10,6 +10,7 @@ using Jellyfin.Data.Enums;
using Jellyfin.Extensions; using Jellyfin.Extensions;
using MediaBrowser.Common; using MediaBrowser.Common;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Chapters;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
@ -51,6 +52,7 @@ namespace Emby.Server.Implementations.Dto
private readonly Lazy<ILiveTvManager> _livetvManagerFactory; private readonly Lazy<ILiveTvManager> _livetvManagerFactory;
private readonly ITrickplayManager _trickplayManager; private readonly ITrickplayManager _trickplayManager;
private readonly IChapterRepository _chapterRepository;
public DtoService( public DtoService(
ILogger<DtoService> logger, ILogger<DtoService> logger,
@ -63,7 +65,8 @@ namespace Emby.Server.Implementations.Dto
IApplicationHost appHost, IApplicationHost appHost,
IMediaSourceManager mediaSourceManager, IMediaSourceManager mediaSourceManager,
Lazy<ILiveTvManager> livetvManagerFactory, Lazy<ILiveTvManager> livetvManagerFactory,
ITrickplayManager trickplayManager) ITrickplayManager trickplayManager,
IChapterRepository chapterRepository)
{ {
_logger = logger; _logger = logger;
_libraryManager = libraryManager; _libraryManager = libraryManager;
@ -76,6 +79,7 @@ namespace Emby.Server.Implementations.Dto
_mediaSourceManager = mediaSourceManager; _mediaSourceManager = mediaSourceManager;
_livetvManagerFactory = livetvManagerFactory; _livetvManagerFactory = livetvManagerFactory;
_trickplayManager = trickplayManager; _trickplayManager = trickplayManager;
_chapterRepository = chapterRepository;
} }
private ILiveTvManager LivetvManager => _livetvManagerFactory.Value; private ILiveTvManager LivetvManager => _livetvManagerFactory.Value;
@ -165,7 +169,7 @@ namespace Emby.Server.Implementations.Dto
return dto; return dto;
} }
private static IList<BaseItem> GetTaggedItems(IItemByName byName, User? user, DtoOptions options) private static IReadOnlyList<BaseItem> GetTaggedItems(IItemByName byName, User? user, DtoOptions options)
{ {
return byName.GetTaggedItems( return byName.GetTaggedItems(
new InternalItemsQuery(user) new InternalItemsQuery(user)
@ -327,7 +331,7 @@ namespace Emby.Server.Implementations.Dto
return dto; return dto;
} }
private static void SetItemByNameInfo(BaseItem item, BaseItemDto dto, IList<BaseItem> taggedItems) private static void SetItemByNameInfo(BaseItem item, BaseItemDto dto, IReadOnlyList<BaseItem> taggedItems)
{ {
if (item is MusicArtist) if (item is MusicArtist)
{ {
@ -1060,7 +1064,7 @@ namespace Emby.Server.Implementations.Dto
if (options.ContainsField(ItemFields.Chapters)) if (options.ContainsField(ItemFields.Chapters))
{ {
dto.Chapters = _itemRepo.GetChapters(item); dto.Chapters = _chapterRepository.GetChapters(item.Id).ToList();
} }
if (options.ContainsField(ItemFields.Trickplay)) if (options.ContainsField(ItemFields.Trickplay))

View File

@ -37,7 +37,7 @@
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup> </PropertyGroup>

View File

@ -34,7 +34,7 @@ public sealed class LibraryChangedNotifier : IHostedService, IDisposable
private readonly IUserManager _userManager; private readonly IUserManager _userManager;
private readonly ILogger<LibraryChangedNotifier> _logger; private readonly ILogger<LibraryChangedNotifier> _logger;
private readonly object _libraryChangedSyncLock = new(); private readonly Lock _libraryChangedSyncLock = new();
private readonly List<Folder> _foldersAddedTo = new(); private readonly List<Folder> _foldersAddedTo = new();
private readonly List<Folder> _foldersRemovedFrom = new(); private readonly List<Folder> _foldersRemovedFrom = new();
private readonly List<BaseItem> _itemsAdded = new(); private readonly List<BaseItem> _itemsAdded = new();

View File

@ -24,7 +24,7 @@ namespace Emby.Server.Implementations.EntryPoints
private readonly IUserManager _userManager; private readonly IUserManager _userManager;
private readonly Dictionary<Guid, List<BaseItem>> _changedItems = new(); private readonly Dictionary<Guid, List<BaseItem>> _changedItems = new();
private readonly object _syncLock = new(); private readonly Lock _syncLock = new();
private Timer? _updateTimer; private Timer? _updateTimer;
@ -144,9 +144,15 @@ namespace Emby.Server.Implementations.EntryPoints
.Select(i => .Select(i =>
{ {
var dto = _userDataManager.GetUserDataDto(i, user); var dto = _userDataManager.GetUserDataDto(i, user);
if (dto is null)
{
return null!;
}
dto.ItemId = i.Id; dto.ItemId = i.Id;
return dto; return dto;
}) })
.Where(e => e is not null)
.ToArray() .ToArray()
}; };
} }

View File

@ -82,17 +82,17 @@ namespace Emby.Server.Implementations.HttpServer
public WebSocketState State => _socket.State; public WebSocketState State => _socket.State;
/// <inheritdoc /> /// <inheritdoc />
public Task SendAsync(OutboundWebSocketMessage message, CancellationToken cancellationToken) public async Task SendAsync(OutboundWebSocketMessage message, CancellationToken cancellationToken)
{ {
var json = JsonSerializer.SerializeToUtf8Bytes(message, _jsonOptions); var json = JsonSerializer.SerializeToUtf8Bytes(message, _jsonOptions);
return _socket.SendAsync(json, WebSocketMessageType.Text, true, cancellationToken); await _socket.SendAsync(json, WebSocketMessageType.Text, true, cancellationToken).ConfigureAwait(false);
} }
/// <inheritdoc /> /// <inheritdoc />
public Task SendAsync<T>(OutboundWebSocketMessage<T> message, CancellationToken cancellationToken) public async Task SendAsync<T>(OutboundWebSocketMessage<T> message, CancellationToken cancellationToken)
{ {
var json = JsonSerializer.SerializeToUtf8Bytes(message, _jsonOptions); var json = JsonSerializer.SerializeToUtf8Bytes(message, _jsonOptions);
return _socket.SendAsync(json, WebSocketMessageType.Text, true, cancellationToken); await _socket.SendAsync(json, WebSocketMessageType.Text, true, cancellationToken).ConfigureAwait(false);
} }
/// <inheritdoc /> /// <inheritdoc />
@ -224,12 +224,12 @@ namespace Emby.Server.Implementations.HttpServer
return ret; return ret;
} }
private Task SendKeepAliveResponse() private async Task SendKeepAliveResponse()
{ {
LastKeepAliveDate = DateTime.UtcNow; LastKeepAliveDate = DateTime.UtcNow;
return SendAsync( await SendAsync(
new OutboundKeepAliveMessage(), new OutboundKeepAliveMessage(),
CancellationToken.None); CancellationToken.None).ConfigureAwait(false);
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@ -84,7 +84,7 @@ namespace Emby.Server.Implementations.HttpServer
/// Processes the web socket message received. /// Processes the web socket message received.
/// </summary> /// </summary>
/// <param name="result">The result.</param> /// <param name="result">The result.</param>
private Task ProcessWebSocketMessageReceived(WebSocketMessageInfo result) private async Task ProcessWebSocketMessageReceived(WebSocketMessageInfo result)
{ {
var tasks = new Task[_webSocketListeners.Length]; var tasks = new Task[_webSocketListeners.Length];
for (var i = 0; i < _webSocketListeners.Length; ++i) for (var i = 0; i < _webSocketListeners.Length; ++i)
@ -92,7 +92,7 @@ namespace Emby.Server.Implementations.HttpServer
tasks[i] = _webSocketListeners[i].ProcessMessageAsync(result); tasks[i] = _webSocketListeners[i].ProcessMessageAsync(result);
} }
return Task.WhenAll(tasks); await Task.WhenAll(tasks).ConfigureAwait(false);
} }
} }
} }

View File

@ -18,8 +18,8 @@ namespace Emby.Server.Implementations.IO
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly IServerConfigurationManager _configurationManager; private readonly IServerConfigurationManager _configurationManager;
private readonly List<string> _affectedPaths = new List<string>(); private readonly List<string> _affectedPaths = new();
private readonly object _timerLock = new object(); private readonly Lock _timerLock = new();
private Timer? _timer; private Timer? _timer;
private bool _disposed; private bool _disposed;

View File

@ -276,6 +276,13 @@ namespace Emby.Server.Implementations.IO
{ {
_logger.LogError(ex, "Reading the file at {Path} failed due to a permissions exception.", fileInfo.FullName); _logger.LogError(ex, "Reading the file at {Path} failed due to a permissions exception.", fileInfo.FullName);
} }
catch (IOException ex)
{
// IOException generally means the file is not accessible due to filesystem issues
// Catch this exception and mark the file as not exist to ignore it
_logger.LogError(ex, "Reading the file at {Path} failed due to an IO Exception. Marking the file as not existing", fileInfo.FullName);
result.Exists = false;
}
} }
} }
@ -561,7 +568,7 @@ namespace Emby.Server.Implementations.IO
{ {
var enumerationOptions = GetEnumerationOptions(recursive); var enumerationOptions = GetEnumerationOptions(recursive);
// On linux and osx the search pattern is case sensitive // On linux and macOS the search pattern is case-sensitive
// If we're OK with case-sensitivity, and we're only filtering for one extension, then use the native method // If we're OK with case-sensitivity, and we're only filtering for one extension, then use the native method
if ((enableCaseSensitiveExtensions || _isEnvironmentCaseInsensitive) && extensions is not null && extensions.Count == 1) if ((enableCaseSensitiveExtensions || _isEnvironmentCaseInsensitive) && extensions is not null && extensions.Count == 1)
{ {
@ -590,6 +597,9 @@ namespace Emby.Server.Implementations.IO
/// <inheritdoc /> /// <inheritdoc />
public virtual IEnumerable<FileSystemMetadata> GetFileSystemEntries(string path, bool recursive = false) public virtual IEnumerable<FileSystemMetadata> GetFileSystemEntries(string path, bool recursive = false)
{ {
// Note: any of unhandled exceptions thrown by this method may cause the caller to believe the whole path is not accessible.
// But what causing the exception may be a single file under that path. This could lead to unexpected behavior.
// For example, the scanner will remove everything in that path due to unhandled errors.
var directoryInfo = new DirectoryInfo(path); var directoryInfo = new DirectoryInfo(path);
var enumerationOptions = GetEnumerationOptions(recursive); var enumerationOptions = GetEnumerationOptions(recursive);
@ -618,7 +628,7 @@ namespace Emby.Server.Implementations.IO
{ {
var enumerationOptions = GetEnumerationOptions(recursive); var enumerationOptions = GetEnumerationOptions(recursive);
// On linux and osx the search pattern is case sensitive // On linux and macOS the search pattern is case-sensitive
// If we're OK with case-sensitivity, and we're only filtering for one extension, then use the native method // If we're OK with case-sensitivity, and we're only filtering for one extension, then use the native method
if ((enableCaseSensitiveExtensions || _isEnvironmentCaseInsensitive) && extensions is not null && extensions.Length == 1) if ((enableCaseSensitiveExtensions || _isEnvironmentCaseInsensitive) && extensions is not null && extensions.Length == 1)
{ {

View File

@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Mime;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
@ -116,13 +117,12 @@ namespace Emby.Server.Implementations.Images
var mimeType = MimeTypes.GetMimeType(outputPath); var mimeType = MimeTypes.GetMimeType(outputPath);
if (string.Equals(mimeType, "application/octet-stream", StringComparison.OrdinalIgnoreCase)) if (string.Equals(mimeType, MediaTypeNames.Application.Octet, StringComparison.OrdinalIgnoreCase))
{ {
mimeType = "image/png"; mimeType = MediaTypeNames.Image.Png;
} }
await ProviderManager.SaveImage(item, outputPath, mimeType, imageType, null, false, cancellationToken).ConfigureAwait(false); await ProviderManager.SaveImage(item, outputPath, mimeType, imageType, null, false, cancellationToken).ConfigureAwait(false);
File.Delete(outputPath);
return ItemUpdateType.ImageUpdate; return ItemUpdateType.ImageUpdate;
} }

View File

@ -76,13 +76,14 @@ namespace Emby.Server.Implementations.Library
private readonly IItemRepository _itemRepository; private readonly IItemRepository _itemRepository;
private readonly IImageProcessor _imageProcessor; private readonly IImageProcessor _imageProcessor;
private readonly NamingOptions _namingOptions; private readonly NamingOptions _namingOptions;
private readonly IPeopleRepository _peopleRepository;
private readonly ExtraResolver _extraResolver; private readonly ExtraResolver _extraResolver;
/// <summary> /// <summary>
/// The _root folder sync lock. /// The _root folder sync lock.
/// </summary> /// </summary>
private readonly object _rootFolderSyncLock = new object(); private readonly Lock _rootFolderSyncLock = new();
private readonly object _userRootFolderSyncLock = new object(); private readonly Lock _userRootFolderSyncLock = new();
private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromHours(24); private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromHours(24);
@ -112,6 +113,7 @@ namespace Emby.Server.Implementations.Library
/// <param name="imageProcessor">The image processor.</param> /// <param name="imageProcessor">The image processor.</param>
/// <param name="namingOptions">The naming options.</param> /// <param name="namingOptions">The naming options.</param>
/// <param name="directoryService">The directory service.</param> /// <param name="directoryService">The directory service.</param>
/// <param name="peopleRepository">The People Repository.</param>
public LibraryManager( public LibraryManager(
IServerApplicationHost appHost, IServerApplicationHost appHost,
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
@ -127,7 +129,8 @@ namespace Emby.Server.Implementations.Library
IItemRepository itemRepository, IItemRepository itemRepository,
IImageProcessor imageProcessor, IImageProcessor imageProcessor,
NamingOptions namingOptions, NamingOptions namingOptions,
IDirectoryService directoryService) IDirectoryService directoryService,
IPeopleRepository peopleRepository)
{ {
_appHost = appHost; _appHost = appHost;
_logger = loggerFactory.CreateLogger<LibraryManager>(); _logger = loggerFactory.CreateLogger<LibraryManager>();
@ -144,7 +147,7 @@ namespace Emby.Server.Implementations.Library
_imageProcessor = imageProcessor; _imageProcessor = imageProcessor;
_cache = new ConcurrentDictionary<Guid, BaseItem>(); _cache = new ConcurrentDictionary<Guid, BaseItem>();
_namingOptions = namingOptions; _namingOptions = namingOptions;
_peopleRepository = peopleRepository;
_extraResolver = new ExtraResolver(loggerFactory.CreateLogger<ExtraResolver>(), namingOptions, directoryService); _extraResolver = new ExtraResolver(loggerFactory.CreateLogger<ExtraResolver>(), namingOptions, directoryService);
_configurationManager.ConfigurationUpdated += ConfigurationUpdated; _configurationManager.ConfigurationUpdated += ConfigurationUpdated;
@ -750,16 +753,9 @@ namespace Emby.Server.Implementations.Library
}; };
if (folder.Id.IsEmpty()) if (folder.Id.IsEmpty())
{
if (string.IsNullOrEmpty(folder.Path))
{
folder.Id = GetNewItemId(folder.GetType().Name, folder.GetType());
}
else
{ {
folder.Id = GetNewItemId(folder.Path, folder.GetType()); folder.Id = GetNewItemId(folder.Path, folder.GetType());
} }
}
var dbItem = GetItemById(folder.Id) as BasePluginFolder; var dbItem = GetItemById(folder.Id) as BasePluginFolder;
@ -1053,9 +1049,17 @@ namespace Emby.Server.Implementations.Library
cancellationToken: cancellationToken).ConfigureAwait(false); cancellationToken: cancellationToken).ConfigureAwait(false);
// Quickly scan CollectionFolders for changes // Quickly scan CollectionFolders for changes
foreach (var folder in GetUserRootFolder().Children.OfType<Folder>()) foreach (var child in GetUserRootFolder().Children.OfType<Folder>())
{ {
await folder.RefreshMetadata(cancellationToken).ConfigureAwait(false); // If the user has somehow deleted the collection directory, remove the metadata from the database.
if (child is CollectionFolder collectionFolder && !Directory.Exists(collectionFolder.Path))
{
_itemRepository.DeleteItem(collectionFolder.Id);
}
else
{
await child.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
} }
} }
@ -1274,7 +1278,7 @@ namespace Emby.Server.Implementations.Library
return ItemIsVisible(item, user) ? item : null; return ItemIsVisible(item, user) ? item : null;
} }
public List<BaseItem> GetItemList(InternalItemsQuery query, bool allowExternalContent) public IReadOnlyList<BaseItem> GetItemList(InternalItemsQuery query, bool allowExternalContent)
{ {
if (query.Recursive && !query.ParentId.IsEmpty()) if (query.Recursive && !query.ParentId.IsEmpty())
{ {
@ -1300,7 +1304,7 @@ namespace Emby.Server.Implementations.Library
return itemList; return itemList;
} }
public List<BaseItem> GetItemList(InternalItemsQuery query) public IReadOnlyList<BaseItem> GetItemList(InternalItemsQuery query)
{ {
return GetItemList(query, true); return GetItemList(query, true);
} }
@ -1324,7 +1328,7 @@ namespace Emby.Server.Implementations.Library
return _itemRepository.GetCount(query); return _itemRepository.GetCount(query);
} }
public List<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents) public IReadOnlyList<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents)
{ {
SetTopParentIdsOrAncestors(query, parents); SetTopParentIdsOrAncestors(query, parents);
@ -1357,7 +1361,7 @@ namespace Emby.Server.Implementations.Library
_itemRepository.GetItemList(query)); _itemRepository.GetItemList(query));
} }
public List<Guid> GetItemIds(InternalItemsQuery query) public IReadOnlyList<Guid> GetItemIds(InternalItemsQuery query)
{ {
if (query.User is not null) if (query.User is not null)
{ {
@ -1955,13 +1959,13 @@ namespace Emby.Server.Implementations.Library
/// <inheritdoc /> /// <inheritdoc />
public async Task UpdateItemsAsync(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken) public async Task UpdateItemsAsync(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
{ {
_itemRepository.SaveItems(items, cancellationToken);
foreach (var item in items) foreach (var item in items)
{ {
await RunMetadataSavers(item, updateReason).ConfigureAwait(false); await RunMetadataSavers(item, updateReason).ConfigureAwait(false);
} }
_itemRepository.SaveItems(items, cancellationToken);
if (ItemUpdated is not null) if (ItemUpdated is not null)
{ {
foreach (var item in items) foreach (var item in items)
@ -2736,12 +2740,12 @@ namespace Emby.Server.Implementations.Library
return path; return path;
} }
public List<PersonInfo> GetPeople(InternalPeopleQuery query) public IReadOnlyList<PersonInfo> GetPeople(InternalPeopleQuery query)
{ {
return _itemRepository.GetPeople(query); return _peopleRepository.GetPeople(query);
} }
public List<PersonInfo> GetPeople(BaseItem item) public IReadOnlyList<PersonInfo> GetPeople(BaseItem item)
{ {
if (item.SupportsPeople) if (item.SupportsPeople)
{ {
@ -2756,12 +2760,12 @@ namespace Emby.Server.Implementations.Library
} }
} }
return new List<PersonInfo>(); return [];
} }
public List<Person> GetPeopleItems(InternalPeopleQuery query) public IReadOnlyList<Person> GetPeopleItems(InternalPeopleQuery query)
{ {
return _itemRepository.GetPeopleNames(query) return _peopleRepository.GetPeopleNames(query)
.Select(i => .Select(i =>
{ {
try try
@ -2779,9 +2783,9 @@ namespace Emby.Server.Implementations.Library
.ToList()!; // null values are filtered out .ToList()!; // null values are filtered out
} }
public List<string> GetPeopleNames(InternalPeopleQuery query) public IReadOnlyList<string> GetPeopleNames(InternalPeopleQuery query)
{ {
return _itemRepository.GetPeopleNames(query); return _peopleRepository.GetPeopleNames(query);
} }
public void UpdatePeople(BaseItem item, List<PersonInfo> people) public void UpdatePeople(BaseItem item, List<PersonInfo> people)
@ -2790,16 +2794,17 @@ namespace Emby.Server.Implementations.Library
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task UpdatePeopleAsync(BaseItem item, List<PersonInfo> people, CancellationToken cancellationToken) public async Task UpdatePeopleAsync(BaseItem item, IReadOnlyList<PersonInfo> people, CancellationToken cancellationToken)
{ {
if (!item.SupportsPeople) if (!item.SupportsPeople)
{ {
return; return;
} }
_itemRepository.UpdatePeople(item.Id, people);
if (people is not null) if (people is not null)
{ {
people = people.Where(e => e is not null).ToArray();
_peopleRepository.UpdatePeople(item.Id, people);
await SavePeopleMetadataAsync(people, cancellationToken).ConfigureAwait(false); await SavePeopleMetadataAsync(people, cancellationToken).ConfigureAwait(false);
} }
} }
@ -2914,14 +2919,13 @@ namespace Emby.Server.Implementations.Library
private async Task SavePeopleMetadataAsync(IEnumerable<PersonInfo> people, CancellationToken cancellationToken) private async Task SavePeopleMetadataAsync(IEnumerable<PersonInfo> people, CancellationToken cancellationToken)
{ {
List<BaseItem>? personsToSave = null;
foreach (var person in people) foreach (var person in people)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
var itemUpdateType = ItemUpdateType.MetadataDownload; var itemUpdateType = ItemUpdateType.MetadataDownload;
var saveEntity = false; var saveEntity = false;
var createEntity = false;
var personEntity = GetPerson(person.Name); var personEntity = GetPerson(person.Name);
if (personEntity is null) if (personEntity is null)
@ -2938,6 +2942,7 @@ namespace Emby.Server.Implementations.Library
personEntity.PresentationUniqueKey = personEntity.CreatePresentationUniqueKey(); personEntity.PresentationUniqueKey = personEntity.CreatePresentationUniqueKey();
saveEntity = true; saveEntity = true;
createEntity = true;
} }
foreach (var id in person.ProviderIds) foreach (var id in person.ProviderIds)
@ -2965,14 +2970,14 @@ namespace Emby.Server.Implementations.Library
if (saveEntity) if (saveEntity)
{ {
(personsToSave ??= new()).Add(personEntity); if (createEntity)
await RunMetadataSavers(personEntity, itemUpdateType).ConfigureAwait(false); {
} CreateItems([personEntity], null, CancellationToken.None);
} }
if (personsToSave is not null) await RunMetadataSavers(personEntity, itemUpdateType).ConfigureAwait(false);
{ CreateItems([personEntity], null, CancellationToken.None);
CreateItems(personsToSave, null, CancellationToken.None); }
} }
} }

View File

@ -5,6 +5,7 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -38,7 +39,7 @@ namespace Emby.Server.Implementations.Library
public class MediaSourceManager : IMediaSourceManager, IDisposable public class MediaSourceManager : IMediaSourceManager, IDisposable
{ {
// Do not use a pipe here because Roku http requests to the server will fail, without any explicit error message. // Do not use a pipe here because Roku http requests to the server will fail, without any explicit error message.
private const char LiveStreamIdDelimeter = '_'; private const char LiveStreamIdDelimiter = '_';
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
private readonly IItemRepository _itemRepo; private readonly IItemRepository _itemRepo;
@ -51,7 +52,8 @@ namespace Emby.Server.Implementations.Library
private readonly ILocalizationManager _localizationManager; private readonly ILocalizationManager _localizationManager;
private readonly IApplicationPaths _appPaths; private readonly IApplicationPaths _appPaths;
private readonly IDirectoryService _directoryService; private readonly IDirectoryService _directoryService;
private readonly IMediaStreamRepository _mediaStreamRepository;
private readonly IMediaAttachmentRepository _mediaAttachmentRepository;
private readonly ConcurrentDictionary<string, ILiveStream> _openStreams = new ConcurrentDictionary<string, ILiveStream>(StringComparer.OrdinalIgnoreCase); private readonly ConcurrentDictionary<string, ILiveStream> _openStreams = new ConcurrentDictionary<string, ILiveStream>(StringComparer.OrdinalIgnoreCase);
private readonly AsyncNonKeyedLocker _liveStreamLocker = new(1); private readonly AsyncNonKeyedLocker _liveStreamLocker = new(1);
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
@ -69,7 +71,9 @@ namespace Emby.Server.Implementations.Library
IFileSystem fileSystem, IFileSystem fileSystem,
IUserDataManager userDataManager, IUserDataManager userDataManager,
IMediaEncoder mediaEncoder, IMediaEncoder mediaEncoder,
IDirectoryService directoryService) IDirectoryService directoryService,
IMediaStreamRepository mediaStreamRepository,
IMediaAttachmentRepository mediaAttachmentRepository)
{ {
_appHost = appHost; _appHost = appHost;
_itemRepo = itemRepo; _itemRepo = itemRepo;
@ -82,6 +86,8 @@ namespace Emby.Server.Implementations.Library
_localizationManager = localizationManager; _localizationManager = localizationManager;
_appPaths = applicationPaths; _appPaths = applicationPaths;
_directoryService = directoryService; _directoryService = directoryService;
_mediaStreamRepository = mediaStreamRepository;
_mediaAttachmentRepository = mediaAttachmentRepository;
} }
public void AddParts(IEnumerable<IMediaSourceProvider> providers) public void AddParts(IEnumerable<IMediaSourceProvider> providers)
@ -89,9 +95,9 @@ namespace Emby.Server.Implementations.Library
_providers = providers.ToArray(); _providers = providers.ToArray();
} }
public List<MediaStream> GetMediaStreams(MediaStreamQuery query) public IReadOnlyList<MediaStream> GetMediaStreams(MediaStreamQuery query)
{ {
var list = _itemRepo.GetMediaStreams(query); var list = _mediaStreamRepository.GetMediaStreams(query);
foreach (var stream in list) foreach (var stream in list)
{ {
@ -121,7 +127,7 @@ namespace Emby.Server.Implementations.Library
return false; return false;
} }
public List<MediaStream> GetMediaStreams(Guid itemId) public IReadOnlyList<MediaStream> GetMediaStreams(Guid itemId)
{ {
var list = GetMediaStreams(new MediaStreamQuery var list = GetMediaStreams(new MediaStreamQuery
{ {
@ -131,7 +137,7 @@ namespace Emby.Server.Implementations.Library
return GetMediaStreamsForItem(list); return GetMediaStreamsForItem(list);
} }
private List<MediaStream> GetMediaStreamsForItem(List<MediaStream> streams) private IReadOnlyList<MediaStream> GetMediaStreamsForItem(IReadOnlyList<MediaStream> streams)
{ {
foreach (var stream in streams) foreach (var stream in streams)
{ {
@ -145,13 +151,13 @@ namespace Emby.Server.Implementations.Library
} }
/// <inheritdoc /> /// <inheritdoc />
public List<MediaAttachment> GetMediaAttachments(MediaAttachmentQuery query) public IReadOnlyList<MediaAttachment> GetMediaAttachments(MediaAttachmentQuery query)
{ {
return _itemRepo.GetMediaAttachments(query); return _mediaAttachmentRepository.GetMediaAttachments(query);
} }
/// <inheritdoc /> /// <inheritdoc />
public List<MediaAttachment> GetMediaAttachments(Guid itemId) public IReadOnlyList<MediaAttachment> GetMediaAttachments(Guid itemId)
{ {
return GetMediaAttachments(new MediaAttachmentQuery return GetMediaAttachments(new MediaAttachmentQuery
{ {
@ -159,7 +165,7 @@ namespace Emby.Server.Implementations.Library
}); });
} }
public async Task<List<MediaSourceInfo>> GetPlaybackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken) public async Task<IReadOnlyList<MediaSourceInfo>> GetPlaybackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken)
{ {
var mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user); var mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user);
@ -212,7 +218,7 @@ namespace Emby.Server.Implementations.Library
list.Add(source); list.Add(source);
} }
return SortMediaSources(list); return SortMediaSources(list).ToArray();
} }
/// <inheritdoc />> /// <inheritdoc />>
@ -307,7 +313,7 @@ namespace Emby.Server.Implementations.Library
private static void SetKeyProperties(IMediaSourceProvider provider, MediaSourceInfo mediaSource) private static void SetKeyProperties(IMediaSourceProvider provider, MediaSourceInfo mediaSource)
{ {
var prefix = provider.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture) + LiveStreamIdDelimeter; var prefix = provider.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture) + LiveStreamIdDelimiter;
if (!string.IsNullOrEmpty(mediaSource.OpenToken) && !mediaSource.OpenToken.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) if (!string.IsNullOrEmpty(mediaSource.OpenToken) && !mediaSource.OpenToken.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
{ {
@ -332,7 +338,7 @@ namespace Emby.Server.Implementations.Library
return sources.FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); return sources.FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));
} }
public List<MediaSourceInfo> GetStaticMediaSources(BaseItem item, bool enablePathSubstitution, User user = null) public IReadOnlyList<MediaSourceInfo> GetStaticMediaSources(BaseItem item, bool enablePathSubstitution, User user = null)
{ {
ArgumentNullException.ThrowIfNull(item); ArgumentNullException.ThrowIfNull(item);
@ -453,7 +459,7 @@ namespace Emby.Server.Implementations.Library
} }
} }
private static List<MediaSourceInfo> SortMediaSources(IEnumerable<MediaSourceInfo> sources) private static IEnumerable<MediaSourceInfo> SortMediaSources(IEnumerable<MediaSourceInfo> sources)
{ {
return sources.OrderBy(i => return sources.OrderBy(i =>
{ {
@ -470,8 +476,7 @@ namespace Emby.Server.Implementations.Library
return stream?.Width ?? 0; return stream?.Width ?? 0;
}) })
.Where(i => i.Type != MediaSourceType.Placeholder) .Where(i => i.Type != MediaSourceType.Placeholder);
.ToList();
} }
public async Task<Tuple<LiveStreamResponse, IDirectStreamProvider>> OpenLiveStreamInternal(LiveStreamRequest request, CancellationToken cancellationToken) public async Task<Tuple<LiveStreamResponse, IDirectStreamProvider>> OpenLiveStreamInternal(LiveStreamRequest request, CancellationToken cancellationToken)
@ -806,7 +811,7 @@ namespace Emby.Server.Implementations.Library
return result.Item1; return result.Item1;
} }
public async Task<List<MediaSourceInfo>> GetRecordingStreamMediaSources(ActiveRecordingInfo info, CancellationToken cancellationToken) public async Task<IReadOnlyList<MediaSourceInfo>> GetRecordingStreamMediaSources(ActiveRecordingInfo info, CancellationToken cancellationToken)
{ {
var stream = new MediaSourceInfo var stream = new MediaSourceInfo
{ {
@ -829,10 +834,7 @@ namespace Emby.Server.Implementations.Library
await new LiveStreamHelper(_mediaEncoder, _logger, _appPaths) await new LiveStreamHelper(_mediaEncoder, _logger, _appPaths)
.AddMediaInfoWithProbe(stream, false, false, cancellationToken).ConfigureAwait(false); .AddMediaInfoWithProbe(stream, false, false, cancellationToken).ConfigureAwait(false);
return new List<MediaSourceInfo> return [stream];
{
stream
};
} }
public async Task CloseLiveStream(string id) public async Task CloseLiveStream(string id)
@ -864,11 +866,11 @@ namespace Emby.Server.Implementations.Library
{ {
ArgumentException.ThrowIfNullOrEmpty(key); ArgumentException.ThrowIfNullOrEmpty(key);
var keys = key.Split(LiveStreamIdDelimeter, 2); var keys = key.Split(LiveStreamIdDelimiter, 2);
var provider = _providers.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture), keys[0], StringComparison.OrdinalIgnoreCase)); var provider = _providers.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture), keys[0], StringComparison.OrdinalIgnoreCase));
var splitIndex = key.IndexOf(LiveStreamIdDelimeter, StringComparison.Ordinal); var splitIndex = key.IndexOf(LiveStreamIdDelimiter, StringComparison.Ordinal);
var keyId = key.Substring(splitIndex + 1); var keyId = key.Substring(splitIndex + 1);
return (provider, keyId); return (provider, keyId);

View File

@ -2,6 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using Jellyfin.Data.Entities; using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
@ -24,30 +25,23 @@ namespace Emby.Server.Implementations.Library
_libraryManager = libraryManager; _libraryManager = libraryManager;
} }
public List<BaseItem> GetInstantMixFromSong(Audio item, User? user, DtoOptions dtoOptions) public IReadOnlyList<BaseItem> GetInstantMixFromSong(Audio item, User? user, DtoOptions dtoOptions)
{
var list = new List<BaseItem>
{
item
};
list.AddRange(GetInstantMixFromGenres(item.Genres, user, dtoOptions));
return list;
}
/// <inheritdoc />
public List<BaseItem> GetInstantMixFromArtist(MusicArtist artist, User? user, DtoOptions dtoOptions)
{
return GetInstantMixFromGenres(artist.Genres, user, dtoOptions);
}
public List<BaseItem> GetInstantMixFromAlbum(MusicAlbum item, User? user, DtoOptions dtoOptions)
{ {
return GetInstantMixFromGenres(item.Genres, user, dtoOptions); return GetInstantMixFromGenres(item.Genres, user, dtoOptions);
} }
public List<BaseItem> GetInstantMixFromFolder(Folder item, User? user, DtoOptions dtoOptions) /// <inheritdoc />
public IReadOnlyList<BaseItem> GetInstantMixFromArtist(MusicArtist artist, User? user, DtoOptions dtoOptions)
{
return GetInstantMixFromGenres(artist.Genres, user, dtoOptions);
}
public IReadOnlyList<BaseItem> GetInstantMixFromAlbum(MusicAlbum item, User? user, DtoOptions dtoOptions)
{
return GetInstantMixFromGenres(item.Genres, user, dtoOptions);
}
public IReadOnlyList<BaseItem> GetInstantMixFromFolder(Folder item, User? user, DtoOptions dtoOptions)
{ {
var genres = item var genres = item
.GetRecursiveChildren(user, new InternalItemsQuery(user) .GetRecursiveChildren(user, new InternalItemsQuery(user)
@ -63,12 +57,12 @@ namespace Emby.Server.Implementations.Library
return GetInstantMixFromGenres(genres, user, dtoOptions); return GetInstantMixFromGenres(genres, user, dtoOptions);
} }
public List<BaseItem> GetInstantMixFromPlaylist(Playlist item, User? user, DtoOptions dtoOptions) public IReadOnlyList<BaseItem> GetInstantMixFromPlaylist(Playlist item, User? user, DtoOptions dtoOptions)
{ {
return GetInstantMixFromGenres(item.Genres, user, dtoOptions); return GetInstantMixFromGenres(item.Genres, user, dtoOptions);
} }
public List<BaseItem> GetInstantMixFromGenres(IEnumerable<string> genres, User? user, DtoOptions dtoOptions) public IReadOnlyList<BaseItem> GetInstantMixFromGenres(IEnumerable<string> genres, User? user, DtoOptions dtoOptions)
{ {
var genreIds = genres.DistinctNames().Select(i => var genreIds = genres.DistinctNames().Select(i =>
{ {
@ -85,7 +79,7 @@ namespace Emby.Server.Implementations.Library
return GetInstantMixFromGenreIds(genreIds, user, dtoOptions); return GetInstantMixFromGenreIds(genreIds, user, dtoOptions);
} }
public List<BaseItem> GetInstantMixFromGenreIds(Guid[] genreIds, User? user, DtoOptions dtoOptions) public IReadOnlyList<BaseItem> GetInstantMixFromGenreIds(Guid[] genreIds, User? user, DtoOptions dtoOptions)
{ {
return _libraryManager.GetItemList(new InternalItemsQuery(user) return _libraryManager.GetItemList(new InternalItemsQuery(user)
{ {
@ -97,7 +91,7 @@ namespace Emby.Server.Implementations.Library
}); });
} }
public List<BaseItem> GetInstantMixFromItem(BaseItem item, User? user, DtoOptions dtoOptions) public IReadOnlyList<BaseItem> GetInstantMixFromItem(BaseItem item, User? user, DtoOptions dtoOptions)
{ {
if (item is MusicGenre) if (item is MusicGenre)
{ {

View File

@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
{ {
if (args.IsDirectory) if (args.IsDirectory)
{ {
// It's a boxset if the path is a directory with [playlist] in its name // It's a playlist if the path is a directory with [playlist] in its name
var filename = Path.GetFileName(Path.TrimEndingDirectorySeparator(args.Path)); var filename = Path.GetFileName(Path.TrimEndingDirectorySeparator(args.Path));
if (string.IsNullOrEmpty(filename)) if (string.IsNullOrEmpty(filename))
{ {

View File

@ -171,7 +171,7 @@ namespace Emby.Server.Implementations.Library
} }
}; };
List<BaseItem> mediaItems; IReadOnlyList<BaseItem> mediaItems;
if (searchQuery.IncludeItemTypes.Length == 1 && searchQuery.IncludeItemTypes[0] == BaseItemKind.MusicArtist) if (searchQuery.IncludeItemTypes.Length == 1 && searchQuery.IncludeItemTypes[0] == BaseItemKind.MusicArtist)
{ {

View File

@ -43,14 +43,26 @@ public class SplashscreenPostScanTask : ILibraryPostScanTask
/// <inheritdoc /> /// <inheritdoc />
public Task Run(IProgress<double> progress, CancellationToken cancellationToken) public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{ {
var posters = GetItemsWithImageType(ImageType.Primary).Select(x => x.GetImages(ImageType.Primary).First().Path).ToList(); var posters = GetItemsWithImageType(ImageType.Primary)
var backdrops = GetItemsWithImageType(ImageType.Thumb).Select(x => x.GetImages(ImageType.Thumb).First().Path).ToList(); .Select(x => x.GetImages(ImageType.Primary).FirstOrDefault()?.Path)
.Where(path => !string.IsNullOrEmpty(path))
.Select(path => path!)
.ToList();
var backdrops = GetItemsWithImageType(ImageType.Thumb)
.Select(x => x.GetImages(ImageType.Thumb).FirstOrDefault()?.Path)
.Where(path => !string.IsNullOrEmpty(path))
.Select(path => path!)
.ToList();
if (backdrops.Count == 0) if (backdrops.Count == 0)
{ {
// Thumb images fit better because they include the title in the image but are not provided with TMDb. // Thumb images fit better because they include the title in the image but are not provided with TMDb.
// Using backdrops as a fallback to generate an image at all // Using backdrops as a fallback to generate an image at all
_logger.LogDebug("No thumb images found. Using backdrops to generate splashscreen"); _logger.LogDebug("No thumb images found. Using backdrops to generate splashscreen");
backdrops = GetItemsWithImageType(ImageType.Backdrop).Select(x => x.GetImages(ImageType.Backdrop).First().Path).ToList(); backdrops = GetItemsWithImageType(ImageType.Backdrop)
.Select(x => x.GetImages(ImageType.Backdrop).FirstOrDefault()?.Path)
.Where(path => !string.IsNullOrEmpty(path))
.Select(path => path!)
.ToList();
} }
_imageEncoder.CreateSplashscreen(posters, backdrops); _imageEncoder.CreateSplashscreen(posters, backdrops);

View File

@ -1,17 +1,21 @@
#pragma warning disable RS0030 // Do not use banned APIs
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.Linq;
using System.Threading; using System.Threading;
using Jellyfin.Data.Entities; using Jellyfin.Data.Entities;
using Jellyfin.Extensions;
using Jellyfin.Server.Implementations;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using Microsoft.EntityFrameworkCore;
using AudioBook = MediaBrowser.Controller.Entities.AudioBook; using AudioBook = MediaBrowser.Controller.Entities.AudioBook;
using Book = MediaBrowser.Controller.Entities.Book; using Book = MediaBrowser.Controller.Entities.Book;
@ -26,22 +30,18 @@ namespace Emby.Server.Implementations.Library
new ConcurrentDictionary<string, UserItemData>(StringComparer.OrdinalIgnoreCase); new ConcurrentDictionary<string, UserItemData>(StringComparer.OrdinalIgnoreCase);
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IUserManager _userManager; private readonly IDbContextFactory<JellyfinDbContext> _repository;
private readonly IUserDataRepository _repository;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="UserDataManager"/> class. /// Initializes a new instance of the <see cref="UserDataManager"/> class.
/// </summary> /// </summary>
/// <param name="config">Instance of the <see cref="IServerConfigurationManager"/> interface.</param> /// <param name="config">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
/// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param> /// <param name="repository">Instance of the <see cref="IDbContextFactory{JellyfinDbContext}"/> interface.</param>
/// <param name="repository">Instance of the <see cref="IUserDataRepository"/> interface.</param>
public UserDataManager( public UserDataManager(
IServerConfigurationManager config, IServerConfigurationManager config,
IUserManager userManager, IDbContextFactory<JellyfinDbContext> repository)
IUserDataRepository repository)
{ {
_config = config; _config = config;
_userManager = userManager;
_repository = repository; _repository = repository;
} }
@ -59,13 +59,27 @@ namespace Emby.Server.Implementations.Library
var keys = item.GetUserDataKeys(); var keys = item.GetUserDataKeys();
var userId = user.InternalId; using var dbContext = _repository.CreateDbContext();
using var transaction = dbContext.Database.BeginTransaction();
foreach (var key in keys) foreach (var key in keys)
{ {
_repository.SaveUserData(userId, key, userData, cancellationToken); userData.Key = key;
var userDataEntry = Map(userData, user.Id, item.Id);
if (dbContext.UserData.Any(f => f.ItemId == userDataEntry.ItemId && f.UserId == userDataEntry.UserId && f.CustomDataKey == userDataEntry.CustomDataKey))
{
dbContext.UserData.Attach(userDataEntry).State = EntityState.Modified;
}
else
{
dbContext.UserData.Add(userDataEntry);
}
} }
dbContext.SaveChanges();
transaction.Commit();
var userId = user.InternalId;
var cacheKey = GetCacheKey(userId, item.Id); var cacheKey = GetCacheKey(userId, item.Id);
_userData.AddOrUpdate(cacheKey, userData, (_, _) => userData); _userData.AddOrUpdate(cacheKey, userData, (_, _) => userData);
@ -84,10 +98,9 @@ namespace Emby.Server.Implementations.Library
{ {
ArgumentNullException.ThrowIfNull(user); ArgumentNullException.ThrowIfNull(user);
ArgumentNullException.ThrowIfNull(item); ArgumentNullException.ThrowIfNull(item);
ArgumentNullException.ThrowIfNull(reason);
ArgumentNullException.ThrowIfNull(userDataDto); ArgumentNullException.ThrowIfNull(userDataDto);
var userData = GetUserData(user, item); var userData = GetUserData(user, item) ?? throw new InvalidOperationException("UserData should not be null.");
if (userDataDto.PlaybackPositionTicks.HasValue) if (userDataDto.PlaybackPositionTicks.HasValue)
{ {
@ -127,33 +140,91 @@ namespace Emby.Server.Implementations.Library
SaveUserData(user, item, userData, reason, CancellationToken.None); SaveUserData(user, item, userData, reason, CancellationToken.None);
} }
private UserItemData GetUserData(User user, Guid itemId, List<string> keys) private UserData Map(UserItemData dto, Guid userId, Guid itemId)
{ {
var userId = user.InternalId; return new UserData()
var cacheKey = GetCacheKey(userId, itemId);
return _userData.GetOrAdd(cacheKey, _ => GetUserDataInternal(userId, keys));
}
private UserItemData GetUserDataInternal(long internalUserId, List<string> keys)
{ {
var userData = _repository.GetUserData(internalUserId, keys); ItemId = itemId,
CustomDataKey = dto.Key,
if (userData is not null) Item = null,
{ User = null,
return userData; AudioStreamIndex = dto.AudioStreamIndex,
} IsFavorite = dto.IsFavorite,
LastPlayedDate = dto.LastPlayedDate,
if (keys.Count > 0) Likes = dto.Likes,
{ PlaybackPositionTicks = dto.PlaybackPositionTicks,
return new UserItemData PlayCount = dto.PlayCount,
{ Played = dto.Played,
Key = keys[0] Rating = dto.Rating,
UserId = userId,
SubtitleStreamIndex = dto.SubtitleStreamIndex,
}; };
} }
throw new UnreachableException(); private UserItemData Map(UserData dto)
{
return new UserItemData()
{
Key = dto.CustomDataKey!,
AudioStreamIndex = dto.AudioStreamIndex,
IsFavorite = dto.IsFavorite,
LastPlayedDate = dto.LastPlayedDate,
Likes = dto.Likes,
PlaybackPositionTicks = dto.PlaybackPositionTicks,
PlayCount = dto.PlayCount,
Played = dto.Played,
Rating = dto.Rating,
SubtitleStreamIndex = dto.SubtitleStreamIndex,
};
}
private UserItemData? GetUserData(User user, Guid itemId, List<string> keys)
{
var cacheKey = GetCacheKey(user.InternalId, itemId);
if (_userData.TryGetValue(cacheKey, out var data))
{
return data;
}
data = GetUserDataInternal(user.Id, itemId, keys);
if (data is null)
{
return new UserItemData()
{
Key = keys[0],
};
}
return _userData.GetOrAdd(cacheKey, data);
}
private UserItemData? GetUserDataInternal(Guid userId, Guid itemId, List<string> keys)
{
if (keys.Count == 0)
{
return null;
}
using var context = _repository.CreateDbContext();
var userData = context.UserData.AsNoTracking().Where(e => e.ItemId == itemId && keys.Contains(e.CustomDataKey) && e.UserId.Equals(userId)).ToArray();
if (userData.Length > 0)
{
var directDataReference = userData.FirstOrDefault(e => e.CustomDataKey == itemId.ToString("N"));
if (directDataReference is not null)
{
return Map(directDataReference);
}
return Map(userData.First());
}
return new UserItemData
{
Key = keys.Last()!
};
} }
/// <summary> /// <summary>
@ -166,20 +237,25 @@ namespace Emby.Server.Implementations.Library
} }
/// <inheritdoc /> /// <inheritdoc />
public UserItemData GetUserData(User user, BaseItem item) public UserItemData? GetUserData(User user, BaseItem item)
{ {
return GetUserData(user, item.Id, item.GetUserDataKeys()); return GetUserData(user, item.Id, item.GetUserDataKeys());
} }
/// <inheritdoc /> /// <inheritdoc />
public UserItemDataDto GetUserDataDto(BaseItem item, User user) public UserItemDataDto? GetUserDataDto(BaseItem item, User user)
=> GetUserDataDto(item, null, user, new DtoOptions()); => GetUserDataDto(item, null, user, new DtoOptions());
/// <inheritdoc /> /// <inheritdoc />
public UserItemDataDto GetUserDataDto(BaseItem item, BaseItemDto? itemDto, User user, DtoOptions options) public UserItemDataDto? GetUserDataDto(BaseItem item, BaseItemDto? itemDto, User user, DtoOptions options)
{ {
var userData = GetUserData(user, item); var userData = GetUserData(user, item);
var dto = GetUserItemDataDto(userData); if (userData is null)
{
return null;
}
var dto = GetUserItemDataDto(userData, item.Id);
item.FillUserDataDtoValues(dto, userData, itemDto, user, options); item.FillUserDataDtoValues(dto, userData, itemDto, user, options);
return dto; return dto;
@ -189,9 +265,10 @@ namespace Emby.Server.Implementations.Library
/// Converts a UserItemData to a DTOUserItemData. /// Converts a UserItemData to a DTOUserItemData.
/// </summary> /// </summary>
/// <param name="data">The data.</param> /// <param name="data">The data.</param>
/// <param name="itemId">The reference key to an Item.</param>
/// <returns>DtoUserItemData.</returns> /// <returns>DtoUserItemData.</returns>
/// <exception cref="ArgumentNullException"><paramref name="data"/> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="data"/> is <c>null</c>.</exception>
private UserItemDataDto GetUserItemDataDto(UserItemData data) private UserItemDataDto GetUserItemDataDto(UserItemData data, Guid itemId)
{ {
ArgumentNullException.ThrowIfNull(data); ArgumentNullException.ThrowIfNull(data);
@ -204,6 +281,7 @@ namespace Emby.Server.Implementations.Library
Rating = data.Rating, Rating = data.Rating,
Played = data.Played, Played = data.Played,
LastPlayedDate = data.LastPlayedDate, LastPlayedDate = data.LastPlayedDate,
ItemId = itemId,
Key = data.Key Key = data.Key
}; };
} }

View File

@ -308,39 +308,40 @@ namespace Emby.Server.Implementations.Library
} }
} }
var mediaTypes = new List<MediaType>(); MediaType[] mediaTypes = [];
if (includeItemTypes.Length == 0) if (includeItemTypes.Length == 0)
{ {
HashSet<MediaType> tmpMediaTypes = [];
foreach (var parent in parents.OfType<ICollectionFolder>()) foreach (var parent in parents.OfType<ICollectionFolder>())
{ {
switch (parent.CollectionType) switch (parent.CollectionType)
{ {
case CollectionType.books: case CollectionType.books:
mediaTypes.Add(MediaType.Book); tmpMediaTypes.Add(MediaType.Book);
mediaTypes.Add(MediaType.Audio); tmpMediaTypes.Add(MediaType.Audio);
break; break;
case CollectionType.music: case CollectionType.music:
mediaTypes.Add(MediaType.Audio); tmpMediaTypes.Add(MediaType.Audio);
break; break;
case CollectionType.photos: case CollectionType.photos:
mediaTypes.Add(MediaType.Photo); tmpMediaTypes.Add(MediaType.Photo);
mediaTypes.Add(MediaType.Video); tmpMediaTypes.Add(MediaType.Video);
break; break;
case CollectionType.homevideos: case CollectionType.homevideos:
mediaTypes.Add(MediaType.Photo); tmpMediaTypes.Add(MediaType.Photo);
mediaTypes.Add(MediaType.Video); tmpMediaTypes.Add(MediaType.Video);
break; break;
default: default:
mediaTypes.Add(MediaType.Video); tmpMediaTypes.Add(MediaType.Video);
break; break;
} }
} }
mediaTypes = mediaTypes.Distinct().ToList(); mediaTypes = tmpMediaTypes.ToArray();
} }
var excludeItemTypes = includeItemTypes.Length == 0 && mediaTypes.Count == 0 var excludeItemTypes = includeItemTypes.Length == 0 && mediaTypes.Length == 0
? new[] ? new[]
{ {
BaseItemKind.Person, BaseItemKind.Person,
@ -366,14 +367,9 @@ namespace Emby.Server.Implementations.Library
Limit = limit * 5, Limit = limit * 5,
IsPlayed = isPlayed, IsPlayed = isPlayed,
DtoOptions = options, DtoOptions = options,
MediaTypes = mediaTypes.ToArray() MediaTypes = mediaTypes
}; };
if (parents.Count == 0)
{
return _libraryManager.GetItemList(query, false);
}
return _libraryManager.GetItemList(query, parents); return _libraryManager.GetItemList(query, parents);
} }
} }

View File

@ -16,7 +16,7 @@
"Folders": "المجلدات", "Folders": "المجلدات",
"Genres": "التصنيفات", "Genres": "التصنيفات",
"HeaderAlbumArtists": "فناني الألبوم", "HeaderAlbumArtists": "فناني الألبوم",
"HeaderContinueWatching": "استئناف المشاهدة", "HeaderContinueWatching": "إستئناف المشاهدة",
"HeaderFavoriteAlbums": "الألبومات المفضلة", "HeaderFavoriteAlbums": "الألبومات المفضلة",
"HeaderFavoriteArtists": "الفنانون المفضلون", "HeaderFavoriteArtists": "الفنانون المفضلون",
"HeaderFavoriteEpisodes": "الحلقات المفضلة", "HeaderFavoriteEpisodes": "الحلقات المفضلة",
@ -31,7 +31,7 @@
"ItemRemovedWithName": "أُزيل {0} من المكتبة", "ItemRemovedWithName": "أُزيل {0} من المكتبة",
"LabelIpAddressValue": "عنوان الآي بي: {0}", "LabelIpAddressValue": "عنوان الآي بي: {0}",
"LabelRunningTimeValue": "مدة التشغيل: {0}", "LabelRunningTimeValue": "مدة التشغيل: {0}",
"Latest": "أحدث", "Latest": "الأحدث",
"MessageApplicationUpdated": "حُدث خادم Jellyfin", "MessageApplicationUpdated": "حُدث خادم Jellyfin",
"MessageApplicationUpdatedTo": "حُدث خادم Jellyfin إلى {0}", "MessageApplicationUpdatedTo": "حُدث خادم Jellyfin إلى {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "حُدثت إعدادات الخادم في قسم {0}", "MessageNamedServerConfigurationUpdatedWithValue": "حُدثت إعدادات الخادم في قسم {0}",
@ -52,7 +52,7 @@
"NotificationOptionInstallationFailed": "فشل في التثبيت", "NotificationOptionInstallationFailed": "فشل في التثبيت",
"NotificationOptionNewLibraryContent": "أُضيف محتوى جديدا", "NotificationOptionNewLibraryContent": "أُضيف محتوى جديدا",
"NotificationOptionPluginError": "فشل في الملحق", "NotificationOptionPluginError": "فشل في الملحق",
"NotificationOptionPluginInstalled": "ثُبتت المكونات الإضافية", "NotificationOptionPluginInstalled": "ثُبتت الملحق",
"NotificationOptionPluginUninstalled": "تمت إزالة الملحق", "NotificationOptionPluginUninstalled": "تمت إزالة الملحق",
"NotificationOptionPluginUpdateInstalled": "تم تثبيت تحديثات الملحق", "NotificationOptionPluginUpdateInstalled": "تم تثبيت تحديثات الملحق",
"NotificationOptionServerRestartRequired": "يجب إعادة تشغيل الخادم", "NotificationOptionServerRestartRequired": "يجب إعادة تشغيل الخادم",
@ -90,10 +90,10 @@
"UserStartedPlayingItemWithValues": "قام {0} ببدء تشغيل {1} على {2}", "UserStartedPlayingItemWithValues": "قام {0} ببدء تشغيل {1} على {2}",
"UserStoppedPlayingItemWithValues": "قام {0} بإيقاف تشغيل {1} على {2}", "UserStoppedPlayingItemWithValues": "قام {0} بإيقاف تشغيل {1} على {2}",
"ValueHasBeenAddedToLibrary": "تمت اضافت {0} إلى مكتبة الوسائط", "ValueHasBeenAddedToLibrary": "تمت اضافت {0} إلى مكتبة الوسائط",
"ValueSpecialEpisodeName": "حلقه خاصه - {0}", "ValueSpecialEpisodeName": "حلقة خاصه - {0}",
"VersionNumber": "الإصدار {0}", "VersionNumber": "الإصدار {0}",
"TaskCleanCacheDescription": "يحذف الملفات المؤقتة التي لم يعد النظام بحاجة إليها.", "TaskCleanCacheDescription": "يحذف الملفات المؤقتة التي لم يعد النظام بحاجة إليها.",
"TaskCleanCache": "احذف ما بمجلد الملفات المؤقتة", "TaskCleanCache": "حذف الملفات المؤقتة",
"TasksChannelsCategory": "قنوات الإنترنت", "TasksChannelsCategory": "قنوات الإنترنت",
"TasksLibraryCategory": "مكتبة", "TasksLibraryCategory": "مكتبة",
"TasksMaintenanceCategory": "صيانة", "TasksMaintenanceCategory": "صيانة",
@ -129,7 +129,12 @@
"TaskRefreshTrickplayImagesDescription": "يُنشئ معاينات Trickplay لمقاطع الفيديو في المكتبات المُمكّنة.", "TaskRefreshTrickplayImagesDescription": "يُنشئ معاينات Trickplay لمقاطع الفيديو في المكتبات المُمكّنة.",
"TaskCleanCollectionsAndPlaylists": "حذف المجموعات وقوائم التشغيل", "TaskCleanCollectionsAndPlaylists": "حذف المجموعات وقوائم التشغيل",
"TaskCleanCollectionsAndPlaylistsDescription": "حذف عناصر من المجموعات وقوائم التشغيل التي لم تعد موجودة.", "TaskCleanCollectionsAndPlaylistsDescription": "حذف عناصر من المجموعات وقوائم التشغيل التي لم تعد موجودة.",
"TaskAudioNormalization": طبيع الصوت", "TaskAudioNormalization": سوية الصوت",
"TaskAudioNormalizationDescription": "مسح الملفات لتطبيع بيانات الصوت.", "TaskAudioNormalizationDescription": "مسح الملفات لتطبيع بيانات الصوت.",
"TaskDownloadMissingLyrics": "تنزيل عبارات القصيدة" "TaskDownloadMissingLyrics": "تنزيل عبارات القصيدة",
"TaskDownloadMissingLyricsDescription": "كلمات",
"TaskExtractMediaSegments": "فحص مقاطع الوسائط",
"TaskExtractMediaSegmentsDescription": "يستخرج مقاطع وسائط من إضافات MediaSegment المُفعّلة.",
"TaskMoveTrickplayImages": "تغيير مكان صور المعاينة السريعة",
"TaskMoveTrickplayImagesDescription": "تُنقل ملفات التشغيل السريع الحالية بناءً على إعدادات المكتبة."
} }

View File

@ -121,7 +121,7 @@
"TaskCleanActivityLog": "Изчисти дневника с активност", "TaskCleanActivityLog": "Изчисти дневника с активност",
"TaskOptimizeDatabaseDescription": "Прави базата данни по-компактна и освобождава място. Пускането на тази задача след сканиране на библиотеката или правене на други промени, свързани с модификации на базата данни, може да подобри производителността.", "TaskOptimizeDatabaseDescription": "Прави базата данни по-компактна и освобождава място. Пускането на тази задача след сканиране на библиотеката или правене на други промени, свързани с модификации на базата данни, може да подобри производителността.",
"TaskOptimizeDatabase": "Оптимизирай базата данни", "TaskOptimizeDatabase": "Оптимизирай базата данни",
"TaskKeyframeExtractorDescription": "Извличат се ключови кадри от видеофайловете ,за да се създаде по точен ХЛС списък . Задачата може да отнеме много време.", "TaskKeyframeExtractorDescription": "Извличат се ключови кадри от видеофайловете ,за да се създаде по точен HLS списък . Задачата може да отнеме много време.",
"TaskKeyframeExtractor": "Извличане на ключови кадри", "TaskKeyframeExtractor": "Извличане на ключови кадри",
"External": "Външен", "External": "Външен",
"HearingImpaired": "Увреден слух", "HearingImpaired": "Увреден слух",
@ -129,8 +129,12 @@
"TaskRefreshTrickplayImagesDescription": "Създава прегледи на Trickplay за видеа в активирани библиотеки.", "TaskRefreshTrickplayImagesDescription": "Създава прегледи на Trickplay за видеа в активирани библиотеки.",
"TaskDownloadMissingLyrics": "Свали липсващи текстове", "TaskDownloadMissingLyrics": "Свали липсващи текстове",
"TaskDownloadMissingLyricsDescription": "Свали текстове за песни", "TaskDownloadMissingLyricsDescription": "Свали текстове за песни",
"TaskCleanCollectionsAndPlaylists": "Изчисти колекциите и плейлистовете", "TaskCleanCollectionsAndPlaylists": "Изчисти колекциите и плейлистите",
"TaskCleanCollectionsAndPlaylistsDescription": "Премахни несъществуващи файлове в колекциите и плейлистите.", "TaskCleanCollectionsAndPlaylistsDescription": "Премахни несъществуващи файлове в колекциите и плейлистите.",
"TaskAudioNormalization": "Нормализиране на звука", "TaskAudioNormalization": "Нормализиране на звука",
"TaskAudioNormalizationDescription": "Сканирай файловете за нормализация на звука." "TaskAudioNormalizationDescription": "Сканирай файловете за нормализация на звука.",
"TaskExtractMediaSegmentsDescription": "Изважда медиини сегменти от MediaSegment плъгини.",
"TaskMoveTrickplayImages": "Мигриране на Локацията за Trickplay изображения",
"TaskMoveTrickplayImagesDescription": "Премества съществуващите trickplay изображения спрямо настройките на библиотеката.",
"TaskExtractMediaSegments": "Сканиране за сегменти"
} }

View File

@ -125,5 +125,11 @@
"TaskKeyframeExtractor": "কি-ফ্রেম নিষ্কাশক", "TaskKeyframeExtractor": "কি-ফ্রেম নিষ্কাশক",
"TaskKeyframeExtractorDescription": "ভিডিয়ো থেকে কি-ফ্রেম নিষ্কাশনের মাধ্যমে অধিকতর সঠিক HLS প্লে লিস্ট তৈরী করে। এই প্রক্রিয়া দীর্ঘ সময় ধরে চলতে পারে।", "TaskKeyframeExtractorDescription": "ভিডিয়ো থেকে কি-ফ্রেম নিষ্কাশনের মাধ্যমে অধিকতর সঠিক HLS প্লে লিস্ট তৈরী করে। এই প্রক্রিয়া দীর্ঘ সময় ধরে চলতে পারে।",
"TaskRefreshTrickplayImages": "ট্রিকপ্লে ইমেজ তৈরি করুন", "TaskRefreshTrickplayImages": "ট্রিকপ্লে ইমেজ তৈরি করুন",
"TaskRefreshTrickplayImagesDescription": "সক্ষম লাইব্রেরিতে ভিডিওর জন্য ট্রিকপ্লে প্রিভিউ তৈরি করে।" "TaskRefreshTrickplayImagesDescription": "সক্ষম লাইব্রেরিতে ভিডিওর জন্য ট্রিকপ্লে প্রিভিউ তৈরি করে।",
"TaskDownloadMissingLyricsDescription": "গানের লিরিক্স ডাউনলোড করে",
"TaskCleanCollectionsAndPlaylists": "সংগ্রহ এবং প্লেলিস্ট পরিষ্কার করুন",
"TaskCleanCollectionsAndPlaylistsDescription": "সংগ্রহ এবং প্লেলিস্ট থেকে আইটেমগুলি সরিয়ে দেয় যা আর বিদ্যমান নেই।",
"TaskExtractMediaSegments": "মিডিয়া সেগমেন্ট স্ক্যান",
"TaskExtractMediaSegmentsDescription": "MediaSegment সক্ষম প্লাগইনগুলি থেকে মিডিয়া সেগমেন্টগুলি বের করে বা প্রাপ্ত করে।",
"TaskDownloadMissingLyrics": "অনুপস্থিত গান ডাউনলোড করুন"
} }

View File

@ -16,7 +16,7 @@
"Folders": "Carpetes", "Folders": "Carpetes",
"Genres": "Gèneres", "Genres": "Gèneres",
"HeaderAlbumArtists": "Artistes de l'àlbum", "HeaderAlbumArtists": "Artistes de l'àlbum",
"HeaderContinueWatching": "Continuar veient", "HeaderContinueWatching": "Continua veient",
"HeaderFavoriteAlbums": "Àlbums preferits", "HeaderFavoriteAlbums": "Àlbums preferits",
"HeaderFavoriteArtists": "Artistes preferits", "HeaderFavoriteArtists": "Artistes preferits",
"HeaderFavoriteEpisodes": "Episodis preferits", "HeaderFavoriteEpisodes": "Episodis preferits",
@ -24,13 +24,13 @@
"HeaderFavoriteSongs": "Cançons preferides", "HeaderFavoriteSongs": "Cançons preferides",
"HeaderLiveTV": "TV en directe", "HeaderLiveTV": "TV en directe",
"HeaderNextUp": "A continuació", "HeaderNextUp": "A continuació",
"HeaderRecordingGroups": "Grups d'enregistrament", "HeaderRecordingGroups": "Grups Musicals",
"HomeVideos": "Vídeos domèstics", "HomeVideos": "Vídeos domèstics",
"Inherit": "Hereta", "Inherit": "Heretat",
"ItemAddedWithName": "{0} ha sigut afegit a la biblioteca", "ItemAddedWithName": "{0} s'ha afegit a la biblioteca",
"ItemRemovedWithName": "{0} ha sigut eliminat de la biblioteca", "ItemRemovedWithName": "{0} s'ha eliminat de la biblioteca",
"LabelIpAddressValue": "Adreça IP: {0}", "LabelIpAddressValue": "Adreça IP: {0}",
"LabelRunningTimeValue": "Temps en funcionament: {0}", "LabelRunningTimeValue": "Temps en marxa: {0}",
"Latest": "Darrers", "Latest": "Darrers",
"MessageApplicationUpdated": "El servidor de Jellyfin ha estat actualitzat", "MessageApplicationUpdated": "El servidor de Jellyfin ha estat actualitzat",
"MessageApplicationUpdatedTo": "El servidor de Jellyfin ha estat actualitzat a {0}", "MessageApplicationUpdatedTo": "El servidor de Jellyfin ha estat actualitzat a {0}",
@ -44,8 +44,8 @@
"NameSeasonNumber": "Temporada {0}", "NameSeasonNumber": "Temporada {0}",
"NameSeasonUnknown": "Temporada desconeguda", "NameSeasonUnknown": "Temporada desconeguda",
"NewVersionIsAvailable": "Una nova versió del servidor de Jellyfin està disponible per a descarregar.", "NewVersionIsAvailable": "Una nova versió del servidor de Jellyfin està disponible per a descarregar.",
"NotificationOptionApplicationUpdateAvailable": "Actualització de l'aplicació disponible", "NotificationOptionApplicationUpdateAvailable": "Actualització de l'aplicatiu disponible",
"NotificationOptionApplicationUpdateInstalled": "Actualització de l'aplicació instal·lada", "NotificationOptionApplicationUpdateInstalled": "Actualització de l'aplicatiu instal·lada",
"NotificationOptionAudioPlayback": "Reproducció d'àudio iniciada", "NotificationOptionAudioPlayback": "Reproducció d'àudio iniciada",
"NotificationOptionAudioPlaybackStopped": "Reproducció d'àudio aturada", "NotificationOptionAudioPlaybackStopped": "Reproducció d'àudio aturada",
"NotificationOptionCameraImageUploaded": "Imatge de càmera pujada", "NotificationOptionCameraImageUploaded": "Imatge de càmera pujada",
@ -54,8 +54,8 @@
"NotificationOptionPluginError": "Un complement ha fallat", "NotificationOptionPluginError": "Un complement ha fallat",
"NotificationOptionPluginInstalled": "Complement instal·lat", "NotificationOptionPluginInstalled": "Complement instal·lat",
"NotificationOptionPluginUninstalled": "Complement desinstal·lat", "NotificationOptionPluginUninstalled": "Complement desinstal·lat",
"NotificationOptionPluginUpdateInstalled": "Actualització de complement instal·lada", "NotificationOptionPluginUpdateInstalled": "Actualització del complement instal·lada",
"NotificationOptionServerRestartRequired": "Reinici del servidor requerit", "NotificationOptionServerRestartRequired": "El servidor s'ha de reiniciar",
"NotificationOptionTaskFailed": "Tasca programada fallida", "NotificationOptionTaskFailed": "Tasca programada fallida",
"NotificationOptionUserLockedOut": "Usuari expulsat", "NotificationOptionUserLockedOut": "Usuari expulsat",
"NotificationOptionVideoPlayback": "Reproducció de vídeo iniciada", "NotificationOptionVideoPlayback": "Reproducció de vídeo iniciada",
@ -64,15 +64,15 @@
"Playlists": "Llistes de reproducció", "Playlists": "Llistes de reproducció",
"Plugin": "Complement", "Plugin": "Complement",
"PluginInstalledWithName": "{0} ha estat instal·lat", "PluginInstalledWithName": "{0} ha estat instal·lat",
"PluginUninstalledWithName": "{0} ha estat desinstal·lat", "PluginUninstalledWithName": "S'ha instalat {0}",
"PluginUpdatedWithName": "{0} ha estat actualitzat", "PluginUpdatedWithName": "S'ha actualitzat {0}",
"ProviderValue": "Proveïdor: {0}", "ProviderValue": "Proveïdor: {0}",
"ScheduledTaskFailedWithName": "{0} ha fallat", "ScheduledTaskFailedWithName": "{0} ha fallat",
"ScheduledTaskStartedWithName": "{0} s'ha iniciat", "ScheduledTaskStartedWithName": "S'ha iniciat {0}",
"ServerNameNeedsToBeRestarted": "{0} necessita ser reiniciat", "ServerNameNeedsToBeRestarted": "S'ha de reiniciar {0}",
"Shows": "Sèries", "Shows": "Sèries",
"Songs": "Cançons", "Songs": "Cançons",
"StartupEmbyServerIsLoading": "El servidor de Jellyfin s'està carregant. Proveu-ho altre cop aviat.", "StartupEmbyServerIsLoading": "El servidor de Jellyfin s'està carregant. Proveu de nou en una estona.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "Els subtítols per a {1} no s'han pogut baixar de {0}", "SubtitleDownloadFailureFromForItem": "Els subtítols per a {1} no s'han pogut baixar de {0}",
"Sync": "Sincronitzar", "Sync": "Sincronitzar",
@ -80,41 +80,41 @@
"TvShows": "Sèries de TV", "TvShows": "Sèries de TV",
"User": "Usuari", "User": "Usuari",
"UserCreatedWithName": "S'ha creat l'usuari {0}", "UserCreatedWithName": "S'ha creat l'usuari {0}",
"UserDeletedWithName": "L'usuari {0} ha estat eliminat", "UserDeletedWithName": "S'ha eliminat l'usuari {0}",
"UserDownloadingItemWithValues": "{0} està descarregant {1}", "UserDownloadingItemWithValues": "{0} està descarregant {1}",
"UserLockedOutWithName": "L'usuari {0} ha sigut expulsat", "UserLockedOutWithName": "S'ha expulsat a l'usuari {0}",
"UserOfflineFromDevice": "{0} s'ha desconnectat de {1}", "UserOfflineFromDevice": "{0} s'ha desconnectat de {1}",
"UserOnlineFromDevice": "{0} està connectat des de {1}", "UserOnlineFromDevice": "{0} està connectat des de {1}",
"UserPasswordChangedWithName": "La contrasenya ha estat canviada per a l'usuari {0}", "UserPasswordChangedWithName": "S'ha canviat la contrasenya per a l'usuari {0}",
"UserPolicyUpdatedWithName": "La política d'usuari s'ha actualitzat per a {0}", "UserPolicyUpdatedWithName": "La política d'usuari s'ha actualitzat per a {0}",
"UserStartedPlayingItemWithValues": "{0} ha començat a reproduir {1}", "UserStartedPlayingItemWithValues": "{0} ha començat a reproduir {1} a {2}",
"UserStoppedPlayingItemWithValues": "{0} ha parat de reproduir {1}", "UserStoppedPlayingItemWithValues": "{0} ha parat de reproduir {1} a {2}",
"ValueHasBeenAddedToLibrary": "{0} ha sigut afegit a la teva biblioteca", "ValueHasBeenAddedToLibrary": "S'ha afegit {0} a la teva biblioteca",
"ValueSpecialEpisodeName": "Especial - {0}", "ValueSpecialEpisodeName": "Especial - {0}",
"VersionNumber": "Versió {0}", "VersionNumber": "Versió {0}",
"TaskDownloadMissingSubtitlesDescription": "Cerca a internet els subtítols que faltin a partir de la configuració de metadades.", "TaskDownloadMissingSubtitlesDescription": "Cerca a internet els subtítols que faltin a partir de la configuració de metadades.",
"TaskDownloadMissingSubtitles": "Descarrega els subtítols que faltin", "TaskDownloadMissingSubtitles": "Descarrega els subtítols que faltin",
"TaskRefreshChannelsDescription": "Actualitza la informació dels canals d'internet.", "TaskRefreshChannelsDescription": "Actualitza la informació dels canals per internet.",
"TaskRefreshChannels": "Actualitza els canals", "TaskRefreshChannels": "Actualitza els canals",
"TaskCleanTranscodeDescription": "Elimina els arxius de transcodificacions que tinguin més d'un dia.", "TaskCleanTranscodeDescription": "Elimina els arxius de transcodificacions que tinguin més d'un dia.",
"TaskCleanTranscode": "Neteja les transcodificacions", "TaskCleanTranscode": "Neteja les transcodificacions",
"TaskUpdatePluginsDescription": "Actualitza els connectors que estan configurats per a actualitzar-se automàticament.", "TaskUpdatePluginsDescription": "Actualitza els complements que estan configurats per a actualitzar-se automàticament.",
"TaskUpdatePlugins": "Actualitza els connectors", "TaskUpdatePlugins": "Actualitza els complements",
"TaskRefreshPeopleDescription": "Actualitza les metadades dels actors i directors de la teva mediateca.", "TaskRefreshPeopleDescription": "Actualitza les metadades dels actors i directors de la teva biblioteca de mitjans.",
"TaskRefreshPeople": "Actualitza les persones", "TaskRefreshPeople": "Actualitza les persones",
"TaskCleanLogsDescription": "Esborra els logs que tinguin més de {0} dies.", "TaskCleanLogsDescription": "Esborra els logs que tinguin més de {0} dies.",
"TaskCleanLogs": "Neteja els registres", "TaskCleanLogs": "Neteja els registres",
"TaskRefreshLibraryDescription": "Escaneja la mediateca buscant fitxers nous i refresca les metadades.", "TaskRefreshLibraryDescription": "Escaneja la biblioteca de mitjans buscant fitxers nous i refresca les metadades.",
"TaskRefreshLibrary": "Escaneja la biblioteca de mitjans", "TaskRefreshLibrary": "Escaneja la biblioteca de mitjans",
"TaskRefreshChapterImagesDescription": "Crea les miniatures dels vídeos que tinguin capítols.", "TaskRefreshChapterImagesDescription": "Crea les miniatures dels vídeos que tinguin capítols.",
"TaskRefreshChapterImages": "Extreure les imatges dels capítols", "TaskRefreshChapterImages": "Extreure les imatges dels capítols",
"TaskCleanCacheDescription": "Elimina els arxius temporals que ja no són necessaris per al servidor.", "TaskCleanCacheDescription": "Elimina la memòria cau no necessària per al servidor.",
"TaskCleanCache": "Elimina arxius temporals", "TaskCleanCache": "Elimina la memòria cau",
"TasksChannelsCategory": "Canals d'internet", "TasksChannelsCategory": "Canals per internet",
"TasksApplicationCategory": "Aplicació", "TasksApplicationCategory": "Aplicatiu",
"TasksLibraryCategory": "Biblioteca", "TasksLibraryCategory": "Biblioteca",
"TasksMaintenanceCategory": "Manteniment", "TasksMaintenanceCategory": "Manteniment",
"TaskCleanActivityLogDescription": "Eliminat entrades del registre d'activitats mes antigues que l'antiguitat configurada.", "TaskCleanActivityLogDescription": "Eliminades les entrades del registre d'activitats més antigues que l'antiguitat configurada.",
"TaskCleanActivityLog": "Buidar el registre d'activitat", "TaskCleanActivityLog": "Buidar el registre d'activitat",
"Undefined": "Indefinit", "Undefined": "Indefinit",
"Forced": "Forçat", "Forced": "Forçat",
@ -128,9 +128,13 @@
"TaskRefreshTrickplayImages": "Generar miniatures de línia de temps", "TaskRefreshTrickplayImages": "Generar miniatures de línia de temps",
"TaskRefreshTrickplayImagesDescription": "Crear miniatures de línia de temps per vídeos en les biblioteques habilitades.", "TaskRefreshTrickplayImagesDescription": "Crear miniatures de línia de temps per vídeos en les biblioteques habilitades.",
"TaskCleanCollectionsAndPlaylistsDescription": "Esborra elements de col·leccions i llistes de reproducció que ja no existeixen.", "TaskCleanCollectionsAndPlaylistsDescription": "Esborra elements de col·leccions i llistes de reproducció que ja no existeixen.",
"TaskCleanCollectionsAndPlaylists": "Neteja col·leccions i llistes de reproducció", "TaskCleanCollectionsAndPlaylists": "Neteja les col·leccions i llistes de reproducció",
"TaskAudioNormalization": "Normalització d'Àudio", "TaskAudioNormalization": "Estabilització d'Àudio",
"TaskAudioNormalizationDescription": "Escaneja arxius per dades de normalització d'àudio.", "TaskAudioNormalizationDescription": "Escaneja arxius per dades d'estabilització d'àudio.",
"TaskDownloadMissingLyricsDescription": "Baixar lletres de les cançons", "TaskDownloadMissingLyricsDescription": "Baixar les lletres de les cançons",
"TaskDownloadMissingLyrics": "Baixar lletres que falten" "TaskDownloadMissingLyrics": "Baixar les lletres que falten",
"TaskExtractMediaSegments": "Escaneig de segments multimèdia",
"TaskExtractMediaSegmentsDescription": "Extreu o obté segments multimèdia usant els connectors MediaSegment activats.",
"TaskMoveTrickplayImages": "Migra la ubicació de la imatge de Trickplay",
"TaskMoveTrickplayImagesDescription": "Mou els fitxers trickplay existents segons la configuració de la biblioteca."
} }

View File

@ -1,5 +1,5 @@
{ {
"Albums": "Album", "Albums": "Albummer",
"AppDeviceValues": "App: {0}, Enhed: {1}", "AppDeviceValues": "App: {0}, Enhed: {1}",
"Application": "Applikation", "Application": "Applikation",
"Artists": "Kunstnere", "Artists": "Kunstnere",
@ -72,7 +72,7 @@
"ServerNameNeedsToBeRestarted": "{0} skal genstartes", "ServerNameNeedsToBeRestarted": "{0} skal genstartes",
"Shows": "Serier", "Shows": "Serier",
"Songs": "Sange", "Songs": "Sange",
"StartupEmbyServerIsLoading": "Jellyfin Server er i gang med at starte. Forsøg igen om et øjeblik.", "StartupEmbyServerIsLoading": "Jellyfin er i gang med at starte. Prøv igen om et øjeblik.",
"SubtitleDownloadFailureForItem": "Fejlet i download af undertekster for {0}", "SubtitleDownloadFailureForItem": "Fejlet i download af undertekster for {0}",
"SubtitleDownloadFailureFromForItem": "Undertekster kunne ikke hentes fra {0} til {1}", "SubtitleDownloadFailureFromForItem": "Undertekster kunne ikke hentes fra {0} til {1}",
"Sync": "Synkroniser", "Sync": "Synkroniser",
@ -93,13 +93,13 @@
"ValueSpecialEpisodeName": "Special - {0}", "ValueSpecialEpisodeName": "Special - {0}",
"VersionNumber": "Version {0}", "VersionNumber": "Version {0}",
"TaskDownloadMissingSubtitlesDescription": "Søger på internettet efter manglende undertekster baseret på metadata-konfigurationen.", "TaskDownloadMissingSubtitlesDescription": "Søger på internettet efter manglende undertekster baseret på metadata-konfigurationen.",
"TaskDownloadMissingSubtitles": "Hentede medie mangler undertekster", "TaskDownloadMissingSubtitles": "Hent manglende undertekster",
"TaskUpdatePluginsDescription": "Henter og installerer opdateringer for plugins, som er konfigurerede til at blive opdateret automatisk.", "TaskUpdatePluginsDescription": "Henter og installerer opdateringer for plugins, som er konfigurerede til at blive opdateret automatisk.",
"TaskUpdatePlugins": "Opdater Plugins", "TaskUpdatePlugins": "Opdater plugins",
"TaskCleanLogsDescription": "Sletter log-filer som er mere end {0} dage gamle.", "TaskCleanLogsDescription": "Sletter log-filer som er mere end {0} dage gamle.",
"TaskCleanLogs": "Ryd Log-mappe", "TaskCleanLogs": "Ryd log-mappe",
"TaskRefreshLibraryDescription": "Scanner dit mediebibliotek for nye filer og opdateret metadata.", "TaskRefreshLibraryDescription": "Scanner dit mediebibliotek for nye filer og opdateret metadata.",
"TaskRefreshLibrary": "Scan Mediebibliotek", "TaskRefreshLibrary": "Scan mediebibliotek",
"TaskCleanCacheDescription": "Sletter cache-filer som systemet ikke længere bruger.", "TaskCleanCacheDescription": "Sletter cache-filer som systemet ikke længere bruger.",
"TaskCleanCache": "Ryd cache-mappe", "TaskCleanCache": "Ryd cache-mappe",
"TasksChannelsCategory": "Internetkanaler", "TasksChannelsCategory": "Internetkanaler",
@ -108,33 +108,33 @@
"TasksMaintenanceCategory": "Vedligeholdelse", "TasksMaintenanceCategory": "Vedligeholdelse",
"TaskRefreshChapterImages": "Udtræk kapitelbilleder", "TaskRefreshChapterImages": "Udtræk kapitelbilleder",
"TaskRefreshChapterImagesDescription": "Laver miniaturebilleder for videoer, der har kapitler.", "TaskRefreshChapterImagesDescription": "Laver miniaturebilleder for videoer, der har kapitler.",
"TaskRefreshChannelsDescription": "Opdaterer information for internetkanal.", "TaskRefreshChannelsDescription": "Opdaterer information for internetkanaler.",
"TaskRefreshChannels": "Opdater Kanaler", "TaskRefreshChannels": "Opdater kanaler",
"TaskCleanTranscodeDescription": "Fjerner transcode-filer, som er mere end 1 dag gammel.", "TaskCleanTranscodeDescription": "Fjerner omkodningsfiler, som er mere end 1 dag gamle.",
"TaskCleanTranscode": "Tøm Transcode-mappen", "TaskCleanTranscode": "Tøm omkodningsmappen",
"TaskRefreshPeople": "Opdater Personer", "TaskRefreshPeople": "Opdater personer",
"TaskRefreshPeopleDescription": "Opdaterer metadata for skuespillere og instruktører i dit mediebibliotek.", "TaskRefreshPeopleDescription": "Opdaterer metadata for skuespillere og instruktører i dit mediebibliotek.",
"TaskCleanActivityLogDescription": "Sletter linjer i aktivitetsloggen ældre end den konfigurerede alder.", "TaskCleanActivityLogDescription": "Sletter linjer i aktivitetsloggen ældre end den konfigurerede alder.",
"TaskCleanActivityLog": "Ryd Aktivitetslog", "TaskCleanActivityLog": "Ryd aktivitetslog",
"Undefined": "Udefineret", "Undefined": "Udefineret",
"Forced": "Tvunget", "Forced": "Tvunget",
"Default": "Standard", "Default": "Standard",
"TaskOptimizeDatabaseDescription": "Komprimerer databasen og frigør plads. Denne handling køres efter at have scannet mediebiblioteket, eller efter at have lavet ændringer til databasen, for at højne ydeevnen.", "TaskOptimizeDatabaseDescription": "Komprimerer databasen for at frigøre plads. Denne handling køres efter at have scannet mediebiblioteket, eller efter at have lavet ændringer til databasen.",
"TaskOptimizeDatabase": "Optimér database", "TaskOptimizeDatabase": "Optimer database",
"TaskKeyframeExtractorDescription": "Udtrækker billeder fra videofiler for at lave mere præcise HLS-playlister. Denne opgave kan tage lang tid.", "TaskKeyframeExtractorDescription": "Udtrækker rammer fra videofiler for at lave mere præcise HLS-playlister. Denne opgave kan tage lang tid.",
"TaskKeyframeExtractor": "Udtræk af nøglebillede", "TaskKeyframeExtractor": "Udtræk nøglerammer",
"External": "Ekstern", "External": "Ekstern",
"HearingImpaired": "Hørehæmmet", "HearingImpaired": "Hørehæmmet",
"TaskRefreshTrickplayImages": "Generér Trickplay Billeder", "TaskRefreshTrickplayImages": "Generer trickplay-billeder",
"TaskRefreshTrickplayImagesDescription": "Laver trickplay forhåndsvisninger for videoer i aktiverede biblioteker.", "TaskRefreshTrickplayImagesDescription": "Laver trickplay-billeder for videoer i aktiverede biblioteker.",
"TaskCleanCollectionsAndPlaylists": "Ryd op i samlinger og afspilningslister", "TaskCleanCollectionsAndPlaylists": "Ryd op i samlinger og afspilningslister",
"TaskCleanCollectionsAndPlaylistsDescription": "Fjerner elementer fra samlinger og afspilningslister der ikke eksisterer længere.", "TaskCleanCollectionsAndPlaylistsDescription": "Fjerner elementer fra samlinger og afspilningslister der ikke eksisterer længere.",
"TaskAudioNormalizationDescription": "Skanner filer for data vedrørende audio-normalisering.", "TaskAudioNormalizationDescription": "Skanner filer for data vedrørende lydnormalisering.",
"TaskAudioNormalization": "Audio-normalisering", "TaskAudioNormalization": "Lydnormalisering",
"TaskDownloadMissingLyricsDescription": "Hentede sange mangler sangtekster", "TaskDownloadMissingLyricsDescription": "Søger på internettet efter manglende sangtekster baseret på metadata-konfigurationen",
"TaskDownloadMissingLyrics": "Hentede medie mangler sangtekster", "TaskDownloadMissingLyrics": "Hent manglende sangtekster",
"TaskExtractMediaSegments": "Scan mediesegment", "TaskExtractMediaSegments": "Scan for mediesegmenter",
"TaskMoveTrickplayImages": "Migrer billedelokation for Trickplay", "TaskMoveTrickplayImages": "Migrer billedelokationer for trickplay-billeder",
"TaskMoveTrickplayImagesDescription": "Flyt eksisterende trickplay-filer jævnfør biblioteksindstillilnger.", "TaskMoveTrickplayImagesDescription": "Flyt eksisterende trickplay-billeder jævnfør biblioteksindstillinger.",
"TaskExtractMediaSegmentsDescription": "Ekstraherer eller henter mediesegmenter fra plugins som understøtter MediaSegment." "TaskExtractMediaSegmentsDescription": "Udtrækker eller henter mediesegmenter fra plugins som understøtter MediaSegment."
} }

View File

@ -5,7 +5,7 @@
"Artists": "Interpreten", "Artists": "Interpreten",
"AuthenticationSucceededWithUserName": "{0} erfolgreich authentifiziert", "AuthenticationSucceededWithUserName": "{0} erfolgreich authentifiziert",
"Books": "Bücher", "Books": "Bücher",
"CameraImageUploadedFrom": "Ein neues Kamerafoto wurde von {0} hochgeladen", "CameraImageUploadedFrom": "Ein neues Kamerabild wurde von {0} hochgeladen",
"Channels": "Kanäle", "Channels": "Kanäle",
"ChapterNameValue": "Kapitel {0}", "ChapterNameValue": "Kapitel {0}",
"Collections": "Sammlungen", "Collections": "Sammlungen",
@ -18,7 +18,7 @@
"HeaderAlbumArtists": "Album-Interpreten", "HeaderAlbumArtists": "Album-Interpreten",
"HeaderContinueWatching": "Weiterschauen", "HeaderContinueWatching": "Weiterschauen",
"HeaderFavoriteAlbums": "Lieblingsalben", "HeaderFavoriteAlbums": "Lieblingsalben",
"HeaderFavoriteArtists": "Lieblings-Interpreten", "HeaderFavoriteArtists": "Lieblingsinterpreten",
"HeaderFavoriteEpisodes": "Lieblingsepisoden", "HeaderFavoriteEpisodes": "Lieblingsepisoden",
"HeaderFavoriteShows": "Lieblingsserien", "HeaderFavoriteShows": "Lieblingsserien",
"HeaderFavoriteSongs": "Lieblingslieder", "HeaderFavoriteSongs": "Lieblingslieder",

View File

@ -130,5 +130,11 @@
"TaskAudioNormalization": "Ομοιομορφία ήχου", "TaskAudioNormalization": "Ομοιομορφία ήχου",
"TaskAudioNormalizationDescription": "Ανίχνευση αρχείων για δεδομένα ομοιομορφίας ήχου.", "TaskAudioNormalizationDescription": "Ανίχνευση αρχείων για δεδομένα ομοιομορφίας ήχου.",
"TaskCleanCollectionsAndPlaylists": "Καθαρισμός συλλογών και λιστών αναπαραγωγής", "TaskCleanCollectionsAndPlaylists": "Καθαρισμός συλλογών και λιστών αναπαραγωγής",
"TaskCleanCollectionsAndPlaylistsDescription": "Αφαιρούνται στοιχεία από τις συλλογές και τις λίστες αναπαραγωγής που δεν υπάρχουν πλέον." "TaskCleanCollectionsAndPlaylistsDescription": "Αφαιρούνται στοιχεία από τις συλλογές και τις λίστες αναπαραγωγής που δεν υπάρχουν πλέον.",
"TaskMoveTrickplayImages": "Αλλαγή τοποθεσίας εικόνων Trickplay",
"TaskDownloadMissingLyrics": "Λήψη στίχων που λείπουν",
"TaskMoveTrickplayImagesDescription": "Μετακινεί τα υπάρχοντα αρχεία trickplay σύμφωνα με τις ρυθμίσεις της βιβλιοθήκης.",
"TaskDownloadMissingLyricsDescription": "Κατεβάζει στίχους για τραγούδια",
"TaskExtractMediaSegments": "Σάρωση τμημάτων πολυμέσων",
"TaskExtractMediaSegmentsDescription": "Εξάγει ή βρίσκει τμήματα πολυμέσων από επεκτάσεις που χρησιμοποιούν το MediaSegment."
} }

View File

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

View File

@ -131,5 +131,9 @@
"TaskAudioNormalizationDescription": "Analiza los archivos para normalizar el audio.", "TaskAudioNormalizationDescription": "Analiza los archivos para normalizar el audio.",
"TaskCleanCollectionsAndPlaylists": "Limpieza de colecciones y listas de reproducción", "TaskCleanCollectionsAndPlaylists": "Limpieza de colecciones y listas de reproducción",
"TaskDownloadMissingLyrics": "Descargar letra faltante", "TaskDownloadMissingLyrics": "Descargar letra faltante",
"TaskDownloadMissingLyricsDescription": "Descarga letras de canciones" "TaskDownloadMissingLyricsDescription": "Descarga letras de canciones",
"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"
} }

View File

@ -19,25 +19,25 @@
"Artists": "Artistak", "Artists": "Artistak",
"Albums": "Albumak", "Albums": "Albumak",
"TaskOptimizeDatabase": "Datu basea optimizatu", "TaskOptimizeDatabase": "Datu basea optimizatu",
"TaskDownloadMissingSubtitlesDescription": "Metadataren konfigurazioan oinarrituta falta diren azpitituluak bilatzen ditu interneten.", "TaskDownloadMissingSubtitlesDescription": "Falta diren azpitituluak bilatzen ditu interneten metadatuen konfigurazioaren arabera.",
"TaskDownloadMissingSubtitles": "Falta diren azpitituluak deskargatu", "TaskDownloadMissingSubtitles": "Falta diren azpitituluak deskargatu",
"TaskRefreshChannelsDescription": "Internet kanalen informazioa eguneratu.", "TaskRefreshChannelsDescription": "Internet kanalen informazioa eguneratu.",
"TaskRefreshChannels": "Kanalak eguneratu", "TaskRefreshChannels": "Kanalak eguneratu",
"TaskCleanTranscodeDescription": "Egun bat baino zaharragoak diren transcode fitxategiak ezabatzen ditu.", "TaskCleanTranscodeDescription": "Egun bat baino zaharragoak diren transkodifikazio fitxategiak ezabatzen ditu.",
"TaskCleanTranscode": "Transcode direktorioa garbitu", "TaskCleanTranscode": "Transkodifikazio direktorioa garbitu",
"TaskUpdatePluginsDescription": "Automatikoki eguneratzeko konfiguratutako pluginen eguneraketak deskargatu eta instalatzen ditu.", "TaskUpdatePluginsDescription": "Automatikoki deskargatu eta instalatu eguneraketak konfiguratutako pluginetarako.",
"TaskUpdatePlugins": "Pluginak eguneratu", "TaskUpdatePlugins": "Pluginak eguneratu",
"TaskRefreshPeopleDescription": "Zure liburutegiko aktore eta zuzendarien metadata eguneratzen du.", "TaskRefreshPeopleDescription": "Zure liburutegiko aktore eta zuzendarien metadatuak eguneratzen ditu.",
"TaskRefreshPeople": "Jendea eguneratu", "TaskRefreshPeople": "Jendea eguneratu",
"TaskCleanLogsDescription": "{0} egun baino zaharragoak diren log fitxategiak ezabatzen ditu.", "TaskCleanLogsDescription": "{0} egun baino zaharragoak diren log fitxategiak ezabatzen ditu.",
"TaskCleanLogs": "Log direktorioa garbitu", "TaskCleanLogs": "Log direktorioa garbitu",
"TaskRefreshLibraryDescription": "Zure multimedia liburutegia eskaneatzen du fitxategi berriak eta metadatak eguneratzeko.", "TaskRefreshLibraryDescription": "Zure multimedia liburutegia eskaneatzen du fitxategi berriak eta metadatuak eguneratzeko.",
"TaskRefreshLibrary": "Multimedia Liburutegia eskaneatu", "TaskRefreshLibrary": "Multimedia liburutegia eskaneatu",
"TaskRefreshChapterImagesDescription": "Kapituluak dituzten bideoen miniaturak sortzen ditu.", "TaskRefreshChapterImagesDescription": "Kapituluak dituzten bideoen miniaturak sortzen ditu.",
"TaskRefreshChapterImages": "Kapituluen irudiak erauzi", "TaskRefreshChapterImages": "Kapituluen irudiak erauzi",
"TaskCleanCacheDescription": "Sistemak behar ez dituen cache fitxategiak ezabatzen ditu.", "TaskCleanCacheDescription": "Sistemak behar ez dituen cache fitxategiak ezabatzen ditu.",
"TaskCleanCache": "Cache Directorioa garbitu", "TaskCleanCache": "Cache direktorioa garbitu",
"TaskCleanActivityLogDescription": "Konfiguratuta data baino zaharragoak diren log-ak ezabatu.", "TaskCleanActivityLogDescription": "Konfiguratutako baino zaharragoak diren jarduera-log sarrerak ezabatzen ditu.",
"TaskCleanActivityLog": "Erabilera Log-a garbitu", "TaskCleanActivityLog": "Erabilera Log-a garbitu",
"TasksChannelsCategory": "Internet Kanalak", "TasksChannelsCategory": "Internet Kanalak",
"TasksApplicationCategory": "Aplikazioa", "TasksApplicationCategory": "Aplikazioa",
@ -45,22 +45,22 @@
"TasksMaintenanceCategory": "Mantenua", "TasksMaintenanceCategory": "Mantenua",
"VersionNumber": "Bertsioa {0}", "VersionNumber": "Bertsioa {0}",
"ValueHasBeenAddedToLibrary": "{0} zure multimedia liburutegian gehitu da", "ValueHasBeenAddedToLibrary": "{0} zure multimedia liburutegian gehitu da",
"UserStoppedPlayingItemWithValues": "{0}-ek {1} ikusteaz bukatu du {2}-(a)n", "UserStoppedPlayingItemWithValues": "{0} {1} ikusten bukatu du {2}-(e)n",
"UserStartedPlayingItemWithValues": "{0} {1} ikusten ari da {2}-(a)n", "UserStartedPlayingItemWithValues": "{0} {1} ikusten ari da {2}-(e)n",
"UserPolicyUpdatedWithName": "{0} Erabiltzailearen politikak aldatu dira", "UserPolicyUpdatedWithName": "{0} erabiltzailearen politikak aldatu dira",
"UserPasswordChangedWithName": "{0} Erabiltzailearen pasahitza aldatu da", "UserPasswordChangedWithName": "{0} erabiltzailearen pasahitza aldatu da",
"UserOnlineFromDevice": "{0} online dago {1}-tik", "UserOnlineFromDevice": "{0} online dago {1}-(e)tik",
"UserOfflineFromDevice": "{0} {1}-tik deskonektatu da", "UserOfflineFromDevice": "{0} {1}-(e)tik deskonektatu da",
"UserLockedOutWithName": "{0} Erabiltzailea blokeatu da", "UserLockedOutWithName": "{0} erabiltzailea blokeatu da",
"UserDownloadingItemWithValues": "{1} {0}-tik deskargatzen", "UserDownloadingItemWithValues": "{0} {1} deskargatzen ari da",
"UserDeletedWithName": "{0} Erabiltzailea ezabatu da", "UserDeletedWithName": "{0} Erabiltzailea ezabatu da",
"UserCreatedWithName": "{0} Erabiltzailea sortu da", "UserCreatedWithName": "{0} Erabiltzailea sortu da",
"User": "Erabiltzailea", "User": "Erabiltzailea",
"Undefined": "Ezezaguna", "Undefined": "Ezezaguna",
"TvShows": "TB showak", "TvShows": "TB serieak",
"System": "Sistema", "System": "Sistema",
"SubtitleDownloadFailureFromForItem": "{1}-en azpitutuluak {0} deskargatzean huts egin du", "SubtitleDownloadFailureFromForItem": "{1}-en azpitutuluak {0}-tik deskargatzeak huts egin du",
"StartupEmbyServerIsLoading": "Jellyfin zerbitzaria kargatzen. Saiatu berriro beranduxeago.", "StartupEmbyServerIsLoading": "Jellyfin zerbitzaria kargatzen. Saiatu berriro beranduago.",
"ServerNameNeedsToBeRestarted": "{0} berrabiarazi behar da", "ServerNameNeedsToBeRestarted": "{0} berrabiarazi behar da",
"ScheduledTaskStartedWithName": "{0} hasi da", "ScheduledTaskStartedWithName": "{0} hasi da",
"ScheduledTaskFailedWithName": "{0} huts egin du", "ScheduledTaskFailedWithName": "{0} huts egin du",
@ -89,26 +89,26 @@
"NameSeasonNumber": "{0} Denboraldia", "NameSeasonNumber": "{0} Denboraldia",
"NameInstallFailed": "{0} instalazioak huts egin du", "NameInstallFailed": "{0} instalazioak huts egin du",
"Music": "Musika", "Music": "Musika",
"MixedContent": "Denetariko edukia", "MixedContent": "Eduki mistoa",
"MessageServerConfigurationUpdated": "Zerbitzariaren konfigurazioa eguneratu da", "MessageServerConfigurationUpdated": "Zerbitzariaren konfigurazioa eguneratu da",
"MessageNamedServerConfigurationUpdatedWithValue": "Zerbitzariaren konfigurazio {0} atala eguneratu da", "MessageNamedServerConfigurationUpdatedWithValue": "Zerbitzariaren {0} konfigurazio atala eguneratu da",
"MessageApplicationUpdatedTo": "Jellyfin zerbitzaria {0}-ra eguneratu da", "MessageApplicationUpdatedTo": "Jellyfin zerbitzaria {0}-ra eguneratu da",
"MessageApplicationUpdated": "Jellyfin zerbitzaria eguneratu da", "MessageApplicationUpdated": "Jellyfin zerbitzaria eguneratu da",
"Latest": "Azkena", "Latest": "Azkena",
"LabelRunningTimeValue": "Denbora martxan: {0}", "LabelRunningTimeValue": "Iraupena: {0}",
"LabelIpAddressValue": "IP helbidea: {0}", "LabelIpAddressValue": "IP helbidea: {0}",
"ItemRemovedWithName": "{0} liburutegitik ezabatu da", "ItemRemovedWithName": "{0} liburutegitik kendu da",
"ItemAddedWithName": "{0} liburutegira gehitu da", "ItemAddedWithName": "{0} liburutegira gehitu da",
"HomeVideos": "Etxeko bideoak", "HomeVideos": "Etxeko bideoak",
"HeaderNextUp": "Nobedadeak", "HeaderNextUp": "Hurrengoa",
"HeaderLiveTV": "Zuzeneko TB", "HeaderLiveTV": "Zuzeneko TB",
"HeaderFavoriteSongs": "Gogoko abestiak", "HeaderFavoriteSongs": "Gogoko abestiak",
"HeaderFavoriteShows": "Gogoko showak", "HeaderFavoriteShows": "Gogoko serieak",
"HeaderFavoriteEpisodes": "Gogoko atalak", "HeaderFavoriteEpisodes": "Gogoko atalak",
"HeaderFavoriteArtists": "Gogoko artistak", "HeaderFavoriteArtists": "Gogoko artistak",
"HeaderFavoriteAlbums": "Gogoko albumak", "HeaderFavoriteAlbums": "Gogoko albumak",
"Forced": "Behartuta", "Forced": "Behartuta",
"FailedLoginAttemptWithUserName": "Login egiten akatsa, saiatu hemen {0}", "FailedLoginAttemptWithUserName": "{0}-tik saioa hasteak huts egin du",
"External": "Kanpokoa", "External": "Kanpokoa",
"DeviceOnlineWithName": "{0} konektatu da", "DeviceOnlineWithName": "{0} konektatu da",
"DeviceOfflineWithName": "{0} deskonektatu da", "DeviceOfflineWithName": "{0} deskonektatu da",
@ -117,13 +117,23 @@
"AuthenticationSucceededWithUserName": "{0} ongi autentifikatu da", "AuthenticationSucceededWithUserName": "{0} ongi autentifikatu da",
"Application": "Aplikazioa", "Application": "Aplikazioa",
"AppDeviceValues": "App: {0}, Gailua: {1}", "AppDeviceValues": "App: {0}, Gailua: {1}",
"HearingImpaired": "Entzunaldia aldatua", "HearingImpaired": "Entzumen urritasuna",
"ProviderValue": "Hornitzailea: {0}", "ProviderValue": "Hornitzailea: {0}",
"TaskKeyframeExtractorDescription": "Bideo fitxategietako fotograma gakoak ateratzen ditu HLS erreprodukzio-zerrenda zehatzagoak sortzeko. Zeregin honek denbora asko iraun dezake.", "TaskKeyframeExtractorDescription": "Bideo fitxategietako fotograma gakoak ateratzen ditu HLS erreprodukzio-zerrenda zehatzagoak sortzeko. Zeregin honek denbora asko iraun dezake.",
"HeaderRecordingGroups": "Grabaketa taldeak", "HeaderRecordingGroups": "Grabaketa taldeak",
"Inherit": "Oinordetu", "Inherit": "Oinordetu",
"TaskOptimizeDatabaseDescription": "Datu-basea trinkotu eta bertatik espazioa askatzen du. Liburutegia eskaneatu ondoren edo datu-basean aldaketak egin ondoren ataza hau exekutatzeak errendimendua hobetu lezake.", "TaskOptimizeDatabaseDescription": "Datu-basea trinkotu eta bertatik espazioa askatzen du. Liburutegia eskaneatu ondoren edo datu-basean aldaketak egin ondoren ataza hau exekutatzeak errendimendua hobetu lezake.",
"TaskKeyframeExtractor": "Fotograma gakoen erauzgailua", "TaskKeyframeExtractor": "Fotograma gakoen erauzgailua",
"TaskRefreshTrickplayImages": "\"Trickplay Irudiak Sortu", "TaskRefreshTrickplayImages": "Trickplay irudiak sortu",
"TaskRefreshTrickplayImagesDescription": "Bideoentzako trickplay aurrebistak sortzen ditu gaitutako liburutegietan." "TaskRefreshTrickplayImagesDescription": "Bideoentzako trickplay aurrebistak sortzen ditu gaitutako liburutegietan.",
"TaskAudioNormalization": "Audio normalizazioa",
"TaskDownloadMissingLyrics": "Deskargatu falta diren letrak",
"TaskDownloadMissingLyricsDescription": "Deskargatu abestientzako letrak",
"TaskExtractMediaSegments": "Multimedia segmentuen eskaneoa",
"TaskCleanCollectionsAndPlaylistsDescription": "Jada existitzen ez diren bildumak eta erreprodukzio-zerrendak kentzen ditu.",
"TaskCleanCollectionsAndPlaylists": "Garbitu bildumak eta erreprodukzio-zerrendak",
"TaskExtractMediaSegmentsDescription": "Media segmentuak atera edo lortzen ditu MediaSegment gaituta duten pluginetik.",
"TaskMoveTrickplayImages": "Aldatu Trickplay irudien kokalekua",
"TaskMoveTrickplayImagesDescription": "Lehendik dauden trickplay fitxategiak liburutegiaren ezarpenen arabera mugitzen dira.",
"TaskAudioNormalizationDescription": "Audio normalizazio datuak lortzeko fitxategiak eskaneatzen ditu."
} }

View File

@ -130,5 +130,10 @@
"TaskCleanCollectionsAndPlaylists": "Puhdista kokoelmat ja soittolistat", "TaskCleanCollectionsAndPlaylists": "Puhdista kokoelmat ja soittolistat",
"TaskAudioNormalization": "Äänenvoimakkuuden normalisointi", "TaskAudioNormalization": "Äänenvoimakkuuden normalisointi",
"TaskAudioNormalizationDescription": "Etsii tiedostoista äänenvoimakkuuden normalisointitietoja.", "TaskAudioNormalizationDescription": "Etsii tiedostoista äänenvoimakkuuden normalisointitietoja.",
"TaskDownloadMissingLyrics": "Lataa puuttuva lyriikka" "TaskDownloadMissingLyrics": "Lataa puuttuva lyriikka",
"TaskExtractMediaSegments": "Mediasegmentin skannaus",
"TaskDownloadMissingLyricsDescription": "Ladataan sanoituksia",
"TaskExtractMediaSegmentsDescription": "Poimii tai hankkii mediasegmenttejä MediaSegment-yhteensopivista laajennuksista.",
"TaskMoveTrickplayImages": "Siirrä Trickplay-kuvien sijainti",
"TaskMoveTrickplayImagesDescription": "Siirtää olemassa olevia trickplay-tiedostoja kirjaston asetusten mukaan."
} }

View File

@ -135,5 +135,6 @@
"TaskDownloadMissingLyricsDescription": "Téléchargement des paroles des chansons", "TaskDownloadMissingLyricsDescription": "Téléchargement des paroles des chansons",
"TaskMoveTrickplayImagesDescription": "Déplace les fichiers trickplay existants en fonction des paramètres de la bibliothèque.", "TaskMoveTrickplayImagesDescription": "Déplace les fichiers trickplay existants en fonction des paramètres de la bibliothèque.",
"TaskDownloadMissingLyrics": "Télécharger les paroles des chansons manquantes", "TaskDownloadMissingLyrics": "Télécharger les paroles des chansons manquantes",
"TaskMoveTrickplayImages": "Changer l'emplacement des images Trickplay" "TaskMoveTrickplayImages": "Changer l'emplacement des images Trickplay",
"TaskExtractMediaSegmentsDescription": "Extrait ou obtient des segments de média à partir des plugins compatibles avec MediaSegment."
} }

View File

@ -1,16 +1,139 @@
{ {
"Albums": "Albaim", "Albums": "Albaim",
"Artists": "Ealaíontóir", "Artists": "Ealaíontóirí",
"AuthenticationSucceededWithUserName": "{0} fíordheimhnithe", "AuthenticationSucceededWithUserName": "D'éirigh le fíordheimhniú {0}",
"Books": "leabhair", "Books": "Leabhair",
"CameraImageUploadedFrom": "Tá íomhá ceamara nua uaslódáilte ó {0}", "CameraImageUploadedFrom": "Uaslódáladh íomhá ceamara nua ó {0}",
"Channels": "Cainéil", "Channels": "Cainéil",
"ChapterNameValue": "Caibidil {0}", "ChapterNameValue": "Caibidil {0}",
"Collections": "Bailiúcháin", "Collections": "Bailiúcháin",
"Default": "Mainneachtain", "Default": "Réamhshocrú",
"DeviceOfflineWithName": "scoireadh {0}", "DeviceOfflineWithName": "Tá {0} dícheangailte",
"DeviceOnlineWithName": "{0} ceangailte", "DeviceOnlineWithName": "Tá {0} nasctha",
"External": "Forimeallach", "External": "Seachtrach",
"FailedLoginAttemptWithUserName": "Iarracht ar theip ar fhíordheimhniú ó {0}", "FailedLoginAttemptWithUserName": "Theip ar iarracht logáil isteach ó {0}",
"Favorites": "Ceanáin" "Favorites": "Ceanáin",
"TaskExtractMediaSegments": "Scanadh Deighleog na Meán",
"TaskMoveTrickplayImages": "Imirce Suíomh Íomhá Trickplay",
"TaskDownloadMissingLyrics": "Íosluchtaigh liricí ar iarraidh",
"TaskKeyframeExtractor": "Keyframe Eastarraingteoir",
"TaskAudioNormalization": "Normalú Fuaime",
"TaskAudioNormalizationDescription": "Scanann comhaid le haghaidh sonraí normalaithe fuaime.",
"TaskRefreshLibraryDescription": "Déanann sé do leabharlann meán a scanadh le haghaidh comhaid nua agus athnuachana meiteashonraí.",
"TaskCleanLogs": "Eolaire Logchomhad Glan",
"TaskCleanLogsDescription": "Scriostar comhaid loga atá níos mó ná {0} lá d'aois.",
"TaskRefreshPeopleDescription": "Nuashonraítear meiteashonraí daisteoirí agus stiúrthóirí i do leabharlann meán.",
"TaskRefreshTrickplayImages": "Gin Íomhánna Trickplay",
"TaskRefreshTrickplayImagesDescription": "Cruthaíonn sé réamhamhairc trickplay le haghaidh físeáin i leabharlanna cumasaithe.",
"TaskRefreshChannels": "Cainéil Athnuaigh",
"TaskRefreshChannelsDescription": "Athnuachan eolas faoi chainéil idirlín.",
"TaskOptimizeDatabase": "Bunachar sonraí a bharrfheabhsú",
"TaskKeyframeExtractorDescription": "Baintear eochairfhrámaí as comhaid físe chun seinmliostaí HLS níos cruinne a chruthú. Féadfaidh an tasc seo a bheith ar siúl ar feadh i bhfad.",
"TaskCleanCollectionsAndPlaylistsDescription": "Baintear míreanna as bailiúcháin agus seinmliostaí nach ann dóibh a thuilleadh.",
"TaskDownloadMissingLyricsDescription": "Íosluchtaigh liricí do na hamhráin",
"TaskUpdatePluginsDescription": "Íoslódálann agus suiteálann nuashonruithe do bhreiseáin atá cumraithe le nuashonrú go huathoibríoch.",
"TaskDownloadMissingSubtitlesDescription": "Déanann sé cuardach ar an idirlíon le haghaidh fotheidil atá ar iarraidh bunaithe ar chumraíocht meiteashonraí.",
"TaskExtractMediaSegmentsDescription": "Sliocht nó faigheann codanna meán ó bhreiseáin chumasaithe MediaSegment.",
"TaskCleanCollectionsAndPlaylists": "Glan suas bailiúcháin agus seinmliostaí",
"TaskOptimizeDatabaseDescription": "Comhdhlúthaíonn bunachar sonraí agus gearrtar spás saor in aisce. Má ritheann tú an tasc seo tar éis scanadh a dhéanamh ar an leabharlann nó athruithe eile a dhéanamh a thugann le tuiscint gur cheart go bhfeabhsófaí an fheidhmíocht.",
"TaskMoveTrickplayImagesDescription": "Bogtar comhaid trickplay atá ann cheana de réir socruithe na leabharlainne.",
"AppDeviceValues": "Aip: {0}, Gléas: {1}",
"Application": "Feidhmchlár",
"Folders": "Fillteáin",
"Forced": "Éigean",
"Genres": "Seánraí",
"HeaderAlbumArtists": "Ealaíontóirí albam",
"HeaderContinueWatching": "Leanúint ar aghaidh ag Breathnú",
"HeaderFavoriteAlbums": "Albam is fearr leat",
"HeaderFavoriteArtists": "Ealaíontóirí is Fearr",
"HeaderFavoriteEpisodes": "Eipeasóid is fearr leat",
"HeaderFavoriteShows": "Seónna is Fearr",
"HeaderFavoriteSongs": "Amhráin is fearr leat",
"HeaderLiveTV": "Teilifís beo",
"HeaderNextUp": "Ar Aghaidh Suas",
"HeaderRecordingGroups": "Grúpaí Taifeadta",
"HearingImpaired": "Lag éisteachta",
"HomeVideos": "Físeáin Baile",
"Inherit": "Oidhreacht",
"ItemAddedWithName": "Cuireadh {0} leis an leabharlann",
"ItemRemovedWithName": "Baineadh {0} den leabharlann",
"LabelIpAddressValue": "Seoladh IP: {0}",
"LabelRunningTimeValue": "Am rite: {0}",
"Latest": "Is déanaí",
"MessageApplicationUpdated": "Tá Freastalaí Jellyfin nuashonraithe",
"MessageApplicationUpdatedTo": "Nuashonraíodh Freastalaí Jellyfin go {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Nuashonraíodh an chuid cumraíochta freastalaí {0}",
"MessageServerConfigurationUpdated": "Nuashonraíodh cumraíocht an fhreastalaí",
"MixedContent": "Ábhar measctha",
"Movies": "Scannáin",
"Music": "Ceol",
"MusicVideos": "Físeáin Ceoil",
"NameInstallFailed": "Theip ar shuiteáil {0}",
"NameSeasonNumber": "Séasúr {0}",
"NameSeasonUnknown": "Séasúr Anaithnid",
"NewVersionIsAvailable": "Tá leagan nua de Jellyfin Server ar fáil le híoslódáil.",
"NotificationOptionApplicationUpdateAvailable": "Nuashonrú feidhmchláir ar fáil",
"NotificationOptionApplicationUpdateInstalled": "Nuashonrú feidhmchláir suiteáilte",
"NotificationOptionAudioPlayback": "Cuireadh tús le hathsheinm fuaime",
"NotificationOptionAudioPlaybackStopped": "Cuireadh deireadh le hathsheinm fuaime",
"NotificationOptionCameraImageUploaded": "Íosluchtaigh grianghraf ceamara",
"NotificationOptionInstallationFailed": "Teip suiteála",
"NotificationOptionNewLibraryContent": "Ábhar nua curtha leis",
"NotificationOptionPluginError": "Teip breiseán",
"NotificationOptionPluginInstalled": "Breiseán suiteáilte",
"NotificationOptionPluginUninstalled": "Breiseán díshuiteáilte",
"NotificationOptionPluginUpdateInstalled": "Nuashonrú breiseán suiteáilte",
"NotificationOptionServerRestartRequired": "Teastaíonn atosú an fhreastalaí",
"NotificationOptionTaskFailed": "Teip tasc sceidealta",
"NotificationOptionUserLockedOut": "Úsáideoir glasáilte amach",
"NotificationOptionVideoPlayback": "Cuireadh tús le hathsheinm físe",
"NotificationOptionVideoPlaybackStopped": "Cuireadh deireadh le hathsheinm físe",
"Photos": "Grianghraif",
"Playlists": "Seinmliostaí",
"Plugin": "Breiseán",
"PluginInstalledWithName": "Suiteáladh {0}",
"PluginUninstalledWithName": "Díshuiteáladh {0}",
"PluginUpdatedWithName": "Nuashonraíodh {0}",
"ProviderValue": "Soláthraí: {0}",
"ScheduledTaskFailedWithName": "Theip ar {0}",
"ScheduledTaskStartedWithName": "Thosaigh {0}",
"ServerNameNeedsToBeRestarted": "Ní mór {0} a atosú",
"Shows": "Seónna",
"Songs": "Amhráin",
"StartupEmbyServerIsLoading": "Tá freastalaí Jellyfin á luchtú. Bain triail eile as gan mhoill.",
"SubtitleDownloadFailureFromForItem": "Theip ar fhotheidil a íoslódáil ó {0} le haghaidh {1}",
"Sync": "Sioncrónaigh",
"System": "Córas",
"TvShows": "Seónna Teilifíse",
"Undefined": "Neamhshainithe",
"User": "Úsáideoir",
"UserCreatedWithName": "Cruthaíodh úsáideoir {0}",
"UserDeletedWithName": "Scriosadh úsáideoir {0}",
"UserDownloadingItemWithValues": "Tá {0} á íoslódáil {1}",
"UserLockedOutWithName": "Tá úsáideoir {0} glasáilte amach",
"UserOfflineFromDevice": "Tá {0} dícheangailte ó {1}",
"UserOnlineFromDevice": "Tá {0} ar líne ó {1}",
"UserPasswordChangedWithName": "Athraíodh pasfhocal don úsáideoir {0}",
"UserPolicyUpdatedWithName": "Nuashonraíodh polasaí úsáideora le haghaidh {0}",
"UserStartedPlayingItemWithValues": "Tá {0} ag seinnt {1} ar {2}",
"UserStoppedPlayingItemWithValues": "Chríochnaigh {0} ag imirt {1} ar {2}",
"ValueHasBeenAddedToLibrary": "Cuireadh {0} le do leabharlann meán",
"ValueSpecialEpisodeName": "Speisialta - {0}",
"VersionNumber": "Leagan {0}",
"TasksMaintenanceCategory": "Cothabháil",
"TasksLibraryCategory": "Leabharlann",
"TasksApplicationCategory": "Feidhmchlár",
"TasksChannelsCategory": "Cainéil Idirlín",
"TaskCleanActivityLog": "Loga Gníomhaíochta Glan",
"TaskCleanActivityLogDescription": "Scrios iontrálacha loga gníomhaíochta atá níos sine ná an aois chumraithe.",
"TaskCleanCache": "Eolaire Taisce Glan",
"TaskCleanCacheDescription": "Scriostar comhaid taisce nach bhfuil ag teastáil ón gcóras a thuilleadh.",
"TaskRefreshChapterImages": "Sliocht Íomhánna Caibidil",
"TaskRefreshChapterImagesDescription": "Cruthaíonn mionsamhlacha le haghaidh físeáin a bhfuil caibidlí acu.",
"TaskRefreshLibrary": "Scan Leabharlann na Meán",
"TaskRefreshPeople": "Daoine Athnuaigh",
"TaskUpdatePlugins": "Nuashonraigh Breiseáin",
"TaskCleanTranscodeDescription": "Scriostar comhaid traschódaithe níos mó ná lá amháin d'aois.",
"TaskCleanTranscode": "Eolaire Transcode Glan",
"TaskDownloadMissingSubtitles": "Íosluchtaigh fotheidil ar iarraidh"
} }

View File

@ -16,7 +16,7 @@
"Folders": "תיקיות", "Folders": "תיקיות",
"Genres": "ז׳אנרים", "Genres": "ז׳אנרים",
"HeaderAlbumArtists": "אמני האלבום", "HeaderAlbumArtists": "אמני האלבום",
"HeaderContinueWatching": "להמשיך לצפות", "HeaderContinueWatching": "המשך צפייה",
"HeaderFavoriteAlbums": "אלבומים מועדפים", "HeaderFavoriteAlbums": "אלבומים מועדפים",
"HeaderFavoriteArtists": "אמנים מועדפים", "HeaderFavoriteArtists": "אמנים מועדפים",
"HeaderFavoriteEpisodes": "פרקים מועדפים", "HeaderFavoriteEpisodes": "פרקים מועדפים",
@ -32,8 +32,8 @@
"LabelIpAddressValue": "Ip כתובת: {0}", "LabelIpAddressValue": "Ip כתובת: {0}",
"LabelRunningTimeValue": "משך צפייה: {0}", "LabelRunningTimeValue": "משך צפייה: {0}",
"Latest": "אחרון", "Latest": "אחרון",
"MessageApplicationUpdated": "שרת הJellyfin עודכן", "MessageApplicationUpdated": "שרת ג'ליפין עודכן",
"MessageApplicationUpdatedTo": "שרת ה־Jellyfin עודכן לגרסה {0}", "MessageApplicationUpdatedTo": "שרת ג'ליפין עודכן לגרסה {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "סעיף הגדרת השרת {0} עודכן", "MessageNamedServerConfigurationUpdatedWithValue": "סעיף הגדרת השרת {0} עודכן",
"MessageServerConfigurationUpdated": "תצורת השרת עודכנה", "MessageServerConfigurationUpdated": "תצורת השרת עודכנה",
"MixedContent": "תוכן מעורב", "MixedContent": "תוכן מעורב",
@ -43,7 +43,7 @@
"NameInstallFailed": "התקנת {0} נכשלה", "NameInstallFailed": "התקנת {0} נכשלה",
"NameSeasonNumber": "עונה {0}", "NameSeasonNumber": "עונה {0}",
"NameSeasonUnknown": "עונה לא ידועה", "NameSeasonUnknown": "עונה לא ידועה",
"NewVersionIsAvailable": "גרסה חדשה של שרת Jellyfin זמינה להורדה.", "NewVersionIsAvailable": "גרסה חדשה של שרת ג'ליפין זמינה להורדה.",
"NotificationOptionApplicationUpdateAvailable": "קיים עדכון זמין ליישום", "NotificationOptionApplicationUpdateAvailable": "קיים עדכון זמין ליישום",
"NotificationOptionApplicationUpdateInstalled": "עדכון ליישום הותקן", "NotificationOptionApplicationUpdateInstalled": "עדכון ליישום הותקן",
"NotificationOptionAudioPlayback": "ניגון שמע החל", "NotificationOptionAudioPlayback": "ניגון שמע החל",
@ -72,7 +72,7 @@
"ServerNameNeedsToBeRestarted": "{0} דורש הפעלה מחדש", "ServerNameNeedsToBeRestarted": "{0} דורש הפעלה מחדש",
"Shows": "סדרות", "Shows": "סדרות",
"Songs": "שירים", "Songs": "שירים",
"StartupEmbyServerIsLoading": "שרת Jellyfin בהליכי טעינה. נא לנסות שנית בהקדם.", "StartupEmbyServerIsLoading": "שרת ג'ליפין טוען. נא לנסות שוב בקרוב.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "הורדת כתוביות מ־{0} עבור {1} נכשלה", "SubtitleDownloadFailureFromForItem": "הורדת כתוביות מ־{0} עבור {1} נכשלה",
"Sync": "סנכרון", "Sync": "סנכרון",
@ -133,8 +133,8 @@
"TaskCleanCollectionsAndPlaylists": "מנקה אוספים ורשימות השמעה", "TaskCleanCollectionsAndPlaylists": "מנקה אוספים ורשימות השמעה",
"TaskDownloadMissingLyrics": "הורדת מילים חסרות", "TaskDownloadMissingLyrics": "הורדת מילים חסרות",
"TaskDownloadMissingLyricsDescription": "הורדת מילים לשירים", "TaskDownloadMissingLyricsDescription": "הורדת מילים לשירים",
"TaskMoveTrickplayImages": "מעביר את מיקום תמונות Trickplay", "TaskMoveTrickplayImages": "העברת מיקום התמונות",
"TaskExtractMediaSegments": "סריקת מדיה", "TaskExtractMediaSegments": "סריקת מדיה",
"TaskExtractMediaSegmentsDescription": "מחלץ חלקי מדיה מתוספים המאפשרים זאת.", "TaskExtractMediaSegmentsDescription": "מחלץ חלקי מדיה מתוספים המאפשרים זאת.",
"TaskMoveTrickplayImagesDescription": "מזיז קבצי trickplay קיימים בהתאם להגדרות הספרייה." "TaskMoveTrickplayImagesDescription": "הזזת קבצי טריקפליי קיימים בהתאם להגדרות הספרייה."
} }

View File

@ -99,7 +99,7 @@
"ValueHasBeenAddedToLibrary": "{0} आपके माध्यम ग्रन्थालय में उपजात हो गया हैं", "ValueHasBeenAddedToLibrary": "{0} आपके माध्यम ग्रन्थालय में उपजात हो गया हैं",
"TasksLibraryCategory": "संग्रहालय", "TasksLibraryCategory": "संग्रहालय",
"TaskOptimizeDatabase": "जानकारी प्रवृद्धि", "TaskOptimizeDatabase": "जानकारी प्रवृद्धि",
"TaskDownloadMissingSubtitles": "असमेत अनुलेख को अवाहरति करें", "TaskDownloadMissingSubtitles": "लापता अनुलेख डाउनलोड करें",
"TaskRefreshLibrary": "माध्यम संग्राहत को छाने", "TaskRefreshLibrary": "माध्यम संग्राहत को छाने",
"TaskCleanActivityLog": "क्रियाकलाप लॉग साफ करें", "TaskCleanActivityLog": "क्रियाकलाप लॉग साफ करें",
"TasksChannelsCategory": "इंटरनेट प्रणाली", "TasksChannelsCategory": "इंटरनेट प्रणाली",
@ -127,5 +127,7 @@
"TaskRefreshTrickplayImages": "ट्रिकप्लै चित्रों को सृजन करे", "TaskRefreshTrickplayImages": "ट्रिकप्लै चित्रों को सृजन करे",
"TaskRefreshTrickplayImagesDescription": "नियत संग्रहों में चलचित्रों का ट्रीकप्लै दर्शनों को सृजन करे.", "TaskRefreshTrickplayImagesDescription": "नियत संग्रहों में चलचित्रों का ट्रीकप्लै दर्शनों को सृजन करे.",
"TaskAudioNormalization": "श्रव्य सामान्यीकरण", "TaskAudioNormalization": "श्रव्य सामान्यीकरण",
"TaskAudioNormalizationDescription": "श्रव्य सामान्यीकरण के लिए फाइलें अन्वेषण करें" "TaskAudioNormalizationDescription": "श्रव्य सामान्यीकरण के लिए फाइलें अन्वेषण करें",
"TaskDownloadMissingLyrics": "लापता गानों के बोल डाउनलोड करेँ",
"TaskDownloadMissingLyricsDescription": "गानों के बोल डाउनलोड करता है"
} }

View File

@ -0,0 +1,3 @@
{
"Books": "liv"
}

View File

@ -134,5 +134,7 @@
"TaskDownloadMissingLyricsDescription": "Scarica testi per le canzoni", "TaskDownloadMissingLyricsDescription": "Scarica testi per le canzoni",
"TaskDownloadMissingLyrics": "Scarica testi mancanti", "TaskDownloadMissingLyrics": "Scarica testi mancanti",
"TaskMoveTrickplayImages": "Sposta le immagini Trickplay", "TaskMoveTrickplayImages": "Sposta le immagini Trickplay",
"TaskMoveTrickplayImagesDescription": "Sposta le immagini Trickplay esistenti secondo la configurazione della libreria." "TaskMoveTrickplayImagesDescription": "Sposta le immagini Trickplay esistenti secondo la configurazione della libreria.",
"TaskExtractMediaSegmentsDescription": "Estrae o ottiene segmenti multimediali dai plugin abilitati MediaSegment.",
"TaskExtractMediaSegments": "Scansiona Segmento Media"
} }

View File

@ -134,5 +134,6 @@
"TaskExtractMediaSegments": "メディアセグメントを読み取る", "TaskExtractMediaSegments": "メディアセグメントを読み取る",
"TaskMoveTrickplayImages": "Trickplayの画像を移動", "TaskMoveTrickplayImages": "Trickplayの画像を移動",
"TaskMoveTrickplayImagesDescription": "ライブラリ設定によりTrickplayのファイルを移動。", "TaskMoveTrickplayImagesDescription": "ライブラリ設定によりTrickplayのファイルを移動。",
"TaskDownloadMissingLyrics": "記録されていない歌詞をダウンロード" "TaskDownloadMissingLyrics": "失われた歌詞をダウンロード",
"TaskExtractMediaSegmentsDescription": "MediaSegment 対応プラグインからメディア セグメントを抽出または取得します。"
} }

View File

@ -3,7 +3,7 @@
"AppDeviceValues": "앱: {0}, 장치: {1}", "AppDeviceValues": "앱: {0}, 장치: {1}",
"Application": "애플리케이션", "Application": "애플리케이션",
"Artists": "아티스트", "Artists": "아티스트",
"AuthenticationSucceededWithUserName": "{0}이(가) 성공적으로 인증됨", "AuthenticationSucceededWithUserName": "{0} 사용자가 성공적으로 인증됨",
"Books": "도서", "Books": "도서",
"CameraImageUploadedFrom": "{0}에서 새로운 카메라 이미지가 업로드됨", "CameraImageUploadedFrom": "{0}에서 새로운 카메라 이미지가 업로드됨",
"Channels": "채널", "Channels": "채널",
@ -70,7 +70,7 @@
"ScheduledTaskFailedWithName": "{0} 실패", "ScheduledTaskFailedWithName": "{0} 실패",
"ScheduledTaskStartedWithName": "{0} 시작", "ScheduledTaskStartedWithName": "{0} 시작",
"ServerNameNeedsToBeRestarted": "{0}를 재시작해야합니다", "ServerNameNeedsToBeRestarted": "{0}를 재시작해야합니다",
"Shows": "", "Shows": "시리즈",
"Songs": "노래", "Songs": "노래",
"StartupEmbyServerIsLoading": "Jellyfin 서버를 불러오고 있습니다. 잠시 후에 다시 시도하십시오.", "StartupEmbyServerIsLoading": "Jellyfin 서버를 불러오고 있습니다. 잠시 후에 다시 시도하십시오.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
@ -81,14 +81,14 @@
"User": "사용자", "User": "사용자",
"UserCreatedWithName": "사용자 {0} 생성됨", "UserCreatedWithName": "사용자 {0} 생성됨",
"UserDeletedWithName": "사용자 {0} 삭제됨", "UserDeletedWithName": "사용자 {0} 삭제됨",
"UserDownloadingItemWithValues": "{0}이(가) {1}을 다운로드 중입니다", "UserDownloadingItemWithValues": "{0} 사용자가 {1} 다운로드 중",
"UserLockedOutWithName": "유저 {0} 은(는) 잠금처리 되었습니다", "UserLockedOutWithName": "{0} 사용자 잠김",
"UserOfflineFromDevice": "{1}에서 {0}의 연결이 끊킴", "UserOfflineFromDevice": "{0} 사용자의 {1}에서 연결이 끊김",
"UserOnlineFromDevice": "{0}이 {1}으로 접속", "UserOnlineFromDevice": "{0} 사용자가 {1}에서 접속함",
"UserPasswordChangedWithName": "사용자 {0}의 비밀번호가 변경되었습니다", "UserPasswordChangedWithName": "{0} 사용자 비밀번호 변경됨",
"UserPolicyUpdatedWithName": "{0}의 사용자 정책이 업데이트되었습니다", "UserPolicyUpdatedWithName": "{0} 사용자 정책 업데이트됨",
"UserStartedPlayingItemWithValues": "{2}에서 {0}이 {1} 재생 중", "UserStartedPlayingItemWithValues": "{0} 사용자의 {2}에서 {1} 재생 중",
"UserStoppedPlayingItemWithValues": "{2}에서 {0}이 {1} 재생을 마침", "UserStoppedPlayingItemWithValues": "{0} 사용자의 {2}에서 {1} 재생을 마침",
"ValueHasBeenAddedToLibrary": "{0}가 미디어 라이브러리에 추가되었습니다", "ValueHasBeenAddedToLibrary": "{0}가 미디어 라이브러리에 추가되었습니다",
"ValueSpecialEpisodeName": "스페셜 - {0}", "ValueSpecialEpisodeName": "스페셜 - {0}",
"VersionNumber": "버전 {0}", "VersionNumber": "버전 {0}",
@ -130,5 +130,11 @@
"TaskAudioNormalizationDescription": "오디오의 볼륨 수준을 일정하게 조정하기 위해 파일을 스캔합니다.", "TaskAudioNormalizationDescription": "오디오의 볼륨 수준을 일정하게 조정하기 위해 파일을 스캔합니다.",
"TaskRefreshTrickplayImages": "비디오 탐색용 미리보기 썸네일 생성", "TaskRefreshTrickplayImages": "비디오 탐색용 미리보기 썸네일 생성",
"TaskRefreshTrickplayImagesDescription": "활성화된 라이브러리에서 비디오의 트릭플레이 미리보기를 생성합니다.", "TaskRefreshTrickplayImagesDescription": "활성화된 라이브러리에서 비디오의 트릭플레이 미리보기를 생성합니다.",
"TaskCleanCollectionsAndPlaylistsDescription": "더 이상 존재하지 않는 컬렉션 및 재생 목록에서 항목을 제거합니다." "TaskCleanCollectionsAndPlaylistsDescription": "더 이상 존재하지 않는 컬렉션 및 재생 목록에서 항목을 제거합니다.",
"TaskExtractMediaSegments": "미디어 세그먼트 스캔",
"TaskExtractMediaSegmentsDescription": "MediaSegment를 지원하는 플러그인에서 미디어 세그먼트를 추출하거나 가져옵니다.",
"TaskMoveTrickplayImages": "트릭플레이 이미지 위치 마이그레이션",
"TaskMoveTrickplayImagesDescription": "추출된 트릭플레이 이미지를 라이브러리 설정에 따라 이동합니다.",
"TaskDownloadMissingLyrics": "누락된 가사 다운로드",
"TaskDownloadMissingLyricsDescription": "가사 다운로드"
} }

View File

@ -0,0 +1,139 @@
{
"Albums": "Alben",
"Application": "Applikatioun",
"Artists": "Kënschtler",
"Books": "Bicher",
"Channels": "Kanäl",
"Collections": "Kollektiounen",
"Default": "Standard",
"ChapterNameValue": "Kapitel {0}",
"DeviceOnlineWithName": "{0} ass Online",
"DeviceOfflineWithName": "{0} ass Offline",
"External": "Extern",
"Favorites": "Favoritten",
"Folders": "Dossieren",
"Forced": "Forcéiert",
"HeaderAlbumArtists": "Album Kënschtler",
"HeaderFavoriteAlbums": "Léifsten Alben",
"HeaderFavoriteArtists": "Léifsten Kënschtler",
"HeaderFavoriteEpisodes": "Léifsten Episoden",
"HeaderFavoriteShows": "Léifsten Shows",
"HeaderFavoriteSongs": "Léifsten Lidder",
"Genres": "Generen",
"HeaderContinueWatching": "Weider kucken",
"Inherit": "Iwwerhuelen",
"HeaderNextUp": "Als Nächst",
"HeaderRecordingGroups": "Opname Gruppen",
"HearingImpaired": "Daaf",
"HomeVideos": "Amateur Videoen",
"ItemRemovedWithName": "Element ewech geholl: {0}",
"LabelIpAddressValue": "IP Adress: {0}",
"LabelRunningTimeValue": "Lafzäit: {0}",
"Latest": "Dat Aktuellst",
"MessageApplicationUpdatedTo": "Jellyfin Server aktualiséiert op {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Server Konfiguratiounssektioun {0} aktualiséiert",
"MessageServerConfigurationUpdated": "Server Konfiguratioun aktualiséiert",
"Movies": "Filmer",
"Music": "Musek",
"NameInstallFailed": "{0} Installatioun net gelongen",
"NameSeasonNumber": "Staffel {0}",
"NameSeasonUnknown": "Staffel Onbekannt",
"MusicVideos": "Museksvideoen",
"NotificationOptionApplicationUpdateAvailable": "Applikatiouns Update verfügbar",
"NotificationOptionApplicationUpdateInstalled": "Applikatiouns Update nët Installéiert",
"NotificationOptionAudioPlayback": "Audio ofspillen gestart",
"NotificationOptionAudioPlaybackStopped": "Audio ofspillen gestoppt",
"NotificationOptionCameraImageUploaded": "Kamera Bild eropgelueden",
"NotificationOptionInstallationFailed": "Installatioun net gelongen",
"NotificationOptionNewLibraryContent": "Neien Bibliothéik Inhalt",
"NotificationOptionPluginError": "Plugin Feeler",
"NotificationOptionPluginInstalled": "Plugin installéiert",
"NotificationOptionPluginUninstalled": "Plugin desinstalléiert",
"NotificationOptionPluginUpdateInstalled": "Plugin Update installéiert",
"Photos": "Fotoen",
"NotificationOptionTaskFailed": "Aufgab net gelongen",
"NotificationOptionUserLockedOut": "Benotzer Gesperrt",
"NotificationOptionVideoPlaybackStopped": "Video ofspillen gestoppt",
"NotificationOptionVideoPlayback": "Video ofspillen gestartet",
"Plugin": "Plugin",
"PluginUninstalledWithName": "{0} desinstalléiert",
"PluginUpdatedWithName": "{0} aktualiséiert",
"ProviderValue": "Provider: {0}",
"ScheduledTaskFailedWithName": "Aufgab: {0} net gelongen",
"Playlists": "Playlëschten",
"Shows": "Shows",
"Songs": "Lidder",
"ServerNameNeedsToBeRestarted": "{0} muss nei gestart ginn",
"StartupEmbyServerIsLoading": "Jellyfin Server luedt. Probéier méi spéit nach eng Kéier.",
"Sync": "Synchroniséieren",
"System": "System",
"User": "Benotzer",
"TvShows": "TV Shows",
"Undefined": "Net definéiert",
"UserCreatedWithName": "Benotzer {0} erstellt",
"UserDownloadingItemWithValues": "{0} luet {1} erof",
"UserOfflineFromDevice": "{0} Benotzer Offline um Gerät {1}",
"UserLockedOutWithName": "Benotzer {0} gesperrt",
"UserOnlineFromDevice": "{0} Benotzer Online um Gerät {1}",
"UserPasswordChangedWithName": "Benotzer Passwuert geännert fir {0}",
"UserPolicyUpdatedWithName": "Benotzer Politik aktualiséiert fir: {0}",
"UserStartedPlayingItemWithValues": "{0} spillt {1} op {2} oof",
"ValueHasBeenAddedToLibrary": "{0} der Bibliothéik bäigefüügt",
"VersionNumber": "Versioun {0}",
"TasksMaintenanceCategory": "Ënnerhalt",
"TasksLibraryCategory": "Bibliothéik",
"ValueSpecialEpisodeName": "Spezial-Episodenumm",
"TasksChannelsCategory": "Internet Kanäl",
"TaskCleanActivityLog": "Aktivitéits Log botzen",
"TaskCleanActivityLogDescription": "Läscht Aktivitéitslogs méi al wéi konfiguréiert.",
"TaskCleanCache": "Aufgab Cache Botzen",
"TaskRefreshChapterImages": "Kapitel Biller erstellen",
"TaskRefreshChapterImagesDescription": "Erstellt Miniaturbiller fir Videoen, déi Kapitelen hunn.",
"TaskAudioNormalization": "Audio Normaliséierung",
"TaskRefreshLibrary": "Bibliothéik aktualiséieren",
"TaskRefreshLibraryDescription": "Scannt deng Mediebibliothéik no neien Dateien a frëscht dMetadata op.",
"TaskCleanLogs": "Log Dateien botzen",
"TaskRefreshPeople": "Persounen aktualiséieren",
"TaskRefreshPeopleDescription": "Aktualiséiert Metadata fir Schauspiller a Regisseuren an denger Mediebibliothéik.",
"TaskRefreshTrickplayImagesDescription": "Erstellt Trickplay-Viraussiichten fir Videoen an aktivéierte Bibliothéiken.",
"TaskCleanTranscode": "Transkodéieren botzen",
"TaskCleanTranscodeDescription": "Läscht Transkodéierungsdateien, déi méi al wéi een Dag sinn.",
"TaskRefreshChannels": "Kanäl aktualiséieren",
"TaskDownloadMissingLyrics": "Fehlend Liddertexter eroflueden",
"TaskDownloadMissingLyricsDescription": "Lued Liddertexter fir Lidder erof",
"TaskDownloadMissingSubtitles": "Fehlend Ënnertitelen eroflueden",
"TaskOptimizeDatabase": "Datebank optiméieren",
"TaskKeyframeExtractor": "Schlësselbild Extrakter",
"TaskCleanCollectionsAndPlaylists": "Sammlungen a Playlisten botzen",
"TaskCleanCollectionsAndPlaylistsDescription": "Ewechhuele vun Elementer aus Sammlungen a Playlisten, déi net méi existéieren.",
"TaskExtractMediaSegments": "Mediesegment-Scan",
"NewVersionIsAvailable": "Nei Versioun fir Jellyfin Server ass verfügbar.",
"CameraImageUploadedFrom": "En neit Kamera Bild gouf vu {0} eropgelueden",
"PluginInstalledWithName": "{0} installéiert",
"TaskMoveTrickplayImagesDescription": "Verschëfft existent Trickplay-Dateien no de Bibliothéik-Astellungen.",
"AppDeviceValues": "App: {0}, Geräter: {1}",
"FailedLoginAttemptWithUserName": "Net Gelongen Umeldung {0}",
"HeaderLiveTV": "LiveTV",
"ItemAddedWithName": "Element derbäi gesat: {0}",
"NotificationOptionServerRestartRequired": "Server Restart Erfuerderlech",
"ScheduledTaskStartedWithName": "Aufgab: {0} gestart",
"AuthenticationSucceededWithUserName": "{0} Authentifikatioun gelongen",
"MixedContent": "Gemëschten Inhalt",
"MessageApplicationUpdated": "Jellyfin Server Aktualiséiert",
"SubtitleDownloadFailureFromForItem": "Ënnertitel Download Feeler vun {0} fir {1}",
"TaskCleanLogsDescription": "Läscht Log-Dateien, déi méi al wéi {0} Deeg sinn.",
"TaskUpdatePlugins": "Plugins aktualiséieren",
"UserDeletedWithName": "Benotzer {0} geläscht",
"TasksApplicationCategory": "Applikatioun",
"TaskCleanCacheDescription": "Läscht Cache-Dateien, déi net méi vum System gebraucht ginn.",
"UserStoppedPlayingItemWithValues": "{0} ass mat {1} op {2} fäerdeg",
"TaskAudioNormalizationDescription": "Scannt Dateien no Donnéeën fir dAudio-Normaliséierung.",
"TaskRefreshTrickplayImages": "Trickplay-Biller generéieren",
"TaskDownloadMissingSubtitlesDescription": "Sicht am Internet no fehlenden Ënnertitelen op Basis vun der Metadata-Konfiguratioun.",
"TaskMoveTrickplayImages": "Trickplay-Biller-Plaz migréieren",
"TaskUpdatePluginsDescription": "Lued Aktualiséierungen erof a installéiert se fir Plugins, déi fir automatesch Updates konfiguréiert sinn.",
"TaskKeyframeExtractorDescription": "Extrahéiert Schlësselbiller aus Videodateien, fir méi präzis HLS-Playlisten ze erstellen. Dës Aufgab kann eng längere Zäit daueren.",
"TaskRefreshChannelsDescription": "Aktualiséiert Informatiounen iwwer Internetkanäl.",
"TaskExtractMediaSegmentsDescription": "Extrahéiert oder kritt Mediesegmenter aus Plugins, déi MediaSegment ënnerstëtzen.",
"TaskOptimizeDatabaseDescription": "Kompriméiert dDatebank a schneit de fräie Speicherplatz zou. Dës Aufgab no engem Bibliothéik-Scan oder anere Ännerungen, déi Datebankmodifikatioune mat sech bréngen, auszeféieren, kann dPerformance verbesseren."
}

View File

@ -94,14 +94,14 @@
"VersionNumber": "Version {0}", "VersionNumber": "Version {0}",
"TaskUpdatePluginsDescription": "Atsisiųsti ir įdiegti atnaujinimus priedams kuriem yra nustatytas automatiškas atnaujinimas.", "TaskUpdatePluginsDescription": "Atsisiųsti ir įdiegti atnaujinimus priedams kuriem yra nustatytas automatiškas atnaujinimas.",
"TaskUpdatePlugins": "Atnaujinti Priedus", "TaskUpdatePlugins": "Atnaujinti Priedus",
"TaskDownloadMissingSubtitlesDescription": "Ieško internete trūkstamų subtitrų remiantis metaduomenų konfigūracija.", "TaskDownloadMissingSubtitlesDescription": "Ieško trūkstamų subtitrų internete remiantis metaduomenų konfigūracija.",
"TaskCleanTranscodeDescription": "Ištrina dienos senumo perkodavimo failus.", "TaskCleanTranscodeDescription": "Ištrina dienos senumo perkodavimo failus.",
"TaskCleanTranscode": "Išvalyti Perkodavimo Direktorija", "TaskCleanTranscode": "Išvalyti Perkodavimo Direktorija",
"TaskRefreshLibraryDescription": "Ieškoti naujų failų jūsų mediatekoje ir atnaujina metaduomenis.", "TaskRefreshLibraryDescription": "Ieškoti naujų failų jūsų mediatekoje ir atnaujina metaduomenis.",
"TaskRefreshLibrary": "Skenuoti Mediateka", "TaskRefreshLibrary": "Skenuoti Mediateka",
"TaskDownloadMissingSubtitles": "Atsisiųsti trūkstamus subtitrus", "TaskDownloadMissingSubtitles": "Atsisiųsti trūkstamus subtitrus",
"TaskRefreshChannelsDescription": "Atnaujina internetinių kanalų informacija.", "TaskRefreshChannelsDescription": "Atnaujina internetinių kanalų informaciją.",
"TaskRefreshChannels": "Atnaujinti Kanalus", "TaskRefreshChannels": "Atnaujinti kanalus",
"TaskRefreshPeopleDescription": "Atnaujina metaduomenis apie aktorius ir režisierius jūsų mediatekoje.", "TaskRefreshPeopleDescription": "Atnaujina metaduomenis apie aktorius ir režisierius jūsų mediatekoje.",
"TaskRefreshPeople": "Atnaujinti Žmones", "TaskRefreshPeople": "Atnaujinti Žmones",
"TaskCleanLogsDescription": "Ištrina žurnalo failus kurie yra senesni nei {0} dienos.", "TaskCleanLogsDescription": "Ištrina žurnalo failus kurie yra senesni nei {0} dienos.",
@ -119,22 +119,22 @@
"Forced": "Priverstas", "Forced": "Priverstas",
"Default": "Numatytas", "Default": "Numatytas",
"TaskCleanActivityLogDescription": "Ištrina veiklos žuranlo įrašus, kurie yra senesni nei nustatytas amžius.", "TaskCleanActivityLogDescription": "Ištrina veiklos žuranlo įrašus, kurie yra senesni nei nustatytas amžius.",
"TaskOptimizeDatabase": "Optimizuoti duomenų bazės", "TaskOptimizeDatabase": "Optimizuoti duomenų bazę",
"TaskKeyframeExtractorDescription": "Iš vaizdo įrašo paruošia reikšminius kadrus, kad būtų sukuriamas tikslenis HLS grojaraštis. Šios užduoties vykdymas gali ilgai užtrukti.", "TaskKeyframeExtractorDescription": "Iš vaizdo įrašo paruošia reikšminius kadrus, kad būtų sukuriamas tikslenis HLS grojaraštis. Šios užduoties vykdymas gali ilgai užtrukti.",
"TaskKeyframeExtractor": "Pagrindinių kadrų ištraukėjas", "TaskKeyframeExtractor": "Pagrindinių kadrų išgavėjas",
"TaskOptimizeDatabaseDescription": "Suspaudžia duomenų bazę ir atlaisvina vietą. Paleidžiant šią užduotį, po bibliotekos skenavimo arba kitų veiksmų kurie galimai modifikuoja duomenų bazė, gali pagerinti greitaveiką.", "TaskOptimizeDatabaseDescription": "Suspaudžia duomenų bazę ir atlaisvina vietą. Paleidžiant šią užduotį, po bibliotekos skenavimo arba kitų veiksmų kurie galimai modifikuoja duomenų bazę, gali pagerinti greitaveiką.",
"External": "Išorinis", "External": "Išorinis",
"HearingImpaired": "Su klausos sutrikimais", "HearingImpaired": "Su klausos sutrikimais",
"TaskRefreshTrickplayImages": "Generuoti Trickplay atvaizdus", "TaskRefreshTrickplayImages": "Generuoti Trickplay atvaizdus",
"TaskRefreshTrickplayImagesDescription": "Sukuria trickplay peržiūras vaizdo įrašams įgalintose bibliotekose.", "TaskRefreshTrickplayImagesDescription": "Sukuria trickplay peržiūras vaizdo įrašams įgalintose bibliotekose.",
"TaskCleanCollectionsAndPlaylists": "Sutvarko duomenis jūsų kolekcijose ir grojaraščiuose", "TaskCleanCollectionsAndPlaylists": "Išvalo duomenis kolekcijose ir grojaraščiuose",
"TaskCleanCollectionsAndPlaylistsDescription": "Pašalina nebeegzistuojančius elementus iš kolekcijų ir grojaraščių.", "TaskCleanCollectionsAndPlaylistsDescription": "Pašalina neegzistuojančius elementus iš kolekcijų ir grojaraščių.",
"TaskAudioNormalization": "Garso Normalizavimas", "TaskAudioNormalization": "Garso Normalizavimas",
"TaskAudioNormalizationDescription": "Skenuoti garso normalizavimo informacijos failuose.", "TaskAudioNormalizationDescription": "Skenuoti garso normalizavimo informacijos failuose.",
"TaskExtractMediaSegments": "Medijos Segmentų Nuskaitymas", "TaskExtractMediaSegments": "Medijos segmentų nuskaitymas",
"TaskDownloadMissingLyrics": "Parsisiųsti trūkstamus dainų tekstus", "TaskDownloadMissingLyrics": "Parsisiųsti trūkstamus dainų tekstus",
"TaskExtractMediaSegmentsDescription": "Ištraukia arba gauna medijos segmentus iš MediaSegment ijungtų papildinių.", "TaskExtractMediaSegmentsDescription": "Ištraukia arba gauna medijos segmentus iš MediaSegment ijungtų papildinių.",
"TaskMoveTrickplayImages": "Migruoti Trickplay Vaizdų Vietą", "TaskMoveTrickplayImages": "Pakeisti Trickplay vaizdų vietą",
"TaskMoveTrickplayImagesDescription": "Perkelia egzisuojančius trickplay failus pagal bibliotekos nustatymus.", "TaskMoveTrickplayImagesDescription": "Perkelia egzistuojančius trickplay failus pagal bibliotekos nustatymus.",
"TaskDownloadMissingLyricsDescription": "Parsisiųsti dainų žodžius" "TaskDownloadMissingLyricsDescription": "Parsisiųsti dainų žodžius"
} }

View File

@ -123,11 +123,17 @@
"External": "Ārējais", "External": "Ārējais",
"HearingImpaired": "Ar dzirdes traucējumiem", "HearingImpaired": "Ar dzirdes traucējumiem",
"TaskKeyframeExtractor": "Atslēgkadru ekstraktors", "TaskKeyframeExtractor": "Atslēgkadru ekstraktors",
"TaskKeyframeExtractorDescription": "Ekstraktē atslēgkadrus no video failiem lai izveidotu precīzākus HLS atskaņošanas sarakstus. Šis process var būt ilgs.", "TaskKeyframeExtractorDescription": "Izvelk atslēgkadrus no video failiem lai izveidotu precīzākus HLS atskaņošanas sarakstus. Šis process var būt ilgs.",
"TaskRefreshTrickplayImages": "Ģenerēt partīšanas attēlus", "TaskRefreshTrickplayImages": "Ģenerēt partīšanas attēlus",
"TaskRefreshTrickplayImagesDescription": "Izveido priekšskatījumus videoklipu pārtīšanai iespējotajās bibliotēkās.", "TaskRefreshTrickplayImagesDescription": "Izveido priekšskatījumus videoklipu pārtīšanai iespējotajās bibliotēkās.",
"TaskAudioNormalization": "Audio normalizācija", "TaskAudioNormalization": "Audio normalizācija",
"TaskCleanCollectionsAndPlaylistsDescription": "Noņem vairs neeksistējošus vienumus no kolekcijām un atskaņošanas sarakstiem.", "TaskCleanCollectionsAndPlaylistsDescription": "Noņem vairs neeksistējošus vienumus no kolekcijām un atskaņošanas sarakstiem.",
"TaskAudioNormalizationDescription": "Skanē failus priekš audio normālizācijas informācijas.", "TaskAudioNormalizationDescription": "Skanē failus priekš audio normālizācijas informācijas.",
"TaskCleanCollectionsAndPlaylists": "Notīrīt kolekcijas un atskaņošanas sarakstus" "TaskCleanCollectionsAndPlaylists": "Notīrīt kolekcijas un atskaņošanas sarakstus",
"TaskExtractMediaSegments": "Multivides segmenta skenēšana",
"TaskExtractMediaSegmentsDescription": "Izvelk vai iegūst multivides segmentus no MediaSegment iespējotiem spraudņiem.",
"TaskMoveTrickplayImages": "Trickplay attēlu pārvietošana",
"TaskMoveTrickplayImagesDescription": "Pārvieto esošos trickplay failus atbilstoši bibliotēkas iestatījumiem.",
"TaskDownloadMissingLyrics": "Lejupielādēt trūkstošos vārdus",
"TaskDownloadMissingLyricsDescription": "Lejupielādēt vārdus dziesmām"
} }

View File

@ -131,5 +131,6 @@
"TaskRefreshTrickplayImages": "Генерирај слики за прегледување (Trickplay)", "TaskRefreshTrickplayImages": "Генерирај слики за прегледување (Trickplay)",
"TaskAudioNormalization": "Нормализација на звукот", "TaskAudioNormalization": "Нормализација на звукот",
"TaskRefreshTrickplayImagesDescription": "Креира трикплеј прегледи за видеа во овозможените библиотеки.", "TaskRefreshTrickplayImagesDescription": "Креира трикплеј прегледи за видеа во овозможените библиотеки.",
"TaskCleanCollectionsAndPlaylistsDescription": "Отстранува ставки од колекциите и плејлистите што веќе не постојат." "TaskCleanCollectionsAndPlaylistsDescription": "Отстранува ставки од колекциите и плејлистите што веќе не постојат.",
"TaskExtractMediaSegments": "Скенирање на сегменти на содржина"
} }

View File

@ -11,7 +11,7 @@
"Collections": "Koleksi", "Collections": "Koleksi",
"DeviceOfflineWithName": "{0} telah diputuskan sambungan", "DeviceOfflineWithName": "{0} telah diputuskan sambungan",
"DeviceOnlineWithName": "{0} telah disambung", "DeviceOnlineWithName": "{0} telah disambung",
"FailedLoginAttemptWithUserName": "Cubaan log masuk gagal dari {0}", "FailedLoginAttemptWithUserName": "Percubaan log masuk daripada {0} gagal",
"Favorites": "Kegemaran", "Favorites": "Kegemaran",
"Folders": "Fail-fail", "Folders": "Fail-fail",
"Genres": "Genre-genre", "Genres": "Genre-genre",
@ -126,5 +126,15 @@
"TaskKeyframeExtractor": "Ekstrak bingkai kunci", "TaskKeyframeExtractor": "Ekstrak bingkai kunci",
"TaskKeyframeExtractorDescription": "Ekstrak bingkai kunci dari fail video untuk membina HLS playlist yang lebih tepat. Tugas ini mungkin perlukan masa yang panjang.", "TaskKeyframeExtractorDescription": "Ekstrak bingkai kunci dari fail video untuk membina HLS playlist yang lebih tepat. Tugas ini mungkin perlukan masa yang panjang.",
"TaskRefreshTrickplayImagesDescription": "Jana gambar prebiu Trickplay untuk video dalam perpustakaan.", "TaskRefreshTrickplayImagesDescription": "Jana gambar prebiu Trickplay untuk video dalam perpustakaan.",
"TaskRefreshTrickplayImages": "Jana gambar Trickplay" "TaskRefreshTrickplayImages": "Jana gambar Trickplay",
"TaskExtractMediaSegments": "Imbasan Segmen Media",
"TaskExtractMediaSegmentsDescription": "Mengekstrak atau mendapatkan segmen media daripada pemalam yang didayakan MediaSegment.",
"TaskMoveTrickplayImagesDescription": "Mengalihkan fail trickplay sedia ada mengikut tetapan pustakan digital.",
"TaskDownloadMissingLyrics": "Muat turun lirik yang hilang",
"TaskDownloadMissingLyricsDescription": "Memuat turun lirik-lirik untuk lagu-lagu",
"TaskMoveTrickplayImages": "Alih Lokasi Imej Trickplay",
"TaskCleanCollectionsAndPlaylists": "Bersihkan koleksi dan senarai audio video",
"TaskAudioNormalization": "Normalisasi Audio",
"TaskAudioNormalizationDescription": "Mengimbas fail-fail untuk data normalisasi audio.",
"TaskCleanCollectionsAndPlaylistsDescription": "Mengalih keluar item daripada koleksi dan senarai audio video yang tidak wujud lagi."
} }

View File

@ -1,86 +1,86 @@
{ {
"Albums": "Albums", "Albums": "Albums",
"AppDeviceValues": "App: {0}, Apparat: {1}", "AppDeviceValues": "Applikazzjoni: {0}, Device: {1}",
"Application": "Applikazzjoni", "Application": "Applikazzjoni",
"Artists": "Artisti", "Artists": "Artisti",
"AuthenticationSucceededWithUserName": "{1} awtentikat b'suċċess", "AuthenticationSucceededWithUserName": "{1} awtentikat b'suċċess",
"Books": "Kotba", "Books": "Kotba",
"CameraImageUploadedFrom": "Ttellgħet immaġni ġdida tal-kamera minn {1}", "CameraImageUploadedFrom": "Ttella' ritratt ġdid tal-kamera minn {1}",
"Channels": "Kanali", "Channels": "Stazzjonijiet",
"ChapterNameValue": "Kapitlu {0}", "ChapterNameValue": "Kapitlu {0}",
"Collections": "Kollezzjonijiet", "Collections": "Kollezzjonijiet",
"DeviceOfflineWithName": "{0} inqatgħa", "DeviceOfflineWithName": "{0} tneħħa",
"DeviceOnlineWithName": "{0} qabad", "DeviceOnlineWithName": "{0} tqabbad",
"External": "Estern", "External": "Estern",
"FailedLoginAttemptWithUserName": "Tentattiv t'aċċess fallut minn {0}", "FailedLoginAttemptWithUserName": "Attentat fallut ta' login minn {0}",
"Favorites": "Favoriti", "Favorites": "Favoriti",
"Forced": "Sfurzat", "Forced": "Sfurzat",
"Genres": "Ġeneri", "Genres": "Ġeneri",
"HeaderAlbumArtists": "Artisti tal-album", "HeaderAlbumArtists": "Artisti tal-album",
"HeaderContinueWatching": "Kompli Segwi", "HeaderContinueWatching": "Kompli Ara",
"HeaderFavoriteAlbums": "Albums Favoriti", "HeaderFavoriteAlbums": "Albums Favoriti",
"HeaderFavoriteArtists": "Artisti Favoriti", "HeaderFavoriteArtists": "Artisti Favoriti",
"HeaderFavoriteEpisodes": "Episodji Favoriti", "HeaderFavoriteEpisodes": "Episodji Favoriti",
"HeaderFavoriteShows": "Programmi Favoriti", "HeaderFavoriteShows": "Programmi Favoriti",
"HeaderFavoriteSongs": "Kanzunetti Favoriti", "HeaderFavoriteSongs": "Kanzunetti Favoriti",
"HeaderNextUp": "Li Jmiss", "HeaderNextUp": "Li Jmiss",
"SubtitleDownloadFailureFromForItem": "Is-sottotitli naqsu milli jitniżżlu minn {0} għal {1}", "SubtitleDownloadFailureFromForItem": "Is-sottotitli ma setgħux jitniżżlu minn {0} għal {1}",
"UserPasswordChangedWithName": "Il-password inbidel għall-utent {0}", "UserPasswordChangedWithName": "Il-password għall-utent {0} inbidlet",
"TaskUpdatePluginsDescription": "Iniżżel u jinstalla aġġornamenti għal plugins li huma kkonfigurati biex jaġġornaw awtomatikament.", "TaskUpdatePluginsDescription": "Iniżżel u jinstalla aġġornamenti għal plugins li huma kkonfigurati biex jaġġornaw awtomatikament.",
"TaskDownloadMissingSubtitlesDescription": "Ifittex fuq l-internet għal sottotitli neqsin abbażi tal-konfigurazzjoni tal-metadata.", "TaskDownloadMissingSubtitlesDescription": "Ifittex fuq l-internet għal sottotitli neqsin skont il-konfigurazzjoni tal-metadata.",
"TaskOptimizeDatabaseDescription": "Jikkompatti d-database u jaqta' l-ispazju ħieles. It-tħaddim ta' dan il-kompitu wara li tiskennja l-librerija jew tagħmel bidliet oħra li jimplikaw modifiki fid-database jistgħu jtejbu l-prestazzjoni.", "TaskOptimizeDatabaseDescription": "Jikkompatta d-database u jaqta' l-ispazju ħieles. It-tħaddim ta' dan it-task wara li tiskennja l-librerija jew tagħmel bidliet oħra li jimplikaw modifiki fid-database jistgħu jtejbu l-mod kif jaħdem.",
"Default": "Standard", "Default": "Standard",
"Folders": "Folders", "Folders": "Folders",
"HeaderLiveTV": "TV Dirett", "HeaderLiveTV": "TV Dirett",
"HeaderRecordingGroups": "Gruppi ta' Reġistrazzjoni", "HeaderRecordingGroups": "Gruppi ta' Rikordjar",
"HearingImpaired": "Nuqqas ta' Smigħ", "HearingImpaired": "Nuqqas ta' Smigħ",
"HomeVideos": "Vidjows Personali", "HomeVideos": "Filmati Personali",
"Inherit": "Jiret", "Inherit": "Jiret",
"ItemAddedWithName": "{0} ġie miżjud mal-librerija", "ItemAddedWithName": "{0} żdied fil-librerija",
"ItemRemovedWithName": "{0} tneħħa mil-librerija", "ItemRemovedWithName": "{0} tneħħa mil-librerija",
"LabelIpAddressValue": "Indirizz IP: {0}", "LabelIpAddressValue": "Indirizz tal-IP: {0}",
"Latest": "Tal-Aħħar", "Latest": "Tal-Aħħar",
"MessageApplicationUpdated": "Jellyfin Server ġie aġġornat", "MessageApplicationUpdated": "Il-Jellyfin Server ġie aġġornat",
"MessageApplicationUpdatedTo": "JellyFin Server ġie aġġornat għal {0}", "MessageApplicationUpdatedTo": "Il-JellyFin Server ġie aġġornat għal {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Is-sezzjoni {0} tal-konfigurazzjoni tas-server ġiet aġġornata", "MessageNamedServerConfigurationUpdatedWithValue": "Is-sezzjoni {0} tal-konfigurazzjoni tas-server ġiet aġġornata",
"MessageServerConfigurationUpdated": "Il-konfigurazzjoni tas-server ġiet aġġornata", "MessageServerConfigurationUpdated": "Il-konfigurazzjoni tas-server ġiet aġġornata",
"MixedContent": "Kontenut imħallat", "MixedContent": "Kontenut imħallat",
"Movies": "Films", "Movies": "Films",
"Music": "Mużika", "Music": "Mużika",
"MusicVideos": "Vidjows tal-Mużika", "MusicVideos": "Music Videos",
"NameInstallFailed": "L-installazzjoni ta' {0} falliet", "NameInstallFailed": "L-installazzjoni ta' {0} falliet",
"NameSeasonNumber": "Staġun {0}", "NameSeasonNumber": "Staġun {0}",
"NameSeasonUnknown": "Staġun Mhux Magħruf", "NameSeasonUnknown": "Staġun Mhux Magħruf",
"NewVersionIsAvailable": "Verżjoni ġdida ta' Jellyfin Server hija disponibbli biex titniżżel.", "NewVersionIsAvailable": "Verżjoni ġdida tal-Jellyfin Server hija disponibbli biex titniżżel.",
"NotificationOptionApplicationUpdateAvailable": "Aġġornament tal-applikazzjoni disponibbli", "NotificationOptionApplicationUpdateAvailable": "Hemm aġġornament tal-applikazzjoni",
"NotificationOptionCameraImageUploaded": "Immaġini tal-kamera mtella'", "NotificationOptionCameraImageUploaded": "Ritratt tal-kamera mtella'",
"LabelRunningTimeValue": "Tul: {0}", "LabelRunningTimeValue": "Tul: {0}",
"NotificationOptionApplicationUpdateInstalled": "Aġġornament tal-applikazzjoni ġie installat", "NotificationOptionApplicationUpdateInstalled": "Aġġornament tal-applikazzjoni ġie installat",
"NotificationOptionAudioPlayback": "Il-playback tal-awdjo beda", "NotificationOptionAudioPlayback": "Beda l-playback tal-awdjo",
"NotificationOptionAudioPlaybackStopped": "Il-playback tal-awdjo twaqqaf", "NotificationOptionAudioPlaybackStopped": "Il-playback tal-awdjo twaqqaf",
"NotificationOptionInstallationFailed": "Installazzjoni falliet", "NotificationOptionInstallationFailed": "L-Installazzjoni falliet",
"NotificationOptionNewLibraryContent": "Kontenut ġdid miżjud", "NotificationOptionNewLibraryContent": "Kontenut ġdid żdied",
"NotificationOptionPluginError": "Ħsara fil-plugin", "NotificationOptionPluginError": "Falliment fil-plugin",
"NotificationOptionPluginInstalled": "Plugin installat", "NotificationOptionPluginInstalled": "Plugin installat",
"NotificationOptionPluginUninstalled": "Plugin tneħħa", "NotificationOptionPluginUninstalled": "Plugin tneħħa",
"NotificationOptionServerRestartRequired": "Meħtieġ l-istartjar mill-ġdid tas-server", "NotificationOptionServerRestartRequired": "Hemm bżonn li tagħmel restart tas-server",
"NotificationOptionTaskFailed": "Falliment tal-kompitu skedat", "NotificationOptionTaskFailed": "Falliment tat-task skedat",
"NotificationOptionUserLockedOut": "Utent imsakkar", "NotificationOptionUserLockedOut": "Utent imsakkar",
"Photos": "Ritratti", "Photos": "Ritratti",
"Playlists": "Playlists", "Playlists": "Playlists",
"Plugin": "Plugin", "Plugin": "Plugin",
"PluginInstalledWithName": "{0} ġie installat", "PluginInstalledWithName": "{0} ġie installat",
"PluginUninstalledWithName": "{0} ġie mneħħi", "PluginUninstalledWithName": "{0} tneħħa",
"PluginUpdatedWithName": "{0} ġie aġġornat", "PluginUpdatedWithName": "{0} ġie aġġornat",
"ProviderValue": "Fornitur: {0}", "ProviderValue": "Fornitur: {0}",
"ScheduledTaskFailedWithName": "{0} falla", "ScheduledTaskFailedWithName": "{0} falla",
"ScheduledTaskStartedWithName": "{0} beda", "ScheduledTaskStartedWithName": "{0} beda",
"ServerNameNeedsToBeRestarted": "{0} jeħtieġ li jerġa' jinbeda", "ServerNameNeedsToBeRestarted": "{0} jeħtieġ restart",
"Songs": "Kanzunetti", "Songs": "Kanzunetti",
"StartupEmbyServerIsLoading": "Jellyfin Server qed jixgħel. Jekk jogħġbok erġa' pprova dalwaqt.", "StartupEmbyServerIsLoading": "Jellyfin Server qed jillowdja. Jekk jogħġbok erġa' pprova ftit tal-ħin oħra.",
"Sync": "Sinkronizza", "Sync": "Sinkronizza",
"System": "Sistema", "System": "Sistema",
"Undefined": "Mhux Definit", "Undefined": "Bla Definizzjoni",
"User": "Utent", "User": "Utent",
"UserCreatedWithName": "L-utent {0} inħoloq", "UserCreatedWithName": "L-utent {0} inħoloq",
"UserDeletedWithName": "L-utent {0} tħassar", "UserDeletedWithName": "L-utent {0} tħassar",
@ -89,45 +89,51 @@
"UserOfflineFromDevice": "{0} skonnettja minn {1}", "UserOfflineFromDevice": "{0} skonnettja minn {1}",
"UserOnlineFromDevice": "{0} huwa online minn {1}", "UserOnlineFromDevice": "{0} huwa online minn {1}",
"NotificationOptionPluginUpdateInstalled": "Aġġornament ta' plugin ġie installat", "NotificationOptionPluginUpdateInstalled": "Aġġornament ta' plugin ġie installat",
"NotificationOptionVideoPlayback": "Il-playback tal-vidjow beda", "NotificationOptionVideoPlayback": "Il-playback tal-filmat beda",
"NotificationOptionVideoPlaybackStopped": "Il-playback tal-vidjow waqaf", "NotificationOptionVideoPlaybackStopped": "Il-playback tal-filmat twaqqaf",
"Shows": "Programmi", "Shows": "Serje",
"TvShows": "Programmi tat-TV", "TvShows": "Serje Televiżivi",
"UserPolicyUpdatedWithName": "Il-policy tal-utent ġiet aġġornata għal {0}", "UserPolicyUpdatedWithName": "Il-politka tal-utent ġiet aġġornata għal {0}",
"UserStartedPlayingItemWithValues": "{0} qed iħaddem {1} fuq {2}", "UserStartedPlayingItemWithValues": "{0} qed jara {1} fuq {2}",
"UserStoppedPlayingItemWithValues": "{0} waqaf iħaddem {1} fuq {2}", "UserStoppedPlayingItemWithValues": "{0} waqaf jara {1} fuq {2}",
"ValueHasBeenAddedToLibrary": "{0} ġie miżjud mal-librerija tal-midja tiegħek", "ValueHasBeenAddedToLibrary": "{0} ġie miżjud mal-librerija tal-midja tiegħek",
"ValueSpecialEpisodeName": "Speċjali - {0}", "ValueSpecialEpisodeName": "Speċjali - {0}",
"VersionNumber": "Verżjoni {0}", "VersionNumber": "Verżjoni {0}",
"TasksMaintenanceCategory": "Manutenzjoni", "TasksMaintenanceCategory": "Manutenzjoni",
"TasksLibraryCategory": "Librerija", "TasksLibraryCategory": "Librerija",
"TasksApplicationCategory": "Applikazzjoni", "TasksApplicationCategory": "Applikazzjoni",
"TasksChannelsCategory": "Kanali tal-Internet", "TasksChannelsCategory": "Stazzjonijiet tal-Internet",
"TaskCleanActivityLog": "Naddaf il-Logg tal-Attività", "TaskCleanActivityLog": "Naddaf il-Logg tal-Attività",
"TaskCleanActivityLogDescription": "Iħassar l-entrati tar-reġistru tal-attività eqdem mill-età kkonfigurata.", "TaskCleanActivityLogDescription": "Iħassar id-daħliet tar-reġistru tal-attività eqdem mill-età li kienet kkonfigurata.",
"TaskCleanCache": "Naddaf id-Direttorju tal-Cache", "TaskCleanCache": "Naddaf id-Direttorju tal-Cache",
"TaskCleanCacheDescription": "Iħassar il-fajls tal-cache li m'għadhomx meħtieġa mis-sistema.", "TaskCleanCacheDescription": "Iħassar il-fajls tal-cache li m'għadhomx meħtieġa mis-sistema.",
"TaskRefreshChapterImages": "Oħroġ l-Immaġini tal-Kapitolu", "TaskRefreshChapterImages": "Oħroġ ir-Ritratti tal-Kapitlu",
"TaskRefreshChapterImagesDescription": "Joħloq thumbnails għal vidjows li għandhom kapitli.", "TaskRefreshChapterImagesDescription": "Joħloq thumbnails għal vidjows li għandhom kapitli.",
"TaskAudioNormalization": "Normalizzazzjoni Awdjo", "TaskAudioNormalization": "Normalizzazzjoni tal-Awdjo",
"TaskAudioNormalizationDescription": "Skennja fajls għal data ta' normalizzazzjoni awdjo.", "TaskAudioNormalizationDescription": "Skennja fajls għal data fuq in-normalizzazzjoni tal-awdjo.",
"TaskRefreshLibrary": "Skennja l-Librerija tal-Midja", "TaskRefreshLibrary": "Skennja l-Librerija tal-Midja",
"TaskRefreshLibraryDescription": "Jiskennja l-librerija tal-midja tiegħek għal fajls ġodda u jġedded il-metadejta.", "TaskRefreshLibraryDescription": "Jiskennja l-librerija tal-midja tiegħek għal fajls ġodda u jġedded il-metadejta.",
"TaskCleanLogs": "Naddaf id-Direttorju tal-Logg", "TaskCleanLogs": "Naddaf id-Direttorju tal-Logg",
"TaskCleanLogsDescription": "Iħassar fajls tal-logg eqdem minn {0} ijiem.", "TaskCleanLogsDescription": "Iħassar fajls tal-logg eqdem minn {0} ijiem.",
"TaskRefreshPeople": "Aġġorna Persuni", "TaskRefreshPeople": "Aġġorna l-Persuni",
"TaskRefreshPeopleDescription": "Jaġġorna l-metadejta għall-atturi u d-diretturi fil-librerija tal-midja tiegħek.", "TaskRefreshPeopleDescription": "Jaġġorna l-metadata għall-atturi u d-diretturi fil-librerija tal-midja tiegħek.",
"TaskRefreshTrickplayImages": "Iġġenera Stampi Trickplay", "TaskRefreshTrickplayImages": "Iġġenera Stampi Trickplay",
"TaskRefreshTrickplayImagesDescription": "Joħloq previews trickplay għal vidjows fil-libreriji attivati.", "TaskRefreshTrickplayImagesDescription": "Joħloq previews trickplay għal videos fil-libreriji li għalihom hi attivata.",
"TaskUpdatePlugins": "Aġġorna il-Plugins", "TaskUpdatePlugins": "Aġġorna l-Plugins",
"TaskCleanTranscode": "Naddaf id-Direttorju tat-Transcode", "TaskCleanTranscode": "Naddaf id-Direttorju tat-Transcoding",
"TaskCleanTranscodeDescription": "Iħassar fajls transcode eqdem minn ġurnata.", "TaskCleanTranscodeDescription": "Iħassar fajls tat-transcoding li huma eqdem minn ġurnata.",
"TaskRefreshChannels": "Aġġorna l-Kanali", "TaskRefreshChannels": "Aġġorna l-Istazzjonijiet",
"TaskRefreshChannelsDescription": "Aġġorna l-informazzjoni tal-kanali tal-internet.", "TaskRefreshChannelsDescription": "Aġġorna l-informazzjoni tal-istazzjonijiet tal-internet.",
"TaskDownloadMissingSubtitles": "Niżżel is-sottotitli nieqsa", "TaskDownloadMissingSubtitles": "Niżżel is-sottotitli nieqsa",
"TaskOptimizeDatabase": "Ottimizza d-database", "TaskOptimizeDatabase": "Ottimiżża d-database",
"TaskKeyframeExtractor": "Estrattur ta' Keyframes", "TaskKeyframeExtractor": "Estrattur ta' Keyframes",
"TaskKeyframeExtractorDescription": "Jiġbed il-keyframes mill-fajls tal-vidjow biex joħloq playlists HLS aktar preċiżi. Dan il-kompitu jista' jdum għal żmien twil.", "TaskKeyframeExtractorDescription": "Jiġbed il-keyframes mill-fajls tal-videos biex jagħmel playlists HLS aktar preċiżi. Dan it-task jista' jdum żmien twil biex ilesti.",
"TaskCleanCollectionsAndPlaylists": "Naddaf il-kollezzjonijiet u l-playlists", "TaskCleanCollectionsAndPlaylists": "Naddaf il-kollezzjonijiet u l-playlists",
"TaskCleanCollectionsAndPlaylistsDescription": "Ineħħi oġġetti minn kollezzjonijiet u playlists li m'għadhomx jeżistu." "TaskCleanCollectionsAndPlaylistsDescription": "Ineħħi oġġetti minn kollezzjonijiet u playlists li m'għadhomx jeżistu.",
"TaskDownloadMissingLyrics": "Niżżel il-lirika nieqsa",
"TaskDownloadMissingLyricsDescription": "Iniżżel il-lirika għal-kanzunetti",
"TaskExtractMediaSegments": "Scan tas-Sezzjoni tal-Midja",
"TaskExtractMediaSegmentsDescription": "Jestratta jew iġib sezzjonijiet tal-midja minn plugins attivati tal-MediaSegment.",
"TaskMoveTrickplayImages": "Mexxi l-post tat-Trickplay Image",
"TaskMoveTrickplayImagesDescription": "Tmexxi l-files tat-trickplay li jeżistu skont kif inhi kkonfigurata l-librerija."
} }

View File

@ -11,7 +11,7 @@
"Collections": "Collecties", "Collections": "Collecties",
"DeviceOfflineWithName": "Verbinding met {0} is verbroken", "DeviceOfflineWithName": "Verbinding met {0} is verbroken",
"DeviceOnlineWithName": "{0} is verbonden", "DeviceOnlineWithName": "{0} is verbonden",
"FailedLoginAttemptWithUserName": "Mislukte aanmeldpoging vanaf {0}", "FailedLoginAttemptWithUserName": "Mislukte aanmeldpoging van {0}",
"Favorites": "Favorieten", "Favorites": "Favorieten",
"Folders": "Mappen", "Folders": "Mappen",
"Genres": "Genres", "Genres": "Genres",
@ -117,7 +117,7 @@
"TaskCleanActivityLogDescription": "Verwijdert activiteitenlogs ouder dan de ingestelde leeftijd.", "TaskCleanActivityLogDescription": "Verwijdert activiteitenlogs ouder dan de ingestelde leeftijd.",
"TaskCleanActivityLog": "Activiteitenlogboek legen", "TaskCleanActivityLog": "Activiteitenlogboek legen",
"Undefined": "Niet gedefinieerd", "Undefined": "Niet gedefinieerd",
"Forced": "Geforceerd", "Forced": "Gedwongen",
"Default": "Standaard", "Default": "Standaard",
"TaskOptimizeDatabaseDescription": "Comprimeert de database en trimt vrije ruimte. Het uitvoeren van deze taak kan de prestaties verbeteren, na het scannen van de bibliotheek of andere aanpassingen die invloed hebben op de database.", "TaskOptimizeDatabaseDescription": "Comprimeert de database en trimt vrije ruimte. Het uitvoeren van deze taak kan de prestaties verbeteren, na het scannen van de bibliotheek of andere aanpassingen die invloed hebben op de database.",
"TaskOptimizeDatabase": "Database optimaliseren", "TaskOptimizeDatabase": "Database optimaliseren",

View File

@ -120,5 +120,20 @@
"Albums": "ਐਲਬਮਾਂ", "Albums": "ਐਲਬਮਾਂ",
"TaskOptimizeDatabase": "ਡਾਟਾਬੇਸ ਅਨੁਕੂਲ ਬਣਾਓ", "TaskOptimizeDatabase": "ਡਾਟਾਬੇਸ ਅਨੁਕੂਲ ਬਣਾਓ",
"External": "ਬਾਹਰੀ", "External": "ਬਾਹਰੀ",
"HearingImpaired": "ਸੁਨਣ ਵਿਚ ਕਮਜ਼ੋਰ" "HearingImpaired": "ਸੁਨਣ ਵਿਚ ਕਮਜ਼ੋਰ",
"TaskAudioNormalizationDescription": "ਆਵਾਜ਼ ਸਧਾਰਣੀਕਰਨ ਡਾਟਾ ਲਈ ਫਾਇਲਾਂ ਖੋਜੋ।",
"TaskRefreshTrickplayImages": "ਟ੍ਰਿਕਪਲੇ ਤਸਵੀਰਾਂ ਤਿਆਰ ਕਰੋ",
"TaskExtractMediaSegments": "ਮੀਡੀਆ ਸੈਗਮੈਂਟ ਸਕੈਨ",
"TaskMoveTrickplayImagesDescription": "ਟ੍ਰਿਕਪਲੇ ਤਸਵੀਰਾਂ ਦੀ ਜਗਾ ਨੂੰ ਲਾਇਬ੍ਰੇਰੀ ਸੈਟਿੰਗਜ਼ ਅਨੁਸਾਰ ਬਦਲੋ।",
"TaskOptimizeDatabaseDescription": "ਡੇਟਾਬੇਸ ਨੂੰ ਸੰਗ੍ਰਹਿਤ ਕਰਦਾ ਹੈ ਅਤੇ ਖਾਲੀ ਜਗ੍ਹਾ ਘਟਾਉਂਦਾ ਹੈ। ਲਾਇਬ੍ਰੇਰੀ ਸਕੈਨ ਕਰਨ ਜਾਂ ਡੇਟਾਬੇਸ ਵਿੱਚ ਸੋਧਾਂ ਕਰਨ ਤੋਂ ਬਾਅਦ ਇਸ ਕੰਮ ਨੂੰ ਚਲਾਉਣਾ ਪ੍ਰਦਰਸ਼ਨ ਵਿੱਚ ਸੁਧਾਰ ਕਰ ਸਕਦਾ ਹੈ।",
"TaskExtractMediaSegmentsDescription": "ਮੀਡੀਆ ਸੈਗਮੈਂਟ ਨੂੰ ਮੀਡੀਆਸੈਗਮੈਂਟ ਯੋਗ ਪਲੱਗਇਨਾਂ ਤੋਂ ਨਿਕਾਲਦਾ ਜਾਂ ਪ੍ਰਾਪਤ ਕਰਦਾ ਹੈ।",
"TaskMoveTrickplayImages": "ਟ੍ਰਿਕਪਲੇ ਤਸਵੀਰਾਂ ਦੀ ਜਗਾ ਬਦਲੋ",
"TaskDownloadMissingLyrics": "ਅਧੂਰੇ ਬੋਲ ਡਾਊਨਲੋਡ ਕਰੋ",
"TaskDownloadMissingLyricsDescription": "ਗੀਤਾਂ ਲਈ ਡਾਊਨਲੋਡ ਕਿਤੇ ਬੋਲ",
"TaskKeyframeExtractor": "ਕੀ-ਫ੍ਰੇਮ ਐਕਸਟ੍ਰੈਕਟਰ",
"TaskCleanCollectionsAndPlaylistsDescription": "ਕਲੈਕਸ਼ਨਾਂ ਅਤੇ ਪਲੇਲਿਸਟਾਂ ਵਿੱਚੋਂ ਉਹ ਆਈਟਮ ਹਟਾਉਂਦਾ ਹੈ ਜੋ ਹੁਣ ਮੌਜੂਦ ਨਹੀਂ ਹਨ।",
"TaskCleanCollectionsAndPlaylists": "ਕਲੈਕਸ਼ਨਾਂ ਅਤੇ ਪਲੇਲਿਸਟਾਂ ਨੂੰ ਸਾਫ ਕਰੋ",
"TaskAudioNormalization": "ਆਵਾਜ਼ ਸਧਾਰਣੀਕਰਨ",
"TaskRefreshTrickplayImagesDescription": "ਚਲ ਰਹੀ ਲਾਇਬ੍ਰੇਰੀਆਂ ਵਿੱਚ ਵੀਡੀਓਜ਼ ਲਈ ਟ੍ਰਿਕਪਲੇ ਪ੍ਰੀਵਿਊ ਬਣਾਉਂਦਾ ਹੈ।",
"TaskKeyframeExtractorDescription": "ਕੀ-ਫ੍ਰੇਮਜ਼ ਨੂੰ ਵੀਡੀਓ ਫਾਈਲਾਂ ਵਿੱਚੋਂ ਨਿਕਾਲਦਾ ਹੈ ਤਾਂ ਜੋ ਹੋਰ ਜ਼ਿਆਦਾ ਸਟਿਕ ਹੋਣ ਵਾਲੀਆਂ HLS ਪਲੇਲਿਸਟਾਂ ਬਣਾਈਆਂ ਜਾ ਸਕਣ। ਇਹ ਕੰਮ ਲੰਬੇ ਸਮੇਂ ਤੱਕ ਚੱਲ ਸਕਦਾ ਹੈ।"
} }

View File

@ -13,7 +13,7 @@
"HeaderContinueWatching": "Continuar a ver", "HeaderContinueWatching": "Continuar a ver",
"HeaderAlbumArtists": "Artistas do Álbum", "HeaderAlbumArtists": "Artistas do Álbum",
"Genres": "Géneros", "Genres": "Géneros",
"Folders": "Diretórios", "Folders": "Pastas",
"Favorites": "Favoritos", "Favorites": "Favoritos",
"Channels": "Canais", "Channels": "Canais",
"UserDownloadingItemWithValues": "{0} está sendo baixado {1}", "UserDownloadingItemWithValues": "{0} está sendo baixado {1}",

View File

@ -77,7 +77,7 @@
"HeaderAlbumArtists": "Artiști album", "HeaderAlbumArtists": "Artiști album",
"Genres": "Genuri", "Genres": "Genuri",
"Folders": "Dosare", "Folders": "Dosare",
"Favorites": "Favorite", "Favorites": "Preferate",
"FailedLoginAttemptWithUserName": "Încercare de conectare eșuată pentru {0}", "FailedLoginAttemptWithUserName": "Încercare de conectare eșuată pentru {0}",
"DeviceOnlineWithName": "{0} este conectat", "DeviceOnlineWithName": "{0} este conectat",
"DeviceOfflineWithName": "{0} s-a deconectat", "DeviceOfflineWithName": "{0} s-a deconectat",

View File

@ -132,5 +132,9 @@
"TaskAudioNormalization": "Нормализация звука", "TaskAudioNormalization": "Нормализация звука",
"TaskAudioNormalizationDescription": "Сканирует файлы на наличие данных о нормализации звука.", "TaskAudioNormalizationDescription": "Сканирует файлы на наличие данных о нормализации звука.",
"TaskDownloadMissingLyrics": "Загрузить недостающий текст", "TaskDownloadMissingLyrics": "Загрузить недостающий текст",
"TaskDownloadMissingLyricsDescription": "Загружает текст песен" "TaskDownloadMissingLyricsDescription": "Загружает текст песен",
"TaskMoveTrickplayImages": "Перенесение местоположения изображений Trickplay",
"TaskExtractMediaSegments": "Сканирование медиасегментов",
"TaskExtractMediaSegmentsDescription": "Извлекает или получает медиасегменты из плагинов MediaSegment.",
"TaskMoveTrickplayImagesDescription": "Перемещает существующие файлы trickplay в соответствии с настройками медиатеки."
} }

View File

@ -126,5 +126,15 @@
"TaskKeyframeExtractorDescription": "Iz video datoteke Izvleče ključne sličice, da ustvari bolj natančne sezname predvajanja HLS. Proces lahko traja dolgo časa.", "TaskKeyframeExtractorDescription": "Iz video datoteke Izvleče ključne sličice, da ustvari bolj natančne sezname predvajanja HLS. Proces lahko traja dolgo časa.",
"HearingImpaired": "Oslabljen sluh", "HearingImpaired": "Oslabljen sluh",
"TaskRefreshTrickplayImages": "Ustvari Trickplay slike", "TaskRefreshTrickplayImages": "Ustvari Trickplay slike",
"TaskRefreshTrickplayImagesDescription": "Ustvari trickplay predoglede za posnetke v omogočenih knjižnicah." "TaskRefreshTrickplayImagesDescription": "Ustvari trickplay predoglede za posnetke v omogočenih knjižnicah.",
"TaskExtractMediaSegmentsDescription": "Ekstrahira ali pridobi medijske segmente iz vtičnikov, ki podpirajo MediaSegment.",
"TaskMoveTrickplayImagesDescription": "Premakne obstoječe datoteke trickplay v skladu z nastavitvami knjižnice.",
"TaskExtractMediaSegments": "Skeniranje segmentov v medijih",
"TaskMoveTrickplayImages": "Preseli lokacijo Trickplay slik",
"TaskDownloadMissingLyrics": "Prenesi manjkajoča besedila pesmi",
"TaskDownloadMissingLyricsDescription": "Prenesi besedila za pesmi",
"TaskCleanCollectionsAndPlaylists": "Počisti zbirke in sezname predvajanja",
"TaskAudioNormalization": "Normalizacija zvoka",
"TaskAudioNormalizationDescription": "Pregled datotek za podatke o normalizaciji zvoka.",
"TaskCleanCollectionsAndPlaylistsDescription": "Odstrani elemente iz zbirk in seznamov predvajanja, ki ne obstajajo več."
} }

View File

@ -78,7 +78,7 @@
"Genres": "Жанрови", "Genres": "Жанрови",
"Folders": "Фасцикле", "Folders": "Фасцикле",
"Favorites": "Омиљено", "Favorites": "Омиљено",
"FailedLoginAttemptWithUserName": "Неуспела пријава са {0}", "FailedLoginAttemptWithUserName": "Неуспели покушај пријавe са {0}",
"DeviceOnlineWithName": "{0} је повезан", "DeviceOnlineWithName": "{0} је повезан",
"DeviceOfflineWithName": "{0} је прекинуо везу", "DeviceOfflineWithName": "{0} је прекинуо везу",
"Collections": "Колекције", "Collections": "Колекције",
@ -121,7 +121,10 @@
"TaskOptimizeDatabase": "Оптимизуј банку података", "TaskOptimizeDatabase": "Оптимизуј банку података",
"TaskOptimizeDatabaseDescription": "Сажима базу података и скраћује слободан простор. Покретање овог задатка након скенирања библиотеке или других промена које подразумевају измене базе података које могу побољшати перформансе.", "TaskOptimizeDatabaseDescription": "Сажима базу података и скраћује слободан простор. Покретање овог задатка након скенирања библиотеке или других промена које подразумевају измене базе података које могу побољшати перформансе.",
"External": "Спољно", "External": "Спољно",
"TaskKeyframeExtractorDescription": "Екстрактује кљулне сличице из видео датотека да би креирао више преицзну HLS плеј-листу. Овај задатак може да потраје дуже време.", "TaskKeyframeExtractorDescription": "Екстрактује кључне сличице из видео датотека да би креирао више прецизнију HLS плејлисту. Овај задатак може да потраје дуже време.",
"TaskKeyframeExtractor": "Екстрактор кључних сличица", "TaskKeyframeExtractor": "Екстрактор кључних сличица",
"HearingImpaired": "ослабљен слух" "HearingImpaired": "ослабљен слух",
"TaskAudioNormalization": "Нормализација звука",
"TaskCleanCollectionsAndPlaylists": "Очистите колекције и плејлисте",
"TaskAudioNormalizationDescription": "Скенира датотеке за податке о нормализацији звука."
} }

View File

@ -82,13 +82,13 @@
"UserCreatedWithName": "Användaren {0} har skapats", "UserCreatedWithName": "Användaren {0} har skapats",
"UserDeletedWithName": "Användaren {0} har tagits bort", "UserDeletedWithName": "Användaren {0} har tagits bort",
"UserDownloadingItemWithValues": "{0} laddar ner {1}", "UserDownloadingItemWithValues": "{0} laddar ner {1}",
"UserLockedOutWithName": "Användare {0} har låsts ute", "UserLockedOutWithName": "Användare {0} har utelåsts",
"UserOfflineFromDevice": "{0} har avbrutit anslutningen från {1}", "UserOfflineFromDevice": "{0} har kopplat ned från {1}",
"UserOnlineFromDevice": "{0} är uppkopplad från {1}", "UserOnlineFromDevice": "{0} är uppkopplad från {1}",
"UserPasswordChangedWithName": "Lösenordet för {0} har ändrats", "UserPasswordChangedWithName": "Lösenordet för {0} har ändrats",
"UserPolicyUpdatedWithName": "Användarpolicyn har uppdaterats för {0}", "UserPolicyUpdatedWithName": "Användarpolicyn har uppdaterats för {0}",
"UserStartedPlayingItemWithValues": "{0} spelar upp {1} på {2}", "UserStartedPlayingItemWithValues": "{0} spelar {1} på {2}",
"UserStoppedPlayingItemWithValues": "{0} har avslutat uppspelningen av {1} på {2}", "UserStoppedPlayingItemWithValues": "{0} har stoppat uppspelningen av {1} på {2}",
"ValueHasBeenAddedToLibrary": "{0} har lagts till i ditt mediebibliotek", "ValueHasBeenAddedToLibrary": "{0} har lagts till i ditt mediebibliotek",
"ValueSpecialEpisodeName": "Specialavsnitt - {0}", "ValueSpecialEpisodeName": "Specialavsnitt - {0}",
"VersionNumber": "Version {0}", "VersionNumber": "Version {0}",
@ -98,8 +98,8 @@
"TaskRefreshChannels": "Uppdatera kanaler", "TaskRefreshChannels": "Uppdatera kanaler",
"TaskCleanTranscodeDescription": "Raderar omkodningsfiler äldre än en dag.", "TaskCleanTranscodeDescription": "Raderar omkodningsfiler äldre än en dag.",
"TaskCleanTranscode": "Rensa omkodningskatalog", "TaskCleanTranscode": "Rensa omkodningskatalog",
"TaskUpdatePluginsDescription": "Laddar ned och installerar uppdateringar till tilläggsprogram som är konfigurerade att uppdateras automatiskt.", "TaskUpdatePluginsDescription": "Laddar ned och installerar uppdateringar till tillägg som är konfigurerade att uppdateras automatiskt.",
"TaskUpdatePlugins": "Uppdatera tilläggsprogram", "TaskUpdatePlugins": "Uppdatera tillägg",
"TaskRefreshPeopleDescription": "Uppdaterar metadata för skådespelare och regissörer i ditt mediabibliotek.", "TaskRefreshPeopleDescription": "Uppdaterar metadata för skådespelare och regissörer i ditt mediabibliotek.",
"TaskCleanLogsDescription": "Raderar loggfiler som är mer än {0} dagar gamla.", "TaskCleanLogsDescription": "Raderar loggfiler som är mer än {0} dagar gamla.",
"TaskCleanLogs": "Rensa loggkatalog", "TaskCleanLogs": "Rensa loggkatalog",

View File

@ -23,5 +23,92 @@
"HeaderLiveTV": "Jonli TV", "HeaderLiveTV": "Jonli TV",
"HeaderNextUp": "Keyingisi", "HeaderNextUp": "Keyingisi",
"ItemAddedWithName": "{0} kutbxonaga qo'shildi", "ItemAddedWithName": "{0} kutbxonaga qo'shildi",
"LabelIpAddressValue": "IP manzil: {0}" "LabelIpAddressValue": "IP manzil: {0}",
"SubtitleDownloadFailureFromForItem": "{0} dan {1} uchun taglavhalarni yuklab boʻlmadi",
"UserPasswordChangedWithName": "Foydalanuvchi {0} paroli oʻzgartirildi",
"ValueHasBeenAddedToLibrary": "{0} kutubxonaga qoʻshildi",
"TaskCleanActivityLogDescription": "Belgilangan yoshdan kattaroq faoliyat jurnali yozuvlarini oʻchiradi.",
"TaskAudioNormalization": "Ovozni normallashtirish",
"TaskRefreshLibraryDescription": "Media kutubxonasi yangi fayllar uchun skanerlanmoqda va metama'lumotlar yangilanmoqda.",
"Default": "Joriy",
"HeaderFavoriteAlbums": "Tanlangan albomlar",
"HeaderFavoriteArtists": "Tanlangan artistlar",
"HeaderFavoriteEpisodes": "Tanlangan epizodlar",
"HeaderFavoriteShows": "Tanlangan shoular",
"HeaderFavoriteSongs": "Tanlangan qo'shiqlar",
"HeaderRecordingGroups": "Yozuvlar guruhi",
"HomeVideos": "Uy videolari",
"NotificationOptionVideoPlaybackStopped": "Video ijrosi toʻxtatildi",
"TvShows": "TV seriallar",
"Undefined": "Belgilanmagan",
"User": "Foydalanuvchi",
"UserCreatedWithName": "{0} foydalanuvchi yaratildi",
"TaskCleanCacheDescription": "Tizimga kerak bo'lmagan kesh fayllari o'chiriladi.",
"TaskAudioNormalizationDescription": "Ovozni normallashtirish ma'lumotlari uchun fayllarni skanerlaydi.",
"PluginInstalledWithName": "{0} - o'rnatildi",
"PluginUninstalledWithName": "{0} - o'chirildi",
"HearingImpaired": "Yaxshi eshitmaydiganlar uchun",
"Inherit": "Meroslangan",
"NotificationOptionApplicationUpdateAvailable": "Ilova yangilanishi mavjud",
"NotificationOptionApplicationUpdateInstalled": "Ilova yangilanishi oʻrnatildi",
"LabelRunningTimeValue": "Davomiyligi",
"NotificationOptionAudioPlayback": "Audio tinglash boshlandi",
"NotificationOptionAudioPlaybackStopped": "Audio tinglash to'xtatildi",
"NotificationOptionCameraImageUploaded": "Kamera tasvirlari yuklandi",
"NotificationOptionInstallationFailed": "O'rnatishda hatolik",
"NotificationOptionNewLibraryContent": "Yangi tarkib qo'shildi",
"NotificationOptionPluginError": "Plagin ishdan chiqdi",
"NotificationOptionPluginInstalled": "Plagin o'rnatildi",
"NotificationOptionPluginUninstalled": "Plagin o'chirildi",
"NotificationOptionPluginUpdateInstalled": "Plagin uchun yangilanish o'rnatildi",
"NotificationOptionServerRestartRequired": "Server-ni qayta yuklash lozim",
"NotificationOptionTaskFailed": "Rejalashtirilgan vazifa bajarilmadi",
"NotificationOptionUserLockedOut": "Foydalanuvchi bloklangan",
"NotificationOptionVideoPlayback": "Video ijrosi boshlandi",
"Photos": "Surat",
"Latest": "So'ngi",
"MessageApplicationUpdated": "Jellyfin Server yangilandi",
"MessageApplicationUpdatedTo": "Jellyfin Server {0} gacha yangilandi",
"MessageNamedServerConfigurationUpdatedWithValue": "Server konfiguratsiyasi ({0}-boʻlim) yangilandi",
"MessageServerConfigurationUpdated": "Server konfiguratsiyasi yangilandi",
"MixedContent": "Aralashgan tarkib",
"Movies": "Kinolar",
"Music": "Qo'shiqlar",
"MusicVideos": "Musiqali videolar",
"NameInstallFailed": "Omadsiz ornatish {0}",
"NameSeasonNumber": "{0} Fasl",
"NameSeasonUnknown": "Fasl aniqlanmagan",
"Playlists": "Pleylistlar",
"NewVersionIsAvailable": "Yuklab olish uchun Jellyfin Server ning yangi versiyasi mavjud",
"Plugin": "Plagin",
"TaskCleanLogs": "Jurnallar katalogini tozalash",
"PluginUpdatedWithName": "{0} - yangilandi",
"ProviderValue": "Yetkazib beruvchi: {0}",
"ScheduledTaskFailedWithName": "{0} - omadsiz",
"ScheduledTaskStartedWithName": "{0} - ishga tushirildi",
"ServerNameNeedsToBeRestarted": "Qayta yuklash kerak {0}",
"Shows": "Teleko'rsatuv",
"Songs": "Kompozitsiyalar",
"StartupEmbyServerIsLoading": "Jellyfin Server yuklanmoqda. Tez orada qayta urinib koʻring.",
"Sync": "Sinxronizatsiya",
"System": "Tizim",
"UserDeletedWithName": "{0} foydalanuvchisi oʻchirib tashlandi",
"UserDownloadingItemWithValues": "{0} yuklanmoqda {1}",
"UserLockedOutWithName": "{0} foydalanuvchisi bloklandi",
"UserOfflineFromDevice": "{0} {1}dan uzildi",
"UserOnlineFromDevice": "{0} {1} dan ulandi",
"UserPolicyUpdatedWithName": "{0} foydalanuvchisining siyosatlari yangilandi",
"UserStartedPlayingItemWithValues": "{0} - {2} da \"{1}\" ijrosi",
"UserStoppedPlayingItemWithValues": "{0} - ijro etish toxtatildi {1} {2}",
"ValueSpecialEpisodeName": "Maxsus qism {0}",
"VersionNumber": "Versiya {0}",
"TasksMaintenanceCategory": "Xizmat ko'rsatish",
"TasksLibraryCategory": "Media kutubxona",
"TasksApplicationCategory": "Ilova",
"TasksChannelsCategory": "Internet kanallari",
"TaskCleanActivityLog": "Faoliyat jurnalini tozalash",
"TaskCleanCache": "Kesh katalogini tozalash",
"TaskRefreshChapterImages": "Sahnadan tasvirini chiqarish",
"TaskRefreshChapterImagesDescription": "Sahnalarni o'z ichiga olgan videolar uchun eskizlarni yaratadi.",
"TaskRefreshLibrary": "Media kutubxonangizni skanerlash"
} }

View File

@ -126,5 +126,15 @@
"External": "外部", "External": "外部",
"HearingImpaired": "聽力障礙", "HearingImpaired": "聽力障礙",
"TaskRefreshTrickplayImages": "建立 Trickplay 圖像", "TaskRefreshTrickplayImages": "建立 Trickplay 圖像",
"TaskRefreshTrickplayImagesDescription": "為已啟用 Trickplay 的媒體庫內的影片建立 Trickplay 預覽圖。" "TaskRefreshTrickplayImagesDescription": "為已啟用 Trickplay 的媒體庫內的影片建立 Trickplay 預覽圖。",
"TaskExtractMediaSegments": "掃描媒體段落",
"TaskExtractMediaSegmentsDescription": "從MediaSegment中被允許的插件獲取媒體段落。",
"TaskDownloadMissingLyrics": "下載欠缺歌詞",
"TaskDownloadMissingLyricsDescription": "下載歌詞",
"TaskCleanCollectionsAndPlaylists": "整理媒體與播放清單",
"TaskAudioNormalization": "音訊同等化",
"TaskAudioNormalizationDescription": "掃描檔案裏的音訊同等化資料。",
"TaskCleanCollectionsAndPlaylistsDescription": "從資料庫及播放清單中移除已不存在的項目。",
"TaskMoveTrickplayImagesDescription": "根據媒體庫設定移動現有的 Trickplay 檔案。",
"TaskMoveTrickplayImages": "轉移 Trickplay 影像位置"
} }

View File

@ -8,7 +8,7 @@
"CameraImageUploadedFrom": "已從 {0} 成功上傳一張相片", "CameraImageUploadedFrom": "已從 {0} 成功上傳一張相片",
"Channels": "頻道", "Channels": "頻道",
"ChapterNameValue": "章節 {0}", "ChapterNameValue": "章節 {0}",
"Collections": "系列", "Collections": "系列",
"DeviceOfflineWithName": "{0} 已中斷連接", "DeviceOfflineWithName": "{0} 已中斷連接",
"DeviceOnlineWithName": "{0} 已連接", "DeviceOnlineWithName": "{0} 已連接",
"FailedLoginAttemptWithUserName": "來自使用者 {0} 的登入失敗嘗試", "FailedLoginAttemptWithUserName": "來自使用者 {0} 的登入失敗嘗試",
@ -126,8 +126,8 @@
"HearingImpaired": "聽力障礙", "HearingImpaired": "聽力障礙",
"TaskRefreshTrickplayImages": "生成快轉縮圖", "TaskRefreshTrickplayImages": "生成快轉縮圖",
"TaskRefreshTrickplayImagesDescription": "為啟用快轉縮圖的媒體庫生成快轉縮圖。", "TaskRefreshTrickplayImagesDescription": "為啟用快轉縮圖的媒體庫生成快轉縮圖。",
"TaskCleanCollectionsAndPlaylists": "清理系列和播放清單", "TaskCleanCollectionsAndPlaylists": "清理系列和播放清單",
"TaskCleanCollectionsAndPlaylistsDescription": "清理系列播放清單中已不存在的項目。", "TaskCleanCollectionsAndPlaylistsDescription": "清理系列作品與播放清單中已不存在的項目。",
"TaskAudioNormalization": "音量標準化", "TaskAudioNormalization": "音量標準化",
"TaskAudioNormalizationDescription": "掃描文件以找出音量標準化資料。", "TaskAudioNormalizationDescription": "掃描文件以找出音量標準化資料。",
"TaskDownloadMissingLyrics": "下載缺少的歌詞", "TaskDownloadMissingLyrics": "下載缺少的歌詞",

View File

@ -231,13 +231,13 @@ namespace Emby.Server.Implementations.Localization
ratings.Add(new ParentalRating("21", 21)); ratings.Add(new ParentalRating("21", 21));
} }
// A lot of countries don't excplicitly have a seperate rating for adult content // A lot of countries don't explicitly have a separate rating for adult content
if (ratings.All(x => x.Value != 1000)) if (ratings.All(x => x.Value != 1000))
{ {
ratings.Add(new ParentalRating("XXX", 1000)); ratings.Add(new ParentalRating("XXX", 1000));
} }
// A lot of countries don't excplicitly have a seperate rating for banned content // A lot of countries don't explicitly have a separate rating for banned content
if (ratings.All(x => x.Value != 1001)) if (ratings.All(x => x.Value != 1001))
{ {
ratings.Add(new ParentalRating("Banned", 1001)); ratings.Add(new ParentalRating("Banned", 1001));

View File

@ -1,8 +1,14 @@
Livre,0 Livre,0
L,0 L,0
ER,9 AL,0
ER,10
10,10 10,10
A10,10
12,12 12,12
A12,12
14,14 14,14
A14,14
16,16 16,16
A16,16
18,18 18,18
A18,18

1 Livre 0
2 L 0
3 ER AL 9 0
4 ER 10
5 10 10
6 A10 10
7 12 12
8 A12 12
9 14 14
10 A14 14
11 16 16
12 A16 16
13 18 18
14 A18 18

View File

@ -6,8 +6,6 @@ TV-Y7,7
TV-Y7-FV,7 TV-Y7-FV,7
PG,9 PG,9
TV-PG,9 TV-PG,9
PG-13,13
13+,13
TV-14,14 TV-14,14
14A,14 14A,14
16+,16 16+,16

1 E 0
6 TV-Y7-FV 7
7 PG 9
8 TV-PG 9
PG-13 13
13+ 13
9 TV-14 14
10 14A 14
11 16+ 16

View File

@ -1,7 +1,7 @@
A,0 A,0
A/fig,0 A/fig,0
A/i,0 A/i,0
A/fig/i,0 A/i/fig,0
APTA,0 APTA,0
ERI,0 ERI,0
TP,0 TP,0

1 A 0
2 A/fig 0
3 A/i 0
4 A/fig/i A/i/fig 0
5 APTA 0
6 ERI 0
7 TP 0

View File

@ -6,10 +6,11 @@ U,0
6+,6 6+,6
7+,7 7+,7
PG,8 PG,8
9+,9 9,9
12,12 12,12
12+,12 12+,12
12A,12 12A,12
12PG,12
Teen,13 Teen,13
13+,13 13+,13
14+,14 14+,14

1 All 0
6 6+ 6
7 7+ 7
8 PG 8
9 9+ 9 9
10 12 12
11 12+ 12
12 12A 12
13 12PG 12
14 Teen 13
15 13+ 13
16 14+ 14

View File

@ -4,6 +4,7 @@ PG,12
12A,12 12A,12
12PG,12 12PG,12
15,15 15,15
15PG,15
15A,15 15A,15
16,16 16,16
18,18 18,18

1 G 4
4 12A 12
5 12PG 12
6 15 15
7 15PG 15
8 15A 15
9 16 16
10 18 18

View File

@ -6,4 +6,5 @@ A,0
12,12 12,12
15,15 15,15
18,18 18,18
C,18
Not approved,1001 Not approved,1001

1 A 0
6 12 12
7 15 15
8 18 18
9 C 18
10 Not approved 1001

View File

@ -10,6 +10,7 @@ R16,16
RP16,16 RP16,16
GA,18 GA,18
R18,18 R18,18
RP18,18
MA,1000 MA,1000
R,1001 R,1001
Objectionable,1001 Objectionable,1001

1 Exempt 0
10 RP16 16
11 GA 18
12 R18 18
13 RP18 18
14 MA 1000
15 R 1001
16 Objectionable 1001

View File

@ -5,23 +5,23 @@ TV-Y,0
TV-Y7,7 TV-Y7,7
TV-Y7-FV,7 TV-Y7-FV,7
PG,10 PG,10
TV-PG,10
TV-PG-D,10
TV-PG-L,10
TV-PG-S,10
TV-PG-V,10
TV-PG-DL,10
TV-PG-DS,10
TV-PG-DV,10
TV-PG-LS,10
TV-PG-LV,10
TV-PG-SV,10
TV-PG-DLS,10
TV-PG-DLV,10
TV-PG-DSV,10
TV-PG-LSV,10
TV-PG-DLSV,10
PG-13,13 PG-13,13
TV-PG,13
TV-PG-D,13
TV-PG-L,13
TV-PG-S,13
TV-PG-V,13
TV-PG-DL,13
TV-PG-DS,13
TV-PG-DV,13
TV-PG-LS,13
TV-PG-LV,13
TV-PG-SV,13
TV-PG-DLS,13
TV-PG-DLV,13
TV-PG-DSV,13
TV-PG-LSV,13
TV-PG-DLSV,13
TV-14,14 TV-14,14
TV-14-D,14 TV-14-D,14
TV-14-L,14 TV-14-L,14
@ -48,3 +48,5 @@ TV-MA-LS,17
TV-MA-LV,17 TV-MA-LV,17
TV-MA-SV,17 TV-MA-SV,17
TV-MA-LSV,17 TV-MA-LSV,17
TV-X,18
TV-AO,18

1 Approved 0
5 TV-Y7 7
6 TV-Y7-FV 7
7 PG 10
8 TV-PG 10
9 TV-PG-D 10
10 TV-PG-L 10
11 TV-PG-S 10
12 TV-PG-V 10
13 TV-PG-DL 10
14 TV-PG-DS 10
15 TV-PG-DV 10
16 TV-PG-LS 10
17 TV-PG-LV 10
18 TV-PG-SV 10
19 TV-PG-DLS 10
20 TV-PG-DLV 10
21 TV-PG-DSV 10
22 TV-PG-LSV 10
23 TV-PG-DLSV 10
24 PG-13 13
TV-PG 13
TV-PG-D 13
TV-PG-L 13
TV-PG-S 13
TV-PG-V 13
TV-PG-DL 13
TV-PG-DS 13
TV-PG-DV 13
TV-PG-LS 13
TV-PG-LV 13
TV-PG-SV 13
TV-PG-DLS 13
TV-PG-DLV 13
TV-PG-DSV 13
TV-PG-LSV 13
TV-PG-DLSV 13
25 TV-14 14
26 TV-14-D 14
27 TV-14-L 14
48 TV-MA-LV 17
49 TV-MA-SV 17
50 TV-MA-LSV 17
51 TV-X 18
52 TV-AO 18

View File

@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.MediaEncoder
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly ILogger<EncodingManager> _logger; private readonly ILogger<EncodingManager> _logger;
private readonly IMediaEncoder _encoder; private readonly IMediaEncoder _encoder;
private readonly IChapterManager _chapterManager; private readonly IChapterRepository _chapterManager;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
/// <summary> /// <summary>
@ -40,7 +40,7 @@ namespace Emby.Server.Implementations.MediaEncoder
ILogger<EncodingManager> logger, ILogger<EncodingManager> logger,
IFileSystem fileSystem, IFileSystem fileSystem,
IMediaEncoder encoder, IMediaEncoder encoder,
IChapterManager chapterManager, IChapterRepository chapterManager,
ILibraryManager libraryManager) ILibraryManager libraryManager)
{ {
_logger = logger; _logger = logger;

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