Initial commit
This commit is contained in:
116
backend/internal/services/modlist_parser.go
Normal file
116
backend/internal/services/modlist_parser.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"arma3-web-server/internal/models"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
func ParseModlistHTML(r io.Reader) (string, []models.ModEntry, error) {
|
||||
doc, err := html.Parse(r)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("parse html: %w", err)
|
||||
}
|
||||
|
||||
var mods []models.ModEntry
|
||||
seen := map[string]bool{}
|
||||
|
||||
var walk func(*html.Node)
|
||||
walk = func(n *html.Node) {
|
||||
if n.Type == html.ElementNode && n.Data == "tr" {
|
||||
if name, id, ok := extractModRow(n); ok && id != "" {
|
||||
if !seen[id] {
|
||||
seen[id] = true
|
||||
mods = append(mods, models.ModEntry{
|
||||
ID: id,
|
||||
Name: cleanModName(name),
|
||||
Enabled: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
walk(c)
|
||||
}
|
||||
}
|
||||
walk(doc)
|
||||
|
||||
if mods == nil {
|
||||
mods = []models.ModEntry{}
|
||||
}
|
||||
return "", mods, nil
|
||||
}
|
||||
|
||||
func extractModRow(tr *html.Node) (name, id string, ok bool) {
|
||||
var cells []*html.Node
|
||||
for c := tr.FirstChild; c != nil; c = c.NextSibling {
|
||||
if c.Type == html.ElementNode && c.Data == "td" {
|
||||
cells = append(cells, c)
|
||||
}
|
||||
}
|
||||
if len(cells) < 3 {
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
name = extractText(cells[0])
|
||||
|
||||
for c := cells[2].FirstChild; c != nil; c = c.NextSibling {
|
||||
if c.Type == html.ElementNode && c.Data == "a" {
|
||||
id = extractWorkshopID(c)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if name == "" {
|
||||
return "", "", false
|
||||
}
|
||||
return name, id, true
|
||||
}
|
||||
|
||||
func extractText(n *html.Node) string {
|
||||
var b strings.Builder
|
||||
var walk func(*html.Node)
|
||||
walk = func(n *html.Node) {
|
||||
if n.Type == html.TextNode {
|
||||
b.WriteString(n.Data)
|
||||
}
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
walk(c)
|
||||
}
|
||||
}
|
||||
walk(n)
|
||||
return strings.TrimSpace(b.String())
|
||||
}
|
||||
|
||||
func extractWorkshopID(a *html.Node) string {
|
||||
for _, attr := range a.Attr {
|
||||
if attr.Key == "href" {
|
||||
u, err := url.Parse(attr.Val)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
return u.Query().Get("id")
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func cleanModName(name string) string {
|
||||
name = strings.TrimSpace(name)
|
||||
name = strings.ReplaceAll(name, "/", "_")
|
||||
name = strings.ReplaceAll(name, "\\", "_")
|
||||
return name
|
||||
}
|
||||
|
||||
func FileNameToModlistName(filename string) string {
|
||||
ext := path.Ext(filename)
|
||||
name := strings.TrimSuffix(filename, ext)
|
||||
name = strings.ReplaceAll(name, "_", " ")
|
||||
name = strings.ReplaceAll(name, "-", " ")
|
||||
return name
|
||||
}
|
||||
Reference in New Issue
Block a user