From d0007db645c82c8799a23be9dad0c7ade4485b3d Mon Sep 17 00:00:00 2001 From: Matt Svoboda Date: Tue, 25 Jun 2024 12:12:17 -0700 Subject: [PATCH] Extend Shuffle mpv hook to support shuffle=yes from mpv options table --- iina/AppDelegate.swift | 4 ++-- iina/PlayerCore.swift | 49 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/iina/AppDelegate.swift b/iina/AppDelegate.swift index 542977abf9..6a67a0d9ca 100644 --- a/iina/AppDelegate.swift +++ b/iina/AppDelegate.swift @@ -1165,9 +1165,9 @@ struct CommandLineStatus { func applyMPVArguments(to playerCore: PlayerCore) { Logger.log("Setting mpv properties from arguments: \(mpvArguments)") for argPair in mpvArguments { - if argPair.0 == "shuffle" && argPair.1 == "yes" { + if argPair.0 == MPVOption.PlaybackControl.shuffle && argPair.1 == "yes" { // Special handling for this one - Logger.log("Found \"shuffle\" request in command-line args. Adding mpv hook to shuffle playlist") + Logger.log("Found \"shuffle\" request in command-line args. Adding mpv hook for shuffling playlist") playerCore.addShufflePlaylistHook() continue } diff --git a/iina/PlayerCore.swift b/iina/PlayerCore.swift index b2b91feda5..cb2955fdb0 100644 --- a/iina/PlayerCore.swift +++ b/iina/PlayerCore.swift @@ -179,6 +179,7 @@ class PlayerCore: NSObject { /// For supporting mpv `--shuffle` arg, to shuffle playlist when launching from command line @Atomic private var shufflePending = false + private var isShuffleHookAlreadyAdded = false // test seeking var triedUsingExactSeekForCurrentFile: Bool = false @@ -308,6 +309,21 @@ class PlayerCore: NSObject { guard !urls.isEmpty else { return 0 } let urls = Utility.resolveURLs(urls) + if Preference.bool(for: .enableAdvancedSettings) { + if let userOptions = Preference.value(for: .userOptions) as? [[String]] { + for op in userOptions { + if op.count == 2, op[0] == MPVOption.PlaybackControl.shuffle && op[1] == "yes" { + log("Found shuffle option in user's mpv options. Will add hook for it if not already added", level: .verbose) + addShufflePlaylistHook() + break + } + } + } else { + // If userOptions failed to parse, an error msg will be displayed to the user later + log("Failed to read mpv options; skipping check for shuffle option", level: .verbose) + } + } + // Handle folder URL (to support mpv shuffle, etc), BD folders and m3u / m3u8 files first. // For these cases, mpv will load/build the playlist and notify IINA when it can be retrieved. if urls.count == 1 { @@ -1639,17 +1655,36 @@ class PlayerCore: NSObject { return GeometryDef.parse(geometry) } - /// Uses an mpv `on_before_start_file` hook to honor mpv's `shuffle` command via IINA CLI. + /// Adds an mpv `on_before_start_file` hook (if not already added) to honor mpv's `shuffle` option. + /// + /// This hook is needed to maintain support for the mpv `shuffle` option while also enabling IINA to filter any opened + /// files/directories for playable files, which mpv does not support (see the `openURLs` method). /// /// There is currently no way to remove an mpv hook once it has been added, so to minimize potential impact and/or side effects /// when not in use: - /// 1. Only add the mpv hook if `--mpv-shuffle` (or equivalent) is specified. Because this decision only happens at launch, - /// there is no risk of adding the hook more than once per player. - /// 2. Use `shufflePending` to decide if it needs to run again. Set to `false` after use, and check its value as early as possible. + /// 1. Only add the mpv hook if `--mpv-shuffle` (or equivalent) is specified via the IINA CLI or via the user's mpv options (in Advanced prefs). + /// 2. Use `isShuffleHookAlreadyAdded` to keep track of whether the hook was already added. It should be added at most once per mpv core. + /// 3. Use `shufflePending` to decide if the playlist needs to be shuffled. The hook will run each time an item in the playlist is about to start, + /// but we only want to do the shuffle before the start of the first file. So we will only shuffle if `shufflePending` is true, then set + /// `shufflePending` to `false` after shuffling. func addShufflePlaylistHook() { - $shufflePending.withLock{ $0 = true } + var mustAddHook = true + $shufflePending.withLock{ shufflePending in + shufflePending = true + + guard !isShuffleHookAlreadyAdded else { + mustAddHook = false + return + } + isShuffleHookAlreadyAdded = true + } + + guard mustAddHook else { + Logger.log("Will reuse existing on_before_start_file hook for playlist shuffle", level: .verbose) + return + } - func callback(next: @escaping () -> Void) { + func shuffleCallback(next: @escaping () -> Void) { var mustShuffle = false $shufflePending.withLock{ shufflePending in if shufflePending { @@ -1673,7 +1708,7 @@ class PlayerCore: NSObject { } } - mpv.addHook(MPVHook.onBeforeStartFile, hook: MPVHookValue(withBlock: callback)) + mpv.addHook(MPVHook.onBeforeStartFile, hook: MPVHookValue(withBlock: shuffleCallback)) } // MARK: - Listeners