Collapsible sidebar that centralizes all detected video/iframe links.
// ==UserScript==
// @name EmbedCast Link Extractor (Sidebar Edition)
// @namespace https://github.com/MoriNo23/embedCast
// @version 2.0
// @description Collapsible sidebar that centralizes all detected video/iframe links.
// @author mori
// @match *://*/*
// @grant none
// @license MIT
// @run-at document-end
// @icon https://raw.githubusercontent.com/MoriNo23/embedCast/main/assets/logo.png
// ==/UserScript==
(function() {
'use strict';
// --- COMMUNICATION SETUP ---
const MESSAGE_TYPE = 'EMBEDCAST_FOUND_URL';
// --- LOGIC FOR IFRAMES (CHILDREN) ---
if (window.self !== window.top) {
const notifyTop = (url) => {
if (url && url.startsWith('http') && !url.includes('about:blank')) {
// Send the URL to the main page
window.top.postMessage({ type: MESSAGE_TYPE, url: url }, '*');
}
};
// Notify the current iframe URL
notifyTop(window.location.href);
// Look for lazyload elements inside the iframe
const checkLazyInIframe = () => {
const lazy = document.querySelector('iframe, .lazyload, .lazyloade, [data-src]');
if (lazy) {
const url = lazy.src || lazy.getAttribute('data-src') || lazy.getAttribute('data-lazy-src');
notifyTop(url);
}
};
setInterval(checkLazyInIframe, 3000);
return; // Stop execution here for iframes
}
// --- LOGIC FOR MAIN WINDOW (PARENT) ---
const capturedUrls = new Set();
let sidebar, listContainer;
const createSidebar = () => {
if (document.getElementById('embedcast-sidebar')) return;
// Create the sidebar container
sidebar = document.createElement('div');
sidebar.id = 'embedcast-sidebar';
Object.assign(sidebar.style, {
position: 'fixed',
right: '-260px', // Hidden by default
top: '0',
width: '260px',
height: '100vh',
background: '#1a1a2e',
borderLeft: '2px solid #00ffcc',
zIndex: '2147483647', // Always on top
transition: 'right 0.3s ease',
display: 'flex',
flexDirection: 'column',
boxShadow: '-5px 0 15px rgba(0,0,0,0.5)',
color: '#fff',
fontFamily: 'sans-serif'
});
sidebar.innerHTML = `
<div style="padding: 15px; background: #16213e; border-bottom: 1px solid #00ffcc; display: flex; align-items: center; justify-content: space-between;">
<div style="display:flex; align-items:center;">
<img src="https://raw.githubusercontent.com/MoriNo23/embedCast/main/assets/logo.png" style="width:20px; margin-right:8px;">
<span style="color:#00ffcc; font-weight:bold; font-size:14px;">EmbedCast</span>
</div>
<button id="ec-close" style="background:none; border:none; color:#00ffcc; cursor:pointer; font-size:18px;">×</button>
</div>
<div id="ec-list" style="flex:1; overflow-y:auto; padding:10px;">
<p id="ec-empty" style="color:#666; font-size:12px; text-align:center; margin-top:20px;">Searching links...</p>
</div>
<div id="ec-toggle" style="position:absolute; left:-40px; top:50%; width:40px; height:60px; background:#1a1a2e; border:2px solid #00ffcc; border-right:none; border-radius:10px 0 0 10px; cursor:pointer; display:flex; align-items:center; justify-content:center; color:#00ffcc; font-weight:bold;">
<img src="https://raw.githubusercontent.com/MoriNo23/embedCast/main/assets/logo.png" style="width:25px;">
</div>
`;
document.body.appendChild(sidebar);
listContainer = document.getElementById('ec-list');
// UI Events
const toggleBtn = document.getElementById('ec-toggle');
const closeBtn = document.getElementById('ec-close');
const toggleSidebar = () => {
const isOpen = sidebar.style.right === '0px';
sidebar.style.right = isOpen ? '-260px' : '0px';
};
toggleBtn.onclick = toggleSidebar;
closeBtn.onclick = toggleSidebar;
};
const addUrlToList = (url) => {
// Avoid duplicate links
if (capturedUrls.has(url)) return;
capturedUrls.add(url);
const emptyMsg = document.getElementById('ec-empty');
if (emptyMsg) emptyMsg.remove();
// Create link item in the list
const item = document.createElement('div');
Object.assign(item.style, {
background: '#16213e',
padding: '10px',
marginBottom: '8px',
borderRadius: '5px',
border: '1px solid #30475e',
fontSize: '11px'
});
item.innerHTML = `
<div style="color:#00ffcc; margin-bottom:5px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;" title="${url}">
${url}
</div>
<button class="ec-copy-btn" style="width:100%; background:#0f3460; color:#00ffcc; border:1px solid #00ffcc; padding:5px; cursor:pointer; border-radius:3px; font-weight:bold;">
COPY
</button>
`;
item.querySelector('.ec-copy-btn').onclick = function() {
navigator.clipboard.writeText(url);
this.innerText = 'COPIED!';
setTimeout(() => this.innerText = 'COPY', 2000);
};
listContainer.appendChild(item);
// Auto-open sidebar when the first link is found
if (capturedUrls.size === 1) {
sidebar.style.right = '0px';
}
};
// Listen for messages from iframes
window.addEventListener('message', (event) => {
if (event.data && event.data.type === MESSAGE_TYPE) {
addUrlToList(event.data.url);
}
});
// Initialize sidebar
createSidebar();
// Also search in the main page for links
setInterval(() => {
const potential = document.querySelectorAll('iframe, .lazyload, .lazyloade, [data-src]');
potential.forEach(el => {
const url = el.src || el.getAttribute('data-src') || el.getAttribute('data-lazy-src');
if (url && url.startsWith('http')) addUrlToList(url);
});
}, 3000);
})();