Add StartOS 0.4.0 packaging
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
import { sdk } from '../sdk'
|
||||
import { updateYtdlp } from './updateYtdlp'
|
||||
|
||||
export const actions = sdk.Actions.of().addAction(updateYtdlp)
|
||||
@@ -0,0 +1,50 @@
|
||||
import { i18n } from '../i18n'
|
||||
import { sdk } from '../sdk'
|
||||
|
||||
export const updateYtdlp = sdk.Action.withoutInput(
|
||||
'update-ytdlp',
|
||||
async ({ effects }) => ({
|
||||
name: i18n('Update yt-dlp'),
|
||||
description: i18n(
|
||||
'Downloads the latest version of yt-dlp. Run this if YouTube downloads are failing — YouTube frequently changes its systems, requiring yt-dlp updates.',
|
||||
),
|
||||
warning: i18n('This may take a minute. The service will continue running.'),
|
||||
allowedStatuses: 'any',
|
||||
group: null,
|
||||
visibility: 'enabled' as const,
|
||||
}),
|
||||
async ({ effects }) => {
|
||||
// Run pip upgrade inside the running subcontainer
|
||||
const sub = await sdk.SubContainer.of(
|
||||
effects,
|
||||
{ imageId: 'main' },
|
||||
sdk.Mounts.of().mountVolume({
|
||||
volumeId: 'main',
|
||||
subpath: null,
|
||||
mountpoint: '/data',
|
||||
readonly: false,
|
||||
}),
|
||||
'ytdlp-update-sub',
|
||||
)
|
||||
|
||||
const result = await sub.exec(['sh', '-c',
|
||||
'pip3 install --break-system-packages -U yt-dlp curl_cffi 2>&1; echo "---"; echo "yt-dlp version: $(yt-dlp --version)"',
|
||||
])
|
||||
|
||||
const output = result.stdout || 'Update completed'
|
||||
|
||||
return {
|
||||
version: '1' as const,
|
||||
title: 'yt-dlp Update',
|
||||
message: output,
|
||||
result: {
|
||||
type: 'single' as const,
|
||||
name: 'Output',
|
||||
value: output,
|
||||
masked: false,
|
||||
copyable: true,
|
||||
qr: false,
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,5 @@
|
||||
import { sdk } from './sdk'
|
||||
|
||||
export const { createBackup, restoreInit } = sdk.setupBackups(
|
||||
async ({ effects }) => sdk.Backups.ofVolumes('main'),
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
import { sdk } from './sdk'
|
||||
|
||||
// YouTube Summarizer has no dependencies on other StartOS services.
|
||||
export const dependencies = sdk.setupDependencies(async ({ effects }) => {
|
||||
return {}
|
||||
})
|
||||
@@ -0,0 +1,25 @@
|
||||
export const DEFAULT_LANG = 'en_US'
|
||||
|
||||
const dict = {
|
||||
// main.ts
|
||||
'Starting YouTube Summarizer...': 0,
|
||||
'Web Interface': 1,
|
||||
'YouTube Summarizer is ready': 2,
|
||||
'YouTube Summarizer is not responding': 3,
|
||||
|
||||
// interfaces.ts
|
||||
'Web UI': 4,
|
||||
'The web interface for YouTube Summarizer — browse, search, and manage your transcript library': 5,
|
||||
|
||||
// actions/updateYtdlp.ts
|
||||
'Update yt-dlp': 6,
|
||||
'Downloads the latest version of yt-dlp. Run this if YouTube downloads are failing — YouTube frequently changes its systems, requiring yt-dlp updates.': 7,
|
||||
'This may take a minute. The service will continue running.': 8,
|
||||
} as const
|
||||
|
||||
/**
|
||||
* Plumbing. DO NOT EDIT.
|
||||
*/
|
||||
export type I18nKey = keyof typeof dict
|
||||
export type LangDict = Record<(typeof dict)[I18nKey], string>
|
||||
export default dict
|
||||
@@ -0,0 +1,4 @@
|
||||
import { LangDict } from './default'
|
||||
|
||||
// English-only for now. Add translations here as needed.
|
||||
export default {} satisfies Record<string, LangDict>
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Plumbing. DO NOT EDIT this file.
|
||||
*/
|
||||
import { setupI18n } from '@start9labs/start-sdk'
|
||||
import defaultDict, { DEFAULT_LANG } from './dictionaries/default'
|
||||
import translations from './dictionaries/translations'
|
||||
|
||||
export const i18n = setupI18n(defaultDict, translations, DEFAULT_LANG)
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Plumbing. DO NOT EDIT.
|
||||
*/
|
||||
export { createBackup } from './backups'
|
||||
export { main } from './main'
|
||||
export { init, uninit } from './init'
|
||||
export { actions } from './actions'
|
||||
import { buildManifest } from '@start9labs/start-sdk'
|
||||
import { manifest as sdkManifest } from './manifest'
|
||||
import { versionGraph } from './versions'
|
||||
export const manifest = buildManifest(versionGraph, sdkManifest)
|
||||
@@ -0,0 +1,11 @@
|
||||
import { sdk } from './sdk'
|
||||
|
||||
// init runs on install, update, restore, and container rebuild.
|
||||
// For YouTube Summarizer, no special initialization is needed —
|
||||
// the entrypoint script handles directory creation and config loading.
|
||||
export const { init, uninit } = sdk.setupInit(
|
||||
async ({ effects, kind }) => {
|
||||
// Nothing to do — directories are created by docker_entrypoint.sh
|
||||
// and config is loaded from the persistent volume.
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,27 @@
|
||||
import { i18n } from './i18n'
|
||||
import { sdk } from './sdk'
|
||||
import { uiPort } from './utils'
|
||||
|
||||
export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => {
|
||||
const uiMulti = sdk.MultiHost.of(effects, 'ui-multi')
|
||||
const uiMultiOrigin = await uiMulti.bindPort(uiPort, {
|
||||
protocol: 'http',
|
||||
})
|
||||
const ui = sdk.createInterface(effects, {
|
||||
name: i18n('Web UI'),
|
||||
id: 'ui',
|
||||
description: i18n(
|
||||
'The web interface for YouTube Summarizer — browse, search, and manage your transcript library',
|
||||
),
|
||||
type: 'ui',
|
||||
masked: false,
|
||||
schemeOverride: null,
|
||||
username: null,
|
||||
path: '',
|
||||
query: {},
|
||||
})
|
||||
|
||||
const uiReceipt = await uiMultiOrigin.export([ui])
|
||||
|
||||
return [uiReceipt]
|
||||
})
|
||||
@@ -0,0 +1,37 @@
|
||||
import { i18n } from './i18n'
|
||||
import { sdk } from './sdk'
|
||||
import { uiPort } from './utils'
|
||||
|
||||
export const main = sdk.setupMain(async ({ effects }) => {
|
||||
console.info(i18n('Starting YouTube Summarizer...'))
|
||||
|
||||
return sdk.Daemons.of(effects).addDaemon('primary', {
|
||||
subcontainer: await sdk.SubContainer.of(
|
||||
effects,
|
||||
{ imageId: 'main' },
|
||||
sdk.Mounts.of().mountVolume({
|
||||
volumeId: 'main',
|
||||
subpath: null,
|
||||
mountpoint: '/data',
|
||||
readonly: false,
|
||||
}),
|
||||
'youtube-summarizer-sub',
|
||||
),
|
||||
exec: {
|
||||
command: [
|
||||
'dumb-init',
|
||||
'--',
|
||||
'/usr/local/bin/docker_entrypoint.sh',
|
||||
],
|
||||
},
|
||||
ready: {
|
||||
display: i18n('Web Interface'),
|
||||
fn: () =>
|
||||
sdk.healthCheck.checkPortListening(effects, uiPort, {
|
||||
successMessage: i18n('YouTube Summarizer is ready'),
|
||||
errorMessage: i18n('YouTube Summarizer is not responding'),
|
||||
}),
|
||||
},
|
||||
requires: [],
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,21 @@
|
||||
export const short = {
|
||||
en_US:
|
||||
'Download, transcribe, and summarize YouTube videos and podcasts with AI.',
|
||||
}
|
||||
|
||||
export const long = {
|
||||
en_US:
|
||||
'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, multiple AI model support, and a responsive web interface. ' +
|
||||
'Requires a Google Gemini API key (free tier available at aistudio.google.com/apikey).',
|
||||
}
|
||||
|
||||
export const alertInstall = {
|
||||
en_US:
|
||||
'After installing, open the web UI and enter your Google Gemini API key in Settings. ' +
|
||||
'For reliable YouTube downloads, set up OAuth2 authentication in Settings > YouTube Authentication.',
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { setupManifest } from '@start9labs/start-sdk'
|
||||
import { alertInstall, long, short } from './i18n'
|
||||
|
||||
export const manifest = setupManifest({
|
||||
id: 'youtube-summarizer',
|
||||
title: 'YouTube Summarizer',
|
||||
license: 'Proprietary',
|
||||
packageRepo: 'https://github.com/user/youtube-summarizer-startos',
|
||||
upstreamRepo: 'https://github.com/user/youtube-summarizer',
|
||||
marketingUrl: 'https://github.com/user/youtube-summarizer',
|
||||
donationUrl: null,
|
||||
docsUrls: [
|
||||
'https://github.com/user/youtube-summarizer/blob/main/README.md',
|
||||
],
|
||||
description: { short, long },
|
||||
volumes: ['main'],
|
||||
images: {
|
||||
main: {
|
||||
source: {
|
||||
dockerBuild: {
|
||||
workdir: '.',
|
||||
dockerfile: './Dockerfile',
|
||||
},
|
||||
},
|
||||
arch: ['x86_64', 'aarch64'],
|
||||
},
|
||||
},
|
||||
alerts: {
|
||||
install: alertInstall,
|
||||
update: null,
|
||||
uninstall: null,
|
||||
restore: null,
|
||||
start: null,
|
||||
stop: null,
|
||||
},
|
||||
dependencies: {},
|
||||
})
|
||||
@@ -0,0 +1,9 @@
|
||||
import { StartSdk } from '@start9labs/start-sdk'
|
||||
import { manifest } from './manifest'
|
||||
|
||||
/**
|
||||
* Plumbing. DO NOT EDIT.
|
||||
*
|
||||
* The exported "sdk" const is used throughout this package codebase.
|
||||
*/
|
||||
export const sdk = StartSdk.of().withManifest(manifest).build(true)
|
||||
@@ -0,0 +1,2 @@
|
||||
// Shared constants used across the package codebase
|
||||
export const uiPort = 3001
|
||||
@@ -0,0 +1,7 @@
|
||||
import { VersionGraph } from '@start9labs/start-sdk'
|
||||
import { v_0_1_0 } from './v0.1.0'
|
||||
|
||||
export const versionGraph = VersionGraph.of({
|
||||
current: v_0_1_0,
|
||||
other: [],
|
||||
})
|
||||
@@ -0,0 +1,16 @@
|
||||
import { VersionInfo } from '@start9labs/start-sdk'
|
||||
|
||||
export const v_0_1_0 = VersionInfo.of({
|
||||
version: '0.1.0:0',
|
||||
releaseNotes: {
|
||||
en_US:
|
||||
'Initial StartOS package. Download, transcribe, and summarize YouTube videos ' +
|
||||
'and podcast episodes using Google Gemini AI. Features include channel and ' +
|
||||
'podcast subscriptions, background processing queue, auto-download per ' +
|
||||
'subscription, and organized history with folders.',
|
||||
},
|
||||
migrations: {
|
||||
up: async ({ effects }) => {},
|
||||
down: 'IMPOSSIBLE',
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user