yt-tools-cli 0.0.0__tar.gz → 0.23.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. yt_tools_cli-0.23.2/.claude-plugin/plugin.json +28 -0
  2. yt_tools_cli-0.23.2/.github/workflows/ci.yml +66 -0
  3. yt_tools_cli-0.23.2/.github/workflows/release.yml +72 -0
  4. yt_tools_cli-0.23.2/.gitignore +23 -0
  5. yt_tools_cli-0.23.2/CHANGELOG.md +251 -0
  6. yt_tools_cli-0.23.2/CONTRIBUTING.md +70 -0
  7. yt_tools_cli-0.23.2/LICENSE +21 -0
  8. yt_tools_cli-0.23.2/PKG-INFO +649 -0
  9. yt_tools_cli-0.23.2/README.md +607 -0
  10. yt_tools_cli-0.23.2/hooks/hooks.json +19 -0
  11. yt_tools_cli-0.23.2/pyproject.toml +120 -0
  12. yt_tools_cli-0.23.2/scripts/check-core-install.sh +206 -0
  13. yt_tools_cli-0.23.2/scripts/check-metadata.py +157 -0
  14. yt_tools_cli-0.23.2/scripts/check-pypi-name.py +166 -0
  15. yt_tools_cli-0.23.2/scripts/ensure-install.ps1 +188 -0
  16. yt_tools_cli-0.23.2/scripts/ensure-install.py +67 -0
  17. yt_tools_cli-0.23.2/scripts/ensure-install.sh +167 -0
  18. yt_tools_cli-0.23.2/scripts/publish-check.py +232 -0
  19. yt_tools_cli-0.23.2/scripts/sync-version.py +173 -0
  20. yt_tools_cli-0.23.2/skills/using-yt-tools/SKILL.md +643 -0
  21. yt_tools_cli-0.23.2/tests/__init__.py +0 -0
  22. yt_tools_cli-0.23.2/tests/test_cache.py +143 -0
  23. yt_tools_cli-0.23.2/tests/test_check_metadata.py +168 -0
  24. yt_tools_cli-0.23.2/tests/test_cli_smoke.py +301 -0
  25. yt_tools_cli-0.23.2/tests/test_comments.py +152 -0
  26. yt_tools_cli-0.23.2/tests/test_core.py +165 -0
  27. yt_tools_cli-0.23.2/tests/test_distribution_name.py +77 -0
  28. yt_tools_cli-0.23.2/tests/test_doctor.py +784 -0
  29. yt_tools_cli-0.23.2/tests/test_failure_modes.py +49 -0
  30. yt_tools_cli-0.23.2/tests/test_frames.py +600 -0
  31. yt_tools_cli-0.23.2/tests/test_listen.py +561 -0
  32. yt_tools_cli-0.23.2/tests/test_markdown.py +138 -0
  33. yt_tools_cli-0.23.2/tests/test_meta.py +151 -0
  34. yt_tools_cli-0.23.2/tests/test_missing_extra.py +318 -0
  35. yt_tools_cli-0.23.2/tests/test_ocr.py +412 -0
  36. yt_tools_cli-0.23.2/tests/test_packaging.py +525 -0
  37. yt_tools_cli-0.23.2/tests/test_parse_interval.py +42 -0
  38. yt_tools_cli-0.23.2/tests/test_publish_check.py +166 -0
  39. yt_tools_cli-0.23.2/tests/test_pypi_name.py +157 -0
  40. yt_tools_cli-0.23.2/tests/test_search.py +287 -0
  41. yt_tools_cli-0.23.2/tests/test_skill_portable.py +101 -0
  42. yt_tools_cli-0.23.2/tests/test_transcript.py +232 -0
  43. yt_tools_cli-0.23.2/tests/test_version.py +274 -0
  44. yt_tools_cli-0.23.2/tests/test_watch.py +324 -0
  45. yt_tools_cli-0.23.2/yt_tools/__init__.py +86 -0
  46. yt_tools_cli-0.23.2/yt_tools/_metadata.py +60 -0
  47. yt_tools_cli-0.23.2/yt_tools/cache.py +103 -0
  48. yt_tools_cli-0.23.2/yt_tools/cli.py +145 -0
  49. yt_tools_cli-0.23.2/yt_tools/comments.py +203 -0
  50. yt_tools_cli-0.23.2/yt_tools/core.py +159 -0
  51. yt_tools_cli-0.23.2/yt_tools/doctor.py +483 -0
  52. yt_tools_cli-0.23.2/yt_tools/extras.py +134 -0
  53. yt_tools_cli-0.23.2/yt_tools/frames.py +597 -0
  54. yt_tools_cli-0.23.2/yt_tools/listen.py +842 -0
  55. yt_tools_cli-0.23.2/yt_tools/markdown.py +127 -0
  56. yt_tools_cli-0.23.2/yt_tools/meta.py +171 -0
  57. yt_tools_cli-0.23.2/yt_tools/ocr.py +296 -0
  58. yt_tools_cli-0.23.2/yt_tools/search.py +308 -0
  59. yt_tools_cli-0.23.2/yt_tools/transcript.py +164 -0
  60. yt_tools_cli-0.23.2/yt_tools/watch.py +288 -0
  61. yt_tools_cli-0.0.0/PKG-INFO +0 -36
  62. yt_tools_cli-0.0.0/README.md +0 -23
  63. yt_tools_cli-0.0.0/pyproject.toml +0 -23
  64. yt_tools_cli-0.0.0/setup.cfg +0 -4
  65. yt_tools_cli-0.0.0/yt_tools_cli.egg-info/PKG-INFO +0 -36
  66. yt_tools_cli-0.0.0/yt_tools_cli.egg-info/SOURCES.txt +0 -6
  67. yt_tools_cli-0.0.0/yt_tools_cli.egg-info/dependency_links.txt +0 -1
  68. yt_tools_cli-0.0.0/yt_tools_cli.egg-info/top_level.txt +0 -1
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "yt-tools",
3
+ "displayName": "yt-tools",
4
+ "version": "0.23.2",
5
+ "description": "Iterative YouTube watching for agents: clean-markdown transcripts with [mm:ss] anchors, targeted frame extraction, FFT audio analysis (BPM, key, chord progression, spectral features), full-metadata extraction (description, chapters, most-replayed, counts, tags), comment scraping, and OCR over cached frames (RapidOCR PP-OCRv5) for silent-with-text videos. Light core (transcript / metadata / comments / search) with heavy stacks in [frames] and [audio] extras. Bundles the using-yt-tools skill and a SessionStart hook that auto-installs from the plugin's local clone via pipx (default [full] extras, fallback to [frames,audio]) and probes ffmpeg.",
6
+ "author": {
7
+ "name": "Victor Kuznetsov",
8
+ "url": "https://github.com/kzntsv-dev"
9
+ },
10
+ "homepage": "https://github.com/kzntsv-dev/yt-tools",
11
+ "repository": "https://github.com/kzntsv-dev/yt-tools",
12
+ "license": "MIT",
13
+ "keywords": [
14
+ "youtube",
15
+ "transcript",
16
+ "agent",
17
+ "claude",
18
+ "audio-analysis",
19
+ "fft",
20
+ "bpm",
21
+ "iterative",
22
+ "yt-dlp",
23
+ "ffmpeg",
24
+ "librosa"
25
+ ],
26
+ "skills": "./skills/",
27
+ "hooks": "./hooks/hooks.json"
28
+ }
@@ -0,0 +1,66 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ tests:
14
+ name: pytest (full extras)
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: actions/setup-python@v5
19
+ with:
20
+ python-version: "3.12"
21
+ # The heavy stacks are installed on purpose: a handful of tests exercise the
22
+ # real audio pipeline rather than a mock, and a suite that is red on a clean
23
+ # machine teaches everyone to ignore it. The light-core contract is covered
24
+ # by the gate job below, which runs without any extra.
25
+ - name: Install
26
+ run: pip install -e ".[full,test]"
27
+ - name: Unit + CLI smoke tests
28
+ run: pytest -q
29
+
30
+ build:
31
+ name: build (sdist + wheel, publishable metadata)
32
+ runs-on: ubuntu-latest
33
+ steps:
34
+ - uses: actions/checkout@v4
35
+ - uses: actions/setup-python@v5
36
+ with:
37
+ python-version: "3.12"
38
+ - name: Install build tooling
39
+ run: python -m pip install --upgrade build twine
40
+ # The release job builds these same artifacts on a tag. Running the build
41
+ # here too is cheap insurance: a packaging mistake should cost a red CI
42
+ # run, not a burned version number. v0.23.0 is the case in point — build
43
+ # green, `twine check` green, and PyPI answered 400 on upload because a
44
+ # `Requires-Dist` carried a direct reference (`bpm-detector @ git+…`).
45
+ - name: Build + twine check
46
+ run: |
47
+ python -m build
48
+ python -m twine check dist/*
49
+ - name: Publishable metadata (no direct refs, no dev-repo meta)
50
+ run: python scripts/check-metadata.py dist
51
+ - uses: actions/upload-artifact@v4
52
+ with:
53
+ name: dist
54
+ path: dist/
55
+ retention-days: 7
56
+
57
+ gate:
58
+ name: install contract (AC4/AC5)
59
+ runs-on: ubuntu-latest
60
+ steps:
61
+ - uses: actions/checkout@v4
62
+ # Runs itself inside python:3.12-slim: core install without extras, once
63
+ # without ffmpeg and once with a real static one, then checks that `warn`
64
+ # and `missing` stay machine-distinguishable in one --json payload.
65
+ - name: Core-install gate
66
+ run: bash scripts/check-core-install.sh
@@ -0,0 +1,72 @@
1
+ name: release
2
+
3
+ # A release is a tag: `git tag v0.23.0 && git push origin v0.23.0`. The version in
4
+ # pyproject.toml must match the tag — publish-check (last job) fails the run when
5
+ # it does not, because a green build is not a published release.
6
+ on:
7
+ push:
8
+ tags: ["v*"]
9
+ workflow_dispatch:
10
+ inputs:
11
+ version:
12
+ description: "Version to publish (must match pyproject.toml)"
13
+ required: true
14
+
15
+ permissions:
16
+ contents: read
17
+
18
+ jobs:
19
+ build:
20
+ name: build sdist + wheel
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+ - uses: actions/setup-python@v5
25
+ with:
26
+ python-version: "3.12"
27
+ - name: Build
28
+ run: |
29
+ python -m pip install --upgrade build twine
30
+ python -m build
31
+ python -m twine check dist/*
32
+ # Last line of defence, on the exact artifacts about to be uploaded: the
33
+ # same check CI runs, because this is the run where a miss costs a version.
34
+ - name: Publishable metadata
35
+ run: python scripts/check-metadata.py dist
36
+ - uses: actions/upload-artifact@v4
37
+ with:
38
+ name: dist
39
+ path: dist/
40
+
41
+ publish:
42
+ name: publish to PyPI (trusted publishing)
43
+ needs: build
44
+ runs-on: ubuntu-latest
45
+ # No API token anywhere: PyPI trusts this workflow through OIDC. The
46
+ # environment name must match the trusted publisher configured for the
47
+ # project on PyPI (`pypi`).
48
+ environment: pypi
49
+ permissions:
50
+ id-token: write # the OIDC token the index exchanges for an upload grant
51
+ steps:
52
+ - uses: actions/download-artifact@v4
53
+ with:
54
+ name: dist
55
+ path: dist/
56
+ - uses: pypa/gh-action-pypi-publish@release/v1
57
+
58
+ verify:
59
+ name: verify the release landed
60
+ needs: publish
61
+ runs-on: ubuntu-latest
62
+ steps:
63
+ - uses: actions/checkout@v4
64
+ - uses: actions/setup-python@v5
65
+ with:
66
+ python-version: "3.12"
67
+ # The point of the job: "uploaded" and "installable" are different claims.
68
+ - name: publish-check (is the version people install the version we tagged?)
69
+ run: |
70
+ version="${GITHUB_REF_NAME#v}"
71
+ [ -n "$version" ] || version="${{ inputs.version }}"
72
+ python scripts/publish-check.py --version "$version"
@@ -0,0 +1,23 @@
1
+ # Build and run artifacts
2
+ __pycache__/
3
+ *.pyc
4
+ .venv/
5
+ .pytest_cache/
6
+ *.egg-info/
7
+ build/
8
+ dist/
9
+ yt-cache/
10
+
11
+ # Development-only paths. They exist in the private development repository and
12
+ # must never appear here: this repository is a curated copy, not the dev tree.
13
+ AGENTS.md
14
+ CLAUDE.md
15
+ .wiki/
16
+ .tasks/
17
+ .brainstorm/
18
+ .archive/
19
+ .mappa
20
+ .mappa-manifest.json
21
+ .pi/
22
+ MEMORY.md
23
+ .mcp.json
@@ -0,0 +1,251 @@
1
+ # Changelog
2
+
3
+ All notable changes to yt-tools. Format follows [Keep a Changelog](https://keepachangelog.com/);
4
+ the project uses [Semantic Versioning](https://semver.org/).
5
+
6
+ The version lives in exactly one place — `pyproject.toml` — and is propagated to
7
+ `.claude-plugin/plugin.json` and the skill frontmatter by `scripts/sync-version.py`.
8
+ Releases before 0.8.0 are summarized only in the git log.
9
+
10
+ ## [Unreleased]
11
+
12
+ _(nothing yet)_
13
+
14
+ ## [0.23.2] — 2026-09-13
15
+
16
+ ### Fixed
17
+
18
+ - `scripts/publish-check.py` no longer reports a false `version drift` seconds
19
+ after a successful upload. PyPI's *project-level* JSON API is CDN-cached
20
+ (`cache-control: max-age=900`), so the `verify` job — which runs about five
21
+ seconds after the publish step — read the previous version and failed the
22
+ `0.23.1` release run while the release itself was live and complete. The check
23
+ now asks the *version-scoped* endpoint (`/pypi/<name>/<version>/json`, a URL
24
+ that cannot be stale) first, and lets the project endpoint only explain. Same
25
+ failure class as the simple-index cache behind the 0.22.1 preflight note: the
26
+ source a check adjudicates on has to be one that cannot lag.
27
+
28
+ ### Changed
29
+
30
+ - The README and the skill lead with the PyPI install instead of the git URL:
31
+ `pipx install "yt-tools-cli[full]"`, a pinned form
32
+ (`pipx install "yt-tools-cli[full]==0.23.1"`), and an explicit note that the
33
+ distribution is `yt-tools-cli` while the project, the repository and every
34
+ command stay `yt-tools` (import name `yt_tools`).
35
+ - The *changing extras* advice is corrected: `pipx uninstall` and then install,
36
+ instead of `pipx install --force`. A forced reinstall on an existing venv was
37
+ observed leaving the new extras half-installed (`scenedetect` skipped entirely)
38
+ and resolving `librosa` 1.0 past its cap; `pipx runpip yt-tools-cli list` is
39
+ the check that sees it.
40
+
41
+ ## [0.23.1] — 2026-09-13
42
+
43
+ > `0.23.0` never reached PyPI: the upload was rejected and no artifact exists
44
+ > for it, so the first installable version is this one.
45
+
46
+ ### Fixed
47
+
48
+ - **PyPI rejected the `0.23.0` upload with `400 Can't have direct dependency`.**
49
+ `[full]` carried `bpm-detector @ git+…` — a PEP 508 direct reference — and PyPI
50
+ refuses those in `Requires-Dist`. `python -m build` and `twine check` both
51
+ accept them, so the failure existed only on the tag. `[full]` is now core +
52
+ `[frames]` + `[audio]`; the plugin hook injects bpm-detector on top of it
53
+ best-effort, and a PyPI install adds it with `pipx inject yt-tools-cli …`
54
+ (README, *Installing bpm-detector*). Without it `yt-listen` still runs —
55
+ librosa-only BPM/key, with the sections it cannot fill marked `n/a`.
56
+ - `[tool.hatch.metadata] allow-direct-references` is gone. It is what let the
57
+ rejected metadata build at all; with no direct references left it is dead
58
+ weight, and keeping it off means the next one fails at `python -m build`.
59
+
60
+ ### Added
61
+
62
+ - `scripts/check-metadata.py`: gates the built artifacts — no direct references
63
+ in `Requires-Dist`, no dev-repo meta (`AGENTS.md`, `CLAUDE.md`, `.mappa/`,
64
+ `.wiki/`, `.tasks/`, `.pi/`) inside the wheel or the sdist. It runs in CI and
65
+ again in the release job, on the exact artifacts about to be uploaded.
66
+ - A `build` job in `ci.yml`: sdist + wheel + `twine check` + the metadata gate on
67
+ every push. A packaging mistake now costs a red run instead of a version
68
+ number.
69
+ - The sdist target excludes the dev-repo meta (`AGENTS.md`, `CLAUDE.md`,
70
+ `.mappa/`, `.mappa-manifest.json`, `.pi/`, `.wiki/`, `.tasks/`). The curated
71
+ pub tree already drops those files, but a build run in the *dev* tree picked
72
+ them up — dev's `.gitignore` keeps them on purpose — so `python -m build` from
73
+ a dev checkout put a canon snapshot into the sdist. The new gate found this on
74
+ its first run.
75
+
76
+ ## [0.23.0] — 2026-09-13
77
+
78
+ ### Changed
79
+
80
+ - **The distribution is `yt-tools-cli`.** PyPI refuses `yt-tools`: the name folds
81
+ into the same similarity class as the existing `yttools`, so the upload is
82
+ rejected at registration. The project, repository, plugin and every console
83
+ command stay `yt-tools`, and the import name stays `yt_tools` — only what
84
+ `pip`/`pipx` resolve changed.
85
+ - Every install hint now names the distribution (`pipx inject yt-tools-cli …`,
86
+ `pip install 'yt-tools-cli[extra]'`), and `doctor` builds its `next_step` from
87
+ the same constant as the refusals instead of a second hardcoded copy that would
88
+ have drifted out of sync.
89
+
90
+ ### Added
91
+
92
+ - `scripts/publish-check.py`: verifies that a release actually landed on PyPI.
93
+ "Never landed", "version drift" and "incomplete artifacts" are reported as
94
+ three different failures with three different fixes.
95
+ - `.github/workflows/release.yml`: tag → build → PyPI by trusted publishing (OIDC,
96
+ no token on disk), with `publish-check` as the final job — a green build is not
97
+ a published release.
98
+
99
+ ## [0.22.1] — 2026-09-13
100
+
101
+ ### Fixed
102
+
103
+ - `scripts/check-pypi-name.py`: documented the staleness of its own input. The
104
+ names come from the simple index, which is CDN-cached, so a project created
105
+ minutes ago can still read as "available" — the verdict is a pre-flight, not a
106
+ report on the last hour. Found live, on the day `yt-tools-cli` was registered.
107
+
108
+ ## [0.21.0] — 2026-09-13
109
+
110
+ ### Added
111
+
112
+ - PyPI name pre-flight (`scripts/check-pypi-name.py`) reproducing warehouse's
113
+ `ultranormalize_name` similarity rule against the live index.
114
+
115
+ ## [0.20.0] — 2026-09-13
116
+
117
+ ### Added
118
+
119
+ - `scripts/check-core-install.sh` — the install contract (AC4/AC5) as a runnable
120
+ gate for a clean `python:3.12-slim`: core install only, then again with a real
121
+ static `ffmpeg`; asserts `can_proceed`/`next_step`/exit code on both sides and
122
+ that `warn` and `missing` stay distinguishable in one `--json` payload. Blocks A
123
+ and B expect opposite exit codes in one run, so it cannot pass vacuously.
124
+
125
+ ## [0.19.1] — 2026-09-13
126
+
127
+ ### Fixed
128
+
129
+ - `yt-tools cache list` / `cache prune` on an unreadable cache: named cause and a
130
+ non-zero exit instead of a traceback (the helper stays strict; the CLI explains).
131
+ - `yt-tools cache list` no longer presents a lower-bound size as an exact total —
132
+ both the entry and the total line carry the qualifier.
133
+ - `yt-tools doctor`: a dangling symlink in place of `yt-cache` (or of one of its
134
+ ancestors) is a `warn` with the reason, not an `ok`.
135
+
136
+ ## [0.19.0] — 2026-09-13
137
+
138
+ ### Added
139
+
140
+ - Public version reader: `yt_tools.__version__` is read from the installed
141
+ distribution metadata (falling back to `pyproject.toml` in a checkout), resolved
142
+ lazily — the last hand-maintained version source is gone.
143
+
144
+ ### Fixed
145
+
146
+ - Coverage gaps found in review around the version reader.
147
+
148
+ ## [0.18.2] — 2026-09-13
149
+
150
+ ### Fixed
151
+
152
+ - `doctor` and `cache list` survive paths that are not representable in a cp1251
153
+ console (the Windows default) — output paths are data, not decoration.
154
+
155
+ ## [0.18.1] — 2026-09-13
156
+
157
+ ### Fixed
158
+
159
+ - `doctor`: an unreadable cache is a `warn` with the reason instead of a crash, and
160
+ a size that could only be partially counted says so ("lower bound").
161
+ - `doctor --base` pointing at a file (or at a path whose ancestor is a file) is a
162
+ `warn`, not a silent `ok`.
163
+ - Writability is decided by an actual write probe, not `os.access` (which reads the
164
+ DOS read-only attribute on Windows and made the check dead on the primary platform).
165
+
166
+ ## [0.18.0] — 2026-09-13
167
+
168
+ ### Added
169
+
170
+ - Portable skill: harness-neutral install and usage instructions, plus the `doctor`
171
+ preflight as the documented first step.
172
+
173
+ ## [0.17.0] — 2026-09-12
174
+
175
+ ### Added
176
+
177
+ - `yt-tools doctor` (`--json`): environment preflight with per-check `status`,
178
+ report-level `can_proceed` and `next_step`. `warn` never blocks; a missing
179
+ required binary (`yt-dlp`, `ffmpeg`) does. Nothing is installed or mutated.
180
+
181
+ ## [0.16.0] — 2026-09-12
182
+
183
+ ### Added
184
+
185
+ - Single source of version truth: `pyproject.toml`, with `scripts/sync-version.py`
186
+ writing the derived manifests and a guard test failing on divergence.
187
+
188
+ ## [0.15.1] — 2026-09-12
189
+
190
+ ### Fixed
191
+
192
+ - Extra detection by import probe rather than a package-metadata guess; degradation
193
+ announced with its own wording instead of a refusal.
194
+
195
+ ## [0.15.0] — 2026-09-12
196
+
197
+ ### Changed
198
+
199
+ - A flow whose optional stack is missing refuses with the exact install command
200
+ instead of raising `ImportError`.
201
+
202
+ ## [0.14.0] — 2026-09-12
203
+
204
+ ### Changed
205
+
206
+ - Light core: `pip install yt-tools` pulls `youtube-transcript-api` and `yt-dlp`
207
+ only. The heavy stacks moved into `[frames]`, `[audio]` and `[ocr]` extras.
208
+
209
+ ## [0.13.1] — 2026-09-12
210
+
211
+ ### Fixed
212
+
213
+ - Findings from the umbrella review of the frame-budget cluster.
214
+
215
+ ## [0.13.0] — 2026-09-12
216
+
217
+ ### Added
218
+
219
+ - A video without subtitles refuses with a named reason instead of an empty file;
220
+ `yt-watch` gained a frames-only mode.
221
+
222
+ ## [0.12.0] — 2026-09-12
223
+
224
+ ### Added
225
+
226
+ - Frame budget recorded in the `watch.md` artifact.
227
+
228
+ ## [0.11.0] — 2026-09-11
229
+
230
+ ### Fixed
231
+
232
+ - Unique frame filenames within the same second.
233
+
234
+ ## [0.10.0] — 2026-09-11
235
+
236
+ ### Added
237
+
238
+ - Near-duplicate frame dedup: the budget is spent on distinct frames.
239
+
240
+ ## [0.9.0] — 2026-09-11
241
+
242
+ ### Added
243
+
244
+ - Uniform-sampling fallback when scene detection finds nothing (a static video no
245
+ longer yields a single frame).
246
+
247
+ ## [0.8.0] — 2026-09-11
248
+
249
+ ### Added
250
+
251
+ - `--max-frames` ceiling: a video cannot hand back an arbitrary number of frames.
@@ -0,0 +1,70 @@
1
+ # Contributing
2
+
3
+ Thanks for looking. yt-tools is a small, opinionated toolkit: nine stateless CLIs
4
+ for agent-driven YouTube watching. The rules below exist to keep it that way.
5
+
6
+ ## Getting set up
7
+
8
+ ```bash
9
+ git clone https://github.com/kzntsv-dev/yt-tools.git
10
+ cd yt-tools
11
+ pip install -e ".[full,test]" # [full] = frames + audio stacks; [ocr] is separate
12
+ pytest -q
13
+ ```
14
+
15
+ `ffmpeg` and `yt-dlp` are external binaries the tools shell out to; `yt-tools
16
+ doctor` reports what is present and the exact command for what is not.
17
+
18
+ Two gates are worth knowing before you push:
19
+
20
+ ```bash
21
+ pytest -q # unit + CLI smoke (network is mocked)
22
+ bash scripts/check-core-install.sh # needs docker: the install contract, end to end
23
+ ```
24
+
25
+ ## What a change should look like
26
+
27
+ - **Test first.** A behaviour change comes with a test that fails without it. If
28
+ you cannot make the test fail on the current code, it is not testing the change.
29
+ A test that cannot fail (a "guard") is welcome, but say so in its docstring.
30
+ - **Explain the refusal.** Absence of an optional dependency is a message with the
31
+ exact command, never a traceback. The same goes for a broken environment: report
32
+ it and exit non-zero.
33
+ - **Say what you know and what you do not.** If a number can only be partially
34
+ computed, qualify it — an undercount presented as an exact figure is the bug
35
+ class this project has already been bitten by.
36
+ - **Keep the core light.** Anything heavy belongs in an extra. `pip install
37
+ yt-tools` must stay a seconds-long install.
38
+ - **Add an entry** to `CHANGELOG.md` under `[Unreleased]`, and bump `version` in
39
+ `pyproject.toml` (see below).
40
+
41
+ ## Style
42
+
43
+ - Python 3.10+ syntax, `from __future__ import annotations` at the top.
44
+ - Comments and docstrings explain *why* — ideally with the failure that motivated
45
+ them. "What" is the code's job.
46
+ - Human-facing output templates stay ASCII: the console may be cp1251 on Windows.
47
+ - Keep output parseable: `--json` where a machine reads it, stable wording where a
48
+ person does.
49
+
50
+ ## Releases
51
+
52
+ The version lives in exactly one place — `pyproject.toml`. The derived files are
53
+ written by a script, never by hand:
54
+
55
+ ```bash
56
+ # 1. bump `version` in pyproject.toml
57
+ python scripts/sync-version.py # plugin.json + skill frontmatter
58
+ python scripts/sync-version.py --check # what the guard test runs
59
+ # 2. add the CHANGELOG entry, commit, push
60
+ git commit -m "feat(scope): what changed (vX.Y.Z)"
61
+ ```
62
+
63
+ `tests/test_version.py` fails when the sources disagree, so a half-finished bump
64
+ cannot ship.
65
+
66
+ ## Reporting a bug
67
+
68
+ Include what you ran, what you expected, what happened (the exact message), your
69
+ platform, and the output of `yt-tools doctor`. Issues that reproduce on a bare
70
+ `pip install yt-tools` are the most valuable ones.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Victor Kuznetsov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.