def _get_streams(self): self.url = http.resolve_url(self.url) match = _url_re.match(self.url) parsed = urlparse(self.url) if parsed.fragment: channel_id = parsed.fragment elif parsed.path[:3] == '/v/': channel_id = parsed.path.split('/')[-1] else: channel_id = match.group("channel") if not channel_id: return channel_id = channel_id.lower().replace("/", "_") res = http.get(API_URL.format(channel_id)) info = http.json(res, schema=_schema) if not info["success"]: return if info.get("isLive"): name = "live" else: name = "vod" stream = HTTPStream(self.session, info["payload"]) # Wrap the stream in a FLVPlaylist to verify the FLV tags stream = FLVPlaylist(self.session, [stream]) return {name: stream}
def _create_video_clip(self, chunks, start_offset, stop_offset): playlist_duration = stop_offset - start_offset playlist_offset = 0 playlist_streams = [] playlist_tags = [] for chunk in chunks: chunk_url = chunk["url"] chunk_length = chunk["length"] chunk_start = playlist_offset chunk_stop = chunk_start + chunk_length chunk_stream = HTTPStream(self.session, chunk_url) if chunk_start <= start_offset <= chunk_stop: try: headers = extract_flv_header_tags(chunk_stream) except IOError as err: raise StreamError("Error while parsing FLV: {0}", err) if not headers.metadata: raise StreamError("Missing metadata tag in the first chunk") metadata = headers.metadata.data.value keyframes = metadata.get("keyframes") if not keyframes: if chunk["upkeep"] == "fail": raise StreamError("Unable to seek into muted chunk, try another timestamp") else: raise StreamError("Missing keyframes info in the first chunk") keyframe_offset = None keyframe_offsets = keyframes.get("filepositions") keyframe_times = [playlist_offset + t for t in keyframes.get("times")] for time, offset in zip(keyframe_times, keyframe_offsets): if time > start_offset: break keyframe_offset = offset if keyframe_offset is None: raise StreamError("Unable to find a keyframe to seek to " "in the first chunk") chunk_headers = dict(Range="bytes={0}-".format(int(keyframe_offset))) chunk_stream = HTTPStream(self.session, chunk_url, headers=chunk_headers) playlist_streams.append(chunk_stream) for tag in headers: playlist_tags.append(tag) elif start_offset <= chunk_start < stop_offset: playlist_streams.append(chunk_stream) playlist_offset += chunk_length return FLVPlaylist(self.session, playlist_streams, tags=playlist_tags, duration=playlist_duration)
def _create_playlist_streams(self, videos): start_offset = int(videos.get("start_offset", 0)) stop_offset = int(videos.get("end_offset", 0)) streams = {} for quality, chunks in videos.get("chunks").items(): if not chunks: if videos.get("restrictions", {}).get(quality) == "chansub": self.logger.warning( "The quality '{0}' is not available " "since it requires a subscription.", quality) continue # Rename 'live' to 'source' if quality == "live": quality = "source" chunks_filtered = list(filter(lambda c: c["url"], chunks)) if len(chunks) != len(chunks_filtered): self.logger.warning( "The video '{0}' contains invalid chunks. " "There will be missing data.", quality) chunks = chunks_filtered chunks_duration = sum(c.get("length") for c in chunks) # If it's a full broadcast we just use all the chunks if start_offset == 0 and chunks_duration == stop_offset: # No need to use the FLV concat if it's just one chunk if len(chunks) == 1: url = chunks[0].get("url") stream = HTTPStream(self.session, url) else: chunks = [ HTTPStream(self.session, c.get("url")) for c in chunks ] stream = FLVPlaylist(self.session, chunks, duration=chunks_duration) else: try: stream = self._create_video_clip(chunks, start_offset, stop_offset) except StreamError as err: self.logger.error("Error while creating video '{0}': {1}", quality, err) continue streams[quality] = stream return streams