Skip to content

Commit

Permalink
feat: cloning, deleting and copy status on clips
Browse files Browse the repository at this point in the history
  • Loading branch information
sparkpunkd committed Jul 3, 2019
1 parent c5082e4 commit 30e3b51
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 25 deletions.
15 changes: 10 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -343,20 +343,21 @@ The `:start` parameter is the first frame in the port's timeline to wipe from an

### Cloning clips

The Quantel systems have a mechanism to clone clips between servers, either within in the same zone or between servers in different zones (_inter-zone cloning_). Only the essence material that is missing from a particular disk pool is copied, meaning that a request to clone can be almost instantaneous where the material has already been duplicated. The Quantel gateway allows clones to be initiated and the subsequent copy progress of that or any other clone to be monitored.
The Quantel systems have a mechanism to clone clips between servers, either within in the same zone or between servers in different zones (_inter-zone cloning_). Only the source essence material that is missing from a particular destination disk pool is copied, meaning that a request to clone can be almost instantaneous where the material has already been duplicated. The Quantel gateway allows clones to be initiated and the subsequent copy progress of that or any other clone to be monitored.

To cause a clone, POST an object containing the source `zoneID` (number, not name), source `clipID` and destination `poolID` to `/:zoneID/copy`, as follows:
To cause a clone, POST an object containing the source `zoneID` (number, omit for a within-zone copy), source `clipID` and destination `poolID` to `/:zoneID/copy`, as follows:

```JSON
{
"zoneID": 1000,
"clipID": 42,
"poolID": 12,
"priority": 15
"priority": 15,
"history": true
}
```

The `priority` is a number between `0` for low and `15` for high that indicates a relative priority for this requested clone wrt other current copy operations. The response is similar, with an additional property `copyID` that is the clip ID of the newly created clip at the destination.
The optional `priority` is a number between `0` for _low_ and `15` for _high_ that provides a relative priority for this requested clone wrt other current copy operations. Relevant for interzone cloning only, the optional `history` flag specifies whether the provenance of the clip should be carried along with the copy. The response is similar to the request, with an additional property `copyID` that is the clip ID of the newly created clip - or the clip ID of an existing copy - at the destination and `copyCreated` if a copy operation was required.

To view the status of a single copy operation for destination clip `:copyID`, use path:

Expand All @@ -382,7 +383,11 @@ Copies can be halted by deleting the destination clip.

### Deleting Clips

TO FOLLOW.
A clip can be deleted by sending a DELETE request to its path:

/:zoneID/clip/:clipID

Note that the clip metadata will persist in the database but the essence will be removed, setting the `Frames` field to `0`.

### Controlling the port

Expand Down
52 changes: 44 additions & 8 deletions src/cxx/clone.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

#include "clone.h"

// TODO leaving sync for now until requirements are clearer
// Deprecated ... cloneInterZone is now a general clone operation
void cloneIfNeededExecute(napi_env env, void* data) {
cloneIfNeededCarrier* c = (cloneIfNeededCarrier*) data;
Quentin::ZonePortal::_ptr_type zp;
Expand Down Expand Up @@ -141,11 +141,22 @@ napi_value cloneIfNeeded(napi_env env, napi_callback_info info) {
void cloneInterZoneExecute(napi_env env, void* data) {
cloneInterZoneCarrier* c = (cloneInterZoneCarrier*) data;
Quentin::ZonePortal::_ptr_type zp;
CORBA::Boolean copyCreated;

try {
resolveZonePortalShared(c->isaIOR, &zp);

c->copyID = zp->cloneClipInterZone(c->zoneID, c->clipID, c->poolID, c->priority);
if (c->zoneID < 0) { // Local zone clone
// Note: Clip does not expire - that's what -1 means!
c->copyID = zp->cloneIfNeeded(c->clipID, c->poolID, 0, c->priority, -1, copyCreated);
c->copyCreated = copyCreated;
} else {
if (c->history) {
c->copyID = zp->cloneClipInterZone(c->zoneID, c->clipID, c->poolID, c->priority);
} else {
c->copyID = zp->cloneClipInterZoneWithoutHistory(c->zoneID, c->clipID, c->poolID, c->priority);
}
}
}
catch(CORBA::SystemException& ex) {
NAPI_REJECT_SYSTEM_EXCEPTION(ex);
Expand All @@ -171,15 +182,17 @@ void cloneInterZoneComplete(napi_env env, napi_status asyncStatus, void* data) {
c->status = napi_create_object(env, &result);
REJECT_STATUS;

c->status = napi_create_string_utf8(env, "CloneInterZoneResult", NAPI_AUTO_LENGTH, &prop);
c->status = napi_create_string_utf8(env, "CloneResult", NAPI_AUTO_LENGTH, &prop);
REJECT_STATUS;
c->status = napi_set_named_property(env, result, "type", prop);
REJECT_STATUS;

c->status = napi_create_int32(env, c->zoneID , &prop);
REJECT_STATUS;
c->status = napi_set_named_property(env, result, "zoneID", prop);
REJECT_STATUS;
if (c->zoneID >= 0) {
c->status = napi_create_int32(env, c->zoneID , &prop);
REJECT_STATUS;
c->status = napi_set_named_property(env, result, "zoneID", prop);
REJECT_STATUS;
}

c->status = napi_create_int32(env, c->clipID , &prop);
REJECT_STATUS;
Expand All @@ -196,6 +209,16 @@ void cloneInterZoneComplete(napi_env env, napi_status asyncStatus, void* data) {
c->status = napi_set_named_property(env, result, "priority", prop);
REJECT_STATUS;

c->status = napi_get_boolean(env, c->history, &prop);
REJECT_STATUS;
c->status = napi_set_named_property(env, result, "history", prop);
REJECT_STATUS;

c->status = napi_get_boolean(env, c->copyCreated, &prop);
REJECT_STATUS;
c->status = napi_set_named_property(env, result, "copyCreated", prop);
REJECT_STATUS;

c->status = napi_create_int32(env, c->copyID, &prop);
REJECT_STATUS;
c->status = napi_set_named_property(env, result, "copyID", prop);
Expand Down Expand Up @@ -252,8 +275,12 @@ napi_value cloneInterZone(napi_env env, napi_callback_info info) {
options = argv[1];
c->status = napi_get_named_property(env, options, "zoneID", &prop);
REJECT_RETURN;
c->status = napi_get_value_int32(env, prop, (int32_t*) &c->zoneID);
c->status = napi_typeof(env, prop, &type);
REJECT_RETURN;
if (type == napi_number) {
c->status = napi_get_value_int32(env, prop, (int32_t*) &c->zoneID);
REJECT_RETURN;
}

c->status = napi_get_named_property(env, options, "clipID", &prop);
REJECT_RETURN;
Expand All @@ -274,6 +301,15 @@ napi_value cloneInterZone(napi_env env, napi_callback_info info) {
REJECT_RETURN;
}

c->status = napi_get_named_property(env, options, "history", &prop);
REJECT_RETURN;
c->status = napi_typeof(env, prop, &type);
REJECT_RETURN;
if (type == napi_boolean) {
c->status = napi_get_value_bool(env, prop, &c->history);
REJECT_RETURN;
}

c->status = napi_create_string_utf8(env, "CloneInterZone", NAPI_AUTO_LENGTH, &resourceName);
REJECT_RETURN;
c->status = napi_create_async_work(env, nullptr, resourceName, cloneInterZoneExecute,
Expand Down
6 changes: 5 additions & 1 deletion src/cxx/clone.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
#include "qgw_util.h"
#include <vector>

// Deprecated ... cloneInterZone is now a general clone operation
napi_value cloneIfNeeded(napi_env env, napi_callback_info info);

napi_value cloneInterZone(napi_env env, napi_callback_info info);
napi_value getCopyRemaining(napi_env env, napi_callback_info info);
napi_value getCopiesRemaining(napi_env env, napi_callback_info info);
Expand All @@ -45,11 +47,13 @@ void cloneInterZoneExecute(napi_env env, void* data);
void cloneInterZoneComplete(napi_env env, napi_status asyncStatus, void* data);

struct cloneInterZoneCarrier : carrier {
int32_t zoneID; // ZoneID of the remote zone where the source clip resides
int32_t zoneID = -1; // ZoneID of the remote zone where the source clip resides, negative for same zone
int32_t clipID; // The clipID of the clip in the remote zone to be cloned
int32_t poolID; // PoolID in the local destination zone where the clip it copied to
int32_t priority = Quentin::Port::StandardPriority; // The priority of the transfer
int32_t copyID; // Newly allocated clipID on the local destination zone
bool history = true;
bool copyCreated = true;
~cloneInterZoneCarrier() { }
};

Expand Down
15 changes: 9 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,16 +357,18 @@ export namespace Quantel {
compressionName: string
}

export interface CloneInterZoneInfo {
zoneID: number // Source zone ID
export interface CloneInfo {
zoneID?: number // Source zone ID, omit for local zone
clipID: number // Source clip ID
poolID: number // Destination pool ID
priority?: number // Prioerity, between 0 (low) and 15 (high) - default is 8 (standard)
priority?: number // Priority, between 0 (low) and 15 (high) - default is 8 (standard)
history?: boolean // Should an interzone clone link to historical provinance - default is true
}

export interface CloneInterZoneResult extends CloneInterZoneInfo {
type: 'CloneInterZoneResult'
export interface CloneResult extends CloneInfo {
type: 'CloneResult'
copyID: number
copyCreated: boolean
}

export interface CopyProgress extends ClipRef {
Expand Down Expand Up @@ -788,6 +790,7 @@ export namespace Quantel {
Plan is to hide this behind the REST API
*/
// Deprecated ... cloneInterZone is now a general clone operation
export async function cloneIfNeeded (options: CloneRequest): Promise<boolean> {
try {
await getISAReference()
Expand All @@ -802,7 +805,7 @@ export namespace Quantel {
}
}

export async function cloneInterZone (options: CloneInterZoneInfo): Promise<CloneInterZoneResult> {
export async function cloneInterZone (options: CloneInfo): Promise<CloneResult> {
try {
await getISAReference()
return await quantel.cloneInterZone(await isaIOR, options)
Expand Down
42 changes: 37 additions & 5 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ router.get('/:zoneID/', async (ctx) => {
let zones = await Quantel.listZones()
let inTheZone = zones.find(z => z.zoneName === ctx.params.zoneID || z.zoneNumber.toString() === ctx.params.zoneID)
if (inTheZone) {
ctx.body = [ 'server/', 'clip/', 'format/' ]
ctx.body = [ 'server/', 'clip/', 'format/', 'copy/' ]
} else {
ctx.status = 404
ctx.body = {
Expand Down Expand Up @@ -182,15 +182,15 @@ router.get('/default/copy/:copyID', async (ctx) => {
})

router.post('/default/copy', async (ctx) => {
let clone: Quantel.CloneInterZoneInfo = {} as Quantel.CloneInterZoneInfo
let clone: Quantel.CloneInfo = {} as Quantel.CloneInfo
try {
if (ctx.body && ctx.status === 400) return
clone = ctx.request.body as Quantel.CloneInterZoneInfo
if (isNaN(+clone.zoneID) || +clone.zoneID < 0) {
clone = ctx.request.body as Quantel.CloneInfo
if (clone.zoneID && (isNaN(+clone.zoneID) || +clone.zoneID < 0)) {
ctx.status = 400
ctx.body = {
status: 400,
message: 'Bad request. A clone request must use a positive integer for zone ID.',
message: 'Bad request. Where present, aclone request must use a positive integer for zone ID.',
stack: ''
}
return
Expand Down Expand Up @@ -223,6 +223,9 @@ router.post('/default/copy', async (ctx) => {
}
return
}
if (clone.history) {
clone.history = true
}
ctx.body = await Quantel.cloneInterZone(clone)
} catch (err) {
if (err.message.indexOf('BadIdent') >= 0) {
Expand Down Expand Up @@ -385,6 +388,35 @@ router.get('/default/clip/:clipID', async (ctx) => {
}
})

router.delete('/default/clip/:clipID', async (ctx) => {
if (isNaN(+ctx.params.clipID) || +ctx.params.clipID < 0) {
ctx.status = 400
ctx.body = {
status: 400,
message: 'Bad request. Clip ID must be a positive number.',
stack: ''
} as JSONError
return
}
try {
ctx.body = {
deleted: await Quantel.deleteClip({ clipID: +ctx.params.clipID })
}
} catch (err) {
if (err.message.indexOf('BadIdent') >= 0) {
ctx.status = 404
ctx.body = {
status: 404,
message: `Not found. A clip with identifier '${ctx.params.clipID}' was not found.`,
stack: ''
}
} else {
throw err
}
}

})

router.get('/default/clip/:clipID/fragments', async (ctx) => {
if (isNaN(+ctx.params.clipID) || +ctx.params.clipID < 0) {
ctx.status = 400
Expand Down

0 comments on commit 30e3b51

Please sign in to comment.