| 1 | // ==UserScript== |
| 2 | // @name Nyaa - Personal Tweaks |
| 3 | // @namespace github.com/Decicus |
| 4 | // @match https://nyaa.si/* |
| 5 | // @grant GM_setClipboard |
| 6 | // @version 1.5.0 |
| 7 | // @author Decicus |
| 8 | // @description Adds some extra functionality to Nyaa. |
| 9 | // ==/UserScript== |
| 10 | |
| 11 | let copyAmount = 5; |
| 12 | const multipliers = { |
| 13 | Bytes: 1, |
| 14 | KiB: 1024, |
| 15 | MiB: 1024 * 1024, |
| 16 | GiB: 1024 * 1024 * 1024, |
| 17 | TiB: 1024 * 1024 * 1024 * 1024, |
| 18 | }; |
| 19 | |
| 20 | const sizesRegex = /(Bytes|KiB|MiB|GiB|TiB)/g; |
| 21 | |
| 22 | function getElements(linkType = 'download') |
| 23 | { |
| 24 | const elements = document.querySelectorAll(`.fa-${linkType}`); |
| 25 | return Array.from(elements) |
| 26 | .map(x => x.parentElement.href) |
| 27 | .filter(x => x && x.trim() !== ''); |
| 28 | } |
| 29 | |
| 30 | function getLinks() |
| 31 | { |
| 32 | return getElements('download'); |
| 33 | } |
| 34 | |
| 35 | function getMagnets() |
| 36 | { |
| 37 | return getElements('magnet'); |
| 38 | } |
| 39 | |
| 40 | /** |
| 41 | * Calculate the approximate total size of all torrents listed in the table and append it as an information row at the bottom of the table |
| 42 | */ |
| 43 | function calculateTotalSize() |
| 44 | { |
| 45 | const timestamps = document.querySelectorAll('.torrent-list td[data-timestamp]'); |
| 46 | let totalBytes = 0; |
| 47 | for (const timestamp of timestamps) |
| 48 | { |
| 49 | const sizeCell = timestamp.previousElementSibling; |
| 50 | const size = sizeCell.textContent.trim(); |
| 51 | let suffix = size.match(sizesRegex); |
| 52 | if (!suffix || suffix.length < 1) { |
| 53 | console.error('Could not find size in element', sizeCell); |
| 54 | continue; |
| 55 | } |
| 56 | |
| 57 | suffix = suffix[0]; |
| 58 | const multiplier = multipliers[suffix]; |
| 59 | const sizeNumber = parseFloat(size.replace(` ${suffix}`, '').trim()); |
| 60 | const torrentBytes = sizeNumber * multiplier; |
| 61 | |
| 62 | totalBytes += torrentBytes; |
| 63 | } |
| 64 | |
| 65 | const multiplierKeys = Object.keys(multipliers).reverse(); |
| 66 | let totalSize = `${totalBytes} Bytes`; |
| 67 | for (const key of multiplierKeys) |
| 68 | { |
| 69 | if (totalBytes === 0) { |
| 70 | break; |
| 71 | } |
| 72 | |
| 73 | const divideBy = multipliers[key]; |
| 74 | const result = totalBytes / divideBy; |
| 75 | if (result > 1) { |
| 76 | totalSize = `${result.toFixed(2)} ${key}`; |
| 77 | break; |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | const torrentList = document.querySelector('.torrent-list tbody'); |
| 82 | if (!torrentList) { |
| 83 | return; |
| 84 | } |
| 85 | |
| 86 | const sampleTorrent = "https://nyaa.si/download/222409.torrent"; |
| 87 | const sampleMagnet = "magnet:?xt=urn:btih:db0ffe8174317b0b0ee4beb7b54f558bb9089746&dn=%5Beoy%5D%20dark%20dragoon%20-%2001.txt&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Fexodus.desync.com%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.torrent.eu.org%3A451%2Fannounce"; |
| 88 | |
| 89 | const html = `<tr class="default" id="all-torrents-size"> |
| 90 | <td class="text-center"> |
| 91 | <a href="#all-torrents-size" title="Total size"> |
| 92 | ๐ฝ |
| 93 | </a> |
| 94 | </td> |
| 95 | <td colspan="2"> |
| 96 | <a href="#all-torrents-size">Total size</a> |
| 97 | </td> |
| 98 | <td class="text-center"> |
| 99 | <!-- These elements purely exist to avoid any issues with extensions or userscripts that would normally expect valid torrent/magnet URIs --> |
| 100 | <!-- In my case I only found an issue with NyaaBlue from SeaDex --> |
| 101 | <a href="${sampleTorrent}" style="display: none;"><!-- Download torrent --></a> |
| 102 | <a href="${sampleMagnet}" style="display: none;"><!-- Magnet URI --></a> |
| 103 | </td> |
| 104 | <td class="text-center">${totalSize}</td> |
| 105 | <td class="text-center"><!-- Timestamp --></td> |
| 106 | <td class="text-center"><!-- Seeders --></td> |
| 107 | <td class="text-center"><!-- Leechers --></td> |
| 108 | <td class="text-center"><!-- Completed --></td> |
| 109 | </tr>`; |
| 110 | |
| 111 | torrentList.insertAdjacentHTML('beforeend', html); |
| 112 | } |
| 113 | |
| 114 | /** |
| 115 | * Handler for copying download links (torrents) |
| 116 | */ |
| 117 | function copyLinks(amount) |
| 118 | { |
| 119 | let links = getLinks(); |
| 120 | if (amount && typeof amount === 'number') { |
| 121 | amount = amount > links.length ? links.length : amount; |
| 122 | links = links.slice(0, amount); |
| 123 | } |
| 124 | |
| 125 | const list = links.join('\n'); |
| 126 | GM_setClipboard(list); |
| 127 | } |
| 128 | |
| 129 | /** |
| 130 | * Handler for copying magnets |
| 131 | */ |
| 132 | function copyMagnets(amount) |
| 133 | { |
| 134 | let magnets = getMagnets(); |
| 135 | if (amount && typeof amount === 'number') { |
| 136 | amount = amount > magnets.length ? magnets.length : amount; |
| 137 | magnets = magnets.slice(0, amount); |
| 138 | } |
| 139 | |
| 140 | const list = magnets.join('\n'); |
| 141 | GM_setClipboard(list); |
| 142 | } |
| 143 | |
| 144 | /** |
| 145 | * Changes the download links for torrents/magnets to trigger a copy, |
| 146 | * instead of default handling (download for torrents, torrent *client* for magnets). |
| 147 | */ |
| 148 | function copyOnClick(ev) |
| 149 | { |
| 150 | ev.preventDefault(); |
| 151 | |
| 152 | const target = ev.target; |
| 153 | const isAnchor = target.nodeName === 'A'; |
| 154 | const parent = isAnchor ? target : target.parentElement; |
| 155 | GM_setClipboard(parent.href + '\n'); |
| 156 | |
| 157 | const checkIcon = 'fa-check'; |
| 158 | const originalIcon = parent.href.includes('/download/') ? 'fa-download' : 'fa-magnet'; |
| 159 | |
| 160 | const iconElement = !target.classList.contains('fa') ? target.querySelector('.fa') : target; |
| 161 | console.log(iconElement); |
| 162 | |
| 163 | if (iconElement) { |
| 164 | iconElement.classList.replace(originalIcon, checkIcon); |
| 165 | |
| 166 | setTimeout( |
| 167 | function() { |
| 168 | iconElement.classList.replace(checkIcon, originalIcon); |
| 169 | }, |
| 170 | 1500 |
| 171 | ); |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | /** |
| 176 | * Changes said magnet/torrent links to use the `copyOnClick` handler. |
| 177 | */ |
| 178 | function changeToCopy() |
| 179 | { |
| 180 | const links = document.querySelectorAll('.fa-magnet, .fa-download'); |
| 181 | |
| 182 | for (const link of links) |
| 183 | { |
| 184 | link.parentElement.addEventListener('click', copyOnClick); |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | /** |
| 189 | * List of regex replacements so that I can copy titles as directory names, with my personally preferred naming scheme. |
| 190 | */ |
| 191 | const titleReplacements = [ |
| 192 | { |
| 193 | comment: 'Remove quoted series/movie title from the end', |
| 194 | search: / \"[A-z0-9-!\s]+\"$/, |
| 195 | replacement: '', |
| 196 | }, |
| 197 | // { |
| 198 | // comment: 'Move release group to the end', |
| 199 | // search: /^\[([-\w ]+)\] (.+)$/, |
| 200 | // replacement: '$2 [$1]', |
| 201 | // }, |
| 202 | { |
| 203 | comment: 'Colon space to space dash space', |
| 204 | search: /: /g, |
| 205 | replacement: ' - ', |
| 206 | }, |
| 207 | { |
| 208 | comment: 'Ensure seasons have two numbers', |
| 209 | search: / \(Season (\d)\)/g, |
| 210 | replacement: ' (Season 0$1)', |
| 211 | }, |
| 212 | { |
| 213 | comment: 'Season naming so Sonarr doesnt get confused because its dumb', |
| 214 | search: / \(Season (\d+)\)/g, |
| 215 | replacement: ' - S$1', |
| 216 | }, |
| 217 | { |
| 218 | comment: 'No slashes, please', |
| 219 | search: /\//g, |
| 220 | replacement: '-', |
| 221 | }, |
| 222 | { |
| 223 | comment: 'Closing => Opening bracket spacing between each "tag"', |
| 224 | search: /\]\[/g, |
| 225 | replacement: '] [', |
| 226 | }, |
| 227 | { |
| 228 | comment: 'Final replacement: Replace 2+ spaces with one space', |
| 229 | search: /\s{2,}/g, |
| 230 | replacement: ' ', |
| 231 | }, |
| 232 | ]; |
| 233 | |
| 234 | /** |
| 235 | * Uses `titleReplacements` to "fix" torrent name when "Copy torrent name" button is clicked. |
| 236 | */ |
| 237 | function fixTitle(input) |
| 238 | { |
| 239 | let finalString = input.trim(); |
| 240 | for (const replace of titleReplacements) |
| 241 | { |
| 242 | const { comment, search, replacement } = replace; |
| 243 | |
| 244 | const match = finalString.match(search); |
| 245 | if (match === null) { |
| 246 | continue; |
| 247 | } |
| 248 | |
| 249 | console.log(`\nFound: ${search}\nComment: ${comment}\nMatches:\n- ${match.join('\n- ')}`); |
| 250 | finalString = finalString.replace(search, replacement || ''); |
| 251 | } |
| 252 | |
| 253 | // Deal with any excess whitespace |
| 254 | finalString = finalString.trim(); |
| 255 | console.log(`Original input: ${input}\nFinal string: ${finalString}`); |
| 256 | |
| 257 | return finalString; |
| 258 | } |
| 259 | |
| 260 | /** |
| 261 | * Adds copy button for torrent titles on torrent pages |
| 262 | * Implements `fixTitle()` |
| 263 | */ |
| 264 | function titleHandler() |
| 265 | { |
| 266 | const title = document.querySelector('h3.panel-title'); |
| 267 | |
| 268 | if (!title) { |
| 269 | return; |
| 270 | } |
| 271 | |
| 272 | const titleParent = title.parentElement; |
| 273 | |
| 274 | const buttonParent = document.createElement('div'); |
| 275 | buttonParent.setAttribute('class', 'btn-group pull-right'); |
| 276 | |
| 277 | const button = document.createElement('button'); |
| 278 | button.textContent = '๐'; |
| 279 | button.setAttribute('class', 'btn btn-primary btn-sm'); |
| 280 | button.addEventListener('click', function() { |
| 281 | const origText = button.textContent; |
| 282 | |
| 283 | button.textContent = 'โ'; |
| 284 | button.setAttribute('disabled', '1'); |
| 285 | |
| 286 | const fixedTitle = fixTitle(title.textContent.trim()); |
| 287 | GM_setClipboard(fixedTitle); |
| 288 | |
| 289 | setTimeout( |
| 290 | () => { |
| 291 | button.textContent = origText; |
| 292 | button.removeAttribute('disabled'); |
| 293 | }, 1500 |
| 294 | ); |
| 295 | }); |
| 296 | |
| 297 | titleParent.classList.add('clearfix'); |
| 298 | buttonParent.insertAdjacentElement('afterbegin', button); |
| 299 | titleParent.insertAdjacentElement('afterbegin', buttonParent); |
| 300 | } |
| 301 | |
| 302 | /** |
| 303 | * Used by `copyFilenameHandler()` to do the actual copying |
| 304 | */ |
| 305 | function copyFilenameTrigger(ev) |
| 306 | { |
| 307 | const { target } = ev; |
| 308 | |
| 309 | // Trust no one, not even yourself |
| 310 | if (!target) { |
| 311 | return; |
| 312 | } |
| 313 | |
| 314 | // We clone it here because I want to remove the file size that's normally at the end |
| 315 | // If I simply do a `remove()` on the usual element, it will actually remove it from the DOM. |
| 316 | const parent = target.parentElement.cloneNode(true); |
| 317 | const fileSize = parent.querySelector('.file-size'); |
| 318 | |
| 319 | if (fileSize) { |
| 320 | fileSize.remove(); |
| 321 | } |
| 322 | |
| 323 | const filename = parent.textContent.trim(); |
| 324 | |
| 325 | target.classList.remove('fa-file'); |
| 326 | target.classList.add('fa-check'); |
| 327 | |
| 328 | GM_setClipboard(filename); |
| 329 | |
| 330 | setTimeout(() => { |
| 331 | target.classList.remove('fa-check'); |
| 332 | target.classList.add('fa-file'); |
| 333 | }, 1500); |
| 334 | } |
| 335 | |
| 336 | /** |
| 337 | * Clicking the file icon in front of each file will allow you to copy the filename |
| 338 | */ |
| 339 | async function copyFilenameHandler() |
| 340 | { |
| 341 | const fileList = document.querySelector('.torrent-file-list'); |
| 342 | if (!fileList) { |
| 343 | return; |
| 344 | } |
| 345 | |
| 346 | const fileIcons = fileList.querySelectorAll('.fa-file'); |
| 347 | |
| 348 | for (const icon of fileIcons) |
| 349 | { |
| 350 | icon.addEventListener('click', copyFilenameTrigger); |
| 351 | } |
| 352 | } |
| 353 | |
| 354 | function bbcodeConverter(html) { |
| 355 | html = html.replace(/<pre(.*?)>(.*?)<\/pre>/gmi, "[code]$2[/code]"); |
| 356 | html = html.replace(/<h[1-7](.*?)>(.*?)<\/h[1-7]>/, "\n[h]$2[/h]\n"); |
| 357 | //paragraph handling: |
| 358 | //- if a paragraph opens on the same line as another one closes, insert an extra blank line |
| 359 | //- opening tag becomes two line breaks |
| 360 | //- closing tags are just removed |
| 361 | // html += html.replace(/<\/p><p/<\/p>\n<p/gi; |
| 362 | // html += html.replace(/<p[^>]*>/\n\n/gi; |
| 363 | // html += html.replace(/<\/p>//gi; |
| 364 | |
| 365 | html = html.replace(/<\/p>\n<p>/gi, "\n\n\n"); |
| 366 | |
| 367 | html = html.replace(/<br(.*?)>/gi, "\n"); |
| 368 | html = html.replace(/<textarea(.*?)>(.*?)<\/textarea>/gmi, "\[code]$2\[\/code]"); |
| 369 | html = html.replace(/<b>/gi, "[b]"); |
| 370 | html = html.replace(/<i>/gi, "[i]"); |
| 371 | html = html.replace(/<u>/gi, "[u]"); |
| 372 | html = html.replace(/<\/b>/gi, "[/b]"); |
| 373 | html = html.replace(/<\/i>/gi, "[/i]"); |
| 374 | html = html.replace(/<\/u>/gi, "[/u]"); |
| 375 | html = html.replace(/<em>/gi, "[b]"); |
| 376 | html = html.replace(/<\/em>/gi, "[/b]"); |
| 377 | html = html.replace(/<strong>/gi, "[b]"); |
| 378 | html = html.replace(/<\/strong>/gi, "[/b]"); |
| 379 | html = html.replace(/<cite>/gi, "[i]"); |
| 380 | html = html.replace(/<\/cite>/gi, "[/i]"); |
| 381 | html = html.replace(/<font color="(.*?)">(.*?)<\/font>/gmi, "[color=$1]$2[/color]"); |
| 382 | html = html.replace(/<font color=(.*?)>(.*?)<\/font>/gmi, "[color=$1]$2[/color]"); |
| 383 | html = html.replace(/<link(.*?)>/gi, ""); |
| 384 | html = html.replace(/<li(.*?)>(.*?)<\/li>/gi, "[*]$2"); |
| 385 | html = html.replace(/<ul(.*?)>/gi, "[list]"); |
| 386 | html = html.replace(/<\/ul>/gi, "[/list]"); |
| 387 | html = html.replace(/<div>/gi, "\n"); |
| 388 | html = html.replace(/<\/div>/gi, "\n"); |
| 389 | html = html.replace(/<td(.*?)>/gi, " "); |
| 390 | html = html.replace(/<tr(.*?)>/gi, "\n"); |
| 391 | |
| 392 | html = html.replace(/<img(.*?)src="(.*?)"(.*?)>/gi, "[img]$2[/img]"); |
| 393 | html = html.replace(/<a(.*?)href="(.*?)"(.*?)>(.*?)<\/a>/gi, "[url=$2]$4[/url]"); |
| 394 | |
| 395 | html = html.replace(/<head>(.*?)<\/head>/gmi, ""); |
| 396 | html = html.replace(/<object>(.*?)<\/object>/gmi, ""); |
| 397 | html = html.replace(/<script(.*?)>(.*?)<\/script>/gmi, ""); |
| 398 | html = html.replace(/<style(.*?)>(.*?)<\/style>/gmi, ""); |
| 399 | html = html.replace(/<title>(.*?)<\/title>/gmi, ""); |
| 400 | html = html.replace(/<!--(.*?)-->/gmi, "\n"); |
| 401 | |
| 402 | html = html.replace(/\/\//gi, "/"); |
| 403 | html = html.replace(/http:\//gi, "http://"); |
| 404 | |
| 405 | html = html.replace(/<(?:[^>'"]*|(['"]).*?\1)*>/gmi, ""); |
| 406 | html = html.replace(/\r\r/gi, ""); |
| 407 | html = html.replace(/\[img]\//gi, "[img]"); |
| 408 | html = html.replace(/\[url=\//gi, "[url="); |
| 409 | |
| 410 | html = html.replace(/(\S)\n/gi, "$1 "); |
| 411 | |
| 412 | return html; |
| 413 | } |
| 414 | |
| 415 | function descriptionHandler() |
| 416 | { |
| 417 | const descriptionElem = document.querySelector('#torrent-description'); |
| 418 | if (!descriptionElem) { |
| 419 | return; |
| 420 | } |
| 421 | |
| 422 | console.log(bbcodeConverter(descriptionElem.innerHTML)); |
| 423 | |
| 424 | const button = document.createElement('button'); |
| 425 | button.textContent = '๐ Copy description as BBCode'; |
| 426 | button.setAttribute('class', 'btn btn-primary'); |
| 427 | button.addEventListener('click', function() { |
| 428 | const origText = button.textContent; |
| 429 | |
| 430 | button.textContent = 'โ Copied!'; |
| 431 | button.setAttribute('disabled', '1'); |
| 432 | |
| 433 | const descriptionBBcode = bbcodeConverter(descriptionElem.innerHTML).trim(); |
| 434 | GM_setClipboard(descriptionBBcode); |
| 435 | |
| 436 | setTimeout( |
| 437 | () => { |
| 438 | button.textContent = origText; |
| 439 | button.removeAttribute('disabled'); |
| 440 | }, 1500 |
| 441 | ); |
| 442 | }); |
| 443 | |
| 444 | const panelHeading = document.createElement('div'); |
| 445 | panelHeading.setAttribute('class', 'panel-heading'); |
| 446 | panelHeading.insertAdjacentElement('beforeend', button); |
| 447 | |
| 448 | descriptionElem.insertAdjacentElement('beforebegin', panelHeading); |
| 449 | } |
| 450 | |
| 451 | /** |
| 452 | * Adds a clipboard icon (using Font Awesome 4) to each row that is marked for "mass copy". |
| 453 | */ |
| 454 | function addClipboardIconToRow() |
| 455 | { |
| 456 | const rows = document.querySelectorAll('.torrent-list > tbody > tr > td[colspan="2"]'); |
| 457 | |
| 458 | let currentAmount = 0; |
| 459 | |
| 460 | for (const row of rows) |
| 461 | { |
| 462 | currentAmount++; |
| 463 | const iconExists = row.querySelector('a.clipboard-icon'); |
| 464 | if (iconExists) { |
| 465 | iconExists.remove(); |
| 466 | } |
| 467 | |
| 468 | // Check if this row is past the point of "don't receive icon" |
| 469 | if (currentAmount > copyAmount) { |
| 470 | // `continue`, not `break` - If we break, then when we lower `copyAmount`, there will be extra rows with clipboard icon. |
| 471 | continue; |
| 472 | } |
| 473 | |
| 474 | row.insertAdjacentHTML('afterbegin', '<a href="#" class="comments clipboard-icon" style="margin-left: 0.5em;"><i class="fa fa-clipboard"></i></a>'); |
| 475 | } |
| 476 | } |
| 477 | |
| 478 | /** |
| 479 | * Adds buttons and number input fields above torrent table, for "mass copying" links/magnets. |
| 480 | */ |
| 481 | function torrentListViewHandler() |
| 482 | { |
| 483 | const parentElement = document.querySelector('.table-responsive'); |
| 484 | |
| 485 | if (!parentElement) { |
| 486 | return; |
| 487 | } |
| 488 | |
| 489 | addClipboardIconToRow(); |
| 490 | |
| 491 | /** |
| 492 | * Start: Copy all links/magnets |
| 493 | */ |
| 494 | const element = document.createElement('button'); |
| 495 | element.innerHTML = '๐ Copy all download links <i class="fa fa-fw fa-download"></i>'; |
| 496 | element.setAttribute('class', 'btn btn-default'); |
| 497 | element.addEventListener('click', function() { |
| 498 | const origText = element.innerHTML; |
| 499 | |
| 500 | element.innerHTML = 'โ'; |
| 501 | element.setAttribute('disabled', '1'); |
| 502 | copyLinks(); |
| 503 | |
| 504 | setTimeout(() => { |
| 505 | element.innerHTML = origText; |
| 506 | element.removeAttribute('disabled'); |
| 507 | }, 1500); |
| 508 | }); |
| 509 | |
| 510 | const magnetCopy = document.createElement('button'); |
| 511 | magnetCopy.innerHTML = '๐ Copy all magnets <i class="fa fa-fw fa-magnet"></i>'; |
| 512 | magnetCopy.setAttribute('class', 'btn btn-default'); |
| 513 | magnetCopy.addEventListener('click', function() { |
| 514 | const origText = magnetCopy.innerHTML; |
| 515 | |
| 516 | magnetCopy.innerHTML = 'โ'; |
| 517 | magnetCopy.setAttribute('disabled', '1'); |
| 518 | copyMagnets(); |
| 519 | |
| 520 | setTimeout(() => { |
| 521 | magnetCopy.innerHTML = origText; |
| 522 | magnetCopy.removeAttribute('disabled'); |
| 523 | }, 1500); |
| 524 | }); |
| 525 | |
| 526 | /** |
| 527 | * End: Copy all links/magnets |
| 528 | */ |
| 529 | |
| 530 | /** |
| 531 | * Start: Copy X links/magnets |
| 532 | */ |
| 533 | const linkAmount = (getLinks()).length; |
| 534 | copyAmount = linkAmount < 5 ? linkAmount : 5; |
| 535 | const amountSelect = document.createElement('input'); |
| 536 | amountSelect.setAttribute('type', 'number'); |
| 537 | amountSelect.setAttribute('min', '2'); |
| 538 | amountSelect.setAttribute('max', linkAmount); |
| 539 | amountSelect.setAttribute('value', copyAmount); |
| 540 | amountSelect.setAttribute('class', 'pull-right'); |
| 541 | |
| 542 | const amountCopyLinks = document.createElement('button'); |
| 543 | amountCopyLinks.innerHTML = `๐ Copy ${copyAmount} download links <i class="fa fa-fw fa-download"></i>`; |
| 544 | amountCopyLinks.setAttribute('class', 'btn btn-default pull-right'); |
| 545 | amountCopyLinks.addEventListener('click', function() { |
| 546 | const origText = amountCopyLinks.innerHTML; |
| 547 | |
| 548 | amountCopyLinks.innerHTML = 'โ'; |
| 549 | amountCopyLinks.setAttribute('disabled', '1'); |
| 550 | copyLinks(copyAmount); |
| 551 | |
| 552 | setTimeout(() => { |
| 553 | amountCopyLinks.innerHTML = origText; |
| 554 | amountCopyLinks.removeAttribute('disabled'); |
| 555 | }, 1500); |
| 556 | }); |
| 557 | |
| 558 | const amountCopyMagnets = document.createElement('button'); |
| 559 | amountCopyMagnets.innerHTML = `๐ Copy ${copyAmount} magnets <i class="fa fa-fw fa-magnet"></i>`; |
| 560 | amountCopyMagnets.setAttribute('class', 'btn btn-default pull-right'); |
| 561 | amountCopyMagnets.addEventListener('click', function() { |
| 562 | const origText = amountCopyMagnets.innerHTML; |
| 563 | |
| 564 | amountCopyMagnets.innerHTML = 'โ'; |
| 565 | amountCopyMagnets.setAttribute('disabled', '1'); |
| 566 | copyMagnets(copyAmount); |
| 567 | |
| 568 | setTimeout(() => { |
| 569 | amountCopyMagnets.innerHTML = origText; |
| 570 | amountCopyMagnets.removeAttribute('disabled'); |
| 571 | }, 1500); |
| 572 | }); |
| 573 | |
| 574 | amountSelect.addEventListener('change', function() { |
| 575 | copyAmount = parseInt(amountSelect.value, 10); |
| 576 | amountCopyLinks.innerHTML = `๐ Copy ${copyAmount} download links <i class="fa fa-fw fa-download"></i>`; |
| 577 | amountCopyMagnets.innerHTML = `๐ Copy ${copyAmount} magnets <i class="fa fa-fw fa-magnet"></i>`; |
| 578 | |
| 579 | addClipboardIconToRow(); |
| 580 | }); |
| 581 | /** |
| 582 | * End: Copy X links/magnets |
| 583 | */ |
| 584 | |
| 585 | /** |
| 586 | * Append all these buttons and inputs to the DOM. |
| 587 | */ |
| 588 | parentElement.insertAdjacentElement('beforebegin', element); |
| 589 | parentElement.insertAdjacentElement('beforebegin', magnetCopy); |
| 590 | |
| 591 | parentElement.insertAdjacentElement('beforebegin', amountSelect); |
| 592 | parentElement.insertAdjacentElement('beforebegin', amountCopyMagnets); |
| 593 | parentElement.insertAdjacentElement('beforebegin', amountCopyLinks); |
| 594 | } |
| 595 | |
| 596 | /** |
| 597 | * This previously changed some `.container` classes to `.container-fluid`, |
| 598 | * but since that breaks other userscripts, such as NyaaBlue from SeaDex, |
| 599 | * I figured it'd be better to simply override the CSS width that .container usually applies. |
| 600 | */ |
| 601 | function widerContainers() |
| 602 | { |
| 603 | const styleDoc = document.createElement('style'); |
| 604 | styleDoc.setAttribute('type', 'text/css'); |
| 605 | |
| 606 | styleDoc.textContent = ` |
| 607 | .container { |
| 608 | width: auto !important; |
| 609 | } |
| 610 | `; |
| 611 | |
| 612 | document.head.insertAdjacentElement('beforeend', styleDoc); |
| 613 | } |
| 614 | |
| 615 | function init() { |
| 616 | changeToCopy(); |
| 617 | titleHandler(); |
| 618 | descriptionHandler(); |
| 619 | copyFilenameHandler(); |
| 620 | torrentListViewHandler(); |
| 621 | calculateTotalSize(); |
| 622 | widerContainers(); |
| 623 | } |
| 624 | |
| 625 | init(); |
Alex / Nyaa - Personal Tweaks
Last active 4 months ago
Miscelleanous tweaks applied to Nyaa.si
Revision 5c617beb80be24e60e92f457a9189323d1c9299b