From 056108a51f09c4ead4bb43aedbfea786a7421a0c Mon Sep 17 00:00:00 2001 From: ferishili Date: Tue, 26 Mar 2024 12:38:43 +0100 Subject: [PATCH] Update transcriptions for OC 15, fixes #368 --- classes/local/attachment_helper.php | 109 +++++++++++-- deletetranscription.php | 31 +++- downloadtranscription.php | 193 +++++++++++++----------- lang/en/block_opencast.php | 2 + managetranscriptions.php | 59 +++++--- renderer.php | 133 +++++++++++----- templates/transcriptions_table.mustache | 4 +- 7 files changed, 363 insertions(+), 168 deletions(-) diff --git a/classes/local/attachment_helper.php b/classes/local/attachment_helper.php index 14c31ab8..6360f31f 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,,) = 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 35b21b8f..0ccffac7 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,34 @@ } 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 0959930b..228e4823 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 39e31039..c22b380c 100644 --- a/lang/en/block_opencast.php +++ b/lang/en/block_opencast.php @@ -833,6 +833,8 @@ $string['unabletodownloadtranscription'] = 'Unable to download transcription'; $string['transcriptionltidownloadcompleted'] = 'The transcription is successfully downloaded. {$a}'; $string['transcriptionreturntomanagement'] = 'Go back to transcription management page'; +$string['transcriptionmanual'] = 'Manual'; +$string['transcriptionauto'] = 'Auto'; // Strings for live update feature. $string['liveupdate_settingheader'] = 'Live Status Update'; $string['liveupdate_settingenabled'] = 'Enable live status update feature'; diff --git a/managetranscriptions.php b/managetranscriptions.php index 2fed1f78..96fbb59c 100644 --- a/managetranscriptions.php +++ b/managetranscriptions.php @@ -24,6 +24,7 @@ require_once('../../config.php'); use block_opencast\local\apibridge; +use block_opencast\local\attachment_helper; use core\output\notification; use tool_opencast\local\settings_api; @@ -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,34 +88,46 @@ // 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(); diff --git a/renderer.php b/renderer.php index 886b5923..2b1f18b4 100644 --- a/renderer.php +++ b/renderer.php @@ -1416,53 +1416,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'; + $generator_type = get_string($stringkey, 'block_opencast'); + $titlearr[] = "({$generator_type})"; + } + 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}}