Adds genres to gloabl search

This commit is contained in:
mikiher 2024-07-21 11:10:05 +03:00
parent 9cd0ac80b1
commit bff56220c2
4 changed files with 135 additions and 61 deletions

View File

@ -0,0 +1,34 @@
<template>
<div class="flex h-full px-1 overflow-hidden">
<div class="w-10 h-10 flex items-center justify-center">
<span class="material-symbols text-2xl text-gray-200">category</span>
</div>
<div class="flex-grow px-2 tagSearchCardContent h-full">
<p class="truncate text-sm">{{ genre }}</p>
</div>
</div>
</template>
<script>
export default {
props: {
genre: String
},
data() {
return {}
},
computed: {},
methods: {},
mounted() {}
}
</script>
<style>
.tagSearchCardContent {
width: calc(100% - 40px);
height: 40px;
display: flex;
flex-direction: column;
justify-content: center;
}
</style>

View File

@ -59,13 +59,22 @@
<p v-if="tagResults.length" class="uppercase text-xs text-gray-400 mb-1 mt-3 px-1 font-semibold">{{ $strings.LabelTags }}</p>
<template v-for="item in tagResults">
<li :key="item.name" class="text-gray-50 select-none relative cursor-pointer hover:bg-black-400 py-1" role="option" @click="clickOption">
<li :key="`tag.${item.name}`" class="text-gray-50 select-none relative cursor-pointer hover:bg-black-400 py-1" role="option" @click="clickOption">
<nuxt-link :to="`/library/${currentLibraryId}/bookshelf?filter=tags.${$encode(item.name)}`">
<cards-tag-search-card :tag="item.name" />
</nuxt-link>
</li>
</template>
<p v-if="genreResults.length" class="uppercase text-xs text-gray-400 mb-1 mt-3 px-1 font-semibold">{{ $strings.LabelGenres }}</p>
<template v-for="item in genreResults">
<li :key="`genre.${item.name}`" class="text-gray-50 select-none relative cursor-pointer hover:bg-black-400 py-1" role="option" @click="clickOption">
<nuxt-link :to="`/library/${currentLibraryId}/bookshelf?filter=genres.${$encode(item.name)}`">
<cards-genre-search-card :genre="item.name" />
</nuxt-link>
</li>
</template>
<p v-if="narratorResults.length" class="uppercase text-xs text-gray-400 mb-1 mt-3 px-1 font-semibold">{{ $strings.LabelNarrators }}</p>
<template v-for="narrator in narratorResults">
<li :key="narrator.name" class="text-gray-50 select-none relative cursor-pointer hover:bg-black-400 py-1" role="option" @click="clickOption">
@ -95,6 +104,7 @@ export default {
authorResults: [],
seriesResults: [],
tagResults: [],
genreResults: [],
narratorResults: [],
searchTimeout: null,
lastSearch: null
@ -105,7 +115,7 @@ export default {
return this.$store.state.libraries.currentLibraryId
},
totalResults() {
return this.bookResults.length + this.seriesResults.length + this.authorResults.length + this.tagResults.length + this.podcastResults.length + this.narratorResults.length
return this.bookResults.length + this.seriesResults.length + this.authorResults.length + this.tagResults.length + this.genreResults.length + this.podcastResults.length + this.narratorResults.length
}
},
methods: {
@ -126,6 +136,7 @@ export default {
this.authorResults = []
this.seriesResults = []
this.tagResults = []
this.genreResults = []
this.narratorResults = []
this.showMenu = false
this.isFetching = false
@ -168,6 +179,7 @@ export default {
this.authorResults = searchResults.authors || []
this.seriesResults = searchResults.series || []
this.tagResults = searchResults.tags || []
this.genreResults = searchResults.genres || []
this.narratorResults = searchResults.narrators || []
this.isFetching = false

View File

@ -1079,7 +1079,7 @@ module.exports = {
// Search tags
const tagMatches = []
const [tagResults] = await Database.sequelize.query(`SELECT value, count(*) AS numItems FROM books b, libraryItems li, json_each(b.tags) WHERE json_valid(b.tags) AND json_each.value LIKE :query AND b.id = li.mediaId AND li.libraryId = :libraryId GROUP BY value LIMIT :limit OFFSET :offset;`, {
const [tagResults] = await Database.sequelize.query(`SELECT value, count(*) AS numItems FROM books b, libraryItems li, json_each(b.tags) WHERE json_valid(b.tags) AND json_each.value LIKE :query AND b.id = li.mediaId AND li.libraryId = :libraryId GROUP BY value ORDER BY numItems DESC LIMIT :limit OFFSET :offset;`, {
replacements: {
query: `%${query}%`,
libraryId: oldLibrary.id,
@ -1095,6 +1095,24 @@ module.exports = {
})
}
// Search genres
const genreMatches = []
const [genreResults] = await Database.sequelize.query(`SELECT value, count(*) AS numItems FROM books b, libraryItems li, json_each(b.genres) WHERE json_valid(b.genres) AND json_each.value LIKE :query AND b.id = li.mediaId AND li.libraryId = :libraryId GROUP BY value ORDER BY numItems DESC LIMIT :limit OFFSET :offset;`, {
replacements: {
query: `%${query}%`,
libraryId: oldLibrary.id,
limit,
offset
},
raw: true
})
for (const row of genreResults) {
genreMatches.push({
name: row.value,
numItems: row.numItems
})
}
// Search series
const allSeries = await Database.seriesModel.findAll({
where: {
@ -1140,6 +1158,7 @@ module.exports = {
book: itemMatches,
narrators: narratorMatches,
tags: tagMatches,
genres: genreMatches,
series: seriesMatches,
authors: authorMatches
}

View File

@ -1,4 +1,3 @@
const Sequelize = require('sequelize')
const Database = require('../../Database')
const Logger = require('../../Logger')
@ -23,9 +22,11 @@ module.exports = {
if (user.permissions.selectedTagsNotAccessible) {
podcastWhere.push(Sequelize.where(Sequelize.literal(`(SELECT count(*) FROM json_each(tags) WHERE json_valid(tags) AND json_each.value IN (:userTagsSelected))`), 0))
} else {
podcastWhere.push(Sequelize.where(Sequelize.literal(`(SELECT count(*) FROM json_each(tags) WHERE json_valid(tags) AND json_each.value IN (:userTagsSelected))`), {
podcastWhere.push(
Sequelize.where(Sequelize.literal(`(SELECT count(*) FROM json_each(tags) WHERE json_valid(tags) AND json_each.value IN (:userTagsSelected))`), {
[Sequelize.Op.gte]: 1
}))
})
)
}
}
return {
@ -130,7 +131,7 @@ module.exports = {
]
} else if (filterGroup === 'recent') {
libraryItemWhere['createdAt'] = {
[Sequelize.Op.gte]: new Date(new Date() - (60 * 24 * 60 * 60 * 1000)) // 60 days ago
[Sequelize.Op.gte]: new Date(new Date() - 60 * 24 * 60 * 60 * 1000) // 60 days ago
}
}
@ -154,10 +155,7 @@ module.exports = {
replacements,
distinct: true,
attributes: {
include: [
[Sequelize.literal(`(SELECT count(*) FROM podcastEpisodes pe WHERE pe.podcastId = podcast.id)`), 'numEpisodes'],
...podcastIncludes
]
include: [[Sequelize.literal(`(SELECT count(*) FROM podcastEpisodes pe WHERE pe.podcastId = podcast.id)`), 'numEpisodes'], ...podcastIncludes]
},
include: [
{
@ -251,7 +249,7 @@ module.exports = {
}
} else if (filterGroup === 'recent') {
podcastEpisodeWhere['createdAt'] = {
[Sequelize.Op.gte]: new Date(new Date() - (60 * 24 * 60 * 60 * 1000)) // 60 days ago
[Sequelize.Op.gte]: new Date(new Date() - 60 * 24 * 60 * 60 * 1000) // 60 days ago
}
}
@ -386,7 +384,7 @@ module.exports = {
// Search tags
const tagMatches = []
const [tagResults] = await Database.sequelize.query(`SELECT value, count(*) AS numItems FROM podcasts p, libraryItems li, json_each(p.tags) WHERE json_valid(p.tags) AND json_each.value LIKE :query AND p.id = li.mediaId AND li.libraryId = :libraryId GROUP BY value LIMIT :limit OFFSET :offset;`, {
const [tagResults] = await Database.sequelize.query(`SELECT value, count(*) AS numItems FROM podcasts p, libraryItems li, json_each(p.tags) WHERE json_valid(p.tags) AND json_each.value LIKE :query AND p.id = li.mediaId AND li.libraryId = :libraryId GROUP BY value ORDER BY numItems DESC LIMIT :limit OFFSET :offset;`, {
replacements: {
query: `%${query}%`,
libraryId: oldLibrary.id,
@ -402,9 +400,28 @@ module.exports = {
})
}
// Search genres
const genreMatches = []
const [genreResults] = await Database.sequelize.query(`SELECT value, count(*) AS numItems FROM podcasts p, libraryItems li, json_each(p.genres) WHERE json_valid(p.genres) AND json_each.value LIKE :query AND p.id = li.mediaId AND li.libraryId = :libraryId GROUP BY value ORDER BY numItems DESC LIMIT :limit OFFSET :offset;`, {
replacements: {
query: `%${query}%`,
libraryId: oldLibrary.id,
limit,
offset
},
raw: true
})
for (const row of genreResults) {
genreMatches.push({
name: row.value,
numItems: row.numItems
})
}
return {
podcast: itemMatches,
tags: tagMatches
tags: tagMatches,
genres: genreMatches
}
},
@ -446,9 +463,7 @@ module.exports = {
required: false
}
],
order: [
['publishedAt', 'DESC']
],
order: [['publishedAt', 'DESC']],
subQuery: false,
limit,
offset
@ -519,11 +534,7 @@ module.exports = {
*/
async getLongestPodcasts(libraryId, limit) {
const podcasts = await Database.podcastModel.findAll({
attributes: [
'id',
'title',
[Sequelize.literal(`(SELECT SUM(json_extract(pe.audioFile, '$.duration')) FROM podcastEpisodes pe WHERE pe.podcastId = podcast.id)`), 'duration']
],
attributes: ['id', 'title', [Sequelize.literal(`(SELECT SUM(json_extract(pe.audioFile, '$.duration')) FROM podcastEpisodes pe WHERE pe.podcastId = podcast.id)`), 'duration']],
include: {
model: Database.libraryItemModel,
attributes: ['id', 'libraryId'],
@ -531,12 +542,10 @@ module.exports = {
libraryId
}
},
order: [
['duration', 'DESC']
],
order: [['duration', 'DESC']],
limit
})
return podcasts.map(podcast => {
return podcasts.map((podcast) => {
return {
id: podcast.libraryItem.id,
title: podcast.title,