bilibili默认开启ai字幕

2023/3/29

// ==UserScript==
// @name        bilibili默认开启ai字幕
// @namespace   Violentmonkey Scripts
// @match         *://www.bilibili.com/video/av*
// @match         *://www.bilibili.com/video/BV*
// @match         *://www.bilibili.com/list/*
// @match         *://www.bilibili.com/bangumi/play/ep*
// @match         *://www.bilibili.com/bangumi/play/ss*
// @match         *://www.bilibili.com/cheese/play/ep*
// @match         *://www.bilibili.com/cheese/play/ss*
// @grant       none
// @version     1.250
// @author      lazy cat
// @description 2023/3/29
// @license MIT
// @homepageURL      https://greasyfork.org/zh-CN/scripts/462859-bilibili%E9%BB%98%E8%AE%A4%E5%BC%80%E5%90%AFai%E5%AD%97%E5%B9%95
// @homepage   https://greasyfork.org/zh-CN/scripts/462859-bilibili%E9%BB%98%E8%AE%A4%E5%BC%80%E5%90%AFai%E5%AD%97%E5%B9%95
// @grant        GM_registerMenuCommand
// ==/UserScript==
//判断字幕是否为ai生成
function ai_judgment(subtitle_button) {
    var subtitle_state = document.querySelector('div[class="bpx-player-ctrl-subtitle-language-item bpx-state-active"]')
    //判断字幕列表是否不为空
    if (subtitle_state){
        var subtitle_name = subtitle_state.innerText
        var state_num = Number(subtitle_name.indexOf('自动'))
        this.subtitle_button = subtitle_button
        if (state_num != -1){
            // 判断字幕是否开启
            if (document.querySelectorAll('svg[preserveAspectRatio="xMidYMid meet"] > defs > filter').length === 3){
                this.subtitle_button.click()
                // 读取并应用本地记忆的字幕位置
                setTimeout(() => {
                    read_sub_path()
                }, 500);
            }
            console.log('字幕已开启')
        }
        else {
            console.log('字幕不为ai生成,跳过该视频')
        }
    }
    else {
        console.log('该视频无字幕')
     }
}
// 开启字幕函数
function open_subtitle(open_num, timeout) {
    var subtitle_button = document.querySelector('[aria-label="字幕"] [class="bpx-common-svg-icon"]')
    if (subtitle_button){
        clearInterval(open_num)
        clearTimeout(timeout)
    	ai_judgment(subtitle_button)
        // 开始检测字幕位置
        if (localStorage.getItem('sub_mod')) {
            monitoring_sub()
            }
        }
}
//判断视频是否加载完毕
function voice_logding(run_num) {
    if (document.querySelector('video[crossorigin="anonymous"]').readyState == 4){
        console.log('视频加载完毕')
        //设定超时
        var timeout = setTimeout(function() {
            console.log('字幕加载超时,当前视频可能无字幕')
            clearInterval(open)
            open = null
        }, 5000)
        //循环直到字幕加载完成或超时
        var open = setInterval(function () {
            open_subtitle(open, timeout)
        }, 200)
        //超时,停止open循环
        clearInterval(run_num)
        //一定时间后进行判断,防止页面url改变
        setTimeout(list_judgment, 10000)
    }
}
function list_judgment() {
    //循环检查页面是否改变
    setInterval(() => {
        new_url = String(window.location.href)
        if (new_url !== url){
            open_subtitle()
            url = new_url
        }
    }, 1000);
}
// 移动字幕防止更改
function move_sub(num) {
    // 获取字幕元素
    let sub_elem = document.querySelector('div[class="bpx-player-subtitle-panel-major-group"]')
    // 获取元素位置信息
    let sub_now_path = sub_elem.getBoundingClientRect()
    // 创建鼠标点击事件
    let sub_down = new MouseEvent('mousedown', {
        bubbles: true,
        cancelable: true,
        clientX: sub_now_path.left,
        clientY: sub_now_path.top
    })
    // 创建鼠标移动事件
    let sub_move = new MouseEvent('mousemove', {
        bubbles: true,
        cancelable: true,
        clientX: sub_now_path.left,
        clientY: sub_now_path.top - num
    })
    // 创建鼠标释放事件
    let sub_up = new MouseEvent('mouseup', {
        bubbles: true,
        cancelable: true,
        clientX: sub_now_path.left,
        clientY: sub_now_path.top - num
    })
    // 应用各个事件
    sub_elem.dispatchEvent(sub_down)
    sub_elem.dispatchEvent(sub_move)
    sub_elem.dispatchEvent(sub_up)
}
// 储存字幕位置
function save_sub_path() {
    let sub_path = document.querySelector('div[class="bpx-player-subtitle-panel-position"]').getAttribute('style')
    // 使用正则表达式筛选top值
    let re = /top: (\d+)px/
    let top_num = sub_path.match(re)[1]
    // 判断当前窗口是否活动
    if (!document.hidden){
        // 储存字幕位置到本地
        localStorage.setItem('top_num', top_num)
    }
}
// 读取并应用字幕位置
function read_sub_path() {
    let locat_top_num = localStorage.getItem('top_num')
    if (locat_top_num && localStorage.getItem('sub_mod')){
        move_sub(1)
        move_sub(-1)
        // 应用字幕位置
        let sub_path = document.querySelector('div[class="bpx-player-subtitle-panel-position"]').getAttribute('style')
        // 使用正则表达式筛选top值
        let re = /top: (\d+)px/
        let top_num = sub_path.match(re)[1]
        num = top_num - locat_top_num
        move_sub(num)
        save_sub_path()
    }
}
// 监控字幕位置改变
function monitoring_sub() {
    setInterval(() => {
        let locat_top_num = localStorage.getItem('top_num')
        // 获取失败则代表没有字幕
        try{
            let sub_path = document.querySelector('div[class="bpx-player-subtitle-panel-position"]').getAttribute('style')
            // 使用正则表达式筛选top值
            let re = /top: (\d+)px/
            let top_num = sub_path.match(re)[1]
            // 如果本地保存的位置和目前的位置不相等
            if (locat_top_num != top_num){
                save_sub_path()
            }
        }
        catch (e) {
            return
         }
    }, 2000);
}
// 是否记忆字幕位置
function change_sub_mod() {
    let sub_mod = localStorage.getItem('sub_mod')
    // 关闭记忆字幕
    if (sub_mod) {
        localStorage.removeItem('top_num')
        localStorage.removeItem('sub_mod')
        alert('已关闭记忆字幕位置')
    }
    // 开启记忆字幕
    else {
        // 修改判断变量
        localStorage.setItem('sub_mod', true)
        alert('已开启记忆字幕位置')
     }
}
//开始运行程序
var new_url = ''
// var url = String(window.location.href)
setTimeout(() => {
    url = String(window.location.href)
}, 5000);
//初始运行,在10秒内反复循环直到找到字幕或超时
var run = setInterval(function () {
    voice_logding(run)
}, 200)
// 添加油猴脚本菜单
GM_registerMenuCommand('记忆字幕位置功能开关', change_sub_mod)