Resolving a URL to a job id cost a metadata request on every invocation, including re-runs with nothing to do. index.json records what the last expansion produced and a single-video URL is now answered from it: measured on a finished job, 2.25s and three requests becomes 0.75s and none, and the resume succeeds with the network blocked entirely, where it previously failed. A playlist is deliberately still re-read. Answering one from the index would silently ignore videos added to it since the last run, and that request is the only way to notice them. Entries are keyed by whether --playlist was in effect, because the same URL names a different set with and without it. When a URL that has been expanded before can no longer be read, the recorded jobs are used and the reason is logged rather than failing the whole URL. This keys on the source being unreadable rather than on a particular error, because an unreachable host surfaces as "no metadata" -- yt-dlp reports the failure and returns nothing rather than raising. Only a previously expanded URL can reach this, so it cannot invent work, and any job still needing a download fails on its own merits. That NotFoundError's hint no longer claims a bad URL is the only explanation. Target replaces the (url, entry) tuple so a job id survives without metadata, and store grows the atomic JSON write that save and the index now share. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
107 lines
2.8 KiB
TOML
107 lines
2.8 KiB
TOML
[build-system]
|
|
requires = ["hatchling"]
|
|
build-backend = "hatchling.build"
|
|
|
|
[project]
|
|
name = "ccn-transcribe"
|
|
version = "0.1.0"
|
|
description = "Download videos, extract FLAC, and transcribe with Whisper on whatever GPU is present."
|
|
requires-python = ">=3.12"
|
|
dependencies = [
|
|
"click>=8.1,<9",
|
|
"numpy>=2.5,<3",
|
|
"huggingface-hub>=1.31,<2",
|
|
"yt-dlp[default,deno]>=2026.8.19",
|
|
"yt-dlp-ejs>=0.8",
|
|
"openvino==2026.3.1",
|
|
"openvino-genai==2026.3.1.0",
|
|
]
|
|
|
|
[project.scripts]
|
|
ccn-transcribe = "ccn_transcribe.cli:main"
|
|
|
|
[dependency-groups]
|
|
dev = [
|
|
"pytest>=8",
|
|
"pytest-cov>=5",
|
|
"ruff>=0.6",
|
|
"pyright>=1.1.380",
|
|
"mypy>=1.11",
|
|
"pylint>=3.2",
|
|
"pre-commit>=3.8",
|
|
]
|
|
|
|
[tool.hatch.build.targets.wheel]
|
|
packages = ["src/ccn_transcribe"]
|
|
|
|
[tool.ruff]
|
|
line-length = 100
|
|
src = ["src", "tests"]
|
|
# ruff format also restyles Python blocks inside Markdown; docs are not ours to restyle.
|
|
extend-exclude = ["*.md"]
|
|
|
|
[tool.ruff.lint]
|
|
select = ["E", "F", "W", "I", "N", "UP", "B", "A", "C4", "SIM", "PTH", "RET", "ARG", "TID", "TC", "RUF", "S"]
|
|
# S607: binaries are looked up on PATH on purpose and preflight-checked with
|
|
# shutil.which; hardcoding absolute paths would be less portable, not safer.
|
|
ignore = ["S607"]
|
|
|
|
[tool.ruff.lint.per-file-ignores]
|
|
# S101: asserts are the point of a test file.
|
|
"tests/*" = ["ARG001", "ARG002", "S101"]
|
|
# Throwaway benchmarks, not pipeline code.
|
|
"scripts/*" = ["S603"]
|
|
|
|
[tool.pyright]
|
|
include = ["src", "tests"]
|
|
typeCheckingMode = "strict"
|
|
venvPath = "."
|
|
venv = ".venv"
|
|
|
|
[tool.mypy]
|
|
strict = true
|
|
files = ["src", "tests"]
|
|
|
|
[[tool.mypy.overrides]]
|
|
# These ship no py.typed marker.
|
|
module = ["openvino.*", "openvino_genai.*", "huggingface_hub.*", "yt_dlp.*"]
|
|
ignore_missing_imports = true
|
|
|
|
[tool.pylint.design]
|
|
max-attributes = 20
|
|
# Keyword-only arguments do not carry the readability cost this check guards against.
|
|
max-args = 10
|
|
# Pipeline entry points carry injectable seams for testing.
|
|
max-locals = 18
|
|
|
|
[tool.pylint.main]
|
|
# W0621: pytest fixtures shadow their names by design.
|
|
# C1803: asserting == () is more precise than truthiness about what a call returns.
|
|
# R0903: dataclasses and pytest grouping classes legitimately have few methods.
|
|
disable = [
|
|
"C0114", "C0115", "C0116", "C0301",
|
|
"W0611", "W0612", "W0621",
|
|
"R0801", "R0903", "C1803",
|
|
]
|
|
|
|
[tool.pytest.ini_options]
|
|
testpaths = ["tests"]
|
|
# NOTE: --cov-fail-under deliberately lives in the pre-commit hook, not here, so a
|
|
# single-file TDD run (`uv run pytest tests/test_x.py`) is not blocked by coverage.
|
|
addopts = "-q"
|
|
|
|
[tool.coverage.run]
|
|
branch = true
|
|
source = ["src/ccn_transcribe"]
|
|
|
|
[tool.coverage.report]
|
|
fail_under = 100
|
|
show_missing = true
|
|
exclude_also = [
|
|
"if TYPE_CHECKING:",
|
|
'if __name__ == "__main__":',
|
|
"@overload",
|
|
"raise NotImplementedError",
|
|
"class .*\\(Protocol\\):",
|
|
]
|