har2mp4

## 此内容,自用备忘 ##

edge 中导出 har,使用脚本解析,输出 mp4

#!/usr/bin/python
#! by mostone@hotmail.com
import os
import sys
import json
import base64
import re

ptnTs = re.compile(r"([^/]+.ts)")
ptnM3u = re.compile(r"([^/]+.m3u8)")

# 解析 HAR 文件,返回数据
'''
result:
{
    "ts": {
        "fileName_1": bytes_1,
        "fileName_2": bytes_2,
        "fileName_3": bytes_3,
        ...
    },
    "m3u": [
        [fileName_1, text_1],
        [fileName_2, text_2],
        [fileName_3, text_3],
        ...
    ]
}
'''
def parseHAR(f):
    print("开始读取 HAR 文件...")
    bs = None
    with open(f, "rb") as har:
        bs = har.read()

    print("开始解析 HAR 文件...")
    data = json.loads(bs)
    print(f"HAR file version: {data["log"]["version"]}")

    tsDict = {}
    m3uList = []

    for e in data["log"]["entries"]:
        if not ("content" in e["response"] and "mimeType" in e["response"]["content"]): continue
        # 以文件名为 key, 添加 bytes 内容
        if e["response"]["content"]["mimeType"] == r"video/mp2t":
            tsDict[ptnTs.search(e["request"]["url"]).group()] = base64.b64decode(e["response"]["content"]["text"])
            continue
        
        if not(e["request"]["url"].endswith(".m3u8")): continue
        
        # 获取 m3u8 文件内容
        txt = e["response"]["content"]["text"]
        if "encoding" in e["response"]["content"]:
            if e["response"]["content"]["encoding"] == "base64":
                txt = base64.b64decode(txt, "utf-8")
            else:
                raise Exception(f"未实现的 m3u8 文件编码格式: {e["response"]["content"]["encoding"]}")
        else:
            # #EXTM3U:表明该文件是一个 m3u8 文件。每个 M3U 文件必须将该标签放置在第一行。
            if txt.find(r"\n", 0, 15) != -1:
                txt =txt.replace(r"\n", "\n")

        m3uList.append([
            ptnM3u.search(e["request"]["url"]).group(),
            txt
        ])

    return {"ts": tsDict, "m3u": m3uList}

# 返回 ts 列表,忽略 m3u8 文件头
# [ [ts-file-name, extinfo], [] ...]
def splitTsInfo(s):
    isHead = True
    extInfo = None
    lst = []
    for l in s.splitlines():
        # 跳过文件头,至到出现 "#EXTINF"
        isExtInfo = l.startswith("#EXTINF:")
        if isHead and isExtInfo:
            isHead = False
        if isHead: continue

        if isExtInfo:
            extInfo = l
        elif extInfo is not None:
            lst.append([ptnTs.search(l).group() ,extInfo])
            extInfo = None
        else:
            continue

    return lst

# 删除 cur 中与 prev 重复的项(复制 cur 列表,不改变原始 cur)
def removeRepeat(prev, cur):
    ret = cur.copy()
    for i in range(len(ret) - 1, -1, -1):
        for p in prev:
            # 比较 ts 文件名
            if ret[i][0] == p[0]: 
                del ret[i]
                break
    return ret

# 返回去重后的 m3u8 列表
def getDistinct(m3u):
    # 逐一保存单个 m3u8 文件
    prev = None
    distinct = []
    for f in m3u:
        cur = splitTsInfo(f[1])
        if prev is None:
            distinct.extend(cur)
        else:
            distinct.extend(removeRepeat(prev, cur))

        prev = cur

    return distinct

def outputMulti(data):
    # 逐一保存单个 ts 文件
    for k, v in data["ts"].items():
        with open(k, "wb") as w: w.write(v)

    # 逐一保存单个 m3u8 文件
    m3u = data["m3u"]
    for i in range(0, len(m3u)):
        with open(m3u[i][0].replace(".m3u8", f"_{i+1}.m3u8"), "w") as w: w.write(m3u[i][1])

    # 保存整合的 m3u8 文件,保存 ts 文件列表
    baM3u = bytearray()
    baLst = bytearray()
    dis = getDistinct(m3u)
    for l in dis:
        # 忽略未实际下载的 ts 文件
        if l[0] in data["ts"]:
            baM3u.extend(bytes(f"{l[1]}\n{l[0]}\n", "utf-8"))
            baLst.extend(bytes(f"file {l[0]}\n", "utf-8"))
        else:
            print(f"[提示]:{l[0]} 文件不存在,未输出至 o.m3u8 和 o.lst")

    with open("o.m3u8", "wb") as w: w.write(baM3u)
    with open("o.lst", "wb") as w: w.write(baLst)

def outputMp4(data, f):
    # 合并所有 ts 文件,输出 o.ts
    dis = getDistinct(data["m3u"])
    isFileMiss = False
    with open("o.ts", "wb") as w:
        for l in dis:
            # 忽略未实际下载的 ts 文件
            if l[0] in data["ts"]:
                w.write(data["ts"][l[0]])
            else:
                print(f"[提示]:{l[0]} 文件不存在,忽略之")
                isFileMiss = True

    if isFileMiss and input("是否继续[y/n]:").upper() != "Y": return
    
    # 转 o.ts -> o.mp4
    cmd = f"ffmpeg -i o.ts -c copy {f}"
    os.system(cmd)

# main
# -----------------------------------------------------
# 解析 HAR 文件,合成一个 mp4 文件
# >>> python py文件名 mp4 HAR文件 o.mp4
if len(sys.argv)==4 and sys.argv[1] == "mp4":
    f = sys.argv[2]
    data = parseHAR(f)
    outputMp4(data, sys.argv[3])
    quit()

# 解析 HAR 文件,并提取所有的 ts, m3u8 文件逐一保存到当前目录
# >>> python py文件名 parse HAR文件
if len(sys.argv)==3 and sys.argv[1] == "parse":
    f = sys.argv[2]
    data = parseHAR(f)
    outputMulti(data)
    quit()

# 调用 ffmpeg 截取视频片断
# >>> python py文件名 split o.mp4 ss to ss.mp4
if len(sys.argv)==6 and sys.argv[1] == "split":
    cmd = f"ffmpeg -i {sys.argv[2]} -ss {sys.argv[3]} -to {sys.argv[4]} -c copy {sys.argv[5]}"
    os.system(cmd)
    quit()

# 合并 ts 文件(需在参数中提供 ts 列表文件)
# >>> python py文件名 contact o.lst o.ts
if len(sys.argv)==4 and sys.argv[1] == "contact":
    with open(sys.argv[3], "wb") as dst:
        with open(sys.argv[2], "r") as lst:
            for l in lst:
                fs = l.split()
                with open(fs[1], "rb") as tsbs: dst.write(tsbs.read())
    quit()

print(f'''命令参数不正确,请使用以下格式:
python {sys.argv[0]} mp4 <HAR文件> <o.mp4>
      解析 HAR 文件,合成一个 mp4 文件
      
python {sys.argv[0]} parse <HAR文件>
      解析 HAR 文件,并提取所有的 ts, m3u8 文件逐一保存到当前目录

python {sys.argv[0]} contact <o.lst> <o.ts>
      合并 ts 文件

python {sys.argv[0]} split <o.mp4> <ss> <to> <ss.mp4>
    调用 ffmpeg 截取视频片断          
    ss 和 to 是时间,格式为:HH:MM:SS.MILLISECONDS''')

### 关于 sanmu 和 har2case 的介绍 #### har2case 工具概述 har2case 是一款专注于 API 测试领域的重要工具,其核心功能在于将 HAR(HTTP Archive)文件转换为多种格式的测试用例。HAR 文件记录了网页加载过程中的网络请求数据,而通过 har2case 可以轻松将其转化为结构化的测试脚本,从而大幅提升开发人员和测试工程师的工作效率[^1]。 以下是关于 har2case 的一些关键特性: - 支持生成多种主流框架兼容的测试用例,例如 pytest、unittest 等。 - 提供灵活的配置选项,允许用户自定义输出模板。 - 集成了对复杂场景的支持能力,比如处理动态参数、依赖关系以及多步操作逻辑。 对于希望提高自动化水平并减少手动编写重复性代码负担的技术团队来说,这是一个非常实用的选择。 #### 如何获取及安装 har2case? 要下载和使用 har2case,请按照如下方法执行: 1. **通过 pip 安装** 如果您的环境中已安装 Python,则可以直接利用 `pip` 命令完成安装工作。 ```bash pip install httprunner ``` 注意:由于 har2case 实际上属于 HttpRunner 生态的一部分,在大多数情况下只需安装后者即可获得前者支持。 2. **克隆源码仓库** 对于更高级的需求或者想要参与贡献者社区的朋友而言,可以从 GitHub 上拉取官方存储库来构建本地版本。 ```bash git clone https://github.com/HttpRunner/httprunner.git cd httprunner &amp;&amp; python setup.py install ``` 3. **运行示例项目验证环境搭建成功与否** 成功部署之后可以参照文档指南尝试几个简单的例子确认一切正常运作无误。 --- #### Sanmu 工具简介 目前公开可查的信息显示,“Sanmu” 并未被广泛提及作为某特定软件名称存在;如果这里指的是某个具体产品或解决方案的话,可能需要进一步澄清确切含义以便提供更加精准的帮助信息。不过基于上下文中提到它与 “API 测试” 主题相关联这一点推测,也许是指代另一款类似的辅助平台或者是内部研发成果? 假如确实如此,那么类似于这样的工具有以下几个推荐方向可供探索学习: - Postman: 虚拟机模拟真实浏览器行为发起各种类型的 HTTP 请求; - Swagger/OpenAPI Generator: 自动生成客户端 SDK 或服务端骨架代码; - Katalon Studio / ReadyAPI : 商业化程度较高的全生命周期管理方案覆盖接口设计到性能评估全流程环节。 当然以上仅限猜测性质结论,建议再次核实目标对象的确切叫法再做深入研究探讨! ```python # 示例演示如何调用 har2case 功能模块 from httprunner import loader, runner testcases = loader.load_cases_from_har("example.har") summary = runner.run(testcases) print(summary) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值