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,41 +72,113 @@ namespace JellyfinPlugin.Api
|
||||
let interceptedItemId = null;
|
||||
let currentCropData = null;
|
||||
window._currentPlayMethod = null;
|
||||
window._isVideoDirect = null;
|
||||
|
||||
function isTranscoding() {
|
||||
if (window._currentPlayMethod === 'Transcode' || window._currentPlayMethod === 'DirectStream') {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
if (window.playbackManager) {
|
||||
const currentPlayer = window.playbackManager.getCurrentPlayer();
|
||||
if (currentPlayer && typeof currentPlayer.getPlayerState === 'function') {
|
||||
const state = currentPlayer.getPlayerState();
|
||||
if (state && state.PlayMethod) {
|
||||
return state.PlayMethod === 'Transcode' || state.PlayMethod === 'DirectStream';
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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')) {
|
||||
if (window._isVideoDirect === false) {
|
||||
return true;
|
||||
}
|
||||
if (window._isVideoDirect === true) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function applyCrop(cropData) {
|
||||
const video = document.querySelector('video');
|
||||
if (!video) return;
|
||||
|
||||
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.transform = '';
|
||||
updateSubtitleStyles(0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -117,21 +189,28 @@ namespace JellyfinPlugin.Api
|
||||
const x = parseInt(parts[2]);
|
||||
const y = parseInt(parts[3]);
|
||||
|
||||
const topPercent = (y / 1080) * 100;
|
||||
if (h > 0 && h < 1080) {
|
||||
const heightPercent = (1080 / h) * 100;
|
||||
const topPercent = -(y / h) * 100;
|
||||
const bottomPercent = ((1080 - (y + h)) / 1080) * 100;
|
||||
const leftPercent = (x / 1920) * 100;
|
||||
const rightPercent = ((1920 - (x + w)) / 1920) * 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);
|
||||
const scaleX = 1 / (1 - (leftPercent + rightPercent)/100);
|
||||
const scale = Math.max(scaleY, scaleX);
|
||||
|
||||
if (scale > 1.01) {
|
||||
video.style.transform = `scale(${scale})`;
|
||||
if (video.parentElement) {
|
||||
video.parentElement.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
updateSubtitleStyles(bottomPercent);
|
||||
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) {
|
||||
interceptedItemId = id;
|
||||
window._currentPlayMethod = null;
|
||||
window._isVideoDirect = null;
|
||||
console.log('Black Bar Remover: Intercepted itemId from fetch:', id);
|
||||
}
|
||||
}
|
||||
@@ -174,8 +254,9 @@ namespace JellyfinPlugin.Api
|
||||
clone.json().then(data => {
|
||||
if (data && data.MediaSources && data.MediaSources[0]) {
|
||||
const ms = data.MediaSources[0];
|
||||
window._currentPlayMethod = ms.PlayMethod || (ms.TranscodingUrl ? 'Transcode' : 'DirectPlay');
|
||||
console.log('Black Bar Remover: PlayMethod from fetch PlaybackInfo:', window._currentPlayMethod);
|
||||
window._currentPlayMethod = ms.PlayMethod;
|
||||
window._isVideoDirect = ms.IsVideoDirect !== false;
|
||||
console.log('Black Bar Remover: PlaybackInfo detected -> PlayMethod:', window._currentPlayMethod, 'IsVideoDirect:', window._isVideoDirect);
|
||||
if (currentCropData) {
|
||||
applyCrop(currentCropData);
|
||||
}
|
||||
@@ -201,6 +282,7 @@ namespace JellyfinPlugin.Api
|
||||
if (id && id !== interceptedItemId) {
|
||||
interceptedItemId = id;
|
||||
window._currentPlayMethod = null;
|
||||
window._isVideoDirect = null;
|
||||
console.log('Black Bar Remover: Intercepted itemId from XHR:', id);
|
||||
}
|
||||
}
|
||||
@@ -210,8 +292,9 @@ namespace JellyfinPlugin.Api
|
||||
const data = JSON.parse(xhr.responseText);
|
||||
if (data && data.MediaSources && data.MediaSources[0]) {
|
||||
const ms = data.MediaSources[0];
|
||||
window._currentPlayMethod = ms.PlayMethod || (ms.TranscodingUrl ? 'Transcode' : 'DirectPlay');
|
||||
console.log('Black Bar Remover: PlayMethod from XHR PlaybackInfo:', window._currentPlayMethod);
|
||||
window._currentPlayMethod = ms.PlayMethod;
|
||||
window._isVideoDirect = ms.IsVideoDirect !== false;
|
||||
console.log('Black Bar Remover: XHR PlaybackInfo detected -> PlayMethod:', window._currentPlayMethod, 'IsVideoDirect:', window._isVideoDirect);
|
||||
if (currentCropData) {
|
||||
applyCrop(currentCropData);
|
||||
}
|
||||
@@ -299,8 +382,12 @@ namespace JellyfinPlugin.Api
|
||||
console.log('Black Bar Remover: Video element found for intercepted item:', currentItemId);
|
||||
fetchCrop(currentItemId);
|
||||
checkServerCrop(currentItemId);
|
||||
} else if (video && lastBottomPercent > 0) {
|
||||
updateSubtitleStyles(lastBottomPercent);
|
||||
adjustVttCues(lastBottomPercent);
|
||||
} else if (!video) {
|
||||
currentItemId = null;
|
||||
lastBottomPercent = 0;
|
||||
window._isServerCropping = false;
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
Reference in New Issue
Block a user