Size
8.1 KB
Version
1.0.1
Created
Oct 25, 2025
Updated
20 days ago
1// ==UserScript==
2// @name PornXnow Video Downloader
3// @description Download videos from pornxnow.me with one click
4// @version 1.0.1
5// @match https://*.pornxnow.me/*
6// @icon https://pornxnow.me/wp-content/uploads/2019/05/cropped-favicon-pornxnow-32x32.png
7// @grant GM.xmlhttpRequest
8// @connect pxnw.xyz
9// @connect *.pxnw.xyz
10// ==/UserScript==
11(function() {
12 'use strict';
13
14 console.log('PornXnow Video Downloader initialized');
15
16 // Debounce function to prevent multiple rapid calls
17 function debounce(func, wait) {
18 let timeout;
19 return function executedFunction(...args) {
20 const later = () => {
21 clearTimeout(timeout);
22 func(...args);
23 };
24 clearTimeout(timeout);
25 timeout = setTimeout(later, wait);
26 };
27 }
28
29 // Function to extract video URL from iframe
30 async function extractVideoUrl(iframeSrc) {
31 try {
32 console.log('Fetching iframe content from:', iframeSrc);
33
34 const response = await GM.xmlhttpRequest({
35 method: 'GET',
36 url: iframeSrc,
37 headers: {
38 'Referer': window.location.href
39 }
40 });
41
42 const html = response.responseText;
43 console.log('Iframe content fetched successfully');
44
45 // Try to find video URL in various formats
46 let videoUrl = null;
47
48 // Method 1: Look for direct video sources
49 const videoSourceMatch = html.match(/sources:\s*\[\s*{[^}]*file:\s*["']([^"']+\.mp4[^"']*)["']/i);
50 if (videoSourceMatch) {
51 videoUrl = videoSourceMatch[1];
52 }
53
54 // Method 2: Look for file: property
55 if (!videoUrl) {
56 const fileMatch = html.match(/file:\s*["']([^"']+\.mp4[^"']*)["']/i);
57 if (fileMatch) {
58 videoUrl = fileMatch[1];
59 }
60 }
61
62 // Method 3: Look for video tag src
63 if (!videoUrl) {
64 const videoTagMatch = html.match(/<video[^>]+src=["']([^"']+)["']/i);
65 if (videoTagMatch) {
66 videoUrl = videoTagMatch[1];
67 }
68 }
69
70 // Method 4: Look for any .mp4 URL
71 if (!videoUrl) {
72 const mp4Match = html.match(/(https?:\/\/[^"'\s]+\.mp4[^"'\s]*)/i);
73 if (mp4Match) {
74 videoUrl = mp4Match[1];
75 }
76 }
77
78 if (videoUrl) {
79 // Clean up the URL
80 videoUrl = videoUrl.replace(/\\/g, '');
81 console.log('Video URL found:', videoUrl);
82 return videoUrl;
83 } else {
84 console.error('Could not find video URL in iframe content');
85 return null;
86 }
87 } catch (error) {
88 console.error('Error extracting video URL:', error);
89 return null;
90 }
91 }
92
93 // Function to create download button
94 function createDownloadButton() {
95 console.log('Creating download button');
96
97 // Find the video player container
98 const videoWrapper = document.querySelector('.video-wrapper');
99 if (!videoWrapper) {
100 console.error('Video wrapper not found');
101 return;
102 }
103
104 // Check if button already exists
105 if (document.getElementById('custom-video-downloader')) {
106 console.log('Download button already exists');
107 return;
108 }
109
110 // Create download button container
111 const downloadContainer = document.createElement('div');
112 downloadContainer.id = 'custom-video-downloader';
113 downloadContainer.style.cssText = `
114 margin: 15px 0;
115 padding: 15px;
116 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
117 border-radius: 8px;
118 text-align: center;
119 box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
120 `;
121
122 // Create download button
123 const downloadButton = document.createElement('button');
124 downloadButton.id = 'download-video-btn';
125 downloadButton.innerHTML = '<i class="fa fa-download"></i> Download Video (HD)';
126 downloadButton.style.cssText = `
127 background: #fff;
128 color: #667eea;
129 border: none;
130 padding: 12px 30px;
131 font-size: 16px;
132 font-weight: bold;
133 border-radius: 5px;
134 cursor: pointer;
135 transition: all 0.3s ease;
136 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
137 `;
138
139 downloadButton.onmouseover = function() {
140 this.style.transform = 'scale(1.05)';
141 this.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.3)';
142 };
143
144 downloadButton.onmouseout = function() {
145 this.style.transform = 'scale(1)';
146 this.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.2)';
147 };
148
149 downloadButton.onclick = async function() {
150 const originalText = this.innerHTML;
151 this.innerHTML = '<i class="fa fa-spinner fa-spin"></i> Extracting video...';
152 this.disabled = true;
153
154 try {
155 // Get iframe source
156 const iframe = document.querySelector('.responsive-player iframe');
157 if (!iframe || !iframe.src) {
158 throw new Error('Video iframe not found');
159 }
160
161 const videoUrl = await extractVideoUrl(iframe.src);
162
163 if (videoUrl) {
164 this.innerHTML = '<i class="fa fa-check"></i> Opening download...';
165
166 // Open video in new tab for download
167 GM.openInTab(videoUrl, false);
168
169 setTimeout(() => {
170 this.innerHTML = originalText;
171 this.disabled = false;
172 }, 2000);
173 } else {
174 throw new Error('Could not extract video URL');
175 }
176 } catch (error) {
177 console.error('Download error:', error);
178 this.innerHTML = '<i class="fa fa-exclamation-triangle"></i> Error - Try again';
179 this.style.background = '#ff6b6b';
180 this.style.color = '#fff';
181
182 setTimeout(() => {
183 this.innerHTML = originalText;
184 this.style.background = '#fff';
185 this.style.color = '#667eea';
186 this.disabled = false;
187 }, 3000);
188 }
189 };
190
191 downloadContainer.appendChild(downloadButton);
192
193 // Insert after video player
194 const videoPlayer = videoWrapper.querySelector('.responsive-player');
195 if (videoPlayer && videoPlayer.nextSibling) {
196 videoWrapper.insertBefore(downloadContainer, videoPlayer.nextSibling);
197 } else {
198 videoWrapper.appendChild(downloadContainer);
199 }
200
201 console.log('Download button created successfully');
202 }
203
204 // Initialize the extension
205 function init() {
206 console.log('Initializing video downloader');
207
208 // Wait for page to load
209 if (document.readyState === 'loading') {
210 document.addEventListener('DOMContentLoaded', init);
211 return;
212 }
213
214 // Check if we're on a video page
215 const videoWrapper = document.querySelector('.video-wrapper');
216 if (!videoWrapper) {
217 console.log('Not a video page, extension will not run');
218 return;
219 }
220
221 // Wait a bit for iframe to load
222 setTimeout(() => {
223 createDownloadButton();
224 }, 2000);
225
226 // Observe DOM changes in case content loads dynamically
227 const observer = new MutationObserver(debounce(() => {
228 if (!document.getElementById('custom-video-downloader')) {
229 createDownloadButton();
230 }
231 }, 1000));
232
233 observer.observe(document.body, {
234 childList: true,
235 subtree: true
236 });
237 }
238
239 // Start the extension
240 init();
241})();