mirror of
https://github.com/mpv-player/mpv
synced 2024-12-28 01:52:19 +00:00
7b09bf7ffc
Currently filenames like `EP.1.v0.1080p.mp4` do not get sorted correctly (e.g. episode 11 right after episode 1). That is caused by the `.` in front of the episode number, making it get sorted as if it were decimals. The solution is to match the whole real number or integer instead of matching the integer part and the fractional part separately. This will regress sorting of numbers with multiple commas where the length of the individual segments differs between filenames. Since those are rather uncommon, that is unlikely to be a problem (for anyone ever).
222 lines
6.3 KiB
Lua
222 lines
6.3 KiB
Lua
-- This script automatically loads playlist entries before and after the
|
|
-- the currently played file. It does so by scanning the directory a file is
|
|
-- located in when starting playback. It sorts the directory entries
|
|
-- alphabetically, and adds entries before and after the current file to
|
|
-- the internal playlist. (It stops if it would add an already existing
|
|
-- playlist entry at the same position - this makes it "stable".)
|
|
-- Add at most 5000 * 2 files when starting a file (before + after).
|
|
|
|
--[[
|
|
To configure this script use file autoload.conf in directory script-opts (the "script-opts"
|
|
directory must be in the mpv configuration directory, typically ~/.config/mpv/).
|
|
|
|
Example configuration would be:
|
|
|
|
disabled=no
|
|
images=no
|
|
videos=yes
|
|
audio=yes
|
|
ignore_hidden=yes
|
|
|
|
--]]
|
|
|
|
MAXENTRIES = 5000
|
|
|
|
local msg = require 'mp.msg'
|
|
local options = require 'mp.options'
|
|
local utils = require 'mp.utils'
|
|
|
|
o = {
|
|
disabled = false,
|
|
images = true,
|
|
videos = true,
|
|
audio = true,
|
|
ignore_hidden = true
|
|
}
|
|
options.read_options(o)
|
|
|
|
function Set (t)
|
|
local set = {}
|
|
for _, v in pairs(t) do set[v] = true end
|
|
return set
|
|
end
|
|
|
|
function SetUnion (a,b)
|
|
local res = {}
|
|
for k in pairs(a) do res[k] = true end
|
|
for k in pairs(b) do res[k] = true end
|
|
return res
|
|
end
|
|
|
|
EXTENSIONS_VIDEO = Set {
|
|
'3g2', '3gp', 'avi', 'flv', 'm2ts', 'm4v', 'mj2', 'mkv', 'mov',
|
|
'mp4', 'mpeg', 'mpg', 'ogv', 'rmvb', 'webm', 'wmv', 'y4m'
|
|
}
|
|
|
|
EXTENSIONS_AUDIO = Set {
|
|
'aiff', 'ape', 'au', 'flac', 'm4a', 'mka', 'mp3', 'oga', 'ogg',
|
|
'ogm', 'opus', 'wav', 'wma'
|
|
}
|
|
|
|
EXTENSIONS_IMAGES = Set {
|
|
'avif', 'bmp', 'gif', 'j2k', 'jp2', 'jpeg', 'jpg', 'jxl', 'png',
|
|
'svg', 'tga', 'tif', 'tiff', 'webp'
|
|
}
|
|
|
|
EXTENSIONS = Set {}
|
|
if o.videos then EXTENSIONS = SetUnion(EXTENSIONS, EXTENSIONS_VIDEO) end
|
|
if o.audio then EXTENSIONS = SetUnion(EXTENSIONS, EXTENSIONS_AUDIO) end
|
|
if o.images then EXTENSIONS = SetUnion(EXTENSIONS, EXTENSIONS_IMAGES) end
|
|
|
|
function add_files_at(index, files)
|
|
index = index - 1
|
|
local oldcount = mp.get_property_number("playlist-count", 1)
|
|
for i = 1, #files do
|
|
mp.commandv("loadfile", files[i], "append")
|
|
mp.commandv("playlist-move", oldcount + i - 1, index + i - 1)
|
|
end
|
|
end
|
|
|
|
function get_extension(path)
|
|
match = string.match(path, "%.([^%.]+)$" )
|
|
if match == nil then
|
|
return "nomatch"
|
|
else
|
|
return match
|
|
end
|
|
end
|
|
|
|
table.filter = function(t, iter)
|
|
for i = #t, 1, -1 do
|
|
if not iter(t[i]) then
|
|
table.remove(t, i)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- alphanum sorting for humans in Lua
|
|
-- http://notebook.kulchenko.com/algorithms/alphanumeric-natural-sorting-for-humans-in-lua
|
|
|
|
function alphanumsort(filenames)
|
|
local function padnum(n, d)
|
|
return #d > 0 and ("%03d%s%.12f"):format(#n, n, tonumber(d) / (10 ^ #d))
|
|
or ("%03d%s"):format(#n, n)
|
|
end
|
|
|
|
local tuples = {}
|
|
for i, f in ipairs(filenames) do
|
|
tuples[i] = {f:lower():gsub("0*(%d+)%.?(%d*)", padnum), f}
|
|
end
|
|
table.sort(tuples, function(a, b)
|
|
return a[1] == b[1] and #b[2] < #a[2] or a[1] < b[1]
|
|
end)
|
|
for i, tuple in ipairs(tuples) do filenames[i] = tuple[2] end
|
|
return filenames
|
|
end
|
|
|
|
local autoloaded = nil
|
|
|
|
function get_playlist_filenames()
|
|
local filenames = {}
|
|
for n = 0, pl_count - 1, 1 do
|
|
local filename = mp.get_property('playlist/'..n..'/filename')
|
|
local _, file = utils.split_path(filename)
|
|
filenames[file] = true
|
|
end
|
|
return filenames
|
|
end
|
|
|
|
function find_and_add_entries()
|
|
local path = mp.get_property("path", "")
|
|
local dir, filename = utils.split_path(path)
|
|
msg.trace(("dir: %s, filename: %s"):format(dir, filename))
|
|
if o.disabled then
|
|
msg.verbose("stopping: autoload disabled")
|
|
return
|
|
elseif #dir == 0 then
|
|
msg.verbose("stopping: not a local path")
|
|
return
|
|
end
|
|
|
|
pl_count = mp.get_property_number("playlist-count", 1)
|
|
-- check if this is a manually made playlist
|
|
if (pl_count > 1 and autoloaded == nil) or
|
|
(pl_count == 1 and EXTENSIONS[string.lower(get_extension(filename))] == nil) then
|
|
msg.verbose("stopping: manually made playlist")
|
|
return
|
|
else
|
|
autoloaded = true
|
|
end
|
|
|
|
local pl = mp.get_property_native("playlist", {})
|
|
local pl_current = mp.get_property_number("playlist-pos-1", 1)
|
|
msg.trace(("playlist-pos-1: %s, playlist: %s"):format(pl_current,
|
|
utils.to_string(pl)))
|
|
|
|
local files = utils.readdir(dir, "files")
|
|
if files == nil then
|
|
msg.verbose("no other files in directory")
|
|
return
|
|
end
|
|
table.filter(files, function (v, k)
|
|
-- The current file could be a hidden file, ignoring it doesn't load other
|
|
-- files from the current directory.
|
|
if (o.ignore_hidden and not (v == filename) and string.match(v, "^%.")) then
|
|
return false
|
|
end
|
|
local ext = get_extension(v)
|
|
if ext == nil then
|
|
return false
|
|
end
|
|
return EXTENSIONS[string.lower(ext)]
|
|
end)
|
|
alphanumsort(files)
|
|
|
|
if dir == "." then
|
|
dir = ""
|
|
end
|
|
|
|
-- Find the current pl entry (dir+"/"+filename) in the sorted dir list
|
|
local current
|
|
for i = 1, #files do
|
|
if files[i] == filename then
|
|
current = i
|
|
break
|
|
end
|
|
end
|
|
if current == nil then
|
|
return
|
|
end
|
|
msg.trace("current file position in files: "..current)
|
|
|
|
local append = {[-1] = {}, [1] = {}}
|
|
local filenames = get_playlist_filenames()
|
|
for direction = -1, 1, 2 do -- 2 iterations, with direction = -1 and +1
|
|
for i = 1, MAXENTRIES do
|
|
local file = files[current + i * direction]
|
|
if file == nil or file[1] == "." then
|
|
break
|
|
end
|
|
|
|
local filepath = dir .. file
|
|
-- skip files already in playlist
|
|
if filenames[file] then break end
|
|
|
|
if direction == -1 then
|
|
if pl_current == 1 then -- never add additional entries in the middle
|
|
msg.info("Prepending " .. file)
|
|
table.insert(append[-1], 1, filepath)
|
|
end
|
|
else
|
|
msg.info("Adding " .. file)
|
|
table.insert(append[1], filepath)
|
|
end
|
|
end
|
|
end
|
|
|
|
add_files_at(pl_current + 1, append[1])
|
|
add_files_at(pl_current, append[-1])
|
|
end
|
|
|
|
mp.register_event("start-file", find_and_add_entries)
|