Files
yattee/Yattee/Models/PlayerControls/BuiltInPresets.swift
2026-02-12 08:58:44 +01:00

230 lines
8.7 KiB
Swift

//
// BuiltInPresets.swift
// Yattee
//
// Factory methods for built-in layout presets.
//
import Foundation
extension LayoutPreset {
/// Bump this version whenever any built-in preset definition changes.
/// On launch, the app compares this against the last-applied version
/// and replaces stale built-in presets with fresh copies from code.
static let builtInPresetsVersion = 5
// MARK: - Built-in Preset IDs
/// Stable UUIDs for built-in presets to ensure consistency across devices.
private enum BuiltInID {
static let defaultPreset = UUID(uuidString: "00000000-0000-0000-0000-000000000001")!
static let minimalPreset = UUID(uuidString: "00000000-0000-0000-0000-000000000002")!
}
// MARK: - Built-in Presets
/// Default preset with balanced controls for general use.
static func defaultPreset(for deviceClass: DeviceClass = .current) -> LayoutPreset {
// Top buttons: titleAuthor (wideOnly), spacer, orientationLock, close
let topButtons: [ControlButtonConfiguration] = [
ControlButtonConfiguration(
buttonType: .titleAuthor,
visibilityMode: .wideOnly
),
.flexibleSpacer(),
ControlButtonConfiguration(
buttonType: .contextMenu,
visibilityMode: .wideOnly
),
.defaultConfiguration(for: .orientationLock),
.defaultConfiguration(for: .close)
]
// Bottom buttons: time, queue (wideOnly), playNext (wideOnly), spacer, contextMenu (wideOnly), settings, PiP, fullscreen
let bottomButtons: [ControlButtonConfiguration] = [
ControlButtonConfiguration(
buttonType: .timeDisplay,
settings: .timeDisplay(TimeDisplaySettings(format: .currentAndTotal))
),
ControlButtonConfiguration(
buttonType: .queue,
visibilityMode: .wideOnly
),
ControlButtonConfiguration(
buttonType: .playNext,
visibilityMode: .wideOnly
),
.flexibleSpacer(),
.defaultConfiguration(for: .settings),
.defaultConfiguration(for: .pictureInPicture),
.defaultConfiguration(for: .fullscreen)
]
// Center: all enabled, 10s seek, left=volume, right=brightness
let centerSettings = CenterSectionSettings(
showPlayPause: true,
showSeekBackward: true,
showSeekForward: true,
seekBackwardSeconds: 10,
seekForwardSeconds: 10,
leftSlider: .volume,
rightSlider: .brightness
)
// Global: plain style, medium buttons, system font
let globalSettings = GlobalLayoutSettings(
style: .plain,
buttonSize: .medium,
fontStyle: .system,
systemControlsMode: .seek,
systemControlsSeekDuration: .tenSeconds,
volumeMode: .mpv
)
// Gestures: tap 2x1 with seek, seek enabled, panscan with snap
let gesturesSettings = GesturesSettings(
tapGestures: TapGesturesSettings(
isEnabled: true,
layout: .horizontalSplit,
zoneConfigurations: [
TapZoneConfiguration(position: .left, action: .seekBackward(seconds: 10)),
TapZoneConfiguration(position: .right, action: .seekForward(seconds: 10))
]
),
seekGesture: SeekGestureSettings(isEnabled: false),
panscanGesture: PanscanGestureSettings(isEnabled: true, snapToEnds: true)
)
// Player pill: queue, previous, play/pause, next, close
let playerPillSettings = PlayerPillSettings(
visibility: .portraitOnly,
buttons: [
ControlButtonConfiguration(buttonType: .queue),
ControlButtonConfiguration(buttonType: .playPrevious),
ControlButtonConfiguration(buttonType: .playPause),
ControlButtonConfiguration(buttonType: .playNext),
ControlButtonConfiguration(buttonType: .close)
]
)
// Mini player: show video, tap for PiP
let miniPlayerSettings = MiniPlayerSettings()
let layout = PlayerControlsLayout(
topSection: LayoutSection(buttons: topButtons),
centerSettings: centerSettings,
bottomSection: LayoutSection(buttons: bottomButtons),
globalSettings: globalSettings,
progressBarSettings: ProgressBarSettings(),
gesturesSettings: gesturesSettings,
playerPillSettings: playerPillSettings,
miniPlayerSettings: miniPlayerSettings
)
return LayoutPreset(
id: BuiltInID.defaultPreset,
name: String(localized: "controls.preset.default"),
createdAt: Date(timeIntervalSince1970: 0),
updatedAt: Date(timeIntervalSince1970: 0),
isBuiltIn: true,
deviceClass: deviceClass,
layout: layout
)
}
/// Minimal preset with stripped-down controls for distraction-free playback.
static func minimalPreset(for deviceClass: DeviceClass = .current) -> LayoutPreset {
let topButtons: [ControlButtonConfiguration] = [
.flexibleSpacer(),
.defaultConfiguration(for: .close)
]
let bottomButtons: [ControlButtonConfiguration] = [
ControlButtonConfiguration(
buttonType: .timeDisplay,
settings: .timeDisplay(TimeDisplaySettings(format: .currentAndTotal))
),
.flexibleSpacer(),
.defaultConfiguration(for: .settings),
.defaultConfiguration(for: .pictureInPicture),
.defaultConfiguration(for: .fullscreen)
]
let centerSettings = CenterSectionSettings(
showPlayPause: true,
showSeekBackward: true,
showSeekForward: true,
seekBackwardSeconds: 10,
seekForwardSeconds: 10,
leftSlider: .disabled,
rightSlider: .disabled
)
let globalSettings = GlobalLayoutSettings(
style: .plain,
buttonSize: .medium,
fontStyle: .system,
systemControlsMode: .seek,
systemControlsSeekDuration: .tenSeconds,
volumeMode: .mpv
)
let gesturesSettings = GesturesSettings(
tapGestures: TapGesturesSettings(
isEnabled: true,
layout: .horizontalSplit,
zoneConfigurations: [
TapZoneConfiguration(position: .left, action: .seekBackward(seconds: 10)),
TapZoneConfiguration(position: .right, action: .seekForward(seconds: 10))
]
),
seekGesture: SeekGestureSettings(isEnabled: false),
panscanGesture: PanscanGestureSettings(isEnabled: true, snapToEnds: true)
)
let playerPillSettings = PlayerPillSettings(
visibility: .never,
buttons: [
ControlButtonConfiguration(buttonType: .queue),
ControlButtonConfiguration(buttonType: .playPrevious),
ControlButtonConfiguration(buttonType: .playPause),
ControlButtonConfiguration(buttonType: .playNext),
ControlButtonConfiguration(buttonType: .close)
]
)
let miniPlayerSettings = MiniPlayerSettings()
let progressBarSettings = ProgressBarSettings(
showChapters: false,
sponsorBlockSettings: SponsorBlockSegmentSettings(showSegments: false)
)
let layout = PlayerControlsLayout(
topSection: LayoutSection(buttons: topButtons),
centerSettings: centerSettings,
bottomSection: LayoutSection(buttons: bottomButtons),
globalSettings: globalSettings,
progressBarSettings: progressBarSettings,
gesturesSettings: gesturesSettings,
playerPillSettings: playerPillSettings,
miniPlayerSettings: miniPlayerSettings
)
return LayoutPreset(
id: BuiltInID.minimalPreset,
name: String(localized: "controls.preset.minimal"),
createdAt: Date(timeIntervalSince1970: 0),
updatedAt: Date(timeIntervalSince1970: 0),
isBuiltIn: true,
deviceClass: deviceClass,
layout: layout
)
}
/// All built-in presets for the given device class.
static func allBuiltIn(for deviceClass: DeviceClass = .current) -> [LayoutPreset] {
[defaultPreset(for: deviceClass), minimalPreset(for: deviceClass)]
}
}