275听书网
https://m.i275.com
zpccool (13551) 5小时前 下载:174
音频 有声小说 听书 免费
275听书网,有声小说免费听
// @name 275听书网
// @uuid 275tingshuwang
// @version 1.0.0
// @author Ai
// @url https://m.i275.com
// @type music
// @enabled true
// @tags 有声小说,听书,免费
// @description 275听书网,有声小说免费听
var BASE = 'https://m.i275.com';
var UA = 'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36';
function makeHeaders(referer) {
var h = {
'User-Agent': UA,
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Origin': BASE
};
h.Referer = referer || BASE + '/';
return h;
}
function toAbs(url) {
if (!url) return '';
if (url.indexOf('//') === 0) return 'https:' + url;
if (url.indexOf('http://') === 0 || url.indexOf('https://') === 0) return url;
if (url.charAt(0) === '/') return BASE + url;
return BASE + '/' + url;
}
function cleanText(text) {
if (!text) return '';
return String(text).replace(/\s+/g, ' ').trim();
}
function cleanAudioUrl(url) {
if (!url) return '';
url = String(url);
url = url.replace(/\\\//g, '/');
url = url.replace(/\\u0026/g, '&').replace(/\\x26/g, '&');
url = url.replace(/&/g, '&').replace(/&/g, '&').trim();
return toAbs(url);
}
function extractBookName(item) {
var name = legado.dom.selectText(item, 'h3');
if (!name) name = legado.dom.selectText(item, '.font-medium.text-sm');
if (!name) name = legado.dom.selectText(item, '.font-medium');
if (!name) name = legado.dom.selectAttr(item, 'img', 'alt');
return cleanText(name);
}
function extractBookList(doc, selector) {
var items = legado.dom.selectAll(doc, selector);
var books = [];
var seen = {};
legado.log('[extractBookList] selector=' + selector + ' found=' + items.length);
for (var i = 0; i < items.length; i++) {
var item = items[i];
var bookUrl = legado.dom.attr(item, 'href') || legado.dom.selectAttr(item, 'a[href^="/book/"]', 'href') || '';
var name = extractBookName(item);
var coverUrl = legado.dom.selectAttr(item, 'img', 'src') || '';
var intro = cleanText(legado.dom.selectText(item, '.line-clamp-2'));
if (!name || !bookUrl || seen[bookUrl]) continue;
seen[bookUrl] = true;
books.push({
name: name,
author: 'Ai',
bookUrl: bookUrl,
coverUrl: toAbs(coverUrl),
intro: intro,
kind: '有声小说'
});
}
legado.log('[extractBookList] total=' + books.length);
return books;
}
function parseInfoText(doc, label) {
var texts = legado.dom.selectAllTexts(doc, '.mt-2 p');
for (var i = 0; i < texts.length; i++) {
var text = cleanText(texts[i]);
if (text.indexOf(label + ':') === 0) {
return cleanText(text.substring(label.length + 1));
}
}
return '';
}
function guessBookReferer(chapterUrl) {
var m = String(chapterUrl).match(/\/play\/(\d+)\//);
if (m && m[1]) return BASE + '/book/' + m[1] + '.html';
return BASE + '/';
}
function selectChapterItems(doc) {
var items = legado.dom.selectAll(doc, 'a[id^="chapter-pos-"][href^="/play/"]');
if (!items.length) items = legado.dom.selectAll(doc, '.grid a[href^="/play/"]');
if (!items.length) items = legado.dom.selectAll(doc, 'a[href^="/play/"]');
return items;
}
function makeAudioResult(audioUrl, chapterUrl) {
return JSON.stringify({
url: cleanAudioUrl(audioUrl),
type: 'audio',
referer: chapterUrl,
headers: {
'User-Agent': UA,
'Referer': chapterUrl,
'Origin': BASE
}
});
}
function extractAudioFromHtml(html) {
var patterns = [
/url\s*:\s*["']([^"']+\.(?:mp3|m4a|aac|flac|wav|ogg|opus|ape|wma)(?:\?[^"']*)?)["']/i,
/audio_url\s*[=:]\s*["']([^"']+\.(?:mp3|m4a|aac|flac|wav|ogg|opus|ape|wma)(?:\?[^"']*)?)["']/i,
/file\s*[=:]\s*["']([^"']+\.(?:mp3|m4a|aac|flac|wav|ogg|opus|ape|wma)(?:\?[^"']*)?)["']/i,
/(https?:\/\/[^"'<>\\\s]+\.(?:mp3|m4a|aac|flac|wav|ogg|opus|ape|wma)(?:\?[^"'<>\\\s]*)?)/i
];
for (var i = 0; i < patterns.length; i++) {
var m = html.match(patterns[i]);
if (m && m[1]) return cleanAudioUrl(m[1]);
}
return '';
}
async function search(keyword, page) {
page = page || 1;
legado.log('[search] keyword=' + keyword + ' page=' + page);
var url = BASE + '/search.php?q=' + encodeURIComponent(keyword) + '&page=' + page;
var html = await legado.http.get(url, makeHeaders(BASE + '/'));
var doc = legado.dom.parse(html);
var books = extractBookList(doc, '.divide-y > a[href^="/book/"], .divide-y a[href^="/book/"]');
if (!books.length) {
books = extractBookList(doc, 'a[href^="/book/"]');
}
legado.log('[search] total=' + books.length);
return books;
}
async function bookInfo(bookUrl) {
legado.log('[bookInfo] url=' + bookUrl);
var url = toAbs(bookUrl);
var html = await legado.http.get(url, makeHeaders(BASE + '/'));
var doc = legado.dom.parse(html);
var name = cleanText(legado.dom.selectText(doc, 'h1.text-2xl'));
if (!name) {
var title = cleanText(legado.dom.selectText(doc, 'title'));
name = title.replace(/\s*-\s*.*$/, '');
}
var coverUrl = legado.dom.selectAttr(doc, '.bg-white .w-32 img, .bg-white .w-48 img, img[alt]', 'src') || '';
var intro = cleanText(legado.dom.selectText(doc, '.line-clamp-3'));
var status = parseInfoText(doc, '状态');
var heat = parseInfoText(doc, '热度');
var chapterCount = 0;
var chapterText = cleanText(legado.dom.selectText(doc, '.active-tab'));
var countMatch = chapterText.match(/\((\d+)\)/);
if (countMatch && countMatch[1]) chapterCount = parseInt(countMatch[1], 10);
var chapters = selectChapterItems(doc);
var lastChapter = '';
var latestChapterUrl = '';
if (chapters.length) {
var last = chapters[chapters.length - 1];
lastChapter = cleanText(legado.dom.selectText(last, '.text-sm.text-gray-700')) || cleanText(legado.dom.text(last));
latestChapterUrl = legado.dom.attr(last, 'href') || '';
if (!chapterCount) chapterCount = chapters.length;
}
return {
name: name,
author: 'Ai',
bookUrl: url,
coverUrl: toAbs(coverUrl),
intro: intro,
kind: '有声小说',
lastChapter: lastChapter,
latestChapter: lastChapter,
latestChapterUrl: latestChapterUrl,
chapterCount: chapterCount,
status: status,
wordCount: heat,
tocUrl: url
};
}
async function chapterList(tocUrl) {
legado.log('[chapterList] url=' + tocUrl);
var url = toAbs(tocUrl);
var html = await legado.http.get(url, makeHeaders(BASE + '/'));
var doc = legado.dom.parse(html);
var chapters = [];
var seen = {};
var items = selectChapterItems(doc);
legado.log('[chapterList] found=' + items.length);
for (var i = 0; i < items.length; i++) {
var item = items[i];
var chapterUrl = legado.dom.attr(item, 'href') || '';
var name = cleanText(legado.dom.selectText(item, '.text-sm.text-gray-700')) || cleanText(legado.dom.text(item));
if (!chapterUrl || chapterUrl.indexOf('/play/') === -1 || !name || seen[chapterUrl]) continue;
seen[chapterUrl] = true;
chapters.push({
name: name,
url: chapterUrl
});
}
legado.log('[chapterList] total=' + chapters.length);
return chapters;
}
async function chapterContent(chapterUrl) {
legado.log('[chapterContent] url=' + chapterUrl);
var url = toAbs(chapterUrl);
var referer = guessBookReferer(url);
var html = await legado.http.get(url, makeHeaders(referer));
var audioUrl = extractAudioFromHtml(html);
if (audioUrl) {
legado.log('[chapterContent] audio=' + audioUrl);
return makeAudioResult(audioUrl, url);
}
var doc = legado.dom.parse(html);
var src = legado.dom.selectAttr(doc, 'audio source, audio', 'src');
if (!src) src = legado.dom.selectAttr(doc, 'audio[data-src], audio[data-url]', 'data-src');
if (!src) src = legado.dom.selectAttr(doc, 'audio[data-src], audio[data-url]', 'data-url');
if (src) {
audioUrl = cleanAudioUrl(src);
legado.log('[chapterContent] audio-dom=' + audioUrl);
return makeAudioResult(audioUrl, url);
}
legado.log('[chapterContent] no direct audio found, fallback to play page');
return makeAudioResult(url, url);
}
async function explore(page, category) {
page = page || 1;
category = category || 'GETALL';
legado.log('[explore] page=' + page + ' category=' + category);
if (category === 'GETALL' || category === '') {
return ['首页'];
}
var html = await legado.http.get(BASE + '/', makeHeaders(BASE + '/'));
var doc = legado.dom.parse(html);
var books = extractBookList(doc, '.grid a[href^="/book/"]');
legado.log('[explore] total=' + books.length);
return books;
}
async function TEST(type) {
if (type === '__list__') return ['search', 'explore', 'info', 'toc', 'content'];
if (type === 'explore') {
var books = await explore(1, '首页');
if (!books || books.length < 1) return { passed: false, message: '发现页为空' };
return { passed: true, message: '发现页 ' + books.length + ' 条' };
}
if (type === 'info') {
var info = await bookInfo('/book/39328.html');
if (!info || !info.name) return { passed: false, message: '书籍详情为空' };
return { passed: true, message: '书籍详情: ' + info.name };
}
if (type === 'toc') {
var chapters = await chapterList('/book/39328.html');
if (!chapters || chapters.length < 1) return { passed: false, message: '章节目录为空' };
return { passed: true, message: '章节 ' + chapters.length + ' 条' };
}
if (type === 'content') {
var content = await chapterContent('/play/39328/23406840.html');
if (!content || content.indexOf('.m4a') === -1) return { passed: false, message: '音频地址获取失败' };
return { passed: true, message: '音频地址获取成功' };
}
if (type === 'search') {
var results = await search('凡人修仙传', 1);
if (!results || results.length < 1) return { passed: false, message: '搜索无结果' };
return { passed: true, message: '搜索返回 ' + results.length + ' 条' };
}
return { passed: false, message: '未知测试: ' + type };
}