feat: auto-aktualizacja yt-dlp (start + cyklicznie) + endpointy /api/ytdlp i przycisk w UI
All checks were successful
deploy / deploy (push) Successful in 2m21s
All checks were successful
deploy / deploy (push) Successful in 2m21s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,9 +2,11 @@ import { Module } from '@nestjs/common';
|
||||
import { JobsController, ProbeController } from './jobs.controller';
|
||||
import { JobsService } from './jobs.service';
|
||||
import { YtdlpService } from '../ytdlp/ytdlp.service';
|
||||
import { UpdaterService } from '../ytdlp/updater.service';
|
||||
import { YtdlpController } from '../ytdlp/ytdlp.controller';
|
||||
|
||||
@Module({
|
||||
controllers: [JobsController, ProbeController],
|
||||
providers: [JobsService, YtdlpService],
|
||||
controllers: [JobsController, ProbeController, YtdlpController],
|
||||
providers: [JobsService, YtdlpService, UpdaterService],
|
||||
})
|
||||
export class JobsModule {}
|
||||
|
||||
89
apps/api/src/ytdlp/updater.service.ts
Normal file
89
apps/api/src/ytdlp/updater.service.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
|
||||
import { spawn } from 'child_process';
|
||||
|
||||
const YTDLP = process.env.YTDLP_BIN || 'yt-dlp';
|
||||
// Co ile godzin sprawdzać aktualizacje (0 lub YTDLP_AUTO_UPDATE=0 wyłącza auto-update)
|
||||
const INTERVAL_H = Number(process.env.YTDLP_UPDATE_INTERVAL_H || 24);
|
||||
|
||||
interface UpdateResult {
|
||||
updated: boolean;
|
||||
message: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UpdaterService implements OnModuleInit {
|
||||
private readonly log = new Logger('Updater');
|
||||
version = 'unknown';
|
||||
lastCheck: string | null = null;
|
||||
lastResult = '';
|
||||
updating = false;
|
||||
|
||||
async onModuleInit() {
|
||||
this.version = await this.readVersion();
|
||||
this.log.log(`yt-dlp w wersji ${this.version}`);
|
||||
if (process.env.YTDLP_AUTO_UPDATE !== '0' && INTERVAL_H > 0) {
|
||||
void this.update(); // sprawdź od razu przy starcie
|
||||
setInterval(() => void this.update(), INTERVAL_H * 3600 * 1000).unref();
|
||||
}
|
||||
}
|
||||
|
||||
private readVersion(): Promise<string> {
|
||||
return run([YTDLP, '--version'])
|
||||
.then((r) => r.out.trim() || 'unknown')
|
||||
.catch(() => 'unknown');
|
||||
}
|
||||
|
||||
/** Uruchamia samoaktualizację yt-dlp (`yt-dlp -U`). */
|
||||
async update(): Promise<UpdateResult> {
|
||||
if (this.updating) {
|
||||
return { updated: false, message: 'Aktualizacja już trwa.', version: this.version };
|
||||
}
|
||||
this.updating = true;
|
||||
try {
|
||||
const r = await run([YTDLP, '-U']);
|
||||
const message = `${r.out}${r.err}`.trim();
|
||||
const updated = /Updated yt-dlp to|Updating to/i.test(message);
|
||||
this.version = await this.readVersion();
|
||||
this.lastCheck = new Date().toISOString();
|
||||
this.lastResult = message;
|
||||
this.log.log(
|
||||
updated
|
||||
? `yt-dlp zaktualizowany do ${this.version}`
|
||||
: `yt-dlp aktualny (${this.version})`,
|
||||
);
|
||||
return { updated, message, version: this.version };
|
||||
} catch (e: any) {
|
||||
const message = `Błąd aktualizacji: ${e?.message || e}`;
|
||||
this.lastCheck = new Date().toISOString();
|
||||
this.lastResult = message;
|
||||
this.log.warn(message);
|
||||
return { updated: false, message, version: this.version };
|
||||
} finally {
|
||||
this.updating = false;
|
||||
}
|
||||
}
|
||||
|
||||
status() {
|
||||
return {
|
||||
version: this.version,
|
||||
autoUpdate: process.env.YTDLP_AUTO_UPDATE !== '0' && INTERVAL_H > 0,
|
||||
intervalHours: INTERVAL_H,
|
||||
lastCheck: this.lastCheck,
|
||||
lastResult: this.lastResult,
|
||||
updating: this.updating,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function run(cmd: string[]): Promise<{ code: number; out: string; err: string }> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const c = spawn(cmd[0], cmd.slice(1));
|
||||
let out = '';
|
||||
let err = '';
|
||||
c.stdout!.on('data', (b) => (out += b.toString()));
|
||||
c.stderr!.on('data', (b) => (err += b.toString()));
|
||||
c.on('close', (code) => resolve({ code: code ?? 0, out, err }));
|
||||
c.on('error', reject);
|
||||
});
|
||||
}
|
||||
19
apps/api/src/ytdlp/ytdlp.controller.ts
Normal file
19
apps/api/src/ytdlp/ytdlp.controller.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Controller, Get, Post } from '@nestjs/common';
|
||||
import { UpdaterService } from './updater.service';
|
||||
|
||||
@Controller('ytdlp')
|
||||
export class YtdlpController {
|
||||
constructor(private readonly updater: UpdaterService) {}
|
||||
|
||||
/** Bieżąca wersja yt-dlp + stan auto-aktualizacji. */
|
||||
@Get()
|
||||
status() {
|
||||
return this.updater.status();
|
||||
}
|
||||
|
||||
/** Ręczne sprawdzenie/wymuszenie aktualizacji yt-dlp. */
|
||||
@Post('update')
|
||||
update() {
|
||||
return this.updater.update();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user