mirror of
https://github.com/VueTorrent/VueTorrent.git
synced 2025-02-26 20:31:13 +03:00
torrent detail page + file list
This commit is contained in:
parent
7819abb1e7
commit
09832e6f1a
15 changed files with 554 additions and 323 deletions
81
README.md
81
README.md
|
@ -1,71 +1,130 @@
|
||||||
|
|
||||||
# VueTorrent
|
# VueTorrent
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The sleekest looking WEBUI for qBittorrent made with Vuejs!
|
The sleekest looking WEBUI for qBittorrent made with Vuejs!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
> Vue, qBitorrent, Vuetify
|
> Vue, qBitorrent, Vuetify
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
|
<a href="https://imgur.com/xgwECT2.png"><img src="https://imgur.com/xgwECT2.png" title="Desktop" alt="Desktop Screenshot" ></a>
|
||||||
<a href="https://i.imgur.com/vPBcrK4.png"><img src="https://i.imgur.com/vPBcrK4.png" title="Desktop" alt="Desktop Screenshot" ></a>
|
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
<a href="https://i.imgur.com/SUOEyy9.png"><img src="https://i.imgur.com/SUOEyy9.png" title="Mobile" alt="Mobile Screenshot" width="320" height="540"></a>
|
<a href="https://i.imgur.com/SUOEyy9.png"><img src="https://i.imgur.com/SUOEyy9.png" title="Mobile" alt="Mobile Screenshot" width="320" height="540"></a>
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- Download & Unzip the latest release
|
- Download & Unzip the latest release
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- Point your Alternate WEBUI location to it
|
- Point your Alternate WEBUI location to it
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- clone the repo
|
- clone the repo
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- npm install
|
- npm install
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- npm run serve
|
- npm run serve
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- viewing sessions status ( down / upload speed, session uploaded / downloaded )
|
- viewing sessions status ( down / upload speed, session uploaded / downloaded )
|
||||||
|
|
||||||
- adding / removing / pausing / resuming torrents
|
- adding / removing / pausing / resuming torrents
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- sorting by every property shown!
|
- sorting by every property shown!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* mobile friendly! (maybe not for thousands of torrents...)
|
* mobile friendly! (maybe not for thousands of torrents...)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- works on QBittorrent V4.2 and later
|
- works on QBittorrent V4.2 and later
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
I'll gladly accept help/pull requests & advice! (this is my first project of this nature, pls be kind 😛 ).
|
I'll gladly accept help/pull requests & advice! (this is my first project of this nature, pls be kind 😛 ).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
- **Why build this??**
|
|
||||||
|
|
||||||
|
- **Why build this??**
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* Why not? Most WebUI's look very dated and now it's no longer necessary to search for a remote control app!
|
* Why not? Most WebUI's look very dated and now it's no longer necessary to search for a remote control app!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Reach out to me at one of the following places!
|
Reach out to me at one of the following places!
|
||||||
|
|
||||||
- <a href="https://m.me/WijnsDaan" target="_blank">`Facebook Messenger`</a>
|
|
||||||
|
|
||||||
|
- <a href="https://m.me/WijnsDaan" target="_blank">`Facebook Messenger`</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* Open up an issue 😛
|
* Open up an issue 😛
|
||||||
|
|
||||||
[<img src="https://cdn.buymeacoffee.com/buttons/lato-blue.png" alt="drawing" width="180"/>](https://www.buymeacoffee.com/wdaan "Buy me a coffee")
|
|
||||||
|
|
||||||
|
[<img src="https://cdn.buymeacoffee.com/buttons/lato-blue.png" alt="drawing" width="180"/>](https://www.buymeacoffee.com/wdaan "Buy me a coffee")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
- Dashboard design heavily inspired by: '[Net Ninja - Vuetify](https://github.com/iamshaunjp/vuetify-playlist)'.
|
|
||||||
Also check out The Net Ninja's Youtube Channel.
|
|
||||||
|
|
||||||
* This repo '[CzBiX qb-web ](https://github.com/CzBiX/qb-web)'
|
- Dashboard design heavily inspired by: '[Net Ninja - Vuetify](https://github.com/iamshaunjp/vuetify-playlist)'.
|
||||||
|
|
||||||
|
Also check out The Net Ninja's Youtube Channel.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* This repo '[CzBiX qb-web ](https://github.com/CzBiX/qb-web)'3.0)
|
379
package-lock.json
generated
379
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -19,6 +19,7 @@
|
||||||
"vue-observe-visibility": "^0.4.6",
|
"vue-observe-visibility": "^0.4.6",
|
||||||
"vue-router": "^3.2.0",
|
"vue-router": "^3.2.0",
|
||||||
"vue-toastification": "^1.7.1",
|
"vue-toastification": "^1.7.1",
|
||||||
|
"vue2-perfect-scrollbar": "^1.5.0",
|
||||||
"vuetify": "^2.2.11",
|
"vuetify": "^2.2.11",
|
||||||
"vuex": "^3.4.0",
|
"vuex": "^3.4.0",
|
||||||
"vuex-persist": "^2.2.0"
|
"vuex-persist": "^2.2.0"
|
||||||
|
|
18
src/App.vue
18
src/App.vue
|
@ -1,9 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<v-app :style="{ backgroundColor : background }" >
|
<v-app :style="{ backgroundColor : background }" >
|
||||||
<AddModal />
|
<AddModal />
|
||||||
<Navbar v-if="authenticated" />
|
<TorrentDetailModal/>
|
||||||
<v-container fill-height fill-width>
|
<Navbar v-if="isAuthenticated" />
|
||||||
<v-content>
|
<v-container class="pa-4">
|
||||||
|
<v-content fill-height fill-width>
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
</v-content>
|
</v-content>
|
||||||
</v-container>
|
</v-container>
|
||||||
|
@ -13,6 +14,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { mapState, mapGetters } from 'vuex'
|
import { mapState, mapGetters } from 'vuex'
|
||||||
import Navbar from '@/components/Navbar.vue'
|
import Navbar from '@/components/Navbar.vue'
|
||||||
|
import {isAuthenticated} from '@/services/auth.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { Navbar },
|
components: { Navbar },
|
||||||
|
@ -20,14 +22,22 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
async getAuth(){
|
||||||
|
return await isAuthenticated()
|
||||||
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['authenticated', 'rid', 'mainData', 'preferences']),
|
...mapState(['rid', 'mainData', 'preferences']),
|
||||||
...mapGetters(['getTheme']),
|
...mapGetters(['getTheme']),
|
||||||
theme() {
|
theme() {
|
||||||
return this.getTheme() ? 'dark' : 'light'
|
return this.getTheme() ? 'dark' : 'light'
|
||||||
},
|
},
|
||||||
background(){
|
background(){
|
||||||
return this.$vuetify.theme.themes[this.theme].background
|
return this.$vuetify.theme.themes[this.theme].background
|
||||||
|
},
|
||||||
|
isAuthenticated(){
|
||||||
|
return this.getAuth()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
203
src/components/Modals/TorrentDetailModal.vue
Normal file
203
src/components/Modals/TorrentDetailModal.vue
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
<template>
|
||||||
|
<v-dialog max-width="800px" v-model="dialog" scrollable>
|
||||||
|
<v-card
|
||||||
|
v-if="torrent"
|
||||||
|
style="min-height: 400px; overflow:hidden !important"
|
||||||
|
>
|
||||||
|
<v-container :class="`pa-0 project ${torrent.state}`">
|
||||||
|
<v-card-title class="justify-center primary">
|
||||||
|
<h2 class="white--text">Torrent Detail</h2>
|
||||||
|
</v-card-title>
|
||||||
|
<v-tabs v-model="tab" background-color="primary" fixed-tabs>
|
||||||
|
<v-tab v-for="item in items" :key="item.tab">
|
||||||
|
{{ item.tab }}
|
||||||
|
</v-tab>
|
||||||
|
</v-tabs>
|
||||||
|
|
||||||
|
<v-tabs-items v-model="tab">
|
||||||
|
<v-tab-item>
|
||||||
|
<v-card flat>
|
||||||
|
<v-card-text style="font-size: 1.2em">
|
||||||
|
<v-flex>
|
||||||
|
<span class="grey--text">Torrent title </span>
|
||||||
|
<span class="torrentmodaltext--text ">{{
|
||||||
|
torrent.name
|
||||||
|
}}</span>
|
||||||
|
</v-flex>
|
||||||
|
<v-flex class="mt-2">
|
||||||
|
<span class="grey--text">hash </span>
|
||||||
|
<span class="torrentmodaltext--text ">{{
|
||||||
|
torrent.hash
|
||||||
|
}}</span>
|
||||||
|
</v-flex>
|
||||||
|
<v-flex class="mt-2">
|
||||||
|
<span class="grey--text">Size </span>
|
||||||
|
<span class="torrentmodaltext--text ">
|
||||||
|
{{ torrent.size }}
|
||||||
|
</span>
|
||||||
|
</v-flex>
|
||||||
|
<v-flex class="mt-2">
|
||||||
|
<span class="grey--text">Done: </span>
|
||||||
|
<span class="torrentmodaltext--text ">
|
||||||
|
{{ torrent.dloaded }}
|
||||||
|
</span>
|
||||||
|
</v-flex>
|
||||||
|
<v-flex class="mt-2">
|
||||||
|
<span class="grey--text">Download </span>
|
||||||
|
<span class="torrentmodaltext--text ">{{
|
||||||
|
torrent.dlspeed
|
||||||
|
}}</span>
|
||||||
|
</v-flex>
|
||||||
|
<v-flex class="mt-2">
|
||||||
|
<span class="grey--text">Upload </span>
|
||||||
|
<span class="torrentmodaltext--text ">{{
|
||||||
|
torrent.upspeed
|
||||||
|
}}</span>
|
||||||
|
</v-flex>
|
||||||
|
<v-flex class="mt-2">
|
||||||
|
<span class="grey--text">ETA </span>
|
||||||
|
<span class="torrentmodaltext--text ">{{
|
||||||
|
torrent.eta
|
||||||
|
}}</span>
|
||||||
|
</v-flex>
|
||||||
|
<v-flex class="mt-2">
|
||||||
|
<span class="grey--text">Peers </span>
|
||||||
|
<span class="torrentmodaltext--text ">
|
||||||
|
{{ torrent.num_leechs
|
||||||
|
}}<span class="grey--text"
|
||||||
|
>/{{ torrent.available_peers }}</span
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</v-flex>
|
||||||
|
<v-flex class="mt-2">
|
||||||
|
<span class=" grey--text">Seeds </span>
|
||||||
|
<span class="torrentmodaltext--text ">
|
||||||
|
{{ torrent.num_seeds
|
||||||
|
}}<span class="grey--text"
|
||||||
|
>/{{ torrent.available_seeds }}</span
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</v-flex>
|
||||||
|
<v-flex class="mt-2">
|
||||||
|
<span class=" grey--text">Ratio </span>
|
||||||
|
<span class="torrentmodaltext--text ">
|
||||||
|
{{ torrent.ratio }}%
|
||||||
|
</span>
|
||||||
|
</v-flex>
|
||||||
|
<v-flex>
|
||||||
|
<span class="grey--text">Status </span>
|
||||||
|
<v-chip
|
||||||
|
small
|
||||||
|
:class="`${torrent.state} white--text my-2 caption`"
|
||||||
|
>{{ torrent.state }}</v-chip
|
||||||
|
>
|
||||||
|
</v-flex>
|
||||||
|
<v-flex>
|
||||||
|
<v-progress-linear
|
||||||
|
height="5"
|
||||||
|
stream
|
||||||
|
rounded
|
||||||
|
color="cyan darken-1"
|
||||||
|
background-color="cyan lighten-3"
|
||||||
|
:buffer-value="torrent.progress"
|
||||||
|
></v-progress-linear>
|
||||||
|
</v-flex>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-tab-item>
|
||||||
|
<v-tab-item>
|
||||||
|
|
||||||
|
<v-card flat class="scrollbar" style="overflow-y: auto !important;">
|
||||||
|
<perfect-scrollbar>
|
||||||
|
<v-card-text style="max-height: 500px; min-height: 400px;">
|
||||||
|
<v-treeview
|
||||||
|
v-model="tree"
|
||||||
|
:items="fileTree"
|
||||||
|
activatable
|
||||||
|
item-key="name"
|
||||||
|
open-on-click
|
||||||
|
>
|
||||||
|
<template v-slot:prepend="{ item, open }">
|
||||||
|
<v-icon v-if="!item.icon">
|
||||||
|
{{ open ? "mdi-folder-open" : "mdi-folder" }}
|
||||||
|
</v-icon>
|
||||||
|
<v-icon v-else>
|
||||||
|
{{ item.icon }}
|
||||||
|
</v-icon>
|
||||||
|
</template>
|
||||||
|
<template v-slot:append="{ item }">
|
||||||
|
<span v-if="!item.icon">
|
||||||
|
{{ item.children.length }} Files
|
||||||
|
</span>
|
||||||
|
<div v-else>
|
||||||
|
<span>[{{ item.size }}]</span>
|
||||||
|
<span class="ml-4">{{ item.progress }}%</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</v-treeview>
|
||||||
|
</v-card-text>
|
||||||
|
</perfect-scrollbar>
|
||||||
|
</v-card>
|
||||||
|
|
||||||
|
</v-tab-item>
|
||||||
|
</v-tabs-items>
|
||||||
|
</v-container>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Modal from "@/mixins/Modal";
|
||||||
|
import { mapGetters } from "vuex";
|
||||||
|
import qbit from "@/services/qbit";
|
||||||
|
import { treeify } from "@/helpers";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "TorrentDetailModal",
|
||||||
|
mixins: [Modal],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tab: null,
|
||||||
|
items: [{ tab: "Info" }, { tab: "Content" }],
|
||||||
|
tempFileTree: [],
|
||||||
|
fileTree: [],
|
||||||
|
tree: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async getTorrentProperties() {
|
||||||
|
const { data } = await qbit.getTorrentFiles(this.hash);
|
||||||
|
// console.log(data)
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
async getTorrentFiles() {
|
||||||
|
const { data } = await qbit.getTorrentFiles(this.hash);
|
||||||
|
this.fileTree = treeify(data);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters(["getTorrent"]),
|
||||||
|
hash() {
|
||||||
|
return this.$store.state.selectedDetailTorrent;
|
||||||
|
},
|
||||||
|
torrent() {
|
||||||
|
return this.getTorrent(this.hash);
|
||||||
|
},
|
||||||
|
torrentFiles() {
|
||||||
|
let arr = this.getTorrentFiles();
|
||||||
|
console.log(arr);
|
||||||
|
return arr.map(this.addnode);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
dialog(visible) {
|
||||||
|
if (visible) {
|
||||||
|
this.getTorrentProperties();
|
||||||
|
this.getTorrentFiles();
|
||||||
|
} else {
|
||||||
|
this.fileTree = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -226,7 +226,7 @@ export default {
|
||||||
animations: {
|
animations: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
dynamicAnimation: {
|
dynamicAnimation: {
|
||||||
speed: 2000
|
speed: 1000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -249,18 +249,6 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: 'upload',
|
|
||||||
type: 'area',
|
|
||||||
data: this.$store.state.upload_data
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'download',
|
|
||||||
type: 'area',
|
|
||||||
data: this.$store.state.download_data
|
|
||||||
}
|
|
||||||
],
|
|
||||||
chartInterval: null
|
chartInterval: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -301,16 +289,24 @@ export default {
|
||||||
},
|
},
|
||||||
altSpeed(){
|
altSpeed(){
|
||||||
return this.getStatus().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
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.chartInterval = setInterval(async () => {
|
|
||||||
this.updateChart()
|
|
||||||
}, 2000)
|
|
||||||
this.$vuetify.theme.dark = this.getTheme()
|
this.$vuetify.theme.dark = this.getTheme()
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
clearInterval(this.chartInterval)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -89,9 +89,10 @@
|
||||||
<v-flex xs12 sm12 md12>
|
<v-flex xs12 sm12 md12>
|
||||||
<v-progress-linear
|
<v-progress-linear
|
||||||
height="3"
|
height="3"
|
||||||
|
rounded
|
||||||
color="cyan darken-1"
|
color="cyan darken-1"
|
||||||
background-color="cyan lighten-3"
|
background-color="cyan lighten-3"
|
||||||
:value="(torrent.dloaded / torrent.size) * 100"
|
:value="torrent.progress"
|
||||||
></v-progress-linear>
|
></v-progress-linear>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
</v-layout>
|
</v-layout>
|
||||||
|
@ -99,7 +100,6 @@
|
||||||
<span>{{ torrent.name }}</span>
|
<span>{{ torrent.name }}</span>
|
||||||
</v-tooltip>
|
</v-tooltip>
|
||||||
<v-divider></v-divider>
|
<v-divider></v-divider>
|
||||||
|
|
||||||
<vue-context ref="menu">
|
<vue-context ref="menu">
|
||||||
<torrentRightClickMenu :hash="torrent.hash" />
|
<torrentRightClickMenu :hash="torrent.hash" />
|
||||||
</vue-context>
|
</vue-context>
|
||||||
|
|
|
@ -6,6 +6,11 @@
|
||||||
:dark="dark"
|
:dark="dark"
|
||||||
>
|
>
|
||||||
<v-list dense rounded>
|
<v-list dense rounded>
|
||||||
|
<v-list-item @click="showInfo" link>
|
||||||
|
<v-icon>info</v-icon>
|
||||||
|
<v-list-item-title class="ml-2" style="font-size:15px;">Show Info</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
<v-divider/>
|
||||||
<v-list-item @click="resume" link>
|
<v-list-item @click="resume" link>
|
||||||
<v-icon>play_arrow</v-icon>
|
<v-icon>play_arrow</v-icon>
|
||||||
<v-list-item-title class="ml-2" style="font-size:15px;">Resume</v-list-item-title>
|
<v-list-item-title class="ml-2" style="font-size:15px;">Resume</v-list-item-title>
|
||||||
|
@ -54,6 +59,10 @@ export default {
|
||||||
},
|
},
|
||||||
deleteWithFiles(){
|
deleteWithFiles(){
|
||||||
qbit.deleteTorrents([this.hash], true)
|
qbit.deleteTorrents([this.hash], true)
|
||||||
|
},
|
||||||
|
showInfo(){
|
||||||
|
this.$store.commit('TOGGLE_MODAL', 'TorrentDetailModal')
|
||||||
|
this.$store.commit('SET_SELECTED_TORRENT_DETAIL', this.hash)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
79
src/helpers.js
Normal file
79
src/helpers.js
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
/* eslint-disable no-unused-vars */
|
||||||
|
export function formatBytes(a, b) {
|
||||||
|
if (a == 0) return "0 Bytes";
|
||||||
|
const c = 1024;
|
||||||
|
const d = b || 2;
|
||||||
|
const e = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||||
|
const f = Math.floor(Math.log(a) / Math.log(c));
|
||||||
|
return `${parseFloat((a / Math.pow(c, f)).toFixed(d))} ${e[f]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getIconForFileType(type) {
|
||||||
|
let types = {
|
||||||
|
html: "mdi-language-html5",
|
||||||
|
js: "mdi-nodejs",
|
||||||
|
json: "mdi-json",
|
||||||
|
md: "mdi-markdown",
|
||||||
|
pdf: "mdi-file-pdf",
|
||||||
|
png: "mdi-file-image",
|
||||||
|
txt: "mdi-file-document-outline",
|
||||||
|
sub: "mdi-file-document-outline",
|
||||||
|
idx: "mdi-file-document-outline",
|
||||||
|
xls: "mdi-file-excel",
|
||||||
|
avi: "movie",
|
||||||
|
mp4: "movie",
|
||||||
|
mkv: "movie",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!types[type]) return "insert_drive_file";
|
||||||
|
|
||||||
|
return types[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function treeify(paths) {
|
||||||
|
let result = [];
|
||||||
|
let level = { result };
|
||||||
|
|
||||||
|
paths.forEach((path) => {
|
||||||
|
path.name.split("/").reduce((r, name, i, a) => {
|
||||||
|
if (!r[name]) {
|
||||||
|
r[name] = { result: [] };
|
||||||
|
r.result.push(createFile(path, name, r[name].result));
|
||||||
|
}
|
||||||
|
|
||||||
|
return r[name];
|
||||||
|
}, level);
|
||||||
|
});
|
||||||
|
|
||||||
|
//parse folders
|
||||||
|
result = result.map((el) => parseFolder(el));
|
||||||
|
|
||||||
|
function parseFolder(el) {
|
||||||
|
if (el.children.length !== 0) {
|
||||||
|
let folder = createFolder(el.name, el.children);
|
||||||
|
folder.children = folder.children.map((el) => parseFolder(el));
|
||||||
|
return folder;
|
||||||
|
}
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFile(data, name, children) {
|
||||||
|
return {
|
||||||
|
name: name,
|
||||||
|
progress: Math.round(data.progress * 100),
|
||||||
|
size: formatBytes(data.size),
|
||||||
|
icon: getIconForFileType(name.split(".").pop()),
|
||||||
|
children: children,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFolder(name, children) {
|
||||||
|
return {
|
||||||
|
name: name,
|
||||||
|
type: "directory",
|
||||||
|
children: children,
|
||||||
|
};
|
||||||
|
}
|
11
src/main.js
11
src/main.js
|
@ -11,7 +11,16 @@ Vue.use(VueObserveVisibility)
|
||||||
import Toast from 'vue-toastification'
|
import Toast from 'vue-toastification'
|
||||||
import 'vue-toastification/dist/index.css'
|
import 'vue-toastification/dist/index.css'
|
||||||
import vuetify from './plugins/vuetify'
|
import vuetify from './plugins/vuetify'
|
||||||
Vue.use(Toast)
|
Vue.use(Toast, {
|
||||||
|
maxToasts: 5,
|
||||||
|
timeout: 2000
|
||||||
|
})
|
||||||
|
|
||||||
|
import PerfectScrollbar from 'vue2-perfect-scrollbar'
|
||||||
|
import 'vue2-perfect-scrollbar/dist/vue2-perfect-scrollbar.css'
|
||||||
|
|
||||||
|
Vue.use(PerfectScrollbar)
|
||||||
|
|
||||||
|
|
||||||
Vue.config.productionTip = false
|
Vue.config.productionTip = false
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,8 @@ export default class Status {
|
||||||
this.upspeed = this.formatBytes(data.up_info_speed, 1)
|
this.upspeed = this.formatBytes(data.up_info_speed, 1)
|
||||||
this.freeDiskSpace = this.formatBytes(data.free_space_on_disk)
|
this.freeDiskSpace = this.formatBytes(data.free_space_on_disk)
|
||||||
this.altSpeed = data.use_alt_speed_limits
|
this.altSpeed = data.use_alt_speed_limits
|
||||||
|
this.dlspeedRaw = Math.round(data.dl_info_speed / 1000)
|
||||||
|
this.upspeedRaw = Math.round(data.up_info_speed / 1000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
export default class Torrent {
|
export default class Torrent {
|
||||||
constructor(data) {
|
constructor(data) {
|
||||||
this.id = data.id
|
|
||||||
this.name = data.name
|
this.name = data.name
|
||||||
this.size = this.formatBytes(data.size)
|
this.size = this.formatBytes(data.size)
|
||||||
this.birth = new Date(data.added_on * 1000).toLocaleString()
|
this.birth = new Date(data.added_on * 1000).toLocaleString()
|
||||||
|
@ -20,6 +19,9 @@ export default class Torrent {
|
||||||
// available seeds
|
// available seeds
|
||||||
this.available_seeds = data.num_complete
|
this.available_seeds = data.num_complete
|
||||||
this.available_peers = data.num_incomplete
|
this.available_peers = data.num_incomplete
|
||||||
|
this.savePath = data.save_path
|
||||||
|
this.progress = Math.round(data.downloaded / data.size * 100)
|
||||||
|
this.ratio = Math.round(data.ratio * 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
formatState(state) {
|
formatState(state) {
|
||||||
|
|
|
@ -29,7 +29,8 @@ export default new Vuetify({
|
||||||
torrent: '#fff',
|
torrent: '#fff',
|
||||||
torrent_selected: colors.grey.lighten2,
|
torrent_selected: colors.grey.lighten2,
|
||||||
background: colors.grey.lighten4,
|
background: colors.grey.lighten4,
|
||||||
search: colors.grey.darken1
|
search: colors.grey.darken1,
|
||||||
|
torrentmodaltext: colors.grey.darken4
|
||||||
},
|
},
|
||||||
dark: {
|
dark: {
|
||||||
primary: "#35495e",
|
primary: "#35495e",
|
||||||
|
@ -44,7 +45,8 @@ export default new Vuetify({
|
||||||
torrent: colors.grey.darken3,
|
torrent: colors.grey.darken3,
|
||||||
torrent_selected: colors.grey,
|
torrent_selected: colors.grey,
|
||||||
background: colors.grey.darken4,
|
background: colors.grey.darken4,
|
||||||
search: colors.grey.darken3
|
search: colors.grey.darken3,
|
||||||
|
torrentmodaltext: colors.grey.lighten4
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -33,9 +33,11 @@ export default new Vuex.Store({
|
||||||
modals: {
|
modals: {
|
||||||
addmodal: false,
|
addmodal: false,
|
||||||
deletemodal: false,
|
deletemodal: false,
|
||||||
settingsmodal: false
|
settingsmodal: false,
|
||||||
|
torrentdetailmodal: false
|
||||||
},
|
},
|
||||||
settings : {}
|
settings : {},
|
||||||
|
selectedDetailTorrent: null
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
containsTorrent: state => hash =>
|
containsTorrent: state => hash =>
|
||||||
|
@ -43,7 +45,8 @@ export default new Vuex.Store({
|
||||||
getTheme: state => () => state.darkTheme,
|
getTheme: state => () => state.darkTheme,
|
||||||
getModalState: state => name => state.modals[name.toLowerCase()],
|
getModalState: state => name => state.modals[name.toLowerCase()],
|
||||||
getSettings: state => () => state.settings,
|
getSettings: state => () => state.settings,
|
||||||
getStatus: state => () => state.status
|
getStatus: state => () => state.status,
|
||||||
|
getTorrent: state => hash => state.torrents.filter(el => el.hash === hash)[0]
|
||||||
},
|
},
|
||||||
|
|
||||||
mutations: {
|
mutations: {
|
||||||
|
@ -92,40 +95,17 @@ export default new Vuex.Store({
|
||||||
state.status = new Status(data.server_state)
|
state.status = new Status(data.server_state)
|
||||||
|
|
||||||
// graph
|
// graph
|
||||||
|
|
||||||
state.download_data.splice(0, 1)
|
state.download_data.splice(0, 1)
|
||||||
if (state.status.dlspeed.indexOf('KB' > -1)) {
|
state.download_data.push(state.status.dlspeedRaw)
|
||||||
state.download_data.push(
|
|
||||||
state.status.dlspeed.substring(
|
|
||||||
0,
|
|
||||||
state.status.dlspeed.indexOf(' ')
|
|
||||||
) / 1000
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
state.download_data.push(
|
|
||||||
state.status.dlspeed(0, state.status.dlspeed.indexOf(' '))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
state.upload_data.splice(0, 1)
|
state.upload_data.splice(0, 1)
|
||||||
if (state.status.upspeed.indexOf('KB' > -1)) {
|
state.upload_data.push(state.status.upspeedRaw)
|
||||||
state.upload_data.push(
|
|
||||||
state.status.upspeed.substring(
|
|
||||||
0,
|
|
||||||
state.status.upspeed.indexOf(' ')
|
|
||||||
) / 1000
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
state.upload_data.push(
|
|
||||||
state.status.upspeed.substring(
|
|
||||||
0,
|
|
||||||
state.status.upspeed.indexOf(' ')
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
SET_SETTINGS: async state => {
|
SET_SETTINGS: async state => {
|
||||||
const {data} = await qbit.getAppPreferences()
|
const {data} = await qbit.getAppPreferences()
|
||||||
state.settings.savePath = data.save_path;
|
state.settings.savePath = data.save_path;
|
||||||
|
},
|
||||||
|
SET_SELECTED_TORRENT_DETAIL: (state, hash) => {
|
||||||
|
state.selectedDetailTorrent = hash
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div style="height: 89vh" color="background" @click.self="resetSelected">
|
<div color="background" @click.self="resetSelected">
|
||||||
<h1 style="font-size: 1.1em !important" class="subtitle-1 grey--text">Dashboard</h1>
|
<h1 style="font-size: 1.1em !important" class="subtitle-1 grey--text">Dashboard</h1>
|
||||||
<v-container color="background" class="my-4" @click.self="resetSelected">
|
<v-container color="background" class="my-4 pa-0" @click.self="resetSelected">
|
||||||
<!-- justify-center here in layout to center!! -->
|
<!-- justify-center here in layout to center!! -->
|
||||||
<v-flex xs12 sm6 md3 @click.self="resetSelected">
|
<v-flex xs12 sm6 md3 @click.self="resetSelected">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
|
|
Loading…
Add table
Reference in a new issue