sehuatang

self mode

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         sehuatang
// @version      0.0.12
// @author       bilabila
// @namespace    https://greasyfork.org/users/164996a
// @match        https://www.sehuatang.org/404
// @match        https://sehuatang.org/404
// @description  self mode
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_listValues
// @grant        GM_deleteValue
// @grant        GM_xmlhttpRequest
// @connect      www.sehuatang.org
// @run-at       document-start
// ==/UserScript==
const tags = {
  fellatiojapan: ['亚洲无码原创', 'fellatiojapan'],
  handjobjapan: ['亚洲无码原创', 'handjobjapan'],
  uralesbian: ['亚洲无码原创', 'uralesbian'],
  spermmania: ['亚洲无码原创', 'spermmania'],
  legsjapan: ['亚洲无码原创', 'legsjapan'],
  无: ['亚洲无码原创', '全部'],
  有: ['亚洲有码原创', '全部'],
  素: ['素人有码系列', '全部'],
}
const head = `<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>sehuatang</title>
<style>
  :root {
    --color1: #444;
    --color2: #bbb;
  }
  body {
    color: var(--color1);
    text-align: center;
    margin: 0;
    box-sizing: border-box;
  }
  img {
    width: 100%;
  }
  ul {
    margin: 0;
    padding: 0;
    list-style-type: none;
  }
  li {
    color: var(--color1);
    margin: 0 6% 3em;
    padding: 0;
  }
  a {
    text-decoration: none;
  }
  .title {
    display: flex;
    justify-content: space-between;
    background-color: white;
  }
  #tag {
    margin: 1em 0;
  }
  #tag > span {
    display: inline-block;
    padding: 0.2em 0.8em;
    color: var(--color1);
    cursor: pointer;
    user-select: none;
  }
  #tag > span.disable {
    color: var(--color2);
  }
  #clear {
    position: absolute;
    top: 1em;
    right: 1em;
    padding: 0.2em;
    cursor: pointer;
    user-select: none;
    color: var(--color2);
  }
  #clear:hover {
    color: var(--color1)
  }
</style>`
const body = `<div id=app>
  <span id=clear>☢</span>
  <div id="tag"></div>
  <ul></ul>
</div>`
document.head.innerHTML = head
document.body.innerHTML = body
const ul = document.querySelector('ul')
Array.prototype.last = function (i = 1) {
  return this[this.length - i]
}
const gmFetch = (url, method = 'GET') =>
  new Promise((onload, onerror) => {
    GM_xmlhttpRequest({ url, method, onload, onerror, cookie: '_safe=' })
  })
const parseHTML = (str) => {
  const tmp = document.implementation.createHTMLDocument()
  tmp.body.innerHTML = str
  return tmp
}
const cache = async (k, f, ...args) => {
  if (k === undefined) return
  let a = GM_getValue(k)
  if (a) return JSON.parse(a)
  a = await f(...args)
  GM_setValue(k, JSON.stringify(a))
  return a
}
// get one post
const fetchOnePost = async (tid) => {
  const url = `https://www.sehuatang.org/forum.php?mod=viewthread&tid=${tid}`
  let a = await gmFetch(url)
  a = parseHTML(a.responseText)
  let title = a.querySelector('h1'),
    img = a.querySelectorAll('.pcb img'),
    magnet = a.querySelector('.blockcode li'),
    torrent = a.querySelector('.attnm > a')

  title = title ? title.textContent : ''
  const img_bl = [
    'https://cdn.jsdelivr.net/gh/hishis/forum-master/public/images/patch.gif',
  ]
  img = img
    ? [...img]
        .map((i) => i.getAttribute('file'))
        .filter((i) => i && !img_bl.includes(i))
    : []
  magnet = magnet ? magnet.textContent : ''
  torrent = torrent ? torrent.href : ''
  a = { img, magnet, torrent, title }
  return a
}
// get one page
const fetchOnePage = async (fid, typeid, page) => {
  let a = await gmFetch(
    `https://www.sehuatang.org/forum.php?mod=forumdisplay&fid=${fid}` +
      (typeid != '0' ? `&filter=typeid&typeid=${typeid}` : '') +
      `&page=${page}`
  )
  a = parseHTML(a.responseText)
  // check page
  // if (a.querySelector('#fd_page_top strong').textContent != page) return
  a = [
    ...a.querySelectorAll(
      '#threadlisttableid tbody[id^=normalthread_] th a[id^=content_]'
    ),
  ]
  return a.map((i) => parseInt(/content_(\d+)/.exec(i.id)[1]))
}
// get type and id of one fid
const fetchTypeId = async (fid) => {
  let a = await gmFetch(
    `https://www.sehuatang.org/forum.php?mod=forumdisplay&fid=${fid}`
  )
  a = parseHTML(a.responseText)
  a = a.querySelectorAll('#thread_types > li:not([id]) > a')
  a = [...a].filter((i) => i.firstChild)
  const b = { 全部: '0' }
  ;[...a].forEach(
    (i) => (b[i.firstChild.textContent] = /typeid=(\d+)/.exec(i.href)[1])
  )
  return b
}
// get all fid and name
const fetchFidName = async () => {
  const url = `https://www.sehuatang.org/forum.php`
  let a = await gmFetch(url)
  a = parseHTML(a.responseText)
  let ans = {}
  for (let b of a.querySelectorAll('a')) {
    if (b.childElementCount != 0) continue
    let fid = /(fid=|forum-)(\d+)/.exec(b.href)
    if (!fid) continue
    fid = fid[2]
    let name = b.textContent
    ans[name] = fid
  }
  return ans
}
class Bot {
  constructor(fid, typeid) {
    this.num_one_page = Number.MAX_SAFE_INTEGER
    this.key = fid + '_' + typeid
    this.data = JSON.parse(GM_getValue(this.key, '[]'))
    this.fid = fid
    this.typeid = typeid
    this.i1 = 0
  }
  async get(page) {
    return await fetchOnePage(this.fid, this.typeid, page)
  }
  async more(a) {
    if (a.page === -1) return
    const aa = a.arr,
      aal = aa.last(),
      b = await this.get(++a.page)
    if (!b) return (a.page = -1)
    let i = 0
    for (; i < b.length && b[i] >= aal; ++i) {}
    // end support for long time scroll interval
    //if (i === b.length) await this.more(a)
    if (i === b.length) return (a.page = -1)
    for (; i < b.length; ++i) aa.push(b[i])
  }
  merge() {
    const data = this.data
    if (data.length < 2) return
    const p = data.last(2),
      c = data.last(),
      pa = p.arr,
      ca = c.arr,
      cal = ca.last(),
      calen = ca.length
    if (cal > pa[0]) return
    let i = 1
    for (; i < pa.length && pa[i] >= cal; ++i) {}
    for (; i < pa.length; ++i) ca.push(pa[i])
    p.arr = ca
    if (p.page !== -1)
      p.page = (c.page + (ca.length - calen) / this.num_one_page) >> 0
    data.pop()
  }
  async refresh() {
    const data = this.data
    const p = data.last() && data.last().arr[0]
    data.push({
      arr: undefined,
      page: 0,
    })
    const a = data.last()
    a.arr = await this.get(++a.page)
    this.num_one_page = a.arr.length
    this.merge()
    if (p != data.last().arr[0]) {
      this.save()
      return true
    }
  }
  async next() {
    const data = this.data
    if (data.length === 0) return await this.refresh()
    await this.more(data.last())
    this.merge()
    this.save()
  }
  save() {
    GM_setValue(this.key, JSON.stringify(this.data))
  }
  async nextOne() {
    if (this.data.length === 0) await this.refresh()
    const a = this.data.last().arr
    if (this.i1 < a.length) return a[this.i1++]
    await this.next()
    return a[this.i1++]
  }
}
const li = (a) => {
  const { title, img, magnet, torrent } = a
  return `
    <li>
      ${img.map((i) => `<img src="${i}"/>`).join('')}
      <div class="title">
        <span>${title}</span>
        <span>
          <a href="${magnet}">magnet</a>
          <a href="${torrent}" target="_blank">torrent</a>
        </span>
      </div>
    </li>
  `
}
const addTag = () => {
  let ts = tags
  let t = GM_getValue('tag')
  t = t ? JSON.parse(t) : [Object.keys(ts)[0]]
  const n = document.querySelector('#tag')
  const a = Object.keys(ts)
    .map((i) => `<span ${t.includes(i) ? '' : 'class=disable'}>${i}</span>`)
    .join('')
  requestAnimationFrame(() => (n.innerHTML = a))
  n.addEventListener('click', (e) => {
    e = e.target
    if (e.nodeName != 'SPAN' || !e.classList.contains('disable')) return
    e.classList.add('disable')
    t = [e.textContent]
    GM_setValue('tag', JSON.stringify(t))
    window.location.reload()
  })
  return [t, ts]
}
const main = async () => {
  const [t, ts] = addTag()
  const v = []
  for (let x of t) {
    x = ts[t]
    let fid = await cache('fid', fetchFidName)
    fid = fid[x[0]]
    let typeid = await cache('typeid' + fid, fetchTypeId, fid)
    typeid = typeid[x[1]]
    v.push(new Bot(fid, typeid))
  }
  const q = await Promise.all(v.map(async (i) => await i.nextOne()))
  const next = async () => {
    let m = 0
    for (let i = 1; i < q.length; ++i)
      if (q[i] !== undefined && q[i] > q[m]) m = i
    if (q[m] === undefined) return
    const r = q[m]
    while (r === q[m]) {
      q[m] = await v[m].nextOne()
    }
    return r
  }
  let isEnd = false
  const add = async (a) => {
    if (a === undefined) {
      if (isEnd) return
      isEnd = true
      window.onscroll = null
      ul.insertAdjacentHTML(
        'afterend',
        `<span>total : ${ul.childElementCount}</span>`
      )
      const total = document.querySelector('ul+span')
      new MutationObserver(
        () => (total.innerHTML = `total : ${ul.childElementCount}`)
      ).observe(ul, { childList: true })
      return
    }
    a = await cache(a, fetchOnePost, a)
    if (!a) return
    requestAnimationFrame(() => ul.insertAdjacentHTML('beforeend', li(a)))
  }
  for (let i = 0; i < 3; ++i) await add(await next())
  const a = await Promise.all(v.map(async (i) => await i.refresh()))
  if (a.some((i) => i)) window.location.reload()
  let isFetching = false
  window.onscroll = async () => {
    if (
      !isFetching &&
      10 * window.innerHeight + window.scrollY >= document.body.offsetHeight
    ) {
      isFetching = true
      await add(await next())
      isFetching = false
    }
  }
}
const init = () => {
  for (let i of GM_listValues()) GM_deleteValue(i)
  GM_setValue('version', GM_info.script.version)
  window.location.reload()
}
window.onbeforeunload = () => window.scrollTo(0, 0)
if (GM_getValue('version') != GM_info.script.version) init()
document.querySelector('#clear').onclick = init
main()