Skip to content

Commit

Permalink
feat: can create a VOD package using OSC
Browse files Browse the repository at this point in the history
  • Loading branch information
birme committed Dec 9, 2024
1 parent de31f04 commit b5508a3
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 69 deletions.
40 changes: 31 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
},
"dependencies": {
"@modelcontextprotocol/sdk": "0.6.0",
"@osaas/client-core": "^0.14.1",
"@osaas/client-services": "^0.2.3",
"@osaas/client-core": "^0.15.0",
"@osaas/client-services": "^0.2.4",
"@osaas/client-transcode": "^0.13.0",
"dotenv": "^16.4.5",
"minio": "^8.0.2",
"zod-to-json-schema": "^3.23.5"
Expand Down
17 changes: 17 additions & 0 deletions src/resources/encore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,20 @@ export async function createEncoreInstance(ctx: Context, name: string) {
url: instance.url.replace(/\/$/, '')
};
}

export async function getEncoreInstance(ctx: Context, name: string) {
const serviceAccessToken = await ctx.getServiceAccessToken(SERVICE_ID);
const instance: Encore = await getInstance(
ctx,
SERVICE_ID,
name,
serviceAccessToken
);
if (!instance) {
throw new Error(`Encore Instance ${name} not found`);
}
return {
jobs: new URL('/encoreJobs', instance.url).toString(),
url: instance.url.replace(/\/$/, '')
};
}
19 changes: 19 additions & 0 deletions src/resources/encore_callback_listener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,22 @@ export async function createEncoreCallbackListenerInstance(
url: new URL('/encoreCallback', instance.url).toString()
};
}

export async function getEncoreCallbackListenerInstance(
ctx: Context,
name: string
) {
const serviceAccessToken = await ctx.getServiceAccessToken(SERVICE_ID);
const instance: EyevinnEncoreCallbackListener = await getInstance(
ctx,
SERVICE_ID,
name,
serviceAccessToken
);
if (!instance) {
throw new Error(`Encore Callback Listener Instance ${name} not found`);
}
return {
url: new URL('/encoreCallback', instance.url).toString()
};
}
14 changes: 14 additions & 0 deletions src/resources/encore_packager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,17 @@ export async function createEncorePackager(
}
return instance;
}

export async function getEncorePackager(ctx: Context, name: string) {
const serviceAccessToken = await ctx.getServiceAccessToken(SERVICE_ID);
const instance: EyevinnEncorePackager = await getInstance(
ctx,
SERVICE_ID,
name,
serviceAccessToken
);
if (!instance) {
throw new Error(`Encore Pckager Instance ${name} not found`);
}
return instance;
}
19 changes: 19 additions & 0 deletions src/resources/minio_minio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,22 @@ export async function createMinioInstance(
secretAccessKey: instance.RootPassword || ''
};
}

export async function getMinioInstance(ctx: Context, name: string) {
const serviceAccessToken = await ctx.getServiceAccessToken(SERVICE_ID);
const instance: MinioMinio = await getInstance(
ctx,
SERVICE_ID,
name,
serviceAccessToken
);
if (!instance) {
throw new Error(`Minio Instance ${name} not found`);
}
return {
name: instance.name,
endpoint: instance.url,
accessKeyId: 'root',
secretAccessKey: instance.RootPassword || ''
};
}
25 changes: 25 additions & 0 deletions src/resources/valkey_io_valkey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,28 @@ export async function createValkeyInstance(ctx: Context, name: string) {
}
return `redis://${redisPort.externalIp}:${redisPort.externalPort}`;
}

export async function getValkeyInstance(ctx: Context, name: string) {
const serviceAccessToken = await ctx.getServiceAccessToken(SERVICE_ID);
const instance: ValkeyIoValkey = await getInstance(
ctx,
SERVICE_ID,
name,
serviceAccessToken
);
if (!instance) {
throw new Error(`Valkey Instance ${name} not found`);
}

const ports = await getPortsForInstance(
ctx,
SERVICE_ID,
name,
serviceAccessToken
);
const redisPort = ports.find((port) => port.internalPort == 6379);
if (!redisPort) {
throw new Error(`Failed to get redis port for instance ${name}`);
}
return `redis://${redisPort.externalIp}:${redisPort.externalPort}`;
}
13 changes: 10 additions & 3 deletions src/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,20 @@ export const StorageBucket = z.object({
});
export type StorageBucket = z.infer<typeof StorageBucket>;

export const CreateVodPackage = z.object({
pipeline: z
.string()
.regex(/^[a-z0-9]+$/)
.describe('Name of the pipeline'),
source: z.string().describe('Source video URL')
});
export type CreateVodPackage = z.infer<typeof CreateVodPackage>;

export const CreateVodPipelineSchema = z.object({
name: z
.string()
.regex(/^[a-z0-9]+$/)
.describe('Name of the pipeline'),
redisUrl: z.string().describe('Redis URL'),
output: StorageBucket.describe('Storage bucket')
.describe('Name of the pipeline')
});
export type CreateVodPipelineSchema = z.infer<typeof CreateVodPipelineSchema>;

Expand Down
94 changes: 39 additions & 55 deletions src/tools/osc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,27 @@ import { z } from 'zod';
import {
CreateBucketSchema,
CreateDatabaseSchema,
CreateVodPackage,
CreateVodPipelineSchema,
RemoveVodPipelineSchema,
StorageBucket
} from '../schemas.js';
import { createValkeyInstance } from '../resources/valkey_io_valkey.js';
import { Context, removeInstance } from '@osaas/client-core';
import { Context } from '@osaas/client-core';
import { CallToolRequest } from '@modelcontextprotocol/sdk/types.js';
import { createMinioInstance } from '../resources/minio_minio.js';
import { createEncoreInstance } from '../resources/encore.js';
import {
createMinioInstance,
getMinioInstance
} from '../resources/minio_minio.js';
import { getEncoreInstance } from '../resources/encore.js';
import { createApacheCouchdbInstance } from '@osaas/client-services';
import { createEncoreCallbackListenerInstance } from '../resources/encore_callback_listener.js';
import { createEncorePackager } from '../resources/encore_packager.js';
import { getEncoreCallbackListenerInstance } from '../resources/encore_callback_listener.js';
import { getEncorePackager } from '../resources/encore_packager.js';
import {
createVod,
createVodPipeline,
removeVodPipeline
} from '@osaas/client-transcode';

export function listOscTools() {
return [
Expand All @@ -30,6 +39,12 @@ export function listOscTools() {
'Create an S3 compatible bucket in Eyevinn Open Source Cloud',
inputSchema: zodToJsonSchema(CreateBucketSchema)
},
{
name: 'osc_create_vod',
description:
'Create a VOD package using a VOD pipeline in Eyevinn Open Source Cloud',
inputSchema: zodToJsonSchema(CreateVodPackage)
},
{
name: 'osc_create_vod_pipeline',
description: 'Create a VOD pipeline in Eyevinn Open Source Cloud',
Expand Down Expand Up @@ -70,14 +85,18 @@ export async function handleOscToolRequest(
);
return { toolResult: { endpoint, accessKeyId, secretAccessKey } };
}
case 'osc_create_vod': {
const args = CreateVodPackage.parse(request.params.arguments);
const pipeline = await getVodPipeline(args.pipeline, context);
if (!pipeline) {
throw new Error(`Pipeline not found: ${args.pipeline}`);
}
const vodPackage = await createVod(pipeline, args.source, context);
return { toolResult: vodPackage };
}
case 'osc_create_vod_pipeline': {
const args = CreateVodPipelineSchema.parse(request.params.arguments);
const pipeline = await createVodPipeline(
args.name,
args.redisUrl,
args.output,
context
);
const pipeline = await createVodPipeline(args.name, context);
return { toolResult: pipeline };
}
case 'osc_remove_vod_pipeline': {
Expand Down Expand Up @@ -135,52 +154,17 @@ export async function createRedisInstance(name: string, context: Context) {
return await createValkeyInstance(context, name);
}

export async function createVodPipeline(
name: string,
redisUrl: string,
storage: StorageBucket,
context: Context
) {
const transcoder = await createEncoreInstance(context, name);
const encoreCallback = await createEncoreCallbackListenerInstance(
context,
name,
redisUrl,
transcoder.url
);
const packager = await createEncorePackager(
context,
name,
redisUrl,
`s3://${name}/`,
storage.accessKeyId,
storage.secretAccessKey,
storage.endpoint
);
export async function getVodPipeline(name: string, context: Context) {
const transcoder = await getEncoreInstance(context, name);
const encoreCallback = await getEncoreCallbackListenerInstance(context, name);
const storage = await getMinioInstance(context, name);
const packager = await getEncorePackager(context, name);

return {
name,
jobs: transcoder.jobs,
callbackUrl: encoreCallback.url,
output: packager.OutputFolder
output: packager.OutputFolder,
endpoint: storage.endpoint
};
}

export async function removeVodPipeline(name: string, context: Context) {
await removeInstance(
context,
'encore',
name,
await context.getServiceAccessToken('encore')
);
await removeInstance(
context,
'eyevinn-encore-callback-listener',
name,
await context.getServiceAccessToken('eyevinn-encore-callback-listener')
);
await removeInstance(
context,
'eyevinn-encore-packager',
name,
await context.getServiceAccessToken('eyevinn-encore-packager')
);
}

0 comments on commit b5508a3

Please sign in to comment.