]>
Commit | Line | Data |
---|---|---|
f0b5d6af PH |
1 | from __future__ import unicode_literals |
2 | ||
3bc2ddcc | 3 | import os |
f0b5d6af | 4 | import re |
3bc2ddcc JMF |
5 | import subprocess |
6 | ||
5f9b8394 | 7 | from ..postprocessor.ffmpeg import FFmpegPostProcessor |
3bc2ddcc | 8 | from .common import FileDownloader |
1cc79574 | 9 | from ..compat import ( |
f0b5d6af | 10 | compat_urlparse, |
b686fc18 | 11 | compat_urllib_request, |
1cc79574 PH |
12 | ) |
13 | from ..utils import ( | |
027008b1 | 14 | encodeArgument, |
3bc2ddcc JMF |
15 | encodeFilename, |
16 | ) | |
17 | ||
18 | ||
19 | class HlsFD(FileDownloader): | |
20 | def real_download(self, filename, info_dict): | |
21 | url = info_dict['url'] | |
22 | self.report_destination(filename) | |
23 | tmpfilename = self.temp_name(filename) | |
24 | ||
63948fc6 | 25 | ffpp = FFmpegPostProcessor(downloader=self) |
8ac27a68 | 26 | if not ffpp.available: |
0e44f90e | 27 | self.report_error('m3u8 download detected but ffmpeg or avconv could not be found. Please install one.') |
baf29075 | 28 | return False |
5f9b8394 | 29 | ffpp.check_version() |
027008b1 | 30 | |
4a3da4eb S |
31 | args = [ |
32 | encodeArgument(opt) | |
73fac4e9 | 33 | for opt in (ffpp.executable, '-y', '-i', url, '-f', 'mp4', '-c', 'copy', '-bsf:a', 'aac_adtstoasc')] |
027008b1 S |
34 | args.append(encodeFilename(tmpfilename, True)) |
35 | ||
4a3da4eb | 36 | retval = subprocess.call(args) |
3bc2ddcc JMF |
37 | if retval == 0: |
38 | fsize = os.path.getsize(encodeFilename(tmpfilename)) | |
4a3da4eb | 39 | self.to_screen('\r[%s] %s bytes' % (args[0], fsize)) |
3bc2ddcc JMF |
40 | self.try_rename(tmpfilename, filename) |
41 | self._hook_progress({ | |
42 | 'downloaded_bytes': fsize, | |
43 | 'total_bytes': fsize, | |
44 | 'filename': filename, | |
45 | 'status': 'finished', | |
46 | }) | |
47 | return True | |
48 | else: | |
0e44f90e | 49 | self.to_stderr('\n') |
73fac4e9 | 50 | self.report_error('%s exited with code %d' % (ffpp.basename, retval)) |
3bc2ddcc | 51 | return False |
f0b5d6af PH |
52 | |
53 | ||
54 | class NativeHlsFD(FileDownloader): | |
55 | """ A more limited implementation that does not require ffmpeg """ | |
56 | ||
57 | def real_download(self, filename, info_dict): | |
58 | url = info_dict['url'] | |
59 | self.report_destination(filename) | |
60 | tmpfilename = self.temp_name(filename) | |
61 | ||
62 | self.to_screen( | |
63 | '[hlsnative] %s: Downloading m3u8 manifest' % info_dict['id']) | |
64 | data = self.ydl.urlopen(url).read() | |
65 | s = data.decode('utf-8', 'ignore') | |
66 | segment_urls = [] | |
67 | for line in s.splitlines(): | |
68 | line = line.strip() | |
69 | if line and not line.startswith('#'): | |
70 | segment_url = ( | |
71 | line | |
72 | if re.match(r'^https?://', line) | |
73 | else compat_urlparse.urljoin(url, line)) | |
74 | segment_urls.append(segment_url) | |
75 | ||
b686fc18 PH |
76 | is_test = self.params.get('test', False) |
77 | remaining_bytes = self._TEST_FILE_SIZE if is_test else None | |
f0b5d6af PH |
78 | byte_counter = 0 |
79 | with open(tmpfilename, 'wb') as outf: | |
80 | for i, segurl in enumerate(segment_urls): | |
f0b5d6af PH |
81 | self.to_screen( |
82 | '[hlsnative] %s: Downloading segment %d / %d' % | |
83 | (info_dict['id'], i + 1, len(segment_urls))) | |
b686fc18 | 84 | seg_req = compat_urllib_request.Request(segurl) |
fec02bcc | 85 | if remaining_bytes is not None: |
b686fc18 PH |
86 | seg_req.add_header('Range', 'bytes=0-%d' % (remaining_bytes - 1)) |
87 | ||
88 | segment = self.ydl.urlopen(seg_req).read() | |
fec02bcc | 89 | if remaining_bytes is not None: |
b686fc18 PH |
90 | segment = segment[:remaining_bytes] |
91 | remaining_bytes -= len(segment) | |
92 | outf.write(segment) | |
93 | byte_counter += len(segment) | |
fec02bcc | 94 | if remaining_bytes is not None and remaining_bytes <= 0: |
b686fc18 | 95 | break |
f0b5d6af PH |
96 | |
97 | self._hook_progress({ | |
98 | 'downloaded_bytes': byte_counter, | |
99 | 'total_bytes': byte_counter, | |
100 | 'filename': filename, | |
101 | 'status': 'finished', | |
102 | }) | |
103 | self.try_rename(tmpfilename, filename) | |
104 | return True |