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.

101 lines
3.2 KiB

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