Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name PW Speed Controller - Fixed Menu & Sessions
- // @namespace http://tampermonkey.net/
- // @version 4.6
- // @description Fixed PW speed controller with session progress and working menu
- // @author u/padhaikarlothoda
- // @match *://www.pw.live/*
- // @match *://*.pw.live/*
- // @grant none
- // @run-at document-end
- // ==/UserScript==
- (function() {
- 'use strict';
- let currentSpeed = 2.5;
- let isVisible = false;
- let video = null;
- let totalTimeSaved = 0;
- // Storage
- function saveData() {
- localStorage.setItem('pwSpeedController', JSON.stringify({
- currentSpeed: currentSpeed,
- totalTimeSaved: totalTimeSaved,
- isVisible: isVisible
- }));
- }
- function loadData() {
- const saved = localStorage.getItem('pwSpeedController');
- if (saved) {
- const data = JSON.parse(saved);
- currentSpeed = data.currentSpeed || 2.5;
- totalTimeSaved = data.totalTimeSaved || 0;
- isVisible = data.isVisible || false;
- }
- }
- // Check if lecture is live
- function isLiveLecture() {
- const liveIndicators = [
- '[class*="live" i]',
- '[data-live="true"]',
- 'span:contains("LIVE")',
- 'div:contains("LIVE")',
- '[class*="streaming" i]'
- ];
- for (const selector of liveIndicators) {
- try {
- if (document.querySelector(selector)) {
- return true;
- }
- } catch (e) {
- // Skip invalid selectors
- }
- }
- return window.location.href.includes('live=true') ||
- window.location.href.includes('type=live');
- }
- // Calculate completion time
- function calculateCompletionTime() {
- if (isLiveLecture()) {
- return "🔴 LIVE";
- }
- if (!video || !video.duration || isNaN(video.duration)) {
- return "Calculating...";
- }
- const remainingTime = (video.duration - video.currentTime) / currentSpeed;
- const completionTime = new Date(Date.now() + remainingTime * 1000);
- return completionTime.toLocaleTimeString('en-US', {
- hour: 'numeric',
- minute: '2-digit',
- hour12: true
- });
- }
- // Calculate session progress
- function getSessionProgress() {
- if (isLiveLecture()) {
- return { text: "🔴 LIVE", percentage: 0 };
- }
- if (!video || !video.duration || isNaN(video.duration)) {
- return { text: "0h 0m 0s (0%)", percentage: 0 };
- }
- const currentTime = video.currentTime;
- const duration = video.duration;
- const percentage = Math.round((currentTime / duration) * 100);
- const hours = Math.floor(currentTime / 3600);
- const minutes = Math.floor((currentTime % 3600) / 60);
- const seconds = Math.floor(currentTime % 60);
- return {
- text: `${hours}h ${minutes}m ${seconds}s (${percentage}%)`,
- percentage: percentage
- };
- }
- // Find video
- function findVideo() {
- return document.querySelector('video');
- }
- // Find video player container
- function findVideoPlayerContainer() {
- const video = findVideo();
- if (!video) return null;
- const selectors = [
- '.video-player',
- '.plyr',
- '.video-container',
- '[class*="player"]',
- '[class*="video"]'
- ];
- for (const selector of selectors) {
- const container = document.querySelector(selector);
- if (container && container.contains(video)) {
- console.log('✅ Found video container:', selector);
- return container;
- }
- }
- return video.parentElement;
- }
- // Find chat button
- function findChatButton() {
- const svgs = document.querySelectorAll('svg[width="40"][height="40"]');
- for (const svg of svgs) {
- const path = svg.querySelector('path[d*="M26.982 21.097"]');
- if (path) {
- return svg.closest('button') || svg.closest('div[role="button"]') || svg.parentElement;
- }
- }
- const chatSvgs = document.querySelectorAll('svg path[d*="M34 10.376v14.979"]');
- for (const path of chatSvgs) {
- const svg = path.closest('svg');
- if (svg) {
- return svg.closest('button') || svg.closest('div[role="button"]') || svg.parentElement;
- }
- }
- return null;
- }
- // Create centered button
- function createCenteredButton() {
- const videoContainer = findVideoPlayerContainer();
- if (!videoContainer) {
- console.log('Video container not found, creating floating button');
- createFloatingButton();
- return;
- }
- const buttonContainer = document.createElement('div');
- buttonContainer.id = 'pw-speed-btn-container';
- const speedBtn = document.createElement('button');
- speedBtn.id = 'pw-speed-btn-centered';
- speedBtn.innerHTML = `⚡ ${currentSpeed}x`;
- buttonContainer.appendChild(speedBtn);
- const style = document.createElement('style');
- style.textContent = `
- #pw-speed-btn-container {
- position: absolute !important;
- bottom: 10px !important;
- left: 50% !important;
- transform: translateX(-50%) !important;
- z-index: 999999 !important;
- pointer-events: none !important;
- }
- #pw-speed-btn-centered {
- background: rgba(0, 0, 0, 0.8) !important;
- backdrop-filter: blur(10px) !important;
- border: 1px solid rgba(255, 255, 255, 0.3) !important;
- border-radius: 8px !important;
- color: #34c759 !important;
- padding: 6px 10px !important;
- font-size: 11px !important;
- font-weight: bold !important;
- cursor: pointer !important;
- transition: all 0.2s ease !important;
- min-width: 45px !important;
- pointer-events: auto !important;
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif !important;
- }
- #pw-speed-btn-centered:hover {
- background: rgba(0, 0, 0, 0.9) !important;
- transform: scale(1.05) !important;
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4) !important;
- }
- `;
- document.head.appendChild(style);
- const containerStyle = window.getComputedStyle(videoContainer);
- if (containerStyle.position === 'static') {
- videoContainer.style.position = 'relative';
- }
- videoContainer.appendChild(buttonContainer);
- speedBtn.addEventListener('click', function() {
- console.log('✅ Button clicked, toggling panel');
- togglePanel();
- });
- console.log('✅ Centered speed button created');
- }
- // Fallback floating button
- function createFloatingButton() {
- const btn = document.createElement('div');
- btn.id = 'pw-speed-floating';
- btn.innerHTML = `<div class="pw-btn">⚡ ${currentSpeed}x</div>`;
- const style = document.createElement('style');
- style.textContent = `
- #pw-speed-floating {
- position: fixed !important;
- bottom: 60px !important;
- right: 20px !important;
- z-index: 999999 !important;
- font-family: Arial, sans-serif !important;
- }
- .pw-btn {
- background: rgba(0, 0, 0, 0.8) !important;
- color: white !important;
- padding: 6px 10px !important;
- border-radius: 8px !important;
- cursor: pointer !important;
- font-size: 11px !important;
- font-weight: bold !important;
- border: 1px solid rgba(255, 255, 255, 0.3) !important;
- transition: all 0.2s !important;
- }
- .pw-btn:hover {
- background: rgba(0, 0, 0, 0.9) !important;
- transform: scale(1.05) !important;
- }
- `;
- document.head.appendChild(style);
- document.body.appendChild(btn);
- btn.addEventListener('click', function() {
- console.log('✅ Floating button clicked, toggling panel');
- togglePanel();
- });
- console.log('✅ Floating button created');
- }
- // Get button position for panel placement
- function getButtonPosition() {
- const centeredBtn = document.getElementById('pw-speed-btn-centered');
- const floatingBtn = document.getElementById('pw-speed-floating');
- const button = centeredBtn || floatingBtn;
- if (!button) {
- console.log('❌ No button found for positioning');
- return { top: '50px', left: '50%' };
- }
- const rect = button.getBoundingClientRect();
- const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
- const position = {
- top: Math.max(10, rect.top + scrollTop - 240) + 'px', // 240px above button, min 10px from top
- left: (rect.left + rect.width / 2) + 'px'
- };
- console.log('✅ Button position calculated:', position);
- return position;
- }
- // Create panel with session progress
- function createPanel() {
- // Remove existing panel if any
- const existingPanel = document.getElementById('pw-speed-panel');
- if (existingPanel) {
- existingPanel.remove();
- }
- const panel = document.createElement('div');
- panel.id = 'pw-speed-panel';
- panel.style.display = 'none'; // Start hidden
- const completion = calculateCompletionTime();
- const sessionProgress = getSessionProgress();
- panel.innerHTML = `
- <div class="panel">
- <div class="panel-header">
- <div class="lecture-status">
- <span class="status-label">🎯 Done by:</span>
- <span class="completion-time" id="completion-time">${completion}</span>
- </div>
- <button class="close" id="close-panel">×</button>
- </div>
- <div class="session-section">
- <div class="session-label">📊 Session Progress:</div>
- <div class="session-progress" id="session-progress">${sessionProgress.text}</div>
- <div class="progress-bar">
- <div class="progress-fill" id="progress-fill" style="width: ${sessionProgress.percentage}%"></div>
- </div>
- </div>
- <div class="speeds-compact">
- <button class="speed-btn-small" data-speed="1">1x</button>
- <button class="speed-btn-small" data-speed="1.25">1.25x</button>
- <button class="speed-btn-small" data-speed="1.5">1.5x</button>
- <button class="speed-btn-small" data-speed="1.75">1.75x</button>
- <button class="speed-btn-small" data-speed="2">2x</button>
- <button class="speed-btn-small active" data-speed="2.5">2.5x</button>
- <button class="speed-btn-small" data-speed="3">3x</button>
- </div>
- <div class="shortcuts-compact">
- <div class="shortcut-item">💬 <kbd>Alt + C</kbd></div>
- <div class="shortcut-item">⚡ <kbd>Shift + Z</kbd></div>
- </div>
- <div class="stats-compact">
- <div>⏰ Saved: <span id="time-saved">0h 0m 0s</span></div>
- </div>
- </div>
- `;
- const panelStyle = document.createElement('style');
- panelStyle.textContent = `
- #pw-speed-panel {
- position: fixed !important;
- z-index: 999998 !important;
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif !important;
- pointer-events: auto !important;
- }
- .panel {
- background: rgba(28, 28, 30, 0.95) !important;
- backdrop-filter: blur(20px) !important;
- border-radius: 10px !important;
- padding: 12px !important;
- width: 240px !important;
- color: white !important;
- border: 1px solid rgba(255, 255, 255, 0.1) !important;
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3) !important;
- animation: slideInUp 0.3s ease-out !important;
- font-size: 11px !important;
- }
- @keyframes slideInUp {
- from { opacity: 0; transform: translateX(-50%) translateY(10px); }
- to { opacity: 1; transform: translateX(-50%) translateY(0); }
- }
- .panel-header {
- display: flex !important;
- justify-content: space-between !important;
- align-items: center !important;
- margin-bottom: 10px !important;
- padding-bottom: 6px !important;
- border-bottom: 1px solid rgba(255, 255, 255, 0.1) !important;
- }
- .lecture-status {
- display: flex !important;
- flex-direction: column !important;
- gap: 2px !important;
- }
- .status-label {
- font-size: 10px !important;
- color: rgba(255, 255, 255, 0.7) !important;
- }
- .completion-time {
- font-size: 12px !important;
- font-weight: 600 !important;
- color: #34c759 !important;
- }
- .close {
- background: none !important;
- border: none !important;
- color: rgba(255, 255, 255, 0.6) !important;
- font-size: 16px !important;
- cursor: pointer !important;
- padding: 0 !important;
- width: 16px !important;
- height: 16px !important;
- border-radius: 50% !important;
- transition: all 0.2s !important;
- }
- .close:hover {
- background: rgba(255, 255, 255, 0.1) !important;
- color: white !important;
- }
- .session-section {
- margin-bottom: 10px !important;
- padding-bottom: 8px !important;
- border-bottom: 1px solid rgba(255, 255, 255, 0.1) !important;
- }
- .session-label {
- font-size: 10px !important;
- color: rgba(255, 255, 255, 0.7) !important;
- margin-bottom: 4px !important;
- }
- .session-progress {
- font-size: 11px !important;
- font-weight: 600 !important;
- color: #34c759 !important;
- margin-bottom: 6px !important;
- }
- .progress-bar {
- width: 100% !important;
- height: 4px !important;
- background: rgba(255, 255, 255, 0.2) !important;
- border-radius: 2px !important;
- overflow: hidden !important;
- }
- .progress-fill {
- height: 100% !important;
- background: linear-gradient(90deg, #34c759, #30d158) !important;
- border-radius: 2px !important;
- transition: width 0.3s ease !important;
- }
- .speeds-compact {
- display: grid !important;
- grid-template-columns: repeat(4, 1fr) !important;
- gap: 4px !important;
- margin-bottom: 10px !important;
- }
- .speed-btn-small {
- background: rgba(58, 58, 60, 0.8) !important;
- border: none !important;
- border-radius: 4px !important;
- color: white !important;
- padding: 4px !important;
- font-size: 9px !important;
- cursor: pointer !important;
- transition: all 0.2s !important;
- font-weight: 500 !important;
- min-height: 22px !important;
- }
- .speed-btn-small:hover {
- background: rgba(78, 78, 80, 0.8) !important;
- transform: translateY(-1px) !important;
- }
- .speed-btn-small.active {
- background: #007aff !important;
- box-shadow: 0 0 0 1px rgba(0, 122, 255, 0.3) !important;
- }
- .shortcuts-compact {
- display: flex !important;
- justify-content: space-between !important;
- background: rgba(44, 44, 46, 0.6) !important;
- border-radius: 6px !important;
- padding: 6px !important;
- margin-bottom: 8px !important;
- }
- .shortcut-item {
- font-size: 9px !important;
- display: flex !important;
- align-items: center !important;
- gap: 3px !important;
- }
- kbd {
- background: rgba(58, 58, 60, 0.8) !important;
- border: 1px solid rgba(255, 255, 255, 0.2) !important;
- border-radius: 2px !important;
- padding: 1px 4px !important;
- font-size: 8px !important;
- color: #34c759 !important;
- font-family: monospace !important;
- }
- .stats-compact {
- font-size: 10px !important;
- color: rgba(255, 255, 255, 0.8) !important;
- text-align: center !important;
- padding-top: 6px !important;
- border-top: 1px solid rgba(255, 255, 255, 0.1) !important;
- }
- #time-saved {
- color: #34c759 !important;
- font-weight: 600 !important;
- }
- `;
- document.head.appendChild(panelStyle);
- document.body.appendChild(panel);
- // Setup event listeners
- setupPanelEventListeners();
- console.log('✅ Panel created with session progress');
- }
- // Setup event listeners for panel
- function setupPanelEventListeners() {
- // Close button
- const closeBtn = document.getElementById('close-panel');
- if (closeBtn) {
- closeBtn.addEventListener('click', function() {
- console.log('✅ Close button clicked');
- togglePanel();
- });
- }
- // Speed buttons
- document.querySelectorAll('.speed-btn-small').forEach(btn => {
- btn.addEventListener('click', function() {
- const speed = parseFloat(this.dataset.speed);
- console.log('✅ Speed button clicked:', speed);
- setSpeed(speed);
- });
- });
- }
- // Toggle panel function
- window.togglePanel = function() {
- console.log('🔄 Toggle panel called, current visible state:', isVisible);
- isVisible = !isVisible;
- const panel = document.getElementById('pw-speed-panel');
- if (panel) {
- if (isVisible) {
- // Update position before showing
- const position = getButtonPosition();
- panel.style.top = position.top;
- panel.style.left = position.left;
- panel.style.transform = 'translateX(-50%)';
- panel.style.display = 'block';
- console.log('✅ Panel shown at position:', position);
- } else {
- panel.style.display = 'none';
- console.log('✅ Panel hidden');
- }
- saveData();
- } else {
- console.log('❌ Panel not found');
- }
- };
- // Set speed function
- window.setSpeed = function(speed) {
- video = findVideo();
- if (video) {
- video.playbackRate = speed;
- currentSpeed = speed;
- // Update buttons
- document.querySelectorAll('.speed-btn-small').forEach(btn => {
- btn.classList.remove('active');
- if (parseFloat(btn.dataset.speed) === speed) {
- btn.classList.add('active');
- }
- });
- // Update speed button text
- const centeredBtn = document.getElementById('pw-speed-btn-centered');
- const floatingBtn = document.querySelector('.pw-btn');
- if (centeredBtn) {
- centeredBtn.textContent = `⚡ ${speed}x`;
- }
- if (floatingBtn) {
- floatingBtn.textContent = `⚡ ${speed}x`;
- }
- // Preserve pitch
- video.preservesPitch = true;
- video.mozPreservesPitch = true;
- video.webkitPreservesPitch = true;
- saveData();
- console.log('✅ Speed set to:', speed);
- }
- };
- // Setup shortcuts
- function setupShortcuts() {
- document.addEventListener('keydown', (e) => {
- // Alt + C for chat
- if (e.altKey && e.key.toLowerCase() === 'c') {
- e.preventDefault();
- e.stopPropagation();
- const chatBtn = findChatButton();
- if (chatBtn) {
- const clickEvent = new MouseEvent('click', {
- view: window,
- bubbles: true,
- cancelable: true
- });
- chatBtn.click();
- chatBtn.dispatchEvent(clickEvent);
- const svg = chatBtn.querySelector('svg[width="40"][height="40"]');
- if (svg) {
- svg.click();
- svg.dispatchEvent(clickEvent);
- }
- console.log('✅ Chat opened via Alt+C');
- }
- }
- // Shift + Z for speed panel toggle
- if (e.shiftKey && e.key.toLowerCase() === 'z') {
- e.preventDefault();
- e.stopPropagation();
- console.log('✅ Shift+Z pressed, toggling panel');
- togglePanel();
- }
- });
- console.log('✅ Shortcuts setup complete');
- }
- // Time tracking with session progress updates
- function startTracking() {
- setInterval(() => {
- video = findVideo();
- // Update time saved
- if (video && currentSpeed > 1 && !video.paused) {
- const timeSavedPerSecond = (currentSpeed - 1) / currentSpeed;
- totalTimeSaved += timeSavedPerSecond;
- const timeSavedEl = document.getElementById('time-saved');
- if (timeSavedEl) {
- const hours = Math.floor(totalTimeSaved / 3600);
- const minutes = Math.floor((totalTimeSaved % 3600) / 60);
- const seconds = Math.floor(totalTimeSaved % 60);
- timeSavedEl.textContent = `${hours}h ${minutes}m ${seconds}s`;
- }
- saveData();
- }
- // Update completion time
- const completionEl = document.getElementById('completion-time');
- if (completionEl) {
- const newTime = calculateCompletionTime();
- completionEl.textContent = newTime;
- if (newTime.includes('LIVE')) {
- completionEl.style.color = '#ff3b30';
- } else {
- completionEl.style.color = '#34c759';
- }
- }
- // Update session progress
- const sessionProgressEl = document.getElementById('session-progress');
- const progressFillEl = document.getElementById('progress-fill');
- if (sessionProgressEl && progressFillEl) {
- const sessionData = getSessionProgress();
- sessionProgressEl.textContent = sessionData.text;
- progressFillEl.style.width = sessionData.percentage + '%';
- if (sessionData.text.includes('LIVE')) {
- sessionProgressEl.style.color = '#ff3b30';
- } else {
- sessionProgressEl.style.color = '#34c759';
- }
- }
- }, 1000);
- console.log('✅ Tracking started');
- }
- // Initialize
- function init() {
- console.log('🚀 Initializing PW Speed Controller...');
- loadData();
- setupShortcuts();
- let attempts = 0;
- const check = setInterval(() => {
- attempts++;
- video = findVideo();
- if (video) {
- clearInterval(check);
- console.log('✅ Video found, setting up controller');
- // Create button with delay
- setTimeout(() => {
- createCenteredButton();
- }, 2000);
- // Create panel with delay
- setTimeout(() => {
- createPanel();
- setSpeed(currentSpeed);
- }, 2500);
- startTracking();
- console.log('🎉 PW Speed Controller ready!');
- console.log('🔥 Shortcuts: Alt+C (Chat), Shift+Z (Speed Panel)');
- } else if (attempts >= 20) {
- clearInterval(check);
- console.log('❌ Video not found after 20 attempts');
- } else {
- console.log(`🔍 Looking for video... attempt ${attempts}/20`);
- }
- }, 1000);
- }
- // Start initialization
- setTimeout(init, 2000);
- })();
Advertisement
Add Comment
Please, Sign In to add comment