最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

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

来源:博客园


【资料图】

一直用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.prototypefor 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    }}

关键词: 二进制的 视频解码 临时文件