Skip to content

Commit

Permalink
Merge pull request #86 from migaku-official/feat/add-card
Browse files Browse the repository at this point in the history
Add Anki cards feature
  • Loading branch information
MinmoTech authored Dec 22, 2023
2 parents 728ccf7 + 9f51bc6 commit eb0bb2b
Show file tree
Hide file tree
Showing 35 changed files with 2,166 additions and 1,166 deletions.
155 changes: 104 additions & 51 deletions src/__init__.py
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()
94 changes: 94 additions & 0 deletions src/card_types.py
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", ""),
)
Loading

0 comments on commit eb0bb2b

Please sign in to comment.