mirror of
https://github.com/Shik3i/KoalaSync.git
synced 2026-07-26 20:18:14 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0f611fa112 | |||
| 67f872ffd2 | |||
| e40e2e5101 |
+28
-2
@@ -191,6 +191,22 @@
|
||||
if (message.type === 'GET_VIDEO_STATE') {
|
||||
const video = findVideo();
|
||||
if (video) {
|
||||
const dataAttributes = {};
|
||||
if (video.attributes) {
|
||||
for (const attr of video.attributes) {
|
||||
if (attr.name.startsWith('data-')) {
|
||||
dataAttributes[attr.name] = attr.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const metadata = (navigator.mediaSession && navigator.mediaSession.metadata) ? {
|
||||
title: navigator.mediaSession.metadata.title,
|
||||
artist: navigator.mediaSession.metadata.artist,
|
||||
album: navigator.mediaSession.metadata.album,
|
||||
artwork: Array.from(navigator.mediaSession.metadata.artwork || []).map(a => a.src)
|
||||
} : null;
|
||||
|
||||
sendResponse({
|
||||
paused: video.paused,
|
||||
currentTime: video.currentTime,
|
||||
@@ -199,7 +215,12 @@
|
||||
muted: video.muted,
|
||||
playbackRate: video.playbackRate,
|
||||
url: window.location.href,
|
||||
id: video.id || 'none'
|
||||
id: video.id || 'none',
|
||||
className: video.className || 'none',
|
||||
src: video.src || 'none',
|
||||
currentSrc: video.currentSrc || 'none',
|
||||
dataAttributes,
|
||||
metadata
|
||||
});
|
||||
} else {
|
||||
sendResponse({ error: 'No video found' });
|
||||
@@ -215,6 +236,8 @@
|
||||
const current = video.currentTime;
|
||||
if (!Number.isFinite(current)) return;
|
||||
|
||||
const mediaTitle = (navigator.mediaSession && navigator.mediaSession.metadata) ? navigator.mediaSession.metadata.title : null;
|
||||
|
||||
const eventState = action === EVENTS.PLAY ? 'playing' : (action === EVENTS.PAUSE ? 'paused' : (action === EVENTS.SEEK ? 'seek' : null));
|
||||
|
||||
if (eventState && lastTargetState === eventState) {
|
||||
@@ -228,6 +251,7 @@
|
||||
payload: {
|
||||
currentTime: current,
|
||||
targetTime: current,
|
||||
mediaTitle: mediaTitle,
|
||||
timestamp: Date.now()
|
||||
}
|
||||
});
|
||||
@@ -286,11 +310,13 @@
|
||||
const heartbeatInterval = setInterval(() => {
|
||||
const video = findVideo();
|
||||
if (video) {
|
||||
const mediaTitle = (navigator.mediaSession && navigator.mediaSession.metadata) ? navigator.mediaSession.metadata.title : null;
|
||||
chrome.runtime.sendMessage({
|
||||
type: 'HEARTBEAT',
|
||||
payload: {
|
||||
playbackState: video.paused ? 'paused' : 'playing',
|
||||
currentTime: video.currentTime
|
||||
currentTime: video.currentTime,
|
||||
mediaTitle: mediaTitle
|
||||
}
|
||||
}).catch(err => {
|
||||
if (err.message.includes('Extension context invalidated')) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "KoalaSync",
|
||||
"version": "1.0.4",
|
||||
"version": "1.1.0",
|
||||
"description": "Synchronize video playback across different tabs and users.",
|
||||
"permissions": [
|
||||
"storage",
|
||||
|
||||
@@ -328,7 +328,7 @@
|
||||
</div>
|
||||
|
||||
<label>Video Debug Info</label>
|
||||
<div id="videoDebug" class="info-card" style="font-size: 10px; font-family: monospace; color: var(--text-muted); min-height: 60px; line-height: 1.4;">
|
||||
<div id="videoDebug" class="info-card" style="font-size: 10px; font-family: monospace; color: var(--text-muted); max-height: 250px; overflow-y: auto; line-height: 1.4;">
|
||||
No tab selected or video detected.
|
||||
</div>
|
||||
|
||||
|
||||
+58
-25
@@ -241,10 +241,17 @@ function updatePeerList(peers) {
|
||||
|
||||
peerItem.appendChild(header);
|
||||
|
||||
if (p.mediaTitle) {
|
||||
const mediaDiv = document.createElement('div');
|
||||
mediaDiv.style.cssText = 'font-size:11px; color:var(--star); font-weight: 600; margin-top: 2px;';
|
||||
mediaDiv.textContent = `🎬 ${p.mediaTitle}`;
|
||||
peerItem.appendChild(mediaDiv);
|
||||
}
|
||||
|
||||
if (pTabTitle) {
|
||||
const titleDiv = document.createElement('div');
|
||||
titleDiv.style.cssText = 'font-size:10px; color:var(--text-muted);';
|
||||
titleDiv.textContent = pTabTitle;
|
||||
titleDiv.style.cssText = 'font-size:10px; color:var(--text-muted); opacity: 0.8;';
|
||||
titleDiv.textContent = p.mediaTitle ? `via ${pTabTitle}` : pTabTitle;
|
||||
peerItem.appendChild(titleDiv);
|
||||
}
|
||||
|
||||
@@ -806,31 +813,57 @@ function refreshDebugInfo() {
|
||||
if (elements.videoDebug) {
|
||||
elements.videoDebug.innerHTML = '';
|
||||
|
||||
const status = document.createElement('div');
|
||||
status.style.cssText = 'color:var(--accent); margin-bottom:4px;';
|
||||
status.textContent = `VIDEO STATE: ${state.paused ? 'PAUSED' : 'PLAYING'}`;
|
||||
const addField = (label, value, color = null) => {
|
||||
const row = document.createElement('div');
|
||||
row.style.marginBottom = '4px';
|
||||
if (color) row.style.color = color;
|
||||
|
||||
const b = document.createElement('b');
|
||||
b.textContent = `${label}: `;
|
||||
b.style.color = 'var(--text-muted)';
|
||||
|
||||
const span = document.createElement('span');
|
||||
span.textContent = value;
|
||||
span.style.wordBreak = 'break-all';
|
||||
|
||||
row.appendChild(b);
|
||||
row.appendChild(span);
|
||||
elements.videoDebug.appendChild(row);
|
||||
};
|
||||
|
||||
const addSection = (title) => {
|
||||
const div = document.createElement('div');
|
||||
div.style.cssText = 'margin: 8px 0 4px 0; border-bottom: 1px solid #334155; padding-bottom: 2px; color: var(--accent); font-weight: bold; font-size: 9px;';
|
||||
div.textContent = title.toUpperCase();
|
||||
elements.videoDebug.appendChild(div);
|
||||
};
|
||||
|
||||
addField('STATE', state.paused ? 'PAUSED' : 'PLAYING', 'var(--accent)');
|
||||
addField('TIME', `${state.currentTime.toFixed(2)}s / ${state.duration.toFixed(2)}s`);
|
||||
addField('READY', state.readyState);
|
||||
|
||||
const time = document.createElement('div');
|
||||
time.style.fontSize = '11px';
|
||||
time.textContent = `Time: ${state.currentTime.toFixed(2)}s / ${state.duration.toFixed(2)}s`;
|
||||
addSection('Identification');
|
||||
addField('URL', state.url);
|
||||
addField('ID', state.id);
|
||||
addField('CLASS', state.className);
|
||||
|
||||
const readyState = document.createElement('div');
|
||||
readyState.style.fontSize = '11px';
|
||||
readyState.textContent = `ReadyState: ${state.readyState}`;
|
||||
|
||||
const misc = document.createElement('div');
|
||||
misc.style.fontSize = '11px';
|
||||
misc.textContent = `Muted: ${state.muted} | PlaybackRate: ${state.playbackRate}`;
|
||||
|
||||
const url = document.createElement('div');
|
||||
url.style.cssText = 'font-size:9px; margin-top:4px; opacity:0.7;';
|
||||
url.textContent = `URL: ${state.url.substring(0, 40)}...`;
|
||||
|
||||
elements.videoDebug.appendChild(status);
|
||||
elements.videoDebug.appendChild(time);
|
||||
elements.videoDebug.appendChild(readyState);
|
||||
elements.videoDebug.appendChild(misc);
|
||||
elements.videoDebug.appendChild(url);
|
||||
addSection('Media Source');
|
||||
addField('CURRENT_SRC', state.currentSrc);
|
||||
addField('SRC', state.src);
|
||||
|
||||
if (state.metadata) {
|
||||
addSection('Media Session API');
|
||||
addField('TITLE', state.metadata.title || 'n/a');
|
||||
addField('ARTIST', state.metadata.artist || 'n/a');
|
||||
addField('ALBUM', state.metadata.album || 'n/a');
|
||||
}
|
||||
|
||||
if (state.dataAttributes && Object.keys(state.dataAttributes).length > 0) {
|
||||
addSection('Data Attributes');
|
||||
for (const [key, val] of Object.entries(state.dataAttributes)) {
|
||||
addField(key.replace('data-', '').toUpperCase(), val);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
+6
-4
@@ -155,7 +155,7 @@ io.on('connection', (socket) => {
|
||||
return;
|
||||
}
|
||||
if (!payload || typeof payload.roomId !== 'string') return;
|
||||
const { roomId, password, peerId, username, tabTitle, protocolVersion } = payload;
|
||||
const { roomId, password, peerId, username, tabTitle, mediaTitle, protocolVersion } = payload;
|
||||
try {
|
||||
// Protocol check
|
||||
if (protocolVersion !== '1.0.0') {
|
||||
@@ -244,12 +244,13 @@ io.on('connection', (socket) => {
|
||||
peerId,
|
||||
username: username || null,
|
||||
tabTitle: tabTitle || null,
|
||||
mediaTitle: mediaTitle || null,
|
||||
lastSeen: Date.now()
|
||||
});
|
||||
socketToRoom.set(socket.id, { roomId, peerId });
|
||||
peerToSocket.set(peerId, socket.id);
|
||||
|
||||
socket.to(roomId).emit(EVENTS.PEER_STATUS, { peerId, username: username || null, tabTitle: tabTitle || null, status: 'joined' });
|
||||
socket.to(roomId).emit(EVENTS.PEER_STATUS, { peerId, username: username || null, tabTitle: tabTitle || null, mediaTitle: mediaTitle || null, status: 'joined' });
|
||||
socket.emit(EVENTS.ROOM_DATA, {
|
||||
roomId,
|
||||
peers: Array.from(room.peers).map(sid => room.peerData.get(sid))
|
||||
@@ -288,8 +289,9 @@ io.on('connection', (socket) => {
|
||||
const existing = room.peerData.get(socket.id) || { peerId: mapping.peerId };
|
||||
room.peerData.set(socket.id, {
|
||||
...existing,
|
||||
username: data.username || existing.username,
|
||||
tabTitle: data.tabTitle || existing.tabTitle,
|
||||
username: data.username !== undefined ? data.username : existing.username,
|
||||
tabTitle: data.tabTitle !== undefined ? data.tabTitle : existing.tabTitle,
|
||||
mediaTitle: data.mediaTitle !== undefined ? data.mediaTitle : existing.mediaTitle,
|
||||
lastSeen: Date.now()
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user