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: { rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': 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'], semi: ['error', 'never'],
quotes: ['error', 'single'], quotes: ['error', 'single'],
'comma-dangle': ['error', 'never'], 'comma-dangle': ['error', 'never'],

View file

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

View file

@ -8,8 +8,8 @@ build_and_copy(){
cd ../vuetorrent-release cd ../vuetorrent-release
git checkout latest-release git checkout latest-release
git pull git pull
sudo rm -r public sudo rm -r public || true
cp -r ../vuetorrent/vuetorrent/* ./ cp -r ../VueTorrent/vuetorrent/* ./
} }

View file

@ -11,7 +11,6 @@
<v-row no-gutters> <v-row no-gutters>
<v-col ref="fileZone"> <v-col ref="fileZone">
<v-file-input <v-file-input
v-if="!url"
v-model="files" v-model="files"
color="deep-purple accent-4" color="deep-purple accent-4"
counter counter
@ -44,8 +43,7 @@
> >
</template> </template>
</v-file-input> </v-file-input>
<v-text-field <v-textarea
v-if="files.length == 0"
label="URL" label="URL"
prepend-icon="mdi-link" prepend-icon="mdi-link"
:rows=" :rows="
@ -53,7 +51,10 @@
" "
required required
:autofocus="!phoneLayout" :autofocus="!phoneLayout"
v-model="url" v-model="urls"
auto-grow
clearable
hint="One link per line"
/> />
</v-col> </v-col>
</v-row> </v-row>
@ -130,17 +131,17 @@ export default {
'Not a valid magnet link' 'Not a valid magnet link'
], ],
loading: false, loading: false,
url: null, urls: null,
valid: false valid: false
} }
}, },
methods: { methods: {
submit() { submit() {
if (this.files.length || this.url) { if (this.files.length || this.urls) {
let torrents = [] let torrents = []
let params = { urls: null, autoTMM: this.autoTMM } let params = { urls: null, autoTMM: this.autoTMM }
if (this.files.length) torrents.push(...this.files) 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.category) params.category = this.category
if (!this.autoTMM) params.savepath = this.directory if (!this.autoTMM) params.savepath = this.directory
if (this.skip_checking) params.skip_checking = this.skip_checking 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], mixins: [Modal],
data() { data() {
return { return {
sortProperty: { value: 'added_on', name: 'Default' }, sortProperty: { value: 'added_on', name: 'Added On' },
reverse: true, reverse: true,
options: [ options: [
{ value: 'added_on', name: 'Default' }, { value: 'added_on', name: 'Added On' },
{ value: 'availability', name: 'Availability' }, { value: 'availability', name: 'Availability' },
{ value: 'category', name: 'Category' }, { value: 'category', name: 'Category' },
{ value: 'completed', name: 'Completed' }, { value: 'completed', name: 'Completed' },

View file

@ -92,8 +92,9 @@ export default {
qbit.resumeTorrents(this.selected_torrents) qbit.resumeTorrents(this.selected_torrents)
}, },
removeTorrents() { removeTorrents() {
qbit.deleteTorrents(this.selected_torrents, false) if(!this.selected_torrents.length) return
this.$store.commit('RESET_SELECTED')
return this.createModal('ConfirmDeleteModal')
}, },
addModal(name) { addModal(name) {
this.createModal(name) this.createModal(name)

View file

@ -1,143 +1,183 @@
<template> <template>
<v-card <v-card
ripple ripple
flat flat
class="pointer torrent noselect" class="pointer torrent noselect"
:class="{ torrent_selected: containsTorrent(torrent.hash) }" :class="{ torrent_selected: isAlreadySelected(torrent.hash) }"
@click.native="selectTorrent(torrent.hash)" @click.native.exact.prevent="
@dblclick.prevent="showInfo(torrent.hash)" $vuetify.breakpoint.smAndDown ?
> selectTorrent(torrent.hash) :
<v-layout row wrap :class="style"> showInfo(torrent.hash)"
<v-flex xs12 class="mb-4"> @dblclick.prevent="showInfo(torrent.hash)"
<div class="caption grey--text">Torrent title</div> @click.ctrl.exact.prevent="selectTorrent(torrent.hash)"
<div class="truncate">{{ torrent.name }}</div> >
</v-flex> <v-tooltip top>
<v-flex xs6 sm1 md1> <template v-slot:activator="{ on }">
<div class="caption grey--text">Size</div> <v-layout v-on="denseDashboard && on" row wrap :class="style">
<div> <v-flex xs12 :class="denseDashboard ? 'sm3' : ''">
{{ torrent.size | getDataValue }} <div class="caption grey--text">Torrent title</div>
<span class="caption grey--text">{{ <div class="truncate">{{ torrent.name }}</div>
torrent.size | getDataUnit </v-flex>
}}</span> <v-flex xs6 sm1 md1 class="mr-2">
</div> <div class="caption grey--text">Size</div>
</v-flex> <div>
<v-flex xs12 sm1 md1 class="mr-4"> {{ torrent.size | getDataValue }}
<div class="caption grey--text">Done</div> <span class="caption grey--text">{{
<v-progress-linear torrent.size | getDataUnit
v-model="torrent.progress" }}</span>
height="20" </div>
:style="phoneLayout ? '' : 'width: 80%;'" </v-flex>
:color="`torrent-${state}-color`" > <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 <span
class="caption" class="caption"
> >
{{ torrent.progress }}% {{ torrent.progress }}%
</span> </span>
</v-progress-linear> </v-progress-linear>
</v-flex> </v-flex>
<v-flex xs6 sm1 md1> <v-flex xs6 sm1 md1 class="mr-2" v-if="torrent.progress !== 100" >
<div class="caption grey--text">Ratio</div> <div class="caption grey--text">Download</div>
<div> <div>
{{ torrent.ratio }} {{ torrent.dlspeed | getDataValue }}
</div> <span class="caption grey--text">{{
</v-flex> torrent.dlspeed | getDataUnit
<v-flex xs6 sm1 md1> }}</span>
<div class="caption grey--text">ETA</div> </div>
<div> </v-flex>
{{ torrent.eta }} <v-flex xs6 sm1 md1 class="mr-2" v-else >
</div> <div class="caption grey--text">Ratio</div>
</v-flex> <div>{{ torrent.ratio }}</div>
<v-flex xs6 sm1 md1> </v-flex>
<div class="caption grey--text">Download</div> <v-flex xs5 sm1 md1 class="mr-2">
<div> <div class="caption grey--text">Upload</div>
{{ torrent.dlspeed | getDataValue }} <div>
<span class="caption grey--text">{{ {{ torrent.upspeed | getDataValue }}
torrent.dlspeed | getDataUnit <span class="caption grey--text">{{
}}</span> torrent.upspeed | getDataUnit
</div> }}</span>
</v-flex> </div>
<v-flex xs6 sm1 md1> </v-flex>
<div class="caption grey--text">Upload</div> <v-flex xs6 sm1 md1 class="mr-2">
<div> <div class="caption grey--text">ETA</div>
{{ torrent.upspeed | getDataValue }} <div>
<span class="caption grey--text">{{ {{ torrent.eta }}
torrent.upspeed | getDataUnit </div>
}}</span> </v-flex>
</div> <v-flex xs5 sm1 md1 class="mr-2">
</v-flex> <div class="caption grey--text">Peers</div>
<v-flex xs6 sm1 md1> <div>
<div class="caption grey--text">Peers</div> {{ torrent.num_leechs }}
<div> <span class="grey--text caption"
{{ torrent.num_leechs }} >/{{ torrent.available_peers }}</span
<span class="grey--text caption" >
>/{{ torrent.available_peers }}</span </div>
> </v-flex>
</div> <v-flex xs6 sm1 md1 class="mr-2">
</v-flex> <div class="caption grey--text">Seeds</div>
<v-flex xs6 sm1 md1> <div>
<div class="caption grey--text">Seeds</div> {{ torrent.num_seeds }}
<div> <span class="grey--text caption"
{{ torrent.num_seeds }} >/{{ torrent.available_seeds }}</span
<span class="grey--text caption" >
>/{{ torrent.available_seeds }}</span </div>
> </v-flex>
</div> <v-flex xs5 sm1>
</v-flex>
<v-flex xs6 sm1 md1 :class="phoneLayout ? '' : 'mr-4'">
<div class="caption grey--text">Status</div> <div class="caption grey--text">Status</div>
<v-chip <v-chip
small small
class="caption" class="caption"
:class=" :class="
theme === 'light' theme === 'light'
? `${state} white--text ` ? `${state} white--text `
: `${state} black--text`"> : `${state} black--text`">
{{ torrent.state }} {{ torrent.state }}
</v-chip> </v-chip>
</v-flex> </v-flex>
<!-- Category --> <!-- 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> <div class="caption grey--text">Category</div>
<v-chip small class="upload white--text caption"> <v-chip small class="upload white--text caption">
{{ torrent.category }} {{ torrent.category }}
</v-chip> </v-chip>
</v-flex> </v-flex>
<!-- Tags --> <!-- Tags -->
<v-flex xs12 sm1> <v-flex xs5 sm2 v-if="torrent.tags && torrent.tags.length">
<div class="caption grey--text">Tags</div> <div class="caption grey--text">Tags</div>
<v-row wrap class="ma-0"> <v-row wrap class="ma-0">
<v-chip v-for="tag in torrent.tags" :key="tag" <v-chip v-for="tag in torrent.tags" :key="tag"
small small
:class=" :class="
theme === 'light' theme === 'light'
? 'white--text' ? 'white--text'
: 'black--text' : 'black--text'
" "
class="download caption mb-1 mx-1" class="download caption mb-1 mx-1"
> >
{{ tag }} {{ tag }}
</v-chip> </v-chip>
</v-row> </v-row>
</v-flex> </v-flex>
</v-layout> </v-layout>
<v-divider v-if="index !== length"></v-divider> </template>
</v-card> <span>{{ torrent.name }}</span>
</v-tooltip>
<v-divider v-if="index !== length"></v-divider>
</v-card>
</template> </template>
<script> <script>
import { General, Torrent } from '@/mixins' import { General, TorrentSelect } from '@/mixins'
import {mapGetters} from 'vuex'
export default { export default {
name: 'Torrent', 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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.topBorderRadius { .topBorderRadius {
border-top-left-radius: 3px; border-top-left-radius: 3px;
} }
.bottomBorderRadius { .bottomBorderRadius {
border-bottom-left-radius: 3px; border-bottom-left-radius: 3px;
} }
</style> </style>

View file

@ -1,59 +1,77 @@
<template> <template>
<v-list dense rounded> <v-list dense rounded>
<v-list-item @click="showInfo" link> <v-list-item @click="resume" link>
<v-icon>info</v-icon> <v-icon >play_arrow</v-icon>
<v-list-item-title class="ml-2" style="font-size: 15px" <v-list-item-title class="ml-2" style="font-size: 12px"
>Show Info</v-list-item-title >Resume</v-list-item-title
> >
</v-list-item> </v-list-item>
<v-divider /> <v-list-item @click="pause" link>
<v-list-item @click="resume" link> <v-icon>pause</v-icon>
<v-icon>play_arrow</v-icon> <v-list-item-title class="ml-2" style="font-size: 12px"
<v-list-item-title class="ml-2" style="font-size: 15px" >Pause</v-list-item-title
>Resume</v-list-item-title >
> </v-list-item>
</v-list-item> <v-divider />
<v-list-item @click="pause" link> <v-list-item @click="deleteWithoutFiles" link>
<v-icon>pause</v-icon> <v-icon color="red">delete</v-icon>
<v-list-item-title class="ml-2" style="font-size: 15px" <v-list-item-title class="ml-2" style="font-size: 12px; color: red"
>Pause</v-list-item-title >Delete</v-list-item-title
> >
</v-list-item> </v-list-item>
<v-divider /> <v-list-item @click="deleteWithFiles" link>
<v-list-item @click="directory" link> <v-icon color="red">delete</v-icon>
<v-icon>folder</v-icon> <v-list-item-title class="ml-2" style="font-size: 12px; color: red"
<v-list-item-title class="ml-2" style="font-size: 15px" >Delete with files</v-list-item-title
>Change location</v-list-item-title >
> </v-list-item>
</v-list-item> <v-divider />
<v-list-item @click="reannounce" link> <v-list-item @click="location" link>
<v-icon>record_voice_over</v-icon> <v-icon>folder</v-icon>
<v-list-item-title class="ml-2" style="font-size: 15px" <v-list-item-title class="ml-2" style="font-size: 12px"
>Reannounce</v-list-item-title >Change location</v-list-item-title
> >
</v-list-item> </v-list-item>
<v-divider /> <v-list-item @click="rename" link>
<v-list-item @click="deleteWithoutFiles" link> <v-icon>edit</v-icon>
<v-icon color="red">delete</v-icon> <v-list-item-title class="ml-2" style="font-size: 12px"
<v-list-item-title class="ml-2" style="font-size: 15px; color: red" >Rename</v-list-item-title
>Delete</v-list-item-title >
> </v-list-item>
</v-list-item> <v-list-item @click="recheck" link>
<v-list-item @click="deleteWithFiles" link> <v-icon>widgets</v-icon>
<v-icon color="red">delete</v-icon> <v-list-item-title class="ml-2" style="font-size: 12px;"
<v-list-item-title class="ml-2" style="font-size: 15px; color: red" >Force recheck</v-list-item-title
>Delete with files</v-list-item-title >
> </v-list-item>
</v-list-item> <v-list-item @click="reannounce" link>
</v-list> <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> </template>
<script> <script>
import qbit from '@/services/qbit' import qbit from '@/services/qbit'
import { General } from '@/mixins' import { General, TorrentSelect } from '@/mixins'
export default { export default {
name: 'TorrentRightClickMenu', name: 'TorrentRightClickMenu',
mixins: [General], mixins: [General, TorrentSelect],
props: { props: {
hash: String hash: String
}, },
@ -64,9 +82,12 @@ export default {
pause() { pause() {
qbit.pauseTorrents([this.hash]) qbit.pauseTorrents([this.hash])
}, },
directory() { location() {
this.createModal('ChangeLocationModal', { hash: this.hash }) this.createModal('ChangeLocationModal', { hash: this.hash })
}, },
rename() {
this.createModal('RenameModal', { hash: this.hash })
},
reannounce() { reannounce() {
qbit.reannounceTorrents([this.hash]) qbit.reannounceTorrents([this.hash])
}, },
@ -76,9 +97,13 @@ export default {
deleteWithFiles() { deleteWithFiles() {
qbit.deleteTorrents([this.hash], true) qbit.deleteTorrents([this.hash], true)
}, },
recheck() {
qbit.recheckTorrents([this.hash])
},
showInfo() { showInfo() {
this.createModal('TorrentDetailModal', { hash: this.hash }) this.createModal('TorrentDetailModal', { hash: this.hash })
} }
}, },
computed: { computed: {
dark() { 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 SettingsTab from './SettingsTab'
import Tab from './Tab' import Tab from './Tab'
import General from './General' 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_leechs = data.num_leechs
this.num_seeds = data.num_seeds this.num_seeds = data.num_seeds
this.path = data.path === undefined ? '/downloads' : data.path this.path = data.path === undefined ? '/downloads' : data.path
this.state = this.formatState(data) this.state = this.formatState(data.state)
// hash is used to identify // hash is used to identify
this.hash = data.hash this.hash = data.hash
// available seeds // available seeds
@ -22,11 +22,11 @@ export default class Torrent {
this.ratio = Math.round(data.ratio * 100) / 100 this.ratio = Math.round(data.ratio * 100) / 100
this.tags = data.tags.length > 0 ? data.tags.split(',') : null this.tags = data.tags.length > 0 ? data.tags.split(',') : null
this.category = data.category this.category = data.category
this.tracker = data.tracker
} }
formatState(item) { formatState(state) {
if (!item.tracker) return 'Fail' switch (state) {
switch (item.state) {
case 'forceDL': case 'forceDL':
case 'downloading': case 'downloading':
return 'Downloading' return 'Downloading'

View file

@ -7,7 +7,6 @@ import { isAuthenticated } from '@/services/auth.js'
Vue.use(Router) Vue.use(Router)
const router = new Router({ const router = new Router({
mode: 'history',
base: process.env.BASE_URL, base: process.env.BASE_URL,
routes: [ routes: [
{ {

View file

@ -132,6 +132,7 @@ class Qbit {
} }
deleteTorrents(hashes, deleteFiles) { deleteTorrents(hashes, deleteFiles) {
if(!hashes.length) return
return this.actionTorrents('delete', hashes, { deleteFiles }) return this.actionTorrents('delete', hashes, { deleteFiles })
} }
@ -184,6 +185,14 @@ class Qbit {
return this.actionTorrents('setLocation', hashes, { location }) return this.actionTorrents('setLocation', hashes, { location })
} }
setTorrentName(hash, name) {
const params = {
hash,
name
}
return this.axios.get('/torrents/rename', { params })
}
getTorrentProperties(hash) { getTorrentProperties(hash) {
const params = { const params = {
hash hash
@ -309,9 +318,8 @@ class Qbit {
// End Categories // End Categories
actionTorrents(action, hashes, extra) { actionTorrents(action, hashes, extra) {
if (action == 'delete' && !hashes.length) return
const params = { const params = {
hashes: hashes.length ? hashes.join('|') : 'all', hashes: hashes.join('|'),
...extra ...extra
} }
const data = new URLSearchParams(params) const data = new URLSearchParams(params)

View file

@ -1,5 +1,8 @@
<template> <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"> <h1 style="font-size: 1.1em !important" class="subtitle-1 grey--text">
Dashboard Dashboard
<p <p
@ -36,7 +39,7 @@
v-for="(torrent, index) in paginatedData" v-for="(torrent, index) in paginatedData"
:key="torrent.hash" :key="torrent.hash"
> >
<Torrent v-if="!denseDashboard" <Torrent
:class="{ :class="{
topBorderRadius: index === 0, topBorderRadius: index === 0,
noBorderRadius: noBorderRadius:
@ -47,17 +50,6 @@
:index="index" :index="index"
:length="torrents.length - 1" :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> </div>
<v-row v-if="pageCount > 1" xs12 justify="center"> <v-row v-if="pageCount > 1" xs12 justify="center">
<v-col> <v-col>
@ -82,15 +74,16 @@
<script> <script>
import { mapState, mapGetters } from 'vuex' import { mapState, mapGetters } from 'vuex'
import Torrent from '@/components/Torrent' import Torrent from '@/components/Torrent'
import TorrentDense from '@/components/TorrentDense'
import Fuse from 'fuse.js' import Fuse from 'fuse.js'
import { VueContext } from 'vue-context' import { VueContext } from 'vue-context'
import 'vue-context/src/sass/vue-context.scss' import 'vue-context/src/sass/vue-context.scss'
import TorrentRightClickMenu from '@/components/Torrent/TorrentRightClickMenu.vue' import TorrentRightClickMenu from '@/components/Torrent/TorrentRightClickMenu.vue'
import { TorrentSelect, General } from '@/mixins'
export default { export default {
name: 'Dashboard', name: 'Dashboard',
components: { Torrent, TorrentDense, VueContext, TorrentRightClickMenu }, components: { Torrent, VueContext, TorrentRightClickMenu },
mixins: [ TorrentSelect, General ],
data() { data() {
return { return {
input: '', input: '',
@ -98,7 +91,7 @@ export default {
} }
}, },
computed: { computed: {
...mapState(['mainData']), ...mapState(['mainData', 'selected_torrents']),
...mapGetters(['getTorrents', 'getTorrentCountString', 'getWebuiSettings']), ...mapGetters(['getTorrents', 'getTorrentCountString', 'getWebuiSettings']),
torrents() { torrents() {
if (!this.input || !this.input.length) return this.getTorrents() if (!this.input || !this.input.length) return this.getTorrents()
@ -133,9 +126,6 @@ export default {
}, },
torrentCountString() { torrentCountString() {
return this.getTorrentCountString() return this.getTorrentCountString()
},
denseDashboard(){
return this.getWebuiSettings().denseDashboard
} }
}, },
methods: { methods: {
@ -147,14 +137,39 @@ export default {
}, },
toTop () { toTop () {
this.$vuetify.goTo(0) 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() { created() {
this.$store.dispatch('INIT_INTERVALS') this.$store.dispatch('INIT_INTERVALS')
this.$store.commit('FETCH_CATEGORIES') this.$store.commit('FETCH_CATEGORIES')
}, },
beforeDestroy() { beforeDestroy() {
this.$store.commit('REMOVE_INTERVALS') this.$store.commit('REMOVE_INTERVALS')
document.removeEventListener('keydown', this.handleKeyboardShortcut)
}, },
watch: { watch: {
torrents: function (torrents) { torrents: function (torrents) {