您需要先安装一款用户样式管理器扩展(如 Stylus)后才能安装此样式。
您需要先安装一款用户样式管理器扩展(如 Stylus)后才能安装此样式。
您需要先安装一款用户样式管理器扩展(如 Stylus)后才能安装此样式。
您需要先安装一款用户样式管理器扩展后才能安装此样式。
您需要先安装一款用户样式管理器扩展后才能安装此样式。
您需要先安装一款用户样式管理器扩展后才能安装此样式。
(我已经安装了用户样式管理器,让我安装!)
// ==UserScript==
// @name What.CD: YAVAH
// @namespace hateradio)))
// @author hateradio
// @version 7.2
// @description Yet Another Various Artists Helper
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjY0NDMyN0UzOUEwQzExRTA4REU5OUY2M0Q5RDZGNTQ1IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjY0NDMyN0U0OUEwQzExRTA4REU5OUY2M0Q5RDZGNTQ1Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NjQ0MzI3RTE5QTBDMTFFMDhERTk5RjYzRDlENkY1NDUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NjQ0MzI3RTI5QTBDMTFFMDhERTk5RjYzRDlENkY1NDUiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6q1rvzAAAEMElEQVR42uyaP0gcQRTG12CpYBkVUkWto22CYqtiq7FWrNVervdPK5r2oq2orSiptQkE1LRqkSooBNIk+xvyLe/m9la9XDzIvoFh9mZnZme+973vvRE7enp6kjKXF3/aD2n9VbJa4eAdKQPepu2nkhJgAAa8KrEHvHyRlLw4AA6AA+AAOAAOgAPgADgADoAD4AA4AA6AA+AAOAAOgAPgADgADoAD4AA4AA6AA+AAOAAOgAPQmjI7O5scHx8nq6urWd/o6Gjoo/JeZWFhIfTRtqIsLS3VfaNtDOju7s6ep6amcvsnJiZCe3Bw0JJvjoyMhPbi4iL3fedzHPzm5ia0XV1doR0cHEyGh4eTu7u7cHj1Awq/T09Pk9vb21wAsSjsoVxeXiaVSqVmLCzT+729vWztvPXapgGiIwe1DNDGT05Ocg+/tbWVjRGQy8vL2e+1tbWa9zMzM2EeQLcVAH2czVCxPn2Hh4ehv7e3N2MF/QImBo1x5+fnyfj4eLK4uBj6BwYGMvZo/tzcXBjDWMrV1VXDvXU+p+WhIwcBBOhpgRkbGwvPAiUusuz6+npGfw4Zv2e+1mUMoNC2FQBLP0SO37u7u+E39AQYDsBzHgAAhPUp9/f3ud/Qe+mN7WtE/7ZEAYlc3gHpB4SiIsorvMXhUqoPoGLF9fV1exnAoaT4tLK+rCMRtOK3v78f2unp6TAHGqMTCJ2dq7V4D4iTk5Oh2lKkAc8eBWIri9IcQKKF3wKKpS7hTu8pPK+srGRrbWxsZO+tK7FGEav4X+H3aVstaSb87tEMIAbjc/heXppbrVZr4rnSXJv+QmH1S6Di1Jh1rDbgCvTLJeJCf/yOeUVzmnIBCRcb1UE50Pz8fBaeRDUrTIxRgeYaY/vtHMKjzeP1LRsJVOQqsY9LKIt8/8kAIDYSLImM2LC5uZn5n01YFIpsri+/7uvrq0tyAEh3AAACbMZrTgxaf39/Xegr6v9rEZSwAACHZ0P0adMWnO3t7WzjsohYYGO01tOcmBGsf3Z2VgdaUZz/JwwQC6Cwwg1WRn1jS7JpDqqNDQ0N1TFA8VpzcDGxBssrgxPz7JzYvXBDaQtVgBbF/6bD4NHRUc1NzAoPWR4A7ezshD5Zzlpb11KyP/q5sDDHWl+XJcV4zYk1wDIrrzyGAU9OhCRiWMvGVytYsfpay2lTjOUmF98LOLwsayOIAKAyVtqiy48dQyR5KP43zQBtzvqdBKvoEmQB1Fzd3mxmGGdxjXL+Rv6ft7+WMkAfsP5lQ5ilsuK4rsDycaWtEjlZykYDXXdtHsK30RPWka7ENz0JpdyvpQxQLGbDsWDFOX5sbSuE2rRETmujB9b3bdEcGaARAx66NXoq3Gwq7H8WdwD+XwB+lPj8PwHgOK3fSnj4r2n9TB7wPa1v0ko0eF2Sw39J60fY/1uAAQDAo7S7+Gt3nAAAAABJRU5ErkJggg==
// @include /https://redacted\.sh/(torrents\.php(\?|\?page=\d+&)id=\d+(&torrentid=\d+)?(#comments)?|upload\.php(\?requestid=\d+)?|requests\.php*)/
// @include /https://orpheus\.network/(torrents\.php(\?|\?page=\d+&)id=\d+(&torrentid=\d+)?(#comments)?|upload\.php(\?requestid=\d+)?|requests\.php*)/
// @include /https://notwhat\.cd/(torrents\.php(\?|\?page=\d+&)id=\d+(&torrentid=\d+)?(#comments)?|upload\.php(\?requestid=\d+)?|requests\.php*)/
// #updated 26 Nov 2024
// #since 18 Jun 2010
// ==/UserScript==
(() => {
if (!Element.prototype.matches)
Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector
if (!Element.prototype.closest) {
Element.prototype.closest = function (s) {
let el = this
if (!document.documentElement.contains(el)) return null
do {
if (el.matches(s)) return el
el = el.parentElement || el.parentNode
} while (el !== null && el.nodeType === 1)
return null
}
}
const _ = {
css: text => {
if (!this.style) {
this.style = document.createElement('style')
this.style.type = 'text/css'
document.body.appendChild(this.style)
}
this.style.appendChild(document.createTextNode(`${text}\n`))
},
js: func => {
const script = document.createElement('script')
script.type = 'application/javascript'
script.textContent = `;(${func})();`
document.body.appendChild(script)
document.body.removeChild(script)
},
debounce: (func, wait) => {
let timeout
return function (...args) {
const run = () => {
timeout = null
func.apply(this, args)
}
clearTimeout(timeout)
timeout = setTimeout(run, wait)
}
},
on: (element, type, selector, listener) => {
element.addEventListener(type, event => {
const found = event.target.closest(selector)
if (found) listener.call(found, event)
}, false)
}
}
class YavaMenu {
constructor() {
this.sibling = document.querySelector('.box_addartists, #artist_tr')
this.setup()
YavaMenu.check = document.getElementById('yavah_semi')
}
get types() {
return ['Main', 'Guest', 'Remixer', 'Composer', 'Conductor', 'DJ / Compiler', 'Producer']
}
setup() {
if (!this.sibling) return
const box = document.createElement('div')
box.id = 'YAVAH'
_.on(box, 'click', 'a', this.toggle)
this.boxSetup(box)
box.querySelector('a').click()
this.box = box
}
boxSetup(box) {
box.className = 'box'
box.innerHTML = `
<div class="head">
<strong><abbr title="Yet Another Various Artists Helper">YAVAH</abbr></strong>
</div>
<div style="padding: 3px 6px 6px">
${this.generateInputs()}
</div>`
this.sibling.parentElement.insertBefore(box, this.sibling)
}
toggle(e) {
e.preventDefault()
const tog = this.parentElement.nextElementSibling.classList.toggle('hidden')
this.innerHTML = `<code>${tog ? '+' : '-'}</code> ${this.dataset.type}`
}
generateInputs() {
// let yavahtog = this.nextElementSibling.classList.toggle('hidden');
// this.firstElementChild.innerHTML = '<code>' + (yavahtog ? '+' : '-') + '</code> ' + this.firstElementChild.dataset.type
const boxes = this.types.map(type => {
return `<p><a href="#" data-type="${type}"><code>+</code> ${type}</a></p><textarea class="noWhutBB yavahtext hidden"></textarea>`
}).join('')
return `
<p>Enter artists, one per line.</p>
<p>
<input type="checkbox" id="yavah_semi"> <label for="yavah_semi">Split semi-colons</label>
</p>
${boxes}
<p>Review the changes below.</p>`
}
addEvent(cb) {
return this.box && !this.box.addEventListener('input', _.debounce(cb, 250), false)
}
}
class YavaMenuTr extends YavaMenu {
boxSetup(box) {
box.innerHTML = this.generateInputs()
const tr = document.createElement('tr')
tr.innerHTML = '<td class="label">YAVAH</td><td></td>'
tr.lastElementChild.appendChild(box)
this.sibling.parentElement.insertBefore(tr, this.sibling)
}
}
class Yavah {
constructor() {
this.selector = 'input[name="aliasname[]"]:last-of-type, input[name="artists[]"]:last-of-type'
this.add = document.querySelector('.box_addartists a, #artistfields a.brackets')
this.inputs = document.querySelector('#AddArtists, #artistfields')
if (!this.inputs)
return
_.css('#YAVAH p a { display:block } .yavahtext{width: 90%; height: 6em}')
this.stored = this.inputs.innerHTML
this.event = this.event.bind(this)
}
regex() {
if (YavaMenu.check.checked)
return /[^\r\n;]+/g
return /[^\r\n]+/g
}
/**
*
* @param {HTMLTextAreaElement} textarea
* @param {number} index Index of HTMLOptionElement within HTMLSelectElement to set (Main, Guest, Composer, etc.)
*/
fill(textarea, index) {
const lines = textarea.value.match(this.REGEX) || []
const unique = new Set(lines.map(_ => _.trim()))
unique.delete('')
unique.forEach(name => {
this.inputs.querySelector(this.selector).value = name
this.inputs.querySelector('select:last-of-type').value = index + 1
this.add.click()
})
}
event() {
// Reset the artist box
this.inputs.innerHTML = this.stored
_.js(() => window.ArtistFieldCount = -1000)
const textareas = document.querySelectorAll('#YAVAH textarea')
this.REGEX = this.regex()
Array.from(textareas).forEach(this.fill, this)
}
static main() {
const yava = new Yavah()
const menu = /(?:requests\.php|upload\.php)/.test(document.location.pathname) ? new YavaMenuTr : new YavaMenu
menu.addEvent(yava.event)
if (document.location.hash === '#yavah-test') {
new YavaTest(yava)
}
}
}
class YavaTest {
constructor(yavah) {
this.y = yavah
this.indices = '1-1-1-2-2-2-3-3-3-4-4-4-5-5-5-6-6-6-7-7-7-1'
this.values = 'A1;A1.1-A2-A3-B1;B1.1-B2-B3-C1;C1.1-C2-C3-D1;D1.1-D2-D3-E1;E1.1-E2-E3-F1;F1.1-F2-F3-G1;G1.1-G2-G3-'
this.indicesSemi = '1-1-1-1-2-2-2-2-3-3-3-3-4-4-4-4-5-5-5-5-6-6-6-6-7-7-7-7-1'
this.valuesSemi = this.values.replaceAll(';', '-')
this.testInit()
this.testRegular()
this.testSemi()
}
testInit() {
console.log('YAVAH TEST!')
// asume if no inputs, then there is nothing to do
if (!this.y.inputs)
return
// fill data
const textareas = Array.from(document.querySelectorAll('.yavahtext'))
textareas.forEach((t, i) => {
const ch = String.fromCharCode(65 + i);
t.value = `${ch}1;${ch}1.1\n${ch}2\n${ch}3`
})
// go for it!
this.y.event()
}
selects(indices) {
// assert dropdowns
console.group('YAVAH DROPDOWN TEST')
const selects = document.querySelectorAll('#AddArtists select, #artistfields select')
if (selects.length === indices.split('-').length)
console.debug('[Passed] Valid number of dropdowns')
else
console.warn('Invalid number of dropdowns')
if (Array.from(selects).map(_ => _.value).join('-') === indices)
console.debug('[Passed] Valid values for dropdowns')
else
console.warn('Invalid values for dropdowns')
console.groupEnd()
}
inputs(values) {
// assert inputs
console.group('YAVAH INPUT TEST')
const inputs = document.querySelectorAll('input[name="aliasname[]"], input[name="artists[]"]')
if (inputs.length === values.split('-').length)
console.debug('[Passed] Valid number of inputs')
else
console.warn('Invalid number of inputs')
if (Array.from(inputs).map(_ => _.value).join('-') === values)
console.debug('[Passed] Valid values for inputs')
else
console.warn('Invalid values for inputs')
console.groupEnd()
}
testRegular() {
console.info('Starting Regular Test')
this.selects(this.indices)
this.inputs(this.values)
}
testSemi() {
console.info('Starting Semicolon Test')
YavaMenu.check.checked = true
this.y.event()
this.selects(this.indicesSemi)
this.inputs(this.valuesSemi)
}
}
Yavah.main()
})()