mirror of
https://github.com/alchemy-fr/Phraseanet.git
synced 2025-10-23 18:03:17 +00:00
Merge pull request #3556 from alchemy-fr/PHRAS-2504-ginga-subtitle
PHRAS-2504 merge Ginger service integration Auto-subtitle
This commit is contained in:
@@ -328,6 +328,14 @@ workers:
|
|||||||
password: guest
|
password: guest
|
||||||
vhost: /
|
vhost: /
|
||||||
|
|
||||||
|
externalservice:
|
||||||
|
ginger:
|
||||||
|
AutoSubtitling:
|
||||||
|
service_base_url: https://base.uri
|
||||||
|
token: 39c6011d
|
||||||
|
transcript_format: text/vtt
|
||||||
|
subdef_source: preview
|
||||||
|
|
||||||
user_account:
|
user_account:
|
||||||
deleting_policies:
|
deleting_policies:
|
||||||
email_confirmation: true
|
email_confirmation: true
|
||||||
|
@@ -91,7 +91,6 @@ use Alchemy\Phrasea\WorkerManager\Provider\AlchemyWorkerServiceProvider;
|
|||||||
use Alchemy\Phrasea\WorkerManager\Provider\QueueWorkerServiceProvider;
|
use Alchemy\Phrasea\WorkerManager\Provider\QueueWorkerServiceProvider;
|
||||||
use Alchemy\QueueProvider\QueueServiceProvider;
|
use Alchemy\QueueProvider\QueueServiceProvider;
|
||||||
use Alchemy\WorkerProvider\WorkerServiceProvider;
|
use Alchemy\WorkerProvider\WorkerServiceProvider;
|
||||||
use Doctrine\DBAL\Event\ConnectionEventArgs;
|
|
||||||
use MediaVorus\Media\MediaInterface;
|
use MediaVorus\Media\MediaInterface;
|
||||||
use MediaVorus\MediaVorus;
|
use MediaVorus\MediaVorus;
|
||||||
use Monolog\Handler\ErrorLogHandler;
|
use Monolog\Handler\ErrorLogHandler;
|
||||||
|
@@ -15,8 +15,10 @@ use Alchemy\Phrasea\Application\Helper\FilesystemAware;
|
|||||||
use Alchemy\Phrasea\Application\Helper\SubDefinitionSubstituerAware;
|
use Alchemy\Phrasea\Application\Helper\SubDefinitionSubstituerAware;
|
||||||
use Alchemy\Phrasea\Controller\Controller;
|
use Alchemy\Phrasea\Controller\Controller;
|
||||||
use Alchemy\Phrasea\Controller\RecordsRequest;
|
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\RecordEvents;
|
||||||
use Alchemy\Phrasea\Core\Event\Record\SubdefinitionCreateEvent;
|
use Alchemy\Phrasea\Core\Event\Record\SubdefinitionCreateEvent;
|
||||||
|
use Alchemy\Phrasea\Core\PhraseaEvents;
|
||||||
use Alchemy\Phrasea\Exception\RuntimeException;
|
use Alchemy\Phrasea\Exception\RuntimeException;
|
||||||
use Alchemy\Phrasea\Metadata\PhraseanetMetadataReader;
|
use Alchemy\Phrasea\Metadata\PhraseanetMetadataReader;
|
||||||
use Alchemy\Phrasea\Metadata\PhraseanetMetadataSetter;
|
use Alchemy\Phrasea\Metadata\PhraseanetMetadataSetter;
|
||||||
@@ -24,7 +26,6 @@ use Alchemy\Phrasea\Record\RecordWasRotated;
|
|||||||
use DataURI\Parser;
|
use DataURI\Parser;
|
||||||
use MediaAlchemyst\Alchemyst;
|
use MediaAlchemyst\Alchemyst;
|
||||||
use MediaVorus\MediaVorus;
|
use MediaVorus\MediaVorus;
|
||||||
use PHPExiftool\Reader;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
class ToolsController extends Controller
|
class ToolsController extends Controller
|
||||||
@@ -45,7 +46,6 @@ class ToolsController extends Controller
|
|||||||
if (count($records) == 1) {
|
if (count($records) == 1) {
|
||||||
/** @var \record_adapter $record */
|
/** @var \record_adapter $record */
|
||||||
$record = $records->first();
|
$record = $records->first();
|
||||||
$databox = $record->getDatabox();
|
|
||||||
|
|
||||||
/**Array list of subdefs**/
|
/**Array list of subdefs**/
|
||||||
$listsubdef = array_keys($record-> get_subdefs());
|
$listsubdef = array_keys($record-> get_subdefs());
|
||||||
@@ -88,7 +88,6 @@ class ToolsController extends Controller
|
|||||||
$metadatas = true;
|
$metadatas = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$conf = $this->getConf();
|
|
||||||
|
|
||||||
return $this->render('prod/actions/Tools/index.html.twig', [
|
return $this->render('prod/actions/Tools/index.html.twig', [
|
||||||
'records' => $records,
|
'records' => $records,
|
||||||
@@ -118,6 +117,7 @@ class ToolsController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($records as $record) {
|
foreach ($records as $record) {
|
||||||
|
/** @var \media_subdef $subdef */
|
||||||
foreach ($record->get_subdefs() as $subdef) {
|
foreach ($record->get_subdefs() as $subdef) {
|
||||||
if ($subdef->get_type() !== \media_subdef::TYPE_IMAGE) {
|
if ($subdef->get_type() !== \media_subdef::TYPE_IMAGE) {
|
||||||
continue;
|
continue;
|
||||||
@@ -146,6 +146,7 @@ class ToolsController extends Controller
|
|||||||
|
|
||||||
foreach ($selection as $record) {
|
foreach ($selection as $record) {
|
||||||
$substituted = false;
|
$substituted = false;
|
||||||
|
/** @var \media_subdef $subdef */
|
||||||
foreach ($record->get_subdefs() as $subdef) {
|
foreach ($record->get_subdefs() as $subdef) {
|
||||||
if ($subdef->is_substituted()) {
|
if ($subdef->is_substituted()) {
|
||||||
$substituted = true;
|
$substituted = true;
|
||||||
@@ -362,14 +363,6 @@ class ToolsController extends Controller
|
|||||||
return $this->app->json($return);
|
return $this->app->json($return);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Reader
|
|
||||||
*/
|
|
||||||
private function getExifToolReader()
|
|
||||||
{
|
|
||||||
return $this->app['exiftool.reader'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Alchemyst
|
* @return Alchemyst
|
||||||
*/
|
*/
|
||||||
@@ -449,13 +442,38 @@ class ToolsController extends Controller
|
|||||||
try {
|
try {
|
||||||
$record->set_metadatas($metadatas);
|
$record->set_metadatas($metadatas);
|
||||||
}
|
}
|
||||||
catch (Exception $e) {
|
catch (\Exception $e) {
|
||||||
return $this->app->json(['success' => false, 'errorMessage' => $e->getMessage()]);
|
return $this->app->json(['success' => false, 'errorMessage' => $e->getMessage()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->app->json(['success' => true, 'errorMessage' => '']);
|
return $this->app->json(['success' => true, 'errorMessage' => '']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function autoSubtitleAction(Request $request)
|
||||||
|
{
|
||||||
|
$record = new \record_adapter($this->app,
|
||||||
|
(int)$request->request->get("databox_id"),
|
||||||
|
(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"))
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->app->json(["status" => "dispatch"]);
|
||||||
|
}
|
||||||
|
|
||||||
public function videoEditorAction(Request $request)
|
public function videoEditorAction(Request $request)
|
||||||
{
|
{
|
||||||
$records = RecordsRequest::fromRequest($this->app, $request, false);
|
$records = RecordsRequest::fromRequest($this->app, $request, false);
|
||||||
@@ -491,7 +509,7 @@ class ToolsController extends Controller
|
|||||||
$fieldValue = array_pop($fieldValues);
|
$fieldValue = array_pop($fieldValues);
|
||||||
$field['value'] = $fieldValue->getValue();
|
$field['value'] = $fieldValue->getValue();
|
||||||
}
|
}
|
||||||
$videoTextTrackFields[] = $field;
|
$videoTextTrackFields[$meta->get_id()] = $field;
|
||||||
unset($field);
|
unset($field);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -511,4 +529,15 @@ class ToolsController extends Controller
|
|||||||
'videoTextTrackFields' => $videoTextTrackFields
|
'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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -72,6 +72,9 @@ class Tools implements ControllerProviderInterface, ServiceProviderInterface
|
|||||||
$controllers->post('/metadata/save/', 'controller.prod.tools:saveMetasAction')
|
$controllers->post('/metadata/save/', 'controller.prod.tools:saveMetasAction')
|
||||||
->bind('prod_tools_metadata_save');
|
->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');
|
$controllers->get('/videoEditor', 'controller.prod.tools:videoEditorAction');
|
||||||
|
|
||||||
return $controllers;
|
return $controllers;
|
||||||
|
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Alchemy\Phrasea\Core\Event\Record;
|
||||||
|
|
||||||
|
use Alchemy\Phrasea\Model\RecordInterface;
|
||||||
|
|
||||||
|
class RecordAutoSubtitleEvent extends RecordEvent
|
||||||
|
{
|
||||||
|
private $languageSource;
|
||||||
|
private $metaStructId;
|
||||||
|
private $permalinkUrl;
|
||||||
|
|
||||||
|
public function __construct(RecordInterface $record, $permalinkUrl, $languageSource, $metaStructId)
|
||||||
|
{
|
||||||
|
parent::__construct($record);
|
||||||
|
|
||||||
|
$this->languageSource = $languageSource;
|
||||||
|
$this->metaStructId = $metaStructId;
|
||||||
|
$this->permalinkUrl = $permalinkUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLanguageSource()
|
||||||
|
{
|
||||||
|
return $this->languageSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMetaStructId()
|
||||||
|
{
|
||||||
|
return $this->metaStructId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPermalinkUrl()
|
||||||
|
{
|
||||||
|
return $this->permalinkUrl;
|
||||||
|
}
|
||||||
|
}
|
@@ -54,6 +54,8 @@ final class PhraseaEvents
|
|||||||
const RECORD_EDIT = 'record.edit';
|
const RECORD_EDIT = 'record.edit';
|
||||||
const RECORD_UPLOAD = 'record.upload';
|
const RECORD_UPLOAD = 'record.upload';
|
||||||
|
|
||||||
|
const RECORD_AUTO_SUBTITLE = 'record.auto-subtitle';
|
||||||
|
|
||||||
const THESAURUS_IMPORTED = 'thesaurus.imported';
|
const THESAURUS_IMPORTED = 'thesaurus.imported';
|
||||||
const THESAURUS_FIELD_LINKED = 'thesaurus.field-linked';
|
const THESAURUS_FIELD_LINKED = 'thesaurus.field-linked';
|
||||||
const THESAURUS_CANDIDATE_ACCEPTED_AS_CONCEPT = 'thesaurus.candidate-accepted-as-concept';
|
const THESAURUS_CANDIDATE_ACCEPTED_AS_CONCEPT = 'thesaurus.candidate-accepted-as-concept';
|
||||||
|
@@ -150,6 +150,9 @@ class RepositoriesServiceProvider implements ServiceProviderInterface
|
|||||||
$app['repo.worker-running-job'] = $app->share(function (PhraseaApplication $app) {
|
$app['repo.worker-running-job'] = $app->share(function (PhraseaApplication $app) {
|
||||||
return $app['orm.em']->getRepository('Phraseanet:WorkerRunningJob');
|
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) {
|
$app['repo.worker-running-populate'] = $app->share(function (PhraseaApplication $app) {
|
||||||
return $app['orm.em']->getRepository('Phraseanet:WorkerRunningPopulate');
|
return $app['orm.em']->getRepository('Phraseanet:WorkerRunningPopulate');
|
||||||
});
|
});
|
||||||
|
@@ -16,7 +16,6 @@ class Version
|
|||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private $number = '4.1.1';
|
private $number = '4.1.1';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
150
lib/Alchemy/Phrasea/Model/Entities/WorkerJob.php
Normal file
150
lib/Alchemy/Phrasea/Model/Entities/WorkerJob.php
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Alchemy\Phrasea\Model\Entities;
|
||||||
|
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use Gedmo\Mapping\Annotation as Gedmo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Table(name="WorkerJob", indexes={@ORM\Index(name="worker_job_type", columns={"type"})})
|
||||||
|
* @ORM\Entity(repositoryClass="Alchemy\Phrasea\Model\Repositories\WorkerJobRepository")
|
||||||
|
*/
|
||||||
|
class WorkerJob
|
||||||
|
{
|
||||||
|
const WAITING = "waiting";
|
||||||
|
const RUNNING = "running";
|
||||||
|
const FINISHED = "finished";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="integer")
|
||||||
|
* @ORM\Id
|
||||||
|
* @ORM\GeneratedValue
|
||||||
|
*/
|
||||||
|
private $id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string", name="type")
|
||||||
|
*/
|
||||||
|
private $type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="json_array", name="data", nullable=false)
|
||||||
|
*/
|
||||||
|
private $data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string", name="status")
|
||||||
|
*/
|
||||||
|
private $status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Gedmo\Timestampable(on="create")
|
||||||
|
* @ORM\Column(type="datetime")
|
||||||
|
*/
|
||||||
|
private $created;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="datetime", nullable=true)
|
||||||
|
*/
|
||||||
|
private $started;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="datetime", nullable=true)
|
||||||
|
*/
|
||||||
|
private $finished;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return integer
|
||||||
|
*/
|
||||||
|
public function getId()
|
||||||
|
{
|
||||||
|
return $this->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;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Alchemy\Phrasea\Model\Repositories;
|
||||||
|
|
||||||
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
|
||||||
|
|
||||||
|
class WorkerJobRepository extends EntityRepository
|
||||||
|
{
|
||||||
|
public function getEntityManager()
|
||||||
|
{
|
||||||
|
return parent::getEntityManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reconnect()
|
||||||
|
{
|
||||||
|
if($this->_em->getConnection()->ping() === false) {
|
||||||
|
$this->_em->getConnection()->close();
|
||||||
|
$this->_em->getConnection()->connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -11,11 +11,13 @@ use Alchemy\Phrasea\WorkerManager\Worker\CreateRecordWorker;
|
|||||||
use Alchemy\Phrasea\WorkerManager\Worker\DeleteRecordWorker;
|
use Alchemy\Phrasea\WorkerManager\Worker\DeleteRecordWorker;
|
||||||
use Alchemy\Phrasea\WorkerManager\Worker\ExportMailWorker;
|
use Alchemy\Phrasea\WorkerManager\Worker\ExportMailWorker;
|
||||||
use Alchemy\Phrasea\WorkerManager\Worker\Factory\CallableWorkerFactory;
|
use Alchemy\Phrasea\WorkerManager\Worker\Factory\CallableWorkerFactory;
|
||||||
|
use Alchemy\Phrasea\WorkerManager\Worker\MainQueueWorker;
|
||||||
use Alchemy\Phrasea\WorkerManager\Worker\PopulateIndexWorker;
|
use Alchemy\Phrasea\WorkerManager\Worker\PopulateIndexWorker;
|
||||||
use Alchemy\Phrasea\WorkerManager\Worker\ProcessPool;
|
use Alchemy\Phrasea\WorkerManager\Worker\ProcessPool;
|
||||||
use Alchemy\Phrasea\WorkerManager\Worker\PullAssetsWorker;
|
use Alchemy\Phrasea\WorkerManager\Worker\PullAssetsWorker;
|
||||||
use Alchemy\Phrasea\WorkerManager\Worker\Resolver\TypeBasedWorkerResolver;
|
use Alchemy\Phrasea\WorkerManager\Worker\Resolver\TypeBasedWorkerResolver;
|
||||||
use Alchemy\Phrasea\WorkerManager\Worker\SubdefCreationWorker;
|
use Alchemy\Phrasea\WorkerManager\Worker\SubdefCreationWorker;
|
||||||
|
use Alchemy\Phrasea\WorkerManager\Worker\SubtitleWorker;
|
||||||
use Alchemy\Phrasea\WorkerManager\Worker\WebhookWorker;
|
use Alchemy\Phrasea\WorkerManager\Worker\WebhookWorker;
|
||||||
use Alchemy\Phrasea\WorkerManager\Worker\WorkerInvoker;
|
use Alchemy\Phrasea\WorkerManager\Worker\WorkerInvoker;
|
||||||
use Alchemy\Phrasea\WorkerManager\Worker\WriteMetadatasWorker;
|
use Alchemy\Phrasea\WorkerManager\Worker\WriteMetadatasWorker;
|
||||||
@@ -128,6 +130,14 @@ class AlchemyWorkerServiceProvider implements PluginProviderInterface
|
|||||||
return (new DeleteRecordWorker())
|
return (new DeleteRecordWorker())
|
||||||
->setApplicationBox($app['phraseanet.appbox']);
|
->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'], $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) {
|
||||||
|
return new MainQueueWorker($app['alchemy_worker.message.publisher'], $app['repo.worker-job']);
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -23,6 +23,7 @@ use Alchemy\Phrasea\WorkerManager\Subscriber\AssetsIngestSubscriber;
|
|||||||
use Alchemy\Phrasea\WorkerManager\Subscriber\ExportSubscriber;
|
use Alchemy\Phrasea\WorkerManager\Subscriber\ExportSubscriber;
|
||||||
use Alchemy\Phrasea\WorkerManager\Subscriber\RecordSubscriber;
|
use Alchemy\Phrasea\WorkerManager\Subscriber\RecordSubscriber;
|
||||||
use Alchemy\Phrasea\WorkerManager\Subscriber\SearchengineSubscriber;
|
use Alchemy\Phrasea\WorkerManager\Subscriber\SearchengineSubscriber;
|
||||||
|
use Alchemy\Phrasea\WorkerManager\Subscriber\SubtitleSubscriber;
|
||||||
use Alchemy\Phrasea\WorkerManager\Subscriber\WebhookSubscriber;
|
use Alchemy\Phrasea\WorkerManager\Subscriber\WebhookSubscriber;
|
||||||
use Silex\Application;
|
use Silex\Application;
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
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 AssetsIngestSubscriber($app['alchemy_worker.message.publisher']));
|
||||||
$dispatcher->addSubscriber(new SearchengineSubscriber($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 WebhookSubscriber($app['alchemy_worker.message.publisher']));
|
||||||
|
$dispatcher->addSubscriber(new SubtitleSubscriber(new LazyLocator($app, 'repo.worker-job'), $app['alchemy_worker.message.publisher']));
|
||||||
|
|
||||||
return $dispatcher;
|
return $dispatcher;
|
||||||
})
|
})
|
||||||
|
@@ -29,7 +29,9 @@ class AMQPConnection
|
|||||||
MessagePublisher::CREATE_RECORD_TYPE => MessagePublisher::CREATE_RECORD_QUEUE,
|
MessagePublisher::CREATE_RECORD_TYPE => MessagePublisher::CREATE_RECORD_QUEUE,
|
||||||
MessagePublisher::PULL_QUEUE => MessagePublisher::PULL_QUEUE,
|
MessagePublisher::PULL_QUEUE => MessagePublisher::PULL_QUEUE,
|
||||||
MessagePublisher::POPULATE_INDEX_TYPE => MessagePublisher::POPULATE_INDEX_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
|
// the corresponding worker queues and retry queues, loop queue
|
||||||
@@ -44,7 +46,6 @@ class AMQPConnection
|
|||||||
MessagePublisher::PULL_QUEUE => MessagePublisher::LOOP_PULL_QUEUE
|
MessagePublisher::PULL_QUEUE => MessagePublisher::LOOP_PULL_QUEUE
|
||||||
];
|
];
|
||||||
|
|
||||||
// default message TTL in retry queue in millisecond
|
|
||||||
public static $defaultFailedQueues = [
|
public static $defaultFailedQueues = [
|
||||||
MessagePublisher::WRITE_METADATAS_TYPE => MessagePublisher::FAILED_METADATAS_QUEUE,
|
MessagePublisher::WRITE_METADATAS_TYPE => MessagePublisher::FAILED_METADATAS_QUEUE,
|
||||||
MessagePublisher::SUBDEF_CREATION_TYPE => MessagePublisher::FAILED_SUBDEF_QUEUE,
|
MessagePublisher::SUBDEF_CREATION_TYPE => MessagePublisher::FAILED_SUBDEF_QUEUE,
|
||||||
|
@@ -18,6 +18,12 @@ class MessagePublisher
|
|||||||
const WEBHOOK_TYPE = 'webhook';
|
const WEBHOOK_TYPE = 'webhook';
|
||||||
const POPULATE_INDEX_TYPE = 'populateIndex';
|
const POPULATE_INDEX_TYPE = 'populateIndex';
|
||||||
const PULL_ASSETS_TYPE = 'pullAssets';
|
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
|
// worker queue to be consumed, when no ack , it is requeued to the retry queue
|
||||||
const EXPORT_QUEUE = 'export-queue';
|
const EXPORT_QUEUE = 'export-queue';
|
||||||
|
@@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Alchemy\Phrasea\WorkerManager\Subscriber;
|
||||||
|
|
||||||
|
use Alchemy\Phrasea\Core\Event\Record\RecordAutoSubtitleEvent;
|
||||||
|
use Alchemy\Phrasea\Core\PhraseaEvents;
|
||||||
|
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
|
||||||
|
{
|
||||||
|
private $messagePublisher;
|
||||||
|
|
||||||
|
/** @var WorkerJobRepository $repoWorkerJob*/
|
||||||
|
private $repoWorkerJob;
|
||||||
|
|
||||||
|
/** @var callable */
|
||||||
|
private $repoWorkerJobLocator;
|
||||||
|
|
||||||
|
public function __construct(callable $repoWorkerJobLocator, MessagePublisher $messagePublisher)
|
||||||
|
{
|
||||||
|
$this->repoWorkerJobLocator = $repoWorkerJobLocator;
|
||||||
|
$this->messagePublisher = $messagePublisher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onRecordAutoSubtitle(RecordAutoSubtitleEvent $event)
|
||||||
|
{
|
||||||
|
$this->repoWorkerJob = $this->getRepoWorkerJob();
|
||||||
|
|
||||||
|
$em = $this->repoWorkerJob->getEntityManager();
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
"databoxId" => $event->getRecord()->getDataboxId(),
|
||||||
|
"recordId" => $event->getRecord()->getRecordId(),
|
||||||
|
"permalinkUrl" => $event->getPermalinkUrl(),
|
||||||
|
"langageSource" => $event->getLanguageSource(),
|
||||||
|
"metaStructureId" => $event->getMetaStructId()
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->repoWorkerJob->reconnect();
|
||||||
|
$em->beginTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$workerJob = new WorkerJob();
|
||||||
|
$workerJob
|
||||||
|
->setType(MessagePublisher::SUBTITLE_TYPE)
|
||||||
|
->setData($data)
|
||||||
|
->setStatus(WorkerJob::WAITING)
|
||||||
|
;
|
||||||
|
|
||||||
|
$em->persist($workerJob);
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
$em->commit();
|
||||||
|
|
||||||
|
$data['workerId'] = $workerJob->getId();
|
||||||
|
$data['type'] = MessagePublisher::SUBTITLE_TYPE;
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'message_type' => MessagePublisher::MAIN_QUEUE_TYPE,
|
||||||
|
'payload' => $data
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->messagePublisher->publishMessage($payload, MessagePublisher::MAIN_QUEUE);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$em->rollback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getSubscribedEvents()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
PhraseaEvents::RECORD_AUTO_SUBTITLE => 'onRecordAutoSubtitle',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return WorkerJobRepository
|
||||||
|
*/
|
||||||
|
private function getRepoWorkerJob()
|
||||||
|
{
|
||||||
|
$callable = $this->repoWorkerJobLocator;
|
||||||
|
|
||||||
|
return $callable();
|
||||||
|
}
|
||||||
|
}
|
49
lib/Alchemy/Phrasea/WorkerManager/Worker/MainQueueWorker.php
Normal file
49
lib/Alchemy/Phrasea/WorkerManager/Worker/MainQueueWorker.php
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Alchemy\Phrasea\WorkerManager\Worker;
|
||||||
|
|
||||||
|
use Alchemy\Phrasea\Model\Repositories\WorkerJobRepository;
|
||||||
|
use Alchemy\Phrasea\WorkerManager\Queue\MessagePublisher;
|
||||||
|
|
||||||
|
class MainQueueWorker implements WorkerInterface
|
||||||
|
{
|
||||||
|
private $messagePublisher;
|
||||||
|
|
||||||
|
private $repoWorkerJob;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
MessagePublisher $messagePublisher,
|
||||||
|
WorkerJobRepository $repoWorkerJob
|
||||||
|
)
|
||||||
|
{
|
||||||
|
$this->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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
212
lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php
Normal file
212
lib/Alchemy/Phrasea/WorkerManager/Worker/SubtitleWorker.php
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
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;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
class SubtitleWorker implements WorkerInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var callable
|
||||||
|
*/
|
||||||
|
private $appboxLocator;
|
||||||
|
|
||||||
|
private $logger;
|
||||||
|
private $conf;
|
||||||
|
|
||||||
|
/** @var WorkerJobRepository $repoWorkerJob*/
|
||||||
|
private $repoWorkerJob;
|
||||||
|
|
||||||
|
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', '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");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @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 ($payload['permalinkUrl'] != '' && $payload['metaStructureId']) {
|
||||||
|
switch ($payload['langageSource']) {
|
||||||
|
case 'En':
|
||||||
|
$language = 'en-GB';
|
||||||
|
break;
|
||||||
|
case 'De':
|
||||||
|
$language = 'de-DE';
|
||||||
|
break;
|
||||||
|
case 'Fr':
|
||||||
|
default:
|
||||||
|
$language = 'fr-FR';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$gingerClient = new Client();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $gingerClient->post($gingaBaseurl.'/media/', [
|
||||||
|
'headers' => [
|
||||||
|
'Authorization' => 'token '.$gingaToken
|
||||||
|
],
|
||||||
|
'json' => [
|
||||||
|
'url' => $payload['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 /media/ : ". $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);
|
||||||
|
$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;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
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($gingaBaseurl.'/media/'.$responseMediaBody['media']['uuid'].'/', [
|
||||||
|
'headers' => [
|
||||||
|
'Authorization' => 'token '.$gingaToken,
|
||||||
|
'ACCEPT' => $gingaTranscriptFormat
|
||||||
|
],
|
||||||
|
'query' => [
|
||||||
|
'language' => $language
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->logger->error($e->getMessage());
|
||||||
|
$this->jobFinished($workerJob);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($response->getStatusCode() !== 200) {
|
||||||
|
$this->logger->error("response status /media/uuid : ". $response->getStatusCode());
|
||||||
|
$this->jobFinished($workerJob);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$transcriptContent = $response->getBody()->getContents();
|
||||||
|
|
||||||
|
$transcriptContent = preg_replace('/WEBVTT/', 'WEBVTT - with cue identifier', $transcriptContent, 1);
|
||||||
|
|
||||||
|
$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->info("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();
|
||||||
|
}
|
||||||
|
}
|
@@ -319,5 +319,15 @@ workers:
|
|||||||
user: ''
|
user: ''
|
||||||
password: ''
|
password: ''
|
||||||
vhost: /
|
vhost: /
|
||||||
|
|
||||||
|
externalservice:
|
||||||
|
ginger:
|
||||||
|
AutoSubtitling:
|
||||||
|
service_base_url: https://base.uri
|
||||||
|
token: 39c6011d
|
||||||
|
transcript_format: text/vtt
|
||||||
|
subdef_source: preview
|
||||||
|
|
||||||
|
|
||||||
Console_logger_enabled_environments: [test]
|
Console_logger_enabled_environments: [test]
|
||||||
|
|
||||||
|
@@ -244,9 +244,9 @@
|
|||||||
<p class="item">
|
<p class="item">
|
||||||
<label>{{ "prod:videoeditor:subtitleRequestTab:label:: Source Audio language" | trans }}</label>
|
<label>{{ "prod:videoeditor:subtitleRequestTab:label:: Source Audio language" | trans }}</label>
|
||||||
<select name="subtitle_language_source" id="subtitle_language_source">
|
<select name="subtitle_language_source" id="subtitle_language_source">
|
||||||
<option value="fr">Fr</option>
|
{% for videoTextTrackField in videoTextTrackFields %}
|
||||||
<option value="en">En</option>
|
<option value="{{ videoTextTrackField.meta_struct_id}}">{{ videoTextTrackField.label}}</option>
|
||||||
<option value="de">De</option>
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</p>
|
</p>
|
||||||
<p class="item">
|
<p class="item">
|
||||||
@@ -354,4 +354,24 @@
|
|||||||
overlapChapters: {% if overlapChapters != NULL %}{{ overlapChapters }}{% else %}1{% endif %},
|
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.getDataboxId }},
|
||||||
|
record_id: {{ record.getRecordId }},
|
||||||
|
meta_struct_id: $('#subtitle_language_source').val(),
|
||||||
|
subtitle_language_source: $('#subtitle_language_source option:selected').text()
|
||||||
|
},
|
||||||
|
success: function success(data) {
|
||||||
|
console.log(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
Reference in New Issue
Block a user