您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Click on collage size = browse through this collage; Alt + click on collage name = remove from this collage
当前为
// ==UserScript== // @name Torrent Collage Extensions for Gazelle // @version 1.11 // @description Click on collage size = browse through this collage; Alt + click on collage name = remove from this collage // @author Anakunda // @license GPL-3.0-or-later // @copyright 2020, Anakunda (https://openuserjs.org/users/Anakunda) // @namespace https://gf.qytechs.cn/users/321857-anakunda // @match https://*/torrents.php?id=* // @match https://*/collages.php?*id=* // @grant GM_getValue // ==/UserScript== 'use strict'; var auth = document.querySelector('input[name="auth"][value]'); if (auth != null) auth = auth.value; else { auth = document.querySelector('li#nav_logout > a'); if (auth != null && /\b(?:auth)=(\w+)\b/.test(auth.search)) auth = RegExp.$1; else throw 'Auth not found'; } const siteApiTimeframeStorageKey = document.location.hostname + ' API time frame'; const gazelleApiFrame = 10500; if (typeof GM_getValue == 'function') var redacted_api_key = GM_getValue('redacted_api_key'); function queryAjaxAPI(action, params) { if (!action) return Promise.reject('Action missing'); var retryCount = 0; return new Promise(function(resolve, reject) { params = new URLSearchParams(params || undefined); params.set('action', action); let url = '/ajax.php?' + params, xhr = new XMLHttpRequest; queryInternal(); function queryInternal() { var now = Date.now(); try { var apiTimeFrame = JSON.parse(window.localStorage[siteApiTimeframeStorageKey]) } catch(e) { apiTimeFrame = {} } if (!apiTimeFrame.timeStamp || now > apiTimeFrame.timeStamp + gazelleApiFrame) { apiTimeFrame.timeStamp = now; apiTimeFrame.requestCounter = 1; } else ++apiTimeFrame.requestCounter; window.localStorage[siteApiTimeframeStorageKey] = JSON.stringify(apiTimeFrame); if (apiTimeFrame.requestCounter <= 5) { xhr.open('GET', url, true); xhr.setRequestHeader('Accept', 'application/json'); if (redacted_api_key) xhr.setRequestHeader('Authorization', redacted_api_key); xhr.responseType = 'json'; //xhr.timeout = 5 * 60 * 1000; xhr.onload = function() { if (xhr.status == 404) return reject('not found'); if (xhr.status < 200 || xhr.status >= 400) return reject(defaultErrorHandler(xhr)); if (xhr.response.status == 'success') return resolve(xhr.response.response); if (xhr.response.error == 'not found') return reject(xhr.response.error); console.warn('queryAjaxAPI.queryInternal(...) response:', xhr, xhr.response); if (xhr.response.error == 'rate limit exceeded') { console.warn('queryAjaxAPI.queryInternal(...) ' + xhr.response.error + ':', apiTimeFrame, now, retryCount); if (retryCount++ <= 10) return setTimeout(queryInternal, apiTimeFrame.timeStamp + gazelleApiFrame - now); } reject('API ' + xhr.response.status + ': ' + xhr.response.error); }; xhr.onerror = function() { reject(defaultErrorHandler(xhr)) }; xhr.ontimeout = function() { reject(defaultTimeoutHandler(xhr)) }; xhr.send(); } else { setTimeout(queryInternal, apiTimeFrame.timeStamp + gazelleApiFrame - now); console.debug('AJAX API request quota exceeded: /ajax.php?action=' + action + ' (' + apiTimeFrame.requestCounter + ')'); } } }); } function addToTorrentCollage(collageId, groupId) { return (function() { if (!collageId) return Promise.reject('collage not defined'); if (!groupId) return Promise.reject('group not defined'); return queryAjaxAPI('collage', { id: collageId }).then(function(collage) { return !collage.torrentGroupIDList.map(parseInt).includes(groupId) ? collageId : Promise.reject('already in collage'); }); })().then(collageId => new Promise(function(resolve, reject) { let form = new URLSearchParams({ auth: auth, collageid: collageId, }); if (groupId) { form.set('action', 'add_torrent'); form.set('url', document.location.origin.concat('/torrents.php?id=', groupId)); form.set('groupid', groupId); } let xhr = new XMLHttpRequest(); xhr.open('POST', '/collages.php', true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.onreadystatechange = function() { if (xhr.readyState < XMLHttpRequest.HEADERS_RECEIVED) return; if (xhr.status >= 200 && xhr.status < 400) resolve(collageId); else reject(defaultErrorHandler(xhr)); xhr.abort(); }; xhr.timeout = timeout; xhr.onerror = function() { reject(defaultErrorHandler(xhr)) }; xhr.ontimeout = function() { reject(defaultTimeoutHandler(xhr)) }; xhr.send(form.toString()); })).then(collageId => queryAjaxAPI('collage', { id: collageId }).then(function(collage) { return collage.torrentGroupIDList.map(parseInt).includes(groupId) ? collage : Promise.reject('Error: not added for unknown reason'); })); } function addToArtistCollage(collageId, artistId) { return (function() { if (!collageId) return Promise.reject('collage not defined'); if (!artistId) return Promise.reject('artist not defined'); return queryAjaxAPI('collage', { id: collageId }).then(function(collage) { return !collage.artists.map(parseInt).includes(artistId) ? collageId : Promise.reject('already in collage'); }); })().then(collageId => new Promise(function(resolve, reject) { let form = new URLSearchParams({ auth: auth, collageid: collageId, }); if (artistId) { form.set('action', 'add_artist'); form.set('url', document.location.origin.concat('/artist.php?id=', artistId)); form.set('artistid', artistId); } let xhr = new XMLHttpRequest(); xhr.open('POST', '/collages.php', true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.onreadystatechange = function() { if (xhr.readyState < XMLHttpRequest.HEADERS_RECEIVED) return; if (xhr.status >= 200 && xhr.status < 400) resolve(collageId); else reject(defaultErrorHandler(xhr)); xhr.abort(); }; xhr.timeout = timeout; xhr.onerror = function() { reject(defaultErrorHandler(xhr)) }; xhr.ontimeout = function() { reject(defaultTimeoutHandler(xhr)) }; xhr.send(form.toString()); })).then(collageId => queryAjaxAPI('collage', { id: collageId }).then(function(collage) { return collage.artists.map(parseInt).includes(artistId) ? collage : Promise.reject('Error: not added for unknown reason'); })); } function removeFromCollage(collageId, groupId, question) { if (!confirm(question)) return Promise.reject('Cancelled'); return new Promise(function(resolve, reject) { let xhr = new XMLHttpRequest, params = new URLSearchParams({ action: 'manage_handle', collageid: collageId, groupid: groupId, auth: auth, submit: 'Remove', }); xhr.open('POST', '/collages.php', true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.onreadystatechange = function() { if (xhr.readyState < XMLHttpRequest.HEADERS_RECEIVED) return; if (xhr.status >= 200 && xhr.status < 400) resolve(xhr.status); else reject(defaultErrorHandler(xhr)); xhr.abort(); }; xhr.onerror = function() { reject(defaultErrorHandler(xhr)) }; xhr.ontimeout = function() { reject(defaultTimeoutHandler(xhr)) }; xhr.send(params); }); } function defaultErrorHandler(response) { console.error('HTTP error:', response); var e = 'HTTP error ' + response.status; if (response.statusText) e += ' (' + response.statusText + ')'; if (response.error) e += ' (' + response.error + ')'; return e; } function defaultTimeoutHandler(response) { console.error('HTTP timeout:', response); const e = 'HTTP timeout'; return e; } switch (document.location.pathname) { case '/torrents.php': { const searchforcollage = document.getElementById('searchforcollage'); if (searchforcollage != null) { if (typeof SearchCollage == 'function') SearchCollage = () => { const searchTerm = $('#searchforcollage').val(), personalCollages = $('#personalcollages'); ajax.get(`ajax.php?action=collages&search=${encodeURIComponent(searchTerm)}`, responseText => { const { response, status } = JSON.parse(responseText); if (status !== 'success') return; const categories = response.reduce((accumulator, item) => { const { collageCategoryName } = item; accumulator[collageCategoryName] = (accumulator[collageCategoryName] || []).concat(item); return accumulator; }, {}); personalCollages.children().remove(); Object.entries(categories).forEach(([category, collages]) => { console.log(collages); personalCollages.append(` <optgroup label="${category}"> ${collages.reduce((accumulator, { id, name }) => `${accumulator}<option value="${id}">${name}</option>` ,'')} </optgroup> `); }); }); }; function inputHandler(evt, key) { const data = evt[key].getData('text/plain').trim(); if (!data) return true; evt.target.value = data; SearchCollage(); setTimeout(function() { const add_to_collage_select = document.querySelector('select.add_to_collage_select'); if (add_to_collage_select != null && add_to_collage_select.options.length > 1) { // TODO: expand } }, 3000); return false; } searchforcollage.onpaste = evt => inputHandler(evt, 'clipboardData'); searchforcollage.ondrop = evt => inputHandler(evt, 'dataTransfer'); } let torrentId = new URLSearchParams(document.location.search).get('id'), collages; if (torrentId) torrentId = parseInt(torrentId); else throw 'Unexpected URL format'; try { collages = JSON.parse(window.sessionStorage.collages) } catch(e) { collages = { } } if (!collages[document.domain]) collages[document.domain] = {}; document.querySelectorAll('table[id$="collages"] > tbody > tr > td > a').forEach(function(link) { if (!link.pathname.startsWith('/collages.php') || !/\b(?:id)=(\d+)\b/.test(link.search)) return; let collageId = parseInt(RegExp.$1), toggle, navLinks = [], numberColumn = link.parentNode.parentNode.querySelector('td.number_column'); link.onclick = function(evt) { return evt.button == 0 && evt.altKey ? removeFromCollage(collageId, torrentId, 'Are you sure to remove this group from collage "' + link.textContent.trim() + '"?') .then(status => { link.parentNode.parentNode.remove() }) : true; }; link.title = 'Use Alt + left click to remove from this collage'; if (numberColumn != null) { numberColumn.style.cursor = 'pointer'; numberColumn.onclick = loadCollage; numberColumn.title = collages[document.domain][collageId] ? 'Refresh' : 'Load collage for direct browsing'; } if (collages[document.domain][collageId]) { expandSection(); addCollageLinks(collages[document.domain][collageId]); } function addCollageLinks(collage) { var index = collage.torrentgroups.findIndex(group => group.id == torrentId); if (index < 0) { console.warn('Assertion failed: torrent', torrentId, 'not found in the collage', collage); return false; } link.style.color = 'white'; link.parentNode.parentNode.style = 'color:white; background-color: darkgoldenrod;'; var stats = document.createElement('span'); stats.textContent = `${index + 1} / ${collage.torrentgroups.length}`; stats.style = 'font-size: 8pt; color: antiquewhite; font-weight: 100; margin-left: 10px;'; navLinks.push(stats); link.parentNode.append(stats); if (collage.torrentgroups[index - 1]) { var a = document.createElement('a'); a.href = '/torrents.php?id=' + collage.torrentgroups[index - 1].id; a.textContent = '[\xA0<\xA0]'; a.title = getTitle(index - 1); a.style = 'color: chartreuse; margin-right: 10px;'; navLinks.push(a); link.parentNode.prepend(a); a = document.createElement('a'); a.href = '/torrents.php?id=' + collage.torrentgroups[0].id; a.textContent = '[\xA0<<\xA0]'; a.title = getTitle(0); a.style = 'color: chartreuse; margin-right: 5px;'; navLinks.push(a); link.parentNode.prepend(a); } if (collage.torrentgroups[index + 1]) { a = document.createElement('a'); a.href = '/torrents.php?id=' + collage.torrentgroups[index + 1].id; a.textContent = '[\xA0>\xA0]'; a.title = getTitle(index + 1); a.style = 'color: chartreuse; margin-left: 10px;'; navLinks.push(a); link.parentNode.append(a); a = document.createElement('a'); a.href = '/torrents.php?id=' + collage.torrentgroups[collage.torrentgroups.length - 1].id; a.textContent = '[\xA0>>\xA0]'; a.title = getTitle(collage.torrentgroups.length - 1); a.style = 'color: chartreuse; margin-left: 5px;'; navLinks.push(a); link.parentNode.append(a); } return true; function getTitle(index) { if (typeof index != 'number' || index < 0 || index >= collage.torrentgroups.length) return undefined; let title = collage.torrentgroups[index].musicInfo && Array.isArray(collage.torrentgroups[index].musicInfo.artists) ? collage.torrentgroups[index].musicInfo.artists.map(artist => artist.name).join(', ') + ' - ' : ''; if (collage.torrentgroups[index].name) title += collage.torrentgroups[index].name; if (collage.torrentgroups[index].year) title += ' (' + collage.torrentgroups[index].year + ')'; return title; } } function expandSection() { if (toggle === undefined) toggle = link.parentNode.parentNode.parentNode.querySelector('td > a[href="#"][onclick]'); if (toggle === null || toggle.dataset.expanded) return false; toggle.dataset.expanded = true; toggle.click(); return true; } function loadCollage(evt) { evt.target.disabled = true; navLinks.forEach(a => { a.remove() }); navLinks = []; var span = document.createElement('span'); span.textContent = '[\xA0loading...\xA0]'; span.style = 'color: red; background-color: white; margin-left: 10px;'; link.parentNode.append(span); queryAjaxAPI('collage', { id: collageId }).then(function(collage) { span.remove(); cacheCollage(collage); addCollageLinks(collage); evt.target.disabled = false; }, function(reason) { span.remove(); evt.target.disabled = false; }); return false; } }); function cacheCollage(collage) { collages[document.domain][collage.id] = { id: collage.id, name: collage.name, torrentgroups: collage.torrentgroups.map(group => ({ id: group.id, musicInfo: group.musicInfo ? { artists: Array.isArray(group.musicInfo.artists) ? group.musicInfo.artists.map(artist => ({ name: artist.name })) : undefined, } : undefined, name: group.name, year: group.year, })), }; window.sessionStorage.collages = JSON.stringify(collages); } break; } case '/collages.php': { let collageId = /\b(?:id)=(\d+)\b/i.exec(document.location.search); if (collageId != null) collageId = parseInt(collageId[1]); else throw 'Collage id missing'; const sel = [ 'tr.group > td[colspan] > strong > a[href^="torrents.php?id="]', 'ul.collage_images > li > a[href^="torrents.php?id="]', ]; function scanPage() { document.querySelectorAll(sel.join(', ')).forEach(function(a) { a.onclick = function(evt) { if (evt.button != 0 || !evt.altKey) return true; let torrentId = new URLSearchParams(a.search); torrentId = torrentId.get('id'); if (torrentId) torrentId = parseInt(torrentId); else { console.warn('Assertion failed: no id', a); throw 'no id'; } removeFromCollage(collageId, torrentId, 'Are you sure to remove selected group from this collage?').then(function(status) { document.querySelectorAll(sel.join(', ')).forEach(function(a) { if (parseInt(new URLSearchParams(a.search).get('id')) == torrentId) switch (a.parentNode.nodeName) { case 'STRONG': a.parentNode.parentNode.parentNode.remove(); break; case 'LI': a.parentNode.remove(); break; } }); }); }; }); } scanPage(); document.querySelectorAll('div#pageslinksdiv > span > a.pageslink') .forEach(a => { a.addEventListener('click', evt => { setTimeout(scanPage, 1000) }) }); break; } }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址