mirror of
https://github.com/Shik3i/KoalaSync.git
synced 2026-08-19 15:46:15 +00:00
263 lines
10 KiB
JavaScript
263 lines
10 KiB
JavaScript
const GOOGLE_DRIVE_HOST = 'drive.google.com';
|
|
const GOOGLE_DRIVE_PLAYER_HOST = 'youtube.googleapis.com';
|
|
export const GOOGLE_DRIVE_PLAYER_ORIGIN_PATTERN = 'https://youtube.googleapis.com/*';
|
|
export const GOOGLE_DRIVE_PLAYER_ACCESS_REQUIRED = 'google_drive_player_access_required';
|
|
export const GOOGLE_DRIVE_PLAYER_AMBIGUOUS = 'google_drive_player_ambiguous';
|
|
|
|
export function isGoogleDriveUrl(value) {
|
|
try {
|
|
return new URL(value).hostname.toLowerCase() === GOOGLE_DRIVE_HOST;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export function inspectMediaFrame() {
|
|
const videos = Array.from(document.querySelectorAll('video'));
|
|
const drivePlayerIframes = Array.from(document.querySelectorAll('iframe')).filter((frame) => {
|
|
try {
|
|
const url = new URL(frame.src);
|
|
if (url.hostname.toLowerCase() !== 'youtube.googleapis.com') return false;
|
|
const origin = url.searchParams.get('origin') || url.searchParams.get('post_message_origin');
|
|
return origin === 'https://drive.google.com';
|
|
} catch {
|
|
return false;
|
|
}
|
|
});
|
|
let bestVideoScore = 0;
|
|
|
|
for (const video of videos) {
|
|
const width = video.videoWidth || video.offsetWidth || 0;
|
|
const height = video.videoHeight || video.offsetHeight || 0;
|
|
const duration = Number.isFinite(video.duration) ? video.duration : 0;
|
|
bestVideoScore = Math.max(bestVideoScore, (width * height) + (duration * 100));
|
|
}
|
|
|
|
return {
|
|
href: window.location.href,
|
|
videoCount: videos.length,
|
|
videoScore: bestVideoScore,
|
|
frameArea: Math.max(0, window.innerWidth) * Math.max(0, window.innerHeight),
|
|
drivePlayerIframeCount: drivePlayerIframes.length,
|
|
parentFrameVisible: window.__koalaParentFrameVisibility?.visible ?? null,
|
|
parentFrameArea: window.__koalaParentFrameVisibility?.area ?? null
|
|
};
|
|
}
|
|
|
|
export function installParentFrameVisibilityProbe(token) {
|
|
const handler = (event) => {
|
|
if (event.source !== window.parent
|
|
|| event.data?.type !== 'KOALASYNC_FRAME_VISIBILITY'
|
|
|| event.data?.token !== token) {
|
|
return;
|
|
}
|
|
window.__koalaParentFrameVisibility = {
|
|
visible: event.data.visible === true,
|
|
area: Number.isFinite(event.data.area) ? event.data.area : 0
|
|
};
|
|
window.removeEventListener('message', handler);
|
|
};
|
|
window.addEventListener('message', handler);
|
|
}
|
|
|
|
export async function dispatchParentFrameVisibilityProbe(token) {
|
|
let count = 0;
|
|
for (const frame of document.querySelectorAll('iframe')) {
|
|
try {
|
|
const url = new URL(frame.src);
|
|
const origin = url.searchParams.get('origin') || url.searchParams.get('post_message_origin');
|
|
if (url.hostname.toLowerCase() !== 'youtube.googleapis.com'
|
|
|| origin !== 'https://drive.google.com') {
|
|
continue;
|
|
}
|
|
const rect = frame.getBoundingClientRect();
|
|
const style = window.getComputedStyle(frame);
|
|
const area = Math.max(0, rect.width) * Math.max(0, rect.height);
|
|
const intersectsViewport = rect.bottom > 0
|
|
&& rect.right > 0
|
|
&& rect.top < window.innerHeight
|
|
&& rect.left < window.innerWidth;
|
|
const browserReportsVisible = typeof frame.checkVisibility === 'function'
|
|
? frame.checkVisibility({ checkOpacity: true, checkVisibilityCSS: true })
|
|
: true;
|
|
const visible = area > 0
|
|
&& intersectsViewport
|
|
&& browserReportsVisible
|
|
&& style.display !== 'none'
|
|
&& style.visibility !== 'hidden'
|
|
&& Number(style.opacity) !== 0;
|
|
frame.contentWindow?.postMessage({
|
|
type: 'KOALASYNC_FRAME_VISIBILITY',
|
|
token,
|
|
visible,
|
|
area
|
|
}, 'https://youtube.googleapis.com');
|
|
count++;
|
|
} catch {
|
|
// Ignore unrelated or not-yet-initialized frames.
|
|
}
|
|
}
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
return count;
|
|
}
|
|
|
|
function isGoogleDrivePlayerFrameUrl(value) {
|
|
try {
|
|
const url = new URL(value);
|
|
if (url.hostname.toLowerCase() !== GOOGLE_DRIVE_PLAYER_HOST || url.pathname !== '/embed/') {
|
|
return false;
|
|
}
|
|
const origin = url.searchParams.get('origin') || url.searchParams.get('post_message_origin');
|
|
return origin === 'https://drive.google.com';
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function createGoogleDrivePlayerAccessError() {
|
|
const error = new Error('Google Drive player frame access is required');
|
|
error.code = GOOGLE_DRIVE_PLAYER_ACCESS_REQUIRED;
|
|
error.originPattern = GOOGLE_DRIVE_PLAYER_ORIGIN_PATTERN;
|
|
return error;
|
|
}
|
|
|
|
function createGoogleDrivePlayerAmbiguousError() {
|
|
const error = new Error('Google Drive player frame could not be identified safely');
|
|
error.code = GOOGLE_DRIVE_PLAYER_AMBIGUOUS;
|
|
return error;
|
|
}
|
|
|
|
export function selectGoogleDrivePlayerFrame(injectionResults) {
|
|
let candidates = (Array.isArray(injectionResults) ? injectionResults : [])
|
|
.filter(entry => Number.isInteger(entry?.frameId) && entry?.result)
|
|
.filter(entry => isGoogleDrivePlayerFrameUrl(entry.result.href));
|
|
|
|
if (candidates.length > 1) {
|
|
const confirmedVisible = candidates.filter(entry => entry.result.parentFrameVisible === true);
|
|
if (confirmedVisible.length > 0) {
|
|
candidates = confirmedVisible;
|
|
} else {
|
|
const notConfirmedHidden = candidates.filter(entry =>
|
|
entry.result.parentFrameVisible !== false
|
|
);
|
|
if (notConfirmedHidden.length !== 1) return null;
|
|
candidates = notConfirmedHidden;
|
|
}
|
|
}
|
|
|
|
candidates.sort((left, right) => {
|
|
const visibilityRank = result => result.parentFrameVisible === true
|
|
? 2
|
|
: result.parentFrameVisible === false
|
|
? 0
|
|
: 1;
|
|
const leftVisible = visibilityRank(left.result);
|
|
const rightVisible = visibilityRank(right.result);
|
|
if (leftVisible !== rightVisible) return rightVisible - leftVisible;
|
|
const leftParentArea = Number.isFinite(left.result.parentFrameArea)
|
|
? left.result.parentFrameArea
|
|
: left.result.frameArea;
|
|
const rightParentArea = Number.isFinite(right.result.parentFrameArea)
|
|
? right.result.parentFrameArea
|
|
: right.result.frameArea;
|
|
if (leftParentArea !== rightParentArea) return rightParentArea - leftParentArea;
|
|
const leftHasVideo = left.result.videoCount > 0 ? 1 : 0;
|
|
const rightHasVideo = right.result.videoCount > 0 ? 1 : 0;
|
|
if (leftHasVideo !== rightHasVideo) return rightHasVideo - leftHasVideo;
|
|
if (left.result.videoScore !== right.result.videoScore) {
|
|
return right.result.videoScore - left.result.videoScore;
|
|
}
|
|
return right.result.frameArea - left.result.frameArea;
|
|
});
|
|
|
|
return candidates[0] || null;
|
|
}
|
|
|
|
export async function resolveMediaScriptTarget(chromeApi, tabId, tabUrl, {
|
|
attempts = 8,
|
|
retryDelayMs = 200
|
|
} = {}) {
|
|
const topFrameTarget = { tabId };
|
|
if (!isGoogleDriveUrl(tabUrl)) return topFrameTarget;
|
|
|
|
let fallbackFrame = null;
|
|
let drivePlayerIframeDetected = false;
|
|
let ambiguousPlayerFramesDetected = false;
|
|
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
let results;
|
|
try {
|
|
results = await chromeApi.scripting.executeScript({
|
|
target: { tabId, allFrames: true },
|
|
func: inspectMediaFrame
|
|
});
|
|
} catch {
|
|
let topFrameResult = null;
|
|
try {
|
|
const topResults = await chromeApi.scripting.executeScript({
|
|
target: { tabId },
|
|
func: inspectMediaFrame
|
|
});
|
|
topFrameResult = Array.isArray(topResults) ? topResults[0]?.result : null;
|
|
} catch {
|
|
// The generic injection path below remains the final source of truth.
|
|
}
|
|
if (topFrameResult?.drivePlayerIframeCount > 0) {
|
|
throw createGoogleDrivePlayerAccessError();
|
|
}
|
|
return topFrameTarget;
|
|
}
|
|
|
|
const topFrame = results.find(entry => entry?.frameId === 0);
|
|
if (topFrame?.result?.drivePlayerIframeCount > 0) {
|
|
drivePlayerIframeDetected = true;
|
|
}
|
|
const playerFrameCount = results.filter(entry =>
|
|
isGoogleDrivePlayerFrameUrl(entry?.result?.href)
|
|
).length;
|
|
if (playerFrameCount > 1) {
|
|
const token = `${tabId}:${attempt}:${Date.now()}:${Math.random()}`;
|
|
try {
|
|
await chromeApi.scripting.executeScript({
|
|
target: { tabId, allFrames: true },
|
|
func: installParentFrameVisibilityProbe,
|
|
args: [token]
|
|
});
|
|
await chromeApi.scripting.executeScript({
|
|
target: { tabId },
|
|
func: dispatchParentFrameVisibilityProbe,
|
|
args: [token]
|
|
});
|
|
results = await chromeApi.scripting.executeScript({
|
|
target: { tabId, allFrames: true },
|
|
func: inspectMediaFrame
|
|
});
|
|
} catch {
|
|
// Ambiguous frames are retried below; never guess a stale player.
|
|
}
|
|
}
|
|
const selectedFrame = selectGoogleDrivePlayerFrame(results);
|
|
if (!selectedFrame && playerFrameCount > 1) {
|
|
ambiguousPlayerFramesDetected = true;
|
|
}
|
|
if (selectedFrame?.result?.videoCount > 0) {
|
|
return { tabId, frameIds: [selectedFrame.frameId] };
|
|
}
|
|
if (selectedFrame) fallbackFrame = selectedFrame;
|
|
|
|
if (attempt < attempts - 1) {
|
|
await new Promise(resolve => setTimeout(resolve, retryDelayMs));
|
|
}
|
|
}
|
|
|
|
if (!fallbackFrame && ambiguousPlayerFramesDetected) {
|
|
throw createGoogleDrivePlayerAmbiguousError();
|
|
}
|
|
if (!fallbackFrame && drivePlayerIframeDetected) {
|
|
throw createGoogleDrivePlayerAccessError();
|
|
}
|
|
|
|
return fallbackFrame
|
|
? { tabId, frameIds: [fallbackFrame.frameId] }
|
|
: topFrameTarget;
|
|
}
|