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:

  1. pytest tests - unit tests

  2. ruff check - linter

  3. pylint - code analysis

  4. mypy - type checking

  5. luacheck on config/, taplo on the TOML files and actionlint on the workflows

Code Quality Tools

Tool

Config File

Purpose

ruff

pyproject.toml ([tool.ruff])

Linting, line length = 130

pylint

.pylintrc

Code analysis

mypy

pyproject.toml ([tool.mypy])

Static type checking

pytest

pyproject.toml ([tool.pytest.ini_options])

Test runner, pythonpath = ["src"]

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, keywords

  • config/version.lua - Version tuple

  • config/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

  1. 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",
    )
  1. 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
  1. 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

xL_sMXfzzyA

Deleted video

6k2nFwVqQFw

Private video

MZnlL8uCCU0

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.