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:
tomasklaen
2023-05-30 20:21:03 +02:00
parent 7e1d26c7a3
commit 442fe6b2e5
4 changed files with 51 additions and 15 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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