diff --git a/classes/local/attachment_helper.php b/classes/local/attachment_helper.php index 14c31ab8..aaf02e47 100644 --- a/classes/local/attachment_helper.php +++ b/classes/local/attachment_helper.php @@ -56,7 +56,16 @@ class attachment_helper { const ATTACHMENT_TYPE_TRANSCRIPTION = 'transcription'; /** @var string transcription flavor */ - const TRANSCRIPTION_FLAVOR_TYPE = 'captions/vtt'; + const TRANSCRIPTION_FLAVOR_TYPE = 'captions'; + + /** @var string transcription manual subflavor */ + const TRANSCRIPTION_MANUAL_SUBFLAVOR_TYPE = 'vtt'; + + /** @var array transcription subflavor types */ + const TRANSCRIPTION_SUBFLAVOR_TYPES = ['vtt', 'delivery', 'prepared']; + + /** @var string transcription mediatype */ + const TRANSCRIPTION_MEDIATYPE = 'text/vtt'; /** * Saves the attachment upload job. @@ -179,6 +188,7 @@ protected function upload_job_transcriptions($attachmentjob, $uploadjob, $video) $mediapackagestr = $apibridge->get_event_media_package($video->identifier); $transcriptionstoupload = json_decode($attachmentjob->files); + $mainmanualflavor = self::TRANSCRIPTION_FLAVOR_TYPE . '/' . self::TRANSCRIPTION_MANUAL_SUBFLAVOR_TYPE; foreach ($transcriptionstoupload as $transcription) { // Get file first. $fs = get_file_storage(); @@ -189,7 +199,7 @@ protected function upload_job_transcriptions($attachmentjob, $uploadjob, $video) continue; } // Prepare flavor based on the flavor code. - $flavor = self::TRANSCRIPTION_FLAVOR_TYPE . "+{$transcription->flavor}"; + $flavor = $mainmanualflavor . "+{$transcription->flavor}"; // Compile and add attachment/track. $mediapackagestr = self::perform_add_attachment($ocinstanceid, $video->identifier, @@ -273,16 +283,25 @@ protected function cleanup_attachment_job($job) { */ private static function perform_add_attachment($ocinstanceid, $identifier, $mediapackagestr, $file, $flavor) { // Remove existing attachments or media with the same flavor. - $mediapackagestr = self::remove_existing_flavor_from_mediapackage($ocinstanceid, $mediapackagestr, 'type', $flavor); + list($mediapackagestr, $removedmediaids, $removedattachmentids) = self::remove_existing_flavor_from_mediapackage( + $ocinstanceid, $mediapackagestr, 'type', $flavor); $apibridge = apibridge::get_instance($ocinstanceid); $filestream = $apibridge->get_upload_filestream($file, 'file'); // We do a version check here to perform addTrack instead of addAttachment. $opencastversion = $apibridge->get_opencast_version(); // We do a version check here to perform the add track feature specifically for transcriptions added in Opencast version 13. - if (version_compare($opencastversion, '13.0.0', '>=') && strpos($flavor, self::TRANSCRIPTION_FLAVOR_TYPE) !== false) { - $apibridge->event_add_track($identifier, $flavor, $filestream); - // We need to get the mediapackage again. - $mediapackagestr = $apibridge->get_event_media_package($identifier); + $mainmanualflavor = self::TRANSCRIPTION_FLAVOR_TYPE . '/' . self::TRANSCRIPTION_MANUAL_SUBFLAVOR_TYPE; + if (version_compare($opencastversion, '13.0.0', '>=') && strpos($flavor, $mainmanualflavor) !== false) { + $trackisadded = $apibridge->event_add_track($identifier, $flavor, $filestream); + if ($trackisadded) { + $mediapackagestr = $apibridge->get_event_media_package($identifier); + // We need to perform extracting the existing media items again, because overwrite existing in add track endpoint + // does not work as expected! + foreach ($removedmediaids as $mediaid) { + list($mediapackagestr, $unusedmediaids, $unusedattachmentids) = self::remove_existing_flavor_from_mediapackage( + $ocinstanceid, $mediapackagestr, 'id', $mediaid); + } + } } else { $mediapackagestr = $apibridge->ingest_add_attachment($mediapackagestr, $flavor, $filestream); } @@ -297,23 +316,24 @@ private static function perform_add_attachment($ocinstanceid, $identifier, $medi * @param string $attributetype the attribute type to check againts. * @param string $value the targeted attribute's value. * - * @return string mediapackage + * @return array [$mediapackage, $mediaids, $attachmentids] the mediapackage string as well as removed media and attachment ids. */ private static function remove_existing_flavor_from_mediapackage($ocinstanceid, $mediapackagestr, $attributetype, $value) { $mediapackage = simplexml_load_string($mediapackagestr); // We loop through the attackments, to get rid of any duplicates. - self::remove_attachment_from_xml($mediapackage, $attributetype, $value); + $attachmentids = self::remove_attachment_from_xml($mediapackage, $attributetype, $value); // Get the opencast version to make sure everything gets removed. $apibridge = apibridge::get_instance($ocinstanceid); $opencastversion = $apibridge->get_opencast_version(); // As of opencast 13 we need to check the media for transcriptions as well. + $mediaids = []; if (version_compare($opencastversion, '13.0.0', '>=')) { // We loop through the media tracks, to get rid of any duplicates. - self::remove_media_from_xml($mediapackage, $attributetype, $value); + $mediaids = self::remove_media_from_xml($mediapackage, $attributetype, $value); } - return $mediapackage->asXML(); + return [$mediapackage->asXML(), $mediaids, $attachmentids]; } /** @@ -322,13 +342,17 @@ private static function remove_existing_flavor_from_mediapackage($ocinstanceid, * @param SimpleXMLElement $mediapackage the mediapackage XML object. * @param string $attributetype the type of attribute to check against. * @param string $value the value of attribute to match with. + * + * @return array to remove ids. */ private static function remove_attachment_from_xml(&$mediapackage, $attributetype, $value) { $i = 0; $toremove = []; + $ids = []; foreach ($mediapackage->attachments->attachment as $item) { if ($item->attributes()[$attributetype] == $value) { $toremove[] = $i; + $ids[] = (string) $item->attributes()['id']; } $i++; } @@ -336,6 +360,7 @@ private static function remove_attachment_from_xml(&$mediapackage, $attributetyp foreach ($toremove as $i) { unset($mediapackage->attachments->attachment[$i]); } + return $ids; } /** @@ -344,13 +369,17 @@ private static function remove_attachment_from_xml(&$mediapackage, $attributetyp * @param SimpleXMLElement $mediapackage the mediapackage XML object. * @param string $attributetype the type of attribute to check against. * @param string $value the value of attribute to match with. + * + * @return array to remove ids. */ private static function remove_media_from_xml(&$mediapackage, $attributetype, $value) { $i = 0; $toremove = []; + $ids = []; foreach ($mediapackage->media->track as $item) { if ($item->attributes()[$attributetype] == $value) { $toremove[] = $i; + $ids[] = (string) $item->attributes()['id']; } $i++; } @@ -358,6 +387,7 @@ private static function remove_media_from_xml(&$mediapackage, $attributetype, $v foreach ($toremove as $i) { unset($mediapackage->media->track[$i]); } + return $ids; } /** @@ -391,16 +421,21 @@ private static function perform_finalize_upload_attachment($ocinstanceid, $media * * @param string $ocinstanceid id of opencast instance * @param string $eventidentifier id of the video - * @param string $transcriptionidentifier id of transcription + * @param stdClass $transcriptionobj transcription publication object. + * @param string $publicationtype the type of publication to look for. * * @return boolean the result of deletion. */ - public static function delete_transcription($ocinstanceid, $eventidentifier, $transcriptionidentifier) { + public static function delete_transcription($ocinstanceid, $eventidentifier, $transcriptionobj, $publicationtype) { $success = false; $apibridge = apibridge::get_instance($ocinstanceid); $mediapackagestr = $apibridge->get_event_media_package($eventidentifier); - $mediapackagestr = self::remove_existing_flavor_from_mediapackage($ocinstanceid, - $mediapackagestr, 'id', $transcriptionidentifier); + + $transcriptionidentifier = self::extract_transcription_id_from_mediapackage($mediapackagestr, $transcriptionobj, + $publicationtype); + + list($mediapackagestr, $removedmediaids, $removedattachmentids) = self::remove_existing_flavor_from_mediapackage( + $ocinstanceid, $mediapackagestr, 'id', $transcriptionidentifier); try { $ingested = $apibridge->ingest($mediapackagestr, get_config('block_opencast', 'deletetranscriptionworkflow_' . $ocinstanceid)); @@ -426,7 +461,8 @@ public static function delete_transcription($ocinstanceid, $eventidentifier, $tr public static function upload_single_transcription($file, $flavorservice, $ocinstanceid, $eventidentifier) { $apibridge = apibridge::get_instance($ocinstanceid); $mediapackagestr = $apibridge->get_event_media_package($eventidentifier); - $flavor = self::TRANSCRIPTION_FLAVOR_TYPE . "+{$flavorservice}"; + $mainmanualflavor = self::TRANSCRIPTION_FLAVOR_TYPE . '/' . self::TRANSCRIPTION_MANUAL_SUBFLAVOR_TYPE; + $flavor = $mainmanualflavor . "+{$flavorservice}"; // Compile and add attachment. $mediapackagestr = self::perform_add_attachment($ocinstanceid, $eventidentifier, $mediapackagestr, $file, $flavor); // Finalizing the attachment upload. @@ -454,4 +490,45 @@ public static function remove_single_transcription_file($fileitemid) { } $files->close(); } + + /** + * Gets the mediapackage id based on comparing the actual publication object. + * + * @param string $mediapackagestr the event mediapackage. + * @param object $transcriptionobj the transcription publication object. + * @param string $pubtype the publication type, either media or attachements. + * + * @return string|null the medispackage id or null if it could not be found. + */ + public static function extract_transcription_id_from_mediapackage($mediapackagestr, $transcriptionobj, $pubtype = 'media') { + $mediapackagexml = simplexml_load_string($mediapackagestr); + $pubsubtype = $pubtype == 'media' ? 'track' : 'attachment'; + if (property_exists($mediapackagexml, $pubtype)) { + foreach ($mediapackagexml->$pubtype->$pubsubtype as $item) { + $itemobj = json_decode(json_encode((array) $item)); + if ($itemobj->mimetype == $transcriptionobj->mediatype && + $itemobj->{'@attributes'}->type == $transcriptionobj->flavor) { + // As of Opencast 15, subtitles are all about tags, therefore we need to go through tags one by one. + if (property_exists($itemobj, 'tags')) { + $itemtags = $itemobj->tags->tag; + if (!is_array($itemtags)) { + $itemtags = [$itemtags]; + } + if (count($transcriptionobj->tags) === count($itemtags)) { + $alltagsmatch = true; + foreach ($itemtags as $tag) { + if (!in_array($tag, $transcriptionobj->tags)) { + $alltagsmatch = false; + } + } + if ($alltagsmatch) { + return $itemobj->{'@attributes'}->id; + } + } + } + } + } + } + return null; + } } diff --git a/deletetranscription.php b/deletetranscription.php index 716e3497..1847c2b7 100644 --- a/deletetranscription.php +++ b/deletetranscription.php @@ -60,7 +60,7 @@ require_capability('block/opencast:addvideo', $coursecontext); $apibridge = apibridge::get_instance($ocinstanceid); -$video = $apibridge->get_opencast_video($videoidentifier); +$video = $apibridge->get_opencast_video($videoidentifier, true); if ($video->error || $video->video->processing_state != 'SUCCEEDED' || empty(get_config('block_opencast', 'transcriptionworkflow_' . $ocinstanceid)) || empty(get_config('block_opencast', 'deletetranscriptionworkflow_' . $ocinstanceid))) { @@ -69,7 +69,35 @@ } if (($action == 'delete') && confirm_sesskey()) { - $deleted = attachment_helper::delete_transcription($ocinstanceid, $videoidentifier, $identifier); + // In order to remove the transcription from the media package we need to look it up in publication first, + // then we find it in the media package based on flavor and tags, because we maintain the compatibility of both ways. + + // 1. Find it in publications. + $transcriptiontodelete = null; + $publicationtype = 'media'; // For opencast 13 and above, this would be always media, unless intentianly configured otherwise. + foreach ($video->video->publications as $publication) { + // Search the attachments. + foreach ($publication->attachments as $attachment) { + if ($attachment->id == $identifier) { + $transcriptiontodelete = $attachment; + $publicationtype = 'attachments'; + break 2; + } + } + // Search the media. + foreach ($publication->media as $media) { + if ($media->id == $identifier) { + $transcriptiontodelete = $media; + break 2; + } + } + } + + $deleted = false; + if (!empty($transcriptiontodelete)) { + $deleted = attachment_helper::delete_transcription($ocinstanceid, $videoidentifier, $transcriptiontodelete, + $publicationtype); + } $message = get_string('transcriptiondeletionsucceeded', 'block_opencast'); $status = notification::NOTIFY_SUCCESS; diff --git a/downloadtranscription.php b/downloadtranscription.php index 5b61ed41..10834d1b 100644 --- a/downloadtranscription.php +++ b/downloadtranscription.php @@ -35,14 +35,14 @@ $courseid = required_param('courseid', PARAM_INT); $identifier = required_param('video_identifier', PARAM_ALPHANUMEXT); -$type = required_param('attachment_type', PARAM_ALPHANUMEXT); +$transcriptionid = required_param('transcription_identifier', PARAM_ALPHANUMEXT); $domain = optional_param('domain', '', PARAM_ALPHA); $ocinstanceid = optional_param('ocinstanceid', settings_api::get_default_ocinstance()->id, PARAM_INT); $indexurl = new moodle_url('/blocks/opencast/index.php', ['courseid' => $courseid, 'ocinstanceid' => $ocinstanceid]); $baseurl = new moodle_url('/blocks/opencast/downloadtranscription.php', ['courseid' => $courseid, 'ocinstanceid' => $ocinstanceid, - 'video_identifier' => $identifier, 'attachment_type' => $type, ]); + 'video_identifier' => $identifier, 'transcription_identifier' => $transcriptionid, ]); $PAGE->set_url($baseurl); $redirecturl = new moodle_url('/blocks/opencast/managetranscriptions.php', @@ -61,9 +61,10 @@ $coursecontext = context_course::instance($courseid); require_capability('block/opencast:addvideo', $coursecontext); -// Make sure downlaod is enabled. +// Make sure transcription as well as the downlaod is enabled. +$transcriptionenabled = get_config('block_opencast', 'transcriptionworkflow_' . $ocinstanceid); $downloadenabled = get_config('block_opencast', 'allowdownloadtranscription_' . $ocinstanceid); -if (empty($downloadenabled)) { +if (empty($downloadenabled) || empty($transcriptionenabled)) { redirect($redirecturl, get_string('unabletodownloadtranscription', 'block_opencast'), null, @@ -72,100 +73,116 @@ $apibridge = apibridge::get_instance($ocinstanceid); $result = $apibridge->get_opencast_video($identifier, true, false, true); -if (!$result->error || $result->video->processing_state != 'SUCCEEDED' || - empty(get_config('block_opencast', 'transcriptionworkflow_' . $ocinstanceid))) { - $video = $result->video; - $downloadurl = ''; - $size = 0; - $mimetype = 'text/vtt'; - $type = str_replace(['-', '_'], ['/', '+'], $type); - if ($domain === 'media' && !empty($video->media)) { - foreach ($video->media as $track) { - if ($track->flavor == $type) { - $downloadurl = $track->uri; - $size = $track->size; - $mimetype = $track->mimetype; - break; - } +// Make sure video is in good condition. +if ($result->error || $result->video->processing_state != 'SUCCEEDED') { + redirect($redirecturl, + get_string('unabletodownloadtranscription', 'block_opencast'), + null, + notification::NOTIFY_ERROR); +} + +$downloadurl = ''; +$size = 0; +$mimetype = 'text/vtt'; // It should be vtt. +// Going through publications. +$publicationmedia = null; +foreach ($result->video->publications as $publication) { + // Search the attachments. + foreach ($publication->attachments as $attachment) { + if ($attachment->id == $transcriptionid) { + $downloadurl = $attachment->url; + $size = $attachment->size; + $mimetype = $attachment->mediatype; + break 2; } - } else { - foreach ($video->publications as $publication) { - foreach ($publication->attachments as $attachment) { - if ($attachment->flavor == $type) { - $downloadurl = $attachment->url; - $size = $attachment->size; - $mimetype = $attachment->mediatype; - break 2; - } - } + } + // Search the media. + foreach ($publication->media as $media) { + if ($media->id == $transcriptionid) { + // We record the media object for a further lookup in media tracks. + $publicationmedia = $media; + $downloadurl = $media->url; + $size = $media->size; + $mimetype = $media->mediatype; + break 2; } } +} - if (empty($downloadurl)) { - redirect($redirecturl, - get_string('unabletodownloadtranscription', 'block_opencast'), - null, - notification::NOTIFY_ERROR); +// Here in order to have the right downloadable url for media, we need to find it from video media data. +// This case happens when using LTI and redirecting to assets/assets, otherwise it displays the file. +if ($domain === 'media' && !empty($result->video->media) && !empty($publicationmedia)) { + foreach ($result->video->media as $track) { + if ($track->mimetype == $publicationmedia->mediatype && + $track->flavor == $publicationmedia->flavor && + $track->tags == $publicationmedia->tags) { + $downloadurl = $track->uri; + $size = $track->size; + $mimetype = $track->mimetype; + break; + } } +} - // Get the LTI required credentials. - $consumerkey = $apibridge->get_lti_consumerkey(); - $consumersecret = $apibridge->get_lti_consumersecret(); - - // We set a flag to determine whether we should perform LTI authentication. - $performlti = true; - // If no key is provided, we proceed with no LTI authentication. - if (empty($consumerkey)) { - $performlti = false; - } +if (empty($downloadurl)) { + redirect($redirecturl, + get_string('unabletodownloadtranscription', 'block_opencast'), + null, + notification::NOTIFY_ERROR); +} - if ($performlti) { - $endpoint = settings_api::get_apiurl($ocinstanceid); +// Get the LTI required credentials. +$consumerkey = $apibridge->get_lti_consumerkey(); +$consumersecret = $apibridge->get_lti_consumersecret(); - // Make sure the endpoint is correct. - if (strpos($endpoint, 'http') !== 0) { - $endpoint = 'http://' . $endpoint; - } +// We set a flag to determine whether we should perform LTI authentication. +$performlti = true; +// If no key is provided, we proceed with no LTI authentication. +if (empty($consumerkey)) { + $performlti = false; +} - $ltiendpoint = rtrim($endpoint, '/') . '/lti'; - - // Create parameters. - $params = lti_helper::create_lti_parameters($consumerkey, $consumersecret, - $ltiendpoint, $downloadurl); - - $renderer = $PAGE->get_renderer('block_opencast'); - echo $OUTPUT->header(); - echo $OUTPUT->heading(get_string('downloadtranscription', 'block_opencast')); - echo $renderer->render_lti_form($ltiendpoint, $params); - $PAGE->requires->js_call_amd('block_opencast/block_lti_form_handler', 'init'); - $htmlreturnlink = html_writer::link($redirecturl, get_string('transcriptionreturntomanagement', 'block_opencast')); - echo html_writer::tag('p', get_string('transcriptionltidownloadcompleted', 'block_opencast', $htmlreturnlink)); - echo $OUTPUT->footer(); - } else { - ob_clean(); - $urlparts = explode('/', $downloadurl); - $filename = $urlparts[count($urlparts) - 1]; - - header('Content-Description: Download Transcription File'); - header('Content-Type: ' . $mimetype); - header('Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode($filename)); - header('Content-Length: ' . $size); - - if (is_https()) { // HTTPS sites - watch out for IE! KB812935 and KB316431. - header('Cache-Control: private, max-age=10, no-transform'); - header('Expires: ' . gmdate('D, d M Y H:i:s', 0) . ' GMT'); - header('Pragma: '); - } else { // Normal http - prevent caching at all cost. - header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0, no-transform'); - header('Expires: ' . gmdate('D, d M Y H:i:s', 0) . ' GMT'); - header('Pragma: no-cache'); - } +if ($performlti) { + $endpoint = settings_api::get_apiurl($ocinstanceid); - readfile($downloadurl); + // Make sure the endpoint is correct. + if (strpos($endpoint, 'http') !== 0) { + $endpoint = 'http://' . $endpoint; } + + $ltiendpoint = rtrim($endpoint, '/') . '/lti'; + + // Create parameters. + $params = lti_helper::create_lti_parameters($consumerkey, $consumersecret, + $ltiendpoint, $downloadurl); + + $renderer = $PAGE->get_renderer('block_opencast'); + echo $OUTPUT->header(); + echo $OUTPUT->heading(get_string('downloadtranscription', 'block_opencast')); + echo $renderer->render_lti_form($ltiendpoint, $params); + $PAGE->requires->js_call_amd('block_opencast/block_lti_form_handler', 'init'); + $htmlreturnlink = html_writer::link($redirecturl, get_string('transcriptionreturntomanagement', 'block_opencast')); + echo html_writer::tag('p', get_string('transcriptionltidownloadcompleted', 'block_opencast', $htmlreturnlink)); + echo $OUTPUT->footer(); } else { - redirect($redirecturl, - get_string('unabletodownloadtranscription', 'block_opencast'), - null, - notification::NOTIFY_ERROR); + ob_clean(); + $urlparts = explode('/', $downloadurl); + $filename = $urlparts[count($urlparts) - 1]; + + header('Content-Description: Download Transcription File'); + header('Content-Type: ' . $mimetype); + header('Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode($filename)); + header('Content-Length: ' . $size); + + if (is_https()) { // HTTPS sites - watch out for IE! KB812935 and KB316431. + header('Cache-Control: private, max-age=10, no-transform'); + header('Expires: ' . gmdate('D, d M Y H:i:s', 0) . ' GMT'); + header('Pragma: '); + } else { // Normal http - prevent caching at all cost. + header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0, no-transform'); + header('Expires: ' . gmdate('D, d M Y H:i:s', 0) . ' GMT'); + header('Pragma: no-cache'); + } + + readfile($downloadurl); } diff --git a/lang/en/block_opencast.php b/lang/en/block_opencast.php index c868ee08..823c6bb2 100644 --- a/lang/en/block_opencast.php +++ b/lang/en/block_opencast.php @@ -526,6 +526,7 @@ $string['managedefaultsforuser'] = 'Manage default values'; $string['managedefaultsforuser_desc'] = 'Here you can manage default values for metadata fields used to add videos as well create new series. If you add a default value for a field here, it will be automatically inserted into the field as predefined value (default) inside those mentioned pages.
NOTE: In case this page is empty, or you are looking for a field that does not exist here, please contact your system administrator and ask him/her to configure the metadata fields as defaultable.'; $string['manageseriesforcourse'] = 'Manage series'; +$string['managetranscription_overwrite_info'] = 'Overwriting subtitles is only available for manually uploaded subtitles via this plugin, auto generated subtitles and those that are uploaded outside this plugin which do not have the same caption flavor cannot be overwritten.'; $string['managetranscriptions'] = 'Manage Transcriptions'; $string['managetranscriptions_header'] = 'Manage Event\'s Transcriptions'; $string['maxseries'] = 'Maximum number of series'; @@ -760,6 +761,7 @@ $string['transcription_flavor_value'] = 'Flavor value'; $string['transcriptionaction_thead'] = 'Actions'; $string['transcriptionaddnewbtn'] = 'Add more transcription set'; +$string['transcriptionauto'] = 'Auto'; $string['transcriptiondeletebtn'] = 'Delete transcription set'; $string['transcriptiondeletionfailed'] = 'Failed to delete transcription'; $string['transcriptiondeletionsucceeded'] = 'Transcription deleted successfully.'; @@ -773,6 +775,7 @@ $string['transcriptionheader'] = 'Upload Transcription Files'; $string['transcriptionheaderexplanation'] = 'In this section you are able to upload a transcription file for each service type, with specified file extension, which then will be uploaded alongside the video and be processed accordingly to provide the (speech to text) capability.'; $string['transcriptionltidownloadcompleted'] = 'The transcription is successfully downloaded. {$a}'; +$string['transcriptionmanual'] = 'Manual'; $string['transcriptionreturntomanagement'] = 'Go back to transcription management page'; $string['transcriptionsettingsheader'] = 'Settings for Transcription'; $string['transcriptionuploadfailed'] = 'Transcription upload failed!'; diff --git a/managetranscriptions.php b/managetranscriptions.php index 5961c1a7..056bbe3b 100644 --- a/managetranscriptions.php +++ b/managetranscriptions.php @@ -23,6 +23,7 @@ */ use block_opencast\local\apibridge; +use block_opencast\local\attachment_helper; use core\output\notification; use tool_opencast\local\settings_api; require_once('../../config.php'); @@ -53,7 +54,7 @@ require_capability('block/opencast:addvideo', $coursecontext); $apibridge = apibridge::get_instance($ocinstanceid); -$video = $apibridge->get_opencast_video($identifier); +$video = $apibridge->get_opencast_video($identifier, true); if ($video->error || $video->video->processing_state != 'SUCCEEDED' || empty(get_config('block_opencast', 'transcriptionworkflow_' . $ocinstanceid))) { redirect($redirecturl, @@ -87,37 +88,50 @@ // Check if download is enabled. $allowdownload = get_config('block_opencast', 'allowdownloadtranscription_' . $ocinstanceid); -// Extract caption from attachments/media. $list = []; -$mediapackagestring = $apibridge->get_event_media_package($identifier); -$mediapackagexml = simplexml_load_string($mediapackagestring); - -// First try to get attachments. $attachmentitems = []; -if (property_exists($mediapackagexml, 'attachments')) { +$mediaitems = []; +// We check the publications to extract attachments as well as media. +if ($video->video->publications) { $attachments = []; - foreach ($mediapackagexml->attachments->attachment as $attachment) { - $attachments[] = $attachment; + $medias = []; + // Look through publications one by one. + foreach ($video->video->publications as $publication) { + // Check the attachments. + if (!empty($publication->attachments)) { + foreach ($publication->attachments as $attachment) { + // When the attachment has the mediatype, we record it. + if (!isset($attachments[$attachment->id]) && $attachment->mediatype == attachment_helper::TRANSCRIPTION_MEDIATYPE) { + $attachments[$attachment->id] = $attachment; + } + } + } + // Check the media. + if (!empty($publication->media)) { + foreach ($publication->media as $media) { + // When the media has the mediatype, we record it. + if (!isset($medias[$media->id]) && $media->mediatype == attachment_helper::TRANSCRIPTION_MEDIATYPE) { + $medias[$media->id] = $media; + } + } + } } - $attachmentitems = $renderer->prepare_transcription_items_for_the_menu($attachments, $courseid, $ocinstanceid, $identifier, - 'attachments', $flavors); -} - -// Then try to get media. -$mediaitems = []; -if (property_exists($mediapackagexml, 'media')) { - $mediatracks = []; - foreach ($mediapackagexml->media->track as $track) { - $mediatracks[] = $track; + // We prepare the list items out of the caption attachments. + foreach ($attachments as $attachment) { + $attachmentitems[] = $renderer->prepare_transcription_item_for_the_menu($attachment, $courseid, $ocinstanceid, $identifier, + 'attachment', $flavors); + } + // We prepare the list items out of the caption media. + foreach ($medias as $media) { + $mediaitems[] = $renderer->prepare_transcription_item_for_the_menu($media, $courseid, $ocinstanceid, $identifier, + 'media', $flavors); } - $mediaitems = $renderer->prepare_transcription_items_for_the_menu($mediatracks, $courseid, $ocinstanceid, $identifier, - 'media', $flavors); } - -// After that, we merge everything together. +// Then we combine the items together to display them in the list. $list = array_merge($mediaitems, $attachmentitems); echo $OUTPUT->header(); echo $OUTPUT->heading(get_string('managetranscriptions_header', 'block_opencast')); +echo \core\notification::info(get_string('managetranscription_overwrite_info', 'block_opencast')); echo $renderer->render_manage_transcriptions_table($list, $addnewurl->out(false), $candelete, $allowdownload); echo $OUTPUT->footer(); diff --git a/renderer.php b/renderer.php index 465911c0..3c764612 100644 --- a/renderer.php +++ b/renderer.php @@ -1417,53 +1417,112 @@ public function close_tags_in_html_string($html) { } /** - * Gts and prepares the items to be displayed in transcription management page. + * Returns the transcription list item extracted from either caption media track or caption attachment publication. + * INFO: This method covers all of the caption formats (Opencast 15 and older). * - * @param array $mediapackagesubs the array of mediapackages subcategory containing \SimpleXMLElement. + * @param object $transcriptionitem the caption publication object. * @param int $courseid course id. * @param int $ocinstanceid opencast instance id. * @param string $identifier event identifier. * @param string $domain a flag to determine where that mediapackage subcategory belongs to (attachments or media). * @param array $flavors a list of pre-defined transcriptions flavors. * - * @return array a list of items to display. + * @return stdClass the list item object. */ - public function prepare_transcription_items_for_the_menu($mediapackagesubs, $courseid, $ocinstanceid, $identifier, - $domain, $flavors) { - $items = []; - foreach ($mediapackagesubs as $sub) { - $subobj = json_decode(json_encode((array)$sub)); - $type = $subobj->{'@attributes'}->type; - if (strpos($type, attachment_helper::TRANSCRIPTION_FLAVOR_TYPE) !== false) { - // Extracting language to be displayed in the table. - $flavortype = str_replace(attachment_helper::TRANSCRIPTION_FLAVOR_TYPE . '+', '', $type); - $flavorname = ''; - if (array_key_exists($flavortype, $flavors)) { - $flavorname = $flavors[$flavortype]; + public function prepare_transcription_item_for_the_menu($transcriptionitem, $courseid, $ocinstanceid, + $identifier, $domain, $flavors) { + $lang = ''; + $itemtitle = ''; + $flavorsplitted = explode('/', $transcriptionitem->flavor, 2); + $mainflavor = $flavorsplitted[0]; + if ($mainflavor != attachment_helper::TRANSCRIPTION_FLAVOR_TYPE) { + return null; + } + $subflavor = $flavorsplitted[1]; + // In case we have vtt+{lang}, then it is considered as manual or the old subtitle management of Opencast, + // which we need to support. + if (strpos($subflavor, 'vtt+') !== false) { + $lang = str_replace('vtt+', '', $subflavor); + $itemtitle = $lang; + if (array_key_exists($lang, $flavors)) { + $itemtitle = $flavors[$lang]; + } + $itemtitle .= ' (' . get_string('transcriptionmanual', 'block_opencast') . ')'; + } else if (in_array($subflavor, attachment_helper::TRANSCRIPTION_SUBFLAVOR_TYPES) && !empty($transcriptionitem->tags)) { + // In Opencast 15 and above we shift to caption/delivery. + $$tagdataarr = []; + foreach ($transcriptionitem->tags as $tag) { + // The safety checker. + if (!is_string($tag)) { + continue; + } + if (strpos($tag, 'lang:') !== false) { + $lang = str_replace('lang:', '', $tag); + $tagdataarr['lang'] = $lang; + } + if (strpos($tag, 'generator-type:') !== false) { + $tagdataarr['generatortype'] = str_replace('generator-type:', '', $tag); + } + if (strpos($tag, 'generator:') !== false) { + $tagdataarr['generator'] = str_replace('generator:', '', $tag); + } + if (strpos($tag, 'type:') !== false) { + $tagdataarr['type'] = str_replace('type:', '', $tag); } - $subobj->flavor = !empty($flavorname) ? - $flavorname : - get_string('notranscriptionflavor', 'block_opencast', $flavortype); - - // Extracting id and type from attributes. - $subobj->id = $subobj->{'@attributes'}->id; - $subobj->type = $type; - - // Preparing delete url. - $deleteurl = new moodle_url('/blocks/opencast/deletetranscription.php', - ['courseid' => $courseid, 'ocinstanceid' => $ocinstanceid, - 'video_identifier' => $identifier, 'transcription_identifier' => $subobj->id, ]); - $subobj->deleteurl = $deleteurl->out(false); - - // Preparing download url. - $downloadurl = new moodle_url('/blocks/opencast/downloadtranscription.php', - ['courseid' => $courseid, 'ocinstanceid' => $ocinstanceid, 'domain' => $domain, - 'video_identifier' => $identifier, 'attachment_type' => str_replace(['/', '+'], ['-', '_'], $type), ]); - $subobj->downloadurl = $downloadurl->out(false); - - $items[] = $subobj; } + $itemtitle = $this->prepare_transcription_item_title($tagdataarr); + } + + $item = new stdClass(); + + // Extracting id and type from attributes. + $item->id = $transcriptionitem->id; + $item->flavor = $transcriptionitem->flavor; + + $item->title = !empty($itemtitle) ? + $itemtitle : + get_string('notranscriptionflavor', 'block_opencast', $lang); + + // Preparing delete url. + $deleteurl = new moodle_url('/blocks/opencast/deletetranscription.php', + ['courseid' => $courseid, 'ocinstanceid' => $ocinstanceid, + 'video_identifier' => $identifier, 'transcription_identifier' => $item->id, ]); + $item->deleteurl = $deleteurl->out(false); + + // Preparing download url. + $downloadurl = new moodle_url('/blocks/opencast/downloadtranscription.php', + ['courseid' => $courseid, 'ocinstanceid' => $ocinstanceid, 'domain' => $domain, + 'video_identifier' => $identifier, 'transcription_identifier' => $item->id, ]); + $item->downloadurl = $downloadurl->out(false); + + return $item; + } + + /** + * Prepares the transcription item title based of the tags data. + * + * @param array $tagdataarr the array of extraced data from the tag. + * + * @return string the title + */ + private function prepare_transcription_item_title($tagdataarr) { + $titlearr = []; + if (array_key_exists('generator', $tagdataarr)) { + $generator = ucfirst($tagdataarr['generator']); + $titlearr[] = $generator; + } + if (array_key_exists('lang', $tagdataarr)) { + $titlearr[] = $tagdataarr['lang']; + } + if (array_key_exists('generatortype', $tagdataarr)) { + $stringkey = $tagdataarr['generatortype'] == 'auto' ? 'transcriptionauto' : 'transcriptionmanual'; + $generatortype = get_string($stringkey, 'block_opencast'); + $titlearr[] = "({$generatortype})"; + } + if (array_key_exists('type', $tagdataarr)) { + $type = ucfirst($tagdataarr['type']); + $titlearr[] = "({$type})"; } - return $items; + return implode(' - ', $titlearr); } } diff --git a/templates/transcriptions_table.mustache b/templates/transcriptions_table.mustache index 89cc18bf..ff4282c1 100644 --- a/templates/transcriptions_table.mustache +++ b/templates/transcriptions_table.mustache @@ -44,7 +44,7 @@ {{#list}} - {{flavor}} + {{title}} {{#hasactions}} @@ -76,4 +76,4 @@ {{# str}}addnewtranscription, block_opencast {{/ str}} -{{/addnewurl}} \ No newline at end of file +{{/addnewurl}}