You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

102 lines
3.3 KiB

9 years ago
9 years ago
9 years ago
  1. from __future__ import unicode_literals
  2. import os
  3. import re
  4. import subprocess
  5. from .common import FileDownloader
  6. from .fragment import FragmentFD
  7. from ..compat import compat_urlparse
  8. from ..postprocessor.ffmpeg import FFmpegPostProcessor
  9. from ..utils import (
  10. encodeArgument,
  11. encodeFilename,
  12. sanitize_open,
  13. )
  14. class HlsFD(FileDownloader):
  15. def real_download(self, filename, info_dict):
  16. url = info_dict['url']
  17. self.report_destination(filename)
  18. tmpfilename = self.temp_name(filename)
  19. ffpp = FFmpegPostProcessor(downloader=self)
  20. if not ffpp.available:
  21. self.report_error('m3u8 download detected but ffmpeg or avconv could not be found. Please install one.')
  22. return False
  23. ffpp.check_version()
  24. args = [
  25. encodeArgument(opt)
  26. for opt in (ffpp.executable, '-y', '-i', url, '-f', 'mp4', '-c', 'copy', '-bsf:a', 'aac_adtstoasc')]
  27. args.append(encodeFilename(tmpfilename, True))
  28. self._debug_cmd(args)
  29. retval = subprocess.call(args)
  30. if retval == 0:
  31. fsize = os.path.getsize(encodeFilename(tmpfilename))
  32. self.to_screen('\r[%s] %s bytes' % (args[0], fsize))
  33. self.try_rename(tmpfilename, filename)
  34. self._hook_progress({
  35. 'downloaded_bytes': fsize,
  36. 'total_bytes': fsize,
  37. 'filename': filename,
  38. 'status': 'finished',
  39. })
  40. return True
  41. else:
  42. self.to_stderr('\n')
  43. self.report_error('%s exited with code %d' % (ffpp.basename, retval))
  44. return False
  45. class NativeHlsFD(FragmentFD):
  46. """ A more limited implementation that does not require ffmpeg """
  47. FD_NAME = 'hlsnative'
  48. def real_download(self, filename, info_dict):
  49. man_url = info_dict['url']
  50. self.to_screen('[%s] Downloading m3u8 manifest' % self.FD_NAME)
  51. manifest = self.ydl.urlopen(man_url).read()
  52. s = manifest.decode('utf-8', 'ignore')
  53. fragment_urls = []
  54. for line in s.splitlines():
  55. line = line.strip()
  56. if line and not line.startswith('#'):
  57. segment_url = (
  58. line
  59. if re.match(r'^https?://', line)
  60. else compat_urlparse.urljoin(man_url, line))
  61. fragment_urls.append(segment_url)
  62. # We only download the first fragment during the test
  63. if self.params.get('test', False):
  64. break
  65. ctx = {
  66. 'filename': filename,
  67. 'total_frags': len(fragment_urls),
  68. }
  69. self._prepare_and_start_frag_download(ctx)
  70. frags_filenames = []
  71. for i, frag_url in enumerate(fragment_urls):
  72. frag_filename = '%s-Frag%d' % (ctx['tmpfilename'], i)
  73. success = ctx['dl'].download(frag_filename, {'url': frag_url})
  74. if not success:
  75. return False
  76. down, frag_sanitized = sanitize_open(frag_filename, 'rb')
  77. ctx['dest_stream'].write(down.read())
  78. frags_filenames.append(frag_sanitized)
  79. self._finish_frag_download(ctx)
  80. for frag_file in frags_filenames:
  81. os.remove(encodeFilename(frag_file))
  82. return True