您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Export HiAnime.to watchlist with all metadata available on watchlist cards, no individual page fetches
// ==UserScript== // @name HiAnime Watchlist Exporter (lite) fast // @author ScriptKiddyMonkey // @license MIT // @version 1-lite // @description Export HiAnime.to watchlist with all metadata available on watchlist cards, no individual page fetches // @match https://hianime.to/user/watch-list* // @grant none // @namespace ScriptKiddyMonkey // ==/UserScript== (function () { 'use strict'; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); const waitForSelector = async (selector) => { while (!document.querySelector(selector)) await sleep(200); return document.querySelector(selector); }; const getCategoryFromElement = (el) => { const added = el.querySelector('.wl-item.added'); if (!added) return 'Unknown'; const map = { '1': 'Watching', '2': 'On-Hold', '3': 'Plan to Watch', '4': 'Dropped', '5': 'Completed', }; return map[added.getAttribute('data-type')] || 'Unknown'; }; const extractTicks = (card) => { const tickContainer = card.querySelector('.tick.ltr'); if (!tickContainer) return {}; const ticks = {}; tickContainer.querySelectorAll('.tick-item').forEach((item) => { const text = item.textContent.trim(); if (item.classList.contains('tick-sub')) { ticks.subbedEpisodes = parseInt(text) || null; } else if (item.classList.contains('tick-dub')) { ticks.dubbedEpisodes = parseInt(text) || null; } else if (item.classList.contains('tick-eps')) { ticks.totalEpisodes = parseInt(text) || null; } }); return ticks; }; const scrapeEntireWatchlist = async () => { let page = 1; let animes = []; while (true) { const url = `${location.origin}${location.pathname}?page=${page}`; console.log(`Scraping page ${page}: ${url}`); const res = await fetch(url); const html = await res.text(); const doc = new DOMParser().parseFromString(html, 'text/html'); const cards = doc.querySelectorAll('.film_list-wrap .flw-item'); if (!cards.length) break; for (const card of cards) { const a = card.querySelector('a.film-poster-ahref'); const href = a?.href || null; const title = a?.getAttribute('oldtitle') || a?.title || null; const id = href ? href.split('/').pop() : null; const img = card.querySelector('img.film-poster-img'); const poster = img?.getAttribute('data-src') || img?.src || null; const category = getCategoryFromElement(card); const ticks = extractTicks(card); // Media type and duration let mediaType = null; let duration = null; const fdInfo = card.querySelector('.film-detail .fd-infor'); if (fdInfo) { const mediaTypeSpan = fdInfo.querySelector('span.fdi-item'); if (mediaTypeSpan) mediaType = mediaTypeSpan.textContent.trim(); const durationSpan = fdInfo.querySelector('span.fdi-duration'); if (durationSpan) duration = durationSpan.textContent.trim(); } animes.push({ id, title, url: href, category, poster, subbedEpisodes: ticks.subbedEpisodes || null, dubbedEpisodes: ticks.dubbedEpisodes || null, totalEpisodes: ticks.totalEpisodes || null, mediaType: mediaType || null, duration: duration || null, }); } page++; await sleep(800); } return animes; }; const exportWatchlist = async (btn) => { btn.disabled = true; btn.textContent = 'Gathering watchlist...'; try { const baseList = await scrapeEntireWatchlist(); const blob = new Blob([JSON.stringify(baseList, null, 2)], { type: 'application/json', }); const username = location.pathname.split('/')[2] || 'user'; const date = new Date().toISOString().split('T')[0]; const filename = `HiAnime_Watchlist_lite_${date}.json`; const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); a.remove(); URL.revokeObjectURL(url); btn.textContent = 'Export Complete!'; } catch (e) { console.error('Export failed:', e); btn.textContent = 'Export Failed. Check console.'; } }; const addExportButton = () => { const btn = document.createElement('button'); btn.textContent = 'Export Watchlist [lite]'; btn.style = 'position:fixed;bottom:20px;right:20px;padding:10px 20px;background:#201f2d;color:#fff;border:none;border-radius:8px;z-index:99999;font-weight:bold;box-shadow:0 4px 12px rgba(0,0,0,0.3);cursor:pointer'; btn.onclick = () => exportWatchlist(btn); document.body.appendChild(btn); }; waitForSelector('.film_list-wrap').then(addExportButton); })();