Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Seek by file #42

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions thumbfast.conf
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,6 @@ max_width=200
# Overlay id
overlay_id=42

# Thumbnail interval in seconds set to 0 to disable (warning: high cpu usage)
interval=6

# Number of thumbnails
min_thumbnails=6
max_thumbnails=120

# Spawn thumbnailer on file load for faster initial thumbnails
spawn_first=no

Expand Down
215 changes: 121 additions & 94 deletions thumbfast.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,68 @@
--
-- Built for easy integration in third-party UIs.

local sub_name = "thumbfast_sub"
-- if subprocess
if mp.get_property_native("title") == sub_name then
local options = {seek = "/tmp/thumbfast.seek", period = 0.1}
mp.options = require "mp.options"
mp.options.read_options(options, sub_name)

local time = nil
local function seek(exact)
if not time then return end
mp.commandv("seek", time, exact and "absolute+exact" or "absolute+keyframes")
end

local seek_file_tmp = options.seek .. ".tmp"
local timer = nil
local missed = false
local function check_seek()
os.rename(options.seek, seek_file_tmp)
local file = io.open(seek_file_tmp, "r")
if file then
-- read as string, because the seek command needs strings
local requested_time = tostring(file:read("*n"))
if time ~= requested_time then
time = requested_time
time = requested_time
seek()
else
seek(true)
end
file:close()
os.remove(seek_file_tmp)
missed = false
else
if not missed then
seek(true)
missed = true
else
timer:kill()
end
end
end
timer = mp.add_periodic_timer(options.period, check_seek)
timer:kill()

--signal to start checking for seek requests
mp.observe_property("title", "native", function(_, _)
check_seek()
timer:resume()
end)
return
end

local options = {
-- Socket path (leave empty for auto)
socket = "",

-- Thumbnail path (leave empty for auto)
thumbnail = "",

-- Seek file path (leave empty for auto)
seek = "",

-- Maximum thumbnail size in pixels (scaled down to fit)
-- Values are scaled when hidpi is enabled
max_height = 200,
Expand All @@ -19,14 +74,6 @@ local options = {
-- Overlay id
overlay_id = 42,

-- Thumbnail interval in seconds, set to 0 to disable (warning: high cpu usage)
-- Clamped to min_thumbnails and max_thumbnails
interval = 6,

-- Number of thumbnails
min_thumbnails = 6,
max_thumbnails = 120,

-- Spawn thumbnailer on file load for faster initial thumbnails
spawn_first = false,

Expand All @@ -41,20 +88,11 @@ mp.utils = require "mp.utils"
mp.options = require "mp.options"
mp.options.read_options(options, "thumbfast")

if options.min_thumbnails < 1 then
options.min_thumbnails = 1
end

local os_name = ""

math.randomseed(os.time())
local unique = math.random(10000000)
local init = false
local script_path = debug.getinfo(1, 'S').source:sub(2)

local spawned = false
local network = false
local disabled = false
local interval = 0

local x = nil
local y = nil
Expand Down Expand Up @@ -89,6 +127,8 @@ local file_timer = nil
local file_check_period = 1/60
local first_file = false

local seek_period = 3/60

local function get_os()
local raw_os_name = ""

Expand Down Expand Up @@ -139,6 +179,45 @@ local function get_os()
return str_os_name
end

local os_name = get_os()

if options.socket == "" then
if os_name == "Windows" then
options.socket = "thumbfast"
elseif os_name == "Mac" then
options.socket = "/tmp/thumbfast"
else
options.socket = "/tmp/thumbfast"
end
end

if options.thumbnail == "" then
if os_name == "Windows" then
options.thumbnail = os.getenv("TEMP").."\\thumbfast.out"
elseif os_name == "Mac" then
options.thumbnail = "/tmp/thumbfast.out"
else
options.thumbnail = "/tmp/thumbfast.out"
end
end

if options.seek == "" then
if os_name == "Windows" then
options.seek = os.getenv("TEMP").."\\thumbfast.seek"
elseif os_name == "Mac" then
options.seek = "/tmp/thumbfast.seek"
else
options.seek = "/tmp/thumbfast.seek"
end
end

math.randomseed(os.time())
local unique = math.random(10000000)

options.socket = options.socket .. unique
options.thumbnail = options.thumbnail .. unique
options.seek = options.seek .. unique

local function vf_string(filters, full)
local vf = ""
local vf_table = mp.get_property_native("vf")
Expand Down Expand Up @@ -217,37 +296,6 @@ local function spawn(time)
path = open_filename
end

if os_name == "" then
os_name = get_os()
end

if options.socket == "" then
if os_name == "Windows" then
options.socket = "thumbfast"
elseif os_name == "Mac" then
options.socket = "/tmp/thumbfast"
else
options.socket = "/tmp/thumbfast"
end
end

if options.thumbnail == "" then
if os_name == "Windows" then
options.thumbnail = os.getenv("TEMP").."\\thumbfast.out"
elseif os_name == "Mac" then
options.thumbnail = "/tmp/thumbfast.out"
else
options.thumbnail = "/tmp/thumbfast.out"
end
end

if not init then
-- ensure uniqueness
options.socket = options.socket .. unique
options.thumbnail = options.thumbnail .. unique
init = true
end

remove_thumbnail_files()

mp.command_native_async(
Expand All @@ -261,7 +309,9 @@ local function spawn(time)
"--vf="..vf_string(filters_all, true),
"--sws-allow-zimg=no", "--sws-fast=yes", "--sws-scaler=fast-bilinear",
"--video-rotate="..last_rotate,
"--ovc=rawvideo", "--of=image2", "--ofopts=update=1", "--o="..options.thumbnail
"--ovc=rawvideo", "--of=image2", "--ofopts=update=1", "--o="..options.thumbnail,
"--title=" .. sub_name, "--script=" .. script_path,
"--script-opts=" .. sub_name .. "-seek=" .. options.seek .. "," .. sub_name .. "-period=" .. seek_period,
}},
function() end
)
Expand All @@ -288,19 +338,6 @@ local function run(command, callback)
)
end

local function thumb_index(thumbtime)
return math.floor(thumbtime / interval)
end

local function index_time(index, thumbtime)
if interval > 0 then
local time = index * interval
return time + interval / 3
else
return thumbtime
end
end

local function draw(w, h, script)
if not w or not show_thumbnail then return end
local display_w, display_h = w, h
Expand Down Expand Up @@ -351,40 +388,35 @@ local function move_file(from, to)
end

local last_seek = 0
local function seek()
local function seek(seek_time)
if last_seek_time then
last_seek = mp.get_time()
run("async seek " .. last_seek_time .. " absolute+keyframes")
local file = io.open(options.seek, 'w')
if file then
file:write(seek_time)
file:close()
local now = mp.get_time()
if now - last_seek > seek_period then
-- signal to check file
run('set title ' .. now)
end
last_seek = now
end
end
end

local seek_period = 0.1
local seek_timer = mp.add_timeout(seek_period, seek)
seek_timer:kill()
local function request_seek()
if seek_timer:is_enabled() then return end
local next_seek = seek_period - (mp.get_time() - last_seek)
if next_seek <= 0 then seek() return end
seek_timer.timeout = next_seek
seek_timer:resume()
end

local function check_new_thumb()
local finfo = mp.utils.file_info(options.thumbnail)
if not finfo then return false end

-- the slave might start writing to the file after checking existance and
-- validity but before actually moving the file, so move to a temporary
-- location before validity check to make sure everything stays consistant
-- and valid thumbnails don't get overwritten by invalid ones
local tmp = options.thumbnail..".tmp"
move_file(options.thumbnail, tmp)
local finfo = mp.utils.file_info(tmp)
if not finfo then return false end
if first_file then
request_seek()
seek(last_seek_time)
first_file = false
end
finfo = mp.utils.file_info(tmp)
if not finfo then return false end
local w, h = real_res(effective_w, effective_h, finfo.size)
if w then -- only accept valid thumbnails
move_file(tmp, options.thumbnail..".bgra")
Expand Down Expand Up @@ -415,27 +447,23 @@ local function thumb(time, r_x, r_y, script)
x, y = math.floor(r_x + 0.5), math.floor(r_y + 0.5)
end

local index = thumb_index(time)
local seek_time = index_time(index, time)

script_name = script
if last_x ~= x or last_y ~= y or seek_time ~= last_seek_time or not show_thumbnail then
if last_x ~= x or last_y ~= y or not show_thumbnail then
show_thumbnail = true
last_x = x
last_y = y
draw(real_w, real_h, script)
end

if seek_time == last_seek_time then return end
last_seek_time = seek_time
if not spawned then spawn(seek_time) end
request_seek()
if time == last_seek_time then return end
last_seek_time = time
if not spawned then spawn(time) end
seek(time)
if not file_timer:is_enabled() then file_timer:resume() end
end

local function clear()
file_timer:kill()
seek_timer:kill()
last_seek = 0
show_thumbnail = false
last_x = nil
Expand Down Expand Up @@ -506,8 +534,6 @@ local function file_load()
info(effective_w, effective_h)
if disabled then return end

interval = math.min(math.max(mp.get_property_number("duration", 1) / options.max_thumbnails, options.interval), mp.get_property_number("duration", options.interval * options.min_thumbnails) / options.min_thumbnails)

spawned = false
if options.spawn_first then
spawn(mp.get_property_number("time-pos", 0))
Expand All @@ -519,6 +545,7 @@ local function shutdown()
run("quit")
remove_thumbnail_files()
os.remove(options.socket)
os.remove(options.seek)
end

mp.observe_property("display-hidpi-scale", "native", watch_changes)
Expand Down