Java 使用递归方法遍历B站下载文件并解析重命名

目录

背景

操作方法

方法优化

修改后的完整方法

声明

背景

        出于学习和日常使用方便的目的,且考虑到有的资源过一段时间会失效,所以有时会下载B站的音频,视频,进行存放保留,下面介绍下载和下载之后解析文件的方法,仅供学习交流使用,请勿未经许可盗取UP主视频盈利。(以下操作均以安卓手机为例,iOS和PC端方法可能不一样)

操作方法

        总的思路就是:将手机中下载好的B站视频,转到电脑上存储,并重命名即可(直接在电脑上下载,下载的文件都是加密过的,无法直接使用,当然,最简单的方法就是油猴上面下载两个插件,直接下载了,但是插件有的很不好用,而且一段时间后就不太稳定,下载画质也有的有限制,所以还是我想还是自己写一个方法,解决一下,并做一个分享,同时欢迎大佬有更简单更好的方法,在评论区可以留言交流)

例如,我使用PC端下载好一个鬼畜视频,将下载的目录打开后发现修改文件名后缀,并不能打开,文件内容应该是加密或者处理过的 (或者有其他办法,我暂时没有去找)

        这里我是直接用手机把文件都去下载好,然后将手机和电脑通过USB线进行数据传输,在电脑上,找到手机下载的文件路径是:

此电脑\手机名\内部存储设备\Android\data\tv.danmaku.bili\download

        与PC端不同的是手机端没有对文件进行加密,仅仅修改了文件的后缀名,反过来只要将音频和视频文件后缀名修改成正常的,即可直接观看使用了

        但是文件很多的情况下,一个一个修改效率太低,于是我写了一个方法,使用Java去直接遍历这个文件夹,并根据文件信息进行重命名,效率将会大大提高很多。

package com.ss.system.util;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Scanner;


public class BilibiliUtils {

    public static void main(String[] args) {

        // 创建一个 File 对象,指向 "D:\\UP" 目录,这是程序开始搜索的根目录。
        File rootDir = new File("D:\\UP");
        // 调用 processDirectory 方法来处理该目录及其子目录。
        processDirectory(rootDir);
    }

    // 声明一个私有的静态方法 processDirectory,接受一个 File 对象作为参数。
    private static void processDirectory(File dir) {
        // if (dir.isDirectory()) 确保传入的 File 对象是一个目录。
        if (dir.isDirectory()) {
            // dir.listFiles() 返回一个 File 数组,包含目录中的所有文件和子目录。
            File[] files = dir.listFiles();
            if (files != null) {
                for (File file : files) {
                    // 递归处理子目录:如果是子目录,递归调用 processDirectory 方法来进一步处理。
                    if (file.isDirectory()) {
                        processDirectory(file);
                        // 如果找到名为 entry.json 的文件,调用 processJsonFile 方法进行处理。
                    } else if (file.getName().equalsIgnoreCase("entry.json")) {
                        processJsonFile(dir);
                    }
                }
            }
        }
    }


    // 声明一个私有的静态方法 processJsonFile,接受一个 File 对象作为参数,这个 File 对象应指向包含 entry.json 文件的目录。
    private static void processJsonFile(File jsonDir) {
        // new File(jsonDir, "entry.json") 创建一个指向 entry.json 文件的 File 对象。
        File jsonFile = new File(jsonDir, "entry.json");
        if (jsonFile.exists()) {
            // 使用 FileReader 和 Scanner 读取文件内容。
            try (FileReader reader = new FileReader(jsonFile);
                 Scanner scanner = new Scanner(reader)) {
                // scanner.useDelimiter("\\A").next() 读取整个文件内容为一个字符串。
                String jsonString = scanner.useDelimiter("\\A").next();
                // 使用 JsonParser 解析 JSON 字符串,并转换为 JsonObject。
                JsonObject jsonObject = JsonParser.parseString(jsonString).getAsJsonObject();
                String title = jsonObject.get("title").getAsString();
                String ownerName = jsonObject.get("owner_name").getAsString();
                String newBaseName = title + " - " + ownerName;
                System.out.println("New base name: " + newBaseName);
                // 调用 searchAndRenameFiles 方法,在当前目录中查找 video.m4s 和 audio.m4s 文件并进行重命名。
                searchAndRenameFiles(jsonDir, newBaseName);
            } catch (IOException e) {
                System.out.println("Failed to read or process JSON file: " + e.getMessage());
            }
        } else {
            System.out.println("entry.json not found in directory: " + jsonDir.getAbsolutePath());
        }
    }

    private static void searchAndRenameFiles(File startDir, String newBaseName) {
        File[] files = startDir.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    // Recursively search in subdirectories
                    searchAndRenameFiles(file, newBaseName);
                } else if (file.getName().equalsIgnoreCase("video.m4s")) {
                    renameFileIfNeeded(file, newBaseName + ".mp4");
                } else if (file.getName().equalsIgnoreCase("audio.m4s")) {
                    renameFileIfNeeded(file, newBaseName + ".mp3");
                }
            }
        }
    }

    private static void renameFileIfNeeded(File file, String newName) {
        if (file.exists()) {
            File newFile = new File(file.getParent(), newName);
            boolean success = file.renameTo(newFile);
            if (success) {
                System.out.println("Renamed to: " + newFile.getAbsolutePath());
            } else {
                System.out.println("Failed to rename: " + file.getAbsolutePath());
            }
        } else {
            System.out.println("File not found: " + file.getAbsolutePath());
        }
    }
}

可以看到文件夹下面的所有文件,已经解析并重命名OK了。

更新(新增一个功能,将指定目录下所有 .mp3 文件复制到 D:\UP,可以在 searchAndRenameFiles 方法中添加复制逻辑。)

package com.ss.system.util;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Scanner;


public class BilibiliUtils {
    public static void main(String[] args) {
        File rootDir = new File("D:\\UP");
        processDirectory(rootDir);
    }

    private static void processDirectory(File dir) {
        if (dir.isDirectory()) {
            File[] files = dir.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        processDirectory(file);
                    } else if (file.getName().equalsIgnoreCase("entry.json")) {
                        processJsonFile(dir);
                    }
                }
            }
        }
    }

    private static void processJsonFile(File jsonDir) {
        File jsonFile = new File(jsonDir, "entry.json");
        if (jsonFile.exists()) {
            try (FileReader reader = new FileReader(jsonFile);
                 Scanner scanner = new Scanner(reader)) {
                String jsonString = scanner.useDelimiter("\\A").next();
                JsonObject jsonObject = JsonParser.parseString(jsonString).getAsJsonObject();
                String title = jsonObject.get("title").getAsString();
                String ownerName = jsonObject.get("owner_name").getAsString();
                String newBaseName = title + " - " + ownerName;
                System.out.println("New base name: " + newBaseName);
                // 重命名文件
                searchAndRenameFiles(jsonDir, newBaseName);
                // 复制MP3文件
                copyMp3Files(jsonDir);

            } catch (IOException e) {
                System.out.println("Failed to read or process JSON file: " + e.getMessage());
            }
        } else {
            System.out.println("entry.json not found in directory: " + jsonDir.getAbsolutePath());
        }
    }

    private static void searchAndRenameFiles(File startDir, String newBaseName) {
        File[] files = startDir.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    searchAndRenameFiles(file, newBaseName);
                } else if (file.getName().equalsIgnoreCase("video.m4s")) {
                    renameFileIfNeeded(file, newBaseName + ".mp4");
                } else if (file.getName().equalsIgnoreCase("audio.m4s")) {
                    renameFileIfNeeded(file, newBaseName + ".mp3");
                }
            }
        }
    }

    private static void renameFileIfNeeded(File file, String newName) {
        if (file.exists()) {
            File newFile = new File(file.getParent(), newName);
            boolean success = file.renameTo(newFile);
            if (success) {
                System.out.println("Renamed to: " + newFile.getAbsolutePath());
            } else {
                System.out.println("Failed to rename: " + file.getAbsolutePath());
            }
        } else {
            System.out.println("File not found: " + file.getAbsolutePath());
        }
    }

    private static void copyMp3Files(File startDir) {
        File[] files = startDir.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    copyMp3Files(file);
                } else if (file.getName().toLowerCase().endsWith(".mp3")) {
                    copyMp3File(file);
                }
            }
        }
    }

    private static void copyMp3File(File mp3File) {
        File destDir = new File("D:\\UP");
        if (!destDir.exists()) {
            destDir.mkdirs();
        }
        File destFile = new File(destDir, mp3File.getName());
        try {
            Files.copy(mp3File.toPath(), destFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
            System.out.println("Copied: " + mp3File.getAbsolutePath() + " to " + destFile.getAbsolutePath());
        } catch (IOException e) {
            System.out.println("Failed to copy: " + mp3File.getAbsolutePath() + " - " + e.getMessage());
        }
    }
}

方法优化

由于在解析json文件进行重命名的时候,会遇到视频合集的情况,这种合集的视频名称前两部分都是相同的,导致最后文件重复,于是在解析的时候,在原有的基础上又新增解析一个字段

{
    "media_type": 2,
    "has_dash_audio": true,
    "is_completed": true,
    "total_bytes": 9016866,
    "downloaded_bytes": 9016866,
    "title": "双笙超好听的古风歌曲合集,特有的深情戏腔之音,真的太适合唱古风了吧!",
    "type_tag": "80",
    "cover": "http:\/\/i2.hdslb.com\/bfs\/archive\/26effb6f93d117bbb38c75fe247187882004cf65.png",
    "video_quality": 80,
    "prefered_video_quality": 80,
    "guessed_total_bytes": 0,
    "total_time_milli": 264103,
    "danmaku_count": 0,
    "time_update_stamp": 1727162435204,
    "time_create_stamp": 1727162384864,
    "can_play_in_advance": true,
    "interrupt_transform_temp_file": false,
    "quality_pithy_description": "1080P",
    "quality_superscript": "",
    "variable_resolution_ratio": false,
    "cache_version_code": 8140300,
    "preferred_audio_quality": 0,
    "audio_quality": 0,
    "avid": 1150639898,
    "spid": 0,
    "seasion_id": 0,
    "bvid": "BV1fZ42127LU",
    "owner_id": 690261349,
    "owner_name": "皓晨音乐",
    "owner_avatar": "https:\/\/i1.hdslb.com\/bfs\/face\/7e9b8553979b8d5f810f42355dd261e562d4f969.jpg",
    "is_charge_video": false,
    "verification_code": 0,
    "page_data": {
        "cid": 1439737172,
        "page": 11,
        "from": "vupload",
        "part": "别赋",
        "link": "",
        "rich_vid": "",
        "vid": "",
        "has_alias": false,
        "tid": 130,
        "width": 1920,
        "height": 1080,
        "rotate": 0,
        "download_title": "视频已缓存完成",
        "download_subtitle": "双笙超好听的古风歌曲合集,特有的深情戏腔之音,真的太适合唱古风了吧! 别赋"
    }
}

或者直接解析 download_subtitle 的内容当作文件新名字进行重命名,则不会重复 ,但是有的文件这个字段又没有,所以还是把 title 保留或新增一个part的解析内容进去

修改代码:

String jsonString = scanner.useDelimiter("\\A").next();
JsonObject jsonObject = JsonParser.parseString(jsonString).getAsJsonObject();
String title = jsonObject.get("title").getAsString();
String ownerName = jsonObject.get("owner_name").getAsString();
String newBaseName = title + " - " + ownerName;
System.out.println("New base name: " + newBaseName);

变成:(并增加判空逻辑)

// 获取 owner_name
String ownerName = jsonObject.get("owner_name") != null ? jsonObject.get("owner_name").getAsString() : "";
// 获取 download_subtitle
String downloadSubtitle = jsonObject.getAsJsonObject("page_data").get("download_subtitle") != null 
?jsonObject.getAsJsonObject("page_data").get("download_subtitle").getAsString() 
                                      : "";
// 获取 part
String part = jsonObject.getAsJsonObject("page_data").get("part") != null 
? jsonObject.getAsJsonObject("page_data").get("part").getAsString() 
                          : "";

// 判空逻辑
if (downloadSubtitle.isEmpty()) {
    System.out.println("Warning: download_subtitle is empty.");
    downloadSubtitle = "Unknown_Title"; // 可以设置默认值
}
if (ownerName.isEmpty()) {
    System.out.println("Warning: owner_name is empty.");
    ownerName = "Unknown_Owner"; // 可以设置默认值
}
if (part.isEmpty()) {
     System.out.println("Warning: part is empty.");
     part = "Unknown_Part"; // 可以设置默认值
}
// 构造新的文件名
     String newBaseName = downloadSubtitle + " - " + ownerName + " - " + part;
     System.out.println("New base name: " + newBaseName);

修改后的完整方法

package com.ss.system.util;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Scanner;


public class BilibiliUtils {
    public static void main(String[] args) {
        File rootDir = new File("D:\\UP");
        processDirectory(rootDir);
    }

    private static void processDirectory(File dir) {
        if (dir.isDirectory()) {
            File[] files = dir.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        processDirectory(file);
                    } else if (file.getName().equalsIgnoreCase("entry.json")) {
                        processJsonFile(dir);
                    }
                }
            }
        }
    }

    private static void processJsonFile(File jsonDir) {
        File jsonFile = new File(jsonDir, "entry.json");
        if (jsonFile.exists()) {
            try (FileReader reader = new FileReader(jsonFile);
                Scanner scanner = new Scanner(reader)) {
                String jsonString = scanner.useDelimiter("\\A").next();
                JsonObject jsonObject = JsonParser.parseString(jsonString).getAsJsonObject();
                // 获取 owner_name
                String ownerName = jsonObject.get("owner_name") != null ? jsonObject.get("owner_name").getAsString() : "";
                // 获取 download_subtitle
                String downloadSubtitle = jsonObject.getAsJsonObject("page_data").get("download_subtitle") != null
                        ? jsonObject.getAsJsonObject("page_data").get("download_subtitle").getAsString()
                        : "";
                // 获取 part
                String part = jsonObject.getAsJsonObject("page_data").get("part") != null
                        ? jsonObject.getAsJsonObject("page_data").get("part").getAsString()
                        : "";
                // 判空逻辑
                if (downloadSubtitle.isEmpty()) {
                    System.out.println("Warning: download_subtitle is empty.");
                    downloadSubtitle = "Unknown_Title"; // 可以设置默认值
                }
                if (ownerName.isEmpty()) {
                    System.out.println("Warning: owner_name is empty.");
                    ownerName = "Unknown_Owner"; // 可以设置默认值
                }
                if (part.isEmpty()) {
                    System.out.println("Warning: part is empty.");
                    part = "Unknown_Part"; // 可以设置默认值
                }
                // 构造新的文件名
                String newBaseName = downloadSubtitle + " - " + ownerName + " - " + part;
                System.out.println("New base name: " + newBaseName);
                // 重命名文件
                searchAndRenameFiles(jsonDir, newBaseName);
                // 复制MP3文件
                copyMp3Files(jsonDir);
            } catch (IOException e) {
                System.out.println("Failed to read or process JSON file: " + e.getMessage());
            }
        } else {
            System.out.println("entry.json not found in directory: " + jsonDir.getAbsolutePath());
        }
    }

    private static void searchAndRenameFiles(File startDir, String newBaseName) {
        File[] files = startDir.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    searchAndRenameFiles(file, newBaseName);
                } else if (file.getName().equalsIgnoreCase("video.m4s")) {
                    renameFileIfNeeded(file, newBaseName + ".mp4");
                } else if (file.getName().equalsIgnoreCase("audio.m4s")) {
                    renameFileIfNeeded(file, newBaseName + ".mp3");
                }
            }
        }
    }

    private static void renameFileIfNeeded(File file, String newName) {
        if (file.exists()) {
            File newFile = new File(file.getParent(), newName);
            boolean success = file.renameTo(newFile);
            if (success) {
                System.out.println("Renamed to: " + newFile.getAbsolutePath());
            } else {
                System.out.println("Failed to rename: " + file.getAbsolutePath());
            }
        } else {
            System.out.println("File not found: " + file.getAbsolutePath());
        }
    }

    private static void copyMp3Files(File startDir) {
        File[] files = startDir.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    copyMp3Files(file);
                } else if (file.getName().toLowerCase().endsWith(".mp3")) {
                    copyMp3File(file);
                }
            }
        }
    }

    private static void copyMp3File(File mp3File) {
        File destDir = new File("D:\\UP");
        if (!destDir.exists()) {
            destDir.mkdirs();
        }
        File destFile = new File(destDir, mp3File.getName());
        try {
            Files.copy(mp3File.toPath(), destFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
            System.out.println("Copied: " + mp3File.getAbsolutePath() + " to " + destFile.getAbsolutePath());
        } catch (IOException e) {
            System.out.println("Failed to copy: " + mp3File.getAbsolutePath() + " - " + e.getMessage());
        }
    }
}

 

提取出的合集文件如下:

声明

        下载文件的方法,网上帖子有很多,我只是借用了他们的方法,并简化了我自己保存文件的繁琐操作,请大家不要怀有恶意,也感谢B站的UP主上传分享的视频,B站如果后期修复,将移动端文件也加密了那这个方法也不奏效了,破解加密那个跟现在文件解析重命名就不是一个等级的工作了,大家也且用且珍惜吧。

        本文仅作为一个分享,希望大家在保存文件的时候,可以方便一点,减少不必要的手动操作,本着技术中立原则,仅作为学习交流,请大家自行使用,不要去以此盗取他人视频商用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值