Created
May 4, 2020 00:23
-
-
Save po5/ef141a3bdab121c174b5f32d26a83e80 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- 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 | |
--]] | |
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, | |
audio_no_images = false | |
} | |
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 { | |
'mkv', 'avi', 'mp4', 'ogv', 'webm', 'rmvb', 'flv', 'wmv', 'mpeg', 'mpg', 'm4v', '3gp' | |
} | |
EXTENSIONS_AUDIO = Set { | |
'mp3', 'wav', 'ogm', 'flac', 'm4a', 'wma', 'ogg', 'opus' | |
} | |
EXTENSIONS_IMAGES = Set { | |
'jpg', 'jpeg', 'png', 'tif', 'tiff', 'gif', 'webp', 'svg', 'bmp' | |
} | |
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 | |
-- splitbynum and alnumcomp from alphanum.lua (C) Andre Bogus | |
-- Released under the MIT License | |
-- http://www.davekoelle.com/files/alphanum.lua | |
-- split a string into a table of number and string values | |
function splitbynum(s) | |
local result = {} | |
for x, y in (s or ""):gmatch("(%d*)(%D*)") do | |
if x ~= "" then table.insert(result, tonumber(x)) end | |
if y ~= "" then table.insert(result, y) end | |
end | |
return result | |
end | |
function clean_key(k) | |
k = (' '..k..' '):gsub("%s+", " "):sub(2, -2):lower() | |
return splitbynum(k) | |
end | |
-- compare two strings | |
function alnumcomp(x, y) | |
local xt, yt = clean_key(x), clean_key(y) | |
for i = 1, math.min(#xt, #yt) do | |
local xe, ye = xt[i], yt[i] | |
if type(xe) == "string" then ye = tostring(ye) | |
elseif type(ye) == "string" then xe = tostring(xe) end | |
if xe ~= ye then return xe < ye end | |
end | |
return #xt < #yt | |
end | |
local autoloaded = nil | |
local remove_last = false | |
local last_dir = nil | |
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 | |
if dir ~= last_dir then | |
last_dir = dir | |
autoloaded = nil | |
remove_last = false | |
end | |
local has_audio | |
local is_image | |
if o.audio_no_images then | |
has_audio = EXTENSIONS_AUDIO[string.lower(get_extension(filename))] | |
is_image = EXTENSIONS_IMAGES[string.lower(get_extension(filename))] | |
end | |
local pl = mp.get_property_native("playlist", {}) | |
local pl_count = mp.get_property_number("playlist-count", 1) | |
local pl_current = mp.get_property_number("playlist-pos-1", 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 | |
if o.audio_no_images and not has_audio then | |
for k, v in ipairs(pl) do | |
if not string.match(v.filename, "^%.") then | |
local ext = get_extension(v.filename) | |
if ext ~= nil then | |
if EXTENSIONS_AUDIO[string.lower(ext)] then | |
has_audio = true | |
end | |
end | |
end | |
end | |
end | |
if has_audio then | |
for i = 1, #pl do | |
local ext = string.lower(get_extension(pl[#pl + 1 - i].filename)) | |
if not EXTENSIONS_AUDIO[ext] and not EXTENSIONS_VIDEO[ext] then | |
if not pl[#pl + 1 - i].current or pl_current ~= pl_count then | |
mp.commandv("playlist-remove", #pl - i) | |
else | |
remove_last = true | |
end | |
end | |
end | |
end | |
msg.verbose("stopping: manually made playlist") | |
return | |
else | |
autoloaded = true | |
end | |
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 | |
if o.audio_no_images and not has_audio then | |
for k, v in ipairs(files) do | |
if not string.match(v, "^%.") then | |
local ext = get_extension(v) | |
if ext ~= nil then | |
if EXTENSIONS_AUDIO[string.lower(ext)] then | |
has_audio = true | |
end | |
end | |
end | |
end | |
end | |
table.filter(files, function (v, k) | |
if string.match(v, "^%.") then | |
return false | |
end | |
local ext = get_extension(v) | |
if ext == nil then | |
return false | |
end | |
if o.audio_no_images and has_audio and v ~= filename and EXTENSIONS_IMAGES[string.lower(ext)] then | |
return false | |
end | |
return EXTENSIONS[string.lower(ext)] | |
end) | |
table.sort(files, alnumcomp) | |
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] = {}} | |
for direction = -1, 1, 2 do -- 2 iterations, with direction = -1 and +1 | |
for i = 1, MAXENTRIES do | |
local file = files[current + i * direction] | |
local pl_e = pl[pl_current + i * direction] | |
if file == nil or file[1] == "." then | |
break | |
end | |
local filepath = dir .. file | |
if pl_e then | |
-- If there's a playlist entry, and it's the same file, stop. | |
msg.trace(pl_e.filename.." == "..filepath.." ?") | |
if pl_e.filename == filepath then | |
break | |
end | |
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]) | |
if is_image and has_audio then | |
if next(append[1]) then | |
mp.commandv("playlist-remove", "current") | |
else | |
remove_last = true | |
end | |
end | |
end | |
function reset(k, v) | |
local pl_count = mp.get_property_number("playlist-count", 1) | |
if remove_last and v ~= pl_count then | |
mp.commandv("playlist-remove", pl_count - 1) | |
remove_last = false | |
end | |
end | |
mp.observe_property("playlist-pos-1", "number", reset) | |
mp.register_event("start-file", find_and_add_entries) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment