sorting and filtering

This commit is contained in:
Daan Wijns 2020-05-26 09:07:09 +02:00
parent c17b0aa792
commit ea9dd56614
5 changed files with 204 additions and 86 deletions

View file

@ -40,6 +40,16 @@ The sleekest looking WEBUI for qBittorrent made with Vuejs!
* works on QBittorrent V4.2 and later * works on QBittorrent V4.2 and later
### Sorting/Filtring
example queries:
- s name asc => sort by name ascending
- sort size desc => sort by size descending
- f ubuntu => filter by torrent-name that contains 'ubuntu'
- f done => filter all completed torrents
- f busy => filter all downloading torrents
## 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 😛 ).

View file

@ -137,7 +137,7 @@
<script> <script>
import { VueContext } from 'vue-context' import { VueContext } from 'vue-context'
import torrentRightClickMenu from '@/components/Torrent/torrentRightClickMenu.vue' import torrentRightClickMenu from '@/components/Torrent/TorrentRightClickMenu.vue'
export default { export default {
name: 'Torrent', name: 'Torrent',

View file

@ -115,9 +115,18 @@ class Qbit {
getTorrents(payload) { getTorrents(payload) {
let params = { let params = {
sort: payload.sort, sort: payload.sort,
reverse: payload.reverse reverse: payload.reverse,
hashes: payload.hashes ? payload.hashes.join('|') : null,
filter: payload.filter ? payload.filter : null
} }
//clean
Object.keys(params).forEach(
key => params[key] == null && delete params[key]
)
const data = new URLSearchParams(params) const data = new URLSearchParams(params)
return this.axios.get(`/torrents/info?${data.toString()}`) return this.axios.get(`/torrents/info?${data.toString()}`)
} }

View file

@ -25,7 +25,7 @@ export default new Vuex.Store({
selected_torrents: [], selected_torrents: [],
authenticated: false, authenticated: false,
loading: false, loading: false,
sort_options: { sort: 'name', reverse: false }, sort_options: { sort: 'name', reverse: false, hashes: [], filter: null },
rid: 0, rid: 0,
mainData: undefined, mainData: undefined,
preferences: null, preferences: null,
@ -111,6 +111,8 @@ export default new Vuex.Store({
UPDATE_SORT_OPTIONS: (state, payload) => { UPDATE_SORT_OPTIONS: (state, payload) => {
state.sort_options.sort = payload.name state.sort_options.sort = payload.name
state.sort_options.reverse = payload.reverse state.sort_options.reverse = payload.reverse
state.sort_options.hashes = payload.hashes ? payload.hashes : null
state.sort_options.filter = payload.filter ? payload.filter : null
} }
}, },
actions: { actions: {

View file

@ -16,7 +16,7 @@
height="50" height="50"
clearable clearable
solo solo
hint="eg `size desc` + enter" hint="eg `s size desc` + enter"
color="search" color="search"
v-model="sort_input" v-model="sort_input"
@keyup.enter.native="sortBy" @keyup.enter.native="sortBy"
@ -40,6 +40,113 @@ import { mapState, mapMutations } from 'vuex'
import Torrent from '@/components/Torrent' import Torrent from '@/components/Torrent'
import TorrentDetailModal from '@/components/TorrentDetailModal/TorrentDetailModal' import TorrentDetailModal from '@/components/TorrentDetailModal/TorrentDetailModal'
function getPropName(prop) {
switch (prop) {
case 'title':
case 'name':
case 'Name':
case 'Title':
return 'name'
case 'size':
case 'Size':
return 'size'
case 'dlspeed':
case 'Dlspeed':
case 'Download':
case 'download':
case 'downloadspeed':
return 'dlspeed'
case 'upspeed':
case 'upload':
case 'Upload':
case 'Upspeed':
case 'uploadspeed':
return 'upspeed'
case 'leechs':
case 'leechers':
case 'leech':
case 'peers':
case 'Leechs':
case 'Leechers':
case 'Leech':
case 'Peers':
return 'num_leechs'
case 'seeds':
case 'seeders':
case 'Seeds':
case 'Seeders':
return 'num_seeds'
case 'remaining':
case 'time':
case 'Time':
case 'ETA':
case 'eta':
return 'eta'
case 'done':
case 'downloaded':
case 'dloaded':
case 'Done':
case 'Downloaded':
case 'Dloaded':
return 'progress'
case 'state':
case 'status':
case 'State':
case 'Status':
return 'state'
default:
return 'name'
}
}
function sortOrFilter(word) {
switch (word) {
case 'sort':
case 's':
case 'srt':
return 'sort'
case 'f':
case 'filter':
case 'filtr':
case 'fltr':
case 'filt':
return 'filter'
default:
return 'sort'
}
}
function filterOption(word) {
switch (word) {
case 'Done':
case 'done':
case 'completed':
case 'complete':
return 'completed'
case 'Busy':
case 'busy':
case 'downl':
case 'download':
case 'downloading':
case 'act':
case 'active':
case 'resumed':
return 'active'
case 'fail':
case 'failed':
case 'faild':
case 'stalled':
case 'stalld':
case 'stall':
return 'stalled'
case 'pause':
case 'paused':
return 'paused'
default:
return null
}
}
export default { export default {
name: 'Dashboard', name: 'Dashboard',
components: { Torrent, TorrentDetailModal }, components: { Torrent, TorrentDetailModal },
@ -54,91 +161,81 @@ export default {
methods: { methods: {
...mapMutations(['SORT_TORRENTS']), ...mapMutations(['SORT_TORRENTS']),
sortBy() { sortBy() {
let name let parts = this.sort_input.split(' ')
let reverse
// search if order was presented if (parts.length === 0) {
const index = this.sort_input.indexOf(' ') let name = 'name'
if (index > -1) { let reverse = false
name = this.sort_input.substring(0, index) return this.$store.commit('UPDATE_SORT_OPTIONS', {
const temp = this.sort_input.substring(index) name,
if (temp.indexOf('asc') > -1) { reverse
reverse = false })
} else if (temp.indexOf('desc') > -1) {
reverse = true
} }
} else { //basic sort
// no order so we assume input is propname if (parts.length === 1) {
name = this.sort_input let name = getPropName(parts[0])
reverse = false let reverse = false
return this.$store.commit('UPDATE_SORT_OPTIONS', {
name,
reverse
})
}
// could be sort OR filter
if (parts.length === 2) {
let type = sortOrFilter(parts[0])
if (type === 'sort') {
let name = getPropName(parts[1])
let reverse = false
return this.$store.commit('UPDATE_SORT_OPTIONS', {
name,
reverse
})
}
if (type === 'filter') {
let ftype = filterOption(parts[1])
//filter state
if (ftype) {
let name = 'name'
let reverse = false
return this.$store.commit('UPDATE_SORT_OPTIONS', {
name,
reverse,
filter: ftype
})
}
//filter name
let filtered = this.torrents.filter(t =>
t.name.toLowerCase().includes(parts[1].toLowerCase())
)
let name = 'name'
let reverse = false
let hashes = filtered.map(t => t.hash)
return this.$store.commit('UPDATE_SORT_OPTIONS', {
name,
reverse,
hashes
})
}
}
//sort with asc/desc
if (parts.length === 3) {
let type = sortOrFilter(parts[0])
if (type === 'sort') {
let name = getPropName(parts[1])
let reverse = parts[2] === 'desc'
return this.$store.commit('UPDATE_SORT_OPTIONS', {
name,
reverse
})
} }
// prop names
switch (name) {
case 'title':
case 'name':
case 'Name':
case 'Title':
name = 'name'
break
case 'size':
case 'Size':
name = 'size'
break
case 'dlspeed':
case 'Dlspeed':
case 'Download':
case 'download':
case 'downloadspeed':
name = 'dlspeed'
break
case 'upspeed':
case 'upload':
case 'Upload':
case 'Upspeed':
case 'uploadspeed':
name = 'upspeed'
break
case 'leechs':
case 'leechers':
case 'leech':
case 'peers':
case 'Leechs':
case 'Leechers':
case 'Leech':
case 'Peers':
name = 'num_leechs'
break
case 'seeds':
case 'seeders':
case 'Seeds':
case 'Seeders':
name = 'num_seeds'
break
case 'remaining':
case 'time':
case 'Time':
case 'ETA':
case 'eta':
name = 'eta'
break
case 'done':
case 'downloaded':
case 'dloaded':
case 'Done':
case 'Downloaded':
case 'Dloaded':
name = 'progress'
break
case 'state':
case 'status':
case 'State':
case 'Status':
name = 'state'
break
default:
name = 'name'
break
} }
this.$store.commit('UPDATE_SORT_OPTIONS', { name, reverse })
}, },
resetSelected() { resetSelected() {
this.$store.commit('RESET_SELECTED') this.$store.commit('RESET_SELECTED')