Sonarr Anime Database Links

Adds AniList, MyAnimeList, SIMKL, AnimeBytes, nzbs.moe, and SeaDex links to Sonarr anime series pages

Size

6.3 KB

Version

1.1.10

Created

Jan 17, 2026

Updated

about 1 month ago

1// ==UserScript==
2// @name		Sonarr Anime Database Links
3// @description		Adds AniList, MyAnimeList, SIMKL, AnimeBytes, nzbs.moe, and SeaDex links to Sonarr anime series pages
4// @version		1.1.10
5// @match       http://192.168.1.50:8992/sonarr-anime/*
6// @match       https://sonarr-anime.flowsflow.com/*
7// @icon		http://192.168.1.50:8992/sonarr-anime/favicon.ico
8// ==/UserScript==
9(function() {
10    'use strict';
11
12    console.log('Sonarr Anime Database Links extension loaded');
13
14    let debounceTimer;
15    let lastProcessedTitle = '';
16
17    // Function to add anime database links in the toolbar
18    async function addAnimeLinks() {
19        // Check if we're on a series page
20        const titleElement = document.querySelector('.SeriesDetails-title-pJv1g');
21        if (!titleElement) {
22            console.log('Not on a series page');
23            return;
24        }
25
26        const seriesTitle = titleElement.textContent.trim();
27        
28        // Check if we already processed this title
29        if (lastProcessedTitle === seriesTitle && document.getElementById('anime-db-container')) {
30            console.log('Anime database links already exist for this title');
31            return;
32        }
33
34        // Remove old container if it exists
35        if (document.getElementById('anime-db-container')) {
36            console.log('Removing old anime database links');
37            document.getElementById('anime-db-container')?.remove();
38        }
39
40        console.log('Series title:', seriesTitle);
41        lastProcessedTitle = seriesTitle;
42
43        // Find the toolbar
44        const toolbar = document.querySelector('.PageToolbarSection-left-E_sPJ');
45        if (!toolbar) {
46            console.log('Toolbar not found');
47            return;
48        }
49
50        // Get AniList ID for SeaDex and nzbs.moe
51        const anilistId = await getAniListId(seriesTitle);
52
53        // Create container for anime database links
54        const container = document.createElement('div');
55        container.id = 'anime-db-container';
56        container.style.cssText = 'display: flex; gap: 8px; align-items: center; margin-left: 12px;';
57
58        // Create buttons with compact style
59        const buttons = [
60            { name: 'AniList', url: `https://anilist.co/search/anime?search=${encodeURIComponent(seriesTitle)}`, color: '#02A9FF' },
61            { name: 'MAL', url: `https://myanimelist.net/anime.php?q=${encodeURIComponent(seriesTitle)}`, color: '#2E51A2' },
62            { name: 'SIMKL', url: `https://simkl.com/search/?type=anime&q=${encodeURIComponent(seriesTitle)}`, color: '#0B0F10' },
63            { name: 'AB', url: `https://animebytes.tv/torrents.php?searchstr=${encodeURIComponent(seriesTitle)}&action=advanced&search_type=title`, color: '#E74C3C' },
64            { name: 'NZBs', url: anilistId ? `https://nzbs.moe/series/al/${anilistId}` : 'https://nzbs.moe/', color: '#FF6B35' },
65            { name: 'SeaDex', url: anilistId ? `https://releases.moe/${anilistId}/` : 'https://releases.moe/about/', color: '#00D9FF' }
66        ];
67
68        buttons.forEach(btn => {
69            const button = document.createElement('button');
70            button.textContent = btn.name;
71            button.title = `Open on ${btn.name}`;
72            button.style.cssText = `
73                padding: 6px 12px;
74                background: ${btn.color};
75                color: white;
76                border: none;
77                border-radius: 4px;
78                cursor: pointer;
79                font-size: 13px;
80                font-weight: 500;
81                transition: opacity 0.2s;
82            `;
83            
84            button.addEventListener('mouseenter', () => {
85                button.style.opacity = '0.8';
86            });
87            
88            button.addEventListener('mouseleave', () => {
89                button.style.opacity = '1';
90            });
91            
92            button.addEventListener('click', (e) => {
93                e.preventDefault();
94                window.open(btn.url, '_blank');
95            });
96            
97            container.appendChild(button);
98        });
99
100        // Add separator before the container
101        const separator = document.createElement('div');
102        separator.className = 'PageToolbarSeparator-separator-N1WHF';
103        
104        // Insert at the end of the toolbar
105        toolbar.appendChild(separator);
106        toolbar.appendChild(container);
107
108        console.log('Anime database links added successfully to toolbar');
109    }
110
111    // Function to get AniList ID from anime title
112    async function getAniListId(title) {
113        const query = `
114            query ($search: String) {
115                Media(search: $search, type: ANIME) {
116                    id
117                }
118            }
119        `;
120
121        try {
122            const response = await fetch('https://graphql.anilist.co', {
123                method: 'POST',
124                headers: {
125                    'Content-Type': 'application/json',
126                    'Accept': 'application/json',
127                },
128                body: JSON.stringify({
129                    query: query,
130                    variables: { search: title }
131                })
132            });
133
134            const data = await response.json();
135            return data.data?.Media?.id || null;
136        } catch (error) {
137            console.error('Error fetching AniList ID:', error);
138            return null;
139        }
140    }
141
142    // Debounced version of addAnimeLinks
143    function debouncedAddAnimeLinks() {
144        clearTimeout(debounceTimer);
145        debounceTimer = setTimeout(() => {
146            addAnimeLinks();
147        }, 300);
148    }
149
150    // Function to initialize
151    function init() {
152        console.log('Initializing anime database links extension');
153        
154        // Try to add the links immediately
155        addAnimeLinks();
156
157        // Watch for navigation changes (Sonarr is a SPA)
158        const observer = new MutationObserver(function() {
159            debouncedAddAnimeLinks();
160        });
161
162        // Observe the main content area for changes
163        const contentArea = document.querySelector('.PageContent-content-C67P9') || document.body;
164        observer.observe(contentArea, {
165            childList: true,
166            subtree: true
167        });
168
169        console.log('Observer set up for dynamic content');
170    }
171
172    // Wait for the page to be ready
173    if (document.readyState === 'loading') {
174        document.addEventListener('DOMContentLoaded', init);
175    } else {
176        init();
177    }
178})();
Sonarr Anime Database Links | Robomonkey