initial commit

This commit is contained in:
Pierre
2026-01-18 00:24:14 +01:00
commit 678af9f9bf
11 changed files with 1120 additions and 0 deletions
+82
View File
@@ -0,0 +1,82 @@
/**
* MetaMesh Plugin: subtitle-extractor
*
* ============================================================================
* PLUGIN MOUNT ARCHITECTURE - DO NOT MODIFY WITHOUT AUTHORIZATION
* ============================================================================
*
* Each plugin container has exactly 3 mounts:
*
* 1. /files (READ-ONLY) - Shared media files, read access only
* 2. /cache (READ-WRITE) - Plugin-specific cache folder
* 3. /output (READ-WRITE) - Plugin output folder for extracted subtitles
*
* SECURITY: Plugins must NEVER write to /files directly.
* All write operations go to /cache or /output only.
*
* ============================================================================
*/
import Fastify from 'fastify';
import type { HealthResponse, ProcessRequest, ProcessResponse, CallbackPayload, ConfigureRequest, ConfigureResponse } from './types.js';
import { manifest, process as processFile, configure } from './plugin.js';
const app = Fastify({ logger: true });
let ready = false;
app.get('/health', async (): Promise<HealthResponse> => ({
status: 'healthy',
ready,
version: manifest.version
}));
app.get('/manifest', async () => manifest);
app.post<{ Body: ConfigureRequest }>('/configure', async (request): Promise<ConfigureResponse> => {
try {
configure(request.body.config || {});
console.log(`[${manifest.id}] Configuration updated`);
return { status: 'ok' };
} catch (error) {
console.error(`[${manifest.id}] Configuration error:`, error);
return { status: 'error', error: error instanceof Error ? error.message : String(error) };
}
});
app.post<{ Body: ProcessRequest }>('/process', async (request, reply) => {
const { taskId, cid, filePath, callbackUrl, metaCoreUrl } = request.body;
if (!taskId || !cid || !filePath || !callbackUrl || !metaCoreUrl) {
return reply.send({ status: 'rejected', error: 'Missing required fields' } as ProcessResponse);
}
// Process asynchronously and send callback when done
processFile(request.body, async (payload: CallbackPayload) => {
try {
await fetch(callbackUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
} catch (error) {
console.error(`[${manifest.id}] Callback error:`, error);
}
}).catch((error) => {
console.error(`[${manifest.id}] Process error:`, error);
});
return reply.send({ status: 'accepted' } as ProcessResponse);
});
const port = parseInt(process.env.PORT || '8080', 10);
app.listen({ port, host: '0.0.0.0' }).then(() => {
ready = true;
console.log(`[${manifest.id}] Listening on port ${port}`);
});
process.on('SIGTERM', async () => {
ready = false;
await app.close();
process.exit(0);
});