From 6de806316efaa61c5ddbac1b8f5b214949f0bcf8 Mon Sep 17 00:00:00 2001 From: Jagandeep Brar Date: Fri, 27 Jan 2023 19:31:56 -0500 Subject: [PATCH] feat(tautulli): display audio language, subtitle language, and video dynamic range for activity --- assets/localization/en.json | 2 + assets/localization/es.json | 2 + assets/localization/tr.json | 26 +- assets/localization/zh-Hans.json | 1 + lib/api/tautulli/models/activity/session.dart | 12 +- lib/api/tautulli/types/media.dart | 30 +- lib/extensions/string/string.dart | 2 + .../core/extensions/tautulli_session.dart | 289 +++++++++++++----- .../activity/widgets/activity_tile.dart | 2 +- .../widgets/block_stream.dart | 31 +- localization/tautulli/en.json | 140 ++++----- 11 files changed, 376 insertions(+), 161 deletions(-) diff --git a/assets/localization/en.json b/assets/localization/en.json index f681cb30..fd8f5f10 100644 --- a/assets/localization/en.json +++ b/assets/localization/en.json @@ -818,6 +818,7 @@ "tautulli.BackupDatabase": "Backup Database", "tautulli.Bandwidth": "Bandwidth", "tautulli.Burn": "Burn", + "tautulli.Converting": "Converting", "tautulli.Container": "Container", "tautulli.Copy": "Copy", "tautulli.DeleteCache": "Delete Cache", @@ -855,6 +856,7 @@ "tautulli.Sessions": "Sessions", "tautulli.SessionsOne": "1 Session", "tautulli.SessionsMany": "{} Sessions", + "tautulli.Speed": "Speed: {}x", "tautulli.Stream": "Stream", "tautulli.Subtitle": "Subtitle", "tautulli.Terminate": "Terminate", diff --git a/assets/localization/es.json b/assets/localization/es.json index daa562dd..4657a322 100644 --- a/assets/localization/es.json +++ b/assets/localization/es.json @@ -680,6 +680,8 @@ "sonarr.Streams": "Transmisiones", "sonarr.DownloadID": "ID de descarga", "sonarr.DownloadFailed": "Descarga fracasó", + "sonarr.DeleteEpisodeFileHint1": "¿Seguro que quieres eliminar este archivo de episodio?", + "sonarr.DeleteReasonManual": "El archivo fue borrado vía UI", "tautulli.Quality": "Calidad", "tautulli.None": "Ninguno", "tautulli.Terminate": "Terminar", diff --git a/assets/localization/tr.json b/assets/localization/tr.json index 61220ccc..db45a8f8 100644 --- a/assets/localization/tr.json +++ b/assets/localization/tr.json @@ -282,6 +282,30 @@ "radarr.Wanted": "İstenen", "radarr.Weight": "Ağırlık", "radarr.Year": "Yıl", + "sabnzbd.Age": "Yaş", + "sabnzbd.Duplicate": "Yinelenen", + "sabnzbd.Failed": "Başarısız", + "sabnzbd.Force": "Zorla", + "sabnzbd.Repair": "Onar", + "sabnzbd.RepairUnpack": "Onar/Ayıkla", + "sabnzbd.RepairUnpackDelete": "Onar/Ayıkla/Sil", + "sabnzbd.Size": "Boyut", + "sabnzbd.ShutdownPC": "Bilgisayarı Kapat", + "sabnzbd.ShutdownSABnzbd": "SABnzbd'yi Kapat", + "sabnzbd.StandbyPC": "Bilgisayarı Beklemeye Al", + "sabnzbd.Stop": "Durdur", + "sabnzbd.CategoryDefault": "Kategori Öntanımlı Değeri", + "sabnzbd.Completed": "Tamamlandı", + "sabnzbd.Descending": "Azalan", + "sabnzbd.High": "Yüksek", + "sabnzbd.Low": "Düşük", + "sabnzbd.Paused": "Duraklatıldı", + "sabnzbd.All": "Tümü", + "sabnzbd.Ascending": "Artan", + "sabnzbd.HibernatePC": "Bilgisayarı Hazırda Beklet", + "sabnzbd.None": "Yok", + "sabnzbd.Name": "Ad", + "sabnzbd.Normal": "Normal", "search.Subcategories": "Alt Kategoriler", "search.Size": "Boyut", "search.SentTo": "{}'e gönderildi", @@ -602,7 +626,7 @@ "sonarr.Monitor": "Takip Et", "sonarr.MonitorEpisode": "Bölümü Takip Et", "sonarr.MonitorSeries": "Diziyi Takip Et", - "sonarr.Name": "İsim", + "sonarr.Name": "Ad", "sonarr.NoEpisodesFound": "Bölüm Bulunamadı", "sonarr.NoSeasonsFound": "Sezonlar Bulunamadı", "sonarr.NoSeriesFound": "Diziler Bulunamadı", diff --git a/assets/localization/zh-Hans.json b/assets/localization/zh-Hans.json index a28788f5..731a582c 100644 --- a/assets/localization/zh-Hans.json +++ b/assets/localization/zh-Hans.json @@ -593,6 +593,7 @@ "settings.DiscordDescription": "请求功能获得支持", "settings.General": "通用", "settings.GeneralDescription": "自定义LunaSea", + "settings.BootModule": "启动模块", "sonarr.AddSeries": "添加系列", "sonarr.AddedOn": "添加于", "sonarr.Age": "年龄", diff --git a/lib/api/tautulli/models/activity/session.dart b/lib/api/tautulli/models/activity/session.dart index 16b21793..3c0f1b55 100644 --- a/lib/api/tautulli/models/activity/session.dart +++ b/lib/api/tautulli/models/activity/session.dart @@ -312,7 +312,7 @@ class TautulliSession { fromJson: TautulliUtilities.ensureStringFromJson) final String? videoResolution; - /// Full resoltuion of the video stream. + /// Full resolution of the video stream. @JsonKey( name: 'video_full_resolution', fromJson: TautulliUtilities.ensureStringFromJson) @@ -793,13 +793,13 @@ class TautulliSession { @JsonKey( name: 'transcode_hw_encode', fromJson: TautulliUtilities.ensureStringFromJson) - final String? transcodeHardwarEencode; + final String? transcodeHardwareEncode; /// Hardware encode title of the transcode stream. @JsonKey( name: 'transcode_hw_encode_title', fromJson: TautulliUtilities.ensureStringFromJson) - final String? transcodeHardwarEencodeTitle; + final String? transcodeHardwareEncodeTitle; /// Is the transcoder using hardware acceleration for the full pipeline? @JsonKey( @@ -958,7 +958,7 @@ class TautulliSession { @JsonKey(name: 'subtitles', fromJson: TautulliUtilities.ensureBooleanFromJson) final bool? subtitles; - /// Full resoltuion of the final video stream. + /// Full resolution of the final video stream. @JsonKey( name: 'stream_video_full_resolution', fromJson: TautulliUtilities.ensureStringFromJson) @@ -1295,8 +1295,8 @@ class TautulliSession { this.transcodeHardwareRequested, this.transcodeHardwareDecode, this.transcodeHardwareDecodeTitle, - this.transcodeHardwarEencode, - this.transcodeHardwarEencodeTitle, + this.transcodeHardwareEncode, + this.transcodeHardwareEncodeTitle, this.transcodeHardwareFullPipeline, this.audioDecision, this.videoDecision, diff --git a/lib/api/tautulli/types/media.dart b/lib/api/tautulli/types/media.dart index b7e1af13..e39923ce 100644 --- a/lib/api/tautulli/types/media.dart +++ b/lib/api/tautulli/types/media.dart @@ -9,8 +9,12 @@ enum TautulliMediaType { ARTIST, ALBUM, TRACK, - LIVE, + PHOTO_ALBUM, + PHOTO, + CLIP, COLLECTION, + PLAYLIST, + LIVE, NULL; static TautulliMediaType from(String? type) { @@ -29,10 +33,18 @@ enum TautulliMediaType { return TautulliMediaType.ALBUM; case 'track': return TautulliMediaType.TRACK; - case 'live': - return TautulliMediaType.LIVE; + case 'photoalbum': + return TautulliMediaType.PHOTO_ALBUM; + case 'photo': + return TautulliMediaType.PHOTO; + case 'clip': + return TautulliMediaType.CLIP; case 'collection': return TautulliMediaType.COLLECTION; + case 'playlist': + return TautulliMediaType.PLAYLIST; + case 'live': + return TautulliMediaType.LIVE; default: return TautulliMediaType.NULL; } @@ -55,10 +67,18 @@ enum TautulliMediaType { return 'album'; case TautulliMediaType.TRACK: return 'track'; - case TautulliMediaType.LIVE: - return 'live'; + case TautulliMediaType.PHOTO_ALBUM: + return 'photoalbum'; + case TautulliMediaType.PHOTO: + return 'photo'; + case TautulliMediaType.CLIP: + return 'clip'; case TautulliMediaType.COLLECTION: return 'collection'; + case TautulliMediaType.PLAYLIST: + return 'playlist'; + case TautulliMediaType.LIVE: + return 'live'; default: return ''; } diff --git a/lib/extensions/string/string.dart b/lib/extensions/string/string.dart index e125edc6..ce68fdf1 100644 --- a/lib/extensions/string/string.dart +++ b/lib/extensions/string/string.dart @@ -20,6 +20,8 @@ extension StringExtension on String { String toTitleCase({ String delimiter = ' ', }) { + if (isEmpty) return ''; + final split = this.split(delimiter); for (var i = 0; i < split.length; i++) { String word = split[i]; diff --git a/lib/modules/tautulli/core/extensions/tautulli_session.dart b/lib/modules/tautulli/core/extensions/tautulli_session.dart index 4d084cb4..7c0c0c77 100644 --- a/lib/modules/tautulli/core/extensions/tautulli_session.dart +++ b/lib/modules/tautulli/core/extensions/tautulli_session.dart @@ -3,65 +3,230 @@ import 'package:lunasea/core.dart'; import 'package:lunasea/extensions/datetime.dart'; import 'package:lunasea/extensions/duration/timestamp.dart'; import 'package:lunasea/extensions/int/bytes.dart'; +import 'package:lunasea/extensions/string/string.dart'; import 'package:lunasea/modules/tautulli.dart'; +extension TautulliSessionAudioExtension on TautulliSession { + String get _language { + if (audioLanguage?.isEmpty ?? true) return 'lunasea.Unknown'.tr(); + return audioLanguage!.toTitleCase(); + } + + String get _codec { + if (audioCodec?.isEmpty ?? true) return LunaUI.TEXT_EMDASH; + if (audioCodec == 'truehd') return 'TrueHD'; + return audioCodec!.toUpperCase(); + } + + String get _channelLayout { + if (audioChannelLayout?.isEmpty ?? true) return LunaUI.TEXT_EMDASH; + return audioChannelLayout!.split('(')[0].toTitleCase(); + } + + String get _streamCodec { + if (streamAudioCodec?.isEmpty ?? true) return LunaUI.TEXT_EMDASH; + if (streamAudioCodec == 'truehd') return 'TrueHD'; + return streamAudioCodec!.toUpperCase(); + } + + String get _streamChannelLayout { + if (streamAudioChannelLayout?.isEmpty ?? false) return LunaUI.TEXT_EMDASH; + return streamAudioChannelLayout!.split('(')[0].toTitleCase(); + } + + String formattedAudio() { + if (streamAudioDecision != null) { + final decision = streamAudioDecision.localizedName; + + switch (streamAudioDecision) { + case TautulliTranscodeDecision.TRANSCODE: + return '$decision ($_language - $_codec $_channelLayout ${LunaUI.TEXT_ARROW_RIGHT} $_streamCodec $_streamChannelLayout)'; + case TautulliTranscodeDecision.COPY: + return '$decision ($_language - $_streamCodec $_streamChannelLayout)'; + default: + return '$decision ($_language - $_streamCodec $_streamChannelLayout)'; + } + } + + return 'tautulli.None'.tr(); + } +} + +extension TautulliSessionContainerExtension on TautulliSession { + String get _container { + if (container?.isEmpty ?? true) return 'lunasea.Unknown'.tr(); + return container!.toUpperCase(); + } + + String get _streamContainer { + if (streamContainer?.isEmpty ?? true) return 'lunasea.Unknown'.tr(); + return streamContainer!.toUpperCase(); + } + + String formattedContainer() { + if (streamContainerDecision == TautulliTranscodeDecision.TRANSCODE) { + return '${'tautulli.Converting'.tr()} ($_container ${LunaUI.TEXT_ARROW_RIGHT} $_streamContainer)'; + } + + return '${TautulliTranscodeDecision.DIRECT_PLAY.localizedName} ($_streamContainer)'; + } +} + +extension TautulliSessionSubtitleExtension on TautulliSession { + String get _language { + if (subtitleLanguage?.isEmpty ?? true) return 'lunasea.Unknown'.tr(); + return subtitleLanguage!.toTitleCase(); + } + + String get _codec { + if ((streamSubtitleTransient ?? false) && streamSubtitleCodec != null) + return 'tautulli.None'.tr(); + if (subtitleCodec?.isEmpty ?? true) return LunaUI.TEXT_EMDASH; + return subtitleCodec!.toUpperCase(); + } + + String get _streamCodec { + if (streamSubtitleCodec?.isEmpty ?? true) return LunaUI.TEXT_EMDASH; + return streamSubtitleCodec!.toUpperCase(); + } + + String formattedSubtitles() { + if (subtitles ?? false) { + final decision = streamSubtitleDecision.localizedName; + final synced = syncedVersion ?? false; + + switch (streamSubtitleDecision) { + case TautulliTranscodeDecision.TRANSCODE: + return '$decision ($_language - $_codec ${LunaUI.TEXT_ARROW_RIGHT} $_streamCodec)'; + case TautulliTranscodeDecision.COPY: + return '$decision ($_language - $_codec)'; + case TautulliTranscodeDecision.BURN: + return '$decision ($_language - $_codec)'; + default: + return '${TautulliTranscodeDecision.DIRECT_PLAY.localizedName} ($_language - ${synced ? _codec : _streamCodec})'; + } + } + + return 'tautulli.None'.tr(); + } +} + +extension TautulliSessionStreamExtension on TautulliSession { + String get _transcodeSpeed { + if (transcodeThrottled ?? true) return 'tautulli.Throttled'.tr(); + return 'tautulli.Speed'.tr(args: ['${transcodeSpeed ?? 0.0}']); + } + + String formattedStream() { + final decision = transcodeDecision.localizedName; + + switch (transcodeDecision) { + case TautulliTranscodeDecision.TRANSCODE: + return '$decision ($_transcodeSpeed)'; + case TautulliTranscodeDecision.COPY: + return decision; + default: + return TautulliTranscodeDecision.DIRECT_PLAY.localizedName; + } + } +} + +extension TautulliSessionVideoExtension on TautulliSession { + String get _codec { + if (videoCodec?.isEmpty ?? true) return 'lunasea.Unknown'.tr(); + return videoCodec!.toUpperCase(); + } + + String get _streamCodec { + if (streamVideoCodec?.isEmpty ?? true) return 'lunasea.Unknown'.tr(); + return streamVideoCodec!.toUpperCase(); + } + + String get _fullResolution { + if (videoFullResolution?.isEmpty ?? true) return 'lunasea.Unknown'.tr(); + return videoFullResolution!; + } + + String get _streamFullResolution { + if (streamVideoFullResolution?.isEmpty ?? true) + return 'lunasea.Unknown'.tr(); + return streamVideoFullResolution!; + } + + String get _dynamicRange { + if (videoDynamicRange?.isEmpty ?? true) return ''; + if (videoDynamicRange != 'SDR') return ' $videoDynamicRange'; + return ''; + } + + String get _streamDynamicRange { + final isSdr = streamVideoDynamicRange == 'SDR'; + + if (streamVideoDynamicRange?.isEmpty ?? true) return ''; + if (!isSdr || _dynamicRange.isNotEmpty) return ' $streamVideoDynamicRange'; + return ''; + } + + String get _hardwareDecoding { + final isTc = streamVideoDecision == TautulliTranscodeDecision.TRANSCODE; + + if (isTc && (transcodeHardwareDecoding ?? false)) return ' (HW)'; + return ''; + } + + String get _hardwareEncoding { + final isTc = streamVideoDecision == TautulliTranscodeDecision.TRANSCODE; + + if (isTc && (transcodeHardwareEncoding ?? false)) return ' (HW)'; + return ''; + } + + String formattedVideo() { + if (streamVideoDecision != null) { + final decision = streamVideoDecision.localizedName; + + switch (streamVideoDecision) { + case TautulliTranscodeDecision.TRANSCODE: + return '$decision ($_codec$_hardwareDecoding $_fullResolution$_dynamicRange ${LunaUI.TEXT_ARROW_RIGHT} $_streamCodec$_hardwareEncoding $_streamFullResolution$_streamDynamicRange)'; + case TautulliTranscodeDecision.COPY: + return '$decision ($_streamCodec $_streamFullResolution$_streamDynamicRange)'; + default: + return '${TautulliTranscodeDecision.DIRECT_PLAY.localizedName} ($_streamCodec $_streamFullResolution$_streamDynamicRange)'; + } + } + + return 'tautulli.None'.tr(); + } +} + extension TautulliSessionExtension on TautulliSession { - String get lunaContainer { - return [ - this.streamContainerDecision.localizedName, - ' (', - this.container!.toUpperCase(), - if (this.streamContainerDecision != null && - this.streamContainerDecision != TautulliTranscodeDecision.DIRECT_PLAY) - ' ${LunaUI.TEXT_ARROW_RIGHT} ${this.streamContainer!.toUpperCase()}', - ')', - ].join(); + bool hasAudio() { + const types = [ + TautulliMediaType.MOVIE, + TautulliMediaType.EPISODE, + TautulliMediaType.CLIP, + TautulliMediaType.TRACK, + ]; + return types.contains(mediaType); } - String get lunaVideo { - return [ - this.videoDecision.localizedName, - ' (${this.videoCodec?.toUpperCase() ?? LunaUI.TEXT_EMDASH} ', - if (this.transcodeHardwareDecoding!) '(HW) ', - this.videoFullResolution, - if (this.transcodeVideoCodec!.isNotEmpty) ' ${LunaUI.TEXT_ARROW_RIGHT} ', - if (this.transcodeVideoCodec!.isNotEmpty) - '${this.transcodeVideoCodec!.toUpperCase()} ', - if (this.transcodeVideoCodec!.isNotEmpty && - this.transcodeHardwareDecoding!) - '(HW) ', - if (this.transcodeVideoCodec!.isNotEmpty) this.streamVideoFullResolution, - ')', - ].join(); + bool hasSubtitles() { + const types = [ + TautulliMediaType.MOVIE, + TautulliMediaType.EPISODE, + TautulliMediaType.CLIP, + ]; + return types.contains(mediaType); } - String get lunaAudio { - return [ - this.audioDecision.localizedName, - ' (', - this.audioCodec?.toUpperCase() ?? LunaUI.TEXT_EMDASH, - if (this.audioChannelLayout?.split('(').isNotEmpty ?? false) - ' ${this.audioChannelLayout?.split('(')[0]}', - if (this.transcodeAudioCodec?.isNotEmpty ?? false) - ' ${LunaUI.TEXT_ARROW_RIGHT} ', - if (this.transcodeAudioCodec?.isNotEmpty ?? false) - this.transcodeAudioCodec!.toUpperCase(), - if ((this.transcodeAudioCodec?.isNotEmpty ?? false) && - (this.streamAudioChannelLayout?.split('(').isNotEmpty ?? false)) - ' ${this.streamAudioChannelLayout?.split('(')[0]}', - ')', - ].join(); - } - - String get lunaSubtitle { - if (!this.subtitles!) return 'tautulli.None'.tr(); - return [ - this.streamSubtitleDecision == TautulliTranscodeDecision.NULL - ? 'tautulli.DirectPlay'.tr() - : this.streamSubtitleDecision.localizedName, - if (this.subtitleCodec != null && this.subtitleCodec!.isNotEmpty) - '(${this.subtitleCodec?.toUpperCase() ?? LunaUI.TEXT_EMDASH})', - ].join(' '); + bool hasVideo() { + const types = [ + TautulliMediaType.MOVIE, + TautulliMediaType.EPISODE, + TautulliMediaType.CLIP, + TautulliMediaType.PHOTO, + ]; + return types.contains(mediaType); } String? get lunaTitle { @@ -169,26 +334,6 @@ extension TautulliSessionExtension on TautulliSession { } } - String get lunaTranscodeDecision { - switch (this.transcodeDecision) { - case TautulliTranscodeDecision.TRANSCODE: - String _transcodeStatus = this.transcodeThrottled! - ? 'tautulli.Throttled'.tr() - : '${this.transcodeSpeed ?? 0.0}x'; - return [ - this.transcodeDecision.localizedName, - ' ($_transcodeStatus)', - ].join(); - case TautulliTranscodeDecision.DIRECT_PLAY: - case TautulliTranscodeDecision.COPY: - case TautulliTranscodeDecision.BURN: - case TautulliTranscodeDecision.NULL: - return this.transcodeDecision.localizedName; - default: - return 'tautulli.None'.tr(); - } - } - IconData get lunaSessionStateIcon { switch (this.state) { case TautulliSessionState.PAUSED: diff --git a/lib/modules/tautulli/routes/activity/widgets/activity_tile.dart b/lib/modules/tautulli/routes/activity/widgets/activity_tile.dart index f3e6be3d..958edbb9 100644 --- a/lib/modules/tautulli/routes/activity/widgets/activity_tile.dart +++ b/lib/modules/tautulli/routes/activity/widgets/activity_tile.dart @@ -86,7 +86,7 @@ class TautulliActivityTile extends StatelessWidget { TextSpan _subtitle3() { return TextSpan( - text: session.lunaTranscodeDecision, + text: session.formattedStream(), style: const TextStyle( fontWeight: LunaUI.FONT_WEIGHT_BOLD, color: LunaColours.accent, diff --git a/lib/modules/tautulli/routes/activity_details/widgets/block_stream.dart b/lib/modules/tautulli/routes/activity_details/widgets/block_stream.dart index 6c36f693..343198f8 100644 --- a/lib/modules/tautulli/routes/activity_details/widgets/block_stream.dart +++ b/lib/modules/tautulli/routes/activity_details/widgets/block_stream.dart @@ -15,15 +15,32 @@ class TautulliActivityDetailsStreamBlock extends StatelessWidget { return LunaTableCard( content: [ LunaTableContent( - title: 'tautulli.Bandwidth'.tr(), body: session.lunaBandwidth), + title: 'tautulli.Bandwidth'.tr(), + body: session.lunaBandwidth, + ), LunaTableContent( - title: 'tautulli.Stream'.tr(), body: session.lunaTranscodeDecision), + title: 'tautulli.Stream'.tr(), + body: session.formattedStream(), + ), LunaTableContent( - title: 'tautulli.Container'.tr(), body: session.lunaContainer), - LunaTableContent(title: 'tautulli.Video'.tr(), body: session.lunaVideo), - LunaTableContent(title: 'tautulli.Audio'.tr(), body: session.lunaAudio), - LunaTableContent( - title: 'tautulli.Subtitle'.tr(), body: session.lunaSubtitle), + title: 'tautulli.Container'.tr(), + body: session.formattedContainer(), + ), + if (session.hasVideo()) + LunaTableContent( + title: 'tautulli.Video'.tr(), + body: session.formattedVideo(), + ), + if (session.hasAudio()) + LunaTableContent( + title: 'tautulli.Audio'.tr(), + body: session.formattedAudio(), + ), + if (session.hasSubtitles()) + LunaTableContent( + title: 'tautulli.Subtitle'.tr(), + body: session.formattedSubtitles(), + ), ], ); } diff --git a/localization/tautulli/en.json b/localization/tautulli/en.json index c4b799c0..c76738d8 100644 --- a/localization/tautulli/en.json +++ b/localization/tautulli/en.json @@ -1,70 +1,72 @@ { - "tautulli.Activity": "Activity", - "tautulli.ActivityDetails": "Activity Details", - "tautulli.Audio": "Audio", - "tautulli.BackingUpConfiguration": "Backing Up Configuration…", - "tautulli.BackingUpConfigurationDescription": "Backing up your configuration in the background", - "tautulli.BackingUpConfigurationFailed": "Failed to Backup Configuration", - "tautulli.BackingUpDatabase": "Backing Up Database…", - "tautulli.BackingUpDatabaseDescription": "Backing up your database in the background", - "tautulli.BackingUpDatabaseFailed": "Failed to Backup Database", - "tautulli.BackupConfiguration": "Backup Configuration", - "tautulli.BackupDatabase": "Backup Database", - "tautulli.Bandwidth": "Bandwidth", - "tautulli.Burn": "Burn", - "tautulli.Container": "Container", - "tautulli.Copy": "Copy", - "tautulli.DeleteCache": "Delete Cache", - "tautulli.DeleteImageCache": "Delete Image Cache", - "tautulli.DeleteTemporarySessions": "Delete Temporary Sessions", - "tautulli.DeletingCache": "Deleting Cache…", - "tautulli.DeletingCacheDescription": "Tautulli cache is being deleted", - "tautulli.DeletingCacheFailed": "Failed to Delete Cache", - "tautulli.DeletingImageCache": "Deleting Image Cache…", - "tautulli.DeletingImageCacheDescription": "Tautulli image cache is being deleted", - "tautulli.DeletingImageCacheFailed": "Failed to Delete Image Cache", - "tautulli.DeletingTemporarySessions": "Deleting Temporary Sessions…", - "tautulli.DeletingTemporarySessionsDescription": "Temporary sessions are being deleted", - "tautulli.DeletingTemporarySessionsFailed": "Failed to Delete Temporary Sessions", - "tautulli.DirectPlay": "Direct Play", - "tautulli.DirectPlays": "Direct Plays", - "tautulli.DirectStream": "Direct Stream", - "tautulli.DirectStreams": "Direct Streams", - "tautulli.Duration": "Duration", - "tautulli.Episode": "Episode {}", - "tautulli.ETA": "ETA", - "tautulli.History": "History", - "tautulli.Library": "Library", - "tautulli.Location": "Location", - "tautulli.Metadata": "Metadata", - "tautulli.More": "More", - "tautulli.None": "None", - "tautulli.NoActiveStreams": "No Active Streams", - "tautulli.Platform": "Platform", - "tautulli.Player": "Player", - "tautulli.Product": "Product", - "tautulli.Quality": "Quality", - "tautulli.Season": "Season {}", - "tautulli.SessionEnded": "Session Ended", - "tautulli.Sessions": "Sessions", - "tautulli.SessionsOne": "1 Session", - "tautulli.SessionsMany": "{} Sessions", - "tautulli.Stream": "Stream", - "tautulli.Subtitle": "Subtitle", - "tautulli.Terminate": "Terminate", - "tautulli.TerminationAttachMessage": "You can optionally attach a termination message below.", - "tautulli.TerminationConfirmMessage": "Do you want to terminate this session?", - "tautulli.TerminationMessage": "Termination Message", - "tautulli.TerminatedSession": "Terminated Session", - "tautulli.TerminateSession": "Terminate Session", - "tautulli.TerminateSessionFailed": "Failed to Terminate Session", - "tautulli.Throttled": "Throttled", - "tautulli.Title": "Title", - "tautulli.Transcode": "Transcode", - "tautulli.Transcodes": "Transcodes", - "tautulli.User": "User", - "tautulli.Users": "Users", - "tautulli.Video": "Video", - "tautulli.ViewWebGUI": "View Web GUI", - "tautulli.Year": "Year" -} + "tautulli.Activity": "Activity", + "tautulli.ActivityDetails": "Activity Details", + "tautulli.Audio": "Audio", + "tautulli.BackingUpConfiguration": "Backing Up Configuration…", + "tautulli.BackingUpConfigurationDescription": "Backing up your configuration in the background", + "tautulli.BackingUpConfigurationFailed": "Failed to Backup Configuration", + "tautulli.BackingUpDatabase": "Backing Up Database…", + "tautulli.BackingUpDatabaseDescription": "Backing up your database in the background", + "tautulli.BackingUpDatabaseFailed": "Failed to Backup Database", + "tautulli.BackupConfiguration": "Backup Configuration", + "tautulli.BackupDatabase": "Backup Database", + "tautulli.Bandwidth": "Bandwidth", + "tautulli.Burn": "Burn", + "tautulli.Converting": "Converting", + "tautulli.Container": "Container", + "tautulli.Copy": "Copy", + "tautulli.DeleteCache": "Delete Cache", + "tautulli.DeleteImageCache": "Delete Image Cache", + "tautulli.DeleteTemporarySessions": "Delete Temporary Sessions", + "tautulli.DeletingCache": "Deleting Cache…", + "tautulli.DeletingCacheDescription": "Tautulli cache is being deleted", + "tautulli.DeletingCacheFailed": "Failed to Delete Cache", + "tautulli.DeletingImageCache": "Deleting Image Cache…", + "tautulli.DeletingImageCacheDescription": "Tautulli image cache is being deleted", + "tautulli.DeletingImageCacheFailed": "Failed to Delete Image Cache", + "tautulli.DeletingTemporarySessions": "Deleting Temporary Sessions…", + "tautulli.DeletingTemporarySessionsDescription": "Temporary sessions are being deleted", + "tautulli.DeletingTemporarySessionsFailed": "Failed to Delete Temporary Sessions", + "tautulli.DirectPlay": "Direct Play", + "tautulli.DirectPlays": "Direct Plays", + "tautulli.DirectStream": "Direct Stream", + "tautulli.DirectStreams": "Direct Streams", + "tautulli.Duration": "Duration", + "tautulli.Episode": "Episode {}", + "tautulli.ETA": "ETA", + "tautulli.History": "History", + "tautulli.Library": "Library", + "tautulli.Location": "Location", + "tautulli.Metadata": "Metadata", + "tautulli.More": "More", + "tautulli.None": "None", + "tautulli.NoActiveStreams": "No Active Streams", + "tautulli.Platform": "Platform", + "tautulli.Player": "Player", + "tautulli.Product": "Product", + "tautulli.Quality": "Quality", + "tautulli.Season": "Season {}", + "tautulli.SessionEnded": "Session Ended", + "tautulli.Sessions": "Sessions", + "tautulli.SessionsOne": "1 Session", + "tautulli.SessionsMany": "{} Sessions", + "tautulli.Speed": "Speed: {}x", + "tautulli.Stream": "Stream", + "tautulli.Subtitle": "Subtitle", + "tautulli.Terminate": "Terminate", + "tautulli.TerminationAttachMessage": "You can optionally attach a termination message below.", + "tautulli.TerminationConfirmMessage": "Do you want to terminate this session?", + "tautulli.TerminationMessage": "Termination Message", + "tautulli.TerminatedSession": "Terminated Session", + "tautulli.TerminateSession": "Terminate Session", + "tautulli.TerminateSessionFailed": "Failed to Terminate Session", + "tautulli.Throttled": "Throttled", + "tautulli.Title": "Title", + "tautulli.Transcode": "Transcode", + "tautulli.Transcodes": "Transcodes", + "tautulli.User": "User", + "tautulli.Users": "Users", + "tautulli.Video": "Video", + "tautulli.ViewWebGUI": "View Web GUI", + "tautulli.Year": "Year" +} \ No newline at end of file