[iOS] Playback Quality - Learn More (#1316)
* Playback Quality - Learn More * TODO: Fix leading not working on second line. * Remove layoutDirection. * Implement for tvOS. Slightly different spacing. * VStack * WIP - tvOS Implementaiton. SUBJECT TO CHANGE / ELIMINATION. * Background Icon & formatting * wip * Review Changes. Remove unused Strings, clean up comments. * Remove duplicate items used for testing * Remove tvOS scrollIfLargerThanContainer for now. --------- Co-authored-by: Ethan Pippin <ethanpippin2343@gmail.com>
This commit is contained in:
parent
687cfa6b5f
commit
994e99d141
|
@ -129,3 +129,29 @@ extension FormatStyle where Self == LastSeenFormatStyle {
|
||||||
|
|
||||||
static var lastSeen: LastSeenFormatStyle { LastSeenFormatStyle() }
|
static var lastSeen: LastSeenFormatStyle { LastSeenFormatStyle() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct IntBitRateFormatStyle: FormatStyle {
|
||||||
|
func format(_ value: Int) -> String {
|
||||||
|
let units = [
|
||||||
|
L10n.bitsPerSecond,
|
||||||
|
L10n.kilobitsPerSecond,
|
||||||
|
L10n.megabitsPerSecond,
|
||||||
|
L10n.gigabitsPerSecond,
|
||||||
|
L10n.terabitsPerSecond,
|
||||||
|
]
|
||||||
|
var adjustedValue = Double(value)
|
||||||
|
var unitIndex = 0
|
||||||
|
|
||||||
|
while adjustedValue >= 1000, unitIndex < units.count - 1 {
|
||||||
|
adjustedValue /= 1000
|
||||||
|
unitIndex += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
let formattedValue = String(format: "%.1f", adjustedValue)
|
||||||
|
return "\(formattedValue) \(units[unitIndex])"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension FormatStyle where Self == IntBitRateFormatStyle {
|
||||||
|
static var bitRate: IntBitRateFormatStyle { IntBitRateFormatStyle() }
|
||||||
|
}
|
||||||
|
|
|
@ -108,6 +108,8 @@ internal enum L10n {
|
||||||
internal static let authorize = L10n.tr("Localizable", "authorize", fallback: "Authorize")
|
internal static let authorize = L10n.tr("Localizable", "authorize", fallback: "Authorize")
|
||||||
/// PlaybackCompatibility Default Category
|
/// PlaybackCompatibility Default Category
|
||||||
internal static let auto = L10n.tr("Localizable", "auto", fallback: "Auto")
|
internal static let auto = L10n.tr("Localizable", "auto", fallback: "Auto")
|
||||||
|
/// Optimizes playback using default settings for most devices. Some formats may require server transcoding for non-compatible media types.
|
||||||
|
internal static let autoDescription = L10n.tr("Localizable", "autoDescription", fallback: "Optimizes playback using default settings for most devices. Some formats may require server transcoding for non-compatible media types.")
|
||||||
/// Auto Play
|
/// Auto Play
|
||||||
internal static let autoPlay = L10n.tr("Localizable", "autoPlay", fallback: "Auto Play")
|
internal static let autoPlay = L10n.tr("Localizable", "autoPlay", fallback: "Auto Play")
|
||||||
/// Back
|
/// Back
|
||||||
|
@ -116,12 +118,14 @@ internal enum L10n {
|
||||||
internal static let barButtons = L10n.tr("Localizable", "barButtons", fallback: "Bar Buttons")
|
internal static let barButtons = L10n.tr("Localizable", "barButtons", fallback: "Bar Buttons")
|
||||||
/// Behavior
|
/// Behavior
|
||||||
internal static let behavior = L10n.tr("Localizable", "behavior", fallback: "Behavior")
|
internal static let behavior = L10n.tr("Localizable", "behavior", fallback: "Behavior")
|
||||||
|
/// Tests your server connection to assess internet speed and adjust bandwidth automatically.
|
||||||
|
internal static let birateAutoDescription = L10n.tr("Localizable", "birateAutoDescription", fallback: "Tests your server connection to assess internet speed and adjust bandwidth automatically.")
|
||||||
/// Option for automatic bitrate selection
|
/// Option for automatic bitrate selection
|
||||||
internal static let bitrateAuto = L10n.tr("Localizable", "bitrateAuto", fallback: "Auto")
|
internal static let bitrateAuto = L10n.tr("Localizable", "bitrateAuto", fallback: "Auto")
|
||||||
/// Default Bitrate
|
/// Default Bitrate
|
||||||
internal static let bitrateDefault = L10n.tr("Localizable", "bitrateDefault", fallback: "Default Bitrate")
|
internal static let bitrateDefault = L10n.tr("Localizable", "bitrateDefault", fallback: "Default Bitrate")
|
||||||
/// Default Bitrate Description
|
/// Default Bitrate Description
|
||||||
internal static let bitrateDefaultDescription = L10n.tr("Localizable", "bitrateDefaultDescription", fallback: "Limits the internet bandwidth used during video playback")
|
internal static let bitrateDefaultDescription = L10n.tr("Localizable", "bitrateDefaultDescription", fallback: "Limits the internet bandwidth used during playback.")
|
||||||
/// Option to set the bitrate to 480p quality at 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")
|
internal static let bitrateKbps1500 = L10n.tr("Localizable", "bitrateKbps1500", fallback: "480p - 1.5 Mbps")
|
||||||
/// Option to set the bitrate to 360p quality at 420 Kbps
|
/// Option to set the bitrate to 360p quality at 420 Kbps
|
||||||
|
@ -130,6 +134,10 @@ internal enum L10n {
|
||||||
internal static let bitrateKbps720 = L10n.tr("Localizable", "bitrateKbps720", fallback: "480p - 720 Kbps")
|
internal static let bitrateKbps720 = L10n.tr("Localizable", "bitrateKbps720", fallback: "480p - 720 Kbps")
|
||||||
/// Option for the maximum bitrate
|
/// Option for the maximum bitrate
|
||||||
internal static let bitrateMax = L10n.tr("Localizable", "bitrateMax", fallback: "Maximum")
|
internal static let bitrateMax = L10n.tr("Localizable", "bitrateMax", fallback: "Maximum")
|
||||||
|
/// Maximizes bandwidth usage, up to %@, for each playback stream to ensure the highest quality.
|
||||||
|
internal static func bitrateMaxDescription(_ p1: Any) -> String {
|
||||||
|
return L10n.tr("Localizable", "bitrateMaxDescription", String(describing: p1), fallback: "Maximizes bandwidth usage, up to %@, for each playback stream to ensure the highest quality.")
|
||||||
|
}
|
||||||
/// Option to set the bitrate to 1080p quality at 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")
|
internal static let bitrateMbps10 = L10n.tr("Localizable", "bitrateMbps10", fallback: "1080p - 10 Mbps")
|
||||||
/// Option to set the bitrate to 4K quality at 120 Mbps
|
/// Option to set the bitrate to 4K quality at 120 Mbps
|
||||||
|
@ -154,10 +162,10 @@ internal enum L10n {
|
||||||
internal static let bitrateMbps80 = L10n.tr("Localizable", "bitrateMbps80", fallback: "4K - 80 Mbps")
|
internal static let bitrateMbps80 = L10n.tr("Localizable", "bitrateMbps80", fallback: "4K - 80 Mbps")
|
||||||
/// Bitrate Automatic Section Header
|
/// Bitrate Automatic Section Header
|
||||||
internal static let bitrateTest = L10n.tr("Localizable", "bitrateTest", fallback: "Bitrate Test")
|
internal static let bitrateTest = L10n.tr("Localizable", "bitrateTest", fallback: "Bitrate Test")
|
||||||
/// 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")
|
|
||||||
/// Description for bitrate test duration indicating longer tests provide more accurate bitrates but may delay 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")
|
internal static let bitrateTestDisclaimer = L10n.tr("Localizable", "bitrateTestDisclaimer", fallback: "Longer tests are more accurate but may result in a delayed playback.")
|
||||||
|
/// bps
|
||||||
|
internal static let bitsPerSecond = L10n.tr("Localizable", "bitsPerSecond", fallback: "bps")
|
||||||
/// Blue
|
/// Blue
|
||||||
internal static let blue = L10n.tr("Localizable", "blue", fallback: "Blue")
|
internal static let blue = L10n.tr("Localizable", "blue", fallback: "Blue")
|
||||||
/// Bugs and Features
|
/// Bugs and Features
|
||||||
|
@ -220,6 +228,8 @@ internal enum L10n {
|
||||||
internal static let compatibility = L10n.tr("Localizable", "compatibility", fallback: "Compatibility")
|
internal static let compatibility = L10n.tr("Localizable", "compatibility", fallback: "Compatibility")
|
||||||
/// PlaybackCompatibility Compatible Category
|
/// PlaybackCompatibility Compatible Category
|
||||||
internal static let compatible = L10n.tr("Localizable", "compatible", fallback: "Most Compatible")
|
internal static let compatible = L10n.tr("Localizable", "compatible", fallback: "Most Compatible")
|
||||||
|
/// Converts all media to H.264 video and AAC audio for maximum compatibility. May require server transcoding for non-compatible media types.
|
||||||
|
internal static let compatibleDescription = L10n.tr("Localizable", "compatibleDescription", fallback: "Converts all media to H.264 video and AAC audio for maximum compatibility. May require server transcoding for non-compatible media types.")
|
||||||
/// Confirm Task Fuction
|
/// Confirm Task Fuction
|
||||||
internal static let confirm = L10n.tr("Localizable", "confirm", fallback: "Confirm")
|
internal static let confirm = L10n.tr("Localizable", "confirm", fallback: "Confirm")
|
||||||
/// Confirm Close
|
/// Confirm Close
|
||||||
|
@ -262,6 +272,8 @@ internal enum L10n {
|
||||||
internal static let currentPosition = L10n.tr("Localizable", "currentPosition", fallback: "Current Position")
|
internal static let currentPosition = L10n.tr("Localizable", "currentPosition", fallback: "Current Position")
|
||||||
/// PlaybackCompatibility Custom Category
|
/// PlaybackCompatibility Custom Category
|
||||||
internal static let custom = L10n.tr("Localizable", "custom", fallback: "Custom")
|
internal static let custom = L10n.tr("Localizable", "custom", fallback: "Custom")
|
||||||
|
/// Allows advanced customization of device profiles for native playback. Incorrect settings may affect playback.
|
||||||
|
internal static let customDescription = L10n.tr("Localizable", "customDescription", fallback: "Allows advanced customization of device profiles for native playback. Incorrect settings may affect playback.")
|
||||||
/// Custom Device Name
|
/// Custom Device Name
|
||||||
internal static let customDeviceName = L10n.tr("Localizable", "customDeviceName", fallback: "Custom Device Name")
|
internal static let customDeviceName = L10n.tr("Localizable", "customDeviceName", fallback: "Custom Device Name")
|
||||||
/// Your custom device name '%1$@' has been saved.
|
/// Your custom device name '%1$@' has been saved.
|
||||||
|
@ -269,11 +281,11 @@ internal enum L10n {
|
||||||
return L10n.tr("Localizable", "customDeviceNameSaved", String(describing: p1), fallback: "Your custom device name '%1$@' has been saved.")
|
return L10n.tr("Localizable", "customDeviceNameSaved", String(describing: p1), fallback: "Your custom device name '%1$@' has been saved.")
|
||||||
}
|
}
|
||||||
/// Custom profile is Added to the Existing 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")
|
internal static let customDeviceProfileAdd = L10n.tr("Localizable", "customDeviceProfileAdd", fallback: "The custom device profiles will be added to the default Swiftfin device profiles.")
|
||||||
/// Device Profile Section Description
|
/// 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")
|
internal static let customDeviceProfileDescription = L10n.tr("Localizable", "customDeviceProfileDescription", fallback: "Dictates back to the Jellyfin Server what this device hardware is capable of playing.")
|
||||||
/// Custom profile will replace the Existing 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")
|
internal static let customDeviceProfileReplace = L10n.tr("Localizable", "customDeviceProfileReplace", fallback: "The custom device profiles will replace the default Swiftfin device profiles.")
|
||||||
/// Settings View - Customize
|
/// Settings View - Customize
|
||||||
internal static let customize = L10n.tr("Localizable", "customize", fallback: "Customize")
|
internal static let customize = L10n.tr("Localizable", "customize", fallback: "Customize")
|
||||||
/// Section Header for a Custom Device Profile
|
/// Section Header for a Custom Device Profile
|
||||||
|
@ -342,10 +354,14 @@ internal enum L10n {
|
||||||
internal static let device = L10n.tr("Localizable", "device", fallback: "Device")
|
internal static let device = L10n.tr("Localizable", "device", fallback: "Device")
|
||||||
/// Section Header for Device Profiles
|
/// Section Header for Device Profiles
|
||||||
internal static let deviceProfile = L10n.tr("Localizable", "deviceProfile", fallback: "Device Profile")
|
internal static let deviceProfile = L10n.tr("Localizable", "deviceProfile", fallback: "Device Profile")
|
||||||
|
/// Decide which media plays natively or requires server transcoding for compatibility.
|
||||||
|
internal static let deviceProfileDescription = L10n.tr("Localizable", "deviceProfileDescription", fallback: "Decide which media plays natively or requires server transcoding for compatibility.")
|
||||||
/// Devices
|
/// Devices
|
||||||
internal static let devices = L10n.tr("Localizable", "devices", fallback: "Devices")
|
internal static let devices = L10n.tr("Localizable", "devices", fallback: "Devices")
|
||||||
/// PlaybackCompatibility DirectPlay Category
|
/// PlaybackCompatibility DirectPlay Category
|
||||||
internal static let direct = L10n.tr("Localizable", "direct", fallback: "Direct Play")
|
internal static let direct = L10n.tr("Localizable", "direct", fallback: "Direct Play")
|
||||||
|
/// Plays content in its original format. May cause playback issues on unsupported media types.
|
||||||
|
internal static let directDescription = L10n.tr("Localizable", "directDescription", fallback: "Plays content in its original format. May cause playback issues on unsupported media types.")
|
||||||
/// DIRECTOR
|
/// DIRECTOR
|
||||||
internal static let director = L10n.tr("Localizable", "director", fallback: "DIRECTOR")
|
internal static let director = L10n.tr("Localizable", "director", fallback: "DIRECTOR")
|
||||||
/// PlayMethod - Direct Play
|
/// PlayMethod - Direct Play
|
||||||
|
@ -428,6 +444,8 @@ internal enum L10n {
|
||||||
internal static let genres = L10n.tr("Localizable", "genres", fallback: "Genres")
|
internal static let genres = L10n.tr("Localizable", "genres", fallback: "Genres")
|
||||||
/// Gestures
|
/// Gestures
|
||||||
internal static let gestures = L10n.tr("Localizable", "gestures", fallback: "Gestures")
|
internal static let gestures = L10n.tr("Localizable", "gestures", fallback: "Gestures")
|
||||||
|
/// Gbps
|
||||||
|
internal static let gigabitsPerSecond = L10n.tr("Localizable", "gigabitsPerSecond", fallback: "Gbps")
|
||||||
/// Green
|
/// Green
|
||||||
internal static let green = L10n.tr("Localizable", "green", fallback: "Green")
|
internal static let green = L10n.tr("Localizable", "green", fallback: "Green")
|
||||||
/// Grid
|
/// Grid
|
||||||
|
@ -486,6 +504,8 @@ internal enum L10n {
|
||||||
}
|
}
|
||||||
/// Kids
|
/// Kids
|
||||||
internal static let kids = L10n.tr("Localizable", "kids", fallback: "Kids")
|
internal static let kids = L10n.tr("Localizable", "kids", fallback: "Kids")
|
||||||
|
/// kbps
|
||||||
|
internal static let kilobitsPerSecond = L10n.tr("Localizable", "kilobitsPerSecond", fallback: "kbps")
|
||||||
/// Larger
|
/// Larger
|
||||||
internal static let larger = L10n.tr("Localizable", "larger", fallback: "Larger")
|
internal static let larger = L10n.tr("Localizable", "larger", fallback: "Larger")
|
||||||
/// Largest
|
/// Largest
|
||||||
|
@ -535,9 +555,11 @@ internal enum L10n {
|
||||||
/// Option to set the maximum bitrate for playback
|
/// Option to set the maximum bitrate for playback
|
||||||
internal static let maximumBitrate = L10n.tr("Localizable", "maximumBitrate", fallback: "Maximum Bitrate")
|
internal static let maximumBitrate = L10n.tr("Localizable", "maximumBitrate", fallback: "Maximum Bitrate")
|
||||||
/// Playback May Fail
|
/// Playback May Fail
|
||||||
internal static let mayResultInPlaybackFailure = L10n.tr("Localizable", "mayResultInPlaybackFailure", fallback: "This setting may result in media failing to start playback")
|
internal static let mayResultInPlaybackFailure = L10n.tr("Localizable", "mayResultInPlaybackFailure", fallback: "This setting may result in media failing to start playback.")
|
||||||
/// Media
|
/// Media
|
||||||
internal static let media = L10n.tr("Localizable", "media", fallback: "Media")
|
internal static let media = L10n.tr("Localizable", "media", fallback: "Media")
|
||||||
|
/// Mbps
|
||||||
|
internal static let megabitsPerSecond = L10n.tr("Localizable", "megabitsPerSecond", fallback: "Mbps")
|
||||||
/// Menu Buttons
|
/// Menu Buttons
|
||||||
internal static let menuButtons = L10n.tr("Localizable", "menuButtons", fallback: "Menu Buttons")
|
internal static let menuButtons = L10n.tr("Localizable", "menuButtons", fallback: "Menu Buttons")
|
||||||
/// Metadata
|
/// Metadata
|
||||||
|
@ -1016,6 +1038,8 @@ internal enum L10n {
|
||||||
internal static let taskTriggerInterval = L10n.tr("Localizable", "taskTriggerInterval", fallback: "Sets the duration (in minutes) in between task triggers.")
|
internal static let taskTriggerInterval = L10n.tr("Localizable", "taskTriggerInterval", fallback: "Sets the duration (in minutes) in between task triggers.")
|
||||||
/// Sets the maximum runtime (in hours) for this task trigger.
|
/// Sets the maximum runtime (in hours) for this task trigger.
|
||||||
internal static let taskTriggerTimeLimit = L10n.tr("Localizable", "taskTriggerTimeLimit", fallback: "Sets the maximum runtime (in hours) for this task trigger.")
|
internal static let taskTriggerTimeLimit = L10n.tr("Localizable", "taskTriggerTimeLimit", fallback: "Sets the maximum runtime (in hours) for this task trigger.")
|
||||||
|
/// Tbps
|
||||||
|
internal static let terabitsPerSecond = L10n.tr("Localizable", "terabitsPerSecond", fallback: "Tbps")
|
||||||
/// Option to set the test size for bitrate testing
|
/// Option to set the test size for bitrate testing
|
||||||
internal static let testSize = L10n.tr("Localizable", "testSize", fallback: "Test Size")
|
internal static let testSize = L10n.tr("Localizable", "testSize", fallback: "Test Size")
|
||||||
/// Time
|
/// Time
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
//
|
||||||
|
// 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 SwiftUI
|
||||||
|
|
||||||
|
struct LearnMoreModal: View {
|
||||||
|
|
||||||
|
private let items: [TextPair]
|
||||||
|
|
||||||
|
// MARK: - Initializer
|
||||||
|
|
||||||
|
init(@ArrayBuilder<TextPair> items: () -> [TextPair]) {
|
||||||
|
self.items = items()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Body
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
ForEach(items) { content in
|
||||||
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
|
Text(content.title)
|
||||||
|
.font(.headline)
|
||||||
|
.foregroundStyle(.primary)
|
||||||
|
|
||||||
|
Text(content.subtitle)
|
||||||
|
.font(.subheadline)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
.padding(8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(24)
|
||||||
|
.background {
|
||||||
|
RoundedRectangle(cornerRadius: 10)
|
||||||
|
.fill(Material.regular)
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,13 +20,22 @@ struct PlaybackQualitySettingsView: View {
|
||||||
@EnvironmentObject
|
@EnvironmentObject
|
||||||
private var router: PlaybackQualitySettingsCoordinator.Router
|
private var router: PlaybackQualitySettingsCoordinator.Router
|
||||||
|
|
||||||
|
// MARK: - Focus Management
|
||||||
|
|
||||||
|
@FocusState
|
||||||
|
private var focusedItem: FocusableItem?
|
||||||
|
|
||||||
|
private enum FocusableItem: Hashable {
|
||||||
|
case maximumBitrate
|
||||||
|
case compatibility
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Body
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
SplitFormWindowView()
|
SplitFormWindowView()
|
||||||
.descriptionView {
|
.descriptionView {
|
||||||
Image(systemName: "play.rectangle.on.rectangle")
|
descriptionView
|
||||||
.resizable()
|
|
||||||
.aspectRatio(contentMode: .fit)
|
|
||||||
.frame(maxWidth: 400)
|
|
||||||
}
|
}
|
||||||
.contentView {
|
.contentView {
|
||||||
Section {
|
Section {
|
||||||
|
@ -34,13 +43,12 @@ struct PlaybackQualitySettingsView: View {
|
||||||
title: L10n.maximumBitrate,
|
title: L10n.maximumBitrate,
|
||||||
selection: $appMaximumBitrate
|
selection: $appMaximumBitrate
|
||||||
)
|
)
|
||||||
|
.focused($focusedItem, equals: .maximumBitrate)
|
||||||
} header: {
|
} header: {
|
||||||
L10n.bitrateDefault.text
|
L10n.bitrateDefault.text
|
||||||
} footer: {
|
} footer: {
|
||||||
VStack(alignment: .leading) {
|
|
||||||
L10n.bitrateDefaultDescription.text
|
L10n.bitrateDefaultDescription.text
|
||||||
}
|
}
|
||||||
}
|
|
||||||
.animation(.none, value: appMaximumBitrate)
|
.animation(.none, value: appMaximumBitrate)
|
||||||
|
|
||||||
if appMaximumBitrate == .auto {
|
if appMaximumBitrate == .auto {
|
||||||
|
@ -49,22 +57,17 @@ struct PlaybackQualitySettingsView: View {
|
||||||
title: L10n.testSize,
|
title: L10n.testSize,
|
||||||
selection: $appMaximumBitrateTest
|
selection: $appMaximumBitrateTest
|
||||||
)
|
)
|
||||||
} header: {
|
|
||||||
L10n.bitrateTest.text
|
|
||||||
} footer: {
|
} footer: {
|
||||||
VStack(alignment: .leading, spacing: 8) {
|
|
||||||
L10n.bitrateTestDescription.text
|
|
||||||
L10n.bitrateTestDisclaimer.text
|
L10n.bitrateTestDisclaimer.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
InlineEnumToggle(
|
InlineEnumToggle(
|
||||||
title: L10n.compatibility,
|
title: L10n.compatibility,
|
||||||
selection: $compatibilityMode
|
selection: $compatibilityMode
|
||||||
)
|
)
|
||||||
.animation(.none, value: compatibilityMode)
|
.focused($focusedItem, equals: .compatibility)
|
||||||
|
|
||||||
if compatibilityMode == .custom {
|
if compatibilityMode == .custom {
|
||||||
ChevronButton(L10n.profiles)
|
ChevronButton(L10n.profiles)
|
||||||
|
@ -74,8 +77,66 @@ struct PlaybackQualitySettingsView: View {
|
||||||
}
|
}
|
||||||
} header: {
|
} header: {
|
||||||
L10n.deviceProfile.text
|
L10n.deviceProfile.text
|
||||||
|
} footer: {
|
||||||
|
L10n.deviceProfileDescription.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle(L10n.playbackQuality)
|
.navigationTitle(L10n.playbackQuality)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Description View Icon
|
||||||
|
|
||||||
|
private var descriptionView: some View {
|
||||||
|
ZStack {
|
||||||
|
Image(systemName: "play.rectangle.on.rectangle")
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.frame(maxWidth: 400)
|
||||||
|
|
||||||
|
focusedDescription
|
||||||
|
.transition(.opacity.animation(.linear(duration: 0.2)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Description View on Focus
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
private var focusedDescription: some View {
|
||||||
|
switch focusedItem {
|
||||||
|
case .maximumBitrate:
|
||||||
|
LearnMoreModal {
|
||||||
|
TextPair(
|
||||||
|
title: L10n.auto,
|
||||||
|
subtitle: L10n.birateAutoDescription
|
||||||
|
)
|
||||||
|
TextPair(
|
||||||
|
title: L10n.bitrateMax,
|
||||||
|
subtitle: L10n.bitrateMaxDescription(PlaybackBitrate.max.rawValue.formatted(.bitRate))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
case .compatibility:
|
||||||
|
LearnMoreModal {
|
||||||
|
TextPair(
|
||||||
|
title: L10n.auto,
|
||||||
|
subtitle: L10n.autoDescription
|
||||||
|
)
|
||||||
|
TextPair(
|
||||||
|
title: L10n.compatible,
|
||||||
|
subtitle: L10n.compatibleDescription
|
||||||
|
)
|
||||||
|
TextPair(
|
||||||
|
title: L10n.direct,
|
||||||
|
subtitle: L10n.directDescription
|
||||||
|
)
|
||||||
|
TextPair(
|
||||||
|
title: L10n.custom,
|
||||||
|
subtitle: L10n.customDescription
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
case nil:
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,7 @@
|
||||||
4E73E2A72C41CFD3002D2A78 /* PlaybackBitrateTestSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E73E2A52C41CFD3002D2A78 /* PlaybackBitrateTestSize.swift */; };
|
4E73E2A72C41CFD3002D2A78 /* PlaybackBitrateTestSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E73E2A52C41CFD3002D2A78 /* PlaybackBitrateTestSize.swift */; };
|
||||||
4E762AAE2C3A1A95004D1579 /* PlaybackBitrate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E762AAD2C3A1A95004D1579 /* PlaybackBitrate.swift */; };
|
4E762AAE2C3A1A95004D1579 /* PlaybackBitrate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E762AAD2C3A1A95004D1579 /* PlaybackBitrate.swift */; };
|
||||||
4E762AAF2C3A1A95004D1579 /* PlaybackBitrate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E762AAD2C3A1A95004D1579 /* PlaybackBitrate.swift */; };
|
4E762AAF2C3A1A95004D1579 /* PlaybackBitrate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E762AAD2C3A1A95004D1579 /* PlaybackBitrate.swift */; };
|
||||||
|
4E884C652CEBB301004CF6AD /* LearnMoreModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E884C642CEBB2FF004CF6AD /* LearnMoreModal.swift */; };
|
||||||
4E8B34EA2AB91B6E0018F305 /* ItemFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E8B34E92AB91B6E0018F305 /* ItemFilter.swift */; };
|
4E8B34EA2AB91B6E0018F305 /* ItemFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E8B34E92AB91B6E0018F305 /* ItemFilter.swift */; };
|
||||||
4E8B34EB2AB91B6E0018F305 /* ItemFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E8B34E92AB91B6E0018F305 /* ItemFilter.swift */; };
|
4E8B34EB2AB91B6E0018F305 /* ItemFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E8B34E92AB91B6E0018F305 /* ItemFilter.swift */; };
|
||||||
4E8F74A22CE03C9000CC8969 /* ItemEditorCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E8F74A02CE03C8B00CC8969 /* ItemEditorCoordinator.swift */; };
|
4E8F74A22CE03C9000CC8969 /* ItemEditorCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E8F74A02CE03C8B00CC8969 /* ItemEditorCoordinator.swift */; };
|
||||||
|
@ -1121,6 +1122,7 @@
|
||||||
4E71D6882C80910900A0174D /* EditCustomDeviceProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditCustomDeviceProfileView.swift; sourceTree = "<group>"; };
|
4E71D6882C80910900A0174D /* EditCustomDeviceProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditCustomDeviceProfileView.swift; sourceTree = "<group>"; };
|
||||||
4E73E2A52C41CFD3002D2A78 /* PlaybackBitrateTestSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybackBitrateTestSize.swift; sourceTree = "<group>"; };
|
4E73E2A52C41CFD3002D2A78 /* PlaybackBitrateTestSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybackBitrateTestSize.swift; sourceTree = "<group>"; };
|
||||||
4E762AAD2C3A1A95004D1579 /* PlaybackBitrate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlaybackBitrate.swift; sourceTree = "<group>"; };
|
4E762AAD2C3A1A95004D1579 /* PlaybackBitrate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlaybackBitrate.swift; sourceTree = "<group>"; };
|
||||||
|
4E884C642CEBB2FF004CF6AD /* LearnMoreModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LearnMoreModal.swift; sourceTree = "<group>"; };
|
||||||
4E8B34E92AB91B6E0018F305 /* ItemFilter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemFilter.swift; sourceTree = "<group>"; };
|
4E8B34E92AB91B6E0018F305 /* ItemFilter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemFilter.swift; sourceTree = "<group>"; };
|
||||||
4E8F74A02CE03C8B00CC8969 /* ItemEditorCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemEditorCoordinator.swift; sourceTree = "<group>"; };
|
4E8F74A02CE03C8B00CC8969 /* ItemEditorCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemEditorCoordinator.swift; sourceTree = "<group>"; };
|
||||||
4E8F74A42CE03D3800CC8969 /* ItemEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemEditorView.swift; sourceTree = "<group>"; };
|
4E8F74A42CE03D3800CC8969 /* ItemEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemEditorView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -3269,6 +3271,7 @@
|
||||||
E1A42E4D28CBD3B200A14DCB /* HomeView */,
|
E1A42E4D28CBD3B200A14DCB /* HomeView */,
|
||||||
E12376B22A33DFAC001F5B44 /* ItemOverviewView.swift */,
|
E12376B22A33DFAC001F5B44 /* ItemOverviewView.swift */,
|
||||||
E193D54E271942C000900D82 /* ItemView */,
|
E193D54E271942C000900D82 /* ItemView */,
|
||||||
|
4E884C642CEBB2FF004CF6AD /* LearnMoreModal.swift */,
|
||||||
E158C8D02A31947500C527C5 /* MediaSourceInfoView.swift */,
|
E158C8D02A31947500C527C5 /* MediaSourceInfoView.swift */,
|
||||||
E103DF932BCF31C5000229B2 /* MediaView */,
|
E103DF932BCF31C5000229B2 /* MediaView */,
|
||||||
E10231572BCF8AF8009D71FC /* ProgramsView */,
|
E10231572BCF8AF8009D71FC /* ProgramsView */,
|
||||||
|
@ -4831,6 +4834,7 @@
|
||||||
E14EDEC62B8FB64E000F00A4 /* AnyItemFilter.swift in Sources */,
|
E14EDEC62B8FB64E000F00A4 /* AnyItemFilter.swift in Sources */,
|
||||||
E14EDEC92B8FB65F000F00A4 /* ItemFilterType.swift in Sources */,
|
E14EDEC92B8FB65F000F00A4 /* ItemFilterType.swift in Sources */,
|
||||||
E1D37F4C2B9CEA5C00343D2B /* ImageSource.swift in Sources */,
|
E1D37F4C2B9CEA5C00343D2B /* ImageSource.swift in Sources */,
|
||||||
|
4E884C652CEBB301004CF6AD /* LearnMoreModal.swift in Sources */,
|
||||||
E1B4E4372CA7795200DC49DE /* OrderedDictionary.swift in Sources */,
|
E1B4E4372CA7795200DC49DE /* OrderedDictionary.swift in Sources */,
|
||||||
E1AD104E26D96CE3003E4A08 /* BaseItemDto.swift in Sources */,
|
E1AD104E26D96CE3003E4A08 /* BaseItemDto.swift in Sources */,
|
||||||
E118959E289312020042947B /* BaseItemPerson+Poster.swift in Sources */,
|
E118959E289312020042947B /* BaseItemPerson+Poster.swift in Sources */,
|
||||||
|
|
|
@ -30,8 +30,16 @@ struct LearnMoreButton: View {
|
||||||
isPresented = true
|
isPresented = true
|
||||||
}
|
}
|
||||||
.foregroundStyle(Color.accentColor)
|
.foregroundStyle(Color.accentColor)
|
||||||
.font(.subheadline)
|
.buttonStyle(.plain)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.sheet(isPresented: $isPresented) {
|
.sheet(isPresented: $isPresented) {
|
||||||
|
learnMoreView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Learn More View
|
||||||
|
|
||||||
|
private var learnMoreView: some View {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
VStack(alignment: .leading, spacing: 16) {
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
@ -45,7 +53,6 @@ struct LearnMoreButton: View {
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
}
|
}
|
||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,4 +66,3 @@ struct LearnMoreButton: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ struct CustomDeviceProfileSettingsView: View {
|
||||||
.navigationTitle(L10n.profiles)
|
.navigationTitle(L10n.profiles)
|
||||||
.topBarTrailing {
|
.topBarTrailing {
|
||||||
if customProfiles.isNotEmpty {
|
if customProfiles.isNotEmpty {
|
||||||
Button("Add") {
|
Button(L10n.add) {
|
||||||
UIDevice.impact(.light)
|
UIDevice.impact(.light)
|
||||||
router.route(to: \.createCustomDeviceProfile)
|
router.route(to: \.createCustomDeviceProfile)
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,7 +150,7 @@ struct CustomizeViewsSettings: View {
|
||||||
|
|
||||||
if libraryDisplayType == .list, UIDevice.isPad {
|
if libraryDisplayType == .list, UIDevice.isPad {
|
||||||
BasicStepper(
|
BasicStepper(
|
||||||
title: "Columns",
|
title: L10n.columns,
|
||||||
value: $listColumnCount,
|
value: $listColumnCount,
|
||||||
range: 1 ... 4,
|
range: 1 ... 4,
|
||||||
step: 1
|
step: 1
|
||||||
|
|
|
@ -31,7 +31,20 @@ struct PlaybackQualitySettingsView: View {
|
||||||
} header: {
|
} header: {
|
||||||
L10n.bitrateDefault.text
|
L10n.bitrateDefault.text
|
||||||
} footer: {
|
} footer: {
|
||||||
L10n.bitrateDefaultDescription.text
|
VStack(alignment: .leading) {
|
||||||
|
Text(L10n.bitrateDefaultDescription)
|
||||||
|
LearnMoreButton(L10n.bitrateDefault) {
|
||||||
|
TextPair(
|
||||||
|
title: L10n.auto,
|
||||||
|
subtitle: L10n.birateAutoDescription
|
||||||
|
)
|
||||||
|
TextPair(
|
||||||
|
title: L10n.bitrateMax,
|
||||||
|
subtitle: L10n.bitrateMaxDescription(PlaybackBitrate.max.rawValue.formatted(.bitRate))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.foregroundStyle(.foreground, .primary)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.animation(.none, value: appMaximumBitrate)
|
.animation(.none, value: appMaximumBitrate)
|
||||||
|
|
||||||
|
@ -44,18 +57,12 @@ struct PlaybackQualitySettingsView: View {
|
||||||
} header: {
|
} header: {
|
||||||
L10n.bitrateTest.text
|
L10n.bitrateTest.text
|
||||||
} footer: {
|
} footer: {
|
||||||
VStack(alignment: .leading, spacing: 8) {
|
VStack(alignment: .leading) {
|
||||||
L10n.bitrateTestDescription.text
|
|
||||||
L10n.bitrateTestDisclaimer.text
|
L10n.bitrateTestDisclaimer.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Have a small description and a "Learn More..."
|
|
||||||
// button that will open a page for longer descriptions
|
|
||||||
// of each option. See: iOS Settings/Accessibility/VoiceOver
|
|
||||||
// for reference
|
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
CaseIterablePicker(
|
CaseIterablePicker(
|
||||||
L10n.compatibility,
|
L10n.compatibility,
|
||||||
|
@ -70,7 +77,30 @@ struct PlaybackQualitySettingsView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} header: {
|
} header: {
|
||||||
L10n.deviceProfile.text
|
Text(L10n.deviceProfile)
|
||||||
|
} footer: {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text(L10n.deviceProfileDescription)
|
||||||
|
LearnMoreButton(L10n.deviceProfile) {
|
||||||
|
TextPair(
|
||||||
|
title: L10n.auto,
|
||||||
|
subtitle: L10n.autoDescription
|
||||||
|
)
|
||||||
|
TextPair(
|
||||||
|
title: L10n.compatible,
|
||||||
|
subtitle: L10n.compatibleDescription
|
||||||
|
)
|
||||||
|
TextPair(
|
||||||
|
title: L10n.direct,
|
||||||
|
subtitle: L10n.directDescription
|
||||||
|
)
|
||||||
|
TextPair(
|
||||||
|
title: L10n.custom,
|
||||||
|
subtitle: L10n.customDescription
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.foregroundStyle(.foreground, .primary)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.animation(.linear, value: appMaximumBitrate)
|
.animation(.linear, value: appMaximumBitrate)
|
||||||
|
|
|
@ -364,11 +364,8 @@
|
||||||
/* Option to set the bitrate to 360p quality at 420 Kbps */
|
/* Option to set the bitrate to 360p quality at 420 Kbps */
|
||||||
"bitrateKbps420" = "360p - 420 Kbps";
|
"bitrateKbps420" = "360p - 420 Kbps";
|
||||||
|
|
||||||
/* Description for bitrate test duration description */
|
|
||||||
"bitrateTestDescription" = "Determines the length of the 'Auto' bitrate test used to find the available internet bandwidth";
|
|
||||||
|
|
||||||
/* Description for bitrate test duration indicating longer tests provide more accurate bitrates but may delay playback */
|
/* Description for bitrate test duration indicating longer tests provide more accurate bitrates but may delay playback */
|
||||||
"bitrateTestDisclaimer" = "Longer tests are more accurate but may result in a delayed playback";
|
"bitrateTestDisclaimer" = "Longer tests are more accurate but may result in a delayed playback.";
|
||||||
|
|
||||||
/* Select Server View */
|
/* Select Server View */
|
||||||
"servers" = "Servers";
|
"servers" = "Servers";
|
||||||
|
@ -464,10 +461,10 @@
|
||||||
"deviceProfile" = "Device Profile";
|
"deviceProfile" = "Device Profile";
|
||||||
|
|
||||||
/* Custom profile is Added to the Existing Profiles */
|
/* Custom profile is Added to the Existing Profiles */
|
||||||
"customDeviceProfileAdd" = "The custom device profiles will be added to the default Swiftfin device profiles";
|
"customDeviceProfileAdd" = "The custom device profiles will be added to the default Swiftfin device profiles.";
|
||||||
|
|
||||||
/* Custom profile will replace the Existing Profiles */
|
/* Custom profile will replace the Existing Profiles */
|
||||||
"customDeviceProfileReplace" = "The custom device profiles will replace the default Swiftfin device profiles";
|
"customDeviceProfileReplace" = "The custom device profiles will replace the default Swiftfin device profiles.";
|
||||||
|
|
||||||
/* Section for Playback Quality Settings */
|
/* Section for Playback Quality Settings */
|
||||||
"playbackQuality" = "Playback Quality";
|
"playbackQuality" = "Playback Quality";
|
||||||
|
@ -503,13 +500,13 @@
|
||||||
"bitrateDefault" = "Default Bitrate";
|
"bitrateDefault" = "Default Bitrate";
|
||||||
|
|
||||||
/* Default Bitrate Description */
|
/* Default Bitrate Description */
|
||||||
"bitrateDefaultDescription" = "Limits the internet bandwidth used during video playback";
|
"bitrateDefaultDescription" = "Limits the internet bandwidth used during playback.";
|
||||||
|
|
||||||
/* Playback May Fail */
|
/* Playback May Fail */
|
||||||
"mayResultInPlaybackFailure" = "This setting may result in media failing to start playback";
|
"mayResultInPlaybackFailure" = "This setting may result in media failing to start playback.";
|
||||||
|
|
||||||
/* Device Profile Section Description */
|
/* Device Profile Section Description */
|
||||||
"customDeviceProfileDescription" = "Dictates back to the Jellyfin Server what this device hardware is capable of playing";
|
"customDeviceProfileDescription" = "Dictates back to the Jellyfin Server what this device hardware is capable of playing.";
|
||||||
|
|
||||||
/* Session Device Section Label */
|
/* Session Device Section Label */
|
||||||
"device" = "Device";
|
"device" = "Device";
|
||||||
|
@ -1322,3 +1319,63 @@
|
||||||
/// Replace all metadata and images
|
/// Replace all metadata and images
|
||||||
/// Full refresh that replaces all unlocked metadata and images
|
/// Full refresh that replaces all unlocked metadata and images
|
||||||
"replaceAllDescription" = "Replace all unlocked metadata and images with new information.";
|
"replaceAllDescription" = "Replace all unlocked metadata and images with new information.";
|
||||||
|
|
||||||
|
/// Device Profile - Description
|
||||||
|
/// Explains how device profiles control playback and transcoding behavior
|
||||||
|
/// Used in the device profile settings section
|
||||||
|
"deviceProfileDescription" = "Decide which media plays natively or requires server transcoding for compatibility.";
|
||||||
|
|
||||||
|
/// Auto - Description
|
||||||
|
/// Optimizes playback using default settings for most devices
|
||||||
|
/// Some formats may require server transcoding
|
||||||
|
"autoDescription" = "Optimizes playback using default settings for most devices. Some formats may require server transcoding for non-compatible media types.";
|
||||||
|
|
||||||
|
/// Compatible - Description
|
||||||
|
/// Converts media to H.264 video and AAC audio for compatibility
|
||||||
|
/// Requires server transcoding for all content
|
||||||
|
"compatibleDescription" = "Converts all media to H.264 video and AAC audio for maximum compatibility. May require server transcoding for non-compatible media types.";
|
||||||
|
|
||||||
|
/// Direct - Description
|
||||||
|
/// Plays content in its original format without transcoding
|
||||||
|
/// May cause playback issues on unsupported devices
|
||||||
|
"directDescription" = "Plays content in its original format. May cause playback issues on unsupported media types.";
|
||||||
|
|
||||||
|
/// Custom - Description
|
||||||
|
/// Allows customization of device profiles for native playback
|
||||||
|
/// Incorrect settings may result in playback issues
|
||||||
|
"customDescription" = "Allows advanced customization of device profiles for native playback. Incorrect settings may affect playback.";
|
||||||
|
|
||||||
|
/// Server Connection Test - Description
|
||||||
|
/// Tests the connection to the server to assess internet speed
|
||||||
|
/// Used to adjust bandwidth settings automatically
|
||||||
|
"birateAutoDescription" = "Tests your server connection to assess internet speed and adjust bandwidth automatically.";
|
||||||
|
|
||||||
|
/// Bandwidth Usage - Description
|
||||||
|
/// Indicates the maximum bandwidth used per playback stream
|
||||||
|
/// Helps to manage data usage during streaming
|
||||||
|
"bitrateMaxDescription" = "Maximizes bandwidth usage, up to %@, for each playback stream to ensure the highest quality.";
|
||||||
|
|
||||||
|
// Bits Per Second - Unit
|
||||||
|
// Represents a speed in bits per second
|
||||||
|
// Used for bandwidth display
|
||||||
|
"bitsPerSecond" = "bps";
|
||||||
|
|
||||||
|
// Kilobits Per Second - Unit
|
||||||
|
// Represents a speed in kilobits per second
|
||||||
|
// Used for bandwidth display
|
||||||
|
"kilobitsPerSecond" = "kbps";
|
||||||
|
|
||||||
|
// Megabits Per Second - Unit
|
||||||
|
// Represents a speed in megabits per second
|
||||||
|
// Used for bandwidth display
|
||||||
|
"megabitsPerSecond" = "Mbps";
|
||||||
|
|
||||||
|
// Gigabits Per Second - Unit
|
||||||
|
// Represents a speed in gigabits per second
|
||||||
|
// Used for bandwidth display
|
||||||
|
"gigabitsPerSecond" = "Gbps";
|
||||||
|
|
||||||
|
// Terabits Per Second - Unit
|
||||||
|
// Represents a speed in terabits per second
|
||||||
|
// Used for bandwidth display
|
||||||
|
"terabitsPerSecond" = "Tbps";
|
||||||
|
|
Loading…
Reference in New Issue