This commit is contained in:
aynakeya
2023-09-01 23:01:29 -07:00
parent caec88b461
commit 68bc5270c9
12 changed files with 554 additions and 188 deletions

135
providers/local/local.go Normal file
View File

@@ -0,0 +1,135 @@
package local
import (
"miaosic"
"os"
"path"
)
type Local struct {
localDir string
playlists map[string]*miaosic.Playlist
}
func NewLocal(localdir string) *Local {
l := &Local{localDir: localdir, playlists: make(map[string]*miaosic.Playlist, 0)}
if err := os.MkdirAll(localdir, 0755); err != nil {
return l
}
for _, n := range getPlaylistNames(localdir) {
playlist := &miaosic.Playlist{Meta: miaosic.MediaMeta{Provider: n}}
if readLocalPlaylist(localdir, playlist) != nil {
l.playlists[playlist.Title] = playlist
}
}
return l
}
func (l *Local) GetName() string {
return "local"
}
func (l *Local) MatchMedia(uri string) *miaosic.Media {
return nil
}
func (l *Local) MatchPlaylist(uri string) *miaosic.Playlist {
return nil
}
func (l *Local) Search(keyword string) ([]*miaosic.Media, error) {
allMedias := make([]*miaosic.Media, 0)
for _, p := range l.playlists {
for _, m := range p.Medias {
allMedias = append(allMedias, m)
}
}
return RankMedia(keyword, allMedias), nil
}
func (l *Local) UpdatePlaylist(playlist *miaosic.Playlist) error {
err := readLocalPlaylist(l.localDir, playlist)
if err != nil {
return err
}
l.playlists[playlist.Meta.Identifier] = playlist
return nil
}
func (l *Local) UpdateMedia(media *miaosic.Media) error {
mediaPath := path.Join(l.localDir, media.Meta.Identifier)
_, err := os.Stat(mediaPath)
if err != nil {
return err
}
return readMediaFile(l.localDir, media)
}
func (l *Local) UpdateMediaUrl(media *miaosic.Media) error {
mediaPath := path.Join(l.localDir, media.Meta.Identifier)
_, err := os.Stat(mediaPath)
if err != nil {
return err
}
media.Url = mediaPath
return nil
}
func (l *Local) UpdateMediaLyric(media *miaosic.Media) error {
return nil
}
//
//func (l *Local) Search(keyword string) ([]*model.Media, error) {
// allMedias := make([]*model.Media, 0)
// for _, p := range l.Playlists {
// for _, m := range p.Medias {
// allMedias = append(allMedias, m)
// }
// }
// MediaSort(keyword, allMedias)
// c := util.Min(len(allMedias), 32)
// medias := make([]*model.Media, c)
// for i := 0; i < c; i++ {
// medias[i] = allMedias[i].Copy()
// }
// return medias, nil
//}
//
//func (l *Local) SearchV1(keyword string) ([]*model.Media, error) {
// result := make([]struct {
// M *model.Media
// N int
// }, 0)
// keywords := strings.Split(keyword, " ")
// for _, p := range l.Playlists {
// for _, m := range p.Medias {
// title := strings.ToLower(m.Title)
// artist := strings.ToLower(m.Artist)
// n := 0
// for _, k := range keywords {
// kw := strings.ToLower(k)
// if strings.Contains(title, kw) || strings.Contains(artist, kw) {
// n++
// }
// if kw == title {
// n += 3
// }
// }
// if n > 0 {
// result = append(result, struct {
// M *model.Media
// N int
// }{M: m, N: n})
// }
// }
// }
// sort.Slice(result, func(i, j int) bool {
// return result[i].N > result[j].N
// })
// medias := make([]*model.Media, len(result))
// for i, r := range result {
// medias[i] = r.M.Copy()
// }
// return medias, nil
//}

View File

@@ -0,0 +1,129 @@
package local
import (
"github.com/dhowden/tag"
"github.com/sahilm/fuzzy"
"miaosic"
"os"
"path"
"path/filepath"
"sort"
"strings"
)
func getPlaylistNames(localdir string) []string {
names := make([]string, 0)
items, _ := os.ReadDir(localdir)
for _, item := range items {
if item.IsDir() {
names = append(names, item.Name())
}
}
return names
}
// readLocalPlaylist read files under a directory
// and return a _LocalPlaylist object.
// This function assume this directory exists
func readLocalPlaylist(localdir string, playlist *miaosic.Playlist) error {
p1th := playlist.Meta.Identifier
playlist.Medias = make([]*miaosic.Media, 0)
fullPath := filepath.Join(localdir, p1th)
if _, err := os.Stat(fullPath); os.IsNotExist(err) {
return err
}
items, _ := os.ReadDir(fullPath)
for _, item := range items {
// if item is a file, read file
if !item.IsDir() {
fn := item.Name()
media := miaosic.Media{
Meta: miaosic.MediaMeta{
Provider: "local",
Identifier: path.Join(playlist.Meta.Identifier, fn),
},
}
if readMediaFile(localdir, &media) != nil {
continue
}
playlist.Medias = append(playlist.Medias, &media)
}
}
return nil
}
func _getOrDefault(s string, def string) string {
if s == "" {
return def
}
return s
}
func readMediaFile(localdir string, media *miaosic.Media) error {
p := path.Join(localdir, media.Meta.Identifier)
f, err := os.Open(p)
if err != nil {
return err
}
defer f.Close()
meta, err := tag.ReadFrom(f)
if err != nil {
return err
}
media.Title = _getOrDefault(meta.Title(), filepath.Base(p))
media.Artist = _getOrDefault(meta.Artist(), "Unknown")
media.Album = _getOrDefault(meta.Album(), "Unknown")
media.Lyric = []miaosic.Lyrics{miaosic.ParseLyrics("default", meta.Lyrics())}
if meta.Picture() != nil {
media.Cover.Data = meta.Picture().Data
}
return nil
}
type mediaRanking struct {
media *miaosic.Media
score int
}
func RankMedia(keyword string, medias []*miaosic.Media) []*miaosic.Media {
patterns := strings.Split(keyword, " ")
data := make([]*mediaRanking, 0)
for _, media := range medias {
m := media
data = append(data, &mediaRanking{
media: m,
score: 0,
})
}
for _, pattern := range patterns {
pattern = strings.ToLower(pattern)
dataStr := make([]string, 0)
for _, d := range data {
dataStr = append(dataStr, strings.ToLower(d.media.Title))
}
for _, match := range fuzzy.Find(pattern, dataStr) {
data[match.Index].score += match.Score
}
dataStr = make([]string, 0)
for _, d := range data {
dataStr = append(dataStr, strings.ToLower(d.media.Artist))
}
for _, match := range fuzzy.Find(pattern, dataStr) {
data[match.Index].score += match.Score
}
}
sort.Slice(data, func(i, j int) bool {
return data[i].score > data[j].score
})
result := make([]*miaosic.Media, 0)
for _, d := range data {
if d.score > 0 {
result = append(result, d.media)
}
}
return result
}

View File

@@ -0,0 +1,82 @@
package local
import (
"fmt"
"github.com/sahilm/fuzzy"
"miaosic"
"sort"
"strings"
"testing"
)
var testData = []miaosic.Media{
{Title: "Shape of You", Artist: "Ed Sheeran"},
{Title: "Lose Yourself", Artist: "Eminem"},
{Title: "Believer", Artist: "Imagine Dragons"},
{Title: "Counting Stars", Artist: "OneRepublic"},
{Title: "Rolling in the Deep", Artist: "Adele"},
{Title: "Uptown Funk", Artist: "Mark Ronson ft. Bruno Mars"},
{Title: "Imagine", Artist: "John Lennon"},
{Title: "I Will Always Love You", Artist: "Whitney Houston"},
{Title: "Smells Like Teen Spirit", Artist: "Nirvana"},
{Title: "Billie Jean", Artist: "Michael Jackson"},
// Chinese songs
{Title: "平凡之路", Artist: "朴树"},
{Title: "染", Artist: "reol"},
{Title: "怪物", Artist: "reol"},
{Title: "怪物", Artist: "王菲"},
{Title: "怪物", Artist: "怪物"},
{Title: "小幸运", Artist: "田馥甄"},
{Title: "遥远的她", Artist: "张学友"},
{Title: "匆匆那年", Artist: "王菲"},
{Title: "岁月神偷", Artist: "金玟岐"},
{Title: "突然好想你", Artist: "五月天"},
{Title: "蓝莲花", Artist: "许巍"},
{Title: "红豆", Artist: "王菲"},
{Title: "夜空中最亮的星", Artist: "逃跑计划"},
{Title: "爱情转移", Artist: "陈奕迅"},
}
func TestLocal_SearchTest1(t *testing.T) {
pattern := "王菲"
patterns := strings.Split(pattern, " ")
data := make([]*mediaRanking, 0)
for _, media := range testData {
m := media
data = append(data, &mediaRanking{
media: &m,
score: 0,
})
}
dataStr := make([]string, 0)
for _, d := range data {
dataStr = append(dataStr, strings.ToLower(d.media.Title+" "+d.media.Artist))
}
for _, pattern := range patterns {
for _, match := range fuzzy.Find(pattern, dataStr) {
data[match.Index].score += match.Score
}
}
sort.Slice(data, func(i, j int) bool {
return data[i].score > data[j].score
})
for _, d := range data {
fmt.Println(d.score, d.media)
}
}
func TestLocal_SearchTest2(t *testing.T) {
data := make([]*miaosic.Media, 0)
for _, media := range testData {
m := media
data = append(data, &m)
}
for _, media := range RankMedia("怪物 reol", data) {
fmt.Println(media)
}
}