Skip to content

Refactor: Unified player state retry system + premium quality #1337

Description

@VampireChicken12

Summary

Create a FeaturePlayerManager registry manager that provides a unified retry system for all player-interacting features, plus add premium quality detection to playerQuality. Inspired by patterns from yt-force-max.

Motivation

Currently, player-related features use inconsistent patterns:

  • No retries: automaticTheaterMode, playerQuality, playerSpeed, automaticallyDisableAmbientMode — single waitForElement, if it times out, the feature silently fails
  • Custom retries: automaticallyDisableAutoPlay has its own clickToggleUntil loop
  • No waitForPlayerLoaded: Only 3 features use it; others may interact with the player before it's ready
  • No re-apply on player state change: Settings only re-apply on navigation events, not on player state transitions

Plan

Step 1: FeaturePlayerManager — new registry manager

File: src/features/_registry/featurePlayerManager.ts

Integrated into FeatureRegistry as registry.playerManager, following the existing FeatureManagerBase pattern.

type PlayerTask = () => boolean | Promise<boolean>;
type PlayerRetryConfig = {
  maxAttempts?: number;        // default: 30
  interval?: number;           // default: 500ms
  overallTimeout?: number;     // default: 15000ms
  waitForLoaded?: boolean;     // default: true
  onPlayerStateChange?: boolean; // default: false
  pageTypes?: string[];        // default: ["watch", "live"]
};

Methods:

  • executeWithRetries(featureId, tasks, config?) — Uses MutationObserver to detect player element, optionally calls waitForPlayerLoaded, then polls tasks until all return true or maxAttempts reached
  • cleanup(featureId?) — Clears active retry loops
  • hookPlayerStateChange(featureId, callback) — Attaches onStateChange listener

Step 2: Premium Quality (playerQuality feature)

New file: src/utils/player/itagDb.ts
Full itag format database (all entries from yt-force-max):

  • H.264, VP9, VP9.2 HDR, AV1 SDR, AV1 HDR, audio formats
  • Premium itags: 356, 712, 721, 141, 774, 616
  • Exported as typed const with lookupItag(itag) helper

New config: preferPremium: boolean (default: false) added to settings

Integration: Use getAvailableQualityData() to find paygatedQualityDetails, pass premium format ID to setPlaybackQualityRange

Step 3: Refactored Features

Feature Task Config
automaticTheaterMode Click size button, verify theater attribute waitForLoaded: false, interval: 300
playerQuality Set quality with premium detection waitForLoaded: true, onPlayerStateChange: true
automaticallyDisableAutoPlay Click toggle until aria-checked=false waitForLoaded: true, maxAttempts: 10
automaticallyDisableAmbientMode Open settings, toggle ambient waitForLoaded: false, maxAttempts: 20
automaticallyMaximizePlayer Maximize player waitForLoaded: true, overallTimeout: 20000
playerSpeed Set playback rate waitForLoaded: true
defaultToOriginalAudioTrack Set audio track waitForLoaded: true, maxAttempts: 15
scrollWheelSpeedControl Attach scroll listener waitForLoaded: false
scrollWheelVolumeControl Attach scroll listener waitForLoaded: false

Step 4: Wire into registry

FeatureNavigationManager.handleNavigation() calls registry.playerManager.cleanup() to abort stale retries on navigation.

Step 5: Enhance waitForPlayerLoaded

Also check videoElement.readyState >= 2 as fallback. Better live stream support.

Migration Commits

  1. FeaturePlayerManager + itagDb (new files)
  2. Refactor automaticTheaterMode (simple test case)
  3. Refactor playerQuality + premium quality (most complex)
  4. Refactor remaining 6 player features (mechanical)
  5. Enhance waitForPlayerLoaded
  6. Final lint + typecheck

Constraints

  • All features remain independently toggleable
  • FeaturePlayerManager uses same error handling/perf tracking as other managers
  • Type-only: no interface keyword — all types use type aliases
  • Zero changes to non-player features or buttonPlacement/featureMenu utilities

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions