Merge branch 'develop' for release v3.68.0
This commit is contained in:
@@ -17,4 +17,6 @@ CheckOptions:
|
|||||||
value: '1'
|
value: '1'
|
||||||
- key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
|
- key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
|
||||||
value: '1'
|
value: '1'
|
||||||
|
- key: readability-function-cognitive-complexity.IgnoreMacros
|
||||||
|
value: '1'
|
||||||
...
|
...
|
||||||
|
|||||||
+4
-4
@@ -1,4 +1,4 @@
|
|||||||
FROM alpine:3.21.3 AS build
|
FROM alpine:3.22 AS build
|
||||||
|
|
||||||
WORKDIR /tmp/workdir
|
WORKDIR /tmp/workdir
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ RUN \
|
|||||||
make -j$(nproc) install
|
make -j$(nproc) install
|
||||||
|
|
||||||
# WT
|
# WT
|
||||||
ARG WT_VERSION=4.11.3
|
ARG WT_VERSION=4.11.4
|
||||||
ARG WT_DEBUG=OFF
|
ARG WT_DEBUG=OFF
|
||||||
RUN \
|
RUN \
|
||||||
DIR=/tmp/wt && mkdir -p ${DIR} && cd ${DIR} && \
|
DIR=/tmp/wt && mkdir -p ${DIR} && cd ${DIR} && \
|
||||||
@@ -101,7 +101,7 @@ RUN \
|
|||||||
cp ./*.h ${PREFIX}/include/stb
|
cp ./*.h ${PREFIX}/include/stb
|
||||||
|
|
||||||
# TAGLIB
|
# TAGLIB
|
||||||
ARG TAGLIB_VERSION=v2.0.2
|
ARG TAGLIB_VERSION=v2.1.1
|
||||||
RUN \
|
RUN \
|
||||||
DIR=/tmp/taglib && mkdir -p ${DIR} && cd ${DIR} && \
|
DIR=/tmp/taglib && mkdir -p ${DIR} && cd ${DIR} && \
|
||||||
curl -sLO https://github.com/taglib/taglib/archive/${TAGLIB_VERSION}.tar.gz && \
|
curl -sLO https://github.com/taglib/taglib/archive/${TAGLIB_VERSION}.tar.gz && \
|
||||||
@@ -145,7 +145,7 @@ RUN \
|
|||||||
rm -rf /tmp/fakeroot/share/Wt/resources/themes
|
rm -rf /tmp/fakeroot/share/Wt/resources/themes
|
||||||
|
|
||||||
## Release Stage
|
## Release Stage
|
||||||
FROM alpine:3.21.3 AS release
|
FROM alpine:3.22 AS release
|
||||||
LABEL maintainer="Emeric Poupon <itmfr@yahoo.fr>"
|
LABEL maintainer="Emeric Poupon <itmfr@yahoo.fr>"
|
||||||
|
|
||||||
ARG RUNTIME_PACKAGES=" \
|
ARG RUNTIME_PACKAGES=" \
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<messages xmlns:if="Wt.WTemplate.conditions">
|
||||||
|
|
||||||
|
<message id="Lms.Admin.DebugTools.Db.template">
|
||||||
|
<form>
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-12">
|
||||||
|
${export-query-plans-btn class="btn btn-primary"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</message>
|
||||||
|
|
||||||
|
</messages>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<messages xmlns:if="Wt.WTemplate.conditions">
|
||||||
|
|
||||||
|
<message id="Lms.Admin.DebugTools.template">
|
||||||
|
<form>
|
||||||
|
<legend>${tr:Lms.Admin.DebugTools.Tracing.tracing}</legend>
|
||||||
|
${tracing}
|
||||||
|
<hr/>
|
||||||
|
<legend>${tr:Lms.Admin.DebugTools.Db.db}</legend>
|
||||||
|
${db}
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</message>
|
||||||
|
|
||||||
|
</messages>
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<messages xmlns:if="Wt.WTemplate.conditions">
|
<messages xmlns:if="Wt.WTemplate.conditions">
|
||||||
|
|
||||||
<message id="Lms.Admin.Tracing.template">
|
<message id="Lms.Admin.DebugTools.Tracing.template">
|
||||||
<form>
|
<form>
|
||||||
<legend>${tr:Lms.Admin.Tracing.tracing}</legend>
|
|
||||||
<div class="row g-3">
|
<div class="row g-3">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
${export-btn class="btn btn-primary"}
|
${export-btn class="btn btn-primary"}
|
||||||
|
|||||||
+4
-1
@@ -44,7 +44,10 @@
|
|||||||
<li>${scan-settings class="dropdown-item"}</li>
|
<li>${scan-settings class="dropdown-item"}</li>
|
||||||
<li>${scanner class="dropdown-item"}</li>
|
<li>${scanner class="dropdown-item"}</li>
|
||||||
<li>${users class="dropdown-item"}</li>
|
<li>${users class="dropdown-item"}</li>
|
||||||
<li>${tracing class="dropdown-item"}</li>
|
${<if-debug-tools>}
|
||||||
|
<li class="dropdown-divider"></li>
|
||||||
|
<li>${debug-tools class="dropdown-item"}</li>
|
||||||
|
${</if-debug-tools>}
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
${</if-is-admin>}
|
${</if-is-admin>}
|
||||||
|
|||||||
+11
-4
@@ -52,10 +52,10 @@
|
|||||||
<message id="Lms.Error.user-not-found">User not found</message>
|
<message id="Lms.Error.user-not-found">User not found</message>
|
||||||
|
|
||||||
<!--Administration-->
|
<!--Administration-->
|
||||||
|
<message id="Lms.Admin.menu-debug-tools"><i class="fa fa-fw fa-bug" aria-hidden="true"></i> Debug tools</message>
|
||||||
<message id="Lms.Admin.menu-media-libraries"><i class="fa fa-fw fa-database" aria-hidden="true"></i> Libraries</message>
|
<message id="Lms.Admin.menu-media-libraries"><i class="fa fa-fw fa-database" aria-hidden="true"></i> Libraries</message>
|
||||||
<message id="Lms.Admin.menu-scan-settings"><i class="fa fa-fw fa-cogs" aria-hidden="true"></i> Scan settings</message>
|
<message id="Lms.Admin.menu-scan-settings"><i class="fa fa-fw fa-cogs" aria-hidden="true"></i> Scan settings</message>
|
||||||
<message id="Lms.Admin.menu-scanner"><i class="fa fa-fw fa-wrench" aria-hidden="true"></i> Scanner</message>
|
<message id="Lms.Admin.menu-scanner"><i class="fa fa-fw fa-wrench" aria-hidden="true"></i> Scanner</message>
|
||||||
<message id="Lms.Admin.menu-tracing"><i class="fa fa-fw fa-bar-chart" aria-hidden="true"></i> Tracing</message>
|
|
||||||
<message id="Lms.Admin.menu-users"><i class="fa fa-fw fa-users" aria-hidden="true"></i> Users</message>
|
<message id="Lms.Admin.menu-users"><i class="fa fa-fw fa-users" aria-hidden="true"></i> Users</message>
|
||||||
|
|
||||||
<!--MediaLibraries-->
|
<!--MediaLibraries-->
|
||||||
@@ -145,13 +145,20 @@
|
|||||||
<message id="Lms.Admin.ScannerController.step-reconciliate-artists">Reconciliating artists: {1} entries...</message>
|
<message id="Lms.Admin.ScannerController.step-reconciliate-artists">Reconciliating artists: {1} entries...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-reloading-similarity-engine">Reloading similarity engine: {1}%...</message>
|
<message id="Lms.Admin.ScannerController.step-reloading-similarity-engine">Reloading similarity engine: {1}%...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-removing-orphaned-entries">Removing orphaned entries: {1} entries...</message>
|
<message id="Lms.Admin.ScannerController.step-removing-orphaned-entries">Removing orphaned entries: {1} entries...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-scanning-files">Scanning files: {1}/{2} ({3}%)...</message>
|
<message id="Lms.Admin.ScannerController.step-scanning-files">Library analysis: {1} files...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-status">Step status</message>
|
<message id="Lms.Admin.ScannerController.step-status">Step status</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-updating-library-fields">Updating library fields: {1} entries</message>
|
<message id="Lms.Admin.ScannerController.step-updating-library-fields">Updating library fields: {1} entries</message>
|
||||||
|
|
||||||
|
<!--Debug Tools-->
|
||||||
|
<message id="Lms.Admin.DebugTools.debug-tools">Debug tools</message>
|
||||||
|
|
||||||
|
<!--Db-->
|
||||||
|
<message id="Lms.Admin.DebugTools.Db.export-query-plans">Export query plans</message>
|
||||||
|
<message id="Lms.Admin.DebugTools.Db.db">Database</message>
|
||||||
|
|
||||||
<!--Tracing-->
|
<!--Tracing-->
|
||||||
<message id="Lms.Admin.Tracing.export-current-buffer">Export traces</message>
|
<message id="Lms.Admin.DebugTools.Tracing.export-current-buffer">Export traces</message>
|
||||||
<message id="Lms.Admin.Tracing.tracing">Tracing</message>
|
<message id="Lms.Admin.DebugTools.Tracing.tracing">Tracing</message>
|
||||||
|
|
||||||
<!--Users-->
|
<!--Users-->
|
||||||
<message id="Lms.Admin.Users.add">New user</message>
|
<message id="Lms.Admin.Users.add">New user</message>
|
||||||
|
|||||||
+11
-5
@@ -52,10 +52,10 @@
|
|||||||
<message id="Lms.Error.user-not-found">No existe el usuario</message>
|
<message id="Lms.Error.user-not-found">No existe el usuario</message>
|
||||||
|
|
||||||
<!--Administration-->
|
<!--Administration-->
|
||||||
|
<message id="Lms.Admin.menu-debug-tools"><i class="fa fa-fw fa-bug" aria-hidden="true"></i> Herramientas de depuración</message>
|
||||||
<message id="Lms.Admin.menu-media-libraries"><i class="fa fa-fw fa-database" aria-hidden="true"></i> Bibliotecas</message>
|
<message id="Lms.Admin.menu-media-libraries"><i class="fa fa-fw fa-database" aria-hidden="true"></i> Bibliotecas</message>
|
||||||
<message id="Lms.Admin.menu-scan-settings"><i class="fa fa-fw fa-cogs" aria-hidden="true"></i> Opciones de escaneo</message>
|
<message id="Lms.Admin.menu-scan-settings"><i class="fa fa-fw fa-cogs" aria-hidden="true"></i> Opciones de escaneo</message>
|
||||||
<message id="Lms.Admin.menu-scanner"><i class="fa fa-fw fa-wrench" aria-hidden="true"></i> Escanear</message>
|
<message id="Lms.Admin.menu-scanner"><i class="fa fa-fw fa-wrench" aria-hidden="true"></i> Escanear</message>
|
||||||
<message id="Lms.Admin.menu-tracing"><i class="fa fa-fw fa-bar-chart" aria-hidden="true"></i> Traces</message>
|
|
||||||
<message id="Lms.Admin.menu-users"><i class="fa fa-fw fa-users" aria-hidden="true"></i> Usuarios</message>
|
<message id="Lms.Admin.menu-users"><i class="fa fa-fw fa-users" aria-hidden="true"></i> Usuarios</message>
|
||||||
|
|
||||||
<!--MediaLibraries-->
|
<!--MediaLibraries-->
|
||||||
@@ -139,19 +139,25 @@
|
|||||||
<message id="Lms.Admin.ScannerController.step-checking-for-removed-files">Comprobando ficheros eliminados... {1}%</message>
|
<message id="Lms.Admin.ScannerController.step-checking-for-removed-files">Comprobando ficheros eliminados... {1}%</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-compact">Compactando la base de datos...</message>
|
<message id="Lms.Admin.ScannerController.step-compact">Compactando la base de datos...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-compute-cluster-stats">Calculando estadísticas... {1}%</message>
|
<message id="Lms.Admin.ScannerController.step-compute-cluster-stats">Calculando estadísticas... {1}%</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-discovering-files">Descubriendo ficheros: {1} ficheros</message>
|
|
||||||
<message id="Lms.Admin.ScannerController.step-fetching-track-features">Accediendo a las características de la pista desde AcousticBrainz : {1}/{2} ficheros ({3}%)...</message>
|
<message id="Lms.Admin.ScannerController.step-fetching-track-features">Accediendo a las características de la pista desde AcousticBrainz : {1}/{2} ficheros ({3}%)...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-optimize">Optimizando la base de datos... {1}%...</message>
|
<message id="Lms.Admin.ScannerController.step-optimize">Optimizando la base de datos... {1}%...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-reconciliate-artists">Reconciliando artistas: {1} entradas...</message>
|
<message id="Lms.Admin.ScannerController.step-reconciliate-artists">Reconciliando artistas: {1} entradas...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-reloading-similarity-engine">Recargando el motor de similitud: {1}%...</message>
|
<message id="Lms.Admin.ScannerController.step-reloading-similarity-engine">Recargando el motor de similitud: {1}%...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-removing-orphaned-entries">Borrando entradas huérfanas: {1} entradas...</message>
|
<message id="Lms.Admin.ScannerController.step-removing-orphaned-entries">Borrando entradas huérfanas: {1} entradas...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-scanning-files">Escaneando ficheros: {1} / {2} ({3}%)...</message>
|
<message id="Lms.Admin.ScannerController.step-scanning-files">Análisis de bibliotecas: {1} ficheros...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-status">Estado de las etapas</message>
|
<message id="Lms.Admin.ScannerController.step-status">Estado de las etapas</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-updating-library-fields">Actualizando campos de la biblioteca: {1} entradas</message>
|
<message id="Lms.Admin.ScannerController.step-updating-library-fields">Actualizando campos de la biblioteca: {1} entradas</message>
|
||||||
|
|
||||||
|
<!--Debug Tools-->
|
||||||
|
<message id="Lms.Admin.DebugTools.debug-tools">Herramientas de depuración</message>
|
||||||
|
|
||||||
|
<!--Db-->
|
||||||
|
<message id="Lms.Admin.DebugTools.Db.export-query-plans">Exportar planes de consulta</message>
|
||||||
|
<message id="Lms.Admin.DebugTools.Db.db">Base de datos</message>
|
||||||
|
|
||||||
<!--Tracing-->
|
<!--Tracing-->
|
||||||
<message id="Lms.Admin.Tracing.export-current-buffer">Exportar las trazas</message>
|
<message id="Lms.Admin.DebugTools.Tracing.export-current-buffer">Exportar las trazas</message>
|
||||||
<message id="Lms.Admin.Tracing.tracing">Trazas</message>
|
<message id="Lms.Admin.DebugTools.Tracing.tracing">Trazas</message>
|
||||||
|
|
||||||
<!--Users-->
|
<!--Users-->
|
||||||
<message id="Lms.Admin.Users.add">Añadir</message>
|
<message id="Lms.Admin.Users.add">Añadir</message>
|
||||||
|
|||||||
+12
-6
@@ -52,10 +52,10 @@
|
|||||||
<message id="Lms.Error.user-not-found">L'utilisateur n'existe pas</message>
|
<message id="Lms.Error.user-not-found">L'utilisateur n'existe pas</message>
|
||||||
|
|
||||||
<!--Administration-->
|
<!--Administration-->
|
||||||
|
<message id="Lms.Admin.menu-debug-tools"><i class="fa fa-fw fa-bug" aria-hidden="true"></i> Outils de débogage</message>
|
||||||
<message id="Lms.Admin.menu-media-libraries"><i class="fa fa-fw fa-database" aria-hidden="true"></i> Bibliothèques</message>
|
<message id="Lms.Admin.menu-media-libraries"><i class="fa fa-fw fa-database" aria-hidden="true"></i> Bibliothèques</message>
|
||||||
<message id="Lms.Admin.menu-scan-settings"><i class="fa fa-fw fa-cogs" aria-hidden="true"></i> Paramètres du scan</message>
|
<message id="Lms.Admin.menu-scan-settings"><i class="fa fa-fw fa-cogs" aria-hidden="true"></i> Paramètres du scan</message>
|
||||||
<message id="Lms.Admin.menu-scanner"><i class="fa fa-fw fa-wrench" aria-hidden="true"></i> Scanner</message>
|
<message id="Lms.Admin.menu-scanner"><i class="fa fa-fw fa-wrench" aria-hidden="true"></i> Scanner</message>
|
||||||
<message id="Lms.Admin.menu-tracing"><i class="fa fa-fw fa-bar-chart" aria-hidden="true"></i> Traces</message>
|
|
||||||
<message id="Lms.Admin.menu-users"><i class="fa fa-fw fa-users" aria-hidden="true"></i> Utilisateurs</message>
|
<message id="Lms.Admin.menu-users"><i class="fa fa-fw fa-users" aria-hidden="true"></i> Utilisateurs</message>
|
||||||
|
|
||||||
<!--MediaLibraries-->
|
<!--MediaLibraries-->
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
|
|
||||||
<!--Scan settings-->
|
<!--Scan settings-->
|
||||||
<message id="Lms.Admin.Database.allow-mbid-artist-merge">Permettre la fusion des artistes sans MBID avec ceux qui en ont un</message>
|
<message id="Lms.Admin.Database.allow-mbid-artist-merge">Permettre la fusion des artistes sans MBID avec ceux qui en ont un</message>
|
||||||
<message id="Lms.Admin.Database.artist-image-fallback-to-release">Utilizza un'immagine dell'album se non è disponibile un'immagine dell'artista</message>
|
<message id="Lms.Admin.Database.artist-image-fallback-to-release">Utiliser une image d'album si aucune image de l'artiste n'est disponible</message>
|
||||||
<message id="Lms.Admin.Database.artist-tag-delimiters">Délimiteurs à utiliser pour séparer les tags d'artistes</message>
|
<message id="Lms.Admin.Database.artist-tag-delimiters">Délimiteurs à utiliser pour séparer les tags d'artistes</message>
|
||||||
<message id="Lms.Admin.Database.artists-to-not-split">Artistes à ne pas séparer en utilisant les délimiteurs (un artiste par ligne)</message>
|
<message id="Lms.Admin.Database.artists-to-not-split">Artistes à ne pas séparer en utilisant les délimiteurs (un artiste par ligne)</message>
|
||||||
<message id="Lms.Admin.Database.daily">Tous les jours</message>
|
<message id="Lms.Admin.Database.daily">Tous les jours</message>
|
||||||
@@ -139,19 +139,25 @@
|
|||||||
<message id="Lms.Admin.ScannerController.step-checking-for-removed-files">Vérification des fichiers supprimés... {1}%</message>
|
<message id="Lms.Admin.ScannerController.step-checking-for-removed-files">Vérification des fichiers supprimés... {1}%</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-compact">Compactage de la base de données...</message>
|
<message id="Lms.Admin.ScannerController.step-compact">Compactage de la base de données...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-compute-cluster-stats">Calcul des statistiques... {1}%</message>
|
<message id="Lms.Admin.ScannerController.step-compute-cluster-stats">Calcul des statistiques... {1}%</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-discovering-files">Découverte des fichiers : {1} fichiers</message>
|
|
||||||
<message id="Lms.Admin.ScannerController.step-fetching-track-features">Récupération des métadonnées AcousticBrainz : {1}/{2} fichiers ({3}%)...</message>
|
<message id="Lms.Admin.ScannerController.step-fetching-track-features">Récupération des métadonnées AcousticBrainz : {1}/{2} fichiers ({3}%)...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-optimize">Optimisation de la base de données... {1}%...</message>
|
<message id="Lms.Admin.ScannerController.step-optimize">Optimisation de la base de données... {1}%...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-reconciliate-artists">Reconciliation des artistes: {1} entrées...</message>
|
<message id="Lms.Admin.ScannerController.step-reconciliate-artists">Reconciliation des artistes: {1} entrées...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-reloading-similarity-engine">Rechargement du moteur de recommandation : {1}%...</message>
|
<message id="Lms.Admin.ScannerController.step-reloading-similarity-engine">Rechargement du moteur de recommandation : {1}%...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-removing-orphaned-entries">Retrait des entrées orphelines: {1} entrées...</message>
|
<message id="Lms.Admin.ScannerController.step-removing-orphaned-entries">Retrait des entrées orphelines: {1} entrées...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-scanning-files">Scan des fichiers : {1}/{2} ({3}%)...</message>
|
<message id="Lms.Admin.ScannerController.step-scanning-files">Analyse des bibliothèques : {1} fichiers...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-status">Statut de l'étape</message>
|
<message id="Lms.Admin.ScannerController.step-status">Statut de l'étape</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-updating-library-fields">Mise à jour des champs des bibliothèques: {1} entrées</message>
|
<message id="Lms.Admin.ScannerController.step-updating-library-fields">Mise à jour des champs des bibliothèques: {1} entrées</message>
|
||||||
|
|
||||||
|
<!--Debug Tools-->
|
||||||
|
<message id="Lms.Admin.DebugTools.debug-tools">Outils de débogage</message>
|
||||||
|
|
||||||
|
<!--Db-->
|
||||||
|
<message id="Lms.Admin.DebugTools.Db.export-query-plans">Exporter les plans de requête</message>
|
||||||
|
<message id="Lms.Admin.DebugTools.Db.db">Base de données</message>
|
||||||
|
|
||||||
<!--Tracing-->
|
<!--Tracing-->
|
||||||
<message id="Lms.Admin.Tracing.export-current-buffer">Exporter les traces</message>
|
<message id="Lms.Admin.DebugTools.Tracing.export-current-buffer">Exporter les traces</message>
|
||||||
<message id="Lms.Admin.Tracing.tracing">Traces</message>
|
<message id="Lms.Admin.DebugTools.Tracing.tracing">Traces</message>
|
||||||
|
|
||||||
<!--Users-->
|
<!--Users-->
|
||||||
<message id="Lms.Admin.Users.add">Ajouter</message>
|
<message id="Lms.Admin.Users.add">Ajouter</message>
|
||||||
|
|||||||
+12
-6
@@ -52,10 +52,10 @@
|
|||||||
<message id="Lms.Error.user-not-found">Utente non trovato</message>
|
<message id="Lms.Error.user-not-found">Utente non trovato</message>
|
||||||
|
|
||||||
<!--Administration-->
|
<!--Administration-->
|
||||||
|
<message id="Lms.Admin.menu-debug-tools"><i class="fa fa-fw fa-bug" aria-hidden="true"></i> Strumenti di debug</message>
|
||||||
<message id="Lms.Admin.menu-media-libraries"><i class="fa fa-fw fa-database" aria-hidden="true"></i> Librerie</message>
|
<message id="Lms.Admin.menu-media-libraries"><i class="fa fa-fw fa-database" aria-hidden="true"></i> Librerie</message>
|
||||||
<message id="Lms.Admin.menu-scan-settings"><i class="fa fa-fw fa-cogs" aria-hidden="true"></i> Impostazioni di scansione</message>
|
<message id="Lms.Admin.menu-scan-settings"><i class="fa fa-fw fa-cogs" aria-hidden="true"></i> Impostazioni di scansione</message>
|
||||||
<message id="Lms.Admin.menu-scanner"><i class="fa fa-fw fa-wrench" aria-hidden="true"></i> Scanner</message>
|
<message id="Lms.Admin.menu-scanner"><i class="fa fa-fw fa-wrench" aria-hidden="true"></i> Scanner</message>
|
||||||
<message id="Lms.Admin.menu-tracing"><i class="fa fa-fw fa-bar-chart" aria-hidden="true"></i> Tracing</message>
|
|
||||||
<message id="Lms.Admin.menu-users"><i class="fa fa-fw fa-users" aria-hidden="true"></i> Utenti</message>
|
<message id="Lms.Admin.menu-users"><i class="fa fa-fw fa-users" aria-hidden="true"></i> Utenti</message>
|
||||||
|
|
||||||
<!--MediaLibraries-->
|
<!--MediaLibraries-->
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
|
|
||||||
<!--Scan settings-->
|
<!--Scan settings-->
|
||||||
<message id="Lms.Admin.Database.allow-mbid-artist-merge">Consentire la fusione degli artisti senza MBID con quelli che ne hanno uno</message>
|
<message id="Lms.Admin.Database.allow-mbid-artist-merge">Consentire la fusione degli artisti senza MBID con quelli che ne hanno uno</message>
|
||||||
<message id="Lms.Admin.Database.artist-image-fallback-to-release">Usa l'immagine dell'album se non è disponibile un'immagine dell'artista</message>
|
<message id="Lms.Admin.Database.artist-image-fallback-to-release">Utilizza un'immagine dell'album se non è disponibile un'immagine dell'artista</message>
|
||||||
<message id="Lms.Admin.Database.artist-tag-delimiters">Delimitatori da utilizzare per separare i tag degli artisti</message>
|
<message id="Lms.Admin.Database.artist-tag-delimiters">Delimitatori da utilizzare per separare i tag degli artisti</message>
|
||||||
<message id="Lms.Admin.Database.artists-to-not-split">Artisti da non suddividere utilizzando i delimitatori (un artista per riga)</message>
|
<message id="Lms.Admin.Database.artists-to-not-split">Artisti da non suddividere utilizzando i delimitatori (un artista per riga)</message>
|
||||||
<message id="Lms.Admin.Database.daily">Giornaliera</message>
|
<message id="Lms.Admin.Database.daily">Giornaliera</message>
|
||||||
@@ -139,19 +139,25 @@
|
|||||||
<message id="Lms.Admin.ScannerController.step-checking-for-removed-files">Controllo file... {1}%</message>
|
<message id="Lms.Admin.ScannerController.step-checking-for-removed-files">Controllo file... {1}%</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-compact">Compattazione del database...</message>
|
<message id="Lms.Admin.ScannerController.step-compact">Compattazione del database...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-compute-cluster-stats">Calcolo statistiche... {1}%</message>
|
<message id="Lms.Admin.ScannerController.step-compute-cluster-stats">Calcolo statistiche... {1}%</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-discovering-files">File trovati: {1} files</message>
|
|
||||||
<message id="Lms.Admin.ScannerController.step-fetching-track-features">Recupero metadati da AcousticBrainz: {1}/{2} tracce ({3}%)...</message>
|
<message id="Lms.Admin.ScannerController.step-fetching-track-features">Recupero metadati da AcousticBrainz: {1}/{2} tracce ({3}%)...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-optimize">Ottimizzazione del database... {1}%...</message>
|
<message id="Lms.Admin.ScannerController.step-optimize">Ottimizzazione del database... {1}%...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-reconciliate-artists">Riconciliazione artisti: {1} voci...</message>
|
<message id="Lms.Admin.ScannerController.step-reconciliate-artists">Riconciliazione artisti: {1} voci...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-reloading-similarity-engine">Ricarica motore di tracce simili: {1}%...</message>
|
<message id="Lms.Admin.ScannerController.step-reloading-similarity-engine">Ricarica motore di tracce simili: {1}%...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-removing-orphaned-entries">Rimozione voci orfane: {1} voci...</message>
|
<message id="Lms.Admin.ScannerController.step-removing-orphaned-entries">Rimozione voci orfane: {1} voci...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-scanning-files">Scansione dei file: {1}/{2} ({3}%)...</message>
|
<message id="Lms.Admin.ScannerController.step-scanning-files">Analisi delle librerie: {1} file...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-status">Stato passo</message>
|
<message id="Lms.Admin.ScannerController.step-status">Stato passo</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-updating-library-fields">Aggiornamento dei campi della libreria: {1} voci</message>
|
<message id="Lms.Admin.ScannerController.step-updating-library-fields">Aggiornamento dei campi della libreria: {1} voci</message>
|
||||||
|
|
||||||
|
<!--Debug Tools-->
|
||||||
|
<message id="Lms.Admin.DebugTools.debug-tools">Strumenti di debug</message>
|
||||||
|
|
||||||
|
<!--Db-->
|
||||||
|
<message id="Lms.Admin.DebugTools.Db.export-query-plans">Esporta i piani di query</message>
|
||||||
|
<message id="Lms.Admin.DebugTools.Db.db">Database</message>
|
||||||
|
|
||||||
<!--Tracing-->
|
<!--Tracing-->
|
||||||
<message id="Lms.Admin.Tracing.export-current-buffer">Esporta tracce</message>
|
<message id="Lms.Admin.DebugTools.Tracing.export-current-buffer">Esporta tracce</message>
|
||||||
<message id="Lms.Admin.Tracing.tracing">Tracing</message>
|
<message id="Lms.Admin.DebugTools.Tracing.tracing">Tracing</message>
|
||||||
|
|
||||||
<!--Users-->
|
<!--Users-->
|
||||||
<message id="Lms.Admin.Users.add">Nuovo utente</message>
|
<message id="Lms.Admin.Users.add">Nuovo utente</message>
|
||||||
|
|||||||
+15
-9
@@ -53,10 +53,10 @@
|
|||||||
<message id="Lms.Error.user-not-found">Użytkownik nie odnaleziony</message>
|
<message id="Lms.Error.user-not-found">Użytkownik nie odnaleziony</message>
|
||||||
|
|
||||||
<!--Administration-->
|
<!--Administration-->
|
||||||
|
<message id="Lms.Admin.menu-debug-tools"><i class="fa fa-fw fa-bug" aria-hidden="true"></i> Narzędzia debugowania</message>
|
||||||
<message id="Lms.Admin.menu-media-libraries"><i class="fa fa-fw fa-database" aria-hidden="true"></i> Biblioteki</message>
|
<message id="Lms.Admin.menu-media-libraries"><i class="fa fa-fw fa-database" aria-hidden="true"></i> Biblioteki</message>
|
||||||
<message id="Lms.Admin.menu-scan-settings"><i class="fa fa-fw fa-cogs" aria-hidden="true"></i> Ustawienia skanowania</message>
|
<message id="Lms.Admin.menu-scan-settings"><i class="fa fa-fw fa-cogs" aria-hidden="true"></i> Ustawienia skanowania</message>
|
||||||
<message id="Lms.Admin.menu-scanner"><i class="fa fa-fw fa-wrench" aria-hidden="true"></i> Skaner</message>
|
<message id="Lms.Admin.menu-scanner"><i class="fa fa-fw fa-wrench" aria-hidden="true"></i> Skaner</message>
|
||||||
<message id="Lms.Admin.menu-tracing"><i class="fa fa-fw fa-bar-chart" aria-hidden="true"></i> Śledzenie</message>
|
|
||||||
<message id="Lms.Admin.menu-users"><i class="fa fa-fw fa-users" aria-hidden="true"></i> Użytkownicy</message>
|
<message id="Lms.Admin.menu-users"><i class="fa fa-fw fa-users" aria-hidden="true"></i> Użytkownicy</message>
|
||||||
|
|
||||||
<!--MediaLibraries-->
|
<!--MediaLibraries-->
|
||||||
@@ -152,23 +152,29 @@
|
|||||||
<message id="Lms.Admin.ScannerController.step-checking-for-removed-files">Sprawdzanie plików... {1}%</message>
|
<message id="Lms.Admin.ScannerController.step-checking-for-removed-files">Sprawdzanie plików... {1}%</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-compact">Prasowanie bazy danych...</message>
|
<message id="Lms.Admin.ScannerController.step-compact">Prasowanie bazy danych...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-compute-cluster-stats">Obliczanie statystyk... {1}%</message>
|
<message id="Lms.Admin.ScannerController.step-compute-cluster-stats">Obliczanie statystyk... {1}%</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-discovering-files">
|
|
||||||
<plural case="0">Odkrywanie plików: {1} plik</plural>
|
|
||||||
<plural case="1">Odkrywanie plików: {1} pliki</plural>
|
|
||||||
<plural case="2">Odkrywanie plików: {1} plików</plural>
|
|
||||||
</message>
|
|
||||||
<message id="Lms.Admin.ScannerController.step-fetching-track-features">Pobieranie danych o ścieżce z AcousticBrainz: {1}/{2} ścieżek ({3}%)...</message>
|
<message id="Lms.Admin.ScannerController.step-fetching-track-features">Pobieranie danych o ścieżce z AcousticBrainz: {1}/{2} ścieżek ({3}%)...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-optimize">Optymalizowanie bazy danych... {1}%...</message>
|
<message id="Lms.Admin.ScannerController.step-optimize">Optymalizowanie bazy danych... {1}%...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-reconciliate-artists">Uzgodnianie artystów: {1} wpisów...</message>
|
<message id="Lms.Admin.ScannerController.step-reconciliate-artists">Uzgodnianie artystów: {1} wpisów...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-reloading-similarity-engine">Przeładowywanie silnika podobieństw: {1}%...</message>
|
<message id="Lms.Admin.ScannerController.step-reloading-similarity-engine">Przeładowywanie silnika podobieństw: {1}%...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-removing-orphaned-entries">Usuwanie osieroconych wpisów: {1} wpisów...</message>
|
<message id="Lms.Admin.ScannerController.step-removing-orphaned-entries">Usuwanie osieroconych wpisów: {1} wpisów...</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-scanning-files">Skanowanie plików: {1}/{2} ({3}%)...</message>
|
<message id="Lms.Admin.ScannerController.step-scanning-files">
|
||||||
|
<plural case="0">Analiza bibliotek: {1} plik</plural>
|
||||||
|
<plural case="1">Analiza bibliotek: {1} pliki</plural>
|
||||||
|
<plural case="2">Analiza bibliotek: {1} plików</plural>
|
||||||
|
</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-status">Obecny krok</message>
|
<message id="Lms.Admin.ScannerController.step-status">Obecny krok</message>
|
||||||
<message id="Lms.Admin.ScannerController.step-updating-library-fields">Aktualizowanie pól biblioteki: {1} wpisów</message>
|
<message id="Lms.Admin.ScannerController.step-updating-library-fields">Aktualizowanie pól biblioteki: {1} wpisów</message>
|
||||||
|
|
||||||
|
<!--Debug Tools-->
|
||||||
|
<message id="Lms.Admin.DebugTools.debug-tools">Narzędzia debugowania</message>
|
||||||
|
|
||||||
|
<!--Db-->
|
||||||
|
<message id="Lms.Admin.DebugTools.Db.export-query-plans">Eksportuj plany zapytań</message>
|
||||||
|
<message id="Lms.Admin.DebugTools.Db.db">Baza danych</message>
|
||||||
|
|
||||||
<!--Tracing-->
|
<!--Tracing-->
|
||||||
<message id="Lms.Admin.Tracing.export-current-buffer">Eksportuj ślady</message>
|
<message id="Lms.Admin.DebugTools.Tracing.export-current-buffer">Eksportuj ślady</message>
|
||||||
<message id="Lms.Admin.Tracing.tracing">Śledzenie</message>
|
<message id="Lms.Admin.DebugTools.Tracing.tracing">Śledzenie</message>
|
||||||
|
|
||||||
<!--Users-->
|
<!--Users-->
|
||||||
<message id="Lms.Admin.Users.add">Nowy użytkownik</message>
|
<message id="Lms.Admin.Users.add">Nowy użytkownik</message>
|
||||||
|
|||||||
@@ -137,15 +137,20 @@
|
|||||||
<message id="Lms.Admin.ScannerController.step-checking-for-removed-files">检查文件中... {1}%</message>
|
<message id="Lms.Admin.ScannerController.step-checking-for-removed-files">检查文件中... {1}%</message>
|
||||||
|
|
||||||
|
|
||||||
<message id="Lms.Admin.ScannerController.step-discovering-files">检索文件中: {1} 文件</message>
|
|
||||||
<message id="Lms.Admin.ScannerController.step-fetching-track-features">从 AcousticBrainz 获取音轨特征: {1}/{2} 音轨 ({3}%)...</message>
|
<message id="Lms.Admin.ScannerController.step-fetching-track-features">从 AcousticBrainz 获取音轨特征: {1}/{2} 音轨 ({3}%)...</message>
|
||||||
|
|
||||||
|
|
||||||
<message id="Lms.Admin.ScannerController.step-reloading-similarity-engine">重载相似引擎中 {1}%...</message>
|
<message id="Lms.Admin.ScannerController.step-reloading-similarity-engine">重载相似引擎中 {1}%...</message>
|
||||||
|
|
||||||
<message id="Lms.Admin.ScannerController.step-scanning-files">扫描文件中: {1}/{2} 个文件 ({3}%)...</message>
|
|
||||||
<message id="Lms.Admin.ScannerController.step-status">当前步骤状态</message>
|
<message id="Lms.Admin.ScannerController.step-status">当前步骤状态</message>
|
||||||
|
|
||||||
|
<!--Debug Tools-->
|
||||||
|
|
||||||
|
|
||||||
|
<!--Db-->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!--Tracing-->
|
<!--Tracing-->
|
||||||
|
|
||||||
|
|||||||
+6
-2
@@ -17,7 +17,11 @@ log-min-severity = "info";
|
|||||||
# Can be "none", "quick", or "full"
|
# Can be "none", "quick", or "full"
|
||||||
db-integrity-check = "quick";
|
db-integrity-check = "quick";
|
||||||
# Output db queries on stdout
|
# Output db queries on stdout
|
||||||
|
# Use this only for debugging purpose, as this may impact performance
|
||||||
db-show-queries = false;
|
db-show-queries = false;
|
||||||
|
# Record query plans for database queries.
|
||||||
|
# Use this only for debugging purposes, as this may impact performance
|
||||||
|
db-record-query-plans = false;
|
||||||
|
|
||||||
# Listen port/addr of the web server
|
# Listen port/addr of the web server
|
||||||
listen-port = 5082;
|
listen-port = 5082;
|
||||||
@@ -118,5 +122,5 @@ scanner-skip-duplicate-mbid = false;
|
|||||||
# Scanner read style for metadata, may be 'fast', 'average' or 'accurate'
|
# Scanner read style for metadata, may be 'fast', 'average' or 'accurate'
|
||||||
scanner-parser-read-style = "average";
|
scanner-parser-read-style = "average";
|
||||||
|
|
||||||
# Number of threads to use for scanning file metadata (0 means number of logical CPUs / 2)
|
# Number of threads to use for parallelized tasks (e.g., scanning file metadata). 0 means half the number of logical CPUs.
|
||||||
scanner-metadata-thread-count = 0;
|
scanner-thread-count = 0;
|
||||||
@@ -28,7 +28,6 @@ extern "C"
|
|||||||
}
|
}
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <map>
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "core/ILogger.hpp"
|
#include "core/ILogger.hpp"
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ add_library(lmscore STATIC
|
|||||||
impl/ChildProcessManager.cpp
|
impl/ChildProcessManager.cpp
|
||||||
impl/Config.cpp
|
impl/Config.cpp
|
||||||
impl/FileResourceHandler.cpp
|
impl/FileResourceHandler.cpp
|
||||||
|
impl/JobScheduler.cpp
|
||||||
impl/IOContextRunner.cpp
|
impl/IOContextRunner.cpp
|
||||||
impl/Logger.cpp
|
impl/Logger.cpp
|
||||||
impl/MimeTypes.cpp
|
impl/MimeTypes.cpp
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Emeric Poupon
|
||||||
|
*
|
||||||
|
* This file is part of LMS.
|
||||||
|
*
|
||||||
|
* LMS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LMS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "JobScheduler.hpp"
|
||||||
|
|
||||||
|
#include <boost/asio/post.hpp>
|
||||||
|
|
||||||
|
#include "core/ITraceLogger.hpp"
|
||||||
|
|
||||||
|
#include "core/IJob.hpp"
|
||||||
|
|
||||||
|
namespace lms::core
|
||||||
|
{
|
||||||
|
std::unique_ptr<IJobScheduler> createJobScheduler(core::LiteralString name, std::size_t threadCount)
|
||||||
|
{
|
||||||
|
return std::make_unique<JobScheduler>(name, threadCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
JobScheduler::JobScheduler(core::LiteralString name, std::size_t threadCount)
|
||||||
|
: _name{ name }
|
||||||
|
, _ioContextRunner{ _ioContext, threadCount, name.str() }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
JobScheduler::~JobScheduler() = default;
|
||||||
|
|
||||||
|
void JobScheduler::setShouldAbortCallback(ShouldAbortCallback callback)
|
||||||
|
{
|
||||||
|
_abortCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t JobScheduler::getThreadCount() const
|
||||||
|
{
|
||||||
|
return _ioContextRunner.getThreadCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JobScheduler::scheduleJob(std::unique_ptr<IJob> job)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::scoped_lock lock{ _mutex };
|
||||||
|
_ongoingJobCount += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto jobHandler{ [job = std::move(job), this]() mutable {
|
||||||
|
if (_abortCallback && _abortCallback())
|
||||||
|
{
|
||||||
|
std::scoped_lock lock{ _mutex };
|
||||||
|
_ongoingJobCount -= 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
{
|
||||||
|
LMS_SCOPED_TRACE_OVERVIEW(_name, job->getName());
|
||||||
|
job->run();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::scoped_lock lock{ _mutex };
|
||||||
|
|
||||||
|
_doneJobs.emplace_back(std::move(job));
|
||||||
|
_ongoingJobCount -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_condVar.notify_all();
|
||||||
|
} };
|
||||||
|
|
||||||
|
boost::asio::post(_ioContext, std::move(jobHandler));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t JobScheduler::getJobsDoneCount() const
|
||||||
|
{
|
||||||
|
std::scoped_lock lock{ _mutex };
|
||||||
|
return _doneJobs.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t JobScheduler::popJobsDone(std::vector<std::unique_ptr<IJob>>& doneJobs, std::size_t maxCount)
|
||||||
|
{
|
||||||
|
doneJobs.clear();
|
||||||
|
doneJobs.reserve(maxCount);
|
||||||
|
|
||||||
|
{
|
||||||
|
std::scoped_lock lock{ _mutex };
|
||||||
|
|
||||||
|
while (doneJobs.size() < maxCount && !_doneJobs.empty())
|
||||||
|
{
|
||||||
|
doneJobs.push_back(std::move(_doneJobs.front()));
|
||||||
|
_doneJobs.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return doneJobs.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JobScheduler::waitUntilJobCountAtMost(std::size_t maxOngoingJobs)
|
||||||
|
{
|
||||||
|
if (_ongoingJobCount <= maxOngoingJobs)
|
||||||
|
return;
|
||||||
|
|
||||||
|
{
|
||||||
|
LMS_SCOPED_TRACE_OVERVIEW(_name, "WaitJobs");
|
||||||
|
|
||||||
|
std::unique_lock lock{ _mutex };
|
||||||
|
_condVar.wait(lock, [=, this] { return _ongoingJobCount <= maxOngoingJobs; });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JobScheduler::wait()
|
||||||
|
{
|
||||||
|
waitUntilJobCountAtMost(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace lms::core
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Emeric Poupon
|
||||||
|
*
|
||||||
|
* This file is part of LMS.
|
||||||
|
*
|
||||||
|
* LMS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LMS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <deque>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "core/IJobScheduler.hpp"
|
||||||
|
#include "core/IOContextRunner.hpp"
|
||||||
|
|
||||||
|
namespace lms::core
|
||||||
|
{
|
||||||
|
class JobScheduler : public IJobScheduler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JobScheduler(core::LiteralString name, std::size_t threadCount);
|
||||||
|
~JobScheduler() override;
|
||||||
|
JobScheduler(const JobScheduler&) = delete;
|
||||||
|
JobScheduler& operator=(const JobScheduler&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setShouldAbortCallback(ShouldAbortCallback callback) override;
|
||||||
|
|
||||||
|
std::size_t getThreadCount() const override;
|
||||||
|
void scheduleJob(std::unique_ptr<IJob> job) override;
|
||||||
|
|
||||||
|
std::size_t getJobsDoneCount() const override;
|
||||||
|
size_t popJobsDone(std::vector<std::unique_ptr<IJob>>& jobs, std::size_t maxCount) override;
|
||||||
|
|
||||||
|
void waitUntilJobCountAtMost(std::size_t maxOngoingJobs) override;
|
||||||
|
void wait() override;
|
||||||
|
|
||||||
|
core::LiteralString _name;
|
||||||
|
boost::asio::io_context _ioContext;
|
||||||
|
core::IOContextRunner _ioContextRunner;
|
||||||
|
|
||||||
|
ShouldAbortCallback _abortCallback;
|
||||||
|
|
||||||
|
mutable std::mutex _mutex;
|
||||||
|
std::atomic<std::size_t> _ongoingJobCount;
|
||||||
|
std::deque<std::unique_ptr<IJob>> _doneJobs;
|
||||||
|
std::condition_variable _condVar;
|
||||||
|
};
|
||||||
|
} // namespace lms::core
|
||||||
@@ -20,45 +20,13 @@
|
|||||||
#include "core/Path.hpp"
|
#include "core/Path.hpp"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <fstream>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <system_error>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <boost/tokenizer.hpp>
|
|
||||||
|
|
||||||
#include "core/Crc32Calculator.hpp"
|
|
||||||
#include "core/Exception.hpp"
|
|
||||||
#include "core/ILogger.hpp"
|
#include "core/ILogger.hpp"
|
||||||
#include "core/String.hpp"
|
#include "core/String.hpp"
|
||||||
|
|
||||||
namespace lms::core::pathUtils
|
namespace lms::core::pathUtils
|
||||||
{
|
{
|
||||||
std::uint32_t computeCrc32(const std::filesystem::path& p)
|
|
||||||
{
|
|
||||||
core::Crc32Calculator crc32;
|
|
||||||
|
|
||||||
std::ifstream ifs{ p, std::ios_base::binary };
|
|
||||||
if (ifs)
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
std::array<char, 1024> buffer;
|
|
||||||
|
|
||||||
ifs.read(buffer.data(), buffer.size());
|
|
||||||
crc32.processBytes(reinterpret_cast<const std::byte*>(buffer.data()), ifs.gcount());
|
|
||||||
} while (ifs);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LMS_LOG(DBUPDATER, ERROR, "Failed to open file " << p);
|
|
||||||
throw LmsException("Failed to open file '" + p.string() + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
return crc32.getResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ensureDirectory(const std::filesystem::path& dir)
|
bool ensureDirectory(const std::filesystem::path& dir)
|
||||||
{
|
{
|
||||||
if (std::filesystem::exists(dir))
|
if (std::filesystem::exists(dir))
|
||||||
@@ -67,81 +35,6 @@ namespace lms::core::pathUtils
|
|||||||
return std::filesystem::create_directory(dir);
|
return std::filesystem::create_directory(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
Wt::WDateTime getLastWriteTime(const std::filesystem::path& file, std::error_code& ec)
|
|
||||||
{
|
|
||||||
Wt::WDateTime res;
|
|
||||||
|
|
||||||
struct stat sb
|
|
||||||
{
|
|
||||||
};
|
|
||||||
if (stat(file.c_str(), &sb) == -1)
|
|
||||||
{
|
|
||||||
ec = std::error_code{ errno, std::generic_category() };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ec = std::error_code{};
|
|
||||||
res = Wt::WDateTime::fromTime_t(sb.st_mtime);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool exploreFilesRecursive(const std::filesystem::path& directory, std::function<bool(std::error_code, const std::filesystem::path&)> cb, const std::filesystem::path* excludeDirFileName)
|
|
||||||
{
|
|
||||||
std::error_code ec;
|
|
||||||
std::filesystem::directory_iterator itPath{ directory, std::filesystem::directory_options::follow_directory_symlink, ec };
|
|
||||||
|
|
||||||
if (ec)
|
|
||||||
{
|
|
||||||
cb(ec, directory);
|
|
||||||
return true; // try to continue exploring anyway
|
|
||||||
}
|
|
||||||
|
|
||||||
if (excludeDirFileName && !excludeDirFileName->empty())
|
|
||||||
{
|
|
||||||
const std::filesystem::path excludePath{ directory / *excludeDirFileName };
|
|
||||||
|
|
||||||
if (std::filesystem::exists(excludePath, ec))
|
|
||||||
{
|
|
||||||
LMS_LOG(DBUPDATER, DEBUG, "Found " << excludePath << ": skipping directory");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::filesystem::directory_iterator itEnd;
|
|
||||||
while (itPath != itEnd)
|
|
||||||
{
|
|
||||||
bool continueExploring{ true };
|
|
||||||
|
|
||||||
if (ec)
|
|
||||||
{
|
|
||||||
continueExploring = cb(ec, *itPath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (std::filesystem::is_regular_file(*itPath, ec))
|
|
||||||
{
|
|
||||||
continueExploring = cb(ec, *itPath);
|
|
||||||
}
|
|
||||||
else if (std::filesystem::is_directory(*itPath, ec))
|
|
||||||
{
|
|
||||||
if (!ec)
|
|
||||||
continueExploring = exploreFilesRecursive(*itPath, cb, excludeDirFileName);
|
|
||||||
else
|
|
||||||
continueExploring = cb(ec, *itPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!continueExploring)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
itPath.increment(ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasFileAnyExtension(const std::filesystem::path& file, std::span<const std::filesystem::path> supportedExtensions)
|
bool hasFileAnyExtension(const std::filesystem::path& file, std::span<const std::filesystem::path> supportedExtensions)
|
||||||
{
|
{
|
||||||
const std::filesystem::path extension{ stringUtils::stringToLower(file.extension().c_str()) };
|
const std::filesystem::path extension{ stringUtils::stringToLower(file.extension().c_str()) };
|
||||||
|
|||||||
+8
-11
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2023 Emeric Poupon
|
* Copyright (C) 2025 Emeric Poupon
|
||||||
*
|
*
|
||||||
* This file is part of LMS.
|
* This file is part of LMS.
|
||||||
*
|
*
|
||||||
@@ -19,19 +19,16 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ScanStepBase.hpp"
|
#include "core/LiteralString.hpp"
|
||||||
|
|
||||||
namespace lms::scanner
|
namespace lms::core
|
||||||
{
|
{
|
||||||
class ScanStepDiscoverFiles : public ScanStepBase
|
class IJob
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using ScanStepBase::ScanStepBase;
|
virtual ~IJob() = default;
|
||||||
|
|
||||||
private:
|
virtual LiteralString getName() const = 0;
|
||||||
ScanStep getStep() const override { return ScanStep::DiscoverFiles; }
|
virtual void run() = 0;
|
||||||
core::LiteralString getStepName() const override { return "Discover files"; }
|
|
||||||
bool needProcess(const ScanContext& context) const override;
|
|
||||||
void process(ScanContext& context) override;
|
|
||||||
};
|
};
|
||||||
} // namespace lms::scanner
|
} // namespace lms::core
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Emeric Poupon
|
||||||
|
*
|
||||||
|
* This file is part of LMS.
|
||||||
|
*
|
||||||
|
* LMS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LMS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "core/LiteralString.hpp"
|
||||||
|
|
||||||
|
namespace lms::core
|
||||||
|
{
|
||||||
|
class IJob;
|
||||||
|
class IJobScheduler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IJobScheduler() = default;
|
||||||
|
|
||||||
|
using ShouldAbortCallback = std::function<bool()>;
|
||||||
|
virtual void setShouldAbortCallback(ShouldAbortCallback callback) = 0;
|
||||||
|
|
||||||
|
virtual std::size_t getThreadCount() const = 0;
|
||||||
|
virtual void scheduleJob(std::unique_ptr<IJob> job) = 0;
|
||||||
|
|
||||||
|
virtual std::size_t getJobsDoneCount() const = 0;
|
||||||
|
virtual size_t popJobsDone(std::vector<std::unique_ptr<IJob>>& jobs, std::size_t maxCount) = 0;
|
||||||
|
|
||||||
|
virtual void waitUntilJobCountAtMost(std::size_t maxOngoingJobs) = 0;
|
||||||
|
virtual void wait() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<IJobScheduler> createJobScheduler(core::LiteralString name, std::size_t threadCount);
|
||||||
|
} // namespace lms::core
|
||||||
@@ -101,3 +101,10 @@ namespace lms::core::logging
|
|||||||
if (auto* logger_{ ::lms::core::Service<::lms::core::logging::ILogger>::get() }; logger_ && logger_->isSeverityActive(::lms::core::logging::Severity::severity)) \
|
if (auto* logger_{ ::lms::core::Service<::lms::core::logging::ILogger>::get() }; logger_ && logger_->isSeverityActive(::lms::core::logging::Severity::severity)) \
|
||||||
::lms::core::logging::Log{ *logger_, ::lms::core::logging::Module::module, ::lms::core::logging::Severity::severity }.getOstream() << message; \
|
::lms::core::logging::Log{ *logger_, ::lms::core::logging::Module::module, ::lms::core::logging::Severity::severity }.getOstream() << message; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#define LMS_LOG_IF(module, severity, cond, message) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
if (auto* logger_{ ::lms::core::Service<::lms::core::logging::ILogger>::get() }; logger_ && logger_->isSeverityActive(::lms::core::logging::Severity::severity) && cond) \
|
||||||
|
::lms::core::logging::Log{ *logger_, ::lms::core::logging::Module::module, ::lms::core::logging::Severity::severity }.getOstream() << message; \
|
||||||
|
} while (0)
|
||||||
|
|||||||
@@ -20,26 +20,16 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <functional>
|
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <system_error>
|
|
||||||
|
|
||||||
#include <Wt/WDateTime.h>
|
#include <Wt/WDateTime.h>
|
||||||
|
|
||||||
namespace lms::core::pathUtils
|
namespace lms::core::pathUtils
|
||||||
{
|
{
|
||||||
std::uint32_t computeCrc32(const std::filesystem::path& p);
|
|
||||||
|
|
||||||
// Make sure the given path is a directory
|
// Make sure the given path is a directory
|
||||||
// Create it if needed
|
// Create it if needed
|
||||||
bool ensureDirectory(const std::filesystem::path& dir);
|
bool ensureDirectory(const std::filesystem::path& dir);
|
||||||
|
|
||||||
// Get the last write time since Epoch
|
|
||||||
Wt::WDateTime getLastWriteTime(const std::filesystem::path& file, std::error_code& ec);
|
|
||||||
|
|
||||||
// returns false if aborted by user
|
|
||||||
bool exploreFilesRecursive(const std::filesystem::path& directory, std::function<bool(std::error_code, const std::filesystem::path&)> cb, const std::filesystem::path* excludeDirFileName = {});
|
|
||||||
|
|
||||||
// Check if file's extension is one of provided extensions
|
// Check if file's extension is one of provided extensions
|
||||||
bool hasFileAnyExtension(const std::filesystem::path& file, std::span<const std::filesystem::path> extensions);
|
bool hasFileAnyExtension(const std::filesystem::path& file, std::span<const std::filesystem::path> extensions);
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ include(GoogleTest)
|
|||||||
|
|
||||||
add_executable(test-core
|
add_executable(test-core
|
||||||
EnumSet.cpp
|
EnumSet.cpp
|
||||||
|
JobScheduler.cpp
|
||||||
LiteralString.cpp
|
LiteralString.cpp
|
||||||
PartialDateTime.cpp
|
PartialDateTime.cpp
|
||||||
Path.cpp
|
Path.cpp
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Emeric Poupon
|
||||||
|
*
|
||||||
|
* This file is part of LMS.
|
||||||
|
*
|
||||||
|
* LMS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LMS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "core/IJob.hpp"
|
||||||
|
#include "core/IJobScheduler.hpp"
|
||||||
|
|
||||||
|
namespace lms::core
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
class TestJob : public IJob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TestJob(std::atomic<std::size_t>& count)
|
||||||
|
: workCount(count) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
LiteralString getName() const override { return "test"; };
|
||||||
|
void run() override
|
||||||
|
{
|
||||||
|
// Simulate some work
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
|
++workCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::atomic<std::size_t>& workCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
TEST(JobScheduler, basic)
|
||||||
|
{
|
||||||
|
std::atomic<std::size_t> workCount{ 0 };
|
||||||
|
|
||||||
|
auto scheduler{ createJobScheduler("TestScheduler", 2) };
|
||||||
|
ASSERT_NE(scheduler, nullptr);
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; ++i)
|
||||||
|
scheduler->scheduleJob(std::make_unique<TestJob>(workCount));
|
||||||
|
|
||||||
|
// Wait for all jobs to complete
|
||||||
|
scheduler->wait();
|
||||||
|
EXPECT_EQ(workCount.load(), 10);
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<IJob>> doneJobs;
|
||||||
|
scheduler->popJobsDone(doneJobs, 10);
|
||||||
|
EXPECT_EQ(doneJobs.size(), 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(JobScheduler, abort)
|
||||||
|
{
|
||||||
|
std::atomic<std::size_t> workCount{ 0 };
|
||||||
|
|
||||||
|
auto scheduler{ createJobScheduler("TestScheduler", 2) };
|
||||||
|
ASSERT_NE(scheduler, nullptr);
|
||||||
|
|
||||||
|
scheduler->setShouldAbortCallback([] { return true; });
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; ++i)
|
||||||
|
scheduler->scheduleJob(std::make_unique<TestJob>(workCount));
|
||||||
|
|
||||||
|
// Wait for all jobs to complete
|
||||||
|
scheduler->wait();
|
||||||
|
EXPECT_EQ(workCount.load(), 0); // nothing was done
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<IJob>> doneJobs;
|
||||||
|
scheduler->popJobsDone(doneJobs, 10);
|
||||||
|
EXPECT_EQ(doneJobs.size(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace lms::core
|
||||||
@@ -1,39 +1,42 @@
|
|||||||
add_library(lmsdatabase STATIC
|
add_library(lmsdatabase STATIC
|
||||||
impl/Artist.cpp
|
impl/objects/Artist.cpp
|
||||||
impl/ArtistInfo.cpp
|
impl/objects/ArtistInfo.cpp
|
||||||
impl/Artwork.cpp
|
impl/objects/Artwork.cpp
|
||||||
impl/AuthToken.cpp
|
impl/objects/AuthToken.cpp
|
||||||
impl/Cluster.cpp
|
impl/objects/Cluster.cpp
|
||||||
|
impl/objects/Directory.cpp
|
||||||
|
impl/objects/Image.cpp
|
||||||
|
impl/objects/Listen.cpp
|
||||||
|
impl/objects/MediaLibrary.cpp
|
||||||
|
impl/objects/PlayListFile.cpp
|
||||||
|
impl/objects/PlayQueue.cpp
|
||||||
|
impl/objects/TrackArtistLink.cpp
|
||||||
|
impl/objects/TrackFeatures.cpp
|
||||||
|
impl/objects/TrackList.cpp
|
||||||
|
impl/objects/RatedArtist.cpp
|
||||||
|
impl/objects/RatedRelease.cpp
|
||||||
|
impl/objects/RatedTrack.cpp
|
||||||
|
impl/objects/Release.cpp
|
||||||
|
impl/objects/ScanSettings.cpp
|
||||||
|
impl/objects/StarredArtist.cpp
|
||||||
|
impl/objects/StarredRelease.cpp
|
||||||
|
impl/objects/StarredTrack.cpp
|
||||||
|
impl/objects/Track.cpp
|
||||||
|
impl/objects/TrackBookmark.cpp
|
||||||
|
impl/objects/TrackEmbeddedImage.cpp
|
||||||
|
impl/objects/TrackEmbeddedImageLink.cpp
|
||||||
|
impl/objects/TrackLyrics.cpp
|
||||||
|
impl/objects/UIState.cpp
|
||||||
|
impl/objects/User.cpp
|
||||||
impl/Db.cpp
|
impl/Db.cpp
|
||||||
impl/Directory.cpp
|
|
||||||
impl/IdType.cpp
|
impl/IdType.cpp
|
||||||
impl/Image.cpp
|
|
||||||
impl/Listen.cpp
|
|
||||||
impl/MediaLibrary.cpp
|
|
||||||
impl/Migration.cpp
|
impl/Migration.cpp
|
||||||
impl/PlayListFile.cpp
|
impl/Object.cpp
|
||||||
impl/PlayQueue.cpp
|
impl/QueryPlanRecorder.cpp
|
||||||
impl/TrackArtistLink.cpp
|
|
||||||
impl/TrackFeatures.cpp
|
|
||||||
impl/TrackList.cpp
|
|
||||||
impl/RatedArtist.cpp
|
|
||||||
impl/RatedRelease.cpp
|
|
||||||
impl/RatedTrack.cpp
|
|
||||||
impl/Release.cpp
|
|
||||||
impl/ScanSettings.cpp
|
|
||||||
impl/Session.cpp
|
impl/Session.cpp
|
||||||
impl/StarredArtist.cpp
|
|
||||||
impl/StarredRelease.cpp
|
|
||||||
impl/StarredTrack.cpp
|
|
||||||
impl/SqlQuery.cpp
|
impl/SqlQuery.cpp
|
||||||
impl/Track.cpp
|
impl/Transaction.cpp
|
||||||
impl/TrackBookmark.cpp
|
|
||||||
impl/TrackEmbeddedImage.cpp
|
|
||||||
impl/TrackEmbeddedImageLink.cpp
|
|
||||||
impl/TrackLyrics.cpp
|
|
||||||
impl/Types.cpp
|
impl/Types.cpp
|
||||||
impl/UIState.cpp
|
|
||||||
impl/User.cpp
|
|
||||||
impl/Utils.cpp
|
impl/Utils.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -46,6 +49,7 @@ target_include_directories(lmsdatabase INTERFACE
|
|||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(lmsdatabase PRIVATE
|
target_include_directories(lmsdatabase PRIVATE
|
||||||
|
impl
|
||||||
include
|
include
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,6 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/Db.hpp"
|
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
@@ -29,7 +27,9 @@
|
|||||||
#include "core/ILogger.hpp"
|
#include "core/ILogger.hpp"
|
||||||
#include "core/Service.hpp"
|
#include "core/Service.hpp"
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/User.hpp"
|
#include "database/objects/User.hpp"
|
||||||
|
|
||||||
|
#include "Db.hpp"
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
@@ -114,7 +114,7 @@ namespace lms::db
|
|||||||
|
|
||||||
std::string table;
|
std::string table;
|
||||||
std::string foreignTable;
|
std::string foreignTable;
|
||||||
// see https://www.sqlite.org/pragma.html#pragma_foreign_key_check for exepcted result
|
// see https://www.sqlite.org/pragma.html#pragma_foreign_key_check for expected result
|
||||||
while (statement->nextRow())
|
while (statement->nextRow())
|
||||||
{
|
{
|
||||||
foreignKeyConstraintsPassed = false;
|
foreignKeyConstraintsPassed = false;
|
||||||
@@ -132,8 +132,64 @@ namespace lms::db
|
|||||||
|
|
||||||
return foreignKeyConstraintsPassed;
|
return foreignKeyConstraintsPassed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<int> getPageSize(Wt::Dbo::SqlConnection& connection)
|
||||||
|
{
|
||||||
|
auto statement = connection.prepareStatement("PRAGMA page_size");
|
||||||
|
statement->execute();
|
||||||
|
|
||||||
|
std::optional<int> res;
|
||||||
|
while (statement->nextRow())
|
||||||
|
{
|
||||||
|
assert(!res);
|
||||||
|
int value{};
|
||||||
|
if (statement->getResult(0, &value))
|
||||||
|
res = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<int> getCacheSize(Wt::Dbo::SqlConnection& connection)
|
||||||
|
{
|
||||||
|
auto statement = connection.prepareStatement("PRAGMA cache_size");
|
||||||
|
statement->execute();
|
||||||
|
|
||||||
|
std::optional<int> res;
|
||||||
|
while (statement->nextRow())
|
||||||
|
{
|
||||||
|
assert(!res);
|
||||||
|
int value{};
|
||||||
|
if (statement->getResult(0, &value))
|
||||||
|
res = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void getCompileOptions(Wt::Dbo::SqlConnection& connection, std::function<void(std::string_view compileOption)> callback)
|
||||||
|
{
|
||||||
|
auto statement = connection.prepareStatement("PRAGMA compile_options");
|
||||||
|
statement->execute();
|
||||||
|
|
||||||
|
std::string res;
|
||||||
|
while (statement->nextRow())
|
||||||
|
{
|
||||||
|
res.clear();
|
||||||
|
|
||||||
|
if (statement->getResult(0, &res, static_cast<int>(res.capacity())))
|
||||||
|
callback(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
std::unique_ptr<IDb> createDb(const std::filesystem::path& dbPath, std::size_t connectionCount)
|
||||||
|
{
|
||||||
|
return std::make_unique<Db>(dbPath, connectionCount);
|
||||||
|
}
|
||||||
|
|
||||||
// Session living class handling the database and the login
|
// Session living class handling the database and the login
|
||||||
Db::Db(const std::filesystem::path& dbPath, std::size_t connectionCount)
|
Db::Db(const std::filesystem::path& dbPath, std::size_t connectionCount)
|
||||||
{
|
{
|
||||||
@@ -152,6 +208,13 @@ namespace lms::db
|
|||||||
|
|
||||||
_connectionPool = std::move(connectionPool);
|
_connectionPool = std::move(connectionPool);
|
||||||
|
|
||||||
|
executeSql("PRAGMA temp_store=MEMORY");
|
||||||
|
executeSql("PRAGMA cache_size=-8000");
|
||||||
|
executeSql("PRAGMA automatic_index=0");
|
||||||
|
|
||||||
|
logPageSize();
|
||||||
|
logCacheSize();
|
||||||
|
logCompileOptions();
|
||||||
if (checkType == "quick")
|
if (checkType == "quick")
|
||||||
{
|
{
|
||||||
performQuickCheck();
|
performQuickCheck();
|
||||||
@@ -194,6 +257,34 @@ namespace lms::db
|
|||||||
return *tlsSession;
|
return *tlsSession;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Db::logPageSize()
|
||||||
|
{
|
||||||
|
ScopedConnection connection{ *_connectionPool };
|
||||||
|
const std::optional<int> pageSize{ getPageSize(*connection) };
|
||||||
|
|
||||||
|
if (pageSize)
|
||||||
|
LMS_LOG(DB, INFO, "Page size set to " << *pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Db::logCacheSize()
|
||||||
|
{
|
||||||
|
ScopedConnection connection{ *_connectionPool };
|
||||||
|
const std::optional<int> cacheSize{ getCacheSize(*connection) };
|
||||||
|
|
||||||
|
if (cacheSize)
|
||||||
|
LMS_LOG(DB, INFO, "Cache size set to " << *cacheSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Db::logCompileOptions()
|
||||||
|
{
|
||||||
|
ScopedConnection connection{ *_connectionPool };
|
||||||
|
|
||||||
|
LMS_LOG(DB, INFO, "Sqlite3 compile options:");
|
||||||
|
getCompileOptions(*connection, [](std::string_view compileOption) {
|
||||||
|
LMS_LOG(DB, INFO, compileOption);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void Db::performQuickCheck()
|
void Db::performQuickCheck()
|
||||||
{
|
{
|
||||||
ScopedConnection connection{ *_connectionPool };
|
ScopedConnection connection{ *_connectionPool };
|
||||||
@@ -201,7 +292,7 @@ namespace lms::db
|
|||||||
LMS_LOG(DB, INFO, "Performing quick database check...");
|
LMS_LOG(DB, INFO, "Performing quick database check...");
|
||||||
|
|
||||||
// Quick check is just a simple integrity check
|
// Quick check is just a simple integrity check
|
||||||
bool quickCheckPassed{ checkDbIntegrity(*connection, IntegrityCheckType::Quick, [&](std::string_view error) {
|
const bool quickCheckPassed{ checkDbIntegrity(*connection, IntegrityCheckType::Quick, [&](std::string_view error) {
|
||||||
LMS_LOG(DB, ERROR, "Quick check error: " << error);
|
LMS_LOG(DB, ERROR, "Quick check error: " << error);
|
||||||
}) };
|
}) };
|
||||||
|
|
||||||
@@ -217,7 +308,7 @@ namespace lms::db
|
|||||||
|
|
||||||
LMS_LOG(DB, INFO, "Checking database integrity...");
|
LMS_LOG(DB, INFO, "Checking database integrity...");
|
||||||
|
|
||||||
bool integrityCheckPassed{ checkDbIntegrity(*connection, IntegrityCheckType::Full, [&](std::string_view error) {
|
const bool integrityCheckPassed{ checkDbIntegrity(*connection, IntegrityCheckType::Full, [&](std::string_view error) {
|
||||||
LMS_LOG(DB, ERROR, "Integrity check error: " << error);
|
LMS_LOG(DB, ERROR, "Integrity check error: " << error);
|
||||||
}) };
|
}) };
|
||||||
|
|
||||||
@@ -233,7 +324,7 @@ namespace lms::db
|
|||||||
|
|
||||||
LMS_LOG(DB, INFO, "Checking foreign key constraints...");
|
LMS_LOG(DB, INFO, "Checking foreign key constraints...");
|
||||||
|
|
||||||
bool foreignKeyConstraintsPassed{ checkDbForeignKeyConstraints(*connection, [&](std::string_view table, long long rowId, std::string_view referredTable) {
|
const bool foreignKeyConstraintsPassed{ checkDbForeignKeyConstraints(*connection, [&](std::string_view table, long long rowId, std::string_view referredTable) {
|
||||||
LMS_LOG(DB, ERROR, "Foreign key constraint failed in table '" << table << "', rowid = " << rowId << ", referred table = '" << referredTable << "'");
|
LMS_LOG(DB, ERROR, "Foreign key constraint failed in table '" << table << "', rowid = " << rowId << ", referred table = '" << referredTable << "'");
|
||||||
}) };
|
}) };
|
||||||
|
|
||||||
|
|||||||
@@ -19,21 +19,22 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <filesystem>
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <Wt/Dbo/SqlConnectionPool.h>
|
#include <Wt/Dbo/SqlConnectionPool.h>
|
||||||
|
|
||||||
#include "core/RecursiveSharedMutex.hpp"
|
#include "core/RecursiveSharedMutex.hpp"
|
||||||
|
|
||||||
|
#include "database/IDb.hpp"
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
class Session;
|
class Db : public IDb
|
||||||
class Db
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Db(const std::filesystem::path& dbPath, std::size_t connectionCount = 10);
|
Db(const std::filesystem::path& dbPath, std::size_t connectionCount);
|
||||||
|
|
||||||
Session& getTLSSession();
|
|
||||||
|
|
||||||
void executeSql(const std::string& sql);
|
void executeSql(const std::string& sql);
|
||||||
|
|
||||||
@@ -43,9 +44,14 @@ namespace lms::db
|
|||||||
|
|
||||||
friend class Session;
|
friend class Session;
|
||||||
|
|
||||||
|
Session& getTLSSession() override;
|
||||||
|
|
||||||
core::RecursiveSharedMutex& getMutex() { return _sharedMutex; }
|
core::RecursiveSharedMutex& getMutex() { return _sharedMutex; }
|
||||||
Wt::Dbo::SqlConnectionPool& getConnectionPool() { return *_connectionPool; }
|
Wt::Dbo::SqlConnectionPool& getConnectionPool() { return *_connectionPool; }
|
||||||
|
|
||||||
|
void logPageSize();
|
||||||
|
void logCacheSize();
|
||||||
|
void logCompileOptions();
|
||||||
void performQuickCheck();
|
void performQuickCheck();
|
||||||
void performIntegrityCheck();
|
void performIntegrityCheck();
|
||||||
void performForeignKeyConstraintsCheck();
|
void performForeignKeyConstraintsCheck();
|
||||||
@@ -24,18 +24,17 @@
|
|||||||
#include "core/Exception.hpp"
|
#include "core/Exception.hpp"
|
||||||
#include "core/ILogger.hpp"
|
#include "core/ILogger.hpp"
|
||||||
#include "core/ITraceLogger.hpp"
|
#include "core/ITraceLogger.hpp"
|
||||||
#include "database/Db.hpp"
|
|
||||||
#include "database/ScanSettings.hpp"
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/User.hpp"
|
#include "database/objects/ScanSettings.hpp"
|
||||||
|
|
||||||
|
#include "Db.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
static constexpr Version LMS_DATABASE_VERSION{ 94 };
|
static constexpr Version LMS_DATABASE_VERSION{ 98 };
|
||||||
}
|
}
|
||||||
|
|
||||||
VersionInfo::VersionInfo()
|
VersionInfo::VersionInfo()
|
||||||
@@ -1386,11 +1385,49 @@ FROM artist)");
|
|||||||
utils::executeCommand(*session.getDboSession(), "ALTER TABLE scan_settings ADD COLUMN artist_image_fallback_to_release BOOLEAN NOT NULL DEFAULT(false)");
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE scan_settings ADD COLUMN artist_image_fallback_to_release BOOLEAN NOT NULL DEFAULT(false)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void migrateFromV94(Session& session)
|
||||||
|
{
|
||||||
|
// Removed not that useful columns in track
|
||||||
|
dropIndexes(session);
|
||||||
|
|
||||||
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track DROP COLUMN relative_file_path");
|
||||||
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track DROP COLUMN file_stem");
|
||||||
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track DROP COLUMN file_name");
|
||||||
|
}
|
||||||
|
|
||||||
|
void migrateFromV95(Session& session)
|
||||||
|
{
|
||||||
|
// Make sure each image and each embessed image has an artwork object
|
||||||
|
utils::executeCommand(*session.getDboSession(), R"(INSERT INTO artwork (version, track_embedded_image_id)
|
||||||
|
SELECT 1 AS version, tei.id
|
||||||
|
FROM track_embedded_image tei
|
||||||
|
LEFT JOIN artwork art ON tei.id = art.track_embedded_image_id
|
||||||
|
WHERE art.track_embedded_image_id IS NULL)");
|
||||||
|
|
||||||
|
utils::executeCommand(*session.getDboSession(), R"(INSERT INTO artwork (version, image_id)
|
||||||
|
SELECT 1 AS version, img.id
|
||||||
|
FROM image img
|
||||||
|
LEFT JOIN artwork art ON img.id = art.image_id
|
||||||
|
WHERE art.image_id IS NULL)");
|
||||||
|
}
|
||||||
|
|
||||||
|
void migrateFromV96(Session& session)
|
||||||
|
{
|
||||||
|
// Removed not that useful indexes
|
||||||
|
dropIndexes(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
void migrateFromV97(Session& session)
|
||||||
|
{
|
||||||
|
// Removed not that useful indexes
|
||||||
|
dropIndexes(session);
|
||||||
|
}
|
||||||
|
|
||||||
bool doDbMigration(Session& session)
|
bool doDbMigration(Session& session)
|
||||||
{
|
{
|
||||||
constexpr std::string_view outdatedMsg{ "Outdated database, please rebuild it (delete the .db file and restart)" };
|
constexpr std::string_view outdatedMsg{ "Outdated database, please rebuild it (delete the .db file and restart)" };
|
||||||
|
|
||||||
ScopedNoForeignKeys noPragmaKeys{ session.getDb() };
|
ScopedNoForeignKeys noPragmaKeys{ static_cast<Db&>(session.getDb()) };
|
||||||
|
|
||||||
using MigrationFunction = std::function<void(Session&)>;
|
using MigrationFunction = std::function<void(Session&)>;
|
||||||
|
|
||||||
@@ -1456,6 +1493,10 @@ FROM artist)");
|
|||||||
{ 91, migrateFromV91 },
|
{ 91, migrateFromV91 },
|
||||||
{ 92, migrateFromV92 },
|
{ 92, migrateFromV92 },
|
||||||
{ 93, migrateFromV93 },
|
{ 93, migrateFromV93 },
|
||||||
|
{ 94, migrateFromV94 },
|
||||||
|
{ 95, migrateFromV95 },
|
||||||
|
{ 96, migrateFromV96 },
|
||||||
|
{ 97, migrateFromV97 },
|
||||||
};
|
};
|
||||||
|
|
||||||
bool migrationPerformed{};
|
bool migrationPerformed{};
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Emeric Poupon
|
||||||
|
*
|
||||||
|
* This file is part of LMS.
|
||||||
|
*
|
||||||
|
* LMS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LMS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "database/Object.hpp"
|
||||||
|
|
||||||
|
#include "TransactionChecker.hpp"
|
||||||
|
|
||||||
|
namespace lms::db
|
||||||
|
{
|
||||||
|
void ObjectPtrBase::checkWriteTransaction(Wt::Dbo::Session& session)
|
||||||
|
{
|
||||||
|
#if LMS_CHECK_TRANSACTION_ACCESSES
|
||||||
|
TransactionChecker::checkWriteTransaction(session);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
} // namespace lms::db
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Emeric Poupon
|
||||||
|
*
|
||||||
|
* This file is part of LMS.
|
||||||
|
*
|
||||||
|
* LMS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LMS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "QueryPlanRecorder.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <Wt/Dbo/SqlStatement.h>
|
||||||
|
#include <Wt/Dbo/Transaction.h>
|
||||||
|
|
||||||
|
#include "core/ILogger.hpp"
|
||||||
|
|
||||||
|
namespace lms::db
|
||||||
|
{
|
||||||
|
std::unique_ptr<IQueryPlanRecorder> createQueryPlanRecorder()
|
||||||
|
{
|
||||||
|
return std::make_unique<QueryPlanRecorder>();
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryPlanRecorder::QueryPlanRecorder()
|
||||||
|
{
|
||||||
|
LMS_LOG(DB, INFO, "Recording database query plans");
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryPlanRecorder::~QueryPlanRecorder() = default;
|
||||||
|
|
||||||
|
void QueryPlanRecorder::visitQueryPlans(const QueryPlanVisitor& visitor) const
|
||||||
|
{
|
||||||
|
const std::shared_lock lock{ _mutex };
|
||||||
|
|
||||||
|
for (const auto& [query, plan] : _queryPlans)
|
||||||
|
visitor(query, plan);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QueryPlanRecorder::recordQueryPlanIfNeeded(Wt::Dbo::Session& session, const std::string& query)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
const std::shared_lock lock{ _mutex };
|
||||||
|
|
||||||
|
if (_queryPlans.contains(query))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Wt::Dbo::Transaction transaction{ session };
|
||||||
|
|
||||||
|
Wt::Dbo::SqlConnection* connection{ transaction.connection() };
|
||||||
|
auto statement{ connection->prepareStatement("EXPLAIN QUERY PLAN " + query) };
|
||||||
|
statement->execute();
|
||||||
|
|
||||||
|
std::map<int, std::string> entries{ { 0, "" } };
|
||||||
|
std::map<int, std::vector<int>> relationships;
|
||||||
|
|
||||||
|
std::string detail;
|
||||||
|
while (statement->nextRow())
|
||||||
|
{
|
||||||
|
detail.clear();
|
||||||
|
|
||||||
|
int id{};
|
||||||
|
int parent{};
|
||||||
|
int unused{};
|
||||||
|
|
||||||
|
if (statement->getResult(0, &id)
|
||||||
|
&& statement->getResult(1, &parent)
|
||||||
|
&& statement->getResult(2, &unused)
|
||||||
|
&& statement->getResult(3, &detail, static_cast<int>(detail.capacity())))
|
||||||
|
{
|
||||||
|
entries.emplace(id, detail);
|
||||||
|
relationships[parent].push_back(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// format
|
||||||
|
std::string result;
|
||||||
|
std::function<void(int, unsigned)> formatQuery = [&](int id, unsigned level) -> void {
|
||||||
|
for (std::size_t i{}; i < level; ++i)
|
||||||
|
result += '\t';
|
||||||
|
|
||||||
|
result += entries.at(id);
|
||||||
|
result += '\n';
|
||||||
|
auto itChildren = relationships.find(id);
|
||||||
|
if (itChildren == relationships.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int child : itChildren->second)
|
||||||
|
formatQuery(child, level + 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
formatQuery(0, 0);
|
||||||
|
|
||||||
|
{
|
||||||
|
const std::unique_lock lock{ _mutex };
|
||||||
|
_queryPlans.try_emplace(query, std::move(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace lms::db
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Emeric Poupon
|
||||||
|
*
|
||||||
|
* This file is part of LMS.
|
||||||
|
*
|
||||||
|
* LMS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LMS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <shared_mutex>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Session.h>
|
||||||
|
|
||||||
|
#include "database/IQueryPlanRecorder.hpp"
|
||||||
|
|
||||||
|
namespace lms::db
|
||||||
|
{
|
||||||
|
class QueryPlanRecorder : public IQueryPlanRecorder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QueryPlanRecorder();
|
||||||
|
~QueryPlanRecorder() override;
|
||||||
|
QueryPlanRecorder(const QueryPlanRecorder&) = delete;
|
||||||
|
QueryPlanRecorder& operator=(const QueryPlanRecorder&) = delete;
|
||||||
|
|
||||||
|
void visitQueryPlans(const QueryPlanVisitor& visitor) const override;
|
||||||
|
|
||||||
|
void recordQueryPlanIfNeeded(Wt::Dbo::Session& session, const std::string& query);
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable std::shared_mutex _mutex;
|
||||||
|
std::map<std::string, std::string> _queryPlans;
|
||||||
|
};
|
||||||
|
} // namespace lms::db
|
||||||
@@ -19,41 +19,44 @@
|
|||||||
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/WtSqlTraits.h>
|
||||||
|
|
||||||
#include "core/ILogger.hpp"
|
#include "core/ILogger.hpp"
|
||||||
#include "core/ITraceLogger.hpp"
|
#include "core/ITraceLogger.hpp"
|
||||||
#include "database/Artist.hpp"
|
|
||||||
#include "database/ArtistInfo.hpp"
|
|
||||||
#include "database/Artwork.hpp"
|
|
||||||
#include "database/AuthToken.hpp"
|
|
||||||
#include "database/Cluster.hpp"
|
|
||||||
#include "database/Db.hpp"
|
|
||||||
#include "database/Directory.hpp"
|
|
||||||
#include "database/Image.hpp"
|
|
||||||
#include "database/Listen.hpp"
|
|
||||||
#include "database/MediaLibrary.hpp"
|
|
||||||
#include "database/PlayListFile.hpp"
|
|
||||||
#include "database/PlayQueue.hpp"
|
|
||||||
#include "database/RatedArtist.hpp"
|
|
||||||
#include "database/RatedRelease.hpp"
|
|
||||||
#include "database/RatedTrack.hpp"
|
|
||||||
#include "database/Release.hpp"
|
|
||||||
#include "database/ScanSettings.hpp"
|
|
||||||
#include "database/StarredArtist.hpp"
|
|
||||||
#include "database/StarredRelease.hpp"
|
|
||||||
#include "database/StarredTrack.hpp"
|
|
||||||
#include "database/Track.hpp"
|
|
||||||
#include "database/TrackArtistLink.hpp"
|
|
||||||
#include "database/TrackBookmark.hpp"
|
|
||||||
#include "database/TrackEmbeddedImage.hpp"
|
|
||||||
#include "database/TrackEmbeddedImageLink.hpp"
|
|
||||||
#include "database/TrackFeatures.hpp"
|
|
||||||
#include "database/TrackList.hpp"
|
|
||||||
#include "database/TrackLyrics.hpp"
|
|
||||||
#include "database/TransactionChecker.hpp"
|
|
||||||
#include "database/UIState.hpp"
|
|
||||||
#include "database/User.hpp"
|
|
||||||
|
|
||||||
|
#include "database/objects/Artist.hpp"
|
||||||
|
#include "database/objects/ArtistInfo.hpp"
|
||||||
|
#include "database/objects/Artwork.hpp"
|
||||||
|
#include "database/objects/AuthToken.hpp"
|
||||||
|
#include "database/objects/Cluster.hpp"
|
||||||
|
#include "database/objects/Directory.hpp"
|
||||||
|
#include "database/objects/Image.hpp"
|
||||||
|
#include "database/objects/Listen.hpp"
|
||||||
|
#include "database/objects/MediaLibrary.hpp"
|
||||||
|
#include "database/objects/PlayListFile.hpp"
|
||||||
|
#include "database/objects/PlayQueue.hpp"
|
||||||
|
#include "database/objects/RatedArtist.hpp"
|
||||||
|
#include "database/objects/RatedRelease.hpp"
|
||||||
|
#include "database/objects/RatedTrack.hpp"
|
||||||
|
#include "database/objects/Release.hpp"
|
||||||
|
#include "database/objects/ScanSettings.hpp"
|
||||||
|
#include "database/objects/StarredArtist.hpp"
|
||||||
|
#include "database/objects/StarredRelease.hpp"
|
||||||
|
#include "database/objects/StarredTrack.hpp"
|
||||||
|
#include "database/objects/Track.hpp"
|
||||||
|
#include "database/objects/TrackArtistLink.hpp"
|
||||||
|
#include "database/objects/TrackBookmark.hpp"
|
||||||
|
#include "database/objects/TrackEmbeddedImage.hpp"
|
||||||
|
#include "database/objects/TrackEmbeddedImageLink.hpp"
|
||||||
|
#include "database/objects/TrackFeatures.hpp"
|
||||||
|
#include "database/objects/TrackList.hpp"
|
||||||
|
#include "database/objects/TrackLyrics.hpp"
|
||||||
|
#include "database/objects/UIState.hpp"
|
||||||
|
#include "database/objects/User.hpp"
|
||||||
|
|
||||||
|
#include "Db.hpp"
|
||||||
#include "Migration.hpp"
|
#include "Migration.hpp"
|
||||||
|
#include "TransactionChecker.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/EnumSetTraits.hpp"
|
#include "traits/EnumSetTraits.hpp"
|
||||||
#include "traits/ImageHashTypeTraits.hpp"
|
#include "traits/ImageHashTypeTraits.hpp"
|
||||||
@@ -62,44 +65,10 @@
|
|||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
WriteTransaction::WriteTransaction(core::RecursiveSharedMutex& mutex, Wt::Dbo::Session& session)
|
Session::Session(IDb& db)
|
||||||
: _lock{ mutex }
|
|
||||||
, _transaction{ session }
|
|
||||||
{
|
|
||||||
#if LMS_CHECK_TRANSACTION_ACCESSES
|
|
||||||
TransactionChecker::pushWriteTransaction(_transaction.session());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteTransaction::~WriteTransaction()
|
|
||||||
{
|
|
||||||
#if LMS_CHECK_TRANSACTION_ACCESSES
|
|
||||||
TransactionChecker::popWriteTransaction(_transaction.session());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
core::tracing::ScopedTrace _trace{ "Database", core::tracing::Level::Detailed, "Commit" };
|
|
||||||
_transaction.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
ReadTransaction::ReadTransaction(Wt::Dbo::Session& session)
|
|
||||||
: _transaction{ session }
|
|
||||||
{
|
|
||||||
#if LMS_CHECK_TRANSACTION_ACCESSES
|
|
||||||
TransactionChecker::pushReadTransaction(_transaction.session());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
ReadTransaction::~ReadTransaction()
|
|
||||||
{
|
|
||||||
#if LMS_CHECK_TRANSACTION_ACCESSES
|
|
||||||
TransactionChecker::popReadTransaction(_transaction.session());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
Session::Session(Db& db)
|
|
||||||
: _db{ db }
|
: _db{ db }
|
||||||
{
|
{
|
||||||
_session.setConnectionPool(_db.getConnectionPool());
|
_session.setConnectionPool(static_cast<Db&>(_db).getConnectionPool());
|
||||||
|
|
||||||
_session.mapClass<Artist>("artist");
|
_session.mapClass<Artist>("artist");
|
||||||
_session.mapClass<ArtistInfo>("artist_info");
|
_session.mapClass<ArtistInfo>("artist_info");
|
||||||
@@ -140,7 +109,7 @@ namespace lms::db
|
|||||||
|
|
||||||
WriteTransaction Session::createWriteTransaction()
|
WriteTransaction Session::createWriteTransaction()
|
||||||
{
|
{
|
||||||
return WriteTransaction{ _db.getMutex(), _session };
|
return WriteTransaction{ static_cast<Db&>(_db).getMutex(), _session };
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadTransaction Session::createReadTransaction()
|
ReadTransaction Session::createReadTransaction()
|
||||||
@@ -148,6 +117,19 @@ namespace lms::db
|
|||||||
return ReadTransaction{ _session };
|
return ReadTransaction{ _session };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Session::checkWriteTransaction() const
|
||||||
|
{
|
||||||
|
#if LMS_CHECK_TRANSACTION_ACCESSES
|
||||||
|
TransactionChecker::checkWriteTransaction(_session);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
void Session::checkReadTransaction() const
|
||||||
|
{
|
||||||
|
#if LMS_CHECK_TRANSACTION_ACCESSES
|
||||||
|
TransactionChecker::checkReadTransaction(_session);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void Session::execute(std::string_view statement)
|
void Session::execute(std::string_view statement)
|
||||||
{
|
{
|
||||||
utils::executeCommand(_session, std::string{ statement });
|
utils::executeCommand(_session, std::string{ statement });
|
||||||
@@ -234,7 +216,7 @@ namespace lms::db
|
|||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_embedded_image_link_id_idx ON track_embedded_image_link(id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_embedded_image_link_id_idx ON track_embedded_image_link(id)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_embedded_image_link_track_id_idx ON track_embedded_image_link(track_id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_embedded_image_link_track_id_idx ON track_embedded_image_link(track_id)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_embedded_image_link_track_embedded_image_id_track_id_idx ON track_embedded_image_link(track_embedded_image_id, track_id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_embedded_image_link_track_embedded_image_id_track_id_idx ON track_embedded_image_link(track_embedded_image_id, track_id)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_embedded_image_link_type_track_embedded_image_id_track_id_idx ON track_embedded_image_link(type,track_embedded_image_id,track_id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_embedded_image_link_track_track_embedded_image_id_idx ON track_embedded_image_link(track_id, track_embedded_image_id)");
|
||||||
|
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS image_directory_stem_idx ON image(directory_id, stem COLLATE NOCASE)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS image_directory_stem_idx ON image(directory_id, stem COLLATE NOCASE)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS image_id_idx ON image(id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS image_id_idx ON image(id)");
|
||||||
@@ -254,6 +236,7 @@ namespace lms::db
|
|||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS media_library_id_idx ON media_library(id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS media_library_id_idx ON media_library(id)");
|
||||||
|
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS playlist_file_id_idx ON playlist_file(id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS playlist_file_id_idx ON playlist_file(id)");
|
||||||
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS playlist_file_directory_idx ON playlist_file(directory_id);");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS playlist_file_absolute_file_path_idx ON playlist_file(absolute_file_path)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS playlist_file_absolute_file_path_idx ON playlist_file(absolute_file_path)");
|
||||||
|
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS rated_artist_user_artist_idx ON rated_artist(user_id,artist_id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS rated_artist_user_artist_idx ON rated_artist(user_id,artist_id)");
|
||||||
@@ -275,27 +258,19 @@ namespace lms::db
|
|||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_absolute_path_idx ON track(absolute_file_path)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_absolute_path_idx ON track(absolute_file_path)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_date_idx ON track(date)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_date_idx ON track(date)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_directory_release_idx ON track(directory_id, release_id);");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_directory_release_idx ON track(directory_id, release_id);");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_directory_file_stem_idx ON track(directory_id, file_stem);");
|
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_file_added_idx ON track(file_added)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_file_added_idx ON track(file_added)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_file_added_desc_idx ON track(file_added DESC)");
|
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_file_last_write_idx ON track(file_last_write)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_file_last_write_idx ON track(file_last_write)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_file_last_write_desc_idx ON track(file_last_write DESC)");
|
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_file_name_idx ON track(file_name COLLATE NOCASE)");
|
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_media_library_idx ON track(media_library_id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_media_library_idx ON track(media_library_id)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_media_library_release_idx ON track(media_library_id, release_id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_media_library_release_idx ON track(media_library_id, release_id)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_mbid_idx ON track(mbid)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_mbid_idx ON track(mbid)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_name_idx ON track(name)");
|
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_name_file_size_idx ON track(name, file_size)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_name_file_size_idx ON track(name, file_size)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_name_nocase_idx ON track(name COLLATE NOCASE)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_name_nocase_idx ON track(name COLLATE NOCASE)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_original_date_idx ON track(original_date)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_original_date_idx ON track(original_date)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_recording_mbid_idx ON track(recording_mbid)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_recording_mbid_idx ON track(recording_mbid)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_release_idx ON track(release_id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_release_disc_idx ON track(release_id, disc_number)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_release_date_idx ON track(release_id, date)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_release_date_idx ON track(release_id, date)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_release_date_desc_idx ON track(release_id, date DESC)");
|
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_release_file_last_write_idx ON track(release_id, file_last_write)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_release_file_last_write_idx ON track(release_id, file_last_write)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_release_file_last_write_desc_idx ON track(release_id, file_last_write DESC)");
|
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_release_file_added_idx ON track(release_id, file_added)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_release_file_added_idx ON track(release_id, file_added)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_release_file_added_desc_idx ON track(release_id, file_added DESC)");
|
|
||||||
|
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS tracklist_id_idx ON tracklist(id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS tracklist_id_idx ON tracklist(id)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS tracklist_name_idx ON tracklist(name)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS tracklist_name_idx ON tracklist(name)");
|
||||||
@@ -309,15 +284,15 @@ namespace lms::db
|
|||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_artist_link_artist_idx ON track_artist_link(artist_id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_artist_link_artist_idx ON track_artist_link(artist_id)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_artist_link_artist_mbid_matched_artist_idx ON track_artist_link(artist_mbid_matched, artist_id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_artist_link_artist_mbid_matched_artist_idx ON track_artist_link(artist_mbid_matched, artist_id)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_artist_link_artist_track_idx ON track_artist_link(artist_id, track_id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_artist_link_artist_track_idx ON track_artist_link(artist_id, track_id)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_artist_link_artist_type_idx ON track_artist_link(artist_id, type)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_artist_link_artist_type_track_idx ON track_artist_link(artist_id, type, track_id)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_artist_link_track_artist_idx ON track_artist_link(track_id, artist_id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_artist_link_track_artist_idx ON track_artist_link(track_id, artist_id)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_artist_link_track_type_idx ON track_artist_link(track_id,type)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_artist_link_track_type_idx ON track_artist_link(track_id, type)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_artist_link_type_track_artist_idx ON track_artist_link(type, track_id, artist_id)");
|
|
||||||
|
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_features_track_idx ON track_features(track_id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_features_track_idx ON track_features(track_id)");
|
||||||
|
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_lyrics_id_idx ON track_lyrics(id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_lyrics_id_idx ON track_lyrics(id)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_lyrics_absolute_file_path_idx ON track_lyrics(absolute_file_path)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_lyrics_absolute_file_path_idx ON track_lyrics(absolute_file_path)");
|
||||||
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_lyrics_directory_idx ON track_lyrics(directory_id)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_lyrics_track_idx ON track_lyrics(track_id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_lyrics_track_idx ON track_lyrics(track_id)");
|
||||||
|
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_bookmark_user_idx ON track_bookmark(user_id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_bookmark_user_idx ON track_bookmark(user_id)");
|
||||||
@@ -359,33 +334,13 @@ namespace lms::db
|
|||||||
|
|
||||||
// We manually take a lock here since vacuum cannot be inside a transaction
|
// We manually take a lock here since vacuum cannot be inside a transaction
|
||||||
{
|
{
|
||||||
std::unique_lock lock{ _db.getMutex() };
|
std::unique_lock lock{ static_cast<Db&>(_db).getMutex() };
|
||||||
_db.executeSql("VACUUM");
|
static_cast<Db&>(_db).executeSql("VACUUM");
|
||||||
}
|
}
|
||||||
|
|
||||||
LMS_LOG(DB, INFO, "Vacuum complete!");
|
LMS_LOG(DB, INFO, "Vacuum complete!");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::refreshTracingLoggerStats()
|
|
||||||
{
|
|
||||||
auto* traceLogger{ core::Service<core::tracing::ITraceLogger>::get() };
|
|
||||||
if (!traceLogger)
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto transaction{ createReadTransaction() };
|
|
||||||
|
|
||||||
traceLogger->setMetadata("db_artist_count", std::to_string(db::Artist::getCount(*this)));
|
|
||||||
traceLogger->setMetadata("db_cluster_count", std::to_string(db::Cluster::getCount(*this)));
|
|
||||||
traceLogger->setMetadata("db_cluster_type_count", std::to_string(db::ClusterType::getCount(*this)));
|
|
||||||
traceLogger->setMetadata("db_starred_artist_count", std::to_string(db::StarredArtist::getCount(*this)));
|
|
||||||
traceLogger->setMetadata("db_starred_release_count", std::to_string(db::StarredRelease::getCount(*this)));
|
|
||||||
traceLogger->setMetadata("db_starred_track_count", std::to_string(db::StarredTrack::getCount(*this)));
|
|
||||||
traceLogger->setMetadata("db_track_bookmark_count", std::to_string(db::TrackBookmark::getCount(*this)));
|
|
||||||
traceLogger->setMetadata("db_listen_count", std::to_string(db::Listen::getCount(*this)));
|
|
||||||
traceLogger->setMetadata("db_release_count", std::to_string(db::Release::getCount(*this)));
|
|
||||||
traceLogger->setMetadata("db_track_count", std::to_string(db::Track::getCount(*this)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Session::fullAnalyze()
|
void Session::fullAnalyze()
|
||||||
{
|
{
|
||||||
LMS_SCOPED_TRACE_OVERVIEW("Database", "Analyze");
|
LMS_SCOPED_TRACE_OVERVIEW("Database", "Analyze");
|
||||||
@@ -403,6 +358,8 @@ namespace lms::db
|
|||||||
|
|
||||||
bool Session::areAllTablesEmpty()
|
bool Session::areAllTablesEmpty()
|
||||||
{
|
{
|
||||||
|
checkReadTransaction();
|
||||||
|
|
||||||
const std::vector<std::string> entryList{ utils::fetchQueryResults(_session.query<std::string>("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'")) };
|
const std::vector<std::string> entryList{ utils::fetchQueryResults(_session.query<std::string>("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'")) };
|
||||||
|
|
||||||
return std::all_of(entryList.cbegin(), entryList.cend(), [this](const std::string& entry) {
|
return std::all_of(entryList.cbegin(), entryList.cend(), [this](const std::string& entry) {
|
||||||
@@ -411,6 +368,19 @@ namespace lms::db
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FileStats Session::getFileStats()
|
||||||
|
{
|
||||||
|
FileStats stats{};
|
||||||
|
|
||||||
|
stats.trackCount = db::Track::getCount(*this);
|
||||||
|
stats.artistInfoCount = db::ArtistInfo::getCount(*this);
|
||||||
|
stats.imageCount = db::Image::getCount(*this);
|
||||||
|
stats.trackLyricsCount = db::TrackLyrics::getExternalLyricsCount(*this);
|
||||||
|
stats.playListCount = db::PlayListFile::getCount(*this);
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
void Session::retrieveEntriesToAnalyze(std::vector<std::string>& entryList)
|
void Session::retrieveEntriesToAnalyze(std::vector<std::string>& entryList)
|
||||||
{
|
{
|
||||||
auto transaction{ createReadTransaction() };
|
auto transaction{ createReadTransaction() };
|
||||||
@@ -426,4 +396,10 @@ namespace lms::db
|
|||||||
}
|
}
|
||||||
LMS_LOG(DB, DEBUG, "Analyzing " << entry << ": done!");
|
LMS_LOG(DB, DEBUG, "Analyzing " << entry << ": done!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Session::execute(std::string_view query, long long id)
|
||||||
|
{
|
||||||
|
utils::executeCommand(_session, query, id);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace lms::db
|
} // namespace lms::db
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Emeric Poupon
|
||||||
|
*
|
||||||
|
* This file is part of LMS.
|
||||||
|
*
|
||||||
|
* LMS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LMS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "database/Transaction.hpp"
|
||||||
|
|
||||||
|
#include "core/RecursiveSharedMutex.hpp"
|
||||||
|
|
||||||
|
#include "TransactionChecker.hpp"
|
||||||
|
|
||||||
|
namespace lms::db
|
||||||
|
{
|
||||||
|
WriteTransaction::WriteTransaction(core::RecursiveSharedMutex& mutex, Wt::Dbo::Session& session)
|
||||||
|
: _lock{ mutex }
|
||||||
|
, _trace{ "Database", core::tracing::Level::Detailed, "WriteTransaction" }
|
||||||
|
, _transaction{ session }
|
||||||
|
{
|
||||||
|
#if LMS_CHECK_TRANSACTION_ACCESSES
|
||||||
|
TransactionChecker::pushWriteTransaction(_transaction.session());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteTransaction::~WriteTransaction()
|
||||||
|
{
|
||||||
|
#if LMS_CHECK_TRANSACTION_ACCESSES
|
||||||
|
TransactionChecker::popWriteTransaction(_transaction.session());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
core::tracing::ScopedTrace _trace{ "Database", core::tracing::Level::Detailed, "Commit" };
|
||||||
|
_transaction.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadTransaction::ReadTransaction(Wt::Dbo::Session& session)
|
||||||
|
: _trace{ "Database", core::tracing::Level::Detailed, "ReadTransaction" }
|
||||||
|
, _transaction{ session }
|
||||||
|
{
|
||||||
|
#if LMS_CHECK_TRANSACTION_ACCESSES
|
||||||
|
TransactionChecker::pushReadTransaction(_transaction.session());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadTransaction::~ReadTransaction()
|
||||||
|
{
|
||||||
|
#if LMS_CHECK_TRANSACTION_ACCESSES
|
||||||
|
TransactionChecker::popReadTransaction(_transaction.session());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
} // namespace lms::db
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/TransactionChecker.hpp"
|
#include "TransactionChecker.hpp"
|
||||||
|
|
||||||
static_assert(LMS_CHECK_TRANSACTION_ACCESSES, "File should be excluded from build");
|
static_assert(LMS_CHECK_TRANSACTION_ACCESSES, "File should be excluded from build");
|
||||||
|
|
||||||
@@ -32,39 +32,39 @@ namespace lms::db
|
|||||||
struct StackEntry
|
struct StackEntry
|
||||||
{
|
{
|
||||||
TransactionChecker::TransactionType type;
|
TransactionChecker::TransactionType type;
|
||||||
Wt::Dbo::Session* session{};
|
const Wt::Dbo::Session* session{};
|
||||||
};
|
};
|
||||||
|
|
||||||
static thread_local std::vector<StackEntry> transactionStack;
|
static thread_local std::vector<StackEntry> transactionStack;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void TransactionChecker::pushWriteTransaction(Wt::Dbo::Session& session)
|
void TransactionChecker::pushWriteTransaction(const Wt::Dbo::Session& session)
|
||||||
{
|
{
|
||||||
pushTransaction(TransactionType::Write, session);
|
pushTransaction(TransactionType::Write, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransactionChecker::pushReadTransaction(Wt::Dbo::Session& session)
|
void TransactionChecker::pushReadTransaction(const Wt::Dbo::Session& session)
|
||||||
{
|
{
|
||||||
pushTransaction(TransactionType::Read, session);
|
pushTransaction(TransactionType::Read, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransactionChecker::popWriteTransaction(Wt::Dbo::Session& session)
|
void TransactionChecker::popWriteTransaction(const Wt::Dbo::Session& session)
|
||||||
{
|
{
|
||||||
popTransaction(TransactionType::Write, session);
|
popTransaction(TransactionType::Write, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransactionChecker::popReadTransaction(Wt::Dbo::Session& session)
|
void TransactionChecker::popReadTransaction(const Wt::Dbo::Session& session)
|
||||||
{
|
{
|
||||||
popTransaction(TransactionType::Read, session);
|
popTransaction(TransactionType::Read, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransactionChecker::pushTransaction(TransactionType type, Wt::Dbo::Session& session)
|
void TransactionChecker::pushTransaction(TransactionType type, const Wt::Dbo::Session& session)
|
||||||
{
|
{
|
||||||
assert(transactionStack.empty() || transactionStack.back().session == &session);
|
assert(transactionStack.empty() || transactionStack.back().session == &session);
|
||||||
transactionStack.push_back(StackEntry{ type, &session });
|
transactionStack.push_back(StackEntry{ type, &session });
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransactionChecker::popTransaction(TransactionType type, Wt::Dbo::Session& session)
|
void TransactionChecker::popTransaction(TransactionType type, const Wt::Dbo::Session& session)
|
||||||
{
|
{
|
||||||
assert(!transactionStack.empty());
|
assert(!transactionStack.empty());
|
||||||
assert(transactionStack.back().type == type);
|
assert(transactionStack.back().type == type);
|
||||||
@@ -72,25 +72,25 @@ namespace lms::db
|
|||||||
transactionStack.pop_back();
|
transactionStack.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransactionChecker::checkWriteTransaction(Wt::Dbo::Session& session)
|
void TransactionChecker::checkWriteTransaction(const Wt::Dbo::Session& session)
|
||||||
{
|
{
|
||||||
assert(!transactionStack.empty());
|
assert(!transactionStack.empty());
|
||||||
assert(transactionStack.back().type == TransactionType::Write);
|
assert(transactionStack.back().type == TransactionType::Write);
|
||||||
assert(transactionStack.back().session == &session);
|
assert(transactionStack.back().session == &session);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransactionChecker::checkWriteTransaction(Session& session)
|
void TransactionChecker::checkWriteTransaction(const Session& session)
|
||||||
{
|
{
|
||||||
checkWriteTransaction(*session.getDboSession());
|
checkWriteTransaction(*session.getDboSession());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransactionChecker::checkReadTransaction(Wt::Dbo::Session& session)
|
void TransactionChecker::checkReadTransaction(const Wt::Dbo::Session& session)
|
||||||
{
|
{
|
||||||
assert(!transactionStack.empty());
|
assert(!transactionStack.empty());
|
||||||
assert(transactionStack.back().session == &session);
|
assert(transactionStack.back().session == &session);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransactionChecker::checkReadTransaction(Session& session)
|
void TransactionChecker::checkReadTransaction(const Session& session)
|
||||||
{
|
{
|
||||||
checkReadTransaction(*session.getDboSession());
|
checkReadTransaction(*session.getDboSession());
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-12
@@ -26,8 +26,11 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if LMS_CHECK_TRANSACTION_ACCESSES
|
#if LMS_CHECK_TRANSACTION_ACCESSES
|
||||||
#include <Wt/Dbo/Session.h>
|
|
||||||
#include <vector>
|
namespace Wt::Dbo
|
||||||
|
{
|
||||||
|
class Session;
|
||||||
|
}
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
@@ -42,20 +45,20 @@ namespace lms::db
|
|||||||
Write,
|
Write,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void pushWriteTransaction(Wt::Dbo::Session& session);
|
static void pushWriteTransaction(const Wt::Dbo::Session& session);
|
||||||
static void pushReadTransaction(Wt::Dbo::Session& session);
|
static void pushReadTransaction(const Wt::Dbo::Session& session);
|
||||||
|
|
||||||
static void popWriteTransaction(Wt::Dbo::Session& session);
|
static void popWriteTransaction(const Wt::Dbo::Session& session);
|
||||||
static void popReadTransaction(Wt::Dbo::Session& session);
|
static void popReadTransaction(const Wt::Dbo::Session& session);
|
||||||
|
|
||||||
static void checkWriteTransaction(Wt::Dbo::Session& session);
|
static void checkWriteTransaction(const Wt::Dbo::Session& session);
|
||||||
static void checkWriteTransaction(Session& session);
|
static void checkWriteTransaction(const Session& session);
|
||||||
static void checkReadTransaction(Wt::Dbo::Session& session);
|
static void checkReadTransaction(const Wt::Dbo::Session& session);
|
||||||
static void checkReadTransaction(Session& session);
|
static void checkReadTransaction(const Session& session);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void pushTransaction(TransactionType type, Wt::Dbo::Session& session);
|
static void pushTransaction(TransactionType type, const Wt::Dbo::Session& session);
|
||||||
static void popTransaction(TransactionType type, Wt::Dbo::Session& session);
|
static void popTransaction(TransactionType type, const Wt::Dbo::Session& session);
|
||||||
};
|
};
|
||||||
} // namespace lms::db
|
} // namespace lms::db
|
||||||
|
|
||||||
@@ -19,28 +19,46 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
#include <Wt/Dbo/Dbo.h>
|
#include <Wt/Dbo/Call.h>
|
||||||
|
#include <Wt/Dbo/Query.h>
|
||||||
|
#include <Wt/Dbo/Session.h>
|
||||||
|
#include <Wt/Dbo/collection.h>
|
||||||
#include <Wt/WDateTime.h>
|
#include <Wt/WDateTime.h>
|
||||||
|
|
||||||
#include "core/ITraceLogger.hpp"
|
#include "core/ITraceLogger.hpp"
|
||||||
|
#include "core/Service.hpp"
|
||||||
#include "database/Types.hpp"
|
#include "database/Types.hpp"
|
||||||
|
|
||||||
|
#include "QueryPlanRecorder.hpp"
|
||||||
|
|
||||||
namespace lms::db::utils
|
namespace lms::db::utils
|
||||||
{
|
{
|
||||||
#define ESCAPE_CHAR_STR "\\"
|
#define ESCAPE_CHAR_STR "\\"
|
||||||
static inline constexpr char escapeChar{ '\\' };
|
static inline constexpr char escapeChar{ '\\' };
|
||||||
std::string escapeLikeKeyword(std::string_view keywords);
|
std::string escapeLikeKeyword(std::string_view keywords);
|
||||||
|
|
||||||
|
Wt::WDateTime normalizeDateTime(const Wt::WDateTime& dateTime);
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
template<typename Query>
|
||||||
|
void recordQueryPlanIfNeeded(const Query& query)
|
||||||
|
{
|
||||||
|
if (IQueryPlanRecorder * recorder{ core::Service<IQueryPlanRecorder>::get() })
|
||||||
|
static_cast<QueryPlanRecorder*>(recorder)->recordQueryPlanIfNeeded(query.session(), query.asString());
|
||||||
|
}
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
template<typename Query>
|
template<typename Query>
|
||||||
void applyRange(Query& query, std::optional<Range> range)
|
void applyRange(Query& query, std::optional<Range> range)
|
||||||
{
|
{
|
||||||
if (range)
|
if (range)
|
||||||
{
|
{
|
||||||
query.limit(static_cast<int>(range->size));
|
query.limit(static_cast<int>(range->size));
|
||||||
|
if (range->offset != 0)
|
||||||
query.offset(static_cast<int>(range->offset));
|
query.offset(static_cast<int>(range->offset));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,13 +100,18 @@ namespace lms::db::utils
|
|||||||
template<typename Query, typename UnaryFunc>
|
template<typename Query, typename UnaryFunc>
|
||||||
void forEachQueryResult(const Query& query, UnaryFunc&& func)
|
void forEachQueryResult(const Query& query, UnaryFunc&& func)
|
||||||
{
|
{
|
||||||
|
details::recordQueryPlanIfNeeded(query);
|
||||||
|
|
||||||
LMS_SCOPED_TRACE_DETAILED_WITH_ARG("Database", "ForEachQueryResult", "Query", query.asString());
|
LMS_SCOPED_TRACE_DETAILED_WITH_ARG("Database", "ForEachQueryResult", "Query", query.asString());
|
||||||
|
|
||||||
forEachResult(query.resultList(), std::forward<UnaryFunc>(func));
|
forEachResult(query.resultList(), std::forward<UnaryFunc>(func));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename Query>
|
template<typename T, typename Query>
|
||||||
std::vector<T> fetchQueryResults(const Query& query)
|
std::vector<T> fetchQueryResults(const Query& query)
|
||||||
{
|
{
|
||||||
|
details::recordQueryPlanIfNeeded(query);
|
||||||
|
|
||||||
LMS_SCOPED_TRACE_DETAILED_WITH_ARG("Database", "FetchQueryResults", "Query", query.asString());
|
LMS_SCOPED_TRACE_DETAILED_WITH_ARG("Database", "FetchQueryResults", "Query", query.asString());
|
||||||
|
|
||||||
auto collection{ query.resultList() };
|
auto collection{ query.resultList() };
|
||||||
@@ -98,6 +121,8 @@ namespace lms::db::utils
|
|||||||
template<typename Query>
|
template<typename Query>
|
||||||
std::vector<typename QueryResultType<Query>::type> fetchQueryResults(const Query& query)
|
std::vector<typename QueryResultType<Query>::type> fetchQueryResults(const Query& query)
|
||||||
{
|
{
|
||||||
|
details::recordQueryPlanIfNeeded(query);
|
||||||
|
|
||||||
LMS_SCOPED_TRACE_DETAILED_WITH_ARG("Database", "FetchQueryResults", "Query", query.asString());
|
LMS_SCOPED_TRACE_DETAILED_WITH_ARG("Database", "FetchQueryResults", "Query", query.asString());
|
||||||
|
|
||||||
auto collection{ query.resultList() };
|
auto collection{ query.resultList() };
|
||||||
@@ -107,6 +132,8 @@ namespace lms::db::utils
|
|||||||
template<typename Query>
|
template<typename Query>
|
||||||
auto fetchQuerySingleResult(const Query& query)
|
auto fetchQuerySingleResult(const Query& query)
|
||||||
{
|
{
|
||||||
|
details::recordQueryPlanIfNeeded(query);
|
||||||
|
|
||||||
LMS_SCOPED_TRACE_DETAILED_WITH_ARG("Database", "FetchQuerySingleResult", "Query", query.asString());
|
LMS_SCOPED_TRACE_DETAILED_WITH_ARG("Database", "FetchQuerySingleResult", "Query", query.asString());
|
||||||
return query.resultValue();
|
return query.resultValue();
|
||||||
}
|
}
|
||||||
@@ -175,11 +202,12 @@ namespace lms::db::utils
|
|||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void executeCommand(Wt::Dbo::Session& session, std::string_view command, const Args&... args)
|
void executeCommand(Wt::Dbo::Session& session, std::string_view command, const Args&... args)
|
||||||
{
|
{
|
||||||
LMS_SCOPED_TRACE_DETAILED_WITH_ARG("Database", "ExecuteCommand", "Command", command);
|
|
||||||
|
|
||||||
Wt::Dbo::Call call{ session.execute(std::string{ command }) };
|
Wt::Dbo::Call call{ session.execute(std::string{ command }) };
|
||||||
(call.bind(args), ...);
|
(call.bind(args), ...);
|
||||||
}
|
|
||||||
|
|
||||||
Wt::WDateTime normalizeDateTime(const Wt::WDateTime& dateTime);
|
{
|
||||||
|
LMS_SCOPED_TRACE_DETAILED_WITH_ARG("Database", "ExecuteCommand", "Command", command);
|
||||||
|
call.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
} // namespace lms::db::utils
|
} // namespace lms::db::utils
|
||||||
@@ -16,22 +16,28 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include "database/Artist.hpp"
|
#include "database/objects/Artist.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
#include <Wt/Dbo/WtSqlTraits.h>
|
#include <Wt/Dbo/WtSqlTraits.h>
|
||||||
|
|
||||||
#include "core/ILogger.hpp"
|
#include "core/ILogger.hpp"
|
||||||
#include "database/Artwork.hpp"
|
|
||||||
#include "database/Cluster.hpp"
|
|
||||||
#include "database/Directory.hpp"
|
|
||||||
#include "database/Release.hpp"
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/Track.hpp"
|
#include "database/objects/Artwork.hpp"
|
||||||
#include "database/User.hpp"
|
#include "database/objects/Cluster.hpp"
|
||||||
|
#include "database/objects/Directory.hpp"
|
||||||
|
#include "database/objects/Release.hpp"
|
||||||
|
#include "database/objects/StarredArtist.hpp"
|
||||||
|
#include "database/objects/Track.hpp"
|
||||||
|
#include "database/objects/TrackArtistLink.hpp"
|
||||||
|
#include "database/objects/User.hpp"
|
||||||
|
|
||||||
#include "SqlQuery.hpp"
|
#include "SqlQuery.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
|
#include "traits/StringViewTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::Artist)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
@@ -237,11 +243,25 @@ namespace lms::db
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Artist::find(Session& session, const IdRange<ArtistId>& idRange, const std::function<void(const Artist::pointer&)>& func)
|
||||||
|
{
|
||||||
|
assert(idRange.isValid());
|
||||||
|
|
||||||
|
auto query{ session.getDboSession()->query<Wt::Dbo::ptr<Artist>>("SELECT a from artist a").orderBy("a.id").where("a.id BETWEEN ? AND ?").bind(idRange.first).bind(idRange.last) };
|
||||||
|
|
||||||
|
utils::forEachQueryResult(query, [&](const Artist::pointer& artist) {
|
||||||
|
func(artist);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Artist::pointer> Artist::find(Session& session, std::string_view name)
|
std::vector<Artist::pointer> Artist::find(Session& session, std::string_view name)
|
||||||
{
|
{
|
||||||
session.checkReadTransaction();
|
session.checkReadTransaction();
|
||||||
|
|
||||||
return utils::fetchQueryResults<Artist::pointer>(session.getDboSession()->query<Wt::Dbo::ptr<Artist>>("SELECT a FROM artist a").where("a.name = ?").bind(std::string{ name, 0, _maxNameLength }).orderBy("LENGTH(a.mbid) DESC")); // put mbid entries first
|
if (name.size() > maxNameLength)
|
||||||
|
name = name.substr(0, maxNameLength);
|
||||||
|
|
||||||
|
return utils::fetchQueryResults<Artist::pointer>(session.getDboSession()->query<Wt::Dbo::ptr<Artist>>("SELECT a FROM artist a").where("a.name = ?").bind(name).orderBy("LENGTH(a.mbid) DESC")); // put mbid entries first
|
||||||
}
|
}
|
||||||
|
|
||||||
Artist::pointer Artist::find(Session& session, const core::UUID& mbid)
|
Artist::pointer Artist::find(Session& session, const core::UUID& mbid)
|
||||||
@@ -280,6 +300,16 @@ namespace lms::db
|
|||||||
utils::forEachQueryRangeResult(query, params.range, func);
|
utils::forEachQueryRangeResult(query, params.range, func);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IdRange<ArtistId> Artist::findNextIdRange(Session& session, ArtistId lastRetrievedId, std::size_t count)
|
||||||
|
{
|
||||||
|
auto query{ session.getDboSession()->query<std::tuple<ArtistId, ArtistId>>("SELECT MIN(sub.id) AS first_id, MAX(sub.id) AS last_id FROM (SELECT a.id FROM artist a WHERE a.id > ? ORDER BY a.id LIMIT ?) sub") };
|
||||||
|
query.bind(lastRetrievedId);
|
||||||
|
query.bind(static_cast<int>(count));
|
||||||
|
|
||||||
|
auto res{ utils::fetchQuerySingleResult(query) };
|
||||||
|
return IdRange<ArtistId>{ .first = std::get<0>(res), .last = std::get<1>(res) };
|
||||||
|
}
|
||||||
|
|
||||||
RangeResults<ArtistId> Artist::findOrphanIds(Session& session, std::optional<Range> range)
|
RangeResults<ArtistId> Artist::findOrphanIds(Session& session, std::optional<Range> range)
|
||||||
{
|
{
|
||||||
session.checkReadTransaction();
|
session.checkReadTransaction();
|
||||||
@@ -304,6 +334,16 @@ AND NOT EXISTS (
|
|||||||
return utils::fetchQuerySingleResult(session.getDboSession()->query<int>("SELECT 1 FROM artist").where("id = ?").bind(id)) == 1;
|
return utils::fetchQuerySingleResult(session.getDboSession()->query<int>("SELECT 1 FROM artist").where("id = ?").bind(id)) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Artist::updatePreferredArtwork(Session& session, ArtistId artistId, ArtworkId artworkId)
|
||||||
|
{
|
||||||
|
session.checkWriteTransaction();
|
||||||
|
|
||||||
|
if (artworkId.isValid())
|
||||||
|
utils::executeCommand(*session.getDboSession(), "UPDATE artist SET preferred_artwork_id = ? WHERE id = ?", artworkId, artistId);
|
||||||
|
else
|
||||||
|
utils::executeCommand(*session.getDboSession(), "UPDATE artist SET preferred_artwork_id = NULL WHERE id = ?", artistId);
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<core::UUID> Artist::getMBID() const
|
std::optional<core::UUID> Artist::getMBID() const
|
||||||
{
|
{
|
||||||
return core::UUID::fromString(_mbid);
|
return core::UUID::fromString(_mbid);
|
||||||
@@ -408,17 +448,14 @@ AND NOT EXISTS (
|
|||||||
|
|
||||||
void Artist::setName(std::string_view name)
|
void Artist::setName(std::string_view name)
|
||||||
{
|
{
|
||||||
_name.assign(name, 0, _maxNameLength);
|
_name.assign(name, 0, maxNameLength);
|
||||||
if (name.size() > _maxNameLength)
|
LMS_LOG_IF(DB, WARNING, name.size() > maxNameLength, "Artist name too long, truncated to '" << _name << "'");
|
||||||
LMS_LOG(DB, WARNING, "Artist name too long, truncated to '" << _name << "'");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Artist::setSortName(std::string_view sortName)
|
void Artist::setSortName(std::string_view sortName)
|
||||||
{
|
{
|
||||||
_sortName.assign(sortName, 0, _maxNameLength);
|
_sortName.assign(sortName, 0, maxNameLength);
|
||||||
|
LMS_LOG_IF(DB, WARNING, sortName.size() > maxNameLength, "Artist sort name too long, truncated to '" << _sortName << "'");
|
||||||
if (sortName.size() > _maxNameLength)
|
|
||||||
LMS_LOG(DB, WARNING, "Artist sort name too long, truncated to '" << _sortName << "'");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Artist::setPreferredArtwork(ObjectPtr<Artwork> artwork)
|
void Artist::setPreferredArtwork(ObjectPtr<Artwork> artwork)
|
||||||
+19
-4
@@ -17,19 +17,22 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/ArtistInfo.hpp"
|
#include "database/objects/ArtistInfo.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
#include <Wt/Dbo/WtSqlTraits.h>
|
#include <Wt/Dbo/WtSqlTraits.h>
|
||||||
|
|
||||||
#include "database/Artist.hpp"
|
|
||||||
#include "database/ArtistInfoId.hpp"
|
|
||||||
#include "database/Directory.hpp"
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
|
#include "database/objects/Artist.hpp"
|
||||||
|
#include "database/objects/ArtistInfoId.hpp"
|
||||||
|
#include "database/objects/Directory.hpp"
|
||||||
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
#include "traits/PathTraits.hpp"
|
#include "traits/PathTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::ArtistInfo)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
ArtistInfo::pointer ArtistInfo::create(Session& session)
|
ArtistInfo::pointer ArtistInfo::create(Session& session)
|
||||||
@@ -124,6 +127,18 @@ namespace lms::db
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ArtistInfo::findAbsoluteFilePath(Session& session, ArtistInfoId& lastRetrievedId, std::size_t count, const std::function<void(ArtistInfoId artistInfoId, const std::filesystem::path& absoluteFilePath)>& func)
|
||||||
|
{
|
||||||
|
session.checkReadTransaction();
|
||||||
|
|
||||||
|
auto query{ session.getDboSession()->query<std::tuple<ArtistInfoId, std::filesystem::path>>("SELECT a_i.id, a_i.absolute_file_path FROM artist_info a_i").orderBy("a_i.id").where("a_i.id > ?").bind(lastRetrievedId).limit(static_cast<int>(count)) };
|
||||||
|
|
||||||
|
utils::forEachQueryResult(query, [&](const auto& res) {
|
||||||
|
func(std::get<0>(res), std::get<1>(res));
|
||||||
|
lastRetrievedId = std::get<0>(res);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Artist::pointer ArtistInfo::getArtist() const
|
Artist::pointer ArtistInfo::getArtist() const
|
||||||
{
|
{
|
||||||
return _artist;
|
return _artist;
|
||||||
@@ -17,16 +17,20 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/Artwork.hpp"
|
#include "database/objects/Artwork.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
#include <Wt/Dbo/WtSqlTraits.h>
|
#include <Wt/Dbo/WtSqlTraits.h>
|
||||||
|
|
||||||
#include "database/Image.hpp"
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/TrackEmbeddedImage.hpp"
|
#include "database/objects/Image.hpp"
|
||||||
|
#include "database/objects/TrackEmbeddedImage.hpp"
|
||||||
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
|
#include "traits/PathTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::Artwork)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
@@ -87,4 +91,17 @@ namespace lms::db
|
|||||||
|
|
||||||
return utils::fetchQuerySingleResult(query);
|
return utils::fetchQuerySingleResult(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::filesystem::path Artwork::getAbsoluteFilePath() const
|
||||||
|
{
|
||||||
|
auto query{ session()->query<std::filesystem::path>("SELECT COALESCE(image.absolute_file_path, track.absolute_file_path) AS absolute_file_path FROM artwork") };
|
||||||
|
query.leftJoin("image ON artwork.image_id = image.id");
|
||||||
|
query.leftJoin("track_embedded_image ON artwork.track_embedded_image_id = track_embedded_image.id");
|
||||||
|
query.leftJoin("track_embedded_image_link ON track_embedded_image.id = track_embedded_image_link.track_embedded_image_id");
|
||||||
|
query.leftJoin("track ON track.id = track_embedded_image_link.track_id");
|
||||||
|
query.limit(1); // there may be several tracks matching this artwork...
|
||||||
|
query.where("artwork.id = ?").bind(getId());
|
||||||
|
|
||||||
|
return utils::fetchQuerySingleResult(query);
|
||||||
|
}
|
||||||
} // namespace lms::db
|
} // namespace lms::db
|
||||||
@@ -17,17 +17,20 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/AuthToken.hpp"
|
#include "database/objects/AuthToken.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
#include <Wt/Dbo/WtSqlTraits.h>
|
#include <Wt/Dbo/WtSqlTraits.h>
|
||||||
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/User.hpp"
|
#include "database/objects/User.hpp"
|
||||||
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
#include "traits/StringViewTraits.hpp"
|
#include "traits/StringViewTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::AuthToken)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
AuthToken::AuthToken(std::string_view domain, std::string_view value, const Wt::WDateTime& expiry, std::optional<long> maxUseCount, ObjectPtr<User> user)
|
AuthToken::AuthToken(std::string_view domain, std::string_view value, const Wt::WDateTime& expiry, std::optional<long> maxUseCount, ObjectPtr<User> user)
|
||||||
@@ -17,22 +17,29 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/Cluster.hpp"
|
#include "database/objects/Cluster.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
|
|
||||||
#include "database/Artist.hpp"
|
|
||||||
#include "database/Artwork.hpp"
|
|
||||||
#include "database/Directory.hpp"
|
|
||||||
#include "database/MediaLibrary.hpp"
|
|
||||||
#include "database/Release.hpp"
|
|
||||||
#include "database/ScanSettings.hpp"
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/Track.hpp"
|
#include "database/objects/Artist.hpp"
|
||||||
|
#include "database/objects/Artwork.hpp"
|
||||||
|
#include "database/objects/Directory.hpp"
|
||||||
|
#include "database/objects/MediaLibrary.hpp"
|
||||||
|
#include "database/objects/Release.hpp"
|
||||||
|
#include "database/objects/Track.hpp"
|
||||||
|
#include "database/objects/TrackArtistLink.hpp"
|
||||||
|
#include "database/objects/TrackEmbeddedImage.hpp"
|
||||||
|
#include "database/objects/TrackEmbeddedImageLink.hpp"
|
||||||
|
#include "database/objects/TrackLyrics.hpp"
|
||||||
|
|
||||||
#include "SqlQuery.hpp"
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
#include "traits/StringViewTraits.hpp"
|
#include "traits/StringViewTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::Cluster)
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::ClusterType)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
@@ -167,7 +174,7 @@ namespace lms::db
|
|||||||
{
|
{
|
||||||
session.checkReadTransaction();
|
session.checkReadTransaction();
|
||||||
|
|
||||||
return utils::fetchQuerySingleResult(session.getDboSession()->query<int>("SELECT COUNT(DISTINCT r.id) FROM release r INNER JOIN track t on t.release_id = r.id INNER JOIN track_cluster t_c ON t_c.track_id = t.id").where("t_c.cluster_id = ?").bind(id));
|
return utils::fetchQuerySingleResult(session.getDboSession()->query<int>("SELECT COUNT(DISTINCT t.release_id) FROM track t INNER JOIN track_cluster t_c ON t_c.track_id = t.id").where("t_c.cluster_id = ?").bind(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cluster::addTrack(ObjectPtr<Track> track)
|
void Cluster::addTrack(ObjectPtr<Track> track)
|
||||||
@@ -17,16 +17,20 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/Directory.hpp"
|
#include "database/objects/Directory.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
|
|
||||||
#include "database/MediaLibrary.hpp"
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/Types.hpp"
|
#include "database/Types.hpp"
|
||||||
|
#include "database/objects/MediaLibrary.hpp"
|
||||||
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
#include "traits/PathTraits.hpp"
|
#include "traits/PathTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::Directory)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
@@ -17,18 +17,21 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/Image.hpp"
|
#include "database/objects/Image.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
#include <Wt/Dbo/WtSqlTraits.h>
|
#include <Wt/Dbo/WtSqlTraits.h>
|
||||||
|
|
||||||
#include "database/Artist.hpp"
|
|
||||||
#include "database/Directory.hpp"
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
|
#include "database/objects/Artist.hpp"
|
||||||
|
#include "database/objects/Directory.hpp"
|
||||||
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
#include "traits/PathTraits.hpp"
|
#include "traits/PathTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::Image)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
@@ -77,15 +80,15 @@ namespace lms::db
|
|||||||
return utils::fetchQuerySingleResult(session.getDboSession()->query<Wt::Dbo::ptr<Image>>("SELECT i from image i").where("i.absolute_file_path = ?").bind(file));
|
return utils::fetchQuerySingleResult(session.getDboSession()->query<Wt::Dbo::ptr<Image>>("SELECT i from image i").where("i.absolute_file_path = ?").bind(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Image::find(Session& session, ImageId& lastRetrievedImage, std::size_t count, const std::function<void(const Image::pointer&)>& func)
|
void Image::find(Session& session, ImageId& lastRetrievedId, std::size_t count, const std::function<void(const Image::pointer&)>& func)
|
||||||
{
|
{
|
||||||
session.checkReadTransaction();
|
session.checkReadTransaction();
|
||||||
|
|
||||||
auto query{ session.getDboSession()->query<Wt::Dbo::ptr<Image>>("SELECT i from image i").orderBy("i.id").where("i.id > ?").bind(lastRetrievedImage).limit(static_cast<int>(count)) };
|
auto query{ session.getDboSession()->query<Wt::Dbo::ptr<Image>>("SELECT i from image i").orderBy("i.id").where("i.id > ?").bind(lastRetrievedId).limit(static_cast<int>(count)) };
|
||||||
|
|
||||||
utils::forEachQueryResult(query, [&](const Image::pointer& image) {
|
utils::forEachQueryResult(query, [&](const Image::pointer& image) {
|
||||||
func(image);
|
func(image);
|
||||||
lastRetrievedImage = image->getId();
|
lastRetrievedId = image->getId();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,6 +108,18 @@ namespace lms::db
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Image::findAbsoluteFilePath(Session& session, ImageId& lastRetrievedId, std::size_t count, const std::function<void(ImageId imageId, const std::filesystem::path& absoluteFilePath)>& func)
|
||||||
|
{
|
||||||
|
session.checkReadTransaction();
|
||||||
|
|
||||||
|
auto query{ session.getDboSession()->query<std::tuple<ImageId, std::filesystem::path>>("SELECT i.id,i.absolute_file_path from image i").orderBy("i.id").where("i.id > ?").bind(lastRetrievedId).limit(static_cast<int>(count)) };
|
||||||
|
|
||||||
|
utils::forEachQueryResult(query, [&](const auto& res) {
|
||||||
|
func(std::get<0>(res), std::get<1>(res));
|
||||||
|
lastRetrievedId = std::get<0>(res);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void Image::setAbsoluteFilePath(const std::filesystem::path& p)
|
void Image::setAbsoluteFilePath(const std::filesystem::path& p)
|
||||||
{
|
{
|
||||||
assert(p.is_absolute());
|
assert(p.is_absolute());
|
||||||
@@ -17,15 +17,21 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/Listen.hpp"
|
#include "database/objects/Listen.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
|
#include <Wt/Dbo/WtSqlTraits.h>
|
||||||
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/Track.hpp"
|
#include "database/objects/Track.hpp"
|
||||||
#include "database/User.hpp"
|
#include "database/objects/User.hpp"
|
||||||
|
|
||||||
#include "SqlQuery.hpp"
|
#include "SqlQuery.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::Listen)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
+6
-3
@@ -17,17 +17,20 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/MediaLibrary.hpp"
|
#include "database/objects/MediaLibrary.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
|
|
||||||
#include "database/ScanSettings.hpp"
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/Track.hpp"
|
#include "database/objects/Track.hpp"
|
||||||
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
#include "traits/PathTraits.hpp"
|
#include "traits/PathTraits.hpp"
|
||||||
#include "traits/StringViewTraits.hpp"
|
#include "traits/StringViewTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::MediaLibrary)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
MediaLibrary::MediaLibrary(std::string_view name, const std::filesystem::path& p)
|
MediaLibrary::MediaLibrary(std::string_view name, const std::filesystem::path& p)
|
||||||
+43
-7
@@ -17,22 +17,26 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/PlayListFile.hpp"
|
#include "database/objects/PlayListFile.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
#include <Wt/Dbo/WtSqlTraits.h>
|
#include <Wt/Dbo/WtSqlTraits.h>
|
||||||
|
|
||||||
#include "core/ILogger.hpp"
|
#include "core/ILogger.hpp"
|
||||||
#include "database/Directory.hpp"
|
|
||||||
#include "database/MediaLibrary.hpp"
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/TrackList.hpp"
|
#include "database/objects/Directory.hpp"
|
||||||
#include "database/User.hpp"
|
#include "database/objects/MediaLibrary.hpp"
|
||||||
|
#include "database/objects/Track.hpp"
|
||||||
|
#include "database/objects/TrackList.hpp"
|
||||||
|
#include "database/objects/User.hpp"
|
||||||
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
#include "traits/PathTraits.hpp"
|
#include "traits/PathTraits.hpp"
|
||||||
#include "traits/StringViewTraits.hpp"
|
#include "traits/StringViewTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::PlayListFile)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
PlayListFile::PlayListFile(const std::filesystem::path& file)
|
PlayListFile::PlayListFile(const std::filesystem::path& file)
|
||||||
@@ -71,6 +75,39 @@ namespace lms::db
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PlayListFile::findAbsoluteFilePath(Session& session, PlayListFileId& lastRetrievedId, std::size_t count, const std::function<void(PlayListFileId playListFileId, const std::filesystem::path& absoluteFilePath)>& func)
|
||||||
|
{
|
||||||
|
session.checkReadTransaction();
|
||||||
|
|
||||||
|
auto query{ session.getDboSession()->query<std::tuple<PlayListFileId, std::filesystem::path>>("SELECT pl_f.id, pl_f.absolute_file_path FROM playlist_file pl_f").orderBy("pl_f.id").where("pl_f.id > ?").bind(lastRetrievedId).limit(static_cast<int>(count)) };
|
||||||
|
|
||||||
|
utils::forEachQueryResult(query, [&](const auto& res) {
|
||||||
|
func(std::get<0>(res), std::get<1>(res));
|
||||||
|
lastRetrievedId = std::get<0>(res);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayListFile::find(Session& session, const IdRange<PlayListFileId>& idRange, const std::function<void(const PlayListFile::pointer&)>& func)
|
||||||
|
{
|
||||||
|
assert(idRange.isValid());
|
||||||
|
|
||||||
|
auto query{ session.getDboSession()->query<Wt::Dbo::ptr<PlayListFile>>("SELECT pl_f from playlist_file pl_f").orderBy("pl_f.id").where("pl_f.id BETWEEN ? AND ?").bind(idRange.first).bind(idRange.last) };
|
||||||
|
|
||||||
|
utils::forEachQueryResult(query, [&](const PlayListFile::pointer& release) {
|
||||||
|
func(release);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
IdRange<PlayListFileId> PlayListFile::findNextIdRange(Session& session, PlayListFileId lastRetrievedId, std::size_t count)
|
||||||
|
{
|
||||||
|
auto query{ session.getDboSession()->query<std::tuple<PlayListFileId, PlayListFileId>>("SELECT MIN(sub.id) AS first_id, MAX(sub.id) AS last_id FROM (SELECT pl_f.id FROM playlist_file pl_f WHERE pl_f.id > ? ORDER BY pl_f.id LIMIT ?) sub") };
|
||||||
|
query.bind(lastRetrievedId);
|
||||||
|
query.bind(static_cast<int>(count));
|
||||||
|
|
||||||
|
auto res{ utils::fetchQuerySingleResult(query) };
|
||||||
|
return IdRange<PlayListFileId>{ .first = std::get<0>(res), .last = std::get<1>(res) };
|
||||||
|
}
|
||||||
|
|
||||||
PlayListFile::pointer PlayListFile::find(Session& session, PlayListFileId id)
|
PlayListFile::pointer PlayListFile::find(Session& session, PlayListFileId id)
|
||||||
{
|
{
|
||||||
session.checkReadTransaction();
|
session.checkReadTransaction();
|
||||||
@@ -130,8 +167,7 @@ namespace lms::db
|
|||||||
void PlayListFile::setName(std::string_view name)
|
void PlayListFile::setName(std::string_view name)
|
||||||
{
|
{
|
||||||
_name = std::string{ name, 0, _maxNameLength };
|
_name = std::string{ name, 0, _maxNameLength };
|
||||||
if (name.size() > _maxNameLength)
|
LMS_LOG_IF(DB, WARNING, name.size() > _maxNameLength, "PlaylistFile name too long, truncated to '" << _name << "'");
|
||||||
LMS_LOG(DB, WARNING, "PlaylistFile name too long, truncated to '" << _name << "'");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayListFile::setFiles(std::span<const std::filesystem::path> files)
|
void PlayListFile::setFiles(std::span<const std::filesystem::path> files)
|
||||||
+17
-8
@@ -17,22 +17,31 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/PlayQueue.hpp"
|
#include "database/objects/PlayQueue.hpp"
|
||||||
|
|
||||||
#include <Wt/Dbo/SqlTraits.h>
|
#include <Wt/Dbo/Impl.h>
|
||||||
|
#include <Wt/Dbo/WtSqlTraits.h>
|
||||||
|
|
||||||
#include "database/Artwork.hpp"
|
|
||||||
#include "database/Directory.hpp"
|
|
||||||
#include "database/MediaLibrary.hpp"
|
|
||||||
#include "database/Release.hpp"
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/Track.hpp"
|
#include "database/objects/Artist.hpp"
|
||||||
#include "database/User.hpp"
|
#include "database/objects/Artwork.hpp"
|
||||||
|
#include "database/objects/Cluster.hpp"
|
||||||
|
#include "database/objects/Directory.hpp"
|
||||||
|
#include "database/objects/MediaLibrary.hpp"
|
||||||
|
#include "database/objects/Release.hpp"
|
||||||
|
#include "database/objects/Track.hpp"
|
||||||
|
#include "database/objects/TrackArtistLink.hpp"
|
||||||
|
#include "database/objects/TrackEmbeddedImage.hpp"
|
||||||
|
#include "database/objects/TrackEmbeddedImageLink.hpp"
|
||||||
|
#include "database/objects/TrackLyrics.hpp"
|
||||||
|
#include "database/objects/User.hpp"
|
||||||
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
#include "traits/StringViewTraits.hpp"
|
#include "traits/StringViewTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::PlayQueue)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
PlayQueue::PlayQueue(const ObjectPtr<User>& user, std::string_view name)
|
PlayQueue::PlayQueue(const ObjectPtr<User>& user, std::string_view name)
|
||||||
+6
-3
@@ -17,17 +17,20 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/RatedArtist.hpp"
|
#include "database/objects/RatedArtist.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
#include <Wt/Dbo/WtSqlTraits.h>
|
#include <Wt/Dbo/WtSqlTraits.h>
|
||||||
|
|
||||||
#include "database/Artist.hpp"
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/User.hpp"
|
#include "database/objects/Artist.hpp"
|
||||||
|
#include "database/objects/User.hpp"
|
||||||
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::RatedArtist)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
RatedArtist::RatedArtist(ObjectPtr<Artist> artist, ObjectPtr<User> user)
|
RatedArtist::RatedArtist(ObjectPtr<Artist> artist, ObjectPtr<User> user)
|
||||||
+6
-3
@@ -17,17 +17,20 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/RatedRelease.hpp"
|
#include "database/objects/RatedRelease.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
#include <Wt/Dbo/WtSqlTraits.h>
|
#include <Wt/Dbo/WtSqlTraits.h>
|
||||||
|
|
||||||
#include "database/Release.hpp"
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/User.hpp"
|
#include "database/objects/Release.hpp"
|
||||||
|
#include "database/objects/User.hpp"
|
||||||
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::RatedRelease)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
RatedRelease::RatedRelease(ObjectPtr<Release> release, ObjectPtr<User> user)
|
RatedRelease::RatedRelease(ObjectPtr<Release> release, ObjectPtr<User> user)
|
||||||
+6
-3
@@ -17,17 +17,20 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/RatedTrack.hpp"
|
#include "database/objects/RatedTrack.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
#include <Wt/Dbo/WtSqlTraits.h>
|
#include <Wt/Dbo/WtSqlTraits.h>
|
||||||
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/Track.hpp"
|
#include "database/objects/Track.hpp"
|
||||||
#include "database/User.hpp"
|
#include "database/objects/User.hpp"
|
||||||
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::RatedTrack)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
RatedTrack::RatedTrack(ObjectPtr<Track> track, ObjectPtr<User> user)
|
RatedTrack::RatedTrack(ObjectPtr<Track> track, ObjectPtr<User> user)
|
||||||
@@ -17,19 +17,25 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/Release.hpp"
|
#include "database/objects/Release.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
#include <Wt/Dbo/WtSqlTraits.h>
|
#include <Wt/Dbo/WtSqlTraits.h>
|
||||||
|
|
||||||
#include "core/PartialDateTime.hpp"
|
#include "core/PartialDateTime.hpp"
|
||||||
#include "database/Artist.hpp"
|
|
||||||
#include "database/Artwork.hpp"
|
|
||||||
#include "database/Cluster.hpp"
|
|
||||||
#include "database/Directory.hpp"
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/Track.hpp"
|
|
||||||
#include "database/Types.hpp"
|
#include "database/Types.hpp"
|
||||||
#include "database/User.hpp"
|
#include "database/objects/Artist.hpp"
|
||||||
|
#include "database/objects/Artwork.hpp"
|
||||||
|
#include "database/objects/Cluster.hpp"
|
||||||
|
#include "database/objects/Directory.hpp"
|
||||||
|
#include "database/objects/MediaLibrary.hpp"
|
||||||
|
#include "database/objects/Track.hpp"
|
||||||
|
#include "database/objects/TrackArtistLink.hpp"
|
||||||
|
#include "database/objects/TrackEmbeddedImage.hpp"
|
||||||
|
#include "database/objects/TrackEmbeddedImageLink.hpp"
|
||||||
|
#include "database/objects/TrackLyrics.hpp"
|
||||||
|
#include "database/objects/User.hpp"
|
||||||
|
|
||||||
#include "SqlQuery.hpp"
|
#include "SqlQuery.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
@@ -38,6 +44,11 @@
|
|||||||
#include "traits/PartialDateTimeTraits.hpp"
|
#include "traits/PartialDateTimeTraits.hpp"
|
||||||
#include "traits/StringViewTraits.hpp"
|
#include "traits/StringViewTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::Country)
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::Label)
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::Release)
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::ReleaseType)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
@@ -504,6 +515,27 @@ namespace lms::db
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Release::find(Session& session, const IdRange<ReleaseId>& idRange, const std::function<void(const Release::pointer&)>& func)
|
||||||
|
{
|
||||||
|
assert(idRange.isValid());
|
||||||
|
|
||||||
|
auto query{ session.getDboSession()->query<Wt::Dbo::ptr<Release>>("SELECT r from release r").orderBy("r.id").where("r.id BETWEEN ? AND ?").bind(idRange.first).bind(idRange.last) };
|
||||||
|
|
||||||
|
utils::forEachQueryResult(query, [&](const Release::pointer& release) {
|
||||||
|
func(release);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
IdRange<ReleaseId> Release::findNextIdRange(Session& session, ReleaseId lastRetrievedId, std::size_t count)
|
||||||
|
{
|
||||||
|
auto query{ session.getDboSession()->query<std::tuple<ReleaseId, ReleaseId>>("SELECT MIN(sub.id) AS first_id, MAX(sub.id) AS last_id FROM (SELECT r.id FROM release r WHERE r.id > ? ORDER BY r.id LIMIT ?) sub") };
|
||||||
|
query.bind(lastRetrievedId);
|
||||||
|
query.bind(static_cast<int>(count));
|
||||||
|
|
||||||
|
auto res{ utils::fetchQuerySingleResult(query) };
|
||||||
|
return IdRange<ReleaseId>{ .first = std::get<0>(res), .last = std::get<1>(res) };
|
||||||
|
}
|
||||||
|
|
||||||
RangeResults<Release::pointer> Release::find(Session& session, const FindParameters& params)
|
RangeResults<Release::pointer> Release::find(Session& session, const FindParameters& params)
|
||||||
{
|
{
|
||||||
session.checkReadTransaction();
|
session.checkReadTransaction();
|
||||||
@@ -528,6 +560,16 @@ namespace lms::db
|
|||||||
return utils::execRangeQuery<ReleaseId>(query, params.range);
|
return utils::execRangeQuery<ReleaseId>(query, params.range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Release::updatePreferredArtwork(Session& session, ReleaseId releaseId, ArtworkId artworkId)
|
||||||
|
{
|
||||||
|
session.checkWriteTransaction();
|
||||||
|
|
||||||
|
if (artworkId.isValid())
|
||||||
|
utils::executeCommand(*session.getDboSession(), "UPDATE release SET preferred_artwork_id = ? WHERE id = ?", artworkId, releaseId);
|
||||||
|
else
|
||||||
|
utils::executeCommand(*session.getDboSession(), "UPDATE release SET preferred_artwork_id = NULL WHERE id = ?", releaseId);
|
||||||
|
}
|
||||||
|
|
||||||
std::size_t Release::getCount(Session& session, const FindParameters& params)
|
std::size_t Release::getCount(Session& session, const FindParameters& params)
|
||||||
{
|
{
|
||||||
session.checkReadTransaction();
|
session.checkReadTransaction();
|
||||||
+5
-2
@@ -17,18 +17,21 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/ScanSettings.hpp"
|
#include "database/objects/ScanSettings.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
#include <Wt/Dbo/WtSqlTraits.h>
|
#include <Wt/Dbo/WtSqlTraits.h>
|
||||||
|
|
||||||
#include "core/String.hpp"
|
#include "core/String.hpp"
|
||||||
#include "database/MediaLibrary.hpp"
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
|
#include "database/objects/MediaLibrary.hpp"
|
||||||
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
#include "traits/StringViewTraits.hpp"
|
#include "traits/StringViewTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::ScanSettings)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
ScanSettings::ScanSettings(std::string_view name)
|
ScanSettings::ScanSettings(std::string_view name)
|
||||||
+6
-3
@@ -17,17 +17,20 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/StarredArtist.hpp"
|
#include "database/objects/StarredArtist.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
#include <Wt/Dbo/WtSqlTraits.h>
|
#include <Wt/Dbo/WtSqlTraits.h>
|
||||||
|
|
||||||
#include "database/Artist.hpp"
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/User.hpp"
|
#include "database/objects/Artist.hpp"
|
||||||
|
#include "database/objects/User.hpp"
|
||||||
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::StarredArtist)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
StarredArtist::StarredArtist(ObjectPtr<Artist> artist, ObjectPtr<User> user, FeedbackBackend backend)
|
StarredArtist::StarredArtist(ObjectPtr<Artist> artist, ObjectPtr<User> user, FeedbackBackend backend)
|
||||||
+6
-3
@@ -17,17 +17,20 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/StarredRelease.hpp"
|
#include "database/objects/StarredRelease.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
#include <Wt/Dbo/WtSqlTraits.h>
|
#include <Wt/Dbo/WtSqlTraits.h>
|
||||||
|
|
||||||
#include "database/Release.hpp"
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/User.hpp"
|
#include "database/objects/Release.hpp"
|
||||||
|
#include "database/objects/User.hpp"
|
||||||
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::StarredRelease)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
StarredRelease::StarredRelease(ObjectPtr<Release> release, ObjectPtr<User> user, FeedbackBackend backend)
|
StarredRelease::StarredRelease(ObjectPtr<Release> release, ObjectPtr<User> user, FeedbackBackend backend)
|
||||||
+6
-3
@@ -17,17 +17,20 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/StarredTrack.hpp"
|
#include "database/objects/StarredTrack.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
#include <Wt/Dbo/WtSqlTraits.h>
|
#include <Wt/Dbo/WtSqlTraits.h>
|
||||||
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/Track.hpp"
|
#include "database/objects/Track.hpp"
|
||||||
#include "database/User.hpp"
|
#include "database/objects/User.hpp"
|
||||||
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::StarredTrack)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
StarredTrack::StarredTrack(ObjectPtr<Track> track, ObjectPtr<User> user, FeedbackBackend backend)
|
StarredTrack::StarredTrack(ObjectPtr<Track> track, ObjectPtr<User> user, FeedbackBackend backend)
|
||||||
@@ -17,24 +17,25 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/Track.hpp"
|
#include "database/objects/Track.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
#include <Wt/Dbo/WtSqlTraits.h>
|
#include <Wt/Dbo/WtSqlTraits.h>
|
||||||
|
|
||||||
#include "core/ILogger.hpp"
|
#include "core/ILogger.hpp"
|
||||||
#include "database/Artist.hpp"
|
|
||||||
#include "database/Artwork.hpp"
|
|
||||||
#include "database/Cluster.hpp"
|
|
||||||
#include "database/Directory.hpp"
|
|
||||||
#include "database/MediaLibrary.hpp"
|
|
||||||
#include "database/Release.hpp"
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/TrackArtistLink.hpp"
|
#include "database/objects/Artist.hpp"
|
||||||
#include "database/TrackEmbeddedImage.hpp"
|
#include "database/objects/Artwork.hpp"
|
||||||
#include "database/TrackEmbeddedImageLink.hpp"
|
#include "database/objects/Cluster.hpp"
|
||||||
#include "database/TrackFeatures.hpp"
|
#include "database/objects/Directory.hpp"
|
||||||
#include "database/TrackLyrics.hpp"
|
#include "database/objects/MediaLibrary.hpp"
|
||||||
#include "database/User.hpp"
|
#include "database/objects/Release.hpp"
|
||||||
|
#include "database/objects/TrackArtistLink.hpp"
|
||||||
|
#include "database/objects/TrackEmbeddedImage.hpp"
|
||||||
|
#include "database/objects/TrackEmbeddedImageLink.hpp"
|
||||||
|
#include "database/objects/TrackFeatures.hpp"
|
||||||
|
#include "database/objects/TrackLyrics.hpp"
|
||||||
|
#include "database/objects/User.hpp"
|
||||||
|
|
||||||
#include "SqlQuery.hpp"
|
#include "SqlQuery.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
@@ -43,6 +44,8 @@
|
|||||||
#include "traits/PathTraits.hpp"
|
#include "traits/PathTraits.hpp"
|
||||||
#include "traits/StringViewTraits.hpp"
|
#include "traits/StringViewTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::Track)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
@@ -58,12 +61,6 @@ namespace lms::db
|
|||||||
for (std::string_view keyword : params.keywords)
|
for (std::string_view keyword : params.keywords)
|
||||||
query.where("t.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + utils::escapeLikeKeyword(keyword) + "%");
|
query.where("t.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + utils::escapeLikeKeyword(keyword) + "%");
|
||||||
|
|
||||||
if (!params.fileStem.empty())
|
|
||||||
query.where("t.file_stem = ?").bind(params.fileStem);
|
|
||||||
|
|
||||||
if (!params.fileName.empty())
|
|
||||||
query.where("t.file_name = ?").bind(params.fileName);
|
|
||||||
|
|
||||||
if (!params.name.empty())
|
if (!params.name.empty())
|
||||||
query.where("t.name = ?").bind(params.name);
|
query.where("t.name = ?").bind(params.name);
|
||||||
|
|
||||||
@@ -212,8 +209,8 @@ namespace lms::db
|
|||||||
case TrackSortMethod::Name:
|
case TrackSortMethod::Name:
|
||||||
query.orderBy("t.name COLLATE NOCASE");
|
query.orderBy("t.name COLLATE NOCASE");
|
||||||
break;
|
break;
|
||||||
case TrackSortMethod::FileName:
|
case TrackSortMethod::AbsoluteFilePath:
|
||||||
query.orderBy("t.file_name COLLATE NOCASE");
|
query.orderBy("t.absolute_file_path COLLATE NOCASE");
|
||||||
break;
|
break;
|
||||||
case TrackSortMethod::DateDescAndRelease:
|
case TrackSortMethod::DateDescAndRelease:
|
||||||
query.orderBy("t.date DESC,t.release_id,t.disc_number,t.track_number");
|
query.orderBy("t.date DESC,t.release_id,t.disc_number,t.track_number");
|
||||||
@@ -264,6 +261,25 @@ namespace lms::db
|
|||||||
return utils::fetchQuerySingleResult(session.getDboSession()->query<Wt::Dbo::ptr<Track>>("SELECT t from track t").where("t.absolute_file_path = ?").bind(p));
|
return utils::fetchQuerySingleResult(session.getDboSession()->query<Wt::Dbo::ptr<Track>>("SELECT t from track t").where("t.absolute_file_path = ?").bind(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<FileInfo> Track::findFileInfo(Session& session, const std::filesystem::path& p)
|
||||||
|
{
|
||||||
|
session.checkReadTransaction();
|
||||||
|
|
||||||
|
auto query{ session.getDboSession()->query<std::tuple<int, Wt::WDateTime>>("SELECT t.scan_version, t.file_last_write FROM track t WHERE t.absolute_file_path = ?") };
|
||||||
|
query.bind(p);
|
||||||
|
|
||||||
|
std::optional<FileInfo> result;
|
||||||
|
|
||||||
|
utils::forEachQueryResult(query, [&](const auto& row) {
|
||||||
|
FileInfo info;
|
||||||
|
info.scanVersion = std::get<0>(row);
|
||||||
|
info.lastWrittenTime = std::get<1>(row);
|
||||||
|
result = info;
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Track::pointer Track::find(Session& session, TrackId id)
|
Track::pointer Track::find(Session& session, TrackId id)
|
||||||
{
|
{
|
||||||
session.checkReadTransaction();
|
session.checkReadTransaction();
|
||||||
@@ -271,21 +287,54 @@ namespace lms::db
|
|||||||
return utils::fetchQuerySingleResult(session.getDboSession()->query<Wt::Dbo::ptr<Track>>("SELECT t from track t").where("t.id = ?").bind(id));
|
return utils::fetchQuerySingleResult(session.getDboSession()->query<Wt::Dbo::ptr<Track>>("SELECT t from track t").where("t.id = ?").bind(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Track::find(Session& session, TrackId& lastRetrievedTrack, std::size_t count, const std::function<void(const Track::pointer&)>& func, MediaLibraryId library)
|
void Track::find(Session& session, TrackId& lastRetrievedId, std::size_t count, const std::function<void(const Track::pointer&)>& func, MediaLibraryId library)
|
||||||
{
|
{
|
||||||
session.checkReadTransaction();
|
session.checkReadTransaction();
|
||||||
|
|
||||||
auto query{ session.getDboSession()->query<Wt::Dbo::ptr<Track>>("SELECT t from track t").orderBy("t.id").where("t.id > ?").bind(lastRetrievedTrack).limit(static_cast<int>(count)) };
|
auto query{ session.getDboSession()->query<Wt::Dbo::ptr<Track>>("SELECT t from track t").orderBy("t.id").where("t.id > ?").bind(lastRetrievedId).limit(static_cast<int>(count)) };
|
||||||
|
|
||||||
if (library.isValid())
|
if (library.isValid())
|
||||||
query.where("media_library_id = ?").bind(library);
|
query.where("media_library_id = ?").bind(library);
|
||||||
|
|
||||||
utils::forEachQueryResult(query, [&](const Track::pointer& track) {
|
utils::forEachQueryResult(query, [&](const Track::pointer& track) {
|
||||||
func(track);
|
func(track);
|
||||||
lastRetrievedTrack = track->getId();
|
lastRetrievedId = track->getId();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Track::findAbsoluteFilePath(Session& session, TrackId& lastRetrievedId, std::size_t count, const std::function<void(TrackId trackId, const std::filesystem::path& absoluteFilePath)>& func)
|
||||||
|
{
|
||||||
|
session.checkReadTransaction();
|
||||||
|
|
||||||
|
auto query{ session.getDboSession()->query<std::tuple<TrackId, std::filesystem::path>>("SELECT t.id,t.absolute_file_path from track t").orderBy("t.id").where("t.id > ?").bind(lastRetrievedId).limit(static_cast<int>(count)) };
|
||||||
|
|
||||||
|
utils::forEachQueryResult(query, [&](const auto& res) {
|
||||||
|
func(std::get<0>(res), std::get<1>(res));
|
||||||
|
lastRetrievedId = std::get<0>(res);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Track::find(Session& session, const IdRange<TrackId>& idRange, const std::function<void(const Track::pointer&)>& func)
|
||||||
|
{
|
||||||
|
assert(idRange.isValid());
|
||||||
|
|
||||||
|
auto query{ session.getDboSession()->query<Wt::Dbo::ptr<Track>>("SELECT t from track t").orderBy("t.id").where("t.id BETWEEN ? AND ?").bind(idRange.first).bind(idRange.last) };
|
||||||
|
|
||||||
|
utils::forEachQueryResult(query, [&](const Track::pointer& track) {
|
||||||
|
func(track);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
IdRange<TrackId> Track::findNextIdRange(Session& session, TrackId lastRetrievedId, std::size_t count)
|
||||||
|
{
|
||||||
|
auto query{ session.getDboSession()->query<std::tuple<TrackId, TrackId>>("SELECT MIN(sub.id) AS first_id, MAX(sub.id) AS last_id FROM (SELECT t.id FROM track t WHERE t.id > ? ORDER BY t.id LIMIT ?) sub") };
|
||||||
|
query.bind(lastRetrievedId);
|
||||||
|
query.bind(static_cast<int>(count));
|
||||||
|
|
||||||
|
auto res{ utils::fetchQuerySingleResult(query) };
|
||||||
|
return IdRange<TrackId>{ .first = std::get<0>(res), .last = std::get<1>(res) };
|
||||||
|
}
|
||||||
|
|
||||||
bool Track::exists(Session& session, TrackId id)
|
bool Track::exists(Session& session, TrackId id)
|
||||||
{
|
{
|
||||||
session.checkReadTransaction();
|
session.checkReadTransaction();
|
||||||
@@ -456,41 +505,25 @@ namespace lms::db
|
|||||||
void Track::setAbsoluteFilePath(const std::filesystem::path& filePath)
|
void Track::setAbsoluteFilePath(const std::filesystem::path& filePath)
|
||||||
{
|
{
|
||||||
assert(filePath.is_absolute());
|
assert(filePath.is_absolute());
|
||||||
|
|
||||||
_absoluteFilePath = filePath;
|
_absoluteFilePath = filePath;
|
||||||
_fileStem = filePath.stem();
|
|
||||||
_fileName = filePath.filename();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Track::setRelativeFilePath(const std::filesystem::path& filePath)
|
|
||||||
{
|
|
||||||
assert(filePath.is_relative());
|
|
||||||
|
|
||||||
assert(_absoluteFilePath.filename() == filePath.filename()); // must be compatible with previous setAbsoluteFilePath call
|
|
||||||
_fileStem = filePath.stem(); // lazy migration (_fileStem added later, could be set only with setAbsoluteFilePath)
|
|
||||||
_fileName = filePath.filename(); // lazy migration (_fileName added later, could be set only with setAbsoluteFilePath)
|
|
||||||
_relativeFilePath = filePath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Track::setName(std::string_view name)
|
void Track::setName(std::string_view name)
|
||||||
{
|
{
|
||||||
_name = std::string{ name, 0, _maxNameLength };
|
_name = std::string{ name, 0, _maxNameLength };
|
||||||
if (name.size() > _maxNameLength)
|
LMS_LOG_IF(DB, WARNING, name.size() > _maxNameLength, "Track name too long, truncated to '" << _name << "'");
|
||||||
LMS_LOG(DB, WARNING, "Track name too long, truncated to '" << _name << "'");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Track::setCopyright(std::string_view copyright)
|
void Track::setCopyright(std::string_view copyright)
|
||||||
{
|
{
|
||||||
_copyright = std::string{ copyright, 0, _maxCopyrightLength };
|
_copyright = std::string{ copyright, 0, _maxCopyrightLength };
|
||||||
if (copyright.size() > _maxCopyrightLength)
|
LMS_LOG_IF(DB, WARNING, copyright.size() > _maxCopyrightLength, "Track copyright too long, truncated to '" << _copyright << "'");
|
||||||
LMS_LOG(DB, WARNING, "Track copyright too long, truncated to '" << _copyright << "'");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Track::setCopyrightURL(std::string_view copyrightURL)
|
void Track::setCopyrightURL(std::string_view copyrightURL)
|
||||||
{
|
{
|
||||||
_copyrightURL = std::string{ copyrightURL, 0, _maxCopyrightURLLength };
|
_copyrightURL = std::string{ copyrightURL, 0, _maxCopyrightURLLength };
|
||||||
if (copyrightURL.size() > _maxCopyrightURLLength)
|
LMS_LOG_IF(DB, WARNING, copyrightURL.size() > _maxCopyrightURLLength, "Track copyright URL too long, truncated to '" << _copyrightURL << "'");
|
||||||
LMS_LOG(DB, WARNING, "Track copyright URL too long, truncated to '" << _copyrightURL << "'");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Track::clearArtistLinks()
|
void Track::clearArtistLinks()
|
||||||
+12
-5
@@ -17,15 +17,20 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/TrackArtistLink.hpp"
|
#include "database/objects/TrackArtistLink.hpp"
|
||||||
|
|
||||||
#include "database/Artist.hpp"
|
#include <Wt/Dbo/Impl.h>
|
||||||
|
|
||||||
|
#include "core/ILogger.hpp"
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/Track.hpp"
|
#include "database/objects/Artist.hpp"
|
||||||
|
#include "database/objects/Track.hpp"
|
||||||
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::TrackArtistLink)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
@@ -173,11 +178,13 @@ namespace lms::db
|
|||||||
|
|
||||||
void TrackArtistLink::setArtistName(std::string_view artistName)
|
void TrackArtistLink::setArtistName(std::string_view artistName)
|
||||||
{
|
{
|
||||||
_artistName = artistName;
|
_artistName.assign(artistName, 0, Artist::maxNameLength);
|
||||||
|
LMS_LOG_IF(DB, WARNING, artistName.size() > Artist::maxNameLength, "Artist link name too long, truncated to '" << _artistName << "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
void TrackArtistLink::setArtistSortName(std::string_view artistSortName)
|
void TrackArtistLink::setArtistSortName(std::string_view artistSortName)
|
||||||
{
|
{
|
||||||
_artistSortName = artistSortName;
|
_artistSortName.assign(artistSortName, 0, Artist::maxNameLength);
|
||||||
|
LMS_LOG_IF(DB, WARNING, artistSortName.size() > Artist::maxNameLength, "Artist link sort name too long, truncated to '" << _artistSortName << "'");
|
||||||
}
|
}
|
||||||
} // namespace lms::db
|
} // namespace lms::db
|
||||||
+7
-3
@@ -17,15 +17,19 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/TrackBookmark.hpp"
|
#include "database/objects/TrackBookmark.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/Track.hpp"
|
#include "database/objects/Track.hpp"
|
||||||
#include "database/User.hpp"
|
#include "database/objects/User.hpp"
|
||||||
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::TrackBookmark)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
TrackBookmark::TrackBookmark(ObjectPtr<User> user, ObjectPtr<Track> track)
|
TrackBookmark::TrackBookmark(ObjectPtr<User> user, ObjectPtr<Track> track)
|
||||||
+11
-17
@@ -17,17 +17,22 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/Directory.hpp"
|
#include "database/objects/TrackEmbeddedImage.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/Track.hpp"
|
|
||||||
#include "database/TrackEmbeddedImage.hpp"
|
|
||||||
#include "database/Types.hpp"
|
#include "database/Types.hpp"
|
||||||
|
#include "database/objects/Track.hpp"
|
||||||
|
|
||||||
|
#include "database/objects/TrackEmbeddedImageLink.hpp"
|
||||||
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
#include "traits/ImageHashTypeTraits.hpp"
|
#include "traits/ImageHashTypeTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::TrackEmbeddedImage)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
@@ -43,7 +48,7 @@ namespace lms::db
|
|||||||
|| params.track.isValid()
|
|| params.track.isValid()
|
||||||
|| params.release.isValid()
|
|| params.release.isValid()
|
||||||
|| params.trackList.isValid()
|
|| params.trackList.isValid()
|
||||||
|| !params.imageTypes.empty()
|
|| params.imageType.has_value()
|
||||||
|| params.sortMethod == TrackEmbeddedImageSortMethod::DiscNumberThenTrackNumberThenSizeDesc
|
|| params.sortMethod == TrackEmbeddedImageSortMethod::DiscNumberThenTrackNumberThenSizeDesc
|
||||||
|| params.sortMethod == TrackEmbeddedImageSortMethod::TrackNumberThenSizeDesc)
|
|| params.sortMethod == TrackEmbeddedImageSortMethod::TrackNumberThenSizeDesc)
|
||||||
{
|
{
|
||||||
@@ -90,19 +95,8 @@ namespace lms::db
|
|||||||
query.where("t_l_e.tracklist_id = ?").bind(params.trackList);
|
query.where("t_l_e.tracklist_id = ?").bind(params.trackList);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!params.imageTypes.empty())
|
if (params.imageType.has_value())
|
||||||
{
|
query.where("t_e_i_l.type = ?").bind(params.imageType.value());
|
||||||
std::string clause{ "t_e_i_l.type IN (" };
|
|
||||||
for (const auto& type : params.imageTypes)
|
|
||||||
{
|
|
||||||
if (clause.back() != '(')
|
|
||||||
clause += ",";
|
|
||||||
clause += "?";
|
|
||||||
query.bind(type);
|
|
||||||
}
|
|
||||||
clause += ")";
|
|
||||||
query.where(clause);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (params.sortMethod)
|
switch (params.sortMethod)
|
||||||
+9
-3
@@ -17,15 +17,21 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/TrackEmbeddedImageLink.hpp"
|
#include "database/objects/TrackEmbeddedImageLink.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
|
#include <Wt/Dbo/WtSqlTraits.h>
|
||||||
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/Track.hpp"
|
|
||||||
#include "database/TrackEmbeddedImage.hpp"
|
|
||||||
#include "database/Types.hpp"
|
#include "database/Types.hpp"
|
||||||
|
#include "database/objects/Track.hpp"
|
||||||
|
#include "database/objects/TrackEmbeddedImage.hpp"
|
||||||
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
|
#include "traits/ImageHashTypeTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::TrackEmbeddedImageLink)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
+6
-3
@@ -17,19 +17,22 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/TrackFeatures.hpp"
|
#include "database/objects/TrackFeatures.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
#include <boost/property_tree/json_parser.hpp>
|
#include <boost/property_tree/json_parser.hpp>
|
||||||
#include <boost/property_tree/ptree.hpp>
|
#include <boost/property_tree/ptree.hpp>
|
||||||
|
|
||||||
#include "core/ILogger.hpp"
|
#include "core/ILogger.hpp"
|
||||||
#include "database/Directory.hpp"
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/Track.hpp"
|
#include "database/objects/Directory.hpp"
|
||||||
|
#include "database/objects/Track.hpp"
|
||||||
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::TrackFeatures)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
|
|
||||||
+11
-5
@@ -16,21 +16,27 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include "database/TrackList.hpp"
|
#include "database/objects/TrackList.hpp"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include "database/Cluster.hpp"
|
#include <Wt/Dbo/Impl.h>
|
||||||
#include "database/PlayListFile.hpp"
|
#include <Wt/Dbo/WtSqlTraits.h>
|
||||||
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/Track.hpp"
|
#include "database/objects/Cluster.hpp"
|
||||||
#include "database/User.hpp"
|
#include "database/objects/PlayListFile.hpp"
|
||||||
|
#include "database/objects/Track.hpp"
|
||||||
|
#include "database/objects/User.hpp"
|
||||||
|
|
||||||
#include "SqlQuery.hpp"
|
#include "SqlQuery.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
#include "traits/StringViewTraits.hpp"
|
#include "traits/StringViewTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::TrackList)
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::TrackListEntry)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
+20
-6
@@ -17,21 +17,23 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/TrackLyrics.hpp"
|
#include "database/objects/TrackLyrics.hpp"
|
||||||
|
|
||||||
#include <Wt/Dbo/SqlTraits.h>
|
#include <Wt/Dbo/Impl.h>
|
||||||
#include <Wt/Dbo/WtSqlTraits.h>
|
#include <Wt/Dbo/WtSqlTraits.h>
|
||||||
|
|
||||||
#include "database/Artist.hpp"
|
|
||||||
#include "database/Directory.hpp"
|
|
||||||
#include "database/Release.hpp"
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/Track.hpp"
|
#include "database/objects/Artist.hpp"
|
||||||
|
#include "database/objects/Directory.hpp"
|
||||||
|
#include "database/objects/Release.hpp"
|
||||||
|
#include "database/objects/Track.hpp"
|
||||||
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
#include "traits/PathTraits.hpp"
|
#include "traits/PathTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::TrackLyrics)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
@@ -126,6 +128,18 @@ namespace lms::db
|
|||||||
return utils::execRangeQuery<TrackLyricsId>(query, range);
|
return utils::execRangeQuery<TrackLyricsId>(query, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TrackLyrics::findAbsoluteFilePath(Session& session, TrackLyricsId& lastRetrievedId, std::size_t count, const std::function<void(TrackLyricsId trackLyricsId, const std::filesystem::path& absoluteFilePath)>& func)
|
||||||
|
{
|
||||||
|
session.checkReadTransaction();
|
||||||
|
|
||||||
|
auto query{ session.getDboSession()->query<std::tuple<TrackLyricsId, std::filesystem::path>>("SELECT t_lrc.id,t_lrc.absolute_file_path from track_lyrics t_lrc").orderBy("t_lrc.id").where("t_lrc.id > ?").bind(lastRetrievedId).limit(static_cast<int>(count)) };
|
||||||
|
|
||||||
|
utils::forEachQueryResult(query, [&](const auto& res) {
|
||||||
|
func(std::get<0>(res), std::get<1>(res));
|
||||||
|
lastRetrievedId = std::get<0>(res);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
TrackLyrics::SynchronizedLines TrackLyrics::getSynchronizedLines() const
|
TrackLyrics::SynchronizedLines TrackLyrics::getSynchronizedLines() const
|
||||||
{
|
{
|
||||||
SynchronizedLines synchronizedLines;
|
SynchronizedLines synchronizedLines;
|
||||||
@@ -17,15 +17,19 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/UIState.hpp"
|
#include "database/objects/UIState.hpp"
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Impl.h>
|
||||||
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/User.hpp"
|
#include "database/objects/User.hpp"
|
||||||
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
#include "traits/StringViewTraits.hpp"
|
#include "traits/StringViewTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::UIState)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
UIState::UIState(std::string_view item, ObjectPtr<User> user)
|
UIState::UIState(std::string_view item, ObjectPtr<User> user)
|
||||||
@@ -17,21 +17,27 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "database/User.hpp"
|
#include "database/objects/User.hpp"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include "database/Artist.hpp"
|
#include <Wt/Dbo/Impl.h>
|
||||||
#include "database/Release.hpp"
|
#include <Wt/Dbo/WtSqlTraits.h>
|
||||||
|
|
||||||
#include "database/Session.hpp"
|
#include "database/Session.hpp"
|
||||||
#include "database/Track.hpp"
|
#include "database/objects/Artist.hpp"
|
||||||
#include "database/UIState.hpp"
|
#include "database/objects/AuthToken.hpp"
|
||||||
|
#include "database/objects/Release.hpp"
|
||||||
|
#include "database/objects/Track.hpp"
|
||||||
|
#include "database/objects/UIState.hpp"
|
||||||
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "traits/EnumSetTraits.hpp"
|
#include "traits/EnumSetTraits.hpp"
|
||||||
#include "traits/IdTypeTraits.hpp"
|
#include "traits/IdTypeTraits.hpp"
|
||||||
#include "traits/StringViewTraits.hpp"
|
#include "traits/StringViewTraits.hpp"
|
||||||
|
|
||||||
|
DBO_INSTANTIATE_TEMPLATES(lms::db::User)
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
User::User(std::string_view loginName)
|
User::User(std::string_view loginName)
|
||||||
+11
-15
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2024 Emeric Poupon
|
* Copyright (C) 2019 Emeric Poupon
|
||||||
*
|
*
|
||||||
* This file is part of LMS.
|
* This file is part of LMS.
|
||||||
*
|
*
|
||||||
@@ -20,22 +20,18 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <memory>
|
||||||
#include "database/ArtworkId.hpp"
|
|
||||||
#include "database/ImageId.hpp"
|
|
||||||
#include "database/Object.hpp"
|
|
||||||
#include "database/TrackEmbeddedImageId.hpp"
|
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
class Artwork;
|
|
||||||
class Session;
|
class Session;
|
||||||
|
class IDb
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IDb() = default;
|
||||||
|
|
||||||
|
virtual Session& getTLSSession() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<IDb> createDb(const std::filesystem::path& dbPath, std::size_t connectionCount = 10);
|
||||||
} // namespace lms::db
|
} // namespace lms::db
|
||||||
|
|
||||||
namespace lms::scanner::utils
|
|
||||||
{
|
|
||||||
db::ObjectPtr<db::Artwork> getOrCreateArtworkFromTrackEmbeddedImage(db::Session& session, db::TrackEmbeddedImageId trackEmbeddedImageId);
|
|
||||||
db::ObjectPtr<db::Artwork> getOrCreateArtworkFromImage(db::Session& session, db::ImageId imageId);
|
|
||||||
|
|
||||||
std::filesystem::path toPath(db::Session& session, db::ArtworkId artworkId);
|
|
||||||
} // namespace lms::scanner::utils
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Emeric Poupon
|
||||||
|
*
|
||||||
|
* This file is part of LMS.
|
||||||
|
*
|
||||||
|
* LMS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LMS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace lms::db
|
||||||
|
{
|
||||||
|
// Due to technical limitations, query plans are recorded globally across all databases.
|
||||||
|
// As a result, this class is implemented as a singleton rather than being owned per DB instance.
|
||||||
|
class IQueryPlanRecorder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IQueryPlanRecorder() = default;
|
||||||
|
|
||||||
|
using QueryPlanVisitor = std::function<void(std::string_view query, std::string_view plan)>;
|
||||||
|
virtual void visitQueryPlans(const QueryPlanVisitor& visitor) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<IQueryPlanRecorder> createQueryPlanRecorder();
|
||||||
|
} // namespace lms::db
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Emeric Poupon
|
||||||
|
*
|
||||||
|
* This file is part of LMS.
|
||||||
|
*
|
||||||
|
* LMS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LMS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace lms::db
|
||||||
|
{
|
||||||
|
template<typename IdType>
|
||||||
|
struct IdRange
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IdType first;
|
||||||
|
IdType last;
|
||||||
|
|
||||||
|
bool isValid() const { return first.isValid() && last.isValid(); }
|
||||||
|
};
|
||||||
|
} // namespace lms::db
|
||||||
@@ -20,15 +20,19 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Wt/Dbo/ptr.h>
|
#include <Wt/Dbo/ptr.h>
|
||||||
#include <Wt/WSignal.h>
|
|
||||||
|
|
||||||
#include "database/IdType.hpp"
|
#include "database/IdType.hpp"
|
||||||
#include "database/TransactionChecker.hpp"
|
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
|
class ObjectPtrBase
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
static void checkWriteTransaction(Wt::Dbo::Session& session);
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class ObjectPtr
|
class ObjectPtr : public ObjectPtrBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ObjectPtr() = default;
|
ObjectPtr() = default;
|
||||||
@@ -45,18 +49,13 @@ namespace lms::db
|
|||||||
|
|
||||||
auto modify()
|
auto modify()
|
||||||
{
|
{
|
||||||
#if LMS_CHECK_TRANSACTION_ACCESSES
|
checkWriteTransaction(*_obj.session());
|
||||||
TransactionChecker::checkWriteTransaction(*_obj.session());
|
|
||||||
#endif
|
|
||||||
return _obj.modify();
|
return _obj.modify();
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove()
|
void remove()
|
||||||
{
|
{
|
||||||
#if LMS_CHECK_TRANSACTION_ACCESSES
|
checkWriteTransaction(*_obj.session());
|
||||||
TransactionChecker::checkWriteTransaction(*_obj.session());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
_obj.remove();
|
_obj.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,56 +19,23 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Wt/Dbo/Dbo.h>
|
#include <Wt/Dbo/Session.h>
|
||||||
#include <Wt/Dbo/SqlConnectionPool.h>
|
|
||||||
|
|
||||||
|
#include <span>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "core/ITraceLogger.hpp"
|
#include "database/Transaction.hpp"
|
||||||
#include "core/RecursiveSharedMutex.hpp"
|
#include "database/Types.hpp"
|
||||||
#include "database/TransactionChecker.hpp"
|
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
class WriteTransaction
|
class IDb;
|
||||||
{
|
|
||||||
public:
|
|
||||||
~WriteTransaction();
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class Session;
|
|
||||||
WriteTransaction(core::RecursiveSharedMutex& mutex, Wt::Dbo::Session& session);
|
|
||||||
|
|
||||||
WriteTransaction(const WriteTransaction&) = delete;
|
|
||||||
WriteTransaction& operator=(const WriteTransaction&) = delete;
|
|
||||||
|
|
||||||
const std::unique_lock<core::RecursiveSharedMutex> _lock;
|
|
||||||
const core::tracing::ScopedTrace _trace{ "Database", core::tracing::Level::Detailed, "WriteTransaction" }; // before actual transaction
|
|
||||||
Wt::Dbo::Transaction _transaction;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ReadTransaction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
~ReadTransaction();
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class Session;
|
|
||||||
ReadTransaction(Wt::Dbo::Session& session);
|
|
||||||
|
|
||||||
ReadTransaction(const ReadTransaction&) = delete;
|
|
||||||
ReadTransaction& operator=(const ReadTransaction&) = delete;
|
|
||||||
|
|
||||||
const core::tracing::ScopedTrace _trace{ "Database", core::tracing::Level::Detailed, "ReadTransaction" }; // before actual transaction
|
|
||||||
Wt::Dbo::Transaction _transaction;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Db;
|
|
||||||
class Session
|
class Session
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Session(Db& db);
|
Session(IDb& db);
|
||||||
~Session() = default;
|
~Session() = default;
|
||||||
Session(const Session&) = delete;
|
Session(const Session&) = delete;
|
||||||
Session& operator=(const Session&) = delete;
|
Session& operator=(const Session&) = delete;
|
||||||
@@ -76,18 +43,8 @@ namespace lms::db
|
|||||||
[[nodiscard]] WriteTransaction createWriteTransaction();
|
[[nodiscard]] WriteTransaction createWriteTransaction();
|
||||||
[[nodiscard]] ReadTransaction createReadTransaction();
|
[[nodiscard]] ReadTransaction createReadTransaction();
|
||||||
|
|
||||||
void checkWriteTransaction()
|
void checkWriteTransaction() const;
|
||||||
{
|
void checkReadTransaction() const;
|
||||||
#if LMS_CHECK_TRANSACTION_ACCESSES
|
|
||||||
TransactionChecker::checkWriteTransaction(_session);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
void checkReadTransaction()
|
|
||||||
{
|
|
||||||
#if LMS_CHECK_TRANSACTION_ACCESSES
|
|
||||||
TransactionChecker::checkReadTransaction(_session);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void execute(std::string_view statement);
|
void execute(std::string_view statement);
|
||||||
|
|
||||||
@@ -97,23 +54,19 @@ namespace lms::db
|
|||||||
void analyzeEntry(const std::string& entry);
|
void analyzeEntry(const std::string& entry);
|
||||||
|
|
||||||
bool areAllTablesEmpty(); // need to acquire a read transaction
|
bool areAllTablesEmpty(); // need to acquire a read transaction
|
||||||
|
FileStats getFileStats(); // need to acquire a read transaction
|
||||||
|
|
||||||
void prepareTablesIfNeeded(); // need to run only once at startup
|
void prepareTablesIfNeeded(); // need to run only once at startup
|
||||||
bool migrateSchemaIfNeeded(); // returns true if migration was performed
|
bool migrateSchemaIfNeeded(); // returns true if migration was performed
|
||||||
void createIndexesIfNeeded();
|
void createIndexesIfNeeded();
|
||||||
void vacuumIfNeeded();
|
void vacuumIfNeeded();
|
||||||
void vacuum();
|
void vacuum();
|
||||||
void refreshTracingLoggerStats();
|
|
||||||
|
|
||||||
// returning a ptr here to ease further wrapping using operator->
|
// returning a ptr here to ease further wrapping using operator->
|
||||||
Wt::Dbo::Session* getDboSession()
|
Wt::Dbo::Session* getDboSession() { return &_session; }
|
||||||
{
|
const Wt::Dbo::Session* getDboSession() const { return &_session; }
|
||||||
return &_session;
|
|
||||||
}
|
IDb& getDb() { return _db; }
|
||||||
Db& getDb()
|
|
||||||
{
|
|
||||||
return _db;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Object, typename... Args>
|
template<typename Object, typename... Args>
|
||||||
typename Object::pointer create(Args&&... args)
|
typename Object::pointer create(Args&&... args)
|
||||||
@@ -126,8 +79,26 @@ namespace lms::db
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Object>
|
||||||
|
void destroy(typename Object::IdType id)
|
||||||
|
{
|
||||||
|
destroy(std::span{ &id, 1 });
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Object>
|
||||||
|
void destroy(std::span<const typename Object::IdType> ids)
|
||||||
|
{
|
||||||
|
checkWriteTransaction();
|
||||||
|
|
||||||
|
const std::string query{ std::string{ "DELETE FROM " } + _session.tableName<Object>() + " WHERE id = ?" };
|
||||||
|
for (typename Object::IdType id : ids)
|
||||||
|
execute(query, id.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Db& _db;
|
void execute(std::string_view query, long long id);
|
||||||
|
|
||||||
|
IDb& _db;
|
||||||
Wt::Dbo::Session _session;
|
Wt::Dbo::Session _session;
|
||||||
};
|
};
|
||||||
} // namespace lms::db
|
} // namespace lms::db
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Emeric Poupon
|
||||||
|
*
|
||||||
|
* This file is part of LMS.
|
||||||
|
*
|
||||||
|
* LMS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LMS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Transaction.h>
|
||||||
|
|
||||||
|
#include "core/ITraceLogger.hpp"
|
||||||
|
|
||||||
|
namespace lms::core
|
||||||
|
{
|
||||||
|
class RecursiveSharedMutex;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace lms::db
|
||||||
|
{
|
||||||
|
class WriteTransaction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~WriteTransaction();
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class Session;
|
||||||
|
WriteTransaction(core::RecursiveSharedMutex& mutex, Wt::Dbo::Session& session);
|
||||||
|
|
||||||
|
WriteTransaction(const WriteTransaction&) = delete;
|
||||||
|
WriteTransaction& operator=(const WriteTransaction&) = delete;
|
||||||
|
|
||||||
|
const std::unique_lock<core::RecursiveSharedMutex> _lock;
|
||||||
|
const core::tracing::ScopedTrace _trace; // before actual transaction
|
||||||
|
Wt::Dbo::Transaction _transaction;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ReadTransaction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~ReadTransaction();
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class Session;
|
||||||
|
ReadTransaction(Wt::Dbo::Session& session);
|
||||||
|
|
||||||
|
ReadTransaction(const ReadTransaction&) = delete;
|
||||||
|
ReadTransaction& operator=(const ReadTransaction&) = delete;
|
||||||
|
|
||||||
|
const core::tracing::ScopedTrace _trace; // before actual transaction
|
||||||
|
Wt::Dbo::Transaction _transaction;
|
||||||
|
};
|
||||||
|
} // namespace lms::db
|
||||||
@@ -102,6 +102,17 @@ namespace lms::db
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct FileStats
|
||||||
|
{
|
||||||
|
std::size_t trackCount;
|
||||||
|
std::size_t imageCount;
|
||||||
|
std::size_t trackLyricsCount;
|
||||||
|
std::size_t playListCount;
|
||||||
|
std::size_t artistInfoCount;
|
||||||
|
|
||||||
|
std::size_t getTotalFileCount() const { return trackCount + imageCount + trackLyricsCount + playListCount + artistInfoCount; }
|
||||||
|
};
|
||||||
|
|
||||||
struct YearRange
|
struct YearRange
|
||||||
{
|
{
|
||||||
int begin{};
|
int begin{};
|
||||||
@@ -114,6 +125,12 @@ namespace lms::db
|
|||||||
std::string name;
|
std::string name;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct FileInfo
|
||||||
|
{
|
||||||
|
Wt::WDateTime lastWrittenTime;
|
||||||
|
std::size_t scanVersion{};
|
||||||
|
};
|
||||||
|
|
||||||
enum class ArtistSortMethod
|
enum class ArtistSortMethod
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
@@ -193,7 +210,7 @@ namespace lms::db
|
|||||||
LastWrittenDesc,
|
LastWrittenDesc,
|
||||||
AddedDesc,
|
AddedDesc,
|
||||||
StarredDateDesc,
|
StarredDateDesc,
|
||||||
FileName,
|
AbsoluteFilePath,
|
||||||
Name,
|
Name,
|
||||||
DateDescAndRelease,
|
DateDescAndRelease,
|
||||||
Release, // order by disc/track number
|
Release, // order by disc/track number
|
||||||
|
|||||||
+18
-11
@@ -25,21 +25,23 @@
|
|||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <Wt/Dbo/Dbo.h>
|
#include <Wt/Dbo/Field.h>
|
||||||
|
#include <Wt/Dbo/collection.h>
|
||||||
#include <Wt/WDateTime.h>
|
#include <Wt/WDateTime.h>
|
||||||
|
|
||||||
#include "core/EnumSet.hpp"
|
#include "core/EnumSet.hpp"
|
||||||
#include "core/UUID.hpp"
|
#include "core/UUID.hpp"
|
||||||
#include "database/ArtistId.hpp"
|
#include "database/IdRange.hpp"
|
||||||
#include "database/ArtworkId.hpp"
|
|
||||||
#include "database/ClusterId.hpp"
|
|
||||||
#include "database/Filters.hpp"
|
|
||||||
#include "database/MediaLibraryId.hpp"
|
|
||||||
#include "database/Object.hpp"
|
#include "database/Object.hpp"
|
||||||
#include "database/ReleaseId.hpp"
|
|
||||||
#include "database/TrackId.hpp"
|
|
||||||
#include "database/Types.hpp"
|
#include "database/Types.hpp"
|
||||||
#include "database/UserId.hpp"
|
#include "database/objects/ArtistId.hpp"
|
||||||
|
#include "database/objects/ArtworkId.hpp"
|
||||||
|
#include "database/objects/ClusterId.hpp"
|
||||||
|
#include "database/objects/Filters.hpp"
|
||||||
|
#include "database/objects/MediaLibraryId.hpp"
|
||||||
|
#include "database/objects/ReleaseId.hpp"
|
||||||
|
#include "database/objects/TrackId.hpp"
|
||||||
|
#include "database/objects/UserId.hpp"
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
@@ -56,6 +58,8 @@ namespace lms::db
|
|||||||
class Artist final : public Object<Artist, ArtistId>
|
class Artist final : public Object<Artist, ArtistId>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static constexpr std::size_t maxNameLength{ 512 };
|
||||||
|
|
||||||
struct FindParameters
|
struct FindParameters
|
||||||
{
|
{
|
||||||
Filters filters;
|
Filters filters;
|
||||||
@@ -125,12 +129,17 @@ namespace lms::db
|
|||||||
static pointer find(Session& session, ArtistId id);
|
static pointer find(Session& session, ArtistId id);
|
||||||
static std::vector<pointer> find(Session& session, std::string_view name); // exact match on name field
|
static std::vector<pointer> find(Session& session, std::string_view name); // exact match on name field
|
||||||
static void find(Session& session, ArtistId& lastRetrievedArtist, std::size_t count, const std::function<void(const Artist::pointer&)>& func, MediaLibraryId library = {});
|
static void find(Session& session, ArtistId& lastRetrievedArtist, std::size_t count, const std::function<void(const Artist::pointer&)>& func, MediaLibraryId library = {});
|
||||||
|
static void find(Session& session, const IdRange<ArtistId>& idRange, const std::function<void(const Artist::pointer&)>& func);
|
||||||
static RangeResults<pointer> find(Session& session, const FindParameters& params);
|
static RangeResults<pointer> find(Session& session, const FindParameters& params);
|
||||||
static void find(Session& session, const FindParameters& params, std::function<void(const pointer&)> func);
|
static void find(Session& session, const FindParameters& params, std::function<void(const pointer&)> func);
|
||||||
|
static IdRange<ArtistId> findNextIdRange(Session& session, ArtistId lastRetrievedId, std::size_t count);
|
||||||
static RangeResults<ArtistId> findIds(Session& session, const FindParameters& params);
|
static RangeResults<ArtistId> findIds(Session& session, const FindParameters& params);
|
||||||
static RangeResults<ArtistId> findOrphanIds(Session& session, std::optional<Range> range = std::nullopt); // No track related
|
static RangeResults<ArtistId> findOrphanIds(Session& session, std::optional<Range> range = std::nullopt); // No track related
|
||||||
static bool exists(Session& session, ArtistId id);
|
static bool exists(Session& session, ArtistId id);
|
||||||
|
|
||||||
|
// Updates
|
||||||
|
static void updatePreferredArtwork(Session& session, ArtistId artistId, ArtworkId artworkId);
|
||||||
|
|
||||||
// Accessors
|
// Accessors
|
||||||
const std::string& getName() const { return _name; }
|
const std::string& getName() const { return _name; }
|
||||||
const std::string& getSortName() const { return _sortName; }
|
const std::string& getSortName() const { return _sortName; }
|
||||||
@@ -166,8 +175,6 @@ namespace lms::db
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr std::size_t _maxNameLength{ 512 };
|
|
||||||
|
|
||||||
friend class Session;
|
friend class Session;
|
||||||
// Create
|
// Create
|
||||||
Artist(const std::string& name, const std::optional<core::UUID>& MBID = {});
|
Artist(const std::string& name, const std::optional<core::UUID>& MBID = {});
|
||||||
+5
-4
@@ -24,14 +24,14 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
#include <Wt/Dbo/Dbo.h>
|
#include <Wt/Dbo/Field.h>
|
||||||
#include <Wt/WDateTime.h>
|
#include <Wt/WDateTime.h>
|
||||||
|
|
||||||
#include "database/ArtistId.hpp"
|
|
||||||
#include "database/ArtistInfoId.hpp"
|
|
||||||
#include "database/DirectoryId.hpp"
|
|
||||||
#include "database/Object.hpp"
|
#include "database/Object.hpp"
|
||||||
#include "database/Types.hpp"
|
#include "database/Types.hpp"
|
||||||
|
#include "database/objects/ArtistId.hpp"
|
||||||
|
#include "database/objects/ArtistInfoId.hpp"
|
||||||
|
#include "database/objects/DirectoryId.hpp"
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
@@ -53,6 +53,7 @@ namespace lms::db
|
|||||||
static void find(Session& session, ArtistInfoId& lastRetrievedId, std::size_t count, const std::function<void(const pointer&)>& func);
|
static void find(Session& session, ArtistInfoId& lastRetrievedId, std::size_t count, const std::function<void(const pointer&)>& func);
|
||||||
static void findArtistNameNoLongerMatch(Session& session, std::optional<Range> range, const std::function<void(const pointer&)>& func);
|
static void findArtistNameNoLongerMatch(Session& session, std::optional<Range> range, const std::function<void(const pointer&)>& func);
|
||||||
static void findWithArtistNameAmbiguity(Session& session, std::optional<Range> range, bool allowArtistMBIDFallback, const std::function<void(const pointer&)>& func);
|
static void findWithArtistNameAmbiguity(Session& session, std::optional<Range> range, bool allowArtistMBIDFallback, const std::function<void(const pointer&)>& func);
|
||||||
|
static void findAbsoluteFilePath(Session& session, ArtistInfoId& lastRetrievedId, std::size_t count, const std::function<void(ArtistInfoId artistInfoId, const std::filesystem::path& absoluteFilePath)>& func);
|
||||||
|
|
||||||
// getters
|
// getters
|
||||||
std::size_t getScanVersion() const { return _scanVersion; }
|
std::size_t getScanVersion() const { return _scanVersion; }
|
||||||
+7
-4
@@ -19,13 +19,15 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Wt/Dbo/Dbo.h>
|
#include <filesystem>
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Field.h>
|
||||||
#include <Wt/WDateTime.h>
|
#include <Wt/WDateTime.h>
|
||||||
|
|
||||||
#include "database/ArtworkId.hpp"
|
|
||||||
#include "database/ImageId.hpp"
|
|
||||||
#include "database/Object.hpp"
|
#include "database/Object.hpp"
|
||||||
#include "database/TrackEmbeddedImageId.hpp"
|
#include "database/objects/ArtworkId.hpp"
|
||||||
|
#include "database/objects/ImageId.hpp"
|
||||||
|
#include "database/objects/TrackEmbeddedImageId.hpp"
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
@@ -48,6 +50,7 @@ namespace lms::db
|
|||||||
TrackEmbeddedImageId getTrackEmbeddedImageId() const { return _trackEmbeddedImage.id(); }
|
TrackEmbeddedImageId getTrackEmbeddedImageId() const { return _trackEmbeddedImage.id(); }
|
||||||
ImageId getImageId() const { return _image.id(); }
|
ImageId getImageId() const { return _image.id(); }
|
||||||
Wt::WDateTime getLastWrittenTime() const;
|
Wt::WDateTime getLastWrittenTime() const;
|
||||||
|
std::filesystem::path getAbsoluteFilePath() const;
|
||||||
|
|
||||||
template<class Action>
|
template<class Action>
|
||||||
void persist(Action& a)
|
void persist(Action& a)
|
||||||
+3
-4
@@ -22,13 +22,12 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
#include <Wt/Dbo/Dbo.h>
|
#include <Wt/Dbo/Field.h>
|
||||||
#include <Wt/WDateTime.h>
|
#include <Wt/WDateTime.h>
|
||||||
|
|
||||||
#include "database/AuthTokenId.hpp"
|
|
||||||
#include "database/Object.hpp"
|
#include "database/Object.hpp"
|
||||||
#include "database/Types.hpp"
|
#include "database/objects/AuthTokenId.hpp"
|
||||||
#include "database/UserId.hpp"
|
#include "database/objects/UserId.hpp"
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
+5
-5
@@ -22,17 +22,17 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <tuple>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <Wt/Dbo/Dbo.h>
|
#include <Wt/Dbo/Field.h>
|
||||||
|
#include <Wt/Dbo/collection.h>
|
||||||
#include <Wt/WDateTime.h>
|
#include <Wt/WDateTime.h>
|
||||||
|
|
||||||
#include "database/ClusterId.hpp"
|
|
||||||
#include "database/Object.hpp"
|
#include "database/Object.hpp"
|
||||||
#include "database/Release.hpp"
|
|
||||||
#include "database/TrackId.hpp"
|
|
||||||
#include "database/Types.hpp"
|
#include "database/Types.hpp"
|
||||||
|
#include "database/objects/ClusterId.hpp"
|
||||||
|
#include "database/objects/Release.hpp"
|
||||||
|
#include "database/objects/TrackId.hpp"
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
+6
-5
@@ -21,19 +21,20 @@
|
|||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <Wt/Dbo/Dbo.h>
|
#include <Wt/Dbo/Field.h>
|
||||||
|
|
||||||
#include "core/EnumSet.hpp"
|
#include "core/EnumSet.hpp"
|
||||||
#include "database/ArtistId.hpp"
|
|
||||||
#include "database/DirectoryId.hpp"
|
|
||||||
#include "database/MediaLibraryId.hpp"
|
|
||||||
#include "database/Object.hpp"
|
#include "database/Object.hpp"
|
||||||
#include "database/ReleaseId.hpp"
|
|
||||||
#include "database/Types.hpp"
|
#include "database/Types.hpp"
|
||||||
|
#include "database/objects/ArtistId.hpp"
|
||||||
|
#include "database/objects/DirectoryId.hpp"
|
||||||
|
#include "database/objects/MediaLibraryId.hpp"
|
||||||
|
#include "database/objects/ReleaseId.hpp"
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
+4
-4
@@ -22,10 +22,10 @@
|
|||||||
#include <span>
|
#include <span>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "database/ClusterId.hpp"
|
#include "database/objects/ClusterId.hpp"
|
||||||
#include "database/LabelId.hpp"
|
#include "database/objects/LabelId.hpp"
|
||||||
#include "database/MediaLibraryId.hpp"
|
#include "database/objects/MediaLibraryId.hpp"
|
||||||
#include "database/ReleaseTypeId.hpp"
|
#include "database/objects/ReleaseTypeId.hpp"
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
+6
-4
@@ -21,14 +21,15 @@
|
|||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include <Wt/Dbo/Dbo.h>
|
#include <Wt/Dbo/Field.h>
|
||||||
#include <Wt/WDateTime.h>
|
#include <Wt/WDateTime.h>
|
||||||
|
|
||||||
#include "database/DirectoryId.hpp"
|
|
||||||
#include "database/ImageId.hpp"
|
|
||||||
#include "database/Object.hpp"
|
#include "database/Object.hpp"
|
||||||
#include "database/Types.hpp"
|
#include "database/Types.hpp"
|
||||||
|
#include "database/objects/DirectoryId.hpp"
|
||||||
|
#include "database/objects/ImageId.hpp"
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
@@ -69,7 +70,8 @@ namespace lms::db
|
|||||||
static pointer find(Session& session, const std::filesystem::path& file);
|
static pointer find(Session& session, const std::filesystem::path& file);
|
||||||
static RangeResults<pointer> find(Session& session, const FindParameters& params);
|
static RangeResults<pointer> find(Session& session, const FindParameters& params);
|
||||||
static void find(Session& session, const FindParameters& parameters, const std::function<void(const Image::pointer&)>& func);
|
static void find(Session& session, const FindParameters& parameters, const std::function<void(const Image::pointer&)>& func);
|
||||||
static void find(Session& session, ImageId& lastRetrievedImage, std::size_t count, const std::function<void(const Image::pointer&)>& func);
|
static void find(Session& session, ImageId& lastRetrievedId, std::size_t count, const std::function<void(const Image::pointer&)>& func);
|
||||||
|
static void findAbsoluteFilePath(Session& session, ImageId& lastRetrievedId, std::size_t count, const std::function<void(ImageId imageId, const std::filesystem::path& absoluteFilePath)>& func);
|
||||||
|
|
||||||
// getters
|
// getters
|
||||||
const std::filesystem::path& getAbsoluteFilePath() const { return _fileAbsolutePath; }
|
const std::filesystem::path& getAbsoluteFilePath() const { return _fileAbsolutePath; }
|
||||||
+7
-7
@@ -21,17 +21,17 @@
|
|||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include <Wt/Dbo/Dbo.h>
|
#include <Wt/Dbo/Field.h>
|
||||||
#include <Wt/WDateTime.h>
|
#include <Wt/WDateTime.h>
|
||||||
|
|
||||||
#include "database/ArtistId.hpp"
|
|
||||||
#include "database/Filters.hpp"
|
|
||||||
#include "database/ListenId.hpp"
|
|
||||||
#include "database/Object.hpp"
|
#include "database/Object.hpp"
|
||||||
#include "database/ReleaseId.hpp"
|
|
||||||
#include "database/TrackId.hpp"
|
|
||||||
#include "database/Types.hpp"
|
#include "database/Types.hpp"
|
||||||
#include "database/UserId.hpp"
|
#include "database/objects/ArtistId.hpp"
|
||||||
|
#include "database/objects/Filters.hpp"
|
||||||
|
#include "database/objects/ListenId.hpp"
|
||||||
|
#include "database/objects/ReleaseId.hpp"
|
||||||
|
#include "database/objects/TrackId.hpp"
|
||||||
|
#include "database/objects/UserId.hpp"
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
+3
-2
@@ -20,13 +20,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
#include <Wt/Dbo/Dbo.h>
|
#include <Wt/Dbo/Field.h>
|
||||||
|
|
||||||
#include "database/MediaLibraryId.hpp"
|
|
||||||
#include "database/Object.hpp"
|
#include "database/Object.hpp"
|
||||||
|
#include "database/objects/MediaLibraryId.hpp"
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
+8
-2
@@ -24,11 +24,14 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
#include <Wt/Dbo/Dbo.h>
|
#include <Wt/Dbo/Field.h>
|
||||||
|
#include <Wt/Dbo/collection.h>
|
||||||
|
#include <Wt/Dbo/weak_ptr.h>
|
||||||
#include <Wt/WDateTime.h>
|
#include <Wt/WDateTime.h>
|
||||||
|
|
||||||
#include "database/DirectoryId.hpp"
|
#include "database/IdRange.hpp"
|
||||||
#include "database/Object.hpp"
|
#include "database/Object.hpp"
|
||||||
|
#include "database/objects/DirectoryId.hpp"
|
||||||
|
|
||||||
LMS_DECLARE_IDTYPE(PlayListFileId)
|
LMS_DECLARE_IDTYPE(PlayListFileId)
|
||||||
|
|
||||||
@@ -49,6 +52,9 @@ namespace lms::db
|
|||||||
static pointer find(Session& session, PlayListFileId id);
|
static pointer find(Session& session, PlayListFileId id);
|
||||||
static pointer find(Session& session, const std::filesystem::path& path);
|
static pointer find(Session& session, const std::filesystem::path& path);
|
||||||
static void find(Session& session, PlayListFileId& lastRetrievedId, std::size_t count, const std::function<void(const pointer&)>& func);
|
static void find(Session& session, PlayListFileId& lastRetrievedId, std::size_t count, const std::function<void(const pointer&)>& func);
|
||||||
|
static void findAbsoluteFilePath(Session& session, PlayListFileId& lastRetrievedId, std::size_t count, const std::function<void(PlayListFileId playListFileId, const std::filesystem::path& absoluteFilePath)>& func);
|
||||||
|
static void find(Session& session, const IdRange<PlayListFileId>& idRange, const std::function<void(const PlayListFile::pointer&)>& func);
|
||||||
|
static IdRange<PlayListFileId> findNextIdRange(Session& session, PlayListFileId lastRetrievedId, std::size_t count);
|
||||||
|
|
||||||
// getters
|
// getters
|
||||||
const std::filesystem::path& getAbsoluteFilePath() const { return _absoluteFilePath; }
|
const std::filesystem::path& getAbsoluteFilePath() const { return _absoluteFilePath; }
|
||||||
+4
-5
@@ -20,17 +20,16 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <span>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
#include <Wt/Dbo/Dbo.h>
|
#include <Wt/Dbo/Field.h>
|
||||||
|
#include <Wt/Dbo/collection.h>
|
||||||
#include <Wt/WDateTime.h>
|
#include <Wt/WDateTime.h>
|
||||||
|
|
||||||
#include "database/Object.hpp"
|
#include "database/Object.hpp"
|
||||||
#include "database/TrackId.hpp"
|
#include "database/objects/TrackId.hpp"
|
||||||
#include "database/Types.hpp"
|
#include "database/objects/UserId.hpp"
|
||||||
#include "database/UserId.hpp"
|
|
||||||
|
|
||||||
LMS_DECLARE_IDTYPE(PlayQueueId)
|
LMS_DECLARE_IDTYPE(PlayQueueId)
|
||||||
|
|
||||||
+6
-4
@@ -19,14 +19,16 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Wt/Dbo/Dbo.h>
|
#include <optional>
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Field.h>
|
||||||
#include <Wt/WDateTime.h>
|
#include <Wt/WDateTime.h>
|
||||||
|
|
||||||
#include "database/ArtistId.hpp"
|
|
||||||
#include "database/Object.hpp"
|
#include "database/Object.hpp"
|
||||||
#include "database/RatedArtistId.hpp"
|
|
||||||
#include "database/Types.hpp"
|
#include "database/Types.hpp"
|
||||||
#include "database/UserId.hpp"
|
#include "database/objects/ArtistId.hpp"
|
||||||
|
#include "database/objects/RatedArtistId.hpp"
|
||||||
|
#include "database/objects/UserId.hpp"
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
+6
-4
@@ -19,14 +19,16 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Wt/Dbo/Dbo.h>
|
#include <optional>
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Field.h>
|
||||||
#include <Wt/WDateTime.h>
|
#include <Wt/WDateTime.h>
|
||||||
|
|
||||||
#include "database/Object.hpp"
|
#include "database/Object.hpp"
|
||||||
#include "database/RatedReleaseId.hpp"
|
|
||||||
#include "database/ReleaseId.hpp"
|
|
||||||
#include "database/Types.hpp"
|
#include "database/Types.hpp"
|
||||||
#include "database/UserId.hpp"
|
#include "database/objects/RatedReleaseId.hpp"
|
||||||
|
#include "database/objects/ReleaseId.hpp"
|
||||||
|
#include "database/objects/UserId.hpp"
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
+6
-4
@@ -19,14 +19,16 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Wt/Dbo/Dbo.h>
|
#include <optional>
|
||||||
|
|
||||||
|
#include <Wt/Dbo/Field.h>
|
||||||
#include <Wt/WDateTime.h>
|
#include <Wt/WDateTime.h>
|
||||||
|
|
||||||
#include "database/Object.hpp"
|
#include "database/Object.hpp"
|
||||||
#include "database/RatedTrackId.hpp"
|
|
||||||
#include "database/TrackId.hpp"
|
|
||||||
#include "database/Types.hpp"
|
#include "database/Types.hpp"
|
||||||
#include "database/UserId.hpp"
|
#include "database/objects/RatedTrackId.hpp"
|
||||||
|
#include "database/objects/TrackId.hpp"
|
||||||
|
#include "database/objects/UserId.hpp"
|
||||||
|
|
||||||
namespace lms::db
|
namespace lms::db
|
||||||
{
|
{
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user