Rename project: youtube-summarizer → recap

The product was always more than YouTube — it handles podcast feeds
too, and the upcoming multi-provider work makes it less Gemini-
specific. New name: Recap.

This is a coordinated identity change across:

  • StartOS package id: youtube-summarizer → recap
    (manifest.id; the .s9pk filename, Docker image namespace, and
    install path under StartOS all derive from this automatically)
  • Display name: "YouTube Summarizer" → "Recap"
    (manifest title, activation screen heading, page <title>, console
    log on boot, i18n strings, ABOUT.md, Dockerfile header,
    docker_entrypoint banner)
  • Keysat product slug: youtube-summarizer → recap
    (server/license.js PRODUCT_SLUG; frontend fallback strings)
  • Daemon subscription id: youtube-summarizer-sub → recap-sub
  • Env var prefix: YT_SUMMARIZER_* → RECAP_*
    (LICENSE_KEY, LICENSE_KEY_PATH, MAX_OFFLINE_DAYS,
    VALIDATE_INTERVAL_MS)
  • localStorage keys: yt-summarizer-* → recap-*
    (gemini-key, activation-skipped, clips)
  • Library export filename: youtube-summarizer-library.json →
    recap-library.json
  • npm package names: youtube-summarizer-{startos,server} → recap-*
  • Deploy paths: youtube-summarizer_x86_64.s9pk → recap_x86_64.s9pk
    (default values in bin/deploy.sh; .deploy.env on dev machine
    needs the same update before next push)
  • Self-hosted registry directory: startos-registry/packages/
    youtube-summarizer → .../recap (with package.json + INSTRUCTIONS
    rewritten)

What does NOT change:
  • Filesystem repo path (still /Users/.../youtube-summarizer/)
  • Git history / commit messages
  • Existing version files in startos/versions/ (kept as-is — the
    version chain belongs to the package's own history regardless of
    its display name)

User-side follow-ups required:
  1. Create "recap" product in Keysat admin, set up Core/Pro tier
     policies (same entitlements as before), mint a fresh test
     license. Old "youtube-summarizer" licenses won't activate
     against the new slug.
  2. Update .deploy.env (gitignored) so FILEBROWSER_PATH and
     REGISTRY_PUBLIC_URL point at recap_x86_64.s9pk.

StartOS will treat this as a brand-new app on install — existing
youtube-summarizer installs will not auto-migrate (acknowledged
intentional given no real users yet).
This commit is contained in:
Keysat
2026-05-08 13:35:27 -05:00
parent 1aaa7a453a
commit 9282440143
23 changed files with 59 additions and 59 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
# ───────────────────────────────────────────────────────── # ─────────────────────────────────────────────────────────
# YouTube Summarizer — StartOS 0.4 Docker image # Recap — StartOS 0.4 Docker image
# #
# Includes: Node.js 20, Python 3, yt-dlp, ffmpeg # Includes: Node.js 20, Python 3, yt-dlp, ffmpeg
# #
+1 -1
View File
@@ -1,4 +1,4 @@
# YouTube Summarizer # Recap
Download, transcribe, and summarize YouTube videos and podcast episodes using Google Gemini AI. Download, transcribe, and summarize YouTube videos and podcast episodes using Google Gemini AI.
+7 -7
View File
@@ -13,10 +13,10 @@
# START9_SERVER — your Start9 server, e.g. https://immense-voyage.local:62185 # START9_SERVER — your Start9 server, e.g. https://immense-voyage.local:62185
# #
# Optional config (sensible defaults): # Optional config (sensible defaults):
# FILEBROWSER_PATH — path on FileBrowser to overwrite. Default: /websites/packages/youtube-summarizer_x86_64.s9pk # FILEBROWSER_PATH — path on FileBrowser to overwrite. Default: /websites/packages/recap_x86_64.s9pk
# REGISTRY_URL — registry JSON-RPC URL. Default: https://registry.satsflows.com # REGISTRY_URL — registry JSON-RPC URL. Default: https://registry.satsflows.com
# REGISTRY_PUBLIC_URL — public .s9pk URL registered with start-cli. # REGISTRY_PUBLIC_URL — public .s9pk URL registered with start-cli.
# Default: https://files.satsflows.com/youtube-summarizer_x86_64.s9pk # Default: https://files.satsflows.com/recap_x86_64.s9pk
set -euo pipefail set -euo pipefail
@@ -35,11 +35,11 @@ fi
: "${FILEBROWSER_PASS:?FILEBROWSER_PASS is required}" : "${FILEBROWSER_PASS:?FILEBROWSER_PASS is required}"
: "${START9_SERVER:?START9_SERVER is required (e.g. https://immense-voyage.local:62185)}" : "${START9_SERVER:?START9_SERVER is required (e.g. https://immense-voyage.local:62185)}"
FILEBROWSER_PATH="${FILEBROWSER_PATH:-/websites/packages/youtube-summarizer_x86_64.s9pk}" FILEBROWSER_PATH="${FILEBROWSER_PATH:-/websites/packages/recap_x86_64.s9pk}"
REGISTRY_URL="${REGISTRY_URL:-https://registry.satsflows.com}" REGISTRY_URL="${REGISTRY_URL:-https://registry.satsflows.com}"
REGISTRY_PUBLIC_URL="${REGISTRY_PUBLIC_URL:-https://files.satsflows.com/youtube-summarizer_x86_64.s9pk}" REGISTRY_PUBLIC_URL="${REGISTRY_PUBLIC_URL:-https://files.satsflows.com/recap_x86_64.s9pk}"
S9PK_FILE="$PROJECT_ROOT/youtube-summarizer_x86_64.s9pk" S9PK_FILE="$PROJECT_ROOT/recap_x86_64.s9pk"
if [ ! -f "$S9PK_FILE" ]; then if [ ! -f "$S9PK_FILE" ]; then
echo "X $S9PK_FILE not found. Run 'make x86' first." >&2 echo "X $S9PK_FILE not found. Run 'make x86' first." >&2
@@ -56,7 +56,7 @@ else
CURRENT_VERSION="unknown" CURRENT_VERSION="unknown"
fi fi
echo "==> Deploying youtube-summarizer $CURRENT_VERSION" echo "==> Deploying recap $CURRENT_VERSION"
echo " source : $S9PK_FILE" echo " source : $S9PK_FILE"
echo " upload : $FILEBROWSER_URL$FILEBROWSER_PATH" echo " upload : $FILEBROWSER_URL$FILEBROWSER_PATH"
echo " public : $REGISTRY_PUBLIC_URL" echo " public : $REGISTRY_PUBLIC_URL"
@@ -106,4 +106,4 @@ curl -fsS -X POST "$REGISTRY_URL/rpc/v0" \
-o /dev/null -o /dev/null
echo "" echo ""
echo "==> Done. youtube-summarizer $CURRENT_VERSION is live." echo "==> Done. recap $CURRENT_VERSION is live."
+1 -1
View File
@@ -41,7 +41,7 @@ export DATA_DIR="$DATA_DIR"
export PORT="${PORT:-3001}" export PORT="${PORT:-3001}"
export HOSTNAME="0.0.0.0" export HOSTNAME="0.0.0.0"
echo "Starting YouTube Summarizer..." echo "Starting Recap..."
echo " yt-dlp: $(yt-dlp --version 2>/dev/null || echo 'not found')" echo " yt-dlp: $(yt-dlp --version 2>/dev/null || echo 'not found')"
echo " ffmpeg: $(ffmpeg -version 2>/dev/null | head -1 || echo 'not found')" echo " ffmpeg: $(ffmpeg -version 2>/dev/null | head -1 || echo 'not found')"
echo " Data: $DATA_DIR" echo " Data: $DATA_DIR"
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"name": "youtube-summarizer-startos", "name": "recap-startos",
"private": true, "private": true,
"scripts": { "scripts": {
"build": "rm -rf ./javascript && ncc build startos/index.ts -o ./javascript", "build": "rm -rf ./javascript && ncc build startos/index.ts -o ./javascript",
+15 -15
View File
@@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>YouTube Transcript Summarizer</title> <title>Recap</title>
<link rel="icon" type="image/png" href="/assets/icon.png"> <link rel="icon" type="image/png" href="/assets/icon.png">
<style> <style>
* { box-sizing: border-box; margin: 0; padding: 0; } * { box-sizing: border-box; margin: 0; padding: 0; }
@@ -1211,7 +1211,7 @@
// ── State ──────────────────────────────────────────────────────────────── // ── State ────────────────────────────────────────────────────────────────
const state = { const state = {
url: "", url: "",
apiKey: localStorage.getItem("yt-summarizer-gemini-key") || "", apiKey: localStorage.getItem("recap-gemini-key") || "",
hasServerKey: false, // will be set by health check hasServerKey: false, // will be set by health check
lanMode: null, // null = unknown, true = home, false = traveling lanMode: null, // null = unknown, true = home, false = traveling
serverStatus: "connecting", // "connected" | "sleeping" | "disconnected" | "connecting" serverStatus: "connecting", // "connected" | "sleeping" | "disconnected" | "connecting"
@@ -1275,7 +1275,7 @@
entitlements: [], entitlements: [],
expiresAt: null, expiresAt: null,
isTrial: false, isTrial: false,
productSlug: "youtube-summarizer", productSlug: "recap",
keysatBaseUrl: "", keysatBaseUrl: "",
}, },
licenseActivating: false, licenseActivating: false,
@@ -1284,7 +1284,7 @@
// Free tier: once dismissed, the activation screen no longer // Free tier: once dismissed, the activation screen no longer
// hard-gates the UI. Persisted so returning unlicensed users land // hard-gates the UI. Persisted so returning unlicensed users land
// straight in the app. // straight in the app.
activationSkipped: localStorage.getItem("yt-summarizer-activation-skipped") === "1", activationSkipped: localStorage.getItem("recap-activation-skipped") === "1",
}; };
const MODELS = ["gemini-3.1-pro-preview", "gemini-3-pro-preview", "gemini-3-flash-preview"]; const MODELS = ["gemini-3.1-pro-preview", "gemini-3-pro-preview", "gemini-3-flash-preview"];
@@ -1624,7 +1624,7 @@
return ` return `
<div class="activation-screen"> <div class="activation-screen">
<div class="activation-card"> <div class="activation-card">
<h1>YouTube Summarizer</h1> <h1>Recap</h1>
<p class="activation-sub"> <p class="activation-sub">
${loading ${loading
? "Checking license…" ? "Checking license…"
@@ -1651,7 +1651,7 @@
</button> </button>
</div> </div>
<div class="activation-meta"> <div class="activation-meta">
Product: <strong>${escHtml(lic.productSlug || "youtube-summarizer")}</strong> Product: <strong>${escHtml(lic.productSlug || "recap")}</strong>
${lic.keysatBaseUrl ? ` &middot; Issuer: <strong>${escHtml(lic.keysatBaseUrl.replace(/^https?:\/\//, ""))}</strong>` : ""} ${lic.keysatBaseUrl ? ` &middot; Issuer: <strong>${escHtml(lic.keysatBaseUrl.replace(/^https?:\/\//, ""))}</strong>` : ""}
</div> </div>
`} `}
@@ -1662,7 +1662,7 @@
function dismissActivation() { function dismissActivation() {
state.activationSkipped = true; state.activationSkipped = true;
try { localStorage.setItem("yt-summarizer-activation-skipped", "1"); } catch {} try { localStorage.setItem("recap-activation-skipped", "1"); } catch {}
render(); render();
} }
@@ -1730,7 +1730,7 @@
function showActivationScreen() { function showActivationScreen() {
// Take user back to the activation modal (e.g. from an upgrade banner). // Take user back to the activation modal (e.g. from an upgrade banner).
state.activationSkipped = false; state.activationSkipped = false;
try { localStorage.removeItem("yt-summarizer-activation-skipped"); } catch {} try { localStorage.removeItem("recap-activation-skipped"); } catch {}
render(); render();
} }
@@ -2057,7 +2057,7 @@
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
const a = document.createElement("a"); const a = document.createElement("a");
a.href = url; a.href = url;
a.download = "youtube-summarizer-library.json"; a.download = "recap-library.json";
a.click(); a.click();
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
showToast("Library exported!", "✓"); showToast("Library exported!", "✓");
@@ -3492,7 +3492,7 @@
function toggleShowKey() { state.showKey = !state.showKey; render(); } function toggleShowKey() { state.showKey = !state.showKey; render(); }
function setApiKey(v) { function setApiKey(v) {
state.apiKey = v; state.apiKey = v;
localStorage.setItem("yt-summarizer-gemini-key", v); localStorage.setItem("recap-gemini-key", v);
} }
function setModel(m) { state.model = m; render(); } function setModel(m) { state.model = m; render(); }
function toggleExpandAll() { function toggleExpandAll() {
@@ -3921,12 +3921,12 @@
} }
function saveClipCollection() { function saveClipCollection() {
localStorage.setItem("yt-summarizer-clips", JSON.stringify(state.clipCollection)); localStorage.setItem("recap-clips", JSON.stringify(state.clipCollection));
} }
function loadClipCollection() { function loadClipCollection() {
try { try {
const saved = localStorage.getItem("yt-summarizer-clips"); const saved = localStorage.getItem("recap-clips");
if (saved) state.clipCollection = JSON.parse(saved); if (saved) state.clipCollection = JSON.parse(saved);
} catch {} } catch {}
} }
@@ -4112,7 +4112,7 @@
entitlements: data.entitlements || [], entitlements: data.entitlements || [],
expiresAt: data.expiresAt || null, expiresAt: data.expiresAt || null,
isTrial: !!data.isTrial, isTrial: !!data.isTrial,
productSlug: data.productSlug || "youtube-summarizer", productSlug: data.productSlug || "recap",
keysatBaseUrl: data.keysatBaseUrl || "", keysatBaseUrl: data.keysatBaseUrl || "",
}; };
} catch { } catch {
@@ -4150,7 +4150,7 @@
entitlements: data.entitlements || [], entitlements: data.entitlements || [],
expiresAt: data.expiresAt, expiresAt: data.expiresAt,
isTrial: !!data.isTrial, isTrial: !!data.isTrial,
productSlug: data.productSlug || "youtube-summarizer", productSlug: data.productSlug || "recap",
keysatBaseUrl: data.keysatBaseUrl || "", keysatBaseUrl: data.keysatBaseUrl || "",
}; };
state.licenseActivationKey = ""; state.licenseActivationKey = "";
@@ -4193,7 +4193,7 @@
} }
function upgradeToProUrl() { function upgradeToProUrl() {
const base = state.license.keysatBaseUrl || "https://licensing.keysat.xyz"; const base = state.license.keysatBaseUrl || "https://licensing.keysat.xyz";
return `${base.replace(/\/$/, "")}/buy/${state.license.productSlug || "youtube-summarizer"}`; return `${base.replace(/\/$/, "")}/buy/${state.license.productSlug || "recap"}`;
} }
function showToast(message, icon = "✓", duration = 4000) { function showToast(message, icon = "✓", duration = 4000) {
+3 -3
View File
@@ -114,7 +114,7 @@ console.log(
// On network errors we keep the prior state up to MAX_OFFLINE_DAYS (see // On network errors we keep the prior state up to MAX_OFFLINE_DAYS (see
// server/license.js); past the ceiling we lock out. // server/license.js); past the ceiling we lock out.
const VALIDATE_INTERVAL_MS = parseInt( const VALIDATE_INTERVAL_MS = parseInt(
process.env.YT_SUMMARIZER_VALIDATE_INTERVAL_MS || String(6 * 60 * 60 * 1000), process.env.RECAP_VALIDATE_INTERVAL_MS || String(6 * 60 * 60 * 1000),
10 10
); );
const ACTIVATE_VALIDATE_TIMEOUT_MS = 8000; const ACTIVATE_VALIDATE_TIMEOUT_MS = 8000;
@@ -821,7 +821,7 @@ app.get("/api/library/export", async (req, res) => {
subscriptions, subscriptions,
}; };
res.setHeader("Content-Type", "application/json"); res.setHeader("Content-Type", "application/json");
res.setHeader("Content-Disposition", 'attachment; filename="youtube-summarizer-library.json"'); res.setHeader("Content-Disposition", 'attachment; filename="recap-library.json"');
res.json(exportData); res.json(exportData);
} catch (err) { } catch (err) {
res.status(500).json({ error: err.message }); res.status(500).json({ error: err.message });
@@ -2823,7 +2823,7 @@ app.get("/api/network-mode", (req, res) => {
// ── Start server ─────────────────────────────────────────────────────────── // ── Start server ───────────────────────────────────────────────────────────
app.listen(PORT, BIND_HOST, async () => { app.listen(PORT, BIND_HOST, async () => {
console.log(`\n YouTube Summarizer API running on http://${BIND_HOST}:${PORT}`); console.log(`\n Recap API running on http://${BIND_HOST}:${PORT}`);
console.log(` Data directory: ${DATA_DIR}`); console.log(` Data directory: ${DATA_DIR}`);
console.log(` Checking yt-dlp...`); console.log(` Checking yt-dlp...`);
+4 -4
View File
@@ -26,7 +26,7 @@ import fs from "fs";
import path from "path"; import path from "path";
import { Verifier, PublicKey, Client } from "@keysat/licensing-client"; import { Verifier, PublicKey, Client } from "@keysat/licensing-client";
export const PRODUCT_SLUG = "youtube-summarizer"; export const PRODUCT_SLUG = "recap";
export const KEYSAT_BASE_URL = "https://licensing.keysat.xyz"; export const KEYSAT_BASE_URL = "https://licensing.keysat.xyz";
const __dirname = path.dirname(new URL(import.meta.url).pathname); const __dirname = path.dirname(new URL(import.meta.url).pathname);
@@ -37,7 +37,7 @@ const ISSUER_PEM = fs.readFileSync(PEM_PATH, "utf8");
// On StartOS that's /data; on local Mac dev it's the project root. // On StartOS that's /data; on local Mac dev it's the project root.
const DATA_DIR = process.env.DATA_DIR || path.join(__dirname, ".."); const DATA_DIR = process.env.DATA_DIR || path.join(__dirname, "..");
export const LICENSE_PATH = export const LICENSE_PATH =
process.env.YT_SUMMARIZER_LICENSE_KEY_PATH || process.env.RECAP_LICENSE_KEY_PATH ||
path.join(DATA_DIR, "license.txt"); path.join(DATA_DIR, "license.txt");
// Grace ceiling for network errors. As long as we successfully validated // Grace ceiling for network errors. As long as we successfully validated
@@ -46,7 +46,7 @@ export const LICENSE_PATH =
// Past the ceiling, we lock out — otherwise a revoked key on a permanently // Past the ceiling, we lock out — otherwise a revoked key on a permanently
// offline machine would never get caught. // offline machine would never get caught.
const MAX_OFFLINE_DAYS = parseInt( const MAX_OFFLINE_DAYS = parseInt(
process.env.YT_SUMMARIZER_MAX_OFFLINE_DAYS || "7", process.env.RECAP_MAX_OFFLINE_DAYS || "7",
10 10
); );
const MAX_OFFLINE_MS = MAX_OFFLINE_DAYS * 24 * 60 * 60 * 1000; const MAX_OFFLINE_MS = MAX_OFFLINE_DAYS * 24 * 60 * 60 * 1000;
@@ -74,7 +74,7 @@ function getOnlineClient() {
// ── Helpers ─────────────────────────────────────────────────────────────── // ── Helpers ───────────────────────────────────────────────────────────────
function readLicenseString() { function readLicenseString() {
const fromEnv = (process.env.YT_SUMMARIZER_LICENSE_KEY || "").trim(); const fromEnv = (process.env.RECAP_LICENSE_KEY || "").trim();
if (fromEnv) return fromEnv; if (fromEnv) return fromEnv;
try { try {
const s = fs.readFileSync(LICENSE_PATH, "utf8").trim(); const s = fs.readFileSync(LICENSE_PATH, "utf8").trim();
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"name": "youtube-summarizer-server", "name": "recap-server",
"version": "1.0.0", "version": "1.0.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
+2 -2
View File
@@ -14,7 +14,7 @@ startos-registry/
nginx.conf # nginx reverse proxy config nginx.conf # nginx reverse proxy config
startos-registry.service # systemd unit file startos-registry.service # systemd unit file
packages/ packages/
youtube-summarizer/ # One directory per package recap/ # One directory per package
package.json # Package metadata (version, description, etc.) package.json # Package metadata (version, description, etc.)
icon.png # Package icon (PNG) icon.png # Package icon (PNG)
LICENSE # License text LICENSE # License text
@@ -46,7 +46,7 @@ startos-registry/
Or use the publish script from your dev machine: Or use the publish script from your dev machine:
```bash ```bash
./scripts/publish.sh youtube-summarizer ./youtube-summarizer_x86_64.s9pk ./scripts/publish.sh recap ./recap_x86_64.s9pk
``` ```
## Version Format (Exver) ## Version Format (Exver)
@@ -1,10 +1,10 @@
# YouTube Summarizer # Recap
## Getting Started ## Getting Started
1. After installing, go to the service menu and run the **Set Gemini API Key** action 1. After installing, go to the service menu and run the **Set Gemini API Key** action
2. Get a free API key at [aistudio.google.com/apikey](https://aistudio.google.com/apikey) 2. Get a free API key at [aistudio.google.com/apikey](https://aistudio.google.com/apikey)
3. Open the YouTube Summarizer web interface from the service's Interfaces section 3. Open the Recap web interface from the service's Interfaces section
4. Paste any YouTube URL to download, transcribe, and summarize 4. Paste any YouTube URL to download, transcribe, and summarize
## Features ## Features

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

@@ -1,10 +1,10 @@
{ {
"id": "youtube-summarizer", "id": "recap",
"title": "YouTube Summarizer", "title": "Recap",
"version": "0.1.4:0", "version": "0.1.18:0",
"descriptionShort": "Download, transcribe, and summarize YouTube videos and podcasts with AI.", "descriptionShort": "Download, transcribe, and summarize YouTube videos and podcasts with AI.",
"descriptionLong": "YouTube Summarizer downloads audio from YouTube videos and podcast RSS feeds, transcribes them using Google Gemini, and produces structured topic-by-topic summaries with timestamps. Features include channel and podcast subscriptions with automatic new episode detection, a background processing queue with configurable delays, auto-download per subscription, organized history with folders, and a responsive web interface. Requires a Google Gemini API key (free tier available at aistudio.google.com/apikey).", "descriptionLong": "Recap downloads audio from YouTube videos and podcast RSS feeds, transcribes them using Google Gemini, and produces structured topic-by-topic summaries with timestamps. Features include channel and podcast subscriptions with automatic new episode detection, a background processing queue with configurable delays, auto-download per subscription, organized history with folders, and a responsive web interface. Requires a Google Gemini API key (free tier available at aistudio.google.com/apikey).",
"license": "Proprietary", "license": "Proprietary",
"categories": ["AI", "Media"], "categories": ["AI", "Media"],
+2 -2
View File
@@ -8,8 +8,8 @@
# ./scripts/publish.sh <package-id> <s9pk-file> [vps-host] # ./scripts/publish.sh <package-id> <s9pk-file> [vps-host]
# #
# Examples: # Examples:
# ./scripts/publish.sh youtube-summarizer ./youtube-summarizer_x86_64.s9pk # ./scripts/publish.sh recap ./recap_x86_64.s9pk
# ./scripts/publish.sh youtube-summarizer ./youtube-summarizer_x86_64.s9pk root@123.45.67.89 # ./scripts/publish.sh recap ./recap_x86_64.s9pk root@123.45.67.89
# #
# What it does: # What it does:
# 1. Uploads the .s9pk file to the VPS # 1. Uploads the .s9pk file to the VPS
+1 -1
View File
@@ -248,7 +248,7 @@ router.get('/latest', (req, res) => {
/** /**
* GET /:s9pk * GET /:s9pk
* Downloads the .s9pk binary file. * Downloads the .s9pk binary file.
* The :s9pk param is the filename (e.g., "youtube-summarizer.s9pk") * The :s9pk param is the filename (e.g., "recap.s9pk")
* *
* Optional query params: * Optional query params:
* spec - version range (ignored for single-version packages) * spec - version range (ignored for single-version packages)
+1 -1
View File
@@ -1,6 +1,6 @@
import { sdk } from './sdk' import { sdk } from './sdk'
// YouTube Summarizer has no dependencies on other StartOS services. // Recap has no dependencies on other StartOS services.
export const setDependencies = sdk.setupDependencies(async ({ effects }) => { export const setDependencies = sdk.setupDependencies(async ({ effects }) => {
return {} return {}
}) })
+4 -4
View File
@@ -2,14 +2,14 @@ export const DEFAULT_LANG = 'en_US'
const dict = { const dict = {
// main.ts // main.ts
'Starting YouTube Summarizer...': 0, 'Starting Recap...': 0,
'Web Interface': 1, 'Web Interface': 1,
'YouTube Summarizer is ready': 2, 'Recap is ready': 2,
'YouTube Summarizer is not responding': 3, 'Recap is not responding': 3,
// interfaces.ts // interfaces.ts
'Web UI': 4, 'Web UI': 4,
'The web interface for YouTube Summarizer — browse, search, and manage your transcript library': 5, 'The web interface for Recap — browse, search, and manage your transcript library': 5,
} as const } as const
/** /**
+1 -1
View File
@@ -1,6 +1,6 @@
import { sdk } from '../sdk' import { sdk } from '../sdk'
// YouTube Summarizer needs no special initialization. // Recap needs no special initialization.
// Directories are created by docker_entrypoint.sh and // Directories are created by docker_entrypoint.sh and
// config is loaded from the persistent volume at runtime. // config is loaded from the persistent volume at runtime.
export const setup = sdk.setupOnInit(async (effects, kind) => { export const setup = sdk.setupOnInit(async (effects, kind) => {
+1 -1
View File
@@ -11,7 +11,7 @@ export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => {
name: i18n('Web UI'), name: i18n('Web UI'),
id: 'ui', id: 'ui',
description: i18n( description: i18n(
'The web interface for YouTube Summarizer — browse, search, and manage your transcript library', 'The web interface for Recap — browse, search, and manage your transcript library',
), ),
type: 'ui', type: 'ui',
masked: false, masked: false,
+4 -4
View File
@@ -3,7 +3,7 @@ import { sdk } from './sdk'
import { uiPort } from './utils' import { uiPort } from './utils'
export const main = sdk.setupMain(async ({ effects }) => { export const main = sdk.setupMain(async ({ effects }) => {
console.info(i18n('Starting YouTube Summarizer...')) console.info(i18n('Starting Recap...'))
return sdk.Daemons.of(effects).addDaemon('primary', { return sdk.Daemons.of(effects).addDaemon('primary', {
subcontainer: await sdk.SubContainer.of( subcontainer: await sdk.SubContainer.of(
@@ -15,7 +15,7 @@ export const main = sdk.setupMain(async ({ effects }) => {
mountpoint: '/data', mountpoint: '/data',
readonly: false, readonly: false,
}), }),
'youtube-summarizer-sub', 'recap-sub',
), ),
exec: { exec: {
command: [ command: [
@@ -28,8 +28,8 @@ export const main = sdk.setupMain(async ({ effects }) => {
display: i18n('Web Interface'), display: i18n('Web Interface'),
fn: () => fn: () =>
sdk.healthCheck.checkPortListening(effects, uiPort, { sdk.healthCheck.checkPortListening(effects, uiPort, {
successMessage: i18n('YouTube Summarizer is ready'), successMessage: i18n('Recap is ready'),
errorMessage: i18n('YouTube Summarizer is not responding'), errorMessage: i18n('Recap is not responding'),
}), }),
}, },
requires: [], requires: [],
+1 -1
View File
@@ -5,7 +5,7 @@ export const short = {
export const long = { export const long = {
en_US: en_US:
'YouTube Summarizer downloads audio from YouTube videos and podcast RSS feeds, ' + 'Recap downloads audio from YouTube videos and podcast RSS feeds, ' +
'transcribes them using Google Gemini, and produces structured topic-by-topic ' + 'transcribes them using Google Gemini, and produces structured topic-by-topic ' +
'summaries with timestamps. Features include channel and podcast subscriptions ' + 'summaries with timestamps. Features include channel and podcast subscriptions ' +
'with automatic new episode detection, a background processing queue with ' + 'with automatic new episode detection, a background processing queue with ' +
+2 -2
View File
@@ -2,8 +2,8 @@ import { setupManifest } from '@start9labs/start-sdk'
import { alertInstall, long, short } from './i18n' import { alertInstall, long, short } from './i18n'
export const manifest = setupManifest({ export const manifest = setupManifest({
id: 'youtube-summarizer', id: 'recap',
title: 'YouTube Summarizer', title: 'Recap',
license: 'Proprietary', license: 'Proprietary',
packageRepo: 'https://ten31.xyz', packageRepo: 'https://ten31.xyz',
upstreamRepo: 'https://ten31.xyz', upstreamRepo: 'https://ten31.xyz',