@twick/studio 0.15.25 → 0.15.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/properties/generate-captions.d.ts +3 -2
- package/dist/helpers/generate-captions.service.d.ts +4 -4
- package/dist/hooks/use-generate-captions.d.ts +2 -2
- package/dist/hooks/use-studio-operation.d.ts +0 -2
- package/dist/index.js +57 -17
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +57 -17
- package/dist/index.mjs.map +1 -1
- package/dist/types/index.d.ts +2 -1
- package/package.json +15 -15
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TrackElement, VideoElement } from '@twick/timeline';
|
|
2
|
-
import { ICaptionGenerationPollingResponse } from '../../types';
|
|
2
|
+
import { CaptionPhraseLength, ICaptionGenerationPollingResponse } from '../../types';
|
|
3
3
|
|
|
4
4
|
export declare function GenerateCaptionsPanel({ selectedElement, addCaptionsToTimeline, onGenerateCaptions, getCaptionstatus, pollingIntervalMs, }: {
|
|
5
5
|
selectedElement: TrackElement;
|
|
@@ -7,8 +7,9 @@ export declare function GenerateCaptionsPanel({ selectedElement, addCaptionsToTi
|
|
|
7
7
|
s: number;
|
|
8
8
|
e: number;
|
|
9
9
|
t: string;
|
|
10
|
+
w?: number[];
|
|
10
11
|
}[]) => void;
|
|
11
|
-
onGenerateCaptions: (videoElement: VideoElement) => Promise<string | null>;
|
|
12
|
+
onGenerateCaptions: (videoElement: VideoElement, language?: string, phraseLength?: CaptionPhraseLength) => Promise<string | null>;
|
|
12
13
|
getCaptionstatus: (reqId: string) => Promise<ICaptionGenerationPollingResponse>;
|
|
13
14
|
pollingIntervalMs?: number;
|
|
14
15
|
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ProjectJSON, VideoElement } from '@twick/timeline';
|
|
2
|
-
import { ICaptionGenerationPollingResponse, ICaptionGenerationService
|
|
2
|
+
import { CaptionEntry, CaptionPhraseLength, ICaptionGenerationPollingResponse, ICaptionGenerationService } from '../types';
|
|
3
3
|
|
|
4
4
|
declare class GenerateCaptionsService implements ICaptionGenerationService {
|
|
5
5
|
/**
|
|
@@ -8,13 +8,13 @@ declare class GenerateCaptionsService implements ICaptionGenerationService {
|
|
|
8
8
|
*/
|
|
9
9
|
videoElement: VideoElement | null;
|
|
10
10
|
projectJSON: ProjectJSON | null;
|
|
11
|
-
generateSubtiltesApi: (videoUrl: string) => Promise<string>;
|
|
11
|
+
generateSubtiltesApi: (videoUrl: string, language?: string, phraseLength?: CaptionPhraseLength) => Promise<string>;
|
|
12
12
|
requestStatusApi: (reqId: string) => Promise<ICaptionGenerationPollingResponse>;
|
|
13
13
|
constructor({ generateSubtiltesApi, requestStatusApi, }: {
|
|
14
|
-
generateSubtiltesApi: (videoUrl: string) => Promise<string>;
|
|
14
|
+
generateSubtiltesApi: (videoUrl: string, language?: string, phraseLength?: CaptionPhraseLength) => Promise<string>;
|
|
15
15
|
requestStatusApi: (reqId: string) => Promise<ICaptionGenerationPollingResponse>;
|
|
16
16
|
});
|
|
17
|
-
generateCaptions(videoElement: VideoElement, projectJSON: ProjectJSON): Promise<string>;
|
|
17
|
+
generateCaptions(videoElement: VideoElement, projectJSON: ProjectJSON, language?: string, phraseLength?: CaptionPhraseLength): Promise<string>;
|
|
18
18
|
getRequestStatus(reqId: string): Promise<ICaptionGenerationPollingResponse>;
|
|
19
19
|
updateProjectWithCaptions(captions: CaptionEntry[]): ProjectJSON;
|
|
20
20
|
generateCaptionVideo(videoUrl: string, videoSize?: {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { VideoElement } from '@twick/timeline';
|
|
2
|
-
import { ICaptionGenerationPollingResponse, StudioConfig, CaptionEntry } from '../types';
|
|
2
|
+
import { ICaptionGenerationPollingResponse, StudioConfig, CaptionEntry, CaptionPhraseLength } from '../types';
|
|
3
3
|
|
|
4
4
|
declare const useGenerateCaptions: (studioConfig?: StudioConfig) => {
|
|
5
|
-
onGenerateCaptions: (videoElement: VideoElement) => Promise<string | null>;
|
|
5
|
+
onGenerateCaptions: (videoElement: VideoElement, language?: string, phraseLength?: CaptionPhraseLength) => Promise<string | null>;
|
|
6
6
|
addCaptionsToTimeline: (captions: CaptionEntry[]) => void;
|
|
7
7
|
getCaptionstatus: (reqId: string) => Promise<ICaptionGenerationPollingResponse>;
|
|
8
8
|
pollingIntervalMs: number;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { VideoElement } from '@twick/timeline';
|
|
2
1
|
import { ICaptionGenerationPollingResponse, StudioConfig, CaptionEntry } from '../types';
|
|
3
2
|
|
|
4
3
|
declare const useStudioOperation: (studioConfig?: StudioConfig) => {
|
|
@@ -8,7 +7,6 @@ declare const useStudioOperation: (studioConfig?: StudioConfig) => {
|
|
|
8
7
|
onNewProject: () => void;
|
|
9
8
|
onExportCaptions: (format: "srt" | "vtt") => Promise<void>;
|
|
10
9
|
onExportChapters: (format: "youtube" | "json") => Promise<void>;
|
|
11
|
-
onGenerateCaptions: (videoElement: VideoElement) => Promise<string | null>;
|
|
12
10
|
addCaptionsToTimeline: (captions: CaptionEntry[]) => void;
|
|
13
11
|
getCaptionstatus: (reqId: string) => Promise<ICaptionGenerationPollingResponse>;
|
|
14
12
|
};
|
package/dist/index.js
CHANGED
|
@@ -3028,10 +3028,10 @@ const TEXT_STYLE_PRESETS = [
|
|
|
3028
3028
|
applyBackground: false
|
|
3029
3029
|
},
|
|
3030
3030
|
{
|
|
3031
|
-
id: "
|
|
3032
|
-
label: "
|
|
3031
|
+
id: "bold-caption",
|
|
3032
|
+
label: "Bold Caption",
|
|
3033
3033
|
description: "Bold white with strong outline",
|
|
3034
|
-
fontFamily: VideoEditor.AVAILABLE_TEXT_FONTS.
|
|
3034
|
+
fontFamily: VideoEditor.AVAILABLE_TEXT_FONTS.ROBOTO,
|
|
3035
3035
|
fontSize: 34,
|
|
3036
3036
|
fontWeight: 700,
|
|
3037
3037
|
textColor: "#FFFFFF",
|
|
@@ -3131,7 +3131,7 @@ const TEXT_STYLE_PRESETS = [
|
|
|
3131
3131
|
id: "cta-pill",
|
|
3132
3132
|
label: "CTA Pill",
|
|
3133
3133
|
description: "Call-to-action pill",
|
|
3134
|
-
fontFamily: VideoEditor.AVAILABLE_TEXT_FONTS.
|
|
3134
|
+
fontFamily: VideoEditor.AVAILABLE_TEXT_FONTS.LUCKIEST_GUY,
|
|
3135
3135
|
fontSize: 28,
|
|
3136
3136
|
fontWeight: 700,
|
|
3137
3137
|
textColor: "#FFFFFF",
|
|
@@ -6943,6 +6943,8 @@ function GenerateCaptionsPanel({
|
|
|
6943
6943
|
const [isGenerating, setIsGenerating] = react.useState(false);
|
|
6944
6944
|
const [pollingStatus, setPollingStatus] = react.useState("idle");
|
|
6945
6945
|
const [errorMessage, setErrorMessage] = react.useState(null);
|
|
6946
|
+
const [selectedLanguage, setSelectedLanguage] = react.useState("auto");
|
|
6947
|
+
const [phraseLength, setPhraseLength] = react.useState("medium");
|
|
6946
6948
|
const pollingIntervalRef = react.useRef(null);
|
|
6947
6949
|
const currentReqIdRef = react.useRef(null);
|
|
6948
6950
|
react.useEffect(() => {
|
|
@@ -7003,7 +7005,12 @@ function GenerateCaptionsPanel({
|
|
|
7003
7005
|
setPollingStatus("polling");
|
|
7004
7006
|
const videoElement = selectedElement;
|
|
7005
7007
|
try {
|
|
7006
|
-
const
|
|
7008
|
+
const language = selectedLanguage === "auto" ? void 0 : selectedLanguage;
|
|
7009
|
+
const reqId = await onGenerateCaptions(
|
|
7010
|
+
videoElement,
|
|
7011
|
+
language,
|
|
7012
|
+
phraseLength
|
|
7013
|
+
);
|
|
7007
7014
|
if (!reqId) {
|
|
7008
7015
|
setPollingStatus("error");
|
|
7009
7016
|
setIsGenerating(false);
|
|
@@ -7062,6 +7069,47 @@ function GenerateCaptionsPanel({
|
|
|
7062
7069
|
/* @__PURE__ */ jsxRuntime.jsx(Volume2, { className: "empty-state-icon" }),
|
|
7063
7070
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "empty-state-text", children: "Audio detected! You can now generate captions" })
|
|
7064
7071
|
] }) }) }),
|
|
7072
|
+
!isLoading && containsAudio === true && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "panel-section", children: [
|
|
7073
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "label-dark", htmlFor: "caption-language", children: "Audio Language" }),
|
|
7074
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
7075
|
+
"select",
|
|
7076
|
+
{
|
|
7077
|
+
id: "caption-language",
|
|
7078
|
+
className: "select-dark",
|
|
7079
|
+
value: selectedLanguage,
|
|
7080
|
+
onChange: (e) => setSelectedLanguage(e.target.value),
|
|
7081
|
+
children: [
|
|
7082
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "auto", children: "Auto (detect)" }),
|
|
7083
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "english", children: "English" }),
|
|
7084
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "italian", children: "Italian" }),
|
|
7085
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "spanish", children: "Spanish" }),
|
|
7086
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "portuguese", children: "Portuguese" }),
|
|
7087
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "french", children: "French" }),
|
|
7088
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "german", children: "German" }),
|
|
7089
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "turkish", children: "Turkish" }),
|
|
7090
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "indonesian", children: "Indonesian" }),
|
|
7091
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "hindi", children: "Hindi" })
|
|
7092
|
+
]
|
|
7093
|
+
}
|
|
7094
|
+
)
|
|
7095
|
+
] }),
|
|
7096
|
+
!isLoading && containsAudio === true && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "panel-section", children: [
|
|
7097
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "label-dark", htmlFor: "caption-phrase-length", children: "Caption length" }),
|
|
7098
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
7099
|
+
"select",
|
|
7100
|
+
{
|
|
7101
|
+
id: "caption-phrase-length",
|
|
7102
|
+
className: "select-dark",
|
|
7103
|
+
value: phraseLength,
|
|
7104
|
+
onChange: (e) => setPhraseLength(e.target.value),
|
|
7105
|
+
children: [
|
|
7106
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "short", children: "Short" }),
|
|
7107
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "medium", children: "Medium" }),
|
|
7108
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "long", children: "Long" })
|
|
7109
|
+
]
|
|
7110
|
+
}
|
|
7111
|
+
)
|
|
7112
|
+
] }),
|
|
7065
7113
|
!isLoading && isGenerating && pollingStatus === "polling" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "panel-section", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "empty-state", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "empty-state-content", children: [
|
|
7066
7114
|
/* @__PURE__ */ jsxRuntime.jsx(LoaderCircle, { className: "empty-state-icon animate-spin" }),
|
|
7067
7115
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "empty-state-text", children: "Generating captions... Please wait" })
|
|
@@ -7597,15 +7645,6 @@ const useStudioOperation = (studioConfig) => {
|
|
|
7597
7645
|
const fileName = `${(projectName || "chapters").replace(/\.json$/i, "")}.${format === "youtube" ? "txt" : "json"}`;
|
|
7598
7646
|
await saveAsFile(content, "text/plain", fileName);
|
|
7599
7647
|
};
|
|
7600
|
-
const onGenerateCaptions = async (videoElement) => {
|
|
7601
|
-
if (studioConfig == null ? void 0 : studioConfig.captionGenerationService) {
|
|
7602
|
-
const service = studioConfig.captionGenerationService;
|
|
7603
|
-
const reqId = await service.generateCaptions(videoElement, present);
|
|
7604
|
-
return reqId;
|
|
7605
|
-
}
|
|
7606
|
-
alert("Generate captions not supported in demo mode");
|
|
7607
|
-
return null;
|
|
7608
|
-
};
|
|
7609
7648
|
const addCaptionsToTimeline = (captions) => {
|
|
7610
7649
|
var _a;
|
|
7611
7650
|
const updatedProjectJSON = (_a = studioConfig == null ? void 0 : studioConfig.captionGenerationService) == null ? void 0 : _a.updateProjectWithCaptions(captions);
|
|
@@ -7630,7 +7669,6 @@ const useStudioOperation = (studioConfig) => {
|
|
|
7630
7669
|
onNewProject,
|
|
7631
7670
|
onExportCaptions,
|
|
7632
7671
|
onExportChapters,
|
|
7633
|
-
onGenerateCaptions,
|
|
7634
7672
|
addCaptionsToTimeline,
|
|
7635
7673
|
getCaptionstatus
|
|
7636
7674
|
};
|
|
@@ -7739,12 +7777,14 @@ function TwickStudio({ studioConfig }) {
|
|
|
7739
7777
|
const useGenerateCaptions = (studioConfig) => {
|
|
7740
7778
|
var _a;
|
|
7741
7779
|
const { editor, present } = timeline.useTimelineContext();
|
|
7742
|
-
const onGenerateCaptions = async (videoElement) => {
|
|
7780
|
+
const onGenerateCaptions = async (videoElement, language, phraseLength) => {
|
|
7743
7781
|
if (studioConfig == null ? void 0 : studioConfig.captionGenerationService) {
|
|
7744
7782
|
const service = studioConfig.captionGenerationService;
|
|
7745
7783
|
const reqId = await service.generateCaptions(
|
|
7746
7784
|
videoElement,
|
|
7747
|
-
present
|
|
7785
|
+
present,
|
|
7786
|
+
language,
|
|
7787
|
+
phraseLength
|
|
7748
7788
|
);
|
|
7749
7789
|
return reqId;
|
|
7750
7790
|
}
|