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:
+1
-1
@@ -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
|
||||
#
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# YouTube Summarizer
|
||||
# Recap
|
||||
|
||||
Download, transcribe, and summarize YouTube videos and podcast episodes using Google Gemini AI.
|
||||
|
||||
|
||||
+7
-7
@@ -13,10 +13,10 @@
|
||||
# START9_SERVER — your Start9 server, e.g. https://immense-voyage.local:62185
|
||||
#
|
||||
# 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_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
|
||||
|
||||
@@ -35,11 +35,11 @@ fi
|
||||
: "${FILEBROWSER_PASS:?FILEBROWSER_PASS is required}"
|
||||
: "${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_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
|
||||
echo "X $S9PK_FILE not found. Run 'make x86' first." >&2
|
||||
@@ -56,7 +56,7 @@ else
|
||||
CURRENT_VERSION="unknown"
|
||||
fi
|
||||
|
||||
echo "==> Deploying youtube-summarizer $CURRENT_VERSION"
|
||||
echo "==> Deploying recap $CURRENT_VERSION"
|
||||
echo " source : $S9PK_FILE"
|
||||
echo " upload : $FILEBROWSER_URL$FILEBROWSER_PATH"
|
||||
echo " public : $REGISTRY_PUBLIC_URL"
|
||||
@@ -106,4 +106,4 @@ curl -fsS -X POST "$REGISTRY_URL/rpc/v0" \
|
||||
-o /dev/null
|
||||
|
||||
echo ""
|
||||
echo "==> Done. youtube-summarizer $CURRENT_VERSION is live."
|
||||
echo "==> Done. recap $CURRENT_VERSION is live."
|
||||
|
||||
@@ -41,7 +41,7 @@ export DATA_DIR="$DATA_DIR"
|
||||
export PORT="${PORT:-3001}"
|
||||
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 " ffmpeg: $(ffmpeg -version 2>/dev/null | head -1 || echo 'not found')"
|
||||
echo " Data: $DATA_DIR"
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "youtube-summarizer-startos",
|
||||
"name": "recap-startos",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "rm -rf ./javascript && ncc build startos/index.ts -o ./javascript",
|
||||
|
||||
+15
-15
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<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">
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
@@ -1211,7 +1211,7 @@
|
||||
// ── State ────────────────────────────────────────────────────────────────
|
||||
const state = {
|
||||
url: "",
|
||||
apiKey: localStorage.getItem("yt-summarizer-gemini-key") || "",
|
||||
apiKey: localStorage.getItem("recap-gemini-key") || "",
|
||||
hasServerKey: false, // will be set by health check
|
||||
lanMode: null, // null = unknown, true = home, false = traveling
|
||||
serverStatus: "connecting", // "connected" | "sleeping" | "disconnected" | "connecting"
|
||||
@@ -1275,7 +1275,7 @@
|
||||
entitlements: [],
|
||||
expiresAt: null,
|
||||
isTrial: false,
|
||||
productSlug: "youtube-summarizer",
|
||||
productSlug: "recap",
|
||||
keysatBaseUrl: "",
|
||||
},
|
||||
licenseActivating: false,
|
||||
@@ -1284,7 +1284,7 @@
|
||||
// Free tier: once dismissed, the activation screen no longer
|
||||
// hard-gates the UI. Persisted so returning unlicensed users land
|
||||
// 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"];
|
||||
@@ -1624,7 +1624,7 @@
|
||||
return `
|
||||
<div class="activation-screen">
|
||||
<div class="activation-card">
|
||||
<h1>YouTube Summarizer</h1>
|
||||
<h1>Recap</h1>
|
||||
<p class="activation-sub">
|
||||
${loading
|
||||
? "Checking license…"
|
||||
@@ -1651,7 +1651,7 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="activation-meta">
|
||||
Product: <strong>${escHtml(lic.productSlug || "youtube-summarizer")}</strong>
|
||||
Product: <strong>${escHtml(lic.productSlug || "recap")}</strong>
|
||||
${lic.keysatBaseUrl ? ` · Issuer: <strong>${escHtml(lic.keysatBaseUrl.replace(/^https?:\/\//, ""))}</strong>` : ""}
|
||||
</div>
|
||||
`}
|
||||
@@ -1662,7 +1662,7 @@
|
||||
|
||||
function dismissActivation() {
|
||||
state.activationSkipped = true;
|
||||
try { localStorage.setItem("yt-summarizer-activation-skipped", "1"); } catch {}
|
||||
try { localStorage.setItem("recap-activation-skipped", "1"); } catch {}
|
||||
render();
|
||||
}
|
||||
|
||||
@@ -1730,7 +1730,7 @@
|
||||
function showActivationScreen() {
|
||||
// Take user back to the activation modal (e.g. from an upgrade banner).
|
||||
state.activationSkipped = false;
|
||||
try { localStorage.removeItem("yt-summarizer-activation-skipped"); } catch {}
|
||||
try { localStorage.removeItem("recap-activation-skipped"); } catch {}
|
||||
render();
|
||||
}
|
||||
|
||||
@@ -2057,7 +2057,7 @@
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = "youtube-summarizer-library.json";
|
||||
a.download = "recap-library.json";
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
showToast("Library exported!", "✓");
|
||||
@@ -3492,7 +3492,7 @@
|
||||
function toggleShowKey() { state.showKey = !state.showKey; render(); }
|
||||
function setApiKey(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 toggleExpandAll() {
|
||||
@@ -3921,12 +3921,12 @@
|
||||
}
|
||||
|
||||
function saveClipCollection() {
|
||||
localStorage.setItem("yt-summarizer-clips", JSON.stringify(state.clipCollection));
|
||||
localStorage.setItem("recap-clips", JSON.stringify(state.clipCollection));
|
||||
}
|
||||
|
||||
function loadClipCollection() {
|
||||
try {
|
||||
const saved = localStorage.getItem("yt-summarizer-clips");
|
||||
const saved = localStorage.getItem("recap-clips");
|
||||
if (saved) state.clipCollection = JSON.parse(saved);
|
||||
} catch {}
|
||||
}
|
||||
@@ -4112,7 +4112,7 @@
|
||||
entitlements: data.entitlements || [],
|
||||
expiresAt: data.expiresAt || null,
|
||||
isTrial: !!data.isTrial,
|
||||
productSlug: data.productSlug || "youtube-summarizer",
|
||||
productSlug: data.productSlug || "recap",
|
||||
keysatBaseUrl: data.keysatBaseUrl || "",
|
||||
};
|
||||
} catch {
|
||||
@@ -4150,7 +4150,7 @@
|
||||
entitlements: data.entitlements || [],
|
||||
expiresAt: data.expiresAt,
|
||||
isTrial: !!data.isTrial,
|
||||
productSlug: data.productSlug || "youtube-summarizer",
|
||||
productSlug: data.productSlug || "recap",
|
||||
keysatBaseUrl: data.keysatBaseUrl || "",
|
||||
};
|
||||
state.licenseActivationKey = "";
|
||||
@@ -4193,7 +4193,7 @@
|
||||
}
|
||||
function upgradeToProUrl() {
|
||||
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) {
|
||||
|
||||
+3
-3
@@ -114,7 +114,7 @@ console.log(
|
||||
// On network errors we keep the prior state up to MAX_OFFLINE_DAYS (see
|
||||
// server/license.js); past the ceiling we lock out.
|
||||
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
|
||||
);
|
||||
const ACTIVATE_VALIDATE_TIMEOUT_MS = 8000;
|
||||
@@ -821,7 +821,7 @@ app.get("/api/library/export", async (req, res) => {
|
||||
subscriptions,
|
||||
};
|
||||
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);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
@@ -2823,7 +2823,7 @@ app.get("/api/network-mode", (req, res) => {
|
||||
// ── Start server ───────────────────────────────────────────────────────────
|
||||
|
||||
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(` Checking yt-dlp...`);
|
||||
|
||||
|
||||
+4
-4
@@ -26,7 +26,7 @@ import fs from "fs";
|
||||
import path from "path";
|
||||
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";
|
||||
|
||||
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.
|
||||
const DATA_DIR = process.env.DATA_DIR || path.join(__dirname, "..");
|
||||
export const LICENSE_PATH =
|
||||
process.env.YT_SUMMARIZER_LICENSE_KEY_PATH ||
|
||||
process.env.RECAP_LICENSE_KEY_PATH ||
|
||||
path.join(DATA_DIR, "license.txt");
|
||||
|
||||
// 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
|
||||
// offline machine would never get caught.
|
||||
const MAX_OFFLINE_DAYS = parseInt(
|
||||
process.env.YT_SUMMARIZER_MAX_OFFLINE_DAYS || "7",
|
||||
process.env.RECAP_MAX_OFFLINE_DAYS || "7",
|
||||
10
|
||||
);
|
||||
const MAX_OFFLINE_MS = MAX_OFFLINE_DAYS * 24 * 60 * 60 * 1000;
|
||||
@@ -74,7 +74,7 @@ function getOnlineClient() {
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────
|
||||
function readLicenseString() {
|
||||
const fromEnv = (process.env.YT_SUMMARIZER_LICENSE_KEY || "").trim();
|
||||
const fromEnv = (process.env.RECAP_LICENSE_KEY || "").trim();
|
||||
if (fromEnv) return fromEnv;
|
||||
try {
|
||||
const s = fs.readFileSync(LICENSE_PATH, "utf8").trim();
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "youtube-summarizer-server",
|
||||
"name": "recap-server",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -14,7 +14,7 @@ startos-registry/
|
||||
nginx.conf # nginx reverse proxy config
|
||||
startos-registry.service # systemd unit file
|
||||
packages/
|
||||
youtube-summarizer/ # One directory per package
|
||||
recap/ # One directory per package
|
||||
package.json # Package metadata (version, description, etc.)
|
||||
icon.png # Package icon (PNG)
|
||||
LICENSE # License text
|
||||
@@ -46,7 +46,7 @@ startos-registry/
|
||||
|
||||
Or use the publish script from your dev machine:
|
||||
```bash
|
||||
./scripts/publish.sh youtube-summarizer ./youtube-summarizer_x86_64.s9pk
|
||||
./scripts/publish.sh recap ./recap_x86_64.s9pk
|
||||
```
|
||||
|
||||
## Version Format (Exver)
|
||||
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
# YouTube Summarizer
|
||||
# Recap
|
||||
|
||||
## Getting Started
|
||||
|
||||
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)
|
||||
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
|
||||
|
||||
## Features
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
+4
-4
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"id": "youtube-summarizer",
|
||||
"title": "YouTube Summarizer",
|
||||
"version": "0.1.4:0",
|
||||
"id": "recap",
|
||||
"title": "Recap",
|
||||
"version": "0.1.18:0",
|
||||
|
||||
"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",
|
||||
"categories": ["AI", "Media"],
|
||||
@@ -8,8 +8,8 @@
|
||||
# ./scripts/publish.sh <package-id> <s9pk-file> [vps-host]
|
||||
#
|
||||
# Examples:
|
||||
# ./scripts/publish.sh youtube-summarizer ./youtube-summarizer_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
|
||||
# ./scripts/publish.sh recap ./recap_x86_64.s9pk root@123.45.67.89
|
||||
#
|
||||
# What it does:
|
||||
# 1. Uploads the .s9pk file to the VPS
|
||||
|
||||
@@ -248,7 +248,7 @@ router.get('/latest', (req, res) => {
|
||||
/**
|
||||
* GET /:s9pk
|
||||
* 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:
|
||||
* spec - version range (ignored for single-version packages)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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 }) => {
|
||||
return {}
|
||||
})
|
||||
|
||||
@@ -2,14 +2,14 @@ export const DEFAULT_LANG = 'en_US'
|
||||
|
||||
const dict = {
|
||||
// main.ts
|
||||
'Starting YouTube Summarizer...': 0,
|
||||
'Starting Recap...': 0,
|
||||
'Web Interface': 1,
|
||||
'YouTube Summarizer is ready': 2,
|
||||
'YouTube Summarizer is not responding': 3,
|
||||
'Recap is ready': 2,
|
||||
'Recap is not responding': 3,
|
||||
|
||||
// interfaces.ts
|
||||
'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
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { sdk } from '../sdk'
|
||||
|
||||
// YouTube Summarizer needs no special initialization.
|
||||
// Recap needs no special initialization.
|
||||
// Directories are created by docker_entrypoint.sh and
|
||||
// config is loaded from the persistent volume at runtime.
|
||||
export const setup = sdk.setupOnInit(async (effects, kind) => {
|
||||
|
||||
@@ -11,7 +11,7 @@ export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => {
|
||||
name: i18n('Web UI'),
|
||||
id: 'ui',
|
||||
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',
|
||||
masked: false,
|
||||
|
||||
+4
-4
@@ -3,7 +3,7 @@ import { sdk } from './sdk'
|
||||
import { uiPort } from './utils'
|
||||
|
||||
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', {
|
||||
subcontainer: await sdk.SubContainer.of(
|
||||
@@ -15,7 +15,7 @@ export const main = sdk.setupMain(async ({ effects }) => {
|
||||
mountpoint: '/data',
|
||||
readonly: false,
|
||||
}),
|
||||
'youtube-summarizer-sub',
|
||||
'recap-sub',
|
||||
),
|
||||
exec: {
|
||||
command: [
|
||||
@@ -28,8 +28,8 @@ export const main = sdk.setupMain(async ({ effects }) => {
|
||||
display: i18n('Web Interface'),
|
||||
fn: () =>
|
||||
sdk.healthCheck.checkPortListening(effects, uiPort, {
|
||||
successMessage: i18n('YouTube Summarizer is ready'),
|
||||
errorMessage: i18n('YouTube Summarizer is not responding'),
|
||||
successMessage: i18n('Recap is ready'),
|
||||
errorMessage: i18n('Recap is not responding'),
|
||||
}),
|
||||
},
|
||||
requires: [],
|
||||
|
||||
@@ -5,7 +5,7 @@ export const short = {
|
||||
|
||||
export const long = {
|
||||
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 ' +
|
||||
'summaries with timestamps. Features include channel and podcast subscriptions ' +
|
||||
'with automatic new episode detection, a background processing queue with ' +
|
||||
|
||||
@@ -2,8 +2,8 @@ import { setupManifest } from '@start9labs/start-sdk'
|
||||
import { alertInstall, long, short } from './i18n'
|
||||
|
||||
export const manifest = setupManifest({
|
||||
id: 'youtube-summarizer',
|
||||
title: 'YouTube Summarizer',
|
||||
id: 'recap',
|
||||
title: 'Recap',
|
||||
license: 'Proprietary',
|
||||
packageRepo: 'https://ten31.xyz',
|
||||
upstreamRepo: 'https://ten31.xyz',
|
||||
|
||||
Reference in New Issue
Block a user