mirror of
https://github.com/FJurmanovic/sugarr.git
synced 2026-11-12 00:01:09 +00:00
Wire up cron scheduling and the app entrypoint
This commit is contained in:
26
src/app/cron.ts
Normal file
26
src/app/cron.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import cron from 'node-cron';
|
||||||
|
import type { Logger } from '../core/logging/logger';
|
||||||
|
import type { AppConfig } from './config/AppConfig';
|
||||||
|
import type { RecommendationPipeline } from './pipelines/RecommendationPipeline';
|
||||||
|
|
||||||
|
export function scheduleCron(config: AppConfig, pipeline: RecommendationPipeline, logger: Logger): void {
|
||||||
|
if (!config.CRON_ENABLED) return;
|
||||||
|
|
||||||
|
cron.schedule(config.CRON_SCHEDULE, async () => {
|
||||||
|
logger.info('Cron: running recommendation pipeline');
|
||||||
|
try {
|
||||||
|
const recommendations = await pipeline.runAsync({
|
||||||
|
count: config.RECOMMEND_COUNT,
|
||||||
|
useLlm: config.LLM_ENABLED,
|
||||||
|
});
|
||||||
|
if (config.RADARR_ENABLED) {
|
||||||
|
await pipeline.autoRequest(recommendations);
|
||||||
|
}
|
||||||
|
logger.info({ count: recommendations.length }, 'Cron: pipeline finished');
|
||||||
|
} catch (err) {
|
||||||
|
logger.error({ err }, 'Cron: pipeline failed');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info({ schedule: config.CRON_SCHEDULE }, 'Cron scheduled');
|
||||||
|
}
|
||||||
86
src/app/index.ts
Normal file
86
src/app/index.ts
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import 'dotenv/config';
|
||||||
|
import { loadAppConfig } from './config/AppConfig';
|
||||||
|
import { createLogger } from '../core/logging/logger';
|
||||||
|
import { LangfuseTracer } from '../core/langfuse/LangfuseTracer';
|
||||||
|
import { LangfusePromptGateway } from '../core/langfuse/LangfusePromptGateway';
|
||||||
|
import { PromptService } from '../core/langfuse/PromptService';
|
||||||
|
import { LlmClient } from '../core/llm/LlmClient';
|
||||||
|
import { buildServer } from '../core/server/buildServer';
|
||||||
|
import { JellyfinClient } from './clients/jellyfin/JellyfinClient';
|
||||||
|
import { TmdbClient } from './clients/tmdb/TmdbClient';
|
||||||
|
import { RadarrClient } from './clients/radarr/RadarrClient';
|
||||||
|
import { MovieRecommenderTool } from './tools/MovieRecommenderTool';
|
||||||
|
import { MovieSearchTool } from './tools/MovieSearchTool';
|
||||||
|
import { RecommendationPipeline } from './pipelines/RecommendationPipeline';
|
||||||
|
import { HealthController } from './controllers/HealthController';
|
||||||
|
import { RecommendationsController } from './controllers/RecommendationsController';
|
||||||
|
import { RequestController } from './controllers/RequestController';
|
||||||
|
import { WebhookController } from './controllers/WebhookController';
|
||||||
|
import { scheduleCron } from './cron';
|
||||||
|
|
||||||
|
async function main(): Promise<void> {
|
||||||
|
const config = loadAppConfig();
|
||||||
|
const logger = createLogger('sugarr');
|
||||||
|
|
||||||
|
const langfuseOpts = {
|
||||||
|
publicKey: config.LANGFUSE_PUBLIC_KEY,
|
||||||
|
secretKey: config.LANGFUSE_SECRET_KEY,
|
||||||
|
baseUrl: config.LANGFUSE_BASE_URL,
|
||||||
|
};
|
||||||
|
const tracer = new LangfuseTracer(langfuseOpts);
|
||||||
|
const promptGateway = new LangfusePromptGateway(langfuseOpts);
|
||||||
|
const promptService = new PromptService(promptGateway, config.LANGFUSE_PROMPT_CACHE_ENABLED);
|
||||||
|
const llmClient = new LlmClient({ baseUrl: config.LLM_BASE_URL, apiKey: config.LLM_API_KEY });
|
||||||
|
|
||||||
|
const jellyfin = new JellyfinClient(config.JELLYFIN_URL, config.JELLYFIN_API_KEY, config.JELLYFIN_USER_ID);
|
||||||
|
const tmdb = new TmdbClient(config.TMDB_API_KEY);
|
||||||
|
const radarr = config.RADARR_ENABLED
|
||||||
|
? new RadarrClient(config.RADARR_URL, config.RADARR_API_KEY, config.RADARR_QUALITY_PROFILE_ID, config.RADARR_ROOT_FOLDER)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const recommenderTool = new MovieRecommenderTool(promptService, llmClient);
|
||||||
|
const searchTool = new MovieSearchTool(promptService, llmClient);
|
||||||
|
|
||||||
|
const pipeline = new RecommendationPipeline(tracer, logger, {
|
||||||
|
jellyfin,
|
||||||
|
tmdb,
|
||||||
|
radarr,
|
||||||
|
tool: recommenderTool,
|
||||||
|
searchTool,
|
||||||
|
minRatedMovies: config.MIN_RATED_MOVIES,
|
||||||
|
maxRetry: config.RECOMMEND_MAX_RETRY,
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
{
|
||||||
|
jellyfin: config.JELLYFIN_URL,
|
||||||
|
llmEnabled: config.LLM_ENABLED,
|
||||||
|
radarrEnabled: config.RADARR_ENABLED,
|
||||||
|
cronEnabled: config.CRON_ENABLED,
|
||||||
|
apiEnabled: config.API_ENABLED,
|
||||||
|
},
|
||||||
|
'sugarr starting'
|
||||||
|
);
|
||||||
|
|
||||||
|
scheduleCron(config, pipeline, logger);
|
||||||
|
|
||||||
|
if (config.API_ENABLED) {
|
||||||
|
const app = buildServer({
|
||||||
|
secretKey: config.SECRET_KEY,
|
||||||
|
controllers: [
|
||||||
|
new HealthController(config),
|
||||||
|
new RecommendationsController(config, pipeline),
|
||||||
|
new RequestController(config, radarr),
|
||||||
|
new WebhookController(promptService),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
await app.listen({ port: config.API_PORT, host: '0.0.0.0' });
|
||||||
|
logger.info({ port: config.API_PORT }, 'HTTP API listening');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((err) => {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error(err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user