feat: Add a new convertAdvanced option
It allows user to convert videos to several other audio/video formats Fixes #148
This commit is contained in:
parent
c5e3c618f2
commit
4972c8ab8e
11 changed files with 221 additions and 14 deletions
|
@ -47,6 +47,20 @@ class Config
|
||||||
*/
|
*/
|
||||||
public $convert = false;
|
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.
|
* avconv or ffmpeg binary path.
|
||||||
*
|
*
|
||||||
|
|
|
@ -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
|
* @throws \Exception If avconv/ffmpeg is missing
|
||||||
*
|
*
|
||||||
* @return Process Process
|
* @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'])) {
|
if (!$this->checkCommand([$this->config->avconv, '-version'])) {
|
||||||
throw(new \Exception('Can\'t find avconv or ffmpeg'));
|
throw(new \Exception('Can\'t find avconv or ffmpeg'));
|
||||||
|
@ -269,6 +272,12 @@ class VideoDownload
|
||||||
$rtmpArguments = [];
|
$rtmpArguments = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($audioOnly) {
|
||||||
|
$videoArguments = ['-vn'];
|
||||||
|
} else {
|
||||||
|
$videoArguments = [];
|
||||||
|
}
|
||||||
|
|
||||||
$arguments = array_merge(
|
$arguments = array_merge(
|
||||||
[
|
[
|
||||||
$this->config->avconv,
|
$this->config->avconv,
|
||||||
|
@ -277,9 +286,11 @@ class VideoDownload
|
||||||
$rtmpArguments,
|
$rtmpArguments,
|
||||||
[
|
[
|
||||||
'-i', $video->url,
|
'-i', $video->url,
|
||||||
'-f', 'mp3',
|
'-f', $filetype,
|
||||||
'-b:a', $this->config->audioBitrate.'k',
|
'-b:a', $audioBitrate.'k',
|
||||||
'-vn',
|
],
|
||||||
|
$videoArguments,
|
||||||
|
[
|
||||||
'pipe:1',
|
'pipe:1',
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -311,7 +322,7 @@ class VideoDownload
|
||||||
throw(new \Exception('Conversion of M3U8 files is not supported.'));
|
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');
|
$stream = popen($avconvProc->getCommandLine(), 'r');
|
||||||
|
|
||||||
|
@ -448,4 +459,36 @@ class VideoDownload
|
||||||
|
|
||||||
return $stream;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,12 @@ params:
|
||||||
# True to enable audio conversion
|
# True to enable audio conversion
|
||||||
convert: false
|
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
|
# Path to your avconv or ffmpeg binary
|
||||||
avconv: vendor/bin/ffmpeg
|
avconv: vendor/bin/ffmpeg
|
||||||
|
|
||||||
|
|
|
@ -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.
|
* Redirect to video file.
|
||||||
*
|
*
|
||||||
|
@ -481,14 +520,18 @@ class FrontController
|
||||||
*/
|
*/
|
||||||
public function redirect(Request $request, Response $response)
|
public function redirect(Request $request, Response $response)
|
||||||
{
|
{
|
||||||
$url = $request->getQueryParam('url');
|
$params = $request->getQueryParams();
|
||||||
$format = $this->getFormat($request);
|
$format = $this->getFormat($request);
|
||||||
if (isset($url)) {
|
if (isset($params['url'])) {
|
||||||
try {
|
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) {
|
} catch (PasswordException $e) {
|
||||||
return $response->withRedirect(
|
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) {
|
} catch (\Exception $e) {
|
||||||
$response->getBody()->write($e->getMessage());
|
$response->getBody()->write($e->getMessage());
|
||||||
|
|
|
@ -556,6 +556,10 @@ h1 {
|
||||||
font-family:monospace;
|
font-family:monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.customBitrate {
|
||||||
|
width: 6ex;
|
||||||
|
}
|
||||||
|
|
||||||
.locales {
|
.locales {
|
||||||
float: left;
|
float: left;
|
||||||
padding-left: 1em;
|
padding-left: 1em;
|
||||||
|
|
|
@ -17,8 +17,8 @@ msgstr ""
|
||||||
msgid ":"
|
msgid ":"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/playlist.tpl:26 templates/password.tpl:10 templates/video.tpl:83
|
#: templates/playlist.tpl:26 templates/password.tpl:10 templates/video.tpl:97
|
||||||
#: templates/video.tpl:86 templates/index.tpl:19
|
#: templates/video.tpl:100 templates/index.tpl:19
|
||||||
msgid "Download"
|
msgid "Download"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -70,6 +70,14 @@ msgstr ""
|
||||||
msgid "Detailed formats"
|
msgid "Detailed formats"
|
||||||
msgstr ""
|
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
|
#: templates/inc/footer.tpl:4
|
||||||
msgid "Code by"
|
msgid "Code by"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
@ -140,3 +140,17 @@ You then need to merge them together with a tool like ffmpeg.
|
||||||
|
|
||||||
You can also enable the experimental remux mode
|
You can also enable the experimental remux mode
|
||||||
that will merge the best video and the best audio format on the fly.
|
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.
|
||||||
|
|
|
@ -79,7 +79,21 @@
|
||||||
{/if}
|
{/if}
|
||||||
{/foreach}
|
{/foreach}
|
||||||
</optgroup>
|
</optgroup>
|
||||||
|
<option value="{$format->format_id}">
|
||||||
</select><br/><br/>
|
</select><br/><br/>
|
||||||
|
{if $config->convertAdvanced}
|
||||||
|
<input type="checkbox" name="customConvert" id="customConvert"/>
|
||||||
|
<label for="customConvert">{t}Convert into a custom format:{/t}</label>
|
||||||
|
<select title="Custom format" name="customFormat">
|
||||||
|
{foreach $config->convertAdvancedFormats as $format}
|
||||||
|
<option>{$format}</option>
|
||||||
|
{/foreach}
|
||||||
|
</select>
|
||||||
|
with
|
||||||
|
<input type="number" value="{$config->audioBitrate}" title="Custom bitrate" class="customBitrate"name="customBitrate" id="customBitrate" />
|
||||||
|
<label for="customBitrate">{t}kbit/s audio{/t}</label>
|
||||||
|
<br/><br/>
|
||||||
|
{/if}
|
||||||
<input class="downloadBtn" type="submit" value="{t}Download{/t}" /><br/>
|
<input class="downloadBtn" type="submit" value="{t}Download{/t}" /><br/>
|
||||||
</form>
|
</form>
|
||||||
{else}
|
{else}
|
||||||
|
|
|
@ -517,6 +517,27 @@ class FrontControllerTest extends TestCase
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the redirect() function with an advanced conversion.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testRedirectWithAdvancedConversion()
|
||||||
|
{
|
||||||
|
$this->config->convertAdvanced = true;
|
||||||
|
$this->assertRequestIsOk(
|
||||||
|
'redirect',
|
||||||
|
[
|
||||||
|
'url' => 'https://www.youtube.com/watch?v=M7IpKCZ47pU',
|
||||||
|
'format' => 'best',
|
||||||
|
'customConvert' => 'on',
|
||||||
|
'customBitrate' => 32,
|
||||||
|
'customFormat' => 'flv',
|
||||||
|
],
|
||||||
|
$this->config
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test the locale() function.
|
* Test the locale() function.
|
||||||
*
|
*
|
||||||
|
|
|
@ -124,4 +124,15 @@ class VideoDownloadStubsTest extends TestCase
|
||||||
);
|
);
|
||||||
$this->download->getPlaylistArchiveStream($video, 'best');
|
$this->download->getPlaylistArchiveStream($video, 'best');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test getConvertedStream function with a buggy popen.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @expectedException Exception
|
||||||
|
*/
|
||||||
|
public function testGetConvertedStreamWithPopenError()
|
||||||
|
{
|
||||||
|
$this->download->getConvertedStream($this->url, 'best', 32, 'flv');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -485,7 +485,7 @@ class VideoDownloadTest extends TestCase
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test getPlaylistArchiveStream function without avconv.
|
* Test getPlaylistArchiveStream function.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
* @requires OS Linux
|
* @requires OS Linux
|
||||||
|
@ -498,4 +498,33 @@ class VideoDownloadTest extends TestCase
|
||||||
);
|
);
|
||||||
$this->assertStream($this->download->getPlaylistArchiveStream($video, 'best'));
|
$this->assertStream($this->download->getPlaylistArchiveStream($video, 'best'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test getConvertedStream function without avconv.
|
||||||
|
*
|
||||||
|
* @param string $url URL
|
||||||
|
* @param string $format Format
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @dataProvider urlProvider
|
||||||
|
*/
|
||||||
|
public function testGetConvertedStream($url, $format)
|
||||||
|
{
|
||||||
|
$this->assertStream($this->download->getConvertedStream($url, $format, 32, 'flv'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test getConvertedStream function with a M3U8 file.
|
||||||
|
*
|
||||||
|
* @param string $url URL
|
||||||
|
* @param string $format Format
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @expectedException Exception
|
||||||
|
* @dataProvider m3uUrlProvider
|
||||||
|
*/
|
||||||
|
public function testGetConvertedStreamM3uError($url, $format)
|
||||||
|
{
|
||||||
|
$this->download->getConvertedStream($url, $format, 32, 'flv');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue