From 490251af998ec8c9df6cb3c74034a449c89e8c7d Mon Sep 17 00:00:00 2001 From: Rain Date: Fri, 3 Oct 2025 21:45:16 -0400 Subject: [PATCH] Added MPV setup --- dot_config/private_mpv/mpv.conf | 1 + .../private_mpv/script-opts/videoclip.conf | 47 + dot_config/private_mpv/script-opts/webm.conf | 73 + .../scripts/symlink_sponsorblock-minimal.lua | 1 + .../private_mpv/scripts/videoclip/LICENSE | 674 ++++ .../private_mpv/scripts/videoclip/README.md | 172 + .../scripts/videoclip/dot_git/HEAD | 1 + .../scripts/videoclip/dot_git/config | 11 + .../scripts/videoclip/dot_git/description | 1 + .../hooks/executable_applypatch-msg.sample | 15 + .../hooks/executable_commit-msg.sample | 24 + .../executable_fsmonitor-watchman.sample | 174 + .../hooks/executable_post-update.sample | 8 + .../hooks/executable_pre-applypatch.sample | 14 + .../hooks/executable_pre-commit.sample | 49 + .../hooks/executable_pre-merge-commit.sample | 13 + .../dot_git/hooks/executable_pre-push.sample | 53 + .../hooks/executable_pre-rebase.sample | 169 + .../hooks/executable_pre-receive.sample | 24 + .../executable_prepare-commit-msg.sample | 42 + .../hooks/executable_push-to-checkout.sample | 78 + .../executable_sendemail-validate.sample | 77 + .../dot_git/hooks/executable_update.sample | 128 + .../scripts/videoclip/dot_git/index | Bin 0 -> 1113 bytes .../scripts/videoclip/dot_git/info/exclude | 6 + .../scripts/videoclip/dot_git/logs/HEAD | 1 + .../videoclip/dot_git/logs/refs/heads/master | 1 + .../dot_git/logs/refs/remotes/origin/HEAD | 1 + .../videoclip/dot_git/objects/info/.keep | 0 ...877bd46235e2bcf152cdad6227778126a1e6c9.idx | Bin 0 -> 16472 bytes ...77bd46235e2bcf152cdad6227778126a1e6c9.pack | Bin 0 -> 142697 bytes ...877bd46235e2bcf152cdad6227778126a1e6c9.rev | Bin 0 -> 2252 bytes .../scripts/videoclip/dot_git/packed-refs | 6 + .../videoclip/dot_git/refs/heads/master | 1 + .../dot_git/refs/remotes/origin/HEAD | 1 + .../scripts/videoclip/dot_git/refs/tags/.keep | 0 .../scripts/videoclip/dot_github/FUNDING.yml | 5 + .../dot_github/ISSUE_TEMPLATE/config.yml | 13 + .../dot_github/ISSUE_TEMPLATE/issue.md | 37 + .../private_mpv/scripts/videoclip/encoder.lua | 246 ++ .../private_mpv/scripts/videoclip/helpers.lua | 141 + .../private_mpv/scripts/videoclip/main.lua | 1 + .../scripts/videoclip/osd_styler.lua | 108 + .../scripts/videoclip/platform.lua | 95 + .../scripts/videoclip/timings_mgr.lua | 29 + .../scripts/videoclip/videoclip.lua | 476 +++ dot_config/private_mpv/scripts/webm.lua | 2914 +++++++++++++++++ 47 files changed, 5931 insertions(+) create mode 100644 dot_config/private_mpv/mpv.conf create mode 100644 dot_config/private_mpv/script-opts/videoclip.conf create mode 100644 dot_config/private_mpv/script-opts/webm.conf create mode 100644 dot_config/private_mpv/scripts/symlink_sponsorblock-minimal.lua create mode 100644 dot_config/private_mpv/scripts/videoclip/LICENSE create mode 100644 dot_config/private_mpv/scripts/videoclip/README.md create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/HEAD create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/config create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/description create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_applypatch-msg.sample create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_commit-msg.sample create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_fsmonitor-watchman.sample create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_post-update.sample create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_pre-applypatch.sample create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_pre-commit.sample create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_pre-merge-commit.sample create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_pre-push.sample create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_pre-rebase.sample create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_pre-receive.sample create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_prepare-commit-msg.sample create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_push-to-checkout.sample create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_sendemail-validate.sample create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_update.sample create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/index create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/info/exclude create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/logs/HEAD create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/logs/refs/heads/master create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/logs/refs/remotes/origin/HEAD create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/objects/info/.keep create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/objects/pack/readonly_pack-98877bd46235e2bcf152cdad6227778126a1e6c9.idx create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/objects/pack/readonly_pack-98877bd46235e2bcf152cdad6227778126a1e6c9.pack create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/objects/pack/readonly_pack-98877bd46235e2bcf152cdad6227778126a1e6c9.rev create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/packed-refs create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/refs/heads/master create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/refs/remotes/origin/HEAD create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_git/refs/tags/.keep create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_github/FUNDING.yml create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_github/ISSUE_TEMPLATE/config.yml create mode 100644 dot_config/private_mpv/scripts/videoclip/dot_github/ISSUE_TEMPLATE/issue.md create mode 100644 dot_config/private_mpv/scripts/videoclip/encoder.lua create mode 100644 dot_config/private_mpv/scripts/videoclip/helpers.lua create mode 100644 dot_config/private_mpv/scripts/videoclip/main.lua create mode 100644 dot_config/private_mpv/scripts/videoclip/osd_styler.lua create mode 100644 dot_config/private_mpv/scripts/videoclip/platform.lua create mode 100644 dot_config/private_mpv/scripts/videoclip/timings_mgr.lua create mode 100644 dot_config/private_mpv/scripts/videoclip/videoclip.lua create mode 100644 dot_config/private_mpv/scripts/webm.lua diff --git a/dot_config/private_mpv/mpv.conf b/dot_config/private_mpv/mpv.conf new file mode 100644 index 0000000..ff66aed --- /dev/null +++ b/dot_config/private_mpv/mpv.conf @@ -0,0 +1 @@ +save-position-on-quit diff --git a/dot_config/private_mpv/script-opts/videoclip.conf b/dot_config/private_mpv/script-opts/videoclip.conf new file mode 100644 index 0000000..2878ba3 --- /dev/null +++ b/dot_config/private_mpv/script-opts/videoclip.conf @@ -0,0 +1,47 @@ +# Absolute paths to the folders where generated clips will be placed. +# `~` is supported, but environment variables (e.g. `$HOME`) are not supported due to mpv limitations. +video_folder_path=~/Desktop +audio_folder_path=~/Desktop + + +# Clean filenames (remove special characters) (yes or no) +clean_filename=yes + +# Video settings +video_width=-2 +video_height=720 +video_bitrate=1M +# Available video formats: mp4, vp9, vp8 +video_format=mp4 +# The range of the scale is 0–51, where 0 is lossless, +# 23 is the default, and 51 is worst quality possible. +# Insane values like 9999 still work but produce the worst quality. +video_quality=23 +# Use the slowest preset that you have patience for. +# https://trac.ffmpeg.org/wiki/Encode/H.264 +preset=fastest +# FPS / framerate. Set to "auto" or a number. +video_fps=auto +#video_fps=60 + +# Audio settings +# Available formats: opus or aac +audio_format=opus +# Opus sounds good at low bitrates 32-64k, but aac requires 128-256k. +audio_bitrate=32k + +# Catbox.moe upload settings +# Whether uploads should go to litterbox instead of catbox. +# catbox files are stored permanently, while litterbox is temporary +litterbox=yes +# If using litterbox, time until video expires +# Available values: 1h, 12h, 24h, 72h +litterbox_expire=72h + +# Filename format +# Available tags: %n = filename, %t = title, %s = start, %e = end, %d = duration, +# %Y = year, %M = months, %D = day, %H = hours (24), %I = hours (12), +# %P = am/pm %N = minutes, %S = seconds +# Title will fallback to filename if it's not present +#filename_template=%n_%s-%e(%d) +filename_template=%n_%s-%e diff --git a/dot_config/private_mpv/script-opts/webm.conf b/dot_config/private_mpv/script-opts/webm.conf new file mode 100644 index 0000000..8360ef1 --- /dev/null +++ b/dot_config/private_mpv/script-opts/webm.conf @@ -0,0 +1,73 @@ +# Defaults to shift+w +keybind=W +# If empty, saves on the same directory of the playing video. +# A starting "~" will be replaced by the home dir. +# This field is delimited by double-square-brackets - [[ and ]] - instead of +# quotes, because Windows users might run into a issue when using +# backslashes as a path separator. Examples of valid inputs for this field +# would be: [[]] (the default, empty value), [[C:\Users\John]] (on Windows), +# and [[/home/john]] (on Unix-like systems eg. Linux). +# The [[]] delimiter is not needed when using from a configuration file +# in the script-opts folder. +output_directory=~/Desktop +run_detached=no +# Template string for the output file +# %f - Filename, with extension +# %F - Filename, without extension +# %T - Media title, if it exists, or filename, with extension (useful for some streams, such as YouTube). +# %s, %e - Start and end time, with milliseconds +# %S, %E - Start and end time, without milliseconds +# %M - "-audio", if audio is enabled, empty otherwise +# %R - "-(height)p", where height is the video's height, or scale_height, if it's enabled. +# More specifiers are supported, see https://mpv.io/manual/master/#options-screenshot-template +# Property expansion is supported (with %{} at top level, ${} when nested), see https://mpv.io/manual/master/#property-expansion +output_template=%F-[%s-%e]%M +# Scale video to a certain height, keeping the aspect ratio. -1 disables it. +scale_height=-1 +# Change the FPS of the output video, dropping or duplicating frames as needed. +# -1 means the FPS will be unchanged from the source. +fps=-1 +# Target filesize, in kB. This will be used to calculate the bitrate +# used on the encode. If this is set to <= 0, the video bitrate will be set +# to 0, which might enable constant quality modes, depending on the +# video codec that's used (VP8 and VP9, for example). +target_filesize=2500 +# If true, will use stricter flags to ensure the resulting file doesn't +# overshoot the target filesize. Not recommended, as constrained quality +# mode should work well, unless you're really having trouble hitting +# the target size. +strict_filesize_constraint=no +strict_bitrate_multiplier=0.95 +# In kilobits. +strict_audio_bitrate=64 +# Sets the output format, from a few predefined ones. +# Currently we have: +# webm-vp8 (libvpx/libvorbis) +# webm-vp9 (libvpx-vp9/libopus) +# mp4 (h264/AAC) +# mp4-nvenc (h264-NVENC/AAC) +# raw (rawvideo/pcm_s16le). +# mp3 (libmp3lame) +# and gif +output_format=mp4 +twopass=no +# If set, applies the video filters currently used on the playback to the encode. +apply_current_filters=yes +# If set, writes the video's filename to the "Title" field on the metadata. +write_filename_on_metadata=no +# Set the number of encoding threads, for codecs libvpx and libvpx-vp9 +libvpx_threads=4 +additional_flags= +# Constant Rate Factor (CRF). The value meaning and limits may change, +# from codec to codec. Set to -1 to disable. +crf=28 +# Useful for flags that may impact output filesize, such as qmin, qmax etc +# Won't be applied when strict_filesize_constraint is on. +non_strict_additional_flags= +# Display the encode progress, in %. Requires run_detached to be disabled. +# On Windows, it shows a cmd popup. "auto" will display progress on non-Windows platforms. +display_progress=auto +# The font size used in the menu. Isn't used for the notifications (started encode, finished encode etc) +font_size=28 +margin=10 +message_duration=5 diff --git a/dot_config/private_mpv/scripts/symlink_sponsorblock-minimal.lua b/dot_config/private_mpv/scripts/symlink_sponsorblock-minimal.lua new file mode 100644 index 0000000..b02411e --- /dev/null +++ b/dot_config/private_mpv/scripts/symlink_sponsorblock-minimal.lua @@ -0,0 +1 @@ +/usr/lib/mpv/sponsorblock-minimal.lua diff --git a/dot_config/private_mpv/scripts/videoclip/LICENSE b/dot_config/private_mpv/scripts/videoclip/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/dot_config/private_mpv/scripts/videoclip/README.md b/dot_config/private_mpv/scripts/videoclip/README.md new file mode 100644 index 0000000..3b78284 --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/README.md @@ -0,0 +1,172 @@ +![screenshot](https://github.com/Lemmmy/videoclip/assets/858456/855bff15-b0cd-4c12-a9ac-40a5e01d3b83) + +# videoclip + +[![Chat](https://img.shields.io/badge/chat-join-green)](https://tatsumoto-ren.github.io/blog/join-our-community.html) +![GitHub](https://img.shields.io/github/license/Ajatt-Tools/videoclip) +![GitHub top language](https://img.shields.io/github/languages/top/Ajatt-Tools/videoclip) +[![Patreon](https://img.shields.io/badge/support-patreon-orange)](https://tatsumoto.neocities.org/blog/donating-to-tatsumoto.html) + +Easily create video and audio clips with mpv in a few keypresses. +Videoclips are saved as `.mp4` or `.webm`. +Subtitles can be embedded into the clips. + +## Prerequisites + +1) [Install mpv](https://mpv.io/installation/). +2) Add the directory where `mpv` is installed + to the [PATH](https://www.mojeek.com/search?q=path+variable). + + If you're using GNU/Linux, this step is likely unnecessary + because package managers (`apt`, `pacman`, etc.) + place executable files to `/usr/bin` which is already added to the `PATH`. + If you have installed `mpv` to a non-standard location, + or if you're not using the GNU operating system, + you need to make sure that `mpv` is added to the `PATH`. + +## Installation + +### Using git + +Clone the repository to the `mpv/scripts` directory. +The command below works on the GNU operating system with `git` installed. + +``` bash +git clone 'https://github.com/Ajatt-Tools/videoclip.git' ~/.config/mpv/scripts/videoclip +``` + +To update the user-script on demand later, you can execute: + +``` bash +cd ~/.config/mpv/scripts/videoclip && git pull +``` + +### Manually + +Download +[the repository](https://github.com/Ajatt-Tools/videoclip/archive/refs/heads/master.zip) +and extract the folder containing +`videoclip.lua` +to your [mpv scripts](https://github.com/mpv-player/mpv/wiki/User-Scripts) directory: + +| OS | Location | +| --- | --- | +| GNU/Linux | `~/.config/mpv/scripts/` | +| Windows | `C:/Users/Username/AppData/Roaming/mpv/scripts/` | + +Note: in [Celluloid](https://www.archlinux.org/packages/community/x86_64/celluloid/) +user scripts are installed by switching to the "Plugins" tab +in the preferences dialog and dropping the files there. + +
+ +Expected directory tree + +``` +~/.config/mpv/scripts +|-- other_addon_1 +|-- other_addon_2 +`-- videoclip + |-- main.lua + |-- ... + `-- videoclip.lua +``` + +
+ +## Configuration + +The config file should be created by the user, if needed. + +| OS | Config location | +| --- | --- | +| GNU/Linux | `~/.config/mpv/script-opts/videoclip.conf` | +| Windows | `C:/Users/Username/AppData/Roaming/mpv/script-opts/videoclip.conf` | + +If a parameter is not specified in the config file, the default value will be used. +mpv doesn't tolerate spaces before and after `=`. + +Example configuration file: + +``` +# Absolute paths to the folders where generated clips will be placed. +# `~` is supported, but environment variables (e.g. `$HOME`) are not supported due to mpv limitations. +video_folder_path=~/Videos +audio_folder_path=~/Music + +# Menu size +font_size=24 + +# OSD settings. Line alignment: https://aegisub.org/docs/3.2/ASS_Tags/#\an +osd_align=7 +osd_outline=1.5 + +# Clean filenames (remove special characters) (yes or no) +clean_filename=yes + +# Video settings +video_width=-2 +video_height=480 +video_bitrate=1M +# Available video formats: mp4, vp9, vp8 +video_format=mp4 +# The range of the scale is 0–51, where 0 is lossless, +# 23 is the default, and 51 is worst quality possible. +# Insane values like 9999 still work but produce the worst quality. +video_quality=23 +# Use the slowest preset that you have patience for. +# https://trac.ffmpeg.org/wiki/Encode/H.264 +preset=faster +# FPS / framerate. Set to "auto" or a number. +video_fps=auto +#video_fps=60 + +# Audio settings +# Available formats: opus or aac +audio_format=opus +# Opus sounds good at low bitrates 32-64k, but aac requires 128-256k. +audio_bitrate=32k + +# Catbox.moe upload settings +# Whether uploads should go to litterbox instead of catbox. +# catbox files are stored permanently, while litterbox is temporary +litterbox=yes +# If using litterbox, time until video expires +# Available values: 1h, 12h, 24h, 72h +litterbox_expire=72h + +# Filename format +# Available tags: %n = filename, %t = title, %s = start, %e = end, %d = duration, +# %Y = year, %M = months, %D = day, %H = hours (24), %I = hours (12), +# %P = am/pm %N = minutes, %S = seconds +# Title will fallback to filename if it's not present +#filename_template=%n_%s-%e(%d) +filename_template=%n_%s-%e +``` + +### Key bindings + +| OS | Config location | +| --- | --- | +| GNU/Linux | `~/.config/mpv/input.conf` | +| Windows | `C:/Users/Username/AppData/Roaming/mpv/input.conf` | + +Add this line if you want to change the key that opens the script's menu. + +``` +c script-binding videoclip-menu-open +``` + +## Usage + +- Open a file in mpv and press `c` to open the script menu. +- Follow the onscreen instructions. You need to set the `start point`, +`end point`, and then press `c` to create the clip. + +It is possible to create silent videoclips. +To do that, first mute audio in mpv. +The default key binding is `m`. + +If a video has visible subtitles, they will be embedded automatically. +Toggle them off in mpv if you don't want any subtitles to be visible. +The default key binding is `v`. diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/HEAD b/dot_config/private_mpv/scripts/videoclip/dot_git/HEAD new file mode 100644 index 0000000..cb089cd --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_git/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/config b/dot_config/private_mpv/scripts/videoclip/dot_git/config new file mode 100644 index 0000000..ae756d9 --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_git/config @@ -0,0 +1,11 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true +[remote "origin"] + url = https://github.com/Ajatt-Tools/videoclip.git + fetch = +refs/heads/*:refs/remotes/origin/* +[branch "master"] + remote = origin + merge = refs/heads/master diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/description b/dot_config/private_mpv/scripts/videoclip/dot_git/description new file mode 100644 index 0000000..498b267 --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_git/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_applypatch-msg.sample b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_applypatch-msg.sample new file mode 100644 index 0000000..a5d7b84 --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_applypatch-msg.sample @@ -0,0 +1,15 @@ +#!/bin/sh +# +# An example hook script to check the commit log message taken by +# applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. The hook is +# allowed to edit the commit message file. +# +# To enable this hook, rename this file to "applypatch-msg". + +. git-sh-setup +commitmsg="$(git rev-parse --git-path hooks/commit-msg)" +test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"} +: diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_commit-msg.sample b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_commit-msg.sample new file mode 100644 index 0000000..b58d118 --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_commit-msg.sample @@ -0,0 +1,24 @@ +#!/bin/sh +# +# An example hook script to check the commit log message. +# Called by "git commit" with one argument, the name of the file +# that has the commit message. The hook should exit with non-zero +# status after issuing an appropriate message if it wants to stop the +# commit. The hook is allowed to edit the commit message file. +# +# To enable this hook, rename this file to "commit-msg". + +# Uncomment the below to add a Signed-off-by line to the message. +# Doing this in a hook is a bad idea in general, but the prepare-commit-msg +# hook is more suited to it. +# +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" + +# This example catches duplicate Signed-off-by lines. + +test "" = "$(grep '^Signed-off-by: ' "$1" | + sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { + echo >&2 Duplicate Signed-off-by lines. + exit 1 +} diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_fsmonitor-watchman.sample b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_fsmonitor-watchman.sample new file mode 100644 index 0000000..23e856f --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_fsmonitor-watchman.sample @@ -0,0 +1,174 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use IPC::Open2; + +# An example hook script to integrate Watchman +# (https://facebook.github.io/watchman/) with git to speed up detecting +# new and modified files. +# +# The hook is passed a version (currently 2) and last update token +# formatted as a string and outputs to stdout a new update token and +# all files that have been modified since the update token. Paths must +# be relative to the root of the working tree and separated by a single NUL. +# +# To enable this hook, rename this file to "query-watchman" and set +# 'git config core.fsmonitor .git/hooks/query-watchman' +# +my ($version, $last_update_token) = @ARGV; + +# Uncomment for debugging +# print STDERR "$0 $version $last_update_token\n"; + +# Check the hook interface version +if ($version ne 2) { + die "Unsupported query-fsmonitor hook version '$version'.\n" . + "Falling back to scanning...\n"; +} + +my $git_work_tree = get_working_dir(); + +my $retry = 1; + +my $json_pkg; +eval { + require JSON::XS; + $json_pkg = "JSON::XS"; + 1; +} or do { + require JSON::PP; + $json_pkg = "JSON::PP"; +}; + +launch_watchman(); + +sub launch_watchman { + my $o = watchman_query(); + if (is_work_tree_watched($o)) { + output_result($o->{clock}, @{$o->{files}}); + } +} + +sub output_result { + my ($clockid, @files) = @_; + + # Uncomment for debugging watchman output + # open (my $fh, ">", ".git/watchman-output.out"); + # binmode $fh, ":utf8"; + # print $fh "$clockid\n@files\n"; + # close $fh; + + binmode STDOUT, ":utf8"; + print $clockid; + print "\0"; + local $, = "\0"; + print @files; +} + +sub watchman_clock { + my $response = qx/watchman clock "$git_work_tree"/; + die "Failed to get clock id on '$git_work_tree'.\n" . + "Falling back to scanning...\n" if $? != 0; + + return $json_pkg->new->utf8->decode($response); +} + +sub watchman_query { + my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty') + or die "open2() failed: $!\n" . + "Falling back to scanning...\n"; + + # In the query expression below we're asking for names of files that + # changed since $last_update_token but not from the .git folder. + # + # To accomplish this, we're using the "since" generator to use the + # recency index to select candidate nodes and "fields" to limit the + # output to file names only. Then we're using the "expression" term to + # further constrain the results. + my $last_update_line = ""; + if (substr($last_update_token, 0, 1) eq "c") { + $last_update_token = "\"$last_update_token\""; + $last_update_line = qq[\n"since": $last_update_token,]; + } + my $query = <<" END"; + ["query", "$git_work_tree", {$last_update_line + "fields": ["name"], + "expression": ["not", ["dirname", ".git"]] + }] + END + + # Uncomment for debugging the watchman query + # open (my $fh, ">", ".git/watchman-query.json"); + # print $fh $query; + # close $fh; + + print CHLD_IN $query; + close CHLD_IN; + my $response = do {local $/; }; + + # Uncomment for debugging the watch response + # open ($fh, ">", ".git/watchman-response.json"); + # print $fh $response; + # close $fh; + + die "Watchman: command returned no output.\n" . + "Falling back to scanning...\n" if $response eq ""; + die "Watchman: command returned invalid output: $response\n" . + "Falling back to scanning...\n" unless $response =~ /^\{/; + + return $json_pkg->new->utf8->decode($response); +} + +sub is_work_tree_watched { + my ($output) = @_; + my $error = $output->{error}; + if ($retry > 0 and $error and $error =~ m/unable to resolve root .* directory (.*) is not watched/) { + $retry--; + my $response = qx/watchman watch "$git_work_tree"/; + die "Failed to make watchman watch '$git_work_tree'.\n" . + "Falling back to scanning...\n" if $? != 0; + $output = $json_pkg->new->utf8->decode($response); + $error = $output->{error}; + die "Watchman: $error.\n" . + "Falling back to scanning...\n" if $error; + + # Uncomment for debugging watchman output + # open (my $fh, ">", ".git/watchman-output.out"); + # close $fh; + + # Watchman will always return all files on the first query so + # return the fast "everything is dirty" flag to git and do the + # Watchman query just to get it over with now so we won't pay + # the cost in git to look up each individual file. + my $o = watchman_clock(); + $error = $output->{error}; + + die "Watchman: $error.\n" . + "Falling back to scanning...\n" if $error; + + output_result($o->{clock}, ("/")); + $last_update_token = $o->{clock}; + + eval { launch_watchman() }; + return 0; + } + + die "Watchman: $error.\n" . + "Falling back to scanning...\n" if $error; + + return 1; +} + +sub get_working_dir { + my $working_dir; + if ($^O =~ 'msys' || $^O =~ 'cygwin') { + $working_dir = Win32::GetCwd(); + $working_dir =~ tr/\\/\//; + } else { + require Cwd; + $working_dir = Cwd::cwd(); + } + + return $working_dir; +} diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_post-update.sample b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_post-update.sample new file mode 100644 index 0000000..ec17ec1 --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_post-update.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script to prepare a packed repository for use over +# dumb transports. +# +# To enable this hook, rename this file to "post-update". + +exec git update-server-info diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_pre-applypatch.sample b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_pre-applypatch.sample new file mode 100644 index 0000000..4142082 --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_pre-applypatch.sample @@ -0,0 +1,14 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed +# by applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-applypatch". + +. git-sh-setup +precommit="$(git rev-parse --git-path hooks/pre-commit)" +test -x "$precommit" && exec "$precommit" ${1+"$@"} +: diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_pre-commit.sample b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_pre-commit.sample new file mode 100644 index 0000000..29ed5ee --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_pre-commit.sample @@ -0,0 +1,49 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by "git commit" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-commit". + +if git rev-parse --verify HEAD >/dev/null 2>&1 +then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=$(git hash-object -t tree /dev/null) +fi + +# If you want to allow non-ASCII filenames set this variable to true. +allownonascii=$(git config --type=bool hooks.allownonascii) + +# Redirect output to stderr. +exec 1>&2 + +# Cross platform projects tend to avoid non-ASCII filenames; prevent +# them from being added to the repository. We exploit the fact that the +# printable range starts at the space character and ends with tilde. +if [ "$allownonascii" != "true" ] && + # Note that the use of brackets around a tr range is ok here, (it's + # even required, for portability to Solaris 10's /usr/bin/tr), since + # the square bracket bytes happen to fall in the designated range. + test $(git diff-index --cached --name-only --diff-filter=A -z $against | + LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 +then + cat <<\EOF +Error: Attempt to add a non-ASCII file name. + +This can cause problems if you want to work with people on other platforms. + +To be portable it is advisable to rename the file. + +If you know what you are doing you can disable this check using: + + git config hooks.allownonascii true +EOF + exit 1 +fi + +# If there are whitespace errors, print the offending file names and fail. +exec git diff-index --check --cached $against -- diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_pre-merge-commit.sample b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_pre-merge-commit.sample new file mode 100644 index 0000000..399eab1 --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_pre-merge-commit.sample @@ -0,0 +1,13 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by "git merge" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message to +# stderr if it wants to stop the merge commit. +# +# To enable this hook, rename this file to "pre-merge-commit". + +. git-sh-setup +test -x "$GIT_DIR/hooks/pre-commit" && + exec "$GIT_DIR/hooks/pre-commit" +: diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_pre-push.sample b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_pre-push.sample new file mode 100644 index 0000000..4ce688d --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_pre-push.sample @@ -0,0 +1,53 @@ +#!/bin/sh + +# An example hook script to verify what is about to be pushed. Called by "git +# push" after it has checked the remote status, but before anything has been +# pushed. If this script exits with a non-zero status nothing will be pushed. +# +# This hook is called with the following parameters: +# +# $1 -- Name of the remote to which the push is being done +# $2 -- URL to which the push is being done +# +# If pushing without using a named remote those arguments will be equal. +# +# Information about the commits which are being pushed is supplied as lines to +# the standard input in the form: +# +# +# +# This sample shows how to prevent push of commits where the log message starts +# with "WIP" (work in progress). + +remote="$1" +url="$2" + +zero=$(git hash-object --stdin &2 "Found WIP commit in $local_ref, not pushing" + exit 1 + fi + fi +done + +exit 0 diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_pre-rebase.sample b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_pre-rebase.sample new file mode 100644 index 0000000..6cbef5c --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_pre-rebase.sample @@ -0,0 +1,169 @@ +#!/bin/sh +# +# Copyright (c) 2006, 2008 Junio C Hamano +# +# The "pre-rebase" hook is run just before "git rebase" starts doing +# its job, and can prevent the command from running by exiting with +# non-zero status. +# +# The hook is called with the following parameters: +# +# $1 -- the upstream the series was forked from. +# $2 -- the branch being rebased (or empty when rebasing the current branch). +# +# This sample shows how to prevent topic branches that are already +# merged to 'next' branch from getting rebased, because allowing it +# would result in rebasing already published history. + +publish=next +basebranch="$1" +if test "$#" = 2 +then + topic="refs/heads/$2" +else + topic=`git symbolic-ref HEAD` || + exit 0 ;# we do not interrupt rebasing detached HEAD +fi + +case "$topic" in +refs/heads/??/*) + ;; +*) + exit 0 ;# we do not interrupt others. + ;; +esac + +# Now we are dealing with a topic branch being rebased +# on top of master. Is it OK to rebase it? + +# Does the topic really exist? +git show-ref -q "$topic" || { + echo >&2 "No such branch $topic" + exit 1 +} + +# Is topic fully merged to master? +not_in_master=`git rev-list --pretty=oneline ^master "$topic"` +if test -z "$not_in_master" +then + echo >&2 "$topic is fully merged to master; better remove it." + exit 1 ;# we could allow it, but there is no point. +fi + +# Is topic ever merged to next? If so you should not be rebasing it. +only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` +only_next_2=`git rev-list ^master ${publish} | sort` +if test "$only_next_1" = "$only_next_2" +then + not_in_topic=`git rev-list "^$topic" master` + if test -z "$not_in_topic" + then + echo >&2 "$topic is already up to date with master" + exit 1 ;# we could allow it, but there is no point. + else + exit 0 + fi +else + not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` + /usr/bin/perl -e ' + my $topic = $ARGV[0]; + my $msg = "* $topic has commits already merged to public branch:\n"; + my (%not_in_next) = map { + /^([0-9a-f]+) /; + ($1 => 1); + } split(/\n/, $ARGV[1]); + for my $elem (map { + /^([0-9a-f]+) (.*)$/; + [$1 => $2]; + } split(/\n/, $ARGV[2])) { + if (!exists $not_in_next{$elem->[0]}) { + if ($msg) { + print STDERR $msg; + undef $msg; + } + print STDERR " $elem->[1]\n"; + } + } + ' "$topic" "$not_in_next" "$not_in_master" + exit 1 +fi + +<<\DOC_END + +This sample hook safeguards topic branches that have been +published from being rewound. + +The workflow assumed here is: + + * Once a topic branch forks from "master", "master" is never + merged into it again (either directly or indirectly). + + * Once a topic branch is fully cooked and merged into "master", + it is deleted. If you need to build on top of it to correct + earlier mistakes, a new topic branch is created by forking at + the tip of the "master". This is not strictly necessary, but + it makes it easier to keep your history simple. + + * Whenever you need to test or publish your changes to topic + branches, merge them into "next" branch. + +The script, being an example, hardcodes the publish branch name +to be "next", but it is trivial to make it configurable via +$GIT_DIR/config mechanism. + +With this workflow, you would want to know: + +(1) ... if a topic branch has ever been merged to "next". Young + topic branches can have stupid mistakes you would rather + clean up before publishing, and things that have not been + merged into other branches can be easily rebased without + affecting other people. But once it is published, you would + not want to rewind it. + +(2) ... if a topic branch has been fully merged to "master". + Then you can delete it. More importantly, you should not + build on top of it -- other people may already want to + change things related to the topic as patches against your + "master", so if you need further changes, it is better to + fork the topic (perhaps with the same name) afresh from the + tip of "master". + +Let's look at this example: + + o---o---o---o---o---o---o---o---o---o "next" + / / / / + / a---a---b A / / + / / / / + / / c---c---c---c B / + / / / \ / + / / / b---b C \ / + / / / / \ / + ---o---o---o---o---o---o---o---o---o---o---o "master" + + +A, B and C are topic branches. + + * A has one fix since it was merged up to "next". + + * B has finished. It has been fully merged up to "master" and "next", + and is ready to be deleted. + + * C has not merged to "next" at all. + +We would want to allow C to be rebased, refuse A, and encourage +B to be deleted. + +To compute (1): + + git rev-list ^master ^topic next + git rev-list ^master next + + if these match, topic has not merged in next at all. + +To compute (2): + + git rev-list master..topic + + if this is empty, it is fully merged to "master". + +DOC_END diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_pre-receive.sample b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_pre-receive.sample new file mode 100644 index 0000000..a1fd29e --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_pre-receive.sample @@ -0,0 +1,24 @@ +#!/bin/sh +# +# An example hook script to make use of push options. +# The example simply echoes all push options that start with 'echoback=' +# and rejects all pushes when the "reject" push option is used. +# +# To enable this hook, rename this file to "pre-receive". + +if test -n "$GIT_PUSH_OPTION_COUNT" +then + i=0 + while test "$i" -lt "$GIT_PUSH_OPTION_COUNT" + do + eval "value=\$GIT_PUSH_OPTION_$i" + case "$value" in + echoback=*) + echo "echo from the pre-receive-hook: ${value#*=}" >&2 + ;; + reject) + exit 1 + esac + i=$((i + 1)) + done +fi diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_prepare-commit-msg.sample b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_prepare-commit-msg.sample new file mode 100644 index 0000000..10fa14c --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_prepare-commit-msg.sample @@ -0,0 +1,42 @@ +#!/bin/sh +# +# An example hook script to prepare the commit log message. +# Called by "git commit" with the name of the file that has the +# commit message, followed by the description of the commit +# message's source. The hook's purpose is to edit the commit +# message file. If the hook fails with a non-zero status, +# the commit is aborted. +# +# To enable this hook, rename this file to "prepare-commit-msg". + +# This hook includes three examples. The first one removes the +# "# Please enter the commit message..." help message. +# +# The second includes the output of "git diff --name-status -r" +# into the message, just before the "git status" output. It is +# commented because it doesn't cope with --amend or with squashed +# commits. +# +# The third example adds a Signed-off-by line to the message, that can +# still be edited. This is rarely a good idea. + +COMMIT_MSG_FILE=$1 +COMMIT_SOURCE=$2 +SHA1=$3 + +/usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE" + +# case "$COMMIT_SOURCE,$SHA1" in +# ,|template,) +# /usr/bin/perl -i.bak -pe ' +# print "\n" . `git diff --cached --name-status -r` +# if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;; +# *) ;; +# esac + +# SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE" +# if test -z "$COMMIT_SOURCE" +# then +# /usr/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE" +# fi diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_push-to-checkout.sample b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_push-to-checkout.sample new file mode 100644 index 0000000..af5a0c0 --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_push-to-checkout.sample @@ -0,0 +1,78 @@ +#!/bin/sh + +# An example hook script to update a checked-out tree on a git push. +# +# This hook is invoked by git-receive-pack(1) when it reacts to git +# push and updates reference(s) in its repository, and when the push +# tries to update the branch that is currently checked out and the +# receive.denyCurrentBranch configuration variable is set to +# updateInstead. +# +# By default, such a push is refused if the working tree and the index +# of the remote repository has any difference from the currently +# checked out commit; when both the working tree and the index match +# the current commit, they are updated to match the newly pushed tip +# of the branch. This hook is to be used to override the default +# behaviour; however the code below reimplements the default behaviour +# as a starting point for convenient modification. +# +# The hook receives the commit with which the tip of the current +# branch is going to be updated: +commit=$1 + +# It can exit with a non-zero status to refuse the push (when it does +# so, it must not modify the index or the working tree). +die () { + echo >&2 "$*" + exit 1 +} + +# Or it can make any necessary changes to the working tree and to the +# index to bring them to the desired state when the tip of the current +# branch is updated to the new commit, and exit with a zero status. +# +# For example, the hook can simply run git read-tree -u -m HEAD "$1" +# in order to emulate git fetch that is run in the reverse direction +# with git push, as the two-tree form of git read-tree -u -m is +# essentially the same as git switch or git checkout that switches +# branches while keeping the local changes in the working tree that do +# not interfere with the difference between the branches. + +# The below is a more-or-less exact translation to shell of the C code +# for the default behaviour for git's push-to-checkout hook defined in +# the push_to_deploy() function in builtin/receive-pack.c. +# +# Note that the hook will be executed from the repository directory, +# not from the working tree, so if you want to perform operations on +# the working tree, you will have to adapt your code accordingly, e.g. +# by adding "cd .." or using relative paths. + +if ! git update-index -q --ignore-submodules --refresh +then + die "Up-to-date check failed" +fi + +if ! git diff-files --quiet --ignore-submodules -- +then + die "Working directory has unstaged changes" +fi + +# This is a rough translation of: +# +# head_has_history() ? "HEAD" : EMPTY_TREE_SHA1_HEX +if git cat-file -e HEAD 2>/dev/null +then + head=HEAD +else + head=$(git hash-object -t tree --stdin &2 + exit 1 +} + +unset GIT_DIR GIT_WORK_TREE +cd "$worktree" && + +if grep -q "^diff --git " "$1" +then + validate_patch "$1" +else + validate_cover_letter "$1" +fi && + +if test "$GIT_SENDEMAIL_FILE_COUNTER" = "$GIT_SENDEMAIL_FILE_TOTAL" +then + git config --unset-all sendemail.validateWorktree && + trap 'git worktree remove -ff "$worktree"' EXIT && + validate_series +fi diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_update.sample b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_update.sample new file mode 100644 index 0000000..c4d426b --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_git/hooks/executable_update.sample @@ -0,0 +1,128 @@ +#!/bin/sh +# +# An example hook script to block unannotated tags from entering. +# Called by "git receive-pack" with arguments: refname sha1-old sha1-new +# +# To enable this hook, rename this file to "update". +# +# Config +# ------ +# hooks.allowunannotated +# This boolean sets whether unannotated tags will be allowed into the +# repository. By default they won't be. +# hooks.allowdeletetag +# This boolean sets whether deleting tags will be allowed in the +# repository. By default they won't be. +# hooks.allowmodifytag +# This boolean sets whether a tag may be modified after creation. By default +# it won't be. +# hooks.allowdeletebranch +# This boolean sets whether deleting branches will be allowed in the +# repository. By default they won't be. +# hooks.denycreatebranch +# This boolean sets whether remotely creating branches will be denied +# in the repository. By default this is allowed. +# + +# --- Command line +refname="$1" +oldrev="$2" +newrev="$3" + +# --- Safety check +if [ -z "$GIT_DIR" ]; then + echo "Don't run this script from the command line." >&2 + echo " (if you want, you could supply GIT_DIR then run" >&2 + echo " $0 )" >&2 + exit 1 +fi + +if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then + echo "usage: $0 " >&2 + exit 1 +fi + +# --- Config +allowunannotated=$(git config --type=bool hooks.allowunannotated) +allowdeletebranch=$(git config --type=bool hooks.allowdeletebranch) +denycreatebranch=$(git config --type=bool hooks.denycreatebranch) +allowdeletetag=$(git config --type=bool hooks.allowdeletetag) +allowmodifytag=$(git config --type=bool hooks.allowmodifytag) + +# check for no description +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +case "$projectdesc" in +"Unnamed repository"* | "") + echo "*** Project description file hasn't been set" >&2 + exit 1 + ;; +esac + +# --- Check types +# if $newrev is 0000...0000, it's a commit to delete a ref. +zero=$(git hash-object --stdin &2 + echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 + exit 1 + fi + ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/tags/*,tag) + # annotated tag + if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 + then + echo "*** Tag '$refname' already exists." >&2 + echo "*** Modifying a tag is not allowed in this repository." >&2 + exit 1 + fi + ;; + refs/heads/*,commit) + # branch + if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then + echo "*** Creating a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/remotes/*,commit) + # tracking branch + ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + *) + # Anything else (is there anything else?) + echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 + exit 1 + ;; +esac + +# --- Finished +exit 0 diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/index b/dot_config/private_mpv/scripts/videoclip/dot_git/index new file mode 100644 index 0000000000000000000000000000000000000000..f71c360a7b0661d9435aab6e4848bdf0a0aa376b GIT binary patch literal 1113 zcmZ?q402{*U|<4bo{YPPe(_c|rNC$gMrJ047m_m=7#f!_FfhM>V1~sFmP<_bF7COv zw&+a5kMNgFhx5M+F$n9WXO?7?Ch5C{`nh=ex$9Nt<^T-^0kC;>H&(!CsJXLH&1Gr` z`MSBYuseCpsdp>96{h!WP^$WRj6o4*u4iy?sB3(Pt8ajhV~DGMa(-S~W;)mwu(|aI zAm#&UsQI(e%zyM{(E_=J<##r<)P_IqV9<?&-Z)_A@k} zfjc!XIX@+}NH3=}5fnKO=K4^@Z}e$#21%T<{-R^-8|zwRP(rxd`(N=cCq2*(i!hM4(|8l z53s!Tf|r4(ASbaTEx#xi;ZYps6`-2O_(n$j=!Fo$=>mdf3pE3rnsS;q{y59PUy_-d znU`K1pPLSg3moPZqMB##cuAMR)86QhiZ_WCPBR0K(%xo?|dlFyNXiW#+%|d`;QAT%-KAfm7E^zwrO^uCzrv+S?dJxwo|Re!H>i JB2(LgJ^-ggqBsBm literal 0 HcmV?d00001 diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/info/exclude b/dot_config/private_mpv/scripts/videoclip/dot_git/info/exclude new file mode 100644 index 0000000..a5196d1 --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_git/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/logs/HEAD b/dot_config/private_mpv/scripts/videoclip/dot_git/logs/HEAD new file mode 100644 index 0000000..8dfb699 --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_git/logs/HEAD @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 44d4087c5fd9cc15e73309778b3af5f73064b358 Rain 1759363834 -0400 clone: from https://github.com/Ajatt-Tools/videoclip.git diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/logs/refs/heads/master b/dot_config/private_mpv/scripts/videoclip/dot_git/logs/refs/heads/master new file mode 100644 index 0000000..8dfb699 --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_git/logs/refs/heads/master @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 44d4087c5fd9cc15e73309778b3af5f73064b358 Rain 1759363834 -0400 clone: from https://github.com/Ajatt-Tools/videoclip.git diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/logs/refs/remotes/origin/HEAD b/dot_config/private_mpv/scripts/videoclip/dot_git/logs/refs/remotes/origin/HEAD new file mode 100644 index 0000000..8dfb699 --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_git/logs/refs/remotes/origin/HEAD @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 44d4087c5fd9cc15e73309778b3af5f73064b358 Rain 1759363834 -0400 clone: from https://github.com/Ajatt-Tools/videoclip.git diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/objects/info/.keep b/dot_config/private_mpv/scripts/videoclip/dot_git/objects/info/.keep new file mode 100644 index 0000000..e69de29 diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/objects/pack/readonly_pack-98877bd46235e2bcf152cdad6227778126a1e6c9.idx b/dot_config/private_mpv/scripts/videoclip/dot_git/objects/pack/readonly_pack-98877bd46235e2bcf152cdad6227778126a1e6c9.idx new file mode 100644 index 0000000000000000000000000000000000000000..29f9bc39d4899d42751d72fa64881455e85bfdde GIT binary patch literal 16472 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 $6I3Bw+tlWBUg-Vi~~YX7Q*EcCPKQ!hLrM| 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 literal 0 HcmV?d00001 diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/objects/pack/readonly_pack-98877bd46235e2bcf152cdad6227778126a1e6c9.rev b/dot_config/private_mpv/scripts/videoclip/dot_git/objects/pack/readonly_pack-98877bd46235e2bcf152cdad6227778126a1e6c9.rev new file mode 100644 index 0000000000000000000000000000000000000000..4549f3222862c46d25038d3fca034063a70afda7 GIT binary patch literal 2252 zcmXBS2N;f37>Duqd`QSj3fak45)I1ULLu2nMB2*83X#ezl`SKYm6?$!B$A4dqRfiQ zmQ~X4Tz%JdU-$oc&iUT=yyu)I^=h_Ak|g|3Xe<^@l2isBrB{YC@ppI~HE|Zbkur7_ zh9Mtb!(9E=Xlb}G|AWc;90uxT>A#QHl8Dg z^zwY{kbmUuTpEYuUpYSa08Yp+b6ZrGeMfjwzRq)eLvk8R^s@4C{D245M~oNbQtHg8 zfU~j`8!nG(Y=j0){QI7BFAuUbE4{h^%PiP!M=oEFXDKdCf&hvac`K0b!eF+~47yd(7GUB<8G8W@U=`tjKn z@tOKQG{O7o@mvpaFAH%c+#~1UkBz&#VY)nwzvf(+EyuBVH)e8vj_W9oI`TR`&G8O6 zmy~Zv{?(hnU!sd#6V?#oT_|nN^}JlYi5qZ3{)nAPun$Rg-pbGO3QUr_@hM)95}sjg z;RE>~--vs05rfeNnK1^N&G8FyEpeajloz45ypZEQ#OJ2RQ=aP{hd3whsDk?`G_lTk*rNo|HXGUP4_AM{)GP0Q_XGdk{WF zBUFa_lB~ufi0|5fyYYnbAp3^U9nLbOQBUNSycP@K{)97#>tDrm{7s zoL|yNy%F`1QN0ZN@PPU;9G3q^Jj-})H}F+Fi&vnw9QUUn3LuY~k~#4IN~UW6lYam<|4>1zly!aYH++O@vPtDoal$V$Oq32Z?pf0xc-#? z$Mu$%<2o|%S=I}kFipPQoJ>4H{U_pik6`Z%lW`m3J3KdZ;ioy?fi>taFGM@+Qs2t% zcrpnk<(`Q5tQo(96S6%HzvEf?Th0di8;YyzqmVokUGcDb6R$;8c`C1D-yXK{9k4&C zxaaX%mtFD zU6)(Io+a^J-|>&=g@$k^LoPVmumj!n`e3XzrdP+a-hkaWu5W*mit+;Nleci3UyDm{ zQMUJ~c;=nu20V~UVxGK;on>-{ry%Z4HjKeBq``Fkj%YS+XrCP&DjZyWs>#;*9g6iF gSajO2o4d8lw0uXQsYfbpN`~hx)Fs{Y!TnnO2eH@}>i_@% literal 0 HcmV?d00001 diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/packed-refs b/dot_config/private_mpv/scripts/videoclip/dot_git/packed-refs new file mode 100644 index 0000000..aa9f642 --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_git/packed-refs @@ -0,0 +1,6 @@ +# pack-refs with: peeled fully-peeled sorted +44d4087c5fd9cc15e73309778b3af5f73064b358 refs/remotes/origin/master +08058ab4b73639120f68b8022d46b9b8ae2f2d46 refs/tags/v0.1 +^249122d245bc5ec2a0687346af730b1cc2273b21 +b8a9c7bf38c4f1b3a069b4d2fedc99fde5464872 refs/tags/v0.2 +^785eb86bc080c445e8feb947d7caa8f3a097bf2b diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/refs/heads/master b/dot_config/private_mpv/scripts/videoclip/dot_git/refs/heads/master new file mode 100644 index 0000000..5c9c190 --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_git/refs/heads/master @@ -0,0 +1 @@ +44d4087c5fd9cc15e73309778b3af5f73064b358 diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/refs/remotes/origin/HEAD b/dot_config/private_mpv/scripts/videoclip/dot_git/refs/remotes/origin/HEAD new file mode 100644 index 0000000..6efe28f --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_git/refs/remotes/origin/HEAD @@ -0,0 +1 @@ +ref: refs/remotes/origin/master diff --git a/dot_config/private_mpv/scripts/videoclip/dot_git/refs/tags/.keep b/dot_config/private_mpv/scripts/videoclip/dot_git/refs/tags/.keep new file mode 100644 index 0000000..e69de29 diff --git a/dot_config/private_mpv/scripts/videoclip/dot_github/FUNDING.yml b/dot_config/private_mpv/scripts/videoclip/dot_github/FUNDING.yml new file mode 100644 index 0000000..8039a43 --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_github/FUNDING.yml @@ -0,0 +1,5 @@ +# These are supported funding model platforms + +patreon: tatsumoto_ren +liberapay: Tatsumoto +custom: https://tatsumoto.neocities.org/blog/donating-to-tatsumoto.html diff --git a/dot_config/private_mpv/scripts/videoclip/dot_github/ISSUE_TEMPLATE/config.yml b/dot_config/private_mpv/scripts/videoclip/dot_github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..54f5b38 --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,13 @@ +blank_issues_enabled: false +contact_links: + - name: Bug Reports + url: https://tatsumoto.neocities.org/blog/join-our-community + about: This issue tracker is for developers. Please report any bugs you encounter over in the chats, and they will be triaged there. + + - name: Questions/Support + url: https://tatsumoto.neocities.org/blog/join-our-community + about: If you have a question or need support, please post in the community chats. + + - name: Feature Requests/Suggestions + url: https://tatsumoto.neocities.org/blog/join-our-community + about: Please post suggestions and feature requests in our chat. diff --git a/dot_config/private_mpv/scripts/videoclip/dot_github/ISSUE_TEMPLATE/issue.md b/dot_config/private_mpv/scripts/videoclip/dot_github/ISSUE_TEMPLATE/issue.md new file mode 100644 index 0000000..f4a2a01 --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/dot_github/ISSUE_TEMPLATE/issue.md @@ -0,0 +1,37 @@ +--- +name: Issue +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +- The add-on crashes? +- The add-on stopped working? +- Have a feature request? + +Please ask on https://tatsumoto.neocities.org/blog/join-our-community.html instead. +It is easier for our members to provide support over in the chats. Thank you! + +Don't forget to provide the following information: + +**Environment** + +1) OS name [e.g. Parabola, Debian] +2) OS version +3) mpv version +4) Logs +5) Screenshots, if applicable + +**Describe the bug** +A description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1) Press x '...' +2) Click on '....' +3) See error + +**Expected behavior** +A description of what you expected to happen. diff --git a/dot_config/private_mpv/scripts/videoclip/encoder.lua b/dot_config/private_mpv/scripts/videoclip/encoder.lua new file mode 100644 index 0000000..ea206b0 --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/encoder.lua @@ -0,0 +1,246 @@ +--[[ +Copyright: Ren Tatsumoto and contributors +License: GNU GPL, version 3 or later; http://www.gnu.org/licenses/gpl.html + +Encoder provides interface for creating audio/video clips. +]] + +local mp = require('mp') +local h = require('helpers') +local utils = require('mp.utils') +local this = {} + +local function toms(timestamp) + --- Trim timestamp down to milliseconds. + return string.format("%.3f", timestamp) +end + +local function clean_filename(filename) + filename = h.remove_extension(filename) + if this.config.clean_filename then + filename = h.remove_text_in_brackets(filename) + filename = h.remove_special_characters(filename) + -- remove_text_in_brackets might leave spaces at the start or the end, so trim those + filename = h.strip(filename) + end + return filename +end + +local function clean_forbidden_characters(title) + return title:gsub('[<>:"/\\|%?%*]+', '.') +end + +local function construct_output_filename_noext() + + local filename = mp.get_property("filename") -- filename without path + local title = mp.get_property("media-title") -- if the video doesn't have a title, it will fallback to filename + local date = os.date("*t") -- get current date and time as table + + -- Apply the same operation when the video doesn't have a title + -- thus it will be the same as filename + if title == filename then + filename = clean_filename(filename) + title = filename + else + filename = clean_filename(filename) + title = clean_forbidden_characters(title) + end + + -- Available tags: %n = filename, %t = title, %s = start, %e = end, %d = duration, + -- %Y = year, %M = months, %D = day, %H = hours (24), %I = hours (12), + -- %P = am/pm %N = minutes, %S = seconds + filename = this.config.filename_template + :gsub("%%n", filename) + :gsub("%%t", title) + :gsub("%%s", h.human_readable_time(this.timings['start'])) + :gsub("%%e", h.human_readable_time(this.timings['end'])) + :gsub("%%d", h.human_readable_time(this.timings['end'] - this.timings['start'])) + :gsub("%%Y", date.year) + :gsub("%%M", h.two_digit(date.month)) + :gsub("%%D", h.two_digit(date.day)) + :gsub("%%H", h.two_digit(date.hour)) + :gsub("%%I", h.two_digit(h.twelve_hour(date.hour)['hour'])) + :gsub("%%P", h.twelve_hour(date.hour)['sign']) + :gsub("%%N", h.two_digit(date.min)) + :gsub("%%S", h.two_digit(date.sec)) + + return filename +end + +function this.get_ext_subs_paths() + local track_list = mp.get_property_native('track-list') + local external_subs_list = {} + for _, track in pairs(track_list) do + if track.type == 'sub' and track.external == true then + external_subs_list[track.id] = track['external-filename'] + end + end + return external_subs_list +end + +function this.append_embed_subs_args(args) + local ext_subs_paths = this.get_ext_subs_paths() + for _, ext_subs_path in pairs(ext_subs_paths) do + table.insert(args, #args, table.concat { '--sub-files-append=', ext_subs_path, }) + end + return args +end + +this.mk_out_path_video = function(clip_filename_noext) + return utils.join_path(h.expand_path(this.config.video_folder_path), clip_filename_noext .. this.config.video_extension) +end + +this.mkargs_video = function(out_clip_path) + local args = { + this.player, + mp.get_property('path'), + '--loop-file=no', + '--keep-open=no', + '--no-ocopy-metadata', + '--no-sub', + '--audio-channels=2', + '--oacopts-add=vbr=on', + '--oacopts-add=application=voip', + '--oacopts-add=compression_level=10', + '--vf-add=format=yuv420p', + '--sub-font-provider=auto', + '--embeddedfonts=yes', + table.concat { '--sub-font=', this.config.sub_font }, + table.concat { '--ovc=', this.config.video_codec }, + table.concat { '--oac=', this.config.audio_codec }, + table.concat { '--start=', toms(this.timings['start']) }, + table.concat { '--end=', toms(this.timings['end']) }, + table.concat { '--aid=', mp.get_property("aid") }, -- track number + table.concat { '--mute=', mp.get_property("mute") }, + table.concat { '--volume=', mp.get_property('volume') }, + table.concat { '--ovcopts-add=b=', this.config.video_bitrate }, + table.concat { '--oacopts-add=b=', this.config.audio_bitrate }, + table.concat { '--ovcopts-add=crf=', this.config.video_quality }, + table.concat { '--ovcopts-add=preset=', this.config.preset }, + table.concat { '--vf-add=scale=', this.config.video_width, ':', this.config.video_height }, + table.concat { '--ytdl-format=', mp.get_property("ytdl-format") }, + table.concat { '--o=', out_clip_path }, + table.concat { '--sid=', mp.get_property("sid") }, + table.concat { '--secondary-sid=', mp.get_property("secondary-sid") }, + table.concat { '--sub-delay=', mp.get_property("sub-delay") }, + table.concat { '--sub-visibility=', mp.get_property("sub-visibility") }, + table.concat { '--secondary-sub-visibility=', mp.get_property("secondary-sub-visibility") }, + table.concat { '--sub-back-color=', mp.get_property("sub-back-color") }, + table.concat { '--sub-border-style=', mp.get_property("sub-border-style") }, + } + + if this.config.video_fps ~= 'auto' then + table.insert(args, #args, table.concat { '--vf-add=fps=', this.config.video_fps }) + end + + args = this.append_embed_subs_args(args) + + return args +end + +this.mk_out_path_audio = function(clip_filename_noext) + return utils.join_path(h.expand_path(this.config.audio_folder_path), clip_filename_noext .. this.config.audio_extension) +end + +this.mkargs_audio = function(out_clip_path) + return { + this.player, + mp.get_property('path'), + '--loop-file=no', + '--keep-open=no', + '--no-ocopy-metadata', + '--no-sub', + '--audio-channels=2', + '--video=no', + '--oacopts-add=vbr=on', + '--oacopts-add=application=voip', + '--oacopts-add=compression_level=10', + table.concat { '--oac=', this.config.audio_codec }, + table.concat { '--start=', toms(this.timings['start']) }, + table.concat { '--end=', toms(this.timings['end']) }, + table.concat { '--volume=', mp.get_property('volume') }, + table.concat { '--aid=', mp.get_property("aid") }, -- track number + table.concat { '--oacopts-add=b=', this.config.audio_bitrate }, + table.concat { '--ytdl-format=', mp.get_property("ytdl-format") }, + table.concat { '--o=', out_clip_path } + } +end + +this.create_clip = function(clip_type, on_complete) + if clip_type == nil then + return + end + + if not this.timings:validate() then + h.notify_error("Wrong timings. Aborting.", "warn", 2) + return + end + + h.notify("Please wait...", "info", 9999) + + local output_file_path, args = (function() + local clip_filename_noext = construct_output_filename_noext() + if clip_type == 'video' then + local output_path = this.mk_out_path_video(clip_filename_noext) + return output_path, this.mkargs_video(output_path) + else + local output_path = this.mk_out_path_audio(clip_filename_noext) + return output_path, this.mkargs_audio(output_path) + end + end)() + + print("The following args will be executed:", table.concat(h.quote_if_necessary(args), " ")) + + local output_dir_path = utils.split_path(output_file_path) + local location_info = utils.file_info(output_dir_path) + if not location_info or not location_info.is_dir then + h.notify_error(string.format("Error: location %s doesn't exist.", output_dir_path), "error", 5) + return + end + + local process_result = function(_, ret, _) + if ret.status ~= 0 or string.match(ret.stdout, "could not open") then + h.notify_error(string.format("Error: couldn't create clip %s.", output_file_path), "error", 5) + else + h.notify(string.format("Clip saved to %s.", output_file_path), "info", 2) + if on_complete then + on_complete(output_file_path) + end + end + end + + h.subprocess_async(args, process_result) + this.timings:reset() +end + +this.set_encoder_alive = function() + local args_mpvnet = { 'mpvnet', '--version' } + local process_result_mpvnet = function(_, ret, _) + -- for some reason stdout is empty + if ret.status ~= 0 then + this.alive = false + else + this.alive = true + this.player = 'mpvnet' + end + end + + local args = { 'mpv', '--version' } + local process_result = function(_, ret, _) + if ret.status ~= 0 or string.match(ret.stdout, "mpv") == nil then + h.subprocess_async(args_mpvnet, process_result_mpvnet) + else + this.alive = true + this.player = 'mpv' + end + end + h.subprocess_async(args, process_result) +end + +this.init = function(config, timings_mgr) + this.config = config + this.timings = timings_mgr + this.set_encoder_alive() +end + +return this diff --git a/dot_config/private_mpv/scripts/videoclip/helpers.lua b/dot_config/private_mpv/scripts/videoclip/helpers.lua new file mode 100644 index 0000000..96e02c8 --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/helpers.lua @@ -0,0 +1,141 @@ +--[[ +Copyright: Ren Tatsumoto and contributors +License: GNU GPL, version 3 or later; http://www.gnu.org/licenses/gpl.html + +Various helper functions. +]] + +local mp = require('mp') +local this = {} +local ass_start = mp.get_property_osd("osd-ass-cc/0") + +this.is_wayland = function() + return os.getenv('WAYLAND_DISPLAY') ~= nil +end + +this.is_win = function() + return mp.get_property("platform") == "windows" +end + +this.is_mac = function() + return mp.get_property("platform") == "darwin" +end + +this.notify = function(message, level, duration) + level = level or 'info' + duration = duration or 1 + mp.msg[level](message) + mp.osd_message(ass_start .. "{\\fs12}{\\bord0.75}" .. message, duration) +end + +this.notify_error = function(message, level, duration) + this.notify("{\\c&H7171f8&}" .. message, level, duration) +end + +this.subprocess = function(args, stdin) + local command_table = { + name = "subprocess", + playback_only = false, + capture_stdout = true, + capture_stderr = true, + args = args, + stdin_data = (stdin or ""), + } + return mp.command_native(command_table) +end + +this.subprocess_async = function(args, on_complete) + local command_table = { + name = "subprocess", + playback_only = false, + capture_stdout = true, + capture_stderr = true, + args = args + } + return mp.command_native_async(command_table, on_complete) +end + +this.remove_extension = function(filename) + return filename:gsub('%.%w+$', '') +end + +this.remove_text_in_brackets = function(str) + return str:gsub('%b[]', '') +end + +this.remove_special_characters = function(str) + return str:gsub('[%-_]', ' '):gsub('[%c%p]', ''):gsub('%s+', ' ') +end + +this.strip = function(str) + return str:gsub("^%s*(.-)%s*$", "%1") +end + +this.two_digit = function(num) + return string.format("%02d", num) +end + +this.twelve_hour = function(num) + local sign = "pm" + local hour = num + + if num > 12 then + hour = hour - 12 + else + sign = "am" + end + + return { sign = sign, hour = hour } +end + +this.expand_path = function (str) + return mp.command_native({"expand-path", str}) +end + +this.human_readable_time = function(seconds) + if type(seconds) ~= 'number' or seconds < 0 then + return 'empty' + end + + local parts = {} + + parts.h = math.floor(seconds / 3600) + parts.m = math.floor(seconds / 60) % 60 + parts.s = math.floor(seconds % 60) + parts.ms = math.floor((seconds * 1000) % 1000) + + local ret = string.format("%02dm%02ds%03dms", parts.m, parts.s, parts.ms) + + if parts.h > 0 then + ret = string.format('%dh%s', parts.h, ret) + end + + return ret +end + +this.quote_if_necessary = function(args) + local ret = {} + for _, v in ipairs(args) do + if v:find(" ", 1, true) or v:find("[", 1, true) then + table.insert(ret, (v:find("'") and string.format('"%s"', v) or string.format("'%s'", v))) + else + table.insert(ret, v) + end + end + return ret +end + +this.query_xdg_user_dir = function(name) + local r = this.subprocess({ "xdg-user-dir", name }) + if r.status == 0 then + return this.strip(r.stdout) + end + return nil +end + +this.query_user_home_dir = function() + --- "USERPROFILE" is used on ReactOS and other Windows-like systems. + return os.getenv("HOME") or os.getenv("USERPROFILE") +end + +return this diff --git a/dot_config/private_mpv/scripts/videoclip/main.lua b/dot_config/private_mpv/scripts/videoclip/main.lua new file mode 100644 index 0000000..4603636 --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/main.lua @@ -0,0 +1 @@ +require('videoclip') diff --git a/dot_config/private_mpv/scripts/videoclip/osd_styler.lua b/dot_config/private_mpv/scripts/videoclip/osd_styler.lua new file mode 100644 index 0000000..7da1d24 --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/osd_styler.lua @@ -0,0 +1,108 @@ +--[[ +A helper class for styling OSD messages +http://docs.aegisub.org/3.2/ASS_Tags/ + +Copyright (C) 2021 Ren Tatsumoto + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +]] + +local OSD = {} +OSD.__index = OSD + +function OSD:new() + return setmetatable({ messages = {} }, self) +end + +function OSD:append(s) + table.insert(self.messages, tostring(s)) + return self +end + +function OSD:newline() + return self:append([[\N]]) +end + +function OSD:tab() + return self:append([[\h\h\h\h]]) +end + +function OSD:config(config) + return self + :size(config.font_size) + :align(config.osd_align) + :outline(config.osd_outline) +end + +function OSD:outline(size) + return self:append('{\\bord'):append(size):append('}') +end + +function OSD:size(size) + return self:append('{\\fs'):append(size):append('}') +end + +function OSD:font(name) + return self:append('{\\fn'):append(name):append('}') +end + +function OSD:align(number) + return self:append('{\\an'):append(number):append('}') +end + +function OSD:get_text() + return table.concat(self.messages) +end + +function OSD:color(code) + return self:append('{\\1c&H') + :append(code:sub(5, 6)) + :append(code:sub(3, 4)) + :append(code:sub(1, 2)) + :append('&}') +end + +function OSD:text(text) + return self:append(text) +end + +function OSD:new_layer() + return self:append('\n') +end + +function OSD:bold(s) + return self:append('{\\b1}'):append(s):append('{\\b0}') +end + +function OSD:italics(s) + return self:append('{\\i1}'):append(s):append('{\\i0}') +end + +function OSD:submenu(text) + return self:color('e56243'):bold(text):color('ffffff') +end + +function OSD:item(text) + return self:color('f5d38a'):bold(text):color('ffffff') +end + +function OSD:selected(text) + return self:color('48a868'):bold(text):color('ffffff') +end + +function OSD:red(text) + return self:color('ff0000'):bold(text):color('ffffff') +end + +return OSD diff --git a/dot_config/private_mpv/scripts/videoclip/platform.lua b/dot_config/private_mpv/scripts/videoclip/platform.lua new file mode 100644 index 0000000..f56667b --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/platform.lua @@ -0,0 +1,95 @@ +--[[ +Copyright: Ren Tatsumoto and contributors +License: GNU GPL, version 3 or later; http://www.gnu.org/licenses/gpl.html + +OS-related constants and functions. +]] + +local h = require('helpers') +local mp = require('mp') +local utils = require('mp.utils') +local this = {} + +local function get_fallback_video_dir() + return utils.join_path( + h.query_user_home_dir(), + (this.platform == this.Platform.macos and "Movies" or "Videos") + ) +end + +local function get_fallback_music_dir() + return utils.join_path(h.query_user_home_dir(), "Music") +end + +this.Platform = { + gnu_linux = "gnu_linux", + macos = "macos", + windows = "windows", +} +this.platform = ( + h.is_win() and this.Platform.windows + or h.is_mac() and this.Platform.macos + or this.Platform.gnu_linux +) +this.default_video_folder = h.query_xdg_user_dir("VIDEOS") or get_fallback_video_dir() +this.default_audio_folder = h.query_xdg_user_dir("MUSIC") or get_fallback_music_dir() +this.curl_exe = (this.platform == this.Platform.windows and 'curl.exe' or 'curl') +this.open_utility = ( + this.platform == this.Platform.windows and 'explorer.exe' + or this.platform == this.Platform.macos and 'open' + or this.platform == this.Platform.gnu_linux and 'xdg-open' +) +this.open = function(file_or_url) + return mp.commandv('run', this.open_utility, file_or_url) +end + +this.clipboard = (function() + local self = {} + if this.platform == this.Platform.windows then + self.clip_exe = "powershell.exe" + self.copy = function(text) + return h.subprocess({ self.clip_exe, '-command', 'Set-Clipboard -Value ' .. text }) + end + else + if this.platform == this.Platform.macos then + self.clip_exe = "pbcopy" + self.clip_cmd = "LANG=en_US.UTF-8 pbcopy" + elseif h.is_wayland() then + self.clip_exe = "wl-copy" + self.clip_cmd = "wl-copy" + else + self.clip_exe = "xclip" + self.clip_cmd = "xclip -i -selection clipboard" + end + self.copy = function(text) + local handle = io.popen(self.clip_cmd, 'w') + if handle then + handle:write(text) + local success, status, signal = handle:close() + if success then + status = 0 + end + return { status = status } + else + return { status = 1 } + end + end + end + return self +end)() + +this.copy_or_open_url = function(url) + local cb = this.clipboard.copy(url) + if cb.status ~= 0 then + local msg = string.format( + "Failed to copy URL to clipboard, trying to open in browser instead. Make sure %s is installed.", + this.clipboard.clip_exe + ) + h.notify_error(msg, "warn", 4) + this.open(url) + else + h.notify("Done! Copied URL to clipboard.", "info", 2) + end + return cb +end +return this diff --git a/dot_config/private_mpv/scripts/videoclip/timings_mgr.lua b/dot_config/private_mpv/scripts/videoclip/timings_mgr.lua new file mode 100644 index 0000000..1c17c5d --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/timings_mgr.lua @@ -0,0 +1,29 @@ +--[[ +Copyright: Ren Tatsumoto and contributors +License: GNU GPL, version 3 or later; http://www.gnu.org/licenses/gpl.html + +Timings class +]] + +local Timings = { + ['start'] = -1, + ['end'] = -1, +} + +function Timings:new(o) + o = o or {} + setmetatable(o, self) + self.__index = self + return o +end + +function Timings:reset() + self['start'] = -1 + self['end'] = -1 +end + +function Timings:validate() + return self['start'] >= 0 and self['start'] < self['end'] +end + +return Timings diff --git a/dot_config/private_mpv/scripts/videoclip/videoclip.lua b/dot_config/private_mpv/scripts/videoclip/videoclip.lua new file mode 100644 index 0000000..d22d004 --- /dev/null +++ b/dot_config/private_mpv/scripts/videoclip/videoclip.lua @@ -0,0 +1,476 @@ +--[[ +Videoclip - mp4/webm clips creator for mpv. + +Copyright (C) 2021 Ren Tatsumoto + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +]] + +local NAME = 'videoclip' +local mp = require('mp') +local mpopt = require('mp.options') +local utils = require('mp.utils') +local OSD = require('osd_styler') +local p = require('platform') +local h = require('helpers') +local encoder = require('encoder') +local Timings = require('timings_mgr') + +------------------------------------------------------------ +-- System-dependent variables + +-- Options can be changed here or in a separate config file. +-- Config path: ~/.config/mpv/script-opts/videoclip.conf +local config = { + -- absolute paths + -- relative paths (e.g. ~ for home dir) do NOT work. + video_folder_path = p.default_video_folder, + audio_folder_path = p.default_audio_folder, + -- The range of the CRF scale is 0–51, where 0 is lossless, + -- 23 is the default, and 51 is worst quality possible. + -- Insane values like 9999 still work but produce the worst quality. + video_quality = 23, + -- Use the slowest preset that you have patience for. + -- https://trac.ffmpeg.org/wiki/Encode/H.264 + preset = 'faster', + video_format = 'mp4', -- mp4, vp9, vp8 + video_bitrate = '1M', + video_width = -2, + video_height = 480, + video_fps = 'auto', + audio_format = 'opus', -- aac, opus + audio_bitrate = '32k', -- 32k, 64k, 128k, 256k. aac requires higher bitrates. + font_size = 24, + osd_align = 7, -- https://aegisub.org/docs/3.2/ASS_Tags/#\an + osd_outline = 1.5, + clean_filename = true, + -- Whether to upload to catbox (permanent) or litterbox (temporary) + litterbox = true, + -- Determines expire time of files uploaded to litterbox + litterbox_expire = '72h', -- 1h, 12h, 24h, 72h + sub_font = 'Noto Sans CJK JP', + -- Filename format + -- Available tags: %n = filename, %t = title, %s = start, %e = end, %d = duration, + -- %Y = year, %M = months, %D = day, %H = hours (24), %I = hours (12), + -- %P = am/pm %N = minutes, %S = seconds + filename_template='%n_%s-%e', +} + +mpopt.read_options(config, NAME) +local main_menu +local pref_menu + +local allowed_presets = { + ultrafast = true, + superfast = true, + veryfast = true, + faster = true, + fast = true, + medium = true, + slow = true, + slower = true, + veryslow = true, +} + +------------------------------------------------------------ +-- Utility functions + +local function force_resolution(width, height, clip_fn, ...) + local cached_prefs = { + video_width = config.video_width, + video_height = config.video_height, + } + config.video_width = width + config.video_height = height + clip_fn(...) + config.video_width = cached_prefs.video_width + config.video_height = cached_prefs.video_height +end + +local function set_encoding_settings() + if config.video_format == 'mp4' then + config.video_codec = 'libx264' + config.video_extension = '.mp4' + elseif config.video_format == 'vp9' then + config.video_codec = 'libvpx-vp9' + config.video_extension = '.webm' + else + config.video_codec = 'libvpx' + config.video_extension = '.webm' + end + + if config.audio_format == 'aac' then + config.audio_codec = 'aac' + config.audio_extension = '.aac' + else + config.audio_codec = 'libopus' + config.audio_extension = '.opus' + end +end + +local function validate_config() + if not config.audio_bitrate:match('^%d+[kK]$') then + config.audio_bitrate = (tonumber(config.audio_bitrate) or 32) .. 'k' + end + + if not config.video_bitrate:match('^%d+[kKmM]$') then + config.video_bitrate = '1M' + end + + if not allowed_presets[config.preset] then + config.preset = 'faster' + end + + set_encoding_settings() +end + +local function upload_to_catbox(outfile) + local endpoint = config.litterbox and 'https://litterbox.catbox.moe/resources/internals/api.php' or 'https://catbox.moe/user/api.php' + h.notify("Uploading to " .. (config.litterbox and "litterbox.catbox.moe..." or "catbox.moe..."), "info", 9999) + + -- This uses cURL to send a request to the cat-/litterbox API. + -- cURL is included on Windows 10 and up, most Linux distributions and macOS. + + local r = h.subprocess({ -- This is technically blocking, but I don't think it has any real consequences ..? + p.curl_exe, '-s', + '-F', 'reqtype=fileupload', + '-F', 'time=' .. config['litterbox_expire'], + '-F', 'fileToUpload=@"' .. outfile .. '"', + endpoint + }) + + -- Exit codes in the range [0, 99] are returned by cURL itself. + -- Any other exit code means the shell failed to execute cURL. + if r.status < 0 or r.status > 99 then + h.notify_error("Error: Failed to upload. Make sure cURL is installed and in your PATH.", "error", 3) + return + elseif r.status ~= 0 then + h.notify_error("Error: Failed to upload to " .. (config.litterbox and "litterbox.catbox.moe" or "catbox.moe"), "error", 2) + return + end + + mp.msg.info("Catbox URL: " .. r.stdout) + -- Copy to clipboard + p.copy_or_open_url(r.stdout) +end + +------------------------------------------------------------ +-- Menu interface + +local Menu = {} +Menu.__index = Menu + +function Menu:new(parent) + local o = { + parent = parent, + overlay = parent and parent.overlay or mp.create_osd_overlay('ass-events'), + keybindings = { }, + } + return setmetatable(o, self) +end + +function Menu:overlay_draw(text) + self.overlay.data = text + self.overlay:update() +end + +function Menu:open() + if self.parent then + self.parent:close() + end + for _, val in pairs(self.keybindings) do + mp.add_forced_key_binding(val.key, val.key, val.fn) + end + self:update() +end + +function Menu:close() + for _, val in pairs(self.keybindings) do + mp.remove_key_binding(val.key) + end + if self.parent then + self.parent:open() + else + self.overlay:remove() + end +end + +function Menu:update() + local osd = OSD:new():config(config) + osd:append('Dummy menu.'):newline() + self:overlay_draw(osd:get_text()) +end + +------------------------------------------------------------ +-- Main menu + +main_menu = Menu:new() +main_menu.timings = Timings:new() + +main_menu.keybindings = { + { key = 's', fn = function() main_menu:set_time('start') end }, + { key = 'e', fn = function() main_menu:set_time('end') end }, + { key = 'S', fn = function() main_menu:set_time_sub('start') end }, + { key = 'E', fn = function() main_menu:set_time_sub('end') end }, + { key = 'r', fn = function() main_menu:reset_timings() end }, + { key = 'c', fn = function() main_menu:create_clip('video') end }, + { key = 'C', fn = function() force_resolution(1920, -2, encoder.create_clip, 'video') end }, + { key = 'a', fn = function() main_menu:create_clip('audio') end }, + { key = 'x', fn = function() main_menu:create_clip('video', upload_to_catbox) end }, + { key = 'X', fn = function() force_resolution(1920, -2, main_menu.create_clip, 'video', upload_to_catbox) end }, + { key = 'p', fn = function() pref_menu:open() end }, + { key = 'o', fn = function() p.open('https://streamable.com/') end }, + { key = 'ESC', fn = function() main_menu:close() end }, +} + +function main_menu:set_time(property) + self.timings[property] = math.max(0, mp.get_property_number('time-pos')) + self:update() +end + +function main_menu:set_time_sub(property) + local sub_delay = mp.get_property_native("sub-delay") + local time_pos = mp.get_property_number(string.format("sub-%s", property)) + + if time_pos == nil then + h.notify_error("Warning: No subtitles visible.", "warn", 2) + return + end + + self.timings[property] = math.max(0, time_pos + sub_delay) + self:update() +end + +function main_menu:reset_timings() + self.timings:reset() + self:update() +end + +main_menu.open = function() + Menu.open(main_menu) +end + +function main_menu:update() + local osd = OSD:new():config(config) + if encoder.alive == false then + osd:red("Error: "):append("mpv is not found in the PATH."):newline() + end + osd:submenu('Timings '):italics('(+shift use sub timings)'):newline() + osd:tab():item('s: '):append('start time '):item(h.human_readable_time(self.timings['start'])):newline() + osd:tab():item('e: '):append('end time '):item(h.human_readable_time(self.timings['end'])):newline() + osd:tab():item('r: '):append('reset'):newline() + osd:submenu('Create clip '):italics('(+shift to force fullHD preset)'):newline() + osd:tab():item('c: '):append('video clip'):newline() + osd:tab():item('a: '):append('audio clip'):newline() + osd:tab():item('x: '):append('video clip to ' .. (config.litterbox and 'litterbox.catbox.moe (' .. config.litterbox_expire .. ')' or 'catbox.moe')):newline() + osd:submenu('Options '):newline() + osd:tab():item('p: '):append('Open preferences'):newline() + osd:tab():item('o: '):append('Open streamable.com'):newline() + osd:tab():item('ESC: '):append('Close'):newline() + + self:overlay_draw(osd:get_text()) +end + +function main_menu:create_clip(clip_type, on_complete_fn) + self:close() + encoder.create_clip(clip_type, on_complete_fn) +end + +------------------------------------------------------------ +-- Preferences + +pref_menu = Menu:new(main_menu) + +pref_menu.keybindings = { + { key = 'f', fn = function() pref_menu:cycle_video_formats() end }, + { key = 'a', fn = function() pref_menu:cycle_audio_formats() end }, + { key = 'm', fn = function() pref_menu:toggle_mute_audio() end }, + { key = 'r', fn = function() pref_menu:cycle_resolutions() end }, + { key = 'b', fn = function() pref_menu:cycle_audio_bitrates() end }, + { key = 'e', fn = function() pref_menu:toggle_embed_subtitles() end }, + { key = 'x', fn = function() pref_menu:toggle_catbox() end }, + { key = 'z', fn = function() pref_menu:cycle_litterbox_expiration() end }, + { key = 's', fn = function() pref_menu:save() end }, + { key = 'c', fn = function() end }, + { key = 'ESC', fn = function() pref_menu:close() end }, + { key = 'q', fn = function() pref_menu:close() end }, +} + +pref_menu.resolutions = { + { w = config.video_width, h = config.video_height, }, + { w = -2, h = -2, }, + { w = -2, h = 240, }, + { w = -2, h = 360, }, + { w = -2, h = 480, }, + { w = -2, h = 720, }, + { w = -2, h = 1080, }, + { w = -2, h = 1440, }, + { w = -2, h = 2160, }, + selected = 1, +} +pref_menu.audio_bitrates = { + config.audio_bitrate, + '32k', + '64k', + '128k', + '256k', + '384k', + selected = 1, +} + +pref_menu.vid_formats = { 'mp4', 'vp9', 'vp8', } +pref_menu.aud_formats = { 'aac', 'opus', } +pref_menu.litterbox_expirations = { '1h', '12h', '24h', '72h', } + +function pref_menu:get_selected_resolution() + return string.format( + '%s x %s', + config.video_width == -2 and 'auto' or config.video_width, + config.video_height == -2 and 'auto' or config.video_height + ) +end + +function pref_menu:cycle_resolutions() + self.resolutions.selected = self.resolutions.selected + 1 > #self.resolutions and 1 or self.resolutions.selected + 1 + local res = self.resolutions[self.resolutions.selected] + config.video_width = res.w + config.video_height = res.h + self:update() +end + +function pref_menu:cycle_audio_bitrates() + self.audio_bitrates.selected = self.audio_bitrates.selected + 1 > #self.audio_bitrates and 1 or self.audio_bitrates.selected + 1 + config.audio_bitrate = self.audio_bitrates[self.audio_bitrates.selected] + self:update() +end + +function pref_menu:cycle_formats(config_type) + local formats + if config_type == 'video_format' then + formats = pref_menu.vid_formats + else + formats = pref_menu.aud_formats + end + + local selected = 1 + for i, format in ipairs(formats) do + if config[config_type] == format then + selected = i + break + end + end + config[config_type] = formats[selected + 1] or formats[1] + set_encoding_settings() + self:update() +end + +function pref_menu:cycle_video_formats() + pref_menu:cycle_formats('video_format') +end + +function pref_menu:cycle_audio_formats() + pref_menu:cycle_formats('audio_format') +end + +function pref_menu:toggle_mute_audio() + mp.commandv("cycle", "mute") + self:update() +end + +function pref_menu:toggle_embed_subtitles() + mp.commandv("cycle", "sub-visibility") + self:update() +end + +function pref_menu:toggle_catbox() + config['litterbox'] = not config['litterbox'] + self:update() +end + +function pref_menu:cycle_litterbox_expiration() + if not config['litterbox'] then + return + end + local expirations = pref_menu.litterbox_expirations + + local selected = 1 + for i, expiration in ipairs(expirations) do + if config['litterbox_expire'] == expiration then + selected = i + break + end + end + config['litterbox_expire'] = expirations[selected + 1] or expirations[1] + self:update() +end + +function pref_menu:update() + local osd = OSD:new():config(config) + osd:submenu('Preferences'):newline() + osd:tab():item('r: Video resolution: '):append(self:get_selected_resolution()):newline() + osd:tab():item('f: Video format: '):append(config.video_format):newline() + osd:tab():item('a: Audio format: '):append(config.audio_format):newline() + osd:tab():item('b: Audio bitrate: '):append(config.audio_bitrate):newline() + osd:tab():item('m: Mute audio: '):append(mp.get_property("mute")):newline() + osd:tab():item('e: Embed subtitles: '):append(mp.get_property("sub-visibility")):newline() + osd:submenu('Catbox'):newline() + osd:tab():item('x: Using: '):append(config.litterbox and 'Litterbox (temporary)' or 'Catbox (permanent)'):newline() + if config.litterbox then + osd:tab():item('z: Litterbox expires after: '):append(config.litterbox_expire):newline() + else + osd:tab():color("b0b0b0"):text('x: Litterbox expires after: '):append("N/A"):newline() + end + osd:submenu('Save'):newline() + osd:tab():item('s: Save preferences'):newline() + self:overlay_draw(osd:get_text()) +end + +function pref_menu:save() + local function lua_to_mpv(config_value) + if type(config_value) == 'boolean' then + return config_value and 'yes' or 'no' + else + return config_value + end + end + local ignore_list = { + video_extension = true, + audio_extension = true, + video_codec = true, + audio_codec = true, + } + local mpv_dirpath = string.gsub(mp.get_script_directory(), "scripts[\\/][^\\/]+", "") + local config_filepath = utils.join_path(utils.join_path(mpv_dirpath, "script-opts"), string.format('%s.conf', NAME)) + local handle = io.open(config_filepath, 'w') + if handle ~= nil then + handle:write(string.format("# Written by %s on %s.\n", NAME, os.date())) + for key, value in pairs(config) do + if ignore_list[key] == nil then + handle:write(string.format('%s=%s\n', key, lua_to_mpv(value))) + end + end + handle:close() + h.notify("Settings saved.", "info", 2) + else + h.notify_error(string.format("Couldn't open %s.", config_filepath), "error", 4) + end +end + +------------------------------------------------------------ +-- Finally, set an 'entry point' in mpv + +validate_config() +encoder.init(config, main_menu.timings) +mp.add_key_binding('c', 'videoclip-menu-open', main_menu.open) +mp.msg.warn("Press 'c' to open the videoclip menu.") diff --git a/dot_config/private_mpv/scripts/webm.lua b/dot_config/private_mpv/scripts/webm.lua new file mode 100644 index 0000000..57a0942 --- /dev/null +++ b/dot_config/private_mpv/scripts/webm.lua @@ -0,0 +1,2914 @@ +local mp = require("mp") +local assdraw = require("mp.assdraw") +local msg = require("mp.msg") +local utils = require("mp.utils") +local mpopts = require("mp.options") +local options = { + -- Defaults to shift+w + keybind = "W", + -- If empty, saves on the same directory of the playing video. + -- A starting "~" will be replaced by the home dir. + -- This field is delimited by double-square-brackets - [[ and ]] - instead of + -- quotes, because Windows users might run into a issue when using + -- backslashes as a path separator. Examples of valid inputs for this field + -- would be: [[]] (the default, empty value), [[C:\Users\John]] (on Windows), + -- and [[/home/john]] (on Unix-like systems eg. Linux). + -- The [[]] delimiter is not needed when using from a configuration file + -- in the script-opts folder. + output_directory = [[]], + run_detached = false, + -- Template string for the output file + -- %f - Filename, with extension + -- %F - Filename, without extension + -- %T - Media title, if it exists, or filename, with extension (useful for some streams, such as YouTube). + -- %s, %e - Start and end time, with milliseconds + -- %S, %E - Start and end time, without milliseconds + -- %M - "-audio", if audio is enabled, empty otherwise + -- %R - "-(height)p", where height is the video's height, or scale_height, if it's enabled. + -- More specifiers are supported, see https://mpv.io/manual/master/#options-screenshot-template + -- Property expansion is supported (with %{} at top level, ${} when nested), see https://mpv.io/manual/master/#property-expansion + output_template = "%F-[%s-%e]%M", + -- Scale video to a certain height, keeping the aspect ratio. -1 disables it. + scale_height = -1, + -- Change the FPS of the output video, dropping or duplicating frames as needed. + -- -1 means the FPS will be unchanged from the source. + fps = -1, + -- Target filesize, in kB. This will be used to calculate the bitrate + -- used on the encode. If this is set to <= 0, the video bitrate will be set + -- to 0, which might enable constant quality modes, depending on the + -- video codec that's used (VP8 and VP9, for example). + target_filesize = 2500, + -- If true, will use stricter flags to ensure the resulting file doesn't + -- overshoot the target filesize. Not recommended, as constrained quality + -- mode should work well, unless you're really having trouble hitting + -- the target size. + strict_filesize_constraint = false, + strict_bitrate_multiplier = 0.95, + -- In kilobits. + strict_audio_bitrate = 64, + -- Sets the output format, from a few predefined ones. + -- Currently we have: + -- webm-vp8 (libvpx/libvorbis) + -- webm-vp9 (libvpx-vp9/libopus) + -- mp4 (h264/AAC) + -- mp4-nvenc (h264-NVENC/AAC) + -- raw (rawvideo/pcm_s16le). + -- mp3 (libmp3lame) + -- and gif + output_format = "webm-vp8", + twopass = true, + -- If set, applies the video filters currently used on the playback to the encode. + apply_current_filters = true, + -- If set, writes the video's filename to the "Title" field on the metadata. + write_filename_on_metadata = false, + -- Set the number of encoding threads, for codecs libvpx and libvpx-vp9 + libvpx_threads = 4, + additional_flags = "", + -- Constant Rate Factor (CRF). The value meaning and limits may change, + -- from codec to codec. Set to -1 to disable. + crf = 10, + -- Useful for flags that may impact output filesize, such as qmin, qmax etc + -- Won't be applied when strict_filesize_constraint is on. + non_strict_additional_flags = "", + -- Display the encode progress, in %. Requires run_detached to be disabled. + -- On Windows, it shows a cmd popup. "auto" will display progress on non-Windows platforms. + display_progress = "auto", + -- The font size used in the menu. Isn't used for the notifications (started encode, finished encode etc) + font_size = 28, + margin = 10, + message_duration = 5, + -- gif dither mode, 0-5 for bayer w/ bayer_scale 0-5, 6 for paletteuse default (sierra2_4a) + gif_dither = 2, + -- Force square pixels on output video + -- Some players like recent Firefox versions display videos with non-square pixels with wrong aspect ratio + force_square_pixels = false, +} + +mpopts.read_options(options) +local base64_chars='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' + +-- encoding +function base64_encode(data) + return ((data:gsub('.', function(x) + local r,b='',x:byte() + for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end + return r; + end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x) + if (#x < 6) then return '' end + local c=0 + for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end + return base64_chars:sub(c+1,c+1) + end)..({ '', '==', '=' })[#data%3+1]) +end + +-- decoding +function base64_decode(data) + data = string.gsub(data, '[^'..base64_chars..'=]', '') + return (data:gsub('.', function(x) + if (x == '=') then return '' end + local r,f='',(base64_chars:find(x)-1) + for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end + return r; + end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x) + if (#x ~= 8) then return '' end + local c=0 + for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end + return string.char(c) + end)) +end +local emit_event +emit_event = function(event_name, ...) + return mp.commandv("script-message", "webm-" .. tostring(event_name), ...) +end +local test_set_options +test_set_options = function(new_options_json) + local new_options = utils.parse_json(new_options_json) + for k, v in pairs(new_options) do + options[k] = v + end +end +mp.register_script_message("mpv-webm-set-options", test_set_options) +local bold +bold = function(text) + return "{\\b1}" .. tostring(text) .. "{\\b0}" +end +local message +message = function(text, duration) + local ass = mp.get_property_osd("osd-ass-cc/0") + ass = ass .. text + return mp.osd_message(ass, duration or options.message_duration) +end +local append +append = function(a, b) + for _, val in ipairs(b) do + a[#a + 1] = val + end + return a +end +local seconds_to_time_string +seconds_to_time_string = function(seconds, no_ms, full) + if seconds < 0 then + return "unknown" + end + local ret = "" + if not (no_ms) then + ret = string.format(".%03d", seconds * 1000 % 1000) + end + ret = string.format("%02d:%02d%s", math.floor(seconds / 60) % 60, math.floor(seconds) % 60, ret) + if full or seconds > 3600 then + ret = string.format("%d:%s", math.floor(seconds / 3600), ret) + end + return ret +end +local seconds_to_path_element +seconds_to_path_element = function(seconds, no_ms, full) + local time_string = seconds_to_time_string(seconds, no_ms, full) + local _ + time_string, _ = time_string:gsub(":", ".") + return time_string +end +local file_exists +file_exists = function(name) + local info, err = utils.file_info(name) + if info ~= nil then + return true + end + return false +end +local expand_properties +expand_properties = function(text, magic) + if magic == nil then + magic = "$" + end + for prefix, raw, prop, colon, fallback, closing in text:gmatch("%" .. magic .. "{([?!]?)(=?)([^}:]*)(:?)([^}]*)(}*)}") do + local err + local prop_value + local compare_value + local original_prop = prop + local get_property = mp.get_property_osd + if raw == "=" then + get_property = mp.get_property + end + if prefix ~= "" then + for actual_prop, compare in prop:gmatch("(.-)==(.*)") do + prop = actual_prop + compare_value = compare + end + end + if colon == ":" then + prop_value, err = get_property(prop, fallback) + else + prop_value, err = get_property(prop, "(error)") + end + prop_value = tostring(prop_value) + if prefix == "?" then + if compare_value == nil then + prop_value = err == nil and fallback .. closing or "" + else + prop_value = prop_value == compare_value and fallback .. closing or "" + end + prefix = "%" .. prefix + elseif prefix == "!" then + if compare_value == nil then + prop_value = err ~= nil and fallback .. closing or "" + else + prop_value = prop_value ~= compare_value and fallback .. closing or "" + end + else + prop_value = prop_value .. closing + end + if colon == ":" then + local _ + text, _ = text:gsub("%" .. magic .. "{" .. prefix .. raw .. original_prop:gsub("%W", "%%%1") .. ":" .. fallback:gsub("%W", "%%%1") .. closing .. "}", expand_properties(prop_value)) + else + local _ + text, _ = text:gsub("%" .. magic .. "{" .. prefix .. raw .. original_prop:gsub("%W", "%%%1") .. closing .. "}", prop_value) + end + end + return text +end +local format_filename +format_filename = function(startTime, endTime, videoFormat) + local hasAudioCodec = videoFormat.audioCodec ~= "" + local replaceFirst = { + ["%%mp"] = "%%mH.%%mM.%%mS", + ["%%mP"] = "%%mH.%%mM.%%mS.%%mT", + ["%%p"] = "%%wH.%%wM.%%wS", + ["%%P"] = "%%wH.%%wM.%%wS.%%wT" + } + local replaceTable = { + ["%%wH"] = string.format("%02d", math.floor(startTime / (60 * 60))), + ["%%wh"] = string.format("%d", math.floor(startTime / (60 * 60))), + ["%%wM"] = string.format("%02d", math.floor(startTime / 60 % 60)), + ["%%wm"] = string.format("%d", math.floor(startTime / 60)), + ["%%wS"] = string.format("%02d", math.floor(startTime % 60)), + ["%%ws"] = string.format("%d", math.floor(startTime)), + ["%%wf"] = string.format("%s", startTime), + ["%%wT"] = string.sub(string.format("%.3f", startTime % 1), 3), + ["%%mH"] = string.format("%02d", math.floor(endTime / (60 * 60))), + ["%%mh"] = string.format("%d", math.floor(endTime / (60 * 60))), + ["%%mM"] = string.format("%02d", math.floor(endTime / 60 % 60)), + ["%%mm"] = string.format("%d", math.floor(endTime / 60)), + ["%%mS"] = string.format("%02d", math.floor(endTime % 60)), + ["%%ms"] = string.format("%d", math.floor(endTime)), + ["%%mf"] = string.format("%s", endTime), + ["%%mT"] = string.sub(string.format("%.3f", endTime % 1), 3), + ["%%f"] = mp.get_property("filename"), + ["%%F"] = mp.get_property("filename/no-ext"), + ["%%s"] = seconds_to_path_element(startTime), + ["%%S"] = seconds_to_path_element(startTime, true), + ["%%e"] = seconds_to_path_element(endTime), + ["%%E"] = seconds_to_path_element(endTime, true), + ["%%T"] = mp.get_property("media-title"), + ["%%M"] = (mp.get_property_native('aid') and not mp.get_property_native('mute') and hasAudioCodec) and '-audio' or '', + ["%%R"] = (options.scale_height ~= -1) and "-" .. tostring(options.scale_height) .. "p" or "-" .. tostring(mp.get_property_native('height')) .. "p", + ["%%t%%"] = "%%" + } + local filename = options.output_template + for format, value in pairs(replaceFirst) do + local _ + filename, _ = filename:gsub(format, value) + end + for format, value in pairs(replaceTable) do + local _ + filename, _ = filename:gsub(format, value) + end + if mp.get_property_bool("demuxer-via-network", false) then + local _ + filename, _ = filename:gsub("%%X{([^}]*)}", "%1") + filename, _ = filename:gsub("%%x", "") + else + local x = string.gsub(mp.get_property("stream-open-filename", ""), string.gsub(mp.get_property("filename", ""), "%W", "%%%1") .. "$", "") + local _ + filename, _ = filename:gsub("%%X{[^}]*}", x) + filename, _ = filename:gsub("%%x", x) + end + filename = expand_properties(filename, "%") + for format in filename:gmatch("%%t([aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ])") do + local _ + filename, _ = filename:gsub("%%t" .. format, os.date("%" .. format)) + end + local _ + filename, _ = filename:gsub("[<>:\"/\\|?*]", "") + return tostring(filename) .. "." .. tostring(videoFormat.outputExtension) +end +local parse_directory +parse_directory = function(dir) + local home_dir = os.getenv("HOME") + if not home_dir then + home_dir = os.getenv("USERPROFILE") + end + if not home_dir then + local drive = os.getenv("HOMEDRIVE") + local path = os.getenv("HOMEPATH") + if drive and path then + home_dir = utils.join_path(drive, path) + else + msg.warn("Couldn't find home dir.") + home_dir = "" + end + end + local _ + dir, _ = dir:gsub("^~", home_dir) + return dir +end +local is_windows = type(package) == "table" and type(package.config) == "string" and package.config:sub(1, 1) == "\\" +local trim +trim = function(s) + return s:match("^%s*(.-)%s*$") +end +local get_null_path +get_null_path = function() + if file_exists("/dev/null") then + return "/dev/null" + end + return "NUL" +end +local run_subprocess +run_subprocess = function(params) + local res = utils.subprocess(params) + msg.verbose("Command stdout: ") + msg.verbose(res.stdout) + if res.status ~= 0 then + msg.verbose("Command failed! Reason: ", res.error, " Killed by us? ", res.killed_by_us and "yes" or "no") + return false + end + return true +end +local shell_escape +shell_escape = function(args) + local ret = { } + for i, a in ipairs(args) do + local s = tostring(a) + if string.match(s, "[^A-Za-z0-9_/:=-]") then + if is_windows then + s = '"' .. string.gsub(s, '"', '"\\""') .. '"' + else + s = "'" .. string.gsub(s, "'", "'\\''") .. "'" + end + end + table.insert(ret, s) + end + local concat = table.concat(ret, " ") + if is_windows then + concat = '"' .. concat .. '"' + end + return concat +end +local run_subprocess_popen +run_subprocess_popen = function(command_line) + local command_line_string = shell_escape(command_line) + command_line_string = command_line_string .. " 2>&1" + msg.verbose("run_subprocess_popen: running " .. tostring(command_line_string)) + return io.popen(command_line_string) +end +local calculate_scale_factor +calculate_scale_factor = function() + local baseResY = 720 + local osd_w, osd_h = mp.get_osd_size() + return osd_h / baseResY +end +local should_display_progress +should_display_progress = function() + if options.display_progress == "auto" then + return not is_windows + end + return options.display_progress +end +local reverse +reverse = function(list) + local _accum_0 = { } + local _len_0 = 1 + local _max_0 = 1 + for _index_0 = #list, _max_0 < 0 and #list + _max_0 or _max_0, -1 do + local element = list[_index_0] + _accum_0[_len_0] = element + _len_0 = _len_0 + 1 + end + return _accum_0 +end +local get_pass_logfile_path +get_pass_logfile_path = function(encode_out_path) + return tostring(encode_out_path) .. "-video-pass1.log" +end +local dimensions_changed = true +local _video_dimensions = { } +local get_video_dimensions +get_video_dimensions = function() + if not (dimensions_changed) then + return _video_dimensions + end + local video_params = mp.get_property_native("video-out-params") + if not video_params then + return nil + end + dimensions_changed = false + local keep_aspect = mp.get_property_bool("keepaspect") + local w = video_params["w"] + local h = video_params["h"] + local dw = video_params["dw"] + local dh = video_params["dh"] + if mp.get_property_number("video-rotate") % 180 == 90 then + w, h = h, w + dw, dh = dh, dw + end + _video_dimensions = { + top_left = { }, + bottom_right = { }, + ratios = { } + } + local window_w, window_h = mp.get_osd_size() + if keep_aspect then + local unscaled = mp.get_property_native("video-unscaled") + local panscan = mp.get_property_number("panscan") + local fwidth = window_w + local fheight = math.floor(window_w / dw * dh) + if fheight > window_h or fheight < h then + local tmpw = math.floor(window_h / dh * dw) + if tmpw <= window_w then + fheight = window_h + fwidth = tmpw + end + end + local vo_panscan_area = window_h - fheight + local f_w = fwidth / fheight + local f_h = 1 + if vo_panscan_area == 0 then + vo_panscan_area = window_h - fwidth + f_w = 1 + f_h = fheight / fwidth + end + if unscaled or unscaled == "downscale-big" then + vo_panscan_area = 0 + if unscaled or (dw <= window_w and dh <= window_h) then + fwidth = dw + fheight = dh + end + end + local scaled_width = fwidth + math.floor(vo_panscan_area * panscan * f_w) + local scaled_height = fheight + math.floor(vo_panscan_area * panscan * f_h) + local split_scaling + split_scaling = function(dst_size, scaled_src_size, zoom, align, pan) + scaled_src_size = math.floor(scaled_src_size * 2 ^ zoom) + align = (align + 1) / 2 + local dst_start = math.floor((dst_size - scaled_src_size) * align + pan * scaled_src_size) + if dst_start < 0 then + dst_start = dst_start + 1 + end + local dst_end = dst_start + scaled_src_size + if dst_start >= dst_end then + dst_start = 0 + dst_end = 1 + end + return dst_start, dst_end + end + local zoom = mp.get_property_number("video-zoom") + local align_x = mp.get_property_number("video-align-x") + local pan_x = mp.get_property_number("video-pan-x") + _video_dimensions.top_left.x, _video_dimensions.bottom_right.x = split_scaling(window_w, scaled_width, zoom, align_x, pan_x) + local align_y = mp.get_property_number("video-align-y") + local pan_y = mp.get_property_number("video-pan-y") + _video_dimensions.top_left.y, _video_dimensions.bottom_right.y = split_scaling(window_h, scaled_height, zoom, align_y, pan_y) + else + _video_dimensions.top_left.x = 0 + _video_dimensions.bottom_right.x = window_w + _video_dimensions.top_left.y = 0 + _video_dimensions.bottom_right.y = window_h + end + _video_dimensions.ratios.w = w / (_video_dimensions.bottom_right.x - _video_dimensions.top_left.x) + _video_dimensions.ratios.h = h / (_video_dimensions.bottom_right.y - _video_dimensions.top_left.y) + return _video_dimensions +end +local set_dimensions_changed +set_dimensions_changed = function() + dimensions_changed = true +end +local monitor_dimensions +monitor_dimensions = function() + local properties = { + "keepaspect", + "video-out-params", + "video-unscaled", + "panscan", + "video-zoom", + "video-align-x", + "video-pan-x", + "video-align-y", + "video-pan-y", + "osd-width", + "osd-height" + } + for _, p in ipairs(properties) do + mp.observe_property(p, "native", set_dimensions_changed) + end +end +local clamp +clamp = function(min, val, max) + if val <= min then + return min + end + if val >= max then + return max + end + return val +end +local clamp_point +clamp_point = function(top_left, point, bottom_right) + return { + x = clamp(top_left.x, point.x, bottom_right.x), + y = clamp(top_left.y, point.y, bottom_right.y) + } +end +local VideoPoint +do + local _class_0 + local _base_0 = { + set_from_screen = function(self, sx, sy) + local d = get_video_dimensions() + local point = clamp_point(d.top_left, { + x = sx, + y = sy + }, d.bottom_right) + self.x = math.floor(d.ratios.w * (point.x - d.top_left.x) + 0.5) + self.y = math.floor(d.ratios.h * (point.y - d.top_left.y) + 0.5) + end, + to_screen = function(self) + local d = get_video_dimensions() + return { + x = math.floor(self.x / d.ratios.w + d.top_left.x + 0.5), + y = math.floor(self.y / d.ratios.h + d.top_left.y + 0.5) + } + end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function(self) + self.x = -1 + self.y = -1 + end, + __base = _base_0, + __name = "VideoPoint" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + VideoPoint = _class_0 +end +local Region +do + local _class_0 + local _base_0 = { + is_valid = function(self) + return self.x > -1 and self.y > -1 and self.w > -1 and self.h > -1 + end, + set_from_points = function(self, p1, p2) + self.x = math.min(p1.x, p2.x) + self.y = math.min(p1.y, p2.y) + self.w = math.abs(p1.x - p2.x) + self.h = math.abs(p1.y - p2.y) + end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function(self) + self.x = -1 + self.y = -1 + self.w = -1 + self.h = -1 + end, + __base = _base_0, + __name = "Region" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + Region = _class_0 +end +local make_fullscreen_region +make_fullscreen_region = function() + local r = Region() + local d = get_video_dimensions() + local a = VideoPoint() + local b = VideoPoint() + local xa, ya + do + local _obj_0 = d.top_left + xa, ya = _obj_0.x, _obj_0.y + end + a:set_from_screen(xa, ya) + local xb, yb + do + local _obj_0 = d.bottom_right + xb, yb = _obj_0.x, _obj_0.y + end + b:set_from_screen(xb, yb) + r:set_from_points(a, b) + return r +end +local read_double +read_double = function(bytes) + local sign = 1 + local mantissa = bytes[2] % 2 ^ 4 + for i = 3, 8 do + mantissa = mantissa * 256 + bytes[i] + end + if bytes[1] > 127 then + sign = -1 + end + local exponent = (bytes[1] % 128) * 2 ^ 4 + math.floor(bytes[2] / 2 ^ 4) + if exponent == 0 then + return 0 + end + mantissa = (math.ldexp(mantissa, -52) + 1) * sign + return math.ldexp(mantissa, exponent - 1023) +end +local write_double +write_double = function(num) + local bytes = { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + } + if num == 0 then + return bytes + end + local anum = math.abs(num) + local mantissa, exponent = math.frexp(anum) + exponent = exponent - 1 + mantissa = mantissa * 2 - 1 + local sign = num ~= anum and 128 or 0 + exponent = exponent + 1023 + bytes[1] = sign + math.floor(exponent / 2 ^ 4) + mantissa = mantissa * 2 ^ 4 + local currentmantissa = math.floor(mantissa) + mantissa = mantissa - currentmantissa + bytes[2] = (exponent % 2 ^ 4) * 2 ^ 4 + currentmantissa + for i = 3, 8 do + mantissa = mantissa * 2 ^ 8 + currentmantissa = math.floor(mantissa) + mantissa = mantissa - currentmantissa + bytes[i] = currentmantissa + end + return bytes +end +local FirstpassStats +do + local _class_0 + local duration_multiplier, fields_before_duration, fields_after_duration + local _base_0 = { + get_duration = function(self) + local big_endian_binary_duration = reverse(self.binary_duration) + return read_double(reversed_binary_duration) / duration_multiplier + end, + set_duration = function(self, duration) + local big_endian_binary_duration = write_double(duration * duration_multiplier) + self.binary_duration = reverse(big_endian_binary_duration) + end, + _bytes_to_string = function(self, bytes) + return string.char(unpack(bytes)) + end, + as_binary_string = function(self) + local before_duration_string = self:_bytes_to_string(self.binary_data_before_duration) + local duration_string = self:_bytes_to_string(self.binary_duration) + local after_duration_string = self:_bytes_to_string(self.binary_data_after_duration) + return before_duration_string .. duration_string .. after_duration_string + end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function(self, before_duration, duration, after_duration) + self.binary_data_before_duration = before_duration + self.binary_duration = duration + self.binary_data_after_duration = after_duration + end, + __base = _base_0, + __name = "FirstpassStats" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + local self = _class_0 + duration_multiplier = 10000000.0 + fields_before_duration = 16 + fields_after_duration = 1 + self.data_before_duration_size = function(self) + return fields_before_duration * 8 + end + self.data_after_duration_size = function(self) + return fields_after_duration * 8 + end + self.size = function(self) + return (fields_before_duration + 1 + fields_after_duration) * 8 + end + self.from_bytes = function(self, bytes) + local before_duration + do + local _accum_0 = { } + local _len_0 = 1 + local _max_0 = self:data_before_duration_size() + for _index_0 = 1, _max_0 < 0 and #bytes + _max_0 or _max_0 do + local b = bytes[_index_0] + _accum_0[_len_0] = b + _len_0 = _len_0 + 1 + end + before_duration = _accum_0 + end + local duration + do + local _accum_0 = { } + local _len_0 = 1 + local _max_0 = self:data_before_duration_size() + 8 + for _index_0 = self:data_before_duration_size() + 1, _max_0 < 0 and #bytes + _max_0 or _max_0 do + local b = bytes[_index_0] + _accum_0[_len_0] = b + _len_0 = _len_0 + 1 + end + duration = _accum_0 + end + local after_duration + do + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = self:data_before_duration_size() + 8 + 1, #bytes do + local b = bytes[_index_0] + _accum_0[_len_0] = b + _len_0 = _len_0 + 1 + end + after_duration = _accum_0 + end + return self(before_duration, duration, after_duration) + end + FirstpassStats = _class_0 +end +local read_logfile_into_stats_array +read_logfile_into_stats_array = function(logfile_path) + local file = assert(io.open(logfile_path, "rb")) + local logfile_string = base64_decode(file:read()) + file:close() + local stats_size = FirstpassStats:size() + assert(logfile_string:len() % stats_size == 0) + local stats = { } + for offset = 1, #logfile_string, stats_size do + local bytes = { + logfile_string:byte(offset, offset + stats_size - 1) + } + assert(#bytes == stats_size) + stats[#stats + 1] = FirstpassStats:from_bytes(bytes) + end + return stats +end +local write_stats_array_to_logfile +write_stats_array_to_logfile = function(stats_array, logfile_path) + local file = assert(io.open(logfile_path, "wb")) + local logfile_string = "" + for _index_0 = 1, #stats_array do + local stat = stats_array[_index_0] + logfile_string = logfile_string .. stat:as_binary_string() + end + file:write(base64_encode(logfile_string)) + return file:close() +end +local vp8_patch_logfile +vp8_patch_logfile = function(logfile_path, encode_total_duration) + local stats_array = read_logfile_into_stats_array(logfile_path) + local average_duration = encode_total_duration / (#stats_array - 1) + for i = 1, #stats_array - 1 do + stats_array[i]:set_duration(average_duration) + end + stats_array[#stats_array]:set_duration(encode_total_duration) + return write_stats_array_to_logfile(stats_array, logfile_path) +end +local formats = { } +local Format +do + local _class_0 + local _base_0 = { + getPreFilters = function(self) + return { } + end, + getPostFilters = function(self) + return { } + end, + getFlags = function(self) + return { } + end, + getCodecFlags = function(self) + local codecs = { } + if self.videoCodec ~= "" then + codecs[#codecs + 1] = "--ovc=" .. tostring(self.videoCodec) + end + if self.audioCodec ~= "" then + codecs[#codecs + 1] = "--oac=" .. tostring(self.audioCodec) + end + return codecs + end, + postCommandModifier = function(self, command, region, startTime, endTime) + return command + end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function(self) + self.displayName = "Basic" + self.supportsTwopass = true + self.videoCodec = "" + self.audioCodec = "" + self.outputExtension = "" + self.acceptsBitrate = true + end, + __base = _base_0, + __name = "Format" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + Format = _class_0 +end +local RawVideo +do + local _class_0 + local _parent_0 = Format + local _base_0 = { + getColorspace = function(self) + local csp = mp.get_property("colormatrix") + local _exp_0 = csp + if "bt.601" == _exp_0 then + return "bt601" + elseif "bt.709" == _exp_0 then + return "bt709" + elseif "bt.2020" == _exp_0 then + return "bt2020" + elseif "smpte-240m" == _exp_0 then + return "smpte240m" + else + msg.info("Warning, unknown colorspace " .. tostring(csp) .. " detected, using bt.601.") + return "bt601" + end + end, + getPostFilters = function(self) + return { + "format=yuv444p16", + "lavfi-scale=in_color_matrix=" .. self:getColorspace(), + "format=bgr24" + } + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self) + self.displayName = "Raw" + self.supportsTwopass = false + self.videoCodec = "rawvideo" + self.audioCodec = "pcm_s16le" + self.outputExtension = "avi" + self.acceptsBitrate = false + end, + __base = _base_0, + __name = "RawVideo", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + RawVideo = _class_0 +end +formats["raw"] = RawVideo() +local WebmVP8 +do + local _class_0 + local _parent_0 = Format + local _base_0 = { + getPreFilters = function(self) + local colormatrixFilter = { + ["bt.709"] = "bt709", + ["bt.2020"] = "bt2020", + ["smpte-240m"] = "smpte240m" + } + local ret = { } + local colormatrix = mp.get_property_native("video-params/colormatrix") + if colormatrixFilter[colormatrix] then + append(ret, { + "lavfi-colormatrix=" .. tostring(colormatrixFilter[colormatrix]) .. ":bt601" + }) + end + return ret + end, + getFlags = function(self) + return { + "--ovcopts-add=threads=" .. tostring(options.libvpx_threads), + "--ovcopts-add=auto-alt-ref=1", + "--ovcopts-add=lag-in-frames=25", + "--ovcopts-add=quality=good", + "--ovcopts-add=cpu-used=0" + } + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self) + self.displayName = "WebM" + self.supportsTwopass = true + self.videoCodec = "libvpx" + self.audioCodec = "libvorbis" + self.outputExtension = "webm" + self.acceptsBitrate = true + end, + __base = _base_0, + __name = "WebmVP8", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + WebmVP8 = _class_0 +end +formats["webm-vp8"] = WebmVP8() +local WebmVP9 +do + local _class_0 + local _parent_0 = Format + local _base_0 = { + getFlags = function(self) + return { + "--ovcopts-add=threads=" .. tostring(options.libvpx_threads) + } + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self) + self.displayName = "WebM (VP9)" + self.supportsTwopass = false + self.videoCodec = "libvpx-vp9" + self.audioCodec = "libopus" + self.outputExtension = "webm" + self.acceptsBitrate = true + end, + __base = _base_0, + __name = "WebmVP9", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + WebmVP9 = _class_0 +end +formats["webm-vp9"] = WebmVP9() +local MP4 +do + local _class_0 + local _parent_0 = Format + local _base_0 = { } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self) + self.displayName = "MP4 (h264/AAC)" + self.supportsTwopass = true + self.videoCodec = "libx264" + self.audioCodec = "aac" + self.outputExtension = "mp4" + self.acceptsBitrate = true + end, + __base = _base_0, + __name = "MP4", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + MP4 = _class_0 +end +formats["mp4"] = MP4() +local MP4NVENC +do + local _class_0 + local _parent_0 = Format + local _base_0 = { } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self) + self.displayName = "MP4 (h264-NVENC/AAC)" + self.supportsTwopass = true + self.videoCodec = "h264_nvenc" + self.audioCodec = "aac" + self.outputExtension = "mp4" + self.acceptsBitrate = true + end, + __base = _base_0, + __name = "MP4NVENC", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + MP4NVENC = _class_0 +end +formats["mp4-nvenc"] = MP4NVENC() +local MP3 +do + local _class_0 + local _parent_0 = Format + local _base_0 = { } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self) + self.displayName = "MP3 (libmp3lame)" + self.supportsTwopass = false + self.videoCodec = "" + self.audioCodec = "libmp3lame" + self.outputExtension = "mp3" + self.acceptsBitrate = true + end, + __base = _base_0, + __name = "MP3", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + MP3 = _class_0 +end +formats["mp3"] = MP3() +local GIF +do + local _class_0 + local _parent_0 = Format + local _base_0 = { + postCommandModifier = function(self, command, region, startTime, endTime) + local new_command = { } + local start_ts = seconds_to_time_string(startTime, false, true) + local end_ts = seconds_to_time_string(endTime, false, true) + start_ts = start_ts:gsub(":", "\\\\:") + end_ts = end_ts:gsub(":", "\\\\:") + local cfilter = "[vid1]trim=start=" .. tostring(start_ts) .. ":end=" .. tostring(end_ts) .. "[vidtmp];" + if mp.get_property("deinterlace") == "yes" then + cfilter = cfilter .. "[vidtmp]yadif=mode=1[vidtmp];" + end + for _, v in ipairs(command) do + local _continue_0 = false + repeat + if v:match("^%-%-vf%-add=lavfi%-scale") or v:match("^%-%-vf%-add=lavfi%-crop") or v:match("^%-%-vf%-add=fps") or v:match("^%-%-vf%-add=lavfi%-eq") then + local n = v:gsub("^%-%-vf%-add=", ""):gsub("^lavfi%-", "") + cfilter = cfilter .. "[vidtmp]" .. tostring(n) .. "[vidtmp];" + else + if v:match("^%-%-video%-rotate=90") then + cfilter = cfilter .. "[vidtmp]transpose=1[vidtmp];" + else + if v:match("^%-%-video%-rotate=270") then + cfilter = cfilter .. "[vidtmp]transpose=2[vidtmp];" + else + if v:match("^%-%-video%-rotate=180") then + cfilter = cfilter .. "[vidtmp]transpose=1[vidtmp];[vidtmp]transpose=1[vidtmp];" + else + if v:match("^%-%-deinterlace=") then + _continue_0 = true + break + else + append(new_command, { + v + }) + _continue_0 = true + break + end + end + end + end + end + _continue_0 = true + until true + if not _continue_0 then + break + end + end + cfilter = cfilter .. "[vidtmp]split[topal][vidf];" + cfilter = cfilter .. "[topal]palettegen[pal];" + cfilter = cfilter .. "[vidf]fifo[vidf];" + cfilter = cfilter .. "[vidf][pal]paletteuse=diff_mode=rectangle" + if options.gif_dither ~= 6 then + cfilter = cfilter .. ":dither=bayer:bayer_scale=" .. tostring(options.gif_dither) + end + cfilter = cfilter .. "[vo]" + append(new_command, { + "--lavfi-complex=" .. tostring(cfilter) + }) + return new_command + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self) + self.displayName = "GIF" + self.supportsTwopass = false + self.videoCodec = "gif" + self.audioCodec = "" + self.outputExtension = "gif" + self.acceptsBitrate = false + end, + __base = _base_0, + __name = "GIF", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + GIF = _class_0 +end +formats["gif"] = GIF() +local Page +do + local _class_0 + local _base_0 = { + add_keybinds = function(self) + if not self.keybinds then + return + end + for key, func in pairs(self.keybinds) do + mp.add_forced_key_binding(key, key, func, { + repeatable = true + }) + end + end, + remove_keybinds = function(self) + if not self.keybinds then + return + end + for key, _ in pairs(self.keybinds) do + mp.remove_key_binding(key) + end + end, + observe_properties = function(self) + self.sizeCallback = function() + return self:draw() + end + local properties = { + "keepaspect", + "video-out-params", + "video-unscaled", + "panscan", + "video-zoom", + "video-align-x", + "video-pan-x", + "video-align-y", + "video-pan-y", + "osd-width", + "osd-height" + } + for _index_0 = 1, #properties do + local p = properties[_index_0] + mp.observe_property(p, "native", self.sizeCallback) + end + end, + unobserve_properties = function(self) + if self.sizeCallback then + mp.unobserve_property(self.sizeCallback) + self.sizeCallback = nil + end + end, + clear = function(self) + local window_w, window_h = mp.get_osd_size() + mp.set_osd_ass(window_w, window_h, "") + return mp.osd_message("", 0) + end, + prepare = function(self) + return nil + end, + dispose = function(self) + return nil + end, + show = function(self) + if self.visible then + return + end + self.visible = true + self:observe_properties() + self:add_keybinds() + self:prepare() + self:clear() + return self:draw() + end, + hide = function(self) + if not self.visible then + return + end + self.visible = false + self:unobserve_properties() + self:remove_keybinds() + self:clear() + return self:dispose() + end, + setup_text = function(self, ass) + local scale = calculate_scale_factor() + local margin = options.margin * scale + ass:append("{\\an7}") + ass:pos(margin, margin) + return ass:append("{\\fs" .. tostring(options.font_size * scale) .. "}") + end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function() end, + __base = _base_0, + __name = "Page" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + Page = _class_0 +end +local EncodeWithProgress +do + local _class_0 + local _parent_0 = Page + local _base_0 = { + draw = function(self) + local progress = 100 * ((self.currentTime - self.startTime) / self.duration) + local progressText = string.format("%d%%", progress) + local window_w, window_h = mp.get_osd_size() + local ass = assdraw.ass_new() + ass:new_event() + self:setup_text(ass) + ass:append("Encoding (" .. tostring(bold(progressText)) .. ")\\N") + return mp.set_osd_ass(window_w, window_h, ass.text) + end, + parseLine = function(self, line) + local matchTime = string.match(line, "Encode time[-]pos: ([0-9.]+)") + local matchExit = string.match(line, "Exiting... [(]([%a ]+)[)]") + if matchTime == nil and matchExit == nil then + return + end + if matchTime ~= nil and tonumber(matchTime) > self.currentTime then + self.currentTime = tonumber(matchTime) + end + if matchExit ~= nil then + self.finished = true + self.finishedReason = matchExit + end + end, + startEncode = function(self, command_line) + local copy_command_line + do + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #command_line do + local arg = command_line[_index_0] + _accum_0[_len_0] = arg + _len_0 = _len_0 + 1 + end + copy_command_line = _accum_0 + end + append(copy_command_line, { + '--term-status-msg=Encode time-pos: ${=time-pos}\\n' + }) + self:show() + local processFd = run_subprocess_popen(copy_command_line) + for line in processFd:lines() do + msg.verbose(string.format('%q', line)) + self:parseLine(line) + self:draw() + end + processFd:close() + self:hide() + if self.finishedReason == "End of file" then + return true + end + return false + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, startTime, endTime) + self.startTime = startTime + self.endTime = endTime + self.duration = endTime - startTime + self.currentTime = startTime + end, + __base = _base_0, + __name = "EncodeWithProgress", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + EncodeWithProgress = _class_0 +end +local get_active_tracks +get_active_tracks = function() + local accepted = { + video = true, + audio = not mp.get_property_bool("mute"), + sub = mp.get_property_bool("sub-visibility") + } + local active = { + video = { }, + audio = { }, + sub = { } + } + for _, track in ipairs(mp.get_property_native("track-list")) do + if track["selected"] and accepted[track["type"]] then + local count = #active[track["type"]] + active[track["type"]][count + 1] = track + end + end + return active +end +local filter_tracks_supported_by_format +filter_tracks_supported_by_format = function(active_tracks, format) + local has_video_codec = format.videoCodec ~= "" + local has_audio_codec = format.audioCodec ~= "" + local supported = { + video = has_video_codec and active_tracks["video"] or { }, + audio = has_audio_codec and active_tracks["audio"] or { }, + sub = has_video_codec and active_tracks["sub"] or { } + } + return supported +end +local append_track +append_track = function(out, track) + local external_flag = { + ["audio"] = "audio-file", + ["sub"] = "sub-file" + } + local internal_flag = { + ["video"] = "vid", + ["audio"] = "aid", + ["sub"] = "sid" + } + if track['external'] and string.len(track['external-filename']) <= 2048 then + return append(out, { + "--" .. tostring(external_flag[track['type']]) .. "=" .. tostring(track['external-filename']) + }) + else + return append(out, { + "--" .. tostring(internal_flag[track['type']]) .. "=" .. tostring(track['id']) + }) + end +end +local append_audio_tracks +append_audio_tracks = function(out, tracks) + local internal_tracks = { } + for _index_0 = 1, #tracks do + local track = tracks[_index_0] + if track['external'] then + append_track(out, track) + else + append(internal_tracks, { + track + }) + end + end + if #internal_tracks > 1 then + local filter_string = "" + for _index_0 = 1, #internal_tracks do + local track = internal_tracks[_index_0] + filter_string = filter_string .. "[aid" .. tostring(track['id']) .. "]" + end + filter_string = filter_string .. "amix[ao]" + return append(out, { + "--lavfi-complex=" .. tostring(filter_string) + }) + else + if #internal_tracks == 1 then + return append_track(out, internal_tracks[1]) + end + end +end +local get_scale_filters +get_scale_filters = function() + local filters = { } + if options.force_square_pixels then + append(filters, { + "lavfi-scale=iw*sar:ih" + }) + end + if options.scale_height > 0 then + append(filters, { + "lavfi-scale=-2:" .. tostring(options.scale_height) + }) + end + return filters +end +local get_fps_filters +get_fps_filters = function() + if options.fps > 0 then + return { + "fps=" .. tostring(options.fps) + } + end + return { } +end +local get_contrast_brightness_and_saturation_filters +get_contrast_brightness_and_saturation_filters = function() + local mpv_brightness = mp.get_property("brightness") + local mpv_contrast = mp.get_property("contrast") + local mpv_saturation = mp.get_property("saturation") + if mpv_brightness == 0 and mpv_contrast == 0 and mpv_saturation == 0 then + return { } + end + local eq_saturation = (mpv_saturation + 100) / 100.0 + local eq_contrast = (mpv_contrast + 100) / 100.0 + local eq_brightness = (mpv_brightness / 50.0 + eq_contrast - 1) / 2.0 + return { + "lavfi-eq=contrast=" .. tostring(eq_contrast) .. ":saturation=" .. tostring(eq_saturation) .. ":brightness=" .. tostring(eq_brightness) + } +end +local append_property +append_property = function(out, property_name, option_name) + option_name = option_name or property_name + local prop = mp.get_property(property_name) + if prop and prop ~= "" then + return append(out, { + "--" .. tostring(option_name) .. "=" .. tostring(prop) + }) + end +end +local append_list_options +append_list_options = function(out, property_name, option_prefix) + option_prefix = option_prefix or property_name + local prop = mp.get_property_native(property_name) + if prop then + for _index_0 = 1, #prop do + local value = prop[_index_0] + append(out, { + "--" .. tostring(option_prefix) .. "-append=" .. tostring(value) + }) + end + end +end +local get_playback_options +get_playback_options = function() + local ret = { } + append_property(ret, "sub-ass-override") + append_property(ret, "sub-ass-force-style") + append_property(ret, "sub-ass-vsfilter-aspect-compat") + append_property(ret, "sub-auto") + append_property(ret, "sub-pos") + append_property(ret, "sub-delay") + append_property(ret, "video-rotate") + append_property(ret, "ytdl-format") + append_property(ret, "deinterlace") + return ret +end +local get_speed_flags +get_speed_flags = function() + local ret = { } + local speed = mp.get_property_native("speed") + if speed ~= 1 then + append(ret, { + "--vf-add=setpts=PTS/" .. tostring(speed), + "--af-add=atempo=" .. tostring(speed), + "--sub-speed=1/" .. tostring(speed) + }) + end + return ret +end +local get_metadata_flags +get_metadata_flags = function() + local title = mp.get_property("filename/no-ext") + return { + "--oset-metadata=title=%" .. tostring(string.len(title)) .. "%" .. tostring(title) + } +end +local apply_current_filters +apply_current_filters = function(filters) + local vf = mp.get_property_native("vf") + msg.verbose("apply_current_filters: got " .. tostring(#vf) .. " currently applied.") + for _index_0 = 1, #vf do + local _continue_0 = false + repeat + local filter = vf[_index_0] + msg.verbose("apply_current_filters: filter name: " .. tostring(filter['name'])) + if filter["enabled"] == false then + _continue_0 = true + break + end + local str = filter["name"] + local params = filter["params"] or { } + for k, v in pairs(params) do + str = str .. ":" .. tostring(k) .. "=%" .. tostring(string.len(v)) .. "%" .. tostring(v) + end + append(filters, { + str + }) + _continue_0 = true + until true + if not _continue_0 then + break + end + end +end +local get_video_filters +get_video_filters = function(format, region) + local filters = { } + append(filters, format:getPreFilters()) + if options.apply_current_filters then + apply_current_filters(filters) + end + if region and region:is_valid() then + append(filters, { + "lavfi-crop=" .. tostring(region.w) .. ":" .. tostring(region.h) .. ":" .. tostring(region.x) .. ":" .. tostring(region.y) + }) + end + append(filters, get_scale_filters()) + append(filters, get_fps_filters()) + append(filters, get_contrast_brightness_and_saturation_filters()) + append(filters, format:getPostFilters()) + return filters +end +local get_video_encode_flags +get_video_encode_flags = function(format, region) + local flags = { } + append(flags, get_playback_options()) + local filters = get_video_filters(format, region) + for _index_0 = 1, #filters do + local f = filters[_index_0] + append(flags, { + "--vf-add=" .. tostring(f) + }) + end + append(flags, get_speed_flags()) + return flags +end +local calculate_bitrate +calculate_bitrate = function(active_tracks, format, length) + if format.videoCodec == "" then + return nil, options.target_filesize * 8 / length + end + local video_kilobits = options.target_filesize * 8 + local audio_kilobits = nil + local has_audio_track = #active_tracks["audio"] > 0 + if options.strict_filesize_constraint and has_audio_track then + audio_kilobits = length * options.strict_audio_bitrate + video_kilobits = video_kilobits - audio_kilobits + end + local video_bitrate = math.floor(video_kilobits / length) + local audio_bitrate = audio_kilobits and math.floor(audio_kilobits / length) or nil + return video_bitrate, audio_bitrate +end +local find_path +find_path = function(startTime, endTime) + local path = mp.get_property('path') + if not path then + return nil, nil, nil, nil, nil + end + local is_stream = not file_exists(path) + local is_temporary = false + if is_stream then + if mp.get_property('file-format') == 'hls' then + path = utils.join_path(parse_directory('~'), 'cache_dump.ts') + mp.command_native({ + 'dump_cache', + seconds_to_time_string(startTime, false, true), + seconds_to_time_string(endTime + 5, false, true), + path + }) + endTime = endTime - startTime + startTime = 0 + is_temporary = true + end + end + return path, is_stream, is_temporary, startTime, endTime +end +local encode +encode = function(region, startTime, endTime) + local format = formats[options.output_format] + local originalStartTime = startTime + local originalEndTime = endTime + local path, is_stream, is_temporary + path, is_stream, is_temporary, startTime, endTime = find_path(startTime, endTime) + if not path then + message("No file is being played") + return + end + local command = { + "mpv", + path, + "--start=" .. seconds_to_time_string(startTime, false, true), + "--end=" .. seconds_to_time_string(endTime, false, true), + "--loop-file=no", + "--no-pause" + } + append(command, format:getCodecFlags()) + local active_tracks = get_active_tracks() + local supported_active_tracks = filter_tracks_supported_by_format(active_tracks, format) + for track_type, tracks in pairs(supported_active_tracks) do + if track_type == "audio" then + append_audio_tracks(command, tracks) + else + for _index_0 = 1, #tracks do + local track = tracks[_index_0] + append_track(command, track) + end + end + end + for track_type, tracks in pairs(supported_active_tracks) do + local _continue_0 = false + repeat + if #tracks > 0 then + _continue_0 = true + break + end + local _exp_0 = track_type + if "video" == _exp_0 then + append(command, { + "--vid=no" + }) + elseif "audio" == _exp_0 then + append(command, { + "--aid=no" + }) + elseif "sub" == _exp_0 then + append(command, { + "--sid=no" + }) + end + _continue_0 = true + until true + if not _continue_0 then + break + end + end + if format.videoCodec ~= "" then + append(command, get_video_encode_flags(format, region)) + end + append(command, format:getFlags()) + if options.write_filename_on_metadata then + append(command, get_metadata_flags()) + end + if format.acceptsBitrate then + if options.target_filesize > 0 then + local length = endTime - startTime + local video_bitrate, audio_bitrate = calculate_bitrate(supported_active_tracks, format, length) + if video_bitrate then + append(command, { + "--ovcopts-add=b=" .. tostring(video_bitrate) .. "k" + }) + end + if audio_bitrate then + append(command, { + "--oacopts-add=b=" .. tostring(audio_bitrate) .. "k" + }) + end + if options.strict_filesize_constraint then + local type = format.videoCodec ~= "" and "ovc" or "oac" + append(command, { + "--" .. tostring(type) .. "opts-add=minrate=" .. tostring(bitrate) .. "k", + "--" .. tostring(type) .. "opts-add=maxrate=" .. tostring(bitrate) .. "k" + }) + end + else + local type = format.videoCodec ~= "" and "ovc" or "oac" + append(command, { + "--" .. tostring(type) .. "opts-add=b=0" + }) + end + end + for token in string.gmatch(options.additional_flags, "[^%s]+") do + command[#command + 1] = token + end + if not options.strict_filesize_constraint then + for token in string.gmatch(options.non_strict_additional_flags, "[^%s]+") do + command[#command + 1] = token + end + if options.crf >= 0 then + append(command, { + "--ovcopts-add=crf=" .. tostring(options.crf) + }) + end + end + local dir = "" + if is_stream then + dir = parse_directory("~") + else + local _ + dir, _ = utils.split_path(path) + end + if options.output_directory ~= "" then + dir = parse_directory(options.output_directory) + end + local formatted_filename = format_filename(originalStartTime, originalEndTime, format) + local out_path = utils.join_path(dir, formatted_filename) + append(command, { + "--o=" .. tostring(out_path) + }) + emit_event("encode-started") + if options.twopass and format.supportsTwopass and not is_stream then + local first_pass_cmdline + do + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #command do + local arg = command[_index_0] + _accum_0[_len_0] = arg + _len_0 = _len_0 + 1 + end + first_pass_cmdline = _accum_0 + end + append(first_pass_cmdline, { + "--ovcopts-add=flags=+pass1" + }) + message("Starting first pass...") + msg.verbose("First-pass command line: ", table.concat(first_pass_cmdline, " ")) + local res = run_subprocess({ + args = first_pass_cmdline, + cancellable = false + }) + if not res then + message("First pass failed! Check the logs for details.") + emit_event("encode-finished", "fail") + return + end + append(command, { + "--ovcopts-add=flags=+pass2" + }) + if format.videoCodec == "libvpx" then + msg.verbose("Patching libvpx pass log file...") + vp8_patch_logfile(get_pass_logfile_path(out_path), endTime - startTime) + end + end + command = format:postCommandModifier(command, region, startTime, endTime) + msg.info("Encoding to", out_path) + msg.verbose("Command line:", table.concat(command, " ")) + if options.run_detached then + message("Started encode, process was detached.") + return utils.subprocess_detached({ + args = command + }) + else + local res = false + if not should_display_progress() then + message("Started encode...") + res = run_subprocess({ + args = command, + cancellable = false + }) + else + local ewp = EncodeWithProgress(startTime, endTime) + res = ewp:startEncode(command) + end + if res then + message("Encoded successfully! Saved to\\N" .. tostring(bold(out_path))) + emit_event("encode-finished", "success") + else + message("Encode failed! Check the logs for details.") + emit_event("encode-finished", "fail") + end + os.remove(get_pass_logfile_path(out_path)) + if is_temporary then + return os.remove(path) + end + end +end +local CropPage +do + local _class_0 + local _parent_0 = Page + local _base_0 = { + reset = function(self) + local dimensions = get_video_dimensions() + local xa, ya + do + local _obj_0 = dimensions.top_left + xa, ya = _obj_0.x, _obj_0.y + end + self.pointA:set_from_screen(xa, ya) + local xb, yb + do + local _obj_0 = dimensions.bottom_right + xb, yb = _obj_0.x, _obj_0.y + end + self.pointB:set_from_screen(xb, yb) + if self.visible then + return self:draw() + end + end, + setPointA = function(self) + local posX, posY = mp.get_mouse_pos() + self.pointA:set_from_screen(posX, posY) + if self.visible then + return self:draw() + end + end, + setPointB = function(self) + local posX, posY = mp.get_mouse_pos() + self.pointB:set_from_screen(posX, posY) + if self.visible then + return self:draw() + end + end, + cancel = function(self) + self:hide() + return self.callback(false, nil) + end, + finish = function(self) + local region = Region() + region:set_from_points(self.pointA, self.pointB) + self:hide() + return self.callback(true, region) + end, + draw_box = function(self, ass) + local region = Region() + region:set_from_points(self.pointA:to_screen(), self.pointB:to_screen()) + local d = get_video_dimensions() + ass:new_event() + ass:append("{\\an7}") + ass:pos(0, 0) + ass:append('{\\bord0}') + ass:append('{\\shad0}') + ass:append('{\\c&H000000&}') + ass:append('{\\alpha&H77}') + ass:draw_start() + ass:rect_cw(d.top_left.x, d.top_left.y, region.x, region.y + region.h) + ass:rect_cw(region.x, d.top_left.y, d.bottom_right.x, region.y) + ass:rect_cw(d.top_left.x, region.y + region.h, region.x + region.w, d.bottom_right.y) + ass:rect_cw(region.x + region.w, region.y, d.bottom_right.x, d.bottom_right.y) + return ass:draw_stop() + end, + draw = function(self) + local window = { } + window.w, window.h = mp.get_osd_size() + local ass = assdraw.ass_new() + self:draw_box(ass) + ass:new_event() + self:setup_text(ass) + ass:append(tostring(bold('Crop:')) .. "\\N") + ass:append(tostring(bold('1:')) .. " change point A (" .. tostring(self.pointA.x) .. ", " .. tostring(self.pointA.y) .. ")\\N") + ass:append(tostring(bold('2:')) .. " change point B (" .. tostring(self.pointB.x) .. ", " .. tostring(self.pointB.y) .. ")\\N") + ass:append(tostring(bold('r:')) .. " reset to whole screen\\N") + ass:append(tostring(bold('ESC:')) .. " cancel crop\\N") + local width, height = math.abs(self.pointA.x - self.pointB.x), math.abs(self.pointA.y - self.pointB.y) + ass:append(tostring(bold('ENTER:')) .. " confirm crop (" .. tostring(width) .. "x" .. tostring(height) .. ")\\N") + return mp.set_osd_ass(window.w, window.h, ass.text) + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, callback, region) + self.pointA = VideoPoint() + self.pointB = VideoPoint() + self.keybinds = { + ["1"] = (function() + local _base_1 = self + local _fn_0 = _base_1.setPointA + return function(...) + return _fn_0(_base_1, ...) + end + end)(), + ["2"] = (function() + local _base_1 = self + local _fn_0 = _base_1.setPointB + return function(...) + return _fn_0(_base_1, ...) + end + end)(), + ["r"] = (function() + local _base_1 = self + local _fn_0 = _base_1.reset + return function(...) + return _fn_0(_base_1, ...) + end + end)(), + ["ESC"] = (function() + local _base_1 = self + local _fn_0 = _base_1.cancel + return function(...) + return _fn_0(_base_1, ...) + end + end)(), + ["ENTER"] = (function() + local _base_1 = self + local _fn_0 = _base_1.finish + return function(...) + return _fn_0(_base_1, ...) + end + end)() + } + self:reset() + self.callback = callback + if region and region:is_valid() then + self.pointA.x = region.x + self.pointA.y = region.y + self.pointB.x = region.x + region.w + self.pointB.y = region.y + region.h + end + end, + __base = _base_0, + __name = "CropPage", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + CropPage = _class_0 +end +local Option +do + local _class_0 + local _base_0 = { + hasPrevious = function(self) + local _exp_0 = self.optType + if "bool" == _exp_0 then + return true + elseif "int" == _exp_0 then + if self.opts.min then + return self.value > self.opts.min + else + return true + end + elseif "list" == _exp_0 then + return self.value > 1 + end + end, + hasNext = function(self) + local _exp_0 = self.optType + if "bool" == _exp_0 then + return true + elseif "int" == _exp_0 then + if self.opts.max then + return self.value < self.opts.max + else + return true + end + elseif "list" == _exp_0 then + return self.value < #self.opts.possibleValues + end + end, + leftKey = function(self) + local _exp_0 = self.optType + if "bool" == _exp_0 then + self.value = not self.value + elseif "int" == _exp_0 then + self.value = self.value - self.opts.step + if self.opts.min and self.opts.min > self.value then + self.value = self.opts.min + end + elseif "list" == _exp_0 then + if self.value > 1 then + self.value = self.value - 1 + end + end + end, + rightKey = function(self) + local _exp_0 = self.optType + if "bool" == _exp_0 then + self.value = not self.value + elseif "int" == _exp_0 then + self.value = self.value + self.opts.step + if self.opts.max and self.opts.max < self.value then + self.value = self.opts.max + end + elseif "list" == _exp_0 then + if self.value < #self.opts.possibleValues then + self.value = self.value + 1 + end + end + end, + getValue = function(self) + local _exp_0 = self.optType + if "bool" == _exp_0 then + return self.value + elseif "int" == _exp_0 then + return self.value + elseif "list" == _exp_0 then + local value, _ + do + local _obj_0 = self.opts.possibleValues[self.value] + value, _ = _obj_0[1], _obj_0[2] + end + return value + end + end, + setValue = function(self, value) + local _exp_0 = self.optType + if "bool" == _exp_0 then + self.value = value + elseif "int" == _exp_0 then + self.value = value + elseif "list" == _exp_0 then + local set = false + for i, possiblePair in ipairs(self.opts.possibleValues) do + local possibleValue, _ + possibleValue, _ = possiblePair[1], possiblePair[2] + if possibleValue == value then + set = true + self.value = i + break + end + end + if not set then + return msg.warn("Tried to set invalid value " .. tostring(value) .. " to " .. tostring(self.displayText) .. " option.") + end + end + end, + getDisplayValue = function(self) + local _exp_0 = self.optType + if "bool" == _exp_0 then + return self.value and "yes" or "no" + elseif "int" == _exp_0 then + if self.opts.altDisplayNames and self.opts.altDisplayNames[self.value] then + return self.opts.altDisplayNames[self.value] + else + return tostring(self.value) + end + elseif "list" == _exp_0 then + local value, displayValue + do + local _obj_0 = self.opts.possibleValues[self.value] + value, displayValue = _obj_0[1], _obj_0[2] + end + return displayValue or value + end + end, + draw = function(self, ass, selected) + if selected then + ass:append(tostring(bold(self.displayText)) .. ": ") + else + ass:append(tostring(self.displayText) .. ": ") + end + if self:hasPrevious() then + ass:append("◀ ") + end + ass:append(self:getDisplayValue()) + if self:hasNext() then + ass:append(" ▶") + end + return ass:append("\\N") + end, + optVisible = function(self) + if self.visibleCheckFn == nil then + return true + else + return self.visibleCheckFn() + end + end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function(self, optType, displayText, value, opts, visibleCheckFn) + self.optType = optType + self.displayText = displayText + self.opts = opts + self.value = 1 + self.visibleCheckFn = visibleCheckFn + return self:setValue(value) + end, + __base = _base_0, + __name = "Option" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + Option = _class_0 +end +local EncodeOptionsPage +do + local _class_0 + local _parent_0 = Page + local _base_0 = { + getCurrentOption = function(self) + return self.options[self.currentOption][2] + end, + leftKey = function(self) + (self:getCurrentOption()):leftKey() + return self:draw() + end, + rightKey = function(self) + (self:getCurrentOption()):rightKey() + return self:draw() + end, + prevOpt = function(self) + for i = self.currentOption - 1, 1, -1 do + if self.options[i][2]:optVisible() then + self.currentOption = i + break + end + end + return self:draw() + end, + nextOpt = function(self) + for i = self.currentOption + 1, #self.options do + if self.options[i][2]:optVisible() then + self.currentOption = i + break + end + end + return self:draw() + end, + confirmOpts = function(self) + for _, optPair in ipairs(self.options) do + local optName, opt + optName, opt = optPair[1], optPair[2] + options[optName] = opt:getValue() + end + self:hide() + return self.callback(true) + end, + cancelOpts = function(self) + self:hide() + return self.callback(false) + end, + draw = function(self) + local window_w, window_h = mp.get_osd_size() + local ass = assdraw.ass_new() + ass:new_event() + self:setup_text(ass) + ass:append(tostring(bold('Options:')) .. "\\N\\N") + for i, optPair in ipairs(self.options) do + local opt = optPair[2] + if opt:optVisible() then + opt:draw(ass, self.currentOption == i) + end + end + ass:append("\\N▲ / ▼: navigate\\N") + ass:append(tostring(bold('ENTER:')) .. " confirm options\\N") + ass:append(tostring(bold('ESC:')) .. " cancel\\N") + return mp.set_osd_ass(window_w, window_h, ass.text) + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, callback) + self.callback = callback + self.currentOption = 1 + local scaleHeightOpts = { + possibleValues = { + { + -1, + "no" + }, + { + 144 + }, + { + 240 + }, + { + 360 + }, + { + 480 + }, + { + 540 + }, + { + 720 + }, + { + 1080 + }, + { + 1440 + }, + { + 2160 + } + } + } + local filesizeOpts = { + step = 250, + min = 0, + altDisplayNames = { + [0] = "0 (constant quality)" + } + } + local crfOpts = { + step = 1, + min = -1, + altDisplayNames = { + [-1] = "disabled" + } + } + local fpsOpts = { + possibleValues = { + { + -1, + "source" + }, + { + 15 + }, + { + 24 + }, + { + 30 + }, + { + 48 + }, + { + 50 + }, + { + 60 + }, + { + 120 + }, + { + 240 + } + } + } + local formatIds = { + "webm-vp8", + "webm-vp9", + "mp4", + "mp4-nvenc", + "raw", + "mp3", + "gif" + } + local formatOpts = { + possibleValues = (function() + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #formatIds do + local fId = formatIds[_index_0] + _accum_0[_len_0] = { + fId, + formats[fId].displayName + } + _len_0 = _len_0 + 1 + end + return _accum_0 + end)() + } + local gifDitherOpts = { + possibleValues = { + { + 0, + "bayer_scale 0" + }, + { + 1, + "bayer_scale 1" + }, + { + 2, + "bayer_scale 2" + }, + { + 3, + "bayer_scale 3" + }, + { + 4, + "bayer_scale 4" + }, + { + 5, + "bayer_scale 5" + }, + { + 6, + "sierra2_4a" + } + } + } + self.options = { + { + "output_format", + Option("list", "Output Format", options.output_format, formatOpts) + }, + { + "twopass", + Option("bool", "Two Pass", options.twopass) + }, + { + "apply_current_filters", + Option("bool", "Apply Current Video Filters", options.apply_current_filters) + }, + { + "scale_height", + Option("list", "Scale Height", options.scale_height, scaleHeightOpts) + }, + { + "strict_filesize_constraint", + Option("bool", "Strict Filesize Constraint", options.strict_filesize_constraint) + }, + { + "write_filename_on_metadata", + Option("bool", "Write Filename on Metadata", options.write_filename_on_metadata) + }, + { + "target_filesize", + Option("int", "Target Filesize", options.target_filesize, filesizeOpts) + }, + { + "crf", + Option("int", "CRF", options.crf, crfOpts) + }, + { + "fps", + Option("list", "FPS", options.fps, fpsOpts) + }, + { + "gif_dither", + Option("list", "GIF Dither Type", options.gif_dither, gifDitherOpts, function() + return self.options[1][2]:getValue() == "gif" + end) + }, + { + "force_square_pixels", + Option("bool", "Force Square Pixels", options.force_square_pixels) + } + } + self.keybinds = { + ["LEFT"] = (function() + local _base_1 = self + local _fn_0 = _base_1.leftKey + return function(...) + return _fn_0(_base_1, ...) + end + end)(), + ["RIGHT"] = (function() + local _base_1 = self + local _fn_0 = _base_1.rightKey + return function(...) + return _fn_0(_base_1, ...) + end + end)(), + ["UP"] = (function() + local _base_1 = self + local _fn_0 = _base_1.prevOpt + return function(...) + return _fn_0(_base_1, ...) + end + end)(), + ["DOWN"] = (function() + local _base_1 = self + local _fn_0 = _base_1.nextOpt + return function(...) + return _fn_0(_base_1, ...) + end + end)(), + ["ENTER"] = (function() + local _base_1 = self + local _fn_0 = _base_1.confirmOpts + return function(...) + return _fn_0(_base_1, ...) + end + end)(), + ["ESC"] = (function() + local _base_1 = self + local _fn_0 = _base_1.cancelOpts + return function(...) + return _fn_0(_base_1, ...) + end + end)() + } + end, + __base = _base_0, + __name = "EncodeOptionsPage", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + EncodeOptionsPage = _class_0 +end +local PreviewPage +do + local _class_0 + local _parent_0 = Page + local _base_0 = { + prepare = function(self) + local vf = mp.get_property_native("vf") + vf[#vf + 1] = { + name = "sub" + } + if self.region:is_valid() then + vf[#vf + 1] = { + name = "crop", + params = { + w = tostring(self.region.w), + h = tostring(self.region.h), + x = tostring(self.region.x), + y = tostring(self.region.y) + } + } + end + mp.set_property_native("vf", vf) + if self.startTime > -1 and self.endTime > -1 then + mp.set_property_native("ab-loop-a", self.startTime) + mp.set_property_native("ab-loop-b", self.endTime) + mp.set_property_native("time-pos", self.startTime) + end + return mp.set_property_native("pause", false) + end, + dispose = function(self) + mp.set_property("ab-loop-a", "no") + mp.set_property("ab-loop-b", "no") + for prop, value in pairs(self.originalProperties) do + mp.set_property_native(prop, value) + end + end, + draw = function(self) + local window_w, window_h = mp.get_osd_size() + local ass = assdraw.ass_new() + ass:new_event() + self:setup_text(ass) + ass:append("Press " .. tostring(bold('ESC')) .. " to exit preview.\\N") + return mp.set_osd_ass(window_w, window_h, ass.text) + end, + cancel = function(self) + self:hide() + return self.callback() + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, callback, region, startTime, endTime) + self.callback = callback + self.originalProperties = { + ["vf"] = mp.get_property_native("vf"), + ["time-pos"] = mp.get_property_native("time-pos"), + ["pause"] = mp.get_property_native("pause") + } + self.keybinds = { + ["ESC"] = (function() + local _base_1 = self + local _fn_0 = _base_1.cancel + return function(...) + return _fn_0(_base_1, ...) + end + end)() + } + self.region = region + self.startTime = startTime + self.endTime = endTime + self.isLoop = false + end, + __base = _base_0, + __name = "PreviewPage", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + PreviewPage = _class_0 +end +local MainPage +do + local _class_0 + local _parent_0 = Page + local _base_0 = { + setStartTime = function(self) + self.startTime = mp.get_property_number("time-pos") + if self.visible then + self:clear() + return self:draw() + end + end, + setEndTime = function(self) + self.endTime = mp.get_property_number("time-pos") + if self.visible then + self:clear() + return self:draw() + end + end, + setupStartAndEndTimes = function(self) + if mp.get_property_native("duration") then + self.startTime = 0 + self.endTime = mp.get_property_native("duration") + else + self.startTime = -1 + self.endTime = -1 + end + if self.visible then + self:clear() + return self:draw() + end + end, + draw = function(self) + local window_w, window_h = mp.get_osd_size() + local ass = assdraw.ass_new() + ass:new_event() + self:setup_text(ass) + ass:append(tostring(bold('WebM maker')) .. "\\N\\N") + ass:append(tostring(bold('c:')) .. " crop\\N") + ass:append(tostring(bold('1:')) .. " set start time (current is " .. tostring(seconds_to_time_string(self.startTime)) .. ")\\N") + ass:append(tostring(bold('2:')) .. " set end time (current is " .. tostring(seconds_to_time_string(self.endTime)) .. ")\\N") + ass:append(tostring(bold('o:')) .. " change encode options\\N") + ass:append(tostring(bold('p:')) .. " preview\\N") + ass:append(tostring(bold('e:')) .. " encode\\N\\N") + ass:append(tostring(bold('ESC:')) .. " close\\N") + return mp.set_osd_ass(window_w, window_h, ass.text) + end, + show = function(self) + _class_0.__parent.show(self) + return emit_event("show-main-page") + end, + onUpdateCropRegion = function(self, updated, newRegion) + if updated then + self.region = newRegion + end + return self:show() + end, + crop = function(self) + self:hide() + local cropPage = CropPage((function() + local _base_1 = self + local _fn_0 = _base_1.onUpdateCropRegion + return function(...) + return _fn_0(_base_1, ...) + end + end)(), self.region) + return cropPage:show() + end, + onOptionsChanged = function(self, updated) + return self:show() + end, + changeOptions = function(self) + self:hide() + local encodeOptsPage = EncodeOptionsPage((function() + local _base_1 = self + local _fn_0 = _base_1.onOptionsChanged + return function(...) + return _fn_0(_base_1, ...) + end + end)()) + return encodeOptsPage:show() + end, + onPreviewEnded = function(self) + return self:show() + end, + preview = function(self) + self:hide() + local previewPage = PreviewPage((function() + local _base_1 = self + local _fn_0 = _base_1.onPreviewEnded + return function(...) + return _fn_0(_base_1, ...) + end + end)(), self.region, self.startTime, self.endTime) + return previewPage:show() + end, + encode = function(self) + self:hide() + if self.startTime < 0 then + message("No start time, aborting") + return + end + if self.endTime < 0 then + message("No end time, aborting") + return + end + if self.startTime >= self.endTime then + message("Start time is ahead of end time, aborting") + return + end + return encode(self.region, self.startTime, self.endTime) + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self) + self.keybinds = { + ["c"] = (function() + local _base_1 = self + local _fn_0 = _base_1.crop + return function(...) + return _fn_0(_base_1, ...) + end + end)(), + ["1"] = (function() + local _base_1 = self + local _fn_0 = _base_1.setStartTime + return function(...) + return _fn_0(_base_1, ...) + end + end)(), + ["2"] = (function() + local _base_1 = self + local _fn_0 = _base_1.setEndTime + return function(...) + return _fn_0(_base_1, ...) + end + end)(), + ["o"] = (function() + local _base_1 = self + local _fn_0 = _base_1.changeOptions + return function(...) + return _fn_0(_base_1, ...) + end + end)(), + ["p"] = (function() + local _base_1 = self + local _fn_0 = _base_1.preview + return function(...) + return _fn_0(_base_1, ...) + end + end)(), + ["e"] = (function() + local _base_1 = self + local _fn_0 = _base_1.encode + return function(...) + return _fn_0(_base_1, ...) + end + end)(), + ["ESC"] = (function() + local _base_1 = self + local _fn_0 = _base_1.hide + return function(...) + return _fn_0(_base_1, ...) + end + end)() + } + self.startTime = -1 + self.endTime = -1 + self.region = Region() + end, + __base = _base_0, + __name = "MainPage", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + MainPage = _class_0 +end +monitor_dimensions() +local mainPage = MainPage() +mp.add_key_binding(options.keybind, "display-webm-encoder", (function() + local _base_0 = mainPage + local _fn_0 = _base_0.show + return function(...) + return _fn_0(_base_0, ...) + end +end)(), { + repeatable = false +}) +mp.register_event("file-loaded", (function() + local _base_0 = mainPage + local _fn_0 = _base_0.setupStartAndEndTimes + return function(...) + return _fn_0(_base_0, ...) + end +end)()) +msg.verbose("Loaded mpv-webm script!") +return emit_event("script-loaded")