summaryrefslogtreecommitdiff
path: root/config/common/mpv
diff options
context:
space:
mode:
Diffstat (limited to 'config/common/mpv')
-rw-r--r--config/common/mpv/.gitignore3
-rw-r--r--config/common/mpv/input.conf23
-rw-r--r--config/common/mpv/scripts/mpv-cut/README.org20
-rw-r--r--config/common/mpv/scripts/mpv-cut/config.lua19
-rw-r--r--config/common/mpv/scripts/mpv-cut/main.lua278
-rw-r--r--config/common/mpv/scripts/mpv-cut/utils44
6 files changed, 382 insertions, 5 deletions
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"
+}