This commit is contained in:
Daan Wijns 2020-11-07 13:05:31 +01:00 committed by GitHub
parent 80d1c1f6a0
commit 8b590e4e98
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
51 changed files with 760 additions and 653 deletions

View file

@ -1,33 +1,31 @@
module.exports = {
root: true,
env: {
node: true,
commonjs: true,
browser: true,
es2021: true
},
extends: ['plugin:vue/essential', 'eslint:recommended'],
extends: [
'plugin:vue/essential',
'google'
],
parserOptions: {
parser: 'babel-eslint'
ecmaVersion: 12,
sourceType: 'module'
},
plugins: [
'vue'
],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
indent: ['warn', 4],
semi: ['error', 'never'],
quotes: ['error', 'single'],
'comma-dangle': ['error', 'never'],
'arrow-parens': ['error', 'as-needed'],
'no-underscore-dangle': ['warn', { allowAfterThis: true }]
},
overrides: [
{
files: [
'**/__tests__/*.{j,t}s?(x)',
'**/tests/unit/**/*.spec.{j,t}s?(x)'
],
env: {
jest: true
}
}
]
'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 }]
}
}

View file

@ -10,7 +10,7 @@ The sleekest looking WEBUI for qBittorrent made with Vuejs!
| | | |
| :--------------------------------: | :--------------------------------: | :--------------------------------: |
| ![](https://imgur.com/Zcm98H3.png) | ![](https://imgur.com/OujrH0f.png) | ![](https://imgur.com/OkukwYY.png) |
| ![](https://imgur.com/Zcm98H3.png) | ![](https://imgur.com/OujrH0f.png) | ![](https://imgur.com/3FZTXPL.png) |
| ![](https://imgur.com/QYpNCXs.png) | ![](https://imgur.com/6j5wxhl.png) | ![](https://imgur.com/jnzDKjW.png) |
<p align="center">

View file

@ -1,7 +1,7 @@
version: '3.6'
services:
qbittorrent:
image: linuxserver/qbittorrent
image: linuxserver/qbittorrent:latest
container_name: qbit
environment:
- PUID=1000

314
package-lock.json generated
View file

@ -1,6 +1,6 @@
{
"name": "vuetorrent",
"version": "0.4.2",
"version": "0.4.5",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -990,9 +990,9 @@
}
},
"@eslint/eslintrc": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.3.tgz",
"integrity": "sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==",
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.1.tgz",
"integrity": "sha512-XRUeBZ5zBWLYgSANMpThFddrZZkEbGHgUdt5UJjZfnlN9BGCiUBrf+nvbRupSjMvqzwnQN0qwCmOxITt1cfywA==",
"dev": true,
"requires": {
"ajv": "^6.12.4",
@ -1007,12 +1007,6 @@
"strip-json-comments": "^3.1.1"
},
"dependencies": {
"acorn": {
"version": "7.4.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
"dev": true
},
"ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@ -1025,23 +1019,6 @@
"uri-js": "^4.2.2"
}
},
"eslint-visitor-keys": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
"integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
"dev": true
},
"espree": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz",
"integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==",
"dev": true,
"requires": {
"acorn": "^7.4.0",
"acorn-jsx": "^5.2.0",
"eslint-visitor-keys": "^1.3.0"
}
},
"globals": {
"version": "12.4.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
@ -1052,9 +1029,9 @@
}
},
"import-fresh": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
"integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz",
"integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==",
"dev": true,
"requires": {
"parent-module": "^1.0.0",
@ -1141,6 +1118,50 @@
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==",
"dev": true
},
"@rollup/plugin-babel": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.2.1.tgz",
"integrity": "sha512-Jd7oqFR2dzZJ3NWANDyBjwTtX/lYbZpVcmkHrfQcpvawHs9E4c0nYk5U2mfZ6I/DZcIvy506KZJi54XK/jxH7A==",
"requires": {
"@babel/helper-module-imports": "^7.10.4",
"@rollup/pluginutils": "^3.1.0"
},
"dependencies": {
"@babel/helper-module-imports": {
"version": "7.12.5",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz",
"integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==",
"requires": {
"@babel/types": "^7.12.5"
}
},
"@babel/helper-validator-identifier": {
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
"integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw=="
},
"@babel/types": {
"version": "7.12.6",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz",
"integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==",
"requires": {
"@babel/helper-validator-identifier": "^7.10.4",
"lodash": "^4.17.19",
"to-fast-properties": "^2.0.0"
}
}
}
},
"@rollup/pluginutils": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
"integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
"requires": {
"@types/estree": "0.0.39",
"estree-walker": "^1.0.1",
"picomatch": "^2.2.2"
}
},
"@soda/friendly-errors-webpack-plugin": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.7.1.tgz",
@ -1239,6 +1260,11 @@
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
"dev": true
},
"@types/estree": {
"version": "0.0.39",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
"integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw=="
},
"@types/events": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
@ -1414,20 +1440,20 @@
}
},
"@vue/cli-plugin-pwa": {
"version": "4.5.7",
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-pwa/-/cli-plugin-pwa-4.5.7.tgz",
"integrity": "sha512-mOaEgoLCT2yE8Pdvlz8LhXKqIs3w4xJjDr2dLrOrxh0+OhSpOHJdJ3yHswlgvkxgg0/FGS6t8haj0DfInQ+fYg==",
"version": "4.5.8",
"resolved": "https://registry.npmjs.org/@vue/cli-plugin-pwa/-/cli-plugin-pwa-4.5.8.tgz",
"integrity": "sha512-B5rFJhwhGLHcPUrAUx185X6jlTUJHgN6FTnU+qKspEoONCtGy09/zIL9ejELRw9jxv5C6x2WKUAkyHhh89RkWw==",
"dev": true,
"requires": {
"@vue/cli-shared-utils": "^4.5.7",
"@vue/cli-shared-utils": "^4.5.8",
"webpack": "^4.0.0",
"workbox-webpack-plugin": "^4.3.1"
},
"dependencies": {
"@vue/cli-shared-utils": {
"version": "4.5.7",
"resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-4.5.7.tgz",
"integrity": "sha512-oicFfx9PvgupxN/LW0s2ktdn1U6bBu8J4lPcW2xj6TtTWUkkxwzis4Tm+XOvgvZnu44+d7216y0Y4TX90q645w==",
"version": "4.5.8",
"resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-4.5.8.tgz",
"integrity": "sha512-pa6oenhBO/5HeDLRSokiwVN01gROACEDy3ESXWuPmragOREGNmmFKtkPHlqeYavGEX6LFp7f0VK3uMX6UYS5mQ==",
"dev": true,
"requires": {
"@hapi/joi": "^15.0.1",
@ -2001,9 +2027,9 @@
"dev": true
},
"acorn-jsx": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz",
"integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==",
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz",
"integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==",
"dev": true
},
"acorn-walk": {
@ -2123,10 +2149,11 @@
}
},
"apexcharts": {
"version": "3.22.0",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.22.0.tgz",
"integrity": "sha512-DDh2eXnAEA8GoKU/hdicOaS2jzGehXwv8Bj1djYYudkeQzEdglFoWsVyIxff+Ds7+aUtVAJzd/9ythZuyyIbXQ==",
"version": "3.22.1",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.22.1.tgz",
"integrity": "sha512-wZ/6FT1JMKy9d6ZFbzNt98DLFYnSl19dhD1wav4rh+QTIQSS8qwD79T9ZaSJNXsWv0KfqLu6BIeUI+Z2U9O/eg==",
"requires": {
"@rollup/plugin-babel": "^5.2.1",
"svg.draggable.js": "^2.2.2",
"svg.easing.js": "^2.0.0",
"svg.filter.js": "^2.0.2",
@ -4103,9 +4130,9 @@
}
},
"dayjs": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.9.3.tgz",
"integrity": "sha512-V+1SyIvkS+HmNbN1G7A9+ERbFTV9KTXu6Oor98v2xHmzzpp52OIJhQuJSTywWuBY5pyAEmlwbCi1Me87n/SLOw=="
"version": "1.9.5",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.9.5.tgz",
"integrity": "sha512-WULIw7UpW/E0y6VywewpbXAMH3d5cZijEhoHLwM+OMVbk/NtchKS/W+57H/0P1rqU7gHrAArjiRLHCUhgMQl6w=="
},
"de-indent": {
"version": "1.0.2",
@ -4771,13 +4798,13 @@
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"eslint": {
"version": "7.11.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.11.0.tgz",
"integrity": "sha512-G9+qtYVCHaDi1ZuWzBsOWo2wSwd70TXnU6UHA3cTYHp7gCTXZcpggWFoUVAMRarg68qtPoNfFbzPh+VdOgmwmw==",
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.13.0.tgz",
"integrity": "sha512-uCORMuOO8tUzJmsdRtrvcGq5qposf7Rw0LwkTJkoDbOycVQtQjmnhZSuLQnozLE4TmAzlMVV45eCHmQ1OpDKUQ==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"@eslint/eslintrc": "^0.1.3",
"@eslint/eslintrc": "^0.2.1",
"ajv": "^6.10.0",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
@ -4815,12 +4842,6 @@
"v8-compile-cache": "^2.0.3"
},
"dependencies": {
"acorn": {
"version": "7.4.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
"dev": true
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
@ -4882,25 +4903,6 @@
"integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==",
"dev": true
},
"espree": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz",
"integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==",
"dev": true,
"requires": {
"acorn": "^7.4.0",
"acorn-jsx": "^5.2.0",
"eslint-visitor-keys": "^1.3.0"
},
"dependencies": {
"eslint-visitor-keys": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
"integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
"dev": true
}
}
},
"esrecurse": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
@ -4934,9 +4936,9 @@
"dev": true
},
"import-fresh": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
"integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz",
"integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==",
"dev": true,
"requires": {
"parent-module": "^1.0.0",
@ -5002,6 +5004,12 @@
}
}
},
"eslint-config-google": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/eslint-config-google/-/eslint-config-google-0.14.0.tgz",
"integrity": "sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==",
"dev": true
},
"eslint-config-prettier": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz",
@ -5034,14 +5042,23 @@
}
},
"eslint-plugin-vue": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-6.2.2.tgz",
"integrity": "sha512-Nhc+oVAHm0uz/PkJAWscwIT4ijTrK5fqNqz9QB1D35SbbuMG1uB6Yr5AJpvPSWg+WOw7nYNswerYh0kOk64gqQ==",
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-7.1.0.tgz",
"integrity": "sha512-9dW7kj8/d2IkDdgNpvIhJdJ3XzU3x4PThXYMzWt49taktYnGyrTY6/bXCYZ/VtQKU9kXPntPrZ41+8Pw0Nxblg==",
"dev": true,
"requires": {
"eslint-utils": "^2.1.0",
"natural-compare": "^1.4.0",
"semver": "^5.6.0",
"vue-eslint-parser": "^7.0.0"
"semver": "^7.3.2",
"vue-eslint-parser": "^7.1.1"
},
"dependencies": {
"semver": {
"version": "7.3.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
"integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
"dev": true
}
}
},
"eslint-scope": {
@ -5070,20 +5087,26 @@
"dev": true
},
"espree": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz",
"integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==",
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz",
"integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==",
"dev": true,
"requires": {
"acorn": "^7.1.1",
"acorn": "^7.4.0",
"acorn-jsx": "^5.2.0",
"eslint-visitor-keys": "^1.1.0"
"eslint-visitor-keys": "^1.3.0"
},
"dependencies": {
"acorn": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.2.0.tgz",
"integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==",
"version": "7.4.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
"dev": true
},
"eslint-visitor-keys": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
"integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
"dev": true
}
}
@ -5103,9 +5126,9 @@
},
"dependencies": {
"estraverse": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz",
"integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
"integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
"dev": true
}
}
@ -5125,6 +5148,11 @@
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
"dev": true
},
"estree-walker": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
"integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg=="
},
"esutils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
@ -5790,9 +5818,9 @@
"dev": true
},
"fuse.js": {
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.4.1.tgz",
"integrity": "sha512-+hAS7KYgLXontDh/vqffs7wIBw0ceb9Sx8ywZQhOsiQGcSO5zInGhttWOUYQYlvV/yYMJOacQ129Xs3mP3+oZQ=="
"version": "6.4.3",
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.4.3.tgz",
"integrity": "sha512-JNgngolukIrqwayWnvy6NLH63hmwKPhm63o0uyBg51jPD0j09IvAzlV1rTXfAsgxpghI7khAo6Mv+EmvjDWXig=="
},
"gauge": {
"version": "2.7.4",
@ -8669,8 +8697,7 @@
"picomatch": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
"dev": true
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg=="
},
"pify": {
"version": "4.0.1",
@ -10119,9 +10146,9 @@
"dev": true
},
"sass": {
"version": "1.27.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.27.0.tgz",
"integrity": "sha512-0gcrER56OkzotK/GGwgg4fPrKuiFlPNitO7eUJ18Bs+/NBlofJfMxmxqpqJxjae9vu0Wq8TZzrSyxZal00WDig==",
"version": "1.29.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.29.0.tgz",
"integrity": "sha512-ZpwAUFgnvAUCdkjwPREny+17BpUj8nh5Yr6zKPGtLNTLrmtoRYIjm7njP24COhjJldjwW1dcv52Lpf4tNZVVRA==",
"dev": true,
"requires": {
"chokidar": ">=2.0.0 <4.0.0"
@ -10796,9 +10823,9 @@
}
},
"sortablejs": {
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.12.0.tgz",
"integrity": "sha512-bPn57rCjBRlt2sC24RBsu40wZsmLkSo2XeqG8k6DC1zru5eObQUIPPZAQG7W2SJ8FZQYq+BEJmvuw1Zxb3chqg=="
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.10.2.tgz",
"integrity": "sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A=="
},
"source-list-map": {
"version": "2.0.1",
@ -11523,8 +11550,7 @@
"to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
"dev": true
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
},
"to-object-path": {
"version": "0.3.0",
@ -11948,9 +11974,9 @@
"integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg=="
},
"v8-compile-cache": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz",
"integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz",
"integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==",
"dev": true
},
"validate-npm-package-license": {
@ -12037,9 +12063,9 @@
}
},
"vue-eslint-parser": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.1.0.tgz",
"integrity": "sha512-Kr21uPfthDc63nDl27AGQEhtt9VrZ9nkYk/NTftJ2ws9XiJwzJJCnCr3AITQ2jpRMA0XPGDECxYH8E027qMK9Q==",
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.1.1.tgz",
"integrity": "sha512-8FdXi0gieEwh1IprIBafpiJWcApwrU+l2FEj8c1HtHFdNXMd0+2jUSjBVmcQYohf/E72irwAXEXLga6TQcB3FA==",
"dev": true,
"requires": {
"debug": "^4.1.1",
@ -12050,15 +12076,49 @@
"lodash": "^4.17.15"
},
"dependencies": {
"acorn": {
"version": "7.4.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
"dev": true
},
"eslint-scope": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz",
"integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==",
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
"integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
"dev": true,
"requires": {
"esrecurse": "^4.1.0",
"esrecurse": "^4.3.0",
"estraverse": "^4.1.1"
}
},
"espree": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz",
"integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==",
"dev": true,
"requires": {
"acorn": "^7.1.1",
"acorn-jsx": "^5.2.0",
"eslint-visitor-keys": "^1.1.0"
}
},
"esrecurse": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
"dev": true,
"requires": {
"estraverse": "^5.2.0"
},
"dependencies": {
"estraverse": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
"integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
"dev": true
}
}
}
}
},
@ -12095,9 +12155,9 @@
"integrity": "sha512-xo0CEVdkjSjhJoDdLSvoZoQrw/H2BlzB5jrCBKGZNXN2zdZgMuZ9BKrxXDjNP2AxlcCoKc8OahI3F3r3JGLv2Q=="
},
"vue-router": {
"version": "3.4.7",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.4.7.tgz",
"integrity": "sha512-CbHXue5BLrDivOk5O4eZ0WT4Yj8XwdXa4kCnsEIOzYUPF/07ZukayA2jGxDCJxLc9SgVQX9QX0OuGOwGlVB4Qg=="
"version": "3.4.9",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.4.9.tgz",
"integrity": "sha512-CGAKWN44RqXW06oC+u4mPgHLQQi2t6vLD/JbGRDAXm0YpMv0bgpKuU5bBd7AvMgfTz9kXVRIWKHqRwGEb8xFkA=="
},
"vue-style-loader": {
"version": "4.1.2",
@ -12149,17 +12209,17 @@
}
},
"vuedraggable": {
"version": "2.24.2",
"resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.24.2.tgz",
"integrity": "sha512-y1NbVhLFOVHHdJl7qsYOtExiTq4zyxF+PxiF9NC8kHEtI6sAFhUHtHYp+ONa8v4S3bAspzGHOHuOq0pNO4fFtA==",
"version": "2.24.3",
"resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.24.3.tgz",
"integrity": "sha512-6/HDXi92GzB+Hcs9fC6PAAozK1RLt1ewPTLjK0anTYguXLAeySDmcnqE8IC0xa7shvSzRjQXq3/+dsZ7ETGF3g==",
"requires": {
"sortablejs": "^1.10.1"
"sortablejs": "1.10.2"
}
},
"vuetify": {
"version": "2.3.14",
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.3.14.tgz",
"integrity": "sha512-1Ys1MreJQOL/Ddp3YotBi1SlC2+1A0/RVkDXX3Azspt8incPdAnNB0JyChHiJ/TM+L+KSA7T4EXF9YDrCWENmg=="
"version": "2.3.16",
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.3.16.tgz",
"integrity": "sha512-LHPqY+Gmyb/75xJscO0a3CuB4ZdpqHLNaGMAbmfTyapI8Q02+hjABEZzitFU/XObD2KhrNWPJzmGZPhbshGUzg=="
},
"vuex": {
"version": "3.5.1",

View file

@ -1,20 +1,20 @@
{
"name": "vuetorrent",
"version": "0.4.4",
"version": "0.4.5",
"private": true,
"scripts": {
"start" : "npm run serve",
"start": "npm run serve",
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"@babel/polyfill": "^7.12.1",
"apexcharts": "^3.22.0",
"apexcharts": "^3.22.1",
"axios": "^0.19.2",
"core-js": "^3.6.4",
"dayjs": "^1.9.3",
"fuse.js": "^6.4.1",
"dayjs": "^1.9.5",
"fuse.js": "^6.4.3",
"lodash": "^4.17.20",
"register-service-worker": "^1.7.1",
"uuid": "^8.3.1",
@ -22,27 +22,28 @@
"vue-apexcharts": "^1.6.0",
"vue-context": "^5.2.0",
"vue-observe-visibility": "^0.4.6",
"vue-router": "^3.4.7",
"vue-router": "^3.4.9",
"vue-toastification": "^1.7.8",
"vue2-perfect-scrollbar": "^1.5.0",
"vuedraggable": "^2.24.2",
"vuetify": "^2.3.14",
"vuedraggable": "^2.24.3",
"vuetify": "^2.3.16",
"vuex": "^3.5.1",
"vuex-persist": "^3.1.3"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.3.0",
"@vue/cli-plugin-eslint": "~4.3.0",
"@vue/cli-plugin-pwa": "^4.5.7",
"@vue/cli-plugin-pwa": "^4.5.8",
"@vue/cli-service": "~4.3.0",
"@vue/eslint-config-prettier": "^6.0.0",
"babel-eslint": "^10.1.0",
"eslint": "^7.10.0",
"eslint": "^7.13.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-prettier": "^3.1.4",
"eslint-plugin-vue": "^6.2.2",
"eslint-plugin-vue": "^7.1.0",
"fibers": "^5.0.0",
"node-sass": "^4.14.1",
"sass": "^1.27.0",
"sass": "^1.29.0",
"sass-loader": "^8.0.2",
"vue-cli-plugin-vuetify": "^2.0.7",
"vue-template-compiler": "^2.6.12"

View file

@ -18,10 +18,12 @@ import { mapState, mapGetters } from 'vuex'
import Navbar from '@/components/Navbar/Navbar.vue'
import { version } from '../package.json'
import qbit from '@/services/qbit'
import { General } from '@/mixins'
export default {
components: { Navbar },
name: 'App',
components: { Navbar },
mixins: [General],
data() {
return {}
},
@ -30,18 +32,15 @@ export default {
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')
if (
!authenticated &&
!this.$router.currentRoute.name.includes('login')
) this.$router.push('login')
}
},
computed: {
...mapState(['rid', 'mainData', 'preferences', 'modals']),
...mapGetters(['getTheme', 'getAuthenticated']),
theme() {
return this.getTheme() ? 'dark' : 'light'
},
background() {
return this.$vuetify.theme.themes[this.theme].background
},
...mapGetters(['getAuthenticated']),
isAuthenticated() {
return this.getAuthenticated()
}

View file

@ -121,6 +121,7 @@ import Modal from '@/mixins/Modal'
import qbit from '@/services/qbit'
export default {
name: 'AddModal',
props: ['initialMagnet'],
mixins: [Modal],
data() {
return {
@ -144,14 +145,13 @@ export default {
}
},
methods: {
addDropFile(e)
{
addDropFile(e) {
this.files.push(...Array.from(e.dataTransfer.files))
},
submit() {
if (this.files.length || this.urls) {
let torrents = []
let params = { urls: null, autoTMM: this.autoTMM }
const torrents = []
const params = { urls: null, 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
@ -188,7 +188,7 @@ export default {
let savePath = this.getSettings().save_path
if (this.category) {
savePath += this.category
let category = this.getCategories()[this.category]
const category = this.getCategories()[this.category]
if (category && category.savePath) savePath = category.savePath
}
return savePath
@ -201,6 +201,7 @@ export default {
this.$store.commit('FETCH_SETTINGS')
this.$store.commit('FETCH_CATEGORIES')
this.directory = this.savepath
this.urls = this.initialMagnet
}
}
</script>

View file

@ -63,9 +63,9 @@ export default {
computed: {
...mapState(['selected_torrents']),
...mapGetters(['getTorrents']),
torrents(){
torrents() {
return this.getTorrents().filter(t => this.selected_torrents.includes(t.hash))
}
}
}
</script>
</script>

View file

@ -77,4 +77,4 @@ export default {
this.name = this.torrent.name
}
}
</script>
</script>

View file

@ -1,184 +0,0 @@
<template>
<v-dialog
v-model="dialog"
scrollable
:width="dialogWidth"
:fullscreen="phoneLayout"
>
<v-card :style="{ height: phoneLayout ? '100vh' : '' }">
<v-container
:style="{ height: phoneLayout ? '100vh' : '' }"
:class="`pa-0 project done`"
>
<v-card-title class="justify-center">
<h2>Search Torrent</h2>
</v-card-title>
<v-card-text>
<div>
<v-container>
<v-row no-gutters>
<v-col ref="fileZone">
<v-text-field
label="Search"
prepend-icon="search"
required
:autofocus="!phoneLayout"
v-model="searchTerm"
v-on:keydown.enter="search"
/>
<v-text-field
label="Category"
prepend-icon="category"
v-model="searchCategory"
v-on:keydown.enter="search"
/>
</v-col>
</v-row>
</v-container>
</div>
</v-card-text>
<v-spacer></v-spacer>
<div>
<v-card-actions class="justify-center">
<v-btn
text
@click="search"
:loading="status === 'Running'"
class="blue_accent white--text"
>Search</v-btn
>
</v-card-actions>
</div>
<div class="text-center mt-6">
<p
v-if="noResults"
class="red--text"
style="font-size: 1.3em"
>
No results could be found
</p>
<div v-if="results.length">
<h2>Results</h2>
<perfect-scrollbar>
<v-list
rounded
style="max-height: 500px; min-height: 400px"
>
<v-list-item>
<v-list-item-title
>FileName</v-list-item-title
>
<v-list-item-subtitle style="max-width: 20%"
>Size</v-list-item-subtitle
>
<v-list-item-subtitle style="max-width: 20%"
>Seeders</v-list-item-subtitle
>
<v-list-item-subtitle style="max-width: 20%"
>Leechers</v-list-item-subtitle
>
</v-list-item>
<v-list-item
@click="addTorrent(res.fileUrl)"
v-for="res in results"
:key="res.title"
>
<v-list-item-title>
{{ res.fileName }}
</v-list-item-title>
<v-list-item-subtitle
style="max-width: 20%"
>
{{ res.fileSize | size }}
</v-list-item-subtitle>
<v-list-item-subtitle
style="max-width: 20%"
>
{{ res.nbSeeders }}
</v-list-item-subtitle>
<v-list-item-subtitle
style="max-width: 20%"
>
{{ res.nbLeechers }}
</v-list-item-subtitle>
</v-list-item>
</v-list>
</perfect-scrollbar>
</div>
</div>
</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>
</template>
<script>
import { Modal, FullScreenModal } from '@/mixins'
import qbit from '@/services/qbit'
export default {
name: 'SearchModal',
mixins: [Modal, FullScreenModal],
data() {
return {
searchTerm: null,
searchCategory: null,
searchId: null,
status: null,
searchInterval: null,
results: [],
noResults: false
}
},
computed: {
dialogWidth() {
return this.phoneLayout ? '100%' : '750px'
}
},
methods: {
async search() {
this.noResults = false
if (this.searchTerm && !this.searchInterval) {
this.status = 'Running'
this.results = []
const { data } = await qbit.startSearch(
this.searchTerm,
this.searchCategory
)
this.searchId = data.id
this.searchInterval = setInterval(async () => {
let status = await this.getStatus()
if (status === 'Stopped') {
clearInterval(this.searchInterval)
this.searchInterval = null
this.getResults()
}
}, 2000)
}
},
async getStatus() {
if (this.searchId) {
const { data } = await qbit.getSearchStatus(this.searchId)
return (this.status = data[0].status)
}
},
async getResults() {
const { data } = await qbit.getSearchResults(this.searchId)
this.results = data.results
if (data.results.length === 0) this.noResults = true
},
addTorrent(torrent) {
let params = { urls: null }
params.urls = torrent
qbit.addTorrents(params)
},
close() {
this.$store.commit('DELETE_MODAL', this.guid)
}
}
}
</script>

View file

@ -0,0 +1,67 @@
<template>
<div>
<v-btn @click="opened = true">
<v-icon>mdi-cog</v-icon> Plugin manager
</v-btn>
<v-bottom-sheet
scrollable
inset
v-model="opened"
v-if="this.$vuetify.breakpoint.smAndDown"
>
<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-card-text>
</v-card>
</v-sheet>
</v-bottom-sheet>
<v-dialog v-model="opened" width="50%" v-else>
<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-card-text>
</v-card>
</v-dialog>
</div>
</template>
<script>
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')
}
}
}
</script>

View file

@ -0,0 +1,166 @@
<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>
</template>
<script>
import { mapGetters } from 'vuex'
import qbit from '@/services/qbit'
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)
}
}
}
</script>

View file

@ -144,7 +144,7 @@
</template>
<script>
import {SettingsTab, FullScreenModal} from '@/mixins'
import { SettingsTab, FullScreenModal } from '@/mixins'
export default {
name: 'BitTorrent',

View file

@ -69,7 +69,7 @@
</template>
<script>
import {FullScreenModal, SettingsTab} from '@/mixins'
import { FullScreenModal, SettingsTab } from '@/mixins'
export default {
name: 'Downloads',

View file

@ -77,7 +77,7 @@ import { mapGetters } from 'vuex'
import qbit from '@/services/qbit'
import { Tab, General,FullScreenModal } from '@/mixins'
import { Tab, General, FullScreenModal } from '@/mixins'
export default {
name: 'TagsAndCategories',

View file

@ -23,7 +23,7 @@
<script>
import General from './Vuetorrent/General'
import Dashboard from './Vuetorrent/Dashboard'
import {FullScreenModal} from '@/mixins'
import { FullScreenModal } from '@/mixins'
export default {
name: 'VueTorrent',
@ -31,7 +31,7 @@ export default {
General, Dashboard
},
mixins: [FullScreenModal],
data : () => ({
data: () => ({
tab: null
}),
methods: {
@ -40,4 +40,4 @@ export default {
}
}
}
</script>
</script>

View file

@ -65,11 +65,11 @@ export default {
components: {
draggable
},
computed : {
busyTorrentProperties(){
computed: {
busyTorrentProperties() {
return this.$store.state.webuiSettings.busyTorrentProperties
},
doneTorrentProperties(){
doneTorrentProperties() {
return this.$store.state.webuiSettings.doneTorrentProperties
}
}
@ -78,4 +78,4 @@ export default {
<style lang="scss" scoped>
@import '@/assets/styles/SettingsTab.scss';
</style>
</style>

View file

@ -172,8 +172,8 @@ export default {
return this.getAppVersion()
}
},
methods : {
async fetchQbitVersion(){
methods: {
async fetchQbitVersion() {
this.Qbitversion = await qbit.getAppVersion()
}
},

View file

@ -55,7 +55,7 @@
</template>
<script>
import {FullScreenModal, SettingsTab} from '@/mixins'
import { FullScreenModal, SettingsTab } from '@/mixins'
export default {
name: 'WebUI',

View file

@ -114,12 +114,15 @@ export default {
.filter(f => f.priority === 0)
.map(f => f.id)
if (filesToExclude.length)
if (filesToExclude.length) {
await qbit.setTorrentFilePriority(this.hash, filesToExclude, 0)
if (filesToInclude.length)
}
if (filesToInclude.length) {
await qbit.setTorrentFilePriority(this.hash, filesToInclude, 1)
if (filesToExclude.length || filesToInclude.length)
}
if (filesToExclude.length || filesToInclude.length) {
await this.getTorrentFiles()
}
},
togleEditing(item) {
item.editing = !item.editing

View file

@ -114,8 +114,8 @@ export default {
return this.getTorrent(this.hash)
},
availableTags() {
let availableTags = this.getAvailableTags()
let currentTags = this.getTorrent(this.hash).tags
const availableTags = this.getAvailableTags()
const currentTags = this.getTorrent(this.hash).tags
return difference(availableTags, currentTags)
},
availableCategories() {

View file

@ -116,4 +116,4 @@ export default {
border-radius: 20px;
}
}
</style>
</style>

View file

@ -53,7 +53,7 @@ export default {
theme: 'light',
x: {
formatter: value => {
let val = 32 - value * 2
const val = 32 - value * 2
return val + ' seconds ago'
}
}

View file

@ -1,7 +1,12 @@
<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-checkbox
class="grey--text"
v-model="$store.state.selectMode"
color="grey" hide-details
style="width: 5px;"
/>
</v-btn>
<v-btn
text
@ -95,7 +100,7 @@ export default {
qbit.resumeTorrents(this.selected_torrents)
},
removeTorrents() {
if(!this.selected_torrents.length) return
if (!this.selected_torrents.length) return
return this.createModal('ConfirmDeleteModal')
},
@ -113,4 +118,4 @@ export default {
#no-background-hover {
cursor: default !important;
}
</style>
</style>

View file

@ -19,7 +19,11 @@
<div class="caption grey--text">Torrent title</div>
<div class="truncate">{{ torrent.name }}</div>
</v-flex>
<component :key="'busy' + item.name" v-for="item in properties" :is="item.name" :torrent="torrent" />
<component
:key="'busy' + item.name"
v-for="item in properties"
:is="item.name" :torrent="torrent"
/>
</v-layout>
</template>
<span>{{ torrent.name }}</span>
@ -30,7 +34,7 @@
<script>
import { General, TorrentSelect } from '@/mixins'
import {mapGetters} from 'vuex'
import { mapGetters } from 'vuex'
import {
Size,
Progress,
@ -83,11 +87,11 @@ export default {
phoneLayout() {
return this.$vuetify.breakpoint.xsOnly
},
denseDashboard(){
denseDashboard() {
return this.getWebuiSettings().denseDashboard
},
properties(){
if(this.torrent.progress === 100){
properties() {
if (this.torrent.progress === 100) {
return this.getWebuiSettings().doneTorrentProperties.filter(i => i.active)
}
return this.getWebuiSettings().busyTorrentProperties.filter(i => i.active)
@ -95,7 +99,7 @@ export default {
},
methods: {
showInfo(hash) {
this.createModal('TorrentDetailModal', {hash})
this.createModal('TorrentDetailModal', { hash })
}
}
}

View file

@ -11,4 +11,4 @@ export default {
name: 'Category',
props: ['torrent']
}
</script>
</script>

View file

@ -14,4 +14,4 @@ export default {
name: 'Download',
props: ['torrent']
}
</script>
</script>

View file

@ -11,4 +11,4 @@ export default {
name: 'ETA',
props: ['torrent']
}
</script>
</script>

View file

@ -14,4 +14,4 @@ export default {
name: 'Peers',
props: ['torrent']
}
</script>
</script>

View file

@ -2,7 +2,7 @@
<v-flex xs12 sm1 md1 class="mr-4">
<div class="caption grey--text">Done</div>
<v-progress-linear
v-model="torrent.progress"
:value="torrent.progress"
height="20"
:style="phoneLayout ? '' : 'width: 80%;'"
:color="`torrent-${state}-color`" >
@ -15,10 +15,10 @@
</v-flex>
</template>
<script>
import {TorrentDashboardItem} from '@/mixins'
import { TorrentDashboardItem } from '@/mixins'
export default {
name: 'Progress',
mixins : [TorrentDashboardItem],
mixins: [TorrentDashboardItem],
props: ['torrent']
}
</script>
</script>

View file

@ -9,4 +9,4 @@ export default {
name: 'Ratio',
props: ['torrent']
}
</script>
</script>

View file

@ -14,4 +14,4 @@ export default {
name: 'Seeds',
props: ['torrent']
}
</script>
</script>

View file

@ -14,4 +14,4 @@ export default {
name: 'Size',
props: ['torrent']
}
</script>
</script>

View file

@ -13,10 +13,10 @@
</v-flex>
</template>
<script>
import {TorrentDashboardItem} from '@/mixins'
import { TorrentDashboardItem } from '@/mixins'
export default {
name: 'Status',
mixins : [TorrentDashboardItem],
mixins: [TorrentDashboardItem],
props: ['torrent']
}
</script>
</script>

View file

@ -17,10 +17,10 @@
</v-flex>
</template>
<script>
import {TorrentDashboardItem} from '@/mixins'
import { TorrentDashboardItem } from '@/mixins'
export default {
name: 'Tags',
mixins : [TorrentDashboardItem],
mixins: [TorrentDashboardItem],
props: ['torrent']
}
</script>
</script>

View file

@ -14,4 +14,4 @@ export default {
name: 'Upload',
props: ['torrent']
}
</script>
</script>

View file

@ -22,4 +22,4 @@ export {
Status,
Category,
Tags
}
}

View file

@ -68,7 +68,7 @@
<script>
import qbit from '@/services/qbit'
import { General, TorrentSelect } from '@/mixins'
import { General, TorrentSelect } from '@/mixins'
export default {
name: 'TorrentRightClickMenu',
mixins: [General, TorrentSelect],

View file

@ -9,7 +9,7 @@ export function formatBytes(a, b) {
}
export function getIconForFileType(type) {
let types = {
const types = {
html: 'mdi-language-html5',
js: 'mdi-nodejs',
json: 'mdi-json',
@ -51,7 +51,7 @@ export function codeToFlag(code) {
export function treeify(paths) {
let result = []
let level = { result }
const level = { result }
paths.forEach(path => {
path.name.split('/').reduce((r, name) => {
@ -64,12 +64,12 @@ export function treeify(paths) {
}, level)
})
//parse folders
// parse folders
result = result.map(el => parseFolder(el))
function parseFolder(el) {
if (el.children.length !== 0) {
let folder = createFolder(el.name, el.children)
const folder = createFolder(el.name, el.children)
folder.children = folder.children.map(el => parseFolder(el))
return folder
}

View file

@ -1,8 +1,18 @@
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
}
},
methods: {
createModal(name, props) {
let component = {
const component = {
component: name,
props,
guid: uuidv4()

View file

@ -1,7 +1,7 @@
import {mapGetters} from 'vuex'
import { mapGetters } from 'vuex'
export default {
computed : {
computed: {
...mapGetters(['getTheme']),
phoneLayout() {
return this.$vuetify.breakpoint.xsOnly
@ -13,4 +13,4 @@ export default {
return this.torrent.state.toLowerCase()
}
}
}
}

View file

@ -10,7 +10,7 @@ export default {
this.$store.commit('SET_SELECTED', { type: 'add', hash })
}
},
selectUntil(hash, index){
selectUntil(hash, index) {
this.$store.commit('SET_SELECTED', { type: 'until', hash, index })
}
}

View file

@ -13,4 +13,4 @@ export { FullScreenModal,
General,
TorrentSelect,
TorrentDashboardItem
}
}

View file

@ -70,7 +70,7 @@ export default class Torrent {
}
formatEta(value) {
let options = { dayLimit: 100 }
const options = { dayLimit: 100 }
const minute = 60
const hour = minute * 60
const day = hour * 24
@ -89,9 +89,9 @@ export default class Torrent {
minUnit: 0
}
const opt = options
? Object.assign(defaultOptions, options)
: defaultOptions
const opt = options ?
Object.assign(defaultOptions, options) :
defaultOptions
if (opt.dayLimit && value >= opt.dayLimit * day) {
return '∞'

View file

@ -36,11 +36,13 @@ router.beforeEach(async (to, from, next) => {
if (!isPublic && !authenticated) {
return next({
path: '/login',
query: { redirect: to.fullPath } // Store the full path to redirect the user to after 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
// Do not allow user to visit login page or register page
// if they are logged in
if (authenticated && onlyWhenLoggedOut) {
return next('/')
}

View file

@ -6,10 +6,18 @@ class Qbit {
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)
@ -50,14 +58,88 @@ class Qbit {
}
getMainData(rid) {
const params = {
rid
}
return this.axios.get('/sync/maindata', {
params
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
}
// 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) {
@ -79,149 +161,60 @@ class Qbit {
return this.axios.post('/torrents/add', data)
}
switchToOldUi() {
const params = {
alternative_webui_enabled: false
}
return this.setPreferences(params)
}
setTorrentFilePriority(hash, idList, priority) {
const idListStr = idList.join('|')
const params = {
hash,
id: idListStr,
id: idList.join('|'),
priority
}
const data = new URLSearchParams(params)
return this.axios.post('/torrents/filePrio', data)
}
getLogs(lastId) {
const params = {
last_known_id: lastId
}
return this.axios.get('/log/main', {
params
})
}
toggleSpeedLimitsMode() {
return this.axios.post('/transfer/toggleSpeedLimitsMode')
}
getTorrents(payload) {
let 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()}`)
return this.execute('post', '/torrents/filePrio', params)
}
deleteTorrents(hashes, deleteFiles) {
if(!hashes.length) return
return this.actionTorrents('delete', hashes, { deleteFiles })
if (!hashes.length) return
return this.torrentAction('delete', hashes, { deleteFiles })
}
pauseTorrents(hashes) {
return this.actionTorrents('pause', hashes)
return this.torrentAction('pause', hashes)
}
resumeTorrents(hashes) {
return this.actionTorrents('resume', hashes)
return this.torrentAction('resume', hashes)
}
reannounceTorrents(hashes) {
return this.actionTorrents('reannounce', hashes)
return this.torrentAction('reannounce', hashes)
}
recheckTorrents(hashes) {
return this.actionTorrents('recheck', hashes)
return this.torrentAction('recheck', hashes)
}
setTorrentsCategory(hashes, category) {
return this.actionTorrents('setCategory', hashes, { category })
}
getTorrentTrackers(hash) {
const params = {
hash
}
return this.axios.get('/torrents/trackers', {
params
})
}
getTorrentPeers(hash, rid) {
const params = {
hash,
rid
}
return this.axios.get('/sync/torrentPeers', {
params
})
return this.torrentAction('setCategory', hashes, { category })
}
editTracker(hash, origUrl, newUrl) {
return this.actionTorrents('editTracker', [hash], { origUrl, newUrl })
return this.torrentAction('editTracker', [hash], { origUrl, newUrl })
}
setTorrentLocation(hashes, location) {
return this.actionTorrents('setLocation', hashes, { location })
}
setTorrentName(hash, name) {
const params = {
hash,
name
}
return this.axios.get('/torrents/rename', { params })
return this.torrentAction('setLocation', hashes, { location })
}
getTorrentProperties(hash) {
const params = {
hash
}
return this.axios.get('/torrents/properties', {
params
params: { hash }
})
}
getTorrentPieceStates(hash) {
torrentAction(action, hashes, extra) {
const params = {
hash
hashes: hashes.join('|'),
...extra
}
return this.axios.get('/torrents/pieceStates', {
params
})
}
getTorrentFiles(hash) {
const params = {
hash
}
return this.axios.get('/torrents/files', {
params
})
return this.execute('post', `/torrents/${action}`, params)
}
renameFile(hash, id, name) {
@ -230,81 +223,60 @@ class Qbit {
id,
name
}
const data = new URLSearchParams(params)
return this.axios.post('/torrents/renameFile', data)
}
getAvailableTags() {
return this.axios.get('/torrents/tags')
return this.execute('post', '/torrents/renameFile', params)
}
/** Begin Torrent Tags **/
removeTorrentTag(hash, tag) {
const params = {
return this.execute('post', '/torrents/removeTags', {
hashes: hash,
tags: tag
}
const data = new URLSearchParams(params)
return this.axios.post('/torrents/removeTags', data)
})
}
addTorrentTag(hash, tag) {
const params = {
return this.execute('post', '/torrents/addTags ', {
hashes: hash,
tags: tag
}
const data = new URLSearchParams(params)
return this.axios.post('/torrents/addTags ', data)
})
}
createTag(tag) {
const params = {
return this.execute('/torrents/createTags ', {
tags: tag
}
const data = new URLSearchParams(params)
return this.axios.post('/torrents/createTags ', data)
})
}
deleteTag(tag) {
const params = {
return this.execute('post', '/torrents/deleteTags', {
tags: tag
}
const data = new URLSearchParams(params)
return this.axios.post('/torrents/deleteTags ', data)
})
}
// Begin Categories
/** Begin Categories **/
getCategories() {
return this.axios.get('/torrents/categories')
.then(res => res.data)
}
deleteCategory(cat) {
const params = {
categories: cat
}
const data = new URLSearchParams(params)
return this.axios.post('/torrents/removeCategories ', data)
deleteCategory(categories) {
return this.execute('post', '/torrents/removeCategories', {
categories
})
}
createCategory(cat) {
const params = {
return this.execute('post', '/torrents/createCategory', {
category: cat.name,
savePath: cat.savePath
}
const data = new URLSearchParams(params)
return this.axios.post('/torrents/createCategory ', data)
})
}
setCategory(hash, cat) {
const params = {
return this.execute('post', '/torrents/setCategory', {
hashes: hash,
category: cat
}
const data = new URLSearchParams(params)
return this.axios.post('/torrents/setCategory ', data)
})
}
editCategory(cat) {
@ -312,48 +284,45 @@ class Qbit {
category: cat.name,
savePath: cat.savePath
}
const data = new URLSearchParams(params)
return this.axios.post('/torrents/editCategory ', data)
return this.execute('post', '/torrents/editCategory', params)
}
// End Categories
/** Search **/
getSearchPlugins() {
return this.axios.get( '/search/plugins')
.then(res => res.data)
}
actionTorrents(action, hashes, extra) {
enableSearchPlugin(plugins, enable) {
const params = {
hashes: hashes.join('|'),
...extra
names: plugins.join('|'),
enable
}
const data = new URLSearchParams(params)
return this.axios.post(`/torrents/${action}`, data)
return this.execute('post', '/search/enablePlugin', params)
}
// Search
startSearch(pattern, category = null) {
startSearch(pattern, plugins) {
const params = {
pattern,
plugins: 'all',
category: category ? category : 'all'
plugins: Array.isArray(plugins) ? plugins.join('|') : 'all',
category: 'all'
}
const data = new URLSearchParams(params)
return this.axios.post('/search/start', data)
return this.execute('post', '/search/start', params)
}
stopSearch(id) {
return this.execute('post', '/search/stop', { id })
}
getSearchStatus(id) {
const params = {
id
}
const data = new URLSearchParams(params)
return this.axios.post('/search/status', data)
return this.execute('post', '/search/status', { id })
}
getSearchResults(id) {
const params = {
return this.execute('post', '/search/results', {
id,
limit: 30
}
const data = new URLSearchParams(params)
return this.axios.post('/search/results', data)
limit: 50
})
}
}

View file

@ -20,5 +20,6 @@ export default {
}
return `${state.filteredTorrentsCount} torrents`
}
},
getSearchPlugins: state => () => state.searchPlugins
}

View file

@ -50,34 +50,35 @@ export default new Vuex.Store({
denseDashboard: true,
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: 'Tags', active: true}
{ 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: 'Tags', 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: 'Tags', active: true}
{ 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: 'Tags', active: true }
]
},
categories: [],
filteredTorrentsCount: 0,
latestSelectedTorrent: null,
selectMode: false
selectMode: false,
searchPlugins: []
},
getters: {
...getters

View file

@ -15,7 +15,7 @@ export default {
DELETE_MODAL(state, guid) {
state.modals = state.modals.filter(m => m.guid !== guid)
},
SET_SELECTED: (state, {type, hash, index}) => {
SET_SELECTED: (state, { type, hash, index }) => {
if (type === 'add') {
state.selected_torrents.push(hash)
state.latestSelectedTorrent = state.torrents.map(t => t.hash).indexOf(hash)
@ -25,10 +25,11 @@ export default {
1
)
} else if (type === 'until') {
let from, until
let from
let until
if (state.latestSelectedTorrent > index) {
from = index
until = state.latestSelectedTorrent + 1 //include latest selected
until = state.latestSelectedTorrent + 1 // include latest selected
} else {
from = state.latestSelectedTorrent
until = index + 1
@ -81,9 +82,7 @@ export default {
state.sort_options.category =
payload.category !== null ? payload.category : null
},
FETCH_CATEGORIES: async state => {
const { data } = await qbit.getCategories()
state.categories = data
},
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)
}

View file

@ -40,7 +40,12 @@
: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-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
@ -88,7 +93,7 @@ import { TorrentSelect, General } from '@/mixins'
export default {
name: 'Dashboard',
components: { Torrent, VueContext, TorrentRightClickMenu },
mixins: [ TorrentSelect, General ],
mixins: [TorrentSelect, General],
data() {
return {
input: '',
@ -120,20 +125,20 @@ export default {
paginationSize() {
return this.getWebuiSettings().paginationSize
},
pageCount(){
let l = this.torrents.length,
s = this.paginationSize
pageCount() {
const l = this.torrents.length
const s = this.paginationSize
return Math.ceil(l/s)
},
paginatedData(){
const start = (this.pageNumber - 1) * this.paginationSize,
end = start + this.paginationSize
paginatedData() {
const start = (this.pageNumber - 1) * this.paginationSize
const end = start + this.paginationSize
return this.torrents.slice(start, end)
},
torrentCountString() {
return this.getTorrentCountString()
},
selectMode(){
selectMode() {
return this.$store.state.selectMode
}
},
@ -141,29 +146,29 @@ export default {
resetSelected() {
this.$store.commit('RESET_SELECTED')
},
resetInput(){
resetInput() {
this.input = ''
},
toTop () {
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 = []
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) {
if (e.keyCode === 46) {
e.preventDefault()
//no torrents select to delete
if(!this.selected_torrents.length) return
// no torrents select to delete
if (!this.selected_torrents.length) return
return this.createModal('ConfirmDeleteModal')
}
@ -181,7 +186,7 @@ export default {
document.removeEventListener('keydown', this.handleKeyboardShortcut)
},
watch: {
torrents: function (torrents) {
torrents: function(torrents) {
this.$store.commit('SET_CURRENT_ITEM_COUNT', torrents.length)
}
}

View file

@ -1,9 +1,9 @@
<template>
<v-layout row wrap align-center class="justify-center">
<div style="margin: 130px auto">
<v-container class="grey lighten-4 pa-0">
<v-container class="pa-0">
<v-card max-width="400" flat>
<v-container :class="`pa-3 project done`">
<v-container class="pa-3 project done">
<v-card-title class="justify-center">
<h2>Login</h2>
</v-card-title>