From d9f11cbe06197430983fd6beaccbb67274328029 Mon Sep 17 00:00:00 2001 From: aynsix Date: Wed, 24 Jun 2020 19:51:06 +0300 Subject: [PATCH 01/18] ginga subtitle --- .../Controller/Prod/ToolsController.php | 141 ++++++++++++++++-- .../Phrasea/ControllerProvider/Prod/Tools.php | 3 + .../prod/actions/Tools/videoEditor.html.twig | 26 +++- 3 files changed, 153 insertions(+), 17 deletions(-) diff --git a/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php b/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php index 3343a05438..b549650286 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php @@ -22,13 +22,17 @@ 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 PHPExiftool\Reader; 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; @@ -45,7 +49,6 @@ class ToolsController extends Controller if (count($records) == 1) { /** @var \record_adapter $record */ $record = $records->first(); - $databox = $record->getDatabox(); /**Array list of subdefs**/ $listsubdef = array_keys($record-> get_subdefs()); @@ -88,14 +91,13 @@ class ToolsController extends Controller $metadatas = true; } } - $conf = $this->getConf(); return $this->render('prod/actions/Tools/index.html.twig', [ 'records' => $records, 'record' => $record, 'recordSubdefs' => $recordAccessibleSubdefs, 'metadatas' => $metadatas, - 'listsubdef' => $listsubdef + 'listsubdef' => $listsubdef ]); } @@ -118,6 +120,7 @@ class ToolsController extends Controller } foreach ($records as $record) { + /** @var \media_subdef $subdef */ foreach ($record->get_subdefs() as $subdef) { if ($subdef->get_type() !== \media_subdef::TYPE_IMAGE) { continue; @@ -146,6 +149,7 @@ class ToolsController extends Controller foreach ($selection as $record) { $substituted = false; + /** @var \media_subdef $subdef */ foreach ($record->get_subdefs() as $subdef) { if ($subdef->is_substituted()) { $substituted = true; @@ -362,14 +366,6 @@ class ToolsController extends Controller return $this->app->json($return); } - /** - * @return Reader - */ - private function getExifToolReader() - { - return $this->app['exiftool.reader']; - } - /** * @return Alchemyst */ @@ -449,13 +445,130 @@ class ToolsController extends Controller try { $record->set_metadatas($metadatas); } - catch (Exception $e) { + catch (\Exception $e) { return $this->app->json(['success' => false, 'errorMessage' => $e->getMessage()]); } return $this->app->json(['success' => true, 'errorMessage' => '']); } + 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; + } + + $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); + } + public function videoEditorAction(Request $request) { $records = RecordsRequest::fromRequest($this->app, $request, false); @@ -491,7 +604,7 @@ class ToolsController extends Controller $fieldValue = array_pop($fieldValues); $field['value'] = $fieldValue->getValue(); } - $videoTextTrackFields[] = $field; + $videoTextTrackFields[$meta->get_id()] = $field; unset($field); } } diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Tools.php b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Tools.php index 8d6932aecf..07f8810ad4 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Tools.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Tools.php @@ -72,6 +72,9 @@ class Tools implements ControllerProviderInterface, ServiceProviderInterface $controllers->post('/metadata/save/', 'controller.prod.tools:saveMetasAction') ->bind('prod_tools_metadata_save'); + $controllers->post('/auto-subtitle/', 'controller.prod.tools:autoSubtitleAction') + ->bind('prod_tools_auto_subtitle'); + $controllers->get('/videoEditor', 'controller.prod.tools:videoEditorAction'); return $controllers; diff --git a/templates/web/prod/actions/Tools/videoEditor.html.twig b/templates/web/prod/actions/Tools/videoEditor.html.twig index 7e0c3d3109..c31743e6e3 100644 --- a/templates/web/prod/actions/Tools/videoEditor.html.twig +++ b/templates/web/prod/actions/Tools/videoEditor.html.twig @@ -242,9 +242,9 @@

@@ -352,4 +352,24 @@ overlapChapters: {% if overlapChapters != NULL %}{{ overlapChapters }}{% else %}1{% endif %}, } }; + + $('#submit-subtitle-request').on('click', function (e) { + e.preventDefault(); + console.log("auto-subtitle process"); + $.ajax({ + type: 'POST', + url: '/prod/tools/auto-subtitle/', + dataType: 'json', + data: { + databox_id: {{ record.get_base_id }}, + record_id: {{ record.get_record_id }}, + meta_struct_id: $('#subtitle_language_source').val(), + subtitle_language_source: $('#subtitle_language_source option:selected').text() + }, + success: function success(data) { + console.log(data); + } + }); + }); + From afc6f9e1939b99e7845d54a89a9a3007fb433643 Mon Sep 17 00:00:00 2001 From: aynsix Date: Thu, 25 Jun 2020 16:58:56 +0300 Subject: [PATCH 02/18] create auto-subtitle event --- .../Controller/Prod/ToolsController.php | 119 +------------ .../Event/Record/RecordAutoSubtitleEvent.php | 29 +++ lib/Alchemy/Phrasea/Core/PhraseaEvents.php | 2 + .../Provider/QueueWorkerServiceProvider.php | 2 + .../Subscriber/SubtitlingSubscriber.php | 168 ++++++++++++++++++ 5 files changed, 208 insertions(+), 112 deletions(-) create mode 100644 lib/Alchemy/Phrasea/Core/Event/Record/RecordAutoSubtitleEvent.php create mode 100644 lib/Alchemy/Phrasea/WorkerManager/Subscriber/SubtitlingSubscriber.php 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(); + } +} From a0d3d8db51a6b2c35ed7269980c3a60e9e4f5f21 Mon Sep 17 00:00:00 2001 From: aynsix Date: Thu, 25 Jun 2020 17:41:38 +0300 Subject: [PATCH 03/18] rename class --- .../WorkerManager/Provider/QueueWorkerServiceProvider.php | 4 ++-- .../{SubtitlingSubscriber.php => SubtitleSubscriber.php} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename lib/Alchemy/Phrasea/WorkerManager/Subscriber/{SubtitlingSubscriber.php => SubtitleSubscriber.php} (98%) diff --git a/lib/Alchemy/Phrasea/WorkerManager/Provider/QueueWorkerServiceProvider.php b/lib/Alchemy/Phrasea/WorkerManager/Provider/QueueWorkerServiceProvider.php index 85e141ee5a..f4ee18eaf5 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Provider/QueueWorkerServiceProvider.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Provider/QueueWorkerServiceProvider.php @@ -23,7 +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\SubtitleSubscriber; use Alchemy\Phrasea\WorkerManager\Subscriber\WebhookSubscriber; use Silex\Application; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -69,7 +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'])); + $dispatcher->addSubscriber(new SubtitleSubscriber(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/SubtitleSubscriber.php similarity index 98% rename from lib/Alchemy/Phrasea/WorkerManager/Subscriber/SubtitlingSubscriber.php rename to lib/Alchemy/Phrasea/WorkerManager/Subscriber/SubtitleSubscriber.php index ceefdbc02a..51c3617e55 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Subscriber/SubtitlingSubscriber.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Subscriber/SubtitleSubscriber.php @@ -8,7 +8,7 @@ use GuzzleHttp\Client; use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -class SubtitlingSubscriber implements EventSubscriberInterface +class SubtitleSubscriber implements EventSubscriberInterface { const GINGER_BASE_URL = "https://test.api.ginger.studio/recognition/speech"; const GINGER_TOKEN = "39c6011d-3bbe-4f39-95d0-a327d320ded4"; From 47f8d2e6df33db64c0ee57480d97a044c4c9591c Mon Sep 17 00:00:00 2001 From: aynsix Date: Thu, 25 Jun 2020 18:40:10 +0300 Subject: [PATCH 04/18] test --- lib/Alchemy/Phrasea/Application.php | 3 +++ .../WorkerManager/Provider/QueueWorkerServiceProvider.php | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/Alchemy/Phrasea/Application.php b/lib/Alchemy/Phrasea/Application.php index 4d0d2ab2d5..1132aef6c9 100644 --- a/lib/Alchemy/Phrasea/Application.php +++ b/lib/Alchemy/Phrasea/Application.php @@ -27,6 +27,7 @@ use Alchemy\Phrasea\Core\Event\Subscriber\PhraseaInstallSubscriber; use Alchemy\Phrasea\Core\Event\Subscriber\RegistrationSubscriber; use Alchemy\Phrasea\Core\Event\Subscriber\ValidationSubscriber; use Alchemy\Phrasea\Core\Event\Subscriber\WebhookUserEventSubscriber; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Core\MetaProvider\DatabaseMetaProvider; use Alchemy\Phrasea\Core\MetaProvider\HttpStackMetaProvider; use Alchemy\Phrasea\Core\MetaProvider\MediaUtilitiesMetaServiceProvider; @@ -89,6 +90,7 @@ use Alchemy\Phrasea\Media\TechnicalDataServiceProvider; use Alchemy\Phrasea\Model\Entities\User; use Alchemy\Phrasea\WorkerManager\Provider\AlchemyWorkerServiceProvider; use Alchemy\Phrasea\WorkerManager\Provider\QueueWorkerServiceProvider; +use Alchemy\Phrasea\WorkerManager\Subscriber\SubtitleSubscriber; use Alchemy\QueueProvider\QueueServiceProvider; use Alchemy\WorkerProvider\WorkerServiceProvider; use Doctrine\DBAL\Event\ConnectionEventArgs; @@ -749,6 +751,7 @@ class Application extends SilexApplication $dispatcher->addSubscriber(new LazaretSubscriber($app)); $dispatcher->addSubscriber(new ValidationSubscriber($app)); $dispatcher->addSubscriber(new WebhookUserEventSubscriber($app)); + $dispatcher->addSubscriber(new SubtitleSubscriber(new LazyLocator($app, 'phraseanet.appbox'), $app['monolog'])); return $dispatcher; }) diff --git a/lib/Alchemy/Phrasea/WorkerManager/Provider/QueueWorkerServiceProvider.php b/lib/Alchemy/Phrasea/WorkerManager/Provider/QueueWorkerServiceProvider.php index f4ee18eaf5..7de8ef2604 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Provider/QueueWorkerServiceProvider.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Provider/QueueWorkerServiceProvider.php @@ -23,7 +23,6 @@ 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\SubtitleSubscriber; use Alchemy\Phrasea\WorkerManager\Subscriber\WebhookSubscriber; use Silex\Application; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -69,7 +68,6 @@ 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 SubtitleSubscriber(new LazyLocator($app, 'phraseanet.appbox'), $app['monolog'])); return $dispatcher; }) From e790c2dc6eb890d4c80affa4caddfd56827da493 Mon Sep 17 00:00:00 2001 From: aynsix Date: Fri, 3 Jul 2020 11:54:19 +0300 Subject: [PATCH 05/18] create subtitle worker --- lib/Alchemy/Phrasea/Application.php | 4 - .../Event/Record/RecordAutoSubtitleEvent.php | 2 +- .../Provider/RepositoriesServiceProvider.php | 3 + lib/Alchemy/Phrasea/Core/Version.php | 3 +- .../Phrasea/Model/Entities/WorkerJob.php | 150 +++++++++++++ .../Repositories/WorkerJobRepository.php | 22 ++ .../Provider/AlchemyWorkerServiceProvider.php | 10 + .../Provider/QueueWorkerServiceProvider.php | 2 + .../WorkerManager/Queue/AMQPConnection.php | 5 +- .../WorkerManager/Queue/MessagePublisher.php | 6 + .../Subscriber/SubtitleSubscriber.php | 168 ++++----------- .../WorkerManager/Worker/MainQueueWorker.php | 49 +++++ .../WorkerManager/Worker/SubtitleWorker.php | 200 ++++++++++++++++++ 13 files changed, 484 insertions(+), 140 deletions(-) create mode 100644 lib/Alchemy/Phrasea/Model/Entities/WorkerJob.php create mode 100644 lib/Alchemy/Phrasea/Model/Repositories/WorkerJobRepository.php create mode 100644 lib/Alchemy/Phrasea/WorkerManager/Worker/MainQueueWorker.php create mode 100644 lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php diff --git a/lib/Alchemy/Phrasea/Application.php b/lib/Alchemy/Phrasea/Application.php index 1132aef6c9..82afe34139 100644 --- a/lib/Alchemy/Phrasea/Application.php +++ b/lib/Alchemy/Phrasea/Application.php @@ -27,7 +27,6 @@ use Alchemy\Phrasea\Core\Event\Subscriber\PhraseaInstallSubscriber; use Alchemy\Phrasea\Core\Event\Subscriber\RegistrationSubscriber; use Alchemy\Phrasea\Core\Event\Subscriber\ValidationSubscriber; use Alchemy\Phrasea\Core\Event\Subscriber\WebhookUserEventSubscriber; -use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Core\MetaProvider\DatabaseMetaProvider; use Alchemy\Phrasea\Core\MetaProvider\HttpStackMetaProvider; use Alchemy\Phrasea\Core\MetaProvider\MediaUtilitiesMetaServiceProvider; @@ -90,10 +89,8 @@ use Alchemy\Phrasea\Media\TechnicalDataServiceProvider; use Alchemy\Phrasea\Model\Entities\User; use Alchemy\Phrasea\WorkerManager\Provider\AlchemyWorkerServiceProvider; use Alchemy\Phrasea\WorkerManager\Provider\QueueWorkerServiceProvider; -use Alchemy\Phrasea\WorkerManager\Subscriber\SubtitleSubscriber; use Alchemy\QueueProvider\QueueServiceProvider; use Alchemy\WorkerProvider\WorkerServiceProvider; -use Doctrine\DBAL\Event\ConnectionEventArgs; use MediaVorus\Media\MediaInterface; use MediaVorus\MediaVorus; use Monolog\Handler\ErrorLogHandler; @@ -751,7 +748,6 @@ class Application extends SilexApplication $dispatcher->addSubscriber(new LazaretSubscriber($app)); $dispatcher->addSubscriber(new ValidationSubscriber($app)); $dispatcher->addSubscriber(new WebhookUserEventSubscriber($app)); - $dispatcher->addSubscriber(new SubtitleSubscriber(new LazyLocator($app, 'phraseanet.appbox'), $app['monolog'])); return $dispatcher; }) diff --git a/lib/Alchemy/Phrasea/Core/Event/Record/RecordAutoSubtitleEvent.php b/lib/Alchemy/Phrasea/Core/Event/Record/RecordAutoSubtitleEvent.php index 5f502930e5..3587a962f2 100644 --- a/lib/Alchemy/Phrasea/Core/Event/Record/RecordAutoSubtitleEvent.php +++ b/lib/Alchemy/Phrasea/Core/Event/Record/RecordAutoSubtitleEvent.php @@ -14,7 +14,7 @@ class RecordAutoSubtitleEvent extends RecordEvent parent::__construct($record); $this->languageSource = $languageSource; - $this->metaStructId = $metaStructId; + $this->metaStructId = $metaStructId; } public function getLanguageSource() diff --git a/lib/Alchemy/Phrasea/Core/Provider/RepositoriesServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/RepositoriesServiceProvider.php index 72f64fa52c..b81835842e 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/RepositoriesServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/RepositoriesServiceProvider.php @@ -150,6 +150,9 @@ class RepositoriesServiceProvider implements ServiceProviderInterface $app['repo.worker-running-job'] = $app->share(function (PhraseaApplication $app) { return $app['orm.em']->getRepository('Phraseanet:WorkerRunningJob'); }); + $app['repo.worker-job'] = $app->share(function (PhraseaApplication $app) { + return $app['orm.em']->getRepository('Phraseanet:WorkerJob'); + }); $app['repo.worker-running-populate'] = $app->share(function (PhraseaApplication $app) { return $app['orm.em']->getRepository('Phraseanet:WorkerRunningPopulate'); }); diff --git a/lib/Alchemy/Phrasea/Core/Version.php b/lib/Alchemy/Phrasea/Core/Version.php index 9ac3908119..4afc5d48ea 100644 --- a/lib/Alchemy/Phrasea/Core/Version.php +++ b/lib/Alchemy/Phrasea/Core/Version.php @@ -16,8 +16,7 @@ class Version /** * @var string */ - - private $number = '4.1.1'; + private $number = '4.1.2'; /** * @var string diff --git a/lib/Alchemy/Phrasea/Model/Entities/WorkerJob.php b/lib/Alchemy/Phrasea/Model/Entities/WorkerJob.php new file mode 100644 index 0000000000..dbeb5d319a --- /dev/null +++ b/lib/Alchemy/Phrasea/Model/Entities/WorkerJob.php @@ -0,0 +1,150 @@ +id; + } + + public function setType($type) + { + $this->type = $type; + + return $this; + } + + public function getType() + { + return $this->type; + } + + /** + * @param array $data + * + * @return WorkerJob + */ + public function setData(array $data) + { + $this->data = $data; + + return $this; + } + + public function getData() + { + return $this->data; + } + + public function setStatus($status) + { + $this->status = $status; + + return $this; + } + + public function getStatus() + { + return $this->status; + } + + /** + * @return \DateTime + */ + public function getCreated() + { + return $this->created; + } + + /** + * @param \DateTime $finished + * @return $this + */ + public function setFinished(\DateTime $finished) + { + $this->finished = $finished; + + return $this; + } + + /** + * @return mixed + */ + public function getFinished() + { + return $this->finished; + } + + /** + * @param \DateTime $started + * @return $this + */ + public function setStarted(\DateTime $started) + { + $this->started = $started; + + return $this; + } + + /** + * @return mixed + */ + public function getStarted() + { + return $this->started; + } +} diff --git a/lib/Alchemy/Phrasea/Model/Repositories/WorkerJobRepository.php b/lib/Alchemy/Phrasea/Model/Repositories/WorkerJobRepository.php new file mode 100644 index 0000000000..dd20a9c95d --- /dev/null +++ b/lib/Alchemy/Phrasea/Model/Repositories/WorkerJobRepository.php @@ -0,0 +1,22 @@ +_em->getConnection()->ping() === false) { + $this->_em->getConnection()->close(); + $this->_em->getConnection()->connect(); + } + } +} diff --git a/lib/Alchemy/Phrasea/WorkerManager/Provider/AlchemyWorkerServiceProvider.php b/lib/Alchemy/Phrasea/WorkerManager/Provider/AlchemyWorkerServiceProvider.php index ecabd28649..a551aef42c 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Provider/AlchemyWorkerServiceProvider.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Provider/AlchemyWorkerServiceProvider.php @@ -11,11 +11,13 @@ use Alchemy\Phrasea\WorkerManager\Worker\CreateRecordWorker; use Alchemy\Phrasea\WorkerManager\Worker\DeleteRecordWorker; use Alchemy\Phrasea\WorkerManager\Worker\ExportMailWorker; use Alchemy\Phrasea\WorkerManager\Worker\Factory\CallableWorkerFactory; +use Alchemy\Phrasea\WorkerManager\Worker\MainQueueWorker; use Alchemy\Phrasea\WorkerManager\Worker\PopulateIndexWorker; use Alchemy\Phrasea\WorkerManager\Worker\ProcessPool; use Alchemy\Phrasea\WorkerManager\Worker\PullAssetsWorker; use Alchemy\Phrasea\WorkerManager\Worker\Resolver\TypeBasedWorkerResolver; use Alchemy\Phrasea\WorkerManager\Worker\SubdefCreationWorker; +use Alchemy\Phrasea\WorkerManager\Worker\SubtitleWorker; use Alchemy\Phrasea\WorkerManager\Worker\WebhookWorker; use Alchemy\Phrasea\WorkerManager\Worker\WorkerInvoker; use Alchemy\Phrasea\WorkerManager\Worker\WriteMetadatasWorker; @@ -128,6 +130,14 @@ class AlchemyWorkerServiceProvider implements PluginProviderInterface return (new DeleteRecordWorker()) ->setApplicationBox($app['phraseanet.appbox']); })); + + $app['alchemy_worker.type_based_worker_resolver']->addFactory(MessagePublisher::SUBTITLE_TYPE, new CallableWorkerFactory(function () use ($app) { + return new SubtitleWorker($app['repo.worker-job'], new LazyLocator($app, 'phraseanet.appbox'), $app['alchemy_worker.logger']); + })); + + $app['alchemy_worker.type_based_worker_resolver']->addFactory(MessagePublisher::MAIN_QUEUE_TYPE, new CallableWorkerFactory(function () use ($app) { + return new MainQueueWorker($app['alchemy_worker.message.publisher'], $app['repo.worker-job']); + })); } /** diff --git a/lib/Alchemy/Phrasea/WorkerManager/Provider/QueueWorkerServiceProvider.php b/lib/Alchemy/Phrasea/WorkerManager/Provider/QueueWorkerServiceProvider.php index 7de8ef2604..0e1977c4c2 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\SubtitleSubscriber; 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 SubtitleSubscriber($app['repo.worker-job'], $app['alchemy_worker.message.publisher'])); return $dispatcher; }) diff --git a/lib/Alchemy/Phrasea/WorkerManager/Queue/AMQPConnection.php b/lib/Alchemy/Phrasea/WorkerManager/Queue/AMQPConnection.php index e1d6fed0ba..4508741ca7 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Queue/AMQPConnection.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Queue/AMQPConnection.php @@ -29,7 +29,9 @@ class AMQPConnection MessagePublisher::CREATE_RECORD_TYPE => MessagePublisher::CREATE_RECORD_QUEUE, MessagePublisher::PULL_QUEUE => MessagePublisher::PULL_QUEUE, MessagePublisher::POPULATE_INDEX_TYPE => MessagePublisher::POPULATE_INDEX_QUEUE, - MessagePublisher::DELETE_RECORD_TYPE => MessagePublisher::DELETE_RECORD_QUEUE + MessagePublisher::DELETE_RECORD_TYPE => MessagePublisher::DELETE_RECORD_QUEUE, + MessagePublisher::MAIN_QUEUE_TYPE => MessagePublisher::MAIN_QUEUE, + MessagePublisher::SUBTITLE_TYPE => MessagePublisher::SUBTITLE_QUEUE ]; // the corresponding worker queues and retry queues, loop queue @@ -44,7 +46,6 @@ class AMQPConnection MessagePublisher::PULL_QUEUE => MessagePublisher::LOOP_PULL_QUEUE ]; - // default message TTL in retry queue in millisecond public static $defaultFailedQueues = [ MessagePublisher::WRITE_METADATAS_TYPE => MessagePublisher::FAILED_METADATAS_QUEUE, MessagePublisher::SUBDEF_CREATION_TYPE => MessagePublisher::FAILED_SUBDEF_QUEUE, diff --git a/lib/Alchemy/Phrasea/WorkerManager/Queue/MessagePublisher.php b/lib/Alchemy/Phrasea/WorkerManager/Queue/MessagePublisher.php index c191017bf8..1a236e2a08 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Queue/MessagePublisher.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Queue/MessagePublisher.php @@ -18,6 +18,12 @@ class MessagePublisher const WEBHOOK_TYPE = 'webhook'; const POPULATE_INDEX_TYPE = 'populateIndex'; const PULL_ASSETS_TYPE = 'pullAssets'; + const SUBTITLE_TYPE = 'subtitle'; + const MAIN_QUEUE_TYPE = 'mainQueue'; + + + const MAIN_QUEUE = 'main-queue'; + const SUBTITLE_QUEUE = 'subtitle-queue'; // worker queue to be consumed, when no ack , it is requeued to the retry queue const EXPORT_QUEUE = 'export-queue'; diff --git a/lib/Alchemy/Phrasea/WorkerManager/Subscriber/SubtitleSubscriber.php b/lib/Alchemy/Phrasea/WorkerManager/Subscriber/SubtitleSubscriber.php index 51c3617e55..d3037401d1 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Subscriber/SubtitleSubscriber.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Subscriber/SubtitleSubscriber.php @@ -2,151 +2,66 @@ namespace Alchemy\Phrasea\WorkerManager\Subscriber; +use Alchemy\Phrasea\Application\Helper\EntityManagerAware; use Alchemy\Phrasea\Core\Event\Record\RecordAutoSubtitleEvent; use Alchemy\Phrasea\Core\PhraseaEvents; -use GuzzleHttp\Client; -use Psr\Log\LoggerInterface; +use Alchemy\Phrasea\Model\Entities\WorkerJob; +use Alchemy\Phrasea\Model\Repositories\WorkerJobRepository; +use Alchemy\Phrasea\WorkerManager\Queue\MessagePublisher; use Symfony\Component\EventDispatcher\EventSubscriberInterface; class SubtitleSubscriber implements EventSubscriberInterface { - 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"; + private $messagePublisher; - /** - * @var callable - */ - private $appboxLocator; + /** @var WorkerJobRepository $repoWorkerJob*/ + private $repoWorkerJob; - private $logger; - - public function __construct(callable $appboxLocator, LoggerInterface $logger) + public function __construct(WorkerJobRepository $repoWorkerJob, MessagePublisher $messagePublisher) { - $this->appboxLocator = $appboxLocator; - $this->logger = $logger; + $this->repoWorkerJob = $repoWorkerJob; + $this->messagePublisher = $messagePublisher; } public function onRecordAutoSubtitle(RecordAutoSubtitleEvent $event) { - $record = $this->getApplicationBox()->get_databox($event->getRecord()->getDataboxId())->get_record($event->getRecord()->getRecordId()); + $em = $this->repoWorkerJob->getEntityManager(); - 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; - } + $data = [ + "databoxId" => $event->getRecord()->getDataboxId(), + "recordId" => $event->getRecord()->getRecordId(), + "langageSource" => $event->getLanguageSource(), + "metaStructureId" => $event->getMetaStructId() + ]; - $permalinkUrl = $previewLink->get_url()->__toString(); + $this->repoWorkerJob->reconnect(); + $em->beginTransaction(); - $gingerClient = new Client(); + try { + $workerJob = new WorkerJob(); + $workerJob + ->setType(MessagePublisher::SUBTITLE_TYPE) + ->setData($data) + ->setStatus(WorkerJob::WAITING) + ; - 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()); + $em->persist($workerJob); + $em->flush(); - return 0; - } + $em->commit(); - if ($response->getStatusCode() !== 201) { - $this->logger->error("response status : ". $response->getStatusCode()); + $data['workerId'] = $workerJob->getId(); + $data['type'] = MessagePublisher::SUBTITLE_TYPE; - 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 + $payload = [ + 'message_type' => MessagePublisher::MAIN_QUEUE_TYPE, + 'payload' => $data ]; - try { - $record->set_metadatas($metadatas); - } catch (\Exception $e) { - $this->logger->error($e->getMessage()); - - return 0; - } - - $this->logger->error("Auto subtitle SUCCESS"); + $this->messagePublisher->publishMessage($payload, MessagePublisher::MAIN_QUEUE); + } catch (\Exception $e) { + $em->rollback(); } - - return 0; } public static function getSubscribedEvents() @@ -156,13 +71,4 @@ class SubtitleSubscriber implements EventSubscriberInterface ]; } - /** - * @return \appbox - */ - private function getApplicationBox() - { - $callable = $this->appboxLocator; - - return $callable(); - } } diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/MainQueueWorker.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/MainQueueWorker.php new file mode 100644 index 0000000000..82471f2b88 --- /dev/null +++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/MainQueueWorker.php @@ -0,0 +1,49 @@ +messagePublisher = $messagePublisher; + $this->repoWorkerJob = $repoWorkerJob; + } + + public function process(array $payload) + { + + // if needed do treatement here depending on the type + $queue = null; + $messageType = ''; + + switch ($payload['type']) { + case MessagePublisher::SUBTITLE_TYPE: + $queue = MessagePublisher::SUBTITLE_QUEUE; + $messageType = $payload['type']; + unset($payload['type']); + + break; + + } + + $data = [ + 'message_type' => $messageType, + 'payload' => $payload + ]; + + if ($queue != null) { + $this->messagePublisher->publishMessage($data, $queue); + } + } +} diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php new file mode 100644 index 0000000000..bf4194c11d --- /dev/null +++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php @@ -0,0 +1,200 @@ +repoWorkerJob = $repoWorkerJob; + $this->appboxLocator = $appboxLocator; + $this->logger = $logger; + } + + public function process(array $payload) + { + /** @var WorkerJob $workerJob */ + $workerJob = $this->repoWorkerJob->find($payload['workerId']); + if ($workerJob == null) { + $this->logger->error("WorkerId not found"); + + return 0; + } + + $workerJob->setStatus(WorkerJob::RUNNING) + ->setStarted(new \DateTime('now')); + + $em = $this->repoWorkerJob->getEntityManager(); + $this->repoWorkerJob->reconnect(); + $em->persist($workerJob); + $em->flush(); + + $record = $this->getApplicationBox()->get_databox($payload['databoxId'])->get_record($payload['recordId']); + + if ($record->has_preview() && ($previewLink = $record->get_preview()->get_permalink()) !== null && $payload['metaStructureId']) { + switch ($payload['langageSource']) { + 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()); + $this->jobFinished($workerJob); + + return 0; + } + + if ($response->getStatusCode() !== 201) { + $this->logger->error("response status : ". $response->getStatusCode()); + $this->jobFinished($workerJob); + + 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"); + $this->jobFinished($workerJob); + + 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()); + $this->jobFinished($workerJob); + + return 0; + } + + if ($response->getStatusCode() !== 201) { + $this->logger->error("response status : ". $response->getStatusCode()); + $this->jobFinished($workerJob); + + return 0; + } + + $transcriptContent = $response->getBody()->getContents(); + + $metadatas[0] = [ + 'meta_struct_id' => (int)$payload['metaStructureId'], + 'meta_id' => '', + 'value' => $transcriptContent + ]; + + try { + $record->set_metadatas($metadatas); + } catch (\Exception $e) { + $this->logger->error($e->getMessage()); + $this->jobFinished($workerJob); + + return 0; + } + + $this->logger->error("Auto subtitle SUCCESS"); + } + + $this->jobFinished($workerJob); + + return 0; + } + + /** + * @return \appbox + */ + private function getApplicationBox() + { + $callable = $this->appboxLocator; + + return $callable(); + } + + private function jobFinished(WorkerJob $workerJob) + { + $workerJob->setStatus(WorkerJob::FINISHED) + ->setFinished(new \DateTime('now')); + + $em = $this->repoWorkerJob->getEntityManager(); + $this->repoWorkerJob->reconnect(); + + $em->persist($workerJob); + $em->flush(); + } +} From 1bdc82232fd0251f5c6f94d904f66fe172e8729e Mon Sep 17 00:00:00 2001 From: aynsix Date: Fri, 3 Jul 2020 12:59:45 +0300 Subject: [PATCH 06/18] fix permalink url --- lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php | 7 ++++++- .../Core/Event/Record/RecordAutoSubtitleEvent.php | 9 ++++++++- .../WorkerManager/Subscriber/SubtitleSubscriber.php | 1 + .../Phrasea/WorkerManager/Worker/SubtitleWorker.php | 6 ++---- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php b/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php index 4bdd562424..f71114b3a4 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php @@ -456,9 +456,14 @@ class ToolsController extends Controller (int)$request->request->get("record_id") ); + $permalinkUrl = ''; + if ($record->has_preview() && ($previewLink = $record->get_preview()->get_permalink()) != null) { + $permalinkUrl = $previewLink->get_url()->__toString(); + } + $this->dispatch( PhraseaEvents::RECORD_AUTO_SUBTITLE, - new RecordAutoSubtitleEvent($record, $request->request->get("subtitle_language_source"), $request->request->get("meta_struct_id")) + new RecordAutoSubtitleEvent($record, $permalinkUrl, $request->request->get("subtitle_language_source"), $request->request->get("meta_struct_id")) ); return $this->app->json(["status" => "dispatch"]); diff --git a/lib/Alchemy/Phrasea/Core/Event/Record/RecordAutoSubtitleEvent.php b/lib/Alchemy/Phrasea/Core/Event/Record/RecordAutoSubtitleEvent.php index 3587a962f2..044303161a 100644 --- a/lib/Alchemy/Phrasea/Core/Event/Record/RecordAutoSubtitleEvent.php +++ b/lib/Alchemy/Phrasea/Core/Event/Record/RecordAutoSubtitleEvent.php @@ -8,13 +8,15 @@ class RecordAutoSubtitleEvent extends RecordEvent { private $languageSource; private $metaStructId; + private $permalinkUrl; - public function __construct(RecordInterface $record, $languageSource, $metaStructId) + public function __construct(RecordInterface $record, $permalinkUrl, $languageSource, $metaStructId) { parent::__construct($record); $this->languageSource = $languageSource; $this->metaStructId = $metaStructId; + $this->permalinkUrl = $permalinkUrl; } public function getLanguageSource() @@ -26,4 +28,9 @@ class RecordAutoSubtitleEvent extends RecordEvent { return $this->metaStructId; } + + 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 d3037401d1..3d207728fd 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Subscriber/SubtitleSubscriber.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Subscriber/SubtitleSubscriber.php @@ -30,6 +30,7 @@ class SubtitleSubscriber implements EventSubscriberInterface $data = [ "databoxId" => $event->getRecord()->getDataboxId(), "recordId" => $event->getRecord()->getRecordId(), + "permalinkUrl" => $event->getPermalinkUrl(), "langageSource" => $event->getLanguageSource(), "metaStructureId" => $event->getMetaStructId() ]; diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php index bf4194c11d..668b43c2c6 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php @@ -50,7 +50,7 @@ class SubtitleWorker implements WorkerInterface $record = $this->getApplicationBox()->get_databox($payload['databoxId'])->get_record($payload['recordId']); - if ($record->has_preview() && ($previewLink = $record->get_preview()->get_permalink()) !== null && $payload['metaStructureId']) { + if ($payload['permalinkUrl'] != '' && $payload['metaStructureId']) { switch ($payload['langageSource']) { case 'En': $language = 'en-GB'; @@ -64,8 +64,6 @@ class SubtitleWorker implements WorkerInterface break; } - $permalinkUrl = $previewLink->get_url()->__toString(); - $gingerClient = new Client(); try { @@ -74,7 +72,7 @@ class SubtitleWorker implements WorkerInterface 'Authorization' => 'token '.self::GINGER_TOKEN ], 'json' => [ - 'url' => $permalinkUrl, + 'url' => $payload['permalinkUrl'], 'language' => $language ] ]); From 122c4c8607c8adb7a8da716dc53d88f21497db16 Mon Sep 17 00:00:00 2001 From: aynsix Date: Fri, 3 Jul 2020 13:18:34 +0300 Subject: [PATCH 07/18] add log --- .../Phrasea/WorkerManager/Worker/SubtitleWorker.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php index 668b43c2c6..785c457758 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php @@ -84,7 +84,7 @@ class SubtitleWorker implements WorkerInterface } if ($response->getStatusCode() !== 201) { - $this->logger->error("response status : ". $response->getStatusCode()); + $this->logger->error("response status /media/ : ". $response->getStatusCode()); $this->jobFinished($workerJob); return 0; @@ -97,7 +97,7 @@ class SubtitleWorker implements WorkerInterface do { // first wait 5 second before check subtitling status sleep(5); - + $this->logger->info("bigin to check status"); try { $response = $gingerClient->get(self::GINGER_BASE_URL.'/task/'.$responseMediaBody['task_id'].'/', [ 'headers' => [ @@ -106,10 +106,13 @@ class SubtitleWorker implements WorkerInterface ]); } catch (\Exception $e) { $checkStatus = false; + + break; } if ($response->getStatusCode() !== 200) { $checkStatus = false; + break; } @@ -142,8 +145,8 @@ class SubtitleWorker implements WorkerInterface return 0; } - if ($response->getStatusCode() !== 201) { - $this->logger->error("response status : ". $response->getStatusCode()); + if ($response->getStatusCode() !== 200) { + $this->logger->error("response status /media/uuid : ". $response->getStatusCode()); $this->jobFinished($workerJob); return 0; From e878a60c5e7e2899118c6cd7e7f76a5203e5f51b Mon Sep 17 00:00:00 2001 From: aynsix Date: Fri, 3 Jul 2020 13:23:08 +0300 Subject: [PATCH 08/18] fix log --- lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php index 785c457758..3b35b8143f 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php @@ -169,7 +169,7 @@ class SubtitleWorker implements WorkerInterface return 0; } - $this->logger->error("Auto subtitle SUCCESS"); + $this->logger->info("Auto subtitle SUCCESS"); } $this->jobFinished($workerJob); From 554d201c2da92606c66639a4c05d3cd987fa6825 Mon Sep 17 00:00:00 2001 From: aynsix Date: Fri, 3 Jul 2020 15:16:56 +0300 Subject: [PATCH 09/18] fix repo lazylocator --- .../Provider/QueueWorkerServiceProvider.php | 2 +- .../Subscriber/SubtitleSubscriber.php | 21 +++++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/Alchemy/Phrasea/WorkerManager/Provider/QueueWorkerServiceProvider.php b/lib/Alchemy/Phrasea/WorkerManager/Provider/QueueWorkerServiceProvider.php index 0e1977c4c2..6bdf421fbf 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Provider/QueueWorkerServiceProvider.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Provider/QueueWorkerServiceProvider.php @@ -69,7 +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 SubtitleSubscriber($app['repo.worker-job'], $app['alchemy_worker.message.publisher'])); + $dispatcher->addSubscriber(new SubtitleSubscriber(new LazyLocator($app, 'repo.worker-job'), $app['alchemy_worker.message.publisher'])); return $dispatcher; }) diff --git a/lib/Alchemy/Phrasea/WorkerManager/Subscriber/SubtitleSubscriber.php b/lib/Alchemy/Phrasea/WorkerManager/Subscriber/SubtitleSubscriber.php index 3d207728fd..bed3a8eb4c 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Subscriber/SubtitleSubscriber.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Subscriber/SubtitleSubscriber.php @@ -2,7 +2,6 @@ namespace Alchemy\Phrasea\WorkerManager\Subscriber; -use Alchemy\Phrasea\Application\Helper\EntityManagerAware; use Alchemy\Phrasea\Core\Event\Record\RecordAutoSubtitleEvent; use Alchemy\Phrasea\Core\PhraseaEvents; use Alchemy\Phrasea\Model\Entities\WorkerJob; @@ -17,14 +16,19 @@ class SubtitleSubscriber implements EventSubscriberInterface /** @var WorkerJobRepository $repoWorkerJob*/ private $repoWorkerJob; - public function __construct(WorkerJobRepository $repoWorkerJob, MessagePublisher $messagePublisher) + /** @var callable */ + private $repoWorkerJobLocator; + + public function __construct(callable $repoWorkerJobLocator, MessagePublisher $messagePublisher) { - $this->repoWorkerJob = $repoWorkerJob; - $this->messagePublisher = $messagePublisher; + $this->repoWorkerJobLocator = $repoWorkerJobLocator; + $this->messagePublisher = $messagePublisher; } public function onRecordAutoSubtitle(RecordAutoSubtitleEvent $event) { + $this->repoWorkerJob = $this->getRepoWorkerJob(); + $em = $this->repoWorkerJob->getEntityManager(); $data = [ @@ -72,4 +76,13 @@ class SubtitleSubscriber implements EventSubscriberInterface ]; } + /** + * @return WorkerJobRepository + */ + private function getRepoWorkerJob() + { + $callable = $this->repoWorkerJobLocator; + + return $callable(); + } } From 8d1fd1703bcf1e1a531c7c1f7c34259bf7a715be Mon Sep 17 00:00:00 2001 From: aynsix Date: Fri, 3 Jul 2020 16:13:28 +0300 Subject: [PATCH 10/18] fix version to 4.1.1 --- lib/Alchemy/Phrasea/Core/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Alchemy/Phrasea/Core/Version.php b/lib/Alchemy/Phrasea/Core/Version.php index 4afc5d48ea..79fe15eb4b 100644 --- a/lib/Alchemy/Phrasea/Core/Version.php +++ b/lib/Alchemy/Phrasea/Core/Version.php @@ -16,7 +16,7 @@ class Version /** * @var string */ - private $number = '4.1.2'; + private $number = '4.1.1'; /** * @var string From 22302982a47c3ef109725576d7f25243824353d6 Mon Sep 17 00:00:00 2001 From: aynsix Date: Mon, 6 Jul 2020 16:27:43 +0300 Subject: [PATCH 11/18] set ginga config --- config/configuration.sample.yml | 6 ++++ .../Provider/AlchemyWorkerServiceProvider.php | 2 +- .../WorkerManager/Worker/SubtitleWorker.php | 33 ++++++++++++------- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/config/configuration.sample.yml b/config/configuration.sample.yml index 968d309c88..b65a9fe0c5 100644 --- a/config/configuration.sample.yml +++ b/config/configuration.sample.yml @@ -328,6 +328,12 @@ workers: password: guest vhost: / +externalservice: + ginga: + service_base_url: https://base.uri + token: 39c6011d + transcript_format: text/vtt + user_account: deleting_policies: email_confirmation: true diff --git a/lib/Alchemy/Phrasea/WorkerManager/Provider/AlchemyWorkerServiceProvider.php b/lib/Alchemy/Phrasea/WorkerManager/Provider/AlchemyWorkerServiceProvider.php index a551aef42c..2011ef9921 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Provider/AlchemyWorkerServiceProvider.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Provider/AlchemyWorkerServiceProvider.php @@ -132,7 +132,7 @@ class AlchemyWorkerServiceProvider implements PluginProviderInterface })); $app['alchemy_worker.type_based_worker_resolver']->addFactory(MessagePublisher::SUBTITLE_TYPE, new CallableWorkerFactory(function () use ($app) { - return new SubtitleWorker($app['repo.worker-job'], new LazyLocator($app, 'phraseanet.appbox'), $app['alchemy_worker.logger']); + return new SubtitleWorker($app['repo.worker-job'], $app['conf'], new LazyLocator($app, 'phraseanet.appbox'), $app['alchemy_worker.logger']); })); $app['alchemy_worker.type_based_worker_resolver']->addFactory(MessagePublisher::MAIN_QUEUE_TYPE, new CallableWorkerFactory(function () use ($app) { diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php index 3b35b8143f..82cc560952 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php @@ -2,6 +2,7 @@ namespace Alchemy\Phrasea\WorkerManager\Worker; +use Alchemy\Phrasea\Core\Configuration\PropertyAccess; use Alchemy\Phrasea\Model\Entities\WorkerJob; use Alchemy\Phrasea\Model\Repositories\WorkerJobRepository; use GuzzleHttp\Client; @@ -9,29 +10,37 @@ use Psr\Log\LoggerInterface; class SubtitleWorker implements WorkerInterface { - 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"; - /** * @var callable */ private $appboxLocator; private $logger; + private $conf; /** @var WorkerJobRepository $repoWorkerJob*/ private $repoWorkerJob; - public function __construct(WorkerJobRepository $repoWorkerJob, callable $appboxLocator, LoggerInterface $logger) + public function __construct(WorkerJobRepository $repoWorkerJob, PropertyAccess $conf, callable $appboxLocator, LoggerInterface $logger) { $this->repoWorkerJob = $repoWorkerJob; + $this->conf = $conf; $this->appboxLocator = $appboxLocator; $this->logger = $logger; } public function process(array $payload) { + $gingaBaseurl = $this->conf->get(['externalservice', 'ginga', 'service_base_url']); + $gingaToken = $this->conf->get(['externalservice', 'ginga', 'token']); + $gingaTranscriptFormat = $this->conf->get(['externalservice', 'ginga', 'transcript_format']); + + if (!$gingaBaseurl || !$gingaToken || !$gingaTranscriptFormat) { + $this->logger->error("External service Ginga not set correctly in configuration.yml"); + + return 0; + } + /** @var WorkerJob $workerJob */ $workerJob = $this->repoWorkerJob->find($payload['workerId']); if ($workerJob == null) { @@ -67,9 +76,9 @@ class SubtitleWorker implements WorkerInterface $gingerClient = new Client(); try { - $response = $gingerClient->post(self::GINGER_BASE_URL.'/media/', [ + $response = $gingerClient->post($gingaBaseurl.'/media/', [ 'headers' => [ - 'Authorization' => 'token '.self::GINGER_TOKEN + 'Authorization' => 'token '.$gingaToken ], 'json' => [ 'url' => $payload['permalinkUrl'], @@ -99,9 +108,9 @@ class SubtitleWorker implements WorkerInterface sleep(5); $this->logger->info("bigin to check status"); try { - $response = $gingerClient->get(self::GINGER_BASE_URL.'/task/'.$responseMediaBody['task_id'].'/', [ + $response = $gingerClient->get($gingaBaseurl.'/task/'.$responseMediaBody['task_id'].'/', [ 'headers' => [ - 'Authorization' => 'token '.self::GINGER_TOKEN + 'Authorization' => 'token '.$gingaToken ] ]); } catch (\Exception $e) { @@ -129,10 +138,10 @@ class SubtitleWorker implements WorkerInterface } try { - $response = $gingerClient->get(self::GINGER_BASE_URL.'/media/'.$responseMediaBody['media']['uuid'].'/', [ + $response = $gingerClient->get($gingaBaseurl.'/media/'.$responseMediaBody['media']['uuid'].'/', [ 'headers' => [ - 'Authorization' => 'token '.self::GINGER_TOKEN, - 'ACCEPT' => self::GINGER_TRANSCRIPT_FORMAT + 'Authorization' => 'token '.$gingaToken, + 'ACCEPT' => $gingaTranscriptFormat ], 'query' => [ 'language' => $language From 8f3f3845c0a098c23fe960d1452f30a5b947ba87 Mon Sep 17 00:00:00 2001 From: aynsix Date: Mon, 6 Jul 2020 18:23:42 +0300 Subject: [PATCH 12/18] add subdef_source in config --- config/configuration.sample.yml | 10 ++++++---- .../Controller/Prod/ToolsController.php | 18 +++++++++++++++++- .../WorkerManager/Worker/SubtitleWorker.php | 6 +++--- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/config/configuration.sample.yml b/config/configuration.sample.yml index b65a9fe0c5..307b4ca827 100644 --- a/config/configuration.sample.yml +++ b/config/configuration.sample.yml @@ -329,10 +329,12 @@ workers: vhost: / externalservice: - ginga: - service_base_url: https://base.uri - token: 39c6011d - transcript_format: text/vtt + ginger: + AutoSubtitling: + service_base_url: https://base.uri + token: 39c6011d + transcript_format: text/vtt + subdef_source: preview user_account: deleting_policies: diff --git a/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php b/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php index f71114b3a4..55c067b17b 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/ToolsController.php @@ -457,7 +457,12 @@ class ToolsController extends Controller ); $permalinkUrl = ''; - if ($record->has_preview() && ($previewLink = $record->get_preview()->get_permalink()) != null) { + $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(); } @@ -524,4 +529,15 @@ class ToolsController extends Controller 'videoTextTrackFields' => $videoTextTrackFields ]); } + + private function isPhysicallyPresent(\record_adapter $record, $subdefName) + { + try { + return $record->get_subdef($subdefName)->is_physically_present(); + } catch (\Exception $e) { + unset($e); + } + + return false; + } } diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php index 82cc560952..361ab5b5ad 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php @@ -31,9 +31,9 @@ class SubtitleWorker implements WorkerInterface public function process(array $payload) { - $gingaBaseurl = $this->conf->get(['externalservice', 'ginga', 'service_base_url']); - $gingaToken = $this->conf->get(['externalservice', 'ginga', 'token']); - $gingaTranscriptFormat = $this->conf->get(['externalservice', 'ginga', 'transcript_format']); + $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']); if (!$gingaBaseurl || !$gingaToken || !$gingaTranscriptFormat) { $this->logger->error("External service Ginga not set correctly in configuration.yml"); From 53397bb3453e9958740c77f9da12dcbff16719f2 Mon Sep 17 00:00:00 2001 From: aynsix Date: Wed, 8 Jul 2020 18:36:39 +0300 Subject: [PATCH 13/18] add an webvtt header description --- lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php index 361ab5b5ad..666c8e3c31 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php @@ -163,6 +163,8 @@ class SubtitleWorker implements WorkerInterface $transcriptContent = $response->getBody()->getContents(); + $transcriptContent = preg_replace('WEBVTT', 'WEBVTT - with cue identifier', $transcriptContent, 1); + $metadatas[0] = [ 'meta_struct_id' => (int)$payload['metaStructureId'], 'meta_id' => '', From 2654037910d8b96bbd8fd89a769bb1a1aeb60a2b Mon Sep 17 00:00:00 2001 From: aynsix Date: Wed, 8 Jul 2020 19:00:23 +0300 Subject: [PATCH 14/18] fix delimiter --- lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php b/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php index 666c8e3c31..e9d66a2c66 100644 --- a/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php +++ b/lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php @@ -163,7 +163,7 @@ class SubtitleWorker implements WorkerInterface $transcriptContent = $response->getBody()->getContents(); - $transcriptContent = preg_replace('WEBVTT', 'WEBVTT - with cue identifier', $transcriptContent, 1); + $transcriptContent = preg_replace('/WEBVTT/', 'WEBVTT - with cue identifier', $transcriptContent, 1); $metadatas[0] = [ 'meta_struct_id' => (int)$payload['metaStructureId'], From 936e3b704c5c257a5bb659b24993f7e22510761e Mon Sep 17 00:00:00 2001 From: aynsix Date: Wed, 8 Jul 2020 19:15:45 +0300 Subject: [PATCH 15/18] add ginger conf in conf.d --- lib/conf.d/configuration.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/conf.d/configuration.yml b/lib/conf.d/configuration.yml index 4013f8a44e..fd59be26f8 100644 --- a/lib/conf.d/configuration.yml +++ b/lib/conf.d/configuration.yml @@ -319,5 +319,15 @@ workers: user: '' password: '' vhost: / + +externalservice: + ginger: + AutoSubtitling: + service_base_url: https://base.uri + token: 39c6011d + transcript_format: text/vtt + subdef_source: preview + + Console_logger_enabled_environments: [test] From 08f63994bd9aad8e27f7438b2b92a39d001a34cd Mon Sep 17 00:00:00 2001 From: Harrys Ravalomanana Date: Thu, 9 Jul 2020 14:30:50 +0400 Subject: [PATCH 16/18] PHRAS-3169 resize video on automatic subtitle tab --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 4d4e4caab2..dae70c0368 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "normalize-css": "^2.1.0", "npm": "^6.0.0", "npm-modernizr": "^2.8.3", - "phraseanet-production-client": "0.34.255-d", + "phraseanet-production-client": "0.34.256-d", "requirejs": "^2.3.5", "tinymce": "^4.0.28", "underscore": "^1.8.3", diff --git a/yarn.lock b/yarn.lock index 30158d604e..2a99f187cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7678,10 +7678,10 @@ phraseanet-common@^0.4.5-d: js-cookie "^2.1.0" pym.js "^1.3.1" -phraseanet-production-client@0.34.255-d: - version "0.34.255-d" - resolved "https://registry.yarnpkg.com/phraseanet-production-client/-/phraseanet-production-client-0.34.255-d.tgz#40e9c97ebb5fd77dd117f2d766126a94437b63ff" - integrity sha512-UQpkjhkKLPF63107y1nG+cd1wxUq2jAmbO58yrp3tJLsfQt1nGAkUfvZb9VEJ3/zbvKcjRmjnHHFDa08RcViww== +phraseanet-production-client@0.34.256-d: + version "0.34.256-d" + resolved "https://registry.yarnpkg.com/phraseanet-production-client/-/phraseanet-production-client-0.34.256-d.tgz#93a6b103ba38fc78d2b7687fec47928e2c46f363" + integrity sha512-cPHofONltBWs6IUOPEsQ02gSN+7HZv1hy9DZ98UPGpqkJG0D7gZLuNxiEVq/OFQFyYDCBTemNm/+BCRm6Abr7A== dependencies: "@mapbox/mapbox-gl-language" "^0.9.2" "@turf/turf" "^5.1.6" From a040a3af5a560693c2229b57d3ccdd7d8fae751b Mon Sep 17 00:00:00 2001 From: aynsix Date: Thu, 9 Jul 2020 16:13:56 +0300 Subject: [PATCH 17/18] fix databoxID --- templates/web/prod/actions/Tools/videoEditor.html.twig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/web/prod/actions/Tools/videoEditor.html.twig b/templates/web/prod/actions/Tools/videoEditor.html.twig index 13283d5812..4d1b3b0ca6 100644 --- a/templates/web/prod/actions/Tools/videoEditor.html.twig +++ b/templates/web/prod/actions/Tools/videoEditor.html.twig @@ -363,8 +363,8 @@ url: '/prod/tools/auto-subtitle/', dataType: 'json', data: { - databox_id: {{ record.get_base_id }}, - record_id: {{ record.get_record_id }}, + databox_id: {{ record.getDataboxId }}, + record_id: {{ record.getRecordId }}, meta_struct_id: $('#subtitle_language_source').val(), subtitle_language_source: $('#subtitle_language_source option:selected').text() }, From b2c4472a40e4bfcca7ad4fe3f4c1a80debb9f5fc Mon Sep 17 00:00:00 2001 From: Harrys Ravalomanana Date: Thu, 9 Jul 2020 18:46:34 +0400 Subject: [PATCH 18/18] PHRAS-3177 Add request status --- .../web/prod/actions/Tools/videoEditor.html.twig | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/templates/web/prod/actions/Tools/videoEditor.html.twig b/templates/web/prod/actions/Tools/videoEditor.html.twig index 4d1b3b0ca6..b9920c7fb2 100644 --- a/templates/web/prod/actions/Tools/videoEditor.html.twig +++ b/templates/web/prod/actions/Tools/videoEditor.html.twig @@ -236,9 +236,7 @@

@@ -252,9 +250,9 @@

@@ -262,6 +260,9 @@
+

+ {{ "prod:videoeditor:subtitleRequestTab:: Request in process" | trans }} +

@@ -370,6 +371,7 @@ }, success: function success(data) { console.log(data); + $('#request-status').removeClass('hide'); } }); });