2021-08-19 03:18:44 +02:00
|
|
|
import { sort } from '@/assets/fastSort'
|
2021-09-05 20:21:02 +02:00
|
|
|
import { decode } from '@/plugins/init.client'
|
2021-08-18 00:01:11 +02:00
|
|
|
|
2021-09-05 20:21:02 +02:00
|
|
|
const STANDARD_GENRES = ['Adventure', 'Autobiography', 'Biography', 'Childrens', 'Comedy', 'Crime', 'Dystopian', 'Fantasy', 'Fiction', 'Health', 'History', 'Horror', 'Mystery', 'New Adult', 'Nonfiction', 'Philosophy', 'Politics', 'Religion', 'Romance', 'Sci-Fi', 'Self-Help', 'Short Story', 'Technology', 'Thriller', 'True Crime', 'Western', 'Young Adult']
|
2021-08-20 00:29:36 +02:00
|
|
|
|
2021-08-18 00:01:11 +02:00
|
|
|
export const state = () => ({
|
|
|
|
audiobooks: [],
|
2021-09-24 14:32:38 +02:00
|
|
|
lastLoad: 0,
|
2021-08-20 00:29:36 +02:00
|
|
|
listeners: [],
|
|
|
|
genres: [...STANDARD_GENRES],
|
2021-08-22 15:52:37 +02:00
|
|
|
tags: [],
|
2021-09-05 20:21:02 +02:00
|
|
|
series: [],
|
|
|
|
keywordFilter: null
|
2021-08-18 00:01:11 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
export const getters = {
|
2021-09-15 03:45:00 +02:00
|
|
|
getAudiobook: (state) => id => {
|
|
|
|
return state.audiobooks.find(ab => ab.id === id)
|
|
|
|
},
|
2021-09-21 23:42:01 +02:00
|
|
|
getFiltered: (state, getters, rootState, rootGetters) => () => {
|
2021-08-19 03:18:44 +02:00
|
|
|
var filtered = state.audiobooks
|
2021-08-24 01:31:04 +02:00
|
|
|
var settings = rootState.user.settings || {}
|
2021-08-20 00:29:36 +02:00
|
|
|
var filterBy = settings.filterBy || ''
|
2021-08-22 15:52:37 +02:00
|
|
|
|
2021-09-21 23:42:01 +02:00
|
|
|
var searchGroups = ['genres', 'tags', 'series', 'authors', 'progress']
|
2021-08-22 15:52:37 +02:00
|
|
|
var group = searchGroups.find(_group => filterBy.startsWith(_group + '.'))
|
|
|
|
if (group) {
|
2021-09-05 20:21:02 +02:00
|
|
|
var filter = decode(filterBy.replace(`${group}.`, ''))
|
2021-08-22 15:52:37 +02:00
|
|
|
if (group === 'genres') filtered = filtered.filter(ab => ab.book && ab.book.genres.includes(filter))
|
|
|
|
else if (group === 'tags') filtered = filtered.filter(ab => ab.tags.includes(filter))
|
|
|
|
else if (group === 'series') filtered = filtered.filter(ab => ab.book && ab.book.series === filter)
|
2021-08-25 03:24:40 +02:00
|
|
|
else if (group === 'authors') filtered = filtered.filter(ab => ab.book && ab.book.author === filter)
|
2021-09-21 23:42:01 +02:00
|
|
|
else if (group === 'progress') {
|
|
|
|
filtered = filtered.filter(ab => {
|
|
|
|
var userAudiobook = rootGetters['user/getUserAudiobook'](ab.id)
|
|
|
|
var isRead = userAudiobook && userAudiobook.isRead
|
|
|
|
if (filter === 'Read' && isRead) return true
|
|
|
|
if (filter === 'Unread' && !isRead) return true
|
|
|
|
if (filter === 'In Progress' && (userAudiobook && !userAudiobook.isRead && userAudiobook.progress > 0)) return true
|
|
|
|
return false
|
|
|
|
})
|
|
|
|
}
|
2021-08-20 00:29:36 +02:00
|
|
|
}
|
2021-09-05 20:21:02 +02:00
|
|
|
if (state.keywordFilter) {
|
|
|
|
const keywordFilterKeys = ['title', 'subtitle', 'author', 'series', 'narrarator']
|
2021-09-11 02:55:02 +02:00
|
|
|
const keyworkFilter = state.keywordFilter.toLowerCase()
|
2021-09-05 20:21:02 +02:00
|
|
|
return filtered.filter(ab => {
|
|
|
|
if (!ab.book) return false
|
2021-09-11 02:55:02 +02:00
|
|
|
return !!keywordFilterKeys.find(key => (ab.book[key] && ab.book[key].toLowerCase().includes(keyworkFilter)))
|
2021-09-05 20:21:02 +02:00
|
|
|
})
|
|
|
|
}
|
2021-08-19 03:18:44 +02:00
|
|
|
return filtered
|
|
|
|
},
|
|
|
|
getFilteredAndSorted: (state, getters, rootState) => () => {
|
2021-08-24 01:31:04 +02:00
|
|
|
var settings = rootState.user.settings
|
2021-08-19 03:18:44 +02:00
|
|
|
var direction = settings.orderDesc ? 'desc' : 'asc'
|
2021-08-18 00:01:11 +02:00
|
|
|
|
2021-08-19 03:18:44 +02:00
|
|
|
var filtered = getters.getFiltered()
|
2021-09-17 21:15:15 +02:00
|
|
|
var orderByNumber = settings.orderBy === 'book.volumeNumber'
|
2021-08-19 03:18:44 +02:00
|
|
|
return sort(filtered)[direction]((ab) => {
|
|
|
|
// Supports dot notation strings i.e. "book.title"
|
2021-09-17 21:15:15 +02:00
|
|
|
var value = settings.orderBy.split('.').reduce((a, b) => a[b], ab)
|
|
|
|
if (orderByNumber && !isNaN(value)) return Number(value)
|
|
|
|
return value
|
2021-08-19 03:18:44 +02:00
|
|
|
})
|
2021-08-25 03:24:40 +02:00
|
|
|
},
|
2021-09-23 03:40:35 +02:00
|
|
|
getSeriesGroups: (state, getters, rootState) => () => {
|
|
|
|
var series = {}
|
|
|
|
state.audiobooks.forEach((audiobook) => {
|
|
|
|
if (audiobook.book && audiobook.book.series) {
|
|
|
|
if (series[audiobook.book.series]) {
|
|
|
|
series[audiobook.book.series].books.push(audiobook)
|
|
|
|
} else {
|
|
|
|
series[audiobook.book.series] = {
|
|
|
|
type: 'series',
|
2021-09-26 22:34:08 +02:00
|
|
|
name: audiobook.book.series || '',
|
2021-09-23 03:40:35 +02:00
|
|
|
books: [audiobook]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
2021-09-26 22:34:08 +02:00
|
|
|
var seriesArray = Object.values(series).map((_series) => {
|
|
|
|
_series.books = sort(_series.books)['asc']((ab) => {
|
|
|
|
return ab.book && ab.book.volumeNumber && !isNaN(ab.book.volumeNumber) ? Number(ab.book.volumeNumber) : null
|
|
|
|
})
|
|
|
|
return _series
|
|
|
|
})
|
|
|
|
if (state.keywordFilter) {
|
|
|
|
const keywordFilter = state.keywordFilter.toLowerCase()
|
|
|
|
return seriesArray.filter((_series) => _series.name.toLowerCase().includes(keywordFilter))
|
|
|
|
}
|
|
|
|
return seriesArray
|
2021-09-23 03:40:35 +02:00
|
|
|
},
|
2021-08-25 03:24:40 +02:00
|
|
|
getUniqueAuthors: (state) => {
|
|
|
|
var _authors = state.audiobooks.filter(ab => !!(ab.book && ab.book.author)).map(ab => ab.book.author)
|
2021-09-05 20:21:02 +02:00
|
|
|
return [...new Set(_authors)].sort((a, b) => a.toLowerCase() < b.toLowerCase() ? -1 : 1)
|
|
|
|
},
|
|
|
|
getGenresUsed: (state) => {
|
|
|
|
var _genres = []
|
|
|
|
state.audiobooks.filter(ab => !!(ab.book && ab.book.genres)).forEach(ab => _genres = _genres.concat(ab.book.genres))
|
|
|
|
return [...new Set(_genres)].sort((a, b) => a.toLowerCase() < b.toLowerCase() ? -1 : 1)
|
2021-09-24 14:32:38 +02:00
|
|
|
},
|
|
|
|
getBookCoverSrc: (state, getters, rootState, rootGetters) => (book, placeholder = '/book_placeholder.jpg') => {
|
|
|
|
if (!book || !book.cover || book.cover === placeholder) return placeholder
|
|
|
|
var cover = book.cover
|
|
|
|
|
|
|
|
// Absolute URL covers
|
|
|
|
if (cover.startsWith('http:') || cover.startsWith('https:')) return cover
|
|
|
|
|
|
|
|
// Server hosted covers
|
|
|
|
try {
|
|
|
|
// Ensure cover is refreshed if cached
|
|
|
|
var bookLastUpdate = book.lastUpdate || Date.now()
|
|
|
|
var userToken = rootGetters['user/getToken']
|
|
|
|
|
|
|
|
var url = new URL(cover, document.baseURI)
|
|
|
|
return url.href + `?token=${userToken}&ts=${bookLastUpdate}`
|
|
|
|
} catch (err) {
|
|
|
|
console.error(err)
|
|
|
|
return placeholder
|
|
|
|
}
|
2021-08-19 03:18:44 +02:00
|
|
|
}
|
2021-08-18 00:01:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export const actions = {
|
2021-09-24 14:32:38 +02:00
|
|
|
// Return true if calling load
|
|
|
|
load({ state, commit, rootState }) {
|
2021-08-24 02:37:40 +02:00
|
|
|
if (!rootState.user || !rootState.user.user) {
|
|
|
|
console.error('audiobooks/load - User not set')
|
2021-09-24 14:32:38 +02:00
|
|
|
return false
|
2021-08-24 02:37:40 +02:00
|
|
|
}
|
2021-09-24 14:32:38 +02:00
|
|
|
|
|
|
|
// Don't load again if already loaded in the last 5 minutes
|
|
|
|
var lastLoadDiff = Date.now() - state.lastLoad
|
|
|
|
if (lastLoadDiff < 5 * 60 * 1000) {
|
|
|
|
// Already up to date
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2021-08-18 00:01:11 +02:00
|
|
|
this.$axios
|
|
|
|
.$get(`/api/audiobooks`)
|
|
|
|
.then((data) => {
|
|
|
|
commit('set', data)
|
2021-09-24 14:32:38 +02:00
|
|
|
commit('setLastLoad')
|
2021-08-18 00:01:11 +02:00
|
|
|
})
|
|
|
|
.catch((error) => {
|
|
|
|
console.error('Failed', error)
|
|
|
|
commit('set', [])
|
|
|
|
})
|
2021-09-24 14:32:38 +02:00
|
|
|
return true
|
2021-08-19 03:18:44 +02:00
|
|
|
},
|
|
|
|
|
2021-08-18 00:01:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export const mutations = {
|
2021-09-24 14:32:38 +02:00
|
|
|
setLastLoad(state) {
|
|
|
|
state.lastLoad = Date.now()
|
|
|
|
},
|
2021-09-05 20:21:02 +02:00
|
|
|
setKeywordFilter(state, val) {
|
|
|
|
state.keywordFilter = val
|
|
|
|
},
|
2021-08-18 00:01:11 +02:00
|
|
|
set(state, audiobooks) {
|
2021-08-20 00:29:36 +02:00
|
|
|
// GENRES
|
|
|
|
var genres = [...state.genres]
|
|
|
|
audiobooks.forEach((ab) => {
|
|
|
|
if (!ab.book) return
|
|
|
|
genres = genres.concat(ab.book.genres)
|
|
|
|
})
|
|
|
|
state.genres = [...new Set(genres)] // Remove Duplicates
|
2021-08-22 15:52:37 +02:00
|
|
|
state.genres.sort((a, b) => a.toLowerCase() < b.toLowerCase() ? -1 : 1)
|
2021-08-20 00:29:36 +02:00
|
|
|
|
|
|
|
// TAGS
|
|
|
|
var tags = []
|
|
|
|
audiobooks.forEach((ab) => {
|
|
|
|
tags = tags.concat(ab.tags)
|
|
|
|
})
|
|
|
|
state.tags = [...new Set(tags)] // Remove Duplicates
|
2021-08-22 15:52:37 +02:00
|
|
|
state.tags.sort((a, b) => a.toLowerCase() < b.toLowerCase() ? -1 : 1)
|
|
|
|
|
|
|
|
// SERIES
|
|
|
|
var series = []
|
|
|
|
audiobooks.forEach((ab) => {
|
|
|
|
if (!ab.book || !ab.book.series || series.includes(ab.book.series)) return
|
|
|
|
series.push(ab.book.series)
|
|
|
|
})
|
|
|
|
state.series = series
|
|
|
|
state.series.sort((a, b) => a.toLowerCase() < b.toLowerCase() ? -1 : 1)
|
2021-08-20 00:29:36 +02:00
|
|
|
|
2021-08-18 00:01:11 +02:00
|
|
|
state.audiobooks = audiobooks
|
|
|
|
state.listeners.forEach((listener) => {
|
|
|
|
listener.meth()
|
|
|
|
})
|
|
|
|
},
|
|
|
|
addUpdate(state, audiobook) {
|
|
|
|
var index = state.audiobooks.findIndex(a => a.id === audiobook.id)
|
2021-08-22 15:52:37 +02:00
|
|
|
var origAudiobook = null
|
2021-08-18 00:01:11 +02:00
|
|
|
if (index >= 0) {
|
2021-08-22 15:52:37 +02:00
|
|
|
origAudiobook = { ...state.audiobooks[index] }
|
2021-08-18 00:01:11 +02:00
|
|
|
state.audiobooks.splice(index, 1, audiobook)
|
|
|
|
} else {
|
|
|
|
state.audiobooks.push(audiobook)
|
|
|
|
}
|
|
|
|
|
2021-08-20 00:29:36 +02:00
|
|
|
if (audiobook.book) {
|
2021-08-22 15:52:37 +02:00
|
|
|
// GENRES
|
2021-08-20 00:29:36 +02:00
|
|
|
var newGenres = []
|
|
|
|
audiobook.book.genres.forEach((genre) => {
|
|
|
|
if (!state.genres.includes(genre)) newGenres.push(genre)
|
|
|
|
})
|
2021-08-22 15:52:37 +02:00
|
|
|
if (newGenres.length) {
|
|
|
|
state.genres = state.genres.concat(newGenres)
|
|
|
|
state.genres.sort((a, b) => a.toLowerCase() < b.toLowerCase() ? -1 : 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SERIES
|
|
|
|
if (audiobook.book.series && !state.series.includes(audiobook.book.series)) {
|
|
|
|
state.series.push(audiobook.book.series)
|
|
|
|
state.series.sort((a, b) => a.toLowerCase() < b.toLowerCase() ? -1 : 1)
|
|
|
|
}
|
|
|
|
if (origAudiobook && origAudiobook.book && origAudiobook.book.series) {
|
|
|
|
var isInAB = state.audiobooks.find(ab => ab.book && ab.book.series === origAudiobook.book.series)
|
|
|
|
if (!isInAB) state.series = state.series.filter(series => series !== origAudiobook.book.series)
|
|
|
|
}
|
2021-08-20 00:29:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// TAGS
|
|
|
|
var newTags = []
|
|
|
|
audiobook.tags.forEach((tag) => {
|
|
|
|
if (!state.tags.includes(tag)) newTags.push(tag)
|
|
|
|
})
|
2021-08-22 15:52:37 +02:00
|
|
|
if (newTags.length) {
|
|
|
|
state.tags = state.tags.concat(newTags)
|
|
|
|
state.tags.sort((a, b) => a.toLowerCase() < b.toLowerCase() ? -1 : 1)
|
|
|
|
}
|
2021-08-20 00:29:36 +02:00
|
|
|
|
2021-08-18 00:01:11 +02:00
|
|
|
state.listeners.forEach((listener) => {
|
|
|
|
if (!listener.audiobookId || listener.audiobookId === audiobook.id) {
|
|
|
|
listener.meth()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
},
|
|
|
|
remove(state, audiobook) {
|
|
|
|
state.audiobooks = state.audiobooks.filter(a => a.id !== audiobook.id)
|
|
|
|
|
2021-08-20 00:29:36 +02:00
|
|
|
if (audiobook.book) {
|
2021-08-22 15:52:37 +02:00
|
|
|
// GENRES
|
2021-08-20 00:29:36 +02:00
|
|
|
audiobook.book.genres.forEach((genre) => {
|
|
|
|
if (!STANDARD_GENRES.includes(genre)) {
|
|
|
|
var isInOtherAB = state.audiobooks.find(ab => {
|
|
|
|
return ab.book && ab.book.genres.includes(genre)
|
|
|
|
})
|
|
|
|
if (!isInOtherAB) {
|
|
|
|
// Genre is not used by any other audiobook - remove it
|
|
|
|
state.genres = state.genres.filter(g => g !== genre)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
2021-08-22 15:52:37 +02:00
|
|
|
|
|
|
|
// SERIES
|
|
|
|
if (audiobook.book.series) {
|
|
|
|
var isInOtherAB = state.audiobooks.find(ab => ab.book && ab.book.series === audiobook.book.series)
|
|
|
|
if (!isInOtherAB) {
|
|
|
|
// Series not used in any other audiobook - remove it
|
|
|
|
state.series = state.series.filter(s => s !== audiobook.book.series)
|
|
|
|
}
|
|
|
|
}
|
2021-08-20 00:29:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// TAGS
|
|
|
|
audiobook.tags.forEach((tag) => {
|
|
|
|
var isInOtherAB = state.audiobooks.find(ab => {
|
|
|
|
return ab.tags.includes(tag)
|
|
|
|
})
|
|
|
|
if (!isInOtherAB) {
|
|
|
|
// Tag is not used by any other audiobook - remove it
|
|
|
|
state.tags = state.tags.filter(t => t !== tag)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2021-08-18 00:01:11 +02:00
|
|
|
state.listeners.forEach((listener) => {
|
|
|
|
if (!listener.audiobookId || listener.audiobookId === audiobook.id) {
|
|
|
|
listener.meth()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
},
|
|
|
|
addListener(state, listener) {
|
|
|
|
var index = state.listeners.findIndex(l => l.id === listener.id)
|
|
|
|
if (index >= 0) state.listeners.splice(index, 1, listener)
|
|
|
|
else state.listeners.push(listener)
|
|
|
|
},
|
|
|
|
removeListener(state, listenerId) {
|
|
|
|
state.listeners = state.listeners.filter(l => l.id !== listenerId)
|
|
|
|
}
|
|
|
|
}
|