// ==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()