mirror of
https://github.com/VueTorrent/VueTorrent.git
synced 2025-03-14 20:20:04 +03:00
0.4.7 (#80)
This commit is contained in:
parent
f895738e70
commit
39e5549b80
81 changed files with 4718 additions and 4235 deletions
100
.eslintrc.js
100
.eslintrc.js
|
@ -1,31 +1,73 @@
|
|||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
es2021: true
|
||||
},
|
||||
extends: [
|
||||
'plugin:vue/essential',
|
||||
'google'
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:vue/recommended'
|
||||
],
|
||||
parserOptions: {
|
||||
parser: 'babel-eslint',
|
||||
ecmaVersion: 2017,
|
||||
sourceType: 'module'
|
||||
},
|
||||
env: {
|
||||
es6: true,
|
||||
browser: true,
|
||||
node: true
|
||||
},
|
||||
plugins: [
|
||||
'vue'
|
||||
],
|
||||
rules: {
|
||||
'arrow-parens': ['error', 'as-needed'],
|
||||
'key-spacing': ['error', { beforeColon: false, afterColon: true }],
|
||||
'array-bracket-spacing': ['error', 'never'],
|
||||
'comma-spacing': ['error', { before: false, after: true }],
|
||||
'brace-style': ['error', '1tbs'],
|
||||
'comma-dangle': ['error', 'never'],
|
||||
'comma-style': ['error', 'last'],
|
||||
eqeqeq: 0,
|
||||
indent: ['error', 2, {
|
||||
SwitchCase: 1
|
||||
}],
|
||||
'vue/html-indent': [
|
||||
'error',
|
||||
2,
|
||||
{ ignores: ['VElement[name=code-block].children'] }
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 12,
|
||||
sourceType: 'module'
|
||||
},
|
||||
plugins: [
|
||||
'vue'
|
||||
],
|
||||
rules: {
|
||||
indent: ['warn', 4],
|
||||
semi: ['error', 'never'],
|
||||
quotes: ['error', 'single'],
|
||||
'comma-dangle': ['error', 'never'],
|
||||
'arrow-parens': ['error', 'as-needed'],
|
||||
'no-underscore-dangle': ['warn', { allowAfterThis: true }],
|
||||
'quote-props': ['error', 'as-needed'],
|
||||
'object-curly-spacing': ['error', 'always'],
|
||||
'require-jsdoc': 'off',
|
||||
'valid-jsdoc': 'off',
|
||||
'max-len': ['error', { code: 100 }],
|
||||
'vue/html-quotes': ['error', 'double', { avoidEscape: true }]
|
||||
}
|
||||
}
|
||||
'vue/multiline-html-element-content-newline': ['error', {
|
||||
ignores: ['code-block']
|
||||
}],
|
||||
'linebreak-style': ['error', 'unix'],
|
||||
'keyword-spacing': 'error',
|
||||
'prefer-const': 'error',
|
||||
'new-parens': 'error',
|
||||
'no-case-declarations': 'off',
|
||||
'no-const-assign': 'error',
|
||||
'no-unused-vars': ['error', { args: 'none' }],
|
||||
'no-whitespace-before-property': 'error',
|
||||
'object-curly-spacing': ['error', 'always'],
|
||||
quotes: ['error', 'single'],
|
||||
'quote-props': ['error', 'as-needed'],
|
||||
semi: ['error', 'never'],
|
||||
'space-before-blocks': 'error',
|
||||
'space-before-function-paren': ['error', { anonymous: 'always', named: 'never' }],
|
||||
'space-in-parens': ['error', 'never'],
|
||||
'space-infix-ops': 'error',
|
||||
strict: ['error', 'never'],
|
||||
'vue/max-attributes-per-line': ['error', {
|
||||
singleline: 3,
|
||||
multiline: {
|
||||
max: 1,
|
||||
allowFirstLine: false
|
||||
}
|
||||
}],
|
||||
'vue/no-v-html': 'off',
|
||||
'vue/require-prop-types': 'off',
|
||||
'vue/require-default-prop': 'off',
|
||||
'vue/require-v-for-key': 'off',
|
||||
'vue/order-in-components': ['error'],
|
||||
'vue/space-infix-ops': 'error',
|
||||
'arrow-spacing': ['error', { before: true, after: true }],
|
||||
'no-multi-spaces': ['error'],
|
||||
'newline-before-return': ['error']
|
||||
}
|
||||
}
|
184
package-lock.json
generated
184
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "vuetorrent",
|
||||
"version": "0.4.6",
|
||||
"version": "0.4.7",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -1029,9 +1029,9 @@
|
|||
}
|
||||
},
|
||||
"import-fresh": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz",
|
||||
"integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==",
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"parent-module": "^1.0.0",
|
||||
|
@ -2100,9 +2100,9 @@
|
|||
}
|
||||
},
|
||||
"apexcharts": {
|
||||
"version": "3.22.3",
|
||||
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.22.3.tgz",
|
||||
"integrity": "sha512-ZRZWmAmSdyc+tFhHMZ10ZxbvSbomWe46izpi8yQj5cKLxuujw2XeXVQ0jxnPl9yE5Q7W2hAbDWStaouBN4mSuw==",
|
||||
"version": "3.23.1",
|
||||
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.23.1.tgz",
|
||||
"integrity": "sha512-7fRpquXp725BUew5OO1mJWk16/IJPCUl0l8SjhISnAhAtbTaM9PnXPSmN2BvKO4RcT457CzMM7MCG5UokiTwcA==",
|
||||
"requires": {
|
||||
"svg.draggable.js": "^2.2.2",
|
||||
"svg.easing.js": "^2.0.0",
|
||||
|
@ -2262,9 +2262,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"astral-regex": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
|
||||
"integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
|
||||
"integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
|
||||
"dev": true
|
||||
},
|
||||
"async": {
|
||||
|
@ -4080,9 +4080,9 @@
|
|||
}
|
||||
},
|
||||
"dayjs": {
|
||||
"version": "1.9.7",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.9.7.tgz",
|
||||
"integrity": "sha512-IC877KBdMhBrCfBfJXHQlo0G8keZ0Opy7YIIq5QKtUbCuHMzim8S4PyiVK4YmihI3iOF9lhfUBW4AQWHTR5WHA=="
|
||||
"version": "1.9.8",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.9.8.tgz",
|
||||
"integrity": "sha512-F42qBtJRa30FKF7XDnOQyNUTsaxDkuaZRj/i7BejSHC34LlLfPoIU4aeopvWfM+m1dJ6/DHKAWLg2ur+pLgq1w=="
|
||||
},
|
||||
"de-indent": {
|
||||
"version": "1.0.2",
|
||||
|
@ -4748,9 +4748,9 @@
|
|||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
|
||||
},
|
||||
"eslint": {
|
||||
"version": "7.15.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.15.0.tgz",
|
||||
"integrity": "sha512-Vr64xFDT8w30wFll643e7cGrIkPEU50yIiI36OdSIDoSGguIeaLzBo0vpGvzo9RECUqq7htURfwEtKqwytkqzA==",
|
||||
"version": "7.16.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.16.0.tgz",
|
||||
"integrity": "sha512-iVWPS785RuDA4dWuhhgXTNrGxHHK3a8HLSMBgbbU59ruJDubUraXN8N5rn7kb8tG6sjg74eE0RA3YWT51eusEw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
|
@ -4787,7 +4787,7 @@
|
|||
"semver": "^7.2.1",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"strip-json-comments": "^3.1.0",
|
||||
"table": "^5.2.3",
|
||||
"table": "^6.0.4",
|
||||
"text-table": "^0.2.0",
|
||||
"v8-compile-cache": "^2.0.3"
|
||||
},
|
||||
|
@ -4886,9 +4886,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"import-fresh": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz",
|
||||
"integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==",
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"parent-module": "^1.0.0",
|
||||
|
@ -5001,24 +5001,24 @@
|
|||
}
|
||||
},
|
||||
"eslint-plugin-prettier": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.2.0.tgz",
|
||||
"integrity": "sha512-kOUSJnFjAUFKwVxuzy6sA5yyMx6+o9ino4gCdShzBNx4eyFRudWRYKCFolKjoM40PEiuU6Cn7wBLfq3WsGg7qg==",
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.0.tgz",
|
||||
"integrity": "sha512-tMTwO8iUWlSRZIwS9k7/E4vrTsfvsrcM5p1eftyuqWH25nKsz/o6/54I7jwQ/3zobISyC7wMy9ZsFwgTxOcOpQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"prettier-linter-helpers": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"eslint-plugin-vue": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-7.2.0.tgz",
|
||||
"integrity": "sha512-4mt0yIv6rBDNtvis/g22a0ozJ12GfcdEzX77u0ICYjKlxOVtGrKGEvo0cbOObHaKDg9a9kJcoaNodqE4TPfS2A==",
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-7.4.0.tgz",
|
||||
"integrity": "sha512-bYJV3nHSGV5IL40Ti1231vlY8I2DzjDHYyDjRv9Z1koEI7qyV2RR3+uKMafHdOioXYH9W3e1+iwe4wy7FIBNCQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"eslint-utils": "^2.1.0",
|
||||
"natural-compare": "^1.4.0",
|
||||
"semver": "^7.3.2",
|
||||
"vue-eslint-parser": "^7.2.0"
|
||||
"vue-eslint-parser": "^7.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"lru-cache": {
|
||||
|
@ -5798,9 +5798,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"fuse.js": {
|
||||
"version": "6.4.3",
|
||||
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.4.3.tgz",
|
||||
"integrity": "sha512-JNgngolukIrqwayWnvy6NLH63hmwKPhm63o0uyBg51jPD0j09IvAzlV1rTXfAsgxpghI7khAo6Mv+EmvjDWXig=="
|
||||
"version": "6.4.4",
|
||||
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.4.4.tgz",
|
||||
"integrity": "sha512-tw9+6zGBerQG207qz4r9TJRA4xhVWiB/4UTA8cw8b2X5v7t94YbgxZ0/9ey9X7268D28HKHlWn10Q0s+kHe+xw=="
|
||||
},
|
||||
"gauge": {
|
||||
"version": "2.7.4",
|
||||
|
@ -9810,9 +9810,9 @@
|
|||
}
|
||||
},
|
||||
"register-service-worker": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/register-service-worker/-/register-service-worker-1.7.1.tgz",
|
||||
"integrity": "sha512-IdTfUZ4u8iJL8o1w8es8l6UMGPmkwHolUdT+UmM1UypC80IB4KbpuIlvwWVj8UDS7eJwkEYRcKRgfRX+oTmJsw=="
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/register-service-worker/-/register-service-worker-1.7.2.tgz",
|
||||
"integrity": "sha512-CiD3ZSanZqcMPRhtfct5K9f7i3OLCcBBWsJjLh1gW9RO/nS94sVzY59iS+fgYBOBqaBpf4EzfqUF3j9IG+xo8A=="
|
||||
},
|
||||
"regjsgen": {
|
||||
"version": "0.5.1",
|
||||
|
@ -10127,9 +10127,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"sass": {
|
||||
"version": "1.30.0",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.30.0.tgz",
|
||||
"integrity": "sha512-26EUhOXRLaUY7+mWuRFqGeGGNmhB1vblpTENO1Z7mAzzIZeVxZr9EZoaY1kyGLFWdSOZxRMAufiN2mkbO6dAlw==",
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.32.0.tgz",
|
||||
"integrity": "sha512-fhyqEbMIycQA4blrz/C0pYhv2o4x2y6FYYAH0CshBw3DXh5D5wyERgxw0ptdau1orc/GhNrhF7DFN2etyOCEng==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chokidar": ">=2.0.0 <4.0.0"
|
||||
|
@ -10601,20 +10601,38 @@
|
|||
"dev": true
|
||||
},
|
||||
"slice-ansi": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
|
||||
"integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
|
||||
"integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.0",
|
||||
"astral-regex": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^2.0.0"
|
||||
"ansi-styles": "^4.0.0",
|
||||
"astral-regex": "^2.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
||||
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
|
@ -11325,47 +11343,27 @@
|
|||
}
|
||||
},
|
||||
"table": {
|
||||
"version": "5.4.6",
|
||||
"resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
|
||||
"integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==",
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/table/-/table-6.0.4.tgz",
|
||||
"integrity": "sha512-sBT4xRLdALd+NFBvwOz8bw4b15htyythha+q+DVZqy2RS08PPC8O2sZFgJYEY7bJvbCFKccs+WIZ/cd+xxTWCw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ajv": "^6.10.2",
|
||||
"lodash": "^4.17.14",
|
||||
"slice-ansi": "^2.1.0",
|
||||
"string-width": "^3.0.0"
|
||||
"ajv": "^6.12.4",
|
||||
"lodash": "^4.17.20",
|
||||
"slice-ansi": "^4.0.0",
|
||||
"string-width": "^4.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"emoji-regex": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
|
||||
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
|
||||
"dev": true
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
||||
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
|
||||
"dev": true
|
||||
},
|
||||
"string-width": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
|
||||
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
|
||||
"ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"emoji-regex": "^7.0.1",
|
||||
"is-fullwidth-code-point": "^2.0.0",
|
||||
"strip-ansi": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
|
||||
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^4.1.0"
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11951,9 +11949,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"uuid": {
|
||||
"version": "8.3.1",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz",
|
||||
"integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg=="
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
|
||||
},
|
||||
"v8-compile-cache": {
|
||||
"version": "2.2.0",
|
||||
|
@ -12010,9 +12008,9 @@
|
|||
"integrity": "sha512-sT6tuVTLBwfH3TA7azecDNS/W70bmz14ZJI7aE7QIqcG9I6OywyH7x3hcOeY1v1DxttI8Svc5RuYj4Dd+A5F4g=="
|
||||
},
|
||||
"vue-cli-plugin-vuetify": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/vue-cli-plugin-vuetify/-/vue-cli-plugin-vuetify-2.0.8.tgz",
|
||||
"integrity": "sha512-BHn9wwj/+B9v25mhZq2dV8NafM2LbogymjluPP+CjDnIdcwR3hW38r3nyKsZNPB1jXfWXsvVszipS3b8FqOBCg==",
|
||||
"version": "2.0.9",
|
||||
"resolved": "https://registry.npmjs.org/vue-cli-plugin-vuetify/-/vue-cli-plugin-vuetify-2.0.9.tgz",
|
||||
"integrity": "sha512-J4fzpz27OmCCAA3CI56ulYsUrZ859dQAh58Z9XZilY03kd/M+svLlPkK45cBIrGGfjSqQ40oyWezA3NiPBEG8g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"null-loader": "^3.0.0",
|
||||
|
@ -12063,9 +12061,9 @@
|
|||
}
|
||||
},
|
||||
"vue-eslint-parser": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.2.0.tgz",
|
||||
"integrity": "sha512-uVcQqe8sUNzdHGcRHMd2Z/hl6qEaWrAmglTKP92Fnq9TYU9un8xsyFgEdFJaXh/1rd7h8Aic1GaiQow5nVneow==",
|
||||
"version": "7.3.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.3.0.tgz",
|
||||
"integrity": "sha512-n5PJKZbyspD0+8LnaZgpEvNCrjQx1DyDHw8JdWwoxhhC+yRip4TAvSDpXGf9SWX6b0umeB5aR61gwUo6NVvFxw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"debug": "^4.1.1",
|
||||
|
@ -12217,9 +12215,9 @@
|
|||
}
|
||||
},
|
||||
"vuetify": {
|
||||
"version": "2.3.19",
|
||||
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.3.19.tgz",
|
||||
"integrity": "sha512-xqaiSEv0SUrzcoYU/N+orF7crRrhFkZt2ZPSDyPdHhiL6vMOFSYyz0OyGX3lAFykX/FJJMkNPgYTdg1l7WD1jA=="
|
||||
"version": "2.3.21",
|
||||
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.3.21.tgz",
|
||||
"integrity": "sha512-c9FOjkpVPDoIim88wbfqSIuCsH3jtgQQBC1iMW+ZFxf/Bj+d73HySL2LhEnZwAQT7XTAUGfad4aLPfcNZzK5YQ=="
|
||||
},
|
||||
"vuex": {
|
||||
"version": "3.6.0",
|
||||
|
|
24
package.json
24
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "vuetorrent",
|
||||
"version": "0.4.6",
|
||||
"version": "0.4.7",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "npm run serve",
|
||||
|
@ -10,14 +10,14 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "^7.12.1",
|
||||
"apexcharts": "^3.22.1",
|
||||
"apexcharts": "^3.23.1",
|
||||
"axios": "^0.19.2",
|
||||
"core-js": "^3.7.0",
|
||||
"dayjs": "^1.9.5",
|
||||
"fuse.js": "^6.4.3",
|
||||
"dayjs": "^1.9.8",
|
||||
"fuse.js": "^6.4.4",
|
||||
"lodash": "^4.17.20",
|
||||
"register-service-worker": "^1.7.1",
|
||||
"uuid": "^8.3.1",
|
||||
"register-service-worker": "^1.7.2",
|
||||
"uuid": "^8.3.2",
|
||||
"vue": "^2.6.12",
|
||||
"vue-apexcharts": "^1.6.0",
|
||||
"vue-context": "^5.2.0",
|
||||
|
@ -26,7 +26,7 @@
|
|||
"vue-toastification": "^1.7.8",
|
||||
"vue2-perfect-scrollbar": "^1.5.0",
|
||||
"vuedraggable": "^2.24.3",
|
||||
"vuetify": "^2.3.16",
|
||||
"vuetify": "2.3.21",
|
||||
"vuex": "^3.6.0",
|
||||
"vuex-persist": "^3.1.3"
|
||||
},
|
||||
|
@ -37,15 +37,15 @@
|
|||
"@vue/cli-service": "~4.3.0",
|
||||
"@vue/eslint-config-prettier": "^6.0.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^7.15.0",
|
||||
"eslint": "^7.16.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-plugin-prettier": "^3.2.0",
|
||||
"eslint-plugin-vue": "^7.2.0",
|
||||
"eslint-plugin-prettier": "^3.3.0",
|
||||
"eslint-plugin-vue": "^7.4.0",
|
||||
"fibers": "^5.0.0",
|
||||
"node-sass": "^4.14.1",
|
||||
"sass": "^1.30.0",
|
||||
"sass": "^1.32.0",
|
||||
"sass-loader": "^8.0.2",
|
||||
"vue-cli-plugin-vuetify": "^2.0.8",
|
||||
"vue-cli-plugin-vuetify": "^2.0.9",
|
||||
"vue-template-compiler": "^2.6.12"
|
||||
},
|
||||
"browserslist": [
|
||||
|
|
78
src/App.vue
78
src/App.vue
|
@ -1,16 +1,16 @@
|
|||
<template>
|
||||
<v-app :style="{ backgroundColor: background }">
|
||||
<component
|
||||
v-for="modal in modals"
|
||||
:key="modal.guid"
|
||||
:is="modal.component"
|
||||
v-bind="{ guid: modal.guid, ...modal.props }"
|
||||
/>
|
||||
<Navbar v-if="isAuthenticated" />
|
||||
<v-main fill-height fill-width>
|
||||
<router-view></router-view>
|
||||
</v-main>
|
||||
</v-app>
|
||||
<v-app :style="{ backgroundColor: background }">
|
||||
<component
|
||||
:is="modal.component"
|
||||
v-for="modal in modals"
|
||||
:key="modal.guid"
|
||||
v-bind="{ guid: modal.guid, ...modal.props }"
|
||||
/>
|
||||
<Navbar v-if="isAuthenticated" />
|
||||
<v-main fill-height fill-width>
|
||||
<router-view />
|
||||
</v-main>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -21,33 +21,33 @@ import qbit from '@/services/qbit'
|
|||
import { General } from '@/mixins'
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: { Navbar },
|
||||
mixins: [General],
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {
|
||||
async checkAuthenticated() {
|
||||
const res = await qbit.login()
|
||||
const authenticated = res === 'Ok.'
|
||||
this.$store.commit('LOGIN', authenticated)
|
||||
if (
|
||||
!authenticated &&
|
||||
!this.$router.currentRoute.name.includes('login')
|
||||
) this.$router.push('login')
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['rid', 'mainData', 'preferences', 'modals']),
|
||||
...mapGetters(['getAuthenticated']),
|
||||
isAuthenticated() {
|
||||
return this.getAuthenticated()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$store.commit('SET_APP_VERSION', version)
|
||||
this.checkAuthenticated()
|
||||
name: 'App',
|
||||
components: { Navbar },
|
||||
mixins: [General],
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['rid', 'mainData', 'preferences', 'modals']),
|
||||
...mapGetters(['getAuthenticated']),
|
||||
isAuthenticated() {
|
||||
return this.getAuthenticated()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$store.commit('SET_APP_VERSION', version)
|
||||
this.checkAuthenticated()
|
||||
},
|
||||
methods: {
|
||||
async checkAuthenticated() {
|
||||
const res = await qbit.login()
|
||||
const authenticated = res === 'Ok.'
|
||||
this.$store.commit('LOGIN', authenticated)
|
||||
if (
|
||||
!authenticated &&
|
||||
!this.$router.currentRoute.name.includes('login')
|
||||
) this.$router.push('login')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,129 +1,132 @@
|
|||
<template>
|
||||
<v-dialog max-width="500px" v-model="dialog">
|
||||
<v-card>
|
||||
<v-container :class="`pa-0 project done`">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>Add a new Torrent</h2>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-form v-model="valid" ref="form">
|
||||
<v-container>
|
||||
<v-row no-gutters>
|
||||
<v-col ref="fileZone">
|
||||
<div
|
||||
@drop.prevent="addDropFile"
|
||||
@dragover.prevent
|
||||
>
|
||||
<v-file-input
|
||||
v-if="!urls"
|
||||
v-model="files"
|
||||
color="deep-purple accent-4"
|
||||
counter
|
||||
label="File input"
|
||||
multiple
|
||||
placeholder="Select your files"
|
||||
prepend-icon="mdi-paperclip"
|
||||
:rules="fileInputRules"
|
||||
outlined
|
||||
:show-size="1000"
|
||||
>
|
||||
<template
|
||||
v-slot:selection="{ index, text }"
|
||||
>
|
||||
<v-chip
|
||||
v-if="index < 2"
|
||||
color="deep-purple accent-4"
|
||||
dark
|
||||
label
|
||||
small
|
||||
>{{ text }}</v-chip
|
||||
>
|
||||
<span
|
||||
v-else-if="index === 2"
|
||||
class="overline grey--text text--darken-3 mx-2"
|
||||
>+{{
|
||||
files.length - 2
|
||||
}}
|
||||
File(s)</span
|
||||
>
|
||||
</template>
|
||||
</v-file-input>
|
||||
</div>
|
||||
<v-textarea
|
||||
v-if="files.length == 0"
|
||||
label="URL"
|
||||
prepend-icon="mdi-link"
|
||||
rows="1"
|
||||
required
|
||||
:autofocus="!phoneLayout"
|
||||
v-model="urls"
|
||||
auto-grow
|
||||
clearable
|
||||
hint="One link per line"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-combobox
|
||||
v-model="category"
|
||||
:items="availableCategories"
|
||||
clearable
|
||||
label="Category"
|
||||
prepend-icon="tag"
|
||||
@input="categoryChanged"
|
||||
></v-combobox>
|
||||
|
||||
<v-text-field
|
||||
:disabled="autoTMM"
|
||||
v-model="directory"
|
||||
label="Download Directory"
|
||||
prepend-icon="folder"
|
||||
></v-text-field>
|
||||
<v-row no-gutters>
|
||||
<v-flex xs12 sm6>
|
||||
<v-checkbox
|
||||
v-model="start"
|
||||
label="Start torrent"
|
||||
></v-checkbox>
|
||||
</v-flex>
|
||||
<v-flex xs12 sm6>
|
||||
<v-checkbox
|
||||
v-model="skip_checking"
|
||||
label="Skip hash check"
|
||||
></v-checkbox>
|
||||
</v-flex>
|
||||
<v-flex xs12 sm6>
|
||||
<v-checkbox
|
||||
v-model="root_folder"
|
||||
label="Create subfolder"
|
||||
></v-checkbox>
|
||||
</v-flex>
|
||||
<v-flex xs12 sm6>
|
||||
<v-checkbox
|
||||
v-model="autoTMM"
|
||||
label="Automatic Torrent Management"
|
||||
></v-checkbox>
|
||||
</v-flex>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
|
||||
<v-spacer></v-spacer>
|
||||
<v-form>
|
||||
<v-card-actions class="justify-center">
|
||||
<v-btn
|
||||
text
|
||||
@click="submit"
|
||||
:disabled="!valid"
|
||||
class="blue_accent white--text mx-0 mt-3"
|
||||
>Add Torrent</v-btn
|
||||
<v-dialog v-model="dialog" max-width="500px">
|
||||
<v-card>
|
||||
<v-container :class="`pa-0 project done`">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>Add a new Torrent</h2>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-form ref="form" v-model="valid">
|
||||
<v-container>
|
||||
<v-row no-gutters>
|
||||
<v-col ref="fileZone">
|
||||
<div
|
||||
@drop.prevent="addDropFile"
|
||||
@dragover.prevent
|
||||
>
|
||||
<v-file-input
|
||||
v-if="!urls"
|
||||
v-model="files"
|
||||
color="deep-purple accent-4"
|
||||
counter
|
||||
label="File input"
|
||||
multiple
|
||||
placeholder="Select your files"
|
||||
prepend-icon="mdi-paperclip"
|
||||
:rules="fileInputRules"
|
||||
outlined
|
||||
:show-size="1000"
|
||||
>
|
||||
<template
|
||||
#selection="{ index, text }"
|
||||
>
|
||||
<v-chip
|
||||
v-if="index < 2"
|
||||
color="deep-purple accent-4"
|
||||
dark
|
||||
label
|
||||
small
|
||||
>
|
||||
</v-card-actions>
|
||||
</v-form>
|
||||
{{ text }}
|
||||
</v-chip>
|
||||
<span
|
||||
v-else-if="index === 2"
|
||||
class="overline grey--text text--darken-3 mx-2"
|
||||
>
|
||||
+{{
|
||||
files.length - 2
|
||||
}}
|
||||
File(s)
|
||||
</span>
|
||||
</template>
|
||||
</v-file-input>
|
||||
</div>
|
||||
<v-textarea
|
||||
v-if="files.length == 0"
|
||||
v-model="urls"
|
||||
label="URL"
|
||||
prepend-icon="mdi-link"
|
||||
rows="1"
|
||||
required
|
||||
:autofocus="!phoneLayout"
|
||||
auto-grow
|
||||
clearable
|
||||
hint="One link per line"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-combobox
|
||||
v-model="category"
|
||||
:items="availableCategories"
|
||||
clearable
|
||||
label="Category"
|
||||
prepend-icon="tag"
|
||||
@input="categoryChanged"
|
||||
/>
|
||||
|
||||
<v-text-field
|
||||
v-model="directory"
|
||||
:disabled="autoTMM"
|
||||
label="Download Directory"
|
||||
prepend-icon="folder"
|
||||
/>
|
||||
<v-row no-gutters>
|
||||
<v-flex xs12 sm6>
|
||||
<v-checkbox
|
||||
v-model="start"
|
||||
label="Start torrent"
|
||||
/>
|
||||
</v-flex>
|
||||
<v-flex xs12 sm6>
|
||||
<v-checkbox
|
||||
v-model="skip_checking"
|
||||
label="Skip hash check"
|
||||
/>
|
||||
</v-flex>
|
||||
<v-flex xs12 sm6>
|
||||
<v-checkbox
|
||||
v-model="root_folder"
|
||||
label="Create subfolder"
|
||||
/>
|
||||
</v-flex>
|
||||
<v-flex xs12 sm6>
|
||||
<v-checkbox
|
||||
v-model="autoTMM"
|
||||
label="Automatic Torrent Management"
|
||||
/>
|
||||
</v-flex>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
|
||||
<v-spacer />
|
||||
<v-form>
|
||||
<v-card-actions class="justify-center">
|
||||
<v-btn
|
||||
text
|
||||
:disabled="!valid"
|
||||
class="blue_accent white--text mx-0 mt-3"
|
||||
@click="submit"
|
||||
>
|
||||
Add Torrent
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-form>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -131,102 +134,104 @@ import { mapGetters } from 'vuex'
|
|||
import Modal from '@/mixins/Modal'
|
||||
import qbit from '@/services/qbit'
|
||||
export default {
|
||||
name: 'AddModal',
|
||||
props: ['initialMagnet'],
|
||||
mixins: [Modal],
|
||||
data() {
|
||||
return {
|
||||
files: [],
|
||||
category: null,
|
||||
directory: '',
|
||||
start: true,
|
||||
skip_checking: false,
|
||||
root_folder: true,
|
||||
autoTMM: true,
|
||||
fileInputRules: [
|
||||
v => {
|
||||
const result = v.every(f => {
|
||||
if (f.type) return f.type === 'application/x-bittorrent'
|
||||
else return /^.*\.torrent$/.test(f.name)
|
||||
})
|
||||
return result ? result : 'One or more files is not a valid torrent'
|
||||
}
|
||||
],
|
||||
loading: false,
|
||||
urls: null,
|
||||
valid: false
|
||||
name: 'AddModal',
|
||||
mixins: [Modal],
|
||||
props: ['initialMagnet'],
|
||||
data() {
|
||||
return {
|
||||
files: [],
|
||||
category: null,
|
||||
directory: '',
|
||||
start: true,
|
||||
skip_checking: false,
|
||||
root_folder: true,
|
||||
autoTMM: true,
|
||||
fileInputRules: [
|
||||
v => {
|
||||
const result = v.every(f => {
|
||||
if (f.type) return f.type === 'application/x-bittorrent'
|
||||
else return /^.*\.torrent$/.test(f.name)
|
||||
})
|
||||
|
||||
return result ? result : 'One or more files is not a valid torrent'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addDropFile(e) {
|
||||
this.files.push(...Array.from(e.dataTransfer.files))
|
||||
},
|
||||
submit() {
|
||||
if (this.files.length || this.urls) {
|
||||
const torrents = []
|
||||
const params = {
|
||||
urls: null,
|
||||
paused: !this.start,
|
||||
skip_checking: this.skip_checking,
|
||||
root_folder: this.root_folder,
|
||||
autoTMM: this.autoTMM
|
||||
}
|
||||
if (this.files.length) torrents.push(...this.files)
|
||||
if (this.urls) params.urls = this.urls
|
||||
if (this.category) params.category = this.category
|
||||
if (!this.autoTMM) params.savepath = this.directory
|
||||
|
||||
qbit.addTorrents(params, torrents)
|
||||
|
||||
this.resetForm()
|
||||
|
||||
this.$store.commit('DELETE_MODAL', this.guid)
|
||||
}
|
||||
},
|
||||
categoryChanged() {
|
||||
if (this.autoTMM) this.directory = this.savepath
|
||||
},
|
||||
resetForm() {
|
||||
this.url = null
|
||||
this.files = []
|
||||
this.category = null
|
||||
this.directory = this.savepath
|
||||
this.skip_checking = null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getSettings', 'getCategories']),
|
||||
settings() {
|
||||
return this.getSettings()
|
||||
},
|
||||
validFile() {
|
||||
return this.Files.length > 0
|
||||
},
|
||||
phoneLayout() {
|
||||
return this.$vuetify.breakpoint.xsOnly
|
||||
},
|
||||
savepath() {
|
||||
let savePath = this.getSettings().save_path
|
||||
if (this.category) {
|
||||
savePath += this.category
|
||||
const category = this.getCategories()[this.category]
|
||||
if (category && category.savePath) savePath = category.savePath
|
||||
}
|
||||
return savePath
|
||||
},
|
||||
availableCategories() {
|
||||
return Object.keys(this.getCategories())
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
settings(newvalue) {
|
||||
this.directory = newvalue.save_path
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$store.commit('FETCH_SETTINGS')
|
||||
this.$store.commit('FETCH_CATEGORIES')
|
||||
this.urls = this.initialMagnet
|
||||
],
|
||||
loading: false,
|
||||
urls: null,
|
||||
valid: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getSettings', 'getCategories']),
|
||||
settings() {
|
||||
return this.getSettings()
|
||||
},
|
||||
validFile() {
|
||||
return this.Files.length > 0
|
||||
},
|
||||
phoneLayout() {
|
||||
return this.$vuetify.breakpoint.xsOnly
|
||||
},
|
||||
savepath() {
|
||||
let savePath = this.getSettings().save_path
|
||||
if (this.category) {
|
||||
savePath += this.category
|
||||
const category = this.getCategories()[this.category]
|
||||
if (category && category.savePath) savePath = category.savePath
|
||||
}
|
||||
|
||||
return savePath
|
||||
},
|
||||
availableCategories() {
|
||||
return Object.keys(this.getCategories())
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
settings(newvalue) {
|
||||
this.directory = newvalue.save_path
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$store.commit('FETCH_SETTINGS')
|
||||
this.$store.commit('FETCH_CATEGORIES')
|
||||
this.urls = this.initialMagnet
|
||||
},
|
||||
methods: {
|
||||
addDropFile(e) {
|
||||
this.files.push(...Array.from(e.dataTransfer.files))
|
||||
},
|
||||
submit() {
|
||||
if (this.files.length || this.urls) {
|
||||
const torrents = []
|
||||
const params = {
|
||||
urls: null,
|
||||
paused: !this.start,
|
||||
skip_checking: this.skip_checking,
|
||||
root_folder: this.root_folder,
|
||||
autoTMM: this.autoTMM
|
||||
}
|
||||
if (this.files.length) torrents.push(...this.files)
|
||||
if (this.urls) params.urls = this.urls
|
||||
if (this.category) params.category = this.category
|
||||
if (!this.autoTMM) params.savepath = this.directory
|
||||
|
||||
qbit.addTorrents(params, torrents)
|
||||
|
||||
this.resetForm()
|
||||
|
||||
this.$store.commit('DELETE_MODAL', this.guid)
|
||||
}
|
||||
},
|
||||
categoryChanged() {
|
||||
if (this.autoTMM) this.directory = this.savepath
|
||||
},
|
||||
resetForm() {
|
||||
this.url = null
|
||||
this.files = []
|
||||
this.category = null
|
||||
this.directory = this.savepath
|
||||
this.skip_checking = null
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,50 +1,59 @@
|
|||
<template>
|
||||
<v-dialog
|
||||
v-model="dialog"
|
||||
scrollable
|
||||
:width="dialogWidth"
|
||||
:fullscreen="phoneLayout"
|
||||
>
|
||||
<v-card style="overflow: hidden !important">
|
||||
<v-container :style="{ height: phoneLayout ? '100vh' : '' }">
|
||||
<v-card-title class="pb-0 justify-center">
|
||||
<h2>Change Torrent Location</h2>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<div>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
label="Torrent Name"
|
||||
prepend-icon="insert_drive_file"
|
||||
readonly
|
||||
v-model="torrent.name"
|
||||
/>
|
||||
<v-text-field
|
||||
label="Directory"
|
||||
prepend-icon="folder"
|
||||
v-model="newPath"
|
||||
v-on:keydown.enter="setLocation"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</div>
|
||||
</v-card-text>
|
||||
<div>
|
||||
<v-card-actions class="justify-center">
|
||||
<v-btn color="success" @click="setLocation">Save</v-btn>
|
||||
</v-card-actions>
|
||||
</div>
|
||||
<v-dialog
|
||||
v-model="dialog"
|
||||
scrollable
|
||||
:width="dialogWidth"
|
||||
:fullscreen="phoneLayout"
|
||||
>
|
||||
<v-card style="overflow: hidden !important">
|
||||
<v-container :style="{ height: phoneLayout ? '100vh' : '' }">
|
||||
<v-card-title class="pb-0 justify-center">
|
||||
<h2>Change Torrent Location</h2>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<div>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
v-model="torrent.name"
|
||||
label="Torrent Name"
|
||||
prepend-icon="insert_drive_file"
|
||||
readonly
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="newPath"
|
||||
label="Directory"
|
||||
prepend-icon="folder"
|
||||
@keydown.enter="setLocation"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
<v-fab-transition v-if="phoneLayout">
|
||||
<v-btn @click="close" color="red" dark absolute bottom right>
|
||||
<v-icon>close</v-icon>
|
||||
</v-btn>
|
||||
</v-fab-transition>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</div>
|
||||
</v-card-text>
|
||||
<div>
|
||||
<v-card-actions class="justify-center">
|
||||
<v-btn color="success" @click="setLocation">
|
||||
Save
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</div>
|
||||
</v-container>
|
||||
<v-fab-transition v-if="phoneLayout">
|
||||
<v-btn
|
||||
color="red"
|
||||
dark
|
||||
absolute
|
||||
bottom
|
||||
right
|
||||
@click="close"
|
||||
>
|
||||
<v-icon>close</v-icon>
|
||||
</v-btn>
|
||||
</v-fab-transition>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -53,36 +62,36 @@ import { mapGetters } from 'vuex'
|
|||
import { Modal, FullScreenModal } from '@/mixins'
|
||||
import qbit from '@/services/qbit'
|
||||
export default {
|
||||
name: 'ChangeLocationModal',
|
||||
mixins: [Modal, FullScreenModal],
|
||||
props: {
|
||||
hash: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
newPath: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getTorrent']),
|
||||
dialogWidth() {
|
||||
return this.phoneLayout ? '100%' : '750px'
|
||||
},
|
||||
torrent() {
|
||||
return this.getTorrent(this.hash)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setLocation() {
|
||||
qbit.setTorrentLocation([this.hash], this.newPath)
|
||||
this.close()
|
||||
},
|
||||
close() {
|
||||
this.$store.commit('DELETE_MODAL', this.guid)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.newPath = this.torrent.savePath
|
||||
name: 'ChangeLocationModal',
|
||||
mixins: [Modal, FullScreenModal],
|
||||
props: {
|
||||
hash: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
newPath: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getTorrent']),
|
||||
dialogWidth() {
|
||||
return this.phoneLayout ? '100%' : '750px'
|
||||
},
|
||||
torrent() {
|
||||
return this.getTorrent(this.hash)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.newPath = this.torrent.savePath
|
||||
},
|
||||
methods: {
|
||||
setLocation() {
|
||||
qbit.setTorrentLocation([this.hash], this.newPath)
|
||||
this.close()
|
||||
},
|
||||
close() {
|
||||
this.$store.commit('DELETE_MODAL', this.guid)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,43 +1,48 @@
|
|||
<template>
|
||||
<v-dialog v-model="dialog" scrollable max-width="500px">
|
||||
<v-card>
|
||||
<v-container :class="`pa-0 project done`">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>Confirm Removal</h2>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-list flat>
|
||||
<v-list-item
|
||||
v-for="t in torrents"
|
||||
:key="t.hash"
|
||||
>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title class="truncate" v-text="t.name"></v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
<v-card-actions class="justify-center pb-5">
|
||||
<v-btn text class="error white--text mt-3"
|
||||
@click="close()"
|
||||
>Cancel</v-btn
|
||||
>
|
||||
<v-btn
|
||||
text
|
||||
class="green_accent white--text mt-3"
|
||||
@click="deleteWithoutFiles()"
|
||||
>Delete</v-btn
|
||||
>
|
||||
<v-btn
|
||||
text
|
||||
class="green_accent white--text mt-3"
|
||||
@click="deleteWithFiles()"
|
||||
>Delete with files</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<v-dialog v-model="dialog" scrollable max-width="500px">
|
||||
<v-card>
|
||||
<v-container :class="`pa-0 project done`">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>Confirm Removal</h2>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-list flat>
|
||||
<v-list-item
|
||||
v-for="t in torrents"
|
||||
:key="t.hash"
|
||||
>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title class="truncate" v-text="t.name" />
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
<v-card-actions class="justify-center pb-5">
|
||||
<v-btn
|
||||
text
|
||||
class="error white--text mt-3"
|
||||
@click="close()"
|
||||
>
|
||||
Cancel
|
||||
</v-btn>
|
||||
<v-btn
|
||||
text
|
||||
class="green_accent white--text mt-3"
|
||||
@click="deleteWithoutFiles()"
|
||||
>
|
||||
Delete
|
||||
</v-btn>
|
||||
<v-btn
|
||||
text
|
||||
class="green_accent white--text mt-3"
|
||||
@click="deleteWithFiles()"
|
||||
>
|
||||
Delete with files
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -45,27 +50,27 @@ import { mapState, mapGetters } from 'vuex'
|
|||
import { Modal } from '@/mixins'
|
||||
import qbit from '@/services/qbit'
|
||||
export default {
|
||||
name: 'ConfirmDeleteModal',
|
||||
mixins: [Modal],
|
||||
methods: {
|
||||
close() {
|
||||
this.$store.commit('DELETE_MODAL', this.guid)
|
||||
},
|
||||
deleteWithoutFiles() {
|
||||
qbit.deleteTorrents(this.selected_torrents, false)
|
||||
this.close()
|
||||
},
|
||||
deleteWithFiles() {
|
||||
qbit.deleteTorrents(this.selected_torrents, true)
|
||||
this.close()
|
||||
}
|
||||
name: 'ConfirmDeleteModal',
|
||||
mixins: [Modal],
|
||||
methods: {
|
||||
close() {
|
||||
this.$store.commit('DELETE_MODAL', this.guid)
|
||||
},
|
||||
computed: {
|
||||
...mapState(['selected_torrents']),
|
||||
...mapGetters(['getTorrents']),
|
||||
torrents() {
|
||||
return this.getTorrents().filter(t => this.selected_torrents.includes(t.hash))
|
||||
}
|
||||
...mapState(['selected_torrents']),
|
||||
...mapGetters(['getTorrents']),
|
||||
torrents() {
|
||||
return this.getTorrents().filter(t => this.selected_torrents.includes(t.hash))
|
||||
}
|
||||
},
|
||||
deleteWithoutFiles() {
|
||||
qbit.deleteTorrents(this.selected_torrents, false)
|
||||
this.close()
|
||||
},
|
||||
deleteWithFiles() {
|
||||
qbit.deleteTorrents(this.selected_torrents, true)
|
||||
this.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<template>
|
||||
<v-dialog
|
||||
v-model="dialog"
|
||||
scrollable
|
||||
:width="dialogWidth"
|
||||
:fullscreen="phoneLayout"
|
||||
v-model="dialog"
|
||||
scrollable
|
||||
:width="dialogWidth"
|
||||
:fullscreen="phoneLayout"
|
||||
>
|
||||
<v-card style="overflow: hidden !important">
|
||||
<v-container :style="{ height: phoneLayout ? '100vh' : '' }">
|
||||
|
@ -16,9 +16,9 @@
|
|||
<v-row>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
label="Torrent Name"
|
||||
prepend-icon="insert_drive_file"
|
||||
v-model="name"
|
||||
v-model="name"
|
||||
label="Torrent Name"
|
||||
prepend-icon="insert_drive_file"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
@ -27,12 +27,21 @@
|
|||
</v-card-text>
|
||||
<div>
|
||||
<v-card-actions class="justify-center">
|
||||
<v-btn color="success" @click="rename">Save</v-btn>
|
||||
<v-btn color="success" @click="rename">
|
||||
Save
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</div>
|
||||
</v-container>
|
||||
<v-fab-transition v-if="phoneLayout">
|
||||
<v-btn @click="close" color="red" dark absolute bottom right>
|
||||
<v-btn
|
||||
color="red"
|
||||
dark
|
||||
absolute
|
||||
bottom
|
||||
right
|
||||
@click="close"
|
||||
>
|
||||
<v-icon>close</v-icon>
|
||||
</v-btn>
|
||||
</v-fab-transition>
|
||||
|
@ -45,36 +54,36 @@ import { mapGetters } from 'vuex'
|
|||
import { Modal, FullScreenModal } from '@/mixins'
|
||||
import qbit from '@/services/qbit'
|
||||
export default {
|
||||
name: 'RenameModal',
|
||||
mixins: [Modal, FullScreenModal],
|
||||
props: {
|
||||
hash: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getTorrent']),
|
||||
dialogWidth() {
|
||||
return this.phoneLayout ? '100%' : '750px'
|
||||
},
|
||||
torrent() {
|
||||
return this.getTorrent(this.hash)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
rename() {
|
||||
qbit.setTorrentName(this.hash, this.name)
|
||||
this.close()
|
||||
},
|
||||
close() {
|
||||
this.$store.commit('DELETE_MODAL', this.guid)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.name = this.torrent.name
|
||||
name: 'RenameModal',
|
||||
mixins: [Modal, FullScreenModal],
|
||||
props: {
|
||||
hash: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getTorrent']),
|
||||
dialogWidth() {
|
||||
return this.phoneLayout ? '100%' : '750px'
|
||||
},
|
||||
torrent() {
|
||||
return this.getTorrent(this.hash)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.name = this.torrent.name
|
||||
},
|
||||
methods: {
|
||||
rename() {
|
||||
qbit.setTorrentName(this.hash, this.name)
|
||||
this.close()
|
||||
},
|
||||
close() {
|
||||
this.$store.commit('DELETE_MODAL', this.guid)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -5,36 +5,36 @@
|
|||
</v-btn>
|
||||
|
||||
<v-bottom-sheet
|
||||
scrollable
|
||||
inset
|
||||
v-model="opened"
|
||||
v-if="this.$vuetify.breakpoint.smAndDown"
|
||||
v-if="this.$vuetify.breakpoint.smAndDown"
|
||||
v-model="opened"
|
||||
scrollable
|
||||
inset
|
||||
>
|
||||
<v-sheet>
|
||||
<v-card>
|
||||
<v-card-title> <v-icon>mdi-toy-brick</v-icon> Plugin manager </v-card-title>
|
||||
<v-card-text>
|
||||
<v-switch
|
||||
v-for="(plugin, key) in searchPlugins"
|
||||
:key="key"
|
||||
:input-value="plugin.enabled"
|
||||
:label="plugin.fullName"
|
||||
@change="togglePlugin(plugin)"
|
||||
v-for="(plugin, key) in searchPlugins"
|
||||
:key="key"
|
||||
:input-value="plugin.enabled"
|
||||
:label="plugin.fullName"
|
||||
@change="togglePlugin(plugin)"
|
||||
/>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-sheet>
|
||||
</v-bottom-sheet>
|
||||
<v-dialog v-model="opened" width="50%" v-else>
|
||||
<v-dialog v-else v-model="opened" width="50%">
|
||||
<v-card>
|
||||
<v-card-title> <v-icon>mdi-toy-brick</v-icon> Plugin manager </v-card-title>
|
||||
<v-card-text>
|
||||
<v-switch
|
||||
v-for="(plugin, key) in searchPlugins"
|
||||
:key="key"
|
||||
v-model="plugin.enabled"
|
||||
:label="plugin.fullName"
|
||||
@change="togglePlugin(plugin)"
|
||||
v-for="(plugin, key) in searchPlugins"
|
||||
:key="key"
|
||||
v-model="plugin.enabled"
|
||||
:label="plugin.fullName"
|
||||
@change="togglePlugin(plugin)"
|
||||
/>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
@ -46,22 +46,22 @@
|
|||
import { mapState } from 'vuex'
|
||||
import qbit from '@/services/qbit'
|
||||
export default {
|
||||
name: 'PluginsManager',
|
||||
data: () =>({
|
||||
opened: false
|
||||
}),
|
||||
computed: {
|
||||
...mapState(['searchPlugins'])
|
||||
},
|
||||
methods: {
|
||||
togglePlugin(plugin) {
|
||||
qbit.enableSearchPlugin([plugin.name], plugin.enabled)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
opened() {
|
||||
this.$store.commit('FETCH_SEARCH_PLUGINS')
|
||||
}
|
||||
name: 'PluginsManager',
|
||||
data: () => ({
|
||||
opened: false
|
||||
}),
|
||||
computed: {
|
||||
...mapState(['searchPlugins'])
|
||||
},
|
||||
watch: {
|
||||
opened() {
|
||||
this.$store.commit('FETCH_SEARCH_PLUGINS')
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
togglePlugin(plugin) {
|
||||
qbit.enableSearchPlugin([plugin.name], plugin.enabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,78 +1,86 @@
|
|||
<template>
|
||||
<v-dialog
|
||||
v-model="dialog"
|
||||
scrollable
|
||||
:width="dialogWidth"
|
||||
:fullscreen="phoneLayout"
|
||||
:style="{ height: phoneLayout ? '100vh' : '' }"
|
||||
>
|
||||
<v-card :style="{ height: phoneLayout ? '100vh' : '' }">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>Search</h2>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-form
|
||||
ref="form"
|
||||
v-model="searchForm.valid"
|
||||
>
|
||||
<v-container fluid>
|
||||
<v-flex row class="col-12 col-sm-6 col-md-8 mx-auto">
|
||||
<v-text-field
|
||||
v-model="searchForm.pattern"
|
||||
prepend-inner-icon="mdi-magnify"
|
||||
@keypress.enter="$refs.searchButton.click"
|
||||
label="Search"
|
||||
:rules="[v => !!v || 'Searchterm is required']"
|
||||
clearable
|
||||
style="width: 70%"
|
||||
/>
|
||||
<v-spacer/>
|
||||
<v-btn
|
||||
ref="searchButton"
|
||||
:disabled="!searchForm.valid"
|
||||
:color="loading ? 'warning' : 'primary'"
|
||||
@click="loading ? stopSearch() : startSearch()"
|
||||
>
|
||||
{{ loading ? "Stop" : "Search"}}
|
||||
</v-btn>
|
||||
</v-flex>
|
||||
</v-container>
|
||||
</v-form>
|
||||
<perfect-scrollbar>
|
||||
<v-data-table
|
||||
:headers="grid.headers"
|
||||
:items="search.results"
|
||||
:items-per-page="10"
|
||||
:loading="loading"
|
||||
:style="{ maxHeight: '60vh'}"
|
||||
>
|
||||
<template #[`item.fileName`]="{ item }">
|
||||
<a
|
||||
:href="item.descrLink"
|
||||
target="_blank"
|
||||
v-text="item.fileName"
|
||||
/>
|
||||
</template>
|
||||
<template v-slot:[`item.fileSize`]="{ item }">
|
||||
{{ item.fileSize | formatSize }}
|
||||
</template>
|
||||
<template v-slot:[`item.actions`]="{ item }">
|
||||
<v-icon @click="downloadTorrent(item)">mdi-download</v-icon>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</perfect-scrollbar>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<PluginManager/>
|
||||
</v-card-actions>
|
||||
<v-fab-transition v-if="phoneLayout">
|
||||
<v-btn @click="close" color="red" dark absolute bottom right>
|
||||
<v-icon>close</v-icon>
|
||||
</v-btn>
|
||||
</v-fab-transition>
|
||||
</v-card>
|
||||
|
||||
</v-dialog>
|
||||
<v-dialog
|
||||
v-model="dialog"
|
||||
scrollable
|
||||
:width="dialogWidth"
|
||||
:fullscreen="phoneLayout"
|
||||
:style="{ height: phoneLayout ? '100vh' : '' }"
|
||||
>
|
||||
<v-card :style="{ height: phoneLayout ? '100vh' : '' }">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>Search</h2>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-form
|
||||
ref="form"
|
||||
v-model="searchForm.valid"
|
||||
>
|
||||
<v-container fluid>
|
||||
<v-flex row class="col-12 col-sm-6 col-md-8 mx-auto">
|
||||
<v-text-field
|
||||
v-model="searchForm.pattern"
|
||||
prepend-inner-icon="mdi-magnify"
|
||||
label="Search"
|
||||
:rules="[v => !!v || 'Searchterm is required']"
|
||||
clearable
|
||||
style="width: 70%"
|
||||
@keypress.enter="$refs.searchButton.click"
|
||||
/>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
ref="searchButton"
|
||||
:disabled="!searchForm.valid"
|
||||
:color="loading ? 'warning' : 'primary'"
|
||||
@click="loading ? stopSearch() : startSearch()"
|
||||
>
|
||||
{{ loading ? "Stop" : "Search" }}
|
||||
</v-btn>
|
||||
</v-flex>
|
||||
</v-container>
|
||||
</v-form>
|
||||
<perfect-scrollbar>
|
||||
<v-data-table
|
||||
:headers="grid.headers"
|
||||
:items="search.results"
|
||||
:items-per-page="10"
|
||||
:loading="loading"
|
||||
:style="{ maxHeight: '60vh'}"
|
||||
>
|
||||
<template #[`item.fileName`]="{ item }">
|
||||
<a
|
||||
:href="item.descrLink"
|
||||
target="_blank"
|
||||
v-text="item.fileName"
|
||||
/>
|
||||
</template>
|
||||
<template #[`item.fileSize`]="{ item }">
|
||||
{{ item.fileSize | formatSize }}
|
||||
</template>
|
||||
<template #[`item.actions`]="{ item }">
|
||||
<v-icon @click="downloadTorrent(item)">
|
||||
mdi-download
|
||||
</v-icon>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</perfect-scrollbar>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<PluginManager />
|
||||
</v-card-actions>
|
||||
<v-fab-transition v-if="phoneLayout">
|
||||
<v-btn
|
||||
color="red"
|
||||
dark
|
||||
absolute
|
||||
bottom
|
||||
right
|
||||
@click="close"
|
||||
>
|
||||
<v-icon>close</v-icon>
|
||||
</v-btn>
|
||||
</v-fab-transition>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -82,85 +90,86 @@ import { Modal, FullScreenModal, General } from '@/mixins'
|
|||
import PluginManager from './PluginManager'
|
||||
|
||||
export default {
|
||||
name: 'SearchModal',
|
||||
components: { PluginManager },
|
||||
mixins: [Modal, FullScreenModal, General],
|
||||
data() {
|
||||
return {
|
||||
search: {
|
||||
id: null,
|
||||
status: null,
|
||||
interval: null,
|
||||
results: []
|
||||
},
|
||||
loading: false,
|
||||
grid: {
|
||||
headers: [
|
||||
{ text: 'Name', value: 'fileName' },
|
||||
{ text: 'Size', value: 'fileSize' },
|
||||
{ text: 'Seeds', value: 'nbSeeders' },
|
||||
{ text: 'Peers', value: 'nbLeechers' },
|
||||
{ text: 'Search_engine', value: 'siteUrl' },
|
||||
{ text: 'Action', value: 'actions', sortable: false }
|
||||
]
|
||||
},
|
||||
searchForm: {
|
||||
valid: false,
|
||||
pattern: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getSearchPlugins']),
|
||||
dialogWidth() {
|
||||
return this.phoneLayout ? '100%' : '60%'
|
||||
},
|
||||
enabledSearchPlugins() {
|
||||
return this.getSearchPlugins().filter(p => p.enabled)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async startSearch() {
|
||||
if (this.searchForm.pattern.length && !this.search.interval) {
|
||||
this.loading = true
|
||||
this.search.status = 'Running'
|
||||
this.search.results = []
|
||||
const data = await qbit.startSearch(
|
||||
this.searchForm.pattern,
|
||||
this.enabledSearchPlugins.map(p => p.name)
|
||||
)
|
||||
this.search.id = data.id
|
||||
await this.getStatus()
|
||||
this.search.interval = setInterval(async () => {
|
||||
const status = await this.getStatus()
|
||||
if (status === 'Stopped') {
|
||||
clearInterval(this.search.interval)
|
||||
this.search.interval = null
|
||||
await this.getResults()
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
},
|
||||
async getStatus() {
|
||||
if (this.search.id) {
|
||||
const data = await qbit.getSearchStatus(this.search.id)
|
||||
return (this.search.status = data[0].status)
|
||||
}
|
||||
},
|
||||
async getResults() {
|
||||
const data = await qbit.getSearchResults(this.search.id)
|
||||
this.search.results = data.results
|
||||
this.loading = false
|
||||
},
|
||||
downloadTorrent(item) {
|
||||
this.createModal('addModal', { initialMagnet: item.fileUrl })
|
||||
},
|
||||
stopSearch() {
|
||||
qbit.stopSearch(this.search.id)
|
||||
},
|
||||
close() {
|
||||
this.$store.commit('DELETE_MODAL', this.guid)
|
||||
}
|
||||
name: 'SearchModal',
|
||||
components: { PluginManager },
|
||||
mixins: [Modal, FullScreenModal, General],
|
||||
data() {
|
||||
return {
|
||||
search: {
|
||||
id: null,
|
||||
status: null,
|
||||
interval: null,
|
||||
results: []
|
||||
},
|
||||
loading: false,
|
||||
grid: {
|
||||
headers: [
|
||||
{ text: 'Name', value: 'fileName' },
|
||||
{ text: 'Size', value: 'fileSize' },
|
||||
{ text: 'Seeds', value: 'nbSeeders' },
|
||||
{ text: 'Peers', value: 'nbLeechers' },
|
||||
{ text: 'Search_engine', value: 'siteUrl' },
|
||||
{ text: 'Action', value: 'actions', sortable: false }
|
||||
]
|
||||
},
|
||||
searchForm: {
|
||||
valid: false,
|
||||
pattern: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getSearchPlugins']),
|
||||
dialogWidth() {
|
||||
return this.phoneLayout ? '100%' : '60%'
|
||||
},
|
||||
enabledSearchPlugins() {
|
||||
return this.getSearchPlugins().filter(p => p.enabled)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async startSearch() {
|
||||
if (this.searchForm.pattern.length && !this.search.interval) {
|
||||
this.loading = true
|
||||
this.search.status = 'Running'
|
||||
this.search.results = []
|
||||
const data = await qbit.startSearch(
|
||||
this.searchForm.pattern,
|
||||
this.enabledSearchPlugins.map(p => p.name)
|
||||
)
|
||||
this.search.id = data.id
|
||||
await this.getStatus()
|
||||
this.search.interval = setInterval(async () => {
|
||||
const status = await this.getStatus()
|
||||
if (status === 'Stopped') {
|
||||
clearInterval(this.search.interval)
|
||||
this.search.interval = null
|
||||
await this.getResults()
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
},
|
||||
async getStatus() {
|
||||
if (this.search.id) {
|
||||
const data = await qbit.getSearchStatus(this.search.id)
|
||||
|
||||
return (this.search.status = data[0].status)
|
||||
}
|
||||
},
|
||||
async getResults() {
|
||||
const data = await qbit.getSearchResults(this.search.id)
|
||||
this.search.results = data.results
|
||||
this.loading = false
|
||||
},
|
||||
downloadTorrent(item) {
|
||||
this.createModal('addModal', { initialMagnet: item.fileUrl })
|
||||
},
|
||||
stopSearch() {
|
||||
qbit.stopSearch(this.search.id)
|
||||
},
|
||||
close() {
|
||||
this.$store.commit('DELETE_MODAL', this.guid)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,90 +1,109 @@
|
|||
<template>
|
||||
<v-dialog
|
||||
v-model="dialog"
|
||||
scrollable
|
||||
:width="dialogWidth"
|
||||
:fullscreen="phoneLayout"
|
||||
>
|
||||
<v-card style="min-height: 400px; overflow: hidden !important">
|
||||
<div :style="{ height: phoneLayout ? '100vh' : '' }">
|
||||
<v-card-title class="pb-0 justify-center primary">
|
||||
<h2 class="white--text">Settings</h2>
|
||||
</v-card-title>
|
||||
<v-tabs v-model="tab" background-color="primary" dark fixed-tabs>
|
||||
<v-tab href="#downloads">Downloads</v-tab>
|
||||
<v-tab href="#bittorrent">BitTorrent</v-tab>
|
||||
<v-tab href="#webui">WebUI</v-tab>
|
||||
<v-tab href="#vuetorrent">VueTorrent</v-tab>
|
||||
<v-tab href="#tagsAndCategories">Tags & Categories</v-tab>
|
||||
</v-tabs>
|
||||
<v-tabs-items v-model="tab" :touch="updateTab(tab)">
|
||||
<v-tab-item value="downloads">
|
||||
<Downloads :is-active="tab === 'downloads'" />
|
||||
</v-tab-item>
|
||||
<v-tab-item value="bittorrent">
|
||||
<BitTorrent :is-active="tab === 'bittorrent'" />
|
||||
</v-tab-item>
|
||||
<v-tab-item value="webui">
|
||||
<WebUI :is-active="tab === 'webui'" />
|
||||
</v-tab-item>
|
||||
<v-dialog
|
||||
v-model="dialog"
|
||||
scrollable
|
||||
:width="dialogWidth"
|
||||
:fullscreen="phoneLayout"
|
||||
>
|
||||
<v-card style="min-height: 400px; overflow: hidden !important">
|
||||
<div :style="{ height: phoneLayout ? '100vh' : '' }">
|
||||
<v-card-title class="pb-0 justify-center primary">
|
||||
<h2 class="white--text">
|
||||
Settings
|
||||
</h2>
|
||||
</v-card-title>
|
||||
<v-tabs
|
||||
v-model="tab"
|
||||
background-color="primary"
|
||||
dark
|
||||
fixed-tabs
|
||||
>
|
||||
<v-tab href="#downloads">
|
||||
Downloads
|
||||
</v-tab>
|
||||
<v-tab href="#bittorrent">
|
||||
BitTorrent
|
||||
</v-tab>
|
||||
<v-tab href="#webui">
|
||||
WebUI
|
||||
</v-tab>
|
||||
<v-tab href="#vuetorrent">
|
||||
VueTorrent
|
||||
</v-tab>
|
||||
<v-tab href="#tagsAndCategories">
|
||||
Tags & Categories
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
<v-tabs-items v-model="tab" :touch="updateTab(tab)">
|
||||
<v-tab-item value="downloads">
|
||||
<Downloads :is-active="tab === 'downloads'" />
|
||||
</v-tab-item>
|
||||
<v-tab-item value="bittorrent">
|
||||
<BitTorrent :is-active="tab === 'bittorrent'" />
|
||||
</v-tab-item>
|
||||
<v-tab-item value="webui">
|
||||
<WebUI :is-active="tab === 'webui'" />
|
||||
</v-tab-item>
|
||||
|
||||
<v-tab-item value="vuetorrent">
|
||||
<VueTorrent :is-active="tab === 'vuetorrent'" />
|
||||
</v-tab-item>
|
||||
<v-tab-item value="tagsAndCategories">
|
||||
<TagsAndCategories
|
||||
:is-active="tab === 'tagsAndCategories'"
|
||||
/>
|
||||
</v-tab-item>
|
||||
</v-tabs-items>
|
||||
</div>
|
||||
<v-card-actions class="d-flex justify-center">
|
||||
<v-btn color="success" @click="saveSettings">Save</v-btn>
|
||||
<v-fab-transition v-if="phoneLayout">
|
||||
<v-btn
|
||||
@click="close"
|
||||
color="red"
|
||||
dark
|
||||
absolute
|
||||
bottom
|
||||
right
|
||||
>
|
||||
<v-icon>close</v-icon>
|
||||
</v-btn>
|
||||
</v-fab-transition>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<v-tab-item value="vuetorrent">
|
||||
<VueTorrent :is-active="tab === 'vuetorrent'" />
|
||||
</v-tab-item>
|
||||
<v-tab-item value="tagsAndCategories">
|
||||
<TagsAndCategories
|
||||
:is-active="tab === 'tagsAndCategories'"
|
||||
/>
|
||||
</v-tab-item>
|
||||
</v-tabs-items>
|
||||
</div>
|
||||
<v-card-actions class="d-flex justify-center">
|
||||
<v-btn color="success" @click="saveSettings">
|
||||
Save
|
||||
</v-btn>
|
||||
<v-fab-transition v-if="phoneLayout">
|
||||
<v-btn
|
||||
color="red"
|
||||
dark
|
||||
absolute
|
||||
bottom
|
||||
right
|
||||
@click="close"
|
||||
>
|
||||
<v-icon>close</v-icon>
|
||||
</v-btn>
|
||||
</v-fab-transition>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Modal, FullScreenModal, SettingsTab } from '@/mixins'
|
||||
import {
|
||||
WebUI,
|
||||
BitTorrent,
|
||||
Downloads,
|
||||
VueTorrent,
|
||||
TagsAndCategories
|
||||
WebUI,
|
||||
BitTorrent,
|
||||
Downloads,
|
||||
VueTorrent,
|
||||
TagsAndCategories
|
||||
} from './Tabs'
|
||||
|
||||
export default {
|
||||
name: 'SettingsModal',
|
||||
mixins: [Modal, FullScreenModal, SettingsTab],
|
||||
components: { WebUI, BitTorrent, Downloads, VueTorrent, TagsAndCategories },
|
||||
data() {
|
||||
return {
|
||||
tab: null,
|
||||
items: [],
|
||||
peers: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.deleteModal()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$store.commit('FETCH_SETTINGS')
|
||||
name: 'SettingsModal',
|
||||
components: { WebUI, BitTorrent, Downloads, VueTorrent, TagsAndCategories },
|
||||
mixins: [Modal, FullScreenModal, SettingsTab],
|
||||
data() {
|
||||
return {
|
||||
tab: null,
|
||||
items: [],
|
||||
peers: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$store.commit('FETCH_SETTINGS')
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.deleteModal()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,156 +1,156 @@
|
|||
<template>
|
||||
<v-container>
|
||||
<v-card flat>
|
||||
<perfect-scrollbar>
|
||||
<v-card-text :style="{ minHeight: phoneLayout ? '' : '70vh', maxHeight: '70vh'}">
|
||||
<h3>Privacy</h3>
|
||||
<div class="settings_content ml-5 mr-5">
|
||||
<v-checkbox
|
||||
dense
|
||||
:label="`Enable DHT (decentralized network) to find more peers`"
|
||||
v-model="settings.dht"
|
||||
/>
|
||||
<v-checkbox
|
||||
dense
|
||||
:label="`Enable Peer Exchange (PeX) to find more peers`"
|
||||
v-model="settings.pex"
|
||||
/>
|
||||
<v-checkbox
|
||||
dense
|
||||
:label="`Enable Local Peer Discovery to find more peers`"
|
||||
v-model="settings.lsd"
|
||||
/>
|
||||
<v-checkbox
|
||||
dense
|
||||
:label="`Enable anonymous mode`"
|
||||
v-model="settings.anonymous_mode"
|
||||
/>
|
||||
</div>
|
||||
<v-card flat>
|
||||
<perfect-scrollbar>
|
||||
<v-card-text :style="{ minHeight: phoneLayout ? '' : '70vh', maxHeight: '70vh'}">
|
||||
<h3>Privacy</h3>
|
||||
<div class="settings_content ml-5 mr-5">
|
||||
<v-checkbox
|
||||
v-model="settings.dht"
|
||||
dense
|
||||
:label="`Enable DHT (decentralized network) to find more peers`"
|
||||
/>
|
||||
<v-checkbox
|
||||
v-model="settings.pex"
|
||||
dense
|
||||
:label="`Enable Peer Exchange (PeX) to find more peers`"
|
||||
/>
|
||||
<v-checkbox
|
||||
v-model="settings.lsd"
|
||||
dense
|
||||
:label="`Enable Local Peer Discovery to find more peers`"
|
||||
/>
|
||||
<v-checkbox
|
||||
v-model="settings.anonymous_mode"
|
||||
dense
|
||||
:label="`Enable anonymous mode`"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<v-checkbox
|
||||
v-model="settings.queueing_enabled"
|
||||
dense
|
||||
:label="`Torrent Queueing`"
|
||||
/>
|
||||
<div class="settings_content ml-5 mr-5">
|
||||
<v-text-field
|
||||
v-model="settings.max_active_downloads"
|
||||
class="mb-2"
|
||||
outlined
|
||||
dense
|
||||
hide-details
|
||||
:label="`Maximum active downloads`"
|
||||
:disabled="!settings.queueing_enabled"
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="settings.max_active_uploads"
|
||||
class="mb-2"
|
||||
outlined
|
||||
dense
|
||||
hide-details
|
||||
:label="`Maximum active uploads`"
|
||||
:disabled="!settings.queueing_enabled"
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="settings.max_active_torrents"
|
||||
class="mb-2"
|
||||
outlined
|
||||
dense
|
||||
hide-details
|
||||
:label="`Maximum active torrents`"
|
||||
:disabled="!settings.queueing_enabled"
|
||||
/>
|
||||
|
||||
<v-checkbox
|
||||
v-model="settings.dont_count_slow_torrents"
|
||||
dense
|
||||
:label="`Do not count slow torrents in these limits`"
|
||||
/>
|
||||
|
||||
<div class="settings_content">
|
||||
<v-text-field
|
||||
v-model="settings.slow_torrent_dl_rate_threshold"
|
||||
class="mb-2"
|
||||
outlined
|
||||
dense
|
||||
hide-details
|
||||
:label="`Download rate threshold KiB/s`"
|
||||
:disabled="!settings.dont_count_slow_torrents"
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="settings.slow_torrent_ul_rate_threshold"
|
||||
class="mb-2"
|
||||
outlined
|
||||
dense
|
||||
hide-details
|
||||
:label="`Upload rate threshold KiB/s`"
|
||||
:disabled="!settings.dont_count_slow_torrents"
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="settings.slow_torrent_inactive_timer"
|
||||
class="mb-2"
|
||||
outlined
|
||||
dense
|
||||
hide-details
|
||||
:label="`Torrent inactivity timer`"
|
||||
:disabled="!settings.dont_count_slow_torrents"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>Seeding Limits</h3>
|
||||
<div class="settings_content ml-5 mr-5">
|
||||
<v-row dense>
|
||||
<v-col cols="10">
|
||||
<v-checkbox
|
||||
dense
|
||||
:label="`Torrent Queueing`"
|
||||
v-model="settings.queueing_enabled"
|
||||
v-model="settings.max_ratio_enabled"
|
||||
dense
|
||||
:label="`When ratio reaches`"
|
||||
/>
|
||||
<div class="settings_content ml-5 mr-5">
|
||||
<v-text-field
|
||||
class="mb-2"
|
||||
outlined
|
||||
dense
|
||||
hide-details
|
||||
:label="`Maximum active downloads`"
|
||||
:disabled="!settings.queueing_enabled"
|
||||
v-model="settings.max_active_downloads"
|
||||
/>
|
||||
<v-text-field
|
||||
class="mb-2"
|
||||
outlined
|
||||
dense
|
||||
hide-details
|
||||
:label="`Maximum active uploads`"
|
||||
:disabled="!settings.queueing_enabled"
|
||||
v-model="settings.max_active_uploads"
|
||||
/>
|
||||
<v-text-field
|
||||
class="mb-2"
|
||||
outlined
|
||||
dense
|
||||
hide-details
|
||||
:label="`Maximum active torrents`"
|
||||
:disabled="!settings.queueing_enabled"
|
||||
v-model="settings.max_active_torrents"
|
||||
/>
|
||||
|
||||
<v-checkbox
|
||||
dense
|
||||
:label="`Do not count slow torrents in these limits`"
|
||||
v-model="settings.dont_count_slow_torrents"
|
||||
/>
|
||||
|
||||
<div class="settings_content">
|
||||
<v-text-field
|
||||
class="mb-2"
|
||||
outlined
|
||||
dense
|
||||
hide-details
|
||||
:label="`Download rate threshold KiB/s`"
|
||||
:disabled="!settings.dont_count_slow_torrents"
|
||||
v-model="settings.slow_torrent_dl_rate_threshold"
|
||||
/>
|
||||
<v-text-field
|
||||
class="mb-2"
|
||||
outlined
|
||||
dense
|
||||
hide-details
|
||||
:label="`Upload rate threshold KiB/s`"
|
||||
:disabled="!settings.dont_count_slow_torrents"
|
||||
v-model="settings.slow_torrent_ul_rate_threshold"
|
||||
/>
|
||||
<v-text-field
|
||||
class="mb-2"
|
||||
outlined
|
||||
dense
|
||||
hide-details
|
||||
:label="`Torrent inactivity timer`"
|
||||
:disabled="!settings.dont_count_slow_torrents"
|
||||
v-model="settings.slow_torrent_inactive_timer"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>Seeding Limits</h3>
|
||||
<div class="settings_content ml-5 mr-5">
|
||||
<v-row dense>
|
||||
<v-col cols="10">
|
||||
<v-checkbox
|
||||
dense
|
||||
:label="`When ratio reaches`"
|
||||
v-model="settings.max_ratio_enabled"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
class="mb-2"
|
||||
outlined
|
||||
dense
|
||||
hide-details
|
||||
:disabled="!settings.max_ratio_enabled"
|
||||
v-model="settings.max_ratio"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row dense>
|
||||
<v-col cols="10">
|
||||
<v-checkbox
|
||||
dense
|
||||
label="When seeding time reaches"
|
||||
v-model="settings.max_seeding_time_enabled"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
class="mb-2"
|
||||
outlined
|
||||
dense
|
||||
hide-details
|
||||
:disabled="!settings.max_seeding_time_enabled"
|
||||
v-model="settings.max_seeding_time"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</perfect-scrollbar>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
v-model="settings.max_ratio"
|
||||
class="mb-2"
|
||||
outlined
|
||||
dense
|
||||
hide-details
|
||||
:disabled="!settings.max_ratio_enabled"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row dense>
|
||||
<v-col cols="10">
|
||||
<v-checkbox
|
||||
v-model="settings.max_seeding_time_enabled"
|
||||
dense
|
||||
label="When seeding time reaches"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
v-model="settings.max_seeding_time"
|
||||
class="mb-2"
|
||||
outlined
|
||||
dense
|
||||
hide-details
|
||||
:disabled="!settings.max_seeding_time_enabled"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</perfect-scrollbar>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { SettingsTab, FullScreenModal } from '@/mixins'
|
||||
|
||||
export default {
|
||||
name: 'BitTorrent',
|
||||
mixins: [SettingsTab, FullScreenModal]
|
||||
name: 'BitTorrent',
|
||||
mixins: [SettingsTab, FullScreenModal]
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,79 +1,79 @@
|
|||
<template>
|
||||
<v-container>
|
||||
<v-card flat>
|
||||
<v-card-text :style="{ minHeight: phoneLayout ? '' : '70vh'}">
|
||||
<h3>When adding a torrent</h3>
|
||||
<div class="settings_content ml-5 mr-5">
|
||||
<v-checkbox
|
||||
dense
|
||||
:label="`Create subfolder for torrents with multiple files`"
|
||||
v-model="settings.create_subfolder_enabled"
|
||||
/>
|
||||
<v-checkbox
|
||||
dense
|
||||
:label="`Do not start the download automatically`"
|
||||
v-model="settings.start_paused_enabled"
|
||||
/>
|
||||
</div>
|
||||
<v-card flat>
|
||||
<v-card-text :style="{ minHeight: phoneLayout ? '' : '70vh'}">
|
||||
<h3>When adding a torrent</h3>
|
||||
<div class="settings_content ml-5 mr-5">
|
||||
<v-checkbox
|
||||
v-model="settings.create_subfolder_enabled"
|
||||
dense
|
||||
:label="`Create subfolder for torrents with multiple files`"
|
||||
/>
|
||||
<v-checkbox
|
||||
v-model="settings.start_paused_enabled"
|
||||
dense
|
||||
:label="`Do not start the download automatically`"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<v-checkbox
|
||||
dense
|
||||
:label="`Pre-allocate disk space for all files`"
|
||||
v-model="settings.preallocate_all"
|
||||
/>
|
||||
<v-checkbox
|
||||
dense
|
||||
:label="` Append .!qB extension to incomplete files`"
|
||||
v-model="settings.incomplete_files_ext"
|
||||
/>
|
||||
<v-checkbox
|
||||
v-model="settings.preallocate_all"
|
||||
dense
|
||||
:label="`Pre-allocate disk space for all files`"
|
||||
/>
|
||||
<v-checkbox
|
||||
v-model="settings.incomplete_files_ext"
|
||||
dense
|
||||
:label="` Append .!qB extension to incomplete files`"
|
||||
/>
|
||||
|
||||
<h3>Saving Management</h3>
|
||||
<div class="settings_content ml-5 mr-5">
|
||||
<v-row dense>
|
||||
<v-col cols="5" class="d-flex align-center">
|
||||
<h4>Default Save Path</h4>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
class="mb-2"
|
||||
outlined
|
||||
dense
|
||||
hide-details
|
||||
v-model="settings.save_path"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row dense>
|
||||
<v-col cols="5">
|
||||
<v-checkbox
|
||||
dense
|
||||
:label="`Keep incomplete torrents in:`"
|
||||
v-model="settings.temp_path_enabled"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
class="mb-2"
|
||||
outlined
|
||||
dense
|
||||
hide-details
|
||||
:disabled="!settings.temp_path_enabled"
|
||||
v-model="settings.temp_path"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-container>
|
||||
<h3>Saving Management</h3>
|
||||
<div class="settings_content ml-5 mr-5">
|
||||
<v-row dense>
|
||||
<v-col cols="5" class="d-flex align-center">
|
||||
<h4>Default Save Path</h4>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
v-model="settings.save_path"
|
||||
class="mb-2"
|
||||
outlined
|
||||
dense
|
||||
hide-details
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row dense>
|
||||
<v-col cols="5">
|
||||
<v-checkbox
|
||||
v-model="settings.temp_path_enabled"
|
||||
dense
|
||||
:label="`Keep incomplete torrents in:`"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
v-model="settings.temp_path"
|
||||
class="mb-2"
|
||||
outlined
|
||||
dense
|
||||
hide-details
|
||||
:disabled="!settings.temp_path_enabled"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { FullScreenModal, SettingsTab } from '@/mixins'
|
||||
|
||||
export default {
|
||||
name: 'Downloads',
|
||||
mixins: [SettingsTab, FullScreenModal]
|
||||
name: 'Downloads',
|
||||
mixins: [SettingsTab, FullScreenModal]
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,73 +1,80 @@
|
|||
<template>
|
||||
<v-container>
|
||||
<v-card flat>
|
||||
<v-card-text
|
||||
class="mx-auto mt-5"
|
||||
style="font-size: 1.1em;"
|
||||
:style="{ minHeight: phoneLayout ? '' : '64vh'}"
|
||||
>
|
||||
<v-layout row wrap>
|
||||
<v-flex class="mx-auto text-center" xs12 md6>
|
||||
<div>
|
||||
<h3>Available Tags:</h3>
|
||||
</div>
|
||||
<div class="mt-3 d-flex flex-wrap justify-center">
|
||||
<v-chip
|
||||
v-for="tag in availableTags"
|
||||
:key="tag"
|
||||
small
|
||||
class="download white--text caption mx-2 my-1"
|
||||
style="font-size: 0.95em !important"
|
||||
>
|
||||
{{ tag }}
|
||||
</v-chip>
|
||||
</div>
|
||||
<v-card-actions class="justify-center pb-5">
|
||||
<v-btn text class="error white--text mt-3" @click="deleteTag"
|
||||
>Delete</v-btn
|
||||
>
|
||||
<v-btn
|
||||
text
|
||||
class="green_accent white--text mt-3"
|
||||
@click="createTag"
|
||||
>Create new</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
</v-flex>
|
||||
<v-flex class="mx-auto text-center" xs12 md6>
|
||||
<div>
|
||||
<h3>Available Categories:</h3>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap mt-3 justify-center" xs12 sm12>
|
||||
<v-chip
|
||||
v-for="cat in availableCategories"
|
||||
:key="cat.name"
|
||||
small
|
||||
class="upload white--text caption mx-2 my-1"
|
||||
style="font-size: 0.95em !important"
|
||||
@click="editCategory(cat)"
|
||||
@click:close="editCategory(cat)"
|
||||
>
|
||||
{{ cat.name }}
|
||||
</v-chip>
|
||||
</div>
|
||||
<v-card-actions class="justify-center pb-5">
|
||||
<v-btn
|
||||
text
|
||||
class="error white--text mt-3"
|
||||
@click="deleteCategory"
|
||||
>Delete</v-btn
|
||||
>
|
||||
<v-btn
|
||||
text
|
||||
class="green_accent white--text mt-3"
|
||||
@click="createCategory"
|
||||
>Create new</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-card-text>
|
||||
<v-card-text
|
||||
class="mx-auto mt-5"
|
||||
style="font-size: 1.1em;"
|
||||
:style="{ minHeight: phoneLayout ? '' : '64vh'}"
|
||||
>
|
||||
<v-layout row wrap>
|
||||
<v-flex class="mx-auto text-center" xs12 md6>
|
||||
<div>
|
||||
<h3>Available Tags:</h3>
|
||||
</div>
|
||||
<div class="mt-3 d-flex flex-wrap justify-center">
|
||||
<v-chip
|
||||
v-for="tag in availableTags"
|
||||
:key="tag"
|
||||
small
|
||||
class="download white--text caption mx-2 my-1"
|
||||
style="font-size: 0.95em !important"
|
||||
>
|
||||
{{ tag }}
|
||||
</v-chip>
|
||||
</div>
|
||||
<v-card-actions class="justify-center pb-5">
|
||||
<v-btn
|
||||
text
|
||||
class="error white--text mt-3"
|
||||
@click="deleteTag"
|
||||
>
|
||||
Delete
|
||||
</v-btn>
|
||||
<v-btn
|
||||
text
|
||||
class="green_accent white--text mt-3"
|
||||
@click="createTag"
|
||||
>
|
||||
Create new
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-flex>
|
||||
<v-flex class="mx-auto text-center" xs12 md6>
|
||||
<div>
|
||||
<h3>Available Categories:</h3>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap mt-3 justify-center" xs12 sm12>
|
||||
<v-chip
|
||||
v-for="cat in availableCategories"
|
||||
:key="cat.name"
|
||||
small
|
||||
class="upload white--text caption mx-2 my-1"
|
||||
style="font-size: 0.95em !important"
|
||||
@click="editCategory(cat)"
|
||||
@click:close="editCategory(cat)"
|
||||
>
|
||||
{{ cat.name }}
|
||||
</v-chip>
|
||||
</div>
|
||||
<v-card-actions class="justify-center pb-5">
|
||||
<v-btn
|
||||
text
|
||||
class="error white--text mt-3"
|
||||
@click="deleteCategory"
|
||||
>
|
||||
Delete
|
||||
</v-btn>
|
||||
<v-btn
|
||||
text
|
||||
class="green_accent white--text mt-3"
|
||||
@click="createCategory"
|
||||
>
|
||||
Create new
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
|
@ -80,51 +87,51 @@ import qbit from '@/services/qbit'
|
|||
import { Tab, General, FullScreenModal } from '@/mixins'
|
||||
|
||||
export default {
|
||||
name: 'TagsAndCategories',
|
||||
mixins: [Tab, General, FullScreenModal],
|
||||
props: {
|
||||
hash: String
|
||||
},
|
||||
data: () => ({
|
||||
selectedCategory: null
|
||||
}),
|
||||
computed: {
|
||||
...mapGetters(['getTorrent', 'getAvailableTags', 'getCategories']),
|
||||
name: 'TagsAndCategories',
|
||||
mixins: [Tab, General, FullScreenModal],
|
||||
props: {
|
||||
hash: String
|
||||
},
|
||||
data: () => ({
|
||||
selectedCategory: null
|
||||
}),
|
||||
computed: {
|
||||
...mapGetters(['getTorrent', 'getAvailableTags', 'getCategories']),
|
||||
|
||||
availableTags() {
|
||||
return this.getAvailableTags()
|
||||
},
|
||||
availableCategories() {
|
||||
return this.getCategories()
|
||||
}
|
||||
availableTags() {
|
||||
return this.getAvailableTags()
|
||||
},
|
||||
methods: {
|
||||
activeMethod() {
|
||||
this.fetchCategories()
|
||||
},
|
||||
async fetchCategories() {
|
||||
const { data } = await qbit.getCategories()
|
||||
this.categories = data
|
||||
},
|
||||
deleteTag() {
|
||||
this.createModal('DeleteTagDialog')
|
||||
},
|
||||
createTag() {
|
||||
this.createModal('CreateTagDialog')
|
||||
},
|
||||
createCategory() {
|
||||
this.createModal('CreateCategoryDialog')
|
||||
},
|
||||
deleteCategory() {
|
||||
this.createModal('DeleteCategoryDialog')
|
||||
},
|
||||
editCategory(cat) {
|
||||
this.createModal('CreateCategoryDialog', { initialCategory: cat })
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$store.commit('FETCH_CATEGORIES')
|
||||
availableCategories() {
|
||||
return this.getCategories()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$store.commit('FETCH_CATEGORIES')
|
||||
},
|
||||
methods: {
|
||||
activeMethod() {
|
||||
this.fetchCategories()
|
||||
},
|
||||
async fetchCategories() {
|
||||
const { data } = await qbit.getCategories()
|
||||
this.categories = data
|
||||
},
|
||||
deleteTag() {
|
||||
this.createModal('DeleteTagDialog')
|
||||
},
|
||||
createTag() {
|
||||
this.createModal('CreateTagDialog')
|
||||
},
|
||||
createCategory() {
|
||||
this.createModal('CreateCategoryDialog')
|
||||
},
|
||||
deleteCategory() {
|
||||
this.createModal('DeleteCategoryDialog')
|
||||
},
|
||||
editCategory(cat) {
|
||||
this.createModal('CreateCategoryDialog', { initialCategory: cat })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,25 +1,29 @@
|
|||
<template>
|
||||
<v-card flat>
|
||||
<perfect-scrollbar>
|
||||
<v-card-text :style="{ minHeight: phoneLayout ? '' : '70vh', maxHeight: '70vh'}">
|
||||
<v-tabs v-model="tab">
|
||||
<v-tab href="#general">General</v-tab>
|
||||
<v-tab href="#dashboard">Dashboard</v-tab>
|
||||
</v-tabs>
|
||||
<v-tabs-items
|
||||
v-model="tab"
|
||||
:touch="updateTab(tab)"
|
||||
>
|
||||
<v-tab-item value="general">
|
||||
<General :is-active="tab === 'downloads'" />
|
||||
</v-tab-item>
|
||||
<v-tab-item value="dashboard">
|
||||
<Dashboard :is-active="tab === 'bittorrent'" />
|
||||
</v-tab-item>
|
||||
</v-tabs-items>
|
||||
</v-card-text>
|
||||
</perfect-scrollbar>
|
||||
</v-card>
|
||||
<v-card flat>
|
||||
<perfect-scrollbar>
|
||||
<v-card-text :style="{ minHeight: phoneLayout ? '' : '70vh', maxHeight: '70vh'}">
|
||||
<v-tabs v-model="tab">
|
||||
<v-tab href="#general">
|
||||
General
|
||||
</v-tab>
|
||||
<v-tab href="#dashboard">
|
||||
Dashboard
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
<v-tabs-items
|
||||
v-model="tab"
|
||||
:touch="updateTab(tab)"
|
||||
>
|
||||
<v-tab-item value="general">
|
||||
<General :is-active="tab === 'downloads'" />
|
||||
</v-tab-item>
|
||||
<v-tab-item value="dashboard">
|
||||
<Dashboard :is-active="tab === 'bittorrent'" />
|
||||
</v-tab-item>
|
||||
</v-tabs-items>
|
||||
</v-card-text>
|
||||
</perfect-scrollbar>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -28,18 +32,18 @@ import Dashboard from './Vuetorrent/Dashboard'
|
|||
import { FullScreenModal } from '@/mixins'
|
||||
|
||||
export default {
|
||||
name: 'VueTorrent',
|
||||
components: {
|
||||
General, Dashboard
|
||||
},
|
||||
mixins: [FullScreenModal],
|
||||
data: () => ({
|
||||
tab: null
|
||||
}),
|
||||
methods: {
|
||||
updateTab(tab) {
|
||||
this.tab = tab
|
||||
}
|
||||
name: 'VueTorrent',
|
||||
components: {
|
||||
General, Dashboard
|
||||
},
|
||||
mixins: [FullScreenModal],
|
||||
data: () => ({
|
||||
tab: null
|
||||
}),
|
||||
methods: {
|
||||
updateTab(tab) {
|
||||
this.tab = tab
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -2,55 +2,65 @@
|
|||
<div class="ma-3 pa-2">
|
||||
<v-card flat>
|
||||
<v-card-text class="pa-0">
|
||||
<v-layout row wrap align-center justify-center>
|
||||
<v-flex class="box ma-1" style="width:50px !important;" xs12 md5>
|
||||
<v-subheader>
|
||||
Properties to display for busy torrents
|
||||
</v-subheader>
|
||||
<v-row dense >
|
||||
<v-list flat class="ma-0 pa-0">
|
||||
<draggable
|
||||
<v-layout
|
||||
row
|
||||
wrap
|
||||
align-center
|
||||
justify-center
|
||||
>
|
||||
<v-flex
|
||||
class="box ma-1"
|
||||
style="width:50px !important;"
|
||||
xs12
|
||||
md5
|
||||
>
|
||||
<v-subheader>
|
||||
Properties to display for busy torrents
|
||||
</v-subheader>
|
||||
<v-row dense>
|
||||
<v-list flat class="ma-0 pa-0">
|
||||
<draggable
|
||||
:list="busyTorrentProperties"
|
||||
tag="tbody"
|
||||
>
|
||||
<v-list-item
|
||||
>
|
||||
<v-list-item
|
||||
v-for="(item, index) in busyTorrentProperties"
|
||||
:key="index"
|
||||
style="width: 30vh"
|
||||
>
|
||||
<v-checkbox v-model="item.active" dense class="pa-0 ma-0 mt-3"/>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title class="truncate" v-text="item.name"/>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</draggable>
|
||||
</v-list>
|
||||
</v-row>
|
||||
</v-flex>
|
||||
>
|
||||
<v-checkbox v-model="item.active" dense class="pa-0 ma-0 mt-3" />
|
||||
<v-list-item-content>
|
||||
<v-list-item-title class="truncate" v-text="item.name" />
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</draggable>
|
||||
</v-list>
|
||||
</v-row>
|
||||
</v-flex>
|
||||
<v-flex class="box ma-1" xs12 md5>
|
||||
<v-subheader>
|
||||
Properties to display for completed torrents
|
||||
</v-subheader>
|
||||
<v-subheader>
|
||||
Properties to display for completed torrents
|
||||
</v-subheader>
|
||||
<v-row dense>
|
||||
<v-list flat class="ma-0 pa-0">
|
||||
<draggable
|
||||
<v-list flat class="ma-0 pa-0">
|
||||
<draggable
|
||||
:list="doneTorrentProperties"
|
||||
tag="tbody"
|
||||
>
|
||||
<v-list-item
|
||||
>
|
||||
<v-list-item
|
||||
v-for="(item, index) in doneTorrentProperties"
|
||||
:key="index"
|
||||
style="width: 30vh"
|
||||
>
|
||||
<v-checkbox v-model="item.active" dense class="pa-0 ma-0 mt-3"/>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title class="truncate" v-text="item.name"/>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</draggable>
|
||||
</v-list>
|
||||
</v-row>
|
||||
</v-flex>
|
||||
>
|
||||
<v-checkbox v-model="item.active" dense class="pa-0 ma-0 mt-3" />
|
||||
<v-list-item-content>
|
||||
<v-list-item-title class="truncate" v-text="item.name" />
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</draggable>
|
||||
</v-list>
|
||||
</v-row>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
@ -60,18 +70,18 @@
|
|||
<script>
|
||||
import draggable from 'vuedraggable'
|
||||
export default {
|
||||
name: 'Dashboard',
|
||||
components: {
|
||||
draggable
|
||||
name: 'Dashboard',
|
||||
components: {
|
||||
draggable
|
||||
},
|
||||
computed: {
|
||||
busyTorrentProperties() {
|
||||
return this.$store.state.webuiSettings.busyTorrentProperties
|
||||
},
|
||||
computed: {
|
||||
busyTorrentProperties() {
|
||||
return this.$store.state.webuiSettings.busyTorrentProperties
|
||||
},
|
||||
doneTorrentProperties() {
|
||||
return this.$store.state.webuiSettings.doneTorrentProperties
|
||||
}
|
||||
doneTorrentProperties() {
|
||||
return this.$store.state.webuiSettings.doneTorrentProperties
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -3,101 +3,129 @@
|
|||
<v-card flat>
|
||||
<v-card-text class="pa-0" style="font-size: 1.1em">
|
||||
<div class="box">
|
||||
<v-subheader
|
||||
>These settings are for the custom WebUI
|
||||
itself</v-subheader
|
||||
>
|
||||
<v-subheader>
|
||||
These settings are for the custom WebUI
|
||||
itself
|
||||
</v-subheader>
|
||||
<v-form class="px-6 mt-3">
|
||||
<v-container>
|
||||
<v-switch
|
||||
class="v-input--reverse v-input--expand pa-0 ma-0"
|
||||
inset
|
||||
v-model="showCurrentSpeed"
|
||||
color="green_accent"
|
||||
>
|
||||
<template #label> Show Current Speed </template>
|
||||
</v-switch>
|
||||
<v-switch
|
||||
class="v-input--reverse v-input--expand pa-0 ma-0"
|
||||
inset
|
||||
v-model="showSpeedGraph"
|
||||
color="green_accent"
|
||||
>
|
||||
<template #label> Show Speed Graph</template>
|
||||
</v-switch>
|
||||
<v-switch
|
||||
class="v-input--reverse v-input--expand pa-0 ma-0"
|
||||
inset
|
||||
v-model="showSessionStat"
|
||||
color="green_accent"
|
||||
>
|
||||
<template #label> Show Session Stats </template>
|
||||
</v-switch>
|
||||
<v-switch
|
||||
class="v-input--reverse v-input--expand pa-0 ma-0"
|
||||
inset
|
||||
v-model="freeSpace"
|
||||
color="green_accent"
|
||||
>
|
||||
<template #label> Show Free Space </template>
|
||||
</v-switch>
|
||||
<v-switch
|
||||
class="v-input--reverse v-input--expand pa-0 ma-0"
|
||||
inset
|
||||
v-model="showGlobalRemoveResumePause"
|
||||
color="green_accent"
|
||||
v-model="showCurrentSpeed"
|
||||
class="v-input--reverse v-input--expand pa-0 ma-0"
|
||||
inset
|
||||
color="green_accent"
|
||||
>
|
||||
<template #label>
|
||||
Global Remove/Resume/Pause Buttons</template
|
||||
>
|
||||
Show Current Speed
|
||||
</template>
|
||||
</v-switch>
|
||||
<v-switch
|
||||
class="v-input--reverse v-input--expand pa-0 ma-0"
|
||||
inset
|
||||
v-model="denseDashboard"
|
||||
color="green_accent"
|
||||
v-model="showSpeedGraph"
|
||||
class="v-input--reverse v-input--expand pa-0 ma-0"
|
||||
inset
|
||||
color="green_accent"
|
||||
>
|
||||
<template #label>
|
||||
Dense version of the dasbhoard</template
|
||||
>
|
||||
Show Speed Graph
|
||||
</template>
|
||||
</v-switch>
|
||||
<v-switch
|
||||
class="v-input--reverse v-input--expand pa-0 ma-0"
|
||||
inset
|
||||
v-model="showTrackerFilter"
|
||||
color="green_accent"
|
||||
v-model="showSessionStat"
|
||||
class="v-input--reverse v-input--expand pa-0 ma-0"
|
||||
inset
|
||||
color="green_accent"
|
||||
>
|
||||
<template #label>
|
||||
Show Tracker Filter</template
|
||||
>
|
||||
Show Session Stats
|
||||
</template>
|
||||
</v-switch>
|
||||
<v-switch
|
||||
v-model="freeSpace"
|
||||
class="v-input--reverse v-input--expand pa-0 ma-0"
|
||||
inset
|
||||
color="green_accent"
|
||||
>
|
||||
<template #label>
|
||||
Show Free Space
|
||||
</template>
|
||||
</v-switch>
|
||||
<v-switch
|
||||
v-model="showGlobalRemoveResumePause"
|
||||
class="v-input--reverse v-input--expand pa-0 ma-0"
|
||||
inset
|
||||
color="green_accent"
|
||||
>
|
||||
<template #label>
|
||||
Global Remove/Resume/Pause Buttons
|
||||
</template>
|
||||
</v-switch>
|
||||
<v-switch
|
||||
v-model="denseDashboard"
|
||||
class="v-input--reverse v-input--expand pa-0 ma-0"
|
||||
inset
|
||||
color="green_accent"
|
||||
>
|
||||
<template #label>
|
||||
Dense version of the dasbhoard
|
||||
</template>
|
||||
</v-switch>
|
||||
<v-switch
|
||||
v-model="showTrackerFilter"
|
||||
class="v-input--reverse v-input--expand pa-0 ma-0"
|
||||
inset
|
||||
color="green_accent"
|
||||
>
|
||||
<template #label>
|
||||
Show Tracker Filter
|
||||
</template>
|
||||
</v-switch>
|
||||
<v-switch
|
||||
v-model="showSpeedInTitle"
|
||||
class="v-input--reverse v-input--expand pa-0 ma-0"
|
||||
inset
|
||||
color="green_accent"
|
||||
>
|
||||
<template #label>
|
||||
Show Speed in Title
|
||||
</template>
|
||||
</v-switch>
|
||||
<v-row dense>
|
||||
<v-col cols="8" sm="8" md="10">
|
||||
<p class="subtitle-1">Pagination size:</p>
|
||||
<p class="subtitle-1">
|
||||
Pagination size:
|
||||
</p>
|
||||
</v-col>
|
||||
<v-col cols="4" sm="4" md="2">
|
||||
<v-select
|
||||
class="pa-0 ma-0"
|
||||
color="green_accent"
|
||||
:items="paginationSizes"
|
||||
v-model="paginationSize"
|
||||
></v-select>
|
||||
v-model="paginationSize"
|
||||
class="pa-0 ma-0"
|
||||
color="green_accent"
|
||||
:items="paginationSizes"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row dense>
|
||||
<v-col cols="10" sm="10" md="11">
|
||||
<p class="subtitle-1">Current Version:</p>
|
||||
<p class="subtitle-1">
|
||||
Current Version:
|
||||
</p>
|
||||
</v-col>
|
||||
<v-col cols="2" sm="2" md="1">
|
||||
<p class="mb-2">{{ version }}</p>
|
||||
<p class="mb-2">
|
||||
{{ version }}
|
||||
</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row dense>
|
||||
<v-col cols="10" sm="10" md="11">
|
||||
<p class="subtitle-1">QBittorrent Version:</p>
|
||||
<p class="subtitle-1">
|
||||
QBittorrent Version:
|
||||
</p>
|
||||
</v-col>
|
||||
<v-col cols="2" sm="2" md="1">
|
||||
<p class="mb-2">{{ Qbitversion }}</p>
|
||||
<p class="mb-2">
|
||||
{{ Qbitversion }}
|
||||
</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
@ -112,92 +140,101 @@
|
|||
import { mapState, mapGetters } from 'vuex'
|
||||
import qbit from '@/services/qbit'
|
||||
export default {
|
||||
name: 'General',
|
||||
data() {
|
||||
return {
|
||||
paginationSizes: [5, 15, 30, 50],
|
||||
Qbitversion: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['webuiSettings']),
|
||||
...mapGetters(['getAppVersion']),
|
||||
freeSpace: {
|
||||
get() {
|
||||
return this.webuiSettings.showFreeSpace
|
||||
},
|
||||
set(val) {
|
||||
this.webuiSettings.showFreeSpace = val
|
||||
}
|
||||
},
|
||||
showCurrentSpeed: {
|
||||
get() {
|
||||
return this.webuiSettings.showCurrentSpeed
|
||||
},
|
||||
set(val) {
|
||||
this.webuiSettings.showCurrentSpeed = val
|
||||
}
|
||||
},
|
||||
showSpeedGraph: {
|
||||
get() {
|
||||
return this.webuiSettings.showSpeedGraph
|
||||
},
|
||||
set(val) {
|
||||
this.webuiSettings.showSpeedGraph = val
|
||||
}
|
||||
},
|
||||
showSessionStat: {
|
||||
get() {
|
||||
return this.webuiSettings.showSessionStat
|
||||
},
|
||||
set(val) {
|
||||
this.webuiSettings.showSessionStat = val
|
||||
}
|
||||
},
|
||||
showGlobalRemoveResumePause: {
|
||||
get() {
|
||||
return this.webuiSettings.showGlobalRemoveResumePause
|
||||
},
|
||||
set(val) {
|
||||
this.webuiSettings.showGlobalRemoveResumePause = val
|
||||
}
|
||||
},
|
||||
denseDashboard: {
|
||||
get() {
|
||||
return this.webuiSettings.denseDashboard
|
||||
},
|
||||
set(val) {
|
||||
this.webuiSettings.denseDashboard = val
|
||||
}
|
||||
},
|
||||
showTrackerFilter: {
|
||||
get() {
|
||||
return this.webuiSettings.showTrackerFilter
|
||||
},
|
||||
set(val) {
|
||||
this.webuiSettings.showTrackerFilter = val
|
||||
}
|
||||
},
|
||||
paginationSize: {
|
||||
get() {
|
||||
return this.webuiSettings.paginationSize
|
||||
},
|
||||
set(val) {
|
||||
this.webuiSettings.paginationSize = val
|
||||
}
|
||||
},
|
||||
version() {
|
||||
return this.getAppVersion()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async fetchQbitVersion() {
|
||||
this.Qbitversion = await qbit.getAppVersion()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchQbitVersion()
|
||||
name: 'General',
|
||||
data() {
|
||||
return {
|
||||
paginationSizes: [5, 15, 30, 50],
|
||||
Qbitversion: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['webuiSettings']),
|
||||
...mapGetters(['getAppVersion']),
|
||||
freeSpace: {
|
||||
get() {
|
||||
return this.webuiSettings.showFreeSpace
|
||||
},
|
||||
set(val) {
|
||||
this.webuiSettings.showFreeSpace = val
|
||||
}
|
||||
},
|
||||
showCurrentSpeed: {
|
||||
get() {
|
||||
return this.webuiSettings.showCurrentSpeed
|
||||
},
|
||||
set(val) {
|
||||
this.webuiSettings.showCurrentSpeed = val
|
||||
}
|
||||
},
|
||||
showSpeedGraph: {
|
||||
get() {
|
||||
return this.webuiSettings.showSpeedGraph
|
||||
},
|
||||
set(val) {
|
||||
this.webuiSettings.showSpeedGraph = val
|
||||
}
|
||||
},
|
||||
showSessionStat: {
|
||||
get() {
|
||||
return this.webuiSettings.showSessionStat
|
||||
},
|
||||
set(val) {
|
||||
this.webuiSettings.showSessionStat = val
|
||||
}
|
||||
},
|
||||
showGlobalRemoveResumePause: {
|
||||
get() {
|
||||
return this.webuiSettings.showGlobalRemoveResumePause
|
||||
},
|
||||
set(val) {
|
||||
this.webuiSettings.showGlobalRemoveResumePause = val
|
||||
}
|
||||
},
|
||||
denseDashboard: {
|
||||
get() {
|
||||
return this.webuiSettings.denseDashboard
|
||||
},
|
||||
set(val) {
|
||||
this.webuiSettings.denseDashboard = val
|
||||
}
|
||||
},
|
||||
showTrackerFilter: {
|
||||
get() {
|
||||
return this.webuiSettings.showTrackerFilter
|
||||
},
|
||||
set(val) {
|
||||
this.webuiSettings.showTrackerFilter = val
|
||||
}
|
||||
},
|
||||
showSpeedInTitle: {
|
||||
get() {
|
||||
return this.webuiSettings.showSpeedInTitle
|
||||
},
|
||||
set(val) {
|
||||
this.webuiSettings.showSpeedInTitle = val
|
||||
document.title = 'VueTorrent'
|
||||
}
|
||||
},
|
||||
paginationSize: {
|
||||
get() {
|
||||
return this.webuiSettings.paginationSize
|
||||
},
|
||||
set(val) {
|
||||
this.webuiSettings.paginationSize = val
|
||||
}
|
||||
},
|
||||
version() {
|
||||
return this.getAppVersion()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchQbitVersion()
|
||||
},
|
||||
methods: {
|
||||
async fetchQbitVersion() {
|
||||
this.Qbitversion = await qbit.getAppVersion()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,65 +1,67 @@
|
|||
<template>
|
||||
<v-container>
|
||||
<v-card flat>
|
||||
<v-card-text class="pa-0" style="font-size: 1.1em"
|
||||
:style="{ minHeight: phoneLayout ? '' : '70vh'}"
|
||||
>
|
||||
<div class="box">
|
||||
<v-subheader>Use Alternative WebUI</v-subheader>
|
||||
<div class="ml-5 mr-5">
|
||||
<v-checkbox
|
||||
dense
|
||||
:label="`Use Alternative WebUI`"
|
||||
v-model="settings.alternative_webui_enabled"
|
||||
/>
|
||||
<v-text-field
|
||||
outlined
|
||||
dense
|
||||
hide-details="true"
|
||||
:label="`Files location`"
|
||||
:disabled="!settings.alternative_webui_enabled"
|
||||
v-model="settings.alternative_webui_path"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<v-subheader
|
||||
>Web User Interface (Remote Control)</v-subheader
|
||||
>
|
||||
<v-row class="ml-5 mr-5">
|
||||
<v-col cols="10">
|
||||
<v-text-field
|
||||
class="mr-1"
|
||||
outlined
|
||||
dense
|
||||
hide-details="true"
|
||||
:label="`IP Address:`"
|
||||
v-model="settings.web_ui_address"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
class="ml-1"
|
||||
outlined
|
||||
dense
|
||||
hide-details="true"
|
||||
:label="`Port`"
|
||||
v-model="settings.web_ui_port"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-container>
|
||||
<v-card flat>
|
||||
<v-card-text
|
||||
class="pa-0"
|
||||
style="font-size: 1.1em"
|
||||
:style="{ minHeight: phoneLayout ? '' : '70vh'}"
|
||||
>
|
||||
<div class="box">
|
||||
<v-subheader>Use Alternative WebUI</v-subheader>
|
||||
<div class="ml-5 mr-5">
|
||||
<v-checkbox
|
||||
v-model="settings.alternative_webui_enabled"
|
||||
dense
|
||||
:label="`Use Alternative WebUI`"
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="settings.alternative_webui_path"
|
||||
outlined
|
||||
dense
|
||||
hide-details="true"
|
||||
:label="`Files location`"
|
||||
:disabled="!settings.alternative_webui_enabled"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<v-subheader>
|
||||
Web User Interface (Remote Control)
|
||||
</v-subheader>
|
||||
<v-row class="ml-5 mr-5">
|
||||
<v-col cols="10">
|
||||
<v-text-field
|
||||
v-model="settings.web_ui_address"
|
||||
class="mr-1"
|
||||
outlined
|
||||
dense
|
||||
hide-details="true"
|
||||
:label="`IP Address:`"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
v-model="settings.web_ui_port"
|
||||
class="ml-1"
|
||||
outlined
|
||||
dense
|
||||
hide-details="true"
|
||||
:label="`Port`"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { FullScreenModal, SettingsTab } from '@/mixins'
|
||||
|
||||
export default {
|
||||
name: 'WebUI',
|
||||
mixins: [SettingsTab, FullScreenModal]
|
||||
name: 'WebUI',
|
||||
mixins: [SettingsTab, FullScreenModal]
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,82 +1,84 @@
|
|||
<template>
|
||||
<v-dialog v-model="dialog" scrollable max-width="500px">
|
||||
<v-card>
|
||||
<v-container :class="`pa-0 project done`">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>Sort Torrents</h2>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-form class="px-6 mt-3 justify-center mx-auto">
|
||||
<v-container class="sortmodal">
|
||||
<v-select
|
||||
:value="sortProperty"
|
||||
v-model="sort_options.sort"
|
||||
flat
|
||||
class="ml-2 mr-2"
|
||||
:items="options"
|
||||
item-text="name"
|
||||
item-value="value"
|
||||
dense
|
||||
solo
|
||||
background-color="select"
|
||||
height="55"
|
||||
></v-select>
|
||||
<v-switch
|
||||
class="v-input--reverse v-input--expand pa-0 ma-0"
|
||||
inset
|
||||
v-model="sort_options.reverse"
|
||||
color="green_accent"
|
||||
style="padding-left: 10px !important"
|
||||
>
|
||||
<template #label> Reverse </template>
|
||||
</v-switch>
|
||||
</v-container>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-dialog v-model="dialog" scrollable max-width="500px">
|
||||
<v-card>
|
||||
<v-container :class="`pa-0 project done`">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>Sort Torrents</h2>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-form class="px-6 mt-3 justify-center mx-auto">
|
||||
<v-container class="sortmodal">
|
||||
<v-select
|
||||
v-model="sort_options.sort"
|
||||
:value="sortProperty"
|
||||
flat
|
||||
class="ml-2 mr-2"
|
||||
:items="options"
|
||||
item-text="name"
|
||||
item-value="value"
|
||||
dense
|
||||
solo
|
||||
background-color="select"
|
||||
height="55"
|
||||
/>
|
||||
<v-switch
|
||||
v-model="sort_options.reverse"
|
||||
class="v-input--reverse v-input--expand pa-0 ma-0"
|
||||
inset
|
||||
color="green_accent"
|
||||
style="padding-left: 10px !important"
|
||||
>
|
||||
<template #label>
|
||||
Reverse
|
||||
</template>
|
||||
</v-switch>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import { Modal } from '@/mixins'
|
||||
export default {
|
||||
name: 'Sort',
|
||||
mixins: [Modal],
|
||||
data() {
|
||||
return {
|
||||
sortProperty: { value: 'added_on', name: 'Added On' },
|
||||
reverse: true,
|
||||
options: [
|
||||
{ value: 'added_on', name: 'Added On' },
|
||||
{ value: 'availability', name: 'Availability' },
|
||||
{ value: 'category', name: 'Category' },
|
||||
{ value: 'completed', name: 'Completed' },
|
||||
{ value: 'dlspeed', name: 'Download Speed' },
|
||||
{ value: 'downloaded', name: 'Downloaded' },
|
||||
{ value: 'eta', name: 'ETA' },
|
||||
{ value: 'name', name: 'Name' },
|
||||
{ value: 'num_leechs', name: 'Peers' },
|
||||
{ value: 'priority', name: 'Priority' },
|
||||
{ value: 'progress', name: 'Progress' },
|
||||
{ value: 'ratio', name: 'Ratio' },
|
||||
{ value: 'size', name: 'Size' },
|
||||
{ value: 'state', name: 'State' },
|
||||
{ value: 'time_active', name: 'Time Active' },
|
||||
{ value: 'uploaded', name: 'Uploaded' },
|
||||
{ value: 'upspeed', name: 'Upload Speed' }
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.$store.commit('DELETE_MODAL', this.guid)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['sort_options'])
|
||||
name: 'Sort',
|
||||
mixins: [Modal],
|
||||
data() {
|
||||
return {
|
||||
sortProperty: { value: 'added_on', name: 'Added On' },
|
||||
reverse: true,
|
||||
options: [
|
||||
{ value: 'added_on', name: 'Added On' },
|
||||
{ value: 'availability', name: 'Availability' },
|
||||
{ value: 'category', name: 'Category' },
|
||||
{ value: 'completed', name: 'Completed' },
|
||||
{ value: 'dlspeed', name: 'Download Speed' },
|
||||
{ value: 'downloaded', name: 'Downloaded' },
|
||||
{ value: 'eta', name: 'ETA' },
|
||||
{ value: 'name', name: 'Name' },
|
||||
{ value: 'num_leechs', name: 'Peers' },
|
||||
{ value: 'priority', name: 'Priority' },
|
||||
{ value: 'progress', name: 'Progress' },
|
||||
{ value: 'ratio', name: 'Ratio' },
|
||||
{ value: 'size', name: 'Size' },
|
||||
{ value: 'state', name: 'State' },
|
||||
{ value: 'time_active', name: 'Time Active' },
|
||||
{ value: 'uploaded', name: 'Uploaded' },
|
||||
{ value: 'upspeed', name: 'Upload Speed' }
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['sort_options'])
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.$store.commit('DELETE_MODAL', this.guid)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,55 +1,61 @@
|
|||
<template>
|
||||
<v-dialog v-model="dialog" max-width="600px">
|
||||
<v-card>
|
||||
<v-container style="min-height: 200px" :class="`pa-0 project done`">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>{{ hasInitialCategory ? 'Edit' : 'Create New' }} Category</h2>
|
||||
</v-card-title>
|
||||
<v-dialog v-model="dialog" max-width="600px">
|
||||
<v-card>
|
||||
<v-container style="min-height: 200px" :class="`pa-0 project done`">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>{{ hasInitialCategory ? 'Edit' : 'Create New' }} Category</h2>
|
||||
</v-card-title>
|
||||
|
||||
<v-form ref="categoryForm" class="px-6 mt-3">
|
||||
<v-container>
|
||||
<v-text-field
|
||||
class="mx-auto"
|
||||
style="max-width: 200px"
|
||||
v-model="category.name"
|
||||
:rules="nameRules"
|
||||
:counter="15"
|
||||
label="Category name"
|
||||
required
|
||||
></v-text-field>
|
||||
<v-text-field
|
||||
class="mx-auto"
|
||||
style="max-width: 200px"
|
||||
v-model="category.savePath"
|
||||
:rules="PathRules"
|
||||
:counter="40"
|
||||
label="Path"
|
||||
required
|
||||
></v-text-field>
|
||||
</v-container>
|
||||
</v-form>
|
||||
</v-container>
|
||||
<v-card-actions class="justify-center pb-5 project done">
|
||||
<v-btn text @click="cancel" class="error white--text mt-3"
|
||||
>Cancel</v-btn
|
||||
>
|
||||
<v-btn
|
||||
v-if="!hasInitialCategory"
|
||||
text
|
||||
@click="create"
|
||||
class="green_accent white--text mt-3"
|
||||
>Save</v-btn
|
||||
>
|
||||
<v-btn
|
||||
v-else
|
||||
text
|
||||
@click="edit"
|
||||
class="green_accent white--text mt-3"
|
||||
>Edit</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<v-form ref="categoryForm" class="px-6 mt-3">
|
||||
<v-container>
|
||||
<v-text-field
|
||||
v-model="category.name"
|
||||
class="mx-auto"
|
||||
style="max-width: 200px"
|
||||
:rules="nameRules"
|
||||
:counter="15"
|
||||
label="Category name"
|
||||
required
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="category.savePath"
|
||||
class="mx-auto"
|
||||
style="max-width: 200px"
|
||||
:rules="PathRules"
|
||||
:counter="40"
|
||||
label="Path"
|
||||
required
|
||||
/>
|
||||
</v-container>
|
||||
</v-form>
|
||||
</v-container>
|
||||
<v-card-actions class="justify-center pb-5 project done">
|
||||
<v-btn
|
||||
text
|
||||
class="error white--text mt-3"
|
||||
@click="cancel"
|
||||
>
|
||||
Cancel
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="!hasInitialCategory"
|
||||
text
|
||||
class="green_accent white--text mt-3"
|
||||
@click="create"
|
||||
>
|
||||
Save
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-else
|
||||
text
|
||||
class="green_accent white--text mt-3"
|
||||
@click="edit"
|
||||
>
|
||||
Edit
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -59,57 +65,57 @@ import { Modal } from '@/mixins'
|
|||
import Vue from 'vue'
|
||||
|
||||
export default {
|
||||
name: 'createNewCategoryDialog',
|
||||
props: {
|
||||
initialCategory: Object
|
||||
},
|
||||
mixins: [Modal],
|
||||
computed: {
|
||||
...mapGetters(['getSelectedCategory']),
|
||||
hasInitialCategory() {
|
||||
return (
|
||||
this.initialCategory &&
|
||||
name: 'CreateNewCategoryDialog',
|
||||
mixins: [Modal],
|
||||
props: {
|
||||
initialCategory: Object
|
||||
},
|
||||
data: () => ({
|
||||
nameRules: [
|
||||
v => !!v || 'Category name is required',
|
||||
v =>
|
||||
(v && v.length <= 15) ||
|
||||
'Category name must be less than 15 characters'
|
||||
],
|
||||
PathRules: [
|
||||
v => !!v || 'Path is required',
|
||||
v => (v && v.length <= 40) || 'Path must be less than 40 characters'
|
||||
],
|
||||
category: { name: '', savePath: '' }
|
||||
}),
|
||||
computed: {
|
||||
...mapGetters(['getSelectedCategory']),
|
||||
hasInitialCategory() {
|
||||
return (
|
||||
this.initialCategory &&
|
||||
this.initialCategory.name
|
||||
)
|
||||
}
|
||||
},
|
||||
data: () => ({
|
||||
nameRules: [
|
||||
v => !!v || 'Category name is required',
|
||||
v =>
|
||||
(v && v.length <= 15) ||
|
||||
'Category name must be less than 15 characters'
|
||||
],
|
||||
PathRules: [
|
||||
v => !!v || 'Path is required',
|
||||
v => (v && v.length <= 40) || 'Path must be less than 40 characters'
|
||||
],
|
||||
category: { name: '', savePath: '' }
|
||||
}),
|
||||
methods: {
|
||||
create() {
|
||||
qbit.createCategory(this.category)
|
||||
this.cancel()
|
||||
},
|
||||
cancel() {
|
||||
this.category.name = ''
|
||||
this.category.savePath = ''
|
||||
this.$refs.categoryForm.reset()
|
||||
this.$store.commit('FETCH_CATEGORIES')
|
||||
this.deleteModal()
|
||||
},
|
||||
edit() {
|
||||
qbit.editCategory(this.category)
|
||||
Vue.$toast.success('Category edited successfully!')
|
||||
this.cancel()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$store.commit('FETCH_CATEGORIES')
|
||||
if (this.hasInitialCategory) {
|
||||
this.category = this.initialCategory
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$store.commit('FETCH_CATEGORIES')
|
||||
if (this.hasInitialCategory) {
|
||||
this.category = this.initialCategory
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
create() {
|
||||
qbit.createCategory(this.category)
|
||||
this.cancel()
|
||||
},
|
||||
cancel() {
|
||||
this.category.name = ''
|
||||
this.category.savePath = ''
|
||||
this.$refs.categoryForm.reset()
|
||||
this.$store.commit('FETCH_CATEGORIES')
|
||||
this.deleteModal()
|
||||
},
|
||||
edit() {
|
||||
qbit.editCategory(this.category)
|
||||
Vue.$toast.success('Category edited successfully!')
|
||||
this.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,63 +1,68 @@
|
|||
<template>
|
||||
<v-dialog v-model="dialog" max-width="600px">
|
||||
<v-card>
|
||||
<v-container style="min-height: 200px" :class="`pa-0 project done`">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>Create New Tag</h2>
|
||||
</v-card-title>
|
||||
<v-dialog v-model="dialog" max-width="600px">
|
||||
<v-card>
|
||||
<v-container style="min-height: 200px" :class="`pa-0 project done`">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>Create New Tag</h2>
|
||||
</v-card-title>
|
||||
|
||||
<v-form class="px-6 mt-3">
|
||||
<v-container>
|
||||
<v-text-field
|
||||
class="mx-auto"
|
||||
style="max-width: 200px"
|
||||
v-model="tagname"
|
||||
:rules="rules"
|
||||
:counter="10"
|
||||
label="Tag name"
|
||||
required
|
||||
></v-text-field>
|
||||
</v-container>
|
||||
</v-form>
|
||||
</v-container>
|
||||
<v-card-actions class="justify-center pb-5 project done">
|
||||
<v-btn text @click="cancel" class="error white--text mt-3"
|
||||
>Cancel</v-btn
|
||||
>
|
||||
<v-btn
|
||||
text
|
||||
@click="create"
|
||||
class="green_accent white--text mt-3"
|
||||
>Save</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<v-form class="px-6 mt-3">
|
||||
<v-container>
|
||||
<v-text-field
|
||||
v-model="tagname"
|
||||
class="mx-auto"
|
||||
style="max-width: 200px"
|
||||
:rules="rules"
|
||||
:counter="10"
|
||||
label="Tag name"
|
||||
required
|
||||
/>
|
||||
</v-container>
|
||||
</v-form>
|
||||
</v-container>
|
||||
<v-card-actions class="justify-center pb-5 project done">
|
||||
<v-btn
|
||||
text
|
||||
class="error white--text mt-3"
|
||||
@click="cancel"
|
||||
>
|
||||
Cancel
|
||||
</v-btn>
|
||||
<v-btn
|
||||
text
|
||||
class="green_accent white--text mt-3"
|
||||
@click="create"
|
||||
>
|
||||
Save
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import qbit from '@/services/qbit'
|
||||
import { Modal } from '@/mixins'
|
||||
export default {
|
||||
name: 'createTagDialog',
|
||||
mixins: [Modal],
|
||||
data: () => ({
|
||||
tagname: '',
|
||||
rules: [
|
||||
v => !!v || 'Tag is required',
|
||||
v => v.length <= 10 || 'Tag must be less than 10 characters'
|
||||
]
|
||||
}),
|
||||
methods: {
|
||||
create() {
|
||||
qbit.createTag(this.tagname)
|
||||
this.cancel()
|
||||
},
|
||||
cancel() {
|
||||
this.tagname = ''
|
||||
this.deleteModal()
|
||||
}
|
||||
name: 'CreateTagDialog',
|
||||
mixins: [Modal],
|
||||
data: () => ({
|
||||
tagname: '',
|
||||
rules: [
|
||||
v => !!v || 'Tag is required',
|
||||
v => v.length <= 10 || 'Tag must be less than 10 characters'
|
||||
]
|
||||
}),
|
||||
methods: {
|
||||
create() {
|
||||
qbit.createTag(this.tagname)
|
||||
this.cancel()
|
||||
},
|
||||
cancel() {
|
||||
this.tagname = ''
|
||||
this.deleteModal()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,44 +1,48 @@
|
|||
<template>
|
||||
<v-dialog :value="dialog" max-width="600px">
|
||||
<v-card>
|
||||
<v-container style="min-height: 200px" :class="`pa-0 project done`">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>Delete Category</h2>
|
||||
</v-card-title>
|
||||
<v-dialog :value="dialog" max-width="600px">
|
||||
<v-card>
|
||||
<v-container style="min-height: 200px" :class="`pa-0 project done`">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>Delete Category</h2>
|
||||
</v-card-title>
|
||||
|
||||
<v-list
|
||||
rounded
|
||||
v-if="categories"
|
||||
class="text-center mx-auto"
|
||||
style="max-width: 200px"
|
||||
>
|
||||
<v-list-item
|
||||
@click="deleteCategory(t)"
|
||||
v-for="(t, i) in categories"
|
||||
:key="i"
|
||||
>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title
|
||||
v-text="t.name"
|
||||
></v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
<v-card-subtitle
|
||||
class="text-center mx-auto"
|
||||
style="font-size: 1.5em; margin-top: 20px"
|
||||
v-else
|
||||
>
|
||||
No categories found
|
||||
</v-card-subtitle>
|
||||
</v-container>
|
||||
<v-card-actions class="justify-center pb-5 project done">
|
||||
<v-btn text @click="cancel" class="error white--text mt-3"
|
||||
>Close</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<v-list
|
||||
v-if="categories"
|
||||
rounded
|
||||
class="text-center mx-auto"
|
||||
style="max-width: 200px"
|
||||
>
|
||||
<v-list-item
|
||||
v-for="(t, i) in categories"
|
||||
:key="i"
|
||||
@click="deleteCategory(t)"
|
||||
>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title
|
||||
v-text="t.name"
|
||||
/>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
<v-card-subtitle
|
||||
v-else
|
||||
class="text-center mx-auto"
|
||||
style="font-size: 1.5em; margin-top: 20px"
|
||||
>
|
||||
No categories found
|
||||
</v-card-subtitle>
|
||||
</v-container>
|
||||
<v-card-actions class="justify-center pb-5 project done">
|
||||
<v-btn
|
||||
text
|
||||
class="error white--text mt-3"
|
||||
@click="cancel"
|
||||
>
|
||||
Close
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -46,24 +50,24 @@ import qbit from '@/services/qbit'
|
|||
import { Modal } from '@/mixins'
|
||||
import { mapGetters } from 'vuex'
|
||||
export default {
|
||||
name: 'DeleteCategoryDialog',
|
||||
mixins: [Modal],
|
||||
computed: {
|
||||
...mapGetters(['getCategories']),
|
||||
categories() {
|
||||
return this.getCategories()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
deleteCategory(cat) {
|
||||
qbit.deleteCategory(cat.name)
|
||||
this.cancel()
|
||||
},
|
||||
cancel() {
|
||||
this.$store.commit('FETCH_CATEGORIES')
|
||||
this.deleteModal()
|
||||
}
|
||||
name: 'DeleteCategoryDialog',
|
||||
mixins: [Modal],
|
||||
computed: {
|
||||
...mapGetters(['getCategories']),
|
||||
categories() {
|
||||
return this.getCategories()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
deleteCategory(cat) {
|
||||
qbit.deleteCategory(cat.name)
|
||||
this.cancel()
|
||||
},
|
||||
cancel() {
|
||||
this.$store.commit('FETCH_CATEGORIES')
|
||||
this.deleteModal()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,42 +1,46 @@
|
|||
<template>
|
||||
<v-dialog v-model="dialog" max-width="600px">
|
||||
<v-card>
|
||||
<v-container style="min-height: 200px" :class="`pa-0 project done`">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>Delete Tag</h2>
|
||||
</v-card-title>
|
||||
<v-dialog v-model="dialog" max-width="600px">
|
||||
<v-card>
|
||||
<v-container style="min-height: 200px" :class="`pa-0 project done`">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>Delete Tag</h2>
|
||||
</v-card-title>
|
||||
|
||||
<v-list
|
||||
rounded
|
||||
v-if="availableTags && availableTags.length"
|
||||
class="text-center mx-auto"
|
||||
style="max-width: 200px"
|
||||
>
|
||||
<v-list-item
|
||||
@click="deleteTag(t)"
|
||||
v-for="(t, i) in availableTags"
|
||||
:key="i"
|
||||
>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title v-text="t"></v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
<v-card-subtitle
|
||||
class="text-center mx-auto"
|
||||
style="font-size: 1.5em; margin-top: 20px"
|
||||
v-else
|
||||
>
|
||||
No tags found
|
||||
</v-card-subtitle>
|
||||
</v-container>
|
||||
<v-card-actions class="justify-center pb-5 project done">
|
||||
<v-btn text @click="cancel" class="error white--text mt-3"
|
||||
>Close</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<v-list
|
||||
v-if="availableTags && availableTags.length"
|
||||
rounded
|
||||
class="text-center mx-auto"
|
||||
style="max-width: 200px"
|
||||
>
|
||||
<v-list-item
|
||||
v-for="(t, i) in availableTags"
|
||||
:key="i"
|
||||
@click="deleteTag(t)"
|
||||
>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title v-text="t" />
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
<v-card-subtitle
|
||||
v-else
|
||||
class="text-center mx-auto"
|
||||
style="font-size: 1.5em; margin-top: 20px"
|
||||
>
|
||||
No tags found
|
||||
</v-card-subtitle>
|
||||
</v-container>
|
||||
<v-card-actions class="justify-center pb-5 project done">
|
||||
<v-btn
|
||||
text
|
||||
class="error white--text mt-3"
|
||||
@click="cancel"
|
||||
>
|
||||
Close
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -44,23 +48,23 @@ import qbit from '@/services/qbit'
|
|||
import { Modal } from '@/mixins'
|
||||
import { mapGetters } from 'vuex'
|
||||
export default {
|
||||
name: 'DeleteTagDialog',
|
||||
mixins: [Modal],
|
||||
computed: {
|
||||
...mapGetters(['getTorrent', 'getAvailableTags']),
|
||||
availableTags() {
|
||||
return this.getAvailableTags()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
deleteTag(tag) {
|
||||
qbit.deleteTag(tag)
|
||||
this.cancel()
|
||||
},
|
||||
cancel() {
|
||||
this.deleteModal()
|
||||
}
|
||||
name: 'DeleteTagDialog',
|
||||
mixins: [Modal],
|
||||
computed: {
|
||||
...mapGetters(['getTorrent', 'getAvailableTags']),
|
||||
availableTags() {
|
||||
return this.getAvailableTags()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
deleteTag(tag) {
|
||||
qbit.deleteTag(tag)
|
||||
this.cancel()
|
||||
},
|
||||
cancel() {
|
||||
this.deleteModal()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@ import CreateNewCategoryDialog from './CreateCategoryDialog.vue'
|
|||
import DeleteCategoryDialog from './DeleteCategoryDialog'
|
||||
|
||||
export {
|
||||
CreateNewTagDialog,
|
||||
DeleteTagDialog,
|
||||
CreateNewCategoryDialog,
|
||||
DeleteCategoryDialog
|
||||
CreateNewTagDialog,
|
||||
DeleteTagDialog,
|
||||
CreateNewCategoryDialog,
|
||||
DeleteCategoryDialog
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<v-card flat>
|
||||
<perfect-scrollbar>
|
||||
<v-card-text :style="{ minHeight: phoneLayout ? '' : '75vh', maxHeight: '75vh'}">
|
||||
<v-card-text :style="{ minHeight: phoneLayout ? '' : '75vh', maxHeight: '75vh'}">
|
||||
<v-treeview
|
||||
v-model="selected"
|
||||
:items="fileTree"
|
||||
|
@ -11,21 +11,23 @@
|
|||
item-key="fullName"
|
||||
open-all
|
||||
>
|
||||
<template v-slot:prepend="{ item, open }">
|
||||
<template #prepend="{ item, open }">
|
||||
<v-icon v-if="!item.icon">
|
||||
{{ open ? "mdi-folder-open" : "mdi-folder" }}
|
||||
</v-icon>
|
||||
<v-icon v-else>{{ item.icon }}</v-icon>
|
||||
<v-icon v-else>
|
||||
{{ item.icon }}
|
||||
</v-icon>
|
||||
</template>
|
||||
<template v-slot:label="{ item }">
|
||||
<template #label="{ item }">
|
||||
<span v-if="!item.editing">{{ item.name }}</span>
|
||||
<v-text-field
|
||||
autofocus
|
||||
v-if="item.editing"
|
||||
v-model="item.newName"
|
||||
autofocus
|
||||
/>
|
||||
</template>
|
||||
<template v-slot:append="{ item }">
|
||||
<template #append="{ item }">
|
||||
<span v-if="!item.icon">{{ item.children.length }} Files</span>
|
||||
<div v-else>
|
||||
<span>[{{ item.size }}]</span>
|
||||
|
@ -70,96 +72,97 @@ import qbit from '@/services/qbit'
|
|||
import { treeify } from '@/helpers'
|
||||
import { FullScreenModal } from '@/mixins'
|
||||
export default {
|
||||
name: 'Content',
|
||||
mixins: [FullScreenModal],
|
||||
props: {
|
||||
hash: String,
|
||||
isActive: Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
opened: null,
|
||||
selected: [],
|
||||
treeData: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
fileTree() {
|
||||
if (this.treeData) {
|
||||
return treeify(this.treeData)
|
||||
}
|
||||
return []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getTorrentFiles() {
|
||||
const { data } = await qbit.getTorrentFiles(this.hash)
|
||||
data.forEach((d, i) => {
|
||||
d.id = i
|
||||
d.name = d.name.replace('.unwanted/', '')
|
||||
})
|
||||
this.treeData = data
|
||||
},
|
||||
async changeFilePriorities(newValue, oldValue) {
|
||||
if (newValue.length == oldValue.length) return
|
||||
|
||||
const filesToExclude = oldValue
|
||||
.filter(f => !newValue.includes(f))
|
||||
.map(name => this.treeData.find(f => f.name === name))
|
||||
.filter(f => f.priority !== 0)
|
||||
.map(f => f.id)
|
||||
const filesToInclude = newValue
|
||||
.filter(f => !oldValue.includes(f))
|
||||
.map(name => this.treeData.find(f => f.name === name))
|
||||
.filter(f => f.priority === 0)
|
||||
.map(f => f.id)
|
||||
|
||||
if (filesToExclude.length) {
|
||||
await qbit.setTorrentFilePriority(this.hash, filesToExclude, 0)
|
||||
}
|
||||
if (filesToInclude.length) {
|
||||
await qbit.setTorrentFilePriority(this.hash, filesToInclude, 1)
|
||||
}
|
||||
if (filesToExclude.length || filesToInclude.length) {
|
||||
await this.getTorrentFiles()
|
||||
}
|
||||
},
|
||||
togleEditing(item) {
|
||||
item.editing = !item.editing
|
||||
},
|
||||
edit(item) {
|
||||
item.newName = item.name
|
||||
this.togleEditing(item)
|
||||
},
|
||||
renameFile(item) {
|
||||
qbit.renameFile(this.hash, item.id, item.newName)
|
||||
item.name = item.newName
|
||||
this.togleEditing(item)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
isActive(active) {
|
||||
if (active) {
|
||||
this.getTorrentFiles()
|
||||
}
|
||||
},
|
||||
selected(newValue, oldValue) {
|
||||
this.changeFilePriorities(newValue, oldValue)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getTorrentFiles().then(() => {
|
||||
this.opened = []
|
||||
.concat(
|
||||
...this.treeData
|
||||
.map(file => file.name.split('/'))
|
||||
.filter(f => f.splice(-1, 1))
|
||||
)
|
||||
.filter((f, index, self) => index === self.indexOf(f))
|
||||
this.selected = this.treeData
|
||||
.filter(file => file.priority !== 0)
|
||||
.map(file => file.name)
|
||||
})
|
||||
name: 'Content',
|
||||
mixins: [FullScreenModal],
|
||||
props: {
|
||||
hash: String,
|
||||
isActive: Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
opened: null,
|
||||
selected: [],
|
||||
treeData: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
fileTree() {
|
||||
if (this.treeData) {
|
||||
return treeify(this.treeData)
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
isActive(active) {
|
||||
if (active) {
|
||||
this.getTorrentFiles()
|
||||
}
|
||||
},
|
||||
selected(newValue, oldValue) {
|
||||
this.changeFilePriorities(newValue, oldValue)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getTorrentFiles().then(() => {
|
||||
this.opened = []
|
||||
.concat(
|
||||
...this.treeData
|
||||
.map(file => file.name.split('/'))
|
||||
.filter(f => f.splice(-1, 1))
|
||||
)
|
||||
.filter((f, index, self) => index === self.indexOf(f))
|
||||
this.selected = this.treeData
|
||||
.filter(file => file.priority !== 0)
|
||||
.map(file => file.name)
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
async getTorrentFiles() {
|
||||
const { data } = await qbit.getTorrentFiles(this.hash)
|
||||
data.forEach((d, i) => {
|
||||
d.id = i
|
||||
d.name = d.name.replace('.unwanted/', '')
|
||||
})
|
||||
this.treeData = data
|
||||
},
|
||||
async changeFilePriorities(newValue, oldValue) {
|
||||
if (newValue.length == oldValue.length) return
|
||||
|
||||
const filesToExclude = oldValue
|
||||
.filter(f => !newValue.includes(f))
|
||||
.map(name => this.treeData.find(f => f.name === name))
|
||||
.filter(f => f.priority !== 0)
|
||||
.map(f => f.id)
|
||||
const filesToInclude = newValue
|
||||
.filter(f => !oldValue.includes(f))
|
||||
.map(name => this.treeData.find(f => f.name === name))
|
||||
.filter(f => f.priority === 0)
|
||||
.map(f => f.id)
|
||||
|
||||
if (filesToExclude.length) {
|
||||
await qbit.setTorrentFilePriority(this.hash, filesToExclude, 0)
|
||||
}
|
||||
if (filesToInclude.length) {
|
||||
await qbit.setTorrentFilePriority(this.hash, filesToInclude, 1)
|
||||
}
|
||||
if (filesToExclude.length || filesToInclude.length) {
|
||||
await this.getTorrentFiles()
|
||||
}
|
||||
},
|
||||
togleEditing(item) {
|
||||
item.editing = !item.editing
|
||||
},
|
||||
edit(item) {
|
||||
item.newName = item.name
|
||||
this.togleEditing(item)
|
||||
},
|
||||
renameFile(item) {
|
||||
qbit.renameFile(this.hash, item.id, item.newName)
|
||||
item.name = item.newName
|
||||
this.togleEditing(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -8,104 +8,137 @@
|
|||
<v-simple-table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="grey--text">Torrent title</td>
|
||||
<td class="grey--text">
|
||||
Torrent title
|
||||
</td>
|
||||
<td class="torrentmodaltext--text">
|
||||
{{ torrent.name }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="grey--text">Directory</td>
|
||||
<td class="grey--text">
|
||||
Directory
|
||||
</td>
|
||||
<td class="torrentmodaltext--text">
|
||||
{{ torrent.savePath }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="margin-top: 10px !important">
|
||||
<td class="grey--text">hash</td>
|
||||
<td class="grey--text">
|
||||
hash
|
||||
</td>
|
||||
<td class="torrentmodaltext--text">
|
||||
{{ torrent.hash }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="grey--text">Size</td>
|
||||
<td class="grey--text">
|
||||
Size
|
||||
</td>
|
||||
<td class="torrentmodaltext--text">
|
||||
{{ torrent.size }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="grey--text">Done:</td>
|
||||
<td class="grey--text">
|
||||
Done:
|
||||
</td>
|
||||
<td class="torrentmodaltext--text">
|
||||
{{ torrent.dloaded }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="grey--text">Uploaded:</td>
|
||||
<td class="grey--text">
|
||||
Uploaded:
|
||||
</td>
|
||||
<td class="torrentmodaltext--text">
|
||||
{{ torrent.uploaded }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="grey--text">Ratio</td>
|
||||
<td class="grey--text">
|
||||
Ratio
|
||||
</td>
|
||||
<td class="torrentmodaltext--text">
|
||||
{{ torrent.ratio }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="grey--text">Download Speed</td>
|
||||
<td class="grey--text">
|
||||
Download Speed
|
||||
</td>
|
||||
<td class="torrentmodaltext--text">
|
||||
{{ torrent.dlspeed }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="grey--text">Upload Speed</td>
|
||||
<td class="grey--text">
|
||||
Upload Speed
|
||||
</td>
|
||||
<td class="torrentmodaltext--text">
|
||||
{{ torrent.upspeed }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="grey--text">ETA</td>
|
||||
<td class="grey--text">
|
||||
ETA
|
||||
</td>
|
||||
<td class="torrentmodaltext--text">
|
||||
{{ torrent.eta }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="grey--text">Peers</td>
|
||||
<td class="grey--text">
|
||||
Peers
|
||||
</td>
|
||||
<td class="torrentmodaltext--text">
|
||||
{{ torrent.num_leechs
|
||||
}}<span class="grey--text">/{{ torrent.available_peers }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="grey--text">Seeds</td>
|
||||
<td class="grey--text">
|
||||
Seeds
|
||||
</td>
|
||||
<td class="torrentmodaltext--text">
|
||||
{{ torrent.num_seeds
|
||||
}}<span class="grey--text">/{{ torrent.available_seeds }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="grey--text">Added on</td>
|
||||
<td class="grey--text">
|
||||
Added on
|
||||
</td>
|
||||
<td class="torrentmodaltext--text">
|
||||
{{ torrent.added_on }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="torrent.tracker">
|
||||
<td class="grey--text">Tracker</td>
|
||||
<td class="grey--text">
|
||||
Tracker
|
||||
</td>
|
||||
<td class="torrentmodaltext--text">
|
||||
{{ torrent.tracker }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="torrent.comment">
|
||||
<td class="grey--text">Comment</td>
|
||||
<td class="grey--text">
|
||||
Comment
|
||||
</td>
|
||||
<td class="torrentmodaltext--text">
|
||||
{{ torrent.comment }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="grey--text">Status</td>
|
||||
<td class="grey--text">
|
||||
Status
|
||||
</td>
|
||||
<v-chip
|
||||
small
|
||||
:class="`${torrent.state.toLowerCase()} white--text my-2 caption`"
|
||||
>{{ torrent.state }}</v-chip
|
||||
>
|
||||
{{ torrent.state }}
|
||||
</v-chip>
|
||||
</tr>
|
||||
</tbody>
|
||||
</v-simple-table>
|
||||
|
@ -118,17 +151,17 @@
|
|||
import { mapGetters } from 'vuex'
|
||||
import { FullScreenModal } from '@/mixins'
|
||||
export default {
|
||||
name: 'Info',
|
||||
mixins: [FullScreenModal],
|
||||
props: {
|
||||
hash: String
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getTorrent']),
|
||||
torrent() {
|
||||
return this.getTorrent(this.hash)
|
||||
}
|
||||
name: 'Info',
|
||||
mixins: [FullScreenModal],
|
||||
props: {
|
||||
hash: String
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getTorrent']),
|
||||
torrent() {
|
||||
return this.getTorrent(this.hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
:hide-default-footer="true"
|
||||
:style="{ minHeight: phoneLayout ? '' : '75vh', maxHeight: '75vh'}"
|
||||
>
|
||||
<template v-slot:item="row">
|
||||
<template #item="row">
|
||||
<tr>
|
||||
<td class="ip">
|
||||
<template v-if="row.item.country_code">
|
||||
|
@ -18,16 +18,20 @@
|
|||
:title="row.item.country"
|
||||
:alt="codeToFlag(row.item.country_code).char"
|
||||
:src="codeToFlag(row.item.country_code).url"
|
||||
/>
|
||||
<template v-else>{{
|
||||
codeToFlag(row.item.country_code).char
|
||||
}}</template>
|
||||
>
|
||||
<template v-else>
|
||||
{{
|
||||
codeToFlag(row.item.country_code).char
|
||||
}}
|
||||
</template>
|
||||
</template>
|
||||
{{ row.item.ip }}
|
||||
<span class="grey--text">:{{ row.item.port }}</span>
|
||||
</td>
|
||||
<td>{{ row.item.connection }}</td>
|
||||
<td :title="row.item.flags_desc">{{ row.item.flags }}</td>
|
||||
<td :title="row.item.flags_desc">
|
||||
{{ row.item.flags }}
|
||||
</td>
|
||||
<td>{{ row.item.client }}</td>
|
||||
<td>{{ row.item.progress | progress }}</td>
|
||||
<td>{{ row.item.dl_speed | networkSpeed }}</td>
|
||||
|
@ -48,66 +52,66 @@ import qbit from '@/services/qbit'
|
|||
import { codeToFlag, isWindows } from '@/helpers'
|
||||
import { FullScreenModal } from '@/mixins'
|
||||
export default {
|
||||
name: 'Peers',
|
||||
mixins: [FullScreenModal],
|
||||
props: { hash: String, isActive: Boolean },
|
||||
data: () => ({
|
||||
headers: [
|
||||
{ text: 'IP', value: 'ip' },
|
||||
{ text: 'Connection', value: 'connection' },
|
||||
{ text: 'Flags', value: 'flags' },
|
||||
{ text: 'Client', value: 'client' },
|
||||
{ text: 'Progress', value: 'progress' },
|
||||
{ text: 'DL Speed', value: 'dl_speed' },
|
||||
{ text: 'Downloaded', value: 'downloaded' },
|
||||
{ text: 'UP Speed', value: 'up_speed' },
|
||||
{ text: 'Uploaded', value: 'uploaded' },
|
||||
{ text: 'Relevance', value: 'relevance' },
|
||||
{ text: 'Files', value: 'files' }
|
||||
],
|
||||
peersObj: null
|
||||
}),
|
||||
methods: {
|
||||
codeToFlag(val) {
|
||||
return codeToFlag(val)
|
||||
},
|
||||
isWindows() {
|
||||
return isWindows()
|
||||
},
|
||||
async getTorrentPeers() {
|
||||
const { data } = await qbit.getTorrentPeers(
|
||||
this.hash,
|
||||
this.rid + 1 || undefined
|
||||
)
|
||||
|
||||
this.rid = data.rid
|
||||
|
||||
this.peersObj = data.peers
|
||||
}
|
||||
name: 'Peers',
|
||||
mixins: [FullScreenModal],
|
||||
props: { hash: String, isActive: Boolean },
|
||||
data: () => ({
|
||||
headers: [
|
||||
{ text: 'IP', value: 'ip' },
|
||||
{ text: 'Connection', value: 'connection' },
|
||||
{ text: 'Flags', value: 'flags' },
|
||||
{ text: 'Client', value: 'client' },
|
||||
{ text: 'Progress', value: 'progress' },
|
||||
{ text: 'DL Speed', value: 'dl_speed' },
|
||||
{ text: 'Downloaded', value: 'downloaded' },
|
||||
{ text: 'UP Speed', value: 'up_speed' },
|
||||
{ text: 'Uploaded', value: 'uploaded' },
|
||||
{ text: 'Relevance', value: 'relevance' },
|
||||
{ text: 'Files', value: 'files' }
|
||||
],
|
||||
peersObj: null
|
||||
}),
|
||||
computed: {
|
||||
rid: {
|
||||
get() {
|
||||
return this.$store.state.rid
|
||||
},
|
||||
set(val) {
|
||||
this.$store.state.rid = val
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
isActive(active) {
|
||||
if (active) {
|
||||
this.getTorrentPeers()
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
rid: {
|
||||
get() {
|
||||
return this.$store.state.rid
|
||||
},
|
||||
set(val) {
|
||||
this.$store.state.rid = val
|
||||
}
|
||||
},
|
||||
peers() {
|
||||
return map(this.peersObj, (value, key) => merge({}, value, { key }))
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getTorrentPeers()
|
||||
peers() {
|
||||
return map(this.peersObj, (value, key) => merge({}, value, { key }))
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
isActive(active) {
|
||||
if (active) {
|
||||
this.getTorrentPeers()
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getTorrentPeers()
|
||||
},
|
||||
methods: {
|
||||
codeToFlag(val) {
|
||||
return codeToFlag(val)
|
||||
},
|
||||
isWindows() {
|
||||
return isWindows()
|
||||
},
|
||||
async getTorrentPeers() {
|
||||
const { data } = await qbit.getTorrentPeers(
|
||||
this.hash,
|
||||
this.rid + 1 || undefined
|
||||
)
|
||||
|
||||
this.rid = data.rid
|
||||
|
||||
this.peersObj = data.peers
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -6,7 +6,12 @@
|
|||
:style="{ minHeight: phoneLayout ? '' : '75vh'}"
|
||||
>
|
||||
<v-row>
|
||||
<v-col :cols="12" :lg="6" :md="6" :sm="12">
|
||||
<v-col
|
||||
:cols="12"
|
||||
:lg="6"
|
||||
:md="6"
|
||||
:sm="12"
|
||||
>
|
||||
<v-layout class="mx-auto" row wrap>
|
||||
<v-flex xs12 sm12>
|
||||
<h3>Available Tags:</h3>
|
||||
|
@ -39,14 +44,22 @@
|
|||
style="font-size: 0.95em !important"
|
||||
@click="deleteTag(tag)"
|
||||
@click:close="deleteTag(tag)"
|
||||
>{{ tag }}</v-chip
|
||||
>
|
||||
{{ tag }}
|
||||
</v-chip>
|
||||
</div>
|
||||
<div v-else>
|
||||
None
|
||||
</div>
|
||||
<div v-else>None</div>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-col>
|
||||
<v-col :cols="12" :lg="6" :md="6" :sm="12">
|
||||
<v-col
|
||||
:cols="12"
|
||||
:lg="6"
|
||||
:md="6"
|
||||
:sm="12"
|
||||
>
|
||||
<v-layout
|
||||
class="mx-auto"
|
||||
:class="this.$vuetify.breakpoint.smAndDown ? 'mt-12' : ''"
|
||||
|
@ -82,9 +95,12 @@
|
|||
style="font-size: 0.95em !important"
|
||||
@click="deleteCategory"
|
||||
@click:close="deleteCategory"
|
||||
>{{ torrent.category }}</v-chip
|
||||
>
|
||||
<div v-else>None</div>
|
||||
{{ torrent.category }}
|
||||
</v-chip>
|
||||
<div v-else>
|
||||
None
|
||||
</div>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-col>
|
||||
|
@ -100,45 +116,46 @@ import qbit from '@/services/qbit'
|
|||
import { FullScreenModal } from '@/mixins'
|
||||
|
||||
export default {
|
||||
name: 'TorrentTagsAndCategories',
|
||||
props: {
|
||||
hash: String
|
||||
name: 'TorrentTagsAndCategories',
|
||||
mixins: [FullScreenModal],
|
||||
props: {
|
||||
hash: String
|
||||
},
|
||||
data: () => ({
|
||||
categories: []
|
||||
}),
|
||||
computed: {
|
||||
...mapGetters(['getTorrent', 'getAvailableTags', 'getCategories']),
|
||||
torrent() {
|
||||
return this.getTorrent(this.hash)
|
||||
},
|
||||
mixins: [FullScreenModal],
|
||||
data: () => ({
|
||||
categories: []
|
||||
}),
|
||||
computed: {
|
||||
...mapGetters(['getTorrent', 'getAvailableTags', 'getCategories']),
|
||||
torrent() {
|
||||
return this.getTorrent(this.hash)
|
||||
},
|
||||
availableTags() {
|
||||
const availableTags = this.getAvailableTags()
|
||||
const currentTags = this.getTorrent(this.hash).tags
|
||||
return difference(availableTags, currentTags)
|
||||
},
|
||||
availableCategories() {
|
||||
return this.getCategories()
|
||||
}
|
||||
availableTags() {
|
||||
const availableTags = this.getAvailableTags()
|
||||
const currentTags = this.getTorrent(this.hash).tags
|
||||
|
||||
return difference(availableTags, currentTags)
|
||||
},
|
||||
methods: {
|
||||
addTag(tag) {
|
||||
qbit.addTorrentTag(this.hash, tag)
|
||||
},
|
||||
deleteTag(tag) {
|
||||
qbit.removeTorrentTag(this.hash, tag)
|
||||
},
|
||||
setCategory(cat) {
|
||||
qbit.setCategory(this.hash, cat)
|
||||
},
|
||||
deleteCategory() {
|
||||
qbit.setCategory(this.hash, '')
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$store.commit('FETCH_CATEGORIES')
|
||||
availableCategories() {
|
||||
return this.getCategories()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$store.commit('FETCH_CATEGORIES')
|
||||
},
|
||||
methods: {
|
||||
addTag(tag) {
|
||||
qbit.addTorrentTag(this.hash, tag)
|
||||
},
|
||||
deleteTag(tag) {
|
||||
qbit.removeTorrentTag(this.hash, tag)
|
||||
},
|
||||
setCategory(cat) {
|
||||
qbit.setCategory(this.hash, cat)
|
||||
},
|
||||
deleteCategory() {
|
||||
qbit.setCategory(this.hash, '')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
:hide-default-footer="true"
|
||||
:style="{ minHeight: phoneLayout ? '' : '75vh', maxHeight: '75vh'}"
|
||||
>
|
||||
<template v-slot:item="row">
|
||||
<template #item="row">
|
||||
<tr>
|
||||
<td>{{ row.item.tier }}</td>
|
||||
<td>{{ row.item.url }}</td>
|
||||
|
@ -27,63 +27,63 @@
|
|||
import qbit from '@/services/qbit'
|
||||
import { FullScreenModal } from '@/mixins'
|
||||
export default {
|
||||
name: 'Trackers',
|
||||
mixins: [FullScreenModal],
|
||||
props: { hash: String, isActive: Boolean },
|
||||
data: () => ({
|
||||
headers: [
|
||||
{ text: '#', value: 'tier' },
|
||||
{ text: 'URL', value: 'url' },
|
||||
{ text: 'Status', value: 'status' },
|
||||
{ text: 'Peers', value: 'num_peers' },
|
||||
{ text: 'Seeds', value: 'num_seeds' },
|
||||
{ text: 'Leeches', value: 'num_leeches' },
|
||||
{ text: 'Downloaded', value: 'num_downloaded' },
|
||||
{ text: 'Message', value: 'msg' }
|
||||
],
|
||||
tempTrackers: []
|
||||
}),
|
||||
methods: {
|
||||
async getTorrentTrackers() {
|
||||
const { data } = await qbit.getTorrentTrackers(this.hash)
|
||||
this.tempTrackers = data
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
isActive(active) {
|
||||
if (active) {
|
||||
this.getTorrentTrackers()
|
||||
}
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
formatTrackerStatus(status) {
|
||||
const map = [
|
||||
'Disabled',
|
||||
'Not contacted',
|
||||
'Working',
|
||||
'Updating',
|
||||
'Not working'
|
||||
]
|
||||
name: 'Trackers',
|
||||
filters: {
|
||||
formatTrackerStatus(status) {
|
||||
const map = [
|
||||
'Disabled',
|
||||
'Not contacted',
|
||||
'Working',
|
||||
'Updating',
|
||||
'Not working'
|
||||
]
|
||||
|
||||
return map[status]
|
||||
},
|
||||
formatTrackerNum(num) {
|
||||
if (num === -1) {
|
||||
return 'N/A'
|
||||
}
|
||||
return map[status]
|
||||
},
|
||||
formatTrackerNum(num) {
|
||||
if (num === -1) {
|
||||
return 'N/A'
|
||||
}
|
||||
|
||||
return num.toString()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
trackers() {
|
||||
return this.tempTrackers
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getTorrentTrackers()
|
||||
return num.toString()
|
||||
}
|
||||
},
|
||||
mixins: [FullScreenModal],
|
||||
props: { hash: String, isActive: Boolean },
|
||||
data: () => ({
|
||||
headers: [
|
||||
{ text: '#', value: 'tier' },
|
||||
{ text: 'URL', value: 'url' },
|
||||
{ text: 'Status', value: 'status' },
|
||||
{ text: 'Peers', value: 'num_peers' },
|
||||
{ text: 'Seeds', value: 'num_seeds' },
|
||||
{ text: 'Leeches', value: 'num_leeches' },
|
||||
{ text: 'Downloaded', value: 'num_downloaded' },
|
||||
{ text: 'Message', value: 'msg' }
|
||||
],
|
||||
tempTrackers: []
|
||||
}),
|
||||
computed: {
|
||||
trackers() {
|
||||
return this.tempTrackers
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
isActive(active) {
|
||||
if (active) {
|
||||
this.getTorrentTrackers()
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getTorrentTrackers()
|
||||
},
|
||||
methods: {
|
||||
async getTorrentTrackers() {
|
||||
const { data } = await qbit.getTorrentTrackers(this.hash)
|
||||
this.tempTrackers = data
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,59 +1,83 @@
|
|||
<template>
|
||||
<v-dialog
|
||||
v-model="dialog"
|
||||
scrollable
|
||||
:width="dialogWidth"
|
||||
:fullscreen="phoneLayout"
|
||||
<v-dialog
|
||||
v-model="dialog"
|
||||
scrollable
|
||||
:width="dialogWidth"
|
||||
:fullscreen="phoneLayout"
|
||||
>
|
||||
<v-card
|
||||
v-if="torrent"
|
||||
style="min-height: 400px; overflow: hidden !important"
|
||||
>
|
||||
<v-card
|
||||
v-if="torrent"
|
||||
style="min-height: 400px; overflow: hidden !important"
|
||||
<div
|
||||
:class="`pa-0 project ${torrent.state}`"
|
||||
:style="{ height: phoneLayout ? '100vh' : '' }"
|
||||
>
|
||||
<v-card-title class="pb-0 justify-center primary">
|
||||
<h2 class="white--text">
|
||||
Torrent Detail
|
||||
</h2>
|
||||
</v-card-title>
|
||||
<v-tabs
|
||||
v-model="tab"
|
||||
background-color="primary"
|
||||
dark
|
||||
fixed-tabs
|
||||
>
|
||||
<div
|
||||
:class="`pa-0 project ${torrent.state}`"
|
||||
:style="{ height: phoneLayout ? '100vh' : '' }"
|
||||
>
|
||||
<v-card-title class="pb-0 justify-center primary">
|
||||
<h2 class="white--text">Torrent Detail</h2>
|
||||
</v-card-title>
|
||||
<v-tabs v-model="tab" background-color="primary" dark fixed-tabs>
|
||||
<v-tab href="#info">Info</v-tab>
|
||||
<v-tab href="#trackers">Trackers</v-tab>
|
||||
<v-tab href="#peers">Peers</v-tab>
|
||||
<v-tab href="#content">Content</v-tab>
|
||||
<v-tab href="#tagsAndCategories">Tags & Categories</v-tab>
|
||||
</v-tabs>
|
||||
<v-tabs-items v-model="tab" :touch="updateTab(tab)">
|
||||
<v-tab-item value="info">
|
||||
<info :is-active="tab === 'info'" :hash="hash" />
|
||||
</v-tab-item>
|
||||
<v-tab-item value="trackers">
|
||||
<Trackers
|
||||
:is-active="tab === 'trackers'"
|
||||
:hash="hash"
|
||||
/>
|
||||
</v-tab-item>
|
||||
<v-tab-item value="peers">
|
||||
<Peers :is-active="tab === 'peers'" :hash="hash" />
|
||||
</v-tab-item>
|
||||
<v-tab-item value="content">
|
||||
<Content :is-active="tab === 'content'" :hash="hash" />
|
||||
</v-tab-item>
|
||||
<v-tab-item value="tagsAndCategories">
|
||||
<TagsAndCategories
|
||||
:is-active="tab === 'tagsAndCategories'"
|
||||
:hash="hash"
|
||||
/>
|
||||
</v-tab-item>
|
||||
</v-tabs-items>
|
||||
</div>
|
||||
<v-fab-transition v-if="phoneLayout">
|
||||
<v-btn @click="close" color="red" dark absolute bottom right>
|
||||
<v-icon>close</v-icon>
|
||||
</v-btn>
|
||||
</v-fab-transition>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<v-tab href="#info">
|
||||
Info
|
||||
</v-tab>
|
||||
<v-tab href="#trackers">
|
||||
Trackers
|
||||
</v-tab>
|
||||
<v-tab href="#peers">
|
||||
Peers
|
||||
</v-tab>
|
||||
<v-tab href="#content">
|
||||
Content
|
||||
</v-tab>
|
||||
<v-tab href="#tagsAndCategories">
|
||||
Tags & Categories
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
<v-tabs-items v-model="tab" :touch="updateTab(tab)">
|
||||
<v-tab-item value="info">
|
||||
<info :is-active="tab === 'info'" :hash="hash" />
|
||||
</v-tab-item>
|
||||
<v-tab-item value="trackers">
|
||||
<Trackers
|
||||
:is-active="tab === 'trackers'"
|
||||
:hash="hash"
|
||||
/>
|
||||
</v-tab-item>
|
||||
<v-tab-item value="peers">
|
||||
<Peers :is-active="tab === 'peers'" :hash="hash" />
|
||||
</v-tab-item>
|
||||
<v-tab-item value="content">
|
||||
<Content :is-active="tab === 'content'" :hash="hash" />
|
||||
</v-tab-item>
|
||||
<v-tab-item value="tagsAndCategories">
|
||||
<TagsAndCategories
|
||||
:is-active="tab === 'tagsAndCategories'"
|
||||
:hash="hash"
|
||||
/>
|
||||
</v-tab-item>
|
||||
</v-tabs-items>
|
||||
</div>
|
||||
<v-fab-transition v-if="phoneLayout">
|
||||
<v-btn
|
||||
color="red"
|
||||
dark
|
||||
absolute
|
||||
bottom
|
||||
right
|
||||
@click="close"
|
||||
>
|
||||
<v-icon>close</v-icon>
|
||||
</v-btn>
|
||||
</v-fab-transition>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -63,29 +87,29 @@ import { Modal, FullScreenModal } from '@/mixins'
|
|||
import { Content, Info, Peers, Trackers, TagsAndCategories } from './Tabs'
|
||||
|
||||
export default {
|
||||
name: 'TorrentDetailModal',
|
||||
mixins: [Modal, FullScreenModal],
|
||||
components: { Content, Info, Peers, Trackers, TagsAndCategories },
|
||||
props: {
|
||||
hash: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tab: null,
|
||||
items: [{ tab: 'Info' }, { tab: 'Content' }],
|
||||
peers: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.deleteModal()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getTorrent']),
|
||||
torrent() {
|
||||
return this.getTorrent(this.hash)
|
||||
}
|
||||
name: 'TorrentDetailModal',
|
||||
components: { Content, Info, Peers, Trackers, TagsAndCategories },
|
||||
mixins: [Modal, FullScreenModal],
|
||||
props: {
|
||||
hash: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tab: null,
|
||||
items: [{ tab: 'Info' }, { tab: 'Content' }],
|
||||
peers: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getTorrent']),
|
||||
torrent() {
|
||||
return this.getTorrent(this.hash)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.deleteModal()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,33 +1,37 @@
|
|||
<template>
|
||||
<div v-if="status">
|
||||
<div class="secondary_lighter--text text-uppercase caption ml-4">
|
||||
current speed
|
||||
</div>
|
||||
<v-card color="secondary" flat class="mr-2 ml-2">
|
||||
<v-layout row wrap class="pa-3 project nav_download mx-auto">
|
||||
<v-icon color="download">keyboard_arrow_down</v-icon>
|
||||
<span class="download--text title">
|
||||
{{ status.dlspeed | getDataValue }}
|
||||
<span class="font-weight-light caption">
|
||||
{{ status.dlspeed | getDataUnit }}/s
|
||||
</span>
|
||||
</span>
|
||||
<v-icon class="pl-5" color="upload">keyboard_arrow_up</v-icon>
|
||||
<span class="upload--text title">
|
||||
{{ status.upspeed | getDataValue }}
|
||||
<span class="font-weight-light caption">
|
||||
{{ status.upspeed | getDataUnit }}/s
|
||||
</span>
|
||||
</span>
|
||||
</v-layout>
|
||||
</v-card>
|
||||
<div v-if="status">
|
||||
<div class="secondary_lighter--text text-uppercase caption ml-4">
|
||||
current speed
|
||||
</div>
|
||||
<v-card color="secondary" flat class="mr-2 ml-2">
|
||||
<v-layout row wrap class="pa-3 project nav_download mx-auto">
|
||||
<v-icon color="download">
|
||||
keyboard_arrow_down
|
||||
</v-icon>
|
||||
<span class="download--text title">
|
||||
{{ status.dlspeed | getDataValue }}
|
||||
<span class="font-weight-light caption">
|
||||
{{ status.dlspeed | getDataUnit }}/s
|
||||
</span>
|
||||
</span>
|
||||
<v-icon class="pl-5" color="upload">
|
||||
keyboard_arrow_up
|
||||
</v-icon>
|
||||
<span class="upload--text title">
|
||||
{{ status.upspeed | getDataValue }}
|
||||
<span class="font-weight-light caption">
|
||||
{{ status.upspeed | getDataUnit }}/s
|
||||
</span>
|
||||
</span>
|
||||
</v-layout>
|
||||
</v-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'CurrentSpeed',
|
||||
props: ['status']
|
||||
name: 'CurrentSpeed',
|
||||
props: ['status']
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,150 +1,152 @@
|
|||
<template>
|
||||
<div style="margin-top: 30px">
|
||||
<div class="secondary_lighter--text text-uppercase caption ml-4">
|
||||
Status
|
||||
</div>
|
||||
<v-select
|
||||
:value="selectedState"
|
||||
v-model="selectedState"
|
||||
flat
|
||||
class="ml-2 mr-2"
|
||||
:items="options"
|
||||
item-text="name"
|
||||
item-value="value"
|
||||
dense
|
||||
solo
|
||||
color="download"
|
||||
background-color="secondary"
|
||||
item-color="download"
|
||||
@input="setStatusFilter"
|
||||
height="55"
|
||||
></v-select>
|
||||
<div class="secondary_lighter--text text-uppercase caption ml-4">
|
||||
Category
|
||||
</div>
|
||||
<v-select
|
||||
:value="selectedCategory"
|
||||
v-model="selectedCategory"
|
||||
flat
|
||||
class="ml-2 mr-2"
|
||||
:items="availableCategories"
|
||||
dense
|
||||
solo
|
||||
color="download"
|
||||
background-color="secondary"
|
||||
item-color="download"
|
||||
@input="setCategoryOrTrackerFilter"
|
||||
height="55"
|
||||
></v-select>
|
||||
<div v-if="showTrackerFilter">
|
||||
<div class="secondary_lighter--text text-uppercase caption ml-4">
|
||||
Tracker
|
||||
</div>
|
||||
<v-select
|
||||
:value="selectedTracker"
|
||||
v-model="selectedTracker"
|
||||
flat
|
||||
class="ml-2 mr-2"
|
||||
:items="availableTrackers"
|
||||
dense
|
||||
solo
|
||||
color="download"
|
||||
background-color="secondary"
|
||||
item-color="download"
|
||||
@input="setCategoryOrTrackerFilter"
|
||||
height="55"
|
||||
></v-select>
|
||||
</div>
|
||||
<div
|
||||
style="font-size: 0.9em"
|
||||
class="download--text text-uppercase text-center"
|
||||
>
|
||||
{{ torrentCountString }}
|
||||
</div>
|
||||
<div style="margin-top: 30px">
|
||||
<div class="secondary_lighter--text text-uppercase caption ml-4">
|
||||
Status
|
||||
</div>
|
||||
<v-select
|
||||
v-model="selectedState"
|
||||
:value="selectedState"
|
||||
flat
|
||||
class="ml-2 mr-2"
|
||||
:items="options"
|
||||
item-text="name"
|
||||
item-value="value"
|
||||
dense
|
||||
solo
|
||||
color="download"
|
||||
background-color="secondary"
|
||||
item-color="download"
|
||||
height="55"
|
||||
@input="setStatusFilter"
|
||||
/>
|
||||
<div class="secondary_lighter--text text-uppercase caption ml-4">
|
||||
Category
|
||||
</div>
|
||||
<v-select
|
||||
v-model="selectedCategory"
|
||||
:value="selectedCategory"
|
||||
flat
|
||||
class="ml-2 mr-2"
|
||||
:items="availableCategories"
|
||||
dense
|
||||
solo
|
||||
color="download"
|
||||
background-color="secondary"
|
||||
item-color="download"
|
||||
height="55"
|
||||
@input="setCategoryOrTrackerFilter"
|
||||
/>
|
||||
<div v-if="showTrackerFilter">
|
||||
<div class="secondary_lighter--text text-uppercase caption ml-4">
|
||||
Tracker
|
||||
</div>
|
||||
<v-select
|
||||
v-model="selectedTracker"
|
||||
:value="selectedTracker"
|
||||
flat
|
||||
class="ml-2 mr-2"
|
||||
:items="availableTrackers"
|
||||
dense
|
||||
solo
|
||||
color="download"
|
||||
background-color="secondary"
|
||||
item-color="download"
|
||||
height="55"
|
||||
@input="setCategoryOrTrackerFilter"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
style="font-size: 0.9em"
|
||||
class="download--text text-uppercase text-center"
|
||||
>
|
||||
{{ torrentCountString }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
export default {
|
||||
name: 'FilterSelect',
|
||||
props: ['showTrackerFilter'],
|
||||
data: () => ({
|
||||
options: [
|
||||
{ value: 'all', name: 'All' },
|
||||
{ value: 'downloading', name: 'Downloading' },
|
||||
{ value: 'seeding', name: 'Seeding' },
|
||||
{ value: 'completed', name: 'Completed' },
|
||||
{ value: 'resumed', name: 'Resumed' },
|
||||
{ value: 'paused', name: 'Paused' },
|
||||
{ value: 'active', name: 'Active' },
|
||||
{ value: 'inactive', name: 'Inactive' },
|
||||
{ value: 'stalled', name: 'Stalled' },
|
||||
{ value: 'stalled_uploading', name: 'Stalled Uploading' },
|
||||
{ value: 'stalled_downloading', name: 'Stalled Downloading' },
|
||||
{ value: 'errored', name: 'Errored' }
|
||||
],
|
||||
selectedState: { value: 'all', name: 'All' },
|
||||
selectedCategory: null,
|
||||
selectedTracker: 'All'
|
||||
}),
|
||||
computed: {
|
||||
...mapGetters(['getCategories', 'getTrackers', 'getTorrentCountString']),
|
||||
availableCategories() {
|
||||
const categories = ['All', 'Uncategorized']
|
||||
categories.push(...Object.keys(this.getCategories()))
|
||||
return categories
|
||||
},
|
||||
categoryFilter() {
|
||||
switch (this.selectedCategory) {
|
||||
case 'All':
|
||||
return null
|
||||
case 'Uncategorized':
|
||||
return ''
|
||||
default:
|
||||
return this.selectedCategory
|
||||
}
|
||||
},
|
||||
availableTrackers() {
|
||||
const trackers = ['All', 'Not working']
|
||||
if (this.showTrackerFilter) {
|
||||
trackers.push(...this.getTrackers())
|
||||
}
|
||||
return trackers
|
||||
},
|
||||
trackerFilter() {
|
||||
switch (this.selectedTracker) {
|
||||
case 'All':
|
||||
return null
|
||||
case 'Not working':
|
||||
return ''
|
||||
default:
|
||||
return this.selectedTracker
|
||||
}
|
||||
},
|
||||
torrentCountString() {
|
||||
return this.getTorrentCountString()
|
||||
}
|
||||
name: 'FilterSelect',
|
||||
props: ['showTrackerFilter'],
|
||||
data: () => ({
|
||||
options: [
|
||||
{ value: 'all', name: 'All' },
|
||||
{ value: 'downloading', name: 'Downloading' },
|
||||
{ value: 'seeding', name: 'Seeding' },
|
||||
{ value: 'completed', name: 'Completed' },
|
||||
{ value: 'resumed', name: 'Resumed' },
|
||||
{ value: 'paused', name: 'Paused' },
|
||||
{ value: 'active', name: 'Active' },
|
||||
{ value: 'inactive', name: 'Inactive' },
|
||||
{ value: 'stalled', name: 'Stalled' },
|
||||
{ value: 'stalled_uploading', name: 'Stalled Uploading' },
|
||||
{ value: 'stalled_downloading', name: 'Stalled Downloading' },
|
||||
{ value: 'errored', name: 'Errored' }
|
||||
],
|
||||
selectedState: { value: 'all', name: 'All' },
|
||||
selectedCategory: null,
|
||||
selectedTracker: 'All'
|
||||
}),
|
||||
computed: {
|
||||
...mapGetters(['getCategories', 'getTrackers', 'getTorrentCountString']),
|
||||
availableCategories() {
|
||||
const categories = ['All', 'Uncategorized']
|
||||
categories.push(...Object.keys(this.getCategories()))
|
||||
|
||||
return categories
|
||||
},
|
||||
methods: {
|
||||
setStatusFilter(value) {
|
||||
this.$store.commit('UPDATE_SORT_OPTIONS', {
|
||||
filter: value,
|
||||
category: this.categoryFilter,
|
||||
tracker: this.trackerFilter
|
||||
})
|
||||
},
|
||||
setCategoryOrTrackerFilter() {
|
||||
this.$store.commit('UPDATE_SORT_OPTIONS', {
|
||||
filter: this.selectedState.value,
|
||||
category: this.categoryFilter,
|
||||
tracker: this.trackerFilter
|
||||
})
|
||||
}
|
||||
categoryFilter() {
|
||||
switch (this.selectedCategory) {
|
||||
case 'All':
|
||||
return null
|
||||
case 'Uncategorized':
|
||||
return ''
|
||||
default:
|
||||
return this.selectedCategory
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.selectedCategory = this.availableCategories[0]
|
||||
availableTrackers() {
|
||||
const trackers = ['All', 'Not working']
|
||||
if (this.showTrackerFilter) {
|
||||
trackers.push(...this.getTrackers())
|
||||
}
|
||||
|
||||
return trackers
|
||||
},
|
||||
trackerFilter() {
|
||||
switch (this.selectedTracker) {
|
||||
case 'All':
|
||||
return null
|
||||
case 'Not working':
|
||||
return ''
|
||||
default:
|
||||
return this.selectedTracker
|
||||
}
|
||||
},
|
||||
torrentCountString() {
|
||||
return this.getTorrentCountString()
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.selectedCategory = this.availableCategories[0]
|
||||
},
|
||||
methods: {
|
||||
setStatusFilter(value) {
|
||||
this.$store.commit('UPDATE_SORT_OPTIONS', {
|
||||
filter: value,
|
||||
category: this.categoryFilter,
|
||||
tracker: this.trackerFilter
|
||||
})
|
||||
},
|
||||
setCategoryOrTrackerFilter() {
|
||||
this.$store.commit('UPDATE_SORT_OPTIONS', {
|
||||
filter: this.selectedState.value,
|
||||
category: this.categoryFilter,
|
||||
tracker: this.trackerFilter
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,29 +1,34 @@
|
|||
<template>
|
||||
<v-card flat style="margin-top: 30px" color="secondary" class="ml-2 mr-2">
|
||||
<v-layout row wrap class="pa-3 project nav_upload mx-auto">
|
||||
<v-flex md6>
|
||||
<div
|
||||
style="font-size: 0.95em; margin-top: 6px"
|
||||
class="upload--text"
|
||||
>
|
||||
Free Space
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex v-if="space" md5 class="ml-4">
|
||||
<span class="upload--text title">
|
||||
{{ space | getDataValue }}
|
||||
<span class="font-weight-light caption">
|
||||
{{ space | getDataUnit }}
|
||||
</span>
|
||||
</span>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-card>
|
||||
<v-card
|
||||
flat
|
||||
style="margin-top: 30px"
|
||||
color="secondary"
|
||||
class="ml-2 mr-2"
|
||||
>
|
||||
<v-layout row wrap class="pa-3 project nav_upload mx-auto">
|
||||
<v-flex md6>
|
||||
<div
|
||||
style="font-size: 0.95em; margin-top: 6px"
|
||||
class="upload--text"
|
||||
>
|
||||
Free Space
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex v-if="space" md5 class="ml-4">
|
||||
<span class="upload--text title">
|
||||
{{ space | getDataValue }}
|
||||
<span class="font-weight-light caption">
|
||||
{{ space | getDataUnit }}
|
||||
</span>
|
||||
</span>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'FreeSpace',
|
||||
props: { space: String }
|
||||
name: 'FreeSpace',
|
||||
props: { space: String }
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,104 +1,106 @@
|
|||
<template>
|
||||
<nav>
|
||||
<!--title-->
|
||||
<v-app-bar app flat color="background">
|
||||
<v-app-bar-nav-icon
|
||||
@click.stop="drawer = !drawer"
|
||||
class="grey--text text--lighten-1"
|
||||
></v-app-bar-nav-icon>
|
||||
<v-toolbar-title
|
||||
:class="[
|
||||
'grey--text',
|
||||
{ 'subheading ml-0': $vuetify.breakpoint.smAndDown }
|
||||
]"
|
||||
>
|
||||
<div v-if="!$vuetify.breakpoint.xs">
|
||||
<span class="font-weight-light">Vue</span>
|
||||
<span>Torrent</span>
|
||||
</div>
|
||||
</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<nav class="navbar">
|
||||
<!--title-->
|
||||
<v-app-bar app flat color="background">
|
||||
<v-app-bar-nav-icon
|
||||
class="grey--text text--lighten-1"
|
||||
@click.stop="drawer = !drawer"
|
||||
/>
|
||||
<v-toolbar-title
|
||||
:class="[
|
||||
'grey--text',
|
||||
{ 'subheading ml-0': $vuetify.breakpoint.smAndDown }
|
||||
]"
|
||||
>
|
||||
<div v-if="!$vuetify.breakpoint.xs">
|
||||
<span class="font-weight-light">Vue</span>
|
||||
<span>Torrent</span>
|
||||
</div>
|
||||
</v-toolbar-title>
|
||||
<v-spacer />
|
||||
|
||||
<TopActions :showAll="webuiSettings.showGlobalRemoveResumePause" />
|
||||
</v-app-bar>
|
||||
<!--navigation drawer itself -->
|
||||
<v-navigation-drawer
|
||||
app
|
||||
v-model="drawer"
|
||||
class="primary drawer"
|
||||
style="position: fixed;"
|
||||
width="280"
|
||||
disable-resize-watcher
|
||||
>
|
||||
<v-flex style="height: 98vh; display: flex; flex-direction: column;"
|
||||
class="mt-3" v-if="status">
|
||||
<CurrentSpeed
|
||||
v-if="webuiSettings.showCurrentSpeed"
|
||||
:status="status"
|
||||
/>
|
||||
<TopActions :show-all="webuiSettings.showGlobalRemoveResumePause" />
|
||||
</v-app-bar>
|
||||
<!--navigation drawer itself -->
|
||||
<v-navigation-drawer
|
||||
v-model="drawer"
|
||||
app
|
||||
class="primary drawer"
|
||||
style="position: fixed;"
|
||||
width="280"
|
||||
disable-resize-watcher
|
||||
>
|
||||
<v-flex
|
||||
v-if="status"
|
||||
style="height: 98vh; display: flex; flex-direction: column;"
|
||||
class="mt-3"
|
||||
>
|
||||
<CurrentSpeed
|
||||
v-if="webuiSettings.showCurrentSpeed"
|
||||
:status="status"
|
||||
/>
|
||||
|
||||
<SpeedGraph v-if="webuiSettings.showSpeedGraph" />
|
||||
<SpeedGraph v-if="webuiSettings.showSpeedGraph" />
|
||||
|
||||
<div class="mt-4"></div>
|
||||
<SessionStats
|
||||
v-if="webuiSettings.showSessionStat"
|
||||
:status="status"
|
||||
/>
|
||||
<div class="mt-4" />
|
||||
<SessionStats
|
||||
v-if="webuiSettings.showSessionStat"
|
||||
:status="status"
|
||||
/>
|
||||
|
||||
<FreeSpace
|
||||
v-if="webuiSettings.showFreeSpace"
|
||||
:space="status.freeDiskSpace"
|
||||
/>
|
||||
<FilterSelect :showTrackerFilter="webuiSettings.showTrackerFilter" />
|
||||
<div class="mt-auto">
|
||||
<BottomActions />
|
||||
</div>
|
||||
</v-flex>
|
||||
</v-navigation-drawer>
|
||||
</nav>
|
||||
<FreeSpace
|
||||
v-if="webuiSettings.showFreeSpace"
|
||||
:space="status.freeDiskSpace"
|
||||
/>
|
||||
<FilterSelect :show-tracker-filter="webuiSettings.showTrackerFilter" />
|
||||
<div class="mt-auto">
|
||||
<BottomActions />
|
||||
</div>
|
||||
</v-flex>
|
||||
</v-navigation-drawer>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import {
|
||||
BottomActions,
|
||||
TopActions,
|
||||
SpeedGraph,
|
||||
FreeSpace,
|
||||
SessionStats,
|
||||
CurrentSpeed,
|
||||
FilterSelect
|
||||
BottomActions,
|
||||
TopActions,
|
||||
SpeedGraph,
|
||||
FreeSpace,
|
||||
SessionStats,
|
||||
CurrentSpeed,
|
||||
FilterSelect
|
||||
} from './index'
|
||||
|
||||
export default {
|
||||
name: 'Navbar',
|
||||
components: {
|
||||
FreeSpace,
|
||||
BottomActions,
|
||||
TopActions,
|
||||
SpeedGraph,
|
||||
SessionStats,
|
||||
CurrentSpeed,
|
||||
FilterSelect
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters(['getTheme', 'getWebuiSettings', 'getStatus']),
|
||||
webuiSettings() {
|
||||
return this.getWebuiSettings()
|
||||
},
|
||||
status() {
|
||||
return this.getStatus()
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
drawer: this.$vuetify.breakpoint.mdAndUp
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$vuetify.theme.dark = this.getTheme()
|
||||
name: 'Navbar',
|
||||
components: {
|
||||
FreeSpace,
|
||||
BottomActions,
|
||||
TopActions,
|
||||
SpeedGraph,
|
||||
SessionStats,
|
||||
CurrentSpeed,
|
||||
FilterSelect
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
drawer: this.$vuetify.breakpoint.mdAndUp
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getTheme', 'getWebuiSettings', 'getStatus']),
|
||||
webuiSettings() {
|
||||
return this.getWebuiSettings()
|
||||
},
|
||||
status() {
|
||||
return this.getStatus()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$vuetify.theme.dark = this.getTheme()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -112,4 +114,15 @@ export default {
|
|||
border-radius: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.v-app-bar > .v-toolbar__content{
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
@media screen and (max-width: 480px) {
|
||||
.v-toolbar__title { display: none; }
|
||||
.spacer { display: none; }
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,45 +1,54 @@
|
|||
<template>
|
||||
<v-row justify="space-between">
|
||||
<v-col>
|
||||
<v-tooltip top>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-list-item v-on="on" @click="logout" link>
|
||||
<v-icon class="white--text">exit_to_app</v-icon>
|
||||
</v-list-item>
|
||||
</template>
|
||||
<span>Log out</span>
|
||||
</v-tooltip>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-tooltip top>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-list-item v-on="on" @click="toggleSpeed" link>
|
||||
<v-icon
|
||||
:color="altSpeed ? 'download' : ''"
|
||||
class="white--text"
|
||||
>speed</v-icon
|
||||
>
|
||||
</v-list-item>
|
||||
</template>
|
||||
<span>Alt speeds</span>
|
||||
</v-tooltip>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-tooltip top>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-list-item v-on="on" @click="toggleTheme" link>
|
||||
<v-icon v-if="theme === 'Light'" class="white--text"
|
||||
>brightness_7</v-icon
|
||||
>
|
||||
<v-icon v-else class="white--text"
|
||||
>brightness_2</v-icon
|
||||
>
|
||||
</v-list-item>
|
||||
</template>
|
||||
<span>{{ theme }}</span>
|
||||
</v-tooltip>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row justify="space-between">
|
||||
<v-col>
|
||||
<v-tooltip top>
|
||||
<template #activator="{ on }">
|
||||
<v-list-item link v-on="on" @click="logout">
|
||||
<v-icon class="white--text">
|
||||
exit_to_app
|
||||
</v-icon>
|
||||
</v-list-item>
|
||||
</template>
|
||||
<span>Log out</span>
|
||||
</v-tooltip>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-tooltip top>
|
||||
<template #activator="{ on }">
|
||||
<v-list-item link v-on="on" @click="toggleSpeed">
|
||||
<v-icon
|
||||
:color="altSpeed ? 'download' : ''"
|
||||
class="white--text"
|
||||
>
|
||||
speed
|
||||
</v-icon>
|
||||
</v-list-item>
|
||||
</template>
|
||||
<span>Alt speeds</span>
|
||||
</v-tooltip>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-tooltip top>
|
||||
<template #activator="{ on }">
|
||||
<v-list-item link v-on="on" @click="toggleTheme">
|
||||
<v-icon
|
||||
v-if="theme === 'Light'"
|
||||
class="white--text"
|
||||
>
|
||||
brightness_7
|
||||
</v-icon>
|
||||
<v-icon
|
||||
v-else
|
||||
class="white--text"
|
||||
>
|
||||
brightness_2
|
||||
</v-icon>
|
||||
</v-list-item>
|
||||
</template>
|
||||
<span>{{ theme }}</span>
|
||||
</v-tooltip>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -47,32 +56,32 @@ import qbit from '@/services/qbit'
|
|||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'BottomActions',
|
||||
methods: {
|
||||
logout() {
|
||||
this.$store.commit('LOGOUT')
|
||||
this.$router.push('/login')
|
||||
},
|
||||
toggleSpeed() {
|
||||
qbit.toggleSpeedLimitsMode()
|
||||
},
|
||||
toggleTheme() {
|
||||
this.$store.commit('TOGGLE_THEME')
|
||||
this.$vuetify.theme.dark = !this.$vuetify.theme.dark
|
||||
}
|
||||
name: 'BottomActions',
|
||||
computed: {
|
||||
...mapGetters(['getTheme', 'getStatus']),
|
||||
theme() {
|
||||
return this.getTheme() ? 'Dark' : 'Light'
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getTheme', 'getStatus']),
|
||||
theme() {
|
||||
return this.getTheme() ? 'Dark' : 'Light'
|
||||
},
|
||||
altSpeed() {
|
||||
const status = this.getStatus()
|
||||
if (status && status.altSpeed) return status.altSpeed
|
||||
altSpeed() {
|
||||
const status = this.getStatus()
|
||||
if (status && status.altSpeed) return status.altSpeed
|
||||
|
||||
return null
|
||||
}
|
||||
return null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
logout() {
|
||||
this.$store.commit('LOGOUT')
|
||||
this.$router.push('/login')
|
||||
},
|
||||
toggleSpeed() {
|
||||
qbit.toggleSpeedLimitsMode()
|
||||
},
|
||||
toggleTheme() {
|
||||
this.$store.commit('TOGGLE_THEME')
|
||||
this.$vuetify.theme.dark = !this.$vuetify.theme.dark
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,55 +1,55 @@
|
|||
<template>
|
||||
<div v-if="status">
|
||||
<div class="secondary_lighter--text text-uppercase caption ml-4">
|
||||
session status
|
||||
</div>
|
||||
<v-card flat color="secondary" class="mr-2 ml-2">
|
||||
<v-layout row wrap class="pa-3 project nav_download mx-auto">
|
||||
<v-flex md6>
|
||||
<div
|
||||
style="font-size: 0.95em; margin-top: 6px"
|
||||
class="download--text"
|
||||
>
|
||||
Downloaded
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex md5 class="ml-4">
|
||||
<span class="download--text title">
|
||||
{{ status.downloaded | getDataValue }}
|
||||
<span class="font-weight-light caption">
|
||||
{{ status.downloaded | getDataUnit }}
|
||||
</span>
|
||||
</span>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-card>
|
||||
<v-card flat color="secondary" class="ml-2 mr-2 mt-1">
|
||||
<v-layout row wrap class="pa-3 project nav_upload mx-auto">
|
||||
<v-flex md6>
|
||||
<div
|
||||
style="font-size: 0.95em; margin-top: 6px"
|
||||
class="upload--text"
|
||||
>
|
||||
Uploaded
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex md5 class="ml-4">
|
||||
<span class="upload--text title">
|
||||
{{ status.uploaded | getDataValue }}
|
||||
<span class="font-weight-light caption">
|
||||
{{ status.uploaded | getDataUnit }}
|
||||
</span>
|
||||
</span>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-card>
|
||||
<div v-if="status">
|
||||
<div class="secondary_lighter--text text-uppercase caption ml-4">
|
||||
session status
|
||||
</div>
|
||||
<v-card flat color="secondary" class="mr-2 ml-2">
|
||||
<v-layout row wrap class="pa-3 project nav_download mx-auto">
|
||||
<v-flex md6>
|
||||
<div
|
||||
style="font-size: 0.95em; margin-top: 6px"
|
||||
class="download--text"
|
||||
>
|
||||
Downloaded
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex md5 class="ml-4">
|
||||
<span class="download--text title">
|
||||
{{ status.downloaded | getDataValue }}
|
||||
<span class="font-weight-light caption">
|
||||
{{ status.downloaded | getDataUnit }}
|
||||
</span>
|
||||
</span>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-card>
|
||||
<v-card flat color="secondary" class="ml-2 mr-2 mt-1">
|
||||
<v-layout row wrap class="pa-3 project nav_upload mx-auto">
|
||||
<v-flex md6>
|
||||
<div
|
||||
style="font-size: 0.95em; margin-top: 6px"
|
||||
class="upload--text"
|
||||
>
|
||||
Uploaded
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex md5 class="ml-4">
|
||||
<span class="upload--text title">
|
||||
{{ status.uploaded | getDataValue }}
|
||||
<span class="font-weight-light caption">
|
||||
{{ status.uploaded | getDataUnit }}
|
||||
</span>
|
||||
</span>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SessionStats',
|
||||
props: ['status']
|
||||
name: 'SessionStats',
|
||||
props: ['status']
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,99 +1,100 @@
|
|||
<template>
|
||||
<div class="mt-4">
|
||||
<apexcharts
|
||||
ref="chart"
|
||||
type="line"
|
||||
:options="chartOptions"
|
||||
:series="series"
|
||||
></apexcharts>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<apexcharts
|
||||
ref="chart"
|
||||
type="line"
|
||||
:options="chartOptions"
|
||||
:series="series"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VueApexCharts from 'vue-apexcharts'
|
||||
import { mapGetters } from 'vuex'
|
||||
export default {
|
||||
name: 'SpeedGraph',
|
||||
components: {
|
||||
apexcharts: VueApexCharts
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chartOptions: {
|
||||
chart: {
|
||||
sparkline: {
|
||||
enabled: true
|
||||
},
|
||||
animations: {
|
||||
enabled: false,
|
||||
dynamicAnimation: {
|
||||
speed: 1000
|
||||
}
|
||||
}
|
||||
},
|
||||
colors: ['#00b3fa', '#64CEAA'],
|
||||
stroke: {
|
||||
show: true,
|
||||
curve: 'smooth',
|
||||
lineCap: 'round',
|
||||
width: 4
|
||||
},
|
||||
fill: {
|
||||
type: 'gradient',
|
||||
gradient: {
|
||||
shade: 'dark',
|
||||
type: 'vertical',
|
||||
shadeIntensity: 0.5,
|
||||
opacityFrom: 0.6,
|
||||
opacityTo: 0.5,
|
||||
stops: [0, 50, 100]
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
theme: 'light',
|
||||
x: {
|
||||
formatter: value => {
|
||||
const val = 32 - value * 2
|
||||
return val + ' seconds ago'
|
||||
}
|
||||
}
|
||||
}
|
||||
name: 'SpeedGraph',
|
||||
components: {
|
||||
apexcharts: VueApexCharts
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chartOptions: {
|
||||
chart: {
|
||||
sparkline: {
|
||||
enabled: true
|
||||
},
|
||||
animations: {
|
||||
enabled: false,
|
||||
dynamicAnimation: {
|
||||
speed: 1000
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
series() {
|
||||
return [
|
||||
{
|
||||
name: 'upload',
|
||||
type: 'area',
|
||||
data: this.$store.state.upload_data
|
||||
},
|
||||
{
|
||||
name: 'download',
|
||||
type: 'area',
|
||||
data: this.$store.state.download_data
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
...mapGetters(['getTheme']),
|
||||
theme() {
|
||||
return this.getTheme() ? 'Dark' : 'Light'
|
||||
colors: ['#00b3fa', '#64CEAA'],
|
||||
stroke: {
|
||||
show: true,
|
||||
curve: 'smooth',
|
||||
lineCap: 'round',
|
||||
width: 4
|
||||
},
|
||||
fill: {
|
||||
type: 'gradient',
|
||||
gradient: {
|
||||
shade: 'dark',
|
||||
type: 'vertical',
|
||||
shadeIntensity: 0.5,
|
||||
opacityFrom: 0.6,
|
||||
opacityTo: 0.5,
|
||||
stops: [0, 50, 100]
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
theme: 'light',
|
||||
x: {
|
||||
formatter: value => {
|
||||
const val = 32 - value * 2
|
||||
|
||||
return val + ' seconds ago'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setChartTooltipTheme(theme) {
|
||||
this.chartOptions.tooltip.theme = theme.toLowerCase()
|
||||
this.$refs.chart.updateOptions(this.chartOptions)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
theme(newValue) {
|
||||
this.setChartTooltipTheme(newValue)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.setChartTooltipTheme(this.theme)
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
series() {
|
||||
return [
|
||||
{
|
||||
name: 'upload',
|
||||
type: 'area',
|
||||
data: this.$store.state.upload_data
|
||||
},
|
||||
{
|
||||
name: 'download',
|
||||
type: 'area',
|
||||
data: this.$store.state.download_data
|
||||
}
|
||||
]
|
||||
},
|
||||
...mapGetters(['getTheme']),
|
||||
theme() {
|
||||
return this.getTheme() ? 'Dark' : 'Light'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
theme(newValue) {
|
||||
this.setChartTooltipTheme(newValue)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.setChartTooltipTheme(this.theme)
|
||||
},
|
||||
methods: {
|
||||
setChartTooltipTheme(theme) {
|
||||
this.chartOptions.tooltip.theme = theme.toLowerCase()
|
||||
this.$refs.chart.updateOptions(this.chartOptions)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,83 +1,103 @@
|
|||
<template>
|
||||
<div>
|
||||
<v-btn :ripple="false" id="no-background-hover" text>
|
||||
<v-checkbox
|
||||
class="grey--text"
|
||||
v-model="$store.state.selectMode"
|
||||
color="grey" hide-details
|
||||
style="width: 5px;"
|
||||
/>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
text
|
||||
small
|
||||
fab
|
||||
color="grey"
|
||||
class="mr-0 ml-0"
|
||||
@click="addModal('SortModal')"
|
||||
>
|
||||
<v-icon color="grey">sort</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
text
|
||||
small
|
||||
fab
|
||||
color="grey"
|
||||
class="mr-0 ml-0"
|
||||
@click="addModal('SearchModal')"
|
||||
>
|
||||
<v-icon color="grey">search</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
text
|
||||
small
|
||||
fab
|
||||
color="grey"
|
||||
class="mr-0 ml-0"
|
||||
@click="addModal('AddModal')"
|
||||
>
|
||||
<v-icon color="grey">add</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="showAll"
|
||||
small
|
||||
fab
|
||||
text
|
||||
class="mr-0 ml-0"
|
||||
@click="removeTorrents"
|
||||
>
|
||||
<v-icon color="grey">remove</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="showAll"
|
||||
small
|
||||
fab
|
||||
text
|
||||
class="mr-0 ml-0"
|
||||
@click="resumeTorrents"
|
||||
>
|
||||
<v-icon color="grey">play_arrow</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="showAll"
|
||||
small
|
||||
fab
|
||||
text
|
||||
class="mr-0 ml-0"
|
||||
@click="pauseTorrents"
|
||||
>
|
||||
<v-icon color="grey">pause</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
small
|
||||
fab
|
||||
text
|
||||
class="mr-0 ml-0"
|
||||
@click="addModal('SettingsModal')"
|
||||
>
|
||||
<v-icon color="grey">settings</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
<div class="flex-shrink-0 ml-0">
|
||||
<v-btn
|
||||
id="no-background-hover"
|
||||
class="pa-0"
|
||||
:ripple="false"
|
||||
text
|
||||
>
|
||||
<v-checkbox
|
||||
v-model="$store.state.selectMode"
|
||||
class="grey--text"
|
||||
color="grey"
|
||||
hide-details
|
||||
style="width: 5px;"
|
||||
/>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
text
|
||||
small
|
||||
fab
|
||||
color="grey"
|
||||
class="mr-0 ml-0"
|
||||
@click="addModal('SortModal')"
|
||||
>
|
||||
<v-icon color="grey">
|
||||
sort
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
text
|
||||
small
|
||||
fab
|
||||
color="grey"
|
||||
class="mr-0 ml-0"
|
||||
@click="addModal('SearchModal')"
|
||||
>
|
||||
<v-icon color="grey">
|
||||
search
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
text
|
||||
small
|
||||
fab
|
||||
color="grey"
|
||||
class="mr-0 ml-0"
|
||||
@click="addModal('AddModal')"
|
||||
>
|
||||
<v-icon color="grey">
|
||||
add
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="showAll"
|
||||
small
|
||||
fab
|
||||
text
|
||||
class="mr-0 ml-0"
|
||||
@click="removeTorrents"
|
||||
>
|
||||
<v-icon color="grey">
|
||||
remove
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="showAll"
|
||||
small
|
||||
fab
|
||||
text
|
||||
class="mr-0 ml-0"
|
||||
@click="resumeTorrents"
|
||||
>
|
||||
<v-icon color="grey">
|
||||
play_arrow
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="showAll"
|
||||
small
|
||||
fab
|
||||
text
|
||||
class="mr-0 ml-0"
|
||||
@click="pauseTorrents"
|
||||
>
|
||||
<v-icon color="grey">
|
||||
pause
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
small
|
||||
fab
|
||||
text
|
||||
class="mr-0 ml-0"
|
||||
@click="addModal('SettingsModal')"
|
||||
>
|
||||
<v-icon color="grey">
|
||||
settings
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -86,28 +106,28 @@ import { mapState } from 'vuex'
|
|||
import { General } from '@/mixins'
|
||||
|
||||
export default {
|
||||
name: 'TopActions',
|
||||
mixins: [General],
|
||||
props: ['showAll'],
|
||||
computed: {
|
||||
...mapState(['selected_torrents'])
|
||||
name: 'TopActions',
|
||||
mixins: [General],
|
||||
props: ['showAll'],
|
||||
computed: {
|
||||
...mapState(['selected_torrents'])
|
||||
},
|
||||
methods: {
|
||||
pauseTorrents() {
|
||||
qbit.pauseTorrents(this.selected_torrents)
|
||||
},
|
||||
methods: {
|
||||
pauseTorrents() {
|
||||
qbit.pauseTorrents(this.selected_torrents)
|
||||
},
|
||||
resumeTorrents() {
|
||||
qbit.resumeTorrents(this.selected_torrents)
|
||||
},
|
||||
removeTorrents() {
|
||||
if (!this.selected_torrents.length) return
|
||||
resumeTorrents() {
|
||||
qbit.resumeTorrents(this.selected_torrents)
|
||||
},
|
||||
removeTorrents() {
|
||||
if (!this.selected_torrents.length) return
|
||||
|
||||
return this.createModal('ConfirmDeleteModal')
|
||||
},
|
||||
addModal(name) {
|
||||
this.createModal(name)
|
||||
}
|
||||
return this.createModal('ConfirmDeleteModal')
|
||||
},
|
||||
addModal(name) {
|
||||
this.createModal(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -117,5 +137,7 @@ export default {
|
|||
}
|
||||
#no-background-hover {
|
||||
cursor: default !important;
|
||||
min-width: 1px;
|
||||
margin-right: 2em;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -8,12 +8,12 @@ import CurrentSpeed from './CurrentSpeed.vue'
|
|||
import FilterSelect from './FilterSelect.vue'
|
||||
|
||||
export {
|
||||
Navbar,
|
||||
BottomActions,
|
||||
TopActions,
|
||||
SpeedGraph,
|
||||
FreeSpace,
|
||||
SessionStats,
|
||||
CurrentSpeed,
|
||||
FilterSelect
|
||||
Navbar,
|
||||
BottomActions,
|
||||
TopActions,
|
||||
SpeedGraph,
|
||||
FreeSpace,
|
||||
SessionStats,
|
||||
CurrentSpeed,
|
||||
FilterSelect
|
||||
}
|
||||
|
|
|
@ -1,109 +1,97 @@
|
|||
<template>
|
||||
<v-card
|
||||
ripple
|
||||
flat
|
||||
class="pointer torrent noselect"
|
||||
:class="{ torrent_selected: isAlreadySelected(torrent.hash) }"
|
||||
@click.native.exact.prevent="
|
||||
ripple
|
||||
flat
|
||||
class="pointer torrent noselect"
|
||||
:class="{ torrent_selected: isAlreadySelected(torrent.hash) }"
|
||||
@click.native.exact.prevent="
|
||||
$vuetify.breakpoint.smAndDown ?
|
||||
selectTorrent(torrent.hash) :
|
||||
showInfo(torrent.hash)"
|
||||
@dblclick.prevent="showInfo(torrent.hash)"
|
||||
@click.ctrl.exact.prevent="selectTorrent(torrent.hash)"
|
||||
@click.shift.exact.prevent="selectUntil(torrent.hash, index)"
|
||||
selectTorrent(torrent.hash) :
|
||||
showInfo(torrent.hash)"
|
||||
@dblclick.prevent="showInfo(torrent.hash)"
|
||||
@click.ctrl.exact.prevent="selectTorrent(torrent.hash)"
|
||||
@click.shift.exact.prevent="selectUntil(torrent.hash, index)"
|
||||
>
|
||||
<v-tooltip top>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-layout v-on="denseDashboard && on" row wrap :class="style">
|
||||
<template #activator="{ on }">
|
||||
<v-layout
|
||||
row
|
||||
wrap
|
||||
:class="style"
|
||||
v-on="denseDashboard && on"
|
||||
>
|
||||
<v-flex xs12 :class="denseDashboard ? 'sm3' : ''">
|
||||
<div class="caption grey--text">Torrent title</div>
|
||||
<div class="truncate mr-4">{{ torrent.name }}</div>
|
||||
<div class="caption grey--text">
|
||||
Torrent title
|
||||
</div>
|
||||
<div class="truncate mr-4">
|
||||
{{ torrent.name }}
|
||||
</div>
|
||||
</v-flex>
|
||||
<component
|
||||
:key="'busy' + item.name"
|
||||
:is="item.name"
|
||||
v-for="item in properties"
|
||||
:is="item.name" :torrent="torrent"
|
||||
:key="'busy' + item.name"
|
||||
:torrent="torrent"
|
||||
/>
|
||||
</v-layout>
|
||||
</template>
|
||||
<span>{{ torrent.name }}</span>
|
||||
</v-tooltip>
|
||||
<v-divider v-if="index !== length"></v-divider>
|
||||
<v-divider v-if="index !== length" />
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { General, TorrentSelect } from '@/mixins'
|
||||
import { mapGetters } from 'vuex'
|
||||
import {
|
||||
Size,
|
||||
Progress,
|
||||
Download,
|
||||
Ratio,
|
||||
Upload,
|
||||
ETA,
|
||||
Peers,
|
||||
Seeds,
|
||||
Status,
|
||||
Category,
|
||||
Tags,
|
||||
AddedOn
|
||||
} from './Torrent/DashboardItems'
|
||||
import * as Fields from './Torrent/DashboardItems'
|
||||
|
||||
export default {
|
||||
name: 'Torrent',
|
||||
components: {
|
||||
Size,
|
||||
Progress,
|
||||
Download,
|
||||
Ratio,
|
||||
Upload,
|
||||
ETA,
|
||||
Peers,
|
||||
Seeds,
|
||||
Status,
|
||||
Category,
|
||||
Tags,
|
||||
AddedOn
|
||||
name: 'Torrent',
|
||||
components: {
|
||||
...Fields
|
||||
},
|
||||
mixins: [General, TorrentSelect],
|
||||
props: {
|
||||
torrent: Object,
|
||||
index: Number,
|
||||
length: Number
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getTheme', 'getWebuiSettings']),
|
||||
theme() {
|
||||
return this.getTheme() ? 'dark' : 'light'
|
||||
},
|
||||
mixins: [General, TorrentSelect],
|
||||
props: {
|
||||
torrent: Object,
|
||||
index: Number,
|
||||
length: Number
|
||||
state() {
|
||||
return this.torrent.state.toLowerCase()
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getTheme', 'getWebuiSettings']),
|
||||
theme() {
|
||||
return this.getTheme() ? 'dark' : 'light'
|
||||
},
|
||||
state() {
|
||||
return this.torrent.state.toLowerCase()
|
||||
},
|
||||
style() {
|
||||
let base = `pa-4 ml-0 sideborder ${this.state} `
|
||||
if (this.index === this.length) base += ' bottomBorderRadius'
|
||||
if (this.index === 0) base += ' topBorderRadius'
|
||||
return base
|
||||
},
|
||||
phoneLayout() {
|
||||
return this.$vuetify.breakpoint.xsOnly
|
||||
},
|
||||
denseDashboard() {
|
||||
return this.getWebuiSettings().denseDashboard
|
||||
},
|
||||
properties() {
|
||||
if (this.torrent.progress === 100) {
|
||||
return this.getWebuiSettings().doneTorrentProperties.filter(i => i.active)
|
||||
}
|
||||
return this.getWebuiSettings().busyTorrentProperties.filter(i => i.active)
|
||||
}
|
||||
style() {
|
||||
let base = `ma-0 pa-4 ml-0 sideborder ${this.state} `
|
||||
if (this.index === this.length) base += ' bottomBorderRadius'
|
||||
if (this.index === 0) base += ' topBorderRadius'
|
||||
|
||||
return base
|
||||
},
|
||||
methods: {
|
||||
showInfo(hash) {
|
||||
this.createModal('TorrentDetailModal', { hash })
|
||||
}
|
||||
phoneLayout() {
|
||||
return this.$vuetify.breakpoint.xsOnly
|
||||
},
|
||||
denseDashboard() {
|
||||
return this.getWebuiSettings().denseDashboard
|
||||
},
|
||||
properties() {
|
||||
if (this.torrent.progress === 100) {
|
||||
return this.getWebuiSettings().doneTorrentProperties.filter(i => i.active)
|
||||
}
|
||||
|
||||
return this.getWebuiSettings().busyTorrentProperties.filter(i => i.active)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showInfo(hash) {
|
||||
this.createModal('TorrentDetailModal', { hash })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
<template>
|
||||
<v-flex xs6 sm2>
|
||||
<div class="caption grey--text">Added On</div>
|
||||
<div class="caption grey--text">
|
||||
Added On
|
||||
</div>
|
||||
<div>{{ torrent.added_on }}</div>
|
||||
</v-flex>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'AddedOn',
|
||||
props: ['torrent']
|
||||
name: 'AddedOn',
|
||||
props: ['torrent']
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
<template>
|
||||
<v-flex v-if="torrent.category" xs6 sm1 md1>
|
||||
<div class="caption grey--text">Category</div>
|
||||
<v-flex
|
||||
v-if="torrent.category"
|
||||
xs6
|
||||
sm1
|
||||
md1
|
||||
>
|
||||
<div class="caption grey--text">
|
||||
Category
|
||||
</div>
|
||||
<v-chip small class="upload white--text caption">
|
||||
{{ torrent.category }}
|
||||
</v-chip>
|
||||
|
@ -8,7 +15,7 @@
|
|||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'Category',
|
||||
props: ['torrent']
|
||||
name: 'Category',
|
||||
props: ['torrent']
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
<template>
|
||||
<v-flex xs6 sm1 md1>
|
||||
<div class="caption grey--text">Download</div>
|
||||
<div class="caption grey--text">
|
||||
Download
|
||||
</div>
|
||||
<div>
|
||||
{{ torrent.dlspeed | getDataValue }}
|
||||
<span class="caption grey--text">{{
|
||||
<span class="caption grey--text">
|
||||
{{
|
||||
torrent.dlspeed | getDataUnit
|
||||
}}</span>
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'Download',
|
||||
props: ['torrent']
|
||||
name: 'Download',
|
||||
props: ['torrent']
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<template>
|
||||
<v-flex xs6 sm1 md1>
|
||||
<div class="caption grey--text">ETA</div>
|
||||
<div class="caption grey--text">
|
||||
ETA
|
||||
</div>
|
||||
<div>
|
||||
{{ torrent.eta }}
|
||||
</div>
|
||||
|
@ -8,7 +10,7 @@
|
|||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'ETA',
|
||||
props: ['torrent']
|
||||
name: 'ETA',
|
||||
props: ['torrent']
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
<template>
|
||||
<v-flex xs6 sm1 md1>
|
||||
<div class="caption grey--text">Peers</div>
|
||||
<div class="caption grey--text">
|
||||
Peers
|
||||
</div>
|
||||
<div>
|
||||
{{ torrent.num_leechs }}
|
||||
<span class="grey--text caption"
|
||||
>/{{ torrent.available_peers }}</span
|
||||
<span
|
||||
class="grey--text caption"
|
||||
>
|
||||
/{{ torrent.available_peers }}
|
||||
</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'Peers',
|
||||
props: ['torrent']
|
||||
name: 'Peers',
|
||||
props: ['torrent']
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,24 +1,32 @@
|
|||
<template>
|
||||
<v-flex xs12 sm1 md1 class="mr-4">
|
||||
<div class="caption grey--text">Done</div>
|
||||
<v-progress-linear
|
||||
:value="torrent.progress"
|
||||
height="20"
|
||||
:style="phoneLayout ? '' : 'width: 80%;'"
|
||||
:color="`torrent-${state}-color`" >
|
||||
<span
|
||||
class="caption white--text"
|
||||
>
|
||||
{{ torrent.progress }}%
|
||||
</span>
|
||||
</v-progress-linear>
|
||||
</v-flex>
|
||||
<v-flex
|
||||
xs12
|
||||
sm1
|
||||
md1
|
||||
class="mr-4"
|
||||
>
|
||||
<div class="caption grey--text">
|
||||
Done
|
||||
</div>
|
||||
<v-progress-linear
|
||||
:value="torrent.progress"
|
||||
height="20"
|
||||
:style="phoneLayout ? '' : 'width: 80%;'"
|
||||
:color="`torrent-${state}-color`"
|
||||
>
|
||||
<span
|
||||
class="caption white--text"
|
||||
>
|
||||
{{ torrent.progress }}%
|
||||
</span>
|
||||
</v-progress-linear>
|
||||
</v-flex>
|
||||
</template>
|
||||
<script>
|
||||
import { TorrentDashboardItem } from '@/mixins'
|
||||
export default {
|
||||
name: 'Progress',
|
||||
mixins: [TorrentDashboardItem],
|
||||
props: ['torrent']
|
||||
name: 'Progress',
|
||||
mixins: [TorrentDashboardItem],
|
||||
props: ['torrent']
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
<template>
|
||||
<v-flex xs6 sm1 md1>
|
||||
<div class="caption grey--text">Ratio</div>
|
||||
<div class="caption grey--text">
|
||||
Ratio
|
||||
</div>
|
||||
<div>{{ torrent.ratio }}</div>
|
||||
</v-flex>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'Ratio',
|
||||
props: ['torrent']
|
||||
name: 'Ratio',
|
||||
props: ['torrent']
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
<template>
|
||||
<v-flex xs6 sm1 md1>
|
||||
<div class="caption grey--text">Seeds</div>
|
||||
<div class="caption grey--text">
|
||||
Seeds
|
||||
</div>
|
||||
<div>
|
||||
{{ torrent.num_seeds }}
|
||||
<span class="grey--text caption"
|
||||
>/{{ torrent.available_seeds }}</span
|
||||
<span
|
||||
class="grey--text caption"
|
||||
>
|
||||
/{{ torrent.available_seeds }}
|
||||
</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'Seeds',
|
||||
props: ['torrent']
|
||||
name: 'Seeds',
|
||||
props: ['torrent']
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
<template>
|
||||
<v-flex xs6 sm1 md1>
|
||||
<div class="caption grey--text">Size</div>
|
||||
<div class="caption grey--text">
|
||||
Size
|
||||
</div>
|
||||
<div>
|
||||
{{ torrent.size | getDataValue }}
|
||||
<span class="caption grey--text">{{
|
||||
<span class="caption grey--text">
|
||||
{{
|
||||
torrent.size | getDataUnit
|
||||
}}</span>
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'Size',
|
||||
props: ['torrent']
|
||||
name: 'Size',
|
||||
props: ['torrent']
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<template>
|
||||
<v-flex xs6 sm1>
|
||||
<div class="caption grey--text">Status</div>
|
||||
<div class="caption grey--text">
|
||||
Status
|
||||
</div>
|
||||
<v-chip
|
||||
small
|
||||
class="caption white--text"
|
||||
|
@ -13,8 +15,8 @@
|
|||
<script>
|
||||
import { TorrentDashboardItem } from '@/mixins'
|
||||
export default {
|
||||
name: 'Status',
|
||||
mixins: [TorrentDashboardItem],
|
||||
props: ['torrent']
|
||||
name: 'Status',
|
||||
mixins: [TorrentDashboardItem],
|
||||
props: ['torrent']
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
<template>
|
||||
<v-flex xs6 sm2 v-if="torrent.tags && torrent.tags.length">
|
||||
<div class="caption grey--text">Tags</div>
|
||||
<v-flex v-if="torrent.tags && torrent.tags.length" xs6 sm2>
|
||||
<div class="caption grey--text">
|
||||
Tags
|
||||
</div>
|
||||
<v-row wrap class="ma-0">
|
||||
<v-chip v-for="tag in torrent.tags" :key="tag"
|
||||
small
|
||||
:class="
|
||||
theme === 'light'
|
||||
? 'white--text'
|
||||
: 'black--text'
|
||||
"
|
||||
class="download caption mb-1 mx-1"
|
||||
<v-chip
|
||||
v-for="tag in torrent.tags"
|
||||
:key="tag"
|
||||
small
|
||||
:class="
|
||||
theme === 'light'
|
||||
? 'white--text'
|
||||
: 'black--text'
|
||||
"
|
||||
class="download caption mb-1 mx-1"
|
||||
>
|
||||
{{ tag }}
|
||||
</v-chip>
|
||||
|
@ -19,8 +23,8 @@
|
|||
<script>
|
||||
import { TorrentDashboardItem } from '@/mixins'
|
||||
export default {
|
||||
name: 'Tags',
|
||||
mixins: [TorrentDashboardItem],
|
||||
props: ['torrent']
|
||||
name: 'Tags',
|
||||
mixins: [TorrentDashboardItem],
|
||||
props: ['torrent']
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
<template>
|
||||
<v-flex xs6 sm1 md1>
|
||||
<div class="caption grey--text">Upload</div>
|
||||
<div class="caption grey--text">
|
||||
Upload
|
||||
</div>
|
||||
<div>
|
||||
{{ torrent.upspeed | getDataValue }}
|
||||
<span class="caption grey--text">{{
|
||||
<span class="caption grey--text">
|
||||
{{
|
||||
torrent.upspeed | getDataUnit
|
||||
}}</span>
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'Upload',
|
||||
props: ['torrent']
|
||||
name: 'Upload',
|
||||
props: ['torrent']
|
||||
}
|
||||
</script>
|
||||
|
|
21
src/components/Torrent/DashboardItems/Uploaded.vue
Normal file
21
src/components/Torrent/DashboardItems/Uploaded.vue
Normal file
|
@ -0,0 +1,21 @@
|
|||
<template>
|
||||
<v-flex xs6 sm1 md1>
|
||||
<div class="caption grey--text">
|
||||
Uploaded
|
||||
</div>
|
||||
<div>
|
||||
{{ torrent.uploaded | getDataValue }}
|
||||
<span class="caption grey--text">
|
||||
{{
|
||||
torrent.uploaded | getDataUnit
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'Uploaded',
|
||||
props: ['torrent']
|
||||
}
|
||||
</script>
|
21
src/components/Torrent/DashboardItems/UploadedSession.vue
Normal file
21
src/components/Torrent/DashboardItems/UploadedSession.vue
Normal file
|
@ -0,0 +1,21 @@
|
|||
<template>
|
||||
<v-flex xs6 sm2 md2>
|
||||
<div class="caption grey--text">
|
||||
Uploaded Session
|
||||
</div>
|
||||
<div>
|
||||
{{ torrent.uploaded_session | getDataValue }}
|
||||
<span class="caption grey--text">
|
||||
{{
|
||||
torrent.uploaded_session | getDataUnit
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'UploadedSession',
|
||||
props: ['torrent']
|
||||
}
|
||||
</script>
|
|
@ -10,18 +10,22 @@ import Status from './Status'
|
|||
import Category from './Category'
|
||||
import Tags from './Tags'
|
||||
import AddedOn from './AddedOn'
|
||||
import Uploaded from './Uploaded'
|
||||
import UploadedSession from './UploadedSession'
|
||||
|
||||
export {
|
||||
Size,
|
||||
Progress,
|
||||
Download,
|
||||
Ratio,
|
||||
Upload,
|
||||
ETA,
|
||||
Peers,
|
||||
Seeds,
|
||||
Status,
|
||||
Category,
|
||||
Tags,
|
||||
AddedOn
|
||||
Size,
|
||||
Progress,
|
||||
Download,
|
||||
Ratio,
|
||||
Upload,
|
||||
ETA,
|
||||
Peers,
|
||||
Seeds,
|
||||
Status,
|
||||
Category,
|
||||
Tags,
|
||||
AddedOn,
|
||||
Uploaded,
|
||||
UploadedSession
|
||||
}
|
||||
|
|
|
@ -1,91 +1,129 @@
|
|||
<template>
|
||||
<v-list dense rounded>
|
||||
<v-list-item @click="resume" link>
|
||||
<v-icon >play_arrow</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 12px"
|
||||
>Resume</v-list-item-title
|
||||
<v-list-item link @click="resume">
|
||||
<v-icon>play_arrow</v-icon>
|
||||
<v-list-item-title
|
||||
class="ml-2"
|
||||
style="font-size: 12px"
|
||||
>
|
||||
Resume
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="pause" link>
|
||||
<v-list-item link @click="pause">
|
||||
<v-icon>pause</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 12px"
|
||||
>Pause</v-list-item-title
|
||||
<v-list-item-title
|
||||
class="ml-2"
|
||||
style="font-size: 12px"
|
||||
>
|
||||
Pause
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-divider />
|
||||
<v-list-item @click="deleteWithoutFiles" link>
|
||||
<v-icon color="red">delete</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 12px; color: red"
|
||||
>Delete</v-list-item-title
|
||||
<v-list-item link @click="deleteWithoutFiles">
|
||||
<v-icon color="red">
|
||||
delete
|
||||
</v-icon>
|
||||
<v-list-item-title
|
||||
class="ml-2"
|
||||
style="font-size: 12px; color: red"
|
||||
>
|
||||
Delete
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="deleteWithFiles" link>
|
||||
<v-icon color="red">delete</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 12px; color: red"
|
||||
>Delete with files</v-list-item-title
|
||||
<v-list-item link @click="deleteWithFiles">
|
||||
<v-icon color="red">
|
||||
delete
|
||||
</v-icon>
|
||||
<v-list-item-title
|
||||
class="ml-2"
|
||||
style="font-size: 12px; color: red"
|
||||
>
|
||||
Delete with files
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-divider />
|
||||
<v-list-item @click="location" link>
|
||||
<v-list-item link @click="location">
|
||||
<v-icon>folder</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 12px"
|
||||
>Change location</v-list-item-title
|
||||
<v-list-item-title
|
||||
class="ml-2"
|
||||
style="font-size: 12px"
|
||||
>
|
||||
Change location
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="rename" link>
|
||||
<v-list-item link @click="rename">
|
||||
<v-icon>edit</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 12px"
|
||||
>Rename</v-list-item-title
|
||||
<v-list-item-title
|
||||
class="ml-2"
|
||||
style="font-size: 12px"
|
||||
>
|
||||
Rename
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="recheck" link>
|
||||
<v-list-item link @click="recheck">
|
||||
<v-icon>widgets</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 12px;"
|
||||
>Force recheck</v-list-item-title
|
||||
<v-list-item-title
|
||||
class="ml-2"
|
||||
style="font-size: 12px;"
|
||||
>
|
||||
Force recheck
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="reannounce" link>
|
||||
<v-list-item link @click="reannounce">
|
||||
<v-icon>record_voice_over</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 12px"
|
||||
>Force reannounce</v-list-item-title
|
||||
<v-list-item-title
|
||||
class="ml-2"
|
||||
style="font-size: 12px"
|
||||
>
|
||||
Force reannounce
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-menu
|
||||
open-on-hover
|
||||
top
|
||||
>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-list-item link v-on="on">
|
||||
<v-icon>trending_up</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 12px"
|
||||
>Set Priority</v-list-item-title>
|
||||
</v-list-item>
|
||||
open-on-hover
|
||||
top
|
||||
>
|
||||
<template #activator="{ on }">
|
||||
<v-list-item link v-on="on">
|
||||
<v-icon>trending_up</v-icon>
|
||||
<v-list-item-title
|
||||
class="ml-2"
|
||||
style="font-size: 12px"
|
||||
>
|
||||
Set Priority
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</template>
|
||||
<v-list dense rounded>
|
||||
<v-list-item link
|
||||
v-for="(item, index) in priority_options"
|
||||
:key="index"
|
||||
@click="setPriority(item.action)"
|
||||
>
|
||||
<v-icon>{{ item.icon }}</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 12px">
|
||||
{{ item.name }}
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
<v-list dense rounded>
|
||||
<v-list-item
|
||||
v-for="(item, index) in priority_options"
|
||||
:key="index"
|
||||
link
|
||||
@click="setPriority(item.action)"
|
||||
>
|
||||
<v-icon>{{ item.icon }}</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 12px">
|
||||
{{ item.name }}
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
<v-divider />
|
||||
<v-list-item @click="showInfo" link>
|
||||
<v-list-item link @click="showInfo">
|
||||
<v-icon>info</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 12px"
|
||||
>Show Info</v-list-item-title
|
||||
<v-list-item-title
|
||||
class="ml-2"
|
||||
style="font-size: 12px"
|
||||
>
|
||||
Show Info
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="selectTorrent(hash)" link>
|
||||
<v-list-item link @click="selectTorrent(hash)">
|
||||
<v-icon>done</v-icon>
|
||||
<v-list-item-title class="ml-2"
|
||||
>Select</v-list-item-title
|
||||
<v-list-item-title
|
||||
class="ml-2"
|
||||
>
|
||||
Select
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</template>
|
||||
|
@ -94,55 +132,55 @@
|
|||
import qbit from '@/services/qbit'
|
||||
import { General, TorrentSelect } from '@/mixins'
|
||||
export default {
|
||||
name: 'TorrentRightClickMenu',
|
||||
mixins: [General, TorrentSelect],
|
||||
props: {
|
||||
hash: String
|
||||
},
|
||||
data: () => ({
|
||||
priority_options: [
|
||||
{ name: 'top', icon: 'upgrade', action: 'topPrio' },
|
||||
{ name: 'increase', icon: 'arrow_drop_up', action: 'increasePrio' },
|
||||
{ name: 'decrease', icon: 'arrow_drop_down', action: 'decreasePrio' },
|
||||
{ name: 'bottom', icon: 'vertical_align_bottom', action: 'bottomPrio' }
|
||||
]
|
||||
}),
|
||||
methods: {
|
||||
resume() {
|
||||
qbit.resumeTorrents([this.hash])
|
||||
},
|
||||
pause() {
|
||||
qbit.pauseTorrents([this.hash])
|
||||
},
|
||||
location() {
|
||||
this.createModal('ChangeLocationModal', { hash: this.hash })
|
||||
},
|
||||
rename() {
|
||||
this.createModal('RenameModal', { hash: this.hash })
|
||||
},
|
||||
reannounce() {
|
||||
qbit.reannounceTorrents([this.hash])
|
||||
},
|
||||
deleteWithoutFiles() {
|
||||
qbit.deleteTorrents([this.hash], false)
|
||||
},
|
||||
deleteWithFiles() {
|
||||
qbit.deleteTorrents([this.hash], true)
|
||||
},
|
||||
recheck() {
|
||||
qbit.recheckTorrents([this.hash])
|
||||
},
|
||||
showInfo() {
|
||||
this.createModal('TorrentDetailModal', { hash: this.hash })
|
||||
},
|
||||
setPriority(priority) {
|
||||
qbit.setTorrentPriority(this.hash, priority)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
dark() {
|
||||
return this.$vuetify.dark
|
||||
}
|
||||
name: 'TorrentRightClickMenu',
|
||||
mixins: [General, TorrentSelect],
|
||||
props: {
|
||||
hash: String
|
||||
},
|
||||
data: () => ({
|
||||
priority_options: [
|
||||
{ name: 'top', icon: 'upgrade', action: 'topPrio' },
|
||||
{ name: 'increase', icon: 'arrow_drop_up', action: 'increasePrio' },
|
||||
{ name: 'decrease', icon: 'arrow_drop_down', action: 'decreasePrio' },
|
||||
{ name: 'bottom', icon: 'vertical_align_bottom', action: 'bottomPrio' }
|
||||
]
|
||||
}),
|
||||
computed: {
|
||||
dark() {
|
||||
return this.$vuetify.dark
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resume() {
|
||||
qbit.resumeTorrents([this.hash])
|
||||
},
|
||||
pause() {
|
||||
qbit.pauseTorrents([this.hash])
|
||||
},
|
||||
location() {
|
||||
this.createModal('ChangeLocationModal', { hash: this.hash })
|
||||
},
|
||||
rename() {
|
||||
this.createModal('RenameModal', { hash: this.hash })
|
||||
},
|
||||
reannounce() {
|
||||
qbit.reannounceTorrents([this.hash])
|
||||
},
|
||||
deleteWithoutFiles() {
|
||||
qbit.deleteTorrents([this.hash], false)
|
||||
},
|
||||
deleteWithFiles() {
|
||||
qbit.deleteTorrents([this.hash], true)
|
||||
},
|
||||
recheck() {
|
||||
qbit.recheckTorrents([this.hash])
|
||||
},
|
||||
showInfo() {
|
||||
this.createModal('TorrentDetailModal', { hash: this.hash })
|
||||
},
|
||||
setPriority(priority) {
|
||||
qbit.setTorrentPriority(this.hash, priority)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -3,94 +3,99 @@ import Vue from 'vue'
|
|||
|
||||
/* eslint-disable no-param-reassign */
|
||||
export function toPrecision(value, precision) {
|
||||
if (value >= 10 ** precision) {
|
||||
return value.toString()
|
||||
}
|
||||
if (value >= 1) {
|
||||
return value.toPrecision(precision)
|
||||
}
|
||||
if (value >= 10 ** precision) {
|
||||
return value.toString()
|
||||
}
|
||||
if (value >= 1) {
|
||||
return value.toPrecision(precision)
|
||||
}
|
||||
|
||||
return value.toFixed(precision - 1)
|
||||
return value.toFixed(precision - 1)
|
||||
}
|
||||
|
||||
export function formatSize(value) {
|
||||
const units = 'KMGTP'
|
||||
let index = -1
|
||||
const units = 'KMGTP'
|
||||
let index = -1
|
||||
|
||||
while (value >= 1000) {
|
||||
value /= 1024
|
||||
index++
|
||||
}
|
||||
while (value >= 1000) {
|
||||
value /= 1024
|
||||
index++
|
||||
}
|
||||
|
||||
const unit = index < 0 ? 'B' : `${units[index]}iB`
|
||||
const unit = index < 0 ? 'B' : `${units[index]}iB`
|
||||
|
||||
if (index < 0) {
|
||||
return `${value} ${unit}`
|
||||
}
|
||||
return `${toPrecision(value, 3)} ${unit}`
|
||||
if (index < 0) {
|
||||
return `${value} ${unit}`
|
||||
}
|
||||
|
||||
return `${toPrecision(value, 3)} ${unit}`
|
||||
}
|
||||
|
||||
Vue.filter('formatSize', formatSize)
|
||||
Vue.filter('size', formatSize)
|
||||
|
||||
export function formatTimestamp(timestamp) {
|
||||
if (timestamp == null || timestamp === -1) {
|
||||
return ''
|
||||
}
|
||||
if (timestamp == null || timestamp === -1) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const m = dayjs.unix(timestamp)
|
||||
return m.format('YYYY-MM-DD HH:mm:ss')
|
||||
const m = dayjs.unix(timestamp)
|
||||
|
||||
return m.format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
|
||||
Vue.filter('formatTimestamp', formatTimestamp)
|
||||
|
||||
export function formatProgress(progress) {
|
||||
progress *= 100
|
||||
return `${toPrecision(progress, 3)}%`
|
||||
progress *= 100
|
||||
|
||||
return `${toPrecision(progress, 3)}%`
|
||||
}
|
||||
|
||||
Vue.filter('progress', formatProgress)
|
||||
|
||||
export function parseDate(str) {
|
||||
if (!str) {
|
||||
return null
|
||||
}
|
||||
if (!str) {
|
||||
return null
|
||||
}
|
||||
|
||||
return Date.parse(str) / 1000
|
||||
return Date.parse(str) / 1000
|
||||
}
|
||||
|
||||
Vue.filter('parseDate', parseDate)
|
||||
|
||||
export function formatNetworkSpeed(speed) {
|
||||
if (speed === 0) {
|
||||
return null
|
||||
}
|
||||
if (speed === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return `${formatSize(speed)}/s`
|
||||
return `${formatSize(speed)}/s`
|
||||
}
|
||||
|
||||
Vue.filter('networkSpeed', formatNetworkSpeed)
|
||||
|
||||
export function networkSize(size) {
|
||||
if (size === 0) {
|
||||
return null
|
||||
}
|
||||
if (size === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return formatSize(size)
|
||||
return formatSize(size)
|
||||
}
|
||||
|
||||
Vue.filter('networkSize', networkSize)
|
||||
|
||||
function getDataUnit(value) {
|
||||
if (!value) return ''
|
||||
return value.substring(value.indexOf(' '))
|
||||
if (!value) return ''
|
||||
|
||||
return value.substring(value.indexOf(' '))
|
||||
}
|
||||
|
||||
Vue.filter('getDataUnit', getDataUnit)
|
||||
|
||||
function getDataValue(value) {
|
||||
if (!value) return ''
|
||||
return value.substring(0, value.indexOf(' '))
|
||||
if (!value) return ''
|
||||
|
||||
return value.substring(0, value.indexOf(' '))
|
||||
}
|
||||
|
||||
Vue.filter('getDataValue', getDataValue)
|
||||
|
|
151
src/helpers.js
151
src/helpers.js
|
@ -1,113 +1,116 @@
|
|||
/* eslint-disable no-unused-vars */
|
||||
export function formatBytes(a, b) {
|
||||
if (a == 0) return '0 B'
|
||||
const c = 1024
|
||||
const d = b || 2
|
||||
const e = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
||||
const f = Math.floor(Math.log(a) / Math.log(c))
|
||||
return `${parseFloat((a / Math.pow(c, f)).toFixed(d))} ${e[f]}`
|
||||
if (a == 0) return '0 B'
|
||||
const c = 1024
|
||||
const d = b || 2
|
||||
const e = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
||||
const f = Math.floor(Math.log(a) / Math.log(c))
|
||||
|
||||
return `${parseFloat((a / Math.pow(c, f)).toFixed(d))} ${e[f]}`
|
||||
}
|
||||
|
||||
export function getIconForFileType(type) {
|
||||
const types = {
|
||||
html: 'mdi-language-html5',
|
||||
js: 'mdi-nodejs',
|
||||
json: 'mdi-json',
|
||||
md: 'mdi-markdown',
|
||||
pdf: 'mdi-file-pdf',
|
||||
png: 'mdi-file-image',
|
||||
txt: 'mdi-file-document-outline',
|
||||
sub: 'mdi-file-document-outline',
|
||||
idx: 'mdi-file-document-outline',
|
||||
xls: 'mdi-file-excel',
|
||||
avi: 'movie',
|
||||
mp4: 'movie',
|
||||
mkv: 'movie'
|
||||
}
|
||||
const types = {
|
||||
html: 'mdi-language-html5',
|
||||
js: 'mdi-nodejs',
|
||||
json: 'mdi-json',
|
||||
md: 'mdi-markdown',
|
||||
pdf: 'mdi-file-pdf',
|
||||
png: 'mdi-file-image',
|
||||
txt: 'mdi-file-document-outline',
|
||||
sub: 'mdi-file-document-outline',
|
||||
idx: 'mdi-file-document-outline',
|
||||
xls: 'mdi-file-excel',
|
||||
avi: 'movie',
|
||||
mp4: 'movie',
|
||||
mkv: 'movie'
|
||||
}
|
||||
|
||||
if (!types[type]) return 'insert_drive_file'
|
||||
if (!types[type]) return 'insert_drive_file'
|
||||
|
||||
return types[type]
|
||||
return types[type]
|
||||
}
|
||||
|
||||
export const isWindows = navigator.userAgent.includes('Windows')
|
||||
|
||||
export function codeToFlag(code) {
|
||||
const magicNumber = 0x1f1a5
|
||||
const magicNumber = 0x1f1a5
|
||||
|
||||
// eslint-disable-next-line
|
||||
// eslint-disable-next-line
|
||||
code = code.toUpperCase()
|
||||
const codePoints = [...code].map(c => magicNumber + c.charCodeAt(0))
|
||||
const char = String.fromCodePoint(...codePoints)
|
||||
const url =
|
||||
const codePoints = [...code].map(c => magicNumber + c.charCodeAt(0))
|
||||
const char = String.fromCodePoint(...codePoints)
|
||||
const url =
|
||||
'https://cdn.jsdelivr.net/npm/twemoji/2/svg/' +
|
||||
`${codePoints[0].toString(16)}-${codePoints[1].toString(16)}.svg`
|
||||
|
||||
return {
|
||||
char,
|
||||
url
|
||||
}
|
||||
return {
|
||||
char,
|
||||
url
|
||||
}
|
||||
}
|
||||
|
||||
export function treeify(paths) {
|
||||
let result = []
|
||||
const level = { result }
|
||||
let result = []
|
||||
const level = { result }
|
||||
|
||||
paths.forEach(path => {
|
||||
path.name.split('/').reduce((r, name) => {
|
||||
if (!r[name]) {
|
||||
r[name] = { result: [] }
|
||||
r.result.push(createFile(path, name, r[name].result))
|
||||
}
|
||||
paths.forEach(path => {
|
||||
path.name.split('/').reduce((r, name) => {
|
||||
if (!r[name]) {
|
||||
r[name] = { result: [] }
|
||||
r.result.push(createFile(path, name, r[name].result))
|
||||
}
|
||||
|
||||
return r[name]
|
||||
}, level)
|
||||
})
|
||||
return r[name]
|
||||
}, level)
|
||||
})
|
||||
|
||||
// parse folders
|
||||
result = result.map(el => parseFolder(el))
|
||||
// parse folders
|
||||
result = result.map(el => parseFolder(el))
|
||||
|
||||
function parseFolder(el) {
|
||||
if (el.children.length !== 0) {
|
||||
const folder = createFolder(el.name, el.children)
|
||||
folder.children = folder.children.map(el => parseFolder(el))
|
||||
return folder
|
||||
}
|
||||
return el
|
||||
function parseFolder(el) {
|
||||
if (el.children.length !== 0) {
|
||||
const folder = createFolder(el.name, el.children)
|
||||
folder.children = folder.children.map(el => parseFolder(el))
|
||||
|
||||
return folder
|
||||
}
|
||||
|
||||
return el
|
||||
}
|
||||
|
||||
return result
|
||||
return result
|
||||
}
|
||||
|
||||
function createFile(data, name, children) {
|
||||
return {
|
||||
id: data.id,
|
||||
name: name,
|
||||
fullName: data.name,
|
||||
progress: Math.round(data.progress * 100),
|
||||
size: formatBytes(data.size),
|
||||
icon: getIconForFileType(name.split('.').pop()),
|
||||
children: children
|
||||
}
|
||||
return {
|
||||
id: data.id,
|
||||
name: name,
|
||||
fullName: data.name,
|
||||
progress: Math.round(data.progress * 100),
|
||||
size: formatBytes(data.size),
|
||||
icon: getIconForFileType(name.split('.').pop()),
|
||||
children: children
|
||||
}
|
||||
}
|
||||
|
||||
function createFolder(name, children) {
|
||||
return {
|
||||
name: name,
|
||||
fullName: name,
|
||||
type: 'directory',
|
||||
children: children
|
||||
}
|
||||
return {
|
||||
name: name,
|
||||
fullName: name,
|
||||
type: 'directory',
|
||||
children: children
|
||||
}
|
||||
}
|
||||
|
||||
export function getHostName(url) {
|
||||
const match = url.match(/:\/\/(www[0-9]?\.)?(.[^/:]+)/i)
|
||||
if (match != null &&
|
||||
const match = url.match(/:\/\/(www[0-9]?\.)?(.[^/:]+)/i)
|
||||
if (match != null &&
|
||||
match.length > 2 &&
|
||||
typeof match[2] === 'string' &&
|
||||
match[2].length > 0) {
|
||||
return match[2]
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
return match[2]
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
|
20
src/main.js
20
src/main.js
|
@ -18,8 +18,8 @@ import Toast from 'vue-toastification'
|
|||
import 'vue-toastification/dist/index.css'
|
||||
import vuetify from './plugins/vuetify'
|
||||
Vue.use(Toast, {
|
||||
maxToasts: 5,
|
||||
timeout: 2000
|
||||
maxToasts: 5,
|
||||
timeout: 2000
|
||||
})
|
||||
|
||||
import PerfectScrollbar from 'vue2-perfect-scrollbar'
|
||||
|
@ -34,14 +34,14 @@ Vue.config.productionTip = false
|
|||
// register modals
|
||||
const files = require.context('@/components/Modals', true, /\.vue$/i)
|
||||
files
|
||||
.keys()
|
||||
.map(key =>
|
||||
Vue.component(key.split('/').pop().split('.')[0], files(key).default)
|
||||
)
|
||||
.keys()
|
||||
.map(key =>
|
||||
Vue.component(key.split('/').pop().split('.')[0], files(key).default)
|
||||
)
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
store,
|
||||
vuetify,
|
||||
render: h => h(App)
|
||||
router,
|
||||
store,
|
||||
vuetify,
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
export default {
|
||||
computed: {
|
||||
phoneLayout() {
|
||||
return this.$vuetify.breakpoint.xsOnly
|
||||
},
|
||||
dialogWidth() {
|
||||
return this.phoneLayout ? '100%' : '80%'
|
||||
}
|
||||
computed: {
|
||||
phoneLayout() {
|
||||
return this.$vuetify.breakpoint.xsOnly
|
||||
},
|
||||
watch: {
|
||||
dialog(visible) {
|
||||
if (!visible) {
|
||||
this.tab = null
|
||||
}
|
||||
}
|
||||
dialogWidth() {
|
||||
return this.phoneLayout ? '100%' : '80%'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
dialog(visible) {
|
||||
if (!visible) {
|
||||
this.tab = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { mapGetters } from 'vuex'
|
||||
export default {
|
||||
computed: {
|
||||
...mapGetters(['getTheme']),
|
||||
theme() {
|
||||
return this.getTheme() ? 'dark' : 'light'
|
||||
},
|
||||
background() {
|
||||
return this.$vuetify.theme.themes[this.theme].background
|
||||
}
|
||||
computed: {
|
||||
...mapGetters(['getTheme']),
|
||||
theme() {
|
||||
return this.getTheme() ? 'dark' : 'light'
|
||||
},
|
||||
methods: {
|
||||
createModal(name, props) {
|
||||
const component = {
|
||||
component: name,
|
||||
props,
|
||||
guid: uuidv4()
|
||||
}
|
||||
|
||||
this.$store.commit('ADD_MODAL', component)
|
||||
}
|
||||
background() {
|
||||
return this.$vuetify.theme.themes[this.theme].background
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
createModal(name, props) {
|
||||
const component = {
|
||||
component: name,
|
||||
props,
|
||||
guid: uuidv4()
|
||||
}
|
||||
|
||||
this.$store.commit('ADD_MODAL', component)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
import { mapGetters } from 'vuex'
|
||||
export default {
|
||||
props: ['guid'],
|
||||
computed: {
|
||||
...mapGetters(['getModalState']),
|
||||
dialog: {
|
||||
get() {
|
||||
return this.getModalState(this.guid)
|
||||
},
|
||||
set() {
|
||||
this.deleteModal()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
deleteModal() {
|
||||
setTimeout(() => this.$store.commit('DELETE_MODAL', this.guid), 100)
|
||||
},
|
||||
updateTab(tab) {
|
||||
this.tab = tab
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
props: ['guid'],
|
||||
computed: {
|
||||
...mapGetters(['getModalState']),
|
||||
dialog: {
|
||||
get() {
|
||||
return this.getModalState(this.guid)
|
||||
},
|
||||
set() {
|
||||
this.deleteModal()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
deleteModal() {
|
||||
setTimeout(() => this.$store.commit('DELETE_MODAL', this.guid), 100)
|
||||
},
|
||||
updateTab(tab) {
|
||||
this.tab = tab
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.deleteModal()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
import { mapGetters } from 'vuex'
|
||||
import qbit from '@/services/qbit'
|
||||
import Vue from 'vue'
|
||||
export default {
|
||||
computed: {
|
||||
...mapGetters(['getSettings']),
|
||||
settings() {
|
||||
return this.getSettings()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
saveSettings() {
|
||||
qbit.setPreferences(this.getSettings()).then(() => {
|
||||
Vue.$toast.success('Settings saved successfully!')
|
||||
})
|
||||
this.$store.commit('FETCH_SETTINGS')
|
||||
this.close()
|
||||
}
|
||||
computed: {
|
||||
...mapGetters(['getSettings']),
|
||||
settings() {
|
||||
return this.getSettings()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
saveSettings() {
|
||||
qbit.setPreferences(this.getSettings()).then(() => {
|
||||
Vue.$toast.success('Settings saved successfully!')
|
||||
})
|
||||
this.$store.commit('FETCH_SETTINGS')
|
||||
this.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
export default {
|
||||
props: {
|
||||
hash: String,
|
||||
isActive: Boolean
|
||||
},
|
||||
watch: {
|
||||
isActive(active) {
|
||||
if (active) {
|
||||
this.activeMethod()
|
||||
}
|
||||
}
|
||||
props: {
|
||||
hash: String,
|
||||
isActive: Boolean
|
||||
},
|
||||
watch: {
|
||||
isActive(active) {
|
||||
if (active) {
|
||||
this.activeMethod()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapGetters(['getTheme']),
|
||||
phoneLayout() {
|
||||
return this.$vuetify.breakpoint.xsOnly
|
||||
},
|
||||
theme() {
|
||||
return this.getTheme() ? 'dark' : 'light'
|
||||
},
|
||||
state() {
|
||||
return this.torrent.state.toLowerCase()
|
||||
}
|
||||
computed: {
|
||||
...mapGetters(['getTheme']),
|
||||
phoneLayout() {
|
||||
return this.$vuetify.breakpoint.xsOnly
|
||||
},
|
||||
theme() {
|
||||
return this.getTheme() ? 'dark' : 'light'
|
||||
},
|
||||
state() {
|
||||
return this.torrent.state.toLowerCase()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
export default {
|
||||
methods: {
|
||||
isAlreadySelected(hash) {
|
||||
return this.$store.getters.containsTorrent(hash)
|
||||
},
|
||||
selectTorrent(hash) {
|
||||
if (this.isAlreadySelected(hash)) {
|
||||
this.$store.commit('SET_SELECTED', { type: 'remove', hash })
|
||||
} else {
|
||||
this.$store.commit('SET_SELECTED', { type: 'add', hash })
|
||||
}
|
||||
},
|
||||
selectUntil(hash, index) {
|
||||
this.$store.commit('SET_SELECTED', { type: 'until', hash, index })
|
||||
}
|
||||
methods: {
|
||||
isAlreadySelected(hash) {
|
||||
return this.$store.getters.containsTorrent(hash)
|
||||
},
|
||||
selectTorrent(hash) {
|
||||
if (this.isAlreadySelected(hash)) {
|
||||
this.$store.commit('SET_SELECTED', { type: 'remove', hash })
|
||||
} else {
|
||||
this.$store.commit('SET_SELECTED', { type: 'add', hash })
|
||||
}
|
||||
},
|
||||
selectUntil(hash, index) {
|
||||
this.$store.commit('SET_SELECTED', { type: 'until', hash, index })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,10 +7,10 @@ import TorrentSelect from './TorrentSelect'
|
|||
import TorrentDashboardItem from './TorrentDashboardItem'
|
||||
|
||||
export { FullScreenModal,
|
||||
Modal,
|
||||
SettingsTab,
|
||||
Tab,
|
||||
General,
|
||||
TorrentSelect,
|
||||
TorrentDashboardItem
|
||||
Modal,
|
||||
SettingsTab,
|
||||
Tab,
|
||||
General,
|
||||
TorrentSelect,
|
||||
TorrentDashboardItem
|
||||
}
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
import { formatBytes } from '../helpers'
|
||||
|
||||
export default class Status {
|
||||
constructor(data, tags) {
|
||||
if (data != undefined && data != null) {
|
||||
this.status = data.connection_status
|
||||
this.downloaded = formatBytes(data.dl_info_data, 1)
|
||||
this.uploaded = formatBytes(data.up_info_data, 1)
|
||||
this.dlspeed = formatBytes(data.dl_info_speed, 1)
|
||||
this.upspeed = formatBytes(data.up_info_speed, 1)
|
||||
this.freeDiskSpace = formatBytes(data.free_space_on_disk)
|
||||
this.altSpeed = data.use_alt_speed_limits
|
||||
this.dlspeedRaw = this.formatSpeed(data.dl_info_speed)
|
||||
this.upspeedRaw = this.formatSpeed(data.up_info_speed)
|
||||
this.tags = tags
|
||||
}
|
||||
constructor(data, tags) {
|
||||
if (data != undefined && data != null) {
|
||||
this.status = data.connection_status
|
||||
this.downloaded = formatBytes(data.dl_info_data, 1)
|
||||
this.uploaded = formatBytes(data.up_info_data, 1)
|
||||
this.dlspeed = formatBytes(data.dl_info_speed, 1)
|
||||
this.upspeed = formatBytes(data.up_info_speed, 1)
|
||||
this.freeDiskSpace = formatBytes(data.free_space_on_disk)
|
||||
this.altSpeed = data.use_alt_speed_limits
|
||||
this.dlspeedRaw = this.formatSpeed(data.dl_info_speed)
|
||||
this.upspeedRaw = this.formatSpeed(data.up_info_speed)
|
||||
this.tags = tags
|
||||
}
|
||||
}
|
||||
|
||||
formatSpeed(value) {
|
||||
if (!value) {
|
||||
return 0
|
||||
}
|
||||
|
||||
formatSpeed(value) {
|
||||
if (!value) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return Math.round(value / 1000)
|
||||
}
|
||||
return Math.round(value / 1000)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,123 +1,124 @@
|
|||
import { formatBytes } from '../helpers'
|
||||
|
||||
export default class Torrent {
|
||||
constructor(data) {
|
||||
this.name = data.name
|
||||
this.size = formatBytes(data.size)
|
||||
this.added_on = new Date(data.added_on * 1000).toLocaleString()
|
||||
this.dlspeed = formatBytes(data.dlspeed, 1) + '/s'
|
||||
this.dloaded = formatBytes(data.completed)
|
||||
this.upspeed = formatBytes(data.upspeed, 1) + '/s'
|
||||
this.uploaded = formatBytes(data.uploaded)
|
||||
this.eta = this.formatEta(data.eta)
|
||||
this.num_leechs = data.num_leechs
|
||||
this.num_seeds = data.num_seeds
|
||||
this.path = data.path === undefined ? '/downloads' : data.path
|
||||
this.state = this.formatState(data.state)
|
||||
// hash is used to identify
|
||||
this.hash = data.hash
|
||||
// available seeds
|
||||
this.available_seeds = data.num_complete
|
||||
this.available_peers = data.num_incomplete
|
||||
this.savePath = data.save_path
|
||||
this.progress = Math.round(data.progress * 10000) / 100
|
||||
this.ratio = Math.round(data.ratio * 100) / 100
|
||||
this.tags = data.tags.length > 0 ? data.tags.split(',') : null
|
||||
this.category = data.category
|
||||
this.tracker = data.tracker
|
||||
this.comment = data.comment
|
||||
constructor(data) {
|
||||
this.name = data.name
|
||||
this.size = formatBytes(data.size)
|
||||
this.added_on = new Date(data.added_on * 1000).toLocaleString()
|
||||
this.dlspeed = formatBytes(data.dlspeed, 1) + '/s'
|
||||
this.dloaded = formatBytes(data.completed)
|
||||
this.upspeed = formatBytes(data.upspeed, 1) + '/s'
|
||||
this.uploaded = formatBytes(data.uploaded)
|
||||
this.uploaded_session = formatBytes(data.uploaded_session)
|
||||
this.eta = this.formatEta(data.eta)
|
||||
this.num_leechs = data.num_leechs
|
||||
this.num_seeds = data.num_seeds
|
||||
this.path = data.path === undefined ? '/downloads' : data.path
|
||||
this.state = this.formatState(data.state)
|
||||
// hash is used to identify
|
||||
this.hash = data.hash
|
||||
// available seeds
|
||||
this.available_seeds = data.num_complete
|
||||
this.available_peers = data.num_incomplete
|
||||
this.savePath = data.save_path
|
||||
this.progress = Math.round(data.progress * 10000) / 100
|
||||
this.ratio = Math.round(data.ratio * 100) / 100
|
||||
this.tags = data.tags.length > 0 ? data.tags.split(',') : null
|
||||
this.category = data.category
|
||||
this.tracker = data.tracker
|
||||
this.comment = data.comment
|
||||
}
|
||||
|
||||
formatState(state) {
|
||||
switch (state) {
|
||||
case 'forceDL':
|
||||
case 'downloading':
|
||||
return 'Downloading'
|
||||
case 'metaDL':
|
||||
return 'Metadata'
|
||||
case 'forcedUP':
|
||||
case 'uploading':
|
||||
case 'stalledUP':
|
||||
return 'Seeding'
|
||||
case 'pausedDL':
|
||||
return 'Paused'
|
||||
case 'pausedUP':
|
||||
return 'Done'
|
||||
case 'queuedDL':
|
||||
case 'queuedUP':
|
||||
return 'Queued'
|
||||
case 'allocating':
|
||||
case 'checkingDL':
|
||||
case 'checkingUP':
|
||||
case 'checkingResumeData':
|
||||
return 'Checking'
|
||||
case 'moving':
|
||||
return 'Moving'
|
||||
case 'unknown':
|
||||
case 'missingFiles':
|
||||
return 'Fail'
|
||||
case 'stalledDL':
|
||||
return 'Stalled'
|
||||
default:
|
||||
return 'Fail'
|
||||
}
|
||||
}
|
||||
|
||||
formatEta(value) {
|
||||
const options = { dayLimit: 100 }
|
||||
const minute = 60
|
||||
const hour = minute * 60
|
||||
const day = hour * 24
|
||||
const year = day * 365
|
||||
|
||||
const durations = [year, day, hour, minute, 1]
|
||||
const units = 'ydhms'
|
||||
|
||||
let index = 0
|
||||
let unitSize = 0
|
||||
const parts = []
|
||||
|
||||
const defaultOptions = {
|
||||
maxUnitSize: 2,
|
||||
dayLimit: 0,
|
||||
minUnit: 0
|
||||
}
|
||||
|
||||
formatState(state) {
|
||||
switch (state) {
|
||||
case 'forceDL':
|
||||
case 'downloading':
|
||||
return 'Downloading'
|
||||
case 'metaDL':
|
||||
return 'Metadata'
|
||||
case 'forcedUP':
|
||||
case 'uploading':
|
||||
case 'stalledUP':
|
||||
return 'Seeding'
|
||||
case 'pausedDL':
|
||||
return 'Paused'
|
||||
case 'pausedUP':
|
||||
return 'Done'
|
||||
case 'queuedDL':
|
||||
case 'queuedUP':
|
||||
return 'Queued'
|
||||
case 'allocating':
|
||||
case 'checkingDL':
|
||||
case 'checkingUP':
|
||||
case 'checkingResumeData':
|
||||
return 'Checking'
|
||||
case 'moving':
|
||||
return 'Moving'
|
||||
case 'unknown':
|
||||
case 'missingFiles':
|
||||
return 'Fail'
|
||||
case 'stalledDL':
|
||||
return 'Stalled'
|
||||
default:
|
||||
return 'Fail'
|
||||
}
|
||||
const opt = options ?
|
||||
Object.assign(defaultOptions, options) :
|
||||
defaultOptions
|
||||
|
||||
if (opt.dayLimit && value >= opt.dayLimit * day) {
|
||||
return '∞'
|
||||
}
|
||||
|
||||
formatEta(value) {
|
||||
const options = { dayLimit: 100 }
|
||||
const minute = 60
|
||||
const hour = minute * 60
|
||||
const day = hour * 24
|
||||
const year = day * 365
|
||||
|
||||
const durations = [year, day, hour, minute, 1]
|
||||
const units = 'ydhms'
|
||||
|
||||
let index = 0
|
||||
let unitSize = 0
|
||||
const parts = []
|
||||
|
||||
const defaultOptions = {
|
||||
maxUnitSize: 2,
|
||||
dayLimit: 0,
|
||||
minUnit: 0
|
||||
}
|
||||
|
||||
const opt = options ?
|
||||
Object.assign(defaultOptions, options) :
|
||||
defaultOptions
|
||||
|
||||
if (opt.dayLimit && value >= opt.dayLimit * day) {
|
||||
return '∞'
|
||||
}
|
||||
|
||||
while (
|
||||
(!opt.maxUnitSize || unitSize !== opt.maxUnitSize) &&
|
||||
while (
|
||||
(!opt.maxUnitSize || unitSize !== opt.maxUnitSize) &&
|
||||
index !== durations.length
|
||||
) {
|
||||
const duration = durations[index]
|
||||
if (value < duration) {
|
||||
index++
|
||||
continue
|
||||
} else if (
|
||||
opt.minUnit &&
|
||||
) {
|
||||
const duration = durations[index]
|
||||
if (value < duration) {
|
||||
index++
|
||||
continue
|
||||
} else if (
|
||||
opt.minUnit &&
|
||||
durations.length - index <= opt.minUnit
|
||||
) {
|
||||
break
|
||||
}
|
||||
) {
|
||||
break
|
||||
}
|
||||
|
||||
const result = Math.floor(value / duration)
|
||||
parts.push(result + units[index])
|
||||
const result = Math.floor(value / duration)
|
||||
parts.push(result + units[index])
|
||||
|
||||
value %= duration
|
||||
index++
|
||||
unitSize++
|
||||
}
|
||||
|
||||
if (!parts.length) {
|
||||
return '0' + units[durations.length - 1 - opt.minUnit]
|
||||
}
|
||||
|
||||
return parts.join(' ')
|
||||
value %= duration
|
||||
index++
|
||||
unitSize++
|
||||
}
|
||||
|
||||
if (!parts.length) {
|
||||
return '0' + units[durations.length - 1 - opt.minUnit]
|
||||
}
|
||||
|
||||
return parts.join(' ')
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,48 +8,48 @@ import variables from '../styles/variables.scss'
|
|||
Vue.use(Vuetify)
|
||||
|
||||
export default new Vuetify({
|
||||
theme: {
|
||||
options: {
|
||||
customProperties: true
|
||||
},
|
||||
dark: false,
|
||||
themes: {
|
||||
light: {
|
||||
primary: '#35495e',
|
||||
secondary: '#3e556d',
|
||||
secondary_lighter: '#56718c',
|
||||
blue_accent: '#3cd1c2',
|
||||
info: '#ffaa2c',
|
||||
error: '#f83e70',
|
||||
green_accent: '#3cd1c2',
|
||||
download: '#64CEAA',
|
||||
upload: '#00b3fa',
|
||||
torrent: '#fff',
|
||||
torrent_selected: colors.grey.lighten2,
|
||||
background: colors.grey.lighten4,
|
||||
search: colors.grey.darken1,
|
||||
torrentmodaltext: colors.grey.darken4,
|
||||
select: colors.grey.lighten4,
|
||||
...variables
|
||||
},
|
||||
dark: {
|
||||
primary: '#35495e',
|
||||
secondary: '#3e556d',
|
||||
secondary_lighter: '#56718c',
|
||||
blue_accent: '#3cd1c2',
|
||||
info: '#ffaa2c',
|
||||
error: '#f83e70',
|
||||
green_accent: '#3cd1c2',
|
||||
download: '#64CEAA',
|
||||
upload: '#00b3fa',
|
||||
torrent: colors.grey.darken3,
|
||||
torrent_selected: colors.blueGrey.darken1,
|
||||
background: colors.grey.darken4,
|
||||
search: colors.grey.darken3,
|
||||
torrentmodaltext: colors.grey.lighten4,
|
||||
select: colors.grey.darken3,
|
||||
...variables
|
||||
}
|
||||
}
|
||||
theme: {
|
||||
options: {
|
||||
customProperties: true
|
||||
},
|
||||
dark: false,
|
||||
themes: {
|
||||
light: {
|
||||
primary: '#35495e',
|
||||
secondary: '#3e556d',
|
||||
secondary_lighter: '#56718c',
|
||||
blue_accent: '#3cd1c2',
|
||||
info: '#ffaa2c',
|
||||
error: '#f83e70',
|
||||
green_accent: '#3cd1c2',
|
||||
download: '#64CEAA',
|
||||
upload: '#00b3fa',
|
||||
torrent: '#fff',
|
||||
torrent_selected: colors.grey.lighten2,
|
||||
background: colors.grey.lighten4,
|
||||
search: colors.grey.darken1,
|
||||
torrentmodaltext: colors.grey.darken4,
|
||||
select: colors.grey.lighten4,
|
||||
...variables
|
||||
},
|
||||
dark: {
|
||||
primary: '#35495e',
|
||||
secondary: '#3e556d',
|
||||
secondary_lighter: '#56718c',
|
||||
blue_accent: '#3cd1c2',
|
||||
info: '#ffaa2c',
|
||||
error: '#f83e70',
|
||||
green_accent: '#3cd1c2',
|
||||
download: '#64CEAA',
|
||||
upload: '#00b3fa',
|
||||
torrent: colors.grey.darken3,
|
||||
torrent_selected: colors.blueGrey.darken1,
|
||||
background: colors.grey.darken4,
|
||||
search: colors.grey.darken3,
|
||||
torrentmodaltext: colors.grey.lighten4,
|
||||
select: colors.grey.darken3,
|
||||
...variables
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -3,32 +3,32 @@
|
|||
import { register } from 'register-service-worker'
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
register(`${process.env.BASE_URL}service-worker.js`, {
|
||||
ready() {
|
||||
console.log(
|
||||
'App is being served from cache by a service worker.\n' +
|
||||
register(`${process.env.BASE_URL}service-worker.js`, {
|
||||
ready() {
|
||||
console.log(
|
||||
'App is being served from cache by a service worker.\n' +
|
||||
'For more details, visit https://goo.gl/AFskqB'
|
||||
)
|
||||
},
|
||||
registered() {
|
||||
console.log('Service worker has been registered.')
|
||||
},
|
||||
cached() {
|
||||
console.log('Content has been cached for offline use.')
|
||||
},
|
||||
updatefound() {
|
||||
console.log('New content is downloading.')
|
||||
},
|
||||
updated() {
|
||||
console.log('New content is available; please refresh.')
|
||||
},
|
||||
offline() {
|
||||
console.log(
|
||||
'No internet connection found. App is running in offline mode.'
|
||||
)
|
||||
},
|
||||
error(error) {
|
||||
console.error('Error during service worker registration:', error)
|
||||
}
|
||||
})
|
||||
)
|
||||
},
|
||||
registered() {
|
||||
console.log('Service worker has been registered.')
|
||||
},
|
||||
cached() {
|
||||
console.log('Content has been cached for offline use.')
|
||||
},
|
||||
updatefound() {
|
||||
console.log('New content is downloading.')
|
||||
},
|
||||
updated() {
|
||||
console.log('New content is available; please refresh.')
|
||||
},
|
||||
offline() {
|
||||
console.log(
|
||||
'No internet connection found. App is running in offline mode.'
|
||||
)
|
||||
},
|
||||
error(error) {
|
||||
console.error('Error during service worker registration:', error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -7,47 +7,47 @@ import { isAuthenticated } from '@/services/auth.js'
|
|||
Vue.use(Router)
|
||||
|
||||
const router = new Router({
|
||||
base: process.env.BASE_URL,
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'dashboard',
|
||||
component: Dashboard
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: Login,
|
||||
meta: {
|
||||
public: true, // Allow access to even if not logged in
|
||||
onlyWhenLoggedOut: true
|
||||
}
|
||||
}
|
||||
]
|
||||
base: process.env.BASE_URL,
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'dashboard',
|
||||
component: Dashboard
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: Login,
|
||||
meta: {
|
||||
public: true, // Allow access to even if not logged in
|
||||
onlyWhenLoggedOut: true
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
const isPublic = to.matched.some(record => record.meta.public)
|
||||
const onlyWhenLoggedOut = to.matched.some(
|
||||
record => record.meta.onlyWhenLoggedOut
|
||||
)
|
||||
const authenticated = isAuthenticated()
|
||||
const isPublic = to.matched.some(record => record.meta.public)
|
||||
const onlyWhenLoggedOut = to.matched.some(
|
||||
record => record.meta.onlyWhenLoggedOut
|
||||
)
|
||||
const authenticated = isAuthenticated()
|
||||
|
||||
if (!isPublic && !authenticated) {
|
||||
return next({
|
||||
path: '/login',
|
||||
// Store the full path to redirect the user to after login
|
||||
query: { redirect: to.fullPath }
|
||||
})
|
||||
}
|
||||
if (!isPublic && !authenticated) {
|
||||
return next({
|
||||
path: '/login',
|
||||
// Store the full path to redirect the user to after login
|
||||
query: { redirect: to.fullPath }
|
||||
})
|
||||
}
|
||||
|
||||
// Do not allow user to visit login page or register page
|
||||
// if they are logged in
|
||||
if (authenticated && onlyWhenLoggedOut) {
|
||||
return next('/')
|
||||
}
|
||||
// Do not allow user to visit login page or register page
|
||||
// if they are logged in
|
||||
if (authenticated && onlyWhenLoggedOut) {
|
||||
return next('/')
|
||||
}
|
||||
|
||||
next()
|
||||
next()
|
||||
})
|
||||
|
||||
export default router
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import store from '../store'
|
||||
|
||||
export function isAuthenticated() {
|
||||
return store.state.authenticated
|
||||
return store.state.authenticated
|
||||
}
|
||||
|
|
|
@ -1,338 +1,349 @@
|
|||
import axios from 'axios'
|
||||
|
||||
class Qbit {
|
||||
constructor() {
|
||||
this.axios = axios.create({
|
||||
baseURL: 'api/v2'
|
||||
})
|
||||
constructor() {
|
||||
this.axios = axios.create({
|
||||
baseURL: 'api/v2'
|
||||
})
|
||||
|
||||
this.axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
|
||||
this.axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
|
||||
}
|
||||
|
||||
execute(method, action, params) {
|
||||
if (method === 'post') {
|
||||
const data = new URLSearchParams(params)
|
||||
|
||||
return this.axios.post(action, data).then(res => res.data)
|
||||
}
|
||||
}
|
||||
|
||||
/** Begin General functions * */
|
||||
|
||||
getAppVersion() {
|
||||
return this.axios.get('/app/version')
|
||||
.then(res => res.data)
|
||||
}
|
||||
|
||||
getApiVersion() {
|
||||
return this.axios.get('/app/webapiVersion')
|
||||
}
|
||||
|
||||
async login(params) {
|
||||
const payload = new URLSearchParams(params)
|
||||
const { data } = await this.axios.post('/auth/login', payload, {
|
||||
validateStatus(status) {
|
||||
return status === 200 || status === 403
|
||||
}
|
||||
})
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
async logout() {
|
||||
this.axios.post('/auth/logout')
|
||||
}
|
||||
|
||||
getGlobalTransferInfo() {
|
||||
return this.axios.get('/transfer/info')
|
||||
}
|
||||
|
||||
getAppPreferences() {
|
||||
return this.axios.get('/app/preferences')
|
||||
}
|
||||
|
||||
setPreferences(params) {
|
||||
const data = new URLSearchParams({
|
||||
json: JSON.stringify(params)
|
||||
})
|
||||
|
||||
return this.axios.post('/app/setPreferences', data)
|
||||
}
|
||||
|
||||
getMainData(rid) {
|
||||
return this.axios.get(
|
||||
'/sync/maindata', { params: { rid } })
|
||||
}
|
||||
|
||||
switchToOldUi() {
|
||||
return this.setPreferences({
|
||||
alternative_webui_enabled: false
|
||||
})
|
||||
}
|
||||
|
||||
toggleSpeedLimitsMode() {
|
||||
return this.axios.post('/transfer/toggleSpeedLimitsMode')
|
||||
}
|
||||
|
||||
/** Begin Torrent functions * */
|
||||
|
||||
// Get
|
||||
|
||||
getLogs(lastId) {
|
||||
return this.axios.get('/log/main', {
|
||||
last_known_id: lastId
|
||||
})
|
||||
}
|
||||
|
||||
getTorrents(payload) {
|
||||
const params = {
|
||||
sort: payload.sort,
|
||||
reverse: payload.reverse,
|
||||
hashes: payload.hashes ? payload.hashes.join('|') : null,
|
||||
filter: payload.filter ? payload.filter : null,
|
||||
category: payload.category !== null ? payload.category : null
|
||||
}
|
||||
|
||||
execute(method, action, params) {
|
||||
if (method === 'post') {
|
||||
const data = new URLSearchParams(params)
|
||||
return this.axios.post(action, data).then(res => res.data)
|
||||
// clean
|
||||
Object.keys(params).forEach(
|
||||
key => params[key] == null && delete params[key]
|
||||
)
|
||||
|
||||
const data = new URLSearchParams(params)
|
||||
|
||||
return this.axios.get(`/torrents/info?${data.toString()}`)
|
||||
}
|
||||
|
||||
getTorrentTrackers(hash) {
|
||||
return this.axios.get('/torrents/trackers', {
|
||||
params: { hash }
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
getTorrentPeers(hash, rid) {
|
||||
return this.axios.get('/sync/torrentPeers', {
|
||||
params: { hash, rid }
|
||||
})
|
||||
}
|
||||
|
||||
setTorrentName(hash, name) {
|
||||
const params = {
|
||||
hash,
|
||||
name
|
||||
}
|
||||
|
||||
return this.axios.get('/torrents/rename', { params })
|
||||
}
|
||||
|
||||
getTorrentPieceStates(hash) {
|
||||
return this.axios.get('/torrents/pieceStates', {
|
||||
params: { hash }
|
||||
})
|
||||
}
|
||||
|
||||
getTorrentFiles(hash) {
|
||||
return this.axios.get('/torrents/files', {
|
||||
params: { hash }
|
||||
})
|
||||
}
|
||||
|
||||
getAvailableTags() {
|
||||
return this.axios.get('/torrents/tags')
|
||||
}
|
||||
|
||||
// Post
|
||||
|
||||
addTorrents(params, torrents) {
|
||||
let data
|
||||
if (torrents) {
|
||||
const formData = new FormData()
|
||||
if (params) {
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
formData.append(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
for (const torrent of torrents) {
|
||||
formData.append('torrents', torrent)
|
||||
}
|
||||
|
||||
data = formData
|
||||
} else {
|
||||
data = new URLSearchParams(params)
|
||||
}
|
||||
|
||||
return this.axios.post('/torrents/add', data)
|
||||
}
|
||||
|
||||
/** Begin General functions * */
|
||||
|
||||
getAppVersion() {
|
||||
return this.axios.get('/app/version')
|
||||
.then(res => res.data)
|
||||
setTorrentFilePriority(hash, idList, priority) {
|
||||
const params = {
|
||||
hash,
|
||||
id: idList.join('|'),
|
||||
priority
|
||||
}
|
||||
|
||||
return this.execute('post', '/torrents/filePrio', params)
|
||||
}
|
||||
|
||||
getApiVersion() {
|
||||
return this.axios.get('/app/webapiVersion')
|
||||
deleteTorrents(hashes, deleteFiles) {
|
||||
if (!hashes.length) return
|
||||
|
||||
return this.torrentAction('delete', hashes, { deleteFiles })
|
||||
}
|
||||
|
||||
pauseTorrents(hashes) {
|
||||
return this.torrentAction('pause', hashes)
|
||||
}
|
||||
|
||||
resumeTorrents(hashes) {
|
||||
return this.torrentAction('resume', hashes)
|
||||
}
|
||||
|
||||
reannounceTorrents(hashes) {
|
||||
return this.torrentAction('reannounce', hashes)
|
||||
}
|
||||
|
||||
recheckTorrents(hashes) {
|
||||
return this.torrentAction('recheck', hashes)
|
||||
}
|
||||
|
||||
setTorrentsCategory(hashes, category) {
|
||||
return this.torrentAction('setCategory', hashes, { category })
|
||||
}
|
||||
|
||||
editTracker(hash, origUrl, newUrl) {
|
||||
return this.torrentAction('editTracker', [hash], { origUrl, newUrl })
|
||||
}
|
||||
|
||||
setTorrentLocation(hashes, location) {
|
||||
return this.torrentAction('setLocation', hashes, { location })
|
||||
}
|
||||
|
||||
getTorrentProperties(hash) {
|
||||
return this.axios.get('/torrents/properties', {
|
||||
params: { hash }
|
||||
})
|
||||
}
|
||||
|
||||
torrentAction(action, hashes, extra) {
|
||||
const params = {
|
||||
hashes: hashes.join('|'),
|
||||
...extra
|
||||
}
|
||||
|
||||
return this.execute('post', `/torrents/${action}`, params)
|
||||
}
|
||||
|
||||
async login(params) {
|
||||
const payload = new URLSearchParams(params)
|
||||
const { data } = await this.axios.post('/auth/login', payload, {
|
||||
validateStatus(status) {
|
||||
return status === 200 || status === 403
|
||||
}
|
||||
})
|
||||
return data
|
||||
renameFile(hash, id, name) {
|
||||
const params = {
|
||||
hash,
|
||||
id,
|
||||
name
|
||||
}
|
||||
|
||||
return this.execute('post', '/torrents/renameFile', params)
|
||||
}
|
||||
|
||||
async logout() {
|
||||
this.axios.post('/auth/logout')
|
||||
/** Torrent Priority **/
|
||||
setTorrentPriority(hash, priority) {
|
||||
if (['increasePrio', 'decreasePrio', 'topPrio', 'bottomPrio'].includes(priority)) {
|
||||
return this.execute('post', `/torrents/${priority}`, {
|
||||
hashes: hash
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
getGlobalTransferInfo() {
|
||||
return this.axios.get('/transfer/info')
|
||||
/** Begin Torrent Tags **/
|
||||
removeTorrentTag(hash, tag) {
|
||||
return this.execute('post', '/torrents/removeTags', {
|
||||
hashes: hash,
|
||||
tags: tag
|
||||
})
|
||||
}
|
||||
|
||||
addTorrentTag(hash, tag) {
|
||||
return this.execute('post', '/torrents/addTags ', {
|
||||
hashes: hash,
|
||||
tags: tag
|
||||
})
|
||||
}
|
||||
|
||||
createTag(tag) {
|
||||
return this.execute('/torrents/createTags ', {
|
||||
tags: tag
|
||||
})
|
||||
}
|
||||
|
||||
deleteTag(tag) {
|
||||
return this.execute('post', '/torrents/deleteTags', {
|
||||
tags: tag
|
||||
})
|
||||
}
|
||||
|
||||
/** Begin Categories **/
|
||||
getCategories() {
|
||||
return this.axios.get('/torrents/categories')
|
||||
.then(res => res.data)
|
||||
}
|
||||
|
||||
deleteCategory(categories) {
|
||||
return this.execute('post', '/torrents/removeCategories', {
|
||||
categories
|
||||
})
|
||||
}
|
||||
|
||||
createCategory(cat) {
|
||||
return this.execute('post', '/torrents/createCategory', {
|
||||
category: cat.name,
|
||||
savePath: cat.savePath
|
||||
})
|
||||
}
|
||||
|
||||
setCategory(hash, cat) {
|
||||
return this.execute('post', '/torrents/setCategory', {
|
||||
hashes: hash,
|
||||
category: cat
|
||||
})
|
||||
}
|
||||
|
||||
editCategory(cat) {
|
||||
const params = {
|
||||
category: cat.name,
|
||||
savePath: cat.savePath
|
||||
}
|
||||
|
||||
return this.execute('post', '/torrents/editCategory', params)
|
||||
}
|
||||
|
||||
getAppPreferences() {
|
||||
return this.axios.get('/app/preferences')
|
||||
/** Search **/
|
||||
getSearchPlugins() {
|
||||
return this.axios.get('/search/plugins')
|
||||
.then(res => res.data)
|
||||
}
|
||||
|
||||
enableSearchPlugin(plugins, enable) {
|
||||
const params = {
|
||||
names: plugins.join('|'),
|
||||
enable
|
||||
}
|
||||
|
||||
return this.execute('post', '/search/enablePlugin', params)
|
||||
}
|
||||
|
||||
setPreferences(params) {
|
||||
const data = new URLSearchParams({
|
||||
json: JSON.stringify(params)
|
||||
})
|
||||
|
||||
return this.axios.post('/app/setPreferences', data)
|
||||
startSearch(pattern, plugins) {
|
||||
const params = {
|
||||
pattern,
|
||||
plugins: Array.isArray(plugins) ? plugins.join('|') : 'all',
|
||||
category: 'all'
|
||||
}
|
||||
|
||||
return this.execute('post', '/search/start', params)
|
||||
}
|
||||
|
||||
getMainData(rid) {
|
||||
return this.axios.get(
|
||||
'/sync/maindata', { params: { rid } })
|
||||
}
|
||||
stopSearch(id) {
|
||||
return this.execute('post', '/search/stop', { id })
|
||||
}
|
||||
|
||||
switchToOldUi() {
|
||||
return this.setPreferences({
|
||||
alternative_webui_enabled: false
|
||||
})
|
||||
}
|
||||
getSearchStatus(id) {
|
||||
return this.execute('post', '/search/status', { id })
|
||||
}
|
||||
|
||||
toggleSpeedLimitsMode() {
|
||||
return this.axios.post('/transfer/toggleSpeedLimitsMode')
|
||||
}
|
||||
|
||||
/** Begin Torrent functions * */
|
||||
|
||||
// Get
|
||||
|
||||
getLogs(lastId) {
|
||||
return this.axios.get('/log/main', {
|
||||
last_known_id: lastId
|
||||
})
|
||||
}
|
||||
|
||||
getTorrents(payload) {
|
||||
const params = {
|
||||
sort: payload.sort,
|
||||
reverse: payload.reverse,
|
||||
hashes: payload.hashes ? payload.hashes.join('|') : null,
|
||||
filter: payload.filter ? payload.filter : null,
|
||||
category: payload.category !== null ? payload.category : null
|
||||
}
|
||||
|
||||
// clean
|
||||
Object.keys(params).forEach(
|
||||
key => params[key] == null && delete params[key]
|
||||
)
|
||||
|
||||
const data = new URLSearchParams(params)
|
||||
|
||||
return this.axios.get(`/torrents/info?${data.toString()}`)
|
||||
}
|
||||
|
||||
getTorrentTrackers(hash) {
|
||||
return this.axios.get('/torrents/trackers', {
|
||||
params: { hash }
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
getTorrentPeers(hash, rid) {
|
||||
return this.axios.get('/sync/torrentPeers', {
|
||||
params: { hash, rid }
|
||||
})
|
||||
}
|
||||
|
||||
setTorrentName(hash, name) {
|
||||
const params = {
|
||||
hash,
|
||||
name
|
||||
}
|
||||
return this.axios.get('/torrents/rename', { params })
|
||||
}
|
||||
|
||||
getTorrentPieceStates(hash) {
|
||||
return this.axios.get('/torrents/pieceStates', {
|
||||
params: { hash }
|
||||
})
|
||||
}
|
||||
|
||||
getTorrentFiles(hash) {
|
||||
return this.axios.get('/torrents/files', {
|
||||
params: { hash }
|
||||
})
|
||||
}
|
||||
|
||||
getAvailableTags() {
|
||||
return this.axios.get('/torrents/tags')
|
||||
}
|
||||
|
||||
// Post
|
||||
|
||||
addTorrents(params, torrents) {
|
||||
let data
|
||||
if (torrents) {
|
||||
const formData = new FormData()
|
||||
if (params) {
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
formData.append(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
for (const torrent of torrents) {
|
||||
formData.append('torrents', torrent)
|
||||
}
|
||||
|
||||
data = formData
|
||||
} else {
|
||||
data = new URLSearchParams(params)
|
||||
}
|
||||
return this.axios.post('/torrents/add', data)
|
||||
}
|
||||
|
||||
setTorrentFilePriority(hash, idList, priority) {
|
||||
const params = {
|
||||
hash,
|
||||
id: idList.join('|'),
|
||||
priority
|
||||
}
|
||||
return this.execute('post', '/torrents/filePrio', params)
|
||||
}
|
||||
|
||||
deleteTorrents(hashes, deleteFiles) {
|
||||
if (!hashes.length) return
|
||||
return this.torrentAction('delete', hashes, { deleteFiles })
|
||||
}
|
||||
|
||||
pauseTorrents(hashes) {
|
||||
return this.torrentAction('pause', hashes)
|
||||
}
|
||||
|
||||
resumeTorrents(hashes) {
|
||||
return this.torrentAction('resume', hashes)
|
||||
}
|
||||
|
||||
reannounceTorrents(hashes) {
|
||||
return this.torrentAction('reannounce', hashes)
|
||||
}
|
||||
|
||||
recheckTorrents(hashes) {
|
||||
return this.torrentAction('recheck', hashes)
|
||||
}
|
||||
|
||||
setTorrentsCategory(hashes, category) {
|
||||
return this.torrentAction('setCategory', hashes, { category })
|
||||
}
|
||||
|
||||
editTracker(hash, origUrl, newUrl) {
|
||||
return this.torrentAction('editTracker', [hash], { origUrl, newUrl })
|
||||
}
|
||||
|
||||
setTorrentLocation(hashes, location) {
|
||||
return this.torrentAction('setLocation', hashes, { location })
|
||||
}
|
||||
|
||||
getTorrentProperties(hash) {
|
||||
return this.axios.get('/torrents/properties', {
|
||||
params: { hash }
|
||||
})
|
||||
}
|
||||
|
||||
torrentAction(action, hashes, extra) {
|
||||
const params = {
|
||||
hashes: hashes.join('|'),
|
||||
...extra
|
||||
}
|
||||
return this.execute('post', `/torrents/${action}`, params)
|
||||
}
|
||||
|
||||
renameFile(hash, id, name) {
|
||||
const params = {
|
||||
hash,
|
||||
id,
|
||||
name
|
||||
}
|
||||
return this.execute('post', '/torrents/renameFile', params)
|
||||
}
|
||||
|
||||
/** Torrent Priority **/
|
||||
setTorrentPriority(hash, priority) {
|
||||
if (['increasePrio', 'decreasePrio', 'topPrio', 'bottomPrio'].includes(priority)) {
|
||||
return this.execute('post', `/torrents/${priority}`, {
|
||||
hashes: hash
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/** Begin Torrent Tags **/
|
||||
removeTorrentTag(hash, tag) {
|
||||
return this.execute('post', '/torrents/removeTags', {
|
||||
hashes: hash,
|
||||
tags: tag
|
||||
})
|
||||
}
|
||||
|
||||
addTorrentTag(hash, tag) {
|
||||
return this.execute('post', '/torrents/addTags ', {
|
||||
hashes: hash,
|
||||
tags: tag
|
||||
})
|
||||
}
|
||||
|
||||
createTag(tag) {
|
||||
return this.execute('/torrents/createTags ', {
|
||||
tags: tag
|
||||
})
|
||||
}
|
||||
|
||||
deleteTag(tag) {
|
||||
return this.execute('post', '/torrents/deleteTags', {
|
||||
tags: tag
|
||||
})
|
||||
}
|
||||
|
||||
/** Begin Categories **/
|
||||
getCategories() {
|
||||
return this.axios.get('/torrents/categories')
|
||||
.then(res => res.data)
|
||||
}
|
||||
|
||||
deleteCategory(categories) {
|
||||
return this.execute('post', '/torrents/removeCategories', {
|
||||
categories
|
||||
})
|
||||
}
|
||||
|
||||
createCategory(cat) {
|
||||
return this.execute('post', '/torrents/createCategory', {
|
||||
category: cat.name,
|
||||
savePath: cat.savePath
|
||||
})
|
||||
}
|
||||
|
||||
setCategory(hash, cat) {
|
||||
return this.execute('post', '/torrents/setCategory', {
|
||||
hashes: hash,
|
||||
category: cat
|
||||
})
|
||||
}
|
||||
|
||||
editCategory(cat) {
|
||||
const params = {
|
||||
category: cat.name,
|
||||
savePath: cat.savePath
|
||||
}
|
||||
return this.execute('post', '/torrents/editCategory', params)
|
||||
}
|
||||
|
||||
/** Search **/
|
||||
getSearchPlugins() {
|
||||
return this.axios.get( '/search/plugins')
|
||||
.then(res => res.data)
|
||||
}
|
||||
|
||||
enableSearchPlugin(plugins, enable) {
|
||||
const params = {
|
||||
names: plugins.join('|'),
|
||||
enable
|
||||
}
|
||||
return this.execute('post', '/search/enablePlugin', params)
|
||||
}
|
||||
|
||||
startSearch(pattern, plugins) {
|
||||
const params = {
|
||||
pattern,
|
||||
plugins: Array.isArray(plugins) ? plugins.join('|') : 'all',
|
||||
category: 'all'
|
||||
}
|
||||
return this.execute('post', '/search/start', params)
|
||||
}
|
||||
|
||||
stopSearch(id) {
|
||||
return this.execute('post', '/search/stop', { id })
|
||||
}
|
||||
|
||||
getSearchStatus(id) {
|
||||
return this.execute('post', '/search/status', { id })
|
||||
}
|
||||
|
||||
getSearchResults(id) {
|
||||
return this.execute('post', '/search/results', {
|
||||
id,
|
||||
limit: 50
|
||||
})
|
||||
}
|
||||
getSearchResults(id) {
|
||||
return this.execute('post', '/search/results', {
|
||||
id,
|
||||
limit: 50
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default new Qbit()
|
||||
|
|
|
@ -2,23 +2,25 @@ import Vue from 'vue'
|
|||
import qbit from '../services/qbit'
|
||||
|
||||
export default {
|
||||
INIT_INTERVALS: async context => {
|
||||
context.state.intervals[0] = setInterval(() => {
|
||||
context.commit('updateMainData')
|
||||
}, 2000)
|
||||
},
|
||||
LOGIN: async (context, payload) => {
|
||||
const res = await qbit.login(payload)
|
||||
console.log(res)
|
||||
if (res === 'Ok.') {
|
||||
Vue.$toast.success('Successfully logged in!')
|
||||
context.commit('LOGIN', true)
|
||||
context.commit('updateMainData')
|
||||
context.commit('FETCH_SETTINGS')
|
||||
context.commit('FETCH_CATEGORIES')
|
||||
return true
|
||||
}
|
||||
Vue.$toast.error('Log in failed 😕')
|
||||
return false
|
||||
INIT_INTERVALS: async context => {
|
||||
context.state.intervals[0] = setInterval(() => {
|
||||
context.commit('updateMainData')
|
||||
}, 2000)
|
||||
},
|
||||
LOGIN: async (context, payload) => {
|
||||
const res = await qbit.login(payload)
|
||||
console.log(res)
|
||||
if (res === 'Ok.') {
|
||||
Vue.$toast.success('Successfully logged in!')
|
||||
context.commit('LOGIN', true)
|
||||
context.commit('updateMainData')
|
||||
context.commit('FETCH_SETTINGS')
|
||||
context.commit('FETCH_CATEGORIES')
|
||||
|
||||
return true
|
||||
}
|
||||
Vue.$toast.error('Log in failed 😕')
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
export default {
|
||||
getAppVersion: state => () => state.version,
|
||||
containsTorrent: state => hash => state.selected_torrents.includes(hash),
|
||||
getTheme: state => () => state.webuiSettings.darkTheme,
|
||||
getModalState: state => guid =>
|
||||
state.modals.filter(m => m.guid === guid)[0],
|
||||
getSettings: state => () => state.settings,
|
||||
getStatus: state => () => state.status,
|
||||
getTorrent: state => hash =>
|
||||
state.torrents.filter(el => el.hash === hash)[0],
|
||||
getWebuiSettings: state => () => state.webuiSettings,
|
||||
getAvailableTags: state => () => state.status.tags,
|
||||
getCategories: state => () => state.categories,
|
||||
getModals: state => () => state.modals,
|
||||
getTorrents: state => () => state.torrents,
|
||||
getTrackers: state => () => state.trackers,
|
||||
getAuthenticated: state => () => state.authenticated,
|
||||
getTorrentCountString: state => () => {
|
||||
if (state.selected_torrents && state.selected_torrents.length) {
|
||||
return `${state.selected_torrents.length} of ${state.filteredTorrentsCount} torrents`
|
||||
}
|
||||
getAppVersion: state => () => state.version,
|
||||
containsTorrent: state => hash => state.selected_torrents.includes(hash),
|
||||
getTheme: state => () => state.webuiSettings.darkTheme,
|
||||
getModalState: state => guid =>
|
||||
state.modals.filter(m => m.guid === guid)[0],
|
||||
getSettings: state => () => state.settings,
|
||||
getStatus: state => () => state.status,
|
||||
getTorrent: state => hash =>
|
||||
state.torrents.filter(el => el.hash === hash)[0],
|
||||
getWebuiSettings: state => () => state.webuiSettings,
|
||||
getAvailableTags: state => () => state.status.tags,
|
||||
getCategories: state => () => state.categories,
|
||||
getModals: state => () => state.modals,
|
||||
getTorrents: state => () => state.torrents,
|
||||
getTrackers: state => () => state.trackers,
|
||||
getAuthenticated: state => () => state.authenticated,
|
||||
getTorrentCountString: state => () => {
|
||||
if (state.selected_torrents && state.selected_torrents.length) {
|
||||
return `${state.selected_torrents.length} of ${state.filteredTorrentsCount} torrents`
|
||||
}
|
||||
|
||||
return `${state.filteredTorrentsCount} torrents`
|
||||
},
|
||||
getSearchPlugins: state => () => state.searchPlugins
|
||||
return `${state.filteredTorrentsCount} torrents`
|
||||
},
|
||||
getSearchPlugins: state => () => state.searchPlugins
|
||||
}
|
||||
|
|
|
@ -3,14 +3,14 @@ import Vuex from 'vuex'
|
|||
import VuexPersist from 'vuex-persist'
|
||||
|
||||
const vuexPersist = new VuexPersist({
|
||||
key: 'vuetorrent',
|
||||
storage: window.localStorage,
|
||||
reducer: state => ({
|
||||
sort_options: state.sort_options,
|
||||
webuiSettings: state.webuiSettings,
|
||||
authenticated: state.authenticated,
|
||||
torrents: state.torrents
|
||||
})
|
||||
key: 'vuetorrent',
|
||||
storage: window.localStorage,
|
||||
reducer: state => ({
|
||||
sort_options: state.sort_options,
|
||||
webuiSettings: state.webuiSettings,
|
||||
authenticated: state.authenticated,
|
||||
torrents: state.torrents
|
||||
})
|
||||
})
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
@ -20,91 +20,92 @@ import mutations from './mutations'
|
|||
import actions from './actions'
|
||||
|
||||
export default new Vuex.Store({
|
||||
plugins: [vuexPersist.plugin],
|
||||
state: {
|
||||
version: 0,
|
||||
intervals: [],
|
||||
status: {
|
||||
status: '',
|
||||
downloaded: '',
|
||||
uploaded: '',
|
||||
dlspeed: '',
|
||||
upspeed: '',
|
||||
freeDiskSpace: '',
|
||||
altSpeed: '',
|
||||
dlspeedRaw: '',
|
||||
upspeedRaw: '',
|
||||
tags: ''
|
||||
},
|
||||
upload_data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
download_data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
torrents: [],
|
||||
selected_torrents: [],
|
||||
authenticated: false,
|
||||
sort_options: {
|
||||
sort: 'default',
|
||||
reverse: true,
|
||||
hashes: [],
|
||||
filter: null
|
||||
},
|
||||
rid: 0,
|
||||
pasteUrl: null,
|
||||
modals: [],
|
||||
settings: {},
|
||||
webuiSettings: {
|
||||
darkTheme: false,
|
||||
showFreeSpace: true,
|
||||
showSpeedGraph: true,
|
||||
showSessionStat: true,
|
||||
showCurrentSpeed: true,
|
||||
showGlobalRemoveResumePause: true,
|
||||
denseDashboard: false,
|
||||
showTrackerFilter: false,
|
||||
paginationSize: 15,
|
||||
busyTorrentProperties: [
|
||||
{ name: 'Size', active: true },
|
||||
{ name: 'Progress', active: true },
|
||||
{ name: 'Download', active: true },
|
||||
{ name: 'Upload', active: true },
|
||||
{ name: 'ETA', active: true },
|
||||
{ name: 'Peers', active: true },
|
||||
{ name: 'Seeds', active: true },
|
||||
{ name: 'Status', active: true },
|
||||
{ name: 'Ratio', active: true },
|
||||
{ name: 'Category', active: true },
|
||||
{ name: 'Tags', active: true },
|
||||
{ name: 'AddedOn', active: true }
|
||||
],
|
||||
doneTorrentProperties: [
|
||||
{ name: 'Size', active: true },
|
||||
{ name: 'Progress', active: true },
|
||||
{ name: 'Download', active: true },
|
||||
{ name: 'Upload', active: true },
|
||||
{ name: 'ETA', active: true },
|
||||
{ name: 'Peers', active: true },
|
||||
{ name: 'Seeds', active: true },
|
||||
{ name: 'Status', active: true },
|
||||
{ name: 'Ratio', active: true },
|
||||
{ name: 'Category', active: true },
|
||||
{ name: 'Tags', active: true },
|
||||
{ name: 'AddedOn', active: true }
|
||||
]
|
||||
},
|
||||
categories: [],
|
||||
trackers: [],
|
||||
filteredTorrentsCount: 0,
|
||||
latestSelectedTorrent: null,
|
||||
selectMode: false,
|
||||
searchPlugins: []
|
||||
plugins: [vuexPersist.plugin],
|
||||
state: {
|
||||
version: 0,
|
||||
intervals: [],
|
||||
status: {
|
||||
status: '',
|
||||
downloaded: '',
|
||||
uploaded: '',
|
||||
dlspeed: '',
|
||||
upspeed: '',
|
||||
freeDiskSpace: '',
|
||||
altSpeed: '',
|
||||
dlspeedRaw: '',
|
||||
upspeedRaw: '',
|
||||
tags: ''
|
||||
},
|
||||
getters: {
|
||||
...getters
|
||||
upload_data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
download_data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
torrents: [],
|
||||
selected_torrents: [],
|
||||
authenticated: false,
|
||||
sort_options: {
|
||||
sort: null,
|
||||
reverse: true,
|
||||
hashes: [],
|
||||
filter: null
|
||||
},
|
||||
rid: 0,
|
||||
pasteUrl: null,
|
||||
modals: [],
|
||||
settings: {},
|
||||
webuiSettings: {
|
||||
darkTheme: false,
|
||||
showFreeSpace: true,
|
||||
showSpeedGraph: true,
|
||||
showSessionStat: true,
|
||||
showCurrentSpeed: true,
|
||||
showGlobalRemoveResumePause: true,
|
||||
denseDashboard: false,
|
||||
showTrackerFilter: false,
|
||||
showSpeedInTitle: false,
|
||||
paginationSize: 15,
|
||||
busyTorrentProperties: [
|
||||
{ name: 'Size', active: true },
|
||||
{ name: 'Progress', active: true },
|
||||
{ name: 'Download', active: true },
|
||||
{ name: 'Upload', active: true },
|
||||
{ name: 'ETA', active: true },
|
||||
{ name: 'Peers', active: true },
|
||||
{ name: 'Seeds', active: true },
|
||||
{ name: 'Status', active: true },
|
||||
{ name: 'Ratio', active: true },
|
||||
{ name: 'Category', active: true },
|
||||
{ name: 'Tags', active: true },
|
||||
{ name: 'AddedOn', active: true }
|
||||
],
|
||||
doneTorrentProperties: [
|
||||
{ name: 'Size', active: true },
|
||||
{ name: 'Progress', active: true },
|
||||
{ name: 'Download', active: true },
|
||||
{ name: 'Upload', active: true },
|
||||
{ name: 'ETA', active: true },
|
||||
{ name: 'Peers', active: true },
|
||||
{ name: 'Seeds', active: true },
|
||||
{ name: 'Status', active: true },
|
||||
{ name: 'Ratio', active: true },
|
||||
{ name: 'Category', active: true },
|
||||
{ name: 'Tags', active: true },
|
||||
{ name: 'AddedOn', active: true }
|
||||
]
|
||||
},
|
||||
categories: [],
|
||||
trackers: [],
|
||||
filteredTorrentsCount: 0,
|
||||
latestSelectedTorrent: null,
|
||||
selectMode: false,
|
||||
searchPlugins: []
|
||||
},
|
||||
getters: {
|
||||
...getters
|
||||
},
|
||||
|
||||
mutations: {
|
||||
...mutations
|
||||
},
|
||||
actions: {
|
||||
...actions
|
||||
}
|
||||
mutations: {
|
||||
...mutations
|
||||
},
|
||||
actions: {
|
||||
...actions
|
||||
}
|
||||
})
|
||||
|
|
|
@ -4,102 +4,108 @@ import qbit from '../services/qbit'
|
|||
import { getHostName } from '../helpers'
|
||||
|
||||
export default {
|
||||
SET_APP_VERSION(state, version) {
|
||||
state.version = version
|
||||
},
|
||||
REMOVE_INTERVALS: state => {
|
||||
state.intervals.forEach(el => clearInterval(el))
|
||||
},
|
||||
ADD_MODAL(state, modal) {
|
||||
state.modals.push(modal)
|
||||
},
|
||||
DELETE_MODAL(state, guid) {
|
||||
state.modals = state.modals.filter(m => m.guid !== guid)
|
||||
},
|
||||
SET_SELECTED: (state, { type, hash, index }) => {
|
||||
if (type === 'add') {
|
||||
state.selected_torrents.push(hash)
|
||||
state.latestSelectedTorrent = state.torrents.map(t => t.hash).indexOf(hash)
|
||||
} else if (type === 'remove') {
|
||||
state.selected_torrents.splice(
|
||||
state.selected_torrents.indexOf(hash),
|
||||
1
|
||||
)
|
||||
} else if (type === 'until') {
|
||||
let from
|
||||
let until
|
||||
if (state.latestSelectedTorrent > index) {
|
||||
from = index
|
||||
until = state.latestSelectedTorrent + 1 // include latest selected
|
||||
} else {
|
||||
from = state.latestSelectedTorrent
|
||||
until = index + 1
|
||||
}
|
||||
state.selected_torrents = state.torrents.map(t => t.hash).slice(from, until)
|
||||
}
|
||||
},
|
||||
RESET_SELECTED: state => {
|
||||
state.selected_torrents = []
|
||||
},
|
||||
TOGGLE_THEME(state) {
|
||||
state.webuiSettings.darkTheme = !state.webuiSettings.darkTheme
|
||||
},
|
||||
LOGOUT: state => {
|
||||
qbit.logout()
|
||||
state.authenticated = false
|
||||
},
|
||||
LOGIN: async (state, payload) => {
|
||||
state.authenticated = payload
|
||||
},
|
||||
updateMainData: async state => {
|
||||
const rid = state.rid ? state.rid : undefined
|
||||
const res = await qbit.getMainData(rid)
|
||||
SET_APP_VERSION(state, version) {
|
||||
state.version = version
|
||||
},
|
||||
REMOVE_INTERVALS: state => {
|
||||
state.intervals.forEach(el => clearInterval(el))
|
||||
},
|
||||
ADD_MODAL(state, modal) {
|
||||
state.modals.push(modal)
|
||||
},
|
||||
DELETE_MODAL(state, guid) {
|
||||
state.modals = state.modals.filter(m => m.guid !== guid)
|
||||
},
|
||||
SET_SELECTED: (state, { type, hash, index }) => {
|
||||
if (type === 'add') {
|
||||
state.selected_torrents.push(hash)
|
||||
state.latestSelectedTorrent = state.torrents.map(t => t.hash).indexOf(hash)
|
||||
} else if (type === 'remove') {
|
||||
state.selected_torrents.splice(
|
||||
state.selected_torrents.indexOf(hash),
|
||||
1
|
||||
)
|
||||
} else if (type === 'until') {
|
||||
let from
|
||||
let until
|
||||
if (state.latestSelectedTorrent > index) {
|
||||
from = index
|
||||
until = state.latestSelectedTorrent + 1 // include latest selected
|
||||
} else {
|
||||
from = state.latestSelectedTorrent
|
||||
until = index + 1
|
||||
}
|
||||
state.selected_torrents = state.torrents.map(t => t.hash).slice(from, until)
|
||||
}
|
||||
},
|
||||
RESET_SELECTED: state => {
|
||||
state.selected_torrents = []
|
||||
},
|
||||
TOGGLE_THEME(state) {
|
||||
state.webuiSettings.darkTheme = !state.webuiSettings.darkTheme
|
||||
},
|
||||
LOGOUT: state => {
|
||||
qbit.logout()
|
||||
state.authenticated = false
|
||||
},
|
||||
LOGIN: async (state, payload) => {
|
||||
state.authenticated = payload
|
||||
},
|
||||
updateMainData: async state => {
|
||||
const rid = state.rid ? state.rid : undefined
|
||||
const res = await qbit.getMainData(rid)
|
||||
|
||||
// status
|
||||
state.status = new Status(res.data.server_state, res.data.tags)
|
||||
// status
|
||||
state.status = new Status(res.data.server_state, res.data.tags)
|
||||
|
||||
// graph
|
||||
state.download_data.splice(0, 1)
|
||||
state.download_data.push(state.status.dlspeedRaw)
|
||||
state.upload_data.splice(0, 1)
|
||||
state.upload_data.push(state.status.upspeedRaw)
|
||||
// graph
|
||||
state.download_data.splice(0, 1)
|
||||
state.download_data.push(state.status.dlspeedRaw)
|
||||
state.upload_data.splice(0, 1)
|
||||
state.upload_data.push(state.status.upspeedRaw)
|
||||
|
||||
// torrents
|
||||
let { data } = await qbit.getTorrents(state.sort_options)
|
||||
// torrents
|
||||
let { data } = await qbit.getTorrents(state.sort_options)
|
||||
|
||||
// trackers
|
||||
if (state.webuiSettings.showTrackerFilter) { // dont calculate trackers when disabled
|
||||
state.trackers = data.map(t => t.tracker)
|
||||
.map(url => getHostName(url))
|
||||
.filter((domain, index, self) => index === self.indexOf(domain) && domain)
|
||||
.sort()
|
||||
// trackers
|
||||
if (state.webuiSettings.showTrackerFilter) { // dont calculate trackers when disabled
|
||||
state.trackers = data.map(t => t.tracker)
|
||||
.map(url => getHostName(url))
|
||||
.filter((domain, index, self) => index === self.indexOf(domain) && domain)
|
||||
.sort()
|
||||
|
||||
if (state.sort_options.tracker !== null) {
|
||||
data = data.filter(d => getHostName(d.tracker) === state.sort_options.tracker)
|
||||
}
|
||||
}
|
||||
if (state.sort_options.tracker !== null) {
|
||||
data = data.filter(d => getHostName(d.tracker) === state.sort_options.tracker)
|
||||
}
|
||||
}
|
||||
|
||||
// torrents
|
||||
state.torrents = []
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
state.torrents.push(new Torrent({ hash: key, ...value }))
|
||||
}
|
||||
},
|
||||
FETCH_SETTINGS: async state => {
|
||||
const { data } = await qbit.getAppPreferences()
|
||||
state.settings = data
|
||||
},
|
||||
UPDATE_SORT_OPTIONS: (state, payload) => {
|
||||
state.sort_options.sort = payload.name ? payload.name : state.sort_options.sort
|
||||
state.sort_options.reverse = payload.reverse ? payload.reverse : state.sort_options.reverse
|
||||
state.sort_options.hashes = payload.hashes ? payload.hashes : state.sort_options.hashes
|
||||
state.sort_options.filter = payload.filter ? payload.filter : state.sort_options.filter
|
||||
state.sort_options.category =
|
||||
// torrent speed in title
|
||||
if (state.webuiSettings.showSpeedInTitle) {
|
||||
// eslint-disable-next-line max-len
|
||||
document.title = `[D: ${state.status.dlspeed}/s, U: ${state.status.upspeed}/s] VueTorrent`
|
||||
}
|
||||
|
||||
// torrents
|
||||
state.torrents = []
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
state.torrents.push(new Torrent({ hash: key, ...value }))
|
||||
}
|
||||
},
|
||||
FETCH_SETTINGS: async state => {
|
||||
const { data } = await qbit.getAppPreferences()
|
||||
state.settings = data
|
||||
},
|
||||
UPDATE_SORT_OPTIONS: (state, payload) => {
|
||||
state.sort_options.sort = payload.name ? payload.name : state.sort_options.sort
|
||||
state.sort_options.reverse = payload.reverse ? payload.reverse : state.sort_options.reverse
|
||||
state.sort_options.hashes = payload.hashes ? payload.hashes : state.sort_options.hashes
|
||||
state.sort_options.filter = payload.filter ? payload.filter : state.sort_options.filter
|
||||
state.sort_options.category =
|
||||
payload.category !== null ? payload.category : null
|
||||
state.sort_options.tracker =
|
||||
state.sort_options.tracker =
|
||||
payload.tracker !== null ? payload.tracker : null
|
||||
},
|
||||
FETCH_CATEGORIES: async state => state.categories = await qbit.getCategories(),
|
||||
FETCH_SEARCH_PLUGINS: async state => state.searchPlugins = await qbit.getSearchPlugins(),
|
||||
SET_CURRENT_ITEM_COUNT: (state, count) => (state.filteredTorrentsCount = count)
|
||||
},
|
||||
FETCH_CATEGORIES: async state => state.categories = await qbit.getCategories(),
|
||||
FETCH_SEARCH_PLUGINS: async state => state.searchPlugins = await qbit.getSearchPlugins(),
|
||||
SET_CURRENT_ITEM_COUNT: (state, count) => (state.filteredTorrentsCount = count)
|
||||
}
|
||||
|
|
|
@ -1,84 +1,93 @@
|
|||
<template>
|
||||
<div class="pl-5 pr-5"
|
||||
color="background"
|
||||
@click.self="resetSelected"
|
||||
>
|
||||
<h1 style="font-size: 1.1em !important" class="subtitle-1 grey--text">
|
||||
Dashboard
|
||||
<p
|
||||
style="float: right; font-size: 0.7em"
|
||||
class="grey--text text-uppercase"
|
||||
>
|
||||
{{ torrentCountString }}
|
||||
</p>
|
||||
</h1>
|
||||
<div
|
||||
class="px-1 px-sm-5"
|
||||
color="background"
|
||||
@click.self="resetSelected"
|
||||
>
|
||||
<h1 style="font-size: 1.1em !important" class="subtitle-1 grey--text">
|
||||
Dashboard
|
||||
<p
|
||||
style="float: right; font-size: 0.7em"
|
||||
class="grey--text text-uppercase"
|
||||
>
|
||||
{{ torrentCountString }}
|
||||
</p>
|
||||
</h1>
|
||||
|
||||
<div
|
||||
color="background"
|
||||
class="my-4 pt-5 px-8"
|
||||
@click.self="resetSelected"
|
||||
<div
|
||||
color="background"
|
||||
class="my-2 my-sm-4 pt-2 pt-sm-5 px-sm-8"
|
||||
@click.self="resetSelected"
|
||||
>
|
||||
<v-flex
|
||||
xs12
|
||||
sm6
|
||||
md3
|
||||
@click.self="resetSelected"
|
||||
>
|
||||
<v-text-field
|
||||
v-model="input"
|
||||
flat
|
||||
label="type to filter..."
|
||||
height="50"
|
||||
clearable
|
||||
solo
|
||||
color="search"
|
||||
@click:clear="resetInput()"
|
||||
/>
|
||||
</v-flex>
|
||||
<div v-if="torrents.length === 0" class="mt-5 text-xs-center">
|
||||
<p class="grey--text">
|
||||
Nothing to see here!
|
||||
</p>
|
||||
</div>
|
||||
<div v-else>
|
||||
<v-layout
|
||||
v-for="(torrent, index) in paginatedData"
|
||||
:key="torrent.hash"
|
||||
@contextmenu.prevent="$refs.menu.open($event, { torrent })"
|
||||
>
|
||||
<v-flex xs12 sm6 md3 @click.self="resetSelected">
|
||||
<v-text-field
|
||||
flat
|
||||
label="type to filter..."
|
||||
height="50"
|
||||
clearable
|
||||
solo
|
||||
color="search"
|
||||
@click:clear="resetInput()"
|
||||
v-model="input"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
<div v-if="torrents.length === 0" class="mt-5 text-xs-center">
|
||||
<p class="grey--text">Nothing to see here!</p>
|
||||
</div>
|
||||
<div v-else>
|
||||
<v-layout
|
||||
@contextmenu.prevent="$refs.menu.open($event, { torrent })"
|
||||
v-for="(torrent, index) in paginatedData"
|
||||
:key="torrent.hash"
|
||||
>
|
||||
<v-flex v-if="selectMode">
|
||||
<v-checkbox
|
||||
color="grey"
|
||||
class="mt-10"
|
||||
xs1
|
||||
:value="selected_torrents.indexOf(torrent.hash) !== -1"
|
||||
@click="selectTorrent(torrent.hash)" />
|
||||
</v-flex>
|
||||
<v-flex :class="selectMode ? 'xs11' : ''">
|
||||
<Torrent
|
||||
:class="{
|
||||
topBorderRadius: index === 0,
|
||||
noBorderRadius:
|
||||
index !== 0 && index !== torrent.length - 1,
|
||||
bottomBorderRadius: index === torrents.length - 1
|
||||
}"
|
||||
:torrent="torrent"
|
||||
:index="index"
|
||||
:length="torrents.length - 1"
|
||||
/>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<v-row v-if="pageCount > 1" xs12 justify="center">
|
||||
<v-col>
|
||||
<v-container>
|
||||
<v-pagination
|
||||
v-model="pageNumber"
|
||||
:length="pageCount"
|
||||
:total-visible="7"
|
||||
@input="toTop"
|
||||
></v-pagination>
|
||||
</v-container>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</div>
|
||||
<vue-context ref="menu" v-slot="{ data }">
|
||||
<TorrentRightClickMenu v-if="data" :hash="data.torrent.hash" />
|
||||
</vue-context>
|
||||
<v-flex v-if="selectMode">
|
||||
<v-checkbox
|
||||
color="grey"
|
||||
class="mt-10"
|
||||
xs1
|
||||
:value="selected_torrents.indexOf(torrent.hash) !== -1"
|
||||
@click="selectTorrent(torrent.hash)"
|
||||
/>
|
||||
</v-flex>
|
||||
<v-flex :class="selectMode ? 'xs11' : ''">
|
||||
<Torrent
|
||||
:class="{
|
||||
topBorderRadius: index === 0,
|
||||
noBorderRadius:
|
||||
index !== 0 && index !== torrent.length - 1,
|
||||
bottomBorderRadius: index === torrents.length - 1
|
||||
}"
|
||||
:torrent="torrent"
|
||||
:index="index"
|
||||
:length="torrents.length - 1"
|
||||
/>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<v-row v-if="pageCount > 1" xs12 justify="center">
|
||||
<v-col>
|
||||
<v-container>
|
||||
<v-pagination
|
||||
v-model="pageNumber"
|
||||
:length="pageCount"
|
||||
:total-visible="7"
|
||||
@input="toTop"
|
||||
/>
|
||||
</v-container>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</div>
|
||||
<vue-context ref="menu" v-slot="{ data }">
|
||||
<TorrentRightClickMenu v-if="data" :hash="data.torrent.hash" />
|
||||
</vue-context>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -91,105 +100,109 @@ import TorrentRightClickMenu from '@/components/Torrent/TorrentRightClickMenu.vu
|
|||
import { TorrentSelect, General } from '@/mixins'
|
||||
|
||||
export default {
|
||||
name: 'Dashboard',
|
||||
components: { Torrent, VueContext, TorrentRightClickMenu },
|
||||
mixins: [TorrentSelect, General],
|
||||
data() {
|
||||
return {
|
||||
input: '',
|
||||
pageNumber: 1
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['mainData', 'selected_torrents']),
|
||||
...mapGetters(['getTorrents', 'getTorrentCountString', 'getWebuiSettings']),
|
||||
torrents() {
|
||||
if (!this.input || !this.input.length) return this.getTorrents()
|
||||
|
||||
const options = {
|
||||
threshold: 0.3,
|
||||
shouldSort: false,
|
||||
keys: [
|
||||
'name',
|
||||
'size',
|
||||
'state',
|
||||
'hash',
|
||||
'savePath',
|
||||
'tags',
|
||||
'category'
|
||||
]
|
||||
}
|
||||
const fuse = new Fuse(this.getTorrents(), options)
|
||||
return fuse.search(this.input).map(el => el.item)
|
||||
},
|
||||
paginationSize() {
|
||||
return this.getWebuiSettings().paginationSize
|
||||
},
|
||||
pageCount() {
|
||||
const l = this.torrents.length
|
||||
const s = this.paginationSize
|
||||
return Math.ceil(l/s)
|
||||
},
|
||||
paginatedData() {
|
||||
const start = (this.pageNumber - 1) * this.paginationSize
|
||||
const end = start + this.paginationSize
|
||||
return this.torrents.slice(start, end)
|
||||
},
|
||||
torrentCountString() {
|
||||
return this.getTorrentCountString()
|
||||
},
|
||||
selectMode() {
|
||||
return this.$store.state.selectMode
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resetSelected() {
|
||||
this.$store.commit('RESET_SELECTED')
|
||||
},
|
||||
resetInput() {
|
||||
this.input = ''
|
||||
},
|
||||
toTop() {
|
||||
this.$vuetify.goTo(0)
|
||||
},
|
||||
handleKeyboardShortcut(e) {
|
||||
// 'ctrl + A' => select torrents
|
||||
if (e.keyCode === 65 && e.ctrlKey) {
|
||||
e.preventDefault()
|
||||
if (this.$store.state.selected_torrents.length === this.torrents.length) {
|
||||
return this.$store.state.selected_torrents = []
|
||||
}
|
||||
const hashes = this.torrents.map(t => t.hash)
|
||||
return this.$store.state.selected_torrents = hashes
|
||||
}
|
||||
|
||||
// 'Delete' => Delete modal
|
||||
if (e.keyCode === 46) {
|
||||
e.preventDefault()
|
||||
|
||||
// no torrents select to delete
|
||||
if (!this.selected_torrents.length) return
|
||||
|
||||
return this.createModal('ConfirmDeleteModal')
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('keydown', this.handleKeyboardShortcut)
|
||||
},
|
||||
created() {
|
||||
this.$store.dispatch('INIT_INTERVALS')
|
||||
this.$store.commit('FETCH_CATEGORIES')
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$store.commit('REMOVE_INTERVALS')
|
||||
document.removeEventListener('keydown', this.handleKeyboardShortcut)
|
||||
},
|
||||
watch: {
|
||||
torrents: function(torrents) {
|
||||
this.$store.commit('SET_CURRENT_ITEM_COUNT', torrents.length)
|
||||
}
|
||||
name: 'Dashboard',
|
||||
components: { Torrent, VueContext, TorrentRightClickMenu },
|
||||
mixins: [TorrentSelect, General],
|
||||
data() {
|
||||
return {
|
||||
input: '',
|
||||
pageNumber: 1
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['mainData', 'selected_torrents']),
|
||||
...mapGetters(['getTorrents', 'getTorrentCountString', 'getWebuiSettings']),
|
||||
torrents() {
|
||||
if (!this.input || !this.input.length) return this.getTorrents()
|
||||
|
||||
const options = {
|
||||
threshold: 0.3,
|
||||
shouldSort: false,
|
||||
keys: [
|
||||
'name',
|
||||
'size',
|
||||
'state',
|
||||
'hash',
|
||||
'savePath',
|
||||
'tags',
|
||||
'category'
|
||||
]
|
||||
}
|
||||
const fuse = new Fuse(this.getTorrents(), options)
|
||||
|
||||
return fuse.search(this.input).map(el => el.item)
|
||||
},
|
||||
paginationSize() {
|
||||
return this.getWebuiSettings().paginationSize
|
||||
},
|
||||
pageCount() {
|
||||
const l = this.torrents.length
|
||||
const s = this.paginationSize
|
||||
|
||||
return Math.ceil(l / s)
|
||||
},
|
||||
paginatedData() {
|
||||
const start = (this.pageNumber - 1) * this.paginationSize
|
||||
const end = start + this.paginationSize
|
||||
|
||||
return this.torrents.slice(start, end)
|
||||
},
|
||||
torrentCountString() {
|
||||
return this.getTorrentCountString()
|
||||
},
|
||||
selectMode() {
|
||||
return this.$store.state.selectMode
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
torrents: function (torrents) {
|
||||
this.$store.commit('SET_CURRENT_ITEM_COUNT', torrents.length)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('keydown', this.handleKeyboardShortcut)
|
||||
},
|
||||
created() {
|
||||
this.$store.dispatch('INIT_INTERVALS')
|
||||
this.$store.commit('FETCH_CATEGORIES')
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$store.commit('REMOVE_INTERVALS')
|
||||
document.removeEventListener('keydown', this.handleKeyboardShortcut)
|
||||
},
|
||||
methods: {
|
||||
resetSelected() {
|
||||
this.$store.commit('RESET_SELECTED')
|
||||
},
|
||||
resetInput() {
|
||||
this.input = ''
|
||||
},
|
||||
toTop() {
|
||||
this.$vuetify.goTo(0)
|
||||
},
|
||||
handleKeyboardShortcut(e) {
|
||||
// 'ctrl + A' => select torrents
|
||||
if (e.keyCode === 65 && e.ctrlKey) {
|
||||
e.preventDefault()
|
||||
if (this.$store.state.selected_torrents.length === this.torrents.length) {
|
||||
return this.$store.state.selected_torrents = []
|
||||
}
|
||||
const hashes = this.torrents.map(t => t.hash)
|
||||
|
||||
return this.$store.state.selected_torrents = hashes
|
||||
}
|
||||
|
||||
// 'Delete' => Delete modal
|
||||
if (e.keyCode === 46) {
|
||||
e.preventDefault()
|
||||
|
||||
// no torrents select to delete
|
||||
if (!this.selected_torrents.length) return
|
||||
|
||||
return this.createModal('ConfirmDeleteModal')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,79 +1,85 @@
|
|||
<template>
|
||||
<v-layout row wrap align-center class="justify-center">
|
||||
<div style="margin: 130px auto">
|
||||
<v-container class="pa-0">
|
||||
<v-card max-width="400" flat>
|
||||
<v-container class="pa-3 project done">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>Login</h2>
|
||||
</v-card-title>
|
||||
<div class="mr-5 ml-5"></div>
|
||||
<v-card-text>
|
||||
<v-form class="px-3" ref="form">
|
||||
<v-text-field
|
||||
flat
|
||||
solo
|
||||
background-color="torrent"
|
||||
label="username"
|
||||
prepend-icon="person"
|
||||
v-model="username"
|
||||
:rules="inputRules"
|
||||
@keyup.enter.native="Login"
|
||||
autocomplete="current email"
|
||||
name="username"
|
||||
></v-text-field>
|
||||
<v-text-field
|
||||
flat
|
||||
solo
|
||||
background-color="torrent"
|
||||
type="password"
|
||||
label="password"
|
||||
prepend-icon="lock"
|
||||
v-model="password"
|
||||
:rules="inputRules"
|
||||
@keyup.enter.native="Login"
|
||||
autocomplete="current password"
|
||||
name="password"
|
||||
></v-text-field>
|
||||
<v-spacer></v-spacer>
|
||||
<v-card-actions class="justify-center">
|
||||
<v-btn
|
||||
text
|
||||
@click="Login"
|
||||
class="blue_accent white--text mx-0 mt-3"
|
||||
>Login</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</div>
|
||||
</v-layout>
|
||||
<v-layout
|
||||
row
|
||||
wrap
|
||||
align-center
|
||||
class="justify-center"
|
||||
>
|
||||
<div style="margin: 130px auto">
|
||||
<v-container class="pa-0">
|
||||
<v-card max-width="400" flat>
|
||||
<v-container class="pa-3 project done">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>Login</h2>
|
||||
</v-card-title>
|
||||
<div class="mr-5 ml-5" />
|
||||
<v-card-text>
|
||||
<v-form ref="form" class="px-3">
|
||||
<v-text-field
|
||||
v-model="username"
|
||||
flat
|
||||
solo
|
||||
background-color="torrent"
|
||||
label="username"
|
||||
prepend-icon="person"
|
||||
:rules="inputRules"
|
||||
autocomplete="current email"
|
||||
name="username"
|
||||
@keyup.enter.native="Login"
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="password"
|
||||
flat
|
||||
solo
|
||||
background-color="torrent"
|
||||
type="password"
|
||||
label="password"
|
||||
prepend-icon="lock"
|
||||
:rules="inputRules"
|
||||
autocomplete="current password"
|
||||
name="password"
|
||||
@keyup.enter.native="Login"
|
||||
/>
|
||||
<v-spacer />
|
||||
<v-card-actions class="justify-center">
|
||||
<v-btn
|
||||
text
|
||||
class="blue_accent white--text mx-0 mt-3"
|
||||
@click="Login"
|
||||
>
|
||||
Login
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</div>
|
||||
</v-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Login',
|
||||
data() {
|
||||
return {
|
||||
username: '',
|
||||
password: '',
|
||||
inputRules: [v => v.length >= 1 || 'At least 1 character']
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async Login() {
|
||||
const authenticated = await this.$store.dispatch('LOGIN', {
|
||||
username: this.username,
|
||||
password: this.password
|
||||
})
|
||||
|
||||
if (authenticated) {
|
||||
this.$router.push('/')
|
||||
}
|
||||
}
|
||||
name: 'Login',
|
||||
data() {
|
||||
return {
|
||||
username: '',
|
||||
password: '',
|
||||
inputRules: [v => v.length >= 1 || 'At least 1 character']
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async Login() {
|
||||
const authenticated = await this.$store.dispatch('LOGIN', {
|
||||
username: this.username,
|
||||
password: this.password
|
||||
})
|
||||
|
||||
if (authenticated) {
|
||||
this.$router.push('/')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,36 +1,37 @@
|
|||
const webpack = require('webpack')
|
||||
module.exports = {
|
||||
chainWebpack: config => {
|
||||
config
|
||||
.plugin('html')
|
||||
.tap(args => {
|
||||
args[0].title = 'VueTorrent'
|
||||
return args
|
||||
})
|
||||
chainWebpack: config => {
|
||||
config
|
||||
.plugin('html')
|
||||
.tap(args => {
|
||||
args[0].title = 'VueTorrent'
|
||||
|
||||
return args
|
||||
})
|
||||
},
|
||||
outputDir: 'vuetorrent/public',
|
||||
publicPath: './',
|
||||
transpileDependencies: ['vuetify'],
|
||||
configureWebpack: {
|
||||
devtool: 'source-map',
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
APPLICATION_VERSION: JSON.stringify(
|
||||
require('./package.json').version
|
||||
)
|
||||
})
|
||||
]
|
||||
},
|
||||
devServer: {
|
||||
watchOptions: {
|
||||
poll: true
|
||||
},
|
||||
outputDir: 'vuetorrent/public',
|
||||
publicPath: './',
|
||||
transpileDependencies: ['vuetify'],
|
||||
configureWebpack: {
|
||||
devtool: 'source-map',
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
APPLICATION_VERSION: JSON.stringify(
|
||||
require('./package.json').version
|
||||
)
|
||||
})
|
||||
]
|
||||
},
|
||||
devServer: {
|
||||
watchOptions: {
|
||||
poll: true
|
||||
},
|
||||
host: '0.0.0.0',
|
||||
port: 8000,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:8080'
|
||||
}
|
||||
}
|
||||
host: '0.0.0.0',
|
||||
port: 8000,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:8080'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue