delete old stats view and added all library stats view

This commit is contained in:
Vito0912 2025-01-01 20:34:20 +01:00
parent 793ef0e9ec
commit b127f0c406
No known key found for this signature in database
GPG Key ID: 29A3D509FE70B237
2 changed files with 31 additions and 181 deletions

View File

@ -1,174 +0,0 @@
<template>
<app-settings-content :header-text="$strings.HeaderAllLibrariesStats" class="!mb-4">
<div class="w-full max-w-4xl mx-auto">
<stats-preview-icons v-if="totalItems" :library-stats="combinedLibraryStats" />
<div class="flex lg:flex-row flex-wrap justify-between flex-col mt-8">
<div class="w-80 my-6 mx-auto">
<h1 class="text-2xl mb-4">{{ $strings.HeaderStatsTop5Genres }}</h1>
<p v-if="!top5Genres.length">{{ $strings.MessageNoGenres }}</p>
<template v-for="genre in top5Genres">
<div :key="genre.genre" class="w-full py-2">
<div class="flex items-end mb-1">
<p class="text-2xl font-bold">{{ Math.round((100 * genre.count) / totalItems) }}&nbsp;%</p>
<div class="flex-grow" />
<nuxt-link :to="`/library/${currentLibraryId}/bookshelf?filter=genres.${$encode(genre.genre)}`" class="text-base text-white text-opacity-70 hover:underline">
{{ genre.genre }}
</nuxt-link>
</div>
<div class="w-full rounded-full h-3 bg-primary bg-opacity-50 overflow-hidden">
<div class="bg-yellow-400 h-full rounded-full" :style="{ width: Math.round((100 * genre.count) / totalItems) + '%' }" />
</div>
</div>
</template>
</div>
<div v-if="top10Authors.length" class="w-80 my-6 mx-auto">
<h1 class="text-2xl mb-4">{{ $strings.HeaderStatsTop10Authors }}</h1>
<p v-if="!top10Authors.length">{{ $strings.MessageNoAuthors }}</p>
<template v-for="(author, index) in top10Authors">
<div :key="author.id" class="w-full py-2">
<div class="flex items-center mb-1">
<p class="text-sm text-white text-opacity-70 w-36 pr-2 truncate">
{{ index + 1 }}.&nbsp;&nbsp;&nbsp;&nbsp;<nuxt-link :to="`/library/${currentLibraryId}/bookshelf?filter=authors.${$encode(author.id)}`" class="hover:underline">{{ author.name }}</nuxt-link>
</p>
<div class="flex-grow rounded-full h-2.5 bg-primary bg-opacity-0 overflow-hidden">
<div class="bg-yellow-400 h-full rounded-full" :style="{ width: Math.round((100 * author.count) / mostUsedAuthorCount) + '%' }" />
</div>
<div class="w-4 ml-3">
<p class="text-sm font-bold">{{ author.count }}</p>
</div>
</div>
</div>
</template>
</div>
<div class="w-80 my-6 mx-auto">
<h1 class="text-2xl mb-4">{{ $strings.HeaderStatsLongestItems }}</h1>
<p v-if="!top10LongestItems.length">{{ $strings.MessageNoItems }}</p>
<template v-for="(ab, index) in top10LongestItems">
<div :key="index" class="w-full py-2">
<div class="flex items-center mb-1">
<p class="text-sm text-white text-opacity-70 w-44 pr-2 truncate">
{{ index + 1 }}.&nbsp;&nbsp;&nbsp;&nbsp;<nuxt-link :to="`/item/${ab.id}`" class="hover:underline">{{ ab.title }}</nuxt-link>
</p>
<div class="flex-grow rounded-full h-2.5 bg-primary bg-opacity-0 overflow-hidden">
<div class="bg-yellow-400 h-full rounded-full" :style="{ width: Math.round((100 * ab.duration) / longestItemDuration) + '%' }" />
</div>
<div class="w-4 ml-3">
<p class="text-sm font-bold">{{ (ab.duration / 3600).toFixed(1) }}</p>
</div>
</div>
</div>
</template>
</div>
<div class="w-80 my-6 mx-auto">
<h1 class="text-2xl mb-4">{{ $strings.HeaderStatsLargestItems }}</h1>
<p v-if="!top10LargestItems.length">{{ $strings.MessageNoItems }}</p>
<template v-for="(ab, index) in top10LargestItems">
<div :key="index" class="w-full py-2">
<div class="flex items-center mb-1">
<p class="text-sm text-white text-opacity-70 w-44 pr-2 truncate">
{{ index + 1 }}.&nbsp;&nbsp;&nbsp;&nbsp;<nuxt-link :to="`/item/${ab.id}`" class="hover:underline">{{ ab.title }}</nuxt-link>
</p>
<div class="flex-grow rounded-full h-2.5 bg-primary bg-opacity-0 overflow-hidden">
<div class="bg-yellow-400 h-full rounded-full" :style="{ width: Math.round((100 * ab.size) / largestItemSize) + '%' }" />
</div>
<div class="w-4 ml-3">
<p class="text-sm font-bold whitespace-nowrap">{{ $bytesPretty(ab.size) }}</p>
</div>
</div>
</div>
</template>
</div>
</div>
</div>
</app-settings-content>
</template>
<script>
export default {
asyncData({ redirect, store }) {
if (!store.getters['user/getIsAdminOrUp']) {
redirect('/')
return
}
return {}
},
data() {
return {
libraryStats: []
}
},
computed: {
combinedLibraryStats() {
return this.libraryStats.reduce(
(combined, stats) => {
combined.totalItems += stats.totalItems || 0
combined.totalDuration += stats.totalDuration || 0
combined.totalAuthors += stats.totalAuthors || 0
combined.totalSize += stats.totalSize || 0
combined.numAudioTracks += stats.numAudioTracks || 0
combined.genresWithCount = [...combined.genresWithCount, ...(stats.genresWithCount || [])].sort((a, b) => b.count - a.count).slice(0, 10)
combined.longestItems = [...combined.longestItems, ...(stats.longestItems || [])].sort((a, b) => b.duration - a.duration).slice(0, 10)
combined.largestItems = [...combined.largestItems, ...(stats.largestItems || [])].sort((a, b) => b.size - a.size).slice(0, 10)
combined.authorsWithCount = [...combined.authorsWithCount, ...(stats.authorsWithCount || [])].sort((a, b) => b.count - a.count).slice(0, 10)
return combined
},
{
totalItems: 0,
totalDuration: 0,
totalAuthors: 0,
totalSize: 0,
numAudioTracks: 0,
genresWithCount: [],
longestItems: [],
largestItems: [],
authorsWithCount: []
}
)
},
totalItems() {
return this.combinedLibraryStats.totalItems || 0
},
top5Genres() {
return this.combinedLibraryStats.genresWithCount?.slice(0, 5) || []
},
top10LongestItems() {
return this.combinedLibraryStats.longestItems || []
},
longestItemDuration() {
if (!this.top10LongestItems.length) return 0
return this.top10LongestItems[0].duration
},
top10LargestItems() {
return this.combinedLibraryStats.largestItems || []
},
largestItemSize() {
if (!this.top10LargestItems.length) return 0
return this.top10LargestItems[0].size
},
top10Authors() {
return this.combinedLibraryStats.authorsWithCount?.slice(0, 10) || []
},
currentLibraryId() {
return this.$store.state.libraries.currentLibraryId
},
mostUsedAuthorCount() {
if (!this.combinedLibraryStats.authorsWithCount.length) return 0
return this.combinedLibraryStats.authorsWithCount[0].count
},
},
methods: {
async init() {
this.libraryStatsInformation = await this.$axios.$get(`/api/libraries/stats`).catch((err) => {
console.error('Failed to get library stats', err)
})
this.libraryStats = this.libraryStatsInformation.map(entry => {
return { ...entry.stats };
});
}
},
mounted() {
this.init()
}
}
</script>

View File

@ -1,6 +1,8 @@
<template>
<div class="page relative" :class="streamLibraryItem ? 'streaming' : ''">
<app-book-shelf-toolbar page="library-stats" is-home />
<ui-btn class="text-xs" :padding-x="1.5" :padding-y="1" @click="toggleStats">
{{ showAllLibraryStats ? currentLibraryName : 'Show all Libraries' }}</ui-btn>
<div id="bookshelf" class="w-full h-full px-1 py-4 md:p-8 relative overflow-y-auto">
<div class="w-full max-w-4xl mx-auto">
<stats-preview-icons v-if="totalItems" :library-stats="libraryStats" />
@ -24,7 +26,7 @@
</div>
</template>
</div>
<div v-if="isBookLibrary" class="w-80 my-6 mx-auto">
<div v-if="isBookLibrary || showAllLibraryStats" class="w-80 my-6 mx-auto">
<h1 class="text-2xl mb-4">{{ $strings.HeaderStatsTop10Authors }}</h1>
<p v-if="!top10Authors.length">{{ $strings.MessageNoAuthors }}</p>
<template v-for="(author, index) in top10Authors">
@ -102,7 +104,8 @@ export default {
},
data() {
return {
libraryStats: null
libraryStats: null,
showAllLibraryStats: false
}
},
watch: {
@ -110,6 +113,12 @@ export default {
if (newVal) {
this.init()
}
},
showAllLibraryStats(newVal) {
console.log('showAllLibraryStats', newVal)
if (newVal) {
this.init()
}
}
},
computed: {
@ -167,11 +176,26 @@ export default {
},
methods: {
async init() {
this.libraryStats = await this.$axios.$get(`/api/libraries/${this.currentLibraryId}/stats`).catch((err) => {
console.error('Failed to get library stats', err)
var errorMsg = err.response ? err.response.data || 'Unknown Error' : 'Unknown Error'
this.$toast.error(`Failed to get library stats: ${errorMsg}`)
})
this.libraryStats = null
if (this.showAllLibraryStats === true) {
this.libraryStats = (await this.$axios.$get(`/api/libraries/stats`).catch((err) => {
console.error('Failed to get library stats', err)
var errorMsg = err.response ? err.response.data || 'Unknown Error' : 'Unknown Error'
this.$toast.error(`Failed to get library stats: ${errorMsg}`)
}))['combined']
} else {
this.libraryStats = await this.$axios.$get(`/api/libraries/${this.currentLibraryId}/stats`).catch((err) => {
console.error('Failed to get library stats', err)
var errorMsg = err.response ? err.response.data || 'Unknown Error' : 'Unknown Error'
this.$toast.error(`Failed to get library stats: ${errorMsg}`)
})
}
},
toggleStats() {
this.showAllLibraryStats = !this.showAllLibraryStats;
this.$nextTick(() => {
this.init();
});
}
},
mounted() {