uaa.
https://www.uaa.com/
zpccool (13551) 1天前 下载:504
小说
完整书源:支持热门小说、热辣视频、热门禁漫及所有排行榜,使用浏览器探测绕过 Cloudflare Shield
// @name uaa.
// @uuid uaa.
// @version 0.4.0
// @author Ai
// @url https://www.uaa.com/
// @type novel
// @enabled true
// @description 完整书源:支持热门小说、热辣视频、热门禁漫及所有排行榜,使用浏览器探测绕过 Cloudflare Shield
var BASE = 'https://www.uaa.com';
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// init:初始化时创建浏览器会话
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
function init() {
legado.log('[init] 初始化浏览器会话...');
legado.log('[init] 浏览器会话初始化完成');
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 探测辅助函数:通过浏览器获取页面内容
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
async function _fetchPageViaProxy(url, jscode) {
legado.log('[_fetchPageViaProxy] url=' + url);
try {
var result = await legado.browser.run(url, jscode || 'return document.documentElement.innerHTML;', {
visible: true,
waitUntil: 'load',
timeoutSecs: 60
});
return result;
} catch (e) {
legado.log('[_fetchPageViaProxy] 错误: ' + e.message);
throw e;
}
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 发现页:GETALL 返回所有分类
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
async function explore(page, category) {
legado.log('[explore] category=' + category + ' page=' + page);
if (category === 'GETALL') {
// 返回我们定义的标准分类,而不是动态提取的全部分类
return [
'热门小说', '热辣视频', '热门禁漫',
'新增小说', '完结小说', '最新小说', '连载小说', '推荐小说'
];
}
// 获取指定分类的内容列表
try {
// 构建分类 URL 映射
var categoryMap = {
// 小说分类
'热门小说': '/novel',
'新增小说': '/novel?sort=new',
'完结小说': '/novel?status=finish',
'最新小说': '/novel?sort=latest',
'连载小说': '/novel?status=serial',
'推荐小说': '/novel?sort=recommend',
// 视频分类
'热辣视频': '/video',
'视频推荐': '/video?sort=recommend',
'视频热门': '/video?sort=hot',
// 漫画分类
'热门禁漫': '/comic',
'漫画推荐': '/comic?sort=recommend',
'漫画热门': '/comic?sort=hot',
// 有声分类
'有声推荐': '/audio?sort=recommend',
'有声热门': '/audio?sort=hot'
};
var categoryUrl = categoryMap[category];
// 如果分类未定义,尝试通用格式
if (!categoryUrl) {
if (category.includes('视频') || category.includes('video')) {
categoryUrl = '/video';
} else if (category.includes('漫画') || category.includes('comic')) {
categoryUrl = '/comic';
} else if (category.includes('有声') || category.includes('audio')) {
categoryUrl = '/audio';
} else {
categoryUrl = '/novel';
}
}
// 添加分页参数
if (page && page > 1) {
categoryUrl += (categoryUrl.indexOf('?') > -1 ? '&' : '?') + 'page=' + page;
}
var fullUrl = BASE + categoryUrl;
legado.log('[explore] 加载分类 "' + category + '" 第 ' + page + ' 页 (url=' + fullUrl + ')');
// 为视频页面使用较短的等待时间,避免超时
var waitTime = (categoryUrl.includes('/video') ? 5000 : 20000);
var bookResult = await _fetchPageViaProxy(fullUrl, `
await new Promise(r => setTimeout(r, ${waitTime}));
// 第一阶段:直接寻找内容链接(最快)
var items = [];
// 优先使用内容链接
items = Array.from(document.querySelectorAll('a[href*="/intro"], a[href*="/video/intro"], a[href*="/comic/"]'));
// 如果没有找到足够的内容,降级到容器搜索(只搜一个主容器)
if (items.length < 10) {
var mainContainer = document.querySelector('main, [role="main"], [class*="content"]');
if (mainContainer) {
items = Array.from(mainContainer.querySelectorAll('a[href*="/"]')).filter(function(a) {
return a.href.length > 20 && (a.href.includes('/novel') || a.href.includes('/video') || a.href.includes('/comic'));
});
}
}
var debugInfo = {
itemsFound: items.length,
pageTitle: document.title,
readyState: document.readyState,
bodyLength: document.body.innerText.length,
htmlSnippet: document.documentElement.innerHTML.substring(0, 300)
};
var books = items.slice(0, 100).map(function(el) {
var linkEl = null;
// 优先使用链接元素本身
if (el.tagName === 'A' && el.href) {
linkEl = el;
}
// 否则在元素内查找链接
if (!linkEl) {
linkEl = el.querySelector('a');
}
// 获取标题 - 使用轻量级属性而非 innerText
var name = '';
var titleEl = el.querySelector('h3, h4, .title, [class*="name"], .book-name, .video-name');
if (titleEl) {
// 避免计算 innerText,使用 textContent 或 title 属性
name = (titleEl.textContent || titleEl.title || '').trim();
}
// 如果还是没有标题,尝试从链接元素的 title 或 alt 获取
if (!name && linkEl) {
name = (linkEl.title || linkEl.alt || linkEl.textContent || '').trim();
}
// 如果都没有,从元素的属性获取
if (!name) {
name = (el.getAttribute('title') || el.getAttribute('alt') || '').trim();
}
// 清理名称中的多余文字
if (name.length > 100) {
name = name.substring(0, 100);
}
var authorEl = el.querySelector('[class*="author"], [class*="writer"], .author, [class*="director"]');
var coverEl = el.querySelector('img');
return {
name: name || '未知',
author: (authorEl ? (authorEl.textContent || '').trim() : ''),
bookUrl: linkEl && linkEl.href ? linkEl.href : '',
coverUrl: coverEl && coverEl.src ? coverEl.src : (coverEl && coverEl.getAttribute('src') ? coverEl.getAttribute('src') : ''),
kind: '${category}'
};
}).filter(function(b) {
// 快速过滤:先检查 URL 有效性(避免多次字符串操作)
if (!b.bookUrl || b.bookUrl.length < 15) return false;
// 检查是否包含导航链接
if (b.bookUrl.includes('/list') || b.bookUrl.includes('/rank') ||
b.bookUrl.includes('/ai/') || b.name === '未知' || b.name.length < 2) {
return false;
}
// 对于每个类别,快速检查 URL 类型
var isSafeUrl = false;
if ('${category}'.includes('视频')) {
isSafeUrl = b.bookUrl.includes('/video/intro') || b.bookUrl.includes('/video/') && !b.bookUrl.includes('video/list');
} else if ('${category}'.includes('漫画')) {
isSafeUrl = b.bookUrl.includes('/comic/');
} else {
isSafeUrl = b.bookUrl.includes('/intro') || b.bookUrl.includes('/novel/');
}
return isSafeUrl;
});
return {
books: books.slice(0, 50),
debug: debugInfo
};
`);
var books = Array.isArray(bookResult) ? bookResult : (bookResult && bookResult.books ? bookResult.books : []);
if (bookResult && bookResult.debug) {
legado.log('[explore] DEBUG: ' + JSON.stringify(bookResult.debug));
}
legado.log('[explore] 分类 "' + category + '" 页 ' + page + ' 获得 ' + books.length + ' 个内容');
return books || [];
} catch (e) {
legado.log('[explore] 获取分类 "' + category + '" 失败: ' + e.message);
return [];
}
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 书籍/内容详情:必须返回 tocUrl
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
async function bookInfo(bookUrl) {
legado.log('[bookInfo] url=' + bookUrl);
if (!bookUrl) {
return { name: '', author: '', tocUrl: '', coverUrl: '' };
}
try {
var infoResult = await _fetchPageViaProxy(bookUrl, `
await new Promise(r => setTimeout(r, 3000));
var nameEl = document.querySelector('h1, h2[class*="title"], [class*="novel-name"]');
var authorEl = document.querySelector('[class*="author"], [class*="writer"]');
var introEl = document.querySelector('[class*="intro"], [class*="description"]');
var coverEl = document.querySelector('img[class*="cover"], img[alt*="cover"]');
// 使用轻量级属性访问,避免 innerText
var name = '';
if (nameEl) {
name = (nameEl.textContent || nameEl.title || '').trim();
}
if (!name) {
name = document.title.split('|')[0] || '';
}
var author = authorEl ? (authorEl.textContent || '').trim() : '';
var intro = '';
if (introEl) {
intro = (introEl.textContent || '').trim().substring(0, 500);
}
var coverUrl = coverEl && coverEl.src ? coverEl.src : '';
var tocLink = document.querySelector('a[href*="/chapter"], a[href*="/list"], button[class*="chapter"]');
var tocUrl = tocLink ? tocLink.href : bookUrl;
if (tocUrl && !tocUrl.startsWith('http')) {
tocUrl = window.location.origin + tocUrl;
}
return {
name: name,
author: author,
intro: intro,
coverUrl: coverUrl,
tocUrl: tocUrl || bookUrl
};
`);
legado.log('[bookInfo] 获取成功: ' + (infoResult ? infoResult.name : ''));
return infoResult || { name: '', author: '', tocUrl: bookUrl, coverUrl: '' };
} catch (e) {
legado.log('[bookInfo] 获取失败: ' + e.message);
return { name: '', author: '', tocUrl: bookUrl, coverUrl: '' };
}
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 章节列表
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
async function chapterList(tocUrl) {
legado.log('[chapterList] url=' + tocUrl);
try {
var chaptersResult = await _fetchPageViaProxy(tocUrl, `
await new Promise(r => setTimeout(r, 3000));
var links = Array.from(document.querySelectorAll('a[href*="chapter"], a[href*="read"], a[href*="intro"], li a, [class*="chapter"] a')).slice(0, 600);
return links
.filter(function(el) { return el.href && el.href.length > 10; })
.map(function(el) {
// 使用轻量级属性,避免 innerText
var text = (el.textContent || el.title || el.alt || '').trim();
return {
name: text.substring(0, 100) || '未命名',
url: el.href
};
})
.slice(0, 500);
`);
legado.log('[chapterList] 获得 ' + (chaptersResult ? chaptersResult.length : 0) + ' 章');
return chaptersResult || [];
} catch (e) {
legado.log('[chapterList] 失败: ' + e.message);
return [];
}
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 章节正文
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
async function chapterContent(chapterUrl) {
legado.log('[chapterContent] url=' + chapterUrl);
try {
var contentResult = await _fetchPageViaProxy(chapterUrl, `
await new Promise(r => setTimeout(r, 3000));
// 优先从最具体的容器获取内容
var contentEl = document.querySelector('[class*="content"], [class*="text"], [class*="article"], main, article, .chapter-content, .post-content');
// 仅当确实需要 innerText 时才调用一次
var text = '';
if (contentEl && contentEl.textContent) {
text = contentEl.textContent;
} else if (document.body && document.body.textContent) {
// 降级:只在必要时获取整个页面文本
text = document.body.textContent;
}
// 快速清理文本(避免过度处理)
var cleanedText = text
.split('\\n')
.slice(0, 5000) // 限制行数避免过大
.map(function(line) { return line.trim(); })
.filter(function(line) {
return line.length > 0 && line.length < 500 &&
!line.includes('广告') && !line.includes('脚本') &&
!line.includes('推广') && !line.includes('赞助');
})
.join('\\n');
return cleanedText;
`);
legado.log('[chapterContent] 获得内容长度 ' + (contentResult ? contentResult.length : 0) + ' 字');
return contentResult || '';
} catch (e) {
legado.log('[chapterContent] 获取失败: ' + e.message);
return '';
}
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 搜索
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
async function search(keyword, page) {
legado.log('[search] keyword=' + keyword + ' page=' + page);
try {
var searchUrl = BASE + '/search?keyword=' + encodeURIComponent(keyword);
if (page && page > 1) {
searchUrl += '&page=' + page;
}
var searchResult = await _fetchPageViaProxy(searchUrl, `
await new Promise(r => setTimeout(r, 5000));
var items = Array.from(document.querySelectorAll('a[href*="/intro"], a[href*="/novel/"], a[href*="/video/"], a[href*="/comic/"]')).slice(0, 100);
if (items.length < 5) {
var container = document.querySelector('[class*="list"], [class*="content"], main, article');
if (container) {
items = Array.from(container.querySelectorAll('a')).filter(function(a) {
return a.href.length > 10 && (a.href.includes('/intro') || a.href.includes('/novel') || a.href.includes('/video') || a.href.includes('/comic'));
});
}
}
var books = items.map(function(el) {
var linkEl = el.tagName === 'A' ? el : el.querySelector('a');
// 使用轻量级属性获取标题
var name = (el.textContent || el.title || el.alt || '').trim();
if (name.length > 100) name = name.substring(0, 100);
var authorEl = el.querySelector('[class*="author"], span[class*="writer"]');
var author = authorEl ? (authorEl.textContent || '').trim() : '';
return {
name: name || '未知',
author: author,
bookUrl: linkEl ? linkEl.href : '',
coverUrl: ''
};
}).filter(function(b) {
return b.name !== '未知' && b.bookUrl && b.bookUrl.length > 10;
});
return books.slice(0, 50);
`);
legado.log('[search] 搜索 "' + keyword + '" 第 ' + page + ' 页获得 ' + (searchResult ? searchResult.length : 0) + ' 个结果');
return searchResult || [];
} catch (e) {
legado.log('[search] 搜索失败: ' + e.message);
return [];
}
}