diff --git a/README.md b/README.md index da0ba75..04683fd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ccn-transcribe +# audio-scribe Download a video, extract its audio losslessly as FLAC, and transcribe it with Whisper on whatever GPU the machine actually has. @@ -7,9 +7,9 @@ Resumable from any completed stage, with errors that tell you what to do next. ```bash uv sync -uv run ccn-transcribe doctor # check the environment first -uv run ccn-transcribe run 'https://youtu.be/...' -uv run ccn-transcribe run -i urls.txt --formats txt,srt +uv run audio-scribe doctor # check the environment first +uv run audio-scribe run 'https://youtu.be/...' +uv run audio-scribe run -i urls.txt --formats txt,srt ``` Hardware setup for the Intel path is documented separately in @@ -163,13 +163,13 @@ under cron without any PATH setup. ## Installing -Either route puts `ccn-transcribe` on your PATH; `ccn-transcribe doctor` tells you +Either route puts `audio-scribe` on your PATH; `audio-scribe doctor` tells you whether the machine can actually run it. **As a tool** (needs uv; tracks nothing but what it installed): ```bash -uv tool install . # then: ccn-transcribe doctor +uv tool install . # then: audio-scribe doctor uv tool install . --reinstall # pick up later commits uv tool install . --editable # or track the checkout instead ``` @@ -177,12 +177,12 @@ uv tool install . --editable # or track the checkout instead **As a self-contained directory** (needs neither Python nor uv at runtime): ```bash -scripts/build-binary.sh # then: dist/ccn-transcribe/ccn-transcribe doctor +scripts/build-binary.sh # then: dist/audio-scribe/audio-scribe doctor ``` ~450 MB, most of it OpenVINO and its 47 runtime-loaded plugins. It is `onedir` rather than `onefile` on purpose: `onefile` extracts the whole bundle to `/tmp` -on every launch. Move or copy the whole `ccn-transcribe/` directory, not just +on every launch. Move or copy the whole `audio-scribe/` directory, not just the executable inside it. `ffmpeg`, `ffprobe` and `aria2c` are still expected on the system either way — they are not bundled. @@ -196,6 +196,6 @@ uv run pytest Commits run ruff, pyright and the full suite at 100% coverage; pushes run pylint and mypy. No unit test touches the GPU, the network or a real model, which is -what keeps the commit gate fast. Live checks sit behind `CCN_LIVE=1`. +what keeps the commit gate fast. Live checks sit behind `AUDIO_SCRIBE_LIVE=1`. Conventional commits are enforced. diff --git a/pyproject.toml b/pyproject.toml index 007d690..4a28e68 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["hatchling"] build-backend = "hatchling.build" [project] -name = "ccn-transcribe" +name = "audio-scribe" version = "0.1.0" description = "Download videos, extract FLAC, and transcribe with Whisper on whatever GPU is present." requires-python = ">=3.12" @@ -18,7 +18,7 @@ dependencies = [ ] [project.scripts] -ccn-transcribe = "ccn_transcribe.cli:main" +audio-scribe = "audio_scribe.cli:main" [dependency-groups] dev = [ @@ -32,7 +32,7 @@ dev = [ ] [tool.hatch.build.targets.wheel] -packages = ["src/ccn_transcribe"] +packages = ["src/audio_scribe"] [tool.ruff] line-length = 100 @@ -92,7 +92,7 @@ addopts = "-q" [tool.coverage.run] branch = true -source = ["src/ccn_transcribe"] +source = ["src/audio_scribe"] [tool.coverage.report] fail_under = 100 diff --git a/scripts/build-binary.sh b/scripts/build-binary.sh index 3c30f4f..550bd36 100755 --- a/scripts/build-binary.sh +++ b/scripts/build-binary.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Build a self-contained ccn-transcribe directory that runs without Python or uv. +# Build a self-contained audio-scribe directory that runs without Python or uv. # # onedir, never onefile: onefile extracts ~450 MB to /tmp on every launch. # deno lands in bin/ because PyInstaller points sys.prefix at the bundle, which @@ -10,7 +10,7 @@ cd "$(dirname "$0")/.." DIST=${1:-dist} uv run --locked --with pyinstaller pyinstaller \ - --noconfirm --onedir --name ccn-transcribe \ + --noconfirm --onedir --name audio-scribe \ --distpath "$DIST" --workpath "build/pyinstaller" --specpath "build" \ --collect-all openvino \ --collect-all openvino_genai \ @@ -18,8 +18,8 @@ uv run --locked --with pyinstaller pyinstaller \ --collect-all yt_dlp \ --collect-all yt_dlp_ejs \ --add-binary "$PWD/.venv/bin/deno:bin" \ - src/ccn_transcribe/__main__.py + src/audio_scribe/__main__.py echo -echo "built $DIST/ccn-transcribe ($(du -sh "$DIST/ccn-transcribe" | cut -f1))" -echo "verify it with: $DIST/ccn-transcribe/ccn-transcribe doctor" +echo "built $DIST/audio-scribe ($(du -sh "$DIST/audio-scribe" | cut -f1))" +echo "verify it with: $DIST/audio-scribe/audio-scribe doctor" diff --git a/src/ccn_transcribe/__init__.py b/src/audio_scribe/__init__.py similarity index 100% rename from src/ccn_transcribe/__init__.py rename to src/audio_scribe/__init__.py diff --git a/src/ccn_transcribe/__main__.py b/src/audio_scribe/__main__.py similarity index 88% rename from src/ccn_transcribe/__main__.py rename to src/audio_scribe/__main__.py index e332f3f..a37f624 100644 --- a/src/ccn_transcribe/__main__.py +++ b/src/audio_scribe/__main__.py @@ -1,8 +1,8 @@ -"""python -m ccn_transcribe""" +"""python -m audio_scribe""" from __future__ import annotations -from ccn_transcribe.cli import main +from audio_scribe.cli import main __all__ = ["main"] diff --git a/src/ccn_transcribe/backends/__init__.py b/src/audio_scribe/backends/__init__.py similarity index 100% rename from src/ccn_transcribe/backends/__init__.py rename to src/audio_scribe/backends/__init__.py diff --git a/src/ccn_transcribe/backends/amd_backend.py b/src/audio_scribe/backends/amd_backend.py similarity index 85% rename from src/ccn_transcribe/backends/amd_backend.py rename to src/audio_scribe/backends/amd_backend.py index dabbd28..8acf129 100644 --- a/src/ccn_transcribe/backends/amd_backend.py +++ b/src/audio_scribe/backends/amd_backend.py @@ -13,9 +13,9 @@ from __future__ import annotations from typing import TYPE_CHECKING, ClassVar -from ccn_transcribe import errors -from ccn_transcribe.backends import probe -from ccn_transcribe.backends.base import ToolchainStatus +from audio_scribe import errors +from audio_scribe.backends import probe +from audio_scribe.backends.base import ToolchainStatus if TYPE_CHECKING: from pathlib import Path @@ -23,12 +23,12 @@ if TYPE_CHECKING: import numpy as np import numpy.typing as npt - from ccn_transcribe.backends.base import TranscribeRequest - from ccn_transcribe.transcript import TranscriptResult + from audio_scribe.backends.base import TranscribeRequest + from audio_scribe.transcript import TranscriptResult _NOT_IMPLEMENTED = "an AMD GPU was detected, but the AMD backend is not implemented in this build" _HINT = ( - "Implement src/ccn_transcribe/backends/amd_backend.py using transformers with a " + "Implement src/audio_scribe/backends/amd_backend.py using transformers with a " "ROCm build of torch (pip --index-url https://download.pytorch.org/whl/rocm6.2). " "The cached OpenVINO IR model cannot be loaded on ROCm." ) diff --git a/src/ccn_transcribe/backends/base.py b/src/audio_scribe/backends/base.py similarity index 95% rename from src/ccn_transcribe/backends/base.py rename to src/audio_scribe/backends/base.py index 63f9b1c..071e635 100644 --- a/src/ccn_transcribe/backends/base.py +++ b/src/audio_scribe/backends/base.py @@ -16,7 +16,7 @@ if TYPE_CHECKING: import numpy as np import numpy.typing as npt - from ccn_transcribe.transcript import TranscriptResult + from audio_scribe.transcript import TranscriptResult @dataclass(frozen=True, slots=True) diff --git a/src/ccn_transcribe/backends/nvidia_backend.py b/src/audio_scribe/backends/nvidia_backend.py similarity index 85% rename from src/ccn_transcribe/backends/nvidia_backend.py rename to src/audio_scribe/backends/nvidia_backend.py index 962645b..cc76836 100644 --- a/src/ccn_transcribe/backends/nvidia_backend.py +++ b/src/audio_scribe/backends/nvidia_backend.py @@ -13,9 +13,9 @@ from __future__ import annotations from typing import TYPE_CHECKING, ClassVar -from ccn_transcribe import errors -from ccn_transcribe.backends import probe -from ccn_transcribe.backends.base import ToolchainStatus +from audio_scribe import errors +from audio_scribe.backends import probe +from audio_scribe.backends.base import ToolchainStatus if TYPE_CHECKING: from pathlib import Path @@ -23,14 +23,14 @@ if TYPE_CHECKING: import numpy as np import numpy.typing as npt - from ccn_transcribe.backends.base import TranscribeRequest - from ccn_transcribe.transcript import TranscriptResult + from audio_scribe.backends.base import TranscribeRequest + from audio_scribe.transcript import TranscriptResult _NOT_IMPLEMENTED = ( "an NVIDIA GPU was detected, but the NVIDIA backend is not implemented in this build" ) _HINT = ( - "Implement src/ccn_transcribe/backends/nvidia_backend.py using faster-whisper " + "Implement src/audio_scribe/backends/nvidia_backend.py using faster-whisper " "(CTranslate2) with a CTranslate2 model. The cached OpenVINO IR model cannot " "be loaded on CUDA." ) diff --git a/src/ccn_transcribe/backends/openvino_backend.py b/src/audio_scribe/backends/openvino_backend.py similarity index 94% rename from src/ccn_transcribe/backends/openvino_backend.py rename to src/audio_scribe/backends/openvino_backend.py index 0fef42c..8f83094 100644 --- a/src/ccn_transcribe/backends/openvino_backend.py +++ b/src/audio_scribe/backends/openvino_backend.py @@ -13,16 +13,16 @@ import time from pathlib import Path from typing import TYPE_CHECKING, Any, ClassVar -from ccn_transcribe import errors -from ccn_transcribe.backends import probe -from ccn_transcribe.backends.base import ToolchainStatus -from ccn_transcribe.transcript import TranscriptResult, normalize_segments +from audio_scribe import errors +from audio_scribe.backends import probe +from audio_scribe.backends.base import ToolchainStatus +from audio_scribe.transcript import TranscriptResult, normalize_segments if TYPE_CHECKING: import numpy as np import numpy.typing as npt - from ccn_transcribe.backends.base import TranscribeRequest + from audio_scribe.backends.base import TranscribeRequest log = logging.getLogger(__name__) @@ -33,7 +33,7 @@ SAMPLE_RATE = 16_000 def cache_root() -> Path: base = os.environ.get("XDG_CACHE_HOME") or str(Path.home() / ".cache") - return Path(base) / "ccn-transcribe" / "ov_cache" + return Path(base) / "audio-scribe" / "ov_cache" def language_token(code: str | None, allowed: set[str] | None = None) -> str | None: @@ -153,14 +153,14 @@ class _OpenvinoBackend: def load_hint(self) -> str: if self.device != "GPU": - return "Try --device cpu, or run `ccn-transcribe doctor`." + return "Try --device cpu, or run `audio-scribe doctor`." nodes = [n for n in probe.render_nodes() if n.vendor == "intel"] if nodes and not nodes[0].writable: return ( f"{nodes[0].path} is not writable by this user. " "Run: sudo usermod -aG render $USER, then log out and back in." ) - return "Run `ccn-transcribe doctor` to separate driver problems from plugin ones." + return "Run `audio-scribe doctor` to separate driver problems from plugin ones." def transcribe( self, pcm: npt.NDArray[np.float32], request: TranscribeRequest diff --git a/src/ccn_transcribe/backends/probe.py b/src/audio_scribe/backends/probe.py similarity index 100% rename from src/ccn_transcribe/backends/probe.py rename to src/audio_scribe/backends/probe.py diff --git a/src/ccn_transcribe/backends/registry.py b/src/audio_scribe/backends/registry.py similarity index 92% rename from src/ccn_transcribe/backends/registry.py rename to src/audio_scribe/backends/registry.py index 4ca615d..cd42a65 100644 --- a/src/ccn_transcribe/backends/registry.py +++ b/src/audio_scribe/backends/registry.py @@ -5,10 +5,10 @@ from __future__ import annotations import logging from typing import TYPE_CHECKING -from ccn_transcribe import errors -from ccn_transcribe.backends.amd_backend import AmdBackend -from ccn_transcribe.backends.nvidia_backend import NvidiaBackend -from ccn_transcribe.backends.openvino_backend import OpenvinoCpuBackend, OpenvinoGpuBackend +from audio_scribe import errors +from audio_scribe.backends.amd_backend import AmdBackend +from audio_scribe.backends.nvidia_backend import NvidiaBackend +from audio_scribe.backends.openvino_backend import OpenvinoCpuBackend, OpenvinoGpuBackend if TYPE_CHECKING: from collections.abc import Iterator @@ -17,8 +17,8 @@ if TYPE_CHECKING: import numpy as np import numpy.typing as npt - from ccn_transcribe.backends.base import Backend, TranscribeRequest - from ccn_transcribe.transcript import TranscriptResult + from audio_scribe.backends.base import Backend, TranscribeRequest + from audio_scribe.transcript import TranscriptResult log = logging.getLogger(__name__) @@ -172,5 +172,5 @@ def transcribe_with_fallback( ) raise errors.TranscribeError( "no usable backend remains", - hint=str(last) if last else "Run `ccn-transcribe doctor` to see what was detected.", + hint=str(last) if last else "Run `audio-scribe doctor` to see what was detected.", ) from last diff --git a/src/ccn_transcribe/cli.py b/src/audio_scribe/cli.py similarity index 93% rename from src/ccn_transcribe/cli.py rename to src/audio_scribe/cli.py index 17eea49..3282f66 100644 --- a/src/ccn_transcribe/cli.py +++ b/src/audio_scribe/cli.py @@ -8,14 +8,14 @@ from typing import Any, NoReturn import click -from ccn_transcribe import doctor as doctor_mod -from ccn_transcribe import errors, logsetup, paths -from ccn_transcribe.config import DEFAULT_FORMATS, RunConfig -from ccn_transcribe.formats import FORMATS -from ccn_transcribe.jobs import batch, store -from ccn_transcribe.jobs.plan import Stage -from ccn_transcribe.media import ffmpeg, ytdlp_opts -from ccn_transcribe.stages import download as download_stage +from audio_scribe import doctor as doctor_mod +from audio_scribe import errors, logsetup, paths +from audio_scribe.config import DEFAULT_FORMATS, RunConfig +from audio_scribe.formats import FORMATS +from audio_scribe.jobs import batch, store +from audio_scribe.jobs.plan import Stage +from audio_scribe.media import ffmpeg, ytdlp_opts +from audio_scribe.stages import download as download_stage STAGE_NAMES = [s.value for s in Stage] @@ -78,7 +78,7 @@ def build_config(**kw: Any) -> RunConfig: @click.group(context_settings={"help_option_names": ["-h", "--help"]}) -@click.version_option(package_name="ccn-transcribe") +@click.version_option(package_name="audio-scribe") def main() -> None: """Download videos, extract FLAC, and transcribe with Whisper.""" @@ -177,7 +177,7 @@ def run_command(urls: tuple[str, ...], input_files: tuple[Path, ...], **kw: Any) raise errors.ConfigError("no URLs given", hint="Pass a URL or -i urls.txt.") batch.confirm_retention(config) result = batch.run_batch(targets, config, dry_run=bool(kw["dry_run"])) - except errors.CcnError as exc: + except errors.AudioScribeError as exc: _fail(exc, verbose=bool(kw["verbose"])) click.echo(result.summary()) sys.exit(result.exit_code) @@ -213,7 +213,7 @@ def status_command(job_ids: tuple[str, ...], workdir: Path) -> None: ) -def _fail(exc: errors.CcnError, *, verbose: bool) -> NoReturn: +def _fail(exc: errors.AudioScribeError, *, verbose: bool) -> NoReturn: click.echo(f"ERROR: {exc}", err=True) if exc.hint: click.echo(f"hint: {exc.hint}", err=True) diff --git a/src/ccn_transcribe/config.py b/src/audio_scribe/config.py similarity index 86% rename from src/ccn_transcribe/config.py rename to src/audio_scribe/config.py index 69b86a1..77ea509 100644 --- a/src/ccn_transcribe/config.py +++ b/src/audio_scribe/config.py @@ -6,11 +6,11 @@ from dataclasses import dataclass, field, replace from pathlib import Path from typing import TYPE_CHECKING -from ccn_transcribe.jobs.plan import Stage -from ccn_transcribe.media.ytdlp_opts import DEFAULT_DOWNLOAD, DownloadConfig +from audio_scribe.jobs.plan import Stage +from audio_scribe.media.ytdlp_opts import DEFAULT_DOWNLOAD, DownloadConfig if TYPE_CHECKING: - from ccn_transcribe.backends.base import TranscribeRequest + from audio_scribe.backends.base import TranscribeRequest DEFAULT_FORMATS = ("txt", "srt", "vtt", "json") @@ -49,7 +49,7 @@ class RunConfig: return "; ".join(parts) def request(self) -> TranscribeRequest: - from ccn_transcribe.backends.base import ( # pylint: disable=import-outside-toplevel + from audio_scribe.backends.base import ( # pylint: disable=import-outside-toplevel TranscribeRequest, ) diff --git a/src/ccn_transcribe/doctor.py b/src/audio_scribe/doctor.py similarity index 98% rename from src/ccn_transcribe/doctor.py rename to src/audio_scribe/doctor.py index 3b5bb63..aaeddd0 100644 --- a/src/ccn_transcribe/doctor.py +++ b/src/audio_scribe/doctor.py @@ -13,13 +13,13 @@ from dataclasses import dataclass from enum import StrEnum from pathlib import Path -from ccn_transcribe.backends import probe -from ccn_transcribe.backends.openvino_backend import ( +from audio_scribe.backends import probe +from audio_scribe.backends.openvino_backend import ( DEFAULT_MODEL, cache_root, snapshot, ) -from ccn_transcribe.media import ffmpeg, ytdlp_opts +from audio_scribe.media import ffmpeg, ytdlp_opts class Status(StrEnum): diff --git a/src/ccn_transcribe/errors.py b/src/audio_scribe/errors.py similarity index 95% rename from src/ccn_transcribe/errors.py rename to src/audio_scribe/errors.py index b8d3abb..3ccc8b2 100644 --- a/src/ccn_transcribe/errors.py +++ b/src/audio_scribe/errors.py @@ -15,8 +15,8 @@ EXIT_FATAL = 2 EXIT_INTERRUPTED = 130 -class CcnError(Exception): - code: ClassVar[str] = "ccn_error" +class AudioScribeError(Exception): + code: ClassVar[str] = "audio_scribe_error" exit_code: ClassVar[int] = EXIT_JOB_FAILED retryable: ClassVar[bool] = False @@ -29,7 +29,7 @@ class CcnError(Exception): return self.message -class FatalError(CcnError): +class FatalError(AudioScribeError): """Aborts the run. Continuing would only produce more bad artifacts.""" code: ClassVar[str] = "fatal" @@ -52,7 +52,7 @@ class SchemaTooNewError(FatalError): code: ClassVar[str] = "schema_too_new" -class JobError(CcnError): +class JobError(AudioScribeError): """Scoped to one job; the batch continues.""" code: ClassVar[str] = "job" diff --git a/src/ccn_transcribe/formats.py b/src/audio_scribe/formats.py similarity index 97% rename from src/ccn_transcribe/formats.py rename to src/audio_scribe/formats.py index 67f26db..dedcc42 100644 --- a/src/ccn_transcribe/formats.py +++ b/src/audio_scribe/formats.py @@ -12,7 +12,7 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: from collections.abc import Iterable, Mapping, Sequence - from ccn_transcribe.transcript import Segment, TranscriptResult + from audio_scribe.transcript import Segment, TranscriptResult FORMATS = ("txt", "srt", "vtt", "json") diff --git a/src/ccn_transcribe/jobs/__init__.py b/src/audio_scribe/jobs/__init__.py similarity index 100% rename from src/ccn_transcribe/jobs/__init__.py rename to src/audio_scribe/jobs/__init__.py diff --git a/src/ccn_transcribe/jobs/batch.py b/src/audio_scribe/jobs/batch.py similarity index 93% rename from src/ccn_transcribe/jobs/batch.py rename to src/audio_scribe/jobs/batch.py index dcf9f14..4600a11 100644 --- a/src/ccn_transcribe/jobs/batch.py +++ b/src/audio_scribe/jobs/batch.py @@ -7,18 +7,18 @@ import sys from dataclasses import dataclass, field from typing import TYPE_CHECKING, TextIO -from ccn_transcribe import errors, logsetup, paths -from ccn_transcribe.backends.registry import BackendHolder -from ccn_transcribe.jobs import index, store -from ccn_transcribe.jobs.index import Target -from ccn_transcribe.jobs.runner import JobOutcome, run_job -from ccn_transcribe.stages import download as download_stage +from audio_scribe import errors, logsetup, paths +from audio_scribe.backends.registry import BackendHolder +from audio_scribe.jobs import index, store +from audio_scribe.jobs.index import Target +from audio_scribe.jobs.runner import JobOutcome, run_job +from audio_scribe.stages import download as download_stage if TYPE_CHECKING: from collections.abc import Sequence from pathlib import Path - from ccn_transcribe.config import RunConfig + from audio_scribe.config import RunConfig log = logging.getLogger(__name__) @@ -151,7 +151,7 @@ def _run_one( targets = expand(url, config, workspace, remember=not dry_run) except errors.FatalError: raise - except errors.CcnError as exc: + except errors.AudioScribeError as exc: log.error("%s: %s", url, exc) result.outcomes.append(JobOutcome(url, url, (), ok=False, error=str(exc))) return @@ -167,7 +167,7 @@ def _run_one( ) except errors.FatalError: raise - except errors.CcnError as exc: + except errors.AudioScribeError as exc: # One bad job must never take the batch down with it. log.error("%s: %s", job.root.name, exc) result.outcomes.append( diff --git a/src/ccn_transcribe/jobs/index.py b/src/audio_scribe/jobs/index.py similarity index 97% rename from src/ccn_transcribe/jobs/index.py rename to src/audio_scribe/jobs/index.py index bb2e7bc..65e8c16 100644 --- a/src/ccn_transcribe/jobs/index.py +++ b/src/audio_scribe/jobs/index.py @@ -13,8 +13,8 @@ import logging from dataclasses import dataclass, field from typing import TYPE_CHECKING, Any, cast -from ccn_transcribe import paths -from ccn_transcribe.jobs import store +from audio_scribe import paths +from audio_scribe.jobs import store if TYPE_CHECKING: from collections.abc import Mapping, Sequence diff --git a/src/ccn_transcribe/jobs/plan.py b/src/audio_scribe/jobs/plan.py similarity index 97% rename from src/ccn_transcribe/jobs/plan.py rename to src/audio_scribe/jobs/plan.py index 3d2168b..0f23469 100644 --- a/src/ccn_transcribe/jobs/plan.py +++ b/src/audio_scribe/jobs/plan.py @@ -10,7 +10,7 @@ from __future__ import annotations from dataclasses import dataclass from enum import StrEnum -from ccn_transcribe.jobs.verify import Verdict +from audio_scribe.jobs.verify import Verdict class Stage(StrEnum): diff --git a/src/ccn_transcribe/jobs/recover.py b/src/audio_scribe/jobs/recover.py similarity index 91% rename from src/ccn_transcribe/jobs/recover.py rename to src/audio_scribe/jobs/recover.py index 9689ade..8601109 100644 --- a/src/ccn_transcribe/jobs/recover.py +++ b/src/audio_scribe/jobs/recover.py @@ -14,17 +14,17 @@ import json import logging from typing import TYPE_CHECKING, Any -from ccn_transcribe import errors, formats -from ccn_transcribe.jobs import store -from ccn_transcribe.jobs.state import Artifact, ArtifactStatus, JobState, Outputs -from ccn_transcribe.jobs.state import TranscriptionRecord as Record -from ccn_transcribe.media import ffmpeg -from ccn_transcribe.stages import download as download_stage +from audio_scribe import errors, formats +from audio_scribe.jobs import store +from audio_scribe.jobs.state import Artifact, ArtifactStatus, JobState, Outputs +from audio_scribe.jobs.state import TranscriptionRecord as Record +from audio_scribe.media import ffmpeg +from audio_scribe.stages import download as download_stage if TYPE_CHECKING: from pathlib import Path - from ccn_transcribe.paths import JobPaths + from audio_scribe.paths import JobPaths log = logging.getLogger(__name__) diff --git a/src/ccn_transcribe/jobs/runner.py b/src/audio_scribe/jobs/runner.py similarity index 91% rename from src/ccn_transcribe/jobs/runner.py rename to src/audio_scribe/jobs/runner.py index c700634..e093265 100644 --- a/src/ccn_transcribe/jobs/runner.py +++ b/src/audio_scribe/jobs/runner.py @@ -7,23 +7,23 @@ from dataclasses import dataclass from datetime import UTC, datetime from typing import TYPE_CHECKING, Any -from ccn_transcribe import errors -from ccn_transcribe.jobs import recover, store, verify -from ccn_transcribe.jobs.plan import Stage, Verdicts, plan_stages -from ccn_transcribe.jobs.state import Artifact, ArtifactStatus, JobState, Outputs -from ccn_transcribe.jobs.state import TranscriptionRecord as Record -from ccn_transcribe.media import ffmpeg -from ccn_transcribe.stages import audio as audio_stage -from ccn_transcribe.stages import download as download_stage -from ccn_transcribe.stages import outputs as outputs_stage -from ccn_transcribe.stages import transcribe as transcribe_stage +from audio_scribe import errors +from audio_scribe.jobs import recover, store, verify +from audio_scribe.jobs.plan import Stage, Verdicts, plan_stages +from audio_scribe.jobs.state import Artifact, ArtifactStatus, JobState, Outputs +from audio_scribe.jobs.state import TranscriptionRecord as Record +from audio_scribe.media import ffmpeg +from audio_scribe.stages import audio as audio_stage +from audio_scribe.stages import download as download_stage +from audio_scribe.stages import outputs as outputs_stage +from audio_scribe.stages import transcribe as transcribe_stage if TYPE_CHECKING: from pathlib import Path - from ccn_transcribe.backends.registry import BackendHolder - from ccn_transcribe.config import RunConfig - from ccn_transcribe.paths import JobPaths + from audio_scribe.backends.registry import BackendHolder + from audio_scribe.config import RunConfig + from audio_scribe.paths import JobPaths log = logging.getLogger(__name__) @@ -118,7 +118,7 @@ def run_job( _execute(job, url, config, state, stages=stages, holder=holder) except errors.FatalError: raise - except errors.CcnError as exc: + except errors.AudioScribeError as exc: state.last_error = {"stage": "unknown", "message": str(exc)} store.save(job, state) return JobOutcome(job.root.name, url, stages, ok=False, error=str(exc)) @@ -239,7 +239,7 @@ def _do_transcript( def _rerender(job: JobPaths, config: RunConfig, state: JobState) -> dict[str, str]: """Re-render other formats from the saved segments, no model needed.""" - from ccn_transcribe.transcript import ( # pylint: disable=import-outside-toplevel + from audio_scribe.transcript import ( # pylint: disable=import-outside-toplevel TranscriptResult, ) diff --git a/src/ccn_transcribe/jobs/state.py b/src/audio_scribe/jobs/state.py similarity index 97% rename from src/ccn_transcribe/jobs/state.py rename to src/audio_scribe/jobs/state.py index 97f9079..6545595 100644 --- a/src/ccn_transcribe/jobs/state.py +++ b/src/audio_scribe/jobs/state.py @@ -15,7 +15,7 @@ from datetime import UTC, datetime from enum import StrEnum from typing import Any -from ccn_transcribe import errors +from audio_scribe import errors SCHEMA_VERSION = 1 @@ -127,7 +127,7 @@ class JobState: if version > SCHEMA_VERSION: raise errors.SchemaTooNewError( f"state.json is schema v{version}, but this build understands v{SCHEMA_VERSION}", - hint="Upgrade ccn-transcribe, or point --workdir somewhere else.", + hint="Upgrade audio-scribe, or point --workdir somewhere else.", ) artifacts = doc.get("artifacts", {}) record = doc.get("transcription") diff --git a/src/ccn_transcribe/jobs/store.py b/src/audio_scribe/jobs/store.py similarity index 96% rename from src/ccn_transcribe/jobs/store.py rename to src/audio_scribe/jobs/store.py index 0ea5898..4b0ddde 100644 --- a/src/ccn_transcribe/jobs/store.py +++ b/src/audio_scribe/jobs/store.py @@ -10,14 +10,14 @@ from contextlib import contextmanager from datetime import UTC, datetime from typing import TYPE_CHECKING, Any -from ccn_transcribe import errors -from ccn_transcribe.jobs.state import JobState +from audio_scribe import errors +from audio_scribe.jobs.state import JobState if TYPE_CHECKING: from collections.abc import Generator from pathlib import Path - from ccn_transcribe.paths import JobPaths + from audio_scribe.paths import JobPaths log = logging.getLogger(__name__) diff --git a/src/ccn_transcribe/jobs/verify.py b/src/audio_scribe/jobs/verify.py similarity index 95% rename from src/ccn_transcribe/jobs/verify.py rename to src/audio_scribe/jobs/verify.py index 9c0efc3..a6b71e9 100644 --- a/src/ccn_transcribe/jobs/verify.py +++ b/src/audio_scribe/jobs/verify.py @@ -11,15 +11,15 @@ import logging from enum import StrEnum from typing import TYPE_CHECKING -from ccn_transcribe import errors -from ccn_transcribe.jobs.state import Artifact, ArtifactStatus, Outputs -from ccn_transcribe.media import ffmpeg +from audio_scribe import errors +from audio_scribe.jobs.state import Artifact, ArtifactStatus, Outputs +from audio_scribe.media import ffmpeg if TYPE_CHECKING: from collections.abc import Iterator, Sequence from pathlib import Path - from ccn_transcribe.paths import JobPaths + from audio_scribe.paths import JobPaths log = logging.getLogger(__name__) diff --git a/src/ccn_transcribe/logsetup.py b/src/audio_scribe/logsetup.py similarity index 95% rename from src/ccn_transcribe/logsetup.py rename to src/audio_scribe/logsetup.py index 9d701ed..9ac9590 100644 --- a/src/ccn_transcribe/logsetup.py +++ b/src/audio_scribe/logsetup.py @@ -10,9 +10,9 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: from collections.abc import Generator - from ccn_transcribe.paths import JobPaths + from audio_scribe.paths import JobPaths -ROOT = "ccn_transcribe" +ROOT = "audio_scribe" def configure(verbose: int = 0, quiet: bool = False) -> None: diff --git a/src/ccn_transcribe/media/__init__.py b/src/audio_scribe/media/__init__.py similarity index 100% rename from src/ccn_transcribe/media/__init__.py rename to src/audio_scribe/media/__init__.py diff --git a/src/ccn_transcribe/media/ffmpeg.py b/src/audio_scribe/media/ffmpeg.py similarity index 99% rename from src/ccn_transcribe/media/ffmpeg.py rename to src/audio_scribe/media/ffmpeg.py index b609496..a4cf3a3 100644 --- a/src/ccn_transcribe/media/ffmpeg.py +++ b/src/audio_scribe/media/ffmpeg.py @@ -17,7 +17,7 @@ from typing import TYPE_CHECKING, Literal import numpy as np -from ccn_transcribe import errors +from audio_scribe import errors if TYPE_CHECKING: from pathlib import Path diff --git a/src/ccn_transcribe/media/ytdlp_opts.py b/src/audio_scribe/media/ytdlp_opts.py similarity index 99% rename from src/ccn_transcribe/media/ytdlp_opts.py rename to src/audio_scribe/media/ytdlp_opts.py index fe3e649..37589d5 100644 --- a/src/ccn_transcribe/media/ytdlp_opts.py +++ b/src/audio_scribe/media/ytdlp_opts.py @@ -18,7 +18,7 @@ from dataclasses import dataclass from pathlib import Path from typing import TYPE_CHECKING, Any -from ccn_transcribe import errors +from audio_scribe import errors if TYPE_CHECKING: from collections.abc import Sequence diff --git a/src/ccn_transcribe/paths.py b/src/audio_scribe/paths.py similarity index 100% rename from src/ccn_transcribe/paths.py rename to src/audio_scribe/paths.py diff --git a/src/ccn_transcribe/stages/__init__.py b/src/audio_scribe/stages/__init__.py similarity index 100% rename from src/ccn_transcribe/stages/__init__.py rename to src/audio_scribe/stages/__init__.py diff --git a/src/ccn_transcribe/stages/audio.py b/src/audio_scribe/stages/audio.py similarity index 93% rename from src/ccn_transcribe/stages/audio.py rename to src/audio_scribe/stages/audio.py index 8f89733..df05b4f 100644 --- a/src/ccn_transcribe/stages/audio.py +++ b/src/audio_scribe/stages/audio.py @@ -9,13 +9,13 @@ from __future__ import annotations import logging from typing import TYPE_CHECKING -from ccn_transcribe import errors -from ccn_transcribe.media import ffmpeg +from audio_scribe import errors +from audio_scribe.media import ffmpeg if TYPE_CHECKING: from pathlib import Path - from ccn_transcribe.paths import JobPaths + from audio_scribe.paths import JobPaths log = logging.getLogger(__name__) diff --git a/src/ccn_transcribe/stages/download.py b/src/audio_scribe/stages/download.py similarity index 97% rename from src/ccn_transcribe/stages/download.py rename to src/audio_scribe/stages/download.py index 11011c6..3d1e4fd 100644 --- a/src/ccn_transcribe/stages/download.py +++ b/src/audio_scribe/stages/download.py @@ -13,14 +13,14 @@ import time from collections.abc import Callable from typing import TYPE_CHECKING, Any -from ccn_transcribe import errors, paths -from ccn_transcribe.media import ytdlp_opts +from audio_scribe import errors, paths +from audio_scribe.media import ytdlp_opts if TYPE_CHECKING: from collections.abc import Iterator, Sequence from pathlib import Path - from ccn_transcribe.paths import JobPaths + from audio_scribe.paths import JobPaths log = logging.getLogger(__name__) @@ -47,7 +47,7 @@ SOURCE_PATTERNS: tuple[tuple[re.Pattern[str], type[errors.SourceError], str], .. re.compile(r"confirm you.{0,5}re not a bot|not a bot", re.I), errors.BotCheckError, "YouTube bot check. Try --cookies-from-browser, or check that a JS runtime " - "is available: ccn-transcribe doctor", + "is available: audio-scribe doctor", ), ( re.compile( @@ -92,7 +92,7 @@ ARIA2_EXITS: dict[int, tuple[str, str]] = { } -def classify(message: str, job_id: str | None = None) -> errors.CcnError: +def classify(message: str, job_id: str | None = None) -> errors.AudioScribeError: """Map a yt-dlp message onto an actionable error.""" if "no space left" in message.lower(): return errors.DiskFullError(message, hint="Free some space and re-run.") @@ -246,7 +246,7 @@ def download( options = download_options(job, config) make = factory or default_factory - last: errors.CcnError | None = None + last: errors.AudioScribeError | None = None for attempt in range(1, attempts + 1): try: with make(options) as ydl: @@ -281,7 +281,7 @@ def download( raise last or errors.DownloadError("download failed", job_id=job.root.name) -def is_retryable(failure: errors.CcnError) -> bool: +def is_retryable(failure: errors.AudioScribeError) -> bool: # A private video will still be private on the third try, and continuing past # ENOSPC only makes more corrupt artifacts. if isinstance(failure, errors.FatalError): diff --git a/src/ccn_transcribe/stages/outputs.py b/src/audio_scribe/stages/outputs.py similarity index 93% rename from src/ccn_transcribe/stages/outputs.py rename to src/audio_scribe/stages/outputs.py index 10abd53..6fb525e 100644 --- a/src/ccn_transcribe/stages/outputs.py +++ b/src/audio_scribe/stages/outputs.py @@ -7,15 +7,15 @@ import os import shutil from typing import TYPE_CHECKING -from ccn_transcribe import formats -from ccn_transcribe.paths import output_stem +from audio_scribe import formats +from audio_scribe.paths import output_stem if TYPE_CHECKING: from collections.abc import Mapping, Sequence from pathlib import Path - from ccn_transcribe.paths import JobPaths - from ccn_transcribe.transcript import TranscriptResult + from audio_scribe.paths import JobPaths + from audio_scribe.transcript import TranscriptResult log = logging.getLogger(__name__) diff --git a/src/ccn_transcribe/stages/transcribe.py b/src/audio_scribe/stages/transcribe.py similarity index 80% rename from src/ccn_transcribe/stages/transcribe.py rename to src/audio_scribe/stages/transcribe.py index 9bb1bab..29bab94 100644 --- a/src/ccn_transcribe/stages/transcribe.py +++ b/src/audio_scribe/stages/transcribe.py @@ -6,17 +6,17 @@ import json import logging from typing import TYPE_CHECKING -from ccn_transcribe.backends import registry -from ccn_transcribe.media import ffmpeg -from ccn_transcribe.transcript import Segment +from audio_scribe.backends import registry +from audio_scribe.media import ffmpeg +from audio_scribe.transcript import Segment if TYPE_CHECKING: from pathlib import Path - from ccn_transcribe.backends.base import Backend, TranscribeRequest - from ccn_transcribe.backends.registry import BackendHolder - from ccn_transcribe.paths import JobPaths - from ccn_transcribe.transcript import TranscriptResult + from audio_scribe.backends.base import Backend, TranscribeRequest + from audio_scribe.backends.registry import BackendHolder + from audio_scribe.paths import JobPaths + from audio_scribe.transcript import TranscriptResult log = logging.getLogger(__name__) diff --git a/src/ccn_transcribe/transcript.py b/src/audio_scribe/transcript.py similarity index 100% rename from src/ccn_transcribe/transcript.py rename to src/audio_scribe/transcript.py diff --git a/tests/test_backends_openvino.py b/tests/test_backends_openvino.py index f53aa22..6db8d49 100644 --- a/tests/test_backends_openvino.py +++ b/tests/test_backends_openvino.py @@ -10,14 +10,15 @@ import numpy as np import openvino_genai import pytest -from ccn_transcribe import errors -from ccn_transcribe.backends import openvino_backend as ovb -from ccn_transcribe.backends import probe as probe_mod -from ccn_transcribe.backends.base import TranscribeRequest -from ccn_transcribe.media import ffmpeg +from audio_scribe import errors +from audio_scribe.backends import openvino_backend as ovb +from audio_scribe.backends import probe as probe_mod +from audio_scribe.backends.base import TranscribeRequest +from audio_scribe.media import ffmpeg LIVE = pytest.mark.skipif( - not os.environ.get("CCN_LIVE"), reason="set CCN_LIVE=1 to run against the real GPU" + not os.environ.get("AUDIO_SCRIBE_LIVE"), + reason="set AUDIO_SCRIBE_LIVE=1 to run against the real GPU", ) @@ -65,11 +66,11 @@ class TestSupportedLanguages: class TestCacheRoot: def test_respects_xdg_cache_home(self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: monkeypatch.setenv("XDG_CACHE_HOME", str(tmp_path)) - assert ovb.cache_root() == tmp_path / "ccn-transcribe/ov_cache" + assert ovb.cache_root() == tmp_path / "audio-scribe/ov_cache" def test_falls_back_to_home_cache(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("XDG_CACHE_HOME", raising=False) - assert ovb.cache_root() == Path.home() / ".cache/ccn-transcribe/ov_cache" + assert ovb.cache_root() == Path.home() / ".cache/audio-scribe/ov_cache" def test_is_not_relative_to_the_working_directory(self) -> None: # The benchmark scripts use a relative .ov_cache_* and only work from the diff --git a/tests/test_backends_probe.py b/tests/test_backends_probe.py index aff2ef7..fb95c7a 100644 --- a/tests/test_backends_probe.py +++ b/tests/test_backends_probe.py @@ -4,7 +4,7 @@ from typing import TYPE_CHECKING import pytest -from ccn_transcribe.backends import probe +from audio_scribe.backends import probe if TYPE_CHECKING: from pathlib import Path diff --git a/tests/test_backends_registry.py b/tests/test_backends_registry.py index 33d03db..7d67eec 100644 --- a/tests/test_backends_registry.py +++ b/tests/test_backends_registry.py @@ -6,10 +6,10 @@ from typing import TYPE_CHECKING, ClassVar import numpy as np import pytest -from ccn_transcribe import errors -from ccn_transcribe.backends import registry -from ccn_transcribe.backends.base import ToolchainStatus, TranscribeRequest -from ccn_transcribe.transcript import Segment, TranscriptResult +from audio_scribe import errors +from audio_scribe.backends import registry +from audio_scribe.backends.base import ToolchainStatus, TranscribeRequest +from audio_scribe.transcript import Segment, TranscriptResult if TYPE_CHECKING: from pathlib import Path diff --git a/tests/test_backends_stubs.py b/tests/test_backends_stubs.py index 4f28921..6b5a106 100644 --- a/tests/test_backends_stubs.py +++ b/tests/test_backends_stubs.py @@ -3,10 +3,10 @@ from __future__ import annotations import numpy as np import pytest -from ccn_transcribe import errors -from ccn_transcribe.backends.amd_backend import AmdBackend -from ccn_transcribe.backends.base import TranscribeRequest -from ccn_transcribe.backends.nvidia_backend import NvidiaBackend +from audio_scribe import errors +from audio_scribe.backends.amd_backend import AmdBackend +from audio_scribe.backends.base import TranscribeRequest +from audio_scribe.backends.nvidia_backend import NvidiaBackend STUBS = [NvidiaBackend, AmdBackend] diff --git a/tests/test_cli_and_batch.py b/tests/test_cli_and_batch.py index 1bb464a..2739d13 100644 --- a/tests/test_cli_and_batch.py +++ b/tests/test_cli_and_batch.py @@ -8,15 +8,15 @@ from typing import Any import pytest from click.testing import CliRunner -import ccn_transcribe.__main__ as entry -from ccn_transcribe import cli, doctor, errors, logsetup, paths -from ccn_transcribe.config import RunConfig -from ccn_transcribe.jobs import batch, store -from ccn_transcribe.jobs.plan import Stage -from ccn_transcribe.jobs.runner import JobOutcome -from ccn_transcribe.jobs.state import JobState -from ccn_transcribe.media import ytdlp_opts -from ccn_transcribe.stages import download as download_stage +import audio_scribe.__main__ as entry +from audio_scribe import cli, doctor, errors, logsetup, paths +from audio_scribe.config import RunConfig +from audio_scribe.jobs import batch, store +from audio_scribe.jobs.plan import Stage +from audio_scribe.jobs.runner import JobOutcome +from audio_scribe.jobs.state import JobState +from audio_scribe.media import ytdlp_opts +from audio_scribe.stages import download as download_stage class TestReadUrls: @@ -246,7 +246,7 @@ class TestRunBatch: def fake_run(job: Any, url: str, *_a: object, **_k: object) -> JobOutcome: return JobOutcome(job.root.name, url, (), ok=True) - monkeypatch.setattr("ccn_transcribe.jobs.batch.run_job", fake_run) + monkeypatch.setattr("audio_scribe.jobs.batch.run_job", fake_run) result = batch.run_batch( ["https://y.test/bad", "https://y.test/good"], self._config(tmp_path) ) @@ -274,7 +274,7 @@ class TestRunBatch: raise errors.MediaError("no audio") monkeypatch.setattr(download_stage, "extract_info", info) - monkeypatch.setattr("ccn_transcribe.jobs.batch.run_job", boom) + monkeypatch.setattr("audio_scribe.jobs.batch.run_job", boom) result = batch.run_batch(["u"], self._config(tmp_path)) assert result.failed[0].error == "no audio" @@ -295,7 +295,7 @@ class TestLogSetup: job.ensure() before = len(logging.getLogger(logsetup.ROOT).handlers) with logsetup.job_log(job): - logging.getLogger("ccn_transcribe.test").info("hello") + logging.getLogger("audio_scribe.test").info("hello") assert "hello" in (job.logs_dir / "job.log").read_text() # A 200-URL batch must not leak 200 open handlers. assert len(logging.getLogger(logsetup.ROOT).handlers) == before @@ -502,7 +502,7 @@ def test_a_fatal_error_inside_a_job_stops_the_batch( raise errors.DiskFullError("no space") monkeypatch.setattr(download_stage, "extract_info", info) - monkeypatch.setattr("ccn_transcribe.jobs.batch.run_job", boom) + monkeypatch.setattr("audio_scribe.jobs.batch.run_job", boom) with pytest.raises(errors.DiskFullError): batch.run_batch(["u"], RunConfig(workdir=tmp_path / "t")) @@ -520,7 +520,7 @@ def test_an_error_without_a_hint_prints_only_the_message( def test_module_entrypoint_is_the_cli() -> None: - # python -m ccn_transcribe must reach the same command group, and the + # python -m audio_scribe must reach the same command group, and the # __main__ guard must be present: Python 3.14's forkserver re-imports # __main__, and without the guard the program runs a second copy of itself. assert entry.main is cli.main diff --git a/tests/test_doctor_failures.py b/tests/test_doctor_failures.py index 14c047f..d0153e6 100644 --- a/tests/test_doctor_failures.py +++ b/tests/test_doctor_failures.py @@ -11,9 +11,9 @@ from typing import TYPE_CHECKING, Any, ClassVar, NamedTuple import openvino as ov -from ccn_transcribe import doctor -from ccn_transcribe.backends import probe -from ccn_transcribe.media import ffmpeg, ytdlp_opts +from audio_scribe import doctor +from audio_scribe.backends import probe +from audio_scribe.media import ffmpeg, ytdlp_opts if TYPE_CHECKING: from collections.abc import Callable diff --git a/tests/test_errors.py b/tests/test_errors.py index ec7f5e4..894a4be 100644 --- a/tests/test_errors.py +++ b/tests/test_errors.py @@ -2,25 +2,25 @@ from __future__ import annotations import pytest -from ccn_transcribe import errors +from audio_scribe import errors def test_base_carries_message_and_hint() -> None: - err = errors.CcnError("something broke", hint="try this") + err = errors.AudioScribeError("something broke", hint="try this") assert str(err) == "something broke" assert err.hint == "try this" assert err.exit_code == errors.EXIT_JOB_FAILED def test_hint_defaults_to_none() -> None: - assert errors.CcnError("bare").hint is None + assert errors.AudioScribeError("bare").hint is None @pytest.mark.parametrize( "cls", [errors.ConfigError, errors.PreflightError, errors.DiskFullError, errors.SchemaTooNewError], ) -def test_fatal_errors_abort_the_run(cls: type[errors.CcnError]) -> None: +def test_fatal_errors_abort_the_run(cls: type[errors.AudioScribeError]) -> None: err = cls("nope") assert err.exit_code == errors.EXIT_FATAL assert isinstance(err, errors.FatalError) @@ -63,7 +63,9 @@ def test_only_network_errors_are_retryable() -> None: def test_every_error_class_declares_a_distinct_code() -> None: classes = [ - v for v in vars(errors).values() if isinstance(v, type) and issubclass(v, errors.CcnError) + v + for v in vars(errors).values() + if isinstance(v, type) and issubclass(v, errors.AudioScribeError) ] codes = [c.code for c in classes] assert len(codes) == len(set(codes)), "duplicate .code values" diff --git a/tests/test_formats.py b/tests/test_formats.py index 15c2e99..9d27918 100644 --- a/tests/test_formats.py +++ b/tests/test_formats.py @@ -4,8 +4,8 @@ import json import pytest -from ccn_transcribe import formats -from ccn_transcribe.transcript import Segment, TranscriptResult +from audio_scribe import formats +from audio_scribe.transcript import Segment, TranscriptResult SEGMENTS = (Segment(0.0, 1.5, "Hello there."), Segment(1.5, 3.25, "General Kenobi.")) diff --git a/tests/test_jobs_index.py b/tests/test_jobs_index.py index 9c0cb91..4adcfe8 100644 --- a/tests/test_jobs_index.py +++ b/tests/test_jobs_index.py @@ -5,8 +5,8 @@ from typing import TYPE_CHECKING import pytest -from ccn_transcribe import paths -from ccn_transcribe.jobs import index +from audio_scribe import paths +from audio_scribe.jobs import index if TYPE_CHECKING: from pathlib import Path diff --git a/tests/test_jobs_plan.py b/tests/test_jobs_plan.py index 61a2cf2..7900210 100644 --- a/tests/test_jobs_plan.py +++ b/tests/test_jobs_plan.py @@ -2,8 +2,8 @@ from __future__ import annotations import pytest -from ccn_transcribe.jobs.plan import Stage, Verdicts, plan_stages -from ccn_transcribe.jobs.verify import Verdict +from audio_scribe.jobs.plan import Stage, Verdicts, plan_stages +from audio_scribe.jobs.verify import Verdict OK = Verdict.OK GONE = Verdict.SATISFIED_ABSENT diff --git a/tests/test_jobs_recover.py b/tests/test_jobs_recover.py index 1c96671..e272cc6 100644 --- a/tests/test_jobs_recover.py +++ b/tests/test_jobs_recover.py @@ -5,10 +5,10 @@ from typing import TYPE_CHECKING import pytest -from ccn_transcribe import paths -from ccn_transcribe.jobs import recover, store -from ccn_transcribe.jobs.state import ArtifactStatus -from ccn_transcribe.media import ffmpeg +from audio_scribe import paths +from audio_scribe.jobs import recover, store +from audio_scribe.jobs.state import ArtifactStatus +from audio_scribe.media import ffmpeg if TYPE_CHECKING: from pathlib import Path diff --git a/tests/test_jobs_runner.py b/tests/test_jobs_runner.py index 6c003fd..cdc391a 100644 --- a/tests/test_jobs_runner.py +++ b/tests/test_jobs_runner.py @@ -6,16 +6,16 @@ from typing import TYPE_CHECKING, ClassVar import pytest -from ccn_transcribe import errors, paths -from ccn_transcribe.backends import registry -from ccn_transcribe.backends.base import ToolchainStatus, TranscribeRequest -from ccn_transcribe.config import RunConfig -from ccn_transcribe.jobs import runner, store -from ccn_transcribe.jobs.plan import Stage, Verdicts -from ccn_transcribe.jobs.state import Artifact, ArtifactStatus, JobState -from ccn_transcribe.jobs.verify import Verdict -from ccn_transcribe.stages import download as download_stage -from ccn_transcribe.transcript import Segment, TranscriptResult +from audio_scribe import errors, paths +from audio_scribe.backends import registry +from audio_scribe.backends.base import ToolchainStatus, TranscribeRequest +from audio_scribe.config import RunConfig +from audio_scribe.jobs import runner, store +from audio_scribe.jobs.plan import Stage, Verdicts +from audio_scribe.jobs.state import Artifact, ArtifactStatus, JobState +from audio_scribe.jobs.verify import Verdict +from audio_scribe.stages import download as download_stage +from audio_scribe.transcript import Segment, TranscriptResult if TYPE_CHECKING: from pathlib import Path diff --git a/tests/test_jobs_state.py b/tests/test_jobs_state.py index 9a1139b..fc883fe 100644 --- a/tests/test_jobs_state.py +++ b/tests/test_jobs_state.py @@ -2,8 +2,8 @@ from __future__ import annotations import pytest -from ccn_transcribe import errors -from ccn_transcribe.jobs import state as job_state +from audio_scribe import errors +from audio_scribe.jobs import state as job_state def make() -> job_state.JobState: diff --git a/tests/test_jobs_store.py b/tests/test_jobs_store.py index ad1a0be..0ec6685 100644 --- a/tests/test_jobs_store.py +++ b/tests/test_jobs_store.py @@ -6,9 +6,9 @@ from typing import TYPE_CHECKING import pytest -from ccn_transcribe import errors, paths -from ccn_transcribe.jobs import state as job_state -from ccn_transcribe.jobs import store +from audio_scribe import errors, paths +from audio_scribe.jobs import state as job_state +from audio_scribe.jobs import store if TYPE_CHECKING: from pathlib import Path diff --git a/tests/test_jobs_verify.py b/tests/test_jobs_verify.py index e2ab4a2..6c4a7b4 100644 --- a/tests/test_jobs_verify.py +++ b/tests/test_jobs_verify.py @@ -4,10 +4,10 @@ from typing import TYPE_CHECKING import pytest -from ccn_transcribe import paths -from ccn_transcribe.jobs import state as job_state -from ccn_transcribe.jobs import verify -from ccn_transcribe.media import ffmpeg +from audio_scribe import paths +from audio_scribe.jobs import state as job_state +from audio_scribe.jobs import verify +from audio_scribe.media import ffmpeg if TYPE_CHECKING: from pathlib import Path diff --git a/tests/test_media_ffmpeg.py b/tests/test_media_ffmpeg.py index af0367a..5a17fac 100644 --- a/tests/test_media_ffmpeg.py +++ b/tests/test_media_ffmpeg.py @@ -7,8 +7,8 @@ from typing import TYPE_CHECKING import numpy as np import pytest -from ccn_transcribe import errors -from ccn_transcribe.media import ffmpeg +from audio_scribe import errors +from audio_scribe.media import ffmpeg if TYPE_CHECKING: from pathlib import Path diff --git a/tests/test_media_ytdlp_opts.py b/tests/test_media_ytdlp_opts.py index 884c6d7..e1055ed 100644 --- a/tests/test_media_ytdlp_opts.py +++ b/tests/test_media_ytdlp_opts.py @@ -6,7 +6,7 @@ from typing import TYPE_CHECKING import pytest -from ccn_transcribe.media import ytdlp_opts as opts +from audio_scribe.media import ytdlp_opts as opts if TYPE_CHECKING: from pathlib import Path diff --git a/tests/test_paths.py b/tests/test_paths.py index 2c4874b..978d0dd 100644 --- a/tests/test_paths.py +++ b/tests/test_paths.py @@ -4,7 +4,7 @@ from typing import TYPE_CHECKING import pytest -from ccn_transcribe import paths +from audio_scribe import paths if TYPE_CHECKING: from pathlib import Path diff --git a/tests/test_stages_download.py b/tests/test_stages_download.py index cac51bf..c306284 100644 --- a/tests/test_stages_download.py +++ b/tests/test_stages_download.py @@ -4,9 +4,9 @@ from typing import TYPE_CHECKING, Any import pytest -from ccn_transcribe import errors, paths -from ccn_transcribe.media import ytdlp_opts -from ccn_transcribe.stages import download +from audio_scribe import errors, paths +from audio_scribe.media import ytdlp_opts +from audio_scribe.stages import download if TYPE_CHECKING: from pathlib import Path diff --git a/tests/test_stages_pipeline.py b/tests/test_stages_pipeline.py index 260532c..f2c1abe 100644 --- a/tests/test_stages_pipeline.py +++ b/tests/test_stages_pipeline.py @@ -5,13 +5,13 @@ from typing import TYPE_CHECKING, ClassVar import pytest -from ccn_transcribe import errors, paths -from ccn_transcribe.backends.base import ToolchainStatus, TranscribeRequest -from ccn_transcribe.media import ffmpeg -from ccn_transcribe.stages import audio as audio_stage -from ccn_transcribe.stages import outputs as outputs_stage -from ccn_transcribe.stages import transcribe as transcribe_stage -from ccn_transcribe.transcript import Segment, TranscriptResult +from audio_scribe import errors, paths +from audio_scribe.backends.base import ToolchainStatus, TranscribeRequest +from audio_scribe.media import ffmpeg +from audio_scribe.stages import audio as audio_stage +from audio_scribe.stages import outputs as outputs_stage +from audio_scribe.stages import transcribe as transcribe_stage +from audio_scribe.transcript import Segment, TranscriptResult if TYPE_CHECKING: from pathlib import Path diff --git a/tests/test_transcript.py b/tests/test_transcript.py index bcfb3bb..31707a6 100644 --- a/tests/test_transcript.py +++ b/tests/test_transcript.py @@ -4,7 +4,7 @@ import logging import pytest -from ccn_transcribe.transcript import Segment, TranscriptResult, normalize_segments +from audio_scribe.transcript import Segment, TranscriptResult, normalize_segments def _result(**kw: object) -> TranscriptResult: diff --git a/uv.lock b/uv.lock index d31dc6f..888056c 100644 --- a/uv.lock +++ b/uv.lock @@ -93,6 +93,53 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b0/cf/1c5f42b110e57bc5502eb80dbc3b03d256926062519224835ef08134f1f9/astroid-4.0.4-py3-none-any.whl", hash = "sha256:52f39653876c7dec3e3afd4c2696920e05c83832b9737afc21928f2d2eb7a753", size = 276445, upload-time = "2026-02-07T23:35:05.344Z" }, ] +[[package]] +name = "audio-scribe" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "click" }, + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "openvino" }, + { name = "openvino-genai" }, + { name = "yt-dlp", extra = ["default", "deno"] }, + { name = "yt-dlp-ejs" }, +] + +[package.dev-dependencies] +dev = [ + { name = "mypy" }, + { name = "pre-commit" }, + { name = "pylint" }, + { name = "pyright" }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "click", specifier = ">=8.1,<9" }, + { name = "huggingface-hub", specifier = ">=1.31,<2" }, + { name = "numpy", specifier = ">=2.5,<3" }, + { name = "openvino", specifier = "==2026.3.1" }, + { name = "openvino-genai", specifier = "==2026.3.1.0" }, + { name = "yt-dlp", extras = ["default", "deno"], specifier = ">=2026.8.19" }, + { name = "yt-dlp-ejs", specifier = ">=0.8" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "mypy", specifier = ">=1.11" }, + { name = "pre-commit", specifier = ">=3.8" }, + { name = "pylint", specifier = ">=3.2" }, + { name = "pyright", specifier = ">=1.1.380" }, + { name = "pytest", specifier = ">=8" }, + { name = "pytest-cov", specifier = ">=5" }, + { name = "ruff", specifier = ">=0.6" }, +] + [[package]] name = "brotli" version = "1.2.0" @@ -152,53 +199,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/95/ae/afd54e744df93b51cc29f6a19beccf9998b25743d7177697390de10479d1/brotlicffi-1.2.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:489ca4da3ee65926d72bf01584b61088a9da6bdd1bb01b2040901e1beaffa8f0", size = 379761, upload-time = "2026-08-21T17:29:10.687Z" }, ] -[[package]] -name = "ccn-transcribe" -version = "0.1.0" -source = { editable = "." } -dependencies = [ - { name = "click" }, - { name = "huggingface-hub" }, - { name = "numpy" }, - { name = "openvino" }, - { name = "openvino-genai" }, - { name = "yt-dlp", extra = ["default", "deno"] }, - { name = "yt-dlp-ejs" }, -] - -[package.dev-dependencies] -dev = [ - { name = "mypy" }, - { name = "pre-commit" }, - { name = "pylint" }, - { name = "pyright" }, - { name = "pytest" }, - { name = "pytest-cov" }, - { name = "ruff" }, -] - -[package.metadata] -requires-dist = [ - { name = "click", specifier = ">=8.1,<9" }, - { name = "huggingface-hub", specifier = ">=1.31,<2" }, - { name = "numpy", specifier = ">=2.5,<3" }, - { name = "openvino", specifier = "==2026.3.1" }, - { name = "openvino-genai", specifier = "==2026.3.1.0" }, - { name = "yt-dlp", extras = ["default", "deno"], specifier = ">=2026.8.19" }, - { name = "yt-dlp-ejs", specifier = ">=0.8" }, -] - -[package.metadata.requires-dev] -dev = [ - { name = "mypy", specifier = ">=1.11" }, - { name = "pre-commit", specifier = ">=3.8" }, - { name = "pylint", specifier = ">=3.2" }, - { name = "pyright", specifier = ">=1.1.380" }, - { name = "pytest", specifier = ">=8" }, - { name = "pytest-cov", specifier = ">=5" }, - { name = "ruff", specifier = ">=0.6" }, -] - [[package]] name = "certifi" version = "2026.7.22"