Full Code of chidiwilliams/buzz for AI

main 6318ad8151f8 cached
254 files
1.9 MB
462.2k tokens
1925 symbols
1 requests
Download .txt
Showing preview only (2,057K chars total). Download the full file or copy to clipboard to get everything.
Repository: chidiwilliams/buzz
Branch: main
Commit: 6318ad8151f8
Files: 254
Total size: 1.9 MB

Directory structure:
gitextract_onk69s2x/

├── .coveragerc
├── .github/
│   └── workflows/
│       ├── ci.yml
│       ├── gh-pages.yml
│       ├── manual-build.yml
│       └── snapcraft.yml
├── .gitignore
├── .gitmodules
├── .pre-commit-config.yaml
├── .pylintrc
├── .python-version
├── .run/
│   └── pytest.run.xml
├── .vscode/
│   ├── launch.json
│   ├── settings.json
│   └── tasks.json
├── Buzz.spec
├── CLAUDE.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.ja_JP.md
├── README.md
├── assets/
│   └── buzz.icns
├── buzz/
│   ├── __init__.py
│   ├── __main__.py
│   ├── __version__.py
│   ├── action.py
│   ├── assets/
│   │   └── buzz.icns
│   ├── assets.py
│   ├── buzz.py
│   ├── cache.py
│   ├── cli.py
│   ├── conn.py
│   ├── cuda_setup.py
│   ├── db/
│   │   ├── __init__.py
│   │   ├── dao/
│   │   │   ├── __init__.py
│   │   │   ├── dao.py
│   │   │   ├── transcription_dao.py
│   │   │   └── transcription_segment_dao.py
│   │   ├── db.py
│   │   ├── entity/
│   │   │   ├── __init__.py
│   │   │   ├── entity.py
│   │   │   ├── transcription.py
│   │   │   └── transcription_segment.py
│   │   ├── helpers.py
│   │   ├── migrator.py
│   │   └── service/
│   │       ├── __init__.py
│   │       └── transcription_service.py
│   ├── dialogs.py
│   ├── file_transcriber_queue_worker.py
│   ├── locale/
│   │   ├── ca_ES/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   ├── da_DK/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   ├── de_DE/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   ├── en_US/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   ├── es_ES/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   ├── it_IT/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   ├── ja_JP/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   ├── lv_LV/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   ├── nl/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   ├── pl_PL/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   ├── pt_BR/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   ├── uk_UA/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   ├── zh_CN/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   └── zh_TW/
│   │       └── LC_MESSAGES/
│   │           └── buzz.po
│   ├── locale.py
│   ├── model_loader.py
│   ├── paths.py
│   ├── recording.py
│   ├── schema.sql
│   ├── settings/
│   │   ├── __init__.py
│   │   ├── recording_transcriber_mode.py
│   │   ├── settings.py
│   │   ├── shortcut.py
│   │   └── shortcuts.py
│   ├── store/
│   │   ├── __init__.py
│   │   └── keyring_store.py
│   ├── transcriber/
│   │   ├── __init__.py
│   │   ├── file_transcriber.py
│   │   ├── local_whisper_cpp_server_transcriber.py
│   │   ├── openai_whisper_api_file_transcriber.py
│   │   ├── recording_transcriber.py
│   │   ├── transcriber.py
│   │   ├── whisper_cpp.py
│   │   └── whisper_file_transcriber.py
│   ├── transformers_whisper.py
│   ├── translator.py
│   ├── update_checker.py
│   ├── whisper_audio.py
│   └── widgets/
│       ├── __init__.py
│       ├── about_dialog.py
│       ├── application.py
│       ├── audio_devices_combo_box.py
│       ├── audio_meter_widget.py
│       ├── audio_player.py
│       ├── form_label.py
│       ├── icon.py
│       ├── icon_presentation.py
│       ├── import_url_dialog.py
│       ├── line_edit.py
│       ├── main_window.py
│       ├── main_window_toolbar.py
│       ├── menu_bar.py
│       ├── model_download_progress_dialog.py
│       ├── model_type_combo_box.py
│       ├── openai_api_key_line_edit.py
│       ├── preferences_dialog/
│       │   ├── __init__.py
│       │   ├── folder_watch_preferences_widget.py
│       │   ├── general_preferences_widget.py
│       │   ├── models/
│       │   │   ├── __init__.py
│       │   │   ├── file_transcription_preferences.py
│       │   │   ├── folder_watch_preferences.py
│       │   │   └── preferences.py
│       │   ├── models_preferences_widget.py
│       │   ├── preferences_dialog.py
│       │   └── shortcuts_editor_preferences_widget.py
│       ├── presentation_window.py
│       ├── record_button.py
│       ├── record_delegate.py
│       ├── recording_transcriber_widget.py
│       ├── sequence_edit.py
│       ├── text_display_box.py
│       ├── toolbar.py
│       ├── transcriber/
│       │   ├── __init__.py
│       │   ├── advanced_settings_button.py
│       │   ├── advanced_settings_dialog.py
│       │   ├── file_transcriber_widget.py
│       │   ├── file_transcription_form_widget.py
│       │   ├── hugging_face_search_line_edit.py
│       │   ├── initial_prompt_text_edit.py
│       │   ├── languages_combo_box.py
│       │   ├── mms_language_line_edit.py
│       │   ├── tasks_combo_box.py
│       │   └── transcription_options_group_box.py
│       ├── transcription_record.py
│       ├── transcription_task_folder_watcher.py
│       ├── transcription_tasks_table_widget.py
│       ├── transcription_viewer/
│       │   ├── __init__.py
│       │   ├── export_transcription_menu.py
│       │   ├── speaker_identification_widget.py
│       │   ├── transcription_resizer_widget.py
│       │   ├── transcription_segments_editor_widget.py
│       │   ├── transcription_view_mode_tool_button.py
│       │   └── transcription_viewer_widget.py
│       ├── update_dialog.py
│       └── video_player.py
├── buzz.desktop
├── docs/
│   ├── .gitignore
│   ├── README.md
│   ├── babel.config.js
│   ├── docs/
│   │   ├── cli.md
│   │   ├── faq.md
│   │   ├── index.md
│   │   ├── installation.md
│   │   ├── preferences.md
│   │   └── usage/
│   │       ├── 1_file_import.md
│   │       ├── 2_live_recording.md
│   │       ├── 3_translations.md
│   │       ├── 4_edit_and_resize.md
│   │       ├── 5_speaker_identification.md
│   │       ├── 5_transcription_viewer.md
│   │       └── _category_.yml
│   ├── docusaurus.config.js
│   ├── i18n/
│   │   └── zh/
│   │       └── docusaurus-plugin-content-docs/
│   │           └── current/
│   │               ├── cli.md
│   │               ├── faq.md
│   │               ├── index.md
│   │               ├── installation.md
│   │               ├── preferences.md
│   │               └── usage/
│   │                   ├── 1_file_import.md
│   │                   ├── 2_live_recording.md
│   │                   ├── 3_translations.md
│   │                   ├── 4_edit_and_resize.md
│   │                   └── _category_.yml
│   ├── package.json
│   ├── sidebars.js
│   ├── src/
│   │   ├── css/
│   │   │   └── custom.css
│   │   └── pages/
│   │       ├── index.module.css
│   │       └── index.tsx
│   ├── static/
│   │   └── .nojekyll
│   └── tsconfig.json
├── entitlements.plist
├── flatpak/
│   └── run-buzz.sh
├── hatch_build.py
├── installer.iss
├── main.py
├── msgfmt.py
├── patches/
│   └── ctc_forced_aligner_windows_mutex.patch
├── pyproject.toml
├── pytest.ini
├── readme/
│   └── README.zh_CN.md
├── share/
│   ├── applications/
│   │   ├── buzz.desktop
│   │   └── io.github.chidiwilliams.Buzz.desktop
│   └── metainfo/
│       └── io.github.chidiwilliams.Buzz.metainfo.xml
├── snap/
│   └── snapcraft.yaml
└── tests/
    ├── __init__.py
    ├── app_main.py
    ├── audio.py
    ├── cache_test.py
    ├── cli_test.py
    ├── conftest.py
    ├── db/
    │   ├── dao/
    │   │   └── transcription_dao_test.py
    │   ├── entity/
    │   │   └── transcription_test.py
    │   └── service/
    │       └── transcription_service_test.py
    ├── gui_test.py
    ├── mock_qt.py
    ├── mock_sounddevice.py
    ├── model_loader.py
    ├── model_loader_test.py
    ├── recording_test.py
    ├── recording_transcriber_test.py
    ├── settings/
    │   └── settings_test.py
    ├── store/
    │   ├── __init__.py
    │   └── keyring_store_test.py
    ├── transcriber/
    │   ├── __init__.py
    │   ├── file_transcriber_queue_worker_test.py
    │   ├── openai_whisper_api_file_transcriber_test.py
    │   ├── recording_transcriber_test.py
    │   ├── transcriber_test.py
    │   ├── transformers_whisper_test.py
    │   ├── whisper_cpp_test.py
    │   └── whisper_file_transcriber_test.py
    ├── transformers_transcriber_test.py
    ├── translator_test.py
    ├── update_checker_test.py
    └── widgets/
        ├── __init__.py
        ├── advanced_settings_dialog_test.py
        ├── audio_meter_widget_test.py
        ├── audio_player_test.py
        ├── conftest.py
        ├── export_transcription_menu_test.py
        ├── file_transcriber_widget_test.py
        ├── hugging_face_search_line_edit_test.py
        ├── import_url_dialog_test.py
        ├── main_window_test.py
        ├── menu_bar_test.py
        ├── model_download_progress_dialog.py
        ├── model_type_combo_box_test.py
        ├── openai_api_key_line_edit_test.py
        ├── preferences_dialog/
        │   ├── __init__.py
        │   ├── folder_watch_preferences_widget_test.py
        │   ├── general_preferences_widget_test.py
        │   ├── models_preferences_widget_test.py
        │   └── preferences_dialog_test.py
        ├── presentation_window_test.py
        ├── recording_transcriber_widget_test.py
        ├── shortcuts_editor_widget_test.py
        ├── speaker_identification_widget_test.py
        ├── transcription_task_folder_watcher_test.py
        ├── transcription_tasks_table_widget_test.py
        ├── transcription_viewer/
        │   ├── __init__.py
        │   ├── transcription_segments_editor_widget_test.py
        │   └── transcription_viewer_widget_additional_test.py
        ├── transcription_viewer_test.py
        ├── update_dialog_test.py
        └── video_player_test.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .coveragerc
================================================
[run]
omit =
  buzz/whisper_cpp/*
  buzz/transcriber/local_whisper_cpp_server_transcriber.py
  *_test.py
  demucs/*
  whisper_diarization/*
  deepmultilingualpunctuation/*
  ctc_forced_aligner/*

[report]
exclude_also =
  if sys.platform == "win32":
  if platform.system\(\) == "Windows":
  if platform.system\(\) == "Linux":
  if platform.system\(\) == "Darwin":

[html]
directory = coverage/html


================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
  push:
    branches:
      - main
    tags:
      - "*"
  pull_request:

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: true

jobs:
  test:
    runs-on: ${{ matrix.os }}
    env:
      BUZZ_DISABLE_TELEMETRY: true
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: macos-15-intel
          - os: macos-latest
          - os: windows-latest
          - os: ubuntu-22.04
          - os: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive

      # Should be removed with next update to whisper.cpp
      - name: Downgrade Xcode
        uses: maxim-lobanov/setup-xcode@v1
        with:
          xcode-version: '16.0.0'
        if: matrix.os == 'macos-latest'

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Install Vulkan SDK
        if: "startsWith(matrix.os, 'ubuntu-') || matrix.os == 'windows-latest'"
        uses: humbletim/install-vulkan-sdk@v1.2
        with:
          version: 1.4.309.0
          cache: true

      - name: Install uv
        uses: astral-sh/setup-uv@v6

      - name: Load cached venv
        id: cached-uv-dependencies
        uses: actions/cache@v4
        with:
          path: .venv
          key: venv-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/uv.lock') }}

      - uses: AnimMouse/setup-ffmpeg@v1
        id: setup-ffmpeg
        with:
          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

      - name: Add msbuild to PATH
        uses: microsoft/setup-msbuild@v2
        if: runner.os == 'Windows'

      - name: Install apt dependencies
        run: |
          sudo apt-get update

          if [ "$(lsb_release -rs)" == "22.04" ]; then
            sudo apt-get install libegl1-mesa

            # Add ubuntu-toolchain-r PPA for newer libstdc++6 with GLIBCXX_3.4.32
            sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
            sudo apt-get update
            sudo apt-get install -y libstdc++6
          fi

          sudo apt-get install libyaml-dev libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-shape0 libxcb-cursor0 libportaudio2 gettext libpulse0 libgl1-mesa-dev libvulkan-dev ccache
        if: "startsWith(matrix.os, 'ubuntu-')"

      - name: Install dependencies
        run: uv sync

      - name: Test
        run: |
          uv run make test
        shell: bash
        env:
          PYTHONFAULTHANDLER: "1"

      - name: Upload coverage reports to Codecov with GitHub Action
        uses: codecov/codecov-action@v4
        with:
          flags: ${{ runner.os }}
          token: ${{ secrets.CODECOV_TOKEN }}
        env:
          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

  build:
    runs-on: ${{ matrix.os }}
    timeout-minutes: 90
    env:
      BUZZ_DISABLE_TELEMETRY: true
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: macos-15-intel
          - os: macos-latest
          - os: windows-latest
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive

      # Should be removed with next update to whisper.cpp
      - name: Downgrade Xcode
        uses: maxim-lobanov/setup-xcode@v1
        with:
          xcode-version: '16.0.0'
        if: matrix.os == 'macos-latest'

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Install Vulkan SDK
        if: "startsWith(matrix.os, 'ubuntu-') || matrix.os == 'windows-latest'"
        uses: humbletim/install-vulkan-sdk@v1.2
        with:
          version: 1.4.309.0
          cache: true

      - name: Install uv
        uses: astral-sh/setup-uv@v6

      - name: Load cached venv
        id: cached-uv-dependencies
        uses: actions/cache@v4
        with:
          path: .venv
          key: venv-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/uv.lock') }}

      - name: Install Inno Setup on Windows
        uses: crazy-max/ghaction-chocolatey@v3
        with:
          args: install innosetup --yes
        if: runner.os == 'Windows'

      - name: Install apt dependencies
        run: |
          sudo apt-get update

          if [ "$(lsb_release -rs)" == "22.04" ]; then
            sudo apt-get install libegl1-mesa

            # Add ubuntu-toolchain-r PPA for newer libstdc++6 with GLIBCXX_3.4.32
            sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
            sudo apt-get update
            sudo apt-get install -y libstdc++6
          fi

          sudo apt-get install libyaml-dev libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-shape0 libxcb-cursor0 libportaudio2 gettext libpulse0 libgl1-mesa-dev libvulkan-dev ccache
        if: "startsWith(matrix.os, 'ubuntu-')"

      - name: Install dependencies
        run: uv sync

      - uses: AnimMouse/setup-ffmpeg@v1
        id: setup-ffmpeg
        with:
          version: ${{ matrix.os == 'macos-15-intel' && '7.1.1' || matrix.os == 'macos-latest' && '80' || '8.0' }}

      - name: Install MSVC for Windows
        run: |
          if [ "$RUNNER_OS" == "Windows" ]; then
            uv add msvc-runtime
            uv pip install -U torch==2.8.0+cu129 torchaudio==2.8.0+cu129 --index-url https://download.pytorch.org/whl/cu129
            uv pip install nvidia-cublas-cu12==12.9.1.4 nvidia-cuda-cupti-cu12==12.9.79 nvidia-cuda-runtime-cu12==12.9.79 --extra-index-url https://pypi.ngc.nvidia.com

            uv cache clean
            uv run pip cache purge
          fi
        shell: bash

      - name: Add msbuild to PATH
        uses: microsoft/setup-msbuild@v2
        if: runner.os == 'Windows'

      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.0"
          bundler-cache: true
        if: "startsWith(matrix.os, 'ubuntu-')"

      - name: Install FPM
        run: gem install fpm
        if: "startsWith(matrix.os, 'ubuntu-')"

      - name: Clear space on Windows
        if: runner.os == 'Windows'
        run: |         
          rm 'C:\Android\android-sdk\' -r -force
          rm 'C:\Program Files (x86)\Google\' -r -force
          rm 'C:\tools\kotlinc\' -r -force
          rm 'C:\tools\php\' -r -force
          rm 'C:\selenium\' -r -force
        shell: pwsh

      - name: Bundle
        run: |
          if [ "$RUNNER_OS" == "macOS" ]; then

            brew install create-dmg

            sudo pkill -9 XProtect >/dev/null || true;
            while pgrep XProtect; do sleep 3; done;

            CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
            KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db

            echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH

            security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
            security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
            security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH

            security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
            security list-keychain -d user -s $KEYCHAIN_PATH

            xcrun notarytool store-credentials --apple-id "$APPLE_ID" --password "$APPLE_APP_PASSWORD" --team-id "$APPLE_TEAM_ID" notarytool --validate

            uv run make bundle_mac

          elif [ "$RUNNER_OS" == "Windows" ]; then

            cp -r ./dll_backup ./buzz/
            uv run make bundle_windows

          fi
        env:
          BUZZ_CODESIGN_IDENTITY: ${{ secrets.BUZZ_CODESIGN_IDENTITY }}
          BUZZ_KEYCHAIN_NOTARY_PROFILE: ${{ secrets.BUZZ_KEYCHAIN_NOTARY_PROFILE }}
          BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
          KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
          P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
          APPLE_ID: ${{ secrets.APPLE_ID }}
          APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }}
          APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
        shell: bash

      - uses: actions/upload-artifact@v4
        with:
          name: Buzz-${{ runner.os }}-${{ runner.arch }}
          path: |
            dist/Buzz*-windows.exe
            dist/Buzz*-windows-*.bin
            dist/Buzz*-mac.dmg

  build_wheels:
    runs-on: ${{ matrix.os }}
    env:
      BUZZ_DISABLE_TELEMETRY: true
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, windows-latest, macos-15-intel, macos-latest]

    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive

      # Should be removed with next update to whisper.cpp
      - name: Downgrade Xcode
        uses: maxim-lobanov/setup-xcode@v1
        with:
          xcode-version: '16.0.0'
        if: matrix.os == 'macos-latest'

      - name: Install Vulkan SDK
        if: "startsWith(matrix.os, 'ubuntu-') || matrix.os == 'windows-latest'"
        uses: humbletim/install-vulkan-sdk@v1.2
        with:
          version: 1.4.309.0
          cache: true

      - name: Install uv
        uses: astral-sh/setup-uv@v6

      - name: Build wheels
        run: uv build --wheel
        shell: bash

      - uses: actions/upload-artifact@v4
        with:
          name: buzz-wheel-${{ runner.os }}-${{ runner.arch }}
          path: ./dist/*.whl

  publish_pypi:
    needs: [build_wheels, test]
    runs-on: ubuntu-latest
    env:
      BUZZ_DISABLE_TELEMETRY: true
    environment: pypi
    permissions:
      id-token: write
    if: startsWith(github.ref, 'refs/tags/')
    steps:
      - uses: actions/download-artifact@v4
        with:
          pattern: buzz-wheel-*
          path: dist
          merge-multiple: true

      - uses: pypa/gh-action-pypi-publish@release/v1
        with:
          verbose: true
          password: ${{ secrets.PYPI_TOKEN }}

  release:
    runs-on: ${{ matrix.os }}
    env:
      BUZZ_DISABLE_TELEMETRY: true
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: macos-15-intel
          - os: macos-latest
          - os: windows-latest
    needs: [build, test]
    if: startsWith(github.ref, 'refs/tags/')
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive

      - uses: actions/download-artifact@v4
        with:
          name: Buzz-${{ runner.os }}-${{ runner.arch }}

      - name: Rename .dmg files
        if: runner.os == 'macOS'
        run: |
          for file in Buzz*.dmg; do
            mv "$file" "${file%.dmg}-${{ runner.arch }}.dmg"
          done

      - name: Release
        uses: softprops/action-gh-release@v2
        with:
          files: |
            Buzz*-unix.tar.gz
            Buzz*.exe
            Buzz*.bin
            Buzz*.dmg

#  Brew Cask deployment fails and the app is deprecated on Brew.
#  deploy_brew_cask:
#    runs-on: macos-latest
#    env:
#      BUZZ_DISABLE_TELEMETRY: true
#    needs: [release]
#    if: startsWith(github.ref, 'refs/tags/')
#    steps:
#      - uses: actions/checkout@v4
#        with:
#          submodules: recursive
#
#      # Should be removed with next update to whisper.cpp
#      - name: Downgrade Xcode
#        uses: maxim-lobanov/setup-xcode@v1
#        with:
#          xcode-version: '16.0.0'
#        if: matrix.os == 'macos-latest'
#
#      - name: Install uv
#        uses: astral-sh/setup-uv@v6
#
#      - name: Set up Python
#        uses: actions/setup-python@v5
#        with:
#          python-version: "3.12"
#
#      - name: Install dependencies
#        run: uv sync
#
#      - name: Upload to Brew
#        run: uv run make upload_brew
#        env:
#          HOMEBREW_GITHUB_API_TOKEN: ${{ secrets.HOMEBREW_GITHUB_API_TOKEN }}


================================================
FILE: .github/workflows/gh-pages.yml
================================================
---
name: GitHub Pages
on:
  push:
    branches:
      - main

jobs:
  deploy:
    name: Deploy
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 18
          cache: npm
          cache-dependency-path: docs/package-lock.json

      - name: Install dependencies
        run: npm ci
        working-directory: docs

      - name: Build
        run: npm run build
        working-directory: docs

      - name: Deploy to GitHub Pages
        uses: peaceiris/actions-gh-pages@v4
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./docs/build


================================================
FILE: .github/workflows/manual-build.yml
================================================
---
name: Manual Build
on: workflow_dispatch

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: true

jobs:
  build:
    runs-on: ${{ matrix.os }}
    env:
      BUZZ_DISABLE_TELEMETRY: true
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: macos-latest
          - os: windows-latest
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      - uses: actions/setup-python@v5
        with:
          python-version: "3.11.9"

      - name: Install Poetry Action
        uses: snok/install-poetry@v1.3.1
        with:
          virtualenvs-create: true
          virtualenvs-in-project: true

      - name: Load cached venv
        id: cached-poetry-dependencies
        uses: actions/cache@v4
        with:
          path: .venv
          key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}-2

      - uses: FedericoCarboni/setup-ffmpeg@v3.1
        id: setup-ffmpeg
        with:
          ffmpeg-version: release
          architecture: 'x64'
          github-token: ${{ github.server_url == 'https://github.com' && github.token || '' }}

      - name: Install dependencies
        run: poetry install

      - name: Bundle
        run: |
          if [ "$RUNNER_OS" == "macOS" ]; then

            brew install create-dmg
            poetry run make bundle_mac_unsigned

          elif [ "$RUNNER_OS" == "Windows" ]; then

            poetry run make bundle_windows

          fi
        shell: bash

      - uses: actions/upload-artifact@v4
        with:
          name: Buzz-${{ runner.os }}
          path: |
            dist/Buzz*-windows.exe
            dist/Buzz*-mac.dmg

  build-snap:
    runs-on: ubuntu-latest
    env:
      BUZZ_DISABLE_TELEMETRY: true
    outputs:
      snap: ${{ steps.snapcraft.outputs.snap }}
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      - uses: snapcore/action-build@v1
        id: snapcraft
      - run: |
          sudo apt-get update
          sudo apt-get install libportaudio2
      - run: sudo snap install --devmode *.snap
      - run: |
          cd $HOME
          xvfb-run buzz --version
      - uses: actions/upload-artifact@v4
        with:
          name: snap
          path: ${{ steps.snapcraft.outputs.snap }}

================================================
FILE: .github/workflows/snapcraft.yml
================================================
---
name: Snapcraft
on:
  push:
    branches:
      - main
    tags:
      - "*"
  pull_request:

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: true

jobs:
  build:
    runs-on: ubuntu-24.04
    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 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: Check available disk space
        run: |
          echo "=== Disk space ==="
          df -h
          echo "=== Memory ==="
          free -h
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      - name: Install Snapcraft and dependencies
        run: |
          set -x
          # Ensure snapd is ready
          sudo systemctl start snapd.socket
          sudo snap wait system seed.loaded

          echo "=== Installing snapcraft ==="
          sudo snap install --classic snapcraft

          echo "=== Installing gnome extension dependencies ==="
          sudo snap install gnome-46-2404 || { echo "Failed to install gnome-46-2404"; sudo journalctl -u snapd --no-pager -n 50; exit 1; }
          sudo snap install gnome-46-2404-sdk || { echo "Failed to install gnome-46-2404-sdk"; sudo journalctl -u snapd --no-pager -n 50; exit 1; }

          echo "=== Installing build-snaps ==="
          sudo snap install --classic astral-uv || { echo "Failed to install astral-uv"; sudo journalctl -u snapd --no-pager -n 50; exit 1; }

          echo "=== Installed snaps ==="
          snap list
      - name: Check disk space before build
        run: df -h
      - name: Build snap
        id: snapcraft
        env:
          SNAPCRAFT_BUILD_ENVIRONMENT: host
        run: |
          sudo -E snapcraft pack --verbose --destructive-mode
          echo "snap=$(ls *.snap)" >> $GITHUB_OUTPUT
      - run: sudo snap install --devmode *.snap
      - run: |
          cd $HOME
          xvfb-run buzz --version
      - uses: actions/upload-artifact@v4
        with:
          name: snap
          path: ${{ steps.snapcraft.outputs.snap }}

  upload-edge:
    runs-on: ubuntu-latest
    needs: [ build ]
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: snap
      - uses: snapcore/action-publish@v1
        env:
          SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_TOKEN }}
        with:
          snap: ${{ needs.build.outputs.snap }}
          release: edge

  upload-stable:
    runs-on: ubuntu-latest
    needs: [ build ]
    if: startsWith(github.ref, 'refs/tags/')
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: snap
      - uses: snapcore/action-publish@v1
        env:
          SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_TOKEN }}
        with:
          snap: ${{ needs.build.outputs.snap }}
          release: stable


================================================
FILE: .gitignore
================================================
dist/
__pycache__/
build/
.pytest_cache/
.coverage*
!.coveragerc
.env
.DS_Store
htmlcov/
coverage.xml
.idea/
.venv/
venv/
.claude/

# whisper_cpp
whisper_cpp
*.exe
*.dll
*.dylib
*.so
buzz/whisper_cpp/*

# Internationalization - compiled binaries
*.mo
*.po~

benchmarks.json

.eggs
*.egg-info
/coverage/
/wheelhouse/
/.flatpak-builder
/repo
/nemo_msdd_configs


================================================
FILE: .gitmodules
================================================
[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_repo"]
	path = demucs_repo
	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


================================================
FILE: .pre-commit-config.yaml
================================================
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.5.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml
      - id: check-added-large-files

  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.1.3
    hooks:
      - id: ruff
        args: [ --fix, --exit-non-zero-on-fix ]

      - id: ruff-format


================================================
FILE: .pylintrc
================================================
[MASTER]
disable=
  C0114, # missing-module-docstring
  C0116, # missing-function-docstring


================================================
FILE: .python-version
================================================
3.12


================================================
FILE: .run/pytest.run.xml
================================================
<component name="ProjectRunConfigurationManager">
  <configuration default="false" name="pytest" type="tests" factoryName="py.test" nameIsGenerated="true">
    <module name="buzz" />
    <option name="ENV_FILES" value="" />
    <option name="INTERPRETER_OPTIONS" value="" />
    <option name="PARENT_ENVS" value="true" />
    <option name="SDK_HOME" value="$PROJECT_DIR$/.venv/bin/python" />
    <option name="SDK_NAME" value="Poetry (buzz) (2)" />
    <option name="WORKING_DIRECTORY" value="" />
    <option name="IS_MODULE_SDK" value="false" />
    <option name="ADD_CONTENT_ROOTS" value="true" />
    <option name="ADD_SOURCE_ROOTS" value="true" />
    <EXTENSION ID="net.ashald.envfile">
      <option name="IS_ENABLED" value="false" />
      <option name="IS_SUBST" value="false" />
      <option name="IS_PATH_MACRO_SUPPORTED" value="false" />
      <option name="IS_IGNORE_MISSING_FILES" value="false" />
      <option name="IS_ENABLE_EXPERIMENTAL_INTEGRATIONS" value="false" />
      <ENTRIES>
        <ENTRY IS_ENABLED="true" PARSER="runconfig" IS_EXECUTABLE="false" />
      </ENTRIES>
    </EXTENSION>
    <option name="_new_keywords" value="&quot;&quot;" />
    <option name="_new_parameters" value="&quot;&quot;" />
    <option name="_new_additionalArguments" value="&quot;-s&quot;" />
    <option name="_new_target" value="&quot;&quot;" />
    <option name="_new_targetType" value="&quot;CUSTOM&quot;" />
    <method v="2" />
  </configuration>
</component>


================================================
FILE: .vscode/launch.json
================================================
{
  // Use IntelliSense to learn about possible attributes.
  // Hover to view descriptions of existing attributes.
  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Buzz",
      "type": "python",
      "request": "launch",
      "module": "main",
      "justMyCode": false
    }
  ]
}


================================================
FILE: .vscode/settings.json
================================================
{
  "files.associations": {
    ".coveragerc": "ini",
    "Buzz.spec": "python",
    "iosfwd": "cpp"
  },
  "files.exclude": {
    "**/.git": true,
    "**/.svn": true,
    "**/.hg": true,
    "**/CVS": true,
    "**/.DS_Store": true,
    "**/Thumbs.db": true
  },
  "python.testing.pytestArgs": ["."],
  "python.testing.unittestEnabled": false,
  "python.testing.pytestEnabled": true,
  "python.linting.pylintEnabled": true,
  "python.linting.enabled": true,
  "cmake.sourceDirectory": "${workspaceFolder}/whisper.cpp"
}


================================================
FILE: .vscode/tasks.json
================================================
{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "tasks": [
    {
      "label": "build",
      "type": "shell",
      "command": "make",
      "problemMatcher": [],
      "group": {
        "kind": "build",
        "isDefault": true
      }
    }
  ]
}


================================================
FILE: Buzz.spec
================================================
# -*- mode: python ; coding: utf-8 -*-
import os
import os.path
import platform
import shutil

from PyInstaller.utils.hooks import collect_data_files, copy_metadata

from buzz.__version__ import VERSION

datas = []
datas += collect_data_files("torch")
datas += collect_data_files("demucs")
datas += copy_metadata("tqdm")
datas += copy_metadata("torch")
datas += copy_metadata("regex")
datas += copy_metadata("requests")
datas += copy_metadata("packaging")
datas += copy_metadata("filelock")
datas += copy_metadata("numpy")
datas += copy_metadata("tokenizers")
datas += copy_metadata("huggingface-hub")
datas += copy_metadata("safetensors")
datas += copy_metadata("pyyaml")
datas += copy_metadata("julius")
datas += copy_metadata("openunmix")
datas += copy_metadata("lameenc")
datas += copy_metadata("diffq")
datas += copy_metadata("einops")
datas += copy_metadata("hydra-core")
datas += copy_metadata("hydra-colorlog")
datas += copy_metadata("museval")
datas += copy_metadata("submitit")
datas += copy_metadata("treetable")
datas += copy_metadata("soundfile")
datas += copy_metadata("dora-search")
datas += copy_metadata("lhotse")

# Allow transformers package to load __init__.py file dynamically:
# https://github.com/chidiwilliams/buzz/issues/272
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 += 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, excludes=["build"])
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", ".")]

block_cipher = None

DEBUG = os.environ.get("PYINSTALLER_DEBUG", "").lower() in ["1", "true"]
if DEBUG:
    options = [("v", None, "OPTION")]
else:
    options = []

def find_dependency(name: str) -> str:
    paths = os.environ["PATH"].split(os.pathsep)
    candidates = []
    for path in paths:
        exe_path = os.path.join(path, name)
        if os.path.isfile(exe_path):
            candidates.append(exe_path)

        # Check for chocolatery shims
        shim_path = os.path.normpath(os.path.join(path, "..", "lib", "ffmpeg", "tools", "ffmpeg", "bin", name))
        if os.path.isfile(shim_path):
            candidates.append(shim_path)

    if not candidates:
        return None

    # Pick the largest file
    return max(candidates, key=lambda f: os.path.getsize(f))

if platform.system() == "Windows":
    binaries = [
        (find_dependency("ffmpeg.exe"), "."),
        (find_dependency("ffprobe.exe"), "."),
    ]
else:
    binaries = [
        (shutil.which("ffmpeg"), "."),
        (shutil.which("ffprobe"), "."),
    ]

binaries.append(("buzz/whisper_cpp/*", "buzz/whisper_cpp"))

if platform.system() == "Windows":
    datas += [("dll_backup", "dll_backup")]
    datas += collect_data_files("msvc-runtime")

    binaries.append(("dll_backup/SDL2.dll", "dll_backup"))

a = Analysis(
    ["main.py"],
    pathex=[],
    binaries=binaries,
    datas=datas,
    hiddenimports=[
        "dora", "dora.log",
        "julius", "julius.core", "julius.resample",
        "openunmix", "openunmix.filtering",
        "lameenc",
        "diffq",
        "einops",
        "hydra", "hydra.core", "hydra.core.global_hydra",
        "hydra_colorlog",
        "museval",
        "submitit",
        "treetable",
        "soundfile",
        "_soundfile_data",
        "lhotse",
    ],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    # pyarrow is excluded because its Windows wheel requires AVX2 CPU instructions,
    # causing a crash (0xc000001d) on older hardware. Buzz does not use pyarrow directly;
    excludes=["pyarrow"],
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

exe = EXE(
    pyz,
    a.scripts,
    options,
    icon="./assets/buzz.ico",
    exclude_binaries=True,
    name="Buzz",
    debug=DEBUG,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    console=DEBUG,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=os.environ.get("BUZZ_CODESIGN_IDENTITY"),
    entitlements_file="entitlements.plist" if platform.system() == "Darwin" else None,
)
coll = COLLECT(
    exe,
    a.binaries,
    a.zipfiles,
    a.datas,
    strip=False,
    upx=False,
    upx_exclude=[],
    name="Buzz",
)
app = BUNDLE(
    coll,
    name="Buzz.app",
    icon="./assets/buzz.icns",
    bundle_identifier="com.chidiwilliams.buzz",
    version=VERSION,
    info_plist={
        "NSPrincipalClass": "NSApplication",
        "NSHighResolutionCapable": "True",
        "NSMicrophoneUsageDescription": "Allow Buzz to record audio from your microphone.",
    },
)


================================================
FILE: CLAUDE.md
================================================
- Use uv to run tests and any scripts

================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.

## Our Standards

Examples of behavior that contributes to a positive environment for our
community include:

* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
  and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
  community

Examples of unacceptable behavior include:

* The use of sexualized language or imagery, and sexual attention or advances of
  any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
  without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.

Community leaders 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, and will communicate reasons for moderation
decisions when appropriate.

## Scope

This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official email address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
williamschidi1@gmail.com.

All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the
reporter of any incident.

## Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:

### 1. Correction

**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.

**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.

### 2. Warning

**Community Impact**: A violation through a single incident or series of
actions.

**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.

### 3. Temporary Ban

**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.

**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.

### 4. Permanent Ban

**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.

**Consequence**: A permanent ban from any sort of public interaction within the
community.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].

Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].

For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].

[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations



================================================
FILE: CONTRIBUTING.md
================================================
# Buzz Contribution Guide

## Internationalization

To contribute a new language translation to Buzz:

1. Run `make translation_po locale=[locale]`. `[locale]` is a string with the format "language\[_script\]\[_country\]",
   where:

    - "language" is a lowercase, two-letter ISO 639 language code,
    - "script" is a titlecase, four-letter, ISO 15924 script code, and
    - "country" is an uppercase, two-letter, ISO 3166 country code.

   For example: `make translation_po locale=en_US`.

2. Fill in the translations in the `.po` file generated in `locale/[locale]/LC_MESSAGES`.
3. Run `make translation_mo` to compile the translations, then test your changes.
4. Create a new pull request with your changes.

## Troubleshooting

If you encounter any issues, please open an issue on the Buzz GitHub repository. Here are a few tips to gather data about the issue, so it is easier for us to fix.

**Provide details**

What version of the Buzz are you using? On what OS? What are steps to reproduce it? What settings were selected, like what model type and size was used.

**Logs**

Log files contain valuable information about what the Buzz was doing before the issue occurred. You can get the logs like this:
* Linux run the app from the terminal and check the output.
* Mac get logs from `~/Library/Logs/Buzz`.
* Windows paste this into the Windows Explorer address bar `%USERPROFILE%\AppData\Local\Buzz\Buzz\Logs` and check the logs file.

**Test on latest version**

To see if your issue has already been fixed, try running the latest version of the Buzz. To get it log in to the GitHub and go to [Actions section](https://github.com/chidiwilliams/buzz/actions/workflows/ci.yml?query=branch%3Amain). Latest development versions attached to Artifacts section of successful builds. 

Linux versions get also pushed to the snap. To install latest development version use `snap install buzz --channel latest/edge`



## Running Buzz locally

### Linux (Ubuntu)

1. Clone the repository `git clone --recursive https://github.com/chidiwilliams/buzz.git`
2. Enter repo folder `cd buzz`
3. Install uv `curl -LsSf https://astral.sh/uv/install.sh | sh` (or see [uv installation docs](https://docs.astral.sh/uv/getting-started/installation/))
4. Install system dependencies you may be missing
```
sudo apt-get install --no-install-recommends libyaml-dev libtbb-dev libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-shape0 libxcb-cursor0 libportaudio2 gettext libpulse0 ffmpeg
```
On versions prior to Ubuntu 24.04 install `sudo apt-get install --no-install-recommends libegl1-mesa`

5. Install the dependencies `uv sync`
6. Run Buzz `uv run buzz`

#### Necessary dependencies for Faster Whisper on GPU

   All the dependencies for GPU support should be included in the dependency packages already installed, 
   but if you get issues running Faster Whisper on GPU, install [CUDA 12](https://developer.nvidia.com/cuda-downloads), [cuBLASS](https://developer.nvidia.com/cublas) and [cuDNN](https://developer.nvidia.com/cudnn).

#### Error for Faster Whisper on GPU `Could not load library libcudnn_ops_infer.so.8`

   You need to add path to the library to the `LD_LIBRARY_PATH` environment variable.
   Check exact path to your uv virtual environment, it may be different for you.

```
  export LD_LIBRARY_PATH=/path/to/buzz/.venv/lib/python3.12/site-packages/nvidia/cudnn/lib/:$LD_LIBRARY_PATH
```

#### For Whisper.cpp you will need to install Vulkan SDK

   Follow the instructions for your distribution https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html

### Mac

1. Clone the repository `git clone --recursive https://github.com/chidiwilliams/buzz.git`
2. Enter repo folder `cd buzz`
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. Run Buzz `uv run buzz`



### Windows

Assumes you have [Git](https://git-scm.com/downloads) and [python](https://www.python.org/downloads) installed and added to PATH.

1. Install the chocolatey package manager for Windows. [More info](https://docs.chocolatey.org/en-us/choco/setup)
```
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
```
2. Install the build tools. `choco install make cmake`
3. Install the ffmpeg. `choco install ffmpeg`
4. Download [Build Tools for Visual Studio 2022](https://visualstudio.microsoft.com/vs/older-downloads/) and install "Desktop development with C++" workload.
5. Add location of `namke` to your PATH environment variable. Usually it is `C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.44.35207\bin\Hostx64\x86`
6. Install Vulkan SDK from https://vulkan.lunarg.com/sdk/home
7. Clone the repository `git clone --recursive https://github.com/chidiwilliams/buzz.git`
8. Enter repo folder `cd buzz`
9. Install uv `powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"`
10. Install the dependencies `uv sync`
11. Build Whisper.cpp `uv run make buzz/whisper_cpp`
12. `cp -r .\dll_backup\ .\buzz\`
13. Run Buzz `uv run buzz`

Note: It should be safe to ignore any "syntax errors" you see during the build. Buzz will work. Also you can ignore any errors for FFmpeg. Buzz tries to load FFmpeg by several different means and some of them throw errors, but FFmpeg should eventually be found and work. 

#### GPU Support

GPU support on Windows with Nvidia GPUs is included out of the box in the `.exe` installer.

To add GPU support for source or `pip` installed version switch torch library to GPU version. For more info see https://pytorch.org/get-started/locally/ .
```
uv add --index https://download.pytorch.org/whl/cu128 torch==2.7.1+cu128 torchaudio==2.7.1+cu128
uv add --index https://pypi.ngc.nvidia.com nvidia-cublas-cu12==12.8.3.14 nvidia-cuda-cupti-cu12==12.8.57 nvidia-cuda-nvrtc-cu12==12.8.61 nvidia-cuda-runtime-cu12==12.8.57 nvidia-cudnn-cu12==9.7.1.26 nvidia-cufft-cu12==11.3.3.41 nvidia-curand-cu12==10.3.9.55 nvidia-cusolver-cu12==11.7.2.55 nvidia-cusparse-cu12==12.5.4.2 nvidia-cusparselt-cu12==0.6.3 nvidia-nvjitlink-cu12==12.8.61 nvidia-nvtx-cu12==12.8.55
```

To use Faster Whisper on GPU, install the following libraries:
* [cuBLAS](https://developer.nvidia.com/cublas)
* [cuDNN](https://developer.nvidia.com/cudnn)

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2022 Chidi Williams

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.


================================================
FILE: Makefile
================================================
# Change also in pyproject.toml and buzz/__version__.py
version := 1.4.4

mac_app_path := ./dist/Buzz.app
mac_zip_path := ./dist/Buzz-${version}-mac.zip
mac_dmg_path := ./dist/Buzz-${version}-mac.dmg

bundle_windows: dist/Buzz
	iscc installer.iss

bundle_mac: dist/Buzz.app codesign_all_mac zip_mac notarize_zip staple_app_mac dmg_mac

bundle_mac_unsigned: dist/Buzz.app zip_mac dmg_mac_unsigned

clean:
ifeq ($(OS), Windows_NT)
	-rmdir /s /q buzz\whisper_cpp
	-rmdir /s /q whisper.cpp\build
	-rmdir /s /q dist
	-Remove-Item -Recurse -Force buzz\whisper_cpp
	-Remove-Item -Recurse -Force whisper.cpp\build
	-Remove-Item -Recurse -Force dist\*
	-rm -rf buzz/whisper_cpp
	-rm -rf whisper.cpp/build
	-rm -rf dist/*
	-rm -rf buzz/__pycache__ buzz/**/__pycache__ buzz/**/**/__pycache__ buzz/**/**/**/__pycache__
	-for /d /r buzz %%d in (__pycache__) do @if exist "%%d" rmdir /s /q "%%d"
else
	rm -rf buzz/whisper_cpp || true
	rm -rf whisper.cpp/build || true
	rm -rf dist/* || true
	find buzz -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
endif

COVERAGE_THRESHOLD := 70

test: buzz/whisper_cpp
# A check to get updates of yt-dlp. Should run only on local as part of regular development operations
# Sort of a local "update checker"
ifndef CI
	uv lock --upgrade-package yt-dlp
endif
	pytest -s -vv --cov=buzz --cov-report=xml --cov-report=html --benchmark-skip --cov-fail-under=${COVERAGE_THRESHOLD} --cov-config=.coveragerc

benchmarks: buzz/whisper_cpp
	pytest -s -vv --benchmark-only --benchmark-json benchmarks.json

dist/Buzz dist/Buzz.app: buzz/whisper_cpp
	pyinstaller --noconfirm Buzz.spec

version:
	echo "VERSION = \"${version}\"" > buzz/__version__.py

buzz/whisper_cpp: translation_mo
ifeq ($(OS), Windows_NT)
	# Build Whisper with Vulkan support.
	# The _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR is needed to prevent mutex lock issues on Windows
	# https://github.com/actions/runner-images/issues/10004#issuecomment-2156109231
	# -DCMAKE_[C|CXX]_COMPILER_WORKS=TRUE is used to prevent issue in building test program that fails on CI
	# GGML_NATIVE=OFF ensures we don't use -march=native (which would target the build machine's CPU)
	cmake -S whisper.cpp -B whisper.cpp/build/ -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_RPATH='$$ORIGIN' -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON -DCMAKE_C_FLAGS="-D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR"  -DCMAKE_CXX_FLAGS="-D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR" -DCMAKE_C_COMPILER_WORKS=TRUE -DCMAKE_CXX_COMPILER_WORKS=TRUE -DGGML_VULKAN=1 -DGGML_NATIVE=OFF
	cmake --build whisper.cpp/build -j --config Release --verbose

	-mkdir buzz/whisper_cpp
	cp whisper.cpp/build/bin/Release/whisper-cli.exe buzz/whisper_cpp/
	cp whisper.cpp/build/bin/Release/whisper-server.exe buzz/whisper_cpp/
	cp dll_backup/SDL2.dll buzz/whisper_cpp
	PowerShell -NoProfile -ExecutionPolicy Bypass -Command "if (-not (Test-Path 'buzz\whisper_cpp\ggml-silero-v6.2.0.bin')) { Start-BitsTransfer -Source https://huggingface.co/ggml-org/whisper-vad/resolve/main/ggml-silero-v6.2.0.bin -Destination 'buzz\whisper_cpp\ggml-silero-v6.2.0.bin' }"
endif

ifeq ($(shell uname -s), Linux)
	# Build Whisper with Vulkan support
	# GGML_NATIVE=OFF ensures we don't use -march=native (which would target the build machine's CPU)
	# This enables portable SSE4.2/AVX/AVX2 optimizations that work on most x86_64 CPUs
	rm -rf whisper.cpp/build || true
	-mkdir -p buzz/whisper_cpp
	cmake -S whisper.cpp -B whisper.cpp/build/ -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_RPATH='$$ORIGIN' -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON -DGGML_VULKAN=1 -DGGML_NATIVE=OFF
	cmake --build whisper.cpp/build -j --config Release --verbose
	cp whisper.cpp/build/bin/whisper-cli buzz/whisper_cpp/ || true
	cp whisper.cpp/build/bin/whisper-server buzz/whisper_cpp/ || true
	cp -P whisper.cpp/build/src/libwhisper.so* buzz/whisper_cpp/ || true
	cp -P whisper.cpp/build/ggml/src/libggml.so* buzz/whisper_cpp/ || true
	cp -P whisper.cpp/build/ggml/src/libggml-base.so* buzz/whisper_cpp/ || true
	cp -P whisper.cpp/build/ggml/src/libggml-cpu.so* buzz/whisper_cpp/ || true
	cp -P whisper.cpp/build/ggml/src/ggml-vulkan/libggml-vulkan.so* buzz/whisper_cpp/ || true
	test -f buzz/whisper_cpp/ggml-silero-v6.2.0.bin || curl -L -o buzz/whisper_cpp/ggml-silero-v6.2.0.bin https://huggingface.co/ggml-org/whisper-vad/resolve/main/ggml-silero-v6.2.0.bin
endif

# Build on Macs
ifeq ($(shell uname -s), Darwin)
	-rm -rf whisper.cpp/build || true
	-mkdir -p buzz/whisper_cpp

ifeq ($(shell uname -m), arm64)
	cmake -S whisper.cpp -B whisper.cpp/build/ -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DWHISPER_COREML=1
else
    # Intel
	cmake -S whisper.cpp -B whisper.cpp/build/ -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DGGML_VULKAN=0 -DGGML_METAL=0
endif

	cmake --build whisper.cpp/build -j --config Release --verbose
	cp whisper.cpp/build/bin/whisper-cli buzz/whisper_cpp/ || true
	cp whisper.cpp/build/bin/whisper-server buzz/whisper_cpp/ || true
	cp whisper.cpp/build/src/libwhisper.dylib buzz/whisper_cpp/ || true
	cp whisper.cpp/build/ggml/src/libggml* buzz/whisper_cpp/ || true
	test -f buzz/whisper_cpp/ggml-silero-v6.2.0.bin || curl -L -o buzz/whisper_cpp/ggml-silero-v6.2.0.bin https://huggingface.co/ggml-org/whisper-vad/resolve/main/ggml-silero-v6.2.0.bin
endif

# Prints all the Mac developer identities used for code signing
print_identities_mac:
	security find-identity -p basic -v

dmg_mac:
	ditto -x -k "${mac_zip_path}" dist/dmg
	create-dmg \
		--volname "Buzz" \
		--volicon "./assets/buzz.icns" \
		--window-pos 200 120 \
		--window-size 600 300 \
		--icon-size 100 \
		--icon "Buzz.app" 175 120 \
		--hide-extension "Buzz.app" \
		--app-drop-link 425 120 \
		--codesign "$$BUZZ_CODESIGN_IDENTITY" \
		--notarize "$$BUZZ_KEYCHAIN_NOTARY_PROFILE" \
		--filesystem APFS \
		"${mac_dmg_path}" \
		"dist/dmg/"

dmg_mac_unsigned:
	ditto -x -k "${mac_zip_path}" dist/dmg
	create-dmg \
		--volname "Buzz" \
		--volicon "./assets/buzz.icns" \
		--window-pos 200 120 \
		--window-size 600 300 \
		--icon-size 100 \
		--icon "Buzz.app" 175 120 \
		--hide-extension "Buzz.app" \
		--app-drop-link 425 120 \
		"${mac_dmg_path}" \
		"dist/dmg/"

staple_app_mac:
	xcrun stapler staple ${mac_app_path}

notarize_zip:
	xcrun notarytool submit ${mac_zip_path} --keychain-profile "$$BUZZ_KEYCHAIN_NOTARY_PROFILE" --wait

zip_mac:
	ditto -c -k --keepParent "${mac_app_path}" "${mac_zip_path}"

codesign_all_mac: dist/Buzz.app
	for i in $$(find dist/Buzz.app/Contents/Resources/torch/bin -name "*" -type f); \
	do \
		codesign --force --options=runtime --sign "$$BUZZ_CODESIGN_IDENTITY" --timestamp "$$i"; \
	done
	for i in $$(find dist/Buzz.app/Contents/Resources -name "*.dylib" -o -name "*.so" -type f); \
	do \
		codesign --force --options=runtime --sign "$$BUZZ_CODESIGN_IDENTITY" --timestamp "$$i"; \
	done
	for i in $$(find dist/Buzz.app/Contents/MacOS -name "*.dylib" -o -name "*.so" -o -name "Qt*" -o -name "Python" -type f); \
	do \
		codesign --force --options=runtime --sign "$$BUZZ_CODESIGN_IDENTITY" --timestamp "$$i"; \
	done
	codesign --force --options=runtime --sign "$$BUZZ_CODESIGN_IDENTITY" --timestamp dist/Buzz.app/Contents/MacOS/Buzz
	codesign --force --options=runtime --sign "$$BUZZ_CODESIGN_IDENTITY" --entitlements ./entitlements.plist --timestamp dist/Buzz.app
	codesign --verify --deep --strict --verbose=2 dist/Buzz.app

# HELPERS

# Get the build logs for a notary upload
notarize_log:
	xcrun notarytool log ${id} --keychain-profile "$$BUZZ_KEYCHAIN_NOTARY_PROFILE"

# Make GGML model from whisper. Example: make ggml model_path=/Users/chidiwilliams/.cache/whisper/medium.pt
ggml:
	python3 ./whisper.cpp/models/convert-pt-to-ggml.py ${model_path} .venv/lib/python3.12/site-packages/whisper dist

upload_brew:
	brew bump-cask-pr --version ${version} --verbose buzz

UPGRADE_VERSION_BRANCH := upgrade-to-${version}
gh_upgrade_pr:
	git checkout main && git pull
	git checkout -B ${UPGRADE_VERSION_BRANCH}

	make version version=${version}

	git commit -am "Upgrade to ${version}"
	git push --set-upstream origin ${UPGRADE_VERSION_BRANCH}

	gh pr create --fill
	gh pr merge ${UPGRADE_VERSION_BRANCH} --auto --squash

# Internationalization

translation_po_all:
	$(MAKE) translation_po locale=ca_ES
	$(MAKE) translation_po locale=da_DK
	$(MAKE) translation_po locale=de_DE
	$(MAKE) translation_po locale=en_US
	$(MAKE) translation_po locale=es_ES
	$(MAKE) translation_po locale=it_IT
	$(MAKE) translation_po locale=ja_JP
	$(MAKE) translation_po locale=lv_LV
	$(MAKE) translation_po locale=nl
	$(MAKE) translation_po locale=pl_PL
	$(MAKE) translation_po locale=pt_BR
	$(MAKE) translation_po locale=uk_UA
	$(MAKE) translation_po locale=zh_CN
	$(MAKE) translation_po locale=zh_TW

TMP_POT_FILE_PATH := $(shell mktemp)
PO_FILE_PATH := buzz/locale/${locale}/LC_MESSAGES/buzz.po
translation_po:
	mkdir -p buzz/locale/${locale}/LC_MESSAGES
	xgettext --from-code=UTF-8 --add-location=file -o "${TMP_POT_FILE_PATH}" -l python $(shell find buzz -name '*.py')
	sed -i.bak 's/CHARSET/UTF-8/' ${TMP_POT_FILE_PATH}
	if [ ! -f ${PO_FILE_PATH} ]; then \
		msginit --no-translator --input=${TMP_POT_FILE_PATH} --output-file=${PO_FILE_PATH}; \
	fi
	rm ${TMP_POT_FILE_PATH}.bak
	msgmerge -U ${PO_FILE_PATH} ${TMP_POT_FILE_PATH}

# On windows we can have two ways to compile locales, one for CI the other for local builds
# Will try both and ignore errors if they fail
translation_mo:
ifeq ($(OS), Windows_NT)
	-forfiles /p buzz\locale /c "cmd /c python ..\..\msgfmt.py -o @path\LC_MESSAGES\buzz.mo @path\LC_MESSAGES\buzz.po"
	-for dir in buzz/locale/*/ ; do \
		python msgfmt.py -o $$dir/LC_MESSAGES/buzz.mo $$dir/LC_MESSAGES/buzz.po; \
	done
else
	for dir in buzz/locale/*/ ; do \
		python3 msgfmt.py -o $$dir/LC_MESSAGES/buzz.mo $$dir/LC_MESSAGES/buzz.po; \
	done
endif

lint:
	ruff check . --fix
	ruff format .


================================================
FILE: README.ja_JP.md
================================================
# Buzz

[ドキュメント](https://chidiwilliams.github.io/buzz/)

パソコン上でオフラインで音声の文字起こしと翻訳を行います。OpenAIの[Whisper](https://github.com/openai/whisper)を使用しています。

![MIT License](https://img.shields.io/badge/license-MIT-green)
[![CI](https://github.com/chidiwilliams/buzz/actions/workflows/ci.yml/badge.svg)](https://github.com/chidiwilliams/buzz/actions/workflows/ci.yml)
[![codecov](https://codecov.io/github/chidiwilliams/buzz/branch/main/graph/badge.svg?token=YJSB8S2VEP)](https://codecov.io/github/chidiwilliams/buzz)
![GitHub release (latest by date)](https://img.shields.io/github/v/release/chidiwilliams/buzz)
[![Github all releases](https://img.shields.io/github/downloads/chidiwilliams/buzz/total.svg)](https://GitHub.com/chidiwilliams/buzz/releases/)

![Buzz](./buzz/assets/buzz-banner.jpg)

## 機能
- 音声・動画ファイルまたはYouTubeリンクの文字起こし
- マイクからのリアルタイム音声文字起こし
  - イベントやプレゼンテーション中に便利なプレゼンテーションウィンドウ
- ノイズの多い音声でより高い精度を得るための、文字起こし前の話者分離
- 文字起こしメディアでの話者識別
- 複数のWhisperバックエンドをサポート
  - Nvidia GPU向けCUDAアクセラレーション対応
  - Mac向けApple Silicon対応
  - Whisper.cppでのVulkanアクセラレーション対応(統合GPUを含むほとんどのGPUで利用可能)
- TXT、SRT、VTT形式での文字起こしエクスポート
- 検索、再生コントロール、速度調整機能を備えた高度な文字起こしビューア
- 効率的なナビゲーションのためのキーボードショートカット
- 新しいファイルの自動文字起こしのための監視フォルダ
- スクリプトや自動化のためのコマンドラインインターフェース

## インストール

### macOS

[SourceForge](https://sourceforge.net/projects/buzz-captions/files/)から`.dmg`ファイルをダウンロードしてください。

### Windows

[SourceForge](https://sourceforge.net/projects/buzz-captions/files/)からインストールファイルを入手してください。

アプリは署名されていないため、インストール時に警告が表示されます。`詳細情報` -> `実行`を選択してください。

### Linux

Buzzは[Flatpak](https://flathub.org/apps/io.github.chidiwilliams.Buzz)または[Snap](https://snapcraft.io/buzz)として利用可能です。

Flatpakをインストールするには、以下を実行してください:
```shell
flatpak install flathub io.github.chidiwilliams.Buzz
```

[![Download on Flathub](https://flathub.org/api/badge?svg&locale=en)](https://flathub.org/en/apps/io.github.chidiwilliams.Buzz)

Snapをインストールするには、以下を実行してください:
```shell
sudo apt-get install libportaudio2 libcanberra-gtk-module libcanberra-gtk3-module
sudo snap install buzz
```

[![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-black.svg)](https://snapcraft.io/buzz)

### PyPI

[ffmpeg](https://www.ffmpeg.org/download.html)をインストールしてください。

Python 3.12環境を使用していることを確認してください。

Buzzをインストール

```shell
pip install buzz-captions
python -m buzz
```

**PyPIでのGPUサポート**

PyPIでインストールしたバージョンでWindows上のNvidia GPUのGPUサポートを有効にするには、[torch](https://pytorch.org/get-started/locally/)のCUDAサポートを確認してください。

```
pip3 install -U torch==2.8.0+cu129 torchaudio==2.8.0+cu129 --index-url https://download.pytorch.org/whl/cu129
pip3 install nvidia-cublas-cu12==12.9.1.4 nvidia-cuda-cupti-cu12==12.9.79 nvidia-cuda-runtime-cu12==12.9.79 --extra-index-url https://pypi.ngc.nvidia.com
```

### 最新開発版

最新の機能やバグ修正を含む最新開発版の入手方法については、[FAQ](https://chidiwilliams.github.io/buzz/docs/faq#9-where-can-i-get-latest-development-version)をご覧ください。

### スクリーンショット

<div style="display: flex; flex-wrap: wrap;">
    <img alt="ファイルインポート" src="share/screenshots/buzz-1-import.png" style="max-width: 18%; margin-right: 1%;" />
    <img alt="メイン画面" src="share/screenshots/buzz-2-main_screen.png" style="max-width: 18%; margin-right: 1%; height:auto;" />
    <img alt="設定" src="share/screenshots/buzz-3-preferences.png" style="max-width: 18%; margin-right: 1%; height:auto;" />
    <img alt="モデル設定" src="share/screenshots/buzz-3.2-model-preferences.png" style="max-width: 18%; margin-right: 1%; height:auto;" />
    <img alt="文字起こし" src="share/screenshots/buzz-4-transcript.png" style="max-width: 18%; margin-right: 1%; height:auto;" />
    <img alt="ライブ録音" src="share/screenshots/buzz-5-live_recording.png" style="max-width: 18%; margin-right: 1%; height:auto;" />
    <img alt="リサイズ" src="share/screenshots/buzz-6-resize.png" style="max-width: 18%;" />
</div>


================================================
FILE: README.md
================================================
[[简体中文](readme/README.zh_CN.md)] <- 点击查看中文页面。

# Buzz

[Documentation](https://chidiwilliams.github.io/buzz/)

Transcribe and translate audio offline on your personal computer. Powered by
OpenAI's [Whisper](https://github.com/openai/whisper).

![MIT License](https://img.shields.io/badge/license-MIT-green)
[![CI](https://github.com/chidiwilliams/buzz/actions/workflows/ci.yml/badge.svg)](https://github.com/chidiwilliams/buzz/actions/workflows/ci.yml)
[![codecov](https://codecov.io/github/chidiwilliams/buzz/branch/main/graph/badge.svg?token=YJSB8S2VEP)](https://codecov.io/github/chidiwilliams/buzz)
![GitHub release (latest by date)](https://img.shields.io/github/v/release/chidiwilliams/buzz)
[![Github all releases](https://img.shields.io/github/downloads/chidiwilliams/buzz/total.svg)](https://GitHub.com/chidiwilliams/buzz/releases/)

![Buzz](https://raw.githubusercontent.com/chidiwilliams/buzz/refs/heads/main/buzz/assets/buzz-banner.jpg)

## Features
- Transcribe audio and video files or Youtube links
- Live realtime audio transcription from microphone
  - Presentation window for easy accessibility during events and presentations
- Speech separation before transcription for better accuracy on noisy audio
- Speaker identification in transcribed media
- Multiple whisper backend support
  - CUDA acceleration support for Nvidia GPUs
  - Apple Silicon support for Macs
  - Vulkan acceleration support for Whisper.cpp on most GPUs, including integrated GPUs
- Export transcripts to TXT, SRT, and VTT
- Advanced Transcription Viewer with search, playback controls, and speed adjustment
- Keyboard shortcuts for efficient navigation
- Watch folder for automatic transcription of new files
- Command-Line Interface for scripting and automation

## Installation

### macOS

Download the `.dmg` from the [SourceForge](https://sourceforge.net/projects/buzz-captions/files/).

### Windows

Get the installation files from the [SourceForge](https://sourceforge.net/projects/buzz-captions/files/).

App is not signed, you will get a warning when you install it. Select `More info` -> `Run anyway`.

### Linux

Buzz is available as a [Flatpak](https://flathub.org/apps/io.github.chidiwilliams.Buzz) or a [Snap](https://snapcraft.io/buzz). 

To install flatpak, run:
```shell
flatpak install flathub io.github.chidiwilliams.Buzz
```

[![Download on Flathub](https://flathub.org/api/badge?svg&locale=en)](https://flathub.org/en/apps/io.github.chidiwilliams.Buzz)

To install snap, run:
```shell
sudo apt-get install libportaudio2 libcanberra-gtk-module libcanberra-gtk3-module
sudo snap install buzz
```

[![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-black.svg)](https://snapcraft.io/buzz)

### PyPI

Install [ffmpeg](https://www.ffmpeg.org/download.html)

Ensure you use Python 3.12 environment.

Install Buzz

```shell
pip install buzz-captions
python -m buzz
```

**GPU support for PyPI**

To have GPU support for Nvidia GPUS on Windows, for PyPI installed version ensure, CUDA support for [torch](https://pytorch.org/get-started/locally/) 

```
pip3 install -U torch==2.8.0+cu129 torchaudio==2.8.0+cu129 --index-url https://download.pytorch.org/whl/cu129
pip3 install nvidia-cublas-cu12==12.9.1.4 nvidia-cuda-cupti-cu12==12.9.79 nvidia-cuda-runtime-cu12==12.9.79 --extra-index-url https://pypi.ngc.nvidia.com
```

### Latest development version

For info on how to get latest development version with latest features and bug fixes see [FAQ](https://chidiwilliams.github.io/buzz/docs/faq#9-where-can-i-get-latest-development-version).

### Support Buzz

You can help the Buzz by starring 🌟 the repo and sharing it with your friends.

### Screenshots

<div style="display: flex; flex-wrap: wrap;">
    <img alt="File import" src="https://github.com/chidiwilliams/buzz/raw/main/share/screenshots/buzz-1-import.png" style="max-width: 18%; margin-right: 1%;" />
    <img alt="Main screen" src="https://github.com/chidiwilliams/buzz/raw/main/share/screenshots/buzz-2-main_screen.png" style="max-width: 18%; margin-right: 1%; height:auto;" />
    <img alt="Preferences" src="https://github.com/chidiwilliams/buzz/raw/main/share/screenshots/buzz-3-preferences.png" style="max-width: 18%; margin-right: 1%; height:auto;" />
    <img alt="Model preferences" src="https://github.com/chidiwilliams/buzz/raw/main/share/screenshots/buzz-3.2-model-preferences.png" style="max-width: 18%; margin-right: 1%; height:auto;" />
    <img alt="Transcript" src="https://github.com/chidiwilliams/buzz/raw/main/share/screenshots/buzz-4-transcript.png" style="max-width: 18%; margin-right: 1%; height:auto;" />
    <img alt="Live recording" src="https://github.com/chidiwilliams/buzz/raw/main/share/screenshots/buzz-5-live_recording.png" style="max-width: 18%; margin-right: 1%; height:auto;" />
    <img alt="Resize" src="https://github.com/chidiwilliams/buzz/raw/main/share/screenshots/buzz-6-resize.png" style="max-width: 18%;" />
</div>



================================================
FILE: buzz/__init__.py
================================================


================================================
FILE: buzz/__main__.py
================================================
import buzz.buzz

if __name__ == "__main__":
    buzz.buzz.main()


================================================
FILE: buzz/__version__.py
================================================
VERSION = "1.4.4"


================================================
FILE: buzz/action.py
================================================
import typing

from PyQt6.QtGui import QAction, QKeySequence


class Action(QAction):
    def setShortcut(
        self,
        shortcut: typing.Union["QKeySequence", "QKeySequence.StandardKey", str, int],
    ) -> None:
        super().setShortcut(shortcut)
        self.setToolTip(Action.get_tooltip(self))

    @classmethod
    def get_tooltip(cls, action: QAction):
        tooltip = action.toolTip()
        shortcut = action.shortcut()

        if shortcut.isEmpty():
            return tooltip

        shortcut_text = shortcut.toString(QKeySequence.SequenceFormat.NativeText)
        return f"<p style='white-space:pre'>{tooltip}&nbsp;&nbsp;<code style='font-size:small'>{shortcut_text}</code></p>"


================================================
FILE: buzz/assets.py
================================================
import os
import sys

APP_BASE_DIR = (
    getattr(sys, "_MEIPASS", os.path.dirname(os.path.abspath(__file__)))
    if getattr(sys, "frozen", False)
    else os.path.dirname(__file__)
)


def get_path(path: str):
    return os.path.join(APP_BASE_DIR, path)


================================================
FILE: buzz/buzz.py
================================================
import faulthandler
import logging
import multiprocessing
import os
import platform
import sys
from pathlib import Path
from typing import TextIO

# Set up CUDA library paths before any torch imports
# This must happen before platformdirs or any other imports that might indirectly load torch
import buzz.cuda_setup  # noqa: F401

from platformdirs import user_log_dir, user_cache_dir, user_data_dir

# Will download all Huggingface data to the app cache directory
os.environ.setdefault("HF_HOME", user_cache_dir("Buzz"))

from buzz.assets import APP_BASE_DIR

# Check for segfaults if not running in frozen mode
# Note: On Windows, faulthandler can print "Windows fatal exception" messages
# for non-fatal RPC errors (0x800706be) during multiprocessing operations.
# These are usually harmless but noisy, so we disable faulthandler on Windows.
if getattr(sys, "frozen", False) is False and platform.system() != "Windows":
    faulthandler.enable()

# Sets stdout/stderr to no-op TextIO when None (run as Windows GUI with --noconsole).
# stdout fix: torch.hub uses sys.stdout.write() for download progress and crashes if None.
# stderr fix: Resolves https://github.com/chidiwilliams/buzz/issues/221
if sys.stdout is None:
    sys.stdout = TextIO()
if sys.stderr is None:
    sys.stderr = TextIO()

# Adds the current directory to the PATH, so the ffmpeg binary get picked up:
# https://stackoverflow.com/a/44352931/9830227
os.environ["PATH"] += os.pathsep + APP_BASE_DIR

# Add the app directory to the DLL list: https://stackoverflow.com/a/64303856
if platform.system() == "Windows":
    os.add_dll_directory(APP_BASE_DIR)

    dll_backup_dir = os.path.join(APP_BASE_DIR, "dll_backup")
    if os.path.isdir(dll_backup_dir):
        os.add_dll_directory(dll_backup_dir)

    onnx_dll_dir = os.path.join(APP_BASE_DIR, "onnxruntime", "capi")
    if os.path.isdir(onnx_dll_dir):
        os.add_dll_directory(onnx_dll_dir)


def main():
    if platform.system() == "Linux":
        multiprocessing.set_start_method("spawn")

    # Fixes opening new window when app has been frozen on Windows:
    # https://stackoverflow.com/a/33979091
    multiprocessing.freeze_support()

    log_dir = user_log_dir(appname="Buzz")
    os.makedirs(log_dir, exist_ok=True)

    log_format = (
        "[%(asctime)s] %(module)s.%(funcName)s:%(lineno)d %(levelname)s -> %(message)s"
    )
    logging.basicConfig(
        filename=os.path.join(log_dir, "logs.txt"),
        level=logging.DEBUG,
        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("nemo_logging").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)
        stdout_handler.setFormatter(logging.Formatter(log_format))
        logging.getLogger().addHandler(stdout_handler)

    from buzz.cli import parse_command_line
    from buzz.widgets.application import Application

    logging.debug("app_dir: %s", APP_BASE_DIR)
    logging.debug("log_dir: %s", log_dir)
    logging.debug("cache_dir: %s", user_cache_dir("Buzz"))
    logging.debug("data_dir: %s", user_data_dir("Buzz"))

    app = Application(sys.argv)
    parse_command_line(app)
    app.show_main_window()
    sys.exit(app.exec())


================================================
FILE: buzz/cache.py
================================================
import json
import logging
import os
import pickle
from typing import List

from platformdirs import user_cache_dir

from buzz.transcriber.transcriber import FileTranscriptionTask


class TasksCache:
    def __init__(self, cache_dir=user_cache_dir("Buzz")):
        os.makedirs(cache_dir, exist_ok=True)
        self.cache_dir = cache_dir
        self.pickle_cache_file_path = os.path.join(cache_dir, "tasks")
        self.tasks_list_file_path = os.path.join(cache_dir, "tasks.json")

    def save(self, tasks: List[FileTranscriptionTask]):
        self.save_json_tasks(tasks=tasks)

    def load(self) -> List[FileTranscriptionTask]:
        if os.path.exists(self.tasks_list_file_path):
            return self.load_json_tasks()

        try:
            with open(self.pickle_cache_file_path, "rb") as file:
                return pickle.load(file)
        except FileNotFoundError:
            return []
        except (
            pickle.UnpicklingError,
            AttributeError,
            ValueError,
        ):  # delete corrupted cache
            os.remove(self.pickle_cache_file_path)
            return []

    def load_json_tasks(self) -> List[FileTranscriptionTask]:
        task_ids: List[int]
        try:
            with open(self.tasks_list_file_path) as file:
                task_ids = json.load(file)
        except json.JSONDecodeError:
            logging.debug(
                "Got JSONDecodeError while reading tasks list file path, "
                "resetting cache..."
            )
            task_ids = []

        tasks = []
        for task_id in task_ids:
            try:
                with open(self.get_task_path(task_id=task_id)) as file:
                    tasks.append(FileTranscriptionTask.from_json(file.read()))
            except (FileNotFoundError, json.JSONDecodeError):
                pass

        return tasks

    def save_json_tasks(self, tasks: List[FileTranscriptionTask]):
        json_str = json.dumps([task.id for task in tasks])
        with open(self.tasks_list_file_path, "w") as file:
            file.write(json_str)

        for task in tasks:
            file_path = self.get_task_path(task_id=task.id)
            json_str = task.to_json()
            with open(file_path, "w") as file:
                file.write(json_str)

    def get_task_path(self, task_id: int):
        path = os.path.join(self.cache_dir, "transcriptions", f"{task_id}.json")
        os.makedirs(os.path.dirname(path), exist_ok=True)
        return path

    def clear(self):
        if os.path.exists(self.pickle_cache_file_path):
            os.remove(self.pickle_cache_file_path)


================================================
FILE: buzz/cli.py
================================================
import enum
import sys
import typing
import urllib.parse

from PyQt6.QtCore import QCommandLineParser, QCommandLineOption

from buzz.model_loader import (
    ModelType,
    WhisperModelSize,
    TranscriptionModel,
    ModelDownloader,
)
from buzz.store.keyring_store import get_password, Key
from buzz.transcriber.transcriber import (
    Task,
    FileTranscriptionTask,
    FileTranscriptionOptions,
    TranscriptionOptions,
    LANGUAGES,
    OutputFormat,
)
from buzz.widgets.application import Application


class CommandLineError(Exception):
    def __init__(self, message: str):
        super().__init__(message)


class CommandLineModelType(enum.Enum):
    WHISPER = "whisper"
    WHISPER_CPP = "whispercpp"
    HUGGING_FACE = "huggingface"
    FASTER_WHISPER = "fasterwhisper"
    OPEN_AI_WHISPER_API = "openaiapi"


def parse_command_line(app: Application):
    parser = QCommandLineParser()
    try:
        parse(app, parser)
    except CommandLineError as exc:
        print(f"Error: {str(exc)}\n", file=sys.stderr)
        print(parser.helpText())
        sys.exit(1)

def is_url(path: str) -> bool:
    parsed = urllib.parse.urlparse(path)
    return all([parsed.scheme, parsed.netloc])

def parse(app: Application, parser: QCommandLineParser):
    parser.addPositionalArgument("<command>", "One of the following commands:\n- add")
    parser.parse(app.arguments())

    args = parser.positionalArguments()
    if len(args) == 0:
        parser.addHelpOption()
        parser.addVersionOption()

        parser.process(app)
        return

    command = args[0]
    if command == "add":
        parser.clearPositionalArguments()

        parser.addPositionalArgument("files", "Input file paths", "[file file file...]")

        task_option = QCommandLineOption(
            ["t", "task"],
            f"The task to perform. Allowed: {join_values(Task)}. Default: {Task.TRANSCRIBE.value}.",
            "task",
            Task.TRANSCRIBE.value,
        )
        model_type_option = QCommandLineOption(
            ["m", "model-type"],
            f"Model type. Allowed: {join_values(CommandLineModelType)}. Default: {CommandLineModelType.WHISPER.value}.",
            "model-type",
            CommandLineModelType.WHISPER.value,
        )
        model_size_option = QCommandLineOption(
            ["s", "model-size"],
            f"Model size. Use only when --model-type is whisper, whispercpp, or fasterwhisper. Allowed: {join_values(WhisperModelSize)}. Default: {WhisperModelSize.TINY.value}.",
            "model-size",
            WhisperModelSize.TINY.value,
        )
        hugging_face_model_id_option = QCommandLineOption(
            ["hfid"],
            'Hugging Face model ID. Use only when --model-type is huggingface. Example: "openai/whisper-tiny"',
            "id",
        )
        language_option = QCommandLineOption(
            ["l", "language"],
            f'Language code. Allowed: {", ".join(sorted([k + " (" + LANGUAGES[k].title() + ")" for k in LANGUAGES]))}. Leave empty to detect language.',
            "code",
            "",
        )
        initial_prompt_option = QCommandLineOption(
            ["p", "prompt"], "Initial prompt.", "prompt", ""
        )
        word_timestamp_option = QCommandLineOption(
            ["w", "word-timestamps"], "Generate word-level timestamps."
        )
        extract_speech_option = QCommandLineOption(
            ["e", "extract-speech"], "Extract speech from audio before transcribing."
        )
        open_ai_access_token_option = QCommandLineOption(
            "openai-token",
            f"OpenAI access token. Use only when --model-type is {CommandLineModelType.OPEN_AI_WHISPER_API.value}. Defaults to your previously saved access token, if one exists.",
            "token",
        )
        output_directory_option = QCommandLineOption(
            ["d", "output-directory"], "Output directory", "directory"
        )
        srt_option = QCommandLineOption(["srt"], "Output result in an SRT file.")
        vtt_option = QCommandLineOption(["vtt"], "Output result in a VTT file.")
        txt_option = QCommandLineOption("txt", "Output result in a TXT file.")
        hide_gui_option = QCommandLineOption("hide-gui", "Hide the main application window.")

        parser.addOptions(
            [
                task_option,
                model_type_option,
                model_size_option,
                hugging_face_model_id_option,
                language_option,
                initial_prompt_option,
                word_timestamp_option,
                extract_speech_option,
                open_ai_access_token_option,
                output_directory_option,
                srt_option,
                vtt_option,
                txt_option,
                hide_gui_option,
            ]
        )

        parser.addHelpOption()
        parser.addVersionOption()

        parser.process(app)

        # slice after first argument, the command
        file_paths = parser.positionalArguments()[1:]
        if len(file_paths) == 0:
            raise CommandLineError("No input files")

        task = parse_enum_option(task_option, parser, Task)

        model_type = parse_enum_option(model_type_option, parser, CommandLineModelType)
        model_size = parse_enum_option(model_size_option, parser, WhisperModelSize)

        hugging_face_model_id = parser.value(hugging_face_model_id_option)

        if (
            hugging_face_model_id == ""
            and model_type == CommandLineModelType.HUGGING_FACE
        ):
            raise CommandLineError(
                "--hfid is required when --model-type is huggingface"
            )

        model = TranscriptionModel(
            model_type=ModelType[model_type.name],
            whisper_model_size=model_size,
            hugging_face_model_id=hugging_face_model_id,
        )
        ModelDownloader(model=model).run()
        model_path = model.get_local_model_path()

        if model_path is None:
            raise CommandLineError("Model not found")

        language = parser.value(language_option)
        if language == "":
            language = None
        elif LANGUAGES.get(language) is None:
            raise CommandLineError("Invalid language option")

        initial_prompt = parser.value(initial_prompt_option)

        word_timestamps = parser.isSet(word_timestamp_option)
        extract_speech = parser.isSet(extract_speech_option)

        output_formats: typing.Set[OutputFormat] = set()
        if parser.isSet(srt_option):
            output_formats.add(OutputFormat.SRT)
        if parser.isSet(vtt_option):
            output_formats.add(OutputFormat.VTT)
        if parser.isSet(txt_option):
            output_formats.add(OutputFormat.TXT)

        openai_access_token = parser.value(open_ai_access_token_option)
        if (
            model.model_type == ModelType.OPEN_AI_WHISPER_API
            and openai_access_token == ""
        ):
            openai_access_token = get_password(key=Key.OPENAI_API_KEY)

            if openai_access_token == "":
                raise CommandLineError("No OpenAI access token found")

        output_directory = parser.value(output_directory_option)

        transcription_options = TranscriptionOptions(
            model=model,
            task=task,
            language=language,
            initial_prompt=initial_prompt,
            word_level_timings=word_timestamps,
            extract_speech=extract_speech,
            openai_access_token=openai_access_token,
        )

        for file_path in file_paths:
            path_is_url = is_url(file_path)

            file_transcription_options = FileTranscriptionOptions(
                file_paths=[file_path] if not path_is_url else None,
                url=file_path if path_is_url else None,
                output_formats=output_formats,
            )

            transcription_task = FileTranscriptionTask(
                file_path=file_path if not path_is_url else None,
                url=file_path if path_is_url else None,
                source=FileTranscriptionTask.Source.FILE_IMPORT if not path_is_url else FileTranscriptionTask.Source.URL_IMPORT,
                model_path=model_path,
                transcription_options=transcription_options,
                file_transcription_options=file_transcription_options,
                output_directory=output_directory if output_directory != "" else None,
            )
            app.add_task(transcription_task, quit_on_complete=True)

        if parser.isSet(hide_gui_option):
            app.hide_main_window = True

T = typing.TypeVar("T", bound=enum.Enum)


def parse_enum_option(
    option: QCommandLineOption, parser: QCommandLineParser, enum_class: typing.Type[T]
) -> T:
    try:
        return enum_class(parser.value(option))
    except ValueError:
        raise CommandLineError(f"Invalid value for --{option.names()[-1]} option.")


def join_values(enum_class: typing.Type[enum.Enum]) -> str:
    return ", ".join([v.value for v in enum_class])


================================================
FILE: buzz/conn.py
================================================
import sys
from contextlib import contextmanager
from multiprocessing.connection import Connection


class ConnWriter:
    def __init__(self, conn: Connection):
        self.conn = conn

    def write(self, s: str):
        self.conn.send(s.strip())


@contextmanager
def pipe_stderr(conn: Connection):
    sys.stderr = ConnWriter(conn)

    try:
        yield
    finally:
        sys.stderr = sys.__stderr__


================================================
FILE: buzz/cuda_setup.py
================================================
"""
CUDA library path setup for nvidia packages installed via pip.

This module must be imported BEFORE any torch or CUDA-dependent libraries are imported.
It handles locating and loading CUDA libraries (cuDNN, cuBLAS, etc.) from the nvidia
pip packages.

On Windows: Uses os.add_dll_directory() to add library paths
On Linux: Uses ctypes to preload libraries (LD_LIBRARY_PATH is read at process start)
On macOS: No action needed (CUDA not supported)
"""

import ctypes
import logging
import os
import platform
import sys
from pathlib import Path


logger = logging.getLogger(__name__)


def _get_nvidia_package_lib_dirs() -> list[Path]:
    """Find all nvidia package library directories in site-packages."""
    lib_dirs = []

    # Find site-packages directories
    site_packages_dirs = []
    for path in sys.path:
        if "site-packages" in path:
            site_packages_dirs.append(Path(path))

    # Also check relative to the current module for frozen apps
    if getattr(sys, "frozen", False):
        # For frozen apps, check the _internal directory
        frozen_lib_dir = Path(sys._MEIPASS) if hasattr(sys, "_MEIPASS") else Path(sys.executable).parent
        nvidia_dir = frozen_lib_dir / "nvidia"
        if nvidia_dir.exists():
            for pkg_dir in nvidia_dir.iterdir():
                if pkg_dir.is_dir():
                    lib_subdir = pkg_dir / "lib"
                    if lib_subdir.exists():
                        lib_dirs.append(lib_subdir)
                    # Some packages have bin directory on Windows
                    bin_subdir = pkg_dir / "bin"
                    if bin_subdir.exists():
                        lib_dirs.append(bin_subdir)

    # Check each site-packages for nvidia packages
    for sp_dir in site_packages_dirs:
        nvidia_dir = sp_dir / "nvidia"
        if nvidia_dir.exists():
            for pkg_dir in nvidia_dir.iterdir():
                if pkg_dir.is_dir():
                    lib_subdir = pkg_dir / "lib"
                    if lib_subdir.exists():
                        lib_dirs.append(lib_subdir)
                    # Some packages have bin directory on Windows
                    bin_subdir = pkg_dir / "bin"
                    if bin_subdir.exists():
                        lib_dirs.append(bin_subdir)

    return lib_dirs


def _setup_windows_dll_directories():
    """Add nvidia library directories to Windows DLL search path."""
    lib_dirs = _get_nvidia_package_lib_dirs()
    for lib_dir in lib_dirs:
        try:
            os.add_dll_directory(str(lib_dir))
        except (OSError, AttributeError) as e:
            pass


def _preload_linux_libraries():
    """Preload CUDA libraries on Linux using ctypes.

    On Linux, LD_LIBRARY_PATH is only read at process start, so we need to
    manually load the libraries using ctypes before torch tries to load them.
    """
    lib_dirs = _get_nvidia_package_lib_dirs()

    # Libraries to skip - NVBLAS requires special configuration and causes issues
    skip_patterns = ["libnvblas"]

    loaded_libs = set()

    for lib_dir in lib_dirs:
        if not lib_dir.exists():
            continue

        # Find all .so files in the directory
        for lib_file in sorted(lib_dir.glob("*.so*")):
            if lib_file.name in loaded_libs:
                continue
            if lib_file.is_symlink() and not lib_file.exists():
                continue

            # Skip problematic libraries
            if any(pattern in lib_file.name for pattern in skip_patterns):
                continue

            try:
                # Use RTLD_GLOBAL so symbols are available to other libraries
                ctypes.CDLL(str(lib_file), mode=ctypes.RTLD_GLOBAL)
                loaded_libs.add(lib_file.name)
            except OSError as e:
                # Some libraries may have missing dependencies, that's ok
                pass


def setup_cuda_libraries():
    """Set up CUDA library paths for the current platform.

    This function should be called as early as possible, before any torch
    or CUDA-dependent libraries are imported.
    """
    system = platform.system()

    if system == "Windows":
        _setup_windows_dll_directories()
    elif system == "Linux":
        _preload_linux_libraries()
    # macOS doesn't have CUDA support, so nothing to do


# Auto-run setup when this module is imported
setup_cuda_libraries()


================================================
FILE: buzz/db/__init__.py
================================================


================================================
FILE: buzz/db/dao/__init__.py
================================================


================================================
FILE: buzz/db/dao/dao.py
================================================
# Adapted from https://github.com/zhiyiYo/Groove
from abc import ABC
from typing import TypeVar, Generic, Any, Type, List

from PyQt6.QtSql import QSqlDatabase, QSqlQuery, QSqlRecord

from buzz.db.entity.entity import Entity

T = TypeVar("T", bound=Entity)


class DAO(ABC, Generic[T]):
    entity: Type[T]
    ignore_fields = []

    def __init__(self, table: str, db: QSqlDatabase):
        self.db = db
        self.table = table

    def insert(self, record: T):
        query = self._create_query()
        fields = [
            field for field in record.__dict__.keys() if field not in self.ignore_fields
        ]
        query.prepare(
            f"""
            INSERT INTO {self.table} ({", ".join(fields)})
            VALUES ({", ".join([f":{key}" for key in fields])})
        """
        )
        for field in fields:
            query.bindValue(f":{field}", getattr(record, field))

        if not query.exec():
            raise Exception(query.lastError().text())

    def find_by_id(self, id: Any) -> T | None:
        query = self._create_query()
        query.prepare(f"SELECT * FROM {self.table} WHERE id = :id")
        query.bindValue(":id", id)
        return self._execute(query)

    def to_entity(self, record: QSqlRecord) -> T:
        kwargs = {record.fieldName(i): record.value(i) for i in range(record.count())}
        return self.entity(**kwargs)

    def _execute(self, query: QSqlQuery) -> T | None:
        if not query.exec():
            raise Exception(query.lastError().text())
        if not query.first():
            return None
        return self.to_entity(query.record())

    def _execute_all(self, query: QSqlQuery) -> List[T]:
        if not query.exec():
            raise Exception(query.lastError().text())
        entities = []
        while query.next():
            entities.append(self.to_entity(query.record()))
        return entities

    def _create_query(self):
        return QSqlQuery(self.db)


================================================
FILE: buzz/db/dao/transcription_dao.py
================================================
import uuid
from datetime import datetime
from uuid import UUID

from PyQt6.QtSql import QSqlDatabase

from buzz.db.dao.dao import DAO
from buzz.db.entity.transcription import Transcription
from buzz.transcriber.transcriber import FileTranscriptionTask


class TranscriptionDAO(DAO[Transcription]):
    entity = Transcription

    def __init__(self, db: QSqlDatabase):
        super().__init__("transcription", db)

    def create_transcription(self, task: FileTranscriptionTask):
        query = self._create_query()
        query.prepare(
            """
            INSERT INTO transcription (
                id,
                export_formats,
                file,
                output_folder,
                language,
                model_type,
                source,
                status,
                task,
                time_queued,
                url,
                whisper_model_size,
                hugging_face_model_id,
                word_level_timings,
                extract_speech,
                name,
                notes
            ) VALUES (
                :id,
                :export_formats,
                :file,
                :output_folder,
                :language,
                :model_type,
                :source,
                :status,
                :task,
                :time_queued,
                :url,
                :whisper_model_size,
                :hugging_face_model_id,
                :word_level_timings,
                :extract_speech,
                :name,
                :notes
            )
            """
        )
        query.bindValue(":id", str(task.uid))
        query.bindValue(
            ":export_formats",
            ", ".join(
                [
                    output_format.value
                    for output_format in task.file_transcription_options.output_formats
                ]
            ),
        )
        query.bindValue(":file", task.file_path)
        query.bindValue(":output_folder", task.output_directory)
        query.bindValue(":language", task.transcription_options.language)
        query.bindValue(
            ":model_type", task.transcription_options.model.model_type.value
        )
        query.bindValue(":source", task.source.value)
        query.bindValue(":status", FileTranscriptionTask.Status.QUEUED.value)
        query.bindValue(":task", task.transcription_options.task.value)
        query.bindValue(":time_queued", datetime.now().isoformat())
        query.bindValue(":url", task.url)
        query.bindValue(
            ":whisper_model_size",
            task.transcription_options.model.whisper_model_size.value
            if task.transcription_options.model.whisper_model_size
            else None,
        )
        query.bindValue(
            ":hugging_face_model_id",
            task.transcription_options.model.hugging_face_model_id
            if task.transcription_options.model.hugging_face_model_id
            else None,
        )
        query.bindValue(
            ":word_level_timings",
            task.transcription_options.word_level_timings
        )
        query.bindValue(
            ":extract_speech",
            task.transcription_options.extract_speech
        )
        query.bindValue(":name", None)  # name is not available in FileTranscriptionTask
        query.bindValue(":notes", None)  # notes is not available in FileTranscriptionTask
        if not query.exec():
            raise Exception(query.lastError().text())

    def copy_transcription(self, id: UUID) -> UUID:
        query = self._create_query()
        query.prepare("SELECT * FROM transcription WHERE id = :id")
        query.bindValue(":id", str(id))
        if not query.exec():
            raise Exception(query.lastError().text())
        if not query.next():
            raise Exception("Transcription not found")

        transcription_data = {field.name: query.value(field.name) for field in
                              self.entity.__dataclass_fields__.values()}

        new_id = uuid.uuid4()
        transcription_data["id"] = str(new_id)
        transcription_data["time_queued"] = datetime.now().isoformat()
        transcription_data["status"] = FileTranscriptionTask.Status.QUEUED.value

        query.prepare(
            """
            INSERT INTO transcription (
                id,
                export_formats,
                file,
                output_folder,
                language,
                model_type,
                source,
                status,
                task,
                time_queued,
                url,
                whisper_model_size,
                hugging_face_model_id,
                word_level_timings,
                extract_speech,
                name,
                notes
            ) VALUES (
                :id,
                :export_formats,
                :file,
                :output_folder,
                :language,
                :model_type,
                :source,
                :status,
                :task,
                :time_queued,
                :url,
                :whisper_model_size,
                :hugging_face_model_id,
                :word_level_timings,
                :extract_speech,
                :name,
                :notes
            )
            """
        )
        for key, value in transcription_data.items():
            query.bindValue(f":{key}", value)
        if not query.exec():
            raise Exception(query.lastError().text())

        return new_id

    def update_transcription_as_started(self, id: UUID):
        query = self._create_query()
        query.prepare(
            """
            UPDATE transcription
            SET status = :status, time_started = :time_started
            WHERE id = :id
        """
        )

        query.bindValue(":id", str(id))
        query.bindValue(":status", FileTranscriptionTask.Status.IN_PROGRESS.value)
        query.bindValue(":time_started", datetime.now().isoformat())
        if not query.exec():
            raise Exception(query.lastError().text())

    def update_transcription_as_failed(self, id: UUID, error: str):
        query = self._create_query()
        query.prepare(
            """
            UPDATE transcription
            SET status = :status, time_ended = :time_ended, error_message = :error_message
            WHERE id = :id
        """
        )

        query.bindValue(":id", str(id))
        query.bindValue(":status", FileTranscriptionTask.Status.FAILED.value)
        query.bindValue(":time_ended", datetime.now().isoformat())
        query.bindValue(":error_message", error)
        if not query.exec():
            raise Exception(query.lastError().text())

    def update_transcription_as_canceled(self, id: UUID):
        query = self._create_query()
        query.prepare(
            """
            UPDATE transcription
            SET status = :status, time_ended = :time_ended
            WHERE id = :id
        """
        )

        query.bindValue(":id", str(id))
        query.bindValue(":status", FileTranscriptionTask.Status.CANCELED.value)
        query.bindValue(":time_ended", datetime.now().isoformat())
        if not query.exec():
            raise Exception(query.lastError().text())

    def update_transcription_progress(self, id: UUID, progress: float):
        query = self._create_query()
        query.prepare(
            """
            UPDATE transcription
            SET status = :status, progress = :progress
            WHERE id = :id
        """
        )

        query.bindValue(":id", str(id))
        query.bindValue(":status", FileTranscriptionTask.Status.IN_PROGRESS.value)
        query.bindValue(":progress", progress)
        if not query.exec():
            raise Exception(query.lastError().text())

    def update_transcription_as_completed(self, id: UUID):
        query = self._create_query()
        query.prepare(
            """
            UPDATE transcription
            SET status = :status, time_ended = :time_ended
            WHERE id = :id
        """
        )

        query.bindValue(":id", str(id))
        query.bindValue(":status", FileTranscriptionTask.Status.COMPLETED.value)
        query.bindValue(":time_ended", datetime.now().isoformat())
        if not query.exec():
            raise Exception(query.lastError().text())

    def update_transcription_file_and_name(self, id: UUID, file_path: str, name: str | None = None):
        query = self._create_query()
        query.prepare(
            """
            UPDATE transcription
            SET file = :file, name = COALESCE(:name, name)
            WHERE id = :id
        """
        )

        query.bindValue(":id", str(id))
        query.bindValue(":file", file_path)
        query.bindValue(":name", name)
        if not query.exec():
            raise Exception(query.lastError().text())

    def update_transcription_name(self, id: UUID, name: str):
        query = self._create_query()
        query.prepare(
            """
            UPDATE transcription
            SET name = :name
            WHERE id = :id
        """
        )

        query.bindValue(":id", str(id))
        query.bindValue(":name", name)
        if not query.exec():
            raise Exception(query.lastError().text())
        if query.numRowsAffected() == 0:
            raise Exception("Transcription not found")

    def update_transcription_notes(self, id: UUID, notes: str):
        query = self._create_query()
        query.prepare(
            """
            UPDATE transcription
            SET notes = :notes
            WHERE id = :id
        """
        )

        query.bindValue(":id", str(id))
        query.bindValue(":notes", notes)
        if not query.exec():
            raise Exception(query.lastError().text())
        if query.numRowsAffected() == 0:
            raise Exception("Transcription not found")

    def reset_transcription_for_restart(self, id: UUID):
        """Reset a transcription to queued status for restart"""
        query = self._create_query()
        query.prepare(
            """
            UPDATE transcription
            SET status = :status, progress = :progress, time_started = NULL, time_ended = NULL, error_message = NULL
            WHERE id = :id
        """
        )

        query.bindValue(":id", str(id))
        query.bindValue(":status", FileTranscriptionTask.Status.QUEUED.value)
        query.bindValue(":progress", 0.0)
        if not query.exec():
            raise Exception(query.lastError().text())
        if query.numRowsAffected() == 0:
            raise Exception("Transcription not found")


================================================
FILE: buzz/db/dao/transcription_segment_dao.py
================================================
from typing import List
from uuid import UUID

from PyQt6.QtSql import QSqlDatabase

from buzz.db.dao.dao import DAO
from buzz.db.entity.transcription_segment import TranscriptionSegment


class TranscriptionSegmentDAO(DAO[TranscriptionSegment]):
    entity = TranscriptionSegment
    ignore_fields = ["id"]

    def __init__(self, db: QSqlDatabase):
        super().__init__("transcription_segment", db)

    def get_segments(self, transcription_id: UUID) -> List[TranscriptionSegment]:
        query = self._create_query()
        query.prepare(
            f"""
            SELECT * FROM {self.table}
            WHERE transcription_id = :transcription_id
        """
        )
        query.bindValue(":transcription_id", str(transcription_id))
        return self._execute_all(query)

    def delete_segments(self, transcription_id: UUID):
        query = self._create_query()
        query.prepare(
            f"""
            DELETE FROM {self.table}
            WHERE transcription_id = :transcription_id
        """
        )
        query.bindValue(":transcription_id", str(transcription_id))
        if not query.exec():
            raise Exception(query.lastError().text())

    def update_segment_translation(self, segment_id: int, translation: str):
        query = self._create_query()
        query.prepare(
            """
            UPDATE transcription_segment
            SET translation = :translation
            WHERE id = :id
        """
        )

        query.bindValue(":id", segment_id)
        query.bindValue(":translation", translation)
        if not query.exec():
            raise Exception(query.lastError().text())


================================================
FILE: buzz/db/db.py
================================================
import logging
import os
import sqlite3
import tempfile

from PyQt6.QtSql import QSqlDatabase
from platformdirs import user_data_dir

from buzz.db.helpers import (
    run_sqlite_migrations,
    copy_transcriptions_from_json_to_sqlite,
    mark_in_progress_and_queued_transcriptions_as_canceled,
)


def setup_app_db() -> QSqlDatabase:
    data_dir = user_data_dir("Buzz")
    os.makedirs(data_dir, exist_ok=True)
    return _setup_db(os.path.join(data_dir, "Buzz.sqlite"))


def setup_test_db() -> QSqlDatabase:
    return _setup_db(tempfile.mktemp())


def _setup_db(path: str) -> QSqlDatabase:
    # Run migrations
    db = sqlite3.connect(path, isolation_level=None, timeout=10.0)
    try:
        run_sqlite_migrations(db)
        copy_transcriptions_from_json_to_sqlite(db)
        mark_in_progress_and_queued_transcriptions_as_canceled(db)
        db.commit()
    finally:
        db.close()

    db = QSqlDatabase.addDatabase("QSQLITE")
    db.setDatabaseName(path)
    if not db.open():
        raise RuntimeError(f"Failed to open database connection: {db.databaseName()}")
    db.exec('PRAGMA foreign_keys = ON')
    logging.debug("Database connection opened: %s", db.databaseName())
    return db


def close_app_db():
    db = QSqlDatabase.database()
    if not db.isValid():
        return

    if db.isOpen():
        db.close()

================================================
FILE: buzz/db/entity/__init__.py
================================================


================================================
FILE: buzz/db/entity/entity.py
================================================
from abc import ABC

from PyQt6.QtSql import QSqlRecord


class Entity(ABC):
    @classmethod
    def from_record(cls, record: QSqlRecord):
        entity = cls()
        for i in range(record.count()):
            setattr(entity, record.fieldName(i), record.value(i))
        return entity


================================================
FILE: buzz/db/entity/transcription.py
================================================
import datetime
import os
import uuid
from dataclasses import dataclass, field

from buzz.db.entity.entity import Entity
from buzz.model_loader import ModelType
from buzz.settings.settings import Settings
from buzz.transcriber.transcriber import OutputFormat, Task, FileTranscriptionTask


@dataclass
class Transcription(Entity):
    status: str = FileTranscriptionTask.Status.QUEUED.value
    task: str = Task.TRANSCRIBE.value
    model_type: str = ModelType.WHISPER.value
    whisper_model_size: str | None = None
    hugging_face_model_id: str | None = None
    word_level_timings: str | None = None
    extract_speech: str | None = None
    language: str | None = None
    id: str = field(default_factory=lambda: str(uuid.uuid4()))
    error_message: str | None = None
    file: str | None = None
    time_queued: str = datetime.datetime.now().isoformat()
    progress: float = 0.0
    time_ended: str | None = None
    time_started: str | None = None
    export_formats: str | None = None
    output_folder: str | None = None
    source: str | None = None
    url: str | None = None
    name: str | None = None
    notes: str | None = None

    @property
    def id_as_uuid(self):
        return uuid.UUID(hex=self.id)

    @property
    def status_as_status(self):
        return FileTranscriptionTask.Status(self.status)

    def get_output_file_path(
        self,
        output_format: OutputFormat,
        output_directory: str | None = None,
    ):
        input_file_name = os.path.splitext(os.path.basename(self.file))[0]

        date_time_now = datetime.datetime.now().strftime("%d-%b-%Y %H-%M-%S")

        export_file_name_template = Settings().get_default_export_file_template()

        output_file_name = (
            export_file_name_template.replace("{{ input_file_name }}", input_file_name)
            .replace("{{ task }}", self.task)
            .replace("{{ language }}", self.language or "")
            .replace("{{ model_type }}", self.model_type)
            .replace("{{ model_size }}", self.whisper_model_size or "")
            .replace("{{ date_time }}", date_time_now)
            + f".{output_format.value}"
        )

        output_directory = output_directory or os.path.dirname(self.file)
        return os.path.join(output_directory, output_file_name)


================================================
FILE: buzz/db/entity/transcription_segment.py
================================================
from dataclasses import dataclass

from buzz.db.entity.entity import Entity


@dataclass
class TranscriptionSegment(Entity):
    start_time: int
    end_time: int
    text: str
    translation: str
    transcription_id: str
    id: int = -1


================================================
FILE: buzz/db/helpers.py
================================================
import os
from datetime import datetime
from sqlite3 import Connection

from buzz.assets import get_path
from buzz.cache import TasksCache
from buzz.db.migrator import dumb_migrate_db


def copy_transcriptions_from_json_to_sqlite(conn: Connection):
    cache = TasksCache()
    if os.path.exists(cache.tasks_list_file_path):
        tasks = cache.load()
        cursor = conn.cursor()
        for task in tasks:
            cursor.execute(
                """
                INSERT INTO transcription (id, error_message, export_formats, file, output_folder, progress, language, model_type, source, status, task, time_ended, time_queued, time_started, url, whisper_model_size, hugging_face_model_id)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, COALESCE(?, ?), ?, ?, ?, ?)
                RETURNING id;
                """,
                (
                    str(task.uid),
                    task.error,
                    ", ".join(
                        [
                            format.value
                            for format in task.file_transcription_options.output_formats
                        ]
                    ),
                    task.file_path,
                    task.output_directory,
                    task.fraction_completed,
                    task.transcription_options.language,
                    task.transcription_options.model.model_type.value,
                    task.source.value,
                    task.status.value,
                    task.transcription_options.task.value,
                    task.completed_at,
                    task.queued_at, datetime.now().isoformat(),
                    task.started_at,
                    task.url,
                    task.transcription_options.model.whisper_model_size.value
                    if task.transcription_options.model.whisper_model_size
                    else None,
                    task.transcription_options.model.hugging_face_model_id
                    if task.transcription_options.model.hugging_face_model_id
                    else None,
                ),
            )
            transcription_id = cursor.fetchone()[0]

            for segment in task.segments:
                cursor.execute(
                    """
                    INSERT INTO transcription_segment (end_time, start_time, text, translation, transcription_id)
                    VALUES (?, ?, ?, ?, ?);
                    """,
                    (
                        segment.end,
                        segment.start,
                        segment.text,
                        segment.translation,
                        transcription_id,
                    ),
                )
        # os.remove(cache.tasks_list_file_path)
        conn.commit()


def run_sqlite_migrations(db: Connection):
    schema_path = get_path("schema.sql")

    with open(schema_path) as schema_file:
        schema = schema_file.read()
        dumb_migrate_db(db=db, schema=schema)


def mark_in_progress_and_queued_transcriptions_as_canceled(conn: Connection):
    cursor = conn.cursor()
    cursor.execute(
        """
        UPDATE transcription
        SET status = 'canceled', time_ended = ?
        WHERE status = 'in_progress' OR status = 'queued';
        """,
        (datetime.now().isoformat(),),
    )
    conn.commit()


================================================
FILE: buzz/db/migrator.py
================================================
# coding: utf-8
# https://gist.github.com/simonw/664b4b0851c1899dc55e1fb655181037

"""Simple declarative schema migration for SQLite.
See <https://david.rothlis.net/declarative-schema-migration-for-sqlite>.
Author: William Manley <will@stb-tester.com>.
Copyright © 2019-2022 Stb-tester.com Ltd.
License: MIT.
"""

import logging
import re
import sqlite3
from textwrap import dedent


def dumb_migrate_db(db, schema, allow_deletions=False):
    """
    Migrates a database to the new schema given by the SQL text `schema`
    preserving the data.  We create any table that exists in schema, delete any
    old table that is no longer used and add/remove columns and indices as
    necessary.
    Under this scheme there are a set of changes that we can make to the schema
    and this script will handle it fine:
    1. Adding a new table
    2. Adding, deleting or modifying an index
    3. Adding a column to an existing table as long as the new column can be
       NULL or has a DEFAULT value specified.
    4. Changing a column to remove NULL or DEFAULT as long as all values in the
       database are not NULL
    5. Changing the type of a column
    6. Changing the user_version
    In addition this function is capable of:
    1. Deleting tables
    2. Deleting columns from tables
    But only if allow_deletions=True.  If the new schema requires a column/table
    to be deleted and allow_deletions=False this function will raise
    `RuntimeError`.
    Note: When this function is called a transaction must not be held open on
    db.  A transaction will be used internally.  If you wish to perform
    additional migration steps as part of a migration use DBMigrator directly.
    Any internally generated rowid columns by SQLite may change values by this
    migration.
    """
    with DBMigrator(db, schema, allow_deletions) as migrator:
        migrator.migrate()
    return bool(migrator.n_changes)


class DBMigrator:
    def __init__(self, db, schema, allow_deletions=False):
        self.db = db
        self.schema = schema
        self.allow_deletions = allow_deletions

        self.pristine = sqlite3.connect(":memory:")
        self.pristine.executescript(schema)
        self.n_changes = 0

        self.orig_foreign_keys = None

    def log_execute(self, msg, sql, args=None):
        # It's important to log any changes we're making to the database for
        # forensics later
        msg_tmpl = "Database migration: %s with SQL:\n%s"
        msg_argv = (msg, _left_pad(dedent(sql)))
        if args:
            msg_tmpl += " args = %r"
            msg_argv += (args,)
        else:
            args = []
        # Uncomment this to get debugging information
        # logging.info(msg_tmpl, *msg_argv)
        self.db.execute(sql, args)
        self.n_changes += 1

    def __enter__(self):
        self.orig_foreign_keys = self.db.execute("PRAGMA foreign_keys").fetchone()[0]
        if self.orig_foreign_keys:
            self.log_execute(
                "Disable foreign keys temporarily for migration",
                "PRAGMA foreign_keys = OFF",
            )
            # This doesn't count as a change because we'll undo it at the end
            self.n_changes = 0

        self.db.__enter__()
        self.db.execute("BEGIN")
        return self

    def __exit__(self, exc_type, exc_value, exc_tb):
        self.db.__exit__(exc_type, exc_value, exc_tb)
        if exc_value is None:
            # The SQLite docs say:
            #
            # > This pragma is a no-op within a transaction; foreign key
            # > constraint enforcement may only be enabled or disabled when
            # > there is no pending BEGIN or SAVEPOINT.
            old_changes = self.n_changes
            new_val = self._migrate_pragma("foreign_keys")
            if new_val == self.orig_foreign_keys:
                self.n_changes = old_changes

            # SQLite docs say:
            #
            # > A VACUUM will fail if there is an open transaction on the database
            # > connection that is attempting to run the VACUUM.
            if self.n_changes:
                self.db.execute("VACUUM")
        else:
            if self.orig_foreign_keys:
                self.log_execute(
                    "Re-enable foreign keys after migration", "PRAGMA foreign_keys = ON"
                )

    def migrate(self):
        # In CI the database schema may be changing all the time.  This checks
        # the current db and if it doesn't match database.sql we will
        # modify it so it does match where possible.
        pristine_tables = dict(
            self.pristine.execute(
                """\
            SELECT name, sql FROM sqlite_master
            WHERE type = \"table\" AND name != \"sqlite_sequence\""""
            ).fetchall()
        )
        pristine_indices = dict(
            self.pristine.execute(
                """\
            SELECT name, sql FROM sqlite_master
            WHERE type = \"index\""""
            ).fetchall()
        )

        tables = dict(
            self.db.execute(
                """\
            SELECT name, sql FROM sqlite_master
            WHERE type = \"table\" AND name != \"sqlite_sequence\""""
            ).fetchall()
        )

        new_tables = set(pristine_tables.keys()) - set(tables.keys())
        removed_tables = set(tables.keys()) - set(pristine_tables.keys())
        if removed_tables and not self.allow_deletions:
            raise RuntimeError(
                "Database migration: Refusing to delete tables %r" % removed_tables
            )

        modified_tables = set(
            name
            for name, sql in pristine_tables.items()
            if normalise_sql(tables.get(name, "")) != normalise_sql(sql)
        )

        # This PRAGMA is automatically disabled when the db is committed
        self.db.execute("PRAGMA defer_foreign_keys = TRUE")

        # New and removed tables are easy:
        for tbl_name in new_tables:
            self.log_execute("Create table %s" % tbl_name, pristine_tables[tbl_name])
        for tbl_name in removed_tables:
            self.log_execute("Drop table %s" % tbl_name, "DROP TABLE %s" % tbl_name)

        for tbl_name in modified_tables:
            # The SQLite documentation insists that we create the new table and
            # rename it over the old rather than moving the old out of the way
            # and then creating the new
            create_table_sql = pristine_tables[tbl_name]
            create_table_sql = re.sub(
                r"\b%s\b" % re.escape(tbl_name),
                tbl_name + "_migration_new",
                create_table_sql,
            )
            self.log_execute(
                "Columns change: Create table %s with updated schema" % tbl_name,
                create_table_sql,
            )

            cols = set(
                [x[1] for x in self.db.execute("PRAGMA table_info(%s)" % tbl_name)]
            )
            pristine_cols = set(
                [
                    x[1]
                    for x in self.pristine.execute("PRAGMA table_info(%s)" % tbl_name)
                ]
            )

            removed_columns = cols - pristine_cols
            if not self.allow_deletions and removed_columns:
                logging.warning(
                    "Database migration: Refusing to remove columns %r from "
                    "table %s.  Current cols are %r attempting migration to %r",
                    removed_columns,
                    tbl_name,
                    cols,
                    pristine_cols,
                )
                raise RuntimeError(
                    "Database migration: Refusing to remove columns %r from "
                    "table %s" % (removed_columns, tbl_name)
                )

            logging.info("cols: %s, pristine_cols: %s", cols, pristine_cols)
            self.log_execute(
                "Migrate data for table %s" % tbl_name,
                """\
                INSERT INTO {tbl_name}_migration_new ({common})
                SELECT {common} FROM {tbl_name}""".format(
                    tbl_name=tbl_name,
                    common=", ".join(cols.intersection(pristine_cols)),
                ),
            )

            # Don't need the old table any more
            self.log_execute(
                "Drop old table %s now data has been migrated" % tbl_name,
                "DROP TABLE %s" % tbl_name,
            )

            self.log_execute(
                "Columns change: Move new table %s over old" % tbl_name,
                "ALTER TABLE %s_migration_new RENAME TO %s" % (tbl_name, tbl_name),
            )

        # Migrate the indices
        indices = dict(
            self.db.execute(
                """\
            SELECT name, sql FROM sqlite_master
            WHERE type = \"index\""""
            ).fetchall()
        )
        for name in set(indices.keys()) - set(pristine_indices.keys()):
            self.log_execute(
                "Dropping obsolete index %s" % name, "DROP INDEX %s" % name
            )
        for name, sql in pristine_indices.items():
            if name not in indices:
                self.log_execute("Creating new index %s" % name, sql)
            elif sql != indices[name]:
                self.log_execute(
                    "Index %s changed: Dropping old version" % name,
                    "DROP INDEX %s" % name,
                )
                self.log_execute(
                    "Index %s changed: Creating updated version in its place" % name,
                    sql,
                )

        self._migrate_pragma("user_version")

        if self.pristine.execute("PRAGMA foreign_keys").fetchone()[0]:
            if self.db.execute("PRAGMA foreign_key_check").fetchall():
                raise RuntimeError("Database migration: Would fail foreign_key_check")

    def _migrate_pragma(self, pragma):
        pristine_val = self.pristine.execute("PRAGMA %s" % pragma).fetchone()[0]
        val = self.db.execute("PRAGMA %s" % pragma).fetchone()[0]

        if val != pristine_val:
            self.log_execute(
                "Set %s to %i from %i" % (pragma, pristine_val, val),
                "PRAGMA %s = %i" % (pragma, pristine_val),
            )

        return pristine_val


def _left_pad(text, indent="    "):
    """Maybe I can find a package in pypi for this?"""
    return "\n".join(indent + line for line in text.split("\n"))


def normalise_sql(sql):
    # Remove comments:
    sql = re.sub(r"--[^\n]*\n", "", sql)
    # Normalise whitespace:
    sql = re.sub(r"\s+", " ", sql)
    sql = re.sub(r" *([(),]) *", r"\1", sql)
    # Remove unnecessary quotes
    sql = re.sub(r'"(\w+)"', r"\1", sql)

    return sql.strip()


================================================
FILE: buzz/db/service/__init__.py
================================================


================================================
FILE: buzz/db/service/transcription_service.py
================================================
from typing import List
from uuid import UUID

from buzz.db.dao.transcription_dao import TranscriptionDAO
from buzz.db.dao.transcription_segment_dao import TranscriptionSegmentDAO
from buzz.db.entity.transcription_segment import TranscriptionSegment
from buzz.transcriber.transcriber import Segment


class TranscriptionService:
    def __init__(
        self,
        transcription_dao: TranscriptionDAO,
        transcription_segment_dao: TranscriptionSegmentDAO,
    ):
        self.transcription_dao = transcription_dao
        self.transcription_segment_dao = transcription_segment_dao

    def create_transcription(self, task):
        self.transcription_dao.create_transcription(task)

    def copy_transcription(self, id: UUID) -> UUID:
        return self.transcription_dao.copy_transcription(id)

    def update_transcription_as_started(self, id: UUID):
        self.transcription_dao.update_transcription_as_started(id)

    def update_transcription_as_failed(self, id: UUID, error: str):
        self.transcription_dao.update_transcription_as_failed(id, error)

    def update_transcription_as_canceled(self, id: UUID):
        self.transcription_dao.update_transcription_as_canceled(id)

    def update_transcription_progress(self, id: UUID, progress: float):
        self.transcription_dao.update_transcription_progress(id, progress)

    def update_transcription_as_completed(self, id: UUID, segments: List[Segment]):
        self.transcription_dao.update_transcription_as_completed(id)
        for segment in segments:
            self.transcription_segment_dao.insert(
                TranscriptionSegment(
                    start_time=segment.start,
                    end_time=segment.end,
                    text=segment.text,
                    translation='',
                    transcription_id=str(id),
                )
            )

    def update_transcription_file_and_name(self, id: UUID, file_path: str, name: str | None = None):
        self.transcription_dao.update_transcription_file_and_name(id, file_path, name)

    def update_transcription_name(self, id: UUID, name: str):
        self.transcription_dao.update_transcription_name(id, name)

    def update_transcription_notes(self, id: UUID, notes: str):
        self.transcription_dao.update_transcription_notes(id, notes)

    def reset_transcription_for_restart(self, id: UUID):
        self.transcription_dao.reset_transcription_for_restart(id)

    def replace_transcription_segments(self, id: UUID, segments: List[Segment]):
        self.transcription_segment_dao.delete_segments(id)
        for segment in segments:
            self.transcription_segment_dao.insert(
                TranscriptionSegment(
                    start_time=segment.start,
                    end_time=segment.end,
                    text=segment.text,
                    translation='',
                    transcription_id=str(id),
                )
            )

    def get_transcription_segments(self, transcription_id: UUID):
        return self.transcription_segment_dao.get_segments(transcription_id)

    def update_segment_translation(self, segment_id: int, translation: str):
        return self.transcription_segment_dao.update_segment_translation(segment_id, translation)


================================================
FILE: buzz/dialogs.py
================================================
from PyQt6.QtWidgets import QWidget, QMessageBox


def show_model_download_error_dialog(parent: QWidget, error: str):
    message = (
        parent.tr("An error occurred while loading the Whisper model")
        + f": {error}{'' if error.endswith('.') else '.'}"
        + parent.tr("Please retry or check the application logs for more information.")
    )

    QMessageBox.critical(parent, "", message)


================================================
FILE: buzz/file_transcriber_queue_worker.py
================================================
import logging
import multiprocessing
import os
import queue
import ssl
import sys
from pathlib import Path
from typing import Optional, Tuple, List, Set
from uuid import UUID

# Fix SSL certificate verification for bundled applications (macOS, Windows)
# This must be done before importing demucs which uses torch.hub with urllib
try:
    import certifi
    os.environ.setdefault('REQUESTS_CA_BUNDLE', certifi.where())
    os.environ.setdefault('SSL_CERT_FILE', certifi.where())
    os.environ.setdefault('SSL_CERT_DIR', os.path.dirname(certifi.where()))
    # Also update the default SSL context for urllib
    ssl._create_default_https_context = lambda: ssl.create_default_context(cafile=certifi.where())
except ImportError:
    pass

from PyQt6.QtCore import QObject, QThread, pyqtSignal, pyqtSlot, Qt

# Patch subprocess for demucs to prevent console windows on Windows
if sys.platform == "win32":
    import subprocess
    _original_run = subprocess.run
    _original_check_output = subprocess.check_output

    def _patched_run(*args, **kwargs):
        if 'startupinfo' not in kwargs:
            si = subprocess.STARTUPINFO()
            si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
            si.wShowWindow = subprocess.SW_HIDE
            kwargs['startupinfo'] = si
        if 'creationflags' not in kwargs:
            kwargs['creationflags'] = subprocess.CREATE_NO_WINDOW
        return _original_run(*args, **kwargs)

    def _patched_check_output(*args, **kwargs):
        if 'startupinfo' not in kwargs:
            si = subprocess.STARTUPINFO()
            si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
            si.wShowWindow = subprocess.SW_HIDE
            kwargs['startupinfo'] = si
        if 'creationflags' not in kwargs:
            kwargs['creationflags'] = subprocess.CREATE_NO_WINDOW
        return _original_check_output(*args, **kwargs)

    subprocess.run = _patched_run
    subprocess.check_output = _patched_check_output

from demucs import api as demucsApi

from buzz.locale import _
from buzz.model_loader import ModelType
from buzz.transcriber.file_transcriber import FileTranscriber
from buzz.transcriber.openai_whisper_api_file_transcriber import (
    OpenAIWhisperAPIFileTranscriber,
)
from buzz.transcriber.transcriber import FileTranscriptionTask, Segment
from buzz.transcriber.whisper_file_transcriber import WhisperFileTranscriber


class FileTranscriberQueueWorker(QObject):
    tasks_queue: multiprocessing.Queue
    current_task: Optional[FileTranscriptionTask] = None
    current_transcriber: Optional[FileTranscriber] = None
    current_transcriber_thread: Optional[QThread] = None

    task_started = pyqtSignal(FileTranscriptionTask)
    task_progress = pyqtSignal(FileTranscriptionTask, float)
    task_download_progress = pyqtSignal(FileTranscriptionTask, float)
    task_completed = pyqtSignal(FileTranscriptionTask, list)
    task_error = pyqtSignal(FileTranscriptionTask, str)

    completed = pyqtSignal()
    trigger_run = pyqtSignal()

    def __init__(self, parent: Optional[QObject] = None):
        super().__init__(parent)
        self.tasks_queue = queue.Queue()
        self.canceled_tasks: Set[UUID] = set()
        self.current_transcriber = None
        self.speech_path = None
        self.is_running = False
        # Use QueuedConnection to ensure run() is called in the correct thread context
        # and doesn't block signal handlers
        self.trigger_run.connect(self.run, Qt.ConnectionType.QueuedConnection)

    @pyqtSlot()
    def run(self):
        if self.is_running:
            return

        logging.debug("Waiting for next transcription task")

        # Clean up of previous run.
        if self.current_transcriber is not None:
            self.current_transcriber.stop()
            self.current_transcriber = None

        # Get next non-canceled task from queue
        while True:
            self.current_task: Optional[FileTranscriptionTask] = self.tasks_queue.get()

            # Stop listening when a "None" task is received
            if self.current_task is None:
                self.is_running = False
                self.completed.emit()
                return

            if self.current_task.uid in self.canceled_tasks:
                continue

            break

        # Set is_running AFTER we have a valid task to process
        self.is_running = True

        if self.current_task.transcription_options.extract_speech:
            logging.debug("Will extract speech")

            def separator_progress_callback(progress):
                self.task_progress.emit(self.current_task, int(progress["segment_offset"] * 100) / int(progress["audio_length"] * 100))

            separator = None
            separated = None
            try:
                # Force CPU if specified, otherwise use CUDA if available
                force_cpu = os.getenv("BUZZ_FORCE_CPU", "false").lower() == "true"
                if force_cpu:
                    device = "cpu"
                else:
                    import torch
                    device = "cuda" if torch.cuda.is_available() else "cpu"
                separator = demucsApi.Separator(
                    device=device,
                    progress=True,
                    callback=separator_progress_callback,
                )
                _origin, separated = separator.separate_audio_file(Path(self.current_task.file_path))

                task_file_path = Path(self.current_task.file_path)
                self.speech_path = task_file_path.with_name(f"{task_file_path.stem}_speech.mp3")
                demucsApi.save_audio(separated["vocals"], self.speech_path, separator.samplerate)

                self.current_task.file_path = str(self.speech_path)
            except Exception as e:
                logging.error(f"Error during speech extraction: {e}", exc_info=True)
                self.task_error.emit(
                    self.current_task,
                    _("Speech extraction failed! Check your internet connection — a model may need to be downloaded."),
                )
                self.is_running = False
                return
            finally:
                # Release memory used by speech extractor
                del separator, separated
                try:
                    import torch
                    if torch.cuda.is_available():
                        torch.cuda.empty_cache()
                except Exception:
                    pass

        logging.debug("Starting next transcription task")
        self.task_progress.emit(self.current_task, 0)

        model_type = self.current_task.transcription_options.model.model_type
        if model_type == ModelType.OPEN_AI_WHISPER_API:
            self.current_transcriber = OpenAIWhisperAPIFileTranscriber(
                task=self.current_task
            )
        elif (
            model_type == ModelType.WHISPER_CPP
            or model_type == ModelType.HUGGING_FACE
            or model_type == ModelType.WHISPER
            or model_type == ModelType.FASTER_WHISPER
        ):
            self.current_transcriber = WhisperFileTranscriber(task=self.current_task)
        else:
            raise Exception(f"Unknown model type: {model_type}")

        self.current_transcriber_thread = QThread(self)

        self.current_transcriber.moveToThread(self.current_transcriber_thread)

        self.current_transcriber_thread.started.connect(self.current_transcriber.run)
        self.current_transcriber.completed.connect(self.current_transcriber_thread.quit)
        self.current_transcriber.error.connect(self.current_transcriber_thread.quit)

        self.current_transcriber.completed.connect(self.current_transcriber.deleteLater)
        self.current_transcriber.error.connect(self.current_transcriber.deleteLater)
        self.current_transcriber_thread.finished.connect(
            self.current_transcriber_thread.deleteLater
        )

        self.current_transcriber.progress.connect(self.on_task_progress)
        self.current_transcriber.download_progress.connect(
            self.on_task_download_progress
        )
        self.current_transcriber.error.connect(self.on_task_error)

        self.current_transcriber.completed.connect(self.on_task_completed)

        # Wait for next item on the queue
        self.current_transcriber.error.connect(lambda: self._on_task_finished())
        self.current_transcriber.completed.connect(lambda: self._on_task_finished())

        self.task_started.emit(self.current_task)
        self.current_transcriber_thread.start()

    def _on_task_finished(self):
        """Called when a task completes or errors, resets state and triggers next run"""
        self.is_running = False
        # Use signal to avoid blocking in signal handler context
        self.trigger_run.emit()

    def add_task(self, task: FileTranscriptionTask):
        # Remove from canceled tasks if it was previously canceled (for restart functionality)
        if task.uid in self.canceled_tasks:
            self.canceled_tasks.remove(task.uid)

        self.tasks_queue.put(task)
        # If the worker is not currently running, trigger it to start processing
        # Use signal to avoid blocking the main thread
        if not self.is_running:
            self.trigger_run.emit()

    def cancel_task(self, task_id: UUID):
        self.canceled_tasks.add(task_id)

        if self.current_task is not None and self.current_task.uid == task_id:
            if self.current_transcriber is not None:
                self.current_transcriber.stop()
                
            if self.current_transcriber_thread is not None:
                if not self.current_transcriber_thread.wait(5000):
                    logging.warning("Transcriber thread did not terminate gracefully")
                    self.current_transcriber_thread.terminate()

    def on_task_error(self, error: str):
        if (
            self.current_task is not None
            and self.current_task.uid not in self.canceled_tasks
        ):
            # Check if the error indicates cancellation
            if "canceled" in error.lower() or "cancelled" in error.lower():
                self.current_task.status = FileTranscriptionTask.Status.CANCELED
                self.current_task.error = error
            else:
                self.current_task.status = FileTranscriptionTask.Status.FAILED
                self.current_task.error = error
            self.task_error.emit(self.current_task, error)

    @pyqtSlot(tuple)
    def on_task_progress(self, progress: Tuple[int, int]):
        if self.current_task is not None:
            self.task_progress.emit(self.current_task, progress[0] / progress[1])

    def on_task_download_progress(self, fraction_downloaded: float):
        if self.current_task is not None:
            self.task_download_progress.emit(self.current_task, fraction_downloaded)

    @pyqtSlot(list)
    def on_task_completed(self, segments: List[Segment]):
        if self.current_task is not None:
            self.task_completed.emit(self.current_task, segments)

        if self.speech_path is not None:
            try:
                Path(self.speech_path).unlink()
            except Exception:
                pass
            self.speech_path = None

    def stop(self):
        self.tasks_queue.put(None)
        if self.current_transcriber is not None:
            self.current_transcriber.stop()


================================================
FILE: buzz/locale/ca_ES/LC_MESSAGES/buzz.po
================================================
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Jordi Mas i Hernàndez <jmas@softcatala.org>, 2022, 2023, 2024
#
msgid ""
msgstr ""
"Project-Id-Version: buzz\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-03-07 20:20+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"
"Language: ca\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.7\n"

#: buzz/widgets/import_url_dialog.py buzz/settings/shortcut.py
msgid "Import URL"
msgstr "URL d'importació"

#: buzz/widgets/import_url_dialog.py
msgid "https://example.com/audio.mp3"
msgstr "https://exemple.com/audio.mp3"

#: buzz/widgets/import_url_dialog.py
#: buzz/widgets/preferences_dialog/preferences_dialog.py
#: buzz/widgets/preferences_dialog/models_preferences_widget.py
#: buzz/widgets/transcriber/advanced_settings_dialog.py
#: buzz/widgets/main_window.py
msgid "Ok"
msgstr "D’acord"

#: buzz/widgets/import_url_dialog.py
#: buzz/widgets/preferences_dialog/preferences_dialog.py
#: buzz/widgets/preferences_dialog/models_preferences_widget.py
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py
#: buzz/widgets/model_download_progress_dialog.py buzz/widgets/main_window.py
msgid "Cancel"
msgstr "Cancel·lar"

#: buzz/widgets/import_url_dialog.py
msgid "URL:"
msgstr "URL:"

#: buzz/widgets/import_url_dialog.py
msgid "Invalid URL"
msgstr "URL no vàlida"

#: buzz/widgets/import_url_dialog.py
msgid "The URL you entered is invalid."
msgstr "L'URL que heu introduït no és vàlid."

#: buzz/widgets/presentation_window.py
msgid "Live Transcript Presentation"
msgstr "Presentació de transcripció en directe"

#: buzz/widgets/preferences_dialog/shortcuts_editor_preferences_widget.py
msgid "Reset to Defaults"
msgstr "Restableix als valors predeterminats"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/transcriber/transcriber.py
msgid "English"
msgstr "Anglès"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/transcriber/transcriber.py
msgid "Catalan"
msgstr "Català"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/transcriber/transcriber.py
msgid "Danish"
msgstr "Danès"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/transcriber/transcriber.py
msgid "Dutch"
msgstr "Holandès"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/transcriber/transcriber.py
msgid "German"
msgstr "Alemany"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/transcriber/transcriber.py
msgid "Spanish"
msgstr "Castellà"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/transcriber/transcriber.py
msgid "Italian"
msgstr "Italià"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/transcriber/transcriber.py
msgid "Japanese"
msgstr "Japonès"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/transcriber/transcriber.py
msgid "Latvian"
msgstr "Letó"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/transcriber/transcriber.py
msgid "Polish"
msgstr "Polonès"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Portuguese (Brazil)"
msgstr "Portuguès (Brasil)"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/transcriber/transcriber.py
msgid "Ukrainian"
msgstr "Ucraïnès"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Chinese (Simplified)"
msgstr "Xinès (simplificat)"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Chinese (Traditional)"
msgstr "Xinès (Tradicional)"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Restart required!"
msgstr "Cal reiniciar!"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Ui Language"
msgstr "Idioma UI"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Font Size"
msgstr "Mida de la lletra"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Test"
msgstr "Prova"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "OpenAI API key"
msgstr "Clau de l'API d'OpenAI"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "OpenAI base url"
msgstr "URL base d'OpenAI"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "OpenAI API model"
msgstr "Model de l'API d'OpenAI"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Default export file name"
msgstr "Nom del fitxer d'exportació per defecte"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Enable live recording transcription export"
msgstr "Activa l'exportació de transcripcions en directe"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/widgets/preferences_dialog/folder_watch_preferences_widget.py
#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Browse"
msgstr "Navega"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Export folder"
msgstr "Exporta la carpeta"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Live recording mode"
msgstr "Mode d'enregistrament en directe"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid ""
"Note: Live recording export settings will be moved to the Advanced Settings "
"in the Live Recording screen in a future version."
msgstr ""
"Nota: La configuració d'exportació d'enregistrament en directe es mourà a la "
"Configuració avançada a la pantalla d'enregistrament en directe en una "
"versió futura."

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Use 8-bit quantization to reduce memory usage"
msgstr "Utilitza la quantització de 8 bits per reduir l'ús de memòria"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid ""
"Applies to Huggingface and Faster Whisper models. Reduces GPU memory usage "
"but may slightly decrease transcription quality."
msgstr ""
"S'aplica als models Huggingface i Faster Whisper. Redueix l'ús de memòria de "
"la GPU, però pot reduir lleugerament la qualitat de la transcripció."

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Reduce GPU RAM"
msgstr "Redueix la RAM de la GPU"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Use only CPU and disable GPU acceleration"
msgstr "Utilitza només la CPU i desactiveu l'acceleració de la GPU"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Set this if larger models do not fit your GPU memory and Buzz crashes"
msgstr ""
"Establiu això si els models més grans no s'ajusten a la memòria de la GPU i "
"Buzz es bloqueja"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Disable GPU"
msgstr "Desactiva la GPU"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "OpenAI API Key Test"
msgstr "Prova de clau OpenAI API"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid ""
"Your API key is valid. Buzz will use this key to perform Whisper API "
"transcriptions and AI translations."
msgstr ""
"La vostra clau API és vàlida. Buzz utilitzarà aquesta clau per realitzar "
"transcripcions de l'API de Whisper i traduccions de la IA."

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Invalid API key"
msgstr "Clau API no vàlida"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid ""
"API supports only base64 characters (A-Za-z0-9+/=_-). Other characters in "
"API key may cause errors."
msgstr ""
"L'API només admet caràcters base64 (A-Za-z0-9+/).-). Altres caràcters de la "
"clau API poden causar errors."

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Select Export Folder"
msgstr "Selecciona la carpeta d'exportació"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid ""
"OpenAI API returned invalid response. Please check the API url or your key. "
"Transcription and translation may still work if the API does not support key "
"validation."
msgstr ""
"L'API d'OpenAI ha retornat una resposta no vàlida. Comproveu l'URL de l'API "
"o la vostra clau. La transcripció i la traducció encara poden funcionar si "
"l'API no admet la validació de claus."

#: buzz/widgets/preferences_dialog/folder_watch_preferences_widget.py
msgid "Enable folder watch"
msgstr "Habilita el seguiment de carpetes"

#: buzz/widgets/preferences_dialog/folder_watch_preferences_widget.py
msgid "Delete processed files"
msgstr "Eliminar fitxers processats"

#: buzz/widgets/preferences_dialog/folder_watch_preferences_widget.py
msgid "Input folder"
msgstr "Carpeta d'entrada"

#: buzz/widgets/preferences_dialog/folder_watch_preferences_widget.py
msgid "Output folder"
msgstr "Carpeta de sortida"

#: buzz/widgets/preferences_dialog/folder_watch_preferences_widget.py
msgid "Select Input Folder"
msgstr "Selecciona la carpeta d'entrada"

#: buzz/widgets/preferences_dialog/folder_watch_preferences_widget.py
msgid "Select Output Folder"
msgstr "Selecciona la carpeta de sortida"

#: buzz/widgets/preferences_dialog/preferences_dialog.py
msgid "Preferences"
msgstr "Preferències"

#: buzz/widgets/preferences_dialog/preferences_dialog.py
msgid "General"
msgstr "General"

#: buzz/widgets/preferences_dialog/preferences_dialog.py
msgid "Models"
msgstr "Models"

#: buzz/widgets/preferences_dialog/preferences_dialog.py
msgid "Shortcuts"
msgstr "Dreceres"

#: buzz/widgets/preferences_dialog/preferences_dialog.py
msgid "Folder Watch"
msgstr "Vigila la carpeta"

#: buzz/widgets/preferences_dialog/models_preferences_widget.py
msgid "Group"
msgstr "Grup"

#: buzz/widgets/preferences_dialog/models_preferences_widget.py
msgid "Huggingface ID of a Faster whisper model"
msgstr "ID de la cara oculta d'un model de whisper més ràpid"

#: buzz/widgets/preferences_dialog/models_preferences_widget.py
msgid "Download"
msgstr "Descàrrega"

#: buzz/widgets/preferences_dialog/models_preferences_widget.py
msgid "Show file location"
msgstr "Mostra la ubicació del fitxer"

#: buzz/widgets/preferences_dialog/models_preferences_widget.py
msgid "Delete"
msgstr "Suprimeix"

#: buzz/widgets/preferences_dialog/models_preferences_widget.py
msgid "Downloaded"
msgstr "Descarregat"

#: buzz/widgets/preferences_dialog/models_preferences_widget.py
msgid "Available for Download"
msgstr "Disponible per descarregar"

#: buzz/widgets/preferences_dialog/models_preferences_widget.py
msgid "Download link to Whisper.cpp ggml model file"
msgstr "Enllaç de descàrrega a Whisper.cpp fitxer de model ggml"

#: buzz/widgets/preferences_dialog/models_preferences_widget.py
msgid "Delete Model"
msgstr "Suprimeix el model"

#: buzz/widgets/preferences_dialog/models_preferences_widget.py
msgid "Are you sure you want to delete the selected model?"
msgstr "Esteu segur que voleu suprimir el model seleccionat?"

#: buzz/widgets/preferences_dialog/models_preferences_widget.py
msgid "Download failed"
msgstr "Descàrrega fallida"

#: buzz/widgets/preferences_dialog/models_preferences_widget.py
#: buzz/widgets/transcription_tasks_table_widget.py
#: buzz/widgets/update_dialog.py buzz/widgets/main_window.py
#: buzz/model_loader.py
msgid "Error"
msgstr "Error"

#: buzz/widgets/record_button.py buzz/widgets/main_window_toolbar.py
msgid "Record"
msgstr "Enregistra"

#: buzz/widgets/record_button.py
msgid "Stop"
msgstr "Atura"

#: buzz/widgets/transcriber/languages_combo_box.py
#: buzz/transcriber/transcriber.py
msgid "Detect Language"
msgstr "Detecta l'idioma"

#: buzz/widgets/transcriber/mms_language_line_edit.py
msgid "e.g., eng, fra, deu"
msgstr "p. ex., eng, fra, deu"

#: buzz/widgets/transcriber/mms_language_line_edit.py
msgid ""
"Enter an ISO 639-3 language code (3 letters).\n"
"Examples: eng (English), fra (French), deu (German),\n"
"spa (Spanish), lav (Latvian)"
msgstr ""
"Introduïu un codi d'idioma ISO 639-3 (3 lletres).\n"
"Exemples: eng (anglès), fra (francès), deu (alemany),\n"
"spa (castellà), lav (letó)"

#: buzz/widgets/transcriber/file_transcriber_widget.py
msgid "Run"
msgstr "Executa"

#: buzz/widgets/transcriber/transcription_options_group_box.py
msgid "Model:"
msgstr "Model:"

#: buzz/widgets/transcriber/transcription_options_group_box.py
#: buzz/transcriber/recording_transcriber.py
msgid "First time use of a model may take up to several minutest to load."
msgstr ""
"L'ús per primera vegada d'un model pot trigar diversos minuts a carregar-se."

#: buzz/widgets/transcriber/transcription_options_group_box.py
msgid "Api Key:"
msgstr "Clau API:"

#: buzz/widgets/transcriber/transcription_options_group_box.py
msgid "Task:"
msgstr "Tasca:"

#: buzz/widgets/transcriber/transcription_options_group_box.py
msgid "Language:"
msgstr "Idioma:"

#: buzz/widgets/transcriber/initial_prompt_text_edit.py
msgid "Enter prompt..."
msgstr "Introduïu el prompt..."

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Advanced Settings"
msgstr "Configuració avançada"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Speech recognition settings"
msgstr "Configuració del reconeixement de veu"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Initial Prompt:"
msgstr "Pregunta inicial:"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Translation settings"
msgstr "Configuració de la traducció"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Enable AI translation"
msgstr "Habilita la traducció de la IA"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "AI model:"
msgstr "Model d'IA:"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid ""
"Please translate each text sent to you from English to Spanish. Translation "
"will be used in an automated system, please do not add any comments or "
"notes, just the translation."
msgstr ""
"Si us plau, traduïu cada text que us enviï de l'anglès al castellà. La "
"traducció s'utilitzarà en un sistema automatitzat; si us plau, no afegiu cap "
"comentari ni nota, només la traducció."

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Instructions for AI:"
msgstr "Instruccions per a la IA:"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Recording settings"
msgstr "Configuració d'enregistrament"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Silence threshold:"
msgstr "Llindar de silenci:"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Live recording mode:"
msgstr "Mode d'enregistrament en directe:"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Line separator:"
msgstr "Separador de línies:"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Transcription step:"
msgstr "Pas de transcripció:"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Hide unconfirmed"
msgstr "Amaga el no confirmat"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Enable live recording export"
msgstr "Activa l'exportació d'enregistrament en directe"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Export folder:"
msgstr "Carpeta d'exportació:"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Export file name:"
msgstr "Nom del fitxer d'exportació:"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Text file (.txt)"
msgstr "Fitxer de text (.txt)"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "CSV (.csv)"
msgstr "CSV (.csv)"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Export file type:"
msgstr "Tipus de fitxer d'exportació:"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid ""
"Limit export entries\n"
"(0 = export all):"
msgstr ""
"Limita les entrades d'exportació\n"
"(0 = exporta tot):"

#: buzz/widgets/transcriber/file_transcription_form_widget.py
msgid "Word-level timings"
msgstr "Temps amb granularitat de paraula"

#: buzz/widgets/transcriber/file_transcription_form_widget.py
msgid "Extract speech"
msgstr "Extreu la veu"

#: buzz/widgets/transcriber/file_transcription_form_widget.py
msgid "Export:"
msgstr "Exporta:"

#: buzz/widgets/transcriber/hugging_face_search_line_edit.py
msgid "Huggingface ID of a model"
msgstr "ID de la cara oculta d'un model"

#: buzz/widgets/transcriber/advanced_settings_button.py
msgid "Advanced..."
msgstr "Avançat..."

#: buzz/widgets/main_window_toolbar.py
msgid "New File Transcription"
msgstr "Nova transcripció de fitxers"

#: buzz/widgets/main_window_toolbar.py
msgid "New URL Transcription"
msgstr "Nova transcripció d'URL"

#: buzz/widgets/main_window_toolbar.py
msgid "Open Transcript"
msgstr "Obre una transcripció"

#: buzz/widgets/main_window_toolbar.py buzz/settings/shortcut.py
msgid "Cancel Transcription"
msgstr "Cancel·la la transcripció"

#: buzz/widgets/main_window_toolbar.py buzz/widgets/main_window.py
#: buzz/settings/shortcut.py
msgid "Clear History"
msgstr "Neteja l'historial"

#: buzz/widgets/main_window_toolbar.py buzz/widgets/update_dialog.py
msgid "Update Available"
msgstr "Actualització disponible"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "In Progress"
msgstr "En progrés"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Completed"
msgstr "Completat"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Failed"
msgstr "Ha fallat"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Canceled"
msgstr "Cancel·lat"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Queued"
msgstr "A la cua"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "File Name / URL"
msgstr "Nom del fitxer / URL"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Model"
msgstr "Model"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Task"
msgstr "Tasca"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Status"
msgstr "Estat"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Date Completed"
msgstr "Data de finalització"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Date Added"
msgstr "Data d'addició"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Notes"
msgstr "Notes"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Reset Column Order"
msgstr "Restableix l'ordre de les columnes"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Restart Transcription"
msgstr "Reinicia la transcripció"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Rename"
msgstr "Canvia el nom"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Add/Edit Notes"
msgstr "Afegeix/Edita notes"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Rename Transcription"
msgstr "Canvia el nom de la transcripció"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Enter new name:"
msgstr "Introduïu el nou nom:"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Enter some relevant notes for this transcription:"
msgstr "Introduïu algunes notes rellevants per a aquesta transcripció:"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Cannot Restart"
msgstr "No es pot reiniciar"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Only failed or canceled transcriptions can be restarted."
msgstr "Només es poden reiniciar les transcripcions fallides o cancel·lades."

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Failed to restart transcription: {}"
msgstr "No s'ha pogut reiniciar la transcripció: {}"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid ""
"Could not restart transcription: model not available and could not be "
"downloaded."
msgstr ""
"No s'ha pogut reiniciar la transcripció: el model no està disponible i no "
"s'ha pogut descarregar."

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Could not restart transcription: transcriber worker not found."
msgstr ""
"No s'ha pogut reiniciar la transcripció: no s'ha trobat el treballador del "
"transcriptor."

#: buzz/widgets/recording_transcriber_widget.py
msgid "Live Recording"
msgstr "Enregistrament en directe"

#: buzz/widgets/recording_transcriber_widget.py
msgid "Click Record to begin..."
msgstr "Feu clic a Enregistra per a començar..."

#: buzz/widgets/recording_transcriber_widget.py
msgid "Waiting for AI translation..."
msgstr "Esperant la traducció de la IA..."

#: buzz/widgets/recording_transcriber_widget.py
msgid "Microphone:"
msgstr "Micròfon:"

#: buzz/widgets/recording_transcriber_widget.py
msgid "Show in new window"
msgstr "Mostra en una nova finestra"

#: buzz/widgets/recording_transcriber_widget.py
msgid "Text Size:"
msgstr "Mida del text:"

#: buzz/widgets/recording_transcriber_widget.py
msgid "Theme"
msgstr "Tema"

#: buzz/widgets/recording_transcriber_widget.py
msgid "Light"
msgstr "Clar"

#: buzz/widgets/recording_transcriber_widget.py
msgid "Dark"
msgstr "Fosc"

#: buzz/widgets/recording_transcriber_widget.py
msgid "Custom"
msgstr "Personalitzat"

#: buzz/widgets/recording_transcriber_widget.py
msgid "Text Color"
msgstr "Color del text"

#: buzz/widgets/recording_transcriber_widget.py
msgid "Background Color"
msgstr "Color de fons"

#: buzz/widgets/recording_transcriber_widget.py
msgid "Fullscreen"
msgstr "Pantalla completa"

#: buzz/widgets/recording_transcriber_widget.py
msgid "Copy"
msgstr "Copia"

#: buzz/widgets/recording_transcriber_widget.py
msgid "Copy transcription to clipboard"
msgstr "Copia la transcripció al porta-retalls"

#: buzz/widgets/recording_transcriber_widget.py
msgid "Nothing to copy!"
msgstr "Res per copiar!"

#: buzz/widgets/recording_transcriber_widget.py
msgid "Copy failed"
msgstr "Ha fallat la còpia"

#: buzz/widgets/recording_transcriber_widget.py
msgid "Copied!"
msgstr "Copiat!"

#: buzz/widgets/recording_transcriber_widget.py
msgid "Select Text Color"
msgstr "Selecciona el color del text"

#: buzz/widgets/recording_transcriber_widget.py
msgid "Select Background Color"
msgstr "Selecciona el color de fons"

#: buzz/widgets/recording_transcriber_widget.py
msgid "An error occurred while starting a new recording:"
msgstr "S'ha produït un error en iniciar un enregistrament nou:"

#: buzz/widgets/recording_transcriber_widget.py
msgid ""
"Please check your audio devices or check the application logs for more "
"information."
msgstr ""
"Comproveu els vostres dispositius d'àudio o els registres de l'aplicació per "
"a més informació."

#: buzz/widgets/update_dialog.py
msgid "A new version of Buzz is available!"
msgstr "Hi ha una nova versió de Buzz disponible!"

#: buzz/widgets/update_dialog.py
msgid "Current version:"
msgstr "Versió actual:"

#: buzz/widgets/update_dialog.py
msgid "New version:"
msgstr "Nova versió:"

#: buzz/widgets/update_dialog.py
msgid "Release Notes:"
msgstr "Notes de la versió:"

#: buzz/widgets/update_dialog.py
msgid "Download and Install"
msgstr "Descarrega i instal·la"

#: buzz/widgets/update_dialog.py
msgid "No download URL available for your platform."
msgstr "No hi ha cap URL de descàrrega disponible per a la vostra plataforma."

#: buzz/widgets/update_dialog.py
msgid "Downloading file {} of {}..."
msgstr "S'està descarregant el fitxer {} de {}..."

#: buzz/widgets/update_dialog.py
msgid "Downloading file {} of {} ({:.1f} MB / {:.1f} MB)..."
msgstr "S'està descarregant el fitxer {} de {} ({:.1f} MB / {:.1f} MB)..."

#: buzz/widgets/update_dialog.py
msgid "Download Failed"
msgstr "Descàrrega fallida"

#: buzz/widgets/update_dialog.py
msgid "Failed to download the update: {}"
msgstr "No s'ha pogut descarregar l'actualització: {}"

#: buzz/widgets/update_dialog.py
msgid "Failed to save the installer: {}"
msgstr "No s'ha pogut desar l'instal·lador: {}"

#: buzz/widgets/update_dialog.py
msgid "Download complete!"
msgstr "Descàrrega completada!"

#: buzz/widgets/update_dialog.py
msgid "Failed to run the installer: {}"
msgstr "No s'ha pogut executar l'instal·lador: {}"

#: buzz/widgets/about_dialog.py
msgid "Check for updates"
msgstr "Comprova si hi ha actualitzacions"

#: buzz/widgets/about_dialog.py
msgid "Show logs"
msgstr "Mostra els registres"

#: buzz/widgets/about_dialog.py
msgid "You're up to date!"
msgstr "Estàs al dia!"

#: buzz/widgets/audio_meter_widget.py
msgid "Average volume"
msgstr "Volum mitjà"

#: buzz/widgets/audio_meter_widget.py
msgid "Queue"
msgstr "Cua"

#: buzz/widgets/transcription_viewer/transcription_segments_editor_widget.py
msgid "Start"
msgstr "Inicia"

#: buzz/widgets/transcription_viewer/transcription_segments_editor_widget.py
msgid "End"
msgstr "Finalitza"

#: buzz/widgets/transcription_viewer/transcription_segments_editor_widget.py
#: buzz/widgets/transcription_viewer/transcription_view_mode_tool_button.py
#: buzz/widgets/transcription_viewer/export_transcription_menu.py
msgid "Text"
msgstr "Text"

#: buzz/widgets/transcription_viewer/transcription_segments_editor_widget.py
#: buzz/widgets/transcription_viewer/transcription_view_mode_tool_button.py
#: buzz/widgets/transcription_viewer/export_transcription_menu.py
msgid "Translation"
msgstr "Traducció"

#: buzz/widgets/transcription_viewer/transcription_view_mode_tool_button.py
msgid "View"
msgstr "Veure"

#: buzz/widgets/transcription_viewer/transcription_view_mode_tool_button.py
msgid "Timestamps"
msgstr "Marqua de temps"

#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py
msgid "Export"
msgstr "Exporta"

#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py
msgid "Translate"
msgstr "Traduir"

#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py
#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py
msgid "Resize"
msgstr "Redimensionar"

#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py
msgid "Identify Speakers"
msgstr "Identifica els parlants"

#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py
msgid "Find"
msgstr "Cerca"

#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py
msgid "Show/Hide Search Bar (Ctrl+F)"
msgstr "Mostra/amaga la barra de cerca (Ctrl+F)"

#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py
msgid "Find:"
msgstr "Cerca:"

#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py
msgid "Enter text to find..."
msgstr "Introduïu el text a cercar..."

#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py
msgid "Previous match (Shift+Enter)"
msgstr "Coincidència anterior (Maj+Retorn)"

#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py
msgid "Next match (Ctrl+Enter)"
msgstr "Coincidència següent (Ctrl+Enter)"

#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py
msgid "Clear"
msgstr "Neteja"

#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py
msgid "Playback Controls:"
msgstr "Controls de reproducció:"

#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py
msgid "Loop Segment"
msgstr "Segment de bucle"

#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py
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
msgid "Follow Audio"
msgstr "Segueix l'àudio"

#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py
msgid ""
"Enable/disable following the current audio position in the transcript. When "
"enabled, automatically scrolls to current text."
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
msgid "Scroll to Current"
msgstr "Desplaça't fins a l'actual"

#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py
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
msgid "1 of 100+ matches"
msgstr "1 de més de 100 coincidències"

#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py
msgid "1 of "
msgstr "1 de "

#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py
msgid " matches"
msgstr " coincidències"

#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py
msgid "No matches found"
msgstr "No s'ha trobat cap coincidència"

#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py
msgid " of 100+ matches"
msgstr " de més de 100 coincidències"

#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py
msgid " of "
msgstr " de "

#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py
msgid "API Key Required"
msgstr "Clau API necessària"

#: buzz/widgets/transcription_viewer/transcription_viewer_widget.py
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
msgid "Extend end time"
msgstr "Amplia el temps final"

#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py
msgid "Extend endings by up to (seconds)"
msgstr "Amplia els finals fins a (segons)"

#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py
msgid "Extend endings"
msgstr "Amplia els finals"

#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py
msgid "Resize Options"
msgstr "Opcions de redimensionament"

#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py
msgid "Desired subtitle length"
msgstr "Longitud desitjada dels subtítols"

#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py
msgid "Available only if word level timings were disabled during transcription"
msgstr ""
"Disponible només si els temps a nivell de paraula estaven desactivats durant "
"la transcripció"

#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py
msgid "Merge Options"
msgstr "Opcions de fusió"

#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py
msgid "Merge by gap"
msgstr "Fusiona per buit"

#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py
msgid "Split by punctuation"
msgstr "Divideix per puntuació"

#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py
msgid "Split by max length"
msgstr "Divideix per la longitud màxima"

#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py
msgid "Merge"
msgstr "Fusiona"

#: buzz/widgets/transcription_viewer/transcription_resizer_widget.py
msgid "Available only if word level timings were enabled during transcription"
msgstr ""
"Disponible només si els temps a nivell de paraula estaven activats durant la "
"transcripció"

#: buzz/widgets/transcription_viewer/speaker_identification_widget.py
msgid ""
"Speaker identification is not available: failed to load required libraries."
msgstr ""
"La identificació de parlants no està disponible: no s'han pogut carregar les "
"biblioteques necessàries."

#: buzz/widgets/transcription_viewer/speaker_identification_widget.py
msgid "1/8 Collecting transcripts"
msgstr "1/8 Recollint transcripcions"

#: buzz/widgets/transcription_viewer/speaker_identification_widget.py
msgid "2/8 Loading audio"
msgstr "2/8 Carregant àudio"

#: buzz/widgets/transcription_viewer/speaker_identification_widget.py
msgid "3/8 Loading alignment model"
msgstr "3/8 Carregant el model d'alineació"

#: buzz/widgets/transcription_viewer/speaker_identification_widget.py
msgid "3/8 Loading alignment model (retrying with cache...)"
msgstr ""
"3/8 Carregant el model d'alineació (tornant a intentar amb la memòria cau...)"

#: buzz/widgets/transcription_viewer/speaker_identification_widget.py
msgid ""
"Failed to load alignment model. Please check your internet connection and "
"try again."
msgstr ""
"No s'ha pogut carregar el model d'alineació. Comproveu la vostra connexió a "
"Internet i torneu-ho a intentar."

#: buzz/widgets/transcription_viewer/speaker_identification_widget.py
msgid "4/8 Processing audio"
msgstr "4/8 Processant àudio"

#: buzz/widgets/transcription_viewer/speaker_identification_widget.py
msgid "5/8 Preparing transcripts"
msgstr "5/8 Preparant transcripcions"

#: buzz/widgets/transcription_viewer/speaker_identification_widget.py
msgid "6/8 Identifying speakers"
msgstr "6/8 Identificant parlants"

#: buzz/widgets/transcription_viewer/speaker_identification_widget.py
msgid "7/8 Mapping speakers to transcripts"
msgstr "7/8 Assignant parlants a transcripcions"

#: buzz/widgets/transcription_viewer/speaker_identification_widget.py
msgid "8/8 Identification done"
msgstr "8/8 Identificació completada"

#: buzz/widgets/transcription_viewer/speaker_identification_widget.py
msgid "0/0 Error identifying speakers"
msgstr "0/0 Error en identificar parlants"

#: buzz/widgets/transcription_viewer/speaker_identification_widget.py
msgid "Step 1: Identify speakers"
msgstr "Pas 1: Identifica els parlants"

#: buzz/widgets/transcription_viewer/speaker_identification_widget.py
msgid "Identify"
msgstr "Identifica"

#: buzz/widgets/transcription_viewer/speaker_identification_widget.py
msgid "Ready to identify speakers"
msgstr "Llest per identificar parlants"

#: buzz/widgets/transcription_viewer/speaker_identification_widget.py
msgid "Audio file not found"
msgstr "Fitxer d'àudio no trobat"

#: buzz/widgets/transcription_viewer/speaker_identification_widget.py
msgid "Step 2: Name speakers"
msgstr "Pas 2: Anomena els parlants"

#: buzz/widgets/transcription_viewer/speaker_identification_widget.py
msgid "Play sample"
msgstr "Reprodueix la mostra"

#: buzz/widgets/transcription_viewer/speaker_identification_widget.py
msgid "Merge speaker sentences"
msgstr "Fusiona les frases del parlant"

#: buzz/widgets/transcription_viewer/speaker_identification_widget.py
msgid "Save"
msgstr "Desa"

#: buzz/widgets/transcription_viewer/speaker_identification_widget.py
msgid "Cancelling..."
msgstr "Cancel·lant..."

#: buzz/widgets/transcription_viewer/speaker_identification_widget.py
msgid "Cancelled"
msgstr "Cancel·lat"

#: buzz/widgets/transcription_viewer/export_transcription_menu.py
msgid "Save File"
msgstr "Desa el fitxer"

#: buzz/widgets/transcription_viewer/export_transcription_menu.py
msgid "Text files"
msgstr "Fitxers de text"

#: buzz/widgets/model_download_progress_dialog.py
msgid "Downloading model"
msgstr "Descarregant el model"

#: buzz/widgets/model_download_progress_dialog.py
msgid "remaining"
msgstr "restant"

#: buzz/widgets/menu_bar.py
msgid "Import File..."
msgstr "Importa fitxer..."

#: buzz/widgets/menu_bar.py
msgid "Import URL..."
msgstr "Importa l'URL..."

#: buzz/widgets/menu_bar.py
msgid "Import Folder..."
msgstr "Importa carpeta..."

#: buzz/widgets/menu_bar.py
msgid "About"
msgstr "Quant a"

#: buzz/widgets/menu_bar.py
msgid "Preferences..."
msgstr "Preferències..."

#: buzz/widgets/menu_bar.py
msgid "Help"
msgstr "Ajuda"

#: buzz/widgets/menu_bar.py
msgid "File"
msgstr "Fitxer"

#: buzz/widgets/main_window.py
msgid ""
"Are you sure you want to delete the selected transcription(s)? This action "
"cannot be undone."
msgstr ""
"Esteu segur que voleu suprimir les transcripcions seleccionades? Aquesta "
"acció no es pot desfer."

#: buzz/widgets/main_window.py
msgid "Select audio file"
msgstr "Selecciona un fitxer d'àudio"

#: buzz/widgets/main_window.py
msgid "Select folder"
msgstr "Selecciona carpeta"

#: buzz/widgets/main_window.py
msgid "Unable to save OpenAI API key to keyring"
msgstr "No s'ha pogut desar la clau OpenAI API a l'anell de claus"

#: buzz/transcriber/local_whisper_cpp_server_transcriber.py
#: buzz/transcriber/recording_transcriber.py
msgid "Whisper server failed to start. Check logs for details."
msgstr ""
"El servidor Whisper no s'ha pogut iniciar. Consulteu els registres per "
"obtenir més informació."

#: buzz/transcriber/local_whisper_cpp_server_transcriber.py
#: buzz/transcriber/recording_transcriber.py
msgid ""
"Whisper server failed to start due to insufficient memory. Please try again "
"with a smaller model. To force CPU mode use BUZZ_FORCE_CPU=TRUE environment "
"variable."
msgstr ""
"El servidor Whisper no s'ha pogut iniciar a causa de la memòria insuficient. "
"Si us plau, torneu-ho a provar amb un model més petit. Per forçar el mode "
"CPU, utilitzeu la variable d'entorn BUZZ_FORCE_CPU=TRUE."

#: buzz/transcriber/transcriber.py
msgid "Translate to English"
msgstr "Traduir a l'anglès"

#: buzz/transcriber/transcriber.py
msgid "Transcribe"
msgstr "Transcriure"

#: buzz/transcriber/transcriber.py
msgid "Chinese"
msgstr "Xinès"

#: buzz/transcriber/transcriber.py
msgid "Russian"
msgstr "Rus"

#: buzz/transcriber/transcriber.py
msgid "Korean"
msgstr "Coreà"

#: buzz/transcriber/transcriber.py
msgid "French"
msgstr "Francés"

#: buzz/transcriber/transcriber.py
msgid "Portuguese"
msgstr "Portuguès"

#: buzz/transcriber/transcriber.py
msgid "Turkish"
msgstr "Turc"

#: buzz/transcriber/transcriber.py
msgid "Arabic"
msgstr "Àrab"

#: buzz/transcriber/transcriber.py
msgid "Swedish"
msgstr "Suec"

#: buzz/transcriber/transcriber.py
msgid "Indonesian"
msgstr "Indonesi"

#: buzz/transcriber/transcriber.py
msgid "Hindi"
msgstr "Hindi"

#: buzz/transcriber/transcriber.py
msgid "Finnish"
msgstr "Finès"

#: buzz/transcriber/transcriber.py
msgid "Vietnamese"
msgstr "Vietnamita"

#: buzz/transcriber/transcriber.py
msgid "Hebrew"
msgstr "Hebreu"

#: buzz/transcriber/transcriber.py
msgid "Greek"
msgstr "Grec"

#: buzz/transcriber/transcriber.py
msgid "Malay"
msgstr "Malai"

#: buzz/transcriber/transcriber.py
msgid "Czech"
msgstr "Txec"

#: buzz/transcriber/transcriber.py
msgid "Romanian"
msgstr "Romanès"

#: buzz/transcriber/transcriber.py
msgid "Hungarian"
msgstr "Hongarès"

#: buzz/transcriber/transcriber.py
msgid "Tamil"
msgstr "Tàmil"

#: buzz/transcriber/transcriber.py
msgid "Norwegian"
msgstr "Noruec"

#: buzz/transcriber/transcriber.py
msgid "Thai"
msgstr "Tailandès"

#: buzz/transcriber/transcriber.py
msgid "Urdu"
msgstr "Urdú"

#: buzz/transcriber/transcriber.py
msgid "Croatian"
msgstr "Croata"

#: buzz/transcriber/transcriber.py
msgid "Bulgarian"
msgstr "Bùlgar"

#: buzz/transcriber/transcriber.py
msgid "Lithuanian"
msgstr "Lituà"

#: buzz/transcriber/transcriber.py
msgid "Latin"
msgstr "Llatí"

#: buzz/transcriber/transcriber.py
msgid "Maori"
msgstr "Maori"

#: buzz/transcriber/transcriber.py
msgid "Malayalam"
msgstr "Malaiàlam"

#: buzz/transcriber/transcriber.py
msgid "Welsh"
msgstr "Gal·lès"

#: buzz/transcriber/transcriber.py
msgid "Slovak"
msgstr "Eslovac"

#: buzz/transcriber/transcriber.py
msgid "Telugu"
msgstr "Telugu"

#: buzz/transcriber/transcriber.py
msgid "Persian"
msgstr "Persa"

#: buzz/transcriber/transcriber.py
msgid "Bengali"
msgstr "Bengalí"

#: buzz/transcriber/transcriber.py
msgid "Serbian"
msgstr "Serbi"

#: buzz/transcriber/transcriber.py
msgid "Azerbaijani"
msgstr "Àzeri"

#: buzz/transcriber/transcriber.py
msgid "Slovenian"
msgstr "Eslovè"

#: buzz/transcriber/transcriber.py
msgid "Kannada"
msgstr "Kannada"

#: buzz/transcriber/transcriber.py
msgid "Estonian"
msgstr "Estònia"

#: buzz/transcriber/transcriber.py
msgid "Macedonian"
msgstr "Macedoni"

#: buzz/transcriber/transcriber.py
msgid "Breton"
msgstr "Breton"

#: buzz/transcriber/transcriber.py
msgid "Basque"
msgstr "Basc"

#: buzz/transcriber/transcriber.py
msgid "Icelandic"
msgstr "Islandès"

#: buzz/transcriber/transcriber.py
msgid "Armenian"
msgstr "Armeni"

#: buzz/transcriber/transcriber.py
msgid "Nepali"
msgstr "Nepalès"

#: buzz/transcriber/transcriber.py
msgid "Mongolian"
msgstr "Mongol"

#: buzz/transcriber/transcriber.py
msgid "Bosnian"
msgstr "Bosnià"

#: buzz/transcriber/transcriber.py
msgid "Kazakh"
msgstr "Kazakh"

#: buzz/transcriber/transcriber.py
msgid "Albanian"
msgstr "Albanès"

#: buzz/transcriber/transcriber.py
msgid "Swahili"
msgstr "Suahili"

#: buzz/transcriber/transcriber.py
msgid "Galician"
msgstr "Gallec"

#: buzz/transcriber/transcriber.py
msgid "Marathi"
msgstr "Marathi"

#: buzz/transcriber/transcriber.py
msgid "Punjabi"
msgstr "Panjabi"

#: buzz/transcriber/transcriber.py
msgid "Sinhala"
msgstr "Singalès"

#: buzz/transcriber/transcriber.py
msgid "Khmer"
msgstr "Khmer"

#: buzz/transcriber/transcriber.py
msgid "Shona"
msgstr "Shona"

#: buzz/transcriber/transcriber.py
msgid "Yoruba"
msgstr "Ioruba"

#: buzz/transcriber/transcriber.py
msgid "Somali"
msgstr "Somali"

#: buzz/transcriber/transcriber.py
msgid "Afrikaans"
msgstr "Afrikaans"

#: buzz/transcriber/transcriber.py
msgid "Occitan"
msgstr "Occità"

#: buzz/transcriber/transcriber.py
msgid "Georgian"
msgstr "Georgià"

#: buzz/transcriber/transcriber.py
msgid "Belarusian"
msgstr "Bielorús"

#: buzz/transcriber/transcriber.py
msgid "Tajik"
msgstr "Tadjik"

#: buzz/transcriber/transcriber.py
msgid "Sindhi"
msgstr "Sindhi"

#: buzz/transcriber/transcriber.py
msgid "Gujarati"
msgstr "Gujarati"

#: buzz/transcriber/transcriber.py
msgid "Amharic"
msgstr "Amhàric"

#: buzz/transcriber/transcriber.py
msgid "Yiddish"
msgstr "Yiddish"

#: buzz/transcriber/transcriber.py
msgid "Lao"
msgstr "Lao"

#: buzz/transcriber/transcriber.py
msgid "Uzbek"
msgstr "Uzbek"

#: buzz/transcriber/transcriber.py
msgid "Faroese"
msgstr "Feroès"

#: buzz/transcriber/transcriber.py
msgid "Haitian Creole"
msgstr "Crioll d'Haití"

#: buzz/transcriber/transcriber.py
msgid "Pashto"
msgstr "Paixtu"

#: buzz/transcriber/transcriber.py
msgid "Turkmen"
msgstr "Turcomans"

#: buzz/transcriber/transcriber.py
msgid "Nynorsk"
msgstr "Nynorsk"

#: buzz/transcriber/transcriber.py
msgid "Maltese"
msgstr "Maltès"

#: buzz/transcriber/transcriber.py
msgid "Sanskrit"
msgstr "Sànscrit"

#: buzz/transcriber/transcriber.py
msgid "Luxembourgish"
msgstr "Luxemburguès"

#: buzz/transcriber/transcriber.py
msgid "Myanmar"
msgstr "Myanmar"

#: buzz/transcriber/transcriber.py
msgid "Tibetan"
msgstr "Tibetà"

#: buzz/transcriber/transcriber.py
msgid "Tagalog"
msgstr "Tagàlog"

#: buzz/transcriber/transcriber.py
msgid "Malagasy"
msgstr "Malgaix"

#: buzz/transcriber/transcriber.py
msgid "Assamese"
msgstr "Assamès"

#: buzz/transcriber/transcriber.py
msgid "Tatar"
msgstr "Tàtar"

#: buzz/transcriber/transcriber.py
msgid "Hawaiian"
msgstr "Hawaià"

#: buzz/transcriber/transcriber.py
msgid "Lingala"
msgstr "Lingala"

#: buzz/transcriber/transcriber.py
msgid "Hausa"
msgstr "Hausa"

#: buzz/transcriber/transcriber.py
msgid "Bashkir"
msgstr "Bashkir"

#: buzz/transcriber/transcriber.py
msgid "Javanese"
msgstr "Javanès"

#: buzz/transcriber/transcriber.py
msgid "Sundanese"
msgstr "Sundanès"

#: buzz/transcriber/transcriber.py
msgid "Cantonese"
msgstr "Cantonès"

#: buzz/transcriber/recording_transcriber.py buzz/model_loader.py
msgid "A connection error occurred"
msgstr "S'ha produït un error de connexió"

#: buzz/transcriber/recording_transcriber.py
msgid "Starting Whisper.cpp..."
msgstr "Començant Whisper.cpp..."

#: buzz/transcriber/recording_transcriber.py
msgid "Starting transcription..."
msgstr "Iniciant la transcripció..."

#: buzz/settings/shortcut.py
msgid "Open Record Window"
msgstr "Obre la finestra de registre"

#: buzz/settings/shortcut.py
msgid "Import File"
msgstr "Importar arxiu"

#: buzz/settings/shortcut.py
msgid "Open Preferences Window"
msgstr "Obre la finestra de preferències"

#: buzz/settings/shortcut.py
msgid "View Transcript Text"
msgstr "Veure el text de la transcripció"

#: buzz/settings/shortcut.py
msgid "View Transcript Translation"
msgstr "Veure la traducció de transcripció"

#: buzz/settings/shortcut.py
msgid "View Transcript Timestamps"
msgstr "Veure les marques de temps de la transcripció"

#: buzz/settings/shortcut.py
msgid "Search Transcript"
msgstr "Cerca una transcripció"

#: buzz/settings/shortcut.py
msgid "Go to Next Transcript Search Result"
msgstr "Ves al resultat de cerca de transcripció següent"

#: buzz/settings/shortcut.py
msgid "Go to Previous Transcript Search Result"
msgstr "Ves al resultat de cerca de transcripció anterior"

#: buzz/settings/shortcut.py
msgid "Scroll to Current Text"
msgstr "Desplaça't fins al text actual"

#: buzz/settings/shortcut.py
msgid "Play/Pause Audio"
msgstr "Reproduir/posar en pausa l'àudio"

#: buzz/settings/shortcut.py
msgid "Replay Current Segment"
msgstr "Reprodueix el segment actual"

#: buzz/settings/shortcut.py
msgid "Toggle Playback Controls"
msgstr "Commuta els controls de reproducció"

#: buzz/settings/shortcut.py
msgid "Decrease Segment Start Time"
msgstr "Disminuir l'hora d'inici del segment"

#: buzz/settings/shortcut.py
msgid "Increase Segment Start Time"
msgstr "Augmenta l'hora d'inici del segment"

#: buzz/settings/shortcut.py
msgid "Decrease Segment End Time"
msgstr "Disminueix l'hora de finalització del segment"

#: buzz/settings/shortcut.py
msgid "Increase Segment End Time"
msgstr "Augmenta l'hora de finalització del segment"

#: buzz/settings/recording_transcriber_mode.py
msgid "Append below"
msgstr "Afegeix a sota"

#: buzz/settings/recording_transcriber_mode.py
msgid "Append above"
msgstr "Afegeix a sobre"

#: buzz/settings/recording_transcriber_mode.py
msgid "Append and correct"
msgstr "Afegeix i corregeix"

#: buzz/file_transcriber_queue_worker.py
msgid ""
"Speech extraction failed! Check your internet connection — a model may need "
"to be downloaded."
msgstr ""
"Ha fallat l'extracció de veu! Comproveu la vostra connexió a Internet — pot "
"ser que s'hagi de descarregar un model."

#~ msgid "Comma-separated, e.g. \"0.0, 0.2, 0.4, 0.6, 0.8, 1.0\""
#~ msgstr "Separat per comes, p. ex. «0.0, 0.2, 0.4, 0.6, 0.8, 1.0»"

#~ msgid "Temperature:"
#~ msgstr "Temperatura:"

#~ msgid "Please translate each text sent to you from English to Spanish."
#~ msgstr "Si us plau, tradueix cada text que t'enviï de l'anglès al castellà."

#~ msgid "Translation error, see logs!"
#~ msgstr "Error de traducció, vegeu els registres!"

#~ msgid "Snap permission notice"
#~ msgstr "Avís de permís d'ajust"

#~ msgid ""
#~ "Detected missing permissions, please check that snap permissions have "
#~ "been granted"
#~ msgstr ""
#~ "S'han detectat permisos que manquen, comproveu que s'han concedit "
#~ "permisos de captura"

#~ msgid ""
#~ "To enable necessary permissions run the following commands in the terminal"
#~ msgstr ""
#~ "Per habilitar els permisos necessaris, executeu les ordres següents al "
#~ "terminal"

#~ msgid "Close"
#~ msgstr "Tanca"

#~ msgid "Enter instructions for AI on how to translate..."
#~ msgstr "Introduïu les instruccions per a la IA sobre com traduir..."

#~ msgid "ID"
#~ msgstr "ID"

#~ msgid "Undo"
#~ msgstr "Desfés"

#~ msgid "Redo"
#~ msgstr "Refés"


================================================
FILE: buzz/locale/da_DK/LC_MESSAGES/buzz.po
================================================
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-03-07 20:20+0200\n"
"PO-Revision-Date: \n"
"Last-Translator: Ole Guldberg2 <xalt7x.service@gmail.com>\n"
"Language-Team: \n"
"Language: da_DK\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=n==1 ? 3 : n%10==1 && n%100!=11 ? 0 : "
"n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"

# "X-Generator: Poedit 3.4.4\n"
#: buzz/widgets/import_url_dialog.py buzz/settings/shortcut.py
msgid "Import URL"
msgstr "Importer fra URL"

#: buzz/widgets/import_url_dialog.py
msgid "https://example.com/audio.mp3"
msgstr "https://example.com/audio.mp3"

#: buzz/widgets/import_url_dialog.py
#: buzz/widgets/preferences_dialog/preferences_dialog.py
#: buzz/widgets/preferences_dialog/models_preferences_widget.py
#: buzz/widgets/transcriber/advanced_settings_dialog.py
#: buzz/widgets/main_window.py
msgid "Ok"
msgstr "OK"

#: buzz/widgets/import_url_dialog.py
#: buzz/widgets/preferences_dialog/preferences_dialog.py
#: buzz/widgets/preferences_dialog/models_preferences_widget.py
#: buzz/widgets/transcription_viewer/speaker_identification_widget.py
#: buzz/widgets/model_download_progress_dialog.py buzz/widgets/main_window.py
msgid "Cancel"
msgstr "Afbryd"

#: buzz/widgets/import_url_dialog.py
msgid "URL:"
msgstr "URL:"

#: buzz/widgets/import_url_dialog.py
msgid "Invalid URL"
msgstr "Ugyldig URL"

#: buzz/widgets/import_url_dialog.py
msgid "The URL you entered is invalid."
msgstr "Den URL du har angivet er ikke gyldig."

#: buzz/widgets/presentation_window.py
msgid "Live Transcript Presentation"
msgstr "Live transkriptionspræsentation"

#: buzz/widgets/preferences_dialog/shortcuts_editor_preferences_widget.py
msgid "Reset to Defaults"
msgstr "Gendan standard-indstillinger"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/transcriber/transcriber.py
msgid "English"
msgstr "Engelsk"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/transcriber/transcriber.py
msgid "Catalan"
msgstr "Catalansk"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/transcriber/transcriber.py
msgid "Danish"
msgstr "Dansk"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/transcriber/transcriber.py
msgid "Dutch"
msgstr "Nederlandsk"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/transcriber/transcriber.py
msgid "German"
msgstr "Tysk"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/transcriber/transcriber.py
msgid "Spanish"
msgstr "Spansk"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/transcriber/transcriber.py
msgid "Italian"
msgstr "Italiensk"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/transcriber/transcriber.py
msgid "Japanese"
msgstr "Japansk"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/transcriber/transcriber.py
msgid "Latvian"
msgstr "Lettisk"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/transcriber/transcriber.py
msgid "Polish"
msgstr "Polsk"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Portuguese (Brazil)"
msgstr "Portugisisk (Brasilien)"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/transcriber/transcriber.py
msgid "Ukrainian"
msgstr "Ukrainsk"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Chinese (Simplified)"
msgstr "Kinesisk (forenklet)"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Chinese (Traditional)"
msgstr "Kinesisk (traditionelt)"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Restart required!"
msgstr "Genstart påkrævet!"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Ui Language"
msgstr "Brugerfladesprog"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Font Size"
msgstr "Skriftypestørrelse"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Test"
msgstr "Test"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "OpenAI API key"
msgstr "OpenAI API-nøgle"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "OpenAI base url"
msgstr "OpenAI base-URL"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "OpenAI API model"
msgstr "OpenAI API-model"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Default export file name"
msgstr "Standard eksport filnavn"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Enable live recording transcription export"
msgstr "Slå transkription af live optagelse eksport til"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/widgets/preferences_dialog/folder_watch_preferences_widget.py
#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Browse"
msgstr "Gennemse"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Export folder"
msgstr "Eksportmappe"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Live recording mode"
msgstr "Live optagelsestilstand"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid ""
"Note: Live recording export settings will be moved to the Advanced Settings "
"in the Live Recording screen in a future version."
msgstr ""
"Bemærk: Indstillinger for live optagelseseksport vil i en fremtidig version "
"blive flyttet til Avancerede indstillinger på skærmen for live optagelse."

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Use 8-bit quantization to reduce memory usage"
msgstr "Brug 8-bit kvantisering for at reducere hukommelsesforbruget"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid ""
"Applies to Huggingface and Faster Whisper models. Reduces GPU memory usage "
"but may slightly decrease transcription quality."
msgstr ""
"Gælder for Huggingface og Faster Whisper modeller. Reducerer GPU "
"hukommelsesforbrug, men kan en smule forringe transkriptionskvaliteten."

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Reduce GPU RAM"
msgstr "Reducer GPU RAM"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Use only CPU and disable GPU acceleration"
msgstr "Brug kun CPU og deaktiver GPU-acceleration"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Set this if larger models do not fit your GPU memory and Buzz crashes"
msgstr ""
"Aktivér dette hvis større modeller ikke passer i GPU-hukommelsen og Buzz "
"crasher"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Disable GPU"
msgstr "Deaktiver GPU"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "OpenAI API Key Test"
msgstr "OpenAI API Nøgle test"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid ""
"Your API key is valid. Buzz will use this key to perform Whisper API "
"transcriptions and AI translations."
msgstr ""
"Din API nøgle er gyldig. Buzz vil benytte nøglen til at anvende Whisper API "
"transkription og AI oversættelser."

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid "Invalid API key"
msgstr "Ugyldig API-nøgle"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid ""
"API supports only base64 characters (A-Za-z0-9+/=_-). Other characters in "
"API key may cause errors."
msgstr ""
"API supporterer kun base64 tegn (A-Za-z0-9+/=_-). Andre tegn i API-nøglen "
"kan guve fejl. "

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Select Export Folder"
msgstr "Vælg eksport-mappe"

#: buzz/widgets/preferences_dialog/general_preferences_widget.py
msgid ""
"OpenAI API returned invalid response. Please check the API url or your key. "
"Transcription and translation may still work if the API does not support key "
"validation."
msgstr ""
"OpenAI API returnerede et ugyldigt svar. Tjek venligst API-URL og nøgle. "
"Transkription og oversættelse virker måske stadig, selvom API'et ikke "
"understøtter nøgle validering."

#: buzz/widgets/preferences_dialog/folder_watch_preferences_widget.py
msgid "Enable folder watch"
msgstr "Aktiver mappeovervågning"

#: buzz/widgets/preferences_dialog/folder_watch_preferences_widget.py
msgid "Delete processed files"
msgstr "Slet behandlede filer"

#: buzz/widgets/preferences_dialog/folder_watch_preferences_widget.py
msgid "Input folder"
msgstr "Inputmappe"

#: buzz/widgets/preferences_dialog/folder_watch_preferences_widget.py
msgid "Output folder"
msgstr "Outputmappe"

#: buzz/widgets/preferences_dialog/folder_watch_preferences_widget.py
msgid "Select Input Folder"
msgstr "Vælg inputmappe"

#: buzz/widgets/preferences_dialog/folder_watch_preferences_widget.py
msgid "Select Output Folder"
msgstr "Vælg outputmappe"

#: buzz/widgets/preferences_dialog/preferences_dialog.py
msgid "Preferences"
msgstr "Indstillinger"

#: buzz/widgets/preferences_dialog/preferences_dialog.py
msgid "General"
msgstr "Generelt"

#: buzz/widgets/preferences_dialog/preferences_dialog.py
msgid "Models"
msgstr "Modeller"

#: buzz/widgets/preferences_dialog/preferences_dialog.py
msgid "Shortcuts"
msgstr "Genveje"

#: buzz/widgets/preferences_dialog/preferences_dialog.py
msgid "Folder Watch"
msgstr "Mappeovervågning"

#: buzz/widgets/preferences_dialog/models_preferences_widget.py
msgid "Group"
msgstr "Grupper"

#: buzz/widgets/preferences_dialog/models_preferences_widget.py
msgid "Huggingface ID of a Faster whisper model"
msgstr "Huggingface ID af Faster Whisper model"

#: buzz/widgets/preferences_dialog/models_preferences_widget.py
msgid "Download"
msgstr "Download"

#: buzz/widgets/preferences_dialog/models_preferences_widget.py
msgid "Show file location"
msgstr "Vis fil-lokation"

#: buzz/widgets/preferences_dialog/models_preferences_widget.py
msgid "Delete"
msgstr "Slet"

#: buzz/widgets/preferences_dialog/models_preferences_widget.py
msgid "Downloaded"
msgstr "Downloadded"

#: buzz/widgets/preferences_dialog/models_preferences_widget.py
msgid "Available for Download"
msgstr "Tilgængelige til download"

#: buzz/widgets/preferences_dialog/models_preferences_widget.py
msgid "Download link to Whisper.cpp ggml model file"
msgstr "Download link til Whisper.cpp ggml model-fil"

#: buzz/widgets/preferences_dialog/models_preferences_widget.py
msgid "Delete Model"
msgstr "Slet model"

#: buzz/widgets/preferences_dialog/models_preferences_widget.py
msgid "Are you sure you want to delete the selected model?"
msgstr "Er du sikker på at du vil slette den valgte model?"

#: buzz/widgets/preferences_dialog/models_preferences_widget.py
msgid "Download failed"
msgstr "Download mislykkedes"

#: buzz/widgets/preferences_dialog/models_preferences_widget.py
#: buzz/widgets/transcription_tasks_table_widget.py
#: buzz/widgets/update_dialog.py buzz/widgets/main_window.py
#: buzz/model_loader.py
msgid "Error"
msgstr "Fejl"

#: buzz/widgets/record_button.py buzz/widgets/main_window_toolbar.py
msgid "Record"
msgstr "Optag"

#: buzz/widgets/record_button.py
msgid "Stop"
msgstr "Stop"

#: buzz/widgets/transcriber/languages_combo_box.py
#: buzz/transcriber/transcriber.py
msgid "Detect Language"
msgstr "Detekter sprog"

#: buzz/widgets/transcriber/mms_language_line_edit.py
msgid "e.g., eng, fra, deu"
msgstr "f.eks. eng, fra, deu"

#: buzz/widgets/transcriber/mms_language_line_edit.py
msgid ""
"Enter an ISO 639-3 language code (3 letters).\n"
"Examples: eng (English), fra (French), deu (German),\n"
"spa (Spanish), lav (Latvian)"
msgstr ""
"Indtast en ISO 639-3 sprogkode (3 bogstaver).\n"
"Eksempler: eng (engelsk), fra (fransk), deu (tysk),\n"
"spa (spansk), lav (lettisk)"

#: buzz/widgets/transcriber/file_transcriber_widget.py
msgid "Run"
msgstr "Kør"

#: buzz/widgets/transcriber/transcription_options_group_box.py
msgid "Model:"
msgstr "Model:"

#: buzz/widgets/transcriber/transcription_options_group_box.py
#: buzz/transcriber/recording_transcriber.py
msgid "First time use of a model may take up to several minutest to load."
msgstr "Først gang kan brug af en model tage flere minutter at indlæse."

#: buzz/widgets/transcriber/transcription_options_group_box.py
msgid "Api Key:"
msgstr "API-nøgle:"

#: buzz/widgets/transcriber/transcription_options_group_box.py
msgid "Task:"
msgstr "Opgave:"

#: buzz/widgets/transcriber/transcription_options_group_box.py
msgid "Language:"
msgstr "Sprog:"

#: buzz/widgets/transcriber/initial_prompt_text_edit.py
msgid "Enter prompt..."
msgstr "Input tekst..."

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Advanced Settings"
msgstr "Advancerede indstillinger"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Speech recognition settings"
msgstr "Talegenkendelsesindstillinger"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Initial Prompt:"
msgstr "Start prompt:"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Translation settings"
msgstr "Oversættelsesindstillinger"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Enable AI translation"
msgstr "Brug AI oversættelse"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "AI model:"
msgstr "AI model:"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid ""
"Please translate each text sent to you from English to Spanish. Translation "
"will be used in an automated system, please do not add any comments or "
"notes, just the translation."
msgstr ""
"Oversæt venligst hver tekst, der sendes til dig, fra engelsk til spansk. "
"Oversættelsen vil blive brugt i et automatiseret system, så tilføj venligst "
"ingen kommentarer eller noter, kun oversættelsen."

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Instructions for AI:"
msgstr "Instruktioner for AI:"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Recording settings"
msgstr "Optagelsesindstillinger"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Silence threshold:"
msgstr "Tærskelværdi for stilhed:"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Live recording mode:"
msgstr "Live optagelsestilstand:"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Line separator:"
msgstr "Linjeseparator:"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Transcription step:"
msgstr "Transskriberingstrin:"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Hide unconfirmed"
msgstr "Skjul ubekræftet"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Enable live recording export"
msgstr "Aktiver eksport af live optagelse"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Export folder:"
msgstr "Eksportmappe:"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Export file name:"
msgstr "Eksport filnavn:"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Text file (.txt)"
msgstr "Tekstfil (.txt)"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "CSV (.csv)"
msgstr "CSV (.csv)"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid "Export file type:"
msgstr "Eksport filtype:"

#: buzz/widgets/transcriber/advanced_settings_dialog.py
msgid ""
"Limit export entries\n"
"(0 = export all):"
msgstr ""
"Begræns eksportposter\n"
"(0 = eksporter alle):"

#: buzz/widgets/transcriber/file_transcription_form_widget.py
msgid "Word-level timings"
msgstr "Ord tidsniveau"

#: buzz/widgets/transcriber/file_transcription_form_widget.py
msgid "Extract speech"
msgstr "Eksakt tale"

#: buzz/widgets/transcriber/file_transcription_form_widget.py
msgid "Export:"
msgstr "Eksporter:"

#: buzz/widgets/transcriber/hugging_face_search_line_edit.py
msgid "Huggingface ID of a model"
msgstr "Huggingface ID til en model"

#: buzz/widgets/transcriber/advanced_settings_button.py
msgid "Advanced..."
msgstr "Advanceret..."

#: buzz/widgets/main_window_toolbar.py
msgid "New File Transcription"
msgstr "Ny filtranskription"

#: buzz/widgets/main_window_toolbar.py
msgid "New URL Transcription"
msgstr "Ny URL-transkription"

#: buzz/widgets/main_window_toolbar.py
msgid "Open Transcript"
msgstr "Åben transkription"

#: buzz/widgets/main_window_toolbar.py buzz/settings/shortcut.py
msgid "Cancel Transcription"
msgstr "Afbryd transkription"

#: buzz/widgets/main_window_toolbar.py buzz/widgets/main_window.py
#: buzz/settings/shortcut.py
msgid "Clear History"
msgstr "Ryd historik"

#: buzz/widgets/main_window_toolbar.py buzz/widgets/update_dialog.py
msgid "Update Available"
msgstr "Opdatering tilgængelig"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "In Progress"
msgstr "Arbejder"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Completed"
msgstr "Færdig"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Failed"
msgstr "Mislykkedes"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Canceled"
msgstr "Afbrudt"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Queued"
msgstr "Sat i kø"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "File Name / URL"
msgstr "Filnavn / URL"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Model"
msgstr "Model"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Task"
msgstr "Opgave"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Status"
msgstr "Status"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Date Completed"
msgstr "Dato for færdiggørelse"

#: buzz/widgets/transcription_tasks_table_widget.py
msgid "Date Added"
msgstr "Dato for tilføjelse"

#: buzz/widgets/transcript
Download .txt
gitextract_onk69s2x/

├── .coveragerc
├── .github/
│   └── workflows/
│       ├── ci.yml
│       ├── gh-pages.yml
│       ├── manual-build.yml
│       └── snapcraft.yml
├── .gitignore
├── .gitmodules
├── .pre-commit-config.yaml
├── .pylintrc
├── .python-version
├── .run/
│   └── pytest.run.xml
├── .vscode/
│   ├── launch.json
│   ├── settings.json
│   └── tasks.json
├── Buzz.spec
├── CLAUDE.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.ja_JP.md
├── README.md
├── assets/
│   └── buzz.icns
├── buzz/
│   ├── __init__.py
│   ├── __main__.py
│   ├── __version__.py
│   ├── action.py
│   ├── assets/
│   │   └── buzz.icns
│   ├── assets.py
│   ├── buzz.py
│   ├── cache.py
│   ├── cli.py
│   ├── conn.py
│   ├── cuda_setup.py
│   ├── db/
│   │   ├── __init__.py
│   │   ├── dao/
│   │   │   ├── __init__.py
│   │   │   ├── dao.py
│   │   │   ├── transcription_dao.py
│   │   │   └── transcription_segment_dao.py
│   │   ├── db.py
│   │   ├── entity/
│   │   │   ├── __init__.py
│   │   │   ├── entity.py
│   │   │   ├── transcription.py
│   │   │   └── transcription_segment.py
│   │   ├── helpers.py
│   │   ├── migrator.py
│   │   └── service/
│   │       ├── __init__.py
│   │       └── transcription_service.py
│   ├── dialogs.py
│   ├── file_transcriber_queue_worker.py
│   ├── locale/
│   │   ├── ca_ES/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   ├── da_DK/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   ├── de_DE/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   ├── en_US/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   ├── es_ES/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   ├── it_IT/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   ├── ja_JP/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   ├── lv_LV/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   ├── nl/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   ├── pl_PL/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   ├── pt_BR/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   ├── uk_UA/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   ├── zh_CN/
│   │   │   └── LC_MESSAGES/
│   │   │       └── buzz.po
│   │   └── zh_TW/
│   │       └── LC_MESSAGES/
│   │           └── buzz.po
│   ├── locale.py
│   ├── model_loader.py
│   ├── paths.py
│   ├── recording.py
│   ├── schema.sql
│   ├── settings/
│   │   ├── __init__.py
│   │   ├── recording_transcriber_mode.py
│   │   ├── settings.py
│   │   ├── shortcut.py
│   │   └── shortcuts.py
│   ├── store/
│   │   ├── __init__.py
│   │   └── keyring_store.py
│   ├── transcriber/
│   │   ├── __init__.py
│   │   ├── file_transcriber.py
│   │   ├── local_whisper_cpp_server_transcriber.py
│   │   ├── openai_whisper_api_file_transcriber.py
│   │   ├── recording_transcriber.py
│   │   ├── transcriber.py
│   │   ├── whisper_cpp.py
│   │   └── whisper_file_transcriber.py
│   ├── transformers_whisper.py
│   ├── translator.py
│   ├── update_checker.py
│   ├── whisper_audio.py
│   └── widgets/
│       ├── __init__.py
│       ├── about_dialog.py
│       ├── application.py
│       ├── audio_devices_combo_box.py
│       ├── audio_meter_widget.py
│       ├── audio_player.py
│       ├── form_label.py
│       ├── icon.py
│       ├── icon_presentation.py
│       ├── import_url_dialog.py
│       ├── line_edit.py
│       ├── main_window.py
│       ├── main_window_toolbar.py
│       ├── menu_bar.py
│       ├── model_download_progress_dialog.py
│       ├── model_type_combo_box.py
│       ├── openai_api_key_line_edit.py
│       ├── preferences_dialog/
│       │   ├── __init__.py
│       │   ├── folder_watch_preferences_widget.py
│       │   ├── general_preferences_widget.py
│       │   ├── models/
│       │   │   ├── __init__.py
│       │   │   ├── file_transcription_preferences.py
│       │   │   ├── folder_watch_preferences.py
│       │   │   └── preferences.py
│       │   ├── models_preferences_widget.py
│       │   ├── preferences_dialog.py
│       │   └── shortcuts_editor_preferences_widget.py
│       ├── presentation_window.py
│       ├── record_button.py
│       ├── record_delegate.py
│       ├── recording_transcriber_widget.py
│       ├── sequence_edit.py
│       ├── text_display_box.py
│       ├── toolbar.py
│       ├── transcriber/
│       │   ├── __init__.py
│       │   ├── advanced_settings_button.py
│       │   ├── advanced_settings_dialog.py
│       │   ├── file_transcriber_widget.py
│       │   ├── file_transcription_form_widget.py
│       │   ├── hugging_face_search_line_edit.py
│       │   ├── initial_prompt_text_edit.py
│       │   ├── languages_combo_box.py
│       │   ├── mms_language_line_edit.py
│       │   ├── tasks_combo_box.py
│       │   └── transcription_options_group_box.py
│       ├── transcription_record.py
│       ├── transcription_task_folder_watcher.py
│       ├── transcription_tasks_table_widget.py
│       ├── transcription_viewer/
│       │   ├── __init__.py
│       │   ├── export_transcription_menu.py
│       │   ├── speaker_identification_widget.py
│       │   ├── transcription_resizer_widget.py
│       │   ├── transcription_segments_editor_widget.py
│       │   ├── transcription_view_mode_tool_button.py
│       │   └── transcription_viewer_widget.py
│       ├── update_dialog.py
│       └── video_player.py
├── buzz.desktop
├── docs/
│   ├── .gitignore
│   ├── README.md
│   ├── babel.config.js
│   ├── docs/
│   │   ├── cli.md
│   │   ├── faq.md
│   │   ├── index.md
│   │   ├── installation.md
│   │   ├── preferences.md
│   │   └── usage/
│   │       ├── 1_file_import.md
│   │       ├── 2_live_recording.md
│   │       ├── 3_translations.md
│   │       ├── 4_edit_and_resize.md
│   │       ├── 5_speaker_identification.md
│   │       ├── 5_transcription_viewer.md
│   │       └── _category_.yml
│   ├── docusaurus.config.js
│   ├── i18n/
│   │   └── zh/
│   │       └── docusaurus-plugin-content-docs/
│   │           └── current/
│   │               ├── cli.md
│   │               ├── faq.md
│   │               ├── index.md
│   │               ├── installation.md
│   │               ├── preferences.md
│   │               └── usage/
│   │                   ├── 1_file_import.md
│   │                   ├── 2_live_recording.md
│   │                   ├── 3_translations.md
│   │                   ├── 4_edit_and_resize.md
│   │                   └── _category_.yml
│   ├── package.json
│   ├── sidebars.js
│   ├── src/
│   │   ├── css/
│   │   │   └── custom.css
│   │   └── pages/
│   │       ├── index.module.css
│   │       └── index.tsx
│   ├── static/
│   │   └── .nojekyll
│   └── tsconfig.json
├── entitlements.plist
├── flatpak/
│   └── run-buzz.sh
├── hatch_build.py
├── installer.iss
├── main.py
├── msgfmt.py
├── patches/
│   └── ctc_forced_aligner_windows_mutex.patch
├── pyproject.toml
├── pytest.ini
├── readme/
│   └── README.zh_CN.md
├── share/
│   ├── applications/
│   │   ├── buzz.desktop
│   │   └── io.github.chidiwilliams.Buzz.desktop
│   └── metainfo/
│       └── io.github.chidiwilliams.Buzz.metainfo.xml
├── snap/
│   └── snapcraft.yaml
└── tests/
    ├── __init__.py
    ├── app_main.py
    ├── audio.py
    ├── cache_test.py
    ├── cli_test.py
    ├── conftest.py
    ├── db/
    │   ├── dao/
    │   │   └── transcription_dao_test.py
    │   ├── entity/
    │   │   └── transcription_test.py
    │   └── service/
    │       └── transcription_service_test.py
    ├── gui_test.py
    ├── mock_qt.py
    ├── mock_sounddevice.py
    ├── model_loader.py
    ├── model_loader_test.py
    ├── recording_test.py
    ├── recording_transcriber_test.py
    ├── settings/
    │   └── settings_test.py
    ├── store/
    │   ├── __init__.py
    │   └── keyring_store_test.py
    ├── transcriber/
    │   ├── __init__.py
    │   ├── file_transcriber_queue_worker_test.py
    │   ├── openai_whisper_api_file_transcriber_test.py
    │   ├── recording_transcriber_test.py
    │   ├── transcriber_test.py
    │   ├── transformers_whisper_test.py
    │   ├── whisper_cpp_test.py
    │   └── whisper_file_transcriber_test.py
    ├── transformers_transcriber_test.py
    ├── translator_test.py
    ├── update_checker_test.py
    └── widgets/
        ├── __init__.py
        ├── advanced_settings_dialog_test.py
        ├── audio_meter_widget_test.py
        ├── audio_player_test.py
        ├── conftest.py
        ├── export_transcription_menu_test.py
        ├── file_transcriber_widget_test.py
        ├── hugging_face_search_line_edit_test.py
        ├── import_url_dialog_test.py
        ├── main_window_test.py
        ├── menu_bar_test.py
        ├── model_download_progress_dialog.py
        ├── model_type_combo_box_test.py
        ├── openai_api_key_line_edit_test.py
        ├── preferences_dialog/
        │   ├── __init__.py
        │   ├── folder_watch_preferences_widget_test.py
        │   ├── general_preferences_widget_test.py
        │   ├── models_preferences_widget_test.py
        │   └── preferences_dialog_test.py
        ├── presentation_window_test.py
        ├── recording_transcriber_widget_test.py
        ├── shortcuts_editor_widget_test.py
        ├── speaker_identification_widget_test.py
        ├── transcription_task_folder_watcher_test.py
        ├── transcription_tasks_table_widget_test.py
        ├── transcription_viewer/
        │   ├── __init__.py
        │   ├── transcription_segments_editor_widget_test.py
        │   └── transcription_viewer_widget_additional_test.py
        ├── transcription_viewer_test.py
        ├── update_dialog_test.py
        └── video_player_test.py
Download .txt
SYMBOL INDEX (1925 symbols across 148 files)

FILE: buzz/action.py
  class Action (line 6) | class Action(QAction):
    method setShortcut (line 7) | def setShortcut(
    method get_tooltip (line 15) | def get_tooltip(cls, action: QAction):

FILE: buzz/assets.py
  function get_path (line 11) | def get_path(path: str):

FILE: buzz/buzz.py
  function main (line 53) | def main():

FILE: buzz/cache.py
  class TasksCache (line 12) | class TasksCache:
    method __init__ (line 13) | def __init__(self, cache_dir=user_cache_dir("Buzz")):
    method save (line 19) | def save(self, tasks: List[FileTranscriptionTask]):
    method load (line 22) | def load(self) -> List[FileTranscriptionTask]:
    method load_json_tasks (line 39) | def load_json_tasks(self) -> List[FileTranscriptionTask]:
    method save_json_tasks (line 61) | def save_json_tasks(self, tasks: List[FileTranscriptionTask]):
    method get_task_path (line 72) | def get_task_path(self, task_id: int):
    method clear (line 77) | def clear(self):

FILE: buzz/cli.py
  class CommandLineError (line 26) | class CommandLineError(Exception):
    method __init__ (line 27) | def __init__(self, message: str):
  class CommandLineModelType (line 31) | class CommandLineModelType(enum.Enum):
  function parse_command_line (line 39) | def parse_command_line(app: Application):
  function is_url (line 48) | def is_url(path: str) -> bool:
  function parse (line 52) | def parse(app: Application, parser: QCommandLineParser):
  function parse_enum_option (line 243) | def parse_enum_option(
  function join_values (line 252) | def join_values(enum_class: typing.Type[enum.Enum]) -> str:

FILE: buzz/conn.py
  class ConnWriter (line 6) | class ConnWriter:
    method __init__ (line 7) | def __init__(self, conn: Connection):
    method write (line 10) | def write(self, s: str):
  function pipe_stderr (line 15) | def pipe_stderr(conn: Connection):

FILE: buzz/cuda_setup.py
  function _get_nvidia_package_lib_dirs (line 24) | def _get_nvidia_package_lib_dirs() -> list[Path]:
  function _setup_windows_dll_directories (line 67) | def _setup_windows_dll_directories():
  function _preload_linux_libraries (line 77) | def _preload_linux_libraries():
  function setup_cuda_libraries (line 114) | def setup_cuda_libraries():

FILE: buzz/db/dao/dao.py
  class DAO (line 12) | class DAO(ABC, Generic[T]):
    method __init__ (line 16) | def __init__(self, table: str, db: QSqlDatabase):
    method insert (line 20) | def insert(self, record: T):
    method find_by_id (line 37) | def find_by_id(self, id: Any) -> T | None:
    method to_entity (line 43) | def to_entity(self, record: QSqlRecord) -> T:
    method _execute (line 47) | def _execute(self, query: QSqlQuery) -> T | None:
    method _execute_all (line 54) | def _execute_all(self, query: QSqlQuery) -> List[T]:
    method _create_query (line 62) | def _create_query(self):

FILE: buzz/db/dao/transcription_dao.py
  class TranscriptionDAO (line 12) | class TranscriptionDAO(DAO[Transcription]):
    method __init__ (line 15) | def __init__(self, db: QSqlDatabase):
    method create_transcription (line 18) | def create_transcription(self, task: FileTranscriptionTask):
    method copy_transcription (line 107) | def copy_transcription(self, id: UUID) -> UUID:
    method update_transcription_as_started (line 172) | def update_transcription_as_started(self, id: UUID):
    method update_transcription_as_failed (line 188) | def update_transcription_as_failed(self, id: UUID, error: str):
    method update_transcription_as_canceled (line 205) | def update_transcription_as_canceled(self, id: UUID):
    method update_transcription_progress (line 221) | def update_transcription_progress(self, id: UUID, progress: float):
    method update_transcription_as_completed (line 237) | def update_transcription_as_completed(self, id: UUID):
    method update_transcription_file_and_name (line 253) | def update_transcription_file_and_name(self, id: UUID, file_path: str,...
    method update_transcription_name (line 269) | def update_transcription_name(self, id: UUID, name: str):
    method update_transcription_notes (line 286) | def update_transcription_notes(self, id: UUID, notes: str):
    method reset_transcription_for_restart (line 303) | def reset_transcription_for_restart(self, id: UUID):

FILE: buzz/db/dao/transcription_segment_dao.py
  class TranscriptionSegmentDAO (line 10) | class TranscriptionSegmentDAO(DAO[TranscriptionSegment]):
    method __init__ (line 14) | def __init__(self, db: QSqlDatabase):
    method get_segments (line 17) | def get_segments(self, transcription_id: UUID) -> List[TranscriptionSe...
    method delete_segments (line 28) | def delete_segments(self, transcription_id: UUID):
    method update_segment_translation (line 40) | def update_segment_translation(self, segment_id: int, translation: str):

FILE: buzz/db/db.py
  function setup_app_db (line 16) | def setup_app_db() -> QSqlDatabase:
  function setup_test_db (line 22) | def setup_test_db() -> QSqlDatabase:
  function _setup_db (line 26) | def _setup_db(path: str) -> QSqlDatabase:
  function close_app_db (line 46) | def close_app_db():

FILE: buzz/db/entity/entity.py
  class Entity (line 6) | class Entity(ABC):
    method from_record (line 8) | def from_record(cls, record: QSqlRecord):

FILE: buzz/db/entity/transcription.py
  class Transcription (line 13) | class Transcription(Entity):
    method id_as_uuid (line 37) | def id_as_uuid(self):
    method status_as_status (line 41) | def status_as_status(self):
    method get_output_file_path (line 44) | def get_output_file_path(

FILE: buzz/db/entity/transcription_segment.py
  class TranscriptionSegment (line 7) | class TranscriptionSegment(Entity):

FILE: buzz/db/helpers.py
  function copy_transcriptions_from_json_to_sqlite (line 10) | def copy_transcriptions_from_json_to_sqlite(conn: Connection):
  function run_sqlite_migrations (line 71) | def run_sqlite_migrations(db: Connection):
  function mark_in_progress_and_queued_transcriptions_as_canceled (line 79) | def mark_in_progress_and_queued_transcriptions_as_canceled(conn: Connect...

FILE: buzz/db/migrator.py
  function dumb_migrate_db (line 17) | def dumb_migrate_db(db, schema, allow_deletions=False):
  class DBMigrator (line 50) | class DBMigrator:
    method __init__ (line 51) | def __init__(self, db, schema, allow_deletions=False):
    method log_execute (line 62) | def log_execute(self, msg, sql, args=None):
    method __enter__ (line 77) | def __enter__(self):
    method __exit__ (line 91) | def __exit__(self, exc_type, exc_value, exc_tb):
    method migrate (line 116) | def migrate(self):
    method _migrate_pragma (line 258) | def _migrate_pragma(self, pragma):
  function _left_pad (line 271) | def _left_pad(text, indent="    "):
  function normalise_sql (line 276) | def normalise_sql(sql):

FILE: buzz/db/service/transcription_service.py
  class TranscriptionService (line 10) | class TranscriptionService:
    method __init__ (line 11) | def __init__(
    method create_transcription (line 19) | def create_transcription(self, task):
    method copy_transcription (line 22) | def copy_transcription(self, id: UUID) -> UUID:
    method update_transcription_as_started (line 25) | def update_transcription_as_started(self, id: UUID):
    method update_transcription_as_failed (line 28) | def update_transcription_as_failed(self, id: UUID, error: str):
    method update_transcription_as_canceled (line 31) | def update_transcription_as_canceled(self, id: UUID):
    method update_transcription_progress (line 34) | def update_transcription_progress(self, id: UUID, progress: float):
    method update_transcription_as_completed (line 37) | def update_transcription_as_completed(self, id: UUID, segments: List[S...
    method update_transcription_file_and_name (line 50) | def update_transcription_file_and_name(self, id: UUID, file_path: str,...
    method update_transcription_name (line 53) | def update_transcription_name(self, id: UUID, name: str):
    method update_transcription_notes (line 56) | def update_transcription_notes(self, id: UUID, notes: str):
    method reset_transcription_for_restart (line 59) | def reset_transcription_for_restart(self, id: UUID):
    method replace_transcription_segments (line 62) | def replace_transcription_segments(self, id: UUID, segments: List[Segm...
    method get_transcription_segments (line 75) | def get_transcription_segments(self, transcription_id: UUID):
    method update_segment_translation (line 78) | def update_segment_translation(self, segment_id: int, translation: str):

FILE: buzz/dialogs.py
  function show_model_download_error_dialog (line 4) | def show_model_download_error_dialog(parent: QWidget, error: str):

FILE: buzz/file_transcriber_queue_worker.py
  function _patched_run (line 31) | def _patched_run(*args, **kwargs):
  function _patched_check_output (line 41) | def _patched_check_output(*args, **kwargs):
  class FileTranscriberQueueWorker (line 66) | class FileTranscriberQueueWorker(QObject):
    method __init__ (line 81) | def __init__(self, parent: Optional[QObject] = None):
    method run (line 93) | def run(self):
    method _on_task_finished (line 215) | def _on_task_finished(self):
    method add_task (line 221) | def add_task(self, task: FileTranscriptionTask):
    method cancel_task (line 232) | def cancel_task(self, task_id: UUID):
    method on_task_error (line 244) | def on_task_error(self, error: str):
    method on_task_progress (line 259) | def on_task_progress(self, progress: Tuple[int, int]):
    method on_task_download_progress (line 263) | def on_task_download_progress(self, fraction_downloaded: float):
    method on_task_completed (line 268) | def on_task_completed(self, segments: List[Segment]):
    method stop (line 279) | def stop(self):

FILE: buzz/model_loader.py
  function _hf_session_factory (line 46) | def _hf_session_factory() -> requests.Session:
  function _windows_create_symlink (line 68) | def _windows_create_symlink(src: Path, dst: Path, new_blob: bool = False...
  class WhisperModelSize (line 110) | class WhisperModelSize(str, enum.Enum):
    method to_faster_whisper_model_size (line 126) | def to_faster_whisper_model_size(self) -> str:
    method to_whisper_cpp_model_size (line 131) | def to_whisper_cpp_model_size(self) -> str:
    method __str__ (line 136) | def __str__(self):
  function get_expected_whisper_model_size (line 155) | def get_expected_whisper_model_size(size: WhisperModelSize) -> Optional[...
  class ModelType (line 159) | class ModelType(enum.Enum):
    method supports_initial_prompt (line 167) | def supports_initial_prompt(self):
    method is_available (line 175) | def is_available(self):
    method is_manually_downloadable (line 185) | def is_manually_downloadable(self):
  function map_language_to_mms (line 244) | def map_language_to_mms(language_code: str) -> str:
  function is_mms_model (line 257) | def is_mms_model(model_id: str) -> bool:
  class TranscriptionModel (line 287) | class TranscriptionModel:
    method __init__ (line 288) | def __init__(
    method __str__ (line 298) | def __str__(self):
    method is_deletable (line 313) | def is_deletable(self):
    method open_file_location (line 320) | def open_file_location(self):
    method default (line 332) | def default():
    method open_path (line 339) | def open_path(path: str):
    method delete_local_file (line 346) | def delete_local_file(self):
    method get_local_model_path (line 389) | def get_local_model_path(self) -> Optional[str]:
  function get_whisper_cpp_file_path (line 446) | def get_whisper_cpp_file_path(size: WhisperModelSize) -> str:
  function get_whisper_file_path (line 471) | def get_whisper_file_path(size: WhisperModelSize) -> str:
  class HuggingfaceDownloadMonitor (line 481) | class HuggingfaceDownloadMonitor:
    method __init__ (line 482) | def __init__(self, model_root: str, progress: pyqtSignal(tuple), total...
    method set_download_roots (line 491) | def set_download_roots(self):
    method clean_tmp_files (line 498) | def clean_tmp_files(self):
    method monitor_file_size (line 503) | def monitor_file_size(self):
    method start_monitoring (line 530) | def start_monitoring(self):
    method stop_monitoring (line 535) | def stop_monitoring(self):
  function get_file_size (line 543) | def get_file_size(url):
  function download_from_huggingface (line 549) | def download_from_huggingface(
  function download_faster_whisper_model (line 612) | def download_faster_whisper_model(
  class ModelDownloader (line 655) | class ModelDownloader(QRunnable):
    class Signals (line 656) | class Signals(QObject):
    method __init__ (line 661) | def __init__(self, model: TranscriptionModel, custom_model_url: Option...
    method run (line 671) | def run(self) -> None:
    method download_model_to_path (line 791) | def download_model_to_path(
    method download_model (line 811) | def download_model(
    method cancel (line 1014) | def cancel(self):

FILE: buzz/paths.py
  function file_path_as_title (line 4) | def file_path_as_title(file_path: str):

FILE: buzz/recording.py
  class RecordingAmplitudeListener (line 9) | class RecordingAmplitudeListener(QObject):
    method __init__ (line 16) | def __init__(
    method start_recording (line 27) | def start_recording(self):
    method stop_recording (line 41) | def stop_recording(self):
    method stream_callback (line 47) | def stream_callback(self, in_data: np.ndarray, frame_count, time_info,...

FILE: buzz/schema.sql
  type transcription (line 1) | CREATE TABLE transcription (
  type transcription_segment (line 25) | CREATE TABLE transcription_segment (
  type idx_transcription_id (line 34) | CREATE INDEX idx_transcription_id ON transcription_segment(transcription...

FILE: buzz/settings/recording_transcriber_mode.py
  class RecordingTranscriberMode (line 4) | class RecordingTranscriberMode(Enum):

FILE: buzz/settings/settings.py
  class Settings (line 11) | class Settings:
    method __init__ (line 12) | def __init__(self, application=""):
    class Key (line 16) | class Key(enum.Enum):
    method get_user_identifier (line 88) | def get_user_identifier(self) -> str:
    method set_value (line 95) | def set_value(self, key: Key, value: typing.Any) -> None:
    method save_custom_model_id (line 98) | def save_custom_model_id(self, model) -> None:
    method load_custom_model_id (line 112) | def load_custom_model_id(self, model) -> str:
    method value (line 128) | def value(
    method clear (line 149) | def clear(self):
    method begin_group (line 152) | def begin_group(self, group: Key) -> None:
    method end_group (line 155) | def end_group(self) -> None:
    method sync (line 158) | def sync(self):
    method get_default_export_file_template (line 161) | def get_default_export_file_template(self) -> str:

FILE: buzz/settings/shortcut.py
  class Shortcut (line 6) | class Shortcut(str, enum.Enum):
    method __new__ (line 10) | def __new__(cls, sequence: str, description: str):
    method get_default_shortcuts (line 42) | def get_default_shortcuts() -> typing.Dict[str, str]:

FILE: buzz/settings/shortcuts.py
  class Shortcuts (line 7) | class Shortcuts:
    method __init__ (line 8) | def __init__(self, settings: Settings):
    method get (line 11) | def get(self, shortcut: Shortcut) -> str:
    method set (line 15) | def set(self, shortcut: Shortcut, sequence: str) -> None:
    method clear (line 20) | def clear(self) -> None:
    method get_custom_shortcuts (line 23) | def get_custom_shortcuts(self) -> typing.Dict[str, str]:

FILE: buzz/store/keyring_store.py
  class Key (line 14) | class Key(enum.Enum):
  function _is_linux (line 18) | def _is_linux() -> bool:
  function _get_secrets_file_path (line 22) | def _get_secrets_file_path() -> str:
  function _get_portal_secret (line 31) | def _get_portal_secret() -> bytes | None:
  function _derive_key (line 99) | def _derive_key(master_secret: bytes, key_name: str) -> bytes:
  function _encrypt_value (line 111) | def _encrypt_value(value: str, key: bytes) -> str:
  function _decrypt_value (line 121) | def _decrypt_value(encrypted: str, key: bytes) -> str:
  function _load_local_secrets (line 129) | def _load_local_secrets() -> dict:
  function _save_local_secrets (line 141) | def _save_local_secrets(secrets: dict) -> None:
  function _get_portal_password (line 153) | def _get_portal_password(key: Key) -> str | None:
  function _set_portal_password (line 172) | def _set_portal_password(key: Key, password: str) -> bool:
  function _delete_portal_password (line 191) | def _delete_portal_password(key: Key) -> bool:
  function get_password (line 201) | def get_password(key: Key) -> str | None:
  function set_password (line 221) | def set_password(username: Key, password: str) -> None:
  function delete_password (line 231) | def delete_password(key: Key) -> None:

FILE: buzz/transcriber/file_transcriber.py
  class FileTranscriber (line 27) | class FileTranscriber(QObject):
    method __init__ (line 34) | def __init__(self, task: FileTranscriptionTask, parent: Optional["QObj...
    method run (line 39) | def run(self):
    method on_download_progress (line 169) | def on_download_progress(self, data: dict):
    method transcribe (line 174) | def transcribe(self) -> List[Segment]:
    method stop (line 178) | def stop(self):
  function write_output (line 182) | def write_output(
  function to_timestamp (line 229) | def to_timestamp(ms: float, ms_separator=".") -> str:
  function is_video_file (line 241) | def is_video_file(path: str) -> bool:

FILE: buzz/transcriber/local_whisper_cpp_server_transcriber.py
  class LocalWhisperCppServerTranscriber (line 17) | class LocalWhisperCppServerTranscriber(OpenAIWhisperAPIFileTranscriber):
    method __init__ (line 19) | def __init__(self, task: FileTranscriptionTask, parent: Optional["QObj...
    method transcribe (line 71) | def transcribe(self) -> List[Segment]:
    method stop (line 77) | def stop(self):
    method __del__ (line 93) | def __del__(self):

FILE: buzz/transcriber/openai_whisper_api_file_transcriber.py
  function append_segment (line 19) | def append_segment(result, txt: bytes, start: int, end: int):
  class OpenAIWhisperAPIFileTranscriber (line 38) | class OpenAIWhisperAPIFileTranscriber(FileTranscriber):
    method __init__ (line 39) | def __init__(self, task: FileTranscriptionTask, parent: Optional["QObj...
    method transcribe (line 58) | def transcribe(self) -> List[Segment]:
    method get_value (line 186) | def get_value(segment, key, default=None):
    method get_segments_for_file (line 193) | def get_segments_for_file(self, file: str, offset_ms: int = 0):
    method stop (line 292) | def stop(self):

FILE: buzz/transcriber/recording_transcriber.py
  class RecordingTranscriber (line 37) | class RecordingTranscriber(QObject):
    method __init__ (line 47) | def __init__(
    method start (line 82) | def start(self):
    method get_device_sample_rate (line 338) | def get_device_sample_rate(device_id: Optional[int]) -> int:
    method stream_callback (line 353) | def stream_callback(self, in_data: np.ndarray, frame_count, time_info,...
    method find_silence_cut_point (line 365) | def find_silence_cut_point(samples: np.ndarray, sample_rate: int,
    method amplitude (line 397) | def amplitude(arr: np.ndarray):
    method _drain_stderr (line 400) | def _drain_stderr(self):
    method stop_recording (line 405) | def stop_recording(self):
    method start_local_whisper_server (line 415) | def start_local_whisper_server(self):
    method __del__ (line 518) | def __del__(self):

FILE: buzz/transcriber/transcriber.py
  class Task (line 18) | class Task(enum.Enum):
  class Segment (line 30) | class Segment:
  class TranscriptionOptions (line 142) | class TranscriptionOptions:
  function humanize_language (line 161) | def humanize_language(language: str) -> str:
  class FileTranscriptionOptions (line 168) | class FileTranscriptionOptions:
  class FileTranscriptionTask (line 176) | class FileTranscriptionTask:
    class Status (line 177) | class Status(enum.Enum):
    class Source (line 184) | class Source(enum.Enum):
  class OutputFormat (line 211) | class OutputFormat(enum.Enum):
  class Stopped (line 217) | class Stopped(Exception):
  function get_output_file_path (line 227) | def get_output_file_path(

FILE: buzz/transcriber/whisper_cpp.py
  class WhisperCpp (line 35) | class WhisperCpp:
    method transcribe (line 37) | def transcribe(task: FileTranscriptionTask) -> List[Segment]:

FILE: buzz/transcriber/whisper_file_transcriber.py
  function check_file_has_audio_stream (line 40) | def check_file_has_audio_stream(file_path: str) -> None:
  class WhisperFileTranscriber (line 56) | class WhisperFileTranscriber(FileTranscriber):
    method __init__ (line 65) | def __init__(
    method transcribe (line 76) | def transcribe(self) -> List[Segment]:
    method transcribe_whisper (line 145) | def transcribe_whisper(
    method transcribe_whisper_cpp (line 211) | def transcribe_whisper_cpp(cls, task: FileTranscriptionTask) -> List[S...
    method transcribe_hugging_face (line 215) | def transcribe_hugging_face(cls, task: FileTranscriptionTask) -> List[...
    method transcribe_faster_whisper (line 251) | def transcribe_faster_whisper(cls, task: FileTranscriptionTask) -> Lis...
    method transcribe_openai_whisper (line 327) | def transcribe_openai_whisper(cls, task: FileTranscriptionTask) -> Lis...
    method stop (line 388) | def stop(self):
    method read_line (line 417) | def read_line(self, pipe: Connection):

FILE: buzz/transformers_whisper.py
  function is_intel_mac (line 21) | def is_intel_mac() -> bool:
  function is_peft_model (line 26) | def is_peft_model(model_id: str) -> bool:
  class PipelineWithProgress (line 31) | class PipelineWithProgress(AutomaticSpeechRecognitionPipeline):  # pragm...
    method chunk_iter (line 34) | def chunk_iter(inputs, feature_extractor, chunk_len, stride_left, stri...
    method preprocess (line 60) | def preprocess(self, inputs, chunk_length_s=0, stride_length_s=None):
  class TransformersTranscriber (line 183) | class TransformersTranscriber:
    method __init__ (line 186) | def __init__(self, model_id: str):
    method is_mms_model (line 192) | def is_mms_model(self) -> bool:
    method is_peft_model (line 197) | def is_peft_model(self) -> bool:
    method transcribe (line 201) | def transcribe(
    method _transcribe_whisper (line 214) | def _transcribe_whisper(
    method _load_peft_model (line 317) | def _load_peft_model(self, device: str, torch_dtype):
    method _get_peft_repo_id (line 385) | def _get_peft_repo_id(self) -> str:
    method _get_mms_repo_id (line 404) | def _get_mms_repo_id(self) -> str:
    method _transcribe_mms (line 428) | def _transcribe_mms(

FILE: buzz/translator.py
  class Translator (line 20) | class Translator(QObject):
    method __init__ (line 24) | def __init__(
    method _translate_single (line 59) | def _translate_single(self, transcript: str, transcript_id: int) -> Tu...
    method _translate_batch (line 82) | def _translate_batch(self, items: List[Tuple[str, int]]) -> List[Tuple...
    method _parse_batch_response (line 131) | def _parse_batch_response(response: str, expected_count: int) -> List[...
    method start (line 147) | def start(self):
    method on_transcription_options_changed (line 188) | def on_transcription_options_changed(
    method enqueue (line 193) | def enqueue(self, transcript: str, transcript_id: Optional[int] = None):
    method stop (line 196) | def stop(self):

FILE: buzz/update_checker.py
  class UpdateInfo (line 15) | class UpdateInfo:
  class UpdateChecker (line 20) | class UpdateChecker(QObject):
    method __init__ (line 27) | def __init__(
    method should_check_for_updates (line 42) | def should_check_for_updates(self) -> bool:
    method check_for_updates (line 69) | def check_for_updates(self) -> None:
    method _on_reply_finished (line 80) | def _on_reply_finished(self, reply: QNetworkReply) -> None:
    method _get_download_url (line 131) | def _get_download_url(self, download_urls: dict) -> list:
    method _is_newer_version (line 147) | def _is_newer_version(self, remote_version: str) -> bool:

FILE: buzz/whisper_audio.py
  function load_audio (line 19) | def load_audio(file: str, sr: int = SAMPLE_RATE):

FILE: buzz/widgets/about_dialog.py
  class AboutDialog (line 25) | class AboutDialog(QDialog):
    method __init__ (line 31) | def __init__(
    method on_click_check_for_updates (line 103) | def on_click_check_for_updates(self):
    method on_click_show_logs (line 108) | def on_click_show_logs(self):
    method on_latest_release_reply (line 112) | def on_latest_release_reply(self, reply: QNetworkReply):
    method is_version_lower (line 123) | def is_version_lower(version_a: str, version_b: str):

FILE: buzz/widgets/application.py
  class Application (line 25) | class Application(QApplication):
    method __init__ (line 28) | def __init__(self, argv: list) -> None:
    method show_main_window (line 96) | def show_main_window(self):
    method add_task (line 100) | def add_task(self, task: FileTranscriptionTask, quit_on_complete: bool...
    method close_database (line 104) | def close_database(self):

FILE: buzz/widgets/audio_devices_combo_box.py
  class AudioDevicesComboBox (line 8) | class AudioDevicesComboBox(QComboBox):
    method __init__ (line 14) | def __init__(self, parent: Optional[QWidget] = None) -> None:
    method get_audio_devices (line 26) | def get_audio_devices(self) -> List[Tuple[int, str]]:
    method on_index_changed (line 43) | def on_index_changed(self, index: int):
    method get_default_device_id (line 46) | def get_default_device_id(self) -> Optional[int]:

FILE: buzz/widgets/audio_meter_widget.py
  class AudioMeterWidget (line 11) | class AudioMeterWidget(QWidget):
    method __init__ (line 22) | def __init__(self, parent: Optional[QWidget] = None):
    method paintEvent (line 46) | def paintEvent(self, event: QtGui.QPaintEvent) -> None:
    method reset_amplitude (line 84) | def reset_amplitude(self):
    method update_amplitude (line 90) | def update_amplitude(self, amplitude: float):
    method update_average_amplitude (line 96) | def update_average_amplitude(self, amplitude: float):
    method update_queue_size (line 100) | def update_queue_size(self, size: int):

FILE: buzz/widgets/audio_player.py
  class AudioPlayer (line 14) | class AudioPlayer(QWidget):
    method __init__ (line 17) | def __init__(self, file_path: str):
    method on_duration_changed (line 112) | def on_duration_changed(self, duration_ms: int):
    method on_position_changed (line 117) | def on_position_changed(self, position_ms: int):
    method on_playback_state_changed (line 140) | def on_playback_state_changed(self, state: QMediaPlayer.PlaybackState):
    method on_media_status_changed (line 146) | def on_media_status_changed(self, status: QMediaPlayer.MediaStatus):
    method on_error_occurred (line 154) | def on_error_occurred(self, error: QMediaPlayer.Error, error_string: s...
    method set_invalid_media (line 157) | def set_invalid_media(self, invalid_media: bool):
    method toggle_play (line 169) | def toggle_play(self):
    method set_range (line 175) | def set_range(self, range_ms: Tuple[int, int]):
    method clear_range (line 185) | def clear_range(self):
    method _reset_looping_flag (line 189) | def _reset_looping_flag(self):
    method on_slider_moved (line 193) | def on_slider_moved(self, position_ms: int):
    method on_slider_pressed (line 202) | def on_slider_pressed(self):
    method on_slider_released (line 206) | def on_slider_released(self):
    method set_position (line 212) | def set_position(self, position_ms: int):
    method update_time_label (line 215) | def update_time_label(self):
    method stop (line 220) | def stop(self):
    method closeEvent (line 223) | def closeEvent(self, a0: QtGui.QCloseEvent) -> None:
    method hideEvent (line 227) | def hideEvent(self, a0: QtGui.QHideEvent) -> None:

FILE: buzz/widgets/form_label.py
  class FormLabel (line 7) | class FormLabel(QLabel):
    method __init__ (line 8) | def __init__(self, name: str, parent: Optional[QWidget], *args) -> None:

FILE: buzz/widgets/icon.py
  class Icon (line 7) | class Icon(QIcon):
    method __init__ (line 11) | def __init__(self, path: str, parent: QWidget):
    method create_default_pixmap (line 23) | def create_default_pixmap(self, path, color):
    method create_disabled_pixmap (line 31) | def create_disabled_pixmap(self, pixmap, color):
    method get_color (line 45) | def get_color(self) -> QColor:
  class PlayIcon (line 52) | class PlayIcon(Icon):
    method __init__ (line 53) | def __init__(self, parent: QWidget):
  class PauseIcon (line 57) | class PauseIcon(Icon):
    method __init__ (line 58) | def __init__(self, parent: QWidget):
  class UndoIcon (line 62) | class UndoIcon(Icon):
    method __init__ (line 63) | def __init__(self, parent: QWidget):
  class RedoIcon (line 67) | class RedoIcon(Icon):
    method __init__ (line 68) | def __init__(self, parent: QWidget):
  class FileDownloadIcon (line 72) | class FileDownloadIcon(Icon):
    method __init__ (line 73) | def __init__(self, parent: QWidget):
  class TranslateIcon (line 77) | class TranslateIcon(Icon):
    method __init__ (line 78) | def __init__(self, parent: QWidget):
  class ResizeIcon (line 81) | class ResizeIcon(Icon):
    method __init__ (line 82) | def __init__(self, parent: QWidget):
  class SpeakerIdentificationIcon (line 85) | class SpeakerIdentificationIcon(Icon):
    method __init__ (line 86) | def __init__(self, parent: QWidget):
  class VisibilityIcon (line 89) | class VisibilityIcon(Icon):
    method __init__ (line 90) | def __init__(self, parent: QWidget):
  class ScrollToCurrentIcon (line 96) | class ScrollToCurrentIcon(Icon):
    method __init__ (line 97) | def __init__(self, parent: QWidget):
  class NewWindowIcon (line 102) | class NewWindowIcon(Icon):
    method __init__ (line 103) | def __init__(self, parent: QWidget):
  class FullscreenIcon (line 107) | class FullscreenIcon(Icon):
    method __init__ (line 108) | def __init__(self, parent: QWidget):
  class ColorBackgroundIcon (line 112) | class ColorBackgroundIcon(Icon):
    method __init__ (line 113) | def __init__(self, parent: QWidget):
  class TextColorIcon (line 117) | class TextColorIcon(Icon):
    method __init__ (line 118) | def __init__(self, parent: QWidget):

FILE: buzz/widgets/icon_presentation.py
  class PresentationIcon (line 7) | class PresentationIcon:
    method __init__ (line 9) | def __init__(self, parent, svg_path: str, color: str = None):
    method get_default_color (line 15) | def get_default_color(self) -> str:
    method get_icon (line 22) | def get_icon(self) -> QIcon:

FILE: buzz/widgets/import_url_dialog.py
  class ImportURLDialog (line 10) | class ImportURLDialog(QDialog):
    method __init__ (line 16) | def __init__(self, parent: Optional[QWidget] = None):
    method accept (line 38) | def accept(self):
    method prompt (line 48) | def prompt(cls, parent: Optional[QWidget] = None) -> Optional[str]:

FILE: buzz/widgets/line_edit.py
  class LineEdit (line 7) | class LineEdit(QLineEdit):
    method __init__ (line 8) | def __init__(self, default_text: str = "", parent: Optional[QWidget] =...

FILE: buzz/widgets/main_window.py
  class MainWindow (line 56) | class MainWindow(QMainWindow):
    method __init__ (line 60) | def __init__(self, transcription_service: TranscriptionService):
    method on_preferences_changed (line 168) | def on_preferences_changed(self, preferences: Preferences):
    method save_preferences (line 174) | def save_preferences(self, preferences: Preferences):
    method load_preferences (line 179) | def load_preferences(self, settings: Settings):
    method dragEnterEvent (line 185) | def dragEnterEvent(self, event):
    method dropEvent (line 192) | def dropEvent(self, event):
    method on_file_transcriber_triggered (line 196) | def on_file_transcriber_triggered(
    method on_clear_history_action_triggered (line 221) | def on_clear_history_action_triggered(self):
    method on_stop_transcription_action_triggered (line 246) | def on_stop_transcription_action_triggered(self):
    method on_new_transcription_action_triggered (line 257) | def on_new_transcription_action_triggered(self):
    method on_new_url_transcription_action_triggered (line 266) | def on_new_url_transcription_action_triggered(self):
    method on_import_folder_action_triggered (line 271) | def on_import_folder_action_triggered(self):
    method open_file_transcriber_widget (line 285) | def open_file_transcriber_widget(
    method on_openai_access_token_changed (line 303) | def on_openai_access_token_changed(access_token: str):
    method open_transcript_viewer (line 312) | def open_transcript_viewer(self):
    method on_table_selection_changed (line 318) | def on_table_selection_changed(self):
    method should_enable_open_transcript_action (line 329) | def should_enable_open_transcript_action(self):
    method can_open_transcript (line 339) | def can_open_transcript(transcription: Transcription) -> bool:
    method should_enable_stop_transcription_action (line 345) | def should_enable_stop_transcription_action(self):
    method should_enable_clear_history_action (line 353) | def should_enable_clear_history_action(self):
    method selected_tasks_have_status (line 362) | def selected_tasks_have_status(self, statuses: List[FileTranscriptionT...
    method on_table_double_clicked (line 374) | def on_table_double_clicked(self, index: QModelIndex):
    method open_transcription_viewer (line 380) | def open_transcription_viewer(self, transcription: Transcription):
    method add_task (line 391) | def add_task(self, task: FileTranscriptionTask):
    method on_transcriptions_updated (line 396) | def on_transcriptions_updated(self):
    method on_task_started (line 399) | def on_task_started(self, task: FileTranscriptionTask):
    method on_task_progress (line 403) | def on_task_progress(self, task: FileTranscriptionTask, progress: float):
    method on_task_download_progress (line 407) | def on_task_download_progress(
    method on_task_completed (line 413) | def on_task_completed(self, task: FileTranscriptionTask, segments: Lis...
    method on_task_error (line 430) | def on_task_error(self, task: FileTranscriptionTask, error: str):
    method on_shortcuts_changed (line 438) | def on_shortcuts_changed(self):
    method resizeEvent (line 442) | def resizeEvent(self, event):
    method closeEvent (line 445) | def closeEvent(self, event: QtGui.QCloseEvent) -> None:
    method save_geometry (line 491) | def save_geometry(self):
    method load_geometry (line 496) | def load_geometry(self):
    method _init_update_checker (line 506) | def _init_update_checker(self):
    method _on_update_available (line 514) | def _on_update_available(self, update_info: UpdateInfo):
    method on_update_action_triggered (line 519) | def on_update_action_triggered(self):

FILE: buzz/widgets/main_window_toolbar.py
  class MainWindowToolbar (line 25) | class MainWindowToolbar(ToolBar):
    method __init__ (line 34) | def __init__(self, shortcuts: Shortcuts, parent: Optional[QWidget]):
    method reset_shortcuts (line 105) | def reset_shortcuts(self):
    method on_record_action_triggered (line 116) | def on_record_action_triggered(self):
    method set_stop_transcription_action_enabled (line 122) | def set_stop_transcription_action_enabled(self, enabled: bool):
    method set_open_transcript_action_enabled (line 125) | def set_open_transcript_action_enabled(self, enabled: bool):
    method set_clear_history_action_enabled (line 128) | def set_clear_history_action_enabled(self, enabled: bool):
    method set_update_available (line 131) | def set_update_available(self, available: bool):

FILE: buzz/widgets/menu_bar.py
  class MenuBar (line 20) | class MenuBar(QMenuBar):
    method __init__ (line 29) | def __init__(
    method on_about_action_triggered (line 75) | def on_about_action_triggered(self):
    method on_preferences_action_triggered (line 79) | def on_preferences_action_triggered(self):
    method on_preferences_dialog_finished (line 92) | def on_preferences_dialog_finished(self, result):
    method on_help_action_triggered (line 98) | def on_help_action_triggered(self):
    method reset_shortcuts (line 101) | def reset_shortcuts(self):

FILE: buzz/widgets/model_download_progress_dialog.py
  class ModelDownloadProgressDialog (line 18) | class ModelDownloadProgressDialog(QProgressDialog):
    method __init__ (line 19) | def __init__(
    method update_label_text (line 49) | def update_label_text(self, fraction_completed: float):
    method set_value (line 61) | def set_value(self, fraction_completed: float):
    method cancel (line 69) | def cancel(self) -> None:

FILE: buzz/widgets/model_type_combo_box.py
  class ModelTypeComboBox (line 9) | class ModelTypeComboBox(QComboBox):
    method __init__ (line 12) | def __init__(
    method on_text_changed (line 32) | def on_text_changed(self, text: str):

FILE: buzz/widgets/openai_api_key_line_edit.py
  class OpenAIAPIKeyLineEdit (line 12) | class OpenAIAPIKeyLineEdit(LineEdit):
    method __init__ (line 16) | def __init__(self, key: str, parent: Optional[QWidget] = None):
    method focusOutEvent (line 36) | def focusOutEvent(self, event):
    method on_toggle_show_action_triggered (line 40) | def on_toggle_show_action_triggered(self):
    method on_openai_api_key_changed (line 48) | def on_openai_api_key_changed(self, key: str):

FILE: buzz/widgets/preferences_dialog/folder_watch_preferences_widget.py
  class FolderWatchPreferencesWidget (line 32) | class FolderWatchPreferencesWidget(QWidget):
    method __init__ (line 35) | def __init__(
    method on_click_browse_input_folder (line 114) | def on_click_browse_input_folder(self):
    method on_input_folder_changed (line 119) | def on_input_folder_changed(self, folder):
    method on_click_browse_output_folder (line 123) | def on_click_browse_output_folder(self):
    method on_output_folder_changed (line 128) | def on_output_folder_changed(self, folder):
    method _set_settings_enabled (line 132) | def _set_settings_enabled(self, enabled: bool):
    method on_enable_changed (line 140) | def on_enable_changed(self, state: int):
    method on_delete_processed_files_changed (line 146) | def on_delete_processed_files_changed(self, state: int):
    method on_transcription_options_changed (line 150) | def on_transcription_options_changed(

FILE: buzz/widgets/preferences_dialog/general_preferences_widget.py
  class GeneralPreferencesWidget (line 52) | class GeneralPreferencesWidget(QWidget):
    method __init__ (line 55) | def __init__(
    method on_default_export_file_name_changed (line 226) | def on_default_export_file_name_changed(self, text: str):
    method update_test_openai_api_key_button (line 229) | def update_test_openai_api_key_button(self):
    method on_click_test_openai_api_key_button (line 232) | def on_click_test_openai_api_key_button(self):
    method on_test_openai_api_key_success (line 243) | def on_test_openai_api_key_success(self):
    method on_test_openai_api_key_failure (line 251) | def on_test_openai_api_key_failure(self, error: str):
    method on_openai_api_key_changed (line 255) | def on_openai_api_key_changed(self, key: str):
    method on_openai_api_key_focus_out (line 260) | def on_openai_api_key_focus_out(self):
    method on_custom_openai_base_url_changed (line 268) | def on_custom_openai_base_url_changed(self, text: str):
    method on_openai_api_model_changed (line 271) | def on_openai_api_model_changed(self, text: str):
    method on_recording_export_enable_changed (line 274) | def on_recording_export_enable_changed(self, state: int):
    method on_click_browse_export_folder (line 285) | def on_click_browse_export_folder(self):
    method on_recording_export_folder_changed (line 290) | def on_recording_export_folder_changed(self, folder):
    method on_language_changed (line 296) | def on_language_changed(self, index):
    method on_font_size_changed (line 304) | def on_font_size_changed(self, value):
    method on_recording_transcriber_mode_changed (line 313) | def on_recording_transcriber_mode_changed(self, value):
    method on_force_cpu_changed (line 316) | def on_force_cpu_changed(self, state: int):
    method on_reduce_gpu_memory_changed (line 326) | def on_reduce_gpu_memory_changed(self, state: int):
  class ValidateOpenAIApiKeyJob (line 337) | class ValidateOpenAIApiKeyJob(QRunnable):
    class Signals (line 338) | class Signals(QObject):
    method __init__ (line 342) | def __init__(self, api_key: str):
    method run (line 347) | def run(self):

FILE: buzz/widgets/preferences_dialog/models/file_transcription_preferences.py
  class FileTranscriptionPreferences (line 16) | class FileTranscriptionPreferences:
    method save (line 28) | def save(self, settings: QSettings) -> None:
    method load (line 44) | def load(cls, settings: QSettings) -> "FileTranscriptionPreferences":
    method from_transcription_options (line 84) | def from_transcription_options(
    method to_transcription_options (line 102) | def to_transcription_options(

FILE: buzz/widgets/preferences_dialog/models/folder_watch_preferences.py
  class FolderWatchPreferences (line 11) | class FolderWatchPreferences:
    method save (line 18) | def save(self, settings: QSettings):
    method load (line 28) | def load(cls, settings: QSettings) -> "FolderWatchPreferences":

FILE: buzz/widgets/preferences_dialog/models/preferences.py
  class Preferences (line 11) | class Preferences:
    method save (line 14) | def save(self, settings: QSettings):
    method load (line 20) | def load(cls, settings: QSettings) -> "Preferences":

FILE: buzz/widgets/preferences_dialog/models_preferences_widget.py
  class ModelsPreferencesWidget (line 32) | class ModelsPreferencesWidget(QWidget):
    method __init__ (line 35) | def __init__(
    method on_model_size_changed (line 119) | def on_model_size_changed(self, current: QTreeWidgetItem, _: QTreeWidg...
    method reset (line 128) | def reset(self):
    method on_model_type_changed (line 205) | def on_model_type_changed(self, model_type: ModelType):
    method on_custom_model_id_input_changed (line 209) | def on_custom_model_id_input_changed(self, text):
    method on_custom_model_link_input_changed (line 216) | def on_custom_model_link_input_changed(self, text):
    method on_download_button_clicked (line 219) | def on_download_button_clicked(self):
    method on_delete_button_clicked (line 243) | def on_delete_button_clicked(self):
    method on_show_file_location_button_clicked (line 260) | def on_show_file_location_button_clicked(self):
    method on_download_completed (line 263) | def on_download_completed(self, _: str):
    method on_download_error (line 269) | def on_download_error(self, error: str):
    method on_download_progress (line 278) | def on_download_progress(self, progress: tuple):
    method on_progress_dialog_canceled (line 282) | def on_progress_dialog_canceled(self):

FILE: buzz/widgets/preferences_dialog/preferences_dialog.py
  class PreferencesDialog (line 27) | class PreferencesDialog(QDialog):
    method __init__ (line 33) | def __init__(

FILE: buzz/widgets/preferences_dialog/shortcuts_editor_preferences_widget.py
  class ShortcutsEditorPreferencesWidget (line 14) | class ShortcutsEditorPreferencesWidget(QWidget):
    method __init__ (line 17) | def __init__(self, shortcuts: Shortcuts, parent: Optional[QWidget] = N...
    method get_key_sequence_changed (line 39) | def get_key_sequence_changed(self, shortcut: Shortcut):
    method reset_to_defaults (line 46) | def reset_to_defaults(self):

FILE: buzz/widgets/presentation_window.py
  class PresentationWindow (line 13) | class PresentationWindow(QWidget):
    method __init__ (line 16) | def __init__(self, parent: Optional[QWidget] = None):
    method load_settings (line 49) | def load_settings(self):
    method apply_styling (line 89) | def apply_styling(self, text_color: str, bg_color: str, text_size: int):
    method update_transcripts (line 113) | def update_transcripts(self, text: str):
    method update_translations (line 138) | def update_translations(self, text: str):
    method toggle_fullscreen (line 165) | def toggle_fullscreen(self):
    method keyPressEvent (line 172) | def keyPressEvent(self, event):
    method get_css_file_path (line 182) | def get_css_file_path(self) -> str:

FILE: buzz/widgets/record_button.py
  class RecordButton (line 8) | class RecordButton(QPushButton):
    method __init__ (line 9) | def __init__(self, parent: Optional[QWidget]) -> None:
    method set_stopped (line 16) | def set_stopped(self):
    method set_recording (line 20) | def set_recording(self):

FILE: buzz/widgets/record_delegate.py
  class RecordDelegate (line 7) | class RecordDelegate(QStyledItemDelegate):
    method __init__ (line 8) | def __init__(self, text_getter: Callable[[QSqlRecord], str]):
    method initStyleOption (line 12) | def initStyleOption(self, option, index):

FILE: buzz/widgets/recording_transcriber_widget.py
  class RecordingTranscriberWidget (line 63) | class RecordingTranscriberWidget(QWidget):
    class RecordingStatus (line 76) | class RecordingStatus(enum.Enum):
    method __init__ (line 80) | def __init__(
    method create_presentation_options_bar (line 244) | def create_presentation_options_bar(self) -> QWidget:
    method create_copy_actions_bar (line 320) | def create_copy_actions_bar(self) -> QWidget:
    method on_copy_transcript_clicked (line 336) | def on_copy_transcript_clicked(self):
    method on_show_presentation_clicked (line 370) | def on_show_presentation_clicked(self):
    method on_text_size_changed (line 394) | def on_text_size_changed(self, value: int):
    method on_theme_changed (line 404) | def on_theme_changed(self, index: int):
    method on_text_color_clicked (line 422) | def on_text_color_clicked(self):
    method on_bg_color_clicked (line 439) | def on_bg_color_clicked(self):
    method on_fullscreen_clicked (line 456) | def on_fullscreen_clicked(self):
    method setup_for_export (line 461) | def setup_for_export(self):
    method on_recording_mode_changed (line 510) | def on_recording_mode_changed(self, mode: RecordingTranscriberMode):
    method on_hide_unconfirmed_changed (line 513) | def on_hide_unconfirmed_changed(self, value: bool):
    method on_transcription_options_changed (line 516) | def on_transcription_options_changed(
    method reset_transcriber_controls (line 528) | def reset_transcriber_controls(self):
    method on_device_changed (line 541) | def on_device_changed(self, device_id: int):
    method reset_recording_amplitude_listener (line 545) | def reset_recording_amplitude_listener(self):
    method on_record_button_clicked (line 572) | def on_record_button_clicked(self):
    method start_recording (line 600) | def start_recording(self):
    method on_model_loaded (line 622) | def on_model_loaded(self, model_path: str):
    method on_download_model_progress (line 701) | def on_download_model_progress(self, progress: Tuple[float, float]):
    method set_recording_status_stopped (line 717) | def set_recording_status_stopped(self):
    method on_download_model_error (line 726) | def on_download_model_error(self, error: str):
    method strip_newlines (line 735) | def strip_newlines(text):
    method filter_text (line 739) | def filter_text(text: str):
    method write_to_export_file (line 748) | def write_to_export_file(file_path: str, content: str, mode: str = "a"...
    method write_csv_export (line 765) | def write_csv_export(file_path: str, text: str, max_entries: int):
    method write_txt_export (line 800) | def write_txt_export(file_path: str, text: str, mode: str, max_entries...
    method read_export_file (line 827) | def read_export_file(file_path: str, retries: int = 5, delay: float = ...
    method find_common_part (line 846) | def find_common_part(text1: str, text2: str) -> str:
    method merge_text_no_overlap (line 868) | def merge_text_no_overlap(text1: str, text2: str) -> str:
    method process_transcription_merge (line 876) | def process_transcription_merge(self, text: str, texts, text_box, expo...
    method on_next_transcription (line 924) | def on_next_transcription(self, text: str):
    method on_next_translation (line 994) | def on_next_translation(self, text: str, _: Optional[int] = None):
    method stop_recording (line 1056) | def stop_recording(self):
    method on_transcriber_finished (line 1066) | def on_transcriber_finished(self):
    method on_transcriber_error (line 1072) | def on_transcriber_error(self, error: str):
    method on_cancel_model_progress_dialog (line 1087) | def on_cancel_model_progress_dialog(self):
    method reset_model_download (line 1095) | def reset_model_download(self):
    method reset_recording_controls (line 1103) | def reset_recording_controls(self):
    method reset_record_button (line 1109) | def reset_record_button(self):
    method on_recording_amplitude_changed (line 1112) | def on_recording_amplitude_changed(self, amplitude: float):
    method closeEvent (line 1115) | def closeEvent(self, event: QCloseEvent) -> None:
    method _on_close_transcriber_finished (line 1151) | def _on_close_transcriber_finished(self):
    method _do_close (line 1155) | def _do_close(self):

FILE: buzz/widgets/sequence_edit.py
  class SequenceEdit (line 9) | class SequenceEdit(QKeySequenceEdit):
    method __init__ (line 10) | def __init__(self, sequence: str, parent: Optional[QWidget] = None):
    method keyPressEvent (line 16) | def keyPressEvent(self, event: QtGui.QKeyEvent) -> None:

FILE: buzz/widgets/text_display_box.py
  class TextDisplayBox (line 6) | class TextDisplayBox(QPlainTextEdit):
    method __init__ (line 9) | def __init__(self, parent: Optional[QWidget], *args) -> None:

FILE: buzz/widgets/toolbar.py
  class ToolBar (line 9) | class ToolBar(QToolBar):
    method __init__ (line 10) | def __init__(self, parent: typing.Optional[QWidget] = None):
    method addAction (line 17) | def addAction(self, *args):
    method addActions (line 22) | def addActions(self, actions: typing.Iterable[QtGui.QAction]) -> None:
    method fix_spacing_on_mac (line 26) | def fix_spacing_on_mac(self):

FILE: buzz/widgets/transcriber/advanced_settings_button.py
  class AdvancedSettingsButton (line 7) | class AdvancedSettingsButton(QPushButton):
    method __init__ (line 8) | def __init__(self, parent: Optional[QWidget]) -> None:

FILE: buzz/widgets/transcriber/advanced_settings_dialog.py
  class AdvancedSettingsDialog (line 27) | class AdvancedSettingsDialog(QDialog):
    method __init__ (line 33) | def __init__(
    method on_initial_prompt_changed (line 239) | def on_initial_prompt_changed(self):
    method on_enable_llm_translation_changed (line 245) | def on_enable_llm_translation_changed(self, state):
    method on_llm_model_changed (line 255) | def on_llm_model_changed(self, text: str):
    method on_llm_prompt_changed (line 259) | def on_llm_prompt_changed(self):
    method on_silence_threshold_changed (line 265) | def on_silence_threshold_changed(self, value: float):
    method on_line_separator_changed (line 269) | def on_line_separator_changed(self, text: str):
    method on_recording_mode_changed (line 276) | def on_recording_mode_changed(self, index: int):
    method _update_recording_mode_visibility (line 282) | def _update_recording_mode_visibility(self, mode: RecordingTranscriber...
    method on_transcription_step_changed (line 291) | def on_transcription_step_changed(self, value: float):
    method on_hide_unconfirmed_changed (line 295) | def on_hide_unconfirmed_changed(self, state: int):
    method on_export_enabled_changed (line 300) | def on_export_enabled_changed(self, state: int):
    method on_export_folder_changed (line 316) | def on_export_folder_changed(self, text: str):
    method on_browse_export_folder (line 319) | def on_browse_export_folder(self):
    method on_export_file_name_changed (line 324) | def on_export_file_name_changed(self, text: str):
    method on_export_file_type_changed (line 327) | def on_export_file_type_changed(self, index: int):
    method on_export_max_entries_changed (line 331) | def on_export_max_entries_changed(self, value: int):

FILE: buzz/widgets/transcriber/file_transcriber_widget.py
  class FileTranscriberWidget (line 31) | class FileTranscriberWidget(QWidget):
    method __init__ (line 42) | def __init__(
    method get_title (line 97) | def get_title(self) -> str:
    method load_preferences (line 104) | def load_preferences(self):
    method save_preferences (line 110) | def save_preferences(self):
    method on_click_run (line 118) | def on_click_run(self):
    method on_model_loaded (line 132) | def on_model_loaded(self, model_path: str):
    method on_download_model_progress (line 140) | def on_download_model_progress(self, progress: Tuple[float, float]):
    method on_download_model_error (line 156) | def on_download_model_error(self, error: str):
    method reset_transcriber_controls (line 161) | def reset_transcriber_controls(self):
    method on_cancel_model_progress_dialog (line 174) | def on_cancel_model_progress_dialog(self):
    method reset_model_download (line 180) | def reset_model_download(self):
    method on_word_level_timings_changed (line 185) | def on_word_level_timings_changed(self, value: int):
    method closeEvent (line 190) | def closeEvent(self, event: QtGui.QCloseEvent) -> None:

FILE: buzz/widgets/transcriber/file_transcription_form_widget.py
  class FileTranscriptionFormWidget (line 19) | class FileTranscriptionFormWidget(QWidget):
    method __init__ (line 23) | def __init__(
    method on_transcription_options_changed (line 83) | def on_transcription_options_changed(
    method on_word_level_timings_changed (line 95) | def on_word_level_timings_changed(self, value: int):
    method on_extract_speech_changed (line 104) | def on_extract_speech_changed(self, value: int):
    method get_on_checkbox_state_changed_callback (line 113) | def get_on_checkbox_state_changed_callback(self, output_format: Output...

FILE: buzz/widgets/transcriber/hugging_face_search_line_edit.py
  class HuggingFaceSearchLineEdit (line 25) | class HuggingFaceSearchLineEdit(LineEdit):
    method __init__ (line 29) | def __init__(
    method focusInEvent (line 65) | def focusInEvent(self, event):
    method on_text_edited (line 70) | def on_text_edited(self, text: str):
    method on_select_item (line 73) | def on_select_item(self):
    method fetch_models (line 82) | def fetch_models(self):
    method on_popup_selected (line 97) | def on_popup_selected(self):
    method on_request_response (line 100) | def on_request_response(self, network_reply: QNetworkReply):
    method eventFilter (line 132) | def eventFilter(self, target: QObject, event: QEvent):

FILE: buzz/widgets/transcriber/initial_prompt_text_edit.py
  class InitialPromptTextEdit (line 7) | class InitialPromptTextEdit(QPlainTextEdit):
    method __init__ (line 8) | def __init__(self, text: str, model_type: ModelType, parent: QWidget |...

FILE: buzz/widgets/transcriber/languages_combo_box.py
  class LanguagesComboBox (line 12) | class LanguagesComboBox(QComboBox):
    method __init__ (line 18) | def __init__(
    method on_index_changed (line 52) | def on_index_changed(self, index: int):
    method showPopup (line 55) | def showPopup(self):

FILE: buzz/widgets/transcriber/mms_language_line_edit.py
  class MMSLanguageLineEdit (line 10) | class MMSLanguageLineEdit(LineEdit):
    method __init__ (line 19) | def __init__(
    method _on_text_changed (line 37) | def _on_text_changed(self, text: str):
    method language (line 42) | def language(self) -> str:
    method setLanguage (line 46) | def setLanguage(self, language: str):

FILE: buzz/widgets/transcriber/tasks_combo_box.py
  class TasksComboBox (line 9) | class TasksComboBox(QComboBox):
    method __init__ (line 14) | def __init__(self, default_task: Task, parent: Optional[QWidget], *arg...
    method on_index_changed (line 21) | def on_index_changed(self, index: int):

FILE: buzz/widgets/transcriber/transcription_options_group_box.py
  class TranscriptionOptionsGroupBox (line 27) | class TranscriptionOptionsGroupBox(QGroupBox):
    method __init__ (line 31) | def __init__(
    method on_openai_access_token_edit_changed (line 137) | def on_openai_access_token_edit_changed(self, access_token: str):
    method on_language_changed (line 141) | def on_language_changed(self, language: str):
    method on_mms_language_changed (line 148) | def on_mms_language_changed(self, language: str):
    method on_task_changed (line 156) | def on_task_changed(self, task: Task):
    method open_advanced_settings (line 160) | def open_advanced_settings(self):
    method on_transcription_options_changed (line 163) | def on_transcription_options_changed(
    method reset_visible_rows (line 169) | def reset_visible_rows(self):
    method on_model_type_changed (line 255) | def on_model_type_changed(self, model_type: ModelType):
    method on_whisper_model_size_changed (line 267) | def on_whisper_model_size_changed(self, text: str):
    method on_hugging_face_model_changed (line 275) | def on_hugging_face_model_changed(self, model: str):
    method _update_language_widget_visibility (line 284) | def _update_language_widget_visibility(self):

FILE: buzz/widgets/transcription_record.py
  class TranscriptionRecord (line 9) | class TranscriptionRecord:
    method id (line 11) | def id(record: QSqlRecord) -> UUID:
    method model (line 15) | def model(record: QSqlRecord) -> TranscriptionModel:
    method task (line 27) | def task(record: QSqlRecord) -> Task:

FILE: buzz/widgets/transcription_task_folder_watcher.py
  class TranscriptionTaskFolderWatcher (line 21) | class TranscriptionTaskFolderWatcher(QFileSystemWatcher):
    method __init__ (line 26) | def __init__(
    method set_preferences (line 38) | def set_preferences(self, preferences: FolderWatchPreferences):
    method find_tasks (line 53) | def find_tasks(self):

FILE: buzz/widgets/transcription_tasks_table_widget.py
  class Column (line 33) | class Column(enum.Enum):
  class ColDef (line 58) | class ColDef:
  function format_record_status_text (line 67) | def format_record_status_text(record: QSqlRecord) -> str:
  class TranscriptionTasksTableHeaderView (line 166) | class TranscriptionTasksTableHeaderView(QHeaderView):
    method __init__ (line 167) | def __init__(self, orientation, parent=None):
    method contextMenuEvent (line 170) | def contextMenuEvent(self, event):
    method on_column_checked (line 190) | def on_column_checked(self, column_index: int, checked: bool):
  class TranscriptionTasksTableWidget (line 231) | class TranscriptionTasksTableWidget(QTableView):
    method __init__ (line 235) | def __init__(self, parent: Optional[QWidget] = None):
    method contextMenuEvent (line 294) | def contextMenuEvent(self, event):
    method save_column_visibility (line 316) | def save_column_visibility(self):
    method on_column_resized (line 326) | def on_column_resized(self, logical_index: int, old_size: int, new_siz...
    method on_column_moved (line 330) | def on_column_moved(self, logical_index: int, old_visual_index: int, n...
    method on_sort_indicator_changed (line 336) | def on_sort_indicator_changed(self, logical_index: int, order: Qt.Sort...
    method on_double_click (line 340) | def on_double_click(self, index: QModelIndex):
    method save_column_widths (line 345) | def save_column_widths(self):
    method save_column_order (line 356) | def save_column_order(self):
    method load_column_widths (line 369) | def load_column_widths(self):
    method save_sort_state (line 379) | def save_sort_state(self):
    method load_sort_state (line 387) | def load_sort_state(self):
    method load_column_visibility (line 398) | def load_column_visibility(self):
    method load_column_order (line 415) | def load_column_order(self):
    method reset_column_order (line 435) | def reset_column_order(self):
    method reload_column_order_from_settings (line 483) | def reload_column_order_from_settings(self):
    method copy_selected_fields (line 545) | def copy_selected_fields(self):
    method mouseDoubleClickEvent (line 557) | def mouseDoubleClickEvent(self, event: QtGui.QMouseEvent) -> None:
    method keyPressEvent (line 568) | def keyPressEvent(self, event: QtGui.QKeyEvent) -> None:
    method selected_transcriptions (line 583) | def selected_transcriptions(self) -> List[Transcription]:
    method delete_transcriptions (line 587) | def delete_transcriptions(self, rows: List[QModelIndex]):
    method transcription (line 592) | def transcription(self, index: QModelIndex) -> Transcription:
    method refresh_all (line 595) | def refresh_all(self):
    method refresh_row (line 598) | def refresh_row(self, id: UUID):
    method format_timedelta (line 606) | def format_timedelta(delta: timedelta):
    method on_rename_action (line 617) | def on_rename_action(self):
    method on_notes_action (line 650) | def on_notes_action(self):
    method on_restart_transcription_action (line 677) | def on_restart_transcription_action(self):
    method _restart_transcription_task (line 708) | def _restart_transcription_task(self, transcription):

FILE: buzz/widgets/transcription_viewer/export_transcription_menu.py
  class ExportTranscriptionMenu (line 16) | class ExportTranscriptionMenu(QMenu):
    method __init__ (line 17) | def __init__(
    method extract_format_and_segment_key (line 49) | def extract_format_and_segment_key(action_text: str):
    method on_translation_available (line 57) | def on_translation_available(self):
    method on_menu_triggered (line 61) | def on_menu_triggered(self, action: QAction):

FILE: buzz/widgets/transcription_viewer/speaker_identification_widget.py
  function process_in_batches (line 50) | def process_in_batches(
  class IdentificationWorker (line 108) | class IdentificationWorker(QObject):
    method __init__ (line 113) | def __init__(self, transcription, transcription_service):
    method cancel (line 119) | def cancel(self):
    method get_transcript (line 123) | def get_transcript(self, audio, **kwargs) -> dict:
    method run (line 152) | def run(self):
  class SpeakerIdentificationWidget (line 413) | class SpeakerIdentificationWidget(QWidget):
    method __init__ (line 418) | def __init__(
    method on_identify_button_clicked (line 553) | def on_identify_button_clicked(self):
    method on_cancel_button_clicked (line 576) | def on_cancel_button_clicked(self):
    method _reset_buttons (line 586) | def _reset_buttons(self):
    method _on_thread_finished (line 593) | def _on_thread_finished(self, result):
    method on_identification_error (line 602) | def on_identification_error(self, error_message):
    method on_progress_update (line 608) | def on_progress_update(self, progress):
    method on_identification_finished (line 623) | def on_identification_finished(self, result):
    method on_speaker_preview (line 667) | def on_speaker_preview(self, speaker_id):
    method on_save_button_clicked (line 687) | def on_save_button_clicked(self):
    method changeEvent (line 762) | def changeEvent(self, event):
    method closeEvent (line 771) | def closeEvent(self, event):
    method _cleanup_thread (line 784) | def _cleanup_thread(self):

FILE: buzz/widgets/transcription_viewer/transcription_resizer_widget.py
  class TranscriptionWorker (line 42) | class TranscriptionWorker(QObject):
    method __init__ (line 45) | def __init__(self, transcription, transcription_options, transcription...
    method get_transcript (line 52) | def get_transcript(self, audio, **kwargs) -> dict:
    method run (line 95) | def run(self):
  class TranscriptionResizerWidget (line 135) | class TranscriptionResizerWidget(QWidget):
    method __init__ (line 140) | def __init__(
    method load_preferences (line 298) | def load_preferences(self):
    method on_resize_button_clicked (line 304) | def on_resize_button_clicked(self):
    method on_extend_button_clicked (line 348) | def on_extend_button_clicked(self):
    method on_merge_button_clicked (line 386) | def on_merge_button_clicked(self):
    method on_transcription_completed (line 432) | def on_transcription_completed(self, segments):
    method closeEvent (line 441) | def closeEvent(self, event):

FILE: buzz/widgets/transcription_viewer/transcription_segments_editor_widget.py
  class Column (line 25) | class Column(enum.Enum):
  class ColDef (line 35) | class ColDef:
  function parse_timestamp (line 42) | def parse_timestamp(timestamp_str: str) -> Optional[int]:
  class TimeStampLineEdit (line 72) | class TimeStampLineEdit(QLineEdit):
    method __init__ (line 75) | def __init__(self, parent=None):
    method set_milliseconds (line 84) | def set_milliseconds(self, ms: int):
    method get_milliseconds (line 88) | def get_milliseconds(self) -> int:
    method keyPressEvent (line 94) | def keyPressEvent(self, event):
    method focusOutEvent (line 106) | def focusOutEvent(self, event):
  class TimeStampDelegate (line 118) | class TimeStampDelegate(QStyledItemDelegate):
    method displayText (line 119) | def displayText(self, value, locale):
  class TimeStampEditorDelegate (line 123) | class TimeStampEditorDelegate(QStyledItemDelegate):
    method createEditor (line 128) | def createEditor(self, parent, option, index):
    method on_editor_text_changed (line 134) | def on_editor_text_changed(self, editor, index):
    method setEditorData (line 139) | def setEditorData(self, editor, index):
    method setModelData (line 145) | def setModelData(self, editor, model, index):
    method displayText (line 198) | def displayText(self, value, locale):
  class CustomTextEdit (line 202) | class CustomTextEdit(QTextEdit):
    method __init__ (line 205) | def __init__(self, parent=None):
    method keyPressEvent (line 208) | def keyPressEvent(self, event):
  class WordWrapDelegate (line 218) | class WordWrapDelegate(QStyledItemDelegate):
    method createEditor (line 219) | def createEditor(self, parent, option, index):
    method setModelData (line 227) | def setModelData(self, editor, model, index):
  class TranscriptionSegmentModel (line 231) | class TranscriptionSegmentModel(QSqlTableModel):
    method __init__ (line 232) | def __init__(self, transcription_id: UUID):
  class TranscriptionSegmentsEditorWidget (line 239) | class TranscriptionSegmentsEditorWidget(QTableView):
    method keyPressEvent (line 244) | def keyPressEvent(self, event):
    method __init__ (line 254) | def __init__(
    method init_row_height (line 321) | def init_row_height(self):
    method has_non_empty_translation (line 329) | def has_non_empty_translation(self) -> bool:
    method resizeEvent (line 335) | def resizeEvent(self, event):
    method update_translation (line 352) | def update_translation(self, translation: str, segment_id: Optional[in...
    method on_selection_changed (line 361) | def on_selection_changed(
    method segment (line 367) | def segment(self, index: QModelIndex) -> QSqlRecord:
    method segments (line 370) | def segments(self) -> list[QSqlRecord]:
    method highlight_and_scroll_to_row (line 373) | def highlight_and_scroll_to_row(self, row_index: int):

FILE: buzz/widgets/transcription_viewer/transcription_view_mode_tool_button.py
  class ViewMode (line 15) | class ViewMode(Enum):
  class TranscriptionViewModeToolButton (line 21) | class TranscriptionViewModeToolButton(QToolButton):
    method __init__ (line 24) | def __init__(
    method on_translation_available (line 65) | def on_translation_available(self):

FILE: buzz/widgets/transcription_viewer/transcription_viewer_widget.py
  class TranscriptionViewerWidget (line 73) | class TranscriptionViewerWidget(QWidget):
    method __init__ (line 78) | def __init__(
    method load_transcription_media (line 384) | def load_transcription_media(self):
    method on_transcript_segment_clicked (line 396) | def on_transcript_segment_clicked(self, segment):
    method restore_ui_state (line 405) | def restore_ui_state(self):
    method create_search_bar (line 415) | def create_search_bar(self):
    method create_loop_controls (line 479) | def create_loop_controls(self):
    method show_loop_controls (line 573) | def show_loop_controls(self):
    method hide_loop_controls (line 582) | def hide_loop_controls(self):
    method toggle_playback_controls_visibility (line 591) | def toggle_playback_controls_visibility(self):
    method toggle_audio_playback (line 598) | def toggle_audio_playback(self):
    method replay_current_segment (line 605) | def replay_current_segment(self):
    method decrease_segment_start (line 621) | def decrease_segment_start(self):
    method increase_segment_start (line 625) | def increase_segment_start(self):
    method decrease_segment_end (line 629) | def decrease_segment_end(self):
    method increase_segment_end (line 633) | def increase_segment_end(self):
    method _adjust_segment_timestamp (line 637) | def _adjust_segment_timestamp(self, field: str, delta_ms: int):
    method on_audio_playback_state_changed (line 724) | def on_audio_playback_state_changed(self, state):
    method initialize_speed_control (line 733) | def initialize_speed_control(self):
    method on_speed_changed (line 749) | def on_speed_changed(self, speed_text: str):
    method increase_speed (line 782) | def increase_speed(self):
    method decrease_speed (line 788) | def decrease_speed(self):
    method get_current_speed (line 794) | def get_current_speed(self) -> float:
    method set_speed (line 802) | def set_speed(self, speed: float):
    method on_search_text_changed (line 817) | def on_search_text_changed(self, text: str):
    method perform_search (line 830) | def perform_search(self):
    method search_in_table (line 842) | def search_in_table(self):
    method search_in_text (line 868) | def search_in_text(self):
    method update_search_ui (line 888) | def update_search_ui(self):
    method highlight_current_match (line 905) | def highlight_current_match(self):
    method highlight_table_match (line 919) | def highlight_table_match(self, row_index: int):
    method highlight_text_match (line 927) | def highlight_text_match(self, start_pos: int):
    method search_next (line 939) | def search_next(self):
    method search_previous (line 949) | def search_previous(self):
    method search_next_if_results (line 959) | def search_next_if_results(self):
    method search_previous_if_results (line 964) | def search_previous_if_results(self):
    method update_search_results_label (line 969) | def update_search_results_label(self):
    method clear_search (line 979) | def clear_search(self):
    method hide_search_bar (line 999) | def hide_search_bar(self):
    method setup_shortcuts (line 1011) | def setup_shortcuts(self):
    method focus_search_input (line 1068) | def focus_search_input(self):
    method toggle_search_bar_visibility (line 1083) | def toggle_search_bar_visibility(self):
    method show_search_bar (line 1095) | def show_search_bar(self):
    method eventFilter (line 1107) | def eventFilter(self, obj, event):
    method reset_view (line 1121) | def reset_view(self):
    method on_view_mode_changed (line 1182) | def on_view_mode_changed(self, view_mode: ViewMode) -> None:
    method on_segment_selected (line 1190) | def on_segment_selected(self, segment: QSqlRecord):
    method on_timestamp_being_edited (line 1230) | def on_timestamp_being_edited(self, row: int, column: int, new_value_m...
    method on_audio_player_position_ms_changed (line 1258) | def on_audio_player_position_ms_changed(self, position_ms: int) -> None:
    method resize_current_segment_frame (line 1311) | def resize_current_segment_frame(self):
    method load_preferences (line 1353) | def load_preferences(self):
    method open_advanced_settings (line 1360) | def open_advanced_settings(self):
    method on_transcription_options_changed (line 1363) | def on_transcription_options_changed(
    method on_translate_button_clicked (line 1368) | def on_translate_button_clicked(self):
    method run_translation (line 1386) | def run_translation(self):
    method on_resize_button_clicked (line 1394) | def on_resize_button_clicked(self):
    method on_speaker_identification_button_clicked (line 1405) | def on_speaker_identification_button_clicked(self):
    method on_loop_toggle_changed (line 1420) | def on_loop_toggle_changed(self, enabled: bool):
    method on_follow_audio_toggle_changed (line 1459) | def on_follow_audio_toggle_changed(self, enabled: bool):
    method on_scroll_to_current_button_clicked (line 1478) | def on_scroll_to_current_button_clicked(self):
    method auto_scroll_to_current_position (line 1507) | def auto_scroll_to_current_position(self):
    method resizeEvent (line 1541) | def resizeEvent(self, event):
    method closeEvent (line 1547) | def closeEvent(self, event):
    method save_geometry (line 1584) | def save_geometry(self):
    method load_geometry (line 1590) | def load_geometry(self):
    method save_splitter_sizes (line 1601) | def save_splitter_sizes(self):
    method load_splitter_sizes (line 1617) | def load_splitter_sizes(self):
    method on_splitter_moved (line 1640) | def on_splitter_moved(self, pos: int, index: int):

FILE: buzz/widgets/update_dialog.py
  class UpdateDialog (line 29) | class UpdateDialog(QDialog):
    method __init__ (line 31) | def __init__(
    method _setup_ui (line 52) | def _setup_ui(self):
    method _on_download_clicked (line 120) | def _on_download_clicked(self):
    method _download_next_file (line 138) | def _download_next_file(self):
    method _on_download_progress (line 158) | def _on_download_progress(self, bytes_received: int, bytes_total: int):
    method _on_download_finished (line 174) | def _on_download_finished(self):
    method _all_downloads_finished (line 223) | def _all_downloads_finished(self):
    method _run_installer (line 229) | def _run_installer(self):
    method _reset_ui (line 257) | def _reset_ui(self):

FILE: buzz/widgets/video_player.py
  class VideoPlayer (line 9) | class VideoPlayer(QWidget):
    method __init__ (line 12) | def __init__(self, file_path: str, parent=None):
    method on_error_occurred (line 85) | def on_error_occurred(self, error: QMediaPlayer.Error, error_string: s...
    method on_media_status_changed (line 88) | def on_media_status_changed(self, status: QMediaPlayer.MediaStatus):
    method toggle_playback (line 100) | def toggle_playback(self):
    method on_slider_moved (line 106) | def on_slider_moved(self, position):
    method on_slider_pressed (line 109) | def on_slider_pressed(self):
    method on_slider_released (line 113) | def on_slider_released(self):
    method set_position (line 119) | def set_position(self, position_ms: int):
    method on_position_changed (line 122) | def on_position_changed(self, position_ms: int):
    method on_duration_changed (line 143) | def on_duration_changed(self, duration_ms: int):
    method on_playback_state_changed (line 148) | def on_playback_state_changed(self, state: QMediaPlayer.PlaybackState):
    method update_time_label (line 154) | def update_time_label(self):
    method set_range (line 159) | def set_range(self, range_ms: Tuple[int, int]):
    method clear_range (line 167) | def clear_range(self):
    method stop (line 171) | def stop(self):

FILE: docs/src/pages/index.tsx
  function Home (line 8) | function Home(): JSX.Element {

FILE: hatch_build.py
  class CustomBuildHook (line 10) | class CustomBuildHook(BuildHookInterface):
    method initialize (line 13) | def initialize(self, version, build_data):

FILE: msgfmt.py
  function usage (line 26) | def usage(ecode, msg=""):
  function make (line 36) | def make(filename, outfile):
  function main (line 41) | def main():

FILE: tests/app_main.py
  class TestMain (line 5) | class TestMain:
    method test_main (line 6) | def test_main(self):

FILE: tests/cache_test.py
  class TestTasksCache (line 9) | class TestTasksCache:
    method test_should_save_and_load (line 10) | def test_should_save_and_load(self, tmp_path):

FILE: tests/cli_test.py
  class TestCLI (line 12) | class TestCLI:
    method test_cli (line 33) | def test_cli(self, qapp, qapp_args, qtbot: QtBot):

FILE: tests/conftest.py
  function db (line 30) | def db() -> QSqlDatabase:
  function transcription_dao (line 38) | def transcription_dao(db, request: SubRequest) -> TranscriptionDAO:
  function transcription_service (line 48) | def transcription_service(
  function transcription_segment_dao (line 55) | def transcription_segment_dao(db) -> TranscriptionSegmentDAO:
  function qapp_cls (line 60) | def qapp_cls():
  function qapp_args (line 65) | def qapp_args(request):
  function settings (line 73) | def settings():
  function shortcuts (line 84) | def shortcuts(settings):

FILE: tests/db/dao/transcription_dao_test.py
  function db (line 11) | def db():
  function transcription_dao (line 50) | def transcription_dao(db):
  function sample_transcription (line 56) | def sample_transcription():
  class TestTranscriptionDAO (line 70) | class TestTranscriptionDAO:
    method test_insert_transcription_with_name_and_notes (line 71) | def test_insert_transcription_with_name_and_notes(self, transcription_...
    method test_update_transcription_name (line 86) | def test_update_transcription_name(self, transcription_dao, sample_tra...
    method test_update_transcription_notes (line 103) | def test_update_transcription_notes(self, transcription_dao, sample_tr...
    method test_update_transcription_name_nonexistent_id (line 120) | def test_update_transcription_name_nonexistent_id(self, transcription_...
    method test_update_transcription_notes_nonexistent_id (line 128) | def test_update_transcription_notes_nonexistent_id(self, transcription...
    method test_update_transcription_name_empty_string (line 136) | def test_update_transcription_name_empty_string(self, transcription_da...
    method test_update_transcription_notes_empty_string (line 152) | def test_update_transcription_notes_empty_string(self, transcription_d...
    method test_update_transcription_name_with_none (line 168) | def test_update_transcription_name_with_none(self, transcription_dao, ...
    method test_update_transcription_notes_with_none (line 185) | def test_update_transcription_notes_with_none(self, transcription_dao,...
    method test_insert_transcription_without_name_and_notes (line 202) | def test_insert_transcription_without_name_and_notes(self, transcripti...
    method test_database_error_handling (line 227) | def test_database_error_handling(self, transcription_dao):

FILE: tests/db/entity/transcription_test.py
  class TestTranscription (line 7) | class TestTranscription:
    method test_transcription_creation_with_name_and_notes (line 8) | def test_transcription_creation_with_name_and_notes(self):
    method test_transcription_creation_without_name_and_notes (line 24) | def test_transcription_creation_without_name_and_notes(self):
    method test_transcription_creation_with_empty_name_and_notes (line 38) | def test_transcription_creation_with_empty_name_and_notes(self):
    method test_transcription_name_assignment (line 54) | def test_transcription_name_assignment(self):
    method test_transcription_notes_assignment (line 77) | def test_transcription_notes_assignment(self):
    method test_transcription_with_unicode_name_and_notes (line 100) | def test_transcription_with_unicode_name_and_notes(self):
    method test_transcription_with_long_name_and_notes (line 116) | def test_transcription_with_long_name_and_notes(self):
    method test_transcription_name_with_special_characters (line 137) | def test_transcription_name_with_special_characters(self):
    method test_transcription_notes_with_newlines (line 153) | def test_transcription_notes_with_newlines(self):
    method test_transcription_equality_with_name_and_notes (line 176) | def test_transcription_equality_with_name_and_notes(self):
    method test_transcription_inequality_with_different_name_and_notes (line 205) | def test_transcription_inequality_with_different_name_and_notes(self):
    method test_transcription_id_as_uuid_property (line 234) | def test_transcription_id_as_uuid_property(self):
    method test_transcription_string_representation_with_name_and_notes (line 252) | def test_transcription_string_representation_with_name_and_notes(self):
    method test_transcription_with_none_values_in_other_fields (line 269) | def test_transcription_with_none_values_in_other_fields(self):

FILE: tests/db/service/transcription_service_test.py
  function mock_transcription_dao (line 10) | def mock_transcription_dao():
  function mock_transcription_segment_dao (line 16) | def mock_transcription_segment_dao():
  function transcription_service (line 22) | def transcription_service(mock_transcription_dao, mock_transcription_seg...
  function sample_transcription (line 28) | def sample_transcription():
  class TestTranscriptionService (line 42) | class TestTranscriptionService:
    method test_update_transcription_name (line 43) | def test_update_transcription_name(self, transcription_service, mock_t...
    method test_update_transcription_notes (line 54) | def test_update_transcription_notes(self, transcription_service, mock_...
    method test_update_transcription_name_with_empty_string (line 65) | def test_update_transcription_name_with_empty_string(self, transcripti...
    method test_update_transcription_notes_with_empty_string (line 76) | def test_update_transcription_notes_with_empty_string(self, transcript...
    method test_update_transcription_name_with_none (line 87) | def test_update_transcription_name_with_none(self, transcription_servi...
    method test_update_transcription_notes_with_none (line 97) | def test_update_transcription_notes_with_none(self, transcription_serv...
    method test_update_transcription_name_propagates_dao_exception (line 107) | def test_update_transcription_name_propagates_dao_exception(self, tran...
    method test_update_transcription_notes_propagates_dao_exception (line 119) | def test_update_transcription_notes_propagates_dao_exception(self, tra...
    method test_update_transcription_name_with_string_uuid (line 131) | def test_update_transcription_name_with_string_uuid(self, transcriptio...
    method test_update_transcription_notes_with_string_uuid (line 145) | def test_update_transcription_notes_with_string_uuid(self, transcripti...
    method test_update_transcription_name_multiple_calls (line 159) | def test_update_transcription_name_multiple_calls(self, transcription_...
    method test_update_transcription_notes_multiple_calls (line 175) | def test_update_transcription_notes_multiple_calls(self, transcription...
    method test_update_transcription_name_with_unicode (line 191) | def test_update_transcription_name_with_unicode(self, transcription_se...
    method test_update_transcription_notes_with_unicode (line 202) | def test_update_transcription_notes_with_unicode(self, transcription_s...

FILE: tests/gui_test.py
  function audio_setup (line 43) | def audio_setup():
  class TestLanguagesComboBox (line 52) | class TestLanguagesComboBox:
    method test_should_show_sorted_whisper_languages (line 53) | def test_should_show_sorted_whisper_languages(self, qtbot):
    method test_should_select_en_as_default_language (line 59) | def test_should_select_en_as_default_language(self, qtbot):
    method test_should_select_detect_language_as_default (line 64) | def test_should_select_detect_language_as_default(self, qtbot):
  class TestAudioDevicesComboBox (line 70) | class TestAudioDevicesComboBox:
    method test_get_devices (line 71) | def test_get_devices(self):
    method test_select_default_mic_when_no_default (line 82) | def test_select_default_mic_when_no_default(self):
  function clear_settings (line 90) | def clear_settings():
  class TestAboutDialog (line 95) | class TestAboutDialog:
    method test_should_check_for_updates (line 96) | def test_should_check_for_updates(self, qtbot: QtBot):
  class TestAdvancedSettingsDialog (line 113) | class TestAdvancedSettingsDialog:
    method test_should_update_advanced_settings (line 114) | def test_should_update_advanced_settings(self, qtbot: QtBot):
  class TestHuggingFaceSearchLineEdit (line 149) | class TestHuggingFaceSearchLineEdit:
    method test_should_update_selected_model_on_type (line 150) | def test_should_update_selected_model_on_type(self, qtbot: QtBot):
    method test_should_show_list_of_models (line 163) | def test_should_show_list_of_models(self, qtbot: QtBot):
    method test_should_select_model_from_list (line 175) | def test_should_select_model_from_list(self, qtbot: QtBot):
    method network_access_manager (line 206) | def network_access_manager():
    method _set_text_and_wait_response (line 213) | def _set_text_and_wait_response(qtbot: QtBot, widget: HuggingFaceSearc...
  class TestTranscriptionOptionsGroupBox (line 219) | class TestTranscriptionOptionsGroupBox:
    method test_should_update_model_type (line 220) | def test_should_update_model_type(self, qtbot):

FILE: tests/mock_qt.py
  class MockNetworkReply (line 8) | class MockNetworkReply(QNetworkReply):
    method __init__ (line 9) | def __init__(self, data: object, _: Optional[QObject] = None) -> None:
    method readAll (line 12) | def readAll(self) -> "QByteArray":
    method error (line 15) | def error(self) -> "QNetworkReply.NetworkError":
    method deleteLater (line 18) | def deleteLater(self) -> None:
  class MockNetworkAccessManager (line 22) | class MockNetworkAccessManager(QNetworkAccessManager):
    method __init__ (line 26) | def __init__(
    method get (line 32) | def get(self, _: "QNetworkRequest") -> "QNetworkReply":
  class MockDownloadReply (line 37) | class MockDownloadReply(QObject):
    method __init__ (line 42) | def __init__(
    method readAll (line 55) | def readAll(self) -> QByteArray:
    method error (line 58) | def error(self) -> "QNetworkReply.NetworkError":
    method errorString (line 61) | def errorString(self) -> str:
    method abort (line 64) | def abort(self) -> None:
    method deleteLater (line 67) | def deleteLater(self) -> None:
    method emit_finished (line 70) | def emit_finished(self) -> None:
  class MockDownloadNetworkManager (line 74) | class MockDownloadNetworkManager(QNetworkAccessManager):
    method __init__ (line 77) | def __init__(
    method get (line 86) | def get(self, _: "QNetworkRequest") -> "MockDownloadReply":

FILE: tests/mock_sounddevice.py
  class MockInputStream (line 97) | class MockInputStream:
    method __init__ (line 101) | def __init__(
    method start (line 121) | def start(self):
    method target (line 124) | def target(self):
    method stop (line 150) | def stop(self):
    method close (line 155) | def close(self):
    method __enter__ (line 158) | def __enter__(self):
    method __exit__ (line 161) | def __exit__(self, exc_type, exc_val, exc_tb):
  class MockSoundDevice (line 165) | class MockSoundDevice:
    method __init__ (line 166) | def __init__(self):
    method InputStream (line 169) | def InputStream(self, *args, **kwargs):
    method query_devices (line 172) | def query_devices(self, device=None):
    method check_input_settings (line 178) | def check_input_settings(self, device=None, samplerate=None):

FILE: tests/model_loader.py
  function get_model_path (line 4) | def get_model_path(transcription_model: TranscriptionModel) -> str:

FILE: tests/model_loader_test.py
  class TestModelLoader (line 24) | class TestModelLoader:
    method test_download_model (line 34) | def test_download_model(self, model: TranscriptionModel):
  class TestMapLanguageToMms (line 45) | class TestMapLanguageToMms:
    method test_empty_returns_english (line 46) | def test_empty_returns_english(self):
    method test_two_letter_known_code (line 49) | def test_two_letter_known_code(self):
    method test_three_letter_code_returned_as_is (line 54) | def test_three_letter_code_returned_as_is(self):
    method test_unknown_two_letter_code_returned_as_is (line 58) | def test_unknown_two_letter_code_returned_as_is(self):
    method test_various_language_codes (line 71) | def test_various_language_codes(self, code, expected):
  class TestIsMmsModel (line 75) | class TestIsMmsModel:
    method test_empty_string (line 76) | def test_empty_string(self):
    method test_mms_in_model_id (line 79) | def test_mms_in_model_id(self):
    method test_mms_case_insensitive (line 82) | def test_mms_case_insensitive(self):
    method test_non_mms_model (line 85) | def test_non_mms_model(self):
  class TestWhisperModelSize (line 89) | class TestWhisperModelSize:
    method test_to_faster_whisper_model_size_large (line 90) | def test_to_faster_whisper_model_size_large(self):
    method test_to_faster_whisper_model_size_tiny (line 93) | def test_to_faster_whisper_model_size_tiny(self):
    method test_to_faster_whisper_model_size_largev3 (line 96) | def test_to_faster_whisper_model_size_largev3(self):
    method test_to_whisper_cpp_model_size_large (line 99) | def test_to_whisper_cpp_model_size_large(self):
    method test_to_whisper_cpp_model_size_tiny (line 102) | def test_to_whisper_cpp_model_size_tiny(self):
    method test_str (line 105) | def test_str(self):
  class TestModelType (line 112) | class TestModelType:
    method test_supports_initial_prompt (line 113) | def test_supports_initial_prompt(self):
    method test_is_available (line 129) | def test_is_available(self, platform_system, platform_machine, expecte...
    method test_is_manually_downloadable (line 141) | def test_is_manually_downloadable(self):
  class TestTranscriptionModel (line 149) | class TestTranscriptionModel:
    method test_str_whisper (line 150) | def test_str_whisper(self):
    method test_str_whisper_cpp (line 156) | def test_str_whisper_cpp(self):
    method test_str_hugging_face (line 162) | def test_str_hugging_face(self):
    method test_str_faster_whisper (line 169) | def test_str_faster_whisper(self):
    method test_str_openai_api (line 176) | def test_str_openai_api(self):
    method test_default (line 180) | def test_default(self):
    method test_get_local_model_path_openai_api (line 185) | def test_get_local_model_path_openai_api(self):
  class TestGetExpectedWhisperModelSize (line 190) | class TestGetExpectedWhisperModelSize:
    method test_known_sizes (line 191) | def test_known_sizes(self):
    method test_unknown_size_returns_none (line 195) | def test_unknown_size_returns_none(self):
    method test_all_defined_sizes_have_values (line 199) | def test_all_defined_sizes_have_values(self):
  class TestGetWhisperFilePath (line 204) | class TestGetWhisperFilePath:
    method test_custom_size (line 205) | def test_custom_size(self):
    method test_tiny_size (line 210) | def test_tiny_size(self):
  class TestTranscriptionModelIsDeletable (line 216) | class TestTranscriptionModelIsDeletable:
    method test_whisper_model_not_downloaded (line 217) | def test_whisper_model_not_downloaded(self):
    method test_whisper_model_downloaded (line 222) | def test_whisper_model_downloaded(self):
    method test_openai_api_not_deletable (line 227) | def test_openai_api_not_deletable(self):
    method test_hugging_face_not_deletable (line 231) | def test_hugging_face_not_deletable(self):
  class TestTranscriptionModelGetLocalModelPath (line 239) | class TestTranscriptionModelGetLocalModelPath:
    method test_whisper_cpp_file_not_exists (line 240) | def test_whisper_cpp_file_not_exists(self):
    method test_whisper_file_not_exists (line 246) | def test_whisper_file_not_exists(self):
    method test_whisper_file_too_small (line 251) | def test_whisper_file_too_small(self):
    method test_whisper_file_valid (line 258) | def test_whisper_file_valid(self):
    method test_faster_whisper_not_found (line 267) | def test_faster_whisper_not_found(self):
    method test_hugging_face_not_found (line 272) | def test_hugging_face_not_found(self):
  class TestTranscriptionModelOpenPath (line 282) | class TestTranscriptionModelOpenPath:
    method test_open_path_linux (line 283) | def test_open_path_linux(self):
    method test_open_path_darwin (line 289) | def test_open_path_darwin(self):
  class TestTranscriptionModelOpenFileLocation (line 296) | class TestTranscriptionModelOpenFileLocation:
    method test_whisper_opens_parent_directory (line 297) | def test_whisper_opens_parent_directory(self):
    method test_hugging_face_opens_grandparent_directory (line 304) | def test_hugging_face_opens_grandparent_directory(self):
    method test_faster_whisper_opens_grandparent_directory (line 315) | def test_faster_whisper_opens_grandparent_directory(self):
    method test_no_model_path_does_nothing (line 323) | def test_no_model_path_does_nothing(self):
  class TestTranscriptionModelDeleteLocalFile (line 331) | class TestTranscriptionModelDeleteLocalFile:
    method test_whisper_model_removes_file (line 332) | def test_whisper_model_removes_file(self, tmp_path):
    method test_whisper_cpp_custom_removes_file (line 340) | def test_whisper_cpp_custom_removes_file(self, tmp_path):
    method test_whisper_cpp_non_custom_removes_bin_file (line 348) | def test_whisper_cpp_non_custom_removes_bin_file(self, tmp_path):
    method test_whisper_cpp_non_custom_removes_coreml_files (line 356) | def test_whisper_cpp_non_custom_removes_coreml_files(self, tmp_path):
    method test_hugging_face_removes_directory_tree (line 370) | def test_hugging_face_removes_directory_tree(self, tmp_path):
    method test_faster_whisper_removes_directory_tree (line 389) | def test_faster_whisper_removes_directory_tree(self, tmp_path):
  class TestHuggingfaceDownloadMonitorFileSize (line 404) | class TestHuggingfaceDownloadMonitorFileSize:
    method _make_monitor (line 405) | def _make_monitor(self, tmp_path):
    method test_emits_progress_for_tmp_files (line 417) | def test_emits_progress_for_tmp_files(self, tmp_path):
    method test_emits_progress_for_incomplete_files (line 451) | def test_emits_progress_for_incomplete_files(self, tmp_path):
    method test_stop_monitoring_emits_100_percent (line 471) | def test_stop_monitoring_emits_100_percent(self, tmp_path):
  class TestModelDownloaderDownloadModel (line 480) | class TestModelDownloaderDownloadModel:
    method _make_downloader (line 481) | def _make_downloader(self, model):
    method test_download_model_fresh_success (line 488) | def test_download_model_fresh_success(self, tmp_path):
    method test_download_model_already_downloaded_sha256_match (line 511) | def test_download_model_already_downloaded_sha256_match(self, tmp_path):
    method test_download_model_sha256_mismatch_redownloads (line 534) | def test_download_model_sha256_mismatch_redownloads(self, tmp_path):
    method test_download_model_stopped_mid_download (line 569) | def test_download_model_stopped_mid_download(self, tmp_path):
    method test_download_model_resumes_partial (line 596) | def test_download_model_resumes_partial(self, tmp_path):
  class TestModelDownloaderWhisperCpp (line 637) | class TestModelDownloaderWhisperCpp:
    method _make_downloader (line 638) | def _make_downloader(self, model, custom_url=None):
    method test_standard_model_calls_download_from_huggingface (line 646) | def test_standard_model_calls_download_from_huggingface(self):
    method test_lumii_model_uses_lumii_repo (line 668) | def test_lumii_model_uses_lumii_repo(self):
    method test_custom_url_calls_download_model_to_path (line 686) | def test_custom_url_calls_download_model_to_path(self):
    method test_coreml_model_includes_mlmodelc_in_file_list (line 701) | def test_coreml_model_includes_mlmodelc_in_file_list(self):
    method test_coreml_zip_extracted_and_existing_dir_removed (line 724) | def test_coreml_zip_extracted_and_existing_dir_removed(self, tmp_path):
  class TestModelLoaderCertifiImportError (line 753) | class TestModelLoaderCertifiImportError:
    method test_certifi_import_error_path (line 754) | def test_certifi_import_error_path(self):
    method test_configure_http_backend_import_error (line 763) | def test_configure_http_backend_import_error(self):

FILE: tests/recording_test.py
  class TestRecordingAmplitudeListenerInit (line 8) | class TestRecordingAmplitudeListenerInit:
    method test_initial_buffer_is_empty (line 9) | def test_initial_buffer_is_empty(self):
    method test_initial_accumulation_size_is_zero (line 15) | def test_initial_accumulation_size_is_zero(self):
  class TestRecordingAmplitudeListenerStreamCallback (line 20) | class TestRecordingAmplitudeListenerStreamCallback:
    method _make_listener (line 21) | def _make_listener(self) -> RecordingAmplitudeListener:
    method test_emits_amplitude_changed (line 26) | def test_emits_amplitude_changed(self):
    method test_amplitude_is_rms (line 37) | def test_amplitude_is_rms(self):
    method test_accumulates_buffer (line 47) | def test_accumulates_buffer(self):
    method test_emits_average_amplitude_when_buffer_full (line 54) | def test_emits_average_amplitude_when_buffer_full(self):
    method test_resets_buffer_after_emitting_average (line 67) | def test_resets_buffer_after_emitting_average(self):
    method test_does_not_emit_average_before_buffer_full (line 77) | def test_does_not_emit_average_before_buffer_full(self):
    method test_average_amplitude_is_rms_of_accumulated_buffer (line 89) | def test_average_amplitude_is_rms_of_accumulated_buffer(self):
  class TestRecordingAmplitudeListenerStart (line 105) | class TestRecordingAmplitudeListenerStart:
    method test_accumulation_size_set_from_sample_rate (line 106) | def test_accumulation_size_set_from_sample_rate(self):

FILE: tests/recording_transcriber_test.py
  function make_transcriber (line 14) | def make_transcriber(
  class TestRecordingTranscriberInit (line 41) | class TestRecordingTranscriberInit:
    method test_default_batch_size_is_5_seconds (line 42) | def test_default_batch_size_is_5_seconds(self):
    method test_append_and_correct_mode_batch_size_uses_transcription_step (line 46) | def test_append_and_correct_mode_batch_size_uses_transcription_step(se...
    method test_append_and_correct_mode_keep_sample_seconds (line 51) | def test_append_and_correct_mode_keep_sample_seconds(self):
    method test_default_keep_sample_seconds (line 56) | def test_default_keep_sample_seconds(self):
    method test_queue_starts_empty (line 60) | def test_queue_starts_empty(self):
    method test_max_queue_size_is_three_batches (line 64) | def test_max_queue_size_is_three_batches(self):
  class TestAmplitude (line 69) | class TestAmplitude:
    method test_silence_returns_zero (line 70) | def test_silence_returns_zero(self):
    method test_unit_signal_returns_one (line 74) | def test_unit_signal_returns_one(self):
    method test_rms_calculation (line 78) | def test_rms_calculation(self):
  class TestStreamCallback (line 84) | class TestStreamCallback:
    method test_emits_amplitude_changed (line 85) | def test_emits_amplitude_changed(self):
    method test_appends_to_queue_when_not_full (line 95) | def test_appends_to_queue_when_not_full(self):
    method test_drops_chunk_when_queue_full (line 102) | def test_drops_chunk_when_queue_full(self):
    method test_thread_safety_with_concurrent_callbacks (line 113) | def test_thread_safety_with_concurrent_callbacks(self):
  class TestGetDeviceSampleRate (line 133) | class TestGetDeviceSampleRate:
    method test_returns_whisper_sample_rate_when_supported (line 134) | def test_returns_whisper_sample_rate_when_supported(self):
    method test_falls_back_to_device_default_sample_rate (line 139) | def test_falls_back_to_device_default_sample_rate(self):
    method test_falls_back_to_whisper_rate_when_query_returns_non_dict (line 145) | def test_falls_back_to_whisper_rate_when_query_returns_non_dict(self):
  class TestStopRecording (line 152) | class TestStopRecording:
    method test_sets_is_running_false (line 153) | def test_sets_is_running_false(self):
    method test_terminates_running_process (line 159) | def test_terminates_running_process(self):
    method test_kills_process_on_timeout (line 169) | def test_kills_process_on_timeout(self):
    method test_skips_terminate_when_process_already_stopped (line 181) | def test_skips_terminate_when_process_already_stopped(self):
  class TestStartWithSilence (line 192) | class TestStartWithSilence:
    method _run_with_mock_model (line 195) | def _run_with_mock_model(self, transcription_options, samples, expecte...
    method test_silent_audio_skips_transcription (line 246) | def test_silent_audio_skips_transcription(self):
  class TestStartPortAudioError (line 283) | class TestStartPortAudioError:
    method test_emits_error_on_portaudio_failure (line 284) | def test_emits_error_on_portaudio_failure(self):

FILE: tests/settings/settings_test.py
  class TestSettings (line 7) | class TestSettings:
    method test_transcription_tasks_table_column_order_key (line 8) | def test_transcription_tasks_table_column_order_key(self):
    method test_transcription_tasks_table_column_widths_key (line 13) | def test_transcription_tasks_table_column_widths_key(self):
    method test_transcription_tasks_table_column_visibility_key_exists (line 18) | def test_transcription_tasks_table_column_visibility_key_exists(self):
    method test_transcription_tasks_table_sort_state_key (line 23) | def test_transcription_tasks_table_sort_state_key(self):
    method test_all_transcription_tasks_table_keys_are_strings (line 28) | def test_all_transcription_tasks_table_keys_are_strings(self):
    method test_transcription_tasks_table_keys_have_correct_prefix (line 35) | def test_transcription_tasks_table_keys_have_correct_prefix(self):
    method test_transcription_tasks_table_keys_are_unique (line 44) | def test_transcription_tasks_table_keys_are_unique(self):
    method test_settings_key_enum_values (line 55) | def test_settings_key_enum_values(self):
    method test_settings_key_immutability (line 69) | def test_settings_key_immutability(self):
    method test_settings_key_format_consistency (line 84) | def test_settings_key_format_consistency(self):
    method test_settings_key_length (line 101) | def test_settings_key_length(self):
    method test_settings_key_naming_convention (line 114) | def test_settings_key_naming_convention(self):
    method test_settings_key_usage_in_code (line 129) | def test_settings_key_usage_in_code(self):

FILE: tests/store/keyring_store_test.py
  class TestKey (line 27) | class TestKey:
    method test_openai_api_key_exists (line 28) | def test_openai_api_key_exists(self):
    method test_openai_api_key_value (line 31) | def test_openai_api_key_value(self):
    method test_key_is_enum (line 34) | def test_key_is_enum(self):
  class TestIsLinux (line 38) | class TestIsLinux:
    method test_returns_true_on_linux (line 40) | def test_returns_true_on_linux(self):
    method test_returns_true_on_linux2 (line 44) | def test_returns_true_on_linux2(self):
    method test_returns_false_on_macos (line 48) | def test_returns_false_on_macos(self):
    method test_returns_false_on_windows (line 52) | def test_returns_false_on_windows(self):
  class TestDeriveKey (line 56) | class TestDeriveKey:
    method test_derive_key_returns_32_bytes (line 57) | def test_derive_key_returns_32_bytes(self):
    method test_derive_key_is_deterministic (line 63) | def test_derive_key_is_deterministic(self):
    method test_derive_key_different_for_different_names (line 70) | def test_derive_key_different_for_different_names(self):
    method test_derive_key_different_for_different_secrets (line 76) | def test_derive_key_different_for_different_secrets(self):
  class TestEncryptDecrypt (line 83) | class TestEncryptDecrypt:
    method test_encrypt_decrypt_roundtrip (line 84) | def test_encrypt_decrypt_roundtrip(self):
    method test_encrypt_decrypt_empty_string (line 91) | def test_encrypt_decrypt_empty_string(self):
    method test_encrypt_decrypt_unicode (line 98) | def test_encrypt_decrypt_unicode(self):
    method test_encrypt_decrypt_long_string (line 105) | def test_encrypt_decrypt_long_string(self):
    method test_encrypted_is_base64 (line 112) | def test_encrypted_is_base64(self):
    method test_different_keys_produce_different_ciphertext (line 120) | def test_different_keys_produce_different_ciphertext(self):
  class TestLocalSecrets (line 129) | class TestLocalSecrets:
    method test_load_empty_file (line 130) | def test_load_empty_file(self):
    method test_save_and_load_secrets (line 139) | def test_save_and_load_secrets(self):
    method test_save_sets_restrictive_permissions (line 152) | def test_save_sets_restrictive_permissions(self):
    method test_load_handles_corrupted_json (line 164) | def test_load_handles_corrupted_json(self):
  class TestPortalPassword (line 177) | class TestPortalPassword:
    method test_get_portal_password_returns_none_when_no_portal (line 180) | def test_get_portal_password_returns_none_when_no_portal(
    method test_get_portal_password_returns_none_when_key_not_found (line 189) | def test_get_portal_password_returns_none_when_key_not_found(
    method test_get_portal_password_decrypts_stored_value (line 199) | def test_get_portal_password_decrypts_stored_value(self, mock_load, mo...
    method test_set_portal_password_returns_false_when_no_portal (line 213) | def test_set_portal_password_returns_false_when_no_portal(self, mock_p...
    method test_set_portal_password_encrypts_and_saves (line 221) | def test_set_portal_password_encrypts_and_saves(
  class TestDeletePortalPassword (line 241) | class TestDeletePortalPassword:
    method test_delete_existing_key (line 244) | def test_delete_existing_key(self, mock_save, mock_load):
    method test_delete_nonexistent_key (line 256) | def test_delete_nonexistent_key(self, mock_save, mock_load):
  class TestGetPassword (line 265) | class TestGetPassword:
    method test_returns_portal_password_on_linux (line 269) | def test_returns_portal_password_on_linux(
    method test_falls_back_to_keyring_when_portal_returns_none (line 283) | def test_falls_back_to_keyring_when_portal_returns_none(
    method test_uses_keyring_directly_on_non_linux (line 299) | def test_uses_keyring_directly_on_non_linux(self, mock_keyring, mock_i...
    method test_returns_empty_string_when_keyring_returns_none (line 309) | def test_returns_empty_string_when_keyring_returns_none(
    method test_returns_empty_string_on_keyring_exception (line 321) | def test_returns_empty_string_on_keyring_exception(
  class TestSetPassword (line 332) | class TestSetPassword:
    method test_uses_portal_on_linux_when_successful (line 336) | def test_uses_portal_on_linux_when_successful(
    method test_falls_back_to_keyring_when_portal_fails (line 350) | def test_falls_back_to_keyring_when_portal_fails(
    method test_uses_keyring_directly_on_non_linux (line 364) | def test_uses_keyring_directly_on_non_linux(self, mock_keyring, mock_i...
  class TestDeletePassword (line 374) | class TestDeletePassword:
    method test_deletes_from_both_on_linux (line 378) | def test_deletes_from_both_on_linux(
    method test_deletes_from_keyring_only_on_non_linux (line 393) | def test_deletes_from_keyring_only_on_non_linux(self, mock_keyring, mo...
    method test_ignores_password_delete_error (line 404) | def test_ignores_password_delete_error(self, mock_keyring, mock_is_lin...
    method test_handles_other_keyring_exceptions (line 416) | def test_handles_other_keyring_exceptions(self, mock_keyring, mock_is_...
  class TestIntegration (line 425) | class TestIntegration:
    method test_full_roundtrip_with_portal (line 429) | def test_full_roundtrip_with_portal(self, mock_portal):

FILE: tests/transcriber/file_transcriber_queue_worker_test.py
  function qapp (line 14) | def qapp():
  function worker (line 23) | def worker(qapp):
  function simple_worker (line 36) | def simple_worker(qapp):
  class TestFileTranscriberQueueWorker (line 42) | class TestFileTranscriberQueueWorker:
    method test_cancel_task_adds_to_canceled_set (line 43) | def test_cancel_task_adds_to_canceled_set(self, simple_worker):
    method test_add_task_removes_from_canceled (line 48) | def test_add_task_removes_from_canceled(self, simple_worker):
    method test_on_task_error_with_cancellation (line 70) | def test_on_task_error_with_cancellation(self, simple_worker):
    method test_on_task_error_with_regular_error (line 89) | def test_on_task_error_with_regular_error(self, simple_worker):
    method test_on_task_progress_conversion (line 108) | def test_on_task_progress_conversion(self, simple_worker):
    method test_stop_puts_sentinel_in_queue (line 128) | def test_stop_puts_sentinel_in_queue(self, simple_worker):
    method test_on_task_completed_with_speech_path (line 134) | def test_on_task_completed_with_speech_path(self, simple_worker, tmp_p...
    method test_on_task_completed_speech_path_missing (line 160) | def test_on_task_completed_speech_path_missing(self, simple_worker, tm...
    method test_on_task_download_progress (line 183) | def test_on_task_download_progress(self, simple_worker):
    method test_cancel_task_stops_current_transcriber (line 204) | def test_cancel_task_stops_current_transcriber(self, simple_worker):
    method test_on_task_error_task_in_canceled_set (line 223) | def test_on_task_error_task_in_canceled_set(self, simple_worker):
  class TestFileTranscriberQueueWorkerRun (line 245) | class TestFileTranscriberQueueWorkerRun:
    method _make_task (line 246) | def _make_task(self, model_type=ModelType.WHISPER_CPP, extract_speech=...
    method test_run_returns_early_when_already_running (line 258) | def test_run_returns_early_when_already_running(self, simple_worker):
    method test_run_stops_on_sentinel (line 265) | def test_run_stops_on_sentinel(self, simple_worker, qapp):
    method test_run_skips_canceled_task_then_stops_on_sentinel (line 275) | def test_run_skips_canceled_task_then_stops_on_sentinel(self, simple_w...
    method test_run_creates_openai_transcriber (line 292) | def test_run_creates_openai_transcriber(self, simple_worker, qapp):
    method test_run_creates_whisper_transcriber_for_whisper_cpp (line 308) | def test_run_creates_whisper_transcriber_for_whisper_cpp(self, simple_...
    method test_run_speech_extraction_failure_emits_error (line 322) | def test_run_speech_extraction_failure_emits_error(self, simple_worker...
  function test_transcription_with_whisper_cpp_tiny_no_speech_extraction (line 339) | def test_transcription_with_whisper_cpp_tiny_no_speech_extraction(worker):
  function test_transcription_with_whisper_cpp_tiny_with_speech_extraction (line 370) | def test_transcription_with_whisper_cpp_tiny_with_speech_extraction(work...

FILE: tests/transcriber/openai_whisper_api_file_transcriber_test.py
  class TestAppendSegment (line 20) | class TestAppendSegment:
    method test_valid_utf8 (line 21) | def test_valid_utf8(self):
    method test_empty_bytes (line 30) | def test_empty_bytes(self):
    method test_invalid_utf8 (line 36) | def test_invalid_utf8(self):
    method test_multibyte_utf8 (line 43) | def test_multibyte_utf8(self):
  class TestGetValue (line 51) | class TestGetValue:
    method test_get_value_from_dict (line 52) | def test_get_value_from_dict(self):
    method test_get_value_from_object (line 57) | def test_get_value_from_object(self):
    method test_get_value_missing_key_dict (line 66) | def test_get_value_missing_key_dict(self):
    method test_get_value_missing_attribute_object (line 71) | def test_get_value_missing_attribute_object(self):
  class TestOpenAIWhisperAPIFileTranscriber (line 80) | class TestOpenAIWhisperAPIFileTranscriber:
    method mock_openai_client (line 82) | def mock_openai_client(self):
    method test_transcribe (line 98) | def test_transcribe(self, mock_openai_client):

FILE: tests/transcriber/recording_transcriber_test.py
  class TestAmplitude (line 19) | class TestAmplitude:
    method test_symmetric_array (line 20) | def test_symmetric_array(self):
    method test_asymmetric_array (line 26) | def test_asymmetric_array(self):
    method test_all_zeros (line 32) | def test_all_zeros(self):
    method test_all_positive (line 37) | def test_all_positive(self):
    method test_all_negative (line 43) | def test_all_negative(self):
    method test_returns_float (line 49) | def test_returns_float(self):
  class TestGetDeviceSampleRate (line 55) | class TestGetDeviceSampleRate:
    method test_returns_default_16khz_when_supported (line 56) | def test_returns_default_16khz_when_supported(self):
    method test_falls_back_to_device_default (line 61) | def test_falls_back_to_device_default(self):
    method test_returns_default_when_query_fails (line 74) | def test_returns_default_when_query_fails(self):
  class TestRecordingTranscriber (line 86) | class TestRecordingTranscriber:
    method test_should_transcribe (line 88) | def test_should_transcribe(self, qtbot):
  class TestRecordingTranscriberInit (line 151) | class TestRecordingTranscriberInit:
    method test_init_default_mode (line 152) | def test_init_default_mode(self):
    method test_init_append_and_correct_mode (line 177) | def test_init_append_and_correct_mode(self):
    method test_init_stores_silence_threshold (line 204) | def test_init_stores_silence_threshold(self):
    method test_init_uses_default_sample_rate_when_none (line 223) | def test_init_uses_default_sample_rate_when_none(self):
  class TestStreamCallback (line 243) | class TestStreamCallback:
    method test_stream_callback_adds_to_queue (line 244) | def test_stream_callback_adds_to_queue(self):
    method test_stream_callback_emits_amplitude_changed (line 269) | def test_stream_callback_emits_amplitude_changed(self):
    method test_stream_callback_drops_data_when_queue_full (line 297) | def test_stream_callback_drops_data_when_queue_full(self):
  class TestStopRecording (line 325) | class TestStopRecording:
    method test_stop_recording_sets_is_running_false (line 326) | def test_stop_recording_sets_is_running_false(self):
    method test_stop_recording_terminates_process (line 347) | def test_stop_recording_terminates_process(self):
    method test_stop_recording_skips_terminated_process (line 374) | def test_stop_recording_skips_terminated_process(self):
  class TestStartLocalWhisperServer (line 402) | class TestStartLocalWhisperServer:
    method test_start_local_whisper_server_creates_openai_client (line 403) | def test_start_local_whisper_server_creates_openai_client(self):
    method test_start_local_whisper_server_with_language (line 439) | def test_start_local_whisper_server_with_language(self):
    method test_start_local_whisper_server_auto_language (line 475) | def test_start_local_whisper_server_auto_language(self):
    method test_start_local_whisper_server_handles_failure (line 511) | def test_start_local_whisper_server_handles_failure(self):

FILE: tests/transcriber/transcriber_test.py
  class TestToTimestamp (line 12) | class TestToTimestamp:
    method test_to_timestamp (line 13) | def test_to_timestamp(self):
  function test_write_output (line 32) | def test_write_output(

FILE: tests/transcriber/transformers_whisper_test.py
  class TestIsIntelMac (line 11) | class TestIsIntelMac:
    method test_is_intel_mac (line 22) | def test_is_intel_mac(self, sys_platform, machine, expected):
  class TestIsPeftModel (line 28) | class TestIsPeftModel:
    method test_peft_detection (line 39) | def test_peft_detection(self, model_id, expected):
  class TestGetPeftRepoId (line 43) | class TestGetPeftRepoId:
    method test_repo_id_returned_as_is (line 44) | def test_repo_id_returned_as_is(self):
    method test_linux_cache_path (line 49) | def test_linux_cache_path(self):
    method test_windows_cache_path (line 56) | def test_windows_cache_path(self):
    method test_fallback_returns_model_id (line 63) | def test_fallback_returns_model_id(self):
  class TestGetMmsRepoId (line 69) | class TestGetMmsRepoId:
    method test_repo_id_returned_as_is (line 72) | def test_repo_id_returned_as_is(self):
    method test_linux_cache_path (line 78) | def test_linux_cache_path(self):
    method test_windows_cache_path (line 86) | def test_windows_cache_path(self):
    method test_fallback_returns_model_id (line 94) | def test_fallback_returns_model_id(self):
    method test_nested_org_name (line 100) | def test_nested_org_name(self):

FILE: tests/transcriber/whisper_cpp_test.py
  class TestWhisperCpp (line 16) | class TestWhisperCpp:
    method test_transcribe (line 17) | def test_transcribe(self):
    method test_transcribe_word_level_timestamps (line 42) | def test_transcribe_word_level_timestamps(self):
    method test_transcribe_chinese_multibyte_word_level_timestamps (line 67) | def test_transcribe_chinese_multibyte_word_level_timestamps(self):
    method test_transcribe_chinese_mixed_complete_and_split_chars (line 162) | def test_transcribe_chinese_mixed_complete_and_split_chars(self):

FILE: tests/transcriber/whisper_file_transcriber_test.py
  class TestCheckFileHasAudioStream (line 33) | class TestCheckFileHasAudioStream:
    method test_valid_audio_file (line 34) | def test_valid_audio_file(self):
    method test_missing_file (line 38) | def test_missing_file(self):
    method test_invalid_media_file (line 42) | def test_invalid_media_file(self):
  class TestProgressRegex (line 54) | class TestProgressRegex:
    method test_integer_percentage (line 55) | def test_integer_percentage(self):
    method test_decimal_percentage (line 60) | def test_decimal_percentage(self):
    method test_no_match (line 65) | def test_no_match(self):
    method test_extract_percentage_value (line 69) | def test_extract_percentage_value(self):
  class TestWhisperFileTranscriber (line 77) | class TestWhisperFileTranscriber:
    method test_default_output_file (line 95) | def test_default_output_file(
    method test_default_output_file_with_date (line 130) | def test_default_output_file_with_date(
    method test_transcribe_from_file (line 231) | def test_transcribe_from_file(
    method test_transcribe_from_url (line 283) | def test_transcribe_from_url(self, qtbot):
    method test_transcribe_from_folder_watch_source (line 326) | def test_transcribe_from_folder_watch_source(self, qtbot):
    method test_transcribe_from_folder_watch_source_deletes_file (line 360) | def test_transcribe_from_folder_watch_source_deletes_file(self, qtbot):
    method test_transcribe_stop (line 397) | def test_transcribe_stop(self):

FILE: tests/transformers_transcriber_test.py
  class TestTransformersTranscriber (line 8) | class TestTransformersTranscriber:
    method test_should_transcribe (line 13) | def test_should_transcribe(self):

FILE: tests/translator_test.py
  class TestParseBatchResponse (line 14) | class TestParseBatchResponse:
    method test_simple_batch (line 15) | def test_simple_batch(self):
    method test_missing_entries_fallback (line 22) | def test_missing_entries_fallback(self):
    method test_multiline_entries (line 30) | def test_multiline_entries(self):
    method test_single_item_batch (line 37) | def test_single_item_batch(self):
    method test_empty_response (line 43) | def test_empty_response(self):
    method test_whitespace_handling (line 50) | def test_whitespace_handling(self):
    method test_out_of_order_entries (line 56) | def test_out_of_order_entries(self):
  class TestTranslator (line 63) | class TestTranslator:
    method test_start (line 66) | def test_start(self, mock_queue, mock_openai, qtbot):
    method test_translator (line 106) | def test_translator(self, mock_openai, qtbot):

FILE: tests/update_checker_test.py
  function checker (line 26) | def checker(settings: Settings) -> UpdateChecker:
  class TestShouldCheckForUpdates (line 32) | class TestShouldCheckForUpdates:
    method test_returns_false_on_linux (line 33) | def test_returns_false_on_linux(self, checker: UpdateChecker):
    method test_returns_true_on_windows_first_run (line 37) | def test_returns_true_on_windows_first_run(self, checker: UpdateChecke...
    method test_returns_true_on_macos_first_run (line 42) | def test_returns_true_on_macos_first_run(self, checker: UpdateChecker,...
    method test_returns_false_when_checked_recently (line 47) | def test_returns_false_when_checked_recently(
    method test_returns_true_when_check_is_overdue (line 56) | def test_returns_true_when_check_is_overdue(
    method test_returns_true_on_invalid_date_in_settings (line 65) | def test_returns_true_on_invalid_date_in_settings(
  class TestIsNewerVersion (line 74) | class TestIsNewerVersion:
    method test_newer_major (line 75) | def test_newer_major(self, checker: UpdateChecker):
    method test_newer_minor (line 79) | def test_newer_minor(self, checker: UpdateChecker):
    method test_newer_patch (line 83) | def test_newer_patch(self, checker: UpdateChecker):
    method test_same_version (line 87) | def test_same_version(self, checker: UpdateChecker):
    method test_older_version (line 91) | def test_older_version(self, checker: UpdateChecker):
    method test_different_segment_count (line 95) | def test_different_segment_count(self, checker: UpdateChecker):
    method test_invalid_version_returns_false (line 99) | def test_invalid_version_returns_false(self, checker: UpdateChecker):
  class TestGetDownloadUrl (line 104) | class TestGetDownloadUrl:
    method test_windows_returns_windows_urls (line 105) | def test_windows_returns_windows_urls(self, checker: UpdateChecker):
    method test_macos_arm_returns_arm_urls (line 110) | def test_macos_arm_returns_arm_urls(self, checker: UpdateChecker):
    method test_macos_x86_returns_x86_urls (line 116) | def test_macos_x86_returns_x86_urls(self, checker: UpdateChecker):
    method test_linux_returns_empty (line 122) | def test_linux_returns_empty(self, checker: UpdateChecker):
    method test_wraps_plain_string_in_list (line 127) | def test_wraps_plain_string_in_list(self, checker: UpdateChecker):
  class TestCheckForUpdates (line 133) | class TestCheckForUpdates:
    method _make_checker (line 134) | def _make_checker(self, settings: Settings, version_data: dict) -> Upd...
    method test_emits_update_available_when_newer_version (line 140) | def test_emits_update_available_when_newer_version(self, settings: Set...
    method test_does_not_emit_when_version_is_current (line 156) | def test_does_not_emit_when_version_is_current(self, settings: Settings):
    method test_skips_network_call_on_linux (line 166) | def test_skips_network_call_on_linux(self, settings: Settings):
    method test_stores_last_check_date_after_reply (line 176) | def test_stores_last_check_date_after_reply(self, settings: Settings):
    method test_stores_available_version_when_update_found (line 186) | def test_stores_available_version_when_update_found(self, settings: Se...
    method test_clears_available_version_when_up_to_date (line 195) | def test_clears_available_version_when_up_to_date(self, settings: Sett...

FILE: tests/widgets/advanced_settings_dialog_test.py
  class TestAdvancedSettingsDialogSilenceThreshold (line 8) | class TestAdvancedSettingsDialogSilenceThreshold:
    method test_silence_threshold_spinbox_hidden_by_default (line 9) | def test_silence_threshold_spinbox_hidden_by_default(self, qtbot: QtBot):
    method test_silence_threshold_spinbox_shown_when_recording_settings (line 16) | def test_silence_threshold_spinbox_shown_when_recording_settings(self,...
    method test_silence_threshold_spinbox_initial_value (line 26) | def test_silence_threshold_spinbox_initial_value(self, qtbot: QtBot):
    method test_silence_threshold_change_updates_options (line 35) | def test_silence_threshold_change_updates_options(self, qtbot: QtBot):
    method test_silence_threshold_change_emits_signal (line 45) | def test_silence_threshold_change_emits_signal(self, qtbot: QtBot):
  class TestAdvancedSettingsDialogLineSeparator (line 62) | class TestAdvancedSettingsDialogLineSeparator:
    method test_line_separator_shown_when_recording_settings (line 63) | def test_line_separator_shown_when_recording_settings(self, qtbot: QtB...
    method test_line_separator_hidden_by_default (line 72) | def test_line_separator_hidden_by_default(self, qtbot: QtBot):
    method test_line_separator_initial_value_displayed_as_escape (line 78) | def test_line_separator_initial_value_displayed_as_escape(self, qtbot:...
    method test_line_separator_change_updates_options (line 86) | def test_line_separator_change_updates_options(self, qtbot: QtBot):
    method test_line_separator_change_emits_signal (line 95) | def test_line_separator_change_emits_signal(self, qtbot: QtBot):
    method test_line_separator_invalid_escape_does_not_crash (line 107) | def test_line_separator_invalid_escape_does_not_crash(self, qtbot: QtB...
    method test_line_separator_tab_character (line 117) | def test_line_separator_tab_character(self, qtbot: QtBot):
    method test_line_separator_plain_text (line 126) | def test_line_separator_plain_text(self, qtbot: QtBot):
  class TestTranscriptionOptionsLineSeparator (line 136) | class TestTranscriptionOptionsLineSeparator:
    method test_default_line_separator (line 137) | def test_default_line_separator(self):
    method test_custom_line_separator (line 141) | def test_custom_line_separator(self):
  class TestTranscriptionOptionsSilenceThreshold (line 146) | class TestTranscriptionOptionsSilenceThreshold:
    method test_default_silence_threshold (line 147) | def test_default_silence_threshold(self):
    method test_custom_silence_threshold (line 151) | def test_custom_silence_threshold(self):

FILE: tests/widgets/audio_meter_widget_test.py
  class TestAudioMeterWidget (line 7) | class TestAudioMeterWidget:
    method test_initial_amplitude_is_zero (line 8) | def test_initial_amplitude_is_zero(self, qtbot: QtBot):
    method test_initial_average_amplitude_is_zero (line 13) | def test_initial_average_amplitude_is_zero(self, qtbot: QtBot):
    method test_update_amplitude (line 18) | def test_update_amplitude(self, qtbot: QtBot):
    method test_update_amplitude_smoothing (line 24) | def test_update_amplitude_smoothing(self, qtbot: QtBot):
    method test_update_average_amplitude (line 33) | def test_update_average_amplitude(self, qtbot: QtBot):
    method test_reset_amplitude_clears_current (line 39) | def test_reset_amplitude_clears_current(self, qtbot: QtBot):
    method test_reset_amplitude_clears_average (line 46) | def test_reset_amplitude_clears_average(self, qtbot: QtBot):
    method test_fixed_height (line 53) | def test_fixed_height(self, qtbot: QtBot):

FILE: tests/widgets/audio_player_test.py
  function assert_approximately_equal (line 14) | def assert_approximately_equal(actual, expected, tolerance=0.001):
  class TestAudioPlayer (line 19) | class TestAudioPlayer:
    method test_should_load_audio (line 20) | def test_should_load_audio(self, qtbot: QtBot):
    method test_should_update_time_label (line 28) | def test_should_update_time_label(self, qtbot: QtBot):
    method test_should_toggle_play_button_icon (line 40) | def test_should_toggle_play_button_icon(self, qtbot: QtBot):
    method test_should_have_basic_audio_controls (line 53) | def test_should_have_basic_audio_controls(self, qtbot: QtBot):
    method test_should_change_playback_rate_directly (line 66) | def test_should_change_playback_rate_directly(self, qtbot: QtBot):
    method test_should_handle_custom_playback_rates (line 75) | def test_should_handle_custom_playback_rates(self, qtbot: QtBot):
    method test_should_handle_various_playback_rates (line 83) | def test_should_handle_various_playback_rates(self, qtbot: QtBot):
    method test_should_use_single_row_layout (line 95) | def test_should_use_single_row_layout(self, qtbot: QtBot):
    method test_should_persist_playback_rate_setting (line 105) | def test_should_persist_playback_rate_setting(self, qtbot: QtBot):
    method test_should_handle_range_looping (line 116) | def test_should_handle_range_looping(self, qtbot: QtBot):
    method test_should_handle_invalid_media (line 128) | def test_should_handle_invalid_media(self, qtbot: QtBot):
    method test_should_stop_playback (line 140) | def test_should_stop_playback(self, qtbot: QtBot):
    method test_should_handle_media_status_changes (line 148) | def test_should_handle_media_status_changes(self, qtbot: QtBot):

FILE: tests/widgets/conftest.py
  function mock_get_password (line 9) | def mock_get_password():
  function force_gc_between_tests (line 15) | def force_gc_between_tests():
  function reset_settings (line 21) | def reset_settings():

FILE: tests/widgets/export_transcription_menu_test.py
  class TranslationSignal (line 18) | class TranslationSignal(QObject):
  class TestExportTranscriptionMenu (line 22) | class TestExportTranscriptionMenu:
    method transcription (line 24) | def transcription(
    method test_should_export_segments (line 45) | def test_should_export_segments(

FILE: tests/widgets/file_transcriber_widget_test.py
  class TestFileTranscriberWidget (line 10) | class TestFileTranscriberWidget:
    method test_should_set_window_title (line 11) | def test_should_set_window_title(self, qtbot: QtBot):
    method test_should_emit_triggered_event (line 18) | def test_should_emit_triggered_event(self, qtbot: QtBot):

FILE: tests/widgets/hugging_face_search_line_edit_test.py
  function widget (line 15) | def widget(qtbot: QtBot):
  class TestHuggingFaceSearchLineEdit (line 27) | class TestHuggingFaceSearchLineEdit:
    method test_initial_state (line 28) | def test_initial_state(self, widget):
    method test_default_value_set (line 32) | def test_default_value_set(self, qtbot: QtBot):
    method test_on_text_edited_emits_model_selected (line 40) | def test_on_text_edited_emits_model_selected(self, widget, qtbot: QtBot):
    method test_fetch_models_skips_short_text (line 46) | def test_fetch_models_skips_short_text(self, widget):
    method test_fetch_models_makes_request_for_long_text (line 51) | def test_fetch_models_makes_request_for_long_text(self, widget):
    method test_fetch_models_url_contains_search_text (line 59) | def test_fetch_models_url_contains_search_text(self, widget):
    method test_on_request_response_network_error_does_not_populate_popup (line 66) | def test_on_request_response_network_error_does_not_populate_popup(sel...
    method test_on_request_response_populates_popup (line 72) | def test_on_request_response_populates_popup(self, widget):
    method test_on_request_response_empty_models_does_not_show_popup (line 82) | def test_on_request_response_empty_models_does_not_show_popup(self, wi...
    method test_on_request_response_item_has_user_role_data (line 90) | def test_on_request_response_item_has_user_role_data(self, widget):
    method test_on_select_item_emits_model_selected (line 99) | def test_on_select_item_emits_model_selected(self, widget, qtbot: QtBot):
    method test_on_select_item_hides_popup (line 112) | def test_on_select_item_hides_popup(self, widget):
    method test_on_popup_selected_stops_timer (line 122) | def test_on_popup_selected_stops_timer(self, widget):
    method test_event_filter_ignores_non_popup_target (line 128) | def test_event_filter_ignores_non_popup_target(self, widget):
    method test_event_filter_mouse_press_hides_popup (line 133) | def test_event_filter_mouse_press_hides_popup(self, widget):
    method test_event_filter_escape_hides_popup (line 141) | def test_event_filter_escape_hides_popup(self, widget, qtbot: QtBot):
    method test_event_filter_enter_selects_item (line 148) | def test_event_filter_enter_selects_item(self, widget, qtbot: QtBot):
    method test_event_filter_enter_no_item_returns_true (line 162) | def test_event_filter_enter_no_item_returns_true(self, widget, qtbot: ...
    method test_event_filter_navigation_keys_return_false (line 167) | def test_event_filter_navigation_keys_return_false(self, widget):
    method test_event_filter_other_key_hides_popup (line 173) | def test_event_filter_other_key_hides_popup(self, widget):

FILE: tests/widgets/import_url_dialog_test.py
  class TestImportURLDialog (line 7) | class TestImportURLDialog:
    method test_should_show_error_with_invalid_url (line 8) | def test_should_show_error_with_invalid_url(self, qtbot):
    method test_should_return_url_with_valid_url (line 18) | def test_should_return_url_with_valid_url(self, qtbot):

FILE: tests/widgets/main_window_test.py
  function get_test_asset (line 32) | def get_test_asset(filename: str):
  class TestMainWindow (line 36) | class TestMainWindow:
    method test_should_set_window_title_and_icon (line 37) | def test_should_set_window_title_and_icon(self, qtbot, transcription_s...
    method test_should_run_file_transcription_task (line 44) | def test_should_run_file_transcription_task(
    method _get_tasks_table (line 65) | def _get_tasks_table(window: MainWindow) -> QTableView:
    method test_should_run_url_import_file_transcription_task (line 68) | def test_should_run_url_import_file_transcription_task(
    method test_should_run_and_cancel_transcription_task (line 97) | def test_should_run_and_cancel_transcription_task(
    method test_should_load_tasks_from_cache (line 153) | def test_should_load_tasks_from_cache(
    method test_should_clear_history_with_rows_selected (line 194) | def test_should_clear_history_with_rows_selected(
    method test_should_have_clear_history_action_disabled_with_no_rows_selected (line 213) | def test_should_have_clear_history_action_disabled_with_no_rows_selected(
    method test_should_open_transcription_viewer_when_menu_action_is_clicked (line 225) | def test_should_open_transcription_viewer_when_menu_action_is_clicked(
    method test_should_open_transcription_viewer_when_return_clicked (line 252) | def test_should_open_transcription_viewer_when_return_clicked(
    method test_should_have_open_transcript_action_disabled_with_no_rows_selected (line 286) | def test_should_have_open_transcript_action_disabled_with_no_rows_sele...
    method test_import_folder_opens_file_transcriber_with_supported_files (line 297) | def test_import_folder_opens_file_transcriber_with_supported_files(
    method test_import_folder_does_nothing_when_cancelled (line 326) | def test_import_folder_does_nothing_when_cancelled(
    method test_import_folder_does_nothing_when_no_supported_files (line 340) | def test_import_folder_does_nothing_when_no_supported_files(
    method _import_file_and_start_transcription (line 359) | def _import_file_and_start_transcription(
    method _get_assert_task_status_callback (line 385) | def _get_assert_task_status_callback(
    method _get_status (line 399) | def _get_status(table_widget: QTableView, row_index: int):
    method _get_toolbar_action (line 403) | def _get_toolbar_action(window: MainWindow, text: str):

FILE: tests/widgets/menu_bar_test.py
  class TestMenuBar (line 10) | class TestMenuBar:
    method test_import_folder_action_emits_signal (line 11) | def test_import_folder_action_emits_signal(self, qtbot, shortcuts):
    method test_open_preferences_dialog (line 23) | def test_open_preferences_dialog(self, qtbot, shortcuts):

FILE: tests/widgets/model_download_progress_dialog.py
  class TestModelDownloadProgressDialog (line 8) | class TestModelDownloadProgressDialog:
    method test_should_show_dialog (line 9) | def test_should_show_dialog(self, qtbot):
    method test_should_update_label_on_progress (line 14) | def test_should_update_label_on_progress(self, qtbot):
    method test_should_be_an_application_modal (line 28) | def test_should_be_an_application_modal(self, qtbot):

FILE: tests/widgets/model_type_combo_box_test.py
  class TestModelTypeComboBox (line 8) | class TestModelTypeComboBox:
    method test_should_display_items (line 29) | def test_should_display_items(self, qtbot, model_types):

FILE: tests/widgets/openai_api_key_line_edit_test.py
  class TestOpenAIAPIKeyLineEdit (line 4) | class TestOpenAIAPIKeyLineEdit:
    method test_should_emit_key_changed (line 5) | def test_should_emit_key_changed(self, qtbot):
    method test_should_toggle_visibility (line 12) | def test_should_toggle_visibility(self, qtbot):

FILE: tests/widgets/preferences_dialog/folder_watch_preferences_widget_test.py
  class TestFolderWatchPreferencesWidget (line 18) | class TestFolderWatchPreferencesWidget:
    method test_edit_folder_watch_preferences (line 19) | def test_edit_folder_watch_preferences(self, qtbot):
    method test_delete_processed_files_checkbox (line 64) | def test_delete_processed_files_checkbox(self, qtbot):

FILE: tests/widgets/preferences_dialog/general_preferences_widget_test.py
  class TestGeneralPreferencesWidget (line 11) | class TestGeneralPreferencesWidget:
    method test_should_disable_test_button_if_no_api_key (line 12) | def test_should_disable_test_button_if_no_api_key(self, qtbot, mocker):
    method test_should_test_openai_api_key (line 33) | def test_should_test_openai_api_key(self, qtbot, mocker):
    method test_recording_export_preferences (line 61) | def test_recording_export_preferences(self, qtbot, mocker):
    method test_openai_base_url_preferences (line 91) | def test_openai_base_url_preferences(self, qtbot, mocker):
  class TestTestOpenAIApiKeyJob (line 113) | class TestTestOpenAIApiKeyJob:
    method test_run_success (line 115) | def test_run_success(self, mocker):
    method test_run_authentication_error (line 134) | def test_run_authentication_error(self, mocker):

FILE: tests/widgets/preferences_dialog/models_preferences_widget_test.py
  class TestModelsPreferencesWidget (line 19) | class TestModelsPreferencesWidget:
    method clear_model_cache (line 21) | def clear_model_cache(self):
    method test_should_show_model_list (line 28) | def test_should_show_model_list(self, qtbot):
    method test_should_change_model_type (line 38) | def test_should_change_model_type(self, qtbot):
    method test_should_download_model (line 52) | def test_should_download_model(self, qtbot: QtBot, clear_model_cache):
    method default_model_path (line 92) | def default_model_path(self) -> str:
    method test_should_show_downloaded_model (line 95) | def test_should_show_downloaded_model(self, qtbot, default_model_path):

FILE: tests/widgets/preferences_dialog/preferences_dialog_test.py
  class TestPreferencesDialog (line 12) | class TestPreferencesDialog:
    method test_create (line 17) | def test_create(self, qtbot: QtBot, shortcuts):
    method test_create_localized (line 33) | def test_create_localized(self, qtbot: QtBot, shortcuts, mocker):

FILE: tests/widgets/presentation_window_test.py
  class TestPresentationWindow (line 14) | class TestPresentationWindow:
    method test_should_set_window_title (line 15) | def test_should_set_window_title(self, qtbot: QtBot):
    method test_should_have_window_flag (line 23) | def test_should_have_window_flag(self, qtbot: QtBot):
    method test_should_have_transcript_display (line 31) | def test_should_have_transcript_display(self, qtbot: QtBot):
    method test_should_have_translation_display_hidden (line 40) | def test_should_have_translation_display_hidden(self, qtbot: QtBot):
    method test_should_have_default_size (line 50) | def test_should_have_default_size(self, qtbot: QtBot):
  class TestPresentationWindowUpdateTranscripts (line 60) | class TestPresentationWindowUpdateTranscripts:
    method test_update_transcripts_with_text (line 61) | def test_update_transcripts_with_text(self, qtbot: QtBot):
    method test_update_transcripts_with_empty_text (line 72) | def test_update_transcripts_with_empty_text(self, qtbot: QtBot):
    method test_update_transcripts_escapes_html (line 82) | def test_update_transcripts_escapes_html(self, qtbot: QtBot):
    method test_update_transcripts_preserves_newlines (line 94) | def test_update_transcripts_preserves_newlines(self, qtbot: QtBot):
  class TestPresentationWindowUpdateTranslations (line 106) | class TestPresentationWindowUpdateTranslations:
    method test_update_translations_with_text (line 107) | def test_update_translations_with_text(self, qtbot: QtBot):
    method test_update_translations_with_empty_text (line 120) | def test_update_translations_with_empty_text(self, qtbot: QtBot):
    method test_update_translations_escapes_html (line 133) | def test_update_translations_escapes_html(self, qtbot: QtBot):
  class TestPresentationWindowLoadSettings (line 145) | class TestPresentationWindowLoadSettings:
    method test_load_settings_light_theme (line 146) | def test_load_settings_light_theme(self, qtbot: QtBot):
    method test_load_settings_dark_theme (line 158) | def test_load_settings_dark_theme(self, qtbot: QtBot):
    method test_load_settings_custom_theme (line 169) | def test_load_settings_custom_theme(self, qtbot: QtBot):
    method test_load_settings_refreshes_content (line 183) | def test_load_settings_refreshes_content(self, qtbot: QtBot):
  class TestPresentationWindowApplyStyling (line 200) | class TestPresentationWindowApplyStyling:
    method test_apply_styling_creates_css (line 201) | def test_apply_styling_creates_css(self, qtbot: QtBot):
    method test_apply_styling_with_custom_css_file (line 213) | def test_apply_styling_with_custom_css_file(self, qtbot: QtBot):
  class TestPresentationWindowFullscreen (line 235) | class TestPresentationWindowFullscreen:
    method test_toggle_fullscreen_enters_fullscreen (line 236) | def test_toggle_fullscreen_enters_fullscreen(self, qtbot: QtBot):
    method test_toggle_fullscreen_exits_fullscreen (line 249) | def test_toggle_fullscreen_exits_fullscreen(self, qtbot: QtBot):
  class TestPresentationWindowKeyPressEvent (line 263) | class TestPresentationWindowKeyPressEvent:
    method test_escape_exits_fullscreen (line 264) | def test_escape_exits_fullscreen(self, qtbot: QtBot):
    method test_escape_does_not_affect_normal_mode (line 284) | def test_escape_does_not_affect_normal_mode(self, qtbot: QtBot):
  class TestPresentationWindowGetCssFilePath (line 303) | class TestPresentationWindowGetCssFilePath:
    method test_get_css_file_path_returns_path (line 304) | def test_get_css_file_path_returns_path(self, qtbot: QtBot):
    method test_get_css_file_path_creates_directory (line 315) | def test_get_css_file_path_creates_directory(self, qtbot: QtBot):

FILE: tests/widgets/recording_transcriber_widget_test.py
  class TestRecordingTranscriberWidget (line 22) | class TestRecordingTranscriberWidget:
    method test_should_set_window_title (line 23) | def test_should_set_window_title(self, qtbot: QtBot):
    method test_should_transcribe (line 42) | def test_should_transcribe(self, qtbot):
    method test_should_transcribe_and_export (line 70) | def test_should_transcribe_and_export(self, qtbot):
    method test_on_next_transcription_append_above (line 119) | def test_on_next_transcription_append_above(self, qtbot: QtBot):
    method test_find_common_part_exact_match (line 141) | def test_find_common_part_exact_match(self):
    method test_find_common_part_partial_match (line 144) | def test_find_common_part_partial_match(self):
    method test_find_common_part_no_match (line 158) | def test_find_common_part_no_match(self):
    method test_find_common_part_different_start (line 161) | def test_find_common_part_different_start(self):
    method test_find_common_part_empty_strings (line 164) | def test_find_common_part_empty_strings(self):
    method test_on_next_transcription_append_and_correct (line 170) | def test_on_next_transcription_append_and_correct(self, qtbot: QtBot):
  class TestRecordingTranscriberWidgetLineSeparator (line 197) | class TestRecordingTranscriberWidgetLineSeparator:
    method test_line_separator_loaded_from_settings (line 199) | def test_line_separator_loaded_from_settings(self, qtbot: QtBot):
    method test_line_separator_saved_on_close (line 207) | def test_line_separator_saved_on_close(self, qtbot: QtBot):
    method test_line_separator_used_in_append_below (line 216) | def test_line_separator_used_in_append_below(self, qtbot: QtBot):
    method test_line_separator_used_in_append_above (line 225) | def test_line_separator_used_in_append_above(self, qtbot: QtBot):
    method test_line_separator_used_in_translation_append_below (line 234) | def test_line_separator_used_in_translation_append_below(self, qtbot: ...
  class TestRecordingTranscriberWidgetSilenceThreshold (line 243) | class TestRecordingTranscriberWidgetSilenceThreshold:
    method test_silence_threshold_loaded_from_settings (line 245) | def test_silence_threshold_loaded_from_settings(self, qtbot: QtBot):
    method test_silence_threshold_saved_on_close (line 263) | def test_silence_threshold_saved_on_close(self, qtbot: QtBot):
  class TestRecordingTranscriberWidgetPresentation (line 282) | class TestRecordingTranscriberWidgetPresentation:
    method test_presentation_options_bar_created (line 286) | def test_presentation_options_bar_created(self, qtbot: QtBot):
    method test_presentation_options_bar_has_buttons (line 304) | def test_presentation_options_bar_has_buttons(self, qtbot: QtBot):
    method test_presentation_window_initially_none (line 326) | def test_presentation_window_initially_none(self, qtbot: QtBot):
    method test_on_show_presentation_clicked_creates_window (line 343) | def test_on_show_presentation_clicked_creates_window(self, qtbot: QtBot):
    method test_on_show_presentation_clicked_syncs_content (line 365) | def test_on_show_presentation_clicked_syncs_content(self, qtbot: QtBot):
    method test_on_show_presentation_clicked_brings_existing_to_front (line 387) | def test_on_show_presentation_clicked_brings_existing_to_front(self, q...
    method test_on_text_size_changed (line 412) | def test_on_text_size_changed(self, qtbot: QtBot):
    method test_on_theme_changed_light (line 437) | def test_on_theme_changed_light(self, qtbot: QtBot):
    method test_on_theme_changed_dark (line 462) | def test_on_theme_changed_dark(self, qtbot: QtBot):
    method test_on_theme_changed_custom (line 487) | def test_on_theme_changed_custom(self, qtbot: QtBot):
    method test_on_text_color_clicked (line 512) | def test_on_text_color_clicked(self, qtbot: QtBot):
    method test_on_text_color_clicked_cancel (line 536) | def test_on_text_color_clicked_cancel(self, qtbot: QtBot):
    method test_on_copy_transcript_clicked_with_text (line 561) | def test_on_copy_transcript_clicked_with_text(self, qtbot: QtBot):
    method test_on_copy_transcript_clicked_without_text (line 591) | def test_on_copy_transcript_clicked_without_text(self, qtbot: QtBot):
    method test_copy_actions_bar_hidden_when_recording_starts (line 615) | def test_copy_actions_bar_hidden_when_recording_starts(self, qtbot: Qt...
    method test_on_bg_color_clicked (line 644) | def test_on_bg_color_clicked(self, qtbot: QtBot):
    method test_on_fullscreen_clicked (line 668) | def test_on_fullscreen_clicked(self, qtbot: QtBot):
    method test_on_fullscreen_clicked_without_window (line 692) | def test_on_fullscreen_clicked_without_window(self, qtbot: QtBot):
    method test_presentation_window_updates_on_transcription (line 710) | def test_presentation_window_updates_on_transcription(self, qtbot: QtB...
    method test_close_event_closes_presentation_window (line 732) | def test_close_event_closes_presentation_window(self, qtbot: QtBot):
    method test_fullscreen_button_disabled_initially (line 753) | def test_fullscreen_button_disabled_initially(self, qtbot: QtBot):
    method test_presentation_bar_shown_when_recording (line 770) | def test_presentation_bar_shown_when_recording(self, qtbot: QtBot):
  function _widget_ctx (line 802) | def _widget_ctx(qtbot):
  class TestResetTranscriberControls (line 814) | class TestResetTranscriberControls:
    method test_record_button_disabled_for_faster_whisper_custom_without_hf_model (line 816) | def test_record_button_disabled_for_faster_whisper_custom_without_hf_m...
    method test_record_button_disabled_for_hugging_face_without_model_id (line 832) | def test_record_button_disabled_for_hugging_face_without_model_id(self...
    method test_record_button_enabled_for_hugging_face_with_model_id (line 847) | def test_record_button_enabled_for_hugging_face_with_model_id(self, qt...
  class TestOnTranscriptionOptionsChanged (line 863) | class TestOnTranscriptionOptionsChanged:
    method test_shows_translation_box_when_llm_enabled (line 865) | def test_shows_translation_box_when_llm_enabled(self, qtbot):
    method test_hides_translation_box_when_llm_disabled (line 874) | def test_hides_translation_box_when_llm_disabled(self, qtbot):
    method test_updates_transcription_options (line 884) | def test_updates_transcription_options(self, qtbot):
  function _model_loaded_ctx (line 894) | def _model_loaded_ctx(qtbot, enable_llm_translation=False):
  class TestTranslatorSetup (line 913) | class TestTranslatorSetup:
    method test_translator_created_when_llm_enabled (line 915) | def test_translator_created_when_llm_enabled(self, qtbot):
    method test_translator_not_created_when_llm_disabled (line 924) | def test_translator_not_created_when_llm_disabled(self, qtbot):
    method test_translator_translation_signal_connected_to_on_next_translation (line 933) | def test_translator_translation_signal_connected_to_on_next_translatio...
  class TestOnDeviceChanged (line 944) | class TestOnDeviceChanged:
    method test_no_new_listener_started_when_device_is_none (line 946) | def test_no_new_listener_started_when_device_is_none(self, qtbot):
    method test_no_new_listener_started_when_device_is_minus_one (line 953) | def test_no_new_listener_started_when_device_is_minus_one(self, qtbot):
    method test_device_id_updated (line 960) | def test_device_id_updated(self, qtbot):
  class TestOnRecordButtonClickedStop (line 967) | class TestOnRecordButtonClickedStop:
    method test_stop_path_sets_status_stopped (line 969) | def test_stop_path_sets_status_stopped(self, qtbot):
    method test_stop_path_hides_presentation_bar (line 978) | def test_stop_path_hides_presentation_bar(self, qtbot):
  class TestOnModelLoaded (line 988) | class TestOnModelLoaded:
    method test_empty_model_path_calls_transcriber_error (line 990) | def test_empty_model_path_calls_transcriber_error(self, qtbot):
  class TestOnTranscriberError (line 1005) | class TestOnTranscriberError:
    method test_shows_message_box (line 1007) | def test_shows_message_box(self, qtbot):
    method test_resets_record_button (line 1017) | def test_resets_record_button(self, qtbot):
  class TestOnCancelModelProgressDialog (line 1027) | class TestOnCancelModelProgressDialog:
    method test_cancels_model_loader (line 1029) | def test_cancels_model_loader(self, qtbot):
    method test_record_button_re_enabled (line 1040) | def test_record_button_re_enabled(self, qtbot):
  class TestOnNextTranscriptionExport (line 1052) | class TestOnNextTranscriptionExport:
    method test_append_below_writes_to_export_file (line 1054) | def test_append_below_writes_to_export_file(self, qtbot):
    method test_append_above_writes_to_export_file (line 1072) | def test_append_above_writes_to_export_file(self, qtbot):
    method test_append_above_csv_prepends_new_column (line 1095) | def test_append_above_csv_prepends_new_column(self, qtbot):
    method test_append_above_csv_respects_max_entries (line 1119) | def test_append_above_csv_respects_max_entries(self, qtbot):
  class TestUploadToServer (line 1147) | class TestUploadToServer:
    method test_transcript_uploaded_when_upload_url_set (line 1149) | def test_transcript_uploaded_when_upload_url_set(self, qtbot):
    method test_transcript_not_uploaded_when_upload_url_empty (line 1162) | def test_transcript_not_uploaded_when_upload_url_empty(self, qtbot):
    method test_transcript_upload_failure_does_not_raise (line 1170) | def test_transcript_upload_failure_does_not_raise(self, qtbot):
    method test_translation_uploaded_when_upload_url_set (line 1178) | def test_translation_uploaded_when_upload_url_set(self, qtbot):
  class TestOnNextTranslation (line 1191) | class TestOnNextTranslation:
    method test_append_below_adds_translation (line 1193) | def test_append_below_adds_translation(self, qtbot):
    method test_append_above_puts_new_text_first (line 1200) | def test_append_above_puts_new_text_first(self, qtbot):
    method test_append_and_correct_merges_translation (line 1209) | def test_append_and_correct_merges_translation(self, qtbot):
    method test_empty_translation_ignored (line 1220) | def test_empty_translation_ignored(self, qtbot):
    method test_updates_presentation_window (line 1227) | def test_updates_presentation_window(self, qtbot):
  class TestExportFileHelpers (line 1237) | class TestExportFileHelpers:
    method test_write_creates_file (line 1238) | def test_write_creates_file(self, tmp_path):
    method test_write_appends_by_default (line 1244) | def test_write_appends_by_default(self, tmp_path):
    method test_write_overwrites_with_mode_w (line 1251) | def test_write_overwrites_with_mode_w(self, tmp_path):
    method test_write_retries_on_permission_error (line 1258) | def test_write_retries_on_permission_error(self, tmp_path):
    method test_write_gives_up_after_max_retries (line 1276) | def test_write_gives_up_after_max_retries(self, tmp_path):
    method test_write_handles_oserror (line 1282) | def test_write_handles_oserror(self, tmp_path):
    method test_read_returns_file_contents (line 1287) | def test_read_returns_file_contents(self, tmp_path):
    method test_read_retries_on_permission_error (line 1293) | def test_read_retries_on_permission_error(self, tmp_path):
    method test_read_returns_empty_string_on_oserror (line 1313) | def test_read_returns_empty_string_on_oserror(self, tmp_path):
    method test_read_returns_empty_string_after_max_retries (line 1318) | def test_read_returns_empty_string_after_max_retries(self, tmp_path):
  class TestWriteCsvExport (line 1326) | class TestWriteCsvExport:
    method test_creates_csv_with_single_entry (line 1327) | def test_creates_csv_with_single_entry(self, tmp_path):
    method test_appends_column_to_existing_csv (line 1334) | def test_appends_column_to_existing_csv(self, tmp_path):
    method test_max_entries_limits_columns (line 1343) | def test_max_entries_limits_columns(self, tmp_path):
    method test_max_entries_zero_means_no_limit (line 1352) | def test_max_entries_zero_means_no_limit(self, tmp_path):
    method test_handles_empty_existing_file (line 1361) | def test_handles_empty_existing_file(self, tmp_path):
    method test_retries_on_permission_error (line 1371) | def test_retries_on_permission_error(self, tmp_path):
  class TestWriteTxtExport (line 1390) | class TestWriteTxtExport:
    method test_append_mode_adds_text (line 1391) | def test_append_mode_adds_text(self, tmp_path):
    method test_append_mode_max_entries_trims_oldest (line 1399) | def test_append_mode_max_entries_trims_oldest(self, tmp_path):
    method test_prepend_mode_puts_text_first (line 1408) | def test_prepend_mode_puts_text_first(self, tmp_path):
    method test_prepend_mode_max_entries_trims_oldest (line 1418) | def test_prepend_mode_max_entries_trims_oldest(self, tmp_path):
    method test_write_mode_overwrites (line 1428) | def test_write_mode_overwrites(self, tmp_path):
    method test_prepend_on_nonexistent_file (line 1436) | def test_prepend_on_nonexistent_file(self, tmp_path):
    method test_append_max_entries_zero_means_no_limit (line 1443) | def test_append_max_entries_zero_means_no_limit(self, tmp_path):
  class TestPresentationTranslationSync (line 1452) | class TestPresentationTranslationSync:
    method test_syncs_translation_when_llm_enabled (line 1454) | def test_syncs_translation_when_llm_enabled(self, qtbot):

FILE: tests/widgets/shortcuts_editor_widget_test.py
  class TestShortcutsEditorWidget (line 13) | class TestShortcutsEditorWidget:
    method test_should_update_shortcuts (line 14) | def test_should_update_shortcuts(self, qtbot: QtBot, shortcuts):
    method test_should_reset_to_defaults (line 23) | def test_should_reset_to_defaults(self, qtbot, shortcuts):
  class TestSequenceEdit (line 64) | class TestSequenceEdit:
    method test_should_ignore_modifier_only_keys (line 65) | def test_should_ignore_modifier_only_keys(self, qtbot: QtBot):
    method test_should_record_key_combination (line 82) | def test_should_record_key_combination(self, qtbot: QtBot):
    method test_should_propagate_escape_to_parent (line 95) | def test_should_propagate_escape_to_parent(self, qtbot: QtBot):

FILE: tests/widgets/speaker_identification_widget_test.py
  class TestSpeakerIdentificationWidget (line 25) | class TestSpeakerIdentificationWidget:
    method transcription (line 27) | def transcription(
    method test_widget_initialization (line 48) | def test_widget_initialization(self, qtbot: QtBot, transcription, tran...
    method test_identification_worker_run (line 70) | def test_identification_worker_run(self, qtbot: QtBot, transcription, ...
    method test_identify_button_toggles_visibility (line 93) | def test_identify_button_toggles_visibility(self, qtbot: QtBot, transc...
    method test_cancel_button_resets_ui (line 122) | def test_cancel_button_resets_ui(self, qtbot: QtBot, transcription, tr...
    method test_on_progress_update_sets_label_and_bar (line 143) | def test_on_progress_update_sets_label_and_bar(self, qtbot: QtBot, tra...
    method test_on_progress_update_step_8_enables_save (line 157) | def test_on_progress_update_step_8_enables_save(self, qtbot: QtBot, tr...
    method test_on_identification_finished_empty_result (line 174) | def test_on_identification_finished_empty_result(self, qtbot: QtBot, t...
    method test_on_identification_finished_populates_speakers (line 191) | def test_on_identification_finished_populates_speakers(self, qtbot: Qt...
    method test_on_identification_error_resets_buttons (line 210) | def test_on_identification_error_resets_buttons(self, qtbot: QtBot, tr...
    method test_on_save_no_merge (line 228) | def test_on_save_no_merge(self, qtbot: QtBot, transcription, transcrip...
    method test_on_save_with_merge (line 255) | def test_on_save_with_merge(self, qtbot: QtBot, transcription, transcr...
    method test_on_save_emits_transcriptions_updated (line 283) | def test_on_save_emits_transcriptions_updated(self, qtbot: QtBot, tran...
    method test_batch_processing_with_many_words (line 304) | def test_batch_processing_with_many_words(self):
    method test_batch_processing_with_assertion_error_fallback (line 339) | def test_batch_processing_with_assertion_error_fallback(self):

FILE: tests/widgets/transcription_task_folder_watcher_test.py
  class TestTranscriptionTaskFolderWatcher (line 26) | class TestTranscriptionTaskFolderWatcher:
    method default_model (line 27) | def default_model(self):
    method test_should_add_task_not_in_tasks (line 33) | def test_should_add_task_not_in_tasks(self, qtbot: QtBot):
    method test_should_not_add_task_in_tasks (line 67) | def test_should_not_add_task_in_tasks(self, qtbot):
    method test_should_find_tasks_in_subfolders (line 119) | def test_should_find_tasks_in_subfolders(self, qtbot: QtBot):
    method test_should_ignore_hidden_directories (line 156) | def test_should_ignore_hidden_directories(self, qtbot: QtBot):
    method test_should_ignore_non_media_files (line 197) | def test_should_ignore_non_media_files(self, qtbot: QtBot):
    method test_should_ignore_temp_conversion_files (line 237) | def test_should_ignore_temp_conversion_files(self, qtbot: QtBot):
    method test_should_ignore_extracted_speech_files (line 277) | def test_should_ignore_extracted_speech_files(self, qtbot: QtBot):
    method test_should_set_delete_source_file_when_preference_enabled (line 317) | def test_should_set_delete_source_file_when_preference_enabled(self, q...
    method test_should_not_set_delete_source_file_when_preference_disabled (line 351) | def test_should_not_set_delete_source_file_when_preference_disabled(se...
    method test_should_set_original_file_path (line 385) | def test_should_set_original_file_path(self, qtbot: QtBot):

FILE: tests/widgets/transcription_tasks_table_widget_test.py
  class MockFileTranscriptionTaskStatus (line 25) | class MockFileTranscriptionTaskStatus(enum.Enum):
  class MockFileTranscriptionTask (line 33) | class MockFileTranscriptionTask:
  function mock_dependencies (line 38) | def mock_dependencies(monkeypatch):
  function db (line 86) | def db():
  function mock_record (line 127) | def mock_record(values):
  function test_format_timedelta (line 136) | def test_format_timedelta(delta, expected):
  function test_format_record_status_text_logic (line 140) | def test_format_record_status_text_logic():
  function test_column_delegates_text_getters (line 159) | def test_column_delegates_text_getters(monkeypatch):
  function widget (line 195) | def widget(qtbot, db):
  class TestTranscriptionTasksTableWidget (line 203) | class TestTranscriptionTasksTableWidget:
    method test_init_and_save_column_visibility (line 204) | def test_init_and_save_column_visibility(self, widget):
    method test_copy_selected_fields (line 213) | def test_copy_selected_fields(self, widget):
    method test_key_press_event (line 224) | def test_key_press_event(self, widget):
    method test_delete_transcriptions (line 230) | def test_delete_transcriptions(self, widget):
    method test_selected_transcriptions (line 235) | def test_selected_transcriptions(self, widget):
    method test_refresh_row (line 242) | def test_refresh_row(self, widget, db):
    method test_context_menus (line 251) | def test_context_menus(self, widget, monkeypatch):
    method test_new_column_definitions (line 264) | def test_new_column_definitions(self):
    method test_file_column_text_getter_with_name (line 282) | def test_file_column_text_getter_with_name(self, widget):
    method test_notes_column_text_getter (line 300) | def test_notes_column_text_getter(self, widget):
    method test_column_visibility_management (line 314) | def test_column_visibility_management(self, widget):
    method test_column_width_management (line 324) | def test_column_width_management(self, widget):
    method test_column_order_management (line 335) | def test_column_order_management(self, widget):
    method test_context_menu_rename_action (line 350) | def test_context_menu_rename_action(self, widget, monkeypatch):
    method test_context_menu_notes_action (line 379) | def test_context_menu_notes_action(self, widget, monkeypatch):
    method test_context_menu_restart_action_success (line 406) | def test_context_menu_restart_action_success(self, widget, monkeypatch):
    method test_context_menu_restart_action_wrong_status (line 437) | def test_context_menu_restart_action_wrong_status(self, widget, monkey...
    method test_restart_action_no_selection_does_nothing (line 459) | def test_restart_action_no_selection_does_nothing(self, widget, monkey...
    method test_restart_action_canceled_status_succeeds (line 466) | def test_restart_action_canceled_status_succeeds(self, widget, monkeyp...
    method test_restart_action_service_exception_shows_warning (line 482) | def test_restart_action_service_exception_shows_warning(self, widget, ...
    method _make_transcription (line 501) | def _make_transcription(self, **kwargs):
    method test_restart_task_happy_path_adds_task (line 525) | def test_restart_task_happy_path_adds_task(self, widget, monkeypatch):
    method test_restart_task_invalid_whisper_model_size_defaults_to_tiny (line 552) | def test_restart_task_invalid_whisper_model_size_defaults_to_tiny(self...
    method test_restart_task_model_not_available_downloads_it (line 584) | def test_restart_task_model_not_available_downloads_it(self, widget, m...
    method test_restart_task_model_unavailable_after_download_shows_warning (line 616) | def test_restart_task_model_unavailable_after_download_shows_warning(s...
    method test_restart_task_no_worker_found_shows_warning (line 637) | def test_restart_task_no_worker_found_shows_warning(self, widget, monk...
    method test_restart_task_export_formats_parsed_correctly (line 663) | def test_restart_task_export_formats_parsed_correctly(self, widget, mo...
    method test_restart_task_invalid_export_format_skipped (line 698) | def test_restart_task_invalid_export_format_skipped(self, widget, monk...
    method test_column_resize_event (line 732) | def test_column_resize_event(self, widget):
    method test_column_move_event (line 740) | def test_column_move_event(self, widget):
    method test_reload_column_order_from_settings (line 750) | def test_reload_column_order_from_settings(self, widget):
    method test_sort_indicator_change_event (line 765) | def test_sort_indicator_change_event(self, widget):
    method test_save_sort_state (line 772) | def test_save_sort_state(self, widget):
    method test_load_sort_state (line 782) | def test_load_sort_state(self, widget):
    method test_reset_column_order_resets_sort (line 796) | def test_reset_column_order_resets_sort(self, widget):
  class TestTranscriptionTasksTableHeaderView (line 810) | class TestTranscriptionTasksTableHeaderView:
    method header (line 812) | def header(self, qtbot, widget):
    method _setup_settings_key (line 815) | def _setup_settings_key(self, widget):
    method test_hiding_column_saves_visibility_and_widths (line 831) | def test_hiding_column_saves_visibility_and_widths(self, header, widget):
    method test_showing_column_makes_it_visible (line 847) | def test_showing_column_makes_it_visible(self, header, widget):
    method test_hiding_already_hidden_column_does_not_save_zero_width (line 859) | def test_hiding_already_hidden_column_does_not_save_zero_width(self, h...
    method test_column_order_saved_on_visibility_change (line 881) | def test_column_order_saved_on_visibility_change(self, header, widget):
    method test_reload_called_after_visibility_change (line 893) | def test_reload_called_after_visibility_change(self, header, widget):

FILE: tests/widgets/transcription_viewer/transcription_segments_editor_widget_test.py
  class TestParseTimestamp (line 27) | class TestParseTimestamp:
    method test_parse_timestamp_full_format (line 30) | def test_parse_timestamp_full_format(self):
    method test_parse_timestamp_minute_format (line 36) | def test_parse_timestamp_minute_format(self):
    method test_parse_timestamp_second_format (line 42) | def test_parse_timestamp_second_format(self):
    method test_parse_timestamp_no_milliseconds (line 48) | def test_parse_timestamp_no_milliseconds(self):
    method test_parse_timestamp_with_whitespace (line 54) | def test_parse_timestamp_with_whitespace(self):
    method test_parse_timestamp_invalid_format (line 60) | def test_parse_timestamp_invalid_format(self):
    method test_parse_timestamp_empty_string (line 66) | def test_parse_timestamp_empty_string(self):
  class TestTimeStampLineEdit (line 71) | class TestTimeStampLineEdit:
    method test_timestamp_line_edit_initialization (line 74) | def test_timestamp_line_edit_initialization(self, qtbot: QtBot):
    method test_set_milliseconds (line 82) | def test_set_milliseconds(self, qtbot: QtBot):
    method test_get_milliseconds (line 92) | def test_get_milliseconds(self, qtbot: QtBot):
    method test_plus_key_increases_time (line 100) | def test_plus_key_increases_time(self, qtbot: QtBot):
    method test_minus_key_decreases_time (line 110) | def test_minus_key_decreases_time(self, qtbot: QtBot):
    method test_minus_key_does_not_go_negative (line 120) | def test_minus_key_does_not_go_negative(self, qtbot: QtBot):
    method test_focus_out_reformats_timestamp (line 130) | def test_focus_out_reformats_timestamp(self, qtbot: QtBot):
    method test_validator_rejects_invalid_characters (line 144) | def test_validator_rejects_invalid_characters(self, qtbot: QtBot):
  class TestTimeStampDelegate (line 156) | class TestTimeStampDelegate:
    method test_display_text_formatting (line 159) | def test_display_text_formatting(self):
  class TestTimeStampEditorDelegate (line 172) | class TestTimeStampEditorDelegate:
    method test_delegate_initialization (line 175) | def test_delegate_initialization(self):
    method test_create_editor (line 180) | def test_create_editor(self, qtbot: QtBot):
  class TestWordWrapDelegate (line 196) | class TestWordWrapDelegate:
    method test_create_editor (line 199) | def test_create_editor(self, qtbot: QtBot):
  class TestCustomTextEdit (line 215) | class TestCustomTextEdit:
    method test_initialization (line 218) | def test_initialization(self, qtbot: QtBot):
    method test_tab_key_closes_editor (line 224) | def test_tab_key_closes_editor(self, qtbot: QtBot):
    method test_enter_key_closes_editor (line 237) | def test_enter_key_closes_editor(self, qtbot: QtBot):
    method test_escape_key_closes_editor (line 248) | def test_escape_key_closes_editor(self, qtbot: QtBot):
  class TestTranscriptionSegmentModel (line 260) | class TestTranscriptionSegmentModel:
    method transcription_id (line 264) | def transcription_id(self) -> UUID:
    method test_model_initialization (line 268) | def test_model_initialization(self, transcription_id):
  class TestTranscriptionSegmentsEditorWidget (line 276) | class TestTranscriptionSegmentsEditorWidget:
    method transcription (line 280) | def transcription(
    method translator (line 308) | def translator(self, qtbot: QtBot):
    method test_widget_initialization (line 314) | def test_widget_initialization(self, qtbot: QtBot, transcription, tran...
    method test_column_definitions (line 327) | def test_column_definitions(self, qtbot: QtBot, transcription, transla...
    method test_segments_method (line 345) | def test_segments_method(self, qtbot: QtBot, transcription, translator):
    method test_segment_method (line 358) | def test_segment_method(self, qtbot: QtBot, transcription, translator):
    method test_highlight_and_scroll_to_row (line 371) | def test_highlight_and_scroll_to_row(self, qtbot: QtBot, transcription...
    method test_has_non_empty_translation (line 387) | def test_has_non_empty_translation(self, qtbot: QtBot, transcription, ...
    method test_init_row_height (line 404) | def test_init_row_height(self, qtbot: QtBot, transcription, translator):
    method test_update_translation (line 420) | def test_update_translation(self, qtbot: QtBot, transcription, transla...
    method test_segment_selected_signal (line 448) | def test_segment_selected_signal(self, qtbot: QtBot, transcription, tr...
    method test_timestamp_being_edited_signal (line 468) | def test_timestamp_being_edited_signal(self, qtbot: QtBot, transcripti...
    method test_enter_key_triggers_editing (line 479) | def test_enter_key_triggers_editing(self, qtbot: QtBot, transcription,...
    method test_column_widths (line 496) | def test_column_widths(self, qtbot: QtBot, transcription, translator):
    method test_alternating_row_colors (line 509) | def test_alternating_row_colors(self, qtbot: QtBot, transcription, tra...
    method test_vertical_header_hidden (line 520) | def test_vertical_header_hidden(self, qtbot: QtBot, transcription, tra...
    method test_selection_behavior (line 531) | def test_selection_behavior(self, qtbot: QtBot, transcription, transla...
    method test_selection_mode (line 543) | def test_selection_mode(self, qtbot: QtBot, transcription, translator):

FILE: tests/widgets/transcription_viewer/transcription_viewer_widget_additional_test.py
  class TestTranscriptionViewerWidgetAdditional (line 21) | class TestTranscriptionViewerWidgetAdditional:
    method transcription (line 25) | def transcription(self, transcription_dao, transcription_segment_dao) ...
    method test_toggle_audio_playback (line 50) | def test_toggle_audio_playback(self, qtbot: QtBot, transcription, tran...
    method test_replay_current_segment (line 66) | def test_replay_current_segment(self, qtbot: QtBot, transcription, tra...
    method test_decrease_segment_start (line 85) | def test_decrease_segment_start(self, qtbot: QtBot, transcription, tra...
    method test_increase_segment_start (line 106) | def test_increase_segment_start(self, qtbot: QtBot, transcription, tra...
    method test_decrease_segment_end (line 125) | def test_decrease_segment_end(self, qtbot: QtBot, transcription, trans...
    method test_increase_segment_end (line 144) | def test_increase_segment_end(self, qtbot: QtBot, transcription, trans...
    method test_adjust_segment_timestamp_start (line 163) | def test_adjust_segment_timestamp_start(self, qtbot: QtBot, transcript...
    method test_adjust_segment_timestamp_end (line 181) | def test_adjust_segment_timestamp_end(self, qtbot: QtBot, transcriptio...
    method test_adjust_segment_timestamp_overlap_prevention (line 199) | def test_adjust_segment_timestamp_overlap_prevention(self, qtbot: QtBo...
    method test_on_audio_playback_state_changed (line 217) | def test_on_audio_playback_state_changed(self, qtbot: QtBot, transcrip...
    method test_initialize_speed_control (line 235) | def test_initialize_speed_control(self, qtbot: QtBot, transcription, t...
    method test_on_speed_changed_valid (line 250) | def test_on_speed_changed_valid(self, qtbot: QtBot, transcription, tra...
    method test_on_speed_changed_invalid (line 264) | def test_on_speed_changed_invalid(self, qtbot: QtBot, transcription, t...
    method test_on_speed_changed_clamping (line 276) | def test_on_speed_changed_clamping(self, qtbot: QtBot, transcription, ...
    method test_increase_speed (line 293) | def test_increase_speed(self, qtbot: QtBot, transcription, transcripti...
    method test_decrease_speed (line 310) | def test_decrease_speed(self, qtbot: QtBot, transcription, transcripti...
    method test_get_current_speed (line 327) | def test_get_current_speed(self, qtbot: QtBot, transcription, transcri...
    method test_set_speed (line 340) | def test_set_speed(self, qtbot: QtBot, transcription, transcription_se...
    method test_perform_search (line 352) | def test_perform_search(self, qtbot: QtBot, transcription, transcripti...
    method test_search_in_table (line 367) | def test_search_in_table(self, qtbot: QtBot, transcription, transcript...
    method test_search_in_text (line 382) | def test_search_in_text(self, qtbot: QtBot, transcription, transcripti...
    method test_update_search_ui_with_results (line 400) | def test_update_search_ui_with_results(self, qtbot: QtBot, transcripti...
    method test_update_search_ui_no_results (line 417) | def test_update_search_ui_no_results(self, qtbot: QtBot, transcription...
    method test_highlight_current_match_table (line 434) | def test_highlight_current_match_table(self, qtbot: QtBot, transcripti...
    method test_highlight_table_match (line 450) | def test_highlight_table_match(self, qtbot: QtBot, transcription, tran...
    method test_highlight_text_match (line 464) | def test_highlight_text_match(self, qtbot: QtBot, transcription, trans...
    method test_update_search_results_label (line 482) | def test_update_search_results_label(self, qtbot: QtBot, transcription...
    method test_show_search_bar (line 500) | def test_show_search_bar(self, qtbot: QtBot, transcription, transcript...
    method test_toggle_search_bar_visibility (line 514) | def test_toggle_search_bar_visibility(self, qtbot: QtBot, transcriptio...
    method test_event_filter (line 529) | def test_event_filter(self, qtbot: QtBot, transcription, transcription...
    method test_reset_view_timestamps (line 552) | def test_reset_view_timestamps(self, qtbot: QtBot, transcription, tran...
    method test_reset_view_text (line 569) | def test_reset_view_text(self, qtbot: QtBot, transcription, transcript...
    method test_reset_view_translation (line 586) | def test_reset_view_translation(self, qtbot: QtBot, transcription, tra...
    method test_on_timestamp_being_edited (line 603) | def test_on_timestamp_being_edited(self, qtbot: QtBot, transcription, ...
    method test_on_scroll_to_current_button_clicked_with_segments (line 624) | def test_on_scroll_to_current_button_clicked_with_segments(self, qtbot...
    method test_auto_scroll_to_current_position (line 640) | def test_auto_scroll_to_current_position(self, qtbot: QtBot, transcrip...
    method test_resize_event (line 656) | def test_resize_event(self, qtbot: QtBot, transcription, transcription...
    method test_close_event (line 674) | def test_close_event(self, qtbot: QtBot, transcription, transcription_...
    method test_save_geometry (line 689) | def test_save_geometry(self, qtbot: QtBot, transcription, transcriptio...
    method test_load_geometry (line 703) | def test_load_geometry(self, qtbot: QtBot, transcription, transcriptio...
    method test_load_preferences (line 717) | def test_load_preferences(self, qtbot: QtBot, transcription, transcrip...
    method test_open_advanced_settings (line 731) | def test_open_advanced_settings(self, qtbot: QtBot, transcription, tra...
    method test_on_transcription_options_changed (line 745) | def test_on_transcription_options_changed(self, qtbot: QtBot, transcri...
    method test_on_translate_button_clicked_no_api_key (line 760) | def test_on_translate_button_clicked_no_api_key(self, qtbot: QtBot, tr...
    method test_run_translation (line 782) | def test_run_translation(self, mock_openai, qtbot: QtBot, transcriptio...
    method test_restore_ui_state (line 804) | def test_restore_ui_state(self, qtbot: QtBot, transcription, transcrip...
    method test_create_search_bar (line 818) | def test_create_search_bar(self, qtbot: QtBot, transcription, transcri...
    method test_create_loop_controls (line 834) | def test_create_loop_controls(self, qtbot: QtBot, transcription, trans...
    method test_on_follow_audio_toggle_changed_enabled (line 854) | def test_on_follow_audio_toggle_changed_enabled(self, qtbot: QtBot, tr...
    method test_on_follow_audio_toggle_changed_disabled (line 870) | def test_on_follow_audio_toggle_changed_disabled(self, qtbot: QtBot, t...
    method test_on_transcript_segment_clicked (line 884) | def test_on_transcript_segment_clicked(self, qtbot: QtBot, transcripti...
    method test_hide_loop_controls (line 906) | def test_hide_loop_controls(self, qtbot: QtBot, transcription, transcr...
    method test_toggle_playback_controls_visibility_hide (line 923) | def test_toggle_playback_controls_visibility_hide(self, qtbot: QtBot, ...
    method test_toggle_audio_playback_when_playing (line 939) | def test_toggle_audio_playback_when_playing(self, qtbot: QtBot, transc...
    method test_video_player_initialization (line 959) | def test_video_player_initialization(self, qtbot: QtBot, transcription...

FILE: tests/widgets/transcription_viewer_test.py
  class TestTranscriptionViewerWidget (line 32) | class TestTranscriptionViewerWidget:
    method transcription (line 34) | def transcription(
    method test_should_display_segments (line 55) | def test_should_display_segments(
    method test_should_update_segment_text (line 73) | def test_should_update_segment_text(
    method test_should_resize_segment_text (line 87) | def test_should_resize_segment_text(self, qtbot, transcription, transc...
    method test_should_extend_segment_endings (line 101) | def test_should_extend_segment_endings(self, qtbot, transcription, tra...
    method test_extend_with_invalid_input_uses_default (line 143) | def test_extend_with_invalid_input_uses_default(self, qtbot, transcrip...
    method test_on_merge_button_clicked (line 166) | def test_on_merge_button_clicked(self, qtbot: QtBot, transcription, tr...
    method test_text_button_changes_view_mode (line 225) | def test_text_button_changes_view_mode(
    method test_transcription_worker_calls_stable_whisper (line 246) | def test_transcription_worker_calls_stable_whisper(self, qtbot: QtBot,...
    method test_loop_toggle_functionality (line 296) | def test_loop_toggle_functionality(
    method test_follow_audio_toggle_functionality (line 329) | def test_follow_audio_toggle_functionality(
    method test_scroll_to_current_button_functionality (line 360) | def test_scroll_to_current_button_functionality(
    method test_search_bar_creation_and_visibility (line 379) | def test_search_bar_creation_and_visibility(
    method test_search_functionality_basic (line 409) | def test_search_functionality_basic(
    method test_search_navigation_buttons (line 437) | def test_search_navigation_buttons(
    method test_search_keyboard_shortcuts (line 457) | def test_search_keyboard_shortcuts(
    method test_search_in_different_view_modes (line 480) | def test_search_in_different_view_modes(
    method test_search_performance_limits (line 504) | def test_search_performance_limits(
    method test_search_clear_functionality (line 525) | def test_search_clear_functionality(
    method test_search_hide_functionality (line 549) | def test_search_hide_functionality(
    method test_speed_controls_functionality (line 573) | def test_speed_controls_functionality(
    method test_ui_state_persistence (line 604) | def test_ui_state_persistence(
    method test_button_sizing_consistency (line 619) | def test_button_sizing_consistency(
    method test_search_input_width (line 654) | def test_search_input_width(
    method test_current_segment_display_improvements (line 668) | def test_current_segment_display_improvements(
    method test_resize_current_segment_frame (line 697) | def test_resize_current_segment_frame(
    method test_playback_controls_visibility_methods (line 737) | def test_playback_controls_visibility_methods(
    method test_layout_optimizations (line 775) | def test_layout_optimizations(
    method test_settings_integration_for_new_features (line 813) | def test_settings_integration_for_new_features(
    method test_search_results_label_format (line 849) | def test_search_results_label_format(
    method test_current_segment_text_scrolling (line 876) | def test_current_segment_text_scrolling(
    method test_search_bar_visibility_toggle (line 903) | def test_search_bar_visibility_toggle(
    method test_audio_player_playback_state_disconnection (line 933) | def test_audio_player_playback_state_disconnection(
    method test_current_segment_display_styling (line 956) | def test_current_segment_display_styling(
    method test_search_clear_functionality_comprehensive (line 991) | def test_search_clear_functionality_comprehensive(
    method test_export_functionality_exists (line 1028) | def test_export_functionality_exists(
    method test_translation_functionality_exists (line 1043) | def test_translation_functionality_exists(
    method test_search_properties_exist (line 1062) | def test_search_properties_exist(
    method test_loop_properties_exist (line 1079) | def test_loop_properties_exist(
    method test_playback_controls_properties_exist (line 1096) | def test_playback_controls_properties_exist(
    method test_find_button_properties_exist (line 1115) | def test_find_button_properties_exist(
    method test_scroll_to_current_button_exists (line 1134) | def test_scroll_to_current_button_exists(
    method test_current_segment_display_exists (line 1152) | def test_current_segment_display_exists(
    method test_segment_selection_functionality_exists (line 1172) | def test_segment_selection_functionality_exists(
    method test_transcription_options_exist (line 1189) | def test_transcription_options_exist(
    method test_preferences_loading_exists (line 1205) | def test_preferences_loading_exists(
    method test_audio_position_tracking_exists (line 1219) | def test_audio_position_tracking_exists(
    method test_resize_current_segment_frame_exists (line 1233) | def test_resize_current_segment_frame_exists(
    method test_merge_button_functionality_exists (line 1249) | def test_merge_button_functionality_exists(
    method test_text_button_functionality_exists (line 1271) | def test_text_button_functionality_exists(
    method test_settings_integration_exists (line 1291) | def test_settings_integration_exists(
    method test_database_integration_exists (line 1306) | def test_database_integration_exists(
    method test_shortcuts_integration_exists (line 1321) | def test_shortcuts_integration_exists(
    method test_transcription_entity_access_exists (line 1336) | def test_transcription_entity_access_exists(
    method test_ui_layout_properties_exist (line 1351) | def test_ui_layout_properties_exist(
    method test_window_title_setting_exists (line 1370) | def test_window_title_setting_exists(
    method test_translations_detection_exists (line 1387) | def test_translations_detection_exists(
    method test_openai_token_access_exists (line 1402) | def test_openai_token_access_exists(
    method test_text_display_box_creation_exists (line 1416) | def test_text_display_box_creation_exists(
    method test_toolbar_creation_exists (line 1431) | def test_toolbar_creation_exists(

FILE: tests/widgets/update_dialog_test.py
  class TestUpdateDialogUI (line 31) | class TestUpdateDialogUI:
    method test_shows_version_info (line 32) | def test_shows_version_info(self, qtbot: QtBot):
    method test_download_button_is_present (line 42) | def test_download_button_is_present(self, qtbot: QtBot):
    method test_progress_bar_hidden_initially (line 47) | def test_progress_bar_hidden_initially(self, qtbot: QtBot):
    method test_status_label_empty_initially (line 52) | def test_status_label_empty_initially(self, qtbot: QtBot):
  class TestUpdateDialogDownload (line 58) | class TestUpdateDialogDownload:
    method test_shows_warning_when_no_download_urls (line 59) | def test_shows_warning_when_no_download_urls(self, qtbot: QtBot):
    method test_download_button_disabled_after_click (line 71) | def test_download_button_disabled_after_click(self, qtbot: QtBot):
    method test_progress_bar_shown_after_download_starts (line 85) | def test_progress_bar_shown_after_download_starts(self, qtbot: QtBot):
    method test_progress_bar_updates_on_progress (line 94) | def test_progress_bar_updates_on_progress(self, qtbot: QtBot):
    method test_single_file_download_runs_installer_on_windows (line 106) | def test_single_file_download_runs_installer_on_windows(self, qtbot: Q...
    method test_single_file_download_opens_dmg_on_macos (line 125) | def test_single_file_download_opens_dmg_on_macos(self, qtbot: QtBot):
    method test_multi_file_download_downloads_sequentially (line 148) | def test_multi_file_download_downloads_sequentially(self, qtbot: QtBot):
    method test_status_shows_file_count_during_multi_file_download (line 170) | def test_status_shows_file_count_during_multi_file_download(self, qtbo...
    method test_progress_bar_reaches_100_after_all_downloads (line 181) | def test_progress_bar_reaches_100_after_all_downloads(self, qtbot: QtB...
    method test_download_error_shows_message_and_resets_ui (line 196) | def test_download_error_shows_message_and_resets_ui(self, qtbot: QtBot):
    method test_save_error_shows_message_and_resets_ui (line 216) | def test_save_error_shows_message_and_resets_ui(self, qtbot: QtBot):
    method test_download_reply_stored_while_in_progress (line 231) | def test_download_reply_stored_while_in_progress(self, qtbot: QtBot):

FILE: tests/widgets/video_player_test.py
  function assert_approximately_equal (line 13) | def assert_approximately_equal(actual, expected, tolerance=0.001):
  class TestVideoPlayer (line 18) | class TestVideoPlayer:
    method test_should_load_media (line 19) | def test_should_load_media(self, qtbot: QtBot):
    method test_should_update_time_label (line 27) | def test_should_update_time_label(self, qtbot: QtBot):
    method test_should_toggle_play_button_icon (line 39) | def test_should_toggle_play_button_icon(self, qtbot: QtBot):
    method test_should_have_basic_video_controls (line 52) | def test_should_have_basic_video_controls(self, qtbot: QtBot):
    method test_should_change_playback_rate_directly (line 65) | def test_should_change_playback_rate_directly(self, qtbot: QtBot):
    method test_should_handle_various_playback_rates (line 72) | def test_should_handle_various_playback_rates(self, qtbot: QtBot):
    method test_should_use_vertical_layout (line 82) | def test_should_use_vertical_layout(self, qtbot: QtBot):
    method test_should_handle_range_looping (line 92) | def test_should_handle_range_looping(self, qtbot: QtBot):
    method test_should_stop_playback (line 104) | def test_should_stop_playback(self, qtbot: QtBot):
    method test_should_set_position (line 112) | def test_should_set_position(self, qtbot: QtBot):
    method test_should_track_slider_dragging (line 121) | def test_should_track_slider_dragging(self, qtbot: QtBot):
    method test_should_emit_position_changed_signal (line 136) | def test_should_emit_position_changed_signal(self, qtbot: QtBot):
    method test_should_update_scrubber_range_on_duration_change (line 146) | def test_should_update_scrubber_range_on_duration_change(self, qtbot: ...
    method test_should_toggle_playback (line 154) | def test_should_toggle_playback(self, qtbot: QtBot):
    method test_should_have_video_widget_constraints (line 161) | def test_should_have_video_widget_constraints(self, qtbot: QtBot):
    method test_should_have_audio_output (line 169) | def test_should_have_audio_output(self, qtbot: QtBot):
    method test_should_handle_range_with_position_outside (line 176) | def test_should_handle_range_with_position_outside(self, qtbot: QtBot):
    method test_should_handle_range_with_position_inside (line 187) | def test_should_handle_range_with_position_inside(self, qtbot: QtBot):
    method test_should_loop_at_range_end (line 198) | def test_should_loop_at_range_end(self, qtbot: QtBot):
    method test_should_not_update_scrubber_while_dragging (line 213) | def test_should_not_update_scrubber_while_dragging(self, qtbot: QtBot):
    method test_should_update_scrubber_when_not_dragging (line 235) | def test_should_update_scrubber_when_not_dragging(self, qtbot: QtBot):
    method test_initial_frame_loading (line 250) | def test_initial_frame_loading(self, qtbot: QtBot):
    method test_play_button_sizing (line 269) | def test_play_button_sizing(self, qtbot: QtBot):
Condensed preview — 254 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,128K chars).
[
  {
    "path": ".coveragerc",
    "chars": 398,
    "preview": "[run]\nomit =\n  buzz/whisper_cpp/*\n  buzz/transcriber/local_whisper_cpp_server_transcriber.py\n  *_test.py\n  demucs/*\n  wh"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 12022,
    "preview": "name: CI\non:\n  push:\n    branches:\n      - main\n    tags:\n      - \"*\"\n  pull_request:\n\nconcurrency:\n  group: ${{ github."
  },
  {
    "path": ".github/workflows/gh-pages.yml",
    "chars": 674,
    "preview": "---\nname: GitHub Pages\non:\n  push:\n    branches:\n      - main\n\njobs:\n  deploy:\n    name: Deploy\n    runs-on: ubuntu-late"
  },
  {
    "path": ".github/workflows/manual-build.yml",
    "chars": 2366,
    "preview": "---\nname: Manual Build\non: workflow_dispatch\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.event.pull_request"
  },
  {
    "path": ".github/workflows/snapcraft.yml",
    "chars": 3303,
    "preview": "---\nname: Snapcraft\non:\n  push:\n    branches:\n      - main\n    tags:\n      - \"*\"\n  pull_request:\n\nconcurrency:\n  group: "
  },
  {
    "path": ".gitignore",
    "chars": 359,
    "preview": "dist/\n__pycache__/\nbuild/\n.pytest_cache/\n.coverage*\n!.coveragerc\n.env\n.DS_Store\nhtmlcov/\ncoverage.xml\n.idea/\n.venv/\nvenv"
  },
  {
    "path": ".gitmodules",
    "chars": 589,
    "preview": "[submodule \"whisper.cpp\"]\n\tpath = whisper.cpp\n\turl = https://github.com/ggerganov/whisper.cpp\n[submodule \"whisper_diariz"
  },
  {
    "path": ".pre-commit-config.yaml",
    "chars": 385,
    "preview": "repos:\n  - repo: https://github.com/pre-commit/pre-commit-hooks\n    rev: v4.5.0\n    hooks:\n      - id: trailing-whitespa"
  },
  {
    "path": ".pylintrc",
    "chars": 92,
    "preview": "[MASTER]\ndisable=\n  C0114, # missing-module-docstring\n  C0116, # missing-function-docstring\n"
  },
  {
    "path": ".python-version",
    "chars": 5,
    "preview": "3.12\n"
  },
  {
    "path": ".run/pytest.run.xml",
    "chars": 1473,
    "preview": "<component name=\"ProjectRunConfigurationManager\">\n  <configuration default=\"false\" name=\"pytest\" type=\"tests\" factoryNam"
  },
  {
    "path": ".vscode/launch.json",
    "chars": 382,
    "preview": "{\n  // Use IntelliSense to learn about possible attributes.\n  // Hover to view descriptions of existing attributes.\n  //"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 522,
    "preview": "{\n  \"files.associations\": {\n    \".coveragerc\": \"ini\",\n    \"Buzz.spec\": \"python\",\n    \"iosfwd\": \"cpp\"\n  },\n  \"files.exclu"
  },
  {
    "path": ".vscode/tasks.json",
    "chars": 342,
    "preview": "{\n  // See https://go.microsoft.com/fwlink/?LinkId=733558\n  // for the documentation about the tasks.json format\n  \"vers"
  },
  {
    "path": "Buzz.spec",
    "chars": 5302,
    "preview": "# -*- mode: python ; coding: utf-8 -*-\nimport os\nimport os.path\nimport platform\nimport shutil\n\nfrom PyInstaller.utils.ho"
  },
  {
    "path": "CLAUDE.md",
    "chars": 37,
    "preview": "- Use uv to run tests and any scripts"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 5490,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 6619,
    "preview": "# Buzz Contribution Guide\n\n## Internationalization\n\nTo contribute a new language translation to Buzz:\n\n1. Run `make tran"
  },
  {
    "path": "LICENSE",
    "chars": 1071,
    "preview": "MIT License\n\nCopyright (c) 2022 Chidi Williams\n\nPermission is hereby granted, free of charge, to any person obtaining a "
  },
  {
    "path": "Makefile",
    "chars": 9850,
    "preview": "# Change also in pyproject.toml and buzz/__version__.py\nversion := 1.4.4\n\nmac_app_path := ./dist/Buzz.app\nmac_zip_path :"
  },
  {
    "path": "README.ja_JP.md",
    "chars": 3790,
    "preview": "# Buzz\n\n[ドキュメント](https://chidiwilliams.github.io/buzz/)\n\nパソコン上でオフラインで音声の文字起こしと翻訳を行います。OpenAIの[Whisper](https://github.co"
  },
  {
    "path": "README.md",
    "chars": 4962,
    "preview": "[[简体中文](readme/README.zh_CN.md)] <- 点击查看中文页面。\n\n# Buzz\n\n[Documentation](https://chidiwilliams.github.io/buzz/)\n\nTranscrib"
  },
  {
    "path": "buzz/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "buzz/__main__.py",
    "chars": 66,
    "preview": "import buzz.buzz\n\nif __name__ == \"__main__\":\n    buzz.buzz.main()\n"
  },
  {
    "path": "buzz/__version__.py",
    "chars": 18,
    "preview": "VERSION = \"1.4.4\"\n"
  },
  {
    "path": "buzz/action.py",
    "chars": 708,
    "preview": "import typing\n\nfrom PyQt6.QtGui import QAction, QKeySequence\n\n\nclass Action(QAction):\n    def setShortcut(\n        self,"
  },
  {
    "path": "buzz/assets.py",
    "chars": 257,
    "preview": "import os\nimport sys\n\nAPP_BASE_DIR = (\n    getattr(sys, \"_MEIPASS\", os.path.dirname(os.path.abspath(__file__)))\n    if g"
  },
  {
    "path": "buzz/buzz.py",
    "chars": 3900,
    "preview": "import faulthandler\nimport logging\nimport multiprocessing\nimport os\nimport platform\nimport sys\nfrom pathlib import Path\n"
  },
  {
    "path": "buzz/cache.py",
    "chars": 2631,
    "preview": "import json\nimport logging\nimport os\nimport pickle\nfrom typing import List\n\nfrom platformdirs import user_cache_dir\n\nfro"
  },
  {
    "path": "buzz/cli.py",
    "chars": 9057,
    "preview": "import enum\nimport sys\nimport typing\nimport urllib.parse\n\nfrom PyQt6.QtCore import QCommandLineParser, QCommandLineOptio"
  },
  {
    "path": "buzz/conn.py",
    "chars": 410,
    "preview": "import sys\nfrom contextlib import contextmanager\nfrom multiprocessing.connection import Connection\n\n\nclass ConnWriter:\n "
  },
  {
    "path": "buzz/cuda_setup.py",
    "chars": 4396,
    "preview": "\"\"\"\nCUDA library path setup for nvidia packages installed via pip.\n\nThis module must be imported BEFORE any torch or CUD"
  },
  {
    "path": "buzz/db/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "buzz/db/dao/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "buzz/db/dao/dao.py",
    "chars": 1961,
    "preview": "# Adapted from https://github.com/zhiyiYo/Groove\nfrom abc import ABC\nfrom typing import TypeVar, Generic, Any, Type, Lis"
  },
  {
    "path": "buzz/db/dao/transcription_dao.py",
    "chars": 10672,
    "preview": "import uuid\nfrom datetime import datetime\nfrom uuid import UUID\n\nfrom PyQt6.QtSql import QSqlDatabase\n\nfrom buzz.db.dao."
  },
  {
    "path": "buzz/db/dao/transcription_segment_dao.py",
    "chars": 1654,
    "preview": "from typing import List\nfrom uuid import UUID\n\nfrom PyQt6.QtSql import QSqlDatabase\n\nfrom buzz.db.dao.dao import DAO\nfro"
  },
  {
    "path": "buzz/db/db.py",
    "chars": 1342,
    "preview": "import logging\nimport os\nimport sqlite3\nimport tempfile\n\nfrom PyQt6.QtSql import QSqlDatabase\nfrom platformdirs import u"
  },
  {
    "path": "buzz/db/entity/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "buzz/db/entity/entity.py",
    "chars": 291,
    "preview": "from abc import ABC\n\nfrom PyQt6.QtSql import QSqlRecord\n\n\nclass Entity(ABC):\n    @classmethod\n    def from_record(cls, r"
  },
  {
    "path": "buzz/db/entity/transcription.py",
    "chars": 2297,
    "preview": "import datetime\nimport os\nimport uuid\nfrom dataclasses import dataclass, field\n\nfrom buzz.db.entity.entity import Entity"
  },
  {
    "path": "buzz/db/entity/transcription_segment.py",
    "chars": 241,
    "preview": "from dataclasses import dataclass\n\nfrom buzz.db.entity.entity import Entity\n\n\n@dataclass\nclass TranscriptionSegment(Enti"
  },
  {
    "path": "buzz/db/helpers.py",
    "chars": 3351,
    "preview": "import os\nfrom datetime import datetime\nfrom sqlite3 import Connection\n\nfrom buzz.assets import get_path\nfrom buzz.cache"
  },
  {
    "path": "buzz/db/migrator.py",
    "chars": 10744,
    "preview": "# coding: utf-8\n# https://gist.github.com/simonw/664b4b0851c1899dc55e1fb655181037\n\n\"\"\"Simple declarative schema migratio"
  },
  {
    "path": "buzz/db/service/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "buzz/db/service/transcription_service.py",
    "chars": 3267,
    "preview": "from typing import List\nfrom uuid import UUID\n\nfrom buzz.db.dao.transcription_dao import TranscriptionDAO\nfrom buzz.db.d"
  },
  {
    "path": "buzz/dialogs.py",
    "chars": 405,
    "preview": "from PyQt6.QtWidgets import QWidget, QMessageBox\n\n\ndef show_model_download_error_dialog(parent: QWidget, error: str):\n  "
  },
  {
    "path": "buzz/file_transcriber_queue_worker.py",
    "chars": 11436,
    "preview": "import logging\nimport multiprocessing\nimport os\nimport queue\nimport ssl\nimport sys\nfrom pathlib import Path\nfrom typing "
  },
  {
    "path": "buzz/locale/ca_ES/LC_MESSAGES/buzz.po",
    "chars": 46749,
    "preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
  },
  {
    "path": "buzz/locale/da_DK/LC_MESSAGES/buzz.po",
    "chars": 44710,
    "preview": "msgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2026-03-07 20:20+0200\\n\"\n\"PO-"
  },
  {
    "path": "buzz/locale/de_DE/LC_MESSAGES/buzz.po",
    "chars": 47032,
    "preview": "# Copyright (C) 2025 THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE pac"
  },
  {
    "path": "buzz/locale/en_US/LC_MESSAGES/buzz.po",
    "chars": 36348,
    "preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
  },
  {
    "path": "buzz/locale/es_ES/LC_MESSAGES/buzz.po",
    "chars": 48134,
    "preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
  },
  {
    "path": "buzz/locale/it_IT/LC_MESSAGES/buzz.po",
    "chars": 47140,
    "preview": "# ITALIAN TRANSLATION.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same lic"
  },
  {
    "path": "buzz/locale/ja_JP/LC_MESSAGES/buzz.po",
    "chars": 41394,
    "preview": "msgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2026-03-07 20:20+0200\\n\"\n\"PO-"
  },
  {
    "path": "buzz/locale/lv_LV/LC_MESSAGES/buzz.po",
    "chars": 45282,
    "preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
  },
  {
    "path": "buzz/locale/nl/LC_MESSAGES/buzz.po",
    "chars": 46185,
    "preview": "# Dutch translations for PACKAGE package\n# Nederlandse vertalingen voor het pakket PACKAGE.\n# Copyright (C) 2025 THE PAC"
  },
  {
    "path": "buzz/locale/pl_PL/LC_MESSAGES/buzz.po",
    "chars": 45437,
    "preview": "# POLISH TRANSLATION.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same lice"
  },
  {
    "path": "buzz/locale/pt_BR/LC_MESSAGES/buzz.po",
    "chars": 46017,
    "preview": "# Portuguese-Brazilian Translation for Buzz Project.\n# Copyright (C) 2025\n# This file is distributed under the same lice"
  },
  {
    "path": "buzz/locale/uk_UA/LC_MESSAGES/buzz.po",
    "chars": 46043,
    "preview": "msgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2026-03-07 20:20+0200\\n\"\n\"PO-"
  },
  {
    "path": "buzz/locale/zh_CN/LC_MESSAGES/buzz.po",
    "chars": 40243,
    "preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
  },
  {
    "path": "buzz/locale/zh_TW/LC_MESSAGES/buzz.po",
    "chars": 39750,
    "preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
  },
  {
    "path": "buzz/locale.py",
    "chars": 466,
    "preview": "import os\nimport logging\nimport gettext\n\nfrom PyQt6.QtCore import QLocale\n\nfrom buzz.assets import get_path\nfrom buzz.se"
  },
  {
    "path": "buzz/model_loader.py",
    "chars": 39977,
    "preview": "import enum\nimport hashlib\nimport logging\nimport os\nimport time\nimport threading\nimport shutil\nimport subprocess\nimport "
  },
  {
    "path": "buzz/paths.py",
    "chars": 91,
    "preview": "import os\n\n\ndef file_path_as_title(file_path: str):\n    return os.path.basename(file_path)\n"
  },
  {
    "path": "buzz/recording.py",
    "chars": 1897,
    "preview": "from typing import Optional\n\nimport logging\nimport numpy as np\nimport sounddevice\nfrom PyQt6.QtCore import QObject, pyqt"
  },
  {
    "path": "buzz/schema.sql",
    "chars": 931,
    "preview": "CREATE TABLE transcription (\n    id TEXT PRIMARY KEY,\n    error_message TEXT,\n    export_formats TEXT,\n    file TEXT,\n  "
  },
  {
    "path": "buzz/settings/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "buzz/settings/recording_transcriber_mode.py",
    "chars": 209,
    "preview": "from enum import Enum\nfrom buzz.locale import _\n\nclass RecordingTranscriberMode(Enum):\n    APPEND_BELOW = _(\"Append belo"
  },
  {
    "path": "buzz/settings/settings.py",
    "chars": 6489,
    "preview": "import enum\nimport typing\nimport logging\nimport uuid\n\nfrom PyQt6.QtCore import QSettings\n\nAPP_NAME = \"Buzz\"\n\n\nclass Sett"
  },
  {
    "path": "buzz/settings/shortcut.py",
    "chars": 1888,
    "preview": "import enum\nimport typing\nfrom buzz.locale import _\n\n\nclass Shortcut(str, enum.Enum):\n    sequence: str\n    description:"
  },
  {
    "path": "buzz/settings/shortcuts.py",
    "chars": 829,
    "preview": "import typing\n\nfrom buzz.settings.settings import Settings\nfrom buzz.settings.shortcut import Shortcut\n\n\nclass Shortcuts"
  },
  {
    "path": "buzz/store/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "buzz/store/keyring_store.py",
    "chars": 7744,
    "preview": "import base64\nimport enum\nimport hashlib\nimport json\nimport logging\nimport os\nimport sys\n\nimport keyring\n\nfrom buzz.sett"
  },
  {
    "path": "buzz/transcriber/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "buzz/transcriber/file_transcriber.py",
    "chars": 8759,
    "preview": "import logging\nimport os\nimport sys\nimport subprocess\nimport shutil\nimport tempfile\nfrom abc import abstractmethod\nfrom "
  },
  {
    "path": "buzz/transcriber/local_whisper_cpp_server_transcriber.py",
    "chars": 3722,
    "preview": "import logging\nimport os\nimport time\nimport subprocess\nfrom typing import Optional, List\n\nfrom PyQt6.QtCore import QObje"
  },
  {
    "path": "buzz/transcriber/openai_whisper_api_file_transcriber.py",
    "chars": 10831,
    "preview": "import logging\nimport math\nimport os\nimport sys\nimport subprocess\nimport tempfile\n\nfrom pathlib import Path\nfrom typing "
  },
  {
    "path": "buzz/transcriber/recording_transcriber.py",
    "chars": 22890,
    "preview": "import datetime\nimport logging\nimport platform\nimport os\nimport sys\nimport wave\nimport time\nimport tempfile\nimport threa"
  },
  {
    "path": "buzz/transcriber/transcriber.py",
    "chars": 7200,
    "preview": "import datetime\nimport enum\nimport os\nimport uuid\nfrom dataclasses import dataclass, field\nfrom random import randint\nfr"
  },
  {
    "path": "buzz/transcriber/whisper_cpp.py",
    "chars": 17099,
    "preview": "import platform\nimport os\nimport sys\nimport logging\nimport subprocess\nimport json\nfrom typing import List\nfrom buzz.asse"
  },
  {
    "path": "buzz/transcriber/whisper_file_transcriber.py",
    "chars": 18261,
    "preview": "import datetime\nimport json\nimport logging\nimport multiprocessing\nimport re\nimport os\nimport sys\n\n# Preload CUDA librari"
  },
  {
    "path": "buzz/transformers_whisper.py",
    "chars": 21546,
    "preview": "import os\nimport sys\nimport logging\nimport platform\nimport numpy as np\n\n# Preload CUDA libraries before importing torch\n"
  },
  {
    "path": "buzz/translator.py",
    "chars": 7477,
    "preview": "import os\nimport re\nimport logging\nimport queue\n\nfrom typing import Optional, List, Tuple\nfrom openai import OpenAI, max"
  },
  {
    "path": "buzz/update_checker.py",
    "chars": 5436,
    "preview": "import json\nimport logging\nimport platform\nfrom datetime import datetime\nfrom typing import Optional\nfrom dataclasses im"
  },
  {
    "path": "buzz/whisper_audio.py",
    "chars": 2038,
    "preview": "import subprocess\nimport numpy as np\nimport sys\nimport os\nimport logging\n\nfrom buzz.assets import APP_BASE_DIR\n\nSAMPLE_R"
  },
  {
    "path": "buzz/widgets/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "buzz/widgets/about_dialog.py",
    "chars": 4323,
    "preview": "import json\nfrom typing import Optional\nfrom platformdirs import user_log_dir\n\nfrom PyQt6 import QtGui\nfrom PyQt6.QtCore"
  },
  {
    "path": "buzz/widgets/application.py",
    "chars": 3867,
    "preview": "import logging\nimport os\nimport sys\nimport locale\nimport platform\nimport darkdetect\n\nfrom posthog import Posthog\n\nfrom P"
  },
  {
    "path": "buzz/widgets/audio_devices_combo_box.py",
    "chars": 1967,
    "preview": "from typing import List, Tuple, Optional\n\nimport sounddevice\nfrom PyQt6.QtCore import pyqtSignal\nfrom PyQt6.QtWidgets im"
  },
  {
    "path": "buzz/widgets/audio_meter_widget.py",
    "chars": 3538,
    "preview": "from typing import Optional\n\nfrom PyQt6 import QtGui\nfrom PyQt6.QtCore import Qt, QRect\nfrom PyQt6.QtGui import QColor, "
  },
  {
    "path": "buzz/widgets/audio_player.py",
    "chars": 9701,
    "preview": "import logging\nfrom typing import Tuple, Optional\n\nfrom PyQt6 import QtGui\nfrom PyQt6.QtCore import QTime, QUrl, Qt, pyq"
  },
  {
    "path": "buzz/widgets/form_label.py",
    "chars": 468,
    "preview": "from typing import Optional\n\nfrom PyQt6.QtCore import Qt\nfrom PyQt6.QtWidgets import QLabel, QWidget\n\n\nclass FormLabel(Q"
  },
  {
    "path": "buzz/widgets/icon.py",
    "chars": 4564,
    "preview": "from PyQt6.QtGui import QIcon, QPixmap, QPainter, QColor\nfrom PyQt6.QtWidgets import QWidget\n\nfrom buzz.assets import ge"
  },
  {
    "path": "buzz/widgets/icon_presentation.py",
    "chars": 1308,
    "preview": "from PyQt6.QtGui import QIcon, QPixmap, QPainter, QPalette\nfrom PyQt6.QtCore import QSize\nfrom PyQt6.QtSvg import QSvgRe"
  },
  {
    "path": "buzz/widgets/import_url_dialog.py",
    "chars": 1940,
    "preview": "from typing import Optional\n\nfrom PyQt6.QtCore import Qt, QRegularExpression\nfrom PyQt6.QtWidgets import QDialog, QWidge"
  },
  {
    "path": "buzz/widgets/line_edit.py",
    "chars": 453,
    "preview": "import platform\nfrom typing import Optional\n\nfrom PyQt6.QtWidgets import QLineEdit, QWidget, QSizePolicy\n\n\nclass LineEdi"
  },
  {
    "path": "buzz/widgets/main_window.py",
    "chars": 20254,
    "preview": "import os\nimport logging\nfrom typing import Tuple, List, Optional\nfrom uuid import UUID\n\nfrom PyQt6 import QtGui\nfrom Py"
  },
  {
    "path": "buzz/widgets/main_window_toolbar.py",
    "chars": 4752,
    "preview": "from typing import Optional\n\nfrom PyQt6.QtCore import pyqtSignal, Qt\nfrom PyQt6.QtGui import QKeySequence\nfrom PyQt6.QtW"
  },
  {
    "path": "buzz/widgets/menu_bar.py",
    "chars": 4334,
    "preview": "import platform\nimport webbrowser\nfrom typing import Optional\n\nfrom PyQt6.QtCore import pyqtSignal\nfrom PyQt6.QtGui impo"
  },
  {
    "path": "buzz/widgets/model_download_progress_dialog.py",
    "chars": 2190,
    "preview": "from datetime import datetime\nfrom typing import Optional\n\nimport humanize\nfrom PyQt6.QtCore import Qt\nfrom PyQt6.QtWidg"
  },
  {
    "path": "buzz/widgets/model_type_combo_box.py",
    "chars": 936,
    "preview": "from typing import Optional, List\n\nfrom PyQt6.QtCore import pyqtSignal\nfrom PyQt6.QtWidgets import QComboBox, QWidget\n\nf"
  },
  {
    "path": "buzz/widgets/openai_api_key_line_edit.py",
    "chars": 1724,
    "preview": "import logging\nfrom typing import Optional\n\nfrom PyQt6.QtCore import pyqtSignal\nfrom PyQt6.QtWidgets import QWidget, QLi"
  },
  {
    "path": "buzz/widgets/preferences_dialog/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "buzz/widgets/preferences_dialog/folder_watch_preferences_widget.py",
    "chars": 6230,
    "preview": "from typing import Tuple, Optional\n\nfrom PyQt6.QtCore import pyqtSignal\nfrom PyQt6.QtWidgets import (\n    QWidget,\n    Q"
  },
  {
    "path": "buzz/widgets/preferences_dialog/general_preferences_widget.py",
    "chars": 15911,
    "preview": "import re\nimport logging\nimport requests\nfrom typing import Optional\nfrom platformdirs import user_documents_dir\n\nfrom P"
  },
  {
    "path": "buzz/widgets/preferences_dialog/models/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "buzz/widgets/preferences_dialog/models/file_transcription_preferences.py",
    "chars": 4977,
    "preview": "from dataclasses import dataclass\nfrom typing import Optional, Tuple, Set, List\n\nfrom PyQt6.QtCore import QSettings\n\nfro"
  },
  {
    "path": "buzz/widgets/preferences_dialog/models/folder_watch_preferences.py",
    "chars": 1859,
    "preview": "from dataclasses import dataclass\n\nfrom PyQt6.QtCore import QSettings\n\nfrom buzz.widgets.preferences_dialog.models.file_"
  },
  {
    "path": "buzz/widgets/preferences_dialog/models/preferences.py",
    "chars": 669,
    "preview": "from dataclasses import dataclass\n\nfrom PyQt6.QtCore import QSettings\n\nfrom buzz.widgets.preferences_dialog.models.folde"
  },
  {
    "path": "buzz/widgets/preferences_dialog/models_preferences_widget.py",
    "chars": 11136,
    "preview": "import logging\nfrom typing import Optional\n\nfrom PyQt6.QtCore import Qt, QThreadPool, QLocale\nfrom PyQt6.QtWidgets impor"
  },
  {
    "path": "buzz/widgets/preferences_dialog/preferences_dialog.py",
    "chars": 2933,
    "preview": "import copy\nfrom typing import Optional\n\nfrom PyQt6.QtCore import pyqtSignal\nfrom PyQt6.QtWidgets import QDialog, QWidge"
  },
  {
    "path": "buzz/widgets/preferences_dialog/shortcuts_editor_preferences_widget.py",
    "chars": 2057,
    "preview": "from typing import Optional\n\nfrom PyQt6.QtCore import pyqtSignal\nfrom PyQt6.QtGui import QKeySequence\nfrom PyQt6.QtWidge"
  },
  {
    "path": "buzz/widgets/presentation_window.py",
    "chars": 5999,
    "preview": "import logging\nfrom typing import Optional\nfrom PyQt6.QtCore import Qt\nfrom PyQt6.QtGui import QTextCursor\nfrom PyQt6.Qt"
  },
  {
    "path": "buzz/widgets/record_button.py",
    "chars": 587,
    "preview": "from typing import Optional\n\nfrom PyQt6.QtWidgets import QPushButton, QWidget, QSizePolicy\n\nfrom buzz.locale import _\n\n\n"
  },
  {
    "path": "buzz/widgets/record_delegate.py",
    "chars": 505,
    "preview": "from typing import Callable\n\nfrom PyQt6.QtSql import QSqlRecord, QSqlTableModel\nfrom PyQt6.QtWidgets import QStyledItemD"
  },
  {
    "path": "buzz/widgets/recording_transcriber_widget.py",
    "chars": 50872,
    "preview": "import csv\nimport io\nimport os\nimport re\nimport enum\nimport time\nimport requests\nimport logging\nimport datetime\nimport s"
  },
  {
    "path": "buzz/widgets/sequence_edit.py",
    "chars": 1187,
    "preview": "import platform\nfrom typing import Optional\n\nfrom PyQt6 import QtGui\nfrom PyQt6.QtCore import Qt\nfrom PyQt6.QtWidgets im"
  },
  {
    "path": "buzz/widgets/text_display_box.py",
    "chars": 307,
    "preview": "from typing import Optional\n\nfrom PyQt6.QtWidgets import QPlainTextEdit, QWidget\n\n\nclass TextDisplayBox(QPlainTextEdit):"
  },
  {
    "path": "buzz/widgets/toolbar.py",
    "chars": 939,
    "preview": "import platform\nimport typing\n\nfrom PyQt6 import QtGui\nfrom PyQt6.QtCore import QSize, Qt\nfrom PyQt6.QtWidgets import QT"
  },
  {
    "path": "buzz/widgets/transcriber/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "buzz/widgets/transcriber/advanced_settings_button.py",
    "chars": 259,
    "preview": "from typing import Optional\n\nfrom PyQt6.QtWidgets import QPushButton, QWidget\n\nfrom buzz.locale import _\n\nclass Advanced"
  },
  {
    "path": "buzz/widgets/transcriber/advanced_settings_dialog.py",
    "chars": 16645,
    "preview": "from PyQt6.QtCore import pyqtSignal\nfrom PyQt6.QtWidgets import (\n    QDialog,\n    QWidget,\n    QDialogButtonBox,\n    QC"
  },
  {
    "path": "buzz/widgets/transcriber/file_transcriber_widget.py",
    "chars": 7137,
    "preview": "import logging\nfrom typing import Optional, List, Tuple\n\nfrom PyQt6 import QtGui\nfrom PyQt6.QtCore import pyqtSignal, Qt"
  },
  {
    "path": "buzz/widgets/transcriber/file_transcription_form_widget.py",
    "chars": 4624,
    "preview": "import logging\nfrom typing import Optional\n\nfrom PyQt6.QtCore import pyqtSignal, Qt\nfrom PyQt6.QtWidgets import QWidget,"
  },
  {
    "path": "buzz/widgets/transcriber/hugging_face_search_line_edit.py",
    "chars": 5452,
    "preview": "import json\nimport logging\nfrom typing import Optional\n\nfrom PyQt6.QtCore import (\n    pyqtSignal,\n    QTimer,\n    Qt,\n "
  },
  {
    "path": "buzz/widgets/transcriber/initial_prompt_text_edit.py",
    "chars": 474,
    "preview": "from PyQt6.QtWidgets import QPlainTextEdit, QWidget\n\nfrom buzz.locale import _\nfrom buzz.model_loader import ModelType\n\n"
  },
  {
    "path": "buzz/widgets/transcriber/languages_combo_box.py",
    "chars": 2245,
    "preview": "from typing import Optional\nimport os\n\nfrom PyQt6.QtCore import pyqtSignal, Qt\nfrom PyQt6.QtWidgets import QComboBox, QW"
  },
  {
    "path": "buzz/widgets/transcriber/mms_language_line_edit.py",
    "chars": 1619,
    "preview": "from typing import Optional\n\nfrom PyQt6.QtCore import pyqtSignal\nfrom PyQt6.QtWidgets import QWidget, QSizePolicy\n\nfrom "
  },
  {
    "path": "buzz/widgets/transcriber/tasks_combo_box.py",
    "chars": 807,
    "preview": "from typing import Optional\n\nfrom PyQt6.QtCore import pyqtSignal\nfrom PyQt6.QtWidgets import QComboBox, QWidget\n\nfrom bu"
  },
  {
    "path": "buzz/widgets/transcriber/transcription_options_group_box.py",
    "chars": 14172,
    "preview": "import os\nimport logging\nimport platform\nfrom typing import Optional, List\n\nfrom PyQt6.QtCore import pyqtSignal, QLocale"
  },
  {
    "path": "buzz/widgets/transcription_record.py",
    "chars": 918,
    "preview": "from uuid import UUID\nimport logging\nfrom PyQt6.QtSql import QSqlRecord\n\nfrom buzz.model_loader import TranscriptionMode"
  },
  {
    "path": "buzz/widgets/transcription_task_folder_watcher.py",
    "chars": 5348,
    "preview": "import logging\nimport os\nfrom typing import Dict\n\nfrom PyQt6.QtCore import QFileSystemWatcher, pyqtSignal, QObject\n\nfrom"
  },
  {
    "path": "buzz/widgets/transcription_tasks_table_widget.py",
    "chars": 31839,
    "preview": "import enum\nimport logging\nimport os\nfrom dataclasses import dataclass\nfrom datetime import datetime, timedelta\nfrom enu"
  },
  {
    "path": "buzz/widgets/transcription_viewer/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "buzz/widgets/transcription_viewer/export_transcription_menu.py",
    "chars": 3182,
    "preview": "import logging\nfrom PyQt6.QtGui import QAction\nfrom PyQt6.QtCore import pyqtSignal\nfrom PyQt6.QtWidgets import QWidget, "
  },
  {
    "path": "buzz/widgets/transcription_viewer/speaker_identification_widget.py",
    "chars": 31053,
    "preview": "import re\nimport os\nimport logging\nimport ssl\nimport time\nimport random\nfrom typing import Optional\n\n# Fix SSL certifica"
  },
  {
    "path": "buzz/widgets/transcription_viewer/transcription_resizer_widget.py",
    "chars": 16123,
    "preview": "import re\nimport os\nimport logging\nimport stable_whisper\nimport srt\nfrom pathlib import Path\nfrom srt_equalizer import s"
  },
  {
    "path": "buzz/widgets/transcription_viewer/transcription_segments_editor_widget.py",
    "chars": 14195,
    "preview": "import enum\nimport logging\nfrom dataclasses import dataclass\nfrom typing import Optional\nfrom uuid import UUID\n\nfrom PyQ"
  },
  {
    "path": "buzz/widgets/transcription_viewer/transcription_view_mode_tool_button.py",
    "chars": 2009,
    "preview": "import logging\nfrom enum import Enum\nfrom typing import Optional\n\nfrom PyQt6.QtCore import pyqtSignal, Qt\nfrom PyQt6.QtG"
  },
  {
    "path": "buzz/widgets/transcription_viewer/transcription_viewer_widget.py",
    "chars": 68332,
    "preview": "import os\nimport logging\nimport platform\nfrom typing import Optional\nfrom uuid import UUID\n\nfrom PyQt6.QtCore import Qt,"
  },
  {
    "path": "buzz/widgets/update_dialog.py",
    "chars": 8678,
    "preview": "import logging\nimport os\nimport platform\nimport subprocess\nimport tempfile\nfrom typing import Optional\n\nfrom PyQt6.QtCor"
  },
  {
    "path": "buzz/widgets/video_player.py",
    "chars": 6849,
    "preview": "import logging\nfrom typing import Tuple, Optional\nfrom PyQt6.QtCore import Qt, QUrl, pyqtSignal, QTime\nfrom PyQt6.QtMult"
  },
  {
    "path": "buzz.desktop",
    "chars": 207,
    "preview": "[Desktop Entry]\n\nType=Application\n\nEncoding=UTF-8\n\nName=Buzz\n\nComment=Buzz transcribes and translates audio offline on y"
  },
  {
    "path": "docs/.gitignore",
    "chars": 233,
    "preview": "# Dependencies\n/node_modules\n\n# Production\n/build\n\n# Generated files\n.docusaurus\n.cache-loader\n\n# Misc\n.DS_Store\n.env.lo"
  },
  {
    "path": "docs/README.md",
    "chars": 770,
    "preview": "# Website\n\nThis website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator.\n\n### I"
  },
  {
    "path": "docs/babel.config.js",
    "chars": 89,
    "preview": "module.exports = {\n  presets: [require.resolve('@docusaurus/core/lib/babel/preset')],\n};\n"
  },
  {
    "path": "docs/docs/cli.md",
    "chars": 5153,
    "preview": "---\ntitle: CLI\nsidebar_position: 5\n---\n\n## Commands\n\n### `add`\n\nStart a new transcription task.\n\n```\nUsage: buzz add [op"
  },
  {
    "path": "docs/docs/faq.md",
    "chars": 7371,
    "preview": "---\ntitle: FAQ\nsidebar_position: 5\n---\n\n### 1. Where are the models stored?\n\nThe models are stored:\n\n- Linux: `~/.cache/"
  },
  {
    "path": "docs/docs/index.md",
    "chars": 2359,
    "preview": "---\ntitle: Introduction\nsidebar_position: 1\n---\n\nTranscribe and translate audio offline on your personal computer. Power"
  },
  {
    "path": "docs/docs/installation.md",
    "chars": 1694,
    "preview": "---\ntitle: Installation\nsidebar_position: 2\n---\n\nTo install Buzz, download the latest version for your operating\nsystem."
  },
  {
    "path": "docs/docs/preferences.md",
    "chars": 7945,
    "preview": "---\ntitle: Preferences\nsidebar_position: 4\n---\n\nOpen the Preferences window from the Menu bar, or click `Ctrl/Cmd + ,`.\n"
  },
  {
    "path": "docs/docs/usage/1_file_import.md",
    "chars": 2630,
    "preview": "---\ntitle: File Import\n---\n\n**To import a file:**\n\n- Click Import Media File on the File menu (or the '+' icon on the to"
  },
  {
    "path": "docs/docs/usage/2_live_recording.md",
    "chars": 7885,
    "preview": "---\ntitle: Live Recording\n---\n\nTo start a live recording:\n\n- Select a recording task, language, quality, and microphone."
  },
  {
    "path": "docs/docs/usage/3_translations.md",
    "chars": 1971,
    "preview": "---\ntitle: Translations\n---\n\nDefault `Translation` task uses Whisper model ability to translate to English, however `Lar"
  },
  {
    "path": "docs/docs/usage/4_edit_and_resize.md",
    "chars": 1502,
    "preview": "---\ntitle: Edit and Resize\n---\n\n[![Resize options](https://raw.githubusercontent.com/chidiwilliams/buzz/main/share/scree"
  },
  {
    "path": "docs/docs/usage/5_speaker_identification.md",
    "chars": 869,
    "preview": "---\ntitle: Speaker identification\n---\n\nWhen transcript of some audio or video file is generated you can identify speaker"
  },
  {
    "path": "docs/docs/usage/5_transcription_viewer.md",
    "chars": 5165,
    "preview": "# Transcription Viewer\n\nThe Buzz transcription viewer provides a powerful interface for reviewing, editing, and navigati"
  },
  {
    "path": "docs/docs/usage/_category_.yml",
    "chars": 25,
    "preview": "label: Usage\nposition: 3\n"
  },
  {
    "path": "docs/docusaurus.config.js",
    "chars": 2705,
    "preview": "// @ts-check\n// Note: type annotations allow type checking and IDEs autocompletion\n\nconst lightCodeTheme = require(\"pris"
  },
  {
    "path": "docs/i18n/zh/docusaurus-plugin-content-docs/current/cli.md",
    "chars": 4963,
    "preview": "---\ntitle: 命令行界面 (CLI)\nsidebar_position: 5\n---\n\n## 命令\n\n### `增加`\n\n启动一个新的转录任务。\n\n```\nUsage: buzz add [options] [file url fi"
  },
  {
    "path": "docs/i18n/zh/docusaurus-plugin-content-docs/current/faq.md",
    "chars": 2699,
    "preview": "---\ntitle: 常见问题(FAQ)\nsidebar_position: 5\n---\n\n### 1. 模型存储在哪里?\n\n模型存储在以下位置:\n\n- Linux: `~/.cache/Buzz`\n- Mac OS: `~/Library"
  },
  {
    "path": "docs/i18n/zh/docusaurus-plugin-content-docs/current/index.md",
    "chars": 1343,
    "preview": "---\ntitle: 介绍\nsidebar_position: 1\n---\n\n在您的个人电脑上离线转录和翻译音频。由 OpenAI 的 [Whisper](https://github.com/openai/whisper) 提供支持。\n\n"
  },
  {
    "path": "docs/i18n/zh/docusaurus-plugin-content-docs/current/installation.md",
    "chars": 894,
    "preview": "---\ntitle: 安装\nsidebar_position: 2\n---\n\n要安装 Buzz,请下载适用于您操作系统的[最新版本](https://github.com/chidiwilliams/buzz/releases/latest"
  },
  {
    "path": "docs/i18n/zh/docusaurus-plugin-content-docs/current/preferences.md",
    "chars": 3395,
    "preview": "---\ntitle: 偏好设置\nsidebar_position: 4\n---\n\n从菜单栏打开偏好设置窗口,或点击 `Ctrl/Cmd + ,`。\n\n## 常规偏好设置\n\n### OpenAI API 偏好设置\n\n**API 密钥** - "
  },
  {
    "path": "docs/i18n/zh/docusaurus-plugin-content-docs/current/usage/1_file_import.md",
    "chars": 1121,
    "preview": "---\ntitle: 文件导入\n---\n\n若要导入文件:\n\n- 点击“文件”菜单中的“导入媒体文件”(或者点击工具栏上的“+”图标,也可以使用快捷键 **Command/Ctrl + O**)。\n- 选择一个音频或视频文件。\n- 选择任务、"
  },
  {
    "path": "docs/i18n/zh/docusaurus-plugin-content-docs/current/usage/2_live_recording.md",
    "chars": 4217,
    "preview": "---\ntitle: 实时录制\n---\n\n若要开始实时录制,请按以下步骤操作:\n\n- 选择录制任务、语言、质量和麦克风。\n- 点击“录制”。\n\n> **注意:** 使用默认的 Whisper 模型转录音频会占用大量系统资源。若想实现实时性能"
  },
  {
    "path": "docs/i18n/zh/docusaurus-plugin-content-docs/current/usage/3_translations.md",
    "chars": 846,
    "preview": "---\ntitle: 翻译功能\n---\n\n默认的“翻译”任务借助 Whisper 模型将内容翻译成英语。从 `1.0.0` 版本开始,Buzz 支持使用其他人工智能将内容翻译成任意语言。\n\n若要使用翻译功能,你需要配置 OpenAI API"
  },
  {
    "path": "docs/i18n/zh/docusaurus-plugin-content-docs/current/usage/4_edit_and_resize.md",
    "chars": 339,
    "preview": "---\ntitle: 编辑与调整\n---\n\n当某个音频或视频文件完成转录后,你可以对其进行编辑,并将其导出为不同的字幕格式或纯文本。在转录列表中双击转录内容,即可查看用于编辑和导出的其他选项。\n\n转录视图界面提供了调整转录内容的选项。点击“"
  },
  {
    "path": "docs/i18n/zh/docusaurus-plugin-content-docs/current/usage/_category_.yml",
    "chars": 24,
    "preview": "label: 使用方法\nposition: 3\n"
  },
  {
    "path": "docs/package.json",
    "chars": 1113,
    "preview": "{\n  \"name\": \"docs\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"docusaurus\": \"docusaurus\",\n    \"start\":"
  },
  {
    "path": "docs/sidebars.js",
    "chars": 781,
    "preview": "/**\n * Creating a sidebar enables you to:\n - create an ordered group of docs\n - render a sidebar for each doc of that gr"
  },
  {
    "path": "docs/src/css/custom.css",
    "chars": 1042,
    "preview": "/**\n * Any CSS included here will be global. The classic template\n * bundles Infima by default. Infima is a CSS framewor"
  },
  {
    "path": "docs/src/pages/index.module.css",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "docs/src/pages/index.tsx",
    "chars": 346,
    "preview": "import React from 'react';\nimport clsx from 'clsx';\nimport Link from '@docusaurus/Link';\nimport useDocusaurusContext fro"
  },
  {
    "path": "docs/static/.nojekyll",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "docs/tsconfig.json",
    "chars": 190,
    "preview": "{\n  // This file is not used in compilation. It is here just for a nice editor experience.\n  \"extends\": \"@tsconfig/docus"
  },
  {
    "path": "entitlements.plist",
    "chars": 540,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "flatpak/run-buzz.sh",
    "chars": 94,
    "preview": "#!/bin/sh\necho \"Running buzz...\"\necho \"Note: ffmpeg errors are safe to ignore\"\n\npython -m buzz"
  },
  {
    "path": "hatch_build.py",
    "chars": 10451,
    "preview": "\"\"\"Custom build hook for hatchling to build whisper.cpp binaries.\"\"\"\nimport glob\nimport subprocess\nimport sys\nfrom pathl"
  },
  {
    "path": "installer.iss",
    "chars": 2879,
    "preview": "; Script generated by the Inno Setup Script Wizard.\n; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FI"
  },
  {
    "path": "main.py",
    "chars": 66,
    "preview": "import buzz.buzz\n\nif __name__ == \"__main__\":\n    buzz.buzz.main()\n"
  },
  {
    "path": "msgfmt.py",
    "chars": 1581,
    "preview": "#!/usr/bin/env python\n\"\"\"\nGenerate binary message catalog from textual translation description.\n\nThis program converts a"
  },
  {
    "path": "patches/ctc_forced_aligner_windows_mutex.patch",
    "chars": 688,
    "preview": "diff --git a/setup.py b/setup.py\nindex de84a25..386f662 100644\n--- a/setup.py\n+++ b/setup.py\n@@ -6,7 +6,10 @@ ext_module"
  },
  {
    "path": "pyproject.toml",
    "chars": 6177,
    "preview": "[project]\nname = \"buzz-captions\"\n# Change also in Makefile and buzz/__version__.py\nversion = \"1.4.4\"\ndescription = \"\"\nau"
  },
  {
    "path": "pytest.ini",
    "chars": 330,
    "preview": "[pytest]\nlog_cli = 1\nlog_cli_level = DEBUG\nqt_api=pyqt6\nlog_format = %(asctime)s %(levelname)s %(module)s::%(funcName)s "
  },
  {
    "path": "readme/README.zh_CN.md",
    "chars": 1547,
    "preview": "[[English](../README.md)] <- Click here to View the English page.\n\n# Buzz\n\n[项目文档](https://chidiwilliams.github.io/buzz/z"
  },
  {
    "path": "share/applications/buzz.desktop",
    "chars": 207,
    "preview": "[Desktop Entry]\n\nType=Application\n\nEncoding=UTF-8\n\nName=Buzz\n\nComment=Buzz transcribes and translates audio offline on y"
  },
  {
    "path": "share/applications/io.github.chidiwilliams.Buzz.desktop",
    "chars": 170,
    "preview": "[Desktop Entry]\n\nType=Application\n\nEncoding=UTF-8\n\nName=Buzz\n\nComment=Transcribe and translate audio\n\nExec=run-buzz.sh\n\n"
  },
  {
    "path": "share/metainfo/io.github.chidiwilliams.Buzz.metainfo.xml",
    "chars": 7607,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<component type=\"desktop-application\">\n  <id>io.github.chidiwilliams.Buzz</id>\n\n "
  },
  {
    "path": "snap/snapcraft.yaml",
    "chars": 6369,
    "preview": "# Development notes:\n# - To build the snap run `snapcraft clean` and `snapcraft pack --verbose`\n# - To install local sna"
  },
  {
    "path": "tests/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/app_main.py",
    "chars": 853,
    "preview": "import os\nfrom unittest.mock import patch\nfrom buzz.buzz import main\n\nclass TestMain:\n    def test_main(self):\n        w"
  },
  {
    "path": "tests/audio.py",
    "chars": 262,
    "preview": "import os.path\n\ntest_audio_path = os.path.abspath(\n    os.path.join(os.path.dirname(__file__), \"../testdata/whisper-fren"
  },
  {
    "path": "tests/cache_test.py",
    "chars": 994,
    "preview": "from buzz.cache import TasksCache\nfrom buzz.transcriber.transcriber import (\n    FileTranscriptionOptions,\n    FileTrans"
  },
  {
    "path": "tests/cli_test.py",
    "chars": 1049,
    "preview": "import os\nimport sys\nfrom tempfile import mkdtemp\n\nimport pytest\nfrom pytestqt.qtbot import QtBot\n\nfrom buzz.cli import "
  },
  {
    "path": "tests/conftest.py",
    "chars": 2239,
    "preview": "import multiprocessing\nimport os\nimport platform\nimport random\nimport string\n\nimport pytest\n\n# Set multiprocessing to us"
  },
  {
    "path": "tests/db/dao/transcription_dao_test.py",
    "chars": 9731,
    "preview": "import pytest\nfrom unittest.mock import Mock, patch\nfrom uuid import UUID, uuid4\nfrom PyQt6.QtSql import QSqlDatabase, Q"
  },
  {
    "path": "tests/db/entity/transcription_test.py",
    "chars": 10031,
    "preview": "import pytest\nfrom uuid import uuid4\n\nfrom buzz.db.entity.transcription import Transcription\n\n\nclass TestTranscription:\n"
  },
  {
    "path": "tests/db/service/transcription_service_test.py",
    "chars": 10100,
    "preview": "import pytest\nfrom unittest.mock import Mock, patch\nfrom uuid import UUID, uuid4\n\nfrom buzz.db.service.transcription_ser"
  }
]

// ... and 54 more files (download for full content)

About this extraction

This page contains the full source code of the chidiwilliams/buzz GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 254 files (1.9 MB), approximately 462.2k tokens, and a symbol index with 1925 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!