Skip to content

Instantly share code, notes, and snippets.

@Mnkai
Last active October 18, 2020 11:50
Show Gist options
  • Save Mnkai/22e3b88e7c61b540716ad7f6cacb1306 to your computer and use it in GitHub Desktop.
Save Mnkai/22e3b88e7c61b540716ad7f6cacb1306 to your computer and use it in GitHub Desktop.
AppleStreamDump

AppleStreamDump

Dumps apple HLS stream format - including alternative audio tracks and all subtitles

Dependencies

  • m3u8
  • ffmpy (and ffmpeg)
  • pycountry

Caveats

  • mov_text format is used for webvtt conversion - thus output container should support mov_text (e.g., mp4)
import m3u8
import sys
import ffmpy
import pycountry
def parse_main_m3u8(main_m3u8_url):
main_playlist = m3u8.load(main_m3u8_url)
video_playlist_uri = get_main_video_stream(main_playlist)
audio_playlist_media_list = get_audio_streams(main_playlist)
sub_playlist_media_list = get_sub_streams(main_playlist)
return video_playlist_uri, audio_playlist_media_list, sub_playlist_media_list
def get_main_video_stream(main_playlist):
bandwidth = -1
for playlist in main_playlist.playlists:
if playlist.stream_info.bandwidth > bandwidth:
bandwidth = playlist.stream_info.bandwidth
for playlist in main_playlist.playlists:
if playlist.stream_info.bandwidth == bandwidth:
return playlist.uri
def get_audio_streams(main_playlist):
to_return = list()
for media in main_playlist.media:
if media.type == "AUDIO":
to_return.append(media)
return to_return
def get_sub_streams(main_playlist):
to_return = list()
for media in main_playlist.media:
if media.type == "SUBTITLES":
to_return.append(media)
return to_return
def main():
if len(sys.argv) != 3:
print("Usage: ", sys.argv[0], " (stream file name) (output_file_name)")
exit(-1)
video_m3u8_url, audio_playlist_media_list, sub_playlist_media_list = parse_main_m3u8(sys.argv[1])
metadata_string = ""
for idx, audio_media in enumerate(audio_playlist_media_list):
metadata_string = metadata_string + \
" -metadata:s:a:" + str(idx) + ' name="' + audio_media.name + '"' + \
" -metadata:s:a:" + str(idx) + ' language="' + \
pycountry.languages.get(alpha_2=audio_media.language).alpha_3 + '"'
for idx, sub_media in enumerate(sub_playlist_media_list):
metadata_string = metadata_string + \
" -metadata:s:s:" + str(idx) + ' name="' + sub_media.name + '"' +\
" -metadata:s:s:" + str(idx) + ' language="' + \
pycountry.languages.get(alpha_2=sub_media.language).alpha_3 + '"'
map_string = ""
# Hard coded video stream count
stream_count = 1
for i in range(0, len(audio_playlist_media_list)):
stream_count += 1
for i in range(0, len(sub_playlist_media_list)):
stream_count += 1
for i in range(0, stream_count):
map_string = map_string + " -map " + str(i)
stream_input_dict = dict()
stream_input_dict[video_m3u8_url] = None
for audio_media in audio_playlist_media_list:
stream_input_dict[audio_media.uri] = None
for sub_media in sub_playlist_media_list:
stream_input_dict[sub_media.uri] = None
ffmpy.FFmpeg(
inputs=stream_input_dict,
outputs={sys.argv[2]: map_string + ' -c:a copy -c:v copy -c:s mov_text' + metadata_string}
).run()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment