├── .github └── FUNDING.yml ├── 18comic_edited.user.js ├── 4ch_b64_tool.user.js ├── README.md ├── aegiswiki.user.js ├── all_link_to_new_tab.user.js ├── anti-bili-anti-copy.user.js ├── autoeq_to_easyq.user.js ├── avgle_m3u8_extractor.user.js ├── az_cn_wiki_fleet_tech_tool.user.js ├── azurlanesd_renamer.user.js ├── display_actual_volume.user.js ├── dlsite_title_reformat.user.js ├── ehx_direct_download.user.js ├── ehx_link_color.user.js ├── ehx_torrent_text.user.js ├── fc2_show_all_products.user.js ├── google_drive_autoclick.user.js ├── mangaoh_title_reformat.user.js ├── newgrounds_tool.user.js ├── nexusmods_skip_countdown.user.js ├── open_discord_link_in_app.user.js ├── oshiro_wiki.user.js ├── ph_user_video.user.js ├── pixiv_blacklist.user.js ├── pixiv_reverse_proxy.user.js ├── prts_redirector.user.js ├── ptt_chrome_blacklist_autosave.user.js ├── ptt_web_image_fix.user.js ├── reddit_img_relink.user.js ├── rule34_tool.user.js ├── sharer-pw_auto_click.user.js ├── skip_gamekee_popup.user.js ├── taipower_tool.user.js └── ytb_url_normalizer.user.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | ko_fi: x94fujo6 4 | -------------------------------------------------------------------------------- /18comic_edited.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name 18comic漫画下载edited 3 | // @namespace http://github.com/eternalphane/Userscripts/ 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/18comic_edited.user.js 5 | // @version 1.0.5.9 6 | // @description 从18comic上下载cbz格式(整话阅读)或webp格式(分页阅读)的漫画 7 | // @author eternalphane (edit by x94fujo6) 8 | // @license MIT 9 | // @match https://18comic.vip/photo/* 10 | // @match https://18comic.org/photo/* 11 | // @match https://18comic.bet/photo/* 12 | // @match https://18comic1.one/photo/* 13 | // @match https://18comic2.one/photo/* 14 | // @connect cdn-msp.18comic.vip 15 | // @connect cdn-msp.18comic.org 16 | // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js 17 | // @require https://unpkg.com/jszip@3.5.0/dist/jszip.min.js 18 | // @resource css https://raw.githubusercontent.com/eternalphane/UserScripts/master/18comic%20Downloader/overlay.css 19 | // @resource html https://raw.githubusercontent.com/eternalphane/UserScripts/master/18comic%20Downloader/overlay.html 20 | // @grant GM_getResourceText 21 | // @grant GM.getResourceText 22 | // @grant GM_addStyle 23 | // @grant GM.addStyle 24 | // @grant GM_xmlhttpRequest 25 | // @grant GM.xmlHttpRequest 26 | // ==/UserScript== 27 | 28 | (async () => { 29 | let btn = document.querySelector('.menu-bolock i.fa-download').parentElement; 30 | if (location.search) { 31 | btn.addEventListener('click', async e => { 32 | e.preventDefault(); 33 | const page = document.querySelector('div[id*=".jpg"],div[id*=".webp"]'); 34 | save( 35 | await download(page.querySelector('img').getAttribute('data-original')), 36 | `${page.id.slice(0, -4)}.webp` 37 | ); 38 | }); 39 | return; 40 | } 41 | GM.addStyle(await GM.getResourceText('css')); 42 | 43 | let overlay = document.createElement('div'); 44 | overlay.id = 'dl-overlay'; 45 | overlay.innerHTML = await GM.getResourceText('html'); 46 | overlay.hidden = true; 47 | document.body.appendChild(overlay); 48 | 49 | let circle = overlay.querySelectorAll('circle')[1], 50 | number = overlay.querySelector('span'), 51 | msg = overlay.querySelector('h2'), 52 | updateProgress = (percent, text) => { 53 | circle.style.strokeDasharray = `${percent} 100`; 54 | number.innerText = Math.round(percent); 55 | if (text != undefined) msg.innerText = text; 56 | }; 57 | 58 | btn.addEventListener('click', async e => { 59 | e.preventDefault(); 60 | if (!overlay.hidden) { 61 | return; 62 | } 63 | overlay.hidden = false; 64 | document.body.classList.add('noscroll'); 65 | 66 | const pages = [...document.querySelectorAll('div[id*=".jpg"],div[id*=".webp"]')]; 67 | const total = pages.length; 68 | 69 | let progress = 0; 70 | updateProgress(0, 'Downloading...'); 71 | 72 | const zip = new JSZip(); 73 | await Promise.all(pages.map(async page => { 74 | zip.file( 75 | `${page.id.slice(0, -4)}.webp`, 76 | await download(page.querySelector('img').getAttribute('data-original')) 77 | ); 78 | updateProgress(++progress * 100 / total); 79 | })); 80 | updateProgress(0, 'Compressing...'); 81 | 82 | // TODO: Select output format? (cbz, cbt, pdf) 83 | save( 84 | await zip.generateAsync( 85 | { 86 | type: 'blob', 87 | compression: 'DEFLATE', 88 | compressionOptions: { level: 9 }, 89 | mimeType: 'application/vnd.comicbook+zip' 90 | }, (meta) => updateProgress(meta.percent) 91 | ), 92 | `${document.querySelector('.panel-heading .pull-left').textContent.trim()}.cbz` 93 | ); 94 | document.body.classList.remove('noscroll'); 95 | overlay.hidden = true; 96 | }); 97 | 98 | (() => { 99 | let class_name = "scramble-page", 100 | ele = document.querySelector(`.${class_name}`); 101 | if (ele) { 102 | [...ele.parentNode.children] 103 | .forEach(e => { 104 | if (!(e.classList.contains(class_name))) { 105 | e.remove(); 106 | } 107 | }); 108 | } 109 | })(); 110 | 111 | function get_num(aid, img_index) { 112 | let n = md5(`${aid}${img_index}`).slice(-1).charCodeAt(0); 113 | if (aid < 268850) return 10; 114 | if (aid >= 268850 && aid <= 421925) n %= 10; 115 | if (aid >= 421926) n %= 8; 116 | return ((n % 10) + 1) * 2; 117 | } 118 | 119 | function get_img(url) { 120 | url = url.replace("-msp2", "-msp"); 121 | return new Promise((resolve, reject) => { 122 | GM.xmlHttpRequest( 123 | { 124 | url, 125 | method: 'GET', 126 | responseType: 'blob', 127 | onload: resolve, 128 | onerror: reject 129 | } 130 | ); 131 | }); 132 | } 133 | 134 | const download = async url => { 135 | let img = new Image(), 136 | img_id = url.match(/\d+\..+$/)[0], 137 | img_index = img_id.split(".")[0], 138 | img_data, 139 | num = get_num(aid, img_index), 140 | canvas = document.createElement('canvas'), 141 | canvas_2d = canvas.getContext('2d'), 142 | img_nwidth = 0, 143 | img_nheight = 0, 144 | sWidth = 0; 145 | 146 | img_data = await get_img(url); 147 | img_data = img_data.response; 148 | img.src = URL.createObjectURL(img_data); 149 | await new Promise((resolve, reject) => (img.onload = resolve, img.onerror = reject)); 150 | img_nwidth = img.naturalWidth; 151 | img_nheight = img.naturalHeight; 152 | sWidth = img_nwidth; 153 | canvas.width = img_nwidth; 154 | canvas.height = img_nheight; 155 | 156 | // `aid`, `scramble_id` and `md5` are both global variables 157 | if (url.includes('.gif') || aid < scramble_id) { 158 | canvas_2d.drawImage(img, 0, 0); 159 | } else { 160 | let naturalHeight = parseInt(img_nheight % num); 161 | for (i = 0; i < num; i++) { 162 | let sHeight = Math.floor(img_nheight / num), 163 | dy = sHeight * i, 164 | sy = img_nheight - sHeight * (i + 1) - naturalHeight; 165 | if (i == 0) { 166 | sHeight += naturalHeight; 167 | } else { 168 | dy += naturalHeight; 169 | } 170 | canvas_2d.drawImage(img, 0, sy, sWidth, sHeight, 0, dy, sWidth, sHeight); 171 | } 172 | } 173 | 174 | URL.revokeObjectURL(img.src); 175 | // TODO: Select image type? Change quality? 176 | return new Promise(resolve => canvas.toBlob(resolve, 'image/webp', 0.9)); 177 | }; 178 | 179 | const save = (blob, filename) => { 180 | const a = document.createElement('a'); 181 | a.href = URL.createObjectURL(blob); 182 | a.download = filename; 183 | a.click(); 184 | URL.revokeObjectURL(a.href); 185 | }; 186 | })(); 187 | -------------------------------------------------------------------------------- /4ch_b64_tool.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name 4chan Base64 Decode Tool 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/4ch_b64_tool.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/4ch_b64_tool.user.js 6 | // @version 0.01 7 | // @description appears in the thread page. auto recursive decoding if the message is coded multiple times. 8 | // @author x94fujo6 9 | // @match https://boards.4chan.org/*/thread/* 10 | // @match https://boards.4channel.org/*/thread/* 11 | // ==/UserScript== 12 | /* jshint esversion: 9 */ 13 | 14 | (function () { 15 | let 16 | elem = { 17 | tool: "decode_tool", 18 | in: "decode_input", 19 | out: "decode_output", 20 | decode: "decode_shit", 21 | copy: "decode_copy", 22 | open: "decode_open", 23 | close: "decode_close", 24 | }, 25 | box = document.createElement("div"), 26 | class_list = { 27 | title: "usTitle", 28 | box: "usBox", 29 | t: "usTextBox", 30 | b: "usButton line-2-item", 31 | }, 32 | getIn = () => document.getElementById(elem.in).value, 33 | getOut = () => document.getElementById(elem.out).value, 34 | setIn = (t = "") => document.getElementById(elem.in).value = t, 35 | setOut = (t = "") => document.getElementById(elem.out).value = t, 36 | decode_text = () => { 37 | let text = getIn(), 38 | count = 0, 39 | max = 100, 40 | [err, output] = decoder(text), 41 | last_output = output, 42 | err_msg = "decode failed"; 43 | setIn(); 44 | console.log(count, text); 45 | if (err) { 46 | setOut(err_msg); 47 | console.log(err_msg); 48 | } 49 | while (!err && output.length && count < max) { 50 | count++; 51 | console.log(count, last_output); 52 | setOut(last_output); 53 | [err, output] = decoder(last_output); 54 | if (err) { 55 | console.log("end loop"); 56 | break; 57 | } else { 58 | setIn(last_output); 59 | last_output = output; 60 | } 61 | } 62 | 63 | function decoder(_t = "") { 64 | let out = false, 65 | error = false; 66 | try { 67 | out = atob(_t); 68 | } catch (e) { 69 | error = e; 70 | } finally { 71 | return [error, out]; 72 | } 73 | } 74 | }, 75 | copy = () => { 76 | let text = document.getElementById(elem.out); 77 | text.select(); 78 | text.setSelectionRange(0, 99999); 79 | document.execCommand("copy"); 80 | }, 81 | open = () => document.getElementById(elem.tool).style.display = "", 82 | close = () => document.getElementById(elem.tool).style.display = "none"; 83 | 84 | box.className = class_list.box; 85 | box.innerHTML = ` 86 |
Base64 Decode Tool
87 | 93 | 94 | 95 | `; 96 | addCss(); 97 | document.body.appendChild(box); 98 | document.getElementById(elem.decode).onclick = () => decode_text(false); 99 | document.getElementById(elem.copy).onclick = () => copy(); 100 | document.getElementById(elem.open).onclick = () => open(); 101 | document.getElementById(elem.close).onclick = () => close(); 102 | 103 | function addCss() { 104 | let s = document.createElement("style"); 105 | document.head.appendChild(s); 106 | s.textContent = ` 107 | .usTitle { 108 | display: block; 109 | font-size: large; 110 | margin: auto; 111 | width: 20rem; 112 | padding: 0.5rem; 113 | } 114 | 115 | .usBox { 116 | position: fixed; 117 | bottom: 1rem; 118 | right: 1rem; 119 | z-index: 100; 120 | border: 2px ridge rgba(0, 0, 0, 0.3); 121 | background-color: rgba(255, 255, 255, 0.75); 122 | display: block; 123 | width: max-content; 124 | height: max-content; 125 | text-align: center; 126 | } 127 | 128 | .usTextBox { 129 | display: flex; 130 | width: 95%; 131 | height: 35%; 132 | margin: 0.2rem auto; 133 | word-break: break-all; 134 | } 135 | 136 | .usButton { 137 | display: inline; 138 | margin: 0.25rem; 139 | padding: 0.5rem; 140 | text-align: center; 141 | font-size: large; 142 | } 143 | 144 | .line-2-item { 145 | max-width: 100%; 146 | width: calc(90% / 2); 147 | } 148 | `; 149 | } 150 | })(); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | If you are using Tampermonkey, it might be a problem after the recent update of the extension. 2 | Scripts does not load regularly and needs to be refresh the page several times. 3 | [Tampermonkey/tampermonkey#1617](https://github.com/Tampermonkey/tampermonkey/issues/1617) 4 | 5 | The end of user scripts 6 | [https://developer.chrome.com/docs/extensions/mv3/mv2-sunset/](https://developer.chrome.com/docs/extensions/mv3/mv2-sunset/) 7 | [https://developer.chrome.com/docs/extensions/mv3/intro/mv3-migration/#remotely-hosted-code](https://developer.chrome.com/docs/extensions/mv3/intro/mv3-migration/#remotely-hosted-code) 8 | [https://github.com/Tampermonkey/tampermonkey/issues/644](https://github.com/Tampermonkey/tampermonkey/issues/644) 9 | Remotely hosted code is no longer allowed; 10 | an extension can only execute JavaScript that is **included within its package**. 11 | 12 | # SomeTampermonkeyScripts 13 | some scripts I made 14 | click link to install 15 | 16 | | script | install | 17 | | ------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | 18 | | [ehx direct download](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts#ehx-direct-download) | [raw](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/ehx_direct_download.user.js) | 19 | | [ehx link color](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts#ehx-link-color) | [raw](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/ehx_link_color.user.js) | 20 | | [ehx torrent text](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts#ehx-torrent-text) | [raw](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/ehx_torrent_text.user.js) | 21 | | [dlsite title reformat](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts#dlsite-title-reformat) | [raw](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/dlsite_title_reformat.user.js) | 22 | | [mangaoh title reformat](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts#mangaoh-title-reformat) | [raw](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/mangaoh_title_reformat.user.js) | 23 | | [pornhub user video](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts#pornhub-user-video) | [raw](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/ph_user_video.user.js) | 24 | | ~~[youtube url normalizer](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts#youtube-url-normalizer)~~ | [raw](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/ytb_url_normalizer.user.js) | 25 | | [prts redirector](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts#prts-redirector) | [raw](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/prts_redirector.user.js) | 26 | | [sharer.pw auto click](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts#sharerpw-auto-click) | [raw](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/sharer-pw_auto_click.user.js) | 27 | | [google drive auto click](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts#google-drive-auto-click) | [raw](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/google_drive_autoclick.user.js) | 28 | | [reddit img relink](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts#reddit-img-relink) | [raw](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/reddit_img_relink.user.js) | 29 | | [anti-bili-anti-copy](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts#anti-bili-anti-copy) | [raw](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/anti-bili-anti-copy.user.js) | 30 | | [fc2 show all products](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts#fc2-show-all-products) | [raw](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/fc2_show_all_products.user.js) | 31 | | [nexusmods skip countdown](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts#nexusmods-skip-countdown) | [raw](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/nexusmods_skip_countdown.user.js) | 32 | | [avgle m3u8 extractor](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts#avgle-m3u8-extractor) | [raw](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/avgle_m3u8_extractor.user.js) | 33 | | [newgrounds tool](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts#newgrounds-tool) | [raw](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/newgrounds_tool.user.js) | 34 | | [AutoEQ to EasyQ converter](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts#autoeq-to-easyq-converter) | [raw](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/autoeq_to_easyq.user.js) | 35 | | [4chan Base64 Decode Tool](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts#4chan-base64-decode-tool) | [raw](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/4ch_b64_tool.user.js) | 36 | | [pixiv blacklist](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts#pixiv-blacklist) | [raw](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/pixiv_blacklist.user.js) | 37 | 38 | ## [[ehx direct download]](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/ehx_direct_download.user.js) 39 | **only work in Thumbnail mode** 40 | 41 | click button to enable (pervent too many requests) 42 | ![](https://i.imgur.com/tAjehJl.png) 43 | 44 | #### Features: 45 | 46 | - #### Memory `downloaded / marked` galleries 47 | - (when click `Archive Download` or use `mark/unmark gallery` under gallery) 48 | - change `downloaded / marked` gallery color to black (quick identify in list) 49 | - #### Enable Archive Download / Sorting / Show torrents Title / Fix Event in Ttile 50 | - ##### add button under gallery: 51 | - archive download 52 | - copy title (auto replace forbidden characters `<>:"/|?*\` to full-width) 53 | - mark/unmark gallery 54 | - show gallery's torrent list in pure text 55 | - ##### sorting gallery 56 | - example: `(aaaaaaaa) [bbbbbbbb] cccccccc (dddddddd)` 57 | - Title (ignore Prefix/Group/End) => `cccccccc` 58 | - Title (ignore Prefix/Group) => `cccccccc (dddddddd)` 59 | - Title (ignore Prefix) => `[bbbbbbbb] cccccccc (dddddddd)` 60 | - Title => `(aaaaaaaa) [bbbbbbbb] cccccccc (dddddddd)` 61 | - Event => `(aaaaaaaa)` 62 | - ##### fix/unfix event in title (auto enable by default) 63 | - search event prefix in `torrent` / `same title gallery` and add to title 64 | - if no 100% match found, try similarity search (disabled by default) 65 | - threshold (edit code if you have issue) 66 | ```js 67 | let sim_search_threshold = 0.6 68 | ``` 69 | - priority: `title_jpn` > `title_en` > `torrent` > `same title gallery` 70 | - highlight prefix [(github doesn't support color in 2020...okay...)](https://github.com/github/markup/issues/369) 71 | - from torrents (![](https://via.placeholder.com/15/008000/000000?text=+) green) 72 | - from other gallery (![](https://via.placeholder.com/15/8A2BE2/000000?text=+) blueviolet) 73 | - some prefix will be ignore: (already categorized) 74 | ``` 75 | "(同人誌)", 76 | "(成年コミック)", 77 | "(成年コミック・雑誌)", 78 | "(一般コミック)", 79 | "(一般コミック・雑誌)", 80 | "(エロライトノベル)", 81 | "(ゲームCG)", 82 | "(同人ゲームCG)", 83 | "(18禁ゲームCG)", 84 | "(同人CG集)", 85 | "(画集)", 86 | ``` 87 | - ##### options 88 | - sort order (descending by default) 89 | - auto copy title when download (enabled by default) 90 | - auto show pure text (enabled by default) 91 | - auto fix event in title (enabled by default) 92 | - ##### sort options 93 | - Numeric `Whether numeric collation should be used, such that "1" < "2" < "10"` 94 | - Ignore Punctuation 95 | - #### Show Pure Text 96 | - add pure text title under gallery (full title) 97 | - #### Jump To Nearest Downloaded (if any) 98 | - #### other 99 | - make all gallery link open in new tab (pervent click on accident) 100 | - make all gallery show entire title 101 | 102 | ### updates 103 | 104 | - v1.15 105 | - add sort 106 | - Page Count 107 | - Rating 108 | - Uploader 109 | 110 | - v1.14 111 | - If the gallery is unavailable 112 | ![](https://i.imgur.com/P4Tn9ho.png) 113 | 114 | - v1.13 115 | - new button style 116 | - change save format (auto migrate) 117 | 118 | - v1.12 119 | - separate jp/en sorting 120 | 121 | - v1.11 122 | - improve sort speed 123 | - fix tag system 124 | - sort by group/circle/artist 125 | - exclude by tag 126 | 127 | - v1.08 128 | - fix addToDownloadedList use wrong limit 129 | 130 | - v1.07 131 | - set default downloaded/marked gallery list size limit to 0 (no limit) 132 | ```js 133 | let gallery_data_max_size = 0; // kb, 0 = no limit 134 | ``` 135 | 136 | - v1.05 137 | - at gallery page, add a link to goto e-h/ex 138 | 139 | - v1.04 140 | - `fix event title` now show the real source 141 | - same title gallery `A B C D`, only `A` have event, `B` get event from `A` 142 | then: 143 | - old 144 | `C` get event from `B` and record it is from `B` 145 | `D` get event from `C` and record it is from `C` 146 | - new 147 | `C` get event from `B` and record it is from `A` 148 | `D` get event from `C` and record it is from `A` 149 | 150 | - v1.02 151 | - fix a bug in title pre-process 152 | ``` 153 | example: (aaa) [bbb] ccc[ddd]eee (fff) 154 | old: ccceee 155 | now: cccdddeee 156 | ``` 157 | - change some code 158 | 159 | - v1.01 160 | - fix script not work in eh (css not found) 161 | - quick check prefix source 162 | - display which gallery the prefix is from under the gallery 163 | - move mouse on top will show the image 164 | 165 | - v0.99 166 | - fix some issue in `show torrent list` 167 | - decode HTML text (like `'`) 168 | - separate each line 169 | - disable similarity search by default 170 | pre-processed title is good enough, and it seems that no match can pass the final test anyway 171 | in some cases, even humans are confused 172 | ```js 173 | //after pre-process they all will be "title" and match 174 | "[group] title (anime series)" 175 | "[group] title (different anime series)" 176 | "[group] title [uploader description]" 177 | "[group] title (uploader description)" 178 | "(some uploader description) title" 179 | 180 | //if not pre-process, these won't match 181 | "[group] title" 182 | "[group] title [uploader description]" 183 | "[group] title (anime series)" 184 | "title" 185 | ``` 186 | maybe add similarity search result under gallery like `torrent list` for quick check in the future 187 | 188 | - v0.97 189 | - add mask test at end so that `oooAAooo` != `oooBBooo` even have high similarity 190 | 191 | - v0.96 192 | - pre-process titles for compare instead of before every compare 193 | 194 | - v0.95 195 | - improve similarity compare 196 | - before compare: 197 | - convert fullwidth to halfwidth 198 | - convert to lower case 199 | - remove all punctuation 200 | 201 | - v0.94 202 | - improve prefix search 203 | - reduce unnecessary `data extract` 204 | - reduce unnecessary `similarity compare`(very slow) 205 | - fix some bug in similarity search 206 | - try prevent script's input element submit data 207 | 208 | - v0.91 209 | - improve prefix search 210 | - try to compare every number in title, abort if not the same 211 | 212 | - v0.88 213 | - improve prefix search 214 | - if no 100% match found, use similarity search and use highest one 215 | 216 | - v0.86 217 | - fixed buttons position, now they all line up 218 | 219 | - v0.85 220 | - new feature `exclude gallery` (opacity = 0.1) by tag 221 | - add/remove tag from exclude list under gallery (after enable `Show Exclude List`) 222 | - tag that already in the list will be red 223 | 224 | - v0.84 225 | - add options for sorting 226 | 227 | - v0.83 228 | - better event search (search in same en or same jpn title) 229 | 230 | - v0.81 231 | - auto replace forbidden characters `<>:"/|?*\` to full-width when copy title 232 | 233 | - v0.79 234 | - highlight prefix 235 | - from torrents (![](https://via.placeholder.com/15/008000/000000?text=+) green) 236 | - from other gallery (![](https://via.placeholder.com/15/8A2BE2/000000?text=+) blueviolet) 237 | [(github doesn't support color in 2020...okay...)](https://github.com/github/markup/issues/369) 238 | - fix text color when at eh 239 | 240 | - v0.78 241 | - option for auto enable `Show Pure Text` 242 | 243 | - v0.76 244 | - add option for auto enable `Fix/Unfix Event in Title` 245 | - now script save option settings 246 | 247 | - v0.75 248 | - add option: auto copy title when click `Archive Download` 249 | 250 | - v0.74 251 | - show full title 252 | - ~~auto enable `Fix/Unfix Event in Title`~~ (optional v0.76) 253 | - add sort by `JP(Recommended)` & `EN` title 254 | (sort still have some bug...) 255 | 256 | - v0.72 257 | - use jpn prefix first 258 | - priority: title_jpn > title_en > torrent > same title gallery 259 | - add `copy title` button 260 | 261 | - v0.71 262 | - new feature `Fix/Unfix Event in Title` 263 | - more sort option 264 | - improve speed (send next request when current one complete) 265 | 266 | - v0.65 267 | - natural sort 268 | 269 | - v0.63 270 | - improve sorting (now sorting is based on previous sort result) 271 | 272 | - v0.61 273 | - new feature `mark/unmark gallery` 274 | - rearrange code 275 | 276 | - v0.55 277 | - new feature `Jump To Nearest Downloaded` (if any) 278 | - mark downloaded gallery color in black 279 | - disable send request to server when click archive download 280 | 281 | - v0.53 282 | - add more sort option 283 | 284 | - v0.51 285 | - after enable `archive download` you can sort gallery (current page) 286 | - show gallery's torrent title list in pure text 287 | - make all gallery link open in new tab (pervent click on accident) 288 | - check every few seconds that is gallery (current page) downloaded or not and change button status 289 | 290 | - v0.43 291 | - now it will update the list when you click archive download in gallery page 292 | add new features when click archive download in list view: 293 | 1. ~~send a request to server as you visited the gallery (not sure if this count)~~ 294 | 2. add gallery link to history ([HTML5 API](https://developer.mozilla.org/en-US/docs/Web/API/History)) `trigger visited css style` 295 | 296 | - v0.35 297 | - list limit up to 10000 298 | 299 | - v0.32 300 | - switch from cookie to [Tampermonkey API storage](https://www.tampermonkey.net/documentation.php) 301 | - if you use any [v0.30] or [v0.31] or you got [400 Bad request] 302 | use cookie editor like EditThisCookie to remove all "exhddl_list" 303 | 304 | - v0.30 305 | - now script save recent downloaded gallerys in a list and set button as downloaded if it in the list 306 | - if reach the limit, it will delete the oldest data until lower the limit 307 | 308 | ## [[ehx link color]](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/ehx_link_color.user.js) 309 | change visited & unvisited link color 310 | if you don't like default color, change it to any valid CSS color you want 311 | ```js 312 | // unvisited link color 313 | let enable_link = true; 314 | let ex = "DeepPink"; 315 | let eh = "DeepPink"; 316 | // visited link color 317 | let enable_visited = true; 318 | let ex_v = "gray"; 319 | let eh_v = "gray"; 320 | ``` 321 | because of the security risk, visited link color many not work in some browser 322 | see [https://dbaron.org/mozilla/visited-privacy](https://dbaron.org/mozilla/visited-privacy) 323 | 324 | v0.27 fix visited link color (work on chrome) 325 | 326 | ## [[ehx torrent text]](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/ehx_torrent_text.user.js) 327 | click to copy torrent name in torrent page 328 | event is from first parentheses in the name, may not correct 329 | auto close window after copy 330 | if you don't want it close, set this value to false 331 | ```js 332 | let autoclose = true; 333 | ``` 334 | 335 | ## [[dlsite title reformat]](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/dlsite_title_reformat.user.js) 336 | 337 | #### Features: 338 | - remove title link 339 | - remove excess text 340 | - custom title format 341 | - auto convert forbidden characters `<>:"/|?*\` to fullwidth 342 | - add copy button for data 343 | - add button to sort search result by ID 344 | - extract track list (experimental) 345 | - official list (example: RJ298079) 346 | - search offset line 347 | - remove time info 348 | - remove excess white space 349 | - sort (only if list is out of order and have 2nd index in title) 350 | - example: RJ237403 351 | ``` 352 | 1.track3 cccc 353 | 2.track1 aaaa 354 | 3.track2 bbbb 355 | 356 | convert to 357 | 358 | 1.1 aaaa 359 | 2.2 bbbb 360 | 3.3 cccc 361 | ``` 362 | 363 | click button to copy 364 | ![](https://i.imgur.com/kdsvTit.jpg) 365 | 366 | - v0.87 367 | - auto re-run script by refresh after click display mode switch 368 | 369 | - v0.84 370 | - fix not work in search (wait element) 371 | 372 | - v0.83 373 | - fix title 374 | - make title selectable 375 | 376 | - v0.75 377 | - sort by `type & favorites count` at `https://www.dlsite.com/maniax/announce/list` 378 | - add sort by type button at `search result / circle` 379 | 380 | - v0.73 381 | - prevent duplicate track list 382 | 383 | - v0.72 384 | - improve track extract (add new extrator) 385 | 386 | - v0.71 387 | - if title is empty after process, return to original title 388 | - remove incomplete bracket `"(no_end" => "no_end"`, `"no_start)" => "no_start"` 389 | - improve excess removal of track title 390 | 391 | - v0.68 392 | - add button to sort item by id 393 | 394 | - v0.67 395 | - fix switch link [R18(HENTAI), R18(BL/TL), All-ages] on top right 396 | make it stay at same product/circle page 397 | 398 | - v0.66 399 | - in list/grid/search view, ~~replace all cover to full size~~ and add a button to download 400 | 401 | - v0.65 402 | - add support for dlsite girl part `https://www.dlsite.com/girls/*` 403 | - fix circle text is not formatted 404 | 405 | - v0.63 406 | - fix a bug in title process 407 | ``` 408 | example: ccc[ddd]eee (fff) 409 | old: ccceee 410 | now: cccdddeee 411 | ``` 412 | 413 | - v0.62 414 | - improved track list extract 415 | - list now use natural sort 416 | 417 | - v0.61 418 | - now script will try to extract track list form entire page 419 | Less accurate. 420 | Can't get the track that has no number. 421 | 422 | - v0.58 423 | - improved excess string removal 424 | - fix track list position 425 | 426 | - v0.56 427 | - add more settings 428 | - script now trigger faster (don't have to wait for the page to fully load) 429 | 430 | - v0.53 431 | - add support to `search result` / `circle` page 432 | - this doesn't support custom title because data is incomplete 433 | - also grid view is too tight to put more buttons 434 | 435 | - v0.51 436 | - add copy button for each data 437 | 438 | - v0.48 439 | - fix some issue in getData 440 | 441 | - v0.45 442 | - rearrange buttons (use less space) 443 | - now old buttons only appear when: 444 | original != custom 445 | default_format != original & default_format != custom 446 | 447 | - v0.38 448 | - now title can be custom to any format you want 449 | 450 | ## [[mangaoh title reformat]](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/mangaoh_title_reformat.user.js) 451 | reformat date & title in search result 452 | click button to copy 453 | ![](https://i.imgur.com/amKQlOX.jpg) 454 | - v0.12 455 | - fix broken scrollbar cause by checkout-js error 456 | 457 | ## [[reddit img relink]](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/reddit_img_relink.user.js) 458 | **only work for New UI** 459 | show images direct link under title (only resized images) 460 | ![](https://i.imgur.com/pw1fW6X.jpg) 461 | 462 | ## [[pornhub user video]](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/ph_user_video.user.js) 463 | - replace `user link` to `user video list`, except comment 464 | it will try to go to ```user/videos/public``` first 465 | if it doesn't exist, auto redirect to ```user/videos``` 466 | 467 | - video page 468 | - add a button to copy link of current page 469 | 470 | - user video list 471 | - add checkbox for each video and a textbox on top to select and copy links 472 | - make upload time visable 473 | - [effect](https://i.imgur.com/lL6sJZX.png) 474 | 475 | - v0.07 476 | - mark related/recommended video if uploader is the same as current video 477 | 478 | - v0.06 479 | - improve auto redirect 480 | 481 | - v0.04 482 | - delay trigger when in background 483 | - replace user link now support other pages 484 | 485 | ## [[anti-bili-anti-copy]](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/anti-bili-anti-copy.user.js) 486 | remove bilibili article copy protection 487 | 488 | ## [[youtube url normalizer]](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/ytb_url_normalizer.user.js) 489 | normalize url in video description 490 | remove miniplayer (still activated when you watch in playlist) 491 | 492 | ## [[prts redirector]](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/prts_redirector.user.js) 493 | Arknights Wiki PRTS 494 | auto redirect & replace all link to desktop version 495 | 496 | ## [[sharer.pw auto click]](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/sharer-pw_auto_click.user.js) 497 | auto click and redirect to download link immediately 498 | 499 | ## [[google drive auto click]](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/google_drive_autoclick.user.js) 500 | auto skip & click download 501 | 502 | ## [[fc2 show all products]](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/fc2_show_all_products.user.js) 503 | only work at link like this: `https://adult.contents.fc2.com/users/*/articles?sort=date&order=desc` 504 | ![](https://i.imgur.com/chXUrUu.png) 505 | ![](https://i.imgur.com/kWXJ5ea.png) 506 | 507 | - show full list of products in 1 page and sort by id 508 | - show id 509 | - show full title 510 | - remove link in title (click image to product page) 511 | - remove some heavy element 512 | - make product link open in new tab 513 | 514 | #### updates 515 | - v0.2 516 | - fix some issue 517 | - improve speed 518 | 519 | ## [[nexusmods skip countdown]](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/nexusmods_skip_countdown.user.js) 520 | [www.nexusmods.com](https://www.nexusmods.com/) 521 | after click MANUAL and selected file (some mod have mutiple files) 522 | ![](https://i.imgur.com/kV1tB5W.png) 523 | - no countdown 524 | - auto start download 525 | 526 | 527 | ## [[avgle m3u8 extractor]](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/avgle_m3u8_extractor.user.js) 528 | - extract m3u8 after click close 529 | - when it done, click button to download 530 | - use video title as filename 531 | 532 | #### the m3u8/url will expire over time 533 | 534 | options: 535 | ```js 536 | let discard_first_seg = false; //discard the first segment 537 | let url_only = false; //only the video url as .txt instead of .m3u8 (for youtube-dl/uget/wget...etc) 538 | ``` 539 | 540 | use m3u8 to download and merge in to single file use [streamlink](https://streamlink.github.io/index.html) 541 | ``` 542 | streamlink --http-header Referer=https://avgle.com/ file://"C:/example.m3u8" best -o example.ts 543 | ``` 544 | convert 545 | ``` 546 | ffmpeg -i example.ts -c copy example.mp4 547 | ``` 548 | 549 | streamlink loacal file path use `/` even in windows 550 | also you might not want use `*.ts` (google: `windows 10 ts file freeze`) 551 | cus windows werid behavior like scan for thumbnail can make system freeze/hanging 552 | change it to like `._ts_` `.tmp` so windows does't recognize it, but ffmpeg still can handle it correctly 553 | 554 | reference: 555 | https://github.com/download-online-video/chrome-avgle-helper/issues/21 556 | https://github.com/download-online-video/chrome-avgle-helper/issues/54 557 | 558 | ## [[newgrounds tool]](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/newgrounds_tool.user.js) 559 | - auto select best resolution 560 | - extract video link & download (right click) 561 | 562 | ## [[AutoEQ to EasyQ converter]](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/autoEQ_to_EasyQ.user.js) 563 | ![](https://i.imgur.com/qZgTYoF.png) 564 | [AutoEq](https://github.com/jaakkopasanen/AutoEq)、[Search your headphone](https://github.com/jaakkopasanen/AutoEq/tree/master/results) 565 | [EasyQ](https://www.kvraudio.com/product/easyq_by_rs_met) 566 | [Require VST adapter (for foobar2000)](https://wiki.hydrogenaud.io/index.php?title=Foobar2000:Components/VST_2.4_adapter_(foo_vst)) 567 | 568 | disable other EQ DSP or **VST adapter** may crash foobar 569 | 570 | Components > VST plug-ins > load EasyQ dll file 571 | DSP > EasyQ > load xml file 572 | 573 | 574 | #### Quick switch different EQ 575 | close **EasyQ** window after load eq 576 | type under **DSP chain presets** for preset name and click **save** 577 | 578 | View > Layout > Enable layout editing mode 579 | right click on toolbar > add **DSP switcher** 580 | to disable editing mode 581 | View > Layout > Enable layout editing mode 582 | 583 | 584 | ## [[4chan Base64 Decode Tool]](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/4ch_b64_tool.user.js) 585 | ![](https://i.imgur.com/YTsCIJe.png) 586 | - appears in the `/thread` page 587 | - decode Base64 588 | - auto recursive decoding if the message is coded multiple times 589 | 590 | example: 591 | ``` 592 | Vm1wR2FtVkdTWGxXYms1cVVteGFXVlpyVmt0VE1WWnhVbTFHVGxadFVsWlZNVkpYWVVVeFdWRnNiRmRXYlZKeVdWWmFXbVZHWkhGWGJIQnNZVE5DU1ZkWE1UUmtNVlp6VVd4V1RsSkVRVGs9 593 | ``` 594 | 595 | 596 | ## [[pixiv blacklist]](https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/pixiv_blacklist.user.js) 597 | visit user's main page `www.pixiv.net/users/xxxxxxxx` 598 | to add/remove user from blacklist 599 | 600 | when in search result `www.pixiv.net/tags/*` 601 | if the user is in the blacklist 602 | script will hide that user's artwork 603 | 604 | ![](https://i.imgur.com/wtcIFtt.jpg) 605 | 606 | -------------------------------------------------------------------------------- /aegiswiki.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name aegiswiki 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/aegiswiki.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/aegiswiki.user.js 6 | // @version 0.1 7 | // @description large image / direct link to unit & class page 8 | // @author x94fujo6 9 | // @match https://wikiwiki.jp/aigiszuki/%E3%83%A6%E3%83%8B%E3%83%83%E3%83%88/%E3%83%AC%E3%82%A2%E3%83%AA%E3%83%86%E3%82%A3/* 10 | // @icon https://www.google.com/s2/favicons?sz=64&domain=wikiwiki.jp 11 | // @grant none 12 | // @run-at document-start 13 | // ==/UserScript== 14 | 15 | (async function aegiswiki() { 16 | let remove_offset = 4; 17 | 18 | document.addEventListener("readystatechange", async () => { 19 | console.log(document.readyState); 20 | if (document.readyState == "interactive") { 21 | console.log("start main"); 22 | await main(); 23 | await removeEle(); 24 | } 25 | }); 26 | 27 | async function main() { 28 | let max_width = document.querySelector("div#body").clientWidth; 29 | let sec = 7; 30 | let top_gap = 5; 31 | let recalc_width = Math.round((max_width) / sec); 32 | let img_size = Math.round((recalc_width - 10) / 3); 33 | let queue = []; 34 | 35 | [...document.querySelectorAll(`.column-center #content>.h-scrollable td[style*="text-align:center;"]`)] 36 | .forEach(td => { 37 | queue.push( 38 | processTd(td) 39 | ); 40 | }); 41 | 42 | await Promise.all(queue); 43 | queue = []; 44 | 45 | [...document.querySelectorAll(".column-center img")] 46 | .forEach(img => { 47 | queue.push( 48 | processImg(img) 49 | ); 50 | }); 51 | 52 | await Promise.all(queue); 53 | queue = []; 54 | 55 | fixTypo(); 56 | 57 | async function findTypo() { 58 | let list = []; 59 | for (let a of [...document.querySelectorAll("a[href*='クラス']")]) { 60 | let result = await testLink(a.href); 61 | console.log(result, a.textContent, decodeURI(a.href)); 62 | if (!result) { 63 | list.push(a.textContent); 64 | } 65 | await sleep(); 66 | } 67 | console.log(list); 68 | console.log(list.map(text => { 69 | return { 70 | a: text, 71 | b: "" 72 | }; 73 | })); 74 | 75 | async function testLink(link) { 76 | let d = await fetch(link); 77 | if (d.status != 200) { 78 | return false; 79 | } else { 80 | return true; 81 | } 82 | } 83 | 84 | function sleep(ms = 500) { 85 | return new Promise(resolve => setTimeout(resolve, ms)); 86 | } 87 | } 88 | //findTypo(); 89 | 90 | async function fixTypo() { 91 | let list = [ 92 | { a: "ちびイモータルプリンセス", b: "イモータルプリンセス" }, 93 | { a: "ちびイビルプリンセス", b: "イビルプリンセス" }, 94 | { a: "ちび中級竜兵", b: "竜兵" }, 95 | { a: "ちび重装砲兵", b: "重装砲兵" }, 96 | { a: "ちびインペリアルナイト", b: "インペリアルナイト" }, 97 | { a: "ちび鬼", b: "鬼" }, 98 | { a: "ちび雷公", b: "雷公" }, 99 | { a: "ちび風伯", b: "風伯" }, 100 | { a: "ちびグランドナイト", b: "グランドナイト" }, 101 | { a: "ちび邪仙", b: "邪仙" }, 102 | { a: "ちび料理人", b: "料理人" }, 103 | { a: "ちびスチームナイト", b: "スチームナイト" }, 104 | { a: "ちびシャーマン", b: "シャーマン" }, 105 | { a: "ちびレンジャー", b: "レンジャー" }, 106 | { a: "ちびルーンアコライト", b: "ルーンアコライト" }, 107 | { a: "ちびシービショップ", b: "シービショップ" }, 108 | 109 | { a: "下級ソルジャー", b: "ソルジャー" }, 110 | { a: "下級ヘビーアーマー", b: "ヘビーアーマー" }, 111 | { a: "下級バンデット", b: "バンデット" }, 112 | { a: "下級竜兵", b: "竜兵" }, 113 | { a: "下級サムライ", b: "サムライ" }, 114 | { a: "下級忍者", b: "忍者" }, 115 | { a: "下級シーソルジャー", b: "シーソルジャー" }, 116 | { a: "下級アーチャー", b: "アーチャー" }, 117 | { a: "下級メイジ", b: "メイジ" }, 118 | { a: "下級パイレーツ", b: "パイレーツ" }, 119 | { a: "下級スカイウォリアー", b: "スカイウォリアー" }, 120 | { a: "下級スカイシューター", b: "スカイシューター" }, 121 | 122 | { a: "儀杖軍神", b: "儀仗軍神" }, 123 | { a: "中級竜兵", b: "竜兵" }, 124 | { a: "仙猿", b: "仙人" }, 125 | { a: "王太子公子", b: "王太子" }, 126 | { a: "盗賊【七つの大罪】盗賊【異郷】", b: "盗賊【七つの大罪】" }, 127 | { a: "巨人【七つの大罪】槌使い【異郷】", b: "巨人【七つの大罪】" }, 128 | { a: "召喚系アイドル", b: "アイドル召喚士" }, 129 | { a: "騎士【七つの大罪】騎士【異郷】", b: "騎士【七つの大罪】" }, 130 | { a: "妖精【七つの大罪】妖精【異郷】", b: "妖精【七つの大罪】" }, 131 | { a: "王女【七つの大罪】王女【異郷】", b: "王女【七つの大罪】" }, 132 | ]; 133 | let eles = document.querySelectorAll("a[href*='クラス']"); 134 | eles.forEach(a => { 135 | let url = decodeURI(a.href); 136 | let index = list.findIndex(str => url.includes(str.a)); 137 | if (index != -1) { 138 | let replace = list[index]; 139 | console.log(`[fix typo] ${replace.a} => ${replace.b}`, a); 140 | a.href = url.replace(replace.a, replace.b); 141 | } 142 | }); 143 | } 144 | 145 | async function processImg(img) { 146 | if (img.alt == img.title) { 147 | ["height", "width"].forEach(attr => { 148 | img.removeAttribute(attr); 149 | }); 150 | img.style = ` 151 | height: ${img_size}px; 152 | width: ${img_size}px; 153 | margin-top: ${top_gap}px; 154 | `; 155 | } 156 | } 157 | 158 | async function processTd(td) { 159 | let img, a, parent; 160 | a = td.querySelector("a"); 161 | // https://wikiwiki.jp/aigiszuki/ユニット/クラス/${class_name} 162 | 163 | if (!a) { 164 | if (td.innerHTML != "") { 165 | // no unit 166 | let unit_class = td.textContent.replace(/\s/gm, ""); 167 | a = document.createElement("a"); 168 | a.href = `https://wikiwiki.jp/aigiszuki/ユニット/クラス/${unit_class}`; 169 | a.target = "_blank"; 170 | a.textContent = unit_class; 171 | td.textContent = ""; 172 | td.appendChild(a); 173 | console.log(`[add link] ${unit_class}: ${a.href}`); 174 | } else { 175 | // empty 176 | } 177 | return true; 178 | } 179 | 180 | td.style = ` 181 | text-align: center; 182 | width: ${recalc_width}px; 183 | padding: 0; 184 | `; 185 | a.href = `https://wikiwiki.jp/aigiszuki/ユニット/クラス/${a.textContent.replace(/\s/gm, "")}`; 186 | a.target = "_blank"; 187 | 188 | // https://wikiwiki.jp/aigiszuki/${title} 189 | img = td.querySelectorAll("img"); 190 | [...img].reverse().forEach(ele => { 191 | parent = ele.parentElement; 192 | a = document.createElement("a"); 193 | a.href = `https://wikiwiki.jp/aigiszuki/${ele.title}`; 194 | a.target = "_blank"; 195 | a.appendChild(ele); 196 | parent.insertAdjacentElement("afterbegin", a); 197 | }); 198 | 199 | let move_ele; 200 | move_ele = [...td.childNodes[0].childNodes].filter(node => node.nodeName != "#text"); 201 | move_ele.reverse().forEach(ele => { 202 | td.insertAdjacentElement("afterbegin", ele); 203 | }); 204 | 205 | return true; 206 | } 207 | } 208 | 209 | async function removeEle() { 210 | let content = [...document.querySelector("#content").childNodes]; 211 | let start_remove = false; 212 | start_remove = content.findIndex(node => { 213 | if (node.nodeType == 3) { 214 | return false; 215 | } 216 | if (node.className.includes("splitinclude")) { 217 | return true; 218 | } 219 | return false; 220 | }); 221 | 222 | console.log("start_remove", start_remove); 223 | if (start_remove == -1) { 224 | return false; 225 | } else { 226 | start_remove = start_remove - remove_offset; 227 | content.forEach((ele, index) => { 228 | if (index >= start_remove) { 229 | ele.remove(); 230 | } 231 | }); 232 | return true; 233 | } 234 | } 235 | })(); 236 | -------------------------------------------------------------------------------- /all_link_to_new_tab.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name all links in new tab 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/all_link_to_new_tab.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/all_link_to_new_tab.user.js 6 | // @version 0.04 7 | // @description open all links in new tab 8 | // @author x94fujo6 9 | // @match *://*/* 10 | // @grant GM_openInTab 11 | // @run-at document-body 12 | // ==/UserScript== 13 | /* jshint esversion: 9 */ 14 | 15 | (function () { 16 | 'use strict'; 17 | const 18 | /** 19 | * active: decides whether the new tab should be focused, (是否在新分頁打開後跳到該分頁) 20 | * insert: that inserts the new tab after the current one, (是否將新分頁排在目前分頁後面) 21 | * setParent: makes the browser re-focus the current tab on close (是否在該分頁關閉時回到目前分頁) 22 | * incognito: makes the tab being opened inside a incognito mode/private mode window. (是否在隱密模式下打開分頁) 23 | */ 24 | newTabConfig = { 25 | active: false, 26 | insert: false, 27 | setParent: false, 28 | incognito: false, 29 | }, 30 | linkReg = /^(https|http):\/\/.+/, 31 | log = (...any) => console.log(`%c[open all link in new tab]%c`, "color:OrangeRed;", "", ...any), 32 | asyncTimeout = (fun, time) => { 33 | return new Promise(resolve => { 34 | setTimeout(() => { 35 | fun(); 36 | resolve(); 37 | }, time); 38 | }); 39 | }, 40 | setOnclick = () => { 41 | let links = document.querySelectorAll("[href]"); 42 | if (links) { 43 | links.forEach(link => { 44 | if (!link.href.match(linkReg)) { 45 | log(`this is not a http/https link, skip`, link); 46 | return; 47 | } 48 | if (link.getAttribute("setNewTab") == "true") { 49 | log(`this link has been set, skip`, link); 50 | return; 51 | } 52 | link.onclick = function (event) { 53 | GM_openInTab(link.href, newTabConfig); 54 | event.preventDefault(); 55 | }; 56 | link.setAttribute("setNewTab", true); 57 | }); 58 | return true; 59 | } else { 60 | setTimeout(setOnclick, 500); 61 | return false; 62 | } 63 | }, 64 | setQueue = async (max = 3) => { 65 | let done = false; 66 | for (let i = 0; i < max; i++) { 67 | await asyncTimeout(() => done = setOnclick(), 1000 * i); 68 | if (!done) continue; 69 | log("all links are set"); 70 | break; 71 | } 72 | }, 73 | ob = new MutationObserver(setQueue); 74 | 75 | //-------------------------------- 76 | log("script start"); 77 | setQueue(); 78 | ob.observe(document.body, { childList: true, }); 79 | 80 | })(); 81 | 82 | -------------------------------------------------------------------------------- /anti-bili-anti-copy.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name anti-bili-anti-copy 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/anti-bili-anti-copy.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/anti-bili-anti-copy.user.js 6 | // @version 0.12 7 | // @description remove bilibili article copy protection 8 | // @author x94fujo6 9 | // @match https://www.bilibili.com/read/* 10 | // @grant none 11 | // ==/UserScript== 12 | 13 | (function () { 14 | 'use strict'; 15 | window.onload = setTimeout(ck, 1000); 16 | function main(article) { 17 | article.forEach(element => element.classList.remove("unable-reprint")); 18 | setTimeout(ck, 1000); 19 | } 20 | function ck() { 21 | let article = document.getElementsByClassName("unable-reprint"); 22 | if (article.length != 0) main(article); 23 | } 24 | })(); -------------------------------------------------------------------------------- /autoeq_to_easyq.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name AutoEQ to EasyQ converter 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/autoeq_to_easyq.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/autoeq_to_easyq.user.js 6 | // @version 0.03 7 | // @description convert & download XML for EasyQ 8 | // @author x94fujo6 9 | // @match https://github.com/jaakkopasanen/AutoEq/* 10 | // ==/UserScript== 11 | 12 | /** 13 | * AutoEq 14 | * https://github.com/jaakkopasanen/AutoEq 15 | * https://github.com/jaakkopasanen/AutoEq/tree/master/results 16 | * 17 | * EasyQ 18 | * https://www.kvraudio.com/product/easyq_by_rs_met 19 | * 20 | * Require (for foobar2000) 21 | * https://wiki.hydrogenaud.io/index.php?title=Foobar2000:Components/VST_2.4_adapter_(foo_vst) 22 | * 23 | * disable other EQ DSP or it may crash foobar 24 | * 25 | * Components > VST plug-ins > load EasyQ dll file 26 | * DSP > EasyQ > load xml file 27 | * 28 | * 29 | * [Quick switch different EQ] 30 | * after load EQ, close EasyQ 31 | * type under [DSP chain presets] for preset name and click [save] 32 | * 33 | * View > Layout > Enable layout editing mode 34 | * [right click] on toolbar > add [DSP switcher] 35 | * to disable editing mode 36 | * View > Layout > Enable layout editing mode 37 | */ 38 | 39 | (function () { 40 | let msgid = "autoEQ converter"; 41 | 42 | window.onload = main(); 43 | 44 | function debug_msg(...any) { 45 | console.log(`[${msgid}]: `, ...any); 46 | } 47 | 48 | function debug_data(...any) { 49 | console.log(...any); 50 | } 51 | 52 | function main() { 53 | let data = genXML(); 54 | if (!data) { 55 | return debug_msg("extract failed"); 56 | } else { 57 | let pos = data[2].children[0], 58 | style = { display: "block", margin: "0.2rem", }, 59 | b1 = newButton("", `Download ${data[0]} (+GlobalGain).xml`, style, () => download(`${data[0]} (+ GlobalGain).xml`, data[1])), 60 | b2 = newButton("", `Download ${data[0]}.xml`, style, () => download(`${data[0]}.xml`, data[1].replace(/ GlobalGain="[\d.]+"/g, ""))), 61 | b3 = newButton("", `Download ${data[0]} (-GlobalGain).xml`, style, () => download(`${data[0]} (- GlobalGain).xml`, data[1].replace(/ GlobalGain="/g, ` GlobalGain="-`))); 62 | pos.insertAdjacentElement("afterend", b3); 63 | pos.insertAdjacentElement("afterend", b2); 64 | pos.insertAdjacentElement("afterend", b1); 65 | } 66 | 67 | function download(filename, data) { 68 | var blob = new Blob([data], { type: 'text/plain', endings: 'native' }); 69 | if (window.navigator.msSaveOrOpenBlob) { 70 | window.navigator.msSaveBlob(blob, filename); 71 | } else { 72 | var elem = window.document.createElement('a'); 73 | elem.href = window.URL.createObjectURL(blob); 74 | elem.download = filename; 75 | document.body.appendChild(elem); 76 | elem.click(); 77 | document.body.removeChild(elem); 78 | } 79 | } 80 | } 81 | 82 | function newButton(id = "", textContent = "", style = {}, onclick = "") { 83 | let b = Object.assign(document.createElement("button"), { id, textContent, onclick, }); 84 | Object.assign(b.style, style); 85 | return b; 86 | } 87 | 88 | function genXML() { 89 | let xml_head = ``, 90 | xml = document.implementation.createDocument(null, "Equalizer"), 91 | xml_file = [], 92 | filename = "", 93 | article = document.querySelector(`div[data-target="readme-toc.content"]>article`), 94 | preamp = article.innerText.match(/preamp of (.*)dB/); 95 | if (!extractData()) return false; 96 | if (preamp) { 97 | preamp = preamp[1].match(/[\d\.]+/gm).reverse()[0]; 98 | } else { 99 | preamp = 0; 100 | } 101 | xml.documentElement.setAttribute("PatchFormat", 2); 102 | xml.documentElement.setAttribute("GlobalGain", preamp); 103 | console.log(xml_file); 104 | xml_file.forEach(line => { 105 | let new_band = document.createElementNS(null, "Band"), 106 | m = mode(line[1]), 107 | f = parseInt(line[2].replace(" Hz", "")), 108 | g = parseFloat(line[4].replace(" dB", "")), 109 | q = parseFloat(line[3]), 110 | b = calcOct(q); 111 | console.log({ mode: m, frequency: f, gain: g, q, bandwidth: b }); 112 | new_band.setAttributeNS(null, "Mode", m); 113 | new_band.setAttributeNS(null, "Frequency", f); 114 | new_band.setAttributeNS(null, "Gain", g); 115 | new_band.setAttributeNS(null, "Bandwidth", b); 116 | xml.documentElement.appendChild(new_band); 117 | }); 118 | xml_file = xml_head + (new XMLSerializer()).serializeToString(xml); 119 | console.log(xml_file); 120 | return [filename, xml_file, article]; 121 | 122 | function calcOct(input = 0) { 123 | let l = 100000000, 124 | Q1 = ((2 * input * input) + 1) / (2 * input * input), 125 | Q2 = Math.pow(2 * Q1, 2) / 4, 126 | Q3 = Math.sqrt(Q2 - 1), 127 | Q4 = Q1 + Q3; 128 | return Math.round(l * Math.log(Q4) / Math.log(2)) / l; 129 | } 130 | 131 | function mode(input = "") { 132 | let table = { 133 | "Peaking": "Peak/Dip", 134 | "LowShelf": "Low Shelving", 135 | "HighShelf": "High Shelving", 136 | }; 137 | return table[input]; 138 | } 139 | 140 | function extractData() { 141 | let data = article.querySelector(`table`), 142 | o = article.querySelector(`a[href="https://github.com/jaakkopasanen/AutoEq#usage"]`); 143 | console.log(data); 144 | if (!data || !o) { 145 | return false; 146 | } else { 147 | filename = article.children[0].textContent; 148 | o = data.querySelectorAll("tbody>tr"); 149 | o.forEach(tr => { 150 | let newline = []; 151 | [...tr.children].forEach(td => newline.push(td.textContent)); 152 | xml_file.push(newline); 153 | }); 154 | return true; 155 | } 156 | } 157 | } 158 | })(); 159 | 160 | -------------------------------------------------------------------------------- /avgle_m3u8_extractor.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name avgle m3u8 extractor 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/avgle_m3u8_extractor.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/avgle_m3u8_extractor.user.js 6 | // @version 0.02 7 | // @description extract m3u8 after click close / use video title as filename 8 | // @author x94fujo6 9 | // @match https://avgle.com/video/* 10 | // ==/UserScript== 11 | /* jshint esversion: 9 */ 12 | /* 13 | reference: 14 | https://github.com/download-online-video/chrome-avgle-helper/issues/21 15 | https://github.com/download-online-video/chrome-avgle-helper/issues/54 16 | */ 17 | (function () { 18 | let msgid = "avgle extract tool"; 19 | let discard_first_seg = false; //discard the first segment 20 | let url_only = false; //only the video url as .txt instead of .m3u8 (for youtube-dl/uget/wget...etc) 21 | 22 | window.onload = main(); 23 | 24 | function debug_msg(...any) { 25 | console.log(`[${msgid}]: `, ...any); 26 | } 27 | 28 | function debug_data(...any) { 29 | console.log(...any); 30 | } 31 | 32 | function newButton(button_id, button_text, button_style, button_onclick) { 33 | let button = Object.assign(document.createElement("button"), { 34 | id: button_id, 35 | textContent: button_text, 36 | onclick: button_onclick, 37 | }); 38 | Object.assign(button.style, button_style); 39 | return button; 40 | } 41 | 42 | async function main() { 43 | let pos = document.querySelector("#response_message").parentNode; 44 | let b = newButton("aet_dl_button", "click close to extract data", { width: "max-content" }, () => { }); 45 | pos.insertAdjacentElement("afterbegin", b); 46 | b = document.querySelector("#aet_dl_button"); 47 | b.disabled = true; 48 | 49 | //add observer 50 | let ob = new MutationObserver(() => { 51 | b.textContent = "waitting data..."; 52 | ob.disconnect(); 53 | }); 54 | 55 | ob.observe(document.querySelector("#player_3x2_container"), { attributes: true }); 56 | 57 | await cap_segments().then(data => { 58 | debug_data(data); 59 | let title = video_title; 60 | let seg_data = data.decrypted.map(x => x.uri); 61 | let duration_data = data.info; 62 | let max_duration = arr => Math.ceil(Math.max(...arr)); 63 | let m3u_inf = time => `#EXTINF:${time}`; 64 | let file; 65 | if (discard_first_seg) { 66 | seg_data.shift(); 67 | duration_data.shift(); 68 | } 69 | b.disabled = false; 70 | if (url_only) { 71 | file = seg_data.join("\n"); 72 | b.textContent = "download url text"; 73 | b.onclick = () => download(`${title}.txt`, file); 74 | } else { 75 | file = [ 76 | "#EXTM3U", 77 | `#EXT-X-TARGETDURATION:${max_duration(duration_data)}`, 78 | "#EXT-X-ALLOW-CACHE:YES", 79 | "#EXT-X-PLAYLIST-TYPE:VOD", 80 | "#EXT-X-VERSION:3", 81 | "#EXT-X-MEDIA-SEQUENCE:1", 82 | ]; 83 | for (let index = 0, length = seg_data.length; index < length; index++) { 84 | file.push(m3u_inf(duration_data[index])); 85 | file.push(seg_data[index]); 86 | } 87 | file.push("#EXT-X-ENDLIST"); 88 | file = file.join("\n"); 89 | b.textContent = "download m3u8"; 90 | b.onclick = () => download(`${title}.m3u8`, file); 91 | } 92 | }); 93 | 94 | function cap_segments() { 95 | return new Promise(resolve => { 96 | let v = videojs('video-player'); 97 | v.on("loadedmetadata", async () => { 98 | await new Promise(resolve => { 99 | b.textContent = "processing..."; 100 | resolve(); 101 | }); 102 | setTimeout(() => { 103 | debug_msg("metadata loaded"); 104 | let segments = v.tech_.hls.playlists.media_.segments; 105 | let arr = []; 106 | let info = []; 107 | arr = segments.map(x => { 108 | return { 109 | uri: x.resolvedUri, 110 | method: "HEAD", 111 | timeout: 50, 112 | }; 113 | }); 114 | arr.forEach((x, index) => { 115 | x.uri = videojs.Hls.xhr(x, function () { }).uri; 116 | info.push(segments[index].duration); 117 | }); 118 | debug_msg(`uri count: ${arr.length}`); 119 | resolve({ decrypted: arr, info: info }); 120 | }); 121 | }); 122 | }); 123 | } 124 | 125 | function download(filename, data) { 126 | var blob = new Blob([data], { type: 'text/plain', endings: 'native' }); 127 | if (window.navigator.msSaveOrOpenBlob) { 128 | window.navigator.msSaveBlob(blob, filename); 129 | } else { 130 | var elem = window.document.createElement('a'); 131 | elem.href = window.URL.createObjectURL(blob); 132 | elem.download = filename; 133 | document.body.appendChild(elem); 134 | elem.click(); 135 | document.body.removeChild(elem); 136 | } 137 | } 138 | } 139 | })(); 140 | -------------------------------------------------------------------------------- /az_cn_wiki_fleet_tech_tool.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name 碧航艦隊科技工具 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/az_cn_wiki_fleet_tech_tool.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/az_cn_wiki_fleet_tech_tool.user.js 6 | // @version 0.05 7 | // @description 海事局的艦隊科技頁面 可以點擊該行來標記已120的船 顯示艦船頭像 8 | // @author x94fujo6 9 | // @match https://wiki.biligame.com/blhx/%E8%88%B0%E9%98%9F%E7%A7%91%E6%8A%80 10 | // @grant GM_getValue 11 | // @grant GM_setValue 12 | // ==/UserScript== 13 | 14 | /* 15 | [changelog] 16 | 0.05 17 | 現在可以關閉/開啟標記功能 18 | 19 | 0.04 20 | 顯示艦船頭像(海事局) 21 | 自動更新並快取 22 | 23 | 0.03 24 | 修改標記顏色/等待的selector、正確應用delay參數 25 | 因用手機看wiki時部分欄位會被隱藏,標記方式改為整行都能觸發 26 | 27 | 0.02 28 | 自動修復當頁面在背景中載入後標題列的錯位問題 (WIKI本身問題 關掉腳本一樣會) 29 | https://i.imgur.com/kQWlEU1.jpg 30 | 31 | */ 32 | 33 | (async function () { 34 | 'use strict'; 35 | const 36 | key = { ship_id: "ship_id", ship_icon: "ship_icon" }, 37 | bg_color = "silver", 38 | getValue = (_key) => GM_getValue(_key, []), 39 | setValue = (_key, _list) => GM_setValue(_key, (_list instanceof Array) ? _list : []), 40 | log = (...any) => console.log(`%c[碧航艦隊科技工具]%c`, "color:OrangeRed;", "", ...any), 41 | sleep = (ms = 0) => new Promise(resolve => setTimeout(resolve, ms)); 42 | let 43 | waitting_result = false; 44 | 45 | await waitTab(); 46 | waitting_result = await waitEle({ retry: 30, selector: "#CardSelectTr>thead" }); 47 | if (!waitting_result) return; 48 | 49 | main(); 50 | 51 | function waitTab() { 52 | return new Promise(resolve => { 53 | if (document.visibilityState === "visible") return resolve(); 54 | log("tab in background, script paused"); 55 | document.addEventListener("visibilitychange", () => { 56 | if (document.visibilityState === "visible") { 57 | log("script unpaused"); 58 | return resolve(); 59 | } 60 | }); 61 | }); 62 | } 63 | 64 | async function waitEle({ retry = 30, selector = "", delay = 500 }) { 65 | let result = false; 66 | for (let i = 0; i < retry; i++) { 67 | result = document.querySelector(selector); 68 | if (result) break; 69 | if (!result) log(`target[${selector}] not found, remaining retries [${retry - i}]`, result); 70 | await sleep(delay); 71 | } 72 | if (result) { 73 | log(`found target[${selector}] continue`); 74 | return true; 75 | } else { 76 | log(`max retries exceeded, target[${selector}] not found`); 77 | return false; 78 | } 79 | } 80 | 81 | async function main() { 82 | let 83 | pos = document.querySelector("#CardSelectTr"), 84 | table = pos.querySelector("tbody"), 85 | msg = document.createElement("div"), 86 | changeColor = (eles, color = "") => { 87 | eles.forEach(ele => ele.style.backgroundColor = color); 88 | }, 89 | updateList = (list) => { 90 | document.querySelector("#wiki_tool_marked_count").textContent = list.size; 91 | document.querySelector("#wiki_tool_marked_list").value = [...list]; 92 | }, 93 | addCss = () => { 94 | let css = document.createElement("style"); 95 | css.textContent = ` 96 | .fleet_tech_tool_ship_icon { 97 | display: inline-block; 98 | border: 1px solid #a2a9b1; 99 | border-radius: 7px; 100 | } 101 | 102 | .fleet_tech_tool_ship_name { 103 | display: inline-block; 104 | width: calc(100% - 40px); 105 | /*justify-content: center;*/ 106 | word-break: keep-all; 107 | vertical-align: middle; 108 | } 109 | `; 110 | document.head.appendChild(css); 111 | }, 112 | data_srcipt = []; 113 | 114 | addCss(); 115 | fixDataHeader(); 116 | table.children.forEach(tr => { 117 | addSwitch([...tr.children], tr); 118 | }); 119 | await checkIcon(); 120 | 121 | msg.innerHTML = ` 122 |
123 |
艦隊科技工具
作用中,點擊艦船該行任意位置進行標記。 124 | 已標記:
${getValue(key.ship_id).length}
125 |
126 |
127 | 128 | 132 | 133 |
134 | `; 135 | pos.insertAdjacentElement("beforebegin", msg); 136 | 137 | waitting_result = false; 138 | waitting_result = await waitEle({ retry: 30, selector: "#wiki_tool_switch" }); 139 | if (!waitting_result) return; 140 | 141 | document.querySelector("#wiki_tool_edit").onclick = () => { 142 | let 143 | box = document.querySelector("#wiki_tool_box"), 144 | display = box.style.display; 145 | box.style.display = (display == "none") ? "" : "none"; 146 | document.querySelector("#wiki_tool_marked_list").value = getValue(key.ship_id); 147 | }; 148 | document.querySelector("#wiki_tool_save").onclick = () => { 149 | let 150 | _list = new Set(document.querySelector("#wiki_tool_marked_list").value.split(",")), 151 | _table = document.querySelector("#CardSelectTr>tbody"); 152 | _list.delete(''); 153 | log("載入清單", [..._list]); 154 | setValue(key.ship_id, [..._list]); 155 | _table.children.forEach(tr => { 156 | let 157 | _id = tr.children[0].textContent.trim(), 158 | _isInList = _list.has(_id); 159 | changeColor([...tr.children], _isInList ? bg_color : ""); 160 | }); 161 | updateList(_list); 162 | }; 163 | document.querySelector("#wiki_tool_switch").onclick = function () { 164 | let is_on = this.classList.contains("active"); 165 | if (is_on) this.classList.remove("active"); 166 | if (!is_on) this.classList.add("active"); 167 | this.classList.remove(is_on ? "btn-success" : "btn-default"); 168 | this.classList.add(!is_on ? "btn-success" : "btn-default"); 169 | this.textContent = `標記功能: ${is_on ? "OFF" : "ON"}`; 170 | }; 171 | 172 | log("載入清單", getValue(key.ship_id)); 173 | loadIcon(); 174 | 175 | function loadIcon() { 176 | let 177 | _getData = () => { 178 | let 179 | list = getValue(key.ship_icon), 180 | data = {}; 181 | list.forEach(o => data[o.id] = o.icon); 182 | return data; 183 | }, 184 | _data = _getData(); 185 | 186 | log(`開始載入icon`); 187 | table.children.forEach(tr => { 188 | let 189 | _id = tr.children[0].textContent.trim(), 190 | _name = tr.children[1], 191 | _img = _name.querySelector("img"), 192 | _div = _name.querySelector("div"); 193 | tr.style.padding = "0px"; 194 | if (_img) _img.remove(); // remove old img if exist 195 | if (_data[_id]) { 196 | _img = document.createElement("img"); 197 | _img.src = _data[_id]; 198 | _img.className = "fleet_tech_tool_ship_icon"; 199 | _name.insertAdjacentElement("afterbegin", _img); 200 | if (!_div) { 201 | _div = document.createElement("div"); 202 | _div.className = "fleet_tech_tool_ship_name"; 203 | _div.appendChild(_name.querySelector("a")); 204 | _name.insertAdjacentElement("beforeend", _div); 205 | } 206 | } 207 | }); 208 | } 209 | 210 | async function checkIcon() { 211 | let icon_length = getValue(key.ship_icon).length; 212 | if (data_srcipt.length != icon_length) { 213 | //add update btn 214 | log(`檢查icon, 本頁${data_srcipt.length} != 已取得${icon_length}, 開始更新`); 215 | await iconHarvester(); 216 | } else { 217 | log(`檢查icon, 本頁${data_srcipt.length} == 已取得${icon_length}, 不需更新`); 218 | } 219 | 220 | async function iconHarvester() { 221 | let 222 | url = "https://wiki.biligame.com/blhx/%E5%AE%9E%E8%A3%85%E6%97%A5%E6%9C%9F", 223 | parser = new DOMParser(), 224 | page = await fetch(url); 225 | if (page.status !== 200) { 226 | throw Error(`unable to get ${url}`); 227 | } else { 228 | let 229 | html_text = await page.text(), 230 | dom = parser.parseFromString(html_text, "text/html"), 231 | waitParser = new Promise((resolve) => { if (dom.readyState == "complete") resolve(true); }), 232 | data_icon = [], 233 | promise_list = []; 234 | log("wait dom"); 235 | 236 | await waitParser.then(() => { 237 | log("dom loaded"); 238 | let data_wiki = extractData(dom); 239 | data_wiki.forEach(o => { 240 | if (data_srcipt.find(id => id == o.id)) { 241 | data_icon.push({ 242 | id: o.id, 243 | icon: o.icon, 244 | }); 245 | } 246 | }); 247 | log("更新完畢", { data_wiki, data_srcipt, data_icon }); 248 | }); 249 | 250 | log("開始轉換為快取"); 251 | data_icon.forEach(data => { 252 | promise_list.push( 253 | fetchImageToDataURI(data.icon) 254 | .then(data_url => data.icon = data_url) 255 | ); 256 | }); 257 | 258 | await Promise.all(promise_list); 259 | log("轉換完畢"); 260 | setValue(key.ship_icon, data_icon); 261 | 262 | return true; 263 | } 264 | 265 | async function fetchImageToDataURI(url = "", test = false) { 266 | let local = window.location.protocol == "file:" ? true : false; 267 | if (test || local) { 268 | return url; // can't fetch in local file 269 | } else { 270 | return fetch(url).then(r => { 271 | return r.blob(); 272 | }).then(blob => { 273 | return blobToURL(blob); 274 | }); 275 | } 276 | function blobToURL(blob) { 277 | return new Promise((resolve, reject) => { 278 | var fr = new FileReader(); 279 | fr.onload = () => { resolve(fr.result); }; 280 | fr.onerror = reject; 281 | fr.readAsDataURL(blob); 282 | }); 283 | } 284 | } 285 | 286 | function extractData(dom) { 287 | let 288 | extractor = (ele) => { 289 | let _data = [], 290 | nor = (node) => node.textContent.trim().replace(/[\s·]/mg, ""); 291 | ele.querySelector("tbody") 292 | .children 293 | .forEach((line, i) => { 294 | if (i > 0) { 295 | let img = line.querySelector("img"); 296 | if (img) { 297 | _data.push({ 298 | id: nor(line.children[0]), 299 | name: nor(line.children[1]), 300 | //date: nor(node.children[2]), 301 | icon: img.src, 302 | }); 303 | } 304 | } 305 | }); 306 | return _data; 307 | }, 308 | list = [], 309 | target = dom.querySelector("#mw-content-text"); 310 | //log([target]); 311 | target.querySelector("div[class=row]") 312 | .children 313 | .forEach(ele => list = list.concat(extractor(ele))); 314 | //log(JSON.stringify(list)); 315 | //log(list); 316 | return list; 317 | } 318 | } 319 | } 320 | 321 | function fixDataHeader() { 322 | let head = document.querySelector("#CardSelectTr>thead"); 323 | if (head.children.length == 1) { 324 | let bottom = document.querySelector("#CardSelectTr>tbody>tr"); 325 | log("missing head, trying to fix it"); 326 | if (!(bottom.innerHTML.match(/dataHeader headerSort/gm))) { 327 | throw Error(`element not found, abort\n${bottom.innerHTML}`); 328 | } else { 329 | head.appendChild(bottom); 330 | log("head fixed"); 331 | } 332 | } else { 333 | log("normal datahead"); 334 | } 335 | } 336 | 337 | function addSwitch(elelist, target) { 338 | target.onclick = () => checkList(); 339 | checkList(true); // at start 340 | 341 | async function checkList(ini = false) { 342 | let 343 | addToList = async () => { 344 | _list.add(`${_id}`); 345 | setValue(key.ship_id, [..._list]); 346 | }, 347 | removeFromList = async () => { 348 | _list.delete(`${_id}`); 349 | setValue(key.ship_id, [..._list]); 350 | }, 351 | _list = new Set(getValue(key.ship_id)), 352 | _id = elelist[0].innerText.trim(), 353 | _name = elelist[1].innerText.trim(), 354 | _isInList = _list.has(_id), 355 | _is_on = document.querySelector("#wiki_tool_switch")?.classList.contains("active"); 356 | if (!_is_on && !ini) return; 357 | if (ini) data_srcipt.push(_id); 358 | if (_isInList) { 359 | if (!ini) { 360 | await removeFromList(_id); 361 | log(`remove ${_id} ${_name}, list size:${_list.size}`); 362 | changeColor(elelist); 363 | updateList(_list); 364 | } else { 365 | changeColor(elelist, bg_color); 366 | } 367 | } else { 368 | if (!ini) { 369 | await addToList(_id); 370 | log(`add ${_id} ${_name}, list size:${_list.size}`); 371 | changeColor(elelist, bg_color); 372 | updateList(_list); 373 | } else { 374 | changeColor(elelist); 375 | } 376 | } 377 | } 378 | } 379 | } 380 | })(); 381 | 382 | -------------------------------------------------------------------------------- /azurlanesd_renamer.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name AzurLaneSD 名稱可讀化 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/azurlanesd_renamer.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/azurlanesd_renamer.user.js 6 | // @version 0.03 7 | // @description 附加可讀的名稱 8 | // @author x94fujo6 9 | // @match https://pelom777.github.io/AzurLaneSD/* 10 | // @match http://pelom777.github.io/AzurLaneSD/* 11 | // @match https://pelom.gitee.io/azurlanesd/* 12 | // @match http://pelom.gitee.io/azurlanesd/* 13 | // @match https://alsd.pelom.cn/* 14 | // @match http://alsd.pelom.cn/* 15 | // ==/UserScript== 16 | /* jshint esversion: 9 */ 17 | 18 | (function () { 19 | let css_name = {}; 20 | 21 | window.onload = () => { 22 | console.log("script start"); 23 | main(); 24 | }; 25 | 26 | function sleep(ms = 0) { 27 | return new Promise(resolve => setTimeout(resolve, ms)); 28 | } 29 | 30 | async function main(retry = 10) { 31 | let list = document.querySelectorAll("#skeletonList option"); 32 | if (!list.length) { 33 | retry--; 34 | console.log(`list is empty... retry:${retry}`); 35 | setTimeout(() => main(retry), 500); 36 | return; 37 | } else { 38 | let ob; 39 | await addCss(); 40 | await sleep(1500); 41 | console.log("start add readable name"); 42 | replacer(); 43 | await sleep(1000); 44 | ob = new MutationObserver(() => replacer()); 45 | ob.observe(document.querySelector("#skeletonList"), { childList: true }); 46 | } 47 | } 48 | 49 | function replacer() { 50 | let list = document.querySelectorAll("#skeletonList option"), 51 | reg = /([^_\s]{1,})_([^_\s]{1,})/, 52 | len = list.length; 53 | for (let i = 0; i < len; i++) { 54 | setTimeout(async () => { 55 | let e = list[i], 56 | name = e.innerText.trim(), 57 | _css; 58 | if (name.length) { 59 | let key = await SHA(name); 60 | _css = css_name[key]; 61 | if (_css) { 62 | e.className = _css; 63 | } else { 64 | let match = name.match(reg); 65 | if (match) { 66 | key = await SHA(match[1]); 67 | _css = css_name[key]; 68 | if (_css) { 69 | e.className = _css; 70 | } 71 | } 72 | } 73 | } 74 | }); 75 | } 76 | } 77 | 78 | async function addCss() { 79 | let new_css = [], 80 | s = document.createElement("style"); 81 | Object.keys(hash_name_data).forEach(key => { 82 | let t = ` 83 | .ship_${key}::before{ 84 | content: "[${hash_name_data[key]}] "; 85 | color: orangered; 86 | } 87 | `; 88 | new_css.push(t); 89 | css_name[key] = `ship_${key}`; 90 | }); 91 | s.innerHTML = new_css.join("\n"); 92 | document.head.appendChild(s); 93 | } 94 | 95 | async function SHA(t = "") { 96 | const n = (new TextEncoder).encode(t), 97 | r = await crypto.subtle.digest("SHA-1", n); 98 | return Array.from(new Uint8Array(r)).map(t => t.toString(16).padStart(2, "0")).join("").slice(0, 5); 99 | } 100 | 101 | const hash_name_data = { 102 | "11756": "尼科洛索·達雷科", 103 | "11812": "扶桑·META", 104 | "13222": "長門", 105 | "14445": "格奈森瑙(META)", 106 | "15850": "福煦", 107 | "17115": "列星頓", 108 | "17320": "確捷", 109 | "17526": "扶桑", 110 | "17737": "華盛頓", 111 | "18104": "伊卡洛斯", 112 | "18597": "雷鳴", 113 | "21605": "螢火蟲", 114 | "25009": "哈曼改", 115 | "27239": "松風", 116 | "27529": "凱旋", 117 | "27930": "Z2", 118 | "29049": "榛名", 119 | "32969": "亞利桑那", 120 | "33521": "阿拉巴馬", 121 | "34083": "巴爾的摩(μ兵裝)", 122 | "34329": "托里拆利", 123 | "35255": "久遠", 124 | "36360": "滿潮", 125 | "37719": "霧島", 126 | "39641": "最上", 127 | "41553": "秋月律子", 128 | "42231": "火槍手", 129 | "44306": "U-556", 130 | "45699": "賓夕法尼亞", 131 | "46265": "昆西", 132 | "46591": "絆愛·SuperGamer", 133 | "46767": "風雲", 134 | "48581": "柯尼斯堡", 135 | "48613": "土佐", 136 | "50441": "絆愛·Elegant", 137 | "57429": "阿卡司塔改", 138 | "60539": "奧利克", 139 | "61643": "U-37", 140 | "61905": "露露緹耶", 141 | "63327": "惡毒(μ兵裝)", 142 | "65143": "龍騎兵", 143 | "65711": "大鬥犬", 144 | "68041": "初霜", 145 | "70130": "信濃", 146 | "72231": "博格改", 147 | "73583": "加賀", 148 | "73836": "川內", 149 | "74340": "加古", 150 | "74466": "Z23", 151 | "78178": "小光輝", 152 | "79029": "阿賈克斯", 153 | "82573": "舊金山", 154 | "84875": "小貝法", 155 | "88347": "千代田", 156 | "90026": "翔鶴", 157 | "91674": "金伯利", 158 | "92494": "卡爾斯魯厄改", 159 | "94306": "逸仙", 160 | "95045": "女將", 161 | "99039": "加古改", 162 | "99193": "比洛克西", 163 | "3cd1e": "泛用型布里", 164 | "2453f": "試作型布里MKII", 165 | "4a638": "特裝型布里MKIII", 166 | "2d4cc": "杜威", 167 | "676d0": "卡辛", 168 | "27b92": "卡辛改", 169 | "95f11": "唐斯", 170 | "9388e": "唐斯改", 171 | "27b72": "格里德利", 172 | "0087b": "克雷文", 173 | "d25b1": "麥考爾", 174 | "a4b05": "莫里", 175 | "b137c": "佛萊契爾", 176 | "baf2f": "查爾斯·奧斯本", 177 | "07350": "柴契爾", 178 | "17dd3": "富特", 179 | "293fb": "斯彭斯", 180 | "56b90": "班森", 181 | "dfaa3": "拉菲", 182 | "8fafc": "拉菲改", 183 | "f6194": "西姆斯", 184 | "2a2da": "西姆斯改", 185 | "d4bc1": "哈曼", 186 | "ca286": "埃爾德里奇", 187 | "0848a": "貝利", 188 | "4b3c1": "貝利改", 189 | "0285b": "拉德福特", 190 | "3d842": "傑金斯", 191 | "e32b5": "尼古拉斯", 192 | "d5ae5": "尼古拉斯改", 193 | "75eaf": "布希", 194 | "35a55": "黑澤伍德", 195 | "60f5d": "貝奇", 196 | "e6c55": "霍比", 197 | "ec052": "科爾克", 198 | "be420": "馬拉尼", 199 | "60d8a": "艾爾文", 200 | "88e07": "斯坦利", 201 | "c4615": "斯莫利", 202 | "cee46": "海爾賽‧鮑威爾", 203 | "efaae": "庫珀", 204 | "d0f42": "艾倫·薩姆納", 205 | "837d6": "史蒂芬·波特", 206 | "fdbb7": "莫里森", 207 | "6c185": "英格拉罕", 208 | "5da52": "奧馬哈", 209 | "5ef7b": "羅利", 210 | "a27b8": "布魯克林", 211 | "3cfe0": "菲尼克斯", 212 | "a26ae": "海倫娜", 213 | "e14b3": "海倫娜改", 214 | "1d5d6": "亞特蘭大", 215 | "399a5": "朱諾", 216 | "84bad": "聖地牙哥", 217 | "8594d": "聖地牙哥改", 218 | "ba3b2": "克里夫蘭", 219 | "bcd86": "哥倫比亞", 220 | "3eed7": "里奇蒙", 221 | "e7b80": "火奴魯魯", 222 | "afeb3": "聖路易斯", 223 | "3960d": "蒙彼利埃", 224 | "16ccc": "丹佛", 225 | "ded24": "曼非斯", 226 | "27e14": "康克德", 227 | "1efc3": "小海倫娜", 228 | "d7372": "小克利夫蘭", 229 | "849b1": "小聖地牙哥", 230 | "e17e7": "聖胡安", 231 | "c93b3": "伯明罕", 232 | "a5c8c": "克里夫蘭(μ兵裝)", 233 | "23e19": "雷諾", 234 | "fc93a": "馬布爾黑德", 235 | "07c48": "博伊西", 236 | "98f24": "彭薩科拉", 237 | "af7e6": "鹽湖城", 238 | "b1960": "北安普敦", 239 | "bb136": "芝加哥", 240 | "a6cba": "休士頓", 241 | "bc44c": "波特蘭", 242 | "9f5ff": "波特蘭改", 243 | "babb5": "印第安納波利斯", 244 | "9629e": "阿斯托利亞", 245 | "5ab44": "文森尼斯", 246 | "85ecd": "威奇塔", 247 | "ae656": "新奧爾良", 248 | "ad286": "明尼亞波利斯", 249 | "94d9d": "巴爾的摩", 250 | "7e8c2": "布雷默頓", 251 | "4708b": "內華達", 252 | "c75a6": "內華達改", 253 | "d8da7": "奧克拉荷馬", 254 | "e298b": "奧克拉荷馬改", 255 | "07071": "田納西", 256 | "738ff": "加利福尼亞", 257 | "ad13e": "科羅拉多", 258 | "98f3d": "馬里蘭", 259 | "a6aa3": "西維吉尼亞", 260 | "969b9": "北卡羅來納", 261 | "68a6a": "南達科他", 262 | "d240d": "新澤西", 263 | "7f581": "麻薩諸塞", 264 | "9303f": "長島", 265 | "5ff5a": "長島改", 266 | "d62ac": "博格", 267 | "46a4d": "卡薩布蘭卡", 268 | "8d75b": "蘭利", 269 | "df538": "蘭利改", 270 | "b5706": "薩拉托加", 271 | "e57e2": "薩拉托加改", 272 | "5f3d6": "遊騎兵", 273 | "a8c0e": "遊騎兵改", 274 | "a4f8b": "約克鎮", 275 | "1f2a5": "企業", 276 | "bbcbb": "大黃蜂", 277 | "cf31d": "胡蜂", 278 | "fc550": "艾塞克斯", 279 | "da7a0": "無畏", 280 | "bced1": "提康德羅加", 281 | "9fa49": "碉堡山", 282 | "2c0b3": "獨立", 283 | "7bee5": "獨立改", 284 | "bb0cb": "普林斯頓", 285 | "b8099": "巴丹", 286 | "c3078": "香格里拉", 287 | "f7bab": "小企業", 288 | "7a62a": "鰷魚", 289 | "30ff4": "大青花魚", 290 | "80fa4": "棘鰭", 291 | "dd6f6": "藍鰓魚", 292 | "6e148": "大青花魚(μ兵裝)", 293 | "64b94": "射水魚", 294 | "a65e9": "鸚鵡螺", 295 | "7d9bb": "女灶神", 296 | "f2cbf": "西雅圖", 297 | "2a910": "喬治亞", 298 | "6f759": "安克雷奇", 299 | "8538e": "女將改", 300 | "3715b": "阿卡司塔", 301 | "7b2b5": "熱心", 302 | "8f676": "熱心改", 303 | "d46a6": "小獵兔犬", 304 | "918ea": "彗星", 305 | "9f6bd": "彗星改", 306 | "4b2e5": "新月", 307 | "b95c1": "新月改", 308 | "6d0b4": "小天鵝", 309 | "b651e": "小天鵝改", 310 | "402bf": "狐提", 311 | "5e52f": "狐提改", 312 | "c20f4": "命運女神", 313 | "028f4": "命運女神改", 314 | "7df8e": "格倫維爾", 315 | "ee13e": "勇敢", 316 | "e323f": "獵人", 317 | "0bf1d": "標槍", 318 | "1f7ba": "標槍改", 319 | "b4c4c": "天后", 320 | "01d7d": "吸血鬼", 321 | "bc2ab": "丘比特", 322 | "1dbe0": "澤西", 323 | "eba49": "無敵", 324 | "a1b11": "回聲", 325 | "5e53a": "愛斯基摩人", 326 | "9a263": "利安得", 327 | "4b89a": "利安得改", 328 | "0d841": "阿基里斯", 329 | "5537b": "阿基里斯改", 330 | "ec4b1": "阿賈克斯改", 331 | "99a4d": "黛朵", 332 | "9f588": "南安普敦", 333 | "b0470": "謝菲爾德", 334 | "417f6": "格洛斯特", 335 | "4123d": "愛丁堡", 336 | "f1cf1": "貝爾法斯特", 337 | "37c6a": "阿瑞托莎", 338 | "b6adc": "加拉蒂亞", 339 | "f167d": "歐若拉", 340 | "3c9d7": "斐濟", 341 | "8a7bc": "牙買加", 342 | "9e6b4": "紐卡斯爾", 343 | "81f72": "紐卡斯爾改", 344 | "aede9": "天狼星", 345 | "dc15c": "庫拉索", 346 | "3715f": "庫拉索改", 347 | "e9798": "杓鷸", 348 | "be1ea": "杓鷸改", 349 | "9cd29": "黑太子", 350 | "871a6": "謝菲爾德(μ兵裝)", 351 | "189a7": "格拉斯哥", 352 | "7d690": "赫敏", 353 | "73b31": "黛朵(μ兵裝)", 354 | "a21ce": "佩內洛珀", 355 | "15b29": "倫敦", 356 | "114f9": "倫敦改", 357 | "c7e60": "什羅普郡", 358 | "ead01": "肯特", 359 | "ecf9a": "薩福克", 360 | "4b97a": "薩福克改", 361 | "04443": "諾福克", 362 | "1ba46": "多塞特郡", 363 | "e3ee2": "約克", 364 | "9cdfe": "約克改", 365 | "f1612": "埃克塞特", 366 | "4a67a": "埃克塞特改", 367 | "ccf3f": "蘇塞克斯", 368 | "e1bf2": "聲望", 369 | "dcedc": "反擊", 370 | "0a60d": "胡德", 371 | "0824e": "小聲望", 372 | "07070": "伊莉莎白女王", 373 | "1513b": "厭戰", 374 | "f2633": "厭戰改", 375 | "62b82": "納爾遜", 376 | "c88cc": "羅德尼", 377 | "5f02a": "英王喬治五世", 378 | "cf369": "威爾斯親王", 379 | "a6287": "約克公爵", 380 | "32da4": "豪", 381 | "79c87": "英勇", 382 | "4e2bf": "競技神", 383 | "b15d5": "競技神改", 384 | "e00dc": "獨角獸", 385 | "fe2e1": "半人馬", 386 | "9bfcd": "追趕者", 387 | "f44f3": "英仙座", 388 | "125a1": "鷹", 389 | "ecbd1": "皇家方舟", 390 | "0c636": "皇家方舟改", 391 | "bc858": "光輝", 392 | "262bb": "勝利", 393 | "53e9f": "可畏", 394 | "9c823": "光榮", 395 | "20ca3": "光輝(μ兵裝)", 396 | "1d84a": "黑暗界", 397 | "8f240": "恐怖", 398 | "9ef1c": "阿貝克隆比", 399 | "630aa": "海王星", 400 | "9565a": "君主", 401 | "7c975": "柴郡", 402 | "d7323": "德雷克", 403 | "605ff": "吹雪", 404 | "0d16d": "白雪", 405 | "15c13": "綾波", 406 | "e4c83": "綾波改", 407 | "3ba7f": "曉", 408 | "4abe2": "響", 409 | "a56ad": "雷", 410 | "06dbf": "電", 411 | "c8aa5": "白露", 412 | "52f87": "夕立", 413 | "d61e1": "夕立改", 414 | "fd794": "時雨", 415 | "dff3d": "時雨改", 416 | "13a8d": "雪風", 417 | "b10c4": "陽炎", 418 | "2135c": "陽炎改", 419 | "2a0fd": "不知火", 420 | "054ea": "不知火改", 421 | "936e6": "野分", 422 | "90d4a": "初春", 423 | "41b1c": "初春改", 424 | "36b7e": "若葉", 425 | "7f8e8": "初霜改", 426 | "a8eea": "有明", 427 | "734d2": "有明改", 428 | "6aad4": "夕暮", 429 | "37e3b": "夕暮改", 430 | "5b063": "黑潮", 431 | "adab2": "親潮", 432 | "c2742": "島風", 433 | "ffc04": "神風", 434 | "f1222": "神風改", 435 | "9bfa8": "松風改", 436 | "b3655": "睦月", 437 | "3c4cf": "睦月改", 438 | "ebe97": "如月", 439 | "3f8b7": "如月改", 440 | "ebe07": "卯月", 441 | "3e197": "水無月", 442 | "bba0e": "文月", 443 | "d701a": "長月", 444 | "31da6": "三日月", 445 | "fa86c": "海風", 446 | "d08a5": "山風", 447 | "0b331": "江風", 448 | "6b1f4": "清波", 449 | "1d589": "新月", 450 | "10ab7": "春月", 451 | "30ece": "宵月", 452 | "0b9ad": "浦風", 453 | "825db": "磯風", 454 | "0f36c": "濱風", 455 | "e300b": "濱風改", 456 | "5314c": "谷風", 457 | "bd23a": "谷風改", 458 | "daaed": "朝潮", 459 | "84c1c": "大潮", 460 | "fb4dc": "荒潮", 461 | "58ca3": "浦波", 462 | "4bbba": "旗風", 463 | "673e5": "卷波", 464 | "2767e": "霞", 465 | "c8900": "霞改", 466 | "d0041": "花月", 467 | "6c1f4": "長波", 468 | "fe0a6": "涼月", 469 | "8076d": "追風", 470 | "232e6": "夕張", 471 | "d91b9": "夕張改", 472 | "c6ad4": "長良", 473 | "8fa9e": "五十鈴", 474 | "d48f4": "五十鈴改", 475 | "f6db4": "由良", 476 | "b172d": "鬼怒", 477 | "0d0fd": "鬼怒改", 478 | "4afab": "阿武隈", 479 | "0e3c8": "阿武隈改", 480 | "a6b98": "最上改", 481 | "00213": "三隈", 482 | "534ec": "川內改", 483 | "839e7": "神通", 484 | "4f019": "神通改", 485 | "c7c84": "那珂", 486 | "4d569": "阿賀野", 487 | "d938e": "能代", 488 | "cd93f": "古鷹", 489 | "a1059": "古鷹改", 490 | "ad573": "青葉", 491 | "55e73": "衣笠", 492 | "bf5ee": "築摩", 493 | "948fc": "妙高", 494 | "94e03": "那智", 495 | "fab3c": "足柄", 496 | "5b237": "高雄", 497 | "20c9b": "愛宕", 498 | "4d13b": "摩耶", 499 | "21eaa": "鳥海", 500 | "fc7f4": "鈴谷", 501 | "4918e": "熊野", 502 | "95dfc": "金剛", 503 | "79c5d": "比叡", 504 | "adc11": "天城", 505 | "52d6e": "小比叡", 506 | "2511a": "小天城", 507 | "99e4b": "扶桑改", 508 | "cfadb": "山城", 509 | "b5f7f": "山城改", 510 | "dc14a": "伊勢", 511 | "2f505": "伊勢改", 512 | "03403": "日向", 513 | "447b8": "日向改", 514 | "ee272": "陸奧", 515 | "4b5b0": "三笠", 516 | "2056e": "紀伊", 517 | "36f0e": "駿河", 518 | "8f6df": "飛鷹", 519 | "c2a3f": "隼鷹", 520 | "549a4": "鳳翔", 521 | "01f48": "祥鳳", 522 | "ccf95": "祥鳳改", 523 | "619dd": "龍驤", 524 | "66da9": "龍鳳", 525 | "b96d7": "千歲", 526 | "bc124": "赤城", 527 | "29ba6": "加賀", 528 | "4679d": "蒼龍", 529 | "ca883": "蒼龍改", 530 | "484cd": "飛龍", 531 | "5ebf7": "飛龍改", 532 | "b80b8": "瑞鶴", 533 | "487fb": "大鳳", 534 | "f6bea": "小赤城", 535 | "151e0": "赤城(μ兵裝)", 536 | "dd63a": "大鳳", 537 | "318d5": "葛城", 538 | "54cc3": "伊19", 539 | "5cf1c": "伊26", 540 | "108cb": "伊58", 541 | "f5114": "伊25", 542 | "7680f": "伊56", 543 | "a4962": "伊168", 544 | "4f34b": "明石", 545 | "527b8": "伊13", 546 | "5bd5b": "樫野", 547 | "dab58": "伊吹", 548 | "d1892": "出雲", 549 | "e5145": "北風", 550 | "ef610": "吾妻", 551 | "f8ea5": "白龍", 552 | "380a4": "Z1", 553 | "af6b3": "Z1改", 554 | "d057a": "Z18", 555 | "5fb8b": "Z19", 556 | "2f4d4": "Z20", 557 | "da059": "Z21", 558 | "adb87": "Z23改", 559 | "31a63": "Z24", 560 | "7b8e2": "Z25", 561 | "50a09": "Z26", 562 | "0d4fe": "Z28", 563 | "b7518": "Z35", 564 | "1038a": "Z36", 565 | "618b8": "Z46", 566 | "7e527": "卡爾斯魯厄", 567 | "0a8bb": "科隆", 568 | "3502b": "科隆改", 569 | "64c02": "萊比錫", 570 | "904db": "萊比錫改", 571 | "da8c9": "紐倫堡", 572 | "1313d": "希佩爾將軍", 573 | "37c07": "歐根親王", 574 | "573b2": "德意志", 575 | "fcdbd": "施佩伯爵將軍", 576 | "edc83": "希佩爾將軍(μ兵裝)", 577 | "0c564": "羅恩(μ兵裝)", 578 | "53c82": "海因里希親王", 579 | "294f5": "沙恩霍斯特", 580 | "f094c": "格奈森瑙", 581 | "9af68": "俾斯麥", 582 | "a9aff": "鐵必制", 583 | "2b8de": "威悉", 584 | "1ba54": "齊柏林伯爵", 585 | "cbbf1": "小齊柏林", 586 | "ed8ef": "彼得·史特拉塞", 587 | "30d58": "U-81", 588 | "52c51": "U-47", 589 | "ee540": "U-557", 590 | "5e0b2": "U-73", 591 | "df905": "U-101", 592 | "000c6": "U-522", 593 | "1d03f": "U-110", 594 | "924a2": "U-96", 595 | "6aebb": "U-410", 596 | "f93ed": "羅恩", 597 | "a8e59": "腓特烈大帝", 598 | "b2970": "美因茲", 599 | "06e1b": "奧丁", 600 | "6ea0a": "埃吉爾", 601 | "9fb08": "奧古斯特·馮·帕塞瓦爾", 602 | "6dfeb": "鞍山", 603 | "0d2ba": "撫順", 604 | "cdc90": "長春", 605 | "b61b2": "太原", 606 | "cb96f": "寧海", 607 | "4bf23": "寧海改", 608 | "643f1": "平海", 609 | "647b2": "平海改", 610 | "ad037": "應瑞", 611 | "f292e": "肇和", 612 | "49fc0": "文琴佐·焦貝蒂", 613 | "940c2": "西北風", 614 | "8cbc9": "西南風", 615 | "3480c": "阿布魯齊公爵", 616 | "64f11": "特倫托", 617 | "165d2": "扎拉", 618 | "2ddb7": "波拉", 619 | "0bde7": "維托里奧·維內托", 620 | "08d77": "利托里奧", 621 | "473b7": "加富爾伯爵", 622 | "ae3a3": "朱利奧·凱撒", 623 | "af000": "天鷹", 624 | "82fea": "馬可波羅", 625 | "df181": "威嚴", 626 | "939d1": "明斯克", 627 | "b6fa2": "塔什干", 628 | "105ed": "塔什干(μ兵裝)", 629 | "02d3f": "神速", 630 | "025d8": "洪亮", 631 | "a1bf7": "曙光", 632 | "f80ca": "水星紀念", 633 | "df7d8": "水星紀念改", 634 | "5c010": "恰巴耶夫", 635 | "1100e": "基洛夫", 636 | "a4394": "摩爾曼斯克", 637 | "47b9b": "塔林", 638 | "7f615": "甘古特", 639 | "f3cbd": "蘇維埃貝拉羅斯", 640 | "9151b": "蘇維埃俄羅斯", 641 | "af40d": "福爾班", 642 | "cb402": "福爾班改", 643 | "425d3": "魯莽", 644 | "cbf4e": "倔強", 645 | "130cb": "可怖", 646 | "d4002": "馬耶·布雷澤", 647 | "7b204": "埃米爾•貝爾坦", 648 | "c16bb": "埃米爾•貝爾坦改", 649 | "f1ea0": "聖女貞德", 650 | "a425a": "黎胥留", 651 | "858b0": "貝亞恩", 652 | "c9bac": "速科夫", 653 | "d77c6": "路易九世", 654 | "ce127": "香檳", 655 | "96d37": "勒馬爾", 656 | "3abd9": "勒馬爾改", 657 | "e82e6": "塔爾圖", 658 | "eb7c6": "沃克蘭", 659 | "863f8": "惡毒", 660 | "c5d2c": "拉·加利索尼埃", 661 | "dad01": "阿爾及利亞", 662 | "a3440": "敦克爾克", 663 | "e33eb": "讓·巴爾", 664 | "1f04c": "加斯科涅(μ兵裝)", 665 | "a27f3": "加斯科涅", 666 | "6d517": "海倫娜·META", 667 | "89e9f": "飛鷹(META)", 668 | "747a4": "飛龍·META", 669 | "3b4cc": "皇家方舟·META", 670 | "7ff40": "蒼龍·META", 671 | "8f86d": "涅普頓", 672 | "f9fda": "諾瓦露", 673 | "50d98": "布蘭", 674 | "86ead": "貝露", 675 | "9c5d0": "紺紫之心", 676 | "a088e": "聖黑之心", 677 | "c78aa": "群白之心", 678 | "1c010": "翡綠之心", 679 | "a31e4": "貓音", 680 | "1d582": "烏璐露", 681 | "9f52d": "薩拉娜", 682 | "aef91": "芙米露露", 683 | "45c9d": "絆愛", 684 | "dcc0f": "絆愛·Anniversary", 685 | "7e339": "白上吹雪", 686 | "897a1": "時乃空", 687 | "453c2": "湊阿庫婭", 688 | "e2d45": "夏色祭", 689 | "a31eb": "百鬼綾目", 690 | "6514f": "紫咲詩音", 691 | "f923d": "大神澪", 692 | "1d9c7": "瑪莉蘿絲", 693 | "00360": "穗香", 694 | "474b1": "霞", 695 | "e1f15": "海咲", 696 | "4cada": "凪咲", 697 | "84af7": "女天狗", 698 | "9b97a": "莫妮卡", 699 | "2b565": "天海春香", 700 | "f174c": "如月千早", 701 | "b5989": "水瀨伊織", 702 | "1396b": "三浦梓", 703 | "a063f": "雙海亞美", 704 | "4c4d3": "雙海真美", 705 | "862d4": "寶多六花", 706 | "edef0": "新條茜", 707 | "e6f14": "莲", 708 | "e502d": "奈美子", 709 | "fcddd": "南夢芽", 710 | "3f463": "飛鳥川千瀨", 711 | "30f08": "貉" 712 | }; 713 | })(); 714 | -------------------------------------------------------------------------------- /display_actual_volume.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name 顯示實際水量 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/display_actual_volume.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/display_actual_volume.user.js 6 | // @version 0.8 7 | // @description 顯示最大蓄水量,顯示上升/下降的實際水量而不是百分比 8 | // @author x94fujo6 9 | // @match https://water.taiwanstat.com/ 10 | // @grant none 11 | // @run-at document-end 12 | // ==/UserScript== 13 | /* jshint esversion: 9 */ 14 | 15 | (function () { 16 | wait(); 17 | 18 | function log(...any) { 19 | console.log(`%c[顯示實際水量]%c`, "color:OrangeRed;", "", ...any); 20 | } 21 | 22 | function wait(retry = 30) { 23 | let target = document.querySelectorAll(".state.blue,.state.red"); 24 | if (!target.length && retry > 0) { 25 | retry--; 26 | log(`target not found, remaining retries [${retry}]`); 27 | setTimeout(() => wait(retry), 500); 28 | } else { 29 | setTimeout(getData, 500); 30 | } 31 | } 32 | 33 | function getData() { 34 | let dataURL = "https://www.taiwanstat.com/waters/latest"; 35 | $.getJSON(dataURL) 36 | .done((data) => main(data[0])) 37 | .fail(() => console.log("getJSON failed")); 38 | } 39 | 40 | function main(data) { 41 | data = mapToID(data); 42 | let eles = document.querySelectorAll("div.reservoir-wrap>div"), 43 | numReg = /:(\d+.\d+)/, 44 | sum = sumAll(data, "volumn"), 45 | sumMax = sumAll(data, "baseAvailable"); 46 | if (!eles) return; 47 | eles.forEach(ele => { 48 | addData(ele, sum, sumMax); 49 | 50 | let name = ele.querySelector(".name"); 51 | if (name) name.childNodes[0].textContent += "  "; 52 | }); 53 | 54 | let ele = addSum(sum, sumMax, data); 55 | addData(ele, sum, sumMax); 56 | editCss(); 57 | add_sort(); 58 | 59 | function add_sort() { 60 | let 61 | b_data = [ 62 | { t: "預設", key: "sid" }, 63 | { t: "有效蓄水", key: "vol" }, 64 | { t: "最大蓄水", key: "max" }, 65 | { t: "昨日上升", key: "inc_v" }, 66 | { t: "昨日下降", key: "dec_v" }, 67 | { t: "昨日上升(%)", key: "inc_p" }, 68 | { t: "昨日下降(%)", key: "dec_p" }, 69 | ], 70 | pos = document.querySelector("div.reservoir-wrap"), 71 | box = Object.assign(document.createElement("div"), { className: "sortbox" }), 72 | sel_a = Object.assign(document.createElement("select"), { id: "sort_des" }), 73 | sel_b = Object.assign(document.createElement("select"), { id: "sort_key" }), 74 | option, 75 | sorting = () => { 76 | let des = document.querySelector("#sort_des").selectedOptions[0].value, 77 | key = document.querySelector("#sort_key").selectedOptions[0].value; 78 | resort(key, parseInt(des)); 79 | }, 80 | b = Object.assign(document.createElement("button"), { textContent: "排序", onclick: () => sorting() }); 81 | 82 | box.style = ` 83 | text-align: center; 84 | font-size: large; 85 | `; 86 | 87 | b.style = ` 88 | margin: 0px 10px; 89 | padding: 0px 20px; 90 | `; 91 | 92 | sel_a.innerHTML = ` 93 | 94 | 95 | `; 96 | 97 | b_data.forEach(data => { 98 | option = document.createElement("option"); 99 | option.text = data.t; 100 | option.value = data.key; 101 | sel_b.appendChild(option); 102 | }); 103 | 104 | box.appendChild(sel_a); 105 | box.appendChild(sel_b); 106 | box.appendChild(b); 107 | pos.insertAdjacentElement("beforebegin", box); 108 | 109 | function resort(key, des = true) { 110 | let 111 | pos = document.querySelector("div.reservoir-wrap"), 112 | eles = pos.querySelectorAll("div.reservoir"), 113 | sort_by = { 114 | sid: true, 115 | vol: /有效蓄水量:(\d+\.\d+)萬立方公尺/, 116 | max: /最大蓄水量:(\d+\.\d+)萬立方公尺/, 117 | inc_p: /昨日水量上升:(\d+\.\d+)%/, 118 | inc_v: /昨日水量上升:(\d+\.\d+)萬立方公尺/, 119 | dec_p: /昨日水量下降:(\d+\.\d+)%/, 120 | dec_v: /昨日水量下降:(\d+\.\d+)萬立方公尺/, 121 | }, 122 | data = [], 123 | rid, sortdata, 124 | ns = (a, b) => String(a).localeCompare(String(b), navigator.languages[0] || navigator.language, { numeric: true }); 125 | 126 | if (!sort_by[key]) throw Error("unknown key"); 127 | 128 | // set id for sort 129 | if (!eles[0].getAttribute("sid")) { 130 | eles.forEach(e => { 131 | e.setAttribute("sid", e.querySelector("svg").id); 132 | }); 133 | } 134 | 135 | eles.forEach(e => { 136 | rid = e.getAttribute("sid"); 137 | if (key !== "sid") { 138 | sortdata = e.innerHTML.match(sort_by[key]); 139 | sortdata = sortdata ? sortdata[1] : ""; 140 | } else { 141 | sortdata = rid; 142 | } 143 | data.push({ 144 | ele: e, 145 | sortdata: sortdata, 146 | }); 147 | }); 148 | 149 | if (key !== "sid") { 150 | if (!des) { 151 | data = data.sort((a, b) => ns(a.sortdata, b.sortdata)); 152 | } else { 153 | data = data.sort((a, b) => ns(b.sortdata, a.sortdata)); 154 | } 155 | } else { 156 | if (des) { 157 | data = data.sort((a, b) => ns(a.sortdata, b.sortdata)); 158 | } else { 159 | data = data.sort((a, b) => ns(b.sortdata, a.sortdata)); 160 | } 161 | } 162 | 163 | log("sort result:", data); 164 | data.forEach(o => pos.appendChild(o.ele)); 165 | } 166 | } 167 | 168 | function sumAll(data, target) { 169 | let sum = 0; 170 | for (let key in data) { 171 | let num = parseFloat(data[key][target]); 172 | sum += isNaN(num) ? 0 : num; 173 | } 174 | log(`${sumAll.name} [${target}]: ${sum}`); 175 | return sum; 176 | } 177 | 178 | function mapToID(data) { 179 | let newData = {}; 180 | Object.keys(data).forEach(key => { 181 | let o = data[key]; 182 | newData[o.id] = o; 183 | }); 184 | return newData; 185 | } 186 | 187 | function addSum(sum, sumMax, rdata) { 188 | let reservoirName = "全台水庫", 189 | dataForSvg = {}; 190 | dataForSvg[reservoirName] = { 191 | name: reservoirName, 192 | daliyNetflow: sumAll(rdata, "daliyNetflow"), 193 | daliyInflow: sumAll(rdata, "daliyInflow"), 194 | daliyOverflow: sumAll(rdata, "daliyOverflow"), 195 | percentage: 0, 196 | volumn: parseFloat(sum).toFixed(2), 197 | updateAt: "userscript", 198 | id: "reservoir999", 199 | baseAvailable: parseFloat(sumMax).toFixed(2), 200 | }; 201 | let reservoir = dataForSvg[reservoirName]; 202 | reservoir.percentage = Math.round((reservoir.volumn / reservoir.baseAvailable) * 10000) / 100; 203 | log(`percentage, ${reservoir.percentage}`); 204 | 205 | // add to data 206 | data[reservoir.id] = reservoir; 207 | log(reservoir); 208 | 209 | let newEle = addNewEle(); 210 | setAnimate(); 211 | return newEle; 212 | 213 | function addNewEle() { 214 | let pos = document.querySelector(".reservoir-wrap"), 215 | ele = document.createElement("div"); 216 | ele.className = "reservoir"; 217 | ele.innerHTML = ` 218 |
219 |

${reservoirName}  

220 |
221 | 222 |
223 |
${reservoir.volumn}萬立方公尺
224 |
225 |
226 |
萬立方公尺
227 |
228 |
229 |
預測剩餘天數:----
230 |
231 |
更新時間:0000-00-00 (0時)
232 | `; 233 | pos.appendChild(ele); 234 | return ele; 235 | } 236 | 237 | // source https://water.taiwanstat.com/js/index.js 238 | function setAnimate() { 239 | var percentage = parseFloat(dataForSvg[reservoirName].percentage).toFixed(1); 240 | var updateAt = dataForSvg[reservoirName].updateAt; 241 | var volumn = dataForSvg[reservoirName].volumn; 242 | var id = dataForSvg[reservoirName].id; 243 | var netFlow = -parseFloat(dataForSvg[reservoirName].daliyNetflow).toFixed(1); 244 | var netPercentageVar; 245 | 246 | log(netFlow); 247 | 248 | if (isNaN(percentage)) { 249 | $('#' + id).parent().remove(); 250 | return; 251 | } 252 | 253 | if (isNaN(netFlow)) { 254 | $('#' + id).siblings('.state') 255 | .children('h5') 256 | .text('昨日水量狀態:待更新'); 257 | $('#' + id).siblings('.state').removeClass(); 258 | } else if (netFlow < 0) { 259 | netPercentageVar = ((-netFlow) / parseFloat(dataForSvg[reservoirName].baseAvailable) * 100).toFixed(2); 260 | 261 | var usageDay = Math.round(percentage / netPercentageVar); 262 | if (dataForSvg[reservoirName].percentage > 80 && netPercentageVar > 2) usageDay = 60; 263 | 264 | if (usageDay >= 60) { 265 | usageDay = '預測剩餘天數:60天以上'; 266 | } else if (usageDay >= 30) { 267 | usageDay = '預測剩餘天數:30天-60天'; 268 | $('#' + id).siblings('.dueDay').addClass('red'); 269 | } else { 270 | usageDay = '預測剩餘天數:' + usageDay + '天'; 271 | $('#' + id).siblings('.dueDay').addClass('red'); 272 | } 273 | 274 | $('#' + id).siblings('.dueDay') 275 | .children('h5') 276 | .text(usageDay); 277 | 278 | $('#' + id).siblings('.state') 279 | .children('h5') 280 | .text('昨日水量下降:' + netPercentageVar + '%'); 281 | 282 | $('#' + id).siblings('.state').addClass('red'); 283 | } else { 284 | netPercentageVar = ((netFlow) / parseFloat(dataForSvg[reservoirName].baseAvailable) * 100).toFixed(2); 285 | 286 | $('#' + id).siblings('.state') 287 | .children('h5') 288 | .text('昨日水量上升:' + netPercentageVar + '%'); 289 | $('#' + id).siblings('.state').addClass('blue'); 290 | } 291 | 292 | configs[reservoirName] = liquidFillGaugeDefaultSettings(); 293 | configs[reservoirName].waveAnimate = true; 294 | configs[reservoirName].waveAnimateTime = setAnimateTime(percentage); 295 | configs[reservoirName].waveOffset = 0.3; 296 | configs[reservoirName].waveHeight = 0.05; 297 | configs[reservoirName].waveCount = setWavaCount(percentage); 298 | setColor(configs[reservoirName], percentage); 299 | 300 | $('#' + id).siblings('.updateAt').html('
更新時間:' + updateAt + '
'); 301 | $('#' + id).siblings('.volumn').children('h5').text('有效蓄水量:' + volumn + '萬立方公尺'); 302 | loadLiquidFillGauge(id, percentage, configs[reservoirName]); 303 | } 304 | 305 | function setColor(config, percentage) { 306 | if (percentage < 25) { 307 | config.circleColor = "#FF7777"; 308 | config.textColor = "#FF4444"; 309 | config.waveTextColor = "#FFAAAA"; 310 | config.waveColor = "#FFDDDD"; 311 | } 312 | else if (percentage < 50) { 313 | config.circleColor = "rgb(255, 160, 119)"; 314 | config.textColor = "rgb(255, 160, 119)"; 315 | config.waveTextColor = "rgb(255, 160, 119)"; 316 | config.waveColor = "rgba(245, 151, 111, 0.48)"; 317 | } 318 | } 319 | 320 | function setWavaCount(percentage) { 321 | if (percentage > 75) { 322 | return 3; 323 | } else if (percentage > 50) { 324 | return 2; 325 | } 326 | return 1; 327 | } 328 | 329 | function setAnimateTime(percentage) { 330 | if (percentage > 75) { 331 | return 2000; 332 | } else if (percentage > 50) { 333 | return 3000; 334 | } else if (percentage > 25) { 335 | return 4000; 336 | } 337 | return 5000; 338 | } 339 | 340 | function addZero(i) { 341 | if (i < 10) { 342 | i = "0" + i; 343 | } 344 | return i; 345 | } 346 | } 347 | 348 | function addData(ele, sum, sumMax) { 349 | let id = ele.querySelector("svg").id, 350 | rData = data[id], 351 | eff = ele.querySelector("div.volumn"), 352 | blue = ele.querySelector("div.state.blue h5"), 353 | red = ele.querySelector("div.state.red h5"); 354 | if (!rData) return; 355 | let max = rData.baseAvailable; 356 | addNewLine(ele, ".volumn", ` └ 佔全台:${Math.floor(max / sumMax * 10000) / 100}%`); 357 | addNewLine(ele, ".volumn", `最大蓄水量:${max}萬立方公尺`); 358 | addNewLine(ele, ".volumn", ` └ 佔全台:${getPercent(eff, sum)}%`); 359 | if (blue) { 360 | addNewLine(ele, ".state.blue", `昨日水量上升:${calcVolume(blue, max)}萬立方公尺`); 361 | } else if (red) { 362 | addNewLine(ele, ".state.red", `昨日水量下降:${calcVolume(red, max)}萬立方公尺`); 363 | } else { 364 | addNewLine(ele, ".dueDay", ` `, true); 365 | } 366 | 367 | function addNewLine(ele, targetClass, text, empty = false) { 368 | let pos = ele.querySelector(targetClass), 369 | newEle = document.createElement("div"); 370 | if (!empty) newEle.className = `${pos.className} davCss`; 371 | newEle.innerHTML = `
${text}
`; 372 | pos.insertAdjacentElement(empty ? "beforebegin" : "afterend", newEle); 373 | } 374 | 375 | function getNum(ele) { 376 | let text = ele.textContent.match(numReg), 377 | num = text ? parseFloat(text[1]) : false; 378 | return num !== false ? num : 0; 379 | } 380 | 381 | function calcVolume(ele, max) { 382 | let num = getNum(ele); 383 | return num !== 0 ? calc(num, max) : 0; 384 | 385 | function calc(num, max) { 386 | return Math.round(num * max) / 100; 387 | } 388 | } 389 | 390 | function getPercent(ele, max) { 391 | let num = getNum(ele); 392 | return num !== 0 ? calc(num, max) : 0; 393 | 394 | function calc(num, max) { 395 | return Math.floor((num / max) * 10000) / 100; 396 | } 397 | } 398 | } 399 | 400 | function editCss() { 401 | let style = 402 | [... 403 | [...document.styleSheets] 404 | .find(s => s.href == "https://water.taiwanstat.com/css/style.css") 405 | .cssRules 406 | ].find(s => s.selectorText == ".reservoir").style; 407 | style.width = "max-content"; 408 | style.marginTop = "20px"; 409 | 410 | let newCss = document.createElement("style"); 411 | newCss.innerHTML = ` 412 | .davCss { 413 | background: rgba(0, 0, 0, 0.05); 414 | } 415 | `; 416 | document.head.appendChild(newCss); 417 | } 418 | } 419 | })(); 420 | -------------------------------------------------------------------------------- /ehx_link_color.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name ehx link color 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/ehx_link_color.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/ehx_link_color.user.js 6 | // @version 0.27 7 | // @description change link color 8 | // @author x94fujo6 9 | // @match https://e-hentai.org/* 10 | // @match https://exhentai.org/* 11 | // @grant none 12 | // ==/UserScript== 13 | 14 | (function () { 15 | 'use strict'; 16 | // (use any valid CSS color you want) 17 | // unvisited link color 18 | let enable_link = true; 19 | let ex = "DeepPink"; 20 | let eh = "DeepPink"; 21 | // visited link color 22 | let enable_visited = true; 23 | let ex_v = "gray"; 24 | let eh_v = "gray"; 25 | // because of the security risk, visited link color many not work in some browser 26 | // see https://dbaron.org/mozilla/visited-privacy 27 | 28 | let domain; 29 | window.onload = setlinkcolor(); 30 | 31 | function setlinkcolor() { 32 | domain = getdomain(); 33 | if (domain) setcss(); 34 | } 35 | 36 | function setcss() { 37 | let link = document.location.href; 38 | let color = (link.indexOf("exhentai") != -1) ? ex : eh; 39 | let color_v = (link.indexOf("exhentai") != -1) ? ex_v : eh_v; 40 | let style = document.createElement("style"); 41 | document.head.appendChild(style); 42 | let csslist = []; 43 | if (enable_link) csslist.push(` 44 | a:link { 45 | color: ${color}; 46 | } 47 | `); 48 | if (enable_visited) csslist.push(` 49 | a:visited .glink, a:active .glink { 50 | color:${color_v} !important; 51 | } 52 | `); 53 | myCss(csslist); 54 | } 55 | 56 | function getdomain() { 57 | let eh = "e-hentai.org"; 58 | let ex = "exhentai.org"; 59 | let link = document.location.href; 60 | if (link.indexOf("exhentai") != -1) { 61 | return ex; 62 | } else if (link.indexOf("e-hentai") != -1) { 63 | return eh; 64 | } 65 | return false; 66 | } 67 | 68 | function myCss(innerlist = []) { 69 | if (innerlist.length > 0) { 70 | let s = document.createElement("style"); 71 | s.id = "mycss"; 72 | document.head.appendChild(s); 73 | let content = ""; 74 | innerlist.forEach(inner => content += inner); 75 | s.innerHTML = content; 76 | } 77 | } 78 | })(); -------------------------------------------------------------------------------- /ehx_torrent_text.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name ehx torrent text 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/ehx_torrent_text.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/ehx_torrent_text.user.js 6 | // @version 0.27 7 | // @description copy text in torrent page 8 | // @author x94fujo6 9 | // @match https://exhentai.org/* 10 | // @match https://e-hentai.org/* 11 | // @grant none 12 | // ==/UserScript== 13 | 14 | (function () { 15 | 'use strict'; 16 | let autoclose = true; 17 | let m = "[ehx torrent text]: "; 18 | window.onload = function () { 19 | window.document.body.onload = main(); 20 | }; 21 | 22 | function main() { 23 | let link = document.location.href; 24 | if (link.indexOf("gallerytorrents.php") === -1) return console.log(`${m}not torrent page, abort ${link}`); 25 | if (link.indexOf("//exhentai") === -1 && link.indexOf("//e-hentai") === -1) return console.log(`${m}incorrect site ${link}`); 26 | 27 | let t = document.querySelectorAll("a[href$='.torrent']"); 28 | if (t) { 29 | t.forEach(e => { 30 | let text = e.textContent; 31 | let tr = creatnewline(text, "Copy Name"); 32 | let pos = e.parentElement.parentElement.parentElement; 33 | pos.appendChild(tr); 34 | 35 | if (text.indexOf("(") === 0) { 36 | let end = text.indexOf(")") + 1; 37 | text = text.substring(0, end) + " "; 38 | tr = creatnewline(text, "Copy Event"); 39 | pos.appendChild(tr); 40 | } 41 | }); 42 | } 43 | } 44 | 45 | function creatnewline(text, copy_text) { 46 | let tr = document.createElement("tr"); 47 | let td = document.createElement("td"); 48 | let s = document.createElement("span"); 49 | s.textContent = text; 50 | td.colSpan = 5; 51 | td.appendChild(s); 52 | tr.appendChild(td); 53 | let e = document.createElement("a"); 54 | e.textContent = copy_text; 55 | e.style = "width: max-content;"; 56 | e.onclick = function () { 57 | navigator.clipboard.writeText(text).then( 58 | function () { 59 | console.log("done"); 60 | if (autoclose) window.close(); 61 | }, function () { 62 | console.log("failed"); 63 | }); 64 | }; 65 | td = document.createElement("td"); 66 | td.rowSpan = 1; 67 | td.style = "width:100px; text-align:center; border-style: outset; height: 1.5rem;"; 68 | td.appendChild(e); 69 | tr.appendChild(td); 70 | return tr; 71 | } 72 | })(); -------------------------------------------------------------------------------- /fc2_show_all_products.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name fc2 show all products 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/fc2_show_all_products.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/fc2_show_all_products.user.js 6 | // @version 0.3 7 | // @description show full list of products in 1 page and sort by id / show full title 8 | // @author x94fujo6 9 | // @match https://adult.contents.fc2.com/* 10 | // @grant none 11 | // ==/UserScript== 12 | /* jshint esversion: 9 */ 13 | 14 | (function () { 15 | 'use strict'; 16 | let msgid = "fc2_script_message"; 17 | let listener = false; 18 | 19 | window.document.body.onload = () => { 20 | startScript(); 21 | }; 22 | 23 | function dPrint(...any) { 24 | console.log(`[${msgid}]: `, ...any); 25 | } 26 | 27 | function startScript() { 28 | if (document.visibilityState == "visible") { 29 | if (listener) { 30 | document.removeEventListener("visibilitychange", startScript); 31 | //dPrint("remove event listener"); 32 | } else { 33 | //dPrint("normal start"); 34 | } 35 | setReload(); 36 | checkLink(); 37 | } else { 38 | document.addEventListener("visibilitychange", startScript); 39 | //dPrint("document not visible, set event listener"); 40 | listener = true; 41 | } 42 | } 43 | 44 | function setReload() { 45 | let menu = document.querySelector("[data-menulist]"); 46 | if (!menu) return; 47 | let links = menu.querySelectorAll("a"); 48 | links.forEach(a => { 49 | if (a.getAttribute("reload")) return; 50 | a.onclick = (event) => { 51 | event.preventDefault(); 52 | document.location.href = a.href; 53 | }; 54 | a.setAttribute("reload", true); 55 | }); 56 | } 57 | 58 | function checkLink() { 59 | let link = document.location.href; 60 | //dPrint(`link: ${link}`); 61 | if (link.match(/users\/[^\/]+\/articles\?sort=date&order=desc/)) { 62 | let regtest = link.match(/page=(\d+)/); 63 | let page = regtest ? regtest[1] : false; 64 | if (regtest) page = (page == 1) ? true : false; 65 | if (!regtest || page) { 66 | dPrint("listview"); 67 | dPrint(`script start, link: ${link}`); 68 | sortList(); 69 | } 70 | } else if (link.match(/article_search.php\?id=\d+/) || link.match(/\/article\/\d+\//)) { 71 | dPrint("productpage"); 72 | productpage(); 73 | } 74 | } 75 | 76 | function updateMessage(text = "") { 77 | document.getElementById(msgid).textContent = text; 78 | dPrint(text); 79 | } 80 | 81 | function sortList() { 82 | let pos = document.querySelector("section.seller_user_articlesList"); 83 | let products; 84 | let list; 85 | 86 | if (!pos) return; 87 | 88 | dPrint("sortList"); 89 | products = [...pos.children]; 90 | list = products.map(div => processDiv(div)); 91 | 92 | document.querySelector(".c-pager-101").remove(); 93 | list.sort((a, b) => b.id - a.id); 94 | 95 | products.forEach(e => e.remove()); 96 | list.forEach(data => { pos.appendChild(data.ele); }); 97 | 98 | let message = Object.assign(document.createElement("span"), { 99 | id: msgid, 100 | textContent: "script start", 101 | style: ` 102 | display: inline-block; 103 | margin: 0.5rem; 104 | color: gold; 105 | `, 106 | }); 107 | document.querySelector("div.seller_user_articles_pageHeader").appendChild(message); 108 | getAllProduct(); 109 | 110 | async function getAllProduct() { 111 | let total = document.querySelector(".seller_user_articles_pageHeaderCount"); 112 | if (!total) return; 113 | total = total.textContent.match(/\((\d*)\)/)[1]; 114 | total = parseInt(total, 10); 115 | 116 | let max_page = Math.ceil(total / 30); 117 | if (max_page == 1) { 118 | updateMessage(`only 1 page, abort`); 119 | } else { 120 | let user = document.location.href.match(/users\/([^\/]*)\/articles/)[1]; 121 | let url = `https://adult.contents.fc2.com/users/${user}/articles?sort=date&order=desc&deal=&page=`; 122 | updateMessage(`max page = ${max_page}, start to retrieve data`); 123 | 124 | for (let page = 2; page <= max_page; page++) { 125 | updateMessage(`processing... please wait [page: ${page}, total: ${max_page}]`); 126 | await getPage(url, page) 127 | .then(async (resolve) => { 128 | await resort(resolve, page); 129 | }) 130 | .catch((reject) => { 131 | updateMessage(`somthing went wrong...script stopped`); 132 | dPrint(reject); 133 | return; 134 | }); 135 | } 136 | } 137 | updateMessage(`done`); 138 | 139 | function getPage(rq_url, page) { 140 | return new Promise((resolve, reject) => { 141 | let rq = new XMLHttpRequest(); 142 | rq.open("GET", `${rq_url}${page}`); 143 | rq.onreadystatechange = function () { 144 | if (rq.readyState == 4) { 145 | return (rq.status == 200) ? setTimeout(resolve, 500, rq.responseText) : reject(rq.responseText); 146 | } 147 | }; 148 | rq.send(null); 149 | }); 150 | } 151 | 152 | function resort(data) { 153 | return new Promise((resolve, reject) => { 154 | let current = getCurrentProduct(); 155 | if (!current) return reject(false); 156 | 157 | data = extractProducts(data); 158 | current.push(...data); 159 | current.sort((a, b) => b.id - a.id); 160 | 161 | let pos = document.querySelector("section.seller_user_articlesList"); 162 | [...pos.children].forEach(e => e.remove()); 163 | 164 | current.forEach(p => pos.appendChild(p.ele)); 165 | 166 | data = null; 167 | current = null; 168 | return resolve(true); 169 | }); 170 | 171 | function getCurrentProduct() { 172 | let pos = document.querySelector("section.seller_user_articlesList"); 173 | if (pos) { 174 | return [...pos.children].map(ele => { 175 | return { 176 | id: ele.querySelector("a").href.match(/id=(\d*)/)[1], 177 | ele: ele.cloneNode(true), 178 | }; 179 | }); 180 | } else { 181 | return false; 182 | } 183 | } 184 | 185 | function extractProducts(domtext) { 186 | let parser = new DOMParser(); 187 | let new_document = parser.parseFromString(domtext, "text/html"); 188 | let pos = new_document.querySelector("section.seller_user_articlesList"); 189 | let products = [...pos.children]; 190 | let list = products.map(div => processDiv(div)); 191 | parser = null; // release memory 192 | new_document = null; 193 | return list; 194 | } 195 | } 196 | } 197 | } 198 | 199 | function processDiv(div) { 200 | let id = div.querySelector("a").href.match(/id=(\d*)/)[1]; 201 | let box = div.querySelector(".c-cntCard-110-f_indetail"); 202 | let title_ele = div.querySelector(".c-cntCard-110-f_itemName"); 203 | let title_span = convertToSpan(title_ele); 204 | let id_span = Object.assign(document.createElement("span"), { 205 | textContent: id, 206 | style: `font-size: 1.5rem;`, 207 | }); 208 | let all_link; 209 | let remove_ele = [ 210 | ".items_article_SmapleVideo", 211 | "span.c-cntCard-110-f_thumb_type", 212 | "button", 213 | "section.c-tooltip-107", 214 | ".detail-layout", 215 | ".c-cntCard-110-f_seller", 216 | ]; 217 | box.insertAdjacentElement("afterbegin", title_span); 218 | box.insertAdjacentElement("afterbegin", id_span); 219 | title_ele.remove(); 220 | 221 | div.querySelector("img").setAttribute("loading", "lazy"); 222 | remove_ele.forEach(css_selector => { 223 | removeFromEle(div, css_selector); 224 | }); 225 | 226 | all_link = div.querySelectorAll("a"); 227 | if (all_link) all_link.forEach(a => { a.target = "_blank"; }); 228 | return { id: id, ele: div.cloneNode(true) }; 229 | 230 | function removeFromEle(ele, css_selector) { 231 | let target = ele.querySelector(css_selector); 232 | if (target) target.remove(); 233 | } 234 | 235 | function convertToSpan(ele) { 236 | return Object.assign(document.createElement("span"), { 237 | textContent: ele.textContent, 238 | className: ele.className, 239 | style: ` 240 | overflow: visible; 241 | display: inline-block; 242 | width: auto; 243 | `, 244 | }); 245 | } 246 | } 247 | 248 | function productpage() { 249 | let data = document.querySelector("[type='application/ld+json']").textContent; 250 | data = JSON.parse(data); 251 | let id = data.productID; 252 | let title = data.name; 253 | let pos = document.querySelector(".items_article_headerInfo h3"); 254 | let e = newButton(`Copy [${id} ${title}]`, `${id} ${title}`); 255 | pos.insertAdjacentElement("afterend", e); 256 | } 257 | 258 | function newButton(text, copy) { 259 | let e = document.createElement("a"); 260 | e.style = "color: deeppink; font-size: 1.5rem;"; 261 | e.textContent = text; 262 | e.onclick = function () { 263 | navigator.clipboard.writeText(repalceForbiddenChar(copy)); 264 | }; 265 | return e; 266 | } 267 | 268 | function repalceForbiddenChar(string = "") { 269 | let forbidden = `<>:"/|?*\\`; 270 | let replacer = `<>:”/|?*\`; 271 | for (let index of forbidden) { 272 | string = string.replaceAll(forbidden[index], replacer[index]); 273 | } 274 | return string.trim(); 275 | } 276 | })(); 277 | -------------------------------------------------------------------------------- /google_drive_autoclick.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name google drive auto click 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/google_drive_autoclick.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/google_drive_autoclick.user.js 6 | // @version 0.2 7 | // @description auto skip & click download 8 | // @author x94fujo6 9 | // @match https://drive.google.com/* 10 | // @match https://docs.google.com/* 11 | // @grant none 12 | // ==/UserScript== 13 | 14 | (function () { 15 | 'use strict'; 16 | document.body.onload = main(); 17 | 18 | function main() { 19 | let link = window.location.href; 20 | let list = [ 21 | "drive", 22 | "docs", 23 | ]; 24 | let index = list.findIndex(key => link.includes(`//${key}.`)); 25 | if (index == -1) { 26 | return; 27 | } 28 | if (link.includes("google.com/file/d/")) { 29 | // https://drive.google.com/file/d/*/view 30 | let id = link.split("/"); 31 | id = id[id.length - 2]; 32 | window.location.href = `https://${list[index]}.google.com/u/0/uc?id=${id}&export=download`; 33 | } else if (link.includes("uc?")) { 34 | if (!link.includes("confirm=")) { 35 | // https://drive.google.com/u/0/uc?id=*&export=download 36 | let id = setInterval(() => clickDL(id), 100); 37 | } else if (link.includes("export=download") && link.includes("confirm=")) { 38 | // https://drive.google.com/u/0/uc?export=download&confirm=*&id=* 39 | let id = setInterval(() => clickDL(id), 100); 40 | } 41 | } 42 | } 43 | 44 | function clickDL(id) { 45 | let button = document.getElementById("uc-download-link"); 46 | if (button) { 47 | button.click(); 48 | clearInterval(id); 49 | } 50 | } 51 | })(); 52 | -------------------------------------------------------------------------------- /mangaoh_title_reformat.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name mangaoh title reformat 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/mangaoh_title_reformat.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/mangaoh_title_reformat.user.js 6 | // @version 0.12 7 | // @description reformat date & title in search result (click button to copy) 8 | // @author x94fujo6 9 | // @match https://www.mangaoh.co.jp/* 10 | // @grant none 11 | // ==/UserScript== 12 | 13 | (function () { 14 | 'use strict'; 15 | window.onload = main; 16 | 17 | function main() { 18 | fixScrollbar(); 19 | if (window.location.href.match(/www\.mangaoh\.co\.jp\/search\/\?q=.+/)) { 20 | let allcard = document.querySelectorAll(".result-card"); 21 | if (allcard) allcard.forEach(card => reformat(card)); 22 | } 23 | } 24 | 25 | // fix broken scrollbar cause by checkout-js error 26 | function fixScrollbar() { 27 | document.body.style = `overflow:scroll !important;`; 28 | } 29 | 30 | function reformat(card) { 31 | let buttonclass = "btn btn-outline-dark", 32 | titleEle = card.querySelector("h2"), 33 | box = document.createElement("div"); 34 | 35 | // get author 36 | let author = card.querySelector("a[href*=作者]"); 37 | if (author) { 38 | author = author.textContent.trim(); 39 | let button_author = document.createElement("button"); 40 | $(button_author).attr({ 41 | class: buttonclass, 42 | onclick: `navigator.clipboard.writeText("${author}")`, 43 | }); 44 | button_author.textContent = author; 45 | box.append(button_author); 46 | } 47 | 48 | // get release date 49 | let date = card.querySelector(".add_filter[href*='発売日']"); 50 | if (date) { 51 | date = date.getAttribute("href"); 52 | if (date) { 53 | date = date.split(":")[1]; 54 | if (date) { 55 | let [y, m, d] = date.split("-"), 56 | button_date = document.createElement("button"); 57 | y = y.slice(2); 58 | date = y + m + d; 59 | 60 | // release date button 61 | $(button_date).attr({ 62 | class: buttonclass, 63 | onclick: `navigator.clipboard.writeText("${date} ")`, 64 | }); 65 | button_date.textContent = date; 66 | box.append(button_date); 67 | } 68 | } 69 | } 70 | 71 | // format title 72 | let title = titleEle.querySelector(".prd_name") 73 | if (title) { 74 | title = title.textContent.trim(); 75 | if (title) { 76 | let button_full = document.createElement("button"), 77 | formatted = `[${author}] ${date} ${title}`, 78 | adult = card.querySelector(".badge-adult"); 79 | formatted = `${adult ? "(成年コミック)" : "(一般コミック)"} ${formatted}`; 80 | $(button_full).attr({ 81 | class: buttonclass, 82 | onclick: `navigator.clipboard.writeText("${formatted}")`, 83 | name: "bookdata", 84 | "book_author": author, 85 | "book_date": date, 86 | "book_title": title, 87 | }); 88 | button_full.textContent = formatted; 89 | box.append(document.createElement("br")); 90 | box.append(button_full); 91 | } 92 | } 93 | 94 | titleEle.insertAdjacentElement("afterend", box); 95 | } 96 | })(); -------------------------------------------------------------------------------- /newgrounds_tool.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name newgrounds tool 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/newgrounds_tool.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/newgrounds_tool.user.js 6 | // @version 0.02 7 | // @description download video/auto select best resolution...etc 8 | // @author x94fujo6 9 | // @match https://www.newgrounds.com/portal/view/* 10 | // ==/UserScript== 11 | /* jshint esversion: 9 */ 12 | 13 | (function () { 14 | const 15 | script_name = "newgrounds tool", 16 | slog = (...any) => console.log(`[${script_name}]`, ...any); 17 | window.onload = () => wait_data(); 18 | 19 | function wait_data(retry = 10) { 20 | let data = $ng_adcode_page; 21 | if (retry <= 0) return slog("max retry, abort"); 22 | if (!data) { 23 | slog(`wait for data..., retry:${retry}`); 24 | retry--; 25 | setTimeout(() => wait_data(retry), 500); 26 | } else { 27 | start_script(); 28 | } 29 | } 30 | 31 | async function start_script() { 32 | let 33 | button = document.querySelector(`[data-action="play"]`), 34 | video = document.querySelector(`.ng-video-player>video>source`); 35 | if (!isPlayer()) return slog("not video"); 36 | if (!button || !video) return slog("no play button or source"); 37 | button.click(); 38 | setTimeout(() => button.click(), 1000); 39 | setTimeout(add_download, 2000); 40 | 41 | async function add_download() { 42 | let res = await sel_best(), 43 | video_data = document.querySelector(`.ng-video-player>video>source`), 44 | pos = document.querySelector(`.body-guts`), 45 | dl_button = document.createElement("a"), 46 | filename = video_data.src.match(/\/(\w+\.\w+\.\w{3,4})\?\d+$/), 47 | format = video_data.src.match(/(.\w{3,4})\?\d+$/), 48 | title = document.querySelector(`#embed_header>[itemprop="name"]`).textContent; 49 | 50 | if (!res || !format) return slog("unknown video format, abort", res, format); 51 | if (!filename) return slog("filename not found", filename); 52 | if (!title) return slog("no title", title); 53 | 54 | filename = filename[1]; 55 | format = format[1]; 56 | dl_button.href = video_data.src; 57 | dl_button.title = dl_button.download = `${title} [${res}]${format}`; 58 | dl_button.innerHTML = `[ Left Click ] to Copy Title: ${dl_button.title}
[ Right Click > Save as ] to Download`; 59 | dl_button.style = ` 60 | display: block; 61 | width: 100%; 62 | text-align: center; 63 | padding: 1rem; 64 | font-size: large; 65 | border: 0.1rem white solid; 66 | `; 67 | dl_button.referrerPolicy = "no-referrer"; 68 | dl_button.rel = "noreferrer noopener nofollow"; 69 | dl_button.target = "_blank"; 70 | dl_button.onclick = (event) => { 71 | event.preventDefault(); 72 | navigator.clipboard.writeText(dl_button.title); 73 | }; 74 | pos.insertAdjacentElement("afterbegin", dl_button); 75 | 76 | slog(`add dl ${dl_button.href}`); 77 | 78 | async function sel_best() { 79 | let q = document.querySelector(`.ng-video-options[data-options="res"]`); 80 | if (!q) return false; 81 | if (!q.children.length) return false; 82 | q.children[0].click(); 83 | return q.children[0].textContent; 84 | } 85 | } 86 | 87 | function isPlayer() { 88 | if ($ng_adcode_page !== "movie-view") return false; 89 | return document.querySelector(".ng-video-player") ? true : false; 90 | } 91 | } 92 | })(); -------------------------------------------------------------------------------- /nexusmods_skip_countdown.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name nexusmods skip countdown 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/nexusmods_skip_countdown.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/nexusmods_skip_countdown.user.js 6 | // @version 0.1 7 | // @description no countdown & auto start download 8 | // @author x94fujo6 9 | // @match https://www.nexusmods.com/* 10 | // @grant none 11 | // ==/UserScript== 12 | 13 | (function () { 14 | 'use strict'; 15 | 16 | window.onload = checkURL(); 17 | 18 | function checkURL() { 19 | // https://www.nexusmods.com/*/mods/*tab=files&file_id=* 20 | if (window.location.href.match(/www\.nexusmods\.com\/.*\/mods\/\d+\?tab=files&file_id=\d+/)) direct_download(); 21 | } 22 | 23 | function direct_download() { 24 | let game_id = window.current_game_id; 25 | let file_id = window.location.href.match(/file_id=(\d+)/); 26 | if (!game_id) return console.log("game_id not found"); 27 | if (!file_id) return console.log("file_id not found"); 28 | file_id = file_id[1]; 29 | 30 | $('.subheader, .table').hide(); 31 | $('.donation-wrapper').show(); 32 | 33 | $.ajax({ 34 | type: "POST", 35 | url: "/Core/Libs/Common/Managers/Downloads?GenerateDownloadUrl", 36 | data: { 37 | fid: file_id, 38 | game_id: game_id, 39 | }, 40 | success: function (data) { 41 | if (data && data.url) { 42 | console.log('Success'); 43 | window.location.href = data.url; 44 | $('.donation-wrapper > p').html(`

Your download has started

If you are having trouble, click here to download manually

`); 45 | } else { 46 | setError(); 47 | } 48 | }, 49 | error: function () { 50 | setError(); 51 | } 52 | }); 53 | } 54 | 55 | function setError() { 56 | console.log('An error occurred'); 57 | $('.donation-wrapper > p').html('

Unfortunately an error occurred while downloading this file

Please try again later or contact support

'); 58 | } 59 | })(); 60 | -------------------------------------------------------------------------------- /open_discord_link_in_app.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name open discord link in app 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/open_discord_app.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/open_discord_app.user.js 6 | // @version 0.02 7 | // @description use discord app to open discord link instead of open in browser 8 | // @author x94fujo6 9 | // @match *://*/* 10 | // ==/UserScript== 11 | /* jshint esversion: 9 */ 12 | 13 | (async function () { 14 | const script_name = "open discord link in app"; 15 | let count = 0, id; 16 | 17 | await wait_tab(); 18 | 19 | id = setInterval(() => { 20 | let result = main(); 21 | if (!result) { 22 | count++; 23 | } 24 | if (count >= 10) { 25 | clearInterval(id); 26 | } 27 | }, 1000); 28 | 29 | function main() { 30 | let reg = /https\:\/\/(discordapp\.com\/channels.*|discord\.com\/channels.*)/, 31 | links = document.querySelectorAll("a"), 32 | discord_links; 33 | if (links.length > 0) { 34 | discord_links = [...links].filter(a => a.href.match(reg)); 35 | if (discord_links.length > 0) { 36 | [...discord_links].forEach(a => a.href = a.href.replace(reg, "discord://$1")); 37 | return true; 38 | } 39 | } 40 | return false; 41 | } 42 | 43 | function wait_tab() { 44 | return new Promise(resolve => { 45 | if (document.visibilityState === "visible") return resolve(); 46 | document.addEventListener("visibilitychange", () => { 47 | if (document.visibilityState === "visible") { 48 | return resolve(); 49 | } 50 | }); 51 | }); 52 | } 53 | 54 | })(); 55 | -------------------------------------------------------------------------------- /oshiro_wiki.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name oshiro wiki QOL 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/oshiro_wiki.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/oshiro_wiki.user.js 6 | // @version 0.1 7 | // @description wiki QOL 8 | // @author x94fujo6 9 | // @match https://scre.swiki.jp/* 10 | // @icon https://www.google.com/s2/favicons?sz=64&domain=swiki.jp 11 | // @grant none 12 | // ==/UserScript== 13 | 14 | /* 15 | 16 | auto search & add event answer 17 | https://i.imgur.com/Uqrx1GL.png 18 | 19 | */ 20 | 21 | (function () { 22 | let data = isChar(); 23 | 24 | if (data) { 25 | charHandler(data); 26 | } 27 | 28 | async function charHandler({ name, link }) { 29 | let giftInfo = await getGiftInfo(link); 30 | let pos = document.querySelector("#evaluation"); 31 | //console.log(giftInfo); 32 | 33 | if (pos) { 34 | addGiftInfo(giftInfo, pos); 35 | } 36 | 37 | function addGiftInfo(info, pos) { 38 | let ele = document.createElement("div"); 39 | if (info) { 40 | let eleHTML = 41 | `
42 | 43 | 44 | ${info} 45 | 46 |
47 |
`; 48 | let list = [3, 4, 5, 6, 7, 8, 9]; 49 | ele.innerHTML = eleHTML; 50 | [...ele.querySelector("tr").childNodes] 51 | .filter((ele, index) => !list.includes(index)) 52 | .forEach(ele => ele.remove()); 53 | } else { 54 | ele.textContent = "not found"; 55 | } 56 | pos.insertAdjacentElement("beforebegin", ele); 57 | 58 | pos = ele; 59 | ele = document.createElement("h3"); 60 | ele.textContent = `${name} 好みとイベント解答`; 61 | pos.insertAdjacentElement("beforebegin", ele); 62 | } 63 | } 64 | 65 | async function getGiftInfo(link = "") { 66 | let info_page = await fetch("https://scre.swiki.jp/index.php?%E8%B4%88%E3%82%8A%E7%89%A9"); 67 | let parser = new DOMParser(); 68 | let _doc = parser.parseFromString(await info_page.text(), "text/html"); 69 | let info = _doc.querySelector(`.style_table a[href="${link}"]`); 70 | if (info) { 71 | info = info.parentNode.parentNode.outerHTML; 72 | } 73 | parser = null; 74 | _doc = null; 75 | return info; 76 | } 77 | 78 | function isChar() { 79 | let list = [ 80 | "evaluation", 81 | "voice", 82 | "image", 83 | "information", 84 | ]; 85 | list = list.map(id => Boolean(document.getElementById(id))); 86 | if (list.every(check => check)) { 87 | let name = document.querySelector("#page_title a").textContent.trim(); 88 | let link = window.location.href; 89 | console.log("this is a char page", list, `name: ${name}, link: ${link}`); 90 | return { name, link }; 91 | } else { 92 | console.log("this is not a char page", list); 93 | return false; 94 | } 95 | } 96 | 97 | })(); 98 | -------------------------------------------------------------------------------- /ph_user_video.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name ph_user_video 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/ph_user_video.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/ph_user_video.user.js 6 | // @version 0.11 7 | // @description redirect link to user video list / muti select & copy video links 8 | // @author x94fujo6 9 | // @match https://*.pornhub.com/* 10 | // ==/UserScript== 11 | 12 | 13 | (function () { 14 | 'use strict'; 15 | let itemIndex = 0; 16 | let trycount = 0; 17 | let listener = false; 18 | 19 | window.onload = startScript(); 20 | 21 | function startScript() { 22 | if (document.visibilityState == "visible") { 23 | if (listener) document.removeEventListener("visibilitychange", startScript); 24 | main(); 25 | } else { 26 | document.addEventListener("visibilitychange", startScript); 27 | listener = true; 28 | } 29 | } 30 | 31 | function main() { 32 | myCss(); 33 | let link = document.location.href, 34 | target_list = ["/videos", "/playlist", "video/search",]; 35 | 36 | //enable this if you want auto switch to EN 37 | //switchLan(); 38 | 39 | if (link.includes("viewkey")) { 40 | setLink(); 41 | } else if (target_list.some(t => link.includes(t))) { 42 | reDirect(link); 43 | } else { 44 | replaceLink(".usernameWrap"); 45 | } 46 | 47 | function switchLan() { 48 | let host = document.location.host, 49 | target_host = "www.pornhub.com"; 50 | if (host != target_host) { 51 | setTimeout(() => { 52 | document.querySelector(`li[data-lang="en"] a`).click(); 53 | }, 1000); 54 | } 55 | } 56 | 57 | function setLink() { 58 | let info = document.querySelector(".video-detailed-info"); 59 | info = info.querySelector(".usernameBadgesWrapper"); 60 | if (!info) return print("no user info"); 61 | info = info.querySelector("a"); 62 | let username = info.textContent; 63 | print("username", username); 64 | 65 | let link = `${info.href}/videos/public`; 66 | info.setAttribute("href", link); 67 | 68 | replaceLink(".usernameWrap"); 69 | markSameUser(username); 70 | 71 | let button = newButton("myButtonB", "Copy Video link", copyLink); 72 | let div = document.createElement("div"); 73 | div.appendChild(button); 74 | 75 | let pos = document.querySelector("#player"); 76 | pos.insertAdjacentElement("afterend", div); 77 | 78 | function copyLink() { 79 | let link = window.location.href; 80 | navigator.clipboard.writeText(link); 81 | } 82 | } 83 | 84 | function markSameUser(username = "") { 85 | mark("#relatedVideosCenter"); 86 | mark("#recommendedVideosVPage"); 87 | 88 | function mark(css_selector) { 89 | let target = document.querySelector(css_selector); 90 | if (target) { 91 | target = target.querySelectorAll("li"); 92 | if (target) { 93 | target.forEach(e => { 94 | let uploader = e.querySelector(".usernameWrap a"); 95 | if (uploader) { 96 | if (uploader.textContent == username) { 97 | e.style = "border: red 0.2rem solid;"; 98 | } 99 | } 100 | }); 101 | } 102 | } 103 | } 104 | } 105 | 106 | function replaceLink(css_selector) { 107 | let target = document.querySelectorAll(css_selector); 108 | if (target) { 109 | target.forEach(e => { 110 | let a = e.querySelector("a"); 111 | if (a) if (!a.href.includes("/videos")) a.href = `${a.href}/videos/public`; 112 | }); 113 | } else { 114 | print(`target ${css_selector} not found`); 115 | } 116 | } 117 | 118 | function reDirect(link) { 119 | let vids = document.querySelector(".profileVids"); // profileVids videoUList 120 | if (!vids) { 121 | if (link.includes("/public")) { 122 | console.log("public"); 123 | setTimeout(() => document.location.href = link.replace("/public", ""), 1000); 124 | } 125 | if (link.includes("/playlist")) { 126 | console.log("playlist"); 127 | vids = document.querySelector(".container.playlistSectionWrapper"); 128 | if (vids) mutiSelect(vids); 129 | } 130 | if (link.includes("/search")) { 131 | console.log("search"); 132 | vids = document.querySelector("#videoSearchResult").parentElement; 133 | if (vids) mutiSelect(vids); 134 | } 135 | console.log("can't found video list, abort"); 136 | return; 137 | } else { 138 | mutiSelect(vids); 139 | } 140 | 141 | function mutiSelect(vid_list) { 142 | let 143 | select_box = document.createElement("div"), 144 | textbox = document.createElement("textarea"), 145 | textbox_style = ` 146 | width: 100%; 147 | height: auto; 148 | display: block; 149 | margin: auto; 150 | `; 151 | 152 | select_box.style = ` 153 | display: flex; 154 | text-align: center; 155 | flex-wrap: wrap; 156 | width: 50%; 157 | margin: auto; 158 | `; 159 | select_box.innerHTML = ``; 160 | vid_list.insertAdjacentElement("afterbegin", select_box); 161 | 162 | let button, 163 | button_class = "myButton line-4-item", 164 | button_box = document.createElement("div"); 165 | button_box.style = `width: 100%;`; 166 | [ 167 | { text: "Select All", click: secectAll }, 168 | { text: "Unselect All", click: unsecectAll }, 169 | { text: "Invert Select", click: invertSecect }, 170 | { text: "Copy", click: copyAll }, 171 | ].forEach(o => { 172 | button = newButton(button_class, o.text, o.click); 173 | button_box.appendChild(button); 174 | }); 175 | select_box.appendChild(button_box); 176 | 177 | vid_list = vid_list.querySelectorAll(".pcVideoListItem"); 178 | addCheckbox(vid_list); 179 | 180 | let more = document.getElementById("moreDataBtn"); 181 | if (more) { 182 | more.addEventListener("click", loadEvent); 183 | let ele = document.createElement("span"); 184 | ele.textContent = "Load button found, this user video list is ajax. Recommend using other tools to extract the list."; 185 | textbox.insertAdjacentElement("beforebegin", ele); 186 | } 187 | 188 | function addCheckbox(nodelist) { 189 | let e, key, vlink; 190 | console.log(`itemIndex:${itemIndex}, nodecount:${nodelist.length}`); 191 | for (itemIndex; itemIndex < nodelist.length; itemIndex++) { 192 | e = nodelist[itemIndex]; 193 | key = e.attributes["data-video-vkey"].value; 194 | vlink = `https://www.pornhub.com/view_video.php?viewkey=${key}`; 195 | 196 | let ck = Object.assign(document.createElement("input"), { 197 | type: "checkbox", 198 | name: "selected_vid_list", 199 | value: vlink, 200 | }); 201 | ck.addEventListener("click", updateTextBox); 202 | 203 | let label = Object.assign(document.createElement("label"), { className: "myLable", }); 204 | label.appendChild(ck); 205 | 206 | let div = Object.assign(document.createElement("div"), { textContent: "Add to List", }); 207 | label.appendChild(div); 208 | 209 | div = document.createElement("div"); 210 | div.appendChild(label); 211 | 212 | let pos = e.querySelector(".thumbnail-info-wrapper"); 213 | pos.insertAdjacentElement("beforebegin", document.createElement("br")); 214 | pos.insertAdjacentElement("beforebegin", div); 215 | 216 | pos = e.querySelector("span.title").querySelector("a"); 217 | pos.removeAttribute("href"); 218 | } 219 | itemIndex = nodelist.length; 220 | } 221 | 222 | function loadEvent() { 223 | let vids = document.querySelector(".profileVids").querySelectorAll(".pcVideoListItem"); 224 | if (vids.length === itemIndex) { 225 | if (trycount < 100) { 226 | console.log(`no new item found, waiting for page to load [retry:${trycount}]`); 227 | setTimeout(loadEvent, 100); 228 | } else { 229 | console.log("retry too much, script stop"); 230 | } 231 | trycount++; 232 | } else { 233 | trycount = 0; 234 | addCheckbox(vids); 235 | } 236 | } 237 | 238 | function copyAll() { 239 | let textbox = document.getElementById("selected_vid_list"); 240 | navigator.clipboard.writeText(textbox.value); 241 | } 242 | 243 | function invertSecect() { 244 | let inputs = document.querySelectorAll("input[name='selected_vid_list']"); 245 | inputs.forEach(e => { 246 | if (e.checked) { 247 | e.checked = false; 248 | } else { 249 | e.checked = true; 250 | } 251 | }); 252 | updateTextBox(); 253 | } 254 | 255 | function unsecectAll() { 256 | let inputs = document.querySelectorAll("input[name='selected_vid_list']:checked"); 257 | inputs.forEach(e => { 258 | e.checked = false; 259 | }); 260 | updateTextBox(); 261 | } 262 | 263 | function secectAll() { 264 | let inputs = document.querySelectorAll("input[name='selected_vid_list']"); 265 | inputs.forEach(e => { 266 | if (!e.checked) e.checked = true; 267 | }); 268 | updateTextBox(); 269 | } 270 | 271 | function updateTextBox() { 272 | let inputs = document.querySelectorAll("input[name='selected_vid_list']:checked"); 273 | let newtext = ""; 274 | if (inputs) { 275 | inputs.forEach(ck => { 276 | if (!newtext.includes(ck.value)) newtext += `${ck.value}\n`; 277 | }); 278 | } 279 | let box = document.getElementById("selected_vid_list"); 280 | box.value = newtext; 281 | } 282 | } 283 | } 284 | } 285 | 286 | function print(...any) { 287 | console.log(...any); 288 | } 289 | 290 | function newButton(bclass, btext, handeler) { 291 | let button = document.createElement("button"); 292 | Object.assign(button, { 293 | className: bclass, 294 | textContent: btext, 295 | onclick: handeler, 296 | }); 297 | return button; 298 | } 299 | 300 | function myCss() { 301 | let s = document.createElement("style"); 302 | s.className = "myCssSheet"; 303 | document.head.appendChild(s); 304 | s.textContent = ` 305 | .added { 306 | display:initial !important; 307 | } 308 | 309 | .myButtonB { 310 | position: relative; 311 | padding: 1rem; 312 | width: 100%; 313 | border-style: solid; 314 | font-size: 1rem; 315 | background: transparent; 316 | color: white; 317 | } 318 | 319 | .myButton { 320 | position: relative; 321 | padding: 0.5rem 0; 322 | margin: 0.5rem; 323 | width: max-content; 324 | font-size: 1rem; 325 | } 326 | 327 | .line-4-item { 328 | max-width: 100%; 329 | width: calc(80% / 4); 330 | } 331 | 332 | .myButton:active, .myButtonB:active { 333 | background-color: DeepPink; 334 | } 335 | 336 | .myLable { 337 | position: relative; 338 | width: auto; 339 | padding: 0.5rem; 340 | border-style: solid; 341 | border-width: 0.1rem; 342 | border-color: gray; 343 | display: flex; 344 | } 345 | 346 | .myLable>input { 347 | position: relative; 348 | margin: auto; 349 | margin-left: 0rem; 350 | margin-right: 0.2rem; 351 | } 352 | 353 | .myLable>div { 354 | position: relative; 355 | margin: 0.1rem; 356 | } 357 | `; 358 | } 359 | })(); 360 | -------------------------------------------------------------------------------- /pixiv_blacklist.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name pixiv blacklist 3 | // @version 0.03 4 | // @description hide unwant artist in search 5 | // @author x94fujo6 6 | // @match https://www.pixiv.net/* 7 | // @grant GM_getValue 8 | // @grant GM_setValue 9 | 10 | // ==/UserScript== 11 | /* jshint esversion: 9 */ 12 | 13 | (async function () { 14 | let current = window.location.href, 15 | retry_count = 0, 16 | delay = 1000; 17 | 18 | console.log("script start"); 19 | 20 | window.onload = setTimeout(checkUrl, delay); 21 | 22 | setInterval(() => { 23 | let url = window.location.href; 24 | if (url != current) { 25 | console.log(`re-run script @${url}`); 26 | window.onload = setTimeout(checkUrl, delay); 27 | current = url; 28 | } 29 | }, 1000); 30 | 31 | function checkUrl() { 32 | let url = window.location.href; 33 | if (url.includes("/tags/")) main("tag"); 34 | if (url.includes("/users/")) main("user"); 35 | } 36 | 37 | async function main(mode = "") { 38 | let 39 | g = { 40 | key: { 41 | blacklist: "blacklist", 42 | }, 43 | default: { 44 | blacklist: [], 45 | }, 46 | get(key) { 47 | this.is_key(key); 48 | return GM_getValue(key, this.default[key]); 49 | }, 50 | set(key, value) { 51 | this.is_key(key); 52 | return GM_setValue(key, value); 53 | }, 54 | is_key(key) { 55 | if (!this.default[key]) throw Error("unknown key"); 56 | }, 57 | }, 58 | checkBlacklist = (ele) => { 59 | let user_id = ele.querySelector("a[href*='/users/']"); 60 | if (user_id) { 61 | user_id = Number(user_id.href.match(/users\/(\d+)/)[1]); 62 | } else { 63 | return false; 64 | } 65 | if (blacklist.has(user_id)) { 66 | let nodes = [...ele.childNodes[0].childNodes]; 67 | nodes.pop(); 68 | nodes.forEach(node => { 69 | node.style.opacity = 0; 70 | node.style.pointerEvents = "none"; 71 | }); 72 | return true; 73 | } 74 | return false; 75 | }, 76 | result = false, 77 | blacklist = new Set(g.get(g.key.blacklist)); 78 | 79 | console.log("script main"); 80 | result = document.querySelectorAll(".juyBTC"); 81 | console.log("result", result); 82 | if (mode == "user") { 83 | console.log("user"); 84 | let b = null, 85 | ids = { 86 | remove: "_blacklist_remove", 87 | add: "_blacklist_add", 88 | }, 89 | add = document.getElementById(ids.add), 90 | remove = document.getElementById(ids.remove); 91 | if (add) { 92 | add.remove(); 93 | add = null; 94 | } 95 | if (remove) { 96 | remove.remove(); 97 | remove = null; 98 | } 99 | b = document.querySelector("button[data-gtm-user-id]"); 100 | if (b) { 101 | let user_id = Number(b.getAttribute("data-gtm-user-id")); 102 | if (blacklist.has(user_id)) { 103 | remove = b.cloneNode(); 104 | remove.id = ids.remove; 105 | remove.textContent = "blacklist--"; 106 | remove.type = "button"; 107 | remove.style = ` 108 | color: white; 109 | background-color: limegreen; 110 | `; 111 | remove.onclick = () => { 112 | blacklist.delete(user_id); 113 | g.set(g.key.blacklist, [...blacklist]); 114 | console.log(`user[${user_id}] is remove from blacklist`, [...blacklist]); 115 | document.location.reload(); 116 | }; 117 | b.insertAdjacentElement("afterend", remove); 118 | } else { 119 | add = b.cloneNode(); 120 | add.id = ids.add; 121 | add.textContent = "blacklist++"; 122 | add.type = "button"; 123 | add.style = ` 124 | color: white; 125 | background-color: black; 126 | `; 127 | add.onclick = () => { 128 | blacklist.add(user_id); 129 | g.set(g.key.blacklist, [...blacklist]); 130 | console.log(`user[${user_id}] is add to blacklist`, [...blacklist]); 131 | document.location.reload(); 132 | }; 133 | b.insertAdjacentElement("afterend", add); 134 | } 135 | b.addEventListener("click", () => { 136 | window.location.reload(); 137 | }); 138 | } else { 139 | console.log("user button not found"); 140 | return false; 141 | } 142 | console.log("script end [user]"); 143 | return true; 144 | } 145 | 146 | if (mode == "tag") { 147 | let count = 0; 148 | console.log("search"); 149 | 150 | result.forEach(r => { 151 | let all = r.querySelectorAll("li"); 152 | all.forEach(ele => { 153 | if (checkBlacklist(ele)) count++; 154 | }); 155 | }); 156 | 157 | console.log(`script hide ${count} artworks`); 158 | console.log("script end [search]"); 159 | if (count == 0) { 160 | console.log(`count = 0, retry(${retry_count})`); 161 | if (retry_count < 3) { 162 | retry_count++; 163 | setTimeout(() => { 164 | main("tag"); 165 | }, delay); 166 | } else { 167 | retry_count = 0; 168 | console.log(`end retry`); 169 | } 170 | } 171 | return count; 172 | } 173 | console.log("script end"); 174 | return false; 175 | } 176 | })(); -------------------------------------------------------------------------------- /pixiv_reverse_proxy.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name pixiv reverse proxy 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @version 0.0.2 5 | // @description Reverse proxy all images. If you are experiencing 56k speeds on pixiv, try this. (I can't even load my own post. WTF) 6 | // @author x94fujo6 7 | // @match https://www.pixiv.net/* 8 | // @match https://i.pximg.net/* 9 | // @grant none 10 | // @run-at document-start 11 | // ==/UserScript== 12 | /*jshint esversion: 6 */ 13 | 14 | (function () { 15 | 16 | setInterval(() => { 17 | main(); 18 | }, 1000); 19 | 20 | function main() { 21 | let url = window.location.href; 22 | if (url.includes("//i.pximg.net")) { 23 | window.location.href = url.replace("//i.pximg.net", "//i.pixiv.cat"); 24 | } else { 25 | replaceLink(); 26 | replaceImg(); 27 | } 28 | } 29 | 30 | function replaceLink() { 31 | let images = document.querySelectorAll("a[href*='i.pximg.net']"); 32 | if (!images) return; 33 | images.forEach(a => { 34 | if (!(a.getAttribute("reverse_proxy"))) { 35 | let link = getProxy(a.href); 36 | if (link) { 37 | a.href = link; 38 | a.setAttribute("reverse_proxy", true); 39 | console.log(`reverse proxy [link]: ${link}`); 40 | } 41 | } 42 | }); 43 | } 44 | 45 | function replaceImg() { 46 | let targets = document.querySelectorAll("img[src*='i.pximg.net']"); 47 | if (!targets) return; 48 | targets.forEach(img => { 49 | if (!(img.getAttribute("reverse_proxy"))) { 50 | let link = getProxy(img.src); 51 | if (link) { 52 | img.src = link; 53 | img.setAttribute("reverse_proxy", true); 54 | console.log(`reverse proxy [img]: ${link}`); 55 | } 56 | } 57 | }); 58 | } 59 | 60 | function getProxy(link) { 61 | let reg = /i\.pximg\.net\/(.*)/, 62 | proxy = "https://i.pixiv.cat/", 63 | extract; 64 | if (!link) throw Error("empty link"); 65 | extract = reg.exec(link); 66 | return extract ? `${proxy}${extract[1]}` : false; 67 | } 68 | })(); 69 | -------------------------------------------------------------------------------- /prts_redirector.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name prts redirector 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/prts_redirector.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/prts_redirector.user.js 6 | // @version 0.2 7 | // @description auto redirect to desktop version 8 | // @author x94fujo6 9 | // @match http://prts.wiki/* 10 | // @grant none 11 | // ==/UserScript== 12 | 13 | (function () { 14 | 'use strict'; 15 | let link = window.location.href; 16 | let reg; 17 | if (link.includes("toggle_view_desktop")) return replaceAllLink(); 18 | reg = link.includes("toggle_view_mobile") ? /title=(.*)&/ : /\/w\/(.[^\&\#]*)/; 19 | redirect(); 20 | 21 | function redirect() { 22 | if (!reg) return; 23 | let extract = reg.exec(link); 24 | if (extract) window.location.href = `http://prts.wiki/index.php?title=${extract[1]}&mobileaction=toggle_view_desktop`; 25 | } 26 | 27 | function replaceAllLink() { 28 | let all = document.querySelectorAll("a"); 29 | all.forEach(a => { 30 | let alink = a.href; 31 | if (!alink.includes("prts.wiki")) return; 32 | reg = alink.includes("title=") ? /title=(.*)&/ : /\/w\/(.[^\&\#]*)/; 33 | let extract = reg.exec(alink); 34 | if (extract) a.href = `http://prts.wiki/index.php?title=${extract[1]}&mobileaction=toggle_view_desktop`; 35 | }); 36 | } 37 | })(); -------------------------------------------------------------------------------- /ptt_chrome_blacklist_autosave.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name ptt chrome autosave blacklist 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/ptt_chrome_blacklist_autosave.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/ptt_chrome_blacklist_autosave.user.js 6 | // @version 0.02 7 | // @description autosave blacklist (use Tampermonkey API GM_setValue & GM_getValue) 8 | // @author x94fujo6 9 | // @match https://iamchucky.github.io/PttChrome/* 10 | // @grant GM_getValue 11 | // @grant GM_setValue 12 | // @run-at document-start 13 | // ==/UserScript== 14 | /* jshint esversion: 9 */ 15 | 16 | (async function () { 17 | let 18 | blacklist = false, 19 | addUser = false, 20 | removeUser = false; 21 | const 22 | listKey = "blackList", 23 | log = (...any) => console.log(`%c[autosave blacklist]%c`, "color:OrangeRed;", "", ...any), 24 | setGMValue = (key = listKey, value = "") => GM_setValue(key, value), 25 | getGMValue = (key = listKey, default_value = false) => GM_getValue(key, default_value), 26 | getListInApp = () => { 27 | if (!pttchrome.app) return false; 28 | let list = Object.keys(pttchrome.app.pref.blacklistedUserIds); 29 | list = list.join("\n"); 30 | return list; 31 | }, 32 | autoSave = () => { 33 | let data = getListInApp(); 34 | if (!data.length) return; 35 | setGMValue(listKey, data); 36 | log(`autosave blacklist [${data.split("\n").length}]`); 37 | }, 38 | loadList = () => { 39 | let data = getGMValue(), 40 | list = pttchrome.app.pref.blacklistedUserIds, 41 | count = 0; 42 | if (!data.length) return; 43 | blacklist.value = data; 44 | data = data.split("\n"); 45 | log(`load blacklist [${data.length}]`); 46 | data.forEach(id => { 47 | if (list[id]) return; 48 | list[id] = true; 49 | count++; 50 | }); 51 | log(`added missing id [${count}]`); 52 | }, 53 | start = () => { 54 | log(`script start`); 55 | blacklist.addEventListener("input", autoSave); 56 | addUser.addEventListener("click", autoSave); 57 | removeUser.addEventListener("click", autoSave); 58 | loadList(); 59 | }, 60 | wait = (retry = 30) => { 61 | blacklist = document.querySelector("#opt_blacklistedUsers"); 62 | addUser = document.querySelector("#cmenu_addBlacklistUserId"); 63 | removeUser = document.querySelector("#cmenu_removeBlacklistUserId"); 64 | if ((!blacklist || !addUser || !removeUser) && retry > 0) { 65 | retry--; 66 | log(`target not found, remaining retries [${retry}]`); 67 | setTimeout(() => wait(retry), 1000); 68 | } else { 69 | setTimeout(start, 1000); 70 | } 71 | }; 72 | wait(); 73 | })(); 74 | -------------------------------------------------------------------------------- /ptt_web_image_fix.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name PTT Web Image Fix 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/ptt_web_image_fix.user.js 5 | // @version 0.11 6 | // @description 修復PTT網頁板自動開圖、嘗試修復被截斷的網址、阻擋黑名單ID的推文/圖片 7 | // @author x94fujo6 8 | // @include https://www.ptt.cc/* 9 | // @grant GM_xmlhttpRequest 10 | // @grant GM_getValue 11 | // @grant GM_setValue 12 | // @grant GM_registerMenuCommand 13 | // @require https://openuserjs.org/src/libs/sizzle/GM_config.js 14 | // @run-at document-start 15 | // ==/UserScript== 16 | 17 | /* 18 | 0.11 19 | 新增阻擋關鍵字功能 20 | 自行修改key_word內容 (regex) 21 | F12控制台會顯示整批被阻擋之ID跟推文內容 22 | ID阻擋>圖片阻擋>關鍵字阻擋 23 | */ 24 | 25 | (function () { 26 | let 27 | blacklist_id = ["s910408", "ig49999", "bowen5566", "sos976431"], 28 | blacklist_img = ["Dey10PF", "WfOR5a8", "wsG5vrZ", "Q7hvcZw", "7h9s0iC", "g28oNwO", "y9arWAn", "9QnqRM3", "UeImoq1", "snzmE7h", "cJXK0nM", "jWy4BKY", "feMElhb", "CpGkeGb", "txz4iGW", "W2i4y4k", "aVXa6GN", "Mni1ayO"], 29 | blocked_id = new Set([]), 30 | blocked_img = new Set([]); 31 | let key_word = `五樓|覺青|莫斯科|演員|司機|小丑|嘻嘻`; 32 | key_word = new RegExp(key_word); 33 | const 34 | script_name = "fix ptt img", fixed = "fix_by_script", 35 | rd_text = (text = "") => { 36 | if (text.length <= 11) return " ██REDACTED██"; 37 | let rp = (len) => "█".repeat(len), 38 | side = (text.length - 11) / 2; 39 | side = (side > 1) ? rp(side) : "█"; 40 | return ` ${side}REDACTED${side}`; 41 | }, 42 | remove_blacklist_target = async () => { 43 | let user, text, ele, 44 | ck_id, ck_img, 45 | push_content = document.querySelectorAll("div.push"), 46 | reg = /(?<=\/)(\w+)(?:\.\w{3,4})*$/; 47 | let ck_kw, kw_list = []; 48 | push_content.forEach(div => { 49 | user = div.querySelector(".push-userid").textContent.trim(); 50 | ele = div.querySelector(".push-content"); 51 | text = ele.textContent; 52 | ck_id = blacklist_id.find(id => id == user); 53 | ck_img = blacklist_img.find(img => text.includes(`/${img}`)); 54 | //ck_kw = key_word.find(key => text.toLowerCase().match(`${key}`)); 55 | ck_kw = text.toLowerCase().match(key_word); 56 | 57 | if (ck_id || ck_img || ck_kw) { 58 | ele.title = text; 59 | ele.innerHTML = rd_text(text); 60 | ele.style = "color: darkred;"; 61 | slog_c(`%cblock by id blacklist %c${user}:%c${ele.title.replace(":", "").trim()}`, "#FF0000;#FFFF00;"); //.replace(/:[\s]*(https|https)*(:\/\/)*/, "") 62 | if (ck_kw && !ck_id && !ck_img) { 63 | kw_list.push( 64 | { 65 | user, 66 | text 67 | } 68 | ); 69 | } 70 | if (ck_id && !ck_img) { 71 | text = text.match(reg); 72 | if (text) { 73 | slog_c(`%cblock by id blacklist %c${user}:%c${ele.title.replace(":", "").trim()}%c img [%c${text[1]}%c] not in list`, "#40E0D0;#FFFF00;;#40E0D0;;#40E0D0"); 74 | blocked_img.add(text[1]); 75 | } 76 | } 77 | if (!ck_id && ck_img) { 78 | slog_c(`%cblock by img blacklist %c${user}:%c${ele.title.replace(":", "").trim()}%c user [%c${user}%c] not in list`, "#FFA500;#FFFF00;;#FFA500;;#FFA500"); 79 | blocked_id.add(user); 80 | } 81 | } 82 | }); 83 | if (kw_list.length > 0) { 84 | let _list = kw_list.map(data => data.user); 85 | _list = new Set(_list); 86 | _list = [..._list]; 87 | slog(`block by key word`); 88 | slog(`\n` + _list.join("\n")); 89 | _list = {}; 90 | kw_list.forEach(data => { 91 | if (!_list[data.user]) { 92 | _list[data.user] = []; 93 | } 94 | _list[data.user].push(data.text); 95 | }); 96 | Object.keys(_list).forEach(id => { 97 | _list[id].forEach(t => { 98 | console.log(`${id}${t}`); 99 | }); 100 | }); 101 | } 102 | return true; 103 | }, 104 | slog = (...any) => console.log(`[${script_name}]`, ...any), 105 | slog_c = (s = "", c = "") => console.log(`[${script_name}] ${s}`, ...c.split(";").map(_c => `color:${_c};`)), 106 | wait_tab = () => { 107 | return new Promise(resolve => { 108 | if (document.visibilityState === "visible") return resolve(); 109 | slog("tab in background, script paused"); 110 | document.addEventListener("visibilitychange", () => { 111 | if (document.visibilityState === "visible") { slog("script unpaused"); return resolve(); } 112 | }); 113 | }); 114 | }, 115 | remove_richcontent = async () => { 116 | let eles = document.querySelectorAll(".richcontent"); 117 | if (!eles.length) { slog(`no richcontent found`); return false; } 118 | slog(`remove ${eles.length} richcontent`); 119 | eles.forEach(e => { if (!e.innerHTML.match(/(youtube.com|youtu.be|-player")/)) e.remove(); }); 120 | return true; 121 | }, 122 | async_push = async (list, item) => list.push(item), 123 | extractor = async (e, adv = false) => { 124 | let url = e.href, extract = false, 125 | reg_list = [ 126 | /imgur\.com\/gallery\/\w{5,7}/, 127 | /imgur\.com\/a\/\w{5,7}/, 128 | /imgur\.com\/\w{5,7}/, 129 | /pbs\.twimg\.com\/media\/[\w-]+/, 130 | /(?<=https:\/\/|http:\/\/).*\.\w{3,4}$/, 131 | ], 132 | twitter_format = /(?<=media[^\.\n]+\.|format=)\w{3,4}/, 133 | format_check = /\.(jpg|jpeg|png|webp|gif|gifv|mp4|webm)$/; 134 | if (e.getAttribute(fixed)) return false; 135 | if (adv) { 136 | if (e.nextSibling) { 137 | if (e.nextSibling.nodeType !== 3) return false; 138 | url += e.nextSibling.textContent.trim(); 139 | } 140 | if (reg_list.find(reg => e.textContent.match(reg))) return false; 141 | } 142 | if (!url) return false; // no link 143 | reg_list = reg_list.map(reg => url.match(reg)); 144 | extract = reg_list.findIndex(reg => Boolean(reg)); 145 | if (extract == -1) return false; //no reg match 146 | if ((extract == reg_list.length - 1) && !(url.match(format_check))) return false; //match the last reg but not in format list 147 | extract = `https://${reg_list[extract][0]}`; 148 | if (extract.includes("pbs.twimg.com")) extract += `.${url.match(twitter_format)[0]}`; 149 | return extract; 150 | }, 151 | extract_in_text = (eles) => extract_url(eles, true), 152 | extract_url = async (eles, in_text = false) => { 153 | let list = [], url; 154 | for (let e of eles) { 155 | url = await extractor(e, in_text); 156 | if (!url) continue; 157 | await async_push(list, { e, url }); 158 | } 159 | return list; 160 | }, 161 | get_imgur_image = (url) => { 162 | return new Promise(reslove => { 163 | GM_xmlhttpRequest({ 164 | method: "GET", url, 165 | onload: async (rs) => { 166 | let full_url = rs.responseText.match(/(https:\/\/i.imgur\.com\/\w+\.\w{3,4})\W[\w#]+">/); 167 | full_url = full_url ? full_url[1] : false; 168 | slog(!full_url ? `${url} has no data` : `GET ${url} done`); 169 | return reslove([full_url, rs.status]); 170 | }, 171 | }); 172 | }); 173 | }, 174 | create_img_ele = (url, get_img) => { 175 | let box = Object.assign(document.createElement("div"), { className: "richcontent" }), 176 | a = Object.assign(document.createElement("a"), { 177 | href: url, 178 | target: "_blank", 179 | style: "text-decoration: none; box-shadow: none; background: none;", 180 | innerHTML: ``, //loading="lazy" 181 | referrerPolicy: "no-referrer", 182 | rel: "noreferrer noopener nofollow", 183 | }); 184 | if (!get_img) { 185 | a.style = a.innerHTML = ""; 186 | a.textContent = `${url} (分段修復)`; 187 | } 188 | a.setAttribute(fixed, 1); 189 | box.appendChild(a); 190 | return box; 191 | }, 192 | create_rd_ele = (text = "") => { 193 | return Object.assign(document.createElement("div"), { 194 | className: "richcontent", 195 | style: "color: darkred;", 196 | title: text, 197 | textContent: rd_text(text), 198 | }); 199 | }, 200 | fix_image = async (obj, get_img) => { 201 | let url, status; 202 | if (obj.url.includes("imgur")) { 203 | for (let retry = 3; retry >= 0; retry--) { 204 | if (retry < 3) slog(`retry ${obj.url}, remain ${retry}`); 205 | [url, status] = await get_imgur_image(obj.url); 206 | if (status == 200) break; 207 | } 208 | } else { 209 | url = obj.url; 210 | } 211 | if (!url) url = "https://i.imgur.com/removed.png"; 212 | url = (blacklist_img.find(img => url.includes(img))) ? create_rd_ele(url) : create_img_ele(url, get_img); 213 | obj.e.insertAdjacentElement("afterend", url); 214 | obj.e.target = "_blank"; 215 | obj.e.setAttribute(fixed, 1); 216 | return; 217 | }, 218 | process_ele = async (eles, extractor, log, get_img = true) => { 219 | if (!eles.length) return; 220 | eles = await extractor(eles); 221 | if (!eles?.length) return; 222 | slog(log, eles); 223 | eles.forEach(e => fix_image(e, get_img)); 224 | return; 225 | }, 226 | main = async () => { 227 | let eles = document.querySelectorAll("a[href]"); 228 | /* 229 | await process_ele(eles, extract_url, "try fix"); 230 | await sleep(1000); 231 | */ 232 | await process_ele(eles, extract_in_text, "try fix spaced", GM_config.get("fix_segment")); 233 | }, 234 | sleep = (ms = 100) => new Promise(resolve => setTimeout(resolve, ms)), 235 | start_script = async () => { 236 | slog("script start"); 237 | await wait_tab(); 238 | await ini_config(); 239 | await load_value(); 240 | await remove_blacklist_target(); 241 | //await remove_richcontent(); 242 | await main(); 243 | GM_config.onOpen(); // update ui 244 | }, 245 | load_value = async () => { 246 | blacklist_id = GM_config.get("blacklist_id").split("\n"); 247 | blacklist_img = GM_config.get("blacklist_img").split("\n"); 248 | slog("load blacklist, id", blacklist_id.length, "img", blacklist_img.length); 249 | return true; 250 | }, 251 | ini_config = async () => { 252 | GM_registerMenuCommand("設定(alt+Q)", () => GM_config.open()); 253 | document.addEventListener("keydown", (e) => { if (e.altKey && e.key == "q") GM_config.open(); }); 254 | GM_config.init({ 255 | id: "settings", // The id used for this instance of GM_config 256 | title: "腳本設定", 257 | fields: { 258 | // This is the id of the field 259 | fix_segment: { 260 | label: "嘗試對分段網址開圖 (小心使用)", 261 | section: "功能設定", 262 | type: "checkbox", 263 | default: true, 264 | }, 265 | blacklist_id: { 266 | label: "ID", 267 | section: ["黑名單", "每行一個ID/圖片檔名"], 268 | type: "textarea", 269 | default: blacklist_id.join("\n") 270 | }, 271 | blocked_id: { 272 | label: "由於圖片被阻擋,但ID未在名單中", 273 | type: "textarea", 274 | default: [...blocked_id].join("\n") 275 | }, 276 | blacklist_img: { 277 | label: "圖片名稱", 278 | type: "textarea", 279 | default: blacklist_img.join("\n") 280 | }, 281 | blocked_img: { 282 | label: "由於ID被阻擋,但圖片未在名單中", 283 | type: "textarea", 284 | default: [...blocked_img].join("\n") 285 | }, 286 | }, 287 | css: ` 288 | #settings_fix_segment_var { 289 | display: inline-flex; 290 | margin: 0.5rem !important; 291 | border: 0.1rem solid; 292 | padding: 0.5rem; 293 | } 294 | 295 | #settings_blacklist_id_var,#settings_blacklist_img_var,#settings_blocked_id_var,#settings_blocked_img_var { 296 | width: calc(90% / 4) !important; 297 | height: 60% !important; 298 | margin: 1rem !important; 299 | display: inline-block; 300 | } 301 | 302 | #settings_blacklist_id_field_label,#settings_blacklist_img_field_label,#settings_blocked_id_field_label,#settings_blocked_img_field_label,#settings_fix_segment_field_label { 303 | font-size: 1rem !important; 304 | text-align: center; 305 | display: block; 306 | } 307 | 308 | #settings_field_blacklist_id,#settings_field_blacklist_img,#settings_field_blocked_id,#settings_field_blocked_img { 309 | width: 100% !important; 310 | height: 90% !important; 311 | } 312 | `, 313 | }); 314 | const 315 | blacklist = ["blacklist_id", "blacklist_img",], 316 | load_list = (list_id = "") => { return GM_config.get(list_id); }; 317 | GM_config.onOpen = () => { 318 | let ui_id = [ 319 | { id: "blocked_id", data: blocked_id }, 320 | { id: "blocked_img", data: blocked_img }, 321 | ]; 322 | ui_id.forEach(o => { 323 | slog("load", o.id, o.data.size); 324 | GM_config.fields[o.id].value = [...o.data].join("\n"); 325 | }); 326 | blacklist.forEach(id => { 327 | let list = load_list(id); 328 | slog("load", id, list.split("\n").length); 329 | GM_config.fields[id].value = list; 330 | }); 331 | }; 332 | GM_config.onSave = () => { 333 | blacklist.forEach(id => { 334 | let list = load_list(id); 335 | slog("save", id, list.split("\n").length); 336 | }); 337 | }; 338 | return true; 339 | }; 340 | document.body.onload = start_script; 341 | })(); 342 | 343 | -------------------------------------------------------------------------------- /reddit_img_relink.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name re-link image in reddit 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/reddit_img_relink.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/reddit_img_relink.user.js 6 | // @version 0.16 7 | // @description show images direct link under title 8 | // @author x94fujo6 9 | // @match https://www.reddit.com/r/* 10 | // @match https://www.reddit.com/r/*/* 11 | // @grant none 12 | // ==/UserScript== 13 | 14 | (function () { 15 | 'use strict'; 16 | window.onload = function () { 17 | window.document.body.onload = setInterval(relink, 1000); 18 | }; 19 | 20 | function relink() { 21 | let posts = document.querySelectorAll(".scrollerItem"); 22 | if (!posts) return; 23 | posts.forEach(post => { 24 | setTimeout(() => { 25 | if (post.getAttribute("fixed_by_script")) return; 26 | let load = post.querySelector("[src='https://www.redditstatic.com/desktop2x/img/renderTimingPixel.png']"); 27 | if (load) load.remove(); 28 | let box = post.querySelector(".STit0dLageRsa2yR4te_b"); 29 | if (box) { 30 | /* 31 | let link_to_post = box.querySelector(`a[href*="/r/"]`); 32 | if (link_to_post) { 33 | let pos = link_to_post.parentElement; 34 | let copy = link_to_post.cloneNode(true); 35 | link_to_post.remove(); 36 | pos.appendChild(copy); 37 | } 38 | */ 39 | let image = box.querySelector("img"); 40 | if (image) { 41 | image.src = image.src.replace(/https:\/\/preview\.redd\.it\/([^.]+\.[^\?]+)\?.*/, (m, p1) => `https://i.redd.it/${p1}`); 42 | if (image.src.match(/external-preview/)) { 43 | let source = post.querySelector(".styled-outbound-link"); 44 | if (source) image.src = source.href; 45 | } 46 | let pos = post.querySelector("._2FCtq-QzlfuN-SwVMUZMM3"); 47 | if (pos) { 48 | let link = document.createElement("a"); 49 | link.textContent = link.href = image.src; 50 | link.target = "_blank"; 51 | let div = document.createElement("div"); 52 | div.appendChild(link); 53 | pos.appendChild(div); 54 | } 55 | } 56 | } 57 | post.setAttribute("fixed_by_script", "true"); 58 | }); 59 | }); 60 | } 61 | })(); 62 | -------------------------------------------------------------------------------- /rule34_tool.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name rule34 harvest tool 3 | // @version 0.01 4 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 5 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/rule34_tool.user.js 6 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/rule34_tool.user.js 7 | // @description get direct link of the image in the list / single post view 8 | // @author x94fujo6 9 | // @match https://rule34.xxx/index.php?page=post&s=view&id=* 10 | // @match https://rule34.xxx/index.php?page=post&s=list* 11 | // @grant none 12 | // ==/UserScript== 13 | /* jshint esversion: 9 */ 14 | 15 | (function () { 16 | const 17 | script_name = "rule34 tool", 18 | slog = (...any) => console.log(`[${script_name}]`, ...any), 19 | sleep = (ms = 100) => new Promise(resolve => setTimeout(resolve, ms)); 20 | let list_id = "selected_list"; 21 | let box_id = "selected_box"; 22 | window.onload = start_script(); 23 | 24 | function start_script() { 25 | let url = document.location.href; 26 | if (url.includes("s=view")) { 27 | single_view(); 28 | } 29 | if (url.includes("s=list")) { 30 | list_view(); 31 | } 32 | } 33 | 34 | async function list_view() { 35 | let pos = document.querySelector(`#content .content`); 36 | let all_ele = pos.querySelectorAll(".thumb"); 37 | let reg = /`; 83 | 84 | let button, 85 | button_class = "myButton line-4-item", 86 | button_box = document.createElement("div"); 87 | button_box.style = `width: 100%;`; 88 | [ 89 | { text: "Select All", click: secectAll }, 90 | { text: "Unselect All", click: unsecectAll }, 91 | { text: "Invert Select", click: invertSecect }, 92 | { text: "Copy", click: copyAll }, 93 | ].forEach(o => { 94 | button = newButton(button_class, o.text, o.click); 95 | button_box.appendChild(button); 96 | }); 97 | select_box.appendChild(button_box); 98 | 99 | target.insertAdjacentElement("afterbegin", select_box); 100 | } 101 | 102 | function addCheckbox(link, toEle) { 103 | let div = document.createElement("div"); 104 | let label = document.createElement("label"); 105 | label.className = "myLable"; 106 | if (link.length > 0) { 107 | label.innerHTML = ` 108 | 109 |
Add to List
110 | `; 111 | label.onclick = updateTextBox; 112 | } else { 113 | label.innerHTML = ` 114 |
found no link
115 | `; 116 | } 117 | div.className = "lable_box"; 118 | div.appendChild(label); 119 | toEle.insertAdjacentElement("afterbegin", div); 120 | } 121 | 122 | async function getTrueLink(post_link) { 123 | let r = await fetch(post_link); 124 | let text = await r.text(); 125 | let link = text.match(reg); 126 | if (link) { 127 | return link[1]; 128 | } else { 129 | return false; 130 | } 131 | } 132 | } 133 | 134 | function updateTextBox() { 135 | let inputs = document.querySelectorAll(`input[name='${list_id}']:checked`); 136 | let newtext = ""; 137 | if (inputs) { 138 | inputs.forEach(ck => { 139 | if (!newtext.includes(ck.value)) newtext += `${ck.value}\n`; 140 | }); 141 | } 142 | let box = document.getElementById(list_id); 143 | box.value = newtext; 144 | } 145 | 146 | function copyAll() { 147 | let textbox = document.getElementById(list_id); 148 | navigator.clipboard.writeText(textbox.value); 149 | } 150 | 151 | function invertSecect() { 152 | let inputs = document.querySelectorAll(`input[name='${list_id}']`); 153 | inputs.forEach(e => { 154 | if (e.checked) { 155 | e.checked = false; 156 | } else { 157 | e.checked = true; 158 | } 159 | }); 160 | updateTextBox(); 161 | } 162 | 163 | function unsecectAll() { 164 | let inputs = document.querySelectorAll(`input[name='${list_id}']:checked`); 165 | inputs.forEach(e => { 166 | e.checked = false; 167 | }); 168 | updateTextBox(); 169 | } 170 | 171 | function secectAll() { 172 | let inputs = document.querySelectorAll(`input[name='${list_id}']`); 173 | inputs.forEach(e => { 174 | if (!e.checked) e.checked = true; 175 | }); 176 | updateTextBox(); 177 | } 178 | 179 | function single_view(getlink = false) { 180 | let link = document.querySelector(`[property="og:image"]`).content; 181 | if (getlink) { 182 | return link; 183 | } else { 184 | let pos = document.querySelector(`#content .content`); 185 | let ele = document.createElement("a"); 186 | let copy = document.createElement("button"); 187 | let box = document.createElement("div"); 188 | ele.textContent = ele.href = link; 189 | 190 | box.style = ` 191 | display: flex; 192 | `; 193 | copy.style = ` 194 | display: flex; 195 | padding: 2rem; 196 | width: max-content; 197 | margin: 0.2rem; 198 | `; 199 | ele.style = ` 200 | display: flex; 201 | padding: 2rem; 202 | background-color: rgba(0,0,0,0.25); 203 | width: max-content; 204 | margin: 0.2rem; 205 | `; 206 | 207 | copy.onclick = () => { 208 | navigator.clipboard.writeText(link); 209 | }; 210 | copy.textContent = "copy link"; 211 | 212 | box.appendChild(copy); 213 | box.appendChild(ele); 214 | pos.insertAdjacentElement("afterbegin", box); 215 | } 216 | } 217 | 218 | function myCss() { 219 | let s = document.createElement("style"); 220 | s.className = "myCssSheet"; 221 | document.head.appendChild(s); 222 | s.textContent = ` 223 | .my_thumb { 224 | height: max-content !important; 225 | } 226 | 227 | .added { 228 | display:initial !important; 229 | } 230 | 231 | .myButtonB { 232 | position: relative; 233 | padding: 1rem; 234 | width: 50%; 235 | font-size: 1.5rem; 236 | margin: 1rem auto; 237 | display: block; 238 | } 239 | 240 | .myButton { 241 | position: relative; 242 | padding: 0.5rem 0; 243 | margin: 0.5rem; 244 | font-size: 1rem; 245 | white-space: nowrap; 246 | text-overflow: ellipsis; 247 | overflow: hidden; 248 | } 249 | 250 | .line-4-item { 251 | max-width: 100%; 252 | width: calc(80% / 4); 253 | } 254 | 255 | .myButton:active, .myButtonB:active { 256 | background-color: DeepPink; 257 | } 258 | 259 | .lable_box { 260 | margin: 0.5rem 0; 261 | } 262 | 263 | .myLable { 264 | position: relative; 265 | width: auto; 266 | padding: 0.5rem; 267 | border-style: solid; 268 | border-width: 0.1rem; 269 | border-color: gray; 270 | display: flex; 271 | justify-content: center; 272 | } 273 | 274 | .myLable>input { 275 | position: relative; 276 | margin: auto; 277 | margin-left: 0rem; 278 | margin-right: 0.2rem; 279 | } 280 | 281 | .myLable>div { 282 | position: relative; 283 | margin: 0.1rem; 284 | } 285 | `; 286 | } 287 | 288 | function newButton(bclass, btext, handeler) { 289 | let button = document.createElement("button"); 290 | Object.assign(button, { 291 | className: bclass, 292 | textContent: btext, 293 | onclick: handeler, 294 | }); 295 | return button; 296 | } 297 | })(); 298 | 299 | -------------------------------------------------------------------------------- /sharer-pw_auto_click.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name sharer.pw auto click 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/sharer-pw_auto_click.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/sharer-pw_auto_click.user.js 6 | // @version 0.2 7 | // @description auto click and redirect to download link immediately 8 | // @author x94fujo6 9 | // @match https://sharer.pw/file/* 10 | // @grant none 11 | // ==/UserScript== 12 | 13 | (function () { 14 | 'use strict'; 15 | 16 | window.onload = main(); 17 | 18 | function main() { 19 | let idButton = setInterval(() => { watcherButton(idButton); }, 100); 20 | let idLink = setInterval(() => { watcherLink(idLink); }, 100); 21 | } 22 | 23 | function watcherButton(id) { 24 | let button = document.getElementById("btndl"); 25 | if (!button) return; 26 | console.log(`stop ${watcherButton.name}(${id})`); 27 | clearInterval(id); 28 | button.click(); 29 | } 30 | 31 | function watcherLink(id) { 32 | let links = getDownloadLink(); 33 | if (!links) return; 34 | console.log(`stop ${watcherLink.name}(${id})`); 35 | clearInterval(id); 36 | if (links.length === 1) { 37 | window.location.href = links[0]; 38 | } else { 39 | console.log("multiple links detected!! abort"); 40 | console.log(links); 41 | } 42 | } 43 | 44 | function getDownloadLink() { 45 | let links = []; 46 | [...document.querySelectorAll("a")] 47 | .filter(e => e.textContent.includes("click here")) 48 | .forEach(e => links.push(e.href)); 49 | if (links.length === 0) { 50 | return false; 51 | } else { 52 | return links; 53 | } 54 | } 55 | })(); -------------------------------------------------------------------------------- /skip_gamekee_popup.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name skip gamekee popup 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/skip_gamekee_popup.user.js 5 | // @version 0.2 6 | // @description 自動關閉gamekee註冊訊息 7 | // @author x94fujo6 8 | // @include https://*.gamekee.com/* 9 | // ==/UserScript== 10 | 11 | (async function () { 12 | function wait_tab() { 13 | return new Promise(resolve => { 14 | if (document.visibilityState === "visible") return resolve(); 15 | console.log("tab in background, script paused"); 16 | document.addEventListener("visibilitychange", () => { 17 | if (document.visibilityState === "visible") { console.log("script unpaused"); return resolve(); } 18 | }); 19 | }); 20 | } 21 | 22 | function waitHTML(css_selector, run) { 23 | let id = setInterval(() => { 24 | if (document.querySelectorAll(css_selector).length) { 25 | clearInterval(id); 26 | run(); 27 | console.log(`found [${css_selector}]`); 28 | } else { 29 | console.log(`[${css_selector}] not found`); 30 | } 31 | }, 100); 32 | } 33 | 34 | function main() { 35 | let css_selector = ".el-popup-parent--hidden"; 36 | removePopup(); 37 | waitHTML(css_selector, () => resumeBody(css_selector.replace(".", ""))); 38 | } 39 | 40 | function removePopup() { 41 | let css_selector = [ 42 | "body > .el-dialog__wrapper", 43 | ".v-modal" 44 | ].join(","); 45 | 46 | document.querySelectorAll(css_selector) 47 | .forEach(ele => { 48 | ele.remove(); 49 | }); 50 | } 51 | 52 | function resumeBody(class_name = "") { 53 | document.body.style.overflow = "unset"; 54 | document.body.classList.remove(class_name); 55 | } 56 | 57 | await wait_tab(); 58 | waitHTML("body > .el-dialog__wrapper", main); 59 | })(); 60 | 61 | -------------------------------------------------------------------------------- /taipower_tool.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name 台電網頁工具 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/taipower_tool.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/taipower_tool.user.js 6 | // @version 0.01 7 | // @description 計算並顯示實際發電能力 8 | // @author x94fujo6 9 | // @match https://www.taipower.com.tw/tc/page.aspx?mid=206* 10 | // ==/UserScript== 11 | /* jshint esversion: 9 */ 12 | 13 | (async function () { 14 | let lastest = new Date(), 15 | hold = true; 16 | 17 | addCss(); 18 | await addInfo(new Date(lastest)); 19 | setHold(); 20 | 21 | setInterval(() => { 22 | let isUpdate = checkUpdate(); 23 | if (isUpdate > lastest && !hold) { 24 | let now = new Date(); 25 | setHold(); 26 | lastest = now.getTime(); 27 | console.log(`資料更新@${getTimeString(now)}`); 28 | addInfo(now); 29 | } 30 | }, 1000); 31 | 32 | function setHold() { 33 | hold = true; 34 | setTimeout(() => { 35 | hold = false; 36 | }, 61000); 37 | } 38 | 39 | function getTimeString(time) { 40 | return time.toTimeString().split(" ")[0]; 41 | } 42 | 43 | function getHMS(time) { 44 | return getTimeString(time).split(":"); 45 | } 46 | 47 | function checkUpdate() { 48 | let now = new Date(), 49 | [h, m, s] = getHMS(now); 50 | return ((m % 10 == 0) && (s > 10)) ? now.getTime() : false; 51 | } 52 | 53 | async function addInfo(time) { 54 | let pos = document.querySelector("#main_info"), 55 | box = document.createElement("div"), 56 | data = await getData(), 57 | html = [], 58 | box_id = "usInfo", 59 | old_box = document.getElementById(box_id); 60 | if (old_box) old_box.remove(); 61 | box.id = box_id; 62 | box.classList.add("usBox"); 63 | for (let [key, value] of Object.entries(data)) { 64 | html.push(` 65 |
66 |
${key.replace(/\d_/, "")}
67 |
${value}
68 |
69 | `); 70 | } 71 | html.push(` 72 |
73 |
單位: MW
74 |
最後更新: ${getTimeString(time)}
75 |
76 | `); 77 | 78 | box.innerHTML = html.join(""); 79 | pos.insertAdjacentElement("afterbegin", box); 80 | } 81 | 82 | async function getData() { 83 | let url = "https://www.taipower.com.tw/d006/loadGraph/loadGraph/data/genary.json", 84 | raw = await fetch(url), 85 | parseRaw = (arr) => { 86 | let [type, group, name, cap, gen, gen_percent, note] = arr; 87 | type = type.match(/b>(.*)<\/b/)[1].trim(); 88 | cap = parseFloat(cap); 89 | gen = parseFloat(gen); 90 | note = note.trim(); 91 | if (isNaN(cap)) cap = 0; 92 | if (isNaN(gen)) gen = 0; 93 | return { type, name, cap, gen, note }; 94 | }, 95 | not_run = [ 96 | "歲修", 97 | "故障", 98 | "環保停機檢修", 99 | "檢修", 100 | "機組安檢", 101 | "測試停機", 102 | ], 103 | limit = [ 104 | "水文限制", 105 | "燃料限制", 106 | "環保限制", 107 | "空污減載", 108 | "測試運轉", 109 | "運轉限制", 110 | "EOH限制", 111 | "合約限制", 112 | "電源線限制", 113 | "輔機檢修", 114 | "外溫高限制", 115 | "歲修逾排程", 116 | 117 | "部分歲修", 118 | "部分檢修", 119 | "部分故障", 120 | 121 | "友善降載減排", 122 | ], 123 | green = [ 124 | "水力(Hydro)", 125 | "風力(Wind)", 126 | "太陽能(Solar)", 127 | "抽蓄發電(Pumping Gen)", 128 | "其它再生能源(Other Renewable Energy)", 129 | ], 130 | offshore = [ 131 | "澎湖", 132 | "金門", 133 | "馬祖", 134 | "離島", 135 | ], 136 | data = [], 137 | p = [], 138 | sum_max_cap = 0, 139 | sum_max_actual = 0, 140 | sum_green_cap = 0, 141 | sum_no_green_cap = 0, 142 | sum_green_gen = 0 143 | ; 144 | raw = await raw.json(); 145 | raw = raw.aaData; 146 | raw.forEach(arr => { 147 | let pp = parseRaw(arr), 148 | { name, cap, gen } = pp; 149 | if ( 150 | name != "小計" && 151 | (cap || gen) && 152 | !offshore.some(t => name.includes(t)) 153 | ) { 154 | data.push(pp); 155 | } 156 | }); 157 | //console.log(data); 158 | data.forEach(pp => { 159 | p.push((async () => { 160 | if (!not_run.some(t => t == pp.note)) { 161 | if (limit.some(t => t == pp.note) || pp.type == "抽蓄負載(Pumping Load)") { 162 | if (green.some(t => t == pp.type)) { 163 | sum_green_cap += pp.gen; 164 | sum_green_gen += pp.gen; 165 | } else { 166 | sum_no_green_cap += pp.gen; 167 | } 168 | } else { 169 | if (green.some(t => t == pp.type)) { 170 | sum_green_cap += pp.cap; 171 | sum_green_gen += pp.gen; 172 | } else { 173 | sum_no_green_cap += pp.cap; 174 | } 175 | } 176 | } 177 | })()); 178 | }); 179 | await Promise.all(p); 180 | sum_max_cap = sum_no_green_cap + sum_green_cap; 181 | sum_max_actual = sum_no_green_cap + sum_green_gen; 182 | [ 183 | sum_max_cap, 184 | sum_green_cap, 185 | sum_no_green_cap, 186 | sum_green_gen, 187 | sum_max_actual, 188 | ] = [ 189 | sum_max_cap, 190 | sum_green_cap, 191 | sum_no_green_cap, 192 | sum_green_gen, 193 | sum_max_actual, 194 | ].map(num => num = Math.floor(num)); 195 | sum_green_gen = `${sum_green_gen} (${Math.floor(sum_green_gen / sum_green_cap * 10000) / 100}%)`; 196 | 197 | let result; 198 | result = { 199 | "1_總容量": sum_max_cap, 200 | "2_總容量 (綠能僅實際發電)": sum_max_actual, 201 | "3_容量 (非綠能)": sum_no_green_cap, 202 | "5_容量 (綠能)": sum_green_cap, 203 | "4_容量 (綠能-實際發電)": sum_green_gen, 204 | }; 205 | console.log(result); 206 | return result; 207 | } 208 | 209 | function addCss() { 210 | let s = document.createElement("style"); 211 | document.head.appendChild(s); 212 | s.textContent = ` 213 | .usBox { 214 | display: block; 215 | width: 50%; 216 | margin: auto; 217 | } 218 | 219 | .usBold { 220 | font-weight: bold; 221 | font-size: large; 222 | } 223 | 224 | .usLine { 225 | display: flex; 226 | margin-bottom: 0.5rem; 227 | } 228 | 229 | .usUnderLine { 230 | border-bottom: 0.1rem black solid; 231 | border-left: 0.2rem black solid; 232 | } 233 | 234 | .usTextL { 235 | position: relative; 236 | margin: 0 auto 0 0; 237 | padding-left: 0.25rem; 238 | } 239 | 240 | .usTextR { 241 | position: relative; 242 | margin: 0 0 0 auto; 243 | padding-right: 0.25rem; 244 | } 245 | `; 246 | } 247 | })(); -------------------------------------------------------------------------------- /ytb_url_normalizer.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name youtube url normalizer 3 | // @namespace https://github.com/x94fujo6rpg/SomeTampermonkeyScripts 4 | // @updateURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/ytb_url_normalizer.user.js 5 | // @downloadURL https://github.com/x94fujo6rpg/SomeTampermonkeyScripts/raw/master/ytb_url_normalizer.user.js 6 | // @version 0.03 7 | // @description normalize url in video description / remove miniplayer (still activated when you watch in playlist) 8 | // @author x94fujo6 9 | // @match https://www.youtube.com/* 10 | // @grant none 11 | // ==/UserScript== 12 | 13 | (function () { 14 | 'use strict'; 15 | window.onload = () => { setInterval(main, 500); }; 16 | 17 | function main() { 18 | let links = document.querySelectorAll("a.yt-formatted-string"); 19 | if (links) normalizer(links); 20 | removeMiniplayer(); 21 | 22 | function normalizer(links) { 23 | links.forEach(a => { 24 | if (!a.href.includes("redirect?")) return; 25 | let url = getLink(a.href); 26 | if (!url) return; 27 | a.href = decodeURIComponent(url); 28 | }); 29 | 30 | function getLink(href) { 31 | let reg = /&q=(.[^\&]*)/; 32 | let extract = reg.exec(href); 33 | return extract ? extract[1] : false; 34 | } 35 | } 36 | 37 | function removeMiniplayer() { 38 | let b = document.querySelectorAll(".ytp-miniplayer-button"); 39 | if (b) b.forEach(e => { e.remove(); }); 40 | b = document.querySelectorAll("[src*='miniplayer.js']"); 41 | if (b) b.forEach(e => { e.remove(); }); 42 | } 43 | } 44 | })(); 45 | --------------------------------------------------------------------------------