Development Guide¶
Prerequisites¶
Python >= 3.14
A Google Cloud project with the YouTube Data API v3 enabled
OAuth2 client credentials at
~/.config/pytubekit/client_secret.json(required to build a release: the build hook copies it into the package)
Setting Up a Development Environment¶
git clone https://github.com/veltzer/pytubekit.git
cd pytubekit
python -m venv .venv
source .venv/bin/activate
pip install -e .
pip install pytest mypy ruff pylint
Running Tests¶
# Run unit tests
pytest tests
# Run the full CI suite (tests + linting + type checking)
rsconstruct build
The rsconstruct build runs:
pytest tests- unit testsruff check- linterpylint- code analysismypy- type checkingluacheckonconfig/,taploon the TOML files andactionlinton the workflows
Code Quality Tools¶
Tool |
Config File |
Purpose |
|---|---|---|
ruff |
|
Linting, line length = 130 |
pylint |
|
Code analysis |
mypy |
|
Static type checking |
pytest |
|
Test runner, pythonpath = |
Project Layout¶
Source code lives in src/pytubekit/ following the src layout convention. The pyproject.toml configures pytest to include src in the Python path.
Code Generation with rsconstruct¶
README.md and src/pytubekit/static.py are rendered from tera templates in tera.templates/ by rsconstruct. The source of truth for project metadata lives in the config/ directory:
config/project.lua- Project name, description, keywordsconfig/version.lua- Version tupleconfig/personal.lua- Author details
To regenerate files, run:
rsconstruct build
Do not manually edit the generated files (README.md, static.py). Edit the templates or config files instead. pyproject.toml is hand-edited and is the authoritative dependency list.
Adding a New CLI Command¶
Define any new configuration classes in
src/pytubekit/configs.py:
class ConfigMyFeature(Config):
""" Parameters for my feature """
my_param = ParamCreator.create_str(
help_string="Description of the parameter",
default="default_value",
)
Add the endpoint function in
src/pytubekit/main.py:
@register_endpoint(
description="Description of what this command does",
configs=[ConfigMyFeature],
)
def my_command() -> None:
youtube = get_youtube()
# ... implementation
Import the new config class at the top of
main.py.
The command will automatically be available as pytubekit my_command.
YouTube API Notes¶
Deleted and Private Videos¶
When listing playlist items, the YouTube API returns deleted videos with the title "Deleted video" and private videos with the title "Private video". Fetching video details for a deleted video returns an empty items array.
Pagination¶
The YouTube API returns at most 50 items per request. The PagedRequest class in util.py handles pagination automatically by following nextPageToken values.
API Quota¶
The YouTube Data API v3 has a daily quota of 10,000 units. Different operations consume different amounts of quota. If you hit the limit, wait until the next day.
Test Video IDs¶
For manual testing:
Type |
Video ID |
|---|---|
Regular video |
|
Deleted video |
|
Private video |
|
CI/CD¶
The project uses GitHub Actions (.github/workflows/build.yml) running on Ubuntu 26.04 with Python 3.14.
Building the Documentation¶
The documentation lives in the sphinx/ directory as reStructuredText and is built with Sphinx, like every other python repository in the fleet. The guide pages sit next to the autodoc API pages, all reachable from sphinx/modules.rst.
Build¶
The sphinx processor is part of the regular build, so from the repository’s venv:
rsconstruct build
The rendered site goes to docs/, which is committed and served by GitHub Pages from the master branch. sphinx/conf.py refuses to build unless the sphinx toolchain matches uv.lock, so activate the repository’s venv first.
Preview locally¶
python -m http.server --directory docs 8000
Then open http://127.0.0.1:8000 in your browser and rebuild after editing the .rst files.
Deploy to GitHub Pages¶
There is no separate deploy step. GitHub Pages is configured to serve the docs/ directory of the master branch, so pushing a commit that contains the rebuilt docs/ publishes the site.