From 8ae55dc57b0c01c1dac339f10e46cfca82b7dd15 Mon Sep 17 00:00:00 2001 From: Joe Date: Tue, 15 Oct 2024 16:04:32 -0600 Subject: [PATCH] [iOS] Admin Dashboard - Active Sessions Play Method Fix (#1272) * Fixes. But is this right? * Ensure there IS an item playing. * https://github.com/jellyfin/Swiftfin/pull/1272#discussion_r1801713951 * Fix the missing labels after merging with Main. --- .../Extensions/JellyfinAPI/SessionInfo.swift | 37 +++ Shared/Strings/Strings.swift | 292 +++++++++--------- Swiftfin.xcodeproj/project.pbxproj | 8 +- .../ActiveSessionDetailView.swift | 4 +- Translations/en.lproj/Localizable.strings | Bin 45276 -> 22646 bytes 5 files changed, 193 insertions(+), 148 deletions(-) create mode 100644 Shared/Extensions/JellyfinAPI/SessionInfo.swift diff --git a/Shared/Extensions/JellyfinAPI/SessionInfo.swift b/Shared/Extensions/JellyfinAPI/SessionInfo.swift new file mode 100644 index 00000000..26e9092e --- /dev/null +++ b/Shared/Extensions/JellyfinAPI/SessionInfo.swift @@ -0,0 +1,37 @@ +// +// Swiftfin is subject to the terms of the Mozilla Public +// License, v2.0. If a copy of the MPL was not distributed with this +// file, you can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2024 Jellyfin & Jellyfin Contributors +// + +import Foundation +import JellyfinAPI + +extension SessionInfo { + + var playMethodDisplayTitle: String? { + guard nowPlayingItem != nil, let playState, let playMethod = playState.playMethod else { return nil } + + if let transcodingInfo { + + let isVideoDirect = transcodingInfo.isVideoDirect ?? false + let hasVideoCodec = transcodingInfo.videoCodec != nil + let isAudioDirect = transcodingInfo.isAudioDirect ?? false + + if isVideoDirect || hasVideoCodec, isAudioDirect { + return L10n.remux + } else if isVideoDirect { + return PlayMethod.directStream.displayTitle + } + } + + switch playMethod { + case .transcode: + return PlayMethod.transcode.displayTitle + case .directPlay, .directStream: + return PlayMethod.directPlay.displayTitle + } + } +} diff --git a/Shared/Strings/Strings.swift b/Shared/Strings/Strings.swift index c7101a92..4c51e9cc 100644 --- a/Shared/Strings/Strings.swift +++ b/Shared/Strings/Strings.swift @@ -18,13 +18,13 @@ internal enum L10n { internal static let accentColorDescription = L10n.tr("Localizable", "accentColorDescription", fallback: "Some views may need an app restart to update.") /// Accessibility internal static let accessibility = L10n.tr("Localizable", "accessibility", fallback: "Accessibility") - /// Active Devices + /// ActiveSessionsView Header internal static let activeDevices = L10n.tr("Localizable", "activeDevices", fallback: "Active Devices") - /// Add Server + /// Select Server View - Add Server internal static let addServer = L10n.tr("Localizable", "addServer", fallback: "Add Server") /// Add URL internal static let addURL = L10n.tr("Localizable", "addURL", fallback: "Add URL") - /// Administration + /// Administration Dashboard Section internal static let administration = L10n.tr("Localizable", "administration", fallback: "Administration") /// Advanced internal static let advanced = L10n.tr("Localizable", "advanced", fallback: "Advanced") @@ -36,11 +36,11 @@ internal enum L10n { internal static let allGenres = L10n.tr("Localizable", "allGenres", fallback: "All Genres") /// All Media internal static let allMedia = L10n.tr("Localizable", "allMedia", fallback: "All Media") - /// All Servers + /// Select Server View - Select All Servers internal static let allServers = L10n.tr("Localizable", "allServers", fallback: "All Servers") - /// Anamorphic video is not supported + /// TranscodeReason - Anamorphic Video Not Supported internal static let anamorphicVideoNotSupported = L10n.tr("Localizable", "anamorphicVideoNotSupported", fallback: "Anamorphic video is not supported") - /// Appearance + /// Represents the Appearance setting label internal static let appearance = L10n.tr("Localizable", "appearance", fallback: "Appearance") /// App Icon internal static let appIcon = L10n.tr("Localizable", "appIcon", fallback: "App Icon") @@ -52,27 +52,27 @@ internal enum L10n { internal static let audio = L10n.tr("Localizable", "audio", fallback: "Audio") /// Audio & Captions internal static let audioAndCaptions = L10n.tr("Localizable", "audioAndCaptions", fallback: "Audio & Captions") - /// The audio bit depth is not supported + /// TranscodeReason - Audio Bit Depth Not Supported internal static let audioBitDepthNotSupported = L10n.tr("Localizable", "audioBitDepthNotSupported", fallback: "The audio bit depth is not supported") - /// The audio bitrate is not supported + /// TranscodeReason - Audio Bitrate Not Supported internal static let audioBitrateNotSupported = L10n.tr("Localizable", "audioBitrateNotSupported", fallback: "The audio bitrate is not supported") - /// The number of audio channels is not supported + /// TranscodeReason - Audio Channels Not Supported internal static let audioChannelsNotSupported = L10n.tr("Localizable", "audioChannelsNotSupported", fallback: "The number of audio channels is not supported") - /// The audio codec is not supported + /// TranscodeReason - Audio Codec Not Supported internal static let audioCodecNotSupported = L10n.tr("Localizable", "audioCodecNotSupported", fallback: "The audio codec is not supported") - /// The audio track is external and requires transcoding + /// TranscodeReason - Audio Is External internal static let audioIsExternal = L10n.tr("Localizable", "audioIsExternal", fallback: "The audio track is external and requires transcoding") /// Audio Offset internal static let audioOffset = L10n.tr("Localizable", "audioOffset", fallback: "Audio Offset") - /// The audio profile is not supported + /// TranscodeReason - Audio Profile Not Supported internal static let audioProfileNotSupported = L10n.tr("Localizable", "audioProfileNotSupported", fallback: "The audio profile is not supported") - /// The audio sample rate is not supported + /// TranscodeReason - Audio Sample Rate Not Supported internal static let audioSampleRateNotSupported = L10n.tr("Localizable", "audioSampleRateNotSupported", fallback: "The audio sample rate is not supported") /// Audio Track internal static let audioTrack = L10n.tr("Localizable", "audioTrack", fallback: "Audio Track") /// Authorize internal static let authorize = L10n.tr("Localizable", "authorize", fallback: "Authorize") - /// Auto + /// PlaybackCompatibility Default Category internal static let auto = L10n.tr("Localizable", "auto", fallback: "Auto") /// Auto Play internal static let autoPlay = L10n.tr("Localizable", "autoPlay", fallback: "Auto Play") @@ -82,47 +82,47 @@ internal enum L10n { internal static let barButtons = L10n.tr("Localizable", "barButtons", fallback: "Bar Buttons") /// Behavior internal static let behavior = L10n.tr("Localizable", "behavior", fallback: "Behavior") - /// Auto + /// Option for automatic bitrate selection internal static let bitrateAuto = L10n.tr("Localizable", "bitrateAuto", fallback: "Auto") /// Default Bitrate internal static let bitrateDefault = L10n.tr("Localizable", "bitrateDefault", fallback: "Default Bitrate") - /// Limits the internet bandwidth used during video playback + /// Default Bitrate Description internal static let bitrateDefaultDescription = L10n.tr("Localizable", "bitrateDefaultDescription", fallback: "Limits the internet bandwidth used during video playback") - /// 480p - 1.5 Mbps + /// Option to set the bitrate to 480p quality at 1.5 Mbps internal static let bitrateKbps1500 = L10n.tr("Localizable", "bitrateKbps1500", fallback: "480p - 1.5 Mbps") - /// 360p - 420 Kbps + /// Option to set the bitrate to 360p quality at 420 Kbps internal static let bitrateKbps420 = L10n.tr("Localizable", "bitrateKbps420", fallback: "360p - 420 Kbps") - /// 480p - 720 Kbps + /// Option to set the bitrate to 480p quality at 720 Kbps internal static let bitrateKbps720 = L10n.tr("Localizable", "bitrateKbps720", fallback: "480p - 720 Kbps") - /// Maximum + /// Option for the maximum bitrate internal static let bitrateMax = L10n.tr("Localizable", "bitrateMax", fallback: "Maximum") - /// 1080p - 10 Mbps + /// Option to set the bitrate to 1080p quality at 10 Mbps internal static let bitrateMbps10 = L10n.tr("Localizable", "bitrateMbps10", fallback: "1080p - 10 Mbps") - /// 4K - 120 Mbps + /// Option to set the bitrate to 4K quality at 120 Mbps internal static let bitrateMbps120 = L10n.tr("Localizable", "bitrateMbps120", fallback: "4K - 120 Mbps") - /// 1080p - 15 Mbps + /// Option to set the bitrate to 1080p quality at 15 Mbps internal static let bitrateMbps15 = L10n.tr("Localizable", "bitrateMbps15", fallback: "1080p - 15 Mbps") - /// 1080p - 20 Mbps + /// Option to set the bitrate to 1080p quality at 20 Mbps internal static let bitrateMbps20 = L10n.tr("Localizable", "bitrateMbps20", fallback: "1080p - 20 Mbps") - /// 480p - 3 Mbps + /// Option to set the bitrate to 480p quality at 3 Mbps internal static let bitrateMbps3 = L10n.tr("Localizable", "bitrateMbps3", fallback: "480p - 3 Mbps") - /// 720p - 4 Mbps + /// Option to set the bitrate to 720p quality at 4 Mbps internal static let bitrateMbps4 = L10n.tr("Localizable", "bitrateMbps4", fallback: "720p - 4 Mbps") - /// 1080p - 40 Mbps + /// Option to set the bitrate to 1080p quality at 40 Mbps internal static let bitrateMbps40 = L10n.tr("Localizable", "bitrateMbps40", fallback: "1080p - 40 Mbps") - /// 720p - 6 Mbps + /// Option to set the bitrate to 720p quality at 6 Mbps internal static let bitrateMbps6 = L10n.tr("Localizable", "bitrateMbps6", fallback: "720p - 6 Mbps") - /// 1080p - 60 Mbps + /// Option to set the bitrate to 1080p quality at 60 Mbps internal static let bitrateMbps60 = L10n.tr("Localizable", "bitrateMbps60", fallback: "1080p - 60 Mbps") - /// 720p - 8 Mbps + /// Option to set the bitrate to 720p quality at 8 Mbps internal static let bitrateMbps8 = L10n.tr("Localizable", "bitrateMbps8", fallback: "720p - 8 Mbps") - /// 4K - 80 Mbps + /// Option to set the bitrate to 4K quality at 80 Mbps internal static let bitrateMbps80 = L10n.tr("Localizable", "bitrateMbps80", fallback: "4K - 80 Mbps") - /// Bitrate Test + /// Bitrate Automatic Section Header internal static let bitrateTest = L10n.tr("Localizable", "bitrateTest", fallback: "Bitrate Test") - /// Determines the length of the 'Auto' bitrate test used to find the available internet bandwidth + /// Description for bitrate test duration description internal static let bitrateTestDescription = L10n.tr("Localizable", "bitrateTestDescription", fallback: "Determines the length of the 'Auto' bitrate test used to find the available internet bandwidth") - /// Longer tests are more accurate but may result in a delayed playback + /// Description for bitrate test duration indicating longer tests provide more accurate bitrates but may delay playback internal static let bitrateTestDisclaimer = L10n.tr("Localizable", "bitrateTestDisclaimer", fallback: "Longer tests are more accurate but may result in a delayed playback") /// Blue internal static let blue = L10n.tr("Localizable", "blue", fallback: "Blue") @@ -132,9 +132,9 @@ internal enum L10n { internal static let buttons = L10n.tr("Localizable", "buttons", fallback: "Buttons") /// Cancel internal static let cancel = L10n.tr("Localizable", "cancel", fallback: "Cancel") - /// Cancelled + /// Task was Canceled internal static let canceled = L10n.tr("Localizable", "canceled", fallback: "Cancelled") - /// Cancelling... + /// Status label for when a task is cancelling internal static let cancelling = L10n.tr("Localizable", "cancelling", fallback: "Cancelling...") /// Cannot connect to host internal static let cannotConnectToHost = L10n.tr("Localizable", "cannotConnectToHost", fallback: "Cannot connect to host") @@ -142,7 +142,7 @@ internal enum L10n { internal static let cast = L10n.tr("Localizable", "cast", fallback: "CAST") /// Cast & Crew internal static let castAndCrew = L10n.tr("Localizable", "castAndCrew", fallback: "Cast & Crew") - /// Category + /// The category label for tasks internal static let category = L10n.tr("Localizable", "category", fallback: "Category") /// Change Server internal static let changeServer = L10n.tr("Localizable", "changeServer", fallback: "Change Server") @@ -154,11 +154,11 @@ internal enum L10n { internal static let chapterSlider = L10n.tr("Localizable", "chapterSlider", fallback: "Chapter Slider") /// Cinematic internal static let cinematic = L10n.tr("Localizable", "cinematic", fallback: "Cinematic") - /// Cinematic Background + /// Customize Server View - Cinematic Background internal static let cinematicBackground = L10n.tr("Localizable", "cinematicBackground", fallback: "Cinematic Background") /// Cinematic Views internal static let cinematicViews = L10n.tr("Localizable", "cinematicViews", fallback: "Cinematic Views") - /// Client + /// The client used for the session internal static let client = L10n.tr("Localizable", "client", fallback: "Client") /// Close internal static let close = L10n.tr("Localizable", "close", fallback: "Close") @@ -168,7 +168,7 @@ internal enum L10n { internal static let collections = L10n.tr("Localizable", "collections", fallback: "Collections") /// Color internal static let color = L10n.tr("Localizable", "color", fallback: "Color") - /// Columns + /// Section Title for Column Configuration internal static let columns = L10n.tr("Localizable", "columns", fallback: "Columns") /// Coming soon internal static let comingSoon = L10n.tr("Localizable", "comingSoon", fallback: "Coming soon") @@ -178,11 +178,11 @@ internal enum L10n { internal static let compactLogo = L10n.tr("Localizable", "compactLogo", fallback: "Compact Logo") /// Compact Poster internal static let compactPoster = L10n.tr("Localizable", "compactPoster", fallback: "Compact Poster") - /// Compatibility + /// PlaybackCompatibility Section Title internal static let compatibility = L10n.tr("Localizable", "compatibility", fallback: "Compatibility") - /// Most Compatible + /// PlaybackCompatibility Compatible Category internal static let compatible = L10n.tr("Localizable", "compatible", fallback: "Most Compatible") - /// Confirm + /// Confirm Task Fuction internal static let confirm = L10n.tr("Localizable", "confirm", fallback: "Confirm") /// Confirm Close internal static let confirmClose = L10n.tr("Localizable", "confirmClose", fallback: "Confirm Close") @@ -198,9 +198,9 @@ internal enum L10n { internal static let connectToJellyfinServerStart = L10n.tr("Localizable", "connectToJellyfinServerStart", fallback: "Connect to a Jellyfin server to get started") /// Connect to Server internal static let connectToServer = L10n.tr("Localizable", "connectToServer", fallback: "Connect to Server") - /// The container bitrate exceeds the allowed limit + /// TranscodeReason - Container Bitrate Exceeds Limit internal static let containerBitrateExceedsLimit = L10n.tr("Localizable", "containerBitrateExceedsLimit", fallback: "The container bitrate exceeds the allowed limit") - /// The container format is not supported + /// TranscodeReason - Container Not Supported internal static let containerNotSupported = L10n.tr("Localizable", "containerNotSupported", fallback: "The container format is not supported") /// Containers internal static let containers = L10n.tr("Localizable", "containers", fallback: "Containers") @@ -212,47 +212,47 @@ internal enum L10n { internal static let current = L10n.tr("Localizable", "current", fallback: "Current") /// Current Position internal static let currentPosition = L10n.tr("Localizable", "currentPosition", fallback: "Current Position") - /// Custom + /// PlaybackCompatibility Custom Category internal static let custom = L10n.tr("Localizable", "custom", fallback: "Custom") - /// The custom device profiles will be added to the default Swiftfin device profiles + /// Custom profile is Added to the Existing Profiles internal static let customDeviceProfileAdd = L10n.tr("Localizable", "customDeviceProfileAdd", fallback: "The custom device profiles will be added to the default Swiftfin device profiles") - /// Dictates back to the Jellyfin Server what this device hardware is capable of playing + /// Device Profile Section Description internal static let customDeviceProfileDescription = L10n.tr("Localizable", "customDeviceProfileDescription", fallback: "Dictates back to the Jellyfin Server what this device hardware is capable of playing") - /// The custom device profiles will replace the default Swiftfin device profiles + /// Custom profile will replace the Existing Profiles internal static let customDeviceProfileReplace = L10n.tr("Localizable", "customDeviceProfileReplace", fallback: "The custom device profiles will replace the default Swiftfin device profiles") - /// Customize + /// Settings View - Customize internal static let customize = L10n.tr("Localizable", "customize", fallback: "Customize") - /// Custom Profile + /// Section Header for a Custom Device Profile internal static let customProfile = L10n.tr("Localizable", "customProfile", fallback: "Custom Profile") - /// Dark + /// Represents the dark theme setting internal static let dark = L10n.tr("Localizable", "dark", fallback: "Dark") - /// Dashboard + /// UserDashboardView Header internal static let dashboard = L10n.tr("Localizable", "dashboard", fallback: "Dashboard") - /// Perform administrative tasks for your Jellyfin server. + /// Description for the dashboard section internal static let dashboardDescription = L10n.tr("Localizable", "dashboardDescription", fallback: "Perform administrative tasks for your Jellyfin server.") - /// Days - internal static let days = L10n.tr("Localizable", "days", fallback: "Days") + /// Time Interval Help Text - Days + internal static let days = L10n.tr("Localizable", "days", fallback: "days") /// Default Scheme internal static let defaultScheme = L10n.tr("Localizable", "defaultScheme", fallback: "Default Scheme") - /// Delete + /// Server Detail View - Delete internal static let delete = L10n.tr("Localizable", "delete", fallback: "Delete") - /// Delete Server + /// Server Detail View - Delete Server internal static let deleteServer = L10n.tr("Localizable", "deleteServer", fallback: "Delete Server") /// Delivery internal static let delivery = L10n.tr("Localizable", "delivery", fallback: "Delivery") - /// Device + /// Session Device Section Label internal static let device = L10n.tr("Localizable", "device", fallback: "Device") - /// Device Profile + /// Section Header for Device Profiles internal static let deviceProfile = L10n.tr("Localizable", "deviceProfile", fallback: "Device Profile") - /// Direct Play + /// PlaybackCompatibility DirectPlay Category internal static let direct = L10n.tr("Localizable", "direct", fallback: "Direct Play") /// DIRECTOR internal static let director = L10n.tr("Localizable", "director", fallback: "DIRECTOR") - /// Direct Play + /// PlayMethod - Direct Play internal static let directPlay = L10n.tr("Localizable", "directPlay", fallback: "Direct Play") - /// An error occurred during direct play + /// TranscodeReason - Direct Play Error internal static let directPlayError = L10n.tr("Localizable", "directPlayError", fallback: "An error occurred during direct play") - /// Direct Stream + /// PlayMethod - Direct Stream internal static let directStream = L10n.tr("Localizable", "directStream", fallback: "Direct Stream") /// Disabled internal static let disabled = L10n.tr("Localizable", "disabled", fallback: "Disabled") @@ -262,15 +262,15 @@ internal enum L10n { internal static let dismiss = L10n.tr("Localizable", "dismiss", fallback: "Dismiss") /// Display order internal static let displayOrder = L10n.tr("Localizable", "displayOrder", fallback: "Display order") - /// Done + /// Done - Completed, end, or save internal static let done = L10n.tr("Localizable", "done", fallback: "Done") /// Downloads internal static let downloads = L10n.tr("Localizable", "downloads", fallback: "Downloads") - /// Edit + /// Button label to edit a task internal static let edit = L10n.tr("Localizable", "edit", fallback: "Edit") /// Edit Jump Lengths internal static let editJumpLengths = L10n.tr("Localizable", "editJumpLengths", fallback: "Edit Jump Lengths") - /// Edit Server + /// Select Server View - Edit an Existing Server internal static let editServer = L10n.tr("Localizable", "editServer", fallback: "Edit Server") /// Empty Next Up internal static let emptyNextUp = L10n.tr("Localizable", "emptyNextUp", fallback: "Empty Next Up") @@ -302,7 +302,7 @@ internal enum L10n { internal static let filterResults = L10n.tr("Localizable", "filterResults", fallback: "Filter Results") /// Filters internal static let filters = L10n.tr("Localizable", "filters", fallback: "Filters") - /// %@fps + /// Transcode FPS internal static func fpsWithString(_ p1: Any) -> String { return L10n.tr("Localizable", "fpsWithString", String(describing: p1), fallback: "%@fps") } @@ -318,23 +318,23 @@ internal enum L10n { internal static let hapticFeedback = L10n.tr("Localizable", "hapticFeedback", fallback: "Haptic Feedback") /// Home internal static let home = L10n.tr("Localizable", "home", fallback: "Home") - /// Indicators + /// Customize Server View - Indicators internal static let indicators = L10n.tr("Localizable", "indicators", fallback: "Indicators") /// Information internal static let information = L10n.tr("Localizable", "information", fallback: "Information") - /// Interlaced video is not supported + /// TranscodeReason - Interlaced Video Not Supported internal static let interlacedVideoNotSupported = L10n.tr("Localizable", "interlacedVideoNotSupported", fallback: "Interlaced video is not supported") /// Inverted Dark internal static let invertedDark = L10n.tr("Localizable", "invertedDark", fallback: "Inverted Dark") /// Inverted Light internal static let invertedLight = L10n.tr("Localizable", "invertedLight", fallback: "Inverted Light") - /// %1$@ / %2$@ + /// SessionPlaybackMethod Remaining Time internal static func itemOverItem(_ p1: Any, _ p2: Any) -> String { return L10n.tr("Localizable", "itemOverItem", String(describing: p1), String(describing: p2), fallback: "%1$@ / %2$@") } /// Items internal static let items = L10n.tr("Localizable", "items", fallback: "Items") - /// Jellyfin + /// General internal static let jellyfin = L10n.tr("Localizable", "jellyfin", fallback: "Jellyfin") /// Jump internal static let jump = L10n.tr("Localizable", "jump", fallback: "Jump") @@ -358,13 +358,13 @@ internal enum L10n { internal static let larger = L10n.tr("Localizable", "larger", fallback: "Larger") /// Largest internal static let largest = L10n.tr("Localizable", "largest", fallback: "Largest") - /// Last run + /// The label for the last run time of a task internal static let lastRun = L10n.tr("Localizable", "lastRun", fallback: "Last run") - /// Last ran %@ + /// Last run message with time internal static func lastRunTime(_ p1: Any) -> String { return L10n.tr("Localizable", "lastRunTime", String(describing: p1), fallback: "Last ran %@") } - /// Last Seen + /// Session Client Last Seen Section Label internal static let lastSeen = L10n.tr("Localizable", "lastSeen", fallback: "Last Seen") /// Latest %@ internal static func latestWithString(_ p1: Any) -> String { @@ -376,7 +376,7 @@ internal enum L10n { internal static let letterPicker = L10n.tr("Localizable", "letterPicker", fallback: "Letter Picker") /// Library internal static let library = L10n.tr("Localizable", "library", fallback: "Library") - /// Light + /// Represents the light theme setting internal static let light = L10n.tr("Localizable", "light", fallback: "Light") /// List internal static let list = L10n.tr("Localizable", "list", fallback: "List") @@ -392,17 +392,17 @@ internal enum L10n { internal static func loginToWithString(_ p1: Any) -> String { return L10n.tr("Localizable", "loginToWithString", String(describing: p1), fallback: "Login to %@") } - /// Logs + /// Settings View - Logs internal static let logs = L10n.tr("Localizable", "logs", fallback: "Logs") - /// Maximum Bitrate + /// Option to set the maximum bitrate for playback internal static let maximumBitrate = L10n.tr("Localizable", "maximumBitrate", fallback: "Maximum Bitrate") - /// This setting may result in media failing to start playback + /// Playback May Fail internal static let mayResultInPlaybackFailure = L10n.tr("Localizable", "mayResultInPlaybackFailure", fallback: "This setting may result in media failing to start playback") /// Media internal static let media = L10n.tr("Localizable", "media", fallback: "Media") /// Menu Buttons internal static let menuButtons = L10n.tr("Localizable", "menuButtons", fallback: "Menu Buttons") - /// Method + /// The play method (e.g., Direct Play, Transcoding) internal static let method = L10n.tr("Localizable", "method", fallback: "Method") /// Missing internal static let missing = L10n.tr("Localizable", "missing", fallback: "Missing") @@ -424,7 +424,7 @@ internal enum L10n { internal static let networking = L10n.tr("Localizable", "networking", fallback: "Networking") /// Network timed out internal static let networkTimedOut = L10n.tr("Localizable", "networkTimedOut", fallback: "Network timed out") - /// Never run + /// Message shown when a task has never run internal static let neverRun = L10n.tr("Localizable", "neverRun", fallback: "Never run") /// News internal static let news = L10n.tr("Localizable", "news", fallback: "News") @@ -434,11 +434,11 @@ internal enum L10n { internal static let nextItem = L10n.tr("Localizable", "nextItem", fallback: "Next Item") /// Next Up internal static let nextUp = L10n.tr("Localizable", "nextUp", fallback: "Next Up") - /// Days in Next Up + /// Settings Description for the day limit in Next Up internal static let nextUpDays = L10n.tr("Localizable", "nextUpDays", fallback: "Days in Next Up") - /// Set the maximum amount of days a show should stay in the 'Next Up' list without watching it. Set the value to 0 to disable. - internal static let nextUpDaysDescription = L10n.tr("Localizable", "nextUpDaysDescription", fallback: "Set the maximum amount of days a show should stay in the 'Next Up' list without watching it. Set the value to 0 to disable.") - /// Rewatching in Next Up + /// Description for how the nextUpDays setting works + internal static let nextUpDaysDescription = L10n.tr("Localizable", "nextUpDaysDescription", fallback: "Set the maximum amount of days a show should stay in the 'Next Up' list without watching it.") + /// Settings Description for enabling rewatching in Next Up internal static let nextUpRewatch = L10n.tr("Localizable", "nextUpRewatch", fallback: "Rewatching in Next Up") /// No Cast devices found.. internal static let noCastdevicesfound = L10n.tr("Localizable", "noCastdevicesfound", fallback: "No Cast devices found..") @@ -458,7 +458,7 @@ internal enum L10n { internal static let noResults = L10n.tr("Localizable", "noResults", fallback: "No results.") /// Normal internal static let normal = L10n.tr("Localizable", "normal", fallback: "Normal") - /// No session + /// No active session available internal static let noSession = L10n.tr("Localizable", "noSession", fallback: "No session") /// N/A internal static let notAvailableSlash = L10n.tr("Localizable", "notAvailableSlash", fallback: "N/A") @@ -468,13 +468,13 @@ internal enum L10n { } /// No title internal static let noTitle = L10n.tr("Localizable", "noTitle", fallback: "No title") - /// Offset + /// Video Player Settings View - Offset internal static let offset = L10n.tr("Localizable", "offset", fallback: "Offset") /// Ok internal static let ok = L10n.tr("Localizable", "ok", fallback: "Ok") /// 1 user internal static let oneUser = L10n.tr("Localizable", "oneUser", fallback: "1 user") - /// Online + /// Indicates that something is Online internal static let online = L10n.tr("Localizable", "online", fallback: "Online") /// On Now internal static let onNow = L10n.tr("Localizable", "onNow", fallback: "On Now") @@ -502,7 +502,7 @@ internal enum L10n { } /// Password internal static let password = L10n.tr("Localizable", "password", fallback: "Password") - /// Pause on background + /// Video Player Settings View - Pause on Background internal static let pauseOnBackground = L10n.tr("Localizable", "pauseOnBackground", fallback: "Pause on background") /// People internal static let people = L10n.tr("Localizable", "people", fallback: "People") @@ -510,11 +510,11 @@ internal enum L10n { internal static let play = L10n.tr("Localizable", "play", fallback: "Play") /// Play / Pause internal static let playAndPause = L10n.tr("Localizable", "playAndPause", fallback: "Play / Pause") - /// Playback + /// Video Player Settings View - Playback Header internal static let playback = L10n.tr("Localizable", "playback", fallback: "Playback") /// Playback Buttons internal static let playbackButtons = L10n.tr("Localizable", "playbackButtons", fallback: "Playback Buttons") - /// Playback Quality + /// Section for Playback Quality Settings internal static let playbackQuality = L10n.tr("Localizable", "playbackQuality", fallback: "Playback Quality") /// Playback settings internal static let playbackSettings = L10n.tr("Localizable", "playbackSettings", fallback: "Playback settings") @@ -530,11 +530,11 @@ internal enum L10n { internal static let playNext = L10n.tr("Localizable", "playNext", fallback: "Play Next") /// Play Next Item internal static let playNextItem = L10n.tr("Localizable", "playNextItem", fallback: "Play Next Item") - /// Play on active + /// Video Player Settings View - Play on Active internal static let playOnActive = L10n.tr("Localizable", "playOnActive", fallback: "Play on active") /// Play Previous Item internal static let playPreviousItem = L10n.tr("Localizable", "playPreviousItem", fallback: "Play Previous Item") - /// Posters + /// Customize Server View - Posters internal static let posters = L10n.tr("Localizable", "posters", fallback: "Posters") /// Present internal static let present = L10n.tr("Localizable", "present", fallback: "Present") @@ -544,7 +544,7 @@ internal enum L10n { internal static let previousItem = L10n.tr("Localizable", "previousItem", fallback: "Previous Item") /// Primary internal static let primary = L10n.tr("Localizable", "primary", fallback: "Primary") - /// Profiles + /// PlaybackCompatibility Profile Sections internal static let profiles = L10n.tr("Localizable", "profiles", fallback: "Profiles") /// Programs internal static let programs = L10n.tr("Localizable", "programs", fallback: "Programs") @@ -570,7 +570,7 @@ internal enum L10n { internal static let quickConnectSuccessMessage = L10n.tr("Localizable", "quickConnectSuccessMessage", fallback: "Authorizing Quick Connect successful. Please continue on your other device.") /// Random internal static let random = L10n.tr("Localizable", "random", fallback: "Random") - /// Random Image + /// Customize Server View - Random Image internal static let randomImage = L10n.tr("Localizable", "randomImage", fallback: "Random Image") /// Rated internal static let rated = L10n.tr("Localizable", "rated", fallback: "Rated") @@ -582,7 +582,7 @@ internal enum L10n { internal static let recommended = L10n.tr("Localizable", "recommended", fallback: "Recommended") /// Red internal static let red = L10n.tr("Localizable", "red", fallback: "Red") - /// The number of reference frames is not supported + /// TranscodeReason - Reference Frames Not Supported internal static let refFramesNotSupported = L10n.tr("Localizable", "refFramesNotSupported", fallback: "The number of reference frames is not supported") /// Refresh internal static let refresh = L10n.tr("Localizable", "refresh", fallback: "Refresh") @@ -602,6 +602,8 @@ internal enum L10n { internal static let removeAllUsers = L10n.tr("Localizable", "removeAllUsers", fallback: "Remove All Users") /// Remove From Resume internal static let removeFromResume = L10n.tr("Localizable", "removeFromResume", fallback: "Remove From Resume") + /// PlayMethod - Remux + internal static let remux = L10n.tr("Localizable", "remux", fallback: "Remux") /// Report an Issue internal static let reportIssue = L10n.tr("Localizable", "reportIssue", fallback: "Report an Issue") /// Request a Feature @@ -614,19 +616,19 @@ internal enum L10n { internal static let resetAppSettings = L10n.tr("Localizable", "resetAppSettings", fallback: "Reset App Settings") /// Reset User Settings internal static let resetUserSettings = L10n.tr("Localizable", "resetUserSettings", fallback: "Reset User Settings") - /// Restart Server + /// Restart Server Label internal static let restartServer = L10n.tr("Localizable", "restartServer", fallback: "Restart Server") - /// Are you sure you want to restart the server? + /// Restart Warning Label internal static let restartWarning = L10n.tr("Localizable", "restartWarning", fallback: "Are you sure you want to restart the server?") - /// Resume + /// Video Player Settings View - Resume internal static let resume = L10n.tr("Localizable", "resume", fallback: "Resume") /// Resume 5 Second Offset internal static let resume5SecondOffset = L10n.tr("Localizable", "resume5SecondOffset", fallback: "Resume 5 Second Offset") /// Resume Offset internal static let resumeOffset = L10n.tr("Localizable", "resumeOffset", fallback: "Resume Offset") - /// Resume content seconds before the recorded resume time + /// Video Player Settings View - Resume Offset Description internal static let resumeOffsetDescription = L10n.tr("Localizable", "resumeOffsetDescription", fallback: "Resume content seconds before the recorded resume time") - /// Resume Offset + /// Video Player Settings View - Resume Offset Title internal static let resumeOffsetTitle = L10n.tr("Localizable", "resumeOffsetTitle", fallback: "Resume Offset") /// Retrieving media information internal static let retrievingMediaInformation = L10n.tr("Localizable", "retrievingMediaInformation", fallback: "Retrieving media information") @@ -634,17 +636,17 @@ internal enum L10n { internal static let retry = L10n.tr("Localizable", "retry", fallback: "Retry") /// Right internal static let `right` = L10n.tr("Localizable", "right", fallback: "Right") - /// Run + /// Button label to run a task internal static let run = L10n.tr("Localizable", "run", fallback: "Run") - /// Running... + /// Status label for when a task is running internal static let running = L10n.tr("Localizable", "running", fallback: "Running...") /// Runtime internal static let runtime = L10n.tr("Localizable", "runtime", fallback: "Runtime") - /// Save + /// Save - Completed, end, or save internal static let save = L10n.tr("Localizable", "save", fallback: "Save") - /// Scan All Libraries + /// Administration Dashboard Scan All Libraries Button internal static let scanAllLibraries = L10n.tr("Localizable", "scanAllLibraries", fallback: "Scan All Libraries") - /// Scheduled Tasks + /// Administration Dashboard Scheduled Tasks internal static let scheduledTasks = L10n.tr("Localizable", "scheduledTasks", fallback: "Scheduled Tasks") /// Scrub Current Time internal static let scrubCurrentTime = L10n.tr("Localizable", "scrubCurrentTime", fallback: "Scrub Current Time") @@ -662,7 +664,7 @@ internal enum L10n { } /// Seasons internal static let seasons = L10n.tr("Localizable", "seasons", fallback: "Seasons") - /// Secondary audio is not supported + /// TranscodeReason - Secondary Audio Not Supported internal static let secondaryAudioNotSupported = L10n.tr("Localizable", "secondaryAudioNotSupported", fallback: "Secondary audio is not supported") /// See All internal static let seeAll = L10n.tr("Localizable", "seeAll", fallback: "See All") @@ -690,13 +692,13 @@ internal enum L10n { internal static let serverDetails = L10n.tr("Localizable", "serverDetails", fallback: "Server Details") /// Server Information internal static let serverInformation = L10n.tr("Localizable", "serverInformation", fallback: "Server Information") - /// Server Logs + /// Title for the server logs section internal static let serverLogs = L10n.tr("Localizable", "serverLogs", fallback: "Server Logs") - /// Servers + /// Select Server View internal static let servers = L10n.tr("Localizable", "servers", fallback: "Servers") /// Server URL internal static let serverURL = L10n.tr("Localizable", "serverURL", fallback: "Server URL") - /// Session + /// The title for the session view internal static let session = L10n.tr("Localizable", "session", fallback: "Session") /// Settings internal static let settings = L10n.tr("Localizable", "settings", fallback: "Settings") @@ -704,9 +706,9 @@ internal enum L10n { internal static let showCastAndCrew = L10n.tr("Localizable", "showCastAndCrew", fallback: "Show Cast & Crew") /// Show Chapters Info In Bottom Overlay internal static let showChaptersInfoInBottomOverlay = L10n.tr("Localizable", "showChaptersInfoInBottomOverlay", fallback: "Show Chapters Info In Bottom Overlay") - /// Show Favorited + /// Indicators View - Show Favorited internal static let showFavorited = L10n.tr("Localizable", "showFavorited", fallback: "Show Favorited") - /// Show Favorites + /// Customize Server View - Show Favorites internal static let showFavorites = L10n.tr("Localizable", "showFavorites", fallback: "Show Favorites") /// Flatten Library Items internal static let showFlattenView = L10n.tr("Localizable", "showFlattenView", fallback: "Flatten Library Items") @@ -716,17 +718,17 @@ internal enum L10n { internal static let showMissingSeasons = L10n.tr("Localizable", "showMissingSeasons", fallback: "Show Missing Seasons") /// Show Poster Labels internal static let showPosterLabels = L10n.tr("Localizable", "showPosterLabels", fallback: "Show Poster Labels") - /// Show Progress + /// Indicators View - Show Progress internal static let showProgress = L10n.tr("Localizable", "showProgress", fallback: "Show Progress") - /// Show Recently Added + /// Customize Server View - Show Recently Added internal static let showRecentlyAdded = L10n.tr("Localizable", "showRecentlyAdded", fallback: "Show Recently Added") - /// Show Unwatched + /// Indicators View - Show Unwatched internal static let showUnwatched = L10n.tr("Localizable", "showUnwatched", fallback: "Show Unwatched") - /// Show Watched + /// Indicators View - Show Watched internal static let showWatched = L10n.tr("Localizable", "showWatched", fallback: "Show Watched") - /// Shutdown Server + /// Shutdown Server Label internal static let shutdownServer = L10n.tr("Localizable", "shutdownServer", fallback: "Shutdown Server") - /// Are you sure you want to shutdown the server? + /// Shutdown Warning Label internal static let shutdownWarning = L10n.tr("Localizable", "shutdownWarning", fallback: "Are you sure you want to shutdown the server?") /// Signed in as %@ internal static func signedInAsWithString(_ p1: Any) -> String { @@ -760,9 +762,9 @@ internal enum L10n { internal static let specialFeatures = L10n.tr("Localizable", "specialFeatures", fallback: "Special Features") /// Sports internal static let sports = L10n.tr("Localizable", "sports", fallback: "Sports") - /// Stop + /// Button label to stop a task internal static let stop = L10n.tr("Localizable", "stop", fallback: "Stop") - /// Streams + /// Session Streaming Clients internal static let streams = L10n.tr("Localizable", "streams", fallback: "Streams") /// STUDIO internal static let studio = L10n.tr("Localizable", "studio", fallback: "STUDIO") @@ -770,7 +772,7 @@ internal enum L10n { internal static let studios = L10n.tr("Localizable", "studios", fallback: "Studios") /// Subtitle internal static let subtitle = L10n.tr("Localizable", "subtitle", fallback: "Subtitle") - /// The subtitle codec is not supported + /// TranscodeReason - Subtitle Codec Not Supported internal static let subtitleCodecNotSupported = L10n.tr("Localizable", "subtitleCodecNotSupported", fallback: "The subtitle codec is not supported") /// Subtitle Color internal static let subtitleColor = L10n.tr("Localizable", "subtitleColor", fallback: "Subtitle Color") @@ -780,7 +782,7 @@ internal enum L10n { internal static let subtitleOffset = L10n.tr("Localizable", "subtitleOffset", fallback: "Subtitle Offset") /// Subtitles internal static let subtitles = L10n.tr("Localizable", "subtitles", fallback: "Subtitles") - /// Settings only affect some subtitle types + /// Video Player Settings View - Disclaimer internal static let subtitlesDisclaimer = L10n.tr("Localizable", "subtitlesDisclaimer", fallback: "Settings only affect some subtitle types") /// Subtitle Size internal static let subtitleSize = L10n.tr("Localizable", "subtitleSize", fallback: "Subtitle Size") @@ -788,27 +790,27 @@ internal enum L10n { internal static let suggestions = L10n.tr("Localizable", "suggestions", fallback: "Suggestions") /// Switch User internal static let switchUser = L10n.tr("Localizable", "switchUser", fallback: "Switch User") - /// System + /// Represents the system theme setting internal static let system = L10n.tr("Localizable", "system", fallback: "System") /// System Control Gestures Enabled internal static let systemControlGesturesEnabled = L10n.tr("Localizable", "systemControlGesturesEnabled", fallback: "System Control Gestures Enabled") /// Tags internal static let tags = L10n.tr("Localizable", "tags", fallback: "Tags") - /// Task + /// The navigation title for the task view internal static let task = L10n.tr("Localizable", "task", fallback: "Task") - /// Aborted + /// Status message for an aborted task internal static let taskAborted = L10n.tr("Localizable", "taskAborted", fallback: "Aborted") - /// Cancelled + /// Status message for a cancelled task internal static let taskCancelled = L10n.tr("Localizable", "taskCancelled", fallback: "Cancelled") - /// Completed + /// Status message for a completed task internal static let taskCompleted = L10n.tr("Localizable", "taskCompleted", fallback: "Completed") - /// Failed + /// Status message for a failed task internal static let taskFailed = L10n.tr("Localizable", "taskFailed", fallback: "Failed") - /// Tasks + /// Title for the tasks section internal static let tasks = L10n.tr("Localizable", "tasks", fallback: "Tasks") - /// Tasks are operations that are scheduled to run periodically or can be triggered manually. + /// Description for the tasks section internal static let tasksDescription = L10n.tr("Localizable", "tasksDescription", fallback: "Tasks are operations that are scheduled to run periodically or can be triggered manually.") - /// Test Size + /// Option to set the test size for bitrate testing internal static let testSize = L10n.tr("Localizable", "testSize", fallback: "Test Size") /// Timestamp internal static let timestamp = L10n.tr("Localizable", "timestamp", fallback: "Timestamp") @@ -818,9 +820,9 @@ internal enum L10n { internal static let tooManyRedirects = L10n.tr("Localizable", "tooManyRedirects", fallback: "Too Many Redirects") /// Trailing Value internal static let trailingValue = L10n.tr("Localizable", "trailingValue", fallback: "Trailing Value") - /// Transcode + /// PlayMethod - Transcode internal static let transcode = L10n.tr("Localizable", "transcode", fallback: "Transcode") - /// Transcode Reason(s) + /// Transcode Reason(s) Section Label internal static let transcodeReasons = L10n.tr("Localizable", "transcodeReasons", fallback: "Transcode Reason(s)") /// Transition internal static let transition = L10n.tr("Localizable", "transition", fallback: "Transition") @@ -840,17 +842,17 @@ internal enum L10n { internal static let unauthorizedUser = L10n.tr("Localizable", "unauthorizedUser", fallback: "Unauthorized user") /// Unknown internal static let unknown = L10n.tr("Localizable", "unknown", fallback: "Unknown") - /// The audio stream information is unknown + /// TranscodeReason - Unknown Audio Stream Info internal static let unknownAudioStreamInfo = L10n.tr("Localizable", "unknownAudioStreamInfo", fallback: "The audio stream information is unknown") /// Unknown Error internal static let unknownError = L10n.tr("Localizable", "unknownError", fallback: "Unknown Error") - /// The video stream information is unknown + /// TranscodeReason - Unknown Video Stream Info internal static let unknownVideoStreamInfo = L10n.tr("Localizable", "unknownVideoStreamInfo", fallback: "The video stream information is unknown") /// Unplayed internal static let unplayed = L10n.tr("Localizable", "unplayed", fallback: "Unplayed") /// URL internal static let url = L10n.tr("Localizable", "url", fallback: "URL") - /// Use as Transcoding Profile + /// Override Transcoding Profile internal static let useAsTranscodingProfile = L10n.tr("Localizable", "useAsTranscodingProfile", fallback: "Use as Transcoding Profile") /// Use Primary Image internal static let usePrimaryImage = L10n.tr("Localizable", "usePrimaryImage", fallback: "Use Primary Image") @@ -868,25 +870,25 @@ internal enum L10n { internal static let version = L10n.tr("Localizable", "version", fallback: "Version") /// Video internal static let video = L10n.tr("Localizable", "video", fallback: "Video") - /// The video bit depth is not supported + /// TranscodeReason - Video Bit Depth Not Supported internal static let videoBitDepthNotSupported = L10n.tr("Localizable", "videoBitDepthNotSupported", fallback: "The video bit depth is not supported") - /// The video bitrate is not supported + /// TranscodeReason - Video Bitrate Not Supported internal static let videoBitrateNotSupported = L10n.tr("Localizable", "videoBitrateNotSupported", fallback: "The video bitrate is not supported") - /// The video codec is not supported + /// TranscodeReason - Video Codec Not Supported internal static let videoCodecNotSupported = L10n.tr("Localizable", "videoCodecNotSupported", fallback: "The video codec is not supported") - /// The video framerate is not supported + /// TranscodeReason - Video Framerate Not Supported internal static let videoFramerateNotSupported = L10n.tr("Localizable", "videoFramerateNotSupported", fallback: "The video framerate is not supported") - /// The video level is not supported + /// TranscodeReason - Video Level Not Supported internal static let videoLevelNotSupported = L10n.tr("Localizable", "videoLevelNotSupported", fallback: "The video level is not supported") - /// Video Player + /// Settings View - Video Player internal static let videoPlayer = L10n.tr("Localizable", "videoPlayer", fallback: "Video Player") /// Video Player Type internal static let videoPlayerType = L10n.tr("Localizable", "videoPlayerType", fallback: "Video Player Type") - /// The video profile is not supported + /// TranscodeReason - Video Profile Not Supported internal static let videoProfileNotSupported = L10n.tr("Localizable", "videoProfileNotSupported", fallback: "The video profile is not supported") - /// The video range type is not supported + /// TranscodeReason - Video Range Type Not Supported internal static let videoRangeTypeNotSupported = L10n.tr("Localizable", "videoRangeTypeNotSupported", fallback: "The video range type is not supported") - /// The video resolution is not supported + /// TranscodeReason - Video Resolution Not Supported internal static let videoResolutionNotSupported = L10n.tr("Localizable", "videoResolutionNotSupported", fallback: "The video resolution is not supported") /// Who's watching? internal static let whosWatching = L10n.tr("Localizable", "WhosWatching", fallback: "Who's watching?") diff --git a/Swiftfin.xcodeproj/project.pbxproj b/Swiftfin.xcodeproj/project.pbxproj index ea6ddfa7..9a503973 100644 --- a/Swiftfin.xcodeproj/project.pbxproj +++ b/Swiftfin.xcodeproj/project.pbxproj @@ -74,6 +74,8 @@ 4EC6C16B2C92999800FC904B /* TranscodeSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EC6C16A2C92999800FC904B /* TranscodeSection.swift */; }; 4ECDAA9E2C920A8E0030F2F5 /* TranscodeReason.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ECDAA9D2C920A8E0030F2F5 /* TranscodeReason.swift */; }; 4ECDAA9F2C920A8E0030F2F5 /* TranscodeReason.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ECDAA9D2C920A8E0030F2F5 /* TranscodeReason.swift */; }; + 4EDBDCD12CBDD6590033D347 /* SessionInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EDBDCD02CBDD6510033D347 /* SessionInfo.swift */; }; + 4EDBDCD22CBDD6590033D347 /* SessionInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EDBDCD02CBDD6510033D347 /* SessionInfo.swift */; }; 4EE141692C8BABDF0045B661 /* ProgressSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE141682C8BABDF0045B661 /* ProgressSection.swift */; }; 4EF18B262CB9934C00343666 /* LibraryRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EF18B252CB9934700343666 /* LibraryRow.swift */; }; 4EF18B282CB9936D00343666 /* ListColumnsPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EF18B272CB9936400343666 /* ListColumnsPickerView.swift */; }; @@ -1066,6 +1068,7 @@ 4EC50D602C934B3A00FC3D0E /* ScheduledTasksViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduledTasksViewModel.swift; sourceTree = ""; }; 4EC6C16A2C92999800FC904B /* TranscodeSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranscodeSection.swift; sourceTree = ""; }; 4ECDAA9D2C920A8E0030F2F5 /* TranscodeReason.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranscodeReason.swift; sourceTree = ""; }; + 4EDBDCD02CBDD6510033D347 /* SessionInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionInfo.swift; sourceTree = ""; }; 4EE141682C8BABDF0045B661 /* ProgressSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressSection.swift; sourceTree = ""; }; 4EF18B252CB9934700343666 /* LibraryRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryRow.swift; sourceTree = ""; }; 4EF18B272CB9936400343666 /* ListColumnsPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListColumnsPickerView.swift; sourceTree = ""; }; @@ -3576,8 +3579,9 @@ E1F5F9B12BA0200500BA5014 /* MediaSourceInfo */, E122A9122788EAAD0060FA63 /* MediaStream.swift */, E1AD105E26D9ADDD003E4A08 /* NameGuidPair.swift */, - E148128428C15472003B8787 /* SortOrder+ItemSortOrder.swift */, 4E2182E42CAF67EF0094806B /* PlayMethod.swift */, + 4EDBDCD02CBDD6510033D347 /* SessionInfo.swift */, + E148128428C15472003B8787 /* SortOrder+ItemSortOrder.swift */, E1DA654B28E69B0500592A73 /* SpecialFeatureType.swift */, E1CB757E2C80F28F00217C76 /* SubtitleProfile.swift */, 4ECDAA9D2C920A8E0030F2F5 /* TranscodeReason.swift */, @@ -4465,6 +4469,7 @@ E11B1B6D2718CD68006DA3E8 /* JellyfinAPIError.swift in Sources */, E12CC1C928D132B800678D5D /* RecentlyAddedView.swift in Sources */, E19D41B32BF2BFEF0082B8B2 /* URLSessionConfiguration.swift in Sources */, + 4EDBDCD12CBDD6590033D347 /* SessionInfo.swift in Sources */, E10B1ECE2BD9AFD800A92EAF /* SwiftfinStore+V2.swift in Sources */, E150C0BE2BFD45BD00944FFA /* RedrawOnNotificationView.swift in Sources */, E1763A722BF3F67C004DF6AB /* SwiftfinStore+Mappings.swift in Sources */, @@ -4946,6 +4951,7 @@ E1D4BF812719D22800A11E64 /* AppAppearance.swift in Sources */, E1BDF2EF29522A5900CC0294 /* AudioActionButton.swift in Sources */, E174120F29AE9D94003EF3B5 /* NavigationCoordinatable.swift in Sources */, + 4EDBDCD22CBDD6590033D347 /* SessionInfo.swift in Sources */, E10231392BCF8A3C009D71FC /* ProgramButtonContent.swift in Sources */, E1DC9844296DECB600982F06 /* ProgressIndicator.swift in Sources */, 6220D0B126D5EC9900B8E046 /* SettingsCoordinator.swift in Sources */, diff --git a/Swiftfin/Views/SettingsView/UserDashboardView/ActiveSessionDetailView/ActiveSessionDetailView.swift b/Swiftfin/Views/SettingsView/UserDashboardView/ActiveSessionDetailView/ActiveSessionDetailView.swift index 3b985b72..806546a0 100644 --- a/Swiftfin/Views/SettingsView/UserDashboardView/ActiveSessionDetailView/ActiveSessionDetailView.swift +++ b/Swiftfin/Views/SettingsView/UserDashboardView/ActiveSessionDetailView/ActiveSessionDetailView.swift @@ -107,8 +107,8 @@ struct ActiveSessionDetailView: View { // TODO: allow showing item stream details? // TODO: don't show codec changes on direct play? Section(L10n.streams) { - if let playMethod = playState.playMethod { - TextPairView(leading: L10n.method, trailing: playMethod.displayTitle) + if let playMethodDisplayTitle = session.playMethodDisplayTitle { + TextPairView(leading: L10n.method, trailing: playMethodDisplayTitle) } StreamSection( diff --git a/Translations/en.lproj/Localizable.strings b/Translations/en.lproj/Localizable.strings index 7233f3b4607e9b400d9b2f14b22919627f9cbb1d..50941eb5924ca7676dd07c19013987024406f9f9 100644 GIT binary patch literal 22646 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 literal 45276 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 bhP8*@q?Dpw~