🔧 Updates to configs for mpv
This commit is contained in:
parent
ab6733fe95
commit
8789ffc5e8
3 changed files with 3570 additions and 4191 deletions
74
.config/mpv/lua-settings/mpv_thumbnail_script.conf
Normal file
74
.config/mpv/lua-settings/mpv_thumbnail_script.conf
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
# The thumbnail cache directory.
|
||||||
|
# On Windows this defaults to %TEMP%\mpv_thumbs_cache,
|
||||||
|
# and on other platforms to /tmp/mpv_thumbs_cache.
|
||||||
|
# The directory will be created automatically, but must be writeable!
|
||||||
|
# Use absolute paths, and take note that environment variables like %TEMP% are unsupported (despite the default)!
|
||||||
|
cache_directory=/tmp/my_mpv_thumbnails
|
||||||
|
# THIS IS NOT A WINDOWS PATH. COMMENT IT OUT OR ADJUST IT YOURSELF.
|
||||||
|
|
||||||
|
# Whether to generate thumbnails automatically on video load, without a keypress
|
||||||
|
# Defaults to yes
|
||||||
|
# autogenerate=[yes/no]
|
||||||
|
autogenerate=yes
|
||||||
|
|
||||||
|
# Only automatically thumbnail videos shorter than this (in seconds)
|
||||||
|
# You will have to press T (or your own keybind) to enable the thumbnail previews
|
||||||
|
# Set to 0 to disable the check, ie. thumbnail videos no matter how long they are
|
||||||
|
# Defaults to 3600 (one hour)
|
||||||
|
autogenerate_max_duration=3600
|
||||||
|
|
||||||
|
# Use mpv to generate thumbnail even if ffmpeg is found in PATH
|
||||||
|
# ffmpeg is slightly faster than mpv but lacks support for ordered chapters in MKVs,
|
||||||
|
# which can break the resulting thumbnails. You have been warned.
|
||||||
|
# Defaults to yes (don't use ffmpeg)
|
||||||
|
# prefer_mpv=[yes/no]
|
||||||
|
prefer_mpv=yes
|
||||||
|
|
||||||
|
# Explicitly disable subtitles on the mpv sub-calls
|
||||||
|
# mpv can and will by default render subtitles into the thumbnails.
|
||||||
|
# If this is not what you wish, set mpv_no_sub to yes
|
||||||
|
# Defaults to no
|
||||||
|
mpv_no_sub=yes
|
||||||
|
|
||||||
|
# Enable to disable the built-in keybind ("T") to add your own, see after the block
|
||||||
|
disable_keybinds=no
|
||||||
|
|
||||||
|
# The maximum dimensions of the thumbnails, in pixels
|
||||||
|
# Defaults to 200 and 200
|
||||||
|
thumbnail_width=200
|
||||||
|
thumbnail_height=200
|
||||||
|
|
||||||
|
# The thumbnail count target
|
||||||
|
# (This will result in a thumbnail every ~10 seconds for a 25 minute video)
|
||||||
|
thumbnail_count=150
|
||||||
|
|
||||||
|
# The above target count will be adjusted by the minimum and
|
||||||
|
# maximum time difference between thumbnails.
|
||||||
|
# The thumbnail_count will be used to calculate a target separation,
|
||||||
|
# and min/max_delta will be used to constrict it.
|
||||||
|
|
||||||
|
# In other words, thumbnails will be:
|
||||||
|
# - at least min_delta seconds apart (limiting the amount)
|
||||||
|
# - at most max_delta seconds apart (raising the amount if needed)
|
||||||
|
# Defaults to 5 and 90, values are seconds
|
||||||
|
min_delta=5
|
||||||
|
max_delta=90
|
||||||
|
# 120 seconds aka 2 minutes will add more thumbnails only when the video is over 5 hours long!
|
||||||
|
|
||||||
|
# Below are overrides for remote urls (you generally want less thumbnails, because it's slow!)
|
||||||
|
# Thumbnailing network paths will be done with mpv (leveraging youtube-dl)
|
||||||
|
|
||||||
|
# Allow thumbnailing network paths (naive check for "://")
|
||||||
|
# Defaults to no
|
||||||
|
# thumbnail_network=[yes/no]
|
||||||
|
thumbnail_network=no
|
||||||
|
# Override thumbnail count, min/max delta, as above
|
||||||
|
remote_thumbnail_count=60
|
||||||
|
remote_min_delta=15
|
||||||
|
remote_max_delta=120
|
||||||
|
|
||||||
|
# Try to grab the raw stream and disable ytdl for the mpv subcalls
|
||||||
|
# Much faster than passing the url to ytdl again, but may cause problems with some sites
|
||||||
|
# Defaults to yes
|
||||||
|
# remote_direct_stream=[yes/no]
|
||||||
|
remote_direct_stream=no
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -13,26 +13,26 @@
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
]]--
|
]]
|
||||||
|
--
|
||||||
--[[
|
--[[
|
||||||
mpv_thumbnail_script.lua unknown - commit unknown (branch unknown)
|
mpv_thumbnail_script.lua 0.4.2 - commit a2de250 (branch master)
|
||||||
https://github.com/TheAMM/mpv_thumbnail_script
|
https://github.com/TheAMM/mpv_thumbnail_script
|
||||||
Built on 2023-11-14 05:54:49
|
Built on 2018-02-07 20:36:54
|
||||||
]]--
|
]]
|
||||||
local assdraw = require 'mp.assdraw'
|
--
|
||||||
local msg = require 'mp.msg'
|
local assdraw = require("mp.assdraw")
|
||||||
local opt = require 'mp.options'
|
local msg = require("mp.msg")
|
||||||
local utils = require 'mp.utils'
|
local opt = require("mp.options")
|
||||||
|
local utils = require("mp.utils")
|
||||||
|
|
||||||
-- Determine if the platform is Windows --
|
-- Determine platform --
|
||||||
ON_WINDOWS = (package.config:sub(1,1) ~= '/')
|
ON_WINDOWS = (package.config:sub(1, 1) ~= "/")
|
||||||
|
|
||||||
-- Determine if the platform is MacOS --
|
|
||||||
local uname = io.popen("uname -s"):read("*l")
|
|
||||||
ON_MAC = not ON_WINDOWS and (uname == "Mac" or uname == "Darwin")
|
|
||||||
|
|
||||||
-- Some helper functions needed to parse the options --
|
-- Some helper functions needed to parse the options --
|
||||||
function isempty(v) return not v or (v == "") or (v == 0) or (type(v) == "table" and not next(v)) end
|
function isempty(v)
|
||||||
|
return (v == false) or (v == nil) or (v == "") or (v == 0) or (type(v) == "table" and next(v) == nil)
|
||||||
|
end
|
||||||
|
|
||||||
function divmod(a, b)
|
function divmod(a, b)
|
||||||
return math.floor(a / b), a % b
|
return math.floor(a / b), a % b
|
||||||
|
|
@ -45,7 +45,7 @@ end
|
||||||
|
|
||||||
function join_paths(...)
|
function join_paths(...)
|
||||||
local sep = ON_WINDOWS and "\\" or "/"
|
local sep = ON_WINDOWS and "\\" or "/"
|
||||||
local result = "";
|
local result = ""
|
||||||
for i, p in pairs({ ... }) do
|
for i, p in pairs({ ... }) do
|
||||||
if p ~= "" then
|
if p ~= "" then
|
||||||
if is_absolute_path(p) then
|
if is_absolute_path(p) then
|
||||||
|
|
@ -61,9 +61,9 @@ end
|
||||||
-- /some/path/file.ext -> /some/path, file.ext
|
-- /some/path/file.ext -> /some/path, file.ext
|
||||||
function split_path(path)
|
function split_path(path)
|
||||||
local sep = ON_WINDOWS and "\\" or "/"
|
local sep = ON_WINDOWS and "\\" or "/"
|
||||||
local first_index, last_index = path:find('^.*' .. sep)
|
local first_index, last_index = path:find("^.*" .. sep)
|
||||||
|
|
||||||
if not last_index then
|
if last_index == nil then
|
||||||
return "", path
|
return "", path
|
||||||
else
|
else
|
||||||
local dir = path:sub(0, last_index - 1)
|
local dir = path:sub(0, last_index - 1)
|
||||||
|
|
@ -81,7 +81,9 @@ end
|
||||||
|
|
||||||
function Set(source)
|
function Set(source)
|
||||||
local set = {}
|
local set = {}
|
||||||
for _, l in ipairs(source) do set[l] = true end
|
for _, l in ipairs(source) do
|
||||||
|
set[l] = true
|
||||||
|
end
|
||||||
return set
|
return set
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -111,10 +113,10 @@ end
|
||||||
|
|
||||||
function file_exists(name)
|
function file_exists(name)
|
||||||
local f = io.open(name, "rb")
|
local f = io.open(name, "rb")
|
||||||
if f then
|
if f ~= nil then
|
||||||
local ok, err, code = f:read(1)
|
local ok, err, code = f:read(1)
|
||||||
io.close(f)
|
io.close(f)
|
||||||
return not code
|
return code == nil
|
||||||
else
|
else
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
@ -122,7 +124,7 @@ end
|
||||||
|
|
||||||
function path_exists(name)
|
function path_exists(name)
|
||||||
local f = io.open(name, "rb")
|
local f = io.open(name, "rb")
|
||||||
if f then
|
if f ~= nil then
|
||||||
io.close(f)
|
io.close(f)
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
|
|
@ -166,7 +168,7 @@ local ExecutableFinder = { path_cache = {} }
|
||||||
function ExecutableFinder:get_executable_path(name, raw_name)
|
function ExecutableFinder:get_executable_path(name, raw_name)
|
||||||
name = ON_WINDOWS and not raw_name and (name .. ".exe") or name
|
name = ON_WINDOWS and not raw_name and (name .. ".exe") or name
|
||||||
|
|
||||||
if not self.path_cache[name] then
|
if self.path_cache[name] == nil then
|
||||||
self.path_cache[name] = find_executable(name) or false
|
self.path_cache[name] = find_executable(name) or false
|
||||||
end
|
end
|
||||||
return self.path_cache[name]
|
return self.path_cache[name]
|
||||||
|
|
@ -174,8 +176,8 @@ end
|
||||||
|
|
||||||
-- Format seconds to HH.MM.SS.sss
|
-- Format seconds to HH.MM.SS.sss
|
||||||
function format_time(seconds, sep, decimals)
|
function format_time(seconds, sep, decimals)
|
||||||
decimals = decimals or 3
|
decimals = decimals == nil and 3 or decimals
|
||||||
sep = sep or "."
|
sep = sep and sep or "."
|
||||||
local s = seconds
|
local s = seconds
|
||||||
local h, s = divmod(s, 60 * 60)
|
local h, s = divmod(s, 60 * 60)
|
||||||
local m, s = divmod(s, 60)
|
local m, s = divmod(s, 60)
|
||||||
|
|
@ -187,8 +189,8 @@ end
|
||||||
|
|
||||||
-- Format seconds to 1h 2m 3.4s
|
-- Format seconds to 1h 2m 3.4s
|
||||||
function format_time_hms(seconds, sep, decimals, force_full)
|
function format_time_hms(seconds, sep, decimals, force_full)
|
||||||
decimals = decimals or 1
|
decimals = decimals == nil and 1 or decimals
|
||||||
sep = sep or " "
|
sep = sep ~= nil and sep or " "
|
||||||
|
|
||||||
local s = seconds
|
local s = seconds
|
||||||
local h, s = divmod(s, 60 * 60)
|
local h, s = divmod(s, 60 * 60)
|
||||||
|
|
@ -272,7 +274,6 @@ function create_temporary_file(base, mode, suffix)
|
||||||
return handle, filename
|
return handle, filename
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function get_processor_count()
|
function get_processor_count()
|
||||||
local proc_count
|
local proc_count
|
||||||
|
|
||||||
|
|
@ -280,15 +281,17 @@ function get_processor_count()
|
||||||
proc_count = tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
|
proc_count = tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
|
||||||
else
|
else
|
||||||
local cpuinfo_handle = io.open("/proc/cpuinfo")
|
local cpuinfo_handle = io.open("/proc/cpuinfo")
|
||||||
if cpuinfo_handle then
|
if cpuinfo_handle ~= nil then
|
||||||
local cpuinfo_contents = cpuinfo_handle:read("*a")
|
local cpuinfo_contents = cpuinfo_handle:read("*a")
|
||||||
local _, replace_count = cpuinfo_contents:gsub('processor', '')
|
local _, replace_count = cpuinfo_contents:gsub("processor", "")
|
||||||
proc_count = replace_count
|
proc_count = replace_count
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if proc_count and proc_count > 0 then
|
if proc_count and proc_count > 0 then
|
||||||
return proc_count
|
return proc_count
|
||||||
|
else
|
||||||
|
return nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -302,7 +305,7 @@ function substitute_values(string, values)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local substituted = string:gsub('%%(.)', substitutor)
|
local substituted = string:gsub("%%(.)", substitutor)
|
||||||
return substituted
|
return substituted
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -344,7 +347,7 @@ function round_rect(ass, x0, y0, x1, y1, rtl, rtr, rbr, rbl)
|
||||||
end
|
end
|
||||||
local SCRIPT_NAME = "mpv_thumbnail_script"
|
local SCRIPT_NAME = "mpv_thumbnail_script"
|
||||||
|
|
||||||
local default_cache_base = ON_WINDOWS and os.getenv("TEMP") or (os.getenv("XDG_CACHE_HOME") or "/tmp/")
|
local default_cache_base = ON_WINDOWS and os.getenv("TEMP") or "/tmp/"
|
||||||
|
|
||||||
local thumbnailer_options = {
|
local thumbnailer_options = {
|
||||||
-- The thumbnail directory
|
-- The thumbnail directory
|
||||||
|
|
@ -377,10 +380,6 @@ local thumbnailer_options = {
|
||||||
-- Add a "--profile=<mpv_profile>" to the mpv sub-call arguments
|
-- Add a "--profile=<mpv_profile>" to the mpv sub-call arguments
|
||||||
-- Use "" to disable
|
-- Use "" to disable
|
||||||
mpv_profile = "",
|
mpv_profile = "",
|
||||||
-- Hardware decoding
|
|
||||||
mpv_hwdec = "no",
|
|
||||||
-- High precision seek
|
|
||||||
mpv_hr_seek = "yes",
|
|
||||||
-- Output debug logs to <thumbnail_path>.log, ala <cache_directory>/<video_filename>/000000.bgra.log
|
-- Output debug logs to <thumbnail_path>.log, ala <cache_directory>/<video_filename>/000000.bgra.log
|
||||||
-- The logs are removed after successful encodes, unless you set mpv_keep_logs below
|
-- The logs are removed after successful encodes, unless you set mpv_keep_logs below
|
||||||
mpv_logs = true,
|
mpv_logs = true,
|
||||||
|
|
@ -450,14 +449,11 @@ local thumbnailer_options = {
|
||||||
-- 120 seconds aka 2 minutes will add more thumbnails when the video is over 5 hours!
|
-- 120 seconds aka 2 minutes will add more thumbnails when the video is over 5 hours!
|
||||||
max_delta = 90,
|
max_delta = 90,
|
||||||
|
|
||||||
|
|
||||||
-- Overrides for remote urls (you generally want less thumbnails!)
|
-- Overrides for remote urls (you generally want less thumbnails!)
|
||||||
-- Thumbnailing network paths will be done with mpv
|
-- Thumbnailing network paths will be done with mpv
|
||||||
|
|
||||||
-- Allow thumbnailing network paths (naive check for "://")
|
-- Allow thumbnailing network paths (naive check for "://")
|
||||||
thumbnail_network = false,
|
thumbnail_network = false,
|
||||||
-- Same as autogenerate_max_duration but for remote videos
|
|
||||||
remote_autogenerate_max_duration = 1200, -- 20 min
|
|
||||||
-- Override thumbnail count, min/max delta
|
-- Override thumbnail count, min/max delta
|
||||||
remote_thumbnail_count = 60,
|
remote_thumbnail_count = 60,
|
||||||
remote_min_delta = 15,
|
remote_min_delta = 15,
|
||||||
|
|
@ -466,13 +462,6 @@ local thumbnailer_options = {
|
||||||
-- Try to grab the raw stream and disable ytdl for the mpv subcalls
|
-- Try to grab the raw stream and disable ytdl for the mpv subcalls
|
||||||
-- Much faster than passing the url to ytdl again, but may cause problems with some sites
|
-- Much faster than passing the url to ytdl again, but may cause problems with some sites
|
||||||
remote_direct_stream = true,
|
remote_direct_stream = true,
|
||||||
|
|
||||||
-- Enable storyboards (requires yt-dlp in PATH). Currently only supports YouTube and Twitch VoDs
|
|
||||||
storyboard_enable = true,
|
|
||||||
-- Max thumbnails for storyboards. It only skips processing some of the downloaded thumbnails and doesn't make it much faster
|
|
||||||
storyboard_max_thumbnail_count = 800,
|
|
||||||
-- Most storyboard thumbnails are 160x90. Enabling this allows upscaling them up to thumbnail_height
|
|
||||||
storyboard_upscale = false,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
read_options(thumbnailer_options, SCRIPT_NAME)
|
read_options(thumbnailer_options, SCRIPT_NAME)
|
||||||
|
|
@ -487,8 +476,8 @@ end
|
||||||
function create_thumbnail_mpv(file_path, timestamp, size, output_path, options)
|
function create_thumbnail_mpv(file_path, timestamp, size, output_path, options)
|
||||||
options = options or {}
|
options = options or {}
|
||||||
|
|
||||||
local ytdl_disabled = not options.enable_ytdl and (mp.get_property_native("ytdl") == false
|
local ytdl_disabled = not options.enable_ytdl
|
||||||
or thumbnailer_options.remote_direct_stream)
|
and (mp.get_property_native("ytdl") == false or thumbnailer_options.remote_direct_stream)
|
||||||
|
|
||||||
local header_fields_arg = nil
|
local header_fields_arg = nil
|
||||||
local header_fields = mp.get_property_native("http-header-fields")
|
local header_fields = mp.get_property_native("http-header-fields")
|
||||||
|
|
@ -504,10 +493,8 @@ function create_thumbnail_mpv(file_path, timestamp, size, output_path, options)
|
||||||
|
|
||||||
local log_arg = "--log-file=" .. output_path .. ".log"
|
local log_arg = "--log-file=" .. output_path .. ".log"
|
||||||
|
|
||||||
local mpv_path = ON_MAC and "/opt/homebrew/bin/mpv" or "mpv"
|
local mpv_command = skip_nil({
|
||||||
|
"mpv",
|
||||||
local mpv_command = skip_nil{
|
|
||||||
mpv_path,
|
|
||||||
-- Hide console output
|
-- Hide console output
|
||||||
"--msg-level=all=no",
|
"--msg-level=all=no",
|
||||||
|
|
||||||
|
|
@ -518,68 +505,63 @@ function create_thumbnail_mpv(file_path, timestamp, size, output_path, options)
|
||||||
-- Pass User-Agent and Referer - should do no harm even with ytdl active
|
-- Pass User-Agent and Referer - should do no harm even with ytdl active
|
||||||
"--user-agent=" .. mp.get_property_native("user-agent"),
|
"--user-agent=" .. mp.get_property_native("user-agent"),
|
||||||
"--referrer=" .. mp.get_property_native("referrer"),
|
"--referrer=" .. mp.get_property_native("referrer"),
|
||||||
-- User set hardware decoding
|
-- Disable hardware decoding
|
||||||
"--hwdec=" .. thumbnailer_options.mpv_hwdec,
|
"--hwdec=no",
|
||||||
|
|
||||||
-- Insert --no-config, --profile=... and --log-file if enabled
|
-- Insert --no-config, --profile=... and --log-file if enabled
|
||||||
(thumbnailer_options.mpv_no_config and "--no-config" or nil),
|
(thumbnailer_options.mpv_no_config and "--no-config" or nil),
|
||||||
profile_arg,
|
profile_arg,
|
||||||
(thumbnailer_options.mpv_logs and log_arg or nil),
|
(thumbnailer_options.mpv_logs and log_arg or nil),
|
||||||
|
|
||||||
|
file_path,
|
||||||
|
|
||||||
"--start=" .. tostring(timestamp),
|
"--start=" .. tostring(timestamp),
|
||||||
"--frames=1",
|
"--frames=1",
|
||||||
"--hr-seek=" .. thumbnailer_options.mpv_hr_seek,
|
"--hr-seek=yes",
|
||||||
"--no-audio",
|
"--no-audio",
|
||||||
-- Optionally disable subtitles
|
-- Optionally disable subtitles
|
||||||
(thumbnailer_options.mpv_no_sub and "--no-sub" or nil),
|
(thumbnailer_options.mpv_no_sub and "--no-sub" or nil),
|
||||||
|
|
||||||
(options.relative_scale
|
("--vf=scale=%d:%d"):format(size.w, size.h),
|
||||||
and ("--vf=scale=iw*%d:ih*%d"):format(size.w, size.h)
|
|
||||||
or ("--vf=scale=%d:%d"):format(size.w, size.h)),
|
|
||||||
|
|
||||||
"--vf-add=format=bgra",
|
"--vf-add=format=bgra",
|
||||||
"--of=rawvideo",
|
"--of=rawvideo",
|
||||||
"--ovc=rawvideo",
|
"--ovc=rawvideo",
|
||||||
("--o=%s"):format(output_path),
|
-- "--o",
|
||||||
|
output_path,
|
||||||
"--",
|
})
|
||||||
|
return utils.subprocess({ args = mpv_command })
|
||||||
file_path,
|
|
||||||
}
|
|
||||||
return mp.command_native{name="subprocess", args=mpv_command}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function create_thumbnail_ffmpeg(file_path, timestamp, size, output_path)
|
||||||
function create_thumbnail_ffmpeg(file_path, timestamp, size, output_path, options)
|
|
||||||
options = options or {}
|
|
||||||
|
|
||||||
local ffmpeg_path = ON_MAC and "/opt/homebrew/bin/ffmpeg" or "ffmpeg"
|
|
||||||
|
|
||||||
local ffmpeg_command = {
|
local ffmpeg_command = {
|
||||||
ffmpeg_path,
|
"ffmpeg",
|
||||||
"-loglevel", "quiet",
|
"-loglevel",
|
||||||
|
"quiet",
|
||||||
"-noaccurate_seek",
|
"-noaccurate_seek",
|
||||||
"-ss", format_time(timestamp, ":"),
|
"-ss",
|
||||||
"-i", file_path,
|
format_time(timestamp, ":"),
|
||||||
|
"-i",
|
||||||
|
file_path,
|
||||||
|
|
||||||
"-frames:v", "1",
|
"-frames:v",
|
||||||
|
"1",
|
||||||
"-an",
|
"-an",
|
||||||
|
|
||||||
"-vf",
|
"-vf",
|
||||||
(options.relative_scale
|
("scale=%d:%d"):format(size.w, size.h),
|
||||||
and ("scale=iw*%d:ih*%d"):format(size.w, size.h)
|
"-c:v",
|
||||||
or ("scale=%d:%d"):format(size.w, size.h)),
|
"rawvideo",
|
||||||
|
"-pix_fmt",
|
||||||
|
"bgra",
|
||||||
|
"-f",
|
||||||
|
"rawvideo",
|
||||||
|
|
||||||
"-c:v", "rawvideo",
|
"-y",
|
||||||
"-pix_fmt", "bgra",
|
output_path,
|
||||||
"-f", "rawvideo",
|
|
||||||
|
|
||||||
"-y", output_path,
|
|
||||||
}
|
}
|
||||||
return mp.command_native{name="subprocess", args=ffmpeg_command}
|
return utils.subprocess({ args = ffmpeg_command })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function check_output(ret, output_path, is_mpv)
|
function check_output(ret, output_path, is_mpv)
|
||||||
local log_path = output_path .. ".log"
|
local log_path = output_path .. ".log"
|
||||||
local success = true
|
local success = true
|
||||||
|
|
@ -587,6 +569,7 @@ function check_output(ret, output_path, is_mpv)
|
||||||
if ret.killed_by_us then
|
if ret.killed_by_us then
|
||||||
return nil
|
return nil
|
||||||
else
|
else
|
||||||
|
print("ret.status :=>", ret.status)
|
||||||
if ret.error or ret.status ~= 0 then
|
if ret.error or ret.status ~= 0 then
|
||||||
msg.error("Thumbnailing command failed!")
|
msg.error("Thumbnailing command failed!")
|
||||||
msg.error("mpv process error:", ret.error)
|
msg.error("mpv process error:", ret.error)
|
||||||
|
|
@ -595,12 +578,12 @@ function check_output(ret, output_path, is_mpv)
|
||||||
msg.error("Debug log:", log_path)
|
msg.error("Debug log:", log_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
success = false
|
-- success = false
|
||||||
end
|
end
|
||||||
|
|
||||||
if not file_exists(output_path) then
|
if not file_exists(log_path) then
|
||||||
msg.error("Output file missing!", output_path)
|
msg.error("Output file missing!", output_path)
|
||||||
success = false
|
-- success = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -614,31 +597,6 @@ function check_output(ret, output_path, is_mpv)
|
||||||
return success
|
return success
|
||||||
end
|
end
|
||||||
|
|
||||||
-- split cols x N atlas in BGRA format into many thumbnail files
|
|
||||||
function split_atlas(atlas_path, cols, thumbnail_size, output_name)
|
|
||||||
local atlas = io.open(atlas_path, "rb")
|
|
||||||
local atlas_filesize = atlas:seek("end")
|
|
||||||
local atlas_pictures = math.floor(atlas_filesize / (4 * thumbnail_size.w * thumbnail_size.h))
|
|
||||||
local stride = 4 * thumbnail_size.w * math.min(cols, atlas_pictures)
|
|
||||||
for pic = 0, atlas_pictures-1 do
|
|
||||||
local x_start = (pic % cols) * thumbnail_size.w
|
|
||||||
local y_start = math.floor(pic / cols) * thumbnail_size.h
|
|
||||||
local filename = output_name(pic)
|
|
||||||
if filename then
|
|
||||||
local thumb_file = io.open(filename, "wb")
|
|
||||||
for line = 0, thumbnail_size.h - 1 do
|
|
||||||
atlas:seek("set", 4 * x_start + (y_start + line) * stride)
|
|
||||||
local data = atlas:read(thumbnail_size.w * 4)
|
|
||||||
if data then
|
|
||||||
thumb_file:write(data)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
thumb_file:close()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
atlas:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
function do_worker_job(state_json_string, frames_json_string)
|
function do_worker_job(state_json_string, frames_json_string)
|
||||||
msg.debug("Handling given job")
|
msg.debug("Handling given job")
|
||||||
local thumb_state, err = utils.parse_json(state_json_string)
|
local thumb_state, err = utils.parse_json(state_json_string)
|
||||||
|
|
@ -665,8 +623,8 @@ function do_worker_job(state_json_string, frames_json_string)
|
||||||
local file_duration = mp.get_property_native("duration")
|
local file_duration = mp.get_property_native("duration")
|
||||||
local file_path = thumb_state.worker_input_path
|
local file_path = thumb_state.worker_input_path
|
||||||
|
|
||||||
if thumb_state.is_remote and not thumb_state.storyboard then
|
if thumb_state.is_remote then
|
||||||
if (thumbnail_func == create_thumbnail_ffmpeg) then
|
if thumbnail_func == create_thumbnail_ffmpeg then
|
||||||
msg.warn("Thumbnailing remote path, falling back on mpv.")
|
msg.warn("Thumbnailing remote path, falling back on mpv.")
|
||||||
end
|
end
|
||||||
thumbnail_func = create_thumbnail_mpv
|
thumbnail_func = create_thumbnail_mpv
|
||||||
|
|
@ -690,7 +648,7 @@ function do_worker_job(state_json_string, frames_json_string)
|
||||||
|
|
||||||
-- Check if the thumbnail already exists and is the correct size
|
-- Check if the thumbnail already exists and is the correct size
|
||||||
local thumbnail_file = io.open(thumbnail_path, "rb")
|
local thumbnail_file = io.open(thumbnail_path, "rb")
|
||||||
if not thumbnail_file then
|
if thumbnail_file == nil then
|
||||||
need_thumbnail_generation = true
|
need_thumbnail_generation = true
|
||||||
else
|
else
|
||||||
local existing_thumbnail_filesize = thumbnail_file:seek("end")
|
local existing_thumbnail_filesize = thumbnail_file:seek("end")
|
||||||
|
|
@ -703,35 +661,16 @@ function do_worker_job(state_json_string, frames_json_string)
|
||||||
end
|
end
|
||||||
|
|
||||||
if need_thumbnail_generation then
|
if need_thumbnail_generation then
|
||||||
local success
|
local ret = thumbnail_func(
|
||||||
if thumb_state.storyboard then
|
file_path,
|
||||||
-- get atlas and then split it into thumbnails
|
timestamp,
|
||||||
local rows = thumb_state.storyboard.rows
|
thumb_state.thumbnail_size,
|
||||||
local cols = thumb_state.storyboard.cols
|
thumbnail_path,
|
||||||
local div = thumb_state.storyboard.divisor
|
thumb_state.worker_extra
|
||||||
local atlas_idx = math.floor(thumb_idx * div /(cols*rows))
|
)
|
||||||
local atlas_path = thumb_state.thumbnail_template:format(atlas_idx) .. ".atlas"
|
local success = check_output(ret, thumbnail_path, thumbnail_func == create_thumbnail_mpv)
|
||||||
local url = thumb_state.storyboard.fragments[atlas_idx+1].url
|
|
||||||
if not url then
|
|
||||||
url = thumb_state.storyboard.fragment_base_url .. "/" .. thumb_state.storyboard.fragments[atlas_idx+1].path
|
|
||||||
end
|
|
||||||
local ret = thumbnail_func(url, 0, { w=thumb_state.storyboard.scale, h=thumb_state.storyboard.scale }, atlas_path, { relative_scale=true })
|
|
||||||
success = check_output(ret, atlas_path, thumbnail_func == create_thumbnail_mpv)
|
|
||||||
if success then
|
|
||||||
split_atlas(atlas_path, cols, thumb_state.thumbnail_size, function(idx)
|
|
||||||
if (atlas_idx * cols * rows + idx) % div ~= 0 then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
return thumb_state.thumbnail_template:format(math.floor((atlas_idx * cols * rows + idx) / div))
|
|
||||||
end)
|
|
||||||
os.remove(atlas_path)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local ret = thumbnail_func(file_path, timestamp, thumb_state.thumbnail_size, thumbnail_path, thumb_state.worker_extra)
|
|
||||||
success = check_output(ret, thumbnail_path, thumbnail_func == create_thumbnail_mpv)
|
|
||||||
end
|
|
||||||
|
|
||||||
if not success then
|
if success == nil then
|
||||||
-- Killed by us, changing files, ignore
|
-- Killed by us, changing files, ignore
|
||||||
msg.debug("Changing files, subprocess killed")
|
msg.debug("Changing files, subprocess killed")
|
||||||
return true
|
return true
|
||||||
|
|
@ -749,7 +688,7 @@ function do_worker_job(state_json_string, frames_json_string)
|
||||||
thumbnail_file = io.open(thumbnail_path, "rb")
|
thumbnail_file = io.open(thumbnail_path, "rb")
|
||||||
|
|
||||||
-- Bail if we can't read the file (it should really exist by now, we checked this in check_output!)
|
-- Bail if we can't read the file (it should really exist by now, we checked this in check_output!)
|
||||||
if not thumbnail_file then
|
if thumbnail_file == nil then
|
||||||
msg.error("Thumbnail suddenly disappeared!")
|
msg.error("Thumbnail suddenly disappeared!")
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
@ -761,9 +700,14 @@ function do_worker_job(state_json_string, frames_json_string)
|
||||||
-- Check if the file is big enough
|
-- Check if the file is big enough
|
||||||
local missing_bytes = math.max(0, thumbnail_raw_size - thumbnail_file_size)
|
local missing_bytes = math.max(0, thumbnail_raw_size - thumbnail_file_size)
|
||||||
if missing_bytes > 0 then
|
if missing_bytes > 0 then
|
||||||
msg.warn(("Thumbnail missing %d bytes (expected %d, had %d), padding %s"):format(
|
msg.warn(
|
||||||
missing_bytes, thumbnail_raw_size, thumbnail_file_size, thumbnail_path
|
("Thumbnail missing %d bytes (expected %d, had %d), padding %s"):format(
|
||||||
))
|
missing_bytes,
|
||||||
|
thumbnail_raw_size,
|
||||||
|
thumbnail_file_size,
|
||||||
|
thumbnail_path
|
||||||
|
)
|
||||||
|
)
|
||||||
-- Pad the file if it's missing content (eg. ffmpeg seek to file end)
|
-- Pad the file if it's missing content (eg. ffmpeg seek to file end)
|
||||||
thumbnail_file = io.open(thumbnail_path, "ab")
|
thumbnail_file = io.open(thumbnail_path, "ab")
|
||||||
thumbnail_file:write(string.rep(string.char(0), missing_bytes))
|
thumbnail_file:write(string.rep(string.char(0), missing_bytes))
|
||||||
|
|
@ -774,17 +718,21 @@ function do_worker_job(state_json_string, frames_json_string)
|
||||||
mp.commandv("script-message", "mpv_thumbnail_script-ready", tostring(thumbnail_index), thumbnail_path)
|
mp.commandv("script-message", "mpv_thumbnail_script-ready", tostring(thumbnail_index), thumbnail_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
msg.debug(("Generating %d thumbnails @ %dx%d for %q"):format(
|
msg.debug(
|
||||||
|
("Generating %d thumbnails @ %dx%d for %q"):format(
|
||||||
#thumbnail_indexes,
|
#thumbnail_indexes,
|
||||||
thumb_state.thumbnail_size.w,
|
thumb_state.thumbnail_size.w,
|
||||||
thumb_state.thumbnail_size.h,
|
thumb_state.thumbnail_size.h,
|
||||||
file_path))
|
file_path
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
for i, thumbnail_index in ipairs(thumbnail_indexes) do
|
for i, thumbnail_index in ipairs(thumbnail_indexes) do
|
||||||
local bail = generate_thumbnail_for_index(thumbnail_index)
|
local bail = generate_thumbnail_for_index(thumbnail_index)
|
||||||
if bail then return end
|
if bail then
|
||||||
|
return
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set up listeners and keybinds
|
-- Set up listeners and keybinds
|
||||||
|
|
@ -792,7 +740,6 @@ end
|
||||||
-- Job listener
|
-- Job listener
|
||||||
mp.register_script_message("mpv_thumbnail_script-job", do_worker_job)
|
mp.register_script_message("mpv_thumbnail_script-job", do_worker_job)
|
||||||
|
|
||||||
|
|
||||||
-- Register this worker with the master script
|
-- Register this worker with the master script
|
||||||
local register_timer = nil
|
local register_timer = nil
|
||||||
local register_timeout = mp.get_time() + 1.5
|
local register_timeout = mp.get_time() + 1.5
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue