Adds a button to view anime on nzbs.moe using the AniList ID from Nyaa torrents
Size
5.1 KB
Version
1.0.1
Created
Jan 17, 2026
Updated
about 1 month ago
1// ==UserScript==
2// @name Nyaa nzbs.moe Link Button
3// @description Adds a button to view anime on nzbs.moe using the AniList ID from Nyaa torrents
4// @version 1.0.1
5// @match https://nyaa.si/view/*
6// @icon https://nyaa.si/static/favicon.png
7// @require https://raw.githubusercontent.com/TalkingJello/moe-delicious-library/refs/heads/main/moe-delicious-library.json
8// ==/UserScript==
9(function() {
10 'use strict';
11
12 console.log('nzbs.moe button extension loaded');
13
14 async function fetchMoeLibrary() {
15 try {
16 const response = await fetch('https://raw.githubusercontent.com/TalkingJello/moe-delicious-library/refs/heads/main/moe-delicious-library.json');
17 const data = await response.json();
18 console.log('Moe library loaded successfully');
19 return data;
20 } catch (error) {
21 console.error('Failed to load moe library:', error);
22 return null;
23 }
24 }
25
26 function findNzbsMoeId(anilistId, moeLibrary) {
27 if (!moeLibrary || !moeLibrary.data) {
28 console.log('Moe library data not available');
29 return null;
30 }
31
32 // Search through the library to find matching AniList ID
33 for (const entry of moeLibrary.data) {
34 if (entry.sources && entry.sources.anilist) {
35 const anilistUrl = entry.sources.anilist;
36 const match = anilistUrl.match(/\/anime\/(\d+)/);
37 if (match && match[1] === anilistId) {
38 return entry.id;
39 }
40 }
41 }
42
43 console.log('No nzbs.moe ID found for AniList ID:', anilistId);
44 return null;
45 }
46
47 async function addButton() {
48 // Check if button already exists
49 if (document.querySelector('.nzbs-moe-button')) {
50 console.log('nzbs.moe button already exists');
51 return;
52 }
53
54 // Find the AniList link in the torrent description
55 const anilistLink = document.querySelector('a[href*="anilist.co/anime/"]');
56
57 if (!anilistLink) {
58 console.log('AniList link not found');
59 return;
60 }
61
62 // Extract the AniList ID from the URL
63 const anilistUrl = anilistLink.href;
64 const anilistIdMatch = anilistUrl.match(/\/anime\/(\d+)/);
65
66 if (!anilistIdMatch) {
67 console.log('Could not extract AniList ID');
68 return;
69 }
70
71 const anilistId = anilistIdMatch[1];
72 console.log('Found AniList ID:', anilistId);
73
74 // Fetch the moe library and find the corresponding nzbs.moe ID
75 const moeLibrary = await fetchMoeLibrary();
76 const nzbsMoeId = findNzbsMoeId(anilistId, moeLibrary);
77
78 if (!nzbsMoeId) {
79 console.log('Could not find nzbs.moe ID for AniList ID:', anilistId);
80 // Fallback to using AniList ID directly
81 const nzbsUrl = `https://nzbs.moe/series/al/${anilistId}`;
82 createButton(nzbsUrl, anilistLink);
83 return;
84 }
85
86 // Create the nzbs.moe URL with the found ID
87 const nzbsUrl = `https://nzbs.moe/series/${nzbsMoeId}`;
88 console.log('nzbs.moe URL:', nzbsUrl);
89
90 createButton(nzbsUrl, anilistLink);
91 }
92
93 function createButton(nzbsUrl, anilistLink) {
94 // Find the container - typically the panel with torrent info
95 const container = anilistLink.closest('.panel-body') || anilistLink.closest('.col-md-5');
96
97 if (!container) {
98 console.log('Container not found');
99 return;
100 }
101
102 // Create the button
103 const nzbsButton = document.createElement('a');
104 nzbsButton.href = nzbsUrl;
105 nzbsButton.target = '_blank';
106 nzbsButton.className = 'nzbs-moe-button btn btn-primary btn-sm';
107 nzbsButton.textContent = 'View on nzbs.moe';
108 nzbsButton.style.cssText = 'margin-top: 10px; display: inline-block;';
109
110 // Insert the button after the AniList link's parent element
111 const linkParent = anilistLink.parentElement;
112 if (linkParent) {
113 linkParent.insertAdjacentElement('afterend', nzbsButton);
114 console.log('nzbs.moe button added successfully');
115 } else {
116 console.log('Could not find parent element for button placement');
117 }
118 }
119
120 function init() {
121 // Try to add button immediately
122 addButton();
123
124 // Also observe for dynamic content loading
125 const observer = new MutationObserver(function() {
126 addButton();
127 });
128
129 observer.observe(document.body, {
130 childList: true,
131 subtree: true
132 });
133
134 // Stop observing after 10 seconds
135 setTimeout(() => {
136 observer.disconnect();
137 console.log('Stopped observing for changes');
138 }, 10000);
139 }
140
141 // Wait for the page to load
142 if (document.readyState === 'loading') {
143 document.addEventListener('DOMContentLoaded', init);
144 } else {
145 init();
146 }
147})();