diff --git a/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php b/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php index 9c522a6ee5..d6eb522fb7 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php @@ -552,25 +552,13 @@ public function autoSubtitleAction(Request $request) (int)$request->request->get("record_id") ); - $permalinkUrl = ''; - $conf = $this->getConf(); - - // if subdef_source not set, by default use the preview permalink - $subdefSource = $conf->get(['externalservice', 'ginger', 'AutoSubtitling', 'subdef_source']) ?: 'preview'; - - if ($this->isPhysicallyPresent($record, $subdefSource) && ($previewLink = $record->get_subdef($subdefSource)->get_permalink()) != null) { - $permalinkUrl = $previewLink->get_url()->__toString(); - } $this->dispatch( PhraseaEvents::RECORD_AUTO_SUBTITLE, new RecordAutoSubtitleEvent( $record, - $permalinkUrl, $request->request->get("subtitle_language_source"), - $request->request->get("meta_struct_id_source"), - $request->request->get("subtitle_language_destination"), - $request->request->get("meta_struct_id_destination") + json_decode($request->request->get("subtitle_destination"), true) ) ); @@ -629,7 +617,8 @@ public function videoEditorAction(Request $request) 'videoEditorConfig' => $conf->get(['video-editor']), 'metadatas' => $metadatas, 'JSonFields' => json_encode($JSFields), - 'videoTextTrackFields' => $videoTextTrackFields + 'videoTextTrackFields' => $videoTextTrackFields, + 'languages' => $this->languageList() ]); } @@ -644,4 +633,42 @@ private function isPhysicallyPresent(record_adapter $record, $subdefName) return false; } + + private function languageList() + { + return [ + 'af-ZA' => 'af-ZA Afrikaans (South Africa)', 'am-ET' => 'am-ET Amharic (Ethiopia)', + 'ar-DZ' => 'ar-DZ Arabic (Algeria)', 'ar-BH' => 'ar-BH Arabic (Bahrain)', + 'ar-EG' => 'ar-EG Arabic (Egypt)', 'ar-IQ' => 'ar-IQ Arabic (Iraq)', + 'ar-IL' => 'ar-IL Arabic (Israel)', 'ar-YE' => 'ar-YE Arabic (Yemen)', + 'eu-ES' => 'eu-ES Basque (Spain)', 'bn-BD' => 'bn-BD Bengali (Bangladesh)', + 'bn-IN' => 'bn-IN Bengali (India)', 'bg-BG' => 'bg-BG Bulgarian (Bulgaria)', + 'ca-ES' => 'ca-ES Catalan (Spain)', 'yue-Hant-HK' => 'yue-Hant-HK Chinese, Cantonese (Traditional, Hong Kong)', + 'cmn-Hans-CN' => 'cmn-Hans-CN Chinese, Mandarin (Simplified, China)', 'hr-HR' => 'hr-HR Croatian (Croatia)', + 'cs-CZ' => 'cs-CZ Czech (Czech Republic)', 'da-DK' => 'da-DK Danish (Denmark)', + 'nl-NL' => 'nl-NL Dutch (Netherlands)', 'nl-BE' => 'nl-BE Dutch (Belgium)', + 'en-AU' => 'en-AU English (Australia)', 'en-CA' => 'en-CA English (Canada)', + 'en-GB' => 'en-GB English (United Kingdom)', 'en-US' => 'en-US English (United States)', + 'fr-CA' => 'fr-CA French (Canada)', 'fr-FR' => 'fr-FR French (France)', + 'fr-BE' => 'fr-BE French (Belgium)', 'fr-CH' => 'fr-CH French (Switzerland)', + 'ka-GE' => 'ka-GE Georgian (Georgia)', 'de-DE' => 'de-DE German (Germany)', + 'el-GR' => 'el-GR Greek (Greece)', 'he-IL' => 'he-IL Hebrew (Israel)', + 'hi-IN' => 'hi-IN Hindi (India)', 'hu-HU' => 'hu-HU Hungarian (Hungary)', + 'is-IS' => 'is-IS Icelandic (Iceland)', 'id-ID' => 'id-ID Indonesian (Indonesia)', + 'it-IT' => 'it-IT Italian (Italy)', 'ja-JP' => 'ja-JP Japanese (Japan)', + 'ko-KR' => 'ko-KR Korean (South Korea)', 'lo-LA' => 'lo-LA Lao (Laos)', + 'lt-LT' => 'lt-LT Lithuanian (Lithuania)', 'ms-MY' => 'ms-MY Malay (Malaysia)', + 'ne-NP' => 'ne-NP Nepali (Nepal)', 'nb-NO' => 'nb-NO Norwegian Bokmål (Norway)', + 'pl-PL' => 'pl-PL Polish (Poland)', 'pt-BR' => 'pt-BR Portuguese (Brazil)', + 'pt-PT' => 'pt-PT Portuguese (Portugal)', 'ro-RO' => 'ro-RO Romanian (Romania)', + 'ru-RU' => 'ru-RU Russian (Russia)', 'sr-RS' => 'sr-RS Serbian (Serbia)', + 'sk-SK' => 'sk-SK Slovak (Slovakia)', 'sl-SI' => 'sl-SI Slovenian (Slovenia)', + 'es-ES' => 'es-ES Spanish (Spain)', 'sv-SE' => 'sv-SE Swedish (Sweden)', + 'th-TH' => 'th-TH Thai (Thailand)', 'tr-TR' => 'tr-TR Turkish (Turkey)', + 'uk-UA' => 'uk-UA Ukrainian (Ukraine)', 'vi-VN' => 'vi-VN Vietnamese (Vietnam)', + 'et-EE' => 'et-EE Estonian (Estonia)', 'mn-MN' => 'mn-MN Mongolian (Mongolia)', + 'uz-UZ' => 'uz-UZ Uzbek (Uzbekistan)' + + ]; + } } diff --git a/lib/Alchemy/Phrasea/Core/Event/Record/RecordAutoSubtitleEvent.php b/lib/Alchemy/Phrasea/Core/Event/Record/RecordAutoSubtitleEvent.php index 307a54e474..37b12fb4e0 100644 --- a/lib/Alchemy/Phrasea/Core/Event/Record/RecordAutoSubtitleEvent.php +++ b/lib/Alchemy/Phrasea/Core/Event/Record/RecordAutoSubtitleEvent.php @@ -10,24 +10,17 @@ class RecordAutoSubtitleEvent extends RecordEvent private $metaStructureIdSource; private $languageDestination; private $metaStructureIdDestination; - private $permalinkUrl; public function __construct( RecordInterface $record, - $permalinkUrl, $languageSource, - $metaStructureIdSource, - $languageDestination, - $metaStructureIdDestination + $languageDestination ) { parent::__construct($record); $this->languageSource = $languageSource; - $this->metaStructureIdSource = $metaStructureIdSource; $this->languageDestination = $languageDestination; - $this->metaStructureIdDestination = $metaStructureIdDestination; - $this->permalinkUrl = $permalinkUrl; } public function getLanguageSource() @@ -35,11 +28,6 @@ public function getLanguageSource() return $this->languageSource; } - public function getMetaStructureIdSource() - { - return $this->metaStructureIdSource; - } - public function getLanguageDestination() { return $this->languageDestination; @@ -49,9 +37,4 @@ public function getMetaStructureIdDestination() { return $this->metaStructureIdDestination; } - - public function getPermalinkUrl() - { - return $this->permalinkUrl; - } } diff --git a/lib/Alchemy/Phrasea/WorkerManager/Subscriber/SubtitleSubscriber.php b/lib/Alchemy/Phrasea/WorkerManager/Subscriber/SubtitleSubscriber.php index 83318bcbb4..b6ab7c4b28 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Subscriber/SubtitleSubscriber.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Subscriber/SubtitleSubscriber.php @@ -27,23 +27,25 @@ public function __construct(callable $repoWorkerJobLocator, MessagePublisher $me public function onRecordAutoSubtitle(RecordAutoSubtitleEvent $event) { - $data = [ - "databoxId" => $event->getRecord()->getDataboxId(), - "recordId" => $event->getRecord()->getRecordId(), - "permalinkUrl" => $event->getPermalinkUrl(), - "languageSource" => $event->getLanguageSource(), - "metaStructureIdSource" => $event->getMetaStructureIdSource(), - "languageDestination" => $event->getLanguageDestination(), - "metaStructureIdDestination" => $event->getMetaStructureIdDestination(), - "type" => MessagePublisher::SUBTITLE_TYPE // used to specify the final Q to publish message - ]; + if (!empty($event->getLanguageDestination())) { + $data = [ + "databoxId" => $event->getRecord()->getDataboxId(), + "recordId" => $event->getRecord()->getRecordId(), + "languageSource" => $event->getLanguageSource(), + "languageDestination" => $event->getLanguageDestination(), + "type" => MessagePublisher::SUBTITLE_TYPE // used to specify the final Q to publish message + ]; - $payload = [ - 'message_type' => MessagePublisher::MAIN_QUEUE_TYPE, - 'payload' => $data - ]; + $payload = [ + 'message_type' => MessagePublisher::MAIN_QUEUE_TYPE, + 'payload' => $data + ]; + + $this->messagePublisher->publishMessage($payload, MessagePublisher::MAIN_QUEUE_TYPE); + } else { + $this->messagePublisher->pushLog("There is no language destination selected when trying do do autosubtitle!"); + } - $this->messagePublisher->publishMessage($payload, MessagePublisher::MAIN_QUEUE_TYPE); } public static function getSubscribedEvents() diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php index 753b9c4c16..d170e6d694 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php @@ -29,6 +29,14 @@ class SubtitleWorker implements WorkerInterface private $repoWorker; private $dispatcher; + /** + * @var Client + */ + private $happyscribeClient; + private $happyscribeToken; + private $extension; + private $workerRunningJob; + private $transcriptionsId; public function __construct(WorkerRunningJobRepository $repoWorker, PropertyAccess $conf, callable $appboxLocator, LoggerInterface $logger, EventDispatcherInterface $dispatcher) { @@ -41,16 +49,17 @@ public function __construct(WorkerRunningJobRepository $repoWorker, PropertyAcce public function process(array $payload) { - $gingaBaseurl = $this->conf->get(['externalservice', 'ginger', 'AutoSubtitling', 'service_base_url']); - $gingaToken = $this->conf->get(['externalservice', 'ginger', 'AutoSubtitling', 'token']); - $gingaTranscriptFormat = $this->conf->get(['externalservice', 'ginger', 'AutoSubtitling', 'transcript_format']); + $this->happyscribeToken = $this->conf->get(['externalservice', 'happyscribe', 'token']); + $organizationId = $this->conf->get(['externalservice', 'happyscribe', 'organization_id']); + $happyscribeTranscriptFormat = $this->conf->get(['externalservice', 'happyscribe', 'transcript_format'], 'vtt'); - if (!$gingaBaseurl || !$gingaToken || !$gingaTranscriptFormat) { + if (!$organizationId || !$this->happyscribeToken ) { $this->logger->error("External service Ginga not set correctly in configuration.yml"); return 0; } - $workerRunningJob = null; + + $this->workerRunningJob = null; $em = $this->repoWorker->getEntityManager(); $em->beginTransaction(); @@ -62,8 +71,8 @@ public function process(array $payload) ]; $date = new \DateTime(); - $workerRunningJob = new WorkerRunningJob(); - $workerRunningJob + $this->workerRunningJob = new WorkerRunningJob(); + $this->workerRunningJob ->setDataboxId($payload['databoxId']) ->setRecordId($payload['recordId']) ->setWork(MessagePublisher::SUBTITLE_TYPE) @@ -73,7 +82,7 @@ public function process(array $payload) ->setPayload($message) ; - $em->persist($workerRunningJob); + $em->persist($this->workerRunningJob); $em->flush(); $em->commit(); @@ -81,223 +90,171 @@ public function process(array $payload) $em->rollback(); } - switch ($gingaTranscriptFormat) { - case 'text/srt,': - $extension = 'srt'; - break; - case 'text/plain': - $extension = 'txt'; - break; - case 'application/json': - $extension = 'json'; - break; - case 'text/vtt': - default: - $extension = 'vtt'; - break; + if (in_array(strtolower($happyscribeTranscriptFormat), ['srt', 'txt', 'json', 'vtt'])) { + $this->extension = strtolower($happyscribeTranscriptFormat); + } else { + $this->extension = 'vtt'; } - $languageSource = $this->getLanguageFormat($payload['languageSource']); - $languageDestination = $this->getLanguageFormat($payload['languageDestination']); - $record = $this->getApplicationBox()->get_databox($payload['databoxId'])->get_record($payload['recordId']); - $languageSourceFieldName = $record->getDatabox()->get_meta_structure()->get_element($payload['metaStructureIdSource'])->get_name(); - - $subtitleSourceTemporaryFile = $this->getTemporaryFilesystem()->createTemporaryFile("subtitle", null, $extension); - $gingerClient = new Client(); - - // if the languageSourceFieldName do not yet exist, first generate subtitle for it - if ($payload['permalinkUrl'] != '' && !$record->get_caption()->has_field($languageSourceFieldName)) { - try { - $response = $gingerClient->post($gingaBaseurl.'/media/', [ - 'headers' => [ - 'Authorization' => 'token '.$gingaToken - ], - 'json' => [ - 'url' => $payload['permalinkUrl'], - 'language' => $languageSource - ] - ]); - } catch(\Exception $e) { - $this->logger->error($e->getMessage()); - $this->jobFinished($workerRunningJob); - - return 0; - } - if ($response->getStatusCode() !== 201) { - $this->logger->error("response status /media/ : ". $response->getStatusCode()); - $this->jobFinished($workerRunningJob); + // if subdef_source not set, by default use the preview permalink + $subdefSource = $this->conf->get(['externalservice', 'happyscribe', 'subdef_source']) ?: 'preview'; - return 0; - } + $tmpUrl = ''; + if ($this->isPhysicallyPresent($record, $subdefSource) && ($previewLink = $record->get_subdef($subdefSource)->get_permalink()) != null) { + $tmpUrl = $previewLink->get_url()->__toString(); + } - $responseMediaBody = $response->getBody()->getContents(); - $responseMediaBody = json_decode($responseMediaBody,true); + $this->happyscribeClient = new Client(); - $checkStatus = true; - do { - // first wait 5 second before check subtitling status - sleep(5); - $this->logger->info("bigin to check status"); - try { - $response = $gingerClient->get($gingaBaseurl.'/task/'.$responseMediaBody['task_id'].'/', [ - 'headers' => [ - 'Authorization' => 'token '.$gingaToken - ] - ]); - } catch (\Exception $e) { - $checkStatus = false; + if (!$this->isRemoteFileExist($tmpUrl)) { + $fileName = $record->get_subdef($subdefSource)->get_file(); - break; - } + // first get a signed URL + $responseUpload = $this->happyscribeClient->get('https://www.happyscribe.com/api/v1/uploads/new?filename=' . $fileName, [ + 'headers' => [ + 'Authorization' => 'Bearer ' . $this->happyscribeToken + ] + ]); - if ($response->getStatusCode() !== 200) { - $checkStatus = false; + if ($responseUpload->getStatusCode() !== 200) { + $this->logger->error("error when uploading file to transcript,response status : ". $responseUpload->getStatusCode()); + $this->jobFinished(); - break; - } + return 0; + } - $responseTaskBody = $response->getBody()->getContents(); - $responseTaskBody = json_decode($responseTaskBody,true); + $responseUploadBody = $responseUpload->getBody()->getContents(); + $responseUploadBody = json_decode($responseUploadBody,true); - } while($responseTaskBody['status'] != 'SUCCESS'); + $tmpUrl = $responseUploadBody['signedUrl']; + $res = $this->happyscribeClient->put($tmpUrl, [ + 'body' => fopen($record->get_subdef($subdefSource)->getRealPath(), 'r') + ]); - if (!$checkStatus) { - $this->logger->error("can't check status"); - $this->jobFinished($workerRunningJob); + if ($res->getStatusCode() !== 200) { + $this->logger->error("error when uploading file to signed url,response status : ". $res->getStatusCode()); + $this->jobFinished(); return 0; } + } - try { - $response = $gingerClient->get($gingaBaseurl.'/media/'.$responseMediaBody['media']['uuid'].'/', [ - 'headers' => [ - 'Authorization' => 'token '.$gingaToken, - 'ACCEPT' => $gingaTranscriptFormat - ], - 'query' => [ - 'language' => $languageSource - ] - ]); - } catch (\Exception $e) { - $this->logger->error($e->getMessage()); - $this->jobFinished($workerRunningJob); + // create a transcription - return 0; - } + try { + $responseTranscription = $this->happyscribeClient->post('https://www.happyscribe.com/api/v1/transcriptions', [ + 'headers' => [ + 'Authorization' => 'Bearer '. $this->happyscribeToken + ], + 'json' => [ + 'transcription' => [ + 'name' => $record->get_title(), + 'is_subtitle' => true, + 'language' => $payload['languageSource'], + 'organization_id' => $organizationId, + 'tmp_url' => $tmpUrl + ] + ] + ]); - if ($response->getStatusCode() !== 200) { - $this->logger->error("response status /media/uuid : ". $response->getStatusCode()); - $this->jobFinished($workerRunningJob); + } catch (\Exception $e) { + $this->logger->error("error when creating transcript : " . $e->getMessage()); + $this->jobFinished(); - return 0; - } + return 0; + } - $transcriptContent = $response->getBody()->getContents(); + if ($responseTranscription->getStatusCode() !== 200) { + $this->logger->error("error when creating transcript,response status : ". $responseTranscription->getStatusCode()); + $this->jobFinished(); - $transcriptContent = preg_replace('/WEBVTT/', 'WEBVTT - with cue identifier', $transcriptContent, 1); + return 0; + } - // save subtitle on temporary file to use to translate if needed - file_put_contents($subtitleSourceTemporaryFile, $transcriptContent); + $responseTranscriptionBody = $responseTranscription->getBody()->getContents(); + $responseTranscriptionBody = json_decode($responseTranscriptionBody,true); + $this->transcriptionsId[] = $transcriptionId = $responseTranscriptionBody['id']; - $metadatas[0] = [ - 'meta_struct_id' => (int)$payload['metaStructureIdSource'], - 'meta_id' => '', - 'value' => $transcriptContent - ]; + // check transcription status + $failureTranscriptMessage = ''; - try { - $record->set_metadatas($metadatas); + do { + // first wait 5 second before check transcript status + sleep(5); + $resCheckTranscript = $this->happyscribeClient->get('https://www.happyscribe.com/api/v1/transcriptions/' . $transcriptionId, [ + 'headers' => [ + 'Authorization' => 'Bearer ' . $this->happyscribeToken + ] + ]); - // order to write meta in file - $this->dispatcher->dispatch(WorkerEvents::RECORDS_WRITE_META, - new RecordsWriteMetaEvent([$record->getRecordId()], $record->getDataboxId())); - } catch (\Exception $e) { - $this->logger->error($e->getMessage()); - $this->jobFinished($workerRunningJob); + if ($resCheckTranscript->getStatusCode() !== 200) { + $this->logger->error("error when checking transcript,response status : ". $responseTranscription->getStatusCode()); + $this->jobFinished(); return 0; } - $this->logger->info("Generate subtitle on language source SUCCESS"); - } elseif ($record->get_caption()->has_field($languageSourceFieldName)) { - // get the source subtitle and save it to a temporary file - $fieldValues = $record->get_caption()->get_field($languageSourceFieldName)->get_values(); - $fieldValue = array_pop($fieldValues); + $resCheckTranscriptBody = $resCheckTranscript->getBody()->getContents(); + $resCheckTranscriptBody = json_decode($resCheckTranscriptBody,true); + $transcriptStatus = $resCheckTranscriptBody['state']; + if (isset($resCheckTranscriptBody['failureMessage'])) { + $failureTranscriptMessage = $resCheckTranscriptBody['failureMessage']; + } - file_put_contents($subtitleSourceTemporaryFile, $fieldValue->getValue()); + } while(!in_array($transcriptStatus, ['automatic_done', 'locked', 'failed'])); + + if ($transcriptStatus != "automatic_done") { + $this->logger->error("error when checking transcript, : " . $failureTranscriptMessage); + $this->jobFinished(); + + return 0; } - if ($payload['metaStructureIdSource'] !== $payload['metaStructureIdDestination']) { - try { - $response = $gingerClient->post($gingaBaseurl.'/translate/', [ - 'headers' => [ - 'Authorization' => 'token '.$gingaToken, - 'ACCEPT' => $gingaTranscriptFormat - ], - 'multipart' => [ - [ - 'name' => 'transcript', - 'contents' => fopen($subtitleSourceTemporaryFile, 'r') - ], - [ - 'name' => 'transcript_format', - 'contents' => $gingaTranscriptFormat, - - ], - [ - 'name' => 'language_in', - 'contents' => $languageSource, - - ], - [ - 'name' => 'language_out', - 'contents' => $languageDestination, - - ] - ] - ]); - } catch(\Exception $e) { - $this->logger->error($e->getMessage()); - $this->jobFinished($workerRunningJob); - return 0; - } + $metadatas = []; - if ($response->getStatusCode() !== 200) { - $this->logger->error("response status /translate/ : ". $response->getStatusCode()); - $this->jobFinished($workerRunningJob); + foreach ($payload['languageDestination'] as $language => $metaStructureIdDestination) { + $languageDestination = strtolower($language); - return 0; + if ($this->getTargetLanguageByCode($payload['languageSource']) == $languageDestination) { + $metadatas[] = [ + 'meta_struct_id' => (int)$metaStructureIdDestination, + 'meta_id' => '', + 'value' => $this->exportTranscription($transcriptionId) + ]; + } else { + $metadatas[] = [ + 'meta_struct_id' => (int)$metaStructureIdDestination, + 'meta_id' => '', + 'value' => $this->translate($transcriptionId, $languageDestination) + ]; } + } - $transcriptContent = $response->getBody()->getContents(); - $transcriptContent = preg_replace('/WEBVTT/', 'WEBVTT - with cue identifier', $transcriptContent, 1); + try { + $record->set_metadatas($metadatas); - $metadatas[0] = [ - 'meta_struct_id' => (int)$payload['metaStructureIdDestination'], - 'meta_id' => '', - 'value' => $transcriptContent - ]; + // order to write meta in file + $this->dispatcher->dispatch(WorkerEvents::RECORDS_WRITE_META, + new RecordsWriteMetaEvent([$record->getRecordId()], $record->getDataboxId())); + } catch (\Exception $e) { + $this->logger->error($e->getMessage()); + $this->jobFinished(); - try { - $record->set_metadatas($metadatas); + return 0; + } - // order to write meta in file - $this->dispatcher->dispatch(WorkerEvents::RECORDS_WRITE_META, - new RecordsWriteMetaEvent([$record->getRecordId()], $record->getDataboxId())); - } catch (\Exception $e) { - $this->logger->error($e->getMessage()); - $this->jobFinished($workerRunningJob); + $this->logger->info("Translate subtitle on language destination SUCCESS"); - return 0; - } + // delete transcription - $this->logger->info("Translate subtitle on language destination SUCCESS"); - } +// foreach ($this->transcriptionsId as $transcriptionId) { +// $this->deleteTranscription($transcriptionId); +// } - $this->jobFinished($workerRunningJob); + $this->jobFinished(); return 0; } @@ -312,30 +269,211 @@ private function getApplicationBox() return $callable(); } - private function jobFinished(WorkerRunningJob $workerRunningJob) + private function jobFinished() { - if ($workerRunningJob != null) { - $workerRunningJob->setStatus(WorkerRunningJob::FINISHED) + if ($this->workerRunningJob != null) { + $this->workerRunningJob->setStatus(WorkerRunningJob::FINISHED) ->setFinished(new \DateTime('now')); $em = $this->repoWorker->getEntityManager(); $this->repoWorker->reconnect(); - $em->persist($workerRunningJob); + $em->persist($this->workerRunningJob); $em->flush(); } } - private function getLanguageFormat($language) + private function isRemoteFileExist($fileUrl) + { + $client = new Client(); + + try { + $client->head($fileUrl); + return true; + } catch (\Exception $e) { + return false; + } + } + + private function isPhysicallyPresent(\record_adapter $record, $subdefName) + { + try { + return $record->get_subdef($subdefName)->is_physically_present(); + } + catch (\Exception $e) { + unset($e); + } + + return false; + } + + private function exportTranscription($transcriptionId) + { + $subtitleTranscriptTemporaryFile = $this->getTemporaryFilesystem()->createTemporaryFile("subtitle", null, $this->extension); + + $resExport = $this->happyscribeClient->post('https://www.happyscribe.com/api/v1/exports', [ + 'headers' => [ + 'Authorization' => 'Bearer ' . $this->happyscribeToken + ], + 'json' => [ + 'export' => [ + 'format' => $this->extension, + 'transcription_ids' => [ + $transcriptionId + ] + ] + ] + ]); + + if ($resExport->getStatusCode() !== 200) { + $this->logger->error("error when creating transcript export, response status : ". $resExport->getStatusCode()); + $this->jobFinished(); + + return 0; + } + + $resExportBody = $resExport->getBody()->getContents(); + $resExportBody = json_decode($resExportBody,true); + + $exportId = $resExportBody['id']; + $failureExportMessage = ''; + + // retrieve transcript export when ready + do { + sleep(3); + $resCheckExport = $this->happyscribeClient->get('https://www.happyscribe.com/api/v1/exports/' . $exportId, [ + 'headers' => [ + 'Authorization' => 'Bearer ' . $this->happyscribeToken + ] + ]); + + if ($resCheckExport->getStatusCode() !== 200) { + $this->logger->error("error when checking transcript export ,response status : ". $resCheckExport->getStatusCode()); + $this->jobFinished(); + + return 0; + } + + $resCheckExportBody = $resCheckExport->getBody()->getContents(); + $resCheckExportBody = json_decode($resCheckExportBody,true); + $exportStatus = $resCheckExportBody['state']; + if (isset($resCheckExportBody['failureMessage'])) { + $failureExportMessage = $resCheckExportBody['failureMessage']; + } + + } while(!in_array($exportStatus, ['ready', 'expired', 'failed'])); + + + if ($exportStatus != 'ready') { + $this->logger->error("error when exporting transcript : " . $failureExportMessage); + $this->jobFinished(); + + return 0; + } + + $this->happyscribeClient->get($resCheckExportBody['download_link'], [ + 'sink' => $subtitleTranscriptTemporaryFile + ]); + + $transcriptContent = file_get_contents($subtitleTranscriptTemporaryFile); + + return $transcriptContent; + } + + private function translate($sourceTranscriptionId, $targetLanguage) { - switch ($language) { - case 'En': - return 'en-GB'; - case 'De': - return 'de-DE'; - case 'Fr': - default: - return 'fr-FR'; + // translate + try { + $resTranslate = $this->happyscribeClient->post('https://www.happyscribe.com/api/v1/task/transcription_translation', [ + 'headers' => [ + 'Authorization' => 'Bearer ' . $this->happyscribeToken + ], + 'json' => [ + 'source_transcription_id' => $sourceTranscriptionId, + 'target_language' => strtolower($targetLanguage) + ] + ]); + } catch (\Exception $e) { + $this->logger->error("error when translate : ". $e->getMessage()); + $this->jobFinished(); + + return 0; } + + + if ($resTranslate->getStatusCode() !== 200) { + $this->logger->error("error when translate, response status : ". $resTranslate->getStatusCode()); + $this->jobFinished(); + + return 0; + } + + $resTranslateBody = $resTranslate->getBody()->getContents(); + $resTranslateBody = json_decode($resTranslateBody,true); + + if ($resTranslateBody['state'] == 'failed') { + $this->logger->error("failed when translate, : " . $resTranslateBody['failureReason']); + $this->jobFinished(); + + return 0; + } + + $translateId = $resTranslateBody['id']; + $this->transcriptionsId[] = $translatedTranscriptionId = $resTranslateBody['translatedTranscriptionId']; + + // check translation + $failureTranslateMessage = ''; + + do { + sleep(5); + + $resCheckTranslate = $this->happyscribeClient->get('https://www.happyscribe.com/api/v1/task/transcription_translation/' . $translateId, [ + 'headers' => [ + 'Authorization' => 'Bearer ' . $this->happyscribeToken + ] + ]); + + if ($resCheckTranslate->getStatusCode() !== 200) { + $this->logger->error("error when checking translation task ,response status : " . $resCheckTranslate->getStatusCode()); + $this->jobFinished(); + + return 0; + } + + $resCheckTranslateBody = $resCheckTranslate->getBody()->getContents(); + $resCheckTranslateBody = json_decode($resCheckTranslateBody,true); + $checkTranslateStatus = $resCheckTranslateBody['state']; + if (isset($resCheckTranslateBody['failureReason'])) { + $failureTranslateMessage = $resCheckTranslateBody['failureReason']; + } + + } while(!in_array($checkTranslateStatus, ['done', 'failed'])); + + if ($checkTranslateStatus != 'done') { + $this->logger->error("error when translate : " . $failureTranslateMessage); + $this->jobFinished(); + + return 0; + } + + // export the translation now + + return $this->exportTranscription($translatedTranscriptionId); + } + + private function deleteTranscription($transcriptionId) + { + $this->happyscribeClient->delete('https://www.happyscribe.com/api/v1/transcriptions/' . $transcriptionId, [ + 'headers' => [ + 'Authorization' => 'Bearer ' . $this->happyscribeToken + ] + ]); + } + + private function getTargetLanguageByCode($code) + { + $t = explode('-', $code); + + return $t[0]; } } diff --git a/templates/web/prod/actions/Tools/videoEditor.html.twig b/templates/web/prod/actions/Tools/videoEditor.html.twig index 50719f5eb3..f3fc5ee52c 100644 --- a/templates/web/prod/actions/Tools/videoEditor.html.twig +++ b/templates/web/prod/actions/Tools/videoEditor.html.twig @@ -229,7 +229,7 @@
@@ -241,18 +241,16 @@
- + {% for videoTextTrackField in videoTextTrackFields %} + + {% endfor %}