├── .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 |
88 |
89 |
90 |
91 |
92 |
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 | 
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 ( green)
72 | - from other gallery ( 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 | 
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 ( green)
236 | - from other gallery ( 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 | 
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 | 
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 | 
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 | 
505 | 
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 | 
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 | 
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 | 
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 | 
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 |
129 |
130 |
131 |
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 |
--------------------------------------------------------------------------------