diff options
| -rw-r--r-- | .config/mpv/input.conf | 23 | ||||
| -rw-r--r-- | .config/mpv/script-opts/crop.conf | 34 | ||||
| -rw-r--r-- | .config/mpv/script-opts/encode_webm.conf | 39 | ||||
| -rw-r--r-- | .config/mpv/scripts/crop.lua | 434 | ||||
| -rw-r--r-- | .config/mpv/scripts/encode.lua | 315 | ||||
| -rw-r--r-- | .config/nvim/lua/user/plugins/dracula.lua | 4 | ||||
| -rw-r--r-- | .config/shell/aliasrc | 1 | ||||
| -rw-r--r-- | .config/shell/profile | 4 |
8 files changed, 850 insertions, 4 deletions
diff --git a/.config/mpv/input.conf b/.config/mpv/input.conf new file mode 100644 index 0000000..396711e --- /dev/null +++ b/.config/mpv/input.conf @@ -0,0 +1,23 @@ +# crop.lua +# ========== +# start cropping +c script-message-to crop start-crop hard +alt+c script-message-to crop start-crop soft +# delogo mode can be used like so +l script-message-to crop start-crop delogo +# remove the crop +d vf del -1 + +# or use the ready-made "toggle" binding +C script-message-to crop toggle-crop hard + +# remove the soft zoom +0 set video-pan-x 0; set video-pan-y 0; set video-zoom 0 + +# encode.lua +# ============ +# use default profile (makes vp8 webms) +E script-message-to encode set-timestamp + +# use webm profile +e script-message-to encode set-timestamp encode_webm diff --git a/.config/mpv/script-opts/crop.conf b/.config/mpv/script-opts/crop.conf new file mode 100644 index 0000000..9f75cdf --- /dev/null +++ b/.config/mpv/script-opts/crop.conf @@ -0,0 +1,34 @@ +# default mode to be used if not specified in the script-message +# can be hard, soft or delogo +# in hard mode, a crop filter is applied to the video +# in soft mode, video-zoom, video-pan-x and video-pan-y are modified to simulate the cropping +# in delogo mode, the delogo filter is applied to the selected are +# hard mode interacts nicely with encode.lua, soft mode does not +mode=hard + +draw_crosshair=yes +draw_text=yes +draw_shade=yes +draw_frame=yes +frame_border_width=2 +frame_border_color=EEEEEE +# hexadecimal: 00 is opaque, FF is transparent +shade_opacity=77 +mouse_support=yes + +# movement is defined in pixels in the window +# which explains the mismatch with the text (in video space) +coarse_movement=30 +left_coarse=LEFT +right_coarse=RIGHT +up_coarse=UP +down_coarse=DOWN +fine_movement=1 +left_fine=ALT+LEFT +right_fine=ALT+RIGHT +up_fine=ALT+UP +down_fine=ALT+DOWN + +# these two options accept comma separated list of keys +accept=ENTER,MOUSE_BTN0 +cancel=ESC diff --git a/.config/mpv/script-opts/encode_webm.conf b/.config/mpv/script-opts/encode_webm.conf new file mode 100644 index 0000000..06cdc34 --- /dev/null +++ b/.config/mpv/script-opts/encode_webm.conf @@ -0,0 +1,39 @@ +# if yes, only encode the currently active tracks +# for example, mute the player / hide the subtitles if you don't want audio / subs to be part of the extract +only_active_tracks=no + +# whether to preserve some of the applied filters (crop, rotate, flip and mirror) into the extract +# this is pretty useful in combination with crop.lua +# note that you cannot copy video streams and apply filters at the same time +preserve_filters=yes + +# apply another filter after the ones from the previous option if any +# can be used to limit the resolution of the output, for example with +# append_filter=scale=2*trunc(iw/max(1\,sqrt((iw*ih)/(960*540)))/2):-2 +append_filter= + +# additional parameters passed to ffmpeg +codec=-c:a libvorbis -crf 1 -b:v 1M + +# format of the output filename +# Does basic interpolation on the following variables: $f, $x, $t, $s, $e, $d, $p, $n which respectively represent +# input filename, input extension, title, start timestamp, end timestamp, duration, profile name and an incrementing number in case of conflicts +# if the extension is not among the recognized ones, it will default to mkv +output_format=$f_$n.webm + +# the directory in which to create the extract +# empty means the same directory as the input file +# relative paths are relative to mpv's working directory, absolute ones work like you would expect +output_directory=~/vids/ + +# if yes, the ffmpeg process will run detached from mpv and we won't know if it succeeded or not +# if no, we know the result of calling ffmpeg, but we can only encode one extract at a time and mpv will block on exit +detached=yes + +# executable to run when encoding (or its full path if not in PATH) +# for example, this can be used with a wrapper script that calls ffmpeg and triggers a notification when finished +# note that the executable gets the ffmpeg arguments as-is, and is expected to call ffmpeg itself +ffmpeg_command=ffmpeg + +# if yes, print the ffmpeg call before executing it +print=yes diff --git a/.config/mpv/scripts/crop.lua b/.config/mpv/scripts/crop.lua new file mode 100644 index 0000000..adbfaa3 --- /dev/null +++ b/.config/mpv/scripts/crop.lua @@ -0,0 +1,434 @@ +local opts = { + mode = "hard", -- can be "hard" or "soft". If hard, apply a crop filter, if soft zoom + pan. Or a bonus "delogo" mode + draw_shade = true, + shade_opacity = "77", + draw_frame = false, + frame_border_width = 2, + frame_border_color = "EEEEEE", + draw_crosshair = true, + draw_text = true, + mouse_support = true, + coarse_movement = 30, + left_coarse = "LEFT", + right_coarse = "RIGHT", + up_coarse = "UP", + down_coarse = "DOWN", + fine_movement = 1, + left_fine = "ALT+LEFT", + right_fine = "ALT+RIGHT", + up_fine = "ALT+UP", + down_fine = "ALT+DOWN", + accept = "ENTER,MOUSE_BTN0", + cancel = "ESC", +} +(require 'mp.options').read_options(opts) + +function split(input) + local ret = {} + for str in string.gmatch(input, "([^,]+)") do + ret[#ret + 1] = str + end + return ret +end +local msg = require 'mp.msg' + +opts.accept = split(opts.accept) +opts.cancel = split(opts.cancel) +function mode_ok(mode) + return mode == "soft" or mode == "hard" or mode == "delogo" +end +if not mode_ok(opts.mode) then + msg.error("Invalid mode value: " .. opts.mode) + return +end + +local assdraw = require 'mp.assdraw' +local active = false +local active_mode = "" -- same possible values as opts.mode +local rect_centered = false +local rect_keepaspect = false +local needs_drawing = false +local crop_first_corner = nil -- in normalized video space +local crop_cursor = { + x = 0, + y = 0 +} + +function redraw() + needs_drawing = true +end + +function rect_from_two_points(p1, p2, centered, ratio) + local c1 = {p1.x, p1.y} + local c2 = {p2.x, p2.y} + if ratio then + -- adjust position of p2, such + if math.abs(c2[1] - c1[1]) < ratio * math.abs(c2[2] - c1[2]) then + local is_left = c2[1] < c1[1] and -1 or 1 + c2[1] = c1[1] + is_left * math.abs(c2[2] - c1[2]) * ratio + else + local is_up = c2[2] < c1[2] and -1 or 1 + c2[2] = c1[2] + is_up * math.abs(c2[1] - c1[1]) / ratio + end + end + if centered then + -- p1 is center => convert it into corner + c1[1] = c1[1] - (c2[1] - c1[1]) + c1[2] = c1[2] - (c2[2] - c1[2]) + end + -- sort corners + if c1[1] > c2[1] then c1[1], c2[1] = c2[1], c1[1] end + if c1[2] > c2[2] then c1[2], c2[2] = c2[2], c1[2] end + return { x = c1[1], y = c1[2] }, { x = c2[1], y = c2[2] } +end + +function clamp(low, value, high) + if value <= low then + return low + elseif value >= high then + return high + else + return value + end +end + +function clamp_point(top_left, point, bottom_right) + return { + x = clamp(top_left.x, point.x, bottom_right.x), + y = clamp(top_left.y, point.y, bottom_right.y) + } +end + +function screen_to_video_norm(point, dim) + return { + x = (point.x - dim.ml) / (dim.w - dim.ml - dim.mr), + y = (point.y - dim.mt) / (dim.h - dim.mt - dim.mb) + } +end + +function video_norm_to_screen(point, dim) + return { + x = math.floor(point.x * (dim.w - dim.ml - dim.mr) + dim.ml + 0.5), + y = math.floor(point.y * (dim.h - dim.mt - dim.mb) + dim.mt + 0.5) + } +end + +function draw_shade(ass, unshaded, window) + ass:new_event() + ass:pos(0, 0) + ass:append("{\\an7}") + ass:append("{\\bord0}") + ass:append("{\\shad0}") + ass:append("{\\c&H000000&}") + ass:append("{\\1a&H" .. opts.shade_opacity .. "}") + ass:append("{\\2a&HFF}") + ass:append("{\\3a&HFF}") + ass:append("{\\4a&HFF}") + local c1, c2 = unshaded.top_left, unshaded.bottom_right + local v = window + -- c1.x c2.x + -- +-----+------------+ + -- | | ur | + -- c1.y| ul +-------+----+ + -- | | | | + -- c2.y+-----+-------+ lr | + -- | ll | | + -- +-------------+----+ + ass:draw_start() + ass:rect_cw(v.top_left.x, v.top_left.y, c1.x, c2.y) -- ul + ass:rect_cw(c1.x, v.top_left.y, v.bottom_right.x, c1.y) -- ur + ass:rect_cw(v.top_left.x, c2.y, c2.x, v.bottom_right.y) -- ll + ass:rect_cw(c2.x, c1.y, v.bottom_right.x, v.bottom_right.y) -- lr + ass:draw_stop() + -- also possible to draw a rect over the whole video + -- and \iclip it in the middle, but seemingy slower +end + +function draw_frame(ass, frame) + ass:new_event() + ass:pos(0, 0) + ass:append("{\\an7}") + ass:append("{\\bord0}") + ass:append("{\\shad0}") + ass:append("{\\c&H" .. opts.frame_border_color .. "&}") + ass:append("{\\1a&H00&}") + ass:append("{\\2a&HFF&}") + ass:append("{\\3a&HFF&}") + ass:append("{\\4a&HFF&}") + local c1, c2 = frame.top_left, frame.bottom_right + local b = opts.frame_border_width + ass:draw_start() + ass:rect_cw(c1.x, c1.y - b, c2.x + b, c1.y) + ass:rect_cw(c2.x, c1.y, c2.x + b, c2.y + b) + ass:rect_cw(c1.x - b, c2.y, c2.x, c2.y + b) + ass:rect_cw(c1.x - b, c1.y - b, c1.x, c2.y) + ass:draw_stop() +end + +function draw_crosshair(ass, center, window_size) + ass:new_event() + ass:pos(0, 0) + ass:append("{\\an7}") + ass:append("{\\bord0}") + ass:append("{\\shad0}") + ass:append("{\\c&HBBBBBB&}") + ass:append("{\\1a&H00&}") + ass:append("{\\2a&HFF&}") + ass:append("{\\3a&HFF&}") + ass:append("{\\4a&HFF&}") + ass:draw_start() + ass:rect_cw(center.x - 0.5, 0, center.x + 0.5, window_size.h) + ass:rect_cw(0, center.y - 0.5, window_size.w, center.y + 0.5) + ass:draw_stop() +end + +function draw_position_text(ass, text, position, window_size, offset) + ass:new_event() + local align = 1 + local ofx = 1 + local ofy = -1 + if position.x > window_size.w / 2 then + align = align + 2 + ofx = -1 + end + if position.y < window_size.h / 2 then + align = align + 6 + ofy = 1 + end + ass:append("{\\an"..align.."}") + ass:append("{\\fs26}") + ass:append("{\\bord1.5}") + ass:pos(ofx*offset + position.x, ofy*offset + position.y) + ass:append(text) +end + +function draw_crop_zone() + if needs_drawing then + local dim = mp.get_property_native("osd-dimensions") + if not dim then + cancel_crop() + return + end + + local cursor = { + x = crop_cursor.x, + y = crop_cursor.y, + } + local ass = assdraw.ass_new() + + if crop_first_corner and (opts.draw_shade or opts.draw_frame) then + local frame = {} + frame.top_left, frame.bottom_right = rect_from_two_points( + video_norm_to_screen(crop_first_corner, dim), + cursor, + rect_centered, + rect_keepaspect and dim.w/dim.h) + -- don't draw shade over non-visible video parts + if opts.draw_shade then + local window = { + top_left = { x = 0, y = 0 }, + bottom_right = { x = dim.w, y = dim.h }, + } + draw_shade(ass, frame, window) + end + if opts.draw_frame then + draw_frame(ass, frame) + end + end + + + if opts.draw_crosshair then + draw_crosshair(ass, cursor, { w = dim.w, h = dim.h }) + end + + if opts.draw_text then + local vop = mp.get_property_native("video-out-params") + if vop then + local cursor_norm = screen_to_video_norm(cursor, dim) + local text = string.format("%d, %d", cursor_norm.x * vop.w, cursor_norm.y * vop.h) + if crop_first_corner then + text = string.format("%s (%dx%d)", text, + math.abs((cursor_norm.x - crop_first_corner.x) * vop.w ), + math.abs((cursor_norm.y - crop_first_corner.y) * vop.h ) + ) + end + draw_position_text(ass, text, cursor, { w = dim.w, h = dim.h }, 6) + end + end + + mp.set_osd_ass(dim.w, dim.h, ass.text) + needs_drawing = false + end +end + +function crop_video(x1, y1, x2, y2) + if active_mode == "soft" then + local w = x2 - x1 + local h = y2 - y1 + local dim = mp.get_property_native("osd-dimensions") + if not dim then return end + + local zoom = mp.get_property_number("video-zoom") + local newZoom1 = math.log(dim.h * (2 ^ zoom) / (dim.h - dim.mt - dim.mb) / h) / math.log(2) + local newZoom2 = math.log(dim.w * (2 ^ zoom) / (dim.w - dim.ml - dim.mr) / w) / math.log(2) + mp.set_property("video-zoom", math.min(newZoom1, newZoom2)) + mp.set_property("video-pan-x", 0.5 - (x1 + w / 2)) + mp.set_property("video-pan-y", 0.5 - (y1 + h / 2)) + elseif active_mode == "hard" or active_mode == "delogo" then + x1 = clamp(0, x1, 1) + y1 = clamp(0, y1, 1) + x2 = clamp(0, x2, 1) + y2 = clamp(0, y2, 1) + local vop = mp.get_property_native("video-out-params") + local vf_table = mp.get_property_native("vf") + local x = math.floor(x1 * vop.w + 0.5) + local y = math.floor(y1 * vop.h + 0.5) + local w = math.floor((x2 - x1) * vop.w + 0.5) + local h = math.floor((y2 - y1) * vop.h + 0.5) + if active_mode == "delogo" then + -- delogo is a little special and needs some padding to function + w = math.min(vop.w - 1, w) + h = math.min(vop.h - 1, h) + x = math.max(1, x) + y = math.max(1, y) + if x + w == vop.w then w = w - 1 end + if y + h == vop.h then h = h - 1 end + end + vf_table[#vf_table + 1] = { + name=(active_mode == "hard") and "crop" or "delogo", + params= { x = tostring(x), y = tostring(y), w = tostring(w), h = tostring(h) } + } + mp.set_property_native("vf", vf_table) + end +end + +function update_crop_zone_state() + local dim = mp.get_property_native("osd-dimensions") + if not dim then + cancel_crop() + return + end + local corner = crop_cursor + if crop_first_corner == nil then + crop_first_corner = screen_to_video_norm(crop_cursor, dim) + redraw() + else + local c1, c2 = rect_from_two_points( + video_norm_to_screen(crop_first_corner, dim), + crop_cursor, + rect_centered, + rect_keepaspect and dim.w/dim.h) + local c1norm = screen_to_video_norm(c1, dim) + local c2norm = screen_to_video_norm(c2, dim) + crop_video(c1norm.x, c1norm.y, c2norm.x, c2norm.y) + cancel_crop() + end +end + +local bindings = {} +local bindings_repeat = {} + +function cancel_crop() + crop_first_corner = nil + for key, _ in pairs(bindings) do + mp.remove_key_binding("crop-"..key) + end + for key, _ in pairs(bindings_repeat) do + mp.remove_key_binding("crop-"..key) + end + mp.unobserve_property(redraw) + mp.unregister_idle(draw_crop_zone) + mp.set_osd_ass(1280, 720, '') + active = false +end + +function start_crop(mode) + if active then return end + if not mp.get_property_native("osd-dimensions") then return end + if mode and not mode_ok(mode) then + msg.error("Invalid mode value: " .. mode) + return + end + local mode_maybe = mode or opts.mode + if mode_maybe ~= 'soft' then + local hwdec = mp.get_property("hwdec-current") + if hwdec and hwdec ~= "no" and not string.find(hwdec, "-copy$") then + msg.error("Cannot crop with hardware decoding active (see manual)") + return + end + end + active = true + active_mode = mode_maybe + + if opts.mouse_support then + crop_cursor.x, crop_cursor.y = mp.get_mouse_pos() + end + redraw() + for key, func in pairs(bindings) do + mp.add_forced_key_binding(key, "crop-"..key, func) + end + for key, func in pairs(bindings_repeat) do + mp.add_forced_key_binding(key, "crop-"..key, func, { repeatable = true }) + end + mp.register_idle(draw_crop_zone) + mp.observe_property("osd-dimensions", nil, redraw) +end + +function toggle_crop(mode) + if mode and not mode_ok(mode) then + msg.error("Invalid mode value: " .. mode) + end + local toggle_mode = mode or opts.mode + if toggle_mode == "soft" then return end -- can't toggle soft mode + + local remove_filter = function() + local to_remove = (toggle_mode == "hard") and "crop" or "delogo" + local vf_table = mp.get_property_native("vf") + if #vf_table > 0 then + for i = #vf_table, 1, -1 do + if vf_table[i].name == to_remove then + for j = i, #vf_table-1 do + vf_table[j] = vf_table[j+1] + end + vf_table[#vf_table] = nil + mp.set_property_native("vf", vf_table) + return true + end + end + end + return false + end + if not remove_filter() then + start_crop(mode) + end +end + +-- bindings +if opts.mouse_support then + bindings["MOUSE_MOVE"] = function() crop_cursor.x, crop_cursor.y = mp.get_mouse_pos(); redraw() end +end +for _, key in ipairs(opts.accept) do + bindings[key] = update_crop_zone_state +end +for _, key in ipairs(opts.cancel) do + bindings[key] = cancel_crop +end +function movement_func(move_x, move_y) + return function() + crop_cursor.x = crop_cursor.x + move_x + crop_cursor.y = crop_cursor.y + move_y + redraw() + end +end +bindings_repeat[opts.left_coarse] = movement_func(-opts.coarse_movement, 0) +bindings_repeat[opts.right_coarse] = movement_func(opts.coarse_movement, 0) +bindings_repeat[opts.up_coarse] = movement_func(0, -opts.coarse_movement) +bindings_repeat[opts.down_coarse] = movement_func(0, opts.coarse_movement) +bindings_repeat[opts.left_fine] = movement_func(-opts.fine_movement, 0) +bindings_repeat[opts.right_fine] = movement_func(opts.fine_movement, 0) +bindings_repeat[opts.up_fine] = movement_func(0, -opts.fine_movement) +bindings_repeat[opts.down_fine] = movement_func(0, opts.fine_movement) + + +mp.add_key_binding(nil, "start-crop", start_crop) +mp.add_key_binding(nil, "toggle-crop", toggle_crop) diff --git a/.config/mpv/scripts/encode.lua b/.config/mpv/scripts/encode.lua new file mode 100644 index 0000000..df518d8 --- /dev/null +++ b/.config/mpv/scripts/encode.lua @@ -0,0 +1,315 @@ +local utils = require "mp.utils" +local msg = require "mp.msg" +local options = require "mp.options" + +local ON_WINDOWS = (package.config:sub(1,1) ~= "/") + +local start_timestamp = nil +local profile_start = "" + +-- implementation detail of the osd message +local timer = nil +local timer_duration = 2 + +function append_table(lhs, rhs) + for i = 1,#rhs do + lhs[#lhs+1] = rhs[i] + end + return lhs +end + +function file_exists(name) + local f = io.open(name, "r") + if f ~= nil then + io.close(f) + return true + else + return false + end +end + +function get_extension(path) + local candidate = string.match(path, "%.([^.]+)$") + if candidate then + for _, ext in ipairs({ "mkv", "webm", "mp4", "avi" }) do + if candidate == ext then + return candidate + end + end + end + return "mkv" +end + +function get_output_string(dir, format, input, extension, title, from, to, profile) + local res = utils.readdir(dir) + if not res then + return nil + end + local files = {} + for _, f in ipairs(res) do + files[f] = true + end + local output = format + output = string.gsub(output, "$f", function() return input end) + output = string.gsub(output, "$t", function() return title end) + output = string.gsub(output, "$s", function() return seconds_to_time_string(from, true) end) + output = string.gsub(output, "$e", function() return seconds_to_time_string(to, true) end) + output = string.gsub(output, "$d", function() return seconds_to_time_string(to-from, true) end) + output = string.gsub(output, "$x", function() return extension end) + output = string.gsub(output, "$p", function() return profile end) + if ON_WINDOWS then + output = string.gsub(output, "[/\\|<>?:\"*]", "_") + end + if not string.find(output, "$n") then + return files[output] and nil or output + end + local i = 1 + while true do + local potential_name = string.gsub(output, "$n", tostring(i)) + if not files[potential_name] then + return potential_name + end + i = i + 1 + end +end + +function get_video_filters() + local filters = {} + for _, vf in ipairs(mp.get_property_native("vf")) do + local name = vf["name"] + name = string.gsub(name, '^lavfi%-', '') + local filter + if name == "crop" then + local p = vf["params"] + filter = string.format("crop=%d:%d:%d:%d", p.w, p.h, p.x, p.y) + elseif name == "mirror" then + filter = "hflip" + elseif name == "flip" then + filter = "vflip" + elseif name == "rotate" then + local rotation = vf["params"]["angle"] + -- rotate is NOT the filter we want here + if rotation == "90" then + filter = "transpose=clock" + elseif rotation == "180" then + filter = "transpose=clock,transpose=clock" + elseif rotation == "270" then + filter = "transpose=cclock" + end + end + filters[#filters + 1] = filter + end + return filters +end + +function get_input_info(default_path, only_active) + local accepted = { + video = true, + audio = not mp.get_property_bool("mute"), + sub = mp.get_property_bool("sub-visibility") + } + local ret = {} + for _, track in ipairs(mp.get_property_native("track-list")) do + local track_path = track["external-filename"] or default_path + if not only_active or (track["selected"] and accepted[track["type"]]) then + local tracks = ret[track_path] + if not tracks then + ret[track_path] = { track["ff-index"] } + else + tracks[#tracks + 1] = track["ff-index"] + end + end + end + return ret +end + +function seconds_to_time_string(seconds, full) + local ret = string.format("%02d:%02d.%03d" + , math.floor(seconds / 60) % 60 + , math.floor(seconds) % 60 + , seconds * 1000 % 1000 + ) + if full or seconds > 3600 then + ret = string.format("%d:%s", math.floor(seconds / 3600), ret) + end + return ret +end + +function start_encoding(from, to, settings) + local args = { + settings.ffmpeg_command, + "-loglevel", "panic", "-hide_banner", + } + local append_args = function(table) args = append_table(args, table) end + + local path = mp.get_property("path") + local is_stream = not file_exists(path) + if is_stream then + path = mp.get_property("stream-path") + end + + local track_args = {} + local start = seconds_to_time_string(from, false) + local input_index = 0 + for input_path, tracks in pairs(get_input_info(path, settings.only_active_tracks)) do + append_args({ + "-ss", start, + "-i", input_path, + }) + if settings.only_active_tracks then + for _, track_index in ipairs(tracks) do + track_args = append_table(track_args, { "-map", string.format("%d:%d", input_index, track_index)}) + end + else + track_args = append_table(track_args, { "-map", tostring(input_index)}) + end + input_index = input_index + 1 + end + + append_args({"-to", tostring(to-from)}) + append_args(track_args) + + -- apply some of the video filters currently in the chain + local filters = {} + if settings.preserve_filters then + filters = get_video_filters() + end + if settings.append_filter ~= "" then + filters[#filters + 1] = settings.append_filter + end + if #filters > 0 then + append_args({ "-filter:v", table.concat(filters, ",") }) + end + + -- split the user-passed settings on whitespace + for token in string.gmatch(settings.codec, "[^%s]+") do + args[#args + 1] = token + end + + -- path of the output + local output_directory = settings.output_directory + if output_directory == "" then + if is_stream then + output_directory = "." + else + output_directory, _ = utils.split_path(path) + end + else + output_directory = string.gsub(output_directory, "^~", os.getenv("HOME") or "~") + end + local input_name = mp.get_property("filename/no-ext") or "encode" + local title = mp.get_property("media-title") + local extension = get_extension(path) + local output_name = get_output_string(output_directory, settings.output_format, input_name, extension, title, from, to, settings.profile) + if not output_name then + mp.osd_message("Invalid path " .. output_directory) + return + end + args[#args + 1] = utils.join_path(output_directory, output_name) + + if settings.print then + local o = "" + -- fuck this is ugly + for i = 1, #args do + local fmt = "" + if i == 1 then + fmt = "%s%s" + elseif i >= 2 and i <= 4 then + fmt = "%s" + elseif args[i-1] == "-i" or i == #args or args[i-1] == "-filter:v" then + fmt = "%s '%s'" + else + fmt = "%s %s" + end + o = string.format(fmt, o, args[i]) + end + print(o) + end + if settings.detached then + utils.subprocess_detached({ args = args }) + else + local res = utils.subprocess({ args = args, max_size = 0, cancellable = false }) + if res.status == 0 then + mp.osd_message("Finished encoding succesfully") + else + mp.osd_message("Failed to encode, check the log") + end + end +end + +function clear_timestamp() + timer:kill() + start_timestamp = nil + profile_start = "" + mp.remove_key_binding("encode-ESC") + mp.remove_key_binding("encode-ENTER") + mp.osd_message("", 0) +end + +function set_timestamp(profile) + if not mp.get_property("path") then + mp.osd_message("No file currently playing") + return + end + if not mp.get_property_bool("seekable") then + mp.osd_message("Cannot encode non-seekable media") + return + end + + if not start_timestamp or profile ~= profile_start then + profile_start = profile + start_timestamp = mp.get_property_number("time-pos") + local msg = function() + mp.osd_message( + string.format("encode [%s]: waiting for end timestamp", profile or "default"), + timer_duration + ) + end + msg() + timer = mp.add_periodic_timer(timer_duration, msg) + mp.add_forced_key_binding("ESC", "encode-ESC", clear_timestamp) + mp.add_forced_key_binding("ENTER", "encode-ENTER", function() set_timestamp(profile) end) + else + local from = start_timestamp + local to = mp.get_property_number("time-pos") + if to <= from then + mp.osd_message("Second timestamp cannot be before the first", timer_duration) + timer:kill() + timer:resume() + return + end + clear_timestamp() + mp.osd_message(string.format("Encoding from %s to %s" + , seconds_to_time_string(from, false) + , seconds_to_time_string(to, false) + ), timer_duration) + -- include the current frame into the extract + local fps = mp.get_property_number("container-fps") or 30 + to = to + 1 / fps / 2 + local settings = { + detached = true, + container = "", + only_active_tracks = false, + preserve_filters = true, + append_filter = "", + codec = "-an -sn -c:v libvpx -crf 10 -b:v 1000k", + output_format = "$f_$n.webm", + output_directory = "", + ffmpeg_command = "ffmpeg", + print = true, + } + if profile then + options.read_options(settings, profile) + if settings.container ~= "" then + msg.warn("The 'container' setting is deprecated, use 'output_format' now") + settings.output_format = settings.output_format .. "." .. settings.container + end + settings.profile = profile + else + settings.profile = "default" + end + start_encoding(from, to, settings) + end +end + +mp.add_key_binding(nil, "set-timestamp", set_timestamp) diff --git a/.config/nvim/lua/user/plugins/dracula.lua b/.config/nvim/lua/user/plugins/dracula.lua index 3d1e91a..77926a0 100644 --- a/.config/nvim/lua/user/plugins/dracula.lua +++ b/.config/nvim/lua/user/plugins/dracula.lua @@ -3,11 +3,11 @@ vim.cmd([[ autocmd! autocmd ColorScheme dracula highlight! link Pmenu DraculaBg autocmd ColorScheme dracula highlight link NormalFloat DraculaBgLight - autocmd ColorScheme dracula highlight FloatBorder guifg=#343746 guibg=#343746 + autocmd ColorScheme dracula highlight FloatBorder guifg=none guibg=none autocmd ColorScheme dracula highlight DraculaBoundary guibg=none autocmd ColorScheme dracula highlight DraculaDiffDelete ctermbg=none guibg=none autocmd ColorScheme dracula highlight DraculaComment cterm=italic gui=italic - autocmd ColorScheme dracula highlight Normal guibg=#000000 + autocmd ColorScheme dracula highlight Normal guibg=none augroup end colorscheme dracula ]]) diff --git a/.config/shell/aliasrc b/.config/shell/aliasrc index 63b4434..93bc200 100644 --- a/.config/shell/aliasrc +++ b/.config/shell/aliasrc @@ -11,6 +11,7 @@ alias mutt='pushd ~/.mutt/attachments; mutt; popd' alias jpwine='LANG=ja_JP.UTF-8 WINEDEBUG=-all wine' alias emacsd='/usr/local/bin/emacs --daemon' alias e="$EDITOR" +alias vi="$EDITOR" alias z='zathura' alias cn='clear;macchina' alias nnn='nnn -Hde' diff --git a/.config/shell/profile b/.config/shell/profile index da7ff8f..a774d0e 100644 --- a/.config/shell/profile +++ b/.config/shell/profile @@ -1,7 +1,7 @@ -umask 0077 +umask 0076 export _JAVA_AWT_WM_NONREPARENTING=1 -export EDITOR="emacsclient -t" +export EDITOR="nvim" export TERMINAL="st" export BROWSER="firefox" |
