// ==UserScript==
// @name MatchedVideosOfBili
// @namespace http://tampermonkey.net/
// @version 0.1
// @description To find the matched videos posted by followings
// @author Ouphi
// @match https://search.bilibili.com/*
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant none
// @license MPL
// ==/UserScript==
var cnt = 0
// 读取预处理好的关注列表文件(内容为uid)
var href = "https://space.bilibili.com/208259"
var iframe
var dm
var uids
var videos_target
var videos_src
var author_name_src
var n_pages_src
var btn_next_src
var btn_next_target
// 主页面翻页时的临时信息
var saved = 0
var saved_up_i
var saved_page_i
var saved_source_i
var saved_target_page
var saved_video_target_href
// 子页面翻页时的临时信息
var saved_video_src_href
init()
function init(){
// 等待主页面加载完成
if(checkElement(document, 'class', "vui_tabs--nav vui_tabs--nav-pl0") == null){
setTimeout(function(){init()}, 100)
return
}
console.log('1 ---- 加载成功')
// 添加子网页,读取关注列表
run_init(0, 1, document, 'class', "header-entry-avatar")
}
// flag=0代表需要继续等待,其他数字代表继续,
function run_init(flag, next_flag, element = null, method = null, text = null) {
// 限制访问次数,在连续搜索不到内容时,由于不同up的页面加载、run0的增加,会触发边界
cnt++
if(cnt >= 2000){ return }
console.log('1 ---- flag, next_flag : ' + flag + "," + next_flag)
// 每隔0.1s判断一次加载进度
if(flag == 0){
if(checkElement(element, method, text) == null){
if(next_flag == 2 || next_flag == 6 || next_flag == 8){
// 需要访问子页面的flag
// 不能直接传之前的document,只能动态更新
dm = document.getElementById('child-iframe').contentDocument
setTimeout(function(){run_init(0, next_flag, dm, method, text)}, 100)
}
else
// 需要访问主页面的flag
setTimeout(function(){run_init(0, next_flag, element, method, text)}, 100)
}
else{
run_init(next_flag, next_flag, element, method, text)
}
}
// 第一段,得到当前用户uid,创建follow page
if(flag == 1){
run1GetAndFixPage()
if(saved == 0)
run_init(0, 2, dm, 'id', "download-following-list-button")
else
run_init(0, 11, window.document, 'class', "vui_button--active-blue")
}
// 第二段,点击download按钮,等待follow page的信息
if(flag == 2){
run2ClickDownButton()
run_init(0, 3, dm, 'id', "following-list-text")
}
// 第三段,得到following list信息
if(flag == 3){
run3GetFollowingUids()
run_init(0, 4, window.document, 'class', "bili-video-card__wrap __scale-wrap")
}
// 第四段,得到主页面的视频列表
if(flag == 4){
run4GetTargetVideos()
if(saved == 1) fixBannerTarget()
if(saved == 1) run_continue()
}
// 第十一段,判断主页面加载完成
if(flag == 11){
if(run11CheckVideosTarget() == 1){
setTimeout(function(){run_init(0, 11, window.document, 'class', "vui_button--active-blue")}, 100)
}
else{
run_init(0, 4, window.document, 'class', "bili-video-card__wrap __scale-wrap")
}
}
}
function run_continue(){
// init后主页面加载完成,save前子页面加载完成,所以可以直接用
run_search(10, 10, document, '', '', 0, saved_up_i, saved_page_i, saved_source_i)
}
// flag=0代表需要继续等待,其他数字代表继续,
function run_search(flag, next_flag, element = null, method = null, text = null, target_i = 0, up_i = 0, page_i = 0, source_i = 0) {
cnt++
if(cnt >= 2000){ return }
var result
console.log('1 ---- flag, next_flag : ' + flag + "," + next_flag)
console.log('1 ---- target_i, up_i, page_i, source_i : ' + target_i + "," + up_i + "," + page_i + "," + source_i)
// 上条需要等待加载完成后再读取
// window.onload只能识别header条,不识别内容加载程度
// 每隔0.1s判断一次加载进度
if(flag == 0){
if(checkElement(element, method, text) == null){
// 带参数的需要写在函数内,不然会出错
if(next_flag == 2 || next_flag == 6 || next_flag == 8){
// 不能直接访问document,只能通过传值
dm = document.getElementById('child-iframe').contentDocument
setTimeout(function(){run_search(0, next_flag, dm, method, text, target_i, up_i, page_i, source_i)}, 100)
}
else{
setTimeout(function(){run_search(0, next_flag, element, method, text, target_i, up_i, page_i, source_i)}, 100)
}
}
else{
run_search(next_flag, next_flag, element, method, text, target_i, up_i, page_i, source_i)
}
}
// 第五段,判断并跳转到每个up主的视频页,等待读取
if(flag == 5){
if(run5ReloadChildPage(up_i) == 0)
run_search(0, 6, dm, 'id', "submit-video-type-filter", target_i, up_i, 0, 0)
}
// 读取当前up主信息
if(flag == 6){
result = run6GetUpVideosInfo(up_i)
if(result == 0) // 判断成功,继续运行
run_search(8, 8, dm, 'class', "small-item fakeDanmu-item", target_i, up_i, 0, 0)
else if(result == 1) // 等待当前页面加载
setTimeout(function(){run_search(0, 6, dm, 'id', "submit-video-type-filter", target_i, up_i, 0, 0)}, 100)
else if(result == 2) // 搜索内容为空,结束当前up主
run_search(5, 5, element, method, text, target_i, up_i+1, 0, 0)
}
// 第七段,判断并跳转到当前up主的下一页,等待读取
if(flag == 7){
// TODO: 冗余判断
if(run7ClickNextPage(page_i) == 1){ // 页数读取完成,结束当前up主
run_search(5, 5, element, method, text, target_i, up_i + 1, 0, 0)
}else{ // 读取下一页
run_search(0, 8, dm, 'class', "small-item fakeDanmu-item", target_i, up_i, page_i, 0)
}
}
// 考虑是空的情况啊啊啊
// 第六段,读取当前页面的视频列表
if(flag == 8){
var result = run8GetSrcVideos(up_i)
if(result == 1)
setTimeout(function(){run_search(8, 8, dm, 'class', "small-item fakeDanmu-item", target_i, up_i, page_i, 0)}, 100)
else
run_search(9, 9, element, method, text, target_i, up_i, page_i, 0)
}
// 第七段,判断结束条件
if(flag == 9){
var result = run9checkBoundary(target_i, up_i, page_i, source_i)
if(result == 0) // 判断成功,继续运行
run_search(10, 10, element, method, text, target_i, up_i, page_i, source_i)
else if(result == 2) // 结束当前up主
run_search(5, 5, element, method, text, target_i, up_i+1, 0, 0)
else if(result == 3) // 结束当前页面
run_search(7, 7, element, method, text, target_i, up_i, page_i + 1, 0)
}
// 第八段,循环
if(flag == 10){
run10ReplaceSingleVideo(target_i, up_i, source_i)
run_search(9, 9, element, method, text, target_i+1, up_i, page_i, source_i+1)
}
}
/***
* 运行流程相关函数
***/
// 1.得到页面信息,并添加子页面
function run1GetAndFixPage(){
// 得到用户uid
href = checkElement(document, 'class', "header-entry-avatar")[0].attributes['href'].nodeValue
var patt = /.+\/([0-9]+).*/
var uid = regGroup(patt, href)
console.log('1 ---- flag1 ' + '当前用户:' + '\r' + uid)
// 初次访问
if(saved == 0){
// 创建关注页面
/********* 如果需要设置分组,请确保分组内有人,可以修改这里的后缀?tagid=xxx ************ */
var src = "https://space.bilibili.com/"+ uid + "/fans/follow"
addChildIframe(src)
}
}
// 2.点击下载按钮
function run2ClickDownButton(){
// 不再重复下载
var btn_down = checkElement(dm, 'id', "download-following-list-button")
btn_down.click()
}
// 3.获取关注列表信息
function run3GetFollowingUids(){
var t_uids = checkElement(dm, 'id', "following-list-text").textContent
uids = t_uids.split('\n')
// 校验内容是否读取正确
if((uids.length - 1).toString() != uids[0]){
console.log('1 ---- flag3 uid wrong\n')
}
else
uids.shift()
// 预处理结束,增加筛选关注up主视频的按钮
addFollowLi()
}
// 4.获取目的视频列表(主页面)
function run4GetTargetVideos(){
var main_target = checkElement(window.document, 'class', "video-list row")[0]
videos_target = checkElement(main_target, 'class', "bili-video-card__wrap __scale-wrap")
videos_target = selectNotHideElement(videos_target)
}
// 5.将子页面设为当前up主的搜索页面,重新加载
function run5ReloadChildPage(i){
// 判断结束条件
if(i >= uids.length){
endButton(btn_next_target)
console.log('1 ---- flag5 ' + "The End:全部up主的视频读取完毕")
return 1
}
var patt = /.+keyword=([^&/]+).*/
var word = regGroup(patt, window.location.href)
console.log('1 ---- flag5 ' + '搜索词:' + '\r' + word)
var src = 'https://space.bilibili.com/' + uids[i] + '/search/video?keyword=' + word
iframe.remove()
addChildIframe(src)
return 0
}
// 6.当前up主的初始信息
function run6GetUpVideosInfo(up_i){
// 双重检验,确保加载成功
// TODO:由于remove重进了,可以不加这层判断
var src_videos_text_2 = dm.getElementsByClassName('v-search-count')[0].textContent
console.log('1 ---- flag6 ' + "当前up匹配的视频数2-", src_videos_text_2)
var src_videos_text = dm.getElementById('submit-video-type-filter').children[0].children[0].textContent
console.log('1 ---- flag6 ' + "当前up匹配的视频数--", src_videos_text)
if(src_videos_text_2 != src_videos_text || isNaN(src_videos_text_2) ||isNaN(src_videos_text)){
return 1
}else if(src_videos_text == '0'){
return 2
}
var t_pages = dm.getElementsByClassName("be-pager-total")[0].textContent
n_pages_src = regGroup(/.+([0-9]+).+/, t_pages)
console.log('2 ---- ' + '当前up总页数:' + ' ' + n_pages_src)
if(n_pages_src == '0'){
return 2
}
// 再检验,确保加载为新up
dm = document.getElementById('child-iframe').contentDocument
if(dm == null){return 1}
href = dm.getElementsByClassName('text router-link-exact-active router-link-active')[0].attributes['href'].nodeValue
btn_next_src = dm.getElementsByClassName('be-pager-next')[0]
var patt = /.*\/([0-9]+)\/.*/
var uid = regGroup(patt, href)
if(uid != uids[up_i]){return 1}
return 0
}
// 当前up主的下一页
function run7ClickNextPage(page_i){
if(page_i >= n_pages_src) return 1
btn_next_src.click()
return 0
}
// 8.获取源视频列表(子页面)
function run8GetSrcVideos(i){
// 获取源视频列表
videos_src = checkElement(dm, 'class', "small-item fakeDanmu-item")
var e_video_href = checkElement(videos_src[0], 'class', "title")
if(e_video_href == null)
return 1
var video_src_href = e_video_href[0].attributes['href'].nodeValue
if(saved_video_src_href == video_src_href)
return 1
author_name_src = dm.getElementById('h-name')
return 0
}
// 9.判断越界情况
function run9checkBoundary(target_i, up_i, page_i, source_i){
console.log('1 ---- flag9 ' + "target--" + videos_target.length + ":" + target_i + " source" + up_i +"-" + page_i + "--" + videos_src.length + ":" + source_i)
if(page_i >= n_pages_src){
console.log('1 ---- flag9 ' + "Mid:当前up主的所有页面读取完毕")
return 2
}
if(source_i >= videos_src.length){
console.log('1 ---- flag9 ' + "Mid:当前up主的当前页面读取完毕")
return 3
}
if(target_i >= videos_target.length){
saveTargrtInfo(up_i, page_i, source_i)
btn_next_target.style.display = 'block'
console.log('1 ---- flag9 ' + "当前主页面加载完成")
return 1
}
return 0
}
// 10.替换单个视频内容
function run10ReplaceSingleVideo(target_i , i, source_i){
console.log('1 ---- flag10 ' + "target--" + videos_target.length + ":" + target_i + " source" + i + "--" + videos_src.length + ":" + source_i)
var video_src = videos_src[source_i]
var img_src = video_src.children[0].getElementsByTagName('img')[0]
var title_src = video_src.getElementsByClassName('title')[0]
var banner_time_src = video_src.getElementsByClassName('time')[0]
var banner_duration_src = video_src.getElementsByClassName('length')[0]
var banner_playback_src = video_src.getElementsByClassName('play')[0]
var video_target = videos_target[target_i]
var img_target = video_target.children[0].getElementsByTagName('img')[0]
var img_source_target = video_target.children[0].getElementsByTagName('source')[0]
var title_target = video_target.children[1].getElementsByTagName('a')[0]
var author_link_target = video_target.getElementsByClassName('bili-video-card__info--owner')[0]
var author_name_target = video_target.getElementsByClassName('bili-video-card__info--author')[0]
var banner_time_target = video_target.getElementsByClassName('bili-video-card__info--date')[0]
var banner_duration_target = video_target.getElementsByClassName('bili-video-card__stats__duration')[0]
var banner_playback_target = video_target.getElementsByClassName('bili-video-card__stats--item')[0].getElementsByTagName('span')[0]
var banner_barage_target = video_target.getElementsByClassName('bili-video-card__stats--item')[1]
var player_target = video_target.getElementsByClassName('v-inline-player')[0]
var later_target = video_target.getElementsByClassName('bili-watch-later')[0]
img_target.parentElement.replaceChild(img_src, img_target)
video_target.children[0].href = title_src.href
author_link_target.href = iframe.src
author_name_target.textContent = author_name_src.textContent
img_source_target.srcset = img_src.src
title_target.parentElement.replaceChild(title_src, title_target)
banner_playback_target.textContent = banner_playback_src.textContent
banner_duration_target.textContent = banner_duration_src.textContent
banner_time_target.textContent = banner_time_src.textContent
player_target.style.display = 'none'
later_target.style.display = 'none'
banner_barage_target.style.display = 'none'
}
// 11.当前页面的信息
function run11CheckVideosTarget(){
// 双重检验,确保加载成功
var target_page = window.document.getElementsByClassName('vui_button--active-blue')[0].textContent
if(target_page == saved_target_page)
return 1
var main_target = checkElement(window.document, 'class', "video-list row")[0]
var video_target = checkElement(main_target, 'class', "bili-video-card__wrap __scale-wrap")[0]
var video_target_href = video_target.children[0].attributes['href'].nodeValue
if(saved_video_target_href == video_target_href)
return 1
var video_later = checkElement(video_target, 'class', 'bili-watch-later')
if(video_later == null)
return 1
return 0
}
/****
* 关注按钮相关函数
*****/
// 增加按钮到搜索栏末尾
function addFollowLi(){
var ul = document.getElementsByClassName("vui_tabs--nav vui_tabs--nav-pl0")[0]
const li = ul.getElementsByClassName("vui_tabs--nav-item")[0]
var follow_li= li.cloneNode(true)
follow_li.getElementsByClassName("vui_tabs--nav-text")[0].innerText = "关注"
follow_li.attributes['class'].nodeValue = "vui_tabs--nav-item"
follow_li.id = 'follow_btn'
follow_li.onclick = function(){clickFollowLi(follow_li)}
ul.appendChild(follow_li)
}
// 选择搜索关注内容,关注的元素内容修改
function clickFollowLi(follow_li){
// 更改活跃显示
var active_li = document.getElementsByClassName("vui_tabs--nav-item-active")[0]
active_li.attributes['class'].nodeValue = "vui_tabs--nav-item"
follow_li.attributes['class'].nodeValue = "vui_tabs--nav-item vui_tabs--nav-item-active"
fixBannerTarget()
// 开始搜索并取代原内容
run_search(5, 5, document, '', '', 0, 0, 0, 0)
}
function endButton(btn){
console.log(btn)
btn.style.display = 'block'
btn.textContent = "结束"
btn.disabled = true
}
/****
* 元素处理函数
*****/
// 得到关注页面,以及各up主的搜索页面
function addChildIframe(src){
var e_parent_iframe = document.body
iframe = document.createElement('iframe')
iframe.src = src
iframe.style.width = '500px'
iframe.style.height = '500px'
e_parent_iframe.appendChild(iframe, e_parent_iframe)
iframe.id = 'child-iframe'
dm = iframe.contentDocument
}
// 筛选未隐藏的视频
function selectNotHideElement(list){
var result = new Array()
for( var i = 0; i < list.length; ++i){
// 这里使用函数,所以不需要nodeValue
if(list[i].parentElement.parentElement.getAttribute('class').indexOf("to_hide_xs") == -1){
result.push(list[i])
}
}
return result
}
function clickNextButtonTarget(btn){
btn.style.display = 'none'
init()
}
function fixBannerTarget(){
// 删去细化筛选栏,综合/视频/番剧/用户栏;会固定按照关注顺序依次显示
if(checkElement(document, 'class', "search-header") != null){
var strategy_banner = document.getElementsByClassName("search-header")[0].children[3]
strategy_banner.style.display = "none"
}
// 删去选页栏,只留下一页;滚动显示,不保留全部内容
// TODO:也可以预处理多花些事件,获取所有视频列表后,排序显示,可选页
if(checkElement(document, 'class', "vui_pagenation--btns") != null){
var page_btns = document.getElementsByClassName("vui_pagenation--btns")[0]
for(var i = 0; i < page_btns.children.length - 1; ++i){
strategy_banner.style.display = "none"
page_btns.children[i].style.display = "none"
}
btn_next_target = page_btns.children[page_btns.children.length - 1]
btn_next_target.style.display = 'none'
btn_next_target.onclick = function(){clickNextButtonTarget(btn_next_target)}
}
}
/****
* 等待相关函数
****/
// 检查所需元素是否已加载成功
function checkElement(element, method, text){
if(element == null) return null
var result = null
switch(method){
case 'class':
result = element.getElementsByClassName(text)
break
case 'tag':
result = element.getElementsByTagName(text)
break
case 'id':
result = element.getElementById(text)
break
default:
result = element.getElementsByClassName(text)
break
}
// console.log(result)
if(result != null && result != undefined && result.length != 0){
return result
}else{
return null
}
}
/****
* 一些小函数
****/
// 正则匹配
function regGroup(patt, text){
var matched_str = text.match(patt)
var result = RegExp.$1
return result
}
// 保存过去信息:主页面翻页时
function saveTargrtInfo(up_i, page_i, source_i){
saved = 1
saved_up_i = up_i
saved_page_i = page_i
saved_source_i = source_i
saved_target_page = window.document.getElementsByClassName('vui_button--active-blue')[0].textContent
saved_video_target_href = videos_target[0].children[0].attributes['href'].nodeValue
}
// 保存过去信息:子页面翻页时
function saveSrcInfo(){
saved_video_src_href = videos_src[0].getElementsByClassName('title')[0].attributes['href'].nodeValue
}