Compare commits
2 commits
a83022b1d3
...
5db3be63f7
Author | SHA1 | Date | |
---|---|---|---|
DataHoarder | 5db3be63f7 | ||
DataHoarder | 4b78f093b5 |
1
go.mod
1
go.mod
|
@ -6,6 +6,7 @@ require (
|
|||
facette.io/natsort v0.0.0-20181210072756-2cd4dd1e2dcb
|
||||
git.gammaspectra.live/S.O.N.G/Hibiki v0.0.0-20220216151616-63d8894466c0
|
||||
git.gammaspectra.live/S.O.N.G/MakyuuIchaival v0.0.0-20220131114831-c08c7d9b4153
|
||||
git.gammaspectra.live/S.O.N.G/wikitext-parser v0.0.0-20220220212802-e21f1e249ca9
|
||||
github.com/dgraph-io/badger/v3 v3.2103.2
|
||||
github.com/dhowden/tag v0.0.0-20201120070457-d52dcb253c63
|
||||
github.com/ikawaha/kagome-dict/uni v1.1.3
|
||||
|
|
2
go.sum
2
go.sum
|
@ -17,6 +17,8 @@ git.gammaspectra.live/S.O.N.G/go-pus v0.0.0-20220130003320-c9b07c6bec7a h1:LxrTp
|
|||
git.gammaspectra.live/S.O.N.G/go-pus v0.0.0-20220130003320-c9b07c6bec7a/go.mod h1:vkoHSHVM9p6vAUmXAik0gvaLcIfiQYrD6bQqVpOulUk=
|
||||
git.gammaspectra.live/S.O.N.G/goborator v0.0.0-20220201143845-faddd6ec920b h1:h7+SZUINAMVCY5h3E5UFT64GLaI+tJ3V758e9inPyeA=
|
||||
git.gammaspectra.live/S.O.N.G/goborator v0.0.0-20220201143845-faddd6ec920b/go.mod h1:ySjuueqe5HUqvf7lWS51Cy5UP2tgJWsezOv8UIm2arA=
|
||||
git.gammaspectra.live/S.O.N.G/wikitext-parser v0.0.0-20220220212802-e21f1e249ca9 h1:lIiSlBlge43zULALq20yrd4Ern1XejSRaIwWQlsc4uM=
|
||||
git.gammaspectra.live/S.O.N.G/wikitext-parser v0.0.0-20220220212802-e21f1e249ca9/go.mod h1:WRXSVczbEaJc+qb8f8C9ZLi4naQl32HS0WK/eccO9Hk=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
|
|
|
@ -18,7 +18,7 @@ type Source struct {
|
|||
|
||||
func NewSource() *Source {
|
||||
s := &Source{}
|
||||
s.client = metadata.NewCachingClient(s.GetURL())
|
||||
s.client = metadata.NewCachingClient(s.GetURL(), time.Second/10)
|
||||
return s
|
||||
}
|
||||
|
||||
|
|
|
@ -14,8 +14,9 @@ var DefaultUserAgent = fmt.Sprintf("Mozilla/5.0 (compatible; METANOIA/%s; +https
|
|||
var defaultCacheStore CacheStore
|
||||
|
||||
type CachingClient struct {
|
||||
context string
|
||||
client *http.Client
|
||||
context string
|
||||
client *http.Client
|
||||
throttler <-chan time.Time
|
||||
}
|
||||
|
||||
type CacheStore interface {
|
||||
|
@ -27,10 +28,11 @@ func SetCacheStore(store CacheStore) {
|
|||
defaultCacheStore = store
|
||||
}
|
||||
|
||||
func NewCachingClient(context string) *CachingClient {
|
||||
func NewCachingClient(context string, rateLimit time.Duration) *CachingClient {
|
||||
return &CachingClient{
|
||||
context: context,
|
||||
client: http.DefaultClient,
|
||||
context: context,
|
||||
client: http.DefaultClient,
|
||||
throttler: time.Tick(rateLimit),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,6 +55,7 @@ func (c *CachingClient) Request(req *http.Request, lifetime time.Duration) (*htt
|
|||
}
|
||||
}
|
||||
|
||||
<-c.throttler
|
||||
response, err := c.client.Do(req)
|
||||
if defaultCacheStore != nil && err == nil {
|
||||
req.Header.Set("X-Fetched-At", time.Now().UTC().Format(time.UnixDate))
|
||||
|
|
|
@ -23,7 +23,7 @@ type Source struct {
|
|||
|
||||
func NewSource() *Source {
|
||||
s := &Source{}
|
||||
s.client = metadata.NewCachingClient(s.GetURL())
|
||||
s.client = metadata.NewCachingClient(s.GetURL(), time.Second/10)
|
||||
return s
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ type Source struct {
|
|||
|
||||
func NewSource() *Source {
|
||||
s := &Source{}
|
||||
s.client = metadata.NewCachingClient(s.GetURL())
|
||||
s.client = metadata.NewCachingClient(s.GetURL(), time.Second)
|
||||
return s
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,9 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"git.gammaspectra.live/S.O.N.G/METANOIA/metadata"
|
||||
"git.gammaspectra.live/S.O.N.G/METANOIA/utilities/wiki"
|
||||
wikitext_parser "git.gammaspectra.live/S.O.N.G/wikitext-parser"
|
||||
"github.com/oriser/regroup"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math"
|
||||
|
@ -25,7 +26,7 @@ type Source struct {
|
|||
|
||||
func NewSource() *Source {
|
||||
s := &Source{}
|
||||
s.client = metadata.NewCachingClient(s.GetURL())
|
||||
s.client = metadata.NewCachingClient(s.GetURL(), time.Second/2)
|
||||
return s
|
||||
}
|
||||
|
||||
|
@ -157,7 +158,7 @@ func (s *Source) FindQueryArguments(queryArgs string) (albums []*metadata.Album)
|
|||
}
|
||||
|
||||
for _, r := range result.Query.Search {
|
||||
album := s.GetAlbumInformation(wiki.NormalizeWikiTitle(r.Title))
|
||||
album := s.GetAlbumInformation(wikitext_parser.NormalizeWikiTitle(r.Title))
|
||||
if album != nil {
|
||||
albums = append(albums, album)
|
||||
}
|
||||
|
@ -247,7 +248,7 @@ func (s *Source) FindAdvancedQueryArguments(queryArgs string) (albums []*metadat
|
|||
}
|
||||
|
||||
for _, albumName := range result.Results.Text {
|
||||
album := s.GetAlbumInformation(wiki.NormalizeWikiTitle(albumName))
|
||||
album := s.GetAlbumInformation(wikitext_parser.NormalizeWikiTitle(albumName))
|
||||
if album != nil {
|
||||
albums = append(albums, album)
|
||||
}
|
||||
|
@ -276,7 +277,7 @@ func (s *Source) GetArticle(title string) ([]interface{}, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return wiki.ParseWikiText(string(body)), nil
|
||||
return wikitext_parser.ParseWikiText(string(body)), nil
|
||||
}
|
||||
|
||||
func (s *Source) GetFileURL(title string) string {
|
||||
|
@ -330,7 +331,7 @@ func (s *Source) GetFileURL(title string) string {
|
|||
|
||||
func (s *Source) GetSongLyrics(songName string) *metadata.Lyrics {
|
||||
uri, _ := url.Parse(baseLyricsURL)
|
||||
uri.Path += "lyrics/" + wiki.NormalizeWikiTitle(songName) + ".lrc"
|
||||
uri.Path += "lyrics/" + wikitext_parser.NormalizeWikiTitle(songName) + ".lrc"
|
||||
response, err := s.client.Request(&http.Request{
|
||||
Method: "GET",
|
||||
URL: uri,
|
||||
|
@ -367,7 +368,7 @@ func (s *Source) GetSongLyrics(songName string) *metadata.Lyrics {
|
|||
|
||||
lyrics := &metadata.Lyrics{
|
||||
Identifiers: []metadata.Name{
|
||||
{Kind: "url", Name: baseURL + "歌词:" + wiki.NormalizeWikiTitle(songName)},
|
||||
{Kind: "url", Name: baseURL + "歌词:" + wikitext_parser.NormalizeWikiTitle(songName)},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -441,6 +442,43 @@ func (s *Source) GetSongLyrics(songName string) *metadata.Lyrics {
|
|||
|
||||
}
|
||||
|
||||
func getWikiStringOptions(title string, trim bool) *wikitext_parser.WikiStringValueOptions {
|
||||
|
||||
opts := &wikitext_parser.WikiStringValueOptions{}
|
||||
opts.Default()
|
||||
opts.PageName = title
|
||||
opts.StringHandler = func(value string, opt *wikitext_parser.WikiStringValueOptions) []string {
|
||||
return []string{norm.NFC.String(norm.NFKD.String(value))}
|
||||
}
|
||||
opts.TemplateHandler = func(template *wikitext_parser.Template, opt *wikitext_parser.WikiStringValueOptions) (result []string) {
|
||||
switch strings.ToUpper(template.Name) {
|
||||
case "CM":
|
||||
if val, ok := template.Parameters["1"]; ok && len(val) > 0 {
|
||||
result = append(result, fmt.Sprintf("Comiket %s", wikitext_parser.GetWikiStringValue(val, opt)[0]))
|
||||
}
|
||||
case "红楼梦":
|
||||
if val, ok := template.Parameters["1"]; ok && len(val) > 0 {
|
||||
result = append(result, fmt.Sprintf("Touhou Kouroumu %s", wikitext_parser.GetWikiStringValue(val, opt)[0]))
|
||||
}
|
||||
case "例大祭":
|
||||
if val, ok := template.Parameters["1"]; ok && len(val) > 0 {
|
||||
result = append(result, fmt.Sprintf("Hakurei Shrine Reitaisai %s", wikitext_parser.GetWikiStringValue(val, opt)[0]))
|
||||
}
|
||||
case "PAGENAME", "SUBPAGENAME":
|
||||
result = append(result, opt.PageName)
|
||||
default:
|
||||
result = append(result, template.Name)
|
||||
}
|
||||
return
|
||||
}
|
||||
opts.Trim = trim
|
||||
return opts
|
||||
}
|
||||
|
||||
func getStringValue(v []interface{}, opt *wikitext_parser.WikiStringValueOptions) []string {
|
||||
return wikitext_parser.GetWikiStringValue(v, opt)
|
||||
}
|
||||
|
||||
func (s *Source) GetAlbumInformation(title string) *metadata.Album {
|
||||
article, err := s.GetArticle(title)
|
||||
if err != nil {
|
||||
|
@ -449,101 +487,10 @@ func (s *Source) GetAlbumInformation(title string) *metadata.Album {
|
|||
|
||||
var album *metadata.Album
|
||||
|
||||
normalizeStringCharacters := func(text string) string {
|
||||
for _, v := range [][2]string{
|
||||
{",", ", "},
|
||||
{"(", "("},
|
||||
{")", ")"},
|
||||
} {
|
||||
text = strings.Replace(text, v[0], v[1], -1)
|
||||
}
|
||||
|
||||
return text
|
||||
}
|
||||
removeLinks := func(text string) (result string) {
|
||||
|
||||
index := 0
|
||||
var isInternal bool
|
||||
|
||||
for i := 0; i < len(text); i++ {
|
||||
c := text[i]
|
||||
|
||||
if index != 0 {
|
||||
if c == ']' {
|
||||
|
||||
if isInternal {
|
||||
segments := strings.Split(text[index:i], "|")
|
||||
if len(segments) > 1 {
|
||||
segments = segments[1:]
|
||||
}
|
||||
result += strings.Join(segments, "|")
|
||||
} else {
|
||||
segments := strings.Split(text[index:i], " ")
|
||||
if len(segments) > 1 {
|
||||
segments = segments[1:]
|
||||
}
|
||||
result += strings.Join(segments, " ")
|
||||
}
|
||||
|
||||
if len(text) > i+1 && text[i+1] == ']' {
|
||||
i++
|
||||
}
|
||||
|
||||
index = 0
|
||||
}
|
||||
} else {
|
||||
if c == '[' {
|
||||
if len(text) > i+1 && text[i+1] == '[' {
|
||||
i++
|
||||
isInternal = true
|
||||
} else {
|
||||
isInternal = false
|
||||
}
|
||||
|
||||
index = i + 1
|
||||
} else {
|
||||
result += text[i : i+1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
getStringValue := func(v []interface{}) (result []string) {
|
||||
|
||||
for _, value := range v {
|
||||
text, ok := value.(string)
|
||||
if ok {
|
||||
result = append(result, normalizeStringCharacters(text))
|
||||
} else {
|
||||
template, ok := value.(*wiki.Template)
|
||||
if ok {
|
||||
switch template.Name {
|
||||
case "PAGENAME":
|
||||
fallthrough
|
||||
case "SUBPAGENAME":
|
||||
result = append(result, title)
|
||||
case "CM":
|
||||
result = append(result, fmt.Sprintf("Comiket %s", template.Parameters["1"][0].(string)))
|
||||
case "红楼梦":
|
||||
result = append(result, fmt.Sprintf("Touhou Kouroumu %s", template.Parameters["1"][0].(string)))
|
||||
case "例大祭":
|
||||
result = append(result, fmt.Sprintf("Hakurei Shrine Reitaisai %s", template.Parameters["1"][0].(string)))
|
||||
default:
|
||||
result = append(result, template.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
opts := getWikiStringOptions(title, true)
|
||||
|
||||
var staffMappings []map[string]string
|
||||
listingStaff := false
|
||||
var staffRE = regroup.MustCompile(`(?m)^;(?P<position>[^:]+):(?P<name>.+)$`)
|
||||
var staffNameSplitRE = regroup.MustCompile(`(?P<name>[^<]+)(?P<position><br/?>|$)`)
|
||||
var staffNameGroupRE = regroup.MustCompile(`(?P<position>.+)\((?P<name>.+)\)`)
|
||||
|
||||
var zunComposerRole = metadata.Role{
|
||||
|
@ -554,9 +501,9 @@ func (s *Source) GetAlbumInformation(title string) *metadata.Album {
|
|||
},
|
||||
}
|
||||
|
||||
handleStaffMapping := func(discIndex int, val []interface{}, kind string, track *metadata.Track) {
|
||||
handleStaffMapping := func(discIndex int, val []interface{}, kind string, track *metadata.Track, opts *wikitext_parser.WikiStringValueOptions) {
|
||||
if len(staffMappings) > discIndex {
|
||||
for _, e := range strings.Split(strings.Join(getStringValue(val), ","), ",") {
|
||||
for _, e := range strings.Split(strings.Join(getStringValue(val, opts), ","), ",") {
|
||||
entryValue := strings.TrimSpace(e)
|
||||
|
||||
if len(entryValue) > 0 {
|
||||
|
@ -565,13 +512,13 @@ func (s *Source) GetAlbumInformation(title string) *metadata.Album {
|
|||
if ok && groupValue != entryValue {
|
||||
track.Roles = append(track.Roles, metadata.Role{
|
||||
Kind: kind,
|
||||
Name: []metadata.Name{{Kind: "original", Name: entryValue}, {Kind: "url", Name: baseURL + wiki.NormalizeWikiTitle(entryValue)}},
|
||||
Name: []metadata.Name{{Kind: "original", Name: entryValue}, {Kind: "url", Name: baseURL + wikitext_parser.NormalizeWikiTitle(entryValue)}},
|
||||
Group: groupValue,
|
||||
})
|
||||
} else {
|
||||
track.Roles = append(track.Roles, metadata.Role{
|
||||
Kind: kind,
|
||||
Name: []metadata.Name{{Kind: "original", Name: entryValue}, {Kind: "url", Name: baseURL + wiki.NormalizeWikiTitle(entryValue)}},
|
||||
Name: []metadata.Name{{Kind: "original", Name: entryValue}, {Kind: "url", Name: baseURL + wikitext_parser.NormalizeWikiTitle(entryValue)}},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -580,293 +527,270 @@ func (s *Source) GetAlbumInformation(title string) *metadata.Album {
|
|||
}
|
||||
|
||||
for _, v := range article {
|
||||
text, ok := v.(string)
|
||||
if ok {
|
||||
if text, ok := v.(string); ok {
|
||||
if strings.Index(text, "== Staff ==") != -1 {
|
||||
listingStaff = true
|
||||
}
|
||||
|
||||
} else if list, ok := v.(*wikitext_parser.DescriptionList); ok {
|
||||
if listingStaff {
|
||||
type StaffMatch struct {
|
||||
Position string `regroup:"position"`
|
||||
Name string `regroup:"name"`
|
||||
roleName := strings.ToLower(strings.Join(getStringValue(list.Name, opts), ""))
|
||||
|
||||
switch roleName {
|
||||
case "total produce":
|
||||
fallthrough
|
||||
case "produce":
|
||||
fallthrough
|
||||
case "production":
|
||||
roleName = "producer"
|
||||
case "assistant":
|
||||
roleName = "assistant"
|
||||
case "illustration":
|
||||
fallthrough
|
||||
case "cover artwork":
|
||||
roleName = "illustration"
|
||||
|
||||
case "mastering":
|
||||
roleName = "mastering"
|
||||
case "graphic design":
|
||||
fallthrough
|
||||
case "graphics":
|
||||
fallthrough
|
||||
case "design":
|
||||
roleName = "design"
|
||||
case "compose":
|
||||
roleName = "composer"
|
||||
}
|
||||
m := &StaffMatch{}
|
||||
rets, err := staffRE.MatchAllToTarget(normalizeStringCharacters(text), -1, m)
|
||||
if err == nil {
|
||||
for _, elem := range rets {
|
||||
match := elem.(*StaffMatch)
|
||||
|
||||
for _, roleName := range strings.Split(match.Position, "/") {
|
||||
roleName = strings.ToLower(strings.TrimSpace(roleName))
|
||||
|
||||
switch roleName {
|
||||
case "total produce":
|
||||
fallthrough
|
||||
case "produce":
|
||||
fallthrough
|
||||
case "production":
|
||||
roleName = "producer"
|
||||
case "assistant":
|
||||
roleName = "assistant"
|
||||
case "illustration":
|
||||
fallthrough
|
||||
case "cover artwork":
|
||||
roleName = "illustration"
|
||||
|
||||
case "mastering":
|
||||
roleName = "mastering"
|
||||
case "graphic design":
|
||||
fallthrough
|
||||
case "graphics":
|
||||
fallthrough
|
||||
case "design":
|
||||
roleName = "design"
|
||||
case "compose":
|
||||
roleName = "composer"
|
||||
}
|
||||
|
||||
rets2, err := staffNameSplitRE.MatchAllToTarget(removeLinks(strings.TrimSpace(match.Name)), -1, m)
|
||||
if err == nil {
|
||||
for _, elem := range rets2 {
|
||||
match2 := elem.(*StaffMatch)
|
||||
|
||||
m3 := &StaffMatch{}
|
||||
|
||||
role := metadata.Role{
|
||||
Kind: roleName,
|
||||
Name: []metadata.Name{{Kind: "original", Name: strings.TrimSpace(match2.Name)}},
|
||||
}
|
||||
|
||||
if staffNameGroupRE.MatchToTarget(strings.TrimSpace(match2.Name), m3) == nil {
|
||||
//Is a group
|
||||
role.Name = []metadata.Name{{Kind: "original", Name: strings.TrimSpace(m3.Position)}}
|
||||
role.Group = strings.TrimSpace(m3.Name)
|
||||
}
|
||||
|
||||
album.Roles = append(album.Roles, role)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for _, a := range strings.Split(strings.Join(getStringValue(list.Entries, opts), ""), ",") {
|
||||
role := metadata.Role{
|
||||
Kind: roleName,
|
||||
Name: []metadata.Name{{Kind: "original", Name: strings.TrimSpace(a)}},
|
||||
}
|
||||
|
||||
m := &struct {
|
||||
Position string `regroup:"position"`
|
||||
Name string `regroup:"name"`
|
||||
}{}
|
||||
if staffNameGroupRE.MatchToTarget(strings.TrimSpace(a), m) == nil {
|
||||
//Is a group
|
||||
role.Name = []metadata.Name{{Kind: "original", Name: strings.TrimSpace(m.Position)}}
|
||||
role.Group = strings.TrimSpace(m.Name)
|
||||
}
|
||||
|
||||
album.Roles = append(album.Roles, role)
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
template, ok := v.(*wiki.Template)
|
||||
if ok {
|
||||
switch template.Name {
|
||||
case "专辑人员列表": //Album Person List
|
||||
if album == nil {
|
||||
continue
|
||||
}
|
||||
} else if template, ok := v.(*wikitext_parser.Template); ok {
|
||||
switch template.Name {
|
||||
case "专辑人员列表": //Album Person List
|
||||
if album == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
discIndex, _ := strconv.Atoi(strings.Join(getStringValue(template.Parameters["碟号"]), " ")) //disc number
|
||||
discIndex--
|
||||
discIndex, _ := strconv.Atoi(strings.Join(getStringValue(template.Parameters["碟号"], opts), " ")) //disc number
|
||||
discIndex--
|
||||
|
||||
staffMappings = append(staffMappings, make(map[string]string))
|
||||
staffMappings = append(staffMappings, make(map[string]string))
|
||||
|
||||
for _, val := range template.Parameters { //nested
|
||||
for _, value := range getStringValue(val) {
|
||||
parts := strings.Split(value, "=")
|
||||
p0 := strings.TrimSpace(parts[0])
|
||||
if len(parts) > 1 {
|
||||
staffMappings[discIndex][p0] = strings.TrimSpace(parts[1])
|
||||
} else {
|
||||
staffMappings[discIndex][p0] = p0
|
||||
}
|
||||
for _, val := range template.Parameters { //nested
|
||||
for _, value := range getStringValue(val, opts) {
|
||||
parts := strings.Split(value, "=")
|
||||
p0 := strings.TrimSpace(parts[0])
|
||||
if len(parts) > 1 {
|
||||
staffMappings[discIndex][p0] = strings.TrimSpace(parts[1])
|
||||
} else {
|
||||
staffMappings[discIndex][p0] = p0
|
||||
}
|
||||
}
|
||||
case "专辑曲目列表": //Album Track List
|
||||
if album == nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
case "专辑曲目列表": //Album Track List
|
||||
if album == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
listingStaff = false
|
||||
listingStaff = false
|
||||
|
||||
discIndex := len(album.Discs)
|
||||
discIndex := len(album.Discs)
|
||||
|
||||
disc := metadata.Disc{}
|
||||
disc := metadata.Disc{}
|
||||
|
||||
for _, value := range template.Parameters["嵌套"] { //nested
|
||||
|
||||
tpl, ok := value.(*wiki.Template)
|
||||
if ok {
|
||||
track := metadata.Track{}
|
||||
for k, val := range tpl.Parameters {
|
||||
switch k {
|
||||
case "时长": //duration
|
||||
d := getStringValue(val)[0]
|
||||
sub := strings.Split(d, ":")
|
||||
s1, _ := strconv.ParseInt(strings.TrimLeft(sub[0], "0"), 10, 0)
|
||||
if len(sub) > 2 {
|
||||
//TODO
|
||||
} else if len(sub) > 1 {
|
||||
s2, _ := strconv.ParseInt(strings.TrimLeft(sub[1], "0"), 10, 0)
|
||||
track.Duration = time.Minute*time.Duration(s1) + time.Second*time.Duration(s2)
|
||||
} else {
|
||||
track.Duration = time.Second * time.Duration(s1)
|
||||
}
|
||||
case "名称": //title
|
||||
trackTitle := strings.Join(getStringValue(val), " ")
|
||||
track.Name = append(track.Name, metadata.Name{
|
||||
Kind: "original",
|
||||
Name: trackTitle,
|
||||
})
|
||||
track.Lyrics = func() *metadata.Lyrics {
|
||||
return s.GetSongLyrics(trackTitle)
|
||||
}
|
||||
case "原专辑": //original release
|
||||
track.Links = append(track.Links, metadata.Link{
|
||||
Kind: "original release",
|
||||
Name: []metadata.Name{{Kind: "original", Name: strings.Join(getStringValue(val), " ")}},
|
||||
})
|
||||
case "原名称": //original release title
|
||||
track.Links = append(track.Links, metadata.Link{
|
||||
Kind: "original release title",
|
||||
Name: []metadata.Name{{Kind: "original", Name: strings.Join(getStringValue(val), " ")}},
|
||||
})
|
||||
case "原曲": //original song
|
||||
|
||||
case "编曲": //arranger/composer
|
||||
if _, ok := tpl.Parameters["原曲"]; ok {
|
||||
handleStaffMapping(discIndex, val, "arranger", &track)
|
||||
track.Roles = append(track.Roles, zunComposerRole) //TODO check
|
||||
} else {
|
||||
handleStaffMapping(discIndex, val, "composer", &track)
|
||||
}
|
||||
case "再编曲": //re-arranger
|
||||
handleStaffMapping(discIndex, val, "remix", &track)
|
||||
case "演唱": //vocals
|
||||
handleStaffMapping(discIndex, val, "vocals", &track)
|
||||
case "作词": //lyrics
|
||||
handleStaffMapping(discIndex, val, "lyrics", &track)
|
||||
for _, value := range template.Parameters["嵌套"] { //nested
|
||||
if tpl, ok := value.(*wikitext_parser.Template); ok {
|
||||
track := metadata.Track{}
|
||||
for k, val := range tpl.Parameters {
|
||||
switch k {
|
||||
case "时长": //duration
|
||||
d := strings.Join(getStringValue(val, opts), "")
|
||||
sub := strings.Split(d, ":")
|
||||
s1, _ := strconv.ParseInt(strings.TrimLeft(sub[0], "0"), 10, 0)
|
||||
if len(sub) > 2 {
|
||||
//TODO
|
||||
} else if len(sub) > 1 {
|
||||
s2, _ := strconv.ParseInt(strings.TrimLeft(sub[1], "0"), 10, 0)
|
||||
track.Duration = time.Minute*time.Duration(s1) + time.Second*time.Duration(s2)
|
||||
} else {
|
||||
track.Duration = time.Second * time.Duration(s1)
|
||||
}
|
||||
case "名称": //title
|
||||
trackTitle := strings.Join(getStringValue(val, opts), " ")
|
||||
track.Name = append(track.Name, metadata.Name{
|
||||
Kind: "original",
|
||||
Name: trackTitle,
|
||||
})
|
||||
track.Lyrics = func() *metadata.Lyrics {
|
||||
return s.GetSongLyrics(trackTitle)
|
||||
}
|
||||
case "原专辑": //original release
|
||||
track.Links = append(track.Links, metadata.Link{
|
||||
Kind: "original release",
|
||||
Name: []metadata.Name{{Kind: "original", Name: strings.Join(getStringValue(val, opts), " ")}},
|
||||
})
|
||||
case "原名称": //original release title
|
||||
track.Links = append(track.Links, metadata.Link{
|
||||
Kind: "original release title",
|
||||
Name: []metadata.Name{{Kind: "original", Name: strings.Join(getStringValue(val, opts), " ")}},
|
||||
})
|
||||
case "原曲": //original song
|
||||
|
||||
case "编曲": //arranger/composer
|
||||
if _, ok := tpl.Parameters["原曲"]; ok {
|
||||
handleStaffMapping(discIndex, val, "arranger", &track, opts)
|
||||
track.Roles = append(track.Roles, zunComposerRole) //TODO check
|
||||
} else {
|
||||
handleStaffMapping(discIndex, val, "composer", &track, opts)
|
||||
}
|
||||
case "再编曲": //re-arranger
|
||||
handleStaffMapping(discIndex, val, "remix", &track, opts)
|
||||
case "演唱": //vocals
|
||||
handleStaffMapping(discIndex, val, "vocals", &track, opts)
|
||||
case "作词": //lyrics
|
||||
handleStaffMapping(discIndex, val, "lyrics", &track, opts)
|
||||
}
|
||||
|
||||
disc.Tracks = append(disc.Tracks, track)
|
||||
}
|
||||
|
||||
disc.Tracks = append(disc.Tracks, track)
|
||||
}
|
||||
}
|
||||
|
||||
album.Discs = append(album.Discs, disc)
|
||||
album.Discs = append(album.Discs, disc)
|
||||
|
||||
case "同人专辑信息": //Doujin Album Information
|
||||
album = &metadata.Album{
|
||||
License: s.GetLicense(),
|
||||
SourceUniqueIdentifier: baseURL + title,
|
||||
Identifiers: []metadata.Name{
|
||||
{
|
||||
Kind: "url",
|
||||
Name: baseURL + title,
|
||||
},
|
||||
case "同人专辑信息": //Doujin Album Information
|
||||
album = &metadata.Album{
|
||||
License: s.GetLicense(),
|
||||
SourceUniqueIdentifier: baseURL + title,
|
||||
Identifiers: []metadata.Name{
|
||||
{
|
||||
Kind: "url",
|
||||
Name: baseURL + title,
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
album.Identifiers = append(album.Identifiers)
|
||||
album.Identifiers = append(album.Identifiers)
|
||||
|
||||
for k, val := range template.Parameters {
|
||||
for k, val := range template.Parameters {
|
||||
|
||||
switch k {
|
||||
case "封面": //cover: jpg
|
||||
fileName := strings.Join(getStringValue(val), "")
|
||||
if strings.Index(fileName, ".") == -1 {
|
||||
fileName = title + "封面." + fileName
|
||||
}
|
||||
f := s.GetFileURL(fileName)
|
||||
if len(f) > 0 {
|
||||
album.Art = append(album.Art, metadata.Name{
|
||||
Kind: "front",
|
||||
Name: s.GetFileURL(fileName),
|
||||
})
|
||||
}
|
||||
|
||||
case "展会": //release
|
||||
for _, value := range val {
|
||||
tpl, ok := value.(*wiki.Template)
|
||||
if ok {
|
||||
switch tpl.Name {
|
||||
case "CM":
|
||||
album.Links = append(album.Links, metadata.Link{
|
||||
Kind: "release",
|
||||
Name: []metadata.Name{
|
||||
{Kind: "name", Name: fmt.Sprintf("Comiket %s", tpl.Parameters["1"][0].(string))},
|
||||
},
|
||||
})
|
||||
case "红楼梦":
|
||||
album.Links = append(album.Links, metadata.Link{
|
||||
Kind: "release",
|
||||
Name: []metadata.Name{
|
||||
{Kind: "name", Name: fmt.Sprintf("Touhou Kouroumu %s", tpl.Parameters["1"][0].(string))},
|
||||
},
|
||||
})
|
||||
case "例大祭":
|
||||
album.Links = append(album.Links, metadata.Link{
|
||||
Kind: "release",
|
||||
Name: []metadata.Name{
|
||||
{Kind: "name", Name: fmt.Sprintf("Hakurei Shrine Reitaisai %s", tpl.Parameters["1"][0].(string))},
|
||||
},
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
case "名称": //name
|
||||
album.Name = append(album.Name, metadata.Name{
|
||||
Kind: "original",
|
||||
Name: strings.Join(getStringValue(val), " "),
|
||||
})
|
||||
|
||||
case "制作方": //produced by
|
||||
for _, producer := range strings.Split(strings.Join(getStringValue(val), ","), ",") {
|
||||
album.Roles = append(album.Roles, metadata.Role{
|
||||
Kind: "producer",
|
||||
Name: []metadata.Name{{Kind: "original", Name: producer}, {Kind: "url", Name: baseURL + wiki.NormalizeWikiTitle(producer)}},
|
||||
})
|
||||
}
|
||||
|
||||
case "发行方": //issuer
|
||||
for _, issuer := range strings.Split(strings.Join(getStringValue(val), ","), ",") {
|
||||
album.Roles = append(album.Roles, metadata.Role{
|
||||
Kind: "issuer",
|
||||
Name: []metadata.Name{{Kind: "original", Name: issuer}, {Kind: "url", Name: baseURL + wiki.NormalizeWikiTitle(issuer)}},
|
||||
})
|
||||
}
|
||||
|
||||
case "编号": //catalog number
|
||||
|
||||
for _, catalog := range strings.Split(strings.Join(getStringValue(val), "+"), "+") {
|
||||
album.Identifiers = append(album.Identifiers, metadata.Name{
|
||||
Kind: "catalog",
|
||||
Name: catalog,
|
||||
})
|
||||
}
|
||||
|
||||
case "风格类型": //style
|
||||
for _, style := range strings.Split(strings.Join(getStringValue(val), ","), ",") {
|
||||
switch strings.ToLower(style) {
|
||||
default:
|
||||
album.Tags = append(album.Tags, metadata.Name{
|
||||
Kind: "genre",
|
||||
Name: strings.ToLower(style), //TODO: normalize
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
case "官网页面": //official page
|
||||
album.Links = append(album.Links, metadata.Link{
|
||||
Kind: "official",
|
||||
Name: []metadata.Name{{Kind: "url", Name: strings.Join(getStringValue(val), " ")}},
|
||||
switch k {
|
||||
case "封面": //cover: jpg
|
||||
fileName := strings.Join(getStringValue(val, opts), "")
|
||||
if strings.Index(fileName, ".") == -1 {
|
||||
fileName = title + "封面." + fileName
|
||||
}
|
||||
f := s.GetFileURL(fileName)
|
||||
if len(f) > 0 {
|
||||
album.Art = append(album.Art, metadata.Name{
|
||||
Kind: "front",
|
||||
Name: s.GetFileURL(fileName),
|
||||
})
|
||||
}
|
||||
|
||||
case "展会": //release
|
||||
for _, value := range val {
|
||||
tpl, ok := value.(*wikitext_parser.Template)
|
||||
if ok {
|
||||
//TODO
|
||||
switch tpl.Name {
|
||||
case "CM":
|
||||
album.Links = append(album.Links, metadata.Link{
|
||||
Kind: "release",
|
||||
Name: []metadata.Name{
|
||||
{Kind: "name", Name: fmt.Sprintf("Comiket %s", tpl.Parameters["1"][0].(string))},
|
||||
},
|
||||
})
|
||||
case "红楼梦":
|
||||
album.Links = append(album.Links, metadata.Link{
|
||||
Kind: "release",
|
||||
Name: []metadata.Name{
|
||||
{Kind: "name", Name: fmt.Sprintf("Touhou Kouroumu %s", tpl.Parameters["1"][0].(string))},
|
||||
},
|
||||
})
|
||||
case "例大祭":
|
||||
album.Links = append(album.Links, metadata.Link{
|
||||
Kind: "release",
|
||||
Name: []metadata.Name{
|
||||
{Kind: "name", Name: fmt.Sprintf("Hakurei Shrine Reitaisai %s", tpl.Parameters["1"][0].(string))},
|
||||
},
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
case "名称": //name
|
||||
album.Name = append(album.Name, metadata.Name{
|
||||
Kind: "original",
|
||||
Name: strings.Join(getStringValue(val, opts), " "),
|
||||
})
|
||||
|
||||
case "制作方": //produced by
|
||||
for _, producer := range strings.Split(strings.Join(getStringValue(val, opts), ","), ",") {
|
||||
album.Roles = append(album.Roles, metadata.Role{
|
||||
Kind: "producer",
|
||||
Name: []metadata.Name{{Kind: "original", Name: producer}, {Kind: "url", Name: baseURL + wikitext_parser.NormalizeWikiTitle(producer)}},
|
||||
})
|
||||
}
|
||||
|
||||
case "发行方": //issuer
|
||||
for _, issuer := range strings.Split(strings.Join(getStringValue(val, opts), ","), ",") {
|
||||
album.Roles = append(album.Roles, metadata.Role{
|
||||
Kind: "issuer",
|
||||
Name: []metadata.Name{{Kind: "original", Name: issuer}, {Kind: "url", Name: baseURL + wikitext_parser.NormalizeWikiTitle(issuer)}},
|
||||
})
|
||||
}
|
||||
|
||||
case "编号": //catalog number
|
||||
|
||||
for _, catalog := range strings.Split(strings.Join(getStringValue(val, opts), "+"), "+") {
|
||||
album.Identifiers = append(album.Identifiers, metadata.Name{
|
||||
Kind: "catalog",
|
||||
Name: catalog,
|
||||
})
|
||||
}
|
||||
|
||||
case "风格类型": //style
|
||||
for _, style := range strings.Split(strings.Join(getStringValue(val, opts), ","), ",") {
|
||||
switch strings.ToLower(style) {
|
||||
default:
|
||||
album.Tags = append(album.Tags, metadata.Name{
|
||||
Kind: "genre",
|
||||
Name: strings.ToLower(style), //TODO: normalize
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
case "官网页面": //official page
|
||||
album.Links = append(album.Links, metadata.Link{
|
||||
Kind: "official",
|
||||
Name: []metadata.Name{{Kind: "url", Name: strings.Join(getStringValue(val, opts), " ")}},
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
//unknown value
|
||||
continue
|
||||
}
|
||||
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ type Source struct {
|
|||
|
||||
func NewSource() *Source {
|
||||
s := &Source{}
|
||||
s.client = metadata.NewCachingClient(s.GetURL())
|
||||
s.client = metadata.NewCachingClient(s.GetURL(), time.Second/10)
|
||||
return s
|
||||
}
|
||||
|
||||
|
|
|
@ -1,108 +0,0 @@
|
|||
package wiki
|
||||
|
||||
import "strings"
|
||||
|
||||
func NormalizeWikiTitle(title string) string {
|
||||
//TODO
|
||||
return strings.Replace(title, " ", "_", -1)
|
||||
}
|
||||
|
||||
//ParseWikiText small WikiText parser that extracts text, Templates, and its arguments/parameters
|
||||
func ParseWikiText(text string) (result []interface{}) {
|
||||
index := 0
|
||||
|
||||
for {
|
||||
templateIndex := strings.Index(text[index:], "{{")
|
||||
if templateIndex == -1 {
|
||||
t := strings.TrimSpace(text[index:])
|
||||
if len(t) > 0 {
|
||||
result = append(result, t)
|
||||
}
|
||||
break
|
||||
} else {
|
||||
t := strings.TrimSpace(text[index : index+templateIndex])
|
||||
if len(t) > 0 {
|
||||
result = append(result, t)
|
||||
}
|
||||
var tpl *Template
|
||||
index, tpl = ParseTemplate(text, index+templateIndex+2, 0)
|
||||
if tpl != nil {
|
||||
result = append(result, tpl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func ParseTemplate(text string, index int, depth int) (i int, template *Template) {
|
||||
|
||||
var c byte
|
||||
lastToken := index
|
||||
|
||||
var key string
|
||||
|
||||
addValue := func() int {
|
||||
if lastToken < len(text) && i-lastToken > 0 {
|
||||
t := strings.TrimSpace(text[lastToken:i])
|
||||
if len(t) > 0 {
|
||||
if template == nil {
|
||||
template = NewTemplate(t)
|
||||
} else {
|
||||
if key == "" {
|
||||
template.AddParameterUnkeyed(t)
|
||||
} else {
|
||||
template.AddParameter(key, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return len(t)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
addKey := func() {
|
||||
if lastToken < len(text) && i-lastToken > 0 {
|
||||
t := strings.TrimSpace(text[lastToken:i])
|
||||
if len(t) > 0 {
|
||||
key = t
|
||||
}
|
||||
}
|
||||
}
|
||||
for i = index; i < len(text); i++ {
|
||||
c = text[i]
|
||||
|
||||
if c == '}' && i < len(text)-1 && text[i+1] == '}' {
|
||||
addValue()
|
||||
i += 2
|
||||
break
|
||||
} else if c == '{' && i < len(text)-1 && text[i+1] == '{' {
|
||||
addValue()
|
||||
var tpl *Template
|
||||
i, tpl = ParseTemplate(text, i+2, depth+1)
|
||||
if tpl != nil {
|
||||
if key == "" {
|
||||
template.AddParameterUnkeyed(tpl)
|
||||
} else {
|
||||
template.AddParameter(key, tpl)
|
||||
}
|
||||
}
|
||||
lastToken = i
|
||||
} else if c == '|' {
|
||||
addValue()
|
||||
lastToken = i + 1
|
||||
key = ""
|
||||
} else if c == '\n' {
|
||||
addValue()
|
||||
lastToken = i + 1
|
||||
} else if c == '=' {
|
||||
if key == "" {
|
||||
addKey()
|
||||
lastToken = i + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package wiki
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Template struct {
|
||||
Name string
|
||||
Parameters map[string][]interface{}
|
||||
}
|
||||
|
||||
func NewTemplate(name string) *Template {
|
||||
return &Template{
|
||||
Name: name,
|
||||
Parameters: make(map[string][]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Template) AddParameterUnkeyed(value interface{}) {
|
||||
t.Parameters[fmt.Sprintf("%d", len(t.Parameters))] = []interface{}{value}
|
||||
}
|
||||
|
||||
func (t *Template) AddParameter(key string, value interface{}) {
|
||||
if _, ok := t.Parameters[key]; !ok {
|
||||
t.Parameters[key] = make([]interface{}, 0, 1)
|
||||
}
|
||||
t.Parameters[key] = append(t.Parameters[key], value)
|
||||
}
|
Loading…
Reference in a new issue