Skip to content

Instantly share code, notes, and snippets.

@b4zz4
Created April 6, 2018 14:26
Show Gist options
  • Save b4zz4/34a1f176fc7b0e50ffe865971759295a to your computer and use it in GitHub Desktop.
Save b4zz4/34a1f176fc7b0e50ffe865971759295a to your computer and use it in GitHub Desktop.
mpv cut video / cortar videos con mpv
-- This script allows to create excerpts of a video that is played,
-- press "ctrl+i" to mark the begin of the range to excerpt,
-- press "ctrl+o" to mark the end of the range to excerpt,
-- press "ctrl+x" to actually start the creation of the excerpt,
-- which will be done by starting an external executable
-- named "excerpt_copy" with the parameters $1 = begin,
-- $2 = duration, $3 = source file name
-- (see bottom of this file for all key bindings)
-- script options: Use...
--
-- --script-opts=excerpt-source-based-filename=1
-- to make excerpt.lua use the source filename as
-- a base for the destination filename for excerpts
-- --script-opts=excerpt-source-based-extension=1
-- to make excerpt.lua use the same filname extension
-- for destination files than that of the source file -
-- without this option, the extension ".mkv" will be used.
-- Notice that unlike specified otherwise in "excerpt_copy",
-- the filename extension will determine the format of the output.
--
-- (--script-opts can parse multiple options as comma-separated key-value pairs.)
-- initialization:
utils = require 'mp.utils'
mp.set_property("hr-seek-framedrop","no")
mp.set_property("options/keep-open","always")
-- alas, the following setting seems to not take effect - needs
-- to be specified on the command line of mpv, instead:
-- mp.set_property("options/script-opts","osc-layout=bottombar,osc-hidetimeout=120000")
-- assume some plausible frame time until property "fps" is set.
frame_time = 24.0 / 1001.0
mp.observe_property("fps", native, excerpt_fps_changed)
-- seeking
seek_account = 0.0
seek_keyframe = true
function excerpt_on_eof()
mp.msg.log("info", "playback reached end of file")
mp.commandv("seek", 100, "absolute-percent", "exact")
end
mp.register_event("eof-reached", excerpt_on_eof)
-- range marking
function excerpt_rangemessage()
local duration = excerpt_end - excerpt_begin
local message = ""
message = message .. "i: " .. string.format("%4.3f", excerpt_begin) .. "s \n"
message = message .. "o: " .. string.format("%4.3f", excerpt_end) .. "s \n"
message = message .. "d: " .. string.format("% 4.3f", duration) .. "s \n"
return message
end
function excerpt_rangeinfo()
local message = mp.get_property_native("playback-time")
mp.osd_message(message, 5)
end
function excerpt_mark_begin_handler()
pt = mp.get_property_native("playback-time")
excerpt_begin = pt
excerpt_rangeinfo()
end
function excerpt_mark_end_handler()
pt = mp.get_property_native("playback-time")
excerpt_end = pt
excerpt_rangeinfo()
end
-- writing
function get_destination_filename()
local sbf = tonumber(mp.get_opt("excerpt-source-based-filename"))
if (sbf == nil) then
sbf = 0
end
local sbe = tonumber(mp.get_opt("excerpt-source-based-extension"))
if sbe == nil then
sbe = 0
end
local srcname = mp.get_property_native("filename")
local srcnamene = mp.get_property_native("filename/no-ext")
local ext_length = string.len(srcname) - string.len(srcnamene)
local srcext = string.sub(srcname, -ext_length)
local dstext = ".mkv"
if sbe == 1 then
-- use same filename extension than input
dstext = srcext
end
local dstname
if sbf == 1 then
dstname = srcnamene .. ".excerpt_" .. excerpt_begin .. "-" .. excerpt_end
else
-- create a new, unique filename by scanning the current
-- directory for non-existence of files named "excerpt_$number.$extension"
local cwd = utils.getcwd()
local direntries = utils.readdir(cwd)
local ftable = {}
for i = 1, #direntries do
-- mp.msg.log("info", "direntries[" .. i .. "] = " .. direntries[i])
ftable[direntries[i]] = 1
end
local fname = ""
for i=0,999 do
local f = string.format("excerpt_%03d", i)
-- mp.msg.log("info", "ftable[" .. f .. dstext .. "] = " .. direntries[f .. dstext])
if ftable[f .. dstext] == nil then
fname = f
break
end
end
if fname == "" then
message = "not writing because all filenames already in use"
mp.osd_message(message, 10)
return ""
end
dstname = fname
end
return dstname .. dstext
end
function excerpt_write_handler()
if excerpt_begin == excerpt_end then
message = "excerpt_write: not writing because begin == end == " .. excerpt_begin
mp.osd_message(message, 3)
return
end
dstname = get_destination_filename()
if (dstname == "") then
-- file name creation has failed
return
end
duration = excerpt_end - excerpt_begin
local srcname = mp.get_property_native("path")
local message = excerpt_rangemessage()
message = message .. "writing excerpt of source file '" .. srcname .. "'\n"
message = message .. "to destination file '" .. dstname .. "'"
mp.msg.log("info", message)
mp.osd_message(message, 10)
local p = {}
p["cancellable"] = false
p["args"] = {}
p["args"][1] = "excerpt_copy"
p["args"][2] = tostring(excerpt_begin)
p["args"][3] = tostring(duration)
p["args"][4] = tostring(srcname)
p["args"][5] = tostring(dstname)
local res = utils.subprocess(p)
if (res["status"] ~= 0) then
message = message .. "failed!\nfailed to run excerpt_copy - status = " .. res["status"]
if (res["error"] ~= nil) then
message = message .. ", error message: " .. res["error"]
end
message = message .. "\nstdout = " .. res["stdout"]
mp.msg.log("error", message)
mp.osd_message(message, 10)
else
mp.msg.log("info", "excerpt '" .. dstname .. "' written.")
message = message .. "... done."
mp.osd_message(message, 10)
end
end
function excerpt_fps_changed(name)
ft = mp.get_property_native("fps")
if ft ~= nil and ft > 0.0 then
frame_time = 1.0 / ft
-- mp.msg.log("info", "fps property changed to " .. ft .. " frame_time=" .. frame_time .. "s")
end
end
function excerpt_seek()
local abs_sa = math.abs(seek_account)
if abs_sa < (frame_time / 2.0) then
seek_account = 0.0
-- no seek required
return
end
-- mp.msg.log("info", "seek_account = " .. seek_account)
if (abs_sa >= 10.0) then
-- for seeks above 10 seconds, always use coarse keyframe seek
local s = seek_account
seek_account = 0.0
mp.commandv("seek", s, "relative+keyframes")
return
end
if ((abs_sa > 0.5) or seek_keyframe) then
-- for small seeks, use exact seek (unless instructed otherwise by user)
local s = seek_account
seek_account = 0.0
local mode = "relative+exact"
if seek_keyframe then
mode = "relative+keyframes"
end
mp.commandv("seek", s, mode)
return
end
-- for tiny seeks, use frame steps
local s = frame_time
if (seek_account < 0.0) then
s = -s
mp.commandv("frame_back_step")
else
mp.commandv("frame_step")
end
seek_account = seek_account - s;
end
function check_key_release(kevent)
-- mp.msg.log("info", tostring(kevent))
-- for k,v in pairs(kevent) do
-- mp.msg.log("info", "kevent[" .. k .. "] = " .. tostring(v))
-- end
if kevent["event"] == "up" then
-- mp.msg.log("info", "key up detected")
-- key was released, so we should immediately stop to do any seeking
seek_account = 0.0
-- The "zero-seek" at key-release seems to do more harm than good with recent mpv versions:
-- if mpv has not reached the new position from the previously issued seek yet and a relative seek to 0.0 is done, this
-- will counter-act the idea of doing a coarse key-frame seek, causing a long wait
-- before an image is shown for the new position.
-- So for no, we do not perform this "zero-seek".
if false then
if (not seek_keyframe) then
-- and do a "zero-seek" to reset mpv's internal frame step counter:
mp.commandv("seek", 0.0, "relative", "exact")
end
end
return true
end
return false
end
function excerpt_frame_forward(kevent)
if check_key_release(kevent) then
return
end
seek_keyframe = false
seek_account = seek_account + frame_time
end
function excerpt_frame_back(kevent)
if check_key_release(kevent) then
return
end
seek_keyframe = false
seek_account = seek_account - frame_time
end
function excerpt_keyframe_forward(kevent)
if check_key_release(kevent) then
return
end
seek_keyframe = true
seek_account = seek_account + 0.4
end
function excerpt_keyframe_back(kevent)
if check_key_release(kevent) then
return
end
seek_keyframe = true
seek_account = seek_account - 0.6
end
function excerpt_seek_begin_handler()
mp.commandv("seek", excerpt_begin, "absolute", "exact")
end
function excerpt_seek_end_handler()
mp.commandv("seek", excerpt_end, "absolute", "exact")
end
-- things to do whenever a new file was loaded:
function excerpt_on_loaded()
-- for i,name in ipairs(mp.get_property_native("property-list")) do
-- print (name)
-- print(mp.get_property_native(name))
-- end
print(mp.get_property_native("length"))
excerpt_begin = 0.0
excerpt_end = mp.get_property_native("length")
if excerpt_end == nil then
excerpt_end = 0.0
end
-- we have excerpt_seek called both periodically and
-- upon the display of yet another frame - this allows
-- to make "framewise" stepping with autorepeating keys to
-- work as smooth as possible
excerpt_seek_timer = mp.add_periodic_timer(0.1, excerpt_seek)
mp.register_event("tick", excerpt_seek)
-- (I have experimented with stopping the timer when possible,
-- but this didn't work out for strange reasons, got error
-- messages from the event loop.)
-- excerpt_rangeinfo()
end
--
function excerpt_test(kevent)
mp.msg.log("info", tostring(kevent))
for k,v in pairs(kevent) do
mp.msg.log("info", "kevent[" .. k .. "] = " .. tostring(v))
end
mp.commandv("seek", 0.0, "absolute", "exact")
end
--
mp.add_key_binding("ctrl+i", "excerpt_mark_begin", excerpt_mark_begin_handler)
mp.add_key_binding("shift+i", "excerpt_seek_begin", excerpt_seek_begin_handler)
mp.add_key_binding("ctrl+o", "excerpt_mark_end", excerpt_mark_end_handler)
mp.add_key_binding("shift+o", "excerpt_seek_end", excerpt_seek_end_handler)
mp.add_key_binding("ctrl+x", "excerpt_write", excerpt_write_handler)
--
mp.register_event("file-loaded", excerpt_on_loaded)
#!/bin/sh
ffmpeg -y -nostdin -ss $1 -i "$3" -t $2 -codec:v copy -codec:a copy -codec:d copy "$4"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment