Files
MeBox/internal/service/strm_tree_parse.go
T
2026-06-30 20:18:02 +08:00

166 lines
4.4 KiB
Go

package service
import (
"path"
"strings"
)
func parseSTRMTreeText(raw string) []string {
var out []string
stack := make([]string, 0, 8)
plainIndents := make([]int, 0, 8)
rootOffset := 0
for _, line := range strings.Split(raw, "\n") {
line = strings.TrimRight(line, " \t\r")
if strings.TrimSpace(line) == "" {
continue
}
if item, level, ok := parseSTRMTreeLine(line); ok {
plainIndents = plainIndents[:0]
level += rootOffset
stack = stack[:min(level, len(stack))]
if strmTreeItemIsVideoSource(item) {
out = append(out, strmTreeJoinedSource(stack, item))
continue
}
stack = append(stack, item)
continue
}
if item, level, ok := parseSTRMTreeContinuationLine(line, len(plainIndents) > 0); ok {
plainIndents = plainIndents[:0]
level += rootOffset
stack = stack[:min(level, len(stack))]
out = append(out, path.Join(append(append([]string{}, stack...), item)...))
continue
}
item := cleanSTRMTreeItemName(line)
if indent := strmTreePlainIndent(line); indent > 0 && !strings.ContainsAny(item, `/\`) {
for len(plainIndents) > 0 && indent <= plainIndents[len(plainIndents)-1] {
plainIndents = plainIndents[:len(plainIndents)-1]
}
level := rootOffset + len(plainIndents)
stack = stack[:min(level, len(stack))]
if strmTreeItemIsVideoSource(item) {
out = append(out, strmTreeJoinedSource(stack, item))
continue
}
stack = append(stack, item)
plainIndents = append(plainIndents, indent)
continue
}
if strmTreeItemIsVideoSource(item) || strings.ContainsAny(item, `/\`) {
plainIndents = plainIndents[:0]
out = append(out, item)
continue
}
stack = []string{item}
plainIndents = plainIndents[:0]
rootOffset = 1
}
return out
}
func parseSTRMTreeLine(line string) (string, int, bool) {
if idx := strings.Index(line, "──"); idx >= 0 {
prefix := line[:idx]
level := strmTreeIndentLevelWithWidth(prefix, 4)
item := cleanSTRMTreeItemName(strings.Trim(strings.TrimSpace(line[idx+len("──"):]), "─- "))
return item, level, item != ""
}
if idx := strings.Index(line, "─"); idx >= 0 {
prefix := line[:idx]
level := strmTreeIndentLevelWithWidth(prefix, 3)
item := cleanSTRMTreeItemName(strings.Trim(strings.TrimSpace(line[idx+len("─"):]), "─- "))
return item, level, item != ""
}
for _, marker := range []string{"|--", "+--", "`--"} {
if idx := strings.Index(line, marker); idx >= 0 {
item := cleanSTRMTreeItemName(line[idx+len(marker):])
return item, strmTreeIndentLevel(line[:idx]), item != ""
}
}
return "", 0, false
}
func parseSTRMTreeContinuationLine(line string, plainTreeActive bool) (string, int, bool) {
prefixLen := 0
hasGuide := false
for _, r := range line {
switch r {
case ' ', '\t', '│', '|':
if r == '│' || r == '|' {
hasGuide = true
}
prefixLen += len(string(r))
default:
if plainTreeActive && !hasGuide {
return "", 0, false
}
item := cleanSTRMTreeItemName(line[prefixLen:])
if item == "" || !strmTreeItemIsVideoSource(item) {
return "", 0, false
}
return item, strmTreeIndentLevel(line[:prefixLen]), true
}
}
return "", 0, false
}
func strmTreeIndentLevel(prefix string) int {
return strmTreeIndentLevelWithWidth(prefix, 4)
}
func strmTreeIndentLevelWithWidth(prefix string, width int) int {
if prefix == "" {
return 0
}
if width <= 0 {
width = 4
}
verticals := strings.Count(prefix, "│") + strings.Count(prefix, "|")
runeLen := len([]rune(strings.ReplaceAll(strings.ReplaceAll(prefix, "│", " "), "|", " ")))
byWidth := 0
if runeLen > 0 {
byWidth = (runeLen - 1) / width
}
if verticals > byWidth {
return verticals
}
return byWidth
}
func strmTreePlainIndent(line string) int {
indent := 0
for _, r := range line {
switch r {
case ' ':
indent++
case '\t':
indent += 4
default:
return indent
}
}
return indent
}
func strmTreeItemIsVideoSource(item string) bool {
if strmTreeSourceIsVideo(item) {
return true
}
source := normalizeSTRMTreeSourceWithProvider(item, "openlist")
return source.Path != "" && strmTreeSourceIsVideo(source.Path)
}
func strmTreeJoinedSource(stack []string, item string) string {
if strmTreeItemIsAbsoluteSource(item) {
return item
}
return path.Join(append(append([]string{}, stack...), item)...)
}
func strmTreeItemIsAbsoluteSource(item string) bool {
value := strings.ToLower(strings.TrimSpace(item))
return strings.Contains(value, "://") || strings.HasPrefix(value, "/api/")
}