mirror of
https://github.com/VueTorrent/VueTorrent.git
synced 2025-03-14 12:10:18 +03:00
0.4.1 (#53)
This commit is contained in:
parent
ae6a2048eb
commit
dde4c08f2b
19 changed files with 454 additions and 377 deletions
|
@ -1 +1 @@
|
|||
node_modules
|
||||
node_modules/*
|
|
@ -12,7 +12,7 @@ module.exports = {
|
|||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
indent: ['error', 4],
|
||||
indent: ['warn', 4],
|
||||
semi: ['error', 'never'],
|
||||
quotes: ['error', 'single'],
|
||||
'comma-dangle': ['error', 'never'],
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "vuetorrent",
|
||||
"version": "0.4.0",
|
||||
"version": "0.4.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
|
|
|
@ -8,8 +8,8 @@ build_and_copy(){
|
|||
cd ../vuetorrent-release
|
||||
git checkout latest-release
|
||||
git pull
|
||||
sudo rm -r public
|
||||
cp -r ../vuetorrent/vuetorrent/* ./
|
||||
sudo rm -r public || true
|
||||
cp -r ../VueTorrent/vuetorrent/* ./
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
<v-row no-gutters>
|
||||
<v-col ref="fileZone">
|
||||
<v-file-input
|
||||
v-if="!url"
|
||||
v-model="files"
|
||||
color="deep-purple accent-4"
|
||||
counter
|
||||
|
@ -44,8 +43,7 @@
|
|||
>
|
||||
</template>
|
||||
</v-file-input>
|
||||
<v-text-field
|
||||
v-if="files.length == 0"
|
||||
<v-textarea
|
||||
label="URL"
|
||||
prepend-icon="mdi-link"
|
||||
:rows="
|
||||
|
@ -53,7 +51,10 @@
|
|||
"
|
||||
required
|
||||
:autofocus="!phoneLayout"
|
||||
v-model="url"
|
||||
v-model="urls"
|
||||
auto-grow
|
||||
clearable
|
||||
hint="One link per line"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
@ -130,17 +131,17 @@ export default {
|
|||
'Not a valid magnet link'
|
||||
],
|
||||
loading: false,
|
||||
url: null,
|
||||
urls: null,
|
||||
valid: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
submit() {
|
||||
if (this.files.length || this.url) {
|
||||
if (this.files.length || this.urls) {
|
||||
let torrents = []
|
||||
let params = { urls: null, autoTMM: this.autoTMM }
|
||||
if (this.files.length) torrents.push(...this.files)
|
||||
if (this.url) params.urls = this.url
|
||||
if (this.urls) params.urls = this.urls
|
||||
if (this.category) params.category = this.category
|
||||
if (!this.autoTMM) params.savepath = this.directory
|
||||
if (this.skip_checking) params.skip_checking = this.skip_checking
|
||||
|
|
71
src/components/Modals/ConfirmDeleteModal.vue
Normal file
71
src/components/Modals/ConfirmDeleteModal.vue
Normal file
|
@ -0,0 +1,71 @@
|
|||
<template>
|
||||
<v-dialog v-model="dialog" scrollable max-width="500px">
|
||||
<v-card>
|
||||
<v-container :class="`pa-0 project done`">
|
||||
<v-card-title class="justify-center">
|
||||
<h2>Confirm Removal</h2>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-list flat>
|
||||
<v-list-item
|
||||
v-for="t in torrents"
|
||||
:key="t.hash"
|
||||
>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title class="truncate" v-text="t.name"></v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
<v-card-actions class="justify-center pb-5">
|
||||
<v-btn text class="error white--text mt-3"
|
||||
@click="close()"
|
||||
>Cancel</v-btn
|
||||
>
|
||||
<v-btn
|
||||
text
|
||||
class="green_accent white--text mt-3"
|
||||
@click="deleteWithoutFiles()"
|
||||
>Delete</v-btn
|
||||
>
|
||||
<v-btn
|
||||
text
|
||||
class="green_accent white--text mt-3"
|
||||
@click="deleteWithFiles()"
|
||||
>Delete with files</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import { Modal } from '@/mixins'
|
||||
import qbit from '@/services/qbit'
|
||||
export default {
|
||||
name: 'ConfirmDeleteModal',
|
||||
mixins: [Modal],
|
||||
methods: {
|
||||
close() {
|
||||
this.$store.commit('DELETE_MODAL', this.guid)
|
||||
},
|
||||
deleteWithoutFiles() {
|
||||
qbit.deleteTorrents(this.selected_torrents, false)
|
||||
this.close()
|
||||
},
|
||||
deleteWithFiles() {
|
||||
qbit.deleteTorrents(this.selected_torrents, true)
|
||||
this.close()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['selected_torrents']),
|
||||
...mapGetters(['getTorrents']),
|
||||
torrents(){
|
||||
return this.getTorrents().filter(t => this.selected_torrents.includes(t.hash))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
80
src/components/Modals/RenameModal.vue
Normal file
80
src/components/Modals/RenameModal.vue
Normal file
|
@ -0,0 +1,80 @@
|
|||
<template>
|
||||
<v-dialog
|
||||
v-model="dialog"
|
||||
scrollable
|
||||
:width="dialogWidth"
|
||||
:fullscreen="phoneLayout"
|
||||
>
|
||||
<v-card style="overflow: hidden !important">
|
||||
<v-container :style="{ height: phoneLayout ? '100vh' : '' }">
|
||||
<v-card-title class="pb-0 justify-center">
|
||||
<h2>Rename Torrent</h2>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<div>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-text-field
|
||||
label="Torrent Name"
|
||||
prepend-icon="insert_drive_file"
|
||||
v-model="name"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</div>
|
||||
</v-card-text>
|
||||
<div>
|
||||
<v-card-actions class="justify-center">
|
||||
<v-btn color="success" @click="rename">Save</v-btn>
|
||||
</v-card-actions>
|
||||
</div>
|
||||
</v-container>
|
||||
<v-fab-transition v-if="phoneLayout">
|
||||
<v-btn @click="close" color="red" dark absolute bottom right>
|
||||
<v-icon>close</v-icon>
|
||||
</v-btn>
|
||||
</v-fab-transition>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import { Modal, FullScreenModal } from '@/mixins'
|
||||
import qbit from '@/services/qbit'
|
||||
export default {
|
||||
name: 'RenameModal',
|
||||
mixins: [Modal, FullScreenModal],
|
||||
props: {
|
||||
hash: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getTorrent']),
|
||||
dialogWidth() {
|
||||
return this.phoneLayout ? '100%' : '750px'
|
||||
},
|
||||
torrent() {
|
||||
return this.getTorrent(this.hash)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
rename() {
|
||||
qbit.setTorrentName(this.hash, this.name)
|
||||
this.close()
|
||||
},
|
||||
close() {
|
||||
this.$store.commit('DELETE_MODAL', this.guid)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.name = this.torrent.name
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -46,10 +46,10 @@ export default {
|
|||
mixins: [Modal],
|
||||
data() {
|
||||
return {
|
||||
sortProperty: { value: 'added_on', name: 'Default' },
|
||||
sortProperty: { value: 'added_on', name: 'Added On' },
|
||||
reverse: true,
|
||||
options: [
|
||||
{ value: 'added_on', name: 'Default' },
|
||||
{ value: 'added_on', name: 'Added On' },
|
||||
{ value: 'availability', name: 'Availability' },
|
||||
{ value: 'category', name: 'Category' },
|
||||
{ value: 'completed', name: 'Completed' },
|
||||
|
|
|
@ -92,8 +92,9 @@ export default {
|
|||
qbit.resumeTorrents(this.selected_torrents)
|
||||
},
|
||||
removeTorrents() {
|
||||
qbit.deleteTorrents(this.selected_torrents, false)
|
||||
this.$store.commit('RESET_SELECTED')
|
||||
if(!this.selected_torrents.length) return
|
||||
|
||||
return this.createModal('ConfirmDeleteModal')
|
||||
},
|
||||
addModal(name) {
|
||||
this.createModal(name)
|
||||
|
|
|
@ -1,143 +1,183 @@
|
|||
<template>
|
||||
<v-card
|
||||
ripple
|
||||
flat
|
||||
class="pointer torrent noselect"
|
||||
:class="{ torrent_selected: containsTorrent(torrent.hash) }"
|
||||
@click.native="selectTorrent(torrent.hash)"
|
||||
@dblclick.prevent="showInfo(torrent.hash)"
|
||||
>
|
||||
<v-layout row wrap :class="style">
|
||||
<v-flex xs12 class="mb-4">
|
||||
<div class="caption grey--text">Torrent title</div>
|
||||
<div class="truncate">{{ torrent.name }}</div>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm1 md1>
|
||||
<div class="caption grey--text">Size</div>
|
||||
<div>
|
||||
{{ torrent.size | getDataValue }}
|
||||
<span class="caption grey--text">{{
|
||||
torrent.size | getDataUnit
|
||||
}}</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs12 sm1 md1 class="mr-4">
|
||||
<div class="caption grey--text">Done</div>
|
||||
<v-progress-linear
|
||||
v-model="torrent.progress"
|
||||
height="20"
|
||||
:style="phoneLayout ? '' : 'width: 80%;'"
|
||||
:color="`torrent-${state}-color`" >
|
||||
<v-card
|
||||
ripple
|
||||
flat
|
||||
class="pointer torrent noselect"
|
||||
:class="{ torrent_selected: isAlreadySelected(torrent.hash) }"
|
||||
@click.native.exact.prevent="
|
||||
$vuetify.breakpoint.smAndDown ?
|
||||
selectTorrent(torrent.hash) :
|
||||
showInfo(torrent.hash)"
|
||||
@dblclick.prevent="showInfo(torrent.hash)"
|
||||
@click.ctrl.exact.prevent="selectTorrent(torrent.hash)"
|
||||
>
|
||||
<v-tooltip top>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-layout v-on="denseDashboard && on" row wrap :class="style">
|
||||
<v-flex xs12 :class="denseDashboard ? 'sm3' : ''">
|
||||
<div class="caption grey--text">Torrent title</div>
|
||||
<div class="truncate">{{ torrent.name }}</div>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Size</div>
|
||||
<div>
|
||||
{{ torrent.size | getDataValue }}
|
||||
<span class="caption grey--text">{{
|
||||
torrent.size | getDataUnit
|
||||
}}</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs12 sm1 md1 class="mr-4">
|
||||
<div class="caption grey--text">Done</div>
|
||||
<v-progress-linear
|
||||
v-model="torrent.progress"
|
||||
height="20"
|
||||
:style="phoneLayout ? '' : 'width: 80%;'"
|
||||
:color="`torrent-${state}-color`" >
|
||||
<span
|
||||
class="caption"
|
||||
>
|
||||
{{ torrent.progress }}%
|
||||
</span>
|
||||
</v-progress-linear>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm1 md1>
|
||||
<div class="caption grey--text">Ratio</div>
|
||||
<div>
|
||||
{{ torrent.ratio }}
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm1 md1>
|
||||
<div class="caption grey--text">ETA</div>
|
||||
<div>
|
||||
{{ torrent.eta }}
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm1 md1>
|
||||
<div class="caption grey--text">Download</div>
|
||||
<div>
|
||||
{{ torrent.dlspeed | getDataValue }}
|
||||
<span class="caption grey--text">{{
|
||||
torrent.dlspeed | getDataUnit
|
||||
}}</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm1 md1>
|
||||
<div class="caption grey--text">Upload</div>
|
||||
<div>
|
||||
{{ torrent.upspeed | getDataValue }}
|
||||
<span class="caption grey--text">{{
|
||||
torrent.upspeed | getDataUnit
|
||||
}}</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm1 md1>
|
||||
<div class="caption grey--text">Peers</div>
|
||||
<div>
|
||||
{{ torrent.num_leechs }}
|
||||
<span class="grey--text caption"
|
||||
>/{{ torrent.available_peers }}</span
|
||||
>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm1 md1>
|
||||
<div class="caption grey--text">Seeds</div>
|
||||
<div>
|
||||
{{ torrent.num_seeds }}
|
||||
<span class="grey--text caption"
|
||||
>/{{ torrent.available_seeds }}</span
|
||||
>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm1 md1 :class="phoneLayout ? '' : 'mr-4'">
|
||||
</v-progress-linear>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm1 md1 class="mr-2" v-if="torrent.progress !== 100" >
|
||||
<div class="caption grey--text">Download</div>
|
||||
<div>
|
||||
{{ torrent.dlspeed | getDataValue }}
|
||||
<span class="caption grey--text">{{
|
||||
torrent.dlspeed | getDataUnit
|
||||
}}</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm1 md1 class="mr-2" v-else >
|
||||
<div class="caption grey--text">Ratio</div>
|
||||
<div>{{ torrent.ratio }}</div>
|
||||
</v-flex>
|
||||
<v-flex xs5 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Upload</div>
|
||||
<div>
|
||||
{{ torrent.upspeed | getDataValue }}
|
||||
<span class="caption grey--text">{{
|
||||
torrent.upspeed | getDataUnit
|
||||
}}</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">ETA</div>
|
||||
<div>
|
||||
{{ torrent.eta }}
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs5 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Peers</div>
|
||||
<div>
|
||||
{{ torrent.num_leechs }}
|
||||
<span class="grey--text caption"
|
||||
>/{{ torrent.available_peers }}</span
|
||||
>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Seeds</div>
|
||||
<div>
|
||||
{{ torrent.num_seeds }}
|
||||
<span class="grey--text caption"
|
||||
>/{{ torrent.available_seeds }}</span
|
||||
>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs5 sm1>
|
||||
<div class="caption grey--text">Status</div>
|
||||
<v-chip
|
||||
small
|
||||
class="caption"
|
||||
:class="
|
||||
<v-chip
|
||||
small
|
||||
class="caption"
|
||||
:class="
|
||||
theme === 'light'
|
||||
? `${state} white--text `
|
||||
: `${state} black--text`">
|
||||
{{ torrent.state }}
|
||||
</v-chip>
|
||||
{{ torrent.state }}
|
||||
</v-chip>
|
||||
</v-flex>
|
||||
<!-- Category -->
|
||||
<v-flex v-if="torrent.category" xs4 sm1 md1>
|
||||
<v-flex v-if="torrent.category" class="mr-2" xs6 sm1 md1>
|
||||
<div class="caption grey--text">Category</div>
|
||||
<v-chip small class="upload white--text caption">
|
||||
{{ torrent.category }}
|
||||
</v-chip>
|
||||
</v-flex>
|
||||
<!-- Tags -->
|
||||
<v-flex xs12 sm1>
|
||||
<div class="caption grey--text">Tags</div>
|
||||
<v-row wrap class="ma-0">
|
||||
<v-chip v-for="tag in torrent.tags" :key="tag"
|
||||
small
|
||||
:class="
|
||||
<!-- Tags -->
|
||||
<v-flex xs5 sm2 v-if="torrent.tags && torrent.tags.length">
|
||||
<div class="caption grey--text">Tags</div>
|
||||
<v-row wrap class="ma-0">
|
||||
<v-chip v-for="tag in torrent.tags" :key="tag"
|
||||
small
|
||||
:class="
|
||||
theme === 'light'
|
||||
? 'white--text'
|
||||
: 'black--text'
|
||||
"
|
||||
class="download caption mb-1 mx-1"
|
||||
>
|
||||
{{ tag }}
|
||||
</v-chip>
|
||||
</v-row>
|
||||
</v-flex>
|
||||
class="download caption mb-1 mx-1"
|
||||
>
|
||||
{{ tag }}
|
||||
</v-chip>
|
||||
</v-row>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<v-divider v-if="index !== length"></v-divider>
|
||||
</v-card>
|
||||
</template>
|
||||
<span>{{ torrent.name }}</span>
|
||||
</v-tooltip>
|
||||
<v-divider v-if="index !== length"></v-divider>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { General, Torrent } from '@/mixins'
|
||||
import { General, TorrentSelect } from '@/mixins'
|
||||
import {mapGetters} from 'vuex'
|
||||
|
||||
|
||||
export default {
|
||||
name: 'Torrent',
|
||||
mixins: [General, Torrent]
|
||||
mixins: [General, TorrentSelect],
|
||||
props: {
|
||||
torrent: Object,
|
||||
index: Number,
|
||||
length: Number
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getTheme', 'getWebuiSettings']),
|
||||
theme() {
|
||||
return this.getTheme() ? 'dark' : 'light'
|
||||
},
|
||||
state() {
|
||||
return this.torrent.state.toLowerCase()
|
||||
},
|
||||
style() {
|
||||
let base = `pa-4 ml-0 sideborder ${this.state} `
|
||||
if (this.index === this.length) base += ' bottomBorderRadius'
|
||||
if (this.index === 0) base += ' topBorderRadius'
|
||||
return base
|
||||
},
|
||||
phoneLayout() {
|
||||
return this.$vuetify.breakpoint.xsOnly
|
||||
},
|
||||
denseDashboard(){
|
||||
return this.getWebuiSettings().denseDashboard
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showInfo(hash) {
|
||||
this.createModal('TorrentDetailModal', {hash})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.topBorderRadius {
|
||||
border-top-left-radius: 3px;
|
||||
border-top-left-radius: 3px;
|
||||
}
|
||||
.bottomBorderRadius {
|
||||
border-bottom-left-radius: 3px;
|
||||
border-bottom-left-radius: 3px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,59 +1,77 @@
|
|||
<template>
|
||||
<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-icon>play_arrow</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 15px"
|
||||
>Resume</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
<v-list-item @click="pause" link>
|
||||
<v-icon>pause</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 15px"
|
||||
>Pause</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
<v-divider />
|
||||
<v-list-item @click="directory" link>
|
||||
<v-icon>folder</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 15px"
|
||||
>Change location</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
<v-list-item @click="reannounce" link>
|
||||
<v-icon>record_voice_over</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 15px"
|
||||
>Reannounce</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
<v-divider />
|
||||
<v-list-item @click="deleteWithoutFiles" link>
|
||||
<v-icon color="red">delete</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 15px; color: red"
|
||||
>Delete</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
<v-list-item @click="deleteWithFiles" link>
|
||||
<v-icon color="red">delete</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 15px; color: red"
|
||||
>Delete with files</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
<v-list dense rounded>
|
||||
<v-list-item @click="resume" link>
|
||||
<v-icon >play_arrow</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 12px"
|
||||
>Resume</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
<v-list-item @click="pause" link>
|
||||
<v-icon>pause</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 12px"
|
||||
>Pause</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
<v-divider />
|
||||
<v-list-item @click="deleteWithoutFiles" link>
|
||||
<v-icon color="red">delete</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 12px; color: red"
|
||||
>Delete</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
<v-list-item @click="deleteWithFiles" link>
|
||||
<v-icon color="red">delete</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 12px; color: red"
|
||||
>Delete with files</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
<v-divider />
|
||||
<v-list-item @click="location" link>
|
||||
<v-icon>folder</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 12px"
|
||||
>Change location</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
<v-list-item @click="rename" link>
|
||||
<v-icon>edit</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 12px"
|
||||
>Rename</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
<v-list-item @click="recheck" link>
|
||||
<v-icon>widgets</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 12px;"
|
||||
>Force recheck</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
<v-list-item @click="reannounce" link>
|
||||
<v-icon>record_voice_over</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 12px"
|
||||
>Force reannounce</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
<v-divider />
|
||||
<v-list-item @click="showInfo" link>
|
||||
<v-icon>info</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 12px"
|
||||
>Show Info</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
<v-list-item @click="selectTorrent(hash)" link>
|
||||
<v-icon>done</v-icon>
|
||||
<v-list-item-title class="ml-2" style="font-size: 12px"
|
||||
>Select</v-list-item-title
|
||||
>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import qbit from '@/services/qbit'
|
||||
import { General } from '@/mixins'
|
||||
import { General, TorrentSelect } from '@/mixins'
|
||||
export default {
|
||||
name: 'TorrentRightClickMenu',
|
||||
mixins: [General],
|
||||
mixins: [General, TorrentSelect],
|
||||
props: {
|
||||
hash: String
|
||||
},
|
||||
|
@ -64,9 +82,12 @@ export default {
|
|||
pause() {
|
||||
qbit.pauseTorrents([this.hash])
|
||||
},
|
||||
directory() {
|
||||
location() {
|
||||
this.createModal('ChangeLocationModal', { hash: this.hash })
|
||||
},
|
||||
rename() {
|
||||
this.createModal('RenameModal', { hash: this.hash })
|
||||
},
|
||||
reannounce() {
|
||||
qbit.reannounceTorrents([this.hash])
|
||||
},
|
||||
|
@ -76,9 +97,13 @@ export default {
|
|||
deleteWithFiles() {
|
||||
qbit.deleteTorrents([this.hash], true)
|
||||
},
|
||||
recheck() {
|
||||
qbit.recheckTorrents([this.hash])
|
||||
},
|
||||
showInfo() {
|
||||
this.createModal('TorrentDetailModal', { hash: this.hash })
|
||||
}
|
||||
|
||||
},
|
||||
computed: {
|
||||
dark() {
|
||||
|
|
|
@ -1,135 +0,0 @@
|
|||
<template>
|
||||
<v-card
|
||||
ripple
|
||||
flat
|
||||
class="pointer torrent noselect"
|
||||
:class="{ torrent_selected: containsTorrent(torrent.hash) }"
|
||||
@click.native="selectTorrent(torrent.hash)"
|
||||
@dblclick.prevent="showInfo(torrent.hash)"
|
||||
>
|
||||
<v-tooltip top>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-layout v-on="on" row wrap :class="style">
|
||||
<v-flex xs12 sm2 md3>
|
||||
<div class="caption grey--text">Torrent title</div>
|
||||
<div class="truncate">{{ torrent.name }}</div>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Size</div>
|
||||
<div>
|
||||
{{ torrent.size | getDataValue }}
|
||||
<span class="caption grey--text">{{
|
||||
torrent.size | getDataUnit
|
||||
}}</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs5 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Done</div>
|
||||
<div>
|
||||
{{ torrent.progress }}<span class="grey--text">% </span>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Download</div>
|
||||
<div>
|
||||
{{ torrent.dlspeed | getDataValue }}
|
||||
<span class="caption grey--text">{{
|
||||
torrent.dlspeed | getDataUnit
|
||||
}}</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs5 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Upload</div>
|
||||
<div>
|
||||
{{ torrent.upspeed | getDataValue }}
|
||||
<span class="caption grey--text">{{
|
||||
torrent.upspeed | getDataUnit
|
||||
}}</span>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">ETA</div>
|
||||
<div>
|
||||
{{ torrent.eta }}
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs5 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Peers</div>
|
||||
<div>
|
||||
{{ torrent.num_leechs }}
|
||||
<span class="grey--text caption"
|
||||
>/{{ torrent.available_peers }}</span
|
||||
>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs6 sm1 md1 class="mr-2">
|
||||
<div class="caption grey--text">Seeds</div>
|
||||
<div>
|
||||
{{ torrent.num_seeds }}
|
||||
<span class="grey--text caption"
|
||||
>/{{ torrent.available_seeds }}</span
|
||||
>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs5 sm1>
|
||||
<div class="caption grey--text">Status</div>
|
||||
<v-chip
|
||||
small
|
||||
class="caption"
|
||||
:class="
|
||||
theme === 'light'
|
||||
? `${state} white--text `
|
||||
: `${state} black--text`">
|
||||
{{ torrent.state }}
|
||||
</v-chip>
|
||||
</v-flex>
|
||||
<!-- Category -->
|
||||
<v-flex v-if="torrent.category" class="mr-2" xs6 sm1 md1>
|
||||
<div class="caption grey--text">Category</div>
|
||||
<v-chip small class="upload white--text caption">
|
||||
{{ torrent.category }}
|
||||
</v-chip>
|
||||
</v-flex>
|
||||
<!-- Tags -->
|
||||
<v-flex xs5 sm4>
|
||||
<div class="caption grey--text">Tags</div>
|
||||
<v-row wrap class="ma-0">
|
||||
<v-chip v-for="tag in torrent.tags" :key="tag"
|
||||
small
|
||||
:class="
|
||||
theme === 'light'
|
||||
? 'white--text'
|
||||
: 'black--text'
|
||||
"
|
||||
class="download caption mb-1 mx-1"
|
||||
>
|
||||
{{ tag }}
|
||||
</v-chip>
|
||||
</v-row>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</template>
|
||||
<span>{{ torrent.name }}</span>
|
||||
</v-tooltip>
|
||||
<v-divider v-if="index !== length"></v-divider>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { General, Torrent } from '@/mixins'
|
||||
|
||||
|
||||
export default {
|
||||
name: 'TorrentDense',
|
||||
mixins: [General, Torrent]
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.topBorderRadius {
|
||||
border-top-left-radius: 3px;
|
||||
}
|
||||
.bottomBorderRadius {
|
||||
border-bottom-left-radius: 3px;
|
||||
}
|
||||
</style>
|
|
@ -1,42 +0,0 @@
|
|||
import {mapGetters} from 'vuex'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
torrent: Object,
|
||||
index: Number,
|
||||
length: Number
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getTheme']),
|
||||
theme() {
|
||||
return this.getTheme() ? 'dark' : 'light'
|
||||
},
|
||||
state() {
|
||||
return this.torrent.state.toLowerCase()
|
||||
},
|
||||
style() {
|
||||
let base = `pa-4 ml-0 sideborder ${this.state} `
|
||||
if (this.index === this.length) base += ' bottomBorderRadius'
|
||||
if (this.index === 0) base += ' topBorderRadius'
|
||||
return base
|
||||
},
|
||||
phoneLayout() {
|
||||
return this.$vuetify.breakpoint.xsOnly
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectTorrent(hash) {
|
||||
if (this.containsTorrent(hash)) {
|
||||
this.$store.commit('SET_SELECTED', { type: 'remove', hash })
|
||||
} else {
|
||||
this.$store.commit('SET_SELECTED', { type: 'add', hash })
|
||||
}
|
||||
},
|
||||
containsTorrent(hash) {
|
||||
return this.$store.getters.containsTorrent(hash)
|
||||
},
|
||||
showInfo(hash) {
|
||||
this.createModal('TorrentDetailModal', { hash })
|
||||
}
|
||||
}
|
||||
}
|
14
src/mixins/TorrentSelect.js
Normal file
14
src/mixins/TorrentSelect.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
export default {
|
||||
methods: {
|
||||
isAlreadySelected(hash) {
|
||||
return this.$store.getters.containsTorrent(hash)
|
||||
},
|
||||
selectTorrent(hash) {
|
||||
if (this.isAlreadySelected(hash)) {
|
||||
this.$store.commit('SET_SELECTED', { type: 'remove', hash })
|
||||
} else {
|
||||
this.$store.commit('SET_SELECTED', { type: 'add', hash })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,6 @@ import Modal from './Modal'
|
|||
import SettingsTab from './SettingsTab'
|
||||
import Tab from './Tab'
|
||||
import General from './General'
|
||||
import Torrent from './Torrent'
|
||||
import TorrentSelect from './TorrentSelect'
|
||||
|
||||
export { FullScreenModal, Modal, SettingsTab, Tab, General, Torrent }
|
||||
export { FullScreenModal, Modal, SettingsTab, Tab, General, TorrentSelect }
|
||||
|
|
|
@ -11,7 +11,7 @@ export default class Torrent {
|
|||
this.num_leechs = data.num_leechs
|
||||
this.num_seeds = data.num_seeds
|
||||
this.path = data.path === undefined ? '/downloads' : data.path
|
||||
this.state = this.formatState(data)
|
||||
this.state = this.formatState(data.state)
|
||||
// hash is used to identify
|
||||
this.hash = data.hash
|
||||
// available seeds
|
||||
|
@ -22,11 +22,11 @@ export default class Torrent {
|
|||
this.ratio = Math.round(data.ratio * 100) / 100
|
||||
this.tags = data.tags.length > 0 ? data.tags.split(',') : null
|
||||
this.category = data.category
|
||||
this.tracker = data.tracker
|
||||
}
|
||||
|
||||
formatState(item) {
|
||||
if (!item.tracker) return 'Fail'
|
||||
switch (item.state) {
|
||||
formatState(state) {
|
||||
switch (state) {
|
||||
case 'forceDL':
|
||||
case 'downloading':
|
||||
return 'Downloading'
|
||||
|
|
|
@ -7,7 +7,6 @@ import { isAuthenticated } from '@/services/auth.js'
|
|||
Vue.use(Router)
|
||||
|
||||
const router = new Router({
|
||||
mode: 'history',
|
||||
base: process.env.BASE_URL,
|
||||
routes: [
|
||||
{
|
||||
|
|
|
@ -132,6 +132,7 @@ class Qbit {
|
|||
}
|
||||
|
||||
deleteTorrents(hashes, deleteFiles) {
|
||||
if(!hashes.length) return
|
||||
return this.actionTorrents('delete', hashes, { deleteFiles })
|
||||
}
|
||||
|
||||
|
@ -184,6 +185,14 @@ class Qbit {
|
|||
return this.actionTorrents('setLocation', hashes, { location })
|
||||
}
|
||||
|
||||
setTorrentName(hash, name) {
|
||||
const params = {
|
||||
hash,
|
||||
name
|
||||
}
|
||||
return this.axios.get('/torrents/rename', { params })
|
||||
}
|
||||
|
||||
getTorrentProperties(hash) {
|
||||
const params = {
|
||||
hash
|
||||
|
@ -309,9 +318,8 @@ class Qbit {
|
|||
// End Categories
|
||||
|
||||
actionTorrents(action, hashes, extra) {
|
||||
if (action == 'delete' && !hashes.length) return
|
||||
const params = {
|
||||
hashes: hashes.length ? hashes.join('|') : 'all',
|
||||
hashes: hashes.join('|'),
|
||||
...extra
|
||||
}
|
||||
const data = new URLSearchParams(params)
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
<template>
|
||||
<div class="pl-5 pr-5" color="background" @click.self="resetSelected">
|
||||
<div class="pl-5 pr-5"
|
||||
color="background"
|
||||
@click.self="resetSelected"
|
||||
>
|
||||
<h1 style="font-size: 1.1em !important" class="subtitle-1 grey--text">
|
||||
Dashboard
|
||||
<p
|
||||
|
@ -36,7 +39,7 @@
|
|||
v-for="(torrent, index) in paginatedData"
|
||||
:key="torrent.hash"
|
||||
>
|
||||
<Torrent v-if="!denseDashboard"
|
||||
<Torrent
|
||||
:class="{
|
||||
topBorderRadius: index === 0,
|
||||
noBorderRadius:
|
||||
|
@ -47,17 +50,6 @@
|
|||
:index="index"
|
||||
:length="torrents.length - 1"
|
||||
/>
|
||||
<TorrentDense v-if="denseDashboard"
|
||||
:class="{
|
||||
topBorderRadius: index === 0,
|
||||
noBorderRadius:
|
||||
index !== 0 && index !== torrent.length - 1,
|
||||
bottomBorderRadius: index === torrents.length - 1
|
||||
}"
|
||||
:torrent="torrent"
|
||||
:index="index"
|
||||
:length="torrents.length - 1"
|
||||
/>
|
||||
</div>
|
||||
<v-row v-if="pageCount > 1" xs12 justify="center">
|
||||
<v-col>
|
||||
|
@ -82,15 +74,16 @@
|
|||
<script>
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import Torrent from '@/components/Torrent'
|
||||
import TorrentDense from '@/components/TorrentDense'
|
||||
import Fuse from 'fuse.js'
|
||||
import { VueContext } from 'vue-context'
|
||||
import 'vue-context/src/sass/vue-context.scss'
|
||||
import TorrentRightClickMenu from '@/components/Torrent/TorrentRightClickMenu.vue'
|
||||
import { TorrentSelect, General } from '@/mixins'
|
||||
|
||||
export default {
|
||||
name: 'Dashboard',
|
||||
components: { Torrent, TorrentDense, VueContext, TorrentRightClickMenu },
|
||||
components: { Torrent, VueContext, TorrentRightClickMenu },
|
||||
mixins: [ TorrentSelect, General ],
|
||||
data() {
|
||||
return {
|
||||
input: '',
|
||||
|
@ -98,7 +91,7 @@ export default {
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['mainData']),
|
||||
...mapState(['mainData', 'selected_torrents']),
|
||||
...mapGetters(['getTorrents', 'getTorrentCountString', 'getWebuiSettings']),
|
||||
torrents() {
|
||||
if (!this.input || !this.input.length) return this.getTorrents()
|
||||
|
@ -133,9 +126,6 @@ export default {
|
|||
},
|
||||
torrentCountString() {
|
||||
return this.getTorrentCountString()
|
||||
},
|
||||
denseDashboard(){
|
||||
return this.getWebuiSettings().denseDashboard
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -147,14 +137,39 @@ export default {
|
|||
},
|
||||
toTop () {
|
||||
this.$vuetify.goTo(0)
|
||||
},
|
||||
handleKeyboardShortcut(e) {
|
||||
// 'ctrl + A' => select torrents
|
||||
if (e.keyCode === 65 && e.ctrlKey) {
|
||||
e.preventDefault()
|
||||
if(this.$store.state.selected_torrents.length === this.torrents.length){
|
||||
return this.$store.state.selected_torrents = []
|
||||
}
|
||||
const hashes = this.torrents.map(t => t.hash)
|
||||
return this.$store.state.selected_torrents = hashes
|
||||
}
|
||||
|
||||
// 'Delete' => Delete modal
|
||||
if(e.keyCode === 46) {
|
||||
e.preventDefault()
|
||||
|
||||
//no torrents select to delete
|
||||
if(!this.selected_torrents.length) return
|
||||
|
||||
return this.createModal('ConfirmDeleteModal')
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('keydown', this.handleKeyboardShortcut)
|
||||
},
|
||||
created() {
|
||||
this.$store.dispatch('INIT_INTERVALS')
|
||||
this.$store.commit('FETCH_CATEGORIES')
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$store.commit('REMOVE_INTERVALS')
|
||||
document.removeEventListener('keydown', this.handleKeyboardShortcut)
|
||||
},
|
||||
watch: {
|
||||
torrents: function (torrents) {
|
||||
|
|
Loading…
Add table
Reference in a new issue