Beispiel #1
0
def test_add_gap():
    obj = m3u8.Segment(uri='fileSequence271.ts', duration=4, gap_tag=True)

    result = str(obj)
    expected = '#EXTINF:4,\n#EXT-X-GAP\nfileSequence271.ts'

    assert result == expected
Beispiel #2
0
def insert_video(input_m3u, video, index):
    """
    insert IN.M3U INDEX VIDEO: update m3u by inserting video at specified index (0 for start)
    """
    m3u8_obj = m3u8.load(input_m3u)

    new_segment = m3u8.Segment(uri=video, title=video, duration=0)
    segment = m3u8_obj.segments[int(index)]

    print("Inserting {} before {}".format(video, segment.uri))
    m3u8_obj.segments.insert(int(index), new_segment)

    with open(input_m3u, "w") as f:
        f.write(m3u8_obj.dumps())
Beispiel #3
0
def append_video(input_m3u, video):
    """
    append IN.M3U VIDEO: update m3u by appending video to end
    """
    try:
        m3u8_obj = m3u8.load(input_m3u)
        new_segment = m3u8.Segment(uri=video, title=video, duration=0)
        print("Append {} to end of playlist".format(video))
        m3u8_obj.segments.append(new_segment)
        with open(input_m3u, "w") as f:
            f.write(m3u8_obj.dumps())
    except:
        with open(input_m3u, "w") as f:
            f.write(stub_m3u_tmpl.format(video=video))
Beispiel #4
0
 async def download(self, link):
     m3u8_obj = m3u8.loads(await self.fetch_with_retry(link, text=True),
                           uri=link)
     if not m3u8_obj.media_sequence:
         if m3u8_obj.is_variant:
             for i, playlist in enumerate(m3u8_obj.playlists):
                 click.echo(
                     f'{i}: bandwidth={playlist.stream_info.bandwidth} '
                     f'resolution={playlist.stream_info.resolution} '
                     f'codecs={playlist.stream_info.codecs} ')
             index = click.prompt('Which playlist to download?',
                                  type=click.Choice(
                                      list(range(len(m3u8_obj.playlists)))),
                                  value_proc=int,
                                  default=0)
             return await self.download(
                 m3u8_obj.playlists[index].absolute_uri)
         else:
             tmp_list = m3u8.M3U8()
             tmp_list.version = '3'
             tmp_list.media_sequence = '0'
             tmp_list.target_duration = m3u8_obj.target_duration
             tmp_list.is_endlist = True
             tasks = []
             os.makedirs(self.cache_dir, exist_ok=True)
             bar = ShadyBar(self.name,
                            max=len(m3u8_obj.segments),
                            suffix='%(percent).1f%% - %(eta_td)s')
             for i, segment in enumerate(m3u8_obj.segments):
                 tmp_list.add_segment(
                     m3u8.Segment(
                         f'{os.path.realpath(self.cache_dir)}/{i}.ts',
                         duration=segment.duration,
                         base_uri='file://'))
                 tasks.append(
                     asyncio.ensure_future(
                         self.download_segment(i, segment, bar)))
             tmp_list.dump(f'{self.cache_dir}/filelist.m3u8')
             await asyncio.gather(*tasks)
     else:
         click.echo('Live streaming media is not suppported!')
def create_iframe_segments(segment):
    """
    Takes a transport stream segment and returns I-frame segments for it
    """
    iframes, ts_data, packets_pos = get_segment_data(segment.absolute_uri)

    segment_bytes = 0
    segment_duration = 0
    iframe_segments = []

    for i, frame in enumerate(iframes):

        for j, pos in enumerate(packets_pos):

            if j < len(packets_pos) - 1 and frame[1] == pos:
                # We compared the output of our library to Apple's
                # example streams, and we were off by 188 bytes
                # for each I-frame byte-range.
                pkt_size = int(packets_pos[j + 1]) - int(pos) + 188
                break
            else:
                pkt_size = frame[2]

        byterange = str(pkt_size) + '@' + frame[1]

        if i < len(iframes) - 1:
            extinf = float(iframes[i + 1][0]) - float(frame[0])
        else:
            last_frame_time = ts_data[-1]['best_effort_timestamp_time']
            extinf = float(last_frame_time) - float(frame[0])

        segment_bytes += int(frame[2])
        segment_duration += extinf

        iframe_segments.append(
            m3u8.Segment(segment.uri,
                         segment.base_uri,
                         duration=extinf,
                         byterange=byterange))

    return iframe_segments, segment_bytes, segment_duration
def rewind_playlist(m3u8_obj, start_at, segment_base_uri, key_headers={}):
    if m3u8_obj.is_variant:
        return None
    if m3u8_obj.media_sequence is None and m3u8_obj.is_endlist:
        # FIXME: This will attempt to play the file via the proxy, which will
        # fail due to the relative URLs.
        return m3u8_obj

    # Parse out the important pieces of the segment URI.
    base_segment = m3u8_obj.segments[0].uri
    segment_name_prefix = base_segment[:base_segment.rfind('_') + 1]
    segment_name_suffix = base_segment[base_segment.rfind('.'):]
    segment_time = base_segment[len(segment_name_prefix):len(base_segment) -
                                len(segment_name_suffix)]
    segment_time = parser.parse(segment_time).replace(tzinfo=tz.tzutc())
    # Rewinding is easy. Going forward in time is, sadly, not possible.
    if start_at >= segment_time:
        return m3u8_obj
    start_at -= timedelta(seconds=start_at.second % m3u8_obj.target_duration)

    # Rewind the playlist to the specified time.
    segment_duration = timedelta(seconds=m3u8_obj.target_duration)
    playlist_beginning = segment_time - (segment_duration *
                                         m3u8_obj.media_sequence)
    if start_at < playlist_beginning:
        start_at = playlist_beginning
    time_difference = segment_time - start_at
    time_difference = time_difference.seconds + time_difference.days * 24 * 3600
    segment_time = start_at

    # Start building our new playlist.
    extra_headers = ''
    if len(key_headers) > 0:
        extra_headers = '&headers=' + urllib.quote(
            urllib.urlencode(key_headers))
    playlist = m3u8.M3U8()
    playlist.target_duration = m3u8_obj.target_duration
    playlist.media_sequence = int(m3u8_obj.media_sequence -
                                  (time_difference / m3u8_obj.target_duration))
    playlist.is_endlist = True

    # Stream encryption key parameters.
    key = None
    if m3u8_obj.key is not None:
        key_base_uri = 'http://%s:%d/key?url=' % (__socket_host__,
                                                  __socket_port__)
        key_uri_fmt = '%s%%s%s' % (
            m3u8_obj.key.uri[:m3u8_obj.key.uri.rfind('/') + 1],
            m3u8_obj.key.uri[m3u8_obj.key.uri.rfind('/') + 15:],
        )
        key_time_fmt = '%Y%m%d%H0000'
        key = {
         'method': m3u8_obj.key.method,
         'uri': key_base_uri + \
          urllib.quote_plus(key_uri_fmt % segment_time.strftime(key_time_fmt)) + \
          extra_headers,
        }
        key_hour = segment_time.hour
        playlist.key = m3u8_obj.key
        playlist.key.uri = key['uri']

    # Add the video segments.
    segment_uri_fmt = '%s%s%%s%s' % (segment_base_uri, segment_name_prefix,
                                     segment_name_suffix)
    segment_time_fmt = '%Y%m%d%H%M%S'
    # Give the playlist a six hour duration.
    # The longest game that I've seen so far is just over 5 hours.  Hopefully
    # 6 hours will be long enough.
    for i in xrange(0, int(21600 / m3u8_obj.target_duration)):
        if key is not None and segment_time.hour != key_hour:
            key_hour = segment_time.hour
            key['uri'] = key_base_uri + \
             urllib.quote_plus(key_uri_fmt % segment_time.strftime(key_time_fmt)) + \
             extra_headers
        segment_uri = segment_uri_fmt % segment_time.strftime(segment_time_fmt)
        segment = m3u8.Segment(segment_uri,
                               '',
                               duration=m3u8_obj.target_duration,
                               key=key)
        playlist.add_segment(segment)
        segment_time += segment_duration
    return playlist