diff --git a/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php b/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php index b549650286..4bdd562424 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php @@ -15,24 +15,21 @@ use Alchemy\Phrasea\Application\Helper\FilesystemAware; use Alchemy\Phrasea\Application\Helper\SubDefinitionSubstituerAware; use Alchemy\Phrasea\Controller\Controller; use Alchemy\Phrasea\Controller\RecordsRequest; +use Alchemy\Phrasea\Core\Event\Record\RecordAutoSubtitleEvent; use Alchemy\Phrasea\Core\Event\Record\RecordEvents; use Alchemy\Phrasea\Core\Event\Record\SubdefinitionCreateEvent; +use Alchemy\Phrasea\Core\PhraseaEvents; use Alchemy\Phrasea\Exception\RuntimeException; use Alchemy\Phrasea\Metadata\PhraseanetMetadataReader; use Alchemy\Phrasea\Metadata\PhraseanetMetadataSetter; use Alchemy\Phrasea\Record\RecordWasRotated; use DataURI\Parser; -use GuzzleHttp\Client; use MediaAlchemyst\Alchemyst; use MediaVorus\MediaVorus; use Symfony\Component\HttpFoundation\Request; class ToolsController extends Controller { - const GINGER_BASE_URL = "https://test.api.ginger.studio/recognition/speech"; - const GINGER_TOKEN = "39c6011d-3bbe-4f39-95d0-a327d320ded4"; - const GINGER_TRANSCRIPT_FORMAT = "text/vtt"; - use DataboxLoggerAware; use DispatcherAware; use FilesystemAware; @@ -454,119 +451,17 @@ class ToolsController extends Controller public function autoSubtitleAction(Request $request) { - $return = ['success' => false, 'errorMessage' => '']; $record = new \record_adapter($this->app, (int)$request->request->get("databox_id"), (int)$request->request->get("record_id") ); - if ($record->has_preview() && ($previewLink = $record->get_preview()->get_permalink()) !== null && $request->request->get("meta_struct_id")) { - switch ($request->request->get("subtitle_language_source")) { - case 'En': - $language = 'en-GB'; - break; - case 'De': - $language = 'de-DE'; - break; - case 'Fr': - default: - $language = 'fr-FR'; - break; - } + $this->dispatch( + PhraseaEvents::RECORD_AUTO_SUBTITLE, + new RecordAutoSubtitleEvent($record, $request->request->get("subtitle_language_source"), $request->request->get("meta_struct_id")) + ); - $permalinkUrl = $previewLink->get_url()->__toString(); - - $gingerClient = new Client(); - - try { - $response = $gingerClient->post(self::GINGER_BASE_URL.'/media/', [ - 'headers' => [ - 'Authorization' => 'token '.self::GINGER_TOKEN - ], - 'json' => [ - 'url' => $permalinkUrl, - 'language' => $language - ] - ]); - } catch(\Exception $e) { - $return['errorMessage'] = $e->getMessage(); - - return $this->app->json($return); - } - - if ($response->getStatusCode() !== 201) { - return $this->app->json($return); - } - - $responseMediaBody = $response->getBody()->getContents(); - $responseMediaBody = json_decode($responseMediaBody,true); - - $checkStatus = null; - do { - // first wait 5 second before check subtitling status - sleep(5); - - try { - $response = $gingerClient->get(self::GINGER_BASE_URL.'/task/'.$responseMediaBody['task_id'].'/', [ - 'headers' => [ - 'Authorization' => 'token '.self::GINGER_TOKEN - ] - ]); - } catch (\Exception $e) { - $checkStatus = false; - } - - if ($response->getStatusCode() !== 200) { - $checkStatus = false; - break; - } - - $responseTaskBody = $response->getBody()->getContents(); - $responseTaskBody = json_decode($responseTaskBody,true); - - } while($responseTaskBody['status'] != 'SUCCESS'); - - if (!$checkStatus) { - return $this->app->json($return); - } - - try { - $response = $gingerClient->get(self::GINGER_BASE_URL.'/media/'.$responseMediaBody['media']['uuid'].'/', [ - 'headers' => [ - 'Authorization' => 'token '.self::GINGER_TOKEN, - 'ACCEPT' => self::GINGER_TRANSCRIPT_FORMAT - ], - 'query' => [ - 'language' => $language - ] - ]); - } catch (\Exception $e) { - return $this->app->json($return); - } - if ($response->getStatusCode() !== 201) { - return $this->app->json($return); - } - - $transcriptContent = $response->getBody()->getContents(); - - $metadatas[0] = [ - 'meta_struct_id' => (int)$request->request->get("meta_struct_id"), - 'meta_id' => '', - 'value' => $transcriptContent - ]; - - try { - $record->set_metadatas($metadatas); - } catch (\Exception $e) { - $return['errorMessage'] = $e->getMessage(); - - return $this->app->json($return); - } - - $return['success'] = true; - } - - return $this->app->json($return); + return $this->app->json(["status" => "dispatch"]); } public function videoEditorAction(Request $request) diff --git a/lib/Alchemy/Phrasea/Core/Event/Record/RecordAutoSubtitleEvent.php b/lib/Alchemy/Phrasea/Core/Event/Record/RecordAutoSubtitleEvent.php new file mode 100644 index 0000000000..5f502930e5 --- /dev/null +++ b/lib/Alchemy/Phrasea/Core/Event/Record/RecordAutoSubtitleEvent.php @@ -0,0 +1,29 @@ +languageSource = $languageSource; + $this->metaStructId = $metaStructId; + } + + public function getLanguageSource() + { + return $this->languageSource; + } + + public function getMetaStructId() + { + return $this->metaStructId; + } +} diff --git a/lib/Alchemy/Phrasea/Core/PhraseaEvents.php b/lib/Alchemy/Phrasea/Core/PhraseaEvents.php index 90adac69dd..1e1daf714b 100644 --- a/lib/Alchemy/Phrasea/Core/PhraseaEvents.php +++ b/lib/Alchemy/Phrasea/Core/PhraseaEvents.php @@ -54,6 +54,8 @@ final class PhraseaEvents const RECORD_EDIT = 'record.edit'; const RECORD_UPLOAD = 'record.upload'; + const RECORD_AUTO_SUBTITLE = 'record.auto-subtitle'; + const THESAURUS_IMPORTED = 'thesaurus.imported'; const THESAURUS_FIELD_LINKED = 'thesaurus.field-linked'; const THESAURUS_CANDIDATE_ACCEPTED_AS_CONCEPT = 'thesaurus.candidate-accepted-as-concept'; diff --git a/lib/Alchemy/Phrasea/WorkerManager/Provider/QueueWorkerServiceProvider.php b/lib/Alchemy/Phrasea/WorkerManager/Provider/QueueWorkerServiceProvider.php index 7de8ef2604..85e141ee5a 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Provider/QueueWorkerServiceProvider.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Provider/QueueWorkerServiceProvider.php @@ -23,6 +23,7 @@ use Alchemy\Phrasea\WorkerManager\Subscriber\AssetsIngestSubscriber; use Alchemy\Phrasea\WorkerManager\Subscriber\ExportSubscriber; use Alchemy\Phrasea\WorkerManager\Subscriber\RecordSubscriber; use Alchemy\Phrasea\WorkerManager\Subscriber\SearchengineSubscriber; +use Alchemy\Phrasea\WorkerManager\Subscriber\SubtitlingSubscriber; use Alchemy\Phrasea\WorkerManager\Subscriber\WebhookSubscriber; use Silex\Application; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -68,6 +69,7 @@ class QueueWorkerServiceProvider implements PluginProviderInterface $dispatcher->addSubscriber(new AssetsIngestSubscriber($app['alchemy_worker.message.publisher'])); $dispatcher->addSubscriber(new SearchengineSubscriber($app['alchemy_worker.message.publisher'])); $dispatcher->addSubscriber(new WebhookSubscriber($app['alchemy_worker.message.publisher'])); + $dispatcher->addSubscriber(new SubtitlingSubscriber(new LazyLocator($app, 'phraseanet.appbox'), $app['monolog'])); return $dispatcher; }) diff --git a/lib/Alchemy/Phrasea/WorkerManager/Subscriber/SubtitlingSubscriber.php b/lib/Alchemy/Phrasea/WorkerManager/Subscriber/SubtitlingSubscriber.php new file mode 100644 index 0000000000..ceefdbc02a --- /dev/null +++ b/lib/Alchemy/Phrasea/WorkerManager/Subscriber/SubtitlingSubscriber.php @@ -0,0 +1,168 @@ +appboxLocator = $appboxLocator; + $this->logger = $logger; + } + + public function onRecordAutoSubtitle(RecordAutoSubtitleEvent $event) + { + $record = $this->getApplicationBox()->get_databox($event->getRecord()->getDataboxId())->get_record($event->getRecord()->getRecordId()); + + if ($record->has_preview() && ($previewLink = $record->get_preview()->get_permalink()) !== null && $event->getMetaStructId()) { + switch ($event->getLanguageSource()) { + case 'En': + $language = 'en-GB'; + break; + case 'De': + $language = 'de-DE'; + break; + case 'Fr': + default: + $language = 'fr-FR'; + break; + } + + $permalinkUrl = $previewLink->get_url()->__toString(); + + $gingerClient = new Client(); + + try { + $response = $gingerClient->post(self::GINGER_BASE_URL.'/media/', [ + 'headers' => [ + 'Authorization' => 'token '.self::GINGER_TOKEN + ], + 'json' => [ + 'url' => $permalinkUrl, + 'language' => $language + ] + ]); + } catch(\Exception $e) { + $this->logger->error($e->getMessage()); + + return 0; + } + + if ($response->getStatusCode() !== 201) { + $this->logger->error("response status : ". $response->getStatusCode()); + + return 0; + } + + $responseMediaBody = $response->getBody()->getContents(); + $responseMediaBody = json_decode($responseMediaBody,true); + + $checkStatus = true; + do { + // first wait 5 second before check subtitling status + sleep(5); + + try { + $response = $gingerClient->get(self::GINGER_BASE_URL.'/task/'.$responseMediaBody['task_id'].'/', [ + 'headers' => [ + 'Authorization' => 'token '.self::GINGER_TOKEN + ] + ]); + } catch (\Exception $e) { + $checkStatus = false; + } + + if ($response->getStatusCode() !== 200) { + $checkStatus = false; + break; + } + + $responseTaskBody = $response->getBody()->getContents(); + $responseTaskBody = json_decode($responseTaskBody,true); + + } while($responseTaskBody['status'] != 'SUCCESS'); + + if (!$checkStatus) { + $this->logger->error("can't check status"); + + return 0; + } + + try { + $response = $gingerClient->get(self::GINGER_BASE_URL.'/media/'.$responseMediaBody['media']['uuid'].'/', [ + 'headers' => [ + 'Authorization' => 'token '.self::GINGER_TOKEN, + 'ACCEPT' => self::GINGER_TRANSCRIPT_FORMAT + ], + 'query' => [ + 'language' => $language + ] + ]); + } catch (\Exception $e) { + $this->logger->error($e->getMessage()); + + return 0; + } + + if ($response->getStatusCode() !== 201) { + $this->logger->error("response status : ". $response->getStatusCode()); + + return 0; + } + + $transcriptContent = $response->getBody()->getContents(); + + $metadatas[0] = [ + 'meta_struct_id' => (int)$event->getMetaStructId(), + 'meta_id' => '', + 'value' => $transcriptContent + ]; + + try { + $record->set_metadatas($metadatas); + } catch (\Exception $e) { + $this->logger->error($e->getMessage()); + + return 0; + } + + $this->logger->error("Auto subtitle SUCCESS"); + } + + return 0; + } + + public static function getSubscribedEvents() + { + return [ + PhraseaEvents::RECORD_AUTO_SUBTITLE => 'onRecordAutoSubtitle', + ]; + } + + /** + * @return \appbox + */ + private function getApplicationBox() + { + $callable = $this->appboxLocator; + + return $callable(); + } +}