sehuatang

self mode

От 05.10.2019. Виж последната версия.

  1. // ==UserScript==
  2. // @name sehuatang
  3. // @version 0.0.3
  4. // @author bilabila
  5. // @namespace https://greasyfork.org/users/164996a
  6. // @match https://www.sehuatang.org/404
  7. // @description self mode
  8. // @grant GM_getValue
  9. // @grant GM_setValue
  10. // @grant GM_listValues
  11. // @grant GM_deleteValue
  12. // @run-at document-start
  13. // ==/UserScript==
  14. const fid = 36
  15. const tag = [
  16. '无码破解',
  17. 'fellatiojapan',
  18. 'handjobjapan',
  19. 'uralesbian',
  20. 'spermmania',
  21. 'legsjapan'
  22. ]
  23. const head = `<meta charset="UTF-8" />
  24. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  25. <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  26. <title>sehuatang</title>
  27. <style>
  28. :root {
  29. --color1: #444;
  30. --color2: #bbb;
  31. }
  32. body {
  33. color: var(--color1);
  34. text-align: center;
  35. margin: 0;
  36. box-sizing: border-box;
  37. }
  38. img {
  39. width: 100%;
  40. }
  41. ul {
  42. margin: 0;
  43. padding: 0;
  44. list-style-type: none;
  45. }
  46. li {
  47. color: var(--color1);
  48. margin: 0 6% 3em;
  49. padding: 0;
  50. }
  51. a {
  52. text-decoration: none;
  53. }
  54. .title {
  55. display: flex;
  56. justify-content: space-between;
  57. background-color: white;
  58. }
  59. #tag {
  60. margin: 1em 0;
  61. }
  62. #tag > span {
  63. display: inline-block;
  64. padding: 0.2em 0.8em;
  65. color: var(--color1);
  66. cursor: pointer;
  67. user-select: none;
  68. }
  69. #tag > span.disable {
  70. color: var(--color2);
  71. }
  72. #clear {
  73. position: absolute;
  74. top: 1em;
  75. right: 1em;
  76. padding: 0.2em;
  77. cursor: pointer;
  78. user-select: none;
  79. color: var(--color2);
  80. }
  81. #clear:hover {
  82. color: var(--color1)
  83. }
  84. </style>`
  85. const body = `<div id=app>
  86. <span id=clear>☢</span>
  87. <div id="tag"></div>
  88. <ul></ul>
  89. </div>`
  90. document.head.innerHTML = head
  91. document.body.innerHTML = body
  92. const ul = document.querySelector('ul')
  93. Array.prototype.last = function(i = 1) {
  94. return this[this.length - i]
  95. }
  96. const parseHTML = str => {
  97. const tmp = document.implementation.createHTMLDocument()
  98. tmp.body.innerHTML = str
  99. return tmp
  100. }
  101. const cache = async (k, f) => {
  102. if (k === undefined) return
  103. let a = GM_getValue(k)
  104. if (a) return JSON.parse(a)
  105. a = await f(k)
  106. GM_setValue(k, JSON.stringify(a))
  107. return a
  108. }
  109. // get one thread
  110. const t0 = async tid => {
  111. const url = `https://www.sehuatang.org/forum.php?mod=viewthread&tid=${tid}`
  112. let a = await fetch(url)
  113. a = await a.text()
  114. a = parseHTML(a)
  115. let title = a.querySelector('#thread_subject'),
  116. img = a.querySelectorAll('.pcb img'),
  117. magnet = a.querySelector('.blockcode li'),
  118. torrent = a.querySelector('.attnm > a')
  119.  
  120. title = title ? title.textContent : ''
  121. img = img ? [...img].map(i => i.getAttribute('file')).filter(i => i) : []
  122. magnet = magnet ? magnet.textContent : ''
  123. torrent = torrent ? torrent.href : ''
  124. a = {
  125. img,
  126. magnet,
  127. torrent,
  128. title
  129. }
  130. return a
  131. }
  132. // get one page
  133. const t1 = async (typeid, page) => {
  134. let a = await fetch(
  135. `https://www.sehuatang.org/forum.php?mod=forumdisplay&fid=${fid}&filter=typeid&typeid=${typeid}&page=${page}`
  136. )
  137. a = await a.text()
  138. a = parseHTML(a)
  139. // check page
  140. if (a.querySelector('#fd_page_top strong').textContent != page) return
  141. a = [...a.querySelectorAll('#threadlisttableid tr > th > em + a')]
  142. return a.map(i => parseInt(/tid=(\d+)/.exec(i.href)[1]))
  143. }
  144. // get type and id of one fid
  145. const t2 = async () => {
  146. let a = await fetch(`https://www.sehuatang.org/forum.php?mod=forumdisplay&fid=${fid}`)
  147. a = await a.text()
  148. a = parseHTML(a)
  149. a = a.querySelectorAll('#thread_types > li:not([id]) > a')
  150. const b = {}
  151. ;[...a].forEach(i => (b[i.firstChild.textContent] = /typeid=(\d+)/.exec(i.href)[1]))
  152. return b
  153. }
  154. class C1 {
  155. constructor(typeid) {
  156. this.num_one_page = Number.MAX_SAFE_INTEGER
  157. this.data = JSON.parse(GM_getValue(typeid, '[]'))
  158. this.typeid = typeid
  159. this.i1 = 0
  160. }
  161. async get(page) {
  162. return await t1(this.typeid, page)
  163. }
  164. async more(a) {
  165. if (a.page === -1) return
  166. const aa = a.arr,
  167. aal = aa.last(),
  168. b = await this.get(++a.page)
  169. if (!b) return (a.page = -1)
  170. let i = 0
  171. for (; i < b.length && b[i] >= aal; ++i) {}
  172. if (i === b.length) await this.more(a)
  173. for (; i < b.length; ++i) aa.push(b[i])
  174. }
  175. merge() {
  176. const data = this.data
  177. if (data.length < 2) return
  178. const p = data.last(2),
  179. c = data.last(),
  180. pa = p.arr,
  181. ca = c.arr,
  182. cal = ca.last(),
  183. calen = ca.length
  184. if (cal > pa[0]) return
  185. let i = 1
  186. for (; i < pa.length && pa[i] >= cal; ++i) {}
  187. for (; i < pa.length; ++i) ca.push(pa[i])
  188. p.arr = ca
  189. if (p.page !== -1) p.page = (c.page + (ca.length - calen) / this.num_one_page) >> 0
  190. data.pop()
  191. }
  192. async refresh() {
  193. const data = this.data
  194. const p = data.last() && data.last().arr[0]
  195. data.push({
  196. arr: undefined,
  197. page: 0
  198. })
  199. const a = data.last()
  200. a.arr = await this.get(++a.page)
  201. this.num_one_page = a.arr.length
  202. this.merge()
  203. if (!p || p != data.last().arr[0]) {
  204. this.save()
  205. return true
  206. }
  207. }
  208. async next() {
  209. const data = this.data
  210. if (data.length === 0) return await this.refresh()
  211. await this.more(data.last())
  212. this.merge()
  213. this.save()
  214. }
  215. save() {
  216. GM_setValue(this.typeid, JSON.stringify(this.data))
  217. }
  218. async nextOne() {
  219. if (this.data.length === 0) await this.refresh()
  220. const a = this.data.last().arr
  221. if (this.i1 < a.length) return a[this.i1++]
  222. await this.next()
  223. return a[this.i1++]
  224. }
  225. }
  226. const li = a => {
  227. const { title, img, magnet, torrent } = a
  228. return `
  229. <li>
  230. ${img.map(i => `<img src="${i}"/>`).join('')}
  231. <div class="title">
  232. <span>${title}</span>
  233. <span>
  234. <a href="${magnet}">magnet</a>
  235. <a href="${torrent}" target="_blank">torrent</a>
  236. </span>
  237. </div>
  238. </li>
  239. `
  240. }
  241. const addTag = () => {
  242. let t = GM_getValue('tag')
  243. t = t ? JSON.parse(t) : tag
  244. const n = document.querySelector('#tag')
  245. const a = tag
  246. .map(i => `<span ${t.includes(i) ? '' : 'class=disable'}>${i}</span>`)
  247. .join('')
  248. requestAnimationFrame(() => (n.innerHTML = a))
  249. n.addEventListener('click', e => {
  250. e = e.target
  251. if (e.nodeName != 'SPAN') return
  252. e.classList.toggle('disable')
  253. if (e.classList.contains('disable')) {
  254. const i = t.indexOf(e.textContent)
  255. if (i !== -1) t.splice(i, 1)
  256. } else {
  257. t.push(e.textContent)
  258. }
  259. GM_setValue('tag', JSON.stringify(t))
  260. window.location.reload()
  261. })
  262. return t
  263. }
  264. const main = async () => {
  265. const tag2id = await cache('tag2id', t2)
  266. const t = addTag()
  267. const v = t.map(i => new C1(tag2id[i]))
  268. const q = await Promise.all(v.map(async i => await i.nextOne()))
  269. const next = async () => {
  270. let m = 0
  271. for (let i = 1; i < q.length; ++i) if (q[i] !== undefined && q[i] > q[m]) m = i
  272. if (q[m] === undefined) return
  273. const r = q[m]
  274. q[m] = await v[m].nextOne()
  275. return r
  276. }
  277. let end = false
  278. const add = async a => {
  279. if (a === undefined) {
  280. if (end) return
  281. end = true
  282. window.onscroll = null
  283. ul.insertAdjacentHTML('afterend', `<span>total : ${ul.childElementCount}</span>`)
  284. const total = document.querySelector('ul+span')
  285. new MutationObserver(
  286. () => (total.innerHTML = `total : ${ul.childElementCount}`)
  287. ).observe(ul, { childList: true })
  288. return
  289. }
  290. a = await cache(a, t0)
  291. if (!a) return
  292. requestAnimationFrame(() => ul.insertAdjacentHTML('beforeend', li(a)))
  293. }
  294. for (let i = 0; i < 10; ++i) await add(await next())
  295. const a = await Promise.all(v.map(async i => await i.refresh()))
  296. if (a.some(i => i)) window.location.reload()
  297. let fetching = false
  298. window.onscroll = async () => {
  299. if (
  300. !fetching &&
  301. 10 * window.innerHeight + window.scrollY >= document.body.offsetHeight
  302. ) {
  303. fetching = true
  304. await add(await next())
  305. fetching = false
  306. }
  307. }
  308. }
  309. window.onbeforeunload = () => window.scrollTo(0, 0)
  310. document.querySelector('#clear').onclick = () => {
  311. for (let i of GM_listValues()) GM_deleteValue(i)
  312. GM_setValue('tag', JSON.stringify(tag.slice(0, 3)))
  313. window.location.reload()
  314. }
  315. main()