summaryrefslogtreecommitdiffstats
path: root/TOOLS/lua
diff options
context:
space:
mode:
Diffstat (limited to 'TOOLS/lua')
-rw-r--r--TOOLS/lua/acompressor.lua11
-rw-r--r--TOOLS/lua/ao-null-reload.lua6
-rw-r--r--TOOLS/lua/audio-hotplug-test.lua6
-rw-r--r--TOOLS/lua/autocrop.lua283
-rw-r--r--TOOLS/lua/autodeint.lua77
-rw-r--r--TOOLS/lua/autoload.lua413
-rw-r--r--TOOLS/lua/command-test.lua40
-rw-r--r--TOOLS/lua/cycle-deinterlace-pullup.lua12
-rw-r--r--TOOLS/lua/nan-test.lua6
-rw-r--r--TOOLS/lua/observe-all.lua10
-rw-r--r--TOOLS/lua/ontop-playback.lua2
-rw-r--r--TOOLS/lua/osd-test.lua2
-rw-r--r--TOOLS/lua/pause-when-minimize.lua2
-rw-r--r--TOOLS/lua/skip-logo.lua20
-rw-r--r--TOOLS/lua/status-line.lua12
-rw-r--r--TOOLS/lua/test-hooks.lua6
16 files changed, 554 insertions, 354 deletions
diff --git a/TOOLS/lua/acompressor.lua b/TOOLS/lua/acompressor.lua
index 5fc5063e8d..e94dc3e0fb 100644
--- a/TOOLS/lua/acompressor.lua
+++ b/TOOLS/lua/acompressor.lua
@@ -2,7 +2,7 @@
-- filter including key bindings for adjusting parameters.
--
-- See https://ffmpeg.org/ffmpeg-filters.html#acompressor for explanation
--- of the parameteres.
+-- of the parameters.
local mp = require 'mp'
local options = require 'mp.options'
@@ -53,8 +53,10 @@ local params = {
}
local function parse_value(value)
- -- Using nil here because tonumber differs between lua 5.1 and 5.2 when parsing fractions in combination with explicit base argument set to 10.
- -- And we can't omit it because gsub returns 2 values which would get unpacked and cause more problems. Gotta love scripting languages.
+ -- Using nil here because tonumber differs between lua 5.1 and 5.2 when
+ -- parsing fractions in combination with explicit base argument set to 10.
+ -- And we can't omit it because gsub returns 2 values which would get
+ -- unpacked and cause more problems. Gotta love scripting languages.
return tonumber(value:gsub('dB$', ''), nil)
end
@@ -76,7 +78,8 @@ local function show_osd(filter)
for _,param in ipairs(params) do
local value = parse_value(filter.params[param.name])
if not (param.hide_default and value == o['default_' .. param.name]) then
- pretty[#pretty+1] = string.format('%s: %g%s', param.name:gsub("^%l", string.upper), value, param.dB)
+ pretty[#pretty+1] = string.format('%s: %g%s', param.name:gsub("^%l", string.upper),
+ value, param.dB)
end
end
diff --git a/TOOLS/lua/ao-null-reload.lua b/TOOLS/lua/ao-null-reload.lua
index 5b2330b517..6809bb3a25 100644
--- a/TOOLS/lua/ao-null-reload.lua
+++ b/TOOLS/lua/ao-null-reload.lua
@@ -3,12 +3,14 @@
-- particular for ao=wasapi, since the internal IMMNotificationClient code that
-- normally triggers ao-reload will not be running in this case.
-function do_reload()
+local reloading
+
+local function do_reload()
mp.command("ao-reload")
reloading = nil
end
-function on_audio_device_list_change()
+local function on_audio_device_list_change()
if mp.get_property("current-ao") == "null" and not reloading then
mp.msg.verbose("audio-device-list changed: reloading audio")
-- avoid calling ao-reload too often
diff --git a/TOOLS/lua/audio-hotplug-test.lua b/TOOLS/lua/audio-hotplug-test.lua
index 8dedc68cbe..e0ef223c0c 100644
--- a/TOOLS/lua/audio-hotplug-test.lua
+++ b/TOOLS/lua/audio-hotplug-test.lua
@@ -1,8 +1,6 @@
-local utils = require("mp.utils")
-
-mp.observe_property("audio-device-list", "native", function(name, val)
+mp.observe_property("audio-device-list", "native", function(_, val)
print("Audio device list changed:")
- for index, e in ipairs(val) do
+ for _, e in ipairs(val) do
print(" - '" .. e.name .. "' (" .. e.description .. ")")
end
end)
diff --git a/TOOLS/lua/autocrop.lua b/TOOLS/lua/autocrop.lua
index af6021b75a..ba38f487cc 100644
--- a/TOOLS/lua/autocrop.lua
+++ b/TOOLS/lua/autocrop.lua
@@ -1,99 +1,71 @@
--[[
-This script uses the lavfi cropdetect filter to automatically
-insert a crop filter with appropriate parameters for the
-currently playing video.
-
-It will automatically crop the video, when playback starts.
-
-Also It registers the key-binding "C" (shift+c). You can manually
-crop the video by pressing the "C" (shift+c) key.
-
-If the "C" key is pressed again, the crop filter is removed
-restoring playback to its original state.
-
-The workflow is as follows: First, it inserts the filter
-vf=lavfi=cropdetect. After <detect_seconds> (default is 1)
-seconds, it then inserts the filter vf=crop=w:h:x:y, where
-w,h,x,y are determined from the vf-metadata gathered by
-cropdetect. The cropdetect filter is removed immediately after
-the crop filter is inserted as it is no longer needed.
-
-Since the crop parameters are determined from the 1 second of
-video between inserting the cropdetect and crop filters, the "C"
-key should be pressed at a position in the video where the crop
-region is unambiguous (i.e., not a black frame, black background
-title card, or dark scene).
-
-The default options can be overridden by adding
-script-opts-append=autocrop-<parameter>=<value> into mpv.conf
-
-List of available parameters (For default values, see <options>):
-
-auto: bool - Whether to automatically apply crop at the start of
- playback. If you don't want to crop automatically, set it to
- false or add "script-opts-append=autocrop-auto=no" into
- mpv.conf.
-
-auto_delay: seconds - Delay before starting crop in auto mode.
- You can try to increase this value to avoid dark scene or
- fade in at beginning. Automatic cropping will not occur if
- the value is larger than the remaining playback time.
-
-detect_limit: number[0-255] - Black threshold for cropdetect.
- Smaller values will generally result in less cropping.
- See limit of https://ffmpeg.org/ffmpeg-filters.html#cropdetect
-
-detect_round: number[2^n] - The value which the width/height
- should be divisible by. Smaller values ​​have better detection
- accuracy. If you have problems with other filters,
- you can try to set it to 4 or 16.
- See round of https://ffmpeg.org/ffmpeg-filters.html#cropdetect
-
-detect_min_ratio: number[0.0-1.0] - The ratio of the minimum clip
- size to the original. If the picture is over cropped or under
- cropped, try adjusting this value.
-
-detect_seconds: seconds - How long to gather cropdetect data.
- Increasing this may be desirable to allow cropdetect more
- time to collect data.
---]]
+This script uses the lavfi cropdetect filter and the video-crop property to
+automatically crop the currently playing video with appropriate parameters.
+
+It automatically crops the video when playback starts.
+
+You can also manually crop the video by pressing the "C" (shift+c) key.
+Pressing it again undoes the crop.
+
+The workflow is as follows: First, it inserts the cropdetect filter. After
+<detect_seconds> (default is 1) seconds, it then sets video-crop based on the
+vf-metadata values gathered by cropdetect. The cropdetect filter is removed
+after video-crop is set as it is no longer needed.
+
+Since the crop parameters are determined from the 1 second of video between
+inserting the cropdetect filter and setting video-crop, the "C" key should be
+pressed at a position in the video where the crop region is unambiguous (i.e.,
+not a black frame, black background title card, or dark scene).
-require "mp.msg"
-require 'mp.options'
+If non-copy-back hardware decoding is in use, hwdec is temporarily disabled for
+the duration of cropdetect as the filter would fail otherwise.
+These are the default options. They can be overridden by adding
+script-opts-append=autocrop-<parameter>=<value> to mpv.conf.
+--]]
local options = {
+ -- Whether to automatically apply crop at the start of playback. If you
+ -- don't want to crop automatically, add
+ -- script-opts-append=autocrop-auto=no to mpv.conf.
auto = true,
+ -- Delay before starting crop in auto mode. You can try to increase this
+ -- value to avoid dark scenes or fade ins at beginning. Automatic cropping
+ -- will not occur if the value is larger than the remaining playback time.
auto_delay = 4,
+ -- Black threshold for cropdetect. Smaller values will generally result in
+ -- less cropping. See limit of
+ -- https://ffmpeg.org/ffmpeg-filters.html#cropdetect
detect_limit = "24/255",
+ -- The value which the width/height should be divisible by. Smaller
+ -- values have better detection accuracy. If you have problems with
+ -- other filters, you can try to set it to 4 or 16. See round of
+ -- https://ffmpeg.org/ffmpeg-filters.html#cropdetect
detect_round = 2,
+ -- The ratio of the minimum clip size to the original. A number from 0 to
+ -- 1. If the picture is over cropped, try adjusting this value.
detect_min_ratio = 0.5,
- detect_seconds = 1
+ -- How long to gather cropdetect data. Increasing this may be desirable to
+ -- allow cropdetect more time to collect data.
+ detect_seconds = 1,
+ -- Whether the OSD shouldn't be used when cropdetect and video-crop are
+ -- applied and removed.
+ suppress_osd = false,
}
-read_options(options)
-local label_prefix = mp.get_script_name()
-local labels = {
- crop = string.format("%s-crop", label_prefix),
- cropdetect = string.format("%s-cropdetect", label_prefix)
-}
+require "mp.options".read_options(options)
-timers = {
+local cropdetect_label = mp.get_script_name() .. "-cropdetect"
+
+local timers = {
auto_delay = nil,
detect_crop = nil
}
-function is_filter_present(label)
- local filters = mp.get_property_native("vf")
- for index, filter in pairs(filters) do
- if filter["label"] == label then
- return true
- end
- end
- return false
-end
+local hwdec_backup
-function is_enough_time(seconds)
+local command_prefix = options.suppress_osd and 'no-osd' or ''
+local function is_enough_time(seconds)
-- Plus 1 second for deviation.
local time_needed = seconds + 1
local playtime_remaining = mp.get_property_native("playtime-remaining")
@@ -101,80 +73,82 @@ function is_enough_time(seconds)
return playtime_remaining and time_needed < playtime_remaining
end
-function is_cropable()
- local vid = mp.get_property_native("vid")
- local is_album = vid and mp.get_property_native(
- string.format("track-list/%d/albumart", vid)
- ) or false
+local function is_cropable(time_needed)
+ if mp.get_property_native('current-tracks/video/image') ~= false then
+ mp.msg.warn("autocrop only works for videos.")
+ return false
+ end
+
+ if not is_enough_time(time_needed) then
+ mp.msg.warn("Not enough time to detect crop.")
+ return false
+ end
- return vid and not is_album
+ return true
end
-function remove_filter(label)
- if is_filter_present(label) then
- mp.command(string.format('vf remove @%s', label))
- return true
+local function remove_cropdetect()
+ for _, filter in pairs(mp.get_property_native("vf")) do
+ if filter.label == cropdetect_label then
+ mp.command(
+ string.format("%s vf remove @%s", command_prefix, filter.label))
+
+ return
+ end
end
- return false
end
-function cleanup()
-
- -- Remove all existing filters.
- for key, value in pairs(labels) do
- remove_filter(value)
+local function restore_hwdec()
+ if hwdec_backup then
+ mp.set_property("hwdec", hwdec_backup)
+ hwdec_backup = nil
end
+end
+
+local function cleanup()
+ remove_cropdetect()
-- Kill all timers.
for index, timer in pairs(timers) do
if timer then
timer:kill()
- timer = nil
+ timers[index] = nil
end
end
+
+ restore_hwdec()
end
-function detect_crop()
+local function apply_crop(meta)
+ -- Verify if it is necessary to crop.
+ local is_effective = meta.w and meta.h and meta.x and meta.y and
+ (meta.x > 0 or meta.y > 0
+ or meta.w < meta.max_w or meta.h < meta.max_h)
- -- If it's not cropable, exit.
- if not is_cropable() then
- mp.msg.warn("autocrop only works for videos.")
- return
+ -- Verify it is not over cropped.
+ local is_excessive = false
+ if is_effective and (meta.w < meta.min_w or meta.h < meta.min_h) then
+ mp.msg.info("The area to be cropped is too large.")
+ mp.msg.info("You might need to decrease detect_min_ratio.")
+ is_excessive = true
end
- -- Verify if there is enough time to detect crop.
- local time_needed = options.detect_seconds
-
- if not is_enough_time(time_needed) then
- mp.msg.warn("Not enough time to detect crop.")
+ if not is_effective or is_excessive then
+ -- Clear any existing crop.
+ mp.command(string.format("%s set file-local-options/video-crop ''", command_prefix))
return
end
- -- Insert the cropdetect filter.
- local limit = options.detect_limit
- local round = options.detect_round
-
- mp.command(
- string.format(
- 'vf pre @%s:cropdetect=limit=%s:round=%d:reset=0',
- labels.cropdetect, limit, round
- )
- )
-
- -- Wait to gather data.
- timers.detect_crop = mp.add_timeout(time_needed, detect_end)
+ -- Apply crop.
+ mp.command(string.format("%s set file-local-options/video-crop %sx%s+%s+%s",
+ command_prefix, meta.w, meta.h, meta.x, meta.y))
end
-function detect_end()
-
+local function detect_end()
-- Get the metadata and remove the cropdetect filter.
- local cropdetect_metadata =
- mp.get_property_native(
- string.format("vf-metadata/%s",
- labels.cropdetect
- )
- )
- remove_filter(labels.cropdetect)
+ local cropdetect_metadata = mp.get_property_native(
+ "vf-metadata/" .. cropdetect_label)
+ remove_cropdetect()
-- Remove the timer of detect crop.
if timers.detect_crop then
@@ -182,7 +156,9 @@ function detect_end()
timers.detect_crop = nil
end
- local meta = {}
+ restore_hwdec()
+
+ local meta
-- Verify the existence of metadata.
if cropdetect_metadata then
@@ -195,7 +171,7 @@ function detect_end()
else
mp.msg.error("No crop data.")
mp.msg.info("Was the cropdetect filter successfully inserted?")
- mp.msg.info("Does your version of ffmpeg/libav support AVFrame metadata?")
+ mp.msg.info("Does your version of FFmpeg support AVFrame metadata?")
return
end
@@ -217,44 +193,41 @@ function detect_end()
else
mp.msg.error("Got empty crop data.")
mp.msg.info("You might need to increase detect_seconds.")
- return
end
apply_crop(meta)
end
-function apply_crop(meta)
-
- -- Verify if it is necessary to crop.
- local is_effective = meta.x > 0 or meta.y > 0
- or meta.w < meta.max_w or meta.h < meta.max_h
+local function detect_crop()
+ local time_needed = options.detect_seconds
- if not is_effective then
- mp.msg.info("No area detected for cropping.")
+ if not is_cropable(time_needed) then
return
end
- -- Verify it is not over cropped.
- local is_excessive = meta.w < meta.min_w and meta.h < meta.min_h
-
- if is_excessive then
- mp.msg.info("The area to be cropped is too large.")
- mp.msg.info("You might need to decrease detect_min_ratio.")
- return
+ local hwdec_current = mp.get_property("hwdec-current")
+ if hwdec_current:find("-copy$") == nil and hwdec_current ~= "no" and
+ hwdec_current ~= "crystalhd" and hwdec_current ~= "rkmpp" then
+ hwdec_backup = mp.get_property("hwdec")
+ mp.set_property("hwdec", "no")
end
- -- Remove existing crop.
- remove_filter(labels.crop)
+ -- Insert the cropdetect filter.
+ local limit = options.detect_limit
+ local round = options.detect_round
- -- Apply crop.
mp.command(
- string.format("vf pre @%s:lavfi-crop=w=%s:h=%s:x=%s:y=%s",
- labels.crop, meta.w, meta.h, meta.x, meta.y
+ string.format(
+ '%s vf pre @%s:cropdetect=limit=%s:round=%d:reset=0',
+ command_prefix, cropdetect_label, limit, round
)
)
+
+ -- Wait to gather data.
+ timers.detect_crop = mp.add_timeout(time_needed, detect_end)
end
-function on_start()
+local function on_start()
-- Clean up at the beginning.
cleanup()
@@ -275,8 +248,7 @@ function on_start()
-- Verify if there is enough time for autocrop.
local time_needed = options.auto_delay + options.detect_seconds
- if not is_enough_time(time_needed) then
- mp.msg.warn("Not enough time for autocrop.")
+ if not is_cropable(time_needed) then
return
end
@@ -294,7 +266,7 @@ function on_start()
end
end
-function on_toggle()
+local function on_toggle()
-- If it is during auto_delay, kill the timer.
if timers.auto_delay then
@@ -303,7 +275,8 @@ function on_toggle()
end
-- Cropped => Remove it.
- if remove_filter(labels.crop) then
+ if mp.get_property("video-crop") ~= "" then
+ mp.command(string.format("%s set file-local-options/video-crop ''", command_prefix))
return
end
@@ -313,7 +286,7 @@ function on_toggle()
return
end
- -- Neither => Do delectcrop.
+ -- Neither => Detect crop.
detect_crop()
end
diff --git a/TOOLS/lua/autodeint.lua b/TOOLS/lua/autodeint.lua
index b891c9a838..df938f8318 100644
--- a/TOOLS/lua/autodeint.lua
+++ b/TOOLS/lua/autodeint.lua
@@ -8,7 +8,7 @@
-- telecined and the interlacing field dominance.
--
-- Based on this information, it may set mpv's ``deinterlace`` property (which
--- usually inserts the yadif filter), or insert the ``pullup`` filter if the
+-- usually inserts the bwdif filter), or insert the ``pullup`` filter if the
-- content is telecined. It also sets field dominance with lavfi setfield.
--
-- OPTIONS:
@@ -26,19 +26,21 @@
require "mp.msg"
-script_name = mp.get_script_name()
-detect_label = string.format("%s-detect", script_name)
-pullup_label = string.format("%s", script_name)
-dominance_label = string.format("%s-dominance", script_name)
-ivtc_detect_label = string.format("%s-ivtc-detect", script_name)
+local script_name = mp.get_script_name()
+local detect_label = string.format("%s-detect", script_name)
+local pullup_label = string.format("%s", script_name)
+local dominance_label = string.format("%s-dominance", script_name)
+local ivtc_detect_label = string.format("%s-ivtc-detect", script_name)
+local timer
+local progressive, interlaced_tff, interlaced_bff, interlaced = 0, 1, 2, 3
-- number of seconds to gather cropdetect data
-detect_seconds = tonumber(mp.get_opt(string.format("%s.detect_seconds", script_name)))
+local detect_seconds = tonumber(mp.get_opt(string.format("%s.detect_seconds", script_name)))
if not detect_seconds then
detect_seconds = 4
end
-function del_filter_if_present(label)
+local function del_filter_if_present(label)
-- necessary because mp.command('vf del @label:filter') raises an
-- error if the filter doesn't exist
local vfs = mp.get_property_native("vf")
@@ -57,39 +59,13 @@ local function add_vf(label, filter)
return mp.command(('vf add @%s:%s'):format(label, filter))
end
-function start_detect()
- -- exit if detection is already in progress
- if timer then
- mp.msg.warn("already detecting!")
- return
- end
-
- mp.set_property("deinterlace","no")
- del_filter_if_present(pullup_label)
- del_filter_if_present(dominance_label)
-
- -- insert the detection filters
- if not (add_vf(detect_label, 'idet') and
- add_vf(dominance_label, 'setfield=mode=auto') and
- add_vf(pullup_label, 'lavfi-pullup') and
- add_vf(ivtc_detect_label, 'idet')) then
- mp.msg.error("failed to insert detection filters")
- return
- end
-
- -- wait to gather data
- timer = mp.add_timeout(detect_seconds, select_filter)
-end
-
-function stop_detect()
+local function stop_detect()
del_filter_if_present(detect_label)
del_filter_if_present(ivtc_detect_label)
timer = nil
end
-progressive, interlaced_tff, interlaced_bff, interlaced = 0, 1, 2, 3, 4
-
-function judge(label)
+local function judge(label)
-- get the metadata
local result = mp.get_property_native(string.format("vf-metadata/%s", label))
local num_tff = tonumber(result["lavfi.idet.multiple.tff"])
@@ -118,7 +94,7 @@ function judge(label)
end
end
-function select_filter()
+local function select_filter()
-- handle the first detection filter results
local verdict = judge(detect_label)
local ivtc_verdict = judge(ivtc_detect_label)
@@ -146,11 +122,36 @@ function select_filter()
mp.msg.info(string.format("telecined with %s field dominance: using pullup", dominance))
stop_detect()
else
- mp.msg.info(string.format("interlaced with %s field dominance: setting deinterlace property", dominance))
+ mp.msg.info("interlaced with " .. dominance ..
+ " field dominance: setting deinterlace property")
del_filter_if_present(pullup_label)
mp.set_property("deinterlace","yes")
stop_detect()
end
end
+local function start_detect()
+ -- exit if detection is already in progress
+ if timer then
+ mp.msg.warn("already detecting!")
+ return
+ end
+
+ mp.set_property("deinterlace","no")
+ del_filter_if_present(pullup_label)
+ del_filter_if_present(dominance_label)
+
+ -- insert the detection filters
+ if not (add_vf(detect_label, 'idet') and
+ add_vf(dominance_label, 'setfield=mode=auto') and
+ add_vf(pullup_label, 'lavfi-pullup') and
+ add_vf(ivtc_detect_label, 'idet')) then
+ mp.msg.error("failed to insert detection filters")
+ return
+ end
+
+ -- wait to gather data
+ timer = mp.add_timeout(detect_seconds, select_filter)
+end
+
mp.add_key_binding("ctrl+d", script_name, start_detect)
diff --git a/TOOLS/lua/autoload.lua b/TOOLS/lua/autoload.lua
index 7150abb95e..dedfc64e85 100644
--- a/TOOLS/lua/autoload.lua
+++ b/TOOLS/lua/autoload.lua
@@ -1,5 +1,5 @@
-- This script automatically loads playlist entries before and after the
--- the currently played file. It does so by scanning the directory a file is
+-- 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
@@ -10,138 +10,330 @@
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/).
+Option `ignore_patterns` is a comma-separated list of patterns (see lua.org/pil/20.2.html).
+Additionally to the standard lua patterns, you can also escape commas with `%`,
+for example, the option `bak%,x%,,another` will be resolved as patterns `bak,x,` and `another`.
+But it does not mean you need to escape all lua patterns twice,
+so the option `bak%%,%.mp4,` will be resolved as two patterns `bak%%` and `%.mp4`.
+
Example configuration would be:
disabled=no
images=no
videos=yes
audio=yes
+additional_image_exts=list,of,ext
+additional_video_exts=list,of,ext
+additional_audio_exts=list,of,ext
+ignore_hidden=yes
+same_type=yes
+directory_mode=recursive
+ignore_patterns=^~,^bak-,%.bak$
--]]
-MAXENTRIES = 5000
+local MAX_ENTRIES = 5000
+local MAX_DIR_STACK = 20
local msg = require 'mp.msg'
local options = require 'mp.options'
local utils = require 'mp.utils'
-o = {
+local o = {
disabled = false,
images = true,
videos = true,
- audio = true
+ audio = true,
+ additional_image_exts = "",
+ additional_video_exts = "",
+ additional_audio_exts = "",
+ ignore_hidden = true,
+ same_type = false,
+ directory_mode = "auto",
+ ignore_patterns = ""
}
-options.read_options(o)
-function Set (t)
+local 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'
+local EXTENSIONS_VIDEO_DEFAULT = Set {
+ '3g2', '3gp', 'avi', 'flv', 'm2ts', 'm4v', 'mj2', 'mkv', 'mov',
+ 'mp4', 'mpeg', 'mpg', 'ogv', 'rmvb', 'webm', 'wmv', 'y4m'
}
-EXTENSIONS_AUDIO = Set {
- 'mp3', 'wav', 'ogm', 'flac', 'm4a', 'wma', 'ogg', 'opus'
+local EXTENSIONS_AUDIO_DEFAULT = Set {
+ 'aiff', 'ape', 'au', 'flac', 'm4a', 'mka', 'mp3', 'oga', 'ogg',
+ 'ogm', 'opus', 'wav', 'wma'
}
-EXTENSIONS_IMAGES = Set {
- 'jpg', 'jpeg', 'png', 'tif', 'tiff', 'gif', 'webp', 'svg', 'bmp'
+local EXTENSIONS_IMAGES_DEFAULT = 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
+local EXTENSIONS, EXTENSIONS_VIDEO, EXTENSIONS_AUDIO, EXTENSIONS_IMAGES
+
+local function SetUnion(a, b)
+ for k in pairs(b) do a[k] = true end
+ return a
+end
+
+-- Returns first and last positions in string or past-to-end indices
+local function FindOrPastTheEnd(string, pattern, start_at)
+ local pos1, pos2 = string:find(pattern, start_at)
+ return pos1 or #string + 1,
+ pos2 or #string + 1
+end
+
+local function Split(list)
+ local set = {}
+
+ local item_pos = 1
+ local item = ""
+
+ while item_pos <= #list do
+ local pos1, pos2 = FindOrPastTheEnd(list, "%%*,", item_pos)
+
+ local pattern_length = pos2 - pos1
+ local is_comma_escaped = pattern_length % 2
-function add_files_at(index, files)
- index = index - 1
+ local pos_before_escape = pos1 - 1
+ local item_escape_count = pattern_length - is_comma_escaped
+
+ item = item .. string.sub(list, item_pos, pos_before_escape + item_escape_count)
+
+ if is_comma_escaped == 1 then
+ item = item .. ","
+ else
+ set[item] = true
+ item = ""
+ end
+
+ item_pos = pos2 + 1
+ end
+
+ set[item] = true
+
+ -- exclude empty items
+ set[""] = nil
+
+ return set
+end
+
+local function split_option_exts(video, audio, image)
+ if video then o.additional_video_exts = Split(o.additional_video_exts) end
+ if audio then o.additional_audio_exts = Split(o.additional_audio_exts) end
+ if image then o.additional_image_exts = Split(o.additional_image_exts) end
+end
+
+local function split_patterns()
+ o.ignore_patterns = Split(o.ignore_patterns)
+end
+
+local function create_extensions()
+ EXTENSIONS = {}
+ EXTENSIONS_VIDEO = {}
+ EXTENSIONS_AUDIO = {}
+ EXTENSIONS_IMAGES = {}
+ if o.videos then
+ SetUnion(SetUnion(EXTENSIONS_VIDEO, EXTENSIONS_VIDEO_DEFAULT), o.additional_video_exts)
+ SetUnion(EXTENSIONS, EXTENSIONS_VIDEO)
+ end
+ if o.audio then
+ SetUnion(SetUnion(EXTENSIONS_AUDIO, EXTENSIONS_AUDIO_DEFAULT), o.additional_audio_exts)
+ SetUnion(EXTENSIONS, EXTENSIONS_AUDIO)
+ end
+ if o.images then
+ SetUnion(SetUnion(EXTENSIONS_IMAGES, EXTENSIONS_IMAGES_DEFAULT), o.additional_image_exts)
+ SetUnion(EXTENSIONS, EXTENSIONS_IMAGES)
+ end
+end
+
+local function validate_directory_mode()
+ if o.directory_mode ~= "recursive" and o.directory_mode ~= "lazy"
+ and o.directory_mode ~= "ignore" then
+ o.directory_mode = nil
+ end
+end
+
+options.read_options(o, nil, function(list)
+ split_option_exts(list.additional_video_exts, list.additional_audio_exts,
+ list.additional_image_exts)
+ if list.videos or list.additional_video_exts or
+ list.audio or list.additional_audio_exts or
+ list.images or list.additional_image_exts then
+ create_extensions()
+ end
+ if list.directory_mode then
+ validate_directory_mode()
+ end
+ if list.ignore_patterns then
+ split_patterns()
+ end
+end)
+
+split_option_exts(true, true, true)
+split_patterns()
+create_extensions()
+validate_directory_mode()
+
+local function add_files(files)
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)
+ mp.commandv("loadfile", files[i][1], "append")
+ mp.commandv("playlist-move", oldcount + i - 1, files[i][2])
end
end
-function get_extension(path)
- match = string.match(path, "%.([^%.]+)$" )
- if match == nil then
- return "nomatch"
- else
- return match
- end
+local function get_extension(path)
+ return path:match("%.([^%.]+)$") or "nomatch"
end
-table.filter = function(t, iter)
- for i = #t, 1, -1 do
- if not iter(t[i]) then
- table.remove(t, i)
+local function is_ignored(file)
+ for pattern in pairs(o.ignore_patterns) do
+ if file:match(pattern) then
+ return true
end
end
+ return false
end
--- splitbynum and alnumcomp from alphanum.lua (C) Andre Bogus
--- Released under the MIT License
--- http://www.davekoelle.com/files/alphanum.lua
+-- alphanum sorting for humans in Lua
+-- http://notebook.kulchenko.com/algorithms/alphanumeric-natural-sorting-for-humans-in-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
+local 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
- return result
-end
-function clean_key(k)
- k = (' '..k..' '):gsub("%s+", " "):sub(2, -2):lower()
- return splitbynum(k)
+ 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
--- 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
+local autoloaded
+local added_entries = {}
+local autoloaded_dir
+
+local function scan_dir(path, current_file, dir_mode, separator, dir_depth, total_files, extensions)
+ if dir_depth == MAX_DIR_STACK then
+ return
+ end
+ msg.trace("scanning: " .. path)
+ local files = utils.readdir(path, "files") or {}
+ local dirs = dir_mode ~= "ignore" and utils.readdir(path, "dirs") or {}
+ local prefix = path == "." and "" or path
+
+ local function filter(t, iter)
+ for i = #t, 1, -1 do
+ if not iter(t[i]) then
+ table.remove(t, i)
+ end
+ end
+ end
+
+ filter(files, function(v)
+ -- Always accept current file
+ local current = prefix .. v == current_file
+ if current then
+ return true
+ end
+ if o.ignore_hidden and v:match("^%.") then