diff --git a/classes/Config.php b/classes/Config.php index d662403..b68a2c7 100644 --- a/classes/Config.php +++ b/classes/Config.php @@ -47,6 +47,20 @@ class Config */ public $convert = false; + /** + * Enable advanced conversion mode. + * + * @var bool + */ + public $convertAdvanced = false; + + /** + * List of formats available in advanced conversion mode. + * + * @var array + */ + public $convertAdvancedFormats = ['mp3', 'avi', 'flv', 'wav']; + /** * avconv or ffmpeg binary path. * diff --git a/classes/VideoDownload.php b/classes/VideoDownload.php index 07a3495..5ac5878 100644 --- a/classes/VideoDownload.php +++ b/classes/VideoDownload.php @@ -249,15 +249,18 @@ class VideoDownload } /** - * Get a process that runs avconv in order to convert a video to MP3. + * Get a process that runs avconv in order to convert a video. * - * @param object $video Video object returned by youtube-dl + * @param object $video Video object returned by youtube-dl + * @param int $audioBitrate Audio bitrate of the converted file + * @param string $filetype Filetype of the converted file + * @param bool $audioOnly True to return an audio-only file * * @throws \Exception If avconv/ffmpeg is missing * * @return Process Process */ - private function getAvconvMp3Process(\stdClass $video) + private function getAvconvProcess(\stdClass $video, $audioBitrate, $filetype = 'mp3', $audioOnly = true) { if (!$this->checkCommand([$this->config->avconv, '-version'])) { throw(new \Exception('Can\'t find avconv or ffmpeg')); @@ -269,6 +272,12 @@ class VideoDownload $rtmpArguments = []; } + if ($audioOnly) { + $videoArguments = ['-vn']; + } else { + $videoArguments = []; + } + $arguments = array_merge( [ $this->config->avconv, @@ -277,9 +286,11 @@ class VideoDownload $rtmpArguments, [ '-i', $video->url, - '-f', 'mp3', - '-b:a', $this->config->audioBitrate.'k', - '-vn', + '-f', $filetype, + '-b:a', $audioBitrate.'k', + ], + $videoArguments, + [ 'pipe:1', ] ); @@ -311,7 +322,7 @@ class VideoDownload throw(new \Exception('Conversion of M3U8 files is not supported.')); } - $avconvProc = $this->getAvconvMp3Process($video); + $avconvProc = $this->getAvconvProcess($video, $this->config->audioBitrate); $stream = popen($avconvProc->getCommandLine(), 'r'); @@ -448,4 +459,36 @@ class VideoDownload return $stream; } + + /** + * Get the stream of a converted video. + * + * @param string $url URL of page + * @param string $format Source format to use for the conversion + * @param int $audioBitrate Audio bitrate of the converted file + * @param string $filetype Filetype of the converted file + * @param string $password Video password + * + * @throws \Exception If your try to convert and M3U8 video + * @throws \Exception If the popen stream was not created correctly + * + * @return resource popen stream + */ + public function getConvertedStream($url, $format, $audioBitrate, $filetype, $password = null) + { + $video = $this->getJSON($url, $format, $password); + if (in_array($video->protocol, ['m3u8', 'm3u8_native'])) { + throw(new \Exception('Conversion of M3U8 files is not supported.')); + } + + $avconvProc = $this->getAvconvProcess($video, $audioBitrate, $filetype, false); + + $stream = popen($avconvProc->getCommandLine(), 'r'); + + if (!is_resource($stream)) { + throw new \Exception('Could not open popen stream.'); + } + + return $stream; + } } diff --git a/config/config.example.yml b/config/config.example.yml index 25d0469..9254516 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -15,6 +15,12 @@ params: # True to enable audio conversion convert: false +# True to enable advanced conversion mode +convertAdvanced: false + +# List of formats available in advanced conversion mode +convertAdvancedFormats: [mp3, avi, flv, wav] + # Path to your avconv or ffmpeg binary avconv: vendor/bin/ffmpeg diff --git a/controllers/FrontController.php b/controllers/FrontController.php index 711f474..721da4b 100644 --- a/controllers/FrontController.php +++ b/controllers/FrontController.php @@ -471,6 +471,45 @@ class FrontController } } + /** + * Return a converted video file. + * + * @param Request $request PSR-7 request + * @param Response $response PSR-7 response + * @param array $params GET query parameters + * @param string $format Requested source format + * + * @return Response HTTP response + */ + private function getConvertedResponse(Request $request, Response $response, array $params, $format) + { + $password = $request->getParam('password'); + $response = $response->withHeader( + 'Content-Disposition', + 'attachment; filename="'. + $this->download->getFileNameWithExtension( + $params['customFormat'], + $params['url'], + $format, + $password + ).'"' + ); + $response = $response->withHeader('Content-Type', 'video/'.$params['customFormat']); + + if ($request->isGet() || $request->isPost()) { + $process = $this->download->getConvertedStream( + $params['url'], + $format, + $params['customBitrate'], + $params['customFormat'], + $password + ); + $response = $response->withBody(new Stream($process)); + } + + return $response; + } + /** * Redirect to video file. * @@ -481,14 +520,18 @@ class FrontController */ public function redirect(Request $request, Response $response) { - $url = $request->getQueryParam('url'); + $params = $request->getQueryParams(); $format = $this->getFormat($request); - if (isset($url)) { + if (isset($params['url'])) { try { - return $this->getRedirectResponse($url, $format, $response, $request); + if ($this->config->convertAdvanced && !is_null($request->getQueryParam('customConvert'))) { + return $this->getConvertedResponse($request, $response, $params, $format); + } + + return $this->getRedirectResponse($params['url'], $format, $response, $request); } catch (PasswordException $e) { return $response->withRedirect( - $this->container->get('router')->pathFor('video').'?url='.urlencode($url) + $this->container->get('router')->pathFor('video').'?url='.urlencode($params['url']) ); } catch (\Exception $e) { $response->getBody()->write($e->getMessage()); diff --git a/css/style.css b/css/style.css index db6ab28..0dc7cd2 100644 --- a/css/style.css +++ b/css/style.css @@ -556,6 +556,10 @@ h1 { font-family:monospace; } +.customBitrate { + width: 6ex; +} + .locales { float: left; padding-left: 1em; diff --git a/i18n/template.pot b/i18n/template.pot index 79b3106..f8dd821 100644 --- a/i18n/template.pot +++ b/i18n/template.pot @@ -17,8 +17,8 @@ msgstr "" msgid ":" msgstr "" -#: templates/playlist.tpl:26 templates/password.tpl:10 templates/video.tpl:83 -#: templates/video.tpl:86 templates/index.tpl:19 +#: templates/playlist.tpl:26 templates/password.tpl:10 templates/video.tpl:97 +#: templates/video.tpl:100 templates/index.tpl:19 msgid "Download" msgstr "" @@ -70,6 +70,14 @@ msgstr "" msgid "Detailed formats" msgstr "" +#: templates/video.tpl:86 +msgid "Convert into a custom format:" +msgstr "" + +#: templates/video.tpl:94 +msgid "kbit/s audio" +msgstr "" + #: templates/inc/footer.tpl:4 msgid "Code by" msgstr "" diff --git a/resources/FAQ.md b/resources/FAQ.md index 5770b72..8f68959 100644 --- a/resources/FAQ.md +++ b/resources/FAQ.md @@ -140,3 +140,17 @@ You then need to merge them together with a tool like ffmpeg. You can also enable the experimental remux mode that will merge the best video and the best audio format on the fly. + +## I want to convert videos to something other than MP3 + +By default the `convert` option only allows converting to MP3, +in order to keep things simple and ressources usage low. +However, you can use the `convertAdvanced` option like this: + +```yaml +convertAdvanced: true +convertAdvancedFormats: [mp3, avi, flv, wav] +``` + +This will add new inputs on the download page +that allow users to converted videos to other formats. diff --git a/templates/video.tpl b/templates/video.tpl index 7198e0a..ba5f35a 100644 --- a/templates/video.tpl +++ b/templates/video.tpl @@ -79,7 +79,21 @@ {/if} {/foreach} +