This commit is contained in:
Daan 2020-07-21 09:46:07 +02:00
commit 352a288ae7
7 changed files with 706 additions and 471 deletions

104
package-lock.json generated
View file

@ -1,6 +1,6 @@
{
"name": "vuetorrent",
"version": "0.1.0",
"version": "0.1.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -845,9 +845,9 @@
}
},
"@babel/polyfill": {
"version": "7.8.7",
"resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.8.7.tgz",
"integrity": "sha512-LeSfP9bNZH2UOZgcGcZ0PIHUt1ZuHub1L3CVmEyqLxCeDLm4C5Gi8jRH8ZX2PNpDhQCo0z6y/+DIs2JlliXW8w==",
"version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.10.4.tgz",
"integrity": "sha512-8BYcnVqQ5kMD2HXoHInBH7H1b/uP3KdnwCYXOqFnXqguOyuu443WXusbIUbWEfY3Z0Txk0M1uG/8YuAMhNl6zg==",
"requires": {
"core-js": "^2.6.5",
"regenerator-runtime": "^0.13.4"
@ -1995,9 +1995,9 @@
}
},
"apexcharts": {
"version": "3.19.2",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.19.2.tgz",
"integrity": "sha512-hMFLRE2Lyx4WrN9pYfQLvBDcn+HOodZrqRwc+kucxM+hcUmI2NHY4z+GI14+VcSFmD4aKiMbS3z3Q2jiBxUrcg==",
"version": "3.19.3",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.19.3.tgz",
"integrity": "sha512-pECgHHNR/etDW2SLUTA58ElrrEyUrhQsEgSiBJCLTwgJ8GMPHA/uSiI5pUJ2jy9+v2FY8Tj+8suH4CCCl3T/pQ==",
"requires": {
"svg.draggable.js": "^2.2.2",
"svg.easing.js": "^2.0.0",
@ -3897,9 +3897,9 @@
}
},
"dayjs": {
"version": "1.8.27",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.27.tgz",
"integrity": "sha512-Jpa2acjWIeOkg8KURUHICk0EqnEFSSF5eMEscsOgyJ92ZukXwmpmRkPSUka7KHSfbj5eKH30ieosYip+ky9emQ=="
"version": "1.8.29",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.29.tgz",
"integrity": "sha512-Vm6teig8ZWK7rH/lxzVGxZJCljPdmUr6q/3f4fr5F0VWNGVkZEjZOQJsAN8hUHUqn+NK4XHNEpJZS1MwLyDcLw=="
},
"de-indent": {
"version": "1.0.2",
@ -4673,9 +4673,9 @@
}
},
"eslint-plugin-prettier": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.3.tgz",
"integrity": "sha512-+HG5jmu/dN3ZV3T6eCD7a4BlAySdN7mLIbJYo0z1cFQuI+r2DiTJEFeF68ots93PsnrMxbzIZ2S/ieX+mkrBeQ==",
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz",
"integrity": "sha512-jZDa8z76klRqo+TdGDTFJSavwbnWK2ZpqGKNZ+VvweMW516pDUMmQ2koXvxEE4JhzNvTv+radye/bWGBmA6jmg==",
"dev": true,
"requires": {
"prettier-linter-helpers": "^1.0.0"
@ -6265,9 +6265,9 @@
}
},
"interpret": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz",
"integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
"integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
"dev": true
},
"invariant": {
@ -7763,6 +7763,29 @@
"boolbase": "~1.0.0"
}
},
"null-loader": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/null-loader/-/null-loader-3.0.0.tgz",
"integrity": "sha512-hf5sNLl8xdRho4UPBOOeoIwT3WhjYcMUQm0zj44EhD6UscMAz72o2udpoDFBgykucdEDGIcd6SXbc/G6zssbzw==",
"dev": true,
"requires": {
"loader-utils": "^1.2.3",
"schema-utils": "^1.0.0"
},
"dependencies": {
"schema-utils": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
"integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
"dev": true,
"requires": {
"ajv": "^6.1.0",
"ajv-errors": "^1.0.0",
"ajv-keywords": "^3.1.0"
}
}
}
},
"num2fraction": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
@ -9878,9 +9901,9 @@
"dev": true
},
"sass": {
"version": "1.26.5",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.26.5.tgz",
"integrity": "sha512-FG2swzaZUiX53YzZSjSakzvGtlds0lcbF+URuU9mxOv7WBh7NhXEVDa4kPKN4hN6fC2TkOTOKqiqp6d53N9X5Q==",
"version": "1.26.10",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.26.10.tgz",
"integrity": "sha512-bzN0uvmzfsTvjz0qwccN1sPm2HxxpNI/Xa+7PlUEMS+nQvbyuEK7Y0qFqxlPHhiNHb1Ze8WQJtU31olMObkAMw==",
"dev": true,
"requires": {
"chokidar": ">=2.0.0 <4.0.0"
@ -11712,9 +11735,9 @@
"integrity": "sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ=="
},
"vue-apexcharts": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/vue-apexcharts/-/vue-apexcharts-1.5.3.tgz",
"integrity": "sha512-ImbvQxgwbLMrEc9/veDIJ7lzncf1fJDSNqqK0x2YDNUCq5tE9uqM4Gb/ZYUB5WlDM3vDpzwDEmsidWcaO6/WXQ=="
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/vue-apexcharts/-/vue-apexcharts-1.6.0.tgz",
"integrity": "sha512-sT6tuVTLBwfH3TA7azecDNS/W70bmz14ZJI7aE7QIqcG9I6OywyH7x3hcOeY1v1DxttI8Svc5RuYj4Dd+A5F4g=="
},
"vue-async-computed": {
"version": "3.8.2",
@ -11722,11 +11745,12 @@
"integrity": "sha512-If5roOhp/x0WWd0TWRD77YsuFoiIw3MbkcRlIB/FE3TqQCPje52eQp5CV5NN/7B0VAyPuGX5JQa+rc9AOcGAYw=="
},
"vue-cli-plugin-vuetify": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/vue-cli-plugin-vuetify/-/vue-cli-plugin-vuetify-2.0.5.tgz",
"integrity": "sha512-jtxcidjLT5f1H9QLYKLFjo/ZG42ud4pI9bK3WNO5DXyhiMDMTwSZ7b3NcJVRH7tKYbv5/ty0VPdDGlf4w22AMA==",
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/vue-cli-plugin-vuetify/-/vue-cli-plugin-vuetify-2.0.7.tgz",
"integrity": "sha512-4riK5bzyvkZ4CxpQk/Vl6z8n8tmJUhuxh+k8xc/MZRdCt9RxAm3G4SxcEweroqKGXg+CRRfhqysaEQVtd4D40Q==",
"dev": true,
"requires": {
"null-loader": "^3.0.0",
"semver": "^7.1.2",
"shelljs": "^0.8.3"
},
@ -11748,9 +11772,9 @@
}
},
"vue-context": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/vue-context/-/vue-context-5.1.0.tgz",
"integrity": "sha512-PcKbc9mjiUt4fswRR/oA/IJc5oXHjpnKIcATWZcBTDk7CzTvLznkIjq6pGFI8vUtGzABxVMvPq93dpBSRfinxg==",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/vue-context/-/vue-context-5.2.0.tgz",
"integrity": "sha512-XH3SwDanAcE7ppzVEkXqpMyzkFKUDp8TDh4vBE9UPbT6OHwLIwtANH6ZAakq8q2iV+hGtDDfwYgX12IbZjyNnw==",
"requires": {
"vue-clickaway": "^2.2.2"
}
@ -11814,9 +11838,9 @@
"integrity": "sha512-xo0CEVdkjSjhJoDdLSvoZoQrw/H2BlzB5jrCBKGZNXN2zdZgMuZ9BKrxXDjNP2AxlcCoKc8OahI3F3r3JGLv2Q=="
},
"vue-router": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.2.0.tgz",
"integrity": "sha512-khkrcUIzMcI1rDcNtqkvLwfRFzB97GmJEsPAQdj7t/VvpGhmXLOkUfhc+Ah8CvpSXGXwuWuQO+x8Sy/xDhXZIA=="
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.3.4.tgz",
"integrity": "sha512-SdKRBeoXUjaZ9R/8AyxsdTqkOfMcI5tWxPZOUX5Ie1BTL5rPSZ0O++pbiZCeYeythiZIdLEfkDiQPKIaWk5hDg=="
},
"vue-style-loader": {
"version": "4.1.2",
@ -11853,9 +11877,9 @@
"dev": true
},
"vue-toastification": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/vue-toastification/-/vue-toastification-1.7.1.tgz",
"integrity": "sha512-T0byaYmOgyTWnROjNBZ7P2aFmHu8RS69JM7TI5kuUC4LWji05xDnuXAj/KmPibakydQTHlgrU2CeGHDp+40PKQ=="
"version": "1.7.6",
"resolved": "https://registry.npmjs.org/vue-toastification/-/vue-toastification-1.7.6.tgz",
"integrity": "sha512-+Nezz/mXglufIaZ+0EAUDYm4Qc4sRPKu7bOX1WMoc0PrYAOVGzEafN7xW7KlVERijk7wUbMtAP9dP1tSMWV4Yw=="
},
"vue2-perfect-scrollbar": {
"version": "1.5.0",
@ -11868,14 +11892,14 @@
}
},
"vuetify": {
"version": "2.2.29",
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.2.29.tgz",
"integrity": "sha512-UI1i8kEdV+s7tNQe2yJJMPlvxGTE8cZfUAXEGV2yO4owHmeybn6p5YzFpJqimoKkfX6ok+zV29+6yIKfkyUDAA=="
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.3.4.tgz",
"integrity": "sha512-vMtCNqv5BhrjfTfIhH2Lptoxx7z/Nu1NfBiZ2oCvI2QbTagMlhF5GMXgbnnyTGIjooFf/ozKznuMxk6tuI5cxw=="
},
"vuex": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.4.0.tgz",
"integrity": "sha512-ajtqwEW/QhnrBZQsZxCLHThZZaa+Db45c92Asf46ZDXu6uHXgbfVuBaJ4gzD2r4UX0oMJHstFwd2r2HM4l8umg=="
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.5.1.tgz",
"integrity": "sha512-w7oJzmHQs0FM9LXodfskhw9wgKBiaB+totOdb8sNzbTB2KDCEEwEs29NzBZFh/lmEK1t5tDmM1vtsO7ubG1DFw=="
},
"vuex-persist": {
"version": "2.2.0",

View file

@ -1,6 +1,6 @@
{
"name": "vuetorrent",
"version": "0.1.0",
"version": "0.1.1",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
@ -9,22 +9,22 @@
"format": "pretty-quick"
},
"dependencies": {
"@babel/polyfill": "^7.4.4",
"apexcharts": "^3.19.2",
"@babel/polyfill": "^7.10.4",
"apexcharts": "^3.19.3",
"axios": "^0.19.2",
"core-js": "^3.6.4",
"dayjs": "^1.8.27",
"dayjs": "^1.8.29",
"register-service-worker": "^1.7.1",
"vue": "^2.6.11",
"vue-apexcharts": "^1.5.3",
"vue-apexcharts": "^1.6.0",
"vue-async-computed": "^3.8.2",
"vue-context": "^5.1.0",
"vue-context": "^5.2.0",
"vue-observe-visibility": "^0.4.6",
"vue-router": "^3.2.0",
"vue-toastification": "^1.7.1",
"vue-router": "^3.3.4",
"vue-toastification": "^1.7.6",
"vue2-perfect-scrollbar": "^1.5.0",
"vuetify": "^2.2.29",
"vuex": "^3.4.0",
"vuetify": "^2.3.4",
"vuex": "^3.5.1",
"vuex-persist": "^2.2.0"
},
"devDependencies": {
@ -34,15 +34,15 @@
"@vue/eslint-config-prettier": "^6.0.0",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-prettier": "^3.1.3",
"eslint-plugin-prettier": "^3.1.4",
"eslint-plugin-vue": "^6.2.2",
"fibers": "^5.0.0",
"node-sass": "^4.14.1",
"prettier": "^2.0.5",
"pretty-quick": "^2.0.1",
"sass": "^1.26.5",
"sass": "^1.26.10",
"sass-loader": "^8.0.2",
"vue-cli-plugin-vuetify": "~2.0.5",
"vue-cli-plugin-vuetify": "^2.0.7",
"vue-template-compiler": "^2.6.11"
},
"browserslist": [

View file

@ -2,6 +2,7 @@
<v-app :style="{ backgroundColor: background }">
<AddModal />
<SettingsModal />
<SearchModal />
<Navbar v-if="isAuthenticated" />
<v-content fill-height fill-width>
<router-view></router-view>

View file

@ -30,16 +30,17 @@
dark
label
small
>{{ text }}</v-chip
>
{{ text }}
</v-chip>
<span
v-else-if="index === 2"
class="overline grey--text text--darken-3 mx-2"
>+{{
files.length - 2
}}
File(s)</span
>
+{{ files.length - 2 }} File(s)
</span>
</template>
</v-file-input>
<v-text-field
@ -123,7 +124,8 @@ export default {
},
resetForm() {
this.url = null
;(this.files = []), (this.directory = null)
this.files = []
this.directory = null
}
},
computed: {

View file

@ -0,0 +1,159 @@
<template>
<v-dialog max-width="500px" v-model="dialog">
<v-card min-height="400px">
<v-container
style="min-height: 400px;"
: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-btn text @click="getStatus" class="blue_accent white--text ml-6">Status</v-btn> -->
</v-card-actions>
</div>
<div class="text-center mt-10" 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>
</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 from '@/mixins/Modal'
import qbit from '@/services/qbit'
export default {
name: 'SearchModal',
mixins: [Modal],
data() {
return {
searchTerm: null,
searchCategory: null,
searchId: null,
status: null,
searchInterval: null,
results: []
}
},
methods: {
async search() {
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.getResults()
}
}, 2000)
}
},
async getStatus() {
if (this.searchId) {
let response = await qbit.getSearchStatus(this.searchId)
return (this.status = response.data[0].status)
}
},
async getResults() {
let response = await qbit.getSearchResults(this.searchId)
this.results = response.data.results
},
addTorrent(torrent) {
let params = { urls: null }
params.urls = torrent
qbit.addTorrents(params)
},
close() {
this.$store.commit('TOGGLE_MODAL', 'SearchModal')
}
},
computed: {
phoneLayout() {
return this.$vuetify.breakpoint.xsOnly
}
}
}
</script>

View file

@ -1,413 +1,433 @@
<template>
<nav>
<!--title-->
<v-app-bar app flat color="background">
<v-app-bar-nav-icon
@click.stop="drawer = !drawer"
class="grey--text text--lighten-1"
></v-app-bar-nav-icon>
<v-toolbar-title
:class="[
'grey--text',
{ 'subheading ml-0': $vuetify.breakpoint.smAndDown }
]"
>
<div v-if="!$vuetify.breakpoint.xs">
<span class="font-weight-light">Vue</span>
<span>Torrent</span>
</div>
</v-toolbar-title>
<v-spacer></v-spacer>
<!--right corner functions-->
<v-btn
text
small
fab
color="grey"
class="mr-0 ml-0"
@click="toggleModal('addmodal')"
>
<v-icon color="grey">add</v-icon>
</v-btn>
<v-btn small fab text class="mr-0 ml-0" @click="removeTorrents">
<v-icon color="grey">remove</v-icon>
</v-btn>
<v-btn small fab text class="mr-0 ml-0" @click="resumeTorrents">
<v-icon color="grey">play_arrow</v-icon>
</v-btn>
<v-btn small fab text class="mr-0 ml-0" @click="pauseTorrents">
<v-icon color="grey">pause</v-icon>
</v-btn>
<v-btn
small
fab
text
class="mr-0 ml-0"
@click="toggleModal('settingsmodal')"
>
<v-icon color="grey">settings</v-icon>
</v-btn>
</v-app-bar>
<!--navigation drawer itself -->
<v-navigation-drawer
app
v-model="drawer"
class="primary"
style="position: fixed;"
disable-resize-watcher
>
<!--current download speeds -->
<v-flex class="mt-3" v-if="status">
<div
class="secondary_lighter--text text-uppercase caption ml-4"
>
current speed
</div>
<v-card color="secondary" flat class="mr-2 ml-2">
<v-layout
row
wrap
class="pa-3 project nav_download mx-auto"
>
<v-icon color="download">keyboard_arrow_down</v-icon>
<span class="download--text title">
{{
status.dlspeed.substring(
0,
status.dlspeed.indexOf(' ')
)
}}
<span class="font-weight-light caption">{{
status.dlspeed.substring(
status.dlspeed.indexOf(' ')
)
}}</span>
</span>
<v-icon class="pl-5" color="upload"
>keyboard_arrow_up</v-icon
>
<span class="upload--text title">
{{
status.upspeed.substring(
0,
status.upspeed.indexOf(' ')
)
}}
<span class="font-weight-light caption">{{
status.upspeed.substring(
status.upspeed.indexOf(' ')
)
}}</span>
</span>
</v-layout>
</v-card>
<!--speeds graph -->
<div class="mt-4">
<apexcharts
ref="chart"
type="line"
:options="chartOptions"
:series="series"
></apexcharts>
</div>
<div class="mt-4"></div>
<div
class="secondary_lighter--text text-uppercase caption ml-4"
>
session status
</div>
<v-card flat color="secondary" class="mr-2 ml-2">
<v-layout
row
wrap
class="pa-3 project nav_download mx-auto"
>
<v-flex md6>
<div
style="font-size: 0.95em; margin-top: 6px;"
class="download--text"
>
Downloaded
</div>
</v-flex>
<v-flex md5 class="ml-4">
<span class="download--text title">
{{
status.downloaded.substring(
0,
status.downloaded.indexOf(' ')
)
}}
<span class="font-weight-light caption">{{
status.downloaded.substring(
status.downloaded.indexOf(' ')
)
}}</span>
</span>
</v-flex>
</v-layout>
</v-card>
<v-card flat color="secondary" class="ml-2 mr-2 mt-1">
<v-layout row wrap class="pa-3 project nav_upload mx-auto">
<v-flex md6>
<div
style="font-size: 0.95em; margin-top: 6px;"
class="upload--text"
>
Uploaded
</div>
</v-flex>
<v-flex md5 class="ml-4">
<span class="upload--text title">
{{
status.uploaded.substring(
0,
status.uploaded.indexOf(' ')
)
}}
<span class="font-weight-light caption">{{
status.uploaded.substring(
status.uploaded.indexOf(' ')
)
}}</span>
</span>
</v-flex>
</v-layout>
</v-card>
<v-card
v-if="webuiSettings.showFreeSpace"
flat
style="margin-top: 30px;"
color="secondary"
class="ml-2 mr-2"
>
<v-layout row wrap class="pa-3 project nav_upload mx-auto">
<v-flex md6>
<div
style="font-size: 0.95em; margin-top: 6px;"
class="upload--text"
>
Free Space
</div>
</v-flex>
<v-flex md5 class="ml-4">
<span class="upload--text title">
{{
status.freeDiskSpace.substring(
0,
status.freeDiskSpace.indexOf(' ')
)
}}
<span class="font-weight-light caption">{{
status.freeDiskSpace.substring(
status.freeDiskSpace.indexOf(' ')
)
}}</span>
</span>
</v-flex>
</v-layout>
</v-card>
</v-flex>
<v-container>
<v-row justify="space-between">
<v-col>
<v-tooltip top>
<template v-slot:activator="{ on }">
<v-list-item v-on="on" @click="logout" link>
<v-icon class="white--text"
>exit_to_app</v-icon
>
</v-list-item>
</template>
<span>Log out</span>
</v-tooltip>
</v-col>
<v-col>
<v-tooltip top>
<template v-slot:activator="{ on }">
<v-list-item
v-on="on"
@click="toggleSpeed"
link
>
<v-icon
:color="altSpeed ? 'download' : ''"
class="white--text"
>speed</v-icon
>
</v-list-item>
</template>
<span>Alt speeds</span>
</v-tooltip>
</v-col>
<v-col>
<v-tooltip top>
<template v-slot:activator="{ on }">
<v-list-item
v-on="on"
@click="toggleTheme"
link
>
<v-icon
v-if="theme === 'Light'"
class="white--text"
>brightness_7</v-icon
>
<v-icon v-else class="pr-2 white--text"
>brightness_2</v-icon
>
</v-list-item>
</template>
<span>{{ theme }}</span>
</v-tooltip>
</v-col>
</v-row>
</v-container>
</v-navigation-drawer>
</nav>
</template>
<script>
import { mapMutations, mapState, mapGetters } from 'vuex'
import VueApexCharts from 'vue-apexcharts'
import qbit from '@/services/qbit'
export default {
components: { apexcharts: VueApexCharts },
data() {
return {
drawer: false,
paused: false,
chartOptions: {
chart: {
sparkline: {
enabled: true
},
animations: {
enabled: false,
dynamicAnimation: {
speed: 1000
}
}
},
colors: ['#00b3fa', '#64CEAA'],
stroke: {
show: true,
curve: 'smooth',
lineCap: 'round',
width: 4
},
fill: {
type: 'gradient',
gradient: {
shade: 'dark',
type: 'vertical',
shadeIntensity: 0.5,
opacityFrom: 0.6,
opacityTo: 0.5,
stops: [0, 50, 100]
}
},
tooltip: {
theme: 'light'
}
},
chartInterval: null
}
},
methods: {
...mapMutations(['REFRESH_TORRENTS', 'CLEAR_INTERVALS']),
pauseTorrents() {
qbit.pauseTorrents(this.selected_torrents)
},
resumeTorrents() {
qbit.resumeTorrents(this.selected_torrents)
},
removeTorrents() {
qbit.deleteTorrents(this.selected_torrents, false)
},
updateChart() {
this.$refs.chart.updateSeries(this.series, true)
},
toggleModal(name) {
this.$store.commit('TOGGLE_MODAL', name)
},
toggleTheme() {
this.$store.commit('TOGGLE_THEME')
this.$vuetify.theme.dark = !this.$vuetify.theme.dark
},
logout() {
this.$store.commit('LOGOUT')
this.$router.push('/login')
},
toggleSpeed() {
qbit.toggleSpeedLimitsMode()
},
setChartTooltipTheme(theme) {
this.chartOptions.tooltip.theme = theme.toLowerCase()
this.$refs.chart.updateOptions(this.chartOptions)
}
},
computed: {
...mapState(['status', 'selected_torrents']),
...mapGetters(['getTheme', 'getStatus', 'getWebuiSettings']),
theme() {
return this.getTheme() ? 'Dark' : 'Light'
},
altSpeed() {
return this.getStatus().altSpeed
},
series() {
return [
{
name: 'upload',
type: 'area',
data: this.$store.state.upload_data
},
{
name: 'download',
type: 'area',
data: this.$store.state.download_data
}
]
},
webuiSettings() {
return this.getWebuiSettings()
}
},
created() {
this.$vuetify.theme.dark = this.getTheme()
},
mounted() {
this.setChartTooltipTheme(this.theme)
},
watch: {
theme(newValue) {
this.setChartTooltipTheme(newValue)
}
}
}
</script>
<style lang="scss" scoped>
.justify-space-between {
position: fixed;
bottom: 0px;
width: 100%;
}
</style>
<style>
.project.nav_upload {
border-left: 4px solid #00b3fa;
}
.project.nav_download {
border-left: 4px solid #64ceaa;
}
.allow-spacer {
display: flex;
flex-direction: column;
}
</style>
<template>
<nav>
<!--title-->
<v-app-bar app flat color="background">
<v-app-bar-nav-icon
@click.stop="drawer = !drawer"
class="grey--text text--lighten-1"
></v-app-bar-nav-icon>
<v-toolbar-title
:class="[
'grey--text',
{ 'subheading ml-0': $vuetify.breakpoint.smAndDown }
]"
>
<div v-if="!$vuetify.breakpoint.xs">
<span class="font-weight-light">Vue</span>
<span>Torrent</span>
</div>
</v-toolbar-title>
<v-spacer></v-spacer>
<!--right corner functions-->
<v-btn
text
small
fab
color="grey"
class="mr-0 ml-0"
@click="toggleModal('searchmodal')"
>
<v-icon color="grey">search</v-icon>
</v-btn>
<v-btn
text
small
fab
color="grey"
class="mr-0 ml-0"
@click="toggleModal('addmodal')"
>
<v-icon color="grey">add</v-icon>
</v-btn>
<v-btn small fab text class="mr-0 ml-0" @click="removeTorrents">
<v-icon color="grey">remove</v-icon>
</v-btn>
<v-btn small fab text class="mr-0 ml-0" @click="resumeTorrents">
<v-icon color="grey">play_arrow</v-icon>
</v-btn>
<v-btn small fab text class="mr-0 ml-0" @click="pauseTorrents">
<v-icon color="grey">pause</v-icon>
</v-btn>
<v-btn
small
fab
text
class="mr-0 ml-0"
@click="toggleModal('settingsmodal')"
>
<v-icon color="grey">settings</v-icon>
</v-btn>
</v-app-bar>
<!--navigation drawer itself -->
<v-navigation-drawer
app
v-model="drawer"
class="primary"
style="position: fixed;"
disable-resize-watcher
>
<!--current download speeds -->
<v-flex class="mt-3" v-if="status">
<div
class="secondary_lighter--text text-uppercase caption ml-4"
>
current speed
</div>
<v-card color="secondary" flat class="mr-2 ml-2">
<v-layout
row
wrap
class="pa-3 project nav_download mx-auto"
>
<v-icon color="download">keyboard_arrow_down</v-icon>
<span class="download--text title">
{{
status.dlspeed.substring(
0,
status.dlspeed.indexOf(' ')
)
}}
<span class="font-weight-light caption">
{{
status.dlspeed.substring(
status.dlspeed.indexOf(' ')
)
}}
</span>
</span>
<v-icon class="pl-5" color="upload"
>keyboard_arrow_up</v-icon
>
<span class="upload--text title">
{{
status.upspeed.substring(
0,
status.upspeed.indexOf(' ')
)
}}
<span class="font-weight-light caption">
{{
status.upspeed.substring(
status.upspeed.indexOf(' ')
)
}}
</span>
</span>
</v-layout>
</v-card>
<!--speeds graph -->
<div class="mt-4">
<apexcharts
ref="chart"
type="line"
:options="chartOptions"
:series="series"
></apexcharts>
</div>
<div class="mt-4"></div>
<div
class="secondary_lighter--text text-uppercase caption ml-4"
>
session status
</div>
<v-card flat color="secondary" class="mr-2 ml-2">
<v-layout
row
wrap
class="pa-3 project nav_download mx-auto"
>
<v-flex md6>
<div
style="font-size: 0.95em; margin-top: 6px;"
class="download--text"
>
Downloaded
</div>
</v-flex>
<v-flex md5 class="ml-4">
<span class="download--text title">
{{
status.downloaded.substring(
0,
status.downloaded.indexOf(' ')
)
}}
<span class="font-weight-light caption">
{{
status.downloaded.substring(
status.downloaded.indexOf(' ')
)
}}
</span>
</span>
</v-flex>
</v-layout>
</v-card>
<v-card flat color="secondary" class="ml-2 mr-2 mt-1">
<v-layout row wrap class="pa-3 project nav_upload mx-auto">
<v-flex md6>
<div
style="font-size: 0.95em; margin-top: 6px;"
class="upload--text"
>
Uploaded
</div>
</v-flex>
<v-flex md5 class="ml-4">
<span class="upload--text title">
{{
status.uploaded.substring(
0,
status.uploaded.indexOf(' ')
)
}}
<span class="font-weight-light caption">
{{
status.uploaded.substring(
status.uploaded.indexOf(' ')
)
}}
</span>
</span>
</v-flex>
</v-layout>
</v-card>
<v-card
v-if="webuiSettings.showFreeSpace"
flat
style="margin-top: 30px;"
color="secondary"
class="ml-2 mr-2"
>
<v-layout row wrap class="pa-3 project nav_upload mx-auto">
<v-flex md6>
<div
style="font-size: 0.95em; margin-top: 6px;"
class="upload--text"
>
Free Space
</div>
</v-flex>
<v-flex md5 class="ml-4">
<span class="upload--text title">
{{
status.freeDiskSpace.substring(
0,
status.freeDiskSpace.indexOf(' ')
)
}}
<span class="font-weight-light caption">
{{
status.freeDiskSpace.substring(
status.freeDiskSpace.indexOf(' ')
)
}}
</span>
</span>
</v-flex>
</v-layout>
</v-card>
</v-flex>
<v-container>
<v-row justify="space-between">
<v-col>
<v-tooltip top>
<template v-slot:activator="{ on }">
<v-list-item v-on="on" @click="logout" link>
<v-icon class="white--text"
>exit_to_app</v-icon
>
</v-list-item>
</template>
<span>Log out</span>
</v-tooltip>
</v-col>
<v-col>
<v-tooltip top>
<template v-slot:activator="{ on }">
<v-list-item
v-on="on"
@click="toggleSpeed"
link
>
<v-icon
:color="altSpeed ? 'download' : ''"
class="white--text"
>speed</v-icon
>
</v-list-item>
</template>
<span>Alt speeds</span>
</v-tooltip>
</v-col>
<v-col>
<v-tooltip top>
<template v-slot:activator="{ on }">
<v-list-item
v-on="on"
@click="toggleTheme"
link
>
<v-icon
v-if="theme === 'Light'"
class="white--text"
>brightness_7</v-icon
>
<v-icon v-else class="pr-2 white--text"
>brightness_2</v-icon
>
</v-list-item>
</template>
<span>{{ theme }}</span>
</v-tooltip>
</v-col>
</v-row>
</v-container>
</v-navigation-drawer>
</nav>
</template>
<script>
import { mapMutations, mapState, mapGetters } from 'vuex'
import VueApexCharts from 'vue-apexcharts'
import qbit from '@/services/qbit'
export default {
components: { apexcharts: VueApexCharts },
data() {
return {
drawer: false,
paused: false,
chartOptions: {
chart: {
sparkline: {
enabled: true
},
animations: {
enabled: false,
dynamicAnimation: {
speed: 1000
}
}
},
colors: ['#00b3fa', '#64CEAA'],
stroke: {
show: true,
curve: 'smooth',
lineCap: 'round',
width: 4
},
fill: {
type: 'gradient',
gradient: {
shade: 'dark',
type: 'vertical',
shadeIntensity: 0.5,
opacityFrom: 0.6,
opacityTo: 0.5,
stops: [0, 50, 100]
}
},
tooltip: {
theme: 'light'
}
},
chartInterval: null
}
},
methods: {
...mapMutations(['REFRESH_TORRENTS', 'CLEAR_INTERVALS']),
pauseTorrents() {
qbit.pauseTorrents(this.selected_torrents)
},
resumeTorrents() {
qbit.resumeTorrents(this.selected_torrents)
},
removeTorrents() {
qbit.deleteTorrents(this.selected_torrents, false)
},
updateChart() {
this.$refs.chart.updateSeries(this.series, true)
},
toggleModal(name) {
this.$store.commit('TOGGLE_MODAL', name)
},
toggleTheme() {
this.$store.commit('TOGGLE_THEME')
this.$vuetify.theme.dark = !this.$vuetify.theme.dark
},
logout() {
this.$store.commit('LOGOUT')
this.$router.push('/login')
},
toggleSpeed() {
qbit.toggleSpeedLimitsMode()
},
setChartTooltipTheme(theme) {
this.chartOptions.tooltip.theme = theme.toLowerCase()
this.$refs.chart.updateOptions(this.chartOptions)
}
},
computed: {
...mapState(['status', 'selected_torrents']),
...mapGetters(['getTheme', 'getStatus', 'getWebuiSettings']),
theme() {
return this.getTheme() ? 'Dark' : 'Light'
},
altSpeed() {
return this.getStatus().altSpeed
},
series() {
return [
{
name: 'upload',
type: 'area',
data: this.$store.state.upload_data
},
{
name: 'download',
type: 'area',
data: this.$store.state.download_data
}
]
},
webuiSettings() {
return this.getWebuiSettings()
}
},
created() {
this.$vuetify.theme.dark = this.getTheme()
},
mounted() {
this.setChartTooltipTheme(this.theme)
},
watch: {
theme(newValue) {
this.setChartTooltipTheme(newValue)
}
}
}
</script>
<style lang="scss" scoped>
.justify-space-between {
position: fixed;
bottom: 0px;
width: 100%;
}
</style>
<style>
.project.nav_upload {
border-left: 4px solid #00b3fa;
}
.project.nav_download {
border-left: 4px solid #64ceaa;
}
.allow-spacer {
display: flex;
flex-direction: column;
}
</style>

View file

@ -261,6 +261,35 @@ class Qbit {
const data = new URLSearchParams(params)
return this.axios.post(`/torrents/${action}`, data)
}
// Search
startSearch(pattern, category = null) {
const params = {
pattern,
plugins: 'all',
category: category ? category : 'all'
}
const data = new URLSearchParams(params)
return this.axios.post('/search/start', data)
}
getSearchStatus(id) {
const params = {
id
}
const data = new URLSearchParams(params)
return this.axios.post('/search/status', data)
}
getSearchResults(id) {
const params = {
id,
limit: 30
}
const data = new URLSearchParams(params)
return this.axios.post('/search/results', data)
}
}
export default new Qbit()