Profile
profile
¶
This module defines the profile_router of the Mirumoji API
Covers a profile's LLM template, saved clips, files, transcripts, and Anki
deck export, all scoped to the active profile via X-Profile-ID
Attributes:
| Name | Type | Description |
|---|---|---|
LOGGER |
Logger
|
Module's logging object |
profile_router |
APIRouter
|
The FastAPI router object |
delete_clip(clip_id, profile_id=Depends(ensure_profile_exists))
async
¶
Deletes one of the active profile's saved clips and its file
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
clip_id
|
UUID
|
Id of the clip to delete |
required |
profile_id
|
str
|
Validated profile id |
Depends(ensure_profile_exists)
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A confirmation payload |
Raises:
| Type | Description |
|---|---|
RecordNotFoundError
|
If the clip doesn't exist or isn't owned by the profile |
StorageError
|
If deleting the clip file fails |
DatabaseError
|
If the deletion fails |
delete_file(file_id, profile_id=Depends(ensure_profile_exists), manager=Depends(get_job_manager))
async
¶
Deletes one of the active profile's files
Source Of Truth
-
Files are the single source of truth for a profile's artifacts. A job that used or produced this file is meaningless without it, so those jobs are cascade-deleted first (a batch parent takes its children)
-
Deleting a file also cascades (via foreign keys) to any transcripts or clips that reference it
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file_id
|
UUID
|
Id of the file to delete |
required |
profile_id
|
str
|
Validated profile id |
Depends(ensure_profile_exists)
|
manager
|
JobQueueManager
|
The job worker |
Depends(get_job_manager)
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A confirmation payload |
Raises:
| Type | Description |
|---|---|
HTTPException
|
If a job still being run by the worker uses this file |
RecordNotFoundError
|
If the file doesn't exist or isn't owned by the profile |
StorageError
|
If deleting the file fails |
DatabaseError
|
If the deletion fails |
delete_template(profile_id=Depends(ensure_profile_exists))
async
¶
Deletes the active profile's LLM template
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
profile_id
|
str
|
Validated profile id |
Depends(ensure_profile_exists)
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A confirmation payload |
Raises:
| Type | Description |
|---|---|
RecordNotFoundError
|
If the profile has no template |
DatabaseError
|
If the deletion fails |
delete_transcript(transcript_id, profile_id=Depends(ensure_profile_exists), manager=Depends(get_job_manager))
async
¶
Deletes one of the active profile's transcripts
A transcribe job that produced this transcript is meaningless without it, so those jobs are cascade-deleted first
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
transcript_id
|
UUID
|
Id of the transcript to delete |
required |
profile_id
|
str
|
Validated profile id |
Depends(ensure_profile_exists)
|
manager
|
JobQueueManager
|
The job worker |
Depends(get_job_manager)
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A confirmation payload |
Raises:
| Type | Description |
|---|---|
HTTPException
|
If a job still being run by the worker produced this transcript |
RecordNotFoundError
|
If the transcript doesn't exist or isn't owned by the profile |
DatabaseError
|
If the deletion fails |
export_anki_deck(profile_id=Depends(ensure_profile_exists))
async
¶
Exports the active profile's saved clips as an Anki deck
Builds one card per saved clip from its stored breakdown payload (focus
word, meanings, sentence, and explanation), bundles the clip media, and
writes the .apkg under the profile
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
profile_id
|
str
|
Validated profile id |
Depends(ensure_profile_exists)
|
Returns:
| Type | Description |
|---|---|
AnkiExportResponse
|
The media URL serving the exported Anki deck |
Raises:
| Type | Description |
|---|---|
DatabaseError
|
If reading the clips fails |
StorageError
|
If writing the deck fails |
get_template(profile_id=Depends(ensure_profile_exists))
async
¶
Retrieves the active profile's LLM template
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
profile_id
|
str
|
Validated profile id |
Depends(ensure_profile_exists)
|
Returns:
| Type | Description |
|---|---|
LlmTemplateResponse
|
The profile's saved LLM template |
Raises:
| Type | Description |
|---|---|
RecordNotFoundError
|
If the profile has no template |
DatabaseError
|
If the lookup fails |
list_clips(profile_id=Depends(ensure_profile_exists))
async
¶
Lists the active profile's saved clips
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
profile_id
|
str
|
Validated profile id |
Depends(ensure_profile_exists)
|
Returns:
| Type | Description |
|---|---|
list[ClipResponse]
|
The profile's saved clips, newest first |
Raises:
| Type | Description |
|---|---|
DatabaseError
|
If the query fails |
list_files(profile_id=Depends(ensure_profile_exists))
async
¶
Lists the active profile's saved files
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
profile_id
|
str
|
Validated profile id |
Depends(ensure_profile_exists)
|
Returns:
| Type | Description |
|---|---|
list[ProfileFileResponse]
|
The profile's saved files, newest first |
Raises:
| Type | Description |
|---|---|
DatabaseError
|
If the query fails |
list_transcripts(profile_id=Depends(ensure_profile_exists))
async
¶
Lists the active profile's transcripts
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
profile_id
|
str
|
Validated profile id |
Depends(ensure_profile_exists)
|
Returns:
| Type | Description |
|---|---|
list[ProfileTranscriptResponse]
|
The profile's transcripts, newest first |
Raises:
| Type | Description |
|---|---|
DatabaseError
|
If the query fails |
save_clip(clip_file=File(...), start_time=Form(...), end_time=Form(...), breakdown=Form(...), profile_id=Depends(ensure_profile_exists))
async
¶
Saves an uploaded video clip for the active profile
Receives the clip and its metadata as a single multipart/form-data
request (the clip is the file part, the rest are form fields), converts it
to WebM for Anki compatibility, stores it under the profile, and persists a
file + clip record
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
clip_file
|
UploadFile
|
The recorded clip (multipart file part) |
File(...)
|
start_time
|
float
|
Clip start time in seconds |
Form(...)
|
end_time
|
float
|
Clip end time in seconds |
Form(...)
|
breakdown
|
str
|
JSON-encoded breakdown payload |
Form(...)
|
profile_id
|
str
|
Validated profile id |
Depends(ensure_profile_exists)
|
Returns:
| Type | Description |
|---|---|
SaveClipResponse
|
The saved clip's id, its file id, and its media URL |
Raises:
| Type | Description |
|---|---|
HTTPException
|
If the breakdown payload is not valid JSON |
FFmpegError
|
If the WebM conversion fails |
StorageError
|
If storing the clip fails |
DatabaseError
|
If persistence fails |
save_subtitles(req, profile_id=Depends(ensure_profile_exists))
async
¶
Persists SRT content under the active profile
Used by the player's "Fix SRT" action: when file_id points at an existing
SRT file owned by the profile, its content is overwritten in place;
otherwise a new SRT file is stored
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
req
|
SaveSubtitlesRequest
|
The SRT content (+ optional file id / name) |
required |
profile_id
|
str
|
Validated profile id |
Depends(ensure_profile_exists)
|
Returns:
| Type | Description |
|---|---|
ProfileFileResponse
|
The stored SRT file's id, name, media URL, type, and timestamp |
Raises:
| Type | Description |
|---|---|
StorageError
|
If writing the SRT fails |
DatabaseError
|
If persistence fails |
upload_file(request, file_name=Header(..., alias='X-File-Name'), file_type=Query(None, alias='type'), folder=Query(None, alias='folder'), profile_id=Depends(ensure_profile_exists))
async
¶
Streams an upload and stores it as a profile file (no processing)
Usage
-
This is the upload-once entry point for the job system
-
The returned file id is then passed to
POST /jobsif an operation on it is requested -
Avoids re-uploading the file
Folder
-
?folder=tags files uploaded together (e.g. from a picked directory) with a shared group label so that the file list can group them -
It is a query param rather than a header so a non-ASCII group name never has to be encoded into a latin-1 header
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
Request
|
The |
required |
file_name
|
str
|
The original file name ( |
Header(..., alias='X-File-Name')
|
file_type
|
str | None
|
Optional file-type tag ( |
Query(None, alias='type')
|
folder
|
str | None
|
Optional group label ( |
Query(None, alias='folder')
|
profile_id
|
str
|
Validated profile id |
Depends(ensure_profile_exists)
|
Returns:
| Type | Description |
|---|---|
ProfileFileResponse
|
The stored file's id, name, media URL, type, and folder |
Raises:
| Type | Description |
|---|---|
UploadError
|
If the upload fails |
DatabaseError
|
If persistence fails |
upsert_template(req, profile_id=Depends(ensure_profile_exists))
async
¶
Creates or updates the active profile's LLM template
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
req
|
LlmTemplateRequest
|
The template data |
required |
profile_id
|
str
|
Validated profile id |
Depends(ensure_profile_exists)
|
Returns:
| Type | Description |
|---|---|
LlmTemplateResponse
|
The created or updated LLM template |
Raises:
| Type | Description |
|---|---|
DatabaseError
|
If the upsert fails |