Torrenting.com Thumbnail Viewer

Adds preview thumbnails to torrent listings from detail pages

Size

8.6 KB

Version

1.2.5

Created

Jan 17, 2026

Updated

18 days ago

1// ==UserScript==
2// @name		Torrenting.com Thumbnail Viewer
3// @description		Adds preview thumbnails to torrent listings from detail pages
4// @version		1.2.5
5// @match		https://*.torrenting.com/*
6// @icon		https://www.torrenting.com/T-favicon.ico
7// ==/UserScript==
8(function() {
9    'use strict';
10
11    console.log('Torrenting.com Thumbnail Viewer loaded');
12
13    // Cache for storing fetched thumbnails
14    const thumbnailCache = {};
15
16    // Add styles for thumbnails
17    TM_addStyle(`
18        .thumbnail-container {
19            display: inline-block;
20            margin-right: 10px;
21            vertical-align: middle;
22            position: relative;
23        }
24        
25        .thumbnail-img {
26            width: 100%;
27            height: auto;
28            max-width: 350px;
29            object-fit: contain;
30            border-radius: 4px;
31            box-shadow: 0 2px 4px rgba(0,0,0,0.3);
32            cursor: pointer;
33            transition: transform 0.2s;
34            display: block;
35        }
36        
37        .thumbnail-img:hover {
38            transform: scale(1.05);
39            z-index: 1000;
40            box-shadow: 0 4px 12px rgba(0,0,0,0.5);
41        }
42        
43        .thumbnail-loading {
44            width: 120px;
45            height: 180px;
46            background: linear-gradient(90deg, #333 25%, #444 50%, #333 75%);
47            background-size: 200% 100%;
48            animation: loading 1.5s infinite;
49            border-radius: 4px;
50        }
51        
52        @keyframes loading {
53            0% { background-position: 200% 0; }
54            100% { background-position: -200% 0; }
55        }
56        
57        .thumbnail-error {
58            width: 120px;
59            height: 180px;
60            background: #2a2a2a;
61            border: 1px solid #444;
62            border-radius: 4px;
63            display: flex;
64            align-items: center;
65            justify-content: center;
66            font-size: 10px;
67            color: #666;
68        }
69    `);
70
71    // Function to fetch thumbnail from details page
72    async function fetchThumbnail(detailsUrl, torrentId) {
73        // Check cache first
74        if (thumbnailCache[torrentId]) {
75            return thumbnailCache[torrentId];
76        }
77
78        try {
79            console.log('Fetching thumbnail for:', detailsUrl);
80            const response = await fetch(detailsUrl);
81            const html = await response.text();
82            
83            const parser = new DOMParser();
84            const doc = parser.parseFromString(html, 'text/html');
85            
86            // First, try to find screenshots in the release-content area
87            const releaseContent = doc.querySelector('.release-content');
88            if (releaseContent) {
89                const screenshots = Array.from(releaseContent.querySelectorAll('img')).filter(img => {
90                    const src = img.src || '';
91                    // Filter for actual screenshot images (not smilies or UI elements)
92                    return !src.includes('smilies') && 
93                           !src.includes('icon') && 
94                           !src.includes('avatar') &&
95                           !src.includes('button') &&
96                           src.length > 20;
97                });
98                
99                if (screenshots.length > 0) {
100                    const thumbnailUrl = screenshots[0].src;
101                    thumbnailCache[torrentId] = thumbnailUrl;
102                    console.log('Found screenshot:', thumbnailUrl);
103                    return thumbnailUrl;
104                }
105            }
106            
107            // Fallback: Look for images in main content, excluding sidebar
108            const mainContent = doc.querySelector('#mainContent');
109            const latestMov = doc.querySelector('#latestMov');
110            
111            if (mainContent) {
112                const allImages = Array.from(mainContent.querySelectorAll('img'));
113                const sidebarImages = latestMov ? Array.from(latestMov.querySelectorAll('img')) : [];
114                
115                const contentImages = allImages.filter(img => {
116                    const src = img.src || '';
117                    return !sidebarImages.includes(img) &&
118                           !src.includes('smilies') &&
119                           !src.includes('icon') && 
120                           !src.includes('avatar') && 
121                           !src.includes('logo') &&
122                           !src.includes('button') &&
123                           !src.includes('TT-Avatar') &&
124                           src.length > 20;
125                });
126                
127                if (contentImages.length > 0) {
128                    const thumbnailUrl = contentImages[0].src;
129                    thumbnailCache[torrentId] = thumbnailUrl;
130                    console.log('Found content image:', thumbnailUrl);
131                    return thumbnailUrl;
132                }
133            }
134
135            console.log('No thumbnail found for:', torrentId);
136            return null;
137        } catch (error) {
138            console.error('Error fetching thumbnail:', error);
139            return null;
140        }
141    }
142
143    // Function to add thumbnail to a torrent row
144    async function addThumbnailToRow(row) {
145        const nameCell = row.querySelector('td.browse a.nameLink');
146        if (!nameCell) return;
147
148        const detailsUrl = nameCell.href;
149        const torrentId = detailsUrl.match(/id=(\d+)/)?.[1];
150        if (!torrentId) return;
151
152        // Check if thumbnail already added
153        if (nameCell.querySelector('.thumbnail-container')) return;
154
155        // Create thumbnail container with loading state
156        const thumbnailContainer = document.createElement('span');
157        thumbnailContainer.className = 'thumbnail-container';
158        
159        const loadingDiv = document.createElement('div');
160        loadingDiv.className = 'thumbnail-loading';
161        thumbnailContainer.appendChild(loadingDiv);
162
163        // Insert thumbnail before the link text
164        nameCell.insertBefore(thumbnailContainer, nameCell.firstChild);
165
166        // Fetch and display thumbnail
167        const thumbnailUrl = await fetchThumbnail(detailsUrl, torrentId);
168        
169        if (thumbnailUrl) {
170            const img = document.createElement('img');
171            img.className = 'thumbnail-img';
172            img.src = thumbnailUrl;
173            img.alt = 'Thumbnail';
174            img.title = 'Click to view full size';
175            
176            // Replace loading with image
177            thumbnailContainer.innerHTML = '';
178            thumbnailContainer.appendChild(img);
179            
180            // Add click handler to open details page
181            img.addEventListener('click', (e) => {
182                e.preventDefault();
183                e.stopPropagation();
184                window.open(detailsUrl, '_blank');
185            });
186        } else {
187            // Show error state
188            const errorDiv = document.createElement('div');
189            errorDiv.className = 'thumbnail-error';
190            errorDiv.textContent = 'N/A';
191            thumbnailContainer.innerHTML = '';
192            thumbnailContainer.appendChild(errorDiv);
193        }
194    }
195
196    // Function to process all torrent rows
197    async function processTorrentRows() {
198        const rows = document.querySelectorAll('tr.torrentsTableTR');
199        console.log('Found', rows.length, 'torrent rows');
200
201        // Process rows in batches to avoid overwhelming the server
202        const batchSize = 5;
203        for (let i = 0; i < rows.length; i += batchSize) {
204            const batch = Array.from(rows).slice(i, i + batchSize);
205            await Promise.all(batch.map(row => addThumbnailToRow(row)));
206            
207            // Small delay between batches
208            if (i + batchSize < rows.length) {
209                await new Promise(resolve => setTimeout(resolve, 500));
210            }
211        }
212    }
213
214    // Initialize when page is ready
215    function init() {
216        // Check if we're on a browse/search page
217        if (window.location.href.includes('browse.php') || 
218            window.location.href.includes('torrents.php')) {
219            
220            console.log('On browse page, adding thumbnails...');
221            
222            // Wait for table to be ready
223            const checkTable = setInterval(() => {
224                const table = document.querySelector('#torrentsTable');
225                if (table) {
226                    clearInterval(checkTable);
227                    processTorrentRows();
228                }
229            }, 500);
230            
231            // Clear interval after 10 seconds if table not found
232            setTimeout(() => clearInterval(checkTable), 10000);
233        }
234    }
235
236    // Run when DOM is ready
237    if (document.readyState === 'loading') {
238        document.addEventListener('DOMContentLoaded', init);
239    } else {
240        init();
241    }
242})();
Torrenting.com Thumbnail Viewer | Robomonkey