Skip to content

Instantly share code, notes, and snippets.

@BrutuZ
Last active October 28, 2025 21:32
Show Gist options
  • Select an option

  • Save BrutuZ/29952c436d5abde675f798788e237432 to your computer and use it in GitHub Desktop.

Select an option

Save BrutuZ/29952c436d5abde675f798788e237432 to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name MU Toggle Backlog
// @version 0.15.2
// @description Adds link to only show items in your list with unread chapters
// @author BrutuZ
// @match https://www.mangaupdates.com/lists/*
// @icon https://www.mangaupdates.com/images/manga-updates.svg
// @homepage https://gist.github.com/BrutuZ/29952c436d5abde675f798788e237432
// @downloadURL https://gist.github.com/BrutuZ/29952c436d5abde675f798788e237432/raw/mu-backlog-toggle.user.js
// @updateURL https://gist.github.com/BrutuZ/29952c436d5abde675f798788e237432/raw/mu-backlog-toggle.user.js
// @connect mangaupdates.com
// @grant GM.xmlHttpRequest
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @noframes
// ==/UserScript==
(function() {
'use strict';
const APIURL = 'https://api.mangaupdates.com/v1/series/';
const CDNURL = 'https://cdn.mangaupdates.com/image/thumb/';
const DRAWING = [];
function backlog() {
document.getElementById('my_toggle')?.remove();
const entries = document.querySelectorAll('div[class^=series-list] > div.row');
const unread = document.querySelectorAll('span[class*=newlist]');
const rem = document.createElement('a');
rem.id = 'my_toggle';
rem.innerText = `👁️‍🗨️ Toggle ${entries.length - unread.length}/${
entries.length
} entries without unread chapters`;
rem.className = 'text';
rem.style = 'cursor: pointer';
rem.addEventListener('click', () => {
entries.forEach(item => {
if (item.querySelector('span[class*=newlist]')) return;
item.hidden = !item.hidden;
if (item.hidden) item.style = 'display:none';
else item.removeAttribute('style');
});
});
document.querySelector('main .d-flex').insertAdjacentElement('afterend', rem);
const footer = document.querySelector('div:has(> a[title^=Export])');
if (footer.textContent.search('unread') === -1) {
footer.insertBefore(
document.createTextNode(`${unread.length} unread] [`),
document.querySelector('a[title^=Export]')
);
}
if (unread.length > 0) document.getElementById('my_toggle').click();
}
function fixStyleSheet() {
for (var i = 0; i < document.styleSheets.length; i++) {
const sheet = document.styleSheets[i];
if (!sheet.href?.startsWith('http')) return false;
var match = false;
for (var j = 0; j < sheet.cssRules.length; j++) {
const rule = sheet.cssRules[j];
match = rule?.selectorText?.match(/.*list_table.* > :nth-child\(2n\)/);
if (match) {
rule.selectorText = match[0].replace('2n', '2n of :not([hidden])');
break;
}
}
if (match) break;
}
}
function drawCovers() {
const newChaps = Array.from(document.querySelectorAll('[class^=series-list-item_newlist] > a'));
if (newChaps.length > 0) backlog();
else return;
console.log(newChaps.length, 'entries');
newChaps.forEach(el => {
const th = el.closest('div').closest('.row').querySelector('.col-auto');
if (th.querySelector('img')) {
console.log(el.closest('.text').textContent, 'Already has an image');
return;
}
const id = el.href.replace(/\D/g, '');
const savedThumb = GM_getValue(id);
if (savedThumb) draw(savedThumb, th, id);
else {
GM.xmlHttpRequest({
url: APIURL + id,
responseType: 'json',
fetch: true,
onloadstart: console.log('Requesting API'),
})
.then(req => {
console.log('Requested:', req.response.title);
const thumb = req.response.image.url.thumb.replace(CDNURL, '');
GM_setValue(id, thumb);
draw(thumb, th, id);
})
.catch(e => {
console.error('Error during request:', e.error);
return;
});
}
});
}
function draw(thumb, th, id) {
if (th.querySelector(`img[src="${CDNURL + thumb}"]`) || DRAWING.includes(id)) {
console.log('Skipping', { ID: id, Thumb: thumb });
return;
}
console.debug('Drawing', { ID: id, Thumb: thumb });
const img = document.createElement('img');
img.loading = 'lazy';
img.src = CDNURL + thumb;
img.className = 'thumbnail';
img.addEventListener('error', () => {
GM_deleteValue(id);
img.remove();
const tid = DRAWING.indexOf(id);
if (tid !== -1) DRAWING.splice(tid, 1);
drawCovers();
});
th.insertAdjacentElement('beforeend', img);
DRAWING.push(id);
}
function createObserver() {
const observer = new MutationObserver(check);
observer.observe(document.querySelector('main'), {
childList: true,
subtree: true,
});
return observer;
}
function check(_, observer) {
if (document.querySelector('div[class^=series-list]') && !document.querySelector('.thumbnail')) {
drawCovers();
}
observer.disconnect();
}
fixStyleSheet();
backlog();
drawCovers();
createObserver();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment