revamped settings frontend
This commit is contained in:
@@ -9,7 +9,8 @@ use OCP\IDBConnection;
|
|||||||
use OCP\Settings\ISettings;
|
use OCP\Settings\ISettings;
|
||||||
use OCA\NCDownloader\Db\Settings;
|
use OCA\NCDownloader\Db\Settings;
|
||||||
|
|
||||||
class Admin implements ISettings {
|
class Admin implements ISettings
|
||||||
|
{
|
||||||
|
|
||||||
/** @var IDBConnection */
|
/** @var IDBConnection */
|
||||||
private $connection;
|
private $connection;
|
||||||
@@ -18,9 +19,11 @@ class Admin implements ISettings {
|
|||||||
/** @var IConfig */
|
/** @var IConfig */
|
||||||
private $config;
|
private $config;
|
||||||
|
|
||||||
public function __construct(IDBConnection $connection,
|
public function __construct(
|
||||||
ITimeFactory $timeFactory,
|
IDBConnection $connection,
|
||||||
IConfig $config) {
|
ITimeFactory $timeFactory,
|
||||||
|
IConfig $config
|
||||||
|
) {
|
||||||
$this->connection = $connection;
|
$this->connection = $connection;
|
||||||
$this->timeFactory = $timeFactory;
|
$this->timeFactory = $timeFactory;
|
||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
@@ -31,13 +34,18 @@ class Admin implements ISettings {
|
|||||||
/**
|
/**
|
||||||
* @return TemplateResponse
|
* @return TemplateResponse
|
||||||
*/
|
*/
|
||||||
public function getForm() {
|
public function getForm()
|
||||||
|
{
|
||||||
$this->settings->setType($this->settings::TYPE['SYSTEM']);
|
$this->settings->setType($this->settings::TYPE['SYSTEM']);
|
||||||
$parameters = [
|
$parameters = [
|
||||||
"path" => "/apps/ncdownloader/admin/save",
|
'settings' => [
|
||||||
"ncd_yt_binary" => $this->settings->get("ncd_yt_binary"),
|
"path" => "/apps/ncdownloader/admin/save",
|
||||||
"ncd_aria2_binary" => $this->settings->get("ncd_aria2_binary"),
|
"ncd_yt_binary" => $this->settings->get("ncd_yt_binary"),
|
||||||
"ncd_rpctoken" => $this->settings->get("ncd_rpctoken"),
|
"ncd_aria2_binary" => $this->settings->get("ncd_aria2_binary"),
|
||||||
|
"ncd_rpctoken" => $this->settings->get("ncd_rpctoken"),
|
||||||
|
"ncd_aria2_rpc_host" => $this->settings->get("ncd_aria2_rpc_host"),
|
||||||
|
"ncd_aria2_rpc_port" => $this->settings->get("ncd_aria2_rpc_port"),
|
||||||
|
]
|
||||||
];
|
];
|
||||||
return new TemplateResponse('ncdownloader', 'settings/Admin', $parameters, '');
|
return new TemplateResponse('ncdownloader', 'settings/Admin', $parameters, '');
|
||||||
}
|
}
|
||||||
@@ -45,7 +53,8 @@ class Admin implements ISettings {
|
|||||||
/**
|
/**
|
||||||
* @return string the section ID, e.g. 'sharing'
|
* @return string the section ID, e.g. 'sharing'
|
||||||
*/
|
*/
|
||||||
public function getSection(): string {
|
public function getSection(): string
|
||||||
|
{
|
||||||
return 'ncdownloader';
|
return 'ncdownloader';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +65,8 @@ class Admin implements ISettings {
|
|||||||
*
|
*
|
||||||
* E.g.: 70
|
* E.g.: 70
|
||||||
*/
|
*/
|
||||||
public function getPriority(): int {
|
public function getPriority(): int
|
||||||
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ use OCP\Settings\ISettings;
|
|||||||
use OCA\NCDownloader\Db\Settings;
|
use OCA\NCDownloader\Db\Settings;
|
||||||
use OCA\NCDownloader\Tools\Helper;
|
use OCA\NCDownloader\Tools\Helper;
|
||||||
|
|
||||||
class Personal implements ISettings {
|
class Personal implements ISettings
|
||||||
|
{
|
||||||
|
|
||||||
/** @var IDBConnection */
|
/** @var IDBConnection */
|
||||||
private $connection;
|
private $connection;
|
||||||
@@ -19,39 +20,45 @@ class Personal implements ISettings {
|
|||||||
/** @var IConfig */
|
/** @var IConfig */
|
||||||
private $config;
|
private $config;
|
||||||
|
|
||||||
public function __construct(IDBConnection $connection,
|
public function __construct(
|
||||||
ITimeFactory $timeFactory,
|
IDBConnection $connection,
|
||||||
IConfig $config) {
|
ITimeFactory $timeFactory,
|
||||||
|
IConfig $config
|
||||||
|
) {
|
||||||
$this->connection = $connection;
|
$this->connection = $connection;
|
||||||
$this->timeFactory = $timeFactory;
|
$this->timeFactory = $timeFactory;
|
||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
$this->UserId = \OC::$server->getUserSession()->getUser()->getUID();
|
$this->UserId = \OC::$server->getUserSession()->getUser()->getUID();
|
||||||
$this->settings = new Settings($this->UserId);
|
$this->settings = new Settings($this->UserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return TemplateResponse
|
* @return TemplateResponse
|
||||||
*/
|
*/
|
||||||
public function getForm() {
|
public function getForm()
|
||||||
|
{
|
||||||
$parameters = [
|
$parameters = [
|
||||||
"ncd_downloader_dir" => Helper::getDownloadDir(),
|
"settings" => [
|
||||||
"ncd_torrents_dir" => $this->settings->get("ncd_torrents_dir"),
|
"ncd_downloader_dir" => Helper::getDownloadDir(),
|
||||||
"ncd_seed_ratio" => $this->settings->get("ncd_seed_ratio"),
|
"ncd_torrents_dir" => $this->settings->get("ncd_torrents_dir"),
|
||||||
'ncd_seed_time_unit' => $this->settings->get("ncd_seed_time_unit"),
|
"ncd_seed_ratio" => $this->settings->get("ncd_seed_ratio"),
|
||||||
'ncd_seed_time' => $this->settings->get("ncd_seed_time"),
|
'ncd_seed_time_unit' => $this->settings->get("ncd_seed_time_unit"),
|
||||||
"path" => '/apps/ncdownloader/personal/save',
|
'ncd_seed_time' => $this->settings->get("ncd_seed_time"),
|
||||||
|
"path" => '/apps/ncdownloader/personal/save',
|
||||||
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
//\OC_Util::addScript($this->appName, 'common');
|
//\OC_Util::addScript($this->appName, 'common');
|
||||||
//\OC_Util::addScript($this->appName, 'settings/personal');
|
//\OC_Util::addScript($this->appName, 'settings/personal');
|
||||||
//file_put_contents("/tmp/re.log",print_r($parameters,true));
|
//file_put_contents("/tmp/re.log",print_r($parameters,true));
|
||||||
return new TemplateResponse('ncdownloader', 'settings/Personal', $parameters, '');
|
return new TemplateResponse('ncdownloader', 'settings/Personal', $parameters, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string the section ID, e.g. 'sharing'
|
* @return string the section ID, e.g. 'sharing'
|
||||||
*/
|
*/
|
||||||
public function getSection(): string {
|
public function getSection(): string
|
||||||
|
{
|
||||||
return 'ncdownloader';
|
return 'ncdownloader';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +69,8 @@ class Personal implements ISettings {
|
|||||||
*
|
*
|
||||||
* E.g.: 70
|
* E.g.: 70
|
||||||
*/
|
*/
|
||||||
public function getPriority(): int {
|
public function getPriority(): int
|
||||||
|
{
|
||||||
return 100;
|
return 100;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": "^5.0.0",
|
"chalk": "^5.0.0",
|
||||||
"toastr": "^2.1.4",
|
"toastr": "^2.1.4",
|
||||||
"v-tooltip": "^4.0.0-alpha.1"
|
"v-tooltip": "^4.0.0-alpha.1",
|
||||||
|
"vuex": "^4.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.0.0",
|
"node": ">=14.0.0",
|
||||||
@@ -52,6 +53,7 @@
|
|||||||
"css-loader": "^6.4.0",
|
"css-loader": "^6.4.0",
|
||||||
"html-webpack-plugin": "^5.3.2",
|
"html-webpack-plugin": "^5.3.2",
|
||||||
"jquery": "^3.6.0",
|
"jquery": "^3.6.0",
|
||||||
|
"mini-css-extract-plugin": "^2.6.0",
|
||||||
"popper.js": "^1.16.1",
|
"popper.js": "^1.16.1",
|
||||||
"sass": "^1.38.0",
|
"sass": "^1.38.0",
|
||||||
"sass-loader": "^10.2.0",
|
"sass-loader": "^10.2.0",
|
||||||
@@ -68,7 +70,6 @@
|
|||||||
"vue-loader": "^16.0.0-beta.10",
|
"vue-loader": "^16.0.0-beta.10",
|
||||||
"vue-svg-loader": "^0.17.0-beta.2",
|
"vue-svg-loader": "^0.17.0-beta.2",
|
||||||
"webpack": "^5.69.0",
|
"webpack": "^5.69.0",
|
||||||
"webpack-cli": "^4.9.0",
|
"webpack-cli": "^4.9.0"
|
||||||
"mini-css-extract-plugin": "^2.6.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
115
src/adminSettings.vue
Normal file
115
src/adminSettings.vue
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
<template>
|
||||||
|
<div class="section">
|
||||||
|
<settingsRow
|
||||||
|
v-for="(option, key) in optionRows"
|
||||||
|
v-bind:key="key"
|
||||||
|
:value="option.value"
|
||||||
|
:id="option.id"
|
||||||
|
:label="option.label"
|
||||||
|
:placeholder="option.placeholder"
|
||||||
|
:path="option.path"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<customOptions
|
||||||
|
name="admin-aria2-settings"
|
||||||
|
@mounted="render"
|
||||||
|
title="Global Aria2 Settings"
|
||||||
|
path="/apps/ncdownloader/admin/aria2/save"
|
||||||
|
:validOptions="validOptions"
|
||||||
|
>
|
||||||
|
<template #save>Save Settings</template>
|
||||||
|
</customOptions>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import customOptions from "./components/customOptions";
|
||||||
|
import helper from "./utils/helper";
|
||||||
|
import aria2Options from "./utils/aria2Options";
|
||||||
|
import settingsRow from "./components/settingsRow";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "adminSettings",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
options: [],
|
||||||
|
validOptions: aria2Options,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
customOptions,
|
||||||
|
settingsRow,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
render(event, $vm) {
|
||||||
|
helper
|
||||||
|
.httpClient(helper.generateUrl("/apps/ncdownloader/admin/aria2/get"))
|
||||||
|
.setMethod("GET")
|
||||||
|
.setHandler((data) => {
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let input = [];
|
||||||
|
for (let key in data) {
|
||||||
|
if (aria2Options.includes(key))
|
||||||
|
input.push({ name: key, value: data[key], id: key });
|
||||||
|
}
|
||||||
|
//settingsForm.getInstance($vm.container).render(input);
|
||||||
|
$vm.options = input;
|
||||||
|
})
|
||||||
|
.send();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
optionRows() {
|
||||||
|
return this.options;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
try {
|
||||||
|
let data = this.$el.parentElement.getAttribute("data-settings");
|
||||||
|
data = JSON.parse(data);
|
||||||
|
let path = "/apps/ncdownloader/admin/save";
|
||||||
|
this.options = [
|
||||||
|
{
|
||||||
|
label: "Aria2 RPC Host",
|
||||||
|
id: "ncd_aria2_rpc_host",
|
||||||
|
value: data.ncd_aria2_rpc_host,
|
||||||
|
placeholder: "127.0.0.1",
|
||||||
|
path: path,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Aria2 RPC Port",
|
||||||
|
id: "ncd_aria2_rpc_port",
|
||||||
|
value: data.ncd_aria2_rpc_port,
|
||||||
|
placeholder: "6800",
|
||||||
|
path: path,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Aria2 RPC Token",
|
||||||
|
id: "ncd_aria2_rpc_token",
|
||||||
|
value: data.ncd_aria2_rpc_token,
|
||||||
|
placeholder: data.ncd_aria2_rpc_token ? data.ncd_aria2_rpc_token : "ncdownloader123",
|
||||||
|
path: path,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Youtube-dl binary",
|
||||||
|
id: "ncd_yt_binary",
|
||||||
|
value: data.ncd_yt_binary,
|
||||||
|
placeholder: data.ncd_yt_binary
|
||||||
|
? data.ncd_yt_binary
|
||||||
|
: "/usr/local/bin/youtube-dl",
|
||||||
|
path: path,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Aria2c binary",
|
||||||
|
id: "ncd_aria2_binary",
|
||||||
|
value: data.ncd_aria2_binary,
|
||||||
|
placeholder: "/usr/local/bin/aria2c",
|
||||||
|
path: path,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} catch (e) {
|
||||||
|
helper.error(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
132
src/components/customOptions.vue
Normal file
132
src/components/customOptions.vue
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
<template>
|
||||||
|
<div class="section" :class="[classes]" :id="container">
|
||||||
|
<h3 class="title">{{ title }}</h3>
|
||||||
|
<div classs="button-container" :id="id" :path="path">
|
||||||
|
<editableRow
|
||||||
|
v-for="(option, key) in rows"
|
||||||
|
v-bind:key="key"
|
||||||
|
:value="option.value"
|
||||||
|
:name="option.name"
|
||||||
|
:placeholder="option.placeholder"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
class="custom-settings-add-btn"
|
||||||
|
@click.prevent="newOption($event, name)"
|
||||||
|
data-tippy-content="Add new options"
|
||||||
|
>
|
||||||
|
<slot name="add">New Option</slot>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="custom-settings-save-btn"
|
||||||
|
@click.prevent="saveOptions($event)"
|
||||||
|
:data-rel="id"
|
||||||
|
>
|
||||||
|
<slot name="save">Save</slot>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import helper from "../utils/helper";
|
||||||
|
import settingsForm from "../lib/settingsForm";
|
||||||
|
import editableRow from "./editableRow";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "customOptions",
|
||||||
|
props: {
|
||||||
|
path: String,
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
default: "settings",
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: "Custom Settings",
|
||||||
|
},
|
||||||
|
classes: String,
|
||||||
|
validOptions: Array,
|
||||||
|
options: Array,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
id: "custom-" + this.name,
|
||||||
|
classes: "custom-settings-container",
|
||||||
|
container: "custom-settings-container",
|
||||||
|
validOptions: this.validOptions,
|
||||||
|
options: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
editableRow,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
rows() {
|
||||||
|
return this.options;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
newOption(e, baseName) {
|
||||||
|
e.stopPropagation();
|
||||||
|
let element = e.target;
|
||||||
|
let nodeList, key, value;
|
||||||
|
nodeList = document.querySelectorAll(`[id^='${baseName}-key']`);
|
||||||
|
if (nodeList.length === 0) {
|
||||||
|
key = `${baseName}-key-1`;
|
||||||
|
value = `${baseName}-value-1`;
|
||||||
|
} else {
|
||||||
|
let index = nodeList.length + 1;
|
||||||
|
key = `${baseName}-key-${index}`;
|
||||||
|
value = `${baseName}-value-${index}`;
|
||||||
|
//selector = `[id^='${baseName}-key']`;
|
||||||
|
}
|
||||||
|
let form = settingsForm.getInstance();
|
||||||
|
element.before(form.createInputGroup(key, value));
|
||||||
|
helper.autoComplete(`[id^='${baseName}-key']`, this.validOptions);
|
||||||
|
},
|
||||||
|
saveOptions(e) {
|
||||||
|
let element = e.target;
|
||||||
|
let container = element.getAttribute("data-rel");
|
||||||
|
let data = helper.getData(container);
|
||||||
|
let url = helper.generateUrl(data._path);
|
||||||
|
data = helper.transformParams(data, this.name);
|
||||||
|
let badOptions = [];
|
||||||
|
for (let name in data) {
|
||||||
|
if (!this.validOptions.includes(name)) {
|
||||||
|
badOptions.push(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (badOptions.length > 0) {
|
||||||
|
helper.error("invalid options: " + badOptions.join(","));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
helper
|
||||||
|
.httpClient(url)
|
||||||
|
.setData(data)
|
||||||
|
.setHandler((resp) => {
|
||||||
|
if (resp.error) {
|
||||||
|
helper.error(resp.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.options = [];
|
||||||
|
for (let key in data) {
|
||||||
|
this.options.push({ name: key, value: data[key] });
|
||||||
|
}
|
||||||
|
let inputDiv = element.parentElement.querySelectorAll(
|
||||||
|
`div[id^='${this.name}-key']`
|
||||||
|
);
|
||||||
|
if (inputDiv && inputDiv.length > 0) {
|
||||||
|
inputDiv.forEach((element) => {
|
||||||
|
element.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
helper.info(resp.message);
|
||||||
|
})
|
||||||
|
.send();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$emit("mounted", event, this);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
34
src/components/editableRow.vue
Normal file
34
src/components/editableRow.vue
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<template>
|
||||||
|
<div :id="name">
|
||||||
|
<label :for="name">{{ name }}</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
:id="name"
|
||||||
|
:name="name"
|
||||||
|
:value="value"
|
||||||
|
placeholder="Leave empty if no value needed"
|
||||||
|
class="form-input-text"
|
||||||
|
/>
|
||||||
|
<button class="has-content icon-close" @click="remove($event)"></button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "editableRow",
|
||||||
|
props: {
|
||||||
|
name: String,
|
||||||
|
value: String,
|
||||||
|
placeholder: String,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
remove(e) {
|
||||||
|
let ele = e.target;
|
||||||
|
let container = ele.closest("div");
|
||||||
|
container.remove();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
72
src/components/settingsRow.vue
Normal file
72
src/components/settingsRow.vue
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<template>
|
||||||
|
<div :class="container" :path="path" :id="container">
|
||||||
|
<label :for="id">{{ label }}</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
:class="classes"
|
||||||
|
:id="id"
|
||||||
|
:name="id"
|
||||||
|
:value="value"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
@blur="saveHandler($event)"
|
||||||
|
:data-rel="container"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
v-if="useBtn"
|
||||||
|
type="button"
|
||||||
|
value="save"
|
||||||
|
:data-rel="container"
|
||||||
|
@click.prevent="saveHandler($event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import helper from "../utils/helper";
|
||||||
|
export default {
|
||||||
|
name: "settingsRow",
|
||||||
|
props: {
|
||||||
|
label: String,
|
||||||
|
id: String,
|
||||||
|
value: String,
|
||||||
|
placeholder: String,
|
||||||
|
path: String,
|
||||||
|
useBtn: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
classes: this.id.replace("_", "-") + "-input",
|
||||||
|
container: this.id.replace("_", "-") + "-container",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
saveHandler(e) {
|
||||||
|
if (e.type == "blur" && this.useBtn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.stopPropagation();
|
||||||
|
let element = e.target;
|
||||||
|
let data = helper.getData(element.getAttribute("data-rel"));
|
||||||
|
let url = helper.generateUrl(data._path);
|
||||||
|
data = helper.transformParams(data);
|
||||||
|
|
||||||
|
helper
|
||||||
|
.httpClient(url)
|
||||||
|
.setData(data)
|
||||||
|
.setHandler(function (resp) {
|
||||||
|
if (resp.error) {
|
||||||
|
helper.error(resp.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
helper.info(resp.message);
|
||||||
|
})
|
||||||
|
.send();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {},
|
||||||
|
mounted() {},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style scoped></style>
|
||||||
@@ -1,34 +1,36 @@
|
|||||||
.ncdownloader-personal-settings,
|
.ncdownloader-personal-settings,
|
||||||
.ncdownloader-admin-settings {
|
.ncdownloader-admin-settings {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-left: 1em;
|
||||||
|
|
||||||
#ncdownloader-message-banner {
|
#ncdownloader-message-banner {
|
||||||
position : fixed;
|
position: fixed;
|
||||||
top : 50px;
|
top: 50px;
|
||||||
text-align : center;
|
text-align: center;
|
||||||
padding : 15px;
|
padding: 15px;
|
||||||
margin-bottom : 20px;
|
margin-bottom: 20px;
|
||||||
border : 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
border-radius : 4px;
|
border-radius: 4px;
|
||||||
text-shadow : 0 1px 0 rgba(255, 255, 255, .2);
|
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
|
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
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, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
width : 100%;
|
width: 100%;
|
||||||
background-color: #dff0d8;
|
background-color: #dff0d8;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ncdownloader-message-banner.success,
|
#ncdownloader-message-banner.success,
|
||||||
.message-banner.success {
|
.message-banner.success {
|
||||||
color : #799479;
|
color: #799479;
|
||||||
background-color: #dff0d8;
|
background-color: #dff0d8;
|
||||||
border-color : #d6e9c6;
|
border-color: #d6e9c6;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ncdownloader-message-banner.error,
|
#ncdownloader-message-banner.error,
|
||||||
.message-banner.error {
|
.message-banner.error {
|
||||||
color : #a94442;
|
color: #a94442;
|
||||||
background-color: #f2dede;
|
background-color: #f2dede;
|
||||||
border-color : #ebccd1;
|
border-color: #ebccd1;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import helper from './utils/helper'
|
import helper from './utils/helper'
|
||||||
import eventHandler from './lib/eventHandler'
|
import eventHandler from './lib/eventHandler'
|
||||||
import Http from './lib/http'
|
|
||||||
import { translate as t, translatePlural as n } from '@nextcloud/l10n'
|
import { translate as t, translatePlural as n } from '@nextcloud/l10n'
|
||||||
import updatePage from './actions/updatePage'
|
import updatePage from './actions/updatePage'
|
||||||
import buttonActions from './actions/buttonActions'
|
import buttonActions from './actions/buttonActions'
|
||||||
@@ -10,7 +9,6 @@ import { createApp } from 'vue'
|
|||||||
import App from './App';
|
import App from './App';
|
||||||
import tippy, { delegate } from 'tippy.js';
|
import tippy, { delegate } from 'tippy.js';
|
||||||
import 'tippy.js/dist/tippy.css';
|
import 'tippy.js/dist/tippy.css';
|
||||||
'use strict'
|
|
||||||
import settingsBar from './settingsBar';
|
import settingsBar from './settingsBar';
|
||||||
const basePath = "/apps/ncdownloader";
|
const basePath = "/apps/ncdownloader";
|
||||||
|
|
||||||
|
|||||||
@@ -8,31 +8,30 @@ type dataItems = {
|
|||||||
type data = Array<dataItems>
|
type data = Array<dataItems>
|
||||||
|
|
||||||
class settingsForm {
|
class settingsForm {
|
||||||
parent = "custom-aria2-settings-container";
|
container;
|
||||||
constructor() {
|
constructor(containerId?: string) {
|
||||||
|
this.container = containerId
|
||||||
}
|
}
|
||||||
static getInstance() {
|
static getInstance(containerId?: string) {
|
||||||
return new this();
|
return new this(containerId);
|
||||||
}
|
}
|
||||||
setParent(selector: string): settingsForm {
|
setContainer(selector: string): settingsForm {
|
||||||
this.parent = selector;
|
this.container = selector;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
create(parent: HTMLElement, element: dataItems) {
|
create(containerEle: HTMLElement, element: dataItems) {
|
||||||
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);
|
||||||
//let saveBtn = this._createSaveBtn(element.id);
|
//let saveBtn = this._createSaveBtn(element.id);
|
||||||
let cancelBtn = this._createCancelBtn("has-content");
|
let cancelBtn = this._createCancelBtn("has-content");
|
||||||
let container = this._createContainer(element.id);
|
let wrapper = this._createContainer(element.id);
|
||||||
[label, input, cancelBtn].forEach(ele => {
|
[label, input, cancelBtn].forEach(ele => {
|
||||||
container.appendChild(ele);
|
wrapper.appendChild(ele);
|
||||||
})
|
})
|
||||||
|
return containerEle.prepend(wrapper);
|
||||||
return parent.prepend(container);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createCustomInput(keyId: string, valueId: string): HTMLElement {
|
createInputGroup(keyId: string, valueId: string): HTMLElement {
|
||||||
let div = this._createContainer(keyId + "-container")
|
let div = this._createContainer(keyId + "-container")
|
||||||
let items: dataItems = {
|
let items: dataItems = {
|
||||||
id: keyId,
|
id: keyId,
|
||||||
@@ -48,7 +47,8 @@ class settingsForm {
|
|||||||
|
|
||||||
_createContainer(id: string): HTMLElement {
|
_createContainer(id: string): HTMLElement {
|
||||||
let div = document.createElement("div");
|
let div = document.createElement("div");
|
||||||
div.classList.add(id);
|
div.setAttribute("id",id);
|
||||||
|
div.classList.add("autocomplete-container")
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
_createCancelBtn(className = ''): HTMLElement {
|
_createCancelBtn(className = ''): HTMLElement {
|
||||||
@@ -57,6 +57,10 @@ class settingsForm {
|
|||||||
button.classList.add(className);
|
button.classList.add(className);
|
||||||
//button.setAttribute("type",'button')
|
//button.setAttribute("type",'button')
|
||||||
button.classList.add("icon-close");
|
button.classList.add("icon-close");
|
||||||
|
button.addEventListener("click", function () {
|
||||||
|
let container = this.parentNode as HTMLElement
|
||||||
|
container.remove()
|
||||||
|
})
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
_createSaveBtn(id: string): HTMLElement {
|
_createSaveBtn(id: string): HTMLElement {
|
||||||
@@ -68,7 +72,7 @@ class settingsForm {
|
|||||||
}
|
}
|
||||||
_createLabel(name: string, id: string): HTMLElement {
|
_createLabel(name: string, id: string): HTMLElement {
|
||||||
name = name.replace('_', '-');
|
name = name.replace('_', '-');
|
||||||
let label = document.createElement("lable");
|
let label = document.createElement("label");
|
||||||
label.setAttribute("for", id);
|
label.setAttribute("for", id);
|
||||||
let text = document.createTextNode(name);
|
let text = document.createTextNode(name);
|
||||||
label.appendChild(text);
|
label.appendChild(text);
|
||||||
@@ -90,9 +94,12 @@ class settingsForm {
|
|||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
render(data: data) {
|
render(data: data) {
|
||||||
let parent = document.getElementById(this.parent)
|
let container = document.getElementById(this.container)
|
||||||
|
if (!container) {
|
||||||
|
throw this.container + " is not found"
|
||||||
|
}
|
||||||
for (const element of data) {
|
for (const element of data) {
|
||||||
this.create(parent, element)
|
this.create(container, element)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
123
src/personalSettings.vue
Normal file
123
src/personalSettings.vue
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
<template>
|
||||||
|
<div class="section ncdownloader-general-settings">
|
||||||
|
<h3>General Settings</h3>
|
||||||
|
<settingsRow
|
||||||
|
v-for="(option, key) in optionRows"
|
||||||
|
v-bind:key="key"
|
||||||
|
:value="option.value"
|
||||||
|
:id="option.id"
|
||||||
|
:label="option.label"
|
||||||
|
:placeholder="option.placeholder"
|
||||||
|
:path="option.path"
|
||||||
|
:useBtn="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<customOptions
|
||||||
|
name="custom-aria2-settings"
|
||||||
|
title="Personal Aria2 Settings"
|
||||||
|
@mounted="renderAria2"
|
||||||
|
path="/apps/ncdownloader/personal/aria2/save"
|
||||||
|
:validOptions="aria2Options"
|
||||||
|
>
|
||||||
|
<template #save>Save Aria2 Settings</template>
|
||||||
|
</customOptions>
|
||||||
|
<customOptions
|
||||||
|
name="custom-ytdl-settings"
|
||||||
|
title="Personal Youtbue-dl Settings"
|
||||||
|
@mounted="renderYtdl"
|
||||||
|
path="/apps/ncdownloader/personal/ytdl/save"
|
||||||
|
:validOptions="ytdlOptions"
|
||||||
|
>
|
||||||
|
<template #save>Save Youtube-dl Settings</template>
|
||||||
|
</customOptions>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import customOptions from "./components/customOptions";
|
||||||
|
import helper from "./utils/helper";
|
||||||
|
import aria2Options from "./utils/aria2Options";
|
||||||
|
import { options as ytdlFullOptions, names as ytdlOptions } from "./utils/ytdlOptions";
|
||||||
|
import settingsRow from "./components/settingsRow";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "personalSettings",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
options: [],
|
||||||
|
aria2Options: aria2Options,
|
||||||
|
ytdlOptions: ytdlOptions,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
customOptions,
|
||||||
|
settingsRow,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
renderAria2(event, $vm) {
|
||||||
|
helper
|
||||||
|
.httpClient(helper.generateUrl("/apps/ncdownloader/personal/aria2/get"))
|
||||||
|
//.setMethod("GET")
|
||||||
|
.setHandler((data) => {
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let input = [];
|
||||||
|
for (let key in data) {
|
||||||
|
if (aria2Options.includes(key))
|
||||||
|
input.push({ name: key, value: data[key], id: key });
|
||||||
|
}
|
||||||
|
//settingsForm.getInstance($vm.container).render(input);
|
||||||
|
$vm.options = input;
|
||||||
|
})
|
||||||
|
.send();
|
||||||
|
},
|
||||||
|
renderYtdl(event, $vm) {
|
||||||
|
helper
|
||||||
|
.httpClient(helper.generateUrl("/apps/ncdownloader/personal/ytdl/get"))
|
||||||
|
//.setMethod("GET")
|
||||||
|
.setHandler((data) => {
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let input = [];
|
||||||
|
for (let key in data) {
|
||||||
|
if (ytdlOptions.includes(key))
|
||||||
|
input.push({ name: key, value: data[key], id: key });
|
||||||
|
}
|
||||||
|
//settingsForm.getInstance($vm.container).render(input);
|
||||||
|
$vm.options = input;
|
||||||
|
})
|
||||||
|
.send();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
optionRows() {
|
||||||
|
return this.options;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
try {
|
||||||
|
let data = this.$el.parentElement.getAttribute("data-settings");
|
||||||
|
data = JSON.parse(data);
|
||||||
|
let path = "/apps/ncdownloader/personal/save";
|
||||||
|
this.options = [
|
||||||
|
{
|
||||||
|
label: "Downloads Folder ",
|
||||||
|
id: "ncd_downloader_dir",
|
||||||
|
value: data.ncd_downloader_dir,
|
||||||
|
placeholder: data.ncd_downloader_dir ? data.ncd_downloader_dir : "/downloads",
|
||||||
|
path: path,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Torrents Folder",
|
||||||
|
id: "ncd_torrents_dir",
|
||||||
|
value: data.ncd_torrents_dir,
|
||||||
|
placeholder: data.ncd_torrents_dir ? data.ncd_torrents_dir : "/torrents",
|
||||||
|
path: path,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} catch (e) {
|
||||||
|
helper.error(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
140
src/settings.js
140
src/settings.js
@@ -1,143 +1,19 @@
|
|||||||
import Http from './lib/http'
|
|
||||||
import OC_msg from './lib/msg'
|
|
||||||
import {
|
|
||||||
generateUrl
|
|
||||||
} from '@nextcloud/router'
|
|
||||||
import settingsForm from './lib/settingsForm'
|
|
||||||
import autoComplete from './lib/autoComplete';
|
|
||||||
import eventHandler from './lib/eventHandler';
|
import eventHandler from './lib/eventHandler';
|
||||||
import aria2Options from './utils/aria2Options';
|
|
||||||
import { options as ytdlFullOptions, names as ytdlOptions } from './utils/ytdlOptions';
|
|
||||||
import helper from './utils/helper';
|
import helper from './utils/helper';
|
||||||
import './css/autoComplete.css'
|
import './css/autoComplete.css'
|
||||||
import './css/settings.scss'
|
import './css/settings.scss'
|
||||||
'use strict';
|
|
||||||
import { delegate } from 'tippy.js';
|
import { delegate } from 'tippy.js';
|
||||||
import 'tippy.js/dist/tippy.css';
|
import 'tippy.js/dist/tippy.css';
|
||||||
|
import { createApp } from 'vue';
|
||||||
|
import adminSettings from './adminSettings';
|
||||||
|
import personalSettings from './personalSettings';
|
||||||
|
|
||||||
|
const customSettings = createApp(adminSettings)
|
||||||
|
const pSettings = createApp(personalSettings)
|
||||||
|
customSettings.mount('#ncdownloader-admin-settings')
|
||||||
|
pSettings.mount('#ncdownloader-personal-settings')
|
||||||
|
|
||||||
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'];
|
|
||||||
const saveHandler = (e, name) => {
|
|
||||||
e.stopImmediatePropagation();
|
|
||||||
let element = e.target;
|
|
||||||
let data = helper.getData(element.getAttribute("data-rel"));
|
|
||||||
let url = generateUrl(data.path);
|
|
||||||
delete data.path;
|
|
||||||
OC_msg.startSaving('#ncdownloader-message-banner');
|
|
||||||
helper.makePair(data, name);
|
|
||||||
let badOptions = [];
|
|
||||||
if (name === 'ytdl-settings') {
|
|
||||||
for (let key in data) {
|
|
||||||
if (!ytdlOptions.includes(key) && !customOptions.includes(key)) {
|
|
||||||
delete data[key];
|
|
||||||
badOptions.push(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} 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;
|
|
||||||
}
|
|
||||||
helper.httpClient(url).setData(data).setHandler(function (data) {
|
|
||||||
if (data.hasOwnProperty("error")) {
|
|
||||||
OC_msg.finishedError('#ncdownloader-message-banner', data.error);
|
|
||||||
} else if (data.hasOwnProperty("message")) {
|
|
||||||
OC_msg.finishedSuccess('#ncdownloader-message-banner', data.message);
|
|
||||||
} else {
|
|
||||||
OC_msg.finishedSuccess('#ncdownloader-message-banner', "DONE");
|
|
||||||
}
|
|
||||||
}).send();
|
|
||||||
}
|
|
||||||
const addOption = (e, name, options) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
let baseName = `${name}-settings`;
|
|
||||||
let element = e.target;
|
|
||||||
let selector = `#${baseName}-key-1`;
|
|
||||||
let form = settingsForm.getInstance();
|
|
||||||
let nodeList, key, value;
|
|
||||||
nodeList = document.querySelectorAll(`[id^='${baseName}-key']`)
|
|
||||||
if (nodeList.length === 0) {
|
|
||||||
key = `${baseName}-key-1`;
|
|
||||||
value = `${baseName}-value-1`;
|
|
||||||
} else {
|
|
||||||
let index = nodeList.length + 1;
|
|
||||||
key = `${baseName}-key-${index}`;
|
|
||||||
value = `${baseName}-value-${index}`;
|
|
||||||
selector = `[id^='${baseName}-key']`;
|
|
||||||
}
|
|
||||||
element.before(form.createCustomInput(key, value));
|
|
||||||
try {
|
|
||||||
autoComplete.getInstance({
|
|
||||||
selector: `[id^='${baseName}-key']`,
|
|
||||||
minChars: 1,
|
|
||||||
sourceHandler: function () {
|
|
||||||
if (Array.isArray(options)) {
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
return Object.keys(options);
|
|
||||||
},
|
|
||||||
renderer: (item, search) => {
|
|
||||||
search = search.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
||||||
let tippy;
|
|
||||||
if (options.hasOwnProperty(item)) {
|
|
||||||
tippy = options[item];
|
|
||||||
} else {
|
|
||||||
tippy = item;
|
|
||||||
}
|
|
||||||
var re = new RegExp(`(${search.split(' ').join('|')})`, "gi");
|
|
||||||
return `<div data-tippy-content="${tippy}" class="suggestion-item" data-val="${item}">${item.replace(re, "<b>$1</b>")}</div>`;
|
|
||||||
}
|
|
||||||
}).run();
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error)
|
|
||||||
OC_msg.finishedError('#ncdownloader-message-banner', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eventHandler.add('click', '.ncdownloader-admin-settings', 'input[type="button"]', (e) => saveHandler(e));
|
|
||||||
eventHandler.add('click', '.ncdownloader-personal-settings', 'input[type="button"]', (e) => saveHandler(e));
|
|
||||||
eventHandler.add("click", "#custom-aria2-settings-container", "button.save-custom-aria2-settings", (e) => saveHandler(e))
|
|
||||||
eventHandler.add("click", "#custom-ytdl-settings-container", "button.save-custom-ytdl-settings", (e) => saveHandler(e, 'ytdl-settings'))
|
|
||||||
|
|
||||||
eventHandler.add('click', '#custom-aria2-settings-container', "button.add-custom-aria2-settings", (e) => addOption(e, 'aria2', aria2Options))
|
|
||||||
eventHandler.add('click', '#custom-ytdl-settings-container', "button.add-custom-ytdl-settings", (e) => addOption(e, 'ytdl', ytdlFullOptions))
|
|
||||||
|
|
||||||
|
|
||||||
eventHandler.add('click', '.ncdownloader-personal-settings', 'button.icon-close', function (e) {
|
|
||||||
e.stopImmediatePropagation();
|
|
||||||
e.preventDefault();
|
|
||||||
this.parentNode.remove();
|
|
||||||
})
|
|
||||||
helper.httpClient(generateUrl("/apps/ncdownloader/personal/aria2/get")).setHandler(function (data) {
|
|
||||||
if (!data) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let input = [];
|
|
||||||
for (let key in data) {
|
|
||||||
if (aria2Options.includes(key))
|
|
||||||
input.push({ name: key, value: data[key], id: key });
|
|
||||||
}
|
|
||||||
settingsForm.getInstance().render(input);
|
|
||||||
}).send();
|
|
||||||
|
|
||||||
helper.httpClient(generateUrl("/apps/ncdownloader/personal/ytdl/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-ytdl-settings-container").render(input);
|
|
||||||
}).send();
|
|
||||||
|
|
||||||
const filepicker = function (event) {
|
const filepicker = function (event) {
|
||||||
let element = event.target;
|
let element = event.target;
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import { translate as t, translatePlural as n } from '@nextcloud/l10n'
|
|||||||
import contentTable from '../lib/contentTable';
|
import contentTable from '../lib/contentTable';
|
||||||
import Http from '../lib/http'
|
import Http from '../lib/http'
|
||||||
import Polling from "../lib/polling";
|
import Polling from "../lib/polling";
|
||||||
|
import autoComplete from '../lib/autoComplete';
|
||||||
|
|
||||||
const helper = {
|
const helper = {
|
||||||
vue: {},
|
vue: {},
|
||||||
addVue(name, object) {
|
addVue(name, object) {
|
||||||
@@ -69,9 +71,13 @@ const helper = {
|
|||||||
return string.charAt(0).toUpperCase() + string.slice(1)
|
return string.charAt(0).toUpperCase() + string.slice(1)
|
||||||
},
|
},
|
||||||
isURL(url) {
|
isURL(url) {
|
||||||
let regex = '^((https?|ftp)://)([a-z0-9-]+\.)?(?:[-a-zA-Z0-9()@:%_\+.~#?&/=]+)$';
|
try {
|
||||||
const pattern = new RegExp(regex, 'i');
|
new URL(url.trim());
|
||||||
return pattern.test(url);
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
isMagnetURI(url) {
|
isMagnetURI(url) {
|
||||||
const magnetURI = /^magnet:\?xt=urn:[a-z0-9]+:[a-z0-9]{32,40}(&dn=.+&tr=.+)?$/i;
|
const magnetURI = /^magnet:\?xt=urn:[a-z0-9]+:[a-z0-9]{32,40}(&dn=.+&tr=.+)?$/i;
|
||||||
@@ -154,23 +160,10 @@ const helper = {
|
|||||||
let doc = parser.parseFromString(htmlString, "text/html")
|
let doc = parser.parseFromString(htmlString, "text/html")
|
||||||
return doc.querySelector("div");
|
return doc.querySelector("div");
|
||||||
},
|
},
|
||||||
makePair: function (data, prefix = "aria2-settings") {
|
|
||||||
for (let key in data) {
|
|
||||||
let index;
|
|
||||||
if ((index = key.indexOf(prefix + "-key-")) !== -1) {
|
|
||||||
let valueKey = prefix + "-value-" + key.substring(key.lastIndexOf('-') + 1);
|
|
||||||
if (data[valueKey] === undefined) continue;
|
|
||||||
let newkey = data[key];
|
|
||||||
data[newkey] = data[valueKey];
|
|
||||||
delete data[key];
|
|
||||||
delete data[valueKey];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getData(selector) {
|
getData(selector) {
|
||||||
const element = typeof selector === "object" ? selector : document.getElementById(selector)
|
const element = typeof selector === "object" ? selector : document.getElementById(selector)
|
||||||
const data = {}
|
const data = {}
|
||||||
data['path'] = element.getAttribute('path') || '';
|
data['_path'] = element.getAttribute('path') || '';
|
||||||
//if the targeted element is not of input or select type, search for such elements below it
|
//if the targeted element is not of input or select type, search for such elements below it
|
||||||
if (!['SELECT', 'INPUT'].includes(element.nodeName.toUpperCase())) {
|
if (!['SELECT', 'INPUT'].includes(element.nodeName.toUpperCase())) {
|
||||||
const nodeList = element.querySelectorAll('input,select')
|
const nodeList = element.querySelectorAll('input,select')
|
||||||
@@ -183,11 +176,17 @@ const helper = {
|
|||||||
const key = element.getAttribute('id')
|
const key = element.getAttribute('id')
|
||||||
data[key] = element.value
|
data[key] = element.value
|
||||||
for (let prop in element.dataset) {
|
for (let prop in element.dataset) {
|
||||||
|
if (prop == "rel") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
data[prop] = element.dataset[prop];
|
data[prop] = element.dataset[prop];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (let prop in element.dataset) {
|
for (let prop in element.dataset) {
|
||||||
|
if (prop == "rel") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
data[prop] = element.dataset[prop];
|
data[prop] = element.dataset[prop];
|
||||||
}
|
}
|
||||||
const key = element.getAttribute('id')
|
const key = element.getAttribute('id')
|
||||||
@@ -287,7 +286,7 @@ const helper = {
|
|||||||
container.setAttribute("type", name);
|
container.setAttribute("type", name);
|
||||||
container.className = "table " + name;
|
container.className = "table " + name;
|
||||||
},
|
},
|
||||||
filepicker(cb,currentPath) {
|
filepicker(cb, currentPath) {
|
||||||
OC.dialogs.filepicker(
|
OC.dialogs.filepicker(
|
||||||
t('ncdownloader', 'Select a directory'),
|
t('ncdownloader', 'Select a directory'),
|
||||||
cb,
|
cb,
|
||||||
@@ -308,6 +307,55 @@ const helper = {
|
|||||||
},
|
},
|
||||||
httpClient(url) {
|
httpClient(url) {
|
||||||
return new Http.create(url, true)
|
return new Http.create(url, true)
|
||||||
|
},
|
||||||
|
autoComplete(selector, options) {
|
||||||
|
try {
|
||||||
|
autoComplete.getInstance({
|
||||||
|
selector: selector,
|
||||||
|
minChars: 1,
|
||||||
|
sourceHandler: function () {
|
||||||
|
if (Array.isArray(options)) {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
return Object.keys(options);
|
||||||
|
},
|
||||||
|
renderer: (item, search) => {
|
||||||
|
if (!item || !search) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
search = search.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||||
|
let tippy;
|
||||||
|
if (options.hasOwnProperty(item)) {
|
||||||
|
tippy = options[item];
|
||||||
|
} else {
|
||||||
|
tippy = item;
|
||||||
|
}
|
||||||
|
var re = new RegExp(`(${search.split(' ').join('|')})`, "gi");
|
||||||
|
return `<div data-tippy-content="${tippy}" class="suggestion-item" data-val="${item}">${item.replace(re, "<b>$1</b>")}</div>`;
|
||||||
|
}
|
||||||
|
}).run();
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
helper.error(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
transformParams(data, prefix = "aria2-settings") {
|
||||||
|
let index
|
||||||
|
for (let key in data) {
|
||||||
|
if (key.charAt(0) == "_") {
|
||||||
|
delete data[key]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ((index = key.indexOf(prefix + "-key-")) !== -1) {
|
||||||
|
let valueKey = prefix + "-value-" + key.substring(key.lastIndexOf('-') + 1);
|
||||||
|
if (data[valueKey] === undefined) continue;
|
||||||
|
let newkey = data[key];
|
||||||
|
data[newkey] = data[valueKey];
|
||||||
|
delete data[key];
|
||||||
|
delete data[valueKey];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,36 +3,6 @@ script("ncdownloader", 'appSettings');
|
|||||||
style("ncdownloader", 'appSettings');
|
style("ncdownloader", 'appSettings');
|
||||||
extract($_);
|
extract($_);
|
||||||
?>
|
?>
|
||||||
<div class="ncdownloader-admin-settings">
|
<div id="ncdownloader-admin-settings" class="ncdownloader-admin-settings" data-settings='<?php print json_encode($settings);?>'>
|
||||||
<div id="ncdownloader-message-banner" style="display: none;"></div>
|
|
||||||
<form id="ncdownloader" class="section">
|
|
||||||
<h2>NCDownloader admin Settings</h2>
|
|
||||||
<div id="ncd_rpctoken_settings" path="<?php print $path;?>">
|
|
||||||
<label for="ncd_rpctoken">
|
|
||||||
<?php print($l->t('Aria2 RPC Token'));?>
|
|
||||||
</label>
|
|
||||||
<input type="text" class="ncd_rpctoken" id="ncd_rpctoken" name="ncd_rpctoken"
|
|
||||||
value="<?php print($ncd_rpctoken ?? 'ncdownloader123');?>"
|
|
||||||
placeholder="ncdownloader123" />
|
|
||||||
<input type="button" value="<?php print($l->t('Save'));?>" data-rel="ncd_rpctoken_settings" />
|
|
||||||
</div>
|
|
||||||
<div id="ncd_yt_binary_container" path="<?php print $path;?>">
|
|
||||||
<label for="ncd_yt_binary">
|
|
||||||
<?php print($l->t('Youtube-dl Binary Path'));?>
|
|
||||||
</label>
|
|
||||||
<input type="text" class="ncd_yt_binary" id="ncd_yt_binary" name="ncd_yt_binary"
|
|
||||||
value="<?php print($ncd_yt_binary ?? '/usr/local/bin/ytdl');?>"
|
|
||||||
placeholder='/usr/local/bin/ytdl' />
|
|
||||||
<input type="button" value="<?php print($l->t('Save'));?>" data-rel="ncd_yt_binary_container" />
|
|
||||||
</div>
|
|
||||||
<div id="ncd_aria2_binary_container" path="<?php print $path;?>">
|
|
||||||
<label for="ncd_aria2_binary">
|
|
||||||
<?php print($l->t('Aria2 Binary Path'));?>
|
|
||||||
</label>
|
|
||||||
<input type="text" class="ncd_aria2_binary" id="ncd_aria2_binary" name="ncd_aria2_binary"
|
|
||||||
value="<?php print($ncd_aria2_binary ?? '/usr/bin/aria2c');?>"
|
|
||||||
placeholder="/usr/bin/aria2c" />
|
|
||||||
<input type="button" value="<?php print($l->t('Save'));?>" data-rel="ncd_aria2_binary_container" />
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
@@ -2,93 +2,8 @@
|
|||||||
script("ncdownloader", 'appSettings');
|
script("ncdownloader", 'appSettings');
|
||||||
style("ncdownloader", 'appSettings');
|
style("ncdownloader", 'appSettings');
|
||||||
extract($_);
|
extract($_);
|
||||||
$time_map = array('i' => 'minutes', 'h' => 'hours', 'w' => 'weeks', 'd' => 'days', 'y' => 'years');
|
|
||||||
?>
|
?>
|
||||||
<div class="ncdownloader-personal-settings">
|
<div class="ncdownloader-personal-settings" id="ncdownloader-personal-settings" data-settings='<?php print json_encode($settings); ?>'>
|
||||||
<div id="ncdownloader-message-banner" style="display: none;"></div>
|
|
||||||
<div id="ncdownloader-settings-form" class="section">
|
|
||||||
<div class="ncdownloader-general-settings">
|
|
||||||
<h2 class="title">
|
|
||||||
<?php print($l->t('NCDownloader Settings'));?>
|
|
||||||
</h2>
|
|
||||||
<div id="ncd_downloader_dir_settings" path="<?php print $path;?>">
|
|
||||||
<label for="ncd_downloader_dir">
|
|
||||||
<?php print($l->t('Downloads Folder'));?>
|
|
||||||
</label>
|
|
||||||
<input type="text" class="ncd_downloader_dir" id="ncd_downloader_dir" name="ncd_downloader_dir"
|
|
||||||
value="<?php print($ncd_downloader_dir ?? '/Downloads');?>" placeholder="/Downloads" />
|
|
||||||
<input type="button" value="<?php print($l->t('Save'));?>" data-rel="ncd_downloader_dir_settings" />
|
|
||||||
</div>
|
|
||||||
<div id="ncd_torrents_dir_settings" path="<?php print $path;?>">
|
|
||||||
<label for="ncd_torrents_dir">
|
|
||||||
<?php print($l->t('Torrents Folder'));?>
|
|
||||||
</label>
|
|
||||||
<input type="text" class="ncd_torrents_dir" id="ncd_torrents_dir"
|
|
||||||
value="<?php print($ncd_torrents_dir ?? '/Downloads/Files/Torrents');?>"
|
|
||||||
placeholder="/Downloads/Files/Torrents" />
|
|
||||||
<input type="button" value="<?php print($l->t('Save'));?>" data-rel="ncd_torrents_dir_settings" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
<div class="ncdownloader-bt-settings">
|
|
||||||
<h2>
|
|
||||||
<?php print($l->t('BT Sharing settings'));?>
|
|
||||||
</h2>
|
|
||||||
<div id="ncd_btratio_container" path="<?php print $path;?>">
|
|
||||||
<label for="ncd_seed_ratio">
|
|
||||||
<?php print($l->t('Seed ratio'));?>
|
|
||||||
</label>
|
|
||||||
<input id="ncd_seed_ratio" value="<?php print($ncd_seed_ratio ?? 1.0);?>" placeholder="1.0">
|
|
||||||
</input>
|
|
||||||
<input type="button" value="<?php print($l->t('Save'));?>" data-rel="ncd_btratio_container" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div id="seed_time_settings_container" path="<?php print $path;?>">
|
|
||||||
<label for="ncd_seed_time">
|
|
||||||
<?php print($l->t('Seed Time in minutes'));?>
|
|
||||||
</label>
|
|
||||||
<input id="ncd_seed_time" type="text" class="ncd_seed_time"
|
|
||||||
value="<?php print($ncd_seed_time ?? 1);?>" placeholder="1 m,h,d,w,m">
|
|
||||||
</input>
|
|
||||||
<input type="button" value="<?php print($l->t('Save'));?>"
|
|
||||||
data-rel="seed_time_settings_container" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
<div class="advanced-settings-container">
|
|
||||||
<h2 class="title">
|
|
||||||
<?php print($l->t('Advanced Settings'));?>
|
|
||||||
</h2>
|
|
||||||
<div class="ncdownloader-aria2-settings">
|
|
||||||
<h3 class="title">
|
|
||||||
<?php print($l->t('Custom Aria2 Settings'));?>
|
|
||||||
</h3>
|
|
||||||
<div classs="section" id="custom-aria2-settings-container"
|
|
||||||
path="/apps/ncdownloader/personal/aria2/save">
|
|
||||||
<button class="add-custom-aria2-settings">
|
|
||||||
<?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-ytdl-settings">
|
|
||||||
<h3 class="title">
|
|
||||||
<?php print($l->t('Custom Youtube-dl Settings'));?>
|
|
||||||
</h3>
|
|
||||||
<div classs="section" id="custom-ytdl-settings-container"
|
|
||||||
path="/apps/ncdownloader/personal/ytdl/save">
|
|
||||||
<button class="add-custom-ytdl-settings">
|
|
||||||
<?php print $l->t('Add Options');?>
|
|
||||||
</button>
|
|
||||||
<button class="save-custom-ytdl-settings" data-tippy-content=''
|
|
||||||
data-rel="custom-ytdl-settings-container">
|
|
||||||
<?php print $l->t('Save');?>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
Reference in New Issue
Block a user