Build Log · ThePokeAlgorithm
A full-stack analytics command center, AI voice engine, and content automation system — built to run my Pokémon TCG YouTube channel like a data lab.
Introduction
Running a data-driven Pokémon TCG YouTube channel means tracking a lot — card prices, view velocity, profit per episode, CTR, audience retention. I was bouncing between YouTube Studio, spreadsheets, TCGPlayer tabs, and my notes app just to plan one video. That was unsustainable.
So I built PokeAlgorithm Lab v3 — a custom Flask web application that lives locally on my machine and acts as a single command center for everything: analytics, content planning, SEO research, profit tracking, script building, and even AI voiceover generation.
This isn't a template or a no-code tool. Every feature was written specifically for how I make content. Data > luck. — The PokeAlgorithm Build Philosophy
01 — Foundation
I chose tools I already knew and that were fast to iterate with. The entire stack is Python-first on the backend with vanilla JS and Chart.js on the frontend — no React, no build pipeline, no bloat.
Lightweight Python web framework. Perfect for a local-only tool — no auth, no deployment overhead, just routes and templates.
All channel analytics charts — view velocity, CTR heatmaps, content type breakdowns — rendered client-side using real CSV data.
Powers the TTS voice engine. Custom voice settings tuned to my energetic NYC delivery style, outputting numbered MP3s per segment.
The ground truth for all analytics. Real exports from YouTube Studio — views, CTR, watch time, impressions — fed directly into the dashboard.
02 — Project Setup
The first step was laying out a clean folder structure and getting Flask running inside a Python virtual environment. Because I'm on WSL (Windows Subsystem for Linux), I had to work around PEP 668's restriction on installing packages system-wide.
# Create and activate the virtual environment python3 -m venv venv source venv/bin/activate # Install all dependencies pip install flask pandas requests elevenlabs
The project folder was organized like this:
pokealgorithm-lab/ ├── app.py # Flask routes & logic ├── tts_engine.py # ElevenLabs voice engine CLI ├── requirements.txt ├── data/ │ ├── Table_data.csv # Per-video performance │ ├── Chart_data.csv # Daily view history (140 rows) │ ├── Totals.csv # 28-day view totals │ └── profit_tracker.csv # Ad revenue + affiliate per episode └── templates/ ├── base.html ├── dashboard.html ├── calendar.html ├── script_builder.html └── ...
/mnt/c/Users/[username]/Downloads/ — a common trip-up when running WSL on Windows.
03 — Core Feature
The dashboard was the most important piece — it needed to show me, at a glance, how the channel was performing so I could make fast decisions. I loaded real YouTube Studio CSV exports and built every chart around actual numbers.
A line chart built from Chart_data.csv (140 rows of daily view data) showing how views ramp up and decay after each upload. This exposed a sharp Day 1 spike with rapid falloff — meaning the algorithm wasn't picking videos up for long-tail distribution.
Each video from Table_data.csv got a card showing Views, Watch Time, CTR, and a calculated letter grade (A–F) based on a weighted score of those metrics. Story-driven titles scored consistently higher — which confirmed the "Profit or Cooked" framing strategy.
Mapped Click-Through Rate against day of the week and hour of upload using data from the impressions column. Immediately showed which upload windows were underperforming.
A donut chart breaking down content types (pack openings, market analysis, Shorts) by view share, plus a goal tracker showing progress toward monthly subscriber and view milestones.
@app.route('/dashboard') def dashboard(): df = pd.read_csv('data/Table_data.csv') chart_df = pd.read_csv('data/Chart_data.csv') # Calculate letter grade per video def grade(row): score = (row['Views'] * 0.4 + row['Watch time (hours)'] * 0.3 + row['Impressions click-through rate (%)'] * 0.3) if score >= 80: return 'A' elif score >= 65: return 'B' elif score >= 50: return 'C' else: return 'D' df['grade'] = df.apply(grade, axis=1) return render_template('dashboard.html', videos=df.to_dict('records'), chart_data=chart_df.to_json())
04 — Content Planning
The content calendar was built to reduce the guesswork of "what should I upload next?" It pulls in past video data, identifies gaps in the schedule, and shows urgency banners based on how long it's been since the last upload.
A days-since-upload urgency banner turns yellow after 5 days and red after 10, nudging me to stay on a consistent cadence. Each slot on the calendar is color-coded by content type — pack openings (red), market analysis (neon green), Shorts (yellow) — so I can see the content mix at a glance.
05 — Production Workflow
This feature turned a plain script into a full CapCut production timeline — one of my favorite parts of the whole build. The idea: I write the episode script in the text area, hit Generate, and get back a timestamped editing plan I can follow inside CapCut.
The builder automatically inserts "INSERT PRICE POP-UP" cue markers at 30-second intervals throughout the timeline, matching the CapCut checklist rule of showing real TCGPlayer/eBay data every 30 seconds.
Pattern-matched against the script to detect natural tension points and insert "RETENTION HOOK OVERLAY" cues every ~35 seconds, keeping watch time up.
Calculates the 80% timestamp from total video duration and auto-inserts a "FOMO GIVEAWAY TEASE" cue — the exact strategy used to spike audience retention near the end.
The full production timeline exports as a downloadable CSV with columns for Timestamp, Cue Type, Script Line, and Notes — ready to follow in CapCut or share with an editor.
def generate_capcut_timeline(script, duration_seconds): timeline = [] words = script.split() words_per_second = len(words) / duration_seconds for t in range(0, duration_seconds, 30): timeline.append({ 'timestamp': fmt_time(t), 'cue': 'PRICE POP-UP', 'note': 'Insert TCGPlayer / eBay live price' }) fomo_t = int(duration_seconds * 0.80) timeline.append({ 'timestamp': fmt_time(fomo_t), 'cue': 'FOMO GIVEAWAY TEASE', 'note': 'Drop the community hint' }) return sorted(timeline, key=lambda x: x['timestamp'])
06 — SEO Workflow
Writing YouTube descriptions was tedious and often skipped under time pressure. The Description Generator takes the episode title, main topics, and affiliate links, then outputs a fully formatted description with SEO keywords baked in — including auto-generated chapter timestamps.
The auto-chapters feature reads the CapCut timeline output and converts production cue timestamps into YouTube chapter markers. So if my price pop-up at 1:30 lines up with a major card pull, that becomes a chapter called 🔥 Big Pull — 1:30 automatically.
07 — Competitive Research
I built a title analysis tool directly into the SEO page so I could reverse-engineer what was working in the Pokémon TCG space without leaving the app.
You paste in a list of competitor video titles. The tool runs four analysis passes:
Counts and ranks every meaningful word across all titles. Surfaces which terms dominate top-performing content in the niche.
Finds two-word phrases ("price spike", "pack opening", "profit or") that appear repeatedly — these are your high-signal title patterns.
Identifies structural patterns: question-style titles, number-led titles, versus-framing, dollar-amount framing, etc.
Cross-references competitor keywords against my own video titles to surface topics I haven't covered that competitors are using successfully.
08 — Market Tracking
As a Pokémon TCG channel, card price movements are content. I need to know when a card spikes before everyone else does. The Watchlist page lets me track specific cards and sets, and stores price history over time.
Each card on the watchlist shows a mini sparkline chart of its price history, a percentage movement badge (green for up, red for down), and a customizable price alert threshold. If a card crosses the threshold, it surfaces with a highlighted alert badge on the dashboard.
const spark = new Chart(ctx, { type: 'line', data: { labels: priceHistory.map(p => p.date), datasets: [{ data: priceHistory.map(p => p.price), borderColor: priceDelta > 0 ? '#00f5c4' : '#e63946', borderWidth: 1.5, pointRadius: 0, tension: 0.4, fill: true, backgroundColor: 'rgba(0,245,196,0.06)' }] }, options: { plugins: { legend: { display: false } }, scales: { x: { display: false }, y: { display: false } } } });
09 — Reporting
Every Sunday I want a clean summary of the week: top performing video, total views, average CTR, biggest price movement on the watchlist, and action items for next week. The Weekly Report feature generates all of this as a downloadable Markdown file.
The action items are auto-generated based on rules: if CTR dropped week-over-week, it adds "A/B test new thumbnail style." If no upload happened for 8+ days, it adds "Urgency: upload queue is empty." This made the weekly review actually useful rather than just a vanity metrics dump.
# PokeAlgorithm Weekly Report — Week of Apr 3, 2026 ## Channel Stats - Total Views This Week: 4,218 - Average CTR: 6.4% - New Subscribers: +31 - Top Video: "Paradox Rift Box — Profit or Cooked?" ## Watchlist Alerts - 🔴 Charizard ex (SV1): +22% — CONSIDER CONTENT ## Action Items - Upload cadence: 9 days since last post. Post this week. - CTR declined 1.2% — A/B test thumbnail this cycle.
10 — UX Polish
Once the app had 7+ pages, navigation was getting slow. I added a global keyboard shortcut system so I could jump anywhere without touching the mouse. Press ? to open the shortcuts modal.
g d → Dashboard, g c → Calendar, g s → Script Builder, g w → Watchlist
Focuses the SEO keyword search bar from anywhere in the app instantly.
Opens the content calendar task creator modal without navigating away from the current page.
Displays all available shortcuts in a full-screen overlay. Press Esc to dismiss from any state.
11 — Voice Automation
tts_engine.py)Beyond the web app, I built a standalone command-line TTS engine that converts my episode scripts into AI voiceover audio files using the ElevenLabs API. This was designed to match my exact delivery style: energetic, NYC-paced, with specific hype phrases baked into the voice settings.
ElevenLabs exposes stability, similarity boost, and style parameters for each generation call. After testing, I landed on:
VOICE_SETTINGS = {
"stability": 0.35, # Lower = more energetic variation
"similarity_boost": 0.85, # Stay close to voice clone
"style": 0.60, # Expressive, not robotic
"use_speaker_boost": True
}
BRAND_OPENERS = [
"Welcome back to the Lab.",
"What's good Lab fam.",
"Lock in, welcome to the Lab."
]
The script accepts a plain text file with tagged segments and outputs numbered MP3 files plus a JSON manifest for CapCut import.
# Basic usage — generate voiceover from script python tts_engine.py --input episode_07.txt --output ./audio/ # Dry-run: validate script parsing without calling API python tts_engine.py --input episode_07.txt --dry-run # Batch mode from CSV (per-segment voice control) python tts_engine.py --csv segments.csv --output ./audio/
audio/
├── 01_intro.mp3
├── 02_price_check.mp3
├── 03_pull_reaction.mp3
├── 04_outro.mp3
└── manifest.json # Segment metadata for CapCut
The manifest.json stores each segment's filename, duration, and script text — structured so it can be dropped into a CapCut import script or referenced while editing.
--dry-run flag was critical during development. It validates all script parsing and file I/O logic without burning ElevenLabs API credits on every test run. This is a pattern I now use in any CLI tool that hits a paid API.
12 — Running It Locally
Everything runs locally on my Windows machine via WSL (Windows Subsystem for Linux) with Ubuntu. No cloud hosting, no always-on server — just a Python venv and a browser tab on localhost:8080.
Unzip the project files from the Windows Downloads folder, accessible in WSL at /mnt/c/Users/[username]/Downloads/.
WSL Ubuntu blocks system-wide pip installs (PEP 668). Solution: python3 -m venv venv && source venv/bin/activate.
With the venv active, pip install -r requirements.txt works cleanly. Includes Flask, pandas, requests, and elevenlabs.
Port 5000 conflicts with macOS AirPlay (and other services). The app runs on flask run --port 8080 — accessible at http://localhost:8080.
cd /mnt/c/Users/smanbari/Downloads/pokealgorithm-lab python3 -m venv venv source venv/bin/activate pip install -r requirements.txt flask run --port 8080
Takeaways
The biggest lesson: build for your actual workflow, not a hypothetical one. Every feature in Lab v3 came from a real pain point — a tab I kept forgetting, a calculation I kept redoing, a decision I kept making without data.
Technically, the modular file approach (separate CSVs, separate route files, standalone CLI tools) made iteration much faster than a monolithic script would have. The dry-run pattern saved API credits and made testing feel safe. And Flask — boring as it sounds — was exactly the right call. No framework overhead, just Python logic and a browser.
The channel improved too. Seeing view decay data made me rethink how I was titling videos. The CTR heatmap changed when I upload. The script builder made me more consistent with retention hooks. Building the tool made me better at the job the tool was supposed to help.
Data > luck. Always. — ThePokeAlgorithm