首页 > 其他分享 >B站PC端下载的视频解密

B站PC端下载的视频解密

时间:2022-09-01 17:12:00浏览次数:75  
标签:pathNm 视频 return filePath pro 解密 PC 文件 const

  1. 基于知乎一位大佬python版的思路,现在用 Cnode 实现
  2. 解密原理:刪除mp4文件开头的FFFFFF
  3. 改进:可批量、深遍历、可指定文件夾/文件

node 实现

  1. 基本实现
    思路:先读取整个文件,再写入

const fs = require('fs')
const path = require('path')
const fsPro = fs.promises

/** promise扁平化 */
const promise = () => {
    const _o = {};
    _o.pro = new Promise((resolve, reject) => {
        _o.reject = reject
        _o.resolve = resolve
    });
    return _o;
}
/** 是否需要解密 */
const needReset = buf => buf[0] == 255 && buf[1] == 255 && buf[2] == 255;
/** 是否为MP4文件 */
const isMp4 = pathNm => path.extname(pathNm) == '.mp4';
/** 是否为文件夹 */
const isDir = pathNm => {
    return fsPro.stat(pathNm)
        .then((curStat) => {
            return curStat.isDirectory();
        }).catch(err => {
            console.log(`文件${pathNm} 解析失敗\n${err}`);
            return null;
        })
};
/** 文件遍历 */
const dirEach = (dirPath, cb) => {
    const pro = promise()

    fs.readdir(
        dirPath,
        function (err, datas) {
            if (err) {
                console.log(`文件夹${dirPath} 读取失败\n`, err)
            } else {
                Promise.all(
                    datas.map(pathNm => cb(path.join(dirPath, pathNm), dirPath, pathNm))
                ).then(() => pro.resolve())
            }
        }
    )
    return pro.pro
}
/** mp4解密 */
const resetMp4 = filePath => {
    const txt = `文件${filePath}`
    const pro = promise()
    fsPro.readFile(filePath)
        .then(buf => {
            if (needReset(buf)) {
                console.log(txt + ` 解密中`);
                fs.writeFile(
                    filePath,
                    buf.slice(3),
                    err => {
                        if (err) return Promise.reject(err)
                        console.log(txt + ` 解密成功`);
                        pro.resolve()
                    }
                )
            } else {
                pro.resolve()
                console.log(txt + ` 不需要解密`)
            }
        }).catch(err => {
            pro.resolve()
            console.log(txt + ` 解密失敗\n${err}`);
        })

    return pro.pro
}
/** 文件操作 */
const resetFile = async pathNm => isMp4(pathNm) && resetMp4(pathNm);

/** 执行解密 */
const run = async pathNm => {

    return (await isDir(pathNm)) ?
        dirEach(pathNm, run) :
        resetFile(pathNm);

}

const _defaulit = pathNm => run(
    pathNm ||
    process.argv[2] ||
    __dirname
);

module.exports = _defaulit;

  1. 流形式实现
    以文件流形式来重写mp4解密函数resetMp4

/** mp4解密 */
const resetMp4 = async filePath => {

    const { toReset, cs } = await readFile(filePath)

    const txt = `文件${filePath}`

    if (toReset) {
        console.log(txt + ` 解密中`)
        await writeFile(cs, filePath)
        console.log(txt + ` 解密成功`)
    } else {
        console.log(txt + ` 不需要解密`);
    }
}

/** 读取文件
 *  1. 判断是否需要解密
 *  2. 若要,则需返回可读流 */
const readFile = filePath => {
    const _pro = promise()

    fs.createReadStream(filePath).once(
        'readable',
        function () {

            let toReset = needReset(this.read(3));

            let resolveVal = { toReset, cs: 0 };

            if (toReset) {
                resolveVal.cs = this
            } else {
                this.push(null)
                this.destroy()
            }
            _pro.resolve(resolveVal)
        })

    return _pro.pro
}

/** 文件解密 */
const writeFile = (csFile, csFilePath) => {
    const _pro = promise()

    let wFilePath = csFilePath + "_v"

    const wFile = fs.createWriteStream(wFilePath)

    csFile.pipe(wFile)

    wFile.on('close', () => {
        fs.unlinkSync(csFilePath)
        fs.renameSync(wFilePath, csFilePath)
        _pro.resolve()
    })

    return _pro.pro
}
  1. 对比
    1. readFile:一次性把整个文件读到内存中
      • 整个文件:内存中加载整个文件
      • 一次性:一段连续读取时间
      • 影响:
        • 单个文件:前期等待时间长,总体时间短;
        • 批量:前期等待时间长,总体时间短;
        • 总体内存消耗大;
        • 书写简单
    2. 流形式:
      • 部分文件内容:每次仅加载一小部分内容
      • 多次读写:多次把加载到的数据追加到新文件
      • 影响:
        • 单个文件:前期等待时间短,总体时间长;
        • 批量:前期等待时间短,总体时间长;
        • 总体内存消耗小;
        • 书写复杂

C 语言实现

  1. 基本实现
#define _CRT_SECURE_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <io.h>
// 判断是否为文件夹
#define isDir(fileInfo) (_A_SUBDIR & fileInfo.attrib)
// 文件深遍历
int forEachFile(char *_fileName, void callback(struct _finddata_t fileInfo, char *curFileNm));
// 判断MP4文件是否需要解密
int needReset(FILE *pFile)
{
	int idx = ftell(pFile);
	rewind(pFile);
	int res = fgetc(pFile) == 255 && fgetc(pFile) == 255 && fgetc(pFile) == 255;
	fseek(pFile, idx, SEEK_SET);
	return res;
}
// 判断为mp4文件
int isMP4(char *curFileNm)
{
	return strstr(curFileNm, ".mp4") != NULL;
}
// 文件深遍历
int forEachFile(char *_fileName, void callback(struct _finddata_t fileInfo, char *curFileNm))
{
	//文件存储信息结构体
	struct _finddata_t fileInfo;

	char fileName[256] = "";
	strcat(fileName, _fileName);
	strcat(fileName, "\\*.*");

	long fHandle = _findfirst(fileName, &fileInfo);

	if (fHandle != -1L)
	{
		_findnext(fHandle, &fileInfo);
		_findnext(fHandle, &fileInfo);

		do
		{
			char fileNm[255] = "";
			strcat(fileNm, _fileName);
			strcat(fileNm, "\\");
			strcat(fileNm, fileInfo.name);

			isDir(fileInfo) ? forEachFile(fileNm, callback)
							: callback(fileInfo, fileNm);
		} while (_findnext(fHandle, &fileInfo) == 0);
	}

	_findclose(fHandle);

	return 0;
}
// mp4文件解密逻辑
int resetFile(char *filePath)
{

	FILE *inFile = fopen(filePath, "rb");
	if (inFile == NULL)
	{
		printf("文件:%s 解析異常\n", filePath);
		return 1;
	}
	if (!needReset(inFile))
	{
		return 0;
	}

	char catchFilePath[255] = "";
	strcat(catchFilePath, filePath);
	strcat(catchFilePath, "_c");

	FILE *outFile = fopen(catchFilePath, "wb");

	int len;

	char buffer[1024];

	if (outFile == NULL)
	{
		remove(catchFilePath);
		return 1;
	}
	printf("文件解码中:%s \n", filePath);

	fseek(inFile, 3, SEEK_SET);

	while ((len = fread(buffer, 1, 1024, inFile)) > 0) //从源文件中读取数据并放到缓冲区中 
	{
		fwrite(buffer, 1, len, outFile); //  将缓冲区的数据写到目标文件中
		fflush(outFile); // 刷新缓冲区
		memset(buffer, 0, 1024); // 清空缓存
	}

	fclose(outFile);
	fclose(inFile);

	remove(filePath);
	rename(catchFilePath, filePath);
	printf("文件解码完成:%s \n", filePath);
	return 0;
}
// 解密MP4文件
void toResetMp4(struct _finddata_t fileInfo, char *curFileNm)
{
	isMP4(curFileNm) && resetFile(curFileNm);
}

void run()
{
	char fileName[] = "D:\\bili\\BilibiliDownload\\625588762\\4";
	forEachFile(fileName, toResetMp4);
}

int main()
{
	run();
	return 0;
}

  1. C
    1. 遍历文件采用的是动态读取文件夹
      • 会读取到正在复制过程的副本文件
      • 改进:静态读取
        • 一次性读取所有文件夹的内容
        • 用链表缓存文件夹内容

标签:pathNm,视频,return,filePath,pro,解密,PC,文件,const
From: https://www.cnblogs.com/nwamtf/p/16585072.html

相关文章

  • 基于AVFoundation实现视频录制的两种方式
    目录一、前言二、方案一:AVCaptureSession+AVCaptureMovieFileOutput1.创建AVCaptureSession2.设置音频、视频输入3.设置文件输出源4.添加视频预览层5.开始采集6.开始录......
  • HttpClient和IHttpClientFactory借助第三方库生成的客户端Refit andWebApiClientCore
    理解ASP.NETCore-发送Http请求(HttpClient)  注:本文隶属于《理解ASP.NETCore》系列文章,请查看置顶博客或点击此处查看全文目录前言在.NET中,我们有很多发送Ht......
  • 利器 | AppCrawler 自动遍历测试实践(三):动手实操与常见问题汇总
    ⬇️点击“下方链接”,提升测试核心竞争力!>>更多技术文章分享和免费资料领取上两篇文章介绍了自动遍历的测试需求、工具选择和AppCrawler的环境安装、启动及配置文件字......
  • After Effects 教程,如何在 After Effects 中创建360 度 VR 视频?
    欢迎观看AfterEffects中文版教程,小编带大家学习AfterEffects的基本工具和使用技巧,了解如何在AE中创建360度VR视频。为视频添加动画效果,在「VRCompEditor」面......
  • 短视频后台Mysql和Redis
    RabbitMQ一、什么是消息队列消息指的是两个应用间传递的数据。数据的类型有很多种形式,可能只包含文本字符串,也可能包含嵌入对象。“消息队列(MessageQueue)”是在消息......
  • AllegroPCB封装制作规则
    AllegroPCB封装制作规则整理者:ZHOU邮箱:[email protected]制作元件流程新建元件封装放置焊盘  <Ecth-TOP>放置安装外框元件本体   <PackageGeometry-Assemb......
  • VUE---公钥私钥加密解密
    最近在开发一些项目,前后端数据分离开发项目,涉及到数据的安全性,需要考虑:1、前端提交数据,进行加密,后端解密。2、前端渲染数据,进行解密,后端加密。第一步:生成加密/解密公钥......
  • centos7 安装python-libpcap
    1.安装依赖yuminstalllibpcaplibpcap-devel2.安装python依赖(python3.6以上版本)$pip3installCython$pip3installpython-libpca3.验证在python命令行......
  • RL 视频讲什么训练效果是好的
    https://www.bilibili.com/video/BV1ca41187qB?p=3  17分55秒    什么效果比较好无论是奖励还是步数都是缓缓的上升         如果奖励......
  • esp32 tcpclient 重启
    xTaskCreate((TaskFunction_t)app_tcp_client,(constchar*)"app_tcp_client",(uint16_t)TCP_SERVER_TASK_STK_SIZE,......