From 2d0774abeb68769becbbf88e3a31a7f006c4e7aa Mon Sep 17 00:00:00 2001 From: spl0k Date: Tue, 15 Oct 2013 13:14:20 +0200 Subject: [PATCH] Transcoding --- api/media.py | 70 ++++++++++++++++++++++++++++++++++++++++++---------- scanner.py | 11 +++++++++ 2 files changed, 68 insertions(+), 13 deletions(-) diff --git a/api/media.py b/api/media.py index 2ed3b69..2c8f512 100755 --- a/api/media.py +++ b/api/media.py @@ -1,14 +1,23 @@ # coding: utf-8 -from flask import request, send_file +from flask import request, send_file, Response import os.path from PIL import Image +import subprocess -import config +import config, scanner from web import app from db import Track, Folder, User, now, session from api import get_entity +def prepare_transcoding_cmdline(base_cmdline, input_file, input_format, output_format, output_bitrate): + if not base_cmdline: + return None + ret = base_cmdline.split() + for i in xrange(len(ret)): + ret[i] = ret[i].replace('%srcpath', input_file).replace('%srcfmt', input_format).replace('%outfmt', output_format).replace('%outrate', str(output_bitrate)) + return ret + @app.route('/rest/stream.view', methods = [ 'GET', 'POST' ]) def stream_media(): status, res = get_entity(request, Track) @@ -19,20 +28,58 @@ def stream_media(): if format: format = format.lower() - if format != 'raw': + do_transcoding = False + src_suffix = res.suffix() + dst_suffix = res.suffix() + dst_bitrate = res.bitrate + dst_mimetype = res.content_type + + if format != 'raw': # That's from API 1.9.0 but whatever if maxBitRate: try: maxBitRate = int(maxBitRate) except: return request.error_formatter(0, 'Invalid bitrate value') - if res.bitrate > maxBitRate: - # TODO transcode - pass + if dst_bitrate > maxBitRate and maxBitRate != 0: + do_transcoding = True + dst_bitrate = maxBitRate - if format and format != res.suffix(): - # TODO transcode - pass + if format and format != src_suffix: + do_transcoding = True + dst_suffix = format + dst_mimetype = scanner.get_mime(dst_suffix) + + if do_transcoding: + transcoder = config.get('transcoding', 'transcoder_{}_{}'.format(src_suffix, dst_suffix)) + decoder = config.get('transcoding', 'decoder_' + src_suffix) or config.get('transcoding', 'decoder') + encoder = config.get('transcoding', 'encoder_' + dst_suffix) or config.get('transcoding', 'encoder') + if not transcoder and (not decoder or not encoder): + transcoder = config.get('transcoding', 'transcoder') + if not transcoder: + return request.error_formatter(0, 'No way to transcode from {} to {}'.format(src_suffix, dst_suffix)) + + transcoder, decoder, encoder = map(lambda x: prepare_transcoding_cmdline(x, res.path, src_suffix, dst_suffix, dst_bitrate), [ transcoder, decoder, encoder ]) + if transcoder: + proc = subprocess.Popen(transcoder, stdout = subprocess.PIPE) + else: + dec_proc = subprocess.Popen(decoder, stdout = subprocess.PIPE) + proc = subprocess.Popen(encoder, stdin = dec_proc.stdout, stdout = subprocess.PIPE) + + def transcode(): + while True: + data = proc.stdout.read(8192) + if not data: + break + yield data + proc.terminate() + proc.wait() + + response = Response(transcode(), mimetype = dst_mimetype) + else: + response = send_file(res.path) + + # TODO handle estimateContentLength res.play_count = res.play_count + 1 res.last_play = now() @@ -40,10 +87,7 @@ def stream_media(): request.user.last_play_date = now() session.commit() - if estimateContentLength == 'true': - return send_file(res.path), 200, { 'Content-Length': os.path.getsize(res.path) } - - return send_file(res.path) + return response @app.route('/rest/download.view', methods = [ 'GET', 'POST' ]) def download_media(): diff --git a/scanner.py b/scanner.py index 552ac46..4d5f996 100755 --- a/scanner.py +++ b/scanner.py @@ -5,6 +5,17 @@ import time, mimetypes import mutagen import db +def get_mime(ext): + ret = mimetypes.guess_type('dummy.' + ext, False)[0] + if ret: + return ret + try: + module = __import__('mutagen.' + ext, fromlist = [ 'Open' ]) + inst = module.Open() + return inst.mime[0] + except: + return None + class Scanner: def __init__(self, session): self.__session = session