Merge branch 'master' into msi-insights-detection

This commit is contained in:
Deluan Quintão 2025-03-22 12:43:09 -04:00 committed by GitHub
commit d342a9a225
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 111 additions and 51 deletions

View File

@ -138,7 +138,6 @@ ENV GODEBUG="asyncpreemptoff=1"
RUN touch /.nddockerenv RUN touch /.nddockerenv
EXPOSE ${ND_PORT} EXPOSE ${ND_PORT}
HEALTHCHECK CMD wget -O- http://localhost:${ND_PORT}/ping || exit 1
WORKDIR /app WORKDIR /app
ENTRYPOINT ["/app/navidrome"] ENTRYPOINT ["/app/navidrome"]

View File

@ -29,7 +29,7 @@ func New() FFmpeg {
} }
const ( const (
extractImageCmd = "ffmpeg -i %s -an -vcodec copy -f image2pipe -" extractImageCmd = "ffmpeg -i %s -map 0:v -map -0:V -vcodec copy -f image2pipe -"
probeCmd = "ffmpeg %s -f ffmetadata" probeCmd = "ffmpeg %s -f ffmetadata"
) )

View File

@ -176,7 +176,11 @@ func (md Metadata) getRoleValues(role model.TagName) []string {
if len(values) == 0 { if len(values) == 0 {
return nil return nil
} }
if conf := model.TagRolesConf(); len(conf.Split) > 0 { conf := model.TagMainMappings()[role]
if conf.Split == nil {
conf = model.TagRolesConf()
}
if len(conf.Split) > 0 {
values = conf.SplitTagValue(values) values = conf.SplitTagValue(values)
return filterDuplicatedOrEmptyValues(values) return filterDuplicatedOrEmptyValues(values)
} }
@ -193,7 +197,11 @@ func (md Metadata) getArtistValues(single, multi model.TagName) []string {
if len(vSingle) != 1 { if len(vSingle) != 1 {
return vSingle return vSingle
} }
if conf := model.TagArtistsConf(); len(conf.Split) > 0 { conf := model.TagMainMappings()[single]
if conf.Split == nil {
conf = model.TagArtistsConf()
}
if len(conf.Split) > 0 {
vSingle = conf.SplitTagValue(vSingle) vSingle = conf.SplitTagValue(vSingle)
return filterDuplicatedOrEmptyValues(vSingle) return filterDuplicatedOrEmptyValues(vSingle)
} }

View File

@ -120,7 +120,7 @@ func (md Metadata) first(key model.TagName) string {
func float(value string, def ...float64) float64 { func float(value string, def ...float64) float64 {
v, err := strconv.ParseFloat(value, 64) v, err := strconv.ParseFloat(value, 64)
if err != nil || v == math.Inf(-1) || v == math.Inf(1) { if err != nil || v == math.Inf(-1) || math.IsInf(v, 1) || math.IsNaN(v) {
if len(def) > 0 { if len(def) > 0 {
return def[0] return def[0]
} }

View File

@ -264,6 +264,7 @@ var _ = Describe("Metadata", func() {
Entry("1.2dB", "1.2dB", 1.2), Entry("1.2dB", "1.2dB", 1.2),
Entry("Infinity", "Infinity", 0.0), Entry("Infinity", "Infinity", 0.0),
Entry("Invalid value", "INVALID VALUE", 0.0), Entry("Invalid value", "INVALID VALUE", 0.0),
Entry("NaN", "NaN", 0.0),
) )
DescribeTable("Peak", DescribeTable("Peak",
func(tagValue string, expected float64) { func(tagValue string, expected float64) {
@ -275,6 +276,7 @@ var _ = Describe("Metadata", func() {
Entry("Invalid dB suffix", "0.7dB", 1.0), Entry("Invalid dB suffix", "0.7dB", 1.0),
Entry("Infinity", "Infinity", 1.0), Entry("Infinity", "Infinity", 1.0),
Entry("Invalid value", "INVALID VALUE", 1.0), Entry("Invalid value", "INVALID VALUE", 1.0),
Entry("NaN", "NaN", 1.0),
) )
DescribeTable("getR128GainValue", DescribeTable("getR128GainValue",
func(tagValue string, expected float64) { func(tagValue string, expected float64) {

View File

@ -201,7 +201,7 @@ func loadTagMappings() {
aliases = oldValue.Aliases aliases = oldValue.Aliases
} }
split := cfg.Split split := cfg.Split
if len(split) == 0 { if split == nil {
split = oldValue.Split split = oldValue.Split
} }
c := TagConf{ c := TagConf{

View File

@ -216,6 +216,7 @@
"username": "Partekatzailea:", "username": "Partekatzailea:",
"url": "URLa", "url": "URLa",
"description": "Deskribapena", "description": "Deskribapena",
"downloadable": "Deskargatzea ahalbidetu?",
"contents": "Edukia", "contents": "Edukia",
"expiresAt": "Iraungitze-data:", "expiresAt": "Iraungitze-data:",
"lastVisitedAt": "Azkenekoz bisitatu zen:", "lastVisitedAt": "Azkenekoz bisitatu zen:",
@ -223,22 +224,24 @@
"format": "Formatua", "format": "Formatua",
"maxBitRate": "Gehienezko bit tasa", "maxBitRate": "Gehienezko bit tasa",
"updatedAt": "Eguneratze-data:", "updatedAt": "Eguneratze-data:",
"createdAt": "Sortze-data:", "createdAt": "Sortze-data:"
"downloadable": "Deskargatzea ahalbidetu?" },
} "notifications": {},
"actions": {}
}, },
"missing": { "missing": {
"name": "", "name": "Fitxategia falta da|||| Fitxategiak falta dira",
"empty": "Ez da fitxategirik falta",
"fields": { "fields": {
"path": "", "path": "Bidea",
"size": "", "size": "Tamaina",
"updatedAt": "" "updatedAt": "Desagertze-data:"
}, },
"actions": { "actions": {
"remove": "" "remove": "Kendu"
}, },
"notifications": { "notifications": {
"removed": "" "removed": "Faltan zeuden fitxategiak kendu dira"
} }
} }
}, },

View File

@ -25,8 +25,13 @@
"quality": "Qualité", "quality": "Qualité",
"bpm": "BPM", "bpm": "BPM",
"playDate": "Derniers joués", "playDate": "Derniers joués",
"channels": "Canaux", "createdAt": "Date d'ajout",
"createdAt": "Date d'ajout" "grouping": "Regroupement",
"mood": "Humeur",
"participants": "Participants supplémentaires",
"tags": "Étiquettes supplémentaires",
"mappedTags": "Étiquettes correspondantes",
"rawTags": "Étiquettes brutes"
}, },
"actions": { "actions": {
"addToQueue": "Ajouter à la file", "addToQueue": "Ajouter à la file",
@ -46,29 +51,35 @@
"duration": "Durée", "duration": "Durée",
"songCount": "Nombre de pistes", "songCount": "Nombre de pistes",
"playCount": "Nombre d'écoutes", "playCount": "Nombre d'écoutes",
"size": "Taille",
"name": "Nom", "name": "Nom",
"genre": "Genre", "genre": "Genre",
"compilation": "Compilation", "compilation": "Compilation",
"year": "Année", "year": "Année",
"originalDate": "Original",
"releaseDate": "Sortie",
"releases": "Sortie |||| Sorties",
"released": "Sortie",
"updatedAt": "Mis à jour le", "updatedAt": "Mis à jour le",
"comment": "Commentaire", "comment": "Commentaire",
"rating": "Classement", "rating": "Classement",
"createdAt": "Date d'ajout", "createdAt": "Date d'ajout",
"size": "Taille", "recordLabel": "Label",
"originalDate": "Original", "catalogNum": "Numéro de catalogue",
"releaseDate": "Sortie", "releaseType": "Type",
"releases": "Sortie |||| Sorties", "grouping": "Regroupement",
"released": "Sortie" "media": "Média",
"mood": "Humeur"
}, },
"actions": { "actions": {
"playAll": "Lire", "playAll": "Lire",
"playNext": "Lire ensuite", "playNext": "Lire ensuite",
"addToQueue": "Ajouter à la file", "addToQueue": "Ajouter à la file",
"share": "Partager",
"shuffle": "Mélanger", "shuffle": "Mélanger",
"addToPlaylist": "Ajouter à la playlist", "addToPlaylist": "Ajouter à la playlist",
"download": "Télécharger", "download": "Télécharger",
"info": "Plus d'informations", "info": "Plus d'informations"
"share": "Partager"
}, },
"lists": { "lists": {
"all": "Tous", "all": "Tous",
@ -86,10 +97,26 @@
"name": "Nom", "name": "Nom",
"albumCount": "Nombre d'albums", "albumCount": "Nombre d'albums",
"songCount": "Nombre de pistes", "songCount": "Nombre de pistes",
"size": "Taille",
"playCount": "Lectures", "playCount": "Lectures",
"rating": "Classement", "rating": "Classement",
"genre": "Genre", "genre": "Genre",
"size": "Taille" "role": "Rôle"
},
"roles": {
"albumartist": "Artiste de l'album |||| Artistes de l'album",
"artist": "Artiste |||| Artistes",
"composer": "Compositeur |||| Compositeurs",
"conductor": "Chef d'orchestre |||| Chefs d'orchestre",
"lyricist": "Parolier |||| Paroliers",
"arranger": "Arrangeur |||| Arrangeurs",
"producer": "Producteur |||| Producteurs",
"director": "Réalisateur |||| Réalisateurs",
"engineer": "Ingénieur |||| Ingénieurs",
"mixer": "Mixeur |||| Mixeurs",
"remixer": "Remixeur |||| Remixeurs",
"djmixer": "Mixeur DJ |||| Mixeurs DJ",
"performer": "Interprète |||| Interprètes"
} }
}, },
"user": { "user": {
@ -98,6 +125,7 @@
"userName": "Nom d'utilisateur", "userName": "Nom d'utilisateur",
"isAdmin": "Administrateur", "isAdmin": "Administrateur",
"lastLoginAt": "Dernière connexion", "lastLoginAt": "Dernière connexion",
"lastAccessAt": "Dernier accès",
"updatedAt": "Dernière mise à jour", "updatedAt": "Dernière mise à jour",
"name": "Nom", "name": "Nom",
"password": "Mot de passe", "password": "Mot de passe",
@ -105,8 +133,7 @@
"changePassword": "Changer le mot de passe ?", "changePassword": "Changer le mot de passe ?",
"currentPassword": "Mot de passe actuel", "currentPassword": "Mot de passe actuel",
"newPassword": "Nouveau mot de passe", "newPassword": "Nouveau mot de passe",
"token": "Token", "token": "Token"
"lastAccessAt": "Dernier accès"
}, },
"helperTexts": { "helperTexts": {
"name": "Les changements liés à votre nom ne seront reflétés qu'à la prochaine connexion" "name": "Les changements liés à votre nom ne seront reflétés qu'à la prochaine connexion"
@ -152,7 +179,7 @@
"public": "Publique", "public": "Publique",
"updatedAt": "Mise à jour le", "updatedAt": "Mise à jour le",
"createdAt": "Créée le", "createdAt": "Créée le",
"songCount": "Titres", "songCount": "Morceaux",
"comment": "Commentaire", "comment": "Commentaire",
"sync": "Import automatique", "sync": "Import automatique",
"path": "Importer depuis" "path": "Importer depuis"
@ -188,6 +215,7 @@
"username": "Partagé(e) par", "username": "Partagé(e) par",
"url": "Lien URL", "url": "Lien URL",
"description": "Description", "description": "Description",
"downloadable": "Autoriser les téléchargements ?",
"contents": "Contenu", "contents": "Contenu",
"expiresAt": "Expire le", "expiresAt": "Expire le",
"lastVisitedAt": "Visité pour la dernière fois", "lastVisitedAt": "Visité pour la dernière fois",
@ -195,8 +223,24 @@
"format": "Format", "format": "Format",
"maxBitRate": "Bitrate maximum", "maxBitRate": "Bitrate maximum",
"updatedAt": "Mis à jour le", "updatedAt": "Mis à jour le",
"createdAt": "Créé le", "createdAt": "Créé le"
"downloadable": "Autoriser les téléchargements ?" },
"notifications": {},
"actions": {}
},
"missing": {
"name": "Fichier manquant|||| Fichiers manquants",
"empty": "Aucun fichier manquant",
"fields": {
"path": "Chemin",
"size": "Taille",
"updatedAt": "A disparu le"
},
"actions": {
"remove": "Supprimer"
},
"notifications": {
"removed": "Fichier(s) manquant(s) supprimé(s)"
} }
} }
}, },
@ -235,6 +279,7 @@
"add": "Ajouter", "add": "Ajouter",
"back": "Retour", "back": "Retour",
"bulk_actions": "%{smart_count} sélectionné |||| %{smart_count} sélectionnés", "bulk_actions": "%{smart_count} sélectionné |||| %{smart_count} sélectionnés",
"bulk_actions_mobile": "1 |||| %{smart_count}",
"cancel": "Annuler", "cancel": "Annuler",
"clear_input_value": "Vider le champ", "clear_input_value": "Vider le champ",
"clone": "Dupliquer", "clone": "Dupliquer",
@ -258,7 +303,6 @@
"close_menu": "Fermer le menu", "close_menu": "Fermer le menu",
"unselect": "Désélectionner", "unselect": "Désélectionner",
"skip": "Ignorer", "skip": "Ignorer",
"bulk_actions_mobile": "1 |||| %{smart_count}",
"share": "Partager", "share": "Partager",
"download": "Télécharger" "download": "Télécharger"
}, },
@ -273,10 +317,10 @@
"error": "Un problème est survenu", "error": "Un problème est survenu",
"list": "%{name}", "list": "%{name}",
"loading": "Chargement", "loading": "Chargement",
"not_found": "Page manquante", "not_found": "Introuvable",
"show": "%{name} #%{id}", "show": "%{name} #%{id}",
"empty": "Pas encore de %{name}.", "empty": "Pas encore de %{name}.",
"invite": "Voulez-vous en créer ?" "invite": "Voulez-vous en créer un ?"
}, },
"input": { "input": {
"file": { "file": {
@ -353,29 +397,31 @@
"noPlaylistsAvailable": "Aucune playlist", "noPlaylistsAvailable": "Aucune playlist",
"delete_user_title": "Supprimer l'utilisateur '%{name}'", "delete_user_title": "Supprimer l'utilisateur '%{name}'",
"delete_user_content": "Êtes-vous sûr(e) de vouloir supprimer cet utilisateur et ses données associées (y compris ses playlists et préférences) ?", "delete_user_content": "Êtes-vous sûr(e) de vouloir supprimer cet utilisateur et ses données associées (y compris ses playlists et préférences) ?",
"remove_missing_title": "Supprimer les fichiers manquants",
"remove_missing_content": "Êtes-vous sûr(e) de vouloir supprimer les fichiers manquants sélectionnés de la base de données ? Ceci supprimera définiviement toute référence à ceux-ci, y compris leurs nombres d'écoutes et leurs notations.",
"notifications_blocked": "Votre navigateur bloque les notifications de ce site", "notifications_blocked": "Votre navigateur bloque les notifications de ce site",
"notifications_not_available": "Votre navigateur ne permet pas d'afficher les notifications sur le bureau ou vous n'accédez pas à Navidrome via HTTPS", "notifications_not_available": "Votre navigateur ne permet pas d'afficher les notifications sur le bureau ou vous n'accédez pas à Navidrome via HTTPS",
"lastfmLinkSuccess": "Last.fm a été correctement relié et le scrobble a été activé", "lastfmLinkSuccess": "Last.fm a été correctement relié et le scrobble a été activé",
"lastfmLinkFailure": "Last.fm n'a pas pu être correctement relié", "lastfmLinkFailure": "Last.fm n'a pas pu être correctement relié",
"lastfmUnlinkSuccess": "Last.fm n'est plus relié et le scrobble a été désactivé", "lastfmUnlinkSuccess": "Last.fm n'est plus relié et le scrobble a été désactivé",
"lastfmUnlinkFailure": "Erreur pendant la suppression du lien avec Last.fm", "lastfmUnlinkFailure": "Erreur pendant la suppression du lien avec Last.fm",
"listenBrainzLinkSuccess": "La liaison et le scrobble avec ListenBrainz sont maintenant activés pour l'utilisateur : %{user}",
"listenBrainzLinkFailure": "Échec lors de la liaison avec ListenBrainz : %{error}",
"listenBrainzUnlinkSuccess": "La liaison et le scrobble avec ListenBrainz sont maintenant désactivés",
"listenBrainzUnlinkFailure": "Échec lors de la désactivation de la liaison avec ListenBrainz",
"openIn": { "openIn": {
"lastfm": "Ouvrir dans Last.fm", "lastfm": "Ouvrir dans Last.fm",
"musicbrainz": "Ouvrir dans MusicBrainz" "musicbrainz": "Ouvrir dans MusicBrainz"
}, },
"lastfmLink": "Lire plus...", "lastfmLink": "Lire plus...",
"listenBrainzLinkSuccess": "La liaison et le scrobble avec ListenBrainz sont maintenant activés pour l'utilisateur : %{user}",
"listenBrainzLinkFailure": "Échec lors de la liaison avec ListenBrainz : %{error}",
"listenBrainzUnlinkSuccess": "La liaison et le scrobble avec ListenBrainz sont maintenant désactivés",
"listenBrainzUnlinkFailure": "Échec lors de la désactivation de la liaison avec ListenBrainz",
"downloadOriginalFormat": "Télécharger au format original",
"shareOriginalFormat": "Partager avec le format original", "shareOriginalFormat": "Partager avec le format original",
"shareDialogTitle": "Partager %{resource} '%{name}'", "shareDialogTitle": "Partager %{resource} '%{name}'",
"shareBatchDialogTitle": "Partager 1 %{resource} |||| Partager %{smart_count} %{resource}", "shareBatchDialogTitle": "Partager 1 %{resource} |||| Partager %{smart_count} %{resource}",
"shareCopyToClipboard": "Copier vers le presse-papier : Ctrl+C, Enter",
"shareSuccess": "Lien copié vers le presse-papier : %{url}", "shareSuccess": "Lien copié vers le presse-papier : %{url}",
"shareFailure": "Erreur en copiant le lien %{url} vers le presse-papier", "shareFailure": "Erreur en copiant le lien %{url} vers le presse-papier",
"downloadDialogTitle": "Télécharger %{resource} '%{name}' (%{size})", "downloadDialogTitle": "Télécharger %{resource} '%{name}' (%{size})",
"shareCopyToClipboard": "Copier vers le presse-papier : Ctrl+C, Enter" "downloadOriginalFormat": "Télécharger au format original"
}, },
"menu": { "menu": {
"library": "Bibliothèque", "library": "Bibliothèque",
@ -389,6 +435,7 @@
"language": "Langue", "language": "Langue",
"defaultView": "Vue par défaut", "defaultView": "Vue par défaut",
"desktop_notifications": "Notifications de bureau", "desktop_notifications": "Notifications de bureau",
"lastfmNotConfigured": "La clef API de Last.fm n'est pas configurée",
"lastfmScrobbling": "Scrobbler vers Last.fm", "lastfmScrobbling": "Scrobbler vers Last.fm",
"listenBrainzScrobbling": "Scrobbler vers ListenBrainz", "listenBrainzScrobbling": "Scrobbler vers ListenBrainz",
"replaygain": "Mode ReplayGain", "replaygain": "Mode ReplayGain",
@ -397,14 +444,13 @@
"none": "Désactivé", "none": "Désactivé",
"album": "Utiliser le gain de l'album", "album": "Utiliser le gain de l'album",
"track": "Utiliser le gain des pistes" "track": "Utiliser le gain des pistes"
}, }
"lastfmNotConfigured": "La clef API de Last.fm n'est pas configurée"
} }
}, },
"albumList": "Albums", "albumList": "Albums",
"about": "À propos",
"playlists": "Playlists", "playlists": "Playlists",
"sharedPlaylists": "Playlists partagées" "sharedPlaylists": "Playlists partagées",
"about": "À propos"
}, },
"player": { "player": {
"playListsText": "File de lecture", "playListsText": "File de lecture",
@ -459,10 +505,10 @@
"toggle_play": "Lecture/Pause", "toggle_play": "Lecture/Pause",
"prev_song": "Morceau précédent", "prev_song": "Morceau précédent",
"next_song": "Morceau suivant", "next_song": "Morceau suivant",
"current_song": "Aller à la chanson en cours",
"vol_up": "Augmenter le volume", "vol_up": "Augmenter le volume",
"vol_down": "Baisser le volume", "vol_down": "Baisser le volume",
"toggle_love": "Ajouter/Enlever le morceau des favoris", "toggle_love": "Ajouter/Enlever le morceau des favoris"
"current_song": "Aller à la chanson en cours"
} }
} }
} }

View File

@ -33,6 +33,8 @@ func (s *scannerExternal) scanAll(ctx context.Context, fullScan bool, progress c
cmd := exec.CommandContext(ctx, exe, "scan", cmd := exec.CommandContext(ctx, exe, "scan",
"--nobanner", "--subprocess", "--nobanner", "--subprocess",
"--configfile", conf.Server.ConfigFile, "--configfile", conf.Server.ConfigFile,
"--datafolder", conf.Server.DataFolder,
"--cachefolder", conf.Server.CacheFolder,
If(fullScan, "--full", "")) If(fullScan, "--full", ""))
in, out := io.Pipe() in, out := io.Pipe()

View File

@ -424,7 +424,7 @@ func (api *Router) buildArtist(r *http.Request, artist *model.Artist) (*response
return nil, err return nil, err
} }
a.Album = slice.MapWithArg(albums, ctx, childFromAlbum) a.Album = slice.MapWithArg(albums, ctx, buildAlbumID3)
return a, nil return a, nil
} }

View File

@ -48,11 +48,11 @@ func AlbumsByArtist() Options {
func AlbumsByArtistID(artistId string) Options { func AlbumsByArtistID(artistId string) Options {
filters := []Sqlizer{ filters := []Sqlizer{
persistence.Exists("json_tree(Participants, '$.albumartist')", Eq{"value": artistId}), persistence.Exists("json_tree(participants, '$.albumartist')", Eq{"value": artistId}),
} }
if conf.Server.Subsonic.ArtistParticipations { if conf.Server.Subsonic.ArtistParticipations {
filters = append(filters, filters = append(filters,
persistence.Exists("json_tree(Participants, '$.artist')", Eq{"value": artistId}), persistence.Exists("json_tree(participants, '$.artist')", Eq{"value": artistId}),
) )
} }
return addDefaultFilters(Options{ return addDefaultFilters(Options{

View File

@ -284,7 +284,7 @@ type OpenSubsonicAlbumID3 struct {
type ArtistWithAlbumsID3 struct { type ArtistWithAlbumsID3 struct {
ArtistID3 ArtistID3
Album []Child `xml:"album" json:"album,omitempty"` Album []AlbumID3 `xml:"album" json:"album,omitempty"`
} }
type AlbumWithSongsID3 struct { type AlbumWithSongsID3 struct {