哔哩哔哩番剧出差助手

为动态页面增加显示哔哩哔哩番剧出差动态功能

目前为 2021-08-10 提交的版本。查看 最新版本

// ==UserScript==
// @name         哔哩哔哩番剧出差助手
// @namespace    bilibili_abroad_assistant
// @version      2.0.0
// @description  为动态页面增加显示哔哩哔哩番剧出差动态功能
// @author       溶酶菌
// @match        *://t.bilibili.com
// @match        *://t.bilibili.com/?*
// @icon         https://www.bilibili.com/favicon.ico
// @grant        GM_addStyle
// @license      MIT
// @run-at       document-idle
// ==/UserScript==
(function () {
  'use strict';
  let loaded = false // 是否已加载
  let showBtn = () => document.querySelector('#show-abroad')
  let hideBtn = () => document.querySelector('#hide-abroad')
  let refreshBtn = () => document.querySelector('#refresh-abroad')
  let backtop = () => document.querySelector("#app > div > div.home-page.f-clear > div.back-top").click()
  let centerPanel = () => document.querySelector("#app > div > div.home-page.f-clear > div.home-container > div > div.center-panel")
  let cardList = () => document.querySelector("#app > div > div.home-page.f-clear > div.home-container > div > div.center-panel > div.card-list")
  let left = () => document.querySelector("#app > div > div.home-page.f-clear > div.home-container > div > div.left-panel > div.adaptive-scroll > div.scroll-content")
  let dynamicsPanel = () => document.querySelector('#dynamics-panel')

  // let loadingTips = () => document.querySelector('#loading-tips')
  let dateFormat = (fmt, date) => {
    let ret;
    const opt = {
      "Y+": date.getFullYear().toString(),        // 年
      "m+": (date.getMonth() + 1).toString(),     // 月
      "d+": date.getDate().toString(),            // 日
      "H+": date.getHours().toString(),           // 时
      "M+": date.getMinutes().toString(),         // 分
      "S+": date.getSeconds().toString()          // 秒
      // 有其他格式化字符需求可以继续添加,必须转化成字符串
    };
    for (let k in opt) {
      ret = new RegExp("(" + k + ")").exec(fmt);
      if (ret) {
        fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0")))
      };
    };
    return fmt;
  }
  let loadData = function (ps = 9, pn = 1) {
    // loadingTips().style.display = 'inline'
    if (isNaN(pn)) {
      return
    }
    fetch(`https://api.bilibili.com/x/space/arc/search?mid=11783021&ps=${ps}&tid=0&pn=${pn}&keyword=&order=pubdate&jsonp=jsonp`, {
      method: 'GET',
      credentials: 'include'
    }).then(res => res.json())
      .then(res => {
        dynamicsPanel().innerHTML = ''
        if (res.code !== 0) {
          return
        }
        let { tlist, vlist } = res.data.list
        let { pn, ps, count } = res.data.page
        // loadingTips().style.display = 'none'
        let listDom = document.createElement('ul')
        listDom.classList.add('cube-list')

        vlist.forEach(item => {
          let timestamp = new Date(item.created * 1e3) // s -> ms
          let itemDom = document.createElement('li')
          itemDom.classList.add('small-item')
          itemDom.innerHTML = `<a href="//www.bilibili.com/video/${item.bvid}" target="_blank" class="cover">
              <img src="${item.pic}@380w_240h_100Q_1c.webp"
                  alt="${item.title}">
              <span class="length">${item.length}</span>
          </a>
          <a href="//www.bilibili.com/video/${item.bvid}" target="_blank" title="${item.title}"
              class="title">${item.title}</a>
          <div class="meta"><span class="play"><i class="icon"></i>${item.play}
              </span>
              <span class="time" title="${dateFormat('YYYY-mm-dd HH:MM:SS', timestamp)}">
                  <i class="icon"></i>${dateFormat('mm-dd HH:MM', timestamp)}
              </span>
          </div>`
          listDom.append(itemDom)
        })
        dynamicsPanel().append(listDom)
        dynamicsPanel().append(getPageBar(ps, pn, count))
      });
  }

  let getPageBar = (ps, pn, count) => {
    let pageBarDom = document.createElement('ul')

    let totalPage = Math.floor(count / ps)
    let prePage = pn > 1 ? pn - 1 : undefined
    let nextPage = pn < totalPage ? pn + 1 : undefined
    let pageCurDom = document.createElement('li')
    let pageNextDom = document.createElement('li')
    let pageTotalDom = document.createElement('span')
    let pageElevatorDom = document.createElement('span')

    pageBarDom.classList.add('be-pager')

    let pagePrev = document.createElement('li')
    pagePrev.classList.add('be-pager-prev')
    pagePrev.innerHTML = `<a>上一页</a>`
    pagePrev.onclick = () => {
      if (prePage) {
        loadData(ps, prePage)
      }
    }
    pageBarDom.append(pagePrev)

    // if (prePage) {
    //   let prev = document.createElement('li')
    //   prev.classList.add('be-pager-item')
    //   prev.innerHTML = `<a>${prePage}</a>`
    //   prev.onclick = () => {
    //     loadData(ps, prePage)
    //   }
    //   pageBarDom.append(prev)
    // }

    pageCurDom.classList.add('be-pager-item')
    pageCurDom.classList.add('be-pager-item-active')
    pageCurDom.innerHTML = `<a>${pn}</a>`
    pageBarDom.append(pageCurDom)

    // if (nextPage) {
    //   let next = document.createElement('li')
    //   next.classList.add('be-pager-item')
    //   next.innerHTML = `<a>${nextPage}</a>`
    //   next.onclick = () => {
    //     loadData(ps, prePage)
    //   }
    //   pageBarDom.append(next)
    // }

    pageNextDom.classList.add('be-pager-next')
    pageNextDom.innerHTML = `<a>下一页</a>`
    pageNextDom.onclick = () => {
      if (nextPage) {
        loadData(ps, nextPage)
      }
    }
    pageBarDom.append(pageNextDom)

    pageTotalDom.classList.add('be-pager-total')
    pageTotalDom.innerHTML = ` 共 ${totalPage} 页,</span>`
    pageBarDom.append(pageTotalDom)

    pageElevatorDom.classList.add('be-pager-options-elevator')

    let span = document.createElement('span')
    span.innerText = '跳转至'
    pageElevatorDom.append(span)
    let spaceInput = document.createElement('input')
    spaceInput.classList.add('space_input')
    spaceInput.setAttribute('type', 'num')
    spaceInput.onkeydown = function (event) {
      if (event.key === 'Enter') {
        let value = Number(spaceInput.value)
        if (!isNaN(value)) {
          if (value > totalPage) {
            spaceInput.value = totalPage
            value = totalPage
          }
          loadData(ps, value)
        }
      }
    }
    pageElevatorDom.append(spaceInput)
    span = document.createElement('span')
    span.innerText = '页'
    pageElevatorDom.append(span)
    pageBarDom.append(pageElevatorDom)
    return pageBarDom
  }

  let start = () => {
    if (!centerPanel()) {
      return
    }
    let panel = document.createElement('div')
    panel.id = 'dynamics-panel'
    panel.style.display = 'none'
    panel.style.marginTop = '8px'
    panel.style.backgroundColor = '#FFF'
    centerPanel().append(panel)

    let actionbar = document.createElement('div')
    actionbar.innerHTML =
      `<div style="margin-top: 8px;border-radius: 4px;position: sticky;top: 8px;background: #FFF;padding: 16px;display: flex;">
        <div class="avatar" style="box-shadow: 0 0 0 1px #f25d8e;border-radius: 22px;height: 38px;width:38px;background-size: 38px 38px;background-image: url(&quot;https://i0.hdslb.com/bfs/face/9f10323503739e676857f06f5e4f5eb323e9f3f2.jpg@96w_96h_100Q_1c.webp&quot;);"></div>
        <div style="margin-left: 11px;">
          <div>哔哩哔哩番剧出差</div>
          <div style="margin-top: 4px;font-size: 12px;color: #00b5e5">
            <span id="show-abroad" style="cursor: pointer">显示</span>
            <span id="hide-abroad" style="cursor: pointer;display: none">隐藏</span>
            <span id="refresh-abroad" style="cursor: pointer;display: none;margin-left: 1em">刷新</span>
          </div>
        </div>
    </div>`

    left().append(actionbar)

    let toggleMode = (show = true) => {
      showBtn().style.display = show ? 'none' : 'inline'
      hideBtn().style.display = show ? 'inline' : 'none'
      refreshBtn().style.display = show ? 'inline' : 'none'
      cardList().style.display = show ? 'none' : 'block'
      dynamicsPanel().style.display = show ? 'block' : 'none'
      backtop()
    }

    showBtn().onclick = () => {
      toggleMode(true)
      if (!loaded) {
        loadData()
        loaded = true
      }
    }
    hideBtn().onclick = () => {
      toggleMode(false)
    }
    refreshBtn().onclick = () => {
      loadData()
      backtop()
    }

  }

  let sleep = function (time) {
    return new Promise((resolve) => setTimeout(resolve, time));
  };

  // 延迟执行,否则可能找不到对应的DOM
  sleep(500).then(() => {
    console.log('哔哩哔哩番剧出差助手运行')
    start()
  })

  //========================================= css
  GM_addStyle(`.icon{vertical-align:middle;background-repeat:no-repeat;background-image:url(//s1.hdslb.com/bfs/static/jinkela/space/assets/icons.png)}a{color:inherit;text-decoration:none;transition:color 0.2s ease, background-color 0.2s ease}.i-watchlater{display:none;position:absolute;right:6px;bottom:4px;width:22px;height:22px;z-index:5;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAABT0lEQVQ4je3VMYrCQBTG8b/DWASLNKJgJ1hY2sloZeF2gkeYW+wZvIVXsLBJY5UdtLJLk17EIghCwChukWRxFzSaaLdfMwm8/EgemTclgOl02gEmwAAoky8RsAA+tdbrUoJ+AVZO8G9CoCeJ39Sq1+sopbBtO5e23+8xxrDdbi1gIog/vxAKYNs2Sqn0diBIenoP9X2fIAgyccv66WZZZBXvdjtc18UYkwlfJxM+n8+/1pfBefMPPw4LEZcEQcBqteJ0Or0GrlartFotLpcLnucxm83YbDbFYSEE/X6f4XBIpVLhcDjgOA6u63I8HvPDaRqNBuPxmHa7DcS7cT6f36yXj8IAUkq63S7NZpPlcomUtx9/Ck5Tq9UYjUZ3a976u0UQz9OiCcMwvYwk8XHyYYxBKXU9+p5Grybg4m1Hk9Bar4Ee4JC0JWeixOhprdffE/1yRW/TLMYAAAAASUVORK5CYII=) no-repeat}.be-tags-container{position:absolute;top:4px;right:4px}.be-tags-container .tag.new-tag{background-color:#42a0c4}.be-tags-container .tag{display:inline-block;padding:0 4px;font-size:10px;color:#fff;text-align:center;line-height:14px;border-radius:2px;margin-left:4px}.cube-list{display:flex;flex-wrap:wrap;justify-content:space-around;padding:20px;width:100%;margin-top:20px;box-sizing:border-box}.cube-list .clearfix{display:block}.cube-list .small-item{display:inline-block;width:160px;position:relative;margin:0 0 3px;padding:10px;cursor:pointer}.cube-list .small-item .length{background:#111;background:rgba(0,0,0,0.5);border-radius:5px 0 0 0;color:#fff;line-height:20px;transition:top 0.2s ease;padding:0 6px;position:absolute;right:0;bottom:0}.cube-list .small-item .cover{background:url(//s1.hdslb.com/bfs/static/jinkela/space/assets/video-placeholder.png) 50%;background-size:cover;border-radius:4px;display:block;width:160px;height:100px;overflow:hidden;position:relative}.cube-list .small-item img{border-radius:4px;display:block;width:160px;height:100px}.cube-list .small-item .title{display:block;line-height:20px;height:38px;margin-top:6px;overflow:hidden}.cube-list .small-item .meta{color:#999;white-space:nowrap;font-size:0;margin-top:6px;height:14px;line-height:14px}.cube-list .small-item .meta>span{display:inline-block;white-space:nowrap;height:14px;line-height:14px;font-size:12px;overflow:hidden}.cube-list .small-item .meta>span .icon{width:16px;height:14px;vertical-align:sub}.cube-list .small-item .meta>span:first-child{width:72px}.cube-list .small-item .play .icon{background-position:-280px -25px;display:inline-block}.cube-list .small-item .time .icon{background-position:-280px -474px;display:inline-block}.cube-list a,.cube-list abbr,.cube-list acronym,.cube-list address,.cube-list applet,.cube-list article,.cube-list aside,.cube-list audio,.cube-list b,.cube-list big,.cube-list blockquote,.cube-list body,.cube-list canvas,.cube-list caption,.cube-list center,.cube-list cite,.cube-list code,.cube-list dd,.cube-list del,.cube-list details,.cube-list dfn,.cube-list div,.cube-list dl,.cube-list dt,.cube-list em,.cube-list embed,.cube-list fieldset,.cube-list figcaption,.cube-list figure,.cube-list footer,.cube-list form,.cube-list h1,.cube-list h2,.cube-list h3,.cube-list h4,.cube-list h5,.cube-list h6,.cube-list header,.cube-list hgroup,.cube-list html,.cube-list i,.cube-list iframe,.cube-list img,.cube-list ins,.cube-list kbd,.cube-list label,.cube-list legend,.cube-list li,.cube-list mark,.cube-list menu,.cube-list nav,.cube-list object,.cube-list ol,.cube-list output,.cube-list p,.cube-list pre,.cube-list q,.cube-list ruby,.cube-list s,.cube-list samp,.cube-list section,.cube-list small,.cube-list span,.cube-list strike,.cube-list strong,.cube-list sub,.cube-list summary,.cube-list sup,.cube-list table,.cube-list tbody,.cube-list td,.cube-list tfoot,.cube-list th,.cube-list thead,.cube-list time,.cube-list tr,.cube-list tt,.cube-list u,.cube-list ul,.cube-list var,.cube-list video{margin:0;padding:0;border:0;vertical-align:baseline;word-break:break-word}.be-pager{user-select:none;padding:15px 0;text-align:center}.be-pager-disabled{display:none}.be-pager-next,.be-pager-prev{padding:0 14px;border:1px solid #d7dde4;border-radius:4px;background-color:#fff}.be-pager-item{display:inline-block;line-height:38px;padding:0 15px;margin-right:4px;text-align:center;list-style:none;background-color:#fff;-ms-user-select:none;user-select:none;cursor:pointer;font-family:Arial;font-size:14px;border:1px solid #d7dde4;border-radius:4px;transition:all 0.2s ease-in-out}.be-pager-item-jump-next,.be-pager-item-jump-prev,.be-pager-next,.be-pager-prev{display:inline-block;font-size:14px;line-height:38px;list-style:none;text-align:center;cursor:pointer;color:#666;font-family:Arial;transition:all 0.2s ease-in-out}.be-pager-next,.be-pager-prev{padding:0 14px;border:1px solid #d7dde4;border-radius:4px;background-color:#fff}.be-pager-prev{margin-right:8px}.be-pager-item a{text-decoration:none;color:#657180}.be-pager-item-active{background-color:#00a1d6;border-color:#00a1d6}.be-pager-item-active:hover a,.be-pager-item-active a{color:#fff}.be-pager-item-active:hover a,.be-pager-item-active a{color:#fff}.be-pager-total{display:inline-block;height:32px;line-height:32px;margin-left:30px;color:#99a2aa}.be-pager-options-elevator{display:inline-block;height:32px;line-height:32px;color:#99a2aa}.be-pager-options-elevator input{border-radius:4px;margin:0 8px;width:50px;outline:none}.space_input{line-height:28px;height:28px;padding:0 10px;transition:all 0.3s ease;vertical-align:top;border:1px solid #ccd0d7;border-radius:0}
`)
})();