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 = { module.exports = {
root: true,
env: { env: {
node: true, browser: true,
commonjs: true,
es2021: true es2021: true
}, },
extends: ['plugin:vue/essential', 'eslint:recommended'], extends: [
'plugin:vue/essential',
'google'
],
parserOptions: { parserOptions: {
parser: 'babel-eslint' ecmaVersion: 12,
sourceType: 'module'
}, },
plugins: [
'vue'
],
rules: { rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
indent: ['warn', 4], indent: ['warn', 4],
semi: ['error', 'never'], semi: ['error', 'never'],
quotes: ['error', 'single'], quotes: ['error', 'single'],
'comma-dangle': ['error', 'never'], 'comma-dangle': ['error', 'never'],
'arrow-parens': ['error', 'as-needed'], 'arrow-parens': ['error', 'as-needed'],
'no-underscore-dangle': ['warn', { allowAfterThis: true }] 'no-underscore-dangle': ['warn', { allowAfterThis: true }],
}, 'quote-props': ['error', 'as-needed'],
overrides: [ 'object-curly-spacing': ['error', 'always'],
{ 'require-jsdoc': 'off',
files: [ 'valid-jsdoc': 'off',
'**/__tests__/*.{j,t}s?(x)', 'max-len': ['error', { code: 100 }],
'**/tests/unit/**/*.spec.{j,t}s?(x)' 'vue/html-quotes': ['error', 'double', { avoidEscape: true }]
], }
env: {
jest: 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) | | ![](https://imgur.com/QYpNCXs.png) | ![](https://imgur.com/6j5wxhl.png) | ![](https://imgur.com/jnzDKjW.png) |
<p align="center"> <p align="center">

View file

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

314
package-lock.json generated
View file

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

View file

@ -1,20 +1,20 @@
{ {
"name": "vuetorrent", "name": "vuetorrent",
"version": "0.4.4", "version": "0.4.5",
"private": true, "private": true,
"scripts": { "scripts": {
"start" : "npm run serve", "start": "npm run serve",
"serve": "vue-cli-service serve", "serve": "vue-cli-service serve",
"build": "vue-cli-service build", "build": "vue-cli-service build",
"lint": "vue-cli-service lint" "lint": "vue-cli-service lint"
}, },
"dependencies": { "dependencies": {
"@babel/polyfill": "^7.12.1", "@babel/polyfill": "^7.12.1",
"apexcharts": "^3.22.0", "apexcharts": "^3.22.1",
"axios": "^0.19.2", "axios": "^0.19.2",
"core-js": "^3.6.4", "core-js": "^3.6.4",
"dayjs": "^1.9.3", "dayjs": "^1.9.5",
"fuse.js": "^6.4.1", "fuse.js": "^6.4.3",
"lodash": "^4.17.20", "lodash": "^4.17.20",
"register-service-worker": "^1.7.1", "register-service-worker": "^1.7.1",
"uuid": "^8.3.1", "uuid": "^8.3.1",
@ -22,27 +22,28 @@
"vue-apexcharts": "^1.6.0", "vue-apexcharts": "^1.6.0",
"vue-context": "^5.2.0", "vue-context": "^5.2.0",
"vue-observe-visibility": "^0.4.6", "vue-observe-visibility": "^0.4.6",
"vue-router": "^3.4.7", "vue-router": "^3.4.9",
"vue-toastification": "^1.7.8", "vue-toastification": "^1.7.8",
"vue2-perfect-scrollbar": "^1.5.0", "vue2-perfect-scrollbar": "^1.5.0",
"vuedraggable": "^2.24.2", "vuedraggable": "^2.24.3",
"vuetify": "^2.3.14", "vuetify": "^2.3.16",
"vuex": "^3.5.1", "vuex": "^3.5.1",
"vuex-persist": "^3.1.3" "vuex-persist": "^3.1.3"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-babel": "~4.3.0", "@vue/cli-plugin-babel": "~4.3.0",
"@vue/cli-plugin-eslint": "~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/cli-service": "~4.3.0",
"@vue/eslint-config-prettier": "^6.0.0", "@vue/eslint-config-prettier": "^6.0.0",
"babel-eslint": "^10.1.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-prettier": "^3.1.4",
"eslint-plugin-vue": "^6.2.2", "eslint-plugin-vue": "^7.1.0",
"fibers": "^5.0.0", "fibers": "^5.0.0",
"node-sass": "^4.14.1", "node-sass": "^4.14.1",
"sass": "^1.27.0", "sass": "^1.29.0",
"sass-loader": "^8.0.2", "sass-loader": "^8.0.2",
"vue-cli-plugin-vuetify": "^2.0.7", "vue-cli-plugin-vuetify": "^2.0.7",
"vue-template-compiler": "^2.6.12" "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 Navbar from '@/components/Navbar/Navbar.vue'
import { version } from '../package.json' import { version } from '../package.json'
import qbit from '@/services/qbit' import qbit from '@/services/qbit'
import { General } from '@/mixins'
export default { export default {
components: { Navbar },
name: 'App', name: 'App',
components: { Navbar },
mixins: [General],
data() { data() {
return {} return {}
}, },
@ -30,18 +32,15 @@ export default {
const res = await qbit.login() const res = await qbit.login()
const authenticated = res === 'Ok.' const authenticated = res === 'Ok.'
this.$store.commit('LOGIN', authenticated) 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: { computed: {
...mapState(['rid', 'mainData', 'preferences', 'modals']), ...mapState(['rid', 'mainData', 'preferences', 'modals']),
...mapGetters(['getTheme', 'getAuthenticated']), ...mapGetters(['getAuthenticated']),
theme() {
return this.getTheme() ? 'dark' : 'light'
},
background() {
return this.$vuetify.theme.themes[this.theme].background
},
isAuthenticated() { isAuthenticated() {
return this.getAuthenticated() return this.getAuthenticated()
} }

View file

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

View file

@ -63,9 +63,9 @@ export default {
computed: { computed: {
...mapState(['selected_torrents']), ...mapState(['selected_torrents']),
...mapGetters(['getTorrents']), ...mapGetters(['getTorrents']),
torrents(){ torrents() {
return this.getTorrents().filter(t => this.selected_torrents.includes(t.hash)) 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 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> </template>
<script> <script>
import {SettingsTab, FullScreenModal} from '@/mixins' import { SettingsTab, FullScreenModal } from '@/mixins'
export default { export default {
name: 'BitTorrent', name: 'BitTorrent',

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,7 +1,12 @@
<template> <template>
<div> <div>
<v-btn :ripple="false" id="no-background-hover" text> <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>
<v-btn <v-btn
text text
@ -95,7 +100,7 @@ export default {
qbit.resumeTorrents(this.selected_torrents) qbit.resumeTorrents(this.selected_torrents)
}, },
removeTorrents() { removeTorrents() {
if(!this.selected_torrents.length) return if (!this.selected_torrents.length) return
return this.createModal('ConfirmDeleteModal') return this.createModal('ConfirmDeleteModal')
}, },
@ -113,4 +118,4 @@ export default {
#no-background-hover { #no-background-hover {
cursor: default !important; cursor: default !important;
} }
</style> </style>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,8 +1,18 @@
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { mapGetters } from 'vuex'
export default { export default {
computed: {
...mapGetters(['getTheme']),
theme() {
return this.getTheme() ? 'dark' : 'light'
},
background() {
return this.$vuetify.theme.themes[this.theme].background
}
},
methods: { methods: {
createModal(name, props) { createModal(name, props) {
let component = { const component = {
component: name, component: name,
props, props,
guid: uuidv4() guid: uuidv4()

View file

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

View file

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

View file

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

View file

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

View file

@ -36,11 +36,13 @@ router.beforeEach(async (to, from, next) => {
if (!isPublic && !authenticated) { if (!isPublic && !authenticated) {
return next({ return next({
path: '/login', 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) { if (authenticated && onlyWhenLoggedOut) {
return next('/') return next('/')
} }

View file

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

View file

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

View file

@ -50,34 +50,35 @@ export default new Vuex.Store({
denseDashboard: true, denseDashboard: true,
paginationSize: 15, paginationSize: 15,
busyTorrentProperties: [ busyTorrentProperties: [
{ name: 'Size', active: true}, { name: 'Size', active: true },
{ name: 'Progress', active: true}, { name: 'Progress', active: true },
{ name: 'Download', active: true}, { name: 'Download', active: true },
{ name: 'Upload', active: true}, { name: 'Upload', active: true },
{ name: 'ETA', active: true}, { name: 'ETA', active: true },
{ name: 'Peers', active: true}, { name: 'Peers', active: true },
{ name: 'Seeds', active: true}, { name: 'Seeds', active: true },
{ name: 'Status', active: true}, { name: 'Status', active: true },
{ name: 'Ratio', active: true}, { name: 'Ratio', active: true },
{ name: 'Tags', active: true} { name: 'Tags', active: true }
], ],
doneTorrentProperties: [ doneTorrentProperties: [
{ name: 'Size', active: true}, { name: 'Size', active: true },
{ name: 'Progress', active: true}, { name: 'Progress', active: true },
{ name: 'Download', active: true}, { name: 'Download', active: true },
{ name: 'Upload', active: true}, { name: 'Upload', active: true },
{ name: 'ETA', active: true}, { name: 'ETA', active: true },
{ name: 'Peers', active: true}, { name: 'Peers', active: true },
{ name: 'Seeds', active: true}, { name: 'Seeds', active: true },
{ name: 'Status', active: true}, { name: 'Status', active: true },
{ name: 'Ratio', active: true}, { name: 'Ratio', active: true },
{ name: 'Tags', active: true} { name: 'Tags', active: true }
] ]
}, },
categories: [], categories: [],
filteredTorrentsCount: 0, filteredTorrentsCount: 0,
latestSelectedTorrent: null, latestSelectedTorrent: null,
selectMode: false selectMode: false,
searchPlugins: []
}, },
getters: { getters: {
...getters ...getters

View file

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

View file

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

View file

@ -1,9 +1,9 @@
<template> <template>
<v-layout row wrap align-center class="justify-center"> <v-layout row wrap align-center class="justify-center">
<div style="margin: 130px auto"> <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-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"> <v-card-title class="justify-center">
<h2>Login</h2> <h2>Login</h2>
</v-card-title> </v-card-title>