Add speaker identification2 (#1290)

Co-authored-by: David Olowomeye <100958002+greatdaveo@users.noreply.github.com>
This commit is contained in:
Raivis Dejus 2025-12-02 21:39:24 +02:00 committed by GitHub
commit 73376a63ac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
153 changed files with 7409 additions and 719 deletions

View file

@ -1,9 +1,12 @@
[run]
omit =
buzz/whisper_cpp/*
buzz/transcriber/local_whisper_cpp_server_transcriber.py
*_test.py
demucs/*
buzz/transcriber/local_whisper_cpp_server_transcriber.py
whisper_diarization/*
deepmultilingualpunctuation/*
ctc_forced_aligner/*
[html]
directory = coverage/html

View file

@ -70,10 +70,10 @@ jobs:
~/AppData/Local/Buzz/Buzz/Cache
key: whisper-models
- uses: AnimMouse/setup-ffmpeg@v1.2.1
- uses: AnimMouse/setup-ffmpeg@v1
id: setup-ffmpeg
with:
version: ${{ matrix.os == 'macos-15-intel' && '7.1.1' || matrix.os == 'macos-latest' && '71' || '7.1' }}
version: ${{ matrix.os == 'macos-15-intel' && '7.1.1' || matrix.os == 'macos-latest' && '80' || '8.0' }}
- name: Test ffmpeg
run: ffmpeg -i ./testdata/audio-long.mp3 ./testdata/audio-long.wav

View file

@ -15,9 +15,22 @@ concurrency:
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 90
env:
BUZZ_DISABLE_TELEMETRY: true
outputs:
snap: ${{ steps.snapcraft.outputs.snap }}
steps:
# Ideas from https://github.com/orgs/community/discussions/25678
- name: Remove unused build tools
run: |
sudo apt-get remove -y '^llvm-.*'
sudo apt-get remove -y 'php.*'
sudo apt-get remove -y azure-cli google-cloud-sdk hhvm google-chrome-stable firefox powershell mono-devel || true
sudo apt-get autoremove -y
sudo apt-get clean
python -m pip cache purge
rm -rf /opt/hostedtoolcache || true
- name: Maximize build space
uses: easimon/maximize-build-space@master
with:

3
.gitignore vendored
View file

@ -31,4 +31,5 @@ benchmarks.json
/coverage/
/wheelhouse/
/.flatpak-builder
/repo
/repo
/nemo_msdd_configs

12
.gitmodules vendored
View file

@ -1,3 +1,15 @@
[submodule "whisper.cpp"]
path = whisper.cpp
url = https://github.com/ggerganov/whisper.cpp
[submodule "whisper_diarization"]
path = whisper_diarization
url = https://github.com/MahmoudAshraf97/whisper-diarization
[submodule "demucs"]
path = demucs
url = https://github.com/MahmoudAshraf97/demucs.git
[submodule "deepmultilingualpunctuation"]
path = deepmultilingualpunctuation
url = https://github.com/oliverguhr/deepmultilingualpunctuation.git
[submodule "ctc_forced_aligner"]
path = ctc_forced_aligner
url = https://github.com/MahmoudAshraf97/ctc-forced-aligner.git

View file

@ -30,7 +30,13 @@ datas += collect_data_files("transformers", include_py_files=True)
datas += collect_data_files("faster_whisper", include_py_files=True)
datas += collect_data_files("stable_whisper", include_py_files=True)
datas += collect_data_files("whisper")
datas += [("demucs", "demucs")]
datas += collect_data_files("demucs", include_py_files=True)
datas += collect_data_files("whisper_diarization", include_py_files=True)
datas += collect_data_files("deepmultilingualpunctuation", include_py_files=True)
datas += collect_data_files("ctc_forced_aligner", include_py_files=True)
datas += collect_data_files("nemo", include_py_files=True)
datas += collect_data_files("lightning_fabric", include_py_files=True)
datas += collect_data_files("pytorch_lightning", include_py_files=True)
datas += [("buzz/assets/*", "assets")]
datas += [("buzz/locale", "locale")]
datas += [("buzz/schema.sql", ".")]

View file

@ -53,8 +53,7 @@ sudo apt-get install --no-install-recommends libyaml-dev libtbb-dev libxkbcommon
```
On versions prior to Ubuntu 24.04 install `sudo apt-get install --no-install-recommends libegl1-mesa`
5. Install the dependencies `uv sync`
6. Build Buzz `uv build`
7. Run Buzz `uv run buzz`
6. Run Buzz `uv run buzz`
#### Necessary dependencies for Faster Whisper on GPU
@ -81,8 +80,7 @@ On versions prior to Ubuntu 24.04 install `sudo apt-get install --no-install-rec
3. Install uv `curl -LsSf https://astral.sh/uv/install.sh | sh` (or `brew install uv`)
4. Install system dependencies you may be missing `brew install ffmpeg`
5. Install the dependencies `uv sync`
6. Build Buzz `uv build`
7. Run Buzz `uv run buzz`
6. Run Buzz `uv run buzz`

View file

@ -1,4 +1,5 @@
version := 1.3.4
# Change also in pyproject.toml and buzz/__version__.py
version := 1.4.0
mac_app_path := ./dist/Buzz.app
mac_zip_path := ./dist/Buzz-${version}-mac.zip
@ -28,7 +29,7 @@ else
rm -rf dist/* || true
endif
COVERAGE_THRESHOLD := 75
COVERAGE_THRESHOLD := 70
test: buzz/whisper_cpp
pytest -s -vv --cov=buzz --cov-report=xml --cov-report=html --benchmark-skip --cov-fail-under=${COVERAGE_THRESHOLD} --cov-config=.coveragerc
@ -67,7 +68,7 @@ ifeq ($(shell uname -s), Linux)
cp whisper.cpp/build/bin/whisper-server buzz/whisper_cpp/ || true
cp whisper.cpp/build/src/libwhisper.so buzz/whisper_cpp/ || true
cp whisper.cpp/build/src/libwhisper.so.1 buzz/whisper_cpp/ || true
cp whisper.cpp/build/src/libwhisper.so.1.7.6 buzz/whisper_cpp/ || true
cp whisper.cpp/build/src/libwhisper.so.1.8.2 buzz/whisper_cpp/ || true
cp whisper.cpp/build/ggml/src/libggml.so buzz/whisper_cpp/ || true
cp whisper.cpp/build/ggml/src/libggml-base.so buzz/whisper_cpp/ || true
cp whisper.cpp/build/ggml/src/libggml-cpu.so buzz/whisper_cpp/ || true

View file

@ -14,7 +14,7 @@ OpenAI's [Whisper](https://github.com/openai/whisper).
[![Github all releases](https://img.shields.io/github/downloads/chidiwilliams/buzz/total.svg)](https://GitHub.com/chidiwilliams/buzz/releases/)
<blockquote>
<p>Buzz is better on the App Store. Get a Mac-native version of Buzz with a cleaner look, audio playback, drag-and-drop import, transcript editing, search, and much more.</p>
<p>An older version of Buzz available on the App Store. Get a Mac-native version of Buzz with a cleaner look, audio playback, drag-and-drop import, transcript editing, search, and much more.</p>
<a href="https://apps.apple.com/us/app/buzz-captions/id6446018936?mt=12&amp;itsct=apps_box_badge&amp;itscg=30200"><img src="https://toolbox.marketingtools.apple.com/api/badges/download-on-the-mac-app-store/black/en-us?size=250x83&amp;releaseDate=1679529600" alt="Download on the Mac App Store" /></a>
</blockquote>

View file

@ -1 +1 @@
VERSION = "1.3.4"
VERSION = "1.4.0"

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<svg height="800px" width="800px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 493.347 493.347" xml:space="preserve">
<g>
<path style="fill:#010002;" d="M191.936,385.946c-14.452,0-29.029-1.36-43.319-4.04l-5.299-0.996l-66.745,37.15v-63.207
l-6.629-4.427C25.496,320.716,0,277.045,0,230.617c0-85.648,86.102-155.33,191.936-155.33c17.077,0,33.623,1.838,49.394,5.239
c-50.486,27.298-84.008,74.801-84.008,128.765c0,72.969,61.25,134.147,142.942,149.464
C269.41,375.892,232.099,385.946,191.936,385.946z"/>
<path style="fill:#010002;" d="M437.777,304.278l-6.629,4.427v48.075l-50.933-28.343l-0.125,0.024l-5.167,0.967
c-11.444,2.142-23.104,3.228-34.673,3.228c-1.241,0-2.47-0.054-3.705-0.078c-82.707-1.599-149.387-56.268-149.387-123.287
c0-52.109,40.324-96.741,97.129-114.791c14.47-4.594,30.001-7.471,46.219-8.3c3.228-0.167,6.468-0.274,9.75-0.274
c84.413,0,153.092,55.343,153.092,123.365C493.347,246.053,473.089,280.679,437.777,304.278z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -56,6 +56,17 @@ def main():
format=log_format,
)
# Silence noisy third-party library loggers
logging.getLogger("matplotlib").setLevel(logging.WARNING)
logging.getLogger("graphviz").setLevel(logging.WARNING)
logging.getLogger("nemo_logger").setLevel(logging.ERROR)
logging.getLogger("numba").setLevel(logging.WARNING)
logging.getLogger("torio._extension.utils").setLevel(logging.WARNING)
logging.getLogger("export_config_manager").setLevel(logging.WARNING)
logging.getLogger("training_telemetry_provider").setLevel(logging.ERROR)
logging.getLogger("default_recorder").setLevel(logging.WARNING)
logging.getLogger("config").setLevel(logging.WARNING)
if getattr(sys, "frozen", False) is False:
stdout_handler = logging.StreamHandler(sys.stdout)
stdout_handler.setLevel(logging.DEBUG)

View file

@ -7,7 +7,7 @@ from uuid import UUID
from PyQt6.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
from demucs import api as demucsApi
from demucs.demucs import api as demucsApi
from buzz.model_loader import ModelType
from buzz.transcriber.file_transcriber import FileTranscriber

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: buzz\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-28 16:49+0200\n"
"POT-Creation-Date: 2025-11-23 12:55+0200\n"
"PO-Revision-Date: 2025-10-17 07:59+0200\n"
"Last-Translator: Éric Duarte <contacto@ericdq.com>\n"
"Language-Team: Catalan <jmas@softcatala.org>\n"
@ -554,64 +554,68 @@ msgstr "Veure"
msgid "Timestamps"
msgstr "Marqua de temps"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:211
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:215
msgid "Export"
msgstr "Exporta"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:230
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:234
msgid "Translate"
msgstr "Traduir"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:240
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:177
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:244
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:175
msgid "Resize"
msgstr "Redimensionar"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:252
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:257
msgid "Identify Speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:269
msgid "Find"
msgstr "Cerca"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:255
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:272
msgid "Show/Hide Search Bar (Ctrl+F)"
msgstr "Mostra/amaga la barra de cerca (Ctrl+F)"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:320
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:337
msgid "Find:"
msgstr "Cerca:"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:326
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:343
msgid "Enter text to find..."
msgstr "Introduïu el text a cercar..."
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:339
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:356
msgid "Previous match (Shift+Enter)"
msgstr "Coincidència anterior (Maj+Retorn)"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:347
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:364
msgid "Next match (Enter)"
msgstr "Coincidència següent (retorn)"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:355
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:372
msgid "Clear"
msgstr "Neteja"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:382
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:399
msgid "Playback Controls:"
msgstr "Controls de reproducció:"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:387
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:404
msgid "Loop Segment"
msgstr "Segment de bucle"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:389
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:406
msgid "Enable/disable looping when clicking on transcript segments"
msgstr "Activa/desactiva el bucle en fer clic als segments de transcripció"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:395
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:412
msgid "Follow Audio"
msgstr "Segueix l'àudio"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:397
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:414
msgid ""
"Enable/disable following the current audio position in the transcript. When "
"enabled, automatically scrolls to current text."
@ -619,75 +623,146 @@ msgstr ""
"Activa/desactiva seguint la posició d'àudio actual a la transcripció. Quan "
"està activada, es desplaça automàticament al text actual."
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:444
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:461
msgid "Scroll to Current"
msgstr "Desplaça't fins a l'actual"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:446
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:463
msgid "Scroll to the currently spoken text"
msgstr "Desplaçar-se fins al text que es parla actualment"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:768
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:785
msgid "1 of 100+ matches"
msgstr "1 de més de 100 coincidències"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
msgid "1 of "
msgstr "1 de "
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " matches"
msgstr " coincidències"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:775
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:792
msgid "No matches found"
msgstr "No s'ha trobat cap coincidència"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:834
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:851
msgid " of 100+ matches"
msgstr " de més de 100 coincidències"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " of "
msgstr " de "
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1191
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1208
msgid "API Key Required"
msgstr "Clau API necessària"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1192
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1209
msgid "Please enter OpenAI API Key in preferences"
msgstr "Introduïu la clau API d'OpenAI a les preferències"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:159
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:157
msgid "Resize Options"
msgstr "Opcions de redimensionament"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:170
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:168
msgid "Desired subtitle length"
msgstr "Longitud desitjada dels subtítols"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:195
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:193
msgid "Merge Options"
msgstr "Opcions de fusió"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:206
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:204
msgid "Merge by gap"
msgstr "Fusiona per buit"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:214
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:212
msgid "Split by punctuation"
msgstr "Divideix per puntuació"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:222
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:220
msgid "Split by max length"
msgstr "Divideix per la longitud màxima"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:234
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:232
msgid "Merge"
msgstr "Fusiona"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:92
msgid "1/8 Collecting transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:106
msgid "2/8 Loading audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:115
msgid "3/8 Loading alignment model"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:121
msgid "4/8 Processing audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:133
#, fuzzy
msgid "5/8 Preparing transcripts"
msgstr "Cancel·la la transcripció"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:151
msgid "6/8 Identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:160
msgid "0/0 Error identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:168
msgid "7/8 Mapping speakers to transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:207
msgid "8/8 Identification done"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:243
msgid "Step 1: Identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:255
msgid "Identify"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:265
msgid "Ready to identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:267
msgid "Audio file not found"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:283
msgid "Step 2: Name speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:298
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:391
msgid "Play sample"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:313
msgid "Merge speaker sentences"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:318
#, fuzzy
msgid "Save"
msgstr "Desa el fitxer"
#: buzz/widgets/transcription_viewer/export_transcription_menu.py:82
msgid "Save File"
msgstr "Desa el fitxer"

View file

@ -2,7 +2,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-28 16:49+0200\n"
"POT-Creation-Date: 2025-11-23 12:55+0200\n"
"PO-Revision-Date: \n"
"Last-Translator: Ole Guldberg2 <xalt7x.service@gmail.com>\n"
"Language-Team: \n"
@ -552,138 +552,213 @@ msgstr "Vis"
msgid "Timestamps"
msgstr "Tidsstempler"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:211
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:215
msgid "Export"
msgstr "Eksporter"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:230
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:234
msgid "Translate"
msgstr "Oversæt"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:240
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:177
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:244
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:175
msgid "Resize"
msgstr "Behandel størrelse"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:252
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:257
msgid "Identify Speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:269
msgid "Find"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:255
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:272
msgid "Show/Hide Search Bar (Ctrl+F)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:320
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:337
msgid "Find:"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:326
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:343
msgid "Enter text to find..."
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:339
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:356
msgid "Previous match (Shift+Enter)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:347
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:364
msgid "Next match (Enter)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:355
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:372
msgid "Clear"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:382
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:399
msgid "Playback Controls:"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:387
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:404
msgid "Loop Segment"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:389
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:406
msgid "Enable/disable looping when clicking on transcript segments"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:395
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:412
msgid "Follow Audio"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:397
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:414
msgid ""
"Enable/disable following the current audio position in the transcript. When "
"enabled, automatically scrolls to current text."
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:444
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:461
msgid "Scroll to Current"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:446
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:463
msgid "Scroll to the currently spoken text"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:768
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:785
msgid "1 of 100+ matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
msgid "1 of "
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:775
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:792
msgid "No matches found"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:834
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:851
msgid " of 100+ matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " of "
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1191
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1208
msgid "API Key Required"
msgstr "API-nøgle påkrævet"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1192
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1209
msgid "Please enter OpenAI API Key in preferences"
msgstr "Indtast venligst OpenAI API-nøgle i indstillinger"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:159
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:157
msgid "Resize Options"
msgstr "Størrelsesindstillinger"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:170
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:168
msgid "Desired subtitle length"
msgstr "Ønskede undertekst længde"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:195
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:193
msgid "Merge Options"
msgstr "Sammenfletningsindstillinger"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:206
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:204
msgid "Merge by gap"
msgstr "Sammenflet ved hul"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:214
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:212
msgid "Split by punctuation"
msgstr "Split ved punktum"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:222
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:220
msgid "Split by max length"
msgstr "Split ved max længde"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:234
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:232
msgid "Merge"
msgstr "Sammenflet"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:92
msgid "1/8 Collecting transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:106
msgid "2/8 Loading audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:115
msgid "3/8 Loading alignment model"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:121
msgid "4/8 Processing audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:133
#, fuzzy
msgid "5/8 Preparing transcripts"
msgstr "Afbryd transkription"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:151
msgid "6/8 Identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:160
msgid "0/0 Error identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:168
msgid "7/8 Mapping speakers to transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:207
msgid "8/8 Identification done"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:243
msgid "Step 1: Identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:255
msgid "Identify"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:265
msgid "Ready to identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:267
msgid "Audio file not found"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:283
msgid "Step 2: Name speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:298
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:391
msgid "Play sample"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:313
msgid "Merge speaker sentences"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:318
#, fuzzy
msgid "Save"
msgstr "Gem fil"
#: buzz/widgets/transcription_viewer/export_transcription_menu.py:82
msgid "Save File"
msgstr "Gem fil"

View file

@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-28 16:49+0200\n"
"POT-Creation-Date: 2025-11-23 12:55+0200\n"
"PO-Revision-Date: 2025-03-05 14:41+0100\n"
"Last-Translator: \n"
"Language-Team: \n"
@ -552,138 +552,213 @@ msgstr "Anzeigen"
msgid "Timestamps"
msgstr "Zeitstempel"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:211
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:215
msgid "Export"
msgstr "Export"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:230
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:234
msgid "Translate"
msgstr "Übersetzen"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:240
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:177
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:244
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:175
msgid "Resize"
msgstr "Größe ändern"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:252
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:257
msgid "Identify Speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:269
msgid "Find"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:255
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:272
msgid "Show/Hide Search Bar (Ctrl+F)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:320
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:337
msgid "Find:"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:326
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:343
msgid "Enter text to find..."
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:339
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:356
msgid "Previous match (Shift+Enter)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:347
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:364
msgid "Next match (Enter)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:355
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:372
msgid "Clear"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:382
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:399
msgid "Playback Controls:"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:387
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:404
msgid "Loop Segment"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:389
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:406
msgid "Enable/disable looping when clicking on transcript segments"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:395
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:412
msgid "Follow Audio"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:397
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:414
msgid ""
"Enable/disable following the current audio position in the transcript. When "
"enabled, automatically scrolls to current text."
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:444
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:461
msgid "Scroll to Current"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:446
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:463
msgid "Scroll to the currently spoken text"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:768
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:785
msgid "1 of 100+ matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
msgid "1 of "
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:775
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:792
msgid "No matches found"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:834
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:851
msgid " of 100+ matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " of "
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1191
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1208
msgid "API Key Required"
msgstr "API-Schlüssel erforderlich"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1192
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1209
msgid "Please enter OpenAI API Key in preferences"
msgstr "Bitte geben Sie den OpenAI-API-Schlüssel in den Einstellungen ein"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:159
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:157
msgid "Resize Options"
msgstr "Größenänderungsoptionen"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:170
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:168
msgid "Desired subtitle length"
msgstr "Gewünschte Untertitellänge"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:195
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:193
msgid "Merge Options"
msgstr "Zusammenführungsoptionen"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:206
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:204
msgid "Merge by gap"
msgstr "Nach Abstand zusammenführen"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:214
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:212
msgid "Split by punctuation"
msgstr "Durch Satzzeichen getrennt"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:222
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:220
msgid "Split by max length"
msgstr "Aufgeteilt nach maximaler Länge"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:234
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:232
msgid "Merge"
msgstr "Vereinigen"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:92
msgid "1/8 Collecting transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:106
msgid "2/8 Loading audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:115
msgid "3/8 Loading alignment model"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:121
msgid "4/8 Processing audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:133
#, fuzzy
msgid "5/8 Preparing transcripts"
msgstr "Transkription abbrechen"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:151
msgid "6/8 Identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:160
msgid "0/0 Error identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:168
msgid "7/8 Mapping speakers to transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:207
msgid "8/8 Identification done"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:243
msgid "Step 1: Identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:255
msgid "Identify"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:265
msgid "Ready to identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:267
msgid "Audio file not found"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:283
msgid "Step 2: Name speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:298
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:391
msgid "Play sample"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:313
msgid "Merge speaker sentences"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:318
#, fuzzy
msgid "Save"
msgstr "Datei speichern"
#: buzz/widgets/transcription_viewer/export_transcription_menu.py:82
msgid "Save File"
msgstr "Datei speichern"

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-28 16:49+0200\n"
"POT-Creation-Date: 2025-11-23 12:55+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -540,138 +540,211 @@ msgstr ""
msgid "Timestamps"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:211
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:215
msgid "Export"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:230
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:234
msgid "Translate"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:240
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:177
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:244
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:175
msgid "Resize"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:252
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:257
msgid "Identify Speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:269
msgid "Find"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:255
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:272
msgid "Show/Hide Search Bar (Ctrl+F)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:320
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:337
msgid "Find:"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:326
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:343
msgid "Enter text to find..."
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:339
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:356
msgid "Previous match (Shift+Enter)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:347
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:364
msgid "Next match (Enter)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:355
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:372
msgid "Clear"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:382
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:399
msgid "Playback Controls:"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:387
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:404
msgid "Loop Segment"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:389
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:406
msgid "Enable/disable looping when clicking on transcript segments"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:395
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:412
msgid "Follow Audio"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:397
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:414
msgid ""
"Enable/disable following the current audio position in the transcript. When "
"enabled, automatically scrolls to current text."
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:444
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:461
msgid "Scroll to Current"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:446
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:463
msgid "Scroll to the currently spoken text"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:768
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:785
msgid "1 of 100+ matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
msgid "1 of "
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:775
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:792
msgid "No matches found"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:834
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:851
msgid " of 100+ matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " of "
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1191
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1208
msgid "API Key Required"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1192
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1209
msgid "Please enter OpenAI API Key in preferences"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:159
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:157
msgid "Resize Options"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:170
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:168
msgid "Desired subtitle length"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:195
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:193
msgid "Merge Options"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:206
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:204
msgid "Merge by gap"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:214
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:212
msgid "Split by punctuation"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:222
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:220
msgid "Split by max length"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:234
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:232
msgid "Merge"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:92
msgid "1/8 Collecting transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:106
msgid "2/8 Loading audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:115
msgid "3/8 Loading alignment model"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:121
msgid "4/8 Processing audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:133
msgid "5/8 Preparing transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:151
msgid "6/8 Identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:160
msgid "0/0 Error identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:168
msgid "7/8 Mapping speakers to transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:207
msgid "8/8 Identification done"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:243
msgid "Step 1: Identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:255
msgid "Identify"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:265
msgid "Ready to identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:267
msgid "Audio file not found"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:283
msgid "Step 2: Name speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:298
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:391
msgid "Play sample"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:313
msgid "Merge speaker sentences"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:318
msgid "Save"
msgstr ""
#: buzz/widgets/transcription_viewer/export_transcription_menu.py:82
msgid "Save File"
msgstr ""

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-28 16:49+0200\n"
"POT-Creation-Date: 2025-11-23 12:55+0200\n"
"PO-Revision-Date: 2025-09-08 12:43+0200\n"
"Last-Translator: Éric Duarte <contacto@ericdq.com>\n"
"Language-Team: \n"
@ -589,66 +589,70 @@ msgstr "Ver"
msgid "Timestamps"
msgstr "Marcas de tiempo"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:211
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:215
msgid "Export"
msgstr "Exportar"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:230
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:234
msgid "Translate"
msgstr "Traducir"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:240
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:177
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:244
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:175
msgid "Resize"
msgstr "Cambiar el tamaño"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:252
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:257
msgid "Identify Speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:269
msgid "Find"
msgstr "Buscar"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:255
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:272
msgid "Show/Hide Search Bar (Ctrl+F)"
msgstr "Mostrar/Ocultar barra de búsqueda (Ctrl+F)"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:320
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:337
msgid "Find:"
msgstr "Encontrar:"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:326
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:343
msgid "Enter text to find..."
msgstr "Introducir texto para encontrar..."
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:339
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:356
msgid "Previous match (Shift+Enter)"
msgstr "Coincidencia anterior (Mayús+Intro)"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:347
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:364
msgid "Next match (Enter)"
msgstr "Siguiente coincidencia (Enter)"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:355
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:372
msgid "Clear"
msgstr "Limpiar"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:382
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:399
msgid "Playback Controls:"
msgstr "Controles de reproducción:"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:387
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:404
msgid "Loop Segment"
msgstr "Segmento de bucle"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:389
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:406
msgid "Enable/disable looping when clicking on transcript segments"
msgstr ""
"Activar/desactivar la reproducción en bucle al hacer clic en segmentos de la "
"transcripción"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:395
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:412
msgid "Follow Audio"
msgstr "Seguir audio"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:397
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:414
msgid ""
"Enable/disable following the current audio position in the transcript. When "
"enabled, automatically scrolls to current text."
@ -657,75 +661,148 @@ msgstr ""
"transcripción. Cuando está activado, se desplaza automáticamente al texto "
"actual."
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:444
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:461
msgid "Scroll to Current"
msgstr "Desplácese hasta Actual"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:446
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:463
msgid "Scroll to the currently spoken text"
msgstr "Desplazarse hasta el texto hablado actualmente"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:768
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:785
msgid "1 of 100+ matches"
msgstr "1 de 100+ coincidencias"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
msgid "1 of "
msgstr "1 de "
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " matches"
msgstr " coincidencias"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:775
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:792
msgid "No matches found"
msgstr "No se encontraron coincidencias"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:834
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:851
msgid " of 100+ matches"
msgstr " de 100+ coincidencias"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " of "
msgstr " de "
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1191
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1208
msgid "API Key Required"
msgstr "Clave de API requerida"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1192
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1209
msgid "Please enter OpenAI API Key in preferences"
msgstr "Ingrese la clave API de OpenAI en las preferencias"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:159
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:157
msgid "Resize Options"
msgstr "Opciones de cambio de tamaño"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:170
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:168
msgid "Desired subtitle length"
msgstr "Longitud deseada de los subtítulos"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:195
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:193
msgid "Merge Options"
msgstr "Opciones de fusión"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:206
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:204
msgid "Merge by gap"
msgstr "Fusión por hueco"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:214
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:212
msgid "Split by punctuation"
msgstr "Dividido por puntuación"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:222
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:220
msgid "Split by max length"
msgstr "Dividido por la longitud máxima"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:234
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:232
msgid "Merge"
msgstr "Fusión"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:92
msgid "1/8 Collecting transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:106
msgid "2/8 Loading audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:115
msgid "3/8 Loading alignment model"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:121
msgid "4/8 Processing audio"
msgstr ""
# automatic translation
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:133
#, fuzzy
msgid "5/8 Preparing transcripts"
msgstr "Cancelar transcripción"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:151
msgid "6/8 Identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:160
msgid "0/0 Error identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:168
msgid "7/8 Mapping speakers to transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:207
msgid "8/8 Identification done"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:243
msgid "Step 1: Identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:255
msgid "Identify"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:265
msgid "Ready to identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:267
msgid "Audio file not found"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:283
msgid "Step 2: Name speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:298
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:391
msgid "Play sample"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:313
msgid "Merge speaker sentences"
msgstr ""
# automatic translation
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:318
#, fuzzy
msgid "Save"
msgstr "Guardar archivo"
# automatic translation
#: buzz/widgets/transcription_viewer/export_transcription_menu.py:82
msgid "Save File"

View file

@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: buzz\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-28 16:49+0200\n"
"POT-Creation-Date: 2025-11-23 12:55+0200\n"
"PO-Revision-Date: 2025-11-09 20:22+0200\n"
"Language-Team: (Italiano) Albano Battistella <albanobattistella@gmail.com>\n"
"Language: it_IT\n"
@ -555,65 +555,69 @@ msgstr "Visualizza"
msgid "Timestamps"
msgstr "Timestamp"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:211
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:215
msgid "Export"
msgstr "Esporta"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:230
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:234
msgid "Translate"
msgstr "Tradurre"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:240
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:177
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:244
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:175
msgid "Resize"
msgstr "Ridimensionare"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:252
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:257
msgid "Identify Speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:269
msgid "Find"
msgstr "Trova"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:255
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:272
msgid "Show/Hide Search Bar (Ctrl+F)"
msgstr "Mostra/Nascondi barra di ricerca (Ctrl+F)"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:320
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:337
msgid "Find:"
msgstr "Trova:"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:326
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:343
msgid "Enter text to find..."
msgstr "Inserisci il testo per trovare..."
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:339
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:356
msgid "Previous match (Shift+Enter)"
msgstr "Corrispondenza precedente (Maiusc+Invio)"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:347
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:364
msgid "Next match (Enter)"
msgstr "Prossima corrispondenza (Invio)"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:355
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:372
msgid "Clear"
msgstr "Elimina"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:382
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:399
msgid "Playback Controls:"
msgstr "Controlli di riproduzione:"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:387
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:404
msgid "Loop Segment"
msgstr "Ciclo di segmento"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:389
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:406
msgid "Enable/disable looping when clicking on transcript segments"
msgstr ""
"Abilita/disabilita il loop quando si fa clic sui segmenti della trascrizione"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:395
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:412
msgid "Follow Audio"
msgstr "Segui Audio"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:397
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:414
msgid ""
"Enable/disable following the current audio position in the transcript. When "
"enabled, automatically scrolls to current text."
@ -622,75 +626,146 @@ msgstr ""
"trascrizione. Quando abilitato, scorre automaticamente fino al testo "
"corrente."
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:444
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:461
msgid "Scroll to Current"
msgstr "Scorri fino al Corrente"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:446
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:463
msgid "Scroll to the currently spoken text"
msgstr "Scorrere fino al testo attualmente pronunciato"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:768
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:785
msgid "1 of 100+ matches"
msgstr "1 di 100+ corrispondenze"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
msgid "1 of "
msgstr "1 di"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " matches"
msgstr "corrispondenze"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:775
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:792
msgid "No matches found"
msgstr "Nessuna corrispondenza trovata"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:834
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:851
msgid " of 100+ matches"
msgstr " di oltre 100 corrispondenze"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " of "
msgstr " di "
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1191
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1208
msgid "API Key Required"
msgstr "Chiave API richiesta"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1192
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1209
msgid "Please enter OpenAI API Key in preferences"
msgstr "Inserisci la chiave API OpenAI nelle preferenze"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:159
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:157
msgid "Resize Options"
msgstr "Opzioni di ridimensionamento"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:170
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:168
msgid "Desired subtitle length"
msgstr "Lunghezza desiderata dei sottotitoli"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:195
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:193
msgid "Merge Options"
msgstr "Opzioni di unione"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:206
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:204
msgid "Merge by gap"
msgstr "Unito per spazio"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:214
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:212
msgid "Split by punctuation"
msgstr "Diviso per punteggiatura"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:222
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:220
msgid "Split by max length"
msgstr "Diviso per lunghezza massima"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:234
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:232
msgid "Merge"
msgstr "Unione"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:92
msgid "1/8 Collecting transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:106
msgid "2/8 Loading audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:115
msgid "3/8 Loading alignment model"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:121
msgid "4/8 Processing audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:133
#, fuzzy
msgid "5/8 Preparing transcripts"
msgstr "Inizio trascrizione..."
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:151
msgid "6/8 Identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:160
msgid "0/0 Error identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:168
msgid "7/8 Mapping speakers to transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:207
msgid "8/8 Identification done"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:243
msgid "Step 1: Identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:255
msgid "Identify"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:265
msgid "Ready to identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:267
msgid "Audio file not found"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:283
msgid "Step 2: Name speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:298
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:391
msgid "Play sample"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:313
msgid "Merge speaker sentences"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:318
#, fuzzy
msgid "Save"
msgstr "Salva file"
#: buzz/widgets/transcription_viewer/export_transcription_menu.py:82
msgid "Save File"
msgstr "Salva file"

View file

@ -2,7 +2,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-28 16:49+0200\n"
"POT-Creation-Date: 2025-11-23 12:55+0200\n"
"PO-Revision-Date: \n"
"Last-Translator: nunawa <71294849+nunawa@users.noreply.github.com>\n"
"Language-Team: \n"
@ -548,139 +548,214 @@ msgstr "表示"
msgid "Timestamps"
msgstr "タイムスタンプ"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:211
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:215
msgid "Export"
msgstr "出力"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:230
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:234
msgid "Translate"
msgstr "翻訳"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:240
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:177
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:244
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:175
msgid "Resize"
msgstr "リサイズ"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:252
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:257
msgid "Identify Speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:269
msgid "Find"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:255
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:272
msgid "Show/Hide Search Bar (Ctrl+F)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:320
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:337
msgid "Find:"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:326
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:343
msgid "Enter text to find..."
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:339
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:356
msgid "Previous match (Shift+Enter)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:347
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:364
msgid "Next match (Enter)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:355
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:372
msgid "Clear"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:382
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:399
msgid "Playback Controls:"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:387
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:404
msgid "Loop Segment"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:389
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:406
msgid "Enable/disable looping when clicking on transcript segments"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:395
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:412
msgid "Follow Audio"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:397
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:414
msgid ""
"Enable/disable following the current audio position in the transcript. When "
"enabled, automatically scrolls to current text."
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:444
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:461
msgid "Scroll to Current"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:446
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:463
msgid "Scroll to the currently spoken text"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:768
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:785
msgid "1 of 100+ matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
msgid "1 of "
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:775
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:792
msgid "No matches found"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:834
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:851
msgid " of 100+ matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " of "
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1191
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1208
msgid "API Key Required"
msgstr "APIキーが必要"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1192
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1209
msgid "Please enter OpenAI API Key in preferences"
msgstr "設定画面でOpenAI APIキーを入力してください"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:159
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:157
#, fuzzy
msgid "Resize Options"
msgstr "リサイズ"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:170
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:168
msgid "Desired subtitle length"
msgstr "希望する字幕の長さ"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:195
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:193
msgid "Merge Options"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:206
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:204
msgid "Merge by gap"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:214
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:212
msgid "Split by punctuation"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:222
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:220
msgid "Split by max length"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:234
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:232
msgid "Merge"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:92
msgid "1/8 Collecting transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:106
msgid "2/8 Loading audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:115
msgid "3/8 Loading alignment model"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:121
msgid "4/8 Processing audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:133
#, fuzzy
msgid "5/8 Preparing transcripts"
msgstr "文字起こしをキャンセルする"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:151
msgid "6/8 Identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:160
msgid "0/0 Error identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:168
msgid "7/8 Mapping speakers to transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:207
msgid "8/8 Identification done"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:243
msgid "Step 1: Identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:255
msgid "Identify"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:265
msgid "Ready to identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:267
msgid "Audio file not found"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:283
msgid "Step 2: Name speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:298
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:391
msgid "Play sample"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:313
msgid "Merge speaker sentences"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:318
#, fuzzy
msgid "Save"
msgstr "ファイルを保存"
#: buzz/widgets/transcription_viewer/export_transcription_menu.py:82
msgid "Save File"
msgstr "ファイルを保存"

View file

@ -3,13 +3,12 @@
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-28 16:49+0200\n"
"PO-Revision-Date: 2025-11-28 16:50+0200\n"
"POT-Creation-Date: 2025-11-23 13:02+0200\n"
"PO-Revision-Date: 2025-11-23 12:58+0200\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: lv_LV\n"
@ -558,64 +557,68 @@ msgstr "Skats"
msgid "Timestamps"
msgstr "Laiks"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:211
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:215
msgid "Export"
msgstr "Eksportēt"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:230
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:234
msgid "Translate"
msgstr "Tulkot"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:240
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:177
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:244
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:175
msgid "Resize"
msgstr "Mainīt garumu"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:252
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:257
msgid "Identify Speakers"
msgstr "Noteikt runātājus"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:269
msgid "Find"
msgstr "Meklēt"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:255
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:272
msgid "Show/Hide Search Bar (Ctrl+F)"
msgstr "Rādīt/Slēpt meklēšanas joslu (Ctrl+F)"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:320
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:337
msgid "Find:"
msgstr "Meklēt:"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:326
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:343
msgid "Enter text to find..."
msgstr "Ievadiet meklējamo..."
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:339
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:356
msgid "Previous match (Shift+Enter)"
msgstr "Iepriekšējais rezultāts (Shift+Enter)"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:347
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:364
msgid "Next match (Enter)"
msgstr "Nākamais rezultāts (Enter)"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:355
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:372
msgid "Clear"
msgstr "Notīrīt"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:382
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:399
msgid "Playback Controls:"
msgstr "Atskaņošanas iespējas:"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:387
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:404
msgid "Loop Segment"
msgstr "Atkārtot segmentu"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:389
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:406
msgid "Enable/disable looping when clicking on transcript segments"
msgstr "Nosaka vai atkārtot izvēlēto segmentu"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:395
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:412
msgid "Follow Audio"
msgstr "Sekot audio"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:397
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:414
msgid ""
"Enable/disable following the current audio position in the transcript. When "
"enabled, automatically scrolls to current text."
@ -623,75 +626,144 @@ msgstr ""
"Nosaka vai atskaņojot audio iezīmētajam segmentam vajadzētu automātiski "
"sekot tam kas tiek atskaņots."
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:444
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:461
msgid "Scroll to Current"
msgstr "Pāriet uz tekošo"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:446
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:463
msgid "Scroll to the currently spoken text"
msgstr "Pāriet uz šobrīd atskaņojamo tesktu"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:768
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:785
msgid "1 of 100+ matches"
msgstr "1 no 100+ "
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
msgid "1 of "
msgstr "1 no "
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " matches"
msgstr " "
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:775
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:792
msgid "No matches found"
msgstr "Nekas nav atrasts"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:834
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:851
msgid " of 100+ matches"
msgstr " no 100+"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " of "
msgstr " no "
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1191
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1208
msgid "API Key Required"
msgstr "API atslēgas kļūda"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1192
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1209
msgid "Please enter OpenAI API Key in preferences"
msgstr "Lūdzu ievadiet OpenAI API atslēgu iestatījumos"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:159
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:157
msgid "Resize Options"
msgstr "Garuma maiņas iestatījumi"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:170
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:168
msgid "Desired subtitle length"
msgstr "Vēlamais teksta garums"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:195
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:193
msgid "Merge Options"
msgstr "Apvienošanas iestatījumi"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:206
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:204
msgid "Merge by gap"
msgstr "Apvienot pēc attāluma"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:214
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:212
msgid "Split by punctuation"
msgstr "Dalīt pie pieturzīmēm"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:222
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:220
msgid "Split by max length"
msgstr "Dalīt pie maksimālā garuma"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:234
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:232
msgid "Merge"
msgstr "Apvienot"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:92
msgid "1/8 Collecting transcripts"
msgstr "1/8 Apkopo transkripcijas"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:106
msgid "2/8 Loading audio"
msgstr "2/8 Ielādē audio"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:115
msgid "3/8 Loading alignment model"
msgstr "3/8 Ielādē identifikācijas modeli"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:121
msgid "4/8 Processing audio"
msgstr "4/8 Apstrādā audio"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:133
msgid "5/8 Preparing transcripts"
msgstr "5/8 Sagatavo transkripcijas"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:151
msgid "6/8 Identifying speakers"
msgstr "6/8 Nosaka runātājus"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:160
msgid "0/0 Error identifying speakers"
msgstr "0/0 Kļūda nosakot runātājus"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:168
msgid "7/8 Mapping speakers to transcripts"
msgstr "7/8 Marķē runātāju teikumus"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:207
msgid "8/8 Identification done"
msgstr "8/8 Runātāju noteikšana pabeigta"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:243
msgid "Step 1: Identify speakers"
msgstr "1. solis: Runātāju noteikšana"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:255
msgid "Identify"
msgstr "Noteikt"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:265
msgid "Ready to identify speakers"
msgstr "Gatavs noteikt runātājus"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:267
msgid "Audio file not found"
msgstr "Audio datne nav atrasta"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:283
msgid "Step 2: Name speakers"
msgstr "2. solis: Runātāju identifikācija"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:298
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:391
msgid "Play sample"
msgstr "Atskaņot paraugu"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:313
msgid "Merge speaker sentences"
msgstr "Apvienot secīgus runātāja teikumus"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:318
msgid "Save"
msgstr "Saglabāt"
#: buzz/widgets/transcription_viewer/export_transcription_menu.py:82
msgid "Save File"
msgstr "Saglabāt failu"

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-28 16:49+0200\n"
"POT-Creation-Date: 2025-11-23 12:55+0200\n"
"PO-Revision-Date: 2025-03-20 18:30+0100\n"
"Last-Translator: Heimen Stoffels <vistausss@fastmail.com>\n"
"Language-Team: none\n"
@ -552,138 +552,213 @@ msgstr "Bekijken"
msgid "Timestamps"
msgstr "Tijdstippen"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:211
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:215
msgid "Export"
msgstr "Exporteren"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:230
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:234
msgid "Translate"
msgstr "Vertalen"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:240
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:177
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:244
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:175
msgid "Resize"
msgstr "Grootte"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:252
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:257
msgid "Identify Speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:269
msgid "Find"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:255
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:272
msgid "Show/Hide Search Bar (Ctrl+F)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:320
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:337
msgid "Find:"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:326
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:343
msgid "Enter text to find..."
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:339
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:356
msgid "Previous match (Shift+Enter)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:347
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:364
msgid "Next match (Enter)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:355
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:372
msgid "Clear"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:382
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:399
msgid "Playback Controls:"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:387
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:404
msgid "Loop Segment"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:389
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:406
msgid "Enable/disable looping when clicking on transcript segments"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:395
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:412
msgid "Follow Audio"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:397
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:414
msgid ""
"Enable/disable following the current audio position in the transcript. When "
"enabled, automatically scrolls to current text."
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:444
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:461
msgid "Scroll to Current"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:446
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:463
msgid "Scroll to the currently spoken text"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:768
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:785
msgid "1 of 100+ matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
msgid "1 of "
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:775
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:792
msgid "No matches found"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:834
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:851
msgid " of 100+ matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " of "
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1191
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1208
msgid "API Key Required"
msgstr "Api-sleutel vereist"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1192
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1209
msgid "Please enter OpenAI API Key in preferences"
msgstr "Voer de OpenAI-api-sleutel in in de instellingen"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:159
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:157
msgid "Resize Options"
msgstr "Grootteopties"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:170
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:168
msgid "Desired subtitle length"
msgstr "Voorkeurslengte van ondertiteling"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:195
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:193
msgid "Merge Options"
msgstr "Samenvoegopties"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:206
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:204
msgid "Merge by gap"
msgstr "Samenvoegen op basis van tussenruimte"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:214
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:212
msgid "Split by punctuation"
msgstr "Splitsen op basis van leestekens"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:222
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:220
msgid "Split by max length"
msgstr "Splitsen op basis van max. lengte"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:234
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:232
msgid "Merge"
msgstr "Samenvoegen"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:92
msgid "1/8 Collecting transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:106
msgid "2/8 Loading audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:115
msgid "3/8 Loading alignment model"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:121
msgid "4/8 Processing audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:133
#, fuzzy
msgid "5/8 Preparing transcripts"
msgstr "Transcriptie wissen"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:151
msgid "6/8 Identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:160
msgid "0/0 Error identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:168
msgid "7/8 Mapping speakers to transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:207
msgid "8/8 Identification done"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:243
msgid "Step 1: Identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:255
msgid "Identify"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:265
msgid "Ready to identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:267
msgid "Audio file not found"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:283
msgid "Step 2: Name speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:298
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:391
msgid "Play sample"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:313
msgid "Merge speaker sentences"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:318
#, fuzzy
msgid "Save"
msgstr "Bestand opslaan"
#: buzz/widgets/transcription_viewer/export_transcription_menu.py:82
msgid "Save File"
msgstr "Bestand opslaan"

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-28 16:49+0200\n"
"POT-Creation-Date: 2025-11-23 12:55+0200\n"
"PO-Revision-Date: 2024-03-17 20:50+0200\n"
"Last-Translator: \n"
"Language-Team: \n"
@ -561,138 +561,213 @@ msgstr ""
msgid "Timestamps"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:211
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:215
msgid "Export"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:230
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:234
msgid "Translate"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:240
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:177
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:244
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:175
msgid "Resize"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:252
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:257
msgid "Identify Speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:269
msgid "Find"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:255
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:272
msgid "Show/Hide Search Bar (Ctrl+F)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:320
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:337
msgid "Find:"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:326
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:343
msgid "Enter text to find..."
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:339
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:356
msgid "Previous match (Shift+Enter)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:347
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:364
msgid "Next match (Enter)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:355
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:372
msgid "Clear"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:382
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:399
msgid "Playback Controls:"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:387
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:404
msgid "Loop Segment"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:389
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:406
msgid "Enable/disable looping when clicking on transcript segments"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:395
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:412
msgid "Follow Audio"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:397
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:414
msgid ""
"Enable/disable following the current audio position in the transcript. When "
"enabled, automatically scrolls to current text."
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:444
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:461
msgid "Scroll to Current"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:446
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:463
msgid "Scroll to the currently spoken text"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:768
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:785
msgid "1 of 100+ matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
msgid "1 of "
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:775
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:792
msgid "No matches found"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:834
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:851
msgid " of 100+ matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " of "
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1191
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1208
msgid "API Key Required"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1192
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1209
msgid "Please enter OpenAI API Key in preferences"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:159
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:157
msgid "Resize Options"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:170
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:168
msgid "Desired subtitle length"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:195
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:193
msgid "Merge Options"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:206
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:204
msgid "Merge by gap"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:214
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:212
msgid "Split by punctuation"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:222
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:220
msgid "Split by max length"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:234
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:232
msgid "Merge"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:92
msgid "1/8 Collecting transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:106
msgid "2/8 Loading audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:115
msgid "3/8 Loading alignment model"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:121
msgid "4/8 Processing audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:133
#, fuzzy
msgid "5/8 Preparing transcripts"
msgstr "Anuluj transkrypcję"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:151
msgid "6/8 Identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:160
msgid "0/0 Error identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:168
msgid "7/8 Mapping speakers to transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:207
msgid "8/8 Identification done"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:243
msgid "Step 1: Identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:255
msgid "Identify"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:265
msgid "Ready to identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:267
msgid "Audio file not found"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:283
msgid "Step 2: Name speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:298
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:391
msgid "Play sample"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:313
msgid "Merge speaker sentences"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:318
#, fuzzy
msgid "Save"
msgstr "Zapisz plik"
#: buzz/widgets/transcription_viewer/export_transcription_menu.py:82
#, fuzzy
msgid "Save File"

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Buzz\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-28 16:49+0200\n"
"POT-Creation-Date: 2025-11-23 12:55+0200\n"
"PO-Revision-Date: 2025-11-01 17:43-0300\n"
"Last-Translator: Paulo Schopf <pschopf@gmail.com>\n"
"Language-Team: none\n"
@ -552,64 +552,68 @@ msgstr "Visualizar"
msgid "Timestamps"
msgstr "Marcações de tempo"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:211
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:215
msgid "Export"
msgstr "Exportar"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:230
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:234
msgid "Translate"
msgstr "Traduzir"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:240
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:177
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:244
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:175
msgid "Resize"
msgstr "Redimensionar"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:252
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:257
msgid "Identify Speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:269
msgid "Find"
msgstr "Procurar"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:255
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:272
msgid "Show/Hide Search Bar (Ctrl+F)"
msgstr "Mostrar/Ocultar a Barra de Pesquisa"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:320
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:337
msgid "Find:"
msgstr "Procurar:"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:326
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:343
msgid "Enter text to find..."
msgstr "Digite o texto a procurar..."
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:339
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:356
msgid "Previous match (Shift+Enter)"
msgstr "Encontro prévio (Shift+Enter)"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:347
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:364
msgid "Next match (Enter)"
msgstr "Póximo encontro (Enter)"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:355
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:372
msgid "Clear"
msgstr "Limpar"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:382
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:399
msgid "Playback Controls:"
msgstr "Controles de Reprodução:"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:387
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:404
msgid "Loop Segment"
msgstr "Segmento de Loop"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:389
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:406
msgid "Enable/disable looping when clicking on transcript segments"
msgstr "Habilitar/desabilitar loop ao clicar em segmentos de transcrição"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:395
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:412
msgid "Follow Audio"
msgstr "Siga o Áudio"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:397
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:414
msgid ""
"Enable/disable following the current audio position in the transcript. When "
"enabled, automatically scrolls to current text."
@ -617,75 +621,146 @@ msgstr ""
"Ativar/desativar a opção de seguir a posição atual do áudio na transcrição. "
"Quando ativado, rola automaticamente para o texto atual."
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:444
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:461
msgid "Scroll to Current"
msgstr "Rolar para o Atual"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:446
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:463
msgid "Scroll to the currently spoken text"
msgstr "Role até o texto falado no momento"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:768
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:785
msgid "1 of 100+ matches"
msgstr "1 de 100+ encontros"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
msgid "1 of "
msgstr "1 de "
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " matches"
msgstr " encontros"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:775
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:792
msgid "No matches found"
msgstr "Nada encontrado"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:834
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:851
msgid " of 100+ matches"
msgstr " de 100+ encontros"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " of "
msgstr " de "
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1191
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1208
msgid "API Key Required"
msgstr "Chave API Necessária"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1192
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1209
msgid "Please enter OpenAI API Key in preferences"
msgstr "Insira a chave API OpenAI nas preferências"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:159
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:157
msgid "Resize Options"
msgstr "Opções de Redimensionamento"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:170
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:168
msgid "Desired subtitle length"
msgstr "Duração desejada da legenda"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:195
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:193
msgid "Merge Options"
msgstr "Opções de Mesclagem"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:206
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:204
msgid "Merge by gap"
msgstr "Mesclar por intervalo"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:214
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:212
msgid "Split by punctuation"
msgstr "Dividir por pontuação"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:222
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:220
msgid "Split by max length"
msgstr "Dividir por tamanho máximo"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:234
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:232
msgid "Merge"
msgstr "Mesclar"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:92
msgid "1/8 Collecting transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:106
msgid "2/8 Loading audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:115
msgid "3/8 Loading alignment model"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:121
msgid "4/8 Processing audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:133
#, fuzzy
msgid "5/8 Preparing transcripts"
msgstr "Iniciando transcrição..."
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:151
msgid "6/8 Identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:160
msgid "0/0 Error identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:168
msgid "7/8 Mapping speakers to transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:207
msgid "8/8 Identification done"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:243
msgid "Step 1: Identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:255
msgid "Identify"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:265
msgid "Ready to identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:267
msgid "Audio file not found"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:283
msgid "Step 2: Name speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:298
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:391
msgid "Play sample"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:313
msgid "Merge speaker sentences"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:318
#, fuzzy
msgid "Save"
msgstr "Salvar Arquivo"
#: buzz/widgets/transcription_viewer/export_transcription_menu.py:82
msgid "Save File"
msgstr "Salvar Arquivo"

View file

@ -2,7 +2,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-28 16:49+0200\n"
"POT-Creation-Date: 2025-11-23 12:55+0200\n"
"PO-Revision-Date: \n"
"Last-Translator: Yevhen Popok <xalt7x.service@gmail.com>\n"
"Language-Team: \n"
@ -550,138 +550,213 @@ msgstr "Вигляд"
msgid "Timestamps"
msgstr "Позначки часу"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:211
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:215
msgid "Export"
msgstr "Експорт"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:230
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:234
msgid "Translate"
msgstr "Перекласти"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:240
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:177
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:244
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:175
msgid "Resize"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:252
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:257
msgid "Identify Speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:269
msgid "Find"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:255
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:272
msgid "Show/Hide Search Bar (Ctrl+F)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:320
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:337
msgid "Find:"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:326
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:343
msgid "Enter text to find..."
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:339
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:356
msgid "Previous match (Shift+Enter)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:347
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:364
msgid "Next match (Enter)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:355
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:372
msgid "Clear"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:382
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:399
msgid "Playback Controls:"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:387
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:404
msgid "Loop Segment"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:389
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:406
msgid "Enable/disable looping when clicking on transcript segments"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:395
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:412
msgid "Follow Audio"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:397
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:414
msgid ""
"Enable/disable following the current audio position in the transcript. When "
"enabled, automatically scrolls to current text."
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:444
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:461
msgid "Scroll to Current"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:446
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:463
msgid "Scroll to the currently spoken text"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:768
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:785
msgid "1 of 100+ matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
msgid "1 of "
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:775
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:792
msgid "No matches found"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:834
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:851
msgid " of 100+ matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " of "
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1191
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1208
msgid "API Key Required"
msgstr "Потрібен API-ключ"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1192
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1209
msgid "Please enter OpenAI API Key in preferences"
msgstr "Будь ласка, введіть API-ключ OpenAI в налаштуваннях"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:159
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:157
msgid "Resize Options"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:170
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:168
msgid "Desired subtitle length"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:195
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:193
msgid "Merge Options"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:206
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:204
msgid "Merge by gap"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:214
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:212
msgid "Split by punctuation"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:222
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:220
msgid "Split by max length"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:234
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:232
msgid "Merge"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:92
msgid "1/8 Collecting transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:106
msgid "2/8 Loading audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:115
msgid "3/8 Loading alignment model"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:121
msgid "4/8 Processing audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:133
#, fuzzy
msgid "5/8 Preparing transcripts"
msgstr "Скасувати транскрипцію"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:151
msgid "6/8 Identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:160
msgid "0/0 Error identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:168
msgid "7/8 Mapping speakers to transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:207
msgid "8/8 Identification done"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:243
msgid "Step 1: Identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:255
msgid "Identify"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:265
msgid "Ready to identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:267
msgid "Audio file not found"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:283
msgid "Step 2: Name speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:298
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:391
msgid "Play sample"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:313
msgid "Merge speaker sentences"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:318
#, fuzzy
msgid "Save"
msgstr "Зберегти файл"
#: buzz/widgets/transcription_viewer/export_transcription_menu.py:82
msgid "Save File"
msgstr "Зберегти файл"

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-28 16:49+0200\n"
"POT-Creation-Date: 2025-11-23 12:55+0200\n"
"PO-Revision-Date: 2023-05-01 15:45+0800\n"
"Last-Translator: \n"
"Language-Team: lamb \n"
@ -562,139 +562,214 @@ msgstr "查看"
msgid "Timestamps"
msgstr "时间戳"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:211
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:215
msgid "Export"
msgstr "导出"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:230
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:234
msgid "Translate"
msgstr "翻译"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:240
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:177
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:244
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:175
msgid "Resize"
msgstr "调整大小"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:252
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:257
msgid "Identify Speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:269
msgid "Find"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:255
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:272
msgid "Show/Hide Search Bar (Ctrl+F)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:320
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:337
msgid "Find:"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:326
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:343
msgid "Enter text to find..."
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:339
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:356
msgid "Previous match (Shift+Enter)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:347
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:364
msgid "Next match (Enter)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:355
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:372
msgid "Clear"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:382
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:399
msgid "Playback Controls:"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:387
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:404
msgid "Loop Segment"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:389
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:406
msgid "Enable/disable looping when clicking on transcript segments"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:395
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:412
msgid "Follow Audio"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:397
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:414
msgid ""
"Enable/disable following the current audio position in the transcript. When "
"enabled, automatically scrolls to current text."
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:444
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:461
msgid "Scroll to Current"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:446
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:463
msgid "Scroll to the currently spoken text"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:768
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:785
msgid "1 of 100+ matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
msgid "1 of "
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:775
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:792
msgid "No matches found"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:834
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:851
msgid " of 100+ matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " of "
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1191
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1208
msgid "API Key Required"
msgstr "需要API Key"
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1192
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1209
msgid "Please enter OpenAI API Key in preferences"
msgstr "请在偏好设置中输入OpenAI API Key"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:159
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:157
#, fuzzy
msgid "Resize Options"
msgstr "调整大小"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:170
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:168
msgid "Desired subtitle length"
msgstr "所需字幕长度"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:195
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:193
msgid "Merge Options"
msgstr "合并选项"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:206
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:204
msgid "Merge by gap"
msgstr "按间隔合并"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:214
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:212
msgid "Split by punctuation"
msgstr "按标点符号拆分"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:222
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:220
msgid "Split by max length"
msgstr "按最大长度拆分"
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:234
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:232
msgid "Merge"
msgstr "合并"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:92
msgid "1/8 Collecting transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:106
msgid "2/8 Loading audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:115
msgid "3/8 Loading alignment model"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:121
msgid "4/8 Processing audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:133
#, fuzzy
msgid "5/8 Preparing transcripts"
msgstr "取消识别"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:151
msgid "6/8 Identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:160
msgid "0/0 Error identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:168
msgid "7/8 Mapping speakers to transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:207
msgid "8/8 Identification done"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:243
msgid "Step 1: Identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:255
msgid "Identify"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:265
msgid "Ready to identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:267
msgid "Audio file not found"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:283
msgid "Step 2: Name speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:298
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:391
msgid "Play sample"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:313
msgid "Merge speaker sentences"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:318
#, fuzzy
msgid "Save"
msgstr "保存文件"
#: buzz/widgets/transcription_viewer/export_transcription_menu.py:82
#, fuzzy
msgid "Save File"

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-28 16:49+0200\n"
"POT-Creation-Date: 2025-11-23 12:55+0200\n"
"PO-Revision-Date: 2023-05-01 15:45+0800\n"
"Last-Translator: \n"
"Language-Team: Lamb\n"
@ -557,138 +557,213 @@ msgstr ""
msgid "Timestamps"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:211
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:215
msgid "Export"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:230
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:234
msgid "Translate"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:240
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:177
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:244
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:175
msgid "Resize"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:252
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:257
msgid "Identify Speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:269
msgid "Find"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:255
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:272
msgid "Show/Hide Search Bar (Ctrl+F)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:320
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:337
msgid "Find:"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:326
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:343
msgid "Enter text to find..."
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:339
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:356
msgid "Previous match (Shift+Enter)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:347
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:364
msgid "Next match (Enter)"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:355
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:372
msgid "Clear"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:382
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:399
msgid "Playback Controls:"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:387
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:404
msgid "Loop Segment"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:389
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:406
msgid "Enable/disable looping when clicking on transcript segments"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:395
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:412
msgid "Follow Audio"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:397
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:414
msgid ""
"Enable/disable following the current audio position in the transcript. When "
"enabled, automatically scrolls to current text."
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:444
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:461
msgid "Scroll to Current"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:446
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:463
msgid "Scroll to the currently spoken text"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:768
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:785
msgid "1 of 100+ matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
msgid "1 of "
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:770
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:787
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:775
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:792
msgid "No matches found"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:834
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:851
msgid " of 100+ matches"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:836
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:853
msgid " of "
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1191
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1208
msgid "API Key Required"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1192
#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py:1209
msgid "Please enter OpenAI API Key in preferences"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:159
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:157
msgid "Resize Options"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:170
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:168
msgid "Desired subtitle length"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:195
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:193
msgid "Merge Options"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:206
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:204
msgid "Merge by gap"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:214
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:212
msgid "Split by punctuation"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:222
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:220
msgid "Split by max length"
msgstr ""
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:234
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py:232
msgid "Merge"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:92
msgid "1/8 Collecting transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:106
msgid "2/8 Loading audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:115
msgid "3/8 Loading alignment model"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:121
msgid "4/8 Processing audio"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:133
#, fuzzy
msgid "5/8 Preparing transcripts"
msgstr "取消錄製"
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:151
msgid "6/8 Identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:160
msgid "0/0 Error identifying speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:168
msgid "7/8 Mapping speakers to transcripts"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:207
msgid "8/8 Identification done"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:243
msgid "Step 1: Identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:255
msgid "Identify"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:265
msgid "Ready to identify speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:267
msgid "Audio file not found"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:283
msgid "Step 2: Name speakers"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:298
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:391
msgid "Play sample"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:313
msgid "Merge speaker sentences"
msgstr ""
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py:318
#, fuzzy
msgid "Save"
msgstr "檔案"
#: buzz/widgets/transcription_viewer/export_transcription_menu.py:82
#, fuzzy
msgid "Save File"

View file

@ -4,7 +4,6 @@ import sys
import logging
import subprocess
import json
import tempfile
from typing import List
from buzz.assets import APP_BASE_DIR
from buzz.transcriber.transcriber import Segment, Task, FileTranscriptionTask
@ -58,9 +57,7 @@ class WhisperCpp:
file_to_process = task.file_path
if file_ext not in supported_formats:
# Create temporary WAV file
temp_dir = tempfile.gettempdir()
temp_file = os.path.join(temp_dir, f"buzz_temp_{os.path.basename(task.file_path)}.wav")
temp_file = task.file_path + ".wav"
logging.info(f"Converting {task.file_path} to WAV format")

View file

@ -82,6 +82,10 @@ class ResizeIcon(Icon):
def __init__(self, parent: QWidget):
super().__init__(get_path("assets/resize_black.svg"), parent)
class SpeakerIdentificationIcon(Icon):
def __init__(self, parent: QWidget):
super().__init__(get_path("assets/speaker-identification.svg"), parent)
class VisibilityIcon(Icon):
def __init__(self, parent: QWidget):
super().__init__(

View file

@ -0,0 +1,504 @@
import re
import os
import logging
import faster_whisper
import torch
import random
from typing import Optional
from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput
from PyQt6.QtCore import Qt, QThread, QObject, pyqtSignal, QUrl, QTimer
from PyQt6.QtGui import QFont
from PyQt6.QtWidgets import (
QWidget,
QFormLayout,
QVBoxLayout,
QHBoxLayout,
QLabel,
QProgressBar,
QPushButton,
QCheckBox,
QGroupBox,
QSpacerItem,
QSizePolicy,
QLayout,
)
from buzz.locale import _
from buzz.db.entity.transcription import Transcription
from buzz.db.service.transcription_service import TranscriptionService
from buzz.paths import file_path_as_title
from buzz.settings.settings import Settings
from buzz.widgets.line_edit import LineEdit
from buzz.transcriber.transcriber import Segment
from ctc_forced_aligner.ctc_forced_aligner import (
generate_emissions,
get_alignments,
get_spans,
load_alignment_model,
postprocess_results,
preprocess_text,
)
from whisper_diarization.helpers import (
get_realigned_ws_mapping_with_punctuation,
get_sentences_speaker_mapping,
get_words_speaker_mapping,
langs_to_iso,
punct_model_langs,
)
from deepmultilingualpunctuation.deepmultilingualpunctuation import PunctuationModel
from whisper_diarization.diarization import MSDDDiarizer
SENTENCE_END = re.compile(r'.*[.!?。!?]')
class IdentificationWorker(QObject):
finished = pyqtSignal(list)
progress_update = pyqtSignal(str)
def __init__(self, transcription, transcription_service):
super().__init__()
self.transcription = transcription
self.transcription_service = transcription_service
def get_transcript(self, audio, **kwargs) -> dict:
buzz_segments = self.transcription_service.get_transcription_segments(
transcription_id=self.transcription.id_as_uuid
)
segments = []
words = []
text = ""
for buzz_segment in buzz_segments:
words.append({
'word': buzz_segment.text + " ",
'start': buzz_segment.start_time / 100,
'end': buzz_segment.end_time / 100,
})
text += buzz_segment.text + " "
if SENTENCE_END.match(buzz_segment.text):
segments.append({
'text': text,
'words': words
})
words = []
text = ""
return {
'language': self.transcription.language,
'segments': segments
}
def run(self):
self.progress_update.emit(_("1/8 Collecting transcripts"))
# Step 1 - Get transcript
# TODO - Add detected language to the transcript, detect and store separately in metadata
# Will also be relevant for template parsing of transcript file names
# - See diarize.py for example on how to get this info from whisper transcript, maybe other whisper models also have it
language = self.transcription.language if self.transcription.language else "en"
segments = self.transcription_service.get_transcription_segments(
transcription_id=self.transcription.id_as_uuid
)
full_transcript = "".join(segment.text for segment in segments)
self.progress_update.emit(_("2/8 Loading audio"))
audio_waveform = faster_whisper.decode_audio(self.transcription.file)
# Step 2 - Forced alignment
force_cpu = os.getenv("BUZZ_FORCE_CPU", "false")
use_cuda = torch.cuda.is_available() and force_cpu == "false"
device = "cuda" if use_cuda else "cpu"
torch_dtype = torch.float16 if use_cuda else torch.float32
self.progress_update.emit(_("3/8 Loading alignment model"))
alignment_model, alignment_tokenizer = load_alignment_model(
device,
dtype=torch_dtype,
)
self.progress_update.emit(_("4/8 Processing audio"))
emissions, stride = generate_emissions(
alignment_model,
torch.from_numpy(audio_waveform)
.to(alignment_model.dtype)
.to(alignment_model.device),
batch_size=8,
)
del alignment_model
torch.cuda.empty_cache()
self.progress_update.emit(_("5/8 Preparing transcripts"))
tokens_starred, text_starred = preprocess_text(
full_transcript,
romanize=True,
language=langs_to_iso[language],
)
segments, scores, blank_token = get_alignments(
emissions,
tokens_starred,
alignment_tokenizer,
)
spans = get_spans(tokens_starred, segments, blank_token)
word_timestamps = postprocess_results(text_starred, spans, stride, scores)
# Step 3 - Diarization
self.progress_update.emit(_("6/8 Identifying speakers"))
try:
diarizer_model = MSDDDiarizer(device)
speaker_ts = diarizer_model.diarize(torch.from_numpy(audio_waveform).unsqueeze(0))
except Exception as e:
self.progress_update.emit(_("0/0 Error identifying speakers"))
logging.error(f"Error during diarization: {e}")
return
finally:
del diarizer_model
torch.cuda.empty_cache()
# Step 4 - Reading timestamps <> Speaker Labels mapping
self.progress_update.emit(_("7/8 Mapping speakers to transcripts"))
wsm = get_words_speaker_mapping(word_timestamps, speaker_ts, "start")
if language in punct_model_langs:
# restoring punctuation in the transcript to help realign the sentences
punct_model = PunctuationModel(model="kredor/punctuate-all")
words_list = list(map(lambda x: x["word"], wsm))
labled_words = punct_model.predict(words_list, chunk_size=230)
ending_puncts = ".?!。!?"
model_puncts = ".,;:!?。!?"
# We don't want to punctuate U.S.A. with a period. Right?
is_acronym = lambda x: re.fullmatch(r"\b(?:[a-zA-Z]\.){2,}", x)
for word_dict, labeled_tuple in zip(wsm, labled_words):
word = word_dict["word"]
if (
word
and labeled_tuple[1] in ending_puncts
and (word[-1] not in model_puncts or is_acronym(word))
):
word += labeled_tuple[1]
if word.endswith(".."):
word = word.rstrip(".")
word_dict["word"] = word
else:
logging.warning(
f"Punctuation restoration is not available for {language} language."
" Using the original punctuation."
)
wsm = get_realigned_ws_mapping_with_punctuation(wsm)
ssm = get_sentences_speaker_mapping(wsm, speaker_ts)
self.progress_update.emit(_("8/8 Identification done"))
self.finished.emit(ssm)
class SpeakerIdentificationWidget(QWidget):
resize_button_clicked = pyqtSignal()
transcription: Transcription
settings = Settings()
def __init__(
self,
transcription: Transcription,
transcription_service: TranscriptionService,
parent: Optional["QWidget"] = None,
flags: Qt.WindowType = Qt.WindowType.Widget,
transcriptions_updated_signal: Optional[pyqtSignal] = None,
) -> None:
super().__init__(parent, flags)
self.transcription = transcription
self.transcription_service = transcription_service
self.transcriptions_updated_signal = transcriptions_updated_signal
self.identification_result = None
self.thread = None
self.worker = None
self.setMinimumWidth(650)
self.setMinimumHeight(400)
self.setWindowTitle(file_path_as_title(transcription.file))
layout = QFormLayout(self)
layout.setSizeConstraint(QLayout.SizeConstraint.SetMinAndMaxSize)
# Step 1: Identify speakers
step_1_label = QLabel(_("Step 1: Identify speakers"), self)
font = step_1_label.font()
font.setWeight(QFont.Weight.Bold)
step_1_label.setFont(font)
layout.addRow(step_1_label)
step_1_group_box = QGroupBox(self)
step_1_group_box.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
step_1_layout = QVBoxLayout(step_1_group_box)
self.step_1_row = QHBoxLayout()
self.step_1_button = QPushButton(_("Identify"))
self.step_1_button.setMinimumWidth(200)
self.step_1_button.clicked.connect(self.on_identify_button_clicked)
# Progress container with label and bar
progress_container = QVBoxLayout()
self.progress_label = QLabel(self)
if os.path.isfile(self.transcription.file):
self.progress_label.setText(_("Ready to identify speakers"))
else:
self.progress_label.setText(_("Audio file not found"))
self.step_1_button.setEnabled(False)
self.progress_bar = QProgressBar(self)
self.progress_bar.setMinimumWidth(400)
self.progress_bar.setRange(0, 8)
self.progress_bar.setValue(0)
progress_container.addWidget(self.progress_label)
progress_container.addWidget(self.progress_bar)
self.step_1_row.addLayout(progress_container)
self.step_1_row.addWidget(self.step_1_button, alignment=Qt.AlignmentFlag.AlignTop)
step_1_layout.addLayout(self.step_1_row)
layout.addRow(step_1_group_box)
# Spacer
spacer = QSpacerItem(0, 10, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed)
layout.addItem(spacer)
# Step 2: Name speakers
step_2_label = QLabel(_("Step 2: Name speakers"), self)
font = step_2_label.font()
font.setWeight(QFont.Weight.Bold)
step_2_label.setFont(font)
layout.addRow(step_2_label)
self.step_2_group_box = QGroupBox(self)
self.step_2_group_box.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
self.step_2_group_box.setEnabled(False)
step_2_layout = QVBoxLayout(self.step_2_group_box)
self.speaker_preview_row = QVBoxLayout()
self.speaker_0_input = LineEdit("Speaker 0", self)
self.speaker_0_preview_button = QPushButton(_("Play sample"))
self.speaker_0_preview_button.setMinimumWidth(200)
self.speaker_0_preview_button.clicked.connect(lambda: self.on_speaker_preview("Speaker 0"))
speaker_0_layout = QHBoxLayout()
speaker_0_layout.addWidget(self.speaker_0_input)
speaker_0_layout.addWidget(self.speaker_0_preview_button)
self.speaker_preview_row.addLayout(speaker_0_layout)
step_2_layout.addLayout(self.speaker_preview_row)
layout.addRow(self.step_2_group_box)
# Save button
self.merge_speaker_sentences = QCheckBox(_("Merge speaker sentences"))
self.merge_speaker_sentences.setChecked(True)
self.merge_speaker_sentences.setEnabled(False)
self.merge_speaker_sentences.setMinimumWidth(250)
self.save_button = QPushButton(_("Save"))
self.save_button.setEnabled(False)
self.save_button.clicked.connect(self.on_save_button_clicked)
layout.addRow(self.merge_speaker_sentences)
layout.addRow(self.save_button)
self.setLayout(layout)
# Invisible preview player
url = QUrl.fromLocalFile(self.transcription.file)
self.player = QMediaPlayer()
self.audio_output = QAudioOutput()
self.player.setAudioOutput(self.audio_output)
self.player.setSource(url)
self.player_timer = None
def on_identify_button_clicked(self):
self.step_1_button.setEnabled(False)
self.thread = QThread()
self.worker = IdentificationWorker(
self.transcription,
self.transcription_service
)
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.run)
self.worker.finished.connect(self.thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.thread.finished.connect(self.thread.deleteLater)
self.worker.finished.connect(self.on_identification_finished)
self.worker.progress_update.connect(self.on_progress_update)
self.thread.start()
def on_progress_update(self, progress):
self.progress_label.setText(progress)
progress_value = 0
if progress and progress[0].isdigit():
progress_value = int(progress[0])
self.progress_bar.setValue(progress_value)
else:
logging.error(f"Invalid progress format: {progress}")
if progress_value == 8:
self.step_2_group_box.setEnabled(True)
self.merge_speaker_sentences.setEnabled(True)
self.save_button.setEnabled(True)
def on_identification_finished(self, result):
self.identification_result = result
unique_speakers = {entry['speaker'] for entry in result}
while self.speaker_preview_row.count():
item = self.speaker_preview_row.takeAt(0)
widget = item.widget()
if widget:
widget.deleteLater()
else:
layout = item.layout()
if layout:
while layout.count():
sub_item = layout.takeAt(0)
sub_widget = sub_item.widget()
if sub_widget:
sub_widget.deleteLater()
for speaker in sorted(unique_speakers):
speaker_input = LineEdit(speaker, self)
speaker_input.setMinimumWidth(200)
speaker_preview_button = QPushButton(_("Play sample"))
speaker_preview_button.setMinimumWidth(200)
speaker_preview_button.clicked.connect(lambda checked, s=speaker: self.on_speaker_preview(s))
speaker_layout = QHBoxLayout()
speaker_layout.addWidget(speaker_input)
speaker_layout.addWidget(speaker_preview_button)
self.speaker_preview_row.addLayout(speaker_layout)
def on_speaker_preview(self, speaker_id):
if self.player_timer:
self.player_timer.stop()
speaker_records = [record for record in self.identification_result if record['speaker'] == speaker_id]
if speaker_records:
random_record = random.choice(speaker_records)
start_time = random_record['start_time']
end_time = random_record['end_time']
self.player.setPosition(int(start_time))
self.player.play()
self.player_timer = QTimer(self)
self.player_timer.setSingleShot(True)
self.player_timer.timeout.connect(self.player.stop)
self.player_timer.start(min(end_time, 10 * 1000)) # 10 seconds
def on_save_button_clicked(self):
speaker_names = []
for i in range(self.speaker_preview_row.count()):
item = self.speaker_preview_row.itemAt(i)
if item.layout():
for j in range(item.layout().count()):
sub_item = item.layout().itemAt(j)
widget = sub_item.widget()
if isinstance(widget, LineEdit):
speaker_names.append(widget.text())
unique_speakers = {entry['speaker'] for entry in self.identification_result}
original_speakers = sorted(unique_speakers)
speaker_mapping = dict(zip(original_speakers, speaker_names))
segments = []
if self.merge_speaker_sentences.isChecked():
previous_segment = None
for entry in self.identification_result:
speaker_name = speaker_mapping.get(entry['speaker'], entry['speaker'])
if previous_segment and previous_segment['speaker'] == speaker_name:
previous_segment['end_time'] = entry['end_time']
previous_segment['text'] += " " + entry['text']
else:
if previous_segment:
segment = Segment(
start=previous_segment['start_time'],
end=previous_segment['end_time'],
text=f"{previous_segment['speaker']}: {previous_segment['text']}"
)
segments.append(segment)
previous_segment = {
'start_time': entry['start_time'],
'end_time': entry['end_time'],
'speaker': speaker_name,
'text': entry['text']
}
if previous_segment:
segment = Segment(
start=previous_segment['start_time'],
end=previous_segment['end_time'],
text=f"{previous_segment['speaker']}: {previous_segment['text']}"
)
segments.append(segment)
else:
for entry in self.identification_result:
speaker_name = speaker_mapping.get(entry['speaker'], entry['speaker'])
segment = Segment(
start=entry['start_time'],
end=entry['end_time'],
text=f"{speaker_name}: {entry['text']}"
)
segments.append(segment)
new_transcript_id = self.transcription_service.copy_transcription(
self.transcription.id_as_uuid
)
self.transcription_service.update_transcription_as_completed(new_transcript_id, segments)
# TODO - See if we can get rows in the transcription viewer to be of variable height
# If text is longer they should expand
if self.transcriptions_updated_signal:
self.transcriptions_updated_signal.emit(new_transcript_id)
self.player.stop()
if self.player_timer:
self.player_timer.stop()
self.close()
def closeEvent(self, event):
self.hide()
super().closeEvent(event)

View file

@ -37,8 +37,7 @@ from buzz.widgets.preferences_dialog.models.file_transcription_preferences impor
SENTENCE_END = re.compile(r'.*[.!?。!?]')
class TranscriptionWorker(QObject):
finished = pyqtSignal()
result_ready = pyqtSignal(list)
finished = pyqtSignal(list)
def __init__(self, transcription, transcription_options, transcription_service, regroup_string: str):
super().__init__()
@ -85,7 +84,7 @@ class TranscriptionWorker(QObject):
if self.transcription_options.extract_speech and os.path.exists(speech_path):
transcription_file = str(speech_path)
transcription_file_exists = True
# TODO - Fix VAD and Silence suppression that fails to work/download VAd model in compilded form on Mac and Windows
# TODO - Fix VAD and Silence suppression that fails to work/download Vad model in compilded form on Mac and Windows
try:
result = stable_whisper.transcribe_any(
@ -113,8 +112,7 @@ class TranscriptionWorker(QObject):
)
)
self.result_ready.emit(segments)
self.finished.emit()
self.finished.emit(segments)
class TranscriptionResizerWidget(QWidget):
@ -336,7 +334,7 @@ class TranscriptionResizerWidget(QWidget):
self.worker.finished.connect(self.thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.thread.finished.connect(self.thread.deleteLater)
self.worker.result_ready.connect(self.on_transcription_completed)
self.worker.finished.connect(self.on_transcription_completed)
self.thread.start()

View file

@ -1,5 +1,6 @@
import os
import logging
import platform
from typing import Optional
from uuid import UUID
@ -38,6 +39,7 @@ from buzz.widgets.icon import (
ResizeIcon,
ScrollToCurrentIcon,
VisibilityIcon,
SpeakerIdentificationIcon,
)
from buzz.translator import Translator
from buzz.widgets.text_display_box import TextDisplayBox
@ -59,6 +61,10 @@ from buzz.widgets.transcription_viewer.transcription_view_mode_tool_button impor
)
from buzz.widgets.transcription_viewer.transcription_resizer_widget import TranscriptionResizerWidget
# Underlying libs do not support intel Macs
if not (platform.system() == "Darwin" and platform.machine() == "x86_64"):
from buzz.widgets.transcription_viewer.speaker_identification_widget import SpeakerIdentificationWidget
class TranscriptionViewerWidget(QWidget):
resize_button_clicked = pyqtSignal()
@ -85,6 +91,7 @@ class TranscriptionViewerWidget(QWidget):
self.setWindowTitle(file_path_as_title(transcription.file))
self.transcription_resizer_dialog = None
self.speaker_identification_dialog = None
self.transcriptions_updated_signal = transcriptions_updated_signal
self.translation_thread = None
@ -98,7 +105,7 @@ class TranscriptionViewerWidget(QWidget):
# Loop functionality
self.segment_looping_enabled = self.settings.settings.value("transcription_viewer/segment_looping_enabled", False, type=bool)
# UI visibility preferences
self.playback_controls_visible = self.settings.settings.value("transcription_viewer/playback_controls_visible", False, type=bool)
self.find_widget_visible = self.settings.settings.value("transcription_viewer/find_widget_visible", False, type=bool)
@ -165,18 +172,18 @@ class TranscriptionViewerWidget(QWidget):
# Create a better current segment display that handles long text
self.current_segment_frame = QFrame()
self.current_segment_frame.setFrameStyle(QFrame.Shape.NoFrame)
segment_layout = QVBoxLayout(self.current_segment_frame)
segment_layout.setContentsMargins(4, 4, 4, 4) # Minimal margins for clean appearance
segment_layout.setSpacing(0) # No spacing between elements
# Text display - centered with scroll capability (no header label)
self.current_segment_text = QLabel("")
self.current_segment_text.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignTop)
self.current_segment_text.setWordWrap(True)
self.current_segment_text.setStyleSheet("color: #666; line-height: 1.2; margin: 0; padding: 4px;")
self.current_segment_text.setMinimumHeight(60) # Ensure minimum height for text
# Make it scrollable for long text
self.current_segment_scroll_area = QScrollArea()
self.current_segment_scroll_area.setWidget(self.current_segment_text)
@ -185,13 +192,13 @@ class TranscriptionViewerWidget(QWidget):
self.current_segment_scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.current_segment_scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
self.current_segment_scroll_area.setStyleSheet("QScrollBar:vertical { width: 12px; } QScrollBar::handle:vertical { background: #ccc; border-radius: 6px; }")
# Ensure the text label can expand to show all content
self.current_segment_text.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
# Add scroll area to layout (simplified single-widget layout)
segment_layout.addWidget(self.current_segment_scroll_area)
# Initially hide the frame until there's content
self.current_segment_frame.hide()
@ -247,6 +254,19 @@ class TranscriptionViewerWidget(QWidget):
toolbar.addWidget(resize_button)
# Underlying libs do not support intel Macs
if not (platform.system() == "Darwin" and platform.machine() == "x86_64"):
speaker_identification_button = QToolButton()
speaker_identification_button.setText(_("Identify Speakers"))
speaker_identification_button.setObjectName("speaker_identification_button")
speaker_identification_button.setIcon(SpeakerIdentificationIcon(self))
speaker_identification_button.setToolButtonStyle(
Qt.ToolButtonStyle.ToolButtonTextBesideIcon
)
speaker_identification_button.clicked.connect(self.on_speaker_identification_button_clicked)
toolbar.addWidget(speaker_identification_button)
# Add Find button
self.find_button = QToolButton()
self.find_button.setText(_("Find"))
@ -267,14 +287,14 @@ class TranscriptionViewerWidget(QWidget):
# Table widget should take the majority of the space
layout.addWidget(self.table_widget, 1) # Stretch factor 1 (majority)
# Loop controls section (minimal space)
self.create_loop_controls()
layout.addWidget(self.loop_controls_frame, 0) # Stretch factor 0 (minimal)
# Audio player (minimal space)
layout.addWidget(self.audio_player, 0) # Stretch factor 0 (minimal)
# Text display box (minimal space)
layout.addWidget(self.text_display_box, 0) # Stretch factor 0 (minimal)
@ -291,7 +311,7 @@ class TranscriptionViewerWidget(QWidget):
# Restore UI state from settings
self.restore_ui_state()
# Restore geometry from settings
self.load_geometry()
@ -302,7 +322,7 @@ class TranscriptionViewerWidget(QWidget):
# Restore playback controls visibility
if self.playback_controls_visible:
self.show_loop_controls()
# Restore find widget visibility
if self.find_widget_visible:
self.show_search_bar()
@ -312,28 +332,28 @@ class TranscriptionViewerWidget(QWidget):
self.search_frame = QFrame()
self.search_frame.setFrameStyle(QFrame.Shape.StyledPanel)
self.search_frame.setMaximumHeight(60)
search_layout = QHBoxLayout(self.search_frame)
search_layout.setContentsMargins(10, 5, 10, 5)
# Find label
search_label = QLabel(_("Find:"))
search_label.setStyleSheet("font-weight: bold;")
search_layout.addWidget(search_label)
# Find input - make it wider for better usability
self.search_input = QLineEdit()
self.search_input.setPlaceholderText(_("Enter text to find..."))
self.search_input.textChanged.connect(self.on_search_text_changed)
self.search_input.returnPressed.connect(self.search_next)
self.search_input.setMinimumWidth(300) # Increased from 200 to 300
# Add keyboard shortcuts for search navigation
from PyQt6.QtGui import QKeySequence
self.search_input.installEventFilter(self)
search_layout.addWidget(self.search_input)
# Search buttons - make them consistent height and remove hardcoded font sizes
self.search_prev_button = QPushButton("")
self.search_prev_button.setToolTip(_("Previous match (Shift+Enter)"))
@ -342,7 +362,7 @@ class TranscriptionViewerWidget(QWidget):
self.search_prev_button.setMaximumWidth(40)
self.search_prev_button.setMinimumHeight(30) # Ensure consistent height
search_layout.addWidget(self.search_prev_button)
self.search_next_button = QPushButton("")
self.search_next_button.setToolTip(_("Next match (Enter)"))
self.search_next_button.clicked.connect(self.search_next)
@ -350,21 +370,21 @@ class TranscriptionViewerWidget(QWidget):
self.search_next_button.setMaximumWidth(40)
self.search_next_button.setMinimumHeight(30) # Ensure consistent height
search_layout.addWidget(self.search_next_button)
# Clear button - make it bigger to accommodate different language translations
self.clear_search_button = QPushButton(_("Clear"))
self.clear_search_button.clicked.connect(self.clear_search)
self.clear_search_button.setMaximumWidth(80) # Increased from 60 to 80
self.clear_search_button.setMinimumHeight(30) # Ensure consistent height
search_layout.addWidget(self.clear_search_button)
# Results label
self.search_results_label = QLabel("")
self.search_results_label.setStyleSheet("color: #666;")
search_layout.addWidget(self.search_results_label)
search_layout.addStretch()
# Initially hide the search bar
self.search_frame.hide()
@ -373,23 +393,23 @@ class TranscriptionViewerWidget(QWidget):
self.loop_controls_frame = QFrame()
self.loop_controls_frame.setFrameStyle(QFrame.Shape.StyledPanel)
self.loop_controls_frame.setMaximumHeight(50)
loop_layout = QHBoxLayout(self.loop_controls_frame)
loop_layout.setContentsMargins(10, 5, 10, 5)
loop_layout.setSpacing(8) # Add some spacing between elements for better visual separation
# Loop controls label
loop_label = QLabel(_("Playback Controls:"))
loop_label.setStyleSheet("font-weight: bold;")
loop_layout.addWidget(loop_label)
# Loop toggle button
self.loop_toggle = QCheckBox(_("Loop Segment"))
self.loop_toggle.setChecked(self.segment_looping_enabled)
self.loop_toggle.setToolTip(_("Enable/disable looping when clicking on transcript segments"))
self.loop_toggle.toggled.connect(self.on_loop_toggle_changed)
loop_layout.addWidget(self.loop_toggle)
# Follow audio toggle button
self.follow_audio_enabled = self.settings.settings.value("transcription_viewer/follow_audio_enabled", False, type=bool)
self.follow_audio_toggle = QCheckBox(_("Follow Audio"))
@ -397,19 +417,19 @@ class TranscriptionViewerWidget(QWidget):
self.follow_audio_toggle.setToolTip(_("Enable/disable following the current audio position in the transcript. When enabled, automatically scrolls to current text."))
self.follow_audio_toggle.toggled.connect(self.on_follow_audio_toggle_changed)
loop_layout.addWidget(self.follow_audio_toggle)
# Visual separator
separator1 = QFrame()
separator1.setFrameShape(QFrame.Shape.VLine)
separator1.setFrameShadow(QFrame.Shadow.Sunken)
separator1.setMaximumHeight(20)
loop_layout.addWidget(separator1)
# Speed controls
speed_label = QLabel("Speed:")
speed_label.setStyleSheet("font-weight: bold;")
loop_layout.addWidget(speed_label)
self.speed_combo = QComboBox()
self.speed_combo.setEditable(True)
self.speed_combo.addItems(["0.5x", "0.75x", "1x", "1.25x", "1.5x", "2x"])
@ -417,29 +437,29 @@ class TranscriptionViewerWidget(QWidget):
self.speed_combo.currentTextChanged.connect(self.on_speed_changed)
self.speed_combo.setMaximumWidth(80)
loop_layout.addWidget(self.speed_combo)
self.speed_down_btn = QPushButton("-")
self.speed_down_btn.setMaximumWidth(40) # Match search button width
self.speed_down_btn.setMinimumHeight(30) # Match search button height
self.speed_down_btn.clicked.connect(self.decrease_speed)
loop_layout.addWidget(self.speed_down_btn)
self.speed_up_btn = QPushButton("+")
self.speed_up_btn.setMaximumWidth(40) # Match speed down button width
self.speed_up_btn.setMinimumHeight(30) # Match search button height
self.speed_up_btn.clicked.connect(self.increase_speed)
loop_layout.addWidget(self.speed_up_btn)
# Initialize speed control with current value from audio player
self.initialize_speed_control()
# Visual separator
separator2 = QFrame()
separator2.setFrameShape(QFrame.Shape.VLine)
separator2.setFrameShadow(QFrame.Shadow.Sunken)
separator2.setMaximumHeight(20)
loop_layout.addWidget(separator2)
# Scroll to current button
self.scroll_to_current_button = QPushButton(_("Scroll to Current"))
self.scroll_to_current_button.setIcon(ScrollToCurrentIcon(self))
@ -448,16 +468,16 @@ class TranscriptionViewerWidget(QWidget):
self.scroll_to_current_button.setMinimumHeight(30)
self.scroll_to_current_button.setStyleSheet("QPushButton { padding: 4px 8px; }") # Better padding
loop_layout.addWidget(self.scroll_to_current_button)
loop_layout.addStretch()
# Initially hide the loop controls frame
self.loop_controls_frame.hide()
def show_loop_controls(self):
"""Show the loop controls when audio is playing"""
self.loop_controls_frame.show()
# Save the visibility state to settings
self.playback_controls_visible = True
self.settings.settings.setValue("transcription_viewer/playback_controls_visible", self.playback_controls_visible)
@ -465,7 +485,7 @@ class TranscriptionViewerWidget(QWidget):
def hide_loop_controls(self):
"""Hide the loop controls when audio is not playing"""
self.loop_controls_frame.hide()
# Save the visibility state to settings
self.playback_controls_visible = False
self.settings.settings.setValue("transcription_viewer/playback_controls_visible", self.playback_controls_visible)
@ -600,7 +620,7 @@ class TranscriptionViewerWidget(QWidget):
def on_audio_playback_state_changed(self, state):
"""Handle audio playback state changes to automatically show/hide playback controls"""
from PyQt6.QtMultimedia import QMediaPlayer
if state == QMediaPlayer.PlaybackState.PlayingState:
# Show playback controls when audio starts playing
if self.view_mode == ViewMode.TIMESTAMPS:
@ -630,25 +650,25 @@ class TranscriptionViewerWidget(QWidget):
# Extract the numeric value from speed text (e.g., "1.5x" -> 1.5)
clean_text = speed_text.replace('x', '').strip()
speed_value = float(clean_text)
# Clamp the speed value to valid range
speed_value = max(0.1, min(5.0, speed_value))
# Update the combo box text to show the clamped value
if not speed_text.endswith('x'):
speed_text = f"{speed_value:.2f}x"
# Block signals to prevent recursion
self.speed_combo.blockSignals(True)
self.speed_combo.setCurrentText(speed_text)
self.speed_combo.blockSignals(False)
# Set the playback rate on the audio player
self.audio_player.media_player.setPlaybackRate(speed_value)
# Save the new rate to settings
self.settings.set_value(self.settings.Key.AUDIO_PLAYBACK_RATE, speed_value)
except ValueError:
logging.warning(f"Invalid speed value: {speed_text}")
# Reset to current valid value
@ -680,14 +700,14 @@ class TranscriptionViewerWidget(QWidget):
"""Set the playback speed programmatically"""
# Clamp the speed value to valid range
speed = max(0.1, min(5.0, speed))
# Update the combo box
speed_text = f"{speed:.2f}x"
self.speed_combo.setCurrentText(speed_text)
# Set the playback rate on the audio player
self.audio_player.media_player.setPlaybackRate(speed)
# Save the new rate to settings
self.settings.set_value(self.settings.Key.AUDIO_PLAYBACK_RATE, speed)
@ -707,49 +727,49 @@ class TranscriptionViewerWidget(QWidget):
"""Perform the actual search based on current view mode"""
self.search_results = []
self.current_search_index = 0
if self.view_mode == ViewMode.TIMESTAMPS:
self.search_in_table()
else: # TEXT or TRANSLATION mode
self.search_in_text()
self.update_search_ui()
def search_in_table(self):
"""Search in the table view (segments)"""
segments = self.table_widget.segments()
search_text_lower = self.search_text.lower()
# Limit search results to avoid performance issues with very long segments
max_results = 100
for i, segment in enumerate(segments):
if len(self.search_results) >= max_results:
break
text = segment.value("text").lower()
if search_text_lower in text:
self.search_results.append(("table", i, segment))
# Also search in translations if available
if self.has_translations:
for i, segment in enumerate(segments):
if len(self.search_results) >= max_results:
break
translation = segment.value("translation").lower()
if search_text_lower in translation:
self.search_results.append(("table", i, segment))
self.search_results.append(("table", i, segment))
def search_in_text(self):
"""Search in the text display box"""
text = self.text_display_box.toPlainText()
search_text_lower = self.search_text.lower()
text_lower = text.lower()
# Limit search results to avoid performance issues with very long text
max_results = 100
start = 0
result_count = 0
while True:
@ -780,9 +800,9 @@ class TranscriptionViewerWidget(QWidget):
"""Highlight the current search match"""
if not self.search_results:
return
match_type, match_data, _ = self.search_results[self.current_search_index]
if match_type == "table":
# Highlight in table
self.highlight_table_match(match_data)
@ -802,10 +822,10 @@ class TranscriptionViewerWidget(QWidget):
cursor = QTextCursor(self.text_display_box.document())
cursor.setPosition(start_pos)
cursor.setPosition(start_pos + len(self.search_text), QTextCursor.MoveMode.KeepAnchor)
# Set the cursor to highlight the text
self.text_display_box.setTextCursor(cursor)
# Ensure the highlighted text is visible
self.text_display_box.ensureCursorVisible()
@ -813,7 +833,7 @@ class TranscriptionViewerWidget(QWidget):
"""Go to next search result"""
if not self.search_results:
return
self.current_search_index = (self.current_search_index + 1) % len(self.search_results)
self.highlight_current_match()
self.update_search_results_label()
@ -822,7 +842,7 @@ class TranscriptionViewerWidget(QWidget):
"""Go to previous search result"""
if not self.search_results:
return
self.current_search_index = (self.current_search_index - 1) % len(self.search_results)
self.highlight_current_match()
self.update_search_results_label()
@ -845,13 +865,13 @@ class TranscriptionViewerWidget(QWidget):
self.search_prev_button.setEnabled(False)
self.search_next_button.setEnabled(False)
# Clear text highlighting
if self.view_mode in (ViewMode.TEXT, ViewMode.TRANSLATION):
cursor = QTextCursor(self.text_display_box.document())
cursor.clearSelection()
self.text_display_box.setTextCursor(cursor)
# Keep search bar visible but clear the input
self.search_input.setFocus()
@ -861,7 +881,7 @@ class TranscriptionViewerWidget(QWidget):
self.find_button.setChecked(False) # Sync button state
self.clear_search()
self.search_input.clearFocus()
# Save the visibility state to settings
self.find_widget_visible = False
self.settings.settings.setValue("transcription_viewer/find_widget_visible", False)
@ -869,11 +889,11 @@ class TranscriptionViewerWidget(QWidget):
def setup_shortcuts(self):
"""Set up keyboard shortcuts"""
from PyQt6.QtGui import QShortcut, QKeySequence
# Search shortcut (Ctrl+F)
search_shortcut = QShortcut(QKeySequence(self.shortcuts.get(Shortcut.SEARCH_TRANSCRIPT)), self)
search_shortcut.activated.connect(self.focus_search_input)
# Scroll to current text shortcut (Ctrl+G)
scroll_to_current_shortcut = QShortcut(QKeySequence(self.shortcuts.get(Shortcut.SCROLL_TO_CURRENT_TEXT)), self)
scroll_to_current_shortcut.activated.connect(self.on_scroll_to_current_button_clicked)
@ -912,7 +932,7 @@ class TranscriptionViewerWidget(QWidget):
self.find_button.setChecked(True) # Sync button state
self.search_input.setFocus()
self.search_input.selectAll()
# Save the visibility state to settings
self.find_widget_visible = True
self.settings.settings.setValue("transcription_viewer/find_widget_visible", True)
@ -923,7 +943,7 @@ class TranscriptionViewerWidget(QWidget):
self.hide_search_bar()
else:
self.show_search_bar()
# Save the visibility state to settings
self.find_widget_visible = self.search_frame.isVisible()
self.settings.settings.setValue("transcription_viewer/find_widget_visible", self.find_widget_visible)
@ -934,7 +954,7 @@ class TranscriptionViewerWidget(QWidget):
self.find_button.setChecked(True)
self.search_input.setFocus()
self.search_input.selectAll()
# Save the visibility state to settings
self.find_widget_visible = True
self.settings.settings.setValue("transcription_viewer/find_widget_visible", True)
@ -942,7 +962,7 @@ class TranscriptionViewerWidget(QWidget):
def eventFilter(self, obj, event):
"""Event filter to handle keyboard shortcuts in search input"""
from PyQt6.QtCore import QEvent, Qt
if obj == self.search_input and event.type() == QEvent.Type.KeyPress:
# The event is already a QKeyEvent, no need to create a new one
if event.key() == Qt.Key.Key_Return and event.modifiers() == Qt.KeyboardModifier.ShiftModifier:
@ -999,7 +1019,7 @@ class TranscriptionViewerWidget(QWidget):
self.loop_controls_frame.hide()
# Hide current segment display in translation mode
self.current_segment_frame.hide()
# Refresh search if there's active search text
if self.search_text:
self.perform_search()
@ -1007,7 +1027,7 @@ class TranscriptionViewerWidget(QWidget):
def on_view_mode_changed(self, view_mode: ViewMode) -> None:
self.view_mode = view_mode
self.reset_view()
# Refresh search if there's active search text
if self.search_text:
self.perform_search()
@ -1091,17 +1111,17 @@ class TranscriptionViewerWidget(QWidget):
if current_segment is not None:
self.current_segment_text.setText(current_segment.value("text"))
self.current_segment_frame.show() # Show the frame when there's a current segment
# Force the text label to recalculate its size
self.current_segment_text.adjustSize()
# Resize the frame to fit the text content
self.resize_current_segment_frame()
# Ensure the scroll area updates properly and shows scrollbars when needed
self.current_segment_scroll_area.updateGeometry()
self.current_segment_scroll_area.verticalScrollBar().setVisible(True) # Ensure scrollbar is visible
# Update highlighting based on follow audio and loop settings
if self.follow_audio_enabled:
# Follow audio mode: highlight the current segment based on audio position
@ -1143,30 +1163,30 @@ class TranscriptionViewerWidget(QWidget):
# Calculate the height needed for the text area
line_height = self.current_segment_text.fontMetrics().lineSpacing()
max_visible_lines = 3 # Fixed at 3 lines for consistency and clean UI
# Calculate the height needed for the maximum visible lines (25% larger)
text_height = line_height * max_visible_lines * 1.25
# Add some vertical margins/padding
margins = 8 # Increased from 2 to 8 for better spacing
# Calculate total height needed (no header height anymore)
total_height = text_height + margins
# Convert to integer since Qt methods expect int values
total_height = int(total_height)
# Set maximum height to ensure consistent sizing, but allow minimum to be flexible
self.current_segment_frame.setMaximumHeight(total_height)
self.current_segment_frame.setMinimumHeight(total_height)
# Convert text_height to integer since Qt methods expect int values
text_height = int(text_height)
# Allow the scroll area to be flexible in height for proper scrolling
self.current_segment_scroll_area.setMinimumHeight(text_height)
self.current_segment_scroll_area.setMaximumHeight(text_height)
# Allow the text label to size naturally for proper scrolling
self.current_segment_text.setMinimumHeight(text_height)
@ -1220,12 +1240,27 @@ class TranscriptionViewerWidget(QWidget):
self.transcription_resizer_dialog.show()
def on_speaker_identification_button_clicked(self):
# Underlying libs do not support intel Macs
if not (platform.system() == "Darwin" and platform.machine() == "x86_64"):
self.speaker_identification_dialog = SpeakerIdentificationWidget(
transcription=self.transcription,
transcription_service=self.transcription_service,
transcriptions_updated_signal=self.transcriptions_updated_signal,
)
self.transcriptions_updated_signal.connect(self.close)
self.speaker_identification_dialog.show()
pass
def on_loop_toggle_changed(self, enabled: bool):
"""Handle loop toggle state change"""
self.segment_looping_enabled = enabled
# Save preference to settings
self.settings.settings.setValue("transcription_viewer/segment_looping_enabled", enabled)
if enabled:
# If looping is re-enabled and we have a selected segment, return to it
if self.currently_selected_segment is not None:
@ -1235,21 +1270,21 @@ class TranscriptionViewerWidget(QWidget):
if segment.value("id") == self.currently_selected_segment.value("id"):
# Highlight and scroll to the selected segment
self.table_widget.highlight_and_scroll_to_row(i)
# Get the segment timing
start_time = self.currently_selected_segment.value("start_time")
end_time = self.currently_selected_segment.value("end_time")
# Set the loop range for the selected segment
self.audio_player.set_range((start_time, end_time))
# If audio is currently playing and outside the range, jump to the start
current_pos = self.audio_player.position_ms
playback_state = self.audio_player.media_player.playbackState()
if (playback_state == QMediaPlayer.PlaybackState.PlayingState and
if (playback_state == QMediaPlayer.PlaybackState.PlayingState and
(current_pos < start_time or current_pos > end_time)):
self.audio_player.set_position(start_time)
break
else:
# Clear any existing range if looping is disabled
@ -1260,7 +1295,7 @@ class TranscriptionViewerWidget(QWidget):
self.follow_audio_enabled = enabled
# Save preference to settings
self.settings.settings.setValue("transcription_viewer/follow_audio_enabled", enabled)
if enabled:
# When follow audio is first enabled, automatically scroll to current position
# This gives immediate feedback that the feature is working
@ -1310,17 +1345,17 @@ class TranscriptionViewerWidget(QWidget):
# Only scroll if we're in timestamps view mode (table is visible)
if self.view_mode != ViewMode.TIMESTAMPS:
return
current_pos = self.audio_player.position_ms
segments = self.table_widget.segments()
# Find the current segment based on audio position
current_segment = next(
(segment for segment in segments
(segment for segment in segments
if segment.value("start_time") <= current_pos < segment.value("end_time")),
None
)
if current_segment is not None:
# Find the row index and scroll to it
for i, segment in enumerate(segments):
@ -1329,7 +1364,7 @@ class TranscriptionViewerWidget(QWidget):
# Method 1: Use the table widget's built-in scrolling method
self.table_widget.highlight_and_scroll_to_row(i)
break
except Exception as e:
pass # Silently handle any errors
@ -1346,6 +1381,9 @@ class TranscriptionViewerWidget(QWidget):
if self.transcription_resizer_dialog:
self.transcription_resizer_dialog.close()
if self.speaker_identification_dialog:
self.speaker_identification_dialog.close()
self.translator.stop()
self.translation_thread.quit()

1
ctc_forced_aligner Submodule

@ -0,0 +1 @@
Subproject commit 1f0a5f860d3d9daf3d94edb1c7d18f90d1702e5b

@ -0,0 +1 @@
Subproject commit 5a0dd7f4fd56687f59405aa8eba1144393d8b74b

33
demucs/.github/ISSUE_TEMPLATE/bug.md vendored Normal file
View file

@ -0,0 +1,33 @@
---
name: 🐛 Bug Report
about: Submit a bug report to help us improve
labels: 'bug'
---
## 🐛 Bug Report
(A clear and concise description of what the bug is)
## To Reproduce
(Write your steps here:)
1. Step 1...
1. Step 2...
1. Step 3...
## Expected behavior
(Write what you thought would happen.)
## Actual Behavior
(Write what happened. Add screenshots, if applicable.)
## Your Environment
<!-- Include as many relevant details about the environment you experienced the bug in -->
- Python and PyTorch version:
- Operating system and version (desktop or mobile):
- Hardware (gpu or cpu, amount of RAM etc.):

View file

@ -0,0 +1,10 @@
---
name: "❓Questions/Help/Support"
about: If you have a question about the paper, code or algorithm, please ask here!
labels: question
---
## ❓ Questions
(Please ask your question here.)

36
demucs/.github/workflows/linter.yml vendored Normal file
View file

@ -0,0 +1,36 @@
name: linter
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
if: ${{ github.repository == 'facebookresearch/demucs' || github.event_name == 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.8
- uses: actions/cache@v2
with:
path: env
key: env-${{ hashFiles('**/requirements.txt', '.github/workflows/*') }}
- name: Install dependencies
run: |
python3 -m venv env
. env/bin/activate
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install '.[dev]'
- name: Run linter
run: |
. env/bin/activate
make linter

36
demucs/.github/workflows/tests.yml vendored Normal file
View file

@ -0,0 +1,36 @@
name: tests
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
if: ${{ github.repository == 'facebookresearch/demucs' || github.event_name == 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.8
- uses: actions/cache@v2
with:
path: env
key: env-${{ hashFiles('**/requirements.txt', '.github/workflows/*') }}
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y ffmpeg
python3 -m venv env
. env/bin/activate
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run separation test
run: |
. env/bin/activate
make test_eval

17
demucs/.gitignore vendored Normal file
View file

@ -0,0 +1,17 @@
*.egg-info
__pycache__
Session.vim
/build
/dist
/lab
/metadata
/notebooks
/outputs
/release
/release_models
/separated
/tests
/trash
/misc
/mdx
.mypy_cache

76
demucs/CODE_OF_CONDUCT.md Normal file
View file

@ -0,0 +1,76 @@
# Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to make participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies within all project spaces, and it also applies when
an individual is representing the project or its community in public spaces.
Examples of representing a project or community include using an official
project e-mail address, posting via an official social media account, or acting
as an appointed representative at an online or offline event. Representation of
a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at <opensource-conduct@fb.com>. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

23
demucs/CONTRIBUTING.md Normal file
View file

@ -0,0 +1,23 @@
# Contributing to Demucs
## Pull Requests
In order to accept your pull request, we need you to submit a CLA. You only need
to do this once to work on any of Facebook's open source projects.
Complete your CLA here: <https://code.facebook.com/cla>
Demucs is the implementation of a research paper.
Therefore, we do not plan on accepting many pull requests for new features.
We certainly welcome them for bug fixes.
## Issues
We use GitHub issues to track public bugs. Please ensure your description is
clear and has sufficient instructions to be able to reproduce the issue.
## License
By contributing to this repository, you agree that your contributions will be licensed
under the LICENSE file in the root directory of this source tree.

153
demucs/Demucs.ipynb Normal file
View file

@ -0,0 +1,153 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "Be9yoh-ILfRr"
},
"source": [
"# Hybrid Demucs\n",
"\n",
"Feel free to use the Colab version:\n",
"https://colab.research.google.com/drive/1dC9nVxk3V_VPjUADsnFu8EiT-xnU1tGH?usp=sharing"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 139
},
"colab_type": "code",
"executionInfo": {
"elapsed": 12277,
"status": "ok",
"timestamp": 1583778134659,
"user": {
"displayName": "Marllus Lustosa",
"photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GgLl2RbW64ZyWz3Y8IBku0zhHCMnt7fz7fEl0LTdA=s64",
"userId": "14811735256675200480"
},
"user_tz": 180
},
"id": "kOjIPLlzhPfn",
"outputId": "c75f17ec-b576-4105-bc5b-c2ac9c1018a3"
},
"outputs": [],
"source": [
"!pip install -U demucs\n",
"# or for local development, if you have a clone of Demucs\n",
"# pip install -e ."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
"id": "5lYOzKKCKAbJ"
},
"outputs": [],
"source": [
"# You can use the `demucs` command line to separate tracks\n",
"!demucs test.mp3"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# You can also load directly the pretrained models,\n",
"# for instance for the MDX 2021 winning model of Track A:\n",
"from demucs import pretrained\n",
"model = pretrained.get_model('mdx')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Because `model` is a bag of 4 models, you cannot directly call it on your data,\n",
"# but the `apply_model` will know what to do of it.\n",
"import torch\n",
"from demucs.apply import apply_model\n",
"x = torch.randn(1, 2, 44100 * 10) # ten seconds of white noise for the demo\n",
"out = apply_model(model, x)[0] # shape is [S, C, T] with S the number of sources\n",
"\n",
"# So let see, where is all the white noise content is going ?\n",
"for name, source in zip(model.sources, out):\n",
" print(name, source.std() / x.std())\n",
"# The outputs are quite weird to be fair, not what I would have expected."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# now let's take a single model from the bag, and let's test it on a pure cosine\n",
"freq = 440 # in Hz\n",
"sr = model.samplerate\n",
"t = torch.arange(10 * sr).float() / sr\n",
"x = torch.cos(2 * 3.1416 * freq * t).expand(1, 2, -1)\n",
"sub_model = model.models[3]\n",
"out = sub_model(x)[0]\n",
"\n",
"# Same question where does it go?\n",
"for name, source in zip(model.sources, out):\n",
" print(name, source.std() / x.std())\n",
" \n",
"# Well now it makes much more sense, all the energy is going\n",
"# in the `other` source.\n",
"# Feel free to try lower pitch (try 80 Hz) to see what happens !"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# For training or more fun, refer to the Demucs README on our repo\n",
"# https://github.com/facebookresearch/demucs/tree/main/demucs"
]
}
],
"metadata": {
"accelerator": "GPU",
"colab": {
"authorship_tag": "ABX9TyM9xpVr1M86NRcjtQ7g9tCx",
"collapsed_sections": [],
"name": "Demucs.ipynb",
"provenance": []
},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 1
}

21
demucs/LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

13
demucs/MANIFEST.in Normal file
View file

@ -0,0 +1,13 @@
recursive-exclude env *
recursive-include conf *.yaml
include Makefile
include LICENSE
include demucs.png
include outputs.tar.gz
include test.mp3
include requirements.txt
include requirements_minimal.txt
include mypy.ini
include demucs/py.typed
include demucs/remote/*.txt
include demucs/remote/*.yaml

36
demucs/Makefile Normal file
View file

@ -0,0 +1,36 @@
all: linter tests
linter:
flake8 demucs
mypy demucs
tests: test_train test_eval
test_train: tests/musdb
_DORA_TEST_PATH=/tmp/demucs python3 -m dora run --clear \
dset.musdb=./tests/musdb dset.segment=4 dset.shift=2 epochs=2 model=demucs \
demucs.depth=2 demucs.channels=4 test.sdr=false misc.num_workers=0 test.workers=0 \
test.shifts=0
test_eval:
python3 -m demucs -n demucs_unittest test.mp3
python3 -m demucs -n demucs_unittest --two-stems=vocals test.mp3
python3 -m demucs -n demucs_unittest --mp3 test.mp3
python3 -m demucs -n demucs_unittest --flac --int24 test.mp3
python3 -m demucs -n demucs_unittest --int24 --clip-mode clamp test.mp3
python3 -m demucs -n demucs_unittest --segment 8 test.mp3
python3 -m demucs.api -n demucs_unittest --segment 8 test.mp3
python3 -m demucs --list-models
tests/musdb:
test -e tests || mkdir tests
python3 -c 'import musdb; musdb.DB("tests/tmp", download=True)'
musdbconvert tests/tmp tests/musdb
dist:
python3 setup.py sdist
clean:
rm -r dist build *.egg-info
.PHONY: linter dist test_train test_eval

319
demucs/README.md Normal file
View file

@ -0,0 +1,319 @@
# Demucs Music Source Separation
![tests badge](https://github.com/facebookresearch/demucs/workflows/tests/badge.svg)
![linter badge](https://github.com/facebookresearch/demucs/workflows/linter/badge.svg)
**This is the officially maintained Demucs** now that I (Alexandre Défossez) have left Meta to join [Kyutai](https://twitter.com/kyutai_labs).
Note that I'm not actively working on Demucs anymore, so expect slow replies and no new feature for now.
This is the 4th release of Demucs (v4), featuring Hybrid Transformer based source separation.
**For the classic Hybrid Demucs (v3):** [Go this commit][demucs_v3].
If you are experiencing issues and want the old Demucs back, please file an issue, and then you can get back to Demucs v3 with
`git checkout v3`. You can also go [Demucs v2][demucs_v2].
Demucs is a state-of-the-art music source separation model, currently capable of separating
drums, bass, and vocals from the rest of the accompaniment.
Demucs is based on a U-Net convolutional architecture inspired by [Wave-U-Net][waveunet].
The v4 version features [Hybrid Transformer Demucs][htdemucs], a hybrid spectrogram/waveform separation model using Transformers.
It is based on [Hybrid Demucs][hybrid_paper] (also provided in this repo), with the innermost layers
replaced by a cross-domain Transformer Encoder. This Transformer uses self-attention within each domain,
and cross-attention across domains.
The model achieves a SDR of 9.00 dB on the MUSDB HQ test set. Moreover, when using sparse attention
kernels to extend its receptive field and per source fine-tuning, we achieve state-of-the-art 9.20 dB of SDR.
Samples are available [on our sample page](https://ai.honu.io/papers/htdemucs/index.html).
Checkout [our paper][htdemucs] for more information.
It has been trained on the [MUSDB HQ][musdb] dataset + an extra training dataset of 800 songs.
This model separates drums, bass and vocals and other stems for any song.
As Hybrid Transformer Demucs is brand new, it is not activated by default, you can activate it in the usual
commands described hereafter with `-n htdemucs_ft`.
The single, non fine-tuned model is provided as `-n htdemucs`, and the retrained baseline
as `-n hdemucs_mmi`. The Sparse Hybrid Transformer model decribed in our paper is not provided as its
requires custom CUDA code that is not ready for release yet.
We are also releasing an experimental 6 sources model, that adds a `guitar` and `piano` source.
Quick testing seems to show okay quality for `guitar`, but a lot of bleeding and artifacts for the `piano` source.
<p align="center">
<img src="./demucs.png" alt="Schema representing the structure of Hybrid Transformer Demucs,
with a dual U-Net structure, one branch for the temporal domain,
and one branch for the spectral domain. There is a cross-domain Transformer between the Encoders and Decoders."
width="800px"></p>
## Important news if you are already using Demucs
See the [release notes](./docs/release.md) for more details.
- 22/02/2023: added support for the [SDX 2023 Challenge](https://www.aicrowd.com/challenges/sound-demixing-challenge-2023),
see the dedicated [doc page](./docs/sdx23.md)
- 07/12/2022: Demucs v4 now on PyPI. **htdemucs** model now used by default. Also releasing
a 6 sources models (adding `guitar` and `piano`, although the latter doesn't work so well at the moment).
- 16/11/2022: Added the new **Hybrid Transformer Demucs v4** models.
Adding support for the [torchaudio implementation of HDemucs](https://pytorch.org/audio/stable/tutorials/hybrid_demucs_tutorial.html).
- 30/08/2022: added reproducibility and ablation grids, along with an updated version of the paper.
- 17/08/2022: Releasing v3.0.5: Set split segment length to reduce memory. Compatible with pyTorch 1.12.
- 24/02/2022: Releasing v3.0.4: split into two stems (i.e. karaoke mode).
Export as float32 or int24.
- 17/12/2021: Releasing v3.0.3: bug fixes (thanks @keunwoochoi), memory drastically
reduced on GPU (thanks @famzah) and new multi-core evaluation on CPU (`-j` flag).
- 12/11/2021: Releasing **Demucs v3** with hybrid domain separation. Strong improvements
on all sources. This is the model that won Sony MDX challenge.
- 11/05/2021: Adding support for MusDB-HQ and arbitrary wav set, for the MDX challenge. For more information
on joining the challenge with Demucs see [the Demucs MDX instructions](docs/mdx.md)
## Comparison with other models
We provide hereafter a summary of the different metrics presented in the paper.
You can also compare Hybrid Demucs (v3), [KUIELAB-MDX-Net][kuielab], [Spleeter][spleeter], Open-Unmix, Demucs (v1), and Conv-Tasnet on one of my favorite
songs on my [soundcloud playlist][soundcloud].
### Comparison of accuracy
`Overall SDR` is the mean of the SDR for each of the 4 sources, `MOS Quality` is a rating from 1 to 5
of the naturalness and absence of artifacts given by human listeners (5 = no artifacts), `MOS Contamination`
is a rating from 1 to 5 with 5 being zero contamination by other sources. We refer the reader to our [paper][hybrid_paper],
for more details.
| Model | Domain | Extra data? | Overall SDR | MOS Quality | MOS Contamination |
|------------------------------|-------------|-------------------|-------------|-------------|-------------------|
| [Wave-U-Net][waveunet] | waveform | no | 3.2 | - | - |
| [Open-Unmix][openunmix] | spectrogram | no | 5.3 | - | - |
| [D3Net][d3net] | spectrogram | no | 6.0 | - | - |
| [Conv-Tasnet][demucs_v2] | waveform | no | 5.7 | - | |
| [Demucs (v2)][demucs_v2] | waveform | no | 6.3 | 2.37 | 2.36 |
| [ResUNetDecouple+][decouple] | spectrogram | no | 6.7 | - | - |
| [KUIELAB-MDX-Net][kuielab] | hybrid | no | 7.5 | **2.86** | 2.55 |
| [Band-Spit RNN][bandsplit] | spectrogram | no | **8.2** | - | - |
| **Hybrid Demucs (v3)** | hybrid | no | 7.7 | **2.83** | **3.04** |
| [MMDenseLSTM][mmdenselstm] | spectrogram | 804 songs | 6.0 | - | - |
| [D3Net][d3net] | spectrogram | 1.5k songs | 6.7 | - | - |
| [Spleeter][spleeter] | spectrogram | 25k songs | 5.9 | - | - |
| [Band-Spit RNN][bandsplit] | spectrogram | 1.7k (mixes only) | **9.0** | - | - |
| **HT Demucs f.t. (v4)** | hybrid | 800 songs | **9.0** | - | - |
## Requirements
You will need at least Python 3.8. See `requirements_minimal.txt` for requirements for separation only,
and `environment-[cpu|cuda].yml` (or `requirements.txt`) if you want to train a new model.
### For Windows users
Everytime you see `python3`, replace it with `python.exe`. You should always run commands from the
Anaconda console.
### For musicians
If you just want to use Demucs to separate tracks, you can install it with
```bash
python3 -m pip install -U demucs
```
For bleeding edge versions, you can install directly from this repo using
```bash
python3 -m pip install -U git+https://github.com/facebookresearch/demucs#egg=demucs
```
Advanced OS support are provided on the following page, **you must read the page for your OS before posting an issues**:
- **If you are using Windows:** [Windows support](docs/windows.md).
- **If you are using macOS:** [macOS support](docs/mac.md).
- **If you are using Linux:** [Linux support](docs/linux.md).
### For machine learning scientists
If you have anaconda installed, you can run from the root of this repository:
```bash
conda env update -f environment-cpu.yml # if you don't have GPUs
conda env update -f environment-cuda.yml # if you have GPUs
conda activate demucs
pip install -e .
```
This will create a `demucs` environment with all the dependencies installed.
You will also need to install [soundstretch/soundtouch](https://www.surina.net/soundtouch/soundstretch.html): on macOS you can do `brew install sound-touch`,
and on Ubuntu `sudo apt-get install soundstretch`. This is used for the
pitch/tempo augmentation.
### Running in Docker
Thanks to @xserrat, there is now a Docker image definition ready for using Demucs. This can ensure all libraries are correctly installed without interfering with the host OS. See his repo [Docker Facebook Demucs](https://github.com/xserrat/docker-facebook-demucs) for more information.
### Running from Colab
I made a Colab to easily separate track with Demucs. Note that
transfer speeds with Colab are a bit slow for large media files,
but it will allow you to use Demucs without installing anything.
[Demucs on Google Colab](https://colab.research.google.com/drive/1dC9nVxk3V_VPjUADsnFu8EiT-xnU1tGH?usp=sharing)
### Web Demo
Integrated to [Hugging Face Spaces](https://huggingface.co/spaces) with [Gradio](https://github.com/gradio-app/gradio). See demo: [![Hugging Face Spaces](https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Spaces-blue)](https://huggingface.co/spaces/akhaliq/demucs)
### Graphical Interface
@CarlGao4 has released a GUI for Demucs: [CarlGao4/Demucs-Gui](https://github.com/CarlGao4/Demucs-Gui). Downloads for Windows and macOS is available [here](https://github.com/CarlGao4/Demucs-Gui/releases). Use [FossHub mirror](https://fosshub.com/Demucs-GUI.html) to speed up your download.
@Anjok07 is providing a self contained GUI in [UVR (Ultimate Vocal Remover)](https://github.com/facebookresearch/demucs/issues/334) that supports Demucs.
### Other providers
Audiostrip is providing free online separation with Demucs on their website [https://audiostrip.co.uk/](https://audiostrip.co.uk/).
[MVSep](https://mvsep.com/) also provides free online separation, select `Demucs3 model B` for the best quality.
[Neutone](https://neutone.space/) provides a realtime Demucs model in their free VST/AU plugin that can be used in your favorite DAW.
## Separating tracks
In order to try Demucs, you can just run from any folder (as long as you properly installed it)
```bash
demucs PATH_TO_AUDIO_FILE_1 [PATH_TO_AUDIO_FILE_2 ...] # for Demucs
# If you used `pip install --user` you might need to replace demucs with python3 -m demucs
python3 -m demucs --mp3 --mp3-bitrate BITRATE PATH_TO_AUDIO_FILE_1 # output files saved as MP3
# use --mp3-preset to change encoder preset, 2 for best quality, 7 for fastest
# If your filename contain spaces don't forget to quote it !!!
demucs "my music/my favorite track.mp3"
# You can select different models with `-n` mdx_q is the quantized model, smaller but maybe a bit less accurate.
demucs -n mdx_q myfile.mp3
# If you only want to separate vocals out of an audio, use `--two-stems=vocals` (You can also set to drums or bass)
demucs --two-stems=vocals myfile.mp3
```
If you have a GPU, but you run out of memory, please use `--segment SEGMENT` to reduce length of each split. `SEGMENT` should be changed to a integer describing the length of each segment in seconds.
A segment length of at least 10 is recommended (the bigger the number is, the more memory is required, but quality may increase). Note that the Hybrid Transformer models only support a maximum segment length of 7.8 seconds.
Creating an environment variable `PYTORCH_NO_CUDA_MEMORY_CACHING=1` is also helpful. If this still does not help, please add `-d cpu` to the command line. See the section hereafter for more details on the memory requirements for GPU acceleration.
Separated tracks are stored in the `separated/MODEL_NAME/TRACK_NAME` folder. There you will find four stereo wav files sampled at 44.1 kHz: `drums.wav`, `bass.wav`,
`other.wav`, `vocals.wav` (or `.mp3` if you used the `--mp3` option).
All audio formats supported by `torchaudio` can be processed (i.e. wav, mp3, flac, ogg/vorbis on Linux/macOS, etc.). On Windows, `torchaudio` has limited support, so we rely on `ffmpeg`, which should support pretty much anything.
Audio is resampled on the fly if necessary.
The output will be a wav file encoded as int16.
You can save as float32 wav files with `--float32`, or 24 bits integer wav with `--int24`.
You can pass `--mp3` to save as mp3 instead, and set the bitrate (in kbps) with `--mp3-bitrate` (default is 320).
It can happen that the output would need clipping, in particular due to some separation artifacts.
Demucs will automatically rescale each output stem so as to avoid clipping. This can however break
the relative volume between stems. If instead you prefer hard clipping, pass `--clip-mode clamp`.
You can also try to reduce the volume of the input mixture before feeding it to Demucs.
Other pre-trained models can be selected with the `-n` flag.
The list of pre-trained models is:
- `htdemucs`: first version of Hybrid Transformer Demucs. Trained on MusDB + 800 songs. Default model.
- `htdemucs_ft`: fine-tuned version of `htdemucs`, separation will take 4 times more time
but might be a bit better. Same training set as `htdemucs`.
- `htdemucs_6s`: 6 sources version of `htdemucs`, with `piano` and `guitar` being added as sources.
Note that the `piano` source is not working great at the moment.
- `hdemucs_mmi`: Hybrid Demucs v3, retrained on MusDB + 800 songs.
- `mdx`: trained only on MusDB HQ, winning model on track A at the [MDX][mdx] challenge.
- `mdx_extra`: trained with extra training data (**including MusDB test set**), ranked 2nd on the track B
of the [MDX][mdx] challenge.
- `mdx_q`, `mdx_extra_q`: quantized version of the previous models. Smaller download and storage
but quality can be slightly worse.
- `SIG`: where `SIG` is a single model from the [model zoo](docs/training.md#model-zoo).
The `--two-stems=vocals` option allows separating vocals from the rest of the accompaniment (i.e., karaoke mode).
`vocals` can be changed to any source in the selected model.
This will mix the files after separating the mix fully, so this won't be faster or use less memory.
The `--shifts=SHIFTS` performs multiple predictions with random shifts (a.k.a the *shift trick*) of the input and average them. This makes prediction `SHIFTS` times
slower. Don't use it unless you have a GPU.
The `--overlap` option controls the amount of overlap between prediction windows. Default is 0.25 (i.e. 25%) which is probably fine.
It can probably be reduced to 0.1 to improve a bit speed.
The `-j` flag allow to specify a number of parallel jobs (e.g. `demucs -j 2 myfile.mp3`).
This will multiply by the same amount the RAM used so be careful!
### Memory requirements for GPU acceleration
If you want to use GPU acceleration, you will need at least 3GB of RAM on your GPU for `demucs`. However, about 7GB of RAM will be required if you use the default arguments. Add `--segment SEGMENT` to change size of each split. If you only have 3GB memory, set SEGMENT to 8 (though quality may be worse if this argument is too small). Creating an environment variable `PYTORCH_NO_CUDA_MEMORY_CACHING=1` can help users with even smaller RAM such as 2GB (I separated a track that is 4 minutes but only 1.5GB is used), but this would make the separation slower.
If you do not have enough memory on your GPU, simply add `-d cpu` to the command line to use the CPU. With Demucs, processing time should be roughly equal to 1.5 times the duration of the track.
## Calling from another Python program
The main function provides an `opt` parameter as a simple API. You can just pass the parsed command line as this parameter:
```python
# Assume that your command is `demucs --mp3 --two-stems vocals -n mdx_extra "track with space.mp3"`
# The following codes are same as the command above:
import demucs.separate
demucs.separate.main(["--mp3", "--two-stems", "vocals", "-n", "mdx_extra", "track with space.mp3"])
# Or like this
import demucs.separate
import shlex
demucs.separate.main(shlex.split('--mp3 --two-stems vocals -n mdx_extra "track with space.mp3"'))
```
To use more complicated APIs, see [API docs](docs/api.md)
## Training Demucs
If you want to train (Hybrid) Demucs, please follow the [training doc](docs/training.md).
## MDX Challenge reproduction
In order to reproduce the results from the Track A and Track B submissions, checkout the [MDX Hybrid Demucs submission repo][mdx_submission].
## How to cite
```
@inproceedings{rouard2022hybrid,
title={Hybrid Transformers for Music Source Separation},
author={Rouard, Simon and Massa, Francisco and D{\'e}fossez, Alexandre},
booktitle={ICASSP 23},
year={2023}
}
@inproceedings{defossez2021hybrid,
title={Hybrid Spectrogram and Waveform Source Separation},
author={D{\'e}fossez, Alexandre},
booktitle={Proceedings of the ISMIR 2021 Workshop on Music Source Separation},
year={2021}
}
```
## License
Demucs is released under the MIT license as found in the [LICENSE](LICENSE) file.
[hybrid_paper]: https://arxiv.org/abs/2111.03600
[waveunet]: https://github.com/f90/Wave-U-Net
[musdb]: https://sigsep.github.io/datasets/musdb.html
[openunmix]: https://github.com/sigsep/open-unmix-pytorch
[mmdenselstm]: https://arxiv.org/abs/1805.02410
[demucs_v2]: https://github.com/facebookresearch/demucs/tree/v2
[demucs_v3]: https://github.com/facebookresearch/demucs/tree/v3
[spleeter]: https://github.com/deezer/spleeter
[soundcloud]: https://soundcloud.com/honualx/sets/source-separation-in-the-waveform-domain
[d3net]: https://arxiv.org/abs/2010.01733
[mdx]: https://www.aicrowd.com/challenges/music-demixing-challenge-ismir-2021
[kuielab]: https://github.com/kuielab/mdx-net-submission
[decouple]: https://arxiv.org/abs/2109.05418
[mdx_submission]: https://github.com/adefossez/mdx21_demucs
[bandsplit]: https://arxiv.org/abs/2209.15174
[htdemucs]: https://arxiv.org/abs/2211.08553

View file

@ -1 +0,0 @@
Inlined demucs https://github.com/adefossez/demucs

304
demucs/conf/config.yaml Normal file
View file

@ -0,0 +1,304 @@
defaults:
- _self_
- dset: musdb44
- svd: default
- variant: default
- override hydra/hydra_logging: colorlog
- override hydra/job_logging: colorlog
dummy:
dset:
musdb: /checkpoint/defossez/datasets/musdbhq
musdb_samplerate: 44100
use_musdb: true # set to false to not use musdb as training data.
wav: # path to custom wav dataset
wav2: # second custom wav dataset
segment: 11
shift: 1
train_valid: false
full_cv: true
samplerate: 44100
channels: 2
normalize: true
metadata: ./metadata
sources: ['drums', 'bass', 'other', 'vocals']
valid_samples: # valid dataset size
backend: null # if provided select torchaudio backend.
test:
save: False
best: True
workers: 2
every: 20
split: true
shifts: 1
overlap: 0.25
sdr: true
metric: 'loss' # metric used for best model selection on the valid set, can also be nsdr
nonhq: # path to non hq MusDB for evaluation
epochs: 360
batch_size: 64
max_batches: # limit the number of batches per epoch, useful for debugging
# or if your dataset is gigantic.
optim:
lr: 3e-4
momentum: 0.9
beta2: 0.999
loss: l1 # l1 or mse
optim: adam
weight_decay: 0
clip_grad: 0
seed: 42
debug: false
valid_apply: true
flag:
save_every:
weights: [1., 1., 1., 1.] # weights over each source for the training/valid loss.
augment:
shift_same: false
repitch:
proba: 0.2
max_tempo: 12
remix:
proba: 1
group_size: 4
scale:
proba: 1
min: 0.25
max: 1.25
flip: true
continue_from: # continue from other XP, give the XP Dora signature.
continue_pretrained: # signature of a pretrained XP, this cannot be a bag of models.
pretrained_repo: # repo for pretrained model (default is official AWS)
continue_best: true
continue_opt: false
misc:
num_workers: 10
num_prints: 4
show: false
verbose: false
# List of decay for EMA at batch or epoch level, e.g. 0.999.
# Batch level EMA are kept on GPU for speed.
ema:
epoch: []
batch: []
use_train_segment: true # to remove
model_segment: # override the segment parameter for the model, usually 4 times the training segment.
model: demucs # see demucs/train.py for the possibilities, and config for each model hereafter.
demucs: # see demucs/demucs.py for a detailed description
# Channels
channels: 64
growth: 2
# Main structure
depth: 6
rewrite: true
lstm_layers: 0
# Convolutions
kernel_size: 8
stride: 4
context: 1
# Activations
gelu: true
glu: true
# Normalization
norm_groups: 4
norm_starts: 4
# DConv residual branch
dconv_depth: 2
dconv_mode: 1 # 1 = branch in encoder, 2 = in decoder, 3 = in both.
dconv_comp: 4
dconv_attn: 4
dconv_lstm: 4
dconv_init: 1e-4
# Pre/post treatment
resample: true
normalize: false
# Weight init
rescale: 0.1
hdemucs: # see demucs/hdemucs.py for a detailed description
# Channels
channels: 48
channels_time:
growth: 2
# STFT
nfft: 4096
wiener_iters: 0
end_iters: 0
wiener_residual: false
cac: true
# Main structure
depth: 6
rewrite: true
hybrid: true
hybrid_old: false
# Frequency Branch
multi_freqs: []
multi_freqs_depth: 3
freq_emb: 0.2
emb_scale: 10
emb_smooth: true
# Convolutions
kernel_size: 8
stride: 4
time_stride: 2
context: 1
context_enc: 0
# normalization
norm_starts: 4
norm_groups: 4
# DConv residual branch
dconv_mode: 1
dconv_depth: 2
dconv_comp: 4
dconv_attn: 4
dconv_lstm: 4
dconv_init: 1e-3
# Weight init
rescale: 0.1
# Torchaudio implementation of HDemucs
torch_hdemucs:
# Channels
channels: 48
growth: 2
# STFT
nfft: 4096
# Main structure
depth: 6
freq_emb: 0.2
emb_scale: 10
emb_smooth: true
# Convolutions
kernel_size: 8
stride: 4
time_stride: 2
context: 1
context_enc: 0
# normalization
norm_starts: 4
norm_groups: 4
# DConv residual branch
dconv_depth: 2
dconv_comp: 4
dconv_attn: 4
dconv_lstm: 4
dconv_init: 1e-3
htdemucs: # see demucs/htdemucs.py for a detailed description
# Channels
channels: 48
channels_time:
growth: 2
# STFT
nfft: 4096
wiener_iters: 0
end_iters: 0
wiener_residual: false
cac: true
# Main structure
depth: 4
rewrite: true
# Frequency Branch
multi_freqs: []
multi_freqs_depth: 3
freq_emb: 0.2
emb_scale: 10
emb_smooth: true
# Convolutions
kernel_size: 8
stride: 4
time_stride: 2
context: 1
context_enc: 0
# normalization
norm_starts: 4
norm_groups: 4
# DConv residual branch
dconv_mode: 1
dconv_depth: 2
dconv_comp: 8
dconv_init: 1e-3
# Before the Transformer
bottom_channels: 0
# CrossTransformer
# ------ Common to all
# Regular parameters
t_layers: 5
t_hidden_scale: 4.0
t_heads: 8
t_dropout: 0.0
t_layer_scale: True
t_gelu: True
# ------------- Positional Embedding
t_emb: sin
t_max_positions: 10000 # for the scaled embedding
t_max_period: 10000.0
t_weight_pos_embed: 1.0
t_cape_mean_normalize: True
t_cape_augment: True
t_cape_glob_loc_scale: [5000.0, 1.0, 1.4]
t_sin_random_shift: 0
# ------------- norm before a transformer encoder
t_norm_in: True
t_norm_in_group: False
# ------------- norm inside the encoder
t_group_norm: False
t_norm_first: True
t_norm_out: True
# ------------- optim
t_weight_decay: 0.0
t_lr:
# ------------- sparsity
t_sparse_self_attn: False
t_sparse_cross_attn: False
t_mask_type: diag
t_mask_random_seed: 42
t_sparse_attn_window: 400
t_global_window: 100
t_sparsity: 0.95
t_auto_sparsity: False
# Cross Encoder First (False)
t_cross_first: False
# Weight init
rescale: 0.1
svd: # see svd.py for documentation
penalty: 0
min_size: 0.1
dim: 1
niters: 2
powm: false
proba: 1
conv_only: false
convtr: false
bs: 1
quant: # quantization hyper params
diffq: # diffq penalty, typically 1e-4 or 3e-4
qat: # use QAT with a fixed number of bits (not as good as diffq)
min_size: 0.2
group_size: 8
dora:
dir: outputs
exclude: ["misc.*", "slurm.*", 'test.reval', 'flag', 'dset.backend']
slurm:
time: 4320
constraint: volta32gb
setup: ['module load cudnn/v8.4.1.50-cuda.11.6 NCCL/2.11.4-6-cuda.11.6 cuda/11.6']
# Hydra config
hydra:
job_logging:
formatters:
colorlog:
datefmt: "%m-%d %H:%M:%S"

View file

@ -0,0 +1,19 @@
# @package _global_
# automix dataset with Musdb, extra training data and the test set of Musdb.
# This used even more remixes than auto_extra_test.
dset:
wav: /checkpoint/defossez/datasets/aetl
samplerate: 44100
channels: 2
epochs: 320
max_batches: 500
augment:
shift_same: true
scale:
proba: 0.
remix:
proba: 0
repitch:
proba: 0

View file

@ -0,0 +1,18 @@
# @package _global_
# automix dataset with Musdb, extra training data and the test set of Musdb.
dset:
wav: /checkpoint/defossez/datasets/automix_extra_test2
samplerate: 44100
channels: 2
epochs: 320
max_batches: 500
augment:
shift_same: true
scale:
proba: 0.
remix:
proba: 0
repitch:
proba: 0

View file

@ -0,0 +1,20 @@
# @package _global_
# Automix dataset based on musdb train set.
dset:
wav: /checkpoint/defossez/datasets/automix_musdb
samplerate: 44100
channels: 2
epochs: 360
max_batches: 300
test:
every: 4
augment:
shift_same: true
scale:
proba: 0.5
remix:
proba: 0
repitch:
proba: 0

View file

@ -0,0 +1,8 @@
# @package _global_
# Musdb + extra tracks
dset:
wav: /checkpoint/defossez/datasets/allstems_44/
samplerate: 44100
channels: 2
epochs: 320

View file

@ -0,0 +1,12 @@
# @package _global_
# Musdb + extra tracks
dset:
wav: /checkpoint/defossez/datasets/allstems_44/
wav2: /checkpoint/defossez/datasets/mmi44_goodclean
samplerate: 44100
channels: 2
wav2_weight: null
wav2_valid: false
valid_samples: 100
epochs: 1200

View file

@ -0,0 +1,12 @@
# @package _global_
# Musdb + extra tracks + test set from musdb.
dset:
wav: /checkpoint/defossez/datasets/allstems_test_44/
samplerate: 44100
channels: 2
epochs: 320
max_batches: 700
test:
sdr: false
every: 500

View file

@ -0,0 +1,5 @@
# @package _global_
dset:
samplerate: 44100
channels: 2

View file

@ -0,0 +1,10 @@
# @package _global_
# Musdb + extra tracks
dset:
wav: /shared/home/defossez/data/datasets/moisesdb23_bleeding_v1.0/
use_musdb: false
samplerate: 44100
channels: 2
backend: soundfile # must use soundfile as some mixture would clip with sox.
epochs: 320

View file

@ -0,0 +1,10 @@
# @package _global_
# Musdb + extra tracks
dset:
wav: /shared/home/defossez/data/datasets/moisesdb23_labelnoise_v1.0
use_musdb: false
samplerate: 44100
channels: 2
backend: soundfile # must use soundfile as some mixture would clip with sox.
epochs: 320

14
demucs/conf/svd/base.yaml Normal file
View file

@ -0,0 +1,14 @@
# @package _global_
svd:
penalty: 0
min_size: 1
dim: 50
niters: 4
powm: false
proba: 1
conv_only: false
convtr: false # ideally this should be true, but some models were trained with this to false.
optim:
beta2: 0.9998

View file

@ -0,0 +1,14 @@
# @package _global_
svd:
penalty: 0
min_size: 1
dim: 100
niters: 4
powm: false
proba: 1
conv_only: false
convtr: true
optim:
beta2: 0.9998

View file

@ -0,0 +1 @@
# @package _global_

View file

@ -0,0 +1 @@
# @package _global_

View file

@ -0,0 +1,5 @@
# @package _global_
model: hdemucs
hdemucs:
channels: 32

View file

@ -0,0 +1,19 @@
# @package _global_
epochs: 4
batch_size: 16
optim:
lr: 0.0006
test:
every: 1
sdr: false
dset:
segment: 28
shift: 2
augment:
scale:
proba: 0
shift_same: true
remix:
proba: 0

BIN
demucs/demucs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 KiB

Some files were not shown because too many files have changed in this diff Show more