BilibiliDown项目实现开机自启周期下载功能的技术解析

BilibiliDown项目实现开机自启周期下载功能的技术解析

痛点场景:B站视频自动化批量下载的迫切需求

作为B站(哔哩哔哩)深度用户,你是否遇到过这样的困扰:

  • 关注的UP主更新频繁,手动一个个下载耗时费力
  • 收藏夹里积累了数百个视频,想要批量下载却无从下手
  • 希望定时自动下载新内容,但缺乏合适的工具支持
  • 需要长期稳定运行,支持开机自启和周期任务调度

BilibiliDown项目的开机自启周期下载功能正是为解决这些痛点而生。本文将深入解析这一功能的技术实现细节,帮助你全面掌握自动化视频下载的核心机制。

功能架构总览

BilibiliDown的周期下载功能采用分层架构设计,核心组件包括:

mermaid

核心配置参数详解

1. 自启动配置

# 程序启动时自动开始周期下载
bilibili.download.batch.plan.runOnStartup = false

# 默认的一键下载配置文件
bilibili.download.batch.config.name = batchDownload.config

# 时间计划配置格式
bilibili.download.batch.plan = 06:00~02:00=>r(300,480); 02:00~06:00=>~06:00+r(0,360)

2. 时间计划语法解析

BilibiliDown采用灵活的时间计划配置语法:

配置格式含义示例
HH:mm~HH:mm=>r(t1,t2)时间段内随机等待t1-t2秒06:00~02:00=>r(300,480)
HH:mm~HH:mm=>~HH:mm等待到指定时刻02:00~04:00=>~06:00
HH:mm~HH:mm=>~HH:mm+r(t1,t2)等到时刻后随机等待02:00~04:00=>~06:00+r(0,360)

核心技术实现解析

1. 周期任务调度引擎

BatchDownloadRbyRThread类是周期下载功能的核心:

public class BatchDownloadRbyRThread extends BatchDownloadThread {
    
    private static ConcurrentHashMap<ClipInfo, TaskInfo> currentTaskList;
    private static long batchDownloadBeginTime;
    private static long batchDownloadEndTime;
    
    public void run() {
        while (true) {
            // 1. 检查当前无活跃下载任务
            if(Global.downloadTab.activeTask == 0) {
                // 2. 清理下载面板避免内存溢出
                for(DownloadInfoPanel dp : Global.downloadTaskList.keySet()) {
                    dp.removeTask(true);
                }
                // 3. 定时刷新Cookie(每天一次)
                if (currentTime - lastCookieRefreshTime > MILLI_SECONDS_OF_ONE_DAY) {
                    CookieRefreshThread thCR = CookieRefreshThread.newInstance();
                    thCR.start();
                    thCR.join();
                }
            }
            // 4. 执行批量下载
            runBatchDownloadOnce();
            // 5. 根据计划计算休眠时间
            sleep(timeToSleep(plans, System.currentTimeMillis()));
        }
    }
}

2. 时间计划解析算法

时间计算采用高效的区间匹配算法:

public long timeToSleep(String[] plans, long currentTime) {
    Random random = new Random();
    String today = sdfToday.format(currentTime);
    
    for(String plan: plans) {
        String[] params = plan.split("=>");
        String[] timePeriod = params[0].split("~");
        
        // 时间区间计算
        long lTime = sdf.parse(today + " " + timePeriod[0].trim()).getTime();
        long rTime = sdf.parse(today + " " + timePeriod[1].trim()).getTime();
        if(rTime <= lTime) rTime += MILLI_SECONDS_OF_ONE_DAY;
        
        // 区间匹配
        if(currentTime >= lTime && currentTime <= rTime) {
            long rollTime = 0L, toTimeDelta = 0L;
            
            // 解析随机时间 r(t1,t2)
            Matcher rm = rollTimePattern.matcher(params[1]);
            if(rm.find()) {
                int rLeft = Integer.parseInt(rm.group(1)) * 1000;
                int rRight = Integer.parseInt(rm.group(2)) * 1000;
                rollTime = rLeft + random.nextInt(rRight - rLeft);
            }
            
            // 解析指定时刻 ~HH:mm
            Matcher tm = toTimePattern.matcher(params[1]);
            if(tm.find()) {
                long toTime = sdf.parse(today + " " + tm.group(1)).getTime();
                if(toTime < currentTime) toTime += MILLI_SECONDS_OF_ONE_DAY;
                toTimeDelta = toTime - currentTime;
            }
            
            return toTimeDelta + rollTime;
        }
    }
    throw new IllegalArgumentException("时间配置错误");
}

3. 任务状态管理机制

采用ConcurrentHashMap进行线程安全的任务状态跟踪:

public static void taskSucceed(ClipInfo clip, String fileName, String fileSize, String qn) {
    if (currentTaskList != null) {
        TaskInfo task = currentTaskList.get(clip);
        if (task != null) {
            task.setFileName(fileName);
            task.setFileSize(fileSize);
            task.setQn(qn);
            task.setStatus("success");
        }
    }
}

public static void taskFail(ClipInfo clip, String status) {
    if (currentTaskList != null) {
        TaskInfo task = currentTaskList.get(clip);
        if (task != null) {
            task.setStatus(status);
        }
    }
}

开机自启实现方案

Windows系统自启

:: 创建注册表启动项
reg add "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run" /v "BilibiliDown" /t REG_SZ /d "\"C:\Program Files\BilibiliDown\BilibiliDown.exe\" -Dbilibili.download.batch.plan.runOnStartup=true" /f

Linux系统自启

# 创建systemd服务
sudo tee /etc/systemd/system/bilibilidown.service > /dev/null <<EOF
[Unit]
Description=BilibiliDown Auto Download Service
After=network.target

[Service]
Type=simple
User=$USER
ExecStart=/usr/bin/java -jar /opt/BilibiliDown/INeedBiliAV.jar -Dbilibili.download.batch.plan.runOnStartup=true
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl enable bilibilidown.service

macOS系统自启

# 创建LaunchAgent
cat > ~/Library/LaunchAgents/top.nicelee.bilibilidown.plist <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>top.nicelee.bilibilidown</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/bin/java</string>
        <string>-jar</string>
        <string>/Applications/BilibiliDown/INeedBiliAV.jar</string>
        <string>-Dbilibili.download.batch.plan.runOnStartup=true</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <false/>
</dict>
</plist>
EOF

配置文件示例与最佳实践

完整的一键下载配置文件

创建config/batchDownload.config

# 批量下载配置示例
# 支持多种URL类型混合配置

# 单个视频
https://www.bilibili.com/video/BV1g5411V7x6

# UP主所有视频
https://space.bilibili.com/123456/video

# 收藏夹
https://space.bilibili.com/123456/favlist?fid=789012

# 稍后再看
https://www.bilibili.com/list/watchlater

# 条件筛选:只下载2024年以后的1080P视频
condition.date = 20240101-
condition.quality = 80

# 重命名规则
naming.pattern = (:listName listName-)avTitle-pDisplay-clipTitle-qn

优化的时间计划配置

# 工作日白天频繁下载,夜间减少频率
bilibili.download.batch.plan = \
  06:00~22:00=>r(300,600); \      # 白天每5-10分钟检查一次
  22:00~06:00=>r(1800,3600); \    # 夜间每30-60分钟检查一次
  00:00~00:00=>r(300,300)         # 默认配置

高级功能与扩展

1. 结果推送通知

支持多种推送方式:

推送类型配置示例特点
控制台打印bilibili.download.push.type=Print默认方式,输出到日志
邮件通知bilibili.download.push.type=Mail需要配置SMTP服务器
自定义推送实现IPush接口支持Webhook等自定义方式

2. 内存优化策略

// 定期清理下载任务避免内存泄漏
for(DownloadInfoPanel dp : Global.downloadTaskList.keySet()) {
    dp.removeTask(true);
}

// 使用弱引用管理大对象
private static WeakReference<ConcurrentHashMap<ClipInfo, TaskInfo>> currentTaskListRef;

3. 异常处理与重试机制

// 网络异常自动重试
if (maxFailRetry > 0) {
    for (int retry = 0; retry < maxFailRetry; retry++) {
        try {
            downloadTask();
            break;
        } catch (IOException e) {
            if (retry == maxFailRetry - 1) throw e;
            Thread.sleep(5000 * (retry + 1));
        }
    }
}

// Cookie失效自动刷新
if (isCookieExpired()) {
    refreshCookie();
    reloadDownloadUrl = true;
}

性能优化建议

1. 资源占用控制

# 控制并发下载数量
bilibili.download.poolSize = 3

# 减少内存占用
bilibili.pageSize = 5
bilibili.restrictTempMode = on

2. 网络请求优化

# 减少不必要的清晰度查询
bilibili.info.query.strategy = returnFixedValue

# 设置合理的请求间隔
bilibili.download.period.between.query = 1000
bilibili.download.period.between.download = 0

常见问题排查

1. 自启动失败排查

# 检查日志输出
tail -f ~/.BilibiliDown/logs/app.log

# 验证Java环境
java -version

# 检查文件权限
ls -la /opt/BilibiliDown/

2. 网络连接问题

# 配置网络中转服务
networkRelayHost = 127.0.0.1
networkRelayPort = 1080

# 跳过证书验证(不推荐)
bilibili.https.allowInsecure = false

3. 内存溢出处理

# 增加JVM内存限制
java -Xmx512m -Xms256m -jar INeedBiliAV.jar

# 定期清理临时文件
find /tmp -name "BilibiliDown_*" -mtime +7 -delete

总结与展望

BilibiliDown的开机自启周期下载功能通过精心设计的架构和算法,实现了:

  1. 灵活的时间调度:支持复杂的时间计划配置
  2. 稳定的长时间运行:完善的异常处理和资源管理
  3. 自动化运维:开机自启、定时任务、结果通知
  4. 资源友好:内存优化、网络请求控制

随着B站API的不断更新和用户需求的多样化,这一功能将继续演进,支持更智能的下载策略、更丰富的推送方式,以及更好的跨平台兼容性。

通过本文的技术解析,相信你已经对BilibiliDown的自动化下载功能有了深入的理解,能够更好地配置和使用这一强大工具,实现B站视频的高效批量下载。

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值