Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multisearch #469

Open
wants to merge 16 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@
CMakeCache.txt
CMakeFiles/
build/
.vscode/
.vscode/
cmake-build-*/
.idea/
2 changes: 2 additions & 0 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ Please refer to [Deployment](#deployment) for further configuration options.
__Note__: this installation process and the default values of the configuration files have been written for _Debian Bookworm_. Therefore, you may have to adapt commands and/or paths in order to fit to your distribution.
### Build dependencies
__Notes__:
* Wt by default uses bundled Sqlite, which does not support needed features. It will probably need to be compiled with
`-DUSE_SYSTEM_SQLITE3=ON` for cmake.
* a C++17 compiler is needed
* ffmpeg version 4 minimum is required
```sh
Expand Down
15 changes: 14 additions & 1 deletion approot/artists.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,28 @@
<div class="d-flex justify-content-between align-items-center mb-3">
${link-type class="me-1"}
<div class="d-flex">
${<if-singlesearch-enabled>}
${search class="form-control form-control-sm me-1" type="search"}
${</if-singlesearch-enabled>}
${sort-mode}
</div>
</div>
${artists}
</message>

<message id="Lms.Explore.Artists.template.entry">
${name class="text-decoration-none link-secondary"}
<div class="d-flex align-items-center mb-2">
<div class="p-1">
${cover}
</div>
<div class="row align-items-center flex-fill overflow-hidden">
<div class="col-12 col-md-6 col-lg-4">
<div class="p-2 overflow-hidden">
<div class="d-block text-truncate">${name class="text-decoration-none link-secondary"}</div>
</div>
</div>
</div>
</div>
</message>

<message id="Lms.Explore.Artists.template.container">
Expand Down
5 changes: 5 additions & 0 deletions approot/main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
</ul>
${filters class="d-flex align-items-center me-auto mb-2 mb-md-0"}
<div class="navbar-nav align-items-md-center">

${<if-multisearch-enabled>}
${multisearch class="form-control form-control-sm me-1" style="min-width: 25em;" type="search"}
${</if-multisearch-enabled>}

${<if-is-admin>}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false" title="${tr:Lms.administration}">
Expand Down
4 changes: 4 additions & 0 deletions approot/messages.xml
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,10 @@
<message id="Lms.PlayHistory.playhistory">Play History</message>

<!--Settings-->
<message id="Lms.Settings.interface">Interface</message>
<message id="Lms.Settings.interface-settings-need-refresh">These settings may require manual browser refresh!</message>
<message id="Lms.Settings.enable-multisearch">Enable global searcher (may be slow for large music collections)</message>
<message id="Lms.Settings.enable-singlesearch">Enable searchers for individual entity types</message>
<message id="Lms.Settings.audio">Audio</message>
<message id="Lms.Settings.audio-settings-are-local">These audio settings are local to your browser!</message>
<message id="Lms.Settings.backend.internal">Internal</message>
Expand Down
4 changes: 4 additions & 0 deletions approot/messages_pl.xml
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,10 @@
<message id="Lms.PlayHistory.playhistory">Historia odtwarzania</message>

<!--Settings-->
<message id="Lms.Settings.interface">Interfejs</message>
<message id="Lms.Settings.interface-settings-need-refresh">Te ustawienia mogą wymagać ręcznego odświerzania przeglądarki!</message>
<message id="Lms.Settings.enable-multisearch">Aktywuj globalną wyszukiwarkę (może być powolna w dużych kolekcjach)</message>
<message id="Lms.Settings.enable-singlesearch">Aktywuj indywidualne wyszukiwarki dla typów</message>
<message id="Lms.Settings.audio">Dźwięk</message>
<message id="Lms.Settings.audio-settings-are-local">Te ustawienia dotyczą wyłącznie tej przeglądarki!</message>
<message id="Lms.Settings.backend.internal">Wewnętrzny</message>
Expand Down
132 changes: 132 additions & 0 deletions approot/multisearch.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@

<?xml version="1.0" encoding="UTF-8" ?>
<messages xmlns:if="Wt.WTemplate.conditions">
<!--FORMS message blocks-->

<message id="Lms.Explore.Multisearch.template">
<div class="d-flex justify-content-center align-items-center mb-3">
${search-all class="btn-check"}
<label class="btn" for="${id:search-all}">${tr:Lms.Explore.all}</label>

${search-releases class="btn-check"}
<label class="btn" for="${id:search-releases}">${tr:Lms.Explore.releases}</label>

${search-artists class="btn-check"}
<label class="btn" for="${id:search-artists}">${tr:Lms.Explore.artists}</label>

${search-tracks class="btn-check"}
<label class="btn" for="${id:search-tracks}">${tr:Lms.Explore.tracks}</label>
</div>
${multisearch-results}
</message>

<message id="Lms.Explore.Multisearch.template.entry-container">
${elements class="Lms-row-container"}
${loading-indicator}
</message>

<message id="Lms.Explore.Multisearch.template.entry-track">
<div class="d-flex align-items-center mb-2">
<div class="p-1">
${cover}
</div>
<div class="p-1">
<i class="fa fa-music" aria-hidden="true"></i>
</div>
<div class="row align-items-center flex-fill overflow-hidden">
<div class="col-12 col-md-6 col-lg-4">
<div class="p-2 overflow-hidden">
<div class="d-block text-truncate">${name}</div>
${<if-has-artists>}${artists class="d-block d-md-none text-truncate"}${</if-has-artists>}
</div>
</div>
<div class="col-md-6 col-lg-4">
${<if-has-artists>}${artists-md class="d-none d-md-block text-truncate"}${</if-has-artists>}
</div>
<div class="col-lg-4 text-truncate">
${<if-has-release>}${release class="d-none d-lg-inline text-truncate text-decoration-none link-success"}${</if-has-release>}
</div>
</div>
<div class="p-2 d-none d-sm-block text-muted text-center Lms-tracks-entry-duration">
${duration}
</div>
<div class="p-2 d-flex">
${play-btn class="d-none d-sm-block btn btn-sm btn-outline-secondary border-0"}
<div class="dropdown d-inline-block">
${more-btn data-bs-toggle="dropdown" aria-expanded="false" class="btn btn-sm btn-outline-secondary border-0"}
<ul class="dropdown-menu" aria-labelledby="${id:more-btn}">
<li class="d-block d-sm-none">${play class="dropdown-item"}</li>
<li>${play-next class="dropdown-item"}</li>
<li>${play-last class="dropdown-item"}</li>
<li>${star class="dropdown-item"}</li>
<li>${download class="dropdown-item"}</li>
<li>${track-info class="dropdown-item"}</li>
</ul>
</div>
</div>
</div>
</message>


<message id="Lms.Explore.Multisearch.template.entry-release">
<div class="d-flex align-items-center mb-2">
<div class="p-1">
${cover}
</div>
<div class="p-1">
<i class="fa fa-list" aria-hidden="true"></i>
</div>
<div class="row align-items-center flex-fill overflow-hidden">
<div class="col-12 col-md-6 col-lg-4">
<div class="p-2 overflow-hidden">
<div class="d-block text-truncate">${release-name class="text-decoration-none link-success"}</div>
${<if-has-release-artists>}${artists class="d-block d-md-none text-truncate"}${</if-has-release-artists>}
</div>
</div>
<div class="col-md-6 col-lg-8">
${<if-has-release-artists>}${artists-md class="d-none d-md-block text-truncate"}${</if-has-release-artists>}
</div>
</div>
<div class="p-2 d-none d-sm-block text-muted text-center Lms-tracks-entry-duration">
${duration}
</div>
<div class="p-2 d-flex">
${play-btn class="d-none d-sm-block btn btn-sm btn-outline-secondary border-0"}
<div class="dropdown d-inline-block">
${more-btn data-bs-toggle="dropdown" aria-expanded="false" class="btn btn-sm btn-outline-secondary border-0"}
<ul class="dropdown-menu" aria-labelledby="${id:more-btn}">
<li class="d-block d-sm-none">${play class="dropdown-item"}</li>
<li>${play-next class="dropdown-item"}</li>
<li>${play-last class="dropdown-item"}</li>
<li>${play-shuffled class="dropdown-item"}</li>
<li>${star class="dropdown-item"}</li>
${<if-has-mbid>}
<li><a href="${mbid-link}" target="_blank" class="dropdown-item">${tr:Lms.Explore.musicbrainz-release}</a></li>
${</if-has-mbid>}
<li>${download class="dropdown-item"}</li>
<li>${release-info class="dropdown-item"}</li>
</ul>
</div>
</div>
</div>
</message>

<message id="Lms.Explore.Multisearch.template.entry-artist">
<div class="d-flex align-items-center mb-2">
<div class="p-1">
${cover}
</div>
<div class="p-1">
<i class="fa fa-user" aria-hidden="true"></i>
</div>
<div class="row align-items-center flex-fill overflow-hidden">
<div class="col-12 col-md-6 col-lg-4">
<div class="p-2 overflow-hidden">
<div class="d-block text-truncate">${name class="text-decoration-none link-success"}</div>
</div>
</div>
</div>
</div>
</message>

</messages>
4 changes: 3 additions & 1 deletion approot/releases.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
</ul>
</div>
<div class="d-flex">
${<if-singlesearch-enabled>}
${search class="form-control form-control-sm me-1" type="search"}
${</if-singlesearch-enabled>}
${sort-mode}
</div>
</div>
Expand All @@ -28,7 +30,7 @@
${cover class="shadow-sm"}
</div>
${release-name class="d-block text-truncate text-nowrap text-decoration-none link-success"}
${<if-has-artist>}${artist-name class="d-block text-truncate text-nowrap"}${</if-has-artist>}
${<if-has-artist>}${artists class="d-block text-truncate text-nowrap"}${</if-has-artist>}
${<if-has-year>}<div class="small text-muted">${year}</div>${</if-has-year>}
</div>
</div>
Expand Down
22 changes: 22 additions & 0 deletions approot/settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,28 @@

<message id="Lms.Settings.template">
<form class="row g-3">
<legend>${tr:Lms.Settings.interface}</legend>
<div class="alert alert-info">
${tr:Lms.Settings.interface-settings-need-refresh}
</div>
<div class="form-check">
${interface-enable-multisearch class="form-check-input"}
<label class="form-check-label" for="${id:interface-enable-multisearch}">
${tr:Lms.Settings.enable-multisearch}
</label>
<div class="invalid-feedback">
${interface-enable-multisearch-info}
</div>
</div>
<div class="form-check">
${interface-enable-singlesearch class="form-check-input"}
<label class="form-check-label" for="${id:interface-enable-singlesearch}">
${tr:Lms.Settings.enable-singlesearch}
</label>
<div class="invalid-feedback">
${interface-enable-singlesearch-info}
</div>
</div>
<legend>${tr:Lms.Settings.audio}</legend>
<div class="alert alert-info">
${tr:Lms.Settings.audio-settings-are-local}
Expand Down
2 changes: 2 additions & 0 deletions approot/tracks.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
</ul>
</div>
<div class="d-flex">
${<if-singlesearch-enabled>}
${search class="form-control form-control-sm me-1" type="search"}
${</if-singlesearch-enabled>}
${sort-mode}
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/libs/av/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pkg_check_modules(LIBAV IMPORTED_TARGET libavcodec libavutil libavformat)
pkg_check_modules(LIBAV REQUIRED IMPORTED_TARGET libavcodec libavutil libavformat)

add_library(lmsav SHARED
impl/AudioFile.cpp
Expand Down
1 change: 1 addition & 0 deletions src/libs/database/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
add_library(lmsdatabase SHARED
impl/AnyMedium.cpp
impl/Artist.cpp
impl/AuthToken.cpp
impl/Cluster.cpp
Expand Down
102 changes: 102 additions & 0 deletions src/libs/database/impl/AnyMedium.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#include "database/AnyMedium.hpp"

#include <ostream>
#include <tuple>

#include <database/Session.hpp>

#include "Utils.hpp"

using namespace lms::db;

AnyMediumId any_medium::fromString(const std::string& type, const Wt::Dbo::dbo_default_traits::IdType id)
{
if (type == "artist")
return ArtistId(id);
if (type == "release")
return ReleaseId(id);
if (type == "track")
return TrackId(id);

throw std::logic_error("unknown medium type");
}

RangeResults<AnyMediumId> any_medium::findIds(Session& session, Type type, const std::vector<std::string_view>& keywords,
std::span<const ClusterId> clusters, MediaLibraryId mediaLibrary,
std::optional<Range> range)
{
using Columns = std::tuple<std::string, Wt::Dbo::dbo_default_traits::IdType, int>;

session.checkReadTransaction();

auto media_library_query = session.getDboSession()->query<Wt::Dbo::dbo_default_traits::IdType>("SELECT json_each.value FROM json_each(media_library_ids)").where("json_each.value = ?").bind(mediaLibrary.getValue());

auto cluster_query = session.getDboSession()->query<Wt::Dbo::dbo_default_traits::IdType>("SELECT json_each.value FROM json_each(cluster_ids)");
for (auto cluster_id : clusters)
cluster_query.orWhere("json_each.value = ?").bind(cluster_id.getValue());

auto query = session.getDboSession()->query<Columns>(R"(
SELECT type, id, sum(weight) AS v
FROM keywords
)");

for (const std::string_view keyword : keywords)
{
query.orWhere("value LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + utils::escapeLikeKeyword(keyword) + "%");
}

if (mediaLibrary != MediaLibraryId{})
query.where("EXISTS(" + media_library_query.asString() + ")").bindSubqueryValues(media_library_query);

if (!clusters.empty())
query.where("EXISTS(" + cluster_query.asString() + ")").bindSubqueryValues(cluster_query);

switch (type)
{
case Type::ALL:
break;
case Type::RELEASES:
query.where("type = 'release'");
break;
case Type::ARTISTS:
query.where("type = 'artist'");
break;
case Type::TRACKS:
query.where("type = 'track'");
break;
}

query
.groupBy("type, id")
.orderBy("v DESC");

auto columns = utils::execRangeQuery<Columns>(query, range);

auto results = std::vector<AnyMediumId>();
results.reserve(columns.results.size());
for (const auto& [type, id, _] : columns.results)
results.emplace_back(fromString(type, id));

return {
columns.range,
results,
columns.moreResults
};
}

std::ostream& lms::db::operator<<(std::ostream& os, const AnyMediumId& v)
{
std::visit([&os](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, ArtistId>)
os << "Artist(" << arg.getValue() << ")";
else if constexpr (std::is_same_v<T, ReleaseId>)
os << "Release(" << arg.getValue() << ")";
else if constexpr (std::is_same_v<T, TrackId>)
os << "Track(" << arg.getValue() << ")";
else
static_assert(false, "inexhaustible patterns");
},
v);
return os;
}
2 changes: 1 addition & 1 deletion src/libs/database/impl/IdTypeTraits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

#include <Wt/Dbo/StdSqlTraits.h>

#include "database/Types.hpp"
#include "database/IdType.hpp"

namespace Wt::Dbo
{
Expand Down
Loading
Loading