B站观看内容统计-我的时间不见了-不完全修改版

在b站网页首页获取历史观看记录近三个月,每次刷新首页会自动获取并存储数据,可以到浏览器控制台 F12 查看

当前为 2023-02-20 提交的版本,查看 最新版本

// ==UserScript==
// @name         B站观看内容统计-我的时间不见了-不完全修改版
// @version      0.0.5
// @description  在b站网页首页获取历史观看记录近三个月,每次刷新首页会自动获取并存储数据,可以到浏览器控制台 F12 查看
// @author       strangeZombies
// @namespace    https://www.github.com/strangeZombies
// @match        https://www.bilibili.com
// @match        https://www.bilibili.com/?*
// @require      https://static.hdslb.com/js/jquery.min.js
// @icon         https://static.hdslb.com/images/favicon.ico
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_listValues
// @grant        GM_deleteValue


// ==/UserScript==
/* globals $ */
 /* jshint esversion: 8 */
//GM_deleteValue("lishijilulist");
//

// 版本 latest - script - bilibili - history - ajax - 202302201449.js
// 原作 判官喵的B站观看内容统计-我的时间都去哪了
// 目前 将其页面跳转获取历史记录更改为Ajax异步的方式
// 未完成 统计页面模块 下载历史记录模块
// 使用方法 f12控制台复制对象 |在本脚本使用函数GM_getvalue('lishijilulist')
// 建议加装 ClearURLs 扩展插件
// 由于能力有限不保证脚本出厂质量 | 我自己用好像没问题 | 也许会更新

(function () {
    'use strict';
    //创建不可见元素及下载事件
    function funDownload(content, filename) {
        // 创建隐藏的可下载链接
        let eleLink = document.createElement('a');
        eleLink.download = filename;
        eleLink.style.display = 'none';
        // 字符内容转变成blob地址
        let blob = new Blob([JSON.stringify(content)], { type: 'text/json' });
        eleLink.href = URL.createObjectURL(blob);
        // 触发点击
        document.body.appendChild(eleLink);
        eleLink.click();
        // 然后移除
        document.body.removeChild(eleLink);
    }

    //创建a标签
    function createAEl(NodeAddtoClass, NodeText, NodeId, NodeHref) {
        let element = document.querySelector(NodeAddtoClass),
            node5a = document.createTextNode(NodeText),
            para5a = document.createElement('a');
        para5a.href = NodeHref;
        para5a.id = NodeId;
        para5a.appendChild(node5a);
        element.appendChild(para5a);
    }

    //下载 ajax get请求携带cookie Json
    async function getOneAjax(addr) {
        try {
            let res = await $.ajax({
                type: "get",
                url: addr,
                dataType: "json",
                async: true,
                // 允许请求携带cookie
                xhrFields: {
                    withCredentials: true
                }
            }).then(json => { return json; })
            return res;
        } catch (err) {
            return err;
        }
    }

    //读取缓存模块 返回string->json格式化缓存内容
    // "lishijilulist"
    function GMgetStrToJson(cacheName) {
        // 初始字符
        let cacheJson, cacheStr;
        // 获取缓存
        cacheStr = GM_getValue(cacheName);
        // 如果存在缓存则格式化为Json并返回
        if (cacheStr == undefined) {
            cacheJson = cacheStr; // 如果不存在则为 undifined
        } else {
            cacheJson = JSON.parse(cacheStr);
        }
        return cacheJson;
    }

    //存储缓存列表模块 Json
    function GMsetJson(cacheName, cacheValue) {
        let cacheValueTemp = JSON.stringify(cacheValue);
        GM_setValue(cacheName, cacheValueTemp);
    }

    //初始化标头存储模块
    function headerCache(firstCache, preJson, cacheBucket,i) {
        let getId, getViewAt, PerJsonBucketLen;
        PerJsonBucketLen = preJson.data.list.length;
        if (PerJsonBucketLen != 0) {
            //提取最后观看数据项oid和观看时间
            getId = preJson.data.list[0].history.oid;
            getViewAt = preJson.data.list[0].view_at;
        }

        //判断是否为首次缓存
        if (firstCache == 1) {
            if (cacheBucket.length == 0) {
                console.log("首次获取数据时间较长请耐心等待!")
                //是首次缓存则 添加标头项首次缓存标记,最后观看oid和时间数据,返回存储列表
                cacheBucket[0] = ({ first_cache: firstCache, last_oid: getId, last_view_at: getViewAt, thisCache_len: 0 })
            }
            return cacheBucket;
        } else {
            //非首次缓存存储最后观看数据项
            //是则 把此页最后观看数据添加到标头项备用栏,返回存储列表
            cacheBucket[0].beiyong_last_oid = getId;
            cacheBucket[0].beiyong_last_view_at = getViewAt;
            cacheBucket[0].bencicunchu_len = 0;
            return cacheBucket;
        }
    }

    //需要被循环执行的模块
    //prejson数据添加到存储列表模块
    function preJsonToCacheBucket(preJson, i, cacheBucket) {
        //每个视频bvid 每个视频时长 每个视频观看时长 每个视频观看时间
        let every_oid, //视频是av号,专栏是cv号,直播是直播间号
            every_author_mid, //up主uid
            every_author_name, //up主名字
            every_badge, //此条记录的类型
            every_title, //标题
            every_duration, //视频时长
            every_progress, //观看时长
            every_view_at; //观看时间
        //开始赋值
        every_oid = preJson.data.list[i].history.oid;
        every_author_mid = preJson.data.list[i].author_mid;
        every_author_name = preJson.data.list[i].author_name;
        every_badge = preJson.data.list[i].badge;
        every_title = preJson.data.list[i].title;
        every_duration = preJson.data.list[i].duration;
        every_progress = preJson.data.list[i].progress;
        every_view_at = preJson.data.list[i].view_at;
        //判断是否有重复内容
        //寻找相同观看时间内容
        let sure = cacheBucket.find(i => i.view_at === every_view_at);
        //获取本次存储计数
        let bencicunchu_len = cacheBucket[0].bencicunchu_len;
        //如果没有找到则 添加数据后返回存储列表
        if (sure == undefined) {
            //console.log('没找到');
            cacheBucket.push({ oid: every_oid, author_mid: every_author_mid, author_name: every_author_name, badge: every_badge, title: every_title, duration: every_duration, progress: every_progress, view_at: every_view_at });
            cacheBucket[0].bencicunchu_len = bencicunchu_len + 1;
            return cacheBucket;
        } else {
            //console.log('有找到')
            //有找到则 不做处理直接返回存储列表
            return cacheBucket;
        }
    }

    //处理json数据加入存储列表模块  调用preJsonToCacheBucket
    function jsonToCacheBucket(preJson, cacheBucket) {
        //提取首次存储标记
        let firstCache = cacheBucket[0].first_cache;
        //提取json数据列表长度
        let preJsonListLen = preJson.data.list.length;
        //提取记录的最后一个观看时间
        let lastViewAt = cacheBucket[0].last_view_at;
        //提取记录的备用最后一个观看时间
        let backupLastViewAt = cacheBucket[0].beiyong_last_view_at
        let backupLastOid = cacheBucket[0].beiyong_last_oid;
        //列表执行计数
        let i;
        //获取时间戳
        let tistime = Date.now();
        //判断数据长度是否为0
        if (preJsonListLen == 0) {
            //如果没有数据则是提取到最后一页,将首次存储状态改为0无效,并存储 返回下页状态为0
            cacheBucket[0].firstcunchu = 0;
            cacheBucket[0].last_jiancha_time = tistime;
            GMsetJson('lishijilulist', cacheBucket);
            console.log("最后一条");
            return 0;
            //返回状态0不再进行下个页面获取
        } else {
            //有数据则判断是否为首次缓存
            if (firstCache == 1) {
                //首次缓存直接循环执行 api页面json数据添加到存储列表模块
                for (i = 0; i < preJsonListLen; i++) {
                    cacheBucket = preJsonToCacheBucket(preJson, i, cacheBucket);
                }
                GMsetJson('lishijilulist', cacheBucket);
                //返回状态1继续进行下个页面获取
                return 1;
            } else {
                //非首次缓存则,判断缓存最后一个观看时间记录能否比页面数据的观看时间记录更小
                //对比缓存的最后一个观看时间与记录列表时间大小
                for (i = 0; i < preJsonListLen; i++) {
                    // 之前的时间小于现在获取的时间
                    if (lastViewAt < preJson.data.list[i].view_at) {
                        //如果时间小于列表时间则加入存储列表
                        cacheBucket = preJsonToCacheBucket(preJson, i, cacheBucket);
                    } else if (lastViewAt == preJson.data.list[i].view_at) {
                        //如果时间等于列表时间则停止获取,将备用最后时间添加到存储列表的最后时间,直接存储已有列表
                        cacheBucket[0].last_view_at = backupLastViewAt;
                        cacheBucket[0].last_oid = backupLastOid;
                        cacheBucket[0].last_jiancha_time = tistime;
                        GMsetJson('lishijilulist', cacheBucket);
                        return 0;
                        //返回状态0不再进行下个页面获取
                    } else {

                        //如果时间大于列表时间则停止获取,将备用最后时间添加到存储列表的最后时间,直接存储已有列表
                        cacheBucket[0].last_jiancha_time = tistime;
                        GMsetJson('lishijilulist', cacheBucket);
                        return 0;
                        //返回状态0不再进行下个页面获取
                    }
                }
                //整页获取完后存储,返回状态1继续进行下个页面获取
                GMsetJson('lishijilulist', cacheBucket);
                return 1;
            }
        }
    }

    //获取一组数据
    async function ajaxOneHistory(maxId, viewAt, businessId) {
        let url = `https://api.bilibili.com/x/web-interface/history/cursor?max=${maxId}&view_at=${viewAt}&business=${businessId}`;
        console.log('正在获取',url);
        await new Promise(resolve => setTimeout(resolve, 100));
        let data = await getOneAjax(url);
        return data;
    }


    async function ajaxHistory() {
        //首次缓存标记,1有效,0无效 | 读取列表  | 存入列表 | 获取列表
        let originCacheBucket, cacheBucket =[], preJson, firstCache;
        //读取缓存
        originCacheBucket = GMgetStrToJson('lishijilulist');
        cacheBucket = originCacheBucket; // 如果没有则此处为undifined;

        //判断缓存是否存在
        if (originCacheBucket == undefined) {
            //不存在则执行以下
            //是首次缓存
            firstCache = 1;
            cacheBucket = [];
        } else {
            firstCache = 0;
        }
        let nextCache = 1;
        let ii = 1;
        let maxId = 0, viewAtId = 0, businessId = '';
        preJson = await ajaxOneHistory(maxId, viewAtId, businessId).then(preJson => { return preJson });
        cacheBucket = headerCache(firstCache, preJson, cacheBucket, ii);
        GMsetJson('lishijilulist', cacheBucket);
        while (nextCache == 1) {
            ii++;
            preJson = await ajaxOneHistory(maxId, viewAtId, businessId).then(preJson => { return preJson });
            nextCache = jsonToCacheBucket(preJson, cacheBucket);
            maxId = preJson.data.cursor.max;
            viewAtId = preJson.data.cursor.view_at;
            businessId = preJson.data.cursor.business;
            console.log('获取次数', ii);
        }
        console.log('观看记录',cacheBucket)
    }

    //添加基本页面元素
    function historyGUI() {
        //createAEl('.bili-header', '下载观看历史', 'myhis', 'javascript:void(0);');
    }

    //完成基本命令
    function baseCommand() {
        ajaxHistory();
    }

    if (unsafeWindow.location.href.indexOf('bilibili.com') != -1) {
            //  historyGUI();
            baseCommand();
    }
})();