diff --git a/frontend/src/editor/export/webvtt.tsx b/frontend/src/editor/export/webvtt.tsx index 08128a3d..3681d067 100644 --- a/frontend/src/editor/export/webvtt.tsx +++ b/frontend/src/editor/export/webvtt.tsx @@ -1,19 +1,39 @@ -import { useMemo, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import * as Automerge from '@automerge/automerge'; import { Checkbox, FormControl, Input, Select } from '../../components/form'; import { canGenerateVtt, generateWebVtt } from '../../utils/export/webvtt'; import { SubtitleFormat } from '@audapolis/webvtt-writer'; import { downloadTextAsFile } from '../../utils/download_text_as_file'; +import { pushToPodlove, checkIsPodloveExportPossible } from '../../utils/export_to_podlove'; import { ExportProps } from '.'; -import { PrimaryButton, SecondaryButton } from '../../components/button'; +import { PrimaryButton, SecondaryButton, IconButton } from '../../components/button'; +import { BsEye, BsEyeSlash } from 'react-icons/bs'; + +type ExportFormat = SubtitleFormat | 'podlove'; export function WebVttExportBody({ onClose, outputNameBase, editor }: ExportProps) { const [includeSpeakerNames, setIncludeSpeakerNames] = useState(true); const [includeWordTimings, setIncludeWordTimings] = useState(false); const [limitLineLength, setLimitLineLength] = useState(false); const [maxLineLength, setMaxLineLength] = useState(60); - const [format, setFormat] = useState('vtt' as SubtitleFormat); + + const [podloveEpisodeId, setPodloveEpisodeId] = useState(1); + const [podloveUser, setPodloveUser] = useState(''); + const [podloveShowApplicationId, setPodloveShowApplicationId] = useState(false); + const [podloveApplicationId, setPodloveId] = useState(''); + const [podloveUrl, setPodloveUrl] = useState(''); + const [podloveExportPossible, setPodloveExportPossible] = useState(false); + useEffect(() => { + checkIsPodloveExportPossible( + podloveEpisodeId, + podloveUser, + podloveApplicationId, + podloveUrl, + ).then(setPodloveExportPossible); + }, [podloveEpisodeId, podloveUser, podloveApplicationId, podloveApplicationId, podloveUrl]); + + const [format, setFormat] = useState('vtt' as ExportFormat); const canExport = useMemo(() => canGenerateVtt(editor.doc.children), [editor.v]); return ( @@ -22,31 +42,100 @@ export function WebVttExportBody({ onClose, outputNameBase, editor }: ExportProp - {format == 'vtt' ? ( + {format == 'podlove' ? ( <> - setIncludeSpeakerNames(x)} - /> - setIncludeWordTimings(x)} - /> + + { + setPodloveUrl(e.target.value); + }} + /> + + + { + setPodloveUser(e.target.value); + }} + /> + + +
+ { + setPodloveId(e.target.value); + }} + /> + { + e.preventDefault(); + setPodloveShowApplicationId(!podloveShowApplicationId); + }} + label={podloveShowApplicationId ? 'Hide' : 'Show'} + iconClassName="inline-block -mt-1" + className="rounded-xl px-4 py-1" + iconAfter={true} + > +
+
+ + { + setPodloveEpisodeId(parseInt(e.target.value)); + }} + /> + ) : ( <> )} + {format == 'vtt' || format == 'podlove' ? ( + setIncludeSpeakerNames(x)} + /> + ) : ( + <> + )} + {format == 'vtt' ? ( + { + setIncludeWordTimings(x); + }} + /> + ) : ( + <> + )} - {!canExport.canGenerate && canExport.reason && ( + {((!canExport.canGenerate && canExport.reason) || + (format === 'podlove' && !podloveExportPossible)) && (
- {canExport.reason} +

{canExport.reason}

+ {format === 'podlove' && !podloveExportPossible && ( +

+ Configured episode could not be found in the podlove publisher instance. Please check + that the publisher url, credentials and episode id are correct. +

+ )}
)}
@@ -82,14 +178,24 @@ export function WebVttExportBody({ onClose, outputNameBase, editor }: ExportProp includeWordTimings, maxLineLength, ); - downloadTextAsFile( - `${outputNameBase}.${format}`, - `text/${format}`, - vtt.toString(format), - ); + if (format === 'vtt' || format === 'srt') { + downloadTextAsFile( + `${outputNameBase}.${format}`, + `text/${format}`, + vtt.toString(format), + ); + } else { + pushToPodlove( + podloveEpisodeId, + podloveUser, + podloveApplicationId, + podloveUrl, + vtt.toString('vtt'), + ); + } onClose(); }} - disabled={!canExport.canGenerate} + disabled={!canExport.canGenerate || (!podloveExportPossible && format == 'podlove')} > Export diff --git a/frontend/src/utils/export_to_podlove.ts b/frontend/src/utils/export_to_podlove.ts new file mode 100644 index 00000000..ce990a3f --- /dev/null +++ b/frontend/src/utils/export_to_podlove.ts @@ -0,0 +1,69 @@ +export function pushToPodlove( + episodeId: number, + user: string, + appId: string, + url: string, + text: string, +) { + // check if episode exist + const podloveUrlEpispde = url + '/wp-json/podlove/v2/episodes/' + episodeId.toString(); + fetch(podloveUrlEpispde, { + method: 'GET', + headers: { + 'Content-type': 'application/json;charset=UTF-8', + Authorization: `Basic ${btoa(`${user}:${appId}`)}`, + }, + }) + .then((response) => { + // export the vtt to the podlove publisher + if (response.status === 200) { + const podloveUrlTranscript = + url + '/wp-json/podlove/v2/transcripts/' + episodeId.toString(); + const podloveData = { + type: 'vtt', + content: text, + }; + fetch(podloveUrlTranscript, { + method: 'POST', + body: JSON.stringify(podloveData), + headers: { + 'Content-type': 'application/json;charset=UTF-8', + Authorization: `Basic ${btoa(`${user}:${appId}`)}`, + }, + }) + .then((response) => response.json()) + .catch((err) => console.error(err)); + } + }) + .catch((err) => console.error(err)); +} + +export async function checkIsPodloveExportPossible( + episodeId: number, + user: string, + appId: string, + url: string, +): Promise { + if (url.length < 1 || appId.length < 1 || user.length < 1 || episodeId < 1) { + return false; + } + + const podloveUrlEpisode = url + '/wp-json/podlove/v2/episodes/' + episodeId.toString(); + try { + const response = await fetch(podloveUrlEpisode, { + method: 'GET', + headers: { + 'Content-type': 'application/json;charset=UTF-8', + Authorization: `Basic ${btoa(`${user}:${appId}`)}`, + }, + }); + if (response.status === 200) { + return true; + } else { + return false; + } + } catch (err) { + console.log(err); + return false; + } +}