summary refs log tree commit diff
path: root/dot_config/mpv/scripts/autoload.lua
diff options
context:
space:
mode:
authorAlen <alen@dotfiles.xyz>2025-01-19 19:06:44 +0400
committerAlen <alen@dotfiles.xyz>2025-01-19 19:06:44 +0400
commit94ad728f49ceb99ab9c75a8ac5edbfcd708066dd (patch)
tree038a430cbe4bf7223d661fb60a3408f0b56828f1 /dot_config/mpv/scripts/autoload.lua
parentf73915ba4fd151b6fc2e4a74b8c54cc7b6e23042 (diff)
Add mpv config
Diffstat (limited to 'dot_config/mpv/scripts/autoload.lua')
-rw-r--r--dot_config/mpv/scripts/autoload.lua221
1 files changed, 221 insertions, 0 deletions
diff --git a/dot_config/mpv/scripts/autoload.lua b/dot_config/mpv/scripts/autoload.lua
new file mode 100644
index 0000000..c2cac86
--- /dev/null
+++ b/dot_config/mpv/scripts/autoload.lua
@@ -0,0 +1,221 @@
+-- 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(d)
+        local dec, n = string.match(d, "(%.?)0*(.+)")
+        return #dec > 0 and ("%.12f"):format(d) or ("%s%03d%s"):format(dec, #n, n)
+    end
+
+    local tuples = {}
+    for i, f in ipairs(filenames) do
+        tuples[i] = {f:lower():gsub("%.?%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)