// ==UserScript==
// @license MIT
// @name Switcher Stream Channel
// @namespace http://tampermonkey.net/
// @version 1.10.10
// @description Replace video feed with specified channel's video stream and provide draggable control panel functionality
// @match https://www.twitch.tv/*
// @icon https://github.com/sopernik566/icons/blob/main/switcher%20player%20icon.png?raw=true
// @author Gullampis810
// @grant none
// @run-at document-end
// ==/UserScript==
(function () {
'use strict';
let channelName = 'tapa_tapa_mateo';
let favoriteChannels = JSON.parse(localStorage.getItem('favoriteChannels')) || [];
let channelHistory = JSON.parse(localStorage.getItem('channelHistory')) || [];
let panelColor = localStorage.getItem('panelColor') || '#333';
let panelPosition = JSON.parse(localStorage.getItem('panelPosition')) || { top: '8px', left: '10px' };
// Initialize Panel UI
const panel = createPanel();
document.body.appendChild(panel);
// Set initial panel position
setPanelPosition(panel, panelPosition);
// Create and position toggle button
const toggleButton = createToggleButton();
document.body.appendChild(toggleButton);
// Enable drag functionality for the panel
enableDrag(panel);
// Dynamically reposition the toggle button on window resize
repositionToggleButton(toggleButton);
// Reposition toggle button on resize
window.addEventListener('resize', () => repositionToggleButton(toggleButton));
// Stream loader
window.addEventListener('load', loadStream);
/** Panel and UI Elements */
function createPanel() {
const panel = document.createElement('div');
Object.assign(panel.style, {
position: 'fixed', width: '322px', padding: '10px',
border: '2px solid #ffffff',
backgroundColor: panelColor, color: '#fff', borderRadius: '5px', zIndex: '9999',
display: 'flex', flexDirection: 'row-reverse', flexWrap: 'wrap', alignContent: 'stretch',
justifyContent: 'flex-end', alignItems: 'center', transition: 'all 0.3s ease', cursor: 'move'
});
panel.append(
createTitle("Switcher channel."),
createChannelInput(),
createButton('Play', loadInputChannel),
createSelect(favoriteChannels, 'Select from favorites'),
createSelect(channelHistory, 'Channel history'),
createButton('Play selected channel', loadSelectedChannel),
createButton('Add to Favorites', addChannelToFavorites),
createButton('Remove from Favorites', removeChannelFromFavorites),
createButton('Clear History', clearHistory),
createColorPicker()
);
return panel;
}
/** Toggle Button UI */
function createToggleButton() {
const toggleButton = document.createElement('button');
Object.assign(toggleButton.style, {
position: 'fixed', // Fixed position for the toggle button
top: '25%', right: '420px',
transform: 'translateY(-50%)', // Center vertically
backgroundColor: '#2b675f', color: '#fff', borderRadius: '10%',
width: '40px', height: '40px', cursor: 'pointer', zIndex: '10000',
display: 'flex', alignItems: 'center', justifyContent: 'center', border: 'none'
});
const img = document.createElement('img');
img.src = 'https://raw.githubusercontent.com/sopernik566/icons/4986e623628f56c95bd45004d6794820d874266d/eye_show.svg';
img.alt = 'Toggle visibility';
img.style.width = '25px';
img.style.height = '25px';
toggleButton.appendChild(img);
toggleButton.addEventListener('click', () => {
const isHidden = panel.style.display === 'none';
panel.style.display = isHidden ? 'block' : 'none';
img.src = isHidden
? 'https://raw.githubusercontent.com/sopernik566/icons/4986e623628f56c95bd45004d6794820d874266d/eye_show.svg'
: 'https://raw.githubusercontent.com/sopernik566/icons/4986e623628f56c95bd45004d6794820d874266d/eye_hidden_1024.svg';
});
toggleButton.addEventListener('mouseover', () => toggleButton.style.backgroundColor = '#3aa39b');
toggleButton.addEventListener('mouseout', () => toggleButton.style.backgroundColor = '#2b675f');
return toggleButton;
}
/** Panel Elements */
function createTitle(text) {
const title = document.createElement('h3');
title.innerText = text;
return title;
}
function createChannelInput() {
const input = document.createElement('input');
Object.assign(input.style, { width: '60%', marginRight: '5px' });
input.type = 'text';
input.placeholder = 'Type channel name';
input.addEventListener('input', (e) => { channelName = e.target.value.trim(); });
return input;
}
function createSelect(options, label) {
const select = document.createElement('select');
Object.assign(select.style, { width: '100%', marginBottom: '5px' });
updateOptions(select, options);
select.addEventListener('change', () => { channelName = select.value; });
return select;
}
function createButton(text, onClick) {
const button = document.createElement('button');
button.innerText = text;
Object.assign(button.style, {
width: '100%', margin: '5px 0', padding: '10px', borderRadius: '5px',
backgroundColor: '#2b675f', color: '#fff', border: 'none', cursor: 'pointer'
});
button.addEventListener('click', onClick);
button.addEventListener('mouseover', () => {
button.style.backgroundColor = '#6da7a8';
button.style.boxShadow = '0px 4px 6px rgba(14, 15, 15, 0.5)';
});
button.addEventListener('mouseout', () => {
button.style.backgroundColor = '#2b675f';
button.style.boxShadow = 'none';
});
return button;
}
function createColorPicker() {
const colorPicker = document.createElement('input');
colorPicker.type = 'color';
colorPicker.value = panelColor;
colorPicker.style.width = '100%';
colorPicker.addEventListener('input', (e) => {
panel.style.backgroundColor = e.target.value;
localStorage.setItem('panelColor', e.target.value);
});
return colorPicker;
}
/** Helper Functions */
function repositionToggleButton(button) {
const windowHeight = window.innerHeight;
const buttonHeight = button.offsetHeight;
// Ensure the button stays visible vertically
let topPosition = parseInt(button.style.top, 10);
if (isNaN(topPosition) || topPosition < 0) {
topPosition = 0;
} else if (topPosition + buttonHeight > windowHeight) {
topPosition = windowHeight - buttonHeight;
}
button.style.top = `${topPosition}px`;
}
function loadInputChannel() {
if (channelName) {
loadStream();
addChannelToHistory(channelName);
}
}
function loadSelectedChannel() {
loadStream();
addChannelToHistory(channelName);
}
function addChannelToFavorites() {
if (channelName && !favoriteChannels.includes(channelName)) {
favoriteChannels.push(channelName);
localStorage.setItem('favoriteChannels', JSON.stringify(favoriteChannels));
alert(`Added ${channelName} to favorites!`);
updateOptions(document.querySelectorAll('select')[0], favoriteChannels);
}
}
function removeChannelFromFavorites() {
favoriteChannels = favoriteChannels.filter(channel => channel !== channelName);
localStorage.setItem('favoriteChannels', JSON.stringify(favoriteChannels));
updateOptions(document.querySelectorAll('select')[0], favoriteChannels);
}
function clearHistory() {
channelHistory = [];
localStorage.setItem('channelHistory', JSON.stringify(channelHistory));
updateOptions(document.querySelectorAll('select')[1], channelHistory);
}
function loadStream() {
setTimeout(() => {
const player = document.querySelector('.video-player__container');
if (player) {
player.innerHTML = '';
const iframe = document.createElement('iframe');
iframe.src = `https://player.twitch.tv/?channel=${channelName}&parent=twitch.tv&quality=1080p&muted=false`;
iframe.width = '100%';
iframe.height = '100%';
iframe.allowFullscreen = true;
player.appendChild(iframe);
}
}, 2000);
}
function addChannelToHistory(channel) {
if (!channelHistory.includes(channel)) {
channelHistory.push(channel);
localStorage.setItem('channelHistory', JSON.stringify(channelHistory));
updateOptions(document.querySelectorAll('select')[1], channelHistory);
}
}
function updateOptions(select, options) {
select.innerHTML = '';
options.forEach(option => {
const opt = document.createElement('option');
opt.value = option;
opt.text = option;
select.appendChild(opt);
});
}
/** Draggable Panel */
function enableDrag(panel) {
let isDragging = false, offsetX, offsetY;
panel.addEventListener('mousedown', (e) => {
isDragging = true;
offsetX = e.clientX - panel.getBoundingClientRect().left;
offsetY = e.clientY - panel.getBoundingClientRect().top;
panel.style.transition = 'none';
});
document.addEventListener('mousemove', (e) => {
if (isDragging) {
const newLeft = e.clientX - offsetX;
const newTop = e.clientY - offsetY;
panel.style.left = `${newLeft}px`;
panel.style.top = `${newTop}px`;
// Save position in localStorage
localStorage.setItem('panelPosition', JSON.stringify({ top: `${newTop}px`, left: `${newLeft}px` }));
}
});
document.addEventListener('mouseup', () => {
isDragging = false;
panel.style.transition = 'all 0.3s ease';
});
}
function setPanelPosition(panel, position) {
panel.style.top = position.top;
panel.style.left = position.left;
}
})();