Merge pull request #3745 from Viperinius/chapter-markers
Add chapter markings to video player slider
This commit is contained in:
commit
1ac326d40b
5 changed files with 123 additions and 7 deletions
|
@ -52,6 +52,7 @@
|
||||||
- [MinecraftPlaye](https://github.com/MinecraftPlaye)
|
- [MinecraftPlaye](https://github.com/MinecraftPlaye)
|
||||||
- [Matthew Jones](https://github.com/matthew-jones-uk)
|
- [Matthew Jones](https://github.com/matthew-jones-uk)
|
||||||
- [taku0](https://github.com/taku0)
|
- [taku0](https://github.com/taku0)
|
||||||
|
- [Viperinius](https://github.com/Viperinius)
|
||||||
- [is343](https://github.com/is343)
|
- [is343](https://github.com/is343)
|
||||||
- [Meet Pandya](https://github.com/meet-k-pandya)
|
- [Meet Pandya](https://github.com/meet-k-pandya)
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
<div class="flex flex-direction-row align-items-center">
|
<div class="flex flex-direction-row align-items-center">
|
||||||
<div class="osdTextContainer startTimeText osdPositionText" style="margin: 0 .25em 0 0;"></div>
|
<div class="osdTextContainer startTimeText osdPositionText" style="margin: 0 .25em 0 0;"></div>
|
||||||
<div class="sliderContainer flex-grow" style="margin: .5em 0 .25em;">
|
<div class="sliderContainer flex-grow" style="margin: .5em 0 .25em;">
|
||||||
|
<div class="sliderMarkerContainer"></div>
|
||||||
<input type="range" step=".01" min="0" max="100" value="0" is="emby-slider" class="osdPositionSlider" data-slider-keep-progress="true" />
|
<input type="range" step=".01" min="0" max="100" value="0" is="emby-slider" class="osdPositionSlider" data-slider-keep-progress="true" />
|
||||||
</div>
|
</div>
|
||||||
<div class="osdTextContainer endTimeText osdDurationText" style="margin: 0 0 0 .25em;"></div>
|
<div class="osdTextContainer endTimeText osdDurationText" style="margin: 0 0 0 .25em;"></div>
|
||||||
|
|
|
@ -1563,6 +1563,25 @@ import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../../components
|
||||||
return '<h1 class="sliderBubbleText">' + datetime.getDisplayRunningTime(ticks) + '</h1>';
|
return '<h1 class="sliderBubbleText">' + datetime.getDisplayRunningTime(ticks) + '</h1>';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
nowPlayingPositionSlider.getMarkerInfo = function () {
|
||||||
|
const markers = [];
|
||||||
|
|
||||||
|
const item = currentItem;
|
||||||
|
|
||||||
|
// use markers based on chapters
|
||||||
|
if (item?.Chapters?.length) {
|
||||||
|
item.Chapters.forEach(currentChapter => {
|
||||||
|
markers.push({
|
||||||
|
className: 'chapterMarker',
|
||||||
|
name: currentChapter.Name,
|
||||||
|
progress: currentChapter.StartPositionTicks / item.RunTimeTicks
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return markers;
|
||||||
|
};
|
||||||
|
|
||||||
view.querySelector('.btnPreviousTrack').addEventListener('click', function () {
|
view.querySelector('.btnPreviousTrack').addEventListener('click', function () {
|
||||||
playbackManager.previousTrack(currentPlayer);
|
playbackManager.previousTrack(currentPlayer);
|
||||||
});
|
});
|
||||||
|
|
|
@ -102,6 +102,13 @@ import '../emby-input/emby-input';
|
||||||
fraction *= 100;
|
fraction *= 100;
|
||||||
backgroundLower.style.width = fraction + '%';
|
backgroundLower.style.width = fraction + '%';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (range.markerContainerElement) {
|
||||||
|
if (!range.triedAddingMarkers) {
|
||||||
|
addMarkers(range);
|
||||||
|
}
|
||||||
|
updateMarkers(range, value);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,6 +137,70 @@ import '../emby-input/emby-input';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setMarker(range, valueMarker, marker, valueProgress) {
|
||||||
|
requestAnimationFrame(function () {
|
||||||
|
const bubbleTrackRect = range.sliderBubbleTrack.getBoundingClientRect();
|
||||||
|
const markerRect = marker.getBoundingClientRect();
|
||||||
|
|
||||||
|
if (!bubbleTrackRect.width || !markerRect.width) {
|
||||||
|
// width is not set, most probably because the OSD is currently hidden
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let markerPos = (bubbleTrackRect.width * valueMarker / 100) - markerRect.width / 2;
|
||||||
|
markerPos = Math.min(Math.max(markerPos, - markerRect.width / 2), bubbleTrackRect.width - markerRect.width / 2);
|
||||||
|
|
||||||
|
marker.style.left = markerPos + 'px';
|
||||||
|
|
||||||
|
if (valueProgress >= valueMarker) {
|
||||||
|
marker.classList.remove('unwatched');
|
||||||
|
marker.classList.add('watched');
|
||||||
|
} else {
|
||||||
|
marker.classList.add('unwatched');
|
||||||
|
marker.classList.remove('watched');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateMarkers(range, currentValue) {
|
||||||
|
if (range.markerInfo && range.markerInfo.length && range.markerElements && range.markerElements.length) {
|
||||||
|
for (let i = 0, length = range.markerElements.length; i < length; i++) {
|
||||||
|
if (range.markerInfo.length > i) {
|
||||||
|
setMarker(range, mapFractionToValue(range, range.markerInfo[i].progress), range.markerElements[i], currentValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addMarkers(range) {
|
||||||
|
range.markerInfo = [];
|
||||||
|
if (range.getMarkerInfo) {
|
||||||
|
range.markerInfo = range.getMarkerInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMarkerHtml(markerInfo) {
|
||||||
|
let markerTypeSpecificClasses = '';
|
||||||
|
|
||||||
|
if (markerInfo.className === 'chapterMarker') {
|
||||||
|
markerTypeSpecificClasses = markerInfo.className;
|
||||||
|
|
||||||
|
if (typeof markerInfo.name === 'string' && markerInfo.name.length) {
|
||||||
|
// limit the class length in case the name contains half a novel
|
||||||
|
markerTypeSpecificClasses = `${markerInfo.className} marker-${markerInfo.name.substring(0, 100).toLowerCase().replace(' ', '-')}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return `<span class="material-icons sliderMarker ${markerTypeSpecificClasses}" aria-hidden="true"></span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
range.markerInfo.forEach(info => {
|
||||||
|
range.markerContainerElement.insertAdjacentHTML('beforeend', getMarkerHtml(info));
|
||||||
|
});
|
||||||
|
|
||||||
|
range.markerElements = range.markerContainerElement.querySelectorAll('.sliderMarker');
|
||||||
|
range.triedAddingMarkers = true;
|
||||||
|
}
|
||||||
|
|
||||||
EmbySliderPrototype.attachedCallback = function () {
|
EmbySliderPrototype.attachedCallback = function () {
|
||||||
if (this.getAttribute('data-embyslider') === 'true') {
|
if (this.getAttribute('data-embyslider') === 'true') {
|
||||||
return;
|
return;
|
||||||
|
@ -187,7 +258,9 @@ import '../emby-input/emby-input';
|
||||||
this.backgroundUpper = containerElement.querySelector('.mdl-slider-background-upper');
|
this.backgroundUpper = containerElement.querySelector('.mdl-slider-background-upper');
|
||||||
const sliderBubble = containerElement.querySelector('.sliderBubble');
|
const sliderBubble = containerElement.querySelector('.sliderBubble');
|
||||||
|
|
||||||
let hasHideClass = sliderBubble.classList.contains('hide');
|
let hasHideBubbleClass = sliderBubble.classList.contains('hide');
|
||||||
|
|
||||||
|
this.markerContainerElement = containerElement.querySelector('.sliderMarkerContainer');
|
||||||
|
|
||||||
dom.addEventListener(this, 'input', function () {
|
dom.addEventListener(this, 'input', function () {
|
||||||
this.dragging = true;
|
this.dragging = true;
|
||||||
|
@ -199,9 +272,9 @@ import '../emby-input/emby-input';
|
||||||
const bubbleValue = mapValueToFraction(this, this.value) * 100;
|
const bubbleValue = mapValueToFraction(this, this.value) * 100;
|
||||||
updateBubble(this, bubbleValue, sliderBubble);
|
updateBubble(this, bubbleValue, sliderBubble);
|
||||||
|
|
||||||
if (hasHideClass) {
|
if (hasHideBubbleClass) {
|
||||||
sliderBubble.classList.remove('hide');
|
sliderBubble.classList.remove('hide');
|
||||||
hasHideClass = false;
|
hasHideBubbleClass = false;
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
passive: true
|
passive: true
|
||||||
|
@ -215,7 +288,7 @@ import '../emby-input/emby-input';
|
||||||
}
|
}
|
||||||
|
|
||||||
sliderBubble.classList.add('hide');
|
sliderBubble.classList.add('hide');
|
||||||
hasHideClass = true;
|
hasHideBubbleClass = true;
|
||||||
}, {
|
}, {
|
||||||
passive: true
|
passive: true
|
||||||
});
|
});
|
||||||
|
@ -227,9 +300,9 @@ import '../emby-input/emby-input';
|
||||||
|
|
||||||
updateBubble(this, bubbleValue, sliderBubble);
|
updateBubble(this, bubbleValue, sliderBubble);
|
||||||
|
|
||||||
if (hasHideClass) {
|
if (hasHideBubbleClass) {
|
||||||
sliderBubble.classList.remove('hide');
|
sliderBubble.classList.remove('hide');
|
||||||
hasHideClass = false;
|
hasHideBubbleClass = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
|
@ -239,7 +312,7 @@ import '../emby-input/emby-input';
|
||||||
/* eslint-disable-next-line compat/compat */
|
/* eslint-disable-next-line compat/compat */
|
||||||
dom.addEventListener(this, (window.PointerEvent ? 'pointerleave' : 'mouseleave'), function () {
|
dom.addEventListener(this, (window.PointerEvent ? 'pointerleave' : 'mouseleave'), function () {
|
||||||
sliderBubble.classList.add('hide');
|
sliderBubble.classList.add('hide');
|
||||||
hasHideClass = true;
|
hasHideBubbleClass = true;
|
||||||
}, {
|
}, {
|
||||||
passive: true
|
passive: true
|
||||||
});
|
});
|
||||||
|
|
|
@ -245,3 +245,25 @@
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 0.25em;
|
margin-bottom: 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sliderMarkerContainer {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: 0 0.54em; /* half of slider thumb size */
|
||||||
|
}
|
||||||
|
|
||||||
|
.sliderMarker {
|
||||||
|
position: absolute;
|
||||||
|
width: 2px;
|
||||||
|
height: 0.5em;
|
||||||
|
transform: translate3d(0, 25%, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sliderMarker.unwatched {
|
||||||
|
background-color: rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sliderMarker.watched {
|
||||||
|
background-color: #00a4dc;
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue