程序员的资源宝库

网站首页 > gitee 正文

用AutoHotkey解决B站客户端缓存视频m4s合并成mp4提示解析失败

sanyeah 2024-03-29 16:14:20 gitee 1 ℃ 0 评论

一直用you-get下载B站视频,这两天发现视频的质量很低,于是又折腾了下

  1. 在线播放有1080P,而you-get -i {url}返回的最高清晰度就是480P
  2. 听朋友安利用哔哩下载姬(DownKyi),试了也是只有480P的清晰度

看来B站作了限制了,于是下载了官方客户端,可以缓存视频,
发现是两个m4s格式的文件,经网上查看,一个是视频,一个是音频,
ffmpeg -i {f1} -i {f2} -c copy out.mp4想合并文件并转为mp4,提示xxx.m4s: Invalid data found when processing input,看来是B站对这个文件也做了处理。
继续找视频,在B站客户端缓存视频解码与打开方法_哔哩哔哩知道了要修改的地方(主要是这个)。
于是又用AutoHotkey折腾了文件二进制的处理。

附上 AutoHotkey v2-beta 代码

m4sDir("c:\Users\Administrator\Desktop\811376303")

;传入文件夹即可
m4sDir(dir) {
    SplitPath(dir, &dn)
    if (dn ~= "^\d{9}$") { ;缓存文件夹是9个数字
        fnn := A_Now
        arrFp := []
        loop files, dir . "\*.m4s", "F" { ;B站缓存
            arrFp.push((fnn . string(A_Index)).fnn2fp("m4s"))
            um_m4s_modify(A_LoopFileFullPath, arrFp[-1])
        }
        ;解析 .videoInfo 文件,获取文件名
        title := json.parse(fileread(".videoInfo".fn2fp(dir), "utf-8"))["title"]
        ;用ffmpeg合并文件
        RunWait(format('ffmpeg.exe -i {1} -i "{2}" -c copy "{3}"',arrFp[1],arrFp[2],title.fnn2fp("mp4",dir)))
        ;删除两个临时文件
        arrFp.map((fp)=>FileDelete(fp))
        return 1
    }
}

;核心:对m4s文件进行二进制处理
um_m4s_modify(fp, fp1:="") {
    o := fileread(fp, "raw")
    o.mid(1,9) ;删除前面9个字符
    o.deleteByStr("avc1", 10, 30) ;删除指定字符串
    numput("char", 32, o, 3) ;修改第4个字符为空格
    if (FileExist(fp1))
        FileDelete(fp1)
    FileAppend(o, fp1)
}

defprop := {}.DefineProp.bind(buffer.prototype)
proto := _Buffer.prototype
for k in proto.OwnProps() {
    if (k != "__Class")
        defprop(k, proto.GetOwnPropDesc(k))
}
class _Buffer {

    static fromFile(fp) => fileread(fp, "raw")

    ;删除 i开始l个字符
    ;o := ClipboardAll(o.ptr + 9, o.size-9)
    mid(i, l){
        dllcall("RtlMoveMemory", "ptr",this.ptr+i-1, "ptr",this.ptr+i+l-1, "uptr",this.size-l)
        this.size -= l
    }

    ;从 start 处开始匹配字符串 limit为限制查找长度
    index(str, start:=0, limit:=0) {
        ;字符串转成 asc 码
        arr := []
        loop parse, str
            arr.push(ord(A_LoopField))
        i := start
        while (i < this.size) {
            if (limit && i-start > limit)
                return 0
            if (numget(this, i, "char") == arr[1]) { ;匹配第一个字符
                OutputDebug(format("i#{1} {2}:{3} match first={4}", A_LineFile,A_LineNumber,A_ThisFunc,i))
                res := i
                ;确认后续内容是否完整匹配
                i++
                loop(arr.length-1) {
                    if (numget(this, i, "char") != arr[A_Index+1]) { ;有一个不匹配,则退出
                        res := 0
                        break
                    }
                    i++
                }
                if (res) {
                    OutputDebug(format("i#{1} {2}:{3} res={4}", A_LineFile,A_LineNumber,A_ThisFunc,res))
                    return res
                }
            } else {
                i++
            }
        }
    }

    ;删除第一个找到的字符串
    deleteByStr(str, start:=0, limit:=0) {
        i := this.index(str, start, limit)
        if (i) {
            this.mid(i+1, strlen(str))
        }
        return i
    }

}

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表