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.

99 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. retval = subprocess.call(args)
  28. if retval == 0:
  29. fsize = os.path.getsize(encodeFilename(tmpfilename))
  30. self.to_screen('\r[%s] %s bytes' % (args[0], fsize))
  31. self.try_rename(tmpfilename, filename)
  32. self._hook_progress({
  33. 'downloaded_bytes': fsize,
  34. 'total_bytes': fsize,
  35. 'filename': filename,
  36. 'status': 'finished',
  37. })
  38. return True
  39. else:
  40. self.to_stderr('\n')
  41. self.report_error('%s exited with code %d' % (ffpp.basename, retval))
  42. return False
  43. class NativeHlsFD(FragmentFD):
  44. """ A more limited implementation that does not require ffmpeg """
  45. FD_NAME = 'hlsnative'
  46. def real_download(self, filename, info_dict):
  47. man_url = info_dict['url']
  48. self.to_screen('[%s] Downloading m3u8 manifest' % self.FD_NAME)
  49. manifest = self.ydl.urlopen(man_url).read()
  50. s = manifest.decode('utf-8', 'ignore')
  51. fragment_urls = []
  52. for line in s.splitlines():
  53. line = line.strip()
  54. if line and not line.startswith('#'):
  55. segment_url = (
  56. line
  57. if re.match(r'^https?://', line)
  58. else compat_urlparse.urljoin(man_url, line))
  59. fragment_urls.append(segment_url)
  60. # We only download the first fragment during the test
  61. if self.params.get('test', False):
  62. break
  63. ctx = {
  64. 'filename': filename,
  65. 'total_frags': len(fragment_urls),
  66. }
  67. self._prepare_and_start_frag_download(ctx)
  68. frags_filenames = []
  69. for i, frag_url in enumerate(fragment_urls):
  70. frag_filename = '%s-Frag%d' % (ctx['tmpfilename'], i)
  71. success = ctx['dl'].download(frag_filename, {'url': frag_url})
  72. if not success:
  73. return False
  74. with open(frag_filename, 'rb') as down:
  75. ctx['dest_stream'].write(down.read())
  76. frags_filenames.append(frag_filename)
  77. self._finish_frag_download(ctx)
  78. for frag_file in frags_filenames:
  79. os.remove(frag_file)
  80. return True