├── .gitignore ├── .idea ├── inspectionProfiles │ └── Project_Default.xml ├── misc.xml ├── modules.xml ├── scripter.iml ├── vcs.xml └── watcherTasks.xml ├── Booru-Selector-Downloader ├── README.MD ├── bord.html └── main.js ├── LICENSE ├── NeteaseCloudMusicPic ├── NeteaseCloudMusicPic.js └── README.md ├── README.md ├── steam-picBed-log ├── README.MD └── steam-picBed-log.js ├── xiamiAlbum └── xiami.js └── yande.re_db ├── gmDownloader.js ├── index.html ├── index.js ├── public.json ├── r.json └── style.css /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Python template 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | venv/ 8 | 9 | # C extensions 10 | *.so 11 | 12 | # vsc 13 | .vscode 14 | 15 | #idea 16 | .idea/dataSources.local.xml 17 | .idea/dataSources.xml 18 | .idea/dataSources/ 19 | .idea/workspace.xml 20 | 21 | # node 22 | taobao_api_spider/node_modules/ -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/scripter.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/watcherTasks.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Booru-Selector-Downloader/README.MD: -------------------------------------------------------------------------------- 1 | ## Booru-Selector-Downloader 2 | 3 | 4 | ### [greasyfork install](https://greasyfork.org/zh-CN/scripts/371605-booru-selector-downloader) 5 | 6 | 7 | ### List of supported websites: 8 | 9 | - [yande.re](https://yande.re/) 10 | - [konachan.net](https://konachan.net) 11 | - [konachan.com](https://konachan.com/) 12 | - [danbooru](https://danbooru.donmai.us/) 13 | - [gelbooru](https://gelbooru.com/) 14 | - [sonohara](https://sonohara.donmai.us/) 15 | 16 | 17 | ### Hotkeys 18 | 19 | `A`: Previous page 20 | 21 | `D`: Next page 22 | 23 | `Q`: Select/Deselect all image 24 | 25 | `S`: Save sample image 26 | 27 | `X`: Save original image(if no original image, the downloader will download the sample image) 28 | 29 | `F`: Favorite image 30 | 31 | `R`: Remove from favorites 32 | 33 | `Ctrl + MouseClick`: Open in the new window 34 | 35 | `Alt + MouseClick`: Open in the new window and auto focus the new tab 36 | 37 | `Shift + MouseHover`: Show preview image when hover the image, default scale size is `scale(2.5, 2.5)` 38 | 39 | -------------------------------------------------------------------------------- /Booru-Selector-Downloader/bord.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 225 | 226 | 227 |
228 |
229 |
230 |
Booru-Selector-Downloader
231 | 241 |
242 |
X
243 |
H
244 |
245 |
246 |
247 |
Select All 0 Image
248 |
249 |
250 |
Download Sample
251 |
252 |
253 |
Download Origin
254 |
255 |
256 |
Add To Favorite
257 |
258 |
259 |
Remove From Favorite
260 |
261 |
262 |
Release Note:
263 | v2.0.0 264 |
265 |
266 |
267 |
Add Favorites:
268 |
269 | 12345 270 | Success 271 | 272 |
273 |
274 |
275 |
276 |
277 |
223555
278 |
279 |
280 |
281 |
100%
282 |
283 |
284 |
223556
285 |
286 |
100%
287 |
288 |
289 |
290 |
291 | 348 | 349 | 350 | -------------------------------------------------------------------------------- /Booru-Selector-Downloader/main.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name Booru-Selector-Downloader 3 | // @namespace http://tampermonkey.net/ 4 | // @icon https://yande.re/favicon.ico 5 | // @version 4.0.1 6 | // @description A selector and downloader for the various booru imageboards 7 | // @description:en A selector and downloader for the various booru imageboards 8 | // @description:zh 图站选择下载工具 9 | // @author Beats0 10 | // @license GPL-3.0 License 11 | // @match *://yande.re/post* 12 | // @match *://konachan.net/* 13 | // @match *://konachan.com/* 14 | // @match *://gelbooru.com/* 15 | // @match *://danbooru.donmai.us/* 16 | // @match *://sonohara.donmai.us/* 17 | // @include *://yande.re/* 18 | // @include *://konachan.net/* 19 | // @include *://konachan.com/* 20 | // @include *://gelbooru.com/* 21 | // @include *://danbooru.donmai.us/* 22 | // @include *://sonohara.donmai.us/* 23 | // @grant GM_addStyle 24 | // @grant GM_download 25 | // @grant GM_openInTab 26 | // @home-url https://greasyfork.org/zh-CN/scripts/371605-booru-selector-downloader 27 | // @home-url2 https://github.com/Beats0/scripter 28 | // ==/UserScript== 29 | 30 | /** 31 | * ### Hot keys 32 | * 33 | * `A`: Previous page 34 | * `D`: Next page 35 | * `Q`: Select/Deselect all image 36 | * `S`: Save sample image 37 | * `X`: Save original image(if no original image, the downloader will download the sample image) 38 | * `F`: Favorite image 39 | * `R`: Remove from favorites 40 | * `Ctrl + MouseClick`: Open in the new window 41 | * `Alt + MouseClick`: Open in the new window and auto focus the new tab 42 | * `Shift + MouseHover`: Show preview image when hover the image, default scale size is `scale(2.5, 2.5)` 43 | * */ 44 | 45 | (function () { 46 | 'use strict'; 47 | const originUrl = document.location.origin; 48 | const locationUrl = document.location.protocol + '//' + window.location.host; 49 | const REyande = /yande/, 50 | REkonachan = /konachan/, 51 | REgelbooru = /gelbooru/, 52 | REdanbooru = /danbooru/, 53 | REsonohara = /sonohara/; 54 | const re1 = /\d\w+/, 55 | re2 = /([a-fA-F0-9]{32})/, 56 | re3 =/\.[0-9a-z]+$/i; 57 | const REyandeResult = REyande.test(originUrl); 58 | const REkonachanResult = REkonachan.test(originUrl); 59 | const REdanbooruResult = REdanbooru.test(originUrl) || REsonohara.test(originUrl); 60 | const REgelbooruResult = REgelbooru.test(originUrl); 61 | let parse = null 62 | const reTrySvgIcon = `` 63 | 64 | function $(selector) { 65 | return document.querySelector(selector) 66 | } 67 | 68 | function $$(selector) { 69 | return document.querySelectorAll(selector) 70 | } 71 | 72 | function domParser(fragment) { 73 | if(!parse) { 74 | const range = document.createRange(); 75 | parse = range.createContextualFragment.bind(range); 76 | } 77 | return parse(fragment) 78 | } 79 | 80 | function promiseFetch(url, data) { 81 | return new Promise((resolve, reject) => { 82 | fetch(url, data) 83 | .then(res => { 84 | if (res.ok) { 85 | resolve(res); 86 | } else { 87 | throw res; 88 | } 89 | }) 90 | .catch(err => { 91 | reject(err); 92 | }); 93 | }); 94 | } 95 | 96 | class BooruDownloader { 97 | constructor() { 98 | this.batchCount = 0 99 | this.downloadLimit = 4 100 | this.hoverEl = null 101 | this.cacheImg = {} // {id: src} 102 | this.init() 103 | } 104 | 105 | init() { 106 | console.log('init BooruDownloader') 107 | if (REyandeResult || REkonachanResult) { 108 | let posts = $('#post-list-posts'); 109 | if (!posts) return 110 | this.init_yande_konachan(); 111 | } 112 | if (REdanbooruResult) { 113 | const posts = $('.posts-container') 114 | if(!posts) return 115 | this.init_danbooru(); 116 | } 117 | if (REgelbooruResult) { 118 | const posts = $('.thumbnail-container') 119 | if(!posts) return 120 | this.init_gelbooru(); 121 | } 122 | this.initStyle() 123 | this.initMenuPanel() 124 | this.initHotKey() 125 | } 126 | 127 | initStyle() { 128 | const styleCode = ` 129 | :root { 130 | --primary-backgroundColor: #eee; 131 | --primary-lineBackgroundColor: #ccc; 132 | --primary-fontColor: #ee8887; 133 | --primary-fontColorHover: #ff4342; 134 | --primary-headerFontColor: #ffffff; 135 | --primary-border: 1px solid transparent; 136 | } 137 | 138 | [data-theme=light] .darkToggleIcon { 139 | display: none; 140 | } 141 | [data-theme=dark] .lightToggleIcon { 142 | display: none; 143 | } 144 | 145 | ul#post-list-posts { 146 | padding-bottom: 350px; 147 | } 148 | 149 | ul#post-list-posts li { 150 | float: none 151 | } 152 | 153 | div#posts { 154 | padding-bottom: 200px; 155 | } 156 | .imgItem { 157 | transition: .2s; 158 | } 159 | .imgItem:hover, .imgItem:focus { 160 | outline: 1px solid var(--primary-fontColor); 161 | } 162 | .imgItem img { 163 | transition: .2s; 164 | } 165 | 166 | .imgItemChecked { 167 | outline: 1px solid var(--primary-fontColor); 168 | } 169 | 170 | article.post-preview { 171 | float: none; 172 | } 173 | 174 | .helper-board { 175 | width: 450px; 176 | height: 320px; 177 | position: fixed; 178 | font-size: 12px; 179 | right: 10px; 180 | bottom: 4px; 181 | background: var(--primary-backgroundColor); 182 | color: var(--primary-fontColor); 183 | border: var(--primary-border); 184 | border-radius: 5px; 185 | overflow: hidden; 186 | transition: all cubic-bezier(.22,.58,.12,.98) .4s; 187 | box-shadow: 0 2px 12px 0 rgba(246, 150, 149, 0.6); 188 | } 189 | 190 | .helper-board.helper-board-small { 191 | width: 50px; 192 | height: 50px; 193 | border-radius: 50%; 194 | bottom: 50vh; 195 | cursor: pointer; 196 | background: var(--primary-fontColor); 197 | } 198 | 199 | .helper-board.helper-board-small .board-header { 200 | width: 50px; 201 | height: 50px; 202 | border-radius: 50%; 203 | } 204 | .helper-board.helper-board-small .board-header .board-close-button, .helper-board.helper-board-small .board-header .board-header-text, .helper-board.helper-board-small .theme-btn { 205 | display: none; 206 | } 207 | .helper-board.helper-board-small .board-header .board-header-small-tip { 208 | display: block; 209 | width: 50px; 210 | height: 50px; 211 | border-radius: 50%; 212 | line-height: 50px; 213 | text-align: center; 214 | font-size: 26px; 215 | } 216 | 217 | .board-header { 218 | height: 30px; 219 | color: var(--primary-headerFontColor); 220 | background: var(--primary-fontColor); 221 | font-size: 16px; 222 | } 223 | 224 | .board-header-inner { 225 | display: flex; 226 | align-items: center; 227 | justify-content: space-between; 228 | padding-right: 32px; 229 | } 230 | 231 | .board-content { 232 | overflow-y: auto; 233 | overflow-x: hidden; 234 | max-height: 340px; 235 | padding: 10px 12px 50px 12px; 236 | height: 100%; 237 | font-size: 14px; 238 | } 239 | 240 | .board-header-text { 241 | line-height: 30px; 242 | padding-left: 10px; 243 | font-size: 14px; 244 | } 245 | 246 | .board-close-button { 247 | position: absolute; 248 | top: 6px; 249 | right: 3px; 250 | width: 20px; 251 | height: 20px; 252 | margin: 0; 253 | padding: 0; 254 | cursor: pointer; 255 | transition: all .3s; 256 | } 257 | 258 | .board-close-button:hover { 259 | color: var(--primary-headerFontColor); 260 | } 261 | .board-header-small-tip { 262 | display: none; 263 | } 264 | 265 | .board-content-row { 266 | display: flex; 267 | align-items: center; 268 | margin-bottom: 8px; 269 | height: 20px; 270 | } 271 | 272 | .row-label { 273 | color: var(--primary-fontColor); 274 | margin-right: 8px; 275 | } 276 | .row-content { 277 | display: flex; 278 | align-items: center; 279 | } 280 | .hover-item-line { 281 | color: var(--primary-fontColor); 282 | text-decoration: underline!important; 283 | cursor: pointer; 284 | } 285 | .hover-item-line:hover { 286 | color: var(--primary-fontColorHover); 287 | } 288 | .hover-item { 289 | color: var(--primary-fontColor); 290 | cursor: pointer; 291 | } 292 | .hover-item:hover { 293 | color: var(--primary-fontColorHover); 294 | } 295 | 296 | .download-row-container { 297 | margin-top: 15px; 298 | padding-bottom: 15px; 299 | } 300 | .download-row { 301 | display: flex; 302 | align-items: center; 303 | margin-bottom: 5px; 304 | } 305 | .download-row-title { 306 | color: var(--primary-fontColor); 307 | margin-right: 10px; 308 | } 309 | .download-row-line { 310 | flex: 1; 311 | height: 4px; 312 | background: var(--primary-lineBackgroundColor); 313 | margin-right: 10px; 314 | } 315 | .download-row-line-active { 316 | height: 100%; 317 | background: var(--primary-fontColor); 318 | transition: width .4s ease; 319 | } 320 | .download-row-percent { 321 | color: var(--primary-fontColor); 322 | width: 50px; 323 | } 324 | .fav-state { 325 | margin-left: 7px; 326 | color: var(--primary-fontColor); 327 | } 328 | .re-try-icon svg { 329 | margin-left: 5px; 330 | cursor: pointer; 331 | transform: translateY(3px); 332 | } 333 | 334 | .theme-btn { 335 | background: none; 336 | border: none; 337 | color: var(--primary-headerFontColor); 338 | cursor: pointer; 339 | font-family: inherit; 340 | padding: 0; 341 | align-items: center; 342 | border-radius: 50%; 343 | display: flex; 344 | height: 100%; 345 | justify-content: center; 346 | transition: all 200ms; 347 | } 348 | .theme-btn:hover { 349 | background: #ebedf0; 350 | color: var(--primary-fontColor); 351 | } 352 | .imgTransform { 353 | opacity: 1!important; 354 | z-index: 999; 355 | } 356 | .imgTransform img { 357 | transform: scale(2.5, 2.5); 358 | transition: .2s; 359 | } 360 | .previewTip .imgItem { 361 | opacity: 0.5; 362 | } 363 | .imgTransform .thumb { 364 | position: absolute; 365 | z-index: 1; 366 | } 367 | .hide { 368 | width: 0; 369 | height: 0; 370 | display: none!important; 371 | } 372 | ` 373 | GM_addStyle(styleCode) 374 | } 375 | 376 | init_yande_konachan() { 377 | let posts = $('#post-list-posts'); 378 | let postsItems = posts.querySelectorAll('li'); 379 | for (let i = 0; i < postsItems.length; i++) { 380 | postsItems[i].classList.add('imgItem'); 381 | postsItems[i].firstElementChild.firstElementChild.setAttribute('onclick', 'return false'); 382 | const template = `
` 383 | postsItems[i].insertAdjacentHTML('afterbegin', template); 384 | postsItems[i].addEventListener('mouseover', (e) => this.setTransition(e, 'mouseover', i)) 385 | postsItems[i].addEventListener('mouseout', (e) => this.setTransition(e, 'mouseout', i)) 386 | } 387 | posts.addEventListener('click', (e) => this.handleClickImg(e)) 388 | } 389 | 390 | init_danbooru() { 391 | const posts = $('.posts-container') 392 | const postsItems = posts.querySelectorAll('article'); 393 | for (let i = 0; i < postsItems.length; i++) { 394 | postsItems[i].classList.add('imgItem'); 395 | postsItems[i].firstElementChild.setAttribute('onclick', 'return false'); 396 | const template = `
` 397 | postsItems[i].insertAdjacentHTML('afterbegin', template); 398 | postsItems[i].addEventListener('mouseover', (e) => this.setTransition(e, 'mouseover', i)) 399 | postsItems[i].addEventListener('mouseout', (e) => this.setTransition(e, 'mouseout', i)) 400 | } 401 | posts.addEventListener('click', (e) => this.handleClickImg(e)) 402 | } 403 | 404 | init_gelbooru() { 405 | const posts = $('.thumbnail-container') 406 | const postsItems = posts.querySelectorAll('article'); 407 | for (let i = 0; i < postsItems.length; i++) { 408 | postsItems[i].classList.add('imgItem'); 409 | postsItems[i].style.position = 'relative' 410 | postsItems[i].firstElementChild.setAttribute('onclick', 'return false'); 411 | const template = `
` 412 | postsItems[i].insertAdjacentHTML('afterbegin', template); 413 | postsItems[i].addEventListener('mouseover', (e) => this.setTransition(e, 'mouseover', i)) 414 | postsItems[i].addEventListener('mouseout', (e) => this.setTransition(e, 'mouseout', i)) 415 | } 416 | posts.addEventListener('click', (e) => this.handleClickImg(e)) 417 | } 418 | 419 | initMenuEvent() { 420 | const headerEl = $('.board-header') 421 | headerEl.onclick = function (e) { 422 | const boardEl = headerEl.parentNode 423 | if(boardEl.classList.contains('helper-board-small')) { 424 | boardEl.classList.remove('helper-board-small') 425 | } else { 426 | if(e.target.className === 'board-close-button') { 427 | boardEl.classList.add('helper-board-small') 428 | } 429 | } 430 | } 431 | const theme = localStorage.getItem('h-theme') || 'light' 432 | const showToolTip = localStorage.getItem('h-show-tooltip') || '1' 433 | this.setTheme(theme) 434 | this.handleToggleToolTip(showToolTip) 435 | 436 | $('.theme-btn').addEventListener('click', (e) => this.handleToggleTheme(e)) 437 | $('#buttonSelectAll').addEventListener('click', (e) => this.handleClickMenuAllBtn()) 438 | $('#downloadSample').addEventListener('click', (e) => this.handleDownLoadImg(e, 'sample')) 439 | $('#downloadOriginal').addEventListener('click', (e) => this.handleDownLoadImg(e, 'original')) 440 | $('#addFavorite').addEventListener('click', (e) => this.handleFavorite(true)) 441 | $('#removeFavorite').addEventListener('click', (e) => this.handleFavorite(false)) 442 | $('.fav-list-container').addEventListener('click', (e) => this.handleClickFavList(e, 'fav-list-container')) 443 | $('#showToolTipBtn').addEventListener('click', (e) => { 444 | const newShowToolTip = localStorage.getItem('h-show-tooltip') === '1' ? '0' : '1' 445 | this.handleToggleToolTip(newShowToolTip) 446 | }) 447 | } 448 | 449 | initMenuPanel() { 450 | const template = ` 451 |
452 |
453 |
454 |
Booru-Selector-Downloader
455 | 465 |
466 |
X
467 |
H
468 |
469 |
470 |
471 |
Select All 0 Image
472 |
473 |
474 |
Download Sample
475 |
476 |
477 |
Download Original
478 |
479 |
480 |
Add To Favorite
481 |
482 |
483 |
Remove From Favorite
484 |
485 |
486 |
Show Preview ToolTip: [On]
487 |
488 |
489 |
Release Note:
490 | v4.0.1 491 |
492 |
493 |
494 |
495 |
496 | ` 497 | document.body.insertAdjacentHTML('beforeend', template) 498 | this.initMenuEvent() 499 | } 500 | 501 | initHotKey() { 502 | window.addEventListener('keydown', (e) => this.hotKeyHandler(e), false) 503 | window.addEventListener('keyup', (e) => this.handleKeyUp(e), false) 504 | } 505 | 506 | /** 507 | * @param {KeyboardEvent} e 508 | * */ 509 | async hotKeyHandler(e) { 510 | // ignore input event 511 | if(e.target && e.target.tagName === 'INPUT') return 512 | 513 | // press key `A` or `D` to paginate 514 | let pageRight = $('#paginator > div > a.next_page') 515 | let pageLeft = $('#paginator > div > a.previous_page') 516 | if (REgelbooruResult) { 517 | pageRight = $('#paginator b').previousElementSibling 518 | pageLeft = $('#paginator b').nextElementSibling 519 | } 520 | // `A`, `D`: paginate(only for yande.re and danbooru) 521 | if (e.key === 'd' && pageRight) { 522 | pageRight.click() 523 | } 524 | if (e.key === 'a' && pageLeft) { 525 | pageLeft.click() 526 | } 527 | 528 | // `Q`: Select all, Deselect all 529 | if (e.key === 'q') { 530 | this.handleClickMenuAllBtn() 531 | } 532 | 533 | // `F`: Favorite 534 | if (e.key === 'f') { 535 | this.handleFavorite(true) 536 | } 537 | // `R`: Remove from favorites 538 | if (e.key === 'r') { 539 | this.handleFavorite(false) 540 | } 541 | 542 | // `S`: Save sample image 543 | if (e.key === 's') { 544 | this.handleDownLoadImg(null, 'sample').then(res => {}) 545 | } 546 | // `X`: Save original image 547 | if (e.key === 'x') { 548 | this.handleDownLoadImg(null, 'original').then(res => {}) 549 | } 550 | // 'Shift': Show larger image tool tip 551 | if (e.key === 'Shift') { 552 | let posts = null 553 | let el = this.hoverEl 554 | if (el) { 555 | this.hoverEl.classList.add('imgTransform') 556 | } 557 | if (REyandeResult || REkonachanResult) { 558 | posts = $('#post-list-posts'); 559 | } 560 | if (REdanbooruResult) { 561 | posts = $('.posts-container') 562 | if (el) { 563 | const id = Number(el.getAttribute('data-id')) 564 | if (!this.cacheImg.hasOwnProperty(id)) { 565 | this.cacheImg[id] = '' 566 | const imgInfo = await this.fetchDetailPage(id) 567 | const sample = imgInfo.sample 568 | this.cacheImg[id] = sample 569 | el.querySelector('source').srcset = sample 570 | el.querySelector('img').src = sample 571 | } 572 | el.classList.add('imgTransform') 573 | } 574 | } 575 | if (REgelbooruResult) { 576 | posts = $('.thumbnail-container') 577 | if (el) { 578 | const id = Number(el.querySelector('a').getAttribute('id').replace('p', '')) 579 | if (!this.cacheImg.hasOwnProperty(id)) { 580 | this.cacheImg[id] = '' 581 | const imgInfo = await this.fetchDetailPage(id) 582 | const sample = imgInfo.sample 583 | this.cacheImg[id] = sample 584 | el.querySelector('img').src = sample 585 | } 586 | } 587 | } 588 | posts.classList.add('previewTip') 589 | } 590 | } 591 | 592 | /** 593 | * @param {KeyboardEvent} e 594 | * */ 595 | handleKeyUp(e) { 596 | // ignore input event 597 | if(e.target && e.target.tagName === 'INPUT') return 598 | 599 | // 'Shift': close larger image tool tip 600 | if (e.key === 'Shift') { 601 | if (this.hoverEl) { 602 | this.hoverEl.classList.remove('imgTransform') 603 | } 604 | let posts = null 605 | if (REyandeResult || REkonachanResult) { 606 | posts = $('#post-list-posts'); 607 | } 608 | if (REdanbooruResult) { 609 | posts = $('.posts-container') 610 | } 611 | if (REgelbooruResult) { 612 | posts = $('.thumbnail-container') 613 | } 614 | posts.classList.remove('previewTip') 615 | } 616 | } 617 | 618 | handleToggleTheme(e) { 619 | const theme = document.body.getAttribute('data-theme') 620 | theme === 'light' ? this.setTheme('dark') : this.setTheme('light') 621 | } 622 | 623 | /** 624 | * @param {string} theme light | dark 625 | * */ 626 | setTheme(theme) { 627 | if(theme === 'light') { 628 | const lightTheme = [ 629 | {key: 'backgroundColor', value: '#eee'}, 630 | {key: 'lineBackgroundColor', value: '#ccc'}, 631 | {key: 'fontColor', value: '#ee8887'}, 632 | {key: 'fontColorHover', value: '#ff4342'}, 633 | {key: 'headerFontColor', value: '#ffffff'}, 634 | {key: 'border', value: '1px solid #fbe0df'}, 635 | ] 636 | lightTheme.forEach(item => this.setCssVariable(item)) 637 | document.body.setAttribute('data-theme', 'light') 638 | localStorage.setItem('h-theme', 'light') 639 | } else { 640 | const darkTheme = [ 641 | {key: 'backgroundColor', value: '#222'}, 642 | {key: 'lineBackgroundColor', value: '#ccc'}, 643 | {key: 'fontColor', value: '#ee8887'}, 644 | {key: 'fontColorHover', value: '#ffffff'}, 645 | {key: 'headerFontColor', value: '#ffffff'}, 646 | {key: 'border', value: '1px solid #ee8887'}, 647 | ] 648 | darkTheme.forEach(item => this.setCssVariable(item)) 649 | document.body.setAttribute('data-theme', 'dark') 650 | localStorage.setItem('h-theme', 'dark') 651 | } 652 | } 653 | 654 | setCssVariable({ key, value }) { 655 | const propertyName = `--primary-${key}`; 656 | document.documentElement.style.setProperty(propertyName, value); 657 | } 658 | 659 | handleClickMenuAllBtn() { 660 | const btn = $('#buttonSelectAll') 661 | if (this.batchCount >= 1) { 662 | btn.innerHTML = "DeselectAll " + this.batchCount + " Image"; 663 | this.deselectAll() 664 | } else { 665 | btn.innerHTML = "SelectAll " + this.batchCount + " Image"; 666 | this.selectAll() 667 | } 668 | } 669 | 670 | /** 671 | * @param {Event} e 672 | * @param {string} type sample | original 673 | * */ 674 | async handleDownLoadImg(e, type = 'sample') { 675 | const postsItems = $$('.imgItemChecked'); 676 | let imgs = [] 677 | if(postsItems.length) { 678 | this.updateFetchingProgress(0, postsItems.length) 679 | } 680 | for (let i = 0; i < postsItems.length; i++) { 681 | let id = 0 682 | if (REyandeResult || REkonachanResult) { 683 | id = Number(postsItems[i].getAttribute('id').replace('p', '')) 684 | } 685 | if (REdanbooruResult) { 686 | id = Number(postsItems[i].getAttribute('data-id')) 687 | } 688 | if (REgelbooruResult) { 689 | id = Number(postsItems[i].querySelector('a').getAttribute('id').replace('p', '')) 690 | } 691 | const imgInfo = await this.fetchDetailPage(id) 692 | imgs.push({ 693 | id, 694 | url: imgInfo[type], 695 | fileName: `${id}${re3.exec(imgInfo[type])[0]}` 696 | }) 697 | this.updateFetchingProgress(i + 1, postsItems.length) 698 | } 699 | this.createDownloadProgress(imgs) 700 | // downloadPool 701 | await this.downloadPool(imgs) 702 | } 703 | 704 | updateFetchingProgress(i, total) { 705 | const el = $('#fetching-download-row') 706 | if(!el) { 707 | const downloadElContainer = $('.download-row-container') 708 | const template = ` 709 |
710 |
Fetching
711 |
712 |
713 |
714 |
${i}/${total}
715 |
716 | ` 717 | downloadElContainer.insertAdjacentHTML('afterbegin', template) 718 | } else { 719 | const width = (Math.round(i / total * 10000) / 100.00); 720 | el.querySelector('.download-row-line-active').style.width = `${width}%` 721 | el.querySelector('.download-row-percent').innerText = `${i}/${total}` 722 | } 723 | } 724 | 725 | async downloadPool(imgs = []) { 726 | for (let i = 0; i < imgs.length; i++) { 727 | this.updateDownloadProgress(imgs[i]) 728 | } 729 | let pool = [] 730 | for (let i = 0; i < imgs.length; i++) { 731 | const img = imgs[i] 732 | const task = this.downloadHandler(img) 733 | pool.push(task) 734 | task 735 | .then((id) => { 736 | console.log(`${ id } ok`) 737 | }) 738 | .catch((id) => { 739 | console.log(`${ id } error`) 740 | }) 741 | .finally(() => { 742 | pool.splice(pool.indexOf(task), 1) 743 | }) 744 | if (pool.length === this.downloadLimit) { 745 | await Promise.race(pool) 746 | } 747 | } 748 | } 749 | 750 | downloadHandler(img) { 751 | // see GM_download: https://www.tampermonkey.net/documentation.php#GM_download 752 | return new Promise((resolve, reject) => { 753 | const arg = { 754 | url: img.url, 755 | name: img.fileName, 756 | onprogress: (xhr) => { 757 | this.downloadProgress(xhr, img.id, false) 758 | if(Math.floor(xhr.loaded / xhr.total * 100) >= 100) { 759 | resolve(img.id) 760 | } 761 | }, 762 | onload: () => { 763 | resolve(img.id) 764 | this.downloadProgress(null, img.id, true) 765 | }, 766 | onerror: () => { 767 | // still resolve 768 | resolve(img.id) 769 | this.onDownloadError(img) 770 | }, 771 | ontimeout: () => { 772 | // still resolve 773 | resolve(img.id) 774 | this.onDownloadError(img) 775 | } 776 | } 777 | GM_download(arg) 778 | }) 779 | } 780 | 781 | /** 782 | * @param {Array} imgs 783 | * */ 784 | createDownloadProgress(imgs) { 785 | for (let i = 0; i < imgs.length; i++) { 786 | const img = imgs[i] 787 | const pageUrl = this.getPageUrl(img.id) 788 | const downloadRowEl = $(`#download-row-${img.id}`) 789 | if(!downloadRowEl) { 790 | // create downloadRow 791 | const downloadList = $('.download-row-container') 792 | const template = ` 793 |
794 | ${img.id} 795 |
796 |
797 |
798 |
0%
799 |
800 | ` 801 | downloadList.insertAdjacentHTML('beforeend', template) 802 | } 803 | } 804 | } 805 | 806 | updateDownloadProgress({id, url, fileName}) { 807 | const downloadRowEl = $(`#download-row-${id}`) 808 | if(!downloadRowEl) { 809 | // create downloadRow 810 | const downloadList = $('.download-row-container') 811 | const pageUrl = this.getPageUrl(id) 812 | const template = ` 813 |
814 | ${id} 815 |
816 |
817 |
818 |
0%
819 |
820 | ` 821 | downloadList.insertAdjacentHTML('beforeend', template) 822 | } 823 | } 824 | 825 | onDownloadError(img) { 826 | const downloadRowEl = $(`#download-row-${img.id}`) 827 | const el = downloadRowEl.querySelector('.download-row-percent') 828 | if(!el.classList.contains('hover-item')) { 829 | el.classList.add('hover-item') 830 | } 831 | el.innerHTML = reTrySvgIcon 832 | el.onclick = this.downloadHandler(img) 833 | } 834 | 835 | /** 836 | * @param {ProgressEventInit | null} xhr 837 | * @param {number} id 838 | * @param {boolean} isFinished 839 | * */ 840 | downloadProgress(xhr, id, isFinished = false) { 841 | let width = 0 842 | if (xhr === null && isFinished) { 843 | width = 100 844 | } else { 845 | width = xhr.lengthComputable ? Math.floor(xhr.loaded / xhr.total * 100) : 0; 846 | } 847 | 848 | const downloadRowEl = $(`#download-row-${ id }`) 849 | if (!downloadRowEl) { 850 | // create downloadRow 851 | const downloadList = $('.download-row-container') 852 | const pageUrl = this.getPageUrl(id) 853 | const template = ` 854 |
855 | ${ id } 856 |
857 |
858 |
859 |
${ width }%
860 |
861 | ` 862 | downloadList.insertAdjacentHTML('beforeend', template) 863 | } else { 864 | // update downloadRow 865 | downloadRowEl.querySelector('.download-row-title').innerText = id 866 | downloadRowEl.querySelector('.download-row-line-active').style.width = `${ width }%` 867 | downloadRowEl.querySelector('.download-row-percent').innerText = `${ width }%` 868 | } 869 | } 870 | 871 | /** 872 | * @param {number} id 873 | * @return {string} 874 | * */ 875 | getPageUrl(id) { 876 | let pageUrl = `` 877 | if(REyandeResult || REkonachanResult) { 878 | pageUrl = `${locationUrl}/post/show/${id}` 879 | } 880 | if(REdanbooruResult) { 881 | pageUrl = `${locationUrl}/posts/${id}` 882 | } 883 | if(REgelbooruResult) { 884 | pageUrl = `${locationUrl}/index.php?page=post&s=view&id=${id}` 885 | } 886 | return pageUrl 887 | } 888 | 889 | /** 890 | * @param {string} showCode '0' | '1' 891 | * */ 892 | handleToggleToolTip(showCode) { 893 | let preViewEl = null, infoEl = null; 894 | if (REyandeResult || REkonachanResult) { 895 | preViewEl = $('#index-hover-overlay') 896 | infoEl = $('#index-hover-info') 897 | } 898 | if (REdanbooruResult) { 899 | preViewEl = $('#post-tooltips') 900 | } 901 | if (showCode === '0') { 902 | preViewEl && preViewEl.classList.remove('hide') 903 | infoEl && infoEl.classList.remove('hide') 904 | } else if (showCode === '1') { 905 | preViewEl && preViewEl.classList.add('hide') 906 | infoEl && infoEl.classList.add('hide') 907 | } 908 | $('#showToolTipBtn span').innerText = showCode === '0' ? '[OFF]' : '[ON]' 909 | localStorage.setItem('h-show-tooltip', showCode) 910 | } 911 | 912 | /** 913 | * @param {boolean} isLike 914 | * */ 915 | handleFavorite(isLike) { 916 | const postsItems = $$('.imgItemChecked'); 917 | for (let i = 0; i < postsItems.length; i++) { 918 | let id = 0 919 | if (REyandeResult || REkonachanResult) { 920 | id = Number(postsItems[i].getAttribute('id').replace('p', '')) 921 | } 922 | if (REdanbooruResult) { 923 | id = Number(postsItems[i].getAttribute('data-id')) 924 | } 925 | if (REgelbooruResult) { 926 | id = Number(postsItems[i].querySelector('a').getAttribute('id').replace('p', '')) 927 | } 928 | this.fetchFavorite(id, isLike) 929 | } 930 | } 931 | 932 | /** 933 | * @param {number} id 934 | * @param {boolean} isLike 935 | * */ 936 | fetchFavorite(id, isLike) { 937 | let url = `` 938 | let data = {} 939 | 940 | if (REyandeResult || REkonachanResult) { 941 | url = `${ locationUrl }/post/vote.json` 942 | const csrfToken = $("meta[name=csrf-token]").content 943 | data = { 944 | "headers": { 945 | "content-type": "application/x-www-form-urlencoded; charset=UTF-8", 946 | "x-csrf-token": csrfToken, 947 | }, 948 | "body": `id=${ id }&score=${ isLike ? 3 : 2 }`, 949 | "method": "POST", 950 | "mode": "cors", 951 | "credentials": "include" 952 | } 953 | } 954 | 955 | if (REdanbooruResult) { 956 | url = isLike ? `${ locationUrl }/favorites?post_id=${ id }` : `${ locationUrl }/favorites/${id}` 957 | const csrfToken = $("meta[name=csrf-token]").content 958 | data = { 959 | "headers": { 960 | "accept": "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01", 961 | "x-csrf-token": csrfToken, 962 | "x-requested-with": "XMLHttpRequest" 963 | }, 964 | "body": null, 965 | "method": isLike ? "POST" : "DELETE", 966 | "mode": "cors", 967 | "credentials": "include" 968 | } 969 | } 970 | 971 | if (REgelbooruResult) { 972 | url = isLike ? `${ locationUrl }/public/addfav.php?id=${id}` : `${ locationUrl }/index.php?page=favorites&s=delete&id=${id}` 973 | data = { 974 | "headers": { 975 | "accept": "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01", 976 | "x-requested-with": "XMLHttpRequest" 977 | }, 978 | "body": null, 979 | "method": "GET", 980 | "mode": "cors", 981 | "credentials": "include" 982 | } 983 | } 984 | 985 | promiseFetch(url, data) 986 | .then(res => { 987 | if (res.status === 200) { 988 | const log = { 989 | id, 990 | isLike, 991 | result: 'success', 992 | } 993 | this.addFavoriteLog(log) 994 | } 995 | }) 996 | .catch(e => { 997 | const log = { 998 | id, 999 | isLike, 1000 | result: 'error', 1001 | } 1002 | this.addFavoriteLog(log) 1003 | console.error('add favorite error') 1004 | }) 1005 | } 1006 | 1007 | handleClickFavList(e, parentClassName) { 1008 | let el = e.target 1009 | let hasEl = false 1010 | while (el !== document && el.className !== parentClassName) { 1011 | // click re-try-icon to re add favorite 1012 | if (el.className === 're-try-icon') { 1013 | hasEl = true 1014 | break; 1015 | } 1016 | el = el.parentNode 1017 | } 1018 | if(!hasEl) return 1019 | 1020 | const id = Number(el.getAttribute('data-id')) 1021 | const isLike = el.getAttribute('data-isLike') === 'true' 1022 | this.fetchFavorite(id, isLike) 1023 | } 1024 | 1025 | /** 1026 | * @param {number} log.id 1027 | * @param {boolean} log.isLike 1028 | * @param {string} log.result success | error 1029 | * */ 1030 | addFavoriteLog(log) { 1031 | const { id, isLike, result } = log 1032 | const el = $('.fav-list-container') 1033 | const logItemEl = $(`#favLog${id}`) 1034 | if(!logItemEl) { 1035 | const pageUrl = this.getPageUrl(id) 1036 | const elItem = document.createElement('div') 1037 | elItem.className = 'board-content-row' 1038 | elItem.id = `favLog${id}` 1039 | elItem.innerHTML = ` 1040 |
${isLike ? 'Add' : 'Remove'} Favorites:
1041 |
1042 | ${id} 1043 | ${result === 'success' ? 'Success' : 'Error'} 1044 | ${ result === 'error' ? `${reTrySvgIcon}` : '' } 1045 |
` 1046 | el.appendChild(elItem) 1047 | } else { 1048 | logItemEl.querySelector('.row-label').innerText = isLike ? 'Add Favorites: ' : 'Remove Favorites: ' 1049 | logItemEl.querySelector('.fav-state').innerText = result === 'success' ? 'Success' : 'Error' 1050 | let iconEl = logItemEl.querySelector('.re-try-icon') 1051 | if(result === 'success') { 1052 | // remove re-icon 1053 | if(iconEl) iconEl.remove() 1054 | } else if(result === 'error') { 1055 | iconEl 1056 | ? iconEl.setAttribute('data-isLike', String(isLike)) 1057 | : logItemEl.querySelector('.row-content').insertAdjacentHTML('beforeend', `${reTrySvgIcon}`); 1058 | } 1059 | } 1060 | } 1061 | 1062 | /** 1063 | * @interface imgInfo 1064 | * @param {number} id 1065 | * @return {Promise} 1066 | * */ 1067 | fetchDetailPage(id) { 1068 | const link = this.getPageUrl(id) 1069 | return new Promise((resolve, reject) => { 1070 | let imgInfo = { 1071 | sample: '', 1072 | original: '', 1073 | } 1074 | if(REyandeResult || REkonachanResult) { 1075 | promiseFetch(link) 1076 | .then(res => res.text()) 1077 | .then(res => { 1078 | const bodyText = res 1079 | const dom = domParser(bodyText) 1080 | const sampleSrc = dom.querySelector('#image').src 1081 | const originalSrc = dom.querySelector('#highres').href 1082 | imgInfo = { 1083 | sample: sampleSrc, 1084 | original: originalSrc, 1085 | } 1086 | resolve(imgInfo) 1087 | }).catch(e => { 1088 | console.log(e) 1089 | reject(e) 1090 | }) 1091 | } 1092 | if(REdanbooruResult) { 1093 | promiseFetch(link) 1094 | .then(res => res.text()) 1095 | .then(res => { 1096 | const bodyText = res 1097 | const dom = domParser(bodyText) 1098 | const sampleSrc = dom.querySelector('#image').src 1099 | const originalEl = dom.querySelector('.image-view-original-link') 1100 | const originalSrc = originalEl ? originalEl.href : sampleSrc 1101 | imgInfo = { 1102 | sample: sampleSrc, 1103 | original: originalSrc, 1104 | } 1105 | resolve(imgInfo) 1106 | }).catch(e => { 1107 | console.log(e) 1108 | reject(e) 1109 | }) 1110 | } 1111 | 1112 | if(REgelbooruResult) { 1113 | promiseFetch(link) 1114 | .then(res => res.text()) 1115 | .then(res => { 1116 | const bodyText = res 1117 | const dom = domParser(bodyText) 1118 | const sampleSrc = dom.querySelector('#image').src 1119 | const originalEl = dom.querySelector("a[rel='noopener']") 1120 | const originalSrc = originalEl ? originalEl.href : sampleSrc 1121 | imgInfo = { 1122 | sample: sampleSrc, 1123 | original: originalSrc, 1124 | } 1125 | resolve(imgInfo) 1126 | }).catch(e => { 1127 | console.log(e) 1128 | reject(e) 1129 | }) 1130 | } 1131 | }) 1132 | } 1133 | 1134 | handleClickImg(e) { 1135 | let el = e.target 1136 | let hasEl = false 1137 | while (el !== document) { 1138 | if ((REyandeResult || REkonachanResult) && el.tagName.toLowerCase() === 'li') { 1139 | hasEl = true 1140 | break; 1141 | } 1142 | if ((REdanbooruResult || REgelbooruResult) && el.tagName.toLowerCase() === 'article') { 1143 | hasEl = true 1144 | break; 1145 | } 1146 | el = el.parentNode 1147 | } 1148 | if(!hasEl) return; 1149 | 1150 | // press ctrlKey: open in new window, loadInBackground true, won't auto focus 1151 | if(e.ctrlKey) { 1152 | let link = `` 1153 | if(REyandeResult || REkonachanResult) { 1154 | link = el.querySelector('a.thumb').href 1155 | } 1156 | if(REdanbooruResult) { 1157 | link = el.querySelector('a.post-preview-link').href 1158 | } 1159 | if(REgelbooruResult) { 1160 | link = el.querySelector('a').href 1161 | } 1162 | GM_openInTab(link, true) 1163 | return; 1164 | } 1165 | // press altKey: open in new window, loadInBackground false, will auto focus 1166 | if(e.altKey) { 1167 | let link = `` 1168 | if(REyandeResult || REkonachanResult) { 1169 | link = el.querySelector('a.thumb').href 1170 | } 1171 | if(REdanbooruResult) { 1172 | link = el.querySelector('a.post-preview-link').href 1173 | } 1174 | if(REgelbooruResult) { 1175 | link = el.querySelector('a').href 1176 | } 1177 | GM_openInTab(link, false) 1178 | return; 1179 | } 1180 | 1181 | const cbEl = el.getElementsByClassName('checkbox')[0] 1182 | cbEl.checked = !cbEl.checked 1183 | cbEl.checked ? el.classList.add('imgItemChecked') : el.classList.remove('imgItemChecked') 1184 | this.updateBatchCount() 1185 | } 1186 | 1187 | /** 1188 | * @param {MouseEvent} e 1189 | * @param {string} mouseEventName mouseover | mouseout 1190 | * **/ 1191 | async setTransition(e, mouseEventName) { 1192 | let el = e.target 1193 | let hasEl = false 1194 | while (el !== document) { 1195 | if ((REyandeResult || REkonachanResult) && el.tagName.toLowerCase() === 'li') { 1196 | hasEl = true 1197 | break; 1198 | } 1199 | if ((REdanbooruResult || REgelbooruResult) && el.tagName.toLowerCase() === 'article') { 1200 | hasEl = true 1201 | break; 1202 | } 1203 | el = el.parentNode 1204 | } 1205 | if(!hasEl) return; 1206 | 1207 | if(mouseEventName === 'mouseout') { 1208 | el.classList.remove('imgTransform') 1209 | this.hoverEl = null 1210 | } 1211 | if(mouseEventName === 'mouseover') { 1212 | this.hoverEl = el 1213 | } 1214 | } 1215 | 1216 | updateBatchCount() { 1217 | let checked = 0; 1218 | $$('.checkbox').forEach(function (checkbox) { 1219 | if (checkbox.checked) { 1220 | ++checked; 1221 | } 1222 | }); 1223 | this.batchCount = checked; 1224 | const btn = $('#buttonSelectAll') 1225 | if (this.batchCount >= 1) { 1226 | btn.innerHTML = "DeselectAll " + this.batchCount + " Image"; 1227 | } else { 1228 | btn.innerHTML = "SelectAll " + this.batchCount + " Image"; 1229 | } 1230 | } 1231 | 1232 | selectAll() { 1233 | $$('.checkbox').forEach(function (checkbox) { 1234 | checkbox.checked = true; 1235 | checkbox.parentNode.parentNode.classList.add('imgItemChecked'); 1236 | }); 1237 | this.updateBatchCount(); 1238 | } 1239 | 1240 | deselectAll() { 1241 | $$('.checkbox').forEach(function (checkbox) { 1242 | checkbox.checked = false; 1243 | checkbox.parentNode.parentNode.classList.remove('imgItemChecked'); 1244 | }); 1245 | this.updateBatchCount(); 1246 | } 1247 | } 1248 | 1249 | const booruDownloader = new BooruDownloader() 1250 | window.booruDownloader = booruDownloader 1251 | })(); 1252 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Beats0 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NeteaseCloudMusicPic/NeteaseCloudMusicPic.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name 网易云显示封面 3 | // @namespace http://tampermonkey.net/ 4 | // @version 0.3 5 | // @description 网易云显示封面,支持歌单页、每日推荐页、歌手热门50首歌曲页、榜单页 6 | // @author Beats0 7 | // @require https://cdn.staticfile.org/jquery/3.3.1/jquery.min.js 8 | // @home-url https://greasyfork.org/zh-CN/scripts/425209-%E7%BD%91%E6%98%93%E4%BA%91%E6%98%BE%E7%A4%BA%E5%B0%81%E9%9D%A2 9 | // @home-url2 https://github.com/Beats0/scripter/tree/master/NeteaseCloudMusicPic 10 | // @match https://music.163.com/ 11 | // @grant none 12 | // ==/UserScript== 13 | 14 | (function () { 15 | 'use strict'; 16 | 17 | function loadPublicCss() { 18 | const gIframe = document.getElementById('g_iframe') 19 | const gDocument = gIframe.contentDocument 20 | const styleCode = `.m-table .w1 { width: 170px; } .m-table .hd { display: flex; width: 100%; height: 100px; align-items: center; justify-content: center; } .picImg { display: block; width: 100px; height: 100px; } tbody .left, .m-table td { padding-top: 0; padding-bottom: 0; }` 21 | const style = gDocument.createElement('style'); 22 | style.type = 'text/css'; 23 | style.rel = 'stylesheet'; 24 | style.appendChild(gDocument.createTextNode(styleCode)); 25 | const head = gDocument.getElementsByTagName('head')[0]; 26 | head.appendChild(style); 27 | } 28 | 29 | class NeteaseCloudMusicPic { 30 | constructor() { 31 | this.init() 32 | } 33 | 34 | init() { 35 | console.log('init NeteaseCloudMusicPic') 36 | this.initPushStateEvent() 37 | let _this = this 38 | window.addEventListener('locationchange', function () { 39 | _this.getList() 40 | }) 41 | this.getList() 42 | } 43 | 44 | // 获取g_iframe document 45 | getGIframeDocument() { 46 | const gIframe = document.getElementById('g_iframe') 47 | if (gIframe) { 48 | return gIframe.contentDocument 49 | } 50 | } 51 | 52 | // 获取数据 53 | getList() { 54 | const currentUrl = document.location.href 55 | // 歌单页 56 | const playlistUrl = `/playlist?id=` 57 | // 每日推荐页 58 | const recommendPlayListUrl = `/discover/recommend/taste` 59 | // 歌手热门50首歌曲页 60 | const artistHotTopUrl = `/artist?id=` 61 | // 榜单页 62 | const topListUrl = `/toplist?id=` 63 | let id = '' 64 | // 歌单 65 | if (currentUrl.indexOf(playlistUrl) !== -1 || currentUrl.indexOf(topListUrl) !== -1) { 66 | const parsedUrlData = this.parseUrl(); 67 | id = parsedUrlData['id'] 68 | if (id) { 69 | this.loadPlaylist(id) 70 | } 71 | } 72 | // 每日推荐 73 | if (currentUrl.indexOf(recommendPlayListUrl) !== -1) { 74 | this.loadRecommendPlayList() 75 | } 76 | // 歌手热门50首歌曲 77 | if (currentUrl.indexOf(artistHotTopUrl) !== -1) { 78 | const parsedUrlData = this.parseUrl(); 79 | id = parsedUrlData['id'] 80 | if (id) { 81 | this.loadArtistHotTop(id) 82 | } 83 | } 84 | } 85 | 86 | // 歌单 87 | loadPlaylist(id) { 88 | const formData = { 89 | id, 90 | limit: "9999", 91 | n: "9999", 92 | offset: "0", 93 | total: "true", 94 | } 95 | let _this = this 96 | const apiUrl = `/weapi/v6/playlist/detail` 97 | this.fetchData(apiUrl, formData) 98 | .then((res) => res.json()) 99 | .then((res) => { 100 | if (res.code === 200) { 101 | let tracks = res.playlist.tracks || []; 102 | setTimeout(() => { 103 | const gDocument = _this.getGIframeDocument(); 104 | const resId = String(gDocument.querySelector('.u-btni-addply').getAttribute('data-res-id')) 105 | let elLists = gDocument.querySelectorAll('td .hd'); 106 | tracks.forEach((track, index) => { 107 | if(resId !== String(id)) return; 108 | const picUrl = track.al.picUrl; 109 | let albumEl = document.createElement('a'); 110 | albumEl.setAttribute('href', picUrl); 111 | albumEl.setAttribute('target', '_blank'); 112 | let albumImgEl = document.createElement('img'); 113 | albumImgEl.setAttribute('src', `${picUrl}?param=150y150`); 114 | albumImgEl.setAttribute('class', `picImg`); 115 | albumEl.appendChild(albumImgEl); 116 | elLists[index].appendChild(albumEl); 117 | }); 118 | }, 1000); 119 | } 120 | }); 121 | } 122 | 123 | // 每日推荐 124 | loadRecommendPlayList() { 125 | const formData = { 126 | 'limit': '30', 127 | 'offset': '0', 128 | 'total': 'true' 129 | } 130 | let _this = this 131 | const apiUrl = `/weapi/v2/discovery/recommend/songs` 132 | this.fetchData(apiUrl, formData) 133 | .then((res) => res.json()) 134 | .then((res) => { 135 | if (res.code === 200) { 136 | let tracks = res.data.dailySongs || [] 137 | setTimeout(() => { 138 | const gDocument = _this.getGIframeDocument() 139 | let elLists = gDocument.querySelectorAll('td.left .hd') 140 | tracks.forEach((track, index) => { 141 | const picUrl = track.album.picUrl 142 | let albumEl = document.createElement('a'); 143 | albumEl.setAttribute('href', picUrl) 144 | albumEl.setAttribute('target', '_blank') 145 | let albumImgEl = document.createElement('img'); 146 | albumImgEl.setAttribute('src', `${ picUrl }?param=150y150`) 147 | albumImgEl.setAttribute('class', `picImg`) 148 | albumEl.appendChild(albumImgEl) 149 | elLists[index].appendChild(albumEl) 150 | }) 151 | }, 1000); 152 | } 153 | }); 154 | } 155 | 156 | // 歌手热门50首歌曲 157 | loadArtistHotTop(id) { 158 | const formData = { 159 | id, 160 | } 161 | let _this = this 162 | const apiUrl = `/api/artist/top/song?id=${ id }` 163 | this.fetchData(apiUrl, formData) 164 | .then((res) => res.json()) 165 | .then((res) => { 166 | if (res.code === 200) { 167 | let tracks = res.songs 168 | setTimeout(() => { 169 | const gDocument = _this.getGIframeDocument() 170 | let elLists = gDocument.querySelectorAll('td .hd') 171 | 172 | tracks.forEach((track, index) => { 173 | const picUrl = track.al.picUrl 174 | let albumEl = document.createElement('a'); 175 | albumEl.setAttribute('href', picUrl) 176 | albumEl.setAttribute('target', '_blank') 177 | let albumImgEl = document.createElement('img'); 178 | albumImgEl.setAttribute('src', `${ picUrl }?param=150y150`) 179 | albumImgEl.setAttribute('class', `picImg`) 180 | albumEl.appendChild(albumImgEl) 181 | elLists[index].appendChild(albumEl) 182 | }) 183 | }, 1000); 184 | } 185 | }); 186 | } 187 | 188 | // fetch 封装 189 | fetchData(url, formData, method = 'POST') { 190 | const csrf_token = this.getCookieName('__csrf') 191 | if (url.indexOf('?') !== -1) { 192 | url = `https://music.163.com${ url }&csrf_token=${ csrf_token }` 193 | } else { 194 | url = `https://music.163.com${ url }?csrf_token=${ csrf_token }` 195 | } 196 | formData.csrf_token = csrf_token 197 | let queryData = [ 198 | JSON.stringify(formData), 199 | "010001", 200 | "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7", 201 | "0CoJUm6Qyw8W8jud", 202 | ]; 203 | 204 | let a = window.asrsea(...queryData); 205 | return fetch(url, { 206 | headers: { 207 | "content-type": "application/x-www-form-urlencoded", 208 | }, 209 | referrer: "https://music.163.com/", 210 | body: `params=${ encodeURIComponent(a.encText) }&encSecKey=${ encodeURIComponent(a.encSecKey) }`, 211 | method 212 | }) 213 | } 214 | 215 | // 获取cookie 216 | getCookieName(cookieName) { 217 | var c = document.cookie 218 | , cc = "\\b" + cookieName + "=" 219 | , cs = c.search(cc); 220 | if (cs < 0) 221 | return ""; 222 | cs += cc.length - 2; 223 | var result = c.indexOf(";", cs); 224 | if (result < 0) 225 | result = c.length; 226 | return c.substring(cs, result) || "" 227 | } 228 | 229 | // 解析url 230 | parseUrl() { 231 | const href = window.location.href; 232 | const params = href.split('?')[1]; 233 | 234 | let result = {} 235 | 236 | if (params && params !== '') { 237 | result = params.split('&').reduce(function (res, item) { 238 | const parts = item.split('='); 239 | res[parts[0]] = parts[1]; 240 | return res; 241 | }, {}); 242 | } 243 | return result 244 | } 245 | 246 | // 监听 history PushState 247 | initPushStateEvent() { 248 | history.pushState = (f => function pushState() { 249 | var ret = f.apply(this, arguments); 250 | window.dispatchEvent(new Event('pushstate')); 251 | window.dispatchEvent(new Event('locationchange')); 252 | return ret; 253 | })(history.pushState); 254 | 255 | history.replaceState = (f => function replaceState() { 256 | var ret = f.apply(this, arguments); 257 | window.dispatchEvent(new Event('replacestate')); 258 | window.dispatchEvent(new Event('locationchange')); 259 | return ret; 260 | })(history.replaceState); 261 | 262 | window.addEventListener('popstate', () => { 263 | window.dispatchEvent(new Event('locationchange')) 264 | }); 265 | } 266 | } 267 | 268 | let iframe = $("#g_iframe"); 269 | iframe.on('load', function () { 270 | // 每次都加载样式 271 | loadPublicCss() 272 | // 防止重复 273 | if (window.neteaseCloudMusicPic) return 274 | const neteaseCloudMusicPic = new NeteaseCloudMusicPic() 275 | window.neteaseCloudMusicPic = neteaseCloudMusicPic 276 | }); 277 | })(); 278 | 279 | 280 | -------------------------------------------------------------------------------- /NeteaseCloudMusicPic/README.md: -------------------------------------------------------------------------------- 1 | # NeteaseCloudMusicPic 2 | 3 | ## 网易云显示封面 4 | 5 | ## [install](https://greasyfork.org/zh-CN/scripts/425209-%E7%BD%91%E6%98%93%E4%BA%91%E6%98%BE%E7%A4%BA%E5%B0%81%E9%9D%A2) 6 | 7 | - 歌单页 8 | 9 | ![https://steamuserimages-a.akamaihd.net/ugc/1749061546129732541/699D9C5A7321E90E925E72CC9AF1909AB18FE8C9/](https://steamuserimages-a.akamaihd.net/ugc/1749061546129732541/699D9C5A7321E90E925E72CC9AF1909AB18FE8C9/) 10 | 11 | - 每日推荐页 12 | 13 | ![https://steamuserimages-a.akamaihd.net/ugc/1749061546129732908/C7BD7742EF992E73F86FB1DBE9FFA0D8DD8AD68A/](https://steamuserimages-a.akamaihd.net/ugc/1749061546129732908/C7BD7742EF992E73F86FB1DBE9FFA0D8DD8AD68A/) 14 | 15 | - 榜单页 16 | 17 | ![https://steamuserimages-a.akamaihd.net/ugc/1749061546129733102/E0017E7A2210952B906FEA52FACB17762220448E/](https://steamuserimages-a.akamaihd.net/ugc/1749061546129733102/E0017E7A2210952B906FEA52FACB17762220448E/) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # scripter 2 | 一些脚本和工具 3 | 4 | ### 脚本 5 | 6 | 7 | #### [NeteaseCloudMusicPic](https://github.com/Beats0/scripter/tree/master/NeteaseCloudMusicPic) 8 | - 网易云显示封面,支持歌单页、每日推荐页、歌手热门50首歌曲页、榜单页 9 | - 安装脚本 [NeteaseCloudMusicPic](https://greasyfork.org/zh-CN/scripts/425209-%E7%BD%91%E6%98%93%E4%BA%91%E6%98%BE%E7%A4%BA%E5%B0%81%E9%9D%A2) 10 | 11 | 12 | #### [Booru-Selector-Downloader](https://github.com/Beats0/scripter/tree/master/Booru-Selector-Downloader) 13 | - 图站脚本, 主要包含下载、收藏、图片预览等功能, 已支持站点: yandere、konachan、danbooru、gelbooru 14 | - 安装脚本 [Booru-Selector-Downloader](https://greasyfork.org/zh-CN/scripts/371605-booru-selector-downloader) 15 | 16 | #### [yande.re_db](https://beats0.github.io/scripter/yande.re_db/) 17 | - yanere背景图备份 18 | - 安装脚本 [yande.re_db](https://greasyfork.org/zh-CN/scripts/377434-yande-re-db) 19 | 20 | 21 | #### [xiamiAlbum](https://github.com/Beats0/scripter/tree/master/xiamiAlbum) 22 | - 给虾米音乐加上封面预览 23 | - 安装脚本 [xiami-album](https://greasyfork.org/zh-CN/scripts/380761-xiami-album) 24 | 25 | #### [steam-picBed-log](https://github.com/Beats0/scripter/tree/master/steam-picBed-log) 26 | - 用Steam做图床 27 | -------------------------------------------------------------------------------- /steam-picBed-log/README.MD: -------------------------------------------------------------------------------- 1 | ## steam-picbed-log 2 | 3 | [usage](https://github.com/Beats0/Beats0.github.io/blob/master/_posts/2018-01-06-steam-PicBed.markdown) -------------------------------------------------------------------------------- /steam-picBed-log/steam-picBed-log.js: -------------------------------------------------------------------------------- 1 | downloadURI = function (uri, name) { 2 | var link = document.createElement("a"); 3 | link.download = name; 4 | link.href = uri; 5 | document.body.appendChild(link); 6 | link.click(); 7 | document.body.removeChild(link); 8 | delete link; 9 | }; 10 | 11 | makeRequest = function (method, url) { 12 | return new Promise(function (resolve, reject) { 13 | var xhr = new XMLHttpRequest(); 14 | xhr.open(method, url); 15 | xhr.onload = function () { 16 | if (this.status >= 200 && this.status < 300) { 17 | resolve(xhr.responseText); 18 | } else { 19 | reject({ 20 | status: this.status, 21 | statusText: xhr.statusText 22 | }); 23 | } 24 | }; 25 | xhr.onerror = function () { 26 | reject({ 27 | status: this.status, 28 | statusText: xhr.statusText 29 | }); 30 | }; 31 | xhr.send(); 32 | }); 33 | }; 34 | 35 | 36 | var steamImageUrls = []; 37 | 38 | getUrlFromResponse = function (responseText) { 39 | window.resp = responseText; 40 | var linkRegex = new RegExp('https://steamuserimages[^\"]+'); 41 | var url = linkRegex.exec(window.resp)[0]; 42 | steamImageUrls.push(url); 43 | //正则过滤,批量打印原图链接地址 44 | console.log(/^.*?(?=(?:\?|$))/gi.exec(url)[0]); 45 | return url; 46 | }; 47 | 48 | var urls = []; 49 | jQuery('a.profile_media_item.modalContentLink').each(function (index, el) { 50 | urls.push(el.href); 51 | }); 52 | 53 | for (var i = 0; i < urls.length; ++i) { 54 | makeRequest("GET", urls[i]) 55 | .then(function (text) { 56 | var imageUrl = getUrlFromResponse(text); 57 | //是否下载当前图片,默认不开启 58 | // downloadURI(imageUrl, 'image' + i + '.jpg'); 59 | }); 60 | } -------------------------------------------------------------------------------- /xiamiAlbum/xiami.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name xiami album 3 | // @namespace http://tampermonkey.net/ 4 | // @version 0.1 5 | // @description 给虾米歌单添加歌曲封面(新版本) 6 | // @description:en insert album for xiami music(for new version) 7 | // @author Beats0 8 | // @match https://www.xiami.com/favorite/* 9 | // @match https://www.xiami.com/collect/* 10 | // @grant none 11 | // @require https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.min.js 12 | // ==/UserScript== 13 | 14 | (function () { 15 | 'use strict' 16 | let href = window.location.href 17 | let flag = false 18 | const n = document.cookie.match(/(?:^|;\s*)xm_sg_tk=([^;]*)/); const a = n && n[1] 19 | if (!a) return 20 | 21 | const showFavoriteAlbum = () => { 22 | const hasPage = document.querySelector('.rc-pagination-item-active') 23 | const page = hasPage ? Number(document.querySelector('.rc-pagination-item-active').getAttribute('title')) : 1 24 | const userId = href.match(/\d+/)[0] 25 | const params = { 26 | type: 1, 27 | pagingVO: { 28 | page, 29 | pageSize: 100 30 | }, 31 | userId 32 | } 33 | 34 | const r = a.split('_')[0] + '_xmMain_' + '/api/favorite/getFavorites' + '_' + JSON.stringify(params) 35 | const _s = md5(r) 36 | const trs = document.querySelectorAll('.table-container table tbody tr') 37 | if(trs[0].childElementCount !== 5) return 38 | fetch(`https://www.xiami.com/api/favorite/getFavorites?_q=%7B%22type%22:1,%22pagingVO%22:%7B%22page%22:${page},%22pageSize%22:${params.pagingVO.pageSize}%7D,%22userId%22:%22${userId}%22%7D&_s=${_s}`, { 39 | method: 'GET' 40 | }).then(response => response.json()) 41 | .then((data) => { 42 | if (data.code !== 'SUCCESS') { 43 | window.alert('请求失败!') 44 | return 45 | } 46 | const { songs } = data.result.data 47 | songs.forEach((song, index) => { 48 | const albumEl = document.createElement('td') 49 | albumEl.innerHTML = ` 50 |
56 | 57 |
` 58 | trs[index].insertBefore(albumEl, trs[index].childNodes[0]) 59 | }) 60 | }) 61 | } 62 | const showCollectAlbum = () => { 63 | const hasPage = document.querySelector('.rc-pagination-item-active') 64 | const page = hasPage ? Number(document.querySelector('.rc-pagination-item-active').getAttribute('title')) : 1 65 | const listId = Number(href.match(/\d+/)[0]) 66 | const params = { 67 | listId, 68 | pagingVO: { 69 | page, 70 | pageSize: 30 71 | } 72 | } 73 | 74 | const r = a.split('_')[0] + '_xmMain_' + '/api/collect/getCollectSongs' + '_' + JSON.stringify(params) 75 | const _s = md5(r) 76 | const trs = document.querySelectorAll('.table-container table tbody tr') 77 | if(trs[0].childElementCount !== 5) return 78 | fetch(`https://www.xiami.com/api/collect/getCollectSongs?_q=%7B%22listId%22:${listId},%22pagingVO%22:%7B%22page%22:${page},%22pageSize%22:${params.pagingVO.pageSize}%7D%7D&_s=${_s}`, { 79 | method: 'GET' 80 | }).then(response => response.json()) 81 | .then((data) => { 82 | if (data.code !== 'SUCCESS') { 83 | window.alert('请求失败!') 84 | return 85 | } 86 | const { songs } = data.result.data 87 | songs.forEach((song, index) => { 88 | const albumEl = document.createElement('td') 89 | albumEl.innerHTML = ` 90 |
96 | 97 |
` 98 | trs[index].insertBefore(albumEl, trs[index].childNodes[0]) 99 | }) 100 | }) 101 | } 102 | 103 | const switchAlbum = () => { 104 | const collectRe = /ollect/ 105 | const favoriteRe = /favorite/ 106 | if (collectRe.test(href)) { 107 | showCollectAlbum() 108 | } else if (favoriteRe.test(href)) { 109 | showFavoriteAlbum() 110 | } 111 | } 112 | 113 | const clickListener = () => { 114 | const pagers = document.querySelectorAll('.rc-pagination li') 115 | for (let i = 0; i < pagers.length; i++) { 116 | pagers[i].addEventListener('click', () => { 117 | setTimeout(switchAlbum, 2000) 118 | }, false) 119 | } 120 | } 121 | 122 | const injected = () => { 123 | setTimeout(function () { 124 | switchAlbum() 125 | clickListener() 126 | }, 3000) 127 | } 128 | 129 | if(!flag) { 130 | injected() 131 | flag = true 132 | } 133 | 134 | document.addEventListener('click', () => { 135 | const newHref = window.location.href 136 | if(flag && href !== newHref) { 137 | injected() 138 | href = newHref 139 | console.log(href) 140 | } 141 | }) 142 | })() 143 | -------------------------------------------------------------------------------- /yande.re_db/gmDownloader.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name yande.re_db 3 | // @namespace http://tampermonkey.net/ 4 | // @version 1.1 5 | // @description yande.re_db downloader by Beats0 6 | // @author Beats0 7 | // @match *://beats0.github.io/scripter/yande.re_db/* 8 | // @match *://mynovel.life/scripter/yande.re_db/* 9 | // @grant GM_download 10 | // @grant GM_info 11 | // @grant GM.download 12 | // @grant GM.info 13 | // ==/UserScript== 14 | 15 | (function () { 16 | 'use strict'; 17 | const re = /([a-fA-F0-9]{40})/; 18 | 19 | function handleDownload(e) { 20 | if (e.target.id === 'download') { 21 | const saveEl = document.querySelectorAll('.btn')[1] 22 | saveEl.click() 23 | const db = JSON.parse(localStorage.getItem('yande.re_db')) 24 | if(!db || db.length === 0) { 25 | window.alert('please select image!') 26 | } 27 | localStorage.setItem('yande.re_db', JSON.stringify([])) 28 | for (let i = 0; i < db.length; i++) { 29 | GMDownload(db[i], db[i].match(re)[0]) 30 | } 31 | } 32 | } 33 | 34 | // GM downloader 35 | function GMDownload(url, name) { 36 | var arg = { 37 | url: `https://steamuserimages-a.akamaihd.net/ugc/${url}`, 38 | name: `${name}.jpg`, 39 | onprogress: downloadProgress 40 | } 41 | console.log(arg) 42 | GM_download(arg) 43 | } 44 | 45 | // downloader progress 46 | function downloadProgress(xhr) { 47 | try { 48 | if (!xhr.lengthComputable) return; 49 | var stripe = document.getElementById('stripe'), 50 | width = Math.floor(xhr.loaded / xhr.total * 100); 51 | width === 100 ? stripe.style.width = 0 : stripe.style.width = `${width}%` 52 | } catch (e) { 53 | console.error(e); 54 | } 55 | } 56 | 57 | // create progress 58 | var body = document.getElementsByTagName('body')[0]; 59 | var progressEl = document.createElement('div'); 60 | progressEl.innerHTML = ` 61 |
` 71 | body.appendChild(progressEl) 72 | 73 | window.addEventListener('click', handleDownload, false) 74 | })(); -------------------------------------------------------------------------------- /yande.re_db/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | yande.re_db 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 | 19 | Public 20 | R 21 | 22 |
23 |
24 |
25 | Current: {{ 50 * (currentPage -1) + 1 }} ~ {{ 50 * (currentPage -1) + pics.length }}    Total: {{ allPics.length }} 26 |
27 | 48 |
49 |
50 | download mode 51 | 52 | 53 | 61 |
62 |
63 |
64 |
66 |
67 | 68 |
69 |
70 |
71 |
72 |
73 |
74 | Current: {{ 50 * (currentPage -1) + 1 }} ~ {{ 50 * (currentPage -1) + pics.length }}    Total: {{ allPics.length }} 75 |
76 | 97 |
98 |
99 | download mode 100 | 101 | 102 | 110 |
111 |
112 |
113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /yande.re_db/index.js: -------------------------------------------------------------------------------- 1 | window.onload = function () { 2 | const downloadHandler = { 3 | // 创建blob对象 4 | downloadBlob: function (url) { 5 | return new Promise((resolve, reject) => { 6 | const xhr = new XMLHttpRequest(); 7 | xhr.open('GET', url); 8 | xhr.responseType = 'blob'; 9 | 10 | xhr.onload = function () { 11 | if (xhr.status === 200) { 12 | resolve(xhr); 13 | } else { 14 | reject(new Error(xhr.statusText || 'Download failed.')); 15 | } 16 | }; 17 | xhr.onerror = function () { 18 | reject(new Error('Download failed.')); 19 | }; 20 | xhr.send(); 21 | }); 22 | }, 23 | 24 | downloadURL: function (url, name = '') { 25 | const link = document.createElement('a'); 26 | link.download = name; 27 | link.href = url; 28 | if ('download' in document.createElement('a')) { 29 | document.body.appendChild(link); 30 | link.click(); 31 | document.body.removeChild(link); 32 | } else { 33 | // 对不支持download进行兼容 34 | this.downloadClick(link, (link.target = '_blank')); 35 | } 36 | }, 37 | 38 | // clone https://github.com/eligrey/FileSaver.js/blob/master/src/FileSaver.js 39 | downloadClick: function (node) { 40 | try { 41 | node.dispatchEvent(new MouseEvent('click')); 42 | } catch (e) { 43 | const evt = document.createEvent('MouseEvents'); 44 | evt.initMouseEvent( 45 | 'click', 46 | true, 47 | true, 48 | window, 49 | 0, 50 | 0, 51 | 0, 52 | 80, 53 | 20, 54 | false, 55 | false, 56 | false, 57 | false, 58 | 0, 59 | null 60 | ); 61 | node.dispatchEvent(evt); 62 | } 63 | }, 64 | 65 | // 以 blob 形式载文件 66 | downloadFileBlob: function (url, fileName = '') { 67 | return this.downloadBlob(url, fileName) 68 | .then(res => { 69 | const resp = res.response 70 | if (resp.blob) { 71 | return resp.blob(); 72 | } else { 73 | return new Blob([resp]); 74 | } 75 | }) 76 | .then(blob => URL.createObjectURL(blob)) 77 | .then(url => { 78 | this.downloadURL(url, fileName); 79 | URL.revokeObjectURL(url); 80 | }) 81 | .catch(err => { 82 | throw new Error(err.message); 83 | }); 84 | }, 85 | } 86 | 87 | const app = new Vue({ 88 | el: '#app', 89 | data: { 90 | pages: 1, 91 | currentPage: 1, 92 | jumpPage: 1, 93 | showR: false, 94 | showCheck: false, 95 | allPics: [], 96 | pics: [], 97 | checkedArr: [], 98 | }, 99 | created() { 100 | this.fetchData() 101 | }, 102 | methods: { 103 | fetchData() { 104 | let picType = '' 105 | picType = this.showR ? './r.json' : './public.json' 106 | axios.get(picType).then(res => { 107 | this.currentPage = 1 108 | this.pages = Math.ceil(res.data.pics.length / 50) 109 | this.allPics = res.data.pics 110 | this.pics = res.data.pics.slice(0, 50) 111 | }) 112 | }, 113 | handleShowPublic() { 114 | if (!this.showR) return 115 | this.showR = false 116 | this.checkedArr = [] 117 | this.fetchData() 118 | }, 119 | handleShowR() { 120 | if (this.showR) return 121 | this.showR = true 122 | this.checkedArr = [] 123 | this.fetchData() 124 | }, 125 | handleCheck(index, item) { 126 | if (!this.showCheck) { 127 | window.open(`https://steamuserimages-a.akamaihd.net/ugc/${ item }`, '_blank') 128 | return 129 | } 130 | if (this.checkedArr.includes(index)) { 131 | this.checkedArr = this.checkedArr.filter(item => item !== index).sort((a, b) => a - b) 132 | } else { 133 | this.checkedArr = Array.from(new Set([...this.checkedArr, index])).sort((a, b) => a - b) 134 | } 135 | }, 136 | isChecked(index) { 137 | return this.checkedArr.includes(index) 138 | }, 139 | handleSelectAll() { 140 | checkedArr = this.checkedArr.length === this.pics.length ? [] : Array.from({ length: this.pics.length }, (v, i) => i) 141 | this.checkedArr = checkedArr 142 | }, 143 | handleDownload() { 144 | const re = /([a-fA-F0-9]{40})/; 145 | this.checkedArr.forEach(i => { 146 | const picCode = this.pics[i] 147 | const url = `https://steamuserimages-a.akamaihd.net/ugc/${picCode}` 148 | const fileName = `${picCode.match(re)[0]}.png` 149 | downloadHandler.downloadFileBlob(url, fileName).then(res => {}).catch(e => { 150 | console.log(`pic download failed: ${picCode}`) 151 | }) 152 | }) 153 | }, 154 | go(page) { 155 | if (page > this.pages) return 156 | if (page === this.currentPage) return 157 | this.currentPage = page < 1 ? 1 : page 158 | this.checkedArr = [] 159 | this.jumpPage = this.currentPage 160 | this.pics = this.allPics.slice(50 * (this.currentPage - 1), 50 * (this.currentPage)) 161 | } 162 | }, 163 | computed: { 164 | pagesArr() { 165 | let pageNum = this.pages, 166 | index = this.currentPage, 167 | arr = [] 168 | if (pageNum <= 5) { 169 | for (let i = 1; i <= pageNum; i++) { 170 | arr.push(i) 171 | } 172 | return arr 173 | } 174 | if (index <= 2) return [1, 2, 3, 0, pageNum] 175 | if (index >= pageNum - 1) return [1, 0, pageNum - 2, pageNum - 1, pageNum] 176 | if (index === 3) return [1, 2, 3, 4, 0, pageNum] 177 | if (index === pageNum - 2) return [1, 0, pageNum - 3, pageNum - 2, pageNum - 1, pageNum] 178 | return [1, 0, index - 1, index, index + 1, 0, pageNum] 179 | }, 180 | isSelectAll() { 181 | return this.checkedArr.length === this.pics.length ? 'deSelectAll' : 'selectAll' 182 | } 183 | }, 184 | watch: { 185 | currentPage(newVal) { 186 | this.jumpPage = newVal 187 | } 188 | } 189 | }) 190 | } 191 | -------------------------------------------------------------------------------- /yande.re_db/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | box-sizing: border-box; 5 | } 6 | 7 | [v-cloak] { 8 | display: none; 9 | } 10 | 11 | body { 12 | background: #1b2838; 13 | text-align: left; 14 | color: #8F98A0; 15 | font-size: 14px; 16 | margin: 0; 17 | padding: 0; 18 | } 19 | 20 | legend { 21 | display: block; 22 | width: 100%; 23 | max-width: 100%; 24 | padding: 0; 25 | margin-bottom: .4rem; 26 | font-size: 1.2rem; 27 | line-height: inherit; 28 | color: inherit; 29 | white-space: normal; 30 | } 31 | 32 | legend > span { 33 | color: #ffffff; 34 | text-decoration: underline; 35 | } 36 | 37 | legend > span:hover { 38 | color: #007bff; 39 | cursor: pointer; 40 | background-color: transparent; 41 | } 42 | 43 | .container { 44 | margin-right: auto; 45 | margin-left: auto; 46 | padding-left: 15px; 47 | padding-right: 15px; 48 | } 49 | 50 | @media (min-width: 768px) { 51 | .container { 52 | width: 750px; 53 | } 54 | } 55 | 56 | @media (min-width: 992px) { 57 | .container { 58 | width: 970px; 59 | } 60 | } 61 | 62 | @media (min-width: 1200px) { 63 | .container { 64 | width: 1170px; 65 | } 66 | } 67 | 68 | .row { 69 | margin-left: -15px; 70 | margin-right: -15px; 71 | } 72 | 73 | .icon { 74 | width: 1em; 75 | height: 1em; 76 | vertical-align: middle; 77 | fill: currentColor; 78 | overflow: hidden; 79 | } 80 | 81 | .btn { 82 | background-color: #eff3f6; 83 | background-image: linear-gradient(-180deg, #fafbfc, #eff3f6 90%); 84 | -moz-appearance: none; 85 | -moz-user-select: none; 86 | -ms-user-select: none; 87 | -webkit-appearance: none; 88 | -webkit-user-select: none; 89 | appearance: none; 90 | background-position: -1px -1px; 91 | background-repeat: repeat-x; 92 | background-size: 110% 110%; 93 | border: 1px solid rgba(27, 31, 35, .2); 94 | border-radius: .25em; 95 | cursor: pointer; 96 | display: inline-block; 97 | line-height: 20px; 98 | padding: 0 10px; 99 | position: relative; 100 | user-select: none; 101 | vertical-align: middle; 102 | white-space: nowrap; 103 | margin-right: 5px; 104 | } 105 | 106 | .btn.hover, .btn:hover { 107 | background-color: #e6ebf1; 108 | background-image: linear-gradient(-180deg, #f0f3f6, #e6ebf1 90%); 109 | background-position: -.5em; 110 | border-color: rgba(27, 31, 35, .35); 111 | } 112 | 113 | .btn:hover { 114 | background-repeat: repeat-x; 115 | text-decoration: none; 116 | } 117 | 118 | .btn-primary { 119 | background-color: #28a745; 120 | background-image: linear-gradient(-180deg, #34d058, #28a745 90%); 121 | color: #fff; 122 | } 123 | 124 | .btn-primary.hover, .btn-primary:hover { 125 | background-color: #269f42; 126 | background-image: linear-gradient(-180deg, #2fcb53, #269f42 90%); 127 | background-position: -.5em; 128 | border-color: rgba(27, 31, 35, .5); 129 | } 130 | 131 | .pics { 132 | width: calc(100% / 5 - 5px); 133 | float: left; 134 | margin: 1.5px 1.5px; 135 | height: 135px; 136 | position: relative; 137 | } 138 | 139 | .pic-container { 140 | display: block; 141 | overflow: hidden; 142 | } 143 | 144 | .pics:hover, .pics:focus { 145 | outline: 1px solid Highlight; 146 | } 147 | 148 | .pic-checked { 149 | outline: 1px solid Highlight; 150 | } 151 | 152 | input[type="checkbox" i] { 153 | margin: 3px 3px 3px 4px; 154 | } 155 | 156 | a, .whiteLink { 157 | color: #ebebeb; 158 | text-decoration: none; 159 | cursor: pointer; 160 | } 161 | 162 | a.hoverunderline:hover { 163 | text-decoration: underline; 164 | } 165 | 166 | a.underlinedLink, span.underlinedLink { 167 | color: #ebebeb; 168 | text-decoration: underline; 169 | cursor: pointer; 170 | } 171 | 172 | a.whiteLink:hover, .whiteLink:hover, a.linkStandard:hover, a.linkTitle:hover, a.underlinedLink:hover, span.underlinedLink:hover { 173 | color: #66C0F4; 174 | text-decoration: none; 175 | cursor: pointer; 176 | } 177 | 178 | .page-container { 179 | padding: 5px 0; 180 | flex: 1; 181 | display: flex; 182 | flex-direction: row; 183 | justify-content: space-between; 184 | align-items: center; 185 | } 186 | 187 | .action-container { 188 | display: flex; 189 | flex-direction: row; 190 | align-items: center; 191 | justify-content: center; 192 | padding: 5px 0; 193 | margin-bottom: 40px; 194 | } 195 | 196 | .page-info { 197 | display: inline; 198 | } 199 | 200 | .pagingPageLinks { 201 | margin-right: 0; 202 | font-size: 12px; 203 | color: #959595; 204 | display: flex; 205 | align-items: center; 206 | justify-content: center; 207 | } 208 | 209 | .pagingCurrentPage { 210 | padding: 0 3px; 211 | background-color: #c9c9c9; 212 | color: #262627; 213 | } 214 | 215 | .pagingArrowImg { 216 | padding: 0 5px 1px 5px; 217 | } 218 | 219 | a.pagingPageLink, a.pagingPageLink:hover, a.pagingPageLink:visited, a.pagingPageLink:active { 220 | font-size: 12px; 221 | text-decoration: none; 222 | color: #c9c9c9; 223 | padding: 0 3px; 224 | } 225 | 226 | .imgWallItem { 227 | display: block; 228 | border: 1px solid black; 229 | background-color: black; 230 | background-repeat: no-repeat; 231 | background-position: center; 232 | background-size: cover; 233 | position: absolute; 234 | top: 1px; 235 | right: 1px; 236 | bottom: 1px; 237 | left: 1px; 238 | overflow: hidden; 239 | } 240 | 241 | .pager-jump { 242 | float: right; 243 | } 244 | 245 | .jump-input { 246 | display: inline-block; 247 | line-height: 1.5; 248 | font-size: 12px; 249 | border: 1px solid #dcdee2; 250 | color: #515a6e; 251 | background-color: #fff; 252 | background-image: none; 253 | position: relative; 254 | cursor: text; 255 | transition: border .2s ease-in-out, background .2s ease-in-out, box-shadow .2s ease-in-out; 256 | margin: 0 8px; 257 | padding: 1px 7px; 258 | height: 24px; 259 | border-radius: 3px; 260 | width: 50px; 261 | } 262 | .download-mode { 263 | margin-right: 5; 264 | color: #fff; 265 | } 266 | .switch { 267 | display: block; 268 | position: relative; 269 | width: 52px; 270 | height: 22px; 271 | margin-right: 5px; 272 | border: 1px solid #DFDFDF; 273 | outline: 0; 274 | border-radius: 11px; 275 | box-sizing: border-box; 276 | background-color: #DFDFDF; 277 | transition: background-color 0.1s, border 0.1s; 278 | cursor: pointer; 279 | } 280 | 281 | 282 | .switch:before { 283 | content: " "; 284 | position: absolute; 285 | top: 0; 286 | left: 0; 287 | width: 50px; 288 | height: 22px; 289 | border-radius: 11px; 290 | background-color: #eff3f6; 291 | transition: transform 0.35s cubic-bezier(0.45, 1, 0.4, 1); 292 | } 293 | .switch:after { 294 | content: " "; 295 | position: absolute; 296 | top: 0; 297 | left: 0; 298 | width: 22px; 299 | height: 22px; 300 | border-radius: 11px; 301 | background-color: #FFFFFF; 302 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4); 303 | transition: transform 0.35s cubic-bezier(0.4, 0.4, 0.25, 1.35); 304 | } 305 | .switch-on { 306 | border-color: #1AAD19; 307 | background-color: #60cc79; 308 | background-image: linear-gradient(-180deg, #34d058, #4bc867 90%); 309 | } 310 | .switch-on:before { 311 | border-color: #1AAD19; 312 | background-color: #28a745; 313 | background-image: linear-gradient(-180deg, #34d058, #28a745 90%); 314 | } 315 | .switch-on:after { 316 | transform: translateX(28px); 317 | } --------------------------------------------------------------------------------