This is a monorepo for the prebunking-at-scale project, consisting of the infrastructure, deployment tooling and application code. It is a work-in-progress and do expect process and code changes as we refine the approach over the coming weeks.
To install the repository just run:
uv sync --all-packages
from the repository root.
All three scrapers (instascraper, tokscraper, tubescraper) share a common base Docker image defined in the root Containerfile. Each scraper has its own Containerfile in projects/src/<scraper>/.
Images are built and pushed automatically by the Build and Push Docker Images workflow (.github/workflows/build-images.yml).
- On every push to
mainordev - On manual trigger via
workflow_dispatch
The workflow uses path filtering to only rebuild what changed:
| Change | What rebuilds |
|---|---|
Containerfile, pyproject.toml, uv.lock, projects/lib/** |
Base image + all scrapers |
projects/src/tubescraper/** |
tubescraper only |
projects/src/tokscraper/** |
tokscraper only |
projects/src/instascraper/** |
instascraper only |
Manual triggers (workflow_dispatch) rebuild everything.
Each scraper image is automatically tagged with:
- A semver version tag (e.g.
v1.2.3) — bumped automatically per scraper latestfor builds frommain,devfor builds fromdev
Tags follow the format <scraper>/vX.Y.Z (e.g. tubescraper/v0.5.0). A GitHub release is created for each build.
All images are pushed to Google Artifact Registry:
europe-west4-docker.pkg.dev/pas-shared/pas/<scraper>:<tag>
You can still build locally with docker compose for development:
DOCKER_DEFAULT_PLATFORM=linux/amd64 docker build -t base_image .
DOCKER_DEFAULT_PLATFORM=linux/amd64 docker compose buildNote: The tokscraper and tubescraper images include additional system dependencies (ffmpeg and deno) required by yt-dlp for video downloading.
Deployments use Kustomize with base manifests in deployments/base/ and environment overlays in deployments/dev/ and deployments/prod/.
Pushes to the dev branch automatically deploy to the dev cluster after images are built. This is handled by the deploy-dev job in the build workflow. It runs kubectl apply -k deployments/dev and restarts the rescrape deployments.
Use the Deploy Scraper workflow (.github/workflows/deploy.yml) via GitHub Actions:
- Go to Actions > Deploy Scraper > Run workflow
- Select the scraper to deploy (
tubescraper,tokscraper, orinstascraper) - Optionally provide a git tag (e.g.
tubescraper/v0.5.0)- If no tag is provided, it deploys the latest production release (the highest non-prerelease tag)
- Tags with a prerelease suffix (e.g.
v0.5.0-beta.1) deploy to dev - Tags without a prerelease suffix (e.g.
v0.5.0) deploy to prod
The workflow sets the image tag via kustomize and applies the appropriate overlay (deployments/dev/ or deployments/prod/) to the corresponding GKE cluster.
| Environment | GCP Project | Cluster | Triggered by |
|---|---|---|---|
| dev | pas-development-1 | dev-cluster | Push to dev branch, or manual deploy with prerelease tag |
| prod | pas-production-1 | prod-cluster | Manual deploy with release tag |
If you need to deploy without GitHub Actions (e.g. for debugging or urgent fixes), you can apply the kustomize overlays directly.
- Get credentials for the target cluster:
# Dev
gcloud container clusters get-credentials dev-cluster --project pas-development-1 --location europe-west4-b
# Prod
gcloud container clusters get-credentials prod-cluster --project pas-production-1 --location europe-west4-b- To deploy all scrapers at the default image tag (
latestfor prod,devfor dev):
kubectl apply -k deployments/prod
# or
kubectl apply -k deployments/dev- To deploy a specific version of a single scraper, use
kustomize edit set imageto override the tag before applying:
cd deployments/prod
kustomize edit set image europe-west4-docker.pkg.dev/pas-shared/pas/tubescraper:v1.2.3
kubectl apply -k .Note: kustomize edit set image modifies kustomization.yaml in place. Avoid committing these local tag overrides — they are managed by CI during normal deployments.
All scrapers share common configuration via environment variables from the shared libraries.
The STORAGE_BUCKET_NAME environment variable controls where videos are stored:
| Value | Storage Type | Description |
|---|---|---|
"local" |
Local Disk | Stores videos in a local directory (useful for development) |
"my-bucket-name" |
Google Cloud Storage | Stores videos in the specified GCS bucket |
Local storage paths:
- Tubescraper:
./youtube/ - Tokscraper:
./tiktok/ - Instascraper:
./instascraper/
Example:
# Use local storage for development
export STORAGE_BUCKET_NAME="local"
# Use GCS for production
export STORAGE_BUCKET_NAME="pas-production-storage"| Variable | Default | Description |
|---|---|---|
API_URL |
http://localhost:8000/ |
Base URL for the Core API (tubescraper/tokscraper) |
API_KEYS |
["abc123"] |
JSON array of API keys (first key is used) |
Note: Instascraper defaults to port 3000 for API_URL.
For production use with rate-limited platforms, configure proxy rotation:
| Variable | Default | Description |
|---|---|---|
PROXY_COUNT |
0 |
Number of available proxies from webshare.io |
PROXY_USERNAME |
"" |
Proxy authentication username |
PROXY_PASSWORD |
"" |
Proxy authentication password |
Note: All three proxy variables must be set together for proxying to work. If any are missing, proxying is disabled.
| Variable | Default | Description |
|---|---|---|
ROOT_LOG_LEVEL |
"warn" |
Log level for root logger (external libraries) |
APP_LOG_LEVEL |
"info" |
Log level for application (structlog) |
Valid levels: debug, info, warn, error (case-insensitive)
Tubescraper only:
| Variable | Default | Description |
|---|---|---|
POT_PROVIDER_URL |
"" |
URL for YouTube POT (Proof of Origin Token) provider |
Platform: Instagram
Purpose: Archives Instagram reels from configured user profiles.
How it works:
- Fetches list of Instagram channels from the Core API
- For each channel, fetches the user's profile using Instagram's public web API
- Extracts up to 12 most recent reels from the profile
- Downloads video bytes and uploads to Google Cloud Storage
- Registers video metadata with the Core API
- Tracks cursor (last reel ID) per channel to avoid re-downloading
Command Line Options:
python -m instascraperNo subcommands - runs the channel scraper directly on startup.
Environment Variables:
STORAGE_BUCKET_NAME(required) - GCS bucket name or"local"for local disk storageAPI_URL(default:http://localhost:3000/) - Core API base URLAPI_KEYS- JSON array of API keys
Platform: TikTok
Purpose: Archives TikTok shorts from configured creator accounts with optional stats rescraping.
How it works:
- Fetches list of TikTok channels from the Core API
- Uses
yt-dlpto extract video entries from each TikTok channel - Downloads up to 200 shorts per channel (max 14-day lookback)
- Uploads videos to Google Cloud Storage and registers metadata
- Tracks timestamp cursor per channel to avoid re-downloading
Command Line Options:
python -m tokscraper [COMMAND]| Command | Description |
|---|---|
channels |
Scrape TikTok shorts from configured channels (default) |
rescrape |
Continuously rescrape existing shorts to update view counts and stats |
Examples:
# Run channel scraper (default)
python -m tokscraper
python -m tokscraper channels
# Run rescraper (infinite loop)
python -m tokscraper rescrapeEnvironment Variables:
STORAGE_BUCKET_NAME(required) - GCS bucket name or"local"for local disk storageAPI_URL(default:http://localhost:8000/) - Core API base URLAPI_KEYS- JSON array of API keysPROXY_COUNT,PROXY_USERNAME,PROXY_PASSWORD- Proxy configuration (optional)
Platform: YouTube
Purpose: Archives YouTube shorts from configured channels or keyword searches, with stats update capability.
How it works:
- Fetches YouTube channel or keyword feeds from the Core API
- Uses
yt-dlpto fetch up to 100 shorts per channel/keyword (max 14-day lookback) - Downloads videos and uploads to Google Cloud Storage
- Only updates stats for existing videos if view count increased by 10%+
- Tracks timestamp cursor per channel/keyword to avoid re-downloading
Command Line Options:
python -m tubescraper [COMMAND]| Command | Description |
|---|---|
channels |
Scrape YouTube shorts from configured channels (default) |
keywords |
Scrape YouTube shorts from keyword searches |
rescrape |
Continuously rescrape existing shorts to update view counts and stats |
Examples:
# Run channel scraper (default)
python -m tubescraper
python -m tubescraper channels
# Run keyword scraper
python -m tubescraper keywords
# Run rescraper (infinite loop)
python -m tubescraper rescrapeEnvironment Variables:
STORAGE_BUCKET_NAME(required) - GCS bucket name or"local"for local disk storageAPI_URL(default:http://localhost:8000/) - Core API base URLAPI_KEYS- JSON array of API keysPOT_PROVIDER_URL- YouTube POT provider URL (optional, for avoiding rate limits)PROXY_COUNT,PROXY_USERNAME,PROXY_PASSWORD- Proxy configuration (optional)
| Feature | Instascraper | Tokscraper | Tubescraper |
|---|---|---|---|
| Platform | TikTok | YouTube | |
| Scraping Method | Instagram API | yt-dlp | yt-dlp |
| Keyword Search | No | No | Yes |
| Rescrape Support | No | Yes | Yes |
| Max Lookback | Unlimited | 14 days | 14 days |
| Extra Dependencies | None | ffmpeg, deno | ffmpeg, deno |