This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-02-10
|
||||
@@ -0,0 +1,148 @@
|
||||
## Context
|
||||
|
||||
This change adds a light-weight, SEO-focused website to grow social discovery and conversions for:
|
||||
- YouTube channel (`youtube.com/santhoshj`)
|
||||
- Instagram (`@santhoshjanan`)
|
||||
- Podcast ("Irregular Mind") on major platforms
|
||||
|
||||
The site needs to:
|
||||
- Be fast and crawlable (SEO-first).
|
||||
- Surface "latest" content and "high-performing" videos prominently on the home page.
|
||||
- Provide clear CTAs to convert site visitors into followers/subscribers/listeners.
|
||||
- Provide analytics (target: Umami) and event tracking to measure progress against 10% month-over-month goals.
|
||||
|
||||
Constraints and unknowns:
|
||||
- Current repo appears to contain only OpenSpec artifacts (no existing website stack captured here yet).
|
||||
- Instagram "dynamic" ingestion is constrained by Meta platform rules and API availability; we should plan for a fallback.
|
||||
- "High-performing" requires a definition (views/likes/comments/watch-time) and may require YouTube Data API access.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Ship a static-first website architecture that is very fast, SEO-friendly, and easy to operate.
|
||||
- Ingest content from YouTube + podcast RSS, and support Instagram via API where feasible or via embed/fallback.
|
||||
- Provide a normalized content model usable by homepage modules (latest + featured/high-performing).
|
||||
- Provide reusable CTAs with trackable outbound clicks and conversion measurement via Umami.
|
||||
- Provide clear, testable SEO outputs: indexable pages, metadata, sitemap/robots, social sharing cards.
|
||||
|
||||
**Non-Goals:**
|
||||
- Building a full CMS/editorial workflow (no admin UI in scope initially).
|
||||
- Real-time updates (seconds/minutes). Near-real-time is not required; periodic refresh is acceptable.
|
||||
- Complex personalization/recommendation engines.
|
||||
- Replacing platform-native analytics; the goal is directional measurement for acquisition/conversion.
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1) Static-first framework and deployment
|
||||
|
||||
**Decision:** Use a static-first site generator (Astro is the default recommendation) and deploy on linode instance over docker container.
|
||||
|
||||
**Rationale:** Static output maximizes performance and SEO while minimizing operational complexity. Astro provides great HTML-by-default output and only hydrates where necessary, keeping the "light-weight" premise intact.
|
||||
|
||||
**Alternatives considered:**
|
||||
- Next.js: good ecosystem, but easier to accidentally ship heavier runtime/hydration than needed.
|
||||
- Pure static HTML/manual: too brittle and hard to keep "dynamic" content updated.
|
||||
|
||||
### 2) Content ingestion model (build-time with scheduled refresh)
|
||||
|
||||
**Decision:** Fetch and normalize social content at build time (SSG) into a local data cache (JSON) that drives rendering. Refresh content on a schedule (cron) and on-demand (manual trigger).
|
||||
|
||||
**Rationale:** Keeps runtime simple and fast; avoids exposing API keys to the browser; avoids rate-limit issues on every pageview.
|
||||
|
||||
**Mechanics:**
|
||||
- A build step runs `fetch-content` to produce a normalized dataset (e.g., `content/cache/*.json`).
|
||||
- CI triggers rebuild on a schedule (e.g., hourly or daily) and after new uploads (manual trigger).
|
||||
|
||||
**Alternatives considered:**
|
||||
- Runtime serverless fetch: more moving parts, harder caching story, slower page loads, and higher risk of API failures impacting the site.
|
||||
- Client-side fetch: worst for SEO and exposes third-party dependence to users.
|
||||
|
||||
### 3) Sources and fallbacks (YouTube, Instagram, podcast)
|
||||
|
||||
**YouTube**
|
||||
- **Decision:** Prefer YouTube Data API (v3) when available to obtain reliable metadata and performance stats (views/likes) for "high-performing".
|
||||
- **Fallback:** Use the channel RSS feed to list latest videos if API keys are not available; in this mode, "high-performing" becomes manually curated or based on a static list.
|
||||
|
||||
**Instagram**
|
||||
- **Decision:** Start with an embed-first approach for Instagram posts (oEmbed or platform embeds) and treat API-based ingestion as optional Phase 2.
|
||||
- **Rationale:** Meta API access is frequently the longest pole (app review, token refresh, account type constraints). Embeds still allow reuse of posts without deep integration.
|
||||
|
||||
**Podcast**
|
||||
- **Decision:** Use the podcast RSS feed as the canonical source; parse episodes into the normalized model. Link out to platform destinations (Apple/Spotify/etc).
|
||||
|
||||
### 4) Normalized content model and ranking
|
||||
|
||||
**Decision:** Define a unified content schema for the site (regardless of source), e.g.:
|
||||
- `id`, `source` (`youtube|instagram|podcast`), `url`
|
||||
- `title`, `description`, `publishedAt`
|
||||
- `thumbnail`, `tags/categories`
|
||||
- `metrics` (optional: `views`, `likes`, `comments`, `duration`)
|
||||
- `featured` (manual override)
|
||||
|
||||
**High-performing definition:**
|
||||
- Default: YouTube videos ranked by `views` (optionally recency-weighted), with a manual override list for editorial selection.
|
||||
- Instagram and podcast "high-performing" starts as manual until reliable metrics are available.
|
||||
|
||||
### 5) Information architecture and SEO surface
|
||||
|
||||
**Decision:** Create a small set of indexable pages with stable URLs:
|
||||
- `/` home (latest + featured/high-performing + channel highlights + CTAs)
|
||||
- `/videos` (YouTube list/detail, or list only initially)
|
||||
- `/podcast` (episode list)
|
||||
- `/about` (channel/personal bio, links)
|
||||
|
||||
**SEO outputs:**
|
||||
- Per-page metadata (title/description), canonical URLs
|
||||
- Open Graph + Twitter cards
|
||||
- `sitemap.xml` and `robots.txt`
|
||||
- Structured data (`JSON-LD`) for Video/Podcast where appropriate
|
||||
|
||||
### 6) Conversion CTAs and event tracking
|
||||
|
||||
**Decision:** Implement CTAs as a reusable component that:
|
||||
- Renders platform-specific primary actions (YouTube subscribe, Instagram follow, podcast listen)
|
||||
- Uses outbound links that include UTM parameters
|
||||
- Emits Umami events for `cta_click` with dimensions like `platform`, `placement`, `target`
|
||||
|
||||
### 7) Analytics (Umami) and measurement plan
|
||||
|
||||
**Decision:** Use Umami for pageviews + custom events.
|
||||
|
||||
**Events (initial):**
|
||||
- `cta_click` (platform, placement)
|
||||
- `outbound_click` (domain, placement)
|
||||
- Optional: `video_play` / `episode_play` if embeds support reliable hooks (may be limited)
|
||||
|
||||
**Reporting targets:**
|
||||
- Traffic by source (SEO vs social referrers)
|
||||
- CTA click-through rate by placement
|
||||
- Growth correlation: site referrals to channel follows (approximate; platform attribution is imperfect)
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [Instagram API complexity] → Start embed-first; treat API ingestion as optional and document requirements for later.
|
||||
- [YouTube API keys / quotas] → Cache results; minimize calls; RSS fallback; allow manual "featured" list.
|
||||
- [“High-performing” ambiguity] → Specify a clear initial metric (views) and allow manual overrides; refine after baseline data.
|
||||
- [Stale content due to build-time fetch] → Scheduled rebuilds; manual trigger; show "Last updated" timestamp.
|
||||
- [SEO requires real content depth] → Add dedicated indexable pages (videos/podcast/about) and ensure metadata + structured data are correct.
|
||||
- [Analytics privacy expectations] → Keep tracking minimal; avoid PII; document what is tracked.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. Implement the static-first site skeleton and deploy a minimal version (home + about + analytics).
|
||||
2. Add ingestion for podcast RSS and YouTube (RSS first, upgrade to API when keys are available).
|
||||
3. Add homepage modules (latest + featured/high-performing) and CTA placements.
|
||||
4. Add SEO hardening (sitemap/robots/canonicals/structured data) and validate indexing.
|
||||
5. Add Instagram embed module (and/or API ingestion if chosen).
|
||||
6. Iterate on ranking and content presentation based on Umami data.
|
||||
|
||||
Rollback:
|
||||
- Revert deploy to previous static build; ingestion failures should fail closed (serve last good cached dataset).
|
||||
|
||||
## Open Questions
|
||||
|
||||
- Hosting target: Cloudflare Pages vs Netlify vs Vercel (affects cron/rebuild approach and any serverless options).
|
||||
- Do you want a custom domain (and which), and should the site target a personal brand name vs channel name?
|
||||
- Is a YouTube Data API key available, or should we start with RSS-only for v1?
|
||||
- Should Instagram be embed-only for v1, or do you want to pursue API ingestion (which may require a business account/app setup)?
|
||||
- Preferred measurement: which CTAs matter most (YouTube subs vs Instagram follows vs podcast listens) and where should they be placed (hero, sticky, end of sections)?
|
||||
@@ -0,0 +1,30 @@
|
||||
## Why
|
||||
|
||||
Grow channel discovery and conversions by giving search engines a fast, crawlable home for SanthoshJ content (YouTube, Instagram, podcast) with clear next actions. This is needed now to drive at least 10% month-over-month growth in both followers and engagement across platforms.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Add a light-weight, SEO-focused website that aggregates and showcases content across YouTube, Instagram, and the Irregular Mind podcast.
|
||||
- Dynamically display newest content and highlight high-performing videos prominently on the home page.
|
||||
- Add prominent conversion CTAs (subscribe/follow/listen) throughout the site to drive measurable conversions from site visits.
|
||||
- Add analytics instrumentation (target: Umami) to measure traffic sources, content performance, and conversion events.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `social-content-aggregation`: Pull and normalize content metadata from YouTube, Instagram, and the podcast feed so the website can render "latest" and "featured" content.
|
||||
- `homepage-content-modules`: Define and render homepage sections for newest content, high-performing videos, and channel/podcast highlights (with graceful empty/loading states).
|
||||
- `seo-content-surface`: Provide SEO fundamentals (metadata, indexable pages, sitemaps) so content can be discovered via search and shared cleanly on social.
|
||||
- `conversion-ctas`: Provide reusable, configurable CTAs (YouTube subscribe, Instagram follow, podcast listen) and trackable outbound links.
|
||||
- `analytics-umami`: Instrument pageviews and key events (CTA clicks, outbound conversions) and support configuration for Umami.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
<!-- None (no existing specs yet) -->
|
||||
|
||||
## Impact
|
||||
|
||||
- Website codebase: new pages/components for homepage, content listings, and SEO metadata; additional build/runtime configuration to support dynamic content ingestion.
|
||||
- External integrations: YouTube (Data API or feeds), Instagram (embed/Graph/API constraints), podcast RSS feed parsing; may require API keys and rate-limit handling.
|
||||
- Analytics/deployment: add Umami script/config and define event taxonomy; ensure privacy expectations and consistent environments (dev/staging/prod).
|
||||
@@ -0,0 +1,33 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Umami pageview tracking
|
||||
When Umami is enabled by configuration, the site MUST load Umami tracking on all indexable pages and MUST record pageviews.
|
||||
|
||||
When Umami is disabled or not configured, the site MUST still function and MUST NOT error in the browser due to missing analytics.
|
||||
|
||||
#### Scenario: Umami enabled
|
||||
- **WHEN** Umami is enabled by configuration
|
||||
- **THEN** the site includes the Umami script on `/`, `/videos`, `/podcast`, and `/about`
|
||||
|
||||
#### Scenario: Umami disabled
|
||||
- **WHEN** Umami is not configured
|
||||
- **THEN** the site renders normally and no analytics script is required
|
||||
|
||||
### Requirement: Custom event tracking
|
||||
When Umami is enabled, the site MUST support custom event emission for:
|
||||
- `cta_click`
|
||||
- `outbound_click`
|
||||
|
||||
Each emitted event MUST include enough properties to segment reports by platform and placement when applicable.
|
||||
|
||||
#### Scenario: Emit outbound click event
|
||||
- **WHEN** a user clicks a non-CTA outbound link from the homepage
|
||||
- **THEN** the system emits an `outbound_click` event with a property identifying the destination domain
|
||||
|
||||
### Requirement: Environment configuration
|
||||
The site MUST support configuration of Umami parameters (at minimum: website ID and script URL) without requiring code changes.
|
||||
|
||||
#### Scenario: Configure Umami via environment
|
||||
- **WHEN** Umami configuration values are provided via environment or config file
|
||||
- **THEN** the site uses those values to initialize analytics without modifying source code
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Reusable CTA component
|
||||
The site MUST implement CTAs as a reusable component that can render at least the following actions:
|
||||
- YouTube subscribe action (linking to the channel)
|
||||
- Instagram follow action (linking to the profile)
|
||||
- Podcast listen action (linking to a designated destination)
|
||||
|
||||
Each CTA MUST be configurable with:
|
||||
- `platform` (`youtube`, `instagram`, `podcast`)
|
||||
- `placement` (e.g., `hero`, `module_header`, `footer`)
|
||||
- destination `url`
|
||||
|
||||
#### Scenario: Rendering a YouTube subscribe CTA
|
||||
- **WHEN** the homepage includes a CTA with `platform: youtube`
|
||||
- **THEN** the site renders a visible action that links to the configured YouTube channel destination URL
|
||||
|
||||
### Requirement: Trackable outbound links
|
||||
CTA outbound links MUST support appending UTM parameters so traffic can be attributed in downstream analytics.
|
||||
|
||||
#### Scenario: UTM parameters applied
|
||||
- **WHEN** a CTA is configured with UTM parameters
|
||||
- **THEN** the rendered outbound link includes the UTM query parameters in its URL
|
||||
|
||||
### Requirement: CTA click event emission
|
||||
CTA clicks MUST emit an analytics event with at least:
|
||||
- event name `cta_click`
|
||||
- `platform`
|
||||
- `placement`
|
||||
- `target` (destination URL or a stable identifier)
|
||||
|
||||
#### Scenario: User clicks CTA
|
||||
- **WHEN** a user clicks an Instagram follow CTA in the hero placement
|
||||
- **THEN** the system emits a `cta_click` event with `platform=instagram` and `placement=hero`
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Homepage modules and ordering
|
||||
The homepage MUST render distinct content modules that include:
|
||||
- newest content
|
||||
- high-performing videos
|
||||
- channel/podcast highlights
|
||||
- prominent conversion CTAs
|
||||
|
||||
The homepage MUST render modules in a deterministic order configured by the site (not dependent on network timing).
|
||||
|
||||
#### Scenario: Homepage render with available data
|
||||
- **WHEN** content data is available in the cache
|
||||
- **THEN** the homepage renders the newest module and the high-performing videos module in the configured order
|
||||
|
||||
### Requirement: Newest content module
|
||||
The system MUST compute a "newest" feed across sources by sorting normalized items by `publishedAt` descending.
|
||||
|
||||
The system MUST support filtering and limiting the number of items displayed for the newest module.
|
||||
|
||||
#### Scenario: Mixed-source newest list
|
||||
- **WHEN** the cached dataset contains YouTube, Instagram, and podcast items with different publish dates
|
||||
- **THEN** the newest module lists items ordered by `publishedAt` descending regardless of source
|
||||
|
||||
### Requirement: High-performing YouTube videos module
|
||||
When `metrics.views` is available for YouTube items, the system MUST compute "high-performing" videos by ranking videos by `metrics.views` (descending) with an optional manual override list.
|
||||
|
||||
When `metrics.views` is not available, the system MUST render the high-performing module using a manual curated list and MUST NOT fail the page render.
|
||||
|
||||
#### Scenario: Views-based ranking available
|
||||
- **WHEN** YouTube items include `metrics.views`
|
||||
- **THEN** the high-performing module shows videos ranked by views, unless a manual override list is configured
|
||||
|
||||
#### Scenario: Views-based ranking unavailable
|
||||
- **WHEN** YouTube items do not include `metrics.views`
|
||||
- **THEN** the high-performing module renders using a manual curated list and the page still loads successfully
|
||||
|
||||
### Requirement: Graceful empty and error states
|
||||
If a module has no content to display, the homepage MUST render a non-broken empty state for that module and MUST still render the rest of the page.
|
||||
|
||||
#### Scenario: No Instagram items available
|
||||
- **WHEN** the cached dataset contains no Instagram items
|
||||
- **THEN** the Instagram-related module renders an empty state and the homepage still renders other modules
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Indexable pages
|
||||
The site MUST provide indexable HTML pages for:
|
||||
- home (`/`)
|
||||
- videos (`/videos`)
|
||||
- podcast (`/podcast`)
|
||||
- about (`/about`)
|
||||
|
||||
These pages MUST be server-rendered or statically generated HTML suitable for search engine crawling (not client-rendered only).
|
||||
|
||||
#### Scenario: Crawling the home page
|
||||
- **WHEN** a crawler requests `/`
|
||||
- **THEN** the server returns an HTML document containing the homepage content modules and metadata
|
||||
|
||||
### Requirement: Metadata and canonical URLs
|
||||
Each indexable page MUST define:
|
||||
- a document title
|
||||
- a meta description
|
||||
- a canonical URL
|
||||
|
||||
#### Scenario: Page metadata is present
|
||||
- **WHEN** a crawler requests `/videos`
|
||||
- **THEN** the HTML contains a `<title>`, a meta description, and a canonical URL for `/videos`
|
||||
|
||||
### Requirement: Social sharing cards
|
||||
The site MUST provide Open Graph and Twitter card metadata for indexable pages so shared links render a preview.
|
||||
|
||||
#### Scenario: Sharing the home page
|
||||
- **WHEN** a social crawler requests `/`
|
||||
- **THEN** the response includes Open Graph and Twitter card tags with a title, description, and image when available
|
||||
|
||||
### Requirement: Sitemap and robots
|
||||
The site MUST provide:
|
||||
- `sitemap.xml` enumerating indexable pages
|
||||
- `robots.txt` that allows indexing of indexable pages
|
||||
|
||||
#### Scenario: Sitemap is available
|
||||
- **WHEN** a crawler requests `/sitemap.xml`
|
||||
- **THEN** the server returns an XML sitemap listing `/`, `/videos`, `/podcast`, and `/about`
|
||||
|
||||
### Requirement: Structured data
|
||||
The site MUST support structured data (JSON-LD) for Video and Podcast content when detail pages exist, and MUST ensure the JSON-LD is valid JSON.
|
||||
|
||||
#### Scenario: Video structured data present
|
||||
- **WHEN** a video detail page exists and is requested
|
||||
- **THEN** the HTML includes JSON-LD describing the video using a recognized schema type
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Normalized content items
|
||||
The system MUST normalize all ingested items (YouTube videos, Instagram posts, podcast episodes) into a single internal schema so the website can render them consistently.
|
||||
|
||||
The normalized item MUST include at minimum:
|
||||
- `id` (stable within its source)
|
||||
- `source` (`youtube`, `instagram`, or `podcast`)
|
||||
- `url`
|
||||
- `title`
|
||||
- `publishedAt` (ISO-8601)
|
||||
- `thumbnailUrl` (optional)
|
||||
|
||||
#### Scenario: Normalizing a YouTube video
|
||||
- **WHEN** the system ingests a YouTube video item
|
||||
- **THEN** it produces a normalized item containing `id`, `source: youtube`, `url`, `title`, and `publishedAt`
|
||||
|
||||
#### Scenario: Normalizing a podcast episode
|
||||
- **WHEN** the system ingests a podcast RSS episode
|
||||
- **THEN** it produces a normalized item containing `id`, `source: podcast`, `url`, `title`, and `publishedAt`
|
||||
|
||||
### Requirement: YouTube ingestion with stats when available
|
||||
The system MUST support ingesting YouTube videos for channel `youtube.com/santhoshj`.
|
||||
|
||||
When a YouTube API key is configured, the system MUST ingest video metadata and MUST ingest view count (and MAY ingest likes/comments if available) so "high-performing" can be computed.
|
||||
|
||||
When no YouTube API key is configured, the system MUST still ingest latest videos using a non-authenticated mechanism (for example, channel RSS) but MUST omit performance stats.
|
||||
|
||||
#### Scenario: API key configured
|
||||
- **WHEN** a YouTube API key is configured
|
||||
- **THEN** the system ingests video metadata and includes `metrics.views` for each ingested video when available from the API
|
||||
|
||||
#### Scenario: No API key configured
|
||||
- **WHEN** no YouTube API key is configured
|
||||
- **THEN** the system ingests latest videos and does not require `metrics.views` to be present
|
||||
|
||||
### Requirement: Podcast RSS ingestion
|
||||
The system MUST ingest the Irregular Mind podcast RSS feed and produce normalized items representing podcast episodes.
|
||||
|
||||
#### Scenario: RSS feed fetch succeeds
|
||||
- **WHEN** the system fetches the podcast RSS feed successfully
|
||||
- **THEN** it produces one normalized item per episode with `source: podcast`
|
||||
|
||||
### Requirement: Instagram content support via embed-first approach
|
||||
The system MUST support representing Instagram posts for `@santhoshjanan` in the site content surface.
|
||||
|
||||
If API-based ingestion is not configured/available, the system MUST support an embed-first representation where the normalized item contains a `url` to the Instagram post and any additional embed metadata needed by the renderer.
|
||||
|
||||
#### Scenario: Embed-first mode
|
||||
- **WHEN** Instagram API ingestion is not configured
|
||||
- **THEN** the system provides normalized Instagram items that contain a public post `url` suitable for embedding
|
||||
|
||||
### Requirement: Refresh and caching
|
||||
The system MUST cache the latest successful ingestion output and MUST serve the cached data to the site renderer.
|
||||
|
||||
The system MUST support periodic refresh on a schedule (at minimum daily) and MUST support a manual refresh trigger.
|
||||
|
||||
On ingestion failure, the system MUST continue serving the most recent cached data.
|
||||
|
||||
#### Scenario: Scheduled refresh fails
|
||||
- **WHEN** a scheduled refresh run fails to fetch one or more sources
|
||||
- **THEN** the site continues to use the most recent successfully cached dataset
|
||||
|
||||
#### Scenario: Manual refresh requested
|
||||
- **WHEN** a manual refresh is triggered
|
||||
- **THEN** the system attempts ingestion immediately and updates the cache if ingestion succeeds
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
## 1. Repo And Tooling Setup
|
||||
|
||||
- [x] 1.1 Choose the site framework and initialize the project skeleton (Astro preferred) with TypeScript support
|
||||
- [x] 1.2 Add a consistent config approach for secrets and runtime settings (env vars for API keys, Umami settings, base URL)
|
||||
- [x] 1.3 Add basic quality gates (lint/format, typecheck) and a minimal CI workflow
|
||||
- [x] 1.4 Add Docker build/run for deployment to the Linode instance (multi-stage build, static output served by nginx or equivalent)
|
||||
|
||||
## 2. Content Model And Cache
|
||||
|
||||
- [x] 2.1 Define the normalized content item schema (id, source, url, title, publishedAt, thumbnailUrl, optional metrics/views)
|
||||
- [x] 2.2 Implement a content cache file format and location (e.g., `content/cache/*.json`) plus a "last updated" timestamp
|
||||
- [x] 2.3 Implement cache read utilities used by page rendering, including source filtering and limit controls
|
||||
- [x] 2.4 Implement "serve last good cache on failure" behavior in the ingestion workflow (write temp then swap)
|
||||
|
||||
## 3. YouTube Ingestion
|
||||
|
||||
- [x] 3.1 Implement YouTube ingestion via RSS as the baseline (latest videos, no metrics)
|
||||
- [x] 3.2 Implement optional YouTube Data API ingestion when `YOUTUBE_API_KEY` is configured (include `metrics.views`)
|
||||
- [x] 3.3 Implement configurable channel identifier for `youtube.com/santhoshj` (document required values)
|
||||
- [x] 3.4 Add a small set of local fixtures/tests for parsing and normalization (RSS and API response samples)
|
||||
|
||||
## 4. Podcast Ingestion
|
||||
|
||||
- [x] 4.1 Add configuration for the Irregular Mind podcast RSS URL
|
||||
- [x] 4.2 Implement podcast RSS fetch + parse to normalized items (one item per episode)
|
||||
- [x] 4.3 Ensure episode URLs and publish dates normalize correctly across common RSS date formats
|
||||
- [x] 4.4 Add fixtures/tests for RSS parsing and normalization
|
||||
|
||||
## 5. Instagram Support (Embed-First)
|
||||
|
||||
- [x] 5.1 Define the minimal normalized representation for Instagram items in embed-first mode (post URL + optional thumbnail)
|
||||
- [x] 5.2 Implement a configuration-driven list of Instagram post URLs (or a simple feed source) for v1
|
||||
- [x] 5.3 Implement the renderer for Instagram embeds with a graceful empty state when no items exist
|
||||
- [x] 5.4 Document a Phase 2 path for API-based ingestion (constraints, account/app requirements, token refresh)
|
||||
|
||||
## 6. Build-Time Refresh And Scheduling
|
||||
|
||||
- [x] 6.1 Implement a `fetch-content` script/command that ingests all sources and writes the cache
|
||||
- [x] 6.2 Add a manual refresh mechanism for ops (document how to run on the Linode host)
|
||||
- [x] 6.3 Add a scheduled refresh plan (cron on Linode or CI scheduler) and document frequency (daily minimum)
|
||||
- [x] 6.4 Add logging for ingestion runs (success/failure per source, cache write result)
|
||||
|
||||
## 7. Pages And SEO Surface
|
||||
|
||||
- [x] 7.1 Implement `/` home page scaffold with deterministic module order
|
||||
- [x] 7.2 Implement `/videos`, `/podcast`, and `/about` indexable pages (static/SSR HTML, not client-only)
|
||||
- [x] 7.3 Add per-page metadata: title, description, canonical URL
|
||||
- [x] 7.4 Add Open Graph + Twitter card tags for indexable pages
|
||||
- [x] 7.5 Implement `sitemap.xml` and `robots.txt` (include `/`, `/videos`, `/podcast`, `/about`)
|
||||
- [x] 7.6 Add structured data (JSON-LD) scaffolding for Video/Podcast where detail pages exist (ensure valid JSON)
|
||||
|
||||
## 8. Homepage Modules
|
||||
|
||||
- [x] 8.1 Implement "newest content" feed (sort by `publishedAt` desc across sources, configurable limits)
|
||||
- [x] 8.2 Implement "high-performing videos" module using `metrics.views` when present
|
||||
- [x] 8.3 Implement a manual override/curation list for featured/high-performing videos
|
||||
- [x] 8.4 Implement graceful empty/error states for each module (page never breaks if a module has no data)
|
||||
|
||||
## 9. CTAs And Outbound Tracking
|
||||
|
||||
- [x] 9.1 Implement a reusable CTA component with `platform`, `placement`, and destination `url`
|
||||
- [x] 9.2 Add default destinations for YouTube channel, Instagram profile, and podcast listen target(s)
|
||||
- [x] 9.3 Add UTM parameter support for CTA outbound links
|
||||
- [x] 9.4 Define CTA placements on homepage (hero + at least one additional placement) and ensure they are visually prominent
|
||||
|
||||
## 10. Umami Analytics
|
||||
|
||||
- [x] 10.1 Add Umami configuration (website ID + script URL) via environment/config without code changes per environment
|
||||
- [x] 10.2 Load Umami on all indexable pages when enabled; ensure site works when disabled
|
||||
- [x] 10.3 Implement `cta_click` event emission with `platform`, `placement`, and `target`
|
||||
- [x] 10.4 Implement `outbound_click` event emission with destination domain/identifier
|
||||
|
||||
## 11. Verification And Launch
|
||||
|
||||
- [x] 11.1 Validate SEO basics locally (view-source contains metadata; sitemap/robots served correctly)
|
||||
- [x] 11.2 Validate performance basics (no blocking API calls at runtime; pages render from cache)
|
||||
- [x] 11.3 Validate analytics in a staging environment (events show up in Umami; disabled mode has no errors)
|
||||
- [x] 11.4 Document deployment steps for the Linode Docker setup (build, run, refresh schedule, rollback)
|
||||
Reference in New Issue
Block a user