import {Action} from "../../internal/Action.js";
import {Transformation} from "../../transformation/Transformation.js";
import {VideoSource} from "../../qualifiers/source/sourceTypes/VideoSource.js";
import {ImageSource} from "../../qualifiers/source/sourceTypes/ImageSource.js";
import {FetchSource} from "../../qualifiers/source/sourceTypes/FetchSource.js";
import {IActionModel} from "../../internal/models/IActionModel.js";
import {IConcatenateActionModel} from "../../internal/models/IConcatenateActionModel.js";
import {ITransformationFromJson} from "../../internal/models/IHasFromJson.js";
import {IVideoSourceModel} from "../../internal/models/IVideoSourceModel.js";
import {createSourceFromModel} from "../../internal/models/createSourceFromModel.js";
/**
* @description Class for Concatenating another video.
*
* <b>Learn more</b>: {@link https://cloudinary.com/documentation/video_trimming_and_concatenating#concatenating_media|Concatenating videos}
* @extends SDK.Action
* @memberOf Actions.VideoEdit
* @see Visit {@link Actions.VideoEdit|VideoEdit} for an example
*/
class ConcatenateAction extends Action {
private concatSource: VideoSource | ImageSource | FetchSource;
private _prepend: boolean;
private _duration: number;
private _transition: VideoSource;
protected _actionModel: IConcatenateActionModel;
/**
*
* @param {Qualifiers.Source.VideoSource | Qualifiers.Source.ImageSource | Qualifiers.Source.FetchSource} source
* the Source to concatenate
*/
constructor(source: VideoSource | ImageSource | FetchSource) {
super();
this._actionModel = {
actionType: 'concatenate',
source: source.toJson() as IVideoSourceModel
};
this.concatSource = source;
}
/**
* @description Sets the transition between a video and a concatenated source
* @param {Qualifiers.Transition.VideoSource} source The source to concatenate.
* @return {this}
*/
transition(source: VideoSource): this {
this._actionModel.transition = source.toJson() as IVideoSourceModel;
this._transition = source;
return this;
}
/**
* @description Prepend the concatenated video - Adds the video before the original
* @return {this}
*/
prepend(): this {
this._actionModel.prepend = true;
this._prepend = true;
return this;
}
/**
* The duration in seconds
* @param {number} sec
* @return {this}
*/
duration(sec: number): this {
this._actionModel.duration = sec;
this._duration = sec;
return this;
}
/**
* @description Get the transitionString for the toString() method
* @return {string}
*/
getTransitionString(): string {
const transTx = this._transition.getTransformation();
return [
`e_transition,${this._transition.getOpenSourceString('l')}`,
transTx && transTx.toString(),
'fl_layer_apply'
].filter((a) => a).join('/');
}
/**
* @description Get the string representation of the Concatenation action
*/
toString(): string {
/*
*
* The toString() method is composed of several steps due to the complex nature of the concatenate transformation.
*
* First, we calculate the open and close parts of the top-level transformation:
* - {open}/{sourceTransformation}/{close}
*
* Unlike a regular overlay, there are multiple 'bits' appended to the open and close parts of the tx.
* - duration (du_) might be prepended on the opening of the layer (du_5,l_sample)
* - fl_splice is also added, but only if a transition is not needed.
*
* once we've calculated the open and close parts, we now need to deal with the Transition.
* the transition is an inner transformation on the source with a special effect (e_transition) appended to it.
*
* To calculate the transition string, we need to take the transformation from the source(assuming it has one)
*/
// Calculate the open part
const open = [
this._duration && `du_${this._duration}`,
!this._transition && `fl_splice`,
`${this.concatSource.getOpenSourceString('l')}`
].filter((a) => a).join(',');
// Calculate the open part
const close = [
'fl_layer_apply',
this._prepend && 'so_0'
].filter((a) => a).join(',');
// Calculate the Transition part
let concatSourceTx;
if (this.concatSource.getTransformation()) {
concatSourceTx = this.concatSource.getTransformation();
} else {
concatSourceTx = new Transformation();
}
if (this._transition) {
concatSourceTx.addTransformation(this.getTransitionString());
}
// Put it all together, the transition is already part of the concatSourceTx
return [
open,
concatSourceTx.toString(),
close
].filter((a) => a).join('/');
}
static fromJson(actionModel: IActionModel, transformationFromJson: ITransformationFromJson): ConcatenateAction {
const {source, transition, prepend, duration} = (actionModel as IConcatenateActionModel);
const sourceInstance = createSourceFromModel(source, transformationFromJson) as ImageSource;
// We are using this() to allow inheriting classes to use super.fromJson.apply(this, [actionModel])
// This allows the inheriting classes to determine the class to be created
const result = new this(sourceInstance);
if (transition){
result.transition(VideoSource.fromJson(transition, transformationFromJson));
}
if (prepend){
result.prepend();
}
if (duration){
result.duration(duration);
}
return result;
}
}
export default ConcatenateAction;