diff options
Diffstat (limited to '')
36 files changed, 2912 insertions, 0 deletions
diff --git a/.config/GIMP/3.0/plug-ins/watermarkfu/watermarkfu.py b/.config/GIMP/3.0/plug-ins/watermarkfu/watermarkfu.py new file mode 100755 index 0000000..79b143a --- /dev/null +++ b/.config/GIMP/3.0/plug-ins/watermarkfu/watermarkfu.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python3 + +import gi +gi.require_version('Gimp', '3.0') +from gi.repository import Gimp +from gi.repository import GLib +from gi.repository import GObject +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk + +import sys +from datetime import date + + +plug_in_proc = 'plug-in-rgz-watermarkfu' +plug_in_binary = 'rgz-watermarkfu' + + +class WatermarkFu(Gimp.PlugIn): + def do_query_procedures(self): + return [ plug_in_proc ] + + def do_create_procedure(self, name): + if name != plug_in_proc: + return None + + procedure = Gimp.ImageProcedure.new(self, + name, + Gimp.PDBProcType.PLUGIN, + self.run, + None) + + procedure.set_sensitivity_mask (Gimp.ProcedureSensitivityMask.DRAWABLE | + Gimp.ProcedureSensitivityMask.DRAWABLES) + + procedure.set_menu_label('WatermarkFu...') + procedure.add_menu_path('<Image>/Filters/Render') + + procedure.set_attribution('Robert Günzler', 'rgz', '2023') + procedure.set_documentation('Watermark your pictures', + 'Watermark your pictures', + name) + + procedure.add_string_argument('watermark', 'Watermark', None, + 'My watermark', + GObject.ParamFlags.READWRITE) + procedure.add_string_argument('anotherline', 'Another line', None, + '<today>', + GObject.ParamFlags.READWRITE) + procedure.add_font_argument('font', 'Font', None, False, None, True, + GObject.ParamFlags.READWRITE) + procedure.add_double_argument('scalefactor_text', 'Scale Text', None, + 0.0, 100.0, 0.2, + GObject.ParamFlags.READWRITE) + procedure.add_double_argument('scalefactor_boundary', 'Scale Boundary', None, + 0.0, 100.0, 2.0, + GObject.ParamFlags.READWRITE) + procedure.add_double_argument('watermark_opacity', 'Opacity', None, + 0.0, 100.0, 16.0, + GObject.ParamFlags.READWRITE) + + return procedure + + + def run(self, procedure, run_mode, img, drawables, config, data): + if len(drawables) != 1: + error = GLib.Error.new_literal( + Gimp.PlugIn.error_quark(), + f"Procedure '{procedure.get_name()}' only works with one drawable.", + 0) + return procedure.new_return_values(Gimp.PDBStatusType.CALLING_ERROR, error) + + if run_mode == Gimp.RunMode.INTERACTIVE: + gi.require_version('GimpUi', '3.0') + from gi.repository import GimpUi + + GimpUi.init(plug_in_binary) + + dialog = GimpUi.ProcedureDialog.new(procedure, config, + 'WatermarkFu') + box = dialog.fill_box('size-box', ['font-size', 'font-unit']) + box.set_orientation (Gtk.Orientation.HORIZONTAL) + dialog.fill_frame('size-frame', 'compute-size', True, 'size-box') + dialog.fill([ + 'watermark', + 'anotherline', + 'font', + 'scalefactor_text', + 'scalefactor_boundary', + 'watermark_opacity' + ]) + + if not dialog.run(): + dialog.destroy() + return procedure.new_return_values(Gimp.PDBStatusType.CANCEL, None) + else: + dialog.destroy() + + # make copy of input + img.undo_group_start() + + + self.watermarkfu(img, drawables[0], + watermark=config.get_property('watermark'), + anotherline=config.get_property('anotherline'), + font=config.get_property('font'), + scalefactor_text=config.get_property('scalefactor_text'), + scalefactor_boundary=config.get_property('scalefactor_boundary'), + watermark_opacity=config.get_property('watermark_opacity')) + + img.undo_group_end() + + # try: + # except: + # img.undo_group_end() + # return procedure.new_return_values(Gimp.PDBStatusType.EXECUTION_ERROR , None) + + return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, None) + + + def watermarkfu(self, + img, + drawable, + watermark, + anotherline='<today>', + font=None, + scalefactor_text=0.2, + scalefactor_boundary=2.0, + watermark_opacity=16.66): + + # simplify source image + img.flatten() + + # expand text macro + anotherline = anotherline.replace('<today>', + date.today().strftime('%Y-%m-%d')) + + # create text + txt = Gimp.TextLayer.new(image=img, + text=("\n".join([watermark, anotherline])), + font=font, + size=68, + unit=Gimp.Unit.point()) + + # add to image, justification doesn't work otherwise + img.insert_layer(txt, drawable.get_parent(), 0) + + # justify text + txt.set_justification(Gimp.TextJustification.CENTER) + + img_width = img.get_width() + img_height = img.get_height() + layer_width = txt.get_width() + layer_height = txt.get_height() + + # scale down text layer, maintaining ratio + txt.scale(new_width=((float(layer_width)/layer_height)*(img_height*scalefactor_text)), + new_height=(img_height*scalefactor_text), + local_origin=True) + + # crate padding around text + Gimp.Layer.resize(txt, + new_width=(layer_width*scalefactor_boundary), + new_height=(layer_height*scalefactor_boundary), + offx=(((layer_width*scalefactor_boundary)-layer_width)/2), + offy=(((layer_height*scalefactor_boundary)-layer_height)/2)) + + txt.set_offsets(offx=0, offy=0) + + pdb = Gimp.get_pdb() + fu_tile = pdb.lookup_procedure('plug-in-tile') + fu_tile_cfg = fu_tile.create_config() + fu_tile_cfg.set_property('run-mode', Gimp.RunMode.NONINTERACTIVE) + fu_tile_cfg.set_property('image', img) + fu_tile_cfg.set_core_object_array('drawables', [txt]) + fu_tile_cfg.set_property('new-width', img_width) + fu_tile_cfg.set_property('new-height', img_height) + fu_tile_cfg.set_property('new-image', False) + fu_tile.run(fu_tile_cfg) + + layer = fu_tile.find_return_value('new-layer') + print('???', layer) + + return + + # create watermark layer + layer = Gimp.Layer.new(image=img, + name='watermark', + width=img_width, + height=img_height, + type=Gimp.ImageType.RGBA_IMAGE) + + # copy txt and make it the active pattern + Gimp.Selection.all(img) + Gimp.edit_copy([txt]) + Gimp.context_set_pattern(Gimp.Pattern.get_by_name('Clipboard Image')) + + # do a pattern fill + layer.fill(Gimp.FillType.PATTERN) + + # make transparent + layer.set_mode(Gimp.LayerMode.HARDLIGHT) + layer.set_opacity(watermark_opacity) + + img.insert_layer(layer, drawable.get_parent(), 0) + + # remove the txt layer + img.remove_layer(txt) + + +Gimp.main(WatermarkFu.__gtype__, sys.argv) diff --git a/.config/desk.toml b/.config/desk.toml new file mode 100644 index 0000000..7e6ea07 --- /dev/null +++ b/.config/desk.toml @@ -0,0 +1,9 @@ +connection_attempts = 5 + +[positions] +walk = 12600 +stand = 11500 +up = 11500 +half = 10000 +sit = 8200 +down = 8200 diff --git a/.config/mpv/.editorconfig b/.config/mpv/.editorconfig new file mode 100644 index 0000000..c0b69e9 --- /dev/null +++ b/.config/mpv/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +tab_width = 8 +end_of_line = lf +insert_final_newline = true + +[*.lua] +max_line_length = 100 diff --git a/.config/mpv/config b/.config/mpv/config new file mode 100644 index 0000000..41b10cb --- /dev/null +++ b/.config/mpv/config @@ -0,0 +1,155 @@ +vo = gpu-next +hwdec = auto + +screenshot-directory = ~/pictures/screenshots/mpv +screenshot-template = '%tY%tm%td-%tH%tM%tS-%F-%p' +screenshot-format = png +screenshot-png-compression = 9 + +# +# profiles +# + +[fullscreen] +vo=dmabuf-wayland + +[protocol.http] +profile-desc="Optimized for streaming video" +glsl-shaders-clr +title="${media-title}" +# ytdl-format=bv[height<=?1080]+ba / b[height<=?1080] +ytdl-format=(bv[vcodec~='^av01']/bv)[height<=?1080]+ba / b[height<=?1080] +demuxer-seekable-cache=yes +cache-pause=no +hls-bitrate=max + +[protocol.https] +profile=protocol.http + +[reset] +profile-desc="Reset everything to vanilla mpv" +profile=default +load-scripts=no +osc=yes +osd-bar=yes + +[bigcache] +demuxer-readahead-secs=20 +demuxer-max-bytes=512M +demuxer-max-back-bytes=512M + +[gpuhq] +profile-desc="Optimized for HQ rendering" +profile-cond=height <= 1440 +profile=gpu-hq + +[scale] +profile-desc="Use fancy scaling algorithms" +profile-cond=height <= 1440 +glsl-shaders-clr +scale=ewa_lanczossharp +dscale=ewa_lanczossharp +correct-downscaling=yes +sigmoid-upscaling=yes + +[webcam] +profile-desc="Optimized for showing real-time webcam feed" +profile-cond=(not not string.match(get('path'), '/dev/video*')) +load-scripts=no +osc=no +profile=low-latency +untimed +video-sync=display-resample +no-demuxer-thread +opengl-glfinish=yes +opengl-swapinterval=0 +vf-add=hflip + +[webcam/local] +profile-cond=(not not string.match(get('path'), '/dev/video0')) +profile=webcam +demuxer-lavf-o=input_format=mjpeg + +[downmix] +ad-lavc-downmix=no +# == pan == +# <output channel layout> | <output channel definition> +# FL = front left +# FC = front center +# BL = back left +# SL = side left +# LFE = low frequency +# +# == dynaudnorm == +# g (gausssize): 3-301 (must be odd; default: 31), window size (in frames) of smoothing filter around current frame +# larger: stronger smoothing -> less gain variation +# f (framelen): 10-8000 ms (default: 500), length of the section of audio used to detect peaks +# r (targetrms): 0.0-1.0 (default: 0.0 disabled), use RMS (root mean square) of signal instead of peak - better approx. of "perceived loudness" +# adjusts frames to a constant RMS value +# p (peak): (default: 0.95 leaves 5% headroom), target peak value +#af-add='lavfi=[pan=2.1| FL < FC+0.8FL+0.1*BL+0.1*SL | FR < FC+0.8*FR+0.1*BR+0.1*SR | LFE < LFE,dynaudnorm=g=41:f=700:r=0.9]' +#af-add='lavfi=[pan=5.1| FL < 0.8*FL | FR < 0.8*FR | LFE < LFE | FC < FC | BL < BL | BR < BR,dynaudnorm=g=41:f=700:r=0.9]' +af-add='lavfi=[pan=5.1| FL = 0.5*FL | FR = 0.5*FR | LFE = LFE | FC = FC | BL = 0.3*BL | BR = 0.3*BR]' + +[fastforward] +profile-restore=copy-equal +profile=fast +scale=bilinear +dscale=bilinear +cscale=bilinear +vd-lavc-skiploopfilter=all +vd-lavc-skipframe=all + +[compressor] +profile-desc="Apply audio compression" +input-commands-add=load-script '/usr/share/mpv/lua/acompressor.lua' +script-opts-add=acompressor-default_enable=yes + +# mpv image viewer settings +[mvi] +profile-desc="mpv image viewer" +profile-restore=copy-equal +profile=gpu-hq +profile=scale +sub-auto=no +audio-file-auto=no +term-status-msg= +title="${?media-title:${media-title}}${!media-title:No file}" +video-aspect-override=no +loop-file=inf +image-display-duration=inf +input-conf=~~/mvi/input.conf +input-commands-add=load-script '~~/mvi/scripts/ruler.lua' +input-commands-add=load-script '~~/mvi/scripts/detect-image.lua' +input-commands-add=load-script '~~/mvi/scripts/freeze-window.lua' +input-commands-add=load-script '~~/mvi/scripts/image-positioning.lua' +input-commands-add=load-script '~~/mvi/scripts/minimap.lua' +input-commands-add=load-script '~~/mvi/scripts/ruler.lua' +script-opts-add=uosc-volume=none +#script-opts-add=uosc-controls=no +#script-opts-add=uosc-progress=no +script-opts-add=uosc-timeline_size=0 +directory-filter-types=image + +[extension.jpg] +profile=mvi +[extension.png] +profile=mvi +[extension.gif] +profile=mvi + +# mpv audio player +[mpa] +profile-desc="mpv audio player" +profile=pseudo-gui +script-opts-add=uosc-volume=none +directory-filter-types=audio + +[extension.ogg] +profile=mpa +[extension.flac] +profile=mpa +[extension.wav] +profile=mpa +[extension.mp3] +profile=mpa diff --git a/.config/mpv/hooks/mako.sh b/.config/mpv/hooks/mako.sh new file mode 100755 index 0000000..60e5804 --- /dev/null +++ b/.config/mpv/hooks/mako.sh @@ -0,0 +1,10 @@ +#!/bin/sh +set -eux + +# TODO: figure out why this fucks mako +exit 0 + +case "${1:-}" in +file-loaded) fyi -a mpv 'mpv hooks' 'dnd on' ;; +shutdown) fyi -a mpv 'mpv hooks' 'dnd off' ;; +esac diff --git a/.config/mpv/input.conf b/.config/mpv/input.conf new file mode 100644 index 0000000..b67dab7 --- /dev/null +++ b/.config/mpv/input.conf @@ -0,0 +1,186 @@ +# MPV INPUT CONFIG + +# taken from the default config +ESC set fullscreen no +f cycle fullscreen + +SPACE cycle pause #! pause/play +m cycle mute #! controls > mute + +# ARROW KEY BINDINGS +LEFT seek -10 exact +RIGHT seek 10 exact +DOWN seek -60 exact +UP seek 60 exact + +WHEEL_UP seek -10 exact +WHEEL_DOWN seek 10 exact +Shift+WHEEL_UP seek -60 exact +Shift+WHEEL_DOWN seek 60 exact + +h seek -10 keyframes +j seek -60 keyframes +k seek 60 keyframes +l seek 10 keyframes + +HOME seek 0 absolute-percent +0 seek 0 absolute-percent + +END seek 100 absolute-percent +$ seek 100 absolute-percent + +Shift+RIGHT playlist-next +n playlist-next #! controls > playlist next +Shift+LEFT playlist-prev +p playlist-prev #! controls > playlist prev +Alt+Shift+s playlist-shuffle #! controls > playlist shuffle + +#Alt+RIGHT frame-step +#Alt+LEFT frame-back-step +> frame-step +< frame-back-step +Ctrl+< seek -1 keyframes +Ctrl+> seek 1 keyframes + +Shift+UP add volume 10 +Shift+DOWN add volume -10 +Alt+UP add volume 5 +Alt+DOWN add volume -5 + +L add chapter 1 #! controls > chapter next +H add chapter -1 #! controls > chapter prev +PGUP add chapter 1 +PGDWN add chapter -1 + +a cycle audio +A script-binding uosc/audio #! select > audio +D cycle deinterlace +v script-binding uosc/video #! select > video + +s cycle sub +S script-binding uosc/subtitles #! select > sub +Ctrl+s script-binding uosc/load-subtitles #! select > load sub... +e add sub-scale +0.1 #! sub > scale up +w add sub-scale -0.1 #! sub > scale down +r add sub-pos +1 #! sub > pos up +t add sub-pos -1 #! sub > pos down + +z add sub-delay -0.1 #! delay > sub -0.1 +x add sub-delay +0.1 #! delay > sub +0.1 +Z add audio-delay -0.1 #! delay > audio -0.1 +X add audio-delay +0.1 #! delay > audio +0.1 +Ctrl+z sub-step -1 #! delay > sub-step -1 +Ctrl+x sub-step 1 #! delay > sub-step +1 + +U cycle-values sub-ass-override "scale" "force" "strip" "no" #! sub > ass override +V cycle sub-visibility #! sub > visibility +# Meta+s cycle sub-ass-force-margins + +# PLAYER BINDINGS +i script-binding stats/display-stats-toggle #! stats +Ctrl+g screenshot #! utils > screenshot > default +Alt+g screenshot video #! utils > screenshot > video + +Ctrl+a cycle-values video-aspect-override "16:9" "4:3" "21:9" "2.35:1" "-1" #! controls > aspect-ratio +Alt+s cycle-values audio-channels "auto-safe" "auto" "stereo" #! controls > audio channels +Alt+a cycle-values video-rotate 0 90 180 270 #! controls > video-rotate +# T cycle ontop + +q stop +Q quit-watch-later #! watch later + +# Ctrl+BS revert-seek mark-permanent + +Alt+] script-binding skip2silence #! utils > skip2silence +Alt+} script-binding skip2scene #! utils > skip2scene +u revert-seek mark-permanent #! controls > undo/revert seek + += add volume 10 +Shift+= add volume 5 +- add volume -10 +Shift+- add volume -5 + +Alt+[ cycle-values speed 0.75 0.5 0.25 #! controls > speed > slower +Alt+] cycle-values speed 1.25 1.5 2.0 #! controls > speed > faster +Alt+BS set speed 1.0 #! utils > speed > reset + +M cycle-values vf setpts=(2/1)*PTS setpts=(4/1)*PTS setpts=(1/2)*PTS setpts=(1/4)*PTS setpts=(1/8)*PTS "" + +Alt+l-f cycle-values loop-file yes no #! loop > file +Ctrl+l ab-loop #! loop > A-B + +# VAPOURSYNTH/FILTER BINDINGS +b ignore # debanding +B ignore +F1 af toggle asubcut #! fx > a:subcut +F2 af toggle loudnorm #! fx > a:loudnorm +F3 af toggle earwax #! fx > a:earwax +F6 af toggle @skip-silence #! fx > a:@skip-silence +F4 vf toggle bwdif #! fx > v:deinterlace +F5 vf toggle @deband #! fx > v:@deband +F7 vf toggle @no-3d #! fx > v:@no-3d + +# uosc +? script-binding uosc/keybinds +Ctrl+SPACE script-binding uosc/toggle-ui +MBTN_MID script-binding uosc/menu +: script-binding uosc/menu +Enter script-binding uosc/menu +P script-binding uosc/items #! select > playlist +C script-binding uosc/chapters #! select > chapters +o script-binding uosc/open-file #! select > file... +Ctrl+o script-binding openuri/openuri #! select > uri... +Ctrl+Shift+o script-binding openuri/pasteuri #! select > paste +Ctrl+n script-binding uosc/next-file +Ctrl+p script-binding uosc/prev-file + +# various +Alt+w script-binding webm/display-webm-encoder #! utils > webm encoder +# Alt+s-a script-binding slicing/slicing_audio #! utils > slicing > audio +# Alt+s-m script-binding slicing/slicing_mark #! utils > slicing > mark +Alt+a-d script-binding autodeint #! utils > autodeint +Alt+a-c script-binding autocrop/toggle_crop #! utils > autocrop +Alt+m-c script-binding mouse_coords #! utils > mouse coords +Alt+b script-binding blur/toggle-blur #! utils > blur + +# gallery +g ignore +Shift+g script-message playlist-view-toggle #! utils > playlist/gallery view + +# yanktime +y-y script_binding yanky/yanktime #! utils > yank > time +y-p script_binding yanky/yankpath #! utils > yank > path +y-s script_binding yanky/yanksubs #! utils > yank > subs + +# sponsorblock +Alt+b-g script_binding sponsorblock/set_segment #! sponsorblock > set +Alt+b-G script_binding sponsorblock/submit_segment #! sponsorblock > submit +Alt+b-u script_binding sponsorblock/upvote_segment #! sponsorblock > upvote +Alt+b-UP script_binding sponsorblock/upvote_segment #! sponsorblock > upvote +Alt+b-d script_binding sponsorblock/downvote_segment #! sponsorblock > downvote +Alt+b-DOWN script_binding sponsorblock/downvote_segment #! sponsorblock > downvote + +# mpvacious +Alt+a script-binding mpvacious-menu-open #! mpvacious > open +Ctrl+n script-binding mpvacious-export-note #! mpvacious > export note + +# 1 ignore +# 2 ignore +# 3 ignore +4 ignore +# 5 ignore +6 ignore +7 ignore +8 ignore +9 ignore + +3 script-message whisper/sub-whisper +5 cycle-values gamma-factor 1.0 1.1 1.2 + +I apply-profile interpolate #! profiles > interpolate + +Ctrl+Shift+0 apply-profile reset #! profiles > reset +Ctrl+Shift+1 apply-profile interpolate #! profiles > interpolate +Ctrl+Shift+2 apply-profile cache #! profiles > cache +Ctrl+Shift+3 apply-profile scale #! profiles > scale +Ctrl+Shift+4 apply-profile compressor #! profiles > compressor diff --git a/.config/mpv/mvi/input.conf b/.config/mpv/mvi/input.conf new file mode 100644 index 0000000..1d5b989 --- /dev/null +++ b/.config/mpv/mvi/input.conf @@ -0,0 +1,109 @@ +1 change-list script-opts append image_positioning-drag_to_pan_margin=200 +2 change-list script-opts append ruler-exit_bindings=8 +3 change-list script-opts append ruler-line_color=FF +4 change-list script-opts append ruler-scale=25 +5 change-list script-opts append ruler-max_size=20,20 + +SPACE repeatable playlist-next +alt+SPACE repeatable playlist-prev + +# familiar binds +n playlist-next +p playlist-prev + +UP ignore +DOWN ignore +LEFT repeatable playlist-prev +RIGHT repeatable playlist-next + +# simple reminder of default bindings +#1 add contrast -1 +#2 add contrast 1 +#3 add brightness -1 +#4 add brightness 1 +#5 add gamma -1 +#6 add gamma 1 +#7 add saturation -1 +#8 add saturation 1 + +# mouse-centric bindings +MBTN_RIGHT script-binding drag-to-pan +MBTN_LEFT script-binding pan-follows-cursor +MBTN_LEFT_DBL ignore +WHEEL_UP script-message cursor-centric-zoom 0.1 +WHEEL_DOWN script-message cursor-centric-zoom -0.1 + +# panning with the keyboard: +# pan-image takes the following arguments +# pan-image AXIS AMOUNT ZOOM_INVARIANT IMAGE_CONSTRAINED +# ^ ^ ^ +# x or y | | +# | | +# if yes, will pan by the same if yes, stops panning if the image +# amount regardless of zoom would go outside of the window + +ctrl+down repeatable script-message pan-image y -0.1 yes yes +ctrl+up repeatable script-message pan-image y +0.1 yes yes +ctrl+right repeatable script-message pan-image x -0.1 yes yes +ctrl+left repeatable script-message pan-image x +0.1 yes yes + +# now with more precision +alt+down repeatable script-message pan-image y -0.01 yes yes +alt+up repeatable script-message pan-image y +0.01 yes yes +alt+right repeatable script-message pan-image x -0.01 yes yes +alt+left repeatable script-message pan-image x +0.01 yes yes + +# replace at will with h,j,k,l if you prefer vim-style bindings + +# on a trackpad you may want to use these +#WHEEL_UP repeatable script-message pan-image y -0.02 yes yes +#WHEEL_DOWN repeatable script-message pan-image y +0.02 yes yes +#WHEEL_LEFT repeatable script-message pan-image x -0.02 yes yes +#WHEEL_RIGHT repeatable script-message pan-image x +0.02 yes yes + +# align the border of the image to the border of the window +# align-border takes the following arguments: +# align-border ALIGN_X ALIGN_Y +# any value for ALIGN_* is accepted, -1 and 1 map to the border of the window +ctrl+shift+right script-message align-border -1 "" +ctrl+shift+left script-message align-border 1 "" +ctrl+shift+down script-message align-border "" -1 +ctrl+shift+up script-message align-border "" 1 + +# reset the image +ctrl+0 no-osd set video-pan-x 0; no-osd set video-pan-y 0; no-osd set video-zoom 0 + ++ add video-zoom 0.5 +- add video-zoom -0.5; script-message reset-pan-if-visible += no-osd set video-zoom 0; script-message reset-pan-if-visible + +e script-message equalizer-toggle +alt+e script-message equalizer-reset + +h no-osd vf toggle hflip; show-text "Horizontal flip" +v no-osd vf toggle vflip; show-text "Vertical flip" + +r script-message rotate-video 90; show-text "Clockwise rotation" +R script-message rotate-video -90; show-text "Counter-clockwise rotation" +alt+r no-osd set video-rotate 0; show-text "Reset rotation" + +d script-message ruler + +# Toggling between pixel-exact reproduction and interpolation +a cycle-values scale nearest ewa_lanczossharp + +# Toggle color management on or off +c cycle icc-profile-auto + +# Screenshot of the window output +S screenshot window + +# Toggle aspect ratio information on and off +A cycle-values video-aspect-override "-1" "no" + +P script-message force-print-filename + +# ADVANCED: you can define bindings that belong to a "section" (named "image-viewer" here) like so: +#alt+SPACE {image-viewer} repeatable playlist-prev +#SPACE {image-viewer} repeatable playlist-next +# to load them conditionally with a command. See scripts-opts/image_viewer.conf for how you can do this diff --git a/.config/mpv/mvi/mpv.conf b/.config/mpv/mvi/mpv.conf new file mode 100644 index 0000000..973ba42 --- /dev/null +++ b/.config/mpv/mvi/mpv.conf @@ -0,0 +1,49 @@ +## IMAGE +# classic opengl-hq parameter, change at will +scale=spline36 +cscale=spline36 +dscale=mitchell +dither-depth=auto +correct-downscaling +sigmoid-upscaling +# debanding seems rarely useful with images +#deband +# dark grey background instead of pure black +background=color +background-color=0.2 + +## MISC +mute=yes +# the osc is mostly useful for videos +osc=no +# don't try to autoload subtitles or audio files +sub-auto=no +audio-file-auto=no +# get rid of the useless V: 00:00:00 / 00:00:00 line +term-status-msg= + +# replace mpv with mvi in the window title +title="${?media-title:${media-title}}${!media-title:No file} - mvi" + +# don't slideshow by default +image-display-duration=inf +# loop files in case of webms or gifs +loop-file=inf +# and loop the whole playlist +loop-playlist=inf + +# you need this if you plan to use drag-to-pan or pan-follows-cursor with MOUSE_LEFT +window-dragging=no + +#according to haasn, aspect ratio info for PNG and JPG is "universally bust" +[extension.png] +video-aspect-override=no + +[extension.jpg] +video-aspect-override=no + +[extension.jpeg] +profile=extension.jpg + +[silent] +msg-level=all=no diff --git a/.config/mpv/mvi/script-opts/detect_image.conf b/.config/mpv/mvi/script-opts/detect_image.conf new file mode 100644 index 0000000..586fb95 --- /dev/null +++ b/.config/mpv/mvi/script-opts/detect_image.conf @@ -0,0 +1,17 @@ +# commands to execute when a file detected as an image (1 frame, no audio) is loaded or unloaded + +# an image was loaded, and the previous file was not an image (or there was no previous file) +command_on_first_image_loaded= +# an image was loaded (regardless of what the previous file was) +command_on_image_loaded= +# a non-image was loaded, and the previous file was an image +command_on_non_image_loaded= + +# the purpose of these "hooks" is to let you change bindings, profiles, reset properties... +# see https://mpv.io/manual/master/#list-of-input-commands for general command information +# note that there is no such thing as "unloading a profile", to emulate this you must create an opposite profile and load that + +# example possible values: +#command_on_first_image_loaded=apply-profile image; enable-section image-viewer; script-message status-line-enable +#command_on_image_loaded=no-osd set video-pan-x 0; script-message align-border "" -1 +#command_on_non_image_loaded=disable-section image-viewer; no-osd set video-pan-x 0; no-osd set video-pan-y 0; no-osd set video-zoom 0; script-message status-line-disable diff --git a/.config/mpv/mvi/script-opts/image_positioning.conf b/.config/mpv/mvi/script-opts/image_positioning.conf new file mode 100644 index 0000000..998ec64 --- /dev/null +++ b/.config/mpv/mvi/script-opts/image_positioning.conf @@ -0,0 +1,14 @@ +# size of the margins with drag-to-pan +drag_to_pan_margin=50 +drag_to_pan_move_if_full_view=no + +# size of the margins with pan-follows-cursor +pan_follows_cursor_margin=50 + +# size of the margins with cursor-centric-zoom +cursor_centric_zoom_margin=50 +# if the borders would show up, move the image +# this makes it not exactly cursor-centric in some cases +cursor_centric_zoom_auto_center=yes +# allow zooming out if the image can already fully fit in the window +cursor_centric_zoom_dezoom_if_full_view=no diff --git a/.config/mpv/mvi/script-opts/minimap.conf b/.config/mpv/mvi/script-opts/minimap.conf new file mode 100644 index 0000000..72a05ea --- /dev/null +++ b/.config/mpv/mvi/script-opts/minimap.conf @@ -0,0 +1,19 @@ +# whether to show by default +enabled=yes +# the position of the center of the minimap, in percentage of the window (x, y) +center=92,92 +# the scale of the minimap (i.e. the view rectangle is scale / 100 times the size of the window) +scale=12 +# the cutoff size of the minimap (i.e. the image rectangle is clipped if it falls outside of the this zone) +max_size=16,16 +# opacity of the "image" (from 00=opaque to FF=transparent) +image_opacity=88 +# color of the "image" (#BBGGRR where each component rages from 00 to FF) +image_color=BBBBBB +# opacity of the "view" +view_opacity=BB +view_color=222222 +# whether the view should be drawn above the image +view_above_image=yes +# whether to show the minimap if the current image is fully visible +hide_when_full_image_in_view=yes diff --git a/.config/mpv/mvi/script-opts/ruler.conf b/.config/mpv/mvi/script-opts/ruler.conf new file mode 100644 index 0000000..5e6f48d --- /dev/null +++ b/.config/mpv/mvi/script-opts/ruler.conf @@ -0,0 +1,21 @@ +# whether to show the length of the lines between the two points +show_distance=yes +# whether to show the coordinates of the two points +show_coordinates=yes +# the coordinate space of the text shown. Can be "image", "window", "both" +coordinates_space=image +# can be "degrees", "radians", "both", or "no" +show_angles=degrees +line_width=2 +dots_radius=3 +font_size=36 +# ranges from 00 (black) to FF (white) +line_color=33 +# bindings used to set points. The binding to trigger ruler mode can also be used. Comma-separated list +confirm_bindings=MBTN_LEFT,ENTER +# bindings used to set points. The binding to trigger ruler mode can also be used. Comma-separated list +exit_bindings=ESC +# if yes, the first point will be immediately set at the cursor position when calling 'ruler' +set_first_point_on_begin=no +# if yes, the ruler overlay will be immediately cleared when setting the second point +clear_on_second_point_set=no diff --git a/.config/mpv/mvi/script-opts/status_line.conf b/.config/mpv/mvi/script-opts/status_line.conf new file mode 100644 index 0000000..ec15108 --- /dev/null +++ b/.config/mpv/mvi/script-opts/status_line.conf @@ -0,0 +1,14 @@ +# whether to show by default +enabled=yes +# its font size +size=36 +# distance of the text to the borders +margin=10 +# the text to be expanded +# see property expansion: https://mpv.io/manual/master/#property-expansion +# \N can be used for line breaks +# you can also use ass tags, see here: http://docs.aegisub.org/3.2/ASS_Tags/ +text_top_left= +text_top_right= +text_bottom_left=${filename} [${playlist-pos-1}/${playlist-count}] +text_bottom_right=[${dwidth:X}x${dheight:X}] diff --git a/.config/mpv/mvi/scripts/detect-image.lua b/.config/mpv/mvi/scripts/detect-image.lua new file mode 100644 index 0000000..bfe52c2 --- /dev/null +++ b/.config/mpv/mvi/scripts/detect-image.lua @@ -0,0 +1,73 @@ +local opts = { + command_on_first_image_loaded="", + command_on_image_loaded="", + command_on_non_image_loaded="", +} +local options = require 'mp.options' +local msg = require 'mp.msg' + +options.read_options(opts, nil, function() end) + +function run_maybe(str) + if str ~= "" then + mp.command(str) + end +end + +local was_image = false + +function set_image(is_image) + if is_image and not was_image then + msg.info("First image detected") + run_maybe(opts.command_on_first_image_loaded) + end + if is_image then + msg.info("Image detected") + run_maybe(opts.command_on_image_loaded) + end + if not is_image and was_image then + msg.info("Non-image detected") + run_maybe(opts.command_on_non_image_loaded) + end + was_image = is_image +end + +local properties = {} + +function properties_changed() + local dwidth = properties["dwidth"] + local tracks = properties["track-list"] + local path = properties["path"] + local framecount = properties["estimated-frame-count"] + + if not path or path == "" then return end + if not tracks or #tracks == 0 then return end + local audio_tracks = 0 + for _, track in ipairs(tracks) do + if track.type == "audio" then + audio_tracks = audio_tracks + 1 + end + end + + -- only do things when state is consistent + if not framecount and audio_tracks > 0 then + set_image(false) + elseif framecount and dwidth and dwidth > 0 then + -- png have 0 frames, jpg 1 ¯\_(ツ)_/¯ + set_image((framecount == 0 or framecount == 1) and audio_tracks == 0) + end +end + +function observe(propname) + mp.observe_property(propname, "native", function(_, val) + if val ~= properties[propname] then + properties[propname] = val + msg.verbose("Property " .. propname .. " changed") + properties_changed() + end + end) +end +observe("estimated-frame-count") +observe("track-list") +observe("dwidth") +observe("path") diff --git a/.config/mpv/mvi/scripts/equalizer.lua b/.config/mpv/mvi/scripts/equalizer.lua new file mode 100644 index 0000000..f7a50ab --- /dev/null +++ b/.config/mpv/mvi/scripts/equalizer.lua @@ -0,0 +1,176 @@ +local opts = { + bars = 'brightness,contrast,gamma,saturation,hue', + draw_icons = true, +} + +local msg = require 'mp.msg' +local assdraw = require 'mp.assdraw' +local options = require 'mp.options' +local utils = require 'mp.utils' + +options.read_options(opts, nil, function(c) +end) + +local enabled = false +local active_bars = {} +local bar_being_dragged = nil +local stale = false + +function split_comma(input) + local ret = {} + for str in string.gmatch(input, "([^,]+)") do + ret[#ret + 1] = str + end + return ret +end + +function get_position_normalized(x, y, bar) + return (x - bar.x) / bar.w, (y - bar.y) / bar.h +end + +function handle_mouse_move() + if not bar_being_dragged then return end + local bar = bar_being_dragged + local mx, my = mp.get_mouse_pos() + local nx, _ = get_position_normalized(mx, my, bar) + nx = math.max(0, math.min(nx, 1)) + local val = math.floor(nx * (bar.max_value - bar.min_value) + bar.min_value + 0.5) + mp.set_property_number(bar.property, val) + -- the observe_property call will take care of setting the value +end + +function handle_mouse_left(table) + if table["event"] == "down" then + local mx, my = mp.get_mouse_pos() + for _, bar in ipairs(active_bars) do + local nx, ny = get_position_normalized(mx, my, bar) + if nx >= 0 and ny >= 0 and nx <= 1 and ny <= 1 then + bar_being_dragged = bar + local val = math.floor(nx * (bar.max_value - bar.min_value) + bar.min_value + 0.5) + mp.set_property_number(bar.property, val) + mp.add_forced_key_binding("mouse_move", "mouse_move", handle_mouse_move) + break + end + end + elseif table["event"] == "up" then + mp.remove_key_binding("mouse_move") + bar_being_dragged = nil + end +end + +function property_changed(prop, val) + for _, bar in ipairs(active_bars) do + if bar.property == prop then + bar.value = val + stale = true + break + end + end +end + +function idle_handler() + if not stale then return end + stale = false + local a = assdraw.ass_new() + a:new_event() + a:append(string.format('{\\an0\\bord2\\shad0\\1a&00&\\1c&%s&}', '888888')) + a:pos(0, 0) + a:draw_start() + for _, bar in ipairs(active_bars) do + a:rect_cw(bar.x, bar.y, bar.x + bar.w, bar.y + bar.h) + end + a:new_event() + a:append(string.format('{\\an0\\bord2\\shad0\\1a&00&\\1c&%s&}', 'dddddd')) + a:pos(0, 0) + a:draw_start() + for _, bar in ipairs(active_bars) do + if bar.value > bar.min_value then + local val_norm = (bar.value - bar.min_value) / (bar.max_value - bar.min_value) + a:rect_cw(bar.x, bar.y, bar.x + val_norm * bar.w, bar.y + bar.h) + end + end + for _, bar in ipairs(active_bars) do + a:new_event() + a:append("{\\an6\\fs40\\bord2}") + a:pos(bar.x - 8, bar.y + bar.h/2 - 2) + a:append(bar.property:sub(1,1):upper() .. bar.property:sub(2,-1)) + end + local ww, wh = mp.get_osd_size() + mp.set_osd_ass(ww, wh, a.text) +end + +function fix_position() + local ww, wh = mp.get_osd_size() + for i, bar in ipairs(active_bars) do + bar.x = ww / 5 + bar.y = wh / 2 + i * 50 + bar.w = ww - 2 * (ww / 5) + bar.h = 30 + end +end + +function dimensions_changed() + stale = true + fix_position() +end + +function enable() + if enabled then return end + enabled = true + mp.add_forced_key_binding("MBTN_LEFT", "mouse_left", handle_mouse_left, {complex=true}) + for i, prop in ipairs(split_comma(opts.bars)) do + local prop_info = mp.get_property_native("option-info/" .. prop) + if not prop_info then + msg.warn("Property \'" .. prop .. "\' does not exist") + elseif not prop_info.type == 'Integer' then + msg.warn("Property \'" .. prop .. "\' is not an integer") + else + mp.observe_property(prop, 'native', property_changed) + active_bars[#active_bars + 1] = { + property = prop, + value = mp.get_property_number(prop), + min_value = prop_info.min, + max_value = prop_info.max, + } + end + end + stale = true + fix_position() + mp.observe_property("osd-dimensions", "native", dimensions_changed) + mp.register_idle(idle_handler) +end + +function disable() + if not enabled then return end + enabled = false + active_bars = {} + bar_being_dragged = nil + mp.remove_key_binding("mouse_left") + mp.remove_key_binding("mouse_move") + mp.unobserve_property(property_changed) + mp.unobserve_property(dimensions_changed) + mp.unregister_idle(idle_handler) + mp.set_osd_ass(1280, 720, "") +end + +function toggle() + if enabled then + disable() + else + enable() + end +end + +function reset() + for _, prop in ipairs(split_comma(opts.bars)) do + local prop_info = mp.get_property_native("option-info/" .. prop) + if prop_info and prop_info["default-value"] then + mp.set_property(prop_info["name"], prop_info["default-value"]) + end + end +end + +mp.add_key_binding(nil, "equalizer-enable", enable) +mp.add_key_binding(nil, "equalizer-disable", disable) +mp.add_key_binding(nil, "equalizer-toggle", toggle) +mp.add_key_binding(nil, "equalizer-reset", reset) diff --git a/.config/mpv/mvi/scripts/freeze-window.lua b/.config/mpv/mvi/scripts/freeze-window.lua new file mode 100644 index 0000000..693c717 --- /dev/null +++ b/.config/mpv/mvi/scripts/freeze-window.lua @@ -0,0 +1,14 @@ +-- credit to TheAMM + +local size_changed = false + +mp.register_idle(function() + if not size_changed then return end + local ww, wh = mp.get_osd_size() + if not ww or ww <= 0 or not wh or wh <= 0 then return end + mp.set_property("geometry", string.format("%dx%d", ww, wh)) + size_changed = false +end) + +mp.observe_property("osd-width", "native", function() size_changed = true end) +mp.observe_property("osd-height", "native", function() size_changed = true end) diff --git a/.config/mpv/mvi/scripts/image-positioning.lua b/.config/mpv/mvi/scripts/image-positioning.lua new file mode 100644 index 0000000..c0036d9 --- /dev/null +++ b/.config/mpv/mvi/scripts/image-positioning.lua @@ -0,0 +1,277 @@ +local opts = { + drag_to_pan_margin = 50, + drag_to_pan_move_if_full_view=false, + + pan_follows_cursor_margin = 50, + + cursor_centric_zoom_margin = 50, + cursor_centric_zoom_auto_center = true, + cursor_centric_zoom_dezoom_if_full_view = false, +} +local options = require 'mp.options' +local msg = require 'mp.msg' +local assdraw = require 'mp.assdraw' + +options.read_options(opts, nil, function() end) + +function clamp(value, low, high) + if value <= low then + return low + elseif value >= high then + return high + else + return value + end +end + + +local cleanup = nil -- function set up by drag-to-pan/pan-follows cursor and must be called to clean lingering state + +function drag_to_pan_handler(table) + if cleanup then + cleanup() + cleanup = nil + end + if table["event"] == "down" then + local dim = mp.get_property_native("osd-dimensions") + if not dim then return end + local mouse_pos_origin, video_pan_origin = {}, {} + local moved = false + mouse_pos_origin[1], mouse_pos_origin[2] = mp.get_mouse_pos() + video_pan_origin[1] = mp.get_property_number("video-pan-x") + video_pan_origin[2] = mp.get_property_number("video-pan-y") + local video_size = { dim.w - dim.ml - dim.mr, dim.h - dim.mt - dim.mb } + local margin = opts.drag_to_pan_margin + local move_up = true + local move_lateral = true + if not opts.drag_to_pan_move_if_full_view then + if dim.ml >= 0 and dim.mr >= 0 then + move_lateral = false + end + if dim.mt >= 0 and dim.mb >= 0 then + move_up = false + end + end + if not move_up and not move_lateral then return end + local idle = function() + if moved then + local mX, mY = mp.get_mouse_pos() + local pX = video_pan_origin[1] + local pY = video_pan_origin[2] + if move_lateral then + pX = video_pan_origin[1] + (mX - mouse_pos_origin[1]) / video_size[1] + if 2 * margin > dim.ml + dim.mr then + pX = clamp(pX, + (-margin + dim.w / 2) / video_size[1] - 0.5, + (margin - dim.w / 2) / video_size[1] + 0.5) + else + pX = clamp(pX, + (margin - dim.w / 2) / video_size[1] + 0.5, + (-margin + dim.w / 2) / video_size[1] - 0.5) + end + end + if move_up then + pY = video_pan_origin[2] + (mY - mouse_pos_origin[2]) / video_size[2] + if 2 * margin > dim.mt + dim.mb then + pY = clamp(pY, + (-margin + dim.h / 2) / video_size[2] - 0.5, + (margin - dim.h / 2) / video_size[2] + 0.5) + else + pY = clamp(pY, + (margin - dim.h / 2) / video_size[2] + 0.5, + (-margin + dim.h / 2) / video_size[2] - 0.5) + end + end + mp.command("no-osd set video-pan-x " .. clamp(pX, -3, 3) .. "; no-osd set video-pan-y " .. clamp(pY, -3, 3)) + moved = false + end + end + mp.register_idle(idle) + mp.add_forced_key_binding("mouse_move", "image-viewer-mouse-move", function() moved = true end) + cleanup = function() + mp.remove_key_binding("image-viewer-mouse-move") + mp.unregister_idle(idle) + end + end +end + +function pan_follows_cursor_handler(table) + if cleanup then + cleanup() + cleanup = nil + end + if table["event"] == "down" then + local dim = mp.get_property_native("osd-dimensions") + if not dim then return end + local video_size = { dim.w - dim.ml - dim.mr, dim.h - dim.mt - dim.mb } + local moved = true + local idle = function() + if moved then + local mX, mY = mp.get_mouse_pos() + local x = math.min(1, math.max(- 2 * mX / dim.w + 1, -1)) + local y = math.min(1, math.max(- 2 * mY / dim.h + 1, -1)) + local command = "" + local margin = opts.pan_follows_cursor_margin + if dim.ml + dim.mr < 0 then + command = command .. "no-osd set video-pan-x " .. clamp(x * (2 * margin - dim.ml - dim.mr) / (2 * video_size[1]), -3, 3) .. ";" + elseif mp.get_property_number("video-pan-x") ~= 0 then + command = command .. "no-osd set video-pan-x " .. "0;" + end + if dim.mt + dim.mb < 0 then + command = command .. "no-osd set video-pan-y " .. clamp(y * (2 * margin - dim.mt - dim.mb) / (2 * video_size[2]), -3, 3) .. ";" + elseif mp.get_property_number("video-pan-y") ~= 0 then + command = command .. "no-osd set video-pan-y " .. "0;" + end + if command ~= "" then + mp.command(command) + end + moved = false + end + end + mp.register_idle(idle) + mp.add_forced_key_binding("mouse_move", "image-viewer-mouse-move", function() moved = true end) + cleanup = function() + mp.remove_key_binding("image-viewer-mouse-move") + mp.unregister_idle(idle) + end + end +end + +function cursor_centric_zoom_handler(amt) + local zoom_inc = tonumber(amt) + if not zoom_inc or zoom_inc == 0 then return end + local dim = mp.get_property_native("osd-dimensions") + if not dim then return end + + local margin = opts.cursor_centric_zoom_margin + + local video_size = { dim.w - dim.ml - dim.mr, dim.h - dim.mt - dim.mb } + + -- the size in pixels of the (in|de)crement + local diff_width = (2 ^ zoom_inc - 1) * video_size[1] + local diff_height = (2 ^ zoom_inc - 1) * video_size[2] + if not opts.cursor_centric_zoom_dezoom_if_full_view and + zoom_inc < 0 and + video_size[1] + diff_width + 2 * margin <= dim.w and + video_size[2] + diff_height + 2 * margin <= dim.h + then + -- the zoom decrement is too much, reduce it such that the full image is visible, no more, no less + -- in addition, this should take care of trying too zoom out while everything is already visible + local new_zoom_inc_x = math.log((dim.w - 2 * margin) / video_size[1]) / math.log(2) + local new_zoom_inc_y = math.log((dim.h - 2 * margin) / video_size[2]) / math.log(2) + local new_zoom_inc = math.min(0, math.min(new_zoom_inc_x, new_zoom_inc_y)) + zoom_inc = new_zoom_inc + diff_width = (2 ^ zoom_inc - 1) * video_size[1] + diff_height = (2 ^ zoom_inc - 1) * video_size[2] + end + local new_width = video_size[1] + diff_width + local new_height = video_size[2] + diff_height + + local mouse_pos_origin = {} + mouse_pos_origin[1], mouse_pos_origin[2] = mp.get_mouse_pos() + local new_pan_x, new_pan_y + + -- some additional constraints: + -- if image can be fully visible (in either direction), set pan to 0 + -- if border would show on either side, then prefer adjusting the pan even if not cursor-centric + local auto_c = opts.cursor_centric_zoom_auto_center + if auto_c and new_width <= dim.w then + new_pan_x = 0 + else + local pan_x = mp.get_property("video-pan-x") + local rx = (dim.ml + video_size[1] / 2 - mouse_pos_origin[1]) / (video_size[1] / 2) + new_pan_x = (pan_x * video_size[1] + rx * diff_width / 2) / new_width + if auto_c then + new_pan_x = clamp(new_pan_x, (dim.w - 2 * margin) / (2 * new_width) - 0.5, - (dim.w - 2 * margin) / (2 * new_width) + 0.5) + end + end + + if auto_c and new_height <= dim.h then + new_pan_y = 0 + else + local pan_y = mp.get_property("video-pan-y") + local ry = (dim.mt + video_size[2] / 2 - mouse_pos_origin[2]) / (video_size[2] / 2) + new_pan_y = (pan_y * video_size[2] + ry * diff_height / 2) / new_height + if auto_c then + new_pan_y = clamp(new_pan_y, (dim.h - 2 * margin) / (2 * new_height) - 0.5, - (dim.h - 2 * margin) / (2 * new_height) + 0.5) + end + end + + local zoom_origin = mp.get_property("video-zoom") + mp.command("no-osd set video-zoom " .. zoom_origin + zoom_inc .. "; no-osd set video-pan-x " .. clamp(new_pan_x, -3, 3) .. "; no-osd set video-pan-y " .. clamp(new_pan_y, -3, 3)) +end + +function align_border(x, y) + local dim = mp.get_property_native("osd-dimensions") + if not dim then return end + local video_size = { dim.w - dim.ml - dim.mr, dim.h - dim.mt - dim.mb } + local x, y = tonumber(x), tonumber(y) + local command = "" + if x then + command = command .. "no-osd set video-pan-x " .. clamp(- x * (dim.ml + dim.mr) / (2 * video_size[1]), -3, 3) .. ";" + end + if y then + command = command .. "no-osd set video-pan-y " .. clamp(- y * (dim.mt + dim.mb) / (2 * video_size[2]), -3, 3) .. ";" + end + if command ~= "" then + mp.command(command) + end +end + +function pan_image(axis, amount, zoom_invariant, image_constrained) + amount = tonumber(amount) + if not amount or amount == 0 or axis ~= "x" and axis ~= "y" then return end + if zoom_invariant == "yes" then + amount = amount / 2 ^ mp.get_property_number("video-zoom") + end + local prop = "video-pan-" .. axis + local old_pan = mp.get_property_number(prop) + if image_constrained == "yes" then + local dim = mp.get_property_native("osd-dimensions") + if not dim then return end + local margin = + (axis == "x" and amount > 0) and dim.ml + or (axis == "x" and amount < 0) and dim.mr + or (amount > 0) and dim.mt + or (amount < 0) and dim.mb + local vid_size = (axis == "x") and (dim.w - dim.ml - dim.mr) or (dim.h - dim.mt - dim.mb) + local pixels_moved = math.abs(amount) * vid_size + -- the margin is already visible, no point going further + if margin >= 0 then + return + elseif margin + pixels_moved > 0 then + amount = -(math.abs(amount) / amount) * margin / vid_size + end + end + mp.set_property_number(prop, old_pan + amount) +end + +function rotate_video(amt) + local rot = mp.get_property_number("video-rotate") + rot = (rot + amt) % 360 + mp.set_property_number("video-rotate", rot) +end + +function reset_pan_if_visible() + local dim = mp.get_property_native("osd-dimensions") + if not dim then return end + local command = "" + if (dim.ml + dim.mr >= 0) then + command = command .. "no-osd set video-pan-x 0" .. ";" + end + if (dim.mt + dim.mb >= 0) then + command = command .. "no-osd set video-pan-y 0" .. ";" + end + if command ~= "" then + mp.command(command) + end +end + +mp.add_key_binding(nil, "drag-to-pan", drag_to_pan_handler, {complex = true}) +mp.add_key_binding(nil, "pan-follows-cursor", pan_follows_cursor_handler, {complex = true}) +mp.add_key_binding(nil, "cursor-centric-zoom", cursor_centric_zoom_handler) +mp.add_key_binding(nil, "align-border", align_border) +mp.add_key_binding(nil, "pan-image", pan_image) +mp.add_key_binding(nil, "rotate-video", rotate_video) +mp.add_key_binding(nil, "reset-pan-if-visible", reset_pan_if_visible) +mp.add_key_binding(nil, "force-print-filename", force_print_filename) diff --git a/.config/mpv/mvi/scripts/minimap.lua b/.config/mpv/mvi/scripts/minimap.lua new file mode 100644 index 0000000..f1b9a7d --- /dev/null +++ b/.config/mpv/mvi/scripts/minimap.lua @@ -0,0 +1,201 @@ +local opts = { + enabled = true, + center = "92,92", + scale = 12, + max_size = "16,16", + image_opacity = "88", + image_color = "BBBBBB", + view_opacity = "BB", + view_color = "222222", + view_above_image = true, + hide_when_full_image_in_view = true, +} + +local msg = require 'mp.msg' +local assdraw = require 'mp.assdraw' +local options = require 'mp.options' + +options.read_options(opts, nil, function(c) + if c["enabled"] then + if opts.enabled then + enable() + else + disable() + end + end + mark_stale() +end) + +function split_comma(input) + local ret = {} + for str in string.gmatch(input, "([^,]+)") do + ret[#ret + 1] = tonumber(str) + end + return ret +end + +local active = false +local refresh = true + +function draw_ass(ass) + local ww, wh = mp.get_osd_size() + mp.set_osd_ass(ww, wh, ass) +end + +function mark_stale() + refresh = true +end + +function refresh_minimap() + if not refresh then return end + refresh = false + + local dim = mp.get_property_native("osd-dimensions") + if not dim then + draw_ass("") + return + end + local ww, wh = dim.w, dim.h + + if not (ww > 0 and wh > 0) then return end + if opts.hide_when_full_image_in_view then + if dim.mt >= 0 and dim.mb >= 0 and dim.ml >= 0 and dim.mr >= 0 then + draw_ass("") + return + end + end + + local center = split_comma(opts.center) + center[1] = center[1] * 0.01 * ww + center[2] = center[2] * 0.01 * wh + local cutoff = split_comma(opts.max_size) + cutoff[1] = cutoff[1] * 0.01 * ww * 0.5 + cutoff[2] = cutoff[2] * 0.01 * wh * 0.5 + + local a = assdraw.ass_new() + local draw = function(x, y, w, h, opacity, color) + a:new_event() + a:pos(center[1], center[2]) + a:append("{\\bord0}") + a:append("{\\shad0}") + a:append("{\\c&" .. color .. "&}") + a:append("{\\2a&HFF}") + a:append("{\\3a&HFF}") + a:append("{\\4a&HFF}") + a:append("{\\1a&H" .. opacity .. "}") + w = w * 0.5 + h = h * 0.5 + a:draw_start() + local rounded = {true,true,true,true} -- tl, tr, br, bl + local x0,y0,x1,y1 = x-w, y-h, x+w, y+h + if x0 < -cutoff[1] then + x0 = -cutoff[1] + rounded[4] = false + rounded[1] = false + end + if y0 < -cutoff[2] then + y0 = -cutoff[2] + rounded[1] = false + rounded[2] = false + end + if x1 > cutoff[1] then + x1 = cutoff[1] + rounded[2] = false + rounded[3] = false + end + if y1 > cutoff[2] then + y1 = cutoff[2] + rounded[3] = false + rounded[4] = false + end + + local r = 3 + local c = 0.551915024494 * r + if rounded[0] then + a:move_to(x0 + r, y0) + else + a:move_to(x0,y0) + end + if rounded[1] then + a:line_to(x1 - r, y0) + a:bezier_curve(x1 - r + c, y0, x1, y0 + r - c, x1, y0 + r) + else + a:line_to(x1, y0) + end + if rounded[2] then + a:line_to(x1, y1 - r) + a:bezier_curve(x1, y1 - r + c, x1 - r + c, y1, x1 - r, y1) + else + a:line_to(x1, y1) + end + if rounded[3] then + a:line_to(x0 + r, y1) + a:bezier_curve(x0 + r - c, y1, x0, y1 - r + c, x0, y1 - r) + else + a:line_to(x0, y1) + end + if rounded[4] then + a:line_to(x0, y0 + r) + a:bezier_curve(x0, y0 + r - c, x0 + r - c, y0, x0 + r, y0) + else + a:line_to(x0, y0) + end + a:draw_stop() + end + local image = function() + draw((dim.ml/2 - dim.mr/2) / opts.scale, + (dim.mt/2 - dim.mb/2) / opts.scale, + (ww - dim.ml - dim.mr) / opts.scale, + (wh - dim.mt - dim.mb) / opts.scale, + opts.image_opacity, + opts.image_color) + end + local view = function() + draw(0, + 0, + ww / opts.scale, + wh / opts.scale, + opts.view_opacity, + opts.view_color) + end + if opts.view_above_image then + image() + view() + else + view() + image() + end + draw_ass(a.text) +end + +function enable() + if active then return end + active = true + mp.observe_property("osd-dimensions", nil, mark_stale) + mp.register_idle(refresh_minimap) + mark_stale() +end + +function disable() + if not active then return end + active = false + mp.unobserve_property(mark_stale) + mp.unregister_idle(refresh_minimap) + draw_ass("") +end + +function toggle() + if active then + disable() + else + enable() + end +end + +if opts.enabled then + enable() +end + +mp.add_key_binding(nil, "minimap-enable", enable) +mp.add_key_binding(nil, "minimap-disable", disable) +mp.add_key_binding(nil, "minimap-toggle", toggle) diff --git a/.config/mpv/mvi/scripts/mouse_coords.lua b/.config/mpv/mvi/scripts/mouse_coords.lua new file mode 120000 index 0000000..ecd3896 --- /dev/null +++ b/.config/mpv/mvi/scripts/mouse_coords.lua @@ -0,0 +1 @@ +../../scripts/mouse_coords.lua \ No newline at end of file diff --git a/.config/mpv/mvi/scripts/ruler.lua b/.config/mpv/mvi/scripts/ruler.lua new file mode 100644 index 0000000..2cfcd0f --- /dev/null +++ b/.config/mpv/mvi/scripts/ruler.lua @@ -0,0 +1,314 @@ +local opts = { + show_distance = true, + show_coordinates = true, + coordinates_space = "image", + show_angles = "degrees", + line_width = 2, + dots_radius = 3, + font_size = 36, + line_color = "33", + confirm_bindings = "MBTN_LEFT,ENTER", + exit_bindings = "ESC", + set_first_point_on_begin = false, + clear_on_second_point_set = false, +} + +local options = require 'mp.options' +local msg = require 'mp.msg' +local assdraw = require 'mp.assdraw' + +local state = 0 -- {0,1,2,3} = {inactive,setting first point,setting second point,done} +local first_point = nil -- in normalized video space coordinates +local second_point = nil -- in normalized video space coordinates +local video_dimensions_stale = false + +function split(input) + local ret = {} + for str in string.gmatch(input, "([^,]+)") do + ret[#ret + 1] = str + end + return ret +end + +local confirm_bindings = split(opts.confirm_bindings) +local exit_bindings = split(opts.exit_bindings) + +options.read_options(opts, nil, function() + if state ~= 0 then + remove_bindings() + end + confirm_bindings = split(opts.confirm_bindings) + exit_bindings = split(opts.exit_bindings) + if state ~= 0 then + add_bindings() + mark_stale() + end +end) + +function draw_ass(ass) + local ww, wh = mp.get_osd_size() + mp.set_osd_ass(ww, wh, ass) +end + + +function cursor_video_space_normalized(dim) + local mx, my = mp.get_mouse_pos() + local ret = {} + ret[1] = (mx - dim.ml) / (dim.w - dim.ml - dim.mr) + ret[2] = (my - dim.mt) / (dim.h - dim.mt - dim.mb) + return ret +end + +function refresh() + if not video_dimensions_stale then return end + video_dimensions_stale = false + + local dim = mp.get_property_native("osd-dimensions") + local out_params = mp.get_property_native("video-out-params") + if not dim or not out_params then + draw_ass("") + return + end + local vid_width = out_params.dw + local vid_height = out_params.dh + + function video_space_normalized_to_video(point) + local ret = {} + ret[1] = point[1] * vid_width + ret[2] = point[2] * vid_height + return ret + end + function video_space_normalized_to_screen(point) + local ret = {} + ret[1] = point[1] * (dim.w - dim.ml - dim.mr) + dim.ml + ret[2] = point[2] * (dim.h - dim.mt - dim.mb) + dim.mt + return ret + end + + local line_start = {} + local line_end = {} + if second_point then + line_start.image = video_space_normalized_to_video(first_point) + line_start.screen = video_space_normalized_to_screen(first_point) + line_end.image = video_space_normalized_to_video(second_point) + line_end.screen = video_space_normalized_to_screen(second_point) + elseif first_point then + line_start.image = video_space_normalized_to_video(first_point) + line_start.screen = video_space_normalized_to_screen(first_point) + line_end.image = video_space_normalized_to_video(cursor_video_space_normalized(dim)) + line_end.screen = {} + line_end.screen[1], line_end.screen[2] = mp.get_mouse_pos() + else + local mx, my = mp.get_mouse_pos() + line_start.image = video_space_normalized_to_video(cursor_video_space_normalized(dim)) + line_start.screen = {} + line_start.screen[1], line_start.screen[2] = mp.get_mouse_pos() + line_end = line_start + end + local distinct = (math.abs(line_start.screen[1] - line_end.screen[1]) >= 1 + or math.abs(line_start.screen[2] - line_end.screen[2]) >= 1) + + local a = assdraw:ass_new() + local draw_setup = function(bord) + a:new_event() + a:pos(0,0) + a:append("{\\bord" .. bord .. "}") + a:append("{\\shad0}") + local r = opts.line_color + a:append("{\\3c&H".. r .. r .. r .. "&}") + a:append("{\\1a&HFF}") + a:append("{\\2a&HFF}") + a:append("{\\3a&H00}") + a:append("{\\4a&HFF}") + a:draw_start() + end + local dot = function(pos, size) + draw_setup(size) + a:move_to(pos[1], pos[2]-0.5) + a:line_to(pos[1], pos[2]+0.5) + end + local line = function(from, to, size) + draw_setup(size) + a:move_to(from[1], from[2]) + a:line_to(to[1], to[2]) + end + if distinct then + dot(line_start.screen, opts.dots_radius) + line(line_start.screen, line_end.screen, opts.line_width) + dot(line_end.screen, opts.dots_radius) + else + dot(line_start.screen, opts.dots_radius) + end + + local line_info = function() + if not opts.show_distance then return end + a:new_event() + a:append("{\\fs36}{\\bord1}") + a:pos((line_start.screen[1] + line_end.screen[1]) / 2, (line_start.screen[2] + line_end.screen[2]) / 2) + local an = 1 + if line_start.image[1] < line_end.image[1] then an = an + 2 end + if line_start.image[2] < line_end.image[2] then an = an + 6 end + a:an(an) + local image = math.sqrt(math.pow(line_start.image[1] - line_end.image[1], 2) + math.pow(line_start.image[2] - line_end.image[2], 2)) + local screen = math.sqrt(math.pow(line_start.screen[1] - line_end.screen[1], 2) + math.pow(line_start.screen[2] - line_end.screen[2], 2)) + if opts.coordinates_space == "both" then + a:append(string.format("image: %.1f\\Nscreen: %.1f", image, screen)) + elseif opts.coordinates_space == "image" then + a:append(string.format("%.1f", image)) + elseif opts.coordinates_space == "window" then + a:append(string.format("%.1f", screen)) + end + end + local dot_info = function(pos, opposite) + if not opts.show_coordinates then return end + a:new_event() + a:append("{\\fs" .. opts.font_size .."}{\\bord1}") + a:pos(pos.screen[1], pos.screen[2]) + local an + if distinct then + an = 1 + if line_start.image[1] > line_end.image[1] then an = an + 2 end + if line_start.image[2] < line_end.image[2] then an = an + 6 end + else + an = 7 + end + if opposite then + an = 9 + 1 - an + end + a:an(an) + if opts.coordinates_space == "both" then + a:append(string.format("image: %.1f, %.1f\\Nscreen: %i, %i", + pos.image[1], pos.image[2], pos.screen[1], pos.screen[2])) + elseif opts.coordinates_space == "image" then + a:append(string.format("%.1f, %.1f", pos.image[1], pos.image[2])) + elseif opts.coordinates_space == "window" then + a:append(string.format("%i, %i", pos.screen[1], pos.screen[2])) + end + end + dot_info(line_start, true) + if distinct then + line_info() + dot_info(line_end, false) + end + if distinct and opts.show_angles ~= "no" then + local dist = 50 + local pos_from_angle = function(mult, angle) + return { + line_start.screen[1] + mult * dist * math.cos(angle), + line_start.screen[2] + mult * dist * math.sin(angle), + } + end + local extended = { line_start.screen[1], line_start.screen[2] } + if line_end.screen[1] > line_start.screen[1] then + extended[1] = extended[1] + dist + else + extended[1] = extended[1] - dist + end + line(line_start.screen, extended, math.max(0, opts.line_width-0.5)) + local angle = math.atan(math.abs(line_start.image[2] - line_end.image[2]) / math.abs(line_start.image[1] - line_end.image[1])) + local fix_angle + local an + if line_end.image[2] < line_start.image[2] and line_end.image[1] > line_start.image[1] then + -- upper-right + an = 4 + fix_angle = function(angle) return - angle end + elseif line_end.image[2] < line_start.image[2] then + -- upper-left + an = 6 + fix_angle = function(angle) return math.pi + angle end + elseif line_end.image[1] < line_start.image[1] then + -- bottom-left + an = 6 + fix_angle = function(angle) return math.pi - angle end + else + -- bottom-right + an = 4 + fix_angle = function(angle) return angle end + end + -- should implement this https://math.stackexchange.com/questions/873224/calculate-control-points-of-cubic-bezier-curve-approximating-a-part-of-a-circle + local cp1 = pos_from_angle(1, fix_angle(angle*1/4)) + local cp2 = pos_from_angle(1, fix_angle(angle*3/4)) + local p2 = pos_from_angle(1, fix_angle(angle)) + a:bezier_curve(cp1[1], cp1[2], cp2[1], cp2[2], p2[1], p2[2]) + + a:new_event() + a:append("{\\fs" .. opts.font_size .."}{\\bord1}") + local text_pos = pos_from_angle(1.1, fix_angle(angle*2/3)) -- you'd think /2 would make more sense, but *2/3 looks better + a:pos(text_pos[1], text_pos[2]) + a:an(an) + if opts.show_angles == "both" then + a:append(string.format("%.2f\\N%.1f°", angle, angle / math.pi * 180)) + elseif opts.show_angles == "degrees" then + a:append(string.format("%.1f°", angle / math.pi * 180)) + elseif opts.show_angles == "radians" then + a:append(string.format("%.2f", angle)) + end + end + + draw_ass(a.text) +end + +function mark_stale() + video_dimensions_stale = true +end + +function add_bindings() + mp.add_forced_key_binding("mouse_move", "ruler-mouse-move", mark_stale) + for _, key in ipairs(confirm_bindings) do + mp.add_forced_key_binding(key, "ruler-next-" .. key, next_step) + end + for _, key in ipairs(exit_bindings) do + mp.add_forced_key_binding(key, "ruler-stop-" .. key, stop) + end +end + +function remove_bindings() + for _, key in ipairs(confirm_bindings) do + mp.remove_key_binding("ruler-next-" .. key) + end + for _, key in ipairs(exit_bindings) do + mp.remove_key_binding("ruler-stop-" .. key) + end + mp.remove_key_binding("ruler-mouse-move") +end + +function next_step() + if state == 0 then + state = 1 + mp.register_idle(refresh) + mp.observe_property("osd-dimensions", nil, mark_stale) + mark_stale() + add_bindings() + if opts.set_first_point_on_begin then + next_step() + end + elseif state == 1 then + local dim = mp.get_property_native("osd-dimensions") + if not dim then return end + state = 2 + first_point = cursor_video_space_normalized(dim) + elseif state == 2 then + local dim = mp.get_property_native("osd-dimensions") + if not dim then return end + state = 3 + second_point = cursor_video_space_normalized(dim) + if opts.clear_on_second_point_set then + next_step() + end + else + stop() + end +end + +function stop() + if state == 0 then return end + mp.unregister_idle(refresh) + mp.unobserve_property(mark_stale) + remove_bindings() + state = 0 + first_point = nil + second_point = nil + draw_ass("") +end + +mp.add_key_binding(nil, "ruler", next_step) diff --git a/.config/mpv/mvi/scripts/status-line.lua b/.config/mpv/mvi/scripts/status-line.lua new file mode 100644 index 0000000..7ac5168 --- /dev/null +++ b/.config/mpv/mvi/scripts/status-line.lua @@ -0,0 +1,142 @@ +local opts = { + enabled = true, + size = 36, + margin = 10, + text_top_left = "", + text_top_right = "", + text_bottom_left = "${filename} [${playlist-pos-1}/${playlist-count}]", + text_bottom_right = "[${dwidth:X}x${dheight:X}]", +} + +local msg = require 'mp.msg' +local assdraw = require 'mp.assdraw' +local options = require 'mp.options' + +options.read_options(opts, nil, function(c) + if c["enabled"] then + if opts.enabled then + enable() + else + disable() + end + end + if c["size"] or c["margin"] then + mark_stale() + end + if c["text_top_left"] or + c["text_top_right"] or + c["text_bottom_left"] or + c["text_bottom_right"] + then + observe_properties() + mark_stale() + end +end) + +local stale = true +local active = false + +function draw_ass(ass) + local ww, wh = mp.get_osd_size() + mp.set_osd_ass(ww, wh, ass) +end + +function refresh() + if not stale then return end + stale = false + local a = assdraw:ass_new() + local draw_text = function(text, an, x, y) + if text == "" then return end + local expanded = mp.command_native({ "expand-text", text}) + if not expanded then + msg.error("Error expanding status-line") + return + end + msg.verbose("Status-line changed to: " .. expanded) + a:new_event() + a:an(an) + a:pos(x,y) + a:append("{\\fs".. opts.size.. "}{\\bord1.0}") + a:append(expanded) + end + local w,h = mp.get_osd_size() + local m = opts.margin + draw_text(opts.text_top_left, 7, m, m) + draw_text(opts.text_top_right, 9, w-m, m) + draw_text(opts.text_bottom_left, 1, m, h-m) + draw_text(opts.text_bottom_right, 3, w-m, h-m) + draw_ass(a.text) +end + +function mark_stale() + stale = true +end + +function observe_properties() + mp.unobserve_property(mark_stale) + if not active then return end + for _, str in ipairs({ + opts.text_top_left, + opts.text_top_right, + opts.text_bottom_left, + opts.text_bottom_right, + }) do + local start = 0 + while true do + local s, e, cap = string.find(str, "%${[?!]?([%l%d-/]*)", start) + if not s then break end + msg.verbose("Observing property " .. cap) + mp.observe_property(cap, nil, mark_stale) + start = e + end + end + mp.observe_property("osd-width", nil, mark_stale) + mp.observe_property("osd-height", nil, mark_stale) +end + +function enable() + if active then return end + active = true + observe_properties() + mp.register_idle(refresh) + mark_stale() +end + + +function disable() + if not active then return end + active = false + observe_properties() + mp.unregister_idle(refresh) + draw_ass("") +end + +function toggle() + if active then + disable() + else + enable() + end +end + +if opts.enabled then + enable() +end + +mp.add_key_binding(nil, "status-line-enable", enable) +mp.add_key_binding(nil, "status-line-disable", disable) +mp.add_key_binding(nil, "status-line-toggle", toggle) + +-- TODO remove +mp.add_key_binding(nil, "enable-status-line", function() + msg.warn("This binding is deprecated, use 'status-line-enable' instead") + enable() +end) +mp.add_key_binding(nil, "disable-status-line", function() + msg.warn("This binding is deprecated, use 'status-line-disable' instead") + disable() +end) +mp.add_key_binding(nil, "toggle-status-line", function() + msg.warn("This binding is deprecated, use 'status-line-toggle' instead") + toggle() +end) diff --git a/.config/mpv/script-opts/playlist_view.conf b/.config/mpv/script-opts/playlist_view.conf new file mode 100644 index 0000000..fcec2f0 --- /dev/null +++ b/.config/mpv/script-opts/playlist_view.conf @@ -0,0 +1,16 @@ +start_on_file_end=no + +gallery_position={ 0, 0 } +gallery_size={ ww, wh } + +background_color=101010 +background_opacity=33 + +UP=k +DOWN=j +LEFT=h +RIGHT=l +PAGE_UP=PGUP +PAGE_DOWN=PGDWN +FIRST=g-g +LAST=G diff --git a/.config/mpv/script-opts/subs2srs.conf b/.config/mpv/script-opts/subs2srs.conf new file mode 100644 index 0000000..09e2af4 --- /dev/null +++ b/.config/mpv/script-opts/subs2srs.conf @@ -0,0 +1,13 @@ +autoclip=yes + +deck_name=subs2srs +model_name=Japanese sentences +sentence_field=SentKanji +secondary_field=SentEng +audio_field=SentAudio +image_field=Image +note_tag=subs2srs + +use_ffmpeg=yes +snapshot_format=webp +opus_container=webm diff --git a/.config/mpv/script-opts/thumbfast.conf b/.config/mpv/script-opts/thumbfast.conf new file mode 100644 index 0000000..5f5a2a6 --- /dev/null +++ b/.config/mpv/script-opts/thumbfast.conf @@ -0,0 +1,19 @@ +# Apply tone-mapping, no to disable +# TODO: no required, see https://github.com/po5/thumbfast/issues/135 +tone_mapping=no + +# Enable on network playback +network=no + +# Enable hardware decoding +hwdec=yes + +# Spawn thumbnailer earlier for faster loads +spawn_first=yes + +# Make smaller thumbnails +max_height=150 +max_width=150 + +# Increase display size +scale_factor=1.2 diff --git a/.config/mpv/script-opts/uosc.conf b/.config/mpv/script-opts/uosc.conf new file mode 100644 index 0000000..0ed5f6e --- /dev/null +++ b/.config/mpv/script-opts/uosc.conf @@ -0,0 +1,10 @@ +border_radius=1 +controls=menu,gap,subtitles,<has_many_audio>audio,<has_many_video>video,<has_many_edition>editions,<stream>stream-quality,<has_chapter>chapters,gap,space,speed,space,shuffle,loop-playlist,loop-file,gap,prev,items,next +controls_size=24 +pause_indicator=none +timeline_size=32 +top_bar=always +top_bar_size=32 +top_bar_alt_title=${filename} +top_bar_flash_on=audio,image,video +volume_size=32 diff --git a/.config/mpv/script-opts/webm.conf b/.config/mpv/script-opts/webm.conf new file mode 100644 index 0000000..90b4aae --- /dev/null +++ b/.config/mpv/script-opts/webm.conf @@ -0,0 +1,3 @@ +output_format=av1 +output_directory=~ +output_template=[%s-%e]--%T diff --git a/.config/mpv/scripts/.stylua.toml b/.config/mpv/scripts/.stylua.toml new file mode 100644 index 0000000..616d360 --- /dev/null +++ b/.config/mpv/scripts/.stylua.toml @@ -0,0 +1,6 @@ +column_width = 100 +line_endings = "Unix" +indent_type = "Spaces" +indent_width = 2 +quote_style = "AutoPreferSingle" +#call_parentheses = "Always" diff --git a/.config/mpv/scripts/hooks.lua b/.config/mpv/scripts/hooks.lua new file mode 100644 index 0000000..81660f7 --- /dev/null +++ b/.config/mpv/scripts/hooks.lua @@ -0,0 +1,25 @@ +local mp = require("mp") +local utils = require("mp.utils") + +local function run(...) + mp.msg.warn('running hooks: ' .. table.concat({ ... }, " ")) + utils.subprocess_detached({ + args = { "sh", "-c", table.concat({ ... }, " ") .. " >/dev/null 2>&1" }, + max_size = 0, + cancellable = false, + }) +end + +mp.register_event("file-loaded", function() + local function cb(n, vo) + if vo == true then + run("run-parts", "-v", "--arg=file-loaded", "~/.config/mpv/hooks") + mp.unobserve_property(cb) + end + end + mp.observe_property("vo-configured", "bool", cb) +end) + +mp.register_event("shutdown", function() + run("run-parts", "-v", "--arg=shutdown", "~/.config/mpv/hooks") +end) diff --git a/.config/mpv/scripts/idle_inhibit.lua b/.config/mpv/scripts/idle_inhibit.lua new file mode 100644 index 0000000..6c9ba1b --- /dev/null +++ b/.config/mpv/scripts/idle_inhibit.lua @@ -0,0 +1,5 @@ +mp.observe_property("pause", "bool", function(_, paused) + local value = paused and "no" or "always" + mp.msg.warn(value) + mp.set_property("stop-screensaver", value) +end) diff --git a/.config/mpv/scripts/mouse_coords.lua b/.config/mpv/scripts/mouse_coords.lua new file mode 100644 index 0000000..b2a2a65 --- /dev/null +++ b/.config/mpv/scripts/mouse_coords.lua @@ -0,0 +1,41 @@ +local msg = require("mp.msg") + +-- local x, y = 0, 0 + +-- function set_marker() +-- x, y = mp.get_mouse_pos() +-- end + +-- function measure() +-- x2, y2 = mp.get_mouse_pos() +-- w = x2-x +-- h = y2-y + +-- pos = "x: " .. x .. ", y:" .. y +-- dimensions = "w: " .. h .. ", h:" .. h +-- output = pos .. " | " .. dimensions + +-- mp.msg.warn(output) +-- mp.osd_message(output) +-- end + +function print_pos() + dw = mp.get_property_number("dwidth") + dh = mp.get_property_number("dheight") + ow, oh = mp.get_osd_size() + + wr = math.floor(ow / dw) + hr = math.floor(oh / dh) + + mx, my = mp.get_mouse_pos() + x = math.floor(mx / wr) + y = math.floor(my / hr) + + pos = dw .. "x" .. dh .. " " .. x .. "," .. y + mp.msg.warn(pos) + mp.osd_message(pos) +end + +mp.add_key_binding("0", mp.get_script_name(), print_pos) +-- mp.add_key_binding("8", mp.get_script_name().."/set", set_marker) +-- mp.add_key_binding("9", mp.get_script_name().."/measure", measure) diff --git a/.config/mpv/scripts/openuri.lua b/.config/mpv/scripts/openuri.lua new file mode 100644 index 0000000..61b20e5 --- /dev/null +++ b/.config/mpv/scripts/openuri.lua @@ -0,0 +1,42 @@ +mp = require("mp") +format_json = require("mp.utils").format_json + +function open_uri() + local json = format_json({ + type = "open-uri", + title = "https://mpv.io", + items = { + { + title = "Enter a network url", + align = "left", + value = "ignore", + muted = true, + selectable = false + } + }, + on_search = { "script-message-to", mp.get_script_name(), "openuri-cb" }, + palette = true, + search_style = "palette", + search_debounce = "submit" + }) or "{}" + mp.commandv("script-message-to", "uosc", "open-menu", json) +end + +mp.register_script_message("openuri-cb", function(path) + mp.commandv("script-message-to", "uosc", "close-menu", "openuri") + mp.commandv("loadfile", path, "append-play") +end) + +mp.add_key_binding("ctrl+o", "openuri", open_uri) + +mp.add_key_binding("ctrl+shift+o", "pasteuri", function() + local r = mp.command_native({ + name = "subprocess", + playback_only = false, + capture_stdout = true, + args = { "wl-paste" }, + }) + if r.status == 0 then + mp.commandv("script-message-to", mp.get_script_name(), "openuri-cb", r.stdout) + end +end) diff --git a/.config/mpv/scripts/whisper.lua b/.config/mpv/scripts/whisper.lua new file mode 100644 index 0000000..424edf8 --- /dev/null +++ b/.config/mpv/scripts/whisper.lua @@ -0,0 +1,543 @@ +--[[ + * whisper.lua + * + * AUTHORS: dyphire,robertgzr + * License: MIT +]] + +local msg = require('mp.msg') +local utils = require('mp.utils') +local options = require('mp.options') + +---- Script Options ---- +local o = { + ffmpeg_path = 'ffmpeg', + model = '~/src/edl-toolbox/whisper-ggml-small.bin', + language = 'auto', + queue = '3', + use_gpu = 'true', + gpu_device = '0', + -- Specify output path, supports absolute and relative paths + -- Special value: "source" saves the subtitle file to the directory + -- where the video file is located + output_path = 'source', + -- Specify how many subtitles are generated before updating + -- to avoid frequent flickering of subtitles + update_interval = 20, + -- Segment duration in seconds + segment_duration = 20, +} + +options.read_options(o, _, function() end) +------------------------ + +o.ffmpeg_path = mp.command_native({ 'expand-path', o.ffmpeg_path }) +o.output_path = mp.command_native({ 'expand-path', o.output_path }) + +local pid = mp.get_property_native('pid') +local temp_path = os.getenv('TEMP') or '/tmp/' + +local subtitles_file +local subtitle_count = 1 +local append_subtitle_count = 1 +local subtitles_written = false +local whisper_running = false +local state = {} +local time_ranges = {} + +local is_windows = package.config:sub(1, 1) == '\\' + +local function is_protocol(path) + return type(path) == 'string' + and (path:find('^%a[%w.+-]-://') ~= nil or path:find('^%a[%w.+-]-:%?') ~= nil) +end + +local function file_exists(path) + if path then + local meta = utils.file_info(path) + return meta and meta.is_file + end + return false +end + +local function is_writable(path) + local file = io.open(path, 'w') + if file then + file:close() + os.remove(path) + return true + end + return false +end + +local function check_and_remove_empty_file(file_path) + if file_exists(file_path) then + local file = io.open(file_path, 'r') + if file then + local content = file:read('*all') + file:close() + if content == '' then + os.remove(file_path) + end + end + end +end + +local function normalize(path) + if normalize_path ~= nil then + if normalize_path then + path = mp.command_native({ 'normalize-path', path }) + else + local directory = mp.get_property('working-directory', '') + path = utils.join_path(directory, path:gsub('^%.[\\/]', '')) + if is_windows then + path = path:gsub('\\', '/') + end + end + return path + end + + normalize_path = false + + local commands = mp.get_property_native('command-list', {}) + for _, command in ipairs(commands) do + if command.name == 'normalize-path' then + normalize_path = true + break + end + end + return normalize(path) +end + +local function format_time(time_str) + local h, m, s, ms = nil, nil, nil, nil + if time_str:match('^%d+:%d+:%d+[%.:]%d+$') then + h, m, s, ms = time_str:match('(%d+):(%d+):(%d+)[%.:](%d+)') + elseif time_str:match('^%d+:%d+[%.:]%d+$') then + h = 0 + m, s, ms = time_str:match('(%d+):(%d+)[%.:](%d+)') + else + return time_str + end + + return string.format('%02d:%02d:%02d,%03d', h, m, s, ms) +end + +local function timestamp_to_seconds(timestamp) + local h, m, s, ms = timestamp:match('(%d+):(%d+):(%d+),(%d+)') + return tonumber(h) * 3600 + tonumber(m) * 60 + tonumber(s) + tonumber(ms) / 1000 +end + +local function seconds_to_timestamp(seconds) + local h = math.floor(seconds / 3600) + local m = math.floor(seconds / 60) % 60 + local s = math.floor(seconds % 60) + local ms = math.floor((seconds - math.floor(seconds)) * 1000) + return string.format('%02d:%02d:%02d,%03d', h, m, s, ms) +end + +local function check_sub(sub_file) + local tracks = mp.get_property_native('track-list') + local _, sub_title = utils.split_path(sub_file) + for _, track in ipairs(tracks) do + local external_filename = track['external-filename'] + local track_title = track['title'] + if external_filename then + _, track_title = utils.split_path(external_filename) + end + + if track['type'] == 'sub' and track_title == sub_title then + return true, track['id'] + end + end + return false, nil +end + +local function append_sub(sub_file, auto) + local sub, id = check_sub(sub_file) + if not sub then + if auto then + mp.commandv('sub-add', sub_file, 'auto') + else + mp.commandv('sub-add', sub_file) + end + else + mp.commandv('sub-reload', id) + end +end + +local function shift_subtitle_timestamps(temp_srt, srt_file, subtitle_count, start_time) + local temp_file = io.open(temp_srt, 'r') + local main_file = io.open(srt_file, 'a') + + if not temp_file or not main_file then + msg.error('Failed to open temporary or main SRT file.') + return subtitle_count + end + + local subtitle_number = subtitle_count + if subtitle_number == 1 then + mp.osd_message('AI subtitles are loaded and updated in real time', 5) + msg.info('AI subtitles are loaded and updated in real time') + end + for line in temp_file:lines() do + if line:match('%d+:%d+:%d+,%d+%D+%d+:%d+:%d+,%d+') then + local start_ts, end_ts = line:match('(%d+:%d+:%d+,%d+)%D+(%d+:%d+:%d+,%d+)') + if start_ts and end_ts then + local start_seconds = timestamp_to_seconds(start_ts) + start_time + local end_seconds = timestamp_to_seconds(end_ts) + start_time + main_file:write(subtitle_number .. '\n') + main_file:write( + seconds_to_timestamp(start_seconds) + .. ' --> ' + .. seconds_to_timestamp(end_seconds) + .. '\n' + ) + subtitle_number = subtitle_number + 1 + end + elseif line ~= '' and not tonumber(line) then + main_file:write(line .. '\n') + end + end + + temp_file:close() + main_file:close() + + os.remove(temp_srt) + + return subtitle_number +end +------------------------ + +local function process_audio_segment(video_path, temp_srt_path, start_time_str, segment_duration) + local whisper_filter = { + 'model=' .. normalize(o.model), + 'language=' .. o.language, + 'use_gpu=' .. o.use_gpu, + 'gpu_device=' .. o.gpu_device, + 'queue=' .. o.queue, + 'format=srt', + 'destination=' .. temp_srt_path, + } + local args = { + o.ffmpeg_path, + '-hide_banner', + '-nostdin', + '-y', + '-loglevel', + 'quiet', + '-i', + video_path, + '-ss', + start_time_str, + '-t', + utils.to_string(segment_duration), + '-map', + string.format('a:%s?', mp.get_property_number('current-tracks/audio/id', 0) - 1), + '-vn', + '-sn', + '-af', + 'whisper=' .. table.concat(whisper_filter, ':'), + '-f', + 'null', + '-', + } + local res = mp.command_native({ + name = 'subprocess', + capture_stdout = true, + capture_stderr = true, + args = args, + }) + + if res and res.status ~= 0 then + msg.error('Processing failed for: ' .. video_path .. '\n' .. res.stdout .. ' -- ' .. res.stderr) + return false + end + + return true +end + +local function process_video_incrementally(video_path, srt_file, segment_duration) + local start_time = 0 + local segment_index = 1 + local file_duration = mp.get_property_number('duration') + + msg.info('hi') + + while true do + if start_time >= file_duration then + break + end + if start_time + segment_duration > file_duration then + segment_duration = file_duration - start_time + end + + local temp_srt_file = utils.join_path(temp_path, 'whisper-' .. pid .. '.srt') + if file_exists(temp_srt_file) then + os.remove(temp_srt_file) + end + + local start_time_str = string.format( + '%02d:%02d:%02d', + math.floor(start_time / 3600), + math.floor(start_time / 60) % 60, + start_time % 60 + ) + msg.verbose( + string.format('Processing segment: %d, Start Time: %s', segment_index, start_time_str) + ) + local success = + process_audio_segment(video_path, temp_srt_file, start_time_str, segment_duration) + if not success or not file_exists(temp_srt_file) then + msg.verbose('Segment processing completed or failed.') + break + end + + if file_exists(temp_srt_file) then + subtitle_count = + shift_subtitle_timestamps(temp_srt_file, srt_file, subtitle_count, start_time) + end + + if file_exists(srt_file) then + append_sub(srt_file) + end + + start_time = start_time + segment_duration + segment_index = segment_index + 1 + end +end + +local function whisper_segment() + local path = mp.get_property('path') + local fname = mp.get_property('filename/no-ext') + if not path or is_protocol(path) then + return + end + if path then + path = normalize(path) + dir = utils.split_path(path) + end + + if o.output_path ~= 'source' then + subtitles_file = utils.join_path(o.output_path, fname .. '.srt') + else + subtitles_file = utils.join_path(dir, fname .. '.srt') + end + + if file_exists(subtitles_file) then + msg.info('Subtitles file already exists: ' .. subtitles_file) + return + end + + if not is_writable(subtitles_file) then + subtitles_file = utils.join_path(temp_path, fname .. '.srt') + end + + mp.osd_message('Subtitle generation in progress', 9) + msg.info('Subtitle generation in progress') + msg.verbose('Subtitle file => ' .. subtitles_file) + + whisper_running = true + process_video_incrementally(path, subtitles_file, o.segment_duration) + whisper_running = false + + if file_exists(subtitles_file) then + mp.osd_message('Subtitles successfully generated', 5) + msg.info('Subtitles successfully generated') + append_sub(subtitles_file) + end +end +------------------------ + +local function adjust_time_range(strat_time, end_time) + for _, range in ipairs(time_ranges) do + if not (end_time <= range.start or strat_time >= range.finish) then + if strat_time >= range.start and end_time <= range.finish then + return nil, nil + end + if strat_time < range.finish and end_time > range.start then + if strat_time < range.finish then + strat_time = range.finish + end + if end_time > range.start then + end_time = range.start + end + end + end + end + return strat_time, end_time +end + +local function get_time_range(strat_time, end_time) + strat_time, end_time = adjust_time_range(strat_time, end_time) + if strat_time and strat_time < end_time then + return true, strat_time, end_time + else + return false + end +end + +local function whisper_cache(current_pos, subtitle_count) + local temp_video_file = utils.join_path(temp_path, 'whisper-' .. pid .. '.mkv') + local srt_file = utils.join_path(temp_path, 'whisper.srt') + local file_duration = mp.get_property_number('duration') + local cache_state = mp.get_property_native('demuxer-cache-state') + local cache_ranges = cache_state and cache_state['seekable-ranges'] or {} + local cache_start = cache_ranges[1] and cache_ranges[1]['start'] or current_pos + local cache_end = cache_ranges[1] and cache_ranges[1]['end'] or current_pos + + if current_pos < cache_start or cache_start < state.pos then + current_pos = cache_start + state.pos = current_pos + end + + if current_pos >= file_duration then + if file_exists(srt_file) then + append_sub(srt_file) + end + return + end + + local valid_range, strat_time, end_time = get_time_range(current_pos, cache_end) + if strat_time and end_time then + current_pos = strat_time + cache_end = end_time + end + + if not valid_range or cache_end <= current_pos then + mp.add_timeout(1, function() + whisper_cache(current_pos, subtitle_count) + end) + return + end + + if subtitle_count == 0 then + mp.osd_message('Subtitle generation in progress (from cache)', 9) + msg.info('Subtitle generation in progress (from cache)') + local files_to_remove = { + temp_srt_file1 = utils.join_path(temp_path, 'whisper.srt'), + temp_srt_file2 = utils.join_path(temp_path, 'whisper-' .. pid .. '.srt'), + } + + for _, file in pairs(files_to_remove) do + if file_exists(file) then + os.remove(file) + end + end + end + + whisper_running = true + mp.commandv('dump-cache', math.ceil(current_pos), math.floor(cache_end), temp_video_file) + local temp_srt = srt_file .. '-1.srt' + local success = + process_audio_segment(temp_video_file, temp_srt, current_pos, (cache_end - current_pos)) + if not success then + msg.verbose('Segment processing completed or failed.') + end + whisper_running = false + + if file_exists(temp_srt) then + subtitle_number = shift_subtitle_timestamps(temp_srt, srt_file, subtitle_number, current_pos) + end + + if file_exists(srt_file) then + subtitle_count = subtitle_count + 1 + append_sub(srt_file) + end + + table.insert(time_ranges, { start = current_pos, finish = cache_end }) + table.sort(time_ranges, function(a, b) + return a.start < b.start + end) + + current_pos = cache_end + + -- Callback + mp.add_timeout(1, function() + whisper_cache(current_pos, subtitle_count) + end) +end +------------------------ + +local function whisper_main() + if whisper_running then + return + end + local path = mp.get_property_native('path') + local cache = mp.get_property_native('cache') + local cache_state = mp.get_property_native('demuxer-cache-state') + local cache_ranges = cache_state and cache_state['seekable-ranges'] or {} + if path and is_protocol(path) or cache == 'auto' and #cache_ranges > 0 then + time_ranges = {} + subtitle_count = 0 + local current_pos = mp.get_property_native('time-pos') + local cache_start = cache_ranges[1]['start'] + state.pos = cache_start or current_pos + whisper_cache(cache_start, subtitle_count) + return + end + whisper_segment() +end + +-- mp.register_event('log-message', function(e) +-- if e.prefix ~= mp.get_script_name() then +-- return +-- end +-- +-- local file = subtitles_file and io.open(subtitles_file, 'a') +-- if file and e.text and e.text ~= '' then +-- local text_pattern = '%[([%d+:]?%d+:%d+%.%d+)%D+([%d+:]?%d+:%d+%.%d+)%]%s*(.*)' +-- local start_time_srt, end_time_srt, subtitle_text = e.text:match(text_pattern) +-- if start_time_srt and end_time_srt and subtitle_text then +-- local start_time = format_time(start_time_srt) +-- local end_time = format_time(end_time_srt) +-- +-- file:write(subtitle_count .. '\n') +-- file:write(start_time .. ' --> ' .. end_time .. '\n') +-- file:write(subtitle_text .. '\n') +-- file:close() +-- +-- subtitle_count = subtitle_count + 1 +-- subtitles_written = true +-- end +-- if subtitle_count % o.update_interval == 1 and subtitles_written then +-- if append_subtitle_count == 1 then +-- mp.osd_message('Subtitles are loaded and updated in real time', 5) +-- msg.info('Subtitles are loaded and updated in real time') +-- end +-- append_sub(subtitles_file) +-- subtitles_written = false +-- append_subtitle_count = append_subtitle_count + 1 +-- end +-- end +-- end) + +mp.add_hook('on_unload', 50, function() + start_index = 1 + in_progress_batches = 0 + time_ranges = nil + progress_cache = nil + collectgarbage() + time_ranges = {} + progress_cache = {} + + temp_path = os.getenv('TEMP') or '/tmp/' + local path = mp.get_property('path') + local dir = utils.split_path(path) + local filename = mp.get_property('filename/no-ext') + local files_to_remove = { + temp_video_file = utils.join_path(temp_path, 'whisper-' .. pid .. '.mkv'), + temp_srt_file1 = utils.join_path(temp_path, 'whisper.srt'), + temp_srt_file2 = utils.join_path(temp_path, 'whisper-' .. pid .. '.srt'), + } + + for _, file in pairs(files_to_remove) do + if file_exists(file) then + os.remove(file) + end + end + + check_and_remove_empty_file(subtitles_file) +end) + +mp.register_script_message('whisper/sub-whisper', whisper_main) diff --git a/.config/mpv/scripts/yanky.lua b/.config/mpv/scripts/yanky.lua new file mode 100644 index 0000000..be9d308 --- /dev/null +++ b/.config/mpv/scripts/yanky.lua @@ -0,0 +1,37 @@ +local mp = require("mp") + +function yank(payload) + mp.commandv("run", "wl-copy", payload) + mp.msg.info("yanked: " .. payload) + mp.osd_message("yanked: " .. payload) +end + +function yankproperty(prop) + yank(mp.get_property_native(prop)) +end + +function yankpath() + local path = mp.get_property_native("path") + if path ~= "^http" then + yank(path) + return + end + local cwd = mp.get_property_native("working-directory") + yank(require("mp.utils").join_path(cwd, path)) +end + +function yanktime() + local s = mp.get_property_native("playback-time") + local ts = string.format("%.2d:%.2d:%.2d.%.3d", s / (60 * 60), s / 60 % 60, s % 60, (s % 1) * 1000) + yank(ts) +end + +function yanksubs() + local subText = mp.get_property("sub-text") + -- local secondarySubText = mp.get_property("secondary-sub-text") + yank(subText) +end + +mp.add_key_binding("y-t", "yanktime", yanktime) +mp.add_key_binding("y-p", "yankpath", yankpath) +mp.add_key_binding("y-s", "yanksubs", yanksubs) diff --git a/.config/mpv/update-uosc.sh b/.config/mpv/update-uosc.sh new file mode 100755 index 0000000..7334b58 --- /dev/null +++ b/.config/mpv/update-uosc.sh @@ -0,0 +1,20 @@ +#!/bin/sh +set -e + +URI=" + https://github.com/tomasklaen/uosc/releases/latest/download/uosc.zip + https://github.com/tomasklaen/uosc/releases/latest/download/uosc.conf +" + +target="$(readlink -f -- "$(dirname "$0")")" +tmp=$(mktemp -d) +trap '{ rm -r $tmp; }' EXIT INT +cd "$tmp" || exit 1 + +for src in $URI; do + curl -sLO "$src" + { unzip -qd "$tmp"/ "$tmp"/*.zip 2>/dev/null && rm "$tmp"/*.zip; } || true + { tar -xf "$tmp"/*.tar* -C "$tmp"/ 2>/dev/null && rm "$tmp"/*.tar*; } || true +done + +rsync -ac "$tmp"/ "$target"/ diff --git a/.config/mpv/vapoursynth/interpolate.vpy b/.config/mpv/vapoursynth/interpolate.vpy new file mode 100644 index 0000000..414aa4d --- /dev/null +++ b/.config/mpv/vapoursynth/interpolate.vpy @@ -0,0 +1,84 @@ +# vim: set ft=python: + +# see the README at https://gist.github.com/phiresky/4bfcfbbd05b3c2ed8645 +# source: https://github.com/mpv-player/mpv/issues/2149 +# source: https://github.com/mpv-player/mpv/issues/566 +# source: https://github.com/haasn/gentoo-conf/blob/nanodesu/home/nand/.mpv/filters/mvtools.vpy + +from vapoursynth import core + +# ref: http://avisynth.org.ru/mvtools/mvtools2.html#functions +# default is 400, less means interpolation will only happen when it will work well +ignore_threshold = 140 +# if n% of blocks change more than threshold then don't interpolate at all (default is 51%) +scene_change_percentage = 15 +# default to 60fps +dst_fps = 60 +realtime = True + +if "video_in" in globals(): + # realtime (mpv) + clip = video_in + # Needed because clip FPS is missing + src_fps_num = int(container_fps * 1e8) + src_fps_den = int(1e8) + clip = core.std.AssumeFPS(clip, fpsnum=src_fps_num, fpsden=src_fps_den) + dst_fps = display_fps + # Interpolating to fps higher than 60 is too CPU-expensive, smoothmotion can handle the rest. + while (dst_fps > 60): + dst_fps /= 2 +else: + realtime = False + core.std.LoadPlugin('/usr/lib64/libffms2.so') + # run with vspipe + clip = core.ffms2.Source(source=video_in_path) + if "output_fps" in globals(): + dst_fps = float(output_fps) + +# resolution in megapixels. 1080p ≈ 2MP, 720p ≈ 1MP +mpix = clip.width * clip.height / 1000000 + +# Skip interpolation for >1080p or 60 Hz content due to performance +if not (realtime and (mpix > 2.5 or clip.fps_num / clip.fps_den > 59)): + analParams = { + 'overlap': 0, + 'search': 3, + 'truemotion': True, + #'chrome': True, + #'blksize':16, + #'searchparam':1 + } + blockParams = { + 'thscd1': ignore_threshold, + 'thscd2': int(scene_change_percentage * 255 / 100), + 'mode': 3, + } + + if realtime and mpix > 1.5: + # can't handle these on Full HD with Intel i5-2500k + # see the description of these parameters in http://avisynth.org.ru/mvtools/mvtools2.html#functions + analParams['search'] = 0 + blockParams['mode'] = 0 + quality = 'low' + else: + quality = 'high' + + dst_fps_num = int(dst_fps * 1e4) + dst_fps_den = int(1e4) + print("Reflowing from {} fps to {} fps (quality={})".format( + clip.fps_num / clip.fps_den, dst_fps_num / dst_fps_den, quality)) + + sup = core.mv.Super(clip, pel=2) + bvec = core.mv.Analyse(sup, isb=True, **analParams) + fvec = core.mv.Analyse(sup, isb=False, **analParams) + clip = core.mv.BlockFPS(clip, + sup, + bvec, + fvec, + num=dst_fps_num, + den=dst_fps_den, + **blockParams) +else: + print("Skipping interpolation") + +clip.set_output() diff --git a/.config/udiskie/config.yml b/.config/udiskie/config.yml new file mode 100644 index 0000000..0ee3241 --- /dev/null +++ b/.config/udiskie/config.yml @@ -0,0 +1,25 @@ +tray: auto + +menu: flat + +quickmenu_actions: all + +automount: false + +notify: true + +notifications: + timeout: 3 + +file_manager: env ASK=1 xdg-open + +terminal: foot + +password_prompt: builtin:gui + +device_config: + - device_file: /dev/disk/by-label/fwdrive + automount: true + + - id_uuid: [23e60daf-1b74-4a3c-ad6f-61136f5d89b2, AE45-F3A6] # old hibiki drive + ignore: true |