-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #86 from migaku-official/feat/add-card
Add Anki cards feature
- Loading branch information
Showing
35 changed files
with
2,166 additions
and
1,166 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,75 +1,128 @@ | ||
from dataclasses import dataclass | ||
from platform import platform | ||
import sys | ||
import os | ||
import platform | ||
|
||
import aqt | ||
from aqt.qt import * | ||
import anki | ||
from .config import get | ||
|
||
from .sys_libraries import init_sys_libs | ||
|
||
# insert librairies into sys.path | ||
|
||
|
||
def add_sys_path(*path_parts): | ||
sys.path.insert( | ||
0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "lib", *path_parts) | ||
) | ||
|
||
|
||
add_sys_path("shared") | ||
if anki.utils.is_lin: | ||
add_sys_path("linux") | ||
elif anki.utils.is_mac and sys.version_info.major >= 3 and sys.version_info.minor >= 11: | ||
add_sys_path("macos_311") | ||
elif anki.utils.is_mac and sys.version_info.major >= 3 and sys.version_info.minor >= 10: | ||
add_sys_path("macos_310") | ||
elif anki.utils.is_mac: | ||
add_sys_path("macos_39") | ||
elif anki.utils.is_win: | ||
add_sys_path("windows") | ||
|
||
|
||
# Allow webviews to access necessary resources | ||
aqt.mw.addonManager.setWebExports(__name__, r"(languages/.*?\.svg|inplace_editor.css)") | ||
init_sys_libs() | ||
|
||
# Initialize sub modules | ||
from . import ( | ||
note_type_dialogs, | ||
anki_version, | ||
browser, | ||
card_layout, | ||
card_type_selector, | ||
note_type_mgr, | ||
reviewer, | ||
click_play_audio, | ||
editor, | ||
inplace_editor, | ||
click_play_audio, | ||
browser, | ||
card_layout, | ||
welcome_wizard, | ||
migaku_connection, | ||
note_type_dialogs, | ||
note_type_mgr, | ||
reviewer, | ||
toolbar, | ||
webview_contextmenu, | ||
settings_window, | ||
ease_reset, | ||
retirement, | ||
balance_scheduler, | ||
balance_scheduler_vacation_window, | ||
balance_scheduler_dayoff_window, | ||
anki_version, | ||
welcome_wizard, | ||
menu, | ||
) | ||
|
||
|
||
from . import migaku_connection | ||
def setup_hooks(): | ||
# Allow webviews to access necessary resources | ||
aqt.mw.addonManager.setWebExports( | ||
__name__, r"(languages/.*?\.svg|inplace_editor.css)" | ||
) | ||
|
||
aqt.gui_hooks.models_did_init_buttons.append(note_type_dialogs.setup_note_editor) | ||
aqt.editor.Editor.onBridgeCmd = anki.hooks.wrap( | ||
aqt.editor.Editor.onBridgeCmd, editor.on_migaku_bridge_cmds, "around" | ||
) | ||
aqt.gui_hooks.editor_did_init.append(editor.editor_did_init) | ||
aqt.gui_hooks.editor_did_load_note.append(editor.editor_did_load_note) | ||
aqt.gui_hooks.editor_did_init_buttons.append(editor.setup_editor_buttons) | ||
|
||
# DECK CHANGE | ||
def deck_change(id): | ||
editor.current_editor.on_addcards_did_change_deck(id) | ||
toolbar.refresh_migaku_toolbar() | ||
|
||
if getattr(aqt.gui_hooks, "add_cards_did_change_deck", None): | ||
aqt.gui_hooks.add_cards_did_change_deck.append(deck_change) | ||
else: | ||
aqt.addcards.AddCards.on_deck_changed = anki.hooks.wrap( | ||
aqt.addcards.AddCards.on_deck_changed, | ||
lambda _, id: deck_change(id), | ||
"before", | ||
) | ||
|
||
### MODEL CHANGE | ||
def notetype_change(id): | ||
editor.current_editor.on_addcards_did_change_note_type(id) | ||
toolbar.refresh_migaku_toolbar() | ||
|
||
if getattr(aqt.gui_hooks, "add_cards_did_change_note_type", None): | ||
aqt.gui_hooks.add_cards_did_change_note_type.append( | ||
lambda _, id: notetype_change(id) | ||
) | ||
elif getattr(aqt.addcards.AddCards, "on_notetype_change", None): | ||
aqt.addcards.AddCards.on_notetype_change = anki.hooks.wrap( | ||
aqt.addcards.AddCards.on_notetype_change, | ||
lambda _, id: notetype_change(id), | ||
) | ||
else: | ||
aqt.addcards.AddCards.onModelChange = anki.hooks.wrap( | ||
aqt.addcards.AddCards.onModelChange, | ||
notetype_change, | ||
) | ||
|
||
aqt.gui_hooks.editor_did_init.append(editor.current_editor.set_current_editor) | ||
aqt.editor.Editor.cleanup = anki.hooks.wrap( | ||
aqt.editor.Editor.cleanup, editor.current_editor.remove_editor, "before" | ||
) | ||
|
||
aqt.gui_hooks.profile_did_open.append(note_type_mgr.update_all_installed) | ||
|
||
aqt.gui_hooks.collection_did_load.append(toolbar.inject_migaku_toolbar) | ||
|
||
@dataclass | ||
class Defaults: | ||
notetype_id: int | ||
deck_id: int | ||
|
||
def set_current_deck_model_to_migaku(current_review_card): | ||
toolbar.set_deck_type_to_migaku() | ||
return Defaults( | ||
get("migakuNotetypeId"), | ||
get("migakuDeckId"), | ||
) | ||
|
||
def overwrite_defaults_for_adding(col): | ||
col.defaults_for_adding = set_current_deck_model_to_migaku | ||
|
||
aqt.gui_hooks.collection_did_load.append(overwrite_defaults_for_adding) | ||
|
||
aqt.addcards.AddCards.on_notetype_change = anki.hooks.wrap( | ||
aqt.addcards.AddCards.on_notetype_change, | ||
lambda addcards, _1: editor.reset_migaku_mode(addcards.editor), | ||
"before", | ||
) | ||
|
||
aqt.gui_hooks.add_cards_did_init.append( | ||
lambda _: toolbar.refresh_migaku_toolbar_opened_addcards() | ||
) | ||
|
||
def setup_menu(): | ||
menu = QMenu("Migaku", aqt.mw) | ||
menu.addAction(settings_window.action) | ||
menu.addSeparator() | ||
menu.addAction(ease_reset.action) | ||
menu.addAction(retirement.action) | ||
menu.addAction(balance_scheduler.action) | ||
menu.addAction(balance_scheduler_vacation_window.action) | ||
menu.addAction(balance_scheduler_dayoff_window.action) | ||
aqt.mw.form.menubar.insertMenu(aqt.mw.form.menuHelp.menuAction(), menu) | ||
# aqt.deckbrowser.DeckBrowser.set_current_deck = anki.hooks.wrap( | ||
# aqt.deckbrowser.DeckBrowser.set_current_deck, | ||
# lambda self, deck_id: toolbar.refresh_migaku_toolbar(), | ||
# "after", | ||
# ) | ||
|
||
|
||
setup_menu() | ||
menu.setup_menu() | ||
setup_hooks() | ||
anki_version.check_anki_version_dialog() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
from base64 import b64decode | ||
from dataclasses import dataclass, field | ||
from typing import Optional | ||
import requests | ||
|
||
from .migaku_connection.handle_files import move_file_to_media_dir | ||
|
||
|
||
@dataclass | ||
class AudioAsset: | ||
id: str | ||
title: str | ||
input: str | ||
r2Url: Optional[str] = None | ||
|
||
|
||
@dataclass | ||
class ImageAsset: | ||
id: str | ||
title: str | ||
src: str | ||
alt: str | ||
r2Url: Optional[str] = None | ||
|
||
|
||
@dataclass | ||
class CardFields: | ||
targetWord: str = "" | ||
sentence: str = "" | ||
translation: str = "" | ||
definitions: str = "" | ||
sentenceAudio: str = "" | ||
wordAudio: str = "" | ||
images: str = "" | ||
exampleSentences: str = "" | ||
notes: str = "" | ||
|
||
|
||
def process_image_asset(image: ImageAsset): | ||
name = f"{image.id}.webp" | ||
|
||
if image.src.startswith("data:image/webp;base64,"): | ||
data = image.src.split(",", 1)[1] | ||
move_file_to_media_dir(b64decode(data), name) | ||
elif image.input.startswith("http"): | ||
data = requests.get(image.src, allow_redirects=True) | ||
move_file_to_media_dir(data.content, name) | ||
|
||
return f"<img src='{name}' />" | ||
|
||
|
||
def process_audio_asset(audio: AudioAsset): | ||
name = f"{audio.id}.m4a" | ||
|
||
if audio.input.startswith("data:audio/mp4;base64,"): | ||
data = audio.input.split(",", 1)[1] | ||
move_file_to_media_dir(b64decode(data), name) | ||
elif audio.input.startswith("http"): | ||
data = requests.get(audio.input, allow_redirects=True) | ||
move_file_to_media_dir(data.content, name) | ||
|
||
return f"[sound:{name}]" | ||
|
||
|
||
def card_fields_from_dict(data: dict[str, any]): | ||
br = "\n<br>\n" | ||
|
||
sentenceAudios = br.join( | ||
[ | ||
process_audio_asset(AudioAsset(**audio)) | ||
for audio in data.get("sentenceAudio", []) | ||
] | ||
) | ||
wordAudios = br.join( | ||
[ | ||
process_audio_asset(AudioAsset(**audio)) | ||
for audio in data.get("wordAudio", []) | ||
] | ||
) | ||
imagess = br.join( | ||
[process_image_asset(ImageAsset(**img)) for img in data.get("images", [])] | ||
) | ||
|
||
return CardFields( | ||
targetWord=data.get("targetWord", ""), | ||
sentence=data.get("sentence", ""), | ||
translation=data.get("translation", ""), | ||
definitions=data.get("definitions", ""), | ||
sentenceAudio=sentenceAudios, | ||
wordAudio=wordAudios, | ||
images=imagess, | ||
exampleSentences=data.get("exampleSentences", ""), | ||
notes=data.get("notes", ""), | ||
) |
Oops, something went wrong.