Skip to content

Instantly share code, notes, and snippets.

@coderanger
Last active September 1, 2025 04:54
Show Gist options
  • Select an option

  • Save coderanger/2b1399a5d01071d839b46bf3d6b423df to your computer and use it in GitHub Desktop.

Select an option

Save coderanger/2b1399a5d01071d839b46bf3d6b423df to your computer and use it in GitHub Desktop.

Revisions

  1. coderanger created this gist Sep 1, 2025.
    84 changes: 84 additions & 0 deletions main.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,84 @@
    # /// script
    # requires-python = ">=3.10"
    # dependencies = [
    # "httpx",
    # ]
    # ///

    import asyncio
    import re
    import sys
    from dataclasses import dataclass
    from pathlib import Path

    import httpx

    client = httpx.AsyncClient()


    @dataclass
    class Chapter:
    name: str
    vtt_caption_url: str
    mp4_url: str
    hls_playlist_url: str


    @dataclass
    class Video:
    chapters: list[Chapter]


    def input_to_video_id(input: str) -> str:
    if md := re.match(r"^https://play\.vidyard\.com/(.+)$", input):
    return md[1]
    return input


    async def get_video(id: str) -> Video:
    resp = await client.get(f"https://play.vidyard.com/player/{id}.json")
    resp.raise_for_status()
    return Video(
    chapters=[
    Chapter(
    name=c["name"],
    vtt_caption_url=[v["vttUrl"] for v in c["captions"] if v["isDefault"]][
    0
    ],
    mp4_url=[
    s["url"] for s in c["sources"]["mp4"] if s["profile"] == "1080p"
    ][0],
    hls_playlist_url=[
    s["url"] for s in c["sources"]["hls"] if s["profile"] == "1080p"
    ][0],
    )
    for c in resp.json()["payload"]["chapters"]
    ]
    )


    async def download_file(video_id: str, chunk_url: str, out_path: Path) -> None:
    with out_path.open("wb") as outf:
    async with client.stream(
    "GET",
    chunk_url,
    headers={"Referer": f"https://play.vidyard.com/{video_id}"},
    ) as resp:
    resp.raise_for_status()
    async for chunk in resp.aiter_bytes():
    outf.write(chunk)


    async def main() -> None:
    for arg in sys.argv[1:]:
    vid_id = input_to_video_id(arg)
    vid = await get_video(vid_id)
    for chap in vid.chapters:
    await download_file(
    vid_id, chap.vtt_caption_url, Path(f"./{chap.name}.vtt")
    )
    await download_file(vid_id, chap.mp4_url, Path(f"./{chap.name}.mp4"))


    if __name__ == "__main__":
    asyncio.run(main())