tidying up
This commit is contained in:
@@ -1,407 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OCA\NCDownloader\Tools;
|
||||
|
||||
//use Symfony\Component\Process\ExecutableFinder;
|
||||
use Symfony\Component\Process\Exception\ProcessFailedException;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class Aria2
|
||||
{
|
||||
//extra Aria2 download options
|
||||
private $options = array();
|
||||
//optional token for authenticating with Aria2
|
||||
private $token = null;
|
||||
//the aria2 method being invoked
|
||||
private $method = null;
|
||||
//the aria2c binary path
|
||||
private $bin = null;
|
||||
// return the following items when getting downloads info by default
|
||||
private $dataFilter = array(
|
||||
'status', 'followedBy', 'totalLength', 'errorMessage', 'dir', 'uploadLength', 'completedLength', 'downloadSpeed', 'files', 'numSeeders', 'connections', 'gid', 'following', 'bittorrent',
|
||||
);
|
||||
//whether to filter the response returned by aria2
|
||||
private $filterResponse = true;
|
||||
//absolute download path
|
||||
private $downloadDir;
|
||||
public function __construct($options = array())
|
||||
{
|
||||
$options += array(
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 6800,
|
||||
'dir' => '/tmp/Downloads',
|
||||
'torrents_dir' => '/tmp/Torrents',
|
||||
'token' => null,
|
||||
'conf_dir' => '/tmp/aria2',
|
||||
'completeHook' => $_SERVER['DOCUMENT_ROOT'] . "/apps/ncdownloader/hooks/completeHook.sh",
|
||||
'settings' => [],
|
||||
);
|
||||
//turn keys in $options into variables
|
||||
extract($options);
|
||||
if (isset($binary) && $this->isExecutable($binary)) {
|
||||
$this->bin = $binary;
|
||||
} else {
|
||||
$this->bin = Helper::findBinaryPath('aria2c', __DIR__ . "/../../bin/aria2c");
|
||||
}
|
||||
if ($this->isInstalled() && !$this->isExecutable()) {
|
||||
chmod($this->bin, 0744);
|
||||
}
|
||||
$this->setDownloadDir($dir);
|
||||
$this->setTorrentsDir($torrents_dir);
|
||||
if (!empty($settings)) {
|
||||
foreach ($settings as $key => $value) {
|
||||
$this->setOption($key, $value);
|
||||
}
|
||||
}
|
||||
$this->php = Helper::findBinaryPath('php');
|
||||
$this->completeHook = $completeHook;
|
||||
$this->startHook = $startHook;
|
||||
$this->rpcUrl = sprintf("http://%s:%s/jsonrpc", $host, $port);
|
||||
$this->tokenString = $token ?? 'ncdownloader123';
|
||||
$this->rpcPort = $rpcPort ?? 6800;
|
||||
$this->dlSpeed = $dlSpeed ?? 0;
|
||||
$this->upSpeed = $upSpeed ?? "1M";
|
||||
$this->logLevel = $logLevel ?? 'warn';
|
||||
$this->setToken($this->tokenString);
|
||||
$this->confDir = $conf_dir;
|
||||
$this->sessionFile = $this->confDir . "/aria2.session";
|
||||
//$this->confFile = $this->confDir . "/aria2.conf";
|
||||
$this->logFile = $this->confDir . "/aria2.log";
|
||||
}
|
||||
public function init()
|
||||
{
|
||||
$this->ch = curl_init($this->rpcUrl);
|
||||
curl_setopt_array($this->ch, array(
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HEADER => false,
|
||||
CURLOPT_SSL_VERIFYPEER => false,
|
||||
));
|
||||
$this->configure();
|
||||
}
|
||||
|
||||
public function setonDownloadStart($path)
|
||||
{
|
||||
$this->onDownloadStart = $path;
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
$this->init();
|
||||
}
|
||||
|
||||
private function hasOption($key)
|
||||
{
|
||||
return (bool) isset($this->options[$key]);
|
||||
}
|
||||
|
||||
private function configure()
|
||||
{
|
||||
if ($this->confDir && !is_dir($this->confDir)) {
|
||||
mkdir($this->confDir, 0755, true);
|
||||
}
|
||||
$dir = "";
|
||||
if (($dir = $this->getDownloadDir()) && !is_dir($dir)) {
|
||||
mkdir($dir, 0755, true);
|
||||
}
|
||||
if (($dir = $this->getTorrentsDir()) && !is_dir($dir)) {
|
||||
mkdir($dir, 0755, true);
|
||||
}
|
||||
$this->followTorrent(true);
|
||||
}
|
||||
public function setToken($token)
|
||||
{
|
||||
$this->token = "token:$token";
|
||||
return $this;
|
||||
}
|
||||
public function setOption($key, $value)
|
||||
{
|
||||
$this->options[$key] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
public function setTorrentsDir($dir)
|
||||
{
|
||||
$this->torrentsDir = $dir;
|
||||
return $this;
|
||||
}
|
||||
public function getTorrentsDir(): string
|
||||
{
|
||||
return $this->torrentsDir;
|
||||
}
|
||||
public function setDownloadDir($dir)
|
||||
{
|
||||
$this->setOption('dir', $dir);
|
||||
$this->downloadDir = $dir;
|
||||
return $this;
|
||||
}
|
||||
public function getDownloadDir(): string
|
||||
{
|
||||
return $this->downloadDir;
|
||||
}
|
||||
public function setFileName($file)
|
||||
{
|
||||
$this->options['out'] = $file;
|
||||
return $this;
|
||||
}
|
||||
public function followTorrent($follow)
|
||||
{
|
||||
$this->options['follow-torrent'] = $follow;
|
||||
return $this;
|
||||
}
|
||||
private function request($data)
|
||||
{
|
||||
$this->init();
|
||||
$defaults = array(
|
||||
'jsonrpc' => '2.0',
|
||||
'id' => 'ncdownloader',
|
||||
'method' => 'aria2.addUri',
|
||||
'params' => null,
|
||||
);
|
||||
|
||||
$data += $defaults;
|
||||
$this->content = json_encode($data);
|
||||
|
||||
if (isset($this->content)) {
|
||||
curl_setopt($this->ch, CURLOPT_POSTFIELDS, $this->content);
|
||||
}
|
||||
$resp = curl_exec($this->ch);
|
||||
curl_close($this->ch);
|
||||
return json_decode($resp, 1);
|
||||
}
|
||||
public function getFollowingGid($gid)
|
||||
{
|
||||
$data = $this->tellStatus($gid);
|
||||
if (!is_array($data)) {
|
||||
return 0;
|
||||
}
|
||||
$data = reset($data);
|
||||
return ($data['following'] ?? 0);
|
||||
}
|
||||
public function tellFail($range = [0, 999])
|
||||
{
|
||||
$this->filterResponse = false;
|
||||
$resp = $this->tellStopped($range);
|
||||
if (!isset($resp['result'])) {
|
||||
return [];
|
||||
}
|
||||
$result = $this->sortDownloadsResult($resp['result'], ['complete', 'removed']);
|
||||
$this->filterResponse = true;
|
||||
return $result;
|
||||
}
|
||||
public function tellAll()
|
||||
{
|
||||
$this->filterResponse = false;
|
||||
return array_merge($this->tellActive([]), $this->tellWaiting([0, 999]), $this->tellStopped([0, 999]));
|
||||
}
|
||||
public function __call($name, $args)
|
||||
{
|
||||
$this->methodName = $name;
|
||||
|
||||
$data = array();
|
||||
if (isset($args[0]) && is_array($args[0]) && count($args) == 1 && strtolower($name) !== "adduri") {
|
||||
$args = reset($args);
|
||||
}
|
||||
switch ($name) {
|
||||
case "addUri":
|
||||
case "addTorrent":
|
||||
array_push($args, $this->options);
|
||||
break;
|
||||
case "tellActive":
|
||||
case "tellWaiting":
|
||||
case "tellStopped":
|
||||
array_push($args, $this->dataFilter);
|
||||
break;
|
||||
case "tellStatus":
|
||||
case "getFiles":
|
||||
array_push($args, $this->dataFilter);
|
||||
break;
|
||||
}
|
||||
if (isset($this->token)) {
|
||||
array_unshift($args, $this->token);
|
||||
}
|
||||
$data = array('params' => $args, 'method' => 'aria2.' . $name);
|
||||
$rawResp = $this->request($data);
|
||||
|
||||
if (!$this->filterResponse) {
|
||||
return $rawResp;
|
||||
}
|
||||
return $this->parseResp($rawResp);
|
||||
}
|
||||
private function sortDownloadsResult($result, $statusFilter = null)
|
||||
{
|
||||
$data = [];
|
||||
if (!isset($statusFilter)) {
|
||||
$statusFilter = ['error'];
|
||||
}
|
||||
if (empty($result)) {
|
||||
return [];
|
||||
}
|
||||
foreach ($result as $info) {
|
||||
$info = Helper::filterData($info);
|
||||
if (isset($info['files'])) {
|
||||
foreach ($info['files'] as $key => &$files) {
|
||||
$files = Helper::filterData($files, array('path', 'length'));
|
||||
}
|
||||
}
|
||||
if (in_array($info['status'], $statusFilter)) {
|
||||
continue;
|
||||
}
|
||||
array_push($data, $info);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
public function parseResp($resp = array())
|
||||
{
|
||||
$data = array();
|
||||
if (isset($resp['error']) && isset($resp['error']['message'])) {
|
||||
$data['error'] = $resp['error']['message'];
|
||||
return $data;
|
||||
}
|
||||
$result = $resp['result'] ?? null;
|
||||
if (!isset($result)) {
|
||||
return $data;
|
||||
}
|
||||
if ($this->methodName === 'tellStatus' && isset($result['files'])) {
|
||||
foreach ($result['files'] as $key => &$files) {
|
||||
$files = Helper::filterData($files, array('path', 'length'));
|
||||
}
|
||||
array_push($data, $result);
|
||||
return $data;
|
||||
}
|
||||
// parse response for tellActive,tellWaiting,and tellStopped
|
||||
if (strpos($this->methodName, "tell") !== false && is_array($result)) {
|
||||
return $this->sortDownloadsResult($result);
|
||||
}
|
||||
return $resp;
|
||||
}
|
||||
public function getStatus($gid)
|
||||
{
|
||||
return $this->tellStatus($gid);
|
||||
}
|
||||
public function restart()
|
||||
{
|
||||
$this->stop();
|
||||
$this->start();
|
||||
}
|
||||
|
||||
public function download(String $url)
|
||||
{
|
||||
$resp = $this->addUri([$url]);
|
||||
|
||||
if (isset($resp['error'])) {
|
||||
return $resp;
|
||||
}
|
||||
|
||||
if (isset($resp['result']) && is_string($gid = $resp['result'])) {
|
||||
return $gid;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function btDownload($file)
|
||||
{
|
||||
if ($data = file_get_contents($file)) {
|
||||
$filename = Helper::getBasicFilename($file);
|
||||
$torrent = base64_encode($data);
|
||||
$resp = $this->addTorrent($torrent, []);
|
||||
} else {
|
||||
return ['error' => "no valid torrents file!"];
|
||||
}
|
||||
if (isset($resp['error'])) {
|
||||
return $resp;
|
||||
}
|
||||
|
||||
if (isset($resp['result']) && is_string($gid = $resp['result'])) {
|
||||
return ['gid' => $gid, 'filename' => $filename];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getDefaults()
|
||||
{
|
||||
return [
|
||||
'--continue',
|
||||
'--daemon=true',
|
||||
'--enable-rpc=true',
|
||||
'--rpc-secret=' . $this->tokenString,
|
||||
'--listen-port=51413',
|
||||
'--save-session=' . $this->sessionFile,
|
||||
'--input-file=' . $this->sessionFile,
|
||||
'--log=' . $this->logFile,
|
||||
'--rpc-listen-port=' . $this->rpcPort,
|
||||
'--follow-torrent=true',
|
||||
'--enable-dht=true',
|
||||
'--enable-peer-exchange=true',
|
||||
'--peer-id-prefix=-TR2770-',
|
||||
'--user-agent=Transmission/2.77',
|
||||
'--log-level=' . $this->logLevel,
|
||||
'--seed-ratio=1.0',
|
||||
'--bt-seed-unverified=true',
|
||||
'--max-overall-upload-limit=' . $this->upSpeed,
|
||||
'--max-overall-download-limit=' . $this->dlSpeed,
|
||||
'--max-connection-per-server=4',
|
||||
'--max-concurrent-downloads=10',
|
||||
'--check-certificate=false',
|
||||
'--on-download-complete=' . $this->completeHook,
|
||||
'--on-download-start=' . $this->startHook,
|
||||
];
|
||||
}
|
||||
public function start($bin = null)
|
||||
{
|
||||
//aria2c refuses to start with no errors when input-file is set but missing
|
||||
if (!file_exists($this->sessionFile)) {
|
||||
file_put_contents($this->sessionFile, '');
|
||||
}
|
||||
|
||||
//$process = new Process([$this->bin, "--conf-path=" . $this->confFile]);
|
||||
$defaults = $this->getDefaults();
|
||||
array_unshift($defaults, $this->bin);
|
||||
$process = new Process($defaults);
|
||||
try {
|
||||
$process->mustRun();
|
||||
$output = $process->getOutput();
|
||||
} catch (ProcessFailedException $exception) {
|
||||
$error = $exception->getMessage();
|
||||
}
|
||||
$resp = [];
|
||||
if (isset($error)) {
|
||||
$resp['error'] = $error;
|
||||
$resp['status'] = false;
|
||||
} else {
|
||||
$resp['status'] = true;
|
||||
}
|
||||
return $resp;
|
||||
}
|
||||
public function isInstalled()
|
||||
{
|
||||
return @is_file($this->bin);
|
||||
}
|
||||
public function isExecutable()
|
||||
{
|
||||
return @is_executable($this->bin);
|
||||
}
|
||||
|
||||
public function isRunning()
|
||||
{
|
||||
$resp = $this->getSessionInfo();
|
||||
return (bool) $resp;
|
||||
}
|
||||
|
||||
public function getBin()
|
||||
{
|
||||
return $this->bin;
|
||||
}
|
||||
public function stop()
|
||||
{
|
||||
$resp = $this->shutdown();
|
||||
sleep(3);
|
||||
return $resp ?? null;
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OCA\NCDownloader\Tools;
|
||||
|
||||
//require __DIR__ . "/../../vendor/autoload.php";
|
||||
use Symfony\Component\HttpClient\HttpClient;
|
||||
|
||||
final class Client
|
||||
{
|
||||
public function __construct(?array $options = null)
|
||||
{
|
||||
$this->client = HttpClient::create($this->configure($options));
|
||||
}
|
||||
|
||||
public static function create(?array $options = null)
|
||||
{
|
||||
return new self($options);
|
||||
}
|
||||
|
||||
private function defaultUserAgent(): string
|
||||
{
|
||||
return "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36";
|
||||
}
|
||||
|
||||
private function defaultOptions(): array
|
||||
{
|
||||
$settings = [
|
||||
'headers' => [
|
||||
],
|
||||
'extra' => ['curl' => null],
|
||||
];
|
||||
return $settings;
|
||||
}
|
||||
|
||||
private function configure(array $options): array
|
||||
{
|
||||
|
||||
extract($options);
|
||||
$settings = $this->defaultOptions();
|
||||
$settings['extra']['curl'] = $curl ?? [];
|
||||
$settings['headers'] = $headers ?? [];
|
||||
|
||||
if ($ipv4 || $force_ipv4) {
|
||||
$settings['extra']['curl'] = [CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4];
|
||||
}
|
||||
$settings['headers']['User-Agent'] = $useragent ?? $this->defaultUserAgent();
|
||||
|
||||
return $settings;
|
||||
}
|
||||
public function request(string $url, $method, ?array $options = [])
|
||||
{
|
||||
return $this->client->request($url, $method, $options);
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace OCA\NCDownloader\Tools;
|
||||
|
||||
use OCA\NCDownloader\Tools\Aria2;
|
||||
use OCA\NCDownloader\Tools\DbHelper;
|
||||
use OCA\NCDownloader\Aria2\Aria2;
|
||||
use OCA\NCDownloader\Db\Helper as DbHelper;
|
||||
|
||||
class Counters
|
||||
{
|
||||
@@ -22,13 +22,13 @@ class Counters
|
||||
'waiting' => $this->getCounter('tellWaiting'),
|
||||
'complete' => $this->getCounter('tellStopped'),
|
||||
'fail' => $this->getCounter('tellFail'),
|
||||
'youtube-dl' => $this->getCounter('youtube-dl'),
|
||||
'ytdl' => $this->getCounter('ytdl'),
|
||||
];
|
||||
}
|
||||
private function getCounter($action = 'tellActive')
|
||||
{
|
||||
if ($action === 'youtube-dl') {
|
||||
$data = $this->dbconn->getYoutubeByUid($this->uid);
|
||||
if ($action === 'ytdl') {
|
||||
$data = $this->dbconn->getYtdlByUid($this->uid);
|
||||
} else if ($action === 'tellActive') {
|
||||
$data = $this->aria2->{$action}([]);
|
||||
} else {
|
||||
@@ -38,7 +38,7 @@ class Counters
|
||||
if (!is_array($data) && count($data) < 1) {
|
||||
return 0;
|
||||
}
|
||||
if ($action !== 'youtube-dl') {
|
||||
if ($action !== 'ytdl') {
|
||||
$data = $this->filterData($data);
|
||||
}
|
||||
return count($data);
|
||||
|
||||
@@ -1,147 +0,0 @@
|
||||
<?php
|
||||
namespace OCA\NCDownloader\Tools;
|
||||
|
||||
class DbHelper
|
||||
{
|
||||
//@var OC\DB\ConnectionAdapter
|
||||
private $conn;
|
||||
private $table = "ncdownloader_info";
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->conn = \OC::$server->getDatabaseConnection();
|
||||
$this->queryBuilder = $this->conn->getQueryBuilder();
|
||||
$this->prefixedTable = $this->queryBuilder->getTableName($this->table);
|
||||
//$container = \OC::$server->query(\OCP\IServerContainer::class);
|
||||
//Helper::debug(get_class($container->query(\OCP\RichObjectStrings\IValidator::class)));
|
||||
//$this->conn = \OC::$server->query(Connection::class);//working only with 22
|
||||
//$this->connAdapter = \OC::$server->getDatabaseConnection();
|
||||
//$this->conn = $this->connAdapter->getInner();
|
||||
}
|
||||
|
||||
public function insert($insert)
|
||||
{
|
||||
$inserted = (bool) $this->conn->insertIfNotExist('*PREFIX*' . $this->table, $insert, [
|
||||
'gid',
|
||||
]);
|
||||
return $inserted;
|
||||
}
|
||||
public function getAll()
|
||||
{
|
||||
//OC\DB\QueryBuilder\QueryBuilder
|
||||
$queryBuilder = $this->queryBuilder
|
||||
->select('filename', 'type', 'gid', 'timestamp', 'status')
|
||||
->from($this->table)
|
||||
->execute();
|
||||
return $queryBuilder->fetchAll();
|
||||
}
|
||||
|
||||
public function getByUid($uid)
|
||||
{
|
||||
$queryBuilder = $this->queryBuilder
|
||||
->select('*')
|
||||
->from($this->table)
|
||||
->where('uid = :uid')
|
||||
->setParameter('uid', $uid)
|
||||
->execute();
|
||||
return $queryBuilder->fetchAll();
|
||||
}
|
||||
|
||||
public function getUidByGid($gid)
|
||||
{
|
||||
$queryBuilder = $this->queryBuilder
|
||||
->select('uid')
|
||||
->from($this->table)
|
||||
->where('gid = :gid')
|
||||
->setParameter('gid', $gid)
|
||||
->execute();
|
||||
return $queryBuilder->fetchColumn();
|
||||
}
|
||||
|
||||
public function getYoutubeByUid($uid)
|
||||
{
|
||||
$qb = $this->queryBuilder
|
||||
->select('*')
|
||||
->from($this->table)
|
||||
->where('uid = :uid')
|
||||
->andWhere('type = :type')
|
||||
->setParameter('uid', $uid)
|
||||
->setParameter('type', Helper::DOWNLOADTYPE['YOUTUBE-DL'])
|
||||
->orderBy('id', 'DESC')
|
||||
->execute();
|
||||
return $qb->fetchAll();
|
||||
}
|
||||
|
||||
public function getByGid($gid)
|
||||
{
|
||||
$queryBuilder = $this->queryBuilder
|
||||
->select('*')
|
||||
->from($this->table)
|
||||
->where('gid = :gid')
|
||||
->setParameter('gid', $gid)
|
||||
->execute();
|
||||
return $queryBuilder->fetch();
|
||||
}
|
||||
|
||||
public function save(array $keys, $values = array(), $conditions = array())
|
||||
{
|
||||
return $this->conn->setValues($this->table, $keys, $values, $conditions);
|
||||
}
|
||||
|
||||
public function deleteByGid($gid)
|
||||
{
|
||||
$qb = $this->queryBuilder
|
||||
->delete($this->table)
|
||||
->where('gid = :gid')
|
||||
->setParameter('gid', $gid);
|
||||
return $qb->execute();
|
||||
}
|
||||
public function executeUpdate($sql, $values)
|
||||
{
|
||||
return $this->conn->executeUpdate($sql, $values);
|
||||
}
|
||||
|
||||
public function updateStatus($gid, $status = 1)
|
||||
{
|
||||
$query = $this->queryBuilder;
|
||||
$query->update($this->table)
|
||||
->set("status", $query->createNamedParameter($status))
|
||||
->where('gid = :gid')
|
||||
->setParameter('gid', $gid);
|
||||
return $query->execute();
|
||||
//$sql = sprintf("UPDATE %s set status = ? WHERE gid = ?", $this->prefixedTable);
|
||||
//$this->execute($sql, [$status, $gid]);
|
||||
}
|
||||
|
||||
public function updateFilename($gid, $filename)
|
||||
{
|
||||
$query = $this->queryBuilder;
|
||||
$query->update($this->table)
|
||||
->set("filename", $query->createNamedParameter($filename))
|
||||
->where('gid = :gid')
|
||||
->andWhere('filename = :filename')
|
||||
->setParameter('gid', $gid)
|
||||
->setParameter('filename', 'unknown');
|
||||
return $query->execute();
|
||||
}
|
||||
|
||||
public function getDBType(): string
|
||||
{
|
||||
return \OC::$server->getConfig()->getSystemValue('dbtype', "mysql");
|
||||
}
|
||||
|
||||
public function getExtra($data)
|
||||
{
|
||||
if ($this->getDBType() == "pgsql" && is_resource($data)) {
|
||||
if (function_exists("pg_unescape_bytea")) {
|
||||
$extra = pg_unescape_bytea(stream_get_contents($data));
|
||||
}
|
||||
else {
|
||||
$extra = stream_get_contents($data);
|
||||
}
|
||||
return unserialize($extra);
|
||||
}
|
||||
return unserialize($data);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,8 +4,8 @@ namespace OCA\NCDownloader\Tools;
|
||||
|
||||
use Exception;
|
||||
use OCA\NCDownloader\Search\Sites\searchInterface;
|
||||
use OCA\NCDownloader\Tools\aria2Options;
|
||||
use OCA\NCDownloader\Tools\Settings;
|
||||
use OCA\NCDownloader\Aria2\Options as aria2Options;
|
||||
use OCA\NCDownloader\Db\Settings;
|
||||
use OCP\IUser;
|
||||
use OC\Files\Filesystem;
|
||||
use OC_Util;
|
||||
@@ -108,7 +108,7 @@ class Helper
|
||||
|
||||
public static function isYoutubeType($url)
|
||||
{
|
||||
$regex = '%^(?:(?:https?)://)(?:[a-z0-9_]*\.)?(?:twitter|youtube)\.com/%i';
|
||||
$regex = '%^(?:(?:https?)://)(?:[a-z0-9_]*\.)?(?:twitter|ytdl)\.com/%i';
|
||||
return (bool) preg_match($regex, $url);
|
||||
}
|
||||
|
||||
@@ -435,12 +435,12 @@ class Helper
|
||||
return Settings::create($uid);
|
||||
}
|
||||
|
||||
public static function getYoutubeConfig($uid = null): array
|
||||
public static function getYtdlConfig($uid = null): array
|
||||
{
|
||||
$config = [
|
||||
'binary' => self::getSettings("ncd_yt_binary", null, Settings::TYPE['SYSTEM']),
|
||||
'downloadDir' => Helper::getRealDownloadDir(),
|
||||
'settings' => self::newSettings()->getYoutube(),
|
||||
'settings' => self::newSettings()->getYtdl(),
|
||||
];
|
||||
return $config;
|
||||
}
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OCA\NCDownloader\Tools;
|
||||
|
||||
use OC\AllConfig;
|
||||
|
||||
class Settings extends AllConfig
|
||||
{
|
||||
//@config OC\AppConfig
|
||||
private $appConfig;
|
||||
|
||||
//@OC\SystemConfig
|
||||
private $sysConfig;
|
||||
|
||||
//@OC\AllConfig
|
||||
private $allConfig;
|
||||
private $user;
|
||||
private $appName;
|
||||
//type of settings (system = 1 or app =2)
|
||||
private $type;
|
||||
private static $instance = null;
|
||||
public const TYPE = ['SYSTEM' => 1, 'USER' => 2, 'APP' => 3];
|
||||
public function __construct($user = null)
|
||||
{
|
||||
$this->appConfig = \OC::$server->getAppConfig();
|
||||
$this->sysConfig = \OC::$server->getSystemConfig();
|
||||
$this->appName = 'ncdownloader';
|
||||
$this->type = self::TYPE['USER'];
|
||||
$this->user = $user;
|
||||
$this->allConfig = new AllConfig($this->sysConfig);
|
||||
//$this->connAdapter = \OC::$server->getDatabaseConnection();
|
||||
//$this->conn = $this->connAdapter->getInner();
|
||||
}
|
||||
public static function create($user = null)
|
||||
{
|
||||
|
||||
if (!self::$instance) {
|
||||
self::$instance = new static($user);
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
public function setType($type)
|
||||
{
|
||||
$this->type = $type;
|
||||
return $this;
|
||||
}
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
if ($this->type == self::TYPE['USER'] && isset($this->user)) {
|
||||
return $this->allConfig->getUserValue($this->user, $this->appName, $key, $default);
|
||||
} else if ($this->type == self::TYPE['SYSTEM']) {
|
||||
return $this->allConfig->getSystemValue($key, $default);
|
||||
} else {
|
||||
return $this->allConfig->getAppValue($this->appName, $key, $default);
|
||||
}
|
||||
}
|
||||
public function getAria2()
|
||||
{
|
||||
$settings = $this->allConfig->getUserValue($this->user, $this->appName, "custom_aria2_settings", '');
|
||||
return json_decode($settings, 1);
|
||||
}
|
||||
|
||||
public function getYoutube()
|
||||
{
|
||||
$settings = $this->get("custom_youtube_dl_settings");
|
||||
return json_decode($settings, 1);
|
||||
}
|
||||
public function getAll()
|
||||
{
|
||||
if ($this->type === self::TYPE['APP']) {
|
||||
return $this->getAllAppValues();
|
||||
} else {
|
||||
$data = $this->getAllUserSettings();
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
public function save($key, $value)
|
||||
{
|
||||
try {
|
||||
if ($this->type == self::TYPE['USER'] && isset($this->user)) {
|
||||
$this->allConfig->setUserValue($this->user, $this->appName, $key, $value);
|
||||
} else if ($this->type == self::TYPE['SYSTEM']) {
|
||||
$this->allConfig->setSystemValue($key, $value);
|
||||
} else {
|
||||
$this->allConfig->setAppValue($this->appName, $key, $value);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return ['error' => $e->getMessage];
|
||||
}
|
||||
return ['message' => "Saved!"];
|
||||
|
||||
}
|
||||
public function getAllAppValues()
|
||||
{
|
||||
$keys = $this->getAllKeys();
|
||||
$value = [];
|
||||
foreach ($keys as $key) {
|
||||
$value[$key] = $this->allConfig->getAppValue($this->appName, $key);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
public function getAllKeys()
|
||||
{
|
||||
return $this->allConfig->getAppKeys($this->appName);
|
||||
}
|
||||
|
||||
public function getAllUserSettings()
|
||||
{
|
||||
$keys = $this->allConfig->getUserKeys($this->user, $this->appName);
|
||||
$value = [];
|
||||
foreach ($keys as $key) {
|
||||
$value[$key] = $this->allConfig->getUserValue($this->user, $this->appName, $key);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
@@ -1,266 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace OCA\NCDownloader\Tools;
|
||||
|
||||
use OCA\NCDownloader\Tools\Helper;
|
||||
use OCA\NCDownloader\Tools\YoutubeHelper;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class Youtube
|
||||
{
|
||||
public $audioOnly = 0;
|
||||
public $audioFormat = 'm4a', $videoFormat = null;
|
||||
//path in nextcloud fs
|
||||
public $dbDlPath = null;
|
||||
private $format = 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best';
|
||||
private $options = [];
|
||||
private $downloadDir;
|
||||
private $timeout = 60 * 60 * 10; //10 hours
|
||||
private $outTpl = "%(id)s-%(title).64s.%(ext)s";
|
||||
private $defaultDir = "/tmp/downloads";
|
||||
private $env = [];
|
||||
private $bin;
|
||||
|
||||
public function __construct(array $options)
|
||||
{
|
||||
$options += ['downloadDir' => '/tmp/downloads', 'settings' => []];
|
||||
$this->init($options);
|
||||
}
|
||||
public function init(array $options)
|
||||
{
|
||||
extract($options);
|
||||
if (isset($binary) && $this->isExecutable($binary)) {
|
||||
$this->bin = $binary;
|
||||
} else {
|
||||
$this->bin = __DIR__ . "/../../bin/yt-dlp"; //Helper::findBinaryPath('youtube-dl', __DIR__ . "/../../bin/yt-dlp");
|
||||
}
|
||||
if ($this->isInstalled() && !$this->isExecutable()) {
|
||||
chmod($this->bin, 0744);
|
||||
}
|
||||
$this->setDownloadDir($downloadDir);
|
||||
if (!empty($settings)) {
|
||||
foreach ($settings as $key => $value) {
|
||||
if (empty($value)) {
|
||||
$this->addOption($key, true);
|
||||
} else {
|
||||
$this->setOption($key, $value, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (empty($lang = getenv('LANG')) || strpos(strtolower($lang), 'c.utf-8') === false) {
|
||||
$lang = 'C.UTF-8';
|
||||
}
|
||||
$this->setEnv('LANG', $lang);
|
||||
$this->addOption("--no-mtime");
|
||||
$this->addOption('--ignore-errors');
|
||||
|
||||
if (($index = $this->hasOption('--output')) !== false) {
|
||||
$this->outTpl = $this->options[$index + 1];
|
||||
unset($this->options[$index]);
|
||||
unset($this->options[$index + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
public function setEnv($key, $val)
|
||||
{
|
||||
$this->env[$key] = $val;
|
||||
}
|
||||
|
||||
public function audioMode()
|
||||
{
|
||||
if (Helper::ffmpegInstalled()) {
|
||||
$this->addOption('--prefer-ffmpeg');
|
||||
// $this->addOption('--add-metadata');
|
||||
// $this->setOption('--metadata-from-title', "%(artist)s-%(title).64s");
|
||||
$this->addOption('--extract-audio');
|
||||
} else {
|
||||
$this->audioFormat = "m4a";
|
||||
}
|
||||
/*$pos = strrpos($this->outTpl, '.');
|
||||
$this->outTpl = substr($this->outTpl, 0, $pos) . "." . $this->audioFormat;
|
||||
$this->outTpl = "/%(id)s-%(title)s.m4a";*/
|
||||
$this->setAudioFormat($this->audioFormat);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setAudioQuality($value = 0)
|
||||
{
|
||||
$this->setOption('--audio-quality', $value);
|
||||
}
|
||||
|
||||
public function setAudioFormat($format)
|
||||
{
|
||||
$this->setOption('--audio-format', $format);
|
||||
}
|
||||
|
||||
public function setVideoFormat($format)
|
||||
{
|
||||
//$this->videoFormat = $format;
|
||||
$this->setOption('--recode-video', $format);
|
||||
}
|
||||
|
||||
public function GetUrlOnly()
|
||||
{
|
||||
$this->addOption('--get-filename');
|
||||
$this->addOption('--get-url');
|
||||
return $this;
|
||||
}
|
||||
|
||||
public static function create($options)
|
||||
{
|
||||
return new self($options);
|
||||
}
|
||||
|
||||
public function setDownloadDir($dir)
|
||||
{
|
||||
$this->downloadDir = rtrim($dir, '/');
|
||||
}
|
||||
|
||||
public function getDownloadDir()
|
||||
{
|
||||
return $this->downloadDir;
|
||||
}
|
||||
|
||||
public function prependOption(string $option)
|
||||
{
|
||||
array_unshift($this->options, $option);
|
||||
}
|
||||
|
||||
public function download($url)
|
||||
{
|
||||
if ($this->audioOnly) {
|
||||
$this->audioMode();
|
||||
} else {
|
||||
if (Helper::ffmpegInstalled() && $this->videoFormat) {
|
||||
$this->setOption('--format', 'bestvideo+bestaudio/best');
|
||||
$this->setVideoFormat($this->videoFormat);
|
||||
} else {
|
||||
$this->setOption('--format', $this->format);
|
||||
}
|
||||
}
|
||||
$this->helper = YoutubeHelper::create();
|
||||
$this->downloadDir = $this->downloadDir ?? $this->defaultDir;
|
||||
$this->setOption("--output", $this->downloadDir . "/" . $this->outTpl);
|
||||
$this->setUrl($url);
|
||||
$this->prependOption($this->bin);
|
||||
$process = new Process($this->options, null, $this->env);
|
||||
$process->setTimeout($this->timeout);
|
||||
$data = ['link' => $url, 'path' => $this->dbDlPath];
|
||||
if ($this->audioOnly) {
|
||||
$data['ext'] = $this->audioFormat;
|
||||
} else {
|
||||
$data['ext'] = $this->videoFormat;
|
||||
}
|
||||
$process->run(function ($type, $buffer) use ($data, $process) {
|
||||
if (Process::ERR === $type) {
|
||||
$this->onError($buffer);
|
||||
} else {
|
||||
$data['pid'] = $process->getPid();
|
||||
$this->onOutput($buffer, $data);
|
||||
}
|
||||
});
|
||||
if ($process->isSuccessful()) {
|
||||
$this->helper->updateStatus(Helper::STATUS['COMPLETE']);
|
||||
return ['message' => $this->helper->file ?? $process->getErrorOutput()];
|
||||
}
|
||||
return ['error' => $process->getErrorOutput()];
|
||||
}
|
||||
|
||||
private function onError($buffer)
|
||||
{
|
||||
$this->helper->log($buffer);
|
||||
}
|
||||
|
||||
public function onOutput($buffer, $extra)
|
||||
{
|
||||
$this->helper->run($buffer, $extra);
|
||||
}
|
||||
public function getDownloadUrl($url)
|
||||
{
|
||||
$this->setUrl($url);
|
||||
$this->GetUrlOnly();
|
||||
$this->buildCMD();
|
||||
exec($this->cmd, $output, $returnCode);
|
||||
if (count($output) === 1) {
|
||||
return ['url' => reset($output)];
|
||||
}
|
||||
list($url, $filename) = $output;
|
||||
$filename = Helper::cleanString($filename);
|
||||
return ['url' => $url, 'filename' => Helper::clipFilename($filename)];
|
||||
}
|
||||
|
||||
public function setUrl($url)
|
||||
{
|
||||
$this->prependOption($url);
|
||||
//$index = array_search('-i', $this->options);
|
||||
//array_splice($this->options, $index + 1, 0, $url);
|
||||
}
|
||||
public function setOption($key, $value, $hyphens = false)
|
||||
{
|
||||
$this->addOption($key, $hyphens);
|
||||
$this->addOption($value, false);
|
||||
return $this;
|
||||
}
|
||||
public function addOption(String $option, $hyphens = false)
|
||||
{
|
||||
if ($hyphens && substr($option, 0, 2) !== '--') {
|
||||
$option = "--" . $option;
|
||||
}
|
||||
array_push($this->options, $option);
|
||||
}
|
||||
|
||||
protected function hasOption($name)
|
||||
{
|
||||
return array_search($name, $this->options);
|
||||
}
|
||||
|
||||
public function forceIPV4()
|
||||
{
|
||||
$this->addOption('force-ipv4', true);
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function buildCMD()
|
||||
{
|
||||
$this->cmd = $this->bin; //. " 2>&1";
|
||||
|
||||
foreach ($this->options as $option) {
|
||||
$this->cmd .= " " . $option;
|
||||
}
|
||||
}
|
||||
public function isInstalled()
|
||||
{
|
||||
return @is_file($this->bin);
|
||||
}
|
||||
public function isExecutable()
|
||||
{
|
||||
return @is_executable($this->bin);
|
||||
}
|
||||
|
||||
public function isReadable()
|
||||
{
|
||||
return @is_readable($this->bin);
|
||||
}
|
||||
|
||||
public function getBin()
|
||||
{
|
||||
return $this->bin;
|
||||
}
|
||||
public function install()
|
||||
{
|
||||
$url = $this->installUrl();
|
||||
$file = __DIR__ . "/../../bin/yt-dlp2";
|
||||
try {
|
||||
Helper::Download($url, $file);
|
||||
chmod($file, 0744);
|
||||
} catch (\Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function installUrl()
|
||||
{
|
||||
return "https://github.com/shiningw/ncdownloader-bin/raw/master/yt-dlp";
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
<?php
|
||||
namespace OCA\NCDownloader\Tools;
|
||||
|
||||
use OCA\NCDownloader\Tools\DbHelper;
|
||||
use OCA\NCDownloader\Tools\Helper;
|
||||
|
||||
class YoutubeHelper
|
||||
{
|
||||
public $file = null;
|
||||
protected $pid = 0;
|
||||
public function __construct()
|
||||
{
|
||||
$this->dbconn = new DbHelper();
|
||||
$this->tablename = $this->dbconn->queryBuilder->getTableName("ncdownloader_info");
|
||||
$this->user = \OC::$server->getUserSession()->getUser()->getUID();
|
||||
}
|
||||
|
||||
public static function create()
|
||||
{
|
||||
|
||||
return new static();
|
||||
}
|
||||
public function getDownloadInfo(string $output): ?array
|
||||
{
|
||||
$rules = '#\[(?<module>(download|ExtractAudio|VideoConvertor|Merger|ffmpeg))\]((\s+|\s+Converting.*;\s+)Destination:\s+|\s+Merging formats into\s+\")' .
|
||||
'(?<filename>.*\.(?<ext>(mp4|mp3|aac|webm|m4a|ogg|3gp|mkv|wav|flv)))#i';
|
||||
|
||||
if (preg_match($rules, $output, $matches)) {
|
||||
return $matches;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getSiteInfo(string $buffer): ?array
|
||||
{
|
||||
$regex = '/\[(?<site>.+)]\s(?<id>.+):\sDownloading\s.*/i';
|
||||
if (preg_match($regex, $buffer, $matches)) {
|
||||
return ["id" => $matches["id"], "site" => $matches["site"]];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getProgress(string $buffer): ?array
|
||||
{
|
||||
$progressRegex = '#\[download\]\s+' .
|
||||
'(?<percentage>\d+(?:\.\d+)?%)' . //progress
|
||||
'\s+of\s+[~]?' .
|
||||
'(?<filesize>\d+(?:\.\d+)?(?:K|M|G)iB)' . //file size
|
||||
'(?:\s+at\s+' .
|
||||
'(?<speed>(\d+(?:\.\d+)?(?:K|M|G)iB/s)|Unknown speed))' . //speed
|
||||
'(?:\s+ETA\s+(?<eta>([\d:]{2,8}|Unknown ETA)))?' . //estimated download time
|
||||
'(\s+in\s+(?<totalTime>[\d:]{2,8}))?#i';
|
||||
|
||||
if (preg_match_all($progressRegex, $buffer, $matches, PREG_SET_ORDER) !== false) {
|
||||
if (count($matches) > 0) {
|
||||
return reset($matches);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function updateProgress(array $data)
|
||||
{
|
||||
extract($data);
|
||||
$sql = sprintf("UPDATE %s set filesize = ?,speed = ?,progress = ? WHERE gid = ?", $this->tablename);
|
||||
$this->dbconn->executeUpdate($sql, [$filesize, $speed, $percentage, $this->gid]);
|
||||
}
|
||||
public function log($message)
|
||||
{
|
||||
Helper::debug($message);
|
||||
}
|
||||
public function updateStatus($status = null)
|
||||
{
|
||||
if (isset($status)) {
|
||||
$this->status = trim($status);
|
||||
}
|
||||
$this->dbconn->updateStatus($this->gid, $this->status);
|
||||
}
|
||||
public function setPid($pid)
|
||||
{
|
||||
$this->pid = $pid;
|
||||
}
|
||||
public function run(string $buffer, array $extra)
|
||||
{
|
||||
$info = $this->getSiteInfo($buffer);
|
||||
if (isset($info["id"])) {
|
||||
$this->gid = Helper::generateGID($info["id"]);
|
||||
}
|
||||
if (!$this->gid) {
|
||||
$this->gid = Helper::generateGID($extra["link"]);
|
||||
}
|
||||
$downloadInfo = $this->getDownloadInfo($buffer);
|
||||
if ($downloadInfo) {
|
||||
$file = $downloadInfo["filename"];
|
||||
$module = $downloadInfo["module"];
|
||||
$this->file = basename($file);
|
||||
if (strtolower($module) == "download") {
|
||||
$this->save($file, $extra);
|
||||
} else {
|
||||
$this->updateFilename($file);
|
||||
}
|
||||
}
|
||||
if ($progress = $this->getProgress($buffer)) {
|
||||
$this->updateProgress($progress);
|
||||
}
|
||||
}
|
||||
protected function save(string $file, array $extra)
|
||||
{
|
||||
$data = [];
|
||||
$extra = serialize($extra);
|
||||
if ($this->dbconn->getDBType() == "pgsql") {
|
||||
if (function_exists("pg_escape_bytea")) {
|
||||
$extra = pg_escape_bytea($extra);
|
||||
}
|
||||
}
|
||||
$data = [
|
||||
'uid' => $this->user,
|
||||
'gid' => $this->gid,
|
||||
'type' => Helper::DOWNLOADTYPE['YOUTUBE-DL'],
|
||||
'filename' => basename($file),
|
||||
'status' => Helper::STATUS['ACTIVE'],
|
||||
'timestamp' => time(),
|
||||
'data' => $extra,
|
||||
];
|
||||
$this->dbconn->insert($data);
|
||||
}
|
||||
private function updateFilename(string $file)
|
||||
{
|
||||
$sql = sprintf("UPDATE %s set filename = ? WHERE gid = ?", $this->tablename);
|
||||
$this->dbconn->executeUpdate($sql, [basename($file), $this->gid]);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
<?php
|
||||
namespace OCA\NCDownloader\Tools;
|
||||
|
||||
class aria2Options
|
||||
{
|
||||
|
||||
public static function get()
|
||||
{
|
||||
return ["ca-certificate", "certificate", "dht-file-path", "dht-file-path6", "dir", "input-file", "load-cookies", "log", "metalink-file", "netrc-path", "on-bt-download-complete", "on-download-complete", "on-download-error", "on-download-start", "on-download-stop", "on-download-pause", "out", "private-key", "rpc-certificate", "rpc-private-key", "save-cookies", "save-session", "server-stat-if", "server-stat-of", "torrent-file", "all-proxy", "all-proxy-passwd", "all-proxy-user", "allow-overwrite", "allow-piece-length-change", "always-resume", "async-dns", "auto-file-renaming", "bt-enable-hook-after-hash-check", "bt-enable-lpd", "bt-exclude-tracker", "bt-external-ip", "bt-force-encryption", "bt-hash-check-seed", "bt-load-saved-metadata", "bt-max-peers", "bt-metadata-only", "bt-min-crypto-level", "bt-prioritize-piece", "bt-remove-unselected-file", "bt-request-peer-speed-limit", "bt-require-crypto", "bt-save-metadata", "bt-seed-unverified", "bt-stop-timeout", "bt-tracker", "bt-tracker-connect-timeout", "bt-tracker-interval", "bt-tracker-timeout", "check-integrity", "checksum", "conditional-get", "connect-timeout", "content-disposition-default-utf8", "continue", "dir", "dry-run", "enable-http-keep-alive", "enable-http-pipelining", "enable-mmap", "enable-peer-exchange", "file-allocation", "follow-metalink", "follow-torrent", "force-save", "ftp-passwd", "ftp-pasv", "ftp-proxy", "ftp-proxy-passwd", "ftp-proxy-user", "ftp-reuse-connection", "ftp-type", "ftp-user", "gid", "hash-check-only", "header", "http-accept-gzip", "http-auth-challenge", "http-no-cache", "http-passwd", "http-proxy", "http-proxy-passwd", "http-proxy-user", "http-user", "https-proxy", "https-proxy-passwd", "https-proxy-user", "index-out", "lowest-speed-limit", "max-connection-per-server", "max-download-limit", "max-file-not-found", "max-mmap-limit", "max-resume-failure-tries", "max-tries", "max-upload-limit", "metalink-base-uri", "metalink-enable-unique-protocol", "metalink-language", "metalink-location", "metalink-os", "metalink-preferred-protocol", "metalink-version", "min-split-size", "no-file-allocation-limit", "no-netrc", "no-proxy", "out", "parameterized-uri", "pause", "pause-metadata", "piece-length", "proxy-method", "realtime-chunk-checksum", "referer", "remote-time", "remove-control-file", "retry-wait", "reuse-uri", "rpc-save-upload-metadata", "seed-ratio", "seed-time", "select-file", "split", "ssh-host-key-md", "stream-piece-selector", "timeout", "uri-selector", "use-head", "user-agent", "dry-run", "metalink-base-uri", "parameterized-uri", "pause", "piece-length", "rpc-save-upload-metadata", "bt-max-peers", "bt-request-peer-speed-limit", "bt-remove-unselected-file", "force-save", "max-download-limit", "max-upload-limit", "bt-max-open-files", "download-result", "keep-unfinished-download-result", "log", "log-level", "max-concurrent-downloads", "max-download-result", "max-overall-download-limit", "max-overall-upload-limit", "optimize-concurrent-downloads", "save-cookies", "save-session", "server-stat-of"];
|
||||
}
|
||||
}
|
||||
@@ -1,274 +0,0 @@
|
||||
<?php
|
||||
namespace OCA\NCDownloader\Tools;
|
||||
|
||||
class youtubedlOptions
|
||||
{
|
||||
|
||||
public static function get()
|
||||
{
|
||||
return array_keys(self::options());
|
||||
}
|
||||
public static function options()
|
||||
{
|
||||
return array(
|
||||
'help' => 'this help text and exit',
|
||||
'version' => 'program version and exit',
|
||||
'update' => 'this program to latest version. Make sure that you have sufficient permissions (run with sudo if needed)',
|
||||
'ignore-errors' => 'download and postprocessing errors. The download will be considered successful even if the postprocessing',
|
||||
'abort-on-error' => 'downloading of further videos if an error occurs (Alias: --no-ignore-errors)',
|
||||
'dump-user-agent' => 'the current user-agent and exit',
|
||||
'list-extractors' => 'all supported extractors and exit',
|
||||
'extractor-descriptions' => 'descriptions of all supported extractors and exit',
|
||||
'force-generic-extractor' => 'extraction to use the generic extractor',
|
||||
'default-search' => 'Use this prefix for unqualified URLs. For example "gvsearch2:" downloads two videos from google videos for the',
|
||||
'ignore-config' => 'Don\'t load any more configuration files except those given by --config-locations. For backward compatibility, if',
|
||||
'config-location' => 'Location of the configuration file; either the path to the config or its containing directory.',
|
||||
'flat-playlist' => 'not extract the videos of a playlist, only list them',
|
||||
'mark-watched' => 'videos watched (even with --simulate). Currently only supported for YouTube',
|
||||
'no-mark-watched' => 'not mark videos watched (default)',
|
||||
'no-color' => 'not emit color codes in output',
|
||||
'proxy' => 'Use the specified HTTP/HTTPS/SOCKS proxy. To enable SOCKS proxy, specify a proper scheme. For example',
|
||||
'socket-timeout' => 'Time to wait before giving up, in seconds',
|
||||
'source-address' => 'Client-side IP address to bind to',
|
||||
'force-ipv4' => 'all connections via IPv4',
|
||||
'force-ipv6' => 'all connections via IPv6',
|
||||
'geo-verification-proxy' => 'Use this proxy to verify the IP address for some geo-restricted sites. The default proxy specified by --proxy (or',
|
||||
'geo-bypass' => 'geographic restriction via faking X-Forwarded-For HTTP header (default)',
|
||||
'no-geo-bypass' => 'not bypass geographic restriction via faking X-Forwarded-For HTTP header',
|
||||
'geo-bypass-country' => 'Force bypass geographic restriction with explicitly provided two-letter ISO 3166-2 country code',
|
||||
'playlist-start' => 'Playlist video to start at (default is 1)',
|
||||
'playlist-end' => 'Playlist video to end at (default is last)',
|
||||
'match-title' => 'Download only matching titles (regex or caseless sub-string)',
|
||||
'reject-title' => 'Skip download for matching titles (regex or caseless sub-string)',
|
||||
'max-downloads' => 'Abort after downloading NUMBER files',
|
||||
'min-filesize' => 'Do not download any videos smaller than SIZE (e.g. 50k or 44.6m)',
|
||||
'max-filesize' => 'Do not download any videos larger than SIZE (e.g. 50k or 44.6m)',
|
||||
'date' => 'Download only videos uploaded on this date. The date can be "YYYYMMDD" or in the format',
|
||||
'datebefore' => 'Download only videos uploaded on or before this date. The date formats accepted is the same as --date',
|
||||
'dateafter' => 'Download only videos uploaded on or after this date. The date formats accepted is the same as --date',
|
||||
'min-views' => 'Do not download any videos with less than COUNT views',
|
||||
'max-views' => 'Do not download any videos with more than COUNT views',
|
||||
'match-filter' => 'Generic video filter. Any field (see "OUTPUT TEMPLATE") can be compared with a number or a string using the',
|
||||
'no-playlist' => 'only the video, if the URL refers to a video and a playlist',
|
||||
'yes-playlist' => 'the playlist, if the URL refers to a video and a playlist',
|
||||
'age-limit' => 'Download only videos suitable for the given age',
|
||||
'download-archive' => 'Download only videos not listed in the archive file. Record the IDs of all downloaded videos in it',
|
||||
'include-ads' => 'advertisements as well (experimental)',
|
||||
'limit-rate' => 'Maximum download rate in bytes per second (e.g. 50K or 4.2M)',
|
||||
'retries' => 'Number of retries (default is 10), or "infinite"',
|
||||
'fragment-retries' => 'Number of retries for a fragment (default is 10), or "infinite" (DASH, hlsnative and ISM)',
|
||||
'skip-unavailable-fragments' => 'unavailable fragments for DASH, hlsnative and ISM (default) (Alias: --no-abort-on-unavailable-fragment)',
|
||||
'abort-on-unavailable-fragment' => 'downloading if a fragment is unavailable (Alias: --no-skip-unavailable-fragments)',
|
||||
'keep-fragments' => 'downloaded fragments on disk after downloading is finished',
|
||||
'buffer-size' => 'Size of download buffer (e.g. 1024 or 16K) (default is 1024)',
|
||||
'no-resize-buffer' => 'not automatically adjust the buffer size',
|
||||
'http-chunk-size' => 'Size of a chunk for chunk-based HTTP downloading (e.g. 10485760 or 10M) (default is disabled). May be useful for',
|
||||
'playlist-reverse' => 'playlist videos in reverse order',
|
||||
'playlist-random' => 'playlist videos in random order',
|
||||
'xattr-set-filesize' => 'file xattribute ytdl.filesize with expected file size',
|
||||
'hls-prefer-native' => 'the native HLS downloader instead of ffmpeg',
|
||||
'hls-prefer-ffmpeg' => 'ffmpeg instead of the native HLS downloader',
|
||||
'hls-use-mpegts' => 'the mpegts container for HLS videos; allowing some players to play the video while downloading, and reducing the',
|
||||
'external-downloader' => 'Use the specified external downloader. Currently supports aria2c,avconv,axel,curl,ffmpeg,httpie,wget',
|
||||
'external-downloader-args' => 'Give these arguments to the external downloader',
|
||||
'batch-file' => 'File containing URLs to download ("-" for stdin), one URL per line. Lines starting with "#", ";" or "]" are',
|
||||
'id' => 'only video ID in file name',
|
||||
'output' => 'an absolute path',
|
||||
'output-na-placeholder' => 'Placeholder value for unavailable meta fields in output filename template (default: "NA")',
|
||||
'autonumber-start' => 'Specify the start value for %(autonumber)s (default is 1)',
|
||||
'restrict-filenames' => 'filenames to only ASCII characters, and avoid "&" and spaces in filenames',
|
||||
'no-overwrites' => 'not overwrite any files',
|
||||
'continue' => 'partially downloaded files/fragments (default)',
|
||||
'no-continue' => 'not resume partially downloaded fragments. If the file is not fragmented, restart download of the entire file',
|
||||
'no-part' => 'not use .part files - write directly into output file',
|
||||
'no-mtime' => 'not use the Last-modified header to set the file modification time',
|
||||
'write-description' => 'video description to a .description file',
|
||||
'write-info-json' => 'video metadata to a .info.json file (this may contain personal information)',
|
||||
'write-annotations' => 'video annotations to a .annotations.xml file',
|
||||
'load-info-json' => 'JSON file containing the video information (created with the "--write-info-json" option)',
|
||||
'cookies' => 'Netscape formatted file to read cookies from and dump cookie jar in',
|
||||
'cache-dir' => 'Location in the filesystem where youtube-dl can store some downloaded information (such as client ids and',
|
||||
'no-cache-dir' => 'filesystem caching',
|
||||
'rm-cache-dir' => 'all filesystem cache files',
|
||||
'write-thumbnail' => 'thumbnail image to disk',
|
||||
'write-all-thumbnails' => 'all thumbnail image formats to disk',
|
||||
'list-thumbnails' => 'available thumbnails of each video. Simulate unless --no-simulate is used',
|
||||
'quiet' => '',
|
||||
'no-warnings' => 'warnings',
|
||||
'simulate' => 'not download the video and do not write anything to disk',
|
||||
'skip-download' => 'not download the video but write all related files (Alias: --no-download)',
|
||||
'get-url' => 'Simulate, quiet but print URL',
|
||||
'get-title' => 'Simulate, quiet but print title',
|
||||
'get-id' => 'Simulate, quiet but print id',
|
||||
'get-thumbnail' => 'Simulate, quiet but print thumbnail URL',
|
||||
'get-description' => 'Simulate, quiet but print video description',
|
||||
'get-duration' => 'Simulate, quiet but print video length',
|
||||
'get-filename' => 'Simulate, quiet but print output filename',
|
||||
'get-format' => 'Simulate, quiet but print output format',
|
||||
'dump-json' => 'Quiet, but print JSON information for each video. Simulate unless --no-simulate is used. See "OUTPUT TEMPLATE" for a',
|
||||
'dump-single-json' => 'Quiet, but print JSON information for each url or infojson passed. Simulate unless --no-simulate is used. If the URL',
|
||||
'print-json' => 'quiet and print the video information as JSON (video is still being downloaded).',
|
||||
'newline' => 'progress bar as new lines',
|
||||
'no-progress' => 'not print progress bar',
|
||||
'console-title' => 'progress in console titlebar',
|
||||
'verbose' => 'various debugging information',
|
||||
'dump-pages' => 'downloaded pages encoded using base64 to debug problems (very verbose)',
|
||||
'write-pages' => 'downloaded intermediary pages to files in the current directory to debug problems',
|
||||
'print-traffic' => 'sent and read HTTP traffic',
|
||||
'call-home' => 'the youtube-dl server for debugging',
|
||||
'no-call-home' => 'NOT contact the youtube-dl server for debugging',
|
||||
'encoding' => 'Force the specified encoding (experimental)',
|
||||
'no-check-certificate' => 'HTTPS certificate validation',
|
||||
'prefer-insecure' => 'an unencrypted connection to retrieve information about the video (Currently supported only for YouTube)',
|
||||
'user-agent' => 'Specify a custom user agent',
|
||||
'referer' => 'Specify a custom referer, use if the video access is restricted to one domain',
|
||||
'add-header' => 'Specify a custom HTTP header and its value, separated by a colon \':\'. You can use this option multiple times',
|
||||
'bidi-workaround' => 'around terminals that lack bidirectional text support. Requires bidiv or fribidi executable in PATH',
|
||||
'sleep-interval' => 'Number of seconds to sleep before each download. This is the minimum time to sleep when used along with --max-sleep-',
|
||||
'max-sleep-interval' => 'Maximum number of seconds to sleep. Can only be used along with --min-sleep-interval',
|
||||
'format' => 'Video format code, see "FORMAT SELECTION" for more details',
|
||||
'all-formats' => 'all available video formats',
|
||||
'prefer-free-formats' => 'video formats with free containers over non-free ones of same quality. Use with "-S ext" to strictly prefer',
|
||||
'list-formats' => 'available formats of each video. Simulate unless --no-simulate is used',
|
||||
'youtube-skip-dash-manifest' => 'not download the DASH manifests and related data on YouTube videos',
|
||||
'merge-output-format' => 'If a merge is required (e.g. bestvideo+bestaudio), output to given container format. One of mkv, mp4, ogg, webm,',
|
||||
'write-sub' => 'subtitle file',
|
||||
'write-auto-sub' => 'automatically generated subtitle file (YouTube only)',
|
||||
'all-subs' => 'all the available subtitles of the video',
|
||||
'list-subs' => 'a list of available language tags',
|
||||
'sub-format' => 'Subtitle format, accepts formats preference, for example: "srt" or "ass/srt/best"',
|
||||
'sub-lang' => 'Languages of the subtitles to download (optional) separated by commas, use --list-subs for available language tags',
|
||||
'username' => 'Login with this account ID',
|
||||
'password' => 'Account password. If this option is left out, yt-dlp will ask interactively',
|
||||
'twofactor' => 'Two-factor authentication code',
|
||||
'netrc' => '.netrc authentication data',
|
||||
'video-password' => 'Video password (vimeo, youku)',
|
||||
'ap-mso' => 'Adobe Pass multiple-system operator (TV provider) identifier, use --ap-list-mso for a list of available MSOs',
|
||||
'ap-username' => 'Multiple-system operator account login',
|
||||
'ap-password' => 'Multiple-system operator account password. If this option is left out, yt-dlp will ask interactively',
|
||||
'ap-list-mso' => 'all supported multiple-system operators',
|
||||
'extract-audio' => 'video files to audio-only files (requires ffmpeg and ffprobe)',
|
||||
'audio-format' => 'Specify audio format to convert the audio to when -x is used. Currently supported formats are: best (default) or one',
|
||||
'audio-quality' => 'Specify ffmpeg audio quality, insert a value between 0 (best) and 10 (worst) for VBR or a specific bitrate like 128K',
|
||||
'recode-video' => 'Re-encode the video into another format if re-encoding is necessary. The syntax and supported formats are the same',
|
||||
'postprocessor-args' => 'Give these arguments to the postprocessor',
|
||||
'keep-video' => 'the intermediate video file on disk after post-processing',
|
||||
'no-post-overwrites' => 'not overwrite post-processed files',
|
||||
'embed-subs' => 'subtitles in the video (only for mp4, webm and mkv videos)',
|
||||
'embed-thumbnail' => 'thumbnail in the video as cover art',
|
||||
'add-metadata' => 'metadata to the video file',
|
||||
'metadata-from-title' => 'Parse additional metadata like song title / artist from the video title. The format syntax is the same as --output.',
|
||||
'xattrs' => 'metadata to the video file\'s xattrs (using dublin core and xdg standards)',
|
||||
'fixup' => 'Automatically correct known faults of the file. One of never (do nothing), warn (only emit a warning),',
|
||||
'prefer-avconv' => 'avconv over ffmpeg for running the postprocessors',
|
||||
'prefer-ffmpeg' => 'ffmpeg over avconv for running the postprocessors (default)',
|
||||
'ffmpeg-location' => 'Location of the ffmpeg binary; either the path to the binary or its containing directory',
|
||||
'exec' => 'Execute a command on the file after downloading and post-processing, similar to find\'s -exec syntax. Example:',
|
||||
'convert-subs' => 'Convert the subtitles to another format (currently supported: srt|vtt|ass|lrc) (Alias: --convert-subtitles)',
|
||||
'no-abort-on-error' => 'with next video on download errors; e.g. to skip unavailable videos in a playlist (default)',
|
||||
'no-config-locations' => 'not load any custom configuration files (default). When given inside a configuration file, ignore all previous',
|
||||
'config-locations' => 'Location of the main configuration file; either the path to the config or its containing directory. Can be used',
|
||||
'no-flat-playlist' => 'the videos of a playlist',
|
||||
'live-from-start' => 'livestreams from the start. Currently only supported for YouTube (Experimental)',
|
||||
'no-live-from-start' => 'livestreams from the current time (default)',
|
||||
'no-wait-for-video' => 'not wait for scheduled streams (default)',
|
||||
'no-colors' => 'not emit color codes in output',
|
||||
'compat-options' => 'Options that can help keep compatibility with youtube-dl or youtube-dlc configurations by reverting some of the',
|
||||
'no-match-filter' => 'not use generic video filter (default)',
|
||||
'no-download-archive' => 'not use archive file (default)',
|
||||
'break-on-existing' => 'the download process when encountering a file that is in the archive',
|
||||
'break-on-reject' => 'the download process when encountering a file that has been filtered out',
|
||||
'break-per-input' => '--break-on-existing and --break-on-reject act only on the current input URL',
|
||||
'no-break-per-input' => '--break-on-existing and --break-on-reject terminates the entire download queue',
|
||||
'skip-playlist-after-errors' => 'Number of allowed failures until the rest of the playlist is skipped',
|
||||
'concurrent-fragments' => 'Number of fragments of a dash/hlsnative video that should be downloaded concurrently (default is 1)',
|
||||
'throttled-rate' => 'Minimum download rate in bytes per second below which throttling is assumed and the video data is re-extracted (e.g.',
|
||||
'file-access-retries' => 'Number of times to retry on file access error (default is 3), or "infinite"',
|
||||
'no-keep-fragments' => 'downloaded fragments after downloading is finished (default)',
|
||||
'resize-buffer' => 'buffer size is automatically resized from an initial value of --buffer-size (default)',
|
||||
'no-playlist-reverse' => 'playlist videos in default order (default)',
|
||||
'no-hls-use-mpegts' => 'not use the mpegts container for HLS videos. This is default when not downloading live streams',
|
||||
'no-batch-file' => 'not read URLs from batch file (default)',
|
||||
'no-restrict-filenames' => 'Unicode characters, "&" and spaces in filenames (default)',
|
||||
'windows-filenames' => 'filenames to be Windows-compatible',
|
||||
'no-windows-filenames' => 'filenames Windows-compatible only if using Windows (default)',
|
||||
'trim-filenames' => 'Limit the filename length (excluding extension) to the specified number of characters',
|
||||
'force-overwrites' => 'all video and metadata files. This option includes --no-continue',
|
||||
'no-force-overwrites' => 'not overwrite the video, but overwrite related files (default)',
|
||||
'part' => '.part files instead of writing directly into output file (default)',
|
||||
'mtime' => 'the Last-modified header to set the file modification time (default)',
|
||||
'no-write-description' => 'not write video description (default)',
|
||||
'no-write-info-json' => 'not write video metadata (default)',
|
||||
'write-playlist-metafiles' => 'playlist metadata in addition to the video metadata when using --write-info-json, --write-description etc.',
|
||||
'no-write-playlist-metafiles' => 'not write playlist metadata when using --write-info-json, --write-description etc.',
|
||||
'clean-info-json' => 'some private fields such as filenames from the infojson. Note that it could still contain some personal',
|
||||
'no-clean-info-json' => 'all fields to the infojson',
|
||||
'write-comments' => 'video comments to be placed in the infojson. The comments are fetched even without this option if the',
|
||||
'no-write-comments' => 'not retrieve video comments unless the extraction is known to be quick (Alias: --no-get-comments)',
|
||||
'no-cookies' => 'not read/dump cookies from/to file (default)',
|
||||
'no-cookies-from-browser' => 'not load cookies from browser (default)',
|
||||
'no-write-thumbnail' => 'not write thumbnail image to disk (default)',
|
||||
'write-link' => 'an internet shortcut file, depending on the current platform (.url, .webloc or .desktop). The URL may be',
|
||||
'write-url-link' => 'a .url Windows internet shortcut. The OS caches the URL based on the file path',
|
||||
'write-webloc-link' => 'a .webloc macOS internet shortcut',
|
||||
'write-desktop-link' => 'a .desktop Linux internet shortcut',
|
||||
'no-simulate' => 'used). This option can be used multiple times',
|
||||
'ignore-no-formats-error' => '"No video formats" error. Useful for extracting metadata even if the videos are not actually available for',
|
||||
'no-ignore-no-formats-error' => 'error when no downloadable video formats are found (default)',
|
||||
'force-write-archive' => 'download archive entries to be written as far as no errors occur, even if -s or another simulation option is',
|
||||
'progress' => 'progress bar, even if in quiet mode',
|
||||
'legacy-server-connect' => 'allow HTTPS connection to servers that do not support RFC 5746 secure renegotiation',
|
||||
'no-check-certificates' => 'HTTPS certificate validation',
|
||||
'sleep-requests' => 'Number of seconds to sleep between requests during data extraction',
|
||||
'sleep-subtitles' => 'Number of seconds to sleep before each subtitle download',
|
||||
'format-sort' => 'Sort the formats by the fields given, see "Sorting Formats" for more details',
|
||||
'format-sort-force' => 'user specified sort order to have precedence over all fields, see "Sorting Formats" for more details',
|
||||
'no-format-sort-force' => 'fields have precedence over the user specified sort order (default), see "Sorting Formats" for more details',
|
||||
'video-multistreams' => 'multiple video streams to be merged into a single file',
|
||||
'no-video-multistreams' => 'one video stream is downloaded for each output file (default)',
|
||||
'audio-multistreams' => 'multiple audio streams to be merged into a single file',
|
||||
'no-audio-multistreams' => 'one audio stream is downloaded for each output file (default)',
|
||||
'no-prefer-free-formats' => 'Don\'t give any special preference to free containers (default)',
|
||||
'check-formats' => 'that the selected formats are actually downloadable',
|
||||
'check-all-formats' => 'all formats for whether they are actually downloadable',
|
||||
'no-check-formats' => 'not check that the formats are actually downloadable',
|
||||
'write-subs' => 'subtitle file',
|
||||
'no-write-subs' => 'not write subtitle file (default)',
|
||||
'write-auto-subs' => 'automatically generated subtitle file (Alias: --write-automatic-subs)',
|
||||
'no-write-auto-subs' => 'not write auto-generated subtitles (default) (Alias: --no-write-automatic-subs)',
|
||||
'sub-langs' => 'Languages of the subtitles to download (can be regex) or "all" separated by commas. (Eg: --sub-langs "en.*,ja") You',
|
||||
'netrc-location' => 'Location of .netrc authentication data; either the path or its containing directory. Defaults to ~/.netrc',
|
||||
'remux-video' => 'Remux the video into another container if necessary (currently supported:',
|
||||
'no-keep-video' => 'the intermediate video file after post-processing (default)',
|
||||
'post-overwrites' => 'post-processed files (default)',
|
||||
'no-embed-subs' => 'not embed subtitles (default)',
|
||||
'no-embed-thumbnail' => 'not embed thumbnail (default)',
|
||||
'embed-metadata' => 'metadata to the video file. Also embeds chapters/infojson if present unless --no-embed-chapters/--no-embed-',
|
||||
'no-embed-metadata' => 'not add metadata to file (default) (Alias: --no-add-metadata)',
|
||||
'embed-chapters' => 'chapter markers to the video file (Alias: --add-chapters)',
|
||||
'no-embed-chapters' => 'not add chapter markers (default) (Alias: --no-add-chapters)',
|
||||
'embed-info-json' => 'the infojson as an attachment to mkv/mka video files',
|
||||
'no-embed-info-json' => 'not embed the infojson as an attachment to the video file',
|
||||
'replace-in-metadata' => 'REGEX REPLACE Replace text in a metadata field using the given regex. This option can be used multiple times',
|
||||
'concat-playlist' => 'Concatenate videos in a playlist. One of "never", "always", or "multi_video" (default; only when the videos form a',
|
||||
'no-exec' => 'any previously defined --exec',
|
||||
'convert-thumbnails' => 'Convert the thumbnails to another format (currently supported: jpg|png|webp)',
|
||||
'split-chapters' => 'video into multiple files based on internal chapters. The "chapter:" prefix can be used with "--paths" and "--',
|
||||
'no-split-chapters' => 'not split video based on chapters (default)',
|
||||
'remove-chapters' => 'Remove chapters whose title matches the given regular expression. Time ranges prefixed by a "*" can also be used in',
|
||||
'no-remove-chapters' => 'not remove any chapters from the file (default)',
|
||||
'force-keyframes-at-cuts' => 'keyframes around the chapters before removing/splitting them. Requires a re-encode and thus is very slow, but',
|
||||
'no-force-keyframes-at-cuts' => 'not force keyframes around the chapters when cutting/splitting (default)',
|
||||
'sponsorblock-mark' => 'SponsorBlock categories to create chapters for, separated by commas. Available categories are all, default(=all),',
|
||||
'sponsorblock-remove' => 'SponsorBlock categories to be removed from the video file, separated by commas. If a category is present in both',
|
||||
'sponsorblock-chapter-title' => 'The title template for SponsorBlock chapters created by --sponsorblock-mark. The same syntax as the output template',
|
||||
'no-sponsorblock' => 'both --sponsorblock-mark and --sponsorblock-remove',
|
||||
'sponsorblock-api' => 'SponsorBlock API location, defaults to https://sponsor.ajay.app',
|
||||
'extractor-retries' => 'Number of retries for known extractor errors (default is 3), or "infinite"',
|
||||
'allow-dynamic-mpd' => 'dynamic DASH manifests (default) (Alias: --no-ignore-dynamic-mpd)',
|
||||
'ignore-dynamic-mpd' => 'not process dynamic DASH manifests (Alias: --no-allow-dynamic-mpd)',
|
||||
'hls-split-discontinuity' => 'HLS playlists to different formats at discontinuities such as ad breaks',
|
||||
'no-hls-split-discontinuity' => 'not split HLS playlists to different formats at discontinuities such as ad breaks (default)',
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user