diff --git a/cds/config.py b/cds/config.py
index 31e83afab..fa87fde27 100644
--- a/cds/config.py
+++ b/cds/config.py
@@ -1084,6 +1084,11 @@ def _parse_env_bool(var_name, default=None):
"qs": 'q=keywords.name:"VNR" OR keywords.name:"video news release"',
},
]
+###############################################################################
+# Record Landing page
+###############################################################################
+
+DESCRIPTION_PREVIEW_LINES = 10
###############################################################################
# Security
diff --git a/cds/modules/deposit/api.py b/cds/modules/deposit/api.py
index b7ae89048..0e3a89384 100644
--- a/cds/modules/deposit/api.py
+++ b/cds/modules/deposit/api.py
@@ -77,7 +77,7 @@
)
from ..records.minters import cds_doi_generator, is_local_doi, report_number_minter
from ..records.resolver import record_resolver
-from ..records.utils import is_record, lowercase_value, parse_video_chapters
+from ..records.utils import is_record, lowercase_value, parse_video_chapters, get_existing_chapter_frame_timestamps
from ..records.validators import PartialDraft4Validator
from ..records.permissions import is_public
from .errors import DiscardConflict
@@ -926,6 +926,10 @@ def _has_chapters_changed(self, old_record=None):
if curr["seconds"] != old["seconds"] or curr["title"] != old["title"]:
return True
+ if current_chapters and not get_existing_chapter_frame_timestamps(self):
+ # Chapters did not change, but chapter frames doesn't exist
+ return True
+
return False
def _trigger_chapter_frame_extraction(self):
diff --git a/cds/modules/flows/tasks.py b/cds/modules/flows/tasks.py
index f9df6918a..d85024922 100644
--- a/cds/modules/flows/tasks.py
+++ b/cds/modules/flows/tasks.py
@@ -63,7 +63,7 @@
from ..opencast.api import OpenCast
from ..opencast.error import RequestError
from ..opencast.utils import get_qualities
-from ..records.utils import to_string, parse_video_chapters
+from ..records.utils import to_string, parse_video_chapters, get_existing_chapter_frame_timestamps
from ..xrootd.utils import file_opener_xrootd
from .deposit import index_deposit_project
from .files import dispose_object_version, move_file_into_local
@@ -834,7 +834,7 @@ def run(self, *args, **kwargs):
raise ValueError("Video duration is 0 - cannot extract frames")
# Check which timestamps already have frames
- existing_timestamps = self._get_existing_chapter_frame_timestamps(deposit_video)
+ existing_timestamps = get_existing_chapter_frame_timestamps(deposit_video)
def progress_updater(current_chapter):
"""Progress reporter."""
@@ -883,18 +883,6 @@ def progress_updater(current_chapter):
self.log("Finished task {0}".format(kwargs["task_id"]))
return "Created {0} chapter frames.".format(total_frames)
- def _get_existing_chapter_frame_timestamps(self, deposit):
- """Get timestamps of existing chapter frames."""
- master_file = CDSVideosFilesIterator.get_master_video_file(deposit)
- frames = CDSVideosFilesIterator.get_video_frames(master_file)
-
- existing = set()
- for f in frames:
- tags = f.get("tags", {})
- if tags.get("is_chapter_frame") == "true":
- existing.add(float(tags.get("timestamp")))
- return existing
-
@classmethod
def _create_chapter_frames(
cls,
diff --git a/cds/modules/records/static/templates/cds_records/video/detail.html b/cds/modules/records/static/templates/cds_records/video/detail.html
index d079dade0..b536dea08 100644
--- a/cds/modules/records/static/templates/cds_records/video/detail.html
+++ b/cds/modules/records/static/templates/cds_records/video/detail.html
@@ -235,7 +235,7 @@
{{translation.title.title}}
-
+
diff --git a/cds/modules/records/templates/cds_records/record_detail.html b/cds/modules/records/templates/cds_records/record_detail.html
index ab0efd30e..2239c2c3b 100644
--- a/cds/modules/records/templates/cds_records/record_detail.html
+++ b/cds/modules/records/templates/cds_records/record_detail.html
@@ -100,6 +100,7 @@
record-views="{{ record_view_url }}"
media-download-event-url="{{ user_action_media_download_url }}"
related-query-url="{{ related_query_url }}"
+ preview-lines="{{ config.DESCRIPTION_PREVIEW_LINES }}"
>
diff --git a/cds/modules/records/utils.py b/cds/modules/records/utils.py
index f269dc5e5..2c6684512 100644
--- a/cds/modules/records/utils.py
+++ b/cds/modules/records/utils.py
@@ -32,6 +32,7 @@
from urllib import parse
import six
+from cds.modules.records.api import CDSVideosFilesIterator
from flask import current_app, g, request
from flask_security import current_user
from invenio_db import db
@@ -486,6 +487,19 @@ def to_string(value):
return json.dumps(value)
+def get_existing_chapter_frame_timestamps(deposit):
+ """Get timestamps of existing chapter frames."""
+ master_file = CDSVideosFilesIterator.get_master_video_file(deposit)
+ frames = CDSVideosFilesIterator.get_video_frames(master_file)
+
+ existing = set()
+ for f in frames:
+ tags = f.get("tags", {})
+ if tags.get("is_chapter_frame") == "true":
+ existing.add(float(tags.get("timestamp")))
+ return existing
+
+
def parse_video_chapters(description):
"""Parse YouTube-style chapter timestamps from video description.
diff --git a/cds/modules/theme/assets/bootstrap3/js/cds_records/cdsRecord.js b/cds/modules/theme/assets/bootstrap3/js/cds_records/cdsRecord.js
index 2760868a3..0327b37de 100644
--- a/cds/modules/theme/assets/bootstrap3/js/cds_records/cdsRecord.js
+++ b/cds/modules/theme/assets/bootstrap3/js/cds_records/cdsRecord.js
@@ -61,6 +61,8 @@ function cdsRecordController($scope, $sce, $http, $timeout, $filter) {
$scope.transcriptSearch = "";
$scope.chapters = [];
$scope.activeTab = "chapters"; // Default to chapters tab
+ $scope.shortDescription = "";
+ $scope.fullDescription = "";
const REQUEST_HEADERS = {
"Content-Type": "application/json",
@@ -188,9 +190,24 @@ function cdsRecordController($scope, $sce, $http, $timeout, $filter) {
$scope.$watch("record", function (newVal) {
if (newVal) {
$scope.initVttLoad(newVal);
+ $scope.prepareDescriptions(newVal.metadata.description);
}
});
+ $scope.prepareDescriptions = function (description) {
+ if (!description) {
+ $scope.shortDescription = "No description";
+ $scope.fullDescription = "No description";
+ return;
+ }
+
+ const lines = description.split(/\r?\n/);
+ const firstTen = lines.slice(0, $scope.DESCRIPTION_PREVIEW_LINES).join("\n");
+
+ $scope.shortDescription = $scope.processDescriptionWithClickableTimestamps(firstTen);
+ $scope.fullDescription = $scope.processDescriptionWithClickableTimestamps(description);
+ };
+
$scope.initVttLoad = function (record) {
const files = record.metadata._files || [];
@@ -644,6 +661,8 @@ function cdsRecordView($http) {
scope.relatedQueryUrl = attrs.relatedQueryUrl;
+ scope.DESCRIPTION_PREVIEW_LINES = parseInt(attrs.previewLines, 10) || 10;
+
// Get the record object and make it available to the scope
$http.get(attrs.record).then(
function (response) {
diff --git a/tests/unit/test_flows_tasks.py b/tests/unit/test_flows_tasks.py
index 71f2b1ffe..ce9ca9eee 100644
--- a/tests/unit/test_flows_tasks.py
+++ b/tests/unit/test_flows_tasks.py
@@ -725,7 +725,7 @@ def test_extract_chapter_frames_task_cleanup(app, db, bucket, video, users):
mock.patch.object(ExtractChapterFramesTask, "_base_payload", {"tags": {"duration": 100}}), \
mock.patch("cds.modules.flows.tasks.ExtractFramesTask._create_object") as mock_create_object, \
mock.patch("cds.modules.flows.tasks.ExtractChapterFramesTask._build_chapter_vtt"), \
- mock.patch("cds.modules.flows.tasks.ExtractChapterFramesTask._get_existing_chapter_frame_timestamps") as mock_existing:
+ mock.patch("cds.modules.flows.tasks.get_existing_chapter_frame_timestamps") as mock_existing:
# Track created & disposed timestamps (floats)
created_timestamps = set()