add crackstream fit to window and enable updates
This commit is contained in:
parent
563ad690c2
commit
260f951cf1
2 changed files with 165 additions and 34 deletions
130
scripts/javascript/cs-fit-to-window.user.js
Normal file
130
scripts/javascript/cs-fit-to-window.user.js
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
// ==UserScript==
|
||||||
|
// @name CrackStreams - Fit Video to Window
|
||||||
|
// @namespace https://git.okseby.com/okseby/crunchbang
|
||||||
|
// @version 1.0
|
||||||
|
// @description Adds a button that expands the video player to fill the browser window (not real fullscreen)
|
||||||
|
// @author seby
|
||||||
|
// @match https://crackstreams.mx/stream/*
|
||||||
|
// @match https://footybite.ac/event/*
|
||||||
|
// @updateURL https://git.okseby.com/okseby/crunchbang/raw/branch/main/scripts/javascript/fit-to-window.user.js
|
||||||
|
// @downloadURL https://git.okseby.com/okseby/crunchbang/raw/branch/main/scripts/javascript/fit-to-window.user.js
|
||||||
|
// @grant none
|
||||||
|
// @run-at document-idle
|
||||||
|
// ==/UserScript==
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const PLAYER_SELECTOR = "#videoPlayer";
|
||||||
|
const BUTTON_ID = "fit-to-window-btn";
|
||||||
|
|
||||||
|
let isFitted = false;
|
||||||
|
let placeholder = null; // marks where the player used to live in the DOM
|
||||||
|
let originalStyle = null;
|
||||||
|
|
||||||
|
function getPlayer() {
|
||||||
|
return document.querySelector(PLAYER_SELECTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeButton() {
|
||||||
|
const btn = document.createElement("button");
|
||||||
|
btn.id = BUTTON_ID;
|
||||||
|
btn.textContent = "Fit to Window";
|
||||||
|
Object.assign(btn.style, {
|
||||||
|
position: "fixed",
|
||||||
|
top: "10px",
|
||||||
|
right: "10px",
|
||||||
|
zIndex: "2147483647",
|
||||||
|
padding: "8px 14px",
|
||||||
|
fontSize: "14px",
|
||||||
|
fontWeight: "600",
|
||||||
|
color: "#fff",
|
||||||
|
background: "#2563eb",
|
||||||
|
border: "none",
|
||||||
|
borderRadius: "6px",
|
||||||
|
cursor: "pointer",
|
||||||
|
boxShadow: "0 2px 6px rgba(0,0,0,0.4)",
|
||||||
|
fontFamily: "system-ui, sans-serif",
|
||||||
|
opacity: "0.08",
|
||||||
|
transition: "opacity 0.2s ease",
|
||||||
|
});
|
||||||
|
btn.addEventListener("mouseenter", () => {
|
||||||
|
btn.style.opacity = "1";
|
||||||
|
});
|
||||||
|
btn.addEventListener("mouseleave", () => {
|
||||||
|
btn.style.opacity = "0.08";
|
||||||
|
});
|
||||||
|
btn.addEventListener("click", toggleFit);
|
||||||
|
document.body.appendChild(btn);
|
||||||
|
return btn;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleFit() {
|
||||||
|
const player = getPlayer();
|
||||||
|
if (!player) return;
|
||||||
|
|
||||||
|
const btn = document.getElementById(BUTTON_ID);
|
||||||
|
|
||||||
|
if (!isFitted) {
|
||||||
|
// Save original position info so we can restore later
|
||||||
|
placeholder = document.createComment("fit-to-window-placeholder");
|
||||||
|
player.parentNode.insertBefore(placeholder, player);
|
||||||
|
|
||||||
|
originalStyle = player.getAttribute("style") || "";
|
||||||
|
|
||||||
|
Object.assign(player.style, {
|
||||||
|
position: "fixed",
|
||||||
|
top: "0",
|
||||||
|
left: "0",
|
||||||
|
width: "100vw",
|
||||||
|
height: "100vh",
|
||||||
|
margin: "0",
|
||||||
|
zIndex: "2147483646",
|
||||||
|
background: "#000",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Make the iframe/video inside stretch fully too
|
||||||
|
const inner = player.querySelector("iframe, video");
|
||||||
|
if (inner) {
|
||||||
|
inner.style.width = "100%";
|
||||||
|
inner.style.height = "100%";
|
||||||
|
}
|
||||||
|
|
||||||
|
document.documentElement.style.overflow = "hidden";
|
||||||
|
document.body.style.overflow = "hidden";
|
||||||
|
|
||||||
|
isFitted = true;
|
||||||
|
if (btn) btn.textContent = "Exit Fit";
|
||||||
|
} else {
|
||||||
|
// Restore original style/position
|
||||||
|
if (originalStyle !== null) {
|
||||||
|
player.setAttribute("style", originalStyle);
|
||||||
|
} else {
|
||||||
|
player.removeAttribute("style");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (placeholder && placeholder.parentNode) {
|
||||||
|
placeholder.parentNode.insertBefore(player, placeholder);
|
||||||
|
placeholder.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
document.documentElement.style.overflow = "";
|
||||||
|
document.body.style.overflow = "";
|
||||||
|
|
||||||
|
isFitted = false;
|
||||||
|
if (btn) btn.textContent = "Fit to Window";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
if (document.getElementById(BUTTON_ID)) return;
|
||||||
|
if (!getPlayer()) {
|
||||||
|
// Player might load slightly after DOM ready; retry briefly
|
||||||
|
setTimeout(init, 500);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
makeButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
})();
|
||||||
|
|
@ -1,15 +1,17 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name PrivateHD Seed Timer
|
// @name PrivateHD Seed Timer
|
||||||
// @namespace https://privatehd.to/
|
// @namespace https://git.okseby.com/okseby/crunchbang
|
||||||
// @version 1.0.0
|
// @version 1.0.0
|
||||||
// @description Shows required seed time and whether you can stop seeding each torrent, based on file size.
|
// @description Shows required seed time and whether you can stop seeding each torrent, based on file size.
|
||||||
// @author you
|
// @author seby
|
||||||
// @match https://privatehd.to/profile/*/active*
|
// @match https://privatehd.to/profile/*/active*
|
||||||
|
// @updateURL https://git.okseby.com/okseby/crunchbang/raw/branch/main/scripts/javascript/privatehd-seed-timer.user.js
|
||||||
|
// @downloadURL https://git.okseby.com/okseby/crunchbang/raw/branch/main/scripts/javascript/privatehd-seed-timer.user.js
|
||||||
// @grant none
|
// @grant none
|
||||||
// ==/UserScript==
|
// ==/UserScript==
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Formula: required seed time in hours, where x = size in GB
|
// Formula: required seed time in hours, where x = size in GB
|
||||||
|
|
@ -18,8 +20,8 @@
|
||||||
// x >= 50 → 100 * ln(x) - 219.2023 h
|
// x >= 50 → 100 * ln(x) - 219.2023 h
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
function requiredHours(sizeGB) {
|
function requiredHours(sizeGB) {
|
||||||
if (sizeGB <= 1) return 72;
|
if (sizeGB <= 1) return 72;
|
||||||
if (sizeGB < 50) return 72 + 2 * sizeGB;
|
if (sizeGB < 50) return 72 + 2 * sizeGB;
|
||||||
return 100 * Math.log(sizeGB) - 219.2023;
|
return 100 * Math.log(sizeGB) - 219.2023;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -29,7 +31,7 @@
|
||||||
function parseSizeToGB(str) {
|
function parseSizeToGB(str) {
|
||||||
const m = str.trim().match(/([\d.]+)\s*(B|KB|MB|GB|TB)/i);
|
const m = str.trim().match(/([\d.]+)\s*(B|KB|MB|GB|TB)/i);
|
||||||
if (!m) return null;
|
if (!m) return null;
|
||||||
const val = parseFloat(m[1]);
|
const val = parseFloat(m[1]);
|
||||||
const unit = m[2].toUpperCase();
|
const unit = m[2].toUpperCase();
|
||||||
const factors = { B: 1e-9, KB: 1e-6, MB: 1e-3, GB: 1, TB: 1e3 };
|
const factors = { B: 1e-9, KB: 1e-6, MB: 1e-3, GB: 1, TB: 1e3 };
|
||||||
return val * (factors[unit] ?? 1);
|
return val * (factors[unit] ?? 1);
|
||||||
|
|
@ -42,11 +44,11 @@
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
function parseElapsedHours(str) {
|
function parseElapsedHours(str) {
|
||||||
let hours = 0;
|
let hours = 0;
|
||||||
const days = str.match(/(\d+)\s*d/);
|
const days = str.match(/(\d+)\s*d/);
|
||||||
const hrs = str.match(/(\d+)\s*h/);
|
const hrs = str.match(/(\d+)\s*h/);
|
||||||
const mins = str.match(/(\d+)\s*m/);
|
const mins = str.match(/(\d+)\s*m/);
|
||||||
if (days) hours += parseInt(days[1], 10) * 24;
|
if (days) hours += parseInt(days[1], 10) * 24;
|
||||||
if (hrs) hours += parseInt(hrs[1], 10);
|
if (hrs) hours += parseInt(hrs[1], 10);
|
||||||
if (mins) hours += parseInt(mins[1], 10) / 60;
|
if (mins) hours += parseInt(mins[1], 10) / 60;
|
||||||
return hours;
|
return hours;
|
||||||
}
|
}
|
||||||
|
|
@ -56,63 +58,63 @@
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
function fmtHours(h) {
|
function fmtHours(h) {
|
||||||
const days = Math.floor(h / 24);
|
const days = Math.floor(h / 24);
|
||||||
const hrs = Math.floor(h % 24);
|
const hrs = Math.floor(h % 24);
|
||||||
const mins = Math.round((h % 1) * 60);
|
const mins = Math.round((h % 1) * 60);
|
||||||
if (days > 0) return `${days}d ${hrs}h`;
|
if (days > 0) return `${days}d ${hrs}h`;
|
||||||
return `${hrs}h ${String(mins).padStart(2, '0')}m`;
|
return `${hrs}h ${String(mins).padStart(2, "0")}m`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Add a "Seed Status" column header
|
// Add a "Seed Status" column header
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
const thead = document.querySelector('table thead tr');
|
const thead = document.querySelector("table thead tr");
|
||||||
if (!thead) return;
|
if (!thead) return;
|
||||||
|
|
||||||
const th = document.createElement('th');
|
const th = document.createElement("th");
|
||||||
th.textContent = 'Seed Status';
|
th.textContent = "Seed Status";
|
||||||
th.style.whiteSpace = 'nowrap';
|
th.style.whiteSpace = "nowrap";
|
||||||
thead.appendChild(th);
|
thead.appendChild(th);
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Process each data row
|
// Process each data row
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
const rows = document.querySelectorAll('table tbody tr');
|
const rows = document.querySelectorAll("table tbody tr");
|
||||||
|
|
||||||
rows.forEach(row => {
|
rows.forEach((row) => {
|
||||||
const cells = row.querySelectorAll('td');
|
const cells = row.querySelectorAll("td");
|
||||||
// Column layout (0-indexed):
|
// Column layout (0-indexed):
|
||||||
// 0: icon | 1: name+stats | 2: progress | 3: status | 4: client
|
// 0: icon | 1: name+stats | 2: progress | 3: status | 4: client
|
||||||
// 5: upload | 6: download | 7: left | 8: ratio | 9: started | 10: updated | 11: For
|
// 5: upload | 6: download | 7: left | 8: ratio | 9: started | 10: updated | 11: For
|
||||||
|
|
||||||
const nameCell = cells[1];
|
const nameCell = cells[1];
|
||||||
const forCell = cells[11]; // "For" — how long you've been seeding
|
const forCell = cells[11]; // "For" — how long you've been seeding
|
||||||
|
|
||||||
if (!nameCell || !forCell) {
|
if (!nameCell || !forCell) {
|
||||||
const td = document.createElement('td');
|
const td = document.createElement("td");
|
||||||
td.textContent = '—';
|
td.textContent = "—";
|
||||||
row.appendChild(td);
|
row.appendChild(td);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// File size lives inside the name cell: fa-database span
|
// File size lives inside the name cell: fa-database span
|
||||||
const sizeSpan = nameCell.querySelector('.fa-database');
|
const sizeSpan = nameCell.querySelector(".fa-database");
|
||||||
const sizeText = sizeSpan ? sizeSpan.textContent.trim() : null;
|
const sizeText = sizeSpan ? sizeSpan.textContent.trim() : null;
|
||||||
const sizeGB = sizeText ? parseSizeToGB(sizeText) : null;
|
const sizeGB = sizeText ? parseSizeToGB(sizeText) : null;
|
||||||
|
|
||||||
const elapsedText = forCell.textContent.trim();
|
const elapsedText = forCell.textContent.trim();
|
||||||
const elapsed = parseElapsedHours(elapsedText);
|
const elapsed = parseElapsedHours(elapsedText);
|
||||||
const required = sizeGB !== null ? requiredHours(sizeGB) : null;
|
const required = sizeGB !== null ? requiredHours(sizeGB) : null;
|
||||||
|
|
||||||
const td = document.createElement('td');
|
const td = document.createElement("td");
|
||||||
td.style.whiteSpace = 'nowrap';
|
td.style.whiteSpace = "nowrap";
|
||||||
td.style.verticalAlign = 'middle';
|
td.style.verticalAlign = "middle";
|
||||||
|
|
||||||
if (required === null) {
|
if (required === null) {
|
||||||
td.textContent = 'No size data';
|
td.textContent = "No size data";
|
||||||
td.style.color = '#999';
|
td.style.color = "#999";
|
||||||
} else {
|
} else {
|
||||||
const remaining = required - elapsed;
|
const remaining = required - elapsed;
|
||||||
const canStop = remaining <= 0;
|
const canStop = remaining <= 0;
|
||||||
|
|
||||||
if (canStop) {
|
if (canStop) {
|
||||||
td.innerHTML =
|
td.innerHTML =
|
||||||
|
|
@ -127,5 +129,4 @@
|
||||||
|
|
||||||
row.appendChild(td);
|
row.appendChild(td);
|
||||||
});
|
});
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue