MatchedVideosOfBili

To find the matched videos posted by followings

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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
}