Initialer Push, Grundsätzlich funktioniert das Programm
This commit is contained in:
@@ -0,0 +1,192 @@
|
||||
"""Tab 2 - Transcript: Metadaten-Kopf, Miniplayer, Sprecher, Transkription, Freitext."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from PySide6.QtCore import Signal
|
||||
from PySide6.QtWidgets import (
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QLineEdit,
|
||||
QMessageBox,
|
||||
QPushButton,
|
||||
QTextEdit,
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
)
|
||||
|
||||
from notarius.gui.controller import AppController
|
||||
from notarius.gui.format_utils import format_duration
|
||||
from notarius.gui.widgets.audio_player import MiniAudioPlayer
|
||||
from notarius.gui.widgets.editable_field import EditableDateTimeField, EditableTextField
|
||||
from notarius.models.recording import Recording
|
||||
from notarius.transcription.worker import STAGE_LABELS, TranscriptionWorker
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TranscriptTab(QWidget):
|
||||
# Wird ausgelöst, sobald ein Transkript neu erzeugt wurde - MainWindow prüft
|
||||
# daraufhin erneut, ob Tab 3 aktiviert werden kann.
|
||||
transcription_finished = Signal()
|
||||
|
||||
def __init__(self, controller: AppController, parent: QWidget | None = None) -> None:
|
||||
super().__init__(parent)
|
||||
self._controller = controller
|
||||
self._recording: Recording | None = None
|
||||
self._worker: TranscriptionWorker | None = None
|
||||
|
||||
self._title_field = EditableTextField("Titel")
|
||||
self._title_field.value_changed.connect(self._on_title_changed)
|
||||
|
||||
self._timestamp_field = EditableDateTimeField("Aufzeichnungszeitpunkt")
|
||||
self._timestamp_field.value_changed.connect(self._on_timestamp_changed)
|
||||
|
||||
self._duration_label = QLabel()
|
||||
|
||||
self._player = MiniAudioPlayer()
|
||||
|
||||
self._speaker_field = QLineEdit()
|
||||
self._speaker_field.setPlaceholderText("Sprecher, kommagetrennt (z.B. Anna, Max)")
|
||||
self._speaker_field.editingFinished.connect(self._on_speakers_edited)
|
||||
|
||||
self._transcribe_button = QPushButton("Transcribe")
|
||||
self._transcribe_button.clicked.connect(self._on_transcribe_clicked)
|
||||
|
||||
self._status_label = QLabel("idle")
|
||||
|
||||
self._text_edit = QTextEdit()
|
||||
self._text_edit.setPlaceholderText("Noch kein Transkript vorhanden.")
|
||||
|
||||
self._save_button = QPushButton("Save")
|
||||
self._save_button.clicked.connect(self._on_save_clicked)
|
||||
|
||||
self._delete_button = QPushButton("Delete")
|
||||
self._delete_button.clicked.connect(self._on_delete_clicked)
|
||||
|
||||
transcribe_row = QHBoxLayout()
|
||||
transcribe_row.addWidget(self._transcribe_button)
|
||||
transcribe_row.addWidget(self._status_label)
|
||||
transcribe_row.addStretch(1)
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
layout.addWidget(self._title_field)
|
||||
layout.addWidget(self._timestamp_field)
|
||||
layout.addWidget(self._duration_label)
|
||||
layout.addWidget(self._player)
|
||||
layout.addWidget(self._speaker_field)
|
||||
layout.addLayout(transcribe_row)
|
||||
layout.addWidget(self._text_edit, stretch=1)
|
||||
layout.addWidget(self._save_button)
|
||||
layout.addStretch(0)
|
||||
layout.addWidget(self._delete_button)
|
||||
|
||||
self.set_recording(None)
|
||||
|
||||
def set_recording(self, recording: Recording | None) -> None:
|
||||
self._recording = recording
|
||||
has_recording = recording is not None
|
||||
|
||||
self.setEnabled(has_recording)
|
||||
if not has_recording:
|
||||
return
|
||||
|
||||
self._title_field.set_value(recording.meta.title)
|
||||
self._timestamp_field.set_value(recording.meta.created_at)
|
||||
self._duration_label.setText(f"<b>Länge:</b> {format_duration(recording.meta.duration_seconds)}")
|
||||
self._player.load(recording.audio_path)
|
||||
self._speaker_field.setText(", ".join(recording.meta.speakers))
|
||||
|
||||
if recording.has_transcript:
|
||||
self._text_edit.setPlainText(recording.transcript_path.read_text(encoding="utf-8"))
|
||||
self._status_label.setText("done")
|
||||
else:
|
||||
self._text_edit.clear()
|
||||
self._status_label.setText("idle")
|
||||
|
||||
self._transcribe_button.setEnabled(True)
|
||||
|
||||
# -- Metadaten-Bearbeitung ----------------------------------------------------
|
||||
|
||||
def _on_title_changed(self, new_title: str) -> None:
|
||||
if self._recording is None:
|
||||
return
|
||||
self._recording.meta.title = new_title
|
||||
self._recording.save_meta()
|
||||
self._controller.recordings_changed.emit()
|
||||
|
||||
def _on_timestamp_changed(self, new_iso: str) -> None:
|
||||
if self._recording is None:
|
||||
return
|
||||
self._recording.meta.created_at = new_iso
|
||||
self._recording.save_meta()
|
||||
self._controller.recordings_changed.emit()
|
||||
|
||||
def _on_speakers_edited(self) -> None:
|
||||
if self._recording is None:
|
||||
return
|
||||
speakers = [s.strip() for s in self._speaker_field.text().split(",") if s.strip()]
|
||||
self._recording.meta.speakers = speakers
|
||||
self._recording.save_meta()
|
||||
|
||||
# -- Transkription -------------------------------------------------------------
|
||||
|
||||
def _on_transcribe_clicked(self) -> None:
|
||||
if self._recording is None:
|
||||
return
|
||||
|
||||
if self._recording.has_transcript:
|
||||
answer = QMessageBox.question(
|
||||
self, "Transkript überschreiben?", "Es existiert bereits ein Transkript. Überschreiben?"
|
||||
)
|
||||
if answer != QMessageBox.StandardButton.Yes:
|
||||
return
|
||||
|
||||
self._transcribe_button.setEnabled(False)
|
||||
self._status_label.setText(STAGE_LABELS["loading_models"])
|
||||
|
||||
self._worker = TranscriptionWorker(self)
|
||||
self._worker.stage_changed.connect(self._on_stage_changed)
|
||||
self._worker.finished.connect(self._on_transcription_worker_finished)
|
||||
self._worker.start(
|
||||
self._recording.audio_path, self._recording.transcript_path, self._recording.meta.title
|
||||
)
|
||||
|
||||
def _on_stage_changed(self, stage: str) -> None:
|
||||
self._status_label.setText(STAGE_LABELS.get(stage, stage))
|
||||
|
||||
def _on_transcription_worker_finished(self, success: bool, error_message: str) -> None:
|
||||
self._transcribe_button.setEnabled(True)
|
||||
self._worker = None
|
||||
|
||||
if not self._recording:
|
||||
return
|
||||
|
||||
if success:
|
||||
self._status_label.setText("done")
|
||||
self._text_edit.setPlainText(self._recording.transcript_path.read_text(encoding="utf-8"))
|
||||
self.transcription_finished.emit()
|
||||
else:
|
||||
self._status_label.setText("error")
|
||||
QMessageBox.critical(self, "Transkription fehlgeschlagen", error_message)
|
||||
|
||||
# -- Freitext / Löschen ----------------------------------------------------------
|
||||
|
||||
def _on_save_clicked(self) -> None:
|
||||
if self._recording is None:
|
||||
return
|
||||
self._recording.transcript_path.write_text(self._text_edit.toPlainText(), encoding="utf-8")
|
||||
|
||||
def _on_delete_clicked(self) -> None:
|
||||
if self._recording is None:
|
||||
return
|
||||
answer = QMessageBox.question(
|
||||
self,
|
||||
"Aufnahme löschen?",
|
||||
"Aufnahme und alle zugehörigen Dateien (Audio, Transkript, Notizen, Summary) "
|
||||
"werden unwiderruflich gelöscht. Fortfahren?",
|
||||
)
|
||||
if answer != QMessageBox.StandardButton.Yes:
|
||||
return
|
||||
self._controller.delete_recording(self._recording)
|
||||
Reference in New Issue
Block a user