Added track parsing, artist parsing
This commit is contained in:
parent
8321adf007
commit
9948b9cab0
|
@ -17,8 +17,8 @@ Small Golang server that loads all albums in an index at startup and is used to
|
|||
* Access page by name ex. `/pages_by_name/RETRO_FUTURE_GIRLS.wiki`
|
||||
* List files in a directory (ex. `/pages/` or `/pageindex/Arrangement_CDs/`)
|
||||
* Query database by either any album title or album catalog number
|
||||
* Title ex. `/search?type=title&query=マジコカタストロフィ`
|
||||
* Catalog ex. `/search?type=title&query=STAL-1302`
|
||||
* Album Title ex. `/search?type=album&query=マジコカタストロフィ`
|
||||
* Album Catalog ex. `/search?type=album&query=STAL-1302`
|
||||
* Values here are normalized so special characters or uppercase are changed. It deals with Unicode too.
|
||||
* Bare CDDB emulator for `query` and `read` commands. Used to query by track count + length of album.
|
||||
* query cmd ex. `/cddb?cmd=cddb+query+730dec08` (no need to provide TOC, but can be provided)
|
||||
|
|
685
server.go
685
server.go
|
@ -17,6 +17,7 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -24,11 +25,13 @@ import (
|
|||
"unicode"
|
||||
)
|
||||
|
||||
var arrangeCDIndex = make(map[int]*arrangeCdEntry)
|
||||
var arrangeCDIndexLock sync.Mutex
|
||||
var cdIndex = make(map[int]*albumEntry)
|
||||
var cdIndexLock sync.Mutex
|
||||
|
||||
var titleLookup = make(map[string][]*arrangeCdEntry)
|
||||
var tracksLookup = make(map[int][]*arrangeCdEntry)
|
||||
var albumTitleLookup = make(map[string][]*albumEntry)
|
||||
|
||||
//TODO: make this work with discs
|
||||
var discTracksLookup = make(map[int][]*albumEntry)
|
||||
|
||||
type categoryPageIndex struct {
|
||||
PageId int `json:"pageid"`
|
||||
|
@ -56,31 +59,237 @@ func parseCategoryPageIndex(filePath string) []categoryPageIndex {
|
|||
return s.Query.Members
|
||||
}
|
||||
|
||||
type arrangeCdEntry struct {
|
||||
Id int `json:"pageid"`
|
||||
MainTitle string `json:"pagetitle"`
|
||||
Group string `json:"pagetitle,omitempty"`
|
||||
Titles []string `json:"titles"`
|
||||
CatalogNumber string `json:"catalognumber,omitempty"`
|
||||
TrackCount int `json:"trackcount,omitempty"`
|
||||
Duration int `json:"duration,omitempty"`
|
||||
Year int `json:"year,omitempty"`
|
||||
type JSONTime struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
func processIndex(filePath string) {
|
||||
func (t JSONTime) MarshalJSON() ([]byte, error) {
|
||||
if t.IsZero() {
|
||||
return nil, nil
|
||||
}
|
||||
return []byte(fmt.Sprintf("\"%s\"", t.Format("2006-01-02"))), nil
|
||||
}
|
||||
|
||||
arrangeIndexPath := path.Join(filePath, "pageindex", "Arrangement_CDs")
|
||||
entries, err := ioutil.ReadDir(arrangeIndexPath)
|
||||
type albumEntry struct {
|
||||
Id int `json:"pageid"`
|
||||
Type string `json:"type"`
|
||||
MainTitle string `json:"pagetitle"`
|
||||
Artists []artistEntry `json:"artists,omitempty"`
|
||||
Titles []string `json:"titles"`
|
||||
CatalogNumber string `json:"catalognumber,omitempty"`
|
||||
Discs []discEntry `json:"discs,omitempty"`
|
||||
TrackCount int `json:"trackcount,omitempty"`
|
||||
Duration int `json:"duration,omitempty"`
|
||||
ReleaseDate JSONTime `json:"date,omitempty"`
|
||||
}
|
||||
type discEntry struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Duration int `json:"duration,omitempty"`
|
||||
TrackCount int `json:"trackcount,omitempty"`
|
||||
Tracks []trackEntry `json:"tracks"`
|
||||
}
|
||||
type trackEntry struct {
|
||||
Duration int `json:"duration,omitempty"`
|
||||
MainTitle string `json:"title,omitempty"`
|
||||
OriginalTitle string `json:"originaltitle,omitempty"`
|
||||
Titles []string `json:"titles,omitempty"`
|
||||
Artists []artistEntry `json:"artists,omitempty"`
|
||||
}
|
||||
type artistEntry struct {
|
||||
Position string `json:"position"`
|
||||
Names []string `json:"names"`
|
||||
}
|
||||
|
||||
func getArtistEntries(kind string, entries []interface{}) (artists []artistEntry) {
|
||||
|
||||
artist := artistEntry{
|
||||
Position: kind,
|
||||
}
|
||||
|
||||
recreateArtist := func(kinds ...string) {
|
||||
var names []string
|
||||
for _, n := range artist.Names {
|
||||
//TODO
|
||||
//n = strings.Trim(n, " ()[]()")
|
||||
n = strings.TrimSpace(n)
|
||||
if len(n) > 0 {
|
||||
var curName string
|
||||
if len(names) > 0 {
|
||||
curName = names[len(names)-1]
|
||||
}
|
||||
if len(curName) > 0 && curName[len(curName)-1] == '(' {
|
||||
names[len(names)-1] += n
|
||||
} else if len(curName) > 0 && len(n) > 1 && n[len(n)-1] == ')' && strings.Index(n, "(") <= 0 {
|
||||
names[len(names)-1] += " " + n
|
||||
} else if len(curName) > 0 && n[0] == '(' && (strings.Index(n, ")") == -1 || n[len(n)-1] == ')') {
|
||||
names[len(names)-1] += " " + n
|
||||
} else if len(curName) > 0 && n == ")" {
|
||||
names[len(names)-1] += n
|
||||
} else if len(curName) > 0 && curName[len(curName)-1] == '/' {
|
||||
names[len(names)-1] += n
|
||||
} else if len(curName) > 0 && n == "/" {
|
||||
names[len(names)-1] += n
|
||||
} else {
|
||||
names = append(names, n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
artist.Names = names
|
||||
if len(artist.Names) > 0 {
|
||||
artists = append(artists, artist)
|
||||
artist = artistEntry{
|
||||
Position: strings.Join(kinds, ", "),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, value := range entries {
|
||||
if text, ok := value.(string); ok {
|
||||
switch strings.ToLower(text) {
|
||||
case "&", "and":
|
||||
recreateArtist(kind)
|
||||
}
|
||||
artist.Names = append(artist.Names, normalizeStringCharacters(text))
|
||||
} else if _, ok := value.(wikiparser.NewLineToken); ok {
|
||||
recreateArtist(kind)
|
||||
} else if tpl, ok := value.(*wikiparser.Template); ok {
|
||||
if tpl.IsLink {
|
||||
var result []string
|
||||
result = append(result, tpl.Name)
|
||||
for _, vv := range tpl.Parameters {
|
||||
result = append(result, getStringValue("", vv)...)
|
||||
}
|
||||
artist.Names = append(artist.Names, strings.Join(result, " "))
|
||||
} else {
|
||||
artist.Names = append(artist.Names, strings.Join(getStringValue("", []interface{}{tpl}), " "))
|
||||
}
|
||||
} else if link, ok := value.(*wikiparser.Link); ok {
|
||||
if len(link.Name) > 0 {
|
||||
artist.Names = append(artist.Names, strings.Join(getStringValue(" ", link.Name), " "))
|
||||
} else if !link.IsExternal {
|
||||
artist.Names = append(artist.Names, link.URL)
|
||||
}
|
||||
} else if unorderedList, ok := value.(*wikiparser.UnorderedList); ok {
|
||||
for _, val := range unorderedList.Entries {
|
||||
recreateArtist(kind)
|
||||
|
||||
if text, ok := val.(string); ok {
|
||||
artist.Names = append(artist.Names, text)
|
||||
} else if tpl, ok := val.(*wikiparser.Template); ok {
|
||||
if tpl.IsLink {
|
||||
artist.Names = append(artist.Names, tpl.Name)
|
||||
for _, val := range tpl.Parameters {
|
||||
artist.Names = append(artist.Names, getStringValue("", val)...)
|
||||
}
|
||||
} else {
|
||||
artist.Names = append(artist.Names, getStringValue("", []interface{}{tpl})...)
|
||||
}
|
||||
}
|
||||
}
|
||||
recreateArtist(kind)
|
||||
} else if descriptionList, ok := value.(*wikiparser.DescriptionList); ok {
|
||||
for _, val := range descriptionList.Entries {
|
||||
recreateArtist(kind, strings.Join(getStringValue("", descriptionList.Name), ", "))
|
||||
|
||||
if text, ok := val.(string); ok {
|
||||
artist.Names = append(artist.Names, text)
|
||||
} else if tpl, ok := val.(*wikiparser.Template); ok {
|
||||
if tpl.IsLink {
|
||||
artist.Names = append(artist.Names, tpl.Name)
|
||||
for _, val := range tpl.Parameters {
|
||||
artist.Names = append(artist.Names, getStringValue("", val)...)
|
||||
}
|
||||
} else {
|
||||
artist.Names = append(artist.Names, getStringValue("", []interface{}{tpl})...)
|
||||
}
|
||||
}
|
||||
|
||||
recreateArtist(kind)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
recreateArtist(kind)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func normalizeStringCharacters(text string) string {
|
||||
//TODO: use transform chain
|
||||
for _, v := range [][2]string{
|
||||
{",", ", "},
|
||||
{"(", "("},
|
||||
{")", ")"},
|
||||
} {
|
||||
text = strings.Replace(text, v[0], v[1], -1)
|
||||
}
|
||||
|
||||
return text
|
||||
}
|
||||
|
||||
func getStringValue(pageName string, v []interface{}) (result []string) {
|
||||
|
||||
for _, value := range v {
|
||||
|
||||
if text, ok := value.(string); ok {
|
||||
result = append(result, normalizeStringCharacters(text))
|
||||
} else if template, ok := value.(*wikiparser.Template); ok {
|
||||
if template.IsLink {
|
||||
result = append(result, template.Name)
|
||||
for _, vv := range template.Parameters {
|
||||
result = append(result, getStringValue(pageName, vv)...)
|
||||
}
|
||||
} else {
|
||||
switch strings.ToUpper(template.Name) {
|
||||
case "H:TITLE":
|
||||
if val, ok := template.Parameters["0"]; ok && len(val) > 0 {
|
||||
result = append(result, getStringValue(pageName, val)[0])
|
||||
}
|
||||
case "LANG":
|
||||
if val, ok := template.Parameters["1"]; ok && len(val) > 0 {
|
||||
result = append(result, getStringValue(pageName, val)[0])
|
||||
}
|
||||
case "GENRE":
|
||||
if val, ok := template.Parameters["0"]; ok && len(val) > 0 {
|
||||
result = append(result, getStringValue(pageName, val)[0])
|
||||
}
|
||||
case "PAGENAME":
|
||||
fallthrough
|
||||
case "SUBPAGENAME":
|
||||
result = append(result, pageName)
|
||||
default:
|
||||
result = append(result, template.Name)
|
||||
}
|
||||
}
|
||||
} else if link, ok := value.(*wikiparser.Link); ok {
|
||||
if len(link.Name) > 0 {
|
||||
result = append(result, getStringValue(pageName, link.Name)...)
|
||||
} else {
|
||||
result = append(result, link.URL)
|
||||
}
|
||||
result = append(result, getStringValue(pageName, link.Name)...)
|
||||
} else if unorderedList, ok := value.(*wikiparser.UnorderedList); ok {
|
||||
result = append(result, getStringValue(pageName, unorderedList.Entries)...)
|
||||
} else if descriptionList, ok := value.(*wikiparser.DescriptionList); ok {
|
||||
result = append(result, strings.Join(getStringValue("", descriptionList.Name), ", ")+": "+strings.Join(getStringValue(pageName, descriptionList.Entries), ", "))
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func processIndexDirectory(filePath, indexPath, kind string, wg *sync.WaitGroup) {
|
||||
entries, err := ioutil.ReadDir(indexPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for _, e := range entries {
|
||||
if path.Ext(e.Name()) == ".json" {
|
||||
for _, v := range parseCategoryPageIndex(path.Join(arrangeIndexPath, e.Name())) {
|
||||
for _, v := range parseCategoryPageIndex(path.Join(indexPath, e.Name())) {
|
||||
wg.Add(1)
|
||||
go func(entry *arrangeCdEntry) {
|
||||
go func(entry *albumEntry) {
|
||||
defer wg.Done()
|
||||
|
||||
contents, err := ioutil.ReadFile(path.Join(filePath, "pages", fmt.Sprintf("%d.wiki", entry.Id)))
|
||||
|
@ -102,120 +311,259 @@ func processIndex(filePath string) {
|
|||
return
|
||||
}
|
||||
|
||||
var stringName string
|
||||
title, ok := tpl.Parameters["titleen"]
|
||||
if ok && len(title) > 0 {
|
||||
stringName, ok = title[0].(string)
|
||||
if ok && len(stringName) > 0 {
|
||||
entry.Titles = append(entry.Titles, stringName)
|
||||
}
|
||||
}
|
||||
title, ok = tpl.Parameters["titlejp"]
|
||||
if ok && len(title) > 0 {
|
||||
stringName, ok = title[0].(string)
|
||||
if ok && len(stringName) > 0 {
|
||||
entry.Titles = append(entry.Titles, stringName)
|
||||
}
|
||||
}
|
||||
title, ok = tpl.Parameters["titlejprom"]
|
||||
if ok && len(title) > 0 {
|
||||
stringName, ok = title[0].(string)
|
||||
if ok && len(stringName) > 0 {
|
||||
entry.Titles = append(entry.Titles, stringName)
|
||||
}
|
||||
}
|
||||
catalogNo, ok := tpl.Parameters["catalogno"]
|
||||
if ok && len(catalogNo) > 0 {
|
||||
stringName, ok = catalogNo[0].(string)
|
||||
if ok && len(stringName) > 0 {
|
||||
entry.CatalogNumber = stringName
|
||||
}
|
||||
}
|
||||
groupCat, ok := tpl.Parameters["groupCat"]
|
||||
if ok && len(groupCat) > 0 {
|
||||
stringName, ok = groupCat[0].(string)
|
||||
if ok && len(stringName) > 0 {
|
||||
entry.Group = stringName
|
||||
}
|
||||
}
|
||||
released, ok := tpl.Parameters["released"]
|
||||
if ok && len(released) > 0 {
|
||||
stringName, ok = released[0].(string)
|
||||
if ok && len(released) > 0 {
|
||||
releaseDate, err := time.ParseInLocation("2006-01-02", stringName, time.UTC)
|
||||
if err == nil {
|
||||
entry.Year = releaseDate.Year()
|
||||
}
|
||||
}
|
||||
}
|
||||
tracks, ok := tpl.Parameters["tracks"]
|
||||
if ok && len(tracks) > 0 {
|
||||
stringName, ok = tracks[0].(string)
|
||||
if ok && len(stringName) > 0 {
|
||||
entry.TrackCount, _ = strconv.Atoi(stringName)
|
||||
}
|
||||
}
|
||||
length, ok := tpl.Parameters["length"]
|
||||
if ok && len(length) > 0 {
|
||||
stringName, ok = length[0].(string)
|
||||
if ok && len(stringName) > 0 {
|
||||
split := strings.Split(stringName, ":")
|
||||
numbers := make([]int, len(split))
|
||||
for i, val := range split {
|
||||
numbers[i], _ = strconv.Atoi(val)
|
||||
}
|
||||
var discLengths []int
|
||||
var discTrackNumbers []int
|
||||
|
||||
if len(numbers) == 3 {
|
||||
entry.Duration = 3600*numbers[0] + 60*numbers[1] + numbers[2]
|
||||
} else if len(numbers) == 2 {
|
||||
entry.Duration = 60*numbers[0] + numbers[1]
|
||||
} else if len(numbers) == 1 {
|
||||
entry.Duration = numbers[0]
|
||||
var val []interface{}
|
||||
var stringVal []string
|
||||
|
||||
if val, ok = tpl.Parameters["titleen"]; ok {
|
||||
if stringVal = getStringValue(entry.MainTitle, val); len(stringVal) > 0 {
|
||||
entry.Titles = append(entry.Titles, stringVal[0])
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok = tpl.Parameters["titlejp"]; ok {
|
||||
if stringVal = getStringValue(entry.MainTitle, val); len(stringVal) > 0 {
|
||||
entry.Titles = append(entry.Titles, stringVal[0])
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok = tpl.Parameters["titlejprom"]; ok {
|
||||
if stringVal = getStringValue(entry.MainTitle, val); len(stringVal) > 0 {
|
||||
entry.Titles = append(entry.Titles, stringVal[0])
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok = tpl.Parameters["catalogno"]; ok {
|
||||
if stringVal = getStringValue(entry.MainTitle, val); len(stringVal) > 0 {
|
||||
entry.CatalogNumber = stringVal[0]
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok = tpl.Parameters["group"]; ok {
|
||||
entry.Artists = append(entry.Artists, getArtistEntries("group", val)...)
|
||||
}
|
||||
if val, ok = tpl.Parameters["masterer"]; ok {
|
||||
entry.Artists = append(entry.Artists, getArtistEntries("mastering", val)...)
|
||||
}
|
||||
if val, ok = tpl.Parameters["illustrator"]; ok {
|
||||
entry.Artists = append(entry.Artists, getArtistEntries("illustration", val)...)
|
||||
}
|
||||
if val, ok = tpl.Parameters["arranger"]; ok {
|
||||
entry.Artists = append(entry.Artists, getArtistEntries("arranger", val)...)
|
||||
}
|
||||
if val, ok = tpl.Parameters["lyricist"]; ok {
|
||||
entry.Artists = append(entry.Artists, getArtistEntries("lyrics", val)...)
|
||||
}
|
||||
if val, ok = tpl.Parameters["vocalist"]; ok {
|
||||
entry.Artists = append(entry.Artists, getArtistEntries("vocals", val)...)
|
||||
}
|
||||
if val, ok = tpl.Parameters["producer"]; ok {
|
||||
entry.Artists = append(entry.Artists, getArtistEntries("producer", val)...)
|
||||
}
|
||||
if val, ok = tpl.Parameters["designer"]; ok {
|
||||
entry.Artists = append(entry.Artists, getArtistEntries("design", val)...)
|
||||
}
|
||||
if val, ok = tpl.Parameters["other"]; ok {
|
||||
entry.Artists = append(entry.Artists, getArtistEntries("other", val)...)
|
||||
}
|
||||
|
||||
if val, ok = tpl.Parameters["released"]; ok {
|
||||
if stringVal = getStringValue(entry.MainTitle, val); len(stringVal) > 0 {
|
||||
if releaseDate, err := time.ParseInLocation("2006-01-02", stringVal[0], time.UTC); err == nil {
|
||||
entry.ReleaseDate = JSONTime{Time: releaseDate}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
arrangeCDIndexLock.Lock()
|
||||
arrangeCDIndex[entry.Id] = entry
|
||||
if entry.TrackCount > 0 {
|
||||
if _, ok := tracksLookup[entry.TrackCount]; !ok {
|
||||
tracksLookup[entry.TrackCount] = []*arrangeCdEntry{entry}
|
||||
if val, ok = tpl.Parameters["length"]; ok {
|
||||
if stringVal = getStringValue(entry.MainTitle, val); len(stringVal) > 0 {
|
||||
for _, item := range strings.Split(strings.Split(stringVal[0], "=")[0], "+") {
|
||||
split := strings.Split(item, ":")
|
||||
numbers := make([]int, len(split))
|
||||
for i, numVal := range split {
|
||||
numbers[i], _ = strconv.Atoi(numVal)
|
||||
}
|
||||
|
||||
var duration int
|
||||
if len(numbers) == 3 {
|
||||
duration = 3600*numbers[0] + 60*numbers[1] + numbers[2]
|
||||
} else if len(numbers) == 2 {
|
||||
duration = 60*numbers[0] + numbers[1]
|
||||
} else if len(numbers) == 1 {
|
||||
duration = numbers[0]
|
||||
}
|
||||
|
||||
discLengths = append(discLengths, duration)
|
||||
|
||||
entry.Duration += duration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok = tpl.Parameters["tracks"]; ok {
|
||||
if stringVal = getStringValue(entry.MainTitle, val); len(stringVal) > 0 {
|
||||
for _, item := range strings.Split(strings.Split(stringVal[0], "=")[0], "+") {
|
||||
trackCount, _ := strconv.Atoi(item)
|
||||
discTrackNumbers = append(discTrackNumbers, trackCount)
|
||||
entry.TrackCount += trackCount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok = tpl.Parameters["tracklist"]; ok {
|
||||
var disc discEntry
|
||||
createDisc := func() {
|
||||
if len(disc.Tracks) > 0 {
|
||||
disc.TrackCount = len(disc.Tracks)
|
||||
entry.Discs = append(entry.Discs, disc)
|
||||
}
|
||||
}
|
||||
for _, listEntry := range val {
|
||||
if strVal, ok := listEntry.(string); ok && len(strVal) > 3 && strVal[0:3] == "===" {
|
||||
//title header
|
||||
if disc.Name != "" {
|
||||
createDisc()
|
||||
}
|
||||
disc.Name = strings.Trim(strVal, "= ")
|
||||
} else if listVal, ok := listEntry.(*wikiparser.UnorderedList); ok && len(listVal.Entries) > 0 && len(listVal.Entries) > 0 {
|
||||
if sliceVal, ok := listVal.Entries[0].([]interface{}); ok && len(sliceVal) > 0 {
|
||||
|
||||
if trackTpl, ok := sliceVal[0].(*wikiparser.Template); ok && strings.ToUpper(trackTpl.Name) == "TRACK" && len(trackTpl.Parameters) >= 3 {
|
||||
track := trackEntry{}
|
||||
|
||||
if mainTitleValue := getStringValue(entry.MainTitle, trackTpl.Parameters["1"]); len(mainTitleValue) > 0 {
|
||||
track.MainTitle = mainTitleValue[0]
|
||||
track.Titles = append(track.Titles, mainTitleValue...)
|
||||
}
|
||||
if durations := getStringValue(entry.MainTitle, trackTpl.Parameters["2"]); len(durations) > 0 {
|
||||
split := strings.Split(durations[0], ":")
|
||||
numbers := make([]int, len(split))
|
||||
for i, numVal := range split {
|
||||
numbers[i], _ = strconv.Atoi(numVal)
|
||||
}
|
||||
|
||||
var duration int
|
||||
if len(numbers) == 3 {
|
||||
duration = 3600*numbers[0] + 60*numbers[1] + numbers[2]
|
||||
} else if len(numbers) == 2 {
|
||||
duration = 60*numbers[0] + numbers[1]
|
||||
} else if len(numbers) == 1 {
|
||||
duration = numbers[0]
|
||||
}
|
||||
track.Duration = duration
|
||||
}
|
||||
|
||||
if len(listVal.Entries) > 1 {
|
||||
if extraListData, ok := listVal.Entries[1].(*wikiparser.UnorderedList); ok && len(extraListData.Entries) > 0 {
|
||||
for i, entryValue := range extraListData.Entries {
|
||||
if descVal, ok := entryValue.([]interface{}); ok && len(descVal) > 0 {
|
||||
|
||||
keyValue := strings.Split(strings.Join(getStringValue(entry.MainTitle, descVal), " "), ":")
|
||||
if len(keyValue) > 0 {
|
||||
if i == 0 && len(keyValue[0]) > 2 && keyValue[0][0:2] == "''" {
|
||||
track.Titles = append(track.Titles, strings.Trim(strings.Join(keyValue, " "), "'' "))
|
||||
continue
|
||||
}
|
||||
keyEntry := strings.ToLower(keyValue[0])
|
||||
var values []interface{}
|
||||
for j, fullValue := range descVal {
|
||||
if strVal, ok = fullValue.(string); j == 0 && ok {
|
||||
values = append(values, strings.Join(strings.Split(strVal, ":")[1:], ":"))
|
||||
} else {
|
||||
values = append(values, fullValue)
|
||||
}
|
||||
}
|
||||
switch keyEntry {
|
||||
case "original title":
|
||||
track.OriginalTitle = strings.TrimSpace(strings.Join(getStringValue(entry.MainTitle, values), " "))
|
||||
case "lyrics", "vocals", "arranger", "composer", "producer":
|
||||
track.Artists = append(track.Artists, getArtistEntries(keyEntry, values)...)
|
||||
case "arrangement":
|
||||
track.Artists = append(track.Artists, getArtistEntries("arranger", values)...)
|
||||
case "composition":
|
||||
track.Artists = append(track.Artists, getArtistEntries("composer", values)...)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
disc.Duration += track.Duration
|
||||
disc.Tracks = append(disc.Tracks, track)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
createDisc()
|
||||
|
||||
}
|
||||
|
||||
cdIndexLock.Lock()
|
||||
cdIndex[entry.Id] = entry
|
||||
for _, d := range entry.Discs {
|
||||
if _, ok := discTracksLookup[d.TrackCount]; !ok {
|
||||
discTracksLookup[d.TrackCount] = []*albumEntry{entry}
|
||||
} else {
|
||||
tracksLookup[entry.TrackCount] = append(tracksLookup[entry.TrackCount], entry)
|
||||
for _, val := range discTracksLookup[d.TrackCount] {
|
||||
if val.Id == entry.Id {
|
||||
goto exit2
|
||||
}
|
||||
}
|
||||
discTracksLookup[d.TrackCount] = append(discTracksLookup[d.TrackCount], entry)
|
||||
|
||||
exit2:
|
||||
}
|
||||
}
|
||||
|
||||
if len(entry.CatalogNumber) > 0 {
|
||||
normalized := normalizeTitle(entry.CatalogNumber)
|
||||
if _, ok := titleLookup[normalized]; !ok {
|
||||
titleLookup[normalized] = []*arrangeCdEntry{entry}
|
||||
if _, ok := albumTitleLookup[normalized]; !ok {
|
||||
albumTitleLookup[normalized] = []*albumEntry{entry}
|
||||
} else {
|
||||
titleLookup[normalized] = append(titleLookup[normalized], entry)
|
||||
albumTitleLookup[normalized] = append(albumTitleLookup[normalized], entry)
|
||||
}
|
||||
}
|
||||
for _, title := range entry.Titles {
|
||||
normalized := normalizeTitle(title)
|
||||
if _, ok := titleLookup[normalized]; !ok {
|
||||
titleLookup[normalized] = []*arrangeCdEntry{entry}
|
||||
if _, ok := albumTitleLookup[normalized]; !ok {
|
||||
albumTitleLookup[normalized] = []*albumEntry{entry}
|
||||
} else {
|
||||
for _, val := range titleLookup[normalized] {
|
||||
for _, val := range albumTitleLookup[normalized] {
|
||||
if val.Id == entry.Id {
|
||||
goto exit
|
||||
}
|
||||
}
|
||||
titleLookup[normalized] = append(titleLookup[normalized], entry)
|
||||
albumTitleLookup[normalized] = append(albumTitleLookup[normalized], entry)
|
||||
|
||||
exit:
|
||||
}
|
||||
}
|
||||
defer arrangeCDIndexLock.Unlock()
|
||||
}(&arrangeCdEntry{
|
||||
defer cdIndexLock.Unlock()
|
||||
}(&albumEntry{
|
||||
Id: v.PageId,
|
||||
MainTitle: v.Title,
|
||||
Type: kind,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func processIndex(filePath string) {
|
||||
|
||||
var wg sync.WaitGroup
|
||||
processIndexDirectory(filePath, path.Join(filePath, "pageindex", "Official_CDs"), "official", &wg)
|
||||
processIndexDirectory(filePath, path.Join(filePath, "pageindex", "Arrangement_CDs"), "arrangement", &wg)
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
|
@ -247,15 +595,26 @@ func normalizeTitle(title string) (normalized string) {
|
|||
return
|
||||
}
|
||||
|
||||
func findByTracksAndDuration(tracks, duration, threshold int) (results []*arrangeCdEntry) {
|
||||
for _, r := range tracksLookup[tracks] {
|
||||
diff := r.Duration - duration
|
||||
if diff < 0 {
|
||||
diff = -diff
|
||||
}
|
||||
type findByDurationResult struct {
|
||||
albumEntry *albumEntry
|
||||
discIndex int
|
||||
}
|
||||
|
||||
if diff <= threshold {
|
||||
results = append(results, r)
|
||||
func findByTracksAndDuration(tracks, duration, threshold int) (results []findByDurationResult) {
|
||||
for _, r := range discTracksLookup[tracks] {
|
||||
for i, d := range r.Discs {
|
||||
|
||||
diff := d.Duration - duration
|
||||
if diff < 0 {
|
||||
diff = -diff
|
||||
}
|
||||
|
||||
if diff <= threshold {
|
||||
results = append(results, findByDurationResult{
|
||||
albumEntry: r,
|
||||
discIndex: i,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -286,6 +645,26 @@ func main() {
|
|||
}
|
||||
switch splits[1] {
|
||||
case "query":
|
||||
formatEntry := func(cddb1 utilities.CDDB1, result findByDurationResult) (out string) {
|
||||
var group string
|
||||
for _, a := range result.albumEntry.Artists {
|
||||
if a.Position == "group" {
|
||||
group = a.Names[0]
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(result.albumEntry.CatalogNumber) > 0 {
|
||||
out = fmt.Sprintf("%sSoundtrack%d_%d %s [%s] %s / %s", strings.ToUpper(result.albumEntry.Type[0:1])+result.albumEntry.Type[1:], result.albumEntry.Id, result.discIndex, cddb1.String(), result.albumEntry.CatalogNumber, group, result.albumEntry.MainTitle)
|
||||
} else {
|
||||
out = fmt.Sprintf("%sSoundtrack%d_%d %s %s / %s", strings.ToUpper(result.albumEntry.Type[0:1])+result.albumEntry.Type[1:], result.albumEntry.Id, result.discIndex, cddb1.String(), group, result.albumEntry.MainTitle)
|
||||
}
|
||||
|
||||
if len(result.albumEntry.Discs) > 1 {
|
||||
out += fmt.Sprintf(" (Disc %d)", result.discIndex+1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
cddb1 := utilities.NewCDDB1FromString(splits[2])
|
||||
if cddb1 == 0 {
|
||||
writer.Write([]byte("500 Command syntax error\n.\n"))
|
||||
|
@ -297,20 +676,12 @@ func main() {
|
|||
writer.Write([]byte("202 No match found\n.\n"))
|
||||
return
|
||||
} else if len(entries) == 1 {
|
||||
if len(entries[0].CatalogNumber) > 0 {
|
||||
writer.Write([]byte(fmt.Sprintf("200 Soundtrack%d %s [%s] %s / %s\n", entries[0].Id, cddb1.String(), entries[0].CatalogNumber, entries[0].Group, entries[0].MainTitle)))
|
||||
} else {
|
||||
writer.Write([]byte(fmt.Sprintf("200 Soundtrack%d %s %s / %s\n", entries[0].Id, cddb1.String(), entries[0].Group, entries[0].MainTitle)))
|
||||
}
|
||||
writer.Write([]byte(fmt.Sprintf("200 %s\n", formatEntry(cddb1, entries[0]))))
|
||||
return
|
||||
} else {
|
||||
writer.Write([]byte("211 Found inexact matches list follows (until terminating marker `.')\n"))
|
||||
for _, e := range entries {
|
||||
if len(e.CatalogNumber) > 0 {
|
||||
writer.Write([]byte(fmt.Sprintf("Soundtrack%d %s [%s] %s / %s\n", e.Id, cddb1.String(), e.CatalogNumber, e.Group, e.MainTitle)))
|
||||
} else {
|
||||
writer.Write([]byte(fmt.Sprintf("Soundtrack%d %s %s / %s\n", e.Id, cddb1.String(), e.Group, e.MainTitle)))
|
||||
}
|
||||
writer.Write([]byte(fmt.Sprintf("%s\n", formatEntry(cddb1, e))))
|
||||
}
|
||||
writer.Write([]byte(".\n"))
|
||||
return
|
||||
|
@ -325,39 +696,83 @@ func main() {
|
|||
writer.Write([]byte("500 Command syntax error\n.\n"))
|
||||
return
|
||||
}
|
||||
pageid, err := strconv.Atoi(strings.ReplaceAll(splits[2], "Soundtrack", ""))
|
||||
firstDigit := strings.IndexFunc(splits[2], unicode.IsNumber)
|
||||
if firstDigit == -1 {
|
||||
writer.Write([]byte("500 Command syntax error\n.\n"))
|
||||
return
|
||||
}
|
||||
|
||||
params := strings.Split(splits[2][firstDigit:], "_")
|
||||
pageid, err := strconv.Atoi(params[0])
|
||||
var discIndex int
|
||||
if err != nil {
|
||||
writer.Write([]byte("500 Command syntax error\n.\n"))
|
||||
return
|
||||
}
|
||||
entry, ok := arrangeCDIndex[pageid]
|
||||
if !ok {
|
||||
|
||||
if len(params) > 1 {
|
||||
discIndex, err = strconv.Atoi(params[1])
|
||||
if err != nil {
|
||||
writer.Write([]byte("500 Command syntax error\n.\n"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
entry, ok := cdIndex[pageid]
|
||||
if !ok || len(entry.Discs) <= discIndex {
|
||||
writer.Write([]byte("401 Entry not found\n.\n"))
|
||||
return
|
||||
}
|
||||
|
||||
writer.Write([]byte(fmt.Sprintf("210 Soundtrack %s\n", cddb1.String())))
|
||||
var group string
|
||||
for _, a := range entry.Artists {
|
||||
if a.Position == "group" {
|
||||
group = a.Names[0]
|
||||
break
|
||||
}
|
||||
}
|
||||
writer.Write([]byte(fmt.Sprintf("210 %sSoundtrack %s\n", strings.ToUpper(entry.Type[0:1])+entry.Type[1:], cddb1.String())))
|
||||
writer.Write([]byte("# xmcd\n# Track frame offsets:\n"))
|
||||
for i := 0; i < entry.TrackCount; i++ {
|
||||
for i := 0; i < entry.Discs[discIndex].TrackCount; i++ {
|
||||
writer.Write([]byte("#\t0\n"))
|
||||
}
|
||||
writer.Write([]byte(fmt.Sprintf("# Disc length: %d seconds\n#\n", entry.Duration)))
|
||||
writer.Write([]byte(fmt.Sprintf("DISCID=%s\n", cddb1.String())))
|
||||
writer.Write([]byte(fmt.Sprintf("DNUM=%d\n", len(entry.Discs))))
|
||||
writer.Write([]byte(fmt.Sprintf("DINDEX=%d\n", discIndex+1)))
|
||||
if len(entry.CatalogNumber) > 0 {
|
||||
writer.Write([]byte(fmt.Sprintf("DTITLE=%s / [%s] %s\n", entry.Group, entry.CatalogNumber, entry.MainTitle)))
|
||||
writer.Write([]byte(fmt.Sprintf("DTITLE=%s / [%s] %s\n", group, entry.CatalogNumber, entry.MainTitle)))
|
||||
} else {
|
||||
writer.Write([]byte(fmt.Sprintf("DTITLE=%s / %s\n", entry.Group, entry.MainTitle)))
|
||||
writer.Write([]byte(fmt.Sprintf("DTITLE=%s / %s\n", group, entry.MainTitle)))
|
||||
}
|
||||
if entry.Year > 0 {
|
||||
writer.Write([]byte(fmt.Sprintf("DYEAR=%d\nDGENRE=Soundtrack\n", entry.Year)))
|
||||
if !entry.ReleaseDate.IsZero() {
|
||||
writer.Write([]byte(fmt.Sprintf("DYEAR=%d\nDGENRE=Soundtrack\n", entry.ReleaseDate.Year())))
|
||||
}
|
||||
for i := 0; i < entry.TrackCount; i++ {
|
||||
writer.Write([]byte(fmt.Sprintf("TTITLE%d=\n", i)))
|
||||
|
||||
for i, t := range entry.Discs[discIndex].Tracks {
|
||||
writer.Write([]byte(fmt.Sprintf("TTITLE%d=%s\n", i, t.MainTitle)))
|
||||
}
|
||||
|
||||
writer.Write([]byte(fmt.Sprintf("EXTD=https://en.touhouwiki.net/index.php?curid=%d\n", entry.Id)))
|
||||
for i := 0; i < entry.TrackCount; i++ {
|
||||
writer.Write([]byte(fmt.Sprintf("EXTT%d=\n", i)))
|
||||
|
||||
for i, t := range entry.Discs[discIndex].Tracks {
|
||||
result := make(map[string][]string)
|
||||
for _, a := range t.Artists {
|
||||
if _, ok := result[a.Position]; !ok {
|
||||
result[a.Position] = []string{}
|
||||
}
|
||||
result[a.Position] = append(result[a.Position], strings.Join(a.Names, "/"))
|
||||
}
|
||||
var resultLines []string
|
||||
for k, v := range result {
|
||||
resultLines = append(resultLines, k+": "+strings.Join(v, ", "))
|
||||
}
|
||||
sort.SliceStable(resultLines, func(i, j int) bool {
|
||||
return resultLines[i] < resultLines[j]
|
||||
})
|
||||
writer.Write([]byte(fmt.Sprintf("EXTT%d=%s\n", i, strings.Join(resultLines, "\\n"))))
|
||||
}
|
||||
|
||||
writer.Write([]byte("PLAYORDER=\n.\n"))
|
||||
return
|
||||
}
|
||||
|
@ -367,8 +782,8 @@ func main() {
|
|||
writer.Header().Set("Content-Type", "application/json")
|
||||
|
||||
switch request.URL.Query().Get("type") {
|
||||
case "title": //search by title or catalog number
|
||||
entries, ok := titleLookup[normalizeTitle(request.URL.Query().Get("query"))]
|
||||
case "album": //search by title or catalog number
|
||||
entries, ok := albumTitleLookup[normalizeTitle(request.URL.Query().Get("query"))]
|
||||
|
||||
if !ok {
|
||||
writer.Write([]byte("[]"))
|
||||
|
|
10
wikiparser/list.go
Normal file
10
wikiparser/list.go
Normal file
|
@ -0,0 +1,10 @@
|
|||
package wikiparser
|
||||
|
||||
type DescriptionList struct {
|
||||
Name []interface{}
|
||||
Entries []interface{}
|
||||
}
|
||||
|
||||
type UnorderedList struct {
|
||||
Entries []interface{}
|
||||
}
|
|
@ -6,25 +6,43 @@ func NormalizeWikiTitle(title string) string {
|
|||
return strings.Replace(title, " ", "_", -1)
|
||||
}
|
||||
|
||||
type Link struct {
|
||||
URL string
|
||||
IsExternal bool
|
||||
Name []interface{}
|
||||
}
|
||||
type NewLineToken struct {
|
||||
}
|
||||
|
||||
//ParseWikiText small WikiText parser that extracts text, Templates, and its arguments/parameters
|
||||
func ParseWikiText(text string) (result []interface{}) {
|
||||
index := 0
|
||||
|
||||
for index < len(text) {
|
||||
templateIndex := strings.Index(text[index:], "{{")
|
||||
if templateIndex == -1 {
|
||||
linkIndex := strings.Index(text[index:], "[[")
|
||||
if templateIndex == -1 && linkIndex == -1 {
|
||||
t := strings.TrimSpace(text[index:])
|
||||
if len(t) > 0 {
|
||||
result = append(result, t)
|
||||
}
|
||||
break
|
||||
} else {
|
||||
t := strings.TrimSpace(text[index : index+templateIndex])
|
||||
bestIndex := templateIndex
|
||||
if templateIndex == -1 {
|
||||
bestIndex = linkIndex
|
||||
} else {
|
||||
if linkIndex != -1 && linkIndex < bestIndex {
|
||||
bestIndex = linkIndex
|
||||
}
|
||||
}
|
||||
|
||||
t := strings.TrimSpace(text[index : index+bestIndex])
|
||||
if len(t) > 0 {
|
||||
result = append(result, t)
|
||||
}
|
||||
var tpl *Template
|
||||
index, tpl = ParseTemplate(text, index+templateIndex+2, 0)
|
||||
index, tpl = ParseTemplate(text, index+bestIndex+2, 0, text[index+bestIndex])
|
||||
if tpl != nil {
|
||||
result = append(result, tpl)
|
||||
}
|
||||
|
@ -34,7 +52,64 @@ func ParseWikiText(text string) (result []interface{}) {
|
|||
return
|
||||
}
|
||||
|
||||
func ParseTemplate(text string, index int, depth int) (i int, template *Template) {
|
||||
func ParseLink(text string, index int, depth int, startCharacter byte) (i int, link *Link) {
|
||||
|
||||
var c byte
|
||||
lastToken := index
|
||||
|
||||
addValue := func() int {
|
||||
if lastToken < len(text) && i-lastToken > 0 {
|
||||
t := strings.TrimSpace(text[lastToken:i])
|
||||
if len(t) > 0 {
|
||||
if link == nil {
|
||||
link = &Link{URL: t, IsExternal: startCharacter == '{'}
|
||||
} else {
|
||||
link.Name = append(link.Name, t)
|
||||
}
|
||||
}
|
||||
|
||||
return len(t)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
for i = index; i < len(text); i++ {
|
||||
c = text[i]
|
||||
|
||||
if c == ' ' || c == '\t' && link == nil {
|
||||
addValue()
|
||||
lastToken = i + 1
|
||||
} else if startCharacter == '{' && c == '}' {
|
||||
addValue()
|
||||
i += 1
|
||||
break
|
||||
} else if startCharacter == '[' && c == ']' { //end of link
|
||||
addValue()
|
||||
i += 1
|
||||
break
|
||||
//template or light might have parameters
|
||||
} else if (c == '{' && i < len(text)-1 && text[i+1] == '{') || (c == '[' && i < len(text)-1 && text[i+1] == '[') {
|
||||
addValue()
|
||||
var tpl *Template
|
||||
var scanIndex int
|
||||
scanIndex, tpl = ParseTemplate(text, i+2, depth+1, c)
|
||||
if tpl != nil {
|
||||
if link == nil {
|
||||
link = &Link{}
|
||||
}
|
||||
|
||||
link.Name = append(link.Name, tpl)
|
||||
}
|
||||
lastToken = scanIndex
|
||||
i = scanIndex - 1
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func ParseTemplate(text string, index int, depth int, startCharacter byte) (i int, template *Template) {
|
||||
|
||||
var c byte
|
||||
lastToken := index
|
||||
|
@ -46,7 +121,7 @@ func ParseTemplate(text string, index int, depth int) (i int, template *Template
|
|||
t := strings.TrimSpace(text[lastToken:i])
|
||||
if len(t) > 0 {
|
||||
if template == nil {
|
||||
template = NewTemplate(t)
|
||||
template = NewTemplate(t, startCharacter == '[')
|
||||
} else {
|
||||
if key == "" {
|
||||
template.AddParameterUnkeyed(t)
|
||||
|
@ -69,17 +144,26 @@ func ParseTemplate(text string, index int, depth int) (i int, template *Template
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
afterNewLine := false
|
||||
|
||||
for i = index; i < len(text); i++ {
|
||||
c = text[i]
|
||||
|
||||
if c == '}' && i < len(text)-1 && text[i+1] == '}' {
|
||||
if startCharacter == '{' && c == '}' && i < len(text)-1 && text[i+1] == '}' { //end of template
|
||||
addValue()
|
||||
i += 2
|
||||
break
|
||||
} else if c == '{' && i < len(text)-1 && text[i+1] == '{' {
|
||||
} else if startCharacter == '[' && c == ']' && i < len(text)-1 && text[i+1] == ']' { //end of link
|
||||
addValue()
|
||||
i += 2
|
||||
break
|
||||
//template or light might have parameters
|
||||
} else if (c == '{' && i < len(text)-1 && text[i+1] == '{') || (c == '[' && i < len(text)-1 && text[i+1] == '[') {
|
||||
addValue()
|
||||
var tpl *Template
|
||||
i, tpl = ParseTemplate(text, i+2, depth+1)
|
||||
var scanIndex int
|
||||
scanIndex, tpl = ParseTemplate(text, i+2, depth+1, c)
|
||||
if tpl != nil {
|
||||
if key == "" {
|
||||
template.AddParameterUnkeyed(tpl)
|
||||
|
@ -87,7 +171,22 @@ func ParseTemplate(text string, index int, depth int) (i int, template *Template
|
|||
template.AddParameter(key, tpl)
|
||||
}
|
||||
}
|
||||
lastToken = i
|
||||
lastToken = scanIndex
|
||||
i = scanIndex - 1
|
||||
} else if (c == '{' && i < len(text)-1 && text[i+1] != '{' && text[i+1] != '[') || (c == '[' && i < len(text)-1 && text[i+1] != '[' && text[i+1] != '{') {
|
||||
addValue()
|
||||
var link *Link
|
||||
var scanIndex int
|
||||
scanIndex, link = ParseLink(text, i+1, depth+1, c)
|
||||
if link != nil && template != nil {
|
||||
if key == "" {
|
||||
template.AddParameterUnkeyed(link)
|
||||
} else {
|
||||
template.AddParameter(key, link)
|
||||
}
|
||||
}
|
||||
lastToken = scanIndex
|
||||
i = scanIndex - 1
|
||||
} else if c == '|' {
|
||||
addValue()
|
||||
lastToken = i + 1
|
||||
|
@ -95,12 +194,239 @@ func ParseTemplate(text string, index int, depth int) (i int, template *Template
|
|||
} else if c == '\n' {
|
||||
addValue()
|
||||
lastToken = i + 1
|
||||
afterNewLine = true
|
||||
|
||||
if template != nil {
|
||||
if key == "" {
|
||||
template.AddParameterUnkeyed(NewLineToken{})
|
||||
} else {
|
||||
template.AddParameter(key, NewLineToken{})
|
||||
}
|
||||
}
|
||||
} else if afterNewLine && (c == '*' || c == '#') {
|
||||
addValue()
|
||||
var list *UnorderedList
|
||||
var scanIndex int
|
||||
scanIndex, list = ParseUnorderedList(text, i, depth+1, 1, c)
|
||||
if list != nil {
|
||||
if key == "" {
|
||||
template.AddParameterUnkeyed(list)
|
||||
} else {
|
||||
template.AddParameter(key, list)
|
||||
}
|
||||
}
|
||||
lastToken = scanIndex
|
||||
i = scanIndex - 1
|
||||
} else if c == ';' {
|
||||
addValue()
|
||||
var list *DescriptionList
|
||||
var scanIndex int
|
||||
scanIndex, list = ParseDescriptionList(text, i+1, depth+1)
|
||||
if list != nil {
|
||||
if key == "" {
|
||||
template.AddParameterUnkeyed(list)
|
||||
} else {
|
||||
template.AddParameter(key, list)
|
||||
}
|
||||
}
|
||||
lastToken = scanIndex
|
||||
i = scanIndex - 1
|
||||
} else if afterNewLine && c == ':' {
|
||||
addValue()
|
||||
lastToken = i + 1
|
||||
} else if c == '=' {
|
||||
if key == "" {
|
||||
addKey()
|
||||
lastToken = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
if afterNewLine && c != '\n' && c != ' ' && c != '\t' {
|
||||
afterNewLine = false
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func ParseUnorderedList(text string, index int, depth int, indent int, startCharacter byte) (i int, list *UnorderedList) {
|
||||
|
||||
list = &UnorderedList{}
|
||||
var c byte
|
||||
lastToken := index
|
||||
|
||||
var currentValue []interface{}
|
||||
|
||||
addValue := func() int {
|
||||
if lastToken < len(text) && i-lastToken > 0 {
|
||||
t := strings.TrimSpace(text[lastToken:i])
|
||||
if len(t) > 0 {
|
||||
currentValue = append(currentValue, t)
|
||||
}
|
||||
|
||||
return len(t)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
afterNewLine := true
|
||||
processIndent := true
|
||||
|
||||
indentation := 0
|
||||
|
||||
for i = index; i < len(text); i++ {
|
||||
c = text[i]
|
||||
|
||||
if c == ' ' || c == '\t' {
|
||||
//keep the check for new line
|
||||
if !afterNewLine {
|
||||
processIndent = false
|
||||
}
|
||||
} else if processIndent && c == startCharacter {
|
||||
indentation++
|
||||
lastToken = i + 1
|
||||
afterNewLine = false
|
||||
} else if afterNewLine { //no new list values
|
||||
if len(currentValue) > 0 {
|
||||
list.Entries = append(list.Entries, currentValue)
|
||||
currentValue = []interface{}{}
|
||||
}
|
||||
return lastToken, list
|
||||
} else if indentation > indent {
|
||||
if len(currentValue) > 0 {
|
||||
list.Entries = append(list.Entries, currentValue)
|
||||
currentValue = []interface{}{}
|
||||
}
|
||||
var level *UnorderedList
|
||||
var scanIndex int
|
||||
scanIndex, level = ParseUnorderedList(text, lastToken-indentation, depth+1, indentation, startCharacter)
|
||||
if level != nil {
|
||||
list.Entries = append(list.Entries, level)
|
||||
}
|
||||
lastToken = scanIndex
|
||||
i = scanIndex - 1
|
||||
indentation = 0
|
||||
afterNewLine = true
|
||||
processIndent = true
|
||||
} else if indentation < indent {
|
||||
if len(currentValue) > 0 {
|
||||
list.Entries = append(list.Entries, currentValue)
|
||||
currentValue = []interface{}{}
|
||||
}
|
||||
return lastToken - indentation, list
|
||||
} else if c == '\n' {
|
||||
addValue()
|
||||
if len(currentValue) > 0 {
|
||||
list.Entries = append(list.Entries, currentValue)
|
||||
currentValue = []interface{}{}
|
||||
}
|
||||
indentation = 0
|
||||
lastToken = i + 1
|
||||
afterNewLine = true
|
||||
processIndent = true
|
||||
} else if (c == '{' && i < len(text)-1 && text[i+1] == '{') || (c == '[' && i < len(text)-1 && text[i+1] == '[') {
|
||||
addValue()
|
||||
var tpl *Template
|
||||
var scanIndex int
|
||||
scanIndex, tpl = ParseTemplate(text, i+2, depth+1, c)
|
||||
if tpl != nil {
|
||||
currentValue = append(currentValue, tpl)
|
||||
}
|
||||
lastToken = scanIndex
|
||||
i = scanIndex - 1
|
||||
} else if (c == '{' && i < len(text)-1 && text[i+1] != '{' && text[i+1] != '[') || (c == '[' && i < len(text)-1 && text[i+1] != '[' && text[i+1] != '{') {
|
||||
addValue()
|
||||
var link *Link
|
||||
var scanIndex int
|
||||
scanIndex, link = ParseLink(text, i+1, depth+1, c)
|
||||
if link != nil {
|
||||
currentValue = append(currentValue, link)
|
||||
}
|
||||
lastToken = scanIndex
|
||||
i = scanIndex - 1
|
||||
} else {
|
||||
processIndent = false
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func ParseDescriptionList(text string, index int, depth int) (i int, list *DescriptionList) {
|
||||
|
||||
var c byte
|
||||
lastToken := index
|
||||
|
||||
list = &DescriptionList{}
|
||||
|
||||
hasKey := false
|
||||
|
||||
addValue := func() int {
|
||||
if lastToken < len(text) && i-lastToken > 0 {
|
||||
t := strings.TrimSpace(text[lastToken:i])
|
||||
if len(t) > 0 {
|
||||
if !hasKey {
|
||||
list.Name = append(list.Name, t)
|
||||
} else {
|
||||
list.Entries = append(list.Entries, t)
|
||||
}
|
||||
}
|
||||
|
||||
return len(t)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
afterNewLine := false
|
||||
|
||||
for i = index; i < len(text); i++ {
|
||||
c = text[i]
|
||||
|
||||
if c == ' ' || c == '\t' {
|
||||
//keep the check for new line
|
||||
} else if c == ':' {
|
||||
addValue()
|
||||
lastToken = i + 1
|
||||
afterNewLine = false
|
||||
hasKey = true
|
||||
} else if afterNewLine { //no new list values
|
||||
return lastToken, list
|
||||
} else if c == '\n' {
|
||||
addValue()
|
||||
lastToken = i + 1
|
||||
afterNewLine = true
|
||||
hasKey = true
|
||||
} else if (c == '{' && i < len(text)-1 && text[i+1] == '{') || (c == '[' && i < len(text)-1 && text[i+1] == '[') {
|
||||
addValue()
|
||||
var tpl *Template
|
||||
var scanIndex int
|
||||
scanIndex, tpl = ParseTemplate(text, i+2, depth+1, c)
|
||||
if tpl != nil {
|
||||
if !hasKey {
|
||||
list.Name = append(list.Name, tpl)
|
||||
} else {
|
||||
list.Entries = append(list.Entries, tpl)
|
||||
}
|
||||
}
|
||||
lastToken = scanIndex
|
||||
i = scanIndex - 1
|
||||
} else if (c == '{' && i < len(text)-1 && text[i+1] != '{' && text[i+1] != '[') || (c == '[' && i < len(text)-1 && text[i+1] != '[' && text[i+1] != '{') {
|
||||
addValue()
|
||||
var link *Link
|
||||
var scanIndex int
|
||||
scanIndex, link = ParseLink(text, i+1, depth+1, c)
|
||||
if link != nil {
|
||||
if !hasKey {
|
||||
list.Name = append(list.Name, link)
|
||||
} else {
|
||||
list.Entries = append(list.Entries, link)
|
||||
}
|
||||
}
|
||||
lastToken = scanIndex
|
||||
i = scanIndex - 1
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
|
|
|
@ -4,12 +4,14 @@ import "fmt"
|
|||
|
||||
type Template struct {
|
||||
Name string
|
||||
IsLink bool
|
||||
Parameters map[string][]interface{}
|
||||
}
|
||||
|
||||
func NewTemplate(name string) *Template {
|
||||
func NewTemplate(name string, isLink bool) *Template {
|
||||
return &Template{
|
||||
Name: name,
|
||||
IsLink: isLink,
|
||||
Parameters: make(map[string][]interface{}),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue