Size
8.8 KB
Version
1.0.1
Created
Jan 31, 2026
Updated
3 days ago
1// ==UserScript==
2// @name Video Downloader for PornforUs
3// @description Download videos from PornforUs with one click
4// @version 1.0.1
5// @match https://*.pornforus.com/*
6// @icon https://www.pornforus.com/favicon.ico
7// ==/UserScript==
8(function() {
9 'use strict';
10
11 console.log('Video Downloader extension loaded');
12
13 // Add custom styles for the download button
14 TM_addStyle(`
15 .video-download-btn {
16 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17 color: white;
18 padding: 12px 24px;
19 border-radius: 8px;
20 font-weight: bold;
21 cursor: pointer;
22 border: none;
23 box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
24 transition: all 0.3s ease;
25 display: inline-flex;
26 align-items: center;
27 gap: 8px;
28 font-size: 16px;
29 margin-top: 16px;
30 }
31
32 .video-download-btn:hover {
33 transform: translateY(-2px);
34 box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
35 background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
36 }
37
38 .video-download-btn:active {
39 transform: translateY(0);
40 }
41
42 .video-download-btn:disabled {
43 opacity: 0.6;
44 cursor: not-allowed;
45 transform: none;
46 }
47
48 .download-status {
49 margin-top: 8px;
50 padding: 8px 12px;
51 border-radius: 6px;
52 font-size: 14px;
53 display: none;
54 }
55
56 .download-status.success {
57 background: #d4edda;
58 color: #155724;
59 display: block;
60 }
61
62 .download-status.error {
63 background: #f8d7da;
64 color: #721c24;
65 display: block;
66 }
67
68 .download-status.info {
69 background: #d1ecf1;
70 color: #0c5460;
71 display: block;
72 }
73 `);
74
75 async function extractVideoUrl(iframeSrc) {
76 try {
77 console.log('Extracting video URL from:', iframeSrc);
78
79 // Extract the video ID from the iframe URL
80 const videoIdMatch = iframeSrc.match(/embed\/\d+\/([a-f0-9-]+)/);
81 if (!videoIdMatch) {
82 throw new Error('Could not extract video ID');
83 }
84
85 const videoId = videoIdMatch[1];
86 console.log('Video ID:', videoId);
87
88 // Construct the direct video URL (Bunny CDN format)
89 const videoUrl = `https://iframe.mediadelivery.net/play/383660/${videoId}`;
90
91 return videoUrl;
92 } catch (error) {
93 console.error('Error extracting video URL:', error);
94 throw error;
95 }
96 }
97
98 async function downloadVideo(videoUrl, filename) {
99 try {
100 console.log('Downloading video from:', videoUrl);
101
102 // Use GM.xmlhttpRequest to fetch the video with proper headers
103 const response = await GM.xmlhttpRequest({
104 method: 'GET',
105 url: videoUrl,
106 responseType: 'blob',
107 headers: {
108 'Referer': window.location.href,
109 'Origin': window.location.origin
110 }
111 });
112
113 console.log('Video fetched, creating download link');
114
115 // Create a blob URL and trigger download
116 const blob = response.response;
117 const blobUrl = URL.createObjectURL(blob);
118
119 const a = document.createElement('a');
120 a.href = blobUrl;
121 a.download = filename;
122 document.body.appendChild(a);
123 a.click();
124 document.body.removeChild(a);
125
126 // Clean up the blob URL after a delay
127 setTimeout(() => URL.revokeObjectURL(blobUrl), 100);
128
129 console.log('Download initiated successfully');
130 return true;
131 } catch (error) {
132 console.error('Error downloading video:', error);
133 throw error;
134 }
135 }
136
137 function createDownloadButton() {
138 console.log('Creating download button');
139
140 // Find the video title container
141 const titleContainer = document.querySelector('.text-2xl.sm\\:text-3xl.font-bold.mb-4');
142 if (!titleContainer) {
143 console.error('Could not find title container');
144 return;
145 }
146
147 const videoTitle = titleContainer.textContent.trim();
148 console.log('Video title:', videoTitle);
149
150 // Create download button container
151 const buttonContainer = document.createElement('div');
152 buttonContainer.style.cssText = 'margin-top: 16px;';
153
154 // Create download button
155 const downloadBtn = document.createElement('button');
156 downloadBtn.className = 'video-download-btn';
157 downloadBtn.innerHTML = `
158 <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
159 <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
160 <polyline points="7 10 12 15 17 10"></polyline>
161 <line x1="12" y1="15" x2="12" y2="3"></line>
162 </svg>
163 Download Video
164 `;
165
166 // Create status message element
167 const statusMsg = document.createElement('div');
168 statusMsg.className = 'download-status';
169
170 buttonContainer.appendChild(downloadBtn);
171 buttonContainer.appendChild(statusMsg);
172
173 // Insert after the title
174 titleContainer.parentElement.insertBefore(buttonContainer, titleContainer.nextSibling);
175
176 // Add click handler
177 downloadBtn.addEventListener('click', async () => {
178 try {
179 downloadBtn.disabled = true;
180 downloadBtn.textContent = 'Preparing download...';
181 statusMsg.className = 'download-status info';
182 statusMsg.textContent = 'Extracting video URL...';
183
184 // Get iframe source
185 const iframe = document.querySelector('iframe');
186 if (!iframe || !iframe.src) {
187 throw new Error('Video iframe not found');
188 }
189
190 const videoUrl = await extractVideoUrl(iframe.src);
191
192 statusMsg.textContent = 'Starting download...';
193
194 // Generate filename from video title
195 const filename = videoTitle.replace(/[^a-z0-9]/gi, '_').toLowerCase() + '.mp4';
196
197 // Try to open the video URL in a new tab as a fallback
198 // This allows the user to download it manually if direct download fails
199 GM.openInTab(videoUrl, false);
200
201 statusMsg.className = 'download-status success';
202 statusMsg.textContent = 'Video opened in new tab! Right-click and select "Save video as..." to download.';
203
204 downloadBtn.disabled = false;
205 downloadBtn.innerHTML = `
206 <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
207 <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
208 <polyline points="7 10 12 15 17 10"></polyline>
209 <line x1="12" y1="15" x2="12" y2="3"></line>
210 </svg>
211 Download Video
212 `;
213
214 } catch (error) {
215 console.error('Download failed:', error);
216 statusMsg.className = 'download-status error';
217 statusMsg.textContent = 'Download failed: ' + error.message;
218
219 downloadBtn.disabled = false;
220 downloadBtn.innerHTML = `
221 <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
222 <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
223 <polyline points="7 10 12 15 17 10"></polyline>
224 <line x1="12" y1="15" x2="12" y2="3"></line>
225 </svg>
226 Download Video
227 `;
228 }
229 });
230
231 console.log('Download button created successfully');
232 }
233
234 function init() {
235 console.log('Initializing Video Downloader');
236
237 // Wait for the page to be fully loaded
238 if (document.readyState === 'loading') {
239 document.addEventListener('DOMContentLoaded', () => {
240 setTimeout(createDownloadButton, 1000);
241 });
242 } else {
243 setTimeout(createDownloadButton, 1000);
244 }
245 }
246
247 init();
248})();