diff --git a/twitchdl/ffmpeg.py b/twitchdl/ffmpeg.py new file mode 100644 index 0000000..f7ab409 --- /dev/null +++ b/twitchdl/ffmpeg.py @@ -0,0 +1,76 @@ +import asyncio +import json +import logging +import re + +from asyncio.subprocess import PIPE +from pprint import pprint +from typing import Optional + +from twitchdl.output import print_out + + +async def join_vods(playlist_path: str, target: str, overwrite: bool, video: dict): + command = [ + "ffmpeg", + "-i", playlist_path, + "-c", "copy", + "-metadata", "artist={}".format(video["creator"]["displayName"]), + "-metadata", "title={}".format(video["title"]), + "-metadata", "encoded_by=twitch-dl", + "-stats", + "-loglevel", "warning", + f"file:{target}", + ] + + if overwrite: + command.append("-y") + + # command = ["ls", "-al"] + + print_out("{}".format(" ".join(command))) + process = await asyncio.create_subprocess_exec(*command, stdout=PIPE, stderr=PIPE) + + assert process.stderr is not None + + await asyncio.gather( + # _read_stream("stdout", process.stdout), + _print_progress("stderr", process.stderr), + process.wait() + ) + + print(process.returncode) + + +async def _read_stream(name: str, stream: Optional[asyncio.StreamReader]): + if stream: + async for line in readlines(stream): + print(name, ">", line) + + +async def _print_progress(stream: asyncio.StreamReader): + async for line in readlines(stream): + print(name, ">", line) + + +pattern = re.compile(br"[\r\n]+") + + +async def readlines(stream: asyncio.StreamReader): + data = bytearray() + + while not stream.at_eof(): + lines = pattern.split(data) + data[:] = lines.pop(-1) + + for line in lines: + yield line + + data.extend(await stream.read(1024)) + + +if __name__ == "__main__": + # logging.basicConfig(level=logging.DEBUG) + video = json.loads('{"id": "1555108011", "title": "Cult of the Lamb", "publishedAt": "2022-08-07T17:00:30Z", "broadcastType": "ARCHIVE", "lengthSeconds": 17948, "game": {"name": "Cult of the Lamb"}, "creator": {"login": "bananasaurus_rex", "displayName": "Bananasaurus_Rex"}, "playlists": [{"bandwidth": 8446533, "resolution": [1920, 1080], "codecs": "avc1.64002A,mp4a.40.2", "video": "chunked", "uri": "https://d1m7jfoe9zdc1j.cloudfront.net/278bcbd011d28f96b856_bananasaurus_rex_40035345017_1659891626/chunked/index-dvr.m3u8"}, {"bandwidth": 3432426, "resolution": [1280, 720], "codecs": "avc1.4D0020,mp4a.40.2", "video": "720p60", "uri": "https://d1m7jfoe9zdc1j.cloudfront.net/278bcbd011d28f96b856_bananasaurus_rex_40035345017_1659891626/720p60/index-dvr.m3u8"}, {"bandwidth": 1445268, "resolution": [852, 480], "codecs": "avc1.4D001F,mp4a.40.2", "video": "480p30", "uri": "https://d1m7jfoe9zdc1j.cloudfront.net/278bcbd011d28f96b856_bananasaurus_rex_40035345017_1659891626/480p30/index-dvr.m3u8"}, {"bandwidth": 215355, "resolution": null, "codecs": "mp4a.40.2", "video": "audio_only", "uri": "https://d1m7jfoe9zdc1j.cloudfront.net/278bcbd011d28f96b856_bananasaurus_rex_40035345017_1659891626/audio_only/index-dvr.m3u8"}, {"bandwidth": 705523, "resolution": [640, 360], "codecs": "avc1.4D001E,mp4a.40.2", "video": "360p30", "uri": "https://d1m7jfoe9zdc1j.cloudfront.net/278bcbd011d28f96b856_bananasaurus_rex_40035345017_1659891626/360p30/index-dvr.m3u8"}, {"bandwidth": 285614, "resolution": [284, 160], "codecs": "avc1.4D000C,mp4a.40.2", "video": "160p30", "uri": "https://d1m7jfoe9zdc1j.cloudfront.net/278bcbd011d28f96b856_bananasaurus_rex_40035345017_1659891626/160p30/index-dvr.m3u8"}]}') + playlist_path = "/tmp/twitch-dl/278bcbd011d28f96b856_bananasaurus_rex_40035345017_1659891626/160p30/playlist_downloaded.m3u8" + asyncio.run(join_vods(playlist_path, "out.mkv", True, video), debug=True)