feat: shuffle now prevents repeating same files often
Specifically, shuffle now guarantees no path/url repetition until at least 80% of the playlist/directory has been exhausted. closes #569
This commit is contained in:
@@ -384,6 +384,7 @@ state = {
|
||||
end)(),
|
||||
cwd = mp.get_property('working-directory'),
|
||||
path = nil, -- current file path or URL
|
||||
history = {}, -- history of last played files stored as full paths
|
||||
title = nil,
|
||||
alt_title = nil,
|
||||
time = nil, -- current media playback time
|
||||
@@ -680,7 +681,10 @@ end
|
||||
mp.observe_property('mouse-pos', 'native', handle_mouse_pos)
|
||||
mp.observe_property('osc', 'bool', function(name, value) if value == true then mp.set_property('osc', 'no') end end)
|
||||
mp.register_event('file-loaded', function()
|
||||
set_state('path', normalize_path(mp.get_property_native('path')))
|
||||
local path = normalize_path(mp.get_property_native('path'))
|
||||
itable_delete_value(state.history, path)
|
||||
state.history[#state.history + 1] = path
|
||||
set_state('path', path)
|
||||
Elements:flash({'top_bar'})
|
||||
end)
|
||||
mp.register_event('end-file', function(event)
|
||||
|
@@ -22,7 +22,7 @@ function Elements:remove(idOrElement)
|
||||
if element then
|
||||
if not element.destroyed then element:destroy() end
|
||||
element.enabled = false
|
||||
self.itable = itable_remove(self.itable, self[id])
|
||||
self.itable = itable_delete_value(self.itable, self[id])
|
||||
self[id] = nil
|
||||
request_render()
|
||||
end
|
||||
|
@@ -75,6 +75,13 @@ function itable_index_of(itable, value)
|
||||
end
|
||||
end
|
||||
|
||||
---@param itable table
|
||||
---@param value any
|
||||
---@return boolean
|
||||
function itable_has(itable, value)
|
||||
return itable_index_of(itable, value) ~= nil
|
||||
end
|
||||
|
||||
---@param itable table
|
||||
---@param compare fun(value: any, index: number)
|
||||
---@param from? number Where to start search, defaults to `1`.
|
||||
@@ -102,8 +109,21 @@ end
|
||||
|
||||
---@param itable table
|
||||
---@param value any
|
||||
function itable_remove(itable, value)
|
||||
return itable_filter(itable, function(item) return item ~= value end)
|
||||
function itable_delete_value(itable, value)
|
||||
for index = 1, #itable, 1 do
|
||||
if itable[index] == value then table.remove(itable, index) end
|
||||
end
|
||||
return itable
|
||||
end
|
||||
|
||||
---@param itable table
|
||||
---@param transformer fun(value: any, index: number) : any
|
||||
function itable_map(itable, transformer)
|
||||
local result = {}
|
||||
for index, value in ipairs(itable) do
|
||||
result[index] = transformer(value, index)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
---@param itable table
|
||||
|
@@ -417,28 +417,39 @@ end
|
||||
|
||||
-- Navigates in a list, using delta or, when `state.shuffle` is enabled,
|
||||
-- randomness to determine the next item. Loops around if `loop-playlist` is enabled.
|
||||
---@param list table
|
||||
---@param paths table
|
||||
---@param current_index number
|
||||
---@param delta number
|
||||
function decide_navigation_in_list(list, current_index, delta)
|
||||
if #list < 2 then return #list, list[#list] end
|
||||
function decide_navigation_in_list(paths, current_index, delta)
|
||||
if #paths < 2 then return #paths, paths[#paths] end
|
||||
|
||||
-- Shuffle looks at the played files history trimmed to 80% length of the paths
|
||||
-- and removes all paths in it from the potential shuffle pool. This guarantees
|
||||
-- no path repetition until at least 80% of the playlist has been exhausted.
|
||||
if state.shuffle then
|
||||
local new_index = current_index
|
||||
local trimmed_history = itable_slice(state.history, -math.floor(#paths * 0.8))
|
||||
local shuffle_pool = {}
|
||||
|
||||
for index, value in ipairs(paths) do
|
||||
if not itable_has(trimmed_history, value) then
|
||||
shuffle_pool[#shuffle_pool + 1] = index
|
||||
end
|
||||
end
|
||||
|
||||
math.randomseed(os.time())
|
||||
while current_index == new_index do new_index = math.random(#list) end
|
||||
return new_index, list[new_index]
|
||||
local next_index = shuffle_pool[math.random(#shuffle_pool)]
|
||||
return next_index, paths[next_index]
|
||||
end
|
||||
|
||||
local new_index = current_index + delta
|
||||
if mp.get_property_native('loop-playlist') then
|
||||
if new_index > #list then new_index = new_index % #list
|
||||
elseif new_index < 1 then new_index = #list - new_index end
|
||||
elseif new_index < 1 or new_index > #list then
|
||||
if new_index > #paths then new_index = new_index % #paths
|
||||
elseif new_index < 1 then new_index = #paths - new_index end
|
||||
elseif new_index < 1 or new_index > #paths then
|
||||
return
|
||||
end
|
||||
|
||||
return new_index, list[new_index]
|
||||
return new_index, paths[new_index]
|
||||
end
|
||||
|
||||
---@param delta number
|
||||
@@ -456,7 +467,8 @@ end
|
||||
function navigate_playlist(delta)
|
||||
local playlist, pos = mp.get_property_native('playlist'), mp.get_property_native('playlist-pos-1')
|
||||
if playlist and #playlist > 1 and pos then
|
||||
local index = decide_navigation_in_list(playlist, pos, delta)
|
||||
local paths = itable_map(playlist, function(item) return normalize_path(item.filename) end)
|
||||
local index = decide_navigation_in_list(paths, pos, delta)
|
||||
if index then mp.commandv('playlist-play-index', index - 1) return true end
|
||||
end
|
||||
return false
|
||||
|
Reference in New Issue
Block a user