replace immersive with animecard
This commit is contained in:
parent
0c7677b6ab
commit
7df9682378
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -16,6 +16,3 @@
|
|||||||
[submodule "scripts/autosubsync-mpv"]
|
[submodule "scripts/autosubsync-mpv"]
|
||||||
path = scripts/autosubsync-mpv
|
path = scripts/autosubsync-mpv
|
||||||
url = git@github.com:Ajatt-Tools/autosubsync-mpv.git
|
url = git@github.com:Ajatt-Tools/autosubsync-mpv.git
|
||||||
[submodule "scripts/immersive"]
|
|
||||||
path = scripts/immersive
|
|
||||||
url = git@github.com:Ben-Kerman/immersive.git
|
|
||||||
|
@ -1 +0,0 @@
|
|||||||
Subproject commit 6cc573ec7dadaec4acca58b7e1b91f86c8e54e95
|
|
@ -1,71 +0,0 @@
|
|||||||
# this line is here so the file is encoded as UTF−8
|
|
||||||
# do not delete it unless you know what that means
|
|
||||||
|
|
||||||
# example dictionary config for JMdict
|
|
||||||
|
|
||||||
[JMdict]
|
|
||||||
location=/home/sudacode/Documents/jmdict/
|
|
||||||
type=yomichan
|
|
||||||
|
|
||||||
|
|
||||||
# Only the two entries above are strictly required for each dictionary.
|
|
||||||
# optional entries:
|
|
||||||
|
|
||||||
# cf. doc/lookup-transformations.md, empty by default:
|
|
||||||
#transformations=deinflect-japanese,deinflect-migaku(ja.json),kana
|
|
||||||
|
|
||||||
# cf. doc/dictionaries.md for details
|
|
||||||
#preload=<...>
|
|
||||||
#insert_cjk_breaks=no
|
|
||||||
exporter=default
|
|
||||||
quick_def_template={{readings:::・}}{{variants:【:】:・}}: {{definitions:::; }}
|
|
||||||
|
|
||||||
export:digits=0123456789
|
|
||||||
export:reading_template={{reading}}{{variants:【:】:・}}
|
|
||||||
export:definition_template={{tags:<span style="font-size\: 0.8em">:</span><br>:, }}{{num}}. {{keywords:::; }}
|
|
||||||
export:template={{readings[1]}}:{{readings[2:] (:): }}<br>{{definitions:::<br>}}
|
|
||||||
export:use_single_template=false
|
|
||||||
export:single_template={{readings[1]}}:{{readings[2:] (:): }}<br>{{definitions:::<br>}}
|
|
||||||
|
|
||||||
# --------------------
|
|
||||||
|
|
||||||
# example config for Daijirin, probably usable for any EPWING exported by
|
|
||||||
# yomichan-import
|
|
||||||
|
|
||||||
# also set 'definition_substitutions=\<br><\n' in your target config
|
|
||||||
# to add HTML line breaks to the definition
|
|
||||||
|
|
||||||
#[大辞林]
|
|
||||||
#location=<set to dictionary directory>
|
|
||||||
#type=yomichan
|
|
||||||
|
|
||||||
# to make text more readable in mpv
|
|
||||||
#insert_cjk_breaks=yes
|
|
||||||
|
|
||||||
# there is only one definition for EPWINGs exported to Yomichan
|
|
||||||
# and it already includes the word
|
|
||||||
#quick_def_template={{definitions}}
|
|
||||||
|
|
||||||
# same as for quick_def_template
|
|
||||||
#export:definition_template={{keywords}}
|
|
||||||
#export:template={{definitions}}
|
|
||||||
|
|
||||||
# these aren't needed since the reading and word are already in the definition
|
|
||||||
#export:reading_template=
|
|
||||||
#export:use_single_template=no
|
|
||||||
|
|
||||||
# --------------------
|
|
||||||
|
|
||||||
# example config for a Migaku dictionary
|
|
||||||
|
|
||||||
#[Migaku Dictionary]
|
|
||||||
#location=<set to path to dictionary JSON file>
|
|
||||||
#type=migaku
|
|
||||||
#exporter=default
|
|
||||||
#quick_def_template={{definitions}}
|
|
||||||
#export:template=[[
|
|
||||||
#{{terms[1]}}{{terms[2:] (:):, }}:<br>
|
|
||||||
#{{altterms::<br>:, }}{{pronunciations::<br>:, }}{{positions::<br>:, }}
|
|
||||||
#{{definition}}
|
|
||||||
#{{examples:::, }}
|
|
||||||
#]]
|
|
@ -1,4 +0,0 @@
|
|||||||
open_global_menu=ctrl+i
|
|
||||||
show_dict_target=ctrl+I
|
|
||||||
export_active_line_instant=ctrl+e
|
|
||||||
export_active_line_menua=ctrl+E
|
|
@ -1,12 +0,0 @@
|
|||||||
# this line is here so the file is encoded as UTF−8
|
|
||||||
# do not delete it unless you know what that means
|
|
||||||
|
|
||||||
## streamed video; something similar should work for any platform youtube-dl supports
|
|
||||||
#[youtube]
|
|
||||||
#title={{media_title}}
|
|
||||||
#keywords=youtu be
|
|
||||||
#
|
|
||||||
## local video file
|
|
||||||
#[kaguya-sama]
|
|
||||||
#title=かぐや様は告らせたい
|
|
||||||
#keywords=kaguya sama kokurasetai
|
|
@ -1,143 +0,0 @@
|
|||||||
# this line is here so the file is encoded as UTF−8
|
|
||||||
# do not delete it unless you know what that means
|
|
||||||
|
|
||||||
# Global entries serve as the basis for all other styles.
|
|
||||||
#align =5
|
|
||||||
#bold =<from mpv property 'osd-bold'>
|
|
||||||
#italic =<from mpv property 'osd-italic'>
|
|
||||||
#underline =no
|
|
||||||
#strikeout =no
|
|
||||||
#border =<from mpv property 'osd-border-size'>
|
|
||||||
#border_x =<from mpv property 'osd-border-size'>
|
|
||||||
#border_y =<from mpv property 'osd-border-size'>
|
|
||||||
#shadow =<from mpv property 'osd-shadow-offset'>
|
|
||||||
#shadow_x =<from mpv property 'osd-shadow-offset'>
|
|
||||||
#shadow_y =<from mpv property 'osd-shadow-offset'>
|
|
||||||
#blur =<from mpv property 'osd-blur'>
|
|
||||||
#font_name =<from mpv property 'osd-font'>
|
|
||||||
#font_size =30
|
|
||||||
#letter_spacing =<from mpv property 'osd-spacing'>
|
|
||||||
#primary_color =<from mpv property 'osd-color'>
|
|
||||||
#secondary_color =808080
|
|
||||||
#border_color =<from mpv property 'osd-border-color'>
|
|
||||||
#shadow_color =<from mpv property 'osd-shadow-color'>
|
|
||||||
#all_alpha =FF
|
|
||||||
#primary_alpha =<from mpv property 'osd-color'>
|
|
||||||
#secondary_alpha =00
|
|
||||||
#border_alpha =<from mpv property 'osd-border-color'>
|
|
||||||
#shadow_alpha =<from mpv property 'osd-shadow-color'>
|
|
||||||
|
|
||||||
# --------------------
|
|
||||||
|
|
||||||
# message log at the top right
|
|
||||||
#[messages]
|
|
||||||
#align=9
|
|
||||||
|
|
||||||
#[messages/fatal]
|
|
||||||
#bold=yes
|
|
||||||
#primary_color=5791F9
|
|
||||||
|
|
||||||
#[messages/error]
|
|
||||||
#primary_color=7A77F2
|
|
||||||
|
|
||||||
#[messages/warn]
|
|
||||||
#primary_color=66CCFF
|
|
||||||
|
|
||||||
#[messages/info]
|
|
||||||
# none
|
|
||||||
|
|
||||||
#[messages/verbose]
|
|
||||||
#primary_color=99CC99
|
|
||||||
|
|
||||||
#[messages/debug]
|
|
||||||
#primary_color=A09F93
|
|
||||||
|
|
||||||
#[messages/trace]
|
|
||||||
# none
|
|
||||||
|
|
||||||
# --------------------
|
|
||||||
|
|
||||||
# menu help ("Press h to show key bindings")
|
|
||||||
#[menu_help]
|
|
||||||
#align=7
|
|
||||||
|
|
||||||
# key bindings
|
|
||||||
#[menu_help/key]
|
|
||||||
#bold=yes
|
|
||||||
|
|
||||||
# top line of the menu help
|
|
||||||
#[menu_help/hint]
|
|
||||||
#italic=yes
|
|
||||||
|
|
||||||
# --------------------
|
|
||||||
|
|
||||||
# menu info (timings, active target, etc.)
|
|
||||||
#[menu_info]
|
|
||||||
#align=1
|
|
||||||
|
|
||||||
# description of an info item
|
|
||||||
#[menu_info/key]
|
|
||||||
#bold=yes
|
|
||||||
|
|
||||||
# unset/unknown/automatically generated values
|
|
||||||
#[menu_info/unset]
|
|
||||||
#italic=yes
|
|
||||||
|
|
||||||
# --------------------
|
|
||||||
|
|
||||||
# line selection
|
|
||||||
#[line_select]
|
|
||||||
# none
|
|
||||||
|
|
||||||
# actively selected line
|
|
||||||
#[line_select/selection]
|
|
||||||
#bold=yes
|
|
||||||
#primary_color=FFD0D0
|
|
||||||
|
|
||||||
# --------------------
|
|
||||||
|
|
||||||
# text selection
|
|
||||||
# applied on top of line_select during target selection
|
|
||||||
#[text_select]
|
|
||||||
# none
|
|
||||||
|
|
||||||
# selected text
|
|
||||||
#[text_select/selection]
|
|
||||||
#primary_color=FF8080
|
|
||||||
|
|
||||||
# --------------------
|
|
||||||
|
|
||||||
# Forvo audio selection
|
|
||||||
# applied on top of line_select
|
|
||||||
#[word_audio_select]
|
|
||||||
# none
|
|
||||||
|
|
||||||
# pronunciations that have not been loaded yet
|
|
||||||
#[word_audio_select/unloaded]
|
|
||||||
#primary_color=808080
|
|
||||||
|
|
||||||
# pronunciations that are currently loading
|
|
||||||
#[word_audio_select/loading]
|
|
||||||
#primary_color=8080FF
|
|
||||||
|
|
||||||
# pronunciations that are ready to play
|
|
||||||
#[word_audio_select/loaded]
|
|
||||||
# none
|
|
||||||
|
|
||||||
# --------------------
|
|
||||||
|
|
||||||
# overlay of selected subtitles during subtitle selection
|
|
||||||
#[selection_overlay]
|
|
||||||
#align=3
|
|
||||||
|
|
||||||
# --------------------
|
|
||||||
|
|
||||||
# overlay shown when Immersive is blocked, e.g. while importing dictionaries
|
|
||||||
#[info_overlay]
|
|
||||||
#align=1
|
|
||||||
|
|
||||||
|
|
||||||
# overlay for hiding the video during and after target selection
|
|
||||||
#[blackout]
|
|
||||||
#primary_color=<from mpv property 'background'>
|
|
||||||
#primary_alpha=<from mpv property 'background'>
|
|
@ -1,139 +0,0 @@
|
|||||||
# this line is here so the file is encoded as UTF−8
|
|
||||||
# do not delete it unless you know what that means
|
|
||||||
|
|
||||||
[target name]
|
|
||||||
# Anki profile the target will use
|
|
||||||
# Can be taken from the window title of the main Anki window or from the profile
|
|
||||||
# menu (Ctrl+Shift+P in Anki)
|
|
||||||
profile=sudacode
|
|
||||||
|
|
||||||
# Anki deck the target will use
|
|
||||||
# Subdecks use the same syntax as in Anki itself
|
|
||||||
# e.g. Root::Subdeck::Subsubdeck
|
|
||||||
deck=Minecraft
|
|
||||||
|
|
||||||
# note type the target will use
|
|
||||||
note_type=Lapis
|
|
||||||
|
|
||||||
# --------------------
|
|
||||||
|
|
||||||
# example field definitions
|
|
||||||
|
|
||||||
# uncomment (remove the #) and change so they fit your note type
|
|
||||||
# Anki field names go between 'field:' and '=', exactly as they are
|
|
||||||
# in Anki, including spaces.
|
|
||||||
# Template variables come after the '=', like in the examples.
|
|
||||||
|
|
||||||
#field:Front={{sentences}}
|
|
||||||
#field:Back={{definitions}}
|
|
||||||
#field:Word={{words[1]}}
|
|
||||||
#field:Words={{words::: }}
|
|
||||||
#field:Audio={{audio}}
|
|
||||||
#field:Word Audio={{word_audio}}
|
|
||||||
#field:Image={{image}}
|
|
||||||
|
|
||||||
field:SentenceAudio={{audio}}
|
|
||||||
field:Picture={{image}}
|
|
||||||
field:MiscInfo={{series_title}}
|
|
||||||
|
|
||||||
# Everything below this line has default values and is not required.
|
|
||||||
# It is recommended to change image/max_width or image/max_height in order to
|
|
||||||
# reduce file sizes, however.
|
|
||||||
# --------------------
|
|
||||||
|
|
||||||
# how export data will be added to existing notes
|
|
||||||
# allowed values: 'append', 'prepend', 'overwrite'
|
|
||||||
# overwrite replaces fields (but cf. template variable {{prev_content}})
|
|
||||||
add_mode=append
|
|
||||||
|
|
||||||
# template used for formatting notes within mpv
|
|
||||||
# when selecting which existing note to export to
|
|
||||||
#note_template={{type}}: {{id}}
|
|
||||||
|
|
||||||
# Anki media dir override, optional and normally derived from system default
|
|
||||||
# Must be an absolute path to the directory that encoded images/audio clips
|
|
||||||
# should be placed in (i.e., to collection.media).
|
|
||||||
#media_directory=<unset>
|
|
||||||
|
|
||||||
# space-separated list of tags that will be added to exported notes
|
|
||||||
#tags=immersive
|
|
||||||
|
|
||||||
# can be set dynamically using field template variables, e.g.:
|
|
||||||
#tags={{series_id}}
|
|
||||||
|
|
||||||
# --------------------
|
|
||||||
|
|
||||||
# substitutions to apply to the {{sentences}} variable
|
|
||||||
# for more information, see doc/card-export.md
|
|
||||||
#sentence_substitutions=[[
|
|
||||||
#<(.-%)
|
|
||||||
#<(.-%)
|
|
||||||
#]]
|
|
||||||
|
|
||||||
# same as sentence_substitutions but for {{definitions}}
|
|
||||||
#definition_substitutions=
|
|
||||||
|
|
||||||
# --------------------
|
|
||||||
|
|
||||||
# audio clip file extension
|
|
||||||
# unrelated to the format used, but should match it (especially on Windows)
|
|
||||||
audio/extension=mp3
|
|
||||||
|
|
||||||
# audio container format
|
|
||||||
# e.g. 'matroska' (MKV/MKA), 'ogg', 'mp3'
|
|
||||||
audio/format=mp3
|
|
||||||
|
|
||||||
# audio codec
|
|
||||||
# e.g. 'libopus' (NOT 'opus'), 'aac', 'vorbis', 'libmp3lame' (MP3)
|
|
||||||
audio/codec=libmp3lame
|
|
||||||
|
|
||||||
# audio bitrate
|
|
||||||
# Uses the same syntax as mpv/ffmpeg bitrates.
|
|
||||||
# Sensible values are 32ki-64ki for libopus and 128ki for AAC and MP3.
|
|
||||||
audio/bitrate=128ki
|
|
||||||
|
|
||||||
# how many seconds of padding to include before the start of audio clips
|
|
||||||
#audio/pad_start=0.1
|
|
||||||
|
|
||||||
# same as above, but after the end of the clip
|
|
||||||
#audio/pad_end=0.1
|
|
||||||
|
|
||||||
# --------------------
|
|
||||||
|
|
||||||
# image file extension
|
|
||||||
image/extension=webp
|
|
||||||
|
|
||||||
# image codec
|
|
||||||
# supported values are 'mjpeg' (JPG), 'libwebp' (WebP), and 'png'
|
|
||||||
# Technically, any codec that works with ffmpeg's image2 format can be used.
|
|
||||||
image/codec=libwebp
|
|
||||||
|
|
||||||
# maximum image width/height
|
|
||||||
# If one option is set to a negative value aspect ratio will be preserved.
|
|
||||||
# If both are negative the video's resolution will be used.
|
|
||||||
#image/max_width=-1
|
|
||||||
#image/max_height=-1
|
|
||||||
|
|
||||||
# quality of JPG (mjpeg) images
|
|
||||||
# valid range: 1-69
|
|
||||||
# lower is better (but files will be larger)
|
|
||||||
# Values above 5-10 result in noticeable artifacting.
|
|
||||||
image/jpeg/qscale=5
|
|
||||||
|
|
||||||
# whether to use lossless compression for WebP
|
|
||||||
image/webp/lossless=yes
|
|
||||||
|
|
||||||
# libwebp quality factor
|
|
||||||
# valid range: 0-100
|
|
||||||
# higher is better
|
|
||||||
image/webp/quality=90
|
|
||||||
|
|
||||||
# libwebp compression level
|
|
||||||
# valid range: 0-6
|
|
||||||
# Higher values result in better compression but take longer to encode.
|
|
||||||
image/webp/compression=4
|
|
||||||
|
|
||||||
# PNG compression level
|
|
||||||
# valid range: 0-9
|
|
||||||
# higher is better
|
|
||||||
#image/png/compression=9
|
|
@ -1,82 +0,0 @@
|
|||||||
# this line is here so the file is encoded as UTF−8
|
|
||||||
# do not delete it unless you know what that means
|
|
||||||
|
|
||||||
# the mpv executable to use for audio previews and encoding
|
|
||||||
# if unset (default), the executable of the current process will be used
|
|
||||||
#mpv_executable=<...>
|
|
||||||
|
|
||||||
# if set to 'yes', load dictionaries when mpv starts
|
|
||||||
preload_dictionaries=no
|
|
||||||
|
|
||||||
# show the dictionary loading overlay when loading dicts at startup
|
|
||||||
startup_dict_overlay=yes
|
|
||||||
|
|
||||||
# maximum number of target words per card
|
|
||||||
# set to 0 to disable limit
|
|
||||||
#max_targets=1
|
|
||||||
|
|
||||||
# always show minutes when displaying times,
|
|
||||||
# even if playback has not reached 01:00 yet
|
|
||||||
#always_show_minutes=yes
|
|
||||||
|
|
||||||
# black out the screen during and after target selection
|
|
||||||
#target_select_blackout=yes
|
|
||||||
|
|
||||||
# black out the screen when looking up words from the active subtitle
|
|
||||||
#active_sub_blackout=yes
|
|
||||||
|
|
||||||
# language code to use when searching Forvo audio
|
|
||||||
forvo_language=ja
|
|
||||||
|
|
||||||
# Automatically load Forvo audio instead of waiting until attempting to play it.
|
|
||||||
forvo_preload_audio=no
|
|
||||||
|
|
||||||
# download mp3 files from Forvo instead of Ogg/Vorbis
|
|
||||||
forvo_prefer_mp3=yes
|
|
||||||
|
|
||||||
# prefix for Forvo filenames in the Anki media directory
|
|
||||||
# Files will be named '<prefix>-<word>.<extension>'.
|
|
||||||
#forvo_prefix=word_audio
|
|
||||||
|
|
||||||
# reencode Forvo audio files since they are unnecessarily large
|
|
||||||
forvo_reencode=yes
|
|
||||||
|
|
||||||
# Forvo audio encoding options
|
|
||||||
# These behave like the corresponding options in target configs.
|
|
||||||
forvo_extension=mp3
|
|
||||||
forvo_format=mp3
|
|
||||||
forvo_codec=libmp3lame
|
|
||||||
forvo_bitrate=128ki
|
|
||||||
|
|
||||||
# AnkiConnect host and port
|
|
||||||
ankiconnect_host=localhost
|
|
||||||
ankiconnect_port=8765
|
|
||||||
|
|
||||||
# Windows clipboard copy mode
|
|
||||||
# "exact" takes longer (200ms-1s), but preserves the text exactly,
|
|
||||||
# i.e. it also copies line breaks.
|
|
||||||
# "quick" is much faster (<50ms), but might not copy the text
|
|
||||||
# with 100% accuracy. This method sometimes causes encoding issues
|
|
||||||
# and is known to sporadically copy corrupted text.
|
|
||||||
#windows_copy_mode=exact
|
|
||||||
|
|
||||||
# enable automatic subtitle copying by default
|
|
||||||
#enable_autocopy=no
|
|
||||||
|
|
||||||
# make subtitle autoselect toggle global instead of
|
|
||||||
# being tied to each subtitle select menu
|
|
||||||
#global_autoselect=yes
|
|
||||||
|
|
||||||
# enable subtitle autoselect by default
|
|
||||||
#enable_autoselect=yes
|
|
||||||
|
|
||||||
# same as above but for the menu help toggle
|
|
||||||
#global_help=yes
|
|
||||||
#enable_help=no
|
|
||||||
|
|
||||||
# enable screenshots by default
|
|
||||||
take_screenshots=yes
|
|
||||||
|
|
||||||
# Hide the menu info at the bottom left if the help overlay is active.
|
|
||||||
# Useful if the two collide due to large font sizes.
|
|
||||||
#hide_infos_if_help_active=no
|
|
496
scripts/animecards.lua
Normal file
496
scripts/animecards.lua
Normal file
@ -0,0 +1,496 @@
|
|||||||
|
------------- Instructions -------------
|
||||||
|
-- -- Video Demonstration: https://www.youtube.com/watch?v=M4t7HYS73ZQ
|
||||||
|
-- IF USING WEBSOCKET (RECOMMENDED)
|
||||||
|
-- -- Install the mpv_webscoket extension: https://github.com/kuroahna/mpv_websocket
|
||||||
|
-- -- Open a LOCAL copy of https://github.com/Renji-XD/texthooker-ui
|
||||||
|
-- -- Configure the script (if you're not using the Lapis note format)
|
||||||
|
-- IF USING CLIPBOARD INSERTER (NOT RECOMMENDED)
|
||||||
|
-- -- Install the clipboard inserter plugin: https://github.com/laplus-sadness/lap-clipboard-inserter
|
||||||
|
-- -- Open the texthooker UI, enable the plugin and enable clipboard pasting: https://github.com/Renji-XD/texthooker-ui
|
||||||
|
-- BOTH
|
||||||
|
-- -- Wait for an unknown word and create the card with Yomichan.
|
||||||
|
-- -- Select all the subtitle lines you wish to add to the card and copy with Ctrl + c.
|
||||||
|
-- -- Press Ctrl + v in MPV to add the lines, their Audio and the currently paused image to the back of the card.
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
------------- Credits -------------
|
||||||
|
-- Credits and copyright go to Anacreon DJT: https://anacreondjt.gitlab.io/
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
------------- Original Credits (Outdated) -------------
|
||||||
|
-- This script was made by users of 4chan's Daily Japanese Thread (DJT) on /jp/
|
||||||
|
-- More information can be found here http://animecards.site/
|
||||||
|
-- Message @Anacreon with bug reports and feature requests on Discord (https://animecards.site/discord/) or 4chan (https://boards.4channel.org/jp/#s=djt)
|
||||||
|
--
|
||||||
|
-- If you like this work please consider subscribing on Patreon!
|
||||||
|
-- https://www.patreon.com/Quizmaster
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
local utils = require("mp.utils")
|
||||||
|
local msg = require("mp.msg")
|
||||||
|
|
||||||
|
------------- User Config -------------
|
||||||
|
-- Set these to match your field names in Anki
|
||||||
|
local FRONT_FIELD = "Expression"
|
||||||
|
local SENTENCE_AUDIO_FIELD = "SentenceAudio"
|
||||||
|
local SENTENCE_FIELD = "Sentence"
|
||||||
|
local IMAGE_FIELD = "Picture"
|
||||||
|
-- Optional padding and fade settings in seconds.
|
||||||
|
-- Padding grabs extra audio around your selected subs.
|
||||||
|
-- Fade does a volume fade effect at the beginning and end of the resulting audio.
|
||||||
|
local AUDIO_CLIP_FADE = 0.2
|
||||||
|
local AUDIO_CLIP_PADDING = 0.75
|
||||||
|
-- Optional play sentence audio automatically after card update
|
||||||
|
local AUTOPLAY_AUDIO = false
|
||||||
|
-- Optional screenshot image format. Valid options: "webp" or "png"
|
||||||
|
-- Change to "png" if you plan to view cards on iOS or Mac.
|
||||||
|
local IMAGE_FORMAT = "png"
|
||||||
|
-- Optional set to true if you want your volume in mpv to affect Anki card volume.
|
||||||
|
local USE_MPV_VOLUME = false
|
||||||
|
-- Set to true if you want writing to clipboard to be enabled by default.
|
||||||
|
-- The more modern and recommended alternative is to use the websocket.
|
||||||
|
local ENABLE_SUBS_TO_CLIP = false
|
||||||
|
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
------------- Internal Variables -------------
|
||||||
|
local subs = {}
|
||||||
|
local debug_mode = false
|
||||||
|
local use_powershell_clipboard = nil
|
||||||
|
local prefix = ""
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
------------- Setup -------------
|
||||||
|
if unpack ~= nil then
|
||||||
|
table.unpack = unpack
|
||||||
|
end
|
||||||
|
|
||||||
|
local o = {}
|
||||||
|
-- Possible platforms: windows, linux, macos
|
||||||
|
local platform = mp.get_property_native("platform")
|
||||||
|
if platform == "darwin" then
|
||||||
|
platform = "macos"
|
||||||
|
end
|
||||||
|
|
||||||
|
local display_server
|
||||||
|
if os.getenv("WAYLAND_DISPLAY") then
|
||||||
|
display_server = "wayland"
|
||||||
|
elseif platform == "linux" then
|
||||||
|
display_server = "xorg"
|
||||||
|
else
|
||||||
|
display_server = ""
|
||||||
|
end
|
||||||
|
|
||||||
|
local function dlog(...)
|
||||||
|
if debug_mode then
|
||||||
|
print(...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function verfiy_libmp3lame()
|
||||||
|
local encoderlist = mp.get_property("encoder-list")
|
||||||
|
if not encoderlist or not string.find(encoderlist, "libmp3lame") then
|
||||||
|
mp.osd_message(
|
||||||
|
"Error: libmp3lame encoder not found. Audio export will not work.\nPlease use a build of mpv with libmp3lame support.",
|
||||||
|
10
|
||||||
|
)
|
||||||
|
msg.error("Error: libmp3lame encoder not found. MP3 audio export will not work.")
|
||||||
|
else
|
||||||
|
dlog("libmp3lame encoder found.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
mp.register_event("file-loaded", verfiy_libmp3lame)
|
||||||
|
|
||||||
|
dlog("Detected Platform: " .. platform)
|
||||||
|
dlog("Detected display server: " .. display_server)
|
||||||
|
|
||||||
|
---------------------------------------
|
||||||
|
-- Handle requests to AnkiConnect
|
||||||
|
local function anki_connect(action, params)
|
||||||
|
local request = utils.format_json({ action = action, params = params, version = 6 })
|
||||||
|
local args = { "curl", "-s", "localhost:8765", "-X", "POST", "-d", request }
|
||||||
|
|
||||||
|
dlog("AnkiConnect request: " .. request)
|
||||||
|
|
||||||
|
local result = utils.subprocess({ args = args, cancellable = false, capture_stderr = true })
|
||||||
|
|
||||||
|
if result.status ~= 0 then
|
||||||
|
msg.error("Curl command failed with status: " .. tostring(result.status))
|
||||||
|
msg.error("Stderr: " .. (result.stderr or "none"))
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if not result.stdout or result.stdout == "" then
|
||||||
|
msg.error("Empty response from AnkiConnect")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
dlog("AnkiConnect response: " .. result.stdout)
|
||||||
|
|
||||||
|
local success, parsed_result = pcall(function()
|
||||||
|
return utils.parse_json(result.stdout)
|
||||||
|
end)
|
||||||
|
if not success or not parsed_result then
|
||||||
|
msg.error("Failed to parse JSON response: " .. (result.stdout or "empty"))
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return parsed_result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get media directory path from AnkiConnect
|
||||||
|
local function set_media_dir()
|
||||||
|
local media_dir_response = anki_connect("getMediaDirPath")
|
||||||
|
if not media_dir_response then
|
||||||
|
msg.error("Failed to communicate with AnkiConnect. Is Anki running and do you have AnkiConnect installed?")
|
||||||
|
mp.osd_message(
|
||||||
|
"Error: Failed to communicate with AnkiConnect. Is Anki running and do you have AnkiConnect installed?",
|
||||||
|
5
|
||||||
|
)
|
||||||
|
return
|
||||||
|
elseif media_dir_response["error"] then
|
||||||
|
msg.error("AnkiConnect error: " .. tostring(media_dir_response["error"]))
|
||||||
|
mp.osd_message("AnkiConnect error: " .. tostring(media_dir_response["error"]), 5)
|
||||||
|
return
|
||||||
|
elseif media_dir_response["result"] then
|
||||||
|
prefix = media_dir_response["result"]
|
||||||
|
dlog("Got media directory path from AnkiConnect: " .. prefix)
|
||||||
|
else
|
||||||
|
msg.error("Unexpected response format from AnkiConnect")
|
||||||
|
mp.osd_message("Error: Unexpected response from AnkiConnect", 5)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function clean(s)
|
||||||
|
for _, ws in ipairs({
|
||||||
|
"%s",
|
||||||
|
" ",
|
||||||
|
"",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
"",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
}) do
|
||||||
|
s = s:gsub(ws .. "+", "")
|
||||||
|
end
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_name(s, e)
|
||||||
|
return mp.get_property("filename"):gsub("%W", "") .. tostring(s) .. tostring(e)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_clipboard()
|
||||||
|
local res
|
||||||
|
if platform == "windows" then
|
||||||
|
res = utils.subprocess({
|
||||||
|
args = {
|
||||||
|
"powershell",
|
||||||
|
"-NoProfile",
|
||||||
|
"-Command",
|
||||||
|
[[& {
|
||||||
|
Trap {
|
||||||
|
Write-Error -ErrorRecord $_
|
||||||
|
Exit 1
|
||||||
|
}
|
||||||
|
$clip = ""
|
||||||
|
if (Get-Command "Get-Clipboard" -errorAction SilentlyContinue) {
|
||||||
|
$clip = Get-Clipboard -Raw -Format Text -TextFormatType UnicodeText
|
||||||
|
} else {
|
||||||
|
Add-Type -AssemblyName PresentationCore
|
||||||
|
$clip = [Windows.Clipboard]::GetText()
|
||||||
|
}
|
||||||
|
$clip = $clip -Replace "`r",""
|
||||||
|
$u8clip = [System.Text.Encoding]::UTF8.GetBytes($clip)
|
||||||
|
[Console]::OpenStandardOutput().Write($u8clip, 0, $u8clip.Length)
|
||||||
|
}]],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
elseif platform == "macos" then
|
||||||
|
return io.popen("LANG=en_US.UTF-8 pbpaste"):read("*a")
|
||||||
|
else -- platform == 'linux'
|
||||||
|
if display_server == "wayland" then
|
||||||
|
res = utils.subprocess({ args = {
|
||||||
|
"wl-paste",
|
||||||
|
} })
|
||||||
|
else -- display_server == 'xorg'
|
||||||
|
res = utils.subprocess({ args = {
|
||||||
|
"xclip",
|
||||||
|
"-selection",
|
||||||
|
"clipboard",
|
||||||
|
"-out",
|
||||||
|
} })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not res.error then
|
||||||
|
return res.stdout
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function powershell_set_clipboard(text)
|
||||||
|
utils.subprocess({
|
||||||
|
args = {
|
||||||
|
"powershell",
|
||||||
|
"-NoProfile",
|
||||||
|
"-Command",
|
||||||
|
[[Set-Clipboard -Value @"]] .. "\n" .. text .. "\n" .. [["@]],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function cmd_set_clipboard(text)
|
||||||
|
local cmd = "echo " .. text .. " | clip"
|
||||||
|
mp.command("run cmd /D /C " .. cmd)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function determine_clip_type()
|
||||||
|
powershell_set_clipboard([[Anacreon様]])
|
||||||
|
use_powershell_clipboard = get_clipboard() == [[Anacreon様]]
|
||||||
|
end
|
||||||
|
|
||||||
|
local function linux_set_clipboard(text)
|
||||||
|
if display_server == "wayland" then
|
||||||
|
os.execute("wl-copy <<EOF\n" .. text .. "\nEOF\n")
|
||||||
|
else -- display_server == 'xorg'
|
||||||
|
os.execute("xclip -selection clipboard <<EOF\n" .. text .. "\nEOF\n")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function macos_set_clipboard(text)
|
||||||
|
os.execute("export LANG=en_US.UTF-8; cat <<EOF | pbcopy\n" .. text .. "\nEOF\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function record_sub(_, text)
|
||||||
|
if text and mp.get_property_number("sub-start") and mp.get_property_number("sub-end") then
|
||||||
|
local sub_delay = mp.get_property_native("sub-delay")
|
||||||
|
local audio_delay = mp.get_property_native("audio-delay")
|
||||||
|
local newtext = clean(text)
|
||||||
|
if newtext == "" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
subs[newtext] = {
|
||||||
|
mp.get_property_number("sub-start") + sub_delay - audio_delay,
|
||||||
|
mp.get_property_number("sub-end") + sub_delay - audio_delay,
|
||||||
|
}
|
||||||
|
dlog(string.format("%s -> %s : %s", subs[newtext][1], subs[newtext][2], newtext))
|
||||||
|
if ENABLE_SUBS_TO_CLIP then
|
||||||
|
-- Remove newlines from text before sending it to clipboard.
|
||||||
|
-- This way pressing control+v without copying from texthooker page
|
||||||
|
-- will always give last line.
|
||||||
|
text = string.gsub(text, "[\n\r]+", " ")
|
||||||
|
if platform == "windows" then
|
||||||
|
if use_powershell_clipboard == nil then
|
||||||
|
determine_clip_type()
|
||||||
|
end
|
||||||
|
if use_powershell_clipboard then
|
||||||
|
powershell_set_clipboard(text)
|
||||||
|
else
|
||||||
|
cmd_set_clipboard(text)
|
||||||
|
end
|
||||||
|
elseif platform == "macos" then
|
||||||
|
macos_set_clipboard(text)
|
||||||
|
else
|
||||||
|
linux_set_clipboard(text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function create_audio(s, e)
|
||||||
|
if s == nil or e == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local name = get_name(s, e)
|
||||||
|
local destination = utils.join_path(prefix, name .. ".mp3")
|
||||||
|
s = s - AUDIO_CLIP_PADDING
|
||||||
|
local t = e - s + AUDIO_CLIP_PADDING
|
||||||
|
local source = mp.get_property("path")
|
||||||
|
local aid = mp.get_property("aid")
|
||||||
|
|
||||||
|
local tracks_count = mp.get_property_number("track-list/count")
|
||||||
|
for i = 1, tracks_count do
|
||||||
|
local track_type = mp.get_property(string.format("track-list/%d/type", i))
|
||||||
|
local track_selected = mp.get_property(string.format("track-list/%d/selected", i))
|
||||||
|
if track_type == "audio" and track_selected == "yes" then
|
||||||
|
if mp.get_property(string.format("track-list/%d/external-filename", i), o) ~= o then
|
||||||
|
source = mp.get_property(string.format("track-list/%d/external-filename", i))
|
||||||
|
aid = "auto"
|
||||||
|
end
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local cmd = {
|
||||||
|
"run",
|
||||||
|
"mpv",
|
||||||
|
source,
|
||||||
|
"--loop-file=no",
|
||||||
|
"--video=no",
|
||||||
|
"--no-ocopy-metadata",
|
||||||
|
"--no-sub",
|
||||||
|
"--audio-channels=1",
|
||||||
|
string.format("--start=%.3f", s),
|
||||||
|
string.format("--length=%.3f", t),
|
||||||
|
string.format("--aid=%s", aid),
|
||||||
|
string.format("--volume=%s", USE_MPV_VOLUME and mp.get_property("volume") or "100"),
|
||||||
|
string.format("--af-append=afade=t=in:curve=ipar:st=%.3f:d=%.3f", s, AUDIO_CLIP_FADE),
|
||||||
|
string.format("--af-append=afade=t=out:curve=ipar:st=%.3f:d=%.3f", s + t - AUDIO_CLIP_FADE, AUDIO_CLIP_FADE),
|
||||||
|
string.format("-o=%s", destination),
|
||||||
|
}
|
||||||
|
mp.commandv(table.unpack(cmd))
|
||||||
|
dlog(utils.to_string(cmd))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function create_screenshot(s, e)
|
||||||
|
local source = mp.get_property("path")
|
||||||
|
local img = utils.join_path(prefix, get_name(s, e) .. "." .. IMAGE_FORMAT)
|
||||||
|
|
||||||
|
local cmd = {
|
||||||
|
"run",
|
||||||
|
"mpv",
|
||||||
|
source,
|
||||||
|
"--loop-file=no",
|
||||||
|
"--audio=no",
|
||||||
|
"--no-ocopy-metadata",
|
||||||
|
"--no-sub",
|
||||||
|
"--frames=1",
|
||||||
|
}
|
||||||
|
if IMAGE_FORMAT == "webp" then
|
||||||
|
table.insert(cmd, "--ovc=libwebp")
|
||||||
|
table.insert(cmd, "--ovcopts-add=lossless=0")
|
||||||
|
table.insert(cmd, "--ovcopts-add=compression_level=6")
|
||||||
|
table.insert(cmd, "--ovcopts-add=preset=drawing")
|
||||||
|
elseif IMAGE_FORMAT == "png" then
|
||||||
|
table.insert(cmd, "--vf-add=format=rgb24")
|
||||||
|
end
|
||||||
|
table.insert(cmd, "--vf-add=scale=480*iw*sar/ih:480")
|
||||||
|
table.insert(cmd, string.format("--start=%.3f", mp.get_property_number("time-pos")))
|
||||||
|
table.insert(cmd, string.format("-o=%s", img))
|
||||||
|
mp.commandv(table.unpack(cmd))
|
||||||
|
dlog(utils.to_string(cmd))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function add_to_last_added(ifield, afield, tfield)
|
||||||
|
local added_notes = anki_connect("findNotes", { query = "added:1" })["result"]
|
||||||
|
table.sort(added_notes)
|
||||||
|
local noteid = added_notes[#added_notes]
|
||||||
|
local note = anki_connect("notesInfo", { notes = { noteid } })
|
||||||
|
|
||||||
|
if note ~= nil then
|
||||||
|
local word = note["result"][1]["fields"][FRONT_FIELD]["value"]
|
||||||
|
local new_fields = {
|
||||||
|
[SENTENCE_AUDIO_FIELD] = afield,
|
||||||
|
[SENTENCE_FIELD] = tfield,
|
||||||
|
[IMAGE_FIELD] = ifield,
|
||||||
|
}
|
||||||
|
|
||||||
|
anki_connect("updateNoteFields", {
|
||||||
|
note = {
|
||||||
|
id = noteid,
|
||||||
|
fields = new_fields,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
mp.osd_message("Updated note: " .. word, 3)
|
||||||
|
msg.info("Updated note: " .. word)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_extract()
|
||||||
|
local lines = get_clipboard()
|
||||||
|
local e = 0
|
||||||
|
local s = 0
|
||||||
|
for line in lines:gmatch("[^\r\n]+") do
|
||||||
|
line = clean(line)
|
||||||
|
dlog(line)
|
||||||
|
if subs[line] ~= nil then
|
||||||
|
if subs[line][1] ~= nil and subs[line][2] ~= nil then
|
||||||
|
if s == 0 then
|
||||||
|
s = subs[line][1]
|
||||||
|
else
|
||||||
|
s = math.min(s, subs[line][1])
|
||||||
|
end
|
||||||
|
e = math.max(e, subs[line][2])
|
||||||
|
end
|
||||||
|
else
|
||||||
|
mp.osd_message("ERR! Line not found: " .. line, 3)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
dlog(string.format("s=%d, e=%d", s, e))
|
||||||
|
if e ~= 0 then
|
||||||
|
create_screenshot(s, e)
|
||||||
|
create_audio(s, e)
|
||||||
|
local ifield = "<img src=" .. get_name(s, e) .. "." .. IMAGE_FORMAT .. ">"
|
||||||
|
local afield = "[sound:" .. get_name(s, e) .. ".mp3]"
|
||||||
|
local tfield = string.gsub(string.gsub(lines, "\n+", "<br />"), "\r", "")
|
||||||
|
add_to_last_added(ifield, afield, tfield)
|
||||||
|
if AUTOPLAY_AUDIO then
|
||||||
|
local name = get_name(s, e)
|
||||||
|
local audio = utils.join_path(prefix, name .. ".mp3")
|
||||||
|
local cmd = { "run", "mpv", audio, "--loop-file=no", "--load-scripts=no" }
|
||||||
|
mp.commandv(table.unpack(cmd))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function ex()
|
||||||
|
if not prefix or prefix == "" then
|
||||||
|
set_media_dir()
|
||||||
|
end
|
||||||
|
|
||||||
|
if debug_mode then
|
||||||
|
get_extract()
|
||||||
|
else
|
||||||
|
pcall(get_extract)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function rec(...)
|
||||||
|
if debug_mode then
|
||||||
|
record_sub(...)
|
||||||
|
else
|
||||||
|
pcall(record_sub, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function toggle_sub_to_clipboard()
|
||||||
|
ENABLE_SUBS_TO_CLIP = not ENABLE_SUBS_TO_CLIP
|
||||||
|
mp.osd_message("Clipboard inserter " .. (ENABLE_SUBS_TO_CLIP and "activated" or "deactived"), 3)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function toggle_debug_mode()
|
||||||
|
debug_mode = not debug_mode
|
||||||
|
mp.osd_message("Debug mode " .. (debug_mode and "activated" or "deactived"), 3)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function clear_subs(_)
|
||||||
|
subs = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
mp.observe_property("sub-text", "string", rec)
|
||||||
|
mp.observe_property("filename", "string", clear_subs)
|
||||||
|
|
||||||
|
mp.add_key_binding("ctrl+v", "update-anki-card", ex)
|
||||||
|
mp.add_key_binding("ctrl+t", "toggle-clipboard-insertion", toggle_sub_to_clipboard)
|
||||||
|
mp.add_key_binding("ctrl+d", "toggle-debug-mode", toggle_debug_mode)
|
||||||
|
mp.add_key_binding("ctrl+V", ex)
|
||||||
|
mp.add_key_binding("ctrl+T", toggle_sub_to_clipboard)
|
||||||
|
mp.add_key_binding("ctrl+D", toggle_debug_mode)
|
@ -1 +0,0 @@
|
|||||||
Subproject commit 9bde59a0abaa018096de5b6f38c3ad409b9cfdf1
|
|
Loading…
x
Reference in New Issue
Block a user