Skip to content

Commit 07c9f6a

Browse files
zifgulegendwordwynnsetlangara2020sz
authored
1213 - Ability to add captions to video nodes (#1215)
- Adds captions to the UrlVideoMedia player. - When a video is uploaded to Kaltura, its captions will also be uploaded to Kaltura and associated with the uploaded video. The captions will be deleted locally after successful upload. - Changes how users create Kaltura videos: to change the Kaltura ID, the user must submit it in the NodeModal, which loads data from Kaltura into the NodeModal to be edited. - Depends on the package [iso-639-1](https://www.npmjs.com/package/iso-639-1) to get ISO 639-1 language names and language codes and convert between the two. **Changes to the video authoring form:** - For mp4 and Kaltura videos, show a toggle for captions (on/off) that is off by default - If the toggle is on, displays one caption row by default, with the option to add more. - Each caption contains: - A file upload field for the caption source. Must be VTT for regular videos, and VTT or SRT for Kaltura videos. - A language field. English by default. - An optional field for the caption label, only visible if the user turns on "Customize label". Otherwise, the name of the language is used as the caption label (e.g. "English", "French"). - A "Display in player" toggle (true by default). The caption should only be displayed in the video player if this is checked. - A button to download the caption. - A video can have a default caption. The “Set as default” button next to each caption sets the default caption. When a Kaltura video node is submitted, the new list of captions replaces the existing list of captions on Kaltura. **Pending captions:** - If an error occurs when uploading a caption to Kaltura (either an error with the .vtt file or the caption metadata), the caption will be saved as a "pending caption" and the user will receive the number of captions that failed to upload. The user can edit pending captions in the NodeModal and add them back to the list. Pending captions are identified by ID, so that adding a pending caption back to the list overwrites the caption with the same ID, if any. ### Details - The set of languages supported by Kaltura is different from the set of languages in ISO 639-1 (see here https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes). We offer a union of Kaltura's language names and the ISO 639-1 language names to users. If the video is a URL video but the caption language does not exist in ISO 639-1, the caption's `srclang` attribute will be "". If the video is a Kaltura video but the caption language does not exist in Kaltura, the label will be set to the language name (unless the user gave a custom label) while the language will be "Undefined". - Since captions do not change often, and to make captions accessible when Kaltura is not available in the backend, captions uploaded to Kaltura are also saved under the node's `typeData.captions`. To keep this information up to date, the captions are fetched from Kaltura every time the node is opened for editing. ## Screenshot New editing workflow for Kaltura videos, and editing captions: https://user-images.githubusercontent.com/44929104/187580872-643b3996-7b1d-4870-b749-f165174fb940.mp4 Caption upload feedback on Kaltura upload dashboard: ![image](https://user-images.githubusercontent.com/44929104/182502434-50472478-e97e-4790-96e2-1e496303e709.png) Pending captions: ![image](https://user-images.githubusercontent.com/44929104/185474436-4824b7c3-eea6-4aa7-95fe-88bd7a543de4.png) --------- Co-authored-by: Legendword <[email protected]> Co-authored-by: Aidin Niavarani <[email protected]> Co-authored-by: langara2020sz <[email protected]>
1 parent aefbb8b commit 07c9f6a

File tree

22 files changed

+1607
-293
lines changed

22 files changed

+1607
-293
lines changed

classes/class.kaltura-api.php

Lines changed: 577 additions & 209 deletions
Large diffs are not rendered by default.

endpoints/endpoints.kaltura.php

Lines changed: 132 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,27 @@ public static function getRoutes()
128128
'permission_callback' => 'KalturaEndpoints::canUploadToKaltura',
129129
],
130130
],
131+
'GET_KALTURA_AVAILABLE_LANGUAGES' => (object) [
132+
'ROUTE' => '/kaltura/languages',
133+
'ARGUMENTS' => [
134+
'methods' => $REST_API_GET_METHOD,
135+
'callback' => 'KalturaEndpoints::getLanguages',
136+
],
137+
],
138+
'GET_KALTURA_VIDEO_CAPTIONS' => (object) [
139+
'ROUTE' => '/kaltura/video/captions',
140+
'ARGUMENTS' => [
141+
'methods' => $REST_API_GET_METHOD,
142+
'callback' => 'KalturaEndpoints::getVideoCaptions',
143+
],
144+
],
145+
'PUT_KALTURA_VIDEO_CAPTIONS' => (object) [
146+
'ROUTE' => '/kaltura/video/captions',
147+
'ARGUMENTS' => [
148+
'methods' => $REST_API_PUT_METHOD,
149+
'callback' => 'KalturaEndpoints::updateVideoCaptions',
150+
],
151+
],
131152
];
132153
}
133154

@@ -291,19 +312,6 @@ public static function getKalturaUploadStatus($request)
291312
];
292313
}
293314

294-
/**
295-
* Save Kaltura upload status
296-
*/
297-
public static function saveVideoUploadStatus($video, $videosToUpload, $newStatus, $kalturaData = null)
298-
{
299-
$video->uploadStatus = $newStatus;
300-
self::_updateUploadLog($videosToUpload);
301-
302-
$node = new TapestryNode($video->tapestryID, $video->nodeID);
303-
self::_saveVideoUploadStatusInNode($node, $newStatus, $kalturaData);
304-
return $node;
305-
}
306-
307315
/**
308316
* Clears Kaltura upload status
309317
*/
@@ -473,7 +481,7 @@ public static function updateConvertingVideos($request)
473481
if ($response->status === EntryStatus::READY) {
474482
self::_saveVideoUploadStatusInNode($node, KalturaUploadStatus::COMPLETE, $response);
475483

476-
$file_path = TapestryHelpers::getPathToNodeMedia($node)->file_path;
484+
$file_path = TapestryHelpers::getPathToMedia($node->getTypeData()->mediaURL)->file_path;
477485
KalturaApi::saveAndDeleteLocalVideo($node, $response, $useKalturaPlayer, $file_path);
478486

479487
$video->currentStatus = KalturaUploadStatus::COMPLETE;
@@ -570,10 +578,80 @@ public static function getKalturaVideoMeta($request)
570578
}
571579
}
572580

581+
public static function getLanguages($request)
582+
{
583+
try {
584+
if (!LOAD_KALTURA) {
585+
throw new TapestryError('KALTURA_NOT_AVAILABLE');
586+
}
587+
588+
$kaltura_api = new KalturaApi();
589+
return $kaltura_api->getLanguages();
590+
} catch (TapestryError $e) {
591+
return new WP_Error($e->getCode(), $e->getMessage(), $e->getStatus());
592+
}
593+
}
594+
595+
public static function getVideoCaptions($request)
596+
{
597+
try {
598+
if (!LOAD_KALTURA) {
599+
throw new TapestryError('KALTURA_NOT_AVAILABLE');
600+
}
601+
602+
$video_entry_id = $request['entry_id'];
603+
604+
$kaltura_api = new KalturaApi();
605+
return $kaltura_api->getCaptionsAndDefaultCaption($video_entry_id);
606+
} catch (TapestryError $e) {
607+
return new WP_Error($e->getCode(), $e->getMessage(), $e->getStatus());
608+
}
609+
}
610+
611+
public static function updateVideoCaptions($request)
612+
{
613+
try {
614+
if (!LOAD_KALTURA) {
615+
throw new TapestryError('KALTURA_NOT_AVAILABLE');
616+
}
617+
618+
$video_entry_id = $request['entry_id'];
619+
$body = json_decode($request->get_body());
620+
$captions = $body->captions;
621+
$default_caption_id = $body->defaultCaptionId;
622+
623+
if (!isset($captions) || !is_array($captions)) {
624+
return null;
625+
}
626+
627+
try {
628+
$kaltura_api = new KalturaApi();
629+
return $kaltura_api->setCaptionsAndDefaultCaption($video_entry_id, $captions, $default_caption_id, true);
630+
} catch (TapestryError $e) {
631+
return new WP_Error($e->getCode(), $e->getMessage(), $e->getStatus());
632+
}
633+
} catch (TapestryError $e) {
634+
return new WP_Error($e->getCode(), $e->getMessage(), $e->getStatus());
635+
}
636+
}
637+
573638
/*******************************************************/
574639
/** PRIVATE FUNCTIONS **/
575640
/*******************************************************/
576641

642+
/**
643+
* Save Kaltura upload status
644+
*/
645+
private static function _saveVideoUploadStatus($video, $videosToUpload, $newStatus, $kalturaData = null)
646+
{
647+
$video->uploadStatus = $newStatus;
648+
self::_updateUploadLog($videosToUpload);
649+
650+
$node = new TapestryNode($video->tapestryID, $video->nodeID);
651+
self::_saveVideoUploadStatusInNode($node, $newStatus, $kalturaData);
652+
return $node;
653+
}
654+
577655
/**
578656
* Update the Kaltura upload status of a video node.
579657
*
@@ -653,7 +731,7 @@ private static function _getVideosToUploadInTapestry($tapestryPostId)
653731

654732
foreach ($tapestry->getNodeIds() as $nodeID) {
655733
$node = new TapestryNode($tapestryPostId, $nodeID);
656-
if (TapestryHelpers::videoCanBeUploaded($node)) {
734+
if (KalturaApi::videoCanBeUploaded($node)) {
657735
$video = (object) [
658736
'tapestryID' => (int) $tapestryPostId,
659737
'nodeID' => $nodeID,
@@ -705,7 +783,7 @@ private static function _performBatchedUploadToKaltura($tapestryPostId, $nodeIds
705783
$batch = array_slice($videosToUpload, $batchStart, KalturaConstants::UPLOAD_BATCH_SIZE);
706784

707785
foreach ($batch as $video) {
708-
self::saveVideoUploadStatus($video, $videosToUpload, KalturaUploadStatus::UPLOADING);
786+
self::_saveVideoUploadStatus($video, $videosToUpload, KalturaUploadStatus::UPLOADING);
709787

710788
$kalturaData = null;
711789
try {
@@ -716,13 +794,13 @@ private static function _performBatchedUploadToKaltura($tapestryPostId, $nodeIds
716794
error_log($error_msg."\nStack trace: \n".$e->getTraceAsString());
717795

718796
$video->additionalInfo = $error_msg;
719-
self::saveVideoUploadStatus($video, $videosToUpload, KalturaUploadStatus::ERROR);
797+
self::_saveVideoUploadStatus($video, $videosToUpload, KalturaUploadStatus::ERROR);
720798
$numUploadedWithError++;
721799
continue;
722800
}
723801

724802
$video->kalturaID = $kalturaData->id;
725-
self::saveVideoUploadStatus($video, $videosToUpload, KalturaUploadStatus::CONVERTING, $kalturaData);
803+
self::_saveVideoUploadStatus($video, $videosToUpload, KalturaUploadStatus::CONVERTING, $kalturaData);
726804
}
727805

728806
// Filter out videos that did not successfully upload so we don't get an infinite loop
@@ -743,16 +821,23 @@ private static function _performBatchedUploadToKaltura($tapestryPostId, $nodeIds
743821
}
744822

745823
if ($response->status === EntryStatus::READY) {
746-
$node = self::saveVideoUploadStatus($video, $videosToUpload, KalturaUploadStatus::COMPLETE);
824+
$node = self::_saveVideoUploadStatus($video, $videosToUpload, KalturaUploadStatus::COMPLETE);
747825
KalturaApi::saveAndDeleteLocalVideo($node, $response, $useKalturaPlayer, $video->file->file_path);
748826
$numSuccessfullyUploaded++;
827+
828+
$failedCaptions = self::_uploadVideoCaptions($node, $kalturaApi, $response);
829+
if ($failedCaptions > 0) {
830+
$plural = $failedCaptions !== 1 ? 's' : '';
831+
$video->additionalInfo = $failedCaptions . ' caption' . $plural . ' failed to upload. Please edit the node to check.';
832+
self::_saveVideoUploadStatus($video, $videosToUpload, KalturaUploadStatus::COMPLETE, null, false);
833+
}
749834
} elseif ($response->status === EntryStatus::ERROR_CONVERTING) {
750835
$video->additionalInfo = 'An error occurred: Could not convert the video.';
751-
self::saveVideoUploadStatus($video, $videosToUpload, KalturaUploadStatus::ERROR);
836+
self::_saveVideoUploadStatus($video, $videosToUpload, KalturaUploadStatus::ERROR);
752837
$numUploadedWithError++;
753838
} else {
754839
$video->additionalInfo = 'An error occurred: Expected the video to be converting, but it was not.';
755-
self::saveVideoUploadStatus($video, $videosToUpload, KalturaUploadStatus::ERROR);
840+
self::_saveVideoUploadStatus($video, $videosToUpload, KalturaUploadStatus::ERROR);
756841
$numUploadedWithError++;
757842
}
758843

@@ -773,7 +858,7 @@ private static function _performBatchedUploadToKaltura($tapestryPostId, $nodeIds
773858

774859
// Mark remaining videos as canceled, if any
775860
for ($i = $batchStart; $i < count($videosToUpload); $i++) {
776-
self::saveVideoUploadStatus($videosToUpload[$i], $videosToUpload, KalturaUploadStatus::CANCELED);
861+
self::_saveVideoUploadStatus($videosToUpload[$i], $videosToUpload, KalturaUploadStatus::CANCELED);
777862
}
778863

779864
return (object) [
@@ -794,13 +879,13 @@ private static function _createUploadQueue($tapestryPostId, $nodeIds)
794879

795880
foreach ($nodeIds as $nodeId) {
796881
$node = new TapestryNode($tapestryPostId, $nodeId);
797-
if (TapestryHelpers::videoCanBeUploaded($node) && KalturaApi::checkVideoFileSize($node)) {
882+
if (KalturaApi::videoCanBeUploaded($node) && KalturaApi::checkVideoFileSize($node)) {
798883
array_push($uploadLog, (object) [
799884
'tapestryID' => $tapestryPostId,
800885
'nodeID' => $nodeId,
801886
'nodeTitle' => $node->getTitle(),
802887
'uploadStatus' => KalturaUploadStatus::NOT_STARTED,
803-
'file' => TapestryHelpers::getPathToNodeMedia($node),
888+
'file' => TapestryHelpers::getPathToMedia($node->getTypeData()->mediaURL),
804889
'kalturaID' => '',
805890
'additionalInfo' => '',
806891
'timestamp' => $timestamp,
@@ -852,4 +937,27 @@ private static function _updateUploadLog($videos)
852937
array_splice($uploadLog, -count($videos), count($uploadLog), self::_filterLoggedVideos($videos));
853938
update_option(KalturaConstants::UPLOAD_LOG_OPTION, $uploadLog);
854939
}
940+
941+
private static function _uploadVideoCaptions($node, $kalturaApi, $kalturaData)
942+
{
943+
$typeData = $node->getTypeData();
944+
$captions = $typeData->captions;
945+
if (!isset($captions) || !is_array($captions)) {
946+
return 0;
947+
}
948+
949+
try {
950+
$captionData = $kalturaApi->setCaptionsAndDefaultCaption($kalturaData->id, $captions, $typeData->defaultCaptionId, false);
951+
952+
$typeData->captions = $captionData->captions;
953+
$typeData->pendingCaptions = $captionData->pendingCaptions;
954+
$typeData->defaultCaptionId = $captionData->defaultCaptionId;
955+
} catch (TapestryError $e) {
956+
$typeData->pendingCaptions = $captions;
957+
}
958+
959+
$node->save();
960+
961+
return count($typeData->pendingCaptions);
962+
}
855963
}

tapestry.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,19 @@ function prefix_title_entity_decode($response)
369369
return $response;
370370
}
371371

372+
// Set the extension and mime type for .vtt files so Wordpress allows their upload
373+
374+
add_filter('wp_check_filetype_and_ext', 'wpse_file_and_ext', 10, 4);
375+
function wpse_file_and_ext($types, $file, $filename, $mimes)
376+
{
377+
if (false !== strpos($filename, '.vtt')) {
378+
$types['ext'] = 'vtt';
379+
$types['type'] = 'text/vtt';
380+
}
381+
382+
return $types;
383+
}
384+
372385
// Analytics
373386

374387
register_activation_hook(__FILE__, 'create_tapestry_analytics_schema');
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
WEBVTT
2+
3+
1
4+
00:00:01.478 --> 00:00:04.020
5+
Example captions
6+
7+
2
8+
00:00:05.045 --> 00:00:09.545
9+
Line 1
10+
11+
3
12+
00:00:09.378 --> 00:00:13.745
13+
Line 2
14+
15+
4
16+
00:00:14.812 --> 00:00:16.144
17+
Multi
18+
Line
19+
Support

0 commit comments

Comments
 (0)