diff --git a/Flixy/Resources/Localizable.xcstrings b/Flixy/Resources/Localizable.xcstrings index c56b0a7..df62f31 100644 --- a/Flixy/Resources/Localizable.xcstrings +++ b/Flixy/Resources/Localizable.xcstrings @@ -1,3688 +1,3689 @@ { - "sourceLanguage": "en", - "strings": { - "%@ GB": { - "comment": "A picker option in the settings view that lets the user select the maximum amount of storage space that can be used for downloaded content. The value is given in gigabytes.", - "isCommentAutoGenerated": true + "sourceLanguage" : "en", + "strings" : { + "%@ GB" : { + "comment" : "A picker option in the settings view that lets the user select the maximum amount of storage space that can be used for downloaded content. The value is given in gigabytes.", + "isCommentAutoGenerated" : true }, - "4K": { - "comment": "A label for a 4K video.", - "isCommentAutoGenerated": true + "4K" : { + "comment" : "A label for a 4K video.", + "isCommentAutoGenerated" : true }, - "720p": { - "comment": "A label for a 720p video.", - "isCommentAutoGenerated": true + "720p" : { + "comment" : "A label for a 720p video.", + "isCommentAutoGenerated" : true }, - "1080p": { - "comment": "A label for a 1080p video.", - "isCommentAutoGenerated": true + "1080p" : { + "comment" : "A label for a 1080p video.", + "isCommentAutoGenerated" : true }, - "action.apply": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Übernehmen" + "action.apply" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Übernehmen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Apply" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apply" } } } }, - "action.cancel": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Abbrechen" + "action.cancel" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abbrechen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Cancel" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancel" } } } }, - "action.clearRating": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bewertung entfernen" + "action.clearRating" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bewertung entfernen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Clear rating" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clear rating" } } } }, - "action.close": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Schließen" + "action.close" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schließen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Close" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Close" } } } }, - "action.delete": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Löschen" + "action.delete" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Löschen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Delete" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete" } } } }, - "action.markUnwatched": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Als ungesehen markieren" + "action.markUnwatched" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Als ungesehen markieren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Mark as unwatched" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mark as unwatched" } } } }, - "action.markWatched": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Als gesehen markieren" + "action.markWatched" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Als gesehen markieren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Mark as watched" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mark as watched" } } } }, - "action.rate": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bewerten" + "action.rate" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bewerten" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Rate" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rate" } } } }, - "action.retry": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Erneut versuchen" + "action.retry" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erneut versuchen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Try again" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Try again" } } } }, - "action.signOut": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Abmelden" + "action.signOut" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abmelden" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sign out" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sign out" } } } }, - "appearance.accent": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Akzentfarbe" + "appearance.accent" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Akzentfarbe" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Accent color" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Accent color" } } } }, - "appearance.accentExplanation": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Die Farbe erscheint auf Knöpfen, Fortschrittsbalken und der ausgewählten Zeile in der Seitenleiste." + "appearance.accentExplanation" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die Farbe erscheint auf Knöpfen, Fortschrittsbalken und der ausgewählten Zeile in der Seitenleiste." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "The color appears on buttons, progress bars and the selected row in the sidebar." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The color appears on buttons, progress bars and the selected row in the sidebar." } } } }, - "appearance.adjusted %@": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Die Farbe wurde auf %@ aufgehellt, damit sie auf dem dunklen Hintergrund lesbar bleibt." + "appearance.adjusted %@" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die Farbe wurde auf %@ aufgehellt, damit sie auf dem dunklen Hintergrund lesbar bleibt." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Lightened to %@ so it stays readable on the dark background." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lightened to %@ so it stays readable on the dark background." } } } }, - "appearance.customColor": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Eigene Farbe" + "appearance.customColor" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eigene Farbe" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Custom color" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Custom color" } } } }, - "appearance.hexPlaceholder": { - "comment": "Beispiel für eine Hex-Farbe", - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "#1DB954" + "appearance.hexPlaceholder" : { + "comment" : "Beispiel für eine Hex-Farbe", + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "#1DB954" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "#1DB954" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "#1DB954" } } } }, - "appearance.invalidHex": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Das ist keine gültige Farbe. Erwartet wird etwas wie #1DB954." + "appearance.invalidHex" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Das ist keine gültige Farbe. Erwartet wird etwas wie #1DB954." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "That's not a valid color. Try something like #1DB954." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "That's not a valid color. Try something like #1DB954." } } } }, - "appearance.preview": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Vorschau" + "appearance.preview" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vorschau" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Preview" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Preview" } } } }, - "appearance.title": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Erscheinungsbild" + "appearance.title" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erscheinungsbild" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Appearance" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Appearance" } } } }, - "connecting.searching": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Server werden gesucht …" + "connecting.searching" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Server werden gesucht …" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Looking for servers…" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Looking for servers…" } } } }, - "connecting.to %@": { - "comment": "Platzhalter: Servername", - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Verbinde mit %@ …" + "connecting.to %@" : { + "comment" : "Platzhalter: Servername", + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbinde mit %@ …" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Connecting to %@…" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connecting to %@…" } } } }, - "detail.cast": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Besetzung" + "detail.cast" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Besetzung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Cast" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cast" } } } }, - "detail.episodeCount %lld": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%lld Folgen" + "detail.episodeCount %lld" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld Folgen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "%lld episodes" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld episodes" } } } }, - "detail.episodes": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Folgen" + "detail.episodes" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Folgen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Episodes" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Episodes" } } } }, - "detail.playFromStart": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Von vorn abspielen" + "detail.playFromStart" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Von vorn abspielen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Play from start" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Play from start" } } } }, - "detail.seasons": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Staffeln" + "detail.seasons" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Staffeln" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Seasons" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seasons" } } } }, - "detail.unwatchedCount %lld": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%lld ungesehen" + "detail.unwatchedCount %lld" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld ungesehen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "%lld unwatched" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld unwatched" } } } }, - "detail.versions": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Fassung" + "detail.versions" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fassung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Version" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Version" } } } }, - "diagnostics.connection": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Verbindung" + "diagnostics.connection" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbindung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Connection" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connection" } } } }, - "diagnostics.connectionUsed": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Verbindung" + "diagnostics.connectionUsed" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbindung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Connection" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connection" } } } }, - "diagnostics.copy": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bericht kopieren" + "diagnostics.copy" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bericht kopieren" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Copy report" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copy report" } } } }, - "diagnostics.copyHint": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Enthält keinen Token. Kannst du unbesorgt weitergeben." + "diagnostics.copyHint" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enthält keinen Token. Kannst du unbesorgt weitergeben." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Contains no token. Safe to share." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contains no token. Safe to share." } } } }, - "diagnostics.lastAttempt": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Letzter Abspielversuch" + "diagnostics.lastAttempt" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Letzter Abspielversuch" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Last playback attempt" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Last playback attempt" } } } }, - "diagnostics.method": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Weg" + "diagnostics.liveTV" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Live-TV-Antwort ansehen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Method" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inspect Live TV response" } } } }, - "diagnostics.noServer": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Keine Serververbindung." + "diagnostics.liveTVHint" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zeigt den Aufbau der Serverantwort — nur Schlüssel, Anzahl und Sendernamen, keine Zugangsdaten. Hilft dabei zu finden, wo die Sender stehen." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "No server connection." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Shows the structure of the server's response — keys, counts and channel names only, no credentials. Helps locate where the channels are." } } } }, - "diagnostics.nothingYet": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Noch nichts abgespielt." + "diagnostics.method" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Weg" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Nothing played yet." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Method" } } } }, - "diagnostics.relayWarning": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Über die Plex-Vermittlung. Stark gedrosselt — große Dateien laufen darüber kaum an." + "diagnostics.noServer" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Serververbindung." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Via the Plex relay. Heavily throttled — large files barely start over it." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No server connection." } } } }, - "diagnostics.source": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Quelle (ohne Token)" + "diagnostics.nothingYet" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Noch nichts abgespielt." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Source (token removed)" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nothing played yet." } } } }, - "diagnostics.steps": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Ablauf" + "diagnostics.relayWarning" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Über die Plex-Vermittlung. Stark gedrosselt — große Dateien laufen darüber kaum an." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Timeline" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Via the Plex relay. Heavily throttled — large files barely start over it." } } } }, - "diagnostics.title": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Diagnose" + "diagnostics.source" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Quelle (ohne Token)" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Diagnostics" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Source (token removed)" } } } }, - "diagnostics.title.label": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Titel" + "diagnostics.steps" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ablauf" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Title" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Timeline" } } } }, - "downloads.active": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Läuft gerade" + "diagnostics.title" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Diagnose" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "In progress" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Diagnostics" } } } }, - "downloads.allEpisodes": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Alle Folgen laden" + "diagnostics.title.label" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Titel" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Download all episodes" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Title" } } } }, - "downloads.available": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Offline verfügbar" + "downloads.active" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Läuft gerade" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Available offline" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "In progress" } } } }, - "downloads.batch.queued %lld": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%lld Folgen in die Warteschlange gelegt." + "downloads.allEpisodes" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alle Folgen laden" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Queued %lld episodes." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Download all episodes" } } } }, - "downloads.batch.title": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Downloads" + "downloads.available" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Offline verfügbar" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Downloads" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Available offline" } } } }, - "downloads.batch.unavailable %lld": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%lld Folgen haben keine abspielbare Datei auf dem Server." + "downloads.batch.queued %lld" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld Folgen in die Warteschlange gelegt." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "%lld episodes have no playable file on the server." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Queued %lld episodes." } } } }, - "downloads.cancel": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Download abbrechen" + "downloads.batch.title" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Downloads" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Cancel download" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Downloads" } } } }, - "downloads.download": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Herunterladen" + "downloads.batch.unavailable %lld" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld Folgen haben keine abspielbare Datei auf dem Server." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Download" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld episodes have no playable file on the server." } } } }, - "downloads.empty.description": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Lade Filme und Folgen herunter, um sie ohne Verbindung anzusehen." + "downloads.cancel" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Download abbrechen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Download movies and episodes to watch them without a connection." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancel download" } } } }, - "downloads.empty.title": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Nichts offline" + "downloads.download" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Herunterladen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Nothing offline" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Download" } } } }, - "downloads.failed": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Fehlgeschlagen" + "downloads.empty.description" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lade Filme und Folgen herunter, um sie ohne Verbindung anzusehen." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Failed" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Download movies and episodes to watch them without a connection." } } } }, - "downloads.movies": { - "comment": "Überschrift für heruntergeladene Filme", - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Filme" + "downloads.empty.title" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nichts offline" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Movies" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nothing offline" } } } }, - "downloads.pause": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Anhalten" + "downloads.failed" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fehlgeschlagen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Pause" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Failed" } } } }, - "downloads.paused": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Angehalten" + "downloads.movies" : { + "comment" : "Überschrift für heruntergeladene Filme", + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filme" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Paused" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Movies" } } } }, - "downloads.quality.approx %@ %@": { - "comment": "Platzhalter: Stufenname, geschätzte Größe", - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%@ · etwa %@" + "downloads.pause" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anhalten" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "%@ · about %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pause" } } } }, - "downloads.quality.hint": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Niedrigere Stufen rechnet der Server unterwegs herunter. Das dauert länger, spart aber deutlich Platz." + "downloads.paused" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Angehalten" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Lower levels are transcoded by the server. That takes longer but saves a lot of space." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paused" } } } }, - "downloads.quality.title": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "In welcher Qualität?" + "downloads.quality.approx %@ %@" : { + "comment" : "Platzhalter: Stufenname, geschätzte Größe", + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ · etwa %@" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Which quality?" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ · about %@" } } } }, - "downloads.queued": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wartet" + "downloads.quality.hint" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Niedrigere Stufen rechnet der Server unterwegs herunter. Das dauert länger, spart aber deutlich Platz." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Queued" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lower levels are transcoded by the server. That takes longer but saves a lot of space." } } } }, - "downloads.refused.exists": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Dieser Titel steht bereits in der Warteschlange." + "downloads.quality.title" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "In welcher Qualität?" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "This title is already queued." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Which quality?" } } } }, - "downloads.refused.limit %@ %@": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Das Speicherlimit ist erreicht: %@ von %@ belegt." + "downloads.queued" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wartet" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Storage limit reached: %@ of %@ used." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Queued" } } } }, - "downloads.refused.noMedia": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zu diesem Titel liegt keine Datei auf dem Server." + "downloads.refused.exists" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dieser Titel steht bereits in der Warteschlange." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "There's no file for this title on the server." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "This title is already queued." } } } }, - "downloads.refused.noPart": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Die Datei auf dem Server hat keinen abrufbaren Teil. Möglicherweise wurde sie noch nicht analysiert." + "downloads.refused.limit %@ %@" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Das Speicherlimit ist erreicht: %@ von %@ belegt." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "The file on the server has no retrievable part. It may not have been analyzed yet." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Storage limit reached: %@ of %@ used." } } } }, - "downloads.refused.noServer": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Keine Verbindung zum Server." + "downloads.refused.noMedia" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zu diesem Titel liegt keine Datei auf dem Server." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "No connection to the server." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "There's no file for this title on the server." } } } }, - "downloads.refused.noURL": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Die Download-Adresse ließ sich nicht bilden." + "downloads.refused.noPart" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die Datei auf dem Server hat keinen abrufbaren Teil. Möglicherweise wurde sie noch nicht analysiert." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "The download address couldn't be built." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The file on the server has no retrievable part. It may not have been analyzed yet." } } } }, - "downloads.refused.space %@ %@": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zu wenig Platz auf dem Gerät: %@ nötig, %@ frei." + "downloads.refused.noServer" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Verbindung zum Server." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Not enough space on this device: %@ needed, %@ free." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No connection to the server." } } } }, - "downloads.refused.title": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Download nicht möglich" + "downloads.refused.noURL" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die Download-Adresse ließ sich nicht bilden." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Can't download" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The download address couldn't be built." } } } }, - "downloads.removeConfirm": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Download entfernen?" + "downloads.refused.space %@ %@" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zu wenig Platz auf dem Gerät: %@ nötig, %@ frei." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Remove download?" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Not enough space on this device: %@ needed, %@ free." } } } }, - "downloads.resume": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Fortsetzen" + "downloads.refused.title" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Download nicht möglich" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Resume" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Can't download" } } } }, - "downloads.season": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Staffel laden" + "downloads.removeConfirm" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Download entfernen?" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Download season" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Remove download?" } } } }, - "downloads.used": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Belegt" + "downloads.resume" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fortsetzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Used" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Resume" } } } }, - "downloads.wifiOnly": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Nur über WLAN laden" + "downloads.season" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Staffel laden" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Download over Wi-Fi only" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Download season" } } } }, - "episode.short %lld %lld": { - "comment": "Staffel- und Folgennummer", - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "S%lld E%lld" + "downloads.used" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Belegt" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "S%lld E%lld" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Used" } } } }, - "error.noServers": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "An deinem Plex-Konto hängt kein Server." + "downloads.wifiOnly" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nur über WLAN laden" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "There's no server on your Plex account." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Download over Wi-Fi only" } } } }, - "error.notFound": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Dieser Titel ist auf dem Server nicht mehr vorhanden." + "episode.short %lld %lld" : { + "comment" : "Staffel- und Folgennummer", + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "S%lld E%lld" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "This title is no longer on the server." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "S%lld E%lld" } } } }, - "error.server %lld": { - "comment": "Platzhalter: HTTP-Statuscode", - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Der Server hat mit Fehler %lld geantwortet." + "error.noServers" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "An deinem Plex-Konto hängt kein Server." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "The server responded with error %lld." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "There's no server on your Plex account." } } } }, - "error.serverUnreachable": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Der Server ist nicht erreichbar. Prüfe, ob er läuft und ob du im selben Netzwerk bist." + "error.notFound" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dieser Titel ist auf dem Server nicht mehr vorhanden." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "The server can't be reached. Check that it's running and that you're on the same network." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "This title is no longer on the server." } } } }, - "error.title": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Das hat nicht geklappt" + "error.server %lld" : { + "comment" : "Platzhalter: HTTP-Statuscode", + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der Server hat mit Fehler %lld geantwortet." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Something went wrong" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The server responded with error %lld." } } } }, - "error.unauthorized": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Die Anmeldung ist abgelaufen. Melde dich erneut an." + "error.serverUnreachable" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der Server ist nicht erreichbar. Prüfe, ob er läuft und ob du im selben Netzwerk bist." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Your sign-in has expired. Please sign in again." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The server can't be reached. Check that it's running and that you're on the same network." } } } }, - "error.unexpectedResponse": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Die Antwort des Servers war unerwartet. Möglicherweise ist eine neuere Plex-Version installiert." + "error.title" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Das hat nicht geklappt" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "The server's response was unexpected. It may be running a newer version of Plex." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Something went wrong" } } } }, - "filter.any": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Alle" + "error.unauthorized" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die Anmeldung ist abgelaufen. Melde dich erneut an." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Any" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Your sign-in has expired. Please sign in again." } } } }, - "filter.filters": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Filter" + "error.unexpectedResponse" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die Antwort des Servers war unerwartet. Möglicherweise ist eine neuere Plex-Version installiert." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Filters" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The server's response was unexpected. It may be running a newer version of Plex." } } } }, - "filter.genre": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Genre" + "filter.any" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alle" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Genre" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Any" } } } }, - "filter.reset": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zurücksetzen" + "filter.filters" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filter" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Reset" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filters" } } } }, - "filter.resolution": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Auflösung" + "filter.genre" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Genre" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Resolution" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Genre" } } } }, - "filter.sort": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sortierung" + "filter.reset" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zurücksetzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sort by" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reset" } } } }, - "filter.title": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Filter" + "filter.resolution" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Auflösung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Filters" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Resolution" } } } }, - "filter.unwatchedOnly": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Nur Ungesehene" + "filter.sort" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sortierung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Unwatched only" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sort by" } } } }, - "hero.details": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Details" + "filter.title" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filter" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Details" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filters" } } } }, - "hero.play": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Abspielen" + "filter.unwatchedOnly" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nur Ungesehene" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Play" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unwatched only" } } } }, - "hero.resume": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Weiterschauen" + "hero.details" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Details" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Resume" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Details" } } } }, - "home.empty.description": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sobald dein Server Mediatheken enthält, erscheinen sie hier." + "hero.play" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abspielen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Once your server has libraries, they'll show up here." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Play" } } } }, - "home.empty.title": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Noch nichts da" + "hero.resume" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Weiterschauen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Nothing here yet" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Resume" } } } }, - "homeLayout.empty": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Der Server liefert derzeit keine Reihen." + "home.empty.description" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sobald dein Server Mediatheken enthält, erscheinen sie hier." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "The server isn't providing any rows right now." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Once your server has libraries, they'll show up here." } } } }, - "homeLayout.explanation": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Schalte Reihen aus, die du nicht brauchst, und sortiere sie über „Bearbeiten“ um. Neue Reihen vom Server erscheinen am Ende." + "home.empty.title" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Noch nichts da" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Turn off rows you don't need and reorder them via Edit. New rows from the server appear at the end." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nothing here yet" } } } }, - "homeLayout.title": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Startseite" + "homeLayout.empty" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der Server liefert derzeit keine Reihen." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Home screen" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The server isn't providing any rows right now." } } } }, - "item.watched": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Gesehen" + "homeLayout.explanation" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schalte Reihen aus, die du nicht brauchst, und sortiere sie über „Bearbeiten“ um. Neue Reihen vom Server erscheinen am Ende." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Watched" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Turn off rows you don't need and reorder them via Edit. New rows from the server appear at the end." } } } }, - "library.clearFilters": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Filter zurücksetzen" + "homeLayout.title" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Startseite" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Clear filters" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Home screen" } } } }, - "library.empty.description": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Mit den aktuellen Filtern bleibt nichts übrig." + "item.watched" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gesehen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Nothing matches the current filters." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Watched" } } } }, - "library.empty.title": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Keine Titel" + "library.clearFilters" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filter zurücksetzen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "No titles" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clear filters" } } } }, - "library.filters": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Filter" + "library.empty.description" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mit den aktuellen Filtern bleibt nichts übrig." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Filters" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nothing matches the current filters." } } } }, - "library.scan": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Mediathek durchsuchen" + "library.empty.title" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Titel" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Scan library" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No titles" } } } }, - "library.toggleLayout": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Ansicht wechseln" + "library.filters" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filter" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Switch layout" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filters" } } } }, - "liveTV.next %@ %@": { - "comment": "Platzhalter: Uhrzeit, Sendungstitel", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Danach %@ · %@" + "library.scan" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mediathek durchsuchen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Next %@ · %@" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Scan library" } } } }, - "liveTV.noChannels.description": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Der Empfänger ist eingerichtet, liefert aber keine Sender." + "library.toggleLayout" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ansicht wechseln" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "The tuner is set up but isn't providing any channels." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Switch layout" } } } }, - "liveTV.noChannels.title": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Keine Sender" + "liveTV.next %@ %@" : { + "comment" : "Platzhalter: Uhrzeit, Sendungstitel", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Danach %@ · %@" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "No channels" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Next %@ · %@" } } } }, - "liveTV.noProgram": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Kein Programm verfügbar" + "liveTV.noChannels.description" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der Empfänger ist eingerichtet, liefert aber keine Sender." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "No guide data" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The tuner is set up but isn't providing any channels." } } } }, - "liveTV.notConfigured.description": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Live-TV braucht eine Fernsehkarte oder einen Netzwerk-Tuner am Plex-Server. Richte ihn in den Servereinstellungen ein, dann erscheinen die Sender hier." + "liveTV.noChannels.title" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Sender" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Live TV needs a tuner card or network tuner on your Plex server. Set one up in the server settings and channels will appear here." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No channels" } } } }, - "liveTV.notConfigured.title": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Kein Empfänger eingerichtet" + "liveTV.noProgram" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kein Programm verfügbar" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "No tuner set up" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No guide data" } } } }, - "placeholder.description": { - "comment": "Platzhalter-Bildschirme", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Dieser Bereich entsteht in einer späteren Phase." + "liveTV.notConfigured.description" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Live-TV braucht eine Fernsehkarte oder einen Netzwerk-Tuner am Plex-Server. Richte ihn in den Servereinstellungen ein, dann erscheinen die Sender hier." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "This area is coming in a later phase." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Live TV needs a tuner card or network tuner on your Plex server. Set one up in the server settings and channels will appear here." } } } }, - "player.audioTracks": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Tonspur" + "liveTV.notConfigured.title" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kein Empfänger eingerichtet" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Audio" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No tuner set up" } } } }, - "player.back10": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "10 Sekunden zurück" + "placeholder.description" : { + "comment" : "Platzhalter-Bildschirme", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dieser Bereich entsteht in einer späteren Phase." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Back 10 seconds" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "This area is coming in a later phase." } } } }, - "player.close": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Player schließen" + "player.audioTracks" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tonspur" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Close player" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Audio" } } } }, - "player.forward10": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "10 Sekunden vor" + "player.back10" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "10 Sekunden zurück" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Forward 10 seconds" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Back 10 seconds" } } } }, - "player.loadFailed": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Die Datei ließ sich nicht öffnen." + "player.close" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Player schließen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "The file couldn't be opened." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Close player" } } } }, - "player.nextEpisode": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Nächste Folge" + "player.forward10" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "10 Sekunden vor" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Next episode" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Forward 10 seconds" } } } }, - "player.noPlayableFile": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zu diesem Titel gibt es keine abspielbare Datei auf dem Server." + "player.loadFailed" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die Datei ließ sich nicht öffnen." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "There's no playable file for this title on the server." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The file couldn't be opened." } } } }, - "player.pause": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Pause" + "player.nextEpisode" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nächste Folge" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Pause" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Next episode" } } } }, - "player.play": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Abspielen" + "player.noPlayableFile" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zu diesem Titel gibt es keine abspielbare Datei auf dem Server." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Play" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "There's no playable file for this title on the server." } } } }, - "player.playbackFailed": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Die Wiedergabe wurde unterbrochen." + "player.pause" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pause" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Playback was interrupted." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pause" } } } }, - "player.preparing": { - "comment": "Hinweis, während der Player die Datei öffnet", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wird vorbereitet …" + "player.play" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abspielen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Preparing…" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Play" } } } }, - "player.quality": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Qualität" + "player.playbackFailed" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die Wiedergabe wurde unterbrochen." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Quality" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Playback was interrupted." } } } }, - "player.relayWarning": { - "comment": "Warnung im Player, wenn die Verbindung über die Plex-Vermittlung läuft", - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Die Verbindung läuft über die Vermittlung von Plex und ist stark gedrosselt. Große Dateien laufen darüber kaum an." + "player.preparing" : { + "comment" : "Hinweis, während der Player die Datei öffnet", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wird vorbereitet …" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "You're connected through the Plex relay, which is heavily throttled. Large files barely start over it." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Preparing…" } } } }, - "player.skipCredits": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Abspann überspringen" + "player.quality" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Qualität" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Skip credits" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Quality" } } } }, - "player.skipIntro": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Intro überspringen" + "player.relayWarning" : { + "comment" : "Warnung im Player, wenn die Verbindung über die Plex-Vermittlung läuft", + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die Verbindung läuft über die Vermittlung von Plex und ist stark gedrosselt. Große Dateien laufen darüber kaum an." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Skip intro" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "You're connected through the Plex relay, which is heavily throttled. Large files barely start over it." } } } }, - "player.subtitlesOff": { - "comment": "Eintrag, der die Untertitel abschaltet", - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Aus" + "player.skipCredits" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abspann überspringen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Off" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skip credits" } } } }, - "player.subtitleTracks": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Untertitel" + "player.skipIntro" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intro überspringen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Subtitles" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skip intro" } } } }, - "player.tracks": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Ton und Untertitel" + "player.subtitlesOff" : { + "comment" : "Eintrag, der die Untertitel abschaltet", + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aus" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Audio and subtitles" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Off" } } } }, - "player.transcodeRefused": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Der Server hat die Wiedergabe abgelehnt." + "player.subtitleTracks" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Untertitel" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "The server refused playback." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Subtitles" } } } }, - "quality.automatic": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Automatisch" + "player.tracks" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ton und Untertitel" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Automatic" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Audio and subtitles" } } } }, - "quality.fullHD": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Full HD · 1080p" + "player.transcodeRefused" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der Server hat die Wiedergabe abgelehnt." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Full HD · 1080p" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The server refused playback." } } } }, - "quality.hd": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "HD · 720p" + "quality.automatic" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatisch" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "HD · 720p" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Automatic" } } } }, - "quality.high": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Hoch (12 Mbit/s)" + "quality.fullHD" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Full HD · 1080p" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "High (12 Mbps)" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Full HD · 1080p" } } } }, - "quality.low": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Niedrig (4 Mbit/s)" + "quality.hd" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "HD · 720p" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Low (4 Mbps)" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "HD · 720p" } } } }, - "quality.lowRes": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sparsam · 480p" + "quality.high" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hoch (12 Mbit/s)" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Data saver · 480p" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "High (12 Mbps)" } } } }, - "quality.medium": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Mittel (8 Mbit/s)" + "quality.low" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Niedrig (4 Mbit/s)" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Medium (8 Mbps)" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Low (4 Mbps)" } } } }, - "quality.minimal": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sparsam (2 Mbit/s)" + "quality.lowRes" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sparsam · 480p" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Data saver (2 Mbps)" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Data saver · 480p" } } } }, - "quality.original": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Original" + "quality.medium" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mittel (8 Mbit/s)" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Original" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Medium (8 Mbps)" } } } }, - "runtime.hoursMinutes %lld %lld": { - "comment": "Laufzeit mit Stunden", - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%lld Std %lld Min" + "quality.minimal" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sparsam (2 Mbit/s)" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "%lldh %lldm" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Data saver (2 Mbps)" } } } }, - "runtime.minutes %lld": { - "comment": "Laufzeit unter einer Stunde", - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%lld Min" + "quality.original" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Original" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "%lldm" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Original" } } } }, - "SD": { - "comment": "A label for a 720p movie.", - "isCommentAutoGenerated": true - }, - "search.hint": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Suche nach Filmen, Serien, Folgen und Personen." + "runtime.hoursMinutes %lld %lld" : { + "comment" : "Laufzeit mit Stunden", + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld Std %lld Min" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Search for movies, shows, episodes and people." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lldh %lldm" } } } }, - "search.recent": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zuletzt gesucht" + "runtime.minutes %lld" : { + "comment" : "Laufzeit unter einer Stunde", + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld Min" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Recent searches" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lldm" } } } }, - "search.title": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Suche" + "SD" : { + "comment" : "A label for a 720p movie.", + "isCommentAutoGenerated" : true + }, + "search.hint" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suche nach Filmen, Serien, Folgen und Personen." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Search" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Search for movies, shows, episodes and people." } } } }, - "serverPicker.owned": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Eigener Server" + "search.recent" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zuletzt gesucht" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Your server" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recent searches" } } } }, - "serverPicker.shared": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Geteilt" + "search.title" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suche" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Shared" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Search" } } } }, - "serverPicker.title": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Server wählen" + "serverPicker.owned" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eigener Server" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Choose a server" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Your server" } } } }, - "settings.address": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Adresse" + "serverPicker.shared" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geteilt" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Address" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Shared" } } } }, - "settings.app": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "App" + "serverPicker.title" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Server wählen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "App" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Choose a server" } } } }, - "settings.autoPlayNext": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Nächste Folge automatisch" + "settings.address" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adresse" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Play next episode automatically" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Address" } } } }, - "settings.autoSkipCredits": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Abspann automatisch überspringen" + "settings.app" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "App" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Skip credits automatically" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "App" } } } }, - "settings.autoSkipIntro": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Intro automatisch überspringen" + "settings.autoPlayNext" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nächste Folge automatisch" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Skip intros automatically" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Play next episode automatically" } } } }, - "settings.build": { - "comment": "Zeitpunkt, zu dem der laufende Build entstanden ist", - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Build" + "settings.autoSkipCredits" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abspann automatisch überspringen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Build" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skip credits automatically" } } } }, - "settings.connection": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Verbindung" + "settings.autoSkipIntro" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Intro automatisch überspringen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Connection" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skip intros automatically" } } } }, - "settings.connectionLocal": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Lokal" + "settings.build" : { + "comment" : "Zeitpunkt, zu dem der laufende Build entstanden ist", + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Build" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Local" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Build" } } } }, - "settings.connectionRelay": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Über Plex-Vermittlung (gedrosselt)" + "settings.connection" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verbindung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Via Plex relay (throttled)" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connection" } } } }, - "settings.connectionRemote": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Direkt von außen" + "settings.connectionLocal" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lokal" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Direct remote" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Local" } } } }, - "settings.deleteAfterDay": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Nach einem Tag" + "settings.connectionRelay" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Über Plex-Vermittlung (gedrosselt)" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "After a day" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Via Plex relay (throttled)" } } } }, - "settings.deleteAfterWeek": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Nach einer Woche" + "settings.connectionRemote" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Direkt von außen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "After a week" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Direct remote" } } } }, - "settings.deleteImmediately": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Sofort" + "settings.deleteAfterDay" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nach einem Tag" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Immediately" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "After a day" } } } }, - "settings.deleteNever": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Nie" + "settings.deleteAfterWeek" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nach einer Woche" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Never" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "After a week" } } } }, - "settings.deleteWatched": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Gesehenes löschen" + "settings.deleteImmediately" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sofort" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Delete watched" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Immediately" } } } }, - "settings.downloadsExplanation": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Fehlende Folgen werden nachgeladen, sobald du eine gesehen hast. Gesehenes wird nach der eingestellten Frist entfernt." + "settings.deleteNever" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nie" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Missing episodes are downloaded once you finish one. Watched titles are removed after the chosen delay." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Never" } } } }, - "settings.keepCount %@": { - "comment": "A label that lets the user choose how many episodes of a series to keep after they've finished watching them all. The argument is the number of episodes to keep.", - "isCommentAutoGenerated": true - }, - "settings.keepCount %lld": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "%lld ungesehene" + "settings.deleteWatched" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gesehenes löschen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "%lld unwatched" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete watched" } } } }, - "settings.keepOff": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Aus" + "settings.downloadsExplanation" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fehlende Folgen werden nachgeladen, sobald du eine gesehen hast. Gesehenes wird nach der eingestellten Frist entfernt." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Off" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Missing episodes are downloaded once you finish one. Watched titles are removed after the chosen delay." } } } }, - "settings.keepUnwatched": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Folgen vorhalten" + "settings.keepCount %@" : { + "comment" : "A label that lets the user choose how many episodes of a series to keep after they've finished watching them all. The argument is the number of episodes to keep.", + "isCommentAutoGenerated" : true + }, + "settings.keepCount %lld" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld ungesehene" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Keep episodes ready" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld unwatched" } } } }, - "settings.libraries": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Mediatheken" + "settings.keepOff" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aus" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Libraries" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Off" } } } }, - "settings.playback": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wiedergabe" + "settings.keepUnwatched" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Folgen vorhalten" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Playback" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keep episodes ready" } } } }, - "settings.quality": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Qualität" + "settings.libraries" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mediatheken" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Quality" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Libraries" } } } }, - "settings.qualityCellular": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Über Mobilfunk" + "settings.playback" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wiedergabe" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "On cellular" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Playback" } } } }, - "settings.qualityExplanation": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wird eine Datei größer als die eingestellte Grenze, rechnet der Server sie unterwegs herunter und passt die Bitrate laufend an die Verbindung an." + "settings.quality" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Qualität" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "If a file exceeds the limit, the server transcodes it on the fly and adapts the bitrate to your connection." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Quality" } } } }, - "settings.qualityWifi": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Im WLAN" + "settings.qualityCellular" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Über Mobilfunk" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "On Wi-Fi" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "On cellular" } } } }, - "settings.server": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Server" + "settings.qualityExplanation" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wird eine Datei größer als die eingestellte Grenze, rechnet der Server sie unterwegs herunter und passt die Bitrate laufend an die Verbindung an." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Server" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "If a file exceeds the limit, the server transcodes it on the fly and adapts the bitrate to your connection." } } } }, - "settings.serverName": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Name" + "settings.qualityWifi" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Im WLAN" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Name" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "On Wi-Fi" } } } }, - "settings.signOutConfirm": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wirklich abmelden?" + "settings.server" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Server" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sign out?" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Server" } } } }, - "settings.signOutExplanation": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Das Token wird vom Gerät entfernt. Heruntergeladene Titel bleiben erhalten." + "settings.serverName" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Name" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "The token will be removed from this device. Downloaded titles are kept." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Name" } } } }, - "settings.storageLimit": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Speicherlimit" + "settings.signOutConfirm" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wirklich abmelden?" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Storage limit" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sign out?" } } } }, - "settings.storageUnlimited": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Unbegrenzt" + "settings.signOutExplanation" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Das Token wird vom Gerät entfernt. Heruntergeladene Titel bleiben erhalten." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Unlimited" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The token will be removed from this device. Downloaded titles are kept." } } } }, - "settings.version": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Version" + "settings.storageLimit" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Speicherlimit" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Version" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Storage limit" } } } }, - "sidebar.close": { - "comment": "Barrierefreiheit: abgedunkelte Fläche", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Seitenleiste schließen" + "settings.storageUnlimited" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unbegrenzt" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Close sidebar" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Unlimited" } } } }, - "sidebar.downloads": { - "comment": "Eintrag in der Seitenleiste", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Downloads" + "settings.version" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Version" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Downloads" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Version" } } } }, - "sidebar.home": { - "comment": "Eintrag der Startseite in der Seitenleiste", - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Start" + "sidebar.close" : { + "comment" : "Barrierefreiheit: abgedunkelte Fläche", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seitenleiste schließen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Home" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Close sidebar" } } } }, - "sidebar.libraries": { - "comment": "Überschrift über der Liste der Mediatheken", - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Mediatheken" + "sidebar.downloads" : { + "comment" : "Eintrag in der Seitenleiste", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Downloads" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Libraries" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Downloads" } } } }, - "sidebar.liveTV": { - "comment": "Eintrag in der Seitenleiste", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Live-TV" + "sidebar.home" : { + "comment" : "Eintrag der Startseite in der Seitenleiste", + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Start" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Live TV" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Home" } } } }, - "sidebar.more": { - "comment": "Überschrift über Live-TV, Downloads und Einstellungen", - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Mehr" + "sidebar.libraries" : { + "comment" : "Überschrift über der Liste der Mediatheken", + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mediatheken" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "More" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Libraries" } } } }, - "sidebar.open": { - "comment": "Barrierefreiheit: Hamburger-Knopf", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Seitenleiste öffnen" + "sidebar.liveTV" : { + "comment" : "Eintrag in der Seitenleiste", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Live-TV" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Open sidebar" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Live TV" } } } }, - "sidebar.settings": { - "comment": "Eintrag in der Seitenleiste", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Einstellungen" + "sidebar.more" : { + "comment" : "Überschrift über Live-TV, Downloads und Einstellungen", + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mehr" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Settings" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "More" } } } }, - "signIn.action": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Mit Plex anmelden" + "sidebar.open" : { + "comment" : "Barrierefreiheit: Hamburger-Knopf", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seitenleiste öffnen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sign in with Plex" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open sidebar" } } } }, - "signIn.cancelled": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Anmeldung abgebrochen." + "sidebar.settings" : { + "comment" : "Eintrag in der Seitenleiste", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Einstellungen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sign-in cancelled." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Settings" } } } }, - "signIn.manualHint": { - "comment": "Hinweis, wenn die Browser-Verknüpfung nicht greift", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Hat das Fenster nicht verknüpft? Öffne plex.tv/link und gib diesen Code ein:" + "signIn.action" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mit Plex anmelden" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Window didn't link? Open plex.tv/link and enter this code:" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sign in with Plex" } } } }, - "signIn.privacy": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Die Anmeldemaske kommt von Plex. Dein Passwort erreicht diese App nie." + "signIn.cancelled" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anmeldung abgebrochen." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "The sign-in page comes from Plex. Your password never reaches this app." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sign-in cancelled." } } } }, - "signIn.tagline": { - "comment": "Untertitel auf dem Anmeldebildschirm", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Deine Plex-Mediathek, schöner." + "signIn.manualHint" : { + "comment" : "Hinweis, wenn die Browser-Verknüpfung nicht greift", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hat das Fenster nicht verknüpft? Öffne plex.tv/link und gib diesen Code ein:" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Your Plex library, nicer." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Window didn't link? Open plex.tv/link and enter this code:" } } } }, - "signIn.timedOut": { - "comment": "Die Anmeldung wurde nicht innerhalb der Gültigkeit des Codes abgeschlossen", - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Die Anmeldung wurde nicht abgeschlossen. Versuch es noch einmal." + "signIn.privacy" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die Anmeldemaske kommt von Plex. Dein Passwort erreicht diese App nie." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Sign-in wasn't completed. Please try again." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The sign-in page comes from Plex. Your password never reaches this app." } } } }, - "signIn.windowFailed": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Das Anmeldefenster ließ sich nicht öffnen." + "signIn.tagline" : { + "comment" : "Untertitel auf dem Anmeldebildschirm", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deine Plex-Mediathek, schöner." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "The sign-in window couldn't be opened." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Your Plex library, nicer." } } } }, - "sort.lastViewed": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zuletzt gesehen" + "signIn.timedOut" : { + "comment" : "Die Anmeldung wurde nicht innerhalb der Gültigkeit des Codes abgeschlossen", + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Die Anmeldung wurde nicht abgeschlossen. Versuch es noch einmal." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Last watched" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sign-in wasn't completed. Please try again." } } } }, - "sort.rating": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bewertung" + "signIn.windowFailed" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Das Anmeldefenster ließ sich nicht öffnen." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Rating" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The sign-in window couldn't be opened." } } } }, - "sort.recentlyAdded": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zuletzt hinzugefügt" + "sort.lastViewed" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zuletzt gesehen" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Recently added" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Last watched" } } } }, - "sort.releaseDate": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Erscheinungsjahr" + "sort.rating" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bewertung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Release date" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rating" } } } }, - "sort.titleAscending": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Titel (A–Z)" + "sort.recentlyAdded" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zuletzt hinzugefügt" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Title (A–Z)" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Recently added" } } } }, - "sort.titleDescending": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Titel (Z–A)" + "sort.releaseDate" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erscheinungsjahr" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Title (Z–A)" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Release date" } } } }, - "technical.audio": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Tonspur" + "sort.titleAscending" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Titel (A–Z)" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Audio" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Title (A–Z)" } } } }, - "technical.bitrate": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bitrate" + "sort.titleDescending" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Titel (Z–A)" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Bitrate" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Title (Z–A)" } } } }, - "technical.container": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Container" + "technical.audio" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tonspur" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Container" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Audio" } } } }, - "technical.directPlayNative": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Direkt (Systemplayer)" + "technical.bitrate" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bitrate" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Direct (system player)" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bitrate" } } } }, - "technical.directPlayVLC": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Direkt (VLC)" + "technical.container" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Container" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Direct (VLC)" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Container" } } } }, - "technical.file": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Datei auf dem Server" + "technical.directPlayNative" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Direkt (Systemplayer)" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "File on server" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Direct (system player)" } } } }, - "technical.playbackMethod": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Wiedergabe" + "technical.directPlayVLC" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Direkt (VLC)" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Playback" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Direct (VLC)" } } } }, - "technical.reason": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Grund" + "technical.file" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datei auf dem Server" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Reason" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "File on server" } } } }, - "technical.reason.audioCodec %@": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "iOS kann %@-Ton nicht wiedergeben." + "technical.playbackMethod" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wiedergabe" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "iOS can't play %@ audio." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Playback" } } } }, - "technical.reason.container %@": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "iOS öffnet den Container %@ nicht." + "technical.reason" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Grund" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "iOS can't open the %@ container." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reason" } } } }, - "technical.reason.unanalyzed": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Der Server hat diese Datei noch nicht analysiert." + "technical.reason.audioCodec %@" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "iOS kann %@-Ton nicht wiedergeben." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "The server hasn't analyzed this file yet." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "iOS can't play %@ audio." } } } }, - "technical.reason.videoCodec %@": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "iOS kann %@ nicht dekodieren." + "technical.reason.container %@" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "iOS öffnet den Container %@ nicht." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "iOS can't decode %@." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "iOS can't open the %@ container." } } } }, - "technical.resolution": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Auflösung" + "technical.reason.unanalyzed" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Der Server hat diese Datei noch nicht analysiert." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Resolution" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The server hasn't analyzed this file yet." } } } }, - "technical.size": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Dateigröße" + "technical.reason.videoCodec %@" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "iOS kann %@ nicht dekodieren." } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "File size" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "iOS can't decode %@." } } } }, - "technical.subtitle": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Untertitel" + "technical.resolution" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Auflösung" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Subtitle" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Resolution" } } } }, - "technical.title": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Technische Angaben" + "technical.size" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dateigröße" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Technical details" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "File size" } } } }, - "technical.transcode": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Umgerechnet vom Server" + "technical.subtitle" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Untertitel" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Transcoded by server" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Subtitle" } } } }, - "technical.videoCodec": { - "extractionState": "stale", - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Bildcodec" + "technical.title" : { + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Technische Angaben" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Video codec" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Technical details" } } } }, - "diagnostics.liveTV": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Live-TV-Antwort ansehen" + "technical.transcode" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Umgerechnet vom Server" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Inspect Live TV response" + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Transcoded by server" } } } }, - "diagnostics.liveTVHint": { - "localizations": { - "de": { - "stringUnit": { - "state": "translated", - "value": "Zeigt den Aufbau der Serverantwort — nur Schlüssel, Anzahl und Sendernamen, keine Zugangsdaten. Hilft dabei zu finden, wo die Sender stehen." + "technical.videoCodec" : { + "extractionState" : "stale", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bildcodec" } }, - "en": { - "stringUnit": { - "state": "translated", - "value": "Shows the structure of the server's response — keys, counts and channel names only, no credentials. Helps locate where the channels are." + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Video codec" } } } } }, - "version": "1.1" -} + "version" : "1.1" +} \ No newline at end of file diff --git a/Packages/FlixyKit/Sources/PlexKit/Endpoints/PlexServer+LiveTV.swift b/Packages/FlixyKit/Sources/PlexKit/Endpoints/PlexServer+LiveTV.swift index e5362f9..7bf5f52 100644 --- a/Packages/FlixyKit/Sources/PlexKit/Endpoints/PlexServer+LiveTV.swift +++ b/Packages/FlixyKit/Sources/PlexKit/Endpoints/PlexServer+LiveTV.swift @@ -31,30 +31,54 @@ extension PlexServer { } } - /// Die Sender eines Empfängers, samt Auskunft darüber, woher sie kamen. + /// Die eingeschalteten Sender eines Empfängers, mit Namen und Logos. /// - /// Zwei Quellen, in dieser Reihenfolge. Die erste ist die verlässliche: - /// Plex liefert die Sender bereits verschachtelt in der Antwort auf - /// `/livetv/dvrs` mit. Der getrennte Endpunkt war meine erste Vermutung - /// und lieferte nichts — daran hing „Keine Sender" bei einem Server mit - /// über dreißig eingerichteten Sendern. + /// Zwei Quellen, die zusammengeführt werden müssen — das war der + /// eigentliche Stolperstein: /// - /// Die Auskunft über die Quelle steht in der Diagnose. Bei einem Bereich - /// ohne offizielle Beschreibung ist „welcher Weg hat gegriffen" die - /// Angabe, mit der sich der nächste Fehler finden lässt. + /// - Der Empfänger kennt nur die **Zuordnung**: welche Sender einer + /// Senderliste eingeschaltet sind, als nackte Schlüssel. Bei einer + /// Swisscom-Liste sind das 28 von 521. Namen stehen dort keine. + /// - Die **Namen, Nummern und Logos** stehen beim Programmanbieter, unter + /// der Senderliste. + /// + /// Zweimal habe ich vorher die Zuordnung für die Senderliste gehalten. + /// Beim ersten Mal blieb sie leer, beim zweiten hätten alle Sender „—" + /// geheißen und dieselbe Kennung getragen. public func channels(inDVR dvr: PlexDVR) async -> ChannelLookup { - if !dvr.channels.isEmpty { - return ChannelLookup(channels: dvr.channels, source: .embeddedInDVR) + let enabled = dvr.enabledChannelKeys + + // `??` mit einem asynchronen Aufruf rechts geht nicht — die + // Kurzschlussauswertung ist nicht nebenläufigkeitsfähig. + var identifier = dvr.epgIdentifier + if identifier == nil { + identifier = await electronicProgramGuideIdentifier() + } + + guard let epgIdentifier = identifier, let lineup = dvr.lineups.first else { + return ChannelLookup(channels: [], source: .noLineup) } do { let container = try await client.send( - PlexRequest(path: "/livetv/dvrs/\(dvr.key)/channels"), + PlexRequest( + path: "/\(epgIdentifier)/lineups/\(lineup.id)/channels", + timeout: .seconds(30) + ), as: PlexMediaContainer.self ) + + // Ohne Zuordnung alles zeigen: besser die vollständige Liste als + // gar keine, wenn der Empfänger keine Auswahl meldet. + let channels = enabled.isEmpty + ? container.content + : container.content.filter { $0.matches(anyOf: enabled) } + return ChannelLookup( - channels: container.content, - source: container.content.isEmpty ? .nothingFound : .separateEndpoint + channels: channels, + source: channels.isEmpty + ? .lineupWithoutMatches(available: container.content.count, enabled: enabled.count) + : .lineup(available: container.content.count) ) } catch { return ChannelLookup(channels: [], source: .failed(String(describing: error))) @@ -148,13 +172,35 @@ extension PlexServer { /// Werte werden nicht gezeigt, nur Struktur und Anzahl — das genügt, um /// den richtigen Schlüssel zu finden, und nichts davon ist vertraulich. public func liveTVStructure() async -> String { + var report = ["=== /livetv/dvrs ==="] + report.append(await structure(ofPath: "/livetv/dvrs")) + + // Und die Senderliste beim Anbieter — dort stehen die Namen. Ohne sie + // im selben Abzug bliebe nach einem Fehlschlag wieder eine Runde + // Raten übrig. + if let dvr = try? await client.send( + PlexRequest(path: "/livetv/dvrs", timeout: .seconds(10)), + as: PlexMediaContainer.self + ).content.first, + let epgIdentifier = dvr.epgIdentifier, + let lineup = dvr.lineups.first { + let path = "/\(epgIdentifier)/lineups/\(lineup.id)/channels" + report.append("\n=== \(path) ===") + report.append(await structure(ofPath: path)) + } else { + report.append("\n=== Senderliste ===\nKeine Kennung oder Senderliste am Empfänger") + } + + return report.joined(separator: "\n") + } + + private func structure(ofPath path: String) async -> String { let data: Data do { - data = try await client.send(PlexRequest(path: "/livetv/dvrs", timeout: .seconds(10))) + data = try await client.send(PlexRequest(path: path, timeout: .seconds(30))) } catch { return "Abfrage gescheitert: \(error)" } - guard let object = try? JSONSerialization.jsonObject(with: data) else { return "Antwort ist kein JSON (\(data.count) Bytes)" } @@ -200,12 +246,12 @@ extension PlexServer { /// Woher die Senderliste kam. public struct ChannelLookup: Sendable { public enum Source: Sendable, Equatable { - /// Aus der Antwort auf `/livetv/dvrs` — der übliche Weg. - case embeddedInDVR - /// Über `/livetv/dvrs/{id}/channels`. - case separateEndpoint - /// Beide Wege lieferten nichts. - case nothingFound + /// Aus der Senderliste des Programmanbieters. + case lineup(available: Int) + /// Die Senderliste kam an, aber keiner passte zur Zuordnung. + case lineupWithoutMatches(available: Int, enabled: Int) + /// Am Empfänger hängt keine Senderliste oder kein Programmanbieter. + case noLineup case failed(String) } @@ -220,10 +266,14 @@ public struct ChannelLookup: Sendable { /// Kurzbeschreibung für die Diagnose. public var description: String { switch source { - case .embeddedInDVR: "\(channels.count) Sender, aus der Empfängerliste" - case .separateEndpoint: "\(channels.count) Sender, über /livetv/dvrs/…/channels" - case .nothingFound: "Kein Weg lieferte Sender" - case .failed(let reason): "Abfrage gescheitert: \(reason)" + case .lineup(let available): + "\(channels.count) von \(available) Sendern eingeschaltet" + case .lineupWithoutMatches(let available, let enabled): + "Senderliste hat \(available) Sender, \(enabled) sind zugeordnet — keiner passte zusammen" + case .noLineup: + "Am Empfänger hängt keine Senderliste" + case .failed(let reason): + "Abfrage gescheitert: \(reason)" } } } diff --git a/Packages/FlixyKit/Sources/PlexKit/Models/PlexLiveTV.swift b/Packages/FlixyKit/Sources/PlexKit/Models/PlexLiveTV.swift index 56af607..f6727c8 100644 --- a/Packages/FlixyKit/Sources/PlexKit/Models/PlexLiveTV.swift +++ b/Packages/FlixyKit/Sources/PlexKit/Models/PlexLiveTV.swift @@ -14,24 +14,32 @@ public struct PlexDVR: Sendable, Equatable, Decodable, Identifiable { public let language: String? public let lineupTitle: String? public let lineup: String? - /// Die Empfänger am Server, jeweils mit ihren Sendern. - /// - /// Plex liefert sie verschachtelt in der Antwort auf `/livetv/dvrs` mit. - /// Der getrennte Endpunkt `/livetv/dvrs/{id}/channels` war die erste - /// Vermutung und lieferte nichts — daran hing „Keine Sender" bei einem - /// Server, auf dem über dreißig Sender eingerichtet sind. + /// Die Empfänger, jeweils mit ihrer Senderzuordnung. public let devices: [PlexTunerDevice] + /// Die Kennung des Programmanbieters, etwa `tv.plex.providers.epg.cloud:2`. + /// + /// Steht direkt am Empfänger — verlässlicher, als sie aus der + /// Anbieterliste zu suchen. + public let epgIdentifier: String? + /// Die Senderlisten, aus denen ausgewählt wurde. + public let lineups: [PlexLineup] public var id: String { key } - /// Alle Sender aller Empfänger dieses DVR. - public var channels: [PlexChannel] { - devices.flatMap(\.channels) + /// Welche Sender eingeschaltet sind, als Schlüssel. + /// + /// Die Zuordnung enthält **keine Namen** — nur, welche Sender der + /// Senderliste aktiv sind. Die Namen stehen beim Programmanbieter. Das + /// war der Grund, warum die Senderansicht leer blieb: die Zuordnung sah + /// nach einer Senderliste aus, war aber nur ein Verweis darauf. + public var enabledChannelKeys: Set { + Set(devices.flatMap(\.channelMappings).filter(\.isEnabled).map(\.channelKey)) } private enum CodingKeys: String, CodingKey { - case key, uuid, language, lineupTitle, lineup + case key, uuid, language, lineupTitle, lineup, epgIdentifier case devices = "Device" + case lineups = "Lineup" } public init(from decoder: any Decoder) throws { @@ -47,6 +55,8 @@ public struct PlexDVR: Sendable, Equatable, Decodable, Identifiable { self.lineupTitle = try values.decodeIfPresent(String.self, forKey: .lineupTitle) self.lineup = try values.decodeIfPresent(String.self, forKey: .lineup) self.devices = try values.decodeIfPresent([PlexTunerDevice].self, forKey: .devices) ?? [] + self.epgIdentifier = try values.decodeIfPresent(String.self, forKey: .epgIdentifier) + self.lineups = try values.decodeIfPresent([PlexLineup].self, forKey: .lineups) ?? [] } public init( @@ -55,7 +65,9 @@ public struct PlexDVR: Sendable, Equatable, Decodable, Identifiable { lineupTitle: String? = nil, lineup: String? = nil, language: String? = nil, - devices: [PlexTunerDevice] = [] + devices: [PlexTunerDevice] = [], + epgIdentifier: String? = nil, + lineups: [PlexLineup] = [] ) { self.key = key self.uuid = uuid @@ -63,6 +75,39 @@ public struct PlexDVR: Sendable, Equatable, Decodable, Identifiable { self.lineup = lineup self.language = language self.devices = devices + self.epgIdentifier = epgIdentifier + self.lineups = lineups + } +} + +/// Eine Senderliste des Anbieters — bei Kabel oder IPTV oft mehrere hundert. +public struct PlexLineup: Sendable, Equatable, Decodable, Identifiable { + public let id: String + public let title: String? + + public init(id: String, title: String? = nil) { + self.id = id + self.title = title + } +} + +/// Ein Eintrag der Senderzuordnung. +/// +/// Sagt, welcher Sender der Senderliste am Empfänger eingeschaltet ist — +/// mehr nicht. Namen, Nummern und Logos stehen beim Programmanbieter. +public struct PlexChannelMapping: Sendable, Equatable, Decodable { + public let channelKey: String + public let lineupIdentifier: String? + private let enabled: String? + + /// Plex schreibt hier `"1"` oder `"0"` als Zeichenkette, nicht als + /// Wahrheitswert. + public var isEnabled: Bool { enabled != "0" } + + public init(channelKey: String, lineupIdentifier: String? = nil, enabled: String? = "1") { + self.channelKey = channelKey + self.lineupIdentifier = lineupIdentifier + self.enabled = enabled } } @@ -71,16 +116,13 @@ public struct PlexTunerDevice: Sendable, Equatable, Decodable, Identifiable { public let key: String? public let uuid: String? public let title: String? - public let channels: [PlexChannel] + /// Welche Sender der Senderliste hier eingeschaltet sind. + public let channelMappings: [PlexChannelMapping] public var id: String { uuid ?? key ?? title ?? "tuner" } - /// Plex nennt die Senderliste je nach Fassung `Channel` oder - /// `ChannelMapping`. Beide werden gelesen — eine falsche Annahme darüber - /// ergibt eine leere Liste, und die sieht aus wie „kein Sender vorhanden". private enum CodingKeys: String, CodingKey { case key, uuid, title - case channel = "Channel" case channelMapping = "ChannelMapping" } @@ -89,21 +131,20 @@ public struct PlexTunerDevice: Sendable, Equatable, Decodable, Identifiable { self.key = try? values.decodeIfPresent(String.self, forKey: .key) self.uuid = try values.decodeIfPresent(String.self, forKey: .uuid) self.title = try values.decodeIfPresent(String.self, forKey: .title) - - if values.contains(.channel) { - self.channels = try values.decode([PlexChannel].self, forKey: .channel) - } else if values.contains(.channelMapping) { - self.channels = try values.decode([PlexChannel].self, forKey: .channelMapping) - } else { - self.channels = [] - } + self.channelMappings = try values + .decodeIfPresent([PlexChannelMapping].self, forKey: .channelMapping) ?? [] } - public init(key: String? = nil, uuid: String? = nil, title: String? = nil, channels: [PlexChannel] = []) { + public init( + key: String? = nil, + uuid: String? = nil, + title: String? = nil, + channelMappings: [PlexChannelMapping] = [] + ) { self.key = key self.uuid = uuid self.title = title - self.channels = channels + self.channelMappings = channelMappings } } @@ -144,6 +185,26 @@ public struct PlexChannel: Sendable, Equatable, Decodable, Identifiable { } } + /// Ob dieser Sender in der Zuordnung des Empfängers steht. + /// + /// Nachsichtig verglichen: die Zuordnung führt einen `channelKey`, und + /// je nach Anbieter entspricht der der Kennung, dem Pfad oder der + /// Sendernummer. Ein strenger Vergleich fände in zwei von drei Fällen + /// nichts — und „keiner passte zusammen" sieht aus wie „keine Sender". + public func matches(anyOf keys: Set) -> Bool { + if keys.contains(id) { return true } + if let channelIdentifier, keys.contains(channelIdentifier) { return true } + if let key { + if keys.contains(key) { return true } + if let last = key.split(separator: "/").last, keys.contains(String(last)) { + return true + } + } + // Umgekehrt: manche Zuordnungen führen den vollen Pfad, der Sender + // aber nur die Kennung. + return keys.contains { $0.split(separator: "/").last.map(String.init) == id } + } + public init(id: String, title: String, channelIdentifier: String? = nil, thumb: String? = nil, key: String? = nil) { self.id = id self.title = title diff --git a/Packages/FlixyKit/Tests/PlexKitTests/ClearLogoTests.swift b/Packages/FlixyKit/Tests/PlexKitTests/ClearLogoTests.swift index a173543..d11bca6 100644 --- a/Packages/FlixyKit/Tests/PlexKitTests/ClearLogoTests.swift +++ b/Packages/FlixyKit/Tests/PlexKitTests/ClearLogoTests.swift @@ -48,67 +48,64 @@ struct ClearLogoTests { } } -@Suite("Empfänger und Sender") +@Suite("Empfänger, Zuordnung und Senderliste") struct TunerDecodingTests { - @Test("Sender werden aus dem Empfänger gelesen") - func readsChannelsFromDevice() throws { - // Der Fall, an dem „Keine Sender" hing: Plex liefert sie verschachtelt - // in der Antwort auf /livetv/dvrs mit, nicht über einen eigenen - // Endpunkt. - let json = """ - {"MediaContainer":{"Dvr":[{ - "key": "1", "lineupTitle": "Kabel", - "Device": [{ - "key": "2", "uuid": "tuner-1", "title": "HDHomeRun", - "Channel": [ - {"id": 10, "channelIdentifier": "1.1", "title": "SRF 1 HD", "key": "/livetv/channel/1"}, - {"id": 11, "channelIdentifier": "1.2", "title": "SRF zwei"} - ] - }] - }]}} - """ + /// Die Antwort, wie sie ein echter Server liefert. + /// + /// Nachgebildet aus dem Strukturabzug eines Servers mit Swisscom-Liste: + /// 28 zugeordnete Sender von 521 in der Liste. Entscheidend daran ist, + /// was NICHT drinsteht — die Zuordnung führt keine Namen. + private let dvrJSON = """ + {"MediaContainer":{"size":1,"Dvr":[{ + "key": "1", + "uuid": "dvr-uuid", + "epgIdentifier": "tv.plex.providers.epg.cloud:2", + "lineupTitle": "Swisscom Blue TV (Deutschschweiz)", + "lineup": "lineup://tv.plex.providers.epg.cloud/swisscom", + "Lineup": [{"id": "swisscom-de", "title": "Swisscom Blue TV (Deutschschweiz)"}], + "Device": [{ + "key": "2", "uuid": "tuner-1", "title": "Swisscom Blue TV", + "ChannelMapping": [ + {"channelKey": "srf1", "lineupIdentifier": "swisscom-de", "enabled": "1"}, + {"channelKey": "srf2", "lineupIdentifier": "swisscom-de", "enabled": "1"}, + {"channelKey": "abgeschaltet", "lineupIdentifier": "swisscom-de", "enabled": "0"} + ] + }] + }]}} + """ + + private func dvr() throws -> PlexDVR { let container = try PlexResponse.decode( PlexMediaContainer.self, - from: Data(json.utf8) + from: Data(dvrJSON.utf8) ) - let dvr = try #require(container.content.first) - - #expect(dvr.devices.count == 1) - #expect(dvr.channels.count == 2) - #expect(dvr.channels.first?.title == "SRF 1 HD") - #expect(dvr.channels.first?.channelIdentifier == "1.1") + return try #require(container.content.first) } - @Test("Auch die ältere Schreibweise ChannelMapping wird gelesen") - func readsChannelMapping() throws { - let json = """ - {"MediaContainer":{"Dvr":[{ - "key": "1", - "Device": [{"uuid": "t", "ChannelMapping": [{"id": "a", "title": "3sat"}]}] - }]}} - """ - let container = try PlexResponse.decode( - PlexMediaContainer.self, - from: Data(json.utf8) - ) - #expect(container.content.first?.channels.first?.title == "3sat") + @Test("Die Kennung des Programmanbieters steht am Empfänger") + func readsEPGIdentifier() throws { + // Verlässlicher, als sie aus der Anbieterliste zu suchen. + #expect(try dvr().epgIdentifier == "tv.plex.providers.epg.cloud:2") } - @Test("Ein Empfänger ohne Sender ergibt eine leere Liste, keinen Fehler") - func deviceWithoutChannels() throws { - let json = #"{"MediaContainer":{"Dvr":[{"key":"1","Device":[{"uuid":"t"}]}]}}"# - let container = try PlexResponse.decode( - PlexMediaContainer.self, - from: Data(json.utf8) - ) - #expect(container.content.first?.channels.isEmpty == true) + @Test("Die Senderliste wird gelesen") + func readsLineup() throws { + #expect(try dvr().lineups.first?.id == "swisscom-de") + } + + @Test("Nur eingeschaltete Sender zählen zur Zuordnung") + func onlyEnabledMappings() throws { + // `enabled` kommt als Zeichenkette „1" oder „0", nicht als + // Wahrheitswert. + let keys = try dvr().enabledChannelKeys + #expect(keys == ["srf1", "srf2"]) + #expect(!keys.contains("abgeschaltet")) } @Test("Ein Empfänger-Schlüssel als Zahl wird gelesen") func numericDVRKey() throws { - // Je nach Serverfassung kommt er als Zahl oder als Zeichenkette. Ein - // fester Typ hätte die ganze Empfängerliste unlesbar gemacht. + // Je nach Serverfassung kommt er als Zahl oder als Zeichenkette. let json = #"{"MediaContainer":{"Dvr":[{"key":42}]}}"# let container = try PlexResponse.decode( PlexMediaContainer.self, @@ -117,3 +114,42 @@ struct TunerDecodingTests { #expect(container.content.first?.key == "42") } } + +@Suite("Abgleich von Zuordnung und Senderliste") +struct ChannelMatchingTests { + + @Test("Übereinstimmung über die Kennung") + func matchesByIdentifier() { + let channel = PlexChannel(id: "srf1", title: "SRF 1") + #expect(channel.matches(anyOf: ["srf1", "srf2"])) + } + + @Test("Übereinstimmung über die Sendernummer") + func matchesByChannelNumber() { + let channel = PlexChannel(id: "x", title: "SRF 1", channelIdentifier: "1.1") + #expect(channel.matches(anyOf: ["1.1"])) + } + + @Test("Übereinstimmung über den Pfad") + func matchesByPath() { + // Je nach Anbieter führt die Zuordnung den vollen Pfad, der Sender + // aber nur die Kennung — oder umgekehrt. + let channel = PlexChannel(id: "x", title: "SRF 1", key: "/livetv/channels/srf1") + #expect(channel.matches(anyOf: ["srf1"])) + #expect(channel.matches(anyOf: ["/livetv/channels/srf1"])) + } + + @Test("Umgekehrt: Zuordnung führt den Pfad, Sender die Kennung") + func matchesReversed() { + let channel = PlexChannel(id: "srf1", title: "SRF 1") + #expect(channel.matches(anyOf: ["/lineups/swisscom/channels/srf1"])) + } + + @Test("Nicht zugeordnete Sender fallen heraus") + func nonMatchingIsExcluded() { + // Der Punkt der ganzen Übung: von 521 Sendern der Liste sollen die 28 + // übrig bleiben, die eingeschaltet sind. + let channel = PlexChannel(id: "orf1", title: "ORF 1") + #expect(!channel.matches(anyOf: ["srf1", "srf2"])) + } +}