Compare commits

..

79 Commits

Author SHA1 Message Date
Andrew Rabert
54fe7d1454 Mitigate pull_request_target privilege escalation
Hotfix — replaces pull_request_target with pull_request to stop
granting write permissions and secrets to fork PRs. Some workflows
will break; can be fixed properly later.
2026-02-20 19:10:39 -05:00
gnattu
a78c3385c9 Don't list every item just to get count
This emby era logic now becomes a serious performance problem on very huge folders. Use the proper item count query for most common cases.
2025-12-02 00:34:22 +08:00
MBR-0001
daca285568 Revert "Localization/iso6392.txt: change pob and pop" (#15555) 2025-11-23 19:20:29 +01:00
theguymadmax
fbb9a0b2c7 Fix ResolveLinkTarget crashing on exFAT drives (#15568) 2025-11-21 21:14:39 -07:00
Ziyuan Qu
29b3aa8543 Add hidden file check in bdInfo (#15582) 2025-11-21 21:14:30 -07:00
theguymadmax
94f3725208 Fix isMovie filter logic (#15594) 2025-11-21 21:14:03 -07:00
theguymadmax
0ee81e87be Fix locked fields on not saving (#15564) 2025-11-19 17:02:53 +01:00
theguymadmax
c491a918c2 Save item to database before providers run to prevent FK constraint errors (#15563) 2025-11-19 17:01:13 +01:00
gnattu
1e7e46cb82 Prevent copying HDR streams when only SDR is supported (#15556) 2025-11-18 18:37:35 -07:00
theguymadmax
5ae444d96d Fix NullReferenceException in filesystem path comparison (#15548) 2025-11-18 18:37:09 -07:00
gnattu
ee7ad83427 Restrict first video frame probing to file protocol (#15557) 2025-11-18 18:36:59 -07:00
Jellyfin Release Bot
921d7d3364 Bump version to 10.11.3 2025-11-16 17:40:07 -05:00
theguymadmax
f8e012582a Fix movie titles using folder name when NFOs saver is enabled (#15529) 2025-11-16 13:59:58 -07:00
theguymadmax
def5956cd1 Fix tmdbid not detected in single movie folder (#14955) 2025-11-16 13:36:35 -07:00
theguymadmax
abfbaca336 Fix series DateLastMediaAdded not updating when new episodes are added (#15472) 2025-11-16 13:35:43 -07:00
theguymadmax
6566188e45 Add 1 minute tolerance for NFO change detection (#15514) 2025-11-15 08:39:25 -07:00
theguymadmax
078f9584ed Fix playlist DateCreated and DateLastMediaAdded not being set (#15508) 2025-11-14 15:19:40 -07:00
Iksas
ee34c75386 fix missing font extraction for certain transcoding settings (#15502) 2025-11-13 18:30:18 -07:00
theguymadmax
e8150428b6 Fix .ignore handling for directories (#15501) 2025-11-13 18:23:18 -07:00
theguymadmax
4b38e35bbb Remove InheritedTags and update tag filtering logic (#15493) 2025-11-13 18:23:03 -07:00
Huo Jiacheng
435bb14bb2 Fix gitignore-style not working properly on windows. (#15487) 2025-11-12 19:43:13 -07:00
theguymadmax
2e5ced5098 Improve season folder parsing (#15404) 2025-11-12 17:36:57 -07:00
Bond-009
f4a846aa4d Don't error out when searching for marker files fails (#15466)
Fixes #15445
2025-11-11 15:45:47 -07:00
Joshua M. Boniface
7c1063177f Merge pull request #15462 from theguymadmax/fix-exception-for-empty-strm-files
Fix NullReferenceException in GetPathProtocol when path is null
2025-11-10 19:30:38 -05:00
Joshua M. Boniface
5878b1ffc5 Merge pull request #15468 from Bond-009/carefulWithLastMinChanges
Check if target exists before trying to follow it
2025-11-10 19:12:24 -05:00
Bond_009
3c3c2aee0d Check if target exists before trying to follow it
Exception got caught in ManagedFileSystem and wrong file info got returned
2025-11-10 23:19:17 +01:00
theguymadmax
511223aac4 Fix NullReferenceException in GetPathProtocol when path is null 2025-11-10 02:30:49 -05:00
Mikal S.
3b2d64995a Resolve symlinks for static media source infos (#15263) 2025-11-09 09:45:02 -07:00
theguymadmax
13c4517a66 Fix collection grouping in mixed libraries (#15373) 2025-11-09 09:35:50 -07:00
theguymadmax
177b6464ca Don't clear baseitemids (#15446) 2025-11-09 09:22:09 -07:00
Bond-009
5a9a8363f4 Merge pull request #15441 from IceStormNG/fix-nullreference-role-null-10.11
Fix System.NullReferenceException when people's role is null (10.11.z)
2025-11-08 18:25:03 +01:00
theguymadmax
49efd68fc7 Invalidate parent folder's cache on deletion/creation (#15423) 2025-11-08 08:30:04 -07:00
Carsten Braun
90a8a26c6e Copy-Pasting is sometimes hard.... 2025-11-08 15:00:11 +01:00
Carsten Braun
002c83e6f5 Fix NullReferenceExceltop when role is null. 2025-11-08 14:32:14 +01:00
theguymadmax
7222910b05 Fix filters to use SortName (#15381) 2025-11-07 18:21:41 -07:00
Bond-009
097cb87f6f Don't enforce a minimum amount of free space for the tmp and log dirs (#15390) 2025-11-07 18:21:10 -07:00
JPVenson
91c3b1617e Fixed missing sort argument (#15413) 2025-11-07 18:20:42 -07:00
theguymadmax
8f71922734 Fix item count display for collapsed items (#15380) 2025-11-07 18:20:10 -07:00
Niels van Velzen
d140630208 Update branding in Swagger page (#15422) 2025-11-07 18:19:30 -07:00
theguymadmax
63a3e55297 Fix search terms using diacritics (#15435) 2025-11-07 18:18:24 -07:00
evanreichard
c2e5081d64 feat(sqlite): add timeout config (#15369) 2025-11-07 18:17:43 -07:00
Jellyfin Release Bot
4187c6f620 Bump version to 10.11.2 2025-11-02 21:28:56 -05:00
Tim Eisele
e7dbb3afec Skip too large extracted season numbers (#15326) 2025-11-02 09:11:48 -07:00
vinnyspb
f994dd6211 Update file size when refreshing metadata (#15325) 2025-11-01 14:18:19 -06:00
Cody Robibero
da254ee968 return instead of break, add check to more migrations (#15322) 2025-11-01 14:17:22 -06:00
Bill Thornton
4ad3141875 Update password reset to always return the same response structure (#15254) 2025-11-01 14:17:09 -06:00
evanreichard
b5f0199a25 fix: in optimistic locking, key off table is locked (#15328) 2025-11-01 14:15:26 -06:00
Nyanmisaka
6bf88c049e Ignore initial delay in audio-only containers (#15247) 2025-10-29 20:40:28 -06:00
Jellyfin Release Bot
40a33da2a5 Bump version to 10.11.1 2025-10-26 22:02:09 -04:00
Joshua M. Boniface
3596fc0693 Fix bump_version to handle spaced filename 2025-10-26 21:50:38 -04:00
Jellyfin Release Bot
93824dad97 Bump version to 10.11.1 2025-10-26 21:41:27 -04:00
Tim Eisele
e5656af1f2 Improve symlink handling (#15209) 2025-10-26 15:10:13 -06:00
Niels van Velzen
c127c10458 Merge pull request #15225 from Bond-009/z440ATL
Update dependency z440.atl.core to 7.6.0
2025-10-26 18:50:04 +01:00
Tim Eisele
7d1824ea27 Fix pagination and sorting for folders (#15187) 2025-10-26 11:34:11 -06:00
Cody Robibero
2966d27c97 Skip invalid database migration (#15212) 2025-10-26 11:34:04 -06:00
Ivan Kara
618ec4543e Add season number fallback for OMDB and TMDB plugins (#15113) 2025-10-26 11:33:55 -06:00
Cody Robibero
0e4031ae52 Skip extracting directory entry when restoring (#15196) 2025-10-26 11:33:47 -06:00
CeruleanRed
442af96ed9 Only save chapters that are within the runtime of the video file (#15176) 2025-10-26 10:37:16 -06:00
JJBlue
a305204cfa Skip extracted files in migration if bad timestamp or no access (#15220)
Fixes #15024
2025-10-26 10:30:43 -06:00
theguymadmax
75f472e6a7 Normalize paths in database queries (#15217) 2025-10-26 10:30:12 -06:00
Bond_009
cc32e8f7cb Update dependency z440.atl.core to 7.6.0 2025-10-26 15:16:08 +01:00
MBR-0001
14b3085ff1 Fix Has(Imdb/Tmdb/Tvdb)Id checks (#15126) 2025-10-25 16:00:55 -06:00
Cody Robibero
5691eee4f1 Prefer filting by package id instead of name (#15197) 2025-10-25 09:37:09 -06:00
theguymadmax
1520a697ad Play selected song first with instant mix (#15133) 2025-10-25 09:33:11 -06:00
Cody Robibero
81b8b0ca4a Add the transcode marker during startup instead of first transcode (#15194) 2025-10-25 09:32:15 -06:00
Cody Robibero
ac3fa3c376 Clean up backup service (#15170) 2025-10-24 17:57:34 -06:00
Tim Eisele
7a1c1cd342 Skip extracted files in migration if bad timestamp or no access (#15112) 2025-10-24 17:57:19 -06:00
gnattu
70c32a26fa Make priority class setting more robust (#15177) 2025-10-24 17:57:02 -06:00
Cody Robibero
2b94bb54aa Fix xml formatter (#15164) 2025-10-24 17:56:38 -06:00
Bond-009
0a6e8146be Lower required tmp dir size to 512MiB (#15098) 2025-10-23 16:38:27 -06:00
theguymadmax
305b0fdca3 Make season paths case-insensitive (#15102) 2025-10-23 16:38:06 -06:00
theguymadmax
d738386fe2 Fix LiveTV images not saving to database (#15083) 2025-10-23 16:37:55 -06:00
Tim Eisele
ca830d5be7 Speed-up trickplay migration (#15054) 2025-10-23 16:37:47 -06:00
theguymadmax
a5bc4524d8 Optimize artist query (#15087) 2025-10-23 16:37:29 -06:00
Nyanmisaka
175ee12bbc Fix videos with cropping metadata are probed as anamorphic (#15144) 2025-10-23 16:31:11 -06:00
Nyanmisaka
a725220c21 Reject stream copy of HDR10+ video if the client does not support HDR10 (#15072) 2025-10-21 17:20:56 -06:00
gnattu
a245605152 Log the message more clear when network manager is not ready (#15055) 2025-10-21 17:18:26 -06:00
Tim Eisele
f4a53209f4 Skip invalid keyframe cache data (#15032) 2025-10-21 17:17:56 -06:00
Jellyfin Release Bot
877251bcae Bump version to 10.11.0 2025-10-19 20:45:12 -04:00
662 changed files with 11039 additions and 44198 deletions

View File

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

View File

@@ -1,31 +1,17 @@
{ {
"name": "Development Jellyfin Server", "name": "Development Jellyfin Server",
"image": "mcr.microsoft.com/devcontainers/dotnet:10.0-noble", "image": "mcr.microsoft.com/devcontainers/dotnet:9.0-bookworm",
"service": "app", "service": "app",
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
// 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": "sudo dotnet restore; sudo dotnet workload update; sudo dotnet dev-certs https --trust; sudo bash \"./.devcontainer/install-ffmpeg.sh\"", "postStartCommand": "sudo dotnet restore; sudo dotnet workload update; sudo dotnet dev-certs https --trust; sudo bash \"./.devcontainer/install-ffmpeg.sh\"",
// The previous way of installing extensions via the vs command dont work on selfhosted devcontainers // reads the extensions list and installs them
"customizations": { "postAttachCommand": "cat .vscode/extensions.json | jq -r .recommendations[] | xargs -n 1 code --install-extension",
"vscode": {
"extensions": [
"ms-dotnettools.csharp",
"editorconfig.editorconfig",
"github.vscode-github-actions",
"ms-dotnettools.vscode-dotnet-runtime",
"ms-dotnettools.csdevkit",
"alexcvzz.vscode-sqlite",
"streetsidesoftware.code-spell-checker",
"eamodio.gitlens",
"redhat.vscode-xml"
]
}
},
"features": { "features": {
"ghcr.io/devcontainers/features/dotnet:2": { "ghcr.io/devcontainers/features/dotnet:2": {
"version": "none", "version": "none",
"dotnetRuntimeVersions": "10.0", "dotnetRuntimeVersions": "9.0",
"aspNetCoreRuntimeVersions": "10.0" "aspNetCoreRuntimeVersions": "9.0"
}, },
"ghcr.io/devcontainers-extra/features/apt-packages:1": { "ghcr.io/devcontainers-extra/features/apt-packages:1": {
"preserve_apt_list": false, "preserve_apt_list": false,

View File

@@ -379,9 +379,6 @@ dotnet_diagnostic.CA1720.severity = suggestion
# disable warning CA1724: Type names should not match namespaces # disable warning CA1724: Type names should not match namespaces
dotnet_diagnostic.CA1724.severity = suggestion dotnet_diagnostic.CA1724.severity = suggestion
# disable warning CA1873: Avoid potentially expensive logging
dotnet_diagnostic.CA1873.severity = suggestion
# disable warning CA1805: Do not initialize unnecessarily # disable warning CA1805: Do not initialize unnecessarily
dotnet_diagnostic.CA1805.severity = suggestion dotnet_diagnostic.CA1805.severity = suggestion
@@ -403,10 +400,6 @@ dotnet_diagnostic.CA1861.severity = suggestion
# disable warning CA2000: Dispose objects before losing scope # disable warning CA2000: Dispose objects before losing scope
dotnet_diagnostic.CA2000.severity = suggestion dotnet_diagnostic.CA2000.severity = suggestion
# TODO: Reevaluate when false positives are fixed: https://github.com/dotnet/roslyn-analyzers/issues/7699
# disable warning CA2025: Do not pass 'IDisposable' instances into unawaited tasks
dotnet_diagnostic.CA2025.severity = suggestion
# disable warning CA2253: Named placeholders should not be numeric values # disable warning CA2253: Named placeholders should not be numeric values
dotnet_diagnostic.CA2253.severity = suggestion dotnet_diagnostic.CA2253.severity = suggestion

15
.github/CODEOWNERS vendored
View File

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

View File

@@ -87,9 +87,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.11.8 - 10.10.0+
- 10.11.7
- 10.11.6
- Master - Master
- Unstable - Unstable
- Older* - Older*
@@ -138,14 +136,13 @@ body:
- **FFmpeg Version**: [e.g. 5.1.2-Jellyfin] - **FFmpeg Version**: [e.g. 5.1.2-Jellyfin]
- **Playback**: [Direct Play, Remux, Direct Stream, Transcode] - **Playback**: [Direct Play, Remux, Direct Stream, Transcode]
- **Hardware Acceleration**: [e.g. none, VAAPI, NVENC, etc.] - **Hardware Acceleration**: [e.g. none, VAAPI, NVENC, etc.]
- **CPU Model**: [e.g. AMD Ryzen 5 9600X, Intel Core i7-8565U, etc.]
- **GPU Model**: [e.g. none, UHD630, GTX1050, etc.] - **GPU Model**: [e.g. none, UHD630, GTX1050, etc.]
- **Installed Plugins**: [e.g. none, Fanart, Anime, etc.] - **Installed Plugins**: [e.g. none, Fanart, Anime, etc.]
- **Reverse Proxy**: [e.g. none, nginx, apache, etc.] - **Reverse Proxy**: [e.g. none, nginx, apache, etc.]
- **Base URL**: [e.g. none, yes: /example] - **Base URL**: [e.g. none, yes: /example]
- **Networking**: [e.g. Host, Bridge/NAT] - **Networking**: [e.g. Host, Bridge/NAT]
- **Jellyfin Data Storage & Filesystem**: [e.g. local SATA SSD - ext4, local HDD - NTFS] - **Jellyfin Data Storage**: [e.g. local SATA SSD, local HDD]
- **Media Storage & Filesystem**: [e.g. Local HDD - ext4, SMB Share] - **Media Storage**: [e.g. Local HDD, SMB Share]
- **External Integrations**: [e.g. Jellystat, Jellyseerr] - **External Integrations**: [e.g. Jellystat, Jellyseerr]
value: | value: |
- OS: - OS:
@@ -156,14 +153,13 @@ body:
- FFmpeg Version: - FFmpeg Version:
- Playback Method: - Playback Method:
- Hardware Acceleration: - Hardware Acceleration:
- CPU Model:
- GPU Model: - GPU Model:
- Plugins: - Plugins:
- Reverse Proxy: - Reverse Proxy:
- Base URL: - Base URL:
- Networking: - Networking:
- Jellyfin Data Storage & Filesystem: - Jellyfin Data Storage:
- Media Storage & Filesystem: - Media Storage:
- External Integrations: - External Integrations:
render: markdown render: markdown
validations: validations:

View File

@@ -8,10 +8,6 @@ on:
schedule: schedule:
- cron: '24 2 * * 4' - cron: '24 2 * * 4'
permissions:
contents: read
security-events: write
jobs: jobs:
analyze: analyze:
name: Analyze name: Analyze
@@ -24,21 +20,18 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
with: with:
dotnet-version: '10.0.x' dotnet-version: '9.0.x'
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 uses: github/codeql-action/init@16140ae1a102900babc80a33c44059580f687047 # v4.30.9
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
queries: +security-extended queries: +security-extended
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 uses: github/codeql-action/autobuild@16140ae1a102900babc80a33c44059580f687047 # v4.30.9
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 uses: github/codeql-action/analyze@16140ae1a102900babc80a33c44059580f687047 # v4.30.9

View File

@@ -11,22 +11,22 @@ jobs:
permissions: read-all permissions: read-all
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with: with:
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
repository: ${{ github.event.pull_request.head.repo.full_name }} repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
with: with:
dotnet-version: '10.0.x' 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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with: with:
name: abi-head name: abi-head
retention-days: 14 retention-days: 14
@@ -40,16 +40,16 @@ jobs:
permissions: read-all permissions: read-all
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with: with:
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
repository: ${{ github.event.pull_request.head.repo.full_name }} repository: ${{ github.event.pull_request.head.repo.full_name }}
fetch-depth: 0 fetch-depth: 0
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
with: with:
dotnet-version: '10.0.x' dotnet-version: '9.0.x'
- name: Checkout common ancestor - name: Checkout common ancestor
env: env:
@@ -65,7 +65,7 @@ jobs:
dotnet build Jellyfin.Server -o ./out dotnet build Jellyfin.Server -o ./out
- name: Upload Head - name: Upload Head
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with: with:
name: abi-base name: abi-base
retention-days: 14 retention-days: 14
@@ -85,13 +85,13 @@ jobs:
steps: steps:
- name: Download abi-head - name: Download abi-head
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with: with:
name: abi-head name: abi-head
path: abi-head path: abi-head
- name: Download abi-base - name: Download abi-base
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with: with:
name: abi-base name: abi-base
path: abi-base path: abi-base
@@ -106,7 +106,7 @@ jobs:
{ {
echo 'body<<EOF' echo 'body<<EOF'
for file in Jellyfin.Data.dll MediaBrowser.Common.dll MediaBrowser.Controller.dll MediaBrowser.Model.dll Emby.Naming.dll Jellyfin.Extensions.dll Jellyfin.MediaEncoding.Keyframes.dll Jellyfin.Database.Implementations.dll; do for file in Jellyfin.Data.dll MediaBrowser.Common.dll MediaBrowser.Controller.dll MediaBrowser.Model.dll Emby.Naming.dll Jellyfin.Extensions.dll Jellyfin.MediaEncoding.Keyframes.dll Jellyfin.Database.Implementations.dll; do
COMPAT_OUTPUT="$( { apicompat --left ./abi-base/${file} --right ./abi-head/${file}; } 2>&1 || true )" COMPAT_OUTPUT="$( { apicompat --left ./abi-base/${file} --right ./abi-head/${file}; } 2>&1 )"
if [ "APICompat ran successfully without finding any breaking changes." != "${COMPAT_OUTPUT}" ]; then if [ "APICompat ran successfully without finding any breaking changes." != "${COMPAT_OUTPUT}" ]; then
printf "\n${file}\n${COMPAT_OUTPUT}\n" printf "\n${file}\n${COMPAT_OUTPUT}\n"
fi fi

271
.github/workflows/ci-openapi.yml vendored Normal file
View File

@@ -0,0 +1,271 @@
name: OpenAPI
on:
push:
branches:
- master
tags:
- 'v*'
pull_request:
permissions: {}
jobs:
openapi-head:
name: OpenAPI - HEAD
runs-on: ubuntu-latest
permissions: read-all
steps:
- name: Checkout repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ github.event.pull_request.head.sha }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Setup .NET
uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
with:
dotnet-version: '9.0.x'
- 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"
- name: Upload openapi.json
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: openapi-head
retention-days: 14
if-no-files-found: error
path: tests/Jellyfin.Server.Integration.Tests/bin/Release/net9.0/openapi.json
openapi-base:
name: OpenAPI - BASE
if: ${{ github.base_ref != '' }}
runs-on: ubuntu-latest
permissions: read-all
steps:
- name: Checkout repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ github.event.pull_request.head.sha }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
fetch-depth: 0
- name: Checkout common ancestor
env:
HEAD_REF: ${{ github.head_ref }}
run: |
git remote add upstream https://github.com/${{ github.event.pull_request.base.repo.full_name }}
git -c protocol.version=2 fetch --prune --progress --no-recurse-submodules upstream +refs/heads/*:refs/remotes/upstream/* +refs/tags/*:refs/tags/*
ANCESTOR_REF=$(git merge-base upstream/${{ github.base_ref }} origin/$HEAD_REF)
git checkout --progress --force $ANCESTOR_REF
- name: Setup .NET
uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
with:
dotnet-version: '9.0.x'
- 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"
- name: Upload openapi.json
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: openapi-base
retention-days: 14
if-no-files-found: error
path: tests/Jellyfin.Server.Integration.Tests/bin/Release/net9.0/openapi.json
openapi-diff:
permissions:
pull-requests: write # to create or update comment (peter-evans/create-or-update-comment)
name: OpenAPI - Difference
if: ${{ github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
needs:
- openapi-head
- openapi-base
steps:
- name: Download openapi-head
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
name: openapi-head
path: openapi-head
- name: Download openapi-base
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
name: openapi-base
path: openapi-base
- name: Workaround openapi-diff issue
run: |
sed -i 's/"allOf"/"oneOf"/g' openapi-head/openapi.json
sed -i 's/"allOf"/"oneOf"/g' openapi-base/openapi.json
- name: Calculate OpenAPI difference
uses: docker://openapitools/openapi-diff
continue-on-error: true
with:
args: --fail-on-changed --markdown openapi-changes.md openapi-base/openapi.json openapi-head/openapi.json
- id: read-diff
name: Read openapi-diff output
run: |
# Read and fix markdown
body=$(cat openapi-changes.md)
# Write to workflow summary
echo "$body" >> $GITHUB_STEP_SUMMARY
# Set ApiChanged var
if [ "$body" != '' ]; then
echo "ApiChanged=1" >> "$GITHUB_OUTPUT"
else
echo "ApiChanged=0" >> "$GITHUB_OUTPUT"
fi
# Add header/footer for diff comment
echo '<!--openapi-diff-workflow-comment-->' > openapi-changes-reply.md
echo "<details>" >> openapi-changes-reply.md
echo "<summary>Changes in OpenAPI specification found. Expand to see details.</summary>" >> openapi-changes-reply.md
echo "" >> openapi-changes-reply.md
echo "$body" >> openapi-changes-reply.md
echo "" >> openapi-changes-reply.md
echo "</details>" >> openapi-changes-reply.md
- name: Find difference comment
uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4.0.0
id: find-comment
with:
issue-number: ${{ github.event.pull_request.number }}
direction: last
body-includes: openapi-diff-workflow-comment
- name: Reply or edit difference comment (changed)
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0
if: ${{ steps.read-diff.outputs.ApiChanged == '1' }}
with:
issue-number: ${{ github.event.pull_request.number }}
comment-id: ${{ steps.find-comment.outputs.comment-id }}
edit-mode: replace
body-path: openapi-changes-reply.md
- name: Edit difference comment (unchanged)
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0
if: ${{ steps.read-diff.outputs.ApiChanged == '0' && steps.find-comment.outputs.comment-id != '' }}
with:
issue-number: ${{ github.event.pull_request.number }}
comment-id: ${{ steps.find-comment.outputs.comment-id }}
edit-mode: replace
body: |
<!--openapi-diff-workflow-comment-->
No changes to OpenAPI specification found. See history of this comment for previous changes.
publish-unstable:
name: OpenAPI - Publish Unstable Spec
if: ${{ github.event_name != 'pull_request' && !startsWith(github.ref, 'refs/tags/v') && contains(github.repository_owner, 'jellyfin') }}
runs-on: ubuntu-latest
needs:
- openapi-head
steps:
- name: Set unstable dated version
id: version
run: |-
echo "JELLYFIN_VERSION=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_ENV
- name: Download openapi-head
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
name: openapi-head
path: openapi-head
- name: Upload openapi.json (unstable) to repository server
uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
with:
host: "${{ secrets.REPO_HOST }}"
username: "${{ secrets.REPO_USER }}"
key: "${{ secrets.REPO_KEY }}"
source: openapi-head/openapi.json
strip_components: 1
target: "/srv/incoming/openapi/unstable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}"
- name: Move openapi.json (unstable) into place
uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
with:
host: "${{ secrets.REPO_HOST }}"
username: "${{ secrets.REPO_USER }}"
key: "${{ secrets.REPO_KEY }}"
debug: false
script_stop: false
script: |
if ! test -d /run/workflows; then
sudo mkdir -p /run/workflows
sudo chown ${{ secrets.REPO_USER }} /run/workflows
fi
(
flock -x -w 300 200 || exit 1
TGT_DIR="/srv/repository/main/openapi"
LAST_SPEC="$( ls -lt ${TGT_DIR}/unstable/ | grep 'jellyfin-openapi' | head -1 | awk '{ print $NF }' )"
# If new and previous spec don't differ (diff retcode 0), remove incoming and finish
if diff /srv/incoming/openapi/unstable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}/openapi.json ${TGT_DIR}/unstable/${LAST_SPEC} &>/dev/null; then
rm -r /srv/incoming/openapi/unstable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}
exit 0
fi
# Move new spec into place
sudo mv /srv/incoming/openapi/unstable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}/openapi.json ${TGT_DIR}/unstable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}.json
# Delete previous jellyfin-openapi-unstable_previous.json
sudo rm ${TGT_DIR}/jellyfin-openapi-unstable_previous.json
# Move current jellyfin-openapi-unstable.json symlink to jellyfin-openapi-unstable_previous.json
sudo mv ${TGT_DIR}/jellyfin-openapi-unstable.json ${TGT_DIR}/jellyfin-openapi-unstable_previous.json
# Create new jellyfin-openapi-unstable.json symlink
sudo ln -s unstable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}.json ${TGT_DIR}/jellyfin-openapi-unstable.json
# Check that the previous openapi unstable spec link is correct
if [[ "$( readlink ${TGT_DIR}/jellyfin-openapi-unstable_previous.json )" != "unstable/${LAST_SPEC}" ]]; then
sudo rm ${TGT_DIR}/jellyfin-openapi-unstable_previous.json
sudo ln -s unstable/${LAST_SPEC} ${TGT_DIR}/jellyfin-openapi-unstable_previous.json
fi
) 200>/run/workflows/openapi-unstable.lock
publish-stable:
name: OpenAPI - Publish Stable Spec
if: ${{ startsWith(github.ref, 'refs/tags/v') && contains(github.repository_owner, 'jellyfin') }}
runs-on: ubuntu-latest
needs:
- openapi-head
steps:
- name: Set version number
id: version
run: |-
echo "JELLYFIN_VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
- name: Download openapi-head
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
name: openapi-head
path: openapi-head
- name: Upload openapi.json (stable) to repository server
uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
with:
host: "${{ secrets.REPO_HOST }}"
username: "${{ secrets.REPO_USER }}"
key: "${{ secrets.REPO_KEY }}"
source: openapi-head/openapi.json
strip_components: 1
target: "/srv/incoming/openapi/stable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}"
- name: Move openapi.json (stable) into place
uses: appleboy/ssh-action@2ead5e36573f08b82fbfce1504f1a4b05a647c6f # v1.2.2
with:
host: "${{ secrets.REPO_HOST }}"
username: "${{ secrets.REPO_USER }}"
key: "${{ secrets.REPO_KEY }}"
debug: false
script_stop: false
script: |
if ! test -d /run/workflows; then
sudo mkdir -p /run/workflows
sudo chown ${{ secrets.REPO_USER }} /run/workflows
fi
(
flock -x -w 300 200 || exit 1
TGT_DIR="/srv/repository/main/openapi"
LAST_SPEC="$( ls -lt ${TGT_DIR}/stable/ | grep 'jellyfin-openapi' | head -1 | awk '{ print $NF }' )"
# If new and previous spec don't differ (diff retcode 0), remove incoming and finish
if diff /srv/incoming/openapi/stable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}/openapi.json ${TGT_DIR}/stable/${LAST_SPEC} &>/dev/null; then
rm -r /srv/incoming/openapi/stable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}
exit 0
fi
# Move new spec into place
sudo mv /srv/incoming/openapi/stable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}/openapi.json ${TGT_DIR}/stable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}.json
# Delete previous jellyfin-openapi-stable_previous.json
sudo rm ${TGT_DIR}/jellyfin-openapi-stable_previous.json
# Move current jellyfin-openapi-stable.json symlink to jellyfin-openapi-stable_previous.json
sudo mv ${TGT_DIR}/jellyfin-openapi-stable.json ${TGT_DIR}/jellyfin-openapi-stable_previous.json
# Create new jellyfin-openapi-stable.json symlink
sudo ln -s stable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}.json ${TGT_DIR}/jellyfin-openapi-stable.json
# Check that the previous openapi stable spec link is correct
if [[ "$( readlink ${TGT_DIR}/jellyfin-openapi-stable_previous.json )" != "stable/${LAST_SPEC}" ]]; then
sudo rm ${TGT_DIR}/jellyfin-openapi-stable_previous.json
sudo ln -s stable/${LAST_SPEC} ${TGT_DIR}/jellyfin-openapi-stable_previous.json
fi
) 200>/run/workflows/openapi-stable.lock

View File

@@ -9,7 +9,7 @@ on:
pull_request: pull_request:
env: env:
SDK_VERSION: "10.0.x" SDK_VERSION: "9.0.x"
jobs: jobs:
run-tests: run-tests:
@@ -20,9 +20,9 @@ jobs:
runs-on: "${{ matrix.os }}" runs-on: "${{ matrix.os }}"
steps: steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 - uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
with: with:
dotnet-version: ${{ env.SDK_VERSION }} dotnet-version: ${{ env.SDK_VERSION }}
@@ -35,7 +35,7 @@ jobs:
--verbosity minimal --verbosity minimal
- name: Merge code coverage results - name: Merge code coverage results
uses: danielpalme/ReportGenerator-GitHub-Action@049f7ec958c672fd31d5cc1cb01622dc8d2e23ab # v5.5.10 uses: danielpalme/ReportGenerator-GitHub-Action@9870ed167742d546b99962ff815fcc1098355ed8 # v5.4.17
with: with:
reports: "**/coverage.cobertura.xml" reports: "**/coverage.cobertura.xml"
targetdir: "merged/" targetdir: "merged/"

View File

@@ -24,7 +24,7 @@ jobs:
reactions: '+1' reactions: '+1'
- name: Checkout the latest code - name: Checkout the latest code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with: with:
token: ${{ secrets.JF_BOT_TOKEN }} token: ${{ secrets.JF_BOT_TOKEN }}
fetch-depth: 0 fetch-depth: 0
@@ -36,23 +36,20 @@ jobs:
rename: rename:
name: Rename name: Rename
if: contains(github.event.comment.body, '@jellyfin-bot rename') if: contains(github.event.comment.body, '@jellyfin-bot rename') && github.event.comment.author_association == 'MEMBER'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: pull in script - name: pull in script
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with: with:
repository: jellyfin/jellyfin-triage-script repository: jellyfin/jellyfin-triage-script
- name: install python - name: install python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with: with:
python-version: '3.14' python-version: '3.14'
cache: 'pip' cache: 'pip'
- name: install python packages - name: install python packages
run: pip install -r rename/requirements.txt run: pip install -r rename/requirements.txt
- name: run rename script - name: run rename script
run: python3 rename.py run: python3 rename.py
working-directory: ./rename working-directory: ./rename

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@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 - uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
with: with:
repo-token: ${{ secrets.JF_BOT_TOKEN }} repo-token: ${{ secrets.JF_BOT_TOKEN }}
ascending: true ascending: true

View File

@@ -10,19 +10,16 @@ jobs:
issues: write issues: write
steps: steps:
- name: pull in script - name: pull in script
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with: with:
repository: jellyfin/jellyfin-triage-script repository: jellyfin/jellyfin-triage-script
- name: install python - name: install python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with: with:
python-version: '3.14' python-version: '3.14'
cache: 'pip' cache: 'pip'
- name: install python packages - name: install python packages
run: pip install -r main-repo-triage/requirements.txt run: pip install -r main-repo-triage/requirements.txt
- name: check and comment issue - name: check and comment issue
working-directory: ./main-repo-triage working-directory: ./main-repo-triage
run: python3 single_issue_gha.py run: python3 single_issue_gha.py

View File

@@ -1,44 +0,0 @@
name: OpenAPI Generate
on:
workflow_call:
inputs:
ref:
required: true
type: string
repository:
required: true
type: string
artifact:
required: true
type: string
permissions:
contents: read
jobs:
main:
name: Main
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ inputs.ref }}
repository: ${{ inputs.repository }}
- name: Configure .NET
uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0
with:
dotnet-version: '10.0.x'
- name: Create File
run: dotnet test tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj -c Release --filter Jellyfin.Server.Integration.Tests.OpenApiSpecTests
- name: Upload Artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ inputs.artifact }}
path: tests/Jellyfin.Server.Integration.Tests/bin/Release/net10.0/openapi.json
retention-days: 14
if-no-files-found: error

View File

@@ -1,140 +0,0 @@
name: OpenAPI Publish
on:
push:
branches:
- master
tags:
- 'v*'
jobs:
publish-openapi:
name: OpenAPI - Publish Artifact
uses: ./.github/workflows/openapi-generate.yml
permissions:
contents: read
with:
ref: ${{ github.sha }}
repository: ${{ github.repository }}
artifact: openapi-head
publish-unstable:
name: OpenAPI - Publish Unstable Spec
if: ${{ github.event_name != 'pull_request' && !startsWith(github.ref, 'refs/tags/v') && contains(github.repository_owner, 'jellyfin') }}
runs-on: ubuntu-latest
needs:
- publish-openapi
steps:
- name: Set unstable dated version
id: version
run: |-
echo "JELLYFIN_VERSION=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_ENV
- name: Download openapi-head
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: openapi-head
path: openapi-head
- name: Upload openapi.json (unstable) to repository server
uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
with:
host: "${{ secrets.REPO_HOST }}"
username: "${{ secrets.REPO_USER }}"
key: "${{ secrets.REPO_KEY }}"
source: openapi-head/openapi.json
strip_components: 1
target: "/srv/incoming/openapi/unstable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}"
- name: Move openapi.json (unstable) into place
uses: appleboy/ssh-action@0ff4204d59e8e51228ff73bce53f80d53301dee2 # v1.2.5
with:
host: "${{ secrets.REPO_HOST }}"
username: "${{ secrets.REPO_USER }}"
key: "${{ secrets.REPO_KEY }}"
debug: false
script: |
if ! test -d /run/workflows; then
sudo mkdir -p /run/workflows
sudo chown ${{ secrets.REPO_USER }} /run/workflows
fi
(
flock -x -w 300 200 || exit 1
TGT_DIR="/srv/repository/main/openapi"
LAST_SPEC="$( ls -lt ${TGT_DIR}/unstable/ | grep 'jellyfin-openapi' | head -1 | awk '{ print $NF }' )"
# If new and previous spec don't differ (diff retcode 0), remove incoming and finish
if diff /srv/incoming/openapi/unstable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}/openapi.json ${TGT_DIR}/unstable/${LAST_SPEC} &>/dev/null; then
rm -r /srv/incoming/openapi/unstable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}
exit 0
fi
# Move new spec into place
sudo mv /srv/incoming/openapi/unstable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}/openapi.json ${TGT_DIR}/unstable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}.json
# Delete previous jellyfin-openapi-unstable_previous.json
sudo rm ${TGT_DIR}/jellyfin-openapi-unstable_previous.json
# Move current jellyfin-openapi-unstable.json symlink to jellyfin-openapi-unstable_previous.json
sudo mv ${TGT_DIR}/jellyfin-openapi-unstable.json ${TGT_DIR}/jellyfin-openapi-unstable_previous.json
# Create new jellyfin-openapi-unstable.json symlink
sudo ln -s unstable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}.json ${TGT_DIR}/jellyfin-openapi-unstable.json
# Check that the previous openapi unstable spec link is correct
if [[ "$( readlink ${TGT_DIR}/jellyfin-openapi-unstable_previous.json )" != "unstable/${LAST_SPEC}" ]]; then
sudo rm ${TGT_DIR}/jellyfin-openapi-unstable_previous.json
sudo ln -s unstable/${LAST_SPEC} ${TGT_DIR}/jellyfin-openapi-unstable_previous.json
fi
) 200>/run/workflows/openapi-unstable.lock
publish-stable:
name: OpenAPI - Publish Stable Spec
if: ${{ startsWith(github.ref, 'refs/tags/v') && contains(github.repository_owner, 'jellyfin') }}
runs-on: ubuntu-latest
needs:
- publish-openapi
steps:
- name: Set version number
id: version
run: |-
echo "JELLYFIN_VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
- name: Download openapi-head
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: openapi-head
path: openapi-head
- name: Upload openapi.json (stable) to repository server
uses: appleboy/scp-action@ff85246acaad7bdce478db94a363cd2bf7c90345 # v1.0.0
with:
host: "${{ secrets.REPO_HOST }}"
username: "${{ secrets.REPO_USER }}"
key: "${{ secrets.REPO_KEY }}"
source: openapi-head/openapi.json
strip_components: 1
target: "/srv/incoming/openapi/stable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}"
- name: Move openapi.json (stable) into place
uses: appleboy/ssh-action@0ff4204d59e8e51228ff73bce53f80d53301dee2 # v1.2.5
with:
host: "${{ secrets.REPO_HOST }}"
username: "${{ secrets.REPO_USER }}"
key: "${{ secrets.REPO_KEY }}"
debug: false
script: |
if ! test -d /run/workflows; then
sudo mkdir -p /run/workflows
sudo chown ${{ secrets.REPO_USER }} /run/workflows
fi
(
flock -x -w 300 200 || exit 1
TGT_DIR="/srv/repository/main/openapi"
LAST_SPEC="$( ls -lt ${TGT_DIR}/stable/ | grep 'jellyfin-openapi' | head -1 | awk '{ print $NF }' )"
# If new and previous spec don't differ (diff retcode 0), remove incoming and finish
if diff /srv/incoming/openapi/stable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}/openapi.json ${TGT_DIR}/stable/${LAST_SPEC} &>/dev/null; then
rm -r /srv/incoming/openapi/stable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}
exit 0
fi
# Move new spec into place
sudo mv /srv/incoming/openapi/stable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}/openapi.json ${TGT_DIR}/stable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}.json
# Delete previous jellyfin-openapi-stable_previous.json
sudo rm ${TGT_DIR}/jellyfin-openapi-stable_previous.json
# Move current jellyfin-openapi-stable.json symlink to jellyfin-openapi-stable_previous.json
sudo mv ${TGT_DIR}/jellyfin-openapi-stable.json ${TGT_DIR}/jellyfin-openapi-stable_previous.json
# Create new jellyfin-openapi-stable.json symlink
sudo ln -s stable/jellyfin-openapi-${{ env.JELLYFIN_VERSION }}.json ${TGT_DIR}/jellyfin-openapi-stable.json
# Check that the previous openapi stable spec link is correct
if [[ "$( readlink ${TGT_DIR}/jellyfin-openapi-stable_previous.json )" != "stable/${LAST_SPEC}" ]]; then
sudo rm ${TGT_DIR}/jellyfin-openapi-stable_previous.json
sudo ln -s stable/${LAST_SPEC} ${TGT_DIR}/jellyfin-openapi-stable_previous.json
fi
) 200>/run/workflows/openapi-stable.lock

View File

@@ -1,80 +0,0 @@
name: OpenAPI Check
on:
pull_request:
jobs:
ancestor:
name: Common Ancestor
runs-on: ubuntu-latest
outputs:
base_ref: ${{ steps.ancestor.outputs.base_ref }}
steps:
- name: Checkout Repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.event.pull_request.head.sha }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
fetch-depth: 0
- name: Search History
id: ancestor
run: |
git remote add upstream https://github.com/${{ github.event.pull_request.base.repo.full_name }}
git fetch --prune --progress --no-recurse-submodules upstream +refs/heads/*:refs/remotes/upstream/* +refs/tags/*:refs/tags/*
ANCESTOR_REF=$(git merge-base upstream/${{ github.base_ref }} HEAD)
echo "ref: ${ANCESTOR_REF}"
echo "base_ref=${ANCESTOR_REF}" >> "$GITHUB_OUTPUT"
head:
name: Head Artifact
uses: ./.github/workflows/openapi-generate.yml
with:
ref: ${{ github.event.pull_request.head.sha }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
artifact: openapi-head
base:
name: Base Artifact
uses: ./.github/workflows/openapi-generate.yml
needs:
- ancestor
with:
ref: ${{ needs.ancestor.outputs.base_ref }}
repository: ${{ github.event.pull_request.base.repo.full_name }}
artifact: openapi-base
diff:
name: Generate Report
runs-on: ubuntu-latest
needs:
- head
- base
steps:
- name: Download Head
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: openapi-head
path: openapi-head
- name: Download Base
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: openapi-base
path: openapi-base
- name: Detect Changes
id: openapi-diff
run: |
sed -i 's:allOf:oneOf:g' openapi-head/openapi.json
sed -i 's:allOf:oneOf:g' openapi-base/openapi.json
mkdir -p /tmp/openapi-report
mv openapi-head/openapi.json /tmp/openapi-report/head.json
mv openapi-base/openapi.json /tmp/openapi-report/base.json
docker run -v /tmp/openapi-report:/data openapitools/openapi-diff:2.1.6 /data/base.json /data/head.json --state -l ERROR --markdown /data/openapi-report.md
- name: Upload Artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: openapi-report
path: /tmp/openapi-report/openapi-report.md

View File

@@ -1,59 +0,0 @@
name: OpenAPI Report
on:
workflow_run:
workflows:
- OpenAPI Check
types:
- completed
jobs:
metadata:
name: Generate Metadata
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
outputs:
pr_number: ${{ steps.pr_number.outputs.pr_number }}
steps:
- name: Get Pull Request Number
id: pr_number
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
run: |
API_RESPONSE=$(gh pr list --repo "${GITHUB_REPOSITORY}" --search "${HEAD_SHA}" --state open --json number)
PR_NUMBER=$(echo "${API_RESPONSE}" | jq '.[0].number')
echo "repository: ${GITHUB_REPOSITORY}"
echo "sha: ${HEAD_SHA}"
echo "response: ${API_RESPONSE}"
echo "pr: ${PR_NUMBER}"
echo "pr_number=${PR_NUMBER}" >> "${GITHUB_OUTPUT}"
comment:
name: Pull Request Comment
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
needs:
- metadata
permissions:
pull-requests: write
actions: read
contents: read
steps:
- name: Download OpenAPI Report
id: download_report
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: openapi-report
path: openapi-report
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Push Comment
uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b # v3.0.1
with:
github-token: ${{ secrets.JF_BOT_TOKEN }}
file-path: ${{ steps.download_report.outputs.download-path }}/openapi-report.md
pr-number: ${{ needs.metadata.outputs.pr_number }}
comment-tag: openapi-report

View File

@@ -21,7 +21,6 @@ jobs:
with: with:
project: Current Release project: Current Release
action: delete action: delete
column: In progress
repo-token: ${{ secrets.JF_BOT_TOKEN }} repo-token: ${{ secrets.JF_BOT_TOKEN }}
- name: Add to 'Release Next' project - name: Add to 'Release Next' project

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@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 - uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
with: with:
repo-token: ${{ secrets.JF_BOT_TOKEN }} repo-token: ${{ secrets.JF_BOT_TOKEN }}
ascending: true ascending: true

View File

@@ -28,12 +28,12 @@ jobs:
timeoutSeconds: 3600 timeoutSeconds: 3600
- name: Setup YQ - name: Setup YQ
uses: chrisdickinson/setup-yq@fa3192edd79d6eb0e4e12de8dde3a0c26f2b853b # latest uses: chrisdickinson/setup-yq@latest
with: with:
yq-version: v4.9.8 yq-version: v4.9.8
- name: Checkout Repository - name: Checkout Repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with: with:
ref: ${{ env.TAG_BRANCH }} ref: ${{ env.TAG_BRANCH }}
@@ -66,7 +66,7 @@ jobs:
NEXT_VERSION: ${{ github.event.inputs.NEXT_VERSION }} NEXT_VERSION: ${{ github.event.inputs.NEXT_VERSION }}
steps: steps:
- name: Checkout Repository - name: Checkout Repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with: with:
ref: ${{ env.TAG_BRANCH }} ref: ${{ env.TAG_BRANCH }}

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/net10.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/net10.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/net10.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

@@ -1,6 +1,5 @@
# Jellyfin Contributors # Jellyfin Contributors
- [0x25CBFC4F](https://github.com/0x25CBFC4F)
- [1337joe](https://github.com/1337joe) - [1337joe](https://github.com/1337joe)
- [97carmine](https://github.com/97carmine) - [97carmine](https://github.com/97carmine)
- [Abbe98](https://github.com/Abbe98) - [Abbe98](https://github.com/Abbe98)
@@ -15,7 +14,7 @@
- [bilde2910](https://github.com/bilde2910) - [bilde2910](https://github.com/bilde2910)
- [bfayers](https://github.com/bfayers) - [bfayers](https://github.com/bfayers)
- [BnMcG](https://github.com/BnMcG) - [BnMcG](https://github.com/BnMcG)
- [Bond_009](https://github.com/Bond-009) - [Bond-009](https://github.com/Bond-009)
- [brianjmurrell](https://github.com/brianjmurrell) - [brianjmurrell](https://github.com/brianjmurrell)
- [bugfixin](https://github.com/bugfixin) - [bugfixin](https://github.com/bugfixin)
- [chaosinnovator](https://github.com/chaosinnovator) - [chaosinnovator](https://github.com/chaosinnovator)
@@ -32,7 +31,6 @@
- [DaveChild](https://github.com/DaveChild) - [DaveChild](https://github.com/DaveChild)
- [DavidFair](https://github.com/DavidFair) - [DavidFair](https://github.com/DavidFair)
- [Delgan](https://github.com/Delgan) - [Delgan](https://github.com/Delgan)
- [DerMaddis](https://github.com/dermaddis)
- [Derpipose](https://github.com/Derpipose) - [Derpipose](https://github.com/Derpipose)
- [dcrdev](https://github.com/dcrdev) - [dcrdev](https://github.com/dcrdev)
- [dhartung](https://github.com/dhartung) - [dhartung](https://github.com/dhartung)
@@ -56,7 +54,6 @@
- [geilername](https://github.com/geilername) - [geilername](https://github.com/geilername)
- [GermanCoding](https://github.com/GermanCoding) - [GermanCoding](https://github.com/GermanCoding)
- [gnattu](https://github.com/gnattu) - [gnattu](https://github.com/gnattu)
- [gnuyent](https://github.com/gnuyent)
- [GodTamIt](https://github.com/GodTamIt) - [GodTamIt](https://github.com/GodTamIt)
- [grafixeyehero](https://github.com/grafixeyehero) - [grafixeyehero](https://github.com/grafixeyehero)
- [h1nk](https://github.com/h1nk) - [h1nk](https://github.com/h1nk)
@@ -64,7 +61,6 @@
- [HelloWorld017](https://github.com/HelloWorld017) - [HelloWorld017](https://github.com/HelloWorld017)
- [ikomhoog](https://github.com/ikomhoog) - [ikomhoog](https://github.com/ikomhoog)
- [iwalton3](https://github.com/iwalton3) - [iwalton3](https://github.com/iwalton3)
- [Jakob Kukla](https://github.com/jakobkukla)
- [jftuga](https://github.com/jftuga) - [jftuga](https://github.com/jftuga)
- [jkhsjdhjs](https://github.com/jkhsjdhjs) - [jkhsjdhjs](https://github.com/jkhsjdhjs)
- [jmshrv](https://github.com/jmshrv) - [jmshrv](https://github.com/jmshrv)
@@ -73,10 +69,8 @@
- [JustAMan](https://github.com/JustAMan) - [JustAMan](https://github.com/JustAMan)
- [justinfenn](https://github.com/justinfenn) - [justinfenn](https://github.com/justinfenn)
- [JPVenson](https://github.com/JPVenson) - [JPVenson](https://github.com/JPVenson)
- [JPUC1143](https://github.com/Jpuc1143/)
- [KerryRJ](https://github.com/KerryRJ) - [KerryRJ](https://github.com/KerryRJ)
- [Larvitar](https://github.com/Larvitar) - [Larvitar](https://github.com/Larvitar)
- [lbenini](https://github.com/lbenini)
- [LeoVerto](https://github.com/LeoVerto) - [LeoVerto](https://github.com/LeoVerto)
- [Liggy](https://github.com/Liggy) - [Liggy](https://github.com/Liggy)
- [lmaonator](https://github.com/lmaonator) - [lmaonator](https://github.com/lmaonator)
@@ -89,19 +83,15 @@
- [marius-luca-87](https://github.com/marius-luca-87) - [marius-luca-87](https://github.com/marius-luca-87)
- [mark-monteiro](https://github.com/mark-monteiro) - [mark-monteiro](https://github.com/mark-monteiro)
- [MarkCiliaVincenti](https://github.com/MarkCiliaVincenti) - [MarkCiliaVincenti](https://github.com/MarkCiliaVincenti)
- [Martin Reuter](https://github.com/reuterma24)
- [Matt07211](https://github.com/Matt07211) - [Matt07211](https://github.com/Matt07211)
- [Matthew Jones](https://github.com/matthew-jones-uk)
- [Maxr1998](https://github.com/Maxr1998) - [Maxr1998](https://github.com/Maxr1998)
- [mcarlton00](https://github.com/mcarlton00) - [mcarlton00](https://github.com/mcarlton00)
- [Michael McElroy](https://github.com/mcmcelro)
- [mitchfizz05](https://github.com/mitchfizz05) - [mitchfizz05](https://github.com/mitchfizz05)
- [mohd-akram](https://github.com/mohd-akram) - [mohd-akram](https://github.com/mohd-akram)
- [MrTimscampi](https://github.com/MrTimscampi) - [MrTimscampi](https://github.com/MrTimscampi)
- [n8225](https://github.com/n8225) - [n8225](https://github.com/n8225)
- [Nalsai](https://github.com/Nalsai) - [Nalsai](https://github.com/Nalsai)
- [Narfinger](https://github.com/Narfinger) - [Narfinger](https://github.com/Narfinger)
- [Nathan McCrina](https://github.com/nfmccrina)
- [NathanPickard](https://github.com/NathanPickard) - [NathanPickard](https://github.com/NathanPickard)
- [neilsb](https://github.com/neilsb) - [neilsb](https://github.com/neilsb)
- [nevado](https://github.com/nevado) - [nevado](https://github.com/nevado)
@@ -112,25 +102,21 @@
- [OancaAndrei](https://github.com/OancaAndrei) - [OancaAndrei](https://github.com/OancaAndrei)
- [obradovichv](https://github.com/obradovichv) - [obradovichv](https://github.com/obradovichv)
- [oddstr13](https://github.com/oddstr13) - [oddstr13](https://github.com/oddstr13)
- [olsh](https://github.com/olsh)
- [orryverducci](https://github.com/orryverducci) - [orryverducci](https://github.com/orryverducci)
- [petermcneil](https://github.com/petermcneil) - [petermcneil](https://github.com/petermcneil)
- [Phlogi](https://github.com/Phlogi) - [Phlogi](https://github.com/Phlogi)
- [pjeanjean](https://github.com/pjeanjean) - [pjeanjean](https://github.com/pjeanjean)
- [ploughpuff](https://github.com/ploughpuff) - [ploughpuff](https://github.com/ploughpuff)
- [poytiis](https://github.com/poytiis)
- [pR0Ps](https://github.com/pR0Ps) - [pR0Ps](https://github.com/pR0Ps)
- [PrplHaz4](https://github.com/PrplHaz4) - [PrplHaz4](https://github.com/PrplHaz4)
- [RazeLighter777](https://github.com/RazeLighter777) - [RazeLighter777](https://github.com/RazeLighter777)
- [redSpoutnik](https://github.com/redSpoutnik) - [redSpoutnik](https://github.com/redSpoutnik)
- [ringmatter](https://github.com/ringmatter) - [ringmatter](https://github.com/ringmatter)
- [Robert Lützner](https://github.com/rluetzner)
- [ryan-hartzell](https://github.com/ryan-hartzell) - [ryan-hartzell](https://github.com/ryan-hartzell)
- [s0urcelab](https://github.com/s0urcelab) - [s0urcelab](https://github.com/s0urcelab)
- [sachk](https://github.com/sachk) - [sachk](https://github.com/sachk)
- [sammyrc34](https://github.com/sammyrc34) - [sammyrc34](https://github.com/sammyrc34)
- [samuel9554](https://github.com/samuel9554) - [samuel9554](https://github.com/samuel9554)
- [SapientGuardian](https://github.com/SapientGuardian)
- [scheidleon](https://github.com/scheidleon) - [scheidleon](https://github.com/scheidleon)
- [sebPomme](https://github.com/sebPomme) - [sebPomme](https://github.com/sebPomme)
- [SegiH](https://github.com/SegiH) - [SegiH](https://github.com/SegiH)
@@ -140,7 +126,6 @@
- [sl1288](https://github.com/sl1288) - [sl1288](https://github.com/sl1288)
- [Smith00101010](https://github.com/Smith00101010) - [Smith00101010](https://github.com/Smith00101010)
- [sorinyo2004](https://github.com/sorinyo2004) - [sorinyo2004](https://github.com/sorinyo2004)
- [Soumyadip Auddy](https://github.com/SoumyadipAuddy)
- [sparky8251](https://github.com/sparky8251) - [sparky8251](https://github.com/sparky8251)
- [spookbits](https://github.com/spookbits) - [spookbits](https://github.com/spookbits)
- [ssenart](https://github.com/ssenart) - [ssenart](https://github.com/ssenart)
@@ -163,7 +148,6 @@
- [twinkybot](https://github.com/twinkybot) - [twinkybot](https://github.com/twinkybot)
- [Ullmie02](https://github.com/Ullmie02) - [Ullmie02](https://github.com/Ullmie02)
- [Unhelpful](https://github.com/Unhelpful) - [Unhelpful](https://github.com/Unhelpful)
- [Utku Özdemir](https://github.com/utkuozdemir)
- [viaregio](https://github.com/viaregio) - [viaregio](https://github.com/viaregio)
- [vitorsemeano](https://github.com/vitorsemeano) - [vitorsemeano](https://github.com/vitorsemeano)
- [voodoos](https://github.com/voodoos) - [voodoos](https://github.com/voodoos)
@@ -179,7 +163,6 @@
- [XVicarious](https://github.com/XVicarious) - [XVicarious](https://github.com/XVicarious)
- [YouKnowBlom](https://github.com/YouKnowBlom) - [YouKnowBlom](https://github.com/YouKnowBlom)
- [ZachPhelan](https://github.com/ZachPhelan) - [ZachPhelan](https://github.com/ZachPhelan)
- [ZeusCraft10](https://github.com/ZeusCraft10)
- [KristupasSavickas](https://github.com/KristupasSavickas) - [KristupasSavickas](https://github.com/KristupasSavickas)
- [Pusta](https://github.com/pusta) - [Pusta](https://github.com/pusta)
- [nielsvanvelzen](https://github.com/nielsvanvelzen) - [nielsvanvelzen](https://github.com/nielsvanvelzen)
@@ -222,15 +205,6 @@
- [theshoeshiner](https://github.com/theshoeshiner) - [theshoeshiner](https://github.com/theshoeshiner)
- [TokerX](https://github.com/TokerX) - [TokerX](https://github.com/TokerX)
- [GeneMarks](https://github.com/GeneMarks) - [GeneMarks](https://github.com/GeneMarks)
- [Kirill Nikiforov](https://github.com/allmazz)
- [bjorntp](https://github.com/bjorntp)
- [martenumberto](https://github.com/martenumberto)
- [ZeusCraft10](https://github.com/ZeusCraft10)
- [MarcoCoreDuo](https://github.com/MarcoCoreDuo)
- [LiHRaM](https://github.com/LiHRaM)
- [MSalman5230](https://github.com/MSalman5230)
- [dwandw](https://github.com/dwandw)
- [Lampan-git](https://github.com/Lampan-git)
# Emby Contributors # Emby Contributors
@@ -294,3 +268,16 @@
- [tikuf](https://github.com/tikuf/) - [tikuf](https://github.com/tikuf/)
- [Tim Hobbs](https://github.com/timhobbs) - [Tim Hobbs](https://github.com/timhobbs)
- [SvenVandenbrande](https://github.com/SvenVandenbrande) - [SvenVandenbrande](https://github.com/SvenVandenbrande)
- [olsh](https://github.com/olsh)
- [lbenini](https://github.com/lbenini)
- [gnuyent](https://github.com/gnuyent)
- [Matthew Jones](https://github.com/matthew-jones-uk)
- [Jakob Kukla](https://github.com/jakobkukla)
- [Utku Özdemir](https://github.com/utkuozdemir)
- [JPUC1143](https://github.com/Jpuc1143/)
- [0x25CBFC4F](https://github.com/0x25CBFC4F)
- [Robert Lützner](https://github.com/rluetzner)
- [Nathan McCrina](https://github.com/nfmccrina)
- [Martin Reuter](https://github.com/reuterma24)
- [Michael McElroy](https://github.com/mcmcelro)
- [Soumyadip Auddy](https://github.com/SoumyadipAuddy)

View File

@@ -4,52 +4,57 @@
</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="8.0.2" /> <PackageVersion Include="AsyncKeyedLock" Version="7.1.7" />
<PackageVersion Include="AutoFixture.AutoMoq" Version="4.18.1" /> <PackageVersion Include="AutoFixture.AutoMoq" Version="4.18.1" />
<PackageVersion Include="AutoFixture.Xunit3" Version="4.19.0" /> <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="BitFaster.Caching" Version="2.5.4" /> <PackageVersion Include="BitFaster.Caching" Version="2.5.4" />
<PackageVersion Include="BlurHashSharp.SkiaSharp" Version="1.4.0-pre.1" /> <PackageVersion Include="BlurHashSharp.SkiaSharp" Version="1.4.0-pre.1" />
<PackageVersion Include="BlurHashSharp" Version="1.4.0-pre.1" /> <PackageVersion Include="BlurHashSharp" Version="1.4.0-pre.1" />
<PackageVersion Include="CommandLineParser" Version="2.9.1" /> <PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="coverlet.collector" Version="10.0.0" /> <PackageVersion Include="coverlet.collector" Version="6.0.4" />
<PackageVersion Include="Diacritics" Version="4.1.8" /> <PackageVersion Include="Diacritics" Version="4.0.17" />
<PackageVersion Include="DiscUtils.Udf" Version="0.16.13" /> <PackageVersion Include="DiscUtils.Udf" Version="0.16.13" />
<PackageVersion Include="DotNet.Glob" Version="3.1.3" /> <PackageVersion Include="DotNet.Glob" Version="3.1.3" />
<PackageVersion Include="FsCheck.Xunit.v3" Version="3.3.3" /> <PackageVersion Include="FsCheck.Xunit" Version="3.3.1" />
<PackageVersion Include="HarfBuzzSharp.NativeAssets.Linux" Version="8.3.1.1" /> <PackageVersion Include="HarfBuzzSharp.NativeAssets.Linux" Version="8.3.1.1" />
<PackageVersion Include="ICU4N.Transliterator" Version="60.1.0-alpha.356" /> <PackageVersion Include="ICU4N.Transliterator" Version="60.1.0-alpha.356" />
<PackageVersion Include="IDisposableAnalyzers" Version="4.0.8" /> <PackageVersion Include="IDisposableAnalyzers" Version="4.0.8" />
<PackageVersion Include="Ignore" Version="0.2.1" /> <PackageVersion Include="Ignore" Version="0.2.1" />
<PackageVersion Include="Jellyfin.XmlTv" Version="10.12.0-pre1" /> <PackageVersion Include="Jellyfin.XmlTv" Version="10.8.0" />
<PackageVersion Include="libse" Version="4.0.12" /> <PackageVersion Include="libse" Version="4.0.12" />
<PackageVersion Include="LrcParser" Version="2025.623.0" /> <PackageVersion Include="LrcParser" Version="2025.623.0" />
<PackageVersion Include="MetaBrainz.MusicBrainz" Version="8.0.1" /> <PackageVersion Include="MetaBrainz.MusicBrainz" Version="6.1.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="10.0.8" /> <PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="9.0.10" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.8" /> <PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.10" />
<PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="4.14.0" /> <PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="4.14.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="5.3.0" /> <PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.14.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="5.3.0" /> <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="5.3.0" /> <PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="10.0.8" /> <PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.8" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.8" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.8" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.8" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.10" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="10.0.8" /> <PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.10" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="10.0.8" /> <PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.10" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.8" /> <PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.10" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.8" /> <PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.10" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.8" /> <PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.10" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="10.0.8" /> <PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="9.0.10" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.8" /> <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.10" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="10.0.8" /> <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.10" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.8" /> <PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="9.0.10" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="10.0.8" /> <PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="9.0.10" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.5.1" /> <PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.10" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.10" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.10" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.10" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.10" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageVersion Include="MimeTypes" Version="2.5.2" /> <PackageVersion Include="MimeTypes" Version="2.5.2" />
<PackageVersion Include="Morestachio" Version="5.0.1.670" /> <PackageVersion Include="Morestachio" Version="5.0.1.631" />
<PackageVersion Include="Moq" Version="4.18.4" /> <PackageVersion Include="Moq" Version="4.18.4" />
<PackageVersion Include="NEbml" Version="1.1.0.5" /> <PackageVersion Include="NEbml" Version="1.1.0.5" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" /> <PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
@@ -57,33 +62,38 @@
<PackageVersion Include="prometheus-net.AspNetCore" Version="8.2.1" /> <PackageVersion Include="prometheus-net.AspNetCore" Version="8.2.1" />
<PackageVersion Include="prometheus-net.DotNetRuntime" Version="4.4.1" /> <PackageVersion Include="prometheus-net.DotNetRuntime" Version="4.4.1" />
<PackageVersion Include="prometheus-net" Version="8.2.1" /> <PackageVersion Include="prometheus-net" Version="8.2.1" />
<PackageVersion Include="Polly" Version="8.6.6" /> <PackageVersion Include="Polly" Version="8.6.4" />
<PackageVersion Include="Serilog.AspNetCore" Version="10.0.0" /> <PackageVersion Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageVersion Include="Serilog.Enrichers.Thread" Version="4.0.0" /> <PackageVersion Include="Serilog.Enrichers.Thread" Version="4.0.0" />
<PackageVersion Include="Serilog.Expressions" Version="5.0.0" /> <PackageVersion Include="Serilog.Expressions" Version="5.0.0" />
<PackageVersion Include="Serilog.Settings.Configuration" Version="10.0.0" /> <PackageVersion Include="Serilog.Settings.Configuration" Version="9.0.0" />
<PackageVersion Include="Serilog.Sinks.Async" Version="2.1.0" /> <PackageVersion Include="Serilog.Sinks.Async" Version="2.1.0" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.1.1" /> <PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" /> <PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageVersion Include="Serilog.Sinks.Graylog" Version="3.1.1" /> <PackageVersion Include="Serilog.Sinks.Graylog" Version="3.1.1" />
<PackageVersion Include="SerilogAnalyzer" Version="0.15.0" /> <PackageVersion Include="SerilogAnalyzer" Version="0.15.0" />
<PackageVersion Include="SharpFuzz" Version="2.2.0" /> <PackageVersion Include="SharpFuzz" Version="2.2.0" />
<!-- Pinned to 3.116.1 because https://github.com/jellyfin/jellyfin/pull/14255 --> <!-- Pinned to 3.116.1 because https://github.com/jellyfin/jellyfin/pull/14255 -->
<PackageVersion Include="SkiaSharp" Version="[3.116.1]" /> <PackageVersion Include="SkiaSharp" Version="3.116.1" />
<PackageVersion Include="SkiaSharp.HarfBuzz" Version="[3.116.1]" /> <PackageVersion Include="SkiaSharp.HarfBuzz" Version="3.116.1" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="[3.116.1]" /> <PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="3.116.1" />
<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="3.4.1" /> <PackageVersion Include="Svg.Skia" Version="3.2.1" />
<PackageVersion Include="Swashbuckle.AspNetCore.ReDoc" Version="10.1.7" /> <PackageVersion Include="Swashbuckle.AspNetCore.ReDoc" Version="6.5.0" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="10.1.7" /> <PackageVersion Include="Swashbuckle.AspNetCore" Version="6.2.3" />
<PackageVersion Include="System.Text.Json" Version="10.0.8" /> <PackageVersion Include="System.Globalization" Version="4.3.0" />
<PackageVersion Include="System.Linq.Async" Version="6.0.3" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.10" />
<PackageVersion Include="System.Text.Json" Version="9.0.10" />
<PackageVersion Include="System.Threading.Tasks.Dataflow" Version="9.0.10" />
<PackageVersion Include="TagLibSharp" Version="2.3.0" /> <PackageVersion Include="TagLibSharp" Version="2.3.0" />
<PackageVersion Include="z440.atl.core" Version="7.13.0" /> <PackageVersion Include="z440.atl.core" Version="7.6.0" />
<PackageVersion Include="TMDbLib" Version="3.0.0" /> <PackageVersion Include="TMDbLib" Version="2.3.0" />
<PackageVersion Include="UTF.Unknown" Version="2.6.0" /> <PackageVersion Include="UTF.Unknown" Version="2.6.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" /> <PackageVersion Include="Xunit.Priority" Version="1.1.6" />
<PackageVersion Include="xunit.v3" Version="3.2.2" /> <PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
<PackageVersion Include="Xunit.v3.Priority" Version="1.1.18" /> <PackageVersion Include="Xunit.SkippableFact" Version="1.5.23" />
<PackageVersion Include="xunit" Version="2.9.3" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

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

View File

@@ -1,75 +0,0 @@
using System.Text.RegularExpressions;
namespace Emby.Naming.Book
{
/// <summary>
/// Helper class to retrieve basic metadata from a book filename.
/// </summary>
public static class BookFileNameParser
{
private const string NameMatchGroup = "name";
private const string IndexMatchGroup = "index";
private const string YearMatchGroup = "year";
private const string SeriesNameMatchGroup = "seriesName";
private static readonly Regex[] _nameMatches =
[
// seriesName (seriesYear) #index (of count) (year) where only seriesName and index are required
new Regex(@"^(?<seriesName>.+?)((\s\((?<seriesYear>[0-9]{4})\))?)\s#(?<index>[0-9]+)((\s\(of\s(?<count>[0-9]+)\))?)((\s\((?<year>[0-9]{4})\))?)$"),
new Regex(@"^(?<name>.+?)\s\((?<seriesName>.+?),\s#(?<index>[0-9]+)\)((\s\((?<year>[0-9]{4})\))?)$"),
new Regex(@"^(?<index>[0-9]+)\s\-\s(?<name>.+?)((\s\((?<year>[0-9]{4})\))?)$"),
new Regex(@"(?<name>.*)\((?<year>[0-9]{4})\)"),
// last resort matches the whole string as the name
new Regex(@"(?<name>.*)")
];
/// <summary>
/// Parse a filename name to retrieve the book name, series name, index, and year.
/// </summary>
/// <param name="name">Book filename to parse for information.</param>
/// <returns>Returns <see cref="BookFileNameParserResult"/> object.</returns>
public static BookFileNameParserResult Parse(string? name)
{
var result = new BookFileNameParserResult();
if (name == null)
{
return result;
}
foreach (var regex in _nameMatches)
{
var match = regex.Match(name);
if (!match.Success)
{
continue;
}
if (match.Groups.TryGetValue(NameMatchGroup, out Group? nameGroup) && nameGroup.Success)
{
result.Name = nameGroup.Value.Trim();
}
if (match.Groups.TryGetValue(IndexMatchGroup, out Group? indexGroup) && indexGroup.Success && int.TryParse(indexGroup.Value, out var index))
{
result.Index = index;
}
if (match.Groups.TryGetValue(YearMatchGroup, out Group? yearGroup) && yearGroup.Success && int.TryParse(yearGroup.Value, out var year))
{
result.Year = year;
}
if (match.Groups.TryGetValue(SeriesNameMatchGroup, out Group? seriesGroup) && seriesGroup.Success)
{
result.SeriesName = seriesGroup.Value.Trim();
}
break;
}
return result;
}
}
}

View File

@@ -1,41 +0,0 @@
using System;
namespace Emby.Naming.Book
{
/// <summary>
/// Data object used to pass metadata parsed from a book filename.
/// </summary>
public class BookFileNameParserResult
{
/// <summary>
/// Initializes a new instance of the <see cref="BookFileNameParserResult"/> class.
/// </summary>
public BookFileNameParserResult()
{
Name = null;
Index = null;
Year = null;
SeriesName = null;
}
/// <summary>
/// Gets or sets the name of the book.
/// </summary>
public string? Name { get; set; }
/// <summary>
/// Gets or sets the book index.
/// </summary>
public int? Index { get; set; }
/// <summary>
/// Gets or sets the publication year.
/// </summary>
public int? Year { get; set; }
/// <summary>
/// Gets or sets the series name.
/// </summary>
public string? SeriesName { get; set; }
}
}

View File

@@ -152,8 +152,8 @@ namespace Emby.Naming.Common
CleanStrings = CleanStrings =
[ [
@"^\s*(?<cleaned>.+?)[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multi|subs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r5|bd5|bd|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|blu-ray|x264|x265|h264|h265|xvid|xvidvd|xxx|www.www|AAC|DTS)(?=[ _\,\.\(\)\[\]\-]|$)", @"^\s*(?<cleaned>.+?)[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multi|subs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r5|bd5|bd|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|blu-ray|x264|x265|h264|h265|xvid|xvidvd|xxx|www.www|AAC|DTS|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
@"^\s*(?<cleaned>.+?)((\s*\[[^\]]+\]\s*)+)(\.[^\s]+)?$", @"^(?<cleaned>.+?)(\[.*\])",
@"^\s*(?<cleaned>.+?)\WE[0-9]+(-|~)E?[0-9]+(\W|$)", @"^\s*(?<cleaned>.+?)\WE[0-9]+(-|~)E?[0-9]+(\W|$)",
@"^\s*\[[^\]]+\](?!\.\w+$)\s*(?<cleaned>.+)", @"^\s*\[[^\]]+\](?!\.\w+$)\s*(?<cleaned>.+)",
@"^\s*(?<cleaned>.+?)\s+-\s+[0-9]+\s*$", @"^\s*(?<cleaned>.+?)\s+-\s+[0-9]+\s*$",
@@ -225,7 +225,6 @@ namespace Emby.Naming.Common
".afc", ".afc",
".amf", ".amf",
".aif", ".aif",
".aifc",
".aiff", ".aiff",
".alac", ".alac",
".amr", ".amr",
@@ -379,14 +378,6 @@ namespace Emby.Naming.Common
IsNamed = true IsNamed = true
}, },
// "Name - 101.mkv", "Name - 101 [720p].mkv", "Name - 101 (2020).mkv"
// Handles absolute episode numbers with hyphen delimiter (common in anime)
// Without brackets (bracketed version handled above)
new EpisodeExpression(@".*[\\\/](?<seriesname>[^\\\/]+?)[\s_]+-[\s_]+(?<epnumber>[0-9]+)[\s_]*(?:\[.*?\]|\(.*?\))*[\s_]*(?:\.\w+)?$")
{
IsNamed = true
},
// /server/anything_102.mp4 // /server/anything_102.mp4
// /server/james.corden.2017.04.20.anne.hathaway.720p.hdtv.x264-crooks.mkv // /server/james.corden.2017.04.20.anne.hathaway.720p.hdtv.x264-crooks.mkv
// /server/anything_1996.11.14.mp4 // /server/anything_1996.11.14.mp4

View File

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

View File

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

View File

@@ -18,7 +18,7 @@ public static class TvParserHelpers
/// <param name="status">The status string.</param> /// <param name="status">The status string.</param>
/// <param name="enumValue">The <see cref="SeriesStatus"/>.</param> /// <param name="enumValue">The <see cref="SeriesStatus"/>.</param>
/// <returns>Returns true if parsing was successful.</returns> /// <returns>Returns true if parsing was successful.</returns>
public static bool TryParseSeriesStatus(string? status, out SeriesStatus? enumValue) public static bool TryParseSeriesStatus(string status, out SeriesStatus? enumValue)
{ {
if (Enum.TryParse(status, true, out SeriesStatus seriesStatus)) if (Enum.TryParse(status, true, out SeriesStatus seriesStatus))
{ {

View File

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

View File

@@ -44,7 +44,7 @@ namespace Emby.Naming.Video
var match = expression.Match(name); var match = expression.Match(name);
if (match.Success && match.Groups.TryGetValue("cleaned", out var cleaned)) if (match.Success && match.Groups.TryGetValue("cleaned", out var cleaned))
{ {
newName = cleaned.Value.Trim(); newName = cleaned.Value;
return true; return true;
} }

View File

@@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@@ -137,27 +136,19 @@ namespace Emby.Naming.Video
if (videos.Count > 1) if (videos.Count > 1)
{ {
var groups = videos var groups = videos.GroupBy(x => ResolutionRegex().IsMatch(x.Files[0].FileNameWithoutExtension)).ToList();
.Select(x => (filename: x.Files[0].FileNameWithoutExtension.ToString(), value: x))
.Select(x => (x.filename, resolutionMatch: ResolutionRegex().Match(x.filename), x.value))
.GroupBy(x => x.resolutionMatch.Success)
.ToList();
videos.Clear(); videos.Clear();
StringComparer comparer = StringComparer.Create(CultureInfo.InvariantCulture, CompareOptions.NumericOrdering);
foreach (var group in groups) foreach (var group in groups)
{ {
if (group.Key) if (group.Key)
{ {
videos.InsertRange(0, group videos.InsertRange(0, group
.OrderByDescending(x => x.resolutionMatch.Value, comparer) .OrderByDescending(x => ResolutionRegex().Match(x.Files[0].FileNameWithoutExtension.ToString()).Value, new AlphanumericComparator())
.ThenBy(x => x.filename, comparer) .ThenBy(x => x.Files[0].FileNameWithoutExtension.ToString(), new AlphanumericComparator()));
.Select(x => x.value));
} }
else else
{ {
videos.AddRange(group.OrderBy(x => x.filename, comparer).Select(x => x.value)); videos.AddRange(group.OrderBy(x => x.Files[0].FileNameWithoutExtension.ToString(), new AlphanumericComparator()));
} }
} }
} }
@@ -217,8 +208,6 @@ namespace Emby.Naming.Video
// The CleanStringParser should have removed common keywords etc. // The CleanStringParser should have removed common keywords etc.
return testFilename.IsEmpty return testFilename.IsEmpty
|| testFilename[0] == '-' || testFilename[0] == '-'
|| testFilename[0] == '_'
|| testFilename[0] == '.'
|| CheckMultiVersionRegex().IsMatch(testFilename); || CheckMultiVersionRegex().IsMatch(testFilename);
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -39,24 +39,22 @@ namespace Emby.Server.Implementations.Cryptography
{ {
if (string.Equals(hash.Id, "PBKDF2", StringComparison.Ordinal)) if (string.Equals(hash.Id, "PBKDF2", StringComparison.Ordinal))
{ {
var iterations = GetIterationsParameter(hash);
return hash.Hash.SequenceEqual( return hash.Hash.SequenceEqual(
Rfc2898DeriveBytes.Pbkdf2( Rfc2898DeriveBytes.Pbkdf2(
password, password,
hash.Salt, hash.Salt,
iterations, int.Parse(hash.Parameters["iterations"], CultureInfo.InvariantCulture),
HashAlgorithmName.SHA1, HashAlgorithmName.SHA1,
32)); 32));
} }
if (string.Equals(hash.Id, "PBKDF2-SHA512", StringComparison.Ordinal)) if (string.Equals(hash.Id, "PBKDF2-SHA512", StringComparison.Ordinal))
{ {
var iterations = GetIterationsParameter(hash);
return hash.Hash.SequenceEqual( return hash.Hash.SequenceEqual(
Rfc2898DeriveBytes.Pbkdf2( Rfc2898DeriveBytes.Pbkdf2(
password, password,
hash.Salt, hash.Salt,
iterations, int.Parse(hash.Parameters["iterations"], CultureInfo.InvariantCulture),
HashAlgorithmName.SHA512, HashAlgorithmName.SHA512,
DefaultOutputLength)); DefaultOutputLength));
} }
@@ -64,27 +62,6 @@ namespace Emby.Server.Implementations.Cryptography
throw new NotSupportedException($"Can't verify hash with id: {hash.Id}"); throw new NotSupportedException($"Can't verify hash with id: {hash.Id}");
} }
/// <summary>
/// Extracts and validates the iterations parameter from a password hash.
/// </summary>
/// <param name="hash">The password hash containing parameters.</param>
/// <returns>The number of iterations.</returns>
/// <exception cref="FormatException">Thrown when iterations parameter is missing or invalid.</exception>
private static int GetIterationsParameter(PasswordHash hash)
{
if (!hash.Parameters.TryGetValue("iterations", out var iterationsStr))
{
throw new FormatException($"Password hash with id '{hash.Id}' is missing required 'iterations' parameter.");
}
if (!int.TryParse(iterationsStr, CultureInfo.InvariantCulture, out var iterations))
{
throw new FormatException($"Password hash with id '{hash.Id}' has invalid 'iterations' parameter: '{iterationsStr}'.");
}
return iterations;
}
/// <inheritdoc /> /// <inheritdoc />
public byte[] GenerateSalt() public byte[] GenerateSalt()
=> GenerateSalt(DefaultSaltLength); => GenerateSalt(DefaultSaltLength);

View File

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

View File

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

View File

@@ -27,6 +27,7 @@
<PackageReference Include="Microsoft.Data.Sqlite" /> <PackageReference Include="Microsoft.Data.Sqlite" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" />
<PackageReference Include="prometheus-net.DotNetRuntime" /> <PackageReference Include="prometheus-net.DotNetRuntime" />
@@ -38,7 +39,7 @@
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>net10.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup> </PropertyGroup>

View File

@@ -1,6 +1,5 @@
using System; using System;
using System.Buffers; using System.Buffers;
using System.Globalization;
using System.IO.Pipelines; using System.IO.Pipelines;
using System.Net; using System.Net;
using System.Net.WebSockets; using System.Net.WebSockets;
@@ -70,11 +69,6 @@ namespace Emby.Server.Implementations.HttpServer
/// <inheritdoc /> /// <inheritdoc />
public IPAddress? RemoteEndPoint { get; } public IPAddress? RemoteEndPoint { get; }
/// <summary>
/// Gets or initializes the UI culture captured from the upgrade request.
/// </summary>
public CultureInfo? RequestUICulture { get; init; }
/// <inheritdoc /> /// <inheritdoc />
public Func<WebSocketMessageInfo, Task>? OnReceive { get; set; } public Func<WebSocketMessageInfo, Task>? OnReceive { get; set; }
@@ -87,17 +81,6 @@ namespace Emby.Server.Implementations.HttpServer
/// <inheritdoc /> /// <inheritdoc />
public WebSocketState State => _socket.State; public WebSocketState State => _socket.State;
/// <inheritdoc />
public void ApplyRequestCulture()
{
if (RequestUICulture is null)
{
return;
}
CultureInfo.CurrentUICulture = RequestUICulture;
}
/// <inheritdoc /> /// <inheritdoc />
public async Task SendAsync(OutboundWebSocketMessage message, CancellationToken cancellationToken) public async Task SendAsync(OutboundWebSocketMessage message, CancellationToken cancellationToken)
{ {

View File

@@ -4,7 +4,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -48,18 +47,14 @@ namespace Emby.Server.Implementations.HttpServer
_logger.LogInformation("WS {IP} request", context.Connection.RemoteIpAddress); _logger.LogInformation("WS {IP} request", context.Connection.RemoteIpAddress);
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false); WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false);
var connection = new WebSocketConnection( var connection = new WebSocketConnection(
_loggerFactory.CreateLogger<WebSocketConnection>(), _loggerFactory.CreateLogger<WebSocketConnection>(),
webSocket, webSocket,
authorizationInfo, authorizationInfo,
context.GetNormalizedRemoteIP()) context.GetNormalizedRemoteIP())
{ {
RequestUICulture = CultureInfo.CurrentUICulture OnReceive = ProcessWebSocketMessageReceived
};
connection.OnReceive = result =>
{
connection.ApplyRequestCulture();
return ProcessWebSocketMessageReceived(result);
}; };
await using (connection.ConfigureAwait(false)) await using (connection.ConfigureAwait(false))
{ {

View File

@@ -21,7 +21,6 @@ namespace Emby.Server.Implementations.IO
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly IServerConfigurationManager _configurationManager; private readonly IServerConfigurationManager _configurationManager;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly DotIgnoreIgnoreRule _dotIgnoreIgnoreRule;
/// <summary> /// <summary>
/// The file system watchers. /// The file system watchers.
@@ -48,23 +47,19 @@ namespace Emby.Server.Implementations.IO
/// <param name="configurationManager">The configuration manager.</param> /// <param name="configurationManager">The configuration manager.</param>
/// <param name="fileSystem">The filesystem.</param> /// <param name="fileSystem">The filesystem.</param>
/// <param name="appLifetime">The <see cref="IHostApplicationLifetime"/>.</param> /// <param name="appLifetime">The <see cref="IHostApplicationLifetime"/>.</param>
/// <param name="dotIgnoreIgnoreRule">The .ignore rule handler.</param>
public LibraryMonitor( public LibraryMonitor(
ILogger<LibraryMonitor> logger, ILogger<LibraryMonitor> logger,
ILibraryManager libraryManager, ILibraryManager libraryManager,
IServerConfigurationManager configurationManager, IServerConfigurationManager configurationManager,
IFileSystem fileSystem, IFileSystem fileSystem,
IHostApplicationLifetime appLifetime, IHostApplicationLifetime appLifetime)
DotIgnoreIgnoreRule dotIgnoreIgnoreRule)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
_logger = logger; _logger = logger;
_configurationManager = configurationManager; _configurationManager = configurationManager;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_dotIgnoreIgnoreRule = dotIgnoreIgnoreRule;
appLifetime.ApplicationStarted.Register(Start); appLifetime.ApplicationStarted.Register(Start);
appLifetime.ApplicationStopping.Register(Stop);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -357,12 +352,6 @@ namespace Emby.Server.Implementations.IO
return; return;
} }
var fileInfo = _fileSystem.GetFileSystemInfo(path);
if (_dotIgnoreIgnoreRule.ShouldIgnore(fileInfo, null))
{
return;
}
// Ignore certain files, If the parent of an ignored path has a change event, ignore that too // Ignore certain files, If the parent of an ignored path has a change event, ignore that too
foreach (var i in _tempIgnoredPaths.Keys) foreach (var i in _tempIgnoredPaths.Keys)
{ {

View File

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

View File

@@ -267,11 +267,8 @@ namespace Emby.Server.Implementations.Images
{ {
var image = item.GetImageInfo(type, 0); var image = item.GetImageInfo(type, 0);
if (image is null) if (image is not null)
{ {
return GetItemsWithImages(item).Count is not 0;
}
if (!image.IsLocalFile) if (!image.IsLocalFile)
{ {
return false; return false;
@@ -286,6 +283,7 @@ namespace Emby.Server.Implementations.Images
{ {
return false; return false;
} }
}
return true; return true;
} }

View File

@@ -40,7 +40,7 @@ namespace Emby.Server.Implementations.Images
includeItemTypes = new[] { BaseItemKind.Series }; includeItemTypes = new[] { BaseItemKind.Series };
break; break;
case CollectionType.music: case CollectionType.music:
includeItemTypes = new[] { BaseItemKind.MusicArtist }; // Music albums usually don't have dedicated backdrops, so use artist instead includeItemTypes = new[] { BaseItemKind.MusicAlbum };
break; break;
case CollectionType.musicvideos: case CollectionType.musicvideos:
includeItemTypes = new[] { BaseItemKind.MusicVideo }; includeItemTypes = new[] { BaseItemKind.MusicVideo };
@@ -98,11 +98,5 @@ namespace Emby.Server.Implementations.Images
return base.CreateImage(item, itemsWithImages, outputPath, imageType, imageIndex); return base.CreateImage(item, itemsWithImages, outputPath, imageType, imageIndex);
} }
protected override bool HasChangedByDate(BaseItem item, ItemImageInfo image)
{
var age = DateTime.UtcNow - image.DateModified;
return age.TotalDays > 7;
}
} }
} }

View File

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

View File

@@ -31,20 +31,6 @@ namespace Emby.Server.Implementations.Library
"**/*.sample.?????", "**/*.sample.?????",
"**/sample/*", "**/sample/*",
// Avoid adding Hungarian sample files
// https://github.com/jellyfin/jellyfin/issues/16237
"**/minta.?",
"**/minta.??",
"**/minta.???", // Matches minta.mkv
"**/minta.????", // Matches minta.webm
"**/minta.?????",
"**/*.minta.?",
"**/*.minta.??",
"**/*.minta.???",
"**/*.minta.????",
"**/*.minta.?????",
"**/minta/*",
// Directories // Directories
"**/metadata/**", "**/metadata/**",
"**/metadata", "**/metadata",
@@ -64,10 +50,6 @@ namespace Emby.Server.Implementations.Library
"**/lost+found", "**/lost+found",
"**/subs/**", "**/subs/**",
"**/subs", "**/subs",
"**/.snapshots/**",
"**/.snapshots",
"**/.snapshot/**",
"**/.snapshot",
// Trickplay files // Trickplay files
"**/*.trickplay", "**/*.trickplay",

View File

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

View File

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

View File

@@ -37,27 +37,17 @@ namespace Emby.Server.Implementations.Library
while (attributeIndex > -1 && attributeIndex < maxIndex) while (attributeIndex > -1 && attributeIndex < maxIndex)
{ {
var attributeEnd = attributeIndex + attribute.Length; var attributeEnd = attributeIndex + attribute.Length;
if (attributeIndex > 0) if (attributeIndex > 0
&& str[attributeIndex - 1] == '['
&& (str[attributeEnd] == '=' || str[attributeEnd] == '-'))
{ {
var attributeOpener = str[attributeIndex - 1]; var closingIndex = str[attributeEnd..].IndexOf(']');
var attributeCloser = attributeOpener switch
{
'[' => ']',
'(' => ')',
'{' => '}',
_ => '\0'
};
if (attributeCloser != '\0' && (str[attributeEnd] == '=' || str[attributeEnd] == '-'))
{
var closingIndex = str[attributeEnd..].IndexOf(attributeCloser);
// Must be at least 1 character before the closing bracket. // Must be at least 1 character before the closing bracket.
if (closingIndex > 1) if (closingIndex > 1)
{ {
return str[(attributeEnd + 1)..(attributeEnd + closingIndex)].Trim().ToString(); return str[(attributeEnd + 1)..(attributeEnd + closingIndex)].Trim().ToString();
} }
} }
}
str = str[attributeEnd..]; str = str[attributeEnd..];
attributeIndex = str.IndexOf(attribute, StringComparison.OrdinalIgnoreCase); attributeIndex = str.IndexOf(attribute, StringComparison.OrdinalIgnoreCase);
@@ -70,16 +60,6 @@ namespace Emby.Server.Implementations.Library
return match ? imdbId.ToString() : null; return match ? imdbId.ToString() : null;
} }
// Allow tmdb as an alias for tmdbid
if (attribute.Equals("tmdbid", StringComparison.OrdinalIgnoreCase))
{
var tmdbValue = str.GetAttributeValue("tmdb");
if (tmdbValue is not null)
{
return tmdbValue;
}
}
return null; return null;
} }

View File

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

View File

@@ -5,12 +5,12 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Emby.Naming.Book;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using Jellyfin.Extensions; using Jellyfin.Extensions;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers; using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
namespace Emby.Server.Implementations.Library.Resolvers.Books namespace Emby.Server.Implementations.Library.Resolvers.Books
{ {
@@ -35,24 +35,19 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books
var extension = Path.GetExtension(args.Path.AsSpan()); var extension = Path.GetExtension(args.Path.AsSpan());
if (!_validExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase)) if (_validExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase))
{ {
return null; // It's a book
}
var result = BookFileNameParser.Parse(Path.GetFileNameWithoutExtension(args.Path));
return new Book return new Book
{ {
Path = args.Path, Path = args.Path,
Name = result.Name ?? string.Empty, IsInMixedFolder = true
IndexNumber = result.Index,
ProductionYear = result.Year,
SeriesName = result.SeriesName ?? Path.GetFileName(Path.GetDirectoryName(args.Path)),
IsInMixedFolder = true,
}; };
} }
return null;
}
private Book GetBook(ItemResolveArgs args) private Book GetBook(ItemResolveArgs args)
{ {
var bookFiles = args.FileSystemChildren.Where(f => var bookFiles = args.FileSystemChildren.Where(f =>
@@ -64,22 +59,15 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books
StringComparison.OrdinalIgnoreCase); StringComparison.OrdinalIgnoreCase);
}).ToList(); }).ToList();
// directory is only considered a book when it contains exactly one supported file // Don't return a Book if there is more (or less) than one document in the directory
// other library structures with multiple books to a directory will get picked up as individual files
if (bookFiles.Count != 1) if (bookFiles.Count != 1)
{ {
return null; return null;
} }
var result = BookFileNameParser.Parse(Path.GetFileName(args.Path));
return new Book return new Book
{ {
Path = bookFiles[0].FullName, Path = bookFiles[0].FullName
Name = result.Name ?? string.Empty,
IndexNumber = result.Index,
ProductionYear = result.Year,
SeriesName = result.SeriesName ?? string.Empty,
}; };
} }
} }

View File

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

View File

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

View File

@@ -42,7 +42,7 @@ namespace Emby.Server.Implementations.Library
results = results.GetRange(query.StartIndex.Value, totalRecordCount - query.StartIndex.Value); results = results.GetRange(query.StartIndex.Value, totalRecordCount - query.StartIndex.Value);
} }
if (query.Limit.HasValue && query.Limit.Value > 0) if (query.Limit.HasValue)
{ {
results = results.GetRange(0, Math.Min(query.Limit.Value, results.Count)); results = results.GetRange(0, Math.Min(query.Limit.Value, results.Count));
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,3 @@
{ {
"AppDeviceValues": "Апп: {0}, Априбор: {1}" "Albums": "аальбомқәа"
} }

View File

@@ -1,8 +1,11 @@
{ {
"Artists": "Kunstenare", "Artists": "Kunstenare",
"Channels": "Kanale",
"Folders": "Lêergidse", "Folders": "Lêergidse",
"Favorites": "Gunstelinge", "Favorites": "Gunstelinge",
"HeaderFavoriteShows": "Gunsteling Vertonings", "HeaderFavoriteShows": "Gunsteling Vertonings",
"ValueSpecialEpisodeName": "Spesiale - {0}",
"HeaderAlbumArtists": "Album kunstenaars",
"Books": "Boeke", "Books": "Boeke",
"HeaderNextUp": "Volgende", "HeaderNextUp": "Volgende",
"Movies": "Flieks", "Movies": "Flieks",
@@ -10,13 +13,24 @@
"HeaderContinueWatching": "Hou aan kyk", "HeaderContinueWatching": "Hou aan kyk",
"HeaderFavoriteEpisodes": "Gunsteling Episodes", "HeaderFavoriteEpisodes": "Gunsteling Episodes",
"Photos": "Foto's", "Photos": "Foto's",
"Playlists": "Snitlyste",
"HeaderFavoriteArtists": "Gunsteling Kunstenaars",
"HeaderFavoriteAlbums": "Gunsteling Albums",
"Sync": "Sinkroniseer",
"HeaderFavoriteSongs": "Gunsteling Liedjies",
"Songs": "Liedjies",
"DeviceOnlineWithName": "{0} is aanlyn",
"DeviceOfflineWithName": "{0} is ontkoppel",
"Collections": "Versamelings", "Collections": "Versamelings",
"Inherit": "Ontvang", "Inherit": "Ontvang",
"HeaderLiveTV": "Lewendige TV", "HeaderLiveTV": "Lewendige TV",
"Application": "Program",
"AppDeviceValues": "App: {0}, Toestel: {1}", "AppDeviceValues": "App: {0}, Toestel: {1}",
"VersionNumber": "Weergawe {0}", "VersionNumber": "Weergawe {0}",
"ValueHasBeenAddedToLibrary": "{0} is by jou media biblioteek bygevoeg",
"UserStoppedPlayingItemWithValues": "{0} het klaar {1} op {2} gespeel", "UserStoppedPlayingItemWithValues": "{0} het klaar {1} op {2} gespeel",
"UserStartedPlayingItemWithValues": "{0} is besig om {1} op {2} te speel", "UserStartedPlayingItemWithValues": "{0} is besig om {1} op {2} te speel",
"UserPolicyUpdatedWithName": "Gebruiker beleid is verander vir {0}",
"UserPasswordChangedWithName": "Gebruiker {0} se wagwoord is verander", "UserPasswordChangedWithName": "Gebruiker {0} se wagwoord is verander",
"UserOnlineFromDevice": "{0} is aanlyn van {1}", "UserOnlineFromDevice": "{0} is aanlyn van {1}",
"UserOfflineFromDevice": "{0} is ontkoppel van {1}", "UserOfflineFromDevice": "{0} is ontkoppel van {1}",
@@ -24,13 +38,19 @@
"UserDownloadingItemWithValues": "{0} is besig om {1} af te laai", "UserDownloadingItemWithValues": "{0} is besig om {1} af te laai",
"UserDeletedWithName": "Gebruiker {0} is verwyder", "UserDeletedWithName": "Gebruiker {0} is verwyder",
"UserCreatedWithName": "Gebruiker {0} is geskep", "UserCreatedWithName": "Gebruiker {0} is geskep",
"User": "Gebruiker",
"TvShows": "TV Programme", "TvShows": "TV Programme",
"System": "Stelsel",
"SubtitleDownloadFailureFromForItem": "Ondertitels het misluk om af te laai van {0} vir {1}", "SubtitleDownloadFailureFromForItem": "Ondertitels het misluk om af te laai van {0} vir {1}",
"StartupEmbyServerIsLoading": "Jellyfin Bediener is besig om te laai. Probeer weer in 'n kort tyd.", "StartupEmbyServerIsLoading": "Jellyfin Bediener is besig om te laai. Probeer weer in 'n kort tyd.",
"ServerNameNeedsToBeRestarted": "{0} moet herbegin word",
"ScheduledTaskStartedWithName": "{0} het begin",
"ScheduledTaskFailedWithName": "{0} het misluk", "ScheduledTaskFailedWithName": "{0} het misluk",
"ProviderValue": "Voorsiener: {0}",
"PluginUpdatedWithName": "{0} was opgedateer", "PluginUpdatedWithName": "{0} was opgedateer",
"PluginUninstalledWithName": "{0} was verwyder", "PluginUninstalledWithName": "{0} was verwyder",
"PluginInstalledWithName": "{0} is geïnstalleer", "PluginInstalledWithName": "{0} is geïnstalleer",
"Plugin": "Inprop module",
"NotificationOptionVideoPlaybackStopped": "Video terugspeel het gestop", "NotificationOptionVideoPlaybackStopped": "Video terugspeel het gestop",
"NotificationOptionVideoPlayback": "Video terugspeel het begin", "NotificationOptionVideoPlayback": "Video terugspeel het begin",
"NotificationOptionUserLockedOut": "Gebruiker uitgeslyt", "NotificationOptionUserLockedOut": "Gebruiker uitgeslyt",
@@ -54,14 +74,23 @@
"MusicVideos": "Musiek Videos", "MusicVideos": "Musiek Videos",
"Music": "Musiek", "Music": "Musiek",
"MixedContent": "Gemengde inhoud", "MixedContent": "Gemengde inhoud",
"MessageServerConfigurationUpdated": "Bediener konfigurasie is opgedateer",
"MessageNamedServerConfigurationUpdatedWithValue": "Bediener konfigurasie seksie {0} is opgedateer",
"MessageApplicationUpdatedTo": "Jellyfin Bediener is opgedateer na {0}",
"MessageApplicationUpdated": "Jellyfin Bediener is opgedateer",
"Latest": "Nuutste", "Latest": "Nuutste",
"LabelRunningTimeValue": "Werktyd: {0}", "LabelRunningTimeValue": "Werktyd: {0}",
"LabelIpAddressValue": "IP adres: {0}", "LabelIpAddressValue": "IP adres: {0}",
"ItemRemovedWithName": "{0} is uit versameling verwyder",
"ItemAddedWithName": "{0} is by die versameling gevoeg",
"HomeVideos": "Tuis Videos", "HomeVideos": "Tuis Videos",
"HeaderRecordingGroups": "Groep Opnames",
"Genres": "Genres", "Genres": "Genres",
"FailedLoginAttemptWithUserName": "Mislukte aanmeldpoging van {0}", "FailedLoginAttemptWithUserName": "Mislukte aanmeldpoging van {0}",
"ChapterNameValue": "Hoofstuk {0}", "ChapterNameValue": "Hoofstuk {0}",
"CameraImageUploadedFrom": "'n Nuwe kamera foto is opgelaai vanaf {0}",
"AuthenticationSucceededWithUserName": "{0} suksesvol geverifieer", "AuthenticationSucceededWithUserName": "{0} suksesvol geverifieer",
"Albums": "Albums",
"TasksChannelsCategory": "Internet kanale", "TasksChannelsCategory": "Internet kanale",
"TasksApplicationCategory": "aansoek", "TasksApplicationCategory": "aansoek",
"TasksLibraryCategory": "biblioteek", "TasksLibraryCategory": "biblioteek",
@@ -99,12 +128,12 @@
"TaskRefreshTrickplayImagesDescription": "Skep fopspeel voorskou vir videos in aangeskakelde media versameling.", "TaskRefreshTrickplayImagesDescription": "Skep fopspeel voorskou vir videos in aangeskakelde media versameling.",
"TaskAudioNormalizationDescription": "Skandeer lêers vir oudio-normaliseringsdata.", "TaskAudioNormalizationDescription": "Skandeer lêers vir oudio-normaliseringsdata.",
"TaskAudioNormalization": "Odio Normalisering", "TaskAudioNormalization": "Odio Normalisering",
"TaskCleanCollectionsAndPlaylists": "Maak versamelings en snitlyste skoon",
"TaskCleanCollectionsAndPlaylistsDescription": "Verwyder items uit versamelings en snitlyste wat nie meer bestaan nie.",
"TaskDownloadMissingLyrics": "Laai tekorte lirieke af", "TaskDownloadMissingLyrics": "Laai tekorte lirieke af",
"TaskDownloadMissingLyricsDescription": "Laai lirieke af vir liedjies", "TaskDownloadMissingLyricsDescription": "Laai lirieke af vir liedjies",
"TaskExtractMediaSegments": "Media Segment Skandeer", "TaskExtractMediaSegments": "Media Segment Skandeer",
"TaskExtractMediaSegmentsDescription": "Onttrek of verkry mediasegmente van MediaSegment-geaktiveerde inproppe.", "TaskExtractMediaSegmentsDescription": "Onttrek of verkry mediasegmente van MediaSegment-geaktiveerde inproppe.",
"TaskMoveTrickplayImages": "Migreer Trickplay Beeldligging", "TaskMoveTrickplayImages": "Migreer Trickplay Beeldligging",
"TaskMoveTrickplayImagesDescription": "Skuif ontstaande trickplay lêers volgens die biblioteekinstellings.", "TaskMoveTrickplayImagesDescription": "Skuif ontstaande trickplay lêers volgens die biblioteekinstellings."
"CleanupUserDataTask": "Gebruikers data skoon maak taak",
"CleanupUserDataTaskDescription": "Maak alle gebruikers data (kykstatus, gunstelingstatus, ens.) skoon van media wat nie meer vir ten minste 90 dae teenwoordig is nie."
} }

View File

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

View File

@@ -1,13 +1,18 @@
{ {
"Albums": "এলবাম",
"Application": "আবেদন",
"AppDeviceValues": "এপ্‌: {0}, ডিভাইচ: {1}", "AppDeviceValues": "এপ্‌: {0}, ডিভাইচ: {1}",
"Artists": "শিল্পী", "Artists": "শিল্পী",
"Channels": "চেনেলস",
"Default": "ডিফল্ট", "Default": "ডিফল্ট",
"AuthenticationSucceededWithUserName": "{0} সফলভাবে প্রমাণিত", "AuthenticationSucceededWithUserName": "{0} সফলভাবে প্রমাণিত",
"Books": "পুস্তক", "Books": "পুস্তক",
"Movies": "চলচ্চিত্ৰ", "Movies": "চলচ্চিত্ৰ",
"CameraImageUploadedFrom": "একটি নতুন ক্যামেরা চিত্র আপলোড করা হয়েছে {0}",
"Collections": "সংগ্রহ", "Collections": "সংগ্রহ",
"HeaderFavoriteShows": "প্রিয় শোসমূহ", "HeaderFavoriteShows": "প্রিয় শোসমূহ",
"Latest": "শেহতীয়া", "Latest": "শেহতীয়া",
"MessageApplicationUpdated": "জেলিফিন চাইভাৰ আপডেট কৰা হৈছে",
"MixedContent": "মিশ্ৰিত সমগ্ৰতা", "MixedContent": "মিশ্ৰিত সমগ্ৰতা",
"NewVersionIsAvailable": "ডাউনলোড কৰিবলৈ জেলিফিন চাইভাৰৰ এটা নতুন সংস্কৰণ উপলব্ধ আছে.", "NewVersionIsAvailable": "ডাউনলোড কৰিবলৈ জেলিফিন চাইভাৰৰ এটা নতুন সংস্কৰণ উপলব্ধ আছে.",
"NotificationOptionCameraImageUploaded": "কেমেৰাৰ চিত্ৰ আপল'ড কৰা হ'ল", "NotificationOptionCameraImageUploaded": "কেমেৰাৰ চিত্ৰ আপল'ড কৰা হ'ল",
@@ -16,14 +21,20 @@
"Folders": "ফোল্ডাৰ", "Folders": "ফোল্ডাৰ",
"Forced": "বলপূর্বক", "Forced": "বলপূর্বক",
"Genres": "শ্রেণী", "Genres": "শ্রেণী",
"HeaderAlbumArtists": "অ্যালবাম শিল্পী",
"HeaderContinueWatching": "দেখা চালিয়ে যান", "HeaderContinueWatching": "দেখা চালিয়ে যান",
"FailedLoginAttemptWithUserName": "লগইন ব্যর্থ চেষ্টা কৰা হৈছে থেকে {0}", "FailedLoginAttemptWithUserName": "লগইন ব্যর্থ চেষ্টা কৰা হৈছে থেকে {0}",
"HeaderFavoriteAlbums": "প্রিয় অ্যালবামসমূহ",
"HeaderFavoriteArtists": "প্রিয় শিল্পীসমূহ",
"HeaderFavoriteEpisodes": "প্রিয় পর্বসমূহ", "HeaderFavoriteEpisodes": "প্রিয় পর্বসমূহ",
"HeaderFavoriteSongs": "প্ৰিয় গীত",
"HeaderLiveTV": "প্ৰতিবেদন টিভি", "HeaderLiveTV": "প্ৰতিবেদন টিভি",
"HeaderNextUp": "পৰৱৰ্তী অংশ", "HeaderNextUp": "পৰৱৰ্তী অংশ",
"HeaderRecordingGroups": "অলংকৰণ গোষ্ঠীসমূহ",
"HearingImpaired": "শ্ৰবণ অক্ষম", "HearingImpaired": "শ্ৰবণ অক্ষম",
"HomeVideos": "ঘৰৰ ভিডিঅ'সমূহ", "HomeVideos": "ঘৰৰ ভিডিঅ'সমূহ",
"Inherit": "উত্তপ্ত কৰা", "Inherit": "উত্তপ্ত কৰা",
"MessageServerConfigurationUpdated": "চাইভাৰ কনফিগাৰেশ্যন আপডেট কৰা হৈছে",
"NotificationOptionApplicationUpdateAvailable": "অ্যাপ্লিকেশ্যন আপডেট উপলব্ধ", "NotificationOptionApplicationUpdateAvailable": "অ্যাপ্লিকেশ্যন আপডেট উপলব্ধ",
"NotificationOptionApplicationUpdateInstalled": "অ্যাপ্লিকেশ্যন আপডেট ইনষ্টল কৰা হ'ল", "NotificationOptionApplicationUpdateInstalled": "অ্যাপ্লিকেশ্যন আপডেট ইনষ্টল কৰা হ'ল",
"NotificationOptionAudioPlayback": "অডিঅ' প্লেবেক আৰম্ভ হ'ল", "NotificationOptionAudioPlayback": "অডিঅ' প্লেবেক আৰম্ভ হ'ল",

View File

@@ -1,24 +1,36 @@
{ {
"Sync": "Сінхранізаваць",
"Playlists": "Плэй-лісты",
"Latest": "Апошняе", "Latest": "Апошняе",
"LabelIpAddressValue": "IP-адрас: {0}", "LabelIpAddressValue": "IP-адрас: {0}",
"ItemAddedWithName": "{0} даданы ў бібліятэку",
"MessageApplicationUpdated": "Сервер Jellyfin абноўлены",
"NotificationOptionApplicationUpdateInstalled": "Абнаўленне праграмы ўсталявана", "NotificationOptionApplicationUpdateInstalled": "Абнаўленне праграмы ўсталявана",
"PluginInstalledWithName": "{0} быў усталяваны", "PluginInstalledWithName": "{0} быў усталяваны",
"UserCreatedWithName": "Карыстальнік {0} быў створаны", "UserCreatedWithName": "Карыстальнік {0} быў створаны",
"Albums": "Альбомы",
"Application": "Праграма",
"AuthenticationSucceededWithUserName": "{0} паспяхова аўтарызаваны", "AuthenticationSucceededWithUserName": "{0} паспяхова аўтарызаваны",
"Channels": "Каналы",
"ChapterNameValue": "Раздзел {0}", "ChapterNameValue": "Раздзел {0}",
"Collections": "Калекцыі", "Collections": "Калекцыі",
"Default": радвызначана", "Default": а змаўчанні",
"FailedLoginAttemptWithUserName": "Няўдалая спроба ўваходу з {0}", "FailedLoginAttemptWithUserName": "Няўдалая спроба ўваходу з {0}",
"Folders": "Папкі", "Folders": "Тэчкі",
"Favorites": "Абранае", "Favorites": "Абранае",
"External": "Знешні", "External": "Знешні",
"Genres": "Жанры", "Genres": "Жанры",
"HeaderContinueWatching": "Працягнуць прагляд", "HeaderContinueWatching": "Працягнуць прагляд",
"HeaderFavoriteAlbums": "Абраныя альбомы",
"HeaderFavoriteEpisodes": "Абраныя серыі", "HeaderFavoriteEpisodes": "Абраныя серыі",
"HeaderFavoriteShows": "Абраныя шоу", "HeaderFavoriteShows": "Абраныя шоу",
"HeaderFavoriteSongs": "Абраныя песні",
"HeaderLiveTV": "Прамы эфір", "HeaderLiveTV": "Прамы эфір",
"HeaderAlbumArtists": "Выканаўцы альбома",
"LabelRunningTimeValue": "Працягласць: {0}", "LabelRunningTimeValue": "Працягласць: {0}",
"HomeVideos": "Хатнія відэа", "HomeVideos": "Хатнія відэа",
"ItemRemovedWithName": "{0} выдалены з бібліятэкі",
"MessageApplicationUpdatedTo": "Сервер Jellyfin абноўлены да версіі {0}",
"Movies": "Фільмы", "Movies": "Фільмы",
"Music": "Музыка", "Music": "Музыка",
"MusicVideos": "Музычныя кліпы", "MusicVideos": "Музычныя кліпы",
@@ -29,35 +41,48 @@
"NotificationOptionPluginUpdateInstalled": "Абнаўленне плагіна ўсталявана", "NotificationOptionPluginUpdateInstalled": "Абнаўленне плагіна ўсталявана",
"NotificationOptionServerRestartRequired": "Патрабуецца перазапуск сервера", "NotificationOptionServerRestartRequired": "Патрабуецца перазапуск сервера",
"Photos": "Фотаздымкі", "Photos": "Фотаздымкі",
"Plugin": "Плагін",
"PluginUninstalledWithName": "{0} быў выдалены", "PluginUninstalledWithName": "{0} быў выдалены",
"PluginUpdatedWithName": "{0} быў абноўлены", "PluginUpdatedWithName": "{0} быў абноўлены",
"ProviderValue": "Пастаўшчык: {0}",
"Songs": "Песні",
"System": "Сістэма",
"User": "Карыстальнік",
"UserDeletedWithName": "Карыстальнік {0} быў выдалены", "UserDeletedWithName": "Карыстальнік {0} быў выдалены",
"UserDownloadingItemWithValues": "{0} спампоўваецца {1}", "UserDownloadingItemWithValues": "{0} спампоўваецца {1}",
"TaskOptimizeDatabase": "Аптымізацыя базы даных", "TaskOptimizeDatabase": "Аптымізаваць базу дадзеных",
"Artists": "Выканаўцы", "Artists": "Выканаўцы",
"UserOfflineFromDevice": "{0} адлучыўся ад {1}", "UserOfflineFromDevice": "{0} адлучыўся ад {1}",
"UserPolicyUpdatedWithName": "Палітыка карыстальніка абноўлена для {0}",
"TaskCleanActivityLogDescription": "Выдаляе запісы старэйшыя за зададзены ўзрост ў журнале актыўнасці.", "TaskCleanActivityLogDescription": "Выдаляе запісы старэйшыя за зададзены ўзрост ў журнале актыўнасці.",
"TaskRefreshChapterImagesDescription": "Стварае мініяцюры для відэа, якія маюць раздзелы.", "TaskRefreshChapterImagesDescription": "Стварае мініяцюры для відэа, якія маюць раздзелы.",
"TaskCleanLogsDescription": "Выдаляе файлы журналу, якім больш за {0} дзён.", "TaskCleanLogsDescription": "Выдаляе файлы журналу, якім больш за {0} дзён.",
"TaskUpdatePluginsDescription": "Спампоўвае і ўсталёўвае абнаўленні для плагінаў, якія сканфігураваныя на аўтаматычнае абнаўленне.", "TaskUpdatePluginsDescription": "Спампоўвае і ўсталёўвае абнаўленні для плагінаў, якія сканфігураваныя на аўтаматычнае абнаўленне.",
"TaskRefreshChannelsDescription": "Абнаўляе інфармацыю аб інтэрнэт-канале.", "TaskRefreshChannelsDescription": "Абнаўляе інфармацыю аб інтэрнэт-канале.",
"TaskDownloadMissingSubtitlesDescription": "Шукае ў інтэрнэце адсутныя субцітры на аснове канфігурацыі метаданых.", "TaskDownloadMissingSubtitlesDescription": "Шукае ў інтэрнэце адсутныя субцітры на аснове канфігурацыі метададзеных.",
"TaskOptimizeDatabaseDescription": "Сціскае базу даных і вызваляе вольную прастору. Выкананне гэтай задачы пасля сканіравання бібліятэкі або іншых змяненняў, якія мадыфікуюць базу даных, можа палепшыць прадукцыйнасць.", "TaskOptimizeDatabaseDescription": "Ушчыльняе базу дадзеных і скарачае вольную прастору. Выкананне гэтай задачы пасля сканавання бібліятэкі або ўнясення іншых зменаў, якія прадугледжваюць мадыфікацыю базы дадзеных, можа палепшыць выдайнасць.",
"TaskKeyframeExtractor": "Экстрактар ключавых кадраў", "TaskKeyframeExtractor": "Экстрактар ключавых кадраў",
"TasksApplicationCategory": "Праграма", "TasksApplicationCategory": "Праграма",
"AppDeviceValues": "Праграма: {0}, Прылада: {1}", "AppDeviceValues": "Праграма: {0}, Прылада: {1}",
"Books": "Кнігі", "Books": "Кнігі",
"CameraImageUploadedFrom": "Новая выява камеры была загружана з {0}",
"DeviceOfflineWithName": "{0} адлучыўся",
"DeviceOnlineWithName": "{0} падлучаны",
"Forced": "Прымусова", "Forced": "Прымусова",
"HeaderRecordingGroups": "Групы запісаў",
"HeaderNextUp": "Наступнае", "HeaderNextUp": "Наступнае",
"HeaderFavoriteArtists": "Абраныя выканаўцы",
"HearingImpaired": "Са слабым слыхам", "HearingImpaired": "Са слабым слыхам",
"Inherit": "Атрымаць у спадчыну", "Inherit": "Атрымаць у спадчыну",
"MessageNamedServerConfigurationUpdatedWithValue": "Канфігурацыя сервера (секцыя {0}) абноўлена",
"MessageServerConfigurationUpdated": "Канфігурацыя сервера абноўлена",
"MixedContent": "Змешаны змест", "MixedContent": "Змешаны змест",
"NameSeasonUnknown": "Невядомы сезон", "NameSeasonUnknown": "Невядомы сезон",
"NotificationOptionInstallationFailed": "Збой усталёўкі", "NotificationOptionInstallationFailed": "Збой усталёўкі",
"NewVersionIsAvailable": "Новая версія сервера Jellyfin даступная для cпампоўкі.", "NewVersionIsAvailable": "Новая версія сервера Jellyfin даступная для cпампоўкі.",
"NotificationOptionCameraImageUploaded": "Выява камеры запампавана", "NotificationOptionCameraImageUploaded": "Выява камеры запампавана",
"NotificationOptionAudioPlaybackStopped": "Прайграванне аўдыя спынена", "NotificationOptionAudioPlaybackStopped": "Прайграванне аўдыё спынена",
"NotificationOptionAudioPlayback": "Прайграванне аўдыя пачалося", "NotificationOptionAudioPlayback": "Прайграванне аўдыё пачалося",
"NotificationOptionNewLibraryContent": "Дададзены новы кантэнт", "NotificationOptionNewLibraryContent": "Дададзены новы кантэнт",
"NotificationOptionPluginError": "Збой плагіна", "NotificationOptionPluginError": "Збой плагіна",
"NotificationOptionPluginUninstalled": "Плагін выдалены", "NotificationOptionPluginUninstalled": "Плагін выдалены",
@@ -66,9 +91,11 @@
"NotificationOptionVideoPlayback": "Пачалося прайграванне відэа", "NotificationOptionVideoPlayback": "Пачалося прайграванне відэа",
"NotificationOptionVideoPlaybackStopped": "Прайграванне відэа спынена", "NotificationOptionVideoPlaybackStopped": "Прайграванне відэа спынена",
"ScheduledTaskFailedWithName": "{0} не атрымалася", "ScheduledTaskFailedWithName": "{0} не атрымалася",
"ScheduledTaskStartedWithName": "{0} пачалося",
"ServerNameNeedsToBeRestarted": "{0} патрабуе перазапуску",
"Shows": "Шоу", "Shows": "Шоу",
"StartupEmbyServerIsLoading": "Jellyfin Server загружаецца. Калі ласка, паўтарыце спробу крыху пазней.", "StartupEmbyServerIsLoading": "Jellyfin Server загружаецца. Калі ласка, паўтарыце спробу крыху пазней.",
"SubtitleDownloadFailureFromForItem": "Субцітры для {1} не ўдалося спампаваць з {0}", "SubtitleDownloadFailureFromForItem": "Не атрымалася спампаваць субтытры з {0} для {1}",
"TvShows": "Тэлепраграма", "TvShows": "Тэлепраграма",
"Undefined": "Нявызначана", "Undefined": "Нявызначана",
"UserLockedOutWithName": "Карыстальнік {0} быў заблакіраваны", "UserLockedOutWithName": "Карыстальнік {0} быў заблакіраваны",
@@ -76,6 +103,8 @@
"UserPasswordChangedWithName": "Пароль быў зменены для карыстальніка {0}", "UserPasswordChangedWithName": "Пароль быў зменены для карыстальніка {0}",
"UserStartedPlayingItemWithValues": "{0} прайграваецца {1} на {2}", "UserStartedPlayingItemWithValues": "{0} прайграваецца {1} на {2}",
"UserStoppedPlayingItemWithValues": "{0} скончыў прайграванне {1} на {2}", "UserStoppedPlayingItemWithValues": "{0} скончыў прайграванне {1} на {2}",
"ValueHasBeenAddedToLibrary": "{0} быў дададзены ў вашу медыятэку",
"ValueSpecialEpisodeName": "Спецэпізод - {0}",
"VersionNumber": "Версія {0}", "VersionNumber": "Версія {0}",
"TasksMaintenanceCategory": "Абслугоўванне", "TasksMaintenanceCategory": "Абслугоўванне",
"TasksLibraryCategory": "Бібліятэка", "TasksLibraryCategory": "Бібліятэка",
@@ -85,7 +114,7 @@
"TaskCleanCacheDescription": "Выдаляе файлы кэша, якія больш не патрэбныя сістэме.", "TaskCleanCacheDescription": "Выдаляе файлы кэша, якія больш не патрэбныя сістэме.",
"TaskRefreshChapterImages": "Вынуць выявы раздзелаў", "TaskRefreshChapterImages": "Вынуць выявы раздзелаў",
"TaskRefreshLibrary": "Сканаваць бібліятэку", "TaskRefreshLibrary": "Сканаваць бібліятэку",
"TaskRefreshLibraryDescription": "Сканіруе вашу медыятэку на наяўнасць новых файлаў і абнаўляе метаданыя.", "TaskRefreshLibraryDescription": "Скануе вашу медыятэку на наяўнасць новых файлаў і абнаўляе метададзеныя.",
"TaskCleanLogs": "Ачысціць журнал", "TaskCleanLogs": "Ачысціць журнал",
"TaskRefreshPeople": "Абнавіць выканаўцаў", "TaskRefreshPeople": "Абнавіць выканаўцаў",
"TaskRefreshPeopleDescription": "Абнаўленне метаданых для акцёраў і рэжысёраў у вашай медыятэцы.", "TaskRefreshPeopleDescription": "Абнаўленне метаданых для акцёраў і рэжысёраў у вашай медыятэцы.",
@@ -94,9 +123,11 @@
"TaskCleanTranscodeDescription": "Выдаляе перакадзіраваныя файлы, старэйшыя за адзін дзень.", "TaskCleanTranscodeDescription": "Выдаляе перакадзіраваныя файлы, старэйшыя за адзін дзень.",
"TaskRefreshChannels": "Абнавіць каналы", "TaskRefreshChannels": "Абнавіць каналы",
"TaskDownloadMissingSubtitles": "Спампаваць адсутныя субцітры", "TaskDownloadMissingSubtitles": "Спампаваць адсутныя субцітры",
"TaskKeyframeExtractorDescription": "Выдае ключавыя кадры з відэафайлаў для стварэння больш дакладных плэй-лістоў HLS. Гэта задача можа выконвацца доўга.", "TaskKeyframeExtractorDescription": "Выдае ключавыя кадры з відэафайлаў для стварэння больш дакладных плэй-лістоў HLS. Гэта задача можа працягнуцца шмат часу.",
"TaskRefreshTrickplayImages": "Стварыць выявы Trickplay", "TaskRefreshTrickplayImages": "Стварыць выявы Trickplay",
"TaskRefreshTrickplayImagesDescription": "Стварае перадпрагляды відэаролікаў для Trickplay у падключаных бібліятэках.", "TaskRefreshTrickplayImagesDescription": "Стварае перадпрагляды відэаролікаў для Trickplay у падключаных бібліятэках.",
"TaskCleanCollectionsAndPlaylists": "Ачысціце калекцыі і плэй-лісты",
"TaskCleanCollectionsAndPlaylistsDescription": "Выдаляе элементы з калекцый і плэй-лістоў, якія больш не існуюць.",
"TaskAudioNormalizationDescription": "Скануе файлы на прадмет нармалізацыі гуку.", "TaskAudioNormalizationDescription": "Скануе файлы на прадмет нармалізацыі гуку.",
"TaskAudioNormalization": "Нармалізацыя гуку", "TaskAudioNormalization": "Нармалізацыя гуку",
"TaskExtractMediaSegmentsDescription": "Выдае або атрымлівае медыясегменты з убудоў з падтрымкай MediaSegment.", "TaskExtractMediaSegmentsDescription": "Выдае або атрымлівае медыясегменты з убудоў з падтрымкай MediaSegment.",
@@ -105,6 +136,6 @@
"TaskDownloadMissingLyricsDescription": "Спампоўвае тэксты для песняў", "TaskDownloadMissingLyricsDescription": "Спампоўвае тэксты для песняў",
"TaskExtractMediaSegments": "Сканіраванне медыя-сегмента", "TaskExtractMediaSegments": "Сканіраванне медыя-сегмента",
"TaskMoveTrickplayImages": "Перанесці месцазнаходжанне выявы Trickplay", "TaskMoveTrickplayImages": "Перанесці месцазнаходжанне выявы Trickplay",
"CleanupUserDataTask": "Задача па ачыстцы даных карыстальніка", "CleanupUserDataTask": "Задача па ачыстцы дадзеных карыстальніка",
"CleanupUserDataTaskDescription": "Ачышчае ўсе даныя карыстальніка (стан прагляду, абранае і г.д.) для медыяфайлаў, што адсутнічаюць больш за 90 дзён." "CleanupUserDataTaskDescription": "Ачысьціць усе дадзеныя карыстальніка (стан прагляду, абранае і г.д.) для медыяфайлаў, што адсутнічаюць больш за 90 дзён."
} }

View File

@@ -1,24 +1,41 @@
{ {
"Albums": "Албуми",
"AppDeviceValues": "Програма: {0}, Устройство: {1}", "AppDeviceValues": "Програма: {0}, Устройство: {1}",
"Application": "Програма",
"Artists": "Артисти", "Artists": "Артисти",
"AuthenticationSucceededWithUserName": "{0} се удостовери успешно", "AuthenticationSucceededWithUserName": "{0} се удостовери успешно",
"Books": "Книги", "Books": "Книги",
"CameraImageUploadedFrom": "Нова снимка от камера беше качена от {0}",
"Channels": "Канали",
"ChapterNameValue": "Глава {0}", "ChapterNameValue": "Глава {0}",
"Collections": "Колекции", "Collections": "Колекции",
"DeviceOfflineWithName": "{0} се разкачи",
"DeviceOnlineWithName": "{0} е свързан",
"FailedLoginAttemptWithUserName": "Неуспешен опит за влизане от {0}", "FailedLoginAttemptWithUserName": "Неуспешен опит за влизане от {0}",
"Favorites": "Любими", "Favorites": "Любими",
"Folders": "Папки", "Folders": "Папки",
"Genres": "Жанрове", "Genres": "Жанрове",
"HeaderAlbumArtists": "Изпълнители на албуми",
"HeaderContinueWatching": "Продължаване на гледането", "HeaderContinueWatching": "Продължаване на гледането",
"HeaderFavoriteAlbums": "Любими албуми",
"HeaderFavoriteArtists": "Любими изпълнители",
"HeaderFavoriteEpisodes": "Любими епизоди", "HeaderFavoriteEpisodes": "Любими епизоди",
"HeaderFavoriteShows": "Любими сериали", "HeaderFavoriteShows": "Любими сериали",
"HeaderFavoriteSongs": "Любими песни",
"HeaderLiveTV": "Телевизия на живо", "HeaderLiveTV": "Телевизия на живо",
"HeaderNextUp": "Следва", "HeaderNextUp": "Следва",
"HeaderRecordingGroups": "Запис групи",
"HomeVideos": "Домашни Клипове", "HomeVideos": "Домашни Клипове",
"Inherit": "Наследяване", "Inherit": "Наследяване",
"ItemAddedWithName": "{0} е добавено към библиотеката",
"ItemRemovedWithName": "{0} е премахнато от библиотеката",
"LabelIpAddressValue": "IP адрес: {0}", "LabelIpAddressValue": "IP адрес: {0}",
"LabelRunningTimeValue": "Продължителност: {0}", "LabelRunningTimeValue": "Продължителност: {0}",
"Latest": "Последни", "Latest": "Последни",
"MessageApplicationUpdated": "Сървърът беше обновен",
"MessageApplicationUpdatedTo": "Сървърът беше обновен до {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Секцията {0} от сървърната конфигурация беше актуализирана",
"MessageServerConfigurationUpdated": "Конфигурацията на сървъра беше актуализирана",
"MixedContent": "Смесено съдържание", "MixedContent": "Смесено съдържание",
"Movies": "Филми", "Movies": "Филми",
"Music": "Музика", "Music": "Музика",
@@ -44,14 +61,24 @@
"NotificationOptionVideoPlayback": "Възпроизвеждането на видео започна", "NotificationOptionVideoPlayback": "Възпроизвеждането на видео започна",
"NotificationOptionVideoPlaybackStopped": "Възпроизвеждането на видео е спряно", "NotificationOptionVideoPlaybackStopped": "Възпроизвеждането на видео е спряно",
"Photos": "Снимки", "Photos": "Снимки",
"Playlists": "Списъци",
"Plugin": "Добавка",
"PluginInstalledWithName": "{0} е инсталиранa", "PluginInstalledWithName": "{0} е инсталиранa",
"PluginUninstalledWithName": "{0} е деинсталиранa", "PluginUninstalledWithName": "{0} е деинсталиранa",
"PluginUpdatedWithName": "{0} е обновенa", "PluginUpdatedWithName": "{0} е обновенa",
"ProviderValue": "Доставчик: {0}",
"ScheduledTaskFailedWithName": "{0} се провали", "ScheduledTaskFailedWithName": "{0} се провали",
"ScheduledTaskStartedWithName": "{0} започна",
"ServerNameNeedsToBeRestarted": "{0} трябва да се рестартира",
"Shows": "Сериали", "Shows": "Сериали",
"Songs": "Песни",
"StartupEmbyServerIsLoading": "Сървърът зарежда. Моля, опитайте отново след малко.", "StartupEmbyServerIsLoading": "Сървърът зарежда. Моля, опитайте отново след малко.",
"SubtitleDownloadFailureForItem": "Неуспешно изтегляне на субтитри за {0}",
"SubtitleDownloadFailureFromForItem": "Субтитрите за {1} от {0} не можаха да бъдат изтеглени", "SubtitleDownloadFailureFromForItem": "Субтитрите за {1} от {0} не можаха да бъдат изтеглени",
"Sync": "Синхронизиране",
"System": "Система",
"TvShows": "Телевизионни сериали", "TvShows": "Телевизионни сериали",
"User": "Потребител",
"UserCreatedWithName": "Потребителят {0} е създаден", "UserCreatedWithName": "Потребителят {0} е създаден",
"UserDeletedWithName": "Потребителят {0} е изтрит", "UserDeletedWithName": "Потребителят {0} е изтрит",
"UserDownloadingItemWithValues": "{0} изтегля {1}", "UserDownloadingItemWithValues": "{0} изтегля {1}",
@@ -59,8 +86,11 @@
"UserOfflineFromDevice": "{0} се разкачи от {1}", "UserOfflineFromDevice": "{0} се разкачи от {1}",
"UserOnlineFromDevice": "{0} е на линия от {1}", "UserOnlineFromDevice": "{0} е на линия от {1}",
"UserPasswordChangedWithName": "Паролата на потребителя {0} е променена", "UserPasswordChangedWithName": "Паролата на потребителя {0} е променена",
"UserPolicyUpdatedWithName": "Потребителската политика за {0} се актуализира",
"UserStartedPlayingItemWithValues": "{0} пусна {1}", "UserStartedPlayingItemWithValues": "{0} пусна {1}",
"UserStoppedPlayingItemWithValues": "{0} спря {1}", "UserStoppedPlayingItemWithValues": "{0} спря {1}",
"ValueHasBeenAddedToLibrary": "{0} беше добавен във Вашата библиотека",
"ValueSpecialEpisodeName": "Специални - {0}",
"VersionNumber": "Версия {0}", "VersionNumber": "Версия {0}",
"TaskDownloadMissingSubtitlesDescription": "Търси Интернет за липсващи субтитри, на база конфигурацията за мета-данни.", "TaskDownloadMissingSubtitlesDescription": "Търси Интернет за липсващи субтитри, на база конфигурацията за мета-данни.",
"TaskDownloadMissingSubtitles": "Изтегляне на липсващи субтитри", "TaskDownloadMissingSubtitles": "Изтегляне на липсващи субтитри",
@@ -99,6 +129,8 @@
"TaskRefreshTrickplayImagesDescription": "Създава прегледи на Trickplay за видеа в активирани библиотеки.", "TaskRefreshTrickplayImagesDescription": "Създава прегледи на Trickplay за видеа в активирани библиотеки.",
"TaskDownloadMissingLyrics": "Свали липсващи текстове", "TaskDownloadMissingLyrics": "Свали липсващи текстове",
"TaskDownloadMissingLyricsDescription": "Свали текстове за песни", "TaskDownloadMissingLyricsDescription": "Свали текстове за песни",
"TaskCleanCollectionsAndPlaylists": "Изчисти колекциите и плейлистите",
"TaskCleanCollectionsAndPlaylistsDescription": "Премахни несъществуващи файлове в колекциите и плейлистите.",
"TaskAudioNormalization": "Нормализиране на звука", "TaskAudioNormalization": "Нормализиране на звука",
"TaskAudioNormalizationDescription": "Сканирай файловете за нормализация на звука.", "TaskAudioNormalizationDescription": "Сканирай файловете за нормализация на звука.",
"TaskExtractMediaSegmentsDescription": "Изважда медиини сегменти от MediaSegment плъгини.", "TaskExtractMediaSegmentsDescription": "Изважда медиини сегменти от MediaSegment плъгини.",

View File

@@ -1,19 +1,31 @@
{ {
"DeviceOnlineWithName": "{0}-এর সাথে সংযুক্ত হয়েছে",
"DeviceOfflineWithName": "{0}-এর সাথে সংযোগ বিচ্ছিন্ন হয়েছে",
"Collections": "সংগ্রহশালা", "Collections": "সংগ্রহশালা",
"ChapterNameValue": "অধ্যায় {0}", "ChapterNameValue": "অধ্যায় {0}",
"Channels": "চ্যানেলসমূহ",
"CameraImageUploadedFrom": "{0} থেকে একটি নতুন ক্যামেরার চিত্র আপলোড করা হয়েছে",
"Books": "পুস্তকসমূহ", "Books": "পুস্তকসমূহ",
"AuthenticationSucceededWithUserName": "{0} সফলভাবে অথেন্টিকেট করেছেন", "AuthenticationSucceededWithUserName": "{0} সফলভাবে অথেন্টিকেট করেছেন",
"Artists": "শিল্পীগণ", "Artists": "শিল্পীগণ",
"Application": "অ্যাপ্লিকেশন",
"Albums": "অ্যালবামসমূহ",
"HeaderFavoriteEpisodes": "প্রিয় পর্বগুলো", "HeaderFavoriteEpisodes": "প্রিয় পর্বগুলো",
"HeaderFavoriteArtists": "প্রিয় শিল্পীরা",
"HeaderFavoriteAlbums": "প্রিয় এলবামগুলো",
"HeaderContinueWatching": "দেখতে থাকুন", "HeaderContinueWatching": "দেখতে থাকুন",
"HeaderAlbumArtists": "অ্যালবাম শিল্পীবৃন্দ",
"Genres": "ধরণ", "Genres": "ধরণ",
"Folders": "ফোল্ডারসমূহ", "Folders": "ফোল্ডারসমূহ",
"Favorites": "পছন্দসমূহ", "Favorites": "পছন্দসমূহ",
"FailedLoginAttemptWithUserName": "{0} লগিন করতে ব্যর্থ হয়েছে", "FailedLoginAttemptWithUserName": "{0} লগিন করতে ব্যর্থ হয়েছে",
"AppDeviceValues": "অ্যাপ: {0}, ডিভাইস: {1}", "AppDeviceValues": "অ্যাপ: {0}, ডিভাইস: {1}",
"VersionNumber": "সংস্করণ {0}", "VersionNumber": "সংস্করণ {0}",
"ValueSpecialEpisodeName": "বিশেষ পর্ব - {0}",
"ValueHasBeenAddedToLibrary": "আপনার লাইব্রেরিতে {0} যোগ করা হয়েছে",
"UserStoppedPlayingItemWithValues": "{2}তে {1} প্লে শেষ করেছেন {0}", "UserStoppedPlayingItemWithValues": "{2}তে {1} প্লে শেষ করেছেন {0}",
"UserStartedPlayingItemWithValues": "{2}তে {1} প্লে করেছেন {0}", "UserStartedPlayingItemWithValues": "{2}তে {1} প্লে করেছেন {0}",
"UserPolicyUpdatedWithName": "{0} এর জন্য ব্যবহার নীতি আপডেট করা হয়েছে",
"UserPasswordChangedWithName": "ব্যবহারকারী {0} এর পাসওয়ার্ড পরিবর্তিত হয়েছে", "UserPasswordChangedWithName": "ব্যবহারকারী {0} এর পাসওয়ার্ড পরিবর্তিত হয়েছে",
"UserOnlineFromDevice": "{0}, {1} থেকে অনলাইন আছে", "UserOnlineFromDevice": "{0}, {1} থেকে অনলাইন আছে",
"UserOfflineFromDevice": "{0} {1} থেকে বিচ্ছিন্ন হয়ে গেছে", "UserOfflineFromDevice": "{0} {1} থেকে বিচ্ছিন্ন হয়ে গেছে",
@@ -21,14 +33,23 @@
"UserDownloadingItemWithValues": "{0}, {1} ডাউনলোড করছে", "UserDownloadingItemWithValues": "{0}, {1} ডাউনলোড করছে",
"UserDeletedWithName": "ব্যবহারকারী {0}কে বাদ দেয়া হয়েছে", "UserDeletedWithName": "ব্যবহারকারী {0}কে বাদ দেয়া হয়েছে",
"UserCreatedWithName": "ব্যবহারকারী {0} সৃষ্টি করা হয়েছে", "UserCreatedWithName": "ব্যবহারকারী {0} সৃষ্টি করা হয়েছে",
"User": "ব্যবহারকারী",
"TvShows": "টিভি শোগুলো", "TvShows": "টিভি শোগুলো",
"System": "সিস্টেম",
"Sync": "সমন্বয় করুন",
"SubtitleDownloadFailureFromForItem": "{0} থেকে {1} এর জন্য সাবটাইটেল ডাউনলোড ব্যর্থ হয়েছে", "SubtitleDownloadFailureFromForItem": "{0} থেকে {1} এর জন্য সাবটাইটেল ডাউনলোড ব্যর্থ হয়েছে",
"StartupEmbyServerIsLoading": "জেলিফিন সার্ভার লোড হচ্ছে। দয়া করে একটু পরে আবার চেষ্টা করুন।", "StartupEmbyServerIsLoading": "জেলিফিন সার্ভার লোড হচ্ছে। দয়া করে একটু পরে আবার চেষ্টা করুন।",
"Songs": "সঙ্গীত সমূহ",
"Shows": "শো সমূহ", "Shows": "শো সমূহ",
"ServerNameNeedsToBeRestarted": "{0} রিস্টার্ট করা প্রয়োজন",
"ScheduledTaskStartedWithName": "{0} শুরু হয়েছে",
"ScheduledTaskFailedWithName": "{0} ব্যর্থ", "ScheduledTaskFailedWithName": "{0} ব্যর্থ",
"ProviderValue": "প্রদানকারী: {0}",
"PluginUpdatedWithName": "{0} আপডেট করা হয়েছে", "PluginUpdatedWithName": "{0} আপডেট করা হয়েছে",
"PluginUninstalledWithName": "{0} আনইন্সটল হয়েছে", "PluginUninstalledWithName": "{0} আনইন্সটল হয়েছে",
"PluginInstalledWithName": "{0} ইন্সটল হয়েছে", "PluginInstalledWithName": "{0} ইন্সটল হয়েছে",
"Plugin": "প্লাগিন",
"Playlists": "প্লে লিস্ট সমূহ",
"Photos": "ছবিসমূহ", "Photos": "ছবিসমূহ",
"NotificationOptionVideoPlaybackStopped": "ভিডিও প্লেব্যাক বন্ধ হয়েছে", "NotificationOptionVideoPlaybackStopped": "ভিডিও প্লেব্যাক বন্ধ হয়েছে",
"NotificationOptionVideoPlayback": "ভিডিও প্লেব্যাক শুরু হয়েছে", "NotificationOptionVideoPlayback": "ভিডিও প্লেব্যাক শুরু হয়েছে",
@@ -54,13 +75,21 @@
"Music": "গান", "Music": "গান",
"Movies": "চলচ্চিত্রসমূহ", "Movies": "চলচ্চিত্রসমূহ",
"MixedContent": "মিশ্র কন্টেন্ট", "MixedContent": "মিশ্র কন্টেন্ট",
"MessageServerConfigurationUpdated": "সার্ভারের কনফিগারেশন আপডেট করা হয়েছে",
"HeaderRecordingGroups": "রেকর্ডিং গ্রুপগুলো",
"MessageNamedServerConfigurationUpdatedWithValue": "সার্ভার কনফিগারেশন সেকশন {0} আপডেট করা হয়েছে",
"MessageApplicationUpdatedTo": "জেলিফিন সার্ভার {0} তে আপডেট করা হয়েছে",
"MessageApplicationUpdated": "জেলিফিন সার্ভার আপডেট করা হয়েছে",
"Latest": "সর্বশেষ", "Latest": "সর্বশেষ",
"LabelRunningTimeValue": "চলার সময়: {0}", "LabelRunningTimeValue": "চলার সময়: {0}",
"LabelIpAddressValue": "আইপি এড্রেস: {0}", "LabelIpAddressValue": "আইপি এড্রেস: {0}",
"ItemRemovedWithName": "{0} লাইব্রেরি থেকে বাদ দেয়া হয়েছে",
"ItemAddedWithName": "{0} লাইব্রেরিতে যোগ করা হয়েছে",
"Inherit": "উত্তরাধিকারসূত্র থেকে গ্রহণ করুন", "Inherit": "উত্তরাধিকারসূত্র থেকে গ্রহণ করুন",
"HomeVideos": "হোম ভিডিও", "HomeVideos": "হোম ভিডিও",
"HeaderNextUp": "এরপরে আসছে", "HeaderNextUp": "এরপরে আসছে",
"HeaderLiveTV": "লাইভ টিভি", "HeaderLiveTV": "লাইভ টিভি",
"HeaderFavoriteSongs": "প্রিয় গানগুলো",
"HeaderFavoriteShows": "প্রিয় শোগুলো", "HeaderFavoriteShows": "প্রিয় শোগুলো",
"TasksLibraryCategory": "লাইব্রেরি", "TasksLibraryCategory": "লাইব্রেরি",
"TasksMaintenanceCategory": "রক্ষণাবেক্ষণ", "TasksMaintenanceCategory": "রক্ষণাবেক্ষণ",
@@ -98,6 +127,8 @@
"TaskRefreshTrickplayImages": "ট্রিকপ্লে ইমেজ তৈরি", "TaskRefreshTrickplayImages": "ট্রিকপ্লে ইমেজ তৈরি",
"TaskRefreshTrickplayImagesDescription": "সক্ষম লাইব্রেরিতে ভিডিওর জন্য ট্রিকপ্লে প্রিভিউ তৈরি করে।", "TaskRefreshTrickplayImagesDescription": "সক্ষম লাইব্রেরিতে ভিডিওর জন্য ট্রিকপ্লে প্রিভিউ তৈরি করে।",
"TaskDownloadMissingLyricsDescription": "গানের জন্য লিরিকস ডাউনলোড করুন", "TaskDownloadMissingLyricsDescription": "গানের জন্য লিরিকস ডাউনলোড করুন",
"TaskCleanCollectionsAndPlaylists": "কালেকশন এবং প্লেলিস্ট পরিষ্কার করুন",
"TaskCleanCollectionsAndPlaylistsDescription": "কালেকশন এবং প্লেলিস্ট থেকে আইটেমগুলি সরিয়ে দেয় যা আর বিদ্যমান নেই।",
"TaskExtractMediaSegments": "মিডিয়া সেগমেন্ট স্ক্যান", "TaskExtractMediaSegments": "মিডিয়া সেগমেন্ট স্ক্যান",
"TaskExtractMediaSegmentsDescription": "মিডিয়া সেগমেন্ট সক্ষম প্লাগইনগুলি থেকে মিডিয়া সেগমেন্ট বের করে বা অর্জন করে।", "TaskExtractMediaSegmentsDescription": "মিডিয়া সেগমেন্ট সক্ষম প্লাগইনগুলি থেকে মিডিয়া সেগমেন্ট বের করে বা অর্জন করে।",
"TaskDownloadMissingLyrics": "অনুপস্থিত গান ডাউনলোড করুন", "TaskDownloadMissingLyrics": "অনুপস্থিত গান ডাউনলোড করুন",

View File

@@ -1,110 +0,0 @@
{
"Artists": "Umjetnici",
"Books": "Knjige",
"Collections": "Zbirke",
"Default": "Zadano",
"Favorites": "Omiljeni",
"Folders": "Mape",
"Genres": "Žanrovi",
"HeaderContinueWatching": "Nastavi gledati",
"Movies": "Filmovi",
"MusicVideos": "Muzički spotovi",
"Photos": "Slike",
"Shows": "Pokazuje",
"AppDeviceValues": "Aplikacija: {0}, Uređaj: {1}",
"AuthenticationSucceededWithUserName": "{0} uspješno autentificirano",
"ChapterNameValue": "Poglavlje {0}",
"External": "Vanjsko",
"FailedLoginAttemptWithUserName": "Neuspjeli pokušaj prijave sa {0}",
"Forced": "Prisilno",
"HeaderFavoriteEpisodes": "Omiljene epizode",
"HeaderFavoriteShows": "Omiljene emisije",
"HeaderLiveTV": "TV uživo",
"HeaderNextUp": "Slijedi",
"HearingImpaired": "Oštećen sluh",
"HomeVideos": "Kućni videozapisi",
"Inherit": "Nasljedi",
"LabelIpAddressValue": "IP adresa: {0}",
"LabelRunningTimeValue": "Trajanje: {0}",
"Latest": "Posljednje dodano",
"MixedContent": "Miješani sadržaj",
"Music": "Muzika",
"NameInstallFailed": "{0} instalacija je propala",
"NameSeasonNumber": "Sezona {0}",
"NameSeasonUnknown": "Sezona nepoznata",
"NewVersionIsAvailable": "Dostupna je nova verzija Jellyfin Servera za preuzimanje.",
"NotificationOptionApplicationUpdateAvailable": "Dostupno ažuriranje aplikacije",
"NotificationOptionApplicationUpdateInstalled": "Ažuriranje aplikacije instalirano",
"NotificationOptionAudioPlayback": "Pokrenuto je reproduciranje zvuka",
"NotificationOptionAudioPlaybackStopped": "Zaustavljeno je reproduciranje zvuka",
"NotificationOptionCameraImageUploaded": "Učitana slika s kamere",
"NotificationOptionInstallationFailed": "Neuspjeh instalacije",
"NotificationOptionNewLibraryContent": "Dodan novi sadržaj",
"NotificationOptionPluginError": "Neuspjeh dodatka",
"NotificationOptionPluginInstalled": "Dodatak je instaliran",
"NotificationOptionPluginUninstalled": "Dodatak je deinstaliran",
"NotificationOptionPluginUpdateInstalled": "Ažuriranje dodatka je instalirano",
"NotificationOptionServerRestartRequired": "Potreban je ponovni pokret servera",
"NotificationOptionTaskFailed": "Neuspjeh zakazane zadatke",
"NotificationOptionUserLockedOut": "Korisnik je zaključan",
"NotificationOptionVideoPlayback": "Pokrenuto je reproduciranje videa",
"NotificationOptionVideoPlaybackStopped": "Reprodukcija videa je zaustavljena",
"PluginInstalledWithName": "{0} je instaliran",
"PluginUninstalledWithName": "{0} je deinstaliran",
"PluginUpdatedWithName": "{0} je ažurirano",
"ScheduledTaskFailedWithName": "{0} nije uspjelo",
"StartupEmbyServerIsLoading": "Jellyfin Server se učitava. Molimo pokušajte ponovo za kratko vrijeme.",
"SubtitleDownloadFailureFromForItem": "Podtitlovi nisu uspjeli preuzeti sa {0} za {1}",
"TvShows": "TV serije",
"Undefined": "Nedefinirano",
"UserCreatedWithName": "Korisnik {0} je kreiran",
"UserDeletedWithName": "Korisnik {0} je izbrisan",
"UserDownloadingItemWithValues": "{0} preuzima {1}",
"UserLockedOutWithName": "Korisnik {0} je zaključan",
"UserOfflineFromDevice": "{0} se odspojio od {1}",
"UserOnlineFromDevice": "{0} je online od {1}",
"UserPasswordChangedWithName": "Lozinka je promijenjena za korisnika {0}",
"UserStartedPlayingItemWithValues": "{0} igra protiv {1} na {2}",
"UserStoppedPlayingItemWithValues": "{0} je završio igru protiv {1} na {2}",
"VersionNumber": "Verzija {0}",
"TasksMaintenanceCategory": "Održavanje",
"TasksLibraryCategory": "Biblioteka",
"TasksApplicationCategory": "Prijava",
"TasksChannelsCategory": "Internetski kanali",
"TaskCleanActivityLog": "Očisti dnevnik aktivnosti",
"TaskCleanActivityLogDescription": "Brisanje unosa u dnevnik aktivnosti starijih od konfigurisane starosti.",
"TaskCleanCache": "Očistite direktorij keša",
"TaskCleanCacheDescription": "Brisanje keš datoteka koje sistemu više nisu potrebne.",
"TaskRefreshChapterImages": "Izvadi slike iz poglavlja",
"TaskRefreshChapterImagesDescription": "Stvara minijature za videozapise koji imaju poglavlja.",
"TaskAudioNormalization": "Normalizacija zvuka",
"TaskAudioNormalizationDescription": "Skeneriše datoteke radi podataka za normalizaciju zvuka.",
"TaskRefreshLibrary": "Skenerisati medijsku biblioteku",
"TaskRefreshLibraryDescription": "Skenerira vašu medijsku biblioteku na nove datoteke i osvježava metapodatke.",
"TaskCleanLogs": "Očisti direktorij dnevnika",
"TaskCleanLogsDescription": "Brisanje dnevničkih datoteka starijih od {0} dana.",
"TaskRefreshPeople": "Osvježite ljude",
"TaskRefreshPeopleDescription": "Ažurira metapodatke za glumce i režisere u vašoj medijskoj biblioteci.",
"TaskRefreshTrickplayImages": "Generirajte Trickplay slike",
"TaskRefreshTrickplayImagesDescription": "Stvara pregled trik-igara za videozapise u omogućenim bibliotekama.",
"TaskUpdatePlugins": "Ažuriraj dodatke",
"TaskUpdatePluginsDescription": "Preuzima i instalira ažuriranja dodataka koji su konfigurisani da se automatski ažuriraju.",
"TaskCleanTranscode": "Očisti Transcode direktorij",
"TaskCleanTranscodeDescription": "Brisanje transkodiranih datoteka starijih od jednog dana.",
"TaskRefreshChannels": "Osvježi kanale",
"TaskRefreshChannelsDescription": "Osvježava informacije o internetskom kanalu.",
"TaskDownloadMissingLyrics": "Preuzmi nedostajuće tekstove",
"TaskDownloadMissingLyricsDescription": "Preuzmi tekstove pjesama",
"TaskDownloadMissingSubtitles": "Preuzmite nedostajuće titlove",
"TaskDownloadMissingSubtitlesDescription": "Pretražuje internet u potrazi za nedostajućim titlovima na osnovu konfiguracije metapodataka.",
"TaskOptimizeDatabase": "Optimizirajte bazu podataka",
"TaskOptimizeDatabaseDescription": "Komprimira bazu podataka i čisti slobodan prostor. Pokretanje ovog zadatka nakon skeniranja biblioteke ili izvođenja drugih promjena koje podrazumijevaju izmjene baze podataka može poboljšati performanse.",
"TaskKeyframeExtractor": "Izvađač ključnih sličica",
"TaskKeyframeExtractorDescription": "Izvlači ključne okvire iz video datoteka kako bi kreirao preciznije HLS playliste. Ovaj zadatak može trajati dugo.",
"TaskExtractMediaSegments": "Analiza medijskog segmenta",
"TaskExtractMediaSegmentsDescription": "Izvlači ili dobija medijske segmente iz dodataka koji podržavaju MediaSegment.",
"TaskMoveTrickplayImages": "Migracija lokacije slike Trickplay",
"TaskMoveTrickplayImagesDescription": "Premješta postojeće datoteke trik-igara prema postavkama biblioteke.",
"CleanupUserDataTask": "Zadatak čišćenja korisničkih podataka",
"CleanupUserDataTaskDescription": "Čisti sve korisničke podatke (stanje praćenja, status omiljenog itd.) sa medija koji više nije prisutan najmanje 90 dana."
}

View File

@@ -1,24 +1,41 @@
{ {
"Albums": "Àlbums",
"AppDeviceValues": "Aplicació: {0}, Dispositiu: {1}", "AppDeviceValues": "Aplicació: {0}, Dispositiu: {1}",
"Application": "Aplicació",
"Artists": "Artistes", "Artists": "Artistes",
"AuthenticationSucceededWithUserName": "{0} s'ha autenticat correctament", "AuthenticationSucceededWithUserName": "{0} s'ha autenticat correctament",
"Books": "Llibres", "Books": "Llibres",
"CameraImageUploadedFrom": "S'ha pujat una nova imatge de càmera des de {0}",
"Channels": "Canals",
"ChapterNameValue": "Capítol {0}", "ChapterNameValue": "Capítol {0}",
"Collections": "Col·leccions", "Collections": "Col·leccions",
"DeviceOfflineWithName": "{0} s'ha desconnectat",
"DeviceOnlineWithName": "{0} està connectat",
"FailedLoginAttemptWithUserName": "Intent de connexió fallit des de {0}", "FailedLoginAttemptWithUserName": "Intent de connexió fallit des de {0}",
"Favorites": "Preferits", "Favorites": "Preferits",
"Folders": "Directoris", "Folders": "Directoris",
"Genres": "Gèneres", "Genres": "Gèneres",
"HeaderAlbumArtists": "Artistes de l'àlbum",
"HeaderContinueWatching": "Continueu mirant", "HeaderContinueWatching": "Continueu mirant",
"HeaderFavoriteAlbums": "Àlbums preferits",
"HeaderFavoriteArtists": "Artistes preferits",
"HeaderFavoriteEpisodes": "Episodis preferits", "HeaderFavoriteEpisodes": "Episodis preferits",
"HeaderFavoriteShows": "Sèries preferides", "HeaderFavoriteShows": "Sèries preferides",
"HeaderFavoriteSongs": "Cançons preferides",
"HeaderLiveTV": "TV en directe", "HeaderLiveTV": "TV en directe",
"HeaderNextUp": "A continuació", "HeaderNextUp": "A continuació",
"HeaderRecordingGroups": "Grups musicals",
"HomeVideos": "Vídeos domèstics", "HomeVideos": "Vídeos domèstics",
"Inherit": "Heretat", "Inherit": "Heretat",
"ItemAddedWithName": "{0} s'ha afegit a la mediateca",
"ItemRemovedWithName": "{0} s'ha eliminat de la mediateca",
"LabelIpAddressValue": "Adreça IP: {0}", "LabelIpAddressValue": "Adreça IP: {0}",
"LabelRunningTimeValue": "Temps en marxa: {0}", "LabelRunningTimeValue": "Temps en marxa: {0}",
"Latest": "Darrers", "Latest": "Darrers",
"MessageApplicationUpdated": "El servidor de Jellyfin ha estat actualitzat",
"MessageApplicationUpdatedTo": "El servidor de Jellyfin ha estat actualitzat a {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "La secció {0} de la configuració del servidor ha estat actualitzada",
"MessageServerConfigurationUpdated": "S'ha actualitzat la configuració del servidor",
"MixedContent": "Contingut barrejat", "MixedContent": "Contingut barrejat",
"Movies": "Pel·lícules", "Movies": "Pel·lícules",
"Music": "Música", "Music": "Música",
@@ -44,14 +61,24 @@
"NotificationOptionVideoPlayback": "Reproducció de vídeo iniciada", "NotificationOptionVideoPlayback": "Reproducció de vídeo iniciada",
"NotificationOptionVideoPlaybackStopped": "Reproducció de vídeo aturada", "NotificationOptionVideoPlaybackStopped": "Reproducció de vídeo aturada",
"Photos": "Fotos", "Photos": "Fotos",
"PluginInstalledWithName": "S'ha instal·lat {0}", "Playlists": "Llistes de reproducció",
"PluginUninstalledWithName": "S'ha desinstal·lat {0}", "Plugin": "Complement",
"PluginInstalledWithName": "{0} ha estat instal·lat",
"PluginUninstalledWithName": "S'ha instal·lat {0}",
"PluginUpdatedWithName": "S'ha actualitzat {0}", "PluginUpdatedWithName": "S'ha actualitzat {0}",
"ProviderValue": "Proveïdor: {0}",
"ScheduledTaskFailedWithName": "{0} ha fallat", "ScheduledTaskFailedWithName": "{0} ha fallat",
"ScheduledTaskStartedWithName": "S'ha iniciat {0}",
"ServerNameNeedsToBeRestarted": "S'ha de reiniciar {0}",
"Shows": "Sèries", "Shows": "Sèries",
"Songs": "Cançons",
"StartupEmbyServerIsLoading": "El servidor de Jellyfin s'està carregant. Proveu-ho de nou en una estona.", "StartupEmbyServerIsLoading": "El servidor de Jellyfin s'està carregant. Proveu-ho de nou en una estona.",
"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": "Sincronitza",
"System": "Sistema",
"TvShows": "Sèries de TV", "TvShows": "Sèries de TV",
"User": "Usuari",
"UserCreatedWithName": "S'ha creat l'usuari {0}", "UserCreatedWithName": "S'ha creat l'usuari {0}",
"UserDeletedWithName": "S'ha eliminat l'usuari {0}", "UserDeletedWithName": "S'ha eliminat l'usuari {0}",
"UserDownloadingItemWithValues": "{0} està descarregant {1}", "UserDownloadingItemWithValues": "{0} està descarregant {1}",
@@ -59,8 +86,11 @@
"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": "S'ha canviat la contrasenya 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}",
"UserStartedPlayingItemWithValues": "{0} ha començat a reproduir {1} a {2}", "UserStartedPlayingItemWithValues": "{0} ha començat a reproduir {1} a {2}",
"UserStoppedPlayingItemWithValues": "{0} ha parat de reproduir {1} a {2}", "UserStoppedPlayingItemWithValues": "{0} ha parat de reproduir {1} a {2}",
"ValueHasBeenAddedToLibrary": "S'ha afegit {0} a la mediateca",
"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": "Descàrrega dels subtítols que faltin", "TaskDownloadMissingSubtitles": "Descàrrega dels subtítols que faltin",
@@ -75,7 +105,7 @@
"TaskCleanLogsDescription": "Esborra els registres que tinguin més de {0} dies.", "TaskCleanLogsDescription": "Esborra els registres que tinguin més de {0} dies.",
"TaskCleanLogs": "Neteja dels registres", "TaskCleanLogs": "Neteja dels registres",
"TaskRefreshLibraryDescription": "Escaneja les mediateques, a la cerca de fitxers nous i refresca les metadades.", "TaskRefreshLibraryDescription": "Escaneja les mediateques, a la cerca de fitxers nous i refresca les metadades.",
"TaskRefreshLibrary": "Escaneja la mediateca", "TaskRefreshLibrary": "Escaneig de les mediateques",
"TaskRefreshChapterImagesDescription": "Creació de les miniatures dels vídeos que tinguin capítols.", "TaskRefreshChapterImagesDescription": "Creació de les miniatures dels vídeos que tinguin capítols.",
"TaskRefreshChapterImages": "Extracció de les imatges dels capítols", "TaskRefreshChapterImages": "Extracció de les imatges dels capítols",
"TaskCleanCacheDescription": "Eliminació de la memòria cau no necessària per al servidor.", "TaskCleanCacheDescription": "Eliminació de la memòria cau no necessària per al servidor.",
@@ -97,6 +127,8 @@
"HearingImpaired": "Discapacitat auditiva", "HearingImpaired": "Discapacitat auditiva",
"TaskRefreshTrickplayImages": "Generació d'imatges de previsualització", "TaskRefreshTrickplayImages": "Generació d'imatges de previsualització",
"TaskRefreshTrickplayImagesDescription": "Creació d'imatges de previsualització per a vídeos en les mediateques habilitades.", "TaskRefreshTrickplayImagesDescription": "Creació d'imatges de previsualització per a vídeos en les mediateques habilitades.",
"TaskCleanCollectionsAndPlaylistsDescription": "Esborra elements de col·leccions i llistes de reproducció que ja no existeixen.",
"TaskCleanCollectionsAndPlaylists": "Neteja de les col·leccions i llistes de reproducció",
"TaskAudioNormalization": "Estabilització de l'àudio", "TaskAudioNormalization": "Estabilització de l'àudio",
"TaskAudioNormalizationDescription": "Escaneja els fitxer per a obtenir dades de normalització de l'àudio.", "TaskAudioNormalizationDescription": "Escaneja els fitxer per a obtenir dades de normalització de l'àudio.",
"TaskDownloadMissingLyricsDescription": "Descàrrega de les lletres de les cançons", "TaskDownloadMissingLyricsDescription": "Descàrrega de les lletres de les cançons",
@@ -106,6 +138,5 @@
"TaskMoveTrickplayImages": "Migració de la ubicació de la imatge de previsualització", "TaskMoveTrickplayImages": "Migració de la ubicació de la imatge de previsualització",
"TaskMoveTrickplayImagesDescription": "Mou els fitxers existents d'imatges de previsualització segons la configuració de la mediateca.", "TaskMoveTrickplayImagesDescription": "Mou els fitxers existents d'imatges de previsualització segons la configuració de la mediateca.",
"CleanupUserDataTaskDescription": "Neteja totes les dades d'usuari (estat de la visualització, estat dels preferits, etc.) del contingut multimèdia que no ha estat present durant almenys 90 dies.", "CleanupUserDataTaskDescription": "Neteja totes les dades d'usuari (estat de la visualització, estat dels preferits, etc.) del contingut multimèdia que no ha estat present durant almenys 90 dies.",
"CleanupUserDataTask": "Tasca de neteja de dades d'usuari", "CleanupUserDataTask": "Tasca de neteja de dades d'usuari"
"Original": "Original"
} }

View File

@@ -1,30 +1,44 @@
{ {
"ChapterNameValue": "Didanedi {0}", "ChapterNameValue": "Didanedi {0}",
"HeaderAlbumArtists": "Didanidanolisgisgi",
"HeaderFavoriteAlbums": "Dvganidi didanidisgisgi",
"HeaderLiveTV": "Anigadi didanidisgosgi", "HeaderLiveTV": "Anigadi didanidisgosgi",
"HeaderRecordingGroups": "Didanisquodiisgisgi",
"HomeVideos": "Diganadi dinagadisgisgi", "HomeVideos": "Diganadi dinagadisgisgi",
"Inherit": "Anigwe", "Inherit": "Anigwe",
"MessageApplicationUpdatedTo": "Tsenigwidinonvhi Jellyfin Server tsadanidigwe anigadi {0}",
"MixedContent": "Ganinidi dininoladisgisgi", "MixedContent": "Ganinidi dininoladisgisgi",
"Movies": "Anidvnisgisgi", "Movies": "Anidvnisgisgi",
"MusicVideos": "Danodisgisgi didanidisgosgi", "MusicVideos": "Danodisgisgi didanidisgosgi",
"NotificationOptionAudioPlayback": "Didanidigwe diganuyisgisgi anigadi", "NotificationOptionAudioPlayback": "Didanidigwe diganuyisgisgi anigadi",
"NotificationOptionInstallationFailed": "Diudvdi anadvnatisgisgi", "NotificationOptionInstallationFailed": "Diudvdi anadvnatisgisgi",
"NotificationOptionPluginUninstalled": "Ditsigvhnidv anawvdisgisgi", "NotificationOptionPluginUninstalled": "Ditsigvhnidv anawvdisgisgi",
"Albums": "Anigawidaniyv",
"Application": "Didanvyi",
"Artists": "Dinidaniyi", "Artists": "Dinidaniyi",
"AuthenticationSucceededWithUserName": "{0} Sesoquonisdi nagadani", "AuthenticationSucceededWithUserName": "{0} Sesoquonisdi nagadani",
"Books": "Didanedi", "Books": "Didanedi",
"CameraImageUploadedFrom": "Anigawidaniyv nasgi didagwalanvyi {0}",
"Channels": "Diganadasgi",
"Collections": "Diganadisgi", "Collections": "Diganadisgi",
"Default": "Dinadi", "Default": "Dinadi",
"DeviceOfflineWithName": "{0} Aniyvolehvi nasgi",
"External": "Amohdi", "External": "Amohdi",
"Favorites": "Nvdayelvdisgi", "Favorites": "Nvdayelvdisgi",
"Folders": "Didanididisgi", "Folders": "Didanididisgi",
"Forced": "Ganedi", "Forced": "Ganedi",
"Genres": "Diganadisgi", "Genres": "Diganadisgi",
"HeaderContinueWatching": "Uwoditsu asdanidisgisgi", "HeaderContinueWatching": "Uwoditsu asdanidisgisgi",
"HeaderFavoriteArtists": "Dvganidi dinidanolisgisgi",
"HeaderFavoriteEpisodes": "Dvganidi didanidilisgadisgisgi", "HeaderFavoriteEpisodes": "Dvganidi didanidilisgadisgisgi",
"HeaderFavoriteShows": "Dvganidi didanididanolisgisgi)", "HeaderFavoriteShows": "Dvganidi didanididanolisgisgi)",
"HeaderFavoriteSongs": "Dvganidi danodisgisgi",
"HeaderNextUp": "Anidvli uwodoli", "HeaderNextUp": "Anidvli uwodoli",
"HearingImpaired": "Anitsunidi talunidisgisgi", "HearingImpaired": "Anitsunidi talunidisgisgi",
"ItemAddedWithName": "{0} Dinigwe anididanidisgi",
"Latest": "Uwodoli", "Latest": "Uwodoli",
"MessageApplicationUpdated": "Tsenigwidinonvhi Jellyfin Server tsadanidigwe",
"MessageServerConfigurationUpdated": "Sedanidvdi anigadi diganidinonvhi",
"Music": "Danodisgisgi", "Music": "Danodisgisgi",
"NameSeasonUnknown": "Tsunita anidvdisgi", "NameSeasonUnknown": "Tsunita anidvdisgi",
"NewVersionIsAvailable": "Danodigwe anigadi Jellyfin Server tsadanidigwe adisdi uwodvdi diganidinonvhi.", "NewVersionIsAvailable": "Danodigwe anigadi Jellyfin Server tsadanidigwe adisdi uwodvdi diganidinonvhi.",

View File

@@ -1,24 +1,41 @@
{ {
"Albums": "Alba",
"AppDeviceValues": "Aplikace: {0}, Zařízení: {1}", "AppDeviceValues": "Aplikace: {0}, Zařízení: {1}",
"Application": "Aplikace",
"Artists": "Umělci", "Artists": "Umělci",
"AuthenticationSucceededWithUserName": "{0} úspěšně ověřen", "AuthenticationSucceededWithUserName": "{0} úspěšně ověřen",
"Books": "Knihy", "Books": "Knihy",
"CameraImageUploadedFrom": "Z {0} byla nahrána nová fotografie z fotoaparátu",
"Channels": "Kanály",
"ChapterNameValue": "Kapitola {0}", "ChapterNameValue": "Kapitola {0}",
"Collections": "Kolekce", "Collections": "Kolekce",
"DeviceOfflineWithName": "{0} se odpojil",
"DeviceOnlineWithName": "{0} je připojen",
"FailedLoginAttemptWithUserName": "Neúspěšný pokus o přihlášení z {0}", "FailedLoginAttemptWithUserName": "Neúspěšný pokus o přihlášení z {0}",
"Favorites": "Oblíbené", "Favorites": "Oblíbené",
"Folders": "Složky", "Folders": "Složky",
"Genres": "Žánry", "Genres": "Žánry",
"HeaderAlbumArtists": "Umělci alba",
"HeaderContinueWatching": "Pokračovat ve sledování", "HeaderContinueWatching": "Pokračovat ve sledování",
"HeaderFavoriteAlbums": "Oblíbená alba",
"HeaderFavoriteArtists": "Oblíbení interpreti",
"HeaderFavoriteEpisodes": "Oblíbené epizody", "HeaderFavoriteEpisodes": "Oblíbené epizody",
"HeaderFavoriteShows": "Oblíbené seriály", "HeaderFavoriteShows": "Oblíbené seriály",
"HeaderFavoriteSongs": "Oblíbená hudba",
"HeaderLiveTV": "TV vysílání", "HeaderLiveTV": "TV vysílání",
"HeaderNextUp": "Další díly", "HeaderNextUp": "Další díly",
"HeaderRecordingGroups": "Skupiny nahrávek",
"HomeVideos": "Domácí videa", "HomeVideos": "Domácí videa",
"Inherit": "Zdědit", "Inherit": "Zdědit",
"ItemAddedWithName": "{0} byl přidán do knihovny",
"ItemRemovedWithName": "{0} byl odstraněn z knihovny",
"LabelIpAddressValue": "IP adresa: {0}", "LabelIpAddressValue": "IP adresa: {0}",
"LabelRunningTimeValue": "Délka média: {0}", "LabelRunningTimeValue": "Délka média: {0}",
"Latest": "Nejnovější", "Latest": "Nejnovější",
"MessageApplicationUpdated": "Jellyfin Server byl aktualizován",
"MessageApplicationUpdatedTo": "Jellyfin server byl aktualizován na verzi {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Konfigurace sekce {0} na serveru byla aktualizována",
"MessageServerConfigurationUpdated": "Konfigurace serveru aktualizována",
"MixedContent": "Smíšený obsah", "MixedContent": "Smíšený obsah",
"Movies": "Filmy", "Movies": "Filmy",
"Music": "Hudba", "Music": "Hudba",
@@ -44,14 +61,24 @@
"NotificationOptionVideoPlayback": "Přehrávání videa zahájeno", "NotificationOptionVideoPlayback": "Přehrávání videa zahájeno",
"NotificationOptionVideoPlaybackStopped": "Přehrávání videa ukončeno", "NotificationOptionVideoPlaybackStopped": "Přehrávání videa ukončeno",
"Photos": "Fotky", "Photos": "Fotky",
"Playlists": "Seznamy skladeb",
"Plugin": "Zásuvný modul",
"PluginInstalledWithName": "{0} byl nainstalován", "PluginInstalledWithName": "{0} byl nainstalován",
"PluginUninstalledWithName": "{0} byl odinstalován", "PluginUninstalledWithName": "{0} byl odinstalován",
"PluginUpdatedWithName": "{0} byl aktualizován", "PluginUpdatedWithName": "{0} byl aktualizován",
"ProviderValue": "Poskytl: {0}",
"ScheduledTaskFailedWithName": "{0} selhalo", "ScheduledTaskFailedWithName": "{0} selhalo",
"ScheduledTaskStartedWithName": "{0} zahájeno",
"ServerNameNeedsToBeRestarted": "{0} vyžaduje restart",
"Shows": "Seriály", "Shows": "Seriály",
"Songs": "Skladby",
"StartupEmbyServerIsLoading": "Jellyfin Server je spouštěn. Zkuste to prosím v brzké době znovu.", "StartupEmbyServerIsLoading": "Jellyfin Server je spouštěn. Zkuste to prosím v brzké době znovu.",
"SubtitleDownloadFailureForItem": "Stahování titulků selhalo pro {0}",
"SubtitleDownloadFailureFromForItem": "Stažení titulků pro {1} z {0} selhalo", "SubtitleDownloadFailureFromForItem": "Stažení titulků pro {1} z {0} selhalo",
"Sync": "Synchronizace",
"System": "Systém",
"TvShows": "Seriály", "TvShows": "Seriály",
"User": "Uživatel",
"UserCreatedWithName": "Uživatel {0} byl vytvořen", "UserCreatedWithName": "Uživatel {0} byl vytvořen",
"UserDeletedWithName": "Uživatel {0} byl smazán", "UserDeletedWithName": "Uživatel {0} byl smazán",
"UserDownloadingItemWithValues": "{0} stahuje {1}", "UserDownloadingItemWithValues": "{0} stahuje {1}",
@@ -59,8 +86,11 @@
"UserOfflineFromDevice": "{0} se odpojil ze zařízení {1}", "UserOfflineFromDevice": "{0} se odpojil ze zařízení {1}",
"UserOnlineFromDevice": "{0} se připojil ze zařízení {1}", "UserOnlineFromDevice": "{0} se připojil ze zařízení {1}",
"UserPasswordChangedWithName": "Provedena změna hesla pro uživatele {0}", "UserPasswordChangedWithName": "Provedena změna hesla pro uživatele {0}",
"UserPolicyUpdatedWithName": "Zásady uživatele pro {0} byly aktualizovány",
"UserStartedPlayingItemWithValues": "{0} spustil přehrávání {1}", "UserStartedPlayingItemWithValues": "{0} spustil přehrávání {1}",
"UserStoppedPlayingItemWithValues": "{0} zastavil přehrávání {1}", "UserStoppedPlayingItemWithValues": "{0} zastavil přehrávání {1}",
"ValueHasBeenAddedToLibrary": "{0} byl přidán do vaší knihovny médií",
"ValueSpecialEpisodeName": "Speciál - {0}",
"VersionNumber": "Verze {0}", "VersionNumber": "Verze {0}",
"TaskDownloadMissingSubtitlesDescription": "Vyhledá na internetu chybějící titulky na základě nastavení metadat.", "TaskDownloadMissingSubtitlesDescription": "Vyhledá na internetu chybějící titulky na základě nastavení metadat.",
"TaskDownloadMissingSubtitles": "Stáhnout chybějící titulky", "TaskDownloadMissingSubtitles": "Stáhnout chybějící titulky",
@@ -97,6 +127,8 @@
"HearingImpaired": "Sluchově postižení", "HearingImpaired": "Sluchově postižení",
"TaskRefreshTrickplayImages": "Generovat obrázky pro Trickplay", "TaskRefreshTrickplayImages": "Generovat obrázky pro Trickplay",
"TaskRefreshTrickplayImagesDescription": "Obrázky Trickplay se používají k zobrazení náhledů u videí v knihovnách, kde je to povoleno.", "TaskRefreshTrickplayImagesDescription": "Obrázky Trickplay se používají k zobrazení náhledů u videí v knihovnách, kde je to povoleno.",
"TaskCleanCollectionsAndPlaylists": "Pročistit kolekce a seznamy přehrávání",
"TaskCleanCollectionsAndPlaylistsDescription": "Odstraní neexistující položky z kolekcí a seznamů přehrávání.",
"TaskAudioNormalization": "Normalizace zvuku", "TaskAudioNormalization": "Normalizace zvuku",
"TaskAudioNormalizationDescription": "Skenovat soubory za účelem normalizace zvuku.", "TaskAudioNormalizationDescription": "Skenovat soubory za účelem normalizace zvuku.",
"TaskDownloadMissingLyrics": "Stáhnout chybějící texty k písni", "TaskDownloadMissingLyrics": "Stáhnout chybějící texty k písni",
@@ -106,6 +138,5 @@
"TaskMoveTrickplayImages": "Přesunout úložiště obrázků Trickplay", "TaskMoveTrickplayImages": "Přesunout úložiště obrázků Trickplay",
"TaskMoveTrickplayImagesDescription": "Přesune existující soubory Trickplay podle nastavení knihovny.", "TaskMoveTrickplayImagesDescription": "Přesune existující soubory Trickplay podle nastavení knihovny.",
"CleanupUserDataTaskDescription": "Odstraní všechna uživatelská data (stav zhlédnutí, oblíbené atd.) z médií, které již neexistují více než 90 dní.", "CleanupUserDataTaskDescription": "Odstraní všechna uživatelská data (stav zhlédnutí, oblíbené atd.) z médií, které již neexistují více než 90 dní.",
"CleanupUserDataTask": "Pročistit uživatelská data", "CleanupUserDataTask": "Pročistit uživatelská data"
"Original": "Originál"
} }

View File

@@ -1,11 +1,16 @@
{ {
"DeviceOnlineWithName": "Mae {0} wedi'i gysylltu",
"DeviceOfflineWithName": "Mae {0} wedi datgysylltu",
"Default": "Diofyn", "Default": "Diofyn",
"Collections": "Casgliadau", "Collections": "Casgliadau",
"ChapterNameValue": "Pennod {0}", "ChapterNameValue": "Pennod {0}",
"Channels": "Sianeli",
"CameraImageUploadedFrom": "Mae delwedd camera newydd wedi'i lanlwytho o {0}",
"Books": "Llyfrau", "Books": "Llyfrau",
"AuthenticationSucceededWithUserName": "{0} wedii ddilysun llwyddiannus", "AuthenticationSucceededWithUserName": "{0} wedii ddilysun llwyddiannus",
"Artists": "Crewyr", "Artists": "Artistiaid",
"AppDeviceValues": "Ap: {0}, Dyfais: {1}", "AppDeviceValues": "Ap: {0}, Dyfais: {1}",
"Albums": "Albwmau",
"Genres": "Genres", "Genres": "Genres",
"Folders": "Ffolderi", "Folders": "Ffolderi",
"Favorites": "Ffefrynnau", "Favorites": "Ffefrynnau",
@@ -15,7 +20,9 @@
"TaskRefreshPeople": "Adnewyddu Pobl", "TaskRefreshPeople": "Adnewyddu Pobl",
"TasksChannelsCategory": "Sianeli Internet", "TasksChannelsCategory": "Sianeli Internet",
"VersionNumber": "Fersiwn {0}", "VersionNumber": "Fersiwn {0}",
"ScheduledTaskStartedWithName": "{0} wedi dechrau",
"ScheduledTaskFailedWithName": "{0} wedi methu", "ScheduledTaskFailedWithName": "{0} wedi methu",
"ProviderValue": "Darparwr: {0}",
"NotificationOptionInstallationFailed": "Fethu Gosod", "NotificationOptionInstallationFailed": "Fethu Gosod",
"NameSeasonUnknown": "Tymor Anhysbys", "NameSeasonUnknown": "Tymor Anhysbys",
"NameSeasonNumber": "Tymor {0}", "NameSeasonNumber": "Tymor {0}",
@@ -23,20 +30,31 @@
"MixedContent": "Cynnwys amrywiol", "MixedContent": "Cynnwys amrywiol",
"HomeVideos": "Genres", "HomeVideos": "Genres",
"HeaderNextUp": "Nesaf i Fyny", "HeaderNextUp": "Nesaf i Fyny",
"HeaderFavoriteArtists": "Ffefryn Artistiaid",
"HeaderFavoriteAlbums": "Ffefryn Albwmau",
"HeaderContinueWatching": "Parhewch i Wylio", "HeaderContinueWatching": "Parhewch i Wylio",
"TasksApplicationCategory": "Rhaglen", "TasksApplicationCategory": "Rhaglen",
"TasksLibraryCategory": "Llyfrgell", "TasksLibraryCategory": "Llyfrgell",
"TasksMaintenanceCategory": "Cynnal a Chadw", "TasksMaintenanceCategory": "Cynnal a Chadw",
"System": "System",
"Plugin": "Ategyn",
"Music": "Cerddoriaeth", "Music": "Cerddoriaeth",
"Latest": "Diweddaraf", "Latest": "Diweddaraf",
"Inherit": "Etifeddu", "Inherit": "Etifeddu",
"Forced": "Orfodi", "Forced": "Orfodi",
"Application": "Rhaglen",
"HeaderAlbumArtists": "Artistiaid albwm",
"Sync": "Cysoni",
"Songs": "Caneuon",
"Shows": "Rhaglenni", "Shows": "Rhaglenni",
"Playlists": "Rhestri Chwarae",
"Photos": "Lluniau", "Photos": "Lluniau",
"ValueSpecialEpisodeName": "Arbennig - {0}",
"Movies": "Ffilmiau", "Movies": "Ffilmiau",
"Undefined": "Heb ddiffiniad", "Undefined": "Heb ddiffiniad",
"TvShows": "Rhaglenni teledu", "TvShows": "Rhaglenni teledu",
"HeaderLiveTV": "Teledu Byw", "HeaderLiveTV": "Teledu Byw",
"User": "Defnyddiwr",
"TaskCleanLogsDescription": "Dileu ffeiliau log sy'n fwy na {0} diwrnod oed.", "TaskCleanLogsDescription": "Dileu ffeiliau log sy'n fwy na {0} diwrnod oed.",
"TaskCleanLogs": "Glanhau ffolder log", "TaskCleanLogs": "Glanhau ffolder log",
"TaskRefreshLibraryDescription": "Sganio'ch llyfrgell gyfryngau am ffeiliau newydd ac yn adnewyddu metaddata.", "TaskRefreshLibraryDescription": "Sganio'ch llyfrgell gyfryngau am ffeiliau newydd ac yn adnewyddu metaddata.",
@@ -47,9 +65,13 @@
"NotificationOptionPluginError": "Methodd ategyn", "NotificationOptionPluginError": "Methodd ategyn",
"NotificationOptionAudioPlaybackStopped": "Stopiwyd chwarae sain", "NotificationOptionAudioPlaybackStopped": "Stopiwyd chwarae sain",
"NotificationOptionAudioPlayback": "Dechreuwyd chwarae sain", "NotificationOptionAudioPlayback": "Dechreuwyd chwarae sain",
"FailedLoginAttemptWithUserName": "Cais mewngofnodi wedi methu o {0}", "MessageServerConfigurationUpdated": "Mae gosodiadau gweinydd wedi'i ddiweddaru",
"MessageNamedServerConfigurationUpdatedWithValue": "Mae adran gosodiadau gweinydd {0} wedi'i diweddaru",
"FailedLoginAttemptWithUserName": "Cais mewngofnodi wedi methu gan {0}",
"ValueHasBeenAddedToLibrary": "{0} wedi'i hychwanegu at eich llyfrgell gyfryngau",
"UserStoppedPlayingItemWithValues": "{0} wedi gorffen chwarae {1} ar {2}", "UserStoppedPlayingItemWithValues": "{0} wedi gorffen chwarae {1} ar {2}",
"UserStartedPlayingItemWithValues": "{0} yn chwarae {1} ar {2}", "UserStartedPlayingItemWithValues": "{0} yn chwarae {1} ar {2}",
"UserPolicyUpdatedWithName": "Polisi defnyddiwr wedi'i newid ar gyfer {0}",
"UserPasswordChangedWithName": "Cyfrinair wedi'i newid ar gyfer defnyddiwr {0}", "UserPasswordChangedWithName": "Cyfrinair wedi'i newid ar gyfer defnyddiwr {0}",
"UserOnlineFromDevice": "Mae {0} ar-lein o {1}", "UserOnlineFromDevice": "Mae {0} ar-lein o {1}",
"UserOfflineFromDevice": "Mae {0} wedi datgysylltu o {1}", "UserOfflineFromDevice": "Mae {0} wedi datgysylltu o {1}",
@@ -58,6 +80,7 @@
"UserDeletedWithName": "Defnyddiwr {0} wedi'i ddileu", "UserDeletedWithName": "Defnyddiwr {0} wedi'i ddileu",
"UserCreatedWithName": "Defnyddiwr {0} wedi'i greu", "UserCreatedWithName": "Defnyddiwr {0} wedi'i greu",
"StartupEmbyServerIsLoading": "Gweinydd Jellyfin yn llwytho. Triwch eto mewn ychydig.", "StartupEmbyServerIsLoading": "Gweinydd Jellyfin yn llwytho. Triwch eto mewn ychydig.",
"ServerNameNeedsToBeRestarted": "Mae angen ailddechrau {0}",
"PluginUpdatedWithName": "{0} wedi'i ddiweddaru", "PluginUpdatedWithName": "{0} wedi'i ddiweddaru",
"PluginUninstalledWithName": "{0} wedi'i ddadosod", "PluginUninstalledWithName": "{0} wedi'i ddadosod",
"PluginInstalledWithName": "{0} wedi'i osod", "PluginInstalledWithName": "{0} wedi'i osod",
@@ -75,7 +98,13 @@
"NotificationOptionApplicationUpdateAvailable": "Diweddariad ap ar gael", "NotificationOptionApplicationUpdateAvailable": "Diweddariad ap ar gael",
"NewVersionIsAvailable": "Mae fersiwn diweddarach o'r gweinydd Jellyfin ar gael.", "NewVersionIsAvailable": "Mae fersiwn diweddarach o'r gweinydd Jellyfin ar gael.",
"NameInstallFailed": "Gosodiad {0} wedi methu", "NameInstallFailed": "Gosodiad {0} wedi methu",
"MessageApplicationUpdatedTo": "Gweinydd Jellyfin wedi'i ddiweddaru i {0}",
"MessageApplicationUpdated": "Gweinydd Jellyfin wedi'i ddiweddaru",
"LabelIpAddressValue": "Cyfeiriad IP: {0}", "LabelIpAddressValue": "Cyfeiriad IP: {0}",
"ItemRemovedWithName": "{0} wedi'i dynnu o'r llyfrgell",
"ItemAddedWithName": "{0} wedi'i adio i'r llyfrgell",
"HeaderRecordingGroups": "Grwpiau Recordio",
"HeaderFavoriteSongs": "Ffefryn Ganeuon",
"HeaderFavoriteShows": "Ffefryn Shoeau", "HeaderFavoriteShows": "Ffefryn Shoeau",
"HeaderFavoriteEpisodes": "Ffefryn Rhaglenni", "HeaderFavoriteEpisodes": "Ffefryn Rhaglenni",
"TaskDownloadMissingSubtitlesDescription": "Chwilio'r rhyngrwyd am is-deitlau coll yn seiliedig ar gosodiadau metaddata.", "TaskDownloadMissingSubtitlesDescription": "Chwilio'r rhyngrwyd am is-deitlau coll yn seiliedig ar gosodiadau metaddata.",
@@ -94,12 +123,5 @@
"TaskRefreshChapterImages": "Echdynnu Lluniau Pennod", "TaskRefreshChapterImages": "Echdynnu Lluniau Pennod",
"TaskCleanCacheDescription": "Dileu ffeiliau cache nad oes eu hangen ar y system mwyach.", "TaskCleanCacheDescription": "Dileu ffeiliau cache nad oes eu hangen ar y system mwyach.",
"TaskCleanCache": "Gwaghau Ffolder Cache", "TaskCleanCache": "Gwaghau Ffolder Cache",
"HearingImpaired": "Nam ar y clyw", "HearingImpaired": "Nam ar y clyw"
"TaskAudioNormalization": "Gwastatau Sain",
"TaskAudioNormalizationDescription": "Yn sganio ffeiliau am ddata gwastatau sain.",
"TaskRefreshTrickplayImages": "Creuwch lluniau Trickplay",
"TaskRefreshTrickplayImagesDescription": "Creu rhagolygon Trickplay ar gyfer fideos mewn llyfrgelloedd gweithredol.",
"TaskDownloadMissingLyrics": "Lawrlwytho geiriau coll",
"TaskDownloadMissingLyricsDescription": "Lawrlwytho geiriau caneuon",
"TaskExtractMediaSegments": "Sganio Darnau Cyfryngau"
} }

View File

@@ -1,24 +1,41 @@
{ {
"Albums": "Albummer",
"AppDeviceValues": "App: {0}, Enhed: {1}", "AppDeviceValues": "App: {0}, Enhed: {1}",
"Application": "Applikation",
"Artists": "Kunstnere", "Artists": "Kunstnere",
"AuthenticationSucceededWithUserName": "{0} er logget ind", "AuthenticationSucceededWithUserName": "{0} er logget ind",
"Books": "Bøger", "Books": "Bøger",
"CameraImageUploadedFrom": "Et nyt kamerabillede er blevet uploadet fra {0}",
"Channels": "Kanaler",
"ChapterNameValue": "Kapitel {0}", "ChapterNameValue": "Kapitel {0}",
"Collections": "Samlinger", "Collections": "Samlinger",
"DeviceOfflineWithName": "{0} har afbrudt forbindelsen",
"DeviceOnlineWithName": "{0} er forbundet",
"FailedLoginAttemptWithUserName": "Mislykket loginforsøg fra {0}", "FailedLoginAttemptWithUserName": "Mislykket loginforsøg fra {0}",
"Favorites": "Favoritter", "Favorites": "Favoritter",
"Folders": "Mapper", "Folders": "Mapper",
"Genres": "Genrer", "Genres": "Genrer",
"HeaderAlbumArtists": "Albumkunstnere",
"HeaderContinueWatching": "Fortsæt afspilning", "HeaderContinueWatching": "Fortsæt afspilning",
"HeaderFavoriteAlbums": "Favoritalbum",
"HeaderFavoriteArtists": "Favoritkunstnere",
"HeaderFavoriteEpisodes": "Yndlingsafsnit", "HeaderFavoriteEpisodes": "Yndlingsafsnit",
"HeaderFavoriteShows": "Yndlingsserier", "HeaderFavoriteShows": "Yndlingsserier",
"HeaderFavoriteSongs": "Yndlingssange",
"HeaderLiveTV": "Live-TV", "HeaderLiveTV": "Live-TV",
"HeaderNextUp": "Næste", "HeaderNextUp": "Næste",
"HeaderRecordingGroups": "Optagelsesgrupper",
"HomeVideos": "Hjemmevideoer", "HomeVideos": "Hjemmevideoer",
"Inherit": "Nedarv", "Inherit": "Nedarv",
"ItemAddedWithName": "{0} blev tilføjet til biblioteket",
"ItemRemovedWithName": "{0} blev fjernet fra biblioteket",
"LabelIpAddressValue": "IP-adresse: {0}", "LabelIpAddressValue": "IP-adresse: {0}",
"LabelRunningTimeValue": "Spilletid: {0}", "LabelRunningTimeValue": "Spilletid: {0}",
"Latest": "Seneste", "Latest": "Seneste",
"MessageApplicationUpdated": "Jellyfin Server er blevet opdateret",
"MessageApplicationUpdatedTo": "Jellyfin Server er blevet opdateret til {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Serverkonfiguration sektion {0} er blevet opdateret",
"MessageServerConfigurationUpdated": "Serverkonfigurationen er blevet opdateret",
"MixedContent": "Blandet indhold", "MixedContent": "Blandet indhold",
"Movies": "Film", "Movies": "Film",
"Music": "Musik", "Music": "Musik",
@@ -44,14 +61,24 @@
"NotificationOptionVideoPlayback": "Videoafspilning påbegyndt", "NotificationOptionVideoPlayback": "Videoafspilning påbegyndt",
"NotificationOptionVideoPlaybackStopped": "Videoafspilning blev stoppet", "NotificationOptionVideoPlaybackStopped": "Videoafspilning blev stoppet",
"Photos": "Fotos", "Photos": "Fotos",
"Playlists": "Afspilningslister",
"Plugin": "Plugin",
"PluginInstalledWithName": "{0} blev installeret", "PluginInstalledWithName": "{0} blev installeret",
"PluginUninstalledWithName": "{0} blev afinstalleret", "PluginUninstalledWithName": "{0} blev afinstalleret",
"PluginUpdatedWithName": "{0} blev opdateret", "PluginUpdatedWithName": "{0} blev opdateret",
"ProviderValue": "Udbyder: {0}",
"ScheduledTaskFailedWithName": "{0} mislykkedes", "ScheduledTaskFailedWithName": "{0} mislykkedes",
"ScheduledTaskStartedWithName": "{0} påbegyndte",
"ServerNameNeedsToBeRestarted": "{0} skal genstartes",
"Shows": "Serier", "Shows": "Serier",
"Songs": "Sange",
"StartupEmbyServerIsLoading": "Jellyfin er i gang med at starte. Prøv 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}",
"SubtitleDownloadFailureFromForItem": "Undertekster kunne ikke hentes fra {0} til {1}", "SubtitleDownloadFailureFromForItem": "Undertekster kunne ikke hentes fra {0} til {1}",
"Sync": "Synkroniser",
"System": "System",
"TvShows": "TV-serier", "TvShows": "TV-serier",
"User": "Bruger",
"UserCreatedWithName": "Bruger {0} er blevet oprettet", "UserCreatedWithName": "Bruger {0} er blevet oprettet",
"UserDeletedWithName": "Brugeren {0} er nu slettet", "UserDeletedWithName": "Brugeren {0} er nu slettet",
"UserDownloadingItemWithValues": "{0} henter {1}", "UserDownloadingItemWithValues": "{0} henter {1}",
@@ -59,8 +86,11 @@
"UserOfflineFromDevice": "{0} har afbrudt fra {1}", "UserOfflineFromDevice": "{0} har afbrudt fra {1}",
"UserOnlineFromDevice": "{0} er online fra {1}", "UserOnlineFromDevice": "{0} er online fra {1}",
"UserPasswordChangedWithName": "Adgangskode er ændret for brugeren {0}", "UserPasswordChangedWithName": "Adgangskode er ændret for brugeren {0}",
"UserPolicyUpdatedWithName": "Brugerpolitikken er blevet opdateret for {0}",
"UserStartedPlayingItemWithValues": "{0} afspiller {1} på {2}", "UserStartedPlayingItemWithValues": "{0} afspiller {1} på {2}",
"UserStoppedPlayingItemWithValues": "{0} har afsluttet afspilning af {1} på {2}", "UserStoppedPlayingItemWithValues": "{0} har afsluttet afspilning af {1} på {2}",
"ValueHasBeenAddedToLibrary": "{0} er blevet tilføjet til dit mediebibliotek",
"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": "Hent manglende undertekster", "TaskDownloadMissingSubtitles": "Hent manglende undertekster",
@@ -97,6 +127,8 @@
"HearingImpaired": "Hørehæmmet", "HearingImpaired": "Hørehæmmet",
"TaskRefreshTrickplayImages": "Generer trickplay-billeder", "TaskRefreshTrickplayImages": "Generer trickplay-billeder",
"TaskRefreshTrickplayImagesDescription": "Laver trickplay-billeder for videoer i aktiverede biblioteker.", "TaskRefreshTrickplayImagesDescription": "Laver trickplay-billeder for videoer i aktiverede biblioteker.",
"TaskCleanCollectionsAndPlaylists": "Ryd op i samlinger og afspilningslister",
"TaskCleanCollectionsAndPlaylistsDescription": "Fjerner elementer fra samlinger og afspilningslister der ikke eksisterer længere.",
"TaskAudioNormalizationDescription": "Skanner filer for data vedrørende lydnormalisering.", "TaskAudioNormalizationDescription": "Skanner filer for data vedrørende lydnormalisering.",
"TaskAudioNormalization": "Lydnormalisering", "TaskAudioNormalization": "Lydnormalisering",
"TaskDownloadMissingLyricsDescription": "Søger på internettet efter manglende sangtekster baseret på metadata-konfigurationen", "TaskDownloadMissingLyricsDescription": "Søger på internettet efter manglende sangtekster baseret på metadata-konfigurationen",

View File

@@ -1,24 +1,41 @@
{ {
"Albums": "Alben",
"AppDeviceValues": "App: {0}, Gerät: {1}", "AppDeviceValues": "App: {0}, Gerät: {1}",
"Application": "Anwendung",
"Artists": "Interpreten", "Artists": "Interpreten",
"AuthenticationSucceededWithUserName": "{0} erfolgreich authentifiziert", "AuthenticationSucceededWithUserName": "{0} erfolgreich authentifiziert",
"Books": "Bücher", "Books": "Bücher",
"CameraImageUploadedFrom": "Ein neues Kamerabild wurde von {0} hochgeladen",
"Channels": "Kanäle",
"ChapterNameValue": "Kapitel {0}", "ChapterNameValue": "Kapitel {0}",
"Collections": "Sammlungen", "Collections": "Sammlungen",
"FailedLoginAttemptWithUserName": "Anmeldung von {0} fehlgeschlagen", "DeviceOfflineWithName": "{0} hat die Verbindung getrennt",
"DeviceOnlineWithName": "{0} ist verbunden",
"FailedLoginAttemptWithUserName": "Fehlgeschlagener Anmeldeversuch von {0}",
"Favorites": "Favoriten", "Favorites": "Favoriten",
"Folders": "Verzeichnisse", "Folders": "Verzeichnisse",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Album-Interpreten",
"HeaderContinueWatching": "Weiterschauen", "HeaderContinueWatching": "Weiterschauen",
"HeaderFavoriteEpisodes": "Lieblingsfolgen", "HeaderFavoriteAlbums": "Lieblingsalben",
"HeaderFavoriteArtists": "Lieblingsinterpreten",
"HeaderFavoriteEpisodes": "Lieblingsepisoden",
"HeaderFavoriteShows": "Lieblingsserien", "HeaderFavoriteShows": "Lieblingsserien",
"HeaderFavoriteSongs": "Lieblingslieder",
"HeaderLiveTV": "Live TV", "HeaderLiveTV": "Live TV",
"HeaderNextUp": "Als Nächstes", "HeaderNextUp": "Als Nächstes",
"HeaderRecordingGroups": "Aufnahme-Gruppen",
"HomeVideos": "Heimvideos", "HomeVideos": "Heimvideos",
"Inherit": "Vererben", "Inherit": "Vererben",
"ItemAddedWithName": "{0} wurde der Bibliothek hinzugefügt",
"ItemRemovedWithName": "{0} wurde aus der Bibliothek entfernt",
"LabelIpAddressValue": "IP-Adresse: {0}", "LabelIpAddressValue": "IP-Adresse: {0}",
"LabelRunningTimeValue": "Laufzeit: {0}", "LabelRunningTimeValue": "Laufzeit: {0}",
"Latest": "Neueste", "Latest": "Neueste",
"MessageApplicationUpdated": "Jellyfin-Server wurde aktualisiert",
"MessageApplicationUpdatedTo": "Jellyfin-Server wurde auf Version {0} aktualisiert",
"MessageNamedServerConfigurationUpdatedWithValue": "Der Server-Einstellungsbereich {0} wurde aktualisiert",
"MessageServerConfigurationUpdated": "Servereinstellungen wurden aktualisiert",
"MixedContent": "Gemischte Inhalte", "MixedContent": "Gemischte Inhalte",
"Movies": "Filme", "Movies": "Filme",
"Music": "Musik", "Music": "Musik",
@@ -29,7 +46,7 @@
"NewVersionIsAvailable": "Eine neue Jellyfin-Serverversion steht zum Download bereit.", "NewVersionIsAvailable": "Eine neue Jellyfin-Serverversion steht zum Download bereit.",
"NotificationOptionApplicationUpdateAvailable": "Anwendungsaktualisierung verfügbar", "NotificationOptionApplicationUpdateAvailable": "Anwendungsaktualisierung verfügbar",
"NotificationOptionApplicationUpdateInstalled": "Anwendungsaktualisierung installiert", "NotificationOptionApplicationUpdateInstalled": "Anwendungsaktualisierung installiert",
"NotificationOptionAudioPlayback": "Audio wird abgespielt", "NotificationOptionAudioPlayback": "Audiowiedergabe gestartet",
"NotificationOptionAudioPlaybackStopped": "Audiowiedergabe gestoppt", "NotificationOptionAudioPlaybackStopped": "Audiowiedergabe gestoppt",
"NotificationOptionCameraImageUploaded": "Foto hochgeladen", "NotificationOptionCameraImageUploaded": "Foto hochgeladen",
"NotificationOptionInstallationFailed": "Installation fehlgeschlagen", "NotificationOptionInstallationFailed": "Installation fehlgeschlagen",
@@ -40,52 +57,65 @@
"NotificationOptionPluginUpdateInstalled": "Pluginaktualisierung installiert", "NotificationOptionPluginUpdateInstalled": "Pluginaktualisierung installiert",
"NotificationOptionServerRestartRequired": "Serverneustart notwendig", "NotificationOptionServerRestartRequired": "Serverneustart notwendig",
"NotificationOptionTaskFailed": "Geplante Aufgabe fehlgeschlagen", "NotificationOptionTaskFailed": "Geplante Aufgabe fehlgeschlagen",
"NotificationOptionUserLockedOut": "Benutzer gesperrt", "NotificationOptionUserLockedOut": "Benutzer ausgeschlossen",
"NotificationOptionVideoPlayback": "Video wird abgespielt", "NotificationOptionVideoPlayback": "Videowiedergabe gestartet",
"NotificationOptionVideoPlaybackStopped": "Videowiedergabe gestoppt", "NotificationOptionVideoPlaybackStopped": "Videowiedergabe gestoppt",
"Photos": "Fotos", "Photos": "Fotos",
"Playlists": "Wiedergabelisten",
"Plugin": "Plugin",
"PluginInstalledWithName": "{0} wurde installiert", "PluginInstalledWithName": "{0} wurde installiert",
"PluginUninstalledWithName": "{0} wurde deinstalliert", "PluginUninstalledWithName": "{0} wurde deinstalliert",
"PluginUpdatedWithName": "{0} wurde aktualisiert", "PluginUpdatedWithName": "{0} wurde aktualisiert",
"ProviderValue": "Anbieter: {0}",
"ScheduledTaskFailedWithName": "{0} ist fehlgeschlagen", "ScheduledTaskFailedWithName": "{0} ist fehlgeschlagen",
"ScheduledTaskStartedWithName": "{0} wurde gestartet",
"ServerNameNeedsToBeRestarted": "{0} muss neu gestartet werden",
"Shows": "Serien", "Shows": "Serien",
"Songs": "Lieder",
"StartupEmbyServerIsLoading": "Jellyfin-Server lädt. Bitte versuche es gleich noch einmal.", "StartupEmbyServerIsLoading": "Jellyfin-Server lädt. Bitte versuche es gleich noch einmal.",
"SubtitleDownloadFailureForItem": "Download der Untertitel fehlgeschlagen für {0}",
"SubtitleDownloadFailureFromForItem": "Untertitel von {0} für {1} konnten nicht heruntergeladen werden", "SubtitleDownloadFailureFromForItem": "Untertitel von {0} für {1} konnten nicht heruntergeladen werden",
"Sync": "Synchronisation",
"System": "System",
"TvShows": "Serien", "TvShows": "Serien",
"User": "Benutzer",
"UserCreatedWithName": "Benutzer {0} wurde erstellt", "UserCreatedWithName": "Benutzer {0} wurde erstellt",
"UserDeletedWithName": "Benutzer {0} wurde gelöscht", "UserDeletedWithName": "Benutzer {0} wurde gelöscht",
"UserDownloadingItemWithValues": "{0} lädt {1} herunter", "UserDownloadingItemWithValues": "{0} lädt {1} herunter",
"UserLockedOutWithName": "Benutzer {0} wurde gesperrt", "UserLockedOutWithName": "Benutzer {0} wurde ausgeschlossen",
"UserOfflineFromDevice": "{0} wurde getrennt von {1}", "UserOfflineFromDevice": "{0} wurde getrennt von {1}",
"UserOnlineFromDevice": "{0} ist online von {1}", "UserOnlineFromDevice": "{0} ist online von {1}",
"UserPasswordChangedWithName": "Das Passwort für Benutzer {0} wurde geändert", "UserPasswordChangedWithName": "Das Passwort für Benutzer {0} wurde geändert",
"UserPolicyUpdatedWithName": "Benutzerrichtlinie von {0} wurde aktualisiert",
"UserStartedPlayingItemWithValues": "{0} hat die Wiedergabe von {1} auf {2} gestartet", "UserStartedPlayingItemWithValues": "{0} hat die Wiedergabe von {1} auf {2} gestartet",
"UserStoppedPlayingItemWithValues": "{0} hat die Wiedergabe von {1} auf {2} beendet", "UserStoppedPlayingItemWithValues": "{0} hat die Wiedergabe von {1} auf {2} beendet",
"ValueHasBeenAddedToLibrary": "{0} wurde deiner Bibliothek hinzugefügt",
"ValueSpecialEpisodeName": "Extra {0}",
"VersionNumber": "Version {0}", "VersionNumber": "Version {0}",
"TaskDownloadMissingSubtitlesDescription": "Sucht im Internet basierend auf den Metadaten-Einstellungen nach fehlenden Untertiteln.", "TaskDownloadMissingSubtitlesDescription": "Sucht im Internet basierend auf den Metadaten-Einstellungen nach fehlenden Untertiteln.",
"TaskDownloadMissingSubtitles": "Fehlende Untertitel herunterladen", "TaskDownloadMissingSubtitles": "Fehlende Untertitel herunterladen",
"TaskRefreshChannelsDescription": "Aktualisiert Internet-Kanal-Informationen.", "TaskRefreshChannelsDescription": "Aktualisiert Internet-Kanal-Informationen.",
"TaskRefreshChannels": "Kanäle aktualisieren", "TaskRefreshChannels": "Kanäle aktualisieren",
"TaskCleanTranscodeDescription": "Löscht Transkodierungsdateien, die älter als einen Tag sind.", "TaskCleanTranscodeDescription": "Löscht Transkodierungsdateien, die älter als einen Tag sind.",
"TaskCleanTranscode": "Transkodierungsverzeichnis leeren", "TaskCleanTranscode": "Transkodierungs-Verzeichnis aufräumen",
"TaskUpdatePluginsDescription": "Lädt Updates für Plugins herunter, welche für automatische Updates konfiguriert sind und installiert diese.", "TaskUpdatePluginsDescription": "Lädt Updates für Plugins herunter, welche für automatische Updates konfiguriert sind und installiert diese.",
"TaskUpdatePlugins": "Plugins aktualisieren", "TaskUpdatePlugins": "Plugins aktualisieren",
"TaskRefreshPeopleDescription": "Aktualisiert Metadaten für Schauspieler und Regisseure in deinen Bibliotheken.", "TaskRefreshPeopleDescription": "Aktualisiert Metadaten für Schauspieler und Regisseure in deinen Bibliotheken.",
"TaskRefreshPeople": "Personen aktualisieren", "TaskRefreshPeople": "Personen aktualisieren",
"TaskCleanLogsDescription": "Lösche Log-Dateien, die älter als {0} Tage sind.", "TaskCleanLogsDescription": "Lösche Log-Dateien, die älter als {0} Tage sind.",
"TaskCleanLogs": "Protokollverzeichnis leeren", "TaskCleanLogs": "Log-Verzeichnis aufräumen",
"TaskRefreshLibraryDescription": "Durchsucht deine Medienbibliothek nach neuen Dateien und aktualisiert Metadaten.", "TaskRefreshLibraryDescription": "Durchsucht alle Bibliotheken nach neu hinzugefügten Dateien und aktualisiert Metadaten.",
"TaskRefreshLibrary": "Medien-Bibliothek scannen", "TaskRefreshLibrary": "Medien-Bibliothek scannen",
"TaskRefreshChapterImagesDescription": "Erstellt Vorschaubilder für Videokapitel.", "TaskRefreshChapterImagesDescription": "Erstellt Vorschaubilder für Videos, die Kapitel besitzen.",
"TaskRefreshChapterImages": "Kapitelvorschauen erstellen", "TaskRefreshChapterImages": "Kapitel-Bilder extrahieren",
"TaskCleanCacheDescription": "Löscht Cache-Dateien, die vom System nicht mehr benötigt werden.", "TaskCleanCacheDescription": "Löscht vom System nicht mehr benötigte Zwischenspeicherdateien.",
"TaskCleanCache": "Cache-Verzeichnis leeren", "TaskCleanCache": "Zwischenspeicher-Verzeichnis aufräumen",
"TasksChannelsCategory": "Internet-Kanäle", "TasksChannelsCategory": "Internet-Kanäle",
"TasksApplicationCategory": "Anwendung", "TasksApplicationCategory": "Anwendung",
"TasksLibraryCategory": "Bibliothek", "TasksLibraryCategory": "Bibliothek",
"TasksMaintenanceCategory": "Wartung", "TasksMaintenanceCategory": "Wartung",
"TaskCleanActivityLogDescription": "Löscht Aktivitätsprotokolleinträge, die älter als das konfigurierte Alter sind.", "TaskCleanActivityLogDescription": "Löscht Aktivitätsprotokolleinträge, die älter als das konfigurierte Alter sind.",
"TaskCleanActivityLog": "Aktivitätsverlauf bereinigen", "TaskCleanActivityLog": "Aktivitätsprotokolle aufräumen",
"Undefined": "Undefiniert", "Undefined": "Undefiniert",
"Forced": "Erzwungen", "Forced": "Erzwungen",
"Default": "Standard", "Default": "Standard",
@@ -97,6 +127,8 @@
"HearingImpaired": "Hörgeschädigt", "HearingImpaired": "Hörgeschädigt",
"TaskRefreshTrickplayImages": "Trickplay-Bilder generieren", "TaskRefreshTrickplayImages": "Trickplay-Bilder generieren",
"TaskRefreshTrickplayImagesDescription": "Erstellt ein Trickplay-Vorschauen für Videos in aktivierten Bibliotheken.", "TaskRefreshTrickplayImagesDescription": "Erstellt ein Trickplay-Vorschauen für Videos in aktivierten Bibliotheken.",
"TaskCleanCollectionsAndPlaylists": "Sammlungen und Playlisten aufräumen",
"TaskCleanCollectionsAndPlaylistsDescription": "Löscht nicht mehr vorhandene Einträge aus den Sammlungen und Playlisten.",
"TaskAudioNormalization": "Audio Normalisierung", "TaskAudioNormalization": "Audio Normalisierung",
"TaskAudioNormalizationDescription": "Durchsucht Dateien nach Audionormalisierungsdaten.", "TaskAudioNormalizationDescription": "Durchsucht Dateien nach Audionormalisierungsdaten.",
"TaskDownloadMissingLyricsDescription": "Lädt Songtexte herunter", "TaskDownloadMissingLyricsDescription": "Lädt Songtexte herunter",
@@ -106,6 +138,5 @@
"TaskMoveTrickplayImages": "Verzeichnis für Trickplay-Bilder migrieren", "TaskMoveTrickplayImages": "Verzeichnis für Trickplay-Bilder migrieren",
"TaskMoveTrickplayImagesDescription": "Trickplay-Bilder werden entsprechend der Bibliothekseinstellungen verschoben.", "TaskMoveTrickplayImagesDescription": "Trickplay-Bilder werden entsprechend der Bibliothekseinstellungen verschoben.",
"CleanupUserDataTask": "Aufgabe zur Bereinigung von Benutzerdaten", "CleanupUserDataTask": "Aufgabe zur Bereinigung von Benutzerdaten",
"CleanupUserDataTaskDescription": "Löscht alle Benutzerdaten (Abspielstatus, Favoritenstatus, usw.) von Medien, die seit mindestens 90 Tagen nicht mehr vorhanden sind.", "CleanupUserDataTaskDescription": "Löscht alle Benutzerdaten (Abspielstatus, Favoritenstatus, usw.) von Medien, die seit mindestens 90 Tagen nicht mehr vorhanden sind."
"Original": "Original"
} }

View File

@@ -1,24 +1,41 @@
{ {
"Albums": "Άλμπουμ",
"AppDeviceValues": "Εφαρμογή: {0}, Συσκευή: {1}", "AppDeviceValues": "Εφαρμογή: {0}, Συσκευή: {1}",
"Application": "Εφαρμογή",
"Artists": "Καλλιτέχνες", "Artists": "Καλλιτέχνες",
"AuthenticationSucceededWithUserName": "Ο χρήστης {0} επαληθεύτηκε επιτυχώς", "AuthenticationSucceededWithUserName": "Ο χρήστης {0} επαληθεύτηκε επιτυχώς",
"Books": "Βιβλία", "Books": "Βιβλία",
"CameraImageUploadedFrom": "Μια νέα φωτογραφία φορτώθηκε από {0}",
"Channels": "Κανάλια",
"ChapterNameValue": "Κεφάλαιο {0}", "ChapterNameValue": "Κεφάλαιο {0}",
"Collections": "Συλλογές", "Collections": "Συλλογές",
"DeviceOfflineWithName": "Ο/Η {0} αποσυνδέθηκε",
"DeviceOnlineWithName": "Ο/Η {0} συνδέθηκε",
"FailedLoginAttemptWithUserName": "Αποτυχία προσπάθειας σύνδεσης από {0}", "FailedLoginAttemptWithUserName": "Αποτυχία προσπάθειας σύνδεσης από {0}",
"Favorites": "Αγαπημένα", "Favorites": "Αγαπημένα",
"Folders": "Φάκελοι", "Folders": "Φάκελοι",
"Genres": "Είδη", "Genres": "Είδη",
"HeaderAlbumArtists": "Καλλιτέχνες άλμπουμ",
"HeaderContinueWatching": "Συνεχίστε την παρακολούθηση", "HeaderContinueWatching": "Συνεχίστε την παρακολούθηση",
"HeaderFavoriteAlbums": "Αγαπημένα Άλμπουμ",
"HeaderFavoriteArtists": "Αγαπημένοι Καλλιτέχνες",
"HeaderFavoriteEpisodes": "Αγαπημένα Επεισόδια", "HeaderFavoriteEpisodes": "Αγαπημένα Επεισόδια",
"HeaderFavoriteShows": "Αγαπημένες Σειρές", "HeaderFavoriteShows": "Αγαπημένες Σειρές",
"HeaderFavoriteSongs": "Αγαπημένα Τραγούδια",
"HeaderLiveTV": "Ζωντανή Τηλεόραση", "HeaderLiveTV": "Ζωντανή Τηλεόραση",
"HeaderNextUp": "Επόμενο", "HeaderNextUp": "Επόμενο",
"HeaderRecordingGroups": "Ομάδες Ηχογράφησης",
"HomeVideos": "Προσωπικά Βίντεο", "HomeVideos": "Προσωπικά Βίντεο",
"Inherit": "Κληρονόμηση", "Inherit": "Κληρονόμηση",
"ItemAddedWithName": "Το {0} προστέθηκε στη βιβλιοθήκη",
"ItemRemovedWithName": "Το {0} διαγράφτηκε από τη βιβλιοθήκη",
"LabelIpAddressValue": "Διεύθυνση IP: {0}", "LabelIpAddressValue": "Διεύθυνση IP: {0}",
"LabelRunningTimeValue": "Διάρκεια: {0}", "LabelRunningTimeValue": "Διάρκεια: {0}",
"Latest": "Πρόσφατα", "Latest": "Πρόσφατα",
"MessageApplicationUpdated": "Ο διακομιστής Jellyfin έχει ενημερωθεί",
"MessageApplicationUpdatedTo": "Ο διακομιστής Jellyfin αναβαθμίστηκε στην έκδοση {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Η ενότητα {0} ρύθμισης παραμέτρων του διακομιστή έχει ενημερωθεί",
"MessageServerConfigurationUpdated": "Η ρύθμιση παραμέτρων του διακομιστή έχει ενημερωθεί",
"MixedContent": "Ανάμεικτο Περιεχόμενο", "MixedContent": "Ανάμεικτο Περιεχόμενο",
"Movies": "Ταινίες", "Movies": "Ταινίες",
"Music": "Μουσική", "Music": "Μουσική",
@@ -44,14 +61,24 @@
"NotificationOptionVideoPlayback": "Η αναπαραγωγή βίντεο ξεκίνησε", "NotificationOptionVideoPlayback": "Η αναπαραγωγή βίντεο ξεκίνησε",
"NotificationOptionVideoPlaybackStopped": "Η αναπαραγωγή βίντεο σταμάτησε", "NotificationOptionVideoPlaybackStopped": "Η αναπαραγωγή βίντεο σταμάτησε",
"Photos": "Φωτογραφίες", "Photos": "Φωτογραφίες",
"Playlists": "Λίστες αναπαραγωγής",
"Plugin": "Πρόσθετο",
"PluginInstalledWithName": "Το {0} εγκαταστάθηκε", "PluginInstalledWithName": "Το {0} εγκαταστάθηκε",
"PluginUninstalledWithName": "Το {0} έχει απεγκατασταθεί", "PluginUninstalledWithName": "Το {0} έχει απεγκατασταθεί",
"PluginUpdatedWithName": "Το {0} ενημερώθηκε", "PluginUpdatedWithName": "Το {0} ενημερώθηκε",
"ProviderValue": "Πάροχος: {0}",
"ScheduledTaskFailedWithName": "{0} αποτυχία", "ScheduledTaskFailedWithName": "{0} αποτυχία",
"ScheduledTaskStartedWithName": "{0} ξεκίνησε",
"ServerNameNeedsToBeRestarted": "{0} χρειάζεται επανεκκίνηση",
"Shows": "Σειρές", "Shows": "Σειρές",
"Songs": "Τραγούδια",
"StartupEmbyServerIsLoading": "Ο διακομιστής Jellyfin φορτώνει. Περιμένετε λίγο και δοκιμάστε ξανά.", "StartupEmbyServerIsLoading": "Ο διακομιστής Jellyfin φορτώνει. Περιμένετε λίγο και δοκιμάστε ξανά.",
"SubtitleDownloadFailureForItem": "Οι υπότιτλοι απέτυχαν να κατέβουν για {0}",
"SubtitleDownloadFailureFromForItem": "Αποτυχίες μεταφόρτωσης υποτίτλων από {0} για {1}", "SubtitleDownloadFailureFromForItem": "Αποτυχίες μεταφόρτωσης υποτίτλων από {0} για {1}",
"Sync": "Συγχρονισμός",
"System": "Σύστημα",
"TvShows": "Τηλεοπτικές Σειρές", "TvShows": "Τηλεοπτικές Σειρές",
"User": "Χρήστης",
"UserCreatedWithName": "Ο χρήστης {0} δημιουργήθηκε", "UserCreatedWithName": "Ο χρήστης {0} δημιουργήθηκε",
"UserDeletedWithName": "Ο χρήστης {0} έχει διαγραφεί", "UserDeletedWithName": "Ο χρήστης {0} έχει διαγραφεί",
"UserDownloadingItemWithValues": "{0} κατεβάζει {1}", "UserDownloadingItemWithValues": "{0} κατεβάζει {1}",
@@ -59,8 +86,11 @@
"UserOfflineFromDevice": "{0} αποσυνδέθηκε από {1}", "UserOfflineFromDevice": "{0} αποσυνδέθηκε από {1}",
"UserOnlineFromDevice": "{0} είναι online απο {1}", "UserOnlineFromDevice": "{0} είναι online απο {1}",
"UserPasswordChangedWithName": "Ο κωδικός του χρήστη {0} έχει αλλάξει", "UserPasswordChangedWithName": "Ο κωδικός του χρήστη {0} έχει αλλάξει",
"UserPolicyUpdatedWithName": "Η πολιτική χρήστη έχει ενημερωθεί για {0}",
"UserStartedPlayingItemWithValues": "{0} παίζει {1} σε {2}", "UserStartedPlayingItemWithValues": "{0} παίζει {1} σε {2}",
"UserStoppedPlayingItemWithValues": "{0} τελείωσε να παίζει {1} σε {2}", "UserStoppedPlayingItemWithValues": "{0} τελείωσε να παίζει {1} σε {2}",
"ValueHasBeenAddedToLibrary": "{0} προστέθηκαν στη βιβλιοθήκη πολυμέσων σας",
"ValueSpecialEpisodeName": "Σπέσιαλ - {0}",
"VersionNumber": "Έκδοση {0}", "VersionNumber": "Έκδοση {0}",
"TaskRefreshPeople": "Ανανέωση Ατόμων", "TaskRefreshPeople": "Ανανέωση Ατόμων",
"TaskCleanLogsDescription": "Διαγράφει αρχεία καταγραφής που είναι πάνω από {0} ημέρες.", "TaskCleanLogsDescription": "Διαγράφει αρχεία καταγραφής που είναι πάνω από {0} ημέρες.",
@@ -99,6 +129,8 @@
"TaskRefreshTrickplayImagesDescription": "Δημιουργεί προεπισκοπήσεις trickplay για βίντεο σε ενεργοποιημένες βιβλιοθήκες.", "TaskRefreshTrickplayImagesDescription": "Δημιουργεί προεπισκοπήσεις trickplay για βίντεο σε ενεργοποιημένες βιβλιοθήκες.",
"TaskAudioNormalization": "Ομοιομορφία ήχου", "TaskAudioNormalization": "Ομοιομορφία ήχου",
"TaskAudioNormalizationDescription": "Ανίχνευση αρχείων για δεδομένα ομοιομορφίας ήχου.", "TaskAudioNormalizationDescription": "Ανίχνευση αρχείων για δεδομένα ομοιομορφίας ήχου.",
"TaskCleanCollectionsAndPlaylists": "Καθαρισμός συλλογών και λιστών αναπαραγωγής",
"TaskCleanCollectionsAndPlaylistsDescription": "Αφαιρούνται στοιχεία από τις συλλογές και τις λίστες αναπαραγωγής που δεν υπάρχουν πλέον.",
"TaskMoveTrickplayImages": "Αλλαγή τοποθεσίας εικόνων Trickplay", "TaskMoveTrickplayImages": "Αλλαγή τοποθεσίας εικόνων Trickplay",
"TaskDownloadMissingLyrics": "Λήψη στίχων που λείπουν", "TaskDownloadMissingLyrics": "Λήψη στίχων που λείπουν",
"TaskMoveTrickplayImagesDescription": "Μετακινεί τα υπάρχοντα αρχεία trickplay σύμφωνα με τις ρυθμίσεις της βιβλιοθήκης.", "TaskMoveTrickplayImagesDescription": "Μετακινεί τα υπάρχοντα αρχεία trickplay σύμφωνα με τις ρυθμίσεις της βιβλιοθήκης.",

View File

@@ -1,24 +1,41 @@
{ {
"Albums": "Albums",
"AppDeviceValues": "App: {0}, Device: {1}", "AppDeviceValues": "App: {0}, Device: {1}",
"Application": "Application",
"Artists": "Artists", "Artists": "Artists",
"AuthenticationSucceededWithUserName": "{0} successfully authenticated", "AuthenticationSucceededWithUserName": "{0} successfully authenticated",
"Books": "Books", "Books": "Books",
"CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
"Channels": "Channels",
"ChapterNameValue": "Chapter {0}", "ChapterNameValue": "Chapter {0}",
"Collections": "Collections", "Collections": "Collections",
"DeviceOfflineWithName": "{0} has disconnected",
"DeviceOnlineWithName": "{0} is connected",
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}", "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
"Favorites": "Favourites", "Favorites": "Favourites",
"Folders": "Folders", "Folders": "Folders",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Album artists",
"HeaderContinueWatching": "Continue Watching", "HeaderContinueWatching": "Continue Watching",
"HeaderFavoriteAlbums": "Favourite Albums",
"HeaderFavoriteArtists": "Favourite Artists",
"HeaderFavoriteEpisodes": "Favourite Episodes", "HeaderFavoriteEpisodes": "Favourite Episodes",
"HeaderFavoriteShows": "Favourite Shows", "HeaderFavoriteShows": "Favourite Shows",
"HeaderFavoriteSongs": "Favourite Songs",
"HeaderLiveTV": "Live TV", "HeaderLiveTV": "Live TV",
"HeaderNextUp": "Next Up", "HeaderNextUp": "Next Up",
"HeaderRecordingGroups": "Recording Groups",
"HomeVideos": "Home Videos", "HomeVideos": "Home Videos",
"Inherit": "Inherit", "Inherit": "Inherit",
"ItemAddedWithName": "{0} was added to the library",
"ItemRemovedWithName": "{0} was removed from the library",
"LabelIpAddressValue": "IP address: {0}", "LabelIpAddressValue": "IP address: {0}",
"LabelRunningTimeValue": "Running time: {0}", "LabelRunningTimeValue": "Running time: {0}",
"Latest": "Latest", "Latest": "Latest",
"MessageApplicationUpdated": "Jellyfin Server has been updated",
"MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
"MessageServerConfigurationUpdated": "Server configuration has been updated",
"MixedContent": "Mixed content", "MixedContent": "Mixed content",
"Movies": "Movies", "Movies": "Movies",
"Music": "Music", "Music": "Music",
@@ -44,14 +61,24 @@
"NotificationOptionVideoPlayback": "Video playback started", "NotificationOptionVideoPlayback": "Video playback started",
"NotificationOptionVideoPlaybackStopped": "Video playback stopped", "NotificationOptionVideoPlaybackStopped": "Video playback stopped",
"Photos": "Photos", "Photos": "Photos",
"Playlists": "Playlists",
"Plugin": "Plugin",
"PluginInstalledWithName": "{0} was installed", "PluginInstalledWithName": "{0} was installed",
"PluginUninstalledWithName": "{0} was uninstalled", "PluginUninstalledWithName": "{0} was uninstalled",
"PluginUpdatedWithName": "{0} was updated", "PluginUpdatedWithName": "{0} was updated",
"ProviderValue": "Provider: {0}",
"ScheduledTaskFailedWithName": "{0} failed", "ScheduledTaskFailedWithName": "{0} failed",
"ScheduledTaskStartedWithName": "{0} started",
"ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
"Shows": "Shows", "Shows": "Shows",
"Songs": "Songs",
"StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.", "StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Sync": "Sync",
"System": "System",
"TvShows": "TV Shows", "TvShows": "TV Shows",
"User": "User",
"UserCreatedWithName": "User {0} has been created", "UserCreatedWithName": "User {0} has been created",
"UserDeletedWithName": "User {0} has been deleted", "UserDeletedWithName": "User {0} has been deleted",
"UserDownloadingItemWithValues": "{0} is downloading {1}", "UserDownloadingItemWithValues": "{0} is downloading {1}",
@@ -59,8 +86,11 @@
"UserOfflineFromDevice": "{0} has disconnected from {1}", "UserOfflineFromDevice": "{0} has disconnected from {1}",
"UserOnlineFromDevice": "{0} is online from {1}", "UserOnlineFromDevice": "{0} is online from {1}",
"UserPasswordChangedWithName": "Password has been changed for user {0}", "UserPasswordChangedWithName": "Password has been changed for user {0}",
"UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"UserStartedPlayingItemWithValues": "{0} has started playing {1}", "UserStartedPlayingItemWithValues": "{0} has started playing {1}",
"UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}", "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
"ValueSpecialEpisodeName": "Special - {0}",
"VersionNumber": "Version {0}", "VersionNumber": "Version {0}",
"TaskDownloadMissingSubtitlesDescription": "Searches the internet for missing subtitles based on metadata configuration.", "TaskDownloadMissingSubtitlesDescription": "Searches the internet for missing subtitles based on metadata configuration.",
"TaskDownloadMissingSubtitles": "Download missing subtitles", "TaskDownloadMissingSubtitles": "Download missing subtitles",
@@ -97,6 +127,8 @@
"HearingImpaired": "Hearing Impaired", "HearingImpaired": "Hearing Impaired",
"TaskRefreshTrickplayImages": "Generate Trickplay Images", "TaskRefreshTrickplayImages": "Generate Trickplay Images",
"TaskRefreshTrickplayImagesDescription": "Creates trickplay previews for videos in enabled libraries.", "TaskRefreshTrickplayImagesDescription": "Creates trickplay previews for videos in enabled libraries.",
"TaskCleanCollectionsAndPlaylists": "Clean up collections and playlists",
"TaskCleanCollectionsAndPlaylistsDescription": "Removes items from collections and playlists that no longer exist.",
"TaskAudioNormalization": "Audio Normalisation", "TaskAudioNormalization": "Audio Normalisation",
"TaskAudioNormalizationDescription": "Scans files for audio normalisation data.", "TaskAudioNormalizationDescription": "Scans files for audio normalisation data.",
"TaskDownloadMissingLyrics": "Download missing lyrics", "TaskDownloadMissingLyrics": "Download missing lyrics",

View File

@@ -1,29 +1,45 @@
{ {
"Albums": "Albums",
"AppDeviceValues": "App: {0}, Device: {1}", "AppDeviceValues": "App: {0}, Device: {1}",
"Application": "Application",
"Artists": "Artists", "Artists": "Artists",
"AuthenticationSucceededWithUserName": "{0} successfully authenticated", "AuthenticationSucceededWithUserName": "{0} successfully authenticated",
"Books": "Books", "Books": "Books",
"CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
"Channels": "Channels",
"ChapterNameValue": "Chapter {0}", "ChapterNameValue": "Chapter {0}",
"Collections": "Collections", "Collections": "Collections",
"Default": "Default", "Default": "Default",
"DeviceOfflineWithName": "{0} has disconnected",
"DeviceOnlineWithName": "{0} is connected",
"External": "External", "External": "External",
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}", "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
"Favorites": "Favorites", "Favorites": "Favorites",
"Folders": "Folders", "Folders": "Folders",
"Forced": "Forced", "Forced": "Forced",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Album artists",
"HeaderContinueWatching": "Continue Watching", "HeaderContinueWatching": "Continue Watching",
"HeaderFavoriteAlbums": "Favorite Albums",
"HeaderFavoriteArtists": "Favorite Artists",
"HeaderFavoriteEpisodes": "Favorite Episodes", "HeaderFavoriteEpisodes": "Favorite Episodes",
"HeaderFavoriteShows": "Favorite Shows", "HeaderFavoriteShows": "Favorite Shows",
"HeaderFavoriteSongs": "Favorite Songs",
"HeaderLiveTV": "Live TV", "HeaderLiveTV": "Live TV",
"HeaderNextUp": "Next Up", "HeaderNextUp": "Next Up",
"HeaderRecordingGroups": "Recording Groups",
"HearingImpaired": "Hearing Impaired", "HearingImpaired": "Hearing Impaired",
"HomeVideos": "Home Videos", "HomeVideos": "Home Videos",
"Inherit": "Inherit", "Inherit": "Inherit",
"ItemAddedWithName": "{0} was added to the library",
"ItemRemovedWithName": "{0} was removed from the library",
"LabelIpAddressValue": "IP address: {0}", "LabelIpAddressValue": "IP address: {0}",
"LabelRunningTimeValue": "Running time: {0}", "LabelRunningTimeValue": "Running time: {0}",
"Latest": "Latest", "Latest": "Latest",
"LyricDownloadFailureFromForItem": "Lyrics failed to download from {0} for {1}", "MessageApplicationUpdated": "Jellyfin Server has been updated",
"MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
"MessageServerConfigurationUpdated": "Server configuration has been updated",
"MixedContent": "Mixed content", "MixedContent": "Mixed content",
"Movies": "Movies", "Movies": "Movies",
"Music": "Music", "Music": "Music",
@@ -48,17 +64,25 @@
"NotificationOptionUserLockedOut": "User locked out", "NotificationOptionUserLockedOut": "User locked out",
"NotificationOptionVideoPlayback": "Video playback started", "NotificationOptionVideoPlayback": "Video playback started",
"NotificationOptionVideoPlaybackStopped": "Video playback stopped", "NotificationOptionVideoPlaybackStopped": "Video playback stopped",
"Original": "Original",
"Photos": "Photos", "Photos": "Photos",
"Playlists": "Playlists",
"Plugin": "Plugin",
"PluginInstalledWithName": "{0} was installed", "PluginInstalledWithName": "{0} was installed",
"PluginUninstalledWithName": "{0} was uninstalled", "PluginUninstalledWithName": "{0} was uninstalled",
"PluginUpdatedWithName": "{0} was updated", "PluginUpdatedWithName": "{0} was updated",
"ProviderValue": "Provider: {0}",
"ScheduledTaskFailedWithName": "{0} failed", "ScheduledTaskFailedWithName": "{0} failed",
"ScheduledTaskStartedWithName": "{0} started",
"ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
"Shows": "Shows", "Shows": "Shows",
"Songs": "Songs",
"StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.", "StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.",
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"Sync": "Sync",
"System": "System",
"TvShows": "TV Shows", "TvShows": "TV Shows",
"Undefined": "Undefined", "Undefined": "Undefined",
"User": "User",
"UserCreatedWithName": "User {0} has been created", "UserCreatedWithName": "User {0} has been created",
"UserDeletedWithName": "User {0} has been deleted", "UserDeletedWithName": "User {0} has been deleted",
"UserDownloadingItemWithValues": "{0} is downloading {1}", "UserDownloadingItemWithValues": "{0} is downloading {1}",
@@ -66,8 +90,11 @@
"UserOfflineFromDevice": "{0} has disconnected from {1}", "UserOfflineFromDevice": "{0} has disconnected from {1}",
"UserOnlineFromDevice": "{0} is online from {1}", "UserOnlineFromDevice": "{0} is online from {1}",
"UserPasswordChangedWithName": "Password has been changed for user {0}", "UserPasswordChangedWithName": "Password has been changed for user {0}",
"UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}", "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
"UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}", "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
"ValueSpecialEpisodeName": "Special - {0}",
"VersionNumber": "Version {0}", "VersionNumber": "Version {0}",
"TasksMaintenanceCategory": "Maintenance", "TasksMaintenanceCategory": "Maintenance",
"TasksLibraryCategory": "Library", "TasksLibraryCategory": "Library",
@@ -103,6 +130,8 @@
"TaskOptimizeDatabaseDescription": "Compacts database and truncates free space. Running this task after scanning the library or doing other changes that imply database modifications might improve performance.", "TaskOptimizeDatabaseDescription": "Compacts database and truncates free space. Running this task after scanning the library or doing other changes that imply database modifications might improve performance.",
"TaskKeyframeExtractor": "Keyframe Extractor", "TaskKeyframeExtractor": "Keyframe Extractor",
"TaskKeyframeExtractorDescription": "Extracts keyframes from video files to create more precise HLS playlists. This task may run for a long time.", "TaskKeyframeExtractorDescription": "Extracts keyframes from video files to create more precise HLS playlists. This task may run for a long time.",
"TaskCleanCollectionsAndPlaylists": "Clean up collections and playlists",
"TaskCleanCollectionsAndPlaylistsDescription": "Removes items from collections and playlists that no longer exist.",
"TaskExtractMediaSegments": "Media Segment Scan", "TaskExtractMediaSegments": "Media Segment Scan",
"TaskExtractMediaSegmentsDescription": "Extracts or obtains media segments from MediaSegment enabled plugins.", "TaskExtractMediaSegmentsDescription": "Extracts or obtains media segments from MediaSegment enabled plugins.",
"TaskMoveTrickplayImages": "Migrate Trickplay Image Location", "TaskMoveTrickplayImages": "Migrate Trickplay Image Location",

View File

@@ -7,22 +7,35 @@
"NameInstallFailed": "{0} instalado fiaskis", "NameInstallFailed": "{0} instalado fiaskis",
"Music": "Muziko", "Music": "Muziko",
"Movies": "Filmoj", "Movies": "Filmoj",
"ItemRemovedWithName": "{0} forigis el la plurmediteko",
"ItemAddedWithName": "{0} aldonis al la plurmediteko",
"HeaderLiveTV": "TV-etero", "HeaderLiveTV": "TV-etero",
"HeaderContinueWatching": "Daŭrigi Spektadon", "HeaderContinueWatching": "Daŭrigi Spektadon",
"HeaderAlbumArtists": "Artistoj de albumo",
"Folders": "Dosierujoj", "Folders": "Dosierujoj",
"DeviceOnlineWithName": "{0} estas konektita",
"Default": "Defaŭlte", "Default": "Defaŭlte",
"Collections": "Kolektoj", "Collections": "Kolektoj",
"ChapterNameValue": "Ĉapitro {0}", "ChapterNameValue": "Ĉapitro {0}",
"Channels": "Kanaloj",
"Books": "Libroj", "Books": "Libroj",
"Artists": "Artistoj", "Artists": "Artistoj",
"Application": "Aplikaĵo",
"AppDeviceValues": "Aplikaĵo: {0}, Aparato: {1}", "AppDeviceValues": "Aplikaĵo: {0}, Aparato: {1}",
"Albums": "Albumoj",
"TasksLibraryCategory": "Plurmediteko", "TasksLibraryCategory": "Plurmediteko",
"VersionNumber": "Versio {0}", "VersionNumber": "Versio {0}",
"UserDownloadingItemWithValues": "{0} elŝutas {1}", "UserDownloadingItemWithValues": "{0} elŝutas {1}",
"UserCreatedWithName": "Uzanto {0} kreiĝis", "UserCreatedWithName": "Uzanto {0} kreiĝis",
"User": "Uzanto",
"System": "Sistemo",
"Songs": "Kantoj",
"ScheduledTaskStartedWithName": "{0} lanĉis",
"ScheduledTaskFailedWithName": "{0} malsukcesis", "ScheduledTaskFailedWithName": "{0} malsukcesis",
"PluginUninstalledWithName": "{0} malinstaliĝis", "PluginUninstalledWithName": "{0} malinstaliĝis",
"PluginInstalledWithName": "{0} instaliĝis", "PluginInstalledWithName": "{0} instaliĝis",
"Plugin": "Kromprogramo",
"Playlists": "Ludlistoj",
"Photos": "Fotoj", "Photos": "Fotoj",
"NotificationOptionPluginUninstalled": "Kromprogramo malinstaliĝis", "NotificationOptionPluginUninstalled": "Kromprogramo malinstaliĝis",
"NotificationOptionNewLibraryContent": "Nova enhavo aldoniĝis", "NotificationOptionNewLibraryContent": "Nova enhavo aldoniĝis",
@@ -30,28 +43,36 @@
"MusicVideos": "Muzikvideoj", "MusicVideos": "Muzikvideoj",
"LabelIpAddressValue": "IP-adreso: {0}", "LabelIpAddressValue": "IP-adreso: {0}",
"Genres": "Ĝenroj", "Genres": "Ĝenroj",
"DeviceOfflineWithName": "{0} malkonektis",
"HeaderFavoriteArtists": "Favorataj Artistoj",
"Shows": "Serioj", "Shows": "Serioj",
"HeaderFavoriteShows": "Favorataj Serioj", "HeaderFavoriteShows": "Favorataj Serioj",
"TvShows": "TV-serioj", "TvShows": "TV-serioj",
"Favorites": "Favorataj", "Favorites": "Favorataj",
"TaskCleanLogs": "Purigi Ĵurnalan Katalogon", "TaskCleanLogs": "Purigi Ĵurnalan Katalogon",
"TaskRefreshLibrary": "Skani Plurmeditekon", "TaskRefreshLibrary": "Skani Plurmeditekon",
"ValueSpecialEpisodeName": "Speciala - {0}",
"TaskOptimizeDatabase": "Optimumigi datenbazon", "TaskOptimizeDatabase": "Optimumigi datenbazon",
"TaskRefreshChannels": "Refreŝigi Kanalojn", "TaskRefreshChannels": "Refreŝigi Kanalojn",
"TaskUpdatePlugins": "Ĝisdatigi Kromprogramojn", "TaskUpdatePlugins": "Ĝisdatigi Kromprogramojn",
"TaskRefreshPeople": "Refreŝigi Homojn", "TaskRefreshPeople": "Refreŝigi Homojn",
"TasksChannelsCategory": "Interretaj Kanaloj", "TasksChannelsCategory": "Interretaj Kanaloj",
"ProviderValue": "Provizanto: {0}",
"NotificationOptionPluginError": "Kromprogramo malsukcesis", "NotificationOptionPluginError": "Kromprogramo malsukcesis",
"MixedContent": "Miksita enhavo", "MixedContent": "Miksita enhavo",
"TasksApplicationCategory": "Aplikaĵo", "TasksApplicationCategory": "Aplikaĵo",
"TasksMaintenanceCategory": "Prizorgado", "TasksMaintenanceCategory": "Prizorgado",
"Undefined": "Nedifinita", "Undefined": "Nedifinita",
"Sync": "Sinkronigo",
"Latest": "Plej novaj", "Latest": "Plej novaj",
"Inherit": "Hereda", "Inherit": "Hereda",
"HomeVideos": "Hejmaj Videoj", "HomeVideos": "Hejmaj Videoj",
"HeaderNextUp": "Sekva Plue", "HeaderNextUp": "Sekva Plue",
"HeaderFavoriteSongs": "Favorataj Kantoj",
"HeaderFavoriteEpisodes": "Favorataj Epizodoj", "HeaderFavoriteEpisodes": "Favorataj Epizodoj",
"HeaderFavoriteAlbums": "Favorataj Albumoj",
"Forced": "Forcita", "Forced": "Forcita",
"ServerNameNeedsToBeRestarted": "{0} devas esti relanĉita",
"NotificationOptionVideoPlayback": "La videoludado lanĉis", "NotificationOptionVideoPlayback": "La videoludado lanĉis",
"NotificationOptionServerRestartRequired": "Servila relanĉigo bezonata", "NotificationOptionServerRestartRequired": "Servila relanĉigo bezonata",
"TaskOptimizeDatabaseDescription": "Kompaktigas datenbazon kaj trunkas liberan lokon. Lanĉi ĉi tiun taskon post la plurmediteka skanado aŭ fari aliajn ŝanĝojn, kiuj implicas datenbazajn modifojn, povus plibonigi rendimenton.", "TaskOptimizeDatabaseDescription": "Kompaktigas datenbazon kaj trunkas liberan lokon. Lanĉi ĉi tiun taskon post la plurmediteka skanado aŭ fari aliajn ŝanĝojn, kiuj implicas datenbazajn modifojn, povus plibonigi rendimenton.",
@@ -64,16 +85,22 @@
"TaskCleanCacheDescription": "Forigas stapla dosierojn ne plu necesajn de la sistemo.", "TaskCleanCacheDescription": "Forigas stapla dosierojn ne plu necesajn de la sistemo.",
"TaskCleanActivityLogDescription": "Forigas aktivecan ĵurnalaĵojn pli malnovajn ol la agordita aĝo.", "TaskCleanActivityLogDescription": "Forigas aktivecan ĵurnalaĵojn pli malnovajn ol la agordita aĝo.",
"TaskCleanTranscodeDescription": "Forigas transkodajn dosierojn aĝajn pli ol unu tagon.", "TaskCleanTranscodeDescription": "Forigas transkodajn dosierojn aĝajn pli ol unu tagon.",
"ValueHasBeenAddedToLibrary": "{0} estis aldonita al via plurmediteko",
"SubtitleDownloadFailureFromForItem": "Subtekstoj malsukcesis elŝuti de {0} por {1}", "SubtitleDownloadFailureFromForItem": "Subtekstoj malsukcesis elŝuti de {0} por {1}",
"StartupEmbyServerIsLoading": "Jellyfin Server ŝarĝas. Provi denove baldaŭ.", "StartupEmbyServerIsLoading": "Jellyfin Server ŝarĝas. Provi denove baldaŭ.",
"TaskRefreshChapterImagesDescription": "Kreas bildetojn por videoj kiuj havas ĉapitrojn.", "TaskRefreshChapterImagesDescription": "Kreas bildetojn por videoj kiuj havas ĉapitrojn.",
"UserStoppedPlayingItemWithValues": "{0} finis ludi {1} ĉe {2}", "UserStoppedPlayingItemWithValues": "{0} finis ludi {1} ĉe {2}",
"UserPolicyUpdatedWithName": "Uzanta politiko estis ĝisdatigita por {0}",
"UserPasswordChangedWithName": "Pasvorto estis ŝanĝita por uzanto {0}", "UserPasswordChangedWithName": "Pasvorto estis ŝanĝita por uzanto {0}",
"UserStartedPlayingItemWithValues": "{0} ludas {1} ĉe {2}", "UserStartedPlayingItemWithValues": "{0} ludas {1} ĉe {2}",
"UserLockedOutWithName": "Uzanto {0} estas elŝlosita", "UserLockedOutWithName": "Uzanto {0} estas elŝlosita",
"UserOnlineFromDevice": "{0} estas enreta de {1}", "UserOnlineFromDevice": "{0} estas enreta de {1}",
"UserOfflineFromDevice": "{0} malkonektis de {1}", "UserOfflineFromDevice": "{0} malkonektis de {1}",
"UserDeletedWithName": "Uzanto {0} estis forigita", "UserDeletedWithName": "Uzanto {0} estis forigita",
"MessageServerConfigurationUpdated": "Servila agordaro estis ĝisdatigita",
"MessageNamedServerConfigurationUpdatedWithValue": "Servila agorda sekcio {0} estis ĝisdatigita",
"MessageApplicationUpdatedTo": "Jellyfin Server estis ĝisdatigita al {0}",
"MessageApplicationUpdated": "Jellyfin Server estis ĝisdatigita",
"TaskRefreshChannelsDescription": "Refreŝigas informon pri interretaj kanaloj.", "TaskRefreshChannelsDescription": "Refreŝigas informon pri interretaj kanaloj.",
"TaskDownloadMissingSubtitles": "Elŝuti mankantajn subtekstojn", "TaskDownloadMissingSubtitles": "Elŝuti mankantajn subtekstojn",
"TaskCleanTranscode": "Malplenigi Transkodadan Katalogon", "TaskCleanTranscode": "Malplenigi Transkodadan Katalogon",
@@ -89,7 +116,9 @@
"NotificationOptionApplicationUpdateInstalled": "Aplikaĵa ĝisdatigo instalita", "NotificationOptionApplicationUpdateInstalled": "Aplikaĵa ĝisdatigo instalita",
"NotificationOptionApplicationUpdateAvailable": "Ĝisdatigo de aplikaĵo havebla", "NotificationOptionApplicationUpdateAvailable": "Ĝisdatigo de aplikaĵo havebla",
"LabelRunningTimeValue": "Ludada tempo: {0}", "LabelRunningTimeValue": "Ludada tempo: {0}",
"HeaderRecordingGroups": "Rikordadaj Grupoj",
"FailedLoginAttemptWithUserName": "Malsukcesa ensaluta provo de {0}", "FailedLoginAttemptWithUserName": "Malsukcesa ensaluta provo de {0}",
"CameraImageUploadedFrom": "Nova kamera bildo estis alŝutita de {0}",
"AuthenticationSucceededWithUserName": "{0} sukcese aŭtentikigis", "AuthenticationSucceededWithUserName": "{0} sukcese aŭtentikigis",
"TaskKeyframeExtractorDescription": "Eltiras ĉefkadrojn el videodosieroj por krei pli precizajn HLS-ludlistojn. Ĉi tiu tasko povas funkcii dum longa tempo.", "TaskKeyframeExtractorDescription": "Eltiras ĉefkadrojn el videodosieroj por krei pli precizajn HLS-ludlistojn. Ĉi tiu tasko povas funkcii dum longa tempo.",
"TaskKeyframeExtractor": "Eltiri Ĉefkadrojn", "TaskKeyframeExtractor": "Eltiri Ĉefkadrojn",

View File

@@ -1,24 +1,41 @@
{ {
"Albums": "Álbumes",
"AppDeviceValues": "Aplicación: {0}, Dispositivo: {1}", "AppDeviceValues": "Aplicación: {0}, Dispositivo: {1}",
"Application": "Aplicación",
"Artists": "Artistas", "Artists": "Artistas",
"AuthenticationSucceededWithUserName": "{0} autenticado correctamente", "AuthenticationSucceededWithUserName": "{0} autenticado correctamente",
"Books": "Libros", "Books": "Libros",
"CameraImageUploadedFrom": "Se ha subido una nueva imagen de cámara desde {0}",
"Channels": "Canales",
"ChapterNameValue": "Capítulo {0}", "ChapterNameValue": "Capítulo {0}",
"Collections": "Colecciones", "Collections": "Colecciones",
"DeviceOfflineWithName": "{0} se ha desconectado",
"DeviceOnlineWithName": "{0} está conectado",
"FailedLoginAttemptWithUserName": "Error al intentar iniciar sesión de {0}", "FailedLoginAttemptWithUserName": "Error al intentar iniciar sesión de {0}",
"Favorites": "Favoritos", "Favorites": "Favoritos",
"Folders": "Carpetas", "Folders": "Carpetas",
"Genres": "Géneros", "Genres": "Géneros",
"HeaderAlbumArtists": "Artistas del álbum",
"HeaderContinueWatching": "Seguir viendo", "HeaderContinueWatching": "Seguir viendo",
"HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderFavoriteArtists": "Artistas favoritos",
"HeaderFavoriteEpisodes": "Capítulos favoritos", "HeaderFavoriteEpisodes": "Capítulos favoritos",
"HeaderFavoriteShows": "Series favoritas", "HeaderFavoriteShows": "Programas favoritos",
"HeaderFavoriteSongs": "Canciones favoritas",
"HeaderLiveTV": "TV en vivo", "HeaderLiveTV": "TV en vivo",
"HeaderNextUp": "Siguiente", "HeaderNextUp": "Siguiente",
"HeaderRecordingGroups": "Grupos de grabación",
"HomeVideos": "Videos caseros", "HomeVideos": "Videos caseros",
"Inherit": "Heredar", "Inherit": "Heredar",
"ItemAddedWithName": "{0} se ha añadido a la biblioteca",
"ItemRemovedWithName": "{0} ha sido eliminado de la biblioteca",
"LabelIpAddressValue": "Dirección IP: {0}", "LabelIpAddressValue": "Dirección IP: {0}",
"LabelRunningTimeValue": "Tiempo de funcionamiento: {0}", "LabelRunningTimeValue": "Tiempo de funcionamiento: {0}",
"Latest": "Últimos", "Latest": "Últimos",
"MessageApplicationUpdated": "El servidor Jellyfin fue actualizado",
"MessageApplicationUpdatedTo": "Se ha actualizado el servidor Jellyfin a la versión {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Se ha actualizado la sección {0} de la configuración del servidor",
"MessageServerConfigurationUpdated": "Se ha actualizado la configuración del servidor",
"MixedContent": "Contenido mezclado", "MixedContent": "Contenido mezclado",
"Movies": "Películas", "Movies": "Películas",
"Music": "Música", "Music": "Música",
@@ -44,14 +61,24 @@
"NotificationOptionVideoPlayback": "Se inició la reproducción de video", "NotificationOptionVideoPlayback": "Se inició la reproducción de video",
"NotificationOptionVideoPlaybackStopped": "Reproducción de video detenida", "NotificationOptionVideoPlaybackStopped": "Reproducción de video detenida",
"Photos": "Fotos", "Photos": "Fotos",
"Playlists": "Listas de reproducción",
"Plugin": "Complemento",
"PluginInstalledWithName": "{0} fue instalado", "PluginInstalledWithName": "{0} fue instalado",
"PluginUninstalledWithName": "{0} fue desinstalado", "PluginUninstalledWithName": "{0} fue desinstalado",
"PluginUpdatedWithName": "{0} fue actualizado", "PluginUpdatedWithName": "{0} fue actualizado",
"ProviderValue": "Proveedor: {0}",
"ScheduledTaskFailedWithName": "{0} falló", "ScheduledTaskFailedWithName": "{0} falló",
"Shows": "Series", "ScheduledTaskStartedWithName": "{0} iniciado",
"ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado",
"Shows": "Programas",
"Songs": "Canciones",
"StartupEmbyServerIsLoading": "El servidor Jellyfin se está cargando. Vuelve a intentarlo en breve.", "StartupEmbyServerIsLoading": "El servidor Jellyfin se está cargando. Vuelve a intentarlo en breve.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "Falló la descarga de subtitulos desde {0} para {1}", "SubtitleDownloadFailureFromForItem": "Falló la descarga de subtitulos desde {0} para {1}",
"Sync": "Sincronizar",
"System": "Sistema",
"TvShows": "Series de TV", "TvShows": "Series de TV",
"User": "Usuario",
"UserCreatedWithName": "El usuario {0} ha sido creado", "UserCreatedWithName": "El usuario {0} ha sido creado",
"UserDeletedWithName": "El usuario {0} ha sido borrado", "UserDeletedWithName": "El usuario {0} ha sido borrado",
"UserDownloadingItemWithValues": "{0} está descargando {1}", "UserDownloadingItemWithValues": "{0} está descargando {1}",
@@ -59,8 +86,11 @@
"UserOfflineFromDevice": "{0} se ha desconectado de {1}", "UserOfflineFromDevice": "{0} se ha desconectado de {1}",
"UserOnlineFromDevice": "{0} está en línea desde {1}", "UserOnlineFromDevice": "{0} está en línea desde {1}",
"UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}", "UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}",
"UserPolicyUpdatedWithName": "Las política de usuario ha sido actualizada para {0}",
"UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}", "UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}",
"UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}", "UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}",
"ValueHasBeenAddedToLibrary": "{0} ha sido añadido a tu biblioteca multimedia",
"ValueSpecialEpisodeName": "Especial - {0}",
"VersionNumber": "Versión {0}", "VersionNumber": "Versión {0}",
"TaskDownloadMissingSubtitlesDescription": "Busca en internet los subtítulos que falten basándose en la configuración de los metadatos.", "TaskDownloadMissingSubtitlesDescription": "Busca en internet los subtítulos que falten basándose en la configuración de los metadatos.",
"TaskDownloadMissingSubtitles": "Descargar subtítulos faltantes", "TaskDownloadMissingSubtitles": "Descargar subtítulos faltantes",
@@ -99,6 +129,8 @@
"TaskRefreshTrickplayImagesDescription": "Crea vistas previas de reproducción engañosa para videos en bibliotecas habilitadas.", "TaskRefreshTrickplayImagesDescription": "Crea vistas previas de reproducción engañosa para videos en bibliotecas habilitadas.",
"TaskAudioNormalization": "Normalización de audio", "TaskAudioNormalization": "Normalización de audio",
"TaskAudioNormalizationDescription": "Escanea archivos en busca de datos de normalización de audio.", "TaskAudioNormalizationDescription": "Escanea archivos en busca de datos de normalización de audio.",
"TaskCleanCollectionsAndPlaylists": "Limpiar colecciones y listas de reproducción",
"TaskCleanCollectionsAndPlaylistsDescription": "Elimina elementos de colecciones y listas de reproducción que ya no existen.",
"TaskDownloadMissingLyrics": "Descargar letra faltante", "TaskDownloadMissingLyrics": "Descargar letra faltante",
"TaskDownloadMissingLyricsDescription": "Descarga letras de canciones", "TaskDownloadMissingLyricsDescription": "Descarga letras de canciones",
"TaskExtractMediaSegments": "Escanear Segmentos de Media", "TaskExtractMediaSegments": "Escanear Segmentos de Media",

View File

@@ -1,24 +1,41 @@
{ {
"Albums": "Álbumes",
"AppDeviceValues": "App: {0}, Dispositivo: {1}", "AppDeviceValues": "App: {0}, Dispositivo: {1}",
"Application": "Aplicación",
"Artists": "Artistas", "Artists": "Artistas",
"AuthenticationSucceededWithUserName": "{0} autenticado con éxito", "AuthenticationSucceededWithUserName": "{0} autenticado con éxito",
"Books": "Libros", "Books": "Libros",
"CameraImageUploadedFrom": "Una nueva imagen de cámara ha sido subida desde {0}",
"Channels": "Canales",
"ChapterNameValue": "Capítulo {0}", "ChapterNameValue": "Capítulo {0}",
"Collections": "Colecciones", "Collections": "Colecciones",
"DeviceOfflineWithName": "{0} se ha desconectado",
"DeviceOnlineWithName": "{0} está conectado",
"FailedLoginAttemptWithUserName": "Intento fallido de inicio de sesión de {0}", "FailedLoginAttemptWithUserName": "Intento fallido de inicio de sesión de {0}",
"Favorites": "Favoritos", "Favorites": "Favoritos",
"Folders": "Carpetas", "Folders": "Carpetas",
"Genres": "Géneros", "Genres": "Géneros",
"HeaderAlbumArtists": "Artistas del Álbum",
"HeaderContinueWatching": "Continuar viendo", "HeaderContinueWatching": "Continuar viendo",
"HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderFavoriteArtists": "Artistas favoritos",
"HeaderFavoriteEpisodes": "Episodios favoritos", "HeaderFavoriteEpisodes": "Episodios favoritos",
"HeaderFavoriteShows": "Programas favoritos", "HeaderFavoriteShows": "Programas favoritos",
"HeaderFavoriteSongs": "Canciones favoritas",
"HeaderLiveTV": "TV en vivo", "HeaderLiveTV": "TV en vivo",
"HeaderNextUp": "A continuación", "HeaderNextUp": "A continuación",
"HeaderRecordingGroups": "Grupos de grabación",
"HomeVideos": "Videos Caseros", "HomeVideos": "Videos Caseros",
"Inherit": "Heredar", "Inherit": "Heredar",
"ItemAddedWithName": "{0} fue agregado a la biblioteca",
"ItemRemovedWithName": "{0} fue removido de la biblioteca",
"LabelIpAddressValue": "Dirección IP: {0}", "LabelIpAddressValue": "Dirección IP: {0}",
"LabelRunningTimeValue": "Tiempo corriendo: {0}", "LabelRunningTimeValue": "Tiempo de reproducción: {0}",
"Latest": "Recientes", "Latest": "Recientes",
"MessageApplicationUpdated": "El servidor Jellyfin ha sido actualizado",
"MessageApplicationUpdatedTo": "El servidor Jellyfin ha sido actualizado a {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Se ha actualizado la sección {0} de la configuración del servidor",
"MessageServerConfigurationUpdated": "Se ha actualizado la configuración del servidor",
"MixedContent": "Contenido mezclado", "MixedContent": "Contenido mezclado",
"Movies": "Películas", "Movies": "Películas",
"Music": "Música", "Music": "Música",
@@ -44,14 +61,24 @@
"NotificationOptionVideoPlayback": "Reproducción de video iniciada", "NotificationOptionVideoPlayback": "Reproducción de video iniciada",
"NotificationOptionVideoPlaybackStopped": "Reproducción de video detenida", "NotificationOptionVideoPlaybackStopped": "Reproducción de video detenida",
"Photos": "Fotos", "Photos": "Fotos",
"Playlists": "Listas de reproducción",
"Plugin": "Complemento",
"PluginInstalledWithName": "{0} fue instalado", "PluginInstalledWithName": "{0} fue instalado",
"PluginUninstalledWithName": "{0} fue desinstalado", "PluginUninstalledWithName": "{0} fue desinstalado",
"PluginUpdatedWithName": "{0} fue actualizado", "PluginUpdatedWithName": "{0} fue actualizado",
"ProviderValue": "Proveedor: {0}",
"ScheduledTaskFailedWithName": "{0} falló", "ScheduledTaskFailedWithName": "{0} falló",
"ScheduledTaskStartedWithName": "{0} iniciado",
"ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado",
"Shows": "Programas", "Shows": "Programas",
"Songs": "Canciones",
"StartupEmbyServerIsLoading": "El servidor Jellyfin está cargando. Por favor, intente de nuevo pronto.", "StartupEmbyServerIsLoading": "El servidor Jellyfin está cargando. Por favor, intente de nuevo pronto.",
"SubtitleDownloadFailureForItem": "Falló la descarga de subtítulos para {0}",
"SubtitleDownloadFailureFromForItem": "Falló la descarga de subtítulos desde {0} para {1}", "SubtitleDownloadFailureFromForItem": "Falló la descarga de subtítulos desde {0} para {1}",
"Sync": "Sincronizar",
"System": "Sistema",
"TvShows": "Programas de TV", "TvShows": "Programas de TV",
"User": "Usuario",
"UserCreatedWithName": "El usuario {0} ha sido creado", "UserCreatedWithName": "El usuario {0} ha sido creado",
"UserDeletedWithName": "El usuario {0} ha sido eliminado", "UserDeletedWithName": "El usuario {0} ha sido eliminado",
"UserDownloadingItemWithValues": "{0} está descargando {1}", "UserDownloadingItemWithValues": "{0} está descargando {1}",
@@ -59,8 +86,11 @@
"UserOfflineFromDevice": "{0} se ha desconectado desde {1}", "UserOfflineFromDevice": "{0} se ha desconectado desde {1}",
"UserOnlineFromDevice": "{0} está en línea desde {1}", "UserOnlineFromDevice": "{0} está en línea desde {1}",
"UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}", "UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}",
"UserPolicyUpdatedWithName": "La política de usuario ha sido actualizada para {0}",
"UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}", "UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}",
"UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}", "UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}",
"ValueHasBeenAddedToLibrary": "{0} se ha añadido a tu biblioteca de medios",
"ValueSpecialEpisodeName": "Especial - {0}",
"VersionNumber": "Versión {0}", "VersionNumber": "Versión {0}",
"TaskDownloadMissingSubtitlesDescription": "Busca subtítulos faltantes en Internet basándose en la configuración de metadatos.", "TaskDownloadMissingSubtitlesDescription": "Busca subtítulos faltantes en Internet basándose en la configuración de metadatos.",
"TaskDownloadMissingSubtitles": "Descargar subtítulos faltantes", "TaskDownloadMissingSubtitles": "Descargar subtítulos faltantes",
@@ -99,6 +129,8 @@
"TaskRefreshTrickplayImages": "Generar imágenes de la barra de reproducción", "TaskRefreshTrickplayImages": "Generar imágenes de la barra de reproducción",
"TaskAudioNormalization": "Normalización de audio", "TaskAudioNormalization": "Normalización de audio",
"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",
"TaskCleanCollectionsAndPlaylistsDescription": "Quita elementos que ya no existen de colecciones y listas de reproducción.",
"TaskDownloadMissingLyrics": "descargar letras que faltan", "TaskDownloadMissingLyrics": "descargar letras que faltan",
"TaskDownloadMissingLyricsDescription": "Descargar letras de canciones", "TaskDownloadMissingLyricsDescription": "Descargar letras de canciones",
"TaskExtractMediaSegments": "Escaneo de segmentos de medios", "TaskExtractMediaSegments": "Escaneo de segmentos de medios",

View File

@@ -1,24 +1,41 @@
{ {
"Albums": "Álbumes",
"AppDeviceValues": "Aplicación: {0}, Dispositivo: {1}", "AppDeviceValues": "Aplicación: {0}, Dispositivo: {1}",
"Application": "Aplicación",
"Artists": "Artistas", "Artists": "Artistas",
"AuthenticationSucceededWithUserName": "{0} autenticado correctamente", "AuthenticationSucceededWithUserName": "{0} autenticado correctamente",
"Books": "Libros", "Books": "Libros",
"CameraImageUploadedFrom": "Se ha subido una nueva imagen por cámara desde {0}",
"Channels": "Canales",
"ChapterNameValue": "Capítulo {0}", "ChapterNameValue": "Capítulo {0}",
"Collections": "Colecciones", "Collections": "Colecciones",
"DeviceOfflineWithName": "{0} se ha desconectado",
"DeviceOnlineWithName": "{0} está conectado",
"FailedLoginAttemptWithUserName": "Intento fallido de inicio de sesión de {0}", "FailedLoginAttemptWithUserName": "Intento fallido de inicio de sesión de {0}",
"Favorites": "Favoritos", "Favorites": "Favoritos",
"Folders": "Carpetas", "Folders": "Carpetas",
"Genres": "Géneros", "Genres": "Géneros",
"HeaderAlbumArtists": "Artistas del álbum",
"HeaderContinueWatching": "Seguir viendo", "HeaderContinueWatching": "Seguir viendo",
"HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderFavoriteArtists": "Artistas favoritos",
"HeaderFavoriteEpisodes": "Episodios favoritos", "HeaderFavoriteEpisodes": "Episodios favoritos",
"HeaderFavoriteShows": "Series favoritas", "HeaderFavoriteShows": "Series favoritas",
"HeaderFavoriteSongs": "Canciones favoritas",
"HeaderLiveTV": "Televisión en directo", "HeaderLiveTV": "Televisión en directo",
"HeaderNextUp": "Siguiente", "HeaderNextUp": "Siguiente",
"HeaderRecordingGroups": "Grupos de grabación",
"HomeVideos": "Vídeos caseros", "HomeVideos": "Vídeos caseros",
"Inherit": "Heredar", "Inherit": "Heredar",
"ItemAddedWithName": "{0} se ha añadido a la biblioteca",
"ItemRemovedWithName": "{0} ha sido eliminado de la biblioteca",
"LabelIpAddressValue": "Dirección IP: {0}", "LabelIpAddressValue": "Dirección IP: {0}",
"LabelRunningTimeValue": "Duración: {0}", "LabelRunningTimeValue": "Duración: {0}",
"Latest": "Últimas", "Latest": "Últimas",
"MessageApplicationUpdated": "Se ha actualizado el servidor Jellyfin",
"MessageApplicationUpdatedTo": "Se ha actualizado el servidor Jellyfin a la versión {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "La sección {0} de configuración del servidor ha sido actualizada",
"MessageServerConfigurationUpdated": "Se ha actualizado la configuración del servidor",
"MixedContent": "Contenido mixto", "MixedContent": "Contenido mixto",
"Movies": "Películas", "Movies": "Películas",
"Music": "Música", "Music": "Música",
@@ -44,14 +61,24 @@
"NotificationOptionVideoPlayback": "Se inició la reproducción de vídeo", "NotificationOptionVideoPlayback": "Se inició la reproducción de vídeo",
"NotificationOptionVideoPlaybackStopped": "Reproducción de vídeo detenida", "NotificationOptionVideoPlaybackStopped": "Reproducción de vídeo detenida",
"Photos": "Fotos", "Photos": "Fotos",
"Playlists": "Listas de reproducción",
"Plugin": "Plugin",
"PluginInstalledWithName": "{0} se ha instalado", "PluginInstalledWithName": "{0} se ha instalado",
"PluginUninstalledWithName": "{0} se ha desinstalado", "PluginUninstalledWithName": "{0} se ha desinstalado",
"PluginUpdatedWithName": "{0} se actualizó", "PluginUpdatedWithName": "{0} se actualizó",
"ProviderValue": "Proveedor: {0}",
"ScheduledTaskFailedWithName": "{0} falló", "ScheduledTaskFailedWithName": "{0} falló",
"ScheduledTaskStartedWithName": "{0} iniciada",
"ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado",
"Shows": "Series", "Shows": "Series",
"Songs": "Canciones",
"StartupEmbyServerIsLoading": "Jellyfin Server se está cargando. Vuelve a intentarlo en breve.", "StartupEmbyServerIsLoading": "Jellyfin Server se está cargando. Vuelve a intentarlo en breve.",
"SubtitleDownloadFailureForItem": "Error al descargar subtítulos para {0}",
"SubtitleDownloadFailureFromForItem": "Fallo en la descarga de subtítulos desde {0} para {1}", "SubtitleDownloadFailureFromForItem": "Fallo en la descarga de subtítulos desde {0} para {1}",
"Sync": "Sincronizar",
"System": "Sistema",
"TvShows": "Series", "TvShows": "Series",
"User": "Usuario",
"UserCreatedWithName": "El usuario {0} ha sido creado", "UserCreatedWithName": "El usuario {0} ha sido creado",
"UserDeletedWithName": "El usuario {0} ha sido borrado", "UserDeletedWithName": "El usuario {0} ha sido borrado",
"UserDownloadingItemWithValues": "{0} está descargando {1}", "UserDownloadingItemWithValues": "{0} está descargando {1}",
@@ -59,8 +86,11 @@
"UserOfflineFromDevice": "{0} se ha desconectado desde {1}", "UserOfflineFromDevice": "{0} se ha desconectado desde {1}",
"UserOnlineFromDevice": "{0} está en línea desde {1}", "UserOnlineFromDevice": "{0} está en línea desde {1}",
"UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}", "UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}",
"UserPolicyUpdatedWithName": "Actualizada política de usuario para {0}",
"UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}", "UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}",
"UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}", "UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}",
"ValueHasBeenAddedToLibrary": "{0} ha sido añadido a tu biblioteca multimedia",
"ValueSpecialEpisodeName": "Especial - {0}",
"VersionNumber": "Versión {0}", "VersionNumber": "Versión {0}",
"TasksMaintenanceCategory": "Mantenimiento", "TasksMaintenanceCategory": "Mantenimiento",
"TasksLibraryCategory": "Biblioteca", "TasksLibraryCategory": "Biblioteca",
@@ -97,6 +127,8 @@
"HearingImpaired": "Discapacidad Auditiva", "HearingImpaired": "Discapacidad Auditiva",
"TaskRefreshTrickplayImages": "Generar miniaturas de línea de tiempo", "TaskRefreshTrickplayImages": "Generar miniaturas de línea de tiempo",
"TaskRefreshTrickplayImagesDescription": "Crear miniaturas de tiempo para videos en las librerías habilitadas.", "TaskRefreshTrickplayImagesDescription": "Crear miniaturas de tiempo para videos en las librerías habilitadas.",
"TaskCleanCollectionsAndPlaylists": "Limpiar colecciones y listas de reproducción",
"TaskCleanCollectionsAndPlaylistsDescription": "Elimina elementos de colecciones y listas de reproducción que ya no existen.",
"TaskAudioNormalization": "Normalización de audio", "TaskAudioNormalization": "Normalización de audio",
"TaskAudioNormalizationDescription": "Escanear archivos para obtener datos de normalización.", "TaskAudioNormalizationDescription": "Escanear archivos para obtener datos de normalización.",
"TaskDownloadMissingLyricsDescription": "Descargar letras para las canciones", "TaskDownloadMissingLyricsDescription": "Descargar letras para las canciones",
@@ -106,6 +138,5 @@
"TaskExtractMediaSegmentsDescription": "Extrae u obtiene segmentos de medios de plugins habilitados para MediaSegment.", "TaskExtractMediaSegmentsDescription": "Extrae u obtiene segmentos de medios de plugins habilitados para MediaSegment.",
"TaskMoveTrickplayImages": "Migrar la ubicación de la imagen de Trickplay", "TaskMoveTrickplayImages": "Migrar la ubicación de la imagen de Trickplay",
"CleanupUserDataTask": "Tarea de limpieza de datos del usuario", "CleanupUserDataTask": "Tarea de limpieza de datos del usuario",
"CleanupUserDataTaskDescription": "Limpia todos los datos del usuario (estado de visualización, favoritos, etc.) de los medios que ya no están disponibles desde hace al menos 90 días.", "CleanupUserDataTaskDescription": "Limpia todos los datos del usuario (estado de visualización, favoritos, etc.) de los medios que ya no están disponibles desde hace al menos 90 días."
"Original": "Original"
} }

View File

@@ -1,19 +1,29 @@
{ {
"LabelRunningTimeValue": "Tiempo en ejecución: {0}", "LabelRunningTimeValue": "Tiempo en ejecución: {0}",
"ValueSpecialEpisodeName": "Especial - {0}",
"Sync": "Sincronizar",
"Songs": "Canciones",
"Shows": "Programas", "Shows": "Programas",
"Playlists": "Listas de reproducción",
"Photos": "Fotos", "Photos": "Fotos",
"Movies": "Películas", "Movies": "Películas",
"HeaderNextUp": "A continuación", "HeaderNextUp": "A continuación",
"HeaderLiveTV": "TV en vivo", "HeaderLiveTV": "TV en vivo",
"HeaderFavoriteSongs": "Canciones favoritas",
"HeaderFavoriteArtists": "Artistas favoritos",
"HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderFavoriteEpisodes": "Episodios favoritos", "HeaderFavoriteEpisodes": "Episodios favoritos",
"HeaderFavoriteShows": "Programas favoritos", "HeaderFavoriteShows": "Programas favoritos",
"HeaderContinueWatching": "Continuar viendo", "HeaderContinueWatching": "Continuar viendo",
"HeaderAlbumArtists": "Artistas de álbum",
"Genres": "Géneros", "Genres": "Géneros",
"Folders": "Carpetas", "Folders": "Carpetas",
"Favorites": "Favoritos", "Favorites": "Favoritos",
"Collections": "Colecciones", "Collections": "Colecciones",
"Channels": "Canales",
"Books": "Libros", "Books": "Libros",
"Artists": "Artistas", "Artists": "Artistas",
"Albums": "Álbumes",
"TaskDownloadMissingSubtitlesDescription": "Busca subtítulos faltantes en Internet basándose en la configuración de metadatos.", "TaskDownloadMissingSubtitlesDescription": "Busca subtítulos faltantes en Internet basándose en la configuración de metadatos.",
"TaskDownloadMissingSubtitles": "Descargar subtítulos faltantes", "TaskDownloadMissingSubtitles": "Descargar subtítulos faltantes",
"TaskRefreshChannelsDescription": "Actualiza la información de canales de Internet.", "TaskRefreshChannelsDescription": "Actualiza la información de canales de Internet.",
@@ -37,8 +47,10 @@
"TasksLibraryCategory": "Biblioteca", "TasksLibraryCategory": "Biblioteca",
"TasksMaintenanceCategory": "Mantenimiento", "TasksMaintenanceCategory": "Mantenimiento",
"VersionNumber": "Versión {0}", "VersionNumber": "Versión {0}",
"ValueHasBeenAddedToLibrary": "{0} se ha añadido a tu biblioteca de medios",
"UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}", "UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}",
"UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}", "UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}",
"UserPolicyUpdatedWithName": "La política de usuario ha sido actualizada para {0}",
"UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}", "UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}",
"UserOnlineFromDevice": "{0} está en línea desde {1}", "UserOnlineFromDevice": "{0} está en línea desde {1}",
"UserOfflineFromDevice": "{0} se ha desconectado desde {1}", "UserOfflineFromDevice": "{0} se ha desconectado desde {1}",
@@ -46,13 +58,19 @@
"UserDownloadingItemWithValues": "{0} está descargando {1}", "UserDownloadingItemWithValues": "{0} está descargando {1}",
"UserDeletedWithName": "El usuario {0} ha sido eliminado", "UserDeletedWithName": "El usuario {0} ha sido eliminado",
"UserCreatedWithName": "El usuario {0} ha sido creado", "UserCreatedWithName": "El usuario {0} ha sido creado",
"User": "Usuario",
"TvShows": "Programas de TV", "TvShows": "Programas de TV",
"System": "Sistema",
"SubtitleDownloadFailureFromForItem": "Falló la descarga de subtítulos desde {0} para {1}", "SubtitleDownloadFailureFromForItem": "Falló la descarga de subtítulos desde {0} para {1}",
"StartupEmbyServerIsLoading": "El servidor Jellyfin está cargando. Por favor, intente de nuevo pronto.", "StartupEmbyServerIsLoading": "El servidor Jellyfin está cargando. Por favor, intente de nuevo pronto.",
"ServerNameNeedsToBeRestarted": "{0} debe ser reiniciado",
"ScheduledTaskStartedWithName": "{0} iniciado",
"ScheduledTaskFailedWithName": "{0} falló", "ScheduledTaskFailedWithName": "{0} falló",
"ProviderValue": "Proveedor: {0}",
"PluginUpdatedWithName": "{0} fue actualizado", "PluginUpdatedWithName": "{0} fue actualizado",
"PluginUninstalledWithName": "{0} fue desinstalado", "PluginUninstalledWithName": "{0} fue desinstalado",
"PluginInstalledWithName": "{0} fue instalado", "PluginInstalledWithName": "{0} fue instalado",
"Plugin": "Complemento",
"NotificationOptionVideoPlaybackStopped": "Reproducción de video detenida", "NotificationOptionVideoPlaybackStopped": "Reproducción de video detenida",
"NotificationOptionVideoPlayback": "Reproducción de video iniciada", "NotificationOptionVideoPlayback": "Reproducción de video iniciada",
"NotificationOptionUserLockedOut": "Usuario bloqueado", "NotificationOptionUserLockedOut": "Usuario bloqueado",
@@ -76,13 +94,24 @@
"MusicVideos": "Videos musicales", "MusicVideos": "Videos musicales",
"Music": "Música", "Music": "Música",
"MixedContent": "Contenido mezclado", "MixedContent": "Contenido mezclado",
"MessageServerConfigurationUpdated": "Se ha actualizado la configuración del servidor",
"MessageNamedServerConfigurationUpdatedWithValue": "Se ha actualizado la sección {0} de la configuración del servidor",
"MessageApplicationUpdatedTo": "El servidor Jellyfin ha sido actualizado a {0}",
"MessageApplicationUpdated": "El servidor Jellyfin ha sido actualizado",
"Latest": "Recientes", "Latest": "Recientes",
"LabelIpAddressValue": "Dirección IP: {0}", "LabelIpAddressValue": "Dirección IP: {0}",
"ItemRemovedWithName": "{0} fue removido de la biblioteca",
"ItemAddedWithName": "{0} fue agregado a la biblioteca",
"Inherit": "Heredar", "Inherit": "Heredar",
"HomeVideos": "Videos caseros", "HomeVideos": "Videos caseros",
"HeaderRecordingGroups": "Grupos de grabación",
"FailedLoginAttemptWithUserName": "Intento de inicio de sesión fallido desde {0}", "FailedLoginAttemptWithUserName": "Intento de inicio de sesión fallido desde {0}",
"DeviceOnlineWithName": "{0} está conectado",
"DeviceOfflineWithName": "{0} se ha desconectado",
"ChapterNameValue": "Capítulo {0}", "ChapterNameValue": "Capítulo {0}",
"CameraImageUploadedFrom": "Una nueva imagen de cámara ha sido subida desde {0}",
"AuthenticationSucceededWithUserName": "{0} autenticado con éxito", "AuthenticationSucceededWithUserName": "{0} autenticado con éxito",
"Application": "Aplicación",
"AppDeviceValues": "Aplicación: {0}, Dispositivo: {1}", "AppDeviceValues": "Aplicación: {0}, Dispositivo: {1}",
"TaskCleanActivityLogDescription": "Elimina las entradas del registro de actividad anteriores al periodo configurado.", "TaskCleanActivityLogDescription": "Elimina las entradas del registro de actividad anteriores al periodo configurado.",
"TaskCleanActivityLog": "Limpiar registro de actividades", "TaskCleanActivityLog": "Limpiar registro de actividades",
@@ -98,7 +127,9 @@
"TaskRefreshTrickplayImagesDescription": "Crea previsualizaciones para la barra de reproducción en las bibliotecas habilitadas.", "TaskRefreshTrickplayImagesDescription": "Crea previsualizaciones para la barra de reproducción en las bibliotecas habilitadas.",
"TaskRefreshTrickplayImages": "Generar imágenes de la barra de reproducción", "TaskRefreshTrickplayImages": "Generar imágenes de la barra de reproducción",
"TaskAudioNormalization": "Normalización de audio", "TaskAudioNormalization": "Normalización de audio",
"TaskCleanCollectionsAndPlaylistsDescription": "Quita elementos que ya no existen de colecciones y listas de reproducción.",
"TaskAudioNormalizationDescription": "Analiza los archivos para normalizar el audio.", "TaskAudioNormalizationDescription": "Analiza los archivos para normalizar el audio.",
"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.", "TaskExtractMediaSegmentsDescription": "Extrae u obtiene segmentos de medios de complementos habilitados para MediaSegment.",

View File

@@ -1,24 +1,35 @@
{ {
"Channels": "Canales",
"Books": "Libros", "Books": "Libros",
"Albums": "Álbumes",
"Collections": "Colecciones", "Collections": "Colecciones",
"Artists": "Artistas", "Artists": "Artistas",
"DeviceOnlineWithName": "{0} está conectado",
"DeviceOfflineWithName": "{0} se ha desconectado",
"ChapterNameValue": "Capítulo {0}", "ChapterNameValue": "Capítulo {0}",
"CameraImageUploadedFrom": "Se ha subido una nueva imagen de cámara desde {0}",
"AuthenticationSucceededWithUserName": "{0} autenticado con éxito", "AuthenticationSucceededWithUserName": "{0} autenticado con éxito",
"Application": "Aplicación",
"AppDeviceValues": "App: {0}, Dispositivo: {1}", "AppDeviceValues": "App: {0}, Dispositivo: {1}",
"HeaderContinueWatching": "Continuar Viendo", "HeaderContinueWatching": "Continuar Viendo",
"HeaderAlbumArtists": "Artistas del álbum",
"Genres": "Géneros", "Genres": "Géneros",
"Folders": "Carpetas", "Folders": "Carpetas",
"Favorites": "Favoritos", "Favorites": "Favoritos",
"FailedLoginAttemptWithUserName": "Intento de inicio de sesión fallido desde {0}", "FailedLoginAttemptWithUserName": "Intento de inicio de sesión fallido desde {0}",
"HeaderFavoriteSongs": "Canciones Favoritas",
"HeaderFavoriteEpisodes": "Episodios Favoritos", "HeaderFavoriteEpisodes": "Episodios Favoritos",
"HeaderFavoriteArtists": "Artistas Favoritos",
"External": "Externo", "External": "Externo",
"Default": "Predeterminado", "Default": "Predeterminado",
"Movies": "Películas", "Movies": "Películas",
"MessageNamedServerConfigurationUpdatedWithValue": "La sección {0} de la configuración ha sido actualizada",
"MixedContent": "Contenido mixto", "MixedContent": "Contenido mixto",
"Music": "Música", "Music": "Música",
"NotificationOptionCameraImageUploaded": "Imagen de la cámara subida", "NotificationOptionCameraImageUploaded": "Imagen de la cámara subida",
"NotificationOptionServerRestartRequired": "Se necesita reiniciar el servidor", "NotificationOptionServerRestartRequired": "Se necesita reiniciar el servidor",
"NotificationOptionVideoPlayback": "Reproducción de video iniciada", "NotificationOptionVideoPlayback": "Reproducción de video iniciada",
"Sync": "Sincronizar",
"Shows": "Series", "Shows": "Series",
"UserDownloadingItemWithValues": "{0} está descargando {1}", "UserDownloadingItemWithValues": "{0} está descargando {1}",
"UserOfflineFromDevice": "{0} se ha desconectado desde {1}", "UserOfflineFromDevice": "{0} se ha desconectado desde {1}",
@@ -30,6 +41,8 @@
"TaskKeyframeExtractorDescription": "Extrae Fotogramas Clave de los archivos de vídeo para crear Listas de Reproducción HLS más precisas. Esta tarea puede durar mucho tiempo.", "TaskKeyframeExtractorDescription": "Extrae Fotogramas Clave de los archivos de vídeo para crear Listas de Reproducción HLS más precisas. Esta tarea puede durar mucho tiempo.",
"TaskAudioNormalization": "Normalización de audio", "TaskAudioNormalization": "Normalización de audio",
"TaskAudioNormalizationDescription": "Escanear archivos para la normalización de data.", "TaskAudioNormalizationDescription": "Escanear archivos para la normalización de data.",
"TaskCleanCollectionsAndPlaylists": "Limpiar colecciones y listas de reproducción",
"TaskCleanCollectionsAndPlaylistsDescription": "Remover elementos de colecciones y listas de reproducción que no existen.",
"TvShows": "Series de TV", "TvShows": "Series de TV",
"UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}", "UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}",
"TaskRefreshChannels": "Actualizar canales", "TaskRefreshChannels": "Actualizar canales",
@@ -37,12 +50,17 @@
"HeaderFavoriteShows": "Programas favoritos", "HeaderFavoriteShows": "Programas favoritos",
"TaskCleanActivityLog": "Limpiar registro de actividades", "TaskCleanActivityLog": "Limpiar registro de actividades",
"UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}", "UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}",
"System": "Sistema",
"User": "Usuario",
"Forced": "Forzado", "Forced": "Forzado",
"PluginInstalledWithName": "{0} ha sido instalado", "PluginInstalledWithName": "{0} ha sido instalado",
"HeaderFavoriteAlbums": "Álbumes favoritos",
"TaskUpdatePlugins": "Actualizar Plugins", "TaskUpdatePlugins": "Actualizar Plugins",
"Latest": "Recientes", "Latest": "Recientes",
"UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}", "UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}",
"Songs": "Canciones",
"NotificationOptionPluginError": "Falla de plugin", "NotificationOptionPluginError": "Falla de plugin",
"ScheduledTaskStartedWithName": "{0} iniciado",
"TasksApplicationCategory": "Aplicación", "TasksApplicationCategory": "Aplicación",
"UserDeletedWithName": "El usuario {0} ha sido eliminado", "UserDeletedWithName": "El usuario {0} ha sido eliminado",
"TaskRefreshChapterImages": "Extraer imágenes de los capítulos", "TaskRefreshChapterImages": "Extraer imágenes de los capítulos",
@@ -55,26 +73,34 @@
"NotificationOptionAudioPlaybackStopped": "Reproducción de audio detenida", "NotificationOptionAudioPlaybackStopped": "Reproducción de audio detenida",
"TasksLibraryCategory": "Biblioteca", "TasksLibraryCategory": "Biblioteca",
"NotificationOptionPluginInstalled": "Plugin instalado", "NotificationOptionPluginInstalled": "Plugin instalado",
"UserPolicyUpdatedWithName": "La política de usuario ha sido actualizada para {0}",
"VersionNumber": "Versión {0}", "VersionNumber": "Versión {0}",
"HeaderNextUp": "A continuación", "HeaderNextUp": "A continuación",
"ValueHasBeenAddedToLibrary": "{0} se ha añadido a tu biblioteca",
"LabelIpAddressValue": "Dirección IP: {0}", "LabelIpAddressValue": "Dirección IP: {0}",
"NameSeasonNumber": "Temporada {0}", "NameSeasonNumber": "Temporada {0}",
"NotificationOptionNewLibraryContent": "Nuevo contenido agregado", "NotificationOptionNewLibraryContent": "Nuevo contenido agregado",
"Plugin": "Plugin",
"NotificationOptionAudioPlayback": "Reproducción de audio iniciada", "NotificationOptionAudioPlayback": "Reproducción de audio iniciada",
"NotificationOptionTaskFailed": "Falló la tarea programada", "NotificationOptionTaskFailed": "Falló la tarea programada",
"LabelRunningTimeValue": "Tiempo en ejecución: {0}", "LabelRunningTimeValue": "Tiempo en ejecución: {0}",
"SubtitleDownloadFailureFromForItem": "Falló la descarga de subtítulos desde {0} para {1}", "SubtitleDownloadFailureFromForItem": "Falló la descarga de subtítulos desde {0} para {1}",
"TaskRefreshLibrary": "Escanear biblioteca de medios", "TaskRefreshLibrary": "Escanear biblioteca de medios",
"ServerNameNeedsToBeRestarted": "{0} debe ser reiniciado",
"TasksMaintenanceCategory": "Mantenimiento", "TasksMaintenanceCategory": "Mantenimiento",
"ProviderValue": "Proveedor: {0}",
"UserCreatedWithName": "El usuario {0} ha sido creado", "UserCreatedWithName": "El usuario {0} ha sido creado",
"PluginUninstalledWithName": "{0} ha sido desinstalado", "PluginUninstalledWithName": "{0} ha sido desinstalado",
"ValueSpecialEpisodeName": "Especial - {0}",
"ScheduledTaskFailedWithName": "{0} falló", "ScheduledTaskFailedWithName": "{0} falló",
"TaskCleanLogs": "Limpiar directorio de registros", "TaskCleanLogs": "Limpiar directorio de registros",
"NameInstallFailed": "Falló la instalación de {0}", "NameInstallFailed": "Falló la instalación de {0}",
"UserLockedOutWithName": "El usuario {0} ha sido bloqueado", "UserLockedOutWithName": "El usuario {0} ha sido bloqueado",
"TaskRefreshLibraryDescription": "Escanea tu biblioteca de medios para encontrar archivos nuevos y actualizar los metadatos.", "TaskRefreshLibraryDescription": "Escanea tu biblioteca de medios para encontrar archivos nuevos y actualizar los metadatos.",
"StartupEmbyServerIsLoading": "El servidor Jellyfin está cargando. Por favor, intente de nuevo en un momento.", "StartupEmbyServerIsLoading": "El servidor Jellyfin está cargando. Por favor, intente de nuevo en un momento.",
"Playlists": "Listas de reproducción",
"TaskDownloadMissingSubtitlesDescription": "Busca subtítulos faltantes en Internet basándose en la configuración de metadatos.", "TaskDownloadMissingSubtitlesDescription": "Busca subtítulos faltantes en Internet basándose en la configuración de metadatos.",
"MessageServerConfigurationUpdated": "Se ha actualizado la configuración del servidor",
"TaskRefreshPeople": "Actualizar personas", "TaskRefreshPeople": "Actualizar personas",
"NotificationOptionVideoPlaybackStopped": "Reproducción de video detenida", "NotificationOptionVideoPlaybackStopped": "Reproducción de video detenida",
"HeaderLiveTV": "TV en vivo", "HeaderLiveTV": "TV en vivo",
@@ -84,10 +110,15 @@
"TaskCleanCache": "Limpiar directorio caché", "TaskCleanCache": "Limpiar directorio caché",
"TaskRefreshChapterImagesDescription": "Crea miniaturas para videos que tienen capítulos.", "TaskRefreshChapterImagesDescription": "Crea miniaturas para videos que tienen capítulos.",
"Inherit": "Heredar", "Inherit": "Heredar",
"HeaderRecordingGroups": "Grupos de grabación",
"ItemAddedWithName": "{0} fue agregado a la biblioteca",
"TaskOptimizeDatabase": "Optimizar base de datos", "TaskOptimizeDatabase": "Optimizar base de datos",
"TaskKeyframeExtractor": "Extractor de Fotogramas Clave", "TaskKeyframeExtractor": "Extractor de Fotogramas Clave",
"HearingImpaired": "Discapacidad auditiva", "HearingImpaired": "Discapacidad auditiva",
"HomeVideos": "Videos caseros", "HomeVideos": "Videos caseros",
"ItemRemovedWithName": "{0} fue removido de la biblioteca",
"MessageApplicationUpdated": "El servidor Jellyfin ha sido actualizado",
"MessageApplicationUpdatedTo": "El servidor Jellyfin ha sido actualizado a {0}",
"MusicVideos": "Videos musicales", "MusicVideos": "Videos musicales",
"NewVersionIsAvailable": "Una nueva versión de Jellyfin está disponible para descargar.", "NewVersionIsAvailable": "Una nueva versión de Jellyfin está disponible para descargar.",
"PluginUpdatedWithName": "{0} ha sido actualizado", "PluginUpdatedWithName": "{0} ha sido actualizado",

View File

@@ -1,6 +1,7 @@
{ {
"TaskCleanActivityLogDescription": "Kustutab määratud ajast vanemad tegevuslogi kirjed.", "TaskCleanActivityLogDescription": "Kustutab määratud ajast vanemad tegevuslogi kirjed.",
"UserDownloadingItemWithValues": "{0} laadib alla {1}", "UserDownloadingItemWithValues": "{0} laadib alla {1}",
"HeaderRecordingGroups": "Salvestusrühmad",
"TaskOptimizeDatabaseDescription": "Tihendab ja puhastab andmebaasi. Selle toimingu tegemine pärast meediakogu andmebaasiga seotud muudatuste skannimist võib jõudlust parandada.", "TaskOptimizeDatabaseDescription": "Tihendab ja puhastab andmebaasi. Selle toimingu tegemine pärast meediakogu andmebaasiga seotud muudatuste skannimist võib jõudlust parandada.",
"TaskOptimizeDatabase": "Optimeeri andmebaasi", "TaskOptimizeDatabase": "Optimeeri andmebaasi",
"TaskDownloadMissingSubtitlesDescription": "Otsib veebist puuduvaid subtiitreid vastavalt määratud metaandmete seadetele.", "TaskDownloadMissingSubtitlesDescription": "Otsib veebist puuduvaid subtiitreid vastavalt määratud metaandmete seadetele.",
@@ -28,19 +29,30 @@
"TasksLibraryCategory": "Meediakogu", "TasksLibraryCategory": "Meediakogu",
"TasksMaintenanceCategory": "Hooldus", "TasksMaintenanceCategory": "Hooldus",
"VersionNumber": "Versioon {0}", "VersionNumber": "Versioon {0}",
"ValueSpecialEpisodeName": "Eriepisood - {0}",
"ValueHasBeenAddedToLibrary": "{0} lisati meediakogusse",
"UserStartedPlayingItemWithValues": "{0} taasesitab {1} seadmes {2}", "UserStartedPlayingItemWithValues": "{0} taasesitab {1} seadmes {2}",
"UserPasswordChangedWithName": "Kasutaja {0} parool muudeti", "UserPasswordChangedWithName": "Kasutaja {0} parool muudeti",
"UserLockedOutWithName": "Kasutaja {0} lukustati", "UserLockedOutWithName": "Kasutaja {0} lukustati",
"UserDeletedWithName": "Kasutaja {0} kustutati", "UserDeletedWithName": "Kasutaja {0} kustutati",
"UserCreatedWithName": "Kasutaja {0} on loodud", "UserCreatedWithName": "Kasutaja {0} on loodud",
"ScheduledTaskStartedWithName": "{0} käivitati",
"ProviderValue": "Allikas: {0}",
"StartupEmbyServerIsLoading": "Jellyfin server laadib. Proovi varsti uuesti.", "StartupEmbyServerIsLoading": "Jellyfin server laadib. Proovi varsti uuesti.",
"User": "Kasutaja",
"Undefined": "Määratlemata", "Undefined": "Määratlemata",
"TvShows": "Sarjad", "TvShows": "Sarjad",
"System": "Süsteem",
"Sync": "Sünkrooni",
"Songs": "Lood",
"Shows": "Sarjad", "Shows": "Sarjad",
"ServerNameNeedsToBeRestarted": "{0} tuleb taaskäivitada",
"ScheduledTaskFailedWithName": "{0} nurjus", "ScheduledTaskFailedWithName": "{0} nurjus",
"PluginUpdatedWithName": "{0} uuendati", "PluginUpdatedWithName": "{0} uuendati",
"PluginUninstalledWithName": "{0} eemaldati", "PluginUninstalledWithName": "{0} eemaldati",
"PluginInstalledWithName": "{0} paigaldati", "PluginInstalledWithName": "{0} paigaldati",
"Plugin": "Plugin",
"Playlists": "Esitusloendid",
"Photos": "Fotod", "Photos": "Fotod",
"NotificationOptionVideoPlaybackStopped": "Video taasesitus lõppes", "NotificationOptionVideoPlaybackStopped": "Video taasesitus lõppes",
"NotificationOptionVideoPlayback": "Video taasesitus algas", "NotificationOptionVideoPlayback": "Video taasesitus algas",
@@ -60,35 +72,52 @@
"NotificationOptionApplicationUpdateAvailable": "Rakenduse uuendus on saadaval", "NotificationOptionApplicationUpdateAvailable": "Rakenduse uuendus on saadaval",
"NewVersionIsAvailable": "Jellyfin serveri uus versioon on allalaadimiseks saadaval.", "NewVersionIsAvailable": "Jellyfin serveri uus versioon on allalaadimiseks saadaval.",
"NameSeasonUnknown": "Tundmatu hooaeg", "NameSeasonUnknown": "Tundmatu hooaeg",
"NameSeasonNumber": "{0}. hooaeg", "NameSeasonNumber": "Hooaeg {0}",
"NameInstallFailed": "{0} paigaldamine nurjus", "NameInstallFailed": "{0} paigaldamine nurjus",
"MusicVideos": "Muusikavideod", "MusicVideos": "Muusikavideod",
"Music": "Muusika", "Music": "Muusika",
"Movies": "Filmid", "Movies": "Filmid",
"MixedContent": "Segatud sisu", "MixedContent": "Segatud sisu",
"MessageServerConfigurationUpdated": "Serveri seadistust uuendati",
"MessageNamedServerConfigurationUpdatedWithValue": "Serveri seadistusosa {0} uuendati",
"MessageApplicationUpdatedTo": "Jellyfin server uuendati versioonile {0}",
"MessageApplicationUpdated": "Jellyfin server uuendati",
"Latest": "Uusimad", "Latest": "Uusimad",
"LabelRunningTimeValue": "Kestus: {0}", "LabelRunningTimeValue": "Kestus: {0}",
"LabelIpAddressValue": "IP aadress: {0}", "LabelIpAddressValue": "IP aadress: {0}",
"ItemRemovedWithName": "{0} eemaldati meediakogust",
"ItemAddedWithName": "{0} lisati meediakogusse",
"Inherit": "Päri", "Inherit": "Päri",
"HomeVideos": "Koduvideod", "HomeVideos": "Koduvideod",
"HeaderNextUp": "Järgmisena", "HeaderNextUp": "Järgmisena",
"HeaderLiveTV": "Otse TV", "HeaderLiveTV": "Otse TV",
"HeaderFavoriteSongs": "Lemmiklood",
"HeaderFavoriteShows": "Lemmiksarjad", "HeaderFavoriteShows": "Lemmiksarjad",
"HeaderFavoriteEpisodes": "Lemmikepisoodid", "HeaderFavoriteEpisodes": "Lemmikepisoodid",
"HeaderFavoriteArtists": "Lemmikesitajad",
"HeaderFavoriteAlbums": "Lemmikalbumid",
"HeaderContinueWatching": "Jätka vaatamist", "HeaderContinueWatching": "Jätka vaatamist",
"HeaderAlbumArtists": "Albumi esitajad",
"Genres": "Žanrid", "Genres": "Žanrid",
"Forced": "Sunnitud", "Forced": "Sunnitud",
"Folders": "Kaustad", "Folders": "Kaustad",
"Favorites": "Lemmikud", "Favorites": "Lemmikud",
"FailedLoginAttemptWithUserName": "Sisselogimine nurjus aadressilt {0}", "FailedLoginAttemptWithUserName": "Sisselogimine nurjus aadressilt {0}",
"DeviceOnlineWithName": "{0} on ühendatud",
"DeviceOfflineWithName": "{0} katkestas ühenduse",
"Default": "Vaikimisi", "Default": "Vaikimisi",
"ChapterNameValue": "Peatükk {0}", "ChapterNameValue": "Peatükk {0}",
"Channels": "Kanalid",
"CameraImageUploadedFrom": "Uus kaamera pilt laaditi üles allikalt {0}",
"Books": "Raamatud", "Books": "Raamatud",
"AuthenticationSucceededWithUserName": "{0} autentimine õnnestus", "AuthenticationSucceededWithUserName": "{0} autentimine õnnestus",
"Artists": "Esitajad", "Artists": "Esitajad",
"Application": "Rakendus",
"AppDeviceValues": "Rakendus: {0}, seade: {1}", "AppDeviceValues": "Rakendus: {0}, seade: {1}",
"Albums": "Albumid",
"UserOfflineFromDevice": "{0} katkestas ühenduse seadmega {1}", "UserOfflineFromDevice": "{0} katkestas ühenduse seadmega {1}",
"SubtitleDownloadFailureFromForItem": "Subtiitrite allalaadimine {0} > {1} nurjus", "SubtitleDownloadFailureFromForItem": "Subtiitrite allalaadimine {0} > {1} nurjus",
"UserPolicyUpdatedWithName": "Kasutaja {0} õigusi värskendati",
"UserStoppedPlayingItemWithValues": "{0} lõpetas {1} taasesituse seadmes {2}", "UserStoppedPlayingItemWithValues": "{0} lõpetas {1} taasesituse seadmes {2}",
"UserOnlineFromDevice": "{0} on ühendatud seadmest {1}", "UserOnlineFromDevice": "{0} on ühendatud seadmest {1}",
"External": "Väline", "External": "Väline",
@@ -99,12 +128,14 @@
"TaskRefreshTrickplayImagesDescription": "Loob trickplay eelvaated videotele lubatud meediakogudes.", "TaskRefreshTrickplayImagesDescription": "Loob trickplay eelvaated videotele lubatud meediakogudes.",
"TaskAudioNormalization": "Normaliseeri helitugevus", "TaskAudioNormalization": "Normaliseeri helitugevus",
"TaskAudioNormalizationDescription": "Otsib failidest helitugevuse normaliseerimise teavet.", "TaskAudioNormalizationDescription": "Otsib failidest helitugevuse normaliseerimise teavet.",
"TaskCleanCollectionsAndPlaylistsDescription": "Eemaldab kogumikest ja esitusloenditest üksused, mida enam ei eksisteeri.",
"TaskCleanCollectionsAndPlaylists": "Puhasta kogumikud ja esitusloendid",
"TaskDownloadMissingLyrics": "Hangi puuduvad laulusõnad", "TaskDownloadMissingLyrics": "Hangi puuduvad laulusõnad",
"TaskDownloadMissingLyricsDescription": "Laulusõnade allalaadimine", "TaskDownloadMissingLyricsDescription": "Laulusõnade allalaadimine",
"TaskMoveTrickplayImagesDescription": "Liigutab trickplay pildid meediakogu sätete kohaselt.", "TaskMoveTrickplayImagesDescription": "Liigutab trickplay pildid meediakogu sätete kohaselt.",
"TaskExtractMediaSegments": "Skaneeri meedialõike", "TaskExtractMediaSegments": "Skaneeri meediasegmente",
"TaskExtractMediaSegmentsDescription": "Eraldab või võtab meedialõigud MediaSegment'i toega pluginatest.", "TaskExtractMediaSegmentsDescription": "Eraldab või võtab meediasegmendid MediaSegment'i lubavatest pluginatest.",
"TaskMoveTrickplayImages": "Muuda trickplay piltide asukoht", "TaskMoveTrickplayImages": "Muuda trickplay piltide asukoht",
"CleanupUserDataTask": "Puhasta kasutajaandmed", "CleanupUserDataTask": "Puhasta kasutajaandmed",
"CleanupUserDataTaskDescription": "Puhastab kõik kasutajaandmed (vaatamise olek, lemmikute olek jne) meediast, mida pole enam vähemalt 90 päeva saadaval olnud." "CleanupUserDataTaskDescription": "Puhastab kõik kasutajaandmed (vaatamise olek, lemmikute olek jne) meediast, mis pole enam vähemalt 90 päeva saadaval olnud."
} }

View File

@@ -1,16 +1,23 @@
{ {
"ValueSpecialEpisodeName": "Berezia - {0}",
"Sync": "Sinkronizatu",
"Songs": "Abestiak",
"Shows": "Serieak", "Shows": "Serieak",
"Playlists": "Erreprodukzio-zerrendak",
"Photos": "Argazkiak", "Photos": "Argazkiak",
"MusicVideos": "Bideo musikalak", "MusicVideos": "Bideo musikalak",
"Movies": "Filmak", "Movies": "Filmak",
"HeaderContinueWatching": "Ikusten jarraitu", "HeaderContinueWatching": "Ikusten jarraitu",
"HeaderAlbumArtists": "Albumeko artistak",
"Genres": "Generoak", "Genres": "Generoak",
"Folders": "Karpetak", "Folders": "Karpetak",
"Favorites": "Gogokoak", "Favorites": "Gogokoak",
"Default": "Lehenetsia", "Default": "Lehenetsia",
"Collections": "Bildumak", "Collections": "Bildumak",
"Channels": "Kanalak",
"Books": "Liburuak", "Books": "Liburuak",
"Artists": "Artistak", "Artists": "Artistak",
"Albums": "Albumak",
"TaskOptimizeDatabase": "Datu basea optimizatu", "TaskOptimizeDatabase": "Datu basea optimizatu",
"TaskDownloadMissingSubtitlesDescription": "Falta diren azpitituluak bilatzen ditu interneten metadatuen konfigurazioaren arabera.", "TaskDownloadMissingSubtitlesDescription": "Falta diren azpitituluak bilatzen ditu interneten metadatuen konfigurazioaren arabera.",
"TaskDownloadMissingSubtitles": "Falta diren azpitituluak deskargatu", "TaskDownloadMissingSubtitles": "Falta diren azpitituluak deskargatu",
@@ -37,8 +44,10 @@
"TasksLibraryCategory": "Liburutegia", "TasksLibraryCategory": "Liburutegia",
"TasksMaintenanceCategory": "Mantenua", "TasksMaintenanceCategory": "Mantenua",
"VersionNumber": "Bertsioa {0}", "VersionNumber": "Bertsioa {0}",
"ValueHasBeenAddedToLibrary": "{0} zure multimedia liburutegian gehitu da",
"UserStoppedPlayingItemWithValues": "{0} {1} ikusten bukatu du {2}-(e)n", "UserStoppedPlayingItemWithValues": "{0} {1} ikusten bukatu du {2}-(e)n",
"UserStartedPlayingItemWithValues": "{0} {1} ikusten ari da {2}-(e)n", "UserStartedPlayingItemWithValues": "{0} {1} ikusten ari da {2}-(e)n",
"UserPolicyUpdatedWithName": "{0} erabiltzailearen politikak aldatu dira",
"UserPasswordChangedWithName": "{0} erabiltzailearen pasahitza aldatu da", "UserPasswordChangedWithName": "{0} erabiltzailearen pasahitza aldatu da",
"UserOnlineFromDevice": "{0} online dago {1}-(e)tik", "UserOnlineFromDevice": "{0} online dago {1}-(e)tik",
"UserOfflineFromDevice": "{0} {1}-(e)tik deskonektatu da", "UserOfflineFromDevice": "{0} {1}-(e)tik deskonektatu da",
@@ -46,14 +55,19 @@
"UserDownloadingItemWithValues": "{0} {1} deskargatzen ari da", "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",
"Undefined": "Ezezaguna", "Undefined": "Ezezaguna",
"TvShows": "TB serieak", "TvShows": "TB serieak",
"System": "Sistema",
"SubtitleDownloadFailureFromForItem": "{1}-en azpitutuluak {0}-tik deskargatzeak huts egin du", "SubtitleDownloadFailureFromForItem": "{1}-en azpitutuluak {0}-tik deskargatzeak huts egin du",
"StartupEmbyServerIsLoading": "Jellyfin zerbitzaria kargatzen. Saiatu berriro beranduago.", "StartupEmbyServerIsLoading": "Jellyfin zerbitzaria kargatzen. Saiatu berriro beranduago.",
"ServerNameNeedsToBeRestarted": "{0} berrabiarazi behar da",
"ScheduledTaskStartedWithName": "{0} hasi da",
"ScheduledTaskFailedWithName": "{0} huts egin du", "ScheduledTaskFailedWithName": "{0} huts egin du",
"PluginUpdatedWithName": "{0} eguneratu da", "PluginUpdatedWithName": "{0} eguneratu da",
"PluginUninstalledWithName": "{0} desinstalatu da", "PluginUninstalledWithName": "{0} desinstalatu da",
"PluginInstalledWithName": "{0} instalatu da", "PluginInstalledWithName": "{0} instalatu da",
"Plugin": "Plugin",
"NotificationOptionVideoPlaybackStopped": "Bideoa geldituta", "NotificationOptionVideoPlaybackStopped": "Bideoa geldituta",
"NotificationOptionVideoPlayback": "Bideoa martxan", "NotificationOptionVideoPlayback": "Bideoa martxan",
"NotificationOptionUserLockedOut": "Erabiltzailea blokeatua", "NotificationOptionUserLockedOut": "Erabiltzailea blokeatua",
@@ -76,22 +90,37 @@
"NameInstallFailed": "{0} instalazioak huts egin du", "NameInstallFailed": "{0} instalazioak huts egin du",
"Music": "Musika", "Music": "Musika",
"MixedContent": "Eduki mistoa", "MixedContent": "Eduki mistoa",
"MessageServerConfigurationUpdated": "Zerbitzariaren konfigurazioa eguneratu da",
"MessageNamedServerConfigurationUpdatedWithValue": "Zerbitzariaren {0} konfigurazio atala eguneratu da",
"MessageApplicationUpdatedTo": "Jellyfin zerbitzaria {0}-ra eguneratu da",
"MessageApplicationUpdated": "Jellyfin zerbitzaria eguneratu da",
"Latest": "Azkena", "Latest": "Azkena",
"LabelRunningTimeValue": "Iraupena: {0}", "LabelRunningTimeValue": "Iraupena: {0}",
"LabelIpAddressValue": "IP helbidea: {0}", "LabelIpAddressValue": "IP helbidea: {0}",
"ItemRemovedWithName": "{0} liburutegitik kendu da",
"ItemAddedWithName": "{0} liburutegira gehitu da",
"HomeVideos": "Etxeko bideoak", "HomeVideos": "Etxeko bideoak",
"HeaderNextUp": "Hurrengoa", "HeaderNextUp": "Hurrengoa",
"HeaderLiveTV": "Zuzeneko TB", "HeaderLiveTV": "Zuzeneko TB",
"HeaderFavoriteSongs": "Gogoko abestiak",
"HeaderFavoriteShows": "Gogoko serieak", "HeaderFavoriteShows": "Gogoko serieak",
"HeaderFavoriteEpisodes": "Gogoko atalak", "HeaderFavoriteEpisodes": "Gogoko atalak",
"HeaderFavoriteArtists": "Gogoko artistak",
"HeaderFavoriteAlbums": "Gogoko albumak",
"Forced": "Behartuta", "Forced": "Behartuta",
"FailedLoginAttemptWithUserName": "{0}-tik saioa hasteak huts egin du", "FailedLoginAttemptWithUserName": "{0}-tik saioa hasteak huts egin du",
"External": "Kanpokoa", "External": "Kanpokoa",
"DeviceOnlineWithName": "{0} konektatu da",
"DeviceOfflineWithName": "{0} deskonektatu da",
"ChapterNameValue": "{0} Kapitulua", "ChapterNameValue": "{0} Kapitulua",
"CameraImageUploadedFrom": "{0}-tik kamera irudi berri bat igo da",
"AuthenticationSucceededWithUserName": "{0} ongi autentifikatu da", "AuthenticationSucceededWithUserName": "{0} ongi autentifikatu da",
"Application": "Aplikazioa",
"AppDeviceValues": "App: {0}, Gailua: {1}", "AppDeviceValues": "App: {0}, Gailua: {1}",
"HearingImpaired": "Entzumen urritasuna", "HearingImpaired": "Entzumen urritasuna",
"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",
"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",
@@ -101,6 +130,8 @@
"TaskDownloadMissingLyrics": "Deskargatu falta diren letrak", "TaskDownloadMissingLyrics": "Deskargatu falta diren letrak",
"TaskDownloadMissingLyricsDescription": "Deskargatu abestientzako letrak", "TaskDownloadMissingLyricsDescription": "Deskargatu abestientzako letrak",
"TaskExtractMediaSegments": "Multimedia segmentuen eskaneoa", "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.", "TaskExtractMediaSegmentsDescription": "Media segmentuak atera edo lortzen ditu MediaSegment gaituta duten pluginetik.",
"TaskMoveTrickplayImages": "Aldatu Trickplay irudien kokalekua", "TaskMoveTrickplayImages": "Aldatu Trickplay irudien kokalekua",
"TaskMoveTrickplayImagesDescription": "Lehendik dauden trickplay fitxategiak liburutegiaren ezarpenen arabera mugitzen dira.", "TaskMoveTrickplayImagesDescription": "Lehendik dauden trickplay fitxategiak liburutegiaren ezarpenen arabera mugitzen dira.",

View File

@@ -1,24 +1,41 @@
{ {
"Albums": "آلبوم‌ها",
"AppDeviceValues": "برنامه: {0} ، دستگاه: {1}", "AppDeviceValues": "برنامه: {0} ، دستگاه: {1}",
"Application": "برنامه",
"Artists": "هنرمندان", "Artists": "هنرمندان",
"AuthenticationSucceededWithUserName": "{0} با موفقیت تایید اعتبار شد", "AuthenticationSucceededWithUserName": "{0} با موفقیت تایید اعتبار شد",
"Books": "کتاب‌ها", "Books": "کتاب‌ها",
"CameraImageUploadedFrom": "یک عکس جدید از دوربین ارسال شده است {0}",
"Channels": "کانالها",
"ChapterNameValue": "قسمت {0}", "ChapterNameValue": "قسمت {0}",
"Collections": "مجموعه‌ها", "Collections": "مجموعه‌ها",
"DeviceOfflineWithName": "ارتباط {0} قطع شد",
"DeviceOnlineWithName": "{0} متصل شد",
"FailedLoginAttemptWithUserName": "تلاش برای ورود از {0} ناموفق بود", "FailedLoginAttemptWithUserName": "تلاش برای ورود از {0} ناموفق بود",
"Favorites": "مورد علاقه‌ها", "Favorites": "مورد علاقه‌ها",
"Folders": "پوشه‌ها", "Folders": "پوشه‌ها",
"Genres": "ژانرها", "Genres": "ژانرها",
"HeaderAlbumArtists": "هنرمندان آلبوم",
"HeaderContinueWatching": "ادامه تماشا", "HeaderContinueWatching": "ادامه تماشا",
"HeaderFavoriteAlbums": "آلبوم‌های مورد علاقه",
"HeaderFavoriteArtists": "هنرمندان مورد علاقه",
"HeaderFavoriteEpisodes": "قسمت‌های مورد علاقه", "HeaderFavoriteEpisodes": "قسمت‌های مورد علاقه",
"HeaderFavoriteShows": "سریال‌های مورد علاقه", "HeaderFavoriteShows": "سریال‌های مورد علاقه",
"HeaderFavoriteSongs": "آهنگ‌های مورد علاقه",
"HeaderLiveTV": "پخش زنده", "HeaderLiveTV": "پخش زنده",
"HeaderNextUp": "قسمت بعدی", "HeaderNextUp": "قسمت بعدی",
"HeaderRecordingGroups": "گروه‌های ضبط",
"HomeVideos": "ویدیوهای خانگی", "HomeVideos": "ویدیوهای خانگی",
"Inherit": "به ارث برده", "Inherit": "به ارث برده",
"ItemAddedWithName": "{0} به کتابخانه افزوده شد",
"ItemRemovedWithName": "{0} از کتابخانه حذف شد",
"LabelIpAddressValue": "آدرس آی پی: {0}", "LabelIpAddressValue": "آدرس آی پی: {0}",
"LabelRunningTimeValue": "زمان اجرا: {0}", "LabelRunningTimeValue": "زمان اجرا: {0}",
"Latest": "جدیدترین‌ها", "Latest": "جدیدترین‌ها",
"MessageApplicationUpdated": "سرور Jellyfin بروزرسانی شد",
"MessageApplicationUpdatedTo": "سرور Jellyfin به نسخه {0} بروزرسانی شد",
"MessageNamedServerConfigurationUpdatedWithValue": "پکربندی بخش {0} سرور بروزرسانی شد",
"MessageServerConfigurationUpdated": "پیکربندی سرور بروزرسانی شد",
"MixedContent": "محتوای مخلوط", "MixedContent": "محتوای مخلوط",
"Movies": "فیلم ها", "Movies": "فیلم ها",
"Music": "موسیقی", "Music": "موسیقی",
@@ -44,14 +61,24 @@
"NotificationOptionVideoPlayback": "پخش ویدیو آغاز شد", "NotificationOptionVideoPlayback": "پخش ویدیو آغاز شد",
"NotificationOptionVideoPlaybackStopped": "پخش ویدیو متوقف شد", "NotificationOptionVideoPlaybackStopped": "پخش ویدیو متوقف شد",
"Photos": "عکس‌ها", "Photos": "عکس‌ها",
"Playlists": "لیست‌های پخش",
"Plugin": "افزونه",
"PluginInstalledWithName": "{0} نصب شد", "PluginInstalledWithName": "{0} نصب شد",
"PluginUninstalledWithName": "{0} حذف شد", "PluginUninstalledWithName": "{0} حذف شد",
"PluginUpdatedWithName": "{0} آپدیت شد", "PluginUpdatedWithName": "{0} آپدیت شد",
"ProviderValue": "ارائه دهنده: {0}",
"ScheduledTaskFailedWithName": "{0} شکست خورد", "ScheduledTaskFailedWithName": "{0} شکست خورد",
"ScheduledTaskStartedWithName": "{0} شروع شد",
"ServerNameNeedsToBeRestarted": "{0} نیاز به راه اندازی مجدد دارد",
"Shows": "سریال‌ها", "Shows": "سریال‌ها",
"Songs": "موسیقی‌ها",
"StartupEmbyServerIsLoading": "سرور Jellyfin در حال بارگیری است. لطفا کمی بعد دوباره تلاش کنید.", "StartupEmbyServerIsLoading": "سرور Jellyfin در حال بارگیری است. لطفا کمی بعد دوباره تلاش کنید.",
"SubtitleDownloadFailureForItem": "دانلود زیرنویس برای {0} ناموفق بود",
"SubtitleDownloadFailureFromForItem": "بارگیری زیرنویس برای {1} از {0} شکست خورد", "SubtitleDownloadFailureFromForItem": "بارگیری زیرنویس برای {1} از {0} شکست خورد",
"Sync": "همگام‌سازی",
"System": "سیستم",
"TvShows": "سریال‌های تلویزیونی", "TvShows": "سریال‌های تلویزیونی",
"User": "کاربر",
"UserCreatedWithName": "کاربر {0} ایجاد شد", "UserCreatedWithName": "کاربر {0} ایجاد شد",
"UserDeletedWithName": "کاربر {0} حذف شد", "UserDeletedWithName": "کاربر {0} حذف شد",
"UserDownloadingItemWithValues": "{0} در حال بارگیری {1} می‌باشد", "UserDownloadingItemWithValues": "{0} در حال بارگیری {1} می‌باشد",
@@ -59,8 +86,11 @@
"UserOfflineFromDevice": "ارتباط {0} از {1} قطع شد", "UserOfflineFromDevice": "ارتباط {0} از {1} قطع شد",
"UserOnlineFromDevice": "{0} از {1} آنلاین می‌باشد", "UserOnlineFromDevice": "{0} از {1} آنلاین می‌باشد",
"UserPasswordChangedWithName": "گذرواژه برای کاربر {0} تغییر کرد", "UserPasswordChangedWithName": "گذرواژه برای کاربر {0} تغییر کرد",
"UserPolicyUpdatedWithName": "سیاست کاربری برای {0} بروزرسانی شد",
"UserStartedPlayingItemWithValues": "{0} در حال پخش {1} بر روی {2} است", "UserStartedPlayingItemWithValues": "{0} در حال پخش {1} بر روی {2} است",
"UserStoppedPlayingItemWithValues": "{0} پخش {1} را بر روی {2} به پایان رساند", "UserStoppedPlayingItemWithValues": "{0} پخش {1} را بر روی {2} به پایان رساند",
"ValueHasBeenAddedToLibrary": "{0} به کتابخانه‌ی رسانه‌ی شما افزوده شد",
"ValueSpecialEpisodeName": "ویژه - {0}",
"VersionNumber": "نسخه {0}", "VersionNumber": "نسخه {0}",
"TaskCleanTranscodeDescription": "فایل‌های کدگذاری که قدیمی‌تر از یک روز هستند را حذف می‌کند.", "TaskCleanTranscodeDescription": "فایل‌های کدگذاری که قدیمی‌تر از یک روز هستند را حذف می‌کند.",
"TaskCleanTranscode": "پاکسازی مسیر کد گذاری", "TaskCleanTranscode": "پاکسازی مسیر کد گذاری",
@@ -97,6 +127,8 @@
"HearingImpaired": "مشکل شنوایی", "HearingImpaired": "مشکل شنوایی",
"TaskRefreshTrickplayImages": "تولید تصاویر Trickplay", "TaskRefreshTrickplayImages": "تولید تصاویر Trickplay",
"TaskRefreshTrickplayImagesDescription": "تولید پیش‌نمایش های trickplay برای ویدیو های فعال شده در کتابخانه.", "TaskRefreshTrickplayImagesDescription": "تولید پیش‌نمایش های trickplay برای ویدیو های فعال شده در کتابخانه.",
"TaskCleanCollectionsAndPlaylists": "پاکسازی مجموعه ها و لیست پخش",
"TaskCleanCollectionsAndPlaylistsDescription": "موارد را از مجموعه ها و لیست پخش هایی که دیگر وجود ندارند حذف میکند.",
"TaskAudioNormalizationDescription": "بررسی فایل برای داده‌های نرمال کردن صدا.", "TaskAudioNormalizationDescription": "بررسی فایل برای داده‌های نرمال کردن صدا.",
"TaskDownloadMissingLyrics": "دانلود متن‌های ناموجود", "TaskDownloadMissingLyrics": "دانلود متن‌های ناموجود",
"TaskDownloadMissingLyricsDescription": "دانلود متن شعر‌ها", "TaskDownloadMissingLyricsDescription": "دانلود متن شعر‌ها",

View File

@@ -8,33 +8,57 @@
"Music": "Musiikki", "Music": "Musiikki",
"Movies": "Elokuvat", "Movies": "Elokuvat",
"MixedContent": "Sekalainen sisältö", "MixedContent": "Sekalainen sisältö",
"MessageServerConfigurationUpdated": "Palvelimen asetukset on päivitetty",
"MessageNamedServerConfigurationUpdatedWithValue": "Palvelimen asetusten osio {0} on päivitetty",
"MessageApplicationUpdatedTo": "Jellyfin-palvelin on päivitetty versioon {0}",
"MessageApplicationUpdated": "Jellyfin-palvelin on päivitetty",
"Latest": "Viimeisimmät", "Latest": "Viimeisimmät",
"LabelRunningTimeValue": "Kesto: {0}", "LabelRunningTimeValue": "Kesto: {0}",
"LabelIpAddressValue": "IP-osoite: {0}", "LabelIpAddressValue": "IP-osoite: {0}",
"ItemRemovedWithName": "{0} poistettiin kirjastosta",
"ItemAddedWithName": "{0} lisättiin kirjastoon",
"Inherit": "Peri", "Inherit": "Peri",
"HomeVideos": "Kotivideot", "HomeVideos": "Kotivideot",
"HeaderRecordingGroups": "Tallennusryhmät",
"HeaderNextUp": "Seuraavaksi", "HeaderNextUp": "Seuraavaksi",
"HeaderFavoriteSongs": "Suosikkikappaleet",
"HeaderFavoriteShows": "Suosikkisarjat", "HeaderFavoriteShows": "Suosikkisarjat",
"HeaderFavoriteEpisodes": "Suosikkijaksot", "HeaderFavoriteEpisodes": "Suosikkijaksot",
"HeaderFavoriteArtists": "Suosikkiesittäjät",
"HeaderFavoriteAlbums": "Suosikkialbumit",
"HeaderContinueWatching": "Jatka katselua", "HeaderContinueWatching": "Jatka katselua",
"HeaderAlbumArtists": "Albumin esittäjät",
"Genres": "Tyylilajit", "Genres": "Tyylilajit",
"Folders": "Kansiot", "Folders": "Kansiot",
"Favorites": "Suosikit", "Favorites": "Suosikit",
"FailedLoginAttemptWithUserName": "Epäonnistunut kirjautumisyritys lähteestä \"{0}\"", "FailedLoginAttemptWithUserName": "Epäonnistunut kirjautumisyritys lähteestä \"{0}\"",
"DeviceOnlineWithName": "{0} on yhdistetty",
"DeviceOfflineWithName": "{0} on katkaissut yhteyden",
"Collections": "Kokoelmat", "Collections": "Kokoelmat",
"ChapterNameValue": "Kappale {0}", "ChapterNameValue": "Kappale {0}",
"Channels": "Kanavat",
"CameraImageUploadedFrom": "Uusi kameran kuva on sirretty lähteestä {0}",
"Books": "Kirjat", "Books": "Kirjat",
"AuthenticationSucceededWithUserName": "{0} todennus onnistunut", "AuthenticationSucceededWithUserName": "{0} on todennettu",
"Artists": "Artistit", "Artists": "Esittäjät",
"Application": "Sovellus",
"AppDeviceValues": "Sovellus: {0}, Laite: {1}", "AppDeviceValues": "Sovellus: {0}, Laite: {1}",
"Albums": "Albumit",
"User": "Käyttäjä",
"System": "Järjestelmä",
"ScheduledTaskFailedWithName": "{0} epäonnistui", "ScheduledTaskFailedWithName": "{0} epäonnistui",
"PluginUpdatedWithName": "{0} päivitettiin", "PluginUpdatedWithName": "{0} päivitettiin",
"PluginInstalledWithName": "{0} asennettiin", "PluginInstalledWithName": "{0} asennettiin",
"Photos": "Valokuvat", "Photos": "Valokuvat",
"ScheduledTaskStartedWithName": "\"{0}\" käynnistetty",
"PluginUninstalledWithName": "{0} poistettiin", "PluginUninstalledWithName": "{0} poistettiin",
"Playlists": "Soittolistat",
"VersionNumber": "Versio {0}", "VersionNumber": "Versio {0}",
"ValueSpecialEpisodeName": "Erikoisjakso - {0}",
"ValueHasBeenAddedToLibrary": "\"{0}\" on lisätty mediakirjastoon",
"UserStoppedPlayingItemWithValues": "{0} lopetti kohteen \"{1}\" toiston sijainnissa \"{2}\"", "UserStoppedPlayingItemWithValues": "{0} lopetti kohteen \"{1}\" toiston sijainnissa \"{2}\"",
"UserStartedPlayingItemWithValues": "{0} toistaa kohdetta \"{1}\" sijainnissa \"{2}\"", "UserStartedPlayingItemWithValues": "{0} toistaa kohdetta \"{1}\" sijainnissa \"{2}\"",
"UserPolicyUpdatedWithName": "Käyttäjän {0} käyttöoikeudet on päivitetty",
"UserPasswordChangedWithName": "Käyttäjän {0} salasana on vaihdettu", "UserPasswordChangedWithName": "Käyttäjän {0} salasana on vaihdettu",
"UserOnlineFromDevice": "{0} on yhdistänyt sijainnista \"{1}\"", "UserOnlineFromDevice": "{0} on yhdistänyt sijainnista \"{1}\"",
"UserOfflineFromDevice": "{0} on katkaissut yhteyden sijainnista \"{1}\"", "UserOfflineFromDevice": "{0} on katkaissut yhteyden sijainnista \"{1}\"",
@@ -43,9 +67,14 @@
"UserDeletedWithName": "Käyttäjä {0} on poistettu", "UserDeletedWithName": "Käyttäjä {0} on poistettu",
"UserCreatedWithName": "Käyttäjä {0} on luotu", "UserCreatedWithName": "Käyttäjä {0} on luotu",
"TvShows": "Sarjat", "TvShows": "Sarjat",
"Sync": "Synkronointi",
"SubtitleDownloadFailureFromForItem": "Tekstityksen lataus lähteestä \"{0}\" kohteelle \"{1}\" epäonnistui", "SubtitleDownloadFailureFromForItem": "Tekstityksen lataus lähteestä \"{0}\" kohteelle \"{1}\" epäonnistui",
"StartupEmbyServerIsLoading": "Jellyfin-palvelin on latautumassa. Yritä hetken kuluttua uudelleen.", "StartupEmbyServerIsLoading": "Jellyfin-palvelin on latautumassa. Yritä hetken kuluttua uudelleen.",
"Songs": "Kappaleet",
"Shows": "Sarjat", "Shows": "Sarjat",
"ServerNameNeedsToBeRestarted": "\"{0}\" on käynnistettävä uudelleen",
"ProviderValue": "Lähde: {0}",
"Plugin": "Lisäosa",
"NotificationOptionVideoPlaybackStopped": "Videon toisto lopetettu", "NotificationOptionVideoPlaybackStopped": "Videon toisto lopetettu",
"NotificationOptionVideoPlayback": "Videon toisto aloitettu", "NotificationOptionVideoPlayback": "Videon toisto aloitettu",
"NotificationOptionUserLockedOut": "Käyttäjä on lukittu", "NotificationOptionUserLockedOut": "Käyttäjä on lukittu",
@@ -97,6 +126,8 @@
"HearingImpaired": "Kuulorajoitteinen", "HearingImpaired": "Kuulorajoitteinen",
"TaskRefreshTrickplayImages": "Luo Trickplay-kuvat", "TaskRefreshTrickplayImages": "Luo Trickplay-kuvat",
"TaskRefreshTrickplayImagesDescription": "Luo Trickplay-esikatselut käytössä olevien kirjastojen videoista.", "TaskRefreshTrickplayImagesDescription": "Luo Trickplay-esikatselut käytössä olevien kirjastojen videoista.",
"TaskCleanCollectionsAndPlaylistsDescription": "Poistaa kohteet kokoelmista ja soittolistoista joita ei ole enää olemassa.",
"TaskCleanCollectionsAndPlaylists": "Puhdista kokoelmat ja soittolistat",
"TaskAudioNormalization": "Äänenvoimakkuuden normalisointi", "TaskAudioNormalization": "Äänenvoimakkuuden normalisointi",
"TaskAudioNormalizationDescription": "Etsii tiedostoista äänenvoimakkuuden normalisointitietoja.", "TaskAudioNormalizationDescription": "Etsii tiedostoista äänenvoimakkuuden normalisointitietoja.",
"TaskDownloadMissingLyrics": "Lataa puuttuva lyriikka", "TaskDownloadMissingLyrics": "Lataa puuttuva lyriikka",

View File

@@ -1,7 +1,10 @@
{ {
"VersionNumber": "Bersyon {0}", "VersionNumber": "Bersyon {0}",
"ValueSpecialEpisodeName": "Espesyal - {0}",
"ValueHasBeenAddedToLibrary": "Naidagdag na ang {0} sa iyong librerya ng medya",
"UserStoppedPlayingItemWithValues": "Natapos ni {0} ang {1} sa {2}", "UserStoppedPlayingItemWithValues": "Natapos ni {0} ang {1} sa {2}",
"UserStartedPlayingItemWithValues": "Si {0} ay nagpla-play ng {1} sa {2}", "UserStartedPlayingItemWithValues": "Si {0} ay nagpla-play ng {1} sa {2}",
"UserPolicyUpdatedWithName": "Ang user policy ay nai-update para kay {0}",
"UserPasswordChangedWithName": "Napalitan na ang password ni {0}", "UserPasswordChangedWithName": "Napalitan na ang password ni {0}",
"UserOnlineFromDevice": "Si {0} ay naka-konekta galing sa {1}", "UserOnlineFromDevice": "Si {0} ay naka-konekta galing sa {1}",
"UserOfflineFromDevice": "Si {0} ay na-diskonekta galing sa {1}", "UserOfflineFromDevice": "Si {0} ay na-diskonekta galing sa {1}",
@@ -9,14 +12,23 @@
"UserDownloadingItemWithValues": "Nagdadownload si {0} ng {1}", "UserDownloadingItemWithValues": "Nagdadownload si {0} ng {1}",
"UserDeletedWithName": "Natanggal na is user {0}", "UserDeletedWithName": "Natanggal na is user {0}",
"UserCreatedWithName": "Nagawa na si user {0}", "UserCreatedWithName": "Nagawa na si user {0}",
"User": "User",
"TvShows": "Mga Palabas sa Telebisyon", "TvShows": "Mga Palabas sa Telebisyon",
"System": "Sistema",
"Sync": "Pag-sync",
"SubtitleDownloadFailureFromForItem": "Hindi nai-download ang subtitles {0} para sa {1}", "SubtitleDownloadFailureFromForItem": "Hindi nai-download ang subtitles {0} para sa {1}",
"StartupEmbyServerIsLoading": "Naglo-load ang Jellyfin Server. Mangyaring subukan ulit sandali.", "StartupEmbyServerIsLoading": "Naglo-load ang Jellyfin Server. Mangyaring subukan ulit sandali.",
"Songs": "Mga Kanta",
"Shows": "Mga Pelikula", "Shows": "Mga Pelikula",
"ServerNameNeedsToBeRestarted": "Kailangan irestart ang {0}",
"ScheduledTaskStartedWithName": "Nagsimula na ang {0}",
"ScheduledTaskFailedWithName": "Hindi gumana ang {0}", "ScheduledTaskFailedWithName": "Hindi gumana ang {0}",
"ProviderValue": "Tagapagtustos: {0}",
"PluginUpdatedWithName": "Naiupdate na ang {0}", "PluginUpdatedWithName": "Naiupdate na ang {0}",
"PluginUninstalledWithName": "Naiuninstall na ang {0}", "PluginUninstalledWithName": "Naiuninstall na ang {0}",
"PluginInstalledWithName": "Nainstall na ang {0}", "PluginInstalledWithName": "Nainstall na ang {0}",
"Plugin": "Plugin",
"Playlists": "Mga Playlist",
"Photos": "Mga Larawan", "Photos": "Mga Larawan",
"NotificationOptionVideoPlaybackStopped": "Huminto na ang pelikula", "NotificationOptionVideoPlaybackStopped": "Huminto na ang pelikula",
"NotificationOptionVideoPlayback": "Nagsimula na ang pelikula", "NotificationOptionVideoPlayback": "Nagsimula na ang pelikula",
@@ -42,25 +54,42 @@
"Music": "Mga Kanta", "Music": "Mga Kanta",
"Movies": "Mga Pelikula", "Movies": "Mga Pelikula",
"MixedContent": "Halo-halong content", "MixedContent": "Halo-halong content",
"MessageServerConfigurationUpdated": "Naiupdate na ang server configuration",
"MessageNamedServerConfigurationUpdatedWithValue": "Naiupdate na ang server configuration section {0}",
"MessageApplicationUpdatedTo": "Ang bersyon ng Jellyfin Server ay naiupdate sa {0}",
"MessageApplicationUpdated": "Naiupdate na ang Jellyfin Server",
"Latest": "Pinakabago", "Latest": "Pinakabago",
"LabelRunningTimeValue": "Oras: {0}", "LabelRunningTimeValue": "Oras: {0}",
"LabelIpAddressValue": "IP address: {0}", "LabelIpAddressValue": "IP address: {0}",
"ItemRemovedWithName": "Naitanggal ang {0} sa librerya",
"ItemAddedWithName": "Naidagdag ang {0} sa librerya",
"Inherit": "Manahin", "Inherit": "Manahin",
"HeaderRecordingGroups": "Pagtatalang Grupo",
"HeaderNextUp": "Susunod", "HeaderNextUp": "Susunod",
"HeaderLiveTV": "Live TV", "HeaderLiveTV": "Live TV",
"HeaderFavoriteSongs": "Mga Paboritong Kanta",
"HeaderFavoriteShows": "Mga Paboritong Pelikula", "HeaderFavoriteShows": "Mga Paboritong Pelikula",
"HeaderFavoriteEpisodes": "Mga Paboritong Yugto", "HeaderFavoriteEpisodes": "Mga Paboritong Yugto",
"HeaderFavoriteArtists": "Mga Paboritong Artista",
"HeaderFavoriteAlbums": "Mga Paboritong Album",
"HeaderContinueWatching": "Magpatuloy sa Panonood", "HeaderContinueWatching": "Magpatuloy sa Panonood",
"HeaderAlbumArtists": "Mga Artista ng Album",
"Genres": "Mga Kategorya", "Genres": "Mga Kategorya",
"Folders": "Mga Folder", "Folders": "Mga Folder",
"Favorites": "Mga Paborito", "Favorites": "Mga Paborito",
"FailedLoginAttemptWithUserName": "Maling login galing kay/sa {0}", "FailedLoginAttemptWithUserName": "Maling login galing kay/sa {0}",
"DeviceOnlineWithName": "Nakakonekta si/ang {0}",
"DeviceOfflineWithName": "Nadiskonekta si/ang {0}",
"Collections": "Mga Koleksyon", "Collections": "Mga Koleksyon",
"ChapterNameValue": "Kabanata {0}", "ChapterNameValue": "Kabanata {0}",
"Channels": "Mga Channel",
"CameraImageUploadedFrom": "May bagong larawan na naupload galing sa/kay {0}",
"Books": "Mga Libro", "Books": "Mga Libro",
"AuthenticationSucceededWithUserName": "Napatunayan si/ang {0}", "AuthenticationSucceededWithUserName": "Napatunayan si/ang {0}",
"Artists": "Mga Artista", "Artists": "Mga Artista",
"Application": "Aplikasyon",
"AppDeviceValues": "Aplikasyon: {0}, Aparato: {1}", "AppDeviceValues": "Aplikasyon: {0}, Aparato: {1}",
"Albums": "Mga Album",
"TaskRefreshLibrary": "Suriin and Librerya ng Medya", "TaskRefreshLibrary": "Suriin and Librerya ng Medya",
"TaskRefreshChapterImagesDescription": "Gumawa ng larawan para sa mga pelikula na may kabanata.", "TaskRefreshChapterImagesDescription": "Gumawa ng larawan para sa mga pelikula na may kabanata.",
"TaskRefreshChapterImages": "Kunin ang mga larawan ng kabanata", "TaskRefreshChapterImages": "Kunin ang mga larawan ng kabanata",

View File

@@ -2,15 +2,17 @@
"Artists": "Listafólk", "Artists": "Listafólk",
"Collections": "Søvn", "Collections": "Søvn",
"Default": "Sjálvgildi", "Default": "Sjálvgildi",
"DeviceOfflineWithName": "{0} hevur slitið sambandið",
"External": "Ytri", "External": "Ytri",
"Genres": "Greinar", "Genres": "Greinar",
"Albums": "Album",
"AppDeviceValues": "App: {0}, Eind: {1}", "AppDeviceValues": "App: {0}, Eind: {1}",
"Application": "Nýtsluskipan",
"Books": "Bøkur", "Books": "Bøkur",
"Channels": "Rásir",
"ChapterNameValue": "Kapittul {0}", "ChapterNameValue": "Kapittul {0}",
"DeviceOnlineWithName": "{0} er sambundið",
"Favorites": "Yndis", "Favorites": "Yndis",
"Folders": "Mappur", "Folders": "Mappur",
"Forced": "Kravt", "Forced": "Kravt"
"FailedLoginAttemptWithUserName": "Miseydnað innritanarroynd frá {0}",
"HeaderFavoriteEpisodes": "Yndispartar",
"LabelIpAddressValue": "IP atsetur: {0}"
} }

View File

@@ -1,24 +1,41 @@
{ {
"Albums": "Albums",
"AppDeviceValues": "App : {0}, Appareil : {1}", "AppDeviceValues": "App : {0}, Appareil : {1}",
"Application": "Application",
"Artists": "Artistes", "Artists": "Artistes",
"AuthenticationSucceededWithUserName": "{0} authentifié avec succès", "AuthenticationSucceededWithUserName": "{0} authentifié avec succès",
"Books": "Livres", "Books": "Livres",
"CameraImageUploadedFrom": "Une nouvelle photo a été téléversée depuis {0}",
"Channels": "Chaînes",
"ChapterNameValue": "Chapitre {0}", "ChapterNameValue": "Chapitre {0}",
"Collections": "Collections", "Collections": "Collections",
"DeviceOfflineWithName": "{0} s'est déconnecté",
"DeviceOnlineWithName": "{0} est connecté",
"FailedLoginAttemptWithUserName": "Tentative de connexion échouée par {0}", "FailedLoginAttemptWithUserName": "Tentative de connexion échouée par {0}",
"Favorites": "Favoris", "Favorites": "Favoris",
"Folders": "Dossiers", "Folders": "Dossiers",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Artistes de l'album",
"HeaderContinueWatching": "Reprendre le visionnement", "HeaderContinueWatching": "Reprendre le visionnement",
"HeaderFavoriteAlbums": "Albums favoris",
"HeaderFavoriteArtists": "Artistes favoris",
"HeaderFavoriteEpisodes": "Épisodes favoris", "HeaderFavoriteEpisodes": "Épisodes favoris",
"HeaderFavoriteShows": "Séries favorites", "HeaderFavoriteShows": "Séries favorites",
"HeaderFavoriteSongs": "Chansons favorites",
"HeaderLiveTV": "TV en direct", "HeaderLiveTV": "TV en direct",
"HeaderNextUp": "À Suivre", "HeaderNextUp": "À Suivre",
"HeaderRecordingGroups": "Groupes d'enregistrements",
"HomeVideos": "Vidéos personnelles", "HomeVideos": "Vidéos personnelles",
"Inherit": "Hérite", "Inherit": "Hérite",
"ItemAddedWithName": "{0} a été ajouté à la médiathèque",
"ItemRemovedWithName": "{0} a été supprimé de la médiathèque",
"LabelIpAddressValue": "Adresse IP : {0}", "LabelIpAddressValue": "Adresse IP : {0}",
"LabelRunningTimeValue": "Durée : {0}", "LabelRunningTimeValue": "Durée : {0}",
"Latest": "Plus récent", "Latest": "Plus récent",
"MessageApplicationUpdated": "Le serveur Jellyfin a été mis à jour",
"MessageApplicationUpdatedTo": "Le serveur Jellyfin a été mis à jour vers la version {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "La configuration de la section {0} du serveur a été mise à jour",
"MessageServerConfigurationUpdated": "La configuration du serveur a été mise à jour",
"MixedContent": "Contenu mixte", "MixedContent": "Contenu mixte",
"Movies": "Films", "Movies": "Films",
"Music": "Musique", "Music": "Musique",
@@ -44,14 +61,24 @@
"NotificationOptionVideoPlayback": "Lecture vidéo démarrée", "NotificationOptionVideoPlayback": "Lecture vidéo démarrée",
"NotificationOptionVideoPlaybackStopped": "Lecture vidéo arrêtée", "NotificationOptionVideoPlaybackStopped": "Lecture vidéo arrêtée",
"Photos": "Photos", "Photos": "Photos",
"Playlists": "Listes de lecture",
"Plugin": "Extension",
"PluginInstalledWithName": "{0} a été installé", "PluginInstalledWithName": "{0} a été installé",
"PluginUninstalledWithName": "{0} a été désinstallé", "PluginUninstalledWithName": "{0} a été désinstallé",
"PluginUpdatedWithName": "{0} a été mis à jour", "PluginUpdatedWithName": "{0} a été mis à jour",
"ProviderValue": "Fournisseur : {0}",
"ScheduledTaskFailedWithName": "{0} a échoué", "ScheduledTaskFailedWithName": "{0} a échoué",
"ScheduledTaskStartedWithName": "{0} a commencé",
"ServerNameNeedsToBeRestarted": "{0} doit être redémarré",
"Shows": "Séries", "Shows": "Séries",
"Songs": "Chansons",
"StartupEmbyServerIsLoading": "Serveur Jellyfin en cours de chargement. Réessayez dans quelques instants.", "StartupEmbyServerIsLoading": "Serveur Jellyfin en cours de chargement. Réessayez dans quelques instants.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "Échec du téléchargement des sous-titres depuis {0} pour {1}", "SubtitleDownloadFailureFromForItem": "Échec du téléchargement des sous-titres depuis {0} pour {1}",
"Sync": "Synchroniser",
"System": "Système",
"TvShows": "Séries Télé", "TvShows": "Séries Télé",
"User": "Utilisateur",
"UserCreatedWithName": "L'utilisateur {0} a été créé", "UserCreatedWithName": "L'utilisateur {0} a été créé",
"UserDeletedWithName": "L'utilisateur {0} supprimé", "UserDeletedWithName": "L'utilisateur {0} supprimé",
"UserDownloadingItemWithValues": "{0} télécharge {1}", "UserDownloadingItemWithValues": "{0} télécharge {1}",
@@ -59,8 +86,11 @@
"UserOfflineFromDevice": "{0} s'est déconnecté de {1}", "UserOfflineFromDevice": "{0} s'est déconnecté de {1}",
"UserOnlineFromDevice": "{0} s'est connecté de {1}", "UserOnlineFromDevice": "{0} s'est connecté de {1}",
"UserPasswordChangedWithName": "Le mot de passe de utilisateur {0} a été modifié", "UserPasswordChangedWithName": "Le mot de passe de utilisateur {0} a été modifié",
"UserPolicyUpdatedWithName": "La politique de l'utilisateur a été mise à jour pour {0}",
"UserStartedPlayingItemWithValues": "{0} joue {1} sur {2}", "UserStartedPlayingItemWithValues": "{0} joue {1} sur {2}",
"UserStoppedPlayingItemWithValues": "{0} a terminé la lecture de {1} sur {2}", "UserStoppedPlayingItemWithValues": "{0} a terminé la lecture de {1} sur {2}",
"ValueHasBeenAddedToLibrary": "{0} a été ajouté à votre médiathèque",
"ValueSpecialEpisodeName": "Spécial - {0}",
"VersionNumber": "Version {0}", "VersionNumber": "Version {0}",
"TasksLibraryCategory": "Médiathèque", "TasksLibraryCategory": "Médiathèque",
"TasksMaintenanceCategory": "Entretien", "TasksMaintenanceCategory": "Entretien",
@@ -97,6 +127,8 @@
"HearingImpaired": "Malentendants", "HearingImpaired": "Malentendants",
"TaskRefreshTrickplayImages": "Générer des images Trickplay", "TaskRefreshTrickplayImages": "Générer des images Trickplay",
"TaskRefreshTrickplayImagesDescription": "Crée des aperçus Trickplay pour les vidéos dans les médiathèques activées.", "TaskRefreshTrickplayImagesDescription": "Crée des aperçus Trickplay pour les vidéos dans les médiathèques activées.",
"TaskCleanCollectionsAndPlaylists": "Nettoyer les collections et les listes de lecture",
"TaskCleanCollectionsAndPlaylistsDescription": "Supprime les éléments des collections et des listes de lecture qui n'existent plus.",
"TaskAudioNormalization": "Normalisation audio", "TaskAudioNormalization": "Normalisation audio",
"TaskAudioNormalizationDescription": "Analyse les fichiers à la recherche de données de normalisation audio.", "TaskAudioNormalizationDescription": "Analyse les fichiers à la recherche de données de normalisation audio.",
"TaskExtractMediaSegments": "Analyse des segments de média", "TaskExtractMediaSegments": "Analyse des segments de média",

View File

@@ -1,24 +1,41 @@
{ {
"Albums": "Albums",
"AppDeviceValues": "Application : {0}, Appareil : {1}", "AppDeviceValues": "Application : {0}, Appareil : {1}",
"Application": "Application",
"Artists": "Artistes", "Artists": "Artistes",
"AuthenticationSucceededWithUserName": "{0} authentifié avec succès", "AuthenticationSucceededWithUserName": "{0} authentifié avec succès",
"Books": "Livres", "Books": "Livres",
"CameraImageUploadedFrom": "Une photo a été téléchargée depuis {0}",
"Channels": "Chaînes",
"ChapterNameValue": "Chapitre {0}", "ChapterNameValue": "Chapitre {0}",
"Collections": "Collections", "Collections": "Collections",
"DeviceOfflineWithName": "{0} s'est déconnecté",
"DeviceOnlineWithName": "{0} est connecté",
"FailedLoginAttemptWithUserName": "Échec de connexion depuis {0}", "FailedLoginAttemptWithUserName": "Échec de connexion depuis {0}",
"Favorites": "Favoris", "Favorites": "Favoris",
"Folders": "Dossiers", "Folders": "Dossiers",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Artistes d'albums",
"HeaderContinueWatching": "Continuer de regarder", "HeaderContinueWatching": "Continuer de regarder",
"HeaderFavoriteAlbums": "Albums favoris",
"HeaderFavoriteArtists": "Artistes préférés",
"HeaderFavoriteEpisodes": "Épisodes favoris", "HeaderFavoriteEpisodes": "Épisodes favoris",
"HeaderFavoriteShows": "Séries favorites", "HeaderFavoriteShows": "Séries favorites",
"HeaderFavoriteSongs": "Chansons préférées",
"HeaderLiveTV": "TV en direct", "HeaderLiveTV": "TV en direct",
"HeaderNextUp": "Prochain à venir", "HeaderNextUp": "Prochain à venir",
"HeaderRecordingGroups": "Groupes d'enregistrements",
"HomeVideos": "Vidéos personnelles", "HomeVideos": "Vidéos personnelles",
"Inherit": "Hériter", "Inherit": "Hériter",
"ItemAddedWithName": "{0} a été ajouté à la médiathèque",
"ItemRemovedWithName": "{0} a été supprimé de la médiathèque",
"LabelIpAddressValue": "Adresse IP : {0}", "LabelIpAddressValue": "Adresse IP : {0}",
"LabelRunningTimeValue": "Durée : {0}", "LabelRunningTimeValue": "Durée : {0}",
"Latest": "Derniers", "Latest": "Derniers",
"MessageApplicationUpdated": "Le serveur Jellyfin a été mis à jour",
"MessageApplicationUpdatedTo": "Le serveur Jellyfin a été mis à jour en version {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "La configuration de la section {0} du serveur a été mise à jour",
"MessageServerConfigurationUpdated": "La configuration du serveur a été mise à jour",
"MixedContent": "Contenu mixte", "MixedContent": "Contenu mixte",
"Movies": "Films", "Movies": "Films",
"Music": "Musique", "Music": "Musique",
@@ -44,14 +61,24 @@
"NotificationOptionVideoPlayback": "Lecture vidéo démarrée", "NotificationOptionVideoPlayback": "Lecture vidéo démarrée",
"NotificationOptionVideoPlaybackStopped": "Lecture vidéo arrêtée", "NotificationOptionVideoPlaybackStopped": "Lecture vidéo arrêtée",
"Photos": "Photos", "Photos": "Photos",
"Playlists": "Listes de lecture",
"Plugin": "Extension",
"PluginInstalledWithName": "{0} a été installé", "PluginInstalledWithName": "{0} a été installé",
"PluginUninstalledWithName": "{0} a été désinstallé", "PluginUninstalledWithName": "{0} a été désinstallé",
"PluginUpdatedWithName": "{0} a été mis à jour", "PluginUpdatedWithName": "{0} a été mis à jour",
"ProviderValue": "Fournisseur : {0}",
"ScheduledTaskFailedWithName": "{0} a échoué", "ScheduledTaskFailedWithName": "{0} a échoué",
"ScheduledTaskStartedWithName": "{0} a démarré",
"ServerNameNeedsToBeRestarted": "{0} doit être redémarré",
"Shows": "Séries", "Shows": "Séries",
"Songs": "Chansons",
"StartupEmbyServerIsLoading": "Le serveur Jellyfin est en cours de chargement. Veuillez réessayer dans quelques instants.", "StartupEmbyServerIsLoading": "Le serveur Jellyfin est en cours de chargement. Veuillez réessayer dans quelques instants.",
"SubtitleDownloadFailureForItem": "Le téléchargement des sous-titres pour {0} a échoué.",
"SubtitleDownloadFailureFromForItem": "Échec du téléchargement des sous-titres depuis {0} pour {1}", "SubtitleDownloadFailureFromForItem": "Échec du téléchargement des sous-titres depuis {0} pour {1}",
"Sync": "Synchroniser",
"System": "Système",
"TvShows": "Séries TV", "TvShows": "Séries TV",
"User": "Utilisateur",
"UserCreatedWithName": "L'utilisateur {0} a été créé", "UserCreatedWithName": "L'utilisateur {0} a été créé",
"UserDeletedWithName": "L'utilisateur {0} a été supprimé", "UserDeletedWithName": "L'utilisateur {0} a été supprimé",
"UserDownloadingItemWithValues": "{0} est en train de télécharger {1}", "UserDownloadingItemWithValues": "{0} est en train de télécharger {1}",
@@ -59,8 +86,11 @@
"UserOfflineFromDevice": "{0} s'est déconnecté depuis {1}", "UserOfflineFromDevice": "{0} s'est déconnecté depuis {1}",
"UserOnlineFromDevice": "{0} s'est connecté depuis {1}", "UserOnlineFromDevice": "{0} s'est connecté depuis {1}",
"UserPasswordChangedWithName": "Le mot de passe pour l'utilisateur {0} a été modifié", "UserPasswordChangedWithName": "Le mot de passe pour l'utilisateur {0} a été modifié",
"UserPolicyUpdatedWithName": "La politique de l'utilisateur a été mise à jour pour {0}",
"UserStartedPlayingItemWithValues": "{0} est en train de lire {1} sur {2}", "UserStartedPlayingItemWithValues": "{0} est en train de lire {1} sur {2}",
"UserStoppedPlayingItemWithValues": "{0} vient d'arrêter la lecture de {1} sur {2}", "UserStoppedPlayingItemWithValues": "{0} vient d'arrêter la lecture de {1} sur {2}",
"ValueHasBeenAddedToLibrary": "{0} a été ajouté à votre médiathèque",
"ValueSpecialEpisodeName": "Spécial - {0}",
"VersionNumber": "Version {0}", "VersionNumber": "Version {0}",
"TasksChannelsCategory": "Chaînes en ligne", "TasksChannelsCategory": "Chaînes en ligne",
"TaskDownloadMissingSubtitlesDescription": "Recherche les sous-titres manquants sur Internet en se basant sur la configuration des métadonnées.", "TaskDownloadMissingSubtitlesDescription": "Recherche les sous-titres manquants sur Internet en se basant sur la configuration des métadonnées.",
@@ -97,6 +127,8 @@
"HearingImpaired": "Malentendants", "HearingImpaired": "Malentendants",
"TaskRefreshTrickplayImages": "Générer des images Trickplay", "TaskRefreshTrickplayImages": "Générer des images Trickplay",
"TaskRefreshTrickplayImagesDescription": "Crée des aperçus Trickplay pour les vidéos dans les médiathèques activées.", "TaskRefreshTrickplayImagesDescription": "Crée des aperçus Trickplay pour les vidéos dans les médiathèques activées.",
"TaskCleanCollectionsAndPlaylists": "Nettoyer les collections et les listes de lecture",
"TaskCleanCollectionsAndPlaylistsDescription": "Supprime les éléments des collections et des listes de lecture qui n'existent plus.",
"TaskAudioNormalization": "Normalisation audio", "TaskAudioNormalization": "Normalisation audio",
"TaskAudioNormalizationDescription": "Analyse les fichiers à la recherche de données de normalisation audio.", "TaskAudioNormalizationDescription": "Analyse les fichiers à la recherche de données de normalisation audio.",
"TaskDownloadMissingLyricsDescription": "Téléchargement des paroles des chansons", "TaskDownloadMissingLyricsDescription": "Téléchargement des paroles des chansons",

View File

@@ -1,10 +1,15 @@
{ {
"Albums": "Albaim",
"Artists": "Ealaíontóirí", "Artists": "Ealaíontóirí",
"AuthenticationSucceededWithUserName": "D'éirigh le fíordheimhniú {0}", "AuthenticationSucceededWithUserName": "D'éirigh le fíordheimhniú {0}",
"Books": "Leabhair", "Books": "Leabhair",
"CameraImageUploadedFrom": "Uaslódáladh íomhá ceamara nua ó {0}",
"Channels": "Cainéil",
"ChapterNameValue": "Caibidil {0}", "ChapterNameValue": "Caibidil {0}",
"Collections": "Bailiúcháin", "Collections": "Bailiúcháin",
"Default": "Réamhshocrú", "Default": "Réamhshocrú",
"DeviceOfflineWithName": "Tá {0} dícheangailte",
"DeviceOnlineWithName": "Tá {0} nasctha",
"External": "Seachtrach", "External": "Seachtrach",
"FailedLoginAttemptWithUserName": "Theip ar iarracht logáil isteach ó {0}", "FailedLoginAttemptWithUserName": "Theip ar iarracht logáil isteach ó {0}",
"Favorites": "Ceanáin", "Favorites": "Ceanáin",
@@ -24,27 +29,41 @@
"TaskRefreshChannelsDescription": "Athnuachan eolas faoi chainéil idirlín.", "TaskRefreshChannelsDescription": "Athnuachan eolas faoi chainéil idirlín.",
"TaskOptimizeDatabase": "Bunachar sonraí a bharrfheabhsú", "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.", "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", "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.", "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í.", "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.", "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.", "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.", "TaskMoveTrickplayImagesDescription": "Bogtar comhaid trickplay atá ann cheana de réir socruithe na leabharlainne.",
"AppDeviceValues": "Aip: {0}, Gléas: {1}", "AppDeviceValues": "Aip: {0}, Gléas: {1}",
"Application": "Feidhmchlár",
"Folders": "Fillteáin", "Folders": "Fillteáin",
"Forced": "Éigean", "Forced": "Éigean",
"Genres": "Seánraí", "Genres": "Seánraí",
"HeaderAlbumArtists": "Ealaíontóirí albam",
"HeaderContinueWatching": "Leanúint ar aghaidh ag Breathnú", "HeaderContinueWatching": "Leanúint ar aghaidh ag Breathnú",
"HeaderFavoriteAlbums": "Albam is fearr leat",
"HeaderFavoriteArtists": "Ealaíontóirí is Fearr",
"HeaderFavoriteEpisodes": "Eipeasóid is fearr leat", "HeaderFavoriteEpisodes": "Eipeasóid is fearr leat",
"HeaderFavoriteShows": "Seónna is Fearr", "HeaderFavoriteShows": "Seónna is Fearr",
"HeaderFavoriteSongs": "Amhráin is fearr leat",
"HeaderLiveTV": "Teilifís beo", "HeaderLiveTV": "Teilifís beo",
"HeaderNextUp": "Ar Aghaidh Suas", "HeaderNextUp": "Ar Aghaidh Suas",
"HeaderRecordingGroups": "Grúpaí Taifeadta",
"HearingImpaired": "Lag éisteachta", "HearingImpaired": "Lag éisteachta",
"HomeVideos": "Físeáin Baile", "HomeVideos": "Físeáin Baile",
"Inherit": "Oidhreacht", "Inherit": "Oidhreacht",
"ItemAddedWithName": "Cuireadh {0} leis an leabharlann",
"ItemRemovedWithName": "Baineadh {0} den leabharlann",
"LabelIpAddressValue": "Seoladh IP: {0}", "LabelIpAddressValue": "Seoladh IP: {0}",
"LabelRunningTimeValue": "Am rite: {0}", "LabelRunningTimeValue": "Am rite: {0}",
"Latest": "Is déanaí", "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", "MixedContent": "Ábhar measctha",
"Movies": "Scannáin", "Movies": "Scannáin",
"Music": "Ceol", "Music": "Ceol",
@@ -70,15 +89,24 @@
"NotificationOptionVideoPlayback": "Cuireadh tús le hathsheinm físe", "NotificationOptionVideoPlayback": "Cuireadh tús le hathsheinm físe",
"NotificationOptionVideoPlaybackStopped": "Cuireadh deireadh le hathsheinm físe", "NotificationOptionVideoPlaybackStopped": "Cuireadh deireadh le hathsheinm físe",
"Photos": "Grianghraif", "Photos": "Grianghraif",
"Playlists": "Seinmliostaí",
"Plugin": "Breiseán",
"PluginInstalledWithName": "Suiteáladh {0}", "PluginInstalledWithName": "Suiteáladh {0}",
"PluginUninstalledWithName": "Díshuiteáladh {0}", "PluginUninstalledWithName": "Díshuiteáladh {0}",
"PluginUpdatedWithName": "Nuashonraíodh {0}", "PluginUpdatedWithName": "Nuashonraíodh {0}",
"ProviderValue": "Soláthraí: {0}",
"ScheduledTaskFailedWithName": "Theip ar {0}", "ScheduledTaskFailedWithName": "Theip ar {0}",
"ScheduledTaskStartedWithName": "Thosaigh {0}",
"ServerNameNeedsToBeRestarted": "Ní mór {0} a atosú",
"Shows": "Seónna", "Shows": "Seónna",
"Songs": "Amhráin",
"StartupEmbyServerIsLoading": "Tá freastalaí Jellyfin á luchtú. Bain triail eile as gan mhoill.", "StartupEmbyServerIsLoading": "Tá freastalaí Jellyfin á luchtú. Bain triail eile as gan mhoill.",
"SubtitleDownloadFailureFromForItem": "Theip ar fhotheidil a íoslódáil ó {0} le haghaidh {1}", "SubtitleDownloadFailureFromForItem": "Theip ar fhotheidil a íoslódáil ó {0} le haghaidh {1}",
"Sync": "Sioncrónaigh",
"System": "Córas",
"TvShows": "Seónna Teilifíse", "TvShows": "Seónna Teilifíse",
"Undefined": "Neamhshainithe", "Undefined": "Neamhshainithe",
"User": "Úsáideoir",
"UserCreatedWithName": "Cruthaíodh úsáideoir {0}", "UserCreatedWithName": "Cruthaíodh úsáideoir {0}",
"UserDeletedWithName": "Scriosadh úsáideoir {0}", "UserDeletedWithName": "Scriosadh úsáideoir {0}",
"UserDownloadingItemWithValues": "Tá {0} á íoslódáil {1}", "UserDownloadingItemWithValues": "Tá {0} á íoslódáil {1}",
@@ -86,8 +114,11 @@
"UserOfflineFromDevice": "Tá {0} dícheangailte ó {1}", "UserOfflineFromDevice": "Tá {0} dícheangailte ó {1}",
"UserOnlineFromDevice": "Tá {0} ar líne ó {1}", "UserOnlineFromDevice": "Tá {0} ar líne ó {1}",
"UserPasswordChangedWithName": "Athraíodh pasfhocal don úsáideoir {0}", "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}", "UserStartedPlayingItemWithValues": "Tá {0} ag seinnt {1} ar {2}",
"UserStoppedPlayingItemWithValues": "Chríochnaigh {0} ag imirt {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}", "VersionNumber": "Leagan {0}",
"TasksMaintenanceCategory": "Cothabháil", "TasksMaintenanceCategory": "Cothabháil",
"TasksLibraryCategory": "Leabharlann", "TasksLibraryCategory": "Leabharlann",
@@ -106,6 +137,5 @@
"TaskCleanTranscode": "Eolaire Transcode Glan", "TaskCleanTranscode": "Eolaire Transcode Glan",
"TaskDownloadMissingSubtitles": "Íosluchtaigh fotheidil ar iarraidh", "TaskDownloadMissingSubtitles": "Íosluchtaigh fotheidil ar iarraidh",
"CleanupUserDataTask": "Tasc glantacháin sonraí úsáideora", "CleanupUserDataTask": "Tasc glantacháin sonraí úsáideora",
"CleanupUserDataTaskDescription": "Glanann sé gach sonraí úsáideora (stádas faire, stádas is fearr leat srl.) ó mheáin nach bhfuil i láthair a thuilleadh ar feadh 90 lá ar a laghad.", "CleanupUserDataTaskDescription": "Glanann sé gach sonraí úsáideora (stádas faire, stádas is fearr leat srl.) ó mheáin nach bhfuil i láthair a thuilleadh ar feadh 90 lá ar a laghad."
"Original": "Bunaidh"
} }

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