allowing for custom youtube-dl options

This commit is contained in:
huangjx
2021-10-28 15:27:22 +08:00
parent 43c50a641d
commit 9670ec883e
13 changed files with 425 additions and 126 deletions

View File

@@ -40,6 +40,7 @@ class Application extends App
$config = [ $config = [
'binary' => $this->settings->setType(Settings::TYPE['SYSTEM'])->get("ncd_yt_binary"), 'binary' => $this->settings->setType(Settings::TYPE['SYSTEM'])->get("ncd_yt_binary"),
'downloadDir' => $this->getRealDownloadDir(), 'downloadDir' => $this->getRealDownloadDir(),
'settings' => $this->settings->setType(Settings::TYPE['USER'])->getYoutube(),
]; ];
return new Youtube($config); return new Youtube($config);
}); });

View File

@@ -20,6 +20,9 @@ return [
['name' => 'Settings#aria2Get', 'url' => '/personal/aria2/get', 'verb' => 'POST'], ['name' => 'Settings#aria2Get', 'url' => '/personal/aria2/get', 'verb' => 'POST'],
['name' => 'Settings#aria2Save', 'url' => '/personal/aria2/save', 'verb' => 'POST'], ['name' => 'Settings#aria2Save', 'url' => '/personal/aria2/save', 'verb' => 'POST'],
['name' => 'Settings#aria2Delete', 'url' => '/personal/aria2/delete', 'verb' => 'POST'], ['name' => 'Settings#aria2Delete', 'url' => '/personal/aria2/delete', 'verb' => 'POST'],
['name' => 'Settings#youtubeGet', 'url' => '/personal/youtube-dl/get', 'verb' => 'POST'],
['name' => 'Settings#youtubeSave', 'url' => '/personal/youtube-dl/save', 'verb' => 'POST'],
['name' => 'Settings#youtubeDelete', 'url' => '/personal/youtube-dl/delete', 'verb' => 'POST'],
], ],
]; ];

View File

@@ -7,7 +7,6 @@ use OCA\NCDownloader\Tools\Settings;
use OCP\AppFramework\Controller; use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest; use OCP\IRequest;
use OC_Util;
class SettingsController extends Controller class SettingsController extends Controller
{ {
@@ -34,13 +33,11 @@ class SettingsController extends Controller
{ {
$params = $this->request->getParams(); $params = $this->request->getParams();
foreach ($params as $key => $value) { foreach ($params as $key => $value) {
if (substr($key, 0, 1) == '_') { $resp = $this->save($key, $value);
continue;
}
$this->save($key, $value);
} }
return new JSONResponse($resp);
} }
/** /**
* @NoAdminRequired * @NoAdminRequired
* @NoCSRFRequired * @NoCSRFRequired
*/ */
@@ -54,15 +51,13 @@ class SettingsController extends Controller
{ {
$this->settings->setType($this->settings::TYPE['SYSTEM']); $this->settings->setType($this->settings::TYPE['SYSTEM']);
$params = $this->request->getParams(); $params = $this->request->getParams();
foreach ($params as $key => $value) {
if (substr($key, 0, 1) == '_') {
continue;
}
$this->save($key, $value);
}
foreach ($params as $key => $value) {
$resp = $this->save($key, $value);
}
return new JSONResponse($resp);
} }
/** /**
* @NoAdminRequired * @NoAdminRequired
* @NoCSRFRequired * @NoCSRFRequired
*/ */
@@ -70,26 +65,72 @@ class SettingsController extends Controller
{ {
$params = $this->request->getParams(); $params = $this->request->getParams();
$data = Helper::filterData($params, Helper::aria2Options()); $data = Helper::filterData($params, Helper::aria2Options());
$this->settings->save("custom_aria2_settings", json_encode($data)); $resp = $this->settings->save("custom_aria2_settings", json_encode($data));
return new JSONResponse($resp);
} }
/** /**
* @NoAdminRequired * @NoAdminRequired
* @NoCSRFRequired * @NoCSRFRequired
*/ */
public function aria2Delete() public function aria2Delete()
{ {
$saved = json_decode($this->settings->get("custom_aria2_settings"),1); $saved = json_decode($this->settings->get("custom_aria2_settings"), 1);
$params = $this->request->getParams(); $params = $this->request->getParams();
$data = Helper::filterData($params, Helper::aria2Options()); $data = Helper::filterData($params, Helper::aria2Options());
foreach ($data as $key => $value) { foreach ($data as $key => $value) {
unset($saved[$key]); unset($saved[$key]);
} }
$this->settings->save("custom_aria2_settings", json_encode($saved)); $resp = $this->settings->save("custom_aria2_settings", json_encode($saved));
return new JSONResponse($saved); return new JSONResponse($resp);
}
/**
* @NoAdminRequired
* @NoCSRFRequired
*/
public function youtubeGet()
{
$data = json_decode($this->settings->get("custom_youtube_dl_settings"));
return new JSONResponse($data);
}
public function youtubeSave()
{
$params = $this->request->getParams();
$data = array_filter($params, function ($key) {
return (bool) (!in_array(substr($key, 0, 1), ['_']));
}, ARRAY_FILTER_USE_KEY);
$resp = $this->settings->save("custom_youtube_dl_settings", json_encode($data));
return new JSONResponse($resp);
}
/**
* @NoAdminRequired
* @NoCSRFRequired
*/
public function youtubeDelete()
{
$saved = json_decode($this->settings->get("custom_youtube_dl_settings"), 1);
$params = $this->request->getParams();
foreach ($data as $key => $value) {
unset($saved[$key]);
}
$resp = $this->settings->save("custom_youtube_dl_settings", json_encode($saved));
return new JSONResponse($resp);
} }
public function save($key, $value) public function save($key, $value)
{ {
$this->settings->save($key, $value); //key starting with _ is invalid
if (substr($key, 0, 1) == '_') {
return;
}
$key = Helper::sanitize($key);
$value = Helper::sanitize($value);
try {
$this->settings->save($key, $value);
} catch (\Exception $e) {
return ['error' => $e->getMessage()];
}
return ['message' => "Saved!"];
} }
} }

View File

@@ -49,6 +49,12 @@ class Settings extends AllConfig
$settings = $this->allConfig->getUserValue($this->user, $this->appName, "custom_aria2_settings", ''); $settings = $this->allConfig->getUserValue($this->user, $this->appName, "custom_aria2_settings", '');
return json_decode($settings, 1); return json_decode($settings, 1);
} }
public function getYoutube()
{
$settings = $this->get("custom_youtube_dl_settings");
return json_decode($settings, 1);
}
public function getAll() public function getAll()
{ {
if ($this->type === self::TYPE['APP']) { if ($this->type === self::TYPE['APP']) {
@@ -61,13 +67,18 @@ class Settings extends AllConfig
} }
public function save($key, $value) public function save($key, $value)
{ {
if ($this->type == self::TYPE['USER'] && isset($this->user)) { try {
return $this->allConfig->setUserValue($this->user, $this->appName, $key, $value); if ($this->type == self::TYPE['USER'] && isset($this->user)) {
} else if ($this->type == self::TYPE['SYSTEM']) { $this->allConfig->setUserValue($this->user, $this->appName, $key, $value);
return $this->allConfig->setSystemValue($key, $value); } else if ($this->type == self::TYPE['SYSTEM']) {
} else { $this->allConfig->setSystemValue($key, $value);
return $this->allConfig->setAppValue($this->appName, $key, $value); } else {
$this->allConfig->setAppValue($this->appName, $key, $value);
}
} catch (\Exception $e) {
return ['error' => $e->getMessage];
} }
return ['message' => "Saved!"];
} }
public function getAllAppValues() public function getAllAppValues()

View File

@@ -30,15 +30,15 @@ class Youtube
if (isset($binary) && @is_executable($binary)) { if (isset($binary) && @is_executable($binary)) {
$this->bin = $binary; $this->bin = $binary;
} else { } else {
$this->bin = Helper::findBinaryPath('youtube-dl',__DIR__."/../../bin/youtube-dl"); $this->bin = Helper::findBinaryPath('youtube-dl', __DIR__ . "/../../bin/youtube-dl");
} }
$this->setDownloadDir($downloadDir); $this->setDownloadDir($downloadDir);
if (!empty($settings)) { if (!empty($settings)) {
foreach ($settings as $key => $value) { foreach ($settings as $key => $value) {
if (empty($value)) { if (empty($value)) {
$this->addOption($key); $this->addOption($key, true);
} else { } else {
$this->setOption($key, $value); $this->setOption($key, $value, true);
} }
} }
} }
@@ -47,6 +47,7 @@ class Youtube
} }
$this->setEnv('LANG', $lang); $this->setEnv('LANG', $lang);
$this->addOption("--no-mtime"); $this->addOption("--no-mtime");
$this->addOption('--ignore-errors');
} }
public function setEnv($key, $val) public function setEnv($key, $val)
@@ -59,8 +60,7 @@ class Youtube
if (Helper::ffmpegInstalled()) { if (Helper::ffmpegInstalled()) {
$this->addOption('--prefer-ffmpeg'); $this->addOption('--prefer-ffmpeg');
$this->addOption('--add-metadata'); $this->addOption('--add-metadata');
$this->addOption('--metadata-from-title'); $this->setOption('--metadata-from-title',"%(artist)s-%(title)s");
$this->addOption("%(artist)s-%(title)s");
$this->addOption('--extract-audio'); $this->addOption('--extract-audio');
} }
$this->outTpl = "/%(id)s-%(title)s.m4a"; $this->outTpl = "/%(id)s-%(title)s.m4a";
@@ -114,7 +114,7 @@ class Youtube
{ {
$this->downloadDir = $this->downloadDir ?? $this->defaultDir; $this->downloadDir = $this->downloadDir ?? $this->defaultDir;
$this->prependOption($this->downloadDir . $this->outTpl); $this->prependOption($this->downloadDir . $this->outTpl);
$this->prependOption("-o"); $this->prependOption("--output");
$this->setUrl($url); $this->setUrl($url);
$this->prependOption($this->bin); $this->prependOption($this->bin);
// $this->buildCMD(); // $this->buildCMD();
@@ -136,16 +136,15 @@ class Youtube
if ($this->audioOnly) { if ($this->audioOnly) {
$this->audioMode(); $this->audioMode();
} else { } else {
$this->setOption('--format',$this->format); $this->setOption('--format', $this->format);
} }
$this->helper = YoutubeHelper::create(); $this->helper = YoutubeHelper::create();
$this->downloadDir = $this->downloadDir ?? $this->defaultDir; $this->downloadDir = $this->downloadDir ?? $this->defaultDir;
$this->prependOption($this->downloadDir . $this->outTpl); $this->setOption("--output", $this->downloadDir . $this->outTpl);
$this->prependOption("-o");
$this->setUrl($url); $this->setUrl($url);
$this->prependOption($this->bin); $this->prependOption($this->bin);
$process = new Process($this->options, null, $this->env); $process = new Process($this->options, null, $this->env);
//\OC::$server->getLogger()->error($process->getWorkingDirectory(), ['app' => 'PHP']); \OC::$server->getLogger()->error($process->getCommandLine(), ['app' => 'PHP']);
$process->setTimeout($this->timeout); $process->setTimeout($this->timeout);
$process->run(function ($type, $buffer) use ($url) { $process->run(function ($type, $buffer) use ($url) {
if (Process::ERR === $type) { if (Process::ERR === $type) {
@@ -186,25 +185,27 @@ class Youtube
public function setUrl($url) public function setUrl($url)
{ {
$this->addOption('-i'); $this->prependOption($url);
$this->addOption($url);
//$index = array_search('-i', $this->options); //$index = array_search('-i', $this->options);
//array_splice($this->options, $index + 1, 0, $url); //array_splice($this->options, $index + 1, 0, $url);
} }
public function setOption($key, $value) public function setOption($key, $value, $hyphens = false)
{ {
$this->addOption($key); $this->addOption($key, $hyphens);
$this->addOption($value); $this->addOption($value, $hyphens);
return $this; return $this;
} }
public function addOption($option) public function addOption(String $option, $hyphens = false)
{ {
if ($hyphens && substr($option, 0, 2) !== '--') {
$option = "--" . $option;
}
array_push($this->options, $option); array_push($this->options, $option);
} }
public function forceIPV4() public function forceIPV4()
{ {
$this->addOption('-4'); $this->addOption('force-ipv4', true);
return $this; return $this;
} }

View File

@@ -0,0 +1,166 @@
<?php
namespace OCA\NCDownloader\Tools;
class youtubedlOptions
{
public static function get()
{
return array_keys(self::options());
}
public static function options()
{
return array(
'ignore-errors' => 'on download errors, for example to skip unavailable videos in a playlist',
'abort-on-error' => 'downloading of further videos (in the playlist or the command line) if an error occurs',
'dump-user-agent' => 'the current browser identification',
'list-extractors' => 'all supported extractors',
'extractor-descriptions' => 'descriptions of all supported extractors',
'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 youtube-',
'ignore-config' => 'not read configuration files. When given in the global configuration file /etc/youtube-dl.conf: Do not read the',
'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 (YouTube only)',
'no-mark-watched' => 'not mark videos watched (YouTube only)',
'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',
'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 in this date',
'datebefore' => 'Download only videos uploaded on or before this date (i.e. inclusive)',
'dateafter' => 'Download only videos uploaded on or after this date (i.e. inclusive)',
'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. Specify any key (see the "OUTPUT TEMPLATE" for a list of available keys) to match if the key',
'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 (DASH, hlsnative and ISM)',
'abort-on-unavailable-fragment' => 'downloading when some fragment is not available',
'keep-fragments' => 'downloaded fragments on disk after downloading is finished; fragments are erased by default',
'buffer-size' => 'Size of download buffer (e.g. 1024 or 16K) (default is 1024)',
'no-resize-buffer' => 'not automatically adjust the buffer size. By default, the buffer size is automatically resized from an initial',
'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 to play the video while downloading (some players may not be able',
'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' => 'Output filename template, see the "OUTPUT TEMPLATE" for all the info',
'output-na-placeholder' => 'Placeholder value for unavailable meta fields in output filename template (default is "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 files',
'continue' => 'resume of partially downloaded files. By default, youtube-dl will resume downloads if possible.',
'no-continue' => 'not resume partially downloaded files (restart from beginning)',
'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',
'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' => 'File to read cookies from and dump cookie jar in',
'cache-dir' => 'Location in the filesystem where youtube-dl can store some downloaded information permanently. By default',
'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' => 'and list all available thumbnail formats',
'quiet' => 'quiet mode',
'no-warnings' => 'warnings',
'simulate' => 'not download the video and do not write anything to disk',
'skip-download' => 'not download the video',
'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' => 'Simulate, quiet but print JSON information. See the "OUTPUT TEMPLATE" for a description of available keys.',
'dump-single-json' => 'Simulate, quiet but print JSON information for each command-line argument. If the URL refers to a playlist, dump',
'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',
'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 when used alone or a lower bound of a range for randomized sleep',
'max-sleep-interval' => 'Upper bound of a range for randomized sleep before each download (maximum possible number of seconds to sleep).',
'format' => 'Video format code, see the "FORMAT SELECTION" for all the info',
'all-formats' => 'all available video formats',
'prefer-free-formats' => 'free video formats unless a specific one is requested',
'list-formats' => 'all available formats of requested videos',
'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' => 'all available subtitles for the video',
'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, youtube-dl 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, youtube-dl will ask interactively.',
'ap-list-mso' => 'all supported multiple-system operators',
'extract-audio' => 'video files to audio-only files (requires ffmpeg/avconv and ffprobe/avprobe)',
'audio-format' => 'Specify audio format: "best", "aac", "flac", "mp3", "m4a", "opus", "vorbis", or "wav"; "best" by default; No effect',
'audio-quality' => 'Specify ffmpeg/avconv audio quality, insert a value between 0 (better) and 9 (worse) for VBR or a specific bitrate',
'recode-video' => 'Encode the video to another format if necessary (currently supported: mp4|flv|ogg|webm|mkv|avi)',
'postprocessor-args' => 'Give these arguments to the postprocessor',
'keep-video' => 'the video file on disk after the post-processing; the video is erased by default',
'no-post-overwrites' => 'not overwrite post-processed files; the post-processed files are overwritten by default',
'embed-subs' => 'subtitles in the video (only for mp4, webm and mkv videos)',
'embed-thumbnail' => 'thumbnail in the audio 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/avconv 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 other format (currently supported: srt|ass|vtt|lrc)',
);
}
}

33
src/css/settings.scss Normal file
View File

@@ -0,0 +1,33 @@
.ncdownloader-personal-settings,.ncdownloader-admin-settings {
position: relative;
#ncdownloader-message-banner {
position : fixed;
top : 50px;
text-align : center;
padding : 15px;
margin-bottom : 20px;
border : 1px solid transparent;
border-radius : 4px;
text-shadow : 0 1px 0 rgba(255, 255, 255, .2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
box-shadow : inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
}
#ncdownloader-message-banner.success,
.message-banner.success {
color : #3c763d;
background-color: #dff0d8;
border-color : #d6e9c6;
width : 100%;
}
#ncdownloader-message-banner.error,
.message-banner.error {
color : #a94442;
background-color: #f2dede;
border-color : #ebccd1;
width : 100%;
}
}

View File

@@ -6,6 +6,10 @@ class settingsForm {
static getInstance() { static getInstance() {
return new this(); return new this();
} }
setParent(selector) {
this.parent = selector;
return this;
}
create(parent, element) { create(parent, element) {
let label = this._createLabel(element.name, element.id) let label = this._createLabel(element.name, element.id)
let input = this._createInput(element); let input = this._createInput(element);
@@ -15,11 +19,8 @@ class settingsForm {
[label, input, cancelBtn].forEach(ele => { [label, input, cancelBtn].forEach(ele => {
container.appendChild(ele); container.appendChild(ele);
}) })
let button;
if (button = parent.querySelector('button.add-custom-aria2-settings')) { return parent.prepend(container);
return parent.insertBefore(container, button);
}
return parent.appendChild(container);
} }
createCustomInput(keyId, valueId) { createCustomInput(keyId, valueId) {
@@ -85,14 +86,14 @@ class settingsForm {
_createInput(data) { _createInput(data) {
let input = document.createElement('input'); let input = document.createElement('input');
let type = data.type || "text"; let type = data.type || "text";
let placeholder = data.placeholder || ''; let placeholder = data.placeholder || 'Leave empty if no value needed';
let value = data.value || placeholder; let value = data.value || '';
input.setAttribute('type', type); input.setAttribute('type', type);
input.setAttribute('id', data.id); input.setAttribute('id', data.id);
input.setAttribute("name", data.name || data.id); input.setAttribute("name", data.name || data.id);
if (type === 'text') { if (type === 'text') {
input.setAttribute('value', value); input.setAttribute('value', value);
input.setAttribute('placeholder', value); input.setAttribute('placeholder', placeholder);
} }
input.classList.add('form-input-' + type); input.classList.add('form-input-' + type);
return input; return input;

View File

@@ -7,67 +7,76 @@ import settingsForm from './lib/settingsForm'
import autoComplete from './lib/autoComplete'; import autoComplete from './lib/autoComplete';
import eventHandler from './lib/eventHandler'; import eventHandler from './lib/eventHandler';
import aria2Options from './utils/aria2Options'; import aria2Options from './utils/aria2Options';
import { names as ytdOptions } from './utils/youtubedlOptions';
import helper from './utils/helper'; import helper from './utils/helper';
import './css/autoComplete.css' import './css/autoComplete.css'
import './css/style.scss'
'use strict'; 'use strict';
window.addEventListener('DOMContentLoaded', function () { window.addEventListener('DOMContentLoaded', function () {
let customOptions = ['ncd_downloader_dir', 'ncd_torrents_dir', 'ncd_seed_ratio', 'ncd_seed_time', 'ncd_rpctoken', 'ncd_yt_binary', 'ncd_aria2_binary'];
eventHandler.add('click', '.ncdownloader-admin-settings', 'input[type="button"]', function (event) { const saveHandler = (e, name) => {
event.stopPropagation(); e.stopImmediatePropagation();
OC_msg.startSaving('#ncdownloader-message-banner',"Saving"); let element = e.target;
const target = this.getAttribute("data-rel"); let data = helper.getData(element.getAttribute("data-rel"));
let inputData = helper.getData(target); let url = generateUrl(data.path);
const path = inputData.url || "/apps/ncdownloader/admin/save"; delete data.path;
let url = generateUrl(path); OC_msg.startSaving('#ncdownloader-message-banner');
Http.getInstance(url).setData(helper.getData(target)).setHandler(function () { helper.makePair(data, name);
OC_msg.finishedSuccess('#ncdownloader-message-banner', "OK"); let badOptions = [];
}).send(); if (name === 'youtube-dl-settings') {
}); for (let key in data) {
eventHandler.add('click', '.ncdownloader-personal-settings', 'input[type="button"]', function (event) { if (!ytdOptions.includes(key) && !customOptions.includes(key)) {
event.preventDefault(); delete data[key];
event.stopPropagation(); badOptions.push(key)
if (event.target.matches('.custom-aria2-settings-container')) { }
}
} else {
for (let key in data) {
if (!aria2Options.includes(key) && !customOptions.includes(key)) {
delete data[key];
badOptions.push(key)
}
}
}
if (badOptions.length > 0) {
OC_msg.finishedError('#ncdownloader-message-banner', 'invalid options: ' + badOptions.join(','));
return; return;
} }
OC_msg.startSaving('#ncdownloader-message-banner'); Http.getInstance(url).setData(data).setHandler(function (data) {
const target = this.getAttribute("data-rel"); if (data.hasOwnProperty("error"))
let inputData = helper.getData(target); OC_msg.finishedError('#ncdownloader-message-banner', data.error);
const path = inputData.url || "/apps/ncdownloader/personal/save"; else if (data.hasOwnProperty("message"))
let url = generateUrl(path); OC_msg.finishedSuccess('#ncdownloader-message-banner', data.message);
Http.getInstance(url).setData(inputData).setHandler(function (data) { else {
OC_msg.finishedSuccess('#ncdownloader-message-banner', "OK"); OC_msg.finishedSuccess('#ncdownloader-message-banner', "DONE");
}
}).send(); }).send();
}); }
eventHandler.add('click', '#custom-aria2-settings-container', "button.add-custom-aria2-settings", function (e) { const addOption = (e, name, options) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
let baseName = `${name}-settings`;
let element = e.target; let element = e.target;
let selector = "#aria2-settings-key-1"; let selector = `#${baseName}-key-1`;
let form = settingsForm.getInstance(); let form = settingsForm.getInstance();
let nodeList, key, value; let nodeList, key, value;
nodeList = document.querySelectorAll("[id^='aria2-settings-key']") nodeList = document.querySelectorAll(`[id^='${baseName}-key']`)
if (nodeList.length === 0) { if (nodeList.length === 0) {
key = "aria2-settings-key-1"; key = `${baseName}-key-1`;
value = "aria2-settings-value-1"; value = `${baseName}-value-1`;
} else { } else {
let index = nodeList.length + 1; let index = nodeList.length + 1;
key = "aria2-settings-key-" + index; key = `${baseName}-key-${index}`;
value = "aria2-settings-value-" + index; value = `${baseName}-value-${index}`;
selector = "[id^='aria2-settings-key']"; selector = `[id^='${baseName}-key']`;
} }
element.before(form.createCustomInput(key, value)); element.before(form.createCustomInput(key, value));
//appended the latest one
nodeList = document.querySelectorAll("[id^='aria2-settings-key']")
try { try {
autoComplete.getInstance({ autoComplete.getInstance({
selector: (nodeList.length !== 0) ? nodeList : selector, selector: `[id^='${baseName}-key']`,
minChars: 1, minChars: 1,
source: function (term, suggest) { source: function (term, suggest) {
term = term.toLowerCase(); term = term.toLowerCase();
let suggestions = [], data = aria2Options; let suggestions = [], data = options;
for (const item of data) { for (const item of data) {
if (item.toLowerCase().indexOf(term, 0) !== -1) { if (item.toLowerCase().indexOf(term, 0) !== -1) {
suggestions.push(item); suggestions.push(item);
@@ -77,22 +86,19 @@ window.addEventListener('DOMContentLoaded', function () {
} }
}).run(); }).run();
} catch (error) { } catch (error) {
console.error(error); OC_msg.finishedError('#ncdownloader-message-banner', error);;
} }
} }
)
eventHandler.add("click", "#custom-aria2-settings-container", "button.save-custom-aria2-settings", function (e) { eventHandler.add('click', '.ncdownloader-admin-settings', 'input[type="button"]', (e) => saveHandler(e));
e.stopImmediatePropagation(); eventHandler.add('click', '.ncdownloader-personal-settings', 'input[type="button"]', (e) => saveHandler(e));
let data = helper.getData(this.getAttribute("data-rel")); eventHandler.add("click", "#custom-aria2-settings-container", "button.save-custom-aria2-settings", (e) => saveHandler(e))
let url = generateUrl(data.path); eventHandler.add("click", "#custom-youtube-dl-settings-container", "button.save-custom-youtube-dl-settings", (e) => saveHandler(e, 'youtube-dl-settings'))
delete data.path;
OC_msg.startSaving('.message-banner'); eventHandler.add('click', '#custom-aria2-settings-container', "button.add-custom-aria2-settings", (e) => addOption(e, 'aria2', aria2Options))
helper.makePair(data); eventHandler.add('click', '#custom-youtube-dl-settings-container', "button.add-custom-youtube-dl-settings", (e) => addOption(e, 'youtube-dl', ytdOptions))
Http.getInstance(url).setData(data).setHandler(function (data) {
OC_msg.finishedSuccess('.message-banner', "OK");
}).send();
})
eventHandler.add('click', '.ncdownloader-personal-settings', 'button.icon-close', function (e) { eventHandler.add('click', '.ncdownloader-personal-settings', 'button.icon-close', function (e) {
e.stopImmediatePropagation(); e.stopImmediatePropagation();
e.preventDefault(); e.preventDefault();
@@ -109,4 +115,15 @@ window.addEventListener('DOMContentLoaded', function () {
} }
settingsForm.getInstance().render(input); settingsForm.getInstance().render(input);
}).send(); }).send();
Http.getInstance(generateUrl("/apps/ncdownloader/personal/youtube-dl/get")).setHandler(function (data) {
if (!data) {
return;
}
let input = [];
for (let key in data) {
input.push({ name: key, value: data[key], id: key });
}
settingsForm.getInstance().setParent("custom-youtube-dl-settings-container").render(input);
}).send();
}); });

View File

@@ -115,7 +115,7 @@ const helper = {
let index; let index;
if ((index = key.indexOf(prefix + "-key-")) !== -1) { if ((index = key.indexOf(prefix + "-key-")) !== -1) {
let valueKey = prefix + "-value-" + key.substring(key.lastIndexOf('-') + 1); let valueKey = prefix + "-value-" + key.substring(key.lastIndexOf('-') + 1);
if (!data[valueKey]) continue; if (data[valueKey] === undefined) continue;
let newkey = data[key]; let newkey = data[key];
data[newkey] = data[valueKey]; data[newkey] = data[valueKey];
delete data[key]; delete data[key];

File diff suppressed because one or more lines are too long

View File

@@ -1,13 +1,12 @@
<?php <?php
script("ncdownloader", 'appSettings'); script("ncdownloader", 'appSettings');
style("ncdownloader", "settings");
extract($_); extract($_);
?> ?>
<div class="ncdownloader-admin-settings"> <div class="ncdownloader-admin-settings">
<div id="ncdownloader-message-banner"></div>
<form id="ncdownloader" class="section"> <form id="ncdownloader" class="section">
<h2>NCDownloader admin Settings</h2> <h2>NCDownloader admin Settings</h2>
<div>
<span id="ncdownloader-message-banner"></span>
</div>
<div id="ncd_rpctoken_settings" path="<?php print $path;?>"> <div id="ncd_rpctoken_settings" path="<?php print $path;?>">
<label for="ncd_rpctoken"> <label for="ncd_rpctoken">
<?php print($l->t('Aria2 RPC Token'));?> <?php print($l->t('Aria2 RPC Token'));?>
@@ -33,7 +32,7 @@ extract($_);
<input type="text" class="ncd_aria2_binary" id="ncd_aria2_binary" name="ncd_aria2_binary" <input type="text" class="ncd_aria2_binary" id="ncd_aria2_binary" name="ncd_aria2_binary"
value="<?php print($ncd_aria2_binary ?? '/usr/bin/aria2c');?>" value="<?php print($ncd_aria2_binary ?? '/usr/bin/aria2c');?>"
placeholder="/usr/bin/aria2c" /> placeholder="/usr/bin/aria2c" />
<input type="button" value="<?php print($l->t('Save'));?>" data-rel="ncd_aria2_binary" /> <input type="button" value="<?php print($l->t('Save'));?>" data-rel="ncd_aria2_binary_container" />
</div> </div>
</form> </form>
</div> </div>

View File

@@ -1,17 +1,17 @@
<?php <?php
//script("ncdownloader", 'common');
//script("ncdownloader", 'settings/personal');
script("ncdownloader", 'appSettings'); script("ncdownloader", 'appSettings');
style("ncdownloader", "autocomplete"); style("ncdownloader", "autocomplete");
style("ncdownloader", "style"); style("ncdownloader", "settings");
extract($_); extract($_);
$time_map = array('i' => 'minutes', 'h' => 'hours', 'w' => 'weeks', 'd' => 'days', 'y' => 'years'); $time_map = array('i' => 'minutes', 'h' => 'hours', 'w' => 'weeks', 'd' => 'days', 'y' => 'years');
?> ?>
<div class="ncdownloader-personal-settings"> <div class="ncdownloader-personal-settings">
<div id="ncdownloader-message-banner"></div>
<div id="ncdownloader-settings-form" class="section"> <div id="ncdownloader-settings-form" class="section">
<div class="ncdownloader-common-settings"> <div class="ncdownloader-general-settings">
<h2><?php print($l->t('NCDownloader Settings'));?></h2> <h2 class="title">
<div id="ncdownloader-message-banner"></div> <?php print($l->t('NCDownloader Settings'));?>
</h2>
<div id="ncd_downloader_dir_settings" path="<?php print $path;?>"> <div id="ncd_downloader_dir_settings" path="<?php print $path;?>">
<label for="ncd_downloader_dir"> <label for="ncd_downloader_dir">
<?php print($l->t('Downloads Folder'));?> <?php print($l->t('Downloads Folder'));?>
@@ -33,7 +33,7 @@ $time_map = array('i' => 'minutes', 'h' => 'hours', 'w' => 'weeks', 'd' => 'days
<hr /> <hr />
<div class="ncdownloader-bt-settings"> <div class="ncdownloader-bt-settings">
<h2> <h2>
<?php print($l->t('BitTorrent protocol settings - Ratio'));?> <?php print($l->t('BT Sharing settings'));?>
</h2> </h2>
<div id="ncd_btratio_container" path="<?php print $path;?>"> <div id="ncd_btratio_container" path="<?php print $path;?>">
<label for="ncd_seed_ratio"> <label for="ncd_seed_ratio">
@@ -55,18 +55,41 @@ $time_map = array('i' => 'minutes', 'h' => 'hours', 'w' => 'weeks', 'd' => 'days
data-rel="seed_time_settings_container" /> data-rel="seed_time_settings_container" />
</div> </div>
</div> </div>
</div>
<h2> <hr />
<?php print($l->t('Custom Aria2 Settings'));?> <div class="advanced-settings-container">
<h2 class="title">
<?php print($l->t('Advanced Settings'));?>
</h2> </h2>
<div class="message-banner"></div> <div class="ncdownloader-aria2-settings">
<div classs="section" id="custom-aria2-settings-container" path="/apps/ncdownloader/personal/aria2/save"> <h3 class="title">
<button class="add-custom-aria2-settings"> <?php print($l->t('Custom Aria2 Settings'));?>
<?php print $l->t('Add Options');?> </h3>
</button> <div classs="section" id="custom-aria2-settings-container"
<button class="save-custom-aria2-settings" data-rel="custom-aria2-settings-container"> path="/apps/ncdownloader/personal/aria2/save">
<?php print $l->t('Save');?> <button class="add-custom-aria2-settings">
</button> <?php print $l->t('Add Options');?>
</button>
<button class="save-custom-aria2-settings" data-rel="custom-aria2-settings-container">
<?php print $l->t('Save');?>
</button>
</div>
</div>
<div class="ncdownloader-youtube-dl-settings">
<h3 class="title">
<?php print($l->t('Custom Youtube-dl Settings'));?>
</h3>
<div classs="section" id="custom-youtube-dl-settings-container"
path="/apps/ncdownloader/personal/youtube-dl/save">
<button class="add-custom-youtube-dl-settings">
<?php print $l->t('Add Options');?>
</button>
<button class="save-custom-youtube-dl-settings" data-tippy-content=''
data-rel="custom-youtube-dl-settings-container">
<?php print $l->t('Save');?>
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
</div>