diff --git a/Flaro/Resources/Localizable.xcstrings b/Flaro/Resources/Localizable.xcstrings index 233662f..fbb0594 100644 --- a/Flaro/Resources/Localizable.xcstrings +++ b/Flaro/Resources/Localizable.xcstrings @@ -1,8096 +1,8112 @@ { - "sourceLanguage" : "en", - "strings" : { - " · " : { - "comment" : "A separator between different parts of the episode metadata.", - "isCommentAutoGenerated" : true + "sourceLanguage": "en", + "strings": { + " · ": { + "comment": "A separator between different parts of the episode metadata.", + "isCommentAutoGenerated": true }, - "%@" : { - "comment" : "A number in a list.", - "isCommentAutoGenerated" : true + "%@": { + "comment": "A number in a list.", + "isCommentAutoGenerated": true }, - "%@ → %@" : { - "comment" : "A line in a diagnostic trace that shows a network request path and its result. The first argument is the network request path. The second argument is the network response result.", - "isCommentAutoGenerated" : true, - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "%1$@ → %2$@" + "%@ 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 + }, + "%@ → %@": { + "comment": "A line in a diagnostic trace that shows a network request path and its result. The first argument is the network request path. The second argument is the network response result.", + "isCommentAutoGenerated": true, + "localizations": { + "en": { + "stringUnit": { + "state": "new", + "value": "%1$@ → %2$@" } } } }, - "%@ 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 - }, - "%@: %@" : { - "comment" : "A label that shows the audio or subtitle language. The argument is the language name.", - "isCommentAutoGenerated" : true, - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "%1$@: %2$@" + "%@: %@": { + "comment": "A label that shows the audio or subtitle language. The argument is the language name.", + "isCommentAutoGenerated": true, + "localizations": { + "en": { + "stringUnit": { + "state": "new", + "value": "%1$@: %2$@" } } } }, - "4K" : { - "comment" : "A label for a 4K video.", - "isCommentAutoGenerated" : true + "4K": { + "comment": "A label for a 4K video.", + "isCommentAutoGenerated": true + }, + "Full HD": { + "comment": "A label for a media resolution.", + "isCommentAutoGenerated": true + }, + "HD": { + "comment": "A label for a media resolution.", + "isCommentAutoGenerated": true }, - "account.hint" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Öffnet Einstellungen und Abmelden" + "SD": { + "comment": "A label for a 720p movie.", + "isCommentAutoGenerated": true + }, + "account.hint": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Öffnet Einstellungen und Abmelden" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Opens settings and sign out" + "en": { + "stringUnit": { + "state": "translated", + "value": "Opens settings and sign out" } } } }, - "account.settings" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Einstellungen" + "account.settings": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Einstellungen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Settings" + "en": { + "stringUnit": { + "state": "translated", + "value": "Settings" } } } }, - "action.addFavourite" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Zu Favoriten" + "action.addFavourite": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Zu Favoriten" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Add to favourites" + "en": { + "stringUnit": { + "state": "translated", + "value": "Add to favourites" } } } }, - "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.back" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Zurück" + "action.back": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Zurück" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Back" + "en": { + "stringUnit": { + "state": "translated", + "value": "Back" } } } }, - "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.done" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Fertig" + "action.done": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Fertig" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Done" + "en": { + "stringUnit": { + "state": "translated", + "value": "Done" } } } }, - "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.removeFavourite" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Aus Favoriten entfernen" + "action.removeFavourite": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Aus Favoriten entfernen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Remove from favourites" + "en": { + "stringUnit": { + "state": "translated", + "value": "Remove from favourites" } } } }, - "action.removeFromContinue" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Aus „Weiterschauen“ entfernen" + "action.removeFromContinue": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Aus „Weiterschauen“ entfernen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Remove from Continue Watching" + "en": { + "stringUnit": { + "state": "translated", + "value": "Remove from Continue Watching" } } } }, - "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.cardScale" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Kachelgröße" + "appearance.cardScale": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Kachelgröße" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tile size" + "en": { + "stringUnit": { + "state": "translated", + "value": "Tile size" } } } }, - "appearance.cardScaleExplanation" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Wie groß Plakate und Vorschaubilder erscheinen — auf der Startseite und in den Mediatheken. Die Einstellung gilt nur auf diesem Gerät." + "appearance.cardScaleExplanation": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Wie groß Plakate und Vorschaubilder erscheinen — auf der Startseite und in den Mediatheken. Die Einstellung gilt nur auf diesem Gerät." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "How large posters and thumbnails appear — on the home screen and in libraries. This setting applies to this device only." + "en": { + "stringUnit": { + "state": "translated", + "value": "How large posters and thumbnails appear — on the home screen and in libraries. This setting applies to this device only." } } } }, - "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.navigation" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Navigation" + "appearance.navigation": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Navigation" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Navigation" + "en": { + "stringUnit": { + "state": "translated", + "value": "Navigation" } } } }, - "appearance.navigationExplanation" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Seitlich fährt die Leiste über den Inhalt und gibt ihn danach wieder frei. Oben stehen alle Einträge gleichzeitig da und nehmen dauerhaft eine Zeile ein." + "appearance.navigationExplanation": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Seitlich fährt die Leiste über den Inhalt und gibt ihn danach wieder frei. Oben stehen alle Einträge gleichzeitig da und nehmen dauerhaft eine Zeile ein." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "At the side, the bar slides over the content and gets out of the way again. At the top, every entry is visible at once and takes up a row permanently." + "en": { + "stringUnit": { + "state": "translated", + "value": "At the side, the bar slides over the content and gets out of the way again. At the top, every entry is visible at once and takes up a row permanently." } } } }, - "appearance.navigationSide" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Seitlich" + "appearance.navigationSide": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Seitlich" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "At the side" + "en": { + "stringUnit": { + "state": "translated", + "value": "At the side" } } } }, - "appearance.navigationTop" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Oben" + "appearance.navigationTop": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Oben" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "At the top" + "en": { + "stringUnit": { + "state": "translated", + "value": "At the top" } } } }, - "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.loadSelected %lld" : { - "comment" : "Die angekreuzten Folgen laden", - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld Folgen laden" + "detail.loadSelected %lld": { + "comment": "Die angekreuzten Folgen laden", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "%lld Folgen laden" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Download %lld episodes" + "en": { + "stringUnit": { + "state": "translated", + "value": "Download %lld 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.select" : { - "comment" : "Mehrere Folgen auswählen", - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Auswählen" + "detail.select": { + "comment": "Mehrere Folgen auswählen", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Auswählen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Select" + "en": { + "stringUnit": { + "state": "translated", + "value": "Select" } } } }, - "detail.selectAll" : { - "comment" : "Alle Folgen ankreuzen", - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Alle" + "detail.selectAll": { + "comment": "Alle Folgen ankreuzen", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Alle" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "All" + "en": { + "stringUnit": { + "state": "translated", + "value": "All" } } } }, - "detail.selectNone" : { - "comment" : "Alle Haken entfernen", - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Keine" + "detail.selectNone": { + "comment": "Alle Haken entfernen", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Keine" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "None" + "en": { + "stringUnit": { + "state": "translated", + "value": "None" } } } }, - "detail.toSeason" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Zur Staffel" + "detail.toSeason": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Zur Staffel" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Go to season" + "en": { + "stringUnit": { + "state": "translated", + "value": "Go to season" } } } }, - "detail.toSeries" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Zur Serie" + "detail.toSeries": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Zur Serie" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Go to series" + "en": { + "stringUnit": { + "state": "translated", + "value": "Go to series" } } } }, - "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.method": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Weg" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Method" + "en": { + "stringUnit": { + "state": "translated", + "value": "Method" } } } }, - "diagnostics.noServer" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Keine Serververbindung." + "diagnostics.noServer": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Keine Serververbindung." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "No server connection." + "en": { + "stringUnit": { + "state": "translated", + "value": "No server connection." } } } }, - "diagnostics.nothingYet" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Noch nichts abgespielt." + "diagnostics.nothingYet": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Noch nichts abgespielt." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nothing played yet." + "en": { + "stringUnit": { + "state": "translated", + "value": "Nothing played yet." } } } }, - "diagnostics.source" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Quelle (ohne Token)" + "diagnostics.source": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Quelle (ohne Token)" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Source (token removed)" + "en": { + "stringUnit": { + "state": "translated", + "value": "Source (token removed)" } } } }, - "diagnostics.steps" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ablauf" + "diagnostics.steps": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Ablauf" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Timeline" + "en": { + "stringUnit": { + "state": "translated", + "value": "Timeline" } } } }, - "diagnostics.title" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Diagnose" + "diagnostics.title": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Diagnose" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Diagnostics" + "en": { + "stringUnit": { + "state": "translated", + "value": "Diagnostics" } } } }, - "diagnostics.title.label" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Titel" + "diagnostics.title.label": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Titel" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Title" + "en": { + "stringUnit": { + "state": "translated", + "value": "Title" } } } }, - "downloads.active" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Läuft gerade" + "downloads.active": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Läuft gerade" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "In progress" + "en": { + "stringUnit": { + "state": "translated", + "value": "In progress" } } } }, - "downloads.allEpisodes" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Alle Folgen laden" + "downloads.allEpisodes": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Alle Folgen laden" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Download all episodes" + "en": { + "stringUnit": { + "state": "translated", + "value": "Download all episodes" } } } }, - "downloads.audio.hint" : { - "comment" : "Erklärt, warum die Wahl jetzt fällt", - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Beim Umpacken kommt genau eine mit — danach lässt sie sich nicht mehr wechseln." + "downloads.audio.hint": { + "comment": "Erklärt, warum die Wahl jetzt fällt", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Beim Umpacken kommt genau eine mit — danach lässt sie sich nicht mehr wechseln." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Repackaging keeps exactly one — it cannot be changed afterwards." + "en": { + "stringUnit": { + "state": "translated", + "value": "Repackaging keeps exactly one — it cannot be changed afterwards." } } } }, - "downloads.audio.recommended %@" : { - "comment" : "Markiert den Vorschlag", - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ · empfohlen" + "downloads.audio.recommended %@": { + "comment": "Markiert den Vorschlag", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "%@ · empfohlen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ · recommended" + "en": { + "stringUnit": { + "state": "translated", + "value": "%@ · recommended" } } } }, - "downloads.audio.title" : { - "comment" : "Überschrift der Tonspurwahl beim Herunterladen", - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Welche Tonspur?" + "downloads.audio.title": { + "comment": "Überschrift der Tonspurwahl beim Herunterladen", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Welche Tonspur?" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Which audio track?" + "en": { + "stringUnit": { + "state": "translated", + "value": "Which audio track?" } } } }, - "downloads.available" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Offline verfügbar" + "downloads.available": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Offline verfügbar" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Available offline" + "en": { + "stringUnit": { + "state": "translated", + "value": "Available offline" } } } }, - "downloads.batch.queued %lld" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld Folgen in die Warteschlange gelegt." + "downloads.batch.queued %lld": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "%lld Folgen in die Warteschlange gelegt." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Queued %lld episodes." + "en": { + "stringUnit": { + "state": "translated", + "value": "Queued %lld episodes." } } } }, - "downloads.batch.title" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Downloads" + "downloads.batch.title": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Downloads" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Downloads" + "en": { + "stringUnit": { + "state": "translated", + "value": "Downloads" } } } }, - "downloads.batch.unavailable %lld" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld Folgen haben keine abspielbare Datei auf dem Server." + "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" : "%lld episodes have no playable file on the server." + "en": { + "stringUnit": { + "state": "translated", + "value": "%lld episodes have no playable file on the server." } } } }, - "downloads.cancel" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Download abbrechen" + "downloads.cancel": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Download abbrechen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cancel download" + "en": { + "stringUnit": { + "state": "translated", + "value": "Cancel download" } } } }, - "downloads.download" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Herunterladen" + "downloads.download": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Herunterladen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Download" + "en": { + "stringUnit": { + "state": "translated", + "value": "Download" } } } }, - "downloads.empty.description" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Lade Filme und Folgen herunter, um sie ohne Verbindung anzusehen." + "downloads.empty.description": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Lade Filme und Folgen herunter, um sie ohne Verbindung anzusehen." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Download movies and episodes to watch them without a connection." + "en": { + "stringUnit": { + "state": "translated", + "value": "Download movies and episodes to watch them without a connection." } } } }, - "downloads.empty.title" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nichts offline" + "downloads.empty.title": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Nichts offline" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nothing offline" + "en": { + "stringUnit": { + "state": "translated", + "value": "Nothing offline" } } } }, - "downloads.episodeCount %lld" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld Folgen geladen" + "downloads.episodeCount %lld": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "%lld Folgen geladen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld episodes downloaded" + "en": { + "stringUnit": { + "state": "translated", + "value": "%lld episodes downloaded" } } } }, - "downloads.failed" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Fehlgeschlagen" + "downloads.failed": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Fehlgeschlagen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Failed" + "en": { + "stringUnit": { + "state": "translated", + "value": "Failed" } } } }, - "downloads.hold.offline" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Keine Verbindung" + "downloads.hold.offline": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Keine Verbindung" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "No connection" + "en": { + "stringUnit": { + "state": "translated", + "value": "No connection" } } } }, - "downloads.hold.offline.description" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ohne Verbindung geht nichts weiter. Angefangene Downloads laufen weiter, sobald das Gerät wieder am Netz ist." + "downloads.hold.offline.description": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Ohne Verbindung geht nichts weiter. Angefangene Downloads laufen weiter, sobald das Gerät wieder am Netz ist." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nothing transfers without a connection. Started downloads continue once the device is back online." + "en": { + "stringUnit": { + "state": "translated", + "value": "Nothing transfers without a connection. Started downloads continue once the device is back online." } } } }, - "downloads.hold.wifi" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Wartet auf WLAN" + "downloads.hold.wifi": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Wartet auf WLAN" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Waiting for Wi-Fi" + "en": { + "stringUnit": { + "state": "translated", + "value": "Waiting for Wi-Fi" } } } }, - "downloads.hold.wifi.description" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "„Nur über WLAN“ ist eingeschaltet und das Gerät hängt gerade am Mobilfunk. Sobald WLAN da ist, geht es von selbst weiter." + "downloads.hold.wifi.description": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "„Nur über WLAN“ ist eingeschaltet und das Gerät hängt gerade am Mobilfunk. Sobald WLAN da ist, geht es von selbst weiter." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "“Wi-Fi only” is on and the device is on cellular. Transfers resume by themselves once Wi-Fi is back." + "en": { + "stringUnit": { + "state": "translated", + "value": "“Wi-Fi only” is on and the device is on cellular. Transfers resume by themselves once Wi-Fi is back." } } } }, - "downloads.movies" : { - "comment" : "Überschrift für heruntergeladene Filme", - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Filme" + "downloads.movies": { + "comment": "Überschrift für heruntergeladene Filme", + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Filme" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Movies" + "en": { + "stringUnit": { + "state": "translated", + "value": "Movies" } } } }, - "downloads.pause" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Anhalten" + "downloads.pause": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Anhalten" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pause" + "en": { + "stringUnit": { + "state": "translated", + "value": "Pause" } } } }, - "downloads.paused" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Angehalten" + "downloads.paused": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Angehalten" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Paused" + "en": { + "stringUnit": { + "state": "translated", + "value": "Paused" } } } }, - "downloads.perSecond %@" : { - "comment" : "Tempo einer laufenden Übertragung", - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@/s" + "downloads.perSecond %@": { + "comment": "Tempo einer laufenden Übertragung", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "%@/s" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@/s" + "en": { + "stringUnit": { + "state": "translated", + "value": "%@/s" } } } }, - "downloads.quality.approx %@ %@" : { - "comment" : "Platzhalter: Stufenname, geschätzte Größe", - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ · etwa %@" + "downloads.quality.approx %@ %@": { + "comment": "Platzhalter: Stufenname, geschätzte Größe", + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "%@ · etwa %@" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ · about %@" + "en": { + "stringUnit": { + "state": "translated", + "value": "%@ · about %@" } } } }, - "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.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" : "Lower levels are transcoded by the server. That takes longer but saves a lot of space." + "en": { + "stringUnit": { + "state": "translated", + "value": "Lower levels are transcoded by the server. That takes longer but saves a lot of space." } } } }, - "downloads.quality.title" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "In welcher Qualität?" + "downloads.quality.title": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "In welcher Qualität?" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Which quality?" + "en": { + "stringUnit": { + "state": "translated", + "value": "Which quality?" } } } }, - "downloads.queued" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Wartet" + "downloads.queued": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Wartet" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Queued" + "en": { + "stringUnit": { + "state": "translated", + "value": "Queued" } } } }, - "downloads.refused.exists" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dieser Titel steht bereits in der Warteschlange." + "downloads.refused.exists": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Dieser Titel steht bereits in der Warteschlange." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "This title is already queued." + "en": { + "stringUnit": { + "state": "translated", + "value": "This title is already queued." } } } }, - "downloads.refused.limit %@ %@" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Das Speicherlimit ist erreicht: %@ von %@ belegt." + "downloads.refused.limit %@ %@": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Das Speicherlimit ist erreicht: %@ von %@ belegt." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Storage limit reached: %@ of %@ used." + "en": { + "stringUnit": { + "state": "translated", + "value": "Storage limit reached: %@ of %@ used." } } } }, - "downloads.refused.noMedia" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Zu diesem Titel liegt keine Datei auf dem 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" : "There's no file for this title on the server." + "en": { + "stringUnit": { + "state": "translated", + "value": "There's no file for this title on the server." } } } }, - "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.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 file on the server has no retrievable part. It may not have been analyzed yet." + "en": { + "stringUnit": { + "state": "translated", + "value": "The file on the server has no retrievable part. It may not have been analyzed yet." } } } }, - "downloads.refused.noServer" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Keine Verbindung zum Server." + "downloads.refused.noServer": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Keine Verbindung zum Server." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "No connection to the server." + "en": { + "stringUnit": { + "state": "translated", + "value": "No connection to the server." } } } }, - "downloads.refused.noURL" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Die Download-Adresse ließ sich nicht bilden." + "downloads.refused.noURL": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Die Download-Adresse ließ sich nicht bilden." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "The download address couldn't be built." + "en": { + "stringUnit": { + "state": "translated", + "value": "The download address couldn't be built." } } } }, - "downloads.refused.space %@ %@" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Zu wenig Platz auf dem Gerät: %@ nötig, %@ frei." + "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" : "Not enough space on this device: %@ needed, %@ free." + "en": { + "stringUnit": { + "state": "translated", + "value": "Not enough space on this device: %@ needed, %@ free." } } } }, - "downloads.refused.title" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Download nicht möglich" + "downloads.refused.title": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Download nicht möglich" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Can't download" + "en": { + "stringUnit": { + "state": "translated", + "value": "Can't download" } } } }, - "downloads.remaining %@" : { - "comment" : "Geschätzte Restzeit eines Downloads", - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "noch %@" + "downloads.remaining %@": { + "comment": "Geschätzte Restzeit eines Downloads", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "noch %@" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ left" + "en": { + "stringUnit": { + "state": "translated", + "value": "%@ left" } } } }, - "downloads.remaining.underMinute" : { - "comment" : "Restzeit unter einer Minute — eine Zahl wäre hier genauer, als die Schätzung ist", - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "gleich fertig" + "downloads.remaining.underMinute": { + "comment": "Restzeit unter einer Minute — eine Zahl wäre hier genauer, als die Schätzung ist", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "gleich fertig" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "almost done" + "en": { + "stringUnit": { + "state": "translated", + "value": "almost done" } } } }, - "downloads.removeConfirm" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Download entfernen?" + "downloads.removeConfirm": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Download entfernen?" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Remove download?" + "en": { + "stringUnit": { + "state": "translated", + "value": "Remove download?" } } } }, - "downloads.resume" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Fortsetzen" + "downloads.resume": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Fortsetzen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Resume" + "en": { + "stringUnit": { + "state": "translated", + "value": "Resume" } } } }, - "downloads.season" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Staffel laden" + "downloads.season": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Staffel laden" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Download season" + "en": { + "stringUnit": { + "state": "translated", + "value": "Download season" } } } }, - "downloads.used" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Belegt" + "downloads.used": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Belegt" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Used" + "en": { + "stringUnit": { + "state": "translated", + "value": "Used" } } } }, - "downloads.wifiOnly" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nur über WLAN laden" + "downloads.wifiOnly": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Nur über WLAN laden" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Download over Wi-Fi only" + "en": { + "stringUnit": { + "state": "translated", + "value": "Download over Wi-Fi only" } } } }, - "episode.long %lld %lld" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Staffel %lld · Folge %lld" + "episode.long %lld %lld": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Staffel %lld · Folge %lld" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Season %lld · Episode %lld" + "en": { + "stringUnit": { + "state": "translated", + "value": "Season %lld · Episode %lld" } } } }, - "episode.number %lld" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Folge %lld" + "episode.number %lld": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Folge %lld" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Episode %lld" + "en": { + "stringUnit": { + "state": "translated", + "value": "Episode %lld" } } } }, - "episode.short %lld %lld" : { - "comment" : "Nur auf Kacheln, wo eine Zeile breit ist. Wo Platz ist — Player, Aufmacher am Fernseher — steht episode.long.", - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "S%lld E%lld" + "episode.short %lld %lld": { + "comment": "Nur auf Kacheln, wo eine Zeile breit ist. Wo Platz ist — Player, Aufmacher am Fernseher — steht episode.long.", + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "S%lld E%lld" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "S%lld E%lld" + "en": { + "stringUnit": { + "state": "translated", + "value": "S%lld E%lld" } } } }, - "error.notFound" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dieser Titel ist auf dem Server nicht mehr vorhanden." + "error.notFound": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Dieser Titel ist auf dem Server nicht mehr vorhanden." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "This title is no longer on the server." + "en": { + "stringUnit": { + "state": "translated", + "value": "This title is no longer on the server." } } } }, - "error.server %lld" : { - "comment" : "Platzhalter: HTTP-Statuscode", - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Der Server hat mit Fehler %lld geantwortet." + "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" : "The server responded with error %lld." + "en": { + "stringUnit": { + "state": "translated", + "value": "The server responded with error %lld." } } } }, - "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.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" : "The server can't be reached. Check that it's running and that you're on the same network." + "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.title" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Das hat nicht geklappt" + "error.title": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Das hat nicht geklappt" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Something went wrong" + "en": { + "stringUnit": { + "state": "translated", + "value": "Something went wrong" } } } }, - "error.unauthorized" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Die Anmeldung ist abgelaufen. Melde dich erneut an." + "error.unauthorized": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Die Anmeldung ist abgelaufen. Melde dich erneut an." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Your sign-in has expired. Please sign in again." + "en": { + "stringUnit": { + "state": "translated", + "value": "Your sign-in has expired. Please sign in again." } } } }, - "error.unexpectedResponse" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Die Antwort des Servers war unerwartet. Möglicherweise läuft dort eine neuere Fassung, als diese App kennt." + "error.unexpectedResponse": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Die Antwort des Servers war unerwartet. Möglicherweise läuft dort eine neuere Fassung, als diese App kennt." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "The server's response was unexpected. It may be running a newer version than this app knows." + "en": { + "stringUnit": { + "state": "translated", + "value": "The server's response was unexpected. It may be running a newer version than this app knows." } } } }, - "filter.any" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Alle" + "filter.any": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Alle" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Any" + "en": { + "stringUnit": { + "state": "translated", + "value": "Any" } } } }, - "filter.downloadedOnly" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nur Heruntergeladene" + "filter.downloadedOnly": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Nur Heruntergeladene" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Downloaded only" + "en": { + "stringUnit": { + "state": "translated", + "value": "Downloaded only" } } } }, - "filter.filters" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Filter" + "filter.filters": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Filter" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Filters" + "en": { + "stringUnit": { + "state": "translated", + "value": "Filters" } } } }, - "filter.genre" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Genre" + "filter.genre": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Genre" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Genre" + "en": { + "stringUnit": { + "state": "translated", + "value": "Genre" } } } }, - "filter.librarySettings" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Diese Mediathek" + "filter.librarySettings": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Diese Mediathek" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "This library" + "en": { + "stringUnit": { + "state": "translated", + "value": "This library" } } } }, - "filter.reset" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Zurücksetzen" + "filter.reset": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Zurücksetzen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Reset" + "en": { + "stringUnit": { + "state": "translated", + "value": "Reset" } } } }, - "filter.resolution" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Auflösung" + "filter.resolution": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Auflösung" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Resolution" + "en": { + "stringUnit": { + "state": "translated", + "value": "Resolution" } } } }, - "filter.sort" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sortierung" + "filter.sort": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Sortierung" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sort by" + "en": { + "stringUnit": { + "state": "translated", + "value": "Sort by" } } } }, - "filter.title" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Filter" + "filter.title": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Filter" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Filters" + "en": { + "stringUnit": { + "state": "translated", + "value": "Filters" } } } }, - "filter.unwatchedOnly" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nur Ungesehene" + "filter.unwatchedOnly": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Nur Ungesehene" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Unwatched only" + "en": { + "stringUnit": { + "state": "translated", + "value": "Unwatched only" } } } }, - "Full HD" : { - "comment" : "A label for a media resolution.", - "isCommentAutoGenerated" : true - }, - "HD" : { - "comment" : "A label for a media resolution.", - "isCommentAutoGenerated" : true - }, - "hero.details" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Details" + "hero.details": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Details" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Details" + "en": { + "stringUnit": { + "state": "translated", + "value": "Details" } } } }, - "hero.next" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nächster Vorschlag" + "hero.next": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Nächster Vorschlag" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Next suggestion" + "en": { + "stringUnit": { + "state": "translated", + "value": "Next suggestion" } } } }, - "hero.play" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Abspielen" + "hero.play": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Abspielen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Play" + "en": { + "stringUnit": { + "state": "translated", + "value": "Play" } } } }, - "hero.previous" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Voriger Vorschlag" + "hero.previous": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Voriger Vorschlag" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Previous suggestion" + "en": { + "stringUnit": { + "state": "translated", + "value": "Previous suggestion" } } } }, - "hero.resume" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Weiterschauen" + "hero.resume": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Weiterschauen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Resume" + "en": { + "stringUnit": { + "state": "translated", + "value": "Resume" } } } }, - "home.combineContinue" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Weiterschauen und Als Nächstes zusammenlegen" + "home.combineContinue": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Weiterschauen und Als Nächstes zusammenlegen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Merge Continue and Next Up" + "en": { + "stringUnit": { + "state": "translated", + "value": "Merge Continue and Next Up" } } } }, - "home.downloaded" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Heruntergeladen" + "home.downloaded": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Heruntergeladen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Downloaded" + "en": { + "stringUnit": { + "state": "translated", + "value": "Downloaded" } } } }, - "home.empty.description" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sobald dein Server Mediatheken enthält, erscheinen sie hier." + "home.empty.description": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Sobald dein Server Mediatheken enthält, erscheinen sie hier." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Once your server has libraries, they'll show up here." + "en": { + "stringUnit": { + "state": "translated", + "value": "Once your server has libraries, they'll show up here." } } } }, - "home.empty.title" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Noch nichts da" + "home.empty.title": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Noch nichts da" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nothing here yet" + "en": { + "stringUnit": { + "state": "translated", + "value": "Nothing here yet" } } } }, - "home.hero" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Aufmacher" + "home.hero": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Aufmacher" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Featured" + "en": { + "stringUnit": { + "state": "translated", + "value": "Featured" } } } }, - "home.heroCount" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Titel im Wechsel" + "home.heroCount": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Titel im Wechsel" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Titles in rotation" + "en": { + "stringUnit": { + "state": "translated", + "value": "Titles in rotation" } } } }, - "home.heroExplanation" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Der Aufmacher wandert alle sieben Sekunden weiter und hört damit auf, sobald du ihn berührst." + "home.heroExplanation": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Der Aufmacher wandert alle sieben Sekunden weiter und hört damit auf, sobald du ihn berührst." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "The featured area moves on every seven seconds and stops as soon as you touch it." + "en": { + "stringUnit": { + "state": "translated", + "value": "The featured area moves on every seven seconds and stops as soon as you touch it." } } } }, - "home.heroHeight" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Höhe des Aufmachers" + "home.heroHeight": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Höhe des Aufmachers" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Featured height" + "en": { + "stringUnit": { + "state": "translated", + "value": "Featured height" } } } }, - "home.heroLibraries" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mediatheken für den Aufmacher" + "home.heroLibraries": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Mediatheken für den Aufmacher" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Libraries for the hero" + "en": { + "stringUnit": { + "state": "translated", + "value": "Libraries for the hero" } } } }, - "home.heroLibrariesExplanation" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ohne Auswahl kommt alles infrage. Ist etwas gewählt, speist sich der Aufmacher nur noch aus diesen Mediatheken — Weiterschauen und Favoriten bleiben dann draussen, weil der Server nicht sagt, aus welcher Mediathek ein angefangener Titel stammt." + "home.heroLibrariesExplanation": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Ohne Auswahl kommt alles infrage. Ist etwas gewählt, speist sich der Aufmacher nur noch aus diesen Mediatheken — Weiterschauen und Favoriten bleiben dann draussen, weil der Server nicht sagt, aus welcher Mediathek ein angefangener Titel stammt." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "With nothing selected, everything is eligible. Once you select libraries, the hero is drawn only from them — Continue Watching and Favourites stay out, because the server does not say which library a part-watched title came from." + "en": { + "stringUnit": { + "state": "translated", + "value": "With nothing selected, everything is eligible. Once you select libraries, the hero is drawn only from them — Continue Watching and Favourites stay out, because the server does not say which library a part-watched title came from." } } } }, - "home.heroLibrariesNoMatch" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Für die gewählten Mediatheken gibt es derzeit keine eigene Reihe auf der Startseite. Die Auswahl greift deshalb nicht — der Aufmacher zeigt alles." + "home.heroLibrariesNoMatch": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Für die gewählten Mediatheken gibt es derzeit keine eigene Reihe auf der Startseite. Die Auswahl greift deshalb nicht — der Aufmacher zeigt alles." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "The selected libraries currently have no row of their own on the home screen, so the selection has no effect — the hero shows everything." + "en": { + "stringUnit": { + "state": "translated", + "value": "The selected libraries currently have no row of their own on the home screen, so the selection has no effect — the hero shows everything." } } } }, - "home.heroSource" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Inhalt" + "home.heroSource": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Inhalt" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Content" + "en": { + "stringUnit": { + "state": "translated", + "value": "Content" } } } }, - "home.heroSource.continueFirst" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Angefangenes zuerst" + "home.heroSource.continueFirst": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Angefangenes zuerst" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Started titles first" + "en": { + "stringUnit": { + "state": "translated", + "value": "Started titles first" } } } }, - "home.heroSource.latestOnly" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nur Neuzugänge" + "home.heroSource.latestOnly": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Nur Neuzugänge" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "New arrivals only" + "en": { + "stringUnit": { + "state": "translated", + "value": "New arrivals only" } } } }, - "home.hideWatched" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gesehenes aus Neuzugängen ausblenden" + "home.hideWatched": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Gesehenes aus Neuzugängen ausblenden" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Hide watched titles from new arrivals" + "en": { + "stringUnit": { + "state": "translated", + "value": "Hide watched titles from new arrivals" } } } }, - "home.offline" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Kein Server erreichbar. Was hier steht, stammt vom letzten Mal — ansehen lässt sich nur, was auf dem Gerät liegt." + "home.offline": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Kein Server erreichbar. Was hier steht, stammt vom letzten Mal — ansehen lässt sich nur, was auf dem Gerät liegt." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "No server reachable. What you see is from last time — only what is on this device can be played." + "en": { + "stringUnit": { + "state": "translated", + "value": "No server reachable. What you see is from last time — only what is on this device can be played." } } } }, - "home.rowsHeader" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Reihen" + "home.rowsHeader": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Reihen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rows" + "en": { + "stringUnit": { + "state": "translated", + "value": "Rows" } } } }, - "homeLayout.empty" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Der Server liefert derzeit keine Reihen." + "homeLayout.empty": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Der Server liefert derzeit keine Reihen." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "The server isn't providing any rows right now." + "en": { + "stringUnit": { + "state": "translated", + "value": "The server isn't providing any rows right now." } } } }, - "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." + "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" : "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": "Turn off rows you don't need and reorder them via Edit. New rows from the server appear at the end." } } } }, - "homeLayout.title" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Startseite" + "homeLayout.title": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Startseite" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Home screen" + "en": { + "stringUnit": { + "state": "translated", + "value": "Home screen" } } } }, - "item.watched" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gesehen" + "item.watched": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Gesehen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Watched" + "en": { + "stringUnit": { + "state": "translated", + "value": "Watched" } } } }, - "languages.audio" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tonspur" + "languages.audio": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Tonspur" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Audio track" + "en": { + "stringUnit": { + "state": "translated", + "value": "Audio track" } } } }, - "languages.audioExplanation" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gibt es beim Abspielen eine Tonspur in dieser Sprache, wird sie von selbst gewählt. Sonst bleibt es bei der Spur, die der Server voranstellt." + "languages.audioExplanation": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Gibt es beim Abspielen eine Tonspur in dieser Sprache, wird sie von selbst gewählt. Sonst bleibt es bei der Spur, die der Server voranstellt." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "If a track in this language exists, it is selected automatically. Otherwise the server's own first track is used." + "en": { + "stringUnit": { + "state": "translated", + "value": "If a track in this language exists, it is selected automatically. Otherwise the server's own first track is used." } } } }, - "languages.audioHeader" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ton" + "languages.audioHeader": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Ton" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Audio" + "en": { + "stringUnit": { + "state": "translated", + "value": "Audio" } } } }, - "languages.audioShort" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ton" + "languages.audioShort": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Ton" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Audio" + "en": { + "stringUnit": { + "state": "translated", + "value": "Audio" } } } }, - "languages.avoidAudioDescription" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Audiodeskription meiden" + "languages.avoidAudioDescription": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Audiodeskription meiden" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Avoid audio description" + "en": { + "stringUnit": { + "state": "translated", + "value": "Avoid audio description" } } } }, - "languages.avoidAudioDescriptionExplanation" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Spuren, die eine Szenenbeschreibung für Sehbehinderte enthalten, werden nur genommen, wenn es keine andere in dieser Sprache gibt. Erkannt wird das am Namen der Spur — der Server kennzeichnet es nicht." + "languages.avoidAudioDescriptionExplanation": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Spuren, die eine Szenenbeschreibung für Sehbehinderte enthalten, werden nur genommen, wenn es keine andere in dieser Sprache gibt. Erkannt wird das am Namen der Spur — der Server kennzeichnet es nicht." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tracks that describe the scene for blind viewers are only used when there is no other track in that language. They are recognised by name — the server doesn't flag them." + "en": { + "stringUnit": { + "state": "translated", + "value": "Tracks that describe the scene for blind viewers are only used when there is no other track in that language. They are recognised by name — the server doesn't flag them." } } } }, - "languages.fallbackAdd" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sprache hinzufügen" + "languages.fallbackAdd": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Sprache hinzufügen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Add language" + "en": { + "stringUnit": { + "state": "translated", + "value": "Add language" } } } }, - "languages.fallbackEmpty" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Keine Ausweichsprachen" + "languages.fallbackEmpty": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Keine Ausweichsprachen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "No fallback languages" + "en": { + "stringUnit": { + "state": "translated", + "value": "No fallback languages" } } } }, - "languages.fallbackExplanation" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gilt nur, wenn ein Titel die gewünschte Tonsprache gar nicht hat. Dann wird von oben nach unten gesucht." + "languages.fallbackExplanation": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Gilt nur, wenn ein Titel die gewünschte Tonsprache gar nicht hat. Dann wird von oben nach unten gesucht." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Applies only when a title doesn't have your preferred audio language at all. Then it works down the list." + "en": { + "stringUnit": { + "state": "translated", + "value": "Applies only when a title doesn't have your preferred audio language at all. Then it works down the list." } } } }, - "languages.fallbackHeader" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rangliste" + "languages.fallbackHeader": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Rangliste" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Fallback order" + "en": { + "stringUnit": { + "state": "translated", + "value": "Fallback order" } } } }, - "languages.forThisLibrary" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tonsprache für diese Mediathek" + "languages.forThisLibrary": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Tonsprache für diese Mediathek" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Audio language for this library" + "en": { + "stringUnit": { + "state": "translated", + "value": "Audio language for this library" } } } }, - "languages.forThisLibraryExplanation" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gilt für alle Titel dieser Mediathek. Eine Serie kann es überstimmen, und was du im Player von Hand wählst, überstimmt beides." + "languages.forThisLibraryExplanation": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Gilt für alle Titel dieser Mediathek. Eine Serie kann es überstimmen, und was du im Player von Hand wählst, überstimmt beides." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Applies to every title in this library. A series can override it, and what you pick by hand in the player overrides both." + "en": { + "stringUnit": { + "state": "translated", + "value": "Applies to every title in this library. A series can override it, and what you pick by hand in the player overrides both." } } } }, - "languages.forThisSeries" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tonsprache für diese Serie" + "languages.forThisSeries": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Tonsprache für diese Serie" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Audio language for this series" + "en": { + "stringUnit": { + "state": "translated", + "value": "Audio language for this series" } } } }, - "languages.forThisTitle" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tonsprache für diesen Titel" + "languages.forThisTitle": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Tonsprache für diesen Titel" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Audio language for this title" + "en": { + "stringUnit": { + "state": "translated", + "value": "Audio language for this title" } } } }, - "languages.headerExplanation" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Welche Tonspur und welche Untertitel beim Abspielen von selbst gewählt werden." + "languages.headerExplanation": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Welche Tonspur und welche Untertitel beim Abspielen von selbst gewählt werden." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Which audio track and which subtitles are chosen automatically during playback." + "en": { + "stringUnit": { + "state": "translated", + "value": "Which audio track and which subtitles are chosen automatically during playback." } } } }, - "languages.inherit" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Wie allgemein" + "languages.inherit": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Wie allgemein" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "As set generally" + "en": { + "stringUnit": { + "state": "translated", + "value": "As set generally" } } } }, - "languages.none" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Keine" + "languages.none": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Keine" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "None" + "en": { + "stringUnit": { + "state": "translated", + "value": "None" } } } }, - "languages.partial %@" : { - "comment" : "Eine kleine Schriftart, die", - "isCommentAutoGenerated" : true + "languages.partial %@": { + "comment": "Eine kleine Schriftart, die", + "isCommentAutoGenerated": true }, - "languages.partial %lld" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nur Sprachen, die in jeder Folge vorkommen. %lld weitere gibt es nur in einem Teil." + "languages.partial %lld": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Nur Sprachen, die in jeder Folge vorkommen. %lld weitere gibt es nur in einem Teil." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Only languages present in every episode. %lld more exist in some episodes only." + "en": { + "stringUnit": { + "state": "translated", + "value": "Only languages present in every episode. %lld more exist in some episodes only." } } } }, - "languages.serverDefault" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Wie der Server vorgibt" + "languages.serverDefault": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Wie der Server vorgibt" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "As the server prefers" + "en": { + "stringUnit": { + "state": "translated", + "value": "As the server prefers" } } } }, - "languages.subtitles" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Untertitel" + "languages.subtitles": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Untertitel" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Subtitles" + "en": { + "stringUnit": { + "state": "translated", + "value": "Subtitles" } } } }, - "languages.subtitlesExplanation" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Untertitel werden nur eingeschaltet, wenn es sie in dieser Sprache gibt. „Aus“ heißt: gar keine, bis du sie im Player selbst einschaltest." + "languages.subtitlesExplanation": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Untertitel werden nur eingeschaltet, wenn es sie in dieser Sprache gibt. „Aus“ heißt: gar keine, bis du sie im Player selbst einschaltest." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Subtitles are only switched on when they exist in this language. “Off” means none until you turn them on in the player." + "en": { + "stringUnit": { + "state": "translated", + "value": "Subtitles are only switched on when they exist in this language. “Off” means none until you turn them on in the player." } } } }, - "languages.subtitlesForThisSeries" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Untertitel für diese Serie" + "languages.subtitlesForThisSeries": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Untertitel für diese Serie" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Subtitles for this series" + "en": { + "stringUnit": { + "state": "translated", + "value": "Subtitles for this series" } } } }, - "languages.subtitlesForThisTitle" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Untertitel für diesen Titel" + "languages.subtitlesForThisTitle": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Untertitel für diesen Titel" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Subtitles for this title" + "en": { + "stringUnit": { + "state": "translated", + "value": "Subtitles for this title" } } } }, - "languages.subtitlesHeader" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Untertitel" + "languages.subtitlesHeader": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Untertitel" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Subtitles" + "en": { + "stringUnit": { + "state": "translated", + "value": "Subtitles" } } } }, - "languages.subtitlesOff" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Aus" + "languages.subtitlesOff": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Aus" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Off" + "en": { + "stringUnit": { + "state": "translated", + "value": "Off" } } } }, - "languages.subtitlesShort" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Untertitel" + "languages.subtitlesShort": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Untertitel" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Subtitles" + "en": { + "stringUnit": { + "state": "translated", + "value": "Subtitles" } } } }, - "languages.title" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ton & Untertitel" + "languages.title": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Ton & Untertitel" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Audio & subtitles" + "en": { + "stringUnit": { + "state": "translated", + "value": "Audio & subtitles" } } } }, - "library.allLoaded %@" : { - "comment" : "A footer in the library view that shows the number of items in the library.", - "isCommentAutoGenerated" : true + "library.allLoaded %@": { + "comment": "A footer in the library view that shows the number of items in the library.", + "isCommentAutoGenerated": true }, - "library.allLoaded %lld" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Alle %lld Titel geladen" + "library.allLoaded %lld": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Alle %lld Titel geladen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "All %lld titles loaded" + "en": { + "stringUnit": { + "state": "translated", + "value": "All %lld titles loaded" } } } }, - "library.clearFilters" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Filter zurücksetzen" + "library.clearFilters": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Filter zurücksetzen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Clear filters" + "en": { + "stringUnit": { + "state": "translated", + "value": "Clear filters" } } } }, - "library.empty.description" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mit den aktuellen Filtern bleibt nichts übrig." + "library.empty.description": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Mit den aktuellen Filtern bleibt nichts übrig." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nothing matches the current filters." + "en": { + "stringUnit": { + "state": "translated", + "value": "Nothing matches the current filters." } } } }, - "library.empty.title" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Keine Titel" + "library.empty.title": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Keine Titel" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "No titles" + "en": { + "stringUnit": { + "state": "translated", + "value": "No titles" } } } }, - "library.filters" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Filter" + "library.filters": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Filter" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Filters" + "en": { + "stringUnit": { + "state": "translated", + "value": "Filters" } } } }, - "library.kind.liveTV" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Live-TV" + "library.kind.liveTV": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Live-TV" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Live TV" + "en": { + "stringUnit": { + "state": "translated", + "value": "Live TV" } } } }, - "library.kind.movie" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Filme" + "library.kind.movie": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Filme" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Movies" + "en": { + "stringUnit": { + "state": "translated", + "value": "Movies" } } } }, - "library.kind.music" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Musik" + "library.kind.music": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Musik" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Music" + "en": { + "stringUnit": { + "state": "translated", + "value": "Music" } } } }, - "library.kind.other" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sonstiges" + "library.kind.other": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Sonstiges" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Other" + "en": { + "stringUnit": { + "state": "translated", + "value": "Other" } } } }, - "library.kind.photo" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Fotos" + "library.kind.photo": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Fotos" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Photos" + "en": { + "stringUnit": { + "state": "translated", + "value": "Photos" } } } }, - "library.kind.show" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Serien" + "library.kind.show": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Serien" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "TV shows" + "en": { + "stringUnit": { + "state": "translated", + "value": "TV shows" } } } }, - "library.loadMore" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Weitere laden" + "library.loadMore": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Weitere laden" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Load more" + "en": { + "stringUnit": { + "state": "translated", + "value": "Load more" } } } }, - "library.loadMoreFailed" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Das Nachladen ist hängen geblieben — es fehlen noch Titel." + "library.loadMoreFailed": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Das Nachladen ist hängen geblieben — es fehlen noch Titel." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loading more stalled — some titles are still missing." + "en": { + "stringUnit": { + "state": "translated", + "value": "Loading more stalled — some titles are still missing." } } } }, - "library.reload" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Neu laden" + "library.reload": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Neu laden" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Reload" + "en": { + "stringUnit": { + "state": "translated", + "value": "Reload" } } } }, - "library.scan" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mediathek durchsuchen" + "library.scan": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Mediathek durchsuchen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Scan library" + "en": { + "stringUnit": { + "state": "translated", + "value": "Scan library" } } } }, - "library.search" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Suchen" + "library.search": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Suchen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Search" + "en": { + "stringUnit": { + "state": "translated", + "value": "Search" } } } }, - "library.title" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mediatheken" + "library.title": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Mediatheken" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Libraries" + "en": { + "stringUnit": { + "state": "translated", + "value": "Libraries" } } } }, - "library.toggleLayout" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ansicht wechseln" + "library.toggleLayout": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Ansicht wechseln" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Switch layout" + "en": { + "stringUnit": { + "state": "translated", + "value": "Switch layout" } } } }, - "libraryHub.empty" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Auf diesem Server ist keine Mediathek zu sehen." + "libraryHub.empty": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Auf diesem Server ist keine Mediathek zu sehen." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "No library is visible on this server." + "en": { + "stringUnit": { + "state": "translated", + "value": "No library is visible on this server." } } } }, - "liveTV.alreadyScheduled" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Zur Aufnahme vorgemerkt" + "liveTV.alreadyScheduled": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Zur Aufnahme vorgemerkt" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Scheduled to record" + "en": { + "stringUnit": { + "state": "translated", + "value": "Scheduled to record" } } } }, - "liveTV.anyChannel" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Auf allen Sendern" + "liveTV.anyChannel": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Auf allen Sendern" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "On any channel" + "en": { + "stringUnit": { + "state": "translated", + "value": "On any channel" } } } }, - "liveTV.cancelTimer" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Absagen" + "liveTV.cancelTimer": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Absagen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cancel" + "en": { + "stringUnit": { + "state": "translated", + "value": "Cancel" } } } }, - "liveTV.channelCount %@" : { - "comment" : "A label displaying the number of channels. The argument is the number of channels.", - "isCommentAutoGenerated" : true + "liveTV.channelCount %@": { + "comment": "A label displaying the number of channels. The argument is the number of channels.", + "isCommentAutoGenerated": true }, - "liveTV.channelCount %lld" : { - "comment" : "Platzhalter: Anzahl Sender", - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld Sender" + "liveTV.channelCount %lld": { + "comment": "Platzhalter: Anzahl Sender", + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "%lld Sender" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld channels" + "en": { + "stringUnit": { + "state": "translated", + "value": "%lld channels" } } } }, - "liveTV.deleteRecording.confirm" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Aufnahme löschen?" + "liveTV.deleteRecording.confirm": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Aufnahme löschen?" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Delete recording?" + "en": { + "stringUnit": { + "state": "translated", + "value": "Delete recording?" } } } }, - "liveTV.gap" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Keine Angaben" + "liveTV.gap": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Keine Angaben" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "No data" + "en": { + "stringUnit": { + "state": "translated", + "value": "No data" } } } }, - "liveTV.mode" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ansicht" + "liveTV.mode": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Ansicht" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "View" + "en": { + "stringUnit": { + "state": "translated", + "value": "View" } } } }, - "liveTV.mode.guide" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Programm" + "liveTV.mode.guide": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Programm" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Guide" + "en": { + "stringUnit": { + "state": "translated", + "value": "Guide" } } } }, - "liveTV.mode.now" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Jetzt" + "liveTV.mode.now": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Jetzt" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Now" + "en": { + "stringUnit": { + "state": "translated", + "value": "Now" } } } }, - "liveTV.next %@ %@" : { - "comment" : "Platzhalter: Uhrzeit, Sendungstitel", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Danach %@ · %@" + "liveTV.next %@ %@": { + "comment": "Platzhalter: Uhrzeit, Sendungstitel", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Danach %@ · %@" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Next %@ · %@" + "en": { + "stringUnit": { + "state": "translated", + "value": "Next %@ · %@" } } } }, - "liveTV.noChannels.description" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Der Empfänger ist eingerichtet, liefert aber keine Sender." + "liveTV.noChannels.description": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Der Empfänger ist eingerichtet, liefert aber keine Sender." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "The tuner is set up but isn't providing any channels." + "en": { + "stringUnit": { + "state": "translated", + "value": "The tuner is set up but isn't providing any channels." } } } }, - "liveTV.noChannels.title" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Keine Sender" + "liveTV.noChannels.title": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Keine Sender" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "No channels" + "en": { + "stringUnit": { + "state": "translated", + "value": "No channels" } } } }, - "liveTV.noProgram" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Kein Programm verfügbar" + "liveTV.noProgram": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Kein Programm verfügbar" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "No guide data" + "en": { + "stringUnit": { + "state": "translated", + "value": "No guide data" } } } }, - "liveTV.noStream" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Der Server hat keine Adresse für diesen Sender geliefert." + "liveTV.noStream": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Der Server hat keine Adresse für diesen Sender geliefert." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "The server did not return a stream for this channel." + "en": { + "stringUnit": { + "state": "translated", + "value": "The server did not return a stream for this channel." } } } }, - "liveTV.notConfigured.description" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Live-TV braucht eine Fernsehkarte oder einen Netzwerk-Tuner am Server. Richte ihn in den Servereinstellungen ein, dann erscheinen die Sender hier." + "liveTV.notConfigured.description": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Live-TV braucht eine Fernsehkarte oder einen Netzwerk-Tuner am Server. Richte ihn in den Servereinstellungen ein, dann erscheinen die Sender hier." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Live TV needs a tuner card or network tuner on your server. Set one up in the server settings and channels will appear here." + "en": { + "stringUnit": { + "state": "translated", + "value": "Live TV needs a tuner card or network tuner on your server. Set one up in the server settings and channels will appear here." } } } }, - "liveTV.notConfigured.title" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Kein Empfänger eingerichtet" + "liveTV.notConfigured.title": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Kein Empfänger eingerichtet" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "No tuner set up" + "en": { + "stringUnit": { + "state": "translated", + "value": "No tuner set up" } } } }, - "liveTV.now" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Jetzt" + "liveTV.now": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Jetzt" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Now" + "en": { + "stringUnit": { + "state": "translated", + "value": "Now" } } } }, - "liveTV.record" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Aufnehmen" + "liveTV.record": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Aufnehmen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Record" + "en": { + "stringUnit": { + "state": "translated", + "value": "Record" } } } }, - "liveTV.recording.inProgress" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nimmt auf" + "liveTV.recordSeries": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Serie aufnehmen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Recording" + "en": { + "stringUnit": { + "state": "translated", + "value": "Record series" } } } }, - "liveTV.recordings" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Aufnahmen" + "liveTV.recording.inProgress": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Nimmt auf" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Recordings" + "en": { + "stringUnit": { + "state": "translated", + "value": "Recording" } } } }, - "liveTV.recordings.empty.description" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Im Programm eine Sendung antippen und „Aufnehmen“ wählen — sie steht dann hier." + "liveTV.recordings": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Aufnahmen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tap a programme in the guide and choose “Record” — it will show up here." + "en": { + "stringUnit": { + "state": "translated", + "value": "Recordings" } } } }, - "liveTV.recordings.empty.title" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Noch nichts aufgenommen" + "liveTV.recordings.empty.description": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Im Programm eine Sendung antippen und „Aufnehmen“ wählen — sie steht dann hier." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nothing recorded yet" + "en": { + "stringUnit": { + "state": "translated", + "value": "Tap a programme in the guide and choose “Record” — it will show up here." } } } }, - "liveTV.recordSeries" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Serie aufnehmen" + "liveTV.recordings.empty.title": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Noch nichts aufgenommen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Record series" + "en": { + "stringUnit": { + "state": "translated", + "value": "Nothing recorded yet" } } } }, - "liveTV.runningNow" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Läuft gerade" + "liveTV.runningNow": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Läuft gerade" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "On now" + "en": { + "stringUnit": { + "state": "translated", + "value": "On now" } } } }, - "liveTV.scheduled" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Geplant" + "liveTV.scheduled": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Geplant" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Scheduled" + "en": { + "stringUnit": { + "state": "translated", + "value": "Scheduled" } } } }, - "liveTV.selection" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sender auswählen" + "liveTV.selection": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Sender auswählen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Choose channels" + "en": { + "stringUnit": { + "state": "translated", + "value": "Choose channels" } } } }, - "liveTV.selection.all" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Alle" + "liveTV.selection.all": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Alle" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "All" + "en": { + "stringUnit": { + "state": "translated", + "value": "All" } } } }, - "liveTV.selection.hint" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ohne Auswahl werden alle Sender gezeigt." + "liveTV.selection.hint": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Ohne Auswahl werden alle Sender gezeigt." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "With nothing selected, all channels are shown." + "en": { + "stringUnit": { + "state": "translated", + "value": "With nothing selected, all channels are shown." } } } }, - "liveTV.series" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ganze Reihe" + "liveTV.series": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Ganze Reihe" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Whole series" + "en": { + "stringUnit": { + "state": "translated", + "value": "Whole series" } } } }, - "liveTV.timeRange %@ %@" : { - "comment" : "Platzhalter: Anfangs- und Endzeit", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ – %@" + "liveTV.timeRange %@ %@": { + "comment": "Platzhalter: Anfangs- und Endzeit", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "%@ – %@" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ – %@" + "en": { + "stringUnit": { + "state": "translated", + "value": "%@ – %@" } } } }, - "liveTV.tune" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Einschalten" + "liveTV.tune": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Einschalten" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Watch" + "en": { + "stringUnit": { + "state": "translated", + "value": "Watch" } } } }, - "liveTV.tuneFailed" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sender lässt sich nicht einschalten" + "liveTV.tuneFailed": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Sender lässt sich nicht einschalten" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cannot tune channel" + "en": { + "stringUnit": { + "state": "translated", + "value": "Cannot tune channel" } } } }, - "miniPlayer.close" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Wiedergabe beenden" + "miniPlayer.close": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Wiedergabe beenden" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Stop playback" + "en": { + "stringUnit": { + "state": "translated", + "value": "Stop playback" } } } }, - "miniPlayer.expand" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Auf Vollbild vergrößern" + "miniPlayer.expand": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Auf Vollbild vergrößern" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Expand to full screen" + "en": { + "stringUnit": { + "state": "translated", + "value": "Expand to full screen" } } } }, - "miniPlayer.resize" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Größe ändern" + "miniPlayer.resize": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Größe ändern" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Resize" + "en": { + "stringUnit": { + "state": "translated", + "value": "Resize" } } } }, - "placeholder.description" : { - "comment" : "Platzhalter-Bildschirme", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dieser Bereich entsteht in einer späteren Phase." + "placeholder.description": { + "comment": "Platzhalter-Bildschirme", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Dieser Bereich entsteht in einer späteren Phase." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "This area is coming in a later phase." + "en": { + "stringUnit": { + "state": "translated", + "value": "This area is coming in a later phase." } } } }, - "player.airPlay" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Auf Gerät übertragen" + "player.airPlay": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Auf Gerät übertragen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Play on another device" + "en": { + "stringUnit": { + "state": "translated", + "value": "Play on another device" } } } }, - "player.audioTracks" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tonspur" + "player.audioTracks": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Tonspur" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Audio" + "en": { + "stringUnit": { + "state": "translated", + "value": "Audio" } } } }, - "player.back10" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "10 Sekunden zurück" + "player.back10": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "10 Sekunden zurück" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Back 10 seconds" + "en": { + "stringUnit": { + "state": "translated", + "value": "Back 10 seconds" } } } }, - "player.buffering" : { - "comment" : "Sprachausgabe für den Abspielknopf, solange auf Daten gewartet wird", - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Lädt …" + "player.buffering": { + "comment": "Sprachausgabe für den Abspielknopf, solange auf Daten gewartet wird", + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Lädt …" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loading…" + "en": { + "stringUnit": { + "state": "translated", + "value": "Loading…" } } } }, - "player.close" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Player schließen" + "player.close": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Player schließen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Close player" + "en": { + "stringUnit": { + "state": "translated", + "value": "Close player" } } } }, - "player.exitFullScreen" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Vollbild beenden" + "player.exitFullScreen": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Vollbild beenden" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Exit full screen" + "en": { + "stringUnit": { + "state": "translated", + "value": "Exit full screen" } } } }, - "player.fillVideo" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bild füllen" + "player.fillVideo": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Bild füllen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Fill screen" + "en": { + "stringUnit": { + "state": "translated", + "value": "Fill screen" } } } }, - "player.fitVideo" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bild einpassen" + "player.fitVideo": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Bild einpassen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Fit to screen" + "en": { + "stringUnit": { + "state": "translated", + "value": "Fit to screen" } } } }, - "player.forgetRememberedTrack" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nicht mehr merken" + "player.forgetRememberedTrack": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Nicht mehr merken" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Stop remembering" + "en": { + "stringUnit": { + "state": "translated", + "value": "Stop remembering" } } } }, - "player.forward10" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "10 Sekunden vor" + "player.forward10": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "10 Sekunden vor" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Forward 10 seconds" + "en": { + "stringUnit": { + "state": "translated", + "value": "Forward 10 seconds" } } } }, - "player.fullScreen" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Vollbild" + "player.fullScreen": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Vollbild" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enter full screen" + "en": { + "stringUnit": { + "state": "translated", + "value": "Enter full screen" } } } }, - "player.loadFailed" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Die Datei ließ sich nicht öffnen." + "player.loadFailed": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Die Datei ließ sich nicht öffnen." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "The file couldn't be opened." + "en": { + "stringUnit": { + "state": "translated", + "value": "The file couldn't be opened." } } } }, - "player.minimize" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Verkleinern" + "player.minimize": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Verkleinern" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Minimize" + "en": { + "stringUnit": { + "state": "translated", + "value": "Minimize" } } } }, - "player.nextEpisode" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nächste Folge" + "player.nextEpisode": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Nächste Folge" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Next episode" + "en": { + "stringUnit": { + "state": "translated", + "value": "Next episode" } } } }, - "player.noPlayableFile" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Zu diesem Titel gibt es keine abspielbare Datei auf dem Server." + "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" : "There's no playable file for this title on the server." + "en": { + "stringUnit": { + "state": "translated", + "value": "There's no playable file for this title on the server." } } } }, - "player.pause" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pause" + "player.pause": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Pause" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pause" + "en": { + "stringUnit": { + "state": "translated", + "value": "Pause" } } } }, - "player.pictureInPicture" : { - "comment" : "Knopf im Player: das Bild in ein kleines Fenster legen", - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bild im Bild" + "player.pictureInPicture": { + "comment": "Knopf im Player: das Bild in ein kleines Fenster legen", + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Bild im Bild" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Picture in Picture" + "en": { + "stringUnit": { + "state": "translated", + "value": "Picture in Picture" } } } }, - "player.play" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Abspielen" + "player.play": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Abspielen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Play" + "en": { + "stringUnit": { + "state": "translated", + "value": "Play" } } } }, - "player.playbackFailed" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Die Wiedergabe wurde unterbrochen." + "player.playbackFailed": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Die Wiedergabe wurde unterbrochen." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Playback was interrupted." + "en": { + "stringUnit": { + "state": "translated", + "value": "Playback was interrupted." } } } }, - "player.position" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Position" + "player.position": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Position" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Position" + "en": { + "stringUnit": { + "state": "translated", + "value": "Position" } } } }, - "player.preparing" : { - "comment" : "Hinweis, während der Player die Datei öffnet", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Wird vorbereitet …" + "player.preparing": { + "comment": "Hinweis, während der Player die Datei öffnet", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Wird vorbereitet …" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Preparing…" + "en": { + "stringUnit": { + "state": "translated", + "value": "Preparing…" } } } }, - "player.previousEpisode" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Vorige Folge" + "player.previousEpisode": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Vorige Folge" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Previous episode" + "en": { + "stringUnit": { + "state": "translated", + "value": "Previous episode" } } } }, - "player.quality" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Qualität" + "player.quality": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Qualität" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Quality" + "en": { + "stringUnit": { + "state": "translated", + "value": "Quality" } } } }, - "player.rebuilding" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Stream wird neu aufgebaut …" + "player.rebuilding": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Stream wird neu aufgebaut …" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rebuilding stream …" + "en": { + "stringUnit": { + "state": "translated", + "value": "Rebuilding stream …" } } } }, - "player.rebuilds" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Stream wird neu aufgebaut" + "player.rebuilds": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Stream wird neu aufgebaut" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Stream will be rebuilt" + "en": { + "stringUnit": { + "state": "translated", + "value": "Stream will be rebuilt" } } } }, - "player.rememberedTrack" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Für diese Serie gemerkt" + "player.rememberedTrack": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Für diese Serie gemerkt" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Remembered for this series" + "en": { + "stringUnit": { + "state": "translated", + "value": "Remembered for this series" } } } }, - "player.rememberedTrackExplanation %@" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Seit du „%@“ gewählt hast, wird diese Spur in jeder Folge genommen." + "player.rememberedTrackExplanation %@": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Seit du „%@“ gewählt hast, wird diese Spur in jeder Folge genommen." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Since you picked “%@”, that track is used for every episode." + "en": { + "stringUnit": { + "state": "translated", + "value": "Since you picked “%@”, that track is used for every episode." } } } }, - "player.restoring" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Stelle wird wiederhergestellt" + "player.restoring": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Stelle wird wiederhergestellt" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Returning to your position" + "en": { + "stringUnit": { + "state": "translated", + "value": "Returning to your position" } } } }, - "player.rotate" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Drehen" + "player.rotate": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Drehen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rotate" + "en": { + "stringUnit": { + "state": "translated", + "value": "Rotate" } } } }, - "player.skipCredits" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Abspann überspringen" + "player.skipCredits": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Abspann überspringen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Skip credits" + "en": { + "stringUnit": { + "state": "translated", + "value": "Skip credits" } } } }, - "player.skipIntro" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Intro überspringen" + "player.skipIntro": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Intro überspringen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Skip intro" + "en": { + "stringUnit": { + "state": "translated", + "value": "Skip intro" } } } }, - "player.stalled" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Es kommen keine Daten mehr. Der Stream ist abgerissen." + "player.stalled": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Es kommen keine Daten mehr. Der Stream ist abgerissen." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "No more data is arriving. The stream stopped." + "en": { + "stringUnit": { + "state": "translated", + "value": "No more data is arriving. The stream stopped." } } } }, - "player.stalledUnlimited" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Es kommen keine Daten mehr. Die Qualität steht auf „Original\", der Server überträgt also die Datei unverändert — dafür reicht diese Verbindung nicht. Unter Einstellungen → Wiedergabe eine kleinere Stufe wählen." + "player.stalledUnlimited": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Es kommen keine Daten mehr. Die Qualität steht auf „Original\", der Server überträgt also die Datei unverändert — dafür reicht diese Verbindung nicht. Unter Einstellungen → Wiedergabe eine kleinere Stufe wählen." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "No more data is arriving. Quality is set to “Original”, so the server sends the file unchanged — this connection cannot carry that. Pick a smaller step under Settings → Playback." + "en": { + "stringUnit": { + "state": "translated", + "value": "No more data is arriving. Quality is set to “Original”, so the server sends the file unchanged — this connection cannot carry that. Pick a smaller step under Settings → Playback." } } } }, - "player.stop" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Beenden" + "player.stop": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Beenden" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Stop" + "en": { + "stringUnit": { + "state": "translated", + "value": "Stop" } } } }, - "player.subtitlesOff" : { - "comment" : "Eintrag, der die Untertitel abschaltet", - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Aus" + "player.subtitleTracks": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Untertitel" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Off" + "en": { + "stringUnit": { + "state": "translated", + "value": "Subtitles" } } } }, - "player.subtitleTracks" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Untertitel" + "player.subtitlesOff": { + "comment": "Eintrag, der die Untertitel abschaltet", + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Aus" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Subtitles" + "en": { + "stringUnit": { + "state": "translated", + "value": "Off" } } } }, - "player.tracks" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ton und Untertitel" + "player.tracks": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Ton und Untertitel" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Audio and subtitles" + "en": { + "stringUnit": { + "state": "translated", + "value": "Audio and subtitles" } } } }, - "player.transcodeRefused" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Der Server hat die Wiedergabe abgelehnt." + "player.transcodeRefused": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Der Server hat die Wiedergabe abgelehnt." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "The server refused playback." + "en": { + "stringUnit": { + "state": "translated", + "value": "The server refused playback." } } } }, - "quality.audioOnly" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nur Ton" + "quality.audioOnly": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Nur Ton" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Audio only" + "en": { + "stringUnit": { + "state": "translated", + "value": "Audio only" } } } }, - "quality.audioOnly.hint" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Der Server kennt für Filme keinen reinen Tonstrom. Diese Stufe überträgt das Bild in der kleinsten Auflösung und blendet es aus." + "quality.audioOnly.hint": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Der Server kennt für Filme keinen reinen Tonstrom. Diese Stufe überträgt das Bild in der kleinsten Auflösung und blendet es aus." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "The server has no audio-only stream for video. This setting sends the picture at the smallest size and hides it." + "en": { + "stringUnit": { + "state": "translated", + "value": "The server has no audio-only stream for video. This setting sends the picture at the smallest size and hides it." } } } }, - "quality.audioShort" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ton" + "quality.audioShort": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Ton" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Audio" + "en": { + "stringUnit": { + "state": "translated", + "value": "Audio" } } } }, - "quality.automatic" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Automatisch" + "quality.autoShort": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Auto" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Automatic" + "en": { + "stringUnit": { + "state": "translated", + "value": "Auto" } } } }, - "quality.autoShort" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Auto" + "quality.automatic": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Automatisch" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Auto" + "en": { + "stringUnit": { + "state": "translated", + "value": "Automatic" } } } }, - "quality.current" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Läuft gerade" + "quality.current": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Läuft gerade" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Currently playing" + "en": { + "stringUnit": { + "state": "translated", + "value": "Currently playing" } } } }, - "quality.current.bitrate" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bitrate" + "quality.current.bitrate": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Bitrate" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bitrate" + "en": { + "stringUnit": { + "state": "translated", + "value": "Bitrate" } } } }, - "quality.current.buffer" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Puffer" + "quality.current.buffer": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Puffer" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Buffer" + "en": { + "stringUnit": { + "state": "translated", + "value": "Buffer" } } } }, - "quality.current.dropped" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Verworfene Bilder" + "quality.current.dropped": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Verworfene Bilder" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dropped frames" + "en": { + "stringUnit": { + "state": "translated", + "value": "Dropped frames" } } } }, - "quality.current.hint" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gemessen, nicht gewünscht: die Stufe sagt, was erlaubt ist — hier steht, was ankommt." + "quality.current.hint": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Gemessen, nicht gewünscht: die Stufe sagt, was erlaubt ist — hier steht, was ankommt." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Measured, not requested: the setting caps the rate, this is what arrives." + "en": { + "stringUnit": { + "state": "translated", + "value": "Measured, not requested: the setting caps the rate, this is what arrives." } } } }, - "quality.current.noLadder" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dieser Stream hat nur eine Stufe — es gibt nichts, worauf der Player ausweichen könnte. Bei Rücklern hilft hier nur eine kleinere Qualität von Hand." + "quality.current.noLadder": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Dieser Stream hat nur eine Stufe — es gibt nichts, worauf der Player ausweichen könnte. Bei Rücklern hilft hier nur eine kleinere Qualität von Hand." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "This stream has a single variant — there is nothing for the player to fall back to. If it stutters, only a manually chosen lower quality helps." + "en": { + "stringUnit": { + "state": "translated", + "value": "This stream has a single variant — there is nothing for the player to fall back to. If it stutters, only a manually chosen lower quality helps." } } } }, - "quality.current.prefetch" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Eigener Vorrat" + "quality.current.prefetch": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Eigener Vorrat" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Own prefetch" + "en": { + "stringUnit": { + "state": "translated", + "value": "Own prefetch" } } } }, - "quality.current.resolution" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Auflösung" + "quality.current.resolution": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Auflösung" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Resolution" + "en": { + "stringUnit": { + "state": "translated", + "value": "Resolution" } } } }, - "quality.current.stalls" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rückler" + "quality.current.stalls": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Rückler" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Stalls" + "en": { + "stringUnit": { + "state": "translated", + "value": "Stalls" } } } }, - "quality.current.variants" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Stufen in der Playlist" + "quality.current.variants": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Stufen in der Playlist" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Variants in playlist" + "en": { + "stringUnit": { + "state": "translated", + "value": "Variants in playlist" } } } }, - "quality.everyRung" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Alle Stufen" + "quality.everyRung": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Alle Stufen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "All steps" + "en": { + "stringUnit": { + "state": "translated", + "value": "All steps" } } } }, - "quality.fullHD" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "1080p" + "quality.fullHD": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "1080p" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "1080p" + "en": { + "stringUnit": { + "state": "translated", + "value": "1080p" } } } }, - "quality.hd" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "720p" + "quality.hd": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "720p" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "720p" + "en": { + "stringUnit": { + "state": "translated", + "value": "720p" } } } }, - "quality.high" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Hoch (12 Mbit/s)" + "quality.high": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Hoch (12 Mbit/s)" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "High (12 Mbps)" + "en": { + "stringUnit": { + "state": "translated", + "value": "High (12 Mbps)" } } } }, - "quality.kilobits %lld" : { - "comment" : "Platzhalter: Kilobit je Sekunde", - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld kbps" + "quality.kilobits %lld": { + "comment": "Platzhalter: Kilobit je Sekunde", + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "%lld kbps" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld kbps" + "en": { + "stringUnit": { + "state": "translated", + "value": "%lld kbps" } } } }, - "quality.low" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Niedrig (4 Mbit/s)" + "quality.low": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Niedrig (4 Mbit/s)" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Low (4 Mbps)" + "en": { + "stringUnit": { + "state": "translated", + "value": "Low (4 Mbps)" } } } }, - "quality.lowRes" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "480p" + "quality.lowRes": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "480p" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "480p" + "en": { + "stringUnit": { + "state": "translated", + "value": "480p" } } } }, - "quality.medium" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mittel (8 Mbit/s)" + "quality.medium": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Mittel (8 Mbit/s)" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Medium (8 Mbps)" + "en": { + "stringUnit": { + "state": "translated", + "value": "Medium (8 Mbps)" } } } }, - "quality.megabits %@" : { - "comment" : "Platzhalter: Megabit je Sekunde", - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ Mbps" + "quality.megabits %@": { + "comment": "Platzhalter: Megabit je Sekunde", + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "%@ Mbps" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ Mbps" + "en": { + "stringUnit": { + "state": "translated", + "value": "%@ Mbps" } } } }, - "quality.minimal" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sparsam (2 Mbit/s)" + "quality.minimal": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Sparsam (2 Mbit/s)" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Data saver (2 Mbps)" + "en": { + "stringUnit": { + "state": "translated", + "value": "Data saver (2 Mbps)" } } } }, - "quality.original" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Original" + "quality.original": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Original" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Original" + "en": { + "stringUnit": { + "state": "translated", + "value": "Original" } } } }, - "quality.originalShort" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Max" + "quality.originalShort": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Max" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Max" + "en": { + "stringUnit": { + "state": "translated", + "value": "Max" } } } }, - "quality.prefetch.ahead %lld" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld s voraus" + "quality.prefetch.ahead %lld": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "%lld s voraus" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld s ahead" + "en": { + "stringUnit": { + "state": "translated", + "value": "%lld s ahead" } } } }, - "quality.prefetch.off" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "aus" + "quality.prefetch.off": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "aus" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "off" + "en": { + "stringUnit": { + "state": "translated", + "value": "off" } } } }, - "quality.prefetch.starting" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "läuft an" + "quality.prefetch.starting": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "läuft an" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "starting" + "en": { + "stringUnit": { + "state": "translated", + "value": "starting" } } } }, - "quality.seconds %@" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ s" + "quality.seconds %@": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "%@ s" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "%@ s" + "en": { + "stringUnit": { + "state": "translated", + "value": "%@ s" } } } }, - "quality.stalls %lld %@" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld · %@ gewartet" + "quality.stalls %lld %@": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "%lld · %@ gewartet" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld · %@ waiting" + "en": { + "stringUnit": { + "state": "translated", + "value": "%lld · %@ waiting" } } } }, - "quickPlay.failed" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Abspielen nicht möglich" + "quickPlay.failed": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Abspielen nicht möglich" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Can’t play this" + "en": { + "stringUnit": { + "state": "translated", + "value": "Can’t play this" } } } }, - "quickPlay.noServer" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Keine Verbindung zum Server." + "quickPlay.noServer": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Keine Verbindung zum Server." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Not connected to the server." + "en": { + "stringUnit": { + "state": "translated", + "value": "Not connected to the server." } } } }, - "quickPlay.nothingToPlay" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Hier ist keine Folge zu finden, die sich abspielen lässt." + "quickPlay.nothingToPlay": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Hier ist keine Folge zu finden, die sich abspielen lässt." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "There’s no episode here that could be played." + "en": { + "stringUnit": { + "state": "translated", + "value": "There’s no episode here that could be played." } } } }, - "runtime.hoursMinutes %lld %lld" : { - "comment" : "Laufzeit mit Stunden", - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld Std %lld Min" + "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" : "%lldh %lldm" + "en": { + "stringUnit": { + "state": "translated", + "value": "%lldh %lldm" } } } }, - "runtime.minutes %lld" : { - "comment" : "Laufzeit unter einer Stunde", - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld Min" + "runtime.minutes %lld": { + "comment": "Laufzeit unter einer Stunde", + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "%lld Min" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lldm" + "en": { + "stringUnit": { + "state": "translated", + "value": "%lldm" } } } }, - "SD" : { - "comment" : "A label for a 720p movie.", - "isCommentAutoGenerated" : true - }, - "search.forgetRecent" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Verlauf löschen" + "search.forgetRecent": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Verlauf löschen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Clear history" + "en": { + "stringUnit": { + "state": "translated", + "value": "Clear history" } } } }, - "search.hint" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Suche nach Filmen, Serien, Folgen und Personen." + "search.hint": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Suche nach Filmen, Serien, Folgen und Personen." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Search for movies, shows, episodes and people." + "en": { + "stringUnit": { + "state": "translated", + "value": "Search for movies, shows, episodes and people." } } } }, - "search.noResults" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nichts gefunden." + "search.noResults": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Nichts gefunden." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nothing found." + "en": { + "stringUnit": { + "state": "translated", + "value": "Nothing found." } } } }, - "search.recent" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Zuletzt gesucht" + "search.recent": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Zuletzt gesucht" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Recent searches" + "en": { + "stringUnit": { + "state": "translated", + "value": "Recent searches" } } } }, - "search.title" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Suche" + "search.title": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Suche" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Search" + "en": { + "stringUnit": { + "state": "translated", + "value": "Search" } } } }, - "season.number %lld" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Staffel %lld" + "season.number %lld": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Staffel %lld" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Season %lld" + "en": { + "stringUnit": { + "state": "translated", + "value": "Season %lld" } } } }, - "season.short %@" : { - "comment" : "A label that shows the number of the selected season.", - "isCommentAutoGenerated" : true + "season.short %@": { + "comment": "A label that shows the number of the selected season.", + "isCommentAutoGenerated": true }, - "season.short %lld" : { - "comment" : "Platzhalter: Staffelnummer", - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Staffel %lld" + "season.short %lld": { + "comment": "Platzhalter: Staffelnummer", + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Staffel %lld" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Season %lld" + "en": { + "stringUnit": { + "state": "translated", + "value": "Season %lld" } } } }, - "settings.account" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Konto" + "settings.account": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Konto" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Account" + "en": { + "stringUnit": { + "state": "translated", + "value": "Account" } } } }, - "settings.address" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Adresse" + "settings.address": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Adresse" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Address" + "en": { + "stringUnit": { + "state": "translated", + "value": "Address" } } } }, - "settings.app" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "App" + "settings.app": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "App" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "App" + "en": { + "stringUnit": { + "state": "translated", + "value": "App" } } } }, - "settings.autoPlayNext" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nächste Folge automatisch" + "settings.autoPlayNext": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Nächste Folge automatisch" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Play next episode automatically" + "en": { + "stringUnit": { + "state": "translated", + "value": "Play next episode automatically" } } } }, - "settings.autoSkipCredits" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Abspann automatisch überspringen" + "settings.autoSkipCredits": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Abspann automatisch überspringen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Skip credits automatically" + "en": { + "stringUnit": { + "state": "translated", + "value": "Skip credits automatically" } } } }, - "settings.autoSkipIntro" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Intro automatisch überspringen" + "settings.autoSkipIntro": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Intro automatisch überspringen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Skip intros automatically" + "en": { + "stringUnit": { + "state": "translated", + "value": "Skip intros automatically" } } } }, - "settings.build" : { - "comment" : "Zeitpunkt, zu dem der laufende Build entstanden ist", - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Build" + "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" : "Build" + "en": { + "stringUnit": { + "state": "translated", + "value": "Build" } } } }, - "settings.concurrent" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Höchstens gleichzeitig" + "settings.concurrent": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Höchstens gleichzeitig" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "At most at once" + "en": { + "stringUnit": { + "state": "translated", + "value": "At most at once" } } } }, - "settings.concurrentExplanation" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Eine ganze Staffel auf einmal zu laden teilt die Leitung so auf, dass keine Folge fertig wird. Unterwegs ist eine fertige Folge mehr wert als zehn halbe." + "settings.concurrentExplanation": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Eine ganze Staffel auf einmal zu laden teilt die Leitung so auf, dass keine Folge fertig wird. Unterwegs ist eine fertige Folge mehr wert als zehn halbe." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Loading a whole season at once splits the line so that no episode finishes. On the move, one finished episode beats ten half ones." + "en": { + "stringUnit": { + "state": "translated", + "value": "Loading a whole season at once splits the line so that no episode finishes. On the move, one finished episode beats ten half ones." } } } }, - "settings.concurrentHeader" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gleichzeitige Downloads" + "settings.concurrentHeader": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Gleichzeitige Downloads" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Concurrent downloads" + "en": { + "stringUnit": { + "state": "translated", + "value": "Concurrent downloads" } } } }, - "settings.connection" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Verbindung" + "settings.connection": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Verbindung" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Connection" + "en": { + "stringUnit": { + "state": "translated", + "value": "Connection" } } } }, - "settings.connectionLocal" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Lokal" + "settings.connectionLocal": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Lokal" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Local" + "en": { + "stringUnit": { + "state": "translated", + "value": "Local" } } } }, - "settings.connectionRemote" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Direkt von außen" + "settings.connectionRemote": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Direkt von außen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Direct remote" + "en": { + "stringUnit": { + "state": "translated", + "value": "Direct remote" } } } }, - "settings.controls" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Steuerung" + "settings.controls": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Steuerung" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Controls" + "en": { + "stringUnit": { + "state": "translated", + "value": "Controls" } } } }, - "settings.controlsExplanation" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Zurück und vor lassen sich getrennt einstellen: zurück springt man, weil man etwas nicht verstanden hat — vor, weil man etwas überspringen will." + "settings.controlsExplanation": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Zurück und vor lassen sich getrennt einstellen: zurück springt man, weil man etwas nicht verstanden hat — vor, weil man etwas überspringen will." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Back and forward are set separately: you jump back because you missed something, and forward because you want to skip something." + "en": { + "stringUnit": { + "state": "translated", + "value": "Back and forward are set separately: you jump back because you missed something, and forward because you want to skip something." } } } }, - "settings.defaultRate" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Startgeschwindigkeit" + "settings.defaultRate": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Startgeschwindigkeit" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Starting speed" + "en": { + "stringUnit": { + "state": "translated", + "value": "Starting speed" } } } }, - "settings.deleteAfterDay" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nach einem Tag" + "settings.deleteAfterDay": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Nach einem Tag" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "After a day" + "en": { + "stringUnit": { + "state": "translated", + "value": "After a day" } } } }, - "settings.deleteAfterWeek" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nach einer Woche" + "settings.deleteAfterWeek": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Nach einer Woche" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "After a week" + "en": { + "stringUnit": { + "state": "translated", + "value": "After a week" } } } }, - "settings.deleteImmediately" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sofort" + "settings.deleteImmediately": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Sofort" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Immediately" + "en": { + "stringUnit": { + "state": "translated", + "value": "Immediately" } } } }, - "settings.deleteNever" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nie" + "settings.deleteNever": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Nie" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Never" + "en": { + "stringUnit": { + "state": "translated", + "value": "Never" } } } }, - "settings.deleteWatched" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gesehenes löschen" + "settings.deleteWatched": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Gesehenes löschen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Delete watched" + "en": { + "stringUnit": { + "state": "translated", + "value": "Delete watched" } } } }, - "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.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" : "Missing episodes are downloaded once you finish one. Watched titles are removed after the chosen delay." + "en": { + "stringUnit": { + "state": "translated", + "value": "Missing episodes are downloaded once you finish one. Watched titles are removed after the chosen delay." } } } }, - "settings.downloadsHeaderExplanation" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Wie viel Platz Offline-Titel belegen dürfen, wie viele gleichzeitig laden und wann sie wieder verschwinden." + "settings.downloadsHeaderExplanation": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Wie viel Platz Offline-Titel belegen dürfen, wie viele gleichzeitig laden und wann sie wieder verschwinden." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "How much space offline titles may take, how many load at once, and when they disappear again." + "en": { + "stringUnit": { + "state": "translated", + "value": "How much space offline titles may take, how many load at once, and when they disappear again." } } } }, - "settings.episodes" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Folgen" + "settings.episodes": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Folgen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Episodes" + "en": { + "stringUnit": { + "state": "translated", + "value": "Episodes" } } } }, - "settings.forward" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Vorspulen" + "settings.forward": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Vorspulen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Fast-forward" + "en": { + "stringUnit": { + "state": "translated", + "value": "Fast-forward" } } } }, - "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 %@": { + "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.keepCount %lld": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "%lld ungesehene" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld unwatched" + "en": { + "stringUnit": { + "state": "translated", + "value": "%lld unwatched" } } } }, - "settings.keepOff" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Aus" + "settings.keepOff": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Aus" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Off" + "en": { + "stringUnit": { + "state": "translated", + "value": "Off" } } } }, - "settings.keepUnwatched" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Folgen vorhalten" + "settings.keepUnwatched": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Folgen vorhalten" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Keep episodes ready" + "en": { + "stringUnit": { + "state": "translated", + "value": "Keep episodes ready" } } } }, - "settings.libraries" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mediatheken" + "settings.libraries": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Mediatheken" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Libraries" + "en": { + "stringUnit": { + "state": "translated", + "value": "Libraries" } } } }, - "settings.lockLandscape" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Im Querformat sperren" + "settings.lockLandscape": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Im Querformat sperren" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Lock to landscape" + "en": { + "stringUnit": { + "state": "translated", + "value": "Lock to landscape" } } } }, - "settings.lockLandscapeExplanation" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Der Player dreht beim Öffnen ins Querformat und bleibt dort. Abgeschaltet folgt er dem Gerät — für den, der im Liegen schaut." + "settings.lockLandscapeExplanation": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Der Player dreht beim Öffnen ins Querformat und bleibt dort. Abgeschaltet folgt er dem Gerät — für den, der im Liegen schaut." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "The player rotates to landscape when it opens and stays there. Switched off it follows the device — for watching while lying down." + "en": { + "stringUnit": { + "state": "translated", + "value": "The player rotates to landscape when it opens and stays there. Switched off it follows the device — for watching while lying down." } } } }, - "settings.playback" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Wiedergabe" + "settings.playback": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Wiedergabe" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Playback" + "en": { + "stringUnit": { + "state": "translated", + "value": "Playback" } } } }, - "settings.playbackExplanation" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Wie gut das Bild sein soll, wie weit der Doppeltipp springt und was am Ende einer Folge passiert." + "settings.playbackExplanation": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Wie gut das Bild sein soll, wie weit der Doppeltipp springt und was am Ende einer Folge passiert." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "How good the picture should be, how far a double tap jumps, and what happens at the end of an episode." + "en": { + "stringUnit": { + "state": "translated", + "value": "How good the picture should be, how far a double tap jumps, and what happens at the end of an episode." } } } }, - "settings.quality" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Qualität" + "settings.quality": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Qualität" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Quality" + "en": { + "stringUnit": { + "state": "translated", + "value": "Quality" } } } }, - "settings.qualityCellular" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Über Mobilfunk" + "settings.qualityCellular": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Über Mobilfunk" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "On cellular" + "en": { + "stringUnit": { + "state": "translated", + "value": "On cellular" } } } }, - "settings.qualityExplanation" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Die Obergrenze, nicht das Ziel. Im eigenen Netz überträgt der Server das Original; unterwegs beginnt Flaro kleiner und tastet sich hinauf, solange die Verbindung es trägt — höchstens bis zu dieser Stufe. Auflösung und Bitrate gehen dabei zusammen." + "settings.qualityExplanation": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Die Obergrenze, nicht das Ziel. Im eigenen Netz überträgt der Server das Original; unterwegs beginnt Flaro kleiner und tastet sich hinauf, solange die Verbindung es trägt — höchstens bis zu dieser Stufe. Auflösung und Bitrate gehen dabei zusammen." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "An upper limit, not a target. On your own network the server sends the original; away from home Flaro starts lower and works its way up as long as the connection holds — never above this step. Resolution and bitrate move together." + "en": { + "stringUnit": { + "state": "translated", + "value": "An upper limit, not a target. On your own network the server sends the original; away from home Flaro starts lower and works its way up as long as the connection holds — never above this step. Resolution and bitrate move together." } } } }, - "settings.qualityUnlimitedHint" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Zuletzt hat die Wiedergabe gehakt. Ohne Grenze überträgt der Server das Original — gibt die Leitung das nicht her, kann nichts ausweichen. Eine Stufe darunter läuft dann ruhiger." + "settings.qualityUnlimitedHint": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Zuletzt hat die Wiedergabe gehakt. Ohne Grenze überträgt der Server das Original — gibt die Leitung das nicht her, kann nichts ausweichen. Eine Stufe darunter läuft dann ruhiger." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Playback stalled recently. Without a limit the server sends the original — if the connection cannot carry it, there is nothing to fall back to. One step down plays more smoothly." + "en": { + "stringUnit": { + "state": "translated", + "value": "Playback stalled recently. Without a limit the server sends the original — if the connection cannot carry it, there is nothing to fall back to. One step down plays more smoothly." } } } }, - "settings.qualityWifi" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Im WLAN" + "settings.qualityWifi": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Im WLAN" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "On Wi-Fi" + "en": { + "stringUnit": { + "state": "translated", + "value": "On Wi-Fi" } } } }, - "settings.rateNormal" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Normal" + "settings.rateNormal": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Normal" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Normal" + "en": { + "stringUnit": { + "state": "translated", + "value": "Normal" } } } }, - "settings.rememberQuality" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gewählte Qualität merken" + "settings.rememberQuality": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Gewählte Qualität merken" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Remember the chosen quality" + "en": { + "stringUnit": { + "state": "translated", + "value": "Remember the chosen quality" } } } }, - "settings.rememberQualityExplanation" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Die im Player gewählte Stufe gilt dann auch für den nächsten Titel. Gibt es sie dort nicht, wird die nächstgelegene genommen." + "settings.rememberQualityExplanation": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Die im Player gewählte Stufe gilt dann auch für den nächsten Titel. Gibt es sie dort nicht, wird die nächstgelegene genommen." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "The step you pick in the player then applies to the next title as well. If it isn't available there, the closest one is used." + "en": { + "stringUnit": { + "state": "translated", + "value": "The step you pick in the player then applies to the next title as well. If it isn't available there, the closest one is used." } } } }, - "settings.rewind" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Zurückspulen" + "settings.rewind": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Zurückspulen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rewind" + "en": { + "stringUnit": { + "state": "translated", + "value": "Rewind" } } } }, - "settings.scanExplanation" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Der Suchlauf durchsucht die Ordner auf dem Server nach neuen Dateien. Das kann einige Minuten dauern." + "settings.scanExplanation": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Der Suchlauf durchsucht die Ordner auf dem Server nach neuen Dateien. Das kann einige Minuten dauern." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "A scan looks through the folders on the server for new files. It can take a few minutes." + "en": { + "stringUnit": { + "state": "translated", + "value": "A scan looks through the folders on the server for new files. It can take a few minutes." } } } }, - "settings.seconds %@" : { - "comment" : "Ein Picker, das die Anzahl der Sekunden zeigt, die man zurückgehen kann.", - "isCommentAutoGenerated" : true + "settings.seconds %@": { + "comment": "Ein Picker, das die Anzahl der Sekunden zeigt, die man zurückgehen kann.", + "isCommentAutoGenerated": true + }, + "settings.seconds %lld": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "%lld Sekunden" + } + }, + "en": { + "stringUnit": { + "state": "translated", + "value": "%lld seconds" + } + } + } }, - "settings.seconds %lld" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld Sekunden" + "settings.section.look": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Darstellung" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld seconds" + "en": { + "stringUnit": { + "state": "translated", + "value": "Appearance" } } } }, - "settings.section.look" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Darstellung" + "settings.section.playing": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Wiedergabe & Offline" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Appearance" + "en": { + "stringUnit": { + "state": "translated", + "value": "Playback & offline" } } } }, - "settings.section.playing" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Wiedergabe & Offline" + "settings.server": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Server" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Playback & offline" + "en": { + "stringUnit": { + "state": "translated", + "value": "Server" } } } }, - "settings.server" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Server" + "settings.serverName": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Name" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Server" + "en": { + "stringUnit": { + "state": "translated", + "value": "Name" } } } }, - "settings.serverName" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Name" + "settings.showBitrate": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Bitrate im Qualitätsmenü zeigen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Name" + "en": { + "stringUnit": { + "state": "translated", + "value": "Show the bitrate in the quality menu" } } } }, - "settings.showBitrate" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bitrate im Qualitätsmenü zeigen" + "settings.signOutConfirm": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Wirklich abmelden?" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Show the bitrate in the quality menu" + "en": { + "stringUnit": { + "state": "translated", + "value": "Sign out?" } } } }, - "settings.signOutConfirm" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Wirklich abmelden?" + "settings.signOutExplanation": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Das Token wird vom Gerät entfernt. Heruntergeladene Titel bleiben erhalten." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sign out?" + "en": { + "stringUnit": { + "state": "translated", + "value": "The token will be removed from this device. Downloaded titles are kept." } } } }, - "settings.signOutExplanation" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Das Token wird vom Gerät entfernt. Heruntergeladene Titel bleiben erhalten." + "settings.skipExtraScenes": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Zusätzliche Szenen mit überspringen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "The token will be removed from this device. Downloaded titles are kept." + "en": { + "stringUnit": { + "state": "translated", + "value": "Skip extra scenes too" } } } }, - "settings.storageLimit" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Speicherlimit" + "settings.storageLimit": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Speicherlimit" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Storage limit" + "en": { + "stringUnit": { + "state": "translated", + "value": "Storage limit" } } } }, - "settings.storageUnlimited" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Unbegrenzt" + "settings.storageUnlimited": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Unbegrenzt" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Unlimited" + "en": { + "stringUnit": { + "state": "translated", + "value": "Unlimited" } } } }, - "settings.version" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Version" + "settings.version": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Version" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Version" + "en": { + "stringUnit": { + "state": "translated", + "value": "Version" } } } }, - "sidebar.close" : { - "comment" : "Barrierefreiheit: abgedunkelte Fläche", - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Seitenleiste schließen" + "sidebar.close": { + "comment": "Barrierefreiheit: abgedunkelte Fläche", + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Seitenleiste schließen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Close sidebar" + "en": { + "stringUnit": { + "state": "translated", + "value": "Close sidebar" } } } }, - "sidebar.downloads" : { - "comment" : "Eintrag in der Seitenleiste", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Downloads" + "sidebar.downloads": { + "comment": "Eintrag in der Seitenleiste", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Downloads" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Downloads" + "en": { + "stringUnit": { + "state": "translated", + "value": "Downloads" } } } }, - "sidebar.edit" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bearbeiten" + "sidebar.edit": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Bearbeiten" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Edit" + "en": { + "stringUnit": { + "state": "translated", + "value": "Edit" } } } }, - "sidebar.editHint" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ziehen zum Umsortieren, Haken zum Ein- und Ausblenden." + "sidebar.editHint": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Ziehen zum Umsortieren, Haken zum Ein- und Ausblenden." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Drag to reorder, tap the check to show or hide." + "en": { + "stringUnit": { + "state": "translated", + "value": "Drag to reorder, tap the check to show or hide." } } } }, - "sidebar.hide" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ausblenden" + "sidebar.hide": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Ausblenden" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Hide" + "en": { + "stringUnit": { + "state": "translated", + "value": "Hide" } } } }, - "sidebar.home" : { - "comment" : "Eintrag der Startseite in der Seitenleiste", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Start" + "sidebar.home": { + "comment": "Eintrag der Startseite in der Seitenleiste", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Start" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Home" + "en": { + "stringUnit": { + "state": "translated", + "value": "Home" } } } }, - "sidebar.libraries" : { - "comment" : "Überschrift über der Liste der Mediatheken", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mediatheken" + "sidebar.libraries": { + "comment": "Überschrift über der Liste der Mediatheken", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Mediatheken" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Libraries" + "en": { + "stringUnit": { + "state": "translated", + "value": "Libraries" } } } }, - "sidebar.library" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bibliothek" + "sidebar.library": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Bibliothek" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Library" + "en": { + "stringUnit": { + "state": "translated", + "value": "Library" } } } }, - "sidebar.liveTV" : { - "comment" : "Eintrag in der Seitenleiste", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Live-TV" + "sidebar.liveTV": { + "comment": "Eintrag in der Seitenleiste", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Live-TV" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Live TV" + "en": { + "stringUnit": { + "state": "translated", + "value": "Live TV" } } } }, - "sidebar.more" : { - "comment" : "Überschrift über Live-TV, Downloads und Einstellungen", - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mehr" + "sidebar.more": { + "comment": "Überschrift über Live-TV, Downloads und Einstellungen", + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Mehr" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "More" + "en": { + "stringUnit": { + "state": "translated", + "value": "More" } } } }, - "sidebar.moveToSide" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Navigation zur Seite" + "sidebar.moveToSide": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Navigation zur Seite" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Move navigation to side" + "en": { + "stringUnit": { + "state": "translated", + "value": "Move navigation to side" } } } }, - "sidebar.moveToTop" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Navigation nach oben" + "sidebar.moveToTop": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Navigation nach oben" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Move navigation to top" + "en": { + "stringUnit": { + "state": "translated", + "value": "Move navigation to top" } } } }, - "sidebar.open" : { - "comment" : "Barrierefreiheit: Hamburger-Knopf", - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Seitenleiste öffnen" + "sidebar.open": { + "comment": "Barrierefreiheit: Hamburger-Knopf", + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Seitenleiste öffnen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Open sidebar" + "en": { + "stringUnit": { + "state": "translated", + "value": "Open sidebar" } } } }, - "sidebar.search" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Suchen" + "sidebar.search": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Suchen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Search" + "en": { + "stringUnit": { + "state": "translated", + "value": "Search" } } } }, - "sidebar.settings" : { - "comment" : "Eintrag in der Seitenleiste", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Einstellungen" + "sidebar.settings": { + "comment": "Eintrag in der Seitenleiste", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Einstellungen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Settings" + "en": { + "stringUnit": { + "state": "translated", + "value": "Settings" } } } }, - "sidebar.show" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Einblenden" + "sidebar.show": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Einblenden" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Show" + "en": { + "stringUnit": { + "state": "translated", + "value": "Show" } } } }, - "signIn.cancelled" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Anmeldung abgebrochen." + "signIn.cancelled": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Anmeldung abgebrochen." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sign-in cancelled." + "en": { + "stringUnit": { + "state": "translated", + "value": "Sign-in cancelled." } } } }, - "signIn.flaro.addressHint" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Schema und Port darfst du weglassen — Flaro probiert die üblichen durch." + "signIn.flaro.addressHint": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Schema und Port darfst du weglassen — Flaro probiert die üblichen durch." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "You can leave out the scheme and port — Flaro tries the usual ones." + "en": { + "stringUnit": { + "state": "translated", + "value": "You can leave out the scheme and port — Flaro tries the usual ones." } } } }, - "signIn.flaro.addressMissing" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Trag eine Adresse ein." + "signIn.flaro.addressMissing": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Trag eine Adresse ein." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enter an address." + "en": { + "stringUnit": { + "state": "translated", + "value": "Enter an address." } } } }, - "signIn.flaro.addressPlaceholder" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "z. B. 192.168.1.50 oder medien.example.com" + "signIn.flaro.addressPlaceholder": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "z. B. 192.168.1.50 oder medien.example.com" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "e.g. 192.168.1.50 or media.example.com" + "en": { + "stringUnit": { + "state": "translated", + "value": "e.g. 192.168.1.50 or media.example.com" } } } }, - "signIn.flaro.addressPrompt" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Wo steht dein Server?" + "signIn.flaro.addressPrompt": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Wo steht dein Server?" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Where is your server?" + "en": { + "stringUnit": { + "state": "translated", + "value": "Where is your server?" } } } }, - "signIn.flaro.changeAddress" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Andere Adresse" + "signIn.flaro.changeAddress": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Andere Adresse" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Change address" + "en": { + "stringUnit": { + "state": "translated", + "value": "Change address" } } } }, - "signIn.flaro.codeExpired" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Der Code ist abgelaufen. Fordere einen neuen an." + "signIn.flaro.codeExpired": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Der Code ist abgelaufen. Fordere einen neuen an." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "The code has expired. Request a new one." + "en": { + "stringUnit": { + "state": "translated", + "value": "The code has expired. Request a new one." } } } }, - "signIn.flaro.codeHint" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Öffne Flaro auf deinem Telefon und gib diesen Code ein." + "signIn.flaro.codeHint": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Öffne Flaro auf deinem Telefon und gib diesen Code ein." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Open Flaro on your phone and enter this code." + "en": { + "stringUnit": { + "state": "translated", + "value": "Open Flaro on your phone and enter this code." } } } }, - "signIn.flaro.credentialsMissing" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Benutzername und Passwort fehlen." + "signIn.flaro.credentialsMissing": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Benutzername und Passwort fehlen." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Username and password are missing." + "en": { + "stringUnit": { + "state": "translated", + "value": "Username and password are missing." } } } }, - "signIn.flaro.findServer" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Server suchen" + "signIn.flaro.findServer": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Server suchen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Find server" + "en": { + "stringUnit": { + "state": "translated", + "value": "Find server" } } } }, - "signIn.flaro.needsSetup" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dieser Server ist noch nicht eingerichtet. Öffne ihn im Browser und lege ein Konto an." + "signIn.flaro.needsSetup": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Dieser Server ist noch nicht eingerichtet. Öffne ihn im Browser und lege ein Konto an." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "This server isn't set up yet. Open it in a browser and create an account." + "en": { + "stringUnit": { + "state": "translated", + "value": "This server isn't set up yet. Open it in a browser and create an account." } } } }, - "signIn.flaro.notFound" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Unter dieser Adresse antwortet kein Flaro-Server." + "signIn.flaro.notFound": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Unter dieser Adresse antwortet kein Flaro-Server." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "No Flaro server answers at this address." + "en": { + "stringUnit": { + "state": "translated", + "value": "No Flaro server answers at this address." } } } }, - "signIn.flaro.searching" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Server wird gesucht …" + "signIn.flaro.searching": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Server wird gesucht …" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Looking for the server…" + "en": { + "stringUnit": { + "state": "translated", + "value": "Looking for the server…" } } } }, - "signIn.flaro.signIn" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Anmelden" + "signIn.flaro.signIn": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Anmelden" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sign in" + "en": { + "stringUnit": { + "state": "translated", + "value": "Sign in" } } } }, - "signIn.flaro.signingIn" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Wird angemeldet …" + "signIn.flaro.signingIn": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Wird angemeldet …" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Signing in…" + "en": { + "stringUnit": { + "state": "translated", + "value": "Signing in…" } } } }, - "signIn.flaro.title" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Flaro-Server" + "signIn.flaro.title": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Flaro-Server" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Flaro Server" + "en": { + "stringUnit": { + "state": "translated", + "value": "Flaro Server" } } } }, - "signIn.flaro.useCode" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Stattdessen Code" + "signIn.flaro.useCode": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Stattdessen Code" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Use a code instead" + "en": { + "stringUnit": { + "state": "translated", + "value": "Use a code instead" } } } }, - "signIn.flaro.usePassword" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Stattdessen Passwort" + "signIn.flaro.usePassword": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Stattdessen Passwort" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Use a password instead" + "en": { + "stringUnit": { + "state": "translated", + "value": "Use a password instead" } } } }, - "signIn.flaro.waitingForApproval" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Warte auf Bestätigung" + "signIn.flaro.waitingForApproval": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Warte auf Bestätigung" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Waiting for approval" + "en": { + "stringUnit": { + "state": "translated", + "value": "Waiting for approval" } } } }, - "signIn.jellyfin.addressHint" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Schema und Port kannst du weglassen — die üblichen werden durchprobiert." + "signIn.jellyfin.addressHint": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Schema und Port kannst du weglassen — die üblichen werden durchprobiert." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "You can leave out the scheme and port — the usual ones are tried in turn." + "en": { + "stringUnit": { + "state": "translated", + "value": "You can leave out the scheme and port — the usual ones are tried in turn." } } } }, - "signIn.jellyfin.addressMissing" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Trag bitte die Adresse deines Servers ein." + "signIn.jellyfin.addressMissing": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Trag bitte die Adresse deines Servers ein." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Please enter your server's address." + "en": { + "stringUnit": { + "state": "translated", + "value": "Please enter your server's address." } } } }, - "signIn.jellyfin.addressPlaceholder" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "192.168.1.50 oder medien.example.com" + "signIn.jellyfin.addressPlaceholder": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "192.168.1.50 oder medien.example.com" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "192.168.1.50 or media.example.com" + "en": { + "stringUnit": { + "state": "translated", + "value": "192.168.1.50 or media.example.com" } } } }, - "signIn.jellyfin.addressPrompt" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Wo steht dein Server?" + "signIn.jellyfin.addressPrompt": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Wo steht dein Server?" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Where is your server?" + "en": { + "stringUnit": { + "state": "translated", + "value": "Where is your server?" } } } }, - "signIn.jellyfin.codeExpired" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Der Code ist abgelaufen. Fordere einen neuen an oder melde dich mit Passwort an." + "signIn.jellyfin.codeExpired": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Der Code ist abgelaufen. Fordere einen neuen an oder melde dich mit Passwort an." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "The code has expired. Request a new one or sign in with a password." + "en": { + "stringUnit": { + "state": "translated", + "value": "The code has expired. Request a new one or sign in with a password." } } } }, - "signIn.jellyfin.connect" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Verbinden" + "signIn.jellyfin.connect": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Verbinden" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Connect" + "en": { + "stringUnit": { + "state": "translated", + "value": "Connect" } } } }, - "signIn.jellyfin.foundServer" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gefundener Server" + "signIn.jellyfin.foundServer": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Gefundener Server" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Server found" + "en": { + "stringUnit": { + "state": "translated", + "value": "Server found" } } } }, - "signIn.jellyfin.notFound" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Unter dieser Adresse antwortet kein Jellyfin-Server." + "signIn.jellyfin.notFound": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Unter dieser Adresse antwortet kein Jellyfin-Server." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "No Jellyfin server answers at that address." + "en": { + "stringUnit": { + "state": "translated", + "value": "No Jellyfin server answers at that address." } } } }, - "signIn.jellyfin.otherAddress" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Andere Adresse eintragen" + "signIn.jellyfin.otherAddress": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Andere Adresse eintragen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Enter a different address" + "en": { + "stringUnit": { + "state": "translated", + "value": "Enter a different address" } } } }, - "signIn.jellyfin.passwordNote" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Das Passwort wird nur für diese eine Anfrage gehalten. Gespeichert wird allein der Zugangsschlüssel, und der im Schlüsselbund." + "signIn.jellyfin.passwordNote": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Das Passwort wird nur für diese eine Anfrage gehalten. Gespeichert wird allein der Zugangsschlüssel, und der im Schlüsselbund." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "The password is held for this one request only. Only the access token is stored, and it goes into the keychain." + "en": { + "stringUnit": { + "state": "translated", + "value": "The password is held for this one request only. Only the access token is stored, and it goes into the keychain." } } } }, - "signIn.jellyfin.privacy" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "So erreicht dein Passwort diese App nie." + "signIn.jellyfin.privacy": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "So erreicht dein Passwort diese App nie." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "This way your password never reaches this app." + "en": { + "stringUnit": { + "state": "translated", + "value": "This way your password never reaches this app." } } } }, - "signIn.jellyfin.quickConnectHint" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Öffne Jellyfin auf einem Gerät, an dem du schon angemeldet bist, und gib diesen Code unter „Benutzer → Quick Connect“ ein." + "signIn.jellyfin.quickConnectHint": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Öffne Jellyfin auf einem Gerät, an dem du schon angemeldet bist, und gib diesen Code unter „Benutzer → Quick Connect“ ein." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Open Jellyfin on a device you're already signed in on and enter this code under “User → Quick Connect”." + "en": { + "stringUnit": { + "state": "translated", + "value": "Open Jellyfin on a device you're already signed in on and enter this code under “User → Quick Connect”." } } } }, - "signIn.jellyfin.searching" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Server wird gesucht …" + "signIn.jellyfin.searching": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Server wird gesucht …" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Looking for the server …" + "en": { + "stringUnit": { + "state": "translated", + "value": "Looking for the server …" } } } }, - "signIn.jellyfin.signingIn" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Anmeldung läuft …" + "signIn.jellyfin.signingIn": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Anmeldung läuft …" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Signing in …" + "en": { + "stringUnit": { + "state": "translated", + "value": "Signing in …" } } } }, - "signIn.jellyfin.submit" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Anmelden" + "signIn.jellyfin.submit": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Anmelden" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sign in" + "en": { + "stringUnit": { + "state": "translated", + "value": "Sign in" } } } }, - "signIn.jellyfin.title" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Jellyfin-Server" + "signIn.jellyfin.title": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Jellyfin-Server" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Jellyfin server" + "en": { + "stringUnit": { + "state": "translated", + "value": "Jellyfin server" } } } }, - "signIn.jellyfin.useCode" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Stattdessen einen Code bestätigen" + "signIn.jellyfin.useCode": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Stattdessen einen Code bestätigen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Approve a code instead" + "en": { + "stringUnit": { + "state": "translated", + "value": "Approve a code instead" } } } }, - "signIn.jellyfin.usePassword" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Stattdessen Benutzername und Passwort" + "signIn.jellyfin.usePassword": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Stattdessen Benutzername und Passwort" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Use a username and password instead" + "en": { + "stringUnit": { + "state": "translated", + "value": "Use a username and password instead" } } } }, - "signIn.jellyfin.waitingForApproval" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Warte auf die Bestätigung …" + "signIn.jellyfin.waitingForApproval": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Warte auf die Bestätigung …" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Waiting for approval …" + "en": { + "stringUnit": { + "state": "translated", + "value": "Waiting for approval …" } } } }, - "signIn.password" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Passwort" + "signIn.password": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Passwort" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Password" + "en": { + "stringUnit": { + "state": "translated", + "value": "Password" } } } }, - "signIn.privacy" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Die Anmeldung läuft unmittelbar mit deinem Server. Kein fremder Dienst ist daran beteiligt." + "signIn.privacy": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Die Anmeldung läuft unmittelbar mit deinem Server. Kein fremder Dienst ist daran beteiligt." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "You sign in directly with your own server. No third-party service is involved." + "en": { + "stringUnit": { + "state": "translated", + "value": "You sign in directly with your own server. No third-party service is involved." } } } }, - "signIn.tagline" : { - "comment" : "Untertitel auf dem Anmeldebildschirm", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Deine Mediathek, schöner." + "signIn.tagline": { + "comment": "Untertitel auf dem Anmeldebildschirm", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Deine Mediathek, schöner." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Your library, nicer." + "en": { + "stringUnit": { + "state": "translated", + "value": "Your library, nicer." } } } }, - "signIn.userName" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Benutzername" + "signIn.userName": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Benutzername" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Username" + "en": { + "stringUnit": { + "state": "translated", + "value": "Username" } } } }, - "signIn.withFlaro" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mit Flaro verbinden" + "signIn.withFlaro": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Mit Flaro verbinden" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Connect to Flaro" + "en": { + "stringUnit": { + "state": "translated", + "value": "Connect to Flaro" } } } }, - "signIn.withJellyfin" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mit Jellyfin anmelden" + "signIn.withJellyfin": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Mit Jellyfin anmelden" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sign in with Jellyfin" + "en": { + "stringUnit": { + "state": "translated", + "value": "Sign in with Jellyfin" } } } }, - "sort.lastPlayed" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Zuletzt gesehen" + "sort.lastPlayed": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Zuletzt gesehen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Last watched" + "en": { + "stringUnit": { + "state": "translated", + "value": "Last watched" } } } }, - "sort.rating" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bewertung" + "sort.rating": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Bewertung" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rating" + "en": { + "stringUnit": { + "state": "translated", + "value": "Rating" } } } }, - "sort.recentlyAdded" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Zuletzt hinzugefügt" + "sort.recentlyAdded": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Zuletzt hinzugefügt" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Recently added" + "en": { + "stringUnit": { + "state": "translated", + "value": "Recently added" } } } }, - "sort.releaseDate" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Erscheinungsjahr" + "sort.releaseDate": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Erscheinungsjahr" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Release date" + "en": { + "stringUnit": { + "state": "translated", + "value": "Release date" } } } }, - "sort.titleAscending" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Titel (A–Z)" + "sort.titleAscending": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Titel (A–Z)" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Title (A–Z)" + "en": { + "stringUnit": { + "state": "translated", + "value": "Title (A–Z)" } } } }, - "sort.titleDescending" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Titel (Z–A)" + "sort.titleDescending": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Titel (Z–A)" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Title (Z–A)" + "en": { + "stringUnit": { + "state": "translated", + "value": "Title (Z–A)" } } } }, - "subtitleStyle.headerExplanation" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Größe und Farbe der Untertitel im Player." + "subtitleStyle.headerExplanation": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Größe und Farbe der Untertitel im Player." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "How subtitles look in the player." + "en": { + "stringUnit": { + "state": "translated", + "value": "How subtitles look in the player." } } } }, - "subtitleStyle.palette" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Darstellung" + "subtitleStyle.palette": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Darstellung" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Appearance" + "en": { + "stringUnit": { + "state": "translated", + "value": "Appearance" } } } }, - "subtitleStyle.palette.blackOnWhite" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Schwarz auf Weiß" + "subtitleStyle.palette.blackOnWhite": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Schwarz auf Weiß" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Black on white" + "en": { + "stringUnit": { + "state": "translated", + "value": "Black on white" } } } }, - "subtitleStyle.palette.whiteOnBlack" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Weiß auf Schwarz" + "subtitleStyle.palette.whiteOnBlack": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Weiß auf Schwarz" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "White on black" + "en": { + "stringUnit": { + "state": "translated", + "value": "White on black" } } } }, - "subtitleStyle.palette.whiteOnClear" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Weiß" + "subtitleStyle.palette.whiteOnClear": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Weiß" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "White" + "en": { + "stringUnit": { + "state": "translated", + "value": "White" } } } }, - "subtitleStyle.palette.yellowOnBlack" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gelb auf Schwarz" + "subtitleStyle.palette.yellowOnBlack": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Gelb auf Schwarz" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Yellow on black" + "en": { + "stringUnit": { + "state": "translated", + "value": "Yellow on black" } } } }, - "subtitleStyle.palette.yellowOnClear" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gelb" + "subtitleStyle.palette.yellowOnClear": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Gelb" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Yellow" + "en": { + "stringUnit": { + "state": "translated", + "value": "Yellow" } } } }, - "subtitleStyle.paletteExplanation" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gilt für den Standardplayer und für Untertitel, die als Text vorliegen. Bilduntertitel von Blu-rays brennt der Server ins Bild — dort gibt es keinen Text zu gestalten, und diese Einstellungen bleiben ohne Wirkung." + "subtitleStyle.paletteExplanation": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Gilt für den Standardplayer und für Untertitel, die als Text vorliegen. Bilduntertitel von Blu-rays brennt der Server ins Bild — dort gibt es keinen Text zu gestalten, und diese Einstellungen bleiben ohne Wirkung." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Applies to the standard player and to subtitles that come as text. Image subtitles from Blu-rays are burned into the picture by the server — there is no text to style, so these settings have no effect there." + "en": { + "stringUnit": { + "state": "translated", + "value": "Applies to the standard player and to subtitles that come as text. Image subtitles from Blu-rays are burned into the picture by the server — there is no text to style, so these settings have no effect there." } } } }, - "subtitleStyle.paletteHeader" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Schrift und Hintergrund" + "subtitleStyle.paletteHeader": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Schrift und Hintergrund" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Text and background" + "en": { + "stringUnit": { + "state": "translated", + "value": "Text and background" } } } }, - "subtitleStyle.previewHeader" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Vorschau" + "subtitleStyle.previewHeader": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Vorschau" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Preview" + "en": { + "stringUnit": { + "state": "translated", + "value": "Preview" } } } }, - "subtitleStyle.previewText" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "So sehen deine Untertitel aus." + "subtitleStyle.previewText": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "So sehen deine Untertitel aus." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "This is how your subtitles look." + "en": { + "stringUnit": { + "state": "translated", + "value": "This is how your subtitles look." } } } }, - "subtitleStyle.size" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Größe" + "subtitleStyle.size": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Größe" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Size" + "en": { + "stringUnit": { + "state": "translated", + "value": "Size" } } } }, - "subtitleStyle.size.huge" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sehr groß" + "subtitleStyle.size.huge": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Sehr groß" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Very large" + "en": { + "stringUnit": { + "state": "translated", + "value": "Very large" } } } }, - "subtitleStyle.size.large" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Groß" + "subtitleStyle.size.large": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Groß" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Large" + "en": { + "stringUnit": { + "state": "translated", + "value": "Large" } } } }, - "subtitleStyle.size.normal" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Normal" + "subtitleStyle.size.normal": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Normal" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Normal" + "en": { + "stringUnit": { + "state": "translated", + "value": "Normal" } } } }, - "subtitleStyle.size.small" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Klein" + "subtitleStyle.size.small": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Klein" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Small" + "en": { + "stringUnit": { + "state": "translated", + "value": "Small" } } } }, - "subtitleStyle.sizeExplanation" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bezogen auf die Vorgabe des Players, nicht auf eine feste Schriftgröße — die Untertitel bleiben damit auf jedem Bildschirm gleich gut lesbar." + "subtitleStyle.sizeExplanation": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Bezogen auf die Vorgabe des Players, nicht auf eine feste Schriftgröße — die Untertitel bleiben damit auf jedem Bildschirm gleich gut lesbar." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Relative to the player’s own default rather than a fixed point size, so subtitles stay readable on any screen." + "en": { + "stringUnit": { + "state": "translated", + "value": "Relative to the player’s own default rather than a fixed point size, so subtitles stay readable on any screen." } } } }, - "subtitleStyle.sizeHeader" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Schriftgröße" + "subtitleStyle.sizeHeader": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Schriftgröße" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Text size" + "en": { + "stringUnit": { + "state": "translated", + "value": "Text size" } } } }, - "subtitleStyle.title" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Untertitel" + "subtitleStyle.title": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Untertitel" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Subtitles" + "en": { + "stringUnit": { + "state": "translated", + "value": "Subtitles" } } } }, - "technical.audio" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tonspur" + "technical.audio": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Tonspur" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Audio" + "en": { + "stringUnit": { + "state": "translated", + "value": "Audio" } } } }, - "technical.bitrate" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bitrate" + "technical.bitrate": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Bitrate" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bitrate" + "en": { + "stringUnit": { + "state": "translated", + "value": "Bitrate" } } } }, - "technical.container" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Container" + "technical.container": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Container" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Container" + "en": { + "stringUnit": { + "state": "translated", + "value": "Container" } } } }, - "technical.directPlayNative" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Direkt (Systemplayer)" + "technical.directPlayNative": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Direkt (Systemplayer)" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Direct (system player)" + "en": { + "stringUnit": { + "state": "translated", + "value": "Direct (system player)" } } } }, - "technical.file" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Datei auf dem Server" + "technical.file": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Datei auf dem Server" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "File on server" + "en": { + "stringUnit": { + "state": "translated", + "value": "File on server" } } } }, - "technical.playbackMethod" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Wiedergabe" + "technical.playbackMethod": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Wiedergabe" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Playback" + "en": { + "stringUnit": { + "state": "translated", + "value": "Playback" } } } }, - "technical.reason" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Grund" + "technical.reason": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Grund" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Reason" + "en": { + "stringUnit": { + "state": "translated", + "value": "Reason" } } } }, - "technical.reason.audioCodec %@" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "iOS kann %@-Ton nicht wiedergeben." + "technical.reason.audioCodec %@": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "iOS kann %@-Ton nicht wiedergeben." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "iOS can't play %@ audio." + "en": { + "stringUnit": { + "state": "translated", + "value": "iOS can't play %@ audio." } } } }, - "technical.reason.chosen" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Im Player wurde eine Qualitätsstufe gewählt." + "technical.reason.chosen": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Im Player wurde eine Qualitätsstufe gewählt." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "A quality step was chosen in the player." + "en": { + "stringUnit": { + "state": "translated", + "value": "A quality step was chosen in the player." } } } }, - "technical.reason.container %@" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "iOS öffnet den Container %@ nicht." + "technical.reason.container %@": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "iOS öffnet den Container %@ nicht." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "iOS can't open the %@ container." + "en": { + "stringUnit": { + "state": "translated", + "value": "iOS can't open the %@ container." } } } }, - "technical.reason.unanalyzed" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Der Server hat diese Datei noch nicht analysiert." + "technical.reason.unanalyzed": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Der Server hat diese Datei noch nicht analysiert." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "The server hasn't analyzed this file yet." + "en": { + "stringUnit": { + "state": "translated", + "value": "The server hasn't analyzed this file yet." } } } }, - "technical.reason.videoCodec %@" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "iOS kann %@ nicht dekodieren." + "technical.reason.videoCodec %@": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "iOS kann %@ nicht dekodieren." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "iOS can't decode %@." + "en": { + "stringUnit": { + "state": "translated", + "value": "iOS can't decode %@." } } } }, - "technical.remux" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Umgepackt" + "technical.remux": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Umgepackt" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Remuxed" + "en": { + "stringUnit": { + "state": "translated", + "value": "Remuxed" } } } }, - "technical.remuxAudio" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Umgepackt, Ton umgerechnet" + "technical.remuxAudio": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Umgepackt, Ton umgerechnet" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Remuxed, audio converted" + "en": { + "stringUnit": { + "state": "translated", + "value": "Remuxed, audio converted" } } } }, - "technical.resolution" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Auflösung" + "technical.resolution": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Auflösung" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Resolution" + "en": { + "stringUnit": { + "state": "translated", + "value": "Resolution" } } } }, - "technical.size" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dateigröße" + "technical.size": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Dateigröße" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "File size" + "en": { + "stringUnit": { + "state": "translated", + "value": "File size" } } } }, - "technical.subtitle" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Untertitel" + "technical.subtitle": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Untertitel" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Subtitle" + "en": { + "stringUnit": { + "state": "translated", + "value": "Subtitle" } } } }, - "technical.title" : { - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Technische Angaben" + "technical.title": { + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Technische Angaben" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Technical details" + "en": { + "stringUnit": { + "state": "translated", + "value": "Technical details" } } } }, - "technical.transcode" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Umgerechnet vom Server" + "technical.transcode": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Umgerechnet vom Server" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Transcoded by server" + "en": { + "stringUnit": { + "state": "translated", + "value": "Transcoded by server" } } } }, - "technical.videoCodec" : { - "extractionState" : "stale", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bildcodec" + "technical.videoCodec": { + "extractionState": "stale", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Bildcodec" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Video codec" + "en": { + "stringUnit": { + "state": "translated", + "value": "Video codec" } } } }, - "tile.play" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Abspielen" + "tile.play": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Abspielen" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Play" + "en": { + "stringUnit": { + "state": "translated", + "value": "Play" } } } }, - "topShelf.noFile" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Noch nichts abgelegt." + "topShelf.noFile": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Noch nichts abgelegt." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Nothing stored yet." + "en": { + "stringUnit": { + "state": "translated", + "value": "Nothing stored yet." } } } }, - "topShelf.noGroup" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Keine App-Gruppe — das Regal kann nichts lesen." + "topShelf.noGroup": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Keine App-Gruppe — das Regal kann nichts lesen." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "No app group — the shelf cannot read anything." + "en": { + "stringUnit": { + "state": "translated", + "value": "No app group — the shelf cannot read anything." } } } }, - "topShelf.title" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Regal über der App" + "topShelf.title": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Regal über der App" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Top Shelf" + "en": { + "stringUnit": { + "state": "translated", + "value": "Top Shelf" } } } }, - "topShelf.unreadable" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Datei liegt da, lässt sich aber nicht lesen." + "topShelf.unreadable": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Datei liegt da, lässt sich aber nicht lesen." } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "File is there but cannot be read." + "en": { + "stringUnit": { + "state": "translated", + "value": "File is there but cannot be read." } } } }, - "topShelf.written %lld %lld %@" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld Reihen, %lld Einträge, zuletzt %@" + "topShelf.written %lld %lld %@": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "%lld Reihen, %lld Einträge, zuletzt %@" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld rows, %lld entries, last %@" + "en": { + "stringUnit": { + "state": "translated", + "value": "%lld rows, %lld entries, last %@" } } } }, - "upcoming.from %@" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ab %@ Uhr" + "upcoming.from %@": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Ab %@ Uhr" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "From %@" + "en": { + "stringUnit": { + "state": "translated", + "value": "From %@" } } } }, - "upcoming.title" : { - "extractionState" : "manual", - "localizations" : { - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bald ist es soweit" + "upcoming.title": { + "extractionState": "manual", + "localizations": { + "de": { + "stringUnit": { + "state": "translated", + "value": "Bald ist es soweit" } }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Coming soon" + "en": { + "stringUnit": { + "state": "translated", + "value": "Coming soon" } } } } }, - "version" : "1.1" + "version": "1.1" } \ No newline at end of file diff --git a/Flaro/Session/PlaybackSettings.swift b/Flaro/Session/PlaybackSettings.swift index af860bb..dabfb44 100644 --- a/Flaro/Session/PlaybackSettings.swift +++ b/Flaro/Session/PlaybackSettings.swift @@ -32,6 +32,7 @@ final class PlaybackSettings { static let cellularQuality = "flaro.playback.quality.cellular" static let autoSkipIntro = "flaro.playback.autoSkipIntro" static let autoSkipCredits = "flaro.playback.autoSkipCredits" + static let skipExtraScenes = "flaro.playback.skipExtraScenes" static let autoPlayNext = "flaro.playback.autoPlayNext" static let locksLandscape = "flaro.playback.locksLandscape" static let rewindSeconds = "flaro.playback.rewindSeconds" @@ -58,6 +59,14 @@ final class PlaybackSettings { var cellularQuality: Quality { didSet { defaults.set(cellularQuality.rawValue, forKey: Key.cellularQuality) } } var autoSkipIntro: Bool { didSet { defaults.set(autoSkipIntro, forKey: Key.autoSkipIntro) } } var autoSkipCredits: Bool { didSet { defaults.set(autoSkipCredits, forKey: Key.autoSkipCredits) } } + + /// Ob „Abspann überspringen" auch über Post-Credit-Szenen hinweggeht. + /// + /// **Eine Gerätesache, keine Servereinstellung.** Ob jemand die Szene + /// sehen will, entscheidet er am Gerät, auf dem er sitzt — wie bei + /// `autoSkipIntro`. Vorgabe aus: wer den Knopf drückt, landet bei der + /// Szene und kann sie wegdrücken, wenn er mag. + var skipExtraScenes: Bool { didSet { defaults.set(skipExtraScenes, forKey: Key.skipExtraScenes) } } var autoPlayNext: Bool { didSet { defaults.set(autoPlayNext, forKey: Key.autoPlayNext) } } /// ISO-Sprachcode, etwa `deu`. Leer heißt: nehmen, was der Server vorgibt. @@ -117,6 +126,7 @@ final class PlaybackSettings { self.autoSkipIntro = defaults.object(forKey: Key.autoSkipIntro) as? Bool ?? false self.autoSkipCredits = defaults.object(forKey: Key.autoSkipCredits) as? Bool ?? false + self.skipExtraScenes = defaults.object(forKey: Key.skipExtraScenes) as? Bool ?? false self.autoPlayNext = defaults.object(forKey: Key.autoPlayNext) as? Bool ?? true self.locksLandscape = defaults.object(forKey: Key.locksLandscape) as? Bool ?? true diff --git a/Flaro/Views/Player/PlayerModel.swift b/Flaro/Views/Player/PlayerModel.swift index 57e4966..fea8749 100644 --- a/Flaro/Views/Player/PlayerModel.swift +++ b/Flaro/Views/Player/PlayerModel.swift @@ -1656,9 +1656,20 @@ final class PlayerModel { /// Springt ans Ende der laufenden Marke. func skipActiveMarker() { - guard let marker = activeMarker else { return } + guard let marker = activeMarker, let item else { return } skippedMarkers.insert(marker.id) - seek(to: .milliseconds(marker.endMilliseconds)) + + // **Mit dem Schalter in einem Zug bis ans Ende.** Sonst endet der + // Sprung an dieser Marke, also bei der Post-Credit-Szene — wer sie + // nicht sehen will, stellt es einmal um und drückt einmal. + let ziel = settings.skipExtraScenes + ? item.markers + .filter { $0.kind == marker.kind && $0.startMilliseconds >= marker.startMilliseconds } + .map(\.endMilliseconds) + .max() ?? marker.endMilliseconds + : marker.endMilliseconds + + seek(to: .milliseconds(ziel)) } /// Ob es bei der Tonspur überhaupt etwas zu wählen gibt. @@ -1838,14 +1849,14 @@ final class PlayerModel { guard let item else { return } let milliseconds = currentTime.milliseconds - activeMarker = item.markers.first { marker in - guard marker.contains(milliseconds) else { return false } - // Der Abspann zählt nur als Marke, wenn danach noch etwas kommt — - // bei einem Film ist „Abspann überspringen" gleichbedeutend mit - // „Film beenden". - if marker.isCredits && item.kind != .episode { return false } - return true - } + // **Was der Server schickt, gilt.** Hier stand eine eigene Regel: + // bei einem Film wurde die Abspannmarke verworfen, weil + // „überspringen" dort gleichbedeutend mit „beenden" war. Das ist + // eine Entscheidung über Marken, und die trifft der Server — er + // schickt sie nur, wenn er einen Abspann gefunden hat, und seit + // ein Abspann geteilt sein darf, führt der Knopf zur + // Post-Credit-Szene statt ins Nichts. + activeMarker = item.markers.first { $0.contains(milliseconds) } guard let marker = activeMarker, !skippedMarkers.contains(marker.id) else { return } let shouldSkip = (marker.isIntro && settings.autoSkipIntro) diff --git a/Flaro/Views/Settings/PlaybackSettingsView.swift b/Flaro/Views/Settings/PlaybackSettingsView.swift index dfb4b47..704ea0a 100644 --- a/Flaro/Views/Settings/PlaybackSettingsView.swift +++ b/Flaro/Views/Settings/PlaybackSettingsView.swift @@ -102,6 +102,7 @@ struct PlaybackSettingsView: View { Section { Toggle("settings.autoSkipIntro", isOn: $settings.autoSkipIntro) Toggle("settings.autoSkipCredits", isOn: $settings.autoSkipCredits) + Toggle("settings.skipExtraScenes", isOn: $settings.skipExtraScenes) Toggle("settings.autoPlayNext", isOn: $settings.autoPlayNext) } header: { Text("settings.episodes") diff --git a/server/src/Flaro.Api/Endpoints/LibraryContentEndpoints.cs b/server/src/Flaro.Api/Endpoints/LibraryContentEndpoints.cs index 6047724..19aed8e 100644 --- a/server/src/Flaro.Api/Endpoints/LibraryContentEndpoints.cs +++ b/server/src/Flaro.Api/Endpoints/LibraryContentEndpoints.cs @@ -1,5 +1,8 @@ +using System.Globalization; +using System.Text; using Flaro.Api.Auth; using Flaro.Core.Library; +using Flaro.Media; using Flaro.Core.Media; using System.Security.Claims; using Flaro.Core.Playback; @@ -152,6 +155,9 @@ public static class LibraryContentEndpoints var roh = await db.Marken.AsNoTracking() .Where(m => m.EpisodeId != null && folgen.Contains(m.EpisodeId!.Value)) + // In Zeitfolge: ein geteilter Abspann kommt als zwei Marken, und + // die Spieler nehmen die erste passende. + .OrderBy(m => m.VonSekunden) .Select(m => new { Id = m.EpisodeId!.Value, m.Art, m.VonSekunden, m.BisSekunden }) .ToListAsync(); @@ -177,6 +183,7 @@ public static class LibraryContentEndpoints var roh = await db.Marken.AsNoTracking() .Where(m => m.MovieId != null && filme.Contains(m.MovieId!.Value)) + .OrderBy(m => m.VonSekunden) .Select(m => new { Id = m.MovieId!.Value, m.Art, m.VonSekunden, m.BisSekunden }) .ToListAsync(); @@ -902,6 +909,42 @@ public static class LibraryContentEndpoints return Results.Accepted(); }).RequireAuthorization(FlaroPolicies.Administrator); + // **Die Messkurve eines Films — zum Nachsehen, nicht zum Erkennen.** + // + // Die Erkennung sagt „kein Abspann" und nennt die Gründe; was sie + // nicht sagen kann, ist, wie die Kurve aussieht. Genau die braucht + // es, bevor eine neue Schwelle gesetzt wird — zum Beispiel für die + // Frage, ob bei Outtakes im Abspann wenigstens die Ränder dunkel + // bleiben. + app.MapGet("/api/v1/movies/{id:guid}/markenkurve", async ( + Guid id, FlaroContext db, Bildabtaster abtaster, CancellationToken ct) => + { + var film = await db.Movies.AsNoTracking() + .Where(m => m.Id == id) + .Join(db.MediaFiles.AsNoTracking(), m => m.Id, d => d.MovieId, + (m, d) => new { m.Title, d.Path, d.Duration }) + .FirstOrDefaultAsync(ct); + + if (film?.Duration is not { TotalSeconds: > 0 } laufzeit) + return Einwand.Bitte("Zu diesem Film gibt es keine Datei mit Laufzeit."); + + var ab = Math.Max(0, laufzeit.TotalSeconds - Markenerkennung.Suchbereich); + var kurve = await abtaster.KurveAsync(film.Path, ab, laufzeit.TotalSeconds - ab, ct); + + var text = new StringBuilder("sekunde;gesamt;links;rechts\n"); + foreach (var punkt in kurve) + { + text.Append(CultureInfo.InvariantCulture, + $"{punkt.Sekunde:0.###};{punkt.Gesamt:0.#};{punkt.Links:0.#};{punkt.Rechts:0.#}\n"); + } + + var name = string.Concat((film.Title ?? "film") + .Select(c => char.IsLetterOrDigit(c) ? c : '-')); + + return Results.File(Encoding.UTF8.GetBytes(text.ToString()), + "text/csv", $"markenkurve-{name}.csv"); + }).RequireAuthorization(FlaroPolicies.Administrator); + // **Eine Marke wieder wegnehmen.** // // Ohne diesen Weg wäre die Erkennung eine Blackbox, die manchmal diff --git a/server/src/Flaro.Api/Endpoints/PlaybackEndpoints.cs b/server/src/Flaro.Api/Endpoints/PlaybackEndpoints.cs index 77e0470..51cf585 100644 --- a/server/src/Flaro.Api/Endpoints/PlaybackEndpoints.cs +++ b/server/src/Flaro.Api/Endpoints/PlaybackEndpoints.cs @@ -100,14 +100,21 @@ public static class PlaybackEndpoints // **Der Abspann, wenn er erkannt wurde.** Er zieht die Schwelle // fürs Häkchen nach vorn — siehe `ProgressRule.Apply`. + // + // **Das früheste Stück, nicht irgendeines.** Teilt eine + // Post-Credit-Szene den Abspann, gibt es zwei Zeilen; ohne + // `OrderBy` entschied die Datenbank, welche gilt. Gemeint ist + // immer die erste — dort ist die Handlung vorbei. var abspannAb = zustand.EpisodeId is { } folge ? await db.Marken.AsNoTracking() .Where(m => m.EpisodeId == folge && m.Art == Markenart.Abspann) + .OrderBy(m => m.VonSekunden) .Select(m => (double?)m.VonSekunden) .FirstOrDefaultAsync() : zustand.MovieId is { } film ? await db.Marken.AsNoTracking() .Where(m => m.MovieId == film && m.Art == Markenart.Abspann) + .OrderBy(m => m.VonSekunden) .Select(m => (double?)m.VonSekunden) .FirstOrDefaultAsync() : null; diff --git a/server/src/Flaro.Api/Markenerkennung.cs b/server/src/Flaro.Api/Markenerkennung.cs index 50ca154..efc6e63 100644 --- a/server/src/Flaro.Api/Markenerkennung.cs +++ b/server/src/Flaro.Api/Markenerkennung.cs @@ -503,7 +503,8 @@ public sealed class Markenerkennung( // allein ist keine Auskunft: ob die Datei zu wenige Schlüsselbilder // hat, ob der Abspann zu hell war oder ob am Schluss eine Tafel // steht, sieht man nur an diesen drei Zahlen — und sonst gar nicht. - if (Abspannkante.Suchen(proben, regeln, out var grund) is not { } stelle) + var stücke = Abspannkante.Suchen(proben, regeln, out var grund); + if (stücke.Count == 0) { // **Mit der Kapitellage.** Ob eine Datei gar keine Kapitel hat // oder nur namenlose, entscheidet, ob sich an diesem Weg @@ -517,16 +518,34 @@ public sealed class Markenerkennung( return false; } - var hell = Abspannkante.HellDavor(proben, stelle, regeln) ?? ab; + // **Nur die erste Kante wird nachgemessen.** Sie ist die, die der + // Zuschauer zuerst sieht, und die, an der „gilt als gesehen" hängt. + // Die übrigen Grenzen liegen an Schlüsselbildern und sind damit auf + // fünf bis zehn Sekunden genau — dafür lohnt keine Sonde. + var hell = Abspannkante.HellDavor(proben, stücke[0].Von, regeln) ?? ab; var kante = await Abspannkante.VerfeinernAsync( - hell, stelle, regeln.Mindestanteil, + hell, stücke[0].Von, regeln.Mindestanteil, wo => abtaster.DunkelanteilAsync(pfad, wo, ct)); - log.LogInformation("„{Titel}“: Abspann im Bild ab {Stelle:h\\:mm\\:ss}.", - name, TimeSpan.FromSeconds(kante)); + var fein = stücke.ToList(); + fein[0] = fein[0] with { Von = kante }; - return await SchreibenAsync(db, titelId, istFilm, Markenart.Abspann, - kante, laufzeit, ct); + if (fein.Count == 1) + { + log.LogInformation("„{Titel}“: Abspann im Bild ab {Stelle:h\\:mm\\:ss}.", + name, TimeSpan.FromSeconds(kante)); + } + else + { + log.LogInformation( + "„{Titel}“: Abspann im Bild in {Stücke} Stücken ab {Stelle:h\\:mm\\:ss} " + + "— dazwischen {Lücken}.", + name, fein.Count, TimeSpan.FromSeconds(kante), + string.Join(", ", fein.Zip(fein.Skip(1), + (a, b) => $"{Zeit(a.Bis)}–{Zeit(b.Von)}"))); + } + + return await SchreibenAsync(db, titelId, istFilm, Markenart.Abspann, fein, ct); } /// Was ein Serienlauf ergeben hat — für die Zusammenfassung. @@ -917,31 +936,53 @@ public sealed class Markenerkennung( }); } + private Task SchreibenAsync( + FlaroContext db, Guid titelId, bool istFilm, Markenart art, + double von, double bis, CancellationToken ct) => + SchreibenAsync(db, titelId, istFilm, art, [new Abspannstück(von, bis)], ct); + + /// Setzt die Marken einer Art auf genau diese Stücke. + /// + /// + /// + /// Ersetzen, nicht umschreiben. Hier stand ein Abgleich auf + /// genau eine Zeile je Titel und Art — das ging, solange ein Abspann + /// eine zusammenhängende Strecke war. Seit eine Post-Credit-Szene ihn + /// teilen darf, sind es ein bis zwei Zeilen, und beim nächsten Lauf + /// vielleicht wieder eine. Die alten zu löschen und die neuen + /// anzulegen ist der einzige Weg, der in beide Richtungen stimmt. + /// + /// + /// In derselben Transaktion — gespeichert wird weiter oben, und + /// bis dahin stehen Löschung und Anlage zusammen. Dazwischen gibt es + /// keinen Augenblick, in dem der Titel gar keine Marke hätte. + /// + /// private async Task SchreibenAsync( FlaroContext db, Guid titelId, bool istFilm, Markenart art, - double von, double bis, CancellationToken ct) + IReadOnlyList stücke, CancellationToken ct) { - var alt = istFilm - ? await db.Marken.FirstOrDefaultAsync(m => m.MovieId == titelId && m.Art == art, ct) - : await db.Marken.FirstOrDefaultAsync(m => m.EpisodeId == titelId && m.Art == art, ct); + if (stücke.Count == 0) return false; - if (alt is not null) + var alte = istFilm + ? await db.Marken.Where(m => m.MovieId == titelId && m.Art == art).ToListAsync(ct) + : await db.Marken.Where(m => m.EpisodeId == titelId && m.Art == art).ToListAsync(ct); + + db.Marken.RemoveRange(alte); + + foreach (var stück in stücke) { - alt.VonSekunden = von; - alt.BisSekunden = bis; - alt.ErkanntAm = clock.GetUtcNow(); - return true; + db.Marken.Add(new Marke + { + EpisodeId = istFilm ? null : titelId, + MovieId = istFilm ? titelId : null, + Art = art, + VonSekunden = stück.Von, + BisSekunden = stück.Bis, + ErkanntAm = clock.GetUtcNow(), + }); } - db.Marken.Add(new Marke - { - EpisodeId = istFilm ? null : titelId, - MovieId = istFilm ? titelId : null, - Art = art, - VonSekunden = von, - BisSekunden = bis, - ErkanntAm = clock.GetUtcNow(), - }); return true; } } diff --git a/server/src/Flaro.Core/Media/Abspannkante.cs b/server/src/Flaro.Core/Media/Abspannkante.cs index e43ab0b..d6d0de3 100644 --- a/server/src/Flaro.Core/Media/Abspannkante.cs +++ b/server/src/Flaro.Core/Media/Abspannkante.cs @@ -22,6 +22,19 @@ namespace Flaro.Core.Media; /// Wie viel Prozent des Bildes dunkel sind, 0 bis 100. public readonly record struct Bildprobe(double Sekunde, double Dunkelanteil); +/// Ein Stück Abspann: von wann bis wann. +/// +/// +/// Mehrere, weil ein Abspann unterbrochen sein kann. Liegt eine +/// Post-Credit-Szene dazwischen, sind es zwei Stücke mit der Szene als +/// Lücke — und der Überspringen-Knopf bringt den Zuschauer genau dorthin +/// statt an ihr vorbei. +/// +public readonly record struct Abspannstück(double Von, double Bis) +{ + public double Länge => Math.Max(0, Bis - Von); +} + /// Die Schwellen, nach denen eine dunkle Strecke ein Abspann ist. /// /// @@ -87,6 +100,28 @@ public readonly record struct Abspannregeln /// public double Schlusstafel { get; init; } + /// Wie kurz ein einzelnes Stück sein darf, um noch zu zählen. + /// + /// + /// Ein Abspann kann aus zwei Stücken bestehen, wenn eine Post-Credit- + /// Szene dazwischenliegt. Zehn Sekunden Schwarz sind aber kein Stück + /// Abspann, sondern eine Blende — die Mindestlänge gilt für die Summe, + /// diese Schranke für das einzelne Stück. + /// + public double Mindeststück { get; init; } + + /// Wie lang eine Post-Credit-Szene höchstens sein darf. + /// + /// + /// Die Lücke, über die hinweg zusammengefasst wird. Eine + /// Post-Credit-Szene im MCU dauert typischerweise eine Minute; die + /// längste gemessene (Thunderbolts*) 2:54. Vier Minuten sind der + /// längste bekannte Wert plus Reserve — und zugleich die Schranke + /// dagegen, dass die Suche rückwärts durch den halben Film läuft. + /// + public double Lückentoleranz { get; init; } + + /// Die Summe aller Stücke, nicht die Länge eines einzelnen. public double Mindestlänge { get; init; } public double Höchstlänge { get; init; } @@ -95,6 +130,8 @@ public readonly record struct Abspannregeln Mindestanteil = 60, Nachsicht = 10, Schlusstafel = 20, + Mindeststück = 15, + Lückentoleranz = 4 * 60, Mindestlänge = 45, Höchstlänge = 12 * 60, }; @@ -124,13 +161,14 @@ public readonly record struct Abspannregeln /// public static class Abspannkante { - /// Die dunkle Strecke, die bis zum Ende durchhält — oder nichts. + /// Die Stücke Abspann am Ende — oder eine leere Liste. /// /// /// Messpunkte über das Ende des Films, aufsteigend nach Zeit. Sie dürfen /// ungleichmässig liegen: Schlüsselbilder tun das. /// - public static double? Suchen(IReadOnlyList proben, Abspannregeln regeln) => + public static IReadOnlyList Suchen( + IReadOnlyList proben, Abspannregeln regeln) => Suchen(proben, regeln, out _); /// @@ -139,68 +177,106 @@ public static class Abspannkante /// sind drei verschiedene Ursachen mit drei verschiedenen Antworten, /// und ohne diese Zeile beginnt jede Nachfrage wieder beim Raten. /// - public static double? Suchen( + public static IReadOnlyList Suchen( IReadOnlyList proben, Abspannregeln regeln, out string grund) { grund = "gefunden"; if (proben.Count < 2) { grund = "zu wenige Proben"; - return null; + return []; } - // **Von hinten.** Der Abspann endet am Dateiende; alles, was davor - // dunkel war und wieder hell wurde, war eine Schwarzblende. - // - // **Das allerletzte Bild muss aber nicht dunkel sein.** Hinter dem - // Abspann steht bei vielen Produktionen noch eine Logotafel — - // Sender, Studio, Förderer —, oft hell und ein paar Sekunden lang. - // Darauf zu bestehen, dass die letzte Probe dunkel ist, hiess: ein - // einziges weisses Schlussbild macht den ganzen Abspann unsichtbar. - var letzte = proben.Count - 1; - var ende = proben[letzte].Sekunde; - while (letzte > 0 && proben[letzte].Dunkelanteil < regeln.Mindestanteil) + var läufe = Läufe(proben, regeln); + if (läufe.Count == 0) { - if (ende - proben[letzte].Sekunde >= regeln.Schlusstafel) - { - grund = "das Ende ist zu lange hell"; - return null; - } - letzte--; + grund = "keine dunkle Strecke im Suchfenster"; + return []; } - if (proben[letzte].Dunkelanteil < regeln.Mindestanteil) + + // **Das letzte Stück muss das Ende erreichen.** Ein Abspann endet am + // Dateiende; was davor dunkel war und wieder hell wurde, war eine + // Schwarzblende. Eine helle Logotafel dahinter ist erlaubt — Sender, + // Studio, Förderer stehen bei vielen Produktionen noch hinter dem + // Abspann. + var ende = proben[^1].Sekunde; + if (ende - läufe[^1].Bis >= regeln.Schlusstafel) { grund = "das Ende ist hell"; - return null; + return []; } - // **Rückwärts, und helle Stellen in Sekunden verzeihen.** Gezählt - // wurden hier früher Proben, und was das bedeutet, hängt allein an - // der Schlüsselbilddichte der Datei — siehe `Nachsicht`. - var anfang = letzte; - for (var i = letzte - 1; i >= 0; i--) + // **Rückwärts zusammenfassen.** Jede Lücke unter der Toleranz ist + // eine Post-Credit-Szene und kein Abbruch; die erste grössere Lücke + // beendet die Suche. + var stücke = new List { läufe[^1] }; + for (var i = läufe.Count - 2; i >= 0; i--) { - if (proben[i].Dunkelanteil >= regeln.Mindestanteil) { anfang = i; continue; } - if (proben[anfang].Sekunde - proben[i].Sekunde > regeln.Nachsicht) break; + if (stücke[^1].Von - läufe[i].Bis >= regeln.Lückentoleranz) break; + stücke.Add(läufe[i]); + } + stücke.Reverse(); + + // **Die Summe entscheidet, nicht das einzelne Stück.** Ein Abspann + // von vier Minuten, den eine Szene in der Mitte teilt, ist ein + // Abspann von vier Minuten — vorher zerfiel er in zwei Reste, von + // denen keiner lang genug war, und der Film bekam gar nichts. + var summe = stücke.Sum(t => t.Länge); + if (summe < regeln.Mindestlänge) + { + grund = $"die dunklen Strecken ergeben zusammen nur {summe:0} s"; + return []; + } + if (summe > regeln.Höchstlänge) + { + grund = $"die dunklen Strecken ergeben zusammen {summe:0} s und damit zu viel"; + return []; } - var länge = proben[letzte].Sekunde - proben[anfang].Sekunde; - if (länge < regeln.Mindestlänge) + // Das letzte Stück reicht bis ans Dateiende: eine Logotafel dahinter + // gehört zum Abspann, nicht zum Film. + stücke[^1] = stücke[^1] with { Bis = ende }; + return stücke; + } + + /// Zusammenhängende dunkle Strecken, kurze Helligkeiten verziehen. + /// + /// + /// Die Nachsicht zählt Sekunden, nicht Proben. Eine Schrifttafel + /// blitzt auf, ein Logo steht zwei Sekunden im Bild; wie viele + /// Messpunkte darauf fallen, hängt allein an der Schlüsselbilddichte + /// der Datei und schwankt um eine Zehnerpotenz. + /// + private static List Läufe( + IReadOnlyList proben, Abspannregeln regeln) + { + var läufe = new List(); + double? von = null; + double bis = 0; + + foreach (var probe in proben) { - grund = $"die dunkle Strecke ist mit {länge:0} s zu kurz"; - return null; - } - if (länge > regeln.Höchstlänge) - { - grund = $"die dunkle Strecke ist mit {länge:0} s zu lang"; - return null; + if (probe.Dunkelanteil >= regeln.Mindestanteil) + { + von ??= probe.Sekunde; + bis = probe.Sekunde; + continue; + } + + // Hell: der Lauf endet erst, wenn die Helligkeit lange genug + // anhält. + if (von is not null && probe.Sekunde - bis > regeln.Nachsicht) + { + läufe.Add(new Abspannstück(von.Value, bis)); + von = null; + } } - // **Die erste dunkle Probe ist nicht der Anfang.** Zwischen ihr und - // der letzten hellen davor liegt die Kante irgendwo; wo genau, sagt - // erst das Nachmessen. Hier steht die späteste Stelle, an der sie - // liegen kann — und die ist als Marke schon brauchbar. - return proben[anfang].Sekunde; + if (von is not null) läufe.Add(new Abspannstück(von.Value, bis)); + + // Zehn Sekunden Schwarz sind eine Blende, kein Stück Abspann. + läufe.RemoveAll(l => l.Länge < regeln.Mindeststück); + return läufe; } /// Die letzte helle Probe vor dem Fund — der Suchbereich fürs Nachmessen. @@ -254,3 +330,19 @@ public static class Abspannkante return dunkel; } } + +/// Ein Messpunkt des Werkzeugs: das ganze Bild und die Ränder. +/// +/// +/// Nur zum Nachsehen, nicht für die Erkennung. Ob die Ränder bei +/// Outtakes im Abspann dunkel bleiben, ist eine Vermutung — und eine, die +/// sich nur an echten Dateien beantworten lässt. Diese Reihe ist die +/// Grundlage dafür. +/// +/// +/// Die Stelle im Film. +/// Dunkelanteil des ganzen Bildes, 0 bis 100. +/// Dunkelanteil der linken fünftel Breite. +/// Dunkelanteil der rechten fünftel Breite. +public readonly record struct Kurvenpunkt( + double Sekunde, double Gesamt, double Links, double Rechts); diff --git a/server/src/Flaro.Data/FlaroContext.cs b/server/src/Flaro.Data/FlaroContext.cs index 4422b6a..bc11140 100644 --- a/server/src/Flaro.Data/FlaroContext.cs +++ b/server/src/Flaro.Data/FlaroContext.cs @@ -311,10 +311,14 @@ public sealed class FlaroContext(DbContextOptions options) : DbCon b.Entity(e => { e.HasKey(x => x.Id); - // Eine Art einmal je Titel: ein zweiter Lauf schreibt um, statt - // eine zweite Zeile anzulegen. - e.HasIndex(x => new { x.EpisodeId, x.Art }).IsUnique().HasFilter("\"EpisodeId\" IS NOT NULL"); - e.HasIndex(x => new { x.MovieId, x.Art }).IsUnique().HasFilter("\"MovieId\" IS NOT NULL"); + // **Mehrere Stücke je Art, und das ist der ganze Zweck.** Hier + // stand ein eindeutiger Index mit dem Vermerk „eine Art einmal + // je Titel" — richtig, solange ein Abspann eine + // zusammenhängende Strecke war. Teilt ihn eine Post-Credit- + // Szene, sind es zwei Zeilen, und der Index wies die zweite ab. + // Zum Nachschlagen bleiben die Indizes, nur ohne `IsUnique`. + e.HasIndex(x => new { x.EpisodeId, x.Art }); + e.HasIndex(x => new { x.MovieId, x.Art }); e.HasOne().WithMany().HasForeignKey(x => x.EpisodeId).OnDelete(DeleteBehavior.Cascade); e.HasOne().WithMany().HasForeignKey(x => x.MovieId).OnDelete(DeleteBehavior.Cascade); // Als Zahl, nicht als Text: die Namen stehen im Quelltext und diff --git a/server/src/Flaro.Data/Migrations/20261015142742_Abspannstuecke.Designer.cs b/server/src/Flaro.Data/Migrations/20261015142742_Abspannstuecke.Designer.cs new file mode 100644 index 0000000..8d8494c --- /dev/null +++ b/server/src/Flaro.Data/Migrations/20261015142742_Abspannstuecke.Designer.cs @@ -0,0 +1,1357 @@ +// +using System; +using Flaro.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Flaro.Data.Migrations +{ + [DbContext(typeof(FlaroContext))] + [Migration("20261015142742_Abspannstuecke")] + partial class Abspannstuecke + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "10.0.12"); + + modelBuilder.Entity("Flaro.Core.Auth.AuthCode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ApprovedByUserId") + .HasColumnType("TEXT"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("TEXT"); + + b.Property("ConsumedAt") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("INTEGER"); + + b.Property("DeviceId") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("DeviceName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("ExpiresAt") + .HasColumnType("INTEGER"); + + b.Property("HandleFingerprint") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Code"); + + b.HasIndex("HandleFingerprint") + .IsUnique(); + + b.ToTable("AuthCodes"); + }); + + modelBuilder.Entity("Flaro.Core.Auth.DeviceToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("INTEGER"); + + b.Property("DeviceId") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("DeviceName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("LastSeenAt") + .HasColumnType("INTEGER"); + + b.Property("RevokedAt") + .HasColumnType("INTEGER"); + + b.Property("TokenFingerprint") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TokenFingerprint") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("DeviceTokens"); + }); + + modelBuilder.Entity("Flaro.Core.Auth.Einladung", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("INTEGER"); + + b.Property("CreatedByUserId") + .HasColumnType("TEXT"); + + b.Property("ExpiresAt") + .HasColumnType("INTEGER"); + + b.Property("MaxUses") + .HasColumnType("INTEGER"); + + b.Property("RevokedAt") + .HasColumnType("INTEGER"); + + b.Property("TokenFingerprint") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.Property("UsedCount") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("CreatedByUserId"); + + b.HasIndex("TokenFingerprint") + .IsUnique(); + + b.ToTable("Einladungen"); + }); + + modelBuilder.Entity("Flaro.Core.Libraries.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("INTEGER"); + + b.Property("Kind") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("Path") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("TEXT"); + + b.Property("ReviewNewItems") + .HasColumnType("INTEGER"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Libraries"); + }); + + modelBuilder.Entity("Flaro.Core.Library.Credit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Character") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("MovieId") + .HasColumnType("TEXT"); + + b.Property("Order") + .HasColumnType("INTEGER"); + + b.Property("PersonId") + .HasColumnType("TEXT"); + + b.Property("Role") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("TEXT"); + + b.Property("SeriesId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.HasIndex("PersonId"); + + b.HasIndex("SeriesId"); + + b.ToTable("Credits"); + }); + + modelBuilder.Entity("Flaro.Core.Library.Episode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AddedAt") + .HasColumnType("INTEGER"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("MetadataLanguage") + .HasMaxLength(16) + .HasColumnType("TEXT"); + + b.Property("Overview") + .HasColumnType("TEXT"); + + b.Property("PublishAt") + .HasColumnType("INTEGER"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("SeriesId") + .HasColumnType("TEXT"); + + b.Property("StillImage") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("SeriesId", "SeasonNumber", "EpisodeNumber") + .IsUnique(); + + b.ToTable("Episodes"); + }); + + modelBuilder.Entity("Flaro.Core.Library.Fremdbewertung", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Anzeige") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("MovieId") + .HasColumnType("TEXT"); + + b.Property("Quelle") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("SeriesId") + .HasColumnType("TEXT"); + + b.Property("Wert") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.HasIndex("MovieId", "Quelle") + .IsUnique() + .HasFilter("\"MovieId\" IS NOT NULL"); + + b.HasIndex("SeriesId", "Quelle") + .IsUnique() + .HasFilter("\"SeriesId\" IS NOT NULL"); + + b.ToTable("Fremdbewertungen"); + }); + + modelBuilder.Entity("Flaro.Core.Library.Genre", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT") + .UseCollation("NOCASE"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Genres"); + }); + + modelBuilder.Entity("Flaro.Core.Library.GenreLink", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("GenreId") + .HasColumnType("TEXT"); + + b.Property("MovieId") + .HasColumnType("TEXT"); + + b.Property("SeriesId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("GenreId", "MovieId") + .IsUnique() + .HasFilter("\"MovieId\" IS NOT NULL"); + + b.HasIndex("GenreId", "SeriesId") + .IsUnique() + .HasFilter("\"SeriesId\" IS NOT NULL"); + + b.ToTable("GenreLinks"); + }); + + modelBuilder.Entity("Flaro.Core.Library.ItemTranslation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("TEXT"); + + b.Property("FetchedAt") + .HasColumnType("INTEGER"); + + b.Property("Language") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("TEXT"); + + b.Property("MovieId") + .HasColumnType("TEXT"); + + b.Property("Overview") + .HasColumnType("TEXT"); + + b.Property("SeriesId") + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("Title") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId", "Language") + .IsUnique() + .HasFilter("\"EpisodeId\" IS NOT NULL"); + + b.HasIndex("MovieId", "Language") + .IsUnique() + .HasFilter("\"MovieId\" IS NOT NULL"); + + b.HasIndex("SeriesId", "Language") + .IsUnique() + .HasFilter("\"SeriesId\" IS NOT NULL"); + + b.ToTable("Translations"); + }); + + modelBuilder.Entity("Flaro.Core.Library.Marke", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Art") + .HasColumnType("INTEGER"); + + b.Property("BisSekunden") + .HasColumnType("REAL"); + + b.Property("EpisodeId") + .HasColumnType("TEXT"); + + b.Property("ErkanntAm") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("TEXT"); + + b.Property("VonSekunden") + .HasColumnType("REAL"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId", "Art"); + + b.HasIndex("MovieId", "Art"); + + b.ToTable("Marken"); + }); + + modelBuilder.Entity("Flaro.Core.Library.Markensuche", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Art") + .HasColumnType("INTEGER"); + + b.Property("Folgen") + .HasColumnType("INTEGER"); + + b.Property("GelaufenAm") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("TEXT"); + + b.Property("SeriesId") + .HasColumnType("TEXT"); + + b.Property("Staffel") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MovieId", "Art") + .IsUnique() + .HasFilter("\"MovieId\" IS NOT NULL"); + + b.HasIndex("SeriesId", "Staffel", "Art") + .IsUnique() + .HasFilter("\"SeriesId\" IS NOT NULL"); + + b.ToTable("Markensuchen"); + }); + + modelBuilder.Entity("Flaro.Core.Library.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("BitRate") + .HasColumnType("INTEGER"); + + b.Property("Container") + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("INTEGER"); + + b.Property("EpisodeId") + .HasColumnType("TEXT"); + + b.Property("LibraryId") + .HasColumnType("TEXT"); + + b.Property("MissingSince") + .HasColumnType("INTEGER"); + + b.Property("ModifiedAt") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("TEXT"); + + b.Property("Path") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("TEXT"); + + b.Property("ProbedAt") + .HasColumnType("INTEGER"); + + b.Property("SeenAt") + .HasColumnType("INTEGER"); + + b.Property("SizeBytes") + .HasColumnType("INTEGER"); + + b.Property("VersionLabel") + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.HasIndex("LibraryId", "Path") + .IsUnique(); + + b.ToTable("MediaFiles"); + }); + + modelBuilder.Entity("Flaro.Core.Library.MediaStream", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Channels") + .HasColumnType("INTEGER"); + + b.Property("Codec") + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.Property("Height") + .HasColumnType("INTEGER"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("IsDefault") + .HasColumnType("INTEGER"); + + b.Property("IsForced") + .HasColumnType("INTEGER"); + + b.Property("Kind") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("TEXT"); + + b.Property("Language") + .HasMaxLength(16) + .HasColumnType("TEXT"); + + b.Property("MediaFileId") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("Width") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MediaFileId", "Index") + .IsUnique(); + + b.ToTable("MediaStreams"); + }); + + modelBuilder.Entity("Flaro.Core.Library.Meldung", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Art") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("TEXT"); + + b.Property("ErledigtAm") + .HasColumnType("INTEGER"); + + b.Property("ErledigtVon") + .HasColumnType("TEXT"); + + b.Property("GemeldetAm") + .HasColumnType("INTEGER"); + + b.Property("Kommentar") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("TEXT"); + + b.Property("MovieId") + .HasColumnType("TEXT"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("SeriesId") + .HasColumnType("TEXT"); + + b.Property("Stelle") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("ErledigtAm", "GemeldetAm"); + + b.HasIndex("UserId", "GemeldetAm"); + + b.ToTable("Meldungen"); + }); + + modelBuilder.Entity("Flaro.Core.Library.Movie", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AddedAt") + .HasColumnType("INTEGER"); + + b.Property("Aufmacherbilder") + .HasColumnType("TEXT"); + + b.Property("BackdropImage") + .HasColumnType("TEXT"); + + b.Property("BackdropPinned") + .HasColumnType("INTEGER"); + + b.Property("ChangedSince") + .HasColumnType("INTEGER"); + + b.Property("ContentRating") + .HasMaxLength(16) + .HasColumnType("TEXT"); + + b.Property("FassungenGeprueftBei") + .HasColumnType("INTEGER"); + + b.Property("ImdbId") + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("LibraryId") + .HasColumnType("TEXT"); + + b.Property("LogoImage") + .HasColumnType("TEXT"); + + b.Property("LogoPinned") + .HasColumnType("INTEGER"); + + b.Property("MatchLocked") + .HasColumnType("INTEGER"); + + b.Property("MetadataLanguage") + .HasMaxLength(16) + .HasColumnType("TEXT"); + + b.Property("OriginalTitle") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("Overview") + .HasColumnType("TEXT"); + + b.Property("ParsedTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ParsedYear") + .HasColumnType("INTEGER"); + + b.Property("PosterImage") + .HasColumnType("TEXT"); + + b.Property("PosterPinned") + .HasColumnType("INTEGER"); + + b.Property("PublishAt") + .HasColumnType("INTEGER"); + + b.Property("Rating") + .HasColumnType("REAL"); + + b.Property("RuntimeMinutes") + .HasColumnType("INTEGER"); + + b.Property("SortTitlePinned") + .HasColumnType("INTEGER"); + + b.Property("SortTitleValue") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("TitlePinned") + .HasColumnType("INTEGER"); + + b.Property("TmdbId") + .HasColumnType("INTEGER"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId", "MetadataLanguage"); + + b.HasIndex("LibraryId", "SortTitleValue"); + + b.HasIndex("LibraryId", "TmdbId") + .IsUnique() + .HasFilter("\"TmdbId\" IS NOT NULL"); + + b.ToTable("Movies"); + }); + + modelBuilder.Entity("Flaro.Core.Library.Person", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Image") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("TmdbId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("TmdbId") + .IsUnique(); + + b.ToTable("People"); + }); + + modelBuilder.Entity("Flaro.Core.Library.Series", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AddedAt") + .HasColumnType("INTEGER"); + + b.Property("Aufmacherbilder") + .HasColumnType("TEXT"); + + b.Property("BackdropImage") + .HasColumnType("TEXT"); + + b.Property("BackdropPinned") + .HasColumnType("INTEGER"); + + b.Property("ChangedSince") + .HasColumnType("INTEGER"); + + b.Property("ContentRating") + .HasMaxLength(16) + .HasColumnType("TEXT"); + + b.Property("ImdbId") + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("LibraryId") + .HasColumnType("TEXT"); + + b.Property("LogoImage") + .HasColumnType("TEXT"); + + b.Property("LogoPinned") + .HasColumnType("INTEGER"); + + b.Property("MatchLocked") + .HasColumnType("INTEGER"); + + b.Property("MetadataLanguage") + .HasMaxLength(16) + .HasColumnType("TEXT"); + + b.Property("OriginalTitle") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("Overview") + .HasColumnType("TEXT"); + + b.Property("ParsedTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ParsedYear") + .HasColumnType("INTEGER"); + + b.Property("PosterImage") + .HasColumnType("TEXT"); + + b.Property("PosterPinned") + .HasColumnType("INTEGER"); + + b.Property("PublishAt") + .HasColumnType("INTEGER"); + + b.Property("Rating") + .HasColumnType("REAL"); + + b.Property("RuntimeMinutes") + .HasColumnType("INTEGER"); + + b.Property("SortTitlePinned") + .HasColumnType("INTEGER"); + + b.Property("SortTitleValue") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("Tagline") + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("TEXT"); + + b.Property("TitlePinned") + .HasColumnType("INTEGER"); + + b.Property("TmdbId") + .HasColumnType("INTEGER"); + + b.Property("Year") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("LibraryId", "MetadataLanguage"); + + b.HasIndex("LibraryId", "SortTitleValue"); + + b.HasIndex("LibraryId", "TmdbId") + .IsUnique() + .HasFilter("\"TmdbId\" IS NOT NULL"); + + b.ToTable("Series"); + }); + + modelBuilder.Entity("Flaro.Core.Library.Stichwort", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Stichworte"); + }); + + modelBuilder.Entity("Flaro.Core.Library.Stichwortlink", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("MovieId") + .HasColumnType("TEXT"); + + b.Property("SeriesId") + .HasColumnType("TEXT"); + + b.Property("StichwortId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("StichwortId", "MovieId") + .IsUnique() + .HasFilter("\"MovieId\" IS NOT NULL"); + + b.HasIndex("StichwortId", "SeriesId") + .IsUnique() + .HasFilter("\"SeriesId\" IS NOT NULL"); + + b.ToTable("Stichwortlinks"); + }); + + modelBuilder.Entity("Flaro.Core.Playback.Sehtag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("EpisodeId") + .HasColumnType("TEXT"); + + b.Property("MovieId") + .HasColumnType("TEXT"); + + b.Property("Sekunden") + .HasColumnType("REAL"); + + b.Property("Tag") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.HasIndex("UserId", "Tag"); + + b.HasIndex("UserId", "Tag", "EpisodeId") + .IsUnique() + .HasFilter("\"EpisodeId\" IS NOT NULL"); + + b.HasIndex("UserId", "Tag", "MovieId") + .IsUnique() + .HasFilter("\"MovieId\" IS NOT NULL"); + + b.ToTable("Sehtage"); + }); + + modelBuilder.Entity("Flaro.Core.Playback.UserItemState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Dismissed") + .HasColumnType("INTEGER"); + + b.Property("EpisodeId") + .HasColumnType("TEXT"); + + b.Property("Favourite") + .HasColumnType("INTEGER"); + + b.Property("InProgress") + .HasColumnType("INTEGER"); + + b.Property("LastPlayedAt") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("TEXT"); + + b.Property("PlayCount") + .HasColumnType("INTEGER"); + + b.Property("Position") + .HasColumnType("INTEGER"); + + b.Property("Rating") + .HasColumnType("INTEGER"); + + b.Property("Runtime") + .HasColumnType("INTEGER"); + + b.Property("SeriesId") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("Watched") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("EpisodeId"); + + b.HasIndex("MovieId"); + + b.HasIndex("SeriesId"); + + b.HasIndex("UserId", "EpisodeId") + .IsUnique() + .HasFilter("\"EpisodeId\" IS NOT NULL"); + + b.HasIndex("UserId", "MovieId") + .IsUnique() + .HasFilter("\"MovieId\" IS NOT NULL"); + + b.HasIndex("UserId", "SeriesId") + .IsUnique() + .HasFilter("\"SeriesId\" IS NOT NULL"); + + b.HasIndex("UserId", "InProgress", "UpdatedAt"); + + b.ToTable("UserItemStates"); + }); + + modelBuilder.Entity("Flaro.Core.ServerSetting", b => + { + b.Property("Key") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("TEXT"); + + b.HasKey("Key"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("Flaro.Core.Users.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AvatarImage") + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("INTEGER"); + + b.Property("IsAdministrator") + .HasColumnType("INTEGER"); + + b.Property("IsOwner") + .HasColumnType("INTEGER"); + + b.Property("Language") + .HasMaxLength(16) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("TEXT") + .UseCollation("NOCASE"); + + b.HasKey("Id"); + + b.HasIndex("UserName") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Flaro.Core.Auth.DeviceToken", b => + { + b.HasOne("Flaro.Core.Users.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Flaro.Core.Auth.Einladung", b => + { + b.HasOne("Flaro.Core.Users.User", null) + .WithMany() + .HasForeignKey("CreatedByUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Flaro.Core.Library.Credit", b => + { + b.HasOne("Flaro.Core.Library.Movie", null) + .WithMany() + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Flaro.Core.Library.Person", null) + .WithMany() + .HasForeignKey("PersonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Flaro.Core.Library.Series", null) + .WithMany() + .HasForeignKey("SeriesId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Flaro.Core.Library.Episode", b => + { + b.HasOne("Flaro.Core.Library.Series", null) + .WithMany() + .HasForeignKey("SeriesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Flaro.Core.Library.Fremdbewertung", b => + { + b.HasOne("Flaro.Core.Library.Movie", null) + .WithMany() + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Flaro.Core.Library.Series", null) + .WithMany() + .HasForeignKey("SeriesId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Flaro.Core.Library.GenreLink", b => + { + b.HasOne("Flaro.Core.Library.Genre", null) + .WithMany() + .HasForeignKey("GenreId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Flaro.Core.Library.Movie", null) + .WithMany() + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Flaro.Core.Library.Series", null) + .WithMany() + .HasForeignKey("SeriesId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Flaro.Core.Library.Marke", b => + { + b.HasOne("Flaro.Core.Library.Episode", null) + .WithMany() + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Flaro.Core.Library.Movie", null) + .WithMany() + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Flaro.Core.Library.Markensuche", b => + { + b.HasOne("Flaro.Core.Library.Movie", null) + .WithMany() + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Flaro.Core.Library.Series", null) + .WithMany() + .HasForeignKey("SeriesId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Flaro.Core.Library.MediaFile", b => + { + b.HasOne("Flaro.Core.Libraries.Library", null) + .WithMany() + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Flaro.Core.Library.MediaStream", b => + { + b.HasOne("Flaro.Core.Library.MediaFile", null) + .WithMany() + .HasForeignKey("MediaFileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Flaro.Core.Library.Meldung", b => + { + b.HasOne("Flaro.Core.Library.Episode", null) + .WithMany() + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Flaro.Core.Library.Movie", null) + .WithMany() + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Flaro.Core.Library.Series", null) + .WithMany() + .HasForeignKey("SeriesId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Flaro.Core.Users.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Flaro.Core.Library.Movie", b => + { + b.HasOne("Flaro.Core.Libraries.Library", null) + .WithMany() + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Flaro.Core.Library.Series", b => + { + b.HasOne("Flaro.Core.Libraries.Library", null) + .WithMany() + .HasForeignKey("LibraryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Flaro.Core.Library.Stichwortlink", b => + { + b.HasOne("Flaro.Core.Library.Movie", null) + .WithMany() + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Flaro.Core.Library.Series", null) + .WithMany() + .HasForeignKey("SeriesId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Flaro.Core.Library.Stichwort", null) + .WithMany() + .HasForeignKey("StichwortId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Flaro.Core.Playback.Sehtag", b => + { + b.HasOne("Flaro.Core.Library.Episode", null) + .WithMany() + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Flaro.Core.Library.Movie", null) + .WithMany() + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Flaro.Core.Users.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Flaro.Core.Playback.UserItemState", b => + { + b.HasOne("Flaro.Core.Library.Episode", null) + .WithMany() + .HasForeignKey("EpisodeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Flaro.Core.Library.Movie", null) + .WithMany() + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Flaro.Core.Library.Series", null) + .WithMany() + .HasForeignKey("SeriesId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Flaro.Core.Users.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/server/src/Flaro.Data/Migrations/20261015142742_Abspannstuecke.cs b/server/src/Flaro.Data/Migrations/20261015142742_Abspannstuecke.cs new file mode 100644 index 0000000..eb55123 --- /dev/null +++ b/server/src/Flaro.Data/Migrations/20261015142742_Abspannstuecke.cs @@ -0,0 +1,58 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Flaro.Data.Migrations +{ + /// + public partial class Abspannstuecke : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_Marken_EpisodeId_Art", + table: "Marken"); + + migrationBuilder.DropIndex( + name: "IX_Marken_MovieId_Art", + table: "Marken"); + + migrationBuilder.CreateIndex( + name: "IX_Marken_EpisodeId_Art", + table: "Marken", + columns: new[] { "EpisodeId", "Art" }); + + migrationBuilder.CreateIndex( + name: "IX_Marken_MovieId_Art", + table: "Marken", + columns: new[] { "MovieId", "Art" }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_Marken_EpisodeId_Art", + table: "Marken"); + + migrationBuilder.DropIndex( + name: "IX_Marken_MovieId_Art", + table: "Marken"); + + migrationBuilder.CreateIndex( + name: "IX_Marken_EpisodeId_Art", + table: "Marken", + columns: new[] { "EpisodeId", "Art" }, + unique: true, + filter: "\"EpisodeId\" IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_Marken_MovieId_Art", + table: "Marken", + columns: new[] { "MovieId", "Art" }, + unique: true, + filter: "\"MovieId\" IS NOT NULL"); + } + } +} diff --git a/server/src/Flaro.Data/Migrations/FlaroContextModelSnapshot.cs b/server/src/Flaro.Data/Migrations/FlaroContextModelSnapshot.cs index 8c4cf0e..4753f62 100644 --- a/server/src/Flaro.Data/Migrations/FlaroContextModelSnapshot.cs +++ b/server/src/Flaro.Data/Migrations/FlaroContextModelSnapshot.cs @@ -432,13 +432,9 @@ namespace Flaro.Data.Migrations b.HasKey("Id"); - b.HasIndex("EpisodeId", "Art") - .IsUnique() - .HasFilter("\"EpisodeId\" IS NOT NULL"); + b.HasIndex("EpisodeId", "Art"); - b.HasIndex("MovieId", "Art") - .IsUnique() - .HasFilter("\"MovieId\" IS NOT NULL"); + b.HasIndex("MovieId", "Art"); b.ToTable("Marken"); }); diff --git a/server/src/Flaro.Media/Bildabtaster.cs b/server/src/Flaro.Media/Bildabtaster.cs index ccc6360..985fa85 100644 --- a/server/src/Flaro.Media/Bildabtaster.cs +++ b/server/src/Flaro.Media/Bildabtaster.cs @@ -92,9 +92,56 @@ public sealed partial class Bildabtaster(FfmpegOptions optionen, ILogger p.Dunkelanteil); } + /// + /// Ein crop-Ausdruck vor der Messung, oder null für das + /// ganze Bild. Nur das Messwerkzeug benutzt das. + /// + /// Drei Kurven über denselben Abschnitt — zum Nachsehen. + /// + /// + /// + /// Ein Werkzeug, keine Erkennung. Die Randstreifen sind eine + /// Vermutung: bei Outtakes im Abspann läuft das Bild vielleicht in der + /// Mitte und die Ränder bleiben schwarz. Ob das trägt, sieht man nur an + /// echten Dateien — und dafür braucht es die Zahlen, bevor eine Schwelle + /// gesetzt wird. + /// + /// + /// Drei Durchläufe, nicht einer. Zwei blackframe in einer + /// Kette schrieben denselben Metadatenschlüssel und überschrieben + /// einander; mit split ginge es, kostete aber drei Ausgabewege + /// und eine Zusammenführung. Für etwas, das jemand einmal je Film von + /// Hand anstösst, sind drei Läufe über Schlüsselbilder das günstigere + /// Geschäft. + /// + /// + public async Task> KurveAsync( + string pfad, double ab, double dauer, CancellationToken ct = default) + { + async Task> Lauf(string? zuschnitt) => Lesen( + await LaufenAsync(pfad, ab, dauer, nurSchluesselbilder: true, + TimeSpan.FromMinutes(10), ct, zuschnitt), + ab); + + var gesamt = await Lauf(null); + var links = await Lauf("crop=iw*0.2:ih:0:0"); + var rechts = await Lauf("crop=iw*0.2:ih:iw*0.8:0"); + + // Nach Zeit zusammenführen: die drei Läufe sehen dieselben + // Schlüsselbilder, aber darauf zu bauen wäre eine Annahme. + double BeiZeit(IReadOnlyList reihe, double sekunde) => + reihe.Count == 0 ? double.NaN + : reihe.MinBy(p => Math.Abs(p.Sekunde - sekunde)).Dunkelanteil; + + return gesamt + .Select(p => new Kurvenpunkt( + p.Sekunde, p.Dunkelanteil, BeiZeit(links, p.Sekunde), BeiZeit(rechts, p.Sekunde))) + .ToList(); + } + private async Task LaufenAsync( string pfad, double ab, double dauer, bool nurSchluesselbilder, - TimeSpan frist, CancellationToken ct) + TimeSpan frist, CancellationToken ct, string? zuschnitt = null) { var start = new ProcessStartInfo(optionen.Ffmpeg) { @@ -135,7 +182,8 @@ public sealed partial class Bildabtaster(FfmpegOptions optionen, ILoggerDer Anfang des ersten Stücks — oder nichts. + /// + /// + /// Die meisten Prüfungen hier fragen nur „wo fängt der Abspann an". + /// Seit die Suche Stücke zurückgibt, steht das in [0].Von; ein + /// Helfer hält die Erwartungen lesbar, statt überall zu indizieren. + /// + private static double? Anfang(IReadOnlyList stücke) => + stücke.Count == 0 ? null : stücke[0].Von; + /// Eine Reihe von Messpunkten im Abstand von zehn Sekunden. /// /// Ab wann dunkel. null heisst: nie. @@ -31,7 +41,7 @@ public class AbspannkanteTests { var proben = Reihe(0, 600, abspannAb: 420); - Assert.That(Abspannkante.Suchen(proben, Regeln), Is.EqualTo(420)); + Assert.That(Anfang(Abspannkante.Suchen(proben, Regeln)), Is.EqualTo(420)); } /// Ein Film, der hell endet, hat keinen erkennbaren Abspann. @@ -44,7 +54,7 @@ public class AbspannkanteTests [Test] public void Ohne_dunkles_Ende_kein_Fund() { - Assert.That(Abspannkante.Suchen(Reihe(0, 600, abspannAb: null), Regeln), Is.Null); + Assert.That(Anfang(Abspannkante.Suchen(Reihe(0, 600, abspannAb: null), Regeln)), Is.Null); } /// Eine Schwarzblende mitten im Film zählt nicht. @@ -54,7 +64,7 @@ public class AbspannkanteTests var proben = Reihe(0, 600, abspannAb: null); for (var i = 20; i < 30; i++) proben[i] = proben[i] with { Dunkelanteil = 92 }; - Assert.That(Abspannkante.Suchen(proben, Regeln), Is.Null, + Assert.That(Anfang(Abspannkante.Suchen(proben, Regeln)), Is.Null, "sie hält nicht bis zum Ende durch"); } @@ -70,7 +80,7 @@ public class AbspannkanteTests for (var i = proben.Count - 5; i < proben.Count; i++) proben[i] = proben[i] with { Dunkelanteil = 0 }; - Assert.That(Abspannkante.Suchen(proben, Regeln), Is.Null); + Assert.That(Anfang(Abspannkante.Suchen(proben, Regeln)), Is.Null); } /// Eine helle Schrifttafel mittendrin bricht die Strecke nicht. @@ -89,21 +99,21 @@ public class AbspannkanteTests var proben = Reihe(0, 600, abspannAb: 420); proben[50] = proben[50] with { Dunkelanteil = 0 }; - Assert.That(Abspannkante.Suchen(proben, Regeln), Is.EqualTo(420)); + Assert.That(Anfang(Abspannkante.Suchen(proben, Regeln)), Is.EqualTo(420)); } /// Zwanzig Sekunden Dunkelheit sind kein Abspann. [Test] public void Zu_kurz_zaehlt_nicht() { - Assert.That(Abspannkante.Suchen(Reihe(0, 600, abspannAb: 580), Regeln), Is.Null); + Assert.That(Anfang(Abspannkante.Suchen(Reihe(0, 600, abspannAb: 580), Regeln)), Is.Null); } /// Ein halber Film im Dunkeln ist ein Fehlfund. [Test] public void Zu_lang_zaehlt_auch_nicht() { - Assert.That(Abspannkante.Suchen(Reihe(0, 3600, abspannAb: 600), Regeln), Is.Null); + Assert.That(Anfang(Abspannkante.Suchen(Reihe(0, 3600, abspannAb: 600), Regeln)), Is.Null); } [Test] @@ -199,6 +209,16 @@ public class AbspannAnteilTests { private static readonly Abspannregeln Regeln = Abspannregeln.Vorgabe; + /// Der Anfang des ersten Stücks — oder nichts. + /// + /// + /// Die meisten Prüfungen hier fragen nur „wo fängt der Abspann an". + /// Seit die Suche Stücke zurückgibt, steht das in [0].Von; ein + /// Helfer hält die Erwartungen lesbar, statt überall zu indizieren. + /// + private static double? Anfang(IReadOnlyList stücke) => + stücke.Count == 0 ? null : stücke[0].Von; + /// Ein Suchfenster, wie es im Betrieb entsteht. /// /// @@ -220,7 +240,7 @@ public class AbspannAnteilTests { var proben = Fenster(abspannAb: 2340, imAbspann: 84); - Assert.That(Abspannkante.Suchen(proben, Regeln), Is.EqualTo(2340.0).Within(5)); + Assert.That(Anfang(Abspannkante.Suchen(proben, Regeln)), Is.EqualTo(2340.0).Within(5)); } [Test] @@ -228,7 +248,7 @@ public class AbspannAnteilTests { var proben = Fenster(abspannAb: 2340, imAbspann: 87); - Assert.That(Abspannkante.Suchen(proben, Regeln), Is.EqualTo(2340.0).Within(5)); + Assert.That(Anfang(Abspannkante.Suchen(proben, Regeln)), Is.EqualTo(2340.0).Within(5)); } /// **Eine dunkle Nachtszene ist kein Abspann.** @@ -244,7 +264,7 @@ public class AbspannAnteilTests { var proben = Fenster(abspannAb: 2340, imAbspann: 37, imInhalt: 12); - Assert.That(Abspannkante.Suchen(proben, Regeln), Is.Null); + Assert.That(Anfang(Abspannkante.Suchen(proben, Regeln)), Is.Null); } /// Und ein heller Film bis zum Ende bleibt Handlung. @@ -253,7 +273,7 @@ public class AbspannAnteilTests { var proben = Fenster(abspannAb: double.MaxValue, imAbspann: 99); - Assert.That(Abspannkante.Suchen(proben, Regeln), Is.Null); + Assert.That(Anfang(Abspannkante.Suchen(proben, Regeln)), Is.Null); } } @@ -270,6 +290,16 @@ public class AbspannSchlusstafelTests { private static readonly Abspannregeln Regeln = Abspannregeln.Vorgabe; + /// Der Anfang des ersten Stücks — oder nichts. + /// + /// + /// Die meisten Prüfungen hier fragen nur „wo fängt der Abspann an". + /// Seit die Suche Stücke zurückgibt, steht das in [0].Von; ein + /// Helfer hält die Erwartungen lesbar, statt überall zu indizieren. + /// + private static double? Anfang(IReadOnlyList stücke) => + stücke.Count == 0 ? null : stücke[0].Von; + private static List Fenster(double abspannAb, double tafelAb = double.MaxValue) { var proben = new List(); @@ -288,7 +318,7 @@ public class AbspannSchlusstafelTests // Fünfzehn Sekunden weiss hinter dem Abspann. var proben = Fenster(abspannAb: 2340, tafelAb: 2690); - Assert.That(Abspannkante.Suchen(proben, Regeln), Is.EqualTo(2340).Within(5)); + Assert.That(Anfang(Abspannkante.Suchen(proben, Regeln)), Is.EqualTo(2340).Within(5)); } /// Eine lange helle Strecke am Ende schon. @@ -302,7 +332,7 @@ public class AbspannSchlusstafelTests { var proben = Fenster(abspannAb: 2340, tafelAb: 2600); - Assert.That(Abspannkante.Suchen(proben, Regeln), Is.Null); + Assert.That(Anfang(Abspannkante.Suchen(proben, Regeln)), Is.Null); } } @@ -329,6 +359,16 @@ public class AbspannNachsichtTests { private static readonly Abspannregeln Regeln = Abspannregeln.Vorgabe; + /// Der Anfang des ersten Stücks — oder nichts. + /// + /// + /// Die meisten Prüfungen hier fragen nur „wo fängt der Abspann an". + /// Seit die Suche Stücke zurückgibt, steht das in [0].Von; ein + /// Helfer hält die Erwartungen lesbar, statt überall zu indizieren. + /// + private static double? Anfang(IReadOnlyList stücke) => + stücke.Count == 0 ? null : stücke[0].Von; + /// Abspann ab 2340 s, darin fünf Sekunden helles Logo. private static List Fenster(double takt) { @@ -345,7 +385,7 @@ public class AbspannNachsichtTests [Test] public void Ein_Logo_zerreisst_den_Abspann_nicht_bei_dichten_Proben() { - Assert.That(Abspannkante.Suchen(Fenster(takt: 0.9), Regeln), + Assert.That(Anfang(Abspannkante.Suchen(Fenster(takt: 0.9), Regeln)), Is.EqualTo(2340.0).Within(2)); } @@ -353,18 +393,20 @@ public class AbspannNachsichtTests [Test] public void Und_auch_nicht_bei_duennen_Proben() { - Assert.That(Abspannkante.Suchen(Fenster(takt: 10), Regeln), + Assert.That(Anfang(Abspannkante.Suchen(Fenster(takt: 10), Regeln)), Is.EqualTo(2340.0).Within(11)); } - /// Eine lange helle Strecke bricht weiterhin ab. + /// Eine Minute helles Bild mitten im Abspann ist eine Szene. /// /// - /// Sonst liefe die Suche rückwärts durch jede Nachtszene und meldete - /// den halben Film als Abspann. + /// Früher brach die Suche hier ab und meldete nur das Stück + /// dahinter. Genau daran scheiterten Marvel-Filme: hinter dem Abspann + /// eine Post-Credit-Szene, davor der eigentliche Abspann — und + /// gefunden wurde nur der kurze Rest, der dann als zu kurz durchfiel. /// [Test] - public void Eine_lange_helle_Strecke_bricht_die_Suche() + public void Eine_Minute_hell_mittendrin_teilt_den_Abspann_in_zwei_Stuecke() { var proben = new List(); for (var t = 1500.0; t <= 2700; t += 1) @@ -374,8 +416,17 @@ public class AbspannNachsichtTests proben.Add(new Bildprobe(t, hell ? 0 : 85)); } - // Gefunden wird nur das Stück hinter der hellen Minute. - Assert.That(Abspannkante.Suchen(proben, Regeln), Is.EqualTo(2460.0).Within(2)); + var stücke = Abspannkante.Suchen(proben, Regeln); + + Assert.Multiple(() => + { + Assert.That(stücke, Has.Count.EqualTo(2)); + Assert.That(stücke[0].Von, Is.EqualTo(2340.0).Within(2)); + Assert.That(stücke[0].Bis, Is.EqualTo(2399.0).Within(2)); + Assert.That(stücke[1].Von, Is.EqualTo(2460.0).Within(2)); + Assert.That(stücke[1].Bis, Is.EqualTo(2700.0).Within(2), + "das letzte Stück reicht bis ans Dateiende"); + }); } /// Und der Grund steht für das Protokoll bereit. @@ -386,13 +437,125 @@ public class AbspannNachsichtTests for (var t = 1500.0; t <= 2700; t += 5) hell.Add(new Bildprobe(t, 0)); Abspannkante.Suchen(hell, Regeln, out var grund); - Assert.That(grund, Does.Contain("hell")); + Assert.That(grund, Does.Contain("keine dunkle Strecke")); var kurz = new List(); for (var t = 1500.0; t <= 2700; t += 5) kurz.Add(new Bildprobe(t, t < 2680 ? 0 : 85)); Abspannkante.Suchen(kurz, Regeln, out var zuKurz); - Assert.That(zuKurz, Does.Contain("kurz")); + Assert.That(zuKurz, Does.Contain("zusammen nur")); + } +} + +/// Ein Abspann darf aus Stücken bestehen. +/// +/// +/// +/// Wofür diese Prüfungen stehen. Zwei Fälle fielen durch, beide am +/// Bestand gesehen. Bei einem Marvel-Film liegt hinter dem Abspann eine +/// Post-Credit-Szene: die Suche begann am Dateiende, fand nur den kurzen +/// Rest dahinter und verwarf ihn als zu kurz. Und ein Abspann, den eine +/// Szene in der Mitte teilt, zerfiel in zwei Reste, von denen keiner lang +/// genug war. +/// +/// +/// Die Antwort ist dieselbe: Stücke über die Lücke hinweg zusammenfassen +/// und die Summe über die Mindestlänge entscheiden lassen. +/// +/// +public class AbspannstueckeTests +{ + private static readonly Abspannregeln Regeln = Abspannregeln.Vorgabe; + + /// Eine Reihe im Sekundentakt, dunkel in den angegebenen Spannen. + private static List Reihe(double bis, params (double Von, double Bis)[] dunkel) + { + var proben = new List(); + for (var t = 0.0; t <= bis; t += 1) + { + var istDunkel = Array.Exists(dunkel, d => t >= d.Von && t < d.Bis); + proben.Add(new Bildprobe(t, istDunkel ? 85 : 0)); + } + return proben; + } + + /// Der Marvel-Fall: Abspann, Szene, Abspann. + [Test] + public void Zwei_Stuecke_mit_einer_Szene_dazwischen() + { + // 180 s Abspann, 90 s Szene, 60 s Abspann bis zum Ende. + var stücke = Abspannkante.Suchen(Reihe(600, (330, 510), (540, 601)), Regeln); + + Assert.Multiple(() => + { + Assert.That(stücke, Has.Count.EqualTo(2)); + Assert.That(stücke[0].Von, Is.EqualTo(330.0).Within(2)); + Assert.That(stücke[1].Von, Is.EqualTo(540.0).Within(2)); + Assert.That(stücke[1].Bis, Is.EqualTo(600.0).Within(2)); + }); + } + + /// **Die Summe entscheidet, nicht das einzelne Stück.** + /// + /// + /// Genau hier scheiterte es vorher: 30 s plus 30 s sind zusammen 60 s + /// und damit ein Abspann, einzeln aber zweimal zu wenig. + /// + [Test] + public void Zwei_kurze_Stuecke_ergeben_zusammen_einen_Abspann() + { + var stücke = Abspannkante.Suchen(Reihe(600, (450, 480), (570, 601)), Regeln); + + Assert.Multiple(() => + { + Assert.That(stücke, Has.Count.EqualTo(2)); + Assert.That(stücke.Sum(t => t.Länge), Is.GreaterThanOrEqualTo(Regeln.Mindestlänge)); + }); + } + + /// Eine zu lange Lücke trennt — dann zählt nur das letzte Stück. + /// + /// + /// Sonst liefe die Suche rückwärts durch jede Nachtszene und hinge den + /// halben Film an den Abspann. Hinter einer Lücke von fünf Minuten + /// bleibt hier ein Stück von 30 s übrig, und das ist zu kurz. + /// + [Test] + public void Eine_zu_lange_Luecke_trennt() + { + var stücke = Abspannkante.Suchen(Reihe(900, (200, 400), (870, 901)), Regeln); + + Assert.That(stücke, Is.Empty); + } + + /// Ein zu kurzes Stück zwischen zwei gültigen zählt nicht mit. + [Test] + public void Ein_Stueck_von_zehn_Sekunden_faellt_heraus() + { + // Das mittlere Stück ist 10 s lang und damit unter `Mindeststück`. + var stücke = Abspannkante.Suchen( + Reihe(600, (300, 360), (450, 460), (540, 601)), Regeln); + + Assert.That(stücke, Has.Count.EqualTo(2), "das Zehnsekundenstück gehört nicht dazu"); + } + + /// Und ein durchgehender Abspann bleibt ein Stück. + /// + /// + /// Die wichtigste Prüfung dieser Gruppe: der Normalfall darf sich durch + /// die Stückelung nicht verändern. + /// + [Test] + public void Ein_durchgehender_Abspann_bleibt_ein_Stueck() + { + var stücke = Abspannkante.Suchen(Reihe(600, (420, 601)), Regeln); + + Assert.Multiple(() => + { + Assert.That(stücke, Has.Count.EqualTo(1)); + Assert.That(stücke[0].Von, Is.EqualTo(420.0).Within(2)); + Assert.That(stücke[0].Bis, Is.EqualTo(600.0).Within(2)); + }); } } diff --git a/server/tests/Flaro.Media.Tests/BildabtasterTests.cs b/server/tests/Flaro.Media.Tests/BildabtasterTests.cs index 04da549..c0af0df 100644 --- a/server/tests/Flaro.Media.Tests/BildabtasterTests.cs +++ b/server/tests/Flaro.Media.Tests/BildabtasterTests.cs @@ -127,13 +127,14 @@ public class BildabtasterLaufTests var proben = await Abtaster().UeberblickAsync(_datei, ab: 30, dauer: 120); Assert.That(proben, Is.Not.Empty, "der Filtergraph muss Zahlen liefern"); - var grob = Abspannkante.Suchen(proben, Abspannregeln.Vorgabe); - Assert.That(grob, Is.Not.Null, "eine dunkle Strecke bis zum Ende muss auffallen"); + var stücke = Abspannkante.Suchen(proben, Abspannregeln.Vorgabe); + Assert.That(stücke, Is.Not.Empty, "eine dunkle Strecke bis zum Ende muss auffallen"); - var hell = Abspannkante.HellDavor(proben, grob!.Value, Abspannregeln.Vorgabe) ?? 0; + var grob = stücke[0].Von; + var hell = Abspannkante.HellDavor(proben, grob, Abspannregeln.Vorgabe) ?? 0; var abtaster = Abtaster(); var kante = await Abspannkante.VerfeinernAsync( - hell, grob.Value, Abspannregeln.Vorgabe.Mindestanteil, + hell, grob, Abspannregeln.Vorgabe.Mindestanteil, stelle => abtaster.DunkelanteilAsync(_datei, stelle)); Assert.That(kante, Is.EqualTo(90).Within(4), @@ -146,7 +147,7 @@ public class BildabtasterLaufTests { var proben = await Abtaster().UeberblickAsync(_datei, ab: 0, dauer: 80); - Assert.That(Abspannkante.Suchen(proben, Abspannregeln.Vorgabe), Is.Null); + Assert.That(Abspannkante.Suchen(proben, Abspannregeln.Vorgabe), Is.Empty); } /// Eine einzelne Sekunde lässt sich messen — und liegt richtig. diff --git a/web/app/src/api.ts b/web/app/src/api.ts index 17efbaf..b70cc04 100644 --- a/web/app/src/api.ts +++ b/web/app/src/api.ts @@ -701,6 +701,14 @@ export async function markenHolen(titelId: string): Promise { export const mediathekMarken = (id: string, umfang: Markenumfang) => anfrage(`/libraries/${id}/marken?umfang=${umfang}`, { method: 'POST' }); +/** Die Adresse der Messkurve eines Films — zum Herunterladen. + * + * **Keine Erkennung, sondern Zahlen zum Ansehen.** Welche Schwelle bei + * einem Abspann über laufendem Bild trägt, lässt sich nur an der Kurve + * entscheiden, nicht am Ergebnis der Erkennung. */ +export const markenkurveAdresse = (filmId: string) => + `/api/v1/movies/${filmId}/markenkurve`; + /** Nimmt die Marken eines Films wieder weg — wenn die Erkennung danebenlag. */ export const filmmarkenVerwerfen = (filmId: string) => anfrage(`/movies/${filmId}/marken`, { method: 'DELETE' }); diff --git a/web/app/src/spieler.ts b/web/app/src/spieler.ts index d9312e5..2d3feb8 100644 --- a/web/app/src/spieler.ts +++ b/web/app/src/spieler.ts @@ -886,9 +886,16 @@ export function spieler(anfrage: Spieleranfrage): void { weiter ? 'btn' : 'btn btn-primary'); u.onclick = e => { e.stopPropagation(); - // Bei einem Film und bei der letzten Folge heisst „überspringen" - // Schluss: ans Ende zu spulen liesse ein stehendes Bild zurück. - if (abspann && !weiter) { void beenden(); return; } + + // **Kommt hinter diesem Stück noch eines, ist der Film nicht aus.** + // + // Ein Abspann kann geteilt sein: davor der eine Teil, dann eine + // Post-Credit-Szene, dann der Rest. Hier stand „bei einem Film + // heisst überspringen Schluss" — und damit bekam niemand die Szene + // je zu sehen. Geschlossen wird nur, wenn nach dieser Marke nichts + // mehr kommt. + const danach = marken.some(m => m.startSeconds >= drin.endSeconds - 1); + if (abspann && !weiter && !danach) { void beenden(); return; } void springen(drin.endSeconds); }; markenreihe.appendChild(u); diff --git a/web/app/src/views/detail.ts b/web/app/src/views/detail.ts index e601860..fde35e0 100644 --- a/web/app/src/views/detail.ts +++ b/web/app/src/views/detail.ts @@ -3,7 +3,7 @@ import { ApiFehler, vorschlaege, zuordnen, bildAdresse, istAdmin, gesehen, favorit, weglegen, serienstimmen, type Film, type Serie, type Folge, type Datei, type Stand, type Mitwirkender, type Spur, aehnliche, metadatenAuffrischen, scanstand, markenSuchen, - filmmarkenSuchen, filmmarkenVerwerfen, + filmmarkenSuchen, filmmarkenVerwerfen, markenkurveAdresse, } from '../api'; import { esc, dauer, groesse, knopf, feld, fehlerzeile, leer, anteil, meldung, hinweisZeigen, @@ -521,6 +521,16 @@ export function detailseite(art: Art, id: string, zurueck: () => void): HTMLElem window.setTimeout(() => void laden(), 4000); })); + // **Die Zahlen hinter dem Urteil.** „Kein Abspann" nennt den Grund, + // aber nicht die Kurve — und ohne die lässt sich keine Schwelle + // begründen. Der Knopf lädt sie als Tabelle herunter. + reihe.appendChild(rundKnopf('protokoll', false, 'Messwerte des Abspanns holen', + async () => { + window.location.href = markenkurveAdresse(eintrag.id); + hinweisZeigen('Die Messung läuft — der Server sieht sich dafür die ' + + 'letzten zwanzig Minuten dreimal an.'); + })); + // **Nur, wenn es etwas zu verwerfen gibt.** Ein Knopf, der nichts // wegnehmen kann, ist eine Behauptung über einen Zustand. if ('markers' in eintrag && (eintrag.markers?.length ?? 0) > 0) {