// ==UserScript== // @name MangaDex Shit Filter // @version 1.5 // @namespace mangadexshitfilter // @author nazatanada // @description Cleans garbage and embeds tags on latest updates page and other title list pages on Mangadex.org/.cc // @include /https?:\/\/mangadex\.org\/?.*/ // @include /https?:\/\/mangadex\.cc\/?.*/ // @require https://code.jquery.com/jquery-3.2.1.min.js // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @run-at document-end // @downloadURL none // ==/UserScript== const UPDATE = 7 * 24 * 60 * 60 * 1000; // Updates records weekly const DEXKEY = 'dex1.3'; const LANG = { 1: 'English', 2: 'Japanese', 3: 'Polish', 8: 'German', 10: 'French', 12: 'Vietnamese', 21: 'Chinese', 27: 'Indonesian', 28: 'Korean', 29: 'Spanish (LATAM)', 32: 'Thai', 34: 'Filipino', 35: 'Chinese (Trad)' }; const STAT = { 1: 'Ongoing', 2: 'Completed', 3: 'Cancelled', 4: 'Hiatus' }; const DEMO = { 1: 'Shounen', 2: 'Shoujo', 3: 'Seinen', 4: 'Josei' }; const GENR = { 1: '4-Koma', 4: 'Award Winning', 7: 'Doujinshi', 9: 'Ecchi', 2: 'Action', 3: 'Adventure', 5: 'Comedy', 8: 'Drama', 6: 'Cooking', 10: 'Fantasy', 11: 'Gyaru', 12: 'Harem', 13: 'Historical', 14: 'Horror', 16: 'Martial Arts', 17: 'Mecha', 18: 'Medical', 19: 'Music', 20: 'Mystery', 21: 'Oneshot', 22: 'Psychological', 23: 'Romance', 24: 'School Life', 25: 'Sci-Fi', 28: 'Shoujo Ai', 30: 'Shounen Ai', 31: 'Slice of Life', 32: 'Smut', 33: 'Sports', 34: 'Supernatural', 35: 'Tragedy', 36: 'Long Strip', 37: 'Yaoi', 38: 'Yuri', 40: 'Video Games', 41: 'Isekai', 42: 'Adaptation', 43: 'Anthology', 44: 'Web Comic', 45: 'Full Color', 46: 'User Created', 47: 'Official Colored', 48: 'Fan Colored', 49: 'Gore', 50: 'Sexual Violence', 51: 'Crime', 52: 'Magical Girls', 53: 'Philosophical', 54: 'Superhero', 55: 'Thriller', 56: 'Wuxia', 57: 'Aliens', 58: 'Animals', 59: 'Crossdressing', 60: 'Demons', 61: 'Delinquents', 62: 'Genderswap', 63: 'Ghosts', 64: 'Monster Girls', 65: 'Loli', 66: 'Magic', 67: 'Military', 68: 'Monsters', 69: 'Ninja', 70: 'Office Workers', 71: 'Police', 72: 'Post-Apocalyptic', 73: 'Reincarnation', 74: 'Reverse Harem', 75: 'Samurai', 76: 'Shota', 77: 'Survival', 78: 'Time Travel', 79: 'Vampires', 80: 'Traditional Games', 81: 'Virtual Reality', 82: 'Zombies', 83: 'Incest', 84: 'Mafia' }; let parallel = 0; let paradise = 5; // Decrease this value if you get a red DESU spam function keyof(arr, val) { let key = 0; if (val) { Object.entries(arr).some(e => val === e[1] && (key = e[0])); } return parseInt(key); } function make(doc, maid) { const gkeys = []; doc.find('a[href^="/genre/"]').each((idx, elm) => { gkeys.push(keyof(GENR, $(elm).text())); }); const dict = GM_getValue(DEXKEY, {}); dict[maid] = { 'm': Date.now(), 'l': keyof(LANG, doc.find('.card-header .flag').first().attr('title')), 's': keyof(STAT, doc.find('div.strong:contains("Pub. status:")').first().next().text()), 'd': keyof(DEMO, doc.find('a[href^="/search?demo_id="]').first().text()), 'g': gkeys, }; GM_setValue(DEXKEY, dict); return dict[maid]; } function filt(madict, shit) { function wrap(bad, ger) { return ger ? '' + ger + ' ' : ''; } if (madict['l'] === 2 && shit[1] !== 'm2254328958') { let e = '
'; e += wrap('primary', STAT[madict['s']]); e += wrap('primary', DEMO[madict['d']]); madict['g'].forEach(g => { const gstr = GENR[g]; e += wrap(/(^(Y[au]|W|U)|Ai$)/.test(gstr) ? 'warning' : 'secondary', gstr); }); shit[0].append(e + '
'); } else { shit[0].remove(); } } function gets(deq, maid, shit) { return () => { $.get({ url: '/title/' + maid, success: (raw) => { if (raw) { filt(make($(raw), maid), shit); } }, error: () => { if (paradise) { deq.queue(gets(deq, maid, shit)); } }, complete: (xhr) => { parallel--; if (xhr.status === 403 || xhr.status === 503) { paradise = 0; deq.clearQueue(); shit[0].parent().prepend($('
DESU
')); } else { do { deq.dequeue(); } while ( parallel++ < paradise ); } } }); }; } function mmh2_32(str, seed) { var l = str.length, h = seed ^ l, i = 0, k; while (l >= 4) { k = ((str.charCodeAt(i) & 0xff)) | ((str.charCodeAt(++i) & 0xff) << 8) | ((str.charCodeAt(++i) & 0xff) << 16) | ((str.charCodeAt(++i) & 0xff) << 24); k = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16)); k ^= k >>> 24; k = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16)); h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16)) ^ k; l -= 4; ++i; } switch (l) { case 3: h ^= (str.charCodeAt(i + 2) & 0xff) << 16; case 2: h ^= (str.charCodeAt(i + 1) & 0xff) << 8; case 1: h ^= (str.charCodeAt(i) & 0xff); h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16)); } h ^= h >>> 13; h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16)); h ^= h >>> 15; return h >>> 0; } $(document).ready(() => { const now = Date.now(); const deq = $({}); ['dex', 'dex1.2'].forEach(v => GM_deleteValue(v, null)); $('.tab-content .manga_title').each((idx, elm) => { const maid = $(elm).attr('href').match(/title\/(\d+)/i)[1]; const shit = [ $(elm).parent().parent(), 'm' + mmh2_32('m' + maid, maid)]; const dict = GM_getValue(DEXKEY, null); if (dict && dict[maid] && now - dict[maid]['m'] < UPDATE) { filt(dict[maid], shit); } else { deq.queue(gets(deq, maid, shit)); } }); });