mirror of
https://github.com/navidrome/navidrome.git
synced 2025-04-04 21:17:37 +03:00
* fix(server): more race conditions when updating artist/album from external sources
Signed-off-by: Deluan <deluan@navidrome.org>
* feat(scanner): add .gitignore syntax to .ndignore. Resolves #1394
Signed-off-by: Deluan <deluan@navidrome.org>
* fix(ui): null
Signed-off-by: Deluan <deluan@navidrome.org>
* fix(scanner): pass configfile option to child process
Signed-off-by: Deluan <deluan@navidrome.org>
* fix(scanner): resume interrupted fullScans
Signed-off-by: Deluan <deluan@navidrome.org>
* fix(scanner): remove old scanner code
Signed-off-by: Deluan <deluan@navidrome.org>
* fix(scanner): rename old metadata package
Signed-off-by: Deluan <deluan@navidrome.org>
* fix(scanner): move old metadata package
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: tests
Signed-off-by: Deluan <deluan@navidrome.org>
* chore(deps): update Go to 1.23.4
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: logs
Signed-off-by: Deluan <deluan@navidrome.org>
* fix(test):
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: log level
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: remove log message
Signed-off-by: Deluan <deluan@navidrome.org>
* feat: add config for scanner watcher
Signed-off-by: Deluan <deluan@navidrome.org>
* refactor: children playlists
Signed-off-by: Deluan <deluan@navidrome.org>
* refactor: replace `interface{}` with `any`
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: smart playlists with genres
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: allow any tags in smart playlists
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: artist names in playlists
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: smart playlist's sort by tags
Signed-off-by: Deluan <deluan@navidrome.org>
* feat(subsonic): add moods to child
Signed-off-by: Deluan <deluan@navidrome.org>
* feat(subsonic): add moods to AlbumID3
Signed-off-by: Deluan <deluan@navidrome.org>
* refactor(subsonic): use generic JSONArray for OS arrays
Signed-off-by: Deluan <deluan@navidrome.org>
* refactor(subsonic): use https in test
Signed-off-by: Deluan <deluan@navidrome.org>
* feat(subsonic): add releaseTypes to AlbumID3
Signed-off-by: Deluan <deluan@navidrome.org>
* feat(subsonic): add recordLabels to AlbumID3
Signed-off-by: Deluan <deluan@navidrome.org>
* refactor(subsonic): rename JSONArray to Array
Signed-off-by: Deluan <deluan@navidrome.org>
* feat(subsonic): add artists to AlbumID3
Signed-off-by: Deluan <deluan@navidrome.org>
* feat(subsonic): add artists to Child
Signed-off-by: Deluan <deluan@navidrome.org>
* fix(scanner): do not pre-populate smart playlists
Signed-off-by: Deluan <deluan@navidrome.org>
* feat(subsonic): implement a simplified version of ArtistID3.
See https://github.com/opensubsonic/open-subsonic-api/discussions/120
Signed-off-by: Deluan <deluan@navidrome.org>
* feat(subsonic): add artists to album child
Signed-off-by: Deluan <deluan@navidrome.org>
* feat(subsonic): add contributors to mediafile Child
Signed-off-by: Deluan <deluan@navidrome.org>
* feat(subsonic): add albumArtists to mediafile Child
Signed-off-by: Deluan <deluan@navidrome.org>
* feat(subsonic): add displayArtist and displayAlbumArtist
Signed-off-by: Deluan <deluan@navidrome.org>
* feat(subsonic): add displayComposer to Child
Signed-off-by: Deluan <deluan@navidrome.org>
* feat(subsonic): add roles to ArtistID3
Signed-off-by: Deluan <deluan@navidrome.org>
* fix(subsonic): use " • " separator for displayComposer
Signed-off-by: Deluan <deluan@navidrome.org>
* refactor:
Signed-off-by: Deluan <deluan@navidrome.org>
* fix(subsonic):
Signed-off-by: Deluan <deluan@navidrome.org>
* fix(subsonic): respect `PreferSortTags` config option
Signed-off-by: Deluan <deluan@navidrome.org>
* refactor(subsonic):
Signed-off-by: Deluan <deluan@navidrome.org>
* refactor: optimize purging non-unused tags
Signed-off-by: Deluan <deluan@navidrome.org>
* refactor: don't run 'refresh artist stats' concurrently with other transactions
Signed-off-by: Deluan <deluan@navidrome.org>
* refactor:
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: log message
Signed-off-by: Deluan <deluan@navidrome.org>
* feat: add Scanner.ScanOnStartup config option, default true
Signed-off-by: Deluan <deluan@navidrome.org>
* feat: better json parsing error msg when importing NSPs
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: don't update album's imported_time when updating external_metadata
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: handle interrupted scans and full scans after migrations
Signed-off-by: Deluan <deluan@navidrome.org>
* feat: run `analyze` when migration requires a full rescan
Signed-off-by: Deluan <deluan@navidrome.org>
* feat: run `PRAGMA optimize` at the end of the scan
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: don't update artist's updated_at when updating external_metadata
Signed-off-by: Deluan <deluan@navidrome.org>
* feat: handle multiple artists and roles in smart playlists
Signed-off-by: Deluan <deluan@navidrome.org>
* feat(ui): dim missing tracks
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: album missing logic
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: error encoding in gob
Signed-off-by: Deluan <deluan@navidrome.org>
* feat: separate warnings from errors
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: mark albums as missing if they were contained in a deleted folder
Signed-off-by: Deluan <deluan@navidrome.org>
* refactor: add participant names to media_file and album tables
Signed-off-by: Deluan <deluan@navidrome.org>
* refactor: use participations in criteria, instead of m2m relationship
Signed-off-by: Deluan <deluan@navidrome.org>
* refactor: rename participations to participants
Signed-off-by: Deluan <deluan@navidrome.org>
* feat(subsonic): add moods to album child
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: albumartist role case
Signed-off-by: Deluan <deluan@navidrome.org>
* feat(scanner): run scanner as an external process by default
Signed-off-by: Deluan <deluan@navidrome.org>
* fix(ui): show albumArtist names
Signed-off-by: Deluan <deluan@navidrome.org>
* fix(ui): dim out missing albums
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: flaky test
Signed-off-by: Deluan <deluan@navidrome.org>
* fix(server): scrobble buffer mapping. fix #3583
Signed-off-by: Deluan <deluan@navidrome.org>
* refactor: more participations renaming
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: listenbrainz scrobbling
Signed-off-by: Deluan <deluan@navidrome.org>
* feat: send release_group_mbid to listenbrainz
Signed-off-by: Deluan <deluan@navidrome.org>
* feat(subsonic): implement OpenSubsonic explicitStatus field (#3597)
* feat: implement OpenSubsonic explicitStatus field
* fix(subsonic): fix failing snapshot tests
* refactor: create helper for setting explicitStatus
* fix: store smaller values for explicit-status on database
* test: ToAlbum explicitStatus
* refactor: rename explicitStatus helper function
---------
Co-authored-by: Deluan Quintão <deluan@navidrome.org>
* fix: handle album and track tags in the DB based on the mappings.yaml file
Signed-off-by: Deluan <deluan@navidrome.org>
* save similar artists as JSONB
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: getAlbumList byGenre
Signed-off-by: Deluan <deluan@navidrome.org>
* detect changes in PID configuration
Signed-off-by: Deluan <deluan@navidrome.org>
* set default album PID to legacy_pid
Signed-off-by: Deluan <deluan@navidrome.org>
* fix tests
Signed-off-by: Deluan <deluan@navidrome.org>
* fix SIGSEGV
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: don't lose album stars/ratings when migrating
Signed-off-by: Deluan <deluan@navidrome.org>
* store full PID conf in properties
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: keep album annotations when changing PID.Album config
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: reassign album annotations
Signed-off-by: Deluan <deluan@navidrome.org>
* feat: use (display) albumArtist and add links to each artist
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: not showing albums by albumartist
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: error msgs
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: hide PID from Native API
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: album cover art resolution
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: trim participant names
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: reduce watcher log spam
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: panic when initializing the watcher
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: various artists
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: don't store empty lyrics in the DB
Signed-off-by: Deluan <deluan@navidrome.org>
* remove unused methods
Signed-off-by: Deluan <deluan@navidrome.org>
* drop full_text indexes, as they are not being used by SQLite
Signed-off-by: Deluan <deluan@navidrome.org>
* keep album created_at when upgrading
Signed-off-by: Deluan <deluan@navidrome.org>
* fix(ui): null pointer
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: album artwork cache
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: don't expose missing files in Subsonic API
Signed-off-by: Deluan <deluan@navidrome.org>
* refactor: searchable interface
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: filter out missing items from subsonic search
* fix: filter out missing items from playlists
* fix: filter out missing items from shares
Signed-off-by: Deluan <deluan@navidrome.org>
* feat(ui): add filter by artist role
Signed-off-by: Deluan <deluan@navidrome.org>
* feat(subsonic): only return albumartists in getIndexes and getArtists endpoints
Signed-off-by: Deluan <deluan@navidrome.org>
* sort roles alphabetically
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: artist playcounts
Signed-off-by: Deluan <deluan@navidrome.org>
* change default Album PID conf
Signed-off-by: Deluan <deluan@navidrome.org>
* fix albumartist link when it does not match any albumartists values
Signed-off-by: Deluan <deluan@navidrome.org>
* fix `Ignoring filter not whitelisted` (role) message
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: trim any names/titles being imported
Signed-off-by: Deluan <deluan@navidrome.org>
* remove unused genre code
Signed-off-by: Deluan <deluan@navidrome.org>
* serialize calls to Last.fm's getArtist
Signed-off-by: Deluan <deluan@navidrome.org>
xxx
Signed-off-by: Deluan <deluan@navidrome.org>
* add counters to genres
Signed-off-by: Deluan <deluan@navidrome.org>
* nit: fix migration `notice` message
Signed-off-by: Deluan <deluan@navidrome.org>
* optimize similar artists query
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: last.fm.getInfo when mbid does not exist
Signed-off-by: Deluan <deluan@navidrome.org>
* ui only show missing items for admins
Signed-off-by: Deluan <deluan@navidrome.org>
* don't allow interaction with missing items
Signed-off-by: Deluan <deluan@navidrome.org>
* Add Missing Files view (WIP)
Signed-off-by: Deluan <deluan@navidrome.org>
* refactor: merged tag_counts into tag table
Signed-off-by: Deluan <deluan@navidrome.org>
* add option to completely disable automatic scanner
Signed-off-by: Deluan <deluan@navidrome.org>
* add delete missing files functionality
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: playlists not showing for regular users
Signed-off-by: Deluan <deluan@navidrome.org>
* reduce updateLastAccess frequency to once every minute
Signed-off-by: Deluan <deluan@navidrome.org>
* reduce update player frequency to once every minute
Signed-off-by: Deluan <deluan@navidrome.org>
* add timeout when updating player
Signed-off-by: Deluan <deluan@navidrome.org>
* remove dead code
Signed-off-by: Deluan <deluan@navidrome.org>
* fix duplicated roles in stats
Signed-off-by: Deluan <deluan@navidrome.org>
* add `; ` to artist splitters
Signed-off-by: Deluan <deluan@navidrome.org>
* fix stats query
Signed-off-by: Deluan <deluan@navidrome.org>
* more logs
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: support legacy clients (DSub) by removing OpenSubsonic extra fields - WIP
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: support legacy clients (DSub) by removing OpenSubsonic extra fields - WIP
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: support legacy clients (DSub) by removing OpenSubsonic extra fields - WIP
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: support legacy clients (DSub) by removing OpenSubsonic extra fields - WIP
Signed-off-by: Deluan <deluan@navidrome.org>
* add record label filter
Signed-off-by: Deluan <deluan@navidrome.org>
* add release type filter
Signed-off-by: Deluan <deluan@navidrome.org>
* fix purgeUnused tags
Signed-off-by: Deluan <deluan@navidrome.org>
* add grouping filter to albums
Signed-off-by: Deluan <deluan@navidrome.org>
* allow any album tags to be used in as filters in the API
Signed-off-by: Deluan <deluan@navidrome.org>
* remove empty tags from album info
Signed-off-by: Deluan <deluan@navidrome.org>
* comments in the migration
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: Cannot read properties of undefined
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: listenbrainz scrobbling (#3640)
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: remove duplicated tag values
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: don't ignore the taglib folder!
Signed-off-by: Deluan <deluan@navidrome.org>
* feat: show track subtitle tag
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: show artists stats based on selected role
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: inspect
Signed-off-by: Deluan <deluan@navidrome.org>
* add media type to album info/filters
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: change format of subtitle in the UI
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: subtitle in Subsonic API and search
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: subtitle in UI's player
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: split strings should be case-insensitive
Signed-off-by: Deluan <deluan@navidrome.org>
* disable ScanSchedule
Signed-off-by: Deluan <deluan@navidrome.org>
* increase default sessiontimeout
Signed-off-by: Deluan <deluan@navidrome.org>
* add sqlite command line tool to docker image
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: resources override
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: album PID conf
Signed-off-by: Deluan <deluan@navidrome.org>
* change migration to mark current artists as albumArtists
Signed-off-by: Deluan <deluan@navidrome.org>
* feat(ui): Allow filtering on multiple genres (#3679)
* feat(ui): Allow filtering on multiple genres
Signed-off-by: Henrik Nordvik <henrikno@gmail.com>
Signed-off-by: Deluan <deluan@navidrome.org>
* add multi-genre filter in Album list
Signed-off-by: Deluan <deluan@navidrome.org>
---------
Signed-off-by: Henrik Nordvik <henrikno@gmail.com>
Signed-off-by: Deluan <deluan@navidrome.org>
Co-authored-by: Henrik Nordvik <henrikno@gmail.com>
* add more multi-valued tag filters to Album and Song views
Signed-off-by: Deluan <deluan@navidrome.org>
* fix(ui): unselect missing files after removing
Signed-off-by: Deluan <deluan@navidrome.org>
* fix(ui): song filter
Signed-off-by: Deluan <deluan@navidrome.org>
* fix sharing tracks. fix #3687
Signed-off-by: Deluan <deluan@navidrome.org>
* use rowids when using search for sync (ex: Symfonium)
Signed-off-by: Deluan <deluan@navidrome.org>
* fix "Report Real Paths" option for subsonic clients
Signed-off-by: Deluan <deluan@navidrome.org>
* fix "Report Real Paths" option for subsonic clients for search
Signed-off-by: Deluan <deluan@navidrome.org>
* add libraryPath to Native API /songs endpoint
Signed-off-by: Deluan <deluan@navidrome.org>
* feat(subsonic): add album version
Signed-off-by: Deluan <deluan@navidrome.org>
* made all tags lowercase as they are case-insensitive anyways.
Signed-off-by: Deluan <deluan@navidrome.org>
* feat(ui): Show full paths, extended properties for album/song (#3691)
* feat(ui): Show full paths, extended properties for album/song
- uses library path + os separator + path
- show participants (album/song) and tags (song)
- make album/participant clickable in show info
* add source to path
* fix pathSeparator in UI
Signed-off-by: Deluan <deluan@navidrome.org>
* fix local artist artwork (#3695)
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: parse vorbis performers
Signed-off-by: Deluan <deluan@navidrome.org>
* refactor: clean function into smaller functions
Signed-off-by: Deluan <deluan@navidrome.org>
* fix translations for en and pt
Signed-off-by: Deluan <deluan@navidrome.org>
* add trace log to show annotations reassignment
Signed-off-by: Deluan <deluan@navidrome.org>
* add trace log to show annotations reassignment
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: allow performers without instrument/subrole
Signed-off-by: Deluan <deluan@navidrome.org>
* refactor: metadata clean function again
Signed-off-by: Deluan <deluan@navidrome.org>
* refactor: optimize split function
Signed-off-by: Deluan <deluan@navidrome.org>
* refactor: split function is now a method of TagConf
Signed-off-by: Deluan <deluan@navidrome.org>
* fix: humanize Artist total size
Signed-off-by: Deluan <deluan@navidrome.org>
* add album version to album details
Signed-off-by: Deluan <deluan@navidrome.org>
* don't display album-level tags in SongInfo
Signed-off-by: Deluan <deluan@navidrome.org>
* fix genre clicking in Album Page
Signed-off-by: Deluan <deluan@navidrome.org>
* don't use mbids in Last.fm api calls.
From 1337574018
:
With MBID:
```
GET https://ws.audioscrobbler.com/2.0/?api_key=XXXX&artist=Van+Morrison&format=json&lang=en&mbid=a41ac10f-0a56-4672-9161-b83f9b223559&method=artist.getInfo
{
artist: {
name: "Bee Gees",
mbid: "bf0f7e29-dfe1-416c-b5c6-f9ebc19ea810",
url: "https://www.last.fm/music/Bee+Gees",
}
```
Without MBID:
```
GET https://ws.audioscrobbler.com/2.0/?api_key=XXXX&artist=Van+Morrison&format=json&lang=en&method=artist.getInfo
{
artist: {
name: "Van Morrison",
mbid: "a41ac10f-0a56-4672-9161-b83f9b223559",
url: "https://www.last.fm/music/Van+Morrison",
}
```
Signed-off-by: Deluan <deluan@navidrome.org>
* better logging for when the artist folder is not found
Signed-off-by: Deluan <deluan@navidrome.org>
* fix various issues with artist image resolution
Signed-off-by: Deluan <deluan@navidrome.org>
* hide "Additional Tags" header if there are none.
Signed-off-by: Deluan <deluan@navidrome.org>
* simplify tag rendering
Signed-off-by: Deluan <deluan@navidrome.org>
* enhance logging for artist folder detection
Signed-off-by: Deluan <deluan@navidrome.org>
* make folderID consistent for relative and absolute folderPaths
Signed-off-by: Deluan <deluan@navidrome.org>
* handle more folder paths scenarios
Signed-off-by: Deluan <deluan@navidrome.org>
* filter out other roles when SubsonicArtistParticipations = true
Signed-off-by: Deluan <deluan@navidrome.org>
* fix "Cannot read properties of undefined"
Signed-off-by: Deluan <deluan@navidrome.org>
* fix lyrics and comments being truncated (#3701)
* fix lyrics and comments being truncated
* specifically test for lyrics and comment length
* reorder assertions
Signed-off-by: Deluan <deluan@navidrome.org>
---------
Signed-off-by: Deluan <deluan@navidrome.org>
Co-authored-by: Deluan <deluan@navidrome.org>
* fix(server): Expose library_path for playlist (#3705)
Allows showing absolute path for UI, and makes "report real path" work for playlists (Subsonic)
* fix BFR on Windows (#3704)
* fix potential reflected cross-site scripting vulnerability
Signed-off-by: Deluan <deluan@navidrome.org>
* hack to make it work on Windows
* ignore windows executables
* try fixing the pipeline
Signed-off-by: Deluan <deluan@navidrome.org>
* allow MusicFolder in other drives
* move windows local drive logic to local storage implementation
---------
Signed-off-by: Deluan <deluan@navidrome.org>
* increase pagination sizes for missing files
Signed-off-by: Deluan <deluan@navidrome.org>
* reduce level of "already scanning" watcher log message
Signed-off-by: Deluan <deluan@navidrome.org>
* only count folders with audio files in it
See https://github.com/navidrome/navidrome/discussions/3676#discussioncomment-11990930
Signed-off-by: Deluan <deluan@navidrome.org>
* add album version and catalog number to search
Signed-off-by: Deluan <deluan@navidrome.org>
* add `organization` alias for `recordlabel`
Signed-off-by: Deluan <deluan@navidrome.org>
* remove mbid from Last.fm agent
Signed-off-by: Deluan <deluan@navidrome.org>
* feat: support inspect in ui (#3726)
* inspect in ui
* address round 1
* add catalogNum to AlbumInfo
Signed-off-by: Deluan <deluan@navidrome.org>
* remove dependency on metadata_old (deprecated) package
Signed-off-by: Deluan <deluan@navidrome.org>
* add `RawTags` to model
Signed-off-by: Deluan <deluan@navidrome.org>
* support parsing MBIDs for roles (from the https://github.com/kgarner7/picard-all-mbids plugin) (#3698)
* parse standard roles, vorbis/m4a work for now
* fix djmixer
* working roles, use DJ-mix
* add performers to file
* map mbids
* add a few more tests
* add test
Signed-off-by: Deluan <deluan@navidrome.org>
* try to simplify the performers logic
Signed-off-by: Deluan <deluan@navidrome.org>
* stylistic changes
---------
Signed-off-by: Deluan <deluan@navidrome.org>
Co-authored-by: Deluan <deluan@navidrome.org>
* remove param mutation
Signed-off-by: Deluan <deluan@navidrome.org>
* run automated SQLite optimizations
Signed-off-by: Deluan <deluan@navidrome.org>
* fix playlists import/export on Windows
* fix import playlists
* fix export playlists
* better handling of Windows volumes
Signed-off-by: Deluan <deluan@navidrome.org>
* handle more album ID reassignments
Signed-off-by: Deluan <deluan@navidrome.org>
* allow adding/overriding tags in the config file
Signed-off-by: Deluan <deluan@navidrome.org>
* fix(ui): Fix playlist track id, handle missing tracks better (#3734)
- Use `mediaFileId` instead of `id` for playlist tracks
- Only fetch if the file is not missing
- If extractor fails to get the file, also error (rather than panic)
* optimize DB after each scan.
Signed-off-by: Deluan <deluan@navidrome.org>
* remove sortable from AlbumSongs columns
Signed-off-by: Deluan <deluan@navidrome.org>
* simplify query to get missing tracks
Signed-off-by: Deluan <deluan@navidrome.org>
* mark Scanner.Extractor as deprecated
Signed-off-by: Deluan <deluan@navidrome.org>
---------
Signed-off-by: Deluan <deluan@navidrome.org>
Signed-off-by: Henrik Nordvik <henrikno@gmail.com>
Co-authored-by: Caio Cotts <caio@cotts.com.br>
Co-authored-by: Henrik Nordvik <henrikno@gmail.com>
Co-authored-by: Kendall Garner <17521368+kgarner7@users.noreply.github.com>
293 lines
9.1 KiB
Go
293 lines
9.1 KiB
Go
package metadata_test
|
|
|
|
import (
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/navidrome/navidrome/model"
|
|
"github.com/navidrome/navidrome/model/metadata"
|
|
"github.com/navidrome/navidrome/utils"
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
var _ = Describe("Metadata", func() {
|
|
var (
|
|
filePath string
|
|
fileInfo os.FileInfo
|
|
props metadata.Info
|
|
md metadata.Metadata
|
|
)
|
|
|
|
BeforeEach(func() {
|
|
// It is easier to have a real file to test the mod and birth times
|
|
filePath = utils.TempFileName("test", ".mp3")
|
|
f, _ := os.Create(filePath)
|
|
DeferCleanup(func() {
|
|
_ = f.Close()
|
|
_ = os.Remove(filePath)
|
|
})
|
|
|
|
fileInfo, _ = os.Stat(filePath)
|
|
props = metadata.Info{
|
|
AudioProperties: metadata.AudioProperties{
|
|
Duration: time.Minute * 3,
|
|
BitRate: 320,
|
|
},
|
|
HasPicture: true,
|
|
FileInfo: testFileInfo{fileInfo},
|
|
}
|
|
})
|
|
|
|
Describe("Metadata", func() {
|
|
Describe("New", func() {
|
|
It("should create a new Metadata object with the correct properties", func() {
|
|
props.Tags = model.RawTags{
|
|
"©ART": {"First Artist", "Second Artist"},
|
|
"----:com.apple.iTunes:CATALOGNUMBER": {"1234"},
|
|
"tbpm": {"120.6"},
|
|
"WM/IsCompilation": {"1"},
|
|
}
|
|
md = metadata.New(filePath, props)
|
|
|
|
Expect(md.FilePath()).To(Equal(filePath))
|
|
Expect(md.ModTime()).To(Equal(fileInfo.ModTime()))
|
|
Expect(md.BirthTime()).To(BeTemporally("~", md.ModTime(), time.Second))
|
|
Expect(md.Size()).To(Equal(fileInfo.Size()))
|
|
Expect(md.Suffix()).To(Equal("mp3"))
|
|
Expect(md.AudioProperties()).To(Equal(props.AudioProperties))
|
|
Expect(md.Length()).To(Equal(float32(3 * 60)))
|
|
Expect(md.HasPicture()).To(Equal(props.HasPicture))
|
|
Expect(md.Strings(model.TagTrackArtist)).To(Equal([]string{"First Artist", "Second Artist"}))
|
|
Expect(md.String(model.TagTrackArtist)).To(Equal("First Artist"))
|
|
Expect(md.Int(model.TagCatalogNumber)).To(Equal(int64(1234)))
|
|
Expect(md.Float(model.TagBPM)).To(Equal(120.6))
|
|
Expect(md.Bool(model.TagCompilation)).To(BeTrue())
|
|
Expect(md.All()).To(SatisfyAll(
|
|
HaveLen(4),
|
|
HaveKeyWithValue(model.TagTrackArtist, []string{"First Artist", "Second Artist"}),
|
|
HaveKeyWithValue(model.TagBPM, []string{"120.6"}),
|
|
HaveKeyWithValue(model.TagCompilation, []string{"1"}),
|
|
HaveKeyWithValue(model.TagCatalogNumber, []string{"1234"}),
|
|
))
|
|
|
|
})
|
|
|
|
It("should clean the tags map correctly", func() {
|
|
const unknownTag = "UNKNOWN_TAG"
|
|
props.Tags = model.RawTags{
|
|
"TPE1": {"Artist Name", "Artist Name", ""},
|
|
"©ART": {"Second Artist"},
|
|
"CatalogNumber": {""},
|
|
"Album": {"Album Name", "", "Album Name"},
|
|
"Date": {"2022-10-02 12:15:01"},
|
|
"Year": {"2022", "2022", ""},
|
|
"Genre": {"Pop", "", "Pop", "Rock"},
|
|
"Track": {"1/10", "1/10", ""},
|
|
unknownTag: {"value"},
|
|
}
|
|
md = metadata.New(filePath, props)
|
|
|
|
Expect(md.All()).To(SatisfyAll(
|
|
HaveLen(5),
|
|
Not(HaveKey(unknownTag)),
|
|
HaveKeyWithValue(model.TagTrackArtist, []string{"Artist Name", "Second Artist"}),
|
|
HaveKeyWithValue(model.TagAlbum, []string{"Album Name"}),
|
|
HaveKeyWithValue(model.TagRecordingDate, []string{"2022-10-02", "2022"}),
|
|
HaveKeyWithValue(model.TagGenre, []string{"Pop", "Rock"}),
|
|
HaveKeyWithValue(model.TagTrackNumber, []string{"1/10"}),
|
|
))
|
|
})
|
|
|
|
It("should truncate long strings", func() {
|
|
props.Tags = model.RawTags{
|
|
"Title": {strings.Repeat("a", 2048)},
|
|
"Comment": {strings.Repeat("a", 8192)},
|
|
"lyrics:xxx": {strings.Repeat("a", 60000)},
|
|
}
|
|
md = metadata.New(filePath, props)
|
|
|
|
Expect(md.String(model.TagTitle)).To(HaveLen(1024))
|
|
Expect(md.String(model.TagComment)).To(HaveLen(4096))
|
|
pair := md.Pairs(model.TagLyrics)
|
|
|
|
Expect(pair).To(HaveLen(1))
|
|
Expect(pair[0].Key()).To(Equal("xxx"))
|
|
|
|
// Note: a total of 6 characters are lost from maxLength from
|
|
// the key portion and separator
|
|
Expect(pair[0].Value()).To(HaveLen(32762))
|
|
})
|
|
|
|
It("should split multiple values", func() {
|
|
props.Tags = model.RawTags{
|
|
"Genre": {"Rock/Pop;;Punk"},
|
|
}
|
|
md = metadata.New(filePath, props)
|
|
|
|
Expect(md.Strings(model.TagGenre)).To(Equal([]string{"Rock", "Pop", "Punk"}))
|
|
})
|
|
})
|
|
|
|
DescribeTable("Date",
|
|
func(value string, expectedYear int, expectedDate string) {
|
|
props.Tags = model.RawTags{
|
|
"date": {value},
|
|
}
|
|
md = metadata.New(filePath, props)
|
|
|
|
testDate := md.Date(model.TagRecordingDate)
|
|
Expect(string(testDate)).To(Equal(expectedDate))
|
|
Expect(testDate.Year()).To(Equal(expectedYear))
|
|
},
|
|
Entry(nil, "1985", 1985, "1985"),
|
|
Entry(nil, "2002-01", 2002, "2002-01"),
|
|
Entry(nil, "1969.06", 1969, "1969"),
|
|
Entry(nil, "1980.07.25", 1980, "1980"),
|
|
Entry(nil, "2004-00-00", 2004, "2004"),
|
|
Entry(nil, "2016-12-31", 2016, "2016-12-31"),
|
|
Entry(nil, "2016-12-31 12:15", 2016, "2016-12-31"),
|
|
Entry(nil, "2013-May-12", 2013, "2013"),
|
|
Entry(nil, "May 12, 2016", 2016, "2016"),
|
|
Entry(nil, "01/10/1990", 1990, "1990"),
|
|
Entry(nil, "invalid", 0, ""),
|
|
)
|
|
|
|
DescribeTable("NumAndTotal",
|
|
func(num, total string, expectedNum int, expectedTotal int) {
|
|
props.Tags = model.RawTags{
|
|
"Track": {num},
|
|
"TrackTotal": {total},
|
|
}
|
|
md = metadata.New(filePath, props)
|
|
|
|
testNum, testTotal := md.NumAndTotal(model.TagTrackNumber)
|
|
Expect(testNum).To(Equal(expectedNum))
|
|
Expect(testTotal).To(Equal(expectedTotal))
|
|
},
|
|
Entry(nil, "2", "", 2, 0),
|
|
Entry(nil, "2", "10", 2, 10),
|
|
Entry(nil, "2/10", "", 2, 10),
|
|
Entry(nil, "", "", 0, 0),
|
|
Entry(nil, "A", "", 0, 0),
|
|
)
|
|
|
|
Describe("Performers", func() {
|
|
Describe("ID3", func() {
|
|
BeforeEach(func() {
|
|
props.Tags = model.RawTags{
|
|
"PERFORMER:GUITAR": {"Guitarist 1", "Guitarist 2"},
|
|
"PERFORMER:BACKGROUND VOCALS": {"Backing Singer"},
|
|
"PERFORMER:PERFORMER": {"Wonderlove", "Lovewonder"},
|
|
}
|
|
})
|
|
|
|
It("should return the performers", func() {
|
|
md = metadata.New(filePath, props)
|
|
|
|
Expect(md.All()).To(HaveKey(model.TagPerformer))
|
|
Expect(md.Strings(model.TagPerformer)).To(ConsistOf(
|
|
metadata.NewPair("guitar", "Guitarist 1"),
|
|
metadata.NewPair("guitar", "Guitarist 2"),
|
|
metadata.NewPair("background vocals", "Backing Singer"),
|
|
metadata.NewPair("", "Wonderlove"),
|
|
metadata.NewPair("", "Lovewonder"),
|
|
))
|
|
})
|
|
})
|
|
|
|
Describe("Vorbis", func() {
|
|
BeforeEach(func() {
|
|
props.Tags = model.RawTags{
|
|
"PERFORMER": {
|
|
"John Adams (Rhodes piano)",
|
|
"Vincent Henry (alto saxophone, baritone saxophone and tenor saxophone)",
|
|
"Salaam Remi (drums (drum set) and organ)",
|
|
"Amy Winehouse (guitar)",
|
|
"Amy Winehouse (vocals)",
|
|
"Wonderlove",
|
|
},
|
|
}
|
|
})
|
|
|
|
It("should return the performers", func() {
|
|
md = metadata.New(filePath, props)
|
|
|
|
Expect(md.All()).To(HaveKey(model.TagPerformer))
|
|
Expect(md.Strings(model.TagPerformer)).To(ConsistOf(
|
|
metadata.NewPair("rhodes piano", "John Adams"),
|
|
metadata.NewPair("alto saxophone, baritone saxophone and tenor saxophone", "Vincent Henry"),
|
|
metadata.NewPair("drums (drum set) and organ", "Salaam Remi"),
|
|
metadata.NewPair("guitar", "Amy Winehouse"),
|
|
metadata.NewPair("vocals", "Amy Winehouse"),
|
|
metadata.NewPair("", "Wonderlove"),
|
|
))
|
|
})
|
|
})
|
|
})
|
|
|
|
Describe("Lyrics", func() {
|
|
BeforeEach(func() {
|
|
props.Tags = model.RawTags{
|
|
"LYRICS:POR": {"Letras"},
|
|
"LYRICS:ENG": {"Lyrics"},
|
|
}
|
|
})
|
|
|
|
It("should return the lyrics", func() {
|
|
md = metadata.New(filePath, props)
|
|
|
|
Expect(md.All()).To(HaveKey(model.TagLyrics))
|
|
Expect(md.Strings(model.TagLyrics)).To(ContainElements(
|
|
metadata.NewPair("por", "Letras"),
|
|
metadata.NewPair("eng", "Lyrics"),
|
|
))
|
|
})
|
|
})
|
|
|
|
Describe("ReplayGain", func() {
|
|
createMF := func(tag, tagValue string) model.MediaFile {
|
|
props.Tags = model.RawTags{
|
|
tag: {tagValue},
|
|
}
|
|
md = metadata.New(filePath, props)
|
|
return md.ToMediaFile(0, "0")
|
|
}
|
|
|
|
DescribeTable("Gain",
|
|
func(tagValue string, expected float64) {
|
|
mf := createMF("replaygain_track_gain", tagValue)
|
|
Expect(mf.RGTrackGain).To(Equal(expected))
|
|
},
|
|
Entry("0", "0", 0.0),
|
|
Entry("1.2dB", "1.2dB", 1.2),
|
|
Entry("Infinity", "Infinity", 0.0),
|
|
Entry("Invalid value", "INVALID VALUE", 0.0),
|
|
)
|
|
DescribeTable("Peak",
|
|
func(tagValue string, expected float64) {
|
|
mf := createMF("replaygain_track_peak", tagValue)
|
|
Expect(mf.RGTrackPeak).To(Equal(expected))
|
|
},
|
|
Entry("0", "0", 0.0),
|
|
Entry("0.5", "0.5", 0.5),
|
|
Entry("Invalid dB suffix", "0.7dB", 1.0),
|
|
Entry("Infinity", "Infinity", 1.0),
|
|
Entry("Invalid value", "INVALID VALUE", 1.0),
|
|
)
|
|
DescribeTable("getR128GainValue",
|
|
func(tagValue string, expected float64) {
|
|
mf := createMF("r128_track_gain", tagValue)
|
|
Expect(mf.RGTrackGain).To(Equal(expected))
|
|
|
|
},
|
|
Entry("0", "0", 5.0),
|
|
Entry("-3776", "-3776", -9.75),
|
|
Entry("Infinity", "Infinity", 0.0),
|
|
Entry("Invalid value", "INVALID VALUE", 0.0),
|
|
)
|
|
})
|
|
|
|
})
|
|
})
|