qrtzcode
HomeDocsAPILeaderboardChangelog
Contribute
Docs

qrtzcode

Kumpulan gist & snippet pribadi — anime, AI tools, scraper, dan randomness.

Navigate

HomeDocsAPI Reference

Links

© 2026 qrtz. All snippets welcome.

ESC

Navigate

Links

Comic

komikindo

scraper komikindo super duper lengkap:p.

Creator

qrtz

Language

javascript

Views

66

Copies

15

Base

https://komikindo.ch

Updated

16 Agu 2026

#comic#manga#manwha#manhua

Code

6615
komikindo.js
const axios = require('axios');
const cheerio = require('cheerio');
const https = require('https');
const BASE_URL = 'https://komikindo.ch';
const USER_AGENTS = [
  'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
  'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
  'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0',
  'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
  'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7; rv:133.0) Gecko/20100101 Firefox/133.0',
  'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
  'Mozilla/5.0 (X11; Linux x86_64; rv:133.0) Gecko/20100101 Firefox/133.0',
  'Mozilla/5.0 (iPhone; CPU iPhone OS 17_6_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.6 Mobile/15E148 Safari/604.1',
  'Mozilla/5.0 (Linux; Android 14; SM-S921B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.104 Mobile Safari/537.36',
  'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.2903.70'
];
function randomIp() {
  return Math.floor(Math.random() * 255) + 1 + '.' + Math.floor(Math.random() * 255) + '.' + Math.floor(Math.random() * 255) + '.' + Math.floor(Math.random() * 255);
}
function randomDelay(min = 500, max = 1500) {
  return new Promise(resolve => setTimeout(resolve, Math.floor(Math.random() * (max - min + 1)) + min));
}
let uaIndex = 0;
function getHeaders(ref) {
  const ua = USER_AGENTS[uaIndex % USER_AGENTS.length];
  uaIndex++;
  const fakeIp = randomIp();
  const isMobile = ua.includes('Mobile') || ua.includes('iPhone') || ua.includes('Android');
  const platform = ua.includes('Windows') ? 'Windows' : ua.includes('Mac') ? 'macOS' : 'Linux';
  const browser = ua.includes('Firefox') ? 'Firefox' : ua.includes('Edg') ? 'Edge' : 'Chrome';
  return {
    'User-Agent': ua,
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
    'Accept-Language': 'id-ID,id;q=0.9,en-US;q=0.8,en;q=0.7',
    'Accept-Encoding': 'gzip, deflate, br',
    'Referer': ref || BASE_URL + '/',
    'Cache-Control': 'no-cache',
    'Pragma': 'no-cache',
    'DNT': '1',
    'Sec-Ch-Ua': browser === 'Chrome' ? '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"' : '"Firefox";v="133", "Not_A Brand";v="24"',
    'Sec-Ch-Ua-Mobile': isMobile ? '?1' : '?0',
    'Sec-Ch-Ua-Platform': `"${platform}"`,
    'Sec-Fetch-Dest': 'document',
    'Sec-Fetch-Mode': 'navigate',
    'Sec-Fetch-Site': 'same-origin',
    'Sec-Fetch-User': '?1',
    'Upgrade-Insecure-Requests': '1',
    'Connection': 'keep-alive',
    'X-Forwarded-For': fakeIp,
    'Client-IP': fakeIp,
    'X-Real-IP': fakeIp
  };
}
async function fetchHTML(url, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      await randomDelay(500, 1500);
      const response = await axios({
        url,
        method: 'GET',
        headers: getHeaders(url),
        timeout: 30000,
        httpsAgent: new https.Agent({ rejectUnauthorized: false, keepAlive: true }),
        maxRedirects: 5,
        decompress: true,
        validateStatus: status => status >= 200 && status < 400
      });
      const html = response.data;
      if (html && html.length > 500 && !html.includes('403 Forbidden') && !html.includes('Access to this resource')) {
        return html;
      }
      if (i < retries - 1) await randomDelay(2000, 4000);
    } catch (e) {
      if (i < retries - 1) await randomDelay(2000, 4000);
      else throw new Error('Gagal fetch: ' + e.message);
    }
  }
  throw new Error('Gagal fetch setelah retry');
}
function cleanText(text) {
  return text ? text.replace(/\s+/g, ' ').trim() : null;
}
function buildResponse(page, url, pagination, data) {
  const result = { creator: 'rynaqrtz', page, url };
  if (pagination && (pagination.total !== null || pagination.current !== 1 || pagination.hasNext)) {
    result.pagination = pagination;
  }
  result.data = data;
  return result;
}
function parsePagination($) {
  const result = { current: 1, total: null, hasNext: false };
  const active = $('.pagination .page-numbers.current');
  if (active.length) {
    const t = active.text().trim();
    if (/^\d+$/.test(t)) result.current = parseInt(t);
  }
  const allPages = $('.pagination .page-numbers:not(.dots):not(.next):not(.prev)');
  let maxPage = 0;
  allPages.each((i, el) => {
    const t = $(el).text().trim();
    if (/^\d+$/.test(t)) {
      const num = parseInt(t);
      if (num > maxPage) maxPage = num;
    }
  });
  if (maxPage > 0) {
    result.total = maxPage;
    result.hasNext = result.current < maxPage;
  }
  const nextLink = $('.pagination .next.page-numbers');
  if (nextLink.length && !result.hasNext) {
    result.hasNext = true;
  }
  return result;
}
function parseComicItem($el) {
  const item = {};
  const title = $el.find('.tt h3').text().trim() || $el.find('.poster-title').text().trim();
  if (title) item.title = title;
  const link = $el.find('a[href^="/komik/"]').first().attr('href') || $el.find('a').first().attr('href');
  if (link) item.url = link.startsWith('http') ? link : BASE_URL + link;
  const poster = $el.find('.limit img').attr('src') || $el.find('.limit img').attr('data-src') || $el.find('img').attr('src') || $el.find('img').attr('data-src');
  if (poster) item.poster = poster;
  const rating = $el.find('.rating i').text().trim() || $el.find('.rtg i').text().trim() || $el.find('.archiveanime-rating i').text().trim();
  if (rating) item.rating = parseFloat(rating);
  const type = $el.find('.typeflag').text().trim() || $el.find('.type-flag').text().trim();
  if (type) item.type = type;
  const format = $el.find('.warnalabel').text().trim();
  if (format) item.format = format;
  const status = $el.find('.status-skroep').text().trim();
  if (status) item.status = status;
  const latestChapter = $el.find('.lsch a').first().attr('href');
  if (latestChapter) item.latestChapter = latestChapter.startsWith('http') ? latestChapter : BASE_URL + latestChapter;
  const latestChapterTitle = $el.find('.lsch a').first().text().trim();
  if (latestChapterTitle) item.latestChapterTitle = latestChapterTitle;
  const date = $el.find('.datech').text().trim();
  if (date) item.date = date;
  return item;
}
function parseComicList($, selector = '.animepost') {
  const items = [];
  $(selector).each((i, el) => {
    const item = parseComicItem($(el));
    if (item.title && item.url) items.push(item);
  });
  if (items.length === 0) {
    $('.film-list .animepost').each((i, el) => {
      const item = parseComicItem($(el));
      if (item.title && item.url) items.push(item);
    });
  }
  if (items.length === 0) {
    $('.listupd .animepost').each((i, el) => {
      const item = parseComicItem($(el));
      if (item.title && item.url) items.push(item);
    });
  }
  return items;
}
function parseDetailInfo($) {
  const info = {};
  $('.spe span').each((i, el) => {
    const text = cleanText($(el).text());
    if (text && text.includes(':')) {
      const parts = text.split(':');
      const key = cleanText(parts[0]);
      const value = cleanText(parts.slice(1).join(':'));
      if (key && value) info[key] = value;
    }
  });
  return info;
}
function parseGenres($) {
  const genres = [];
  $('.genre-info a').each((i, el) => {
    const text = cleanText($(el).text());
    if (text) genres.push(text);
  });
  return genres;
}
class KomikIndoScraper {
  constructor() {
    this.base = BASE_URL;
  }
  async home() {
    const url = this.base + '/';
    const html = await fetchHTML(url);
    const $ = cheerio.load(html);
    const popular = parseComicList($, '.listupd.customslider .animepost');
    const latest = parseComicList($, '.chapterbaru .animepost');
    if (latest.length === 0) {
      const alt = parseComicList($, '.widget-body .animepost');
      if (alt.length > 0) latest.push(...alt);
    }
    const pagination = { current: 1, total: null, hasNext: false };
    return buildResponse('home', url, pagination, { popular, latest });
  }
  async komikTerbaru(page = 1) {
    const url = page === 1 ? this.base + '/komik-terbaru/' : this.base + `/komik-terbaru/page/${page}/`;
    const html = await fetchHTML(url);
    const $ = cheerio.load(html);
    const items = parseComicList($, '.film-list .animepost');
    const pagination = parsePagination($);
    return buildResponse('komik-terbaru', url, pagination, items);
  }
  async komikPopuler(page = 1) {
    const url = page === 1 ? this.base + '/komik-populer/' : this.base + `/komik-populer/page/${page}/`;
    const html = await fetchHTML(url);
    const $ = cheerio.load(html);
    const items = parseComicList($, '.film-list .animepost');
    const pagination = parsePagination($);
    return buildResponse('komik-populer', url, pagination, items);
  }
  async komikBerwarna(page = 1) {
    const url = page === 1 ? this.base + '/komik-berwarna/' : this.base + `/komik-berwarna/page/${page}/`;
    const html = await fetchHTML(url);
    const $ = cheerio.load(html);
    const items = parseComicList($, '.film-list .animepost');
    const pagination = parsePagination($);
    return buildResponse('komik-berwarna', url, pagination, items);
  }
  async manhwa(page = 1) {
    const url = page === 1 ? this.base + '/manhwa/' : this.base + `/manhwa/page/${page}/`;
    const html = await fetchHTML(url);
    const $ = cheerio.load(html);
    const items = parseComicList($, '.film-list .animepost');
    const pagination = parsePagination($);
    return buildResponse('manhwa', url, pagination, items);
  }
  async manga(page = 1) {
    const url = page === 1 ? this.base + '/manga/' : this.base + `/manga/page/${page}/`;
    const html = await fetchHTML(url);
    const $ = cheerio.load(html);
    const items = parseComicList($, '.film-list .animepost');
    const pagination = parsePagination($);
    return buildResponse('manga', url, pagination, items);
  }
  async daftarManga(filters = {}, page = 1) {
    const query = new URLSearchParams();
    if (filters.status) query.append('status', filters.status);
    if (filters.type) query.append('type', filters.type);
    if (filters.format) query.append('format', filters.format);
    if (filters.order) query.append('order', filters.order);
    if (filters.title) query.append('title', filters.title);
    if (filters.genre && Array.isArray(filters.genre)) {
      filters.genre.forEach(g => query.append('genre[]', g));
    }
    if (filters.demografis && Array.isArray(filters.demografis)) {
      filters.demografis.forEach(d => query.append('demografis[]', d));
    }
    if (filters.konten && Array.isArray(filters.konten)) {
      filters.konten.forEach(k => query.append('konten[]', k));
    }
    if (filters.tema && Array.isArray(filters.tema)) {
      filters.tema.forEach(t => query.append('tema[]', t));
    }
    const baseUrl = this.base + '/daftar-manga/';
    const pageUrl = page > 1 ? `page/${page}/` : '';
    const queryStr = query.toString();
    const url = baseUrl + pageUrl + (queryStr ? '?' + queryStr : '');
    const html = await fetchHTML(url);
    const $ = cheerio.load(html);
    const items = parseComicList($, '.film-list .animepost');
    const pagination = parsePagination($);
    return buildResponse('daftar-manga', url, pagination, items);
  }
  async search(query, page = 1) {
    const url = this.base + `/?s=${encodeURIComponent(query)}${page > 1 ? `&page=${page}` : ''}`;
    const html = await fetchHTML(url);
    const $ = cheerio.load(html);
    const items = parseComicList($, '.film-list .animepost');
    const pagination = parsePagination($);
    if (items.length > 0 && pagination.total === null) {
      const lastPageLink = $('.pagination .page-numbers:not(.dots):not(.next):not(.prev)').last();
      if (lastPageLink.length) {
        const t = lastPageLink.text().trim();
        if (/^\d+$/.test(t)) pagination.total = parseInt(t);
      }
      const nextLink = $('.pagination .next.page-numbers');
      if (nextLink.length && !pagination.hasNext) pagination.hasNext = true;
    }
    return buildResponse('search', url, pagination, items);
  }
  async detail(slug) {
    const url = this.base + '/komik/' + slug.replace(/^\/+/, '');
    const html = await fetchHTML(url);
    const $ = cheerio.load(html);
    const title = cleanText($('.entry-title').text()) || cleanText($('h1.entry-title').text()) || cleanText($('h1').first().text()) || null;
    const ratingText = cleanText($('.rtg i').text()) || cleanText($('.rating i').text()) || cleanText($('.archiveanime-rating i').text());
    const rating = ratingText ? parseFloat(ratingText) : null;
    const poster = $('.thumb img').attr('src') || $('.thumb img').attr('data-src') || $('img[itemprop="image"]').attr('src') || null;
    const synopsis = cleanText($('.entry-content p').text()) || cleanText($('.shortcsc.sht2 p').text()) || cleanText($('.desc p').text()) || null;
    const info = parseDetailInfo($);
    const genres = parseGenres($);
    const chapters = [];
    $('.eps_lst .listeps ul li').each((i, el) => {
      const $el = $(el);
      const link = $el.find('.lchx a').attr('href');
      const chapter = $el.find('chapter').text().trim() || $el.find('.lchx a').text().trim().replace(/Chapter\s*/i, '').trim() || null;
      const date = $el.find('.dt a').text().trim() || $el.find('.dt').text().trim() || null;
      if (link && chapter) {
        chapters.push({
          chapter: chapter,
          url: link.startsWith('http') ? link : this.base + link,
          date: date || null
        });
      }
    });
    if (chapters.length === 0) {
      $('.listeps ul li').each((i, el) => {
        const $el = $(el);
        const link = $el.find('a').attr('href');
        const chapter = $el.find('chapter').text().trim() || $el.find('a').text().trim().replace(/Chapter\s*/i, '').trim() || null;
        const date = $el.find('.dt').text().trim() || null;
        if (link && chapter) {
          chapters.push({
            chapter: chapter,
            url: link.startsWith('http') ? link : this.base + link,
            date: date || null
          });
        }
      });
    }
    const related = [];
    $('.miripmanga .serieslist li').each((i, el) => {
      const $el = $(el);
      const link = $el.find('.imgseries a').attr('href') || $el.find('a').first().attr('href');
      const title = cleanText($el.find('.leftseries h3 a').text()) || cleanText($el.find('.leftseries h3').text()) || null;
      const poster = $el.find('.imgseries img').attr('src') || $el.find('.imgseries img').attr('data-src') || null;
      if (link && title) {
        related.push({
          title: title,
          url: link.startsWith('http') ? link : this.base + link,
          poster: poster || null
        });
      }
    });
    const data = { title, rating, poster, synopsis, info, genres, chapters, related };
    const pagination = { current: 1, total: null, hasNext: false };
    return buildResponse('detail', url, pagination, data);
  }
  async chapter(slug) {
    const url = this.base + '/' + slug.replace(/^\/+/, '');
    const html = await fetchHTML(url);
    const $ = cheerio.load(html);
    const title = cleanText($('.entry-title').text()) || cleanText($('h1.entry-title').text()) || cleanText($('h1').first().text()) || null;
    const images = [];
    $('#chimg-auh img').each((i, el) => {
      const src = $(el).attr('src');
      if (src) images.push(src);
    });
    if (images.length === 0) {
      $('.chapter-image img, .img-landmine img, #Baca_Komik img').each((i, el) => {
        const src = $(el).attr('src');
        if (src) images.push(src);
      });
    }
    const nav = { daftarChapter: null, next: null, prev: null };
    $('.navig .nextprev a, .nextprev a').each((i, el) => {
      const text = cleanText($(el).text());
      const href = $(el).attr('href');
      if (text && href) {
        if (text.includes('Daftar Chapter') || text.includes('Daftar') || text.includes('Chapter List')) {
          nav.daftarChapter = href.startsWith('http') ? href : this.base + href;
        } else if (text.includes('Chapter Selanjutnya') || text.includes('Next') || text.includes('Selanjutnya')) {
          nav.next = href.startsWith('http') ? href : this.base + href;
        } else if (text.includes('Chapter Sebelumnya') || text.includes('Prev') || text.includes('Sebelumnya')) {
          nav.prev = href.startsWith('http') ? href : this.base + href;
        }
      }
    });
    const data = { title, images, navigation: nav };
    const pagination = { current: 1, total: null, hasNext: !!nav.next };
    return buildResponse('chapter', url, pagination, data);
  }
}
if (require.main === module) {
  const args = process.argv.slice(2);
  const cmd = args[0];
  const params = args.slice(1);
  const scraper = new KomikIndoScraper();
  (async () => {
    let result;
    try {
      switch (cmd) {
        case 'home':
          result = await scraper.home();
          break;
        case 'terbaru':
          result = await scraper.komikTerbaru(parseInt(params[0]) || 1);
          break;
        case 'populer':
          result = await scraper.komikPopuler(parseInt(params[0]) || 1);
          break;
        case 'berwarna':
          result = await scraper.komikBerwarna(parseInt(params[0]) || 1);
          break;
        case 'manhwa':
          result = await scraper.manhwa(parseInt(params[0]) || 1);
          break;
        case 'manga':
          result = await scraper.manga(parseInt(params[0]) || 1);
          break;
        case 'daftar': {
          let page = 1;
          const filters = {};
          for (let i = 0; i < params.length; i++) {
            const arg = params[i];
            if (/^\d+$/.test(arg)) {
              page = parseInt(arg);
            } else if (arg.includes('=')) {
              const [key, value] = arg.split('=');
              if (['genre', 'demografis', 'konten', 'tema'].includes(key)) {
                if (!filters[key]) filters[key] = [];
                filters[key].push(value);
              } else {
                filters[key] = value;
              }
            } else {
              const lower = arg.toLowerCase();
              if (['completed', 'ongoing'].includes(lower)) {
                filters.status = arg;
              } else if (['manga', 'manhwa', 'manhua'].includes(lower)) {
                filters.type = arg;
              } else if (['bw', 'colorized'].includes(lower)) {
                filters.format = arg;
              } else if (['popular', 'update', 'latest'].includes(lower)) {
                filters.order = arg;
              } else {
                if (!filters.genre) filters.genre = [];
                filters.genre.push(arg);
              }
            }
          }
          result = await scraper.daftarManga(filters, page);
          break;
        }
        case 'search':
          if (!params[0]) throw new Error('Query required');
          result = await scraper.search(params[0], parseInt(params[1]) || 1);
          break;
        case 'detail':
          if (!params[0]) throw new Error('Slug required');
          result = await scraper.detail(params[0]);
          break;
        case 'chapter':
          if (!params[0]) throw new Error('Slug required');
          result = await scraper.chapter(params[0]);
          break;
        default:
          console.error(`Commands:
  home
  terbaru [page]
  populer [page]
  berwarna [page]
  manhwa [page]
  manga [page]
  daftar [filter=value] [page]
  search <query> [page]
  detail <slug>
  chapter <slug>
Examples:
  node komikindo.js daftar completed manga 2
  node komikindo.js detail mato-seihei-no-slave`);
          process.exit(1);
      }
      console.log(JSON.stringify(result, null, 2));
    } catch (err) {
      console.error(JSON.stringify({ error: err.message }));
      process.exit(1);
    }
  })();
}
module.exports = KomikIndoScraper;

Rating

5.0(1)

Gimana snippet ini menurutmu?

</> Embed

Embed ke website / blog

<iframe src="https://qrtzcode.vercel.app/api/embed/comic/komikindo" style="width:100%;height:400px;border:none;border-radius:12px;"></iframe>

Related Snippets

mgkomik

scraper mgkomik lengkap.

16
3

webtoon

yh.

25
8