c795bcfcf7
* 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 https://discord.com/channels/671335427726114836/704303730660737113/1337574018143879248: 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>
596 lines
32 KiB
Go
596 lines
32 KiB
Go
package responses
|
|
|
|
import (
|
|
"encoding/json"
|
|
"encoding/xml"
|
|
"time"
|
|
)
|
|
|
|
type Subsonic struct {
|
|
XMLName xml.Name `xml:"http://subsonic.org/restapi subsonic-response" json:"-"`
|
|
Status string `xml:"status,attr" json:"status"`
|
|
Version string `xml:"version,attr" json:"version"`
|
|
Type string `xml:"type,attr" json:"type"`
|
|
ServerVersion string `xml:"serverVersion,attr" json:"serverVersion"`
|
|
OpenSubsonic bool `xml:"openSubsonic,attr,omitempty" json:"openSubsonic,omitempty"`
|
|
Error *Error `xml:"error,omitempty" json:"error,omitempty"`
|
|
License *License `xml:"license,omitempty" json:"license,omitempty"`
|
|
MusicFolders *MusicFolders `xml:"musicFolders,omitempty" json:"musicFolders,omitempty"`
|
|
Indexes *Indexes `xml:"indexes,omitempty" json:"indexes,omitempty"`
|
|
Directory *Directory `xml:"directory,omitempty" json:"directory,omitempty"`
|
|
User *User `xml:"user,omitempty" json:"user,omitempty"`
|
|
Users *Users `xml:"users,omitempty" json:"users,omitempty"`
|
|
AlbumList *AlbumList `xml:"albumList,omitempty" json:"albumList,omitempty"`
|
|
AlbumList2 *AlbumList `xml:"albumList2,omitempty" json:"albumList2,omitempty"`
|
|
Playlists *Playlists `xml:"playlists,omitempty" json:"playlists,omitempty"`
|
|
Playlist *PlaylistWithSongs `xml:"playlist,omitempty" json:"playlist,omitempty"`
|
|
SearchResult2 *SearchResult2 `xml:"searchResult2,omitempty" json:"searchResult2,omitempty"`
|
|
SearchResult3 *SearchResult3 `xml:"searchResult3,omitempty" json:"searchResult3,omitempty"`
|
|
Starred *Starred `xml:"starred,omitempty" json:"starred,omitempty"`
|
|
Starred2 *Starred `xml:"starred2,omitempty" json:"starred2,omitempty"`
|
|
NowPlaying *NowPlaying `xml:"nowPlaying,omitempty" json:"nowPlaying,omitempty"`
|
|
Song *Child `xml:"song,omitempty" json:"song,omitempty"`
|
|
RandomSongs *Songs `xml:"randomSongs,omitempty" json:"randomSongs,omitempty"`
|
|
SongsByGenre *Songs `xml:"songsByGenre,omitempty" json:"songsByGenre,omitempty"`
|
|
Genres *Genres `xml:"genres,omitempty" json:"genres,omitempty"`
|
|
|
|
// ID3
|
|
Artist *Artists `xml:"artists,omitempty" json:"artists,omitempty"`
|
|
ArtistWithAlbumsID3 *ArtistWithAlbumsID3 `xml:"artist,omitempty" json:"artist,omitempty"`
|
|
AlbumWithSongsID3 *AlbumWithSongsID3 `xml:"album,omitempty" json:"album,omitempty"`
|
|
|
|
AlbumInfo *AlbumInfo `xml:"albumInfo,omitempty" json:"albumInfo,omitempty"`
|
|
ArtistInfo *ArtistInfo `xml:"artistInfo,omitempty" json:"artistInfo,omitempty"`
|
|
ArtistInfo2 *ArtistInfo2 `xml:"artistInfo2,omitempty" json:"artistInfo2,omitempty"`
|
|
SimilarSongs *SimilarSongs `xml:"similarSongs,omitempty" json:"similarSongs,omitempty"`
|
|
SimilarSongs2 *SimilarSongs2 `xml:"similarSongs2,omitempty" json:"similarSongs2,omitempty"`
|
|
TopSongs *TopSongs `xml:"topSongs,omitempty" json:"topSongs,omitempty"`
|
|
|
|
PlayQueue *PlayQueue `xml:"playQueue,omitempty" json:"playQueue,omitempty"`
|
|
Shares *Shares `xml:"shares,omitempty" json:"shares,omitempty"`
|
|
Bookmarks *Bookmarks `xml:"bookmarks,omitempty" json:"bookmarks,omitempty"`
|
|
ScanStatus *ScanStatus `xml:"scanStatus,omitempty" json:"scanStatus,omitempty"`
|
|
Lyrics *Lyrics `xml:"lyrics,omitempty" json:"lyrics,omitempty"`
|
|
|
|
InternetRadioStations *InternetRadioStations `xml:"internetRadioStations,omitempty" json:"internetRadioStations,omitempty"`
|
|
|
|
JukeboxStatus *JukeboxStatus `xml:"jukeboxStatus,omitempty" json:"jukeboxStatus,omitempty"`
|
|
JukeboxPlaylist *JukeboxPlaylist `xml:"jukeboxPlaylist,omitempty" json:"jukeboxPlaylist,omitempty"`
|
|
|
|
// OpenSubsonic extensions
|
|
OpenSubsonicExtensions *OpenSubsonicExtensions `xml:"openSubsonicExtensions,omitempty" json:"openSubsonicExtensions,omitempty"`
|
|
LyricsList *LyricsList `xml:"lyricsList,omitempty" json:"lyricsList,omitempty"`
|
|
}
|
|
|
|
const (
|
|
StatusOK = "ok"
|
|
StatusFailed = "failed"
|
|
)
|
|
|
|
type JsonWrapper struct {
|
|
Subsonic Subsonic `json:"subsonic-response"`
|
|
}
|
|
|
|
type Error struct {
|
|
Code int32 `xml:"code,attr" json:"code"`
|
|
Message string `xml:"message,attr" json:"message"`
|
|
}
|
|
|
|
type License struct {
|
|
Valid bool `xml:"valid,attr" json:"valid"`
|
|
}
|
|
|
|
type MusicFolder struct {
|
|
Id int32 `xml:"id,attr" json:"id"`
|
|
Name string `xml:"name,attr" json:"name"`
|
|
}
|
|
|
|
type MusicFolders struct {
|
|
Folders []MusicFolder `xml:"musicFolder" json:"musicFolder,omitempty"`
|
|
}
|
|
|
|
type Artist struct {
|
|
Id string `xml:"id,attr" json:"id"`
|
|
Name string `xml:"name,attr" json:"name"`
|
|
AlbumCount int32 `xml:"albumCount,attr,omitempty" json:"albumCount,omitempty"`
|
|
Starred *time.Time `xml:"starred,attr,omitempty" json:"starred,omitempty"`
|
|
UserRating int32 `xml:"userRating,attr,omitempty" json:"userRating,omitempty"`
|
|
CoverArt string `xml:"coverArt,attr,omitempty" json:"coverArt,omitempty"`
|
|
ArtistImageUrl string `xml:"artistImageUrl,attr,omitempty" json:"artistImageUrl,omitempty"`
|
|
/* TODO:
|
|
<xs:attribute name="averageRating" type="sub:AverageRating" use="optional"/> <!-- Added in 1.13.0 -->
|
|
*/
|
|
}
|
|
|
|
type Index struct {
|
|
Name string `xml:"name,attr" json:"name"`
|
|
Artists []Artist `xml:"artist" json:"artist"`
|
|
}
|
|
|
|
type Indexes struct {
|
|
Index []Index `xml:"index" json:"index,omitempty"`
|
|
LastModified int64 `xml:"lastModified,attr" json:"lastModified"`
|
|
IgnoredArticles string `xml:"ignoredArticles,attr" json:"ignoredArticles"`
|
|
}
|
|
|
|
type IndexID3 struct {
|
|
Name string `xml:"name,attr" json:"name"`
|
|
Artists []ArtistID3 `xml:"artist" json:"artist"`
|
|
}
|
|
|
|
type Artists struct {
|
|
Index []IndexID3 `xml:"index" json:"index,omitempty"`
|
|
LastModified int64 `xml:"lastModified,attr" json:"lastModified"`
|
|
IgnoredArticles string `xml:"ignoredArticles,attr" json:"ignoredArticles"`
|
|
}
|
|
|
|
type MediaType string
|
|
|
|
const (
|
|
MediaTypeSong MediaType = "song"
|
|
MediaTypeAlbum MediaType = "album"
|
|
MediaTypeArtist MediaType = "artist"
|
|
)
|
|
|
|
type Child struct {
|
|
Id string `xml:"id,attr" json:"id"`
|
|
Parent string `xml:"parent,attr,omitempty" json:"parent,omitempty"`
|
|
IsDir bool `xml:"isDir,attr" json:"isDir"`
|
|
Title string `xml:"title,attr,omitempty" json:"title,omitempty"`
|
|
Name string `xml:"name,attr,omitempty" json:"name,omitempty"`
|
|
Album string `xml:"album,attr,omitempty" json:"album,omitempty"`
|
|
Artist string `xml:"artist,attr,omitempty" json:"artist,omitempty"`
|
|
Track int32 `xml:"track,attr,omitempty" json:"track,omitempty"`
|
|
Year int32 `xml:"year,attr,omitempty" json:"year,omitempty"`
|
|
Genre string `xml:"genre,attr,omitempty" json:"genre,omitempty"`
|
|
CoverArt string `xml:"coverArt,attr,omitempty" json:"coverArt,omitempty"`
|
|
Size int64 `xml:"size,attr,omitempty" json:"size,omitempty"`
|
|
ContentType string `xml:"contentType,attr,omitempty" json:"contentType,omitempty"`
|
|
Suffix string `xml:"suffix,attr,omitempty" json:"suffix,omitempty"`
|
|
Starred *time.Time `xml:"starred,attr,omitempty" json:"starred,omitempty"`
|
|
TranscodedContentType string `xml:"transcodedContentType,attr,omitempty" json:"transcodedContentType,omitempty"`
|
|
TranscodedSuffix string `xml:"transcodedSuffix,attr,omitempty" json:"transcodedSuffix,omitempty"`
|
|
Duration int32 `xml:"duration,attr,omitempty" json:"duration,omitempty"`
|
|
BitRate int32 `xml:"bitRate,attr,omitempty" json:"bitRate,omitempty"`
|
|
Path string `xml:"path,attr,omitempty" json:"path,omitempty"`
|
|
PlayCount int64 `xml:"playCount,attr,omitempty" json:"playCount,omitempty"`
|
|
DiscNumber int32 `xml:"discNumber,attr,omitempty" json:"discNumber,omitempty"`
|
|
Created *time.Time `xml:"created,attr,omitempty" json:"created,omitempty"`
|
|
AlbumId string `xml:"albumId,attr,omitempty" json:"albumId,omitempty"`
|
|
ArtistId string `xml:"artistId,attr,omitempty" json:"artistId,omitempty"`
|
|
Type string `xml:"type,attr,omitempty" json:"type,omitempty"`
|
|
UserRating int32 `xml:"userRating,attr,omitempty" json:"userRating,omitempty"`
|
|
SongCount int32 `xml:"songCount,attr,omitempty" json:"songCount,omitempty"`
|
|
IsVideo bool `xml:"isVideo,attr" json:"isVideo"`
|
|
BookmarkPosition int64 `xml:"bookmarkPosition,attr,omitempty" json:"bookmarkPosition,omitempty"`
|
|
/*
|
|
<xs:attribute name="averageRating" type="sub:AverageRating" use="optional"/> <!-- Added in 1.6.0 -->
|
|
*/
|
|
*OpenSubsonicChild `xml:",omitempty" json:",omitempty"`
|
|
}
|
|
|
|
type OpenSubsonicChild struct {
|
|
// OpenSubsonic extensions
|
|
Played *time.Time `xml:"played,attr,omitempty" json:"played,omitempty"`
|
|
BPM int32 `xml:"bpm,attr,omitempty" json:"bpm"`
|
|
Comment string `xml:"comment,attr,omitempty" json:"comment"`
|
|
SortName string `xml:"sortName,attr,omitempty" json:"sortName"`
|
|
MediaType MediaType `xml:"mediaType,attr,omitempty" json:"mediaType"`
|
|
MusicBrainzId string `xml:"musicBrainzId,attr,omitempty" json:"musicBrainzId"`
|
|
Genres Array[ItemGenre] `xml:"genres,omitempty" json:"genres"`
|
|
ReplayGain ReplayGain `xml:"replayGain,omitempty" json:"replayGain"`
|
|
ChannelCount int32 `xml:"channelCount,attr,omitempty" json:"channelCount"`
|
|
SamplingRate int32 `xml:"samplingRate,attr,omitempty" json:"samplingRate"`
|
|
BitDepth int32 `xml:"bitDepth,attr,omitempty" json:"bitDepth"`
|
|
Moods Array[string] `xml:"moods,omitempty" json:"moods"`
|
|
Artists Array[ArtistID3Ref] `xml:"artists,omitempty" json:"artists"`
|
|
DisplayArtist string `xml:"displayArtist,attr,omitempty" json:"displayArtist"`
|
|
AlbumArtists Array[ArtistID3Ref] `xml:"albumArtists,omitempty" json:"albumArtists"`
|
|
DisplayAlbumArtist string `xml:"displayAlbumArtist,attr,omitempty" json:"displayAlbumArtist"`
|
|
Contributors Array[Contributor] `xml:"contributors,omitempty" json:"contributors"`
|
|
DisplayComposer string `xml:"displayComposer,attr,omitempty" json:"displayComposer"`
|
|
ExplicitStatus string `xml:"explicitStatus,attr,omitempty" json:"explicitStatus"`
|
|
}
|
|
|
|
type Songs struct {
|
|
Songs []Child `xml:"song" json:"song,omitempty"`
|
|
}
|
|
|
|
type Directory struct {
|
|
Child []Child `xml:"child" json:"child,omitempty"`
|
|
Id string `xml:"id,attr" json:"id"`
|
|
Name string `xml:"name,attr" json:"name"`
|
|
Parent string `xml:"parent,attr,omitempty" json:"parent,omitempty"`
|
|
Starred *time.Time `xml:"starred,attr,omitempty" json:"starred,omitempty"`
|
|
PlayCount int64 `xml:"playCount,attr,omitempty" json:"playCount,omitempty"`
|
|
Played *time.Time `xml:"played,attr,omitempty" json:"played,omitempty"`
|
|
UserRating int32 `xml:"userRating,attr,omitempty" json:"userRating,omitempty"`
|
|
|
|
// ID3
|
|
Artist string `xml:"artist,attr,omitempty" json:"artist,omitempty"`
|
|
ArtistId string `xml:"artistId,attr,omitempty" json:"artistId,omitempty"`
|
|
CoverArt string `xml:"coverArt,attr,omitempty" json:"coverArt,omitempty"`
|
|
SongCount int32 `xml:"songCount,attr,omitempty" json:"songCount,omitempty"`
|
|
AlbumCount int32 `xml:"albumCount,attr,omitempty" json:"albumCount,omitempty"`
|
|
Duration int32 `xml:"duration,attr,omitempty" json:"duration,omitempty"`
|
|
Created *time.Time `xml:"created,attr,omitempty" json:"created,omitempty"`
|
|
Year int32 `xml:"year,attr,omitempty" json:"year,omitempty"`
|
|
Genre string `xml:"genre,attr,omitempty" json:"genre,omitempty"`
|
|
|
|
/*
|
|
<xs:attribute name="averageRating" type="sub:AverageRating" use="optional"/> <!-- Added in 1.13.0 -->
|
|
*/
|
|
}
|
|
|
|
// ArtistID3Ref is a reference to an artist, a simplified version of ArtistID3. This is used to resolve the
|
|
// documentation conflict in OpenSubsonic: https://github.com/opensubsonic/open-subsonic-api/discussions/120
|
|
type ArtistID3Ref struct {
|
|
Id string `xml:"id,attr" json:"id"`
|
|
Name string `xml:"name,attr" json:"name"`
|
|
}
|
|
|
|
type ArtistID3 struct {
|
|
Id string `xml:"id,attr" json:"id"`
|
|
Name string `xml:"name,attr" json:"name"`
|
|
CoverArt string `xml:"coverArt,attr,omitempty" json:"coverArt,omitempty"`
|
|
AlbumCount int32 `xml:"albumCount,attr,omitempty" json:"albumCount,omitempty"`
|
|
Starred *time.Time `xml:"starred,attr,omitempty" json:"starred,omitempty"`
|
|
UserRating int32 `xml:"userRating,attr,omitempty" json:"userRating,omitempty"`
|
|
ArtistImageUrl string `xml:"artistImageUrl,attr,omitempty" json:"artistImageUrl,omitempty"`
|
|
*OpenSubsonicArtistID3 `xml:",omitempty" json:",omitempty"`
|
|
}
|
|
|
|
type OpenSubsonicArtistID3 struct {
|
|
// OpenSubsonic extensions
|
|
MusicBrainzId string `xml:"musicBrainzId,attr,omitempty" json:"musicBrainzId"`
|
|
SortName string `xml:"sortName,attr,omitempty" json:"sortName"`
|
|
Roles Array[string] `xml:"roles,omitempty" json:"roles"`
|
|
}
|
|
|
|
type AlbumID3 struct {
|
|
Id string `xml:"id,attr" json:"id"`
|
|
Name string `xml:"name,attr" json:"name"`
|
|
Artist string `xml:"artist,attr,omitempty" json:"artist,omitempty"`
|
|
ArtistId string `xml:"artistId,attr,omitempty" json:"artistId,omitempty"`
|
|
CoverArt string `xml:"coverArt,attr,omitempty" json:"coverArt,omitempty"`
|
|
SongCount int32 `xml:"songCount,attr,omitempty" json:"songCount,omitempty"`
|
|
Duration int32 `xml:"duration,attr,omitempty" json:"duration,omitempty"`
|
|
PlayCount int64 `xml:"playCount,attr,omitempty" json:"playCount,omitempty"`
|
|
Created *time.Time `xml:"created,attr,omitempty" json:"created,omitempty"`
|
|
Starred *time.Time `xml:"starred,attr,omitempty" json:"starred,omitempty"`
|
|
Year int32 `xml:"year,attr,omitempty" json:"year,omitempty"`
|
|
Genre string `xml:"genre,attr,omitempty" json:"genre,omitempty"`
|
|
*OpenSubsonicAlbumID3 `xml:",omitempty" json:",omitempty"`
|
|
}
|
|
|
|
type OpenSubsonicAlbumID3 struct {
|
|
// OpenSubsonic extensions
|
|
Played *time.Time `xml:"played,attr,omitempty" json:"played,omitempty"`
|
|
UserRating int32 `xml:"userRating,attr,omitempty" json:"userRating"`
|
|
Genres Array[ItemGenre] `xml:"genres,omitempty" json:"genres"`
|
|
MusicBrainzId string `xml:"musicBrainzId,attr,omitempty" json:"musicBrainzId"`
|
|
IsCompilation bool `xml:"isCompilation,attr,omitempty" json:"isCompilation"`
|
|
SortName string `xml:"sortName,attr,omitempty" json:"sortName"`
|
|
DiscTitles Array[DiscTitle] `xml:"discTitles,omitempty" json:"discTitles"`
|
|
OriginalReleaseDate ItemDate `xml:"originalReleaseDate,omitempty" json:"originalReleaseDate"`
|
|
ReleaseDate ItemDate `xml:"releaseDate,omitempty" json:"releaseDate"`
|
|
ReleaseTypes Array[string] `xml:"releaseTypes,omitempty" json:"releaseTypes"`
|
|
RecordLabels Array[RecordLabel] `xml:"recordLabels,omitempty" json:"recordLabels"`
|
|
Moods Array[string] `xml:"moods,omitempty" json:"moods"`
|
|
Artists Array[ArtistID3Ref] `xml:"artists,omitempty" json:"artists"`
|
|
DisplayArtist string `xml:"displayArtist,attr,omitempty" json:"displayArtist"`
|
|
ExplicitStatus string `xml:"explicitStatus,attr,omitempty" json:"explicitStatus"`
|
|
Version string `xml:"version,attr,omitempty" json:"version"`
|
|
}
|
|
|
|
type ArtistWithAlbumsID3 struct {
|
|
ArtistID3
|
|
Album []Child `xml:"album" json:"album,omitempty"`
|
|
}
|
|
|
|
type AlbumWithSongsID3 struct {
|
|
AlbumID3
|
|
Song []Child `xml:"song" json:"song,omitempty"`
|
|
}
|
|
|
|
type AlbumList struct {
|
|
Album []Child `xml:"album" json:"album,omitempty"`
|
|
}
|
|
|
|
type Playlist struct {
|
|
Id string `xml:"id,attr" json:"id"`
|
|
Name string `xml:"name,attr" json:"name"`
|
|
Comment string `xml:"comment,attr,omitempty" json:"comment,omitempty"`
|
|
SongCount int32 `xml:"songCount,attr" json:"songCount"`
|
|
Duration int32 `xml:"duration,attr" json:"duration"`
|
|
Public bool `xml:"public,attr" json:"public"`
|
|
Owner string `xml:"owner,attr,omitempty" json:"owner,omitempty"`
|
|
Created time.Time `xml:"created,attr" json:"created"`
|
|
Changed time.Time `xml:"changed,attr" json:"changed"`
|
|
CoverArt string `xml:"coverArt,attr,omitempty" json:"coverArt,omitempty"`
|
|
/*
|
|
<xs:sequence>
|
|
<xs:element name="allowedUser" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> <!--Added in 1.8.0-->
|
|
</xs:sequence>
|
|
*/
|
|
}
|
|
|
|
type Playlists struct {
|
|
Playlist []Playlist `xml:"playlist" json:"playlist,omitempty"`
|
|
}
|
|
|
|
type PlaylistWithSongs struct {
|
|
Playlist
|
|
Entry []Child `xml:"entry" json:"entry,omitempty"`
|
|
}
|
|
|
|
type SearchResult2 struct {
|
|
Artist []Artist `xml:"artist" json:"artist,omitempty"`
|
|
Album []Child `xml:"album" json:"album,omitempty"`
|
|
Song []Child `xml:"song" json:"song,omitempty"`
|
|
}
|
|
|
|
type SearchResult3 struct {
|
|
Artist []ArtistID3 `xml:"artist" json:"artist,omitempty"`
|
|
Album []AlbumID3 `xml:"album" json:"album,omitempty"`
|
|
Song []Child `xml:"song" json:"song,omitempty"`
|
|
}
|
|
|
|
type Starred struct {
|
|
Artist []Artist `xml:"artist" json:"artist,omitempty"`
|
|
Album []Child `xml:"album" json:"album,omitempty"`
|
|
Song []Child `xml:"song" json:"song,omitempty"`
|
|
}
|
|
|
|
type NowPlayingEntry struct {
|
|
Child
|
|
UserName string `xml:"username,attr" json:"username"`
|
|
MinutesAgo int32 `xml:"minutesAgo,attr" json:"minutesAgo"`
|
|
PlayerId int32 `xml:"playerId,attr" json:"playerId"`
|
|
PlayerName string `xml:"playerName,attr" json:"playerName,omitempty"`
|
|
}
|
|
|
|
type NowPlaying struct {
|
|
Entry []NowPlayingEntry `xml:"entry" json:"entry,omitempty"`
|
|
}
|
|
|
|
type User struct {
|
|
Username string `xml:"username,attr" json:"username"`
|
|
Email string `xml:"email,attr,omitempty" json:"email,omitempty"`
|
|
ScrobblingEnabled bool `xml:"scrobblingEnabled,attr" json:"scrobblingEnabled"`
|
|
MaxBitRate int32 `xml:"maxBitRate,attr,omitempty" json:"maxBitRate,omitempty"`
|
|
AdminRole bool `xml:"adminRole,attr" json:"adminRole"`
|
|
SettingsRole bool `xml:"settingsRole,attr" json:"settingsRole"`
|
|
DownloadRole bool `xml:"downloadRole,attr" json:"downloadRole"`
|
|
UploadRole bool `xml:"uploadRole,attr" json:"uploadRole"`
|
|
PlaylistRole bool `xml:"playlistRole,attr" json:"playlistRole"`
|
|
CoverArtRole bool `xml:"coverArtRole,attr" json:"coverArtRole"`
|
|
CommentRole bool `xml:"commentRole,attr" json:"commentRole"`
|
|
PodcastRole bool `xml:"podcastRole,attr" json:"podcastRole"`
|
|
StreamRole bool `xml:"streamRole,attr" json:"streamRole"`
|
|
JukeboxRole bool `xml:"jukeboxRole,attr" json:"jukeboxRole"`
|
|
ShareRole bool `xml:"shareRole,attr" json:"shareRole"`
|
|
VideoConversionRole bool `xml:"videoConversionRole,attr" json:"videoConversionRole"`
|
|
Folder []int32 `xml:"folder,omitempty" json:"folder,omitempty"`
|
|
}
|
|
|
|
type Users struct {
|
|
User []User `xml:"user" json:"user"`
|
|
}
|
|
|
|
type Genre struct {
|
|
Name string `xml:",chardata" json:"value,omitempty"`
|
|
SongCount int32 `xml:"songCount,attr" json:"songCount"`
|
|
AlbumCount int32 `xml:"albumCount,attr" json:"albumCount"`
|
|
}
|
|
|
|
type Genres struct {
|
|
Genre []Genre `xml:"genre,omitempty" json:"genre,omitempty"`
|
|
}
|
|
|
|
type AlbumInfo struct {
|
|
Notes string `xml:"notes,omitempty" json:"notes,omitempty"`
|
|
MusicBrainzID string `xml:"musicBrainzId,omitempty" json:"musicBrainzId,omitempty"`
|
|
LastFmUrl string `xml:"lastFmUrl,omitempty" json:"lastFmUrl,omitempty"`
|
|
SmallImageUrl string `xml:"smallImageUrl,omitempty" json:"smallImageUrl,omitempty"`
|
|
MediumImageUrl string `xml:"mediumImageUrl,omitempty" json:"mediumImageUrl,omitempty"`
|
|
LargeImageUrl string `xml:"largeImageUrl,omitempty" json:"largeImageUrl,omitempty"`
|
|
}
|
|
|
|
type ArtistInfoBase struct {
|
|
Biography string `xml:"biography,omitempty" json:"biography,omitempty"`
|
|
MusicBrainzID string `xml:"musicBrainzId,omitempty" json:"musicBrainzId,omitempty"`
|
|
LastFmUrl string `xml:"lastFmUrl,omitempty" json:"lastFmUrl,omitempty"`
|
|
SmallImageUrl string `xml:"smallImageUrl,omitempty" json:"smallImageUrl,omitempty"`
|
|
MediumImageUrl string `xml:"mediumImageUrl,omitempty" json:"mediumImageUrl,omitempty"`
|
|
LargeImageUrl string `xml:"largeImageUrl,omitempty" json:"largeImageUrl,omitempty"`
|
|
}
|
|
|
|
type ArtistInfo struct {
|
|
ArtistInfoBase
|
|
SimilarArtist []Artist `xml:"similarArtist,omitempty" json:"similarArtist,omitempty"`
|
|
}
|
|
|
|
type ArtistInfo2 struct {
|
|
ArtistInfoBase
|
|
SimilarArtist []ArtistID3 `xml:"similarArtist,omitempty" json:"similarArtist,omitempty"`
|
|
}
|
|
|
|
type SimilarSongs struct {
|
|
Song []Child `xml:"song,omitempty" json:"song,omitempty"`
|
|
}
|
|
|
|
type SimilarSongs2 struct {
|
|
Song []Child `xml:"song,omitempty" json:"song,omitempty"`
|
|
}
|
|
|
|
type TopSongs struct {
|
|
Song []Child `xml:"song,omitempty" json:"song,omitempty"`
|
|
}
|
|
|
|
type PlayQueue struct {
|
|
Entry []Child `xml:"entry,omitempty" json:"entry,omitempty"`
|
|
Current string `xml:"current,attr,omitempty" json:"current,omitempty"`
|
|
Position int64 `xml:"position,attr,omitempty" json:"position,omitempty"`
|
|
Username string `xml:"username,attr" json:"username"`
|
|
Changed *time.Time `xml:"changed,attr,omitempty" json:"changed,omitempty"`
|
|
ChangedBy string `xml:"changedBy,attr" json:"changedBy"`
|
|
}
|
|
|
|
type Bookmark struct {
|
|
Entry Child `xml:"entry,omitempty" json:"entry,omitempty"`
|
|
Position int64 `xml:"position,attr,omitempty" json:"position,omitempty"`
|
|
Username string `xml:"username,attr" json:"username"`
|
|
Comment string `xml:"comment,attr" json:"comment"`
|
|
Created time.Time `xml:"created,attr" json:"created"`
|
|
Changed time.Time `xml:"changed,attr" json:"changed"`
|
|
}
|
|
|
|
type Bookmarks struct {
|
|
Bookmark []Bookmark `xml:"bookmark,omitempty" json:"bookmark,omitempty"`
|
|
}
|
|
|
|
type Share struct {
|
|
Entry []Child `xml:"entry,omitempty" json:"entry,omitempty"`
|
|
ID string `xml:"id,attr" json:"id"`
|
|
Url string `xml:"url,attr" json:"url"`
|
|
Description string `xml:"description,omitempty,attr" json:"description,omitempty"`
|
|
Username string `xml:"username,attr" json:"username"`
|
|
Created time.Time `xml:"created,attr" json:"created"`
|
|
Expires *time.Time `xml:"expires,omitempty,attr" json:"expires,omitempty"`
|
|
LastVisited *time.Time `xml:"lastVisited,omitempty,attr" json:"lastVisited,omitempty"`
|
|
VisitCount int32 `xml:"visitCount,attr" json:"visitCount"`
|
|
}
|
|
|
|
type Shares struct {
|
|
Share []Share `xml:"share,omitempty" json:"share,omitempty"`
|
|
}
|
|
|
|
type ScanStatus struct {
|
|
Scanning bool `xml:"scanning,attr" json:"scanning"`
|
|
Count int64 `xml:"count,attr" json:"count"`
|
|
FolderCount int64 `xml:"folderCount,attr" json:"folderCount"`
|
|
LastScan *time.Time `xml:"lastScan,attr,omitempty" json:"lastScan,omitempty"`
|
|
}
|
|
|
|
type Lyrics struct {
|
|
Artist string `xml:"artist,omitempty,attr" json:"artist,omitempty"`
|
|
Title string `xml:"title,omitempty,attr" json:"title,omitempty"`
|
|
Value string `xml:",chardata" json:"value"`
|
|
}
|
|
|
|
type InternetRadioStations struct {
|
|
Radios []Radio `xml:"internetRadioStation" json:"internetRadioStation,omitempty"`
|
|
}
|
|
|
|
type Radio struct {
|
|
ID string `xml:"id,attr" json:"id"`
|
|
Name string `xml:"name,attr" json:"name"`
|
|
StreamUrl string `xml:"streamUrl,attr" json:"streamUrl"`
|
|
HomepageUrl string `xml:"homePageUrl,omitempty,attr" json:"homePageUrl,omitempty"`
|
|
}
|
|
|
|
type JukeboxStatus struct {
|
|
CurrentIndex int32 `xml:"currentIndex,attr" json:"currentIndex"`
|
|
Playing bool `xml:"playing,attr" json:"playing"`
|
|
Gain float32 `xml:"gain,attr" json:"gain"`
|
|
Position int32 `xml:"position,omitempty,attr" json:"position"`
|
|
}
|
|
|
|
type JukeboxPlaylist struct {
|
|
JukeboxStatus
|
|
Entry []Child `xml:"entry,omitempty" json:"entry,omitempty"`
|
|
}
|
|
|
|
type Line struct {
|
|
Start *int64 `xml:"start,attr,omitempty" json:"start,omitempty"`
|
|
Value string `xml:",chardata" json:"value"`
|
|
}
|
|
|
|
type StructuredLyric struct {
|
|
DisplayArtist string `xml:"displayArtist,attr,omitempty" json:"displayArtist,omitempty"`
|
|
DisplayTitle string `xml:"displayTitle,attr,omitempty" json:"displayTitle,omitempty"`
|
|
Lang string `xml:"lang,attr" json:"lang"`
|
|
Line []Line `xml:"line" json:"line"`
|
|
Offset *int64 `xml:"offset,attr,omitempty" json:"offset,omitempty"`
|
|
Synced bool `xml:"synced,attr" json:"synced"`
|
|
}
|
|
|
|
type StructuredLyrics []StructuredLyric
|
|
type LyricsList struct {
|
|
StructuredLyrics []StructuredLyric `xml:"structuredLyrics,omitempty" json:"structuredLyrics,omitempty"`
|
|
}
|
|
|
|
type OpenSubsonicExtension struct {
|
|
Name string `xml:"name,attr" json:"name"`
|
|
Versions []int32 `xml:"versions" json:"versions"`
|
|
}
|
|
|
|
type OpenSubsonicExtensions []OpenSubsonicExtension
|
|
|
|
type ItemGenre struct {
|
|
Name string `xml:"name,attr" json:"name"`
|
|
}
|
|
|
|
type ReplayGain struct {
|
|
TrackGain float64 `xml:"trackGain,omitempty,attr" json:"trackGain,omitempty"`
|
|
AlbumGain float64 `xml:"albumGain,omitempty,attr" json:"albumGain,omitempty"`
|
|
TrackPeak float64 `xml:"trackPeak,omitempty,attr" json:"trackPeak,omitempty"`
|
|
AlbumPeak float64 `xml:"albumPeak,omitempty,attr" json:"albumPeak,omitempty"`
|
|
BaseGain float64 `xml:"baseGain,omitempty,attr" json:"baseGain,omitempty"`
|
|
FallbackGain float64 `xml:"fallbackGain,omitempty,attr" json:"fallbackGain,omitempty"`
|
|
}
|
|
|
|
func (r ReplayGain) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
|
if r.TrackGain == 0 && r.AlbumGain == 0 && r.TrackPeak == 0 && r.AlbumPeak == 0 && r.BaseGain == 0 && r.FallbackGain == 0 {
|
|
return nil
|
|
}
|
|
type replayGain ReplayGain
|
|
return e.EncodeElement(replayGain(r), start)
|
|
}
|
|
|
|
type DiscTitle struct {
|
|
Disc int32 `xml:"disc,attr" json:"disc"`
|
|
Title string `xml:"title,attr" json:"title"`
|
|
}
|
|
|
|
type ItemDate struct {
|
|
Year int32 `xml:"year,attr,omitempty" json:"year,omitempty"`
|
|
Month int32 `xml:"month,attr,omitempty" json:"month,omitempty"`
|
|
Day int32 `xml:"day,attr,omitempty" json:"day,omitempty"`
|
|
}
|
|
|
|
func (d ItemDate) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
|
if d.Year == 0 && d.Month == 0 && d.Day == 0 {
|
|
return nil
|
|
}
|
|
type itemDate ItemDate
|
|
return e.EncodeElement(itemDate(d), start)
|
|
}
|
|
|
|
type RecordLabel struct {
|
|
Name string `xml:"name,attr" json:"name"`
|
|
}
|
|
|
|
type Contributor struct {
|
|
Role string `xml:"role,attr" json:"role"`
|
|
SubRole string `xml:"subRole,attr,omitempty" json:"subRole,omitempty"`
|
|
Artist ArtistID3Ref `xml:"artist" json:"artist"`
|
|
}
|
|
|
|
// Array is a generic type for marshalling slices to JSON. It is used to avoid marshalling empty slices as null.
|
|
type Array[T any] []T
|
|
|
|
func (a Array[T]) MarshalJSON() ([]byte, error) {
|
|
return marshalJSONArray(a)
|
|
}
|
|
|
|
// marshalJSONArray marshals a slice of any type to JSON. If the slice is empty, it is marshalled as an
|
|
// empty array instead of null.
|
|
func marshalJSONArray[T any](v []T) ([]byte, error) {
|
|
if len(v) == 0 {
|
|
return json.Marshal([]T{})
|
|
}
|
|
return json.Marshal(v)
|
|
}
|