diff --git a/data/podcast-checklist.json b/data/podcast-checklist.json new file mode 100644 index 0000000..8202a54 --- /dev/null +++ b/data/podcast-checklist.json @@ -0,0 +1,681 @@ +{ + "checklist": { + "tasks": [ + { + "id": "verify_script", + "label": "Verify Script", + "phase": "pre" + }, + { + "id": "read_script", + "label": "Read Script", + "phase": "pre" + }, + { + "id": "record", + "label": "Record", + "phase": "pre" + }, + { + "id": "edit", + "label": "Edit", + "phase": "pre" + }, + { + "id": "mix", + "label": "Mix", + "phase": "pre" + }, + { + "id": "video_script", + "label": "Run Video Conversion Script", + "phase": "pre" + }, + { + "id": "post_spotify", + "label": "Post on Spotify", + "phase": "pre" + }, + { + "id": "update_website", + "label": "Update Website", + "phase": "post" + }, + { + "id": "send_email", + "label": "Send Email", + "phase": "post" + } + ], + "episodes": [ + { + "id": "titus-11", + "series": "Titus", + "episodeNumber": 11, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "titus-12", + "series": "Titus", + "episodeNumber": 12, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "titus-13", + "series": "Titus", + "episodeNumber": 13, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "titus-14", + "series": "Titus", + "episodeNumber": 14, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "titus-15", + "series": "Titus", + "episodeNumber": 15, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-1", + "series": "Colossians", + "episodeNumber": 1, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-2", + "series": "Colossians", + "episodeNumber": 2, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-3", + "series": "Colossians", + "episodeNumber": 3, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-4", + "series": "Colossians", + "episodeNumber": 4, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-5", + "series": "Colossians", + "episodeNumber": 5, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-6", + "series": "Colossians", + "episodeNumber": 6, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-7", + "series": "Colossians", + "episodeNumber": 7, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-8", + "series": "Colossians", + "episodeNumber": 8, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-9", + "series": "Colossians", + "episodeNumber": 9, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-10", + "series": "Colossians", + "episodeNumber": 10, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-11", + "series": "Colossians", + "episodeNumber": 11, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-12", + "series": "Colossians", + "episodeNumber": 12, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-13", + "series": "Colossians", + "episodeNumber": 13, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-14", + "series": "Colossians", + "episodeNumber": 14, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-15", + "series": "Colossians", + "episodeNumber": 15, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-16", + "series": "Colossians", + "episodeNumber": 16, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-17", + "series": "Colossians", + "episodeNumber": 17, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-18", + "series": "Colossians", + "episodeNumber": 18, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-19", + "series": "Colossians", + "episodeNumber": 19, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-20", + "series": "Colossians", + "episodeNumber": 20, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-21", + "series": "Colossians", + "episodeNumber": 21, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-22", + "series": "Colossians", + "episodeNumber": 22, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-23", + "series": "Colossians", + "episodeNumber": 23, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-24", + "series": "Colossians", + "episodeNumber": 24, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-25", + "series": "Colossians", + "episodeNumber": 25, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-26", + "series": "Colossians", + "episodeNumber": 26, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "colossians-27", + "series": "Colossians", + "episodeNumber": 27, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + }, + { + "id": "episode-mpb66z1c", + "series": "Titus -10", + "episodeNumber": 0, + "title": "", + "datePublished": "", + "expanded": false, + "tasks": { + "verify_script": false, + "read_script": false, + "record": false, + "edit": false, + "mix": false, + "video_script": false, + "post_spotify": false, + "update_website": false, + "send_email": false + } + } + ] + }, + "updatedAt": "2026-05-18T12:18:35.130Z" +} \ No newline at end of file diff --git a/podcast-checklist/Dockerfile b/podcast-checklist/Dockerfile new file mode 100644 index 0000000..eb99c1a --- /dev/null +++ b/podcast-checklist/Dockerfile @@ -0,0 +1,18 @@ +FROM node:20-alpine + +WORKDIR /app + +COPY package.json ./ +RUN npm install --production + +COPY server.js ./ +COPY public/ ./public/ + +VOLUME ["/data"] + +ENV PORT=3000 +ENV DATA_FILE=/data/progress.json + +EXPOSE 3000 + +CMD ["node", "server.js"] diff --git a/podcast-checklist/README.md b/podcast-checklist/README.md new file mode 100644 index 0000000..9b3b113 --- /dev/null +++ b/podcast-checklist/README.md @@ -0,0 +1,63 @@ +# Podcast Checklist + +Episode production tracker for *Verse by Verse with Nate*. + +## Deploy + +### 1. Build and push to GHCR + +```bash +docker build -t ghcr.io/nmemmert/podcast-checklist:latest . +docker push ghcr.io/nmemmert/podcast-checklist:latest +``` + +### 2. On the Oracle Cloud VM + +```bash +docker pull ghcr.io/nmemmert/podcast-checklist:latest +docker rm -f podcast-checklist 2>/dev/null || true +docker run -d \ + --name podcast-checklist \ + --restart unless-stopped \ + -p 3001:3000 \ + -v podcast-checklist-data:/data \ + ghcr.io/nmemmert/podcast-checklist:latest +``` + +> Uses port **3001** on the host so it doesn't clash with your existing site on 3000. +> Progress is saved to a named Docker volume (`podcast-checklist-data`) so it survives container restarts and re-deploys. + +### 3. Cloudflare DNS + +Add a CNAME record in Cloudflare: +- **Name:** `checklist` (or whatever subdomain you want) +- **Target:** `versebyversewithnate.us` +- **Proxy:** Enabled (orange cloud) + +### 4. Cloudflare Tunnel or Nginx proxy + +If you're using Nginx Proxy Manager or a Cloudflare Tunnel on the VM, add a rule to forward `checklist.versebyversewithnate.us` → `localhost:3001`. + +--- + +## Update workflow + +Same as your site: + +```bash +# Local +docker build -t ghcr.io/nmemmert/podcast-checklist:latest . +docker push ghcr.io/nmemmert/podcast-checklist:latest + +# SSH into VM +docker pull ghcr.io/nmemmert/podcast-checklist:latest +docker rm -f podcast-checklist +docker run -d \ + --name podcast-checklist \ + --restart unless-stopped \ + -p 3001:3000 \ + -v podcast-checklist-data:/data \ + ghcr.io/nmemmert/podcast-checklist:latest +``` + +Progress data is in the named volume and is **not affected** by pulling a new image. diff --git a/podcast-checklist/package.json b/podcast-checklist/package.json new file mode 100644 index 0000000..b6e1815 --- /dev/null +++ b/podcast-checklist/package.json @@ -0,0 +1,12 @@ +{ + "name": "podcast-checklist", + "version": "1.0.0", + "description": "Verse by Verse with Nate — Episode Production Checklist", + "main": "server.js", + "scripts": { + "start": "node server.js" + }, + "dependencies": { + "express": "^4.18.2" + } +} diff --git a/podcast-checklist/public/index.html b/podcast-checklist/public/index.html new file mode 100644 index 0000000..0a28f1e --- /dev/null +++ b/podcast-checklist/public/index.html @@ -0,0 +1,401 @@ + + + + + + Episode Checklist — Verse by Verse with Nate + + + + +
+ +
+
Verse by Verse with Nate
+
Production Checklist
+
Titus · Colossians
+
+
+ +
+ +
+
+
Overall Progress
+
0%
+
+
+
+
Titus — 0 / 5 episodes done
+
Colossians — 0 / 27 episodes done
+
+
+ +
+ + + +
+ +
Loading…
+
+ + + + diff --git a/podcast-checklist/server.js b/podcast-checklist/server.js new file mode 100644 index 0000000..efded97 --- /dev/null +++ b/podcast-checklist/server.js @@ -0,0 +1,43 @@ +const express = require("express"); +const fs = require("fs"); +const path = require("path"); + +const app = express(); +const PORT = process.env.PORT || 3000; +const DATA_FILE = process.env.DATA_FILE || "/data/progress.json"; + +app.use(express.json()); +app.use(express.static(path.join(__dirname, "public"))); + +// Ensure data directory and file exist +function ensureDataFile() { + const dir = path.dirname(DATA_FILE); + if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); + if (!fs.existsSync(DATA_FILE)) fs.writeFileSync(DATA_FILE, JSON.stringify({})); +} + +// GET progress +app.get("/api/progress", (req, res) => { + try { + ensureDataFile(); + const data = JSON.parse(fs.readFileSync(DATA_FILE, "utf8")); + res.json(data); + } catch (err) { + console.error("Read error:", err); + res.status(500).json({ error: "Failed to read progress" }); + } +}); + +// POST progress (full state) +app.post("/api/progress", (req, res) => { + try { + ensureDataFile(); + fs.writeFileSync(DATA_FILE, JSON.stringify(req.body, null, 2)); + res.json({ ok: true }); + } catch (err) { + console.error("Write error:", err); + res.status(500).json({ error: "Failed to save progress" }); + } +}); + +app.listen(PORT, () => console.log(`Podcast checklist running on port ${PORT}`));