Add common go types
This commit is contained in:
parent
377e57ee43
commit
af4fc2fb64
4 changed files with 414 additions and 0 deletions
189
internal/common/album.go
Normal file
189
internal/common/album.go
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"suruatoel.xyz/mcg/internal/i18n"
|
||||
)
|
||||
|
||||
type Album struct {
|
||||
title string
|
||||
songs []*Song
|
||||
artists []string
|
||||
pathes []string
|
||||
dates []string
|
||||
length uint64
|
||||
lastModified time.Time
|
||||
}
|
||||
|
||||
func NewAlbum(title string) *Album {
|
||||
album := Album{
|
||||
title: title,
|
||||
}
|
||||
|
||||
return &album
|
||||
}
|
||||
|
||||
func (a *Album) Title() string {
|
||||
return a.title
|
||||
}
|
||||
|
||||
func (a *Album) AddSong(song *Song) {
|
||||
a.songs = append(a.songs, song)
|
||||
a.length += song.Length()
|
||||
|
||||
for _, artist := range song.AlbumArtists() {
|
||||
if !slices.Contains(a.artists, artist) {
|
||||
a.artists = append(a.artists, artist)
|
||||
}
|
||||
}
|
||||
|
||||
if !slices.Contains(a.dates, song.Date()) {
|
||||
a.dates = append(a.dates, song.Date())
|
||||
}
|
||||
|
||||
path := filepath.Base(song.File())
|
||||
if !slices.Contains(a.pathes, path) {
|
||||
a.pathes = append(a.pathes, path)
|
||||
}
|
||||
|
||||
if !song.LastModified().After(a.lastModified) {
|
||||
a.lastModified = song.LastModified()
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Album) Songs() []*Song {
|
||||
return a.songs
|
||||
}
|
||||
|
||||
func (a *Album) Artists() []string {
|
||||
return a.artists
|
||||
}
|
||||
|
||||
func (a *Album) Dates() []string {
|
||||
return a.dates
|
||||
}
|
||||
|
||||
func (a *Album) Length() uint64 {
|
||||
return a.length
|
||||
}
|
||||
|
||||
func (a *Album) LastModified() time.Time {
|
||||
return a.lastModified
|
||||
}
|
||||
|
||||
func (a *Album) ID() string {
|
||||
return generateAlbumId(a.title)
|
||||
}
|
||||
|
||||
func (a *Album) Tooltip() string {
|
||||
artists := strings.Join(a.Artists(), ", ")
|
||||
dates := strings.Join(a.dates, ", ")
|
||||
minutes := a.length / 60
|
||||
seconds := a.length - minutes*60
|
||||
length := fmt.Sprintf(i18n.T("%d:%d minutes"), minutes, seconds)
|
||||
|
||||
return strings.Join(
|
||||
[]string{
|
||||
a.title,
|
||||
dates,
|
||||
artists,
|
||||
length,
|
||||
},
|
||||
"\n",
|
||||
)
|
||||
}
|
||||
|
||||
type PlaylistAlbum struct {
|
||||
*Album
|
||||
songs []*PlaylistSong
|
||||
length uint64
|
||||
}
|
||||
|
||||
func NewPlaylistAlbum(album *Album) *PlaylistAlbum {
|
||||
playlistAlbum := PlaylistAlbum{
|
||||
Album: album,
|
||||
}
|
||||
|
||||
return &playlistAlbum
|
||||
}
|
||||
|
||||
func (a *PlaylistAlbum) AddSong(playlistSong *PlaylistSong) {
|
||||
a.songs = append(a.songs, playlistSong)
|
||||
|
||||
song := playlistSong.Song
|
||||
a.length += song.Length()
|
||||
|
||||
for _, artist := range song.AlbumArtists() {
|
||||
if !slices.Contains(a.artists, artist) {
|
||||
a.artists = append(a.artists, artist)
|
||||
}
|
||||
}
|
||||
|
||||
if !slices.Contains(a.dates, song.Date()) {
|
||||
a.dates = append(a.dates, song.Date())
|
||||
}
|
||||
|
||||
path := filepath.Base(song.File())
|
||||
if !slices.Contains(a.pathes, path) {
|
||||
a.pathes = append(a.pathes, path)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *PlaylistAlbum) Songs() []*PlaylistSong {
|
||||
return a.songs
|
||||
}
|
||||
|
||||
func (a *PlaylistAlbum) Length() uint64 {
|
||||
return a.length
|
||||
}
|
||||
|
||||
func (a *Album) IsMatch(query string) bool {
|
||||
if query == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
for keyword := range strings.SplitSeq(query, " ") {
|
||||
if !a.isKeywordMatch(keyword) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (a *Album) isKeywordMatch(keyword string) bool {
|
||||
if strings.Contains(strings.ToLower(a.title), keyword) {
|
||||
return true
|
||||
}
|
||||
for _, artist := range a.artists {
|
||||
if strings.Contains(strings.ToLower(artist), keyword) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, song := range a.songs {
|
||||
if strings.Contains(strings.ToLower(song.Title()), keyword) {
|
||||
return true
|
||||
}
|
||||
if strings.Contains(strings.ToLower(song.File()), keyword) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func generateAlbumId(values ...string) string {
|
||||
h := md5.New()
|
||||
for _, value := range values {
|
||||
io.WriteString(h, value)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%x", h.Sum(nil))
|
||||
}
|
||||
14
internal/common/outputdevice.go
Normal file
14
internal/common/outputdevice.go
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package common
|
||||
|
||||
type OutputDevice struct {
|
||||
ID string
|
||||
Name string
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
func NewOutputDevice(id string, name string) *OutputDevice {
|
||||
return &OutputDevice{
|
||||
ID: id,
|
||||
Name: name,
|
||||
}
|
||||
}
|
||||
122
internal/common/song.go
Normal file
122
internal/common/song.go
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Song struct {
|
||||
artists []string
|
||||
title string
|
||||
file string
|
||||
albumartists []string
|
||||
track string
|
||||
length uint64
|
||||
date string
|
||||
lastModified time.Time
|
||||
}
|
||||
|
||||
func NewSong(artist string, title string, file string) *Song {
|
||||
song := Song{
|
||||
artists: []string{artist},
|
||||
title: title,
|
||||
file: file,
|
||||
}
|
||||
|
||||
return &song
|
||||
}
|
||||
|
||||
func (s *Song) AddArtist(artist string) {
|
||||
s.artists = append(s.artists, artist)
|
||||
}
|
||||
|
||||
func (s *Song) AddAlbumartist(artist string) {
|
||||
s.albumartists = append(s.albumartists, artist)
|
||||
}
|
||||
|
||||
func (s *Song) SongArtists() []string {
|
||||
if len(s.albumartists) > 0 {
|
||||
songArtists := []string{}
|
||||
for _, artist := range s.artists {
|
||||
if !slices.Contains(s.albumartists, artist) {
|
||||
songArtists = append(songArtists, artist)
|
||||
}
|
||||
}
|
||||
|
||||
return songArtists
|
||||
}
|
||||
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (s *Song) AlbumArtists() []string {
|
||||
if len(s.albumartists) > 0 {
|
||||
return s.albumartists
|
||||
}
|
||||
|
||||
return s.artists
|
||||
}
|
||||
|
||||
func (s *Song) Title() string {
|
||||
return s.title
|
||||
}
|
||||
|
||||
func (s *Song) File() string {
|
||||
return s.file
|
||||
}
|
||||
|
||||
func (s *Song) SetTrack(track string) {
|
||||
s.track = track
|
||||
}
|
||||
|
||||
func (s *Song) Track() string {
|
||||
return s.track
|
||||
}
|
||||
|
||||
func (s *Song) SetLength(length uint64) {
|
||||
s.length = length
|
||||
}
|
||||
|
||||
func (s *Song) Length() uint64 {
|
||||
return s.length
|
||||
}
|
||||
|
||||
func (s *Song) SetDate(date string) {
|
||||
s.date = date
|
||||
}
|
||||
|
||||
func (s *Song) Date() string {
|
||||
return s.date
|
||||
}
|
||||
|
||||
func (s *Song) SetLastModified(lastModified time.Time) {
|
||||
s.lastModified = lastModified
|
||||
}
|
||||
|
||||
func (s *Song) LastModified() time.Time {
|
||||
return s.lastModified
|
||||
}
|
||||
|
||||
type PlaylistSong struct {
|
||||
*Song
|
||||
id string
|
||||
pos uint64
|
||||
}
|
||||
|
||||
func NewPlaylistSong(song *Song, id string, pos uint64) *PlaylistSong {
|
||||
playlistSong := &PlaylistSong{
|
||||
Song: song,
|
||||
id: id,
|
||||
pos: pos,
|
||||
}
|
||||
|
||||
return playlistSong
|
||||
}
|
||||
|
||||
func (t *PlaylistSong) ID() string {
|
||||
return t.id
|
||||
}
|
||||
|
||||
func (t *PlaylistSong) Pos() uint64 {
|
||||
return t.pos
|
||||
}
|
||||
89
internal/common/sort.go
Normal file
89
internal/common/sort.go
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
package common
|
||||
|
||||
import "sort"
|
||||
|
||||
type albumLessFunc func(p1, p2 *Album) bool
|
||||
|
||||
type albumSorter struct {
|
||||
albums []*Album
|
||||
less []albumLessFunc
|
||||
}
|
||||
|
||||
func (s *albumSorter) Len() int {
|
||||
return len(s.albums)
|
||||
}
|
||||
|
||||
func (s *albumSorter) Swap(i, j int) {
|
||||
s.albums[i], s.albums[j] = s.albums[j], s.albums[i]
|
||||
}
|
||||
|
||||
func (s *albumSorter) Less(i, j int) bool {
|
||||
p, q := s.albums[i], s.albums[j]
|
||||
|
||||
var k int
|
||||
for k = 0; k < len(s.less)-1; k++ {
|
||||
less := s.less[k]
|
||||
switch {
|
||||
case less(p, q):
|
||||
return true
|
||||
case less(q, p):
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return s.less[k](p, q)
|
||||
}
|
||||
|
||||
func (s *albumSorter) Sort(albums []*Album) {
|
||||
s.albums = albums
|
||||
sort.Sort(s)
|
||||
}
|
||||
|
||||
func OrderAlbumBy(less ...albumLessFunc) *albumSorter {
|
||||
return &albumSorter{
|
||||
less: less,
|
||||
}
|
||||
}
|
||||
|
||||
func SortAlbums(album []*Album, field string) {
|
||||
title := orderAlbumByTitle
|
||||
artists := orderAlbumByArtists
|
||||
years := orderAlbumByYears
|
||||
modified := orderAlbumByModified
|
||||
|
||||
var sorter *albumSorter
|
||||
switch field {
|
||||
case "artist":
|
||||
sorter = OrderAlbumBy(artists, title, years, modified)
|
||||
case "year":
|
||||
sorter = OrderAlbumBy(years, title, artists, modified)
|
||||
case "modified":
|
||||
sorter = OrderAlbumBy(modified, title, artists, years)
|
||||
default:
|
||||
sorter = OrderAlbumBy(title, artists, years, modified)
|
||||
}
|
||||
|
||||
sorter.Sort(album)
|
||||
}
|
||||
|
||||
func orderAlbumByTitle(c1, c2 *Album) bool {
|
||||
return c1.Title() < c2.Title()
|
||||
}
|
||||
|
||||
func orderAlbumByArtists(left, right *Album) bool {
|
||||
if len(left.Artists()) > 0 && len(right.Artists()) > 0 {
|
||||
return left.Artists()[0] < right.Artists()[0]
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func orderAlbumByYears(c1, c2 *Album) bool {
|
||||
if len(c1.Dates()) > 0 && len(c2.Dates()) > 0 {
|
||||
return c1.Dates()[0] < c2.Dates()[0]
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func orderAlbumByModified(c1, c2 *Album) bool {
|
||||
return c1.LastModified().Before(c2.LastModified())
|
||||
}
|
||||
Loading…
Reference in a new issue