This commit is contained in:
Daan Wijns 2020-10-19 12:05:11 +02:00 committed by GitHub
parent ae6a2048eb
commit dde4c08f2b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 454 additions and 377 deletions

View file

@ -1 +1 @@
node_modules
node_modules/*

View file

@ -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'],

View file

@ -1,6 +1,6 @@
{
"name": "vuetorrent",
"version": "0.4.0",
"version": "0.4.1",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",

View file

@ -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/* ./
}

View file

@ -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

View 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>

View 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>

View file

@ -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' },

View file

@ -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)

View file

@ -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>

View file

@ -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() {

View file

@ -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>

View file

@ -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 })
}
}
}

View 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 })
}
}
}
}

View file

@ -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 }

View file

@ -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'

View file

@ -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: [
{

View file

@ -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)

View file

@ -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) {