-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path__init__.py
152 lines (127 loc) · 5.39 KB
/
__init__.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
from aqt import mw
from aqt import gui_hooks
from aqt.qt import *
from aqt.utils import showInfo, qconnect
from anki import hooks
from aqt.operations import QueryOp
from re import escape
import gc
import os
import random
from collections import defaultdict
from pathlib import Path
from glob import glob
from glob import escape
from collections import defaultdict
from functools import reduce
from pprint import pprint
import kanji_writing.data
ignore = set(data.ignore)
identity = lambda *args: args
kanji_only = lambda x: set(x) - ignore
def hhmmss_to_seconds(hh, mm, ss):
return hh * 60 * 60 + mm * 60 + ss
# MEDIA_EXTENSIONS = ['mkv', 'mp4', 'mp3', 'm4b', 'm4a', 'aac', 'flac']
MEDIA_EXTENSIONS = {'mkv', 'mp4', 'mp3', 'm4b', 'm4a', 'aac', 'flac'}
def get_audio_file(path):
# Hack for file names like 第7.5巻
x = str(path).split('.')
z = (x[0] + '.' + x[1]) if len(x) > 1 and len(x[1]) > 5 else x[0]
y = next(filter(lambda i: i.suffix[1:] in MEDIA_EXTENSIONS, map(Path, glob(escape(z) + ".*"))))
return y# z[0]
def srt_to_timings(content, with_indicies=False):
out = []
f = 1 if with_indicies else 0
for i in content:
n = i.strip().split('\n')
timings = tuple([hhmmss_to_seconds(*map(float, x.split(":"))) for x in n[f].replace(",", ".").split(" --> ")])
out.append((timings, ''.join(n[f+1:])))
return out
class Addon:
def __init__(self):
self.subs = defaultdict(set)
self.kanji_ease = dict({})
self.ease_max = 0
self.config = mw.addonManager.getConfig(__name__)
self.queue = {}
def start(self):
QueryOp(
parent=mw,
op=self.load_ease,
success=identity
).with_progress().run_in_background()
QueryOp(
parent=mw,
op=self.process_subs,
success=identity
).with_progress().run_in_background()
def stop(self):
del self.config
self.config = mw.addonManager.getConfig(__name__)
del self.subs
self.subs = defaultdict(set)
del self.kanji_ease
self.kanji_ease = dict({})
del self.queue
self.queue = {}
gc.collect()
def restart(self):
self.stop()
self.start()
def process_subs(self, col):
for i in self.config['paths']:
for i in map(Path, glob(escape(i) + "/**.vtt") + glob(escape(i) + "/**.srt")):
with i.open() as f:
content = f.read().strip().split('\n\n')
if i.suffix == '.vtt': content = content[1:]
audio_path = get_audio_file(i)
for timings, line in srt_to_timings(content, with_indicies=(i.suffix == '.srt')):
for s in kanji_only(line): self.subs[s].add((timings, audio_path, line))
print(len(self.subs))
# return self.subs
def load_ease(self, col):
for i in map(col.get_note, col.find_notes(f'deck:"{self.config["deck"]}"')):
c = i.cards()[0]
if c.reps == 0: continue
for k in kanji_only([i for i in i.items() if i[0] == 'Kanji'][0][1]):
self.kanji_ease[k] = min(self.kanji_ease.get(k, c.due), c.due)
self.ease_max = max(self.kanji_ease[k], self.ease_max)
print(len(self.kanji_ease))
# return (self.kanji_ease, self.ease_max)
def dynamic(self, field_text, field_name, filter_name, ctx):
if filter_name != "DynamicKanji": return field_text
card_id = ctx.card().id
if card_id in self.queue:
out = self.queue[card_id]#[1]#card
del self.queue[card_id]
return '\n<br>\n'.join(out)
kanji = sorted(kanji_only(field_text), key=lambda x: self.kanji_ease.get(x, self.ease_max))
sentences = reduce(set.intersection, [self.subs[i] for i in kanji])
n = 0 # If we can't find a sentence with both kanji, use the kanji with the lowest ease
while len(sentences) == 0 and n < len(kanji):
sentences = self.subs[kanji[n]]
n += 1
if n == len(kanji): return "" # TODO(YM): No sentences exist in the current database, use the sentence provided in the card
def value(sen):
senkanji = kanji_only(sen)
return sum([self.ease_max - self.kanji_ease.get(i, self.ease_max) for i in senkanji]) / len(senkanji) * len(sen)
timings, file, sentence = random.choice(sorted(sentences, key=value)[:5])
if os.path.islink('/tmp/whatever'): os.unlink('/tmp/whatever')
os.symlink(file, '/tmp/whatever')
# Requires advanced mpv player
self.queue[card_id] = (sentence, f'[sound:/tmp/whatever --vid=no --start=+{timings[0]-0.1} --end=+{timings[1]+0.1}]')
# self.queue[card_id] = (sentence, f'[sound:"{file}" --vid=no --start=+{timings[0]-0.1} --end=+{timings[1]+0.1}]')
# return sentence + '\n' + f'[sound:/tmp/whatever --vid=no --start=+{timings[0]-0.1} --end=+{timings[1]+0.1}]'
return self.queue[card_id][1]
def init():
addon = Addon()
hooks.field_filter.append(addon.dynamic)
mm = mw.form.menuTools.addMenu("Kanji Writing")
def create_action(name, func):
a = QAction(name, mw)
qconnect(a.triggered, func)
mm.addAction(a)
create_action("Load Kanji", addon.start)
create_action("Free memory", addon.stop)
create_action("Restart", addon.restart)
gui_hooks.main_window_did_init.append(init)