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 @@
+
+