您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Improvements for working with groups of tags + increased efficiency of new requests creation
当前为
- // ==UserScript==
- // @name [GMT] Tags Helper
- // @version 1.01.5
- // @author Anakunda
- // @copyright 2021, Anakunda (https://greasyfork.org/users/321857-anakunda)
- // @license GPL-3.0-or-later
- // @namespace https://greasyfork.org/users/321857-anakunda
- // @run-at document-end
- // @match https://*/artist.php?id=*
- // @match https://*/artist.php?*&id=*
- // @match https://*/requests.php
- // @match https://*/requests.php?submit=true&*
- // @match https://*/requests.php?type=*
- // @match https://*/requests.php?page=*
- // @match https://*/requests.php?action=new*
- // @match https://*/requests.php?action=view&id=*
- // @match https://*/requests.php?action=view&*&id=*
- // @match https://*/requests.php?action=edit&id=*
- // @match https://*/torrents.php?id=*
- // @match https://*/torrents.php
- // @match https://*/torrents.php?action=advanced
- // @match https://*/torrents.php?action=advanced&*
- // @match https://*/torrents.php?*&action=advanced
- // @match https://*/torrents.php?*&action=advanced&*
- // @match https://*/torrents.php?action=basic
- // @match https://*/torrents.php?action=basic&*
- // @match https://*/torrents.php?*&action=basic
- // @match https://*/torrents.php?*&action=basic&*
- // @match https://*/torrents.php?page=*
- // @match https://*/torrents.php?action=notify
- // @match https://*/torrents.php?action=notify&*
- // @match https://*/torrents.php?type=*
- // @match https://*/collages.php?id=*
- // @match https://*/collages.php?page=*&id=*
- // @match https://*/collages.php?action=new
- // @match https://*/collages.php?action=edit&collageid=*
- // @match https://*/bookmarks.php?type=*
- // @match https://*/bookmarks.php?page=*
- // @match https://*/upload.php
- // @match https://*/upload.php?url=*
- // @match https://*/upload.php?tags=*
- // @match https://*/bookmarks.php?type=torrents
- // @match https://*/bookmarks.php?page=*&type=torrents
- // @match https://*/top10.php
- // @match https://*/top10.php?*
- // @grant GM_getValue
- // @grant GM_setClipboard
- // @grant GM_registerMenuCommand
- // @grant GM_getValue
- // @grant GM_setValue
- // @grant GM_deleteValue
- // @require https://openuserjs.org/src/libs/Anakunda/libLocks.min.js
- // @require https://openuserjs.org/src/libs/Anakunda/gazelleApiLib.min.js
- // @require https://openuserjs.org/src/libs/Anakunda/QobuzLib.min.js
- // @require https://openuserjs.org/src/libs/Anakunda/GazelleTagManager.min.js
- // @description Improvements for working with groups of tags + increased efficiency of new requests creation
- // ==/UserScript==
- (function() {
- 'use strict';
- const hasStyleSheet = styleSheet => document.styleSheetSets && document.styleSheetSets.contains(styleSheet);
- const isLightTheme = ['postmod', 'shiro', 'layer_cake', 'proton', 'red_light'].some(hasStyleSheet);
- const isDarkTheme = ['kuro', 'minimal', 'red_dark'].some(hasStyleSheet);
- const uriTest = /^(https?:\/\/.+)$/i;
- const isFirefox = /\b(?:Firefox)\b/.test(navigator.userAgent) || Boolean(window.InstallTrigger);
- const fieldNames = ['tags', 'tagname', 'taglist'];
- const exclusions = GM_getValue('exclusions', [
- '/^(?:\\d{4}s)$/i',
- '/^(?:delete\.this\.tag)$/i',
- ]).map(function(expr) {
- const m = /^\/(.+)\/([dgimsuy]*)$/.exec(expr);
- if (m != null) return new RegExp(m[1], m[2]);
- }).filter(it => it instanceof RegExp);
- const getTagsFromIterable = iterable => Array.from(iterable)
- .filter(elem => elem.offsetWidth > 0 && elem.offsetHeight > 0 && elem.pathname && elem.search
- && fieldNames.some(URLSearchParams.prototype.has.bind(new URLSearchParams(elem.search))))
- .map(elem => elem.textContent.trim())
- .filter(tag => /^([a-z\d\.]+)$/.test(tag) && !exclusions.some(rx => rx.test(tag)));
- const contextId = 'cae67c72-9aa7-4b96-855e-73cb23f5c7f8';
- let menuHooks = 0, menuInvoker;
- function createMenu() {
- const menu = document.createElement('menu');
- menu.type = 'context';
- menu.id = contextId;
- menu.className = 'tags-helper';
- function addMenuItem(label, callback) {
- if (label) {
- let menuItem = document.createElement('MENUITEM');
- menuItem.label = label;
- if (typeof callback == 'function') menuItem.onclick = callback;
- menu.append(menuItem);
- }
- return menu.children.length;
- }
- addMenuItem('Copy tags to clipboard', function(evt) {
- console.assert(menuInvoker instanceof HTMLElement, 'menuInvoker instanceof HTMLElement')
- if (!(menuInvoker instanceof HTMLElement)) throw 'Invalid invoker';
- const tags = getTagsFromIterable(menuInvoker.getElementsByTagName('A'));
- if (tags.length > 0) GM_setClipboard(tags.join(', '), 'text');
- });
- addMenuItem('Make new request using these tags', function(evt) {
- console.assert(menuInvoker instanceof HTMLElement, 'menuInvoker instanceof HTMLElement')
- if (!(menuInvoker instanceof HTMLElement)) throw 'Invalid invoker';
- const tags = getTagsFromIterable(menuInvoker.getElementsByTagName('A'));
- if (tags.length > 0) document.location.assign('/requests.php?' + new URLSearchParams({
- action: 'new',
- tags: JSON.stringify(tags),
- }).toString());
- });
- addMenuItem('Make new upload using these tags', function(evt) {
- console.assert(menuInvoker instanceof HTMLElement, 'menuInvoker instanceof HTMLElement')
- if (!(menuInvoker instanceof HTMLElement)) throw 'Invalid invoker';
- const tags = getTagsFromIterable(menuInvoker.getElementsByTagName('A'));
- if (tags.length > 0) document.location.assign('/upload.php?' + new URLSearchParams({
- tags: JSON.stringify(tags),
- }).toString());
- });
- document.body.append(menu);
- }
- function setElemHandlers(elem, textCallback) {
- console.assert(elem instanceof HTMLElement);
- elem.addEventListener('click', function(evt) {
- if (evt.altKey) evt.preventDefault(); else return;
- const tags = getTagsFromIterable(evt.currentTarget.getElementsByTagName('A'));
- if (tags.length > 0) if (evt.ctrlKey) document.location.assign('/requests.php?' + new URLSearchParams({
- action: 'new',
- tags: JSON.stringify(tags)
- }).toString()); else if (evt.shiftKey) document.location.assign('/upload.php?' + new URLSearchParams({
- tags: JSON.stringify(tags)
- }).toString()); else {
- GM_setClipboard(tags.join(', '), 'text');
- evt.currentTarget.style.backgroundColor = isDarkTheme ? 'darkgreen' : 'lightgreen';
- setTimeout(elem => { elem.style.backgroundColor = null }, 1000, evt.currentTarget);
- }
- return false;
- });
- elem.ondragover = evt => false;
- elem.ondragenter = evt => { evt.currentTarget.style.backgroundColor = 'lawngreen' };
- elem[isFirefox ? 'ondragexit' : 'ondragleave'] = evt => { evt.currentTarget.style.backgroundColor = null };
- elem.draggable = true;
- elem.ondragstart = function(evt) {
- //evt.dataTransfer.clearData('text/uri-list');
- //evt.dataTransfer.clearData('text/x-moz-url');
- evt.dataTransfer.setData('text/plain',
- getTagsFromIterable(evt.currentTarget.getElementsByTagName('A')).join(', '));
- console.debug(evt.currentTarget, evt.currentTarget.getElementsByTagName('A'),
- getTagsFromIterable(evt.currentTarget.getElementsByTagName('A')));
- };
- elem.ondrop = function(evt) {
- evt.stopPropagation();
- let links = evt.dataTransfer.getData('text/uri-list');
- if (links) links = links.split(/\r?\n/); else {
- links = evt.dataTransfer.getData('text/x-moz-url');
- if (links) links = links.split(/\r?\n/).filter((item, ndx) => ndx % 2 == 0);
- else if (links = evt.dataTransfer.getData('text/plain'))
- links = links.split(/\r?\n/).filter(RegExp.prototype.test.bind(uriTest));
- }
- if (Array.isArray(links) && links.length > 0) {
- const tags = getTagsFromIterable(evt.currentTarget.getElementsByTagName('A'));
- if (tags.length > 0) if (evt.shiftKey) document.location.assign('/upload.php?' + new URLSearchParams({
- url: links[0],
- tags: JSON.stringify(tags),
- }).toString()); else document.location.assign('/requests.php?' + new URLSearchParams({
- action: 'new',
- url: links[0],
- tags: JSON.stringify(tags),
- }).toString());
- } else if (typeof textCallback == 'function' && (links = evt.dataTransfer.getData('text/plain'))
- //&& (links = links.split(/[\r\n\,\;\|\>]+/).map(expr => expr.trim()).filter(Boolean)).length > 0
- && (links = new TagManager(links)).length > 0) textCallback(evt, links);
- evt.currentTarget.style.backgroundColor = null;
- return false;
- };
- elem.setAttribute('contextmenu', contextId);
- elem.oncontextmenu = evt => { menuInvoker = evt.currentTarget };
- elem.style.cursor = 'context-menu';
- ++menuHooks;
- elem.title = `Alt + click => copy tags to clipboard
- Ctrl + Alt + click => make new request using these tags
- Shift + Alt + click => make new upload using these tags
- ---
- Drag & drop active link here => make new request using these tags
- Shift + Drag & drop active link here => make new upload using these tags
- Drag this tags area and drop to any text input to get inserted all tags as comma-separated list
- --or-- use context menu (older browsers only)`;
- }
- switch (document.location.pathname) {
- case '/torrents.php': {
- if (!new URLSearchParams(document.location.search).has('id')) break;
- const urlParams = new URLSearchParams(document.location.search);
- try {
- let tags = urlParams.get('tags');
- if (tags && (tags = JSON.parse(tags)).length > 0) {
- const input = document.getElementById('tagname');
- if (input == null) throw 'Tags input not found';
- tags = new TagManager(...tags);
- input.value = tags.toString();
- input.scrollIntoView({ behavior: 'smooth', block: 'start' });
- //if (input.nextElementSibling != null) input.nextElementSibling.click();
- }
- } catch(e) { }
- break;
- }
- case '/requests.php':
- case '/upload.php': {
- const urlParams = new URLSearchParams(document.location.search);
- try {
- let tags = urlParams.get('tags');
- if (tags && (tags = JSON.parse(tags)).length > 0) {
- const input = document.getElementById('tags');
- if (input == null) throw 'Tags input not found';
- tags = new TagManager(...tags);
- input.value = tags.toString();
- }
- } catch(e) { }
- const url = urlParams.get('url');
- if (uriTest.test(url)) {
- let ua = document.getElementById('ua-data');
- function feedData() {
- ua.value = url;
- if ((ua = document.getElementById('autofill-form-2')) == null) return; // assertion failed
- if (typeof ua.onclick == 'function') ua.onclick(); else ua.click();
- }
- if (ua != null) feedData(); else {
- const container = document.querySelector('form#request_form > table > tbody');
- if (container != null) {
- let counters = [0, 0], timeStamp = Date.now();
- const mo = new MutationObserver(function(mutationsList) {
- ++counters[0];
- for (let mutation of mutationsList) for (let node of mutation.addedNodes) {
- ++counters[1];
- if (node.nodeName != 'TR' || (ua = node.querySelector('textarea#ua-data')) == null) continue;
- console.log('Found UA data by trigger:', counters, (Date.now() - timeStamp) / 1000);
- clearTimeout(timer);
- return feedData();
- }
- }), timer = setTimeout(mo => { mo.disconnect() }, 10000, mo);
- mo.observe(container, { childList: true });
- }
- }
- }
- break;
- }
- }
- document.body.querySelectorAll('div.tags').forEach(div => { setElemHandlers(div, function(evt, tags) {
- const a = evt.currentTarget.parentNode.querySelector('a[href^="torrents.php?id="]');
- if (a == null) return false;
- if (evt.ctrlKey && ajaxApiKey) {
- const tagsElement = evt.currentTarget, groupId = parseInt(new URLSearchParams(a.search).get('id')) || undefined;
- if (groupId) queryAjaxAPI('addtag', { groupid: groupId }, { tagname: tags.toString() }).then(function(response) {
- console.log(response);
- if (!['added', 'voted'].some(key => response[key].length > 0)) return;
- queryAjaxAPI('torrentgroup', { id: groupId }).then(function(response) {
- if (!response.group.tags) {
- document.location.reload();
- return;
- }
- const urlParams = new URLSearchParams(tagsElement.childElementCount > 0 ? tagsElement.children[0].search : {
- action: 'advanced',
- searchsubmit: 1,
- });
- while (tagsElement.childNodes.length > 0) tagsElement.removeChild(tagsElement.childNodes[0]);
- for (let tag of response.group.tags) {
- if (tagsElement.childElementCount > 0) tagsElement.append(', ');
- const a = document.createElement('A');
- for (let param of fieldNames) if (urlParams.has(param)) urlParams.set(param, a.textContent = tag);
- a.setAttribute('href', 'torrents.php?' + urlParams.toString());
- tagsElement.append(a);
- }
- });
- });
- } else document.location.assign(a.href + '&tags=' + encodeURIComponent(JSON.stringify(tags)));
- }) });
- (function() {
- const tagsBox = document.body.querySelector('div.box_tags');
- if (tagsBox != null) setElemHandlers(tagsBox, function(evt, tags) {
- function fallBack() {
- const input = document.getElementById('tagname');
- if (input == null) throw 'Tags input not found';
- input.value = tags.toString();
- input.scrollIntoView({ behavior: 'smooth', block: 'start' });
- //if (input.nextElementSibling != null) input.nextElementSibling.click();
- }
- const groupId = document.location.pathname == '/torrents.php'
- && parseInt(new URLSearchParams(document.location.search).get('id')) || undefined;
- if (!groupId) return fallBack();
- if (ajaxApiKey) {
- const tagsElement = evt.currentTarget.querySelector('ul') || evt.currentTarget.querySelector('ol');
- if (tagsElement != null) queryAjaxAPI('addtag', { groupid: groupId }, { tagname: tags.toString() }).then(function(response) {
- console.log(response);
- if (['added', 'voted'].some(key => response[key].length > 0)) document.location.reload();
- // queryAjaxAPI('torrentgroup', { id: groupId }).then(function(response) {
- // if (!response.group.tags) {
- // document.location.reload();
- // return;
- // }
- // let a = tagsElement.querySelector('li > a');
- // const urlParams = new URLSearchParams(a != null ? a.search : undefined);
- // while (tagsElement.childNodes.length > 0) tagsElement.removeChild(tagsElement.childNodes[0]);
- // for (let tag of response.group.tags) {
- // urlParams.set('taglist', (a = document.createElement('A')).textContent = tag);
- // a.setAttribute('href', 'torrents.php?' + encodeURIComponent(urlParams.toString()));
- // const li = document.createElement('LI');
- // li.append(a);
- // tagsElement.append(li);
- // }
- // });
- }); else fallBack();
- } else fallBack();
- }); else return;
- const head = tagsBox.querySelector('div.head');
- if (head == null) return;
- const span = document.createElement('SPAN'), a = document.createElement('A');
- span.style.float = 'right';
- a.className = 'brackets';
- a.textContent = 'Copy';
- a.href = '#';
- a.onclick = function(evt) {
- let tags = getTagsFromIterable(tagsBox.querySelectorAll('* > li > a'));
- if (tags.length <= 0) return false;
- GM_setClipboard(tags.join(', '), 'text');
- evt.currentTarget.style.color = isDarkTheme ? 'darkgreen' : 'lightgreen';
- setTimeout(elem => {elem.style.color = null }, 1000, evt.currentTarget);
- return false;
- };
- span.append(a);
- head.append(span);
- })();
- if (menuHooks > 0) createMenu();
- function inputDataHandler(evt) {
- switch (evt.type) {
- case 'paste': var tags = evt.clipboardData; break;
- case 'drop': tags = evt.dataTransfer; break;
- }
- if (tags) tags = tags.getData('text/plain'); else return;
- //if (tags) tags = tags.split(/[\r\n\;\|\>]+|,(?:\s*&)?/).map(expr => expr.trim()).filter(Boolean); else return;
- if (tags.length > 0) switch (evt.type) {
- case 'paste': tags = new TagManager(tags); break;
- case 'drop': tags = new TagManager(evt.currentTarget.value, tags); break;
- } else return;
- if (tags.length > 0) tags = tags.toString(); else return;
- evt.stopPropagation();
- switch (evt.type) {
- case 'paste': {
- const cursor = evt.currentTarget.selectionStart + tags.length;
- evt.currentTarget.value = evt.currentTarget.value.slice(0, evt.currentTarget.selectionStart) +
- tags + evt.currentTarget.value.slice(evt.currentTarget.selectionEnd);
- evt.currentTarget.selectionEnd = evt.currentTarget.selectionStart = cursor;
- break;
- }
- case 'drop': evt.currentTarget.value = tags; break;
- }
- return false;
- }
- for (let input of document.body.querySelectorAll(fieldNames.map(name => `input[name="${name}"]`).join(', ')))
- input.onpaste = input.ondrop = inputDataHandler;
- })();