* Update inject.js * Update inject.js * Update inject.js * Update inject.js * Update inject.js * Update inject.js * Update inject.js * update Update inject.js --------- Co-authored-by: rlauu <46294892+rlauu@users.noreply.github.com>
This commit is contained in:
parent
b7889c44c1
commit
1f17792bc6
@ -3,14 +3,23 @@ const introSkipper = {
|
|||||||
d: msg => console.debug("[intro skipper] ", msg),
|
d: msg => console.debug("[intro skipper] ", msg),
|
||||||
setup() {
|
setup() {
|
||||||
this.initializeState();
|
this.initializeState();
|
||||||
|
this.initializeObserver();
|
||||||
|
this.currentOption = localStorage.getItem('introskipperOption') || 'Show Button';
|
||||||
document.addEventListener("viewshow", this.viewShow.bind(this));
|
document.addEventListener("viewshow", this.viewShow.bind(this));
|
||||||
window.fetch = this.fetchWrapper.bind(this);
|
window.fetch = this.fetchWrapper.bind(this);
|
||||||
this.videoPositionChanged = this.videoPositionChanged.bind(this);
|
this.videoPositionChanged = this.videoPositionChanged.bind(this);
|
||||||
|
this.handleEscapeKey = this.handleEscapeKey.bind(this);
|
||||||
this.d("Registered hooks");
|
this.d("Registered hooks");
|
||||||
},
|
},
|
||||||
initializeState() {
|
initializeState() {
|
||||||
Object.assign(this, { allowEnter: true, skipSegments: {}, videoPlayer: null, skipButton: null, osdElement: null, skipperData: null, currentEpisodeId: null, injectMetadata: false });
|
Object.assign(this, { allowEnter: true, skipSegments: {}, videoPlayer: null, skipButton: null, osdElement: null, skipperData: null, currentEpisodeId: null, injectMetadata: false });
|
||||||
},
|
},
|
||||||
|
initializeObserver() {
|
||||||
|
this.observer = new MutationObserver(mutations => {
|
||||||
|
const actionSheet = mutations[mutations.length - 1].target.querySelector('.actionSheet');
|
||||||
|
if (actionSheet && !actionSheet.querySelector(`[data-id="${'introskipperMenu'}"]`)) this.injectIntroSkipperOptions(actionSheet);
|
||||||
|
});
|
||||||
|
},
|
||||||
/** Wrapper around fetch() that retrieves skip segments for the currently playing item or metadata. */
|
/** Wrapper around fetch() that retrieves skip segments for the currently playing item or metadata. */
|
||||||
async fetchWrapper(resource, options) {
|
async fetchWrapper(resource, options) {
|
||||||
const response = await this.originalFetch(resource, options);
|
const response = await this.originalFetch(resource, options);
|
||||||
@ -60,8 +69,12 @@ const introSkipper = {
|
|||||||
this.d("Hooking video timeupdate");
|
this.d("Hooking video timeupdate");
|
||||||
this.videoPlayer.addEventListener("timeupdate", this.videoPositionChanged);
|
this.videoPlayer.addEventListener("timeupdate", this.videoPositionChanged);
|
||||||
this.osdElement = document.querySelector("div.videoOsdBottom")
|
this.osdElement = document.querySelector("div.videoOsdBottom")
|
||||||
|
this.observer.observe(document.body, { childList: true, subtree: false });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
this.observer.disconnect();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Injects the CSS used by the skip intro button.
|
* Injects the CSS used by the skip intro button.
|
||||||
@ -167,7 +180,7 @@ const introSkipper = {
|
|||||||
/** Get the currently playing skippable segment. */
|
/** Get the currently playing skippable segment. */
|
||||||
getCurrentSegment(position) {
|
getCurrentSegment(position) {
|
||||||
for (const [key, segment] of Object.entries(this.skipSegments)) {
|
for (const [key, segment] of Object.entries(this.skipSegments)) {
|
||||||
if ((position > segment.ShowSkipPromptAt && position < segment.HideSkipPromptAt - 1) ||
|
if ((position > segment.ShowSkipPromptAt && position < segment.HideSkipPromptAt - 1) ||
|
||||||
(this.osdVisible() && position > segment.IntroStart && position < segment.IntroEnd - 1)) {
|
(this.osdVisible() && position > segment.IntroStart && position < segment.IntroEnd - 1)) {
|
||||||
segment.SegmentType = key;
|
segment.SegmentType = key;
|
||||||
return segment;
|
return segment;
|
||||||
@ -190,18 +203,22 @@ const introSkipper = {
|
|||||||
if (!this.skipButton) return;
|
if (!this.skipButton) return;
|
||||||
const embyButton = this.skipButton.querySelector(".emby-button");
|
const embyButton = this.skipButton.querySelector(".emby-button");
|
||||||
const segmentType = this.getCurrentSegment(this.videoPlayer.currentTime).SegmentType;
|
const segmentType = this.getCurrentSegment(this.videoPlayer.currentTime).SegmentType;
|
||||||
if (segmentType === "None") {
|
if (segmentType === "None" || this.currentOption === "Off" || !this.allowEnter) {
|
||||||
if (!this.skipButton.classList.contains('show')) return;
|
if (this.skipButton.classList.contains('show')) {
|
||||||
this.skipButton.classList.remove('show');
|
this.skipButton.classList.remove('show');
|
||||||
embyButton.addEventListener("transitionend", () => {
|
embyButton.addEventListener("transitionend", () => {
|
||||||
this.skipButton.classList.add("hide");
|
this.skipButton.classList.add("hide");
|
||||||
this.allowEnter = true;
|
if (this.osdVisible()) {
|
||||||
if (this.osdVisible()) {
|
this.osdElement.querySelector('button.btnPause').focus();
|
||||||
this.osdElement.querySelector('button.btnPause').focus();
|
} else {
|
||||||
} else {
|
embyButton.originalBlur();
|
||||||
embyButton.originalBlur();
|
}
|
||||||
}
|
}, { once: true });
|
||||||
}, { once: true });
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.currentOption === "Automatically Skip" || (this.currentOption === "Button w/ auto PiP" && document.pictureInPictureElement)) {
|
||||||
|
this.doSkip();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.skipButton.querySelector("#btnSkipSegmentText").textContent = this.skipButton.dataset[segmentType];
|
this.skipButton.querySelector("#btnSkipSegmentText").textContent = this.skipButton.dataset[segmentType];
|
||||||
@ -228,10 +245,88 @@ const introSkipper = {
|
|||||||
}
|
}
|
||||||
this.d(`Skipping ${segment.SegmentType}`);
|
this.d(`Skipping ${segment.SegmentType}`);
|
||||||
this.allowEnter = false;
|
this.allowEnter = false;
|
||||||
|
const seekedHandler = () => {
|
||||||
|
this.videoPlayer.removeEventListener('seeked', seekedHandler);
|
||||||
|
setTimeout(() => {
|
||||||
|
this.allowEnter = true;
|
||||||
|
}, 500);
|
||||||
|
};
|
||||||
|
this.videoPlayer.addEventListener('seeked', seekedHandler);
|
||||||
this.videoPlayer.currentTime = segment.SegmentType === "Credits" && this.videoPlayer.duration - segment.IntroEnd < 3
|
this.videoPlayer.currentTime = segment.SegmentType === "Credits" && this.videoPlayer.duration - segment.IntroEnd < 3
|
||||||
? this.videoPlayer.duration + 10
|
? this.videoPlayer.duration + 10
|
||||||
: segment.IntroEnd;
|
: segment.IntroEnd;
|
||||||
},
|
},
|
||||||
|
createButton(ref, id, innerHTML, clickHandler) {
|
||||||
|
const button = ref.cloneNode(true);
|
||||||
|
button.setAttribute('data-id', id);
|
||||||
|
button.innerHTML = innerHTML;
|
||||||
|
button.addEventListener('click', clickHandler);
|
||||||
|
return button;
|
||||||
|
},
|
||||||
|
closeSubmenu(fullscreen) {
|
||||||
|
document.querySelector('.dialogContainer').remove();
|
||||||
|
document.querySelector('.dialogBackdrop').remove()
|
||||||
|
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Control' }));
|
||||||
|
if (!fullscreen) return;
|
||||||
|
document.removeEventListener('keydown', this.handleEscapeKey);
|
||||||
|
document.querySelector('.btnVideoOsdSettings').focus();
|
||||||
|
},
|
||||||
|
openSubmenu(ref, menu) {
|
||||||
|
const options = ['Show Button', 'Button w/ auto PiP', 'Automatically Skip', 'Off'];
|
||||||
|
const submenu = menu.cloneNode(true);
|
||||||
|
const scroller = submenu.querySelector('.actionSheetScroller');
|
||||||
|
scroller.innerHTML = '';
|
||||||
|
options.forEach(option => {
|
||||||
|
if (option !== 'Button w/ auto PiP' || document.pictureInPictureEnabled) {
|
||||||
|
const button = this.createButton(ref, `introskipper-${option.toLowerCase().replace(' ', '-')}`,
|
||||||
|
`<span class="actionsheetMenuItemIcon listItemIcon listItemIcon-transparent material-icons check" aria-hidden="true" style="visibility:${option === this.currentOption ? 'visible' : 'hidden'};"></span><div class="listItemBody actionsheetListItemBody"><div class="listItemBodyText actionSheetItemText">${option}</div></div>`,
|
||||||
|
() => this.selectOption(option));
|
||||||
|
scroller.appendChild(button);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const backdrop = document.createElement('div');
|
||||||
|
backdrop.className = 'dialogBackdrop dialogBackdropOpened';
|
||||||
|
document.body.append(backdrop, submenu);
|
||||||
|
const actionSheet = submenu.querySelector('.actionSheet');
|
||||||
|
if (actionSheet.classList.contains('actionsheet-not-fullscreen')) {
|
||||||
|
this.adjustPosition(actionSheet, document.querySelector('.btnVideoOsdSettings'));
|
||||||
|
submenu.addEventListener('click', () => this.closeSubmenu(false));
|
||||||
|
} else {
|
||||||
|
submenu.querySelector('.btnCloseActionSheet').addEventListener('click', () => this.closeSubmenu(true))
|
||||||
|
scroller.addEventListener('click', () => this.closeSubmenu(true))
|
||||||
|
document.addEventListener('keydown', this.handleEscapeKey);
|
||||||
|
setTimeout(() => scroller.firstElementChild.focus(), 240);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectOption(option) {
|
||||||
|
this.currentOption = option;
|
||||||
|
localStorage.setItem('introskipperOption', option);
|
||||||
|
this.d(`Introskipper option selected and saved: ${option}`);
|
||||||
|
},
|
||||||
|
injectIntroSkipperOptions(actionSheet) {
|
||||||
|
if (!this.skipButton) return;
|
||||||
|
const statsButton = actionSheet.querySelector('[data-id="stats"]');
|
||||||
|
if (!statsButton) return;
|
||||||
|
const menuItem = this.createButton(statsButton, 'introskipperMenu',
|
||||||
|
`<div class="listItemBody actionsheetListItemBody"><div class="listItemBodyText actionSheetItemText">Intro Skipper</div></div><div class="listItemAside actionSheetItemAsideText">${this.currentOption}</div>`,
|
||||||
|
() => this.openSubmenu(statsButton, actionSheet.closest('.dialogContainer')));
|
||||||
|
const originalWidth = actionSheet.offsetWidth;
|
||||||
|
statsButton.before(menuItem);
|
||||||
|
if (actionSheet.classList.contains('actionsheet-not-fullscreen')) this.adjustPosition(actionSheet, menuItem, originalWidth);
|
||||||
|
},
|
||||||
|
adjustPosition(element, reference, originalWidth) {
|
||||||
|
if (originalWidth) {
|
||||||
|
const currentTop = parseInt(element.style.top, 10) || 0;
|
||||||
|
element.style.top = `${currentTop - reference.offsetHeight}px`;
|
||||||
|
const newWidth = Math.max(reference.offsetWidth - originalWidth, 0);
|
||||||
|
const originalLeft = parseInt(element.style.left, 10) || 0;
|
||||||
|
element.style.left = `${originalLeft - newWidth / 2}px`;
|
||||||
|
} else {
|
||||||
|
const rect = reference.getBoundingClientRect();
|
||||||
|
element.style.left = `${Math.min(rect.left - (element.offsetWidth - rect.width) / 2, window.innerWidth - element.offsetWidth - 10)}px`;
|
||||||
|
element.style.top = `${rect.top - element.offsetHeight + rect.height}px`;
|
||||||
|
}
|
||||||
|
},
|
||||||
injectSkipperFields(metadataFormFields) {
|
injectSkipperFields(metadataFormFields) {
|
||||||
const skipperFields = document.createElement('div');
|
const skipperFields = document.createElement('div');
|
||||||
skipperFields.className = 'detailSection introskipperSection';
|
skipperFields.className = 'detailSection introskipperSection';
|
||||||
@ -355,6 +450,12 @@ const introSkipper = {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.doSkip();
|
this.doSkip();
|
||||||
|
},
|
||||||
|
handleEscapeKey(e) {
|
||||||
|
if (e.key === 'Escape' || e.keyCode === 461 || e.keyCode === 10009) {
|
||||||
|
e.stopPropagation();
|
||||||
|
this.closeSubmenu(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
introSkipper.setup();
|
introSkipper.setup();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user