diff --git a/app/controllers/contents.php b/app/controllers/contents.php index da61cc7b3..f239dbd6d 100644 --- a/app/controllers/contents.php +++ b/app/controllers/contents.php @@ -33,5 +33,6 @@ public function index_action() PageLayout::setBodyElementId('opencast-plugin'); $this->studip_version = $this->getStudIPVersion(); + $this->languages = json_encode($GLOBALS['CONTENT_LANGUAGES']); } } diff --git a/app/controllers/course.php b/app/controllers/course.php index f129de0ed..7f8cac67f 100644 --- a/app/controllers/course.php +++ b/app/controllers/course.php @@ -34,6 +34,7 @@ public function index_action() PageLayout::setBodyElementId('opencast-plugin'); $this->studip_version = $this->getStudIPVersion(); + $this->languages = json_encode($GLOBALS['CONTENT_LANGUAGES']); $this->render_template('course/index', $GLOBALS['template_factory']->open('layouts/base.php')); } diff --git a/assets/css/opencast.scss b/assets/css/opencast.scss index 376b90945..b8e13387d 100644 --- a/assets/css/opencast.scss +++ b/assets/css/opencast.scss @@ -745,6 +745,23 @@ h2.oc--loadingbar, .oc--loadingbar-title { } } +/* * * * * * * * * * * * * * * * * * */ +/* U P L O A D E L E M E N T S */ +/* * * * * * * * * * * * * * * * * * */ + +label.oc--file-upload { + cursor: pointer; + color: $base-color; + + input[type=file] { + display: none; + } + .filename { + padding-left: 0.5em; + color: $light-gray-color-80; + } +} + /* * * * * * * * * * * * * * * * * * * * * */ /* S C R E E N S I Z E C L A S S E S */ /* * * * * * * * * * * * * * * * * * * * * */ diff --git a/lib/Models/REST/ApiEventsClient.php b/lib/Models/REST/ApiEventsClient.php index 007c7d27e..6ba05e619 100644 --- a/lib/Models/REST/ApiEventsClient.php +++ b/lib/Models/REST/ApiEventsClient.php @@ -212,4 +212,22 @@ public function deleteEpisode($episode_id) $response = $this->opencastApi->eventsApi->delete($episode_id); return $response['code'] < 400; } + + /** + * Return media files + * + * @param + * + * @return array + */ + public function getMedia($eventId) + { + $response = $this->opencastApi->eventsApi->getMedia($eventId); + + if ($response['code'] == 200) { + return $response['body']; + } + + return false; + } } diff --git a/lib/Models/Videos.php b/lib/Models/Videos.php index 27e5e542b..8b0ef4a22 100644 --- a/lib/Models/Videos.php +++ b/lib/Models/Videos.php @@ -806,4 +806,33 @@ public static function addToCoursePlaylist($eventType, $episode, $video) } } } + + /** + * Fetch caption data for a given token + * + * @param string $token + * + * @return Array which contains urls for the caption files + */ + public static function getCaptionByToken($token) + { + $caption_download = []; + $video = self::findByToken($token); + + // Get caption files + $api_event_client = ApiEventsClient::getInstance($video->config_id); + $media_tracks = $api_event_client->getMedia($video->episode); + + foreach($media_tracks as $track) { + if (substr($track->flavor, 0, 9) === 'captions/' && + $track->mimetype === 'text/vtt') + { + $caption_download[$track->flavor] = [ + 'url' => $track->uri + ]; + } + } + + return $caption_download; + } } diff --git a/lib/RouteMap.php b/lib/RouteMap.php index d03d4e614..a981e6192 100644 --- a/lib/RouteMap.php +++ b/lib/RouteMap.php @@ -49,6 +49,7 @@ public function authenticatedRoutes() $this->app->post('/videos/{token}/report', Routes\Video\VideoReport::class); $this->app->post('/videos/{token}/playlists', Routes\Video\VideoAddToPlaylist::class); + $this->app->get('/videos/{token}/captions', Routes\Video\VideoCaptions::class); $this->app->get('/videos/{token}/shares', Routes\Video\VideoSharesList::class); $this->app->put('/videos/{token}/shares', Routes\Video\VideoSharesUpdate::class); $this->app->post('/videos/{course_id}/copy', Routes\Video\VideoCopyToCourse::class); diff --git a/lib/Routes/Config/SimpleConfigList.php b/lib/Routes/Config/SimpleConfigList.php index c4b2d9d33..88d0119d3 100644 --- a/lib/Routes/Config/SimpleConfigList.php +++ b/lib/Routes/Config/SimpleConfigList.php @@ -31,12 +31,13 @@ public function __invoke(Request $request, Response $response, $args) foreach ($config as $conf) { $config_list[$conf->id] = [ - 'id' => $conf->id, - 'name' => $conf->service_url, - 'version' => $conf->service_version, - 'ingest' => reset(Endpoints::findBySql("config_id = ? AND service_type = 'ingest'", [$conf->id]))->service_url, - 'studio' => $conf->service_url . '/studio/index.html', - 'lti_num' => sizeof(LtiHelper::getLtiLinks($conf->id)) // used to iterate over all Opencast nodes + 'id' => $conf->id, + 'name' => $conf->service_url, + 'version' => $conf->service_version, + 'ingest' => reset(Endpoints::findBySql("config_id = ? AND service_type = 'ingest'", [$conf->id]))->service_url, + 'apievents' => reset(Endpoints::findBySql("config_id = ? AND service_type = 'apievents'", [$conf->id]))->service_url, + 'studio' => $conf->service_url . '/studio/index.html', + 'lti_num' => sizeof(LtiHelper::getLtiLinks($conf->id)) // used to iterate over all Opencast nodes ]; } diff --git a/lib/Routes/Video/VideoCaptions.php b/lib/Routes/Video/VideoCaptions.php new file mode 100644 index 000000000..a4430b740 --- /dev/null +++ b/lib/Routes/Video/VideoCaptions.php @@ -0,0 +1,27 @@ +createResponse([ + 'caption' => $captions + ], $response); + } +} diff --git a/vueapp/common/upload.service.js b/vueapp/common/upload.service.js index 95ee4893a..5362aa46c 100644 --- a/vueapp/common/upload.service.js +++ b/vueapp/common/upload.service.js @@ -167,19 +167,41 @@ class UploadService { let obj = this; return files.reduce(function(promise, file) { return promise.then(function (mediaPackage) { - return obj.addTrack(mediaPackage, file, onProgress); + + var data = new FormData(); + data.append('mediaPackage', mediaPackage); + data.append('flavor', file.flavor); + data.append('tags', ''); + data.append('BODY', file.file, file.file.name); + + return obj.addTrack(data, "/addTrack", onProgress); }); }, Promise.resolve(mediaPackage)) } - addTrack(mediaPackage, track, onProgress) { - var media = track.file; - var data = new FormData(); - data.append('mediaPackage', mediaPackage); - data.append('flavor', track.flavor); - data.append('tags', ''); - data.append('BODY', media, media.name); + async uploadCaptions(files, episode_id, options) { + this.fixFilenames(files); + let onProgress = options.uploadProgress; + let uploadDone = options.uploadDone; + + let obj = this; + return files.reduce(function(promise, file) { + return promise.then(function () { + + var data = new FormData(); + data.append('flavor', file.flavor); + data.append('overwriteExisting', file.overwriteExisting); + data.append('track', file.file); + + return obj.addTrack(data, "/" + episode_id + "/track", onProgress); + }); + }, Promise.resolve()) + .then(() => { + uploadDone(); + }) + } + addTrack(data, url_path, onProgress) { var fnOnProgress = function (event) { onProgress(track, event.loaded, event.total); }; @@ -191,7 +213,7 @@ class UploadService { obj.request = axios.CancelToken.source(); return axios({ - url: obj.service_url + "/addTrack", + url: obj.service_url + url_path, method: "POST", data: data, processData: false, diff --git a/vueapp/components/Videos/Actions/CaptionUpload.vue b/vueapp/components/Videos/Actions/CaptionUpload.vue new file mode 100644 index 000000000..175dfe57d --- /dev/null +++ b/vueapp/components/Videos/Actions/CaptionUpload.vue @@ -0,0 +1,242 @@ + + + diff --git a/vueapp/components/Videos/VideoCard.vue b/vueapp/components/Videos/VideoCard.vue index 2a5e4c107..3a0d8e3bf 100644 --- a/vueapp/components/Videos/VideoCard.vue +++ b/vueapp/components/Videos/VideoCard.vue @@ -299,6 +299,14 @@ export default { }); } + menuItems.push({ + id: 7, + label: this.$gettext('Untertitel hinzufügen'), + icon: 'knife', + emit: 'performAction', + emitArguments: 'CaptionUpload' + }); + menuItems.push({ id: 8, label: this.$gettext('Entfernen'), diff --git a/vueapp/components/Videos/VideoFilePreview.vue b/vueapp/components/Videos/VideoFilePreview.vue index 42cbc9464..9b7c1ca95 100644 --- a/vueapp/components/Videos/VideoFilePreview.vue +++ b/vueapp/components/Videos/VideoFilePreview.vue @@ -7,12 +7,15 @@

Folien

+

+ Untertitel für {{ files.language }} +

Name: {{ file.name }} - + Größe: {{ $filters.filesize(file.size) }} @@ -24,6 +27,16 @@ Entfernen + + + + + + diff --git a/vueapp/components/Videos/VideoUpload.vue b/vueapp/components/Videos/VideoUpload.vue index 23c91eeaa..b9079e7f2 100644 --- a/vueapp/components/Videos/VideoUpload.vue +++ b/vueapp/components/Videos/VideoUpload.vue @@ -122,12 +122,14 @@
- - Aufzeichnung des/der Vortragende*n hinzufügen - - +