Skipping intros stops being a switch and becomes a three-way choice —
never / ask / always — matching what Jellyfin offers and giving viewers a
way to turn the prompt off, which the boolean could not express.
Contract revision 6 → 7: adds playback.intro_skip_mode (enum, default
"ask", profile + profile_device scopes) and marks playback.auto_skip_intro
deprecated without removing it. Every shipped client still reads the
boolean, so for one release the server keeps the pair in step at write
time: canonical PUT/DELETE, the legacy /profiles route, and the legacy
runtime /settings/{key} route all land both rows, and a profile-scope enum
write refreshes user_profiles.auto_skip_intro so GET /profiles stays
truthful. Existing rows are carried onto the new key by a Goose migration
(Postgres) and an InitSchema twin (per-user SQLite); the settings-migrate
planner emits the companion for installs whose backfill runs later.
The spec in docs/design/2026-08-16-intro-skip-mode.md also defines the
prompt state machine every client (web, Android, Apple; browser, tablet,
mobile, TV) implements against this key. It builds on the Android TV
Skip Intro work in silo-android#210 — wall-clock timer, rebuffer-vs-pause
debounce, root-level key handling.
Co-authored-by: evulhotdog <365456+evulhotdog@users.noreply.github.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
187 lines
5.6 KiB
Go
187 lines
5.6 KiB
Go
// Code generated by cmd/settingsgen from contracts/settings/v1/manifest.json. DO NOT EDIT.
|
|
//
|
|
// Regenerate with: make settings-bindings
|
|
//
|
|
// Every key, type, scope and default here comes from the manifest, so a client
|
|
// cannot drift from the server's contract by editing a constant. Adding a setting
|
|
// is a manifest change plus a regeneration, never a hand-written key.
|
|
|
|
package settingskeys
|
|
|
|
// Revision is the manifest revision these bindings were generated from.
|
|
const Revision = 7
|
|
|
|
// Setting keys, one constant per definition.
|
|
const (
|
|
// Metadata language
|
|
CatalogMetadataLanguage = "catalog.metadata_language"
|
|
// Metadata language exceptions
|
|
CatalogMetadataLanguageOverrides = "catalog.metadata_language_overrides"
|
|
// Download quality
|
|
DownloadsDefaultQuality = "downloads.default_quality"
|
|
// Keep watched downloads
|
|
DownloadsKeepWatched = "downloads.keep_watched"
|
|
// Download over Wi-Fi only
|
|
DownloadsWifiOnly = "downloads.wifi_only"
|
|
// Primary menu
|
|
NavPrimaryMenu = "nav.primary_menu"
|
|
// Navigation shortcuts
|
|
NavShortcuts = "nav.shortcuts"
|
|
// Show audiobooks
|
|
NavShowAudiobooks = "nav.show_audiobooks"
|
|
// Preferred audio language
|
|
PlaybackAudioLanguage = "playback.audio_language"
|
|
// Auto-play next episode
|
|
PlaybackAutoPlayNext = "playback.auto_play_next"
|
|
// Preview next episode
|
|
PlaybackAutoPlayNextPreview = "playback.auto_play_next_preview"
|
|
// Auto-skip credits
|
|
PlaybackAutoSkipCredits = "playback.auto_skip_credits"
|
|
// Auto-skip intros
|
|
PlaybackAutoSkipIntro = "playback.auto_skip_intro"
|
|
// Auto-skip recaps
|
|
PlaybackAutoSkipRecap = "playback.auto_skip_recap"
|
|
// Skip intros
|
|
PlaybackIntroSkipMode = "playback.intro_skip_mode"
|
|
// Maximum bitrate
|
|
PlaybackMaxBitrateKbps = "playback.max_bitrate_kbps"
|
|
// Next up prompt
|
|
PlaybackNextUpPromptSeconds = "playback.next_up_prompt_seconds"
|
|
// Preferred quality
|
|
PlaybackPreferredQuality = "playback.preferred_quality"
|
|
// Show forced subtitles
|
|
PlaybackShowForcedSubtitles = "playback.show_forced_subtitles"
|
|
// Subtitle appearance
|
|
PlaybackSubtitleAppearance = "playback.subtitle_appearance"
|
|
// Preferred subtitle language
|
|
PlaybackSubtitleLanguage = "playback.subtitle_language"
|
|
// Subtitles
|
|
PlaybackSubtitleMode = "playback.subtitle_mode"
|
|
// Audio sync offset
|
|
PlayerAudioSyncMs = "player.audio_sync_ms"
|
|
// Dolby Vision
|
|
PlayerDolbyVisionEnabled = "player.dolby_vision_enabled"
|
|
// Dolby Vision Profile 7 fallback
|
|
PlayerDvProfile7Hdr10Fallback = "player.dv_profile7_hdr10_fallback"
|
|
// HDR
|
|
PlayerHdrEnabled = "player.hdr_enabled"
|
|
// Match content frame rate
|
|
PlayerMatchFrameRate = "player.match_frame_rate"
|
|
// Screen orientation
|
|
PlayerOrientationMode = "player.orientation_mode"
|
|
// Still watching prompt
|
|
PlayerPassoutThreshold = "player.passout_threshold"
|
|
// Picture in picture
|
|
PlayerPictureInPictureEnabled = "player.picture_in_picture_enabled"
|
|
// Playback speed
|
|
PlayerPlaybackSpeed = "player.playback_speed"
|
|
// Rewind on resume
|
|
PlayerResumeRewindSeconds = "player.resume_rewind_seconds"
|
|
// Seek cache
|
|
PlayerSeekCacheEnabled = "player.seek_cache_enabled"
|
|
// Default sleep timer
|
|
PlayerSleepTimerDefaultMinutes = "player.sleep_timer_default_minutes"
|
|
// Subtitle sync offset
|
|
PlayerSubtitleSyncMs = "player.subtitle_sync_ms"
|
|
// Video sizing
|
|
PlayerVideoGravity = "player.video_gravity"
|
|
// Search scope
|
|
SearchMediaScope = "search.media_scope"
|
|
// Match device caption settings
|
|
SubtitleMatchesDevice = "subtitle.matches_device"
|
|
// Poster badges
|
|
UiCardOverlays = "ui.card_overlays"
|
|
// Media cards
|
|
UiCardPresentation = "ui.card_presentation"
|
|
// Custom CSS
|
|
UiCustomCss = "ui.custom_css"
|
|
// Custom theme variables
|
|
UiCustomThemeVars = "ui.custom_theme_vars"
|
|
// Date format
|
|
UiDateFormat = "ui.date_format"
|
|
// Hidden libraries
|
|
UiDisabledLibraryIds = "ui.disabled_library_ids"
|
|
// High contrast
|
|
UiHighContrast = "ui.high_contrast"
|
|
// Library order
|
|
UiLibraryOrder = "ui.library_order"
|
|
// Remembered library view
|
|
UiLibraryPageState = "ui.library_page_state"
|
|
// Next up episodes
|
|
UiNextUpMode = "ui.next_up_mode"
|
|
// Remember library view
|
|
UiRememberLibraryPageState = "ui.remember_library_page_state"
|
|
// Pinned sidebar items
|
|
UiSidebarPins = "ui.sidebar_pins"
|
|
// Text size
|
|
UiTextScale = "ui.text_scale"
|
|
// Text weight
|
|
UiTextWeight = "ui.text_weight"
|
|
// Theme
|
|
UiTheme = "ui.theme"
|
|
// Time format
|
|
UiTimeFormat = "ui.time_format"
|
|
)
|
|
|
|
// Remote lists every key the server stores.
|
|
var Remote = []string{
|
|
CatalogMetadataLanguage,
|
|
CatalogMetadataLanguageOverrides,
|
|
NavPrimaryMenu,
|
|
NavShortcuts,
|
|
PlaybackAudioLanguage,
|
|
PlaybackAutoPlayNext,
|
|
PlaybackAutoPlayNextPreview,
|
|
PlaybackAutoSkipCredits,
|
|
PlaybackAutoSkipIntro,
|
|
PlaybackAutoSkipRecap,
|
|
PlaybackIntroSkipMode,
|
|
PlaybackMaxBitrateKbps,
|
|
PlaybackNextUpPromptSeconds,
|
|
PlaybackPreferredQuality,
|
|
PlaybackShowForcedSubtitles,
|
|
PlaybackSubtitleAppearance,
|
|
PlaybackSubtitleLanguage,
|
|
PlaybackSubtitleMode,
|
|
PlayerAudioSyncMs,
|
|
PlayerDolbyVisionEnabled,
|
|
PlayerDvProfile7Hdr10Fallback,
|
|
PlayerHdrEnabled,
|
|
PlayerMatchFrameRate,
|
|
PlayerOrientationMode,
|
|
PlayerPlaybackSpeed,
|
|
PlayerSeekCacheEnabled,
|
|
PlayerSleepTimerDefaultMinutes,
|
|
PlayerSubtitleSyncMs,
|
|
PlayerVideoGravity,
|
|
SearchMediaScope,
|
|
UiCardOverlays,
|
|
UiCardPresentation,
|
|
UiCustomCss,
|
|
UiCustomThemeVars,
|
|
UiDateFormat,
|
|
UiDisabledLibraryIds,
|
|
UiHighContrast,
|
|
UiLibraryOrder,
|
|
UiLibraryPageState,
|
|
UiNextUpMode,
|
|
UiRememberLibraryPageState,
|
|
UiSidebarPins,
|
|
UiTextScale,
|
|
UiTextWeight,
|
|
UiTheme,
|
|
UiTimeFormat,
|
|
}
|
|
|
|
// ClientLocal lists keys the contract defines but the server never stores.
|
|
var ClientLocal = []string{
|
|
DownloadsDefaultQuality,
|
|
DownloadsKeepWatched,
|
|
DownloadsWifiOnly,
|
|
NavShowAudiobooks,
|
|
PlayerPassoutThreshold,
|
|
PlayerPictureInPictureEnabled,
|
|
PlayerResumeRewindSeconds,
|
|
SubtitleMatchesDevice,
|
|
}
|