Fix #716: add "Last played" tracklist columnn
This commit is contained in:
@@ -430,6 +430,8 @@ func toTrack(ch *jellyfin.Song) *mediaprovider.Track {
|
|||||||
coverArtID = ch.Id
|
coverArtID = ch.Id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastPlayed, _ := time.Parse(time.RFC3339Nano, ch.UserData.LastPlayedDate)
|
||||||
|
|
||||||
t := &mediaprovider.Track{
|
t := &mediaprovider.Track{
|
||||||
ID: ch.Id,
|
ID: ch.Id,
|
||||||
CoverArtID: coverArtID,
|
CoverArtID: coverArtID,
|
||||||
@@ -447,6 +449,7 @@ func toTrack(ch *jellyfin.Song) *mediaprovider.Track {
|
|||||||
Rating: ch.UserData.Rating,
|
Rating: ch.UserData.Rating,
|
||||||
Favorite: ch.UserData.IsFavorite,
|
Favorite: ch.UserData.IsFavorite,
|
||||||
PlayCount: ch.UserData.PlayCount,
|
PlayCount: ch.UserData.PlayCount,
|
||||||
|
LastPlayed: lastPlayed,
|
||||||
}
|
}
|
||||||
if len(ch.MediaSources) > 0 {
|
if len(ch.MediaSources) > 0 {
|
||||||
t.FilePath = ch.MediaSources[0].Path
|
t.FilePath = ch.MediaSources[0].Path
|
||||||
|
|||||||
@@ -131,6 +131,7 @@
|
|||||||
"My Server": "My Server",
|
"My Server": "My Server",
|
||||||
"Name": "Name",
|
"Name": "Name",
|
||||||
"Name (A-Z)": "Name (A-Z)",
|
"Name (A-Z)": "Name (A-Z)",
|
||||||
|
"never": "never",
|
||||||
"Next": "Next",
|
"Next": "Next",
|
||||||
"Nickname": "Nickname",
|
"Nickname": "Nickname",
|
||||||
"Normal": "Normal",
|
"Normal": "Normal",
|
||||||
@@ -289,5 +290,26 @@
|
|||||||
"playlist.addedtracks": {
|
"playlist.addedtracks": {
|
||||||
"one": "Added one track to playlist",
|
"one": "Added one track to playlist",
|
||||||
"other": "Added {{.trackCount}} tracks to playlist"
|
"other": "Added {{.trackCount}} tracks to playlist"
|
||||||
|
},
|
||||||
|
|
||||||
|
"x_minutes_ago": {
|
||||||
|
"one": "a minute ago",
|
||||||
|
"other": "{{.minutes}} minutes ago"
|
||||||
|
},
|
||||||
|
"x_hours_ago": {
|
||||||
|
"one": "an hour ago",
|
||||||
|
"other": "{{.hours}} hours ago"
|
||||||
|
},
|
||||||
|
"x_days_ago": {
|
||||||
|
"one": "a day ago",
|
||||||
|
"other": "{{.days}} days ago"
|
||||||
|
},
|
||||||
|
"x_months_ago": {
|
||||||
|
"one": "a month ago",
|
||||||
|
"other": "{{.months}} months ago"
|
||||||
|
},
|
||||||
|
"x_years_ago": {
|
||||||
|
"one": "a year ago",
|
||||||
|
"other": "{{.years}} years ago"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,6 +113,7 @@
|
|||||||
"My Server": "Mi servidor",
|
"My Server": "Mi servidor",
|
||||||
"Name": "Nombre",
|
"Name": "Nombre",
|
||||||
"Name (A-Z)": "Nombre (A-Z)",
|
"Name (A-Z)": "Nombre (A-Z)",
|
||||||
|
"never": "nunca",
|
||||||
"Next": "Siguiente",
|
"Next": "Siguiente",
|
||||||
"Nickname": "Apodo",
|
"Nickname": "Apodo",
|
||||||
"Now Playing": "Reproduciendo",
|
"Now Playing": "Reproduciendo",
|
||||||
@@ -252,5 +253,26 @@
|
|||||||
"playlist.addedtracks": {
|
"playlist.addedtracks": {
|
||||||
"one": "Se ha añadido una pista a la lista de reproducción",
|
"one": "Se ha añadido una pista a la lista de reproducción",
|
||||||
"other": "Se han añadido {{.trackCount}} pistas a la lista de reproducción"
|
"other": "Se han añadido {{.trackCount}} pistas a la lista de reproducción"
|
||||||
|
},
|
||||||
|
|
||||||
|
"x_minutes_ago": {
|
||||||
|
"one": "hace un minuto",
|
||||||
|
"other": "hace {{.minutes}} minutos"
|
||||||
|
},
|
||||||
|
"x_hours_ago": {
|
||||||
|
"one": "hace una hora",
|
||||||
|
"other": "hace {{.hours}} horas"
|
||||||
|
},
|
||||||
|
"x_days_ago": {
|
||||||
|
"one": "hace un día",
|
||||||
|
"other": "hace {{.days}} días"
|
||||||
|
},
|
||||||
|
"x_months_ago": {
|
||||||
|
"one": "hace un mes",
|
||||||
|
"other": "hace {{.months}} meses"
|
||||||
|
},
|
||||||
|
"x_years_ago": {
|
||||||
|
"one": "hace un año",
|
||||||
|
"other": "hace {{.years}} años"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,6 +100,39 @@ func FormatItemDate(date mediaprovider.ItemDate) string {
|
|||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func LastPlayedDisplayString(t time.Time) string {
|
||||||
|
if t.IsZero() {
|
||||||
|
return lang.L("never")
|
||||||
|
}
|
||||||
|
switch d := time.Since(t); {
|
||||||
|
case d.Hours() < 1:
|
||||||
|
mins := int(d.Minutes())
|
||||||
|
return lang.LocalizePluralKey("x_minutes_ago",
|
||||||
|
fmt.Sprintf("%d minutes ago", mins), mins,
|
||||||
|
map[string]string{"minutes": strconv.Itoa(mins)})
|
||||||
|
case d.Hours() < 24:
|
||||||
|
hrs := int(d.Hours())
|
||||||
|
return lang.LocalizePluralKey("x_hours_ago",
|
||||||
|
fmt.Sprintf("%d hours ago", hrs), hrs,
|
||||||
|
map[string]string{"hours": strconv.Itoa(hrs)})
|
||||||
|
case d.Hours() < 24*31:
|
||||||
|
days := int(d.Hours() / 24)
|
||||||
|
return lang.LocalizePluralKey("x_days_ago",
|
||||||
|
fmt.Sprintf("%d days ago", days), days,
|
||||||
|
map[string]string{"days": strconv.Itoa(days)})
|
||||||
|
case d.Hours() < 24*365:
|
||||||
|
months := int(d.Hours() / (24 * 31))
|
||||||
|
return lang.LocalizePluralKey("x_months_ago",
|
||||||
|
fmt.Sprintf("%d months ago", months), months,
|
||||||
|
map[string]string{"months": strconv.Itoa(months)})
|
||||||
|
default:
|
||||||
|
years := int(d.Hours() / (24 * 365))
|
||||||
|
return lang.LocalizePluralKey("x_years_ago",
|
||||||
|
fmt.Sprintf("%d years ago", years), years,
|
||||||
|
map[string]string{"years": strconv.Itoa(years)})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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 {
|
||||||
|
|||||||
@@ -446,6 +446,8 @@ func (t *Tracklist) doSortTracks() {
|
|||||||
t.intSort(func(tr *util.TrackListModel) int64 { return tr.Track().Size })
|
t.intSort(func(tr *util.TrackListModel) int64 { return tr.Track().Size })
|
||||||
case ColumnPlays:
|
case ColumnPlays:
|
||||||
t.intSort(func(tr *util.TrackListModel) int64 { return int64(tr.Track().PlayCount) })
|
t.intSort(func(tr *util.TrackListModel) int64 { return int64(tr.Track().PlayCount) })
|
||||||
|
case ColumnLastPlayed:
|
||||||
|
t.intSort(func(tr *util.TrackListModel) int64 { return tr.Track().LastPlayed.Unix() })
|
||||||
case ColumnComment:
|
case ColumnComment:
|
||||||
t.stringSort(func(tr *util.TrackListModel) string { return tr.Track().Comment })
|
t.stringSort(func(tr *util.TrackListModel) string { return tr.Track().Comment })
|
||||||
case ColumnBPM:
|
case ColumnBPM:
|
||||||
|
|||||||
+23
-11
@@ -61,6 +61,7 @@ const (
|
|||||||
ColumnFavorite = "Favorite"
|
ColumnFavorite = "Favorite"
|
||||||
ColumnRating = "Rating"
|
ColumnRating = "Rating"
|
||||||
ColumnPlays = "Plays"
|
ColumnPlays = "Plays"
|
||||||
|
ColumnLastPlayed = "LastPlayed"
|
||||||
ColumnComment = "Comment"
|
ColumnComment = "Comment"
|
||||||
ColumnBPM = "BPM"
|
ColumnBPM = "BPM"
|
||||||
ColumnBitrate = "Bitrate"
|
ColumnBitrate = "Bitrate"
|
||||||
@@ -87,6 +88,7 @@ var (
|
|||||||
fav := lang.L("Fav.")
|
fav := lang.L("Fav.")
|
||||||
rating := lang.L("Rating")
|
rating := lang.L("Rating")
|
||||||
plays := lang.L("Plays")
|
plays := lang.L("Plays")
|
||||||
|
lastPlayed := lang.L("Last played")
|
||||||
comment := lang.L("Comment")
|
comment := lang.L("Comment")
|
||||||
bpm := lang.L("BPM")
|
bpm := lang.L("BPM")
|
||||||
bitrate := lang.L("Bit rate")
|
bitrate := lang.L("Bit rate")
|
||||||
@@ -104,6 +106,7 @@ var (
|
|||||||
{Name: ColumnFavorite, Col: ListColumn{Text: " " + fav, Alignment: fyne.TextAlignCenter, CanToggleVisible: true}},
|
{Name: ColumnFavorite, Col: ListColumn{Text: " " + fav, Alignment: fyne.TextAlignCenter, CanToggleVisible: true}},
|
||||||
{Name: ColumnRating, Col: ListColumn{Text: rating, Alignment: fyne.TextAlignLeading, CanToggleVisible: true}},
|
{Name: ColumnRating, Col: ListColumn{Text: rating, Alignment: fyne.TextAlignLeading, CanToggleVisible: true}},
|
||||||
{Name: ColumnPlays, Col: ListColumn{Text: plays, Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}},
|
{Name: ColumnPlays, Col: ListColumn{Text: plays, Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}},
|
||||||
|
{Name: ColumnLastPlayed, Col: ListColumn{Text: lastPlayed, Alignment: fyne.TextAlignLeading, CanToggleVisible: true}},
|
||||||
{Name: ColumnComment, Col: ListColumn{Text: comment, Alignment: fyne.TextAlignLeading, CanToggleVisible: true}},
|
{Name: ColumnComment, Col: ListColumn{Text: comment, Alignment: fyne.TextAlignLeading, CanToggleVisible: true}},
|
||||||
{Name: ColumnBPM, Col: ListColumn{Text: bpm, Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}},
|
{Name: ColumnBPM, Col: ListColumn{Text: bpm, Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}},
|
||||||
{Name: ColumnBitrate, Col: ListColumn{Text: bitrate, Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}},
|
{Name: ColumnBitrate, Col: ListColumn{Text: bitrate, Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}},
|
||||||
@@ -121,6 +124,7 @@ var (
|
|||||||
{Name: ColumnFavorite, Col: ListColumn{Text: fav, Alignment: fyne.TextAlignCenter, CanToggleVisible: true}},
|
{Name: ColumnFavorite, Col: ListColumn{Text: fav, Alignment: fyne.TextAlignCenter, CanToggleVisible: true}},
|
||||||
{Name: ColumnRating, Col: ListColumn{Text: rating, Alignment: fyne.TextAlignLeading, CanToggleVisible: true}},
|
{Name: ColumnRating, Col: ListColumn{Text: rating, Alignment: fyne.TextAlignLeading, CanToggleVisible: true}},
|
||||||
{Name: ColumnPlays, Col: ListColumn{Text: plays, Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}},
|
{Name: ColumnPlays, Col: ListColumn{Text: plays, Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}},
|
||||||
|
{Name: ColumnLastPlayed, Col: ListColumn{Text: lastPlayed, Alignment: fyne.TextAlignLeading, CanToggleVisible: true}},
|
||||||
{Name: ColumnComment, Col: ListColumn{Text: comment, Alignment: fyne.TextAlignLeading, CanToggleVisible: true}},
|
{Name: ColumnComment, Col: ListColumn{Text: comment, Alignment: fyne.TextAlignLeading, CanToggleVisible: true}},
|
||||||
{Name: ColumnBPM, Col: ListColumn{Text: bpm, Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}},
|
{Name: ColumnBPM, Col: ListColumn{Text: bpm, Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}},
|
||||||
{Name: ColumnBitrate, Col: ListColumn{Text: bitrate, Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}},
|
{Name: ColumnBitrate, Col: ListColumn{Text: bitrate, Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}},
|
||||||
@@ -144,6 +148,10 @@ var (
|
|||||||
playsColWidth := fyne.Max(
|
playsColWidth := fyne.Max(
|
||||||
widget.NewLabel("9999").MinSize().Width,
|
widget.NewLabel("9999").MinSize().Width,
|
||||||
widget.NewLabel(plays).MinSize().Width+sortIconWidth)
|
widget.NewLabel(plays).MinSize().Width+sortIconWidth)
|
||||||
|
lastPlayedColWidth := fyne.Max(
|
||||||
|
widget.NewLabel(lang.LocalizePluralKey("x_minutes_ago", "59 minutes ago", 59, map[string]string{"minutes": "59"})).MinSize().Width,
|
||||||
|
widget.NewLabel(lastPlayed).MinSize().Width+sortIconWidth,
|
||||||
|
)
|
||||||
bpmColWidth := fyne.Max(
|
bpmColWidth := fyne.Max(
|
||||||
widget.NewLabel(bpm+" ").MinSize().Width,
|
widget.NewLabel(bpm+" ").MinSize().Width,
|
||||||
widget.NewLabel("9999").MinSize().Width)
|
widget.NewLabel("9999").MinSize().Width)
|
||||||
@@ -154,10 +162,10 @@ var (
|
|||||||
widget.NewLabel("99.9 MB").MinSize().Width,
|
widget.NewLabel("99.9 MB").MinSize().Width,
|
||||||
widget.NewLabel(size).MinSize().Width+sortIconWidth)
|
widget.NewLabel(size).MinSize().Width+sortIconWidth)
|
||||||
|
|
||||||
// #, Title, Artist, Album, Composer, Time, Year, Favorite, Rating, Plays, Comment, BPM, Bitrate, Size, Path
|
// #, Title, Artist, Album, Composer, Time, Year, Favorite, Rating, Plays, LastPlayed, Comment, BPM, Bitrate, Size, Path
|
||||||
CompactTracklistRowColumnWidths = []float32{numColWidth, -1, -1, -1, -1, timeColWidth, yearColWidth, favColWidth, ratingColWidth, playsColWidth, -1, bpmColWidth, bitrateColWidth, sizeColWidth, -1}
|
CompactTracklistRowColumnWidths = []float32{numColWidth, -1, -1, -1, -1, timeColWidth, yearColWidth, favColWidth, ratingColWidth, playsColWidth, lastPlayedColWidth, -1, bpmColWidth, bitrateColWidth, sizeColWidth, -1}
|
||||||
// #, Title/Artist, Album, Composer, Time, Year, Favorite, Rating, Plays, Comment, BPM, Bitrate, Size, Path
|
// #, Title/Artist, Album, Composer, Time, Year, Favorite, Rating, Plays, LastPlayed, Comment, BPM, Bitrate, Size, Path
|
||||||
ExpandedTracklistRowColumnWidths = []float32{numColWidth, -1, -1, -1, timeColWidth, yearColWidth, favColWidth, ratingColWidth, playsColWidth, -1, bpmColWidth, bitrateColWidth, sizeColWidth, -1}
|
ExpandedTracklistRowColumnWidths = []float32{numColWidth, -1, -1, -1, timeColWidth, yearColWidth, favColWidth, ratingColWidth, playsColWidth, lastPlayedColWidth, -1, bpmColWidth, bitrateColWidth, sizeColWidth, -1}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -196,6 +204,7 @@ type tracklistRowBase struct {
|
|||||||
rating *StarRating
|
rating *StarRating
|
||||||
bitrate *widget.Label
|
bitrate *widget.Label
|
||||||
plays *widget.Label
|
plays *widget.Label
|
||||||
|
lastPlayed *widget.Label
|
||||||
comment *ttwidget.Label
|
comment *ttwidget.Label
|
||||||
bpm *widget.Label
|
bpm *widget.Label
|
||||||
size *widget.Label
|
size *widget.Label
|
||||||
@@ -253,7 +262,7 @@ func NewExpandedTracklistRow(tracklist *Tracklist, im *backend.ImageManager, pla
|
|||||||
|
|
||||||
v := makeVerticallyCentered // func alias
|
v := makeVerticallyCentered // func alias
|
||||||
container := container.New(tracklist.colLayout,
|
container := container.New(tracklist.colLayout,
|
||||||
v(t.num), titleArtistImg, v(t.album), v(t.composer), v(t.dur), v(t.year), v(t.favorite), v(t.rating), v(t.plays), v(t.comment), v(t.bpm), v(t.bitrate), v(t.size), v(t.path))
|
v(t.num), titleArtistImg, v(t.album), v(t.composer), v(t.dur), v(t.year), v(t.favorite), v(t.rating), v(t.plays), v(t.lastPlayed), v(t.comment), v(t.bpm), v(t.bitrate), v(t.size), v(t.path))
|
||||||
t.Content = container
|
t.Content = container
|
||||||
t.setColVisibility = func(colNum int, vis bool) bool {
|
t.setColVisibility = func(colNum int, vis bool) bool {
|
||||||
c := container.Objects[colNum].(*fyne.Container)
|
c := container.Objects[colNum].(*fyne.Container)
|
||||||
@@ -281,7 +290,7 @@ func NewCompactTracklistRow(tracklist *Tracklist, playingIcon fyne.CanvasObject)
|
|||||||
t.playingIcon = playingIcon
|
t.playingIcon = playingIcon
|
||||||
|
|
||||||
t.Content = container.New(tracklist.colLayout,
|
t.Content = container.New(tracklist.colLayout,
|
||||||
t.num, t.name, t.artist, t.album, t.composer, t.dur, t.year, t.favorite, t.rating, t.plays, t.comment, t.bpm, t.bitrate, t.size, t.path)
|
t.num, t.name, t.artist, t.album, t.composer, t.dur, t.year, t.favorite, t.rating, t.plays, t.lastPlayed, t.comment, t.bpm, t.bitrate, t.size, t.path)
|
||||||
|
|
||||||
colHiddenPtrMap := map[int]*bool{
|
colHiddenPtrMap := map[int]*bool{
|
||||||
2: &t.artist.Hidden,
|
2: &t.artist.Hidden,
|
||||||
@@ -292,11 +301,12 @@ func NewCompactTracklistRow(tracklist *Tracklist, playingIcon fyne.CanvasObject)
|
|||||||
7: &t.favorite.Hidden,
|
7: &t.favorite.Hidden,
|
||||||
8: &t.rating.Hidden,
|
8: &t.rating.Hidden,
|
||||||
9: &t.plays.Hidden,
|
9: &t.plays.Hidden,
|
||||||
10: &t.comment.Hidden,
|
10: &t.lastPlayed.Hidden,
|
||||||
11: &t.bpm.Hidden,
|
11: &t.comment.Hidden,
|
||||||
12: &t.bitrate.Hidden,
|
12: &t.bpm.Hidden,
|
||||||
13: &t.size.Hidden,
|
13: &t.bitrate.Hidden,
|
||||||
14: &t.path.Hidden,
|
14: &t.size.Hidden,
|
||||||
|
15: &t.path.Hidden,
|
||||||
}
|
}
|
||||||
t.setColVisibility = func(colNum int, vis bool) bool {
|
t.setColVisibility = func(colNum int, vis bool) bool {
|
||||||
ptr, ok := colHiddenPtrMap[colNum]
|
ptr, ok := colHiddenPtrMap[colNum]
|
||||||
@@ -338,6 +348,7 @@ func (t *tracklistRowBase) create(tracklist *Tracklist) {
|
|||||||
t.rating.StarSize = 16
|
t.rating.StarSize = 16
|
||||||
t.rating.OnRatingChanged = t.setTrackRating
|
t.rating.OnRatingChanged = t.setTrackRating
|
||||||
t.plays = util.NewTrailingAlignLabel()
|
t.plays = util.NewTrailingAlignLabel()
|
||||||
|
t.lastPlayed = util.NewTruncatingLabel()
|
||||||
t.comment = util.NewTruncatingTooltipLabel()
|
t.comment = util.NewTruncatingTooltipLabel()
|
||||||
t.comment.OnMouseIn = t.MouseIn
|
t.comment.OnMouseIn = t.MouseIn
|
||||||
t.comment.OnMouseOut = t.MouseOut
|
t.comment.OnMouseOut = t.MouseOut
|
||||||
@@ -405,6 +416,7 @@ func (t *tracklistRowBase) doUpdate(tm *util.TrackListModel, rowNum int) {
|
|||||||
t.dur.Text = util.SecondsToMMSS(tr.Duration.Seconds())
|
t.dur.Text = util.SecondsToMMSS(tr.Duration.Seconds())
|
||||||
t.year.Text = strconv.Itoa(tr.Year)
|
t.year.Text = strconv.Itoa(tr.Year)
|
||||||
t.plays.Text = strconv.Itoa(int(tr.PlayCount))
|
t.plays.Text = strconv.Itoa(int(tr.PlayCount))
|
||||||
|
t.lastPlayed.Text = util.LastPlayedDisplayString(tr.LastPlayed)
|
||||||
t.comment.Text = strings.ReplaceAll(tr.Comment, "\n", " ")
|
t.comment.Text = strings.ReplaceAll(tr.Comment, "\n", " ")
|
||||||
t.comment.SetToolTip(tr.Comment)
|
t.comment.SetToolTip(tr.Comment)
|
||||||
t.bpm.Text = strconv.Itoa(tr.BPM)
|
t.bpm.Text = strconv.Itoa(tr.BPM)
|
||||||
|
|||||||
Reference in New Issue
Block a user