// ==UserScript==
// @name Youtube 迷你懸浮視窗
// @namespace http://tampermonkey.net/
// @version 2024-03-29
// @description try to take over the world!
// @author You
// @match https://www.youtube.com/*
// @require https://code.jquery.com/jquery-3.5.0.slim.min.js
// @grant GM_addStyle
// ==/UserScript==
//●目前問題
//※在迷你懸浮視窗移動的過程中,如果按下右鍵(此時鼠標正在迷你懸浮視窗上)
//會有即使你鬆開左鍵迷你懸浮視窗還是會黏在你鼠標上的bug
//如果將迷你懸浮視窗拖曳到視窗外放開左鍵也會有這個bug
//阻止影片加速的功能,並不是完全阻止影片加速,中間還是會有類似一幀的播放
//懸浮視窗大小是設定鎖死的 320px * 180px,所以遇到其他大小的影片比例(EX:直式短影音)會看不到完整影片
//若要改善可能整個4個拖曳區塊大小、播放/暫停鍵位置、懸浮視窗關閉按鈕的位置都要重寫
//(已解決)如果是在影片介面(網址含有youtube.com/watch?v=)重整
//當下由於抓不到影片元素腳本就被執行的關係,整個懸浮視窗除了CSS的部分其他都無法套用
//點擊進去其他影片就可以恢復
//有時候影片播放狀態跟暫停/播放鍵的狀態還是會不同(EX:影片正在播放可以暫停/播放鍵顯示的是三角形)
//是否顯示進度條
const DisplayProgressBar = true;
(function() {
GM_addStyle(`
.mini-close {
float: right;
position: relative;
bottom: 382px;
right: 7px;
fill: #fff;
width: 26px;
height: 26px;
opacity: 0;
z-index: 200;
/*cursor: pointer;*/
transition: 0.2s;
}
.mini-state-play , .mini-state-pause {
/*border: 1px #fff solid;*/
position: relative;
left: 100px;
width: 119px;
height: 104px;
opacity: 0;
transition: 0.2s;
}
#movie_player:not(.alike) .mini-state-play button svg path,
#movie_player:not(.alike) .mini-state-pause button svg path {
display: none;
}
#movie_player:not(.alike) .mini-state-play , #movie_player:not(.alike) .mini-state-pause {
z-index: -1;
}
.mini-state-play {
bottom: 145px;
}
.mini-state-pause {
bottom: 251px;
/*padding-left: 1px;*/
}
.drag {
border: 0px #000 solid;
/*cursor: move;*/
position: relative;
z-index: 100;
}
.drag-top {
width: 100%;
height: 40px;
}
.drag-left {
width: 100px;
height: 100px;
float: left;
}
.drag-right {
width: 100px;
height: 100px;
float: right;
}
.drag-bottom {
width: 100%;
height: 40px;
margin-top: 100px
}
#movie_player.ytp-fullscreen {
width: 100% ;
height: 100% ;
position: static;
}
#movie_player.alike:not(.ytp-fullscreen) {
position: fixed;
z-index: 1337;
width: 320px ;
height: 180px ;
border-radius: 8px;
box-shadow: 0px 0px 8px #000 !important;
/*如果要回復以前預設的懸浮視窗位置,把下面的margin left top註解掉就好*/
margin: 0 !important;
left: 850px;
top: 400px;
}
#movie_player.alike:not(.ytp-fullscreen) .ytp-bezel-text-hide ,
#movie_player.alike:not(.ytp-fullscreen) .ytp-spinner ,
#movie_player.alike:not(.ytp-fullscreen) .ytp-overlay .ytp-speedmaster-has-icon,
#movie_player.alike:not(.ytp-fullscreen) .ytp-gradient-bottom ,
#movie_player.alike:not(.ytp-fullscreen) .html5-endscreen ,
#movie_player.alike:not(.ytp-fullscreen) .ytp-ce-element{
display: none !important;
}
#columns #movie_player.alike > .ytp-chrome-bottom {
display: none;
}
.mini-progress-bar .ytp-chapter-hover-container {
width: 320px !important;
}
.mini-progress-bar {
opacity: 0;
/*transition: 0.1s;*/
top: 176px;
pointer-events: none;
}
ytd-watch-flexy:not(.alikeSmall) #columns #movie_player.alike:not(.ytp-fullscreen) video,
ytd-watch-flexy:not(.alikeSmall) #columns #movie_player.alike:not(.ytp-fullscreen) .html5-video-container {
display: block;
width: 100% !important;
height: 100% !important;
top: 0 !important;
left: 0 !important;
}
/*CC字幕*/
#movie_player.alike:not(.ytp-fullscreen) #ytp-caption-window-container .caption-window .captions-text .caption-visual-line .ytp-caption-segment {
font-size: 14.5px !important;
}
#movie_player.alike:not(.ytp-fullscreen) .caption-window.ytp-caption-window-bottom {
margin-bottom: 4px !important;
pointer-events: none;
}
`);
let playerDocked = false;
function activate() {
if (!playerDocked) {
$("#movie_player").addClass("alike");
window.dispatchEvent(new Event('resize'));
playerDocked = true;
}
}
function deactivate() {
if (playerDocked) {
$("#movie_player").removeClass("alike");
window.dispatchEvent(new Event('resize'));
playerDocked = false;
}
}
function scrolling() {
let limiter = $("#player-container #ytd-player");
let top_of_element = $(limiter).offset().top;
let bottom_of_element = $(limiter).offset().top + $(limiter).outerHeight();
let bottom_of_screen = $(window).scrollTop() + $(window).innerHeight();
let top_of_screen = $(window).scrollTop();
if ((bottom_of_screen > top_of_element) && (top_of_screen < bottom_of_element)) {
//如果影片可視(迷你視窗關閉)
deactivate();
$('#movie_player').css('right','');
$('#movie_player').css('left','');
$('#movie_player').css('top','');
$('#movie_player').css('bottom','');
} else {
//如果影片不可視(迷你視窗開啟)
activate();
}
}
//阻止長按加速功能
function preventLongPressAcceleration() {
let timeStart, timeEnd , time;
$('.mini-state-pause , .mini-state-play , .drag , .mini-close').each((index) => {
$('.mini-state-pause , .mini-state-play , .drag , .mini-close')[index].onmousedown = (e) => {
if($('#movie_player.alike')[0]) {
timeStart = new Date().getTime();
time = setInterval(() => {
timeEnd = new Date().getTime();
if(timeEnd - timeStart > 560) {
clearInterval(time);
$('.html5-video-container video')[0].playbackRate = $('.ytp-bezel[aria-label = "暫停"]')[0]||$('.ytp-bezel[aria-label="停止即時播放"]')[0] ? 0 : 1;
}
}, 10);
}
}
});
$('.mini-state-pause , .mini-state-play , .drag , .mini-close').each((index) => {
$('.mini-state-pause , .mini-state-play , .drag , .mini-close')[index].onmouseup = (e) => {
if($('#movie_player.alike')[0]) {
clearInterval(time);
$('.html5-video-container video')[0].playbackRate = 1;
}
}
});
}
//確保找到影片元素再執行
function findMoviePlayer() {
return new Promise((resolve, reject) => {
const checkPlayer = () => {
if ($('#movie_player')[0] === undefined) {
//console.log('抓不到');
setTimeout(checkPlayer, 100);
}
else {
resolve($('#movie_player')[0]);
}
}
checkPlayer();
});
}
//主要拖曳事件函數 參考來自:https://zh.javascript.info/mouse-drag-and-drop
function drag(miniVideo, flag) {
//let miniVideo = $('#movie_player')[0];
//console.log(miniVideo);
miniVideo.onmousedown = (e) => {
//如果現在是一般頁面(迷你懸浮視窗沒有出現),則不給拖曳
if(!$('#movie_player.alike:not(.ytp-fullscreen)')[0]) return
let mouseCoordinatesX = e.pageX - $('#movie_player').offset().left;
let mouseCoordinatesY = e.pageY - $('#movie_player').offset().top;
//console.log(e.pageX - $('#movie_player').offset().left,e.pageY - $('#movie_player').offset().top);
//如果在播放按鈕上,則不給拖曳
if(100 < mouseCoordinatesX && mouseCoordinatesX < 200 && 39 < mouseCoordinatesY && mouseCoordinatesY < 139) {
//console.log('現在在播放按鈕上');
//終止冒泡,取消滑鼠在播放按鈕上的拖曳效果所造成的暫停/播放
e.stopPropagation();
return
}
let shiftX = e.clientX - miniVideo.getBoundingClientRect().left;
let shiftY = e.clientY - miniVideo.getBoundingClientRect().top;
function move(clientX , clientY) {
miniVideo.style.left = clientX - shiftX + 'px';
miniVideo.style.top = clientY - shiftY + 'px';
}
move(e.clientX, e.clientY);
function onMouseMove(e) {
move(e.clientX, e.clientY);
}
document.addEventListener('mousemove', onMouseMove);
miniVideo.onmouseup = () => {
document.removeEventListener('mousemove', onMouseMove);
miniVideo.onmouseup = null;
}
}
miniVideo.ondragstart = () => {
return false;
}
};
function addNode() {
$('.html5-video-container').append($(`<div class='drag drag-top'></div>`));
$('.html5-video-container').append($(`<div class='drag drag-left'></div>`));
$('.html5-video-container').append($(`<div class='drag drag-right'></div>`));
$('.html5-video-container').append($(`<div class='drag drag-bottom'></div>`));
$('.html5-video-container').append($(`<div class='mini-state-play'></div>`));
$('.html5-video-container').append($(`<div class='mini-state-pause'></div>`));
$('.html5-video-container').append($(`<div class='mini-close'></div>`));
//影片暫停時的播放標誌(三角形)
$('.mini-state-pause').append($(`<button class="ytp-play-button ytp-button" aria-keyshortcuts="k" data-title-no-tooltip="播放" aria-label="播放鍵盤快速鍵k"><svg height="100%" version="1.1" viewBox="0 0 36 36" width="100%"><use class="ytp-svg-shadow" xlink:href="#ytp-id-43"></use><path class="ytp-svg-fill" d="M 12,26 18.5,22 18.5,14 12,10 z M 18.5,22 25,18 25,18 18.5,14 z" id="ytp-id-43"></path></svg></button>`));
//影片播放時的播放標誌(雙長方形)
$('.mini-state-play').append($(`<button class="ytp-play-button ytp-button" aria-keyshortcuts="k" data-title-no-tooltip="暫停" aria-label="暫停鍵盤快速鍵k"><svg height="100%" version="1.1" viewBox="0 0 36 36" width="100%"><use class="ytp-svg-shadow" xlink:href="#ytp-id-73"></use><path class="ytp-svg-fill" d="M 12,26 16,26 16,10 12,10 z M 21,26 25,26 25,10 21,10 z" id="ytp-id-73"></path></svg></button>`));
//增加右上角關閉迷你懸浮視窗的按鈕
$('.mini-close').append($(`<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" data-pointer="none" viewBox="0 0 16 16"><path d="m8 6.939 3.182-3.182a.75.75 0 1 1 1.061 1.061L9.061 8l3.182 3.182a.75.75 0 1 1-1.061 1.061L8 9.061l-3.182 3.182a.75.75 0 1 1-1.061-1.061L6.939 8 3.757 4.818a.75.75 0 1 1 1.061-1.061L8 6.939z"></path></svg>`));
}
function miniProgressBar() {
if($('#movie_player.alike:not(.ytp-fullscreen)')[0]) {
if(!$('.mini-progress-bar')[0]) {
$('#movie_player').append($('.ytp-progress-bar-container'));
$('.ytp-progress-bar-container').addClass('mini-progress-bar');
$('.mini-progress-bar').css('opacity', '0');
$('.ytp-progress-bar-container').css('display', 'block');
//console.log('已增加');
}
}
else if($('#movie_player:not(.alike)')[0]||$('#movie_player.ytp-fullscreen')[0]) {
if($('.mini-progress-bar')[0]) {
$('.ytp-chrome-bottom').append($('.ytp-progress-bar-container'));
$('.mini-progress-bar').css('opacity', '1');
$('.ytp-progress-bar-container').removeClass('mini-progress-bar');
//console.log('已還原');
}
}
}
async function handleUIActions(nowUrl) {
//暫停時點擊其他影片進去後會自動播放,所以將標籤內容改正回來,改為.ytp-bezel[aria-label = "播放"]
await findMoviePlayer();
if(nowUrl!==window.location.href) {
$('.ytp-bezel[aria-label = "暫停"] , .ytp-bezel[aria-label="停止即時播放"]').attr('aria-label', '播放');
miniProgressBar();
}
/*$('.drag , .mini-close').mouseover((e) => {
if($('#movie_player.alike:not(.ytp-fullscreen)')[0]) {
//終止冒泡
$('.drag').css('cursor', 'move');
$('.mini-close').css('cursor', 'pointer');
}
});*/
//在迷你懸浮視窗上的「可移動區域」、「關閉按鈕」上,封鎖點擊滑鼠左鍵造成暫停的效果
$('.drag , .mini-close').click((e) => {
if($('#movie_player.alike:not(.ytp-fullscreen)')[0]) {
//終止冒泡
e.stopPropagation();
}
});
//滑鼠移動到迷你懸浮視窗上的「可移動區域」、「關閉按鈕」時,改變滑鼠樣式
$('.drag , .mini-close').mouseover((e) => {
if($('#movie_player.alike:not(.ytp-fullscreen)')[0]) {
$('.drag').css('cursor', 'move');
$('.mini-close').css('cursor', 'pointer');
}
});
//滑鼠移上去時顯示暫停/播放標誌
$('.mini-state-pause , .mini-state-play , .drag , .mini-close').mouseover((e) => {
if($('#movie_player.alike')[0]) {
//當滑鼠移上去時,如果目前是暫停狀態,表示點擊後會播放,所以顯示播放圖示,隱藏暫停圖示
if($('.ytp-bezel[aria-label = "暫停"]')[0]||$('.ytp-bezel[aria-label="停止即時播放"]')[0]) {
$('.mini-state-pause').css('opacity', '0.9');
$('.mini-state-play').css('opacity', '0');
}
//當滑鼠移上去時,如果目前是播放狀態,表示點擊後會暫停,所以顯示暫停圖示,隱藏播放圖示
else {
$('.mini-state-pause').css('opacity', '0');
$('.mini-state-play').css('opacity', '0.9');
}
$('.mini-close , .mini-progress-bar').css('opacity', '0.9');
$('.mini-progress-bar').css('opacity', '1');
$('.mini-state-pause .ytp-button , .mini-state-play .ytp-button').css('cursor', 'pointer');
}
else {
$('.mini-state-pause .ytp-button , .mini-state-play .ytp-button').css('cursor', 'default');
$('.drag').css('cursor', 'default');
$('.mini-close').css('cursor', 'default');
}
});
$('.mini-state-pause , .drag , .mini-close').mouseout((e) => {
$('.mini-state-pause , .mini-state-play , .mini-close , .mini-progress-bar').css('opacity', '0');
});
//點擊播放、暫停時,更換標誌
$('.mini-state-pause').click((e) => {
if($('#movie_player.alike')[0]) {
//當點擊時,如果目前是暫停狀態,表示點擊後會播放,所以顯示播放圖示,隱藏暫停圖示
if($('.ytp-bezel[aria-label = "暫停"]')[0]||$('.ytp-bezel[aria-label="停止即時播放"]')[0]) {
$('.mini-state-pause').css('opacity', '0');
$('.mini-state-play').css('opacity', '0.9');
//這邊在檢查到$('.ytp-bezel[aria-label = "暫停"]')[0]後將其印出
//但結果是ytp-bezel[aria-label = "播放"],表示在按下按鈕的那刻還是暫停狀態,之後就變到播放狀態了
//console.log($('.ytp-bezel[aria-label = "暫停"]')[0],'播放');
}
//當點擊時,如果目前是播放狀態,表示點擊後會暫停,所以顯示暫停圖示,隱藏播放圖示
else {
$('.mini-state-pause').css('opacity', '0.9');
$('.mini-state-play').css('opacity', '0');
//console.log($('.ytp-bezel[aria-label = "暫停"]')[0],'暫停');
}
}
//e.stopPropagation();
});
//右上角關閉迷你懸浮視窗的按鈕功能實現
$('.mini-close').click(() => {
/*if(!$('#movie_player:not(.alike)')[0]) {
$('#movie_player').hide();
}*/
$("#movie_player").removeClass("alike");
});
}
async function Run() {
let miniVideo = await findMoviePlayer();
drag(miniVideo);
addNode();
preventLongPressAcceleration();
}
$(() => {
window.addEventListener("yt-navigate-finish", () => {
if (window.location.href.indexOf('youtube.com/watch?v=') > -1) {
$("#movie_player").removeClass("alike");
//scrolling();
$(window).scroll(() => {
scrolling();
});
if(!$('.drag')[0]) {
//儲存目前網址,用來檢查有沒有切換頁面
var nowUrl = window.location.href;
Run();
}
handleUIActions(nowUrl);
if(DisplayProgressBar) {
$(window).scroll(() => {
miniProgressBar();
});
$(window).keydown((e) => {
if(e.keyCode == 70) {
miniProgressBar();
}
});
}
}
else {
if($("#movie_player.alike")[0]) $("#movie_player").removeClass("alike");
console.log('非影片介面');
console.log($("#movie_player")[0]);
}
});
});
})();