在B站看课的进度助手

效果

在这里插入图片描述

代码

BilibiliVideoDurationCrawler

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class BilibiliVideoDurationCrawler {
    private static final Pattern VIDEO_PART_PATTERN = Pattern.compile("\"part\":\"(.*?)\",\"duration\":(\\d+),");
    /**
     * 主函数:根据视频链接获取视频分P信息并打印每一集的观看进度
     * 参数:args - 传入的命令行参数数组
     */
    public static void main(String[] args) {
        

        // 根据视频链接获取视频分P信息的步骤
        String url = "https://www.bilibili.com/video/BV1834y1676P/";
        List<VideoPart> videoParts = new ArrayList<>();
        try {
            videoParts = getVideoPartsFromUrl(url); // 1. 获取网页源代码并爬取视频信息,转换为视频对象列表
        } catch (IOException | ParseException e) {
            System.err.println("获取视频信息失败:" + e.getMessage());
            return;
        }

        // 打印每一集所在进度的步骤
        if (!videoParts.isEmpty()) {
            for (int i = 0; i < videoParts.size(); i++) {
                String progress = getProgressStr(videoParts, i); // 获取指定集数的观看进度的字符串表示
                System.out.println("p" + (i + 1) + " " + videoParts.get(i).getPart() + " " + progress); // 打印集数、标题和进度
            }
        }
    }

    /**
     * 从给定的URL获取视频分段信息的列表。
     * @param url 需要解析的网页URL,预期包含视频分段的相关信息。
     * @return 返回一个包含视频分段及其持续时间的VideoPart对象列表。
     * @throws IOException 如果在连接或获取网页内容时发生IO异常。
     */
    public static List<VideoPart> getVideoPartsFromUrl(String url) throws IOException, ParseException {
        // 使用Jsoup连接指定URL并获取网页内容,模拟浏览器行为
        Document doc = Jsoup.connect(url).userAgent("Mozilla/5.0").get();

        // 选择网页中所有的<script>元素
        Elements elements = doc.select("script");

        // 用于临时存储匹配到的视频分段信息
        List<String> result = new ArrayList<>();

        // 遍历所有<script>元素,尝试匹配视频分段信息
        for (Element element : elements) {
            Matcher matcher = VIDEO_PART_PATTERN.matcher(element.html());
            while (matcher.find()) {
                // 将匹配到的分段信息以字符串形式添加到result列表中
                result.add("Part: " + matcher.group(1) + ", Duration: " + matcher.group(2));
            }
        }

        // 从result列表中解析出VideoPart对象并添加到videoParts列表中
        List<VideoPart> videoParts = new ArrayList<>();
        // 优化 totalDuration 的计算, 避免重复计算
        long totalDuration = 0;
        for (String str : result) {
            // 分割字符串以获取分段名称和持续时间
            String[] parts = str.split(", ");
            String part = parts[0].split(": ")[1];
            long duration = Long.parseLong(parts[1].split(": ")[1]);
            totalDuration += duration;
            // 创建VideoPart对象并添加到列表
            VideoPart videoPart = new VideoPart(part, duration);
            videoParts.add(videoPart);
        }
        VideoPart.setTotalDuration(totalDuration);
        return videoParts;
    }

    /**
     * 计算给定视频片段列表中前p个片段的进度百分比,并返回格式化后的字符串。
     *
     * @param videoParts 视频片段列表,每个片段包含持续时间。
     * @param p 计算进度时考虑的视频片段数量(从0开始)。
     * @return 返回计算出的进度百分比的字符串表示,保留两位小数。
     */
    public static String getProgressStr(List<VideoPart> videoParts, int p) {
        // 验证参数合法性
        if (videoParts == null || p < 0 || p >= videoParts.size()) {
            throw new IllegalArgumentException("Invalid video parts or index p");
        }

        // 计算所有视频片段的总长度
        long totalLength = VideoPart.getTotalDuration();

        // 计算前p个视频片段的长度总和
        long lengthBeforeP = 0;
        for (int i = 0; i <= p; i++) {
            lengthBeforeP += videoParts.get(i).getDuration();
        }

        // 根据前面计算的长度,计算并返回进度百分比,结果保留两位小数
        double progress = (double) lengthBeforeP / (totalLength == 0 ? 1 : totalLength) * 100;
        return String.format("%.2f%%", progress);
    }
}


VideoPart

import lombok.AllArgsConstructor;
import lombok.Data;

import java.util.Date;

@Data
@AllArgsConstructor
/**
 * 视频部分信息类,用于描述视频的一个片段。
 */
public class VideoPart {
    /**
     * 构造方法,初始化视频片段的信息。
     * @param part 视频片段的标识。
     * @param duration 视频片段的持续时间,单位为秒。
     */
    public VideoPart(String part,long duration){
        this.part = part;
        this.duration = duration;
    }

    public static void setTotalDuration(long totalDuration) {
        VideoPart.totalDuration = totalDuration;
    }

    public static long getTotalDuration() {
        return totalDuration;
    }

    /**
     * 将持续时间转换为需要的时间对象。
     * 该方法将持续时间(秒)转换为Date对象,假设每秒为1000毫秒。
     */
    public void Duration2NeedTime(){
        this.needTime = new Date(duration*1000);
    }
    private String part; // 视频片段标识
    private long duration; // 视频片段持续时间,单位为秒
    private Date needTime; // 视频片段需要的时间,Date对象表示
    private double progress; // 视频片段的进度

    private static long totalDuration =-1; // 所有视频片段的总持续时间,初始值为-1表示未计算
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/531093.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【教程】混淆Dart 代码

什么是代码混淆&#xff1f; 代码混淆是一种将应用程序二进制文件转换为功能上等价&#xff0c;但人类难于阅读和理解的行为。在编译 Dart 代码时&#xff0c;混淆会隐藏函数和类的名称&#xff0c;并用其他符号替代每个符号&#xff0c;从而使攻击者难以进行逆向工程。 Flut…

每日一题:有效的数独

请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 &#xff0c;验证已经填入的数字是否有效即可。 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。&#xff08;请参考示例图&#xff09; 注…

教你构建一个优秀的SD Prompt

构建一个优秀的Prompt 在使用Stable Diffusion AI时,构建一个有效的提示(Prompt)是至关重要的第一步。这个过程涉及到创造性的尝试和对AI行为的理解。这里我会对如何构建一个好的Prompt进行一个总结。 什么是一个好的提示词 构建有效的提示是使用Stable Diffusion AI或其…

职场商务英语口语柯桥外语培训之“手机欠费”用英文怎么说?

大家天天玩手机&#xff0c; 肯定会碰到 “欠费”“没电”“关机” 这些情况&#xff0c; 那么问题来了&#xff0c; 你知道用英语怎么说&#xff1f; 一起来和小编来学习下吧 今天&#xff0c;一起来学习一下吧。 ● 手机欠费 英语怎么说&#xff1f; ● 肯定有同学要…

二手车商的套路

https://www.dongchedi.com/article/7126394624675578405 https://www.dongchedi.com/article/7126394624675578405 现在&#xff0c;有越来越多的人去了解二手车&#xff0c;二手车相对于新车来说&#xff0c;更加的亲民划算。很多新车需要四五十万&#xff0c;而二手车有可…

信息素养与终身学习解锁题目搜索之道的新引擎【文末送书】

文章目录 信息素养&#xff1a;搜索前的准备终身学习&#xff1a;搜索后的深化新引擎的构建与运行 搜索之道&#xff1a;信息素养与终身学习的新引擎【文末送书】 随着互联网的快速发展和信息技术的日益成熟&#xff0c;搜索已经成为获取知识和信息的主要途径之一。然而&#x…

浙大恩特客户资源管理系统 RegulatePriceAction SQL注入漏洞复现

0x01 产品简介 浙大恩特客户资源管理系统是一款针对企业客户资源管理的软件产品。该系统旨在帮助企业高效地管理和利用客户资源,提升销售和市场营销的效果。 0x02 漏洞概述 浙大恩特客户资源管理系统 RegulatePriceAction接口存在 SQL 注入漏洞,攻击者可通过输入恶意 SQL …

47 转置卷积【李沐动手学深度学习v2课程笔记】

1. 转置卷积 卷积层和汇聚层通常会减少下采样输入图像的空间维度&#xff08;高和宽&#xff09;&#xff0c;卷积通常来说不会增大输入的高和宽&#xff0c;要么保持高和宽不变&#xff0c;要么会将高宽减半&#xff0c;很少会有卷积将高宽变大的。可以通过 padding 来增加高…

layui复选框勾选取消勾选事件监听

监听事件放置位置&#xff1a; form.on(checkbox(equipInputClick), function(data){var a data.elem.checked;var val data.value;if(a true){}else{}});html部分 <input lay-filter"equipInputClick" type"checkbox" lay-skin"primary&quo…

Leetcode 23.合并K个升序链表

心路历程&#xff1a; 第一反应是直接暴力求解出来&#xff0c;因为题中也没有关于复杂度的要求。写完发现竟然也通过了&#xff0c;后来发现这道题本来考察的是分治算法&#xff0c;不过能解决问题就行吧。 从评论区看到了一句话很有意思&#xff1a;链表的题能暴力做出来的…

Vue 移动端(H5)项目怎么实现页面缓存(即列表页面进入详情返回后列表页面缓存且还原页面滚动条位置)keep-alive简单使用

一、需求 产品要求&#xff1a;Vue移动端项目进入列表页&#xff0c;列表页需要刷新&#xff0c;而从详情页返回列表页&#xff0c;列表页则需要缓存并且还原页面滚动条位置 二、实现思路 1、使用Vue中的keep-alive组件&#xff0c;keep-alive提供了路由缓存功能 2、因为我项…

优先级队列

优先级队列的基本使用 模拟实现上面的接口函数&#xff0c;优先级队列不是队列&#xff0c;而是类似一个堆一样的东西&#xff0c;我们先来试试它的接口函数是怎么个样子的。 需要包含的头文件是queue。 #include<iostream> #include<queue> using namespace std;…

2024/4/5—力扣—字符串相乘

代码实现&#xff1a; 方法一&#xff1a;常规解法——超出整数表示范围 long long char_to_num(char *str) {long long num 0;for (int i 0; i < strlen(str); i) {num num * 10 (str[i] - 0);}return num; }char* multiply(char *num1, char *num2) {long long a cha…

拥抱智能,IT运维将有哪些变化?

Gartner数据显示&#xff0c;2023年AIOps在中国市场渗透率只达到目标受众的5%-20%。这一数据意味着仍有大量企业还未进行AIOps建设&#xff0c;未来AIOps市场前景广阔。目前&#xff0c;已经开始应用AIOps的企业&#xff0c;智能运维水平普遍还处于辅助智能化运维阶段&#xff…

linux-docker安装nginx

1.拉取镜像&#xff1a; docker pull nginx2.创建挂在路径&#xff1a; mkdir -p /usr/local/nginx/conf mkdir -p /usr/local/nginx/logs mkdir -p /usr/local/nginx/www mkdir -p /usr/local/nginx/conf.d 3.启动镜像:为了拿到位置文件&#xff0c;先启动下 docker run -…

Linux_应用篇(03) 文件 I/O 加强

经过上一章内容的学习&#xff0c;相信各位读者对 Linux 系统应用编程中的基础文件 I/O 操作有了一定的认识和理解了&#xff0c;能够独立完成一些简单地文件 I/O 编程问题&#xff0c; 如果你的工作中仅仅只是涉及到一些简单文件读写操作相关的问题&#xff0c;其实上一章的知…

spring框架构成

spring框架构成 模块 spring中集成了多个模块&#xff0c;包含有核心容器、数据访问、web、AOP等模块 核心容器包含有Spring Core、Spring Beans、Spring Context和EL模块 Spring Core spring的核心&#xff0c;提供Spring框架的基本功能。主要组件是BeanFactory&#xff0c;工…

实战攻防 | 记一次项目上的任意文件下载

1、开局 开局一个弱口令&#xff0c;正常来讲我们一般是弱口令或者sql&#xff0c;或者未授权 那么这次运气比较好&#xff0c;直接弱口令进去了 直接访问看看有没有功能点&#xff0c;正常做测试我们一定要先找功能点 发现一个文件上传点&#xff0c;不过老规矩&#xff0c;还…

实用运维工具(转载)

1、查看进程占用带宽情况-Nethogs Nethogs 是一个终端下的网络流量监控工具可以直观的显示每个进程占用的带宽。 下载&#xff1a;http://sourceforge.net/projects/nethogs/files/nethogs/0.8/nethogs-0.8.0.tar.gz/download [rootlocalhost ~]#yum -y install libpcap-deve…

Jenkins 命令无法后台运行,使用BUILD_ID=dontKillMe解决

例子&#xff1a; jenkins如果在shell里使用nohup发现还是不能后台运行&#xff0c;直接挂掉。 那么可以在jenkins命令里加上BUILD_IDdontKillMe解决 nohup python main.py >server.out 2>&1 &它的作用是在后台运行名为main.py的Python脚本&#xff0c;并将标准…