fix: update client-side subtitle positioning logic and vertical offset alignment
Build and Release Plugin / release (push) Successful in 33s
Build and Release Plugin / release (push) Successful in 33s
This commit is contained in:
@@ -72,31 +72,96 @@ namespace JellyfinPlugin.Api
|
|||||||
let interceptedItemId = null;
|
let interceptedItemId = null;
|
||||||
let currentCropData = null;
|
let currentCropData = null;
|
||||||
window._currentPlayMethod = null;
|
window._currentPlayMethod = null;
|
||||||
|
window._isVideoDirect = null;
|
||||||
|
|
||||||
function isTranscoding() {
|
function isTranscoding() {
|
||||||
if (window._currentPlayMethod === 'Transcode' || window._currentPlayMethod === 'DirectStream') {
|
if (window._isVideoDirect === false) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
try {
|
if (window._isVideoDirect === true) {
|
||||||
if (window.playbackManager) {
|
return false;
|
||||||
const currentPlayer = window.playbackManager.getCurrentPlayer();
|
}
|
||||||
if (currentPlayer && typeof currentPlayer.getPlayerState === 'function') {
|
return false;
|
||||||
const state = currentPlayer.getPlayerState();
|
}
|
||||||
if (state && state.PlayMethod) {
|
|
||||||
return state.PlayMethod === 'Transcode' || state.PlayMethod === 'DirectStream';
|
function ensureBoundingBoxHook(video) {
|
||||||
|
if (!video._origGetBoundingClientRect) {
|
||||||
|
video._origGetBoundingClientRect = video.getBoundingClientRect;
|
||||||
|
video.getBoundingClientRect = function() {
|
||||||
|
if (video._isCroppedByPlugin && video.parentElement) {
|
||||||
|
const rect = video.parentElement.getBoundingClientRect();
|
||||||
|
return {
|
||||||
|
top: rect.top,
|
||||||
|
right: rect.right,
|
||||||
|
bottom: rect.bottom,
|
||||||
|
left: rect.left,
|
||||||
|
width: rect.width,
|
||||||
|
height: rect.height,
|
||||||
|
x: rect.x !== undefined ? rect.x : rect.left,
|
||||||
|
y: rect.y !== undefined ? rect.y : rect.top,
|
||||||
|
toJSON: function() { return this; }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return video._origGetBoundingClientRect.apply(this, arguments);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let lastBottomPercent = 0;
|
||||||
|
|
||||||
|
function updateSubtitleStyles(bottomPercent) {
|
||||||
|
lastBottomPercent = bottomPercent;
|
||||||
|
let styleEl = document.getElementById('black-bar-remover-sub-style');
|
||||||
|
if (!styleEl) {
|
||||||
|
styleEl = document.createElement('style');
|
||||||
|
styleEl.id = 'black-bar-remover-sub-style';
|
||||||
|
document.head.appendChild(styleEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bottomPercent > 0) {
|
||||||
|
const offsetVh = (bottomPercent * 0.4 + 2.5).toFixed(2);
|
||||||
|
styleEl.textContent = `
|
||||||
|
.videoSubtitles {
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
justify-content: flex-end !important;
|
||||||
|
transform: translateY(-${offsetVh}vh) !important;
|
||||||
|
}
|
||||||
|
.track-text-output,
|
||||||
|
.custom-subtitle-overlay,
|
||||||
|
.subtitleSyncContainer,
|
||||||
|
.videoSubtitlesContainer,
|
||||||
|
.libassjs-canvas-parent,
|
||||||
|
canvas.libassjs-canvas,
|
||||||
|
.subtitles-octopus-canvas,
|
||||||
|
.htmlvideoplayer::-webkit-media-text-track-container,
|
||||||
|
.htmlvideoplayer::-webkit-media-text-track-display {
|
||||||
|
transform: translateY(-${offsetVh}vh) !important;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
console.log('Black Bar Remover: Subtitle CSS style updated translateY(-' + offsetVh + 'vh)');
|
||||||
|
} else {
|
||||||
|
styleEl.textContent = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function adjustVttCues(bottomPercent) {
|
||||||
|
const video = document.querySelector('video');
|
||||||
|
if (!video || !video.textTracks) return;
|
||||||
|
const targetLine = Math.max(10, 92 - Math.round(bottomPercent * 0.4 + 2.5));
|
||||||
|
for (let i = 0; i < video.textTracks.length; i++) {
|
||||||
|
const track = video.textTracks[i];
|
||||||
|
if (track && track.cues) {
|
||||||
|
for (let j = 0; j < track.cues.length; j++) {
|
||||||
|
const cue = track.cues[j];
|
||||||
|
if (cue && !cue._pluginAdjusted) {
|
||||||
|
cue.snapToLines = false;
|
||||||
|
cue.line = targetLine;
|
||||||
|
cue._pluginAdjusted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
const video = document.querySelector('video');
|
|
||||||
if (video && video.src) {
|
|
||||||
const src = video.src.toLowerCase();
|
|
||||||
if (src.includes('transcode') || src.includes('.m3u8') || src.includes('transcodereasons')) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyCrop(cropData) {
|
function applyCrop(cropData) {
|
||||||
@@ -104,9 +169,16 @@ namespace JellyfinPlugin.Api
|
|||||||
if (!video) return;
|
if (!video) return;
|
||||||
|
|
||||||
if (isTranscoding()) {
|
if (isTranscoding()) {
|
||||||
console.log('Black Bar Remover: Video is transcoded by server (PlayMethod:', window._currentPlayMethod, '), skipping CSS crop.');
|
console.log('Black Bar Remover: Video is transcoded by server (IsVideoDirect: false), skipping CSS crop.');
|
||||||
|
video.style.position = '';
|
||||||
|
video.style.objectFit = '';
|
||||||
|
video.style.width = '';
|
||||||
|
video.style.height = '';
|
||||||
|
video.style.top = '';
|
||||||
|
video.style.left = '';
|
||||||
video.style.clipPath = '';
|
video.style.clipPath = '';
|
||||||
video.style.transform = '';
|
video.style.transform = '';
|
||||||
|
updateSubtitleStyles(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,21 +189,28 @@ namespace JellyfinPlugin.Api
|
|||||||
const x = parseInt(parts[2]);
|
const x = parseInt(parts[2]);
|
||||||
const y = parseInt(parts[3]);
|
const y = parseInt(parts[3]);
|
||||||
|
|
||||||
const topPercent = (y / 1080) * 100;
|
if (h > 0 && h < 1080) {
|
||||||
const bottomPercent = ((1080 - (y + h)) / 1080) * 100;
|
const heightPercent = (1080 / h) * 100;
|
||||||
const leftPercent = (x / 1920) * 100;
|
const topPercent = -(y / h) * 100;
|
||||||
const rightPercent = ((1920 - (x + w)) / 1920) * 100;
|
const bottomPercent = ((1080 - (y + h)) / 1080) * 100;
|
||||||
|
|
||||||
video.style.clipPath = `inset(${topPercent}% ${rightPercent}% ${bottomPercent}% ${leftPercent}%)`;
|
video.style.position = 'absolute';
|
||||||
|
video.style.objectFit = 'fill';
|
||||||
|
video.style.width = '100%';
|
||||||
|
video.style.height = heightPercent.toFixed(2) + '%';
|
||||||
|
video.style.top = topPercent.toFixed(2) + '%';
|
||||||
|
video.style.left = '0%';
|
||||||
|
video.style.transform = 'none';
|
||||||
|
video.style.clipPath = 'none';
|
||||||
|
|
||||||
const scaleY = 1 / (1 - (topPercent + bottomPercent)/100);
|
if (video.parentElement) {
|
||||||
const scaleX = 1 / (1 - (leftPercent + rightPercent)/100);
|
video.parentElement.style.overflow = 'hidden';
|
||||||
const scale = Math.max(scaleY, scaleX);
|
}
|
||||||
|
|
||||||
if (scale > 1.01) {
|
updateSubtitleStyles(bottomPercent);
|
||||||
video.style.transform = `scale(${scale})`;
|
adjustVttCues(bottomPercent);
|
||||||
|
console.log('Black Bar Remover: Applied non-scaling crop height:', heightPercent.toFixed(2) + '% top:', topPercent.toFixed(2) + '% subOffset:', (bottomPercent * 0.4 + 2.5).toFixed(2) + 'vh');
|
||||||
}
|
}
|
||||||
console.log('Black Bar Remover: Applied crop', cropData, 'scale', scale);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,6 +240,7 @@ namespace JellyfinPlugin.Api
|
|||||||
if (id && id !== interceptedItemId) {
|
if (id && id !== interceptedItemId) {
|
||||||
interceptedItemId = id;
|
interceptedItemId = id;
|
||||||
window._currentPlayMethod = null;
|
window._currentPlayMethod = null;
|
||||||
|
window._isVideoDirect = null;
|
||||||
console.log('Black Bar Remover: Intercepted itemId from fetch:', id);
|
console.log('Black Bar Remover: Intercepted itemId from fetch:', id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,8 +254,9 @@ namespace JellyfinPlugin.Api
|
|||||||
clone.json().then(data => {
|
clone.json().then(data => {
|
||||||
if (data && data.MediaSources && data.MediaSources[0]) {
|
if (data && data.MediaSources && data.MediaSources[0]) {
|
||||||
const ms = data.MediaSources[0];
|
const ms = data.MediaSources[0];
|
||||||
window._currentPlayMethod = ms.PlayMethod || (ms.TranscodingUrl ? 'Transcode' : 'DirectPlay');
|
window._currentPlayMethod = ms.PlayMethod;
|
||||||
console.log('Black Bar Remover: PlayMethod from fetch PlaybackInfo:', window._currentPlayMethod);
|
window._isVideoDirect = ms.IsVideoDirect !== false;
|
||||||
|
console.log('Black Bar Remover: PlaybackInfo detected -> PlayMethod:', window._currentPlayMethod, 'IsVideoDirect:', window._isVideoDirect);
|
||||||
if (currentCropData) {
|
if (currentCropData) {
|
||||||
applyCrop(currentCropData);
|
applyCrop(currentCropData);
|
||||||
}
|
}
|
||||||
@@ -201,6 +282,7 @@ namespace JellyfinPlugin.Api
|
|||||||
if (id && id !== interceptedItemId) {
|
if (id && id !== interceptedItemId) {
|
||||||
interceptedItemId = id;
|
interceptedItemId = id;
|
||||||
window._currentPlayMethod = null;
|
window._currentPlayMethod = null;
|
||||||
|
window._isVideoDirect = null;
|
||||||
console.log('Black Bar Remover: Intercepted itemId from XHR:', id);
|
console.log('Black Bar Remover: Intercepted itemId from XHR:', id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,8 +292,9 @@ namespace JellyfinPlugin.Api
|
|||||||
const data = JSON.parse(xhr.responseText);
|
const data = JSON.parse(xhr.responseText);
|
||||||
if (data && data.MediaSources && data.MediaSources[0]) {
|
if (data && data.MediaSources && data.MediaSources[0]) {
|
||||||
const ms = data.MediaSources[0];
|
const ms = data.MediaSources[0];
|
||||||
window._currentPlayMethod = ms.PlayMethod || (ms.TranscodingUrl ? 'Transcode' : 'DirectPlay');
|
window._currentPlayMethod = ms.PlayMethod;
|
||||||
console.log('Black Bar Remover: PlayMethod from XHR PlaybackInfo:', window._currentPlayMethod);
|
window._isVideoDirect = ms.IsVideoDirect !== false;
|
||||||
|
console.log('Black Bar Remover: XHR PlaybackInfo detected -> PlayMethod:', window._currentPlayMethod, 'IsVideoDirect:', window._isVideoDirect);
|
||||||
if (currentCropData) {
|
if (currentCropData) {
|
||||||
applyCrop(currentCropData);
|
applyCrop(currentCropData);
|
||||||
}
|
}
|
||||||
@@ -299,8 +382,12 @@ namespace JellyfinPlugin.Api
|
|||||||
console.log('Black Bar Remover: Video element found for intercepted item:', currentItemId);
|
console.log('Black Bar Remover: Video element found for intercepted item:', currentItemId);
|
||||||
fetchCrop(currentItemId);
|
fetchCrop(currentItemId);
|
||||||
checkServerCrop(currentItemId);
|
checkServerCrop(currentItemId);
|
||||||
|
} else if (video && lastBottomPercent > 0) {
|
||||||
|
updateSubtitleStyles(lastBottomPercent);
|
||||||
|
adjustVttCues(lastBottomPercent);
|
||||||
} else if (!video) {
|
} else if (!video) {
|
||||||
currentItemId = null;
|
currentItemId = null;
|
||||||
|
lastBottomPercent = 0;
|
||||||
window._isServerCropping = false;
|
window._isServerCropping = false;
|
||||||
}
|
}
|
||||||
}, 2000);
|
}, 2000);
|
||||||
|
|||||||
Reference in New Issue
Block a user