YYDS漫画

https://www.yydsmh.com

zpccool (13551) 1天前 下载:445

漫画 漫画 国漫 韩漫 日漫 免费漫画
YYDS漫画(yydsmh.com),免费在线漫画平台,提供国漫、韩漫、日漫等多种类型漫画。
二维码导入(APP尚未完成该功能)
// @name        YYDS漫画
// @uuid       yydsmanhua
// @version     1.0.1
// @author      Ai
// @url         https://www.yydsmh.com
// @logo        https://www.yydsmh.com/favicon.ico
// @type        comic
// @enabled false
// @tags        漫画,国漫,韩漫,日漫,免费漫画
// @description YYDS漫画(yydsmh.com),免费在线漫画平台,提供国漫、韩漫、日漫等多种类型漫画。

// ─── 内置测试 ────────────────────────────────────────────────────
async function TEST(type) {
  if (type === '__list__') return ['search', 'explore', 'bookInfo', 'chapterList', 'chapterContent'];

  if (type === 'search') {
    var results = await search('斗破苍穹', 1);
    if (!results || results.length < 1) return { passed: false, message: '搜索结果为空' };
    var found = false;
    for (var i = 0; i < results.length; i++) {
      if (results[i].name && results[i].name.indexOf('斗破苍穹') !== -1) { found = true; break; }
    }
    if (!found) return { passed: false, message: '搜索结果中未找到"斗破苍穹"' };
    return { passed: true, message: '搜索"斗破苍穹"返回 ' + results.length + ' 条结果 ✓' };
  }

  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 === 'bookInfo') {
    var r = await bookInfo('https://www.yydsmh.com/manga/38458/');
    return { passed: !!r.name, message: 'bookInfo name=' + r.name };
  }

  if (type === 'chapterList') {
    var r = await chapterList('https://www.yydsmh.com/manga/38458/');
    return { passed: r.length > 0, message: 'chapterList cnt=' + r.length + ' first=' + (r[0] ? r[0].name : 'N/A') };
  }

  if (type === 'chapterContent') {
    var r = await chapterContent('https://www.yydsmh.com/episode/38458/1090683.html');
    return { passed: r.length > 10, message: 'chapterContent len=' + r.length + ' (图片URL列表)' };
  }

  return { passed: false, message: '未知测试类型: ' + type };
}

// ─── 配置 ────────────────────────────────────────────────────────
var BASE = 'https://www.yydsmh.com';
var API_SEARCH = '/api/front/index/search';

// 发现页分类:使用排行页和更新页(服务端渲染,无需 API)
var EXPLORE_CATEGORIES = {
  '排行榜': '/toplist/alldj.html',
  '最新更新': '/manga-update/4.html'
};

// ─── 工具 ────────────────────────────────────────────────────────
function toAbs(href) {
  if (!href) return '';
  if (href.indexOf('http') === 0) return href;
  return BASE + (href.charAt(0) === '/' ? href : '/' + href);
}

/**
 * 处理反爬验证:检测响应中的 ge_js_validator cookie 设置脚本,
 * 若包含则提取 cookie 并通过手动设置 header 重试原始请求。
 * @param {string} resp - HTTP 响应体
 * @param {string} url - 原始请求 URL
 * @param {object} [headers] - 额外 headers
 * @returns {string} 最终响应体
 */
async function handleAntiCrawl(resp, url, headers) {
  if (!resp || resp.indexOf('ge_js_validator') === -1) return resp;
  var m = resp.match(/document\.cookie\s*=\s*"([^"]+)"/);
  if (!m) return resp;
  var cookie = m[1].split(';')[0];
  legado.log('[antiCrawl] cookie=' + cookie);
  var h = headers || {};
  h['Cookie'] = cookie;
  return await legado.http.get(url, h);
}

/**
 * 调用搜索 API:POST /api/front/index/search,body = key=<keyword>
 * @param {string} keyword
 * @returns {Array} 书籍列表
 */
async function apiSearch(keyword) {
  var body = 'key=' + encodeURIComponent(keyword);
  var headers = {
    'Content-Type': 'application/x-www-form-urlencoded',
    'X-Requested-With': 'XMLHttpRequest',
    'Referer': BASE + '/'
  };
  var resp = await legado.http.post(BASE + API_SEARCH, body, headers);
  var respStr = '' + resp;
  legado.log('[apiSearch] resp length=' + respStr.length);

  // 反爬检测(POST 也可能触发)
  if (respStr.indexOf('ge_js_validator') !== -1) {
    var m = respStr.match(/document\.cookie\s*=\s*"([^"]+)"/);
    if (m) {
      headers['Cookie'] = m[1].split(';')[0];
      resp = await legado.http.post(BASE + API_SEARCH, body, headers);
      respStr = '' + resp;
    }
  }

  if (!respStr || respStr.length === 0) return [];

  var obj = JSON.parse(respStr);
  if (!obj || obj.code !== 0) return [];
  var data = obj.data || [];
  var books = [];
  for (var i = 0; i < data.length; i++) {
    var item = data[i];
    books.push({
      name: item.name || '',
      author: (item.author || '').replace(/\\/g, ''),
      bookUrl: toAbs(item.info_url || ('/manga/' + item.id + '/')),
      coverUrl: item.cover || '',
      kind: (item.tags && item.tags.length > 0) ? item.tags.join(',') : '漫画',
      intro: item.content || item.intro || ''
    });
  }
  return books;
}

/**
 * 解析排行/更新页 HTML 中的漫画卡片(li.acgn-item)
 * 排行页结构:li.acgn-item > a.acgn-thumbnail(href, img.cover) + div.acgn-info > h3.comic-name > a
 * 更新页结构:li.acgn-item > div.__left > a.acgn-thumbnail(href, img.cover) + div.__right > div.acgn-info
 * @param {string} html
 * @returns {Array}
 */
function parseListHtml(html) {
  var doc = legado.dom.parse(html);
  var items = legado.dom.selectAll(doc, 'li.acgn-item');
  var books = [];
  var seenUrls = {};

  for (var i = 0; i < items.length; i++) {
    var el = items[i];
    var a = legado.dom.select(el, 'a.acgn-thumbnail');
    if (!a) continue;
    var href = legado.dom.attr(a, 'href') || '';
    if (!href || href.indexOf('/manga/') === -1) continue;

    var url = toAbs(href);
    if (seenUrls[url]) continue;
    seenUrls[url] = true;

    var img = legado.dom.select(el, 'img.cover');
    var coverUrl = img ? (legado.dom.attr(img, 'src') || '') : '';

    // 标题:从 h3 或 a 的 title 中取
    var titleEl = legado.dom.select(el, 'h3.comic-name a') || legado.dom.select(el, 'h3 a');
    var name = '';
    if (titleEl) {
      name = (legado.dom.text(titleEl) || '').trim();
      // 排行页标题前有 "NO.1" 等序号,去掉
      name = name.replace(/^NO\.\d+\s*/i, '');
    }
    if (!name) {
      // fallback:从 a.acgn-thumbnail 的 title 取
      var titleStr = legado.dom.attr(a, 'title') || '';
      name = titleStr.split(',')[0].trim();
    }
    if (!name) continue;

    books.push({
      name: name,
      author: '',
      bookUrl: url,
      coverUrl: coverUrl.trim(),
      kind: '漫画',
      intro: ''
    });
  }
  legado.dom.free(doc);
  return books;
}

// ─── 搜索 ────────────────────────────────────────────────────────
async function search(keyword, page) {
  legado.log('[search] keyword=' + keyword + ' page=' + page);
  // 搜索 API 不支持分页,只返回一页结果
  if (page > 1) return [];
  var books = await apiSearch(keyword);
  legado.log('[search] found=' + books.length);
  return books;
}

// ─── 书籍详情 ────────────────────────────────────────────────────
async function bookInfo(bookUrl) {
  legado.log('[bookInfo] url=' + bookUrl);

  var html = await legado.http.get(bookUrl);
  var doc = legado.dom.parse(html);

  // 从 OG meta 提取
  var name = legado.dom.selectAttr(doc, 'meta[property="og:novel:book_name"]', 'content')
          || legado.dom.selectText(doc, '#js_comic-title')
          || '';
  var author = legado.dom.selectAttr(doc, 'meta[property="og:novel:author"]', 'content')
            || legado.dom.selectText(doc, '.author')
            || '';
  var coverUrl = legado.dom.selectAttr(doc, 'meta[property="og:image"]', 'content') || '';
  var intro = legado.dom.selectAttr(doc, 'meta[property="og:description"]', 'content')
           || legado.dom.selectText(doc, '#js_desc_content')
           || '';
  var kind = legado.dom.selectAttr(doc, 'meta[property="og:novel:category"]', 'content') || '漫画';
  var status = legado.dom.selectAttr(doc, 'meta[property="og:novel:status"]', 'content') || '';
  var lastChapter = legado.dom.selectAttr(doc, 'meta[property="og:novel:latest_chapter_name"]', 'content') || '';

  // 清理
  name = name.replace(/~$/, '').trim();
  author = author.replace(/\\/g, '').trim();
  kind = kind.replace(/,$/, '').trim();

  legado.dom.free(doc);
  legado.log('[bookInfo] name=' + name + ' author=' + author);

  return {
    name: name,
    author: author,
    coverUrl: coverUrl,
    intro: intro,
    kind: kind,
    status: status,
    lastChapter: lastChapter,
    tocUrl: bookUrl
  };
}

// ─── 章节列表 ────────────────────────────────────────────────────
async function chapterList(tocUrl) {
  legado.log('[chapterList] url=' + tocUrl);

  var html = await legado.http.get(tocUrl);

  // 反爬处理
  var htmlStr = '' + html;
  if (htmlStr.indexOf('ge_js_validator') !== -1) {
    html = await handleAntiCrawl(htmlStr, tocUrl);
  }

  var doc = legado.dom.parse(html);

  // 章节在 #detail-chapter 区域,链接格式 /episode/{id}/{chapterId}.html
  // 选择器:.chapter-list .item a(排除 header 里的"开始阅读"和"最新章节"链接)
  var links = legado.dom.selectAll(doc, '#detail-chapter .chapter-list .item a');
  legado.log('[chapterList] raw links=' + links.length);

  var chapters = [];
  var seenUrls = {};

  for (var i = 0; i < links.length; i++) {
    var a = links[i];
    var name = (legado.dom.text(a) || '').trim();
    var href = legado.dom.attr(a, 'href') || '';

    if (!href || href.indexOf('javascript') !== -1) continue;
    var url = toAbs(href);

    if (!name) continue;
    if (seenUrls[url]) continue;
    seenUrls[url] = true;

    chapters.push({ name: name, url: url });
  }

  legado.dom.free(doc);
  legado.log('[chapterList] total=' + chapters.length);
  return chapters;
}

// ─── 正文 ────────────────────────────────────────────────────────
async function chapterContent(chapterUrl) {
  legado.log('[content] url=' + chapterUrl);

  var html = await legado.http.get(chapterUrl);

  // 反爬处理
  var htmlStr = '' + html;
  if (htmlStr.indexOf('ge_js_validator') !== -1) {
    html = await handleAntiCrawl(htmlStr, chapterUrl);
  }

  var doc = legado.dom.parse(html);

  // 图片在 #imgsec figure.item img 中,使用 data-src
  var imgEls = legado.dom.selectAll(doc, '#imgsec img');
  if (!imgEls || imgEls.length === 0) {
    imgEls = legado.dom.selectAll(doc, '.acgn-reader-chapter__item-box img');
  }

  var urls = [];
  for (var i = 0; i < imgEls.length; i++) {
    var src = legado.dom.attr(imgEls[i], 'data-src')
           || legado.dom.attr(imgEls[i], 'src')
           || '';
    src = src.trim();
    if (src && src.indexOf('http') === 0 && src.indexOf('load.gif') === -1) {
      urls.push(src);
    }
  }

  legado.dom.free(doc);
  legado.log('[content] images=' + urls.length);
  return JSON.stringify(urls);
}

// ─── 发现页 ────────────────────────────────────────────────────
async function explore(page, category) {
  if (!page || page < 1) page = 1;
  // 排行/更新页无分页,仅返回第一页
  if (page > 1) return [];

  // 未匹配分类 → 返回分类名列表
  var path = null;
  if (category) {
    for (var key in EXPLORE_CATEGORIES) {
      if (EXPLORE_CATEGORIES.hasOwnProperty(key) && key === category) {
        path = EXPLORE_CATEGORIES[key];
        break;
      }
    }
  }

  if (!path) {
    legado.log('[explore] returning category list');
    var cats = [];
    for (var k in EXPLORE_CATEGORIES) {
      if (EXPLORE_CATEGORIES.hasOwnProperty(k)) cats.push(k);
    }
    return cats;
  }

  legado.log('[explore] category=' + category + ' url=' + path);

  var url = BASE + path;
  var html = await legado.http.get(url);
  html = '' + html;

  // 反爬处理
  if (html.indexOf('ge_js_validator') !== -1) {
    html = await handleAntiCrawl(html, url);
    html = '' + html;
  }

  if (!html || html.length === 0) return [];
  var books = parseListHtml(html);
  legado.log('[explore] found=' + books.length);
  return books;
}
广告