diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | config/common/mpv/.gitignore | 3 | ||||
-rw-r--r-- | config/common/mpv/input.conf | 23 | ||||
-rw-r--r-- | config/common/mpv/scripts/mpv-cut/README.org | 20 | ||||
-rw-r--r-- | config/common/mpv/scripts/mpv-cut/config.lua | 19 | ||||
-rw-r--r-- | config/common/mpv/scripts/mpv-cut/main.lua | 278 | ||||
-rw-r--r-- | config/common/mpv/scripts/mpv-cut/utils | 44 |
7 files changed, 382 insertions, 7 deletions
@@ -2,8 +2,6 @@ bin/common/stowdots config/common/btop/ config/common/mpd/database config/common/mpd/pid -config/common/mpv/playfile.txt -config/common/mpv/watch_later/* config/essentials/gnupg/* config/essentials/nvim/lazy-lock.json config/essentials/nvim/lua/.luarc.json diff --git a/config/common/mpv/.gitignore b/config/common/mpv/.gitignore new file mode 100644 index 0000000..36aafa7 --- /dev/null +++ b/config/common/mpv/.gitignore @@ -0,0 +1,3 @@ +.gitignore +watch_later +playfile.txt
\ No newline at end of file diff --git a/config/common/mpv/input.conf b/config/common/mpv/input.conf index 7e8c13c..7fc1c12 100644 --- a/config/common/mpv/input.conf +++ b/config/common/mpv/input.conf @@ -183,22 +183,33 @@ q quit-watch-later Alt+l cycle-values loop-file "inf" "no" # toggle infinite looping -# seeking +# Seeking H add chapter -1 L add chapter 1 h seek -5 l seek 5 - seek -60 = seek 60 +RIGHT seek 2 exact +LEFT seek -2 exact +UP seek 2 keyframes +DOWN seek -2 keyframes + +# Speed +] add speed 0.5 +[ add speed -0.5 +} add speed 0.25 +{ add speed -0.25 +\ set speed 1 + # Add/Decrease volume j add volume -5 k add volume 5 # Subtitles -c cycle sub # switch subtitle track up-order -C cycle sub down # switch subtitle track down-order -Alt+c cycle sub-visibility # Toggle subtitles +S cycle sub # switch subtitle track up-order +ctrl+s cycle sub-visibility # Toggle subtitles # Cycle audio tracks v cycle audio # switch audio track @@ -208,4 +219,6 @@ ctrl+l cycle-values loop-file "inf" "no" # toggle infinite looping # Keep open after exit P cycle keep-open up -K script-message osc-chapterlist
\ No newline at end of file +# OSC +K script-message osc-chapterlist +BS script-binding osc/visibility
\ No newline at end of file diff --git a/config/common/mpv/scripts/mpv-cut/README.org b/config/common/mpv/scripts/mpv-cut/README.org new file mode 100644 index 0000000..02540b0 --- /dev/null +++ b/config/common/mpv/scripts/mpv-cut/README.org @@ -0,0 +1,20 @@ +[[https://github.com/familyfriendlymikey/mpv-cut][mpv-cut]] + +* Files +|------------+---------------| +| file | what | +|------------+---------------| +| ~config.lua~ | configuration | +| ~.book~ | bookmarks | +| ~.list~ | backup | +|------------+---------------| + +* Actions +|---+--------------------------------------------| +| ~c~ | START/END cut | +| ~C~ | cancel cut | +| ~a~ | cycle actions | +| ~i~ | bookmark timestamp to ~.list~ (+add chapter) | +| ~-~ | decrement channel +| ~=~ | increment channel | + diff --git a/config/common/mpv/scripts/mpv-cut/config.lua b/config/common/mpv/scripts/mpv-cut/config.lua new file mode 100644 index 0000000..4639cb7 --- /dev/null +++ b/config/common/mpv/scripts/mpv-cut/config.lua @@ -0,0 +1,19 @@ +-- Key config +KEY_CUT = "c" +KEY_CANCEL_CUT = "C" +KEY_CYCLE_ACTION = "a" +KEY_BOOKMARK_ADD = "i" +KEY_CHANNEL_INC = "=" +KEY_CHANNEL_DEC = "-" + +-- The list of channel names, you can choose whatever you want. +CHANNEL_NAMES[1] = "FUNNY" + +-- The default channel +CHANNEL = 1 + +-- The default action +ACTION = "COPY" + +-- Delete a default action +ACTIONS.LIST = nil
\ No newline at end of file diff --git a/config/common/mpv/scripts/mpv-cut/main.lua b/config/common/mpv/scripts/mpv-cut/main.lua new file mode 100644 index 0000000..7db65aa --- /dev/null +++ b/config/common/mpv/scripts/mpv-cut/main.lua @@ -0,0 +1,278 @@ +mp.msg.info("MPV-CUT LOADED") + +utils = require "mp.utils" + +local function print(s) + mp.msg.info(s) + mp.osd_message(s) +end + +local function table_to_str(o) + if type(o) == 'table' then + local s = '' + for k,v in pairs(o) do + if type(k) ~= 'number' then k = '"'..k..'"' end + s = s .. '['..k..'] = ' .. table_to_str(v) .. '\n' + end + return s + else + return tostring(o) + end +end + +local function to_hms(seconds) + local ms = math.floor((seconds - math.floor(seconds)) * 1000) + local secs = math.floor(seconds) + local mins = math.floor(secs / 60) + secs = secs % 60 + local hours = math.floor(mins / 60) + mins = mins % 60 + return string.format("%02d-%02d-%02d-%03d", hours, mins, secs, ms) +end + +local function next_table_key(t, current) + local keys = {} + for k in pairs(t) do + keys[#keys + 1] = k + end + table.sort(keys) + for i = 1, #keys do + if keys[i] == current then + return keys[(i % #keys) + 1] + end + end + return keys[1] +end + +ACTIONS = {} + +ACTIONS.COPY = function(d) + local args = { + "ffmpeg", + "-nostdin", "-y", + "-loglevel", "error", + "-ss", d.start_time, + "-t", d.duration, + "-i", d.inpath, + "-c", "copy", + "-map", "0", + "-dn", + "-avoid_negative_ts", "make_zero", + utils.join_path(d.indir, "COPY_" .. d.channel .. "_" .. d.infile_noext .. "_FROM_" .. d.start_time_hms .. "_TO_" .. d.end_time_hms .. d.ext) + } + mp.command_native_async({ + name = "subprocess", + args = args, + playback_only = false, + }, function() print("Done") end) +end + +ACTIONS.ENCODE = function(d) + local args = { + "ffmpeg", + "-nostdin", "-y", + "-loglevel", "error", + "-ss", d.start_time, + "-t", d.duration, + "-i", d.inpath, + "-pix_fmt", "yuv420p", + "-crf", "16", + "-preset", "superfast", + utils.join_path(d.indir, "ENCODE_" .. d.channel .. "_" .. d.infile_noext .. "_FROM_" .. d.start_time_hms .. "_TO_" .. d.end_time_hms .. d.ext) + } + mp.command_native_async({ + name = "subprocess", + args = args, + playback_only = false, + }, function() print("Done") end) +end + +ACTIONS.LIST = function(d) + local inpath = mp.get_property("path") + local outpath = inpath .. ".list" + local file = io.open(outpath, "a") + if not file then print("Error writing to cut list") return end + local filesize = file:seek("end") + local s = "\n" .. d.channel + .. ":" .. d.start_time + .. ":" .. d.end_time + file:write(s) + local delta = file:seek("end") - filesize + io.close(file) + print("Δ " .. delta) +end + +ACTION = "COPY" + +CHANNEL = 1 + +CHANNEL_NAMES = {} + +KEY_CUT = "c" +KEY_CANCEL_CUT = "C" +KEY_CYCLE_ACTION = "a" +KEY_BOOKMARK_ADD = "i" +KEY_CHANNEL_INC = "=" +KEY_CHANNEL_DEC = "-" + +home_config = mp.command_native({"expand-path", "~/.config/mpv-cut/config.lua"}) +if pcall(require, "config") then + mp.msg.info("Loaded config file from script dir") +elseif pcall(dofile, home_config) then + mp.msg.info("Loaded config file from " .. home_config) +else + mp.msg.info("No config loaded") +end + +for i, v in ipairs(CHANNEL_NAMES) do + CHANNEL_NAMES[i] = string.gsub(v, ":", "-") +end + +if not ACTIONS[ACTION] then ACTION = next_table_key(ACTIONS, nil) end + +START_TIME = nil + +local function get_current_channel_name() + return CHANNEL_NAMES[CHANNEL] or tostring(CHANNEL) +end + +local function get_data() + local d = {} + d.inpath = mp.get_property("path") + d.indir = utils.split_path(d.inpath) + d.infile = mp.get_property("filename") + d.infile_noext = mp.get_property("filename/no-ext") + d.ext = mp.get_property("filename"):match("^.+(%..+)$") or ".mp4" + d.channel = get_current_channel_name() + return d +end + +local function get_times(start_time, end_time) + local d = {} + d.start_time = tostring(start_time) + d.end_time = tostring(end_time) + d.duration = tostring(end_time - start_time) + d.start_time_hms = tostring(to_hms(start_time)) + d.end_time_hms = tostring(to_hms(end_time)) + d.duration_hms = tostring(to_hms(end_time - start_time)) + return d +end + +text_overlay = mp.create_osd_overlay("ass-events") +text_overlay.hidden = true +text_overlay:update() + +local function text_overlay_off() + -- https://github.com/mpv-player/mpv/issues/10227 + text_overlay:update() + text_overlay.hidden = true + text_overlay:update() +end + +local function text_overlay_on() + local channel = get_current_channel_name() + text_overlay.data = string.format("%s in %s from %s", ACTION, channel, START_TIME) + text_overlay.hidden = false + text_overlay:update() +end + +local function print_or_update_text_overlay(content) + if START_TIME then text_overlay_on() else print(content) end +end + +local function cycle_action() + ACTION = next_table_key(ACTIONS, ACTION) + print_or_update_text_overlay("ACTION: " .. ACTION) +end + +local function cut(start_time, end_time) + local d = get_data() + local t = get_times(start_time, end_time) + for k, v in pairs(t) do d[k] = v end + mp.msg.info(ACTION) + mp.msg.info(table_to_str(d)) + ACTIONS[ACTION](d) +end + +local function put_time() + local time = mp.get_property_number("time-pos") + if not START_TIME then + START_TIME = time + text_overlay_on() + return + end + text_overlay_off() + if time > START_TIME then + cut(START_TIME, time) + START_TIME = nil + else + print("INVALID") + START_TIME = nil + end +end + +local function cancel_cut() + text_overlay_off() + START_TIME = nil + print("CANCELLED CUT") +end + +local function get_bookmark_file_path() + local d = get_data() + mp.msg.info(table_to_str(d)) + local outfile = string.format("%s_%s.book", d.channel, d.infile) + return utils.join_path(d.indir, outfile) +end + +local function bookmarks_load() + local inpath = get_bookmark_file_path() + local file = io.open(inpath, "r") + if not file then return end + local arr = {} + for line in file:lines() do + if tonumber(line) then + table.insert(arr, { + time = tonumber(line), + title = "chapter_" .. line + }) + end + end + file:close() + table.sort(arr, function(a, b) return a.time < b.time end) + mp.set_property_native("chapter-list", arr) +end + +local function bookmark_add() + local d = get_data() + local outpath = get_bookmark_file_path() + local file = io.open(outpath, "a") + if not file then print("Failed to open bookmark file for writing") return end + local out_string = mp.get_property_number("time-pos") .. "\n" + local filesize = file:seek("end") + file:write(out_string) + local delta = file:seek("end") - filesize + io.close(file) + bookmarks_load() + print(string.format("Δ %s, %s", delta, d.channel)) +end + +local function channel_inc() + CHANNEL = CHANNEL + 1 + bookmarks_load() + print_or_update_text_overlay(get_current_channel_name()) +end + +local function channel_dec() + if CHANNEL >= 2 then CHANNEL = CHANNEL - 1 end + bookmarks_load() + print_or_update_text_overlay(get_current_channel_name()) +end + +mp.add_key_binding(KEY_CUT, "cut", put_time) +mp.add_key_binding(KEY_CANCEL_CUT, "cancel_cut", cancel_cut) +mp.add_key_binding(KEY_BOOKMARK_ADD, "bookmark_add", bookmark_add) +mp.add_key_binding(KEY_CHANNEL_INC, "channel_inc", channel_inc) +mp.add_key_binding(KEY_CHANNEL_DEC, "channel_dec", channel_dec) +mp.add_key_binding(KEY_CYCLE_ACTION, "cycle_action", cycle_action) + +mp.register_event('file-loaded', bookmarks_load) diff --git a/config/common/mpv/scripts/mpv-cut/utils b/config/common/mpv/scripts/mpv-cut/utils new file mode 100644 index 0000000..b64ca8e --- /dev/null +++ b/config/common/mpv/scripts/mpv-cut/utils @@ -0,0 +1,44 @@ +#! /usr/bin/env bash + +mcc() { + local list=( *.list ) + if [[ ${#list[@]} -ne 1 ]]; then + echo "Number of .list files in cwd must be exactly 1, exiting." + return 1 + fi + make_cuts "$list" "$@" + concat CUT -c copy "CONCAT_${list%.*}" +} + +concat() { + local prefix="$1" + shift + ffmpeg -f concat -safe 0 -i <(printf 'file %q\n' "$PWD"/"$prefix"*) "$@" +} + +make_cuts() { + local list="$1" + local vid="${list%.*}" + local ext="${vid##*.}" + local vid_noext="${vid%.*}" + local start_ts_hms + local end_ts_hms + shift + while IFS=: read -r channel_name start_ts end_ts || [[ -n "$channel_name" ]]; do + if [[ -z "$end_ts" ]]; then continue; fi + start_ts_hms="$(_mpv_cut_to_hms "$start_ts")" + end_ts_hms="$(_mpv_cut_to_hms "$end_ts")" + echo "$channel_name" "$start_ts" "$end_ts" + ffmpeg -nostdin -ss "$start_ts" -to "$end_ts" -i "$vid" "$@" "CUT_${channel_name}_${vid_noext}_${start_ts_hms}_${end_ts_hms}.${ext}" + done < "$list" +} + +_mpv_cut_to_hms() { + local total_seconds="$1" + local hours=$(( ${total_seconds%.*} / 3600 )) + local minutes=$(( (${total_seconds%.*} % 3600) / 60 )) + local seconds=$(( ${total_seconds%.*} % 60 )) + local ms + ms=$(printf "%.0f" "$(echo "($total_seconds - ${total_seconds%.*}) * 1000" | bc)") + printf "%02d-%02d-%02d-%03d\n" $hours $minutes $seconds "$ms" +} |