Skip to content

Commit

Permalink
Add to playlist bugfix (#5445)
Browse files Browse the repository at this point in the history
* Use active section/timespan items in Ramp to populate add to playlist options

* Add form validation, fix playlist form reset after success

* Disable/enable current track option based on the player's currentTime

* Update app/assets/javascripts/ramp_utils.js

Co-authored-by: Mason Ballengee <[email protected]>

* Update app/assets/javascripts/ramp_utils.js

Co-authored-by: Mason Ballengee <[email protected]>

* Update app/assets/javascripts/ramp_utils.js

Co-authored-by: Mason Ballengee <[email protected]>

---------

Co-authored-by: Mason Ballengee <[email protected]>
  • Loading branch information
Dananji and masaball authored Nov 2, 2023
1 parent c6725bd commit 1032b41
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 97 deletions.
137 changes: 80 additions & 57 deletions app/assets/javascripts/ramp_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,87 @@
* --- END LICENSE_HEADER BLOCK ---
*/

/** Get the current active structure item from DOM */
function getActiveItem() {
let currentPlayer = document.getElementById('iiif-media-player');
let duration = currentPlayer.player.duration();
let currentStructureItem = $('li[class="ramp--structured-nav__list-item active"]');
let currentSection = $('div[class="ramp--structured-nav__section active"]');

if(currentStructureItem?.length > 0) {
/**
* When there's an active timespan in the structured navigation
* use its details to populate the create timeline and add to
* playlist optioins
*/
let label = currentStructureItem[0].dataset.label;
let activeCanvasOnly = currentSection.parent().is(currentStructureItem);
// When canvas item is the only active structure item, add it as an option
if(activeCanvasOnly) {
let { mediafrag, label } = currentSection[0].dataset;
let [ itemId, timeHash ] = mediafrag.split('#t=');
return {
label,
times: {
begin: parseFloat(timeHash.split(',')[0]) || 0,
end: parseFloat(timeHash.split(',')[1]) || duration
},
tags: ['current-track', 'current-section'],
streamId: itemId.split('/').pop()
}
}

// When structure has an active timespan child
if(currentStructureItem.find('a').length > 0) {
let item = currentStructureItem.find('a')[0];
let timeHash = item.hash.split('#t=').pop();
let times = {
begin: parseFloat(timeHash.split(',')[0]) || 0,
end: parseFloat(timeHash.split(',')[1]) || duration
}
let streamId = item.pathname.split('/').pop();
return { label, times, tags: ['current-track'], streamId };
}
} else if (currentSection?.length > 0) {
/** When the structured navigation doesn't have an active timespan
* get the current active section to populate the timeline and add
* to playlist options */
let label = currentSection[0].dataset.label;
return {
label,
times: {
begin: currentPlayer.player.currentTime(),
end: duration,
},
tags: ['current-section'],
streamId: '',
}
}
}

/**
* Get new timeline scopes for active section playing
* Get new timeline scopes for active section
* @function getTimelineScopes
* @param title title of the mediaobject
* @return { [{string, int, string}], string } { [{label, tracks, t}], streamId } = [scope label, number of tracks, mediafragment], masterfile id
* @returns {object} { [{label: string, tracks: int, times: { begin: float, end: float }, tag: string }], streamId: string }
* {[{ scope label, number of tracks, { start, end times of the mediafragment }, tag }], masterfile id }
*/
function getTimelineScopes(title) {
function getTimelineScopes() {
let scopes = new Array();
let trackCount = 1;
let currentStructureItem = $('li[class="ramp--structured-nav__list-item active"]');
let currentStructureItem = $('li[class="ramp--structured-nav__list-item active"]') ||
$('div[class="ramp--structured-nav__section active"]');
let activeItem = getActiveItem();
let streamId = activeItem.streamId;
let streamId = '';

scopes.push({
label: activeItem.label,
tracks: trackCount,
times: activeItem.times,
tag: 'current-track',
});
if(activeItem != undefined) {
streamId = activeItem.streamId;
scopes.push({
label: activeItem.label,
tracks: trackCount,
times: activeItem.times,
tags: activeItem.tags,
});
}

let parent = currentStructureItem.closest('ul').closest('li');
while (parent.length > 0) {
Expand All @@ -42,36 +104,18 @@ function getTimelineScopes(title) {
let begin = parseFloat(tracks[0].hash.split('#t=').reverse()[0].split(',')[0]) || 0;
let end = parseFloat(tracks[trackCount - 1].hash.split('#t=').reverse()[0].split(',')[1]) || '';
streamId = tracks[0].pathname.split('/').reverse()[0];
let label = cleanLabel(
parent[0].childNodes[0].textContent,
parent.find('.ramp--structured-nav__section-duration')
);
let label = parent[0].dataset.label;
scopes.push({
label: next.length == 0 ? `${title} - ${label}` : label,
label: label,
tracks: trackCount,
times: { begin, end },
tags: next.length == 0 ? ['current-section'] : [], // mark the outermost item representing the current section
});
parent = next;
}
return { scopes: scopes.reverse(), streamId };
}

/**
* Clean label text from structured navigation
* @param {String} label full label text of active item
* @param {Object} timestamp HTML span element with duration for section items
* @returns {String} label without index numbers and duration information
*/
function cleanLabel(label, timestamp) {
let labelWoIndex = label.replace(/^[0-9]+./, '');
if(timestamp?.length > 0) {
let time = timestamp[0].textContent;
return labelWoIndex.replace(time, '');
} else {
return labelWoIndex.split(' (')[0];
}
}

/**
* Parse time in seconds to hh:mm:ss.ms format
* @param {Number} secTime time in seconds
Expand Down Expand Up @@ -136,7 +180,7 @@ function collapseMoreDetails() {
/** AJAX request for add to playlist for submission for playlist item for
* a selected clip
*/
function addPlaylistItem (playlistId, masterfileId) {
function addPlaylistItem (playlistId, masterfileId, starttime, endtime) {
$.ajax({
url: '/playlists/' + playlistId + '/items',
type: 'POST',
Expand All @@ -145,8 +189,8 @@ function addPlaylistItem (playlistId, masterfileId) {
master_file_id: masterfileId,
title: $('#playlist_item_title').val(),
comment: $('#playlist_item_description').val(),
start_time: $('#playlist_item_start').val(),
end_time: $('#playlist_item_end').val()
start_time: starttime,
end_time: endtime,
}
},
success: function(res) {
Expand Down Expand Up @@ -214,8 +258,6 @@ function handleAddError(error) {

/** Reset add to playlist form */
function resetAddToPlaylistForm() {
$('#playlist_item_start')[0].value = '';
$('#playlist_item_end')[0].value = '';
$('#playlist_item_description').value = '';
$('#playlist_item_title').value = '';
$('input[name="post[playlistitem_scope]"]').prop('checked', false);
Expand All @@ -229,22 +271,3 @@ function closeAlert() {
$('#add_to_playlist_alert').slideUp();
$('#add_to_playlist_form_group').slideDown();
}

/** Get the current active structure item from DOM */
function getActiveItem() {
let currentPlayer = document.getElementById('iiif-media-player');
let duration = currentPlayer.player.duration();
let currentStructureItem = $('li[class="ramp--structured-nav__list-item active"]');
if(currentStructureItem.find('a').length > 0) {
let item = currentStructureItem.find('a')[0];
let label = cleanLabel(item.text,
currentStructureItem.find('.ramp--structured-nav__section-duration'));
let timeHash = item.hash.split('#t=').reverse()[0];
let times = {
begin: parseFloat(timeHash.split(',')[0]) || 0,
end: parseFloat(timeHash.split(',')[1]) || duration
}
let streamId = item.pathname.split('/').reverse()[0];
return { label, times, streamId };
}
}
27 changes: 27 additions & 0 deletions app/assets/stylesheets/avalon/_form.scss
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,30 @@ label {
flex-wrap: nowrap;
}
}

/* Add to playlist form on item-view page */
#add-to-playlist-form-group {
.form-check {
padding-bottom: 0.1rem;
}

#playlist_item_end, #playlist_item_start {
border-radius: 0.25rem;
border: 1px solid $gray;
padding: 0 0.5rem;
}

#playlist_item_end:invalid:focus,
#playlist_item_start:invalid:focus {
color: $danger;
box-shadow: 0 0 10px $danger;
border: 1px solid $danger;
}

#playlist_item_end:valid:focus,
#playlist_item_start:valid:focus {
color: $dark;
box-shadow: none;
border: 1px solid $gray;
}
}
4 changes: 2 additions & 2 deletions app/javascript/components/Ramp.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,12 @@ const Ramp = ({
</div>
<Row className="mx-0">
<Col md={12} lg={12} sm={12}>
<div className="collapse multi-collapse" id="addToPlaylistPanel">
<div className="collapse" id="addToPlaylistPanel">
<div className="card card-body" dangerouslySetInnerHTML={{ __html: playlist.tab }} />
</div>
</Col>
<Col md={12} lg={12} sm={12}>
<div className="collapse multi-collapse" id="shareResourcePanel">
<div className="collapse" id="shareResourcePanel">
<div className="card card-body share-tabs" dangerouslySetInnerHTML={{ __html: share.content }} />
</div>
</Col>
Expand Down
Loading

0 comments on commit 1032b41

Please sign in to comment.