From bef5dff5fa5f20a9f9c5c643030cd1935843415e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Cr=C3=A8me?= Date: Tue, 8 Nov 2022 21:48:31 +0100 Subject: [PATCH 01/24] Add trigger upload method --- src/nomalab.ts | 176 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 134 insertions(+), 42 deletions(-) diff --git a/src/nomalab.ts b/src/nomalab.ts index 08e4a6d..652dfb5 100644 --- a/src/nomalab.ts +++ b/src/nomalab.ts @@ -9,8 +9,8 @@ import { Organization, Path, Show, - ShowKind, ShowClass, + ShowKind, } from "./types.ts"; import * as mod from "https://deno.land/std@0.148.0/http/cookie.ts"; @@ -31,7 +31,12 @@ export class Nomalab { async getShow(showUuid: string): Promise { const response = await this.#fetch(`shows/${showUuid}`, {}); - if (!response.ok) this.#throwError(`ERROR - Can't find show with id ${showUuid}.`, response); + if (!response.ok) { + this.#throwError( + `ERROR - Can't find show with id ${showUuid}.`, + response, + ); + } return response.json() as Promise; } @@ -44,7 +49,12 @@ export class Nomalab { return response.json() as Promise; } - async createHierarchy(organizationId: string, name: string, kind: NodeKind, parent?: string): Promise { + async createHierarchy( + organizationId: string, + name: string, + kind: NodeKind, + parent?: string, + ): Promise { const response = await this.#requestWithSwitch( organizationId, "hierarchy", @@ -52,23 +62,31 @@ export class Nomalab { { name, parent, - kind - } + kind, + }, ); - if (!response.ok) this.#throwError(`ERROR - Can't create ${kind} ${name}.`, response); + if (!response.ok) { + this.#throwError(`ERROR - Can't create ${kind} ${name}.`, response); + } return response.json() as Promise; } - async createShow(nodeId: string, name: string, kind: ShowKind): Promise { + async createShow( + nodeId: string, + name: string, + kind: ShowKind, + ): Promise { const response = await this.#fetch(`hierarchy/${nodeId}/shows`, { method: "POST", bodyJsonObject: { name, - kind - } + kind, + }, }); - if (!response.ok) this.#throwError(`ERROR - Can't create show ${kind} ${name}.`, response); + if (!response.ok) { + this.#throwError(`ERROR - Can't create show ${kind} ${name}.`, response); + } const { id } = await response.json() as ShowClass; return id; } @@ -83,8 +101,9 @@ export class Nomalab { `users/switch`, { bodyJsonObject: { organization: organizationId }, - method: "POST" - }); + method: "POST", + }, + ); if (response.status != 200) { this.#throwError(`Can't switch to org ${organizationId}`, response); } @@ -99,17 +118,23 @@ export class Nomalab { const cookie = mod.getCookies(headers); const bodyJsonObject = method == "POST" ? (body ?? {}) : undefined; return this.#fetch( - partialUrl, { + partialUrl, + { bodyJsonObject, method, - cookieHeader: cookie - } + cookieHeader: cookie, + }, ); } async getChildren(nodeUuid: string): Promise { const response = await this.#fetch(`hierarchy/${nodeUuid}/children`, {}); - if (!response.ok) this.#throwError(`ERROR - Can't find children with id ${nodeUuid}.`, response); + if (!response.ok) { + this.#throwError( + `ERROR - Can't find children with id ${nodeUuid}.`, + response, + ); + } return response.json() as Promise; } @@ -129,13 +154,23 @@ export class Nomalab { async getNode(nodeUuid: string): Promise { const response = await this.#fetch(`hierarchy/${nodeUuid}`, {}); - if (!response.ok) this.#throwError(`ERROR - Can't find node with id ${nodeUuid}.`, response); + if (!response.ok) { + this.#throwError( + `ERROR - Can't find node with id ${nodeUuid}.`, + response, + ); + } return response.json() as Promise; } async getShowsForNode(nodeUuid: string): Promise { const response = await this.#fetch(`hierarchy/${nodeUuid}/shows`, {}); - if (!response.ok) this.#throwError(`ERROR - error when retrieving shows for node ${nodeUuid}.`, response); + if (!response.ok) { + this.#throwError( + `ERROR - error when retrieving shows for node ${nodeUuid}.`, + response, + ); + } return response.json() as Promise; } @@ -144,16 +179,23 @@ export class Nomalab { `admin/shows/path`, { bodyJsonObject: { showIds: [showUuid] }, - method: "POST" - } + method: "POST", + }, ); - if (!response.ok) this.#throwError(`ERROR - Can't find show with id ${showUuid}.`, response); + if (!response.ok) { + this.#throwError( + `ERROR - Can't find show with id ${showUuid}.`, + response, + ); + } return response.json() as Promise; } async getOrganizations(): Promise { const response = await this.#fetch(`organizations`, {}); - if (!response.ok) this.#throwError(`ERROR - Can't get organizations.`, response); + if (!response.ok) { + this.#throwError(`ERROR - Can't get organizations.`, response); + } return response.json() as Promise; } @@ -169,7 +211,9 @@ export class Nomalab { async getJob(jobUuid: string): Promise { const response = await this.#fetch(`jobs/${jobUuid}`, {}); - if (!response.ok) this.#throwError(`ERROR - Can't find job with id ${jobUuid}.`, response); + if (!response.ok) { + this.#throwError(`ERROR - Can't find job with id ${jobUuid}.`, response); + } return response.json() as Promise; } @@ -178,19 +222,26 @@ export class Nomalab { "aws/copy", { bodyJsonObject: payload, - method: "POST" - } + method: "POST", + }, ); - if (!response.ok) this.#throwError(`ERROR - Can't make a s3 copy with payload.${JSON.stringify(payload)}`, response); + if (!response.ok) { + this.#throwError( + `ERROR - Can't make a s3 copy with payload.${JSON.stringify(payload)}`, + response, + ); + } return Promise.resolve(); } async accept(showId: string): Promise { const response = await this.#fetch( `shows/${showId}/accept`, - { method: "POST" } + { method: "POST" }, ); - if (!response.ok) this.#throwError(`ERROR - Can't accept show. ${showId}`, response); + if (!response.ok) { + this.#throwError(`ERROR - Can't accept show. ${showId}`, response); + } return response.json() as Promise; } @@ -201,15 +252,39 @@ export class Nomalab { { bodyJsonObject: deliverPayload, method: "POST", + }, + ); + if (!response.ok) { + if (response.status == 409) { + throw new AlreadyPresentDeliverable( + "Can't deliver because of an already present deliverable.", + ); + } else { + this.#throwError( + `ERROR - Can't deliver with payload.${deliverPayload}`, + response, + ); } + } + return Promise.resolve() as Promise; + } + + async triggerUpload(broadcastableId: string) { + const response = await this.#fetch( + `broadcastables/${broadcastableId}/delivery`, + { method: "POST", bodyJsonObject: {} }, ); + console.log(response.headers); if (!response.ok) { if (response.status == 409) { throw new AlreadyPresentDeliverable( "Can't deliver because of an already present deliverable.", ); } else { - this.#throwError(`ERROR - Can't deliver with payload.${deliverPayload}`, response); + this.#throwError( + `ERROR - Can't accept and deliver show. [${broadcastableId}]`, + response, + ); } } return Promise.resolve() as Promise; @@ -225,14 +300,17 @@ export class Nomalab { `broadcastables/${broadcastableId}/delivery`, { method: "POST", bodyJsonObject: {} }, ); - console.log(response.headers) + console.log(response.headers); if (!response.ok) { if (response.status == 409) { throw new AlreadyPresentDeliverable( "Can't deliver because of an already present deliverable.", ); } else { - this.#throwError(`ERROR - Can't accept and deliver show. [${showId}]`, response); + this.#throwError( + `ERROR - Can't accept and deliver show. [${showId}]`, + response, + ); } } return Promise.resolve(); @@ -247,7 +325,7 @@ export class Nomalab { `broadcastables/${broadcastableId}/copyToOrganization`, { bodyJsonObject: { targetOrg: targetOrgId }, - method: "POST" + method: "POST", }, ); if (!response.ok) { @@ -256,7 +334,10 @@ export class Nomalab { "Can't deliver because of an already present deliverable.", ); } else { - this.#throwError(`ERROR - Can't deliver without transcoding to org id <${targetOrgId}>`, response); + this.#throwError( + `ERROR - Can't deliver without transcoding to org id <${targetOrgId}>`, + response, + ); } } return response.json() as Promise; @@ -266,10 +347,15 @@ export class Nomalab { const response = await this.#fetch( `files/${proxyId}/manifest`, { - contentType: "application/xml" - } + contentType: "application/xml", + }, ); - if (!response.ok) this.#throwError(`ERROR - Can't find manifest with proxyId ${proxyId}.`, response); + if (!response.ok) { + this.#throwError( + `ERROR - Can't find manifest with proxyId ${proxyId}.`, + response, + ); + } return response.blob() as Promise; } @@ -281,17 +367,23 @@ export class Nomalab { #fetch( partialUrl: string, optionalArg: { - method?: string, - bodyJsonObject?: unknown, - contentType?: string, - cookieHeader?: Record, + method?: string; + bodyJsonObject?: unknown; + contentType?: string; + cookieHeader?: Record; }, ): Promise { const myHeaders = new Headers(); - myHeaders.append("Content-Type", optionalArg.contentType ?? "application/json"); + myHeaders.append( + "Content-Type", + optionalArg.contentType ?? "application/json", + ); myHeaders.append("Authorization", `Bearer ${this.#apiToken} `); if (optionalArg.cookieHeader) { - myHeaders.append("Cookie", `sessionJwt=${optionalArg.cookieHeader["sessionJwt"]}`) + myHeaders.append( + "Cookie", + `sessionJwt=${optionalArg.cookieHeader["sessionJwt"]}`, + ); } const request = new Request( `https://${this.#contextSubDomain()}.nomalab.com/v3/${partialUrl}`, From 710ca104692213676f37cdd22c35bc94ef814c1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Cr=C3=A8me?= Date: Tue, 8 Nov 2022 22:24:52 +0100 Subject: [PATCH 02/24] change VF to VD --- src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.ts b/src/types.ts index d90dce8..3296e12 100644 --- a/src/types.ts +++ b/src/types.ts @@ -133,7 +133,7 @@ export interface SubtitleFormat { } export interface DeliverPayload { format: string; - versionMapping: "VFVO"|"VF"|"VO"; + versionMapping: "VO"|"VD"|"VOVD"; timecodeOut: string | null; timecodeIn: string | null; subtitles: DeliverSubtitle | null; From 410249f255776a9397e23e195046b98c3721df42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Cr=C3=A8me?= Date: Tue, 8 Nov 2022 22:28:45 +0100 Subject: [PATCH 03/24] Fix VDVO --- src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.ts b/src/types.ts index 3296e12..5602a46 100644 --- a/src/types.ts +++ b/src/types.ts @@ -133,7 +133,7 @@ export interface SubtitleFormat { } export interface DeliverPayload { format: string; - versionMapping: "VO"|"VD"|"VOVD"; + versionMapping: "VO"|"VD"|"VDVO"; timecodeOut: string | null; timecodeIn: string | null; subtitles: DeliverSubtitle | null; From 0f27750ae6438510db9dc4e5afb06073606b606b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Cr=C3=A8me?= Date: Wed, 9 Nov 2022 12:28:41 +0100 Subject: [PATCH 04/24] Add orgaByName --- src/nomalab.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/nomalab.ts b/src/nomalab.ts index 652dfb5..0eeb924 100644 --- a/src/nomalab.ts +++ b/src/nomalab.ts @@ -209,6 +209,16 @@ export class Nomalab { return Promise.resolve(organisation[0]); } + async getOrganizationByName(organizationName : string) : Promise { + const organisation = (await this.getOrganizations()).filter((org) => { + return org.name == organizationName; + }); + if (organisation.length == 0) { + return Promise.reject(`Org ${organizationName} not found`); + } + return Promise.resolve(organisation[0]); + } + async getJob(jobUuid: string): Promise { const response = await this.#fetch(`jobs/${jobUuid}`, {}); if (!response.ok) { From 432983a625e852a5bfefa170e0bc230b60aabb3d Mon Sep 17 00:00:00 2001 From: "perrine@nomalab.com" Date: Mon, 7 Nov 2022 10:56:02 +0100 Subject: [PATCH 05/24] If destRole is filled launch copyFromExt route --- src/nomalab.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/nomalab.ts b/src/nomalab.ts index 0eeb924..b73adb0 100644 --- a/src/nomalab.ts +++ b/src/nomalab.ts @@ -218,7 +218,7 @@ export class Nomalab { } return Promise.resolve(organisation[0]); } - + async getJob(jobUuid: string): Promise { const response = await this.#fetch(`jobs/${jobUuid}`, {}); if (!response.ok) { @@ -228,19 +228,22 @@ export class Nomalab { } async s3Upload(payload: CopyToBroadcastable): Promise { + const url = payload.destRole ? "aws/copyFromExt" : "aws/copy"; const response = await this.#fetch( - "aws/copy", + url, { bodyJsonObject: payload, method: "POST", }, ); + if (!response.ok) { this.#throwError( - `ERROR - Can't make a s3 copy with payload.${JSON.stringify(payload)}`, - response, + `ERROR - Can't make a s3 copy with payload.${JSON.stringify(payload)} on url ${url}.`, + response ); } + return Promise.resolve(); } From edd380a36313d8ef6ece49687a2f658a0f1655f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Cr=C3=A8me?= Date: Fri, 30 Dec 2022 12:09:53 +0100 Subject: [PATCH 06/24] Add Build. --- .github/workflows/build.yml | 15 ++++++++++++++ README.md | 13 ++++++++---- build.ts | 4 ++++ mod.ts | 8 ++++---- src/nomalab.ts | 8 +++++--- src/types.ts | 40 ++++++++++++++++++------------------- 6 files changed, 57 insertions(+), 31 deletions(-) create mode 100644 .github/workflows/build.yml create mode 100644 build.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..7a9bc4f --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,15 @@ +name: Build + +on: push + +jobs: + build: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: denoland/setup-deno@v1.1.1 + with: + deno-version: v1.x # Run with latest stable Deno. + - run: deno fmt --check + - run: deno lint + - run: deno build.ts diff --git a/README.md b/README.md index ec35f00..c80cb71 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,17 @@ # deno_lib + Deno lib to handle Nomalab API with deno # Example + ```ts -import { Nomalab, Show } from "https://raw.githubusercontent.com/nomalab/deno_lib/main/mod.ts"; +import { + Nomalab, + Show, +} from "https://raw.githubusercontent.com/nomalab/deno_lib/main/mod.ts"; -const apiToken = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' -const context = 'beta' +const apiToken = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +const context = "beta"; const nomalab = new Nomalab(context, apiToken); -const show = await nomalab.getShow('xxx-x-xxx--xxxx-xxxxx'); +const show = await nomalab.getShow("xxx-x-xxx--xxxx-xxxxx"); ``` diff --git a/build.ts b/build.ts new file mode 100644 index 0000000..ce89e25 --- /dev/null +++ b/build.ts @@ -0,0 +1,4 @@ +let resp = await fetch("https://example.com"); +console.log(resp.status); // 200 +console.log(resp.headers.get("Content-Type")); // "text/html" +console.log(await resp.text()); // "Hello, World!" diff --git a/mod.ts b/mod.ts index 11045c7..b10ca34 100644 --- a/mod.ts +++ b/mod.ts @@ -1,19 +1,19 @@ export * from "./src/nomalab.ts"; export type { BroadcastableKind, - Delivery, Deliveries, DeliverPayload, + Delivery, + FileWrapper, Job, Material, - FileWrapper, Node, - NodeKind, NodeClass, + NodeKind, Organization, Path, Show, - ShowKind, ShowClass, + ShowKind, Subtitle, } from "./src/types.ts"; diff --git a/src/nomalab.ts b/src/nomalab.ts index b73adb0..bfe4174 100644 --- a/src/nomalab.ts +++ b/src/nomalab.ts @@ -209,7 +209,7 @@ export class Nomalab { return Promise.resolve(organisation[0]); } - async getOrganizationByName(organizationName : string) : Promise { + async getOrganizationByName(organizationName: string): Promise { const organisation = (await this.getOrganizations()).filter((org) => { return org.name == organizationName; }); @@ -239,8 +239,10 @@ export class Nomalab { if (!response.ok) { this.#throwError( - `ERROR - Can't make a s3 copy with payload.${JSON.stringify(payload)} on url ${url}.`, - response + `ERROR - Can't make a s3 copy with payload.${ + JSON.stringify(payload) + } on url ${url}.`, + response, ); } diff --git a/src/types.ts b/src/types.ts index 5602a46..d2fcd2e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -70,7 +70,7 @@ export interface Files { } export interface FileWrapper { - file : FileClass; + file: FileClass; } export interface Material extends FileWrapper { container: Container; @@ -92,28 +92,28 @@ export interface Container { } export interface Deliveries { - shows: Show[]; - nodes: DeliveryNode[]; - formats: DeliveriesFormat[]; + shows: Show[]; + nodes: DeliveryNode[]; + formats: DeliveriesFormat[]; subtitleFormats: SubtitleFormat[]; } export interface DeliveriesFormat { - organizationId: string; - organizationName: string; + organizationId: string; + organizationName: string; organizationAllowDeliveryWithoutTranscoding: boolean; - formats: FormatElement[]; + formats: FormatElement[]; } export interface FormatElement { - id: string; + id: string; name: string; } export interface DeliveryNode { showId: string; - id: string; - name: string; + id: string; + name: string; parent: null | string; } @@ -127,18 +127,18 @@ export enum ArchiveState { } export interface SubtitleFormat { - organizationId: string; + organizationId: string; organizationName: string; - subtitleFormats: FormatElement[]; + subtitleFormats: FormatElement[]; } export interface DeliverPayload { - format: string; - versionMapping: "VO"|"VD"|"VDVO"; - timecodeOut: string | null; - timecodeIn: string | null; - subtitles: DeliverSubtitle | null; - targetOrg: string; - targetId: null; + format: string; + versionMapping: "VO" | "VD" | "VDVO"; + timecodeOut: string | null; + timecodeIn: string | null; + subtitles: DeliverSubtitle | null; + targetOrg: string; + targetId: null; } export interface DeliverSubtitle { format: string | null; @@ -570,7 +570,7 @@ export enum BroadcastableKind { Subtitle = "Subtitle", Material = "Material", Audio = "Audio", - Extra = "Extra" + Extra = "Extra", } export interface CopyToBroadcastable { From 2addb2e2924ad5e0920771db5062af1eaf7064ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Cr=C3=A8me?= Date: Fri, 30 Dec 2022 14:12:52 +0100 Subject: [PATCH 07/24] remove lint problems. --- build.ts | 2 +- src/types.ts | 45 +++++++++++++++++++++++---------------------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/build.ts b/build.ts index ce89e25..6408829 100644 --- a/build.ts +++ b/build.ts @@ -1,4 +1,4 @@ -let resp = await fetch("https://example.com"); +const resp = await fetch("https://example.com"); console.log(resp.status); // 200 console.log(resp.headers.get("Content-Type")); // "text/html" console.log(await resp.text()); // "Hello, World!" diff --git a/src/types.ts b/src/types.ts index d2fcd2e..62337c4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -37,18 +37,18 @@ export interface Show { jobs: Job[]; creator: Creator; organization: ShowOrganization; - channels: any[]; - invitations: any[]; - timeline: any[]; - extras: any[]; + channels: unknown[]; + invitations: unknown[]; + timeline: unknown[]; + extras: unknown[]; activeBroadcastable: ActiveBroadcastable; - previousBroadcastables: any[]; + previousBroadcastables: unknown[]; } export interface ActiveBroadcastable { broadcastable: Broadcastable; files: Files; - comments: any[]; + comments: unknown[]; } export interface Broadcastable { @@ -118,7 +118,7 @@ export interface DeliveryNode { } export interface Subtitles { - subtitle: any[]; + subtitle: unknown[]; } export enum ArchiveState { @@ -160,7 +160,7 @@ export interface DeliveryTranscoding { startedAt: string; progressedAt: string; log: null | string; - warning: any[]; + warning: unknown[]; } export enum Phase { @@ -197,7 +197,7 @@ export interface FileTranscoding { startedAt: string; progressedAt: string; log: null | string; - warning: any[]; + warning: unknown[]; } export interface Upload { @@ -247,8 +247,8 @@ export interface StreamValue { } export interface Issues { - errors: any[]; - warnings: any[]; + errors: unknown[]; + warnings: unknown[]; } export interface PurpleNodeType { @@ -340,7 +340,7 @@ export interface LivingstoneSouthernWhiteFacedOwl { } export interface Purple { - area: any[]; + area: unknown[]; name: string; value: null | string; mapping: MaxShortTermLoudness[]; @@ -361,6 +361,7 @@ export interface Fluffy { value: null | string; } +// deno-lint-ignore no-empty-interface export interface ProgramLoudnessEbu { } @@ -369,16 +370,16 @@ export interface MP4TimeCodeTrackClass { } export interface MP4TimeCodeTrack { - area: any[]; + area: unknown[]; name: string; value: null; - mapping: any[]; + mapping: unknown[]; SystemItem: ProgramLoudnessEbu[]; TimeCodeTrack: TimeCodeTrack[]; TimecodeTrack: ProgramLoudnessEbu[]; ProgramLoudnessEBU: ProgramLoudnessEbu[]; VideoTrackProperty: ProgramLoudnessEbu[]; - MaxShortTermLoudness: any[]; + MaxShortTermLoudness: unknown[]; } export interface TimeCodeTrack { @@ -397,16 +398,16 @@ export interface ProgLoudnessEBUClass { } export interface ProgLoudnessEBU { - area: any[]; + area: unknown[]; name: string; value: null; - mapping: any[]; + mapping: unknown[]; SystemItem: ProgramLoudnessEbu[]; TimeCodeTrack: ProgramLoudnessEbu[]; TimecodeTrack: ProgramLoudnessEbu[]; ProgramLoudnessEBU: ProgramLoudnessEBU[]; VideoTrackProperty: ProgramLoudnessEbu[]; - MaxShortTermLoudness: any[]; + MaxShortTermLoudness: unknown[]; } export interface ProgramLoudnessEBU { @@ -484,8 +485,8 @@ export interface Subtitle extends FileWrapper { proxies: Proxies; reportXml: null; reportPdf: null; - deliveries: any[]; - segments: any[]; + deliveries: unknown[]; + segments: unknown[]; } export interface FluffyStream { @@ -532,8 +533,8 @@ export interface ShowOrganization { allowDeliveryWithoutTranscoding: boolean; logo: null; webhooks: Webhook[]; - formats: any[]; - subtitleFormats: any[]; + formats: unknown[]; + subtitleFormats: unknown[]; } export interface Webhook { From 34ccf897e9a9ad3a3b99e3fc201481f96b9d8a4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Cr=C3=A8me?= Date: Fri, 30 Dec 2022 14:14:02 +0100 Subject: [PATCH 08/24] fix. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7a9bc4f..aa0a337 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,4 +12,4 @@ jobs: deno-version: v1.x # Run with latest stable Deno. - run: deno fmt --check - run: deno lint - - run: deno build.ts + - run: deno run --allow-all build.ts From a02a386e50675cbfb7b519cda595809d33496f70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Cr=C3=A8me?= Date: Fri, 30 Dec 2022 15:10:29 +0100 Subject: [PATCH 09/24] test --- build.ts | 5 +---- deno.jsonc | 12 ++++++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 deno.jsonc diff --git a/build.ts b/build.ts index 6408829..21c37c3 100644 --- a/build.ts +++ b/build.ts @@ -1,4 +1 @@ -const resp = await fetch("https://example.com"); -console.log(resp.status); // 200 -console.log(resp.headers.get("Content-Type")); // "text/html" -console.log(await resp.text()); // "Hello, World!" +console.log(`GITHUB_REF_NAME : ${Deno.env.get("GITHUB_REF_NAME")}`); diff --git a/deno.jsonc b/deno.jsonc new file mode 100644 index 0000000..8c07a93 --- /dev/null +++ b/deno.jsonc @@ -0,0 +1,12 @@ +{ + "lint": { + "files": { + "include": ["src/"] + } + }, + "fmt": { + "files": { + "include": ["src/"] + } + } +} From ac00eff265c8f419cd5b217a30699235ed0fd668 Mon Sep 17 00:00:00 2001 From: Maxime Dantec Date: Fri, 16 Dec 2022 11:18:33 +0100 Subject: [PATCH 10/24] add needed apis for multi delivery et al. --- deno.jsonc | 3 + mod.ts | 20 +---- src/deps.ts | 2 + src/formats.ts | 196 +++++++++++++++++++++++++++++++++++++++++++++++++ src/nomalab.ts | 139 ++++++++++++++++++++++++----------- src/types.ts | 73 ++++++++++++++++-- 6 files changed, 366 insertions(+), 67 deletions(-) create mode 100644 src/deps.ts create mode 100644 src/formats.ts diff --git a/deno.jsonc b/deno.jsonc index 8c07a93..41cb719 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -7,6 +7,9 @@ "fmt": { "files": { "include": ["src/"] + }, + "options": { + "lineWidth": 80 } } } diff --git a/mod.ts b/mod.ts index b10ca34..3d18903 100644 --- a/mod.ts +++ b/mod.ts @@ -1,19 +1,3 @@ export * from "./src/nomalab.ts"; -export type { - BroadcastableKind, - Deliveries, - DeliverPayload, - Delivery, - FileWrapper, - Job, - Material, - Node, - NodeClass, - NodeKind, - Organization, - Path, - Show, - ShowClass, - ShowKind, - Subtitle, -} from "./src/types.ts"; +export * from "./src/types.ts"; +export * from "./src/formats.ts"; diff --git a/src/deps.ts b/src/deps.ts new file mode 100644 index 0000000..57bd117 --- /dev/null +++ b/src/deps.ts @@ -0,0 +1,2 @@ +export * from "https://deno.land/std@0.170.0/http/cookie.ts"; +export * from "https://deno.land/std@0.170.0/testing/asserts.ts"; diff --git a/src/formats.ts b/src/formats.ts new file mode 100644 index 0000000..c17e899 --- /dev/null +++ b/src/formats.ts @@ -0,0 +1,196 @@ +export type Format = { + id: string; + name: string; + specification: FormatSpec; + audioCodec: FormatAudioCodec; + audioBitrate?: number; + startTimecode: string; + frameRate: FrameRate; + noFrameRateConversion: boolean; + ffmpegArgs?: string; + audioArgs?: string; + bmxArgs?: string; + options?: FormatMxfOptions; + noLoudness?: boolean; + loudnessRange?: number; + loudnessProgram?: number; + loudnessTruePeak?: number; + videoEdit?: FormatVideoEdit; + writeTimecode: boolean; + videoBitrate?: number; + encodeSubtitle: boolean; + crop?: FormatCropParameters; + scale?: FormatScaleParameters; + clip?: FormatClipParameters; + qcTestPlan: string; + qcReportTemplate: string; + subtitleVersion?: Version; + subtitleTypeVersion?: SubtitleTypeVersion; + subtitleFormat?: string; +}; + +export enum FormatSpec { + Mxf = "Mxf", + Mp4 = "Mp4", + ProRes422 = "ProRes422", + ProRes4444 = "ProRes4444", + ADN = "ADN", + AVCIntra100 = "AVCIntra100", + IMX50 = "IMX50", + Mp4Salto = "Mp4Salto", + MovH264 = "MovH264", + MovHevc = "MovHevc", + MxfProgressive = "MxfProgressive", + Demux = "Demux", + AudioExtract = "AudioExtract", +} + +export enum FormatAudioCodec { + PCMS24LE = "PCMS24LE", + PCMS16LE = "PCMS16LE", + PCMS24BE = "PCMS24BE", + AAC = "AAC", + MP3 = "MP3", + MOV_Conteneur = "MOV_Conteneur", +} + +export type FrameRate = { + id: string; + numerator: number; + denominator: number; +}; + +export type FormatMxfOptions = { + controlInstantaneousBitrate: boolean; + as10: boolean; + as11: boolean; + bwf: boolean; + afd?: number; + version12: boolean; + deinterlacing: boolean; + qmax?: number; +}; + +export type FormatVideoEdit = { + before: FormatSegment[]; + after: FormatSegment[]; +}; + +export type FormatSegment = { + index: number; + kind: FormatSegmentKind; + duration: number; + sourceName?: string; + sourceBucket?: string; + sourceKey?: string; + subtract: boolean; +}; + +export enum FormatSegmentKind { + Mire = "Mire", + Black = "Black", + Slate = "Slate", + Video = "Video", + Countdown = "Countdown", +} + +export type FormatCropParameters = { + leftPx: number; + rightPx: number; + topPx: number; + bottomPx: number; +}; + +export type FormatScaleParameters = { + width: number; + height: number; + scaleAspectRatio: boolean; + scaleLetterbox: boolean; +}; + +export type FormatClipParameters = { + clipMin: number; + clipMax: number; +}; + +export type FormatSubtitle = { + id: string; + name: string; + fileFormat: SubtitleFileFormat; + subtitleTimecode?: string; + subtitleFrameRate?: FrameRate; + displayStandard?: SubtitleDisplayStandard; + offset?: string; +}; + +export enum SubtitleFileFormat { + STL = "STL", + WebVTT = "WebVTT", + SRT = "SRT", +} + +export enum SubtitleDisplayStandard { + Open = "Open", + Teletext1 = "Teletext1", + Teletext2 = "Teletext2", +} + +export enum SubtitleTypeVersion { + PARTIAL = "PARTIAL", + COMPLETE = "COMPLETE", + COMPLETE_WITHOUT_PARTIAL = "COMPLETE_WITHOUT_PARTIAL", + SDH = "SDH", +} + +export enum SegmentLabel { + OpeningCredits = "OpeningCredits", + EndingCredits = "EndingCredits", + Introduction = "Introduction", + Program = "Program", + Trailer = "Trailer", + Advertising = "Advertising", + TestPattern = "TestPattern", + Black = "Black", + Slate = "Slate", + NeutralBases = "NeutralBases", + CustomDelivery = "CustomDelivery", +} + +export enum Version { + ARA = "ARA", + CHI = "CHI", + KOR = "KOR", + DAN = "DAN", + DUT = "DUT", + HEB = "HEB", + NLD = "NLD", + RUS = "RUS", + SWE = "SWE", + FRA = "FRA", + GER = "GER", + ITA = "ITA", + POR = "POR", + ENG = "ENG", + SPA = "SPA", + JPN = "JPN", + NOR = "NOR", + UKR = "UKR", + INT = "INT", + NOTHING = "", +} + +export enum Mapping { + AsMaster = "AsMaster", + NoSound = "NoSound", + VD = "VD", + VO = "VO", + VI = "VI", + VDVO = "VDVO", + VOAD = "VOAD", + VDAD = "VDAD", + VIVD = "VIVD", + VIVO = "VIVO", + VDVOAD = "VDVOAD", + VDVIVONLY = "VDVIVONLY", + VDVIMEVONLY = "VDVIMEVONLY", +} diff --git a/src/nomalab.ts b/src/nomalab.ts index bfe4174..a4d2a50 100644 --- a/src/nomalab.ts +++ b/src/nomalab.ts @@ -1,18 +1,24 @@ import { CopyToBroadcastable, + DeliverableOrganization, Deliveries, DeliverPayload, + DeliveryApi, Job, + MeUser, Node, NodeClass, NodeKind, Organization, Path, + Segment, Show, ShowClass, ShowKind, + SubtitleFormat, } from "./types.ts"; -import * as mod from "https://deno.land/std@0.148.0/http/cookie.ts"; +import { Format } from "./formats.ts"; +import { assert, getSetCookies } from "./deps.ts"; export class AlreadyPresentDeliverable extends Error { constructor(msg: string) { @@ -29,6 +35,14 @@ export class Nomalab { this.#apiToken = apiToken; } + async me(): Promise { + const response = await this.#fetch(`users/me`, {}); + if (!response.ok) { + this.#throwError(`ERROR - Can't load user infos.`, response); + } + return response.json() as Promise; + } + async getShow(showUuid: string): Promise { const response = await this.#fetch(`shows/${showUuid}`, {}); if (!response.ok) { @@ -44,6 +58,7 @@ export class Nomalab { const response = await this.#requestWithSwitch( organizationId, "hierarchy", + {}, ); if (!response.ok) this.#throwError(`ERROR - Can't find root.`, response); return response.json() as Promise; @@ -58,11 +73,13 @@ export class Nomalab { const response = await this.#requestWithSwitch( organizationId, "hierarchy", - "POST", { - name, - parent, - kind, + method: "POST", + bodyJsonObject: { + name, + parent, + kind, + }, }, ); if (!response.ok) { @@ -94,8 +111,12 @@ export class Nomalab { async #requestWithSwitch( organizationId: string, partialUrl: string, - method?: "GET" | "POST", - body?: unknown, + optionalArg: { + method?: string; + bodyJsonObject?: unknown; + contentType?: string; + cookieHeader?: Record; + }, ): Promise { const response = await this.#fetch( `users/switch`, @@ -104,27 +125,17 @@ export class Nomalab { method: "POST", }, ); - if (response.status != 200) { + if (!response.ok) { this.#throwError(`Can't switch to org ${organizationId}`, response); } - const headers = new Headers(); - const setCookie = response.headers.get("set-cookie"); - if (setCookie != null) { - headers.append("Cookie", setCookie); - } + + const setCookie = getSetCookies(response.headers)[0]; + assert(setCookie, "No cookie"); + this.#apiToken = setCookie.value; + // To avoid leak since we don't use the body of the response await response.body?.cancel(); - - const cookie = mod.getCookies(headers); - const bodyJsonObject = method == "POST" ? (body ?? {}) : undefined; - return this.#fetch( - partialUrl, - { - bodyJsonObject, - method, - cookieHeader: cookie, - }, - ); + return this.#fetch(partialUrl, optionalArg); } async getChildren(nodeUuid: string): Promise { @@ -219,6 +230,20 @@ export class Nomalab { return Promise.resolve(organisation[0]); } + async jobs(organizationId: string): Promise { + const response = await this.#fetch( + `organizations/${organizationId}/jobs`, + {}, + ); + if (!response.ok) { + this.#throwError( + `ERROR - Can't find jobs for organisation id ${organizationId}.`, + response, + ); + } + return response.json() as Promise; + } + async getJob(jobUuid: string): Promise { const response = await this.#fetch(`jobs/${jobUuid}`, {}); if (!response.ok) { @@ -236,16 +261,10 @@ export class Nomalab { method: "POST", }, ); - if (!response.ok) { - this.#throwError( - `ERROR - Can't make a s3 copy with payload.${ - JSON.stringify(payload) - } on url ${url}.`, - response, - ); + console.log("[S3 UPLOAD]", "url:", url, "payload:", payload); + this.#throwError(`ERROR - Can't make a s3 copy with payload.`, response); } - return Promise.resolve(); } @@ -275,10 +294,7 @@ export class Nomalab { "Can't deliver because of an already present deliverable.", ); } else { - this.#throwError( - `ERROR - Can't deliver with payload.${deliverPayload}`, - response, - ); + this.#throwError(`ERROR - Can't deliver with payload.`, response); } } return Promise.resolve() as Promise; @@ -289,17 +305,13 @@ export class Nomalab { `broadcastables/${broadcastableId}/delivery`, { method: "POST", bodyJsonObject: {} }, ); - console.log(response.headers); if (!response.ok) { if (response.status == 409) { throw new AlreadyPresentDeliverable( "Can't deliver because of an already present deliverable.", ); } else { - this.#throwError( - `ERROR - Can't accept and deliver show. [${broadcastableId}]`, - response, - ); + this.#throwError(`ERROR - Can't deliver show.`, response); } } return Promise.resolve() as Promise; @@ -315,7 +327,6 @@ export class Nomalab { `broadcastables/${broadcastableId}/delivery`, { method: "POST", bodyJsonObject: {} }, ); - console.log(response.headers); if (!response.ok) { if (response.status == 409) { throw new AlreadyPresentDeliverable( @@ -374,11 +385,53 @@ export class Nomalab { return response.blob() as Promise; } + async getDeliverableOrgs() { + const response = await this.#fetch(`organizations/deliverables`, {}); + return response.json() as Promise; + } + + async getFileSegments(materialId: string) { + const response = await this.#fetch(`files/${materialId}/segments`, {}); + return response.json() as Promise; + } + + async getOrganizationDeliveries(orgId: string) { + const response = await this.#fetch( + `organizations/${orgId}/shows/deliveries`, + {}, + ); + return response.json() as Promise; + } + + async getFormats(orgId: string) { + const response = await this.#fetch(`organizations/${orgId}/formats`, {}); + return response.json() as Promise; + } + + async getSubtitleFormats(orgId: string) { + const response = await this.#fetch( + `organizations/${orgId}/subtitleFormats`, + {}, + ); + return response.json() as Promise; + } + #throwError(message: string, response: Response): void { console.error(message); console.error(response); throw new Error(message); } + proxy( + partialUrl: string, + optionalArg?: { + method?: string; + bodyJsonObject?: unknown; + contentType?: string; + cookieHeader?: Record; + }, + ): Promise { + return this.#fetch(partialUrl, optionalArg || {}); + } #fetch( partialUrl: string, optionalArg: { @@ -393,7 +446,7 @@ export class Nomalab { "Content-Type", optionalArg.contentType ?? "application/json", ); - myHeaders.append("Authorization", `Bearer ${this.#apiToken} `); + myHeaders.append("Authorization", `Bearer ${this.#apiToken}`); if (optionalArg.cookieHeader) { myHeaders.append( "Cookie", diff --git a/src/types.ts b/src/types.ts index 62337c4..1ce8bdc 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,3 +1,20 @@ +import * as Formats from "./formats.ts"; + +export interface MeUser { + admin: boolean; + avatar: string; + disableOrganizationEmails: boolean; + email: string; + id: string; + name: string; + organization: string; + organizations: { + organizationId: string; + organizationName: string; + logo?: string; + }[]; +} + export interface Job { id: string; createdAt: string; @@ -80,6 +97,7 @@ export interface Material extends FileWrapper { reportPdf: FileClass; deliveries: Delivery[]; segments: Segment[]; + subtitleWarnings: SubtitleWarning[]; } export interface Container { @@ -121,6 +139,11 @@ export interface Subtitles { subtitle: unknown[]; } +export interface SubtitleWarning { + name: string; + timecode: string; +} + export enum ArchiveState { Active = "active", Archived = "archived", @@ -133,12 +156,13 @@ export interface SubtitleFormat { } export interface DeliverPayload { format: string; - versionMapping: "VO" | "VD" | "VDVO"; + versionMapping: Formats.Mapping; timecodeOut: string | null; timecodeIn: string | null; + segments?: string[]; subtitles: DeliverSubtitle | null; targetOrg: string; - targetId: null; + targetId: string | null; } export interface DeliverSubtitle { format: string | null; @@ -424,7 +448,7 @@ export interface Proxies { export interface Segment { id: string; - label: string; + label: Formats.SegmentLabel; creator: Creator; createdAt: string; file: string; @@ -600,11 +624,30 @@ export interface Organization { allowDeliveryWithoutTranscoding: boolean; replication: boolean; logo: null | string; - formats: Format[]; - subtitleFormats: Format[]; + formats: FormatClass[]; + subtitleFormats: FormatClass[]; +} +export interface ShowOrganization { + id: string; + name: string; + createdAt: string; + qcMasterTestPlan: string; + qcMasterReportTemplate: string; + enableCreationEmail: boolean; + enableVideoReadyEmail: boolean; + enableUploadSuccessEmail: boolean; + enableAutoAccept: boolean; + enableAutoReject: boolean; + broadcaster: null; + manualDelivery: boolean; + allowDeliveryWithoutTranscoding: boolean; + logo: null; + webhooks: Webhook[]; + formats: unknown[]; + subtitleFormats: unknown[]; } -export interface Format { +export interface FormatClass { id: string; name: string; } @@ -700,3 +743,21 @@ export interface NodeClass { kind: NodeKind; state: string; } + +export type DeliverableOrganization = { + id: string; + name: string; + allowDeliveryWithoutTranscoding: boolean; +}; + +export type DeliveryApi = { + nodes: NodeDelivery[]; + shows: ShowClass[]; +}; + +export type NodeDelivery = { + showId: string; + id: string; + name: string; + parent?: string; +}; From 2a03a7215b4257e93f00571cc99573b5d399d9aa Mon Sep 17 00:00:00 2001 From: Maxime Dantec Date: Tue, 3 Jan 2023 18:15:38 +0100 Subject: [PATCH 11/24] add .githooks/ --- .githooks/pre-commit | 3 +++ 1 file changed, 3 insertions(+) create mode 100755 .githooks/pre-commit diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..f361c3e --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,3 @@ +#!/bin/sh + +deno fmt From 699c18075aa5a72d3206a6276f1cc692a4d9b705 Mon Sep 17 00:00:00 2001 From: Maxime Dantec Date: Fri, 3 Feb 2023 10:19:10 +0100 Subject: [PATCH 12/24] add broadcastable apis --- src/formats.ts | 27 ++++ src/types.ts | 419 +++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 363 insertions(+), 83 deletions(-) diff --git a/src/formats.ts b/src/formats.ts index c17e899..4222ad5 100644 --- a/src/formats.ts +++ b/src/formats.ts @@ -194,3 +194,30 @@ export enum Mapping { VDVIVONLY = "VDVIVONLY", VDVIMEVONLY = "VDVIMEVONLY", } + +export enum Layout { + Mono = "Mono", + DualMono = "DualMono", + Stereo = "Stereo", + StereoL = "StereoL", + StereoR = "StereoR", + FiveDotOne = "FiveDotOne", + FiveDotOneL = "FiveDotOneL", + FiveDotOneR = "FiveDotOneR", + FiveDotOneC = "FiveDotOneC", + FiveDotOneSL = "FiveDotOneSL", + FiveDotOneSR = "FiveDotOneSR", + FiveDotOneLFE = "FiveDotOneLFE", + SevenDotOne = "SevenDotOne", + OneTrack = "OneTrack", +} + +export enum TypeVersion { + ORIGINAL = "ORIGINAL", + DUBBED = "DUBBED", + AD = "AD", + MUTE = "MUTE", + INT = "INT", + ME = "ME", + VONLY = "VONLY", +} diff --git a/src/types.ts b/src/types.ts index 1ce8bdc..4d41f96 100644 --- a/src/types.ts +++ b/src/types.ts @@ -91,7 +91,7 @@ export interface FileWrapper { } export interface Material extends FileWrapper { container: Container; - streams: Array>; + streams: FileStream[]; proxies: Proxies; reportXml: FileClass; reportPdf: FileClass; @@ -144,11 +144,6 @@ export interface SubtitleWarning { timecode: string; } -export enum ArchiveState { - Active = "active", - Archived = "archived", -} - export interface SubtitleFormat { organizationId: string; organizationName: string; @@ -174,24 +169,7 @@ export interface Delivery { id: string; title: string; organizationName: string; - transcoding: DeliveryTranscoding; -} - -export interface DeliveryTranscoding { - file: string; - phase: Phase; - progress: number | null | string; - startedAt: string; - progressedAt: string; - log: null | string; - warning: unknown[]; -} - -export enum Phase { - Encoding = "Encoding", - Finished = "Finished", - Packaging = "Packaging", - Waiting = "Waiting", + transcoding: Transcoding; } export interface FileClass { @@ -210,34 +188,10 @@ export interface FileClass { uploadedAt: null | string; verification: Verification | null; sourceId: null | string; - transcoding: FileTranscoding | null; + transcoding: Transcoding | null; format: null; } -export interface FileTranscoding { - file: string; - phase: Phase; - progress: number | null; - startedAt: string; - progressedAt: string; - log: null | string; - warning: unknown[]; -} - -export interface Upload { - file: string; - user: string; - progress: number; - progressedAt: string; - pausedAt: null; - completedAt: string; - error: null; - s3Id: string; - speed: number; - secondsLeft: number; - source: string; -} - export interface Verification { progress: number; error: null; @@ -266,7 +220,7 @@ export interface StreamValue { name: string; issues: Issues; parent: string; - nodeType: Array | FluffyNodeType | string>; + nodeType: NodeType; properties: Properties; } @@ -275,24 +229,6 @@ export interface Issues { warnings: unknown[]; } -export interface PurpleNodeType { - duration: string; - startTimecode: string; -} - -export interface FluffyNodeType { - frameRate?: null; - chromaFormat?: null; - scanningType?: null; - cadencePattern?: null; - activePixelsArea?: null; - displayAspectRatio?: null; - isMute?: boolean; - channels?: Channel[]; - loudnessRange?: string; - programLoudness?: string; -} - export interface Channel { label: string; truePeakLevel: string; @@ -443,7 +379,7 @@ export interface ProgramLoudnessEBU { export interface Proxies { lowRes: FileClass; - hiRes: null; + hiRes: FileClass | null; } export interface Segment { @@ -475,7 +411,13 @@ export interface OrganizationElement { logo: null | string; } -export interface PurpleStream { +export type FileStream = + | ["VideoStream", FileVideoStream] + | ["AudioStream", FileAudioStream] + | ["SubtitleStream", FileSubtitleStream] + | ["DataStream", FileDataStream]; + +export interface FileVideoStream { fileId: string; index: number; codecName?: string; @@ -486,21 +428,50 @@ export interface PurpleStream { displayAspectRatioNumerator?: number; displayAspectRatioDenominator?: number; bitRate?: number; - rFrameRateNumerator?: number; - rFrameRateDenominator?: number; - level?: null; - profile?: null; - startTime?: number; + rFrameRateNumerator: number; + rFrameRateDenominator: number; + level?: number; + profile?: string; chromaSubsampling?: string; scanningType?: string; timecode?: string; - sampleRate?: number; - sampleFormat?: string; - channels?: number; - bitsPerSample?: number; - channelLayout?: string; - version?: string; - typeVersion?: string; + nbFrames?: number; +} + +export interface FileAudioStream { + fileId: string; + index: number; + codecName: string; + codecLongName: string; + duration?: number; + sampleRate: number; + sampleFormat: string; + channels: number; + bitsPerSample: number; + bitRate: number; + channelLayout: Formats.Layout | null; + version: Formats.Version | null; + typeVersion: Formats.TypeVersion | null; +} + +export interface FileSubtitleStream { + fileId: string; + index: number; + codecName?: string; + codecLongName?: string; + version?: Formats.Version; + frameRateNumerator?: number; + frameRateDenominator?: number; + startTimecode?: string; + firstCue?: string; + subtitleType?: string; + typeVersion: Formats.SubtitleTypeVersion | null; +} + +export interface FileDataStream { + fileId: string; + index: number; + timecode?: string; } export interface Subtitle extends FileWrapper { @@ -587,7 +558,7 @@ export interface ShowClass { accepted: boolean; commandInfoXML: null; kind: ShowKind; - state: ArchiveState; + state: State; parent: string; } @@ -627,6 +598,7 @@ export interface Organization { formats: FormatClass[]; subtitleFormats: FormatClass[]; } + export interface ShowOrganization { id: string; name: string; @@ -761,3 +733,284 @@ export type NodeDelivery = { name: string; parent?: string; }; + +export interface FileContainer { + fileId: string; + formatName: string; + formatLongName: string; + duration?: number; + bitRate?: number; + timecode?: string; +} + +export interface FileLike { + name: string; + mimeType?: string; +} + +export enum BroadcastableFileKind { + ProxyManifest = "ProxyManifest", + ProxyDashVideo = "ProxyDashVideo", + ProxyAudio = "ProxyAudio", + ProxySubtitle = "ProxySubtitle", + VerificationReportPdf = "VerificationReportPdf", + VerificationReportXml = "VerificationReportXml", + Video = "Video", + Audio = "Audio", + Subtitle = "Subtitle", + Extra = "Extra", +} + +export interface File { + id: string; + createdAt: string; + name: string; + size: number; + mimeType?: string; + bucket: string; + key: string; + kind: Kind; + uploaderId?: string; + upload: Upload | null; + uploadedAt?: string; + verification: Verification | null; + sourceId?: string; + transcoding: Transcoding | null; + state: State; + stateExpireAt?: string; + format: Formats.Format | null; +} + +export interface ExtraApi { + file: File; + proxy: File | null; + segments: FileSegment[]; + container: FileContainer | null; + streams: FileStream[]; +} + +export interface FileUploads { + uploads: File[]; + parts: UploadPart[]; +} + +export interface UploadPart { + uploadId: string; + key: string; +} + +export interface FileLinkQueries { + download: boolean; +} + +export interface NewFile { + name: string; + size: number; + mimeType?: string; + bucket: string; + key: string; + kind: Kind; + uploaderId?: string; + upload: Upload | null; + sourceId?: string; + transcoding: Transcoding | null; +} + +export interface BroadcastableApi { + file: File; + container: FileContainer | null; + streams: FileStream[]; + proxies: Proxies; + reportXml: File | null; + reportPdf: File | null; + deliveries: Delivery[]; + segments: FileSegment[]; + subtitleWarnings: { name: string; timecode: string }[]; +} + +export interface FileSegment { + id: string; + label: SegmentLabel; + creator: User; + createdAt: string; + file: string; + frameIn: number; + frameOut: number; +} + +export enum SegmentLabel { + OpeningCredits = "OpeningCredits", + EndingCredits = "EndingCredits", + Introduction = "Introduction", + Program = "Program", + Trailer = "Trailer", + Advertising = "Advertising", + TestPattern = "TestPattern", + Black = "Black", + Slate = "Slate", + NeutralBases = "NeutralBases", + CustomDelivery = "CustomDelivery", +} + +export interface FileSegmentPayload { + label: SegmentLabel; + frameIn: number; + frameOut: number; +} + +export interface CreateFile { + name: string; + size: number; + mimeType?: string; + source?: string; + kind: BroadcastableFileKind; +} + +export interface ResumeFile { + name: string; + kind: BroadcastableFileKind; +} + +export enum FileTypeVideo { + Mxf = "Mxf", + Qtff = "Qtff", + Mp4 = "Mp4", +} + +export enum FileTypeAudio { + Mp3 = "Mp3", +} + +export type FileType = + | ["FileTypeVideo", FileTypeVideo] + | ["FileTypeAudio", FileTypeAudio] + | ["FileTypeSubtitle"]; + +export enum Phase { + Waiting = "Waiting", + Downloading = "Downloading", + Encoding = "Encoding", + Packaging = "Packaging", + Uploading = "Uploading", + Finished = "Finished", +} + +export interface TranscodeWarning { + name: string; + count: number; + firstFrameApprox: number; +} + +export interface Transcoding { + file?: string; + phase: Phase; + progress?: number; + startedAt: string; + progressedAt: string; + log?: string; + warning: TranscodeWarning[]; +} + +export interface Progress { + phase: Phase; + progress?: number; +} + +export interface Upload { + file: string; + user: string; + progress: number; + progressedAt: string; + pausedAt?: string; + completedAt?: string; + error?: string; + s3Id?: string; + speed: number; + secondsLeft?: number; + source?: string; +} + +export interface User { + id: string; + name: string; + email: string; + avatar: string; + organization?: string; + organizations: OrganizationUserWithLabel[]; + admin: boolean; + disableOrganizationEmails: boolean; +} + +export interface OrganizationUserWithLabel { + organizationId: string; + organizationName: string; + logo?: string; +} + +export interface OrganizationUser { + userId: string; + organizationId: string; +} + +export type NodeType = + | ["AudioProgram", AudioProgram] + | ["VideoProgram", VideoProgram] + | ["Container", ContainerProgram] + | ["UnknownNodeType"]; + +export interface AudioProgram { + programLoudness?: string; + loudnessRange?: string; + channels: AudioChannel[]; + isMute: boolean; +} + +export interface AudioChannel { + label: string; + truePeakLevel: string; +} + +export interface VideoProgram { + displayAspectRatio?: string; + frameRate?: string; + activePixelsArea?: string; + cadencePattern?: string; + chromaFormat?: string; + scanningType?: string; +} + +export type ContainerProgram = + | ["Mxf", MxfContainerProperties] + | ["Mov", MovContainerProperties] + | ["UnsupportedContainer"]; + +export interface MovContainerProperties { + startTimecode?: string; + duration?: string; +} + +export interface MxfContainerProperties { + operationalPattern?: string; + timeCodes: TimeCodes; + product: Product; +} + +export interface TimeCodes { + duration?: string; + systemItemStart?: string; + sourcePackageStart?: string; + materialPackageStart?: string; + hasVitc?: string; +} + +export interface Product { + name?: string; + version?: string; + issuer?: string; +} + +export enum State { + Active = "Active", + Archived = "Archived", + Restoring = "Restoring", +} From dde9630a29b1b6fcc273f0a18f3f7812eff101af Mon Sep 17 00:00:00 2001 From: Maxime Dantec Date: Mon, 13 Mar 2023 15:20:59 +0100 Subject: [PATCH 13/24] tmp --- src/nomalab.ts | 21 +++++++-------------- src/types.ts | 51 ++++++++++++++++---------------------------------- 2 files changed, 23 insertions(+), 49 deletions(-) diff --git a/src/nomalab.ts b/src/nomalab.ts index a4d2a50..bbdb771 100644 --- a/src/nomalab.ts +++ b/src/nomalab.ts @@ -442,25 +442,18 @@ export class Nomalab { }, ): Promise { const myHeaders = new Headers(); - myHeaders.append( - "Content-Type", - optionalArg.contentType ?? "application/json", - ); - myHeaders.append("Authorization", `Bearer ${this.#apiToken}`); - if (optionalArg.cookieHeader) { - myHeaders.append( - "Cookie", - `sessionJwt=${optionalArg.cookieHeader["sessionJwt"]}`, - ); - } + myHeaders.append("Content-Type", optionalArg.contentType ?? "application/json"); + myHeaders.append("Cookie", `sessionJwt=${this.#apiToken}`); + // myHeaders.append("Authorization", `Bearer ${this.#apiToken} `); + // if (optionalArg.cookieHeader) { + // myHeaders.append("Cookie", `sessionJwt=${optionalArg.cookieHeader["sessionJwt"]}`) + // } const request = new Request( `https://${this.#contextSubDomain()}.nomalab.com/v3/${partialUrl}`, { method: optionalArg.method ?? "GET", headers: myHeaders, - body: (optionalArg.bodyJsonObject === undefined) - ? null - : JSON.stringify(optionalArg.bodyJsonObject), + body: (optionalArg.bodyJsonObject === undefined) ? null : JSON.stringify(optionalArg.bodyJsonObject), credentials: "include", }, ); diff --git a/src/types.ts b/src/types.ts index 4d41f96..86ccc51 100644 --- a/src/types.ts +++ b/src/types.ts @@ -172,26 +172,6 @@ export interface Delivery { transcoding: Transcoding; } -export interface FileClass { - state: string; - stateExpireAt: null; - id: string; - createdAt: string; - name: string; - size: number; - mimeType: null | string; - bucket: string; - key: string; - kind: string; - uploaderId: null | string; - upload: Upload | null; - uploadedAt: null | string; - verification: Verification | null; - sourceId: null | string; - transcoding: Transcoding | null; - format: null; -} - export interface Verification { progress: number; error: null; @@ -761,36 +741,37 @@ export enum BroadcastableFileKind { Extra = "Extra", } -export interface File { + +export interface FileClass { + state: string; + stateExpireAt: string | null; id: string; createdAt: string; name: string; size: number; - mimeType?: string; + mimeType: null | string; bucket: string; key: string; - kind: Kind; - uploaderId?: string; + kind: string; + uploaderId: null | string; upload: Upload | null; - uploadedAt?: string; + uploadedAt: null | string; verification: Verification | null; - sourceId?: string; + sourceId: null | string; transcoding: Transcoding | null; - state: State; - stateExpireAt?: string; - format: Formats.Format | null; + format: Formats.Format; } export interface ExtraApi { - file: File; - proxy: File | null; + file: FileClass; + proxy: FileClass | null; segments: FileSegment[]; container: FileContainer | null; streams: FileStream[]; } export interface FileUploads { - uploads: File[]; + uploads: FileClass[]; parts: UploadPart[]; } @@ -817,12 +798,12 @@ export interface NewFile { } export interface BroadcastableApi { - file: File; + file: FileClass; container: FileContainer | null; streams: FileStream[]; proxies: Proxies; - reportXml: File | null; - reportPdf: File | null; + reportXml: FileClass | null; + reportPdf: FileClass | null; deliveries: Delivery[]; segments: FileSegment[]; subtitleWarnings: { name: string; timecode: string }[]; From 8e5a7b1aba9aec24cf982f866b8886dd19cd590b Mon Sep 17 00:00:00 2001 From: Maxime Dantec Date: Thu, 15 Jun 2023 10:13:11 +0200 Subject: [PATCH 14/24] fmt --- src/nomalab.ts | 4 +++- src/types.ts | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/nomalab.ts b/src/nomalab.ts index 2c7135e..9cad06f 100644 --- a/src/nomalab.ts +++ b/src/nomalab.ts @@ -464,7 +464,9 @@ export class Nomalab { { method: optionalArg.method ?? "GET", headers: myHeaders, - body: (optionalArg.bodyJsonObject === undefined) ? null : JSON.stringify(optionalArg.bodyJsonObject), + body: (optionalArg.bodyJsonObject === undefined) + ? null + : JSON.stringify(optionalArg.bodyJsonObject), credentials: "include", }, ); diff --git a/src/types.ts b/src/types.ts index 046309b..42e093d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -821,7 +821,6 @@ export enum BroadcastableFileKind { Extra = "Extra", } - export interface FileClass { state: string; stateExpireAt: string | null; From a5d17af8341f97cbb29d0238a16c5c5ce5ea73ec Mon Sep 17 00:00:00 2001 From: Maxime Dantec Date: Thu, 15 Jun 2023 10:23:54 +0200 Subject: [PATCH 15/24] fix login --- src/nomalab.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/nomalab.ts b/src/nomalab.ts index 9cad06f..c314d83 100644 --- a/src/nomalab.ts +++ b/src/nomalab.ts @@ -133,15 +133,9 @@ export class Nomalab { // To avoid leak since we don't use the body of the response await response.body?.cancel(); - const cookie = mod.getCookies(headers); - const bodyJsonObject = method == "POST" ? (body ?? {}) : undefined; return this.#fetch( partialUrl, - { - bodyJsonObject, - method, - cookieHeader: cookie, - }, + optionalArg, ); } @@ -458,6 +452,12 @@ export class Nomalab { "Cookie", `sessionJwt=${optionalArg.cookieHeader["sessionJwt"]}`, ); + } else { + myHeaders.append( + "Cookie", + `sessionJwt=${this.#apiToken}`, + ); + } const request = new Request( `https://${this.#contextSubDomain()}.nomalab.com/v3/${partialUrl}`, From 66e48e9bfc497c0be0117e8f41eeebffd74e33ba Mon Sep 17 00:00:00 2001 From: Maxime Dantec Date: Mon, 19 Jun 2023 09:20:04 +0200 Subject: [PATCH 16/24] fix --- src/nomalab.ts | 11 +++++++++++ src/types.ts | 26 ++++++++++---------------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/nomalab.ts b/src/nomalab.ts index c314d83..f4f067c 100644 --- a/src/nomalab.ts +++ b/src/nomalab.ts @@ -4,6 +4,7 @@ import { Deliveries, DeliverPayload, DeliveryApi, + AudioMappingPayload, Job, MeUser, Node, @@ -416,6 +417,16 @@ export class Nomalab { return response.json() as Promise; } + setAudioMapping(fileId : string, mappingPayload : AudioMappingPayload): Promise { + return this.#fetch( + `files/${fileId}/audioMapping`, + { + bodyJsonObject: mappingPayload, + method: "POST", + }, + ); + } + #throwError(message: string, response: Response): void { console.error(message); console.error(response); diff --git a/src/types.ts b/src/types.ts index 42e093d..b3e39e9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -109,6 +109,13 @@ export interface Container { timecode: null; } +export interface AudioMappingPayload { + index: number; + channelLayout?: string; + version?: string; + typeVersion?: string; +} + export interface Deliveries { shows: Show[]; nodes: DeliveryNode[]; @@ -491,6 +498,7 @@ export interface FileVideoStream { rFrameRateDenominator: number; level?: number; profile?: string; + startTime?: number; chromaSubsampling?: string; scanningType?: string; timecode?: string; @@ -890,7 +898,7 @@ export interface BroadcastableApi { export interface FileSegment { id: string; - label: SegmentLabel; + label: Formats.SegmentLabel; creator: User; createdAt: string; file: string; @@ -898,22 +906,8 @@ export interface FileSegment { frameOut: number; } -export enum SegmentLabel { - OpeningCredits = "OpeningCredits", - EndingCredits = "EndingCredits", - Introduction = "Introduction", - Program = "Program", - Trailer = "Trailer", - Advertising = "Advertising", - TestPattern = "TestPattern", - Black = "Black", - Slate = "Slate", - NeutralBases = "NeutralBases", - CustomDelivery = "CustomDelivery", -} - export interface FileSegmentPayload { - label: SegmentLabel; + label: Formats.SegmentLabel; frameIn: number; frameOut: number; } From 442dcaf54770b53dbc2768d10c7785244b2bd1ff Mon Sep 17 00:00:00 2001 From: Maxime Dantec Date: Thu, 22 Jun 2023 09:16:37 +0200 Subject: [PATCH 17/24] segments & fix cookie error --- src/nomalab.ts | 38 +++++++++----------------------------- src/types.ts | 10 +++++----- 2 files changed, 14 insertions(+), 34 deletions(-) diff --git a/src/nomalab.ts b/src/nomalab.ts index f4f067c..d5df9bb 100644 --- a/src/nomalab.ts +++ b/src/nomalab.ts @@ -130,6 +130,7 @@ export class Nomalab { const setCookie = getSetCookies(response.headers)[0]; assert(setCookie, "No cookie"); this.#apiToken = setCookie.value; + getSetCookies(response.headers)[0]; // To avoid leak since we don't use the body of the response await response.body?.cancel(); @@ -274,27 +275,14 @@ export class Nomalab { } // Deliver with starting a transcode - async deliver(showId: string, deliverPayload: DeliverPayload): Promise { - const response = await this.#fetch( - `broadcastables/${showId}/deliver`, + deliver(broadcastableId: string, deliverPayload: DeliverPayload): Promise { + return this.#fetch( + `broadcastables/${broadcastableId}/deliver`, { bodyJsonObject: deliverPayload, method: "POST", }, - ); - if (!response.ok) { - if (response.status == 409) { - throw new AlreadyPresentDeliverable( - "Can't deliver because of an already present deliverable.", - ); - } else { - this.#throwError( - `ERROR - Can't deliver with payload.${deliverPayload}`, - response, - ); - } - } - return Promise.resolve() as Promise; + ) as Promise; } async triggerUpload(broadcastableId: string) { @@ -457,19 +445,11 @@ export class Nomalab { "Content-Type", optionalArg.contentType ?? "application/json", ); - myHeaders.append("Authorization", `Bearer ${this.#apiToken} `); - if (optionalArg.cookieHeader) { - myHeaders.append( - "Cookie", - `sessionJwt=${optionalArg.cookieHeader["sessionJwt"]}`, - ); - } else { - myHeaders.append( - "Cookie", - `sessionJwt=${this.#apiToken}`, - ); + myHeaders.append( + "Cookie", + `sessionJwt=${this.#apiToken}`, + ); - } const request = new Request( `https://${this.#contextSubDomain()}.nomalab.com/v3/${partialUrl}`, { diff --git a/src/types.ts b/src/types.ts index b3e39e9..a6b2468 100644 --- a/src/types.ts +++ b/src/types.ts @@ -152,15 +152,15 @@ export interface SubtitleWarning { } export interface SubtitleFormat { - organizationId: string; - organizationName: string; - subtitleFormats: FormatElement[]; + id: string; + name: string; + formats: string; } export interface DeliverPayload { format: string; versionMapping: Formats.Mapping; - timecodeOut: string | null; - timecodeIn: string | null; + timecodeOut?: string; + timecodeIn?: string; segments?: string[]; subtitles: DeliverSubtitle | null; targetOrg: string; From 493762fcb77245a044745c6ee899591761829cb2 Mon Sep 17 00:00:00 2001 From: Maxime Dantec Date: Thu, 24 Aug 2023 11:30:18 +0200 Subject: [PATCH 18/24] make it browser compatible --- deno.jsonc | 11 ++- package.json | 15 ++++ src/deps.ts | 2 - src/nomalab.ts | 237 ++++++++++++++++++------------------------------- src/types.ts | 52 ++--------- tsconfig.json | 12 +++ 6 files changed, 125 insertions(+), 204 deletions(-) create mode 100644 package.json delete mode 100644 src/deps.ts create mode 100644 tsconfig.json diff --git a/deno.jsonc b/deno.jsonc index 8c07a93..8febc66 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -1,12 +1,11 @@ { + "imports": { + "std/": "https://deno.land/std@0.199.0/" + }, "lint": { - "files": { - "include": ["src/"] - } + "include": ["src/"] }, "fmt": { - "files": { - "include": ["src/"] - } + "include": ["src/"] } } diff --git a/package.json b/package.json new file mode 100644 index 0000000..2861c0b --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "name": "deno_lib", + "version": "1.0.0", + "description": "Deno lib to handle Nomalab API with deno", + "main": "mod.js", + "types": "mod.ts", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "typescript": "^5.1.6" + } +} diff --git a/src/deps.ts b/src/deps.ts deleted file mode 100644 index 57bd117..0000000 --- a/src/deps.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "https://deno.land/std@0.170.0/http/cookie.ts"; -export * from "https://deno.land/std@0.170.0/testing/asserts.ts"; diff --git a/src/nomalab.ts b/src/nomalab.ts index d5df9bb..defebe4 100644 --- a/src/nomalab.ts +++ b/src/nomalab.ts @@ -1,10 +1,10 @@ import { + AudioMappingPayload, CopyToBroadcastable, DeliverableOrganization, Deliveries, DeliverPayload, DeliveryApi, - AudioMappingPayload, Job, MeUser, Node, @@ -16,10 +16,9 @@ import { Show, ShowClass, ShowKind, - SubtitleFormat, + SubtitleFormatApi, } from "./types.ts"; import { Format } from "./formats.ts"; -import { assert, getSetCookies } from "./deps.ts"; export class AlreadyPresentDeliverable extends Error { constructor(msg: string) { @@ -28,30 +27,22 @@ export class AlreadyPresentDeliverable extends Error { } } export class Nomalab { - #context: string; - #apiToken: string; + // both unset in the front-end + #context?: string; + #apiToken?: string; - constructor(context: string, apiToken: string) { + constructor(context?: string, apiToken?: string) { this.#context = context; this.#apiToken = apiToken; } async me(): Promise { const response = await this.#fetch(`users/me`, {}); - if (!response.ok) { - this.#throwError(`ERROR - Can't load user infos.`, response); - } return response.json() as Promise; } async getShow(showUuid: string): Promise { const response = await this.#fetch(`shows/${showUuid}`, {}); - if (!response.ok) { - this.#throwError( - `ERROR - Can't find show with id ${showUuid}.`, - response, - ); - } return response.json() as Promise; } @@ -59,9 +50,7 @@ export class Nomalab { const response = await this.#requestWithSwitch( organizationId, "hierarchy", - {}, ); - if (!response.ok) this.#throwError(`ERROR - Can't find root.`, response); return response.json() as Promise; } @@ -75,14 +64,14 @@ export class Nomalab { organizationId, "hierarchy", { - name, - parent, - kind, + method: "POST", + bodyJsonObject: { + name, + parent, + kind, + }, }, ); - if (!response.ok) { - this.#throwError(`ERROR - Can't create ${kind} ${name}.`, response); - } return response.json() as Promise; } @@ -98,10 +87,6 @@ export class Nomalab { kind, }, }); - - if (!response.ok) { - this.#throwError(`ERROR - Can't create show ${kind} ${name}.`, response); - } const { id } = await response.json() as ShowClass; return id; } @@ -114,7 +99,7 @@ export class Nomalab { bodyJsonObject?: unknown; contentType?: string; cookieHeader?: Record; - }, + } = {}, ): Promise { const response = await this.#fetch( `users/switch`, @@ -123,14 +108,15 @@ export class Nomalab { method: "POST", }, ); - if (!response.ok) { - this.#throwError(`Can't switch to org ${organizationId}`, response); - } - const setCookie = getSetCookies(response.headers)[0]; - assert(setCookie, "No cookie"); - this.#apiToken = setCookie.value; - getSetCookies(response.headers)[0]; + if (this.#apiToken && response.headers.has("set-cookie")) { + response.headers.getSetCookie().forEach((sc) => { + const [name, value] = sc.split("="); + if (name == "sessionJwt") { + this.#apiToken = value; + } + }); + } // To avoid leak since we don't use the body of the response await response.body?.cancel(); @@ -143,48 +129,21 @@ export class Nomalab { async getChildren(nodeUuid: string): Promise { const response = await this.#fetch(`hierarchy/${nodeUuid}/children`, {}); - if (!response.ok) { - this.#throwError( - `ERROR - Can't find children with id ${nodeUuid}.`, - response, - ); - } return response.json() as Promise; } async getDeliveries(): Promise { const response = await this.#fetch(`shows/deliveries`, {}); - if (!response.ok) { - if (response.status == 409) { - throw new AlreadyPresentDeliverable( - "Can't deliver because of an already present deliverable.", - ); - } else { - this.#throwError(`ERROR - Can't get deliveries.`, response); - } - } return response.json() as Promise; } async getNode(nodeUuid: string): Promise { const response = await this.#fetch(`hierarchy/${nodeUuid}`, {}); - if (!response.ok) { - this.#throwError( - `ERROR - Can't find node with id ${nodeUuid}.`, - response, - ); - } return response.json() as Promise; } async getShowsForNode(nodeUuid: string): Promise { const response = await this.#fetch(`hierarchy/${nodeUuid}/shows`, {}); - if (!response.ok) { - this.#throwError( - `ERROR - error when retrieving shows for node ${nodeUuid}.`, - response, - ); - } return response.json() as Promise; } @@ -196,20 +155,11 @@ export class Nomalab { method: "POST", }, ); - if (!response.ok) { - this.#throwError( - `ERROR - Can't find show with id ${showUuid}.`, - response, - ); - } return response.json() as Promise; } async getOrganizations(): Promise { const response = await this.#fetch(`organizations`, {}); - if (!response.ok) { - this.#throwError(`ERROR - Can't get organizations.`, response); - } return response.json() as Promise; } @@ -235,15 +185,17 @@ export class Nomalab { async getJob(jobUuid: string): Promise { const response = await this.#fetch(`jobs/${jobUuid}`, {}); - if (!response.ok) { - this.#throwError(`ERROR - Can't find job with id ${jobUuid}.`, response); - } return response.json() as Promise; } + async getSegments(fileId: string): Promise { + const response = await this.#fetch(`files/${fileId}/segments`, {}); + return response.json() as Promise; + } + async s3Upload(payload: CopyToBroadcastable): Promise { const url = payload.destRole ? "aws/copyFromExt" : "aws/copy"; - const response = await this.#fetch( + await this.#fetch( url, { bodyJsonObject: payload, @@ -251,15 +203,6 @@ export class Nomalab { }, ); - if (!response.ok) { - this.#throwError( - `ERROR - Can't make a s3 copy with payload.${ - JSON.stringify(payload) - } on url ${url}.`, - response, - ); - } - return Promise.resolve(); } @@ -268,14 +211,14 @@ export class Nomalab { `shows/${showId}/accept`, { method: "POST" }, ); - if (!response.ok) { - this.#throwError(`ERROR - Can't accept show. ${showId}`, response); - } return response.json() as Promise; } // Deliver with starting a transcode - deliver(broadcastableId: string, deliverPayload: DeliverPayload): Promise { + deliver( + broadcastableId: string, + deliverPayload: DeliverPayload, + ): Promise { return this.#fetch( `broadcastables/${broadcastableId}/deliver`, { @@ -286,23 +229,10 @@ export class Nomalab { } async triggerUpload(broadcastableId: string) { - const response = await this.#fetch( + await this.#fetch( `broadcastables/${broadcastableId}/delivery`, { method: "POST", bodyJsonObject: {} }, ); - console.log(response.headers); - if (!response.ok) { - if (response.status == 409) { - throw new AlreadyPresentDeliverable( - "Can't deliver because of an already present deliverable.", - ); - } else { - this.#throwError( - `ERROR - Can't accept and deliver show. [${broadcastableId}]`, - response, - ); - } - } return Promise.resolve() as Promise; } @@ -312,22 +242,10 @@ export class Nomalab { showId: string, ): Promise { await this.accept(showId).then(async () => { - const response = await this.#fetch( + await this.#fetch( `broadcastables/${broadcastableId}/delivery`, { method: "POST", bodyJsonObject: {} }, ); - if (!response.ok) { - if (response.status == 409) { - throw new AlreadyPresentDeliverable( - "Can't deliver because of an already present deliverable.", - ); - } else { - this.#throwError( - `ERROR - Can't accept and deliver show. [${showId}]`, - response, - ); - } - } return Promise.resolve(); }); } @@ -343,18 +261,21 @@ export class Nomalab { method: "POST", }, ); - if (!response.ok) { - if (response.status == 409) { - throw new AlreadyPresentDeliverable( - "Can't deliver because of an already present deliverable.", - ); - } else { - this.#throwError( - `ERROR - Can't deliver without transcoding to org id <${targetOrgId}>`, - response, - ); - } - } + return response.json() as Promise; + } + + async copyToShow( + broadcastableId: string, + target: string, + subtitles?: SubtitleFormatApi, + ): Promise { + const response = await this.#fetch( + `broadcastables/${broadcastableId}/copyToShow`, + { + bodyJsonObject: { target, subtitles }, + method: "POST", + }, + ); return response.json() as Promise; } @@ -365,12 +286,6 @@ export class Nomalab { contentType: "application/xml", }, ); - if (!response.ok) { - this.#throwError( - `ERROR - Can't find manifest with proxyId ${proxyId}.`, - response, - ); - } return response.blob() as Promise; } @@ -387,7 +302,6 @@ export class Nomalab { async getOrganizationDeliveries(orgId: string) { const response = await this.#fetch( `organizations/${orgId}/shows/deliveries`, - {}, ); return response.json() as Promise; } @@ -400,12 +314,14 @@ export class Nomalab { async getSubtitleFormats(orgId: string) { const response = await this.#fetch( `organizations/${orgId}/subtitleFormats`, - {}, ); - return response.json() as Promise; + return response.json() as Promise; } - setAudioMapping(fileId : string, mappingPayload : AudioMappingPayload): Promise { + setAudioMapping( + fileId: string, + mappingPayload: AudioMappingPayload, + ): Promise { return this.#fetch( `files/${fileId}/audioMapping`, { @@ -415,11 +331,6 @@ export class Nomalab { ); } - #throwError(message: string, response: Response): void { - console.error(message); - console.error(response); - throw new Error(message); - } proxy( partialUrl: string, optionalArg?: { @@ -431,6 +342,7 @@ export class Nomalab { ): Promise { return this.#fetch(partialUrl, optionalArg || {}); } + #fetch( partialUrl: string, optionalArg: { @@ -438,35 +350,54 @@ export class Nomalab { bodyJsonObject?: unknown; contentType?: string; cookieHeader?: Record; - }, + } = {}, ): Promise { const myHeaders = new Headers(); myHeaders.append( "Content-Type", optionalArg.contentType ?? "application/json", ); - myHeaders.append( - "Cookie", - `sessionJwt=${this.#apiToken}`, - ); + + if (this.#apiToken) { + myHeaders.append( + "Cookie", + `sessionJwt=${this.#apiToken}`, + ); + } const request = new Request( - `https://${this.#contextSubDomain()}.nomalab.com/v3/${partialUrl}`, + `${this.#contextSubDomain()}/v3/${partialUrl}`, { method: optionalArg.method ?? "GET", headers: myHeaders, body: (optionalArg.bodyJsonObject === undefined) ? null : JSON.stringify(optionalArg.bodyJsonObject), - credentials: "include", + credentials: this.#context ? "include" : undefined, }, ); - return fetch(request); + return fetch(request).then((response) => { + if (response.ok) { + return response; + } else { + throw response; + } + }); } + #contextSubDomain(): string { - if (this.#context == "www") return "app"; - else { - return `app-${this.#context}`; + if (this.#context) { + const ctx = this.#context == "www" ? "app" : `app-${this.#context}`; + return `https://${ctx}.nomalab.com`; + } else { + // front-end just wants to keep its own context + return ""; } } } + +declare global { + interface Headers { + getSetCookie(): string[]; + } +} \ No newline at end of file diff --git a/src/types.ts b/src/types.ts index a6b2468..2aba602 100644 --- a/src/types.ts +++ b/src/types.ts @@ -152,10 +152,17 @@ export interface SubtitleWarning { } export interface SubtitleFormat { + organizationId: string; + organizationName: string; + subtitleFormats: FormatElement[]; +} + +export interface SubtitleFormatApi { id: string; name: string; - formats: string; + format: string; } + export interface DeliverPayload { format: string; versionMapping: Formats.Mapping; @@ -189,33 +196,6 @@ export interface DeliveryTranscoding { warning: TranscodeWarning[]; } -export enum Phase { - Encoding = "Encoding", - Finished = "Finished", - Packaging = "Packaging", - Waiting = "Waiting", -} - -export interface FileClass { - state: string; - stateExpireAt: null; - id: string; - createdAt: string; - name: string; - size: number; - mimeType: null | string; - bucket: string; - key: string; - kind: string; - uploaderId: null | string; - upload: Upload | null; - uploadedAt: null | string; - verification: Verification | null; - sourceId: null | string; - transcoding: FileTranscoding | null; - format: null; -} - export interface FileTranscoding { file: string; phase: Phase; @@ -226,20 +206,6 @@ export interface FileTranscoding { warning: unknown[]; } -export interface Upload { - file: string; - user: string; - progress: number; - progressedAt: string; - pausedAt: null; - completedAt: string; - error: null; - s3Id: string; - speed: number; - secondsLeft: number; - source: string; -} - export interface Verification { progress: number; error: null; @@ -450,7 +416,7 @@ export interface Proxies { export interface Segment { id: string; - label: Formats.SegmentLabel; + label: string | Formats.SegmentLabel; creator: Creator; createdAt: string; file: string; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..a37f7d9 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "sourceMap": true, + "noImplicitAny": true, + "module": "es6", + "moduleResolution": "nodenext", + "target": "es2021", + "noEmit": true, + "allowImportingTsExtensions": true + }, + "include": ["mod.ts", "src/**/*"] + } \ No newline at end of file From 59484bc4253e1779990626c0ea8a6c69a6f562a3 Mon Sep 17 00:00:00 2001 From: Maxime Dantec Date: Thu, 24 Aug 2023 11:37:09 +0200 Subject: [PATCH 19/24] fmt --- src/nomalab.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/nomalab.ts b/src/nomalab.ts index defebe4..a3392e4 100644 --- a/src/nomalab.ts +++ b/src/nomalab.ts @@ -397,7 +397,7 @@ export class Nomalab { } declare global { - interface Headers { - getSetCookie(): string[]; - } -} \ No newline at end of file + interface Headers { + getSetCookie(): string[]; + } +} From fb960b34759bfdf9800a2a616995d7c3d0994666 Mon Sep 17 00:00:00 2001 From: Maxime Dantec Date: Thu, 24 Aug 2023 11:40:19 +0200 Subject: [PATCH 20/24] bundle --- mod.js | 486 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 486 insertions(+) create mode 100644 mod.js diff --git a/mod.js b/mod.js new file mode 100644 index 0000000..0c0798b --- /dev/null +++ b/mod.js @@ -0,0 +1,486 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file +// This code was bundled using `deno bundle` and it's not recommended to edit it manually + +class AlreadyPresentDeliverable extends Error { + constructor(msg){ + super(msg); + this.name = "Inaccessible"; + } +} +class Nomalab { + #context; + #apiToken; + constructor(context, apiToken){ + this.#context = context; + this.#apiToken = apiToken; + } + async me() { + const response = await this.#fetch(`users/me`, {}); + return response.json(); + } + async getShow(showUuid) { + const response = await this.#fetch(`shows/${showUuid}`, {}); + return response.json(); + } + async getRoots(organizationId) { + const response = await this.#requestWithSwitch(organizationId, "hierarchy"); + return response.json(); + } + async createHierarchy(organizationId, name, kind, parent) { + const response = await this.#requestWithSwitch(organizationId, "hierarchy", { + method: "POST", + bodyJsonObject: { + name, + parent, + kind + } + }); + return response.json(); + } + async createShow(nodeId, name, kind) { + const response = await this.#fetch(`hierarchy/${nodeId}/shows`, { + method: "POST", + bodyJsonObject: { + name, + kind + } + }); + const { id } = await response.json(); + return id; + } + async #requestWithSwitch(organizationId, partialUrl, optionalArg = {}) { + const response = await this.#fetch(`users/switch`, { + bodyJsonObject: { + organization: organizationId + }, + method: "POST" + }); + if (this.#apiToken && response.headers.has("set-cookie")) { + response.headers.getSetCookie().forEach((sc)=>{ + const [name, value] = sc.split("="); + if (name == "sessionJwt") { + this.#apiToken = value; + } + }); + } + await response.body?.cancel(); + return this.#fetch(partialUrl, optionalArg); + } + async getChildren(nodeUuid) { + const response = await this.#fetch(`hierarchy/${nodeUuid}/children`, {}); + return response.json(); + } + async getDeliveries() { + const response = await this.#fetch(`shows/deliveries`, {}); + return response.json(); + } + async getNode(nodeUuid) { + const response = await this.#fetch(`hierarchy/${nodeUuid}`, {}); + return response.json(); + } + async getShowsForNode(nodeUuid) { + const response = await this.#fetch(`hierarchy/${nodeUuid}/shows`, {}); + return response.json(); + } + async getPath(showUuid) { + const response = await this.#fetch(`admin/shows/path`, { + bodyJsonObject: { + showIds: [ + showUuid + ] + }, + method: "POST" + }); + return response.json(); + } + async getOrganizations() { + const response = await this.#fetch(`organizations`, {}); + return response.json(); + } + async getOrganization(organizationId) { + const organisation = (await this.getOrganizations()).filter((org)=>{ + return org.id == organizationId; + }); + if (organisation.length == 0) { + return Promise.reject(`Org ${organizationId} not found`); + } + return Promise.resolve(organisation[0]); + } + async getOrganizationByName(organizationName) { + const organisation = (await this.getOrganizations()).filter((org)=>{ + return org.name == organizationName; + }); + if (organisation.length == 0) { + return Promise.reject(`Org ${organizationName} not found`); + } + return Promise.resolve(organisation[0]); + } + async getJob(jobUuid) { + const response = await this.#fetch(`jobs/${jobUuid}`, {}); + return response.json(); + } + async getSegments(fileId) { + const response = await this.#fetch(`files/${fileId}/segments`, {}); + return response.json(); + } + async s3Upload(payload) { + const url = payload.destRole ? "aws/copyFromExt" : "aws/copy"; + await this.#fetch(url, { + bodyJsonObject: payload, + method: "POST" + }); + return Promise.resolve(); + } + async accept(showId) { + const response = await this.#fetch(`shows/${showId}/accept`, { + method: "POST" + }); + return response.json(); + } + deliver(broadcastableId, deliverPayload) { + return this.#fetch(`broadcastables/${broadcastableId}/deliver`, { + bodyJsonObject: deliverPayload, + method: "POST" + }); + } + async triggerUpload(broadcastableId) { + await this.#fetch(`broadcastables/${broadcastableId}/delivery`, { + method: "POST", + bodyJsonObject: {} + }); + return Promise.resolve(); + } + async acceptAndDeliver(broadcastableId, showId) { + await this.accept(showId).then(async ()=>{ + await this.#fetch(`broadcastables/${broadcastableId}/delivery`, { + method: "POST", + bodyJsonObject: {} + }); + return Promise.resolve(); + }); + } + async deliverWithoutTranscoding(broadcastableId, targetOrgId) { + const response = await this.#fetch(`broadcastables/${broadcastableId}/copyToOrganization`, { + bodyJsonObject: { + targetOrg: targetOrgId + }, + method: "POST" + }); + return response.json(); + } + async copyToShow(broadcastableId, target, subtitles) { + const response = await this.#fetch(`broadcastables/${broadcastableId}/copyToShow`, { + bodyJsonObject: { + target, + subtitles + }, + method: "POST" + }); + return response.json(); + } + async getManifest(proxyId) { + const response = await this.#fetch(`files/${proxyId}/manifest`, { + contentType: "application/xml" + }); + return response.blob(); + } + async getDeliverableOrgs() { + const response = await this.#fetch(`organizations/deliverables`, {}); + return response.json(); + } + async getFileSegments(materialId) { + const response = await this.#fetch(`files/${materialId}/segments`, {}); + return response.json(); + } + async getOrganizationDeliveries(orgId) { + const response = await this.#fetch(`organizations/${orgId}/shows/deliveries`); + return response.json(); + } + async getFormats(orgId) { + const response = await this.#fetch(`organizations/${orgId}/formats`, {}); + return response.json(); + } + async getSubtitleFormats(orgId) { + const response = await this.#fetch(`organizations/${orgId}/subtitleFormats`); + return response.json(); + } + setAudioMapping(fileId, mappingPayload) { + return this.#fetch(`files/${fileId}/audioMapping`, { + bodyJsonObject: mappingPayload, + method: "POST" + }); + } + proxy(partialUrl, optionalArg) { + return this.#fetch(partialUrl, optionalArg || {}); + } + #fetch(partialUrl, optionalArg = {}) { + const myHeaders = new Headers(); + myHeaders.append("Content-Type", optionalArg.contentType ?? "application/json"); + if (this.#apiToken) { + myHeaders.append("Cookie", `sessionJwt=${this.#apiToken}`); + } + const request = new Request(`${this.#contextSubDomain()}/v3/${partialUrl}`, { + method: optionalArg.method ?? "GET", + headers: myHeaders, + body: optionalArg.bodyJsonObject === undefined ? null : JSON.stringify(optionalArg.bodyJsonObject), + credentials: this.#context ? "include" : undefined + }); + return fetch(request).then((response)=>{ + if (response.ok) { + return response; + } else { + throw response; + } + }); + } + #contextSubDomain() { + if (this.#context) { + const ctx = this.#context == "www" ? "app" : `app-${this.#context}`; + return `https://${ctx}.nomalab.com`; + } else { + return ""; + } + } +} +export { AlreadyPresentDeliverable as AlreadyPresentDeliverable }; +export { Nomalab as Nomalab }; +var BroadcastableKind; +(function(BroadcastableKind) { + BroadcastableKind["Subtitle"] = "Subtitle"; + BroadcastableKind["Material"] = "Material"; + BroadcastableKind["Audio"] = "Audio"; + BroadcastableKind["Extra"] = "Extra"; +})(BroadcastableKind || (BroadcastableKind = {})); +var Kind; +(function(Kind) { + Kind["Create"] = "Create"; + Kind["Finish"] = "Finish"; + Kind["Request"] = "Request"; +})(Kind || (Kind = {})); +var MXFVersion; +(function(MXFVersion) { + MXFVersion["AsMaster"] = "AsMaster"; + MXFVersion["Vd"] = "VD"; + MXFVersion["Vo"] = "VO"; +})(MXFVersion || (MXFVersion = {})); +var TranscodeKind; +(function(TranscodeKind) { + TranscodeKind["Copy"] = "copy"; + TranscodeKind["Deliverable"] = "deliverable"; + TranscodeKind["ProxyLowRes"] = "proxy_low_res"; + TranscodeKind["ProxySubtitle"] = "proxy_subtitle"; +})(TranscodeKind || (TranscodeKind = {})); +var NodeKind; +(function(NodeKind) { + NodeKind["Season"] = "Season"; + NodeKind["Collection"] = "Collection"; + NodeKind["Episode"] = "Episode"; + NodeKind["Unitary"] = "Unitary"; +})(NodeKind || (NodeKind = {})); +var ShowKind; +(function(ShowKind) { + ShowKind["Delivery"] = "Delivery"; + ShowKind["Master"] = "Master"; +})(ShowKind || (ShowKind = {})); +var EventEnum; +(function(EventEnum) { + EventEnum["Ingest"] = "Ingest"; + EventEnum["Lifecycle"] = "Lifecycle"; + EventEnum["QualityCheck"] = "QualityCheck"; + EventEnum["Transcode"] = "Transcode"; +})(EventEnum || (EventEnum = {})); +var BroadcastableFileKind; +(function(BroadcastableFileKind) { + BroadcastableFileKind["ProxyManifest"] = "ProxyManifest"; + BroadcastableFileKind["ProxyDashVideo"] = "ProxyDashVideo"; + BroadcastableFileKind["ProxyAudio"] = "ProxyAudio"; + BroadcastableFileKind["ProxySubtitle"] = "ProxySubtitle"; + BroadcastableFileKind["VerificationReportPdf"] = "VerificationReportPdf"; + BroadcastableFileKind["VerificationReportXml"] = "VerificationReportXml"; + BroadcastableFileKind["Video"] = "Video"; + BroadcastableFileKind["Audio"] = "Audio"; + BroadcastableFileKind["Subtitle"] = "Subtitle"; + BroadcastableFileKind["Extra"] = "Extra"; +})(BroadcastableFileKind || (BroadcastableFileKind = {})); +var FileTypeVideo; +(function(FileTypeVideo) { + FileTypeVideo["Mxf"] = "Mxf"; + FileTypeVideo["Qtff"] = "Qtff"; + FileTypeVideo["Mp4"] = "Mp4"; +})(FileTypeVideo || (FileTypeVideo = {})); +var FileTypeAudio; +(function(FileTypeAudio) { + FileTypeAudio["Mp3"] = "Mp3"; +})(FileTypeAudio || (FileTypeAudio = {})); +var Phase; +(function(Phase) { + Phase["Waiting"] = "Waiting"; + Phase["Downloading"] = "Downloading"; + Phase["Encoding"] = "Encoding"; + Phase["Packaging"] = "Packaging"; + Phase["Uploading"] = "Uploading"; + Phase["Finished"] = "Finished"; +})(Phase || (Phase = {})); +var State; +(function(State) { + State["Active"] = "Active"; + State["Archived"] = "Archived"; + State["Restoring"] = "Restoring"; +})(State || (State = {})); +export { BroadcastableKind as BroadcastableKind }; +export { Kind as Kind }; +export { MXFVersion as MXFVersion }; +export { TranscodeKind as TranscodeKind }; +export { NodeKind as NodeKind }; +export { ShowKind as ShowKind }; +export { EventEnum as EventEnum }; +export { BroadcastableFileKind as BroadcastableFileKind }; +export { FileTypeVideo as FileTypeVideo }; +export { FileTypeAudio as FileTypeAudio }; +export { Phase as Phase }; +export { State as State }; +var FormatSpec; +(function(FormatSpec) { + FormatSpec["Mxf"] = "Mxf"; + FormatSpec["Mp4"] = "Mp4"; + FormatSpec["ProRes422"] = "ProRes422"; + FormatSpec["ProRes4444"] = "ProRes4444"; + FormatSpec["ADN"] = "ADN"; + FormatSpec["AVCIntra100"] = "AVCIntra100"; + FormatSpec["IMX50"] = "IMX50"; + FormatSpec["Mp4Salto"] = "Mp4Salto"; + FormatSpec["MovH264"] = "MovH264"; + FormatSpec["MovHevc"] = "MovHevc"; + FormatSpec["MxfProgressive"] = "MxfProgressive"; + FormatSpec["Demux"] = "Demux"; + FormatSpec["AudioExtract"] = "AudioExtract"; +})(FormatSpec || (FormatSpec = {})); +var FormatAudioCodec; +(function(FormatAudioCodec) { + FormatAudioCodec["PCMS24LE"] = "PCMS24LE"; + FormatAudioCodec["PCMS16LE"] = "PCMS16LE"; + FormatAudioCodec["PCMS24BE"] = "PCMS24BE"; + FormatAudioCodec["AAC"] = "AAC"; + FormatAudioCodec["MP3"] = "MP3"; + FormatAudioCodec["MOV_Conteneur"] = "MOV_Conteneur"; +})(FormatAudioCodec || (FormatAudioCodec = {})); +var FormatSegmentKind; +(function(FormatSegmentKind) { + FormatSegmentKind["Mire"] = "Mire"; + FormatSegmentKind["Black"] = "Black"; + FormatSegmentKind["Slate"] = "Slate"; + FormatSegmentKind["Video"] = "Video"; + FormatSegmentKind["Countdown"] = "Countdown"; +})(FormatSegmentKind || (FormatSegmentKind = {})); +var SubtitleFileFormat; +(function(SubtitleFileFormat) { + SubtitleFileFormat["STL"] = "STL"; + SubtitleFileFormat["WebVTT"] = "WebVTT"; + SubtitleFileFormat["SRT"] = "SRT"; +})(SubtitleFileFormat || (SubtitleFileFormat = {})); +var SubtitleDisplayStandard; +(function(SubtitleDisplayStandard) { + SubtitleDisplayStandard["Open"] = "Open"; + SubtitleDisplayStandard["Teletext1"] = "Teletext1"; + SubtitleDisplayStandard["Teletext2"] = "Teletext2"; +})(SubtitleDisplayStandard || (SubtitleDisplayStandard = {})); +var SubtitleTypeVersion; +(function(SubtitleTypeVersion) { + SubtitleTypeVersion["PARTIAL"] = "PARTIAL"; + SubtitleTypeVersion["COMPLETE"] = "COMPLETE"; + SubtitleTypeVersion["COMPLETE_WITHOUT_PARTIAL"] = "COMPLETE_WITHOUT_PARTIAL"; + SubtitleTypeVersion["SDH"] = "SDH"; +})(SubtitleTypeVersion || (SubtitleTypeVersion = {})); +var SegmentLabel; +(function(SegmentLabel) { + SegmentLabel["OpeningCredits"] = "OpeningCredits"; + SegmentLabel["EndingCredits"] = "EndingCredits"; + SegmentLabel["Introduction"] = "Introduction"; + SegmentLabel["Program"] = "Program"; + SegmentLabel["Trailer"] = "Trailer"; + SegmentLabel["Advertising"] = "Advertising"; + SegmentLabel["TestPattern"] = "TestPattern"; + SegmentLabel["Black"] = "Black"; + SegmentLabel["Slate"] = "Slate"; + SegmentLabel["NeutralBases"] = "NeutralBases"; + SegmentLabel["CustomDelivery"] = "CustomDelivery"; +})(SegmentLabel || (SegmentLabel = {})); +var Version; +(function(Version) { + Version["ARA"] = "ARA"; + Version["CHI"] = "CHI"; + Version["KOR"] = "KOR"; + Version["DAN"] = "DAN"; + Version["DUT"] = "DUT"; + Version["HEB"] = "HEB"; + Version["NLD"] = "NLD"; + Version["RUS"] = "RUS"; + Version["SWE"] = "SWE"; + Version["FRA"] = "FRA"; + Version["GER"] = "GER"; + Version["ITA"] = "ITA"; + Version["POR"] = "POR"; + Version["ENG"] = "ENG"; + Version["SPA"] = "SPA"; + Version["JPN"] = "JPN"; + Version["NOR"] = "NOR"; + Version["UKR"] = "UKR"; + Version["INT"] = "INT"; + Version["NOTHING"] = ""; +})(Version || (Version = {})); +var Mapping; +(function(Mapping) { + Mapping["AsMaster"] = "AsMaster"; + Mapping["NoSound"] = "NoSound"; + Mapping["VD"] = "VD"; + Mapping["VO"] = "VO"; + Mapping["VI"] = "VI"; + Mapping["VDVO"] = "VDVO"; + Mapping["VOAD"] = "VOAD"; + Mapping["VDAD"] = "VDAD"; + Mapping["VIVD"] = "VIVD"; + Mapping["VIVO"] = "VIVO"; + Mapping["VDVOAD"] = "VDVOAD"; + Mapping["VDVIVONLY"] = "VDVIVONLY"; + Mapping["VDVIMEVONLY"] = "VDVIMEVONLY"; +})(Mapping || (Mapping = {})); +var Layout; +(function(Layout) { + Layout["Mono"] = "Mono"; + Layout["DualMono"] = "DualMono"; + Layout["Stereo"] = "Stereo"; + Layout["StereoL"] = "StereoL"; + Layout["StereoR"] = "StereoR"; + Layout["FiveDotOne"] = "FiveDotOne"; + Layout["FiveDotOneL"] = "FiveDotOneL"; + Layout["FiveDotOneR"] = "FiveDotOneR"; + Layout["FiveDotOneC"] = "FiveDotOneC"; + Layout["FiveDotOneSL"] = "FiveDotOneSL"; + Layout["FiveDotOneSR"] = "FiveDotOneSR"; + Layout["FiveDotOneLFE"] = "FiveDotOneLFE"; + Layout["SevenDotOne"] = "SevenDotOne"; + Layout["OneTrack"] = "OneTrack"; +})(Layout || (Layout = {})); +var TypeVersion; +(function(TypeVersion) { + TypeVersion["ORIGINAL"] = "ORIGINAL"; + TypeVersion["DUBBED"] = "DUBBED"; + TypeVersion["AD"] = "AD"; + TypeVersion["MUTE"] = "MUTE"; + TypeVersion["INT"] = "INT"; + TypeVersion["ME"] = "ME"; + TypeVersion["VONLY"] = "VONLY"; +})(TypeVersion || (TypeVersion = {})); +export { FormatSpec as FormatSpec }; +export { FormatAudioCodec as FormatAudioCodec }; +export { FormatSegmentKind as FormatSegmentKind }; +export { SubtitleFileFormat as SubtitleFileFormat }; +export { SubtitleDisplayStandard as SubtitleDisplayStandard }; +export { SubtitleTypeVersion as SubtitleTypeVersion }; +export { SegmentLabel as SegmentLabel }; +export { Version as Version }; +export { Mapping as Mapping }; +export { Layout as Layout }; +export { TypeVersion as TypeVersion }; + From 875700ceb5e287c7a146870233db389170be6d45 Mon Sep 17 00:00:00 2001 From: Maxime Dantec Date: Tue, 29 Aug 2023 11:39:30 +0200 Subject: [PATCH 21/24] fixes --- mod.js | 15 ++++++++++----- src/nomalab.ts | 17 ++++++++++++----- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/mod.js b/mod.js index 0c0798b..4f2cbfa 100644 --- a/mod.js +++ b/mod.js @@ -16,11 +16,11 @@ class Nomalab { this.#apiToken = apiToken; } async me() { - const response = await this.#fetch(`users/me`, {}); + const response = await this.#fetch(`users/me`); return response.json(); } async getShow(showUuid) { - const response = await this.#fetch(`shows/${showUuid}`, {}); + const response = await this.#fetch(`shows/${showUuid}`); return response.json(); } async getRoots(organizationId) { @@ -58,7 +58,7 @@ class Nomalab { }); if (this.#apiToken && response.headers.has("set-cookie")) { response.headers.getSetCookie().forEach((sc)=>{ - const [name, value] = sc.split("="); + const [name, value, ..._xs] = sc.split(";")[0]?.split("="); if (name == "sessionJwt") { this.#apiToken = value; } @@ -218,6 +218,7 @@ class Nomalab { const myHeaders = new Headers(); myHeaders.append("Content-Type", optionalArg.contentType ?? "application/json"); if (this.#apiToken) { + myHeaders.append("Authorization", `Bearer ${this.#apiToken}`); myHeaders.append("Cookie", `sessionJwt=${this.#apiToken}`); } const request = new Request(`${this.#contextSubDomain()}/v3/${partialUrl}`, { @@ -226,11 +227,15 @@ class Nomalab { body: optionalArg.bodyJsonObject === undefined ? null : JSON.stringify(optionalArg.bodyJsonObject), credentials: this.#context ? "include" : undefined }); - return fetch(request).then((response)=>{ + console.log(request.url); + console.log(this.#contextSubDomain()); + console.log(this.#apiToken); + console.log(myHeaders); + return fetch(request).then(async (response)=>{ if (response.ok) { return response; } else { - throw response; + throw await response.json(); } }); } diff --git a/src/nomalab.ts b/src/nomalab.ts index a3392e4..85d9481 100644 --- a/src/nomalab.ts +++ b/src/nomalab.ts @@ -37,12 +37,12 @@ export class Nomalab { } async me(): Promise { - const response = await this.#fetch(`users/me`, {}); + const response = await this.#fetch(`users/me`); return response.json() as Promise; } async getShow(showUuid: string): Promise { - const response = await this.#fetch(`shows/${showUuid}`, {}); + const response = await this.#fetch(`shows/${showUuid}`); return response.json() as Promise; } @@ -111,7 +111,7 @@ export class Nomalab { if (this.#apiToken && response.headers.has("set-cookie")) { response.headers.getSetCookie().forEach((sc) => { - const [name, value] = sc.split("="); + const [name, value, ..._xs] = sc.split(";")[0]?.split("="); if (name == "sessionJwt") { this.#apiToken = value; } @@ -359,6 +359,7 @@ export class Nomalab { ); if (this.#apiToken) { + myHeaders.append("Authorization", `Bearer ${this.#apiToken}`); myHeaders.append( "Cookie", `sessionJwt=${this.#apiToken}`, @@ -376,11 +377,17 @@ export class Nomalab { credentials: this.#context ? "include" : undefined, }, ); - return fetch(request).then((response) => { + + console.log(request.url); + console.log(this.#contextSubDomain()); + console.log(this.#apiToken); + console.log(myHeaders); + + return fetch(request).then(async (response) => { if (response.ok) { return response; } else { - throw response; + throw (await response.json()); } }); } From 28e5c73761ef15e22da06440292071472db344d9 Mon Sep 17 00:00:00 2001 From: "perrine@nomalab.com" Date: Wed, 14 Feb 2024 12:55:56 +0100 Subject: [PATCH 22/24] Fix types --- src/nomalab.ts | 6 ++--- src/types.ts | 63 ++++++++++++++++++-------------------------------- 2 files changed, 26 insertions(+), 43 deletions(-) diff --git a/src/nomalab.ts b/src/nomalab.ts index 85d9481..0b9ac32 100644 --- a/src/nomalab.ts +++ b/src/nomalab.ts @@ -11,11 +11,11 @@ import { NodeClass, NodeKind, Organization, - Path, Segment, Show, ShowClass, ShowKind, + ShowPath, SubtitleFormatApi, } from "./types.ts"; import { Format } from "./formats.ts"; @@ -147,7 +147,7 @@ export class Nomalab { return response.json() as Promise; } - async getPath(showUuid: string): Promise { + async getPath(showUuid: string): Promise { const response = await this.#fetch( `admin/shows/path`, { @@ -155,7 +155,7 @@ export class Nomalab { method: "POST", }, ); - return response.json() as Promise; + return response.json() as Promise; } async getOrganizations(): Promise { diff --git a/src/types.ts b/src/types.ts index 2aba602..0a9e5dc 100644 --- a/src/types.ts +++ b/src/types.ts @@ -18,8 +18,8 @@ export interface MeUser { export interface Job { id: string; createdAt: string; - startedAt: null; - completedAt: string; + startedAt: null | string; + completedAt: null | string; show: string; organization: string; requester: string; @@ -29,14 +29,15 @@ export interface Job { | "QC" | "SimpleTranscode" | "Spotcheck"; - externalJobId: null; - format: string; - startedBy: null; - completedBy: null; + externalJobId: null | string; + format: null | string; + startedBy: null | string; + completedBy: null | string; acknowledge: boolean; - acknowledgedBy: null; + acknowledgedBy: null | string; } -export interface Path { + +export interface ShowPath { showId: string; path: PathElement[]; } @@ -56,13 +57,12 @@ export interface Show { organization: ShowOrganization; channels: unknown[]; invitations: unknown[]; - timeline: unknown[]; - extras: FileWrapper[]; - activeBroadcastable: ActiveBroadcastable; - previousBroadcastables: unknown[]; + extras: ExtraFile[]; + activeBroadcastable: BroadcastableApi; + previousBroadcastables: BroadcastableApi[]; } -export interface ActiveBroadcastable { +export interface BroadcastableApi { broadcastable: Broadcastable; files: Files; comments: unknown[]; @@ -83,13 +83,19 @@ export interface Broadcastable { export interface Files { material: Material; audios: Material[]; - subtitles: Subtitle[]; + subtitles: Material[]; } -export interface FileWrapper { +export interface ExtraFile { file: FileClass; + container: null | Container; + streams: FileStream[]; + proxy: null | FileClass; + segments: FileSegment[]; } -export interface Material extends FileWrapper { + +export interface Material { + file: FileClass; container: Container; streams: FileStream[]; proxies: Proxies; @@ -142,10 +148,6 @@ export interface DeliveryNode { parent: null | string; } -export interface Subtitles { - subtitle: unknown[]; -} - export interface SubtitleWarning { name: string; timecode: string; @@ -189,7 +191,7 @@ export interface Delivery { export interface DeliveryTranscoding { file: string; phase: Phase; - progress: number | null | string; + progress: null | number; startedAt: string; progressedAt: string; log: null | string; @@ -507,17 +509,6 @@ export interface FileDataStream { timecode?: string; } -export interface Subtitle { - file: FileClass; - container: null; - streams: Array>; - proxies: Proxies; - reportXml: null; - reportPdf: null; - deliveries: unknown[]; - segments: unknown[]; -} - export interface FluffyStream { fileId: string; index: number; @@ -815,14 +806,6 @@ export interface FileClass { format: Formats.Format; } -export interface ExtraApi { - file: FileClass; - proxy: FileClass | null; - segments: FileSegment[]; - container: FileContainer | null; - streams: FileStream[]; -} - export interface FileUploads { uploads: FileClass[]; parts: UploadPart[]; From c4860d49d5068c42c9d5314e59aabbbc1d66e54a Mon Sep 17 00:00:00 2001 From: nomalab Date: Wed, 21 Feb 2024 15:07:19 +0100 Subject: [PATCH 23/24] add_getSubtitleFormatsList --- src/nomalab.ts | 12 ++++++++++++ src/types.ts | 28 ++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/nomalab.ts b/src/nomalab.ts index 0b9ac32..7f44cda 100644 --- a/src/nomalab.ts +++ b/src/nomalab.ts @@ -17,6 +17,7 @@ import { ShowKind, ShowPath, SubtitleFormatApi, + SubtitleFormats, } from "./types.ts"; import { Format } from "./formats.ts"; @@ -294,6 +295,17 @@ export class Nomalab { return response.json() as Promise; } + async getSubtitleFormatsList(): Promise { + const response = await this.#fetch(`subtitleFormats`, {}); + return response.json() as Promise; + } + + #throwError(message: string, response: Response): void { + console.error(message); + console.error(response); + throw new Error(message); + } + async getFileSegments(materialId: string) { const response = await this.#fetch(`files/${materialId}/segments`, {}); return response.json() as Promise; diff --git a/src/types.ts b/src/types.ts index 0a9e5dc..b127a7d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -165,6 +165,34 @@ export interface SubtitleFormatApi { format: string; } +export interface SubtitleFormats { + id: string; + name: string; + format: SubtitleFileFormat; + start_timecode?: string; + frame_rate?: FrameRate; + display_standard?: SubtitleDisplayStandard; + offset?: string; +} + +export enum SubtitleFileFormat { + STL = "STL", + WebVTT = "WebVTT", + SRT = "SRT", +} + +export enum SubtitleDisplayStandard { + Open = "Open", + Teletext1 = "Teletext1", + Teletext2 = "Teletext2", +} + +export interface FrameRate { + id: string; + numerator: number; + denominator: number; +} + export interface DeliverPayload { format: string; versionMapping: Formats.Mapping; From 23ff61c5be7b9c9fd9db64294e9b88ef218cca64 Mon Sep 17 00:00:00 2001 From: Bramart Date: Tue, 23 Apr 2024 11:54:58 +0200 Subject: [PATCH 24/24] return list instead of one --- src/nomalab.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nomalab.ts b/src/nomalab.ts index 7f44cda..b53bcab 100644 --- a/src/nomalab.ts +++ b/src/nomalab.ts @@ -295,9 +295,9 @@ export class Nomalab { return response.json() as Promise; } - async getSubtitleFormatsList(): Promise { + async getSubtitleFormatsList(): Promise { const response = await this.#fetch(`subtitleFormats`, {}); - return response.json() as Promise; + return response.json() as Promise; } #throwError(message: string, response: Response): void {