Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name 및챌헬퍼_번호체계변경대응ver2
- // @version 1.2.1-beta-theif-2
- // @include https://arca.live/b/momoirocode/*
- // @include https://arca.live/b/momoirocode/*
- // @include https://arca.live/b/momoirocode?*
- // @author ggee 개발 // tensorcrisis 수정 // 그걸 또 카와와시즈코 수정
- // @grant GM.xmlHttpRequest
- // ==/UserScript==
- ;(() => {
- const lang = document.documentElement.lang
- const DL_BASE_URL = 'https://www.dlsite.com/maniax/work/=/product_id'
- const HVDB_BASE_URL = 'https://hvdb.me/Dashboard/Add/?id='
- const KIKOERU_BASE_URL = 'https://asmr.one/work/'
- const RJ_REGEXP = /(?:rj)?(\d{6,8})/gi
- const style = document.createElement('style')
- style.appendChild(document.createTextNode(`
- .RJ-modal { display: none; align-items: center; justify-content: center; background: rgba(0,0,0,0.5); position: fixed; width: 100%; height: 100%; top: 0; right: 0; bottom: 0; left: 0; z-index: 2147483647 }
- .RJ-modal-content { display: flex; flex-direction: column; width: 320px; border-radius: 10px; background: #fff; box-shadow: 0 2px 5px rgba(0,0,0,0.8); padding-top: 10px; padding-bottom: 10px; text-align: center }
- .RJ-modal-title { width: 320px; padding-top: 0.3em; padding-bottom: 0.3em; color: #000 }
- .RJ-modal-meta { text-align: left; padding: 10px 20px; line-height: 1.5; margin-top: 25px; }
- .RJ-modal-meta a { border-bottom: 1px dotted }
- .RJ-modal-meta .main_genre > a + a { margin-left: 10px }
- .RJ-modal-meta > dt { float: left; clear: left; text-align: right; min-width: 50px; font-weight: bold; color: #000 }
- .RJ-modal-meta > dd { padding-left: 60px; color: #000 }
- .RJ-modal-img { width: 100% }
- .RJ-modal-button { box-sizing: border-box; width: 100%; display: block; padding-top: 0.6em; padding-bottom: 0.6em; font-size: 1.8em; font-weight: bold; color: #000 }
- .RJ-modal-button:hover, .RJ-modal-button:focus { background-color: #ddf; color:#00F }
- .RJ-rel { border: 1px dashed #999; border-radius: 5px; padding: 2px; font-weight: bold; }
- .RJ-link { background: #999; color: #fff; border-radius: 5px; line-height: 1; display: inline-block; width: 20px; height: 13px; text-align: center; font-weight: bold; cursor: pointer }
- .RJ-link:first-child { margin-left: 2px }
- .RJ-link:hover { background: blue }
- `))
- document.head.appendChild(style)
- function request(options = {}) {
- return new Promise((resolve, reject) => {
- GM.xmlHttpRequest(Object.assign({
- method: 'GET',
- onload: function (resp) {
- if (resp.status === 200) { resolve(resp.response) }
- else { reject(resp) }
- },
- onerror: function (resp) {
- reject(resp)
- },
- }, options))
- })
- }
- function fetchAPI(rj) {
- const url = `https://www.dlsite.com/maniax/product/info/ajax?product_id=${rj}&cdn_cache_min=1`
- return request({ url, responseType: 'json' })
- }
- function makeModal() {
- const container = document.createElement('div')
- container.className = 'RJ-modal'
- container.addEventListener('click', function(event) { if (event.target === this) container.style.display = 'none' })
- document.body.appendChild(container)
- return container
- }
- function makeRate(rate) {
- const half = rate % 1 ? '◐' : ''
- const star = Array(Math.floor(rate)).fill('●').join('')
- const empty = Array(Math.floor(5 - rate)).fill('○').join('')
- return [star, half, empty].join('')
- }
- const modalContainer = makeModal()
- async function showModal(rj) {
- modalContainer.innerHTML = ''
- const content = document.createElement('figure')
- content.className = 'RJ-modal-content'
- if (!rj.startsWith("RJ")) {
- rj = `RJ${rj}`;
- }
- const title = document.createElement('h1')
- title.textContent = rj
- title.className = 'RJ-modal-title'
- content.appendChild(title)
- const imgA = document.createElement('a')
- imgA.href = getThumbSrc(rj)
- imgA.target = '_blank'
- const img = document.createElement('img')
- img.src = imgA.href
- img.className = 'RJ-modal-img'
- imgA.appendChild(img)
- content.appendChild(imgA)
- const a = document.createElement('a')
- a.className = 'RJ-modal-button'
- a.href = `${DL_BASE_URL}/${rj}.html`
- a.target = '_blank'
- a.textContent = 'DLsite'
- img.addEventListener('error', (event) => {
- // 이미지 로딩에 실패했을 경우 예고작으로 간주함
- if (event.target.classList.contains('RJ-announce')) { return }
- event.target.classList.add('RJ-announce')
- replaceAnnounceUrl({ a, img })
- })
- img.addEventListener('load', (event) => {
- addMetadata({ rj, a, img })
- })
- const a2 = a.cloneNode(true)
- a2.href = `${HVDB_BASE_URL}${rj}`
- a2.textContent = 'HVDB'
- const a3 = a.cloneNode(true)
- a3.href = `${KIKOERU_BASE_URL}${rj}`
- a3.textContent = 'Kikoeru'
- content.appendChild(a)
- content.appendChild(a2)
- content.appendChild(a3)
- modalContainer.appendChild(content)
- modalContainer.style.display = 'flex'
- }
- let tryCount = 0
- async function addMetadata({ rj, a, img }) {
- const numFormat = new Intl.NumberFormat()
- const toBlank = aHtml => aHtml.replace(/a href/g, 'a target="_blank" href')
- const insertBefore = (rf, node) => rf.parentNode.insertBefore(node, rf);
- const metaList = document.createElement('dl')
- metaList.className = 'RJ-modal-meta'
- const meta = {}
- const APPEND_DICT = lang === 'ja-jp'
- ? {
- '販売日': '발매일',
- '予告開始日': '예고일',
- '声優': '성우',
- '年齢指定': '수위',
- 'ジャンル': '태그',
- }
- : {
- '판매일': '발매일',
- '예고 개시일': '예고일',
- '성우': '성우',
- '연령 지정': '수위',
- '작품 형식': '태그',
- }
- const DICT = {
- 'TITLE': '제목',
- 'CIRCLE': '서클',
- 'PRICE': '가격',
- 'RATE': '평점',
- 'DL': '다운수',
- 'WISH': '찜수',
- ...APPEND_DICT,
- }
- try {
- const body = await request({ url: a.href })
- const frag = document.createElement('div')
- frag.innerHTML = body
- meta.TITLE = frag.querySelector('#work_name > a').textContent
- meta.CIRCLE = toBlank(frag.querySelector('.maker_name').innerHTML)
- const table = frag.querySelector('#work_outline')
- const rows = Array.from(table.querySelectorAll('tr'))
- const appendDictKeys = Object.keys(APPEND_DICT);
- const pHtml = rows.forEach(row => {
- const [th, td] = Array.from(row.children)
- if (appendDictKeys.includes(th.textContent)) {
- meta[th.textContent] = toBlank(td.innerHTML)
- }
- })
- } catch (e) {
- console.error(e)
- if (e.status === 404) {
- if (tryCount++ < 1) {
- replaceAnnounceUrl({ a })
- addMetadata({ rj, a, img })
- }
- return
- }
- }
- try {
- const api = await fetchAPI(rj)
- const data = api[rj] || {}
- const { dl_count, wishlist_count, rate_average_2dp, rate_count, price } = data
- meta.RATE = `${makeRate(rate_average_2dp)} ${rate_average_2dp}점 (평가수: ${numFormat.format(rate_count)})`
- meta.DL = numFormat.format(dl_count)
- meta.WISH = numFormat.format(wishlist_count)
- meta.PRICE = `${numFormat.format(price)}円`
- } catch (e) { console.error(e) }
- Object.entries(DICT).forEach(([ key, value ]) => {
- if (!meta[key]) { return }
- const dt = document.createElement('dt')
- const dd = document.createElement('dd')
- dt.textContent = value
- dd.innerHTML = meta[key]
- metaList.appendChild(dt)
- metaList.appendChild(dd)
- // dt가 목차, dd가 내용물
- // 원하는 dict이랑 meta만 없애버리면
- })
- insertBefore(a, metaList)
- // img 뒤에 추가
- }
- function replaceAnnounceUrl({ a, img }) {
- if (img) {
- const toSrcReplaced = img.src.replace('/work/', '/ana/').replace('_img_', '_ana_img_')
- if (img.src !== toSrcReplaced) { img.src = toSrcReplaced }
- }
- if (a) {
- const toHrefReplaced = a.href.replace('/work/', '/announce/')
- if (a.href !== toHrefReplaced) { a.href = toHrefReplaced }
- }
- }
- function makeModalButton(rj) {
- const a = document.createElement('span')
- a.setAttribute('data-rj', rj)
- rj = rj.length === 6 ? `RJ${rj}` : rj.toUpperCase()
- a.textContent = '?'
- a.className = 'RJ-link'
- a.addEventListener('click', (event) => { event.preventDefault(); event.stopPropagation(); showModal(rj) })
- return a
- }
- function getThumbSrc(rj) {
- rj = rj.replace(/rj/gi, '')
- let bucket = Math.ceil(Number(rj) / 1000) * 1000
- bucket = ('000000' + String(bucket)).slice(-rj.length)
- return `https://img.dlsite.jp/modpub/images2/work/doujin/RJ${bucket}/RJ${rj}_img_main.jpg`
- }
- // 갤에서 RJ코드 해당되는거 글씨 바꾸기
- function filterText(text = '') {
- let matches
- let i = 0
- let rjs = []
- while ((matches = RJ_REGEXP.exec(text)) !== null) {
- let rj = matches[0]
- let startIndex = matches.index
- let lastIndex = RJ_REGEXP.lastIndex
- if (text[startIndex - 1] && /[\w/px"]/.test(text[startIndex - 1])) {
- // 앞글자가 붙어있음: pass
- continue
- }
- if (text[lastIndex] && /[\w/px"]/.test(text[lastIndex])) {
- // 뒷글자가 붙어있음: pass
- continue
- }
- rjs.push(rj)
- // text replace
- text = `${text.slice(0, matches.index)}__{${i++}}__${text.slice(lastIndex)}`
- }
- for (let j = 0, len = rjs.length; j < len; j++) {
- text = text.replace(
- new RegExp('__\\{' + j + '\\}__', 'g'),
- `<em class="RJ-rel" data-rj="${rjs[j]}">${rjs[j]}</em>`
- )
- }
- return text
- }
- //
- function addLink(node) {
- if (!node) return
- let matches = node.textContent.match(RJ_REGEXP)
- if (!matches) return
- if (node.classList.contains('RJ')) return
- node.classList.add('RJ')
- matches = [...new Set(matches)]
- node.innerHTML = filterText(node.innerHTML)
- matches.forEach(match => {
- const mb = makeModalButton(match)
- const rj = mb.getAttribute('data-rj')
- const target = node.querySelector(`[data-rj="${rj}"]`)
- if (!target) return
- target.appendChild(mb)
- //버튼을 뒤에 추가
- })
- }
- const titles = Array.from(document.querySelectorAll('.title ion-ios-photos-outline, .title'))
- const body = document.querySelector('.article-content')
- const titleSubject = document.querySelector('.title_subject')
- const getComments = () => Array.from(document.querySelectorAll('.text'))
- let comments = getComments()
- const commentWrap = document.querySelector('.comment_wrap')
- if (commentWrap) {
- const commentObserver = new MutationObserver(mutations => {
- comments = getComments()
- comments.map(addLink)
- })
- commentObserver.observe(commentWrap, { attributes: true, childList: true, characterData: true })
- }
- titles.map(addLink)
- addLink(titleSubject)
- addLink(body)
- comments.map(addLink)
- // 타이틀 댓글 제목 등에 링크 추가해주는 부분, 즉 갤용
- })();
Add Comment
Please, Sign In to add comment