def test_add_part_to_segment(): obj = Segment(uri='fileSequence271.ts', duration=4.00008) obj.add_part(PartialSegment(None, 'filePart271.0.ts', 0.33334)) result = obj.dumps(None) expected = '#EXT-X-PART:DURATION=0.33334,URI="filePart271.0.ts"' assert expected in result
def test_segment_str_method(): segment = Segment('entire.ts', 'http://1.2/', duration=1) expected = '#EXTINF:1,\nentire.ts' result = str(segment).strip() assert result == expected
def test_dumps_segment_with_key(): ("Segment.dumps should render a X-KEY if present but `last_segment` is None") key = { 'method': 'AES-128', 'uri': 'http://foo.bar/key.aes', 'iv': 1234, } segment = Segment( uri='http://foo.bar/video1.ts', duration=2, base_uri='http://foo.bar', key=key, ) rendered = segment.dumps() assert 'X-KEY' in rendered, 'X-KEY not present in {0}'.format(rendered)
def test_add_segment_to_playlist(): obj = m3u8.M3U8() obj.add_segment( Segment( 'entire.ts', 'http://1.2/', duration=1 ) )
def test_dumps_segment_with_key(): ("Segment.dumps should render a X-KEY if present but `last_segment` is None" ) key = { 'method': 'AES-128', 'uri': 'http://foo.bar/key.aes', 'iv': 1234, } segment = Segment( uri='http://foo.bar/video1.ts', duration=2, base_uri='http://foo.bar', key=key, ) rendered = segment.dumps() assert 'X-KEY' in rendered, 'X-KEY not present in {0}'.format(rendered)
async def video(self, url, media_dir, title, playurl): """ :param url: hls 视频流文件 :param media_dir: 下载保存目录 :param title: 视频标题 :param playurl: ts文件地址 :return: """ resp = await self.client.get(url, headers=self.header) media = loads(resp.text) # 拼接ts文件列表 playlist = [ "{playurl}{uri}".format(playurl=playurl, uri=uri) for uri in media.segments.uri ] n = 0 new_segments = [] semaphore = asyncio.BoundedSemaphore(20) tasks = [] # get ts file list # async with self.client.parallel() as parallel: for url in playlist: ts_file = os.path.join(media_dir, title, 'm_{num}.ts'.format(num=n)) ts_path = os.path.join(title, 'm_{num}.ts'.format(num=n)) media.data['segments'][n]['uri'] = ts_path new_segments.append(media.data.get('segments')[n]) tasks.append( asyncio.ensure_future( self.fetch_single_ts_file(url, ts_file, semaphore))) n += 1 results = await asyncio.gather(*tasks) for download_result in results: if not download_result.success: logger.exception("url: {} download failed, reason: {}".format( download_result.data_url, download_result.reason)) # change m3u8 data media.data['segments'] = new_segments # 修改m3u8文件信息 segments = SegmentList([ Segment(base_uri=None, keyobject=find_key(segment.get('key', {}), media.keys), **segment) for segment in media.data.get('segments', []) ]) media.segments = segments # save m3u8 file m3u8_file = os.path.join(media_dir, '{title}.m3u8'.format(title=title)) if not os.path.exists(m3u8_file): with open(m3u8_file, 'w', encoding='utf8') as f: f.write(media.dumps())
def download_video(self, download_dir, resource, nocache=False): resource_dir = os.path.join(download_dir, resource['id']) os.makedirs(resource_dir, exist_ok=True) url = resource['video_hls'].replace('\\', '') self.session.headers.update({ 'Referer': 'https://pc-shop.xiaoe-tech.com/{appid}/video_details?id={resourceid}' .format(appid=self.appid, resourceid=resource['id']) }) media = m3u8.loads(self.session.get(url).text) url_prefix, segments, changed, complete = url.split( 'v.f230')[0], SegmentList(), False, True print('Total: {} part'.format(len(media.data['segments']))) for index, segment in enumerate(media.data['segments']): ts_file = os.path.join(resource_dir, 'v_{}.ts'.format(index)) if not nocache and os.path.exists(ts_file): print('Already Downloaded: {title} {file}'.format( title=resource['title'], file=ts_file)) else: url = url_prefix + segment.get('uri') res = self.session.get(url) if res.status_code == 200: with open(ts_file + '.tmp', 'wb') as ts: ts.write(res.content) os.rename(ts_file + '.tmp', ts_file) changed = True print('Download Successful: {title} {file}'.format( title=resource['title'], file=ts_file)) else: print('Download Failed: {title} {file}'.format( title=resource['title'], file=ts_file)) complete = False segment['uri'] = 'v_{}.ts'.format(index) segments.append( Segment(base_uri=None, keyobject=find_key(segment.get('key', {}), media.keys), **segment)) m3u8_file = os.path.join(resource_dir, 'video.m3u8') if changed or not os.path.exists(m3u8_file): media.segments = segments with open(m3u8_file, 'w', encoding='utf8') as f: f.write(media.dumps()) metadata = {'title': resource['title'], 'complete': complete} with open(os.path.join(download_dir, resource['id'], 'metadata'), 'w') as f: json.dump(metadata, f) return
def video(self, url, media_dir, title, playurl): ''' :param url: hls 视频流文件 :param media_dir: 下载保存目录 :param title: 视频标题 :param playurl: ts文件地址 :return: ''' resp = self.session.get(url, headers=self.header) media = loads(resp.text) # 拼接ts文件列表 playlist = ["{playurl}{uri}".format(playurl=playurl, uri=uri) for uri in media.segments.uri] n = 0 new_segments = [] # get ts file list for url in playlist: ts_file = os.path.join(media_dir, title, 'm_{num}.ts'.format(num=n)) ts_path = os.path.join(title, 'm_{num}.ts'.format(num=n)) media.data['segments'][n]['uri'] = ts_path new_segments.append(media.data.get('segments')[n]) # 下载ts文件 resp = self.session.get(url, headers=self.header, cookies=self.cookie) if resp.status_code != 200: print('Error: {title} {tsfile}'.format(title=title, tsfile=ts_file)) # 如果文件不存在或者本地文件大小于接口返回大小不一致则保存ts文件 if not os.path.exists(ts_file) or os.stat(ts_file).st_size != resp.headers['content-length']: with open(ts_file, 'wb') as ts: ts.write(resp.content) n += 1 # change m3u8 data media.data['segments'] = new_segments # 修改m3u8文件信息 segments = SegmentList( [Segment(base_uri=None, keyobject=find_key(segment.get('key', {}), media.keys), **segment) for segment in media.data.get('segments', [])]) media.segments = segments # save m3u8 file m3u8_file = os.path.join(media_dir, '{title}.m3u8'.format(title=title)) if not os.path.exists(m3u8_file): with open(m3u8_file, 'wb', encoding='utf8') as f: f.write(media.dumps())
def test_should_correctly_update_base_path_if_its_blank(): segment = Segment('entire.ts', 'http://1.2/') assert not segment.base_path segment.base_path = "base_path" assert "http://1.2/base_path/entire.ts" == segment.absolute_uri
def test_base_path_should_just_return_uri_if_absolute(): segment = Segment('http://1.2/entire.ts', '') assert 'http://1.2/entire.ts' == segment.absolute_uri