Merge pull request #514 from dweymouth/feat/opensubsonic-date
(OpenSubsonic) Show full album dates when available
This commit is contained in:
@@ -458,7 +458,7 @@ func fillAlbum(a *jellyfin.Album, album *mediaprovider.Album) {
|
|||||||
album.Duration = int(a.RunTimeTicks / runTimeTicksPerSecond)
|
album.Duration = int(a.RunTimeTicks / runTimeTicksPerSecond)
|
||||||
album.ArtistIDs = artistIDs
|
album.ArtistIDs = artistIDs
|
||||||
album.ArtistNames = artistNames
|
album.ArtistNames = artistNames
|
||||||
album.Year = a.Year
|
album.Date.Year = &a.Year
|
||||||
album.TrackCount = a.ChildCount
|
album.TrackCount = a.ChildCount
|
||||||
album.Genres = a.Genres
|
album.Genres = a.Genres
|
||||||
album.Favorite = a.UserData.IsFavorite
|
album.Favorite = a.UserData.IsFavorite
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ func (f albumFilter) Matches(album *Album) bool {
|
|||||||
if f.options.ExcludeUnfavorited && !album.Favorite {
|
if f.options.ExcludeUnfavorited && !album.Favorite {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if y := album.Year; y < f.options.MinYear || (f.options.MaxYear > 0 && y > f.options.MaxYear) {
|
if y := album.YearOrZero(); y < f.options.MinYear || (f.options.MaxYear > 0 && y > f.options.MaxYear) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if len(f.options.Genres) == 0 {
|
if len(f.options.Genres) == 0 {
|
||||||
|
|||||||
@@ -32,6 +32,12 @@ const (
|
|||||||
ReleaseTypeSpokenWord ReleaseType = 0x8000
|
ReleaseTypeSpokenWord ReleaseType = 0x8000
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ItemDate struct {
|
||||||
|
Year *int
|
||||||
|
Month *int
|
||||||
|
Day *int
|
||||||
|
}
|
||||||
|
|
||||||
type Album struct {
|
type Album struct {
|
||||||
ID string
|
ID string
|
||||||
CoverArtID string
|
CoverArtID string
|
||||||
@@ -39,14 +45,21 @@ type Album struct {
|
|||||||
Duration int
|
Duration int
|
||||||
ArtistIDs []string
|
ArtistIDs []string
|
||||||
ArtistNames []string
|
ArtistNames []string
|
||||||
Year int
|
Date ItemDate
|
||||||
ReissueYear int
|
ReissueDate ItemDate
|
||||||
Genres []string
|
Genres []string
|
||||||
TrackCount int
|
TrackCount int
|
||||||
Favorite bool
|
Favorite bool
|
||||||
ReleaseTypes ReleaseTypes
|
ReleaseTypes ReleaseTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Album) YearOrZero() int {
|
||||||
|
if a.Date.Year != nil {
|
||||||
|
return *a.Date.Year
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
type AlbumWithTracks struct {
|
type AlbumWithTracks struct {
|
||||||
Album
|
Album
|
||||||
Tracks []*Track
|
Tracks []*Track
|
||||||
|
|||||||
@@ -581,14 +581,21 @@ func fillAlbum(subAlbum *subsonic.AlbumID3, album *mediaprovider.Album) {
|
|||||||
genres = append(genres, subAlbum.Genre)
|
genres = append(genres, subAlbum.Genre)
|
||||||
}
|
}
|
||||||
|
|
||||||
album.Year = subAlbum.Year
|
if ord := subAlbum.OriginalReleaseDate; ord != nil && ord.Year != nil {
|
||||||
if subAlbum.OriginalReleaseDate != nil &&
|
album.Date = mediaprovider.ItemDate{
|
||||||
subAlbum.OriginalReleaseDate.Year != nil &&
|
Year: ord.Year,
|
||||||
*subAlbum.OriginalReleaseDate.Year < subAlbum.Year {
|
Month: ord.Month,
|
||||||
album.Year = *subAlbum.OriginalReleaseDate.Year
|
Day: ord.Day,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
album.Date.Year = &subAlbum.Year
|
||||||
|
}
|
||||||
|
if rd := subAlbum.ReleaseDate; rd != nil && rd.Year != nil {
|
||||||
|
album.ReissueDate = mediaprovider.ItemDate{
|
||||||
|
Year: rd.Year,
|
||||||
|
Month: rd.Month,
|
||||||
|
Day: rd.Day,
|
||||||
}
|
}
|
||||||
if subAlbum.ReleaseDate != nil && subAlbum.ReleaseDate.Year != nil {
|
|
||||||
album.ReissueYear = *subAlbum.ReleaseDate.Year
|
|
||||||
}
|
}
|
||||||
|
|
||||||
album.ID = subAlbum.ID
|
album.ID = subAlbum.ID
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ require (
|
|||||||
github.com/pelletier/go-toml/v2 v2.0.8
|
github.com/pelletier/go-toml/v2 v2.0.8
|
||||||
github.com/quarckster/go-mpris-server v1.0.3
|
github.com/quarckster/go-mpris-server v1.0.3
|
||||||
github.com/supersonic-app/go-mpv v0.1.0
|
github.com/supersonic-app/go-mpv v0.1.0
|
||||||
github.com/supersonic-app/go-subsonic v0.0.0-20241017150725-a5686988ef9c
|
github.com/supersonic-app/go-subsonic v0.0.0-20241224013245-9b2841f3711d
|
||||||
github.com/zalando/go-keyring v0.2.1
|
github.com/zalando/go-keyring v0.2.1
|
||||||
golang.org/x/net v0.25.0
|
golang.org/x/net v0.25.0
|
||||||
golang.org/x/text v0.16.0
|
golang.org/x/text v0.16.0
|
||||||
|
|||||||
@@ -311,8 +311,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
|
|||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
github.com/supersonic-app/go-mpv v0.1.0 h1:U+cCnLQxmpqx5mY6nMlC0J4uIdCCXUbAjpjS04XkFu8=
|
github.com/supersonic-app/go-mpv v0.1.0 h1:U+cCnLQxmpqx5mY6nMlC0J4uIdCCXUbAjpjS04XkFu8=
|
||||||
github.com/supersonic-app/go-mpv v0.1.0/go.mod h1:1bQz6kBQumJopXEbkiqoLxIXLy7F7yWFBvknvpAtIC0=
|
github.com/supersonic-app/go-mpv v0.1.0/go.mod h1:1bQz6kBQumJopXEbkiqoLxIXLy7F7yWFBvknvpAtIC0=
|
||||||
github.com/supersonic-app/go-subsonic v0.0.0-20241017150725-a5686988ef9c h1:UsXe5/TXViPgw21zO2Fgh0F/hSl0TXox0yjRc+vT6XQ=
|
github.com/supersonic-app/go-subsonic v0.0.0-20241224013245-9b2841f3711d h1:70+Nn7yh+cfeKqqXVTdpneFqXuvrBLyP7U6GVUsjTU4=
|
||||||
github.com/supersonic-app/go-subsonic v0.0.0-20241017150725-a5686988ef9c/go.mod h1:D+OWPXeD9owcdcoXATv5YPBGWxxVvn5k98rt5B4wMc4=
|
github.com/supersonic-app/go-subsonic v0.0.0-20241224013245-9b2841f3711d/go.mod h1:D+OWPXeD9owcdcoXATv5YPBGWxxVvn5k98rt5B4wMc4=
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
|||||||
@@ -227,5 +227,17 @@
|
|||||||
"Remove from queue": "Aus Wiedergabeliste entfernen",
|
"Remove from queue": "Aus Wiedergabeliste entfernen",
|
||||||
"Play song radio": "Liedradio abspielen",
|
"Play song radio": "Liedradio abspielen",
|
||||||
"to": "bis",
|
"to": "bis",
|
||||||
"by":"von"
|
"by": "von",
|
||||||
|
"Jan": "Jan",
|
||||||
|
"Feb": "Feb",
|
||||||
|
"Mar": "März",
|
||||||
|
"Apr": "Apr",
|
||||||
|
"May": "Mai",
|
||||||
|
"Jun": "Jun",
|
||||||
|
"Jul": "Jul",
|
||||||
|
"Aug": "Aug",
|
||||||
|
"Sept": "Sept",
|
||||||
|
"Oct": "Okt",
|
||||||
|
"Nov": "Nov",
|
||||||
|
"Dec": "Dec"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -229,5 +229,17 @@
|
|||||||
"Remove from queue": "Remove from queue",
|
"Remove from queue": "Remove from queue",
|
||||||
"Play song radio": "Play song radio",
|
"Play song radio": "Play song radio",
|
||||||
"to": "to",
|
"to": "to",
|
||||||
"by":"by"
|
"by": "by",
|
||||||
|
"Jan": "Jan",
|
||||||
|
"Feb": "Feb",
|
||||||
|
"Mar": "Mar",
|
||||||
|
"Apr": "Apr",
|
||||||
|
"May": "May",
|
||||||
|
"Jun": "Jun",
|
||||||
|
"Jul": "Jul",
|
||||||
|
"Aug": "Aug",
|
||||||
|
"Sept": "Sept",
|
||||||
|
"Oct": "Oct",
|
||||||
|
"Nov": "Nov",
|
||||||
|
"Dec": "Dec"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -229,5 +229,17 @@
|
|||||||
"Remove from queue": "Remover de la cola",
|
"Remove from queue": "Remover de la cola",
|
||||||
"Play song radio": "Reproducir canciones similares",
|
"Play song radio": "Reproducir canciones similares",
|
||||||
"to": "a",
|
"to": "a",
|
||||||
"by": "por"
|
"by": "por",
|
||||||
|
"Jan": "Ene",
|
||||||
|
"Feb": "Feb",
|
||||||
|
"Mar": "Mar",
|
||||||
|
"Apr": "Abr",
|
||||||
|
"May": "May",
|
||||||
|
"Jun": "Jun",
|
||||||
|
"Jul": "Jul",
|
||||||
|
"Aug": "Ago",
|
||||||
|
"Sept": "Set",
|
||||||
|
"Oct": "Oct",
|
||||||
|
"Nov": "Nov",
|
||||||
|
"Dec": "Dic"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -228,5 +228,17 @@
|
|||||||
"Remove from queue": "Retirer de la file d'attente",
|
"Remove from queue": "Retirer de la file d'attente",
|
||||||
"Play song radio": "Lire une radio basée sur la piste",
|
"Play song radio": "Lire une radio basée sur la piste",
|
||||||
"to": "à",
|
"to": "à",
|
||||||
"by": "par"
|
"by": "par",
|
||||||
|
"Jan": "Janv",
|
||||||
|
"Feb": "Févr",
|
||||||
|
"Mar": "Mars",
|
||||||
|
"Apr": "Avr",
|
||||||
|
"May": "Mai",
|
||||||
|
"Jun": "Juin",
|
||||||
|
"Jul": "Juil",
|
||||||
|
"Aug": "Août",
|
||||||
|
"Sept": "Sept",
|
||||||
|
"Oct": "Oct",
|
||||||
|
"Nov": "Nov",
|
||||||
|
"Dec": "Déc"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -226,5 +226,17 @@
|
|||||||
"Remove from queue": "Rimuovi dalla coda",
|
"Remove from queue": "Rimuovi dalla coda",
|
||||||
"Play song radio": "Avvia radio mix",
|
"Play song radio": "Avvia radio mix",
|
||||||
"to": "al",
|
"to": "al",
|
||||||
"by": "by"
|
"by": "by",
|
||||||
|
"Jan": "Gen",
|
||||||
|
"Feb": "Feb",
|
||||||
|
"Mar": "Mar",
|
||||||
|
"Apr": "Apr",
|
||||||
|
"May": "Mag",
|
||||||
|
"Jun": "Giu",
|
||||||
|
"Jul": "Lug",
|
||||||
|
"Aug": "Ago",
|
||||||
|
"Sept": "Set",
|
||||||
|
"Oct": "Ott",
|
||||||
|
"Nov": "Nov",
|
||||||
|
"Dec": "Dic"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,5 +218,17 @@
|
|||||||
"Remove from queue": "Șterge din lista de redare",
|
"Remove from queue": "Șterge din lista de redare",
|
||||||
"Play song radio": "Redă melodie radio",
|
"Play song radio": "Redă melodie radio",
|
||||||
"to": "la",
|
"to": "la",
|
||||||
"by": "de"
|
"by": "de",
|
||||||
|
"Jan": "Ian",
|
||||||
|
"Feb": "Feb",
|
||||||
|
"Mar": "Mar",
|
||||||
|
"Apr": "Apr",
|
||||||
|
"May": "Mai",
|
||||||
|
"Jun": "Iunie",
|
||||||
|
"Jul": "Iulie",
|
||||||
|
"Aug": "Aug",
|
||||||
|
"Sept": "Sept",
|
||||||
|
"Oct": "Oct",
|
||||||
|
"Nov": "Noiem",
|
||||||
|
"Dec": "Dec"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package browsing
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/dweymouth/supersonic/backend"
|
"github.com/dweymouth/supersonic/backend"
|
||||||
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
||||||
@@ -370,9 +369,9 @@ func formatMiscLabelStr(a *mediaprovider.AlbumWithTracks) string {
|
|||||||
if a.TrackCount == 1 {
|
if a.TrackCount == 1 {
|
||||||
tracks = lang.L("track")
|
tracks = lang.L("track")
|
||||||
}
|
}
|
||||||
yearStr := strconv.Itoa(a.Year)
|
yearStr := util.FormatItemDate(a.Date)
|
||||||
if a.ReissueYear > a.Year {
|
if y := a.ReissueDate.Year; y != nil && *y > a.YearOrZero() {
|
||||||
yearStr += fmt.Sprintf(" (%s %d)", lang.L("reissued"), a.ReissueYear)
|
yearStr += fmt.Sprintf(" (%s %d)", lang.L("reissued"), util.FormatItemDate(a.ReissueDate))
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s · %d %s · %s%s", yearStr, a.TrackCount, tracks, discs, util.SecondsToTimeString(float64(a.Duration)))
|
return fmt.Sprintf("%s · %d %s · %s%s", yearStr, a.TrackCount, tracks, discs, util.SecondsToTimeString(float64(a.Duration)))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -203,12 +203,12 @@ func (a *ArtistPage) getGridViewAlbumsModel() []widgets.GridViewItemModel {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
sortFunc := func(x, y int) bool {
|
sortFunc := func(x, y int) bool {
|
||||||
return a.artistInfo.Albums[x].Year < a.artistInfo.Albums[y].Year
|
return a.artistInfo.Albums[x].YearOrZero() < a.artistInfo.Albums[y].YearOrZero()
|
||||||
}
|
}
|
||||||
switch a.cfg.DiscographySort {
|
switch a.cfg.DiscographySort {
|
||||||
case discographySorts[1]: /*year descending*/
|
case discographySorts[1]: /*year descending*/
|
||||||
sortFunc = func(x, y int) bool {
|
sortFunc = func(x, y int) bool {
|
||||||
return a.artistInfo.Albums[x].Year > a.artistInfo.Albums[y].Year
|
return a.artistInfo.Albums[x].YearOrZero() > a.artistInfo.Albums[y].YearOrZero()
|
||||||
}
|
}
|
||||||
case discographySorts[2]: /*name*/
|
case discographySorts[2]: /*name*/
|
||||||
sortFunc = func(x, y int) bool {
|
sortFunc = func(x, y int) bool {
|
||||||
@@ -222,7 +222,7 @@ func (a *ArtistPage) getGridViewAlbumsModel() []widgets.GridViewItemModel {
|
|||||||
Name: al.Name,
|
Name: al.Name,
|
||||||
ID: al.ID,
|
ID: al.ID,
|
||||||
CoverArtID: al.CoverArtID,
|
CoverArtID: al.CoverArtID,
|
||||||
Secondary: []string{strconv.Itoa(al.Year)},
|
Secondary: []string{strconv.Itoa(al.YearOrZero())},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,75 @@ import (
|
|||||||
"golang.org/x/net/html"
|
"golang.org/x/net/html"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type DateFormat int
|
||||||
|
|
||||||
|
const (
|
||||||
|
DateFormatDMY DateFormat = iota
|
||||||
|
DateFormatMDY
|
||||||
|
DateFormatYMD
|
||||||
|
)
|
||||||
|
|
||||||
|
func dateFormatForLocale(locale string) DateFormat {
|
||||||
|
var region string
|
||||||
|
if i := strings.Index(locale, "-"); i > 0 && len(locale) >= 5 {
|
||||||
|
region = locale[i+1 : i+3]
|
||||||
|
}
|
||||||
|
switch strings.ToUpper(region) {
|
||||||
|
case "US":
|
||||||
|
return DateFormatMDY
|
||||||
|
case "CN", "JP", "KR", "HU", "MN", "LT":
|
||||||
|
return DateFormatYMD
|
||||||
|
default:
|
||||||
|
return DateFormatDMY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func shortMonthName(month int) string {
|
||||||
|
var months = [12]string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"}
|
||||||
|
if month >= 1 && month <= 12 {
|
||||||
|
return lang.L(months[month-1])
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func FormatItemDate(date mediaprovider.ItemDate) string {
|
||||||
|
var sb strings.Builder
|
||||||
|
df := dateFormatForLocale(fyne.CurrentDevice().Locale().String())
|
||||||
|
switch df {
|
||||||
|
case DateFormatDMY:
|
||||||
|
if d := date.Day; d != nil {
|
||||||
|
sb.WriteString(fmt.Sprintf("%d ", *d))
|
||||||
|
}
|
||||||
|
if m := date.Month; m != nil {
|
||||||
|
sb.WriteString(shortMonthName(*m) + " ")
|
||||||
|
}
|
||||||
|
if y := date.Year; y != nil {
|
||||||
|
sb.WriteString(fmt.Sprintf("%d", *y))
|
||||||
|
}
|
||||||
|
case DateFormatMDY:
|
||||||
|
if m := date.Month; m != nil {
|
||||||
|
sb.WriteString(shortMonthName(*m) + " ")
|
||||||
|
}
|
||||||
|
if d := date.Day; d != nil {
|
||||||
|
sb.WriteString(fmt.Sprintf("%d, ", *d))
|
||||||
|
}
|
||||||
|
if y := date.Year; y != nil {
|
||||||
|
sb.WriteString(fmt.Sprintf("%d", *y))
|
||||||
|
}
|
||||||
|
case DateFormatYMD:
|
||||||
|
if y := date.Year; y != nil {
|
||||||
|
sb.WriteString(fmt.Sprintf("%d ", *y))
|
||||||
|
}
|
||||||
|
if m := date.Month; m != nil {
|
||||||
|
sb.WriteString(shortMonthName(*m) + " ")
|
||||||
|
}
|
||||||
|
if d := date.Day; d != nil {
|
||||||
|
sb.WriteString(fmt.Sprintf("%d", *d))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
var BoldRichTextStyle = widget.RichTextStyle{TextStyle: fyne.TextStyle{Bold: true}, Inline: true}
|
var BoldRichTextStyle = widget.RichTextStyle{TextStyle: fyne.TextStyle{Bold: true}, Inline: true}
|
||||||
|
|
||||||
func MakeOpaque(c color.Color) color.Color {
|
func MakeOpaque(c color.Color) color.Color {
|
||||||
|
|||||||
@@ -61,8 +61,8 @@ func (g gridViewAlbumIterator) NextN(n int) []GridViewItemModel {
|
|||||||
Secondary: al.ArtistNames,
|
Secondary: al.ArtistNames,
|
||||||
SecondaryIDs: al.ArtistIDs,
|
SecondaryIDs: al.ArtistIDs,
|
||||||
}
|
}
|
||||||
if al.Year > 0 {
|
if y := al.Date.Year; y != nil {
|
||||||
model.Suffix = strconv.Itoa(al.Year)
|
model.Suffix = strconv.Itoa(*al.Date.Year)
|
||||||
}
|
}
|
||||||
return model
|
return model
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user