Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/linters.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Linters

on:
pull_request:
branches:
- main

jobs:

isort:
name: isort
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: isort/isort-action@v1.1.0
with:
configuration: "--check --diff"

black:
name: black
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: psf/black@stable
with:
options: "--check --diff"
version: "~= 23.0"
115 changes: 60 additions & 55 deletions ankiconnect.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,46 @@
import json
import urllib.request
from pathlib import Path

import eel
import yaml
from pathlib import Path
from logger import AUDIO_LOG_PATH
from config import r_config, ANKI_CONFIG

from config import ANKI_CONFIG, r_config
from dictionary import get_jpod_audio_base64
from tools import bundle_dir
from logger import AUDIO_LOG_PATH
from tools import bundle_dir

ANKI_MODELS_FILENAME = 'ankimodels.yaml'
ANKI_MODELS_FILENAME = "ankimodels.yaml"

NOTE_SCREENSHOT = "screenshot"

NOTE_SCREENSHOT = 'screenshot'

def request(action, params):
return {'action': action, 'params': params, 'version': 6}
return {"action": action, "params": params, "version": 6}


@eel.expose
def invoke(action, params):
try:
requestJson = json.dumps(request(action, params)).encode('utf-8')
response = json.load(urllib.request.urlopen(urllib.request.Request('http://localhost:8765', requestJson)))
requestJson = json.dumps(request(action, params)).encode("utf-8")
response = json.load(urllib.request.urlopen(urllib.request.Request("http://localhost:8765", requestJson)))
if len(response) != 2:
return 'Error: Response has an unexpected number of fields'
if 'error' not in response:
return 'Error: Response has an unexpected number of fields'
if 'result' not in response:
return 'Error: Response is missing required result field'
if response['error'] is not None:
return 'Error:' + response['error']
return response['result']
return "Error: Response has an unexpected number of fields"
if "error" not in response:
return "Error: Response is missing required error field"
if "result" not in response:
return "Error: Response is missing required result field"
if response["error"] is not None:
return "Error:" + response["error"]
return response["result"]
except:
return 'Error: Failed to connect to Anki.'
return "Error: Failed to connect to Anki."


def get_anki_models():
filename = str(Path(bundle_dir, 'anki', ANKI_MODELS_FILENAME))
filename = str(Path(bundle_dir, "anki", ANKI_MODELS_FILENAME))
ankiModels = []
with open(filename, 'r') as stream:
with open(filename, "r") as stream:
try:
ankiModels = yaml.safe_load(stream)
return ankiModels
Expand All @@ -45,72 +49,73 @@ def get_anki_models():

return ankiModels


def update_anki_models(ankiModels):
# save ankimodels
with open(str(Path(bundle_dir, 'anki', ANKI_MODELS_FILENAME)), 'w') as outfile:
with open(str(Path(bundle_dir, "anki", ANKI_MODELS_FILENAME)), "w") as outfile:
yaml.dump(ankiModels, outfile, sort_keys=False, default_flow_style=False)
return outfile.name


def fetch_anki_fields(model_names):
for model_name in model_names:
field_names = invoke('modelFieldNames', {'modelName': model_name})
field_names = invoke("modelFieldNames", {"modelName": model_name})
eel.setAnkiFields(model_name, field_names)()


def create_anki_note(note_data):
field_value_map = eel.getFieldValueMap()()
fields = {}
picture_fields = []
audio_fields = []
word_audio_fields = []
for field in field_value_map:
value = field_value_map[field].replace(' ', '').lower()
value = field_value_map[field].replace(" ", "").lower()
if value in note_data:
if (value == 'screenshot'):
if value == "screenshot":
picture_fields.append(field)
elif (value == 'audio'):
elif value == "audio":
audio_fields.append(field)
elif (value == 'wordaudio'):
elif value == "wordaudio":
word_audio_fields.append(field)
else:
fields[field] = note_data[value]
note_params = {
'note': {
'deckName': r_config(ANKI_CONFIG, 'deck'),
'modelName': r_config(ANKI_CONFIG, 'model'),
'fields': fields,
'options': {
"allowDuplicate": True
},
"tags": r_config(ANKI_CONFIG, 'cardtags').split()
"note": {
"deckName": r_config(ANKI_CONFIG, "deck"),
"modelName": r_config(ANKI_CONFIG, "model"),
"fields": fields,
"options": {"allowDuplicate": True},
"tags": r_config(ANKI_CONFIG, "cardtags").split(),
}
}
if (picture_fields):
if picture_fields:
# TODO: check to see if image is already in file and add from path
picture_params = {
'filename': note_data['filename'] + '.' + note_data['imagetype'],
'fields': picture_fields,
'data': note_data['screenshot']
"filename": note_data["filename"] + "." + note_data["imagetype"],
"fields": picture_fields,
"data": note_data["screenshot"],
}
note_params['note']['picture'] = [picture_params]
if (audio_fields):
note_params["note"]["picture"] = [picture_params]
if audio_fields:
audio_params = {
'filename': note_data['audio'],
'fields': audio_fields,
'path': str(Path(AUDIO_LOG_PATH, note_data['folder'], note_data['audio']))
"filename": note_data["audio"],
"fields": audio_fields,
"path": str(Path(AUDIO_LOG_PATH, note_data["folder"], note_data["audio"])),
}
note_params['note']['audio'] = [audio_params]
if (word_audio_fields):
word_audio_url = note_data['wordaudio']
kana = word_audio_url.split('kana=')[1]
note_params["note"]["audio"] = [audio_params]
if word_audio_fields:
word_audio_url = note_data["wordaudio"]
kana = word_audio_url.split("kana=")[1]
word_audio_params = {
'filename': kana + '_' + note_data['filename'] + '.mp3',
'fields': word_audio_fields,
'data': get_jpod_audio_base64(word_audio_url)
"filename": kana + "_" + note_data["filename"] + ".mp3",
"fields": word_audio_fields,
"data": get_jpod_audio_base64(word_audio_url),
}
if 'audio' in note_params['note']:
note_params['note']['audio'].append(word_audio_params)
if "audio" in note_params["note"]:
note_params["note"]["audio"].append(word_audio_params)
else:
note_params['note']['audio'] = [word_audio_params]
result = invoke('addNote', note_params)
note_params["note"]["audio"] = [word_audio_params]
result = invoke("addNote", note_params)
print(result)
return result
return result
42 changes: 26 additions & 16 deletions audio.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
from pydub import AudioSegment
from pydub.playback import play
import pyaudio
import wave
import os
import platform
from shutil import copyfile
from pathlib import Path

import eel
from config import r_config, LOG_CONFIG
from tools import path_to_ffmpeg, path_to_ffmpeg_folder
import pyaudio
from pydub import AudioSegment
from pydub.playback import play

from config import LOG_CONFIG, r_config
from tools import path_to_ffmpeg

AudioSegment.ffmpeg = path_to_ffmpeg()
os.environ["PATH"] += os.pathsep + path_to_ffmpeg_folder()
ffmpeg_path = path_to_ffmpeg()
if ffmpeg_path is not None:
AudioSegment.ffmpeg = ffmpeg_path
os.environ["PATH"] += os.pathsep + str(Path(ffmpeg_path).parent)

# User config device exists? use config device, if not check if (1) valid, use (1), if not no audio

# User config device exists? use config device, if not check if (1) valid, use (1), if not no audio
@eel.expose
def get_recommended_device_index(audio_host):
config_device_name = r_config(LOG_CONFIG, "logaudiodevice")
Expand All @@ -25,9 +29,10 @@ def get_recommended_device_index(audio_host):
return default_device_index
return -1


def get_default_device_index():
p = pyaudio.PyAudio()
#Set default to first in list or ask Windows
# Set default to first in list or ask Windows
try:
default_device_index = p.get_default_input_device_info()
except IOError:
Expand All @@ -36,7 +41,8 @@ def get_default_device_index():
p.terminate()
return info["index"]

#Select Device

# Select Device
@eel.expose
def get_audio_objects():
p = pyaudio.PyAudio()
Expand All @@ -45,20 +51,22 @@ def get_audio_objects():
info = p.get_device_info_by_index(i)
audio_host = p.get_host_api_info_by_index(info["hostApi"])["name"]
if valid_output_device(info["index"]):
audio_objects.setdefault(audio_host,[]).append({info["index"]: info["name"]})
audio_objects.setdefault(audio_host, []).append({info["index"]: info["name"]})
p.terminate()
return audio_objects


def get_audio_device_index_by_name(audio_host, device_name):
p = pyaudio.PyAudio()
for i in range(0, p.get_device_count()):
info = p.get_device_info_by_index(i)
if (info["name"] == device_name and p.get_host_api_info_by_index(info["hostApi"])["name"] == audio_host):
if info["name"] == device_name and p.get_host_api_info_by_index(info["hostApi"])["name"] == audio_host:
p.terminate()
return info["index"]
p.terminate()
return -1


def valid_output_device(deviceIndex):
if not isinstance(deviceIndex, int):
return False
Expand All @@ -67,7 +75,7 @@ def valid_output_device(deviceIndex):
p = pyaudio.PyAudio()
device_info = p.get_device_info_by_index(deviceIndex)
is_input = device_info["maxInputChannels"] > 0
is_windows = (platform.system() == 'Windows')
is_windows = platform.system() == "Windows"
is_wasapi = (p.get_host_api_info_by_index(device_info["hostApi"])["name"]).find("WASAPI") != -1
p.terminate()
if is_input:
Expand All @@ -77,12 +85,14 @@ def valid_output_device(deviceIndex):
return True
return True


def play_audio_from_file(file):
filename, file_extension = os.path.splitext(file)
song = AudioSegment.from_file(file, file_extension[1:])
play(song)
return song.duration_seconds


def convert_audio(in_file, out_file):
filename, file_extension = os.path.splitext(out_file)
AudioSegment.from_mp3(in_file).export(out_file, format=file_extension[1:])
AudioSegment.from_mp3(in_file).export(out_file, format=file_extension[1:])
6 changes: 4 additions & 2 deletions clipboard.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import pyperclip
import eel
import pyperclip

previous_text = ""


def clipboard_to_output():
global previous_text
if not previous_text:
previous_text = eel.getOutputText()()
if previous_text != pyperclip.paste():
parsed_output = pyperclip.paste().replace('\r\n', ' ')
parsed_output = pyperclip.paste().replace("\r\n", " ")
eel.updateOutput(parsed_output)()
previous_text = pyperclip.paste()


def text_to_clipboard(text):
pyperclip.copy(text)
Loading