85 lines
2.5 KiB
Python
85 lines
2.5 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
"""Episode Downloader"""
|
||
|
|
||
|
from __future__ import absolute_import, division, unicode_literals
|
||
|
|
||
|
import logging
|
||
|
import re
|
||
|
import subprocess
|
||
|
|
||
|
_LOGGER = logging.getLogger('downloader')
|
||
|
|
||
|
|
||
|
class Downloader:
|
||
|
""" Allows to download an episode to disk for caching purposes. """
|
||
|
|
||
|
def __init__(self):
|
||
|
pass
|
||
|
|
||
|
@staticmethod
|
||
|
def check():
|
||
|
""" Check if we have ffmpeg installed."""
|
||
|
try:
|
||
|
proc = subprocess.Popen(['ffmpeg', '-version'], stderr=subprocess.PIPE)
|
||
|
except OSError:
|
||
|
return False
|
||
|
|
||
|
# Wait for the process to finish
|
||
|
output = proc.stderr.readlines()
|
||
|
proc.wait()
|
||
|
|
||
|
# Check error code
|
||
|
if proc.returncode != 0:
|
||
|
_LOGGER.error(output)
|
||
|
return False
|
||
|
|
||
|
# TODO: Check version
|
||
|
_LOGGER.debug('Output: %s', output)
|
||
|
|
||
|
return True
|
||
|
|
||
|
@staticmethod
|
||
|
def download(stream, output, progress_callback=None):
|
||
|
"""Download the stream to destination."""
|
||
|
try:
|
||
|
cmd = ['ffmpeg', '-y', '-loglevel', 'info', '-i', stream, '-codec', 'copy', output]
|
||
|
# `universal_newlines` makes proc.stderr.readline() also work on \r
|
||
|
proc = subprocess.Popen(cmd, stderr=subprocess.PIPE, universal_newlines=True)
|
||
|
except OSError:
|
||
|
return False
|
||
|
|
||
|
regex_total = re.compile(r"Duration: (\d{2}):(\d{2}):(\d{2})")
|
||
|
regex_current = re.compile(r"time=(\d{2}):(\d{2}):(\d{2})")
|
||
|
|
||
|
# Keep looping over ffmpeg output
|
||
|
total = None
|
||
|
while True:
|
||
|
line = proc.stderr.readline()
|
||
|
if not line:
|
||
|
break
|
||
|
|
||
|
_LOGGER.debug('ffmpeg output: %s', line.rstrip())
|
||
|
|
||
|
# Read the current status that is printed every few seconds.
|
||
|
match = regex_current.search(line)
|
||
|
if match and progress_callback:
|
||
|
cancel = progress_callback(total, int(match.group(1)) * 3600 + int(match.group(2)) * 60 + int(match.group(3)))
|
||
|
if cancel:
|
||
|
proc.terminate()
|
||
|
continue
|
||
|
|
||
|
# Read the total stream duration if we haven't found it already. It's there somewhere in the output. We'll find it.
|
||
|
if not total:
|
||
|
match = regex_total.search(line)
|
||
|
if match:
|
||
|
total = int(match.group(1)) * 3600 + int(match.group(2)) * 60 + int(match.group(3))
|
||
|
|
||
|
# Wait for ffmpeg to be fully finished
|
||
|
proc.wait()
|
||
|
|
||
|
# Check error code
|
||
|
if proc.returncode != 0:
|
||
|
return False
|
||
|
|
||
|
return True
|