mirumoji 2.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mirumoji-2.0.1/LICENSE +21 -0
- mirumoji-2.0.1/PKG-INFO +95 -0
- mirumoji-2.0.1/README.md +76 -0
- mirumoji-2.0.1/pyproject.toml +30 -0
- mirumoji-2.0.1/setup.cfg +4 -0
- mirumoji-2.0.1/src/mirumoji/__init__.py +0 -0
- mirumoji-2.0.1/src/mirumoji/launcher.py +605 -0
- mirumoji-2.0.1/src/mirumoji.egg-info/PKG-INFO +95 -0
- mirumoji-2.0.1/src/mirumoji.egg-info/SOURCES.txt +11 -0
- mirumoji-2.0.1/src/mirumoji.egg-info/dependency_links.txt +1 -0
- mirumoji-2.0.1/src/mirumoji.egg-info/entry_points.txt +2 -0
- mirumoji-2.0.1/src/mirumoji.egg-info/requires.txt +2 -0
- mirumoji-2.0.1/src/mirumoji.egg-info/top_level.txt +1 -0
mirumoji-2.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 svdc
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
mirumoji-2.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mirumoji
|
|
3
|
+
Version: 2.0.1
|
|
4
|
+
Summary: CLI launcher for the Mirumoji project, an open-source, self-hostable Japanese language immersion tool.
|
|
5
|
+
Author-email: svdc <svdc1mail@gmail.com>
|
|
6
|
+
Maintainer-email: svdc <svdc1mail@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/svdC1/mirumoji
|
|
9
|
+
Project-URL: Issues, https://github.com/svdC1/mirumoji/issues
|
|
10
|
+
Keywords: japanese,video-player,language,cli,language-learning
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Requires-Dist: click
|
|
17
|
+
Requires-Dist: python-dotenv
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# Overview
|
|
21
|
+
|
|
22
|
+
> Mirumoji is an open-source, self-hosted Japanese immersion toolkit. Drop in any video, anime episode, drama or audio clip and it gives you: clickable tokenized subtitles with dictionary pop-ups, Whisper-powered transcription, instant SRT/clip extraction, and one-click Anki deck export — all in Docker, all running on your own machine.
|
|
23
|
+
|
|
24
|
+
> Optional [`OpenAI`](https://platform.openai.com/docs/overview) Integration - Customizable GPT Breakdowns of Subtitles -
|
|
25
|
+
|
|
26
|
+
> Optional [`Modal`](https://modal.com) Integration - Install a CPU only version and run all GPU tasks on the cloud -
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
# Preview
|
|
31
|
+
|
|
32
|
+
> You can see a preview of the application's frontend _(No backend running)_ [`Here`](https://svdc1.github.io/mirumoji)
|
|
33
|
+
|
|
34
|
+
# Features
|
|
35
|
+
|
|
36
|
+
## **Interactive Video Player**
|
|
37
|
+
|
|
38
|
+

|
|
39
|
+
|
|
40
|
+
> Upload your local anime/J-Drama espisodes or any Japanese video and `.SRT` subtitles.
|
|
41
|
+
|
|
42
|
+
## **Clickable Japanese Subtitles**
|
|
43
|
+
|
|
44
|
+

|
|
45
|
+
|
|
46
|
+
> Subtitles are tokenized ([`kuromoji.js`](https://github.com/takuyaa/kuromoji.js)), allowing you to click individual words with
|
|
47
|
+
> integrated offline JMDict for definitions.
|
|
48
|
+
|
|
49
|
+
## **OpenAI Integration**
|
|
50
|
+
|
|
51
|
+

|
|
52
|
+
|
|
53
|
+
> Modify System Message and User Prompt with `{sentence}` and `{word}` variables.
|
|
54
|
+
|
|
55
|
+
## **Local Media Processing**
|
|
56
|
+
|
|
57
|
+
### **Video Conversion**
|
|
58
|
+
|
|
59
|
+
> Upload videos in various formats; they can be converted to MP4 for optimal playback.
|
|
60
|
+
|
|
61
|
+
### **SRT Generation**
|
|
62
|
+
|
|
63
|
+
> Generate subtitles for your videos. Runs [`FasterWhisper`](https://github.com/SYSTRAN/faster-whisper) with modified parameters to increase accuracy for longer media such as Anime/ J-Drama episodes.
|
|
64
|
+
|
|
65
|
+
## **Audio Transcription**
|
|
66
|
+
|
|
67
|
+

|
|
68
|
+
|
|
69
|
+
> Transcribe Japanese audio from recordings or uploaded files.
|
|
70
|
+
|
|
71
|
+
## **Profile-Based Data Management:**
|
|
72
|
+
|
|
73
|
+
### **Persistent Storage**
|
|
74
|
+
|
|
75
|
+

|
|
76
|
+
|
|
77
|
+
> Profile configurations and all other profile-related data is stored and managed via SQLite database by the backend.
|
|
78
|
+
|
|
79
|
+
### **Clip Saving**
|
|
80
|
+
|
|
81
|
+

|
|
82
|
+
|
|
83
|
+
> Save important video segments with their associated word breakdowns and export as an Anki Deck
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
# Setup
|
|
88
|
+
|
|
89
|
+
> Both the [`frontend`](https://github.com/svdC1/mirumoji/tree/main/apps/frontend) and [`backend`](https://github.com/svdC1/mirumoji/tree/main/apps/backend) have pre-built [`Docker`](https://www.docker.com/) images and are set up to work with Docker Compose.
|
|
90
|
+
|
|
91
|
+
> The easiest setup is to run the [`launcher`](https://github.com/svdC1/mirumoji/tree/main/apps/cli) for your platform. **For detailed instructions please refer to the [`Setup Guide`](https://github.com/svdC1/mirumoji/wiki/Setup-Guide)**
|
|
92
|
+
|
|
93
|
+
> You can also choose to [`build`](https://docs.docker.com/build/) the images locally with the Dockerfiles provided.
|
|
94
|
+
|
|
95
|
+
---
|
mirumoji-2.0.1/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Overview
|
|
2
|
+
|
|
3
|
+
> Mirumoji is an open-source, self-hosted Japanese immersion toolkit. Drop in any video, anime episode, drama or audio clip and it gives you: clickable tokenized subtitles with dictionary pop-ups, Whisper-powered transcription, instant SRT/clip extraction, and one-click Anki deck export — all in Docker, all running on your own machine.
|
|
4
|
+
|
|
5
|
+
> Optional [`OpenAI`](https://platform.openai.com/docs/overview) Integration - Customizable GPT Breakdowns of Subtitles -
|
|
6
|
+
|
|
7
|
+
> Optional [`Modal`](https://modal.com) Integration - Install a CPU only version and run all GPU tasks on the cloud -
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Preview
|
|
12
|
+
|
|
13
|
+
> You can see a preview of the application's frontend _(No backend running)_ [`Here`](https://svdc1.github.io/mirumoji)
|
|
14
|
+
|
|
15
|
+
# Features
|
|
16
|
+
|
|
17
|
+
## **Interactive Video Player**
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+
|
|
21
|
+
> Upload your local anime/J-Drama espisodes or any Japanese video and `.SRT` subtitles.
|
|
22
|
+
|
|
23
|
+
## **Clickable Japanese Subtitles**
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+
|
|
27
|
+
> Subtitles are tokenized ([`kuromoji.js`](https://github.com/takuyaa/kuromoji.js)), allowing you to click individual words with
|
|
28
|
+
> integrated offline JMDict for definitions.
|
|
29
|
+
|
|
30
|
+
## **OpenAI Integration**
|
|
31
|
+
|
|
32
|
+

|
|
33
|
+
|
|
34
|
+
> Modify System Message and User Prompt with `{sentence}` and `{word}` variables.
|
|
35
|
+
|
|
36
|
+
## **Local Media Processing**
|
|
37
|
+
|
|
38
|
+
### **Video Conversion**
|
|
39
|
+
|
|
40
|
+
> Upload videos in various formats; they can be converted to MP4 for optimal playback.
|
|
41
|
+
|
|
42
|
+
### **SRT Generation**
|
|
43
|
+
|
|
44
|
+
> Generate subtitles for your videos. Runs [`FasterWhisper`](https://github.com/SYSTRAN/faster-whisper) with modified parameters to increase accuracy for longer media such as Anime/ J-Drama episodes.
|
|
45
|
+
|
|
46
|
+
## **Audio Transcription**
|
|
47
|
+
|
|
48
|
+

|
|
49
|
+
|
|
50
|
+
> Transcribe Japanese audio from recordings or uploaded files.
|
|
51
|
+
|
|
52
|
+
## **Profile-Based Data Management:**
|
|
53
|
+
|
|
54
|
+
### **Persistent Storage**
|
|
55
|
+
|
|
56
|
+

|
|
57
|
+
|
|
58
|
+
> Profile configurations and all other profile-related data is stored and managed via SQLite database by the backend.
|
|
59
|
+
|
|
60
|
+
### **Clip Saving**
|
|
61
|
+
|
|
62
|
+

|
|
63
|
+
|
|
64
|
+
> Save important video segments with their associated word breakdowns and export as an Anki Deck
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
# Setup
|
|
69
|
+
|
|
70
|
+
> Both the [`frontend`](https://github.com/svdC1/mirumoji/tree/main/apps/frontend) and [`backend`](https://github.com/svdC1/mirumoji/tree/main/apps/backend) have pre-built [`Docker`](https://www.docker.com/) images and are set up to work with Docker Compose.
|
|
71
|
+
|
|
72
|
+
> The easiest setup is to run the [`launcher`](https://github.com/svdC1/mirumoji/tree/main/apps/cli) for your platform. **For detailed instructions please refer to the [`Setup Guide`](https://github.com/svdC1/mirumoji/wiki/Setup-Guide)**
|
|
73
|
+
|
|
74
|
+
> You can also choose to [`build`](https://docs.docker.com/build/) the images locally with the Dockerfiles provided.
|
|
75
|
+
|
|
76
|
+
---
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77.0.3"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "mirumoji"
|
|
7
|
+
version = "2.0.1"
|
|
8
|
+
authors = [{ name = "svdc", email = "svdc1mail@gmail.com" }]
|
|
9
|
+
maintainers = [{ name = "svdc", email = "svdc1mail@gmail.com" }]
|
|
10
|
+
description = "CLI launcher for the Mirumoji project, an open-source, self-hostable Japanese language immersion tool."
|
|
11
|
+
keywords = ["japanese", "video-player", "language", "cli", "language-learning"]
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.9"
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Operating System :: OS Independent",
|
|
17
|
+
]
|
|
18
|
+
license = "MIT"
|
|
19
|
+
license-files = ["LICEN[CS]E*"]
|
|
20
|
+
dependencies = ["click", "python-dotenv"]
|
|
21
|
+
|
|
22
|
+
[project.scripts]
|
|
23
|
+
mirumoji = "mirumoji.launcher:cli"
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://github.com/svdC1/mirumoji"
|
|
27
|
+
Issues = "https://github.com/svdC1/mirumoji/issues"
|
|
28
|
+
|
|
29
|
+
[tool.setuptools.packages.find]
|
|
30
|
+
where = ["src"]
|
mirumoji-2.0.1/setup.cfg
ADDED
|
File without changes
|
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import os
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
import socket
|
|
6
|
+
from textwrap import dedent
|
|
7
|
+
from typing import Optional, List
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from dotenv import dotenv_values, load_dotenv
|
|
10
|
+
|
|
11
|
+
MAIN_REPO_SUBDIR = Path("mirumoji_workspace")
|
|
12
|
+
MAIN_REPO_URL = "https://github.com/svdC1/mirumoji.git"
|
|
13
|
+
# Docker image names for local builds
|
|
14
|
+
FRONTEND_LOCAL_IMAGE_NAME = "mirumoji_frontend_local:latest"
|
|
15
|
+
BACKEND_GPU_LOCAL_IMAGE_NAME = "mirumoji_backend_gpu_local:latest"
|
|
16
|
+
BACKEND_CPU_LOCAL_IMAGE_NAME = "mirumoji_backend_cpu_local:latest"
|
|
17
|
+
|
|
18
|
+
# Relative paths within MAIN_REPO
|
|
19
|
+
FRONTEND_DOCKERFILE_RELPATH = Path("apps/frontend/Dockerfile")
|
|
20
|
+
FRONTEND_BUILD_CONTEXT_RELPATH = Path("apps/frontend")
|
|
21
|
+
BACKEND_GPU_DOCKERFILE_RELPATH = Path("apps/backend/Dockerfile")
|
|
22
|
+
BACKEND_CPU_DOCKERFILE_RELPATH = Path("apps/backend/Dockerfile.cpu")
|
|
23
|
+
BACKEND_BUILD_CONTEXT_RELPATH = Path("apps/backend")
|
|
24
|
+
|
|
25
|
+
COMPOSE_PREBUILT_CPU_RELPATH = Path("compose/docker-compose.cpu.yaml")
|
|
26
|
+
COMPOSE_PREBUILT_GPU_RELPATH = Path("compose/docker-compose.gpu.yaml")
|
|
27
|
+
COMPOSE_PREBUILT_DOCKER_GPU_RELPATH = Path(
|
|
28
|
+
"compose/docker-compose.gpu.dockerpull.yaml")
|
|
29
|
+
COMPOSE_PREBUILT_DOCKER_CPU_RELPATH = Path(
|
|
30
|
+
"compose/docker-compose.cpu.dockerpull.yaml")
|
|
31
|
+
COMPOSE_LOCAL_CPU_RELPATH = Path("compose/docker-compose.local.cpu.yaml")
|
|
32
|
+
COMPOSE_LOCAL_GPU_RELPATH = Path("compose/docker-compose.local.gpu.yaml")
|
|
33
|
+
ENV_FILE_NAME = ".env"
|
|
34
|
+
|
|
35
|
+
# --- Help Messages ---
|
|
36
|
+
BUILD_HELP = "Build Docker images locally (--build) or pull pre-built \
|
|
37
|
+
images from registry. (--pull)"
|
|
38
|
+
|
|
39
|
+
GPU_HELP = "Use GPU Version of Backend (--gpu) or CPU version (--cpu)"
|
|
40
|
+
REG_HELP = "Pull Images from GitHub Registry (--github-pull) or \
|
|
41
|
+
from Docker Hub (--docker-pull)"
|
|
42
|
+
# --- Helper Functions ---
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_host_lan_ip():
|
|
46
|
+
"""
|
|
47
|
+
Returns the primary LAN IPv4 address of the host machine
|
|
48
|
+
and loads it as an environment variable
|
|
49
|
+
"""
|
|
50
|
+
click.secho("\n--- Getting HOST IPv4 ---", fg="blue")
|
|
51
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
52
|
+
try:
|
|
53
|
+
s.connect(('8.8.8.8', 80))
|
|
54
|
+
ip = s.getsockname()[0]
|
|
55
|
+
click.secho(f"HOST IPv4 Acquired : {ip}", fg="bright_green")
|
|
56
|
+
os.environ["HOST_LAN_IP"] = ip
|
|
57
|
+
return ip
|
|
58
|
+
except Exception as e:
|
|
59
|
+
click.secho(
|
|
60
|
+
f"Error Getting Host IPv4 Address: {e}",
|
|
61
|
+
fg="red",
|
|
62
|
+
err=True
|
|
63
|
+
)
|
|
64
|
+
sys.exit(1)
|
|
65
|
+
finally:
|
|
66
|
+
s.close()
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def run_command(command_list: List,
|
|
70
|
+
cwd: Optional[Path] = None,
|
|
71
|
+
check: bool = True,
|
|
72
|
+
shell: bool = False
|
|
73
|
+
):
|
|
74
|
+
"""
|
|
75
|
+
Runs a command, streams output, and handles errors.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
if not shell and not isinstance(command_list, list):
|
|
79
|
+
click.secho(
|
|
80
|
+
message="'command_list' must be a list for non-shell commands.",
|
|
81
|
+
fg='red',
|
|
82
|
+
err=True
|
|
83
|
+
)
|
|
84
|
+
sys.exit(1)
|
|
85
|
+
if shell:
|
|
86
|
+
cmd_str = command_list
|
|
87
|
+
else:
|
|
88
|
+
cmd_str = " ".join(map(str, command_list))
|
|
89
|
+
if not cwd:
|
|
90
|
+
cwd = Path.cwd()
|
|
91
|
+
|
|
92
|
+
click.secho(message=f"CWD: {cwd}",
|
|
93
|
+
fg='cyan')
|
|
94
|
+
click.secho(message=f"Running command: '{cmd_str}'",
|
|
95
|
+
fg='cyan')
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
process = subprocess.Popen(
|
|
99
|
+
args=command_list,
|
|
100
|
+
cwd=cwd,
|
|
101
|
+
stdout=subprocess.PIPE,
|
|
102
|
+
stderr=subprocess.STDOUT,
|
|
103
|
+
text=True,
|
|
104
|
+
shell=shell,
|
|
105
|
+
bufsize=1,
|
|
106
|
+
universal_newlines=True,
|
|
107
|
+
encoding='utf-8',
|
|
108
|
+
errors='replace'
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# Stream output
|
|
112
|
+
if process.stdout:
|
|
113
|
+
for line in iter(process.stdout.readline, ''):
|
|
114
|
+
line_content = line.rstrip('\r\n')
|
|
115
|
+
if line_content:
|
|
116
|
+
click.secho(message=f" ↪ {line_content}",
|
|
117
|
+
fg="cyan"
|
|
118
|
+
)
|
|
119
|
+
else:
|
|
120
|
+
click.echo("")
|
|
121
|
+
process.stdout.close()
|
|
122
|
+
|
|
123
|
+
return_code = process.wait()
|
|
124
|
+
if check and return_code != 0:
|
|
125
|
+
# Error message printed after all output has been streamed
|
|
126
|
+
raise subprocess.CalledProcessError(return_code, cmd_str)
|
|
127
|
+
return process
|
|
128
|
+
except subprocess.CalledProcessError as e:
|
|
129
|
+
message = dedent(f"""\
|
|
130
|
+
Error: Command '{e.cmd}' returned non-zero exit status "
|
|
131
|
+
{e.returncode}.
|
|
132
|
+
""")
|
|
133
|
+
click.secho(message=message, fg="red", err=True)
|
|
134
|
+
sys.exit(e.returncode or 1)
|
|
135
|
+
|
|
136
|
+
except FileNotFoundError as e:
|
|
137
|
+
message = dedent(f"""\
|
|
138
|
+
Error: Command not found: {e.filename}.
|
|
139
|
+
Please ensure it's installed and in your PATH.
|
|
140
|
+
""")
|
|
141
|
+
click.secho(message=message, fg='red', err=True)
|
|
142
|
+
sys.exit(1)
|
|
143
|
+
except Exception as e:
|
|
144
|
+
message = dedent(f"""\
|
|
145
|
+
An unexpected error occurred while trying to run command '{cmd_str}':
|
|
146
|
+
{e}
|
|
147
|
+
""")
|
|
148
|
+
click.secho(message=message, fg='red', err=True)
|
|
149
|
+
sys.exit(1)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def ensure_repo(repo_url: str,
|
|
153
|
+
repo_path: Path):
|
|
154
|
+
"""
|
|
155
|
+
Ensures the repository is cloned or updated.
|
|
156
|
+
"""
|
|
157
|
+
if not repo_path.is_dir():
|
|
158
|
+
click.secho(
|
|
159
|
+
message=f"Cloning repository: {repo_url} into {repo_path}...",
|
|
160
|
+
fg="green"
|
|
161
|
+
)
|
|
162
|
+
run_command(["git", "clone", repo_url, str(repo_path)])
|
|
163
|
+
else:
|
|
164
|
+
click.secho(
|
|
165
|
+
message=f"Repo {repo_path} already exists. Fetching updates...",
|
|
166
|
+
fg="green"
|
|
167
|
+
)
|
|
168
|
+
run_command(["git", "fetch", "--all"],
|
|
169
|
+
cwd=repo_path)
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
git_rev_parse_cmd = ["git", "rev-parse", "--abbrev-ref", "HEAD"]
|
|
173
|
+
result = subprocess.run(
|
|
174
|
+
git_rev_parse_cmd,
|
|
175
|
+
cwd=repo_path,
|
|
176
|
+
text=True,
|
|
177
|
+
capture_output=True,
|
|
178
|
+
check=True
|
|
179
|
+
)
|
|
180
|
+
current_branch = result.stdout.strip()
|
|
181
|
+
except subprocess.CalledProcessError:
|
|
182
|
+
current_branch = "HEAD"
|
|
183
|
+
|
|
184
|
+
if current_branch == "HEAD":
|
|
185
|
+
click.secho(
|
|
186
|
+
message="Currently in a detached HEAD state.",
|
|
187
|
+
fg="yellow"
|
|
188
|
+
)
|
|
189
|
+
click.secho(
|
|
190
|
+
message="Attempting to checkout default branch (main)...",
|
|
191
|
+
fg="yellow"
|
|
192
|
+
)
|
|
193
|
+
# Try checking out 'main'
|
|
194
|
+
checkout_main_cmd = ["git", "checkout", "main"]
|
|
195
|
+
run_command(checkout_main_cmd,
|
|
196
|
+
cwd=repo_path,
|
|
197
|
+
check=True)
|
|
198
|
+
|
|
199
|
+
result = subprocess.run(
|
|
200
|
+
git_rev_parse_cmd,
|
|
201
|
+
cwd=repo_path,
|
|
202
|
+
text=True,
|
|
203
|
+
capture_output=True,
|
|
204
|
+
check=True
|
|
205
|
+
)
|
|
206
|
+
current_branch = result.stdout.strip()
|
|
207
|
+
|
|
208
|
+
click.secho(
|
|
209
|
+
message=f"Pulling latest changes for branch '{current_branch}'...",
|
|
210
|
+
fg="green"
|
|
211
|
+
)
|
|
212
|
+
run_command(["git", "pull", "origin", current_branch], cwd=repo_path)
|
|
213
|
+
click.secho("Repository setup complete.", fg="bright_green")
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def check_env_file(expected_vars: List,
|
|
217
|
+
env_file_path: Path):
|
|
218
|
+
"""
|
|
219
|
+
Checks for .env file and required variables.
|
|
220
|
+
"""
|
|
221
|
+
click.secho(
|
|
222
|
+
message=f"Checking for {ENV_FILE_NAME} file at: {env_file_path}",
|
|
223
|
+
fg="green"
|
|
224
|
+
)
|
|
225
|
+
if not env_file_path.is_file():
|
|
226
|
+
message = dedent(f"""\
|
|
227
|
+
Error: {ENV_FILE_NAME} file not found at {env_file_path}.
|
|
228
|
+
Please create it with the variables: {', '.join(expected_vars)}
|
|
229
|
+
""")
|
|
230
|
+
click.secho(message=message, fg="red", err=True)
|
|
231
|
+
sys.exit(1)
|
|
232
|
+
|
|
233
|
+
click.secho(f"Loading variables from {ENV_FILE_NAME}...", fg="green")
|
|
234
|
+
env_config = dotenv_values(env_file_path)
|
|
235
|
+
missing_vars = [var for var in expected_vars if not env_config.get(var)]
|
|
236
|
+
|
|
237
|
+
if missing_vars:
|
|
238
|
+
message = dedent(f"""\
|
|
239
|
+
Error: Missing or empty variables in {env_file_path}:
|
|
240
|
+
{', '.join(missing_vars)}.
|
|
241
|
+
Please ensure all required variables are set:
|
|
242
|
+
{', '.join(expected_vars)}
|
|
243
|
+
""")
|
|
244
|
+
click.secho(message=message, fg="red", err=True)
|
|
245
|
+
sys.exit(1)
|
|
246
|
+
load_dotenv(dotenv_path=env_file_path)
|
|
247
|
+
click.secho(message="Variable Configuration Passed", fg="bright_green")
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def get_build_locally():
|
|
251
|
+
"""
|
|
252
|
+
Asks for user input on whether to build images locally or pull
|
|
253
|
+
pre-built images.
|
|
254
|
+
"""
|
|
255
|
+
try:
|
|
256
|
+
click.secho("\n--- Build Configuration ---", fg="blue")
|
|
257
|
+
build_locally = click.confirm(
|
|
258
|
+
text="Build Docker images locally?",
|
|
259
|
+
default=False
|
|
260
|
+
)
|
|
261
|
+
click.secho(
|
|
262
|
+
f"\nSelected:\nBuild Locally: {build_locally}",
|
|
263
|
+
fg="blue"
|
|
264
|
+
)
|
|
265
|
+
return build_locally
|
|
266
|
+
|
|
267
|
+
except Exception as e:
|
|
268
|
+
click.secho(
|
|
269
|
+
f"Error while selecting configuration options: {e}",
|
|
270
|
+
fg="red",
|
|
271
|
+
err=True
|
|
272
|
+
)
|
|
273
|
+
sys.exit(1)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def get_gpu_cpu():
|
|
277
|
+
"""
|
|
278
|
+
Asks for user input on whether to use CPU or GPU version.
|
|
279
|
+
"""
|
|
280
|
+
try:
|
|
281
|
+
click.secho("\n--- Backend Configuration ---", fg="blue")
|
|
282
|
+
use_gpu = click.confirm(
|
|
283
|
+
text="Run Local GPU version of the backend (NVIDIA GPU required)?",
|
|
284
|
+
default=False
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
click.secho(
|
|
288
|
+
f"\nSelected:\nUse GPU: {use_gpu}",
|
|
289
|
+
fg="blue"
|
|
290
|
+
)
|
|
291
|
+
return use_gpu
|
|
292
|
+
|
|
293
|
+
except Exception as e:
|
|
294
|
+
click.secho(
|
|
295
|
+
f"Error while selecting configuration options: {e}",
|
|
296
|
+
fg="red",
|
|
297
|
+
err=True
|
|
298
|
+
)
|
|
299
|
+
sys.exit(1)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def get_registry():
|
|
303
|
+
try:
|
|
304
|
+
click.secho("\n--- Registry Configuration ---", fg="blue")
|
|
305
|
+
pull_registry = click.confirm(
|
|
306
|
+
text="Pull from GitHub Registry ? (N = Pull from DockerHub)",
|
|
307
|
+
default=False
|
|
308
|
+
)
|
|
309
|
+
if pull_registry:
|
|
310
|
+
reg = "GitHub"
|
|
311
|
+
else:
|
|
312
|
+
reg = "DockerHub"
|
|
313
|
+
click.secho(
|
|
314
|
+
f"\nSelected:\nRegistry:{reg}",
|
|
315
|
+
fg="blue"
|
|
316
|
+
)
|
|
317
|
+
return reg
|
|
318
|
+
|
|
319
|
+
except Exception as e:
|
|
320
|
+
click.secho(
|
|
321
|
+
f"Error while selecting registry configuration options: {e}",
|
|
322
|
+
fg="red",
|
|
323
|
+
err=True
|
|
324
|
+
)
|
|
325
|
+
sys.exit(1)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def build_imgs_locally(use_gpu: bool):
|
|
329
|
+
"""
|
|
330
|
+
Runs the docker build command for the frontend and backend
|
|
331
|
+
images based on whether CPU or GPU version is chosen.
|
|
332
|
+
"""
|
|
333
|
+
try:
|
|
334
|
+
click.secho("\n--- Building Docker Images ---", fg="blue")
|
|
335
|
+
click.secho("\nBuilding frontend image...", fg="green")
|
|
336
|
+
frontend_build_cmd = [
|
|
337
|
+
"docker",
|
|
338
|
+
"build",
|
|
339
|
+
"-t",
|
|
340
|
+
FRONTEND_LOCAL_IMAGE_NAME,
|
|
341
|
+
"-f",
|
|
342
|
+
str(FRONTEND_DOCKERFILE_RELPATH),
|
|
343
|
+
str(FRONTEND_BUILD_CONTEXT_RELPATH)
|
|
344
|
+
]
|
|
345
|
+
run_command(frontend_build_cmd)
|
|
346
|
+
click.secho("Frontend image build complete.", fg="bright_green")
|
|
347
|
+
|
|
348
|
+
if use_gpu:
|
|
349
|
+
click.secho("Building GPU backend image...", fg="green")
|
|
350
|
+
backend_image_name = BACKEND_GPU_LOCAL_IMAGE_NAME
|
|
351
|
+
backend_dockerfile_relpath = BACKEND_GPU_DOCKERFILE_RELPATH
|
|
352
|
+
else:
|
|
353
|
+
click.secho("Building CPU backend image...", fg="green")
|
|
354
|
+
backend_image_name = BACKEND_CPU_LOCAL_IMAGE_NAME
|
|
355
|
+
backend_dockerfile_relpath = BACKEND_CPU_DOCKERFILE_RELPATH
|
|
356
|
+
|
|
357
|
+
backend_build_cmd = [
|
|
358
|
+
"docker",
|
|
359
|
+
"build",
|
|
360
|
+
"-t",
|
|
361
|
+
backend_image_name,
|
|
362
|
+
"-f",
|
|
363
|
+
str(backend_dockerfile_relpath),
|
|
364
|
+
str(BACKEND_BUILD_CONTEXT_RELPATH)
|
|
365
|
+
]
|
|
366
|
+
run_command(backend_build_cmd)
|
|
367
|
+
click.secho("\nBackend image build complete.", fg="bright_green")
|
|
368
|
+
|
|
369
|
+
except Exception as e:
|
|
370
|
+
click.secho(
|
|
371
|
+
f"Error while building local images: {e}",
|
|
372
|
+
fg="red",
|
|
373
|
+
err=True
|
|
374
|
+
)
|
|
375
|
+
sys.exit(1)
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def configure_repo():
|
|
379
|
+
"""
|
|
380
|
+
Displays a header, sets up the local repository path, clones or
|
|
381
|
+
updates the repository, sets the working directory to the local repository
|
|
382
|
+
path and returns both the local repository path and original working
|
|
383
|
+
directory.
|
|
384
|
+
"""
|
|
385
|
+
try:
|
|
386
|
+
click.secho("--- Mirumoji Launcher ---", fg="magenta")
|
|
387
|
+
current_user_cwd = Path.cwd()
|
|
388
|
+
repo_path = current_user_cwd / MAIN_REPO_SUBDIR
|
|
389
|
+
ensure_repo(MAIN_REPO_URL, repo_path)
|
|
390
|
+
original_cwd = current_user_cwd
|
|
391
|
+
# All subsequent paths are relative to repo_path
|
|
392
|
+
os.chdir(repo_path)
|
|
393
|
+
click.secho(message=f"Changed working directory to: {repo_path}",
|
|
394
|
+
fg="blue")
|
|
395
|
+
return (repo_path, original_cwd)
|
|
396
|
+
|
|
397
|
+
except Exception as e:
|
|
398
|
+
click.secho(
|
|
399
|
+
f"Error while configuring repository: {e}",
|
|
400
|
+
fg="red",
|
|
401
|
+
err=True
|
|
402
|
+
)
|
|
403
|
+
sys.exit(1)
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
# --- Click CLI structure ---
|
|
407
|
+
@click.group()
|
|
408
|
+
def cli():
|
|
409
|
+
"""Mirumoji Launcher: Setup and run Mirumoji with Docker."""
|
|
410
|
+
pass
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
@cli.command()
|
|
414
|
+
@click.option('--build/--pull',
|
|
415
|
+
default=None,
|
|
416
|
+
help=BUILD_HELP)
|
|
417
|
+
@click.option("--gpu/--cpu",
|
|
418
|
+
default=None,
|
|
419
|
+
help=GPU_HELP)
|
|
420
|
+
@click.option("--github-pull/--docker-pull",
|
|
421
|
+
default=None,
|
|
422
|
+
help=REG_HELP)
|
|
423
|
+
@click.option('--no-clear',
|
|
424
|
+
is_flag=True,
|
|
425
|
+
default=False,
|
|
426
|
+
help="Do not clear the terminal after each step")
|
|
427
|
+
def launch(build, gpu, github_pull, no_clear):
|
|
428
|
+
"""
|
|
429
|
+
Guides through setup, image building (optional), and running Mirumoji.
|
|
430
|
+
"""
|
|
431
|
+
# --- Option Config ---
|
|
432
|
+
try:
|
|
433
|
+
if build is None:
|
|
434
|
+
build_locally = get_build_locally()
|
|
435
|
+
if not no_clear:
|
|
436
|
+
click.clear()
|
|
437
|
+
else:
|
|
438
|
+
build_locally = build
|
|
439
|
+
if gpu is None:
|
|
440
|
+
use_gpu = get_gpu_cpu()
|
|
441
|
+
if not no_clear:
|
|
442
|
+
click.clear()
|
|
443
|
+
else:
|
|
444
|
+
use_gpu = gpu
|
|
445
|
+
except Exception as e:
|
|
446
|
+
click.secho(
|
|
447
|
+
f"\nError while configuring options: {e}",
|
|
448
|
+
fg="red",
|
|
449
|
+
err=True
|
|
450
|
+
)
|
|
451
|
+
sys.exit(1)
|
|
452
|
+
|
|
453
|
+
repo_path, original_cwd = configure_repo()
|
|
454
|
+
if not no_clear:
|
|
455
|
+
click.clear()
|
|
456
|
+
try:
|
|
457
|
+
if build_locally:
|
|
458
|
+
build_imgs_locally(use_gpu=use_gpu)
|
|
459
|
+
if not no_clear:
|
|
460
|
+
click.clear()
|
|
461
|
+
else:
|
|
462
|
+
if github_pull is None:
|
|
463
|
+
registry = get_registry()
|
|
464
|
+
if not no_clear:
|
|
465
|
+
click.clear()
|
|
466
|
+
else:
|
|
467
|
+
if github_pull:
|
|
468
|
+
reg = "GitHub"
|
|
469
|
+
else:
|
|
470
|
+
reg = "DockerHub"
|
|
471
|
+
registry = reg
|
|
472
|
+
click.secho("\nUsing pre-built images.", fg='green')
|
|
473
|
+
|
|
474
|
+
click.secho(f"\n--- Checking {ENV_FILE_NAME} File ---", fg="blue")
|
|
475
|
+
env_file_abs_path = original_cwd / ENV_FILE_NAME
|
|
476
|
+
required_env_vars = ["OPENAI_API_KEY"]
|
|
477
|
+
|
|
478
|
+
if not use_gpu:
|
|
479
|
+
# CPU version requires Modal keys
|
|
480
|
+
required_env_vars.extend(["MODAL_TOKEN_ID", "MODAL_TOKEN_SECRET"])
|
|
481
|
+
check_env_file(required_env_vars,
|
|
482
|
+
env_file_abs_path)
|
|
483
|
+
if not no_clear:
|
|
484
|
+
click.clear()
|
|
485
|
+
# Get Host IPv4
|
|
486
|
+
HOST_LAN_IP = get_host_lan_ip()
|
|
487
|
+
if not no_clear:
|
|
488
|
+
click.clear()
|
|
489
|
+
|
|
490
|
+
if build_locally:
|
|
491
|
+
if use_gpu:
|
|
492
|
+
compose_file_relpath = COMPOSE_LOCAL_GPU_RELPATH
|
|
493
|
+
else:
|
|
494
|
+
compose_file_relpath = COMPOSE_LOCAL_CPU_RELPATH
|
|
495
|
+
else:
|
|
496
|
+
if use_gpu:
|
|
497
|
+
if registry == "DockerHub":
|
|
498
|
+
compose_file_relpath = COMPOSE_PREBUILT_DOCKER_GPU_RELPATH
|
|
499
|
+
else:
|
|
500
|
+
compose_file_relpath = COMPOSE_PREBUILT_GPU_RELPATH
|
|
501
|
+
else:
|
|
502
|
+
if registry == "DockerHub":
|
|
503
|
+
compose_file_relpath = COMPOSE_PREBUILT_DOCKER_CPU_RELPATH
|
|
504
|
+
else:
|
|
505
|
+
compose_file_relpath = COMPOSE_PREBUILT_CPU_RELPATH
|
|
506
|
+
|
|
507
|
+
click.secho("\n--- Running Docker Compose ---", fg="blue")
|
|
508
|
+
click.secho(f"Using compose file: {compose_file_relpath}",
|
|
509
|
+
fg="bright_magenta")
|
|
510
|
+
docker_compose_cmd = [
|
|
511
|
+
"docker",
|
|
512
|
+
"compose",
|
|
513
|
+
"-f",
|
|
514
|
+
str(compose_file_relpath),
|
|
515
|
+
"-p",
|
|
516
|
+
"mirumoji",
|
|
517
|
+
"up",
|
|
518
|
+
"-d"
|
|
519
|
+
]
|
|
520
|
+
run_command(docker_compose_cmd)
|
|
521
|
+
if not no_clear:
|
|
522
|
+
click.clear()
|
|
523
|
+
stop_instructions = dedent(f"""\
|
|
524
|
+
|
|
525
|
+
--- Accessible at ---
|
|
526
|
+
|
|
527
|
+
Local: 'https://localhost'
|
|
528
|
+
|
|
529
|
+
LAN: 'https://{HOST_LAN_IP}'
|
|
530
|
+
|
|
531
|
+
--- Launcher Stop Command ---
|
|
532
|
+
|
|
533
|
+
launcher shutdown
|
|
534
|
+
|
|
535
|
+
--- Docker Stop Command ---
|
|
536
|
+
|
|
537
|
+
docker compose -p mirumoji down
|
|
538
|
+
""")
|
|
539
|
+
click.secho(message=stop_instructions, fg="bright_green")
|
|
540
|
+
|
|
541
|
+
except Exception as e:
|
|
542
|
+
click.secho(
|
|
543
|
+
f"An unexpected error occurred during the launch process: {e}",
|
|
544
|
+
fg="red",
|
|
545
|
+
err=True
|
|
546
|
+
)
|
|
547
|
+
sys.exit(1)
|
|
548
|
+
finally:
|
|
549
|
+
os.chdir(original_cwd)
|
|
550
|
+
click.secho(f"Returned to original working directory: {original_cwd}",
|
|
551
|
+
fg="blue")
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
@cli.command()
|
|
555
|
+
@click.option("--clean/--no-clean",
|
|
556
|
+
default=None,
|
|
557
|
+
help="Delete Docker Volumes")
|
|
558
|
+
@click.option('--no-clear',
|
|
559
|
+
is_flag=True,
|
|
560
|
+
default=False,
|
|
561
|
+
help="Do not clear the terminal after each step")
|
|
562
|
+
def shutdown(clean, no_clear):
|
|
563
|
+
"""
|
|
564
|
+
Runs docker compose down on application.
|
|
565
|
+
"""
|
|
566
|
+
repo_path, original_cwd = configure_repo()
|
|
567
|
+
if not no_clear:
|
|
568
|
+
click.clear()
|
|
569
|
+
if clean is None:
|
|
570
|
+
delete_volumes = click.confirm(
|
|
571
|
+
text="Delete Data (Docker Volumes) ?",
|
|
572
|
+
default=False
|
|
573
|
+
)
|
|
574
|
+
if not no_clear:
|
|
575
|
+
click.clear()
|
|
576
|
+
else:
|
|
577
|
+
delete_volumes = clean
|
|
578
|
+
try:
|
|
579
|
+
cmd = ['docker',
|
|
580
|
+
'compose',
|
|
581
|
+
'-p',
|
|
582
|
+
'mirumoji',
|
|
583
|
+
"down"
|
|
584
|
+
]
|
|
585
|
+
if delete_volumes:
|
|
586
|
+
cmd.append("-v")
|
|
587
|
+
run_command(cmd, cwd=repo_path)
|
|
588
|
+
if not no_clear:
|
|
589
|
+
click.clear()
|
|
590
|
+
click.secho(message="All Services Stopped.", fg="bright_green")
|
|
591
|
+
except Exception as e:
|
|
592
|
+
click.secho(
|
|
593
|
+
f"An unexpected error occurred during shutdown: {e}",
|
|
594
|
+
fg="red",
|
|
595
|
+
err=True
|
|
596
|
+
)
|
|
597
|
+
sys.exit(1)
|
|
598
|
+
finally:
|
|
599
|
+
os.chdir(original_cwd)
|
|
600
|
+
click.secho(f"Returned to original working directory: {original_cwd}",
|
|
601
|
+
fg="blue")
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
if __name__ == '__main__':
|
|
605
|
+
cli()
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mirumoji
|
|
3
|
+
Version: 2.0.1
|
|
4
|
+
Summary: CLI launcher for the Mirumoji project, an open-source, self-hostable Japanese language immersion tool.
|
|
5
|
+
Author-email: svdc <svdc1mail@gmail.com>
|
|
6
|
+
Maintainer-email: svdc <svdc1mail@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/svdC1/mirumoji
|
|
9
|
+
Project-URL: Issues, https://github.com/svdC1/mirumoji/issues
|
|
10
|
+
Keywords: japanese,video-player,language,cli,language-learning
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Requires-Dist: click
|
|
17
|
+
Requires-Dist: python-dotenv
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# Overview
|
|
21
|
+
|
|
22
|
+
> Mirumoji is an open-source, self-hosted Japanese immersion toolkit. Drop in any video, anime episode, drama or audio clip and it gives you: clickable tokenized subtitles with dictionary pop-ups, Whisper-powered transcription, instant SRT/clip extraction, and one-click Anki deck export — all in Docker, all running on your own machine.
|
|
23
|
+
|
|
24
|
+
> Optional [`OpenAI`](https://platform.openai.com/docs/overview) Integration - Customizable GPT Breakdowns of Subtitles -
|
|
25
|
+
|
|
26
|
+
> Optional [`Modal`](https://modal.com) Integration - Install a CPU only version and run all GPU tasks on the cloud -
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
# Preview
|
|
31
|
+
|
|
32
|
+
> You can see a preview of the application's frontend _(No backend running)_ [`Here`](https://svdc1.github.io/mirumoji)
|
|
33
|
+
|
|
34
|
+
# Features
|
|
35
|
+
|
|
36
|
+
## **Interactive Video Player**
|
|
37
|
+
|
|
38
|
+

|
|
39
|
+
|
|
40
|
+
> Upload your local anime/J-Drama espisodes or any Japanese video and `.SRT` subtitles.
|
|
41
|
+
|
|
42
|
+
## **Clickable Japanese Subtitles**
|
|
43
|
+
|
|
44
|
+

|
|
45
|
+
|
|
46
|
+
> Subtitles are tokenized ([`kuromoji.js`](https://github.com/takuyaa/kuromoji.js)), allowing you to click individual words with
|
|
47
|
+
> integrated offline JMDict for definitions.
|
|
48
|
+
|
|
49
|
+
## **OpenAI Integration**
|
|
50
|
+
|
|
51
|
+

|
|
52
|
+
|
|
53
|
+
> Modify System Message and User Prompt with `{sentence}` and `{word}` variables.
|
|
54
|
+
|
|
55
|
+
## **Local Media Processing**
|
|
56
|
+
|
|
57
|
+
### **Video Conversion**
|
|
58
|
+
|
|
59
|
+
> Upload videos in various formats; they can be converted to MP4 for optimal playback.
|
|
60
|
+
|
|
61
|
+
### **SRT Generation**
|
|
62
|
+
|
|
63
|
+
> Generate subtitles for your videos. Runs [`FasterWhisper`](https://github.com/SYSTRAN/faster-whisper) with modified parameters to increase accuracy for longer media such as Anime/ J-Drama episodes.
|
|
64
|
+
|
|
65
|
+
## **Audio Transcription**
|
|
66
|
+
|
|
67
|
+

|
|
68
|
+
|
|
69
|
+
> Transcribe Japanese audio from recordings or uploaded files.
|
|
70
|
+
|
|
71
|
+
## **Profile-Based Data Management:**
|
|
72
|
+
|
|
73
|
+
### **Persistent Storage**
|
|
74
|
+
|
|
75
|
+

|
|
76
|
+
|
|
77
|
+
> Profile configurations and all other profile-related data is stored and managed via SQLite database by the backend.
|
|
78
|
+
|
|
79
|
+
### **Clip Saving**
|
|
80
|
+
|
|
81
|
+

|
|
82
|
+
|
|
83
|
+
> Save important video segments with their associated word breakdowns and export as an Anki Deck
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
# Setup
|
|
88
|
+
|
|
89
|
+
> Both the [`frontend`](https://github.com/svdC1/mirumoji/tree/main/apps/frontend) and [`backend`](https://github.com/svdC1/mirumoji/tree/main/apps/backend) have pre-built [`Docker`](https://www.docker.com/) images and are set up to work with Docker Compose.
|
|
90
|
+
|
|
91
|
+
> The easiest setup is to run the [`launcher`](https://github.com/svdC1/mirumoji/tree/main/apps/cli) for your platform. **For detailed instructions please refer to the [`Setup Guide`](https://github.com/svdC1/mirumoji/wiki/Setup-Guide)**
|
|
92
|
+
|
|
93
|
+
> You can also choose to [`build`](https://docs.docker.com/build/) the images locally with the Dockerfiles provided.
|
|
94
|
+
|
|
95
|
+
---
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/mirumoji/__init__.py
|
|
5
|
+
src/mirumoji/launcher.py
|
|
6
|
+
src/mirumoji.egg-info/PKG-INFO
|
|
7
|
+
src/mirumoji.egg-info/SOURCES.txt
|
|
8
|
+
src/mirumoji.egg-info/dependency_links.txt
|
|
9
|
+
src/mirumoji.egg-info/entry_points.txt
|
|
10
|
+
src/mirumoji.egg-info/requires.txt
|
|
11
|
+
src/mirumoji.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
mirumoji
|