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;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -556,6 +556,10 @@ h1 {
|
|||
font-family:monospace;
|
||||
}
|
||||
|
||||
.customBitrate {
|
||||
width: 6ex;
|
||||
}
|
||||
|
||||
.locales {
|
||||
float: left;
|
||||
padding-left: 1em;
|
||||
|
|
|
@ -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 ""
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -79,7 +79,21 @@
|
|||
{/if}
|
||||
{/foreach}
|
||||
</optgroup>
|
||||
<option value="{$format->format_id}">
|
||||
</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/>
|
||||
</form>
|
||||
{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.
|
||||
*
|
||||
|
|
|
@ -124,4 +124,15 @@ class VideoDownloadStubsTest extends TestCase
|
|||
);
|
||||
$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
|
||||
* @requires OS Linux
|
||||
|
@ -498,4 +498,33 @@ class VideoDownloadTest extends TestCase
|
|||
);
|
||||
$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