[Extension] Create Cutlists during Playback
Posted: 10 May 2014 13:18
Hi,
I am using some open source media libraries to cut mostly .mkv files and they accept cutlists as parameters. Those cutlists are easily created with this extension while the file plays in vlc. The cutlists are stored in cutlist.txt (in vlc.config.userdatadir()) as a Lua table constructor, e.g.
EDIT: ATM, it does not use "path/to/file" but URI.
so that an application can easily loop over a batch of files given the path to cutlist.txt.
(Those time-sequences are of course not restricted to be used as cutlists.)
Originally this extension was depending on add_callback/del_callback and it was working
better. This is a rewrite using a dialog interface. It works but it is not heavily adapted to this interface.
EDIT: The current version is adapted and offers a broad and clean interface.
Patches, Feedback and Suggestions are welcome.
Best Regards
(bad english for common amusement)
Suggestions:
-- (TODO) Buttons for play/pause, back, forward (maybe with different step-sizes)
---- DONE
-- use local file path (if available) instead of URI.
-- let vlc also cut the files? Is this possible from within an extension?
-- dialog-listview based display instead of osd?
Source:
I am using some open source media libraries to cut mostly .mkv files and they accept cutlists as parameters. Those cutlists are easily created with this extension while the file plays in vlc. The cutlists are stored in cutlist.txt (in vlc.config.userdatadir()) as a Lua table constructor, e.g.
Code: Select all
{
["path/to/file"] = {123.123123, 234.234234, 345.345345, 456.456456}
}
so that an application can easily loop over a batch of files given the path to cutlist.txt.
(Those time-sequences are of course not restricted to be used as cutlists.)
Originally this extension was depending on add_callback/del_callback and it was working
better. This is a rewrite using a dialog interface. It works but it is not heavily adapted to this interface.
EDIT: The current version is adapted and offers a broad and clean interface.
Patches, Feedback and Suggestions are welcome.
Best Regards
(bad english for common amusement)
Suggestions:
-- (TODO) Buttons for play/pause, back, forward (maybe with different step-sizes)
---- DONE
-- use local file path (if available) instead of URI.
-- let vlc also cut the files? Is this possible from within an extension?
-- dialog-listview based display instead of osd?
Source:
Code: Select all
--[[
author: randomguy at videolan.org forum
license: GPL
--]]
local DATA_FD = nil -- fill in file-dir if vlc.config.userdatadir() does not work.
local DATA_FN, STEP, BIGSTEP = "cutlist.txt", 10, 120
local PATH, TIME, CHANNEL, FILE, LISTS, LIST, BTTN, DLG, INPUT -- environment.
CHANNEL = {
register = function(self)
self.channel = vlc.osd.channel_register()
return self.channel
end,
send = function(self, msg)
vlc.osd.message(msg, self.channel or self:register())
end,
unregister = function(self)
if self.channel then vlc.osd.channel_clear(self.channel) end
end,
}
local function seek(p)
vlc.var.set(INPUT, "time", TIME+p)
end
local function save()
CHANNEL:send(
#LIST%2 ~= 0 and
"Missing last TO-POSITION, but anyway, saving to Cutlist..."
or
"Saving to Cutlist..."
)
for n, Ln in pairs(LISTS) do
if next(Ln) == nil then LISTS[n] = nil end
end
FILE:write_enc(LISTS)
end
BTTN = {
{
"&q from pos",
function()
local maxn = #LIST
if maxn%2 ~= 0 or (maxn > 0 and LIST[maxn] > TIME) then return end
LIST[maxn+1] = TIME
CHANNEL:send(("|<-- %d sec"):format(TIME))
end,
1, 1
}, {
"&w to pos",
function()
local maxn = #LIST
if maxn%2 == 0 or LIST[maxn] > TIME then return end
LIST[maxn+1] = TIME
CHANNEL:send(("-->| %d sec"):format(TIME))
end,
2, 1
}, {
"&e remove pos",
function()
CHANNEL:send"Removing last position."
LIST[#LIST] = nil
end,
3, 1
}, {
"&r remove item",
function()
CHANNEL:send"Removing file from Cutlist."
LISTS[PATH] = nil
end,
4, 1
}, {
"&t print",
function()
if next(LIST) == nil then
CHANNEL:send"Empty list"
return
end
local hms, frmt = {PATH, "\n"}, "%02d:%02d:%02d"
for i, sec in ipairs(LIST) do
hms[#hms+1] = frmt:format(sec/3600, (sec%3600)/60, sec%60)
hms[#hms+1] = i%2 ~= 0 and " - " or "\n"
end
CHANNEL:send(table.concat(hms))
end,
5, 1
}, {
"&y save",
save,
6, 1
}, {
"&a <<<",
function() seek(-BIGSTEP) end,
1, 2
}, {
"&s <<",
function() seek(-STEP) end,
2, 2
}, {
"&d >",
function() vlc.playlist.pause() end,
3, 2
}, {
"&f >>",
function() seek(STEP) end,
4, 2
}, {
"&g >>>",
function() seek(BIGSTEP) end,
5, 2
}, {
"&z save",
save,
1, 3
}, {
"&x prev",
function() vlc.playlist.prev() end,
2, 3
}, {
"&c next",
function() vlc.playlist.next() end,
3, 3
},
}
------------------------------------------------------------------------------------
local function decode(s) return loadstring("return "..s)() end
local function encode(t)
local buf = {}
for p, tp in pairs(t) do
buf[#buf+1] = (" [%q] = {%s}"):format(p, table.concat(tp, ", "))
end
return "{\n"..table.concat(buf, ",\n").."\n}"
end
------------------------------------------------------------------------------------
function descriptor()
return {
title = "Create Cutlist",
version = "0.4",
author = "randomguy at videolan.org forum",
shortdesc = "Creates a cutlist in userdatadir.",
capabilities = {"input-listener"},
}
end
function activate()
os.setlocale("C", "numeric")
for _, bttn in pairs(BTTN) do
local cb = bttn[2]
bttn[2] = function(...) -- preparing
if not INPUT then return end
TIME = vlc.var.get(INPUT, "time")
if not LISTS[PATH] then LISTS[PATH] = {} end
LIST = LISTS[PATH]
return cb(...)
end
end
local pd = package.config:sub(1, 1)
local fp = (DATA_FD or vlc.config.userdatadir()):gsub(pd.."+$", "")..pd..DATA_FN
local fr = io.open(fp, "r")
if fr then
LISTS = decode(fr:read"*a" or "{}")
fr:close()
end
if type(LISTS) ~= "table" then LISTS = {} end
FILE = {
write = function(self, ...)
local fw = io.open(fp, "w")
fw:write(...)
fw:close()
end,
write_enc = function(self, t) self:write(encode(t)) end,
}
DLG = vlc.dialog"Create Cutlist"
for i = 1, #BTTN do DLG:add_button(unpack(BTTN[i])) end
DLG:show()
end
function input_changed()
local item = vlc.input.item()
local input = vlc.object.input()
PATH = item:uri() -- vlc.strings.decode_uri(item:uri()):gsub("^[^/]*//", "")
INPUT = input
end
function deactivate()
CHANNEL:unregister()
vlc.deactivate()
end
close = deactivate