@stellartech/voice-widget-directus 1.0.0 → 1.0.1
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/index.js +440 -290
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -547,7 +547,8 @@ function useVoicingApi(api) {
|
|
|
547
547
|
texts: [SAMPLE_TEXT],
|
|
548
548
|
provider,
|
|
549
549
|
preprocessing: false,
|
|
550
|
-
title: `Voice Sample - ${voiceId}
|
|
550
|
+
title: `Voice Sample - ${voiceId}`,
|
|
551
|
+
audio_files_collection: "AudioFiles"
|
|
551
552
|
};
|
|
552
553
|
if (provider === "gemini") {
|
|
553
554
|
voicingPayload.speakers = [{ voice: voiceId, name: "Sample", style: "neutral" }];
|
|
@@ -557,14 +558,28 @@ function useVoicingApi(api) {
|
|
|
557
558
|
voicingPayload.voice_id = voiceId;
|
|
558
559
|
}
|
|
559
560
|
try {
|
|
561
|
+
console.log("[Voice Widget] Calling flow:", effectiveFlowId);
|
|
560
562
|
const response = await api.post(`/flows/trigger/${effectiveFlowId}`, voicingPayload);
|
|
563
|
+
console.log("[Voice Widget] Full response:", JSON.stringify(response.data, null, 2));
|
|
561
564
|
const flowResponse = response.data;
|
|
562
|
-
|
|
565
|
+
let audioFileId = null;
|
|
566
|
+
if (flowResponse?.data?.audio_file_id) {
|
|
567
|
+
audioFileId = flowResponse.data.audio_file_id;
|
|
568
|
+
} else if (flowResponse?.audio_file_id) {
|
|
569
|
+
audioFileId = flowResponse.audio_file_id;
|
|
570
|
+
} else if (typeof flowResponse === "string") {
|
|
571
|
+
try {
|
|
572
|
+
const parsed = JSON.parse(flowResponse);
|
|
573
|
+
audioFileId = parsed?.data?.audio_file_id || parsed?.audio_file_id;
|
|
574
|
+
} catch {
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
console.log("[Voice Widget] Extracted audioFileId:", audioFileId);
|
|
563
578
|
if (audioFileId) {
|
|
564
579
|
const url = await resolveAudioFileUrl(audioFileId);
|
|
565
580
|
return { url, audioFileId };
|
|
566
581
|
}
|
|
567
|
-
throw new Error("No audio file returned from voice generation");
|
|
582
|
+
throw new Error("No audio file returned from voice generation. Response: " + JSON.stringify(flowResponse));
|
|
568
583
|
} catch (e) {
|
|
569
584
|
console.error("Failed to generate voice sample:", e);
|
|
570
585
|
throw new Error(e.response?.data?.detail || e.message || "Failed to generate sample");
|
|
@@ -576,10 +591,11 @@ function useVoicingApi(api) {
|
|
|
576
591
|
if (!effectiveFlowId) {
|
|
577
592
|
throw new Error("Voice flow ID is not configured. Set it in Widget Config or in the Flow ID field.");
|
|
578
593
|
}
|
|
594
|
+
const collection = request.collection || "SM_Lessons";
|
|
579
595
|
let texts = [];
|
|
580
596
|
let lessonTitle = `Lesson ${request.lessonId}`;
|
|
581
597
|
try {
|
|
582
|
-
const lessonResponse = await api.get(`/items
|
|
598
|
+
const lessonResponse = await api.get(`/items/${collection}/${request.lessonId}`, {
|
|
583
599
|
params: { fields: ["text", "title"] }
|
|
584
600
|
});
|
|
585
601
|
const textContent = lessonResponse.data.data?.text || "";
|
|
@@ -596,11 +612,24 @@ function useVoicingApi(api) {
|
|
|
596
612
|
console.error("Failed to fetch lesson text content:", e);
|
|
597
613
|
throw new Error(e.message || "Failed to fetch lesson text content");
|
|
598
614
|
}
|
|
615
|
+
const voiceConfig = {
|
|
616
|
+
provider: request.provider,
|
|
617
|
+
voice_id: request.voiceId,
|
|
618
|
+
style: request.style,
|
|
619
|
+
preprocessing: request.preprocessing,
|
|
620
|
+
tone_id: request.toneId,
|
|
621
|
+
style_id: request.styleId
|
|
622
|
+
};
|
|
599
623
|
const voicingPayload = {
|
|
600
624
|
texts,
|
|
601
625
|
provider: request.provider,
|
|
602
626
|
preprocessing: request.preprocessing,
|
|
603
|
-
title: `Voiceover - ${lessonTitle}
|
|
627
|
+
title: `Voiceover - ${lessonTitle}`,
|
|
628
|
+
audio_files_collection: "AudioFiles",
|
|
629
|
+
// Pass these so the flow can include them in callback_data
|
|
630
|
+
lesson_id: request.lessonId,
|
|
631
|
+
collection,
|
|
632
|
+
voice_config: voiceConfig
|
|
604
633
|
};
|
|
605
634
|
if (request.provider === "gemini") {
|
|
606
635
|
voicingPayload.speakers = [{
|
|
@@ -617,43 +646,20 @@ function useVoicingApi(api) {
|
|
|
617
646
|
const response = await api.post(`/flows/trigger/${effectiveFlowId}`, voicingPayload);
|
|
618
647
|
const flowResponse = response.data;
|
|
619
648
|
const data = flowResponse?.data || flowResponse;
|
|
620
|
-
let
|
|
621
|
-
if (data?.
|
|
649
|
+
let jobId = null;
|
|
650
|
+
if (data?.callback_data) {
|
|
622
651
|
try {
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
fileUuid = audioFilesResponse.data.data?.file || null;
|
|
627
|
-
} catch (e) {
|
|
628
|
-
console.warn("[Voice Widget] Could not resolve file UUID:", e);
|
|
629
|
-
}
|
|
630
|
-
if (fileUuid) {
|
|
631
|
-
try {
|
|
632
|
-
await api.post("/items/VoiceVariants", {
|
|
633
|
-
lesson_id: request.lessonId,
|
|
634
|
-
audio_file_id: fileUuid,
|
|
635
|
-
voice_config: {
|
|
636
|
-
provider: request.provider,
|
|
637
|
-
voice_id: request.voiceId,
|
|
638
|
-
style: request.style,
|
|
639
|
-
preprocessing: request.preprocessing,
|
|
640
|
-
tone_id: request.toneId,
|
|
641
|
-
style_id: request.styleId
|
|
642
|
-
},
|
|
643
|
-
callback_data: data.callback_data || null,
|
|
644
|
-
status: "published"
|
|
645
|
-
});
|
|
646
|
-
console.log("[Voice Widget] Saved voice variant with file UUID:", fileUuid);
|
|
647
|
-
} catch (saveError) {
|
|
648
|
-
console.warn("Failed to save voice variant:", saveError);
|
|
649
|
-
}
|
|
652
|
+
const callbackData = JSON.parse(data.callback_data);
|
|
653
|
+
jobId = callbackData.job_id || null;
|
|
654
|
+
} catch {
|
|
650
655
|
}
|
|
651
656
|
}
|
|
652
657
|
return {
|
|
653
|
-
audio_file_id:
|
|
654
|
-
status: data?.status ||
|
|
658
|
+
audio_file_id: data?.audio_file_id || null,
|
|
659
|
+
status: data?.status || "processing",
|
|
655
660
|
callback_data: data?.callback_data || null,
|
|
656
|
-
generation_time: data?.generation_time || null
|
|
661
|
+
generation_time: data?.generation_time || null,
|
|
662
|
+
job_id: jobId
|
|
657
663
|
};
|
|
658
664
|
} catch (e) {
|
|
659
665
|
console.error("Failed to generate voiceover:", e);
|
|
@@ -668,7 +674,8 @@ function useVoicingApi(api) {
|
|
|
668
674
|
const response = await api.get(`/items/AudioFiles/${audioFilesRecordId}`, {
|
|
669
675
|
params: { fields: ["file"] }
|
|
670
676
|
});
|
|
671
|
-
const
|
|
677
|
+
const fileField = response.data.data?.file;
|
|
678
|
+
const fileId = typeof fileField === "string" ? fileField : fileField?.id;
|
|
672
679
|
if (fileId) {
|
|
673
680
|
return `/assets/${fileId}`;
|
|
674
681
|
}
|
|
@@ -694,14 +701,18 @@ function useVoicingApi(api) {
|
|
|
694
701
|
}
|
|
695
702
|
async function updateVoiceSampleFile(voiceId, audioFilesRecordId, collection = "Voices") {
|
|
696
703
|
try {
|
|
704
|
+
console.log(`[Voice Widget] updateVoiceSampleFile called with voiceId=${voiceId}, audioFilesRecordId=${audioFilesRecordId}, collection=${collection}`);
|
|
697
705
|
const response = await api.get(`/items/AudioFiles/${audioFilesRecordId}`, {
|
|
698
706
|
params: { fields: ["file"] }
|
|
699
707
|
});
|
|
700
|
-
|
|
708
|
+
console.log("[Voice Widget] AudioFiles response:", response.data);
|
|
709
|
+
const fileField = response.data.data?.file;
|
|
710
|
+
const fileUuid = typeof fileField === "string" ? fileField : fileField?.id;
|
|
701
711
|
if (!fileUuid) {
|
|
702
|
-
console.warn("[Voice Widget] Could not get file UUID from AudioFiles record");
|
|
712
|
+
console.warn("[Voice Widget] Could not get file UUID from AudioFiles record. fileField:", fileField);
|
|
703
713
|
return;
|
|
704
714
|
}
|
|
715
|
+
console.log(`[Voice Widget] Patching ${collection}/${voiceId} with sample_file=${fileUuid}`);
|
|
705
716
|
await api.patch(`/items/${collection}/${voiceId}`, {
|
|
706
717
|
sample_file: fileUuid
|
|
707
718
|
});
|
|
@@ -710,6 +721,66 @@ function useVoicingApi(api) {
|
|
|
710
721
|
console.error("[Voice Widget] Failed to update voice sample_file:", error);
|
|
711
722
|
}
|
|
712
723
|
}
|
|
724
|
+
async function deleteVoiceVariant(variantId) {
|
|
725
|
+
try {
|
|
726
|
+
await api.delete(`/items/VoiceVariants/${variantId}`);
|
|
727
|
+
console.log(`[Voice Widget] Deleted voice variant: ${variantId}`);
|
|
728
|
+
} catch (error) {
|
|
729
|
+
console.error("[Voice Widget] Failed to delete voice variant:", error);
|
|
730
|
+
throw error;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
async function createVoiceVariant(lessonId, audioFileId, voiceConfig) {
|
|
734
|
+
try {
|
|
735
|
+
const response = await api.post("/items/VoiceVariants", {
|
|
736
|
+
lesson_id: lessonId,
|
|
737
|
+
audio_file_id: audioFileId,
|
|
738
|
+
voice_config: voiceConfig,
|
|
739
|
+
status: "draft"
|
|
740
|
+
});
|
|
741
|
+
const variantId = response.data.data?.id;
|
|
742
|
+
console.log(`[Voice Widget] Created VoiceVariant: ${variantId}`);
|
|
743
|
+
return variantId;
|
|
744
|
+
} catch (error) {
|
|
745
|
+
console.error("[Voice Widget] Failed to create VoiceVariant:", error);
|
|
746
|
+
throw error;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
async function linkAudioToLesson(lessonId, audioFileId, collection) {
|
|
750
|
+
try {
|
|
751
|
+
await api.patch(`/items/${collection}/${lessonId}`, {
|
|
752
|
+
audio: audioFileId
|
|
753
|
+
});
|
|
754
|
+
console.log(`[Voice Widget] Linked audio ${audioFileId} to lesson ${lessonId} in ${collection}`);
|
|
755
|
+
} catch (error) {
|
|
756
|
+
console.error("[Voice Widget] Failed to link audio to lesson:", error);
|
|
757
|
+
throw error;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
async function pollVoicingJob(jobId) {
|
|
761
|
+
try {
|
|
762
|
+
const response = await api.get("/items/VoicingJobs", {
|
|
763
|
+
params: {
|
|
764
|
+
filter: { job_id: { _eq: jobId } },
|
|
765
|
+
fields: ["status", "progress", "message", "audio_file_id"],
|
|
766
|
+
limit: 1
|
|
767
|
+
}
|
|
768
|
+
});
|
|
769
|
+
const job = response.data.data?.[0];
|
|
770
|
+
if (job) {
|
|
771
|
+
return {
|
|
772
|
+
status: job.status,
|
|
773
|
+
progress: job.progress,
|
|
774
|
+
message: job.message,
|
|
775
|
+
audio_file_id: job.audio_file_id
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
return null;
|
|
779
|
+
} catch (error) {
|
|
780
|
+
console.error("[Voice Widget] Failed to poll voicing job:", error);
|
|
781
|
+
return null;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
713
784
|
return {
|
|
714
785
|
fetchVoices,
|
|
715
786
|
fetchFlowId,
|
|
@@ -720,7 +791,11 @@ function useVoicingApi(api) {
|
|
|
720
791
|
getAudioUrl,
|
|
721
792
|
resolveAudioFileUrl,
|
|
722
793
|
fetchVoiceVariants,
|
|
723
|
-
updateVoiceSampleFile
|
|
794
|
+
updateVoiceSampleFile,
|
|
795
|
+
deleteVoiceVariant,
|
|
796
|
+
createVoiceVariant,
|
|
797
|
+
linkAudioToLesson,
|
|
798
|
+
pollVoicingJob
|
|
724
799
|
};
|
|
725
800
|
}
|
|
726
801
|
|
|
@@ -733,49 +808,56 @@ const _hoisted_3 = { class: "widget__progress" };
|
|
|
733
808
|
const _hoisted_4 = { class: "widget__progress-bar" };
|
|
734
809
|
const _hoisted_5 = {
|
|
735
810
|
key: 0,
|
|
811
|
+
class: "widget__progress-status"
|
|
812
|
+
};
|
|
813
|
+
const _hoisted_6 = {
|
|
814
|
+
key: 1,
|
|
736
815
|
class: "widget__error"
|
|
737
816
|
};
|
|
738
|
-
const
|
|
739
|
-
const
|
|
740
|
-
const
|
|
741
|
-
const
|
|
742
|
-
const
|
|
743
|
-
const
|
|
744
|
-
const
|
|
745
|
-
const
|
|
746
|
-
const
|
|
747
|
-
const
|
|
748
|
-
const
|
|
749
|
-
const
|
|
750
|
-
const
|
|
751
|
-
const
|
|
752
|
-
const
|
|
753
|
-
const _hoisted_21 = { class: "widget__section" };
|
|
817
|
+
const _hoisted_7 = { class: "widget__loading" };
|
|
818
|
+
const _hoisted_8 = { class: "widget__error" };
|
|
819
|
+
const _hoisted_9 = { class: "widget__header" };
|
|
820
|
+
const _hoisted_10 = { class: "widget__header-row" };
|
|
821
|
+
const _hoisted_11 = { class: "widget__header-text" };
|
|
822
|
+
const _hoisted_12 = { class: "widget__title" };
|
|
823
|
+
const _hoisted_13 = ["title"];
|
|
824
|
+
const _hoisted_14 = { class: "widget__header-controls" };
|
|
825
|
+
const _hoisted_15 = { class: "widget__url-input" };
|
|
826
|
+
const _hoisted_16 = ["disabled"];
|
|
827
|
+
const _hoisted_17 = { class: "widget__section" };
|
|
828
|
+
const _hoisted_18 = { class: "widget__model-buttons" };
|
|
829
|
+
const _hoisted_19 = ["onClick"];
|
|
830
|
+
const _hoisted_20 = { class: "widget__section" };
|
|
831
|
+
const _hoisted_21 = { class: "widget__voice-list" };
|
|
754
832
|
const _hoisted_22 = { class: "widget__section" };
|
|
755
|
-
const _hoisted_23 = { class: "widget__section
|
|
756
|
-
const _hoisted_24 = { class: "
|
|
757
|
-
const _hoisted_25 = { class: "
|
|
758
|
-
const _hoisted_26 = { class: "widget__footer
|
|
759
|
-
const _hoisted_27 =
|
|
833
|
+
const _hoisted_23 = { class: "widget__section" };
|
|
834
|
+
const _hoisted_24 = { class: "widget__section widget__section--toggle" };
|
|
835
|
+
const _hoisted_25 = { class: "widget__toggle" };
|
|
836
|
+
const _hoisted_26 = { class: "widget__footer" };
|
|
837
|
+
const _hoisted_27 = { class: "widget__footer-right" };
|
|
760
838
|
const _hoisted_28 = ["disabled"];
|
|
761
|
-
const _hoisted_29 =
|
|
762
|
-
const _hoisted_30 = { class: "widget__header
|
|
763
|
-
const _hoisted_31 = { class: "widget__header-
|
|
764
|
-
const _hoisted_32 = { class: "
|
|
765
|
-
const _hoisted_33 = { class: "
|
|
766
|
-
const _hoisted_34 = {
|
|
839
|
+
const _hoisted_29 = ["disabled"];
|
|
840
|
+
const _hoisted_30 = { class: "widget__header" };
|
|
841
|
+
const _hoisted_31 = { class: "widget__header-row" };
|
|
842
|
+
const _hoisted_32 = { class: "widget__header-text" };
|
|
843
|
+
const _hoisted_33 = { class: "widget__title" };
|
|
844
|
+
const _hoisted_34 = { class: "widget__subtitle" };
|
|
845
|
+
const _hoisted_35 = { class: "widget__variants-list" };
|
|
846
|
+
const _hoisted_36 = ["onClick"];
|
|
847
|
+
const _hoisted_37 = { class: "widget__variant-radio" };
|
|
848
|
+
const _hoisted_38 = ["checked", "onChange"];
|
|
849
|
+
const _hoisted_39 = { class: "widget__variant-player" };
|
|
850
|
+
const _hoisted_40 = { class: "widget__variant-meta" };
|
|
851
|
+
const _hoisted_41 = { class: "widget__variant-voice" };
|
|
852
|
+
const _hoisted_42 = { class: "widget__variant-date" };
|
|
853
|
+
const _hoisted_43 = ["onClick"];
|
|
854
|
+
const _hoisted_44 = {
|
|
767
855
|
key: 0,
|
|
768
|
-
class: "
|
|
769
|
-
};
|
|
770
|
-
const _hoisted_35 = ["disabled"];
|
|
771
|
-
const _hoisted_36 = { class: "widget__variant-counter" };
|
|
772
|
-
const _hoisted_37 = ["disabled"];
|
|
773
|
-
const _hoisted_38 = { class: "widget__result" };
|
|
774
|
-
const _hoisted_39 = { class: "widget__result-info" };
|
|
775
|
-
const _hoisted_40 = {
|
|
776
|
-
key: 0,
|
|
777
|
-
class: "widget__result-date"
|
|
856
|
+
class: "widget__selected-info"
|
|
778
857
|
};
|
|
858
|
+
const _hoisted_45 = { class: "widget__footer" };
|
|
859
|
+
const _hoisted_46 = { class: "widget__footer-right" };
|
|
860
|
+
const _hoisted_47 = ["disabled"];
|
|
779
861
|
var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
780
862
|
__name: "interface",
|
|
781
863
|
props: {
|
|
@@ -804,7 +886,11 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
804
886
|
getAudioUrl,
|
|
805
887
|
resolveAudioFileUrl,
|
|
806
888
|
fetchVoiceVariants,
|
|
807
|
-
updateVoiceSampleFile
|
|
889
|
+
updateVoiceSampleFile,
|
|
890
|
+
deleteVoiceVariant,
|
|
891
|
+
createVoiceVariant,
|
|
892
|
+
linkAudioToLesson,
|
|
893
|
+
pollVoicingJob
|
|
808
894
|
} = useVoicingApi(api);
|
|
809
895
|
const currentMode = ref("selection");
|
|
810
896
|
const loading = ref(true);
|
|
@@ -832,13 +918,17 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
832
918
|
const hasExistingVoices = ref(false);
|
|
833
919
|
const allVariants = ref([]);
|
|
834
920
|
const currentVariantIndex = ref(0);
|
|
921
|
+
const currentJobId = ref(null);
|
|
922
|
+
const isPollingProgress = ref(false);
|
|
923
|
+
const progressStatus = ref("");
|
|
924
|
+
const selectedVariant = computed(() => allVariants.value[currentVariantIndex.value]);
|
|
835
925
|
const currentVoices = computed(() => {
|
|
836
926
|
return voices.value.filter((v) => v.provider === selectedModel.value);
|
|
837
927
|
});
|
|
838
928
|
const canGenerate = computed(() => {
|
|
839
929
|
return selectedVoiceId.value !== null && selectedToneId.value !== null && selectedStyleId.value !== null;
|
|
840
930
|
});
|
|
841
|
-
|
|
931
|
+
computed(() => {
|
|
842
932
|
const variant = allVariants.value[currentVariantIndex.value];
|
|
843
933
|
if (!variant?.date_created) return null;
|
|
844
934
|
return new Date(variant.date_created).toLocaleString();
|
|
@@ -906,15 +996,15 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
906
996
|
if (!canGenerate.value) return;
|
|
907
997
|
currentMode.value = "progress";
|
|
908
998
|
errorMessage.value = null;
|
|
909
|
-
processingMessage.value = "
|
|
910
|
-
progressPercent.value =
|
|
999
|
+
processingMessage.value = "Starting generation...";
|
|
1000
|
+
progressPercent.value = 5;
|
|
1001
|
+
progressStatus.value = "";
|
|
911
1002
|
try {
|
|
912
1003
|
const tonePrompt = getSelectedTonePrompt();
|
|
913
1004
|
const stylePrompt = getSelectedStylePrompt();
|
|
914
1005
|
const combinedStyle = `${tonePrompt}. ${stylePrompt}`.trim();
|
|
915
1006
|
const selectedVoice = voices.value.find((v) => v.id === selectedVoiceId.value);
|
|
916
1007
|
const providerVoiceId = selectedVoice?.voice_id || selectedVoiceId.value;
|
|
917
|
-
progressPercent.value = 30;
|
|
918
1008
|
const result = await generateFullVoiceover({
|
|
919
1009
|
provider: selectedModel.value,
|
|
920
1010
|
voiceId: providerVoiceId,
|
|
@@ -922,25 +1012,41 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
922
1012
|
preprocessing: preprocessingEnabled.value,
|
|
923
1013
|
flowId: flowId.value,
|
|
924
1014
|
lessonId: props.primaryKey,
|
|
1015
|
+
collection: props.collection || "SM_Lessons",
|
|
925
1016
|
toneId: selectedToneId.value || void 0,
|
|
926
1017
|
styleId: selectedStyleId.value || void 0
|
|
927
1018
|
});
|
|
928
|
-
|
|
929
|
-
|
|
1019
|
+
const voiceConfig = {
|
|
1020
|
+
provider: selectedModel.value,
|
|
1021
|
+
voice_id: providerVoiceId,
|
|
1022
|
+
style: combinedStyle,
|
|
1023
|
+
tone_id: selectedToneId.value,
|
|
1024
|
+
style_id: selectedStyleId.value
|
|
1025
|
+
};
|
|
1026
|
+
if (result.job_id) {
|
|
1027
|
+
processingMessage.value = "Processing in background...";
|
|
1028
|
+
await startProgressPolling(result.job_id, voiceConfig);
|
|
1029
|
+
} else if (result.audio_file_id) {
|
|
930
1030
|
generatedAudioId.value = result.audio_file_id;
|
|
931
1031
|
generatedAudioUrl.value = getAudioUrl(result.audio_file_id);
|
|
932
1032
|
progressPercent.value = 100;
|
|
1033
|
+
await createVoiceVariant(
|
|
1034
|
+
props.primaryKey,
|
|
1035
|
+
result.audio_file_id,
|
|
1036
|
+
voiceConfig
|
|
1037
|
+
);
|
|
1038
|
+
await refreshVariants();
|
|
933
1039
|
currentMode.value = "result";
|
|
934
1040
|
} else if (result.status === "processing" && result.callback_data) {
|
|
935
1041
|
processingMessage.value = "Processing in background...";
|
|
936
|
-
await pollForCompletion(result.callback_data);
|
|
1042
|
+
await pollForCompletion(result.callback_data, voiceConfig);
|
|
937
1043
|
}
|
|
938
1044
|
} catch (error) {
|
|
939
1045
|
errorMessage.value = error.message || "Failed to generate voiceover";
|
|
940
1046
|
progressPercent.value = 0;
|
|
941
1047
|
}
|
|
942
1048
|
}
|
|
943
|
-
async function pollForCompletion(callbackData) {
|
|
1049
|
+
async function pollForCompletion(callbackData, voiceConfig) {
|
|
944
1050
|
const maxAttempts = 60;
|
|
945
1051
|
const pollInterval = 5e3;
|
|
946
1052
|
for (let i = 0; i < maxAttempts; i++) {
|
|
@@ -970,18 +1076,76 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
970
1076
|
currentMode.value = "selection";
|
|
971
1077
|
errorMessage.value = null;
|
|
972
1078
|
progressPercent.value = 0;
|
|
1079
|
+
progressStatus.value = "";
|
|
1080
|
+
isPollingProgress.value = false;
|
|
1081
|
+
}
|
|
1082
|
+
function selectVariant(index) {
|
|
1083
|
+
currentVariantIndex.value = index;
|
|
1084
|
+
loadVariantAtIndex(index);
|
|
973
1085
|
}
|
|
974
|
-
async function
|
|
975
|
-
if (
|
|
976
|
-
|
|
977
|
-
await
|
|
1086
|
+
async function deleteVariant(variantId) {
|
|
1087
|
+
if (!confirm("Delete this voiceover?")) return;
|
|
1088
|
+
try {
|
|
1089
|
+
await deleteVoiceVariant(variantId);
|
|
1090
|
+
allVariants.value = allVariants.value.filter((v) => v.id !== variantId);
|
|
1091
|
+
if (currentVariantIndex.value >= allVariants.value.length) {
|
|
1092
|
+
currentVariantIndex.value = Math.max(0, allVariants.value.length - 1);
|
|
1093
|
+
}
|
|
1094
|
+
if (allVariants.value.length === 0) {
|
|
1095
|
+
hasExistingVoices.value = false;
|
|
1096
|
+
goBackToSelection();
|
|
1097
|
+
}
|
|
1098
|
+
} catch (error) {
|
|
1099
|
+
console.error("Failed to delete variant:", error);
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
async function startProgressPolling(jobId, voiceConfig) {
|
|
1103
|
+
currentJobId.value = jobId;
|
|
1104
|
+
isPollingProgress.value = true;
|
|
1105
|
+
while (isPollingProgress.value) {
|
|
1106
|
+
const job = await pollVoicingJob(jobId);
|
|
1107
|
+
if (job) {
|
|
1108
|
+
progressPercent.value = job.progress;
|
|
1109
|
+
processingMessage.value = job.message;
|
|
1110
|
+
progressStatus.value = job.status;
|
|
1111
|
+
if (job.status === "completed") {
|
|
1112
|
+
isPollingProgress.value = false;
|
|
1113
|
+
if (job.audio_file_id) {
|
|
1114
|
+
await createVoiceVariant(
|
|
1115
|
+
props.primaryKey,
|
|
1116
|
+
job.audio_file_id,
|
|
1117
|
+
voiceConfig
|
|
1118
|
+
);
|
|
1119
|
+
}
|
|
1120
|
+
await refreshVariants();
|
|
1121
|
+
currentMode.value = "result";
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
if (job.status.startsWith("failed")) {
|
|
1125
|
+
isPollingProgress.value = false;
|
|
1126
|
+
errorMessage.value = job.message || "Generation failed";
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
978
1131
|
}
|
|
979
1132
|
}
|
|
980
|
-
async function
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
1133
|
+
async function refreshVariants() {
|
|
1134
|
+
const variants = await fetchVoiceVariants(props.primaryKey);
|
|
1135
|
+
allVariants.value = variants;
|
|
1136
|
+
for (const v of allVariants.value) {
|
|
1137
|
+
const isUuid = v.audio_file_id?.includes("-");
|
|
1138
|
+
v.audioUrl = isUuid ? getAudioUrl(v.audio_file_id) : await resolveAudioFileUrl(v.audio_file_id);
|
|
984
1139
|
}
|
|
1140
|
+
currentVariantIndex.value = 0;
|
|
1141
|
+
hasExistingVoices.value = variants.length > 0;
|
|
1142
|
+
}
|
|
1143
|
+
function formatVariantDate(dateStr) {
|
|
1144
|
+
if (!dateStr) return "";
|
|
1145
|
+
return new Date(dateStr).toLocaleString();
|
|
1146
|
+
}
|
|
1147
|
+
function getVoiceNameFromConfig(config) {
|
|
1148
|
+
return config?.voice_id || "Unknown";
|
|
985
1149
|
}
|
|
986
1150
|
async function loadVariantAtIndex(index) {
|
|
987
1151
|
const variant = allVariants.value[index];
|
|
@@ -1015,25 +1179,41 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1015
1179
|
function regenerateVoiceover() {
|
|
1016
1180
|
generateVoiceover();
|
|
1017
1181
|
}
|
|
1018
|
-
function confirmVoiceover() {
|
|
1019
|
-
const
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1182
|
+
async function confirmVoiceover() {
|
|
1183
|
+
const variant = selectedVariant.value;
|
|
1184
|
+
if (!variant) return;
|
|
1185
|
+
try {
|
|
1186
|
+
await linkAudioToLesson(
|
|
1187
|
+
props.primaryKey,
|
|
1188
|
+
variant.audio_file_id,
|
|
1189
|
+
props.collection || "SM_Lessons"
|
|
1190
|
+
);
|
|
1191
|
+
const value = {
|
|
1192
|
+
audio_file_id: variant.audio_file_id,
|
|
1193
|
+
model: variant.voice_config?.provider || selectedModel.value,
|
|
1194
|
+
voice_id: variant.voice_config?.voice_id || selectedVoiceId.value,
|
|
1195
|
+
tone_id: variant.voice_config?.tone_id || selectedToneId.value,
|
|
1196
|
+
style_id: variant.voice_config?.style_id || selectedStyleId.value,
|
|
1197
|
+
custom_tone: customTone.value,
|
|
1198
|
+
custom_style: customStyle.value,
|
|
1199
|
+
preprocessing: variant.voice_config?.preprocessing ?? preprocessingEnabled.value,
|
|
1200
|
+
confirmed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1201
|
+
};
|
|
1202
|
+
emit("input", value);
|
|
1203
|
+
goBackToSelection();
|
|
1204
|
+
} catch (error) {
|
|
1205
|
+
console.error("Failed to link audio:", error);
|
|
1206
|
+
}
|
|
1031
1207
|
}
|
|
1032
1208
|
async function viewGeneratedVoices() {
|
|
1033
1209
|
try {
|
|
1034
1210
|
const variants = await fetchVoiceVariants(props.primaryKey);
|
|
1035
1211
|
if (variants.length > 0) {
|
|
1036
1212
|
allVariants.value = variants;
|
|
1213
|
+
for (const v of allVariants.value) {
|
|
1214
|
+
const isUuid = v.audio_file_id?.includes("-");
|
|
1215
|
+
v.audioUrl = isUuid ? getAudioUrl(v.audio_file_id) : await resolveAudioFileUrl(v.audio_file_id);
|
|
1216
|
+
}
|
|
1037
1217
|
currentVariantIndex.value = 0;
|
|
1038
1218
|
await loadVariantAtIndex(0);
|
|
1039
1219
|
currentMode.value = "result";
|
|
@@ -1042,27 +1222,6 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1042
1222
|
console.error("Failed to fetch voice variants:", error);
|
|
1043
1223
|
}
|
|
1044
1224
|
}
|
|
1045
|
-
function getModelName(modelId) {
|
|
1046
|
-
const model = VOICE_MODELS.find((m) => m.id === modelId);
|
|
1047
|
-
return model?.name || modelId;
|
|
1048
|
-
}
|
|
1049
|
-
function getVoiceName(voiceId) {
|
|
1050
|
-
if (!voiceId) return "Not selected";
|
|
1051
|
-
const voice = voices.value.find((v) => v.id === voiceId);
|
|
1052
|
-
return voice?.name || voiceId;
|
|
1053
|
-
}
|
|
1054
|
-
function getToneName(toneId) {
|
|
1055
|
-
if (!toneId) return "Not selected";
|
|
1056
|
-
if (toneId === "other") return customTone.value || "Custom";
|
|
1057
|
-
const tone = tones.value.find((t) => t.id === toneId);
|
|
1058
|
-
return tone?.name || toneId;
|
|
1059
|
-
}
|
|
1060
|
-
function getStyleName(styleId) {
|
|
1061
|
-
if (!styleId) return "Not selected";
|
|
1062
|
-
if (styleId === "other") return customStyle.value || "Custom";
|
|
1063
|
-
const style = styles.value.find((s) => s.id === styleId);
|
|
1064
|
-
return style?.name || styleId;
|
|
1065
|
-
}
|
|
1066
1225
|
async function initialize() {
|
|
1067
1226
|
loading.value = true;
|
|
1068
1227
|
initError.value = null;
|
|
@@ -1172,7 +1331,14 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1172
1331
|
/* STYLE */
|
|
1173
1332
|
)
|
|
1174
1333
|
]),
|
|
1175
|
-
errorMessage.value ? (openBlock(), createElementBlock(
|
|
1334
|
+
progressStatus.value && !errorMessage.value ? (openBlock(), createElementBlock(
|
|
1335
|
+
"p",
|
|
1336
|
+
_hoisted_5,
|
|
1337
|
+
" Status: " + toDisplayString(progressStatus.value),
|
|
1338
|
+
1
|
|
1339
|
+
/* TEXT */
|
|
1340
|
+
)) : createCommentVNode("v-if", true),
|
|
1341
|
+
errorMessage.value ? (openBlock(), createElementBlock("div", _hoisted_6, [
|
|
1176
1342
|
createVNode(_component_v_icon, { name: "error" }),
|
|
1177
1343
|
createElementVNode(
|
|
1178
1344
|
"span",
|
|
@@ -1197,7 +1363,7 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1197
1363
|
{ key: 1 },
|
|
1198
1364
|
[
|
|
1199
1365
|
createCommentVNode(" Loading State "),
|
|
1200
|
-
createElementVNode("div",
|
|
1366
|
+
createElementVNode("div", _hoisted_7, [
|
|
1201
1367
|
createVNode(_component_v_icon, {
|
|
1202
1368
|
name: "refresh",
|
|
1203
1369
|
class: "spinning"
|
|
@@ -1218,7 +1384,7 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1218
1384
|
{ key: 2 },
|
|
1219
1385
|
[
|
|
1220
1386
|
createCommentVNode(" Error State "),
|
|
1221
|
-
createElementVNode("div",
|
|
1387
|
+
createElementVNode("div", _hoisted_8, [
|
|
1222
1388
|
createVNode(_component_v_icon, { name: "error" }),
|
|
1223
1389
|
createElementVNode(
|
|
1224
1390
|
"span",
|
|
@@ -1240,10 +1406,10 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1240
1406
|
{ key: 3 },
|
|
1241
1407
|
[
|
|
1242
1408
|
createCommentVNode(" Page 1: Selection Mode "),
|
|
1243
|
-
createElementVNode("div",
|
|
1244
|
-
createElementVNode("div",
|
|
1245
|
-
createElementVNode("div",
|
|
1246
|
-
createElementVNode("h2",
|
|
1409
|
+
createElementVNode("div", _hoisted_9, [
|
|
1410
|
+
createElementVNode("div", _hoisted_10, [
|
|
1411
|
+
createElementVNode("div", _hoisted_11, [
|
|
1412
|
+
createElementVNode("h2", _hoisted_12, [
|
|
1247
1413
|
createVNode(_component_v_icon, { name: "mic" }),
|
|
1248
1414
|
_cache[4] || (_cache[4] = createTextVNode(
|
|
1249
1415
|
" Voice Generation ",
|
|
@@ -1260,7 +1426,7 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1260
1426
|
name: headerExpanded.value ? "expand_less" : "expand_more",
|
|
1261
1427
|
small: ""
|
|
1262
1428
|
}, null, 8, ["name"])
|
|
1263
|
-
], 8,
|
|
1429
|
+
], 8, _hoisted_13)
|
|
1264
1430
|
]),
|
|
1265
1431
|
_cache[5] || (_cache[5] = createElementVNode(
|
|
1266
1432
|
"p",
|
|
@@ -1272,9 +1438,9 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1272
1438
|
]),
|
|
1273
1439
|
withDirectives(createElementVNode(
|
|
1274
1440
|
"div",
|
|
1275
|
-
|
|
1441
|
+
_hoisted_14,
|
|
1276
1442
|
[
|
|
1277
|
-
createElementVNode("div",
|
|
1443
|
+
createElementVNode("div", _hoisted_15, [
|
|
1278
1444
|
_cache[6] || (_cache[6] = createElementVNode(
|
|
1279
1445
|
"label",
|
|
1280
1446
|
{ class: "widget__url-label" },
|
|
@@ -1287,7 +1453,7 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1287
1453
|
class: "widget__url-field",
|
|
1288
1454
|
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => flowId.value = $event),
|
|
1289
1455
|
disabled: savingUrl.value
|
|
1290
|
-
}, null, 8,
|
|
1456
|
+
}, null, 8, _hoisted_16), [
|
|
1291
1457
|
[vModelText, flowId.value]
|
|
1292
1458
|
])
|
|
1293
1459
|
])
|
|
@@ -1300,7 +1466,7 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1300
1466
|
])
|
|
1301
1467
|
]),
|
|
1302
1468
|
createCommentVNode(" Model Selection "),
|
|
1303
|
-
createElementVNode("div",
|
|
1469
|
+
createElementVNode("div", _hoisted_17, [
|
|
1304
1470
|
_cache[7] || (_cache[7] = createElementVNode(
|
|
1305
1471
|
"label",
|
|
1306
1472
|
{ class: "widget__section-label" },
|
|
@@ -1308,7 +1474,7 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1308
1474
|
-1
|
|
1309
1475
|
/* CACHED */
|
|
1310
1476
|
)),
|
|
1311
|
-
createElementVNode("div",
|
|
1477
|
+
createElementVNode("div", _hoisted_18, [
|
|
1312
1478
|
(openBlock(true), createElementBlock(
|
|
1313
1479
|
Fragment,
|
|
1314
1480
|
null,
|
|
@@ -1317,7 +1483,7 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1317
1483
|
key: model.id,
|
|
1318
1484
|
class: normalizeClass(["widget__model-btn", { "widget__model-btn--active": selectedModel.value === model.id }]),
|
|
1319
1485
|
onClick: ($event) => selectModel(model.id)
|
|
1320
|
-
}, toDisplayString(model.name), 11,
|
|
1486
|
+
}, toDisplayString(model.name), 11, _hoisted_19);
|
|
1321
1487
|
}),
|
|
1322
1488
|
128
|
|
1323
1489
|
/* KEYED_FRAGMENT */
|
|
@@ -1325,7 +1491,7 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1325
1491
|
])
|
|
1326
1492
|
]),
|
|
1327
1493
|
createCommentVNode(" Voice Selection "),
|
|
1328
|
-
createElementVNode("div",
|
|
1494
|
+
createElementVNode("div", _hoisted_20, [
|
|
1329
1495
|
_cache[8] || (_cache[8] = createElementVNode(
|
|
1330
1496
|
"label",
|
|
1331
1497
|
{ class: "widget__section-label" },
|
|
@@ -1333,7 +1499,7 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1333
1499
|
-1
|
|
1334
1500
|
/* CACHED */
|
|
1335
1501
|
)),
|
|
1336
|
-
createElementVNode("div",
|
|
1502
|
+
createElementVNode("div", _hoisted_21, [
|
|
1337
1503
|
(openBlock(true), createElementBlock(
|
|
1338
1504
|
Fragment,
|
|
1339
1505
|
null,
|
|
@@ -1355,7 +1521,7 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1355
1521
|
])
|
|
1356
1522
|
]),
|
|
1357
1523
|
createCommentVNode(" Tone Selection "),
|
|
1358
|
-
createElementVNode("div",
|
|
1524
|
+
createElementVNode("div", _hoisted_22, [
|
|
1359
1525
|
_cache[9] || (_cache[9] = createElementVNode(
|
|
1360
1526
|
"label",
|
|
1361
1527
|
{ class: "widget__section-label" },
|
|
@@ -1373,7 +1539,7 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1373
1539
|
}, null, 8, ["items", "selected-id", "custom-value"])
|
|
1374
1540
|
]),
|
|
1375
1541
|
createCommentVNode(" Style Selection "),
|
|
1376
|
-
createElementVNode("div",
|
|
1542
|
+
createElementVNode("div", _hoisted_23, [
|
|
1377
1543
|
_cache[10] || (_cache[10] = createElementVNode(
|
|
1378
1544
|
"label",
|
|
1379
1545
|
{ class: "widget__section-label" },
|
|
@@ -1391,8 +1557,8 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1391
1557
|
}, null, 8, ["items", "selected-id", "custom-value"])
|
|
1392
1558
|
]),
|
|
1393
1559
|
createCommentVNode(" Preprocessing Toggle "),
|
|
1394
|
-
createElementVNode("div",
|
|
1395
|
-
createElementVNode("label",
|
|
1560
|
+
createElementVNode("div", _hoisted_24, [
|
|
1561
|
+
createElementVNode("label", _hoisted_25, [
|
|
1396
1562
|
withDirectives(createElementVNode(
|
|
1397
1563
|
"input",
|
|
1398
1564
|
{
|
|
@@ -1422,7 +1588,7 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1422
1588
|
))
|
|
1423
1589
|
]),
|
|
1424
1590
|
createCommentVNode(" Footer Actions "),
|
|
1425
|
-
createElementVNode("div",
|
|
1591
|
+
createElementVNode("div", _hoisted_26, [
|
|
1426
1592
|
_cache[13] || (_cache[13] = createElementVNode(
|
|
1427
1593
|
"div",
|
|
1428
1594
|
{ class: "widget__footer-left" },
|
|
@@ -1430,17 +1596,17 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1430
1596
|
-1
|
|
1431
1597
|
/* CACHED */
|
|
1432
1598
|
)),
|
|
1433
|
-
createElementVNode("div",
|
|
1599
|
+
createElementVNode("div", _hoisted_27, [
|
|
1434
1600
|
createElementVNode("button", {
|
|
1435
1601
|
class: "widget__btn widget__btn--secondary",
|
|
1436
1602
|
onClick: viewGeneratedVoices,
|
|
1437
1603
|
disabled: !hasExistingVoices.value
|
|
1438
|
-
}, " View Generated Voices ", 8,
|
|
1604
|
+
}, " View Generated Voices ", 8, _hoisted_28),
|
|
1439
1605
|
createElementVNode("button", {
|
|
1440
1606
|
class: "widget__btn widget__btn--primary",
|
|
1441
1607
|
onClick: generateVoiceover,
|
|
1442
1608
|
disabled: !canGenerate.value
|
|
1443
|
-
}, " Generate Voiceover ", 8,
|
|
1609
|
+
}, " Generate Voiceover ", 8, _hoisted_29)
|
|
1444
1610
|
])
|
|
1445
1611
|
])
|
|
1446
1612
|
],
|
|
@@ -1450,163 +1616,147 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1450
1616
|
Fragment,
|
|
1451
1617
|
{ key: 4 },
|
|
1452
1618
|
[
|
|
1453
|
-
createCommentVNode(" Page 3: Result Mode "),
|
|
1454
|
-
createElementVNode("div",
|
|
1455
|
-
createElementVNode("div",
|
|
1456
|
-
createElementVNode("div",
|
|
1457
|
-
createElementVNode("h2",
|
|
1619
|
+
createCommentVNode(" Page 3: Result Mode - List View "),
|
|
1620
|
+
createElementVNode("div", _hoisted_30, [
|
|
1621
|
+
createElementVNode("div", _hoisted_31, [
|
|
1622
|
+
createElementVNode("div", _hoisted_32, [
|
|
1623
|
+
createElementVNode("h2", _hoisted_33, [
|
|
1458
1624
|
createVNode(_component_v_icon, { name: "check_circle" }),
|
|
1459
1625
|
_cache[14] || (_cache[14] = createTextVNode(
|
|
1460
|
-
"
|
|
1626
|
+
" Generated Voiceovers ",
|
|
1461
1627
|
-1
|
|
1462
1628
|
/* CACHED */
|
|
1463
1629
|
))
|
|
1464
1630
|
]),
|
|
1465
|
-
createElementVNode(
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
[
|
|
1470
|
-
createTextVNode(
|
|
1471
|
-
" Viewing " + toDisplayString(currentVariantIndex.value + 1) + " of " + toDisplayString(allVariants.value.length) + " generated voiceovers ",
|
|
1472
|
-
1
|
|
1473
|
-
/* TEXT */
|
|
1474
|
-
)
|
|
1475
|
-
],
|
|
1476
|
-
64
|
|
1477
|
-
/* STABLE_FRAGMENT */
|
|
1478
|
-
)) : (openBlock(), createElementBlock(
|
|
1479
|
-
Fragment,
|
|
1480
|
-
{ key: 1 },
|
|
1481
|
-
[
|
|
1482
|
-
createTextVNode(" Listen to your generated voiceover and confirm or regenerate ")
|
|
1483
|
-
],
|
|
1484
|
-
64
|
|
1485
|
-
/* STABLE_FRAGMENT */
|
|
1486
|
-
))
|
|
1487
|
-
])
|
|
1488
|
-
])
|
|
1489
|
-
])
|
|
1490
|
-
]),
|
|
1491
|
-
createCommentVNode(" Navigation for multiple variants "),
|
|
1492
|
-
allVariants.value.length > 1 ? (openBlock(), createElementBlock("div", _hoisted_34, [
|
|
1493
|
-
createElementVNode("button", {
|
|
1494
|
-
class: "widget__btn widget__btn--icon",
|
|
1495
|
-
onClick: prevVariant,
|
|
1496
|
-
disabled: currentVariantIndex.value === 0
|
|
1497
|
-
}, [
|
|
1498
|
-
createVNode(_component_v_icon, { name: "chevron_left" })
|
|
1499
|
-
], 8, _hoisted_35),
|
|
1500
|
-
createElementVNode(
|
|
1501
|
-
"span",
|
|
1502
|
-
_hoisted_36,
|
|
1503
|
-
toDisplayString(currentVariantIndex.value + 1) + " / " + toDisplayString(allVariants.value.length),
|
|
1504
|
-
1
|
|
1505
|
-
/* TEXT */
|
|
1506
|
-
),
|
|
1507
|
-
createElementVNode("button", {
|
|
1508
|
-
class: "widget__btn widget__btn--icon",
|
|
1509
|
-
onClick: nextVariant,
|
|
1510
|
-
disabled: currentVariantIndex.value === allVariants.value.length - 1
|
|
1511
|
-
}, [
|
|
1512
|
-
createVNode(_component_v_icon, { name: "chevron_right" })
|
|
1513
|
-
], 8, _hoisted_37)
|
|
1514
|
-
])) : createCommentVNode("v-if", true),
|
|
1515
|
-
createElementVNode("div", _hoisted_38, [
|
|
1516
|
-
createVNode(AudioPlayer, {
|
|
1517
|
-
src: generatedAudioUrl.value,
|
|
1518
|
-
loading: false,
|
|
1519
|
-
size: "large"
|
|
1520
|
-
}, null, 8, ["src"]),
|
|
1521
|
-
createElementVNode("div", _hoisted_39, [
|
|
1522
|
-
createElementVNode("p", null, [
|
|
1523
|
-
_cache[15] || (_cache[15] = createElementVNode(
|
|
1524
|
-
"strong",
|
|
1525
|
-
null,
|
|
1526
|
-
"Model:",
|
|
1527
|
-
-1
|
|
1528
|
-
/* CACHED */
|
|
1529
|
-
)),
|
|
1530
|
-
createTextVNode(
|
|
1531
|
-
" " + toDisplayString(getModelName(selectedModel.value)),
|
|
1532
|
-
1
|
|
1533
|
-
/* TEXT */
|
|
1534
|
-
)
|
|
1535
|
-
]),
|
|
1536
|
-
createElementVNode("p", null, [
|
|
1537
|
-
_cache[16] || (_cache[16] = createElementVNode(
|
|
1538
|
-
"strong",
|
|
1539
|
-
null,
|
|
1540
|
-
"Voice:",
|
|
1541
|
-
-1
|
|
1542
|
-
/* CACHED */
|
|
1543
|
-
)),
|
|
1544
|
-
createTextVNode(
|
|
1545
|
-
" " + toDisplayString(getVoiceName(selectedVoiceId.value)),
|
|
1546
|
-
1
|
|
1547
|
-
/* TEXT */
|
|
1548
|
-
)
|
|
1549
|
-
]),
|
|
1550
|
-
createElementVNode("p", null, [
|
|
1551
|
-
_cache[17] || (_cache[17] = createElementVNode(
|
|
1552
|
-
"strong",
|
|
1553
|
-
null,
|
|
1554
|
-
"Tone:",
|
|
1555
|
-
-1
|
|
1556
|
-
/* CACHED */
|
|
1557
|
-
)),
|
|
1558
|
-
createTextVNode(
|
|
1559
|
-
" " + toDisplayString(getToneName(selectedToneId.value)),
|
|
1560
|
-
1
|
|
1561
|
-
/* TEXT */
|
|
1562
|
-
)
|
|
1563
|
-
]),
|
|
1564
|
-
createElementVNode("p", null, [
|
|
1565
|
-
_cache[18] || (_cache[18] = createElementVNode(
|
|
1566
|
-
"strong",
|
|
1567
|
-
null,
|
|
1568
|
-
"Style:",
|
|
1569
|
-
-1
|
|
1570
|
-
/* CACHED */
|
|
1571
|
-
)),
|
|
1572
|
-
createTextVNode(
|
|
1573
|
-
" " + toDisplayString(getStyleName(selectedStyleId.value)),
|
|
1574
|
-
1
|
|
1575
|
-
/* TEXT */
|
|
1576
|
-
)
|
|
1577
|
-
]),
|
|
1578
|
-
currentVariantDate.value ? (openBlock(), createElementBlock("p", _hoisted_40, [
|
|
1579
|
-
_cache[19] || (_cache[19] = createElementVNode(
|
|
1580
|
-
"strong",
|
|
1581
|
-
null,
|
|
1582
|
-
"Generated:",
|
|
1583
|
-
-1
|
|
1584
|
-
/* CACHED */
|
|
1585
|
-
)),
|
|
1586
|
-
createTextVNode(
|
|
1587
|
-
" " + toDisplayString(currentVariantDate.value),
|
|
1631
|
+
createElementVNode(
|
|
1632
|
+
"p",
|
|
1633
|
+
_hoisted_34,
|
|
1634
|
+
toDisplayString(allVariants.value.length) + " voiceover(s) available. Select one to preview and confirm. ",
|
|
1588
1635
|
1
|
|
1589
1636
|
/* TEXT */
|
|
1590
1637
|
)
|
|
1591
|
-
])
|
|
1638
|
+
])
|
|
1592
1639
|
])
|
|
1593
1640
|
]),
|
|
1594
|
-
|
|
1641
|
+
createCommentVNode(" Variants List "),
|
|
1642
|
+
createElementVNode("div", _hoisted_35, [
|
|
1643
|
+
(openBlock(true), createElementBlock(
|
|
1644
|
+
Fragment,
|
|
1645
|
+
null,
|
|
1646
|
+
renderList(allVariants.value, (variant, index) => {
|
|
1647
|
+
return openBlock(), createElementBlock("div", {
|
|
1648
|
+
key: variant.id,
|
|
1649
|
+
class: normalizeClass(["widget__variant-item", { "widget__variant-item--selected": currentVariantIndex.value === index }]),
|
|
1650
|
+
onClick: ($event) => selectVariant(index)
|
|
1651
|
+
}, [
|
|
1652
|
+
createElementVNode("div", _hoisted_37, [
|
|
1653
|
+
createElementVNode("input", {
|
|
1654
|
+
type: "radio",
|
|
1655
|
+
checked: currentVariantIndex.value === index,
|
|
1656
|
+
onChange: ($event) => selectVariant(index)
|
|
1657
|
+
}, null, 40, _hoisted_38)
|
|
1658
|
+
]),
|
|
1659
|
+
createElementVNode("div", _hoisted_39, [
|
|
1660
|
+
createVNode(AudioPlayer, {
|
|
1661
|
+
src: variant.audioUrl,
|
|
1662
|
+
loading: false,
|
|
1663
|
+
size: "small"
|
|
1664
|
+
}, null, 8, ["src"])
|
|
1665
|
+
]),
|
|
1666
|
+
createElementVNode("div", _hoisted_40, [
|
|
1667
|
+
createElementVNode(
|
|
1668
|
+
"span",
|
|
1669
|
+
_hoisted_41,
|
|
1670
|
+
toDisplayString(variant.voice_config?.provider) + " - " + toDisplayString(getVoiceNameFromConfig(variant.voice_config)),
|
|
1671
|
+
1
|
|
1672
|
+
/* TEXT */
|
|
1673
|
+
),
|
|
1674
|
+
createElementVNode(
|
|
1675
|
+
"span",
|
|
1676
|
+
_hoisted_42,
|
|
1677
|
+
toDisplayString(formatVariantDate(variant.date_created)),
|
|
1678
|
+
1
|
|
1679
|
+
/* TEXT */
|
|
1680
|
+
)
|
|
1681
|
+
]),
|
|
1682
|
+
createElementVNode("button", {
|
|
1683
|
+
class: "widget__btn widget__btn--icon widget__btn--danger",
|
|
1684
|
+
onClick: withModifiers(($event) => deleteVariant(variant.id), ["stop"]),
|
|
1685
|
+
title: "Delete this voiceover"
|
|
1686
|
+
}, [
|
|
1687
|
+
createVNode(_component_v_icon, {
|
|
1688
|
+
name: "delete",
|
|
1689
|
+
small: ""
|
|
1690
|
+
})
|
|
1691
|
+
], 8, _hoisted_43)
|
|
1692
|
+
], 10, _hoisted_36);
|
|
1693
|
+
}),
|
|
1694
|
+
128
|
|
1695
|
+
/* KEYED_FRAGMENT */
|
|
1696
|
+
))
|
|
1697
|
+
]),
|
|
1698
|
+
createCommentVNode(" Selected Variant Details "),
|
|
1699
|
+
selectedVariant.value ? (openBlock(), createElementBlock("div", _hoisted_44, [
|
|
1700
|
+
createElementVNode("p", null, [
|
|
1701
|
+
_cache[15] || (_cache[15] = createElementVNode(
|
|
1702
|
+
"strong",
|
|
1703
|
+
null,
|
|
1704
|
+
"Model:",
|
|
1705
|
+
-1
|
|
1706
|
+
/* CACHED */
|
|
1707
|
+
)),
|
|
1708
|
+
createTextVNode(
|
|
1709
|
+
" " + toDisplayString(selectedVariant.value.voice_config?.provider),
|
|
1710
|
+
1
|
|
1711
|
+
/* TEXT */
|
|
1712
|
+
)
|
|
1713
|
+
]),
|
|
1714
|
+
createElementVNode("p", null, [
|
|
1715
|
+
_cache[16] || (_cache[16] = createElementVNode(
|
|
1716
|
+
"strong",
|
|
1717
|
+
null,
|
|
1718
|
+
"Voice:",
|
|
1719
|
+
-1
|
|
1720
|
+
/* CACHED */
|
|
1721
|
+
)),
|
|
1722
|
+
createTextVNode(
|
|
1723
|
+
" " + toDisplayString(getVoiceNameFromConfig(selectedVariant.value.voice_config)),
|
|
1724
|
+
1
|
|
1725
|
+
/* TEXT */
|
|
1726
|
+
)
|
|
1727
|
+
]),
|
|
1728
|
+
createElementVNode("p", null, [
|
|
1729
|
+
_cache[17] || (_cache[17] = createElementVNode(
|
|
1730
|
+
"strong",
|
|
1731
|
+
null,
|
|
1732
|
+
"Style:",
|
|
1733
|
+
-1
|
|
1734
|
+
/* CACHED */
|
|
1735
|
+
)),
|
|
1736
|
+
createTextVNode(
|
|
1737
|
+
" " + toDisplayString(selectedVariant.value.voice_config?.style),
|
|
1738
|
+
1
|
|
1739
|
+
/* TEXT */
|
|
1740
|
+
)
|
|
1741
|
+
])
|
|
1742
|
+
])) : createCommentVNode("v-if", true),
|
|
1743
|
+
createElementVNode("div", _hoisted_45, [
|
|
1595
1744
|
createElementVNode("div", { class: "widget__footer-left" }, [
|
|
1596
1745
|
createElementVNode("button", {
|
|
1597
1746
|
class: "widget__btn widget__btn--secondary",
|
|
1598
1747
|
onClick: goBackToSelection
|
|
1599
|
-
}, "
|
|
1748
|
+
}, " Back ")
|
|
1600
1749
|
]),
|
|
1601
|
-
createElementVNode("div",
|
|
1750
|
+
createElementVNode("div", _hoisted_46, [
|
|
1602
1751
|
createElementVNode("button", {
|
|
1603
1752
|
class: "widget__btn widget__btn--secondary",
|
|
1604
1753
|
onClick: regenerateVoiceover
|
|
1605
1754
|
}, " Regenerate "),
|
|
1606
1755
|
createElementVNode("button", {
|
|
1607
1756
|
class: "widget__btn widget__btn--primary",
|
|
1608
|
-
onClick: confirmVoiceover
|
|
1609
|
-
|
|
1757
|
+
onClick: confirmVoiceover,
|
|
1758
|
+
disabled: !selectedVariant.value
|
|
1759
|
+
}, " Confirm ", 8, _hoisted_47)
|
|
1610
1760
|
])
|
|
1611
1761
|
])
|
|
1612
1762
|
],
|
|
@@ -1618,10 +1768,10 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1618
1768
|
}
|
|
1619
1769
|
});
|
|
1620
1770
|
|
|
1621
|
-
var css = "\n.voice-widget[data-v-
|
|
1771
|
+
var css = "\n.voice-widget[data-v-10fc7a16] {\n font-family: var(--theme--fonts--sans--font-family);\n padding: 16px;\n border: 1px solid var(--theme--form--field--input--border-color);\n border-radius: var(--theme--border-radius);\n background: var(--theme--background);\n}\n.widget__header[data-v-10fc7a16] {\n margin-bottom: 20px;\n}\n.widget__header-row[data-v-10fc7a16] {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n gap: 16px;\n}\n.widget__header-text[data-v-10fc7a16] {\n flex: 1;\n}\n.widget__title[data-v-10fc7a16] {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0 0 4px 0;\n font-size: 18px;\n font-weight: 600;\n color: var(--theme--foreground);\n}\n.widget__collapse-btn[data-v-10fc7a16] {\n background: none;\n border: none;\n padding: 4px;\n cursor: pointer;\n color: var(--theme--foreground-subdued);\n border-radius: 4px;\n}\n.widget__collapse-btn[data-v-10fc7a16]:hover {\n background: var(--theme--background-accent);\n}\n.widget__subtitle[data-v-10fc7a16] {\n margin: 0;\n font-size: 14px;\n color: var(--theme--foreground-subdued);\n}\n.widget__header-controls[data-v-10fc7a16] {\n display: flex;\n gap: 16px;\n align-items: center;\n}\n.widget__url-input[data-v-10fc7a16] {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.widget__url-label[data-v-10fc7a16] {\n font-size: 12px;\n color: var(--theme--foreground-subdued);\n white-space: nowrap;\n}\n.widget__url-field[data-v-10fc7a16] {\n padding: 6px 10px;\n border: 1px solid var(--theme--form--field--input--border-color);\n border-radius: var(--theme--border-radius);\n font-size: 12px;\n width: 280px;\n background: var(--theme--form--field--input--background);\n color: var(--theme--foreground);\n}\n.widget__section[data-v-10fc7a16] {\n margin-bottom: 20px;\n}\n.widget__section-label[data-v-10fc7a16] {\n display: block;\n margin-bottom: 8px;\n font-size: 14px;\n font-weight: 500;\n color: var(--theme--foreground);\n}\n.widget__model-buttons[data-v-10fc7a16] {\n display: flex;\n gap: 8px;\n}\n.widget__model-btn[data-v-10fc7a16] {\n padding: 8px 16px;\n border: 1px solid var(--theme--form--field--input--border-color);\n border-radius: var(--theme--border-radius);\n background: var(--theme--background);\n color: var(--theme--foreground);\n cursor: pointer;\n font-size: 14px;\n transition: all 0.15s ease;\n}\n.widget__model-btn[data-v-10fc7a16]:hover {\n border-color: var(--theme--primary);\n}\n.widget__model-btn--active[data-v-10fc7a16] {\n background: var(--theme--primary);\n border-color: var(--theme--primary);\n color: var(--theme--primary-foreground, #fff);\n}\n.widget__voice-list[data-v-10fc7a16] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.widget__section--toggle[data-v-10fc7a16] {\n padding: 12px;\n background: var(--theme--background-subdued);\n border-radius: var(--theme--border-radius);\n}\n.widget__toggle[data-v-10fc7a16] {\n display: flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n}\n.widget__toggle input[data-v-10fc7a16] {\n width: 16px;\n height: 16px;\n cursor: pointer;\n}\n.widget__toggle-label[data-v-10fc7a16] {\n font-size: 14px;\n font-weight: 500;\n color: var(--theme--foreground);\n}\n.widget__toggle-note[data-v-10fc7a16] {\n margin: 4px 0 0 24px;\n font-size: 12px;\n color: var(--theme--foreground-subdued);\n}\n.widget__footer[data-v-10fc7a16] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-top: 16px;\n border-top: 1px solid var(--theme--border-color-subdued);\n margin-top: 20px;\n}\n.widget__footer-left[data-v-10fc7a16],\n.widget__footer-right[data-v-10fc7a16] {\n display: flex;\n gap: 8px;\n}\n.widget__variant-nav[data-v-10fc7a16] {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n margin-bottom: 16px;\n padding: 8px 0;\n}\n.widget__variant-counter[data-v-10fc7a16] {\n font-size: 14px;\n font-weight: 500;\n color: var(--theme--foreground-subdued);\n min-width: 60px;\n text-align: center;\n}\n.widget__btn--icon[data-v-10fc7a16] {\n padding: 6px;\n min-width: 32px;\n min-height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.widget__result-date[data-v-10fc7a16] {\n margin-top: 8px;\n font-size: 12px;\n color: var(--theme--foreground-subdued);\n}\n.widget__btn[data-v-10fc7a16] {\n padding: 8px 16px;\n border: none;\n border-radius: var(--theme--border-radius);\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n.widget__btn[data-v-10fc7a16]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.widget__btn--primary[data-v-10fc7a16] {\n background: var(--theme--primary);\n color: var(--theme--primary-foreground, #fff);\n}\n.widget__btn--primary[data-v-10fc7a16]:hover:not(:disabled) {\n background: var(--theme--primary-accent);\n}\n.widget__btn--secondary[data-v-10fc7a16] {\n background: var(--theme--background-accent);\n color: var(--theme--foreground);\n border: 1px solid var(--theme--form--field--input--border-color);\n}\n.widget__btn--secondary[data-v-10fc7a16]:hover:not(:disabled) {\n background: var(--theme--background-normal);\n}\n\n/* Processing State */\n.widget__processing[data-v-10fc7a16] {\n padding: 40px 20px;\n text-align: center;\n}\n.widget__progress[data-v-10fc7a16] {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n margin-bottom: 16px;\n font-size: 16px;\n color: var(--theme--foreground);\n}\n.widget__progress-bar[data-v-10fc7a16] {\n height: 8px;\n background: var(--theme--background-accent);\n border-radius: 4px;\n overflow: hidden;\n max-width: 400px;\n margin: 0 auto;\n}\n.widget__progress-fill[data-v-10fc7a16] {\n height: 100%;\n background: var(--theme--primary);\n transition: width 0.3s ease;\n}\n\n/* Error State */\n.widget__error[data-v-10fc7a16] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n padding: 20px;\n color: var(--theme--danger);\n text-align: center;\n}\n.widget__error-actions[data-v-10fc7a16] {\n display: flex;\n gap: 8px;\n margin-top: 8px;\n}\n.widget__retry[data-v-10fc7a16] {\n padding: 6px 12px;\n background: var(--theme--danger);\n color: #fff;\n border: none;\n border-radius: var(--theme--border-radius);\n cursor: pointer;\n}\n\n/* Loading State */\n.widget__loading[data-v-10fc7a16] {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 40px 20px;\n color: var(--theme--foreground-subdued);\n}\n\n/* Result State */\n.widget__result[data-v-10fc7a16] {\n padding: 20px;\n background: var(--theme--background-subdued);\n border-radius: var(--theme--border-radius);\n margin-bottom: 20px;\n}\n.widget__result-info[data-v-10fc7a16] {\n margin-top: 16px;\n padding-top: 16px;\n border-top: 1px solid var(--theme--border-color-subdued);\n}\n.widget__result-info p[data-v-10fc7a16] {\n margin: 4px 0;\n font-size: 14px;\n color: var(--theme--foreground-subdued);\n}\n.widget__result-info strong[data-v-10fc7a16] {\n color: var(--theme--foreground);\n}\n\n/* Variants List View */\n.widget__variants-list[data-v-10fc7a16] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n max-height: 400px;\n overflow-y: auto;\n margin-bottom: 16px;\n}\n.widget__variant-item[data-v-10fc7a16] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px;\n border: 1px solid var(--theme--border-color-subdued);\n border-radius: var(--theme--border-radius);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n.widget__variant-item[data-v-10fc7a16]:hover {\n border-color: var(--theme--primary);\n}\n.widget__variant-item--selected[data-v-10fc7a16] {\n border-color: var(--theme--primary);\n background: var(--theme--primary-background);\n}\n.widget__variant-radio[data-v-10fc7a16] {\n flex-shrink: 0;\n}\n.widget__variant-radio input[data-v-10fc7a16] {\n width: 16px;\n height: 16px;\n cursor: pointer;\n}\n.widget__variant-player[data-v-10fc7a16] {\n flex: 1;\n min-width: 200px;\n}\n.widget__variant-meta[data-v-10fc7a16] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 120px;\n}\n.widget__variant-voice[data-v-10fc7a16] {\n font-size: 13px;\n font-weight: 500;\n color: var(--theme--foreground);\n}\n.widget__variant-date[data-v-10fc7a16] {\n font-size: 11px;\n color: var(--theme--foreground-subdued);\n}\n.widget__btn--danger[data-v-10fc7a16] {\n color: var(--theme--danger);\n background: transparent;\n border: none;\n}\n.widget__btn--danger[data-v-10fc7a16]:hover {\n background: var(--theme--danger-background);\n}\n.widget__selected-info[data-v-10fc7a16] {\n padding: 12px;\n background: var(--theme--background-subdued);\n border-radius: var(--theme--border-radius);\n margin-bottom: 16px;\n}\n.widget__selected-info p[data-v-10fc7a16] {\n margin: 4px 0;\n font-size: 14px;\n color: var(--theme--foreground-subdued);\n}\n.widget__selected-info strong[data-v-10fc7a16] {\n color: var(--theme--foreground);\n}\n.widget__progress-status[data-v-10fc7a16] {\n text-align: center;\n font-size: 12px;\n color: var(--theme--foreground-subdued);\n margin-top: 8px;\n}\n\n/* Animations */\n.spinning[data-v-10fc7a16] {\n animation: spin-10fc7a16 1s linear infinite;\n}\n@keyframes spin-10fc7a16 {\nfrom { transform: rotate(0deg);\n}\nto { transform: rotate(360deg);\n}\n}\n";
|
|
1622
1772
|
n(css,{});
|
|
1623
1773
|
|
|
1624
|
-
var InterfaceComponent = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-
|
|
1774
|
+
var InterfaceComponent = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-10fc7a16"], ["__file", "interface.vue"]]);
|
|
1625
1775
|
|
|
1626
1776
|
var index = defineInterface({
|
|
1627
1777
|
id: "voice-widget",
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stellartech/voice-widget-directus",
|
|
3
3
|
"description": "Voice generation widget with model/voice selection and audio preview for Directus",
|
|
4
4
|
"icon": "mic",
|
|
5
|
-
"version": "1.0.
|
|
5
|
+
"version": "1.0.1",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"readme": "README.md",
|
|
8
8
|
"repository": {
|