coub-dl/coub-dl.py

92 lines
3.1 KiB
Python

from subprocess import call, Popen, PIPE, check_output
from os import listdir, remove
from os.path import splitext
from re import match
from sys import argv
from enum import Enum
from datetime import timedelta
from math import ceil
class Stream(Enum):
AUDIO = 1
VIDEO = 2
class File(Enum):
LIST = 1
LOOP = 2
OUTPUT = 3
def getCmdStdErr(command):
process = Popen(command, stderr=PIPE, stdout=PIPE)
out, err = process.communicate()
return err
def getDuration(ffprobe_output):
durationPattern = r'.*Duration:\s(.+),\sstart.*'
regex = match(durationPattern, str(ffprobe_output))
duration = regex.groups()[0] if regex else None
if not duration:
raise ValueError('Cannot process ffprobe output!')
return duration
def downloadStreams():
call(('/usr/bin/env', 'youtube-dl', '--extract-audio',
'--output', '{}.%(ext)s'.format(FILES[Stream.AUDIO]),
URL))
call(('/usr/bin/env', 'youtube-dl', '--output', '{}.%(ext)s'.format(FILES[Stream.VIDEO]),
URL))
def readExtensions():
for file in listdir():
for filename in FILES:
if match('^{}.*'.format(FILES[filename]), file):
FILES[filename] = file
FILES = {Stream.AUDIO: 'audio', Stream.VIDEO: 'video',
File.LIST: 'list.txt', File.LOOP: 'loop', File.OUTPUT: 'output.mp4'}
OUTPUT_KEYS = [File.OUTPUT]
URL = argv[1] if len(argv) > 0 else '' # youtube-dl error message will be shown if ''
downloadStreams()
readExtensions()
# get stream lengths via ffprobe
audioData= getDuration(getCmdStdErr(('/usr/bin/env', 'ffprobe', FILES[Stream.AUDIO]))).split(':')
videoData = getDuration(getCmdStdErr(('/usr/bin/env', 'ffprobe', FILES[Stream.VIDEO]))).split(':')
audioLen = timedelta(hours=float(audioData[0]), minutes=float(audioData[1]), seconds=float(audioData[2]))
videoLen = timedelta(hours=float(videoData[0]), minutes=float(videoData[1]), seconds=float(videoData[2]))
# decide which stream needs some looping
longer = audioLen if audioLen > videoLen else videoLen
shorter = audioLen if audioLen < videoLen else videoLen
shorterFile = FILES[Stream.AUDIO] if audioLen < videoLen else FILES[Stream.VIDEO]
FILES[File.LOOP] += splitext(shorterFile)[1]
timesLoop = ceil(longer.seconds / shorter.seconds)
# write concat helper file for ffmpeg
with open(FILES[File.LIST], 'w') as f:
for i in range(timesLoop):
print("file '{}'".format(shorterFile), file=f)
# fetch video title
FILES[File.OUTPUT] = check_output(('/usr/bin/env', 'youtube-dl', '--get-title', argv[1])).decode('utf-8').strip() + '.mp4'
# loop & mux
call(('/usr/bin/env', 'ffmpeg', '-f', 'concat', '-i', FILES[File.LIST], '-c', 'copy', FILES[File.LOOP]))
call(('/usr/bin/env', 'ffmpeg', '-i', FILES[File.LOOP],
'-i', FILES[Stream.AUDIO],
'-map', '0', '-map', '1',
'-c', 'copy', FILES[File.OUTPUT]))
# cleanup
for key in FILES:
if key not in OUTPUT_KEYS:
remove(FILES[key])