Compare commits

...

41 Commits

Author SHA1 Message Date
Crowdin Bot
6ec7f555bd feat(i18n): update translations from Crowdin 2026-05-30 07:23:23 +00:00
Gauvain
0f86c776ba feat(player): add chapter markers and chapter list (#1586)
Co-authored-by: retardgerman <78982850+retardgerman@users.noreply.github.com>
2026-05-30 09:22:23 +02:00
lostb1t
07b79de203 fix: Do not cache background request for mediasources (#1602) 2026-05-30 09:11:59 +02:00
renovate[bot]
cf91c4c682 chore(deps): Update dependency @react-native-community/netinfo to v12 (#1457)
Some checks failed
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (actions) (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (javascript-typescript) (push) Has been cancelled
🏷️🔀Merge Conflict Labeler / 🏷️ Labeling Merge Conflicts (push) Has been cancelled
🏗️ Build Apps / 🤖 Build Android APK (Phone) (push) Has been cancelled
🏗️ Build Apps / 🤖 Build Android APK (TV) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone - Unsigned) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build tvOS IPA (push) Has been cancelled
🏗️ Build Apps / 🍎 Build tvOS IPA (Unsigned) (push) Has been cancelled
🔒 Lockfile Consistency Check / 🔍 Check bun.lock and package.json consistency (push) Has been cancelled
🚦 Security & Quality Gate / 📝 Validate PR Title (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Vulnerable Dependencies (push) Has been cancelled
🚦 Security & Quality Gate / 🚑 Expo Doctor Check (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (check) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (format) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (lint) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (typecheck) (push) Has been cancelled
2026-05-27 22:44:18 +02:00
renovate[bot]
1545790528 chore(deps): Lock file maintenance (#1595)
Some checks failed
🏗️ Build Apps / 🤖 Build Android APK (Phone) (push) Has been cancelled
🏗️ Build Apps / 🤖 Build Android APK (TV) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone - Unsigned) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build tvOS IPA (push) Has been cancelled
🏗️ Build Apps / 🍎 Build tvOS IPA (Unsigned) (push) Has been cancelled
🔒 Lockfile Consistency Check / 🔍 Check bun.lock and package.json consistency (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (actions) (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (javascript-typescript) (push) Has been cancelled
🏷️🔀Merge Conflict Labeler / 🏷️ Labeling Merge Conflicts (push) Has been cancelled
🚦 Security & Quality Gate / 📝 Validate PR Title (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Vulnerable Dependencies (push) Has been cancelled
🚦 Security & Quality Gate / 🚑 Expo Doctor Check (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (check) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (format) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (lint) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (typecheck) (push) Has been cancelled
2026-05-27 15:38:42 +02:00
renovate[bot]
11ec778bd8 chore(deps): Update github/codeql-action action to v4.36.0 (#1593)
Some checks failed
🏷️🔀Merge Conflict Labeler / 🏷️ Labeling Merge Conflicts (push) Has been cancelled
🏗️ Build Apps / 🤖 Build Android APK (Phone) (push) Has been cancelled
🏗️ Build Apps / 🤖 Build Android APK (TV) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone - Unsigned) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build tvOS IPA (push) Has been cancelled
🏗️ Build Apps / 🍎 Build tvOS IPA (Unsigned) (push) Has been cancelled
🔒 Lockfile Consistency Check / 🔍 Check bun.lock and package.json consistency (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (actions) (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (javascript-typescript) (push) Has been cancelled
🚦 Security & Quality Gate / 📝 Validate PR Title (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Vulnerable Dependencies (push) Has been cancelled
🚦 Security & Quality Gate / 🚑 Expo Doctor Check (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (check) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (format) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (lint) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (typecheck) (push) Has been cancelled
2026-05-26 15:23:11 +02:00
renovate[bot]
0c6ef5cbda chore(deps): Update maxim-lobanov/setup-xcode digest to ed7a3b1 (#1581) 2026-05-26 13:34:48 +02:00
renovate[bot]
1e14c7ec46 chore(deps): Update actions/cache action to v5.0.5 (#1582) 2026-05-26 11:42:23 +02:00
renovate[bot]
c8ddb9a892 chore(deps): Update actions/upload-artifact action to v7 (#1584) 2026-05-26 11:20:23 +02:00
renovate[bot]
9ee71a002d chore(deps): Update oven-sh/setup-bun action to v2.2.0 (#1583)
Some checks failed
🏗️ Build Apps / 🤖 Build Android APK (Phone) (push) Has been cancelled
🏗️ Build Apps / 🤖 Build Android APK (TV) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone - Unsigned) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build tvOS IPA (push) Has been cancelled
🏗️ Build Apps / 🍎 Build tvOS IPA (Unsigned) (push) Has been cancelled
🔒 Lockfile Consistency Check / 🔍 Check bun.lock and package.json consistency (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (actions) (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (javascript-typescript) (push) Has been cancelled
🏷️🔀Merge Conflict Labeler / 🏷️ Labeling Merge Conflicts (push) Has been cancelled
🚦 Security & Quality Gate / 📝 Validate PR Title (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Vulnerable Dependencies (push) Has been cancelled
🚦 Security & Quality Gate / 🚑 Expo Doctor Check (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (check) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (format) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (lint) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (typecheck) (push) Has been cancelled
2026-05-26 09:40:57 +02:00
renovate[bot]
c950408bdb chore(deps): Update dependency expo-doctor to v1.19.7 (#1578)
Some checks failed
🏷️🔀Merge Conflict Labeler / 🏷️ Labeling Merge Conflicts (push) Has been cancelled
🏗️ Build Apps / 🤖 Build Android APK (Phone) (push) Has been cancelled
🏗️ Build Apps / 🤖 Build Android APK (TV) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone - Unsigned) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build tvOS IPA (push) Has been cancelled
🏗️ Build Apps / 🍎 Build tvOS IPA (Unsigned) (push) Has been cancelled
🔒 Lockfile Consistency Check / 🔍 Check bun.lock and package.json consistency (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (actions) (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (javascript-typescript) (push) Has been cancelled
🚦 Security & Quality Gate / 📝 Validate PR Title (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Vulnerable Dependencies (push) Has been cancelled
🚦 Security & Quality Gate / 🚑 Expo Doctor Check (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (check) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (format) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (lint) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (typecheck) (push) Has been cancelled
🌐 Translation Sync / sync-translations (push) Has been cancelled
2026-05-24 14:00:03 +02:00
renovate[bot]
1ac0644a57 chore(deps): Pin expo/expo-github-action action to b184ff8 (#1580)
Some checks failed
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (actions) (push) Has been cancelled
🏗️ Build Apps / 🤖 Build Android APK (Phone) (push) Has been cancelled
🏗️ Build Apps / 🤖 Build Android APK (TV) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone - Unsigned) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build tvOS IPA (push) Has been cancelled
🏗️ Build Apps / 🍎 Build tvOS IPA (Unsigned) (push) Has been cancelled
🔒 Lockfile Consistency Check / 🔍 Check bun.lock and package.json consistency (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (javascript-typescript) (push) Has been cancelled
🏷️🔀Merge Conflict Labeler / 🏷️ Labeling Merge Conflicts (push) Has been cancelled
🚦 Security & Quality Gate / 📝 Validate PR Title (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Vulnerable Dependencies (push) Has been cancelled
🚦 Security & Quality Gate / 🚑 Expo Doctor Check (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (check) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (format) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (lint) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (typecheck) (push) Has been cancelled
2026-05-22 12:32:51 +02:00
Gauvain
0aa2dc5924 feat: Enables iOS TV builds (#1422) 2026-05-22 08:49:15 +02:00
renovate[bot]
e7f200a114 chore(deps): Update dependency i18next to v26 (#1573)
Some checks failed
🏗️ Build Apps / 🤖 Build Android APK (Phone) (push) Has been cancelled
🏗️ Build Apps / 🤖 Build Android APK (TV) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone - Unsigned) (push) Has been cancelled
🔒 Lockfile Consistency Check / 🔍 Check bun.lock and package.json consistency (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (actions) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (lint) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (typecheck) (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (javascript-typescript) (push) Has been cancelled
🏷️🔀Merge Conflict Labeler / 🏷️ Labeling Merge Conflicts (push) Has been cancelled
🚦 Security & Quality Gate / 📝 Validate PR Title (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Vulnerable Dependencies (push) Has been cancelled
🚦 Security & Quality Gate / 🚑 Expo Doctor Check (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (check) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (format) (push) Has been cancelled
2026-05-22 01:32:27 +02:00
renovate[bot]
da9afacbf7 chore(deps): Update dependency react-i18next to v17 (#1574) 2026-05-22 01:07:52 +02:00
renovate[bot]
80fdd579f3 chore(deps): Update dependency @react-native-community/cli to v20.1.3 (#1571)
Some checks failed
🏗️ Build Apps / 🤖 Build Android APK (Phone) (push) Has been cancelled
🏗️ Build Apps / 🤖 Build Android APK (TV) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone - Unsigned) (push) Has been cancelled
🔒 Lockfile Consistency Check / 🔍 Check bun.lock and package.json consistency (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (actions) (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (javascript-typescript) (push) Has been cancelled
🏷️🔀Merge Conflict Labeler / 🏷️ Labeling Merge Conflicts (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (check) (push) Has been cancelled
🚦 Security & Quality Gate / 📝 Validate PR Title (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Vulnerable Dependencies (push) Has been cancelled
🚦 Security & Quality Gate / 🚑 Expo Doctor Check (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (format) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (lint) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (typecheck) (push) Has been cancelled
2026-05-21 02:28:38 +02:00
renovate[bot]
f79cf1925d chore(deps): Update dependency @react-native-tvos/config-tv to v0.1.6 (#1430) 2026-05-21 00:18:29 +02:00
renovate[bot]
8bb0d845a2 chore(deps): Update dependency lint-staged to v17 (#1570) 2026-05-20 23:29:39 +02:00
renovate[bot]
c7cd8217c9 chore(deps): Update dependency react-i18next to v16.5.8 (#1500)
Some checks failed
🏗️ Build Apps / 🤖 Build Android APK (Phone) (push) Has been cancelled
🏗️ Build Apps / 🤖 Build Android APK (TV) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone - Unsigned) (push) Has been cancelled
🔒 Lockfile Consistency Check / 🔍 Check bun.lock and package.json consistency (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (actions) (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (javascript-typescript) (push) Has been cancelled
🏷️🔀Merge Conflict Labeler / 🏷️ Labeling Merge Conflicts (push) Has been cancelled
🚦 Security & Quality Gate / 📝 Validate PR Title (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Vulnerable Dependencies (push) Has been cancelled
🚦 Security & Quality Gate / 🚑 Expo Doctor Check (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (check) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (format) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (lint) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (typecheck) (push) Has been cancelled
2026-05-20 23:03:35 +02:00
renovate[bot]
235ba1473f chore(deps): Update dependency lint-staged to v16.4.0 (#1507) 2026-05-20 22:40:20 +02:00
renovate[bot]
284a4e3d41 chore(deps): Update dependency expo-doctor to v1.18.22 (#1506) 2026-05-20 19:10:36 +02:00
renovate[bot]
1fd3574520 chore(deps): Update actions/github-script action to v9 (#1568) 2026-05-20 18:36:38 +02:00
renovate[bot]
f1188c090a chore(deps): Update peter-evans/create-pull-request action to v8.1.1 (#1566) 2026-05-20 17:37:53 +02:00
renovate[bot]
1321a5c000 chore(deps): Update actions/dependency-review-action action to v5 (#1567) 2026-05-20 17:05:10 +02:00
renovate[bot]
52bc5e912d chore(deps): Update actions/dependency-review-action action to v4.9.0 (#1502) 2026-05-20 15:28:34 +02:00
renovate[bot]
7bccafc476 chore(deps): Update actions/dependency-review-action action to v4.8.3 (#1499)
Some checks failed
🏗️ Build Apps / 🤖 Build Android APK (Phone) (push) Has been cancelled
🏗️ Build Apps / 🤖 Build Android APK (TV) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone - Unsigned) (push) Has been cancelled
🔒 Lockfile Consistency Check / 🔍 Check bun.lock and package.json consistency (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (actions) (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (javascript-typescript) (push) Has been cancelled
🏷️🔀Merge Conflict Labeler / 🏷️ Labeling Merge Conflicts (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (lint) (push) Has been cancelled
🚦 Security & Quality Gate / 📝 Validate PR Title (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Vulnerable Dependencies (push) Has been cancelled
🚦 Security & Quality Gate / 🚑 Expo Doctor Check (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (check) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (format) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (typecheck) (push) Has been cancelled
2026-05-20 14:49:24 +02:00
renovate[bot]
8df61838d4 chore(deps): Update actions/cache action to v5.0.5 (#1429) 2026-05-20 13:43:17 +02:00
renovate[bot]
55776d887f chore(deps): Update maxim-lobanov/setup-xcode digest to ed7a3b1 (#1498) 2026-05-20 13:17:20 +02:00
renovate[bot]
2e7079cb5a chore(deps): Update github/codeql-action action to v4.35.5 (#1508) 2026-05-20 12:54:50 +02:00
renovate[bot]
428455f6a6 chore(deps): Update actions/setup-node action to v6.4.0 (#1503)
Some checks failed
🏗️ Build Apps / 🤖 Build Android APK (Phone) (push) Has been cancelled
🏗️ Build Apps / 🤖 Build Android APK (TV) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone - Unsigned) (push) Has been cancelled
🔒 Lockfile Consistency Check / 🔍 Check bun.lock and package.json consistency (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (javascript-typescript) (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (actions) (push) Has been cancelled
🏷️🔀Merge Conflict Labeler / 🏷️ Labeling Merge Conflicts (push) Has been cancelled
🚦 Security & Quality Gate / 📝 Validate PR Title (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Vulnerable Dependencies (push) Has been cancelled
🚦 Security & Quality Gate / 🚑 Expo Doctor Check (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (check) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (format) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (lint) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (typecheck) (push) Has been cancelled
2026-05-20 01:08:17 +02:00
renovate[bot]
8c749cdc4d chore(deps): Update oven-sh/setup-bun action to v2.2.0 (#1509) 2026-05-20 00:44:45 +02:00
renovate[bot]
7ed0c00ce7 chore(deps): Update actions/upload-artifact action to v7 (#1510) 2026-05-20 00:24:58 +02:00
renovate[bot]
222ae69644 chore(deps): Update marocchino/sticky-pull-request-comment action to v3 (#1512) 2026-05-20 00:03:36 +02:00
renovate[bot]
fec8df37f7 chore(deps): Pin dependencies (#1537) 2026-05-19 23:40:40 +02:00
renovate[bot]
0e0e722e1c chore(deps): Update crowdin/github-action action to v2.16.2 (#1504)
Some checks failed
🏗️ Build Apps / 🤖 Build Android APK (Phone) (push) Has been cancelled
🏗️ Build Apps / 🤖 Build Android APK (TV) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone - Unsigned) (push) Has been cancelled
🔒 Lockfile Consistency Check / 🔍 Check bun.lock and package.json consistency (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (actions) (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (javascript-typescript) (push) Has been cancelled
🏷️🔀Merge Conflict Labeler / 🏷️ Labeling Merge Conflicts (push) Has been cancelled
🚦 Security & Quality Gate / 📝 Validate PR Title (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Vulnerable Dependencies (push) Has been cancelled
🚦 Security & Quality Gate / 🚑 Expo Doctor Check (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (check) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (format) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (lint) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (typecheck) (push) Has been cancelled
🌐 Translation Sync / sync-translations (push) Has been cancelled
2026-05-19 19:58:56 +02:00
Théo FORTIN
2ce810c191 fix(player/ios): Disable subtitles embedding for iOS simulator (#1544)
Some checks failed
🏗️ Build Apps / 🤖 Build Android APK (Phone) (push) Has been cancelled
🏗️ Build Apps / 🤖 Build Android APK (TV) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone - Unsigned) (push) Has been cancelled
🔒 Lockfile Consistency Check / 🔍 Check bun.lock and package.json consistency (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (actions) (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (javascript-typescript) (push) Has been cancelled
🏷️🔀Merge Conflict Labeler / 🏷️ Labeling Merge Conflicts (push) Has been cancelled
🚦 Security & Quality Gate / 📝 Validate PR Title (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Vulnerable Dependencies (push) Has been cancelled
🚦 Security & Quality Gate / 🚑 Expo Doctor Check (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (check) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (format) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (lint) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (typecheck) (push) Has been cancelled
🌐 Translation Sync / sync-translations (push) Has been cancelled
2026-04-24 10:44:30 +02:00
Alex Collado
564a593a3a fix: Close modal when back button is pressed on Android (#1487)
Some checks failed
🚦 Security & Quality Gate / 🔍 Vulnerable Dependencies (push) Has been cancelled
🚦 Security & Quality Gate / 🚑 Expo Doctor Check (push) Has been cancelled
🏗️ Build Apps / 🤖 Build Android APK (Phone) (push) Has been cancelled
🏗️ Build Apps / 🤖 Build Android APK (TV) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone - Unsigned) (push) Has been cancelled
🔒 Lockfile Consistency Check / 🔍 Check bun.lock and package.json consistency (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (actions) (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (javascript-typescript) (push) Has been cancelled
🏷️🔀Merge Conflict Labeler / 🏷️ Labeling Merge Conflicts (push) Has been cancelled
🚦 Security & Quality Gate / 📝 Validate PR Title (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (check) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (format) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (lint) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (typecheck) (push) Has been cancelled
🌐 Translation Sync / sync-translations (push) Has been cancelled
2026-03-12 13:45:17 +01:00
Gallyam Biktashev
479e23f001 fix(i18n): Update transalation for Russian lang (#1427)
Some checks failed
🏗️ Build Apps / 🤖 Build Android APK (Phone) (push) Has been cancelled
🏗️ Build Apps / 🤖 Build Android APK (TV) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone - Unsigned) (push) Has been cancelled
🔒 Lockfile Consistency Check / 🔍 Check bun.lock and package.json consistency (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (actions) (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (javascript-typescript) (push) Has been cancelled
🏷️🔀Merge Conflict Labeler / 🏷️ Labeling Merge Conflicts (push) Has been cancelled
🌐 Translation Sync / sync-translations (push) Has been cancelled
🚦 Security & Quality Gate / 📝 Validate PR Title (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Vulnerable Dependencies (push) Has been cancelled
🚦 Security & Quality Gate / 🚑 Expo Doctor Check (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (check) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (format) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (lint) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (typecheck) (push) Has been cancelled
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-21 18:51:01 +01:00
github-actions[bot]
019f863b3e feat: New Crowdin Translations (#1346)
Some checks failed
🏗️ Build Apps / 🤖 Build Android APK (Phone) (push) Has been cancelled
🏗️ Build Apps / 🤖 Build Android APK (TV) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone) (push) Has been cancelled
🏗️ Build Apps / 🍎 Build iOS IPA (Phone - Unsigned) (push) Has been cancelled
🔒 Lockfile Consistency Check / 🔍 Check bun.lock and package.json consistency (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (actions) (push) Has been cancelled
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (javascript-typescript) (push) Has been cancelled
🏷️🔀Merge Conflict Labeler / 🏷️ Labeling Merge Conflicts (push) Has been cancelled
🌐 Translation Sync / sync-translations (push) Has been cancelled
🚦 Security & Quality Gate / 📝 Validate PR Title (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Vulnerable Dependencies (push) Has been cancelled
🚦 Security & Quality Gate / 🚑 Expo Doctor Check (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (check) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (format) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (lint) (push) Has been cancelled
🚦 Security & Quality Gate / 🔍 Lint & Test (typecheck) (push) Has been cancelled
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-19 07:45:11 +01:00
renovate[bot]
54d8693999 chore(deps): Update crowdin/github-action action to v2.14.1 (#1453)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-19 07:44:30 +01:00
quang-tran
8c0cbffd98 fix(logs): change the mimeType and UTI to the proper types for log ex… (#1424) 2026-02-19 07:39:29 +01:00
52 changed files with 3465 additions and 3071 deletions

View File

@@ -26,7 +26,7 @@ jobs:
steps: steps:
- name: 🔍 Get PR and Artifacts - name: 🔍 Get PR and Artifacts
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
with: with:
script: | script: |
// Check if we're running from a fork (more precise detection) // Check if we're running from a fork (more precise detection)
@@ -188,6 +188,17 @@ jobs:
if (latestAppsRun) { if (latestAppsRun) {
console.log(`Getting individual job statuses for run ${latestAppsRun.id} (status: ${latestAppsRun.status}, conclusion: ${latestAppsRun.conclusion || 'none'})`); console.log(`Getting individual job statuses for run ${latestAppsRun.id} (status: ${latestAppsRun.status}, conclusion: ${latestAppsRun.conclusion || 'none'})`);
// Map job names to our build targets. Declared outside the try so
// the catch fallback can reuse the same keys.
const jobMappings = {
'Android Phone': ['🤖 Build Android APK (Phone)', 'build-android-phone'],
'Android TV': ['🤖 Build Android APK (TV)', 'build-android-tv'],
'iOS': ['🍎 Build iOS IPA (Phone)', 'build-ios-phone'],
'iOS Unsigned': ['🍎 Build iOS IPA (Phone - Unsigned)', 'build-ios-phone-unsigned'],
'tvOS': ['🍎 Build tvOS IPA', 'build-ios-tv'],
'tvOS Unsigned': ['🍎 Build tvOS IPA (Unsigned)', 'build-ios-tv-unsigned']
};
try { try {
// Get all jobs for this workflow run // Get all jobs for this workflow run
const { data: jobs } = await github.rest.actions.listJobsForWorkflowRun({ const { data: jobs } = await github.rest.actions.listJobsForWorkflowRun({
@@ -216,13 +227,6 @@ jobs:
return; // Exit early return; // Exit early
} }
// Map job names to our build targets
const jobMappings = {
'Android Phone': ['🤖 Build Android APK (Phone)', 'build-android-phone'],
'Android TV': ['🤖 Build Android APK (TV)', 'build-android-tv'],
'iOS Phone': ['🍎 Build iOS IPA (Phone)', 'build-ios-phone']
};
// Create individual status for each job // Create individual status for each job
for (const [platform, jobNames] of Object.entries(jobMappings)) { for (const [platform, jobNames] of Object.entries(jobMappings)) {
const job = jobs.jobs.find(j => const job = jobs.jobs.find(j =>
@@ -236,7 +240,9 @@ jobs:
conclusion: job.conclusion, conclusion: job.conclusion,
url: job.html_url, url: job.html_url,
runId: latestAppsRun.id, runId: latestAppsRun.id,
created_at: job.started_at || latestAppsRun.created_at created_at: job.started_at || latestAppsRun.created_at,
started_at: job.started_at,
completed_at: job.completed_at
}; };
console.log(`Mapped ${platform} to job: ${job.name} (${job.status}/${job.conclusion || 'none'})`); console.log(`Mapped ${platform} to job: ${job.name} (${job.status}/${job.conclusion || 'none'})`);
} else { } else {
@@ -247,22 +253,30 @@ jobs:
conclusion: latestAppsRun.conclusion, conclusion: latestAppsRun.conclusion,
url: latestAppsRun.html_url, url: latestAppsRun.html_url,
runId: latestAppsRun.id, runId: latestAppsRun.id,
created_at: latestAppsRun.created_at created_at: latestAppsRun.created_at,
started_at: latestAppsRun.run_started_at,
completed_at: latestAppsRun.updated_at
}; };
} }
} }
} catch (error) { } catch (error) {
console.log(`Failed to get jobs for run ${latestAppsRun.id}:`, error.message); console.log(`Failed to get jobs for run ${latestAppsRun.id}:`, error.message);
// Fallback to workflow-level status // Fallback to workflow-level status for every build target.
buildStatuses['Android Phone'] = buildStatuses['Android TV'] = buildStatuses['iOS Phone'] = { // Keys must match jobMappings / buildTargets statusKey values.
const fallbackStatus = {
name: latestAppsRun.name, name: latestAppsRun.name,
status: latestAppsRun.status, status: latestAppsRun.status,
conclusion: latestAppsRun.conclusion, conclusion: latestAppsRun.conclusion,
url: latestAppsRun.html_url, url: latestAppsRun.html_url,
runId: latestAppsRun.id, runId: latestAppsRun.id,
created_at: latestAppsRun.created_at created_at: latestAppsRun.created_at,
started_at: latestAppsRun.run_started_at,
completed_at: latestAppsRun.updated_at
}; };
for (const platform of Object.keys(jobMappings)) {
buildStatuses[platform] = fallbackStatus;
}
} }
// Collect artifacts if any job has completed successfully // Collect artifacts if any job has completed successfully
@@ -353,10 +367,12 @@ jobs:
// Process each expected build target individually // Process each expected build target individually
const buildTargets = [ const buildTargets = [
{ name: 'Android Phone', platform: '🤖', device: '📱', statusKey: 'Android Phone', artifactPattern: /android.*phone/i }, { name: 'Android Phone', platform: '🤖', device: '📱 Phone', statusKey: 'Android Phone', artifactPattern: /android.*phone/i },
{ name: 'Android TV', platform: '🤖', device: '📺', statusKey: 'Android TV', artifactPattern: /android.*tv/i }, { name: 'Android TV', platform: '🤖', device: '📺 TV', statusKey: 'Android TV', artifactPattern: /android.*tv/i },
{ name: 'iOS Phone', platform: '🍎', device: '📱', statusKey: 'iOS Phone', artifactPattern: /ios.*phone/i }, { name: 'iOS', platform: '🍎', device: '📱 Phone', statusKey: 'iOS', artifactPattern: /ios.*phone.*ipa(?!.*unsigned)/i },
{ name: 'iOS TV', platform: '🍎', device: '📺', statusKey: 'iOS TV', artifactPattern: /ios.*tv/i } { name: 'iOS Unsigned', platform: '🍎', device: '📱 Phone Unsigned', statusKey: 'iOS Unsigned', artifactPattern: /ios.*phone.*unsigned/i },
{ name: 'tvOS', platform: '🍎', device: '📺 TV', statusKey: 'tvOS', artifactPattern: /ios.*tv.*ipa(?!.*unsigned)/i },
{ name: 'tvOS Unsigned', platform: '🍎', device: '📺 TV Unsigned', statusKey: 'tvOS Unsigned', artifactPattern: /ios.*tv.*unsigned/i }
]; ];
for (const target of buildTargets) { for (const target of buildTargets) {
@@ -371,16 +387,31 @@ jobs:
let status = '⏳ Pending'; let status = '⏳ Pending';
let downloadLink = '*Waiting for build...*'; let downloadLink = '*Waiting for build...*';
// Special case for iOS TV - show as disabled // tvOS builds are temporarily disabled until feat/tv-interface
if (target.name === 'iOS TV') { // is merged - show them as disabled instead of stuck pending.
if (target.name === 'tvOS' || target.name === 'tvOS Unsigned') {
status = '💤 Disabled'; status = '💤 Disabled';
downloadLink = '*Disabled for now*'; downloadLink = '*Disabled until feat/tv-interface is merged*';
} else if (matchingStatus) { } else if (matchingStatus) {
if (matchingStatus.conclusion === 'success' && matchingArtifact) { if (matchingStatus.conclusion === 'success' && matchingArtifact) {
status = '✅ Complete'; status = '✅ Complete';
const directLink = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${matchingArtifact.workflow_run.id}/artifacts/${matchingArtifact.id}`; const directLink = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${matchingArtifact.workflow_run.id}/artifacts/${matchingArtifact.id}`;
const fileType = target.name.includes('Android') ? 'APK' : 'IPA'; const fileType = target.name.includes('Android') ? 'APK' : 'IPA';
downloadLink = `[📥 Download ${fileType}](${directLink})`;
// Format file size
const sizeInMB = (matchingArtifact.size_in_bytes / (1024 * 1024)).toFixed(1);
const sizeInfo = `(${sizeInMB} MB)`;
// Calculate build duration
let durationInfo = '';
if (matchingStatus.started_at && matchingStatus.completed_at) {
const durationMs = new Date(matchingStatus.completed_at) - new Date(matchingStatus.started_at);
const durationMin = Math.floor(durationMs / 60000);
const durationSec = Math.floor((durationMs % 60000) / 1000);
durationInfo = ` - ${durationMin}m ${durationSec}s`;
}
downloadLink = `[📥 Download ${fileType}](${directLink}) ${sizeInfo}${durationInfo}`;
} else if (matchingStatus.conclusion === 'failure') { } else if (matchingStatus.conclusion === 'failure') {
status = `❌ [Failed](${matchingStatus.url})`; status = `❌ [Failed](${matchingStatus.url})`;
downloadLink = '*Build failed*'; downloadLink = '*Build failed*';
@@ -408,7 +439,7 @@ jobs:
} }
} }
commentBody += `| ${target.platform} ${target.name.split(' ')[0]} | ${target.device} ${target.name.split(' ')[1]} | ${status} | ${downloadLink} |\n`; commentBody += `| ${target.platform} ${target.name} | ${target.device} | ${status} | ${downloadLink} |\n`;
} }
commentBody += `\n`; commentBody += `\n`;

View File

@@ -41,12 +41,12 @@ jobs:
show-progress: false show-progress: false
- name: 🍞 Setup Bun - name: 🍞 Setup Bun
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2 uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with: with:
bun-version: latest bun-version: latest
- name: 💾 Cache Bun dependencies - name: 💾 Cache Bun dependencies
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2 uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with: with:
path: ~/.bun/install/cache path: ~/.bun/install/cache
key: ${{ runner.os }}-${{ runner.arch }}-bun-develop-${{ hashFiles('bun.lock') }} key: ${{ runner.os }}-${{ runner.arch }}-bun-develop-${{ hashFiles('bun.lock') }}
@@ -60,7 +60,7 @@ jobs:
bun run submodule-reload bun run submodule-reload
- name: 💾 Cache Gradle global - name: 💾 Cache Gradle global
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2 uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with: with:
path: | path: |
~/.gradle/caches ~/.gradle/caches
@@ -73,7 +73,7 @@ jobs:
run: bun run prebuild run: bun run prebuild
- name: 💾 Cache project Gradle (.gradle) - name: 💾 Cache project Gradle (.gradle)
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2 uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with: with:
path: android/.gradle path: android/.gradle
key: ${{ runner.os }}-android-gradle-develop-${{ hashFiles('android/**/build.gradle', 'android/gradle/wrapper/gradle-wrapper.properties') }} key: ${{ runner.os }}-android-gradle-develop-${{ hashFiles('android/**/build.gradle', 'android/gradle/wrapper/gradle-wrapper.properties') }}
@@ -88,7 +88,7 @@ jobs:
run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV
- name: 📤 Upload APK artifact - name: 📤 Upload APK artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with: with:
name: streamyfin-android-phone-apk-${{ env.DATE_TAG }} name: streamyfin-android-phone-apk-${{ env.DATE_TAG }}
path: | path: |
@@ -124,12 +124,12 @@ jobs:
show-progress: false show-progress: false
- name: 🍞 Setup Bun - name: 🍞 Setup Bun
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2 uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with: with:
bun-version: latest bun-version: latest
- name: 💾 Cache Bun dependencies - name: 💾 Cache Bun dependencies
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2 uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with: with:
path: ~/.bun/install/cache path: ~/.bun/install/cache
key: ${{ runner.os }}-${{ runner.arch }}-bun-develop-${{ hashFiles('bun.lock') }} key: ${{ runner.os }}-${{ runner.arch }}-bun-develop-${{ hashFiles('bun.lock') }}
@@ -143,7 +143,7 @@ jobs:
bun run submodule-reload bun run submodule-reload
- name: 💾 Cache Gradle global - name: 💾 Cache Gradle global
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2 uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with: with:
path: | path: |
~/.gradle/caches ~/.gradle/caches
@@ -156,7 +156,7 @@ jobs:
run: bun run prebuild:tv run: bun run prebuild:tv
- name: 💾 Cache project Gradle (.gradle) - name: 💾 Cache project Gradle (.gradle)
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2 uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with: with:
path: android/.gradle path: android/.gradle
key: ${{ runner.os }}-android-gradle-develop-${{ hashFiles('android/**/build.gradle', 'android/gradle/wrapper/gradle-wrapper.properties') }} key: ${{ runner.os }}-android-gradle-develop-${{ hashFiles('android/**/build.gradle', 'android/gradle/wrapper/gradle-wrapper.properties') }}
@@ -171,7 +171,7 @@ jobs:
run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV
- name: 📤 Upload APK artifact - name: 📤 Upload APK artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with: with:
name: streamyfin-android-tv-apk-${{ env.DATE_TAG }} name: streamyfin-android-tv-apk-${{ env.DATE_TAG }}
path: | path: |
@@ -195,12 +195,12 @@ jobs:
show-progress: false show-progress: false
- name: 🍞 Setup Bun - name: 🍞 Setup Bun
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2 uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with: with:
bun-version: latest bun-version: latest
- name: 💾 Cache Bun dependencies - name: 💾 Cache Bun dependencies
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2 uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with: with:
path: ~/.bun/install/cache path: ~/.bun/install/cache
key: ${{ runner.os }}-bun-cache-${{ hashFiles('bun.lock') }} key: ${{ runner.os }}-bun-cache-${{ hashFiles('bun.lock') }}
@@ -216,12 +216,12 @@ jobs:
run: bun run prebuild run: bun run prebuild
- name: 🔧 Setup Xcode - name: 🔧 Setup Xcode
uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1 uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1
with: with:
xcode-version: "26.2" xcode-version: "26.2"
- name: 🏗️ Setup EAS - name: 🏗️ Setup EAS
uses: expo/expo-github-action@main uses: expo/expo-github-action@b184ff86a3c926240f1b6db41764c83a01c02eef # main
with: with:
eas-version: latest eas-version: latest
token: ${{ secrets.EXPO_TOKEN }} token: ${{ secrets.EXPO_TOKEN }}
@@ -236,7 +236,7 @@ jobs:
run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV
- name: 📤 Upload IPA artifact - name: 📤 Upload IPA artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with: with:
name: streamyfin-ios-phone-ipa-${{ env.DATE_TAG }} name: streamyfin-ios-phone-ipa-${{ env.DATE_TAG }}
path: build-*.ipa path: build-*.ipa
@@ -259,12 +259,12 @@ jobs:
show-progress: false show-progress: false
- name: 🍞 Setup Bun - name: 🍞 Setup Bun
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2 uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with: with:
bun-version: latest bun-version: latest
- name: 💾 Cache Bun dependencies - name: 💾 Cache Bun dependencies
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2 uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with: with:
path: ~/.bun/install/cache path: ~/.bun/install/cache
key: ${{ runner.os }}-bun-cache-${{ hashFiles('bun.lock') }} key: ${{ runner.os }}-bun-cache-${{ hashFiles('bun.lock') }}
@@ -280,7 +280,7 @@ jobs:
run: bun run prebuild run: bun run prebuild
- name: 🔧 Setup Xcode - name: 🔧 Setup Xcode
uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1 uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1
with: with:
xcode-version: "26.2" xcode-version: "26.2"
@@ -293,73 +293,133 @@ jobs:
run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV
- name: 📤 Upload IPA artifact - name: 📤 Upload IPA artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with: with:
name: streamyfin-ios-phone-unsigned-ipa-${{ env.DATE_TAG }} name: streamyfin-ios-phone-unsigned-ipa-${{ env.DATE_TAG }}
path: build/*.ipa path: build/*.ipa
retention-days: 7 retention-days: 7
# Disabled for now - uncomment when ready to build iOS TV build-ios-tv:
# build-ios-tv: # Temporarily disabled until feat/tv-interface is merged (TV UI not ready).
# if: (!contains(github.event.head_commit.message, '[skip ci]') && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'streamyfin/streamyfin')) # Re-enable by removing the `false &&` prefix below.
# runs-on: macos-26 if: false && (!contains(github.event.head_commit.message, '[skip ci]') && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'streamyfin/streamyfin'))
# name: 🍎 Build iOS IPA (TV) runs-on: macos-26
# permissions: name: 🍎 Build tvOS IPA
# contents: read permissions:
# contents: read
# steps:
# - name: 📥 Checkout code steps:
# uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: 📥 Checkout code
# with: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
# ref: ${{ github.event.pull_request.head.sha || github.sha }} with:
# fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha || github.sha }}
# submodules: recursive fetch-depth: 0
# show-progress: false submodules: recursive
# show-progress: false
# - name: 🍞 Setup Bun
# uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2.0.2 - name: 🍞 Setup Bun
# with: uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
# bun-version: latest with:
# bun-version: latest
# - name: 💾 Cache Bun dependencies
# uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 - name: 💾 Cache Bun dependencies
# with: uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
# path: ~/.bun/install/cache with:
# key: ${{ runner.os }}-bun-cache-${{ hashFiles('bun.lock') }} path: ~/.bun/install/cache
# restore-keys: | key: ${{ runner.os }}-bun-cache-${{ hashFiles('bun.lock') }}
# ${{ runner.os }}-bun-cache restore-keys: |
# ${{ runner.os }}-bun-cache
# - name: 📦 Install dependencies and reload submodules
# run: | - name: 📦 Install dependencies and reload submodules
# bun install --frozen-lockfile run: |
# bun run submodule-reload bun install --frozen-lockfile
# bun run submodule-reload
# - name: 🛠️ Generate project files
# run: bun run prebuild:tv - name: 🛠️ Generate project files
# run: bun run prebuild:tv
# - name: 🔧 Setup Xcode
# uses: maxim-lobanov/setup-xcode@v1 - name: 🔧 Setup Xcode
# with: uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1
# xcode-version: '26.0.1' with:
# xcode-version: "26.2"
# - name: 🏗️ Setup EAS
# uses: expo/expo-github-action@main - name: 🏗️ Setup EAS
# with: uses: expo/expo-github-action@b184ff86a3c926240f1b6db41764c83a01c02eef # main
# eas-version: latest with:
# token: ${{ secrets.EXPO_TOKEN }} eas-version: latest
# eas-cache: true token: ${{ secrets.EXPO_TOKEN }}
# eas-cache: true
# - name: 🚀 Build iOS app
# env: - name: 🚀 Build iOS app
# EXPO_TV: 1 env:
# run: eas build -p ios --local --non-interactive EXPO_TV: 1
# run: eas build -p ios --local --non-interactive
# - name: 📅 Set date tag
# run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV - name: 📅 Set date tag
# run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV
# - name: 📤 Upload IPA artifact
# uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - name: 📤 Upload IPA artifact
# with: uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
# name: streamyfin-ios-tv-ipa-${{ env.DATE_TAG }} with:
# path: build-*.ipa name: streamyfin-ios-tv-ipa-${{ env.DATE_TAG }}
# retention-days: 7 path: build-*.ipa
retention-days: 7
build-ios-tv-unsigned:
# Temporarily disabled until feat/tv-interface is merged (TV UI not ready).
# Re-enable by removing the `false &&` prefix below.
if: false && (!contains(github.event.head_commit.message, '[skip ci]'))
runs-on: macos-26
name: 🍎 Build tvOS IPA (Unsigned)
permissions:
contents: read
steps:
- name: 📥 Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
fetch-depth: 0
submodules: recursive
show-progress: false
- name: 🍞 Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: latest
- name: 💾 Cache Bun dependencies
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-bun-cache-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-cache
- name: 📦 Install dependencies and reload submodules
run: |
bun install --frozen-lockfile
bun run submodule-reload
- name: 🛠️ Generate project files
run: bun run prebuild:tv
- name: 🔧 Setup Xcode
uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1
with:
xcode-version: "26.2"
- name: 🚀 Build iOS app
env:
EXPO_TV: 1
run: bun run ios:unsigned-build:tv ${{ github.event_name == 'pull_request' && '-- --verbose' || '' }}
- name: 📅 Set date tag
run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV
- name: 📤 Upload IPA artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: streamyfin-ios-tv-unsigned-ipa-${{ env.DATE_TAG }}
path: build/*.ipa
retention-days: 7

View File

@@ -27,12 +27,12 @@ jobs:
fetch-depth: 0 fetch-depth: 0
- name: 🍞 Setup Bun - name: 🍞 Setup Bun
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2 uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with: with:
bun-version: latest bun-version: latest
- name: 💾 Cache Bun dependencies - name: 💾 Cache Bun dependencies
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2 uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with: with:
path: | path: |
~/.bun/install/cache ~/.bun/install/cache

View File

@@ -27,13 +27,13 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: 🏁 Initialize CodeQL - name: 🏁 Initialize CodeQL
uses: github/codeql-action/init@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0 uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
queries: +security-extended,security-and-quality queries: +security-extended,security-and-quality
- name: 🛠️ Autobuild - name: 🛠️ Autobuild
uses: github/codeql-action/autobuild@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0 uses: github/codeql-action/autobuild@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0
- name: 🧪 Perform CodeQL Analysis - name: 🧪 Perform CodeQL Analysis
uses: github/codeql-action/analyze@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0 uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0

View File

@@ -28,7 +28,7 @@ jobs:
fetch-depth: 0 fetch-depth: 0
- name: 🌐 Sync Translations with Crowdin - name: 🌐 Sync Translations with Crowdin
uses: crowdin/github-action@b4b468cffefb50bdd99dd83e5d2eaeb63c880380 # v2.14.0 uses: crowdin/github-action@8868a33591d21088edfc398968173a3b98d51706 # v2.16.2
with: with:
upload_sources: true upload_sources: true
upload_translations: true upload_translations: true

View File

@@ -25,7 +25,7 @@ jobs:
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2.9.4 - uses: marocchino/sticky-pull-request-comment@0ea0beb66eb9baf113663a64ec522f60e49231c0 # v3.0.4
if: always() && (steps.lint_pr_title.outputs.error_message != null) if: always() && (steps.lint_pr_title.outputs.error_message != null)
with: with:
header: pr-title-lint-error header: pr-title-lint-error
@@ -39,7 +39,7 @@ jobs:
``` ```
- if: ${{ steps.lint_pr_title.outputs.error_message == null }} - if: ${{ steps.lint_pr_title.outputs.error_message == null }}
uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2.9.4 uses: marocchino/sticky-pull-request-comment@0ea0beb66eb9baf113663a64ec522f60e49231c0 # v3.0.4
with: with:
header: pr-title-lint-error header: pr-title-lint-error
delete: true delete: true
@@ -57,7 +57,7 @@ jobs:
fetch-depth: 0 fetch-depth: 0
- name: Dependency Review - name: Dependency Review
uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2 uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
with: with:
fail-on-severity: high fail-on-severity: high
base-ref: ${{ github.event.pull_request.base.sha || 'develop' }} base-ref: ${{ github.event.pull_request.base.sha || 'develop' }}
@@ -76,7 +76,7 @@ jobs:
fetch-depth: 0 fetch-depth: 0
- name: 🍞 Setup Bun - name: 🍞 Setup Bun
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2 uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with: with:
bun-version: latest bun-version: latest
@@ -107,12 +107,12 @@ jobs:
fetch-depth: 0 fetch-depth: 0
- name: "🟢 Setup Node.js" - name: "🟢 Setup Node.js"
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with: with:
node-version: '24.x' node-version: '24.x'
- name: "🍞 Setup Bun" - name: "🍞 Setup Bun"
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2 uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with: with:
bun-version: latest bun-version: latest

View File

@@ -21,14 +21,14 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: "🟢 Setup Node.js" - name: "🟢 Setup Node.js"
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with: with:
node-version: '24.x' node-version: '24.x'
cache: 'npm' cache: 'npm'
- name: 🔍 Extract minor version from app.json - name: 🔍 Extract minor version from app.json
id: minor id: minor
uses: actions/github-script@main uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # main
with: with:
result-encoding: string result-encoding: string
script: | script: |
@@ -54,7 +54,7 @@ jobs:
dry_run: no-push dry_run: no-push
- name: 📬 Commit and create pull request - name: 📬 Commit and create pull request
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with: with:
add-paths: .github/ISSUE_TEMPLATE/bug_report.yml add-paths: .github/ISSUE_TEMPLATE/bug_report.yml
branch: ci-update-bug-report branch: ci-update-bug-report

View File

@@ -61,7 +61,10 @@ export default function Page() {
setLoading(true); setLoading(true);
try { try {
logsFile.write(JSON.stringify(filteredLogs)); logsFile.write(JSON.stringify(filteredLogs));
await Sharing.shareAsync(logsFile.uri, { mimeType: "txt", UTI: "txt" }); await Sharing.shareAsync(logsFile.uri, {
mimeType: "text/plain",
UTI: "public.plain-text",
});
} catch (e: any) { } catch (e: any) {
writeErrorLog("Something went wrong attempting to export", e); writeErrorLog("Something went wrong attempting to export", e);
} finally { } finally {

View File

@@ -30,8 +30,10 @@ const Page: React.FC = () => {
ItemFields.MediaStreams, ItemFields.MediaStreams,
]); ]);
// Lazily preload item with full media sources in background // Lazily preload item with full media sources in background — never cache
const { data: itemWithSources } = useItemQuery(id, isOffline, undefined, []); const { data: itemWithSources } = useItemQuery(id, isOffline, undefined, [], {
gcTime: 0,
});
const opacity = useSharedValue(1); const opacity = useSharedValue(1);
const animatedStyle = useAnimatedStyle(() => { const animatedStyle = useAnimatedStyle(() => {

View File

@@ -375,8 +375,9 @@ function Layout() {
maxAge: 1000 * 60 * 60 * 24, // 24 hours max cache age maxAge: 1000 * 60 * 60 * 24, // 24 hours max cache age
dehydrateOptions: { dehydrateOptions: {
shouldDehydrateQuery: (query) => { shouldDehydrateQuery: (query) => {
// Only persist successful queries return (
return query.state.status === "success"; query.state.status === "success" && query.options.gcTime !== 0
);
}, },
}, },
}} }}

1426
bun.lock

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,7 @@ import type {
BaseItemDto, BaseItemDto,
MediaSourceInfo, MediaSourceInfo,
} from "@jellyfin/sdk/lib/generated-client/models"; } from "@jellyfin/sdk/lib/generated-client/models";
import { getUserLibraryApi } from "@jellyfin/sdk/lib/utils/api";
import { type Href } from "expo-router"; import { type Href } from "expo-router";
import { t } from "i18next"; import { t } from "i18next";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
@@ -195,9 +196,30 @@ export const DownloadItems: React.FC<DownloadProps> = ({
); );
} }
const downloadDetailsPromises = items.map(async (item) => { const downloadDetailsPromises = items.map(async (item) => {
// Ensure the snapshot we store offline carries the Chapters array.
// Page-level fetches sometimes use a fields filter that omits it; the
// offline player would then render no chapter ticks / list.
let itemForDownload = item;
if (!itemForDownload.Chapters && itemForDownload.Id) {
try {
const enriched = await getUserLibraryApi(api).getItem({
itemId: itemForDownload.Id,
userId: user.Id!,
});
if (enriched.data) {
itemForDownload = enriched.data;
}
} catch (e) {
console.warn(
"[DownloadItem] failed to refresh item for Chapters, falling back to original",
e,
);
}
}
const { mediaSource, audioIndex, subtitleIndex } = const { mediaSource, audioIndex, subtitleIndex } =
itemsNotDownloaded.length > 1 itemsNotDownloaded.length > 1
? getDefaultPlaySettings(item, settings!) ? getDefaultPlaySettings(itemForDownload, settings!)
: { : {
mediaSource: selectedOptions?.mediaSource, mediaSource: selectedOptions?.mediaSource,
audioIndex: selectedOptions?.audioIndex, audioIndex: selectedOptions?.audioIndex,
@@ -206,7 +228,7 @@ export const DownloadItems: React.FC<DownloadProps> = ({
const downloadDetails = await getDownloadUrl({ const downloadDetails = await getDownloadUrl({
api, api,
item, item: itemForDownload,
userId: user.Id!, userId: user.Id!,
mediaSource: mediaSource!, mediaSource: mediaSource!,
audioStreamIndex: audioIndex ?? -1, audioStreamIndex: audioIndex ?? -1,
@@ -218,7 +240,7 @@ export const DownloadItems: React.FC<DownloadProps> = ({
return { return {
url: downloadDetails?.url, url: downloadDetails?.url,
item, item: itemForDownload,
mediaSource: downloadDetails?.mediaSource, mediaSource: downloadDetails?.mediaSource,
}; };
}); });

View File

@@ -0,0 +1,196 @@
/**
* A modal listing an item's chapters. Each row shows the chapter name and its
* timestamp; the current chapter is highlighted. Tapping a row seeks to that
* chapter and closes the modal. Player-agnostic — the seek is injected.
*/
import { Ionicons } from "@expo/vector-icons";
import type { ChapterInfo } from "@jellyfin/sdk/lib/generated-client/models";
import { memo, useEffect, useMemo, useRef } from "react";
import { useTranslation } from "react-i18next";
import { FlatList, Modal, Pressable, StyleSheet, View } from "react-native";
import { Text } from "@/components/common/Text";
import { Colors } from "@/constants/Colors";
import {
type ChapterEntry,
chapterStartsMs,
formatChapterTime,
sortedChapters,
} from "@/utils/chapters";
interface ChapterListProps {
visible: boolean;
chapters: ChapterInfo[] | null | undefined;
/** Current playback position in milliseconds (to highlight the row). */
currentPositionMs: number;
/** Seek the player to this millisecond position. */
onSeek: (positionMs: number) => void;
onClose: () => void;
}
const ROW_HEIGHT = 48;
function ChapterListComponent({
visible,
chapters,
currentPositionMs,
onSeek,
onClose,
}: ChapterListProps) {
const { t } = useTranslation();
const listRef = useRef<FlatList<ChapterEntry>>(null);
const entries = useMemo(() => sortedChapters(chapters), [chapters]);
// Memoize starts so currentChapterIndex computation doesn't re-sort/filter
// every tick — chapters is the only input that drives the underlying array.
const starts = useMemo(() => chapterStartsMs(chapters), [chapters]);
const activeIndex = useMemo(() => {
let idx = -1;
for (let i = 0; i < starts.length; i++) {
if (currentPositionMs >= starts[i]) idx = i;
else break;
}
return idx;
}, [currentPositionMs, starts]);
// FlatList.initialScrollIndex only fires at first mount; <Modal> keeps its
// children mounted across visible toggles, so subsequent opens never scroll.
// Trigger an imperative scroll each time the sheet becomes visible.
useEffect(() => {
if (!visible || activeIndex < 0 || entries.length === 0) return;
const raf = requestAnimationFrame(() => {
listRef.current?.scrollToIndex({
index: activeIndex,
animated: false,
viewPosition: 0.5,
});
});
return () => cancelAnimationFrame(raf);
}, [visible, activeIndex, entries.length]);
return (
<Modal
visible={visible}
transparent
animationType='slide'
onRequestClose={onClose}
>
<Pressable onPress={onClose} style={styles.backdrop}>
<Pressable onPress={(e) => e.stopPropagation()} style={styles.sheet}>
<View style={styles.header}>
<Text style={styles.title}>{t("chapters.title")}</Text>
<Pressable
onPress={onClose}
hitSlop={10}
accessibilityRole='button'
accessibilityLabel={t("chapters.close")}
>
<Ionicons name='close' size={24} color={Colors.text} />
</Pressable>
</View>
<FlatList
ref={listRef}
data={entries}
keyExtractor={(item, index) => `${item.positionMs}-${index}`}
getItemLayout={(_, index) => ({
length: ROW_HEIGHT,
offset: ROW_HEIGHT * index,
index,
})}
onScrollToIndexFailed={(info) => {
// Required when getItemLayout is provided and the target index
// is outside the currently rendered window. Fallback to an
// offset-based scroll, then retry the precise scroll once a
// frame has elapsed.
listRef.current?.scrollToOffset({
offset: info.averageItemLength * info.index,
animated: false,
});
setTimeout(() => {
listRef.current?.scrollToIndex({
index: info.index,
animated: false,
viewPosition: 0.5,
});
}, 50);
}}
renderItem={({ item, index }) => {
const positionMs = item.positionMs;
const isActive = index === activeIndex;
return (
<Pressable
onPress={() => {
onSeek(positionMs);
onClose();
}}
style={[
styles.row,
isActive && { backgroundColor: `${Colors.primary}33` },
]}
>
<Text
style={[
styles.rowText,
{ color: isActive ? Colors.primary : Colors.text },
]}
numberOfLines={1}
>
{item.chapter.Name ||
t("chapters.chapter_number", { number: index + 1 })}
</Text>
<Text style={styles.rowTime}>
{formatChapterTime(positionMs)}
</Text>
</Pressable>
);
}}
/>
</Pressable>
</Pressable>
</Modal>
);
}
export const ChapterList = memo(ChapterListComponent);
const styles = StyleSheet.create({
backdrop: {
flex: 1,
justifyContent: "flex-end",
backgroundColor: "rgba(0,0,0,0.6)",
},
sheet: {
backgroundColor: Colors.background,
borderTopLeftRadius: 16,
borderTopRightRadius: 16,
maxHeight: "70%",
paddingBottom: 24,
},
header: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
padding: 16,
},
title: {
color: Colors.text,
fontSize: 17,
fontWeight: "700",
},
row: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingHorizontal: 16,
height: ROW_HEIGHT,
},
rowText: {
fontSize: 15,
flex: 1,
},
rowTime: {
color: Colors.icon,
fontSize: 13,
marginLeft: 12,
},
});

View File

@@ -0,0 +1,87 @@
/**
* Chapter tick marks drawn as an absolute overlay over a progress slider.
* Renders nothing for media with one or zero chapters. `pointerEvents: "none"`
* so the slider underneath still receives touches.
*/
import { memo, useState } from "react";
import { type LayoutChangeEvent, PixelRatio, View } from "react-native";
import type { ChapterMarker } from "@/utils/chapters";
interface ChapterTicksProps {
/** Pre-computed markers (caller memoizes — avoids double-computing here). */
markers: ChapterMarker[];
/** Tick colour. */
color?: string;
/** Tick height in px — slightly less than the slider track thickness. */
height?: number;
/** Tick width in px — integer to avoid sub-pixel anti-aliasing. */
width?: number;
}
function ChapterTicksComponent({
markers,
// Semi-transparent black contrasts against both the filled progress
// (#fff) and the unfilled track (rgba(255,255,255,0.2)) so the ticks
// stay visible across the whole bar as playback advances.
color = "rgba(0,0,0,0.55)",
height = 14,
width = 2,
}: ChapterTicksProps) {
// Hooks must run unconditionally — keep them before any early return.
const [sliderWidth, setSliderWidth] = useState(0);
const handleLayout = (e: LayoutChangeEvent) => {
setSliderWidth(e.nativeEvent.layout.width);
};
// One chapter (typically a single marker at 0) is not worth marking.
if (markers.length <= 1) return null;
return (
<View
pointerEvents='none'
onLayout={handleLayout}
style={{
position: "absolute",
left: 0,
right: 0,
top: 0,
bottom: 0,
// Let ticks taller than this container bleed beyond its bounds.
overflow: "visible",
}}
>
{sliderWidth > 0 &&
markers
// Skip the leading 0ms marker — it overlaps the slider start and
// adds visual noise at an already-rendered boundary.
.filter((marker) => marker.positionMs > 0)
.map((marker, index) => {
// Align both the position AND the width onto the device's
// physical pixel grid. Without this, fractional dp values land
// at different sub-pixel fractions per tick — Android samples
// each one differently and some ticks render visibly thicker.
const centerDp = (marker.percent / 100) * sliderWidth;
const left = PixelRatio.roundToNearestPixel(centerDp - width / 2);
const snappedWidth = PixelRatio.roundToNearestPixel(width);
return (
<View
key={`${marker.positionMs}-${index}`}
style={{
position: "absolute",
left,
top: "50%",
marginTop: -height / 2,
height,
width: snappedWidth,
backgroundColor: color,
}}
/>
);
})}
</View>
);
}
export const ChapterTicks = memo(ChapterTicksComponent);

View File

@@ -1,18 +1,34 @@
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client"; import { Ionicons } from "@expo/vector-icons";
import type { FC } from "react"; import type {
import { View } from "react-native"; BaseItemDto,
ChapterInfo,
} from "@jellyfin/sdk/lib/generated-client";
import { type FC, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { Pressable, View } from "react-native";
import { Slider } from "react-native-awesome-slider"; import { Slider } from "react-native-awesome-slider";
import { type SharedValue } from "react-native-reanimated"; import { type SharedValue } from "react-native-reanimated";
import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useSafeAreaInsets } from "react-native-safe-area-context";
import { ChapterList } from "@/components/chapters/ChapterList";
import { ChapterTicks } from "@/components/chapters/ChapterTicks";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useSettings } from "@/utils/atoms/settings"; import { useSettings } from "@/utils/atoms/settings";
import { chapterMarkers, chapterNameAt } from "@/utils/chapters";
import NextEpisodeCountDownButton from "./NextEpisodeCountDownButton"; import NextEpisodeCountDownButton from "./NextEpisodeCountDownButton";
import SkipButton from "./SkipButton"; import SkipButton from "./SkipButton";
import { TimeDisplay } from "./TimeDisplay"; import { TimeDisplay } from "./TimeDisplay";
import { TrickplayBubble } from "./TrickplayBubble"; import { TrickplayBubble } from "./TrickplayBubble";
// Chapter tick height in dp — matches the slider track height for a clean,
// flush look (no top/bottom overflow).
const TICK_HEIGHT = 10;
interface BottomControlsProps { interface BottomControlsProps {
item: BaseItemDto; item: BaseItemDto;
/** Item chapters, used for the tick overlay and chapter list. */
chapters?: ChapterInfo[] | null;
/** Total media duration in milliseconds. */
durationMs: number;
showControls: boolean; showControls: boolean;
isSliding: boolean; isSliding: boolean;
showRemoteBubble: boolean; showRemoteBubble: boolean;
@@ -38,6 +54,8 @@ interface BottomControlsProps {
handleSliderChange: (value: number) => void; handleSliderChange: (value: number) => void;
handleTouchStart: () => void; handleTouchStart: () => void;
handleTouchEnd: () => void; handleTouchEnd: () => void;
/** Programmatic seek (chapter list, hotkeys) — bypasses slide gesture state. */
seekTo: (value: number) => void;
// Trickplay props // Trickplay props
trickPlayUrl: { trickPlayUrl: {
@@ -61,6 +79,8 @@ interface BottomControlsProps {
export const BottomControls: FC<BottomControlsProps> = ({ export const BottomControls: FC<BottomControlsProps> = ({
item, item,
chapters,
durationMs,
showControls, showControls,
isSliding, isSliding,
showRemoteBubble, showRemoteBubble,
@@ -84,12 +104,38 @@ export const BottomControls: FC<BottomControlsProps> = ({
handleSliderChange, handleSliderChange,
handleTouchStart, handleTouchStart,
handleTouchEnd, handleTouchEnd,
seekTo,
trickPlayUrl, trickPlayUrl,
trickplayInfo, trickplayInfo,
time, time,
}) => { }) => {
const { settings } = useSettings(); const { settings } = useSettings();
const { t } = useTranslation();
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const [chapterListVisible, setChapterListVisible] = useState(false);
// Only expose chapter UI when there are at least two real markers.
const chapterMarkerList = useMemo(
() => chapterMarkers(chapters, durationMs),
[chapters, durationMs],
);
const hasChapters = chapterMarkerList.length > 1;
// Current chapter name for the always-visible header label (live playback).
const currentChapterName = useMemo(
() => (hasChapters ? chapterNameAt(currentTime, chapters) : null),
[hasChapters, currentTime, chapters],
);
// Chapter name at the scrubbed position for the trickplay bubble. `time` is
// an {h,m,s} object derived from the slider's dragged value — convert back
// to ms for the lookup. Only useful while actively scrubbing.
const scrubChapterName = useMemo(() => {
if (!hasChapters) return null;
const scrubMs =
(time.hours * 3600 + time.minutes * 60 + time.seconds) * 1000;
return chapterNameAt(scrubMs, chapters);
}, [hasChapters, time.hours, time.minutes, time.seconds, chapters]);
return ( return (
<View <View
@@ -131,8 +177,24 @@ export const BottomControls: FC<BottomControlsProps> = ({
{item?.Type === "Audio" && ( {item?.Type === "Audio" && (
<Text className='text-xs opacity-50'>{item?.Album}</Text> <Text className='text-xs opacity-50'>{item?.Album}</Text>
)} )}
{currentChapterName ? (
<Text className='text-xs opacity-70 mt-1' numberOfLines={1}>
{currentChapterName}
</Text>
) : null}
</View> </View>
<View className='flex flex-row space-x-2 shrink-0'> <View className='flex flex-row items-center space-x-2 shrink-0'>
{hasChapters && (
<Pressable
onPress={() => setChapterListVisible(true)}
hitSlop={10}
className='justify-center mr-4'
accessibilityRole='button'
accessibilityLabel={t("chapters.open")}
>
<Ionicons name='bookmarks' size={24} color='white' />
</Pressable>
)}
<SkipButton <SkipButton
showButton={showSkipButton} showButton={showSkipButton}
onPress={skipIntro} onPress={skipIntro}
@@ -176,6 +238,9 @@ export const BottomControls: FC<BottomControlsProps> = ({
height: 10, height: 10,
justifyContent: "center", justifyContent: "center",
alignItems: "stretch", alignItems: "stretch",
// Allow chapter ticks taller than the 10px track to bleed out
// top/bottom (RN defaults to overflow: "hidden" on Android).
overflow: "visible",
}} }}
onTouchStart={handleTouchStart} onTouchStart={handleTouchStart}
onTouchEnd={handleTouchEnd} onTouchEnd={handleTouchEnd}
@@ -203,6 +268,7 @@ export const BottomControls: FC<BottomControlsProps> = ({
trickPlayUrl={trickPlayUrl} trickPlayUrl={trickPlayUrl}
trickplayInfo={trickplayInfo} trickplayInfo={trickplayInfo}
time={time} time={time}
chapterName={scrubChapterName}
/> />
) )
} }
@@ -212,6 +278,7 @@ export const BottomControls: FC<BottomControlsProps> = ({
minimumValue={min} minimumValue={min}
maximumValue={max} maximumValue={max}
/> />
<ChapterTicks markers={chapterMarkerList} height={TICK_HEIGHT} />
</View> </View>
<TimeDisplay <TimeDisplay
currentTime={currentTime} currentTime={currentTime}
@@ -219,6 +286,13 @@ export const BottomControls: FC<BottomControlsProps> = ({
/> />
</View> </View>
</View> </View>
<ChapterList
visible={chapterListVisible}
chapters={chapters}
currentPositionMs={currentTime}
onSeek={seekTo}
onClose={() => setChapterListVisible(false)}
/>
</View> </View>
); );
}; };

View File

@@ -251,6 +251,7 @@ export const Controls: FC<Props> = ({
handleTouchEnd, handleTouchEnd,
handleSliderComplete, handleSliderComplete,
handleSliderChange, handleSliderChange,
seekTo,
} = useVideoSlider({ } = useVideoSlider({
progress, progress,
isSeeking, isSeeking,
@@ -528,6 +529,8 @@ export const Controls: FC<Props> = ({
> >
<BottomControls <BottomControls
item={item} item={item}
chapters={item.Chapters}
durationMs={maxMs}
showControls={showControls} showControls={showControls}
isSliding={isSliding} isSliding={isSliding}
showRemoteBubble={showRemoteBubble} showRemoteBubble={showRemoteBubble}
@@ -551,6 +554,7 @@ export const Controls: FC<Props> = ({
handleSliderChange={handleSliderChange} handleSliderChange={handleSliderChange}
handleTouchStart={handleTouchStart} handleTouchStart={handleTouchStart}
handleTouchEnd={handleTouchEnd} handleTouchEnd={handleTouchEnd}
seekTo={seekTo}
trickPlayUrl={trickPlayUrl} trickPlayUrl={trickPlayUrl}
trickplayInfo={trickplayInfo} trickplayInfo={trickplayInfo}
time={isSliding || showRemoteBubble ? time : remoteTime} time={isSliding || showRemoteBubble ? time : remoteTime}

View File

@@ -22,12 +22,15 @@ interface TrickplayBubbleProps {
minutes: number; minutes: number;
seconds: number; seconds: number;
}; };
/** Chapter name at the scrubbed position, if any. */
chapterName?: string | null;
} }
export const TrickplayBubble: FC<TrickplayBubbleProps> = ({ export const TrickplayBubble: FC<TrickplayBubbleProps> = ({
trickPlayUrl, trickPlayUrl,
trickplayInfo, trickplayInfo,
time, time,
chapterName,
}) => { }) => {
if (!trickPlayUrl || !trickplayInfo) { if (!trickPlayUrl || !trickplayInfo) {
return null; return null;
@@ -36,18 +39,30 @@ export const TrickplayBubble: FC<TrickplayBubbleProps> = ({
const { x, y, url } = trickPlayUrl; const { x, y, url } = trickPlayUrl;
const tileWidth = CONTROLS_CONSTANTS.TILE_WIDTH; const tileWidth = CONTROLS_CONSTANTS.TILE_WIDTH;
const tileHeight = tileWidth / trickplayInfo.aspectRatio!; const tileHeight = tileWidth / trickplayInfo.aspectRatio!;
const timeStr = `${time.hours > 0 ? `${time.hours}:` : ""}${
time.minutes < 10 ? `0${time.minutes}` : time.minutes
}:${time.seconds < 10 ? `0${time.seconds}` : time.seconds}`;
// Slightly larger preview than before (scale 1.6 vs old 1.4) to give the
// overlay text more room and feel closer to the Jellyfin web style.
const previewScale = 1.6;
return ( return (
<View <View
style={{ style={{
position: "absolute", position: "absolute",
left: -62, left: -62,
// Sit just above the slider — high enough not to overlap the
// progress bar, low enough to feel anchored to the thumb.
bottom: 0, bottom: 0,
paddingTop: 30, paddingTop: 12,
paddingBottom: 5, paddingBottom: 5,
width: tileWidth * 1.5, width: tileWidth * 1.5,
justifyContent: "center", justifyContent: "center",
alignItems: "center", alignItems: "center",
// Bring the bubble in front of the player title / overlays.
zIndex: 999,
elevation: 10,
}} }}
> >
<View <View
@@ -55,7 +70,7 @@ export const TrickplayBubble: FC<TrickplayBubbleProps> = ({
width: tileWidth, width: tileWidth,
height: tileHeight, height: tileHeight,
alignSelf: "center", alignSelf: "center",
transform: [{ scale: 1.4 }], transform: [{ scale: previewScale }],
borderRadius: 5, borderRadius: 5,
}} }}
className='bg-neutral-800 overflow-hidden' className='bg-neutral-800 overflow-hidden'
@@ -75,17 +90,51 @@ export const TrickplayBubble: FC<TrickplayBubbleProps> = ({
source={{ uri: url }} source={{ uri: url }}
contentFit='cover' contentFit='cover'
/> />
{/*
* Bottom-right overlay (Jellyfin web style) — chapter name (small,
* faded) above the timestamp (small, bold). Sits on top of the
* trickplay frame inside the same overflow:hidden container so it
* always stays within the bubble bounds.
*/}
<View
pointerEvents='none'
style={{
position: "absolute",
left: 4,
bottom: 3,
alignItems: "flex-start",
paddingHorizontal: 3,
paddingVertical: 1,
borderRadius: 3,
backgroundColor: "rgba(0,0,0,0.55)",
maxWidth: tileWidth - 8,
}}
>
{chapterName ? (
<Text
numberOfLines={1}
style={{
color: "#fff",
fontSize: 7,
opacity: 0.85,
lineHeight: 9,
}}
>
{chapterName}
</Text>
) : null}
<Text
style={{
color: "#fff",
fontSize: 8,
fontWeight: "600",
lineHeight: 10,
}}
>
{timeStr}
</Text>
</View>
</View> </View>
<Text
style={{
marginTop: 30,
fontSize: 16,
}}
>
{`${time.hours > 0 ? `${time.hours}:` : ""}${
time.minutes < 10 ? `0${time.minutes}` : time.minutes
}:${time.seconds < 10 ? `0${time.seconds}` : time.seconds}`}
</Text>
</View> </View>
); );
}; };

View File

@@ -74,6 +74,21 @@ export function useVideoSlider({
[seek, play, progress, isSeeking], [seek, play, progress, isSeeking],
); );
// Programmatic seek (chapter list, hotkeys) that bypasses the slide gesture.
// Reads `isPlaying` directly instead of `wasPlayingRef`, which is only set
// during a real slide and would carry stale state on a tap-to-seek.
const seekTo = useCallback(
(value: number) => {
const seekValue = Math.max(0, Math.floor(value));
progress.value = seekValue;
seek(seekValue);
if (isPlaying) {
play();
}
},
[seek, play, progress, isPlaying],
);
const handleSliderChange = useCallback( const handleSliderChange = useCallback(
debounce((value: number) => { debounce((value: number) => {
// Convert ms to ticks for trickplay // Convert ms to ticks for trickplay
@@ -96,5 +111,6 @@ export function useVideoSlider({
handleTouchEnd, handleTouchEnd,
handleSliderComplete, handleSliderComplete,
handleSliderChange, handleSliderChange,
seekTo,
}; };
} }

View File

@@ -12,11 +12,17 @@ export const excludeFields = (fieldsToExclude: ItemFields[]) => {
); );
}; };
type ExtraQueryOptions = {
gcTime?: number;
staleTime?: number;
};
export const useItemQuery = ( export const useItemQuery = (
itemId: string | undefined, itemId: string | undefined,
isOffline?: boolean, isOffline?: boolean,
fields?: ItemFields[], fields?: ItemFields[],
excludeFields?: ItemFields[], excludeFields?: ItemFields[],
queryOptions?: ExtraQueryOptions,
) => { ) => {
const [api] = useAtom(apiAtom); const [api] = useAtom(apiAtom);
const [user] = useAtom(userAtom); const [user] = useAtom(userAtom);
@@ -53,5 +59,6 @@ export const useItemQuery = (
refetchOnWindowFocus: true, refetchOnWindowFocus: true,
refetchOnReconnect: true, refetchOnReconnect: true,
networkMode: "always", networkMode: "always",
...queryOptions,
}); });
}; };

View File

@@ -171,7 +171,11 @@ final class MPVLayerRenderer {
// Enable composite OSD mode - renders subtitles directly onto video frames using GPU // Enable composite OSD mode - renders subtitles directly onto video frames using GPU
// This is better for PiP as subtitles are baked into the video // This is better for PiP as subtitles are baked into the video
// NOTE: Must be set BEFORE the #if targetEnvironment check or tvOS will freeze on player exit // NOTE: Must be set BEFORE the #if targetEnvironment check or tvOS will freeze on player exit
#if targetEnvironment(simulator)
checkError(mpv_set_option_string(handle, "avfoundation-composite-osd", "no"))
#else
checkError(mpv_set_option_string(handle, "avfoundation-composite-osd", "yes")) checkError(mpv_set_option_string(handle, "avfoundation-composite-osd", "yes"))
#endif
// Hardware decoding with VideoToolbox // Hardware decoding with VideoToolbox
// On simulator, use software decoding since VideoToolbox is not available // On simulator, use software decoding since VideoToolbox is not available

View File

@@ -15,6 +15,7 @@
"android:tv": "cross-env EXPO_TV=1 expo run:android", "android:tv": "cross-env EXPO_TV=1 expo run:android",
"build:android:local": "cd android && cross-env NODE_ENV=production ./gradlew assembleRelease", "build:android:local": "cd android && cross-env NODE_ENV=production ./gradlew assembleRelease",
"ios:unsigned-build": "cross-env EXPO_TV=0 bun scripts/ios/build-ios.ts --production", "ios:unsigned-build": "cross-env EXPO_TV=0 bun scripts/ios/build-ios.ts --production",
"ios:unsigned-build:tv": "cross-env EXPO_TV=1 bun scripts/ios/build-ios.ts --production",
"prepare": "husky", "prepare": "husky",
"typecheck": "node scripts/typecheck.js", "typecheck": "node scripts/typecheck.js",
"check": "biome check . --max-diagnostics 1000", "check": "biome check . --max-diagnostics 1000",
@@ -33,7 +34,7 @@
"@expo/vector-icons": "^15.0.3", "@expo/vector-icons": "^15.0.3",
"@gorhom/bottom-sheet": "5.2.8", "@gorhom/bottom-sheet": "5.2.8",
"@jellyfin/sdk": "^0.13.0", "@jellyfin/sdk": "^0.13.0",
"@react-native-community/netinfo": "^11.4.1", "@react-native-community/netinfo": "^12.0.0",
"@react-navigation/material-top-tabs": "7.4.9", "@react-navigation/material-top-tabs": "7.4.9",
"@react-navigation/native": "^7.0.14", "@react-navigation/native": "^7.0.14",
"@shopify/flash-list": "2.0.2", "@shopify/flash-list": "2.0.2",
@@ -70,14 +71,14 @@
"expo-system-ui": "~6.0.9", "expo-system-ui": "~6.0.9",
"expo-task-manager": "14.0.9", "expo-task-manager": "14.0.9",
"expo-web-browser": "~15.0.10", "expo-web-browser": "~15.0.10",
"i18next": "^25.0.0", "i18next": "^26.0.0",
"jotai": "2.16.2", "jotai": "2.16.2",
"lodash": "4.17.23", "lodash": "4.17.23",
"nativewind": "^2.0.11", "nativewind": "^2.0.11",
"patch-package": "^8.0.0", "patch-package": "^8.0.0",
"react": "19.1.0", "react": "19.1.0",
"react-dom": "19.1.0", "react-dom": "19.1.0",
"react-i18next": "16.5.4", "react-i18next": "17.0.8",
"react-native": "0.81.5", "react-native": "0.81.5",
"react-native-awesome-slider": "^2.9.0", "react-native-awesome-slider": "^2.9.0",
"react-native-bottom-tabs": "1.1.0", "react-native-bottom-tabs": "1.1.0",
@@ -117,16 +118,16 @@
"devDependencies": { "devDependencies": {
"@babel/core": "7.28.6", "@babel/core": "7.28.6",
"@biomejs/biome": "2.3.11", "@biomejs/biome": "2.3.11",
"@react-native-community/cli": "20.1.1", "@react-native-community/cli": "20.1.3",
"@react-native-tvos/config-tv": "0.1.4", "@react-native-tvos/config-tv": "0.1.6",
"@types/jest": "29.5.14", "@types/jest": "29.5.14",
"@types/lodash": "4.17.23", "@types/lodash": "4.17.23",
"@types/react": "19.1.17", "@types/react": "19.1.17",
"@types/react-test-renderer": "19.1.0", "@types/react-test-renderer": "19.1.0",
"cross-env": "10.1.0", "cross-env": "10.1.0",
"expo-doctor": "1.17.14", "expo-doctor": "1.19.7",
"husky": "9.1.7", "husky": "9.1.7",
"lint-staged": "16.2.7", "lint-staged": "17.0.5",
"react-test-renderer": "19.2.3", "react-test-renderer": "19.2.3",
"typescript": "5.9.3" "typescript": "5.9.3"
}, },

View File

@@ -5,10 +5,13 @@ import {
type ReactNode, type ReactNode,
useCallback, useCallback,
useContext, useContext,
useEffect,
useRef, useRef,
useState, useState,
} from "react"; } from "react";
import { BackHandler, Platform } from "react-native";
interface ModalOptions { interface ModalOptions {
enableDynamicSizing?: boolean; enableDynamicSizing?: boolean;
snapPoints?: (string | number)[]; snapPoints?: (string | number)[];
@@ -73,6 +76,25 @@ export const GlobalModalProvider: React.FC<GlobalModalProviderProps> = ({
}); });
}, []); }, []);
useEffect(() => {
if (Platform.OS !== "android") return;
const onBackPress = () => {
if (isVisible) {
hideModal();
return true;
}
return false;
};
const subscription = BackHandler.addEventListener(
"hardwareBackPress",
onBackPress,
);
return () => subscription.remove();
}, [isVisible, hideModal]);
const value = { const value = {
showModal, showModal,
hideModal, hideModal,

View File

@@ -30,48 +30,48 @@
"connect_button": "اتصل", "connect_button": "اتصل",
"previous_servers": "الخوادم السابقة", "previous_servers": "الخوادم السابقة",
"clear_button": "مسح", "clear_button": "مسح",
"swipe_to_remove": "Swipe to remove", "swipe_to_remove": "مرر للإزالة",
"search_for_local_servers": "البحث عن الخوادم المحلية", "search_for_local_servers": "البحث عن الخوادم المحلية",
"searching": "جاري البحث...", "searching": "جاري البحث...",
"servers": "الخوادم", "servers": "الخوادم",
"saved": "Saved", "saved": "تم الحفظ",
"session_expired": "Session Expired", "session_expired": "انتهت الجلسة",
"please_login_again": "Your saved session has expired. Please log in again.", "please_login_again": "انتهت مدة صلاحية جلستك. الرجاء تسجيل الدخول مرة أخرى.",
"remove_saved_login": "Remove Saved Login", "remove_saved_login": "إزالة تسجيل دخول محفوظ",
"remove_saved_login_description": "This will remove your saved credentials for this server. You'll need to enter your username and password again next time.", "remove_saved_login_description": "سيؤدي هذا إلى إزالة بيانات تسجيل الدخول الخاص بك المحفوظة لهذا الخادم. ستحتاج إلى إدخال اسم المستخدم وكلمة المرور مرة أخرى في المرة القادمة.",
"accounts_count": "{{count}} accounts", "accounts_count": "الحسابات {{count}}",
"select_account": "Select Account", "select_account": "اختر الحساب",
"add_account": "Add Account", "add_account": "إضافة حساب",
"remove_account_description": "This will remove the saved credentials for {{username}}." "remove_account_description": "سيؤدي هذا إلى إزالة بيانات تسجيل الدخول لـ {{username}}."
}, },
"save_account": { "save_account": {
"title": "Save Account", "title": "حفظ الحساب",
"save_for_later": "Save this account", "save_for_later": "حفظ هذا الحساب",
"security_option": "Security Option", "security_option": "‮خيارات الأمان",
"no_protection": "No protection", "no_protection": "بدون حماية",
"no_protection_desc": "Quick login without authentication", "no_protection_desc": "تسجيل دخول سريع بدون مصادقة",
"pin_code": "PIN code", "pin_code": "رمز PIN",
"pin_code_desc": "4-digit PIN required when switching", "pin_code_desc": "رمز PIN مكون من 4 أرقام مطلوب عند التبديل",
"password": "Re-enter password", "password": "أعد إدخال كلمة المرور",
"password_desc": "Password required when switching", "password_desc": "كلمة المرور مطلوبة عند التبديل",
"save_button": "Save", "save_button": "حفظ",
"cancel_button": "Cancel" "cancel_button": "إلغاء"
}, },
"pin": { "pin": {
"enter_pin": "Enter PIN", "enter_pin": "‏أدخل رمز PIN",
"enter_pin_for": "Enter PIN for {{username}}", "enter_pin_for": "أدخل رمز PIN لـ {{username}}",
"enter_4_digits": "Enter 4 digits", "enter_4_digits": "ادخل 4 أرقام",
"invalid_pin": "Invalid PIN", "invalid_pin": "PIN غير صالح",
"setup_pin": "Set Up PIN", "setup_pin": "تعيين رمز PIN",
"confirm_pin": "Confirm PIN", "confirm_pin": "تأكيد رمز PIN",
"pins_dont_match": "PINs don't match", "pins_dont_match": "رموز PIN غير متطابقة",
"forgot_pin": "Forgot PIN?", "forgot_pin": "نسيت رمز PIN؟",
"forgot_pin_desc": "Your saved credentials will be removed" "forgot_pin_desc": "سيتم إزالة بيانات تسجيل الدخول المحفوظة الخاصة بك"
}, },
"password": { "password": {
"enter_password": "Enter Password", "enter_password": "أدخل كلمة المرور",
"enter_password_for": "Enter password for {{username}}", "enter_password_for": "أدخل كلمة المرور لـ {{username}}",
"invalid_password": "Invalid password" "invalid_password": "كلمة المرور غير صحيحة"
}, },
"home": { "home": {
"checking_server_connection": "التحقق من اتصال الخادم...", "checking_server_connection": "التحقق من اتصال الخادم...",
@@ -87,7 +87,7 @@
"error_message": "حدث خطأ ما.\nيرجى تسجيل الخروج ثم الدخول مرة أخرى.", "error_message": "حدث خطأ ما.\nيرجى تسجيل الخروج ثم الدخول مرة أخرى.",
"continue_watching": "متابعة المشاهدة", "continue_watching": "متابعة المشاهدة",
"next_up": "التالي", "next_up": "التالي",
"continue_and_next_up": "Continue & Next Up", "continue_and_next_up": "تابع و التالي",
"recently_added_in": "أضيف مؤخراً في {{libraryName}}", "recently_added_in": "أضيف مؤخراً في {{libraryName}}",
"suggested_movies": "أفلام مقترحة", "suggested_movies": "أفلام مقترحة",
"suggested_episodes": "حلقات مقترحة", "suggested_episodes": "حلقات مقترحة",
@@ -120,36 +120,36 @@
}, },
"appearance": { "appearance": {
"title": "المظهر", "title": "المظهر",
"merge_next_up_continue_watching": "Merge Continue Watching & Next Up", "merge_next_up_continue_watching": "دمج تابع المشاهدة والتالي",
"hide_remote_session_button": "Hide Remote Session Button" "hide_remote_session_button": "إخفاء زر جلسة البث عن بُعد"
}, },
"network": { "network": {
"title": "Network", "title": "الشبكة",
"local_network": "Local Network", "local_network": "الشبكة المحلية",
"auto_switch_enabled": "Auto-switch when at home", "auto_switch_enabled": "التبديل التلقائي عند المنزل",
"auto_switch_description": "Automatically switch to local URL when connected to home WiFi", "auto_switch_description": "التبديل تلقائياً إلى رابط URL محلي عند الاتصال بشبكة WiFi المنزلية",
"local_url": "Local URL", "local_url": "رابط محلي",
"local_url_hint": "Enter your local server address (e.g., http://192.168.1.100:8096)", "local_url_hint": "أدخل عنوان الخادم المحلي الخاص بك (على سبيل المثال http://192.168.1.100:8096)",
"local_url_placeholder": "http://192.168.1.100:8096", "local_url_placeholder": "http://192.168.1.100:8096",
"home_wifi_networks": "Home WiFi Networks", "home_wifi_networks": "شبكات WiFi المنزل",
"add_current_network": "Add \"{{ssid}}\"", "add_current_network": "إضافة \"{{ssid}}\"",
"not_connected_to_wifi": "Not connected to WiFi", "not_connected_to_wifi": "غير متصل بشبكة WiFi",
"no_networks_configured": "No networks configured", "no_networks_configured": "لا توجد شبكات مكونة",
"add_network_hint": "Add your home WiFi network to enable auto-switching", "add_network_hint": "إضافة شبكة WiFi المنزلية الخاصة بك لتمكين التبديل التلقائي",
"current_wifi": "Current WiFi", "current_wifi": "شبكة WiFi الحالية",
"using_url": "Using", "using_url": "استخدام",
"local": "Local URL", "local": "رابط محلي",
"remote": "Remote URL", "remote": "الـ URL الخارجي",
"not_connected": "Not connected", "not_connected": "غير متصل",
"current_server": "Current Server", "current_server": "الخادم الحالي",
"remote_url": "Remote URL", "remote_url": "الـ URL الخارجي",
"active_url": "Active URL", "active_url": "الرابط النشط",
"not_configured": "Not configured", "not_configured": "لم يتم تكوينه",
"network_added": "Network added", "network_added": "تمت إضافة الشبكة",
"network_already_added": "Network already added", "network_already_added": "الشبكة مضافة مسبقاً",
"no_wifi_connected": "Not connected to WiFi", "no_wifi_connected": "غير متصل بشبكة WiFi",
"permission_denied": "Location permission denied", "permission_denied": "تم رفض إذن الوصول إلى الموقع",
"permission_denied_explanation": "Location permission is required to detect WiFi network for auto-switching. Please enable it in Settings." "permission_denied_explanation": "يتطلب التعرف على شبكة WiFi للتبديل التلقائي الحصول على إذن الوصول إلى الموقع. يرجى تفعيله من الإعدادات."
}, },
"user_info": { "user_info": {
"user_info_title": "معلومات المستخدم", "user_info_title": "معلومات المستخدم",
@@ -182,10 +182,10 @@
"left_side_brightness_description": "اسحب لأعلى/لأسفل على الجانب الأيسر لضبط السطوع", "left_side_brightness_description": "اسحب لأعلى/لأسفل على الجانب الأيسر لضبط السطوع",
"right_side_volume": "التحكم في مستوى الصوت من الجانب الأيمن", "right_side_volume": "التحكم في مستوى الصوت من الجانب الأيمن",
"right_side_volume_description": "اسحب لأعلى/لأسفل على الجانب الأيمن لضبط مستوى الصوت", "right_side_volume_description": "اسحب لأعلى/لأسفل على الجانب الأيمن لضبط مستوى الصوت",
"hide_volume_slider": "Hide Volume Slider", "hide_volume_slider": "إخفاء شريط مستوى الصوت",
"hide_volume_slider_description": "Hide the volume slider in the video player", "hide_volume_slider_description": "إخفاء شريط التحكم في مستوى الصوت في مشغل الفيديو",
"hide_brightness_slider": "Hide Brightness Slider", "hide_brightness_slider": "إخفاء شريط السطوع",
"hide_brightness_slider_description": "Hide the brightness slider in the video player" "hide_brightness_slider_description": "إخفاء شريط التحكم في السطوع في مشغل الفيديو"
}, },
"audio": { "audio": {
"audio_title": "الصوت", "audio_title": "الصوت",
@@ -195,12 +195,12 @@
"none": "لا شيء", "none": "لا شيء",
"language": "اللغة", "language": "اللغة",
"transcode_mode": { "transcode_mode": {
"title": "Audio Transcoding", "title": "تحويل ترميز الصوت",
"description": "Controls how surround audio (7.1, TrueHD, DTS-HD) is handled", "description": "يتحكم في كيفية التعامل مع الصوت المحيطي (7.1، TrueHD، DTS-HD)",
"auto": "Auto", "auto": "تلقائي",
"stereo": "Force Stereo", "stereo": "إجبار تشغيل ستيريو",
"5_1": "Allow 5.1", "5_1": "السماح بـ 5.1",
"passthrough": "Passthrough" "passthrough": "تمرير الصوت"
} }
}, },
"subtitles": { "subtitles": {
@@ -251,29 +251,29 @@
"Normal": "عادي", "Normal": "عادي",
"Thick": "سميك" "Thick": "سميك"
}, },
"subtitle_color": "Subtitle Color", "subtitle_color": "لون الترجمة",
"subtitle_background_color": "Background Color", "subtitle_background_color": "لون الخلفية",
"subtitle_font": "Subtitle Font", "subtitle_font": "خط الترجمة",
"ksplayer_title": "KSPlayer Settings", "ksplayer_title": "إعدادات KSPlayer",
"hardware_decode": "Hardware Decoding", "hardware_decode": "فك الترميز بواسطة الجهاز",
"hardware_decode_description": "Use hardware acceleration for video decoding. Disable if you experience playback issues." "hardware_decode_description": "استخدم تسريع العتاد لفك ترميز الفيديو. قم بتعطيله إذا واجهت مشكلات في التشغيل."
}, },
"vlc_subtitles": { "vlc_subtitles": {
"title": "VLC Subtitle Settings", "title": "إعدادات ترجمة VLC",
"hint": "Customize subtitle appearance for VLC player. Changes take effect on next playback.", "hint": "تخصيص مظهر الترجمة لمشغل VLC. تصبح التغييرات سارية المفعول عند التشغيل التالي.",
"text_color": "Text Color", "text_color": "لون النص",
"background_color": "Background Color", "background_color": "لون الخلفية",
"background_opacity": "Background Opacity", "background_opacity": "شفافية الخلفية",
"outline_color": "Outline Color", "outline_color": "لون إطار الخط",
"outline_opacity": "Outline Opacity", "outline_opacity": "شفافية إطار الخط",
"outline_thickness": "Outline Thickness", "outline_thickness": "سمك إطار الخط",
"bold": "Bold Text", "bold": "خط عريض",
"margin": "Bottom Margin" "margin": "الهامش السفلي"
}, },
"video_player": { "video_player": {
"title": "Video Player", "title": "مشغل الفيديو",
"video_player": "Video Player", "video_player": "مشغل الفيديو",
"video_player_description": "Choose which video player to use on iOS.", "video_player_description": "اختر مشغل الفيديو الذي سيتم استخدامه على نظام iOS.",
"ksplayer": "KSPlayer", "ksplayer": "KSPlayer",
"vlc": "VLC" "vlc": "VLC"
}, },
@@ -305,8 +305,8 @@
"select_liraries_you_want_to_hide": "اختر المكتبات التي تريد إخفاءها من تبويب المكتبة وأقسام الصفحة الرئيسية.", "select_liraries_you_want_to_hide": "اختر المكتبات التي تريد إخفاءها من تبويب المكتبة وأقسام الصفحة الرئيسية.",
"disable_haptic_feedback": "تعطيل ردود الفعل اللمسية", "disable_haptic_feedback": "تعطيل ردود الفعل اللمسية",
"default_quality": "الجودة الافتراضية", "default_quality": "الجودة الافتراضية",
"default_playback_speed": "Default Playback Speed", "default_playback_speed": "سرعة التشغيل الافتراضية",
"auto_play_next_episode": "Auto-play Next Episode", "auto_play_next_episode": "تشغيل الحلقة التالية تلقائياً",
"max_auto_play_episode_count": "الحد الأقصى لعدد الحلقات التي يتم تشغيلها تلقائيًا", "max_auto_play_episode_count": "الحد الأقصى لعدد الحلقات التي يتم تشغيلها تلقائيًا",
"disabled": "معطل" "disabled": "معطل"
}, },
@@ -314,15 +314,15 @@
"downloads_title": "التنزيلات" "downloads_title": "التنزيلات"
}, },
"music": { "music": {
"title": "Music", "title": "الموسيقى",
"playback_title": "Playback", "playback_title": "التشغيل",
"playback_description": "Configure how music is played.", "playback_description": "ضبط كيفية تشغيل الموسيقى.",
"prefer_downloaded": "Prefer Downloaded Songs", "prefer_downloaded": "تفضيل الأغاني التي تم تنزيلها",
"caching_title": "Caching", "caching_title": "التخزين المؤقت",
"caching_description": "Automatically cache upcoming tracks for smoother playback.", "caching_description": "تخزين الأغاني التالية مؤقتاً تلقائياً لضمان تشغيل أكثر سلاسة.",
"lookahead_enabled": "Enable Look-Ahead Caching", "lookahead_enabled": "تفعيل التخزين المؤقت الاستباقي",
"lookahead_count": "Tracks to Pre-cache", "lookahead_count": "عدد الأغاني المراد تخزينها مسبقاً",
"max_cache_size": "Max Cache Size" "max_cache_size": "الحد الأقصى لحجم التخزين المؤقت"
}, },
"plugins": { "plugins": {
"plugins_title": "الإضافات", "plugins_title": "الإضافات",
@@ -357,39 +357,39 @@
"save_button": "حفظ", "save_button": "حفظ",
"toasts": { "toasts": {
"saved": "تم الحفظ", "saved": "تم الحفظ",
"refreshed": "Settings refreshed from server" "refreshed": "تم تحديث الإعدادات من الخادم"
}, },
"refresh_from_server": "Refresh Settings from Server" "refresh_from_server": "تحديث الإعدادات من الخادم"
}, },
"streamystats": { "streamystats": {
"enable_streamystats": "Enable Streamystats", "enable_streamystats": "تفعيل Streamystats",
"disable_streamystats": "Disable Streamystats", "disable_streamystats": "تعطيل Streamystats",
"enable_search": "Use for Search", "enable_search": "استخدم للبحث",
"url": "URL", "url": "الرابط",
"server_url_placeholder": "http(s)://streamystats.example.com", "server_url_placeholder": "http(s)://streamystats.example.com",
"streamystats_search_hint": "Enter the URL for your Streamystats server. The URL should include http or https and optionally the port.", "streamystats_search_hint": "أدخل رابط خادم Streamystats الخاص بك. يجب أن يتضمن الرابط البروتوكول http أو https مع رقم المنفذ اختيارياً.",
"read_more_about_streamystats": "Read More About Streamystats.", "read_more_about_streamystats": "اقرأ المزيد عن Streamystats.",
"save_button": "Save", "save_button": "حفظ",
"save": "Save", "save": "حفظ",
"features_title": "Features", "features_title": "المميزات",
"home_sections_title": "Home Sections", "home_sections_title": "أقسام الرئيسية",
"enable_movie_recommendations": "Movie Recommendations", "enable_movie_recommendations": "توصيات الأفلام",
"enable_series_recommendations": "Series Recommendations", "enable_series_recommendations": "توصيات المسلسلات",
"enable_promoted_watchlists": "Promoted Watchlists", "enable_promoted_watchlists": "قوائم مشاهدة مختارة",
"hide_watchlists_tab": "Hide Watchlists Tab", "hide_watchlists_tab": "إخفاء تبويب قوائم المشاهدة",
"home_sections_hint": "Show personalized recommendations and promoted watchlists from Streamystats on the home page.", "home_sections_hint": "إظهار التوصيات المخصصة وقوائم المشاهدة المختارة من Streamystats في الصفحة الرئيسية.",
"recommended_movies": "Recommended Movies", "recommended_movies": "أفلام موصى بها",
"recommended_series": "Recommended Series", "recommended_series": "مسلسلات موصى بها",
"toasts": { "toasts": {
"saved": "Saved", "saved": "تم الحفظ",
"refreshed": "Settings refreshed from server", "refreshed": "تم تحديث الإعدادات من الخادم",
"disabled": "Streamystats disabled" "disabled": "تم تعطيل Streamystats"
}, },
"refresh_from_server": "Refresh Settings from Server" "refresh_from_server": "تحديث الإعدادات من الخادم"
}, },
"kefinTweaks": { "kefinTweaks": {
"watchlist_enabler": "Enable our Watchlist integration", "watchlist_enabler": "تفعيل الربط مع قائمة المشاهدة الخاصة بنا",
"watchlist_button": "Toggle Watchlist integration" "watchlist_button": "تبديل حالة ربط قائمة المشاهدة"
} }
}, },
"storage": { "storage": {
@@ -398,15 +398,15 @@
"device_usage": "الجهاز {{availableSpace}}%", "device_usage": "الجهاز {{availableSpace}}%",
"size_used": "تم استخدام {{used}} من {{total}}", "size_used": "تم استخدام {{used}} من {{total}}",
"delete_all_downloaded_files": "حذف جميع الملفات التي تم تنزيلها", "delete_all_downloaded_files": "حذف جميع الملفات التي تم تنزيلها",
"music_cache_title": "Music Cache", "music_cache_title": "التخزين المؤقت للموسيقى",
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support", "music_cache_description": "تخزين الأغاني تلقائياً أثناء الاستماع لضمان تشغيل أكثر سلاسة ودعم الاستماع بدون اتصال",
"enable_music_cache": "Enable Music Cache", "enable_music_cache": "تمكين التخزين المؤقت للموسيقى",
"clear_music_cache": "Clear Music Cache", "clear_music_cache": "مسح التخزين المؤقت للموسيقى",
"music_cache_size": "{{size}} cached", "music_cache_size": "تم تخزين {{size}} مؤقتاً",
"music_cache_cleared": "Music cache cleared", "music_cache_cleared": "تم مسح التخزين المؤقت للموسيقى",
"delete_all_downloaded_songs": "Delete All Downloaded Songs", "delete_all_downloaded_songs": "حذف جميع الأغاني التي تم تنزيلها",
"downloaded_songs_size": "{{size}} downloaded", "downloaded_songs_size": "تم تنزيل {{size}}",
"downloaded_songs_deleted": "Downloaded songs deleted" "downloaded_songs_deleted": "تم حذف الأغاني التي تم تنزيلها"
}, },
"intro": { "intro": {
"title": "المقدمة", "title": "المقدمة",
@@ -499,15 +499,15 @@
"subtitle": "الترجمة", "subtitle": "الترجمة",
"play": "تشغيل", "play": "تشغيل",
"none": "لا شيء", "none": "لا شيء",
"track": "Track", "track": "أغنية",
"cancel": "Cancel", "cancel": "إلغاء",
"delete": "Delete", "delete": "حذف",
"ok": "OK", "ok": "حسناً",
"remove": "Remove", "remove": "إزالة",
"next": "Next", "next": "التالي",
"back": "Back", "back": "رجوع",
"continue": "Continue", "continue": "متابعة",
"verifying": "Verifying..." "verifying": "جارٍ التحقق..."
}, },
"search": { "search": {
"search": "بحث...", "search": "بحث...",
@@ -521,10 +521,10 @@
"episodes": "حلقات", "episodes": "حلقات",
"collections": "مجموعات", "collections": "مجموعات",
"actors": "ممثلون", "actors": "ممثلون",
"artists": "Artists", "artists": "الفنانون",
"albums": "Albums", "albums": "الألبومات",
"songs": "Songs", "songs": "الأغاني",
"playlists": "Playlists", "playlists": "قوائم التشغيل",
"request_movies": "طلب أفلام", "request_movies": "طلب أفلام",
"request_series": "طلب مسلسلات", "request_series": "طلب مسلسلات",
"recently_added": "أضيف مؤخرًا", "recently_added": "أضيف مؤخرًا",
@@ -572,7 +572,7 @@
"genres": "الأنواع", "genres": "الأنواع",
"years": "السنوات", "years": "السنوات",
"sort_by": "ترتيب حسب", "sort_by": "ترتيب حسب",
"filter_by": "Filter By", "filter_by": "تصفية حسب",
"sort_order": "اتجاه الترتيب", "sort_order": "اتجاه الترتيب",
"tags": "الوسوم" "tags": "الوسوم"
} }
@@ -604,11 +604,17 @@
"index": "الفِهْرِس:", "index": "الفِهْرِس:",
"continue_watching": "متابعة المشاهدة", "continue_watching": "متابعة المشاهدة",
"go_back": "رجوع", "go_back": "رجوع",
"downloaded_file_title": "You have this file downloaded", "downloaded_file_title": "تم تنزيل هذا الملف",
"downloaded_file_message": "هل تريد تشغيل الملف الذي تم تنزيله؟", "downloaded_file_message": "هل تريد تشغيل الملف الذي تم تنزيله؟",
"downloaded_file_yes": "نعم", "downloaded_file_yes": "نعم",
"downloaded_file_no": "لا", "downloaded_file_no": "لا",
"downloaded_file_cancel": "Cancel" "downloaded_file_cancel": "إلغاء"
},
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
}, },
"item_card": { "item_card": {
"next_up": "التالي", "next_up": "التالي",
@@ -624,7 +630,7 @@
"no_similar_items_found": "لم يتم العثور على عناصر مشابهة", "no_similar_items_found": "لم يتم العثور على عناصر مشابهة",
"video": "فيديو", "video": "فيديو",
"more_details": "المزيد من التفاصيل", "more_details": "المزيد من التفاصيل",
"media_options": "Media Options", "media_options": "خيارات الوسائط",
"quality": "الجودة", "quality": "الجودة",
"audio": "الصوت", "audio": "الصوت",
"subtitles": "الترجمة", "subtitles": "الترجمة",
@@ -719,127 +725,127 @@
"favorites": "المفضلة" "favorites": "المفضلة"
}, },
"music": { "music": {
"title": "Music", "title": "الموسيقى",
"tabs": { "tabs": {
"suggestions": "Suggestions", "suggestions": "الإقتراحات",
"albums": "Albums", "albums": "الألبومات",
"artists": "Artists", "artists": "الفنانون",
"playlists": "Playlists", "playlists": "قوائم التشغيل",
"tracks": "tracks" "tracks": "الأغاني"
}, },
"filters": { "filters": {
"all": "All" "all": "الكل"
}, },
"recently_added": "Recently Added", "recently_added": "أضيف مؤخرًا",
"recently_played": "Recently Played", "recently_played": "تم تشغيله مؤخرًا",
"frequently_played": "Frequently Played", "frequently_played": "الأكثر تشغيلاً",
"explore": "Explore", "explore": "اكتشف",
"top_tracks": "Top Tracks", "top_tracks": "أفضل الأغاني",
"play": "Play", "play": "تشغيل",
"shuffle": "Shuffle", "shuffle": "ترتيب عشوائي",
"play_top_tracks": "Play Top Tracks", "play_top_tracks": "تشغيل أفضل الأغاني",
"no_suggestions": "No suggestions available", "no_suggestions": "لا توجد مقترحات متاحة",
"no_albums": "No albums found", "no_albums": "لا توجد ألبومات",
"no_artists": "No artists found", "no_artists": "لا يوجد فنانون",
"no_playlists": "No playlists found", "no_playlists": "لا توجد قوائم تشغيل",
"album_not_found": "Album not found", "album_not_found": "الألبوم غير موجود",
"artist_not_found": "Artist not found", "artist_not_found": "الفنان غير موجود",
"playlist_not_found": "Playlist not found", "playlist_not_found": "قائمة التشغيل غير موجودة",
"track_options": { "track_options": {
"play_next": "Play Next", "play_next": "تشغيل التالي",
"add_to_queue": "Add to Queue", "add_to_queue": "إضافة إلى قائمة الانتظار",
"add_to_playlist": "Add to Playlist", "add_to_playlist": "أضف إلى قائمة التشغيل",
"download": "Download", "download": "تنزيل",
"downloaded": "Downloaded", "downloaded": "تم التنزيل",
"downloading": "Downloading...", "downloading": "جارٍ التنزيل...",
"cached": "Cached", "cached": "تم التخزين مؤقتاً",
"delete_download": "Delete Download", "delete_download": "حذف ملف التنزيل",
"delete_cache": "Remove from Cache", "delete_cache": "إزالة من التخزين المؤقت",
"go_to_artist": "Go to Artist", "go_to_artist": "انتقال إلى الفنان",
"go_to_album": "Go to Album", "go_to_album": "انتقال إلى الألبوم",
"add_to_favorites": "Add to Favorites", "add_to_favorites": "إضافة إلى المفضلة",
"remove_from_favorites": "Remove from Favorites", "remove_from_favorites": "إزالة من المفضلة",
"remove_from_playlist": "Remove from Playlist" "remove_from_playlist": "إزالة من قائمة التشغيل"
}, },
"playlists": { "playlists": {
"create_playlist": "Create Playlist", "create_playlist": "إنشاء قائمة التشغيل",
"playlist_name": "Playlist Name", "playlist_name": "اسم قائمة التشغيل",
"enter_name": "Enter playlist name", "enter_name": "أدخل اسم قائمة التشغيل",
"create": "Create", "create": "إنشاء",
"search_playlists": "Search playlists...", "search_playlists": "البحث عن قوائم التشغيل...",
"added_to": "Added to {{name}}", "added_to": "تمت الإضافة إلى {{name}}",
"added": "Added to playlist", "added": "تمت الإضافة إلى قائمة التشغيل",
"removed_from": "Removed from {{name}}", "removed_from": "تمت الإزالة من {{name}}",
"removed": "Removed from playlist", "removed": "تمت الازالة من قائمة التشغيل",
"created": "Playlist created", "created": "تم إنشاء قائمة التشغيل",
"create_new": "Create New Playlist", "create_new": "إنشاء قائمة تشغيل جديدة",
"failed_to_add": "Failed to add to playlist", "failed_to_add": "فشلت الإضافة إلى قائمة التشغيل",
"failed_to_remove": "Failed to remove from playlist", "failed_to_remove": "فشلت الإزالة من قائمة التشغيل",
"failed_to_create": "Failed to create playlist", "failed_to_create": "فشل إنشاء قائمة التشغيل",
"delete_playlist": "Delete Playlist", "delete_playlist": "حذف قائمة التشغيل",
"delete_confirm": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.", "delete_confirm": "هل أنت متأكد من رغبتك في حذف {{name}}؟ لا يمكن التراجع عن هذا الإجراء.",
"deleted": "Playlist deleted", "deleted": "تم حذف قائمة التشغيل",
"failed_to_delete": "Failed to delete playlist" "failed_to_delete": "فشل إنشاء قائمة التشغيل"
}, },
"sort": { "sort": {
"title": "Sort By", "title": "ترتيب حسب",
"alphabetical": "Alphabetical", "alphabetical": "أبجدي",
"date_created": "Date Created" "date_created": "تاريخ الإنشاء"
} }
}, },
"watchlists": { "watchlists": {
"title": "Watchlists", "title": "قوائم المشاهدة",
"my_watchlists": "My Watchlists", "my_watchlists": "قوائم المشاهدة الخاصة بي",
"public_watchlists": "Public Watchlists", "public_watchlists": "قوائم مشاهدة عامة",
"create_title": "Create Watchlist", "create_title": "إنشاء قائمة مشاهدة",
"edit_title": "Edit Watchlist", "edit_title": "تعديل قائمة المشاهدة",
"create_button": "Create Watchlist", "create_button": "إنشاء قائمة مشاهدة",
"save_button": "Save Changes", "save_button": "حفظ التغييرات",
"delete_button": "Delete", "delete_button": "حذف",
"remove_button": "Remove", "remove_button": "إزالة",
"cancel_button": "Cancel", "cancel_button": "إلغاء",
"name_label": "Name", "name_label": "الاسم",
"name_placeholder": "Enter watchlist name", "name_placeholder": "أدخل اسم قائمة المشاهدة",
"description_label": "Description", "description_label": "الوصف",
"description_placeholder": "Enter description (optional)", "description_placeholder": "أدخل الوصف (اختياري)",
"is_public_label": "Public Watchlist", "is_public_label": "قائمة مشاهدة عامة",
"is_public_description": "Allow others to view this watchlist", "is_public_description": "السماح للآخرين بعرض قائمة المشاهدة هذه",
"allowed_type_label": "Content Type", "allowed_type_label": "نوع المحتوى",
"sort_order_label": "Default Sort Order", "sort_order_label": "الترتيب الافتراضي",
"empty_title": "No Watchlists", "empty_title": "لا توجد قوائم مشاهدة",
"empty_description": "Create your first watchlist to start organizing your media", "empty_description": "قم بإنشاء أول قائمة مشاهدة لبدء تنظيم الوسائط الخاصة بك",
"empty_watchlist": "This watchlist is empty", "empty_watchlist": "قائمة المشاهدة هذه فارغة",
"empty_watchlist_hint": "Add items from your library to this watchlist", "empty_watchlist_hint": "إضافة عناصر من مكتبتك إلى قائمة المشاهدة هذه",
"not_configured_title": "Streamystats Not Configured", "not_configured_title": "لم يتم ضبط Streamystats",
"not_configured_description": "Configure Streamystats in settings to use watchlists", "not_configured_description": "اضبط Streamystats في الإعدادات لاستخدام قوائم المشاهدة",
"go_to_settings": "Go to Settings", "go_to_settings": "الذهاب إلى الإعدادات",
"add_to_watchlist": "Add to Watchlist", "add_to_watchlist": "إضافة إلى قائمة المشاهدة",
"remove_from_watchlist": "Remove from Watchlist", "remove_from_watchlist": "إزالة من قائمة المشاهدة",
"select_watchlist": "Select Watchlist", "select_watchlist": "تحديد قائمة المشاهدة",
"create_new": "Create New Watchlist", "create_new": "إنشاء قائمة مشاهدة جديدة",
"item": "item", "item": "عنصر",
"items": "items", "items": "عناصر",
"public": "Public", "public": "عامة",
"private": "Private", "private": "خاصة",
"you": "You", "you": "أنت",
"by_owner": "By another user", "by_owner": "بواسطة مستخدم آخر",
"not_found": "Watchlist not found", "not_found": "قائمة المشاهدة غير موجودة",
"delete_confirm_title": "Delete Watchlist", "delete_confirm_title": "حذف قائمة المشاهدة",
"delete_confirm_message": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.", "delete_confirm_message": "هل أنت متأكد من رغبتك في حذف \"{{name}}\"؟ لا يمكن التراجع عن هذا الإجراء.",
"remove_item_title": "Remove from Watchlist", "remove_item_title": "إزالة من قائمة المشاهدة",
"remove_item_message": "Remove \"{{name}}\" from this watchlist?", "remove_item_message": "إزالة \"{{name}}\" من قائمة المشاهدة هذه؟",
"loading": "Loading watchlists...", "loading": "تحميل قوائم المشاهدة...",
"no_compatible_watchlists": "No compatible watchlists", "no_compatible_watchlists": "لا توجد قوائم مشاهدة متوافقة",
"create_one_first": "Create a watchlist that accepts this content type" "create_one_first": "إنشاء قائمة مشاهدة تقبل نوع المحتوى هذا"
}, },
"playback_speed": { "playback_speed": {
"title": "Playback Speed", "title": "سرعة التشغيل",
"apply_to": "Apply To", "apply_to": "تطبيق على",
"speed": "Speed", "speed": "السرعة",
"scope": { "scope": {
"media": "This media only", "media": "الوسائط هذه فقط",
"show": "This show", "show": "هذا المسلسل",
"all": "All media (default)" "all": "جميع الوسائط (الافتراضي)"
} }
} }
} }

View File

@@ -610,6 +610,12 @@
"downloaded_file_no": "No", "downloaded_file_no": "No",
"downloaded_file_cancel": "Cancel" "downloaded_file_cancel": "Cancel"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "A continuació", "next_up": "A continuació",
"no_items_to_display": "No hi ha elements per mostrar", "no_items_to_display": "No hi ha elements per mostrar",

View File

@@ -610,6 +610,12 @@
"downloaded_file_no": "No", "downloaded_file_no": "No",
"downloaded_file_cancel": "Cancel" "downloaded_file_cancel": "Cancel"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "Další nahoru", "next_up": "Další nahoru",
"no_items_to_display": "Žádné položky k zobrazení", "no_items_to_display": "Žádné položky k zobrazení",

View File

@@ -610,6 +610,12 @@
"downloaded_file_no": "No", "downloaded_file_no": "No",
"downloaded_file_cancel": "Cancel" "downloaded_file_cancel": "Cancel"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "Næste", "next_up": "Næste",
"no_items_to_display": "Ingen elementer at vise", "no_items_to_display": "Ingen elementer at vise",

View File

@@ -7,7 +7,7 @@
"username_placeholder": "Benutzername", "username_placeholder": "Benutzername",
"password_placeholder": "Passwort", "password_placeholder": "Passwort",
"login_button": "Anmelden", "login_button": "Anmelden",
"quick_connect": "Schnellverbindung", "quick_connect": "Quick Connect",
"enter_code_to_login": "Gib den Code {{code}} ein, um dich anzumelden", "enter_code_to_login": "Gib den Code {{code}} ein, um dich anzumelden",
"failed_to_initiate_quick_connect": "Fehler beim Initiieren der Schnellverbindung", "failed_to_initiate_quick_connect": "Fehler beim Initiieren der Schnellverbindung",
"got_it": "Verstanden", "got_it": "Verstanden",
@@ -30,48 +30,48 @@
"connect_button": "Verbinden", "connect_button": "Verbinden",
"previous_servers": "Vorherige Server", "previous_servers": "Vorherige Server",
"clear_button": "Löschen", "clear_button": "Löschen",
"swipe_to_remove": "Swipe to remove", "swipe_to_remove": "Wischen, um zu entfernen",
"search_for_local_servers": "Nach lokalen Servern suchen", "search_for_local_servers": "Nach lokalen Servern suchen",
"searching": "Suche...", "searching": "Suche...",
"servers": "Server", "servers": "Server",
"saved": "Saved", "saved": "Gespeichert",
"session_expired": "Session Expired", "session_expired": "Sitzung abgelaufen",
"please_login_again": "Your saved session has expired. Please log in again.", "please_login_again": "Ihre Sitzung ist abgelaufen. Bitte erneut anmelden.",
"remove_saved_login": "Remove Saved Login", "remove_saved_login": "Gespeicherte Zugangsdaten entfernen",
"remove_saved_login_description": "This will remove your saved credentials for this server. You'll need to enter your username and password again next time.", "remove_saved_login_description": "Hiermit werden ihre gespeicherten Zugangsdaten für diesen Server entfernt. Sie müssen sich dann erneut anmelden.",
"accounts_count": "{{count}} accounts", "accounts_count": "{{count}} Konten",
"select_account": "Select Account", "select_account": "Konto auswählen",
"add_account": "Add Account", "add_account": "Konto hinzufügen",
"remove_account_description": "This will remove the saved credentials for {{username}}." "remove_account_description": "Hiermit werden die gespeicherten Zugangsdaten für {{username}} entfernt."
}, },
"save_account": { "save_account": {
"title": "Save Account", "title": "Konto speichern",
"save_for_later": "Save this account", "save_for_later": "Dieses Konto speichern",
"security_option": "Security Option", "security_option": "Sicherheitseinstellung",
"no_protection": "No protection", "no_protection": "Keine",
"no_protection_desc": "Quick login without authentication", "no_protection_desc": "Schnellanmeldung ohne Authentifizierung",
"pin_code": "PIN code", "pin_code": "PIN",
"pin_code_desc": "4-digit PIN required when switching", "pin_code_desc": "4-stellige PIN bei Konto-Wechsel erforderlich",
"password": "Re-enter password", "password": "Passwort wiederholen",
"password_desc": "Password required when switching", "password_desc": "Passwort bei Konto-Wechsel erforderlich",
"save_button": "Save", "save_button": "Speichern",
"cancel_button": "Cancel" "cancel_button": "Abbrechen"
}, },
"pin": { "pin": {
"enter_pin": "Enter PIN", "enter_pin": "PIN eingeben",
"enter_pin_for": "Enter PIN for {{username}}", "enter_pin_for": "PIN für {{username}} eingeben",
"enter_4_digits": "Enter 4 digits", "enter_4_digits": "4 Ziffern eingeben",
"invalid_pin": "Invalid PIN", "invalid_pin": "Ungültige PIN",
"setup_pin": "Set Up PIN", "setup_pin": "PIN festlegen",
"confirm_pin": "Confirm PIN", "confirm_pin": "PIN bestätigen",
"pins_dont_match": "PINs don't match", "pins_dont_match": "PIN stimmt nicht überein",
"forgot_pin": "Forgot PIN?", "forgot_pin": "PIN vergessen?",
"forgot_pin_desc": "Your saved credentials will be removed" "forgot_pin_desc": "Ihre gespeicherten Zugangsdaten werden entfernt"
}, },
"password": { "password": {
"enter_password": "Enter Password", "enter_password": "Passwort eingeben",
"enter_password_for": "Enter password for {{username}}", "enter_password_for": "Passwort für {{username}} eingeben",
"invalid_password": "Invalid password" "invalid_password": "Ungültiges Passwort"
}, },
"home": { "home": {
"checking_server_connection": "Überprüfe Serververbindung...", "checking_server_connection": "Überprüfe Serververbindung...",
@@ -87,7 +87,7 @@
"error_message": "Etwas ist schiefgelaufen.\nBitte melde dich ab und wieder an.", "error_message": "Etwas ist schiefgelaufen.\nBitte melde dich ab und wieder an.",
"continue_watching": "Weiterschauen", "continue_watching": "Weiterschauen",
"next_up": "Als nächstes", "next_up": "Als nächstes",
"continue_and_next_up": "Continue & Next Up", "continue_and_next_up": "\"Weiterschauen\" und \"Als Nächstes\"",
"recently_added_in": "Kürzlich hinzugefügt in {{libraryName}}", "recently_added_in": "Kürzlich hinzugefügt in {{libraryName}}",
"suggested_movies": "Empfohlene Filme", "suggested_movies": "Empfohlene Filme",
"suggested_episodes": "Empfohlene Episoden", "suggested_episodes": "Empfohlene Episoden",
@@ -120,36 +120,36 @@
}, },
"appearance": { "appearance": {
"title": "Aussehen", "title": "Aussehen",
"merge_next_up_continue_watching": "Merge Continue Watching & Next Up", "merge_next_up_continue_watching": "\"Weiterschauen\" und \"Als Nächstes\" kombinieren",
"hide_remote_session_button": "Hide Remote Session Button" "hide_remote_session_button": "Button für Remote-Sitzung ausblenden"
}, },
"network": { "network": {
"title": "Network", "title": "Netzwerk",
"local_network": "Local Network", "local_network": "Lokales Netzwerk",
"auto_switch_enabled": "Auto-switch when at home", "auto_switch_enabled": "Zuhause automatisch wechseln",
"auto_switch_description": "Automatically switch to local URL when connected to home WiFi", "auto_switch_description": "Im WLAN Zuhause automatisch zu lokaler URL wechseln",
"local_url": "Local URL", "local_url": "Lokale URL",
"local_url_hint": "Enter your local server address (e.g., http://192.168.1.100:8096)", "local_url_hint": "Lokale Server-URL eingeben (zB. http://192.168.1.100:8096)",
"local_url_placeholder": "http://192.168.1.100:8096", "local_url_placeholder": "http://192.168.1.100:8096",
"home_wifi_networks": "Home WiFi Networks", "home_wifi_networks": "Private WLAN-Netze",
"add_current_network": "Add \"{{ssid}}\"", "add_current_network": "{{ssid}} hinzufügen",
"not_connected_to_wifi": "Not connected to WiFi", "not_connected_to_wifi": "Nicht mit WLAN verbunden",
"no_networks_configured": "No networks configured", "no_networks_configured": "Keine Netzwerke konfiguriert",
"add_network_hint": "Add your home WiFi network to enable auto-switching", "add_network_hint": "Füge dein privates WLAN-Netz hinzu um automatischen Wechsel zu aktivieren",
"current_wifi": "Current WiFi", "current_wifi": "Aktuelles WLAN-Netz",
"using_url": "Using", "using_url": "Verwendet",
"local": "Local URL", "local": "Lokale URL",
"remote": "Remote URL", "remote": "Remote URL",
"not_connected": "Not connected", "not_connected": "Nicht verbunden",
"current_server": "Current Server", "current_server": "Aktueller Server",
"remote_url": "Remote URL", "remote_url": "Remote URL",
"active_url": "Active URL", "active_url": "Aktive URL",
"not_configured": "Not configured", "not_configured": "Nicht konfiguriert",
"network_added": "Network added", "network_added": "Netzwerk hinzugefügt",
"network_already_added": "Network already added", "network_already_added": "Netzwerk bereits hinzugefügt",
"no_wifi_connected": "Not connected to WiFi", "no_wifi_connected": "Nicht mit WLAN verbunden",
"permission_denied": "Location permission denied", "permission_denied": "Standortberechtigung nicht verfügbar",
"permission_denied_explanation": "Location permission is required to detect WiFi network for auto-switching. Please enable it in Settings." "permission_denied_explanation": "Standortberechtigung ist nötig um WLAN-Netze für den automatischen Wechsel zu erkennen. Bitte in den Einstellungen aktivieren."
}, },
"user_info": { "user_info": {
"user_info_title": "Benutzerinformationen", "user_info_title": "Benutzerinformationen",
@@ -159,82 +159,82 @@
"app_version": "App-Version" "app_version": "App-Version"
}, },
"quick_connect": { "quick_connect": {
"quick_connect_title": "Schnellverbindung", "quick_connect_title": "Quick Connect",
"authorize_button": "Schnellverbindung autorisieren", "authorize_button": "Quick Connect autorisieren",
"enter_the_quick_connect_code": "Gib den Schnellverbindungscode ein...", "enter_the_quick_connect_code": "Quick Connect-Code eingeben...",
"success": "Erfolg", "success": "Erfolgreich verbunden",
"quick_connect_autorized": "Schnellverbindung autorisiert", "quick_connect_autorized": "Quick Connect autorisiert",
"error": "Fehler", "error": "Fehler",
"invalid_code": "Ungültiger Code", "invalid_code": "Ungültiger Code",
"authorize": "Autorisieren" "authorize": "Autorisieren"
}, },
"media_controls": { "media_controls": {
"media_controls_title": "Mediensteuerung", "media_controls_title": "Mediensteuerung",
"forward_skip_length": "Vorspulzeit", "forward_skip_length": "Vorspullänge",
"rewind_length": "Rückspulzeit", "rewind_length": "Rückspullänge",
"seconds_unit": "s" "seconds_unit": "s"
}, },
"gesture_controls": { "gesture_controls": {
"gesture_controls_title": "Gestensteuerung", "gesture_controls_title": "Gestensteuerung",
"horizontal_swipe_skip": "Horizontales Wischen zum Überspringen", "horizontal_swipe_skip": "Horizontal Wischen zum Überspringen",
"horizontal_swipe_skip_description": "Wische links/rechts, wenn Steuerelemente ausgeblendet werden um zu überspringen", "horizontal_swipe_skip_description": "Wische links/rechts, wenn Steuerelemente ausgeblendet sind um zu überspringen",
"left_side_brightness": "Helligkeitskontrolle der linken Seite", "left_side_brightness": "Helligkeitsregler Links",
"left_side_brightness_description": "Wischen Sie auf der linken Seite nach oben/runter, um die Helligkeit anzupassen", "left_side_brightness_description": "Links nach oben/unten wischen um Helligkeit anzupassen",
"right_side_volume": "Lautstärkeregelung der rechten Seite", "right_side_volume": "Lautstärkeregler Rechts",
"right_side_volume_description": "Auf der rechten Seite nach oben/unten wischen, um Lautstärke anzupassen", "right_side_volume_description": "Rechts nach oben/unten wischen um Lautstärke anzupassen",
"hide_volume_slider": "Hide Volume Slider", "hide_volume_slider": "Lautstärkeregler ausblenden",
"hide_volume_slider_description": "Hide the volume slider in the video player", "hide_volume_slider_description": "Lautstärkeregler im Videoplayer ausblenden",
"hide_brightness_slider": "Hide Brightness Slider", "hide_brightness_slider": "Helligkeitsregler ausblenden",
"hide_brightness_slider_description": "Hide the brightness slider in the video player" "hide_brightness_slider_description": "Helligkeitsregler im Videoplayer ausblenden"
}, },
"audio": { "audio": {
"audio_title": "Audio", "audio_title": "Audio",
"set_audio_track": "Audiospur aus dem vorherigen Element festlegen", "set_audio_track": "Audiospur aus dem vorherigen Element übernehmen",
"audio_language": "Audio-Sprache", "audio_language": "Audio-Sprache",
"audio_hint": "Wähl die Standardsprache für Audio aus.", "audio_hint": "Standardsprache für Audio auswählen.",
"none": "Keine", "none": "Keine",
"language": "Sprache", "language": "Sprache",
"transcode_mode": { "transcode_mode": {
"title": "Audio Transcoding", "title": "Audio-Transcoding",
"description": "Controls how surround audio (7.1, TrueHD, DTS-HD) is handled", "description": "Legt fest, wie Surround-Audio (7.1, TrueHD, DTS-HD) behandelt wird",
"auto": "Auto", "auto": "Auto",
"stereo": "Force Stereo", "stereo": "Stereo erzwingen",
"5_1": "Allow 5.1", "5_1": "5.1 erlauben",
"passthrough": "Passthrough" "passthrough": "Passthrough"
} }
}, },
"subtitles": { "subtitles": {
"subtitle_title": "Untertitel", "subtitle_title": "Untertitel",
"subtitle_hint": "Konfigurier die Untertitel-Präferenzen.", "subtitle_hint": "Untertitel-Erscheinungsbild und Verhalten konfigurieren.",
"subtitle_language": "Untertitel-Sprache", "subtitle_language": "Untertitel-Sprache",
"subtitle_mode": "Untertitel-Modus", "subtitle_mode": "Untertitel-Modus",
"set_subtitle_track": "Untertitel-Spur aus dem vorherigen Element festlegen", "set_subtitle_track": "Untertitel-Spur aus dem vorherigen Element übernehmen",
"subtitle_size": "Untertitel-Größe", "subtitle_size": "Untertitel-Größe",
"none": "Keine", "none": "Keine",
"language": "Sprache", "language": "Sprache",
"loading": "Lädt", "loading": "Lädt",
"modes": { "modes": {
"Default": "Standard", "Default": "Standard",
"Smart": "Intelligent", "Smart": "Smart",
"Always": "Immer", "Always": "Immer",
"None": "Keine", "None": "Keine",
"OnlyForced": "Nur erzwungen" "OnlyForced": "Nur erzwungene"
}, },
"text_color": "Textfarbe", "text_color": "Textfarbe",
"background_color": "Hintergrundfarbe", "background_color": "Hintergrundfarbe",
"outline_color": "Konturfarbe", "outline_color": "Konturfarbe",
"outline_thickness": "Umriss Dicke", "outline_thickness": "Konturdicke",
"background_opacity": "Hintergrundtransparenz", "background_opacity": "Hintergrundtransparenz",
"outline_opacity": "Kontur-Deckkraft", "outline_opacity": "Konturtransparenz",
"bold_text": "Bold Text", "bold_text": "Fettgedruckter Text",
"colors": { "colors": {
"Black": "Schwarz", "Black": "Schwarz",
"Gray": "Grau", "Gray": "Grau",
"Silver": "Silber", "Silver": "Silber",
"White": "Weiß", "White": "Weiß",
"Maroon": "Marotte", "Maroon": "Rotbraun",
"Red": "Rot", "Red": "Rot",
"Fuchsia": "Fuchsia", "Fuchsia": "Magenta",
"Yellow": "Gelb", "Yellow": "Gelb",
"Olive": "Olivgrün", "Olive": "Olivgrün",
"Green": "Grün", "Green": "Grün",
@@ -251,29 +251,29 @@
"Normal": "Normal", "Normal": "Normal",
"Thick": "Dick" "Thick": "Dick"
}, },
"subtitle_color": "Subtitle Color", "subtitle_color": "Untertitelfarbe",
"subtitle_background_color": "Background Color", "subtitle_background_color": "Hintergrundfarbe",
"subtitle_font": "Subtitle Font", "subtitle_font": "Untertitel-Schriftart",
"ksplayer_title": "KSPlayer Settings", "ksplayer_title": "KSPlayer Einstellungen",
"hardware_decode": "Hardware Decoding", "hardware_decode": "Hardware Decoding",
"hardware_decode_description": "Use hardware acceleration for video decoding. Disable if you experience playback issues." "hardware_decode_description": "Hardwarebeschleunigung für Video Decoding verwenden. Deaktivieren wenn Wiedergabeprobleme auftreten."
}, },
"vlc_subtitles": { "vlc_subtitles": {
"title": "VLC Subtitle Settings", "title": "VLC Untertitel-Einstellungen",
"hint": "Customize subtitle appearance for VLC player. Changes take effect on next playback.", "hint": "Anpassen des Untertitel-Erscheinungsbildes für VLC. Änderungen werden bei der nächsten Wiedergabe übernommen.",
"text_color": "Text Color", "text_color": "Schriftfarbe",
"background_color": "Background Color", "background_color": "Hintergrundfarbe",
"background_opacity": "Background Opacity", "background_opacity": "Hintergrundtransparenz",
"outline_color": "Outline Color", "outline_color": "Konturfarbe",
"outline_opacity": "Outline Opacity", "outline_opacity": "Konturtransparenz",
"outline_thickness": "Outline Thickness", "outline_thickness": "Konturdicke",
"bold": "Bold Text", "bold": "Fettgedruckter Text",
"margin": "Bottom Margin" "margin": "Unterer Abstand"
}, },
"video_player": { "video_player": {
"title": "Video Player", "title": "Videoplayer",
"video_player": "Video Player", "video_player": "Videoplayer",
"video_player_description": "Choose which video player to use on iOS.", "video_player_description": "Videoplayer auf iOS auswählen.",
"ksplayer": "KSPlayer", "ksplayer": "KSPlayer",
"vlc": "VLC" "vlc": "VLC"
}, },
@@ -282,7 +282,7 @@
"video_orientation": "Videoausrichtung", "video_orientation": "Videoausrichtung",
"orientation": "Ausrichtung", "orientation": "Ausrichtung",
"orientations": { "orientations": {
"DEFAULT": "Standard", "DEFAULT": "Geräteausrichtung folgen",
"ALL": "Alle", "ALL": "Alle",
"PORTRAIT": "Hochformat", "PORTRAIT": "Hochformat",
"PORTRAIT_UP": "Hochformat oben", "PORTRAIT_UP": "Hochformat oben",
@@ -294,54 +294,54 @@
"UNKNOWN": "Unbekannt" "UNKNOWN": "Unbekannt"
}, },
"safe_area_in_controls": "Sicherer Bereich in den Steuerungen", "safe_area_in_controls": "Sicherer Bereich in den Steuerungen",
"video_player": "Video player", "video_player": "Videoplayer",
"video_players": { "video_players": {
"VLC_3": "VLC 3", "VLC_3": "VLC 3",
"VLC_4": "VLC 4 (Experimentell + PiP)" "VLC_4": "VLC 4 (Experimentell + PiP)"
}, },
"show_custom_menu_links": "Benutzerdefinierte Menülinks anzeigen", "show_custom_menu_links": "Benutzerdefinierte Menülinks anzeigen",
"show_large_home_carousel": "Zeige Großes Heimkarussell (Beta)", "show_large_home_carousel": "Zeige große Startseiten-Übersicht (Beta)",
"hide_libraries": "Bibliotheken ausblenden", "hide_libraries": "Bibliotheken ausblenden",
"select_liraries_you_want_to_hide": "Wähl die Bibliotheken aus, die du im Bibliothekstab und auf der Startseite ausblenden möchtest.", "select_liraries_you_want_to_hide": "Bibliotheken auswählen die aus dem Bibliothekstab und der Startseite ausgeblendet werden sollen.",
"disable_haptic_feedback": "Haptisches Feedback deaktivieren", "disable_haptic_feedback": "Haptisches Feedback deaktivieren",
"default_quality": "Standardqualität", "default_quality": "Standardqualität",
"default_playback_speed": "Default Playback Speed", "default_playback_speed": "Standard-Wiedergabegeschwindigkeit",
"auto_play_next_episode": "Auto-play Next Episode", "auto_play_next_episode": "Automatisch nächste Episode abspielen",
"max_auto_play_episode_count": "Max. automatische Wiedergabe Episodenanzahl", "max_auto_play_episode_count": "Maximale automatisch abzuspielende Episodenanzahl",
"disabled": "Deaktiviert" "disabled": "Deaktiviert"
}, },
"downloads": { "downloads": {
"downloads_title": "Downloads" "downloads_title": "Downloads"
}, },
"music": { "music": {
"title": "Music", "title": "Musik",
"playback_title": "Playback", "playback_title": "Wiedergabe",
"playback_description": "Configure how music is played.", "playback_description": "Konfigurieren, wie Musik abgespielt wird.",
"prefer_downloaded": "Prefer Downloaded Songs", "prefer_downloaded": "Bevorzuge heruntergeladene Titel",
"caching_title": "Caching", "caching_title": "Caching",
"caching_description": "Automatically cache upcoming tracks for smoother playback.", "caching_description": "Automatisches Caching anstehender Titel für bessere Wiedergabe.",
"lookahead_enabled": "Enable Look-Ahead Caching", "lookahead_enabled": "Look-Ahead Caching aktivieren",
"lookahead_count": "Tracks to Pre-cache", "lookahead_count": "Titel vorher in den Cache laden",
"max_cache_size": "Max Cache Size" "max_cache_size": "Maximale Cache-Größe"
}, },
"plugins": { "plugins": {
"plugins_title": "Erweiterungen", "plugins_title": "Plugins",
"jellyseerr": { "jellyseerr": {
"jellyseerr_warning": "Diese integration ist in einer frühen Entwicklungsphase. Erwarte Veränderungen.", "jellyseerr_warning": "Diese Integration ist in einer frühen Entwicklungsphase und kann jederzeit geändert werden.",
"server_url": "Server Adresse", "server_url": "Server URL",
"server_url_hint": "Beispiel: http(s)://your-host.url\n(Portnummer hinzufügen, falls erforderlich)", "server_url_hint": "Beispiel: http(s)://your-host.url\n(Port hinzufügen, falls erforderlich)",
"server_url_placeholder": "Jellyseerr URL...", "server_url_placeholder": "Seerr URL",
"password": "Passwort", "password": "Passwort",
"password_placeholder": "Passwort für Jellyfin Benutzer {{username}} eingeben", "password_placeholder": "Passwort für Jellyfin Benutzer {{username}} eingeben",
"login_button": "Anmelden", "login_button": "Anmelden",
"total_media_requests": "Gesamtanfragen", "total_media_requests": "Gesamtanfragen",
"movie_quota_limit": "Film-Anfragelimit", "movie_quota_limit": "Film-Anfragelimit",
"movie_quota_days": "Film-Anfragetage", "movie_quota_days": "Film-Anfragetagelimit",
"tv_quota_limit": "TV-Anfragelimit", "tv_quota_limit": "Serien-Anfragelimit",
"tv_quota_days": "TV-Anfragetage", "tv_quota_days": "Serien-Anfragetagelimit",
"reset_jellyseerr_config_button": "Setze Jellyseerr-Konfiguration zurück", "reset_jellyseerr_config_button": "Seerr-Konfiguration zurücksetzen",
"unlimited": "Unlimitiert", "unlimited": "Unlimitiert",
"plus_n_more": "+{{n}} more", "plus_n_more": "+{{n}} weitere",
"order_by": { "order_by": {
"DEFAULT": "Standard", "DEFAULT": "Standard",
"VOTE_COUNT_AND_AVERAGE": "Stimmenanzahl und Durchschnitt", "VOTE_COUNT_AND_AVERAGE": "Stimmenanzahl und Durchschnitt",
@@ -352,71 +352,71 @@
"enable_marlin_search": "Aktiviere Marlin Search", "enable_marlin_search": "Aktiviere Marlin Search",
"url": "URL", "url": "URL",
"server_url_placeholder": "http(s)://domain.org:port", "server_url_placeholder": "http(s)://domain.org:port",
"marlin_search_hint": "Gib die URL für den Marlin Server ein. Die URL sollte http oder https enthalten und optional den Port.", "marlin_search_hint": "URL für den Marlin Server eingeben. Die URL sollte http oder https enthalten und optional den Port.",
"read_more_about_marlin": "Erfahre mehr über Marlin.", "read_more_about_marlin": "Erfahre mehr über Marlin.",
"save_button": "Speichern", "save_button": "Speichern",
"toasts": { "toasts": {
"saved": "Gespeichert", "saved": "Gespeichert",
"refreshed": "Settings refreshed from server" "refreshed": "Einstellungen vom Server aktualisiert"
}, },
"refresh_from_server": "Refresh Settings from Server" "refresh_from_server": "Einstellungen vom Server aktualisieren"
}, },
"streamystats": { "streamystats": {
"enable_streamystats": "Enable Streamystats", "enable_streamystats": "Streamystats aktivieren",
"disable_streamystats": "Disable Streamystats", "disable_streamystats": "Streamystats deaktivieren",
"enable_search": "Use for Search", "enable_search": "Zum Suchen verwenden",
"url": "URL", "url": "URL",
"server_url_placeholder": "http(s)://streamystats.example.com", "server_url_placeholder": "http(s)://streamystats.example.com",
"streamystats_search_hint": "Enter the URL for your Streamystats server. The URL should include http or https and optionally the port.", "streamystats_search_hint": "URL für den Streamystats-Server eingeben.",
"read_more_about_streamystats": "Read More About Streamystats.", "read_more_about_streamystats": "Mehr über Streamystats erfahren.",
"save_button": "Save", "save_button": "Speichern",
"save": "Save", "save": "Gespeichert",
"features_title": "Features", "features_title": "Features",
"home_sections_title": "Home Sections", "home_sections_title": "Startseitenbereiche",
"enable_movie_recommendations": "Movie Recommendations", "enable_movie_recommendations": "Filmempfehlungen",
"enable_series_recommendations": "Series Recommendations", "enable_series_recommendations": "Serienempfehlungen",
"enable_promoted_watchlists": "Promoted Watchlists", "enable_promoted_watchlists": "Empfohlene Merklisten",
"hide_watchlists_tab": "Hide Watchlists Tab", "hide_watchlists_tab": "Merklisten-Tab ausblenden",
"home_sections_hint": "Show personalized recommendations and promoted watchlists from Streamystats on the home page.", "home_sections_hint": "Zeige personalisierte Empfehlungen und empfohlene Merklisten von Streamystats auf der Startseite.",
"recommended_movies": "Recommended Movies", "recommended_movies": "Empfohlene Filme",
"recommended_series": "Recommended Series", "recommended_series": "Empfohlene Serien",
"toasts": { "toasts": {
"saved": "Saved", "saved": "Gespeichert",
"refreshed": "Settings refreshed from server", "refreshed": "Einstellungen vom Server aktualisiert",
"disabled": "Streamystats disabled" "disabled": "Streamystats deaktiviert"
}, },
"refresh_from_server": "Refresh Settings from Server" "refresh_from_server": "Einstellungen vom Server aktualisieren"
}, },
"kefinTweaks": { "kefinTweaks": {
"watchlist_enabler": "Enable our Watchlist integration", "watchlist_enabler": "Merklisten-Integration aktivieren",
"watchlist_button": "Toggle Watchlist integration" "watchlist_button": "Merklisten-Integration umschalten"
} }
}, },
"storage": { "storage": {
"storage_title": "Speicher", "storage_title": "Speicher",
"app_usage": "App {{usedSpace}}%", "app_usage": "App {{usedSpace}}%",
"device_usage": "Gerät {{availableSpace}}%", "device_usage": "Gerät {{availableSpace}}%",
"size_used": "{{used}} von {{total}} benutzt", "size_used": "{{used}} von {{total}} genutzt",
"delete_all_downloaded_files": "Alle Downloads löschen", "delete_all_downloaded_files": "Alle heruntergeladenen Dateien löschen",
"music_cache_title": "Music Cache", "music_cache_title": "Musik-Cache",
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support", "music_cache_description": "Beim Anhören Titel automatisch in den Cache laden um bessere Wiedergabe und Offline-Wiedergabe zu ermöglichen",
"enable_music_cache": "Enable Music Cache", "enable_music_cache": "Musik-Cache aktivieren",
"clear_music_cache": "Clear Music Cache", "clear_music_cache": "Musik-Cache leeren",
"music_cache_size": "{{size}} cached", "music_cache_size": "{{size}} gechached",
"music_cache_cleared": "Music cache cleared", "music_cache_cleared": "Musik-Cache geleert",
"delete_all_downloaded_songs": "Delete All Downloaded Songs", "delete_all_downloaded_songs": "Alle heruntergeladenen Titel löschen",
"downloaded_songs_size": "{{size}} downloaded", "downloaded_songs_size": "{{size}} heruntergeladen",
"downloaded_songs_deleted": "Downloaded songs deleted" "downloaded_songs_deleted": "Heruntergeladene Titel gelöscht"
}, },
"intro": { "intro": {
"title": "Intro ", "title": "Einführung",
"show_intro": "Show intro", "show_intro": "Einführung anzeigen",
"reset_intro": "Reset intro" "reset_intro": "Einführung zurücksetzen"
}, },
"logs": { "logs": {
"logs_title": "Logs", "logs_title": "Logs",
"export_logs": "Export logs", "export_logs": "Logs exportieren",
"click_for_more_info": "Click for more info", "click_for_more_info": "Für mehr Informationen klicken",
"level": "Level", "level": "Level",
"no_logs_available": "Keine Logs verfügbar", "no_logs_available": "Keine Logs verfügbar",
"delete_all_logs": "Alle Logs löschen" "delete_all_logs": "Alle Logs löschen"
@@ -438,21 +438,21 @@
}, },
"downloads": { "downloads": {
"downloads_title": "Downloads", "downloads_title": "Downloads",
"tvseries": "TV-Serien", "tvseries": "Serien",
"movies": "Filme", "movies": "Filme",
"queue": "Warteschlange", "queue": "Warteschlange",
"other_media": "Andere Medien", "other_media": "Andere Medien",
"queue_hint": "Warteschlange und aktive Downloads gehen verloren bei App-Neustart", "queue_hint": "Warteschlange und aktive Downloads gehen verloren wenn die App neu gestartet wird",
"no_items_in_queue": "Keine Elemente in der Warteschlange", "no_items_in_queue": "Keine Elemente in der Warteschlange",
"no_downloaded_items": "Keine heruntergeladenen Elemente", "no_downloaded_items": "Keine heruntergeladenen Elemente",
"delete_all_movies_button": "Alle Filme löschen", "delete_all_movies_button": "Alle Filme löschen",
"delete_all_tvseries_button": "Alle TV-Serien löschen", "delete_all_tvseries_button": "Alle Serien löschen",
"delete_all_button": "Alles löschen", "delete_all_button": "Alles löschen",
"delete_all_other_media_button": "Andere Medien löschen", "delete_all_other_media_button": "Alle anderen Medien löschen",
"active_download": "Aktiver Download", "active_download": "Aktiver Download",
"no_active_downloads": "Keine aktiven Downloads", "no_active_downloads": "Keine aktiven Downloads",
"active_downloads": "Aktive Downloads", "active_downloads": "Aktive Downloads",
"new_app_version_requires_re_download": "Die neue App-Version erfordert das erneute Herunterladen.", "new_app_version_requires_re_download": "Neue App-Version erfordert erneutes Herunterladen",
"new_app_version_requires_re_download_description": "Die neue App-Version erfordert das erneute Herunterladen von Filmen und Serien. Bitte lösche alle heruntergeladenen Elemente und starte den Download erneut.", "new_app_version_requires_re_download_description": "Die neue App-Version erfordert das erneute Herunterladen von Filmen und Serien. Bitte lösche alle heruntergeladenen Elemente und starte den Download erneut.",
"back": "Zurück", "back": "Zurück",
"delete": "Löschen", "delete": "Löschen",
@@ -463,8 +463,8 @@
"you_are_not_allowed_to_download_files": "Du hast keine Berechtigung, Dateien herunterzuladen", "you_are_not_allowed_to_download_files": "Du hast keine Berechtigung, Dateien herunterzuladen",
"deleted_all_movies_successfully": "Alle Filme erfolgreich gelöscht!", "deleted_all_movies_successfully": "Alle Filme erfolgreich gelöscht!",
"failed_to_delete_all_movies": "Fehler beim Löschen aller Filme", "failed_to_delete_all_movies": "Fehler beim Löschen aller Filme",
"deleted_all_tvseries_successfully": "Alle TV-Serien erfolgreich gelöscht!", "deleted_all_tvseries_successfully": "Alle Serien erfolgreich gelöscht!",
"failed_to_delete_all_tvseries": "Fehler beim Löschen aller TV-Serien", "failed_to_delete_all_tvseries": "Fehler beim Löschen aller Serien",
"deleted_media_successfully": "Andere Medien erfolgreich gelöscht!", "deleted_media_successfully": "Andere Medien erfolgreich gelöscht!",
"failed_to_delete_media": "Fehler beim Löschen anderer Medien", "failed_to_delete_media": "Fehler beim Löschen anderer Medien",
"download_deleted": "Download gelöscht", "download_deleted": "Download gelöscht",
@@ -486,7 +486,7 @@
"all_files_folders_and_jobs_deleted_successfully": "Alle Dateien, Ordner und Jobs erfolgreich gelöscht", "all_files_folders_and_jobs_deleted_successfully": "Alle Dateien, Ordner und Jobs erfolgreich gelöscht",
"failed_to_clean_cache_directory": "Fehler beim Bereinigen des Cache-Verzeichnisses", "failed_to_clean_cache_directory": "Fehler beim Bereinigen des Cache-Verzeichnisses",
"could_not_get_download_url_for_item": "Download-URL für {{itemName}} konnte nicht geladen werden", "could_not_get_download_url_for_item": "Download-URL für {{itemName}} konnte nicht geladen werden",
"go_to_downloads": "Gehe zu den Downloads", "go_to_downloads": "Zu Downloads gehen",
"file_deleted": "{{item}} gelöscht" "file_deleted": "{{item}} gelöscht"
} }
} }
@@ -499,18 +499,18 @@
"subtitle": "Untertitel", "subtitle": "Untertitel",
"play": "Abspielen", "play": "Abspielen",
"none": "Keine", "none": "Keine",
"track": "Track", "track": "Spur",
"cancel": "Cancel", "cancel": "Abbrechen",
"delete": "Delete", "delete": "Löschen",
"ok": "OK", "ok": "OK",
"remove": "Remove", "remove": "Entfernen",
"next": "Next", "next": "Weiter",
"back": "Back", "back": "Zurück",
"continue": "Continue", "continue": "Fortsetzen",
"verifying": "Verifying..." "verifying": "Verifiziere..."
}, },
"search": { "search": {
"search": "Suche...", "search": "Suchen...",
"x_items": "{{count}} Elemente", "x_items": "{{count}} Elemente",
"library": "Bibliothek", "library": "Bibliothek",
"discover": "Entdecken", "discover": "Entdecken",
@@ -521,33 +521,33 @@
"episodes": "Episoden", "episodes": "Episoden",
"collections": "Sammlungen", "collections": "Sammlungen",
"actors": "Schauspieler", "actors": "Schauspieler",
"artists": "Artists", "artists": "Künstler",
"albums": "Albums", "albums": "Alben",
"songs": "Songs", "songs": "Titel",
"playlists": "Playlists", "playlists": "Playlists",
"request_movies": "Film anfragen", "request_movies": "Film anfragen",
"request_series": "Serie anfragen", "request_series": "Serie anfragen",
"recently_added": "Kürzlich hinzugefügt", "recently_added": "Kürzlich hinzugefügt",
"recent_requests": "Kürzlich angefragt", "recent_requests": "Kürzlich angefragt",
"plex_watchlist": "Plex Watchlist", "plex_watchlist": "Plex Merkliste",
"trending": "In den Trends", "trending": "Beliebt",
"popular_movies": "Beliebte Filme", "popular_movies": "Beliebte Filme",
"movie_genres": "Film-Genres", "movie_genres": "Film-Genres",
"upcoming_movies": "Kommende Filme", "upcoming_movies": "Kommende Filme",
"studios": "Studios", "studios": "Studios",
"popular_tv": "Beliebte TV-Serien", "popular_tv": "Beliebte Serien",
"tv_genres": "TV-Serien-Genres", "tv_genres": "Serien-Genres",
"upcoming_tv": "Kommende TV-Serien", "upcoming_tv": "Kommende Serien",
"networks": "Netzwerke", "networks": "Sender",
"tmdb_movie_keyword": "TMDB Film-Schlüsselwort", "tmdb_movie_keyword": "TMDB Film-Schlüsselwort",
"tmdb_movie_genre": "TMDB Film-Genre", "tmdb_movie_genre": "TMDB Film-Genre",
"tmdb_tv_keyword": "TMDB TV-Serien-Schlüsselwort", "tmdb_tv_keyword": "TMDB Serien-Schlüsselwort",
"tmdb_tv_genre": "TMDB TV-Serien-Genre", "tmdb_tv_genre": "TMDB Serien-Genre",
"tmdb_search": "TMDB Suche", "tmdb_search": "TMDB Suche",
"tmdb_studio": "TMDB Studio", "tmdb_studio": "TMDB Studio",
"tmdb_network": "TMDB Netzwerk", "tmdb_network": "TMDB Netzwerk",
"tmdb_movie_streaming_services": "TMDB Film-Streaming-Dienste", "tmdb_movie_streaming_services": "TMDB Film-Streaming-Dienste",
"tmdb_tv_streaming_services": "TMDB TV-Serien-Streaming-Dienste" "tmdb_tv_streaming_services": "TMDB Serien-Streaming-Dienste"
}, },
"library": { "library": {
"no_results": "Keine Ergebnisse", "no_results": "Keine Ergebnisse",
@@ -572,7 +572,7 @@
"genres": "Genres", "genres": "Genres",
"years": "Jahre", "years": "Jahre",
"sort_by": "Sortieren nach", "sort_by": "Sortieren nach",
"filter_by": "Filter By", "filter_by": "Filtern nach",
"sort_order": "Sortierreihenfolge", "sort_order": "Sortierreihenfolge",
"tags": "Tags" "tags": "Tags"
} }
@@ -585,7 +585,7 @@
"boxsets": "Boxsets", "boxsets": "Boxsets",
"playlists": "Wiedergabelisten", "playlists": "Wiedergabelisten",
"noDataTitle": "Noch keine Favoriten", "noDataTitle": "Noch keine Favoriten",
"noData": "Markiere Elemente als Favoriten, damit sie hier für einen schnellen Zugriff angezeigt werden." "noData": "Elemente als Favoriten markieren, um sie hier anzuzeigen."
}, },
"custom_links": { "custom_links": {
"no_links": "Keine Links" "no_links": "Keine Links"
@@ -593,7 +593,7 @@
"player": { "player": {
"error": "Fehler", "error": "Fehler",
"failed_to_get_stream_url": "Fehler beim Abrufen der Stream-URL", "failed_to_get_stream_url": "Fehler beim Abrufen der Stream-URL",
"an_error_occured_while_playing_the_video": "Ein Fehler ist beim Abspielen des Videos aufgetreten. Überprüf die Logs in den Einstellungen.", "an_error_occured_while_playing_the_video": "Ein Fehler ist beim Abspielen des Videos aufgetreten. Logs in den Einstellungen überprüfen.",
"client_error": "Client-Fehler", "client_error": "Client-Fehler",
"could_not_create_stream_for_chromecast": "Konnte keinen Stream für Chromecast erstellen", "could_not_create_stream_for_chromecast": "Konnte keinen Stream für Chromecast erstellen",
"message_from_server": "Nachricht vom Server: {{message}}", "message_from_server": "Nachricht vom Server: {{message}}",
@@ -602,17 +602,23 @@
"audio_tracks": "Audiospuren:", "audio_tracks": "Audiospuren:",
"playback_state": "Wiedergabestatus:", "playback_state": "Wiedergabestatus:",
"index": "Index:", "index": "Index:",
"continue_watching": "Weiterschauen", "continue_watching": "Fortsetzen",
"go_back": "Zurück", "go_back": "Zurück",
"downloaded_file_title": "Diese Datei wurde heruntergeladen", "downloaded_file_title": "Diese Datei wurde bereits heruntergeladen",
"downloaded_file_message": "Möchten Sie die heruntergeladene Datei abspielen?", "downloaded_file_message": "Heruntergeladene Datei abspielen?",
"downloaded_file_yes": "Ja", "downloaded_file_yes": "Ja",
"downloaded_file_no": "Nein", "downloaded_file_no": "Nein",
"downloaded_file_cancel": "Abbrechen" "downloaded_file_cancel": "Abbrechen"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "Als Nächstes", "next_up": "Als Nächstes",
"no_items_to_display": "Keine Elemente zum Anzeigen", "no_items_to_display": "Keine Elemente",
"cast_and_crew": "Besetzung und Crew", "cast_and_crew": "Besetzung und Crew",
"series": "Serien", "series": "Serien",
"seasons": "Staffeln", "seasons": "Staffeln",
@@ -630,7 +636,7 @@
"subtitles": "Untertitel", "subtitles": "Untertitel",
"show_more": "Mehr anzeigen", "show_more": "Mehr anzeigen",
"show_less": "Weniger anzeigen", "show_less": "Weniger anzeigen",
"appeared_in": "Erschienen in", "appeared_in": "Erschien in",
"could_not_load_item": "Konnte Element nicht laden", "could_not_load_item": "Konnte Element nicht laden",
"none": "Keine", "none": "Keine",
"download": { "download": {
@@ -639,13 +645,13 @@
"download_episode": "Episode herunterladen", "download_episode": "Episode herunterladen",
"download_movie": "Film herunterladen", "download_movie": "Film herunterladen",
"download_x_item": "{{item_count}} Elemente herunterladen", "download_x_item": "{{item_count}} Elemente herunterladen",
"download_unwatched_only": "Nur unbeobachtete", "download_unwatched_only": "Nur Ungesehene",
"download_button": "Herunterladen" "download_button": "Herunterladen"
} }
}, },
"live_tv": { "live_tv": {
"next": "Nächster", "next": "Nächste",
"previous": "Vorheriger", "previous": "Vorherige",
"coming_soon": "Demnächst", "coming_soon": "Demnächst",
"on_now": "Jetzt", "on_now": "Jetzt",
"shows": "Serien", "shows": "Serien",
@@ -658,10 +664,10 @@
"confirm": "Bestätigen", "confirm": "Bestätigen",
"cancel": "Abbrechen", "cancel": "Abbrechen",
"yes": "Ja", "yes": "Ja",
"whats_wrong": "Hast du Probleme?", "whats_wrong": "Was stimmt nicht?",
"issue_type": "Fehlerart", "issue_type": "Art des Problems",
"select_an_issue": "Wähle einen Fehlerart aus", "select_an_issue": "Wähle die Art des Problems aus",
"types": "Arten", "types": "Problem-Arten",
"describe_the_issue": "(optional) Beschreibe das Problem", "describe_the_issue": "(optional) Beschreibe das Problem",
"submit_button": "Absenden", "submit_button": "Absenden",
"report_issue_button": "Fehler melden", "report_issue_button": "Fehler melden",
@@ -671,7 +677,7 @@
"cast": "Besetzung", "cast": "Besetzung",
"details": "Details", "details": "Details",
"status": "Status", "status": "Status",
"original_title": "Original Titel", "original_title": "Originaltitel",
"series_type": "Serien Typ", "series_type": "Serien Typ",
"release_dates": "Veröffentlichungsdaten", "release_dates": "Veröffentlichungsdaten",
"first_air_date": "Erstausstrahlungsdatum", "first_air_date": "Erstausstrahlungsdatum",
@@ -687,10 +693,10 @@
"request_as": "Anfragen als", "request_as": "Anfragen als",
"tags": "Tags", "tags": "Tags",
"quality_profile": "Qualitätsprofil", "quality_profile": "Qualitätsprofil",
"root_folder": "Root-Ordner", "root_folder": "Stammverzeichnis",
"season_all": "Season (all)", "season_all": "Staffeln (alle)",
"season_number": "Staffel {{season_number}}", "season_number": "Staffel {{season_number}}",
"number_episodes": "{{episode_number}} Folgen", "number_episodes": "{{episode_number}} Episoden",
"born": "Geboren", "born": "Geboren",
"appearances": "Auftritte", "appearances": "Auftritte",
"approve": "Genehmigen", "approve": "Genehmigen",
@@ -698,9 +704,9 @@
"requested_by": "Angefragt von {{user}}", "requested_by": "Angefragt von {{user}}",
"unknown_user": "Unbekannter Nutzer", "unknown_user": "Unbekannter Nutzer",
"toasts": { "toasts": {
"jellyseer_does_not_meet_requirements": "Jellyseerr Server erfüllt nicht die Anforderungsversion. Bitte aktualisiere deinen Jellyseerr Server auf mindestens 2.0.0", "jellyseer_does_not_meet_requirements": "Seerr-Server erfüllt nicht die minimalen Versionsanforderungen. Bitte den Seerr-Server auf mindestens 2.0.0 aktualisieren.",
"jellyseerr_test_failed": "Jellyseerr-Test fehlgeschlagen. Bitte versuche es erneut.", "jellyseerr_test_failed": "Seerr-Test fehlgeschlagen. Bitte erneut versuchen.",
"failed_to_test_jellyseerr_server_url": "Fehler beim Testen der Jellyseerr-Server-URL", "failed_to_test_jellyseerr_server_url": "Fehler beim Test der Seerr-Server-URL",
"issue_submitted": "Problem eingereicht!", "issue_submitted": "Problem eingereicht!",
"requested_item": "{{item}} angefragt!", "requested_item": "{{item}} angefragt!",
"you_dont_have_permission_to_request": "Du hast keine Berechtigung Anfragen zu stellen", "you_dont_have_permission_to_request": "Du hast keine Berechtigung Anfragen zu stellen",
@@ -715,131 +721,131 @@
"home": "Startseite", "home": "Startseite",
"search": "Suche", "search": "Suche",
"library": "Bibliothek", "library": "Bibliothek",
"custom_links": "Benutzerdefinierte Links", "custom_links": "Links",
"favorites": "Favoriten" "favorites": "Favoriten"
}, },
"music": { "music": {
"title": "Music", "title": "Musik",
"tabs": { "tabs": {
"suggestions": "Suggestions", "suggestions": "Vorschläge",
"albums": "Albums", "albums": "Alben",
"artists": "Artists", "artists": "Künstler",
"playlists": "Playlists", "playlists": "Playlists",
"tracks": "tracks" "tracks": "Titel"
}, },
"filters": { "filters": {
"all": "All" "all": "Alle"
}, },
"recently_added": "Recently Added", "recently_added": "Kürzlich hinzugefügt",
"recently_played": "Recently Played", "recently_played": "Vor kurzem gehört",
"frequently_played": "Frequently Played", "frequently_played": "Oft gehört",
"explore": "Explore", "explore": "Entdecken",
"top_tracks": "Top Tracks", "top_tracks": "Top-Titel",
"play": "Play", "play": "Abspielen",
"shuffle": "Shuffle", "shuffle": "Shuffle",
"play_top_tracks": "Play Top Tracks", "play_top_tracks": "Top-Tracks abspielen",
"no_suggestions": "No suggestions available", "no_suggestions": "Keine Vorschläge verfügbar",
"no_albums": "No albums found", "no_albums": "Keine Alben gefunden",
"no_artists": "No artists found", "no_artists": "Keine Künstler gefunden",
"no_playlists": "No playlists found", "no_playlists": "Keine Playlists gefunden",
"album_not_found": "Album not found", "album_not_found": "Album nicht gefunden",
"artist_not_found": "Artist not found", "artist_not_found": "Künstler nicht gefunden",
"playlist_not_found": "Playlist not found", "playlist_not_found": "Playlist nicht gefunden",
"track_options": { "track_options": {
"play_next": "Play Next", "play_next": "Als Nächstes wiedergeben",
"add_to_queue": "Add to Queue", "add_to_queue": "Zur Warteschlange hinzufügen",
"add_to_playlist": "Add to Playlist", "add_to_playlist": "Zur Playlist hinzufügen",
"download": "Download", "download": "Herunterladen",
"downloaded": "Downloaded", "downloaded": "Heruntergeladen",
"downloading": "Downloading...", "downloading": "Wird heruntergeladen...",
"cached": "Cached", "cached": "Gecached",
"delete_download": "Delete Download", "delete_download": "Download löschen",
"delete_cache": "Remove from Cache", "delete_cache": "Aus dem Cache löschen",
"go_to_artist": "Go to Artist", "go_to_artist": "Zum Künstler gehen",
"go_to_album": "Go to Album", "go_to_album": "Zum Album gehen",
"add_to_favorites": "Add to Favorites", "add_to_favorites": "Zu Favoriten hinzufügen",
"remove_from_favorites": "Remove from Favorites", "remove_from_favorites": "Aus Favoriten entfernen",
"remove_from_playlist": "Remove from Playlist" "remove_from_playlist": "Aus Playlist entfernen"
}, },
"playlists": { "playlists": {
"create_playlist": "Create Playlist", "create_playlist": "Playlist erstellen",
"playlist_name": "Playlist Name", "playlist_name": "Playlist Name",
"enter_name": "Enter playlist name", "enter_name": "Playlist Name eingeben",
"create": "Create", "create": "Erstellen",
"search_playlists": "Search playlists...", "search_playlists": "Playlisten durchsuchen...",
"added_to": "Added to {{name}}", "added_to": "Zu {{name}} hinzugefügt",
"added": "Added to playlist", "added": "Zur Playlist hinzugefügt",
"removed_from": "Removed from {{name}}", "removed_from": "Aus {{name}} entfernt",
"removed": "Removed from playlist", "removed": "Aus Playlist entfernt",
"created": "Playlist created", "created": "Playlist erstellt",
"create_new": "Create New Playlist", "create_new": "Neue Playlist erstellen",
"failed_to_add": "Failed to add to playlist", "failed_to_add": "Fehler beim Hinzufügen zur Playlist",
"failed_to_remove": "Failed to remove from playlist", "failed_to_remove": "Fehler beim Entfernen aus der Playlist",
"failed_to_create": "Failed to create playlist", "failed_to_create": "Fehler beim Erstellen der Playlist",
"delete_playlist": "Delete Playlist", "delete_playlist": "Playlist löschen",
"delete_confirm": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.", "delete_confirm": "Bist Du sicher, dass Du \"{{name}}\" löschen möchtest? Das kann nicht rückgängig gemacht werden.",
"deleted": "Playlist deleted", "deleted": "Playlist gelöscht",
"failed_to_delete": "Failed to delete playlist" "failed_to_delete": "Fehler beim Löschen der Playlist"
}, },
"sort": { "sort": {
"title": "Sort By", "title": "Sortieren nach",
"alphabetical": "Alphabetical", "alphabetical": "Alphabetisch",
"date_created": "Date Created" "date_created": "Erstellungsdatum"
} }
}, },
"watchlists": { "watchlists": {
"title": "Watchlists", "title": "Merklisten",
"my_watchlists": "My Watchlists", "my_watchlists": "Meine Merklisten",
"public_watchlists": "Public Watchlists", "public_watchlists": "Öffentliche Merklisten",
"create_title": "Create Watchlist", "create_title": "Merkliste erstellen",
"edit_title": "Edit Watchlist", "edit_title": "Merkliste bearbeiten",
"create_button": "Create Watchlist", "create_button": "Merkliste erstellen",
"save_button": "Save Changes", "save_button": "Änderungen speichern",
"delete_button": "Delete", "delete_button": "Löschen",
"remove_button": "Remove", "remove_button": "Entfernen",
"cancel_button": "Cancel", "cancel_button": "Abbrechen",
"name_label": "Name", "name_label": "Name",
"name_placeholder": "Enter watchlist name", "name_placeholder": "Merklistenname eingeben",
"description_label": "Description", "description_label": "Beschreibung",
"description_placeholder": "Enter description (optional)", "description_placeholder": "Beschreibung eingeben (optional)",
"is_public_label": "Public Watchlist", "is_public_label": "Öffentliche Merkliste",
"is_public_description": "Allow others to view this watchlist", "is_public_description": "Anderen erlauben diese Merkliste anzusehen",
"allowed_type_label": "Content Type", "allowed_type_label": "Inhaltstyp",
"sort_order_label": "Default Sort Order", "sort_order_label": "Standard-Sortierreihenfolge",
"empty_title": "No Watchlists", "empty_title": "Keine Merklisten",
"empty_description": "Create your first watchlist to start organizing your media", "empty_description": "Erstelle deine erste Merkliste um deine Medien zu organisieren",
"empty_watchlist": "This watchlist is empty", "empty_watchlist": "Diese Merkliste ist leer",
"empty_watchlist_hint": "Add items from your library to this watchlist", "empty_watchlist_hint": "Füge Elemente aus deiner Bibliothek zu dieser Merkliste hinzu",
"not_configured_title": "Streamystats Not Configured", "not_configured_title": "Streamystats nicht konfiguriert",
"not_configured_description": "Configure Streamystats in settings to use watchlists", "not_configured_description": "Streamystats in den Einstellungen konfigurieren, um Merklisten zu verwenden",
"go_to_settings": "Go to Settings", "go_to_settings": "Gehe zu Einstellungen",
"add_to_watchlist": "Add to Watchlist", "add_to_watchlist": "Zur Merkliste hinzufügen",
"remove_from_watchlist": "Remove from Watchlist", "remove_from_watchlist": "Von Merkliste entfernen",
"select_watchlist": "Select Watchlist", "select_watchlist": "Merkliste auswählen",
"create_new": "Create New Watchlist", "create_new": "Neue Merkliste erstellen",
"item": "item", "item": "Element",
"items": "items", "items": "Elemente",
"public": "Public", "public": "Öffentlich",
"private": "Private", "private": "Privat",
"you": "You", "you": "Du",
"by_owner": "By another user", "by_owner": "Von einem anderen Benutzer",
"not_found": "Watchlist not found", "not_found": "Merkliste nicht gefunden",
"delete_confirm_title": "Delete Watchlist", "delete_confirm_title": "Merkliste löschen",
"delete_confirm_message": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.", "delete_confirm_message": "Bist Du sicher, dass Du \"{{name}}\" löschen möchtest? Das kann nicht rückgängig gemacht werden.",
"remove_item_title": "Remove from Watchlist", "remove_item_title": "Von Merkliste entfernen",
"remove_item_message": "Remove \"{{name}}\" from this watchlist?", "remove_item_message": "\"{{name}}\" von dieser Merkliste entfernen?",
"loading": "Loading watchlists...", "loading": "Lade Merklisten...",
"no_compatible_watchlists": "No compatible watchlists", "no_compatible_watchlists": "Keine kompatiblen Merklisten",
"create_one_first": "Create a watchlist that accepts this content type" "create_one_first": "Erstelle eine Merkliste, welche diesen Inhaltstyp akzeptiert"
}, },
"playback_speed": { "playback_speed": {
"title": "Playback Speed", "title": "Wiedergabegeschwindigkeit",
"apply_to": "Apply To", "apply_to": "Anwenden auf",
"speed": "Speed", "speed": "Geschwindigkeit",
"scope": { "scope": {
"media": "This media only", "media": "Nur hier",
"show": "This show", "show": "Nur diese Serie",
"all": "All media (default)" "all": "Alle (Standard)"
} }
} }
} }

View File

@@ -610,6 +610,12 @@
"downloaded_file_no": "No", "downloaded_file_no": "No",
"downloaded_file_cancel": "Cancel" "downloaded_file_cancel": "Cancel"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "Επόμενο Επάνω", "next_up": "Επόμενο Επάνω",
"no_items_to_display": "Δεν υπάρχουν στοιχεία προς εμφάνιση", "no_items_to_display": "Δεν υπάρχουν στοιχεία προς εμφάνιση",

View File

@@ -610,6 +610,12 @@
"downloaded_file_no": "No", "downloaded_file_no": "No",
"downloaded_file_cancel": "Cancel" "downloaded_file_cancel": "Cancel"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "Next Up", "next_up": "Next Up",
"no_items_to_display": "No Items to Display", "no_items_to_display": "No Items to Display",

View File

@@ -39,39 +39,39 @@
"please_login_again": "Su sesión guardada ha caducado. Por favor, inicie sesión de nuevo.", "please_login_again": "Su sesión guardada ha caducado. Por favor, inicie sesión de nuevo.",
"remove_saved_login": "Eliminar inicio de sesión guardado", "remove_saved_login": "Eliminar inicio de sesión guardado",
"remove_saved_login_description": "Esto eliminará tus credenciales guardadas para este servidor. Tendrás que volver a introducir tu nombre de usuario y contraseña la próxima vez.", "remove_saved_login_description": "Esto eliminará tus credenciales guardadas para este servidor. Tendrás que volver a introducir tu nombre de usuario y contraseña la próxima vez.",
"accounts_count": "{{count}} accounts", "accounts_count": "{{count}} cuentas",
"select_account": "Select Account", "select_account": "Seleccione una cuenta",
"add_account": "Add Account", "add_account": "Añadir cuenta",
"remove_account_description": "This will remove the saved credentials for {{username}}." "remove_account_description": "Esto eliminará las credenciales guardadas para {{username}}."
}, },
"save_account": { "save_account": {
"title": "Save Account", "title": "Guardar Cuenta",
"save_for_later": "Save this account", "save_for_later": "Guardar esta cuenta",
"security_option": "Security Option", "security_option": "Opciones de seguridad",
"no_protection": "No protection", "no_protection": "Sin Protección",
"no_protection_desc": "Quick login without authentication", "no_protection_desc": "Inicio de sesión rápido sin autenticación",
"pin_code": "PIN code", "pin_code": "Código PIN",
"pin_code_desc": "4-digit PIN required when switching", "pin_code_desc": "PIN de 4 dígitos requerido al cambiar",
"password": "Re-enter password", "password": "Vuelva a introducir la contraseña",
"password_desc": "Password required when switching", "password_desc": "Contraseña requerida al cambiar",
"save_button": "Save", "save_button": "Guardar",
"cancel_button": "Cancel" "cancel_button": "Cancelar"
}, },
"pin": { "pin": {
"enter_pin": "Enter PIN", "enter_pin": "Introduce el PIN",
"enter_pin_for": "Enter PIN for {{username}}", "enter_pin_for": "Introduzca el PIN para {{username}}",
"enter_4_digits": "Enter 4 digits", "enter_4_digits": "Introduce 4 dígitos",
"invalid_pin": "Invalid PIN", "invalid_pin": "PIN inválido",
"setup_pin": "Set Up PIN", "setup_pin": "Configurar PIN",
"confirm_pin": "Confirm PIN", "confirm_pin": "Confirmar PIN",
"pins_dont_match": "PINs don't match", "pins_dont_match": "Los códigos PIN no coinciden",
"forgot_pin": "Forgot PIN?", "forgot_pin": "¿Olvidó el PIN?",
"forgot_pin_desc": "Your saved credentials will be removed" "forgot_pin_desc": "Sus credenciales guardadas serán eliminadas"
}, },
"password": { "password": {
"enter_password": "Enter Password", "enter_password": "Introduzca la contraseña",
"enter_password_for": "Enter password for {{username}}", "enter_password_for": "Introduzca la contraseña para {{username}}",
"invalid_password": "Invalid password" "invalid_password": "Contraseña inválida"
}, },
"home": { "home": {
"checking_server_connection": "Comprobando conexión con el servidor...", "checking_server_connection": "Comprobando conexión con el servidor...",
@@ -124,32 +124,32 @@
"hide_remote_session_button": "Ocultar botón de sesión remota" "hide_remote_session_button": "Ocultar botón de sesión remota"
}, },
"network": { "network": {
"title": "Network", "title": "Cadena",
"local_network": "Local Network", "local_network": "Red local",
"auto_switch_enabled": "Auto-switch when at home", "auto_switch_enabled": "Cambiar automáticamente en casa",
"auto_switch_description": "Automatically switch to local URL when connected to home WiFi", "auto_switch_description": "Cambiar automáticamente a la URL local cuando se conecta a la WiFi de casa",
"local_url": "Local URL", "local_url": "URL local",
"local_url_hint": "Enter your local server address (e.g., http://192.168.1.100:8096)", "local_url_hint": "Introduzca la dirección de su servidor local (por ejemplo, http://192.168.1.100:8096)",
"local_url_placeholder": "http://192.168.1.100:8096", "local_url_placeholder": "http://192.168.1.100:8096",
"home_wifi_networks": "Home WiFi Networks", "home_wifi_networks": "Redes WiFi domésticas",
"add_current_network": "Add \"{{ssid}}\"", "add_current_network": "Añadir \"{{ssid}}\"",
"not_connected_to_wifi": "Not connected to WiFi", "not_connected_to_wifi": "No está conectado a WiFi",
"no_networks_configured": "No networks configured", "no_networks_configured": "No hay redes configuradas",
"add_network_hint": "Add your home WiFi network to enable auto-switching", "add_network_hint": "Añade tu red WiFi doméstica para activar el cambio automático",
"current_wifi": "Current WiFi", "current_wifi": "WiFi actual",
"using_url": "Using", "using_url": "Utilizando",
"local": "Local URL", "local": "URL local",
"remote": "Remote URL", "remote": "URL Remota",
"not_connected": "Not connected", "not_connected": "Sin conexión",
"current_server": "Current Server", "current_server": "Servidor actual",
"remote_url": "Remote URL", "remote_url": "URL Remota",
"active_url": "Active URL", "active_url": "URL Activa",
"not_configured": "Not configured", "not_configured": "Sin configurar",
"network_added": "Network added", "network_added": "Red añadida",
"network_already_added": "Network already added", "network_already_added": "Red ya añadida",
"no_wifi_connected": "Not connected to WiFi", "no_wifi_connected": "Sin conexión a WiFi",
"permission_denied": "Location permission denied", "permission_denied": "Permiso de ubicación denegado",
"permission_denied_explanation": "Location permission is required to detect WiFi network for auto-switching. Please enable it in Settings." "permission_denied_explanation": "Se necesita el permiso de ubicación para detectar la red WiFi para cambiar automáticamente. Por favor, actívala en Configuración."
}, },
"user_info": { "user_info": {
"user_info_title": "Información de usuario", "user_info_title": "Información de usuario",
@@ -195,12 +195,12 @@
"none": "Ninguno", "none": "Ninguno",
"language": "Idioma", "language": "Idioma",
"transcode_mode": { "transcode_mode": {
"title": "Audio Transcoding", "title": "Transcodificación de audio",
"description": "Controls how surround audio (7.1, TrueHD, DTS-HD) is handled", "description": "Controla cómo el audio envolvente (7.1, TrueHD, DTS-HD) es manejado",
"auto": "Auto", "auto": "Auto",
"stereo": "Force Stereo", "stereo": "Forzar salida estéreo",
"5_1": "Allow 5.1", "5_1": "Permitir 5.1",
"passthrough": "Passthrough" "passthrough": "Directo"
} }
}, },
"subtitles": { "subtitles": {
@@ -259,16 +259,16 @@
"hardware_decode_description": "Utilizar la aceleración de hardware para la decodificación de vídeo. Deshabilite si experimenta problemas de reproducción." "hardware_decode_description": "Utilizar la aceleración de hardware para la decodificación de vídeo. Deshabilite si experimenta problemas de reproducción."
}, },
"vlc_subtitles": { "vlc_subtitles": {
"title": "VLC Subtitle Settings", "title": "Configuración de subtítulos VLC",
"hint": "Customize subtitle appearance for VLC player. Changes take effect on next playback.", "hint": "Personalizar la apariencia de los subtítulos para el reproductor VLC. Los cambios tendrán efecto en la próxima reproducción.",
"text_color": "Text Color", "text_color": "Color del texto",
"background_color": "Background Color", "background_color": "Color del fondo",
"background_opacity": "Background Opacity", "background_opacity": "Opacidad del fondo",
"outline_color": "Outline Color", "outline_color": "Color del contorno",
"outline_opacity": "Outline Opacity", "outline_opacity": "Opacidad del contorno",
"outline_thickness": "Outline Thickness", "outline_thickness": "Grosor del contorno",
"bold": "Bold Text", "bold": "Texto en negrita",
"margin": "Bottom Margin" "margin": "Margen inferior"
}, },
"video_player": { "video_player": {
"title": "Reproductor de vídeo", "title": "Reproductor de vídeo",
@@ -300,13 +300,13 @@
"VLC_4": "VLC 4 (Experimental + PiP)" "VLC_4": "VLC 4 (Experimental + PiP)"
}, },
"show_custom_menu_links": "Mostrar enlaces de menú personalizados", "show_custom_menu_links": "Mostrar enlaces de menú personalizados",
"show_large_home_carousel": "Show Large Home Carousel (beta)", "show_large_home_carousel": "Mostrar carrusel del menú principal grande (beta)",
"hide_libraries": "Ocultar bibliotecas", "hide_libraries": "Ocultar bibliotecas",
"select_liraries_you_want_to_hide": "Selecciona las bibliotecas que quieres ocultar de la pestaña Bibliotecas y de Inicio.", "select_liraries_you_want_to_hide": "Selecciona las bibliotecas que quieres ocultar de la pestaña Bibliotecas y de Inicio.",
"disable_haptic_feedback": "Desactivar feedback háptico", "disable_haptic_feedback": "Desactivar feedback háptico",
"default_quality": "Calidad por defecto", "default_quality": "Calidad por defecto",
"default_playback_speed": "Velocidad de reproducción predeterminada", "default_playback_speed": "Velocidad de reproducción predeterminada",
"auto_play_next_episode": "Auto-play Next Episode", "auto_play_next_episode": "Reproducir automáticamente el siguiente episodio",
"max_auto_play_episode_count": "Máximo número de episodios de Auto Play", "max_auto_play_episode_count": "Máximo número de episodios de Auto Play",
"disabled": "Deshabilitado" "disabled": "Deshabilitado"
}, },
@@ -317,10 +317,10 @@
"title": "Música", "title": "Música",
"playback_title": "Reproducir", "playback_title": "Reproducir",
"playback_description": "Configurar cómo se reproduce la música.", "playback_description": "Configurar cómo se reproduce la música.",
"prefer_downloaded": "Prefer Downloaded Songs", "prefer_downloaded": "Preferir las canciones descargadas",
"caching_title": "Almacenando en caché", "caching_title": "Almacenando en caché",
"caching_description": "Automatically cache upcoming tracks for smoother playback.", "caching_description": "Cachear automáticamente las próximas canciones para una reproducción más suave.",
"lookahead_enabled": "Enable Look-Ahead Caching", "lookahead_enabled": "Activar el look-Ahead Cache",
"lookahead_count": "", "lookahead_count": "",
"max_cache_size": "Tamaño máximo del caché" "max_cache_size": "Tamaño máximo del caché"
}, },
@@ -399,7 +399,7 @@
"size_used": "{{used}} de {{total}} usado", "size_used": "{{used}} de {{total}} usado",
"delete_all_downloaded_files": "Eliminar todos los archivos descargados", "delete_all_downloaded_files": "Eliminar todos los archivos descargados",
"music_cache_title": "Caché de música", "music_cache_title": "Caché de música",
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support", "music_cache_description": "Cachear automáticamente las canciones mientras escuchas una reproducción más suave y soporte sin conexión",
"enable_music_cache": "Activar Caché de Música", "enable_music_cache": "Activar Caché de Música",
"clear_music_cache": "Borrar Caché de Música", "clear_music_cache": "Borrar Caché de Música",
"music_cache_size": "Caché {{Tamaño}}", "music_cache_size": "Caché {{Tamaño}}",
@@ -504,10 +504,10 @@
"delete": "Borrar", "delete": "Borrar",
"ok": "Aceptar", "ok": "Aceptar",
"remove": "Eliminar", "remove": "Eliminar",
"next": "Next", "next": "Siguiente",
"back": "Back", "back": "Atrás",
"continue": "Continue", "continue": "Continuar",
"verifying": "Verifying..." "verifying": "Verificando..."
}, },
"search": { "search": {
"search": "Buscar...", "search": "Buscar...",
@@ -610,6 +610,12 @@
"downloaded_file_no": "No", "downloaded_file_no": "No",
"downloaded_file_cancel": "Cancelar" "downloaded_file_cancel": "Cancelar"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "A continuación", "next_up": "A continuación",
"no_items_to_display": "No hay ítems para mostrar", "no_items_to_display": "No hay ítems para mostrar",
@@ -753,8 +759,8 @@
"downloaded": "Descargado", "downloaded": "Descargado",
"downloading": "Descargando...", "downloading": "Descargando...",
"cached": "En caché", "cached": "En caché",
"delete_download": "Delete Download", "delete_download": "Eliminar descarga",
"delete_cache": "Remove from Cache", "delete_cache": "Borrar del caché",
"go_to_artist": "Ir al artista", "go_to_artist": "Ir al artista",
"go_to_album": "Ir al álbum", "go_to_album": "Ir al álbum",
"add_to_favorites": "Añadir a Favoritos", "add_to_favorites": "Añadir a Favoritos",

View File

@@ -610,6 +610,12 @@
"downloaded_file_no": "Ei", "downloaded_file_no": "Ei",
"downloaded_file_cancel": "Peruuta" "downloaded_file_cancel": "Peruuta"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "Seuraavaksi", "next_up": "Seuraavaksi",
"no_items_to_display": "Ei kohteita näytettäväksi", "no_items_to_display": "Ei kohteita näytettäväksi",

View File

@@ -305,8 +305,8 @@
"select_liraries_you_want_to_hide": "Sélectionnez les bibliothèques que vous souhaitez masquer dans l'onglet Bibliothèque et les sections de la page d'accueil.", "select_liraries_you_want_to_hide": "Sélectionnez les bibliothèques que vous souhaitez masquer dans l'onglet Bibliothèque et les sections de la page d'accueil.",
"disable_haptic_feedback": "Désactiver le retour haptique", "disable_haptic_feedback": "Désactiver le retour haptique",
"default_quality": "Qualité par défaut", "default_quality": "Qualité par défaut",
"default_playback_speed": "Default Playback Speed", "default_playback_speed": "Vitesse de lecture par défaut",
"auto_play_next_episode": "Auto-play Next Episode", "auto_play_next_episode": "Lecture automatique de l'épisode suivant",
"max_auto_play_episode_count": "Nombre d'épisodes en lecture automatique max", "max_auto_play_episode_count": "Nombre d'épisodes en lecture automatique max",
"disabled": "Désactivé" "disabled": "Désactivé"
}, },
@@ -314,15 +314,15 @@
"downloads_title": "Téléchargements" "downloads_title": "Téléchargements"
}, },
"music": { "music": {
"title": "Music", "title": "Musique",
"playback_title": "Playback", "playback_title": "Lecture",
"playback_description": "Configure how music is played.", "playback_description": "Configurer le mode de lecture de la musique.",
"prefer_downloaded": "Prefer Downloaded Songs", "prefer_downloaded": "Supprimer toutes les musiques téléchargées",
"caching_title": "Caching", "caching_title": "Mise en cache",
"caching_description": "Automatically cache upcoming tracks for smoother playback.", "caching_description": "Mettre automatiquement en cache les pistes à venir pour une lecture plus fluide.",
"lookahead_enabled": "Enable Look-Ahead Caching", "lookahead_enabled": "Activer la mise en cache guidée",
"lookahead_count": "Tracks to Pre-cache", "lookahead_count": "Pistes à pré-mettre en cache",
"max_cache_size": "Max Cache Size" "max_cache_size": "Taille max de cache"
}, },
"plugins": { "plugins": {
"plugins_title": "Plugins", "plugins_title": "Plugins",
@@ -357,19 +357,19 @@
"save_button": "Enregistrer", "save_button": "Enregistrer",
"toasts": { "toasts": {
"saved": "Enregistré", "saved": "Enregistré",
"refreshed": "Settings refreshed from server" "refreshed": "Paramètres actualisés depuis le serveur"
}, },
"refresh_from_server": "Refresh Settings from Server" "refresh_from_server": "Rafraîchir les paramètres depuis le serveur"
}, },
"streamystats": { "streamystats": {
"enable_streamystats": "Enable Streamystats", "enable_streamystats": "Activer Streamystats",
"disable_streamystats": "Disable Streamystats", "disable_streamystats": "Désactiver Streamystats",
"enable_search": "Use for Search", "enable_search": "Utiliser pour la recherche",
"url": "URL", "url": "URL",
"server_url_placeholder": "http(s)://streamystats.example.com", "server_url_placeholder": "http(s)://streamystats.example.com",
"streamystats_search_hint": "Enter the URL for your Streamystats server. The URL should include http or https and optionally the port.", "streamystats_search_hint": "Entrez l'URL de votre serveur Streamystats. L'URL doit inclure http ou https et éventuellement le port.",
"read_more_about_streamystats": "Read More About Streamystats.", "read_more_about_streamystats": "En savoir plus sur Streamystats.",
"save_button": "Save", "save_button": "Enregistrer",
"save": "Enregistrer", "save": "Enregistrer",
"features_title": "Fonctionnalités", "features_title": "Fonctionnalités",
"home_sections_title": "Sections de la page d´accueil", "home_sections_title": "Sections de la page d´accueil",
@@ -572,7 +572,7 @@
"genres": "Genres", "genres": "Genres",
"years": "Années", "years": "Années",
"sort_by": "Trier par", "sort_by": "Trier par",
"filter_by": "Filter By", "filter_by": "Filtrer par",
"sort_order": "Ordre de tri", "sort_order": "Ordre de tri",
"tags": "Tags" "tags": "Tags"
} }
@@ -610,6 +610,12 @@
"downloaded_file_no": "Non", "downloaded_file_no": "Non",
"downloaded_file_cancel": "Annuler" "downloaded_file_cancel": "Annuler"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "À suivre", "next_up": "À suivre",
"no_items_to_display": "Aucuns médias à afficher", "no_items_to_display": "Aucuns médias à afficher",
@@ -719,127 +725,127 @@
"favorites": "Favoris" "favorites": "Favoris"
}, },
"music": { "music": {
"title": "Music", "title": "Musique",
"tabs": { "tabs": {
"suggestions": "Suggestions", "suggestions": "Suggestions",
"albums": "Albums", "albums": "Albums",
"artists": "Artists", "artists": "Artistes",
"playlists": "Playlists", "playlists": "Playlists",
"tracks": "tracks" "tracks": "morceaux"
}, },
"filters": { "filters": {
"all": "All" "all": "Toutes"
}, },
"recently_added": "Recently Added", "recently_added": "Ajoutés récemment",
"recently_played": "Recently Played", "recently_played": "Récemment joué",
"frequently_played": "Frequently Played", "frequently_played": "Fréquemment joué",
"explore": "Explore", "explore": "Explorez",
"top_tracks": "Top Tracks", "top_tracks": "Top chansons",
"play": "Play", "play": "Lecture",
"shuffle": "Shuffle", "shuffle": "Aléatoire",
"play_top_tracks": "Play Top Tracks", "play_top_tracks": "Jouer les pistes les plus populaires",
"no_suggestions": "No suggestions available", "no_suggestions": "Pas de suggestion disponible",
"no_albums": "No albums found", "no_albums": "Pas d'albums trouvés",
"no_artists": "No artists found", "no_artists": "Pas d'artistes trouvé",
"no_playlists": "No playlists found", "no_playlists": "Pas de playlists trouvées",
"album_not_found": "Album not found", "album_not_found": "Album introuvable",
"artist_not_found": "Artist not found", "artist_not_found": "Artiste introuvable",
"playlist_not_found": "Playlist not found", "playlist_not_found": "Playlist introuvable",
"track_options": { "track_options": {
"play_next": "Play Next", "play_next": "Lecture suivante",
"add_to_queue": "Add to Queue", "add_to_queue": "Ajouter à la file d'attente",
"add_to_playlist": "Add to Playlist", "add_to_playlist": "Ajouter à la playlist",
"download": "Download", "download": "Télécharger",
"downloaded": "Downloaded", "downloaded": "Téléchargé",
"downloading": "Downloading...", "downloading": "Téléchargement en cours...",
"cached": "Cached", "cached": "En cache",
"delete_download": "Delete Download", "delete_download": "Supprimer un téléchargement",
"delete_cache": "Remove from Cache", "delete_cache": "Supprimer du cache",
"go_to_artist": "Go to Artist", "go_to_artist": "Voir l'artiste",
"go_to_album": "Go to Album", "go_to_album": "Aller à lalbum",
"add_to_favorites": "Add to Favorites", "add_to_favorites": "Ajouter aux favoris",
"remove_from_favorites": "Remove from Favorites", "remove_from_favorites": "Retirer des favoris",
"remove_from_playlist": "Remove from Playlist" "remove_from_playlist": "Retirer de la playlist"
}, },
"playlists": { "playlists": {
"create_playlist": "Create Playlist", "create_playlist": "Créer une Playlist",
"playlist_name": "Playlist Name", "playlist_name": "Nom de la Playlist",
"enter_name": "Enter playlist name", "enter_name": "Entrer le nom de la playlist",
"create": "Create", "create": "Créer",
"search_playlists": "Search playlists...", "search_playlists": "Rechercher des playlists...",
"added_to": "Added to {{name}}", "added_to": "Ajouté à {{name}}",
"added": "Added to playlist", "added": "Ajouté à la playlist",
"removed_from": "Removed from {{name}}", "removed_from": "Retiré de {{name}}",
"removed": "Removed from playlist", "removed": "Retiré de la playlist",
"created": "Playlist created", "created": "Playlist créée",
"create_new": "Create New Playlist", "create_new": "Créer une nouvelle playlist",
"failed_to_add": "Failed to add to playlist", "failed_to_add": "Échec de l'ajout à la playlist",
"failed_to_remove": "Failed to remove from playlist", "failed_to_remove": "Échec de la suppression de la playlist",
"failed_to_create": "Failed to create playlist", "failed_to_create": "Échec de la suppression de la playlist",
"delete_playlist": "Delete Playlist", "delete_playlist": "Supprimer la playlist",
"delete_confirm": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.", "delete_confirm": "Êtes-vous sûr de vouloir supprimer « {{ name }} » ? Cette action est irréversible.",
"deleted": "Playlist deleted", "deleted": "Playlist supprimée",
"failed_to_delete": "Failed to delete playlist" "failed_to_delete": "Échec de la suppression de la playlist"
}, },
"sort": { "sort": {
"title": "Sort By", "title": "Trier par",
"alphabetical": "Alphabetical", "alphabetical": "Ordre alphabétique",
"date_created": "Date Created" "date_created": "Date de création"
} }
}, },
"watchlists": { "watchlists": {
"title": "Watchlists", "title": "Listes de lecture",
"my_watchlists": "My Watchlists", "my_watchlists": "Mes listes de lecture",
"public_watchlists": "Public Watchlists", "public_watchlists": "Watchlist publique",
"create_title": "Create Watchlist", "create_title": "Créer une Watchlist",
"edit_title": "Edit Watchlist", "edit_title": "Modifier la Watchlist",
"create_button": "Create Watchlist", "create_button": "Créer une Watchlist",
"save_button": "Save Changes", "save_button": "Enregistrer les modifications",
"delete_button": "Delete", "delete_button": "Supprimer",
"remove_button": "Remove", "remove_button": "Retirer",
"cancel_button": "Cancel", "cancel_button": "Annuler",
"name_label": "Name", "name_label": "Nom",
"name_placeholder": "Enter watchlist name", "name_placeholder": "Entrer le nom de la playlist",
"description_label": "Description", "description_label": "Description",
"description_placeholder": "Enter description (optional)", "description_placeholder": "Entrez la description (facultatif)",
"is_public_label": "Public Watchlist", "is_public_label": "Liste de lecture Publique",
"is_public_description": "Allow others to view this watchlist", "is_public_description": "Autoriser d'autres personnes à voir cette liste de suivi",
"allowed_type_label": "Content Type", "allowed_type_label": "Type de contenu",
"sort_order_label": "Default Sort Order", "sort_order_label": "Ordre de tri par défaut",
"empty_title": "No Watchlists", "empty_title": "Pas de Watchlists",
"empty_description": "Create your first watchlist to start organizing your media", "empty_description": "Créez votre première liste de suivi pour commencer à organiser vos médias",
"empty_watchlist": "This watchlist is empty", "empty_watchlist": "Cette liste de suivi est vide",
"empty_watchlist_hint": "Add items from your library to this watchlist", "empty_watchlist_hint": "Ajouter des éléments de votre bibliothèque à cette liste de suivi",
"not_configured_title": "Streamystats Not Configured", "not_configured_title": "Streamystats non configuré",
"not_configured_description": "Configure Streamystats in settings to use watchlists", "not_configured_description": "Configurer Streamystats dans les paramètres pour utiliser les listes de suivi",
"go_to_settings": "Go to Settings", "go_to_settings": "Accédez aux Paramètres",
"add_to_watchlist": "Add to Watchlist", "add_to_watchlist": "Ajouter à la Watchlist",
"remove_from_watchlist": "Remove from Watchlist", "remove_from_watchlist": "Retirer de la Watchlist",
"select_watchlist": "Select Watchlist", "select_watchlist": "Sélectionner la liste de suivi",
"create_new": "Create New Watchlist", "create_new": "Créer une Watchlist",
"item": "item", "item": "médias",
"items": "items", "items": "élément",
"public": "Public", "public": "Publique",
"private": "Private", "private": "Privée",
"you": "You", "you": "Vous-même",
"by_owner": "By another user", "by_owner": "Par un autre utilisateur",
"not_found": "Watchlist not found", "not_found": "Playlist introuvable",
"delete_confirm_title": "Delete Watchlist", "delete_confirm_title": "Supprimer la Watchlist",
"delete_confirm_message": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.", "delete_confirm_message": "Tous les médias (par défaut)",
"remove_item_title": "Remove from Watchlist", "remove_item_title": "Retirer de la Watchlist",
"remove_item_message": "Remove \"{{name}}\" from this watchlist?", "remove_item_message": "Retirer «{{name}}» de cette liste de suivi?",
"loading": "Loading watchlists...", "loading": "Chargement des listes de suivi...",
"no_compatible_watchlists": "No compatible watchlists", "no_compatible_watchlists": "Aucune liste de suivi compatible",
"create_one_first": "Create a watchlist that accepts this content type" "create_one_first": "Créer une liste de suivi qui accepte ce type de contenu"
}, },
"playback_speed": { "playback_speed": {
"title": "Playback Speed", "title": "Vitesse de lecture",
"apply_to": "Apply To", "apply_to": "Appliquer à",
"speed": "Speed", "speed": "Vitesse",
"scope": { "scope": {
"media": "This media only", "media": "Ce média uniquement",
"show": "This show", "show": "Cette série",
"all": "All media (default)" "all": "Tous les médias (par défaut)"
} }
} }
} }

View File

@@ -39,7 +39,7 @@
"please_login_again": "Your saved session has expired. Please log in again.", "please_login_again": "Your saved session has expired. Please log in again.",
"remove_saved_login": "Remove Saved Login", "remove_saved_login": "Remove Saved Login",
"remove_saved_login_description": "This will remove your saved credentials for this server. You'll need to enter your username and password again next time.", "remove_saved_login_description": "This will remove your saved credentials for this server. You'll need to enter your username and password again next time.",
"accounts_count": "{{count}} accounts", "accounts_count": "{{count}} חשבונות",
"select_account": "Select Account", "select_account": "Select Account",
"add_account": "Add Account", "add_account": "Add Account",
"remove_account_description": "This will remove the saved credentials for {{username}}." "remove_account_description": "This will remove the saved credentials for {{username}}."
@@ -110,16 +110,16 @@
"settings_title": "הגדרות", "settings_title": "הגדרות",
"log_out_button": "התנתק", "log_out_button": "התנתק",
"categories": { "categories": {
"title": "Categories" "title": "קטגוריות"
}, },
"playback_controls": { "playback_controls": {
"title": "Playback & Controls" "title": "Playback & Controls"
}, },
"audio_subtitles": { "audio_subtitles": {
"title": "Audio & Subtitles" "title": "שמע וכתוביות"
}, },
"appearance": { "appearance": {
"title": "Appearance", "title": "מראה",
"merge_next_up_continue_watching": "Merge Continue Watching & Next Up", "merge_next_up_continue_watching": "Merge Continue Watching & Next Up",
"hide_remote_session_button": "Hide Remote Session Button" "hide_remote_session_button": "Hide Remote Session Button"
}, },
@@ -188,7 +188,7 @@
"hide_brightness_slider_description": "Hide the brightness slider in the video player" "hide_brightness_slider_description": "Hide the brightness slider in the video player"
}, },
"audio": { "audio": {
"audio_title": "אודיו", "audio_title": "שמע",
"set_audio_track": "בחר רצועת שמע מהפריט הקודם", "set_audio_track": "בחר רצועת שמע מהפריט הקודם",
"audio_language": "שפת שמע", "audio_language": "שפת שמע",
"audio_hint": "בחר שפת שמע אוטומטית.", "audio_hint": "בחר שפת שמע אוטומטית.",
@@ -271,8 +271,8 @@
"margin": "Bottom Margin" "margin": "Bottom Margin"
}, },
"video_player": { "video_player": {
"title": "Video Player", "title": "נגן וידאו",
"video_player": "Video Player", "video_player": "נגן וידאו",
"video_player_description": "Choose which video player to use on iOS.", "video_player_description": "Choose which video player to use on iOS.",
"ksplayer": "KSPlayer", "ksplayer": "KSPlayer",
"vlc": "VLC" "vlc": "VLC"
@@ -314,7 +314,7 @@
"downloads_title": "הורדות" "downloads_title": "הורדות"
}, },
"music": { "music": {
"title": "Music", "title": "מוזיקה",
"playback_title": "Playback", "playback_title": "Playback",
"playback_description": "Configure how music is played.", "playback_description": "Configure how music is played.",
"prefer_downloaded": "Prefer Downloaded Songs", "prefer_downloaded": "Prefer Downloaded Songs",
@@ -409,7 +409,7 @@
"downloaded_songs_deleted": "Downloaded songs deleted" "downloaded_songs_deleted": "Downloaded songs deleted"
}, },
"intro": { "intro": {
"title": "Intro", "title": "הקדמה",
"show_intro": "הצג פתיח", "show_intro": "הצג פתיח",
"reset_intro": "אפס פתיח" "reset_intro": "אפס פתיח"
}, },
@@ -495,7 +495,7 @@
"select": "בחר", "select": "בחר",
"no_trailer_available": "אין טריילר זמין", "no_trailer_available": "אין טריילר זמין",
"video": "וידאו", "video": "וידאו",
"audio": "אודיו", "audio": "שמע",
"subtitle": "כתובית", "subtitle": "כתובית",
"play": "נגן", "play": "נגן",
"none": "ללא", "none": "ללא",
@@ -521,9 +521,9 @@
"episodes": "פרקים", "episodes": "פרקים",
"collections": "אוספים", "collections": "אוספים",
"actors": "שחקנים", "actors": "שחקנים",
"artists": "Artists", "artists": "אומנים",
"albums": "Albums", "albums": "אלבומים",
"songs": "Songs", "songs": "שירים",
"playlists": "Playlists", "playlists": "Playlists",
"request_movies": "סרטים מבוקשים", "request_movies": "סרטים מבוקשים",
"request_series": "סדרות מבוקשים", "request_series": "סדרות מבוקשים",
@@ -606,10 +606,16 @@
"go_back": "חזור", "go_back": "חזור",
"downloaded_file_title": "You have this file downloaded", "downloaded_file_title": "You have this file downloaded",
"downloaded_file_message": "Do you want to play the downloaded file?", "downloaded_file_message": "Do you want to play the downloaded file?",
"downloaded_file_yes": "Yes", "downloaded_file_yes": "כן",
"downloaded_file_no": "No", "downloaded_file_no": "לא",
"downloaded_file_cancel": "Cancel" "downloaded_file_cancel": "Cancel"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "הבא בתור", "next_up": "הבא בתור",
"no_items_to_display": "אין פריטים להציג", "no_items_to_display": "אין פריטים להציג",
@@ -626,7 +632,7 @@
"more_details": "פרטים נוספים", "more_details": "פרטים נוספים",
"media_options": "Media Options", "media_options": "Media Options",
"quality": "איכות", "quality": "איכות",
"audio": "אודיו", "audio": "שמע",
"subtitles": "כתובית", "subtitles": "כתובית",
"show_more": "הצג עוד", "show_more": "הצג עוד",
"show_less": "הצג פחות", "show_less": "הצג פחות",
@@ -719,10 +725,10 @@
"favorites": "מועדפים" "favorites": "מועדפים"
}, },
"music": { "music": {
"title": "Music", "title": "מוזיקה",
"tabs": { "tabs": {
"suggestions": "Suggestions", "suggestions": "Suggestions",
"albums": "Albums", "albums": "אלבומים",
"artists": "Artists", "artists": "Artists",
"playlists": "Playlists", "playlists": "Playlists",
"tracks": "tracks" "tracks": "tracks"
@@ -798,9 +804,9 @@
"delete_button": "Delete", "delete_button": "Delete",
"remove_button": "Remove", "remove_button": "Remove",
"cancel_button": "Cancel", "cancel_button": "Cancel",
"name_label": "Name", "name_label": "שם",
"name_placeholder": "Enter watchlist name", "name_placeholder": "Enter watchlist name",
"description_label": "Description", "description_label": "תיאור",
"description_placeholder": "Enter description (optional)", "description_placeholder": "Enter description (optional)",
"is_public_label": "Public Watchlist", "is_public_label": "Public Watchlist",
"is_public_description": "Allow others to view this watchlist", "is_public_description": "Allow others to view this watchlist",
@@ -817,10 +823,10 @@
"remove_from_watchlist": "Remove from Watchlist", "remove_from_watchlist": "Remove from Watchlist",
"select_watchlist": "Select Watchlist", "select_watchlist": "Select Watchlist",
"create_new": "Create New Watchlist", "create_new": "Create New Watchlist",
"item": "item", "item": "פריט",
"items": "items", "items": "פריטים",
"public": "Public", "public": "ציבורי",
"private": "Private", "private": "פרטי",
"you": "You", "you": "You",
"by_owner": "By another user", "by_owner": "By another user",
"not_found": "Watchlist not found", "not_found": "Watchlist not found",
@@ -835,7 +841,7 @@
"playback_speed": { "playback_speed": {
"title": "Playback Speed", "title": "Playback Speed",
"apply_to": "Apply To", "apply_to": "Apply To",
"speed": "Speed", "speed": "מהירות",
"scope": { "scope": {
"media": "This media only", "media": "This media only",
"show": "This show", "show": "This show",

View File

@@ -610,6 +610,12 @@
"downloaded_file_no": "No", "downloaded_file_no": "No",
"downloaded_file_cancel": "Cancel" "downloaded_file_cancel": "Cancel"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "Következő", "next_up": "Következő",
"no_items_to_display": "Nincs Megjeleníthető Elem", "no_items_to_display": "Nincs Megjeleníthető Elem",

View File

@@ -34,9 +34,9 @@
"search_for_local_servers": "Ricerca dei server locali", "search_for_local_servers": "Ricerca dei server locali",
"searching": "Cercando...", "searching": "Cercando...",
"servers": "Server", "servers": "Server",
"saved": "Saved", "saved": "Salvato",
"session_expired": "Session Expired", "session_expired": "Session Expired",
"please_login_again": "Your saved session has expired. Please log in again.", "please_login_again": "La tua sessione è scaduta. Si prega di eseguire nuovamente l'accesso.",
"remove_saved_login": "Remove Saved Login", "remove_saved_login": "Remove Saved Login",
"remove_saved_login_description": "This will remove your saved credentials for this server. You'll need to enter your username and password again next time.", "remove_saved_login_description": "This will remove your saved credentials for this server. You'll need to enter your username and password again next time.",
"accounts_count": "{{count}} accounts", "accounts_count": "{{count}} accounts",
@@ -125,7 +125,7 @@
}, },
"network": { "network": {
"title": "Network", "title": "Network",
"local_network": "Local Network", "local_network": "",
"auto_switch_enabled": "Auto-switch when at home", "auto_switch_enabled": "Auto-switch when at home",
"auto_switch_description": "Automatically switch to local URL when connected to home WiFi", "auto_switch_description": "Automatically switch to local URL when connected to home WiFi",
"local_url": "Local URL", "local_url": "Local URL",
@@ -136,8 +136,8 @@
"not_connected_to_wifi": "Not connected to WiFi", "not_connected_to_wifi": "Not connected to WiFi",
"no_networks_configured": "No networks configured", "no_networks_configured": "No networks configured",
"add_network_hint": "Add your home WiFi network to enable auto-switching", "add_network_hint": "Add your home WiFi network to enable auto-switching",
"current_wifi": "Current WiFi", "current_wifi": "WiFi Attuale",
"using_url": "Using", "using_url": "Sta utilizzando",
"local": "Local URL", "local": "Local URL",
"remote": "Remote URL", "remote": "Remote URL",
"not_connected": "Not connected", "not_connected": "Not connected",
@@ -610,6 +610,12 @@
"downloaded_file_no": "No", "downloaded_file_no": "No",
"downloaded_file_cancel": "Cancel" "downloaded_file_cancel": "Cancel"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "Il prossimo", "next_up": "Il prossimo",
"no_items_to_display": "Nessun elemento da visualizzare", "no_items_to_display": "Nessun elemento da visualizzare",

View File

@@ -610,6 +610,12 @@
"downloaded_file_no": "No", "downloaded_file_no": "No",
"downloaded_file_cancel": "Cancel" "downloaded_file_cancel": "Cancel"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "次", "next_up": "次",
"no_items_to_display": "表示するアイテムがありません", "no_items_to_display": "表示するアイテムがありません",

View File

@@ -1,187 +1,187 @@
{ {
"login": { "login": {
"username_required": "Username Is Required", "username_required": "사용자 이름이 필요합니다",
"error_title": "Error", "error_title": "오류",
"login_title": "Log In", "login_title": "로그인",
"login_to_title": "Log in to", "login_to_title": "다음 서비스에 연결 중",
"username_placeholder": "Username", "username_placeholder": "사용자 이름",
"password_placeholder": "Password", "password_placeholder": "비밀번호",
"login_button": "Log In", "login_button": "로그인",
"quick_connect": "Quick Connect", "quick_connect": "퀵 커넥트",
"enter_code_to_login": "Enter code {{code}} to login", "enter_code_to_login": "로그인 하기 위해 코드{{code}}를 입력하세요",
"failed_to_initiate_quick_connect": "Failed to initiate Quick Connect", "failed_to_initiate_quick_connect": "Quick Connect 연결을 시작하는 데 실패했습니다",
"got_it": "Got It", "got_it": "성공",
"connection_failed": "Connection Failed", "connection_failed": "연결 실패",
"could_not_connect_to_server": "Could not connect to the server. Please check the URL and your network connection.", "could_not_connect_to_server": "서버에 연결되지 않았습니다. URL과 네트워크 상태를 확인하세요.",
"an_unexpected_error_occured": "An Unexpected Error Occurred", "an_unexpected_error_occured": "예기치 않은 오류가 발생했습니다",
"change_server": "Change Server", "change_server": "서버 변경",
"invalid_username_or_password": "Invalid Username or Password", "invalid_username_or_password": "잘못된 아이디 혹은 비밀번호입니다",
"user_does_not_have_permission_to_log_in": "User does not have permission to log in", "user_does_not_have_permission_to_log_in": "로그인 하기 위한 권한이 없습니다",
"server_is_taking_too_long_to_respond_try_again_later": "Server is taking too long to respond, try again later", "server_is_taking_too_long_to_respond_try_again_later": "서버 응답이 너무 느립니다. 나중에 다시 시도하세요",
"server_received_too_many_requests_try_again_later": "Server received too many requests, try again later.", "server_received_too_many_requests_try_again_later": "서버가 너무 많은 요청을 받았습니다. 나중에 다시 시도하세요.",
"there_is_a_server_error": "There is a server error", "there_is_a_server_error": "서버 에러",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "An unexpected error occurred. Did you enter the server URL correctly?", "an_unexpected_error_occured_did_you_enter_the_correct_url": "예기치 않은 오류가 발생했습니다. 서버 URL을 올바르게 입력하셨습니까?",
"too_old_server_text": "Unsupported Jellyfin Server Discovered", "too_old_server_text": "Unsupported Jellyfin Server Discovered",
"too_old_server_description": "Please update Jellyfin to the latest version" "too_old_server_description": "Please update Jellyfin to the latest version"
}, },
"server": { "server": {
"enter_url_to_jellyfin_server": "Enter the URL to your Jellyfin server", "enter_url_to_jellyfin_server": "Enter the URL to your Jellyfin server",
"server_url_placeholder": "http(s)://your-server.com", "server_url_placeholder": "http(s)://your-server.com",
"connect_button": "Connect", "connect_button": "연결",
"previous_servers": "Previous Servers", "previous_servers": "이전 서버",
"clear_button": "Clear all", "clear_button": "모두 지우기",
"swipe_to_remove": "Swipe to remove", "swipe_to_remove": "스와이프해서 지우기",
"search_for_local_servers": "Search for Local Servers", "search_for_local_servers": "로컬 서버 찾기",
"searching": "Searching...", "searching": "찾는 중...",
"servers": "Servers", "servers": "서버",
"saved": "Saved", "saved": "저장됨",
"session_expired": "Session Expired", "session_expired": "세션 만료됨",
"please_login_again": "Your saved session has expired. Please log in again.", "please_login_again": "사용자 세션이 만료되었습니다. 다시 로그인하십시오.",
"remove_saved_login": "Remove Saved Login", "remove_saved_login": "저장된 로그인 정보 삭제",
"remove_saved_login_description": "This will remove your saved credentials for this server. You'll need to enter your username and password again next time.", "remove_saved_login_description": "해당 서버에 저장된 자격 증명이 삭제됩니다. 다음에 접속할 때는 사용자 이름과 비밀번호를 다시 입력해야 합니다.",
"accounts_count": "{{count}} accounts", "accounts_count": "{{count}} 계정",
"select_account": "Select Account", "select_account": "계정 선택",
"add_account": "Add Account", "add_account": "계정 추가",
"remove_account_description": "This will remove the saved credentials for {{username}}." "remove_account_description": "{{username}}에 저장된 자격 증명이 삭제됩니다."
}, },
"save_account": { "save_account": {
"title": "Save Account", "title": "계정 저장",
"save_for_later": "Save this account", "save_for_later": "이 계정 저장",
"security_option": "Security Option", "security_option": "보안 설정",
"no_protection": "No protection", "no_protection": "보안 없음",
"no_protection_desc": "Quick login without authentication", "no_protection_desc": "인증 없이 빠른 로그인",
"pin_code": "PIN code", "pin_code": "PIN 코드",
"pin_code_desc": "4-digit PIN required when switching", "pin_code_desc": "전환하려면 4자리 PIN 필요함",
"password": "Re-enter password", "password": "암호 확인",
"password_desc": "Password required when switching", "password_desc": "전환하려면 비밀번호 필요함",
"save_button": "Save", "save_button": "저장",
"cancel_button": "Cancel" "cancel_button": "취소"
}, },
"pin": { "pin": {
"enter_pin": "Enter PIN", "enter_pin": "PIN 입력",
"enter_pin_for": "Enter PIN for {{username}}", "enter_pin_for": "{{username}} PIN 입력",
"enter_4_digits": "Enter 4 digits", "enter_4_digits": "4자리 입력",
"invalid_pin": "Invalid PIN", "invalid_pin": "잘못된 PIN",
"setup_pin": "Set Up PIN", "setup_pin": "PIN 설정",
"confirm_pin": "Confirm PIN", "confirm_pin": "PIN 확인",
"pins_dont_match": "PINs don't match", "pins_dont_match": "PIN이 일치하지 않습니다",
"forgot_pin": "Forgot PIN?", "forgot_pin": "PIN을 잊으셨나요?",
"forgot_pin_desc": "Your saved credentials will be removed" "forgot_pin_desc": "저장된 계정 정보가 삭제됩니다"
}, },
"password": { "password": {
"enter_password": "Enter Password", "enter_password": "비밀번호 입력",
"enter_password_for": "Enter password for {{username}}", "enter_password_for": "{{username}}의 비밀번호 입력",
"invalid_password": "Invalid password" "invalid_password": "잘못된 비밀번호"
}, },
"home": { "home": {
"checking_server_connection": "Checking server connection...", "checking_server_connection": "서버 연결 체크중...",
"no_internet": "No Internet", "no_internet": "인터넷에 연결되지 않음",
"no_items": "No Items", "no_items": "항목 없음",
"no_internet_message": "No worries, you can still watch\ndownloaded content.", "no_internet_message": "걱정마세요. 다운로드 된 컨텐츠는 여전히 볼 수 있습니다.",
"checking_server_connection_message": "Checking connection to server", "checking_server_connection_message": "Checking connection to server",
"go_to_downloads": "Go to Downloads", "go_to_downloads": "Go to Downloads",
"retry": "Retry", "retry": "재시도",
"server_unreachable": "Server Unreachable", "server_unreachable": "서버에 연결할 수 없음",
"server_unreachable_message": "Could not reach the server.\nPlease check your network connection.", "server_unreachable_message": "서버에 연결할 수 없습니다. 네트워크 상태를 체크하세요.",
"oops": "Oops!", "oops": "이런!",
"error_message": "Something went wrong.\nPlease log out and in again.", "error_message": "문제가 발생했습니다.\n로그아웃 후 다시 로그인해 주세요.",
"continue_watching": "Continue Watching", "continue_watching": "이어서 보기",
"next_up": "Next Up", "next_up": "다음 시청",
"continue_and_next_up": "Continue & Next Up", "continue_and_next_up": "이어서 보기 & 다음 시청",
"recently_added_in": "Recently Added in {{libraryName}}", "recently_added_in": "최근에 추가된 {{libraryName}}",
"suggested_movies": "Suggested Movies", "suggested_movies": "추천 영화",
"suggested_episodes": "Suggested Episodes", "suggested_episodes": "추천 에피소드",
"intro": { "intro": {
"welcome_to_streamyfin": "Welcome to Streamyfin", "welcome_to_streamyfin": "스트리미핀에 오신 것을 환영합니다",
"a_free_and_open_source_client_for_jellyfin": "A Free and Open-Source Client for Jellyfin.", "a_free_and_open_source_client_for_jellyfin": "젤리핀을 위한 무료 오픈소스 클라이언트입니다.",
"features_title": "Features", "features_title": "기능",
"features_description": "Streamyfin has a bunch of features and integrates with a wide array of software which you can find in the settings menu, these include:", "features_description": "스트리미핀은 다양한 기능을 제공하며 설정 메뉴에서 확인할 수 있는 여러 소프트웨어와 통합됩니다. 이러한 소프트웨어에는 다음이 포함됩니다:",
"jellyseerr_feature_description": "Connect to your Seerr instance and request movies directly in the app.", "jellyseerr_feature_description": "Seerr 인스턴스에 연결하여 앱에서 직접 영화를 요청할 수 있습니다.",
"downloads_feature_title": "Downloads", "downloads_feature_title": "다운로드된 컨텐츠",
"downloads_feature_description": "Download movies and tv-shows to view offline. Use either the default method or install the optimize server to download files in the background.", "downloads_feature_description": "오프라인으로 보기위해 다운로드 하세요. 기본 다운로드 방식을 사용하거나, 백그라운드에서 파일을 다운로드하는 최적화 서버를 설치할 수 있습니다.",
"chromecast_feature_description": "Cast movies and tv-shows to your Chromecast devices.", "chromecast_feature_description": "영화와 TV 프로그램을 Chromecast 기기로 전송하기",
"centralised_settings_plugin_title": "Centralised Settings Plugin", "centralised_settings_plugin_title": "중앙 설정 플러그인",
"centralised_settings_plugin_description": "Configure settings from a centralised location on your Jellyfin server. All client settings for all users will be synced automatically.", "centralised_settings_plugin_description": "Jellyfin 서버의 중앙 집중식 위치에서 설정을 구성합니다. 모든 사용자의 모든 클라이언트 설정이 자동으로 동기화됩니다.",
"done_button": "Done", "done_button": "확인",
"go_to_settings_button": "Go to Settings", "go_to_settings_button": "설정으로 이동",
"read_more": "Read More" "read_more": "자세히 보기"
}, },
"settings": { "settings": {
"settings_title": "Settings", "settings_title": "설정",
"log_out_button": "Log Out", "log_out_button": "로그아웃",
"categories": { "categories": {
"title": "Categories" "title": "카테고리"
}, },
"playback_controls": { "playback_controls": {
"title": "Playback & Controls" "title": "재생 & 컨트롤"
}, },
"audio_subtitles": { "audio_subtitles": {
"title": "Audio & Subtitles" "title": "오디오 & 자막"
}, },
"appearance": { "appearance": {
"title": "Appearance", "title": "화면 스타일",
"merge_next_up_continue_watching": "Merge Continue Watching & Next Up", "merge_next_up_continue_watching": "[이어보기]와 [다음 보기] 합치기",
"hide_remote_session_button": "Hide Remote Session Button" "hide_remote_session_button": "원격 세션 버튼 숨기기"
}, },
"network": { "network": {
"title": "Network", "title": "네트워크",
"local_network": "Local Network", "local_network": "로컬 네트워크",
"auto_switch_enabled": "Auto-switch when at home", "auto_switch_enabled": "홈 네트워크 자동 전환",
"auto_switch_description": "Automatically switch to local URL when connected to home WiFi", "auto_switch_description": "홈 WiFi에 연결되었을 때 로컬 URL로 자동 전환",
"local_url": "Local URL", "local_url": "로컬 URL",
"local_url_hint": "Enter your local server address (e.g., http://192.168.1.100:8096)", "local_url_hint": "로컬 서버 주소를 입력하세요 (e.g., http://192.168.1.100:8096)",
"local_url_placeholder": "http://192.168.1.100:8096", "local_url_placeholder": "http://192.168.1.100:8096",
"home_wifi_networks": "Home WiFi Networks", "home_wifi_networks": " WiFi 네트워크",
"add_current_network": "Add \"{{ssid}}\"", "add_current_network": "\"{{ssid}}\" 추가",
"not_connected_to_wifi": "Not connected to WiFi", "not_connected_to_wifi": "WiFi에 연결되지 않음",
"no_networks_configured": "No networks configured", "no_networks_configured": "구성된 네트워크가 없습니다",
"add_network_hint": "Add your home WiFi network to enable auto-switching", "add_network_hint": "자동 전환을 위한 홈 WiFi 추가",
"current_wifi": "Current WiFi", "current_wifi": "현재 WiFi",
"using_url": "Using", "using_url": "사용중",
"local": "Local URL", "local": "로컬 URL",
"remote": "Remote URL", "remote": "원격 URL",
"not_connected": "Not connected", "not_connected": "연결되지 않았습니다",
"current_server": "Current Server", "current_server": "현재 서버",
"remote_url": "Remote URL", "remote_url": "원격 URL",
"active_url": "Active URL", "active_url": "현재 사용 중인 URL",
"not_configured": "Not configured", "not_configured": "설정되지 않음",
"network_added": "Network added", "network_added": "네트워크 추가됨",
"network_already_added": "Network already added", "network_already_added": "네트워크 이미 추가됨",
"no_wifi_connected": "Not connected to WiFi", "no_wifi_connected": "WiFi에 연결되지 않음",
"permission_denied": "Location permission denied", "permission_denied": "위치 권한이 거부되었습니다",
"permission_denied_explanation": "Location permission is required to detect WiFi network for auto-switching. Please enable it in Settings." "permission_denied_explanation": "자동 전환 Wi-Fi 네트워크를 감지하려면 위치 권한이 필요합니다. 설정에서 위치 권한을 활성화해 주세요."
}, },
"user_info": { "user_info": {
"user_info_title": "User Info", "user_info_title": "사용자 정보",
"user": "User", "user": "사용자",
"server": "Server", "server": "서버",
"token": "Token", "token": "토큰",
"app_version": "App Version" "app_version": "앱 버전"
}, },
"quick_connect": { "quick_connect": {
"quick_connect_title": "Quick Connect", "quick_connect_title": "퀵 커넥트",
"authorize_button": "Authorize Quick Connect", "authorize_button": "퀵 커넥트 승인",
"enter_the_quick_connect_code": "Enter the quick connect code...", "enter_the_quick_connect_code": "퀵 커넥트 코드 입력...",
"success": "Success", "success": "성공",
"quick_connect_autorized": "Quick Connect Authorized", "quick_connect_autorized": "퀵 커넥트 승인됨",
"error": "Error", "error": "오류",
"invalid_code": "Invalid Code", "invalid_code": "유효하지 않은 코드",
"authorize": "Authorize" "authorize": "승인"
}, },
"media_controls": { "media_controls": {
"media_controls_title": "Media Controls", "media_controls_title": "미디어 컨트롤",
"forward_skip_length": "Forward Skip Length", "forward_skip_length": "앞으로 건너뛸 시간",
"rewind_length": "Rewind Length", "rewind_length": "뒤로 되감을 시간",
"seconds_unit": "s" "seconds_unit": ""
}, },
"gesture_controls": { "gesture_controls": {
"gesture_controls_title": "Gesture Controls", "gesture_controls_title": "제스처 제어",
"horizontal_swipe_skip": "Horizontal Swipe to Skip", "horizontal_swipe_skip": "좌/우로 스와이프하여 건너뛰기",
"horizontal_swipe_skip_description": "Swipe left/right when controls are hidden to skip", "horizontal_swipe_skip_description": "컨트롤 숨김상태에서 좌/우로 스와이프하여 건너뛰기",
"left_side_brightness": "Left Side Brightness Control", "left_side_brightness": "왼쪽 영역 밝기 조정 컨트롤",
"left_side_brightness_description": "Swipe up/down on left side to adjust brightness", "left_side_brightness_description": "왼쪽 영역을 위/아래 스와이프하여 밝기 조절",
"right_side_volume": "Right Side Volume Control", "right_side_volume": "오른쪽 영역 볼륨 컨트롤",
"right_side_volume_description": "Swipe up/down on right side to adjust volume", "right_side_volume_description": "오른족 영역을 위/아래로 스와이프 하여 볼륨 조절",
"hide_volume_slider": "Hide Volume Slider", "hide_volume_slider": "Hide Volume Slider",
"hide_volume_slider_description": "Hide the volume slider in the video player", "hide_volume_slider_description": "Hide the volume slider in the video player",
"hide_brightness_slider": "Hide Brightness Slider", "hide_brightness_slider": "Hide Brightness Slider",
@@ -196,7 +196,7 @@
"language": "Language", "language": "Language",
"transcode_mode": { "transcode_mode": {
"title": "Audio Transcoding", "title": "Audio Transcoding",
"description": "Controls how surround audio (7.1, TrueHD, DTS-HD) is handled", "description": "서라운드 오디오(7.1, TrueHD, DTS-HD)를 어떻게 처리할지 설정합니다",
"auto": "Auto", "auto": "Auto",
"stereo": "Force Stereo", "stereo": "Force Stereo",
"5_1": "Allow 5.1", "5_1": "Allow 5.1",
@@ -228,52 +228,52 @@
"outline_opacity": "Outline Opacity", "outline_opacity": "Outline Opacity",
"bold_text": "Bold Text", "bold_text": "Bold Text",
"colors": { "colors": {
"Black": "Black", "Black": "검정색",
"Gray": "Gray", "Gray": "회색",
"Silver": "Silver", "Silver": "은색",
"White": "White", "White": "흰색",
"Maroon": "Maroon", "Maroon": "밤색",
"Red": "Red", "Red": "빨간색",
"Fuchsia": "Fuchsia", "Fuchsia": "분홍색",
"Yellow": "Yellow", "Yellow": "노란색",
"Olive": "Olive", "Olive": "올리브 색",
"Green": "Green", "Green": "녹색",
"Teal": "Teal", "Teal": "청록색",
"Lime": "Lime", "Lime": "라임색",
"Purple": "Purple", "Purple": "보라색",
"Navy": "Navy", "Navy": "남색",
"Blue": "Blue", "Blue": "파란색",
"Aqua": "Aqua" "Aqua": "아쿠아색"
}, },
"thickness": { "thickness": {
"None": "None", "None": "없음",
"Thin": "Thin", "Thin": "얇게",
"Normal": "Normal", "Normal": "보통",
"Thick": "Thick" "Thick": "굵게"
}, },
"subtitle_color": "Subtitle Color", "subtitle_color": "자막 색상",
"subtitle_background_color": "Background Color", "subtitle_background_color": "배경 색상",
"subtitle_font": "Subtitle Font", "subtitle_font": "자막 폰트",
"ksplayer_title": "KSPlayer Settings", "ksplayer_title": "KSPlayer 설정",
"hardware_decode": "Hardware Decoding", "hardware_decode": "하드웨어 디코딩",
"hardware_decode_description": "Use hardware acceleration for video decoding. Disable if you experience playback issues." "hardware_decode_description": "비디오 디코딩에 하드웨어 가속을 사용하십시오. 재생 문제가 발생하는 경우 비활성화하십시오."
}, },
"vlc_subtitles": { "vlc_subtitles": {
"title": "VLC Subtitle Settings", "title": "VLC 자막 설정",
"hint": "Customize subtitle appearance for VLC player. Changes take effect on next playback.", "hint": "VLC 플레이어의 자막 표시 방식을 설정하세요. 변경 사항은 다음 재생 시 적용됩니다.",
"text_color": "Text Color", "text_color": "글자색",
"background_color": "Background Color", "background_color": "배경 색상",
"background_opacity": "Background Opacity", "background_opacity": "배경 투명도",
"outline_color": "Outline Color", "outline_color": "외곽선 색상",
"outline_opacity": "Outline Opacity", "outline_opacity": "외곽선 투명도",
"outline_thickness": "Outline Thickness", "outline_thickness": "외곽선 굵기",
"bold": "Bold Text", "bold": "굵은 글씨",
"margin": "Bottom Margin" "margin": "아래쪽 여백"
}, },
"video_player": { "video_player": {
"title": "Video Player", "title": "비디오 플레이어",
"video_player": "Video Player", "video_player": "비디오 플레이어",
"video_player_description": "Choose which video player to use on iOS.", "video_player_description": "iOS 사용자는 비디오 플레이어를 선택하세요.",
"ksplayer": "KSPlayer", "ksplayer": "KSPlayer",
"vlc": "VLC" "vlc": "VLC"
}, },
@@ -288,20 +288,20 @@
"PORTRAIT_UP": "Portrait Up", "PORTRAIT_UP": "Portrait Up",
"PORTRAIT_DOWN": "Portrait Down", "PORTRAIT_DOWN": "Portrait Down",
"LANDSCAPE": "Landscape", "LANDSCAPE": "Landscape",
"LANDSCAPE_LEFT": "Landscape Left", "LANDSCAPE_LEFT": "왼쪽 가로 모드",
"LANDSCAPE_RIGHT": "Landscape Right", "LANDSCAPE_RIGHT": "오른쪽 가로 모드",
"OTHER": "Other", "OTHER": "Other",
"UNKNOWN": "Unknown" "UNKNOWN": "Unknown"
}, },
"safe_area_in_controls": "Safe Area in Controls", "safe_area_in_controls": "컨트롤 안전 영역",
"video_player": "Video Player", "video_player": "Video Player",
"video_players": { "video_players": {
"VLC_3": "VLC 3", "VLC_3": "VLC 3",
"VLC_4": "VLC 4 (Experimental + PiP)" "VLC_4": "VLC 4 (Experimental + PiP)"
}, },
"show_custom_menu_links": "Show Custom Menu Links", "show_custom_menu_links": "사용자 지정 메뉴 링크 표시",
"show_large_home_carousel": "Show Large Home Carousel (beta)", "show_large_home_carousel": "대형 홈 슬라이드 배너 표시 (베타)",
"hide_libraries": "Hide Libraries", "hide_libraries": "라이브러리 숨기기",
"select_liraries_you_want_to_hide": "Select the libraries you want to hide from the Library tab and home page sections.", "select_liraries_you_want_to_hide": "Select the libraries you want to hide from the Library tab and home page sections.",
"disable_haptic_feedback": "Disable Haptic Feedback", "disable_haptic_feedback": "Disable Haptic Feedback",
"default_quality": "Default Quality", "default_quality": "Default Quality",
@@ -334,24 +334,24 @@
"password": "Password", "password": "Password",
"password_placeholder": "Enter password for Jellyfin user {{username}}", "password_placeholder": "Enter password for Jellyfin user {{username}}",
"login_button": "Login", "login_button": "Login",
"total_media_requests": "Total Media Requests", "total_media_requests": "전체 미디어 요청 수",
"movie_quota_limit": "Movie Quota Limit", "movie_quota_limit": "영화 요청 한도",
"movie_quota_days": "Movie Quota Days", "movie_quota_days": "영화 요청 제한 기간",
"tv_quota_limit": "TV Quota Limit", "tv_quota_limit": "TV Quota Limit",
"tv_quota_days": "TV Quota Days", "tv_quota_days": "TV 요청 제한 기간",
"reset_jellyseerr_config_button": "Reset Seerr Config", "reset_jellyseerr_config_button": "Seerr 설정 초기화",
"unlimited": "Unlimited", "unlimited": "Unlimited",
"plus_n_more": "+{{n}} More", "plus_n_more": "+{{n}}개 더",
"order_by": { "order_by": {
"DEFAULT": "Default", "DEFAULT": "Default",
"VOTE_COUNT_AND_AVERAGE": "Vote count and average", "VOTE_COUNT_AND_AVERAGE": "평균 평점 및 투표 수",
"POPULARITY": "Popularity" "POPULARITY": "Popularity"
} }
}, },
"marlin_search": { "marlin_search": {
"enable_marlin_search": "Enable Marlin Search", "enable_marlin_search": "Marlin 검색 활성화",
"url": "URL", "url": "URL",
"server_url_placeholder": "http(s)://domain.org:port", "server_url_placeholder": "http(s)://도메인:포트",
"marlin_search_hint": "Enter the URL for the Marlin server. The URL should include http or https and optionally the port.", "marlin_search_hint": "Enter the URL for the Marlin server. The URL should include http or https and optionally the port.",
"read_more_about_marlin": "Read More About Marlin.", "read_more_about_marlin": "Read More About Marlin.",
"save_button": "Save", "save_button": "Save",
@@ -374,28 +374,28 @@
"features_title": "Features", "features_title": "Features",
"home_sections_title": "Home Sections", "home_sections_title": "Home Sections",
"enable_movie_recommendations": "Movie Recommendations", "enable_movie_recommendations": "Movie Recommendations",
"enable_series_recommendations": "Series Recommendations", "enable_series_recommendations": "시리즈 추천",
"enable_promoted_watchlists": "Promoted Watchlists", "enable_promoted_watchlists": "추천 관심 목록",
"hide_watchlists_tab": "Hide Watchlists Tab", "hide_watchlists_tab": "관심 목록 탭 숨기기",
"home_sections_hint": "Show personalized recommendations and promoted watchlists from Streamystats on the home page.", "home_sections_hint": "홈 페이지에서 Streamystats의 개인 맞춤 추천 및 추천 관심 목록을 표시합니다.",
"recommended_movies": "Recommended Movies", "recommended_movies": "Recommended Movies",
"recommended_series": "Recommended Series", "recommended_series": "추천 시리즈",
"toasts": { "toasts": {
"saved": "Saved", "saved": "Saved",
"refreshed": "Settings refreshed from server", "refreshed": "서버에서 설정을 새로고침했습니다",
"disabled": "Streamystats disabled" "disabled": "Streamystats 비활성화됨"
}, },
"refresh_from_server": "Refresh Settings from Server" "refresh_from_server": "서버에서 설정 새로고침"
}, },
"kefinTweaks": { "kefinTweaks": {
"watchlist_enabler": "Enable our Watchlist integration", "watchlist_enabler": "관심 목록 통합 기능 활성화",
"watchlist_button": "Toggle Watchlist integration" "watchlist_button": "관심 목록 연동 켜기/끄기"
} }
}, },
"storage": { "storage": {
"storage_title": "Storage", "storage_title": "Storage",
"app_usage": "App {{usedSpace}}%", "app_usage": " {{usedSpace}}",
"device_usage": "Device {{availableSpace}}%", "device_usage": "디바이스 {{availableSpace}}%",
"size_used": "{{used}} of {{total}} Used", "size_used": "{{used}} of {{total}} Used",
"delete_all_downloaded_files": "Delete All Downloaded Files", "delete_all_downloaded_files": "Delete All Downloaded Files",
"music_cache_title": "Music Cache", "music_cache_title": "Music Cache",
@@ -403,10 +403,10 @@
"enable_music_cache": "Enable Music Cache", "enable_music_cache": "Enable Music Cache",
"clear_music_cache": "Clear Music Cache", "clear_music_cache": "Clear Music Cache",
"music_cache_size": "{{size}} cached", "music_cache_size": "{{size}} cached",
"music_cache_cleared": "Music cache cleared", "music_cache_cleared": "음악 캐시가 삭제되었습니다",
"delete_all_downloaded_songs": "Delete All Downloaded Songs", "delete_all_downloaded_songs": "Delete All Downloaded Songs",
"downloaded_songs_size": "{{size}} downloaded", "downloaded_songs_size": "{{size}} downloaded",
"downloaded_songs_deleted": "Downloaded songs deleted" "downloaded_songs_deleted": "다운로드한 노래가 삭제되었습니다"
}, },
"intro": { "intro": {
"title": "Intro", "title": "Intro",
@@ -434,7 +434,7 @@
}, },
"sessions": { "sessions": {
"title": "Sessions", "title": "Sessions",
"no_active_sessions": "No Active Sessions" "no_active_sessions": "세션 비활성화"
}, },
"downloads": { "downloads": {
"downloads_title": "Downloads", "downloads_title": "Downloads",
@@ -610,6 +610,12 @@
"downloaded_file_no": "No", "downloaded_file_no": "No",
"downloaded_file_cancel": "Cancel" "downloaded_file_cancel": "Cancel"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "Next Up", "next_up": "Next Up",
"no_items_to_display": "No Items to Display", "no_items_to_display": "No Items to Display",

View File

@@ -30,7 +30,7 @@
"connect_button": "Verbinden", "connect_button": "Verbinden",
"previous_servers": "vorige servers", "previous_servers": "vorige servers",
"clear_button": "Wissen", "clear_button": "Wissen",
"swipe_to_remove": "Swipe to remove", "swipe_to_remove": "Swipe om te verwijderen.",
"search_for_local_servers": "Zoek naar lokale servers", "search_for_local_servers": "Zoek naar lokale servers",
"searching": "Zoeken...", "searching": "Zoeken...",
"servers": "Servers", "servers": "Servers",
@@ -40,38 +40,38 @@
"remove_saved_login": "Opgeslagen login verwijderen", "remove_saved_login": "Opgeslagen login verwijderen",
"remove_saved_login_description": "Hiermee worden uw opgeslagen gegevens voor deze server verwijderd. U moet uw gebruikersnaam en wachtwoord de volgende keer opnieuw invoeren.", "remove_saved_login_description": "Hiermee worden uw opgeslagen gegevens voor deze server verwijderd. U moet uw gebruikersnaam en wachtwoord de volgende keer opnieuw invoeren.",
"accounts_count": "{{count}} accounts", "accounts_count": "{{count}} accounts",
"select_account": "Select Account", "select_account": "Account selecteren",
"add_account": "Add Account", "add_account": "Account toevoegen",
"remove_account_description": "This will remove the saved credentials for {{username}}." "remove_account_description": "Hiermee worden de opgeslagen inloggegevens voor {{username}} verwijderd."
}, },
"save_account": { "save_account": {
"title": "Save Account", "title": "Account opslaan",
"save_for_later": "Save this account", "save_for_later": "Dit account opslaan",
"security_option": "Security Option", "security_option": "Beveiligingsopties",
"no_protection": "No protection", "no_protection": "Geen beveiliging",
"no_protection_desc": "Quick login without authentication", "no_protection_desc": "Snelle login zonder authenticatie",
"pin_code": "PIN code", "pin_code": "Pincode",
"pin_code_desc": "4-digit PIN required when switching", "pin_code_desc": "4-cijferige pincode vereist bij wisselen",
"password": "Re-enter password", "password": "Wachtwoord opnieuw invoeren",
"password_desc": "Password required when switching", "password_desc": "Wachtwoord vereist bij wisselen",
"save_button": "Save", "save_button": "Opslaan",
"cancel_button": "Cancel" "cancel_button": "Annuleren"
}, },
"pin": { "pin": {
"enter_pin": "Enter PIN", "enter_pin": "Pincode invoeren",
"enter_pin_for": "Enter PIN for {{username}}", "enter_pin_for": "Pincode voor {{username}} invoeren",
"enter_4_digits": "Enter 4 digits", "enter_4_digits": "Voer 6 cijfers in",
"invalid_pin": "Invalid PIN", "invalid_pin": "Ongeldige pincode",
"setup_pin": "Set Up PIN", "setup_pin": "Pincode instellen",
"confirm_pin": "Confirm PIN", "confirm_pin": "Pincode bevestigen",
"pins_dont_match": "PINs don't match", "pins_dont_match": "Pincodes komen niet overeen",
"forgot_pin": "Forgot PIN?", "forgot_pin": "Pincode vergeten?",
"forgot_pin_desc": "Your saved credentials will be removed" "forgot_pin_desc": "Je opgeslagen inloggegevens worden verwijderd"
}, },
"password": { "password": {
"enter_password": "Enter Password", "enter_password": "Voer wachtwoord in",
"enter_password_for": "Enter password for {{username}}", "enter_password_for": "Voer wachtwoord voor {{username}} in",
"invalid_password": "Invalid password" "invalid_password": "Ongeldig wachtwoord"
}, },
"home": { "home": {
"checking_server_connection": "Serververbinding controleren...", "checking_server_connection": "Serververbinding controleren...",
@@ -84,7 +84,7 @@
"server_unreachable": "Server onbereikbaar", "server_unreachable": "Server onbereikbaar",
"server_unreachable_message": "Kon de server niet bereiken.\nControleer uw netwerkverbinding.", "server_unreachable_message": "Kon de server niet bereiken.\nControleer uw netwerkverbinding.",
"oops": "Oeps!", "oops": "Oeps!",
"error_message": "Er ging iets fout\nGelieve af en aan te melden.", "error_message": "Er ging iets fout\nProbeer opnieuw in te loggen.",
"continue_watching": "Verder Kijken", "continue_watching": "Verder Kijken",
"next_up": "Volgende", "next_up": "Volgende",
"continue_and_next_up": "Doorgaan & Volgende", "continue_and_next_up": "Doorgaan & Volgende",
@@ -124,32 +124,32 @@
"hide_remote_session_button": "Verberg Knop voor Externe Sessie" "hide_remote_session_button": "Verberg Knop voor Externe Sessie"
}, },
"network": { "network": {
"title": "Network", "title": "Netwerk",
"local_network": "Local Network", "local_network": "Lokaal netwerk",
"auto_switch_enabled": "Auto-switch when at home", "auto_switch_enabled": "Automatisch wisselen wanneer thuis",
"auto_switch_description": "Automatically switch to local URL when connected to home WiFi", "auto_switch_description": "Automatisch wisselen naar lokale URL wanneer verbonden met thuisnetwerk",
"local_url": "Local URL", "local_url": "Lokale URL",
"local_url_hint": "Enter your local server address (e.g., http://192.168.1.100:8096)", "local_url_hint": "Voer uw lokale serveradres in (bijv. http://192.168.1.100:8096)",
"local_url_placeholder": "http://192.168.1.100:8096", "local_url_placeholder": "http://192.168.1.100:8096",
"home_wifi_networks": "Home WiFi Networks", "home_wifi_networks": "Wi-Fi netwerken",
"add_current_network": "Add \"{{ssid}}\"", "add_current_network": "Voeg \"{{ssid}} \" toe",
"not_connected_to_wifi": "Not connected to WiFi", "not_connected_to_wifi": "Niet verbonden met Wi-Fi",
"no_networks_configured": "No networks configured", "no_networks_configured": "Geen netwerken geconfigureerd",
"add_network_hint": "Add your home WiFi network to enable auto-switching", "add_network_hint": "Voeg je thuisnetwerk toe om automatisch wisselen in te schakelen",
"current_wifi": "Current WiFi", "current_wifi": "Huidige Wi-Fi",
"using_url": "Using", "using_url": "Gebruik makend van",
"local": "Local URL", "local": "Lokale URL",
"remote": "Remote URL", "remote": "Externe URL",
"not_connected": "Not connected", "not_connected": "Niet verbonden",
"current_server": "Current Server", "current_server": "Huidige Server",
"remote_url": "Remote URL", "remote_url": "Externe URL",
"active_url": "Active URL", "active_url": "Actieve URL",
"not_configured": "Not configured", "not_configured": "Niet geconfigureerd",
"network_added": "Network added", "network_added": "Netwerk toegevoegd",
"network_already_added": "Network already added", "network_already_added": "Netwerk reeds toegevoegd",
"no_wifi_connected": "Not connected to WiFi", "no_wifi_connected": "Niet verbonden met Wi-Fi",
"permission_denied": "Location permission denied", "permission_denied": "Locatie toestemming geweigerd",
"permission_denied_explanation": "Location permission is required to detect WiFi network for auto-switching. Please enable it in Settings." "permission_denied_explanation": "Locatie permissie is vereist om Wifi-netwerk te kunnen detecteren voor automatisch wisselen. Schakel het in via Instellingen."
}, },
"user_info": { "user_info": {
"user_info_title": "Gebruiker Info", "user_info_title": "Gebruiker Info",
@@ -195,11 +195,11 @@
"none": "Geen", "none": "Geen",
"language": "Taal", "language": "Taal",
"transcode_mode": { "transcode_mode": {
"title": "Audio Transcoding", "title": "Audio-transcoding",
"description": "Controls how surround audio (7.1, TrueHD, DTS-HD) is handled", "description": "Bepaalt hoe surround audio (7.1, TrueHD, DTS-HD) wordt behandeld",
"auto": "Auto", "auto": "Automatisch",
"stereo": "Force Stereo", "stereo": "Stereo forceren",
"5_1": "Allow 5.1", "5_1": "5.1 toestaan",
"passthrough": "Passthrough" "passthrough": "Passthrough"
} }
}, },
@@ -231,7 +231,7 @@
"Black": "Zwart", "Black": "Zwart",
"Gray": "Grijs", "Gray": "Grijs",
"Silver": "Zilver", "Silver": "Zilver",
"White": "wit", "White": "Wit",
"Maroon": "Kastanjebruin", "Maroon": "Kastanjebruin",
"Red": "Rood", "Red": "Rood",
"Fuchsia": "Fuchsia", "Fuchsia": "Fuchsia",
@@ -259,14 +259,14 @@
"hardware_decode_description": "Gebruik hardware acceleratie voor video-decodering. Uitschakelen als u problemen met afspelen ondervindt." "hardware_decode_description": "Gebruik hardware acceleratie voor video-decodering. Uitschakelen als u problemen met afspelen ondervindt."
}, },
"vlc_subtitles": { "vlc_subtitles": {
"title": "VLC Subtitle Settings", "title": "VLC ondertitel instellingen",
"hint": "Customize subtitle appearance for VLC player. Changes take effect on next playback.", "hint": "Aanpassen van ondertiteling voor VLC-speler. Wijzigingen worden toegepast bij het afspelen.",
"text_color": "Text Color", "text_color": "Tekstkleur",
"background_color": "Background Color", "background_color": "Achtergrondkleur",
"background_opacity": "Background Opacity", "background_opacity": "Doorzichtigheid achtergrond",
"outline_color": "Outline Color", "outline_color": "Kleur omlijning",
"outline_opacity": "Outline Opacity", "outline_opacity": "Omtrek opaciteit",
"outline_thickness": "Outline Thickness", "outline_thickness": "Omtrek dikte",
"bold": "Bold Text", "bold": "Bold Text",
"margin": "Bottom Margin" "margin": "Bottom Margin"
}, },
@@ -306,7 +306,7 @@
"disable_haptic_feedback": "Haptische feedback uitschakelen", "disable_haptic_feedback": "Haptische feedback uitschakelen",
"default_quality": "Standaard kwaliteit", "default_quality": "Standaard kwaliteit",
"default_playback_speed": "Standaard Afspeelsnelheid", "default_playback_speed": "Standaard Afspeelsnelheid",
"auto_play_next_episode": "Auto-play Next Episode", "auto_play_next_episode": "Volgende aflevering automatisch afspelen",
"max_auto_play_episode_count": "Max Automatisch Aflevering Aantal", "max_auto_play_episode_count": "Max Automatisch Aflevering Aantal",
"disabled": "Uitgeschakeld" "disabled": "Uitgeschakeld"
}, },
@@ -378,12 +378,12 @@
"enable_promoted_watchlists": "Gepromote Kijklijst", "enable_promoted_watchlists": "Gepromote Kijklijst",
"hide_watchlists_tab": "Hide Watchlists Tab", "hide_watchlists_tab": "Hide Watchlists Tab",
"home_sections_hint": "Show personalized recommendations and promoted watchlists from Streamystats on the home page.", "home_sections_hint": "Show personalized recommendations and promoted watchlists from Streamystats on the home page.",
"recommended_movies": "Recommended Movies", "recommended_movies": "Aanbevolen films",
"recommended_series": "Recommended Series", "recommended_series": "Aanbevolen serie",
"toasts": { "toasts": {
"saved": "Saved", "saved": "Opgeslagen",
"refreshed": "Settings refreshed from server", "refreshed": "Settings refreshed from server",
"disabled": "Streamystats disabled" "disabled": "Streamystats uitgeschakeld"
}, },
"refresh_from_server": "Refresh Settings from Server" "refresh_from_server": "Refresh Settings from Server"
}, },
@@ -402,24 +402,24 @@
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support", "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
"enable_music_cache": "Enable Music Cache", "enable_music_cache": "Enable Music Cache",
"clear_music_cache": "Clear Music Cache", "clear_music_cache": "Clear Music Cache",
"music_cache_size": "{{size}} cached", "music_cache_size": "{{size}} gecached",
"music_cache_cleared": "Music cache cleared", "music_cache_cleared": "Muziek cache gewist",
"delete_all_downloaded_songs": "Delete All Downloaded Songs", "delete_all_downloaded_songs": "Verwijder alle gedownloade liedjes",
"downloaded_songs_size": "{{size}} downloaded", "downloaded_songs_size": "{{size}} gedownload",
"downloaded_songs_deleted": "Downloaded songs deleted" "downloaded_songs_deleted": "Downloaded songs deleted"
}, },
"intro": { "intro": {
"title": "Intro", "title": "Intro",
"show_intro": "Toon intro", "show_intro": "Toon intro",
"reset_intro": "intro opnieuw instellen" "reset_intro": "Reset Intro"
}, },
"logs": { "logs": {
"logs_title": "Logboek", "logs_title": "Logboek",
"export_logs": "Export logs", "export_logs": "Export logs",
"click_for_more_info": "Click for more info", "click_for_more_info": "Klik voor meer info",
"level": "Niveau", "level": "Niveau",
"no_logs_available": "Geen logs beschikbaar", "no_logs_available": "Geen logs beschikbaar",
"delete_all_logs": "Verwijder alle logs" "delete_all_logs": "Alle logs verwijderen"
}, },
"languages": { "languages": {
"title": "Talen", "title": "Talen",
@@ -500,14 +500,14 @@
"play": "Afspelen", "play": "Afspelen",
"none": "Geen", "none": "Geen",
"track": "Spoor", "track": "Spoor",
"cancel": "Cancel", "cancel": "Annuleren",
"delete": "Delete", "delete": "Verwijderen",
"ok": "OK", "ok": "O",
"remove": "Remove", "remove": "Verwijderen",
"next": "Next", "next": "Volgende",
"back": "Back", "back": "Terug",
"continue": "Continue", "continue": "Doorgaan",
"verifying": "Verifying..." "verifying": "Verifiëren..."
}, },
"search": { "search": {
"search": "Zoek...", "search": "Zoek...",
@@ -521,10 +521,10 @@
"episodes": "Afleveringen", "episodes": "Afleveringen",
"collections": "Collecties", "collections": "Collecties",
"actors": "Acteurs", "actors": "Acteurs",
"artists": "Artists", "artists": "Artiesten",
"albums": "Albums", "albums": "Albums",
"songs": "Songs", "songs": "Nummers",
"playlists": "Playlists", "playlists": "Afspeellijsten",
"request_movies": "Vraag films aan", "request_movies": "Vraag films aan",
"request_series": "Vraag series aan", "request_series": "Vraag series aan",
"recently_added": "Recent Toegevoegd", "recently_added": "Recent Toegevoegd",
@@ -572,7 +572,7 @@
"genres": "Genres", "genres": "Genres",
"years": "Jaren", "years": "Jaren",
"sort_by": "Sorteren op", "sort_by": "Sorteren op",
"filter_by": "Filter By", "filter_by": "Filteren op",
"sort_order": "Sorteer volgorde", "sort_order": "Sorteer volgorde",
"tags": "Labels" "tags": "Labels"
} }
@@ -610,6 +610,12 @@
"downloaded_file_no": "Nee", "downloaded_file_no": "Nee",
"downloaded_file_cancel": "Annuleren" "downloaded_file_cancel": "Annuleren"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "Volgende", "next_up": "Volgende",
"no_items_to_display": "Geen items om te tonen", "no_items_to_display": "Geen items om te tonen",
@@ -719,127 +725,127 @@
"favorites": "Favorieten" "favorites": "Favorieten"
}, },
"music": { "music": {
"title": "Music", "title": "Muziek",
"tabs": { "tabs": {
"suggestions": "Suggestions", "suggestions": "Suggesties",
"albums": "Albums", "albums": "Albums",
"artists": "Artists", "artists": "Artiesten",
"playlists": "Playlists", "playlists": "Afspeellijsten",
"tracks": "tracks" "tracks": "Nummers"
}, },
"filters": { "filters": {
"all": "All" "all": "Alle"
}, },
"recently_added": "Recently Added", "recently_added": "Recent toegevoegd",
"recently_played": "Recently Played", "recently_played": "Onlangs afgespeeld",
"frequently_played": "Frequently Played", "frequently_played": "Vaak afgespeeld",
"explore": "Explore", "explore": "Ontdek",
"top_tracks": "Top Tracks", "top_tracks": "Top Tracks",
"play": "Play", "play": "Afspelen",
"shuffle": "Shuffle", "shuffle": "Shuffle",
"play_top_tracks": "Play Top Tracks", "play_top_tracks": "Play Top Tracks",
"no_suggestions": "No suggestions available", "no_suggestions": "Geen suggesties beschikbaar",
"no_albums": "No albums found", "no_albums": "Geen albums gevonden",
"no_artists": "No artists found", "no_artists": "Geen artiesten gevonden",
"no_playlists": "No playlists found", "no_playlists": "Geen afspeellijsten gevonden",
"album_not_found": "Album not found", "album_not_found": "Album niet gevonden",
"artist_not_found": "Artist not found", "artist_not_found": "Artiest niet gevonden",
"playlist_not_found": "Playlist not found", "playlist_not_found": "Afspeellijst niet gevonden",
"track_options": { "track_options": {
"play_next": "Play Next", "play_next": "Speel volgende af",
"add_to_queue": "Add to Queue", "add_to_queue": "Toevoegen aan wachtrij",
"add_to_playlist": "Add to Playlist", "add_to_playlist": "Voeg toe aan afspeellijst",
"download": "Download", "download": "Downloaden",
"downloaded": "Downloaded", "downloaded": "Gedownload",
"downloading": "Downloading...", "downloading": "Downloaden...",
"cached": "Cached", "cached": "Gecached",
"delete_download": "Delete Download", "delete_download": "Download verwijderen",
"delete_cache": "Remove from Cache", "delete_cache": "Verwijderen uit cache",
"go_to_artist": "Go to Artist", "go_to_artist": "Ga naar artiest",
"go_to_album": "Go to Album", "go_to_album": "Ga naar album",
"add_to_favorites": "Add to Favorites", "add_to_favorites": "Toevoegen aan favorieten",
"remove_from_favorites": "Remove from Favorites", "remove_from_favorites": "Verwijderen uit favorieten",
"remove_from_playlist": "Remove from Playlist" "remove_from_playlist": "Verwijder uit afspeellijst"
}, },
"playlists": { "playlists": {
"create_playlist": "Create Playlist", "create_playlist": "Afspeellijst aanmaken",
"playlist_name": "Playlist Name", "playlist_name": "Afspeellijst naam",
"enter_name": "Enter playlist name", "enter_name": "Enter playlist name",
"create": "Create", "create": "Aanmaken",
"search_playlists": "Search playlists...", "search_playlists": "Playlist zoeken...",
"added_to": "Added to {{name}}", "added_to": "Toegevoegd aan {{name}}",
"added": "Added to playlist", "added": "Toegevoegd aan playlist",
"removed_from": "Removed from {{name}}", "removed_from": "Verwijderd uit {{name}}",
"removed": "Removed from playlist", "removed": "Verwijderd uit playlist",
"created": "Playlist created", "created": "Playlist created",
"create_new": "Create New Playlist", "create_new": "Create New Playlist",
"failed_to_add": "Failed to add to playlist", "failed_to_add": "Failed to add to playlist",
"failed_to_remove": "Failed to remove from playlist", "failed_to_remove": "Verwijderen uit afspeellijst is mislukt",
"failed_to_create": "Failed to create playlist", "failed_to_create": "Het maken van de afspeellijst is mislukt",
"delete_playlist": "Delete Playlist", "delete_playlist": "Afspeellijst verwijderen",
"delete_confirm": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.", "delete_confirm": "Weet u zeker dat u \"{{name}}\"wilt verwijderen? Deze actie kan niet ongedaan worden gemaakt.",
"deleted": "Playlist deleted", "deleted": "Afspeellijst verwijderd.",
"failed_to_delete": "Failed to delete playlist" "failed_to_delete": "Verwijderen van afspeellijst mislukt"
}, },
"sort": { "sort": {
"title": "Sort By", "title": "Sorteren op",
"alphabetical": "Alphabetical", "alphabetical": "Alfabetisch",
"date_created": "Date Created" "date_created": "Aanmaakdatum"
} }
}, },
"watchlists": { "watchlists": {
"title": "Watchlists", "title": "Watchlist",
"my_watchlists": "My Watchlists", "my_watchlists": "Mijn watchlists",
"public_watchlists": "Public Watchlists", "public_watchlists": "Public Watchlists",
"create_title": "Create Watchlist", "create_title": "Create Watchlist",
"edit_title": "Edit Watchlist", "edit_title": "Edit Watchlist",
"create_button": "Create Watchlist", "create_button": "Create Watchlist",
"save_button": "Save Changes", "save_button": "Wijzigingen opslaan",
"delete_button": "Delete", "delete_button": "Verwijder",
"remove_button": "Remove", "remove_button": "Verwijderen",
"cancel_button": "Cancel", "cancel_button": "Annuleren",
"name_label": "Name", "name_label": "Naam",
"name_placeholder": "Enter watchlist name", "name_placeholder": "Voer naam van kijklijst in",
"description_label": "Description", "description_label": "Beschrijving",
"description_placeholder": "Enter description (optional)", "description_placeholder": "Voer beschrijving in (optioneel)",
"is_public_label": "Public Watchlist", "is_public_label": "Openbare Kijklijst",
"is_public_description": "Allow others to view this watchlist", "is_public_description": "Sta anderen toe om deze kijklijst te bekijken",
"allowed_type_label": "Content Type", "allowed_type_label": "Inhoudstype",
"sort_order_label": "Default Sort Order", "sort_order_label": "Standaard Sortering",
"empty_title": "No Watchlists", "empty_title": "Geen Kijklijsten",
"empty_description": "Create your first watchlist to start organizing your media", "empty_description": "Maak je eerste kijklijst om je media te organiseren",
"empty_watchlist": "This watchlist is empty", "empty_watchlist": "Deze watchlist is leeg",
"empty_watchlist_hint": "Add items from your library to this watchlist", "empty_watchlist_hint": "Voeg items uit je bibliotheek toe aan deze kijklijst",
"not_configured_title": "Streamystats Not Configured", "not_configured_title": "Streamystats niet geconfigureerd",
"not_configured_description": "Configure Streamystats in settings to use watchlists", "not_configured_description": "Configureer Streamystats in instellingen om kijklijsten te gebruiken",
"go_to_settings": "Go to Settings", "go_to_settings": "Ga naar Instellingen",
"add_to_watchlist": "Add to Watchlist", "add_to_watchlist": "Voeg toe aan kijklijst",
"remove_from_watchlist": "Remove from Watchlist", "remove_from_watchlist": "Verwijder van kijklijst",
"select_watchlist": "Select Watchlist", "select_watchlist": "Selecteer kijklijst",
"create_new": "Create New Watchlist", "create_new": "Nieuwe kijklijst aanmaken",
"item": "item", "item": "item",
"items": "items", "items": "items",
"public": "Public", "public": "Publiek",
"private": "Private", "private": "Privé",
"you": "You", "you": "Jij",
"by_owner": "By another user", "by_owner": "Door een andere gebruiker",
"not_found": "Watchlist not found", "not_found": "Kijklijst niet gevonden",
"delete_confirm_title": "Delete Watchlist", "delete_confirm_title": "Verwijder kijklijst",
"delete_confirm_message": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.", "delete_confirm_message": "Weet u zeker dat u \"{{name}}\"wilt verwijderen? Deze actie kan niet ongedaan worden gemaakt.",
"remove_item_title": "Remove from Watchlist", "remove_item_title": "Verwijder van watchlist",
"remove_item_message": "Remove \"{{name}}\" from this watchlist?", "remove_item_message": "Verwijder \"{{name}}\" uit deze watchlist?",
"loading": "Loading watchlists...", "loading": "Laden van watchlists...",
"no_compatible_watchlists": "No compatible watchlists", "no_compatible_watchlists": "Geen compatibele watchlist",
"create_one_first": "Create a watchlist that accepts this content type" "create_one_first": "Maak een watchlist aan die dit inhoudstype accepteert"
}, },
"playback_speed": { "playback_speed": {
"title": "Playback Speed", "title": "Afspeelsnelheid",
"apply_to": "Apply To", "apply_to": "Toepassen op",
"speed": "Speed", "speed": "Snelheid",
"scope": { "scope": {
"media": "This media only", "media": "Alleen deze media",
"show": "This show", "show": "Deze serie",
"all": "All media (default)" "all": "Alle media (standaard)"
} }
} }
} }

View File

@@ -610,6 +610,12 @@
"downloaded_file_no": "No", "downloaded_file_no": "No",
"downloaded_file_cancel": "Cancel" "downloaded_file_cancel": "Cancel"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "Neste opp", "next_up": "Neste opp",
"no_items_to_display": "Ingen elementer å vise", "no_items_to_display": "Ingen elementer å vise",

View File

@@ -610,6 +610,12 @@
"downloaded_file_no": "Nie", "downloaded_file_no": "Nie",
"downloaded_file_cancel": "Anuluj" "downloaded_file_cancel": "Anuluj"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "Następne", "next_up": "Następne",
"no_items_to_display": "Brak elementów do wyświetlenia", "no_items_to_display": "Brak elementów do wyświetlenia",

View File

@@ -610,6 +610,12 @@
"downloaded_file_no": "Não", "downloaded_file_no": "Não",
"downloaded_file_cancel": "Cancelar" "downloaded_file_cancel": "Cancelar"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "A Seguir", "next_up": "A Seguir",
"no_items_to_display": "Nenhum item para exibir", "no_items_to_display": "Nenhum item para exibir",

View File

@@ -610,6 +610,12 @@
"downloaded_file_no": "Nu", "downloaded_file_no": "Nu",
"downloaded_file_cancel": "Anulează" "downloaded_file_cancel": "Anulează"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "Urmează", "next_up": "Urmează",
"no_items_to_display": "Niciun element de afișat", "no_items_to_display": "Niciun element de afișat",

File diff suppressed because it is too large Load Diff

View File

@@ -610,6 +610,12 @@
"downloaded_file_no": "Nej", "downloaded_file_no": "Nej",
"downloaded_file_cancel": "Avbryt" "downloaded_file_cancel": "Avbryt"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "Näst på tur", "next_up": "Näst på tur",
"no_items_to_display": "Inga Artiklar Att Visa", "no_items_to_display": "Inga Artiklar Att Visa",

View File

@@ -610,6 +610,12 @@
"downloaded_file_no": "No", "downloaded_file_no": "No",
"downloaded_file_cancel": "Cancel" "downloaded_file_cancel": "Cancel"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "Next Up", "next_up": "Next Up",
"no_items_to_display": "No Items to Display", "no_items_to_display": "No Items to Display",

View File

@@ -610,6 +610,12 @@
"downloaded_file_no": "No", "downloaded_file_no": "No",
"downloaded_file_cancel": "Cancel" "downloaded_file_cancel": "Cancel"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "wej", "next_up": "wej",
"no_items_to_display": "Doch pagh HochlaH", "no_items_to_display": "Doch pagh HochlaH",

View File

@@ -7,88 +7,88 @@
"username_placeholder": "Kullanıcı adı", "username_placeholder": "Kullanıcı adı",
"password_placeholder": "Şifre", "password_placeholder": "Şifre",
"login_button": "Giriş yap", "login_button": "Giriş yap",
"quick_connect": "Hızlı Bağlantı", "quick_connect": "Hızlı Bağlan",
"enter_code_to_login": "Giriş yapmak için {{code}} kodunu girin", "enter_code_to_login": "Giriş yapmak için {{code}} kodunu girin",
"failed_to_initiate_quick_connect": "Quick Connect başlatılamadı", "failed_to_initiate_quick_connect": "Hızlı Bağlan başlatılamadı",
"got_it": "Anlaşıldı", "got_it": "Anlaşıldı",
"connection_failed": "Bağlantı başarısız", "connection_failed": "Bağlantı başarısız",
"could_not_connect_to_server": "Sunucuya bağlanılamadı. Lütfen URL'yi ve ağ bağlantınızı kontrol edin", "could_not_connect_to_server": "Sunucuya bağlanılamadı. Lütfen URL'yi ve ağ bağlantınızı kontrol edin.",
"an_unexpected_error_occured": "Beklenmedik bir hata oluştu", "an_unexpected_error_occured": "Beklenmedik bir hata oluştu",
"change_server": "Sunucuyu değiştir", "change_server": "Sunucu değiştir",
"invalid_username_or_password": "Geçersiz kullanıcı adı veya şifre", "invalid_username_or_password": "Geçersiz kullanıcı adı veya şifre",
"user_does_not_have_permission_to_log_in": "Kullanıcının giriş yapma izni yok", "user_does_not_have_permission_to_log_in": "Kullanıcının giriş yapma izni yok",
"server_is_taking_too_long_to_respond_try_again_later": "Sunucu yanıt vermekte çok uzun sürüyor, lütfen tekrar deneyin", "server_is_taking_too_long_to_respond_try_again_later": "Sunucunun yanıt vermesi çok uzun sürüyor, lütfen daha sonra tekrar deneyin",
"server_received_too_many_requests_try_again_later": "Sunucu çok fazla istek aldı, lütfen tekrar deneyin.", "server_received_too_many_requests_try_again_later": "Sunucu çok fazla istek aldı, lütfen daha sonra tekrar deneyin.",
"there_is_a_server_error": "Sunucu hatası var", "there_is_a_server_error": "Sunucu hatası var",
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Beklenmedik bir hata oluştu. Sunucu URL'sini doğru girdiğinizden emin oldunuz mu?", "an_unexpected_error_occured_did_you_enter_the_correct_url": "Beklenmedik bir hata oluştu. Sunucu URL'sini doğru girdiğinizden emin misiniz?",
"too_old_server_text": "Unsupported Jellyfin Server Discovered", "too_old_server_text": "Desteklenmeyen Jellyfin Sunucu sürümü bulundu.",
"too_old_server_description": "Please update Jellyfin to the latest version" "too_old_server_description": "Lütfen Jellyfin'i en son sürüme güncelleyin."
}, },
"server": { "server": {
"enter_url_to_jellyfin_server": "Jellyfin sunucunusun URL'sini girin", "enter_url_to_jellyfin_server": "Jellyfin sunucunusun URL adresini girin",
"server_url_placeholder": "http(s)://sunucunuz.com", "server_url_placeholder": "http(s)://sunucunuz.com",
"connect_button": "Bağlan", "connect_button": "Bağlan",
"previous_servers": "Önceki sunucular", "previous_servers": "Önceki sunucular",
"clear_button": "Temizle", "clear_button": "Temizle",
"swipe_to_remove": "Swipe to remove", "swipe_to_remove": "Kaldırmak için kaydırın",
"search_for_local_servers": "Yerel sunucuları ara", "search_for_local_servers": "Yerel sunucuları ara",
"searching": "Aranıyor...", "searching": "Aranıyor...",
"servers": "Sunucular", "servers": "Sunucular",
"saved": "Saved", "saved": "Kaydedildi",
"session_expired": "Session Expired", "session_expired": "Oturum süresi doldu",
"please_login_again": "Your saved session has expired. Please log in again.", "please_login_again": "Kaydedilmiş oturumunuzun süresi doldu. Lütfen tekrar giriş yapın.",
"remove_saved_login": "Remove Saved Login", "remove_saved_login": "Kayıtlı oturumu kaldır",
"remove_saved_login_description": "This will remove your saved credentials for this server. You'll need to enter your username and password again next time.", "remove_saved_login_description": "Bu sunucu için kaydedilmiş kimlik bilgileriniz kaldırılacaktır. Bir sonraki sefere kullanıcı adı ve şifrenizi yeniden girmeniz gerekecek.",
"accounts_count": "{{count}} accounts", "accounts_count": "{{count}} hesap",
"select_account": "Select Account", "select_account": "Hesap Seç",
"add_account": "Add Account", "add_account": "Hesap Ekle",
"remove_account_description": "This will remove the saved credentials for {{username}}." "remove_account_description": "{{username}} için kayıtlı bilgiler kaldırılacaktır."
}, },
"save_account": { "save_account": {
"title": "Save Account", "title": "Hesabı Kaydet",
"save_for_later": "Save this account", "save_for_later": "Bu hesabı kaydet",
"security_option": "Security Option", "security_option": "Güvenlik Seçeneği",
"no_protection": "No protection", "no_protection": "No protection",
"no_protection_desc": "Quick login without authentication", "no_protection_desc": "Kimlik doğrulamasız hızlı giriş",
"pin_code": "PIN code", "pin_code": "PIN kodu",
"pin_code_desc": "4-digit PIN required when switching", "pin_code_desc": "Geçiş yaparken 4 haneli PIN kodu gereklidir",
"password": "Re-enter password", "password": "Şifrenizi tekrar girin ",
"password_desc": "Password required when switching", "password_desc": "Geçiş yaparken şifre gereklidir",
"save_button": "Save", "save_button": "Kaydet",
"cancel_button": "Cancel" "cancel_button": "Vazgeç"
}, },
"pin": { "pin": {
"enter_pin": "Enter PIN", "enter_pin": "PIN kodunu girin",
"enter_pin_for": "Enter PIN for {{username}}", "enter_pin_for": "{{username}} için PIN kodunu girin",
"enter_4_digits": "Enter 4 digits", "enter_4_digits": "4 hane girin",
"invalid_pin": "Invalid PIN", "invalid_pin": "Geçersiz PIN kodu",
"setup_pin": "Set Up PIN", "setup_pin": "PIN kodunu ayarla",
"confirm_pin": "Confirm PIN", "confirm_pin": "PIN kodunu onayla",
"pins_dont_match": "PINs don't match", "pins_dont_match": "PIN kodları eşleşmiyor",
"forgot_pin": "Forgot PIN?", "forgot_pin": "PIN kodunu mu unuttunuz?",
"forgot_pin_desc": "Your saved credentials will be removed" "forgot_pin_desc": "Kayıtlı bilgileriniz kaldırılacaktır"
}, },
"password": { "password": {
"enter_password": "Enter Password", "enter_password": "Şifrenizi girin",
"enter_password_for": "Enter password for {{username}}", "enter_password_for": "{{username}} için şifrenizi girin",
"invalid_password": "Invalid password" "invalid_password": "Geçersiz şifre"
}, },
"home": { "home": {
"checking_server_connection": "Checking server connection...", "checking_server_connection": "Sunucu bağlantısı kontrol ediliyor...",
"no_internet": "İnternet Yok", "no_internet": "İnternet Yok",
"no_items": "Öge Yok", "no_items": "Öge Yok",
"no_internet_message": "Endişelenmeyin, hala\ndownloaded içerik izleyebilirsiniz.", "no_internet_message": "Endişelenmeyin, indirilmiş içerikleri izleyebilirsiniz.",
"checking_server_connection_message": "Checking connection to server", "checking_server_connection_message": "Sunucuya bağlantı kontrol ediliyor",
"go_to_downloads": "İndirmelere Git", "go_to_downloads": "İndirilenlere git",
"retry": "Retry", "retry": "Tekrar dene",
"server_unreachable": "Server Unreachable", "server_unreachable": "Sunucuya ulaşılamıyor",
"server_unreachable_message": "Could not reach the server.\nPlease check your network connection.", "server_unreachable_message": "Sunucuya bağlanılamadı. Lütfen ağ bağlantınızı kontrol edin.",
"oops": "Hups!", "oops": "Hups!",
"error_message": "Bir şeyler ters gitti.\nLütfen çıkış yapın ve tekrar giriş yapın.", "error_message": "Bir şeyler ters gitti.\nLütfen çıkış yapıp tekrar giriş yapın.",
"continue_watching": "İzlemeye Devam Et", "continue_watching": "İzlemeye Devam Et",
"next_up": "Sonraki", "next_up": "Sonraki",
"continue_and_next_up": "Continue & Next Up", "continue_and_next_up": "İzlemeye Devam Et & Sıradakiler",
"recently_added_in": "{{libraryName}}'de Yakınlarda Eklendi", "recently_added_in": "{{libraryName}} Kütüphanesine Son Eklenenler",
"suggested_movies": "Önerilen Filmler", "suggested_movies": "Önerilen Filmler",
"suggested_episodes": "Önerilen Bölümler", "suggested_episodes": "Önerilen Bölümler",
"intro": { "intro": {
@@ -110,52 +110,52 @@
"settings_title": "Ayarlar", "settings_title": "Ayarlar",
"log_out_button": ıkış Yap", "log_out_button": ıkış Yap",
"categories": { "categories": {
"title": "Categories" "title": "Kategoriler"
}, },
"playback_controls": { "playback_controls": {
"title": "Playback & Controls" "title": "Oynatma & Kontroller"
}, },
"audio_subtitles": { "audio_subtitles": {
"title": "Audio & Subtitles" "title": "Ses & Altyazılar"
}, },
"appearance": { "appearance": {
"title": "Appearance", "title": "Görünüm",
"merge_next_up_continue_watching": "Merge Continue Watching & Next Up", "merge_next_up_continue_watching": "İzlemeye Devam Et & Sıradakiler'i birleştir",
"hide_remote_session_button": "Hide Remote Session Button" "hide_remote_session_button": "Uzak Oturum Butonunu Gizle"
}, },
"network": { "network": {
"title": "Network", "title": "",
"local_network": "Local Network", "local_network": "Yerel Ağ",
"auto_switch_enabled": "Auto-switch when at home", "auto_switch_enabled": "Evdeyken otomatik geçiş yap",
"auto_switch_description": "Automatically switch to local URL when connected to home WiFi", "auto_switch_description": "Ev WiFi'sine bağlanınca otomatik olarak yerek URL adresine geçiş yap",
"local_url": "Local URL", "local_url": "Yerel URL Adresi",
"local_url_hint": "Enter your local server address (e.g., http://192.168.1.100:8096)", "local_url_hint": "Yerel sunucu adresinizi girin (http://192.168.1.100:8096, gibi)",
"local_url_placeholder": "http://192.168.1.100:8096", "local_url_placeholder": "http://192.168.1.100:8096",
"home_wifi_networks": "Home WiFi Networks", "home_wifi_networks": "Ev WiFi ağları",
"add_current_network": "Add \"{{ssid}}\"", "add_current_network": "\"{{ssid}}\"'yi ekle",
"not_connected_to_wifi": "Not connected to WiFi", "not_connected_to_wifi": "WiFi'a bağlı değil",
"no_networks_configured": "No networks configured", "no_networks_configured": "Herhangi bir ağ ayarlanmadı",
"add_network_hint": "Add your home WiFi network to enable auto-switching", "add_network_hint": "Otomatik geçişi etkinleştirmek için ev WiFi'nizi ekleyin",
"current_wifi": "Current WiFi", "current_wifi": "Şu anki WiFi",
"using_url": "Using", "using_url": "Kullanılıyor",
"local": "Local URL", "local": "Yerel URL Adresi",
"remote": "Remote URL", "remote": "Uzak URL Adresi",
"not_connected": "Not connected", "not_connected": "Bağlı değil",
"current_server": "Current Server", "current_server": "Geçerli Sunucu",
"remote_url": "Remote URL", "remote_url": "Uzak URL Adresi",
"active_url": "Active URL", "active_url": "Aktif URL Adresi",
"not_configured": "Not configured", "not_configured": "Yapılandırılmamış",
"network_added": "Network added", "network_added": "Ağ eklendi",
"network_already_added": "Network already added", "network_already_added": "Ağ zaten eklendi",
"no_wifi_connected": "Not connected to WiFi", "no_wifi_connected": "WiFi'a bağlı değil",
"permission_denied": "Location permission denied", "permission_denied": "Konum izni reddedildi",
"permission_denied_explanation": "Location permission is required to detect WiFi network for auto-switching. Please enable it in Settings." "permission_denied_explanation": "Otomatik geçiş yapabilmek için WiFi ağını algılayabilmek için konum izni gereklidir. Lütfen Ayarlarda etkinleştirin."
}, },
"user_info": { "user_info": {
"user_info_title": "Kullanıcı Bilgisi", "user_info_title": "Kullanıcı Bilgisi",
"user": "Kullanıcı", "user": "Kullanıcı",
"server": "Sunucu", "server": "Sunucu",
"token": "Token", "token": "Erişim Anahtarı",
"app_version": "Uygulama Sürümü" "app_version": "Uygulama Sürümü"
}, },
"quick_connect": { "quick_connect": {
@@ -172,20 +172,20 @@
"media_controls_title": "Medya Kontrolleri", "media_controls_title": "Medya Kontrolleri",
"forward_skip_length": "İleri Sarma Uzunluğu", "forward_skip_length": "İleri Sarma Uzunluğu",
"rewind_length": "Geri Sarma Uzunluğu", "rewind_length": "Geri Sarma Uzunluğu",
"seconds_unit": "s" "seconds_unit": "sn"
}, },
"gesture_controls": { "gesture_controls": {
"gesture_controls_title": "Gesture Controls", "gesture_controls_title": "Hareketle Kontrol",
"horizontal_swipe_skip": "Horizontal Swipe to Skip", "horizontal_swipe_skip": "Atlamak için yatay kaydırma",
"horizontal_swipe_skip_description": "Swipe left/right when controls are hidden to skip", "horizontal_swipe_skip_description": "Kontroller gizliyken sola/sağa kaydırarak atlama",
"left_side_brightness": "Left Side Brightness Control", "left_side_brightness": "Sol Taraf Parlaklık Kontrolü",
"left_side_brightness_description": "Swipe up/down on left side to adjust brightness", "left_side_brightness_description": "Sol tarafta aşağı/yukarı kaydırarak parlaklık ayarı",
"right_side_volume": "Right Side Volume Control", "right_side_volume": "Sağ Taraf Ses Kontrolü",
"right_side_volume_description": "Swipe up/down on right side to adjust volume", "right_side_volume_description": "Sağ tarafta aşağı/yukarı kaydırarak ses ayarı",
"hide_volume_slider": "Hide Volume Slider", "hide_volume_slider": "Ses Ayarını Gizle",
"hide_volume_slider_description": "Hide the volume slider in the video player", "hide_volume_slider_description": "Video oynatıcıda ses ayarını gizle",
"hide_brightness_slider": "Hide Brightness Slider", "hide_brightness_slider": "Parlaklık Ayarını Gizle",
"hide_brightness_slider_description": "Hide the brightness slider in the video player" "hide_brightness_slider_description": "Video oynatıcıda parlaklık ayarını gizle"
}, },
"audio": { "audio": {
"audio_title": "Ses", "audio_title": "Ses",
@@ -195,12 +195,12 @@
"none": "Yok", "none": "Yok",
"language": "Dil", "language": "Dil",
"transcode_mode": { "transcode_mode": {
"title": "Audio Transcoding", "title": "Ses Kod Dönüştürmesi",
"description": "Controls how surround audio (7.1, TrueHD, DTS-HD) is handled", "description": "Surround sesin (7.1, TrueHD, DTS-HD) nasıl işleneceğini kontrol eder.",
"auto": "Auto", "auto": "Oto",
"stereo": "Force Stereo", "stereo": "Stereo'ya zorla",
"5_1": "Allow 5.1", "5_1": "5.1'e izin ver",
"passthrough": "Passthrough" "passthrough": "Doğrudan geçiş"
} }
}, },
"subtitles": { "subtitles": {
@@ -220,60 +220,60 @@
"None": "Yok", "None": "Yok",
"OnlyForced": "Sadece Zorunlu" "OnlyForced": "Sadece Zorunlu"
}, },
"text_color": "Text Color", "text_color": "Metin Rengi",
"background_color": "Background Color", "background_color": "Arkaplan Rengi",
"outline_color": "Outline Color", "outline_color": "Kenarlık Rengi",
"outline_thickness": "Outline Thickness", "outline_thickness": "Kenarlık kalınlığı",
"background_opacity": "Background Opacity", "background_opacity": "Arkaplan Opaklığı",
"outline_opacity": "Outline Opacity", "outline_opacity": "Kenarlık Opaklığı",
"bold_text": "Bold Text", "bold_text": "Kalın Metin",
"colors": { "colors": {
"Black": "Black", "Black": "Siyah",
"Gray": "Gray", "Gray": "Gri",
"Silver": "Silver", "Silver": "Gümüş",
"White": "White", "White": "Beyaz",
"Maroon": "Maroon", "Maroon": "Kestane",
"Red": "Red", "Red": "Kırmızı",
"Fuchsia": "Fuchsia", "Fuchsia": "Fuşya",
"Yellow": "Yellow", "Yellow": "Sarı",
"Olive": "Olive", "Olive": "Zeytin yeşili",
"Green": "Green", "Green": "Yeşil",
"Teal": "Teal", "Teal": "Deniz mavisi",
"Lime": "Lime", "Lime": "Limon",
"Purple": "Purple", "Purple": "Mor",
"Navy": "Navy", "Navy": "Lacivert",
"Blue": "Blue", "Blue": "Mavi",
"Aqua": "Aqua" "Aqua": "Açık Mavi"
}, },
"thickness": { "thickness": {
"None": "Hiçbiri", "None": "Hiçbiri",
"Thin": "Thin", "Thin": "İnce",
"Normal": "Normal", "Normal": "Normal",
"Thick": "Thick" "Thick": "Kalın"
}, },
"subtitle_color": "Subtitle Color", "subtitle_color": "Altyazı Rengi",
"subtitle_background_color": "Background Color", "subtitle_background_color": "Arkaplan Rengi",
"subtitle_font": "Subtitle Font", "subtitle_font": "Altyazı Yazı Tipi",
"ksplayer_title": "KSPlayer Settings", "ksplayer_title": "KSPlayer Ayarları",
"hardware_decode": "Hardware Decoding", "hardware_decode": "Donanımsal Kod Çözme",
"hardware_decode_description": "Use hardware acceleration for video decoding. Disable if you experience playback issues." "hardware_decode_description": "Video kod çözme için donanımsal hızlandırma kullan. Oynatma sorunları yaşıyorsanız devre dışı bırakın."
}, },
"vlc_subtitles": { "vlc_subtitles": {
"title": "VLC Subtitle Settings", "title": "VLC Altyazı Ayarları",
"hint": "Customize subtitle appearance for VLC player. Changes take effect on next playback.", "hint": "VLC oynatıcı için altyazı görünümünü değiştirin. Değişiklikler bir sonraki oynatmada etkili olacak.",
"text_color": "Text Color", "text_color": "Metin Rengi",
"background_color": "Background Color", "background_color": "Arkaplan Rengi",
"background_opacity": "Background Opacity", "background_opacity": "Arkaplan Opaklığı",
"outline_color": "Outline Color", "outline_color": "Kenarlık Rengi",
"outline_opacity": "Outline Opacity", "outline_opacity": "Kenarlık Opaklığı",
"outline_thickness": "Outline Thickness", "outline_thickness": "Kenarlık Kalınlığı",
"bold": "Bold Text", "bold": "Kalın Metin",
"margin": "Bottom Margin" "margin": "Alt Kenar Boşluğu"
}, },
"video_player": { "video_player": {
"title": "Video Player", "title": "Video oynatıcısı",
"video_player": "Video Player", "video_player": "Video oynatıcısı",
"video_player_description": "Choose which video player to use on iOS.", "video_player_description": "iOS'da hangi video oynatıcının kullanılacağını seçin.",
"ksplayer": "KSPlayer", "ksplayer": "KSPlayer",
"vlc": "VLC" "vlc": "VLC"
}, },
@@ -297,7 +297,7 @@
"video_player": "Video player", "video_player": "Video player",
"video_players": { "video_players": {
"VLC_3": "VLC 3", "VLC_3": "VLC 3",
"VLC_4": "VLC 4 (Experimental + PiP)" "VLC_4": "VLC 4 (Deneysel + PiP)"
}, },
"show_custom_menu_links": "Özel Menü Bağlantılarını Göster", "show_custom_menu_links": "Özel Menü Bağlantılarını Göster",
"show_large_home_carousel": "Show Large Home Carousel (beta)", "show_large_home_carousel": "Show Large Home Carousel (beta)",
@@ -305,24 +305,24 @@
"select_liraries_you_want_to_hide": "Kütüphane sekmesinden ve ana sayfa bölümlerinden gizlemek istediğiniz kütüphaneleri seçin.", "select_liraries_you_want_to_hide": "Kütüphane sekmesinden ve ana sayfa bölümlerinden gizlemek istediğiniz kütüphaneleri seçin.",
"disable_haptic_feedback": "Dokunsal Geri Bildirimi Devre Dışı Bırak", "disable_haptic_feedback": "Dokunsal Geri Bildirimi Devre Dışı Bırak",
"default_quality": "Varsayılan kalite", "default_quality": "Varsayılan kalite",
"default_playback_speed": "Default Playback Speed", "default_playback_speed": "Varsayılan Oynatma Hızı",
"auto_play_next_episode": "Auto-play Next Episode", "auto_play_next_episode": "Otomatik Sonraki Bölümü Oynat",
"max_auto_play_episode_count": "Max Auto Play Episode Count", "max_auto_play_episode_count": "En Fazla Otomatik Oynatılacak Bölüm Sayısı",
"disabled": "Devre dışı" "disabled": "Devre dışı"
}, },
"downloads": { "downloads": {
"downloads_title": "İndirmeler" "downloads_title": "İndirmeler"
}, },
"music": { "music": {
"title": "Music", "title": "Müzik",
"playback_title": "Playback", "playback_title": "Oynatma",
"playback_description": "Configure how music is played.", "playback_description": "Müziğin nasıl çalınacağını ayarlayın.",
"prefer_downloaded": "Prefer Downloaded Songs", "prefer_downloaded": "İndirilmiş Şarkıları Tercih Et",
"caching_title": "Caching", "caching_title": "Önbellekleme",
"caching_description": "Automatically cache upcoming tracks for smoother playback.", "caching_description": "Akıcı oynatım için gelecek şarkıları otomatik önbelleğe al.",
"lookahead_enabled": "Enable Look-Ahead Caching", "lookahead_enabled": "Enable Look-Ahead Caching",
"lookahead_count": "Tracks to Pre-cache", "lookahead_count": "Önden Önbelleklenecek Parça Sayısı",
"max_cache_size": "Max Cache Size" "max_cache_size": "Maksimum Önbellek Boyutu"
}, },
"plugins": { "plugins": {
"plugins_title": "Eklentiler", "plugins_title": "Eklentiler",
@@ -345,7 +345,7 @@
"order_by": { "order_by": {
"DEFAULT": "Varsayılan", "DEFAULT": "Varsayılan",
"VOTE_COUNT_AND_AVERAGE": "Vote count and average", "VOTE_COUNT_AND_AVERAGE": "Vote count and average",
"POPULARITY": "Popularity" "POPULARITY": "Popülerlik"
} }
}, },
"marlin_search": { "marlin_search": {
@@ -357,35 +357,35 @@
"save_button": "Kaydet", "save_button": "Kaydet",
"toasts": { "toasts": {
"saved": "Kaydedildi", "saved": "Kaydedildi",
"refreshed": "Settings refreshed from server" "refreshed": "Ayarlar sunucudan yeniden alındı"
}, },
"refresh_from_server": "Refresh Settings from Server" "refresh_from_server": "Ayarları Sunucudan Yeniden Al"
}, },
"streamystats": { "streamystats": {
"enable_streamystats": "Enable Streamystats", "enable_streamystats": "Streamystats'ı Etkinleştir",
"disable_streamystats": "Disable Streamystats", "disable_streamystats": "Streamystats'ı Devre Dışı Bırak",
"enable_search": "Use for Search", "enable_search": "Arama için kullan",
"url": "URL", "url": "URL Adresi",
"server_url_placeholder": "http(s)://streamystats.example.com", "server_url_placeholder": "http(s)://streamystats.example.com",
"streamystats_search_hint": "Enter the URL for your Streamystats server. The URL should include http or https and optionally the port.", "streamystats_search_hint": "Streamystats sunucu URL'sini girin. URL, http veya https içermeli ve isteğe bağlı olarak portu içerebilir.",
"read_more_about_streamystats": "Read More About Streamystats.", "read_more_about_streamystats": "Streamystats hakkında daha fazla bilgi.",
"save_button": "Save", "save_button": "Kaydet",
"save": "Save", "save": "Kaydet",
"features_title": "Features", "features_title": "Özellikler",
"home_sections_title": "Home Sections", "home_sections_title": "Home Sections",
"enable_movie_recommendations": "Movie Recommendations", "enable_movie_recommendations": "Film Önerileri",
"enable_series_recommendations": "Series Recommendations", "enable_series_recommendations": "Dizi Önerileri",
"enable_promoted_watchlists": "Promoted Watchlists", "enable_promoted_watchlists": "Promoted Watchlists",
"hide_watchlists_tab": "Hide Watchlists Tab", "hide_watchlists_tab": "Hide Watchlists Tab",
"home_sections_hint": "Show personalized recommendations and promoted watchlists from Streamystats on the home page.", "home_sections_hint": "Show personalized recommendations and promoted watchlists from Streamystats on the home page.",
"recommended_movies": "Recommended Movies", "recommended_movies": "Önerilen Filmler",
"recommended_series": "Recommended Series", "recommended_series": "Önerilen Diziler",
"toasts": { "toasts": {
"saved": "Saved", "saved": "Kaydedildi",
"refreshed": "Settings refreshed from server", "refreshed": "Ayarlar sunucudan yeniden alındı",
"disabled": "Streamystats disabled" "disabled": "Streamystats devre dışı"
}, },
"refresh_from_server": "Refresh Settings from Server" "refresh_from_server": "Ayarları Sunucudan Yeniden Al"
}, },
"kefinTweaks": { "kefinTweaks": {
"watchlist_enabler": "Enable our Watchlist integration", "watchlist_enabler": "Enable our Watchlist integration",
@@ -398,18 +398,18 @@
"device_usage": "Cihaz {{availableSpace}}%", "device_usage": "Cihaz {{availableSpace}}%",
"size_used": "{{used}} / {{total}} kullanıldı", "size_used": "{{used}} / {{total}} kullanıldı",
"delete_all_downloaded_files": "Tüm indirilen dosyaları sil", "delete_all_downloaded_files": "Tüm indirilen dosyaları sil",
"music_cache_title": "Music Cache", "music_cache_title": "Müzik Ön Belleği",
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support", "music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
"enable_music_cache": "Enable Music Cache", "enable_music_cache": "Müzik Ön Belleğini Etkinleştir",
"clear_music_cache": "Clear Music Cache", "clear_music_cache": "Müzik Ön Belleğini Temizle",
"music_cache_size": "{{size}} cached", "music_cache_size": "{{size}} ön belleklendi",
"music_cache_cleared": "Music cache cleared", "music_cache_cleared": "Müzik ön belleği temizlendi",
"delete_all_downloaded_songs": "Delete All Downloaded Songs", "delete_all_downloaded_songs": "Tüm İndirilen Müzikleri Sil",
"downloaded_songs_size": "{{size}} downloaded", "downloaded_songs_size": "{{size}} indirildi",
"downloaded_songs_deleted": "Downloaded songs deleted" "downloaded_songs_deleted": "İndirilen müzikler silindi"
}, },
"intro": { "intro": {
"title": "Intro", "title": "Giriş",
"show_intro": "Tanıtımı Göster", "show_intro": "Tanıtımı Göster",
"reset_intro": "Tanıtımı Sıfırla" "reset_intro": "Tanıtımı Sıfırla"
}, },
@@ -417,7 +417,7 @@
"logs_title": "Günlükler", "logs_title": "Günlükler",
"export_logs": "Export logs", "export_logs": "Export logs",
"click_for_more_info": "Click for more info", "click_for_more_info": "Click for more info",
"level": "Level", "level": "Düzey",
"no_logs_available": "Günlükler mevcut değil", "no_logs_available": "Günlükler mevcut değil",
"delete_all_logs": "Tüm günlükleri sil" "delete_all_logs": "Tüm günlükleri sil"
}, },
@@ -433,22 +433,22 @@
} }
}, },
"sessions": { "sessions": {
"title": "Sessions", "title": "Oturumlar",
"no_active_sessions": "No Active Sessions" "no_active_sessions": "Aktif Oturum Yok"
}, },
"downloads": { "downloads": {
"downloads_title": "İndirilenler", "downloads_title": "İndirilenler",
"tvseries": "Diziler", "tvseries": "Diziler",
"movies": "Filmler", "movies": "Filmler",
"queue": "Sıra", "queue": "Sıra",
"other_media": "Other media", "other_media": "Diğer medya",
"queue_hint": "Sıra ve indirmeler uygulama yeniden başlatıldığında kaybolacaktır", "queue_hint": "Sıra ve indirmeler uygulama yeniden başlatıldığında kaybolacaktır",
"no_items_in_queue": "Sırada öğe yok", "no_items_in_queue": "Sırada öğe yok",
"no_downloaded_items": "İndirilen öğe yok", "no_downloaded_items": "İndirilen öğe yok",
"delete_all_movies_button": "Tüm Filmleri Sil", "delete_all_movies_button": "Tüm Filmleri Sil",
"delete_all_tvseries_button": "Tüm Dizileri Sil", "delete_all_tvseries_button": "Tüm Dizileri Sil",
"delete_all_button": "Tümünü Sil", "delete_all_button": "Tümünü Sil",
"delete_all_other_media_button": "Delete other media", "delete_all_other_media_button": "Diğer medyayı sil",
"active_download": "Aktif indirme", "active_download": "Aktif indirme",
"no_active_downloads": "Aktif indirme yok", "no_active_downloads": "Aktif indirme yok",
"active_downloads": "Aktif indirmeler", "active_downloads": "Aktif indirmeler",
@@ -465,49 +465,49 @@
"failed_to_delete_all_movies": "Filmler silinemedi", "failed_to_delete_all_movies": "Filmler silinemedi",
"deleted_all_tvseries_successfully": "Tüm diziler başarıyla silindi!", "deleted_all_tvseries_successfully": "Tüm diziler başarıyla silindi!",
"failed_to_delete_all_tvseries": "Diziler silinemedi", "failed_to_delete_all_tvseries": "Diziler silinemedi",
"deleted_media_successfully": "Deleted other media Successfully!", "deleted_media_successfully": "Diğer medya başarıyla silindi!",
"failed_to_delete_media": "Failed to Delete other media", "failed_to_delete_media": "Failed to Delete other media",
"download_deleted": "Download Deleted", "download_deleted": "İndirme silindi",
"download_cancelled": "İndirme iptal edildi", "download_cancelled": "İndirme iptal edildi",
"could_not_delete_download": "Could Not Delete Download", "could_not_delete_download": "İndirme Silinemedi",
"download_paused": "Download Paused", "download_paused": "İndirme Duraklatıldı",
"could_not_pause_download": "Could Not Pause Download", "could_not_pause_download": "İndirme Duraklatılamadı",
"download_resumed": "Download Resumed", "download_resumed": "İndirme Devam Ediyor",
"could_not_resume_download": "Could Not Resume Download", "could_not_resume_download": "İndirme Devam Ettirilemedi",
"download_completed": "İndirme tamamlandı", "download_completed": "İndirme tamamlandı",
"download_failed": "Download Failed", "download_failed": "İndirme başarısız oldu",
"download_failed_for_item": "{{item}} için indirme başarısız oldu - {{error}}", "download_failed_for_item": "{{item}} için indirme başarısız oldu - {{error}}",
"download_completed_for_item": "{{item}} için indirme tamamlandı", "download_completed_for_item": "{{item}} için indirme tamamlandı",
"download_started_for_item": "Download Started for {{item}}", "download_started_for_item": "{{item}} için indirme başladı",
"failed_to_start_download": "Failed to start download", "failed_to_start_download": "İndirme başlatılamadı",
"item_already_downloading": "{{item}} is already downloading", "item_already_downloading": "{{item}} zaten indiriliyor",
"all_files_deleted": "All Downloads Deleted Successfully", "all_files_deleted": "Bütün indirilenler başarıyla silindi",
"files_deleted_by_type": "{{count}} {{type}} deleted", "files_deleted_by_type": "{{count}} {{type}} silindi",
"all_files_folders_and_jobs_deleted_successfully": "Tüm dosyalar, klasörler ve işler başarıyla silindi", "all_files_folders_and_jobs_deleted_successfully": "Tüm dosyalar, klasörler ve işler başarıyla silindi",
"failed_to_clean_cache_directory": "Failed to clean cache directory", "failed_to_clean_cache_directory": "Önbellek dizini temizlenemedi",
"could_not_get_download_url_for_item": "Could not get download URL for {{itemName}}", "could_not_get_download_url_for_item": "{{itemName}} için indirme URL'si alınamadı",
"go_to_downloads": "İndirmelere git", "go_to_downloads": "İndirmelere git",
"file_deleted": "{{item}} deleted" "file_deleted": "{{item}} silindi"
} }
} }
}, },
"common": { "common": {
"select": "Select", "select": "Seç",
"no_trailer_available": "No trailer available", "no_trailer_available": "Fragman mevcut değil",
"video": "Video", "video": "Video",
"audio": "Ses", "audio": "Ses",
"subtitle": "Altyazı", "subtitle": "Altyazı",
"play": "Play", "play": "Oynat",
"none": "None", "none": "Hiçbiri",
"track": "Track", "track": "Parça",
"cancel": "Cancel", "cancel": "Vazgeç",
"delete": "Delete", "delete": "Sil",
"ok": "OK", "ok": "Tamam",
"remove": "Remove", "remove": "Kaldır",
"next": "Next", "next": "Sonraki",
"back": "Back", "back": "Geri",
"continue": "Continue", "continue": "Devam",
"verifying": "Verifying..." "verifying": "Doğrulanıyor..."
}, },
"search": { "search": {
"search": "Ara...", "search": "Ara...",
@@ -521,10 +521,10 @@
"episodes": "Bölümler", "episodes": "Bölümler",
"collections": "Koleksiyonlar", "collections": "Koleksiyonlar",
"actors": "Oyuncular", "actors": "Oyuncular",
"artists": "Artists", "artists": "Sanatçılar",
"albums": "Albums", "albums": "Albümler",
"songs": "Songs", "songs": "Şarkılar",
"playlists": "Playlists", "playlists": "Çalma listeleri",
"request_movies": "Film Talep Et", "request_movies": "Film Talep Et",
"request_series": "Dizi Talep Et", "request_series": "Dizi Talep Et",
"recently_added": "Son Eklenenler", "recently_added": "Son Eklenenler",
@@ -572,7 +572,7 @@
"genres": "Türler", "genres": "Türler",
"years": "Yıllar", "years": "Yıllar",
"sort_by": "Sırala", "sort_by": "Sırala",
"filter_by": "Filter By", "filter_by": "Filtrele",
"sort_order": "Sıralama düzeni", "sort_order": "Sıralama düzeni",
"tags": "Etiketler" "tags": "Etiketler"
} }
@@ -604,11 +604,17 @@
"index": "İndeks:", "index": "İndeks:",
"continue_watching": "İzlemeye devam et", "continue_watching": "İzlemeye devam et",
"go_back": "Geri", "go_back": "Geri",
"downloaded_file_title": "You have this file downloaded", "downloaded_file_title": "Bu dosya indirilmiş",
"downloaded_file_message": "Do you want to play the downloaded file?", "downloaded_file_message": "İndirilmiş dosyayı oynatmak ister misiniz?",
"downloaded_file_yes": "Yes", "downloaded_file_yes": "Evet",
"downloaded_file_no": "No", "downloaded_file_no": "Hayır",
"downloaded_file_cancel": "Cancel" "downloaded_file_cancel": "Vazgeç"
},
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
}, },
"item_card": { "item_card": {
"next_up": "Sıradaki", "next_up": "Sıradaki",
@@ -624,7 +630,7 @@
"no_similar_items_found": "Benzer öge bulunamadı", "no_similar_items_found": "Benzer öge bulunamadı",
"video": "Video", "video": "Video",
"more_details": "Daha fazla detay", "more_details": "Daha fazla detay",
"media_options": "Media Options", "media_options": "Medya Seçenekleri",
"quality": "Kalite", "quality": "Kalite",
"audio": "Ses", "audio": "Ses",
"subtitles": "Altyazı", "subtitles": "Altyazı",
@@ -639,7 +645,7 @@
"download_episode": "Bölümü indir", "download_episode": "Bölümü indir",
"download_movie": "Filmi indir", "download_movie": "Filmi indir",
"download_x_item": "{{item_count}} tane ögeyi indir", "download_x_item": "{{item_count}} tane ögeyi indir",
"download_unwatched_only": "Unwatched Only", "download_unwatched_only": "Yalnızca İzlenmemişler",
"download_button": "İndir" "download_button": "İndir"
} }
}, },
@@ -693,10 +699,10 @@
"number_episodes": "Bölüm {{episode_number}}", "number_episodes": "Bölüm {{episode_number}}",
"born": "Doğum", "born": "Doğum",
"appearances": "Görünmeler", "appearances": "Görünmeler",
"approve": "Approve", "approve": "Onayla",
"decline": "Decline", "decline": "Reddet",
"requested_by": "Requested by {{user}}", "requested_by": "{{user}} tarafından istendi",
"unknown_user": "Unknown User", "unknown_user": "Bilinmeyen Kullanıcı",
"toasts": { "toasts": {
"jellyseer_does_not_meet_requirements": "Jellyseerr sunucusu minimum sürüm gereksinimlerini karşılamıyor! Lütfen en az 2.0.0 sürümüne güncelleyin", "jellyseer_does_not_meet_requirements": "Jellyseerr sunucusu minimum sürüm gereksinimlerini karşılamıyor! Lütfen en az 2.0.0 sürümüne güncelleyin",
"jellyseerr_test_failed": "Jellyseerr testi başarısız oldu. Lütfen tekrar deneyin.", "jellyseerr_test_failed": "Jellyseerr testi başarısız oldu. Lütfen tekrar deneyin.",
@@ -705,10 +711,10 @@
"requested_item": "{{item}} talep edildi!", "requested_item": "{{item}} talep edildi!",
"you_dont_have_permission_to_request": "İstek göndermeye izniniz yok!", "you_dont_have_permission_to_request": "İstek göndermeye izniniz yok!",
"something_went_wrong_requesting_media": "Medya talep edilirken bir şeyler ters gitti!", "something_went_wrong_requesting_media": "Medya talep edilirken bir şeyler ters gitti!",
"request_approved": "Request Approved!", "request_approved": "İstek Onaylandı!",
"request_declined": "Request Declined!", "request_declined": "İstek Reddedildi!",
"failed_to_approve_request": "Failed to Approve Request", "failed_to_approve_request": "İsteği Onaylama Başarısız Oldu",
"failed_to_decline_request": "Failed to Decline Request" "failed_to_decline_request": "İsteği Reddetme Başarısız Oldu"
} }
}, },
"tabs": { "tabs": {
@@ -719,127 +725,127 @@
"favorites": "Favoriler" "favorites": "Favoriler"
}, },
"music": { "music": {
"title": "Music", "title": "Müzik",
"tabs": { "tabs": {
"suggestions": "Suggestions", "suggestions": "Öneriler",
"albums": "Albums", "albums": "Albümler",
"artists": "Artists", "artists": "Sanatçılar",
"playlists": "Playlists", "playlists": "Çalma listeleri",
"tracks": "tracks" "tracks": "parçalar"
}, },
"filters": { "filters": {
"all": "All" "all": "Tümü"
}, },
"recently_added": "Recently Added", "recently_added": "Son Eklenenler",
"recently_played": "Recently Played", "recently_played": "Son Oynatılanlar",
"frequently_played": "Frequently Played", "frequently_played": "Sık Oynatılanlar",
"explore": "Explore", "explore": "Keşfet",
"top_tracks": "Top Tracks", "top_tracks": "En Popülar Parçalar",
"play": "Play", "play": "Oynat",
"shuffle": "Shuffle", "shuffle": "Karıştır",
"play_top_tracks": "Play Top Tracks", "play_top_tracks": "En Çok Oynatılan Parçaları Oynat",
"no_suggestions": "No suggestions available", "no_suggestions": "Öneri mevcut değil",
"no_albums": "No albums found", "no_albums": "Hiç albüm bulunamadı",
"no_artists": "No artists found", "no_artists": "Hiç sanatçı bulunamadı",
"no_playlists": "No playlists found", "no_playlists": "Hiç çalma listesi bulunamadı",
"album_not_found": "Album not found", "album_not_found": "Albüm bulunamadı",
"artist_not_found": "Artist not found", "artist_not_found": "Sanatçı bulunamadı",
"playlist_not_found": "Playlist not found", "playlist_not_found": "Çalma listesi bulunamadı",
"track_options": { "track_options": {
"play_next": "Play Next", "play_next": "Sıradakini Çal",
"add_to_queue": "Add to Queue", "add_to_queue": "Sıraya Ekle",
"add_to_playlist": "Add to Playlist", "add_to_playlist": "Çalma listesine ekle",
"download": "Download", "download": "İndir",
"downloaded": "Downloaded", "downloaded": "İndirildi",
"downloading": "Downloading...", "downloading": "İndiriliyor...",
"cached": "Cached", "cached": "Önbellekte",
"delete_download": "Delete Download", "delete_download": "İndirmeyi Sil",
"delete_cache": "Remove from Cache", "delete_cache": "Ön bellekten kaldır",
"go_to_artist": "Go to Artist", "go_to_artist": "Sanatçıya Git",
"go_to_album": "Go to Album", "go_to_album": "Albüme Git",
"add_to_favorites": "Add to Favorites", "add_to_favorites": "Favorilere Ekle",
"remove_from_favorites": "Remove from Favorites", "remove_from_favorites": "Favorilerden Kaldır",
"remove_from_playlist": "Remove from Playlist" "remove_from_playlist": "Çalma Listesinden Kaldır"
}, },
"playlists": { "playlists": {
"create_playlist": "Create Playlist", "create_playlist": "Çalma Listesi Oluştur",
"playlist_name": "Playlist Name", "playlist_name": "Çalma Listesi Adı",
"enter_name": "Enter playlist name", "enter_name": "Çalma listesi adı girin",
"create": "Create", "create": "Oluştur",
"search_playlists": "Search playlists...", "search_playlists": "Çalma listelerini ara...",
"added_to": "Added to {{name}}", "added_to": "Şu çalma listesine eklendi: {{name}}",
"added": "Added to playlist", "added": "Çalma listesine eklendi",
"removed_from": "Removed from {{name}}", "removed_from": "Şu çalma listesinden kaldırıldı: {{name}}",
"removed": "Removed from playlist", "removed": "Çalma listesinden kaldır",
"created": "Playlist created", "created": "Çalma listesi oluşturuldu",
"create_new": "Create New Playlist", "create_new": "Yeni Çalma Listesi Oluştur",
"failed_to_add": "Failed to add to playlist", "failed_to_add": "Çalma listesine eklenemedi",
"failed_to_remove": "Failed to remove from playlist", "failed_to_remove": "Çalma listesinden kaldırılamadı",
"failed_to_create": "Failed to create playlist", "failed_to_create": "Çalma listesi oluşturulamadı",
"delete_playlist": "Delete Playlist", "delete_playlist": "Çalma Listesini Sil",
"delete_confirm": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.", "delete_confirm": "\"{{name}}\" adlı çalma listesini silmek istediğinize emin misiniz? Bu işlem geri alınamaz.",
"deleted": "Playlist deleted", "deleted": "Çalma listesi silindi",
"failed_to_delete": "Failed to delete playlist" "failed_to_delete": "Çalma listesi oluşturulamadı"
}, },
"sort": { "sort": {
"title": "Sort By", "title": "Sırala",
"alphabetical": "Alphabetical", "alphabetical": "Alfabetik",
"date_created": "Date Created" "date_created": "Oluşturulma Tarihi"
} }
}, },
"watchlists": { "watchlists": {
"title": "Watchlists", "title": "İzleme listeleri",
"my_watchlists": "My Watchlists", "my_watchlists": "İzleme listelerim",
"public_watchlists": "Public Watchlists", "public_watchlists": "Herkese açık izleme listeleri",
"create_title": "Create Watchlist", "create_title": "İzleme listesi oluştur",
"edit_title": "Edit Watchlist", "edit_title": "İzleme listesini düzenle",
"create_button": "Create Watchlist", "create_button": "İzleme listesi oluştur",
"save_button": "Save Changes", "save_button": "Değişiklikleri Kaydet",
"delete_button": "Delete", "delete_button": "Sil",
"remove_button": "Remove", "remove_button": "Kaldır",
"cancel_button": "Cancel", "cancel_button": "Vazgeç",
"name_label": "Name", "name_label": "Name",
"name_placeholder": "Enter watchlist name", "name_placeholder": "İzleme listesi adını girin",
"description_label": "Description", "description_label": "ıklama",
"description_placeholder": "Enter description (optional)", "description_placeholder": "ıklama girin (isteğe bağlı)",
"is_public_label": "Public Watchlist", "is_public_label": "Herkese açık izleme listesi",
"is_public_description": "Allow others to view this watchlist", "is_public_description": "Başkalarının da bu izleme listesini görmesine izin ver",
"allowed_type_label": "Content Type", "allowed_type_label": "İçerik Türü",
"sort_order_label": "Default Sort Order", "sort_order_label": "Varsayılan Sıralama",
"empty_title": "No Watchlists", "empty_title": "İzleme listesi yok",
"empty_description": "Create your first watchlist to start organizing your media", "empty_description": "Create your first watchlist to start organizing your media",
"empty_watchlist": "This watchlist is empty", "empty_watchlist": "Bu izleme listesi boş",
"empty_watchlist_hint": "Add items from your library to this watchlist", "empty_watchlist_hint": "Kütüphanenizdeki nesneleri bu izleme listesine ekleyin",
"not_configured_title": "Streamystats Not Configured", "not_configured_title": "Streamystats ayarlanmamış",
"not_configured_description": "Configure Streamystats in settings to use watchlists", "not_configured_description": "İzleme listelerini kullanmak için ayarlardan Streamystats'ı ayarlayın",
"go_to_settings": "Go to Settings", "go_to_settings": "Ayarlara git",
"add_to_watchlist": "Add to Watchlist", "add_to_watchlist": "İzleme Listesine Ekle",
"remove_from_watchlist": "Remove from Watchlist", "remove_from_watchlist": "İzleme Listesinden Kaldır",
"select_watchlist": "Select Watchlist", "select_watchlist": "İzleme Listesi Seç",
"create_new": "Create New Watchlist", "create_new": "Yeni İzleme Listesi Oluştur",
"item": "item", "item": "öğe",
"items": "items", "items": "öğeler",
"public": "Public", "public": "Herkese Açık",
"private": "Private", "private": "Özel",
"you": "You", "you": "Siz",
"by_owner": "By another user", "by_owner": "Başka kullanıcı tarafından",
"not_found": "Watchlist not found", "not_found": "İzleme listesi bulunamadı",
"delete_confirm_title": "Delete Watchlist", "delete_confirm_title": "İzleme listesini sil",
"delete_confirm_message": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.", "delete_confirm_message": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
"remove_item_title": "Remove from Watchlist", "remove_item_title": "İzleme Listesinden Kaldır",
"remove_item_message": "Remove \"{{name}}\" from this watchlist?", "remove_item_message": "{{name}} bu izleme listesinden kaldırılsın mı?",
"loading": "Loading watchlists...", "loading": "İzleme listeleri yükleniyor...",
"no_compatible_watchlists": "No compatible watchlists", "no_compatible_watchlists": "Uyumlu izleme listesi yok",
"create_one_first": "Create a watchlist that accepts this content type" "create_one_first": "Bu içerik türünü kabul eden bir izleme listesi oluşturun"
}, },
"playback_speed": { "playback_speed": {
"title": "Playback Speed", "title": "Oynatma Hızı",
"apply_to": "Apply To", "apply_to": "Şuna Uygula",
"speed": "Speed", "speed": "Hız",
"scope": { "scope": {
"media": "This media only", "media": "Yalnızca bu medyada",
"show": "This show", "show": "Bu dizide",
"all": "All media (default)" "all": "Bütün medyalarda (varsayılan)"
} }
} }
} }

View File

@@ -610,6 +610,12 @@
"downloaded_file_no": "No", "downloaded_file_no": "No",
"downloaded_file_cancel": "Cancel" "downloaded_file_cancel": "Cancel"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "Далі", "next_up": "Далі",
"no_items_to_display": "Немає елементів для відображення", "no_items_to_display": "Немає елементів для відображення",

View File

@@ -610,6 +610,12 @@
"downloaded_file_no": "No", "downloaded_file_no": "No",
"downloaded_file_cancel": "Cancel" "downloaded_file_cancel": "Cancel"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "Tiếp theo", "next_up": "Tiếp theo",
"no_items_to_display": "Không có nội dung để hiển thị", "no_items_to_display": "Không có nội dung để hiển thị",

View File

@@ -610,6 +610,12 @@
"downloaded_file_no": "No", "downloaded_file_no": "No",
"downloaded_file_cancel": "Cancel" "downloaded_file_cancel": "Cancel"
}, },
"chapters": {
"title": "Chapters",
"chapter_number": "Chapter {{number}}",
"open": "Open chapters",
"close": "Close chapters"
},
"item_card": { "item_card": {
"next_up": "Next Up", "next_up": "Next Up",
"no_items_to_display": "No Items to Display", "no_items_to_display": "No Items to Display",

138
utils/chapters.test.ts Normal file
View File

@@ -0,0 +1,138 @@
import { describe, expect, test } from "bun:test";
import {
chapterMarkers,
chapterNameAt,
chapterStartsMs,
currentChapterIndex,
formatChapterTime,
sortedChapters,
} from "./chapters";
// Helper: a ChapterInfo with a start in milliseconds.
const ch = (ms: number, name?: string) => ({
StartPositionTicks: ms * 10000,
Name: name,
});
describe("chapterMarkers", () => {
test("maps chapters to position + percent", () => {
expect(chapterMarkers([ch(0), ch(30_000), ch(60_000)], 120_000)).toEqual([
{ positionMs: 0, percent: 0 },
{ positionMs: 30_000, percent: 25 },
{ positionMs: 60_000, percent: 50 },
]);
});
test("drops chapters past the duration", () => {
expect(chapterMarkers([ch(0), ch(200_000)], 120_000)).toEqual([
{ positionMs: 0, percent: 0 },
]);
});
test("returns [] when duration is 0 or chapters missing", () => {
expect(chapterMarkers([ch(0)], 0)).toEqual([]);
expect(chapterMarkers(null, 120_000)).toEqual([]);
expect(chapterMarkers(undefined, 120_000)).toEqual([]);
});
test("excludes a chapter exactly at the duration", () => {
expect(chapterMarkers([ch(0), ch(120_000)], 120_000)).toEqual([
{ positionMs: 0, percent: 0 },
]);
});
test("skips chapters with no StartPositionTicks", () => {
expect(
chapterMarkers([{ StartPositionTicks: undefined }, ch(30_000)], 120_000),
).toEqual([{ positionMs: 30_000, percent: 25 }]);
});
});
describe("currentChapterIndex", () => {
const chapters = [ch(0), ch(30_000), ch(60_000)];
test("returns the chapter containing the position", () => {
expect(currentChapterIndex(0, chapters)).toBe(0);
expect(currentChapterIndex(15_000, chapters)).toBe(0);
expect(currentChapterIndex(30_000, chapters)).toBe(1);
expect(currentChapterIndex(90_000, chapters)).toBe(2);
});
test("returns -1 before the first chapter and for no chapters", () => {
expect(currentChapterIndex(-5, chapters)).toBe(-1);
expect(currentChapterIndex(10_000, [])).toBe(-1);
expect(currentChapterIndex(10_000, null)).toBe(-1);
});
});
describe("sortedChapters", () => {
test("pairs each chapter with its ms start, sorted ascending", () => {
const a = ch(60_000, "C");
const b = ch(0, "A");
const c = ch(30_000, "B");
expect(sortedChapters([a, b, c])).toEqual([
{ chapter: b, positionMs: 0 },
{ chapter: c, positionMs: 30_000 },
{ chapter: a, positionMs: 60_000 },
]);
});
test("returns [] for null/undefined", () => {
expect(sortedChapters(null)).toEqual([]);
expect(sortedChapters(undefined)).toEqual([]);
});
});
describe("chapterStartsMs", () => {
test("returns sorted ms positions", () => {
expect(chapterStartsMs([ch(60_000), ch(0), ch(30_000)])).toEqual([
0, 30_000, 60_000,
]);
});
test("skips entries without StartPositionTicks", () => {
expect(
chapterStartsMs([ch(30_000), { StartPositionTicks: undefined }, ch(0)]),
).toEqual([0, 30_000]);
});
test("returns [] for null/undefined/empty", () => {
expect(chapterStartsMs(null)).toEqual([]);
expect(chapterStartsMs(undefined)).toEqual([]);
expect(chapterStartsMs([])).toEqual([]);
});
});
describe("chapterNameAt", () => {
const named = [
{ StartPositionTicks: 0, Name: "Intro" },
{ StartPositionTicks: 30_000 * 10000, Name: "Action" },
{ StartPositionTicks: 60_000 * 10000, Name: "Outro" },
];
test("returns the chapter name for the active position", () => {
expect(chapterNameAt(0, named)).toBe("Intro");
expect(chapterNameAt(15_000, named)).toBe("Intro");
expect(chapterNameAt(45_000, named)).toBe("Action");
expect(chapterNameAt(90_000, named)).toBe("Outro");
});
test("returns null before the first chapter", () => {
expect(chapterNameAt(-1, named)).toBeNull();
});
test("returns null for null/undefined/empty chapters", () => {
expect(chapterNameAt(10_000, null)).toBeNull();
expect(chapterNameAt(10_000, undefined)).toBeNull();
expect(chapterNameAt(10_000, [])).toBeNull();
});
test("returns null when the active chapter has no Name", () => {
expect(chapterNameAt(15_000, [ch(0), ch(30_000)])).toBeNull();
});
});
describe("formatChapterTime", () => {
test("formats m:ss and h:mm:ss", () => {
expect(formatChapterTime(65_000)).toBe("1:05");
expect(formatChapterTime(3_725_000)).toBe("1:02:05");
expect(formatChapterTime(-100)).toBe("0:00");
});
});

97
utils/chapters.ts Normal file
View File

@@ -0,0 +1,97 @@
/**
* Pure helpers for Jellyfin chapter markers. Dependency-free so they are
* unit-testable under `bun test`.
*/
import type { ChapterInfo } from "@jellyfin/sdk/lib/generated-client/models";
import { ticksToMs } from "@/utils/time";
export interface ChapterMarker {
/** Chapter start, in milliseconds. */
positionMs: number;
/** Chapter start as a percentage (0-100) of the media duration. */
percent: number;
}
export interface ChapterEntry {
chapter: ChapterInfo;
/** Chapter start, in milliseconds. */
positionMs: number;
}
/** Chapters paired with their millisecond start, sorted ascending by start. */
export const sortedChapters = (
chapters: ChapterInfo[] | null | undefined,
): ChapterEntry[] =>
(chapters ?? [])
.filter((c) => c.StartPositionTicks != null)
.map((chapter) => ({
chapter,
positionMs: ticksToMs(chapter.StartPositionTicks),
}))
.sort((a, b) => a.positionMs - b.positionMs);
/** Chapter start positions in milliseconds, ascending. */
export const chapterStartsMs = (
chapters: ChapterInfo[] | null | undefined,
): number[] =>
(chapters ?? [])
.filter((c) => c.StartPositionTicks != null)
.map((c) => ticksToMs(c.StartPositionTicks))
.sort((a, b) => a - b);
/** Chapter markers within [0, durationMs]; empty when duration is unknown. */
export const chapterMarkers = (
chapters: ChapterInfo[] | null | undefined,
durationMs: number,
): ChapterMarker[] => {
if (durationMs <= 0) return [];
return chapterStartsMs(chapters)
.filter((ms) => ms >= 0 && ms < durationMs)
.map((ms) => ({ positionMs: ms, percent: (ms / durationMs) * 100 }));
};
/** Index of the chapter containing `positionMs`, or -1 if before the first. */
export const currentChapterIndex = (
positionMs: number,
chapters: ChapterInfo[] | null | undefined,
): number => {
const starts = chapterStartsMs(chapters);
let index = -1;
for (let i = 0; i < starts.length; i++) {
if (positionMs >= starts[i]) index = i;
else break;
}
return index;
};
/** Name of the chapter containing `positionMs`, or null if none / unnamed. */
export const chapterNameAt = (
positionMs: number,
chapters: ChapterInfo[] | null | undefined,
): string | null => {
// Sort once, derive both the active index and the entry from the same array
// — `chapterNameAt` runs on every playback tick, so paying for one `sort()`
// instead of two is worth the duplication of the index loop here.
const sorted = sortedChapters(chapters);
let idx = -1;
for (let i = 0; i < sorted.length; i++) {
if (positionMs >= sorted[i].positionMs) idx = i;
else break;
}
if (idx < 0) return null;
const name = sorted[idx]?.chapter.Name;
return name && name.length > 0 ? name : null;
};
/** `m:ss` (or `h:mm:ss` past an hour) label for a millisecond position. */
export const formatChapterTime = (positionMs: number): string => {
const total = Math.max(0, Math.floor(positionMs / 1000));
const hours = Math.floor(total / 3600);
const minutes = Math.floor((total % 3600) / 60);
const seconds = total % 60;
const pad = (n: number) => String(n).padStart(2, "0");
return hours > 0
? `${hours}:${pad(minutes)}:${pad(seconds)}`
: `${minutes}:${pad(seconds)}`;
};