mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-05-30 03:19:28 +02:00
152 lines
3.7 KiB
Go
152 lines
3.7 KiB
Go
// Package gobackend provides Track Matching API for extension runtime
|
|
package gobackend
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/dop251/goja"
|
|
)
|
|
|
|
// ==================== Track Matching API ====================
|
|
|
|
// matchingCompareStrings compares two strings with fuzzy matching
|
|
func (r *ExtensionRuntime) matchingCompareStrings(call goja.FunctionCall) goja.Value {
|
|
if len(call.Arguments) < 2 {
|
|
return r.vm.ToValue(0.0)
|
|
}
|
|
|
|
str1 := strings.ToLower(strings.TrimSpace(call.Arguments[0].String()))
|
|
str2 := strings.ToLower(strings.TrimSpace(call.Arguments[1].String()))
|
|
|
|
if str1 == str2 {
|
|
return r.vm.ToValue(1.0)
|
|
}
|
|
|
|
// Calculate Levenshtein distance-based similarity
|
|
similarity := calculateStringSimilarity(str1, str2)
|
|
return r.vm.ToValue(similarity)
|
|
}
|
|
|
|
// matchingCompareDuration compares two durations with tolerance
|
|
func (r *ExtensionRuntime) matchingCompareDuration(call goja.FunctionCall) goja.Value {
|
|
if len(call.Arguments) < 2 {
|
|
return r.vm.ToValue(false)
|
|
}
|
|
|
|
dur1 := int(call.Arguments[0].ToInteger())
|
|
dur2 := int(call.Arguments[1].ToInteger())
|
|
|
|
// Default tolerance: 3 seconds
|
|
tolerance := 3000 // milliseconds
|
|
if len(call.Arguments) > 2 && !goja.IsUndefined(call.Arguments[2]) {
|
|
tolerance = int(call.Arguments[2].ToInteger())
|
|
}
|
|
|
|
diff := dur1 - dur2
|
|
if diff < 0 {
|
|
diff = -diff
|
|
}
|
|
|
|
return r.vm.ToValue(diff <= tolerance)
|
|
}
|
|
|
|
// matchingNormalizeString normalizes a string for comparison
|
|
func (r *ExtensionRuntime) matchingNormalizeString(call goja.FunctionCall) goja.Value {
|
|
if len(call.Arguments) < 1 {
|
|
return r.vm.ToValue("")
|
|
}
|
|
|
|
str := call.Arguments[0].String()
|
|
normalized := normalizeStringForMatching(str)
|
|
return r.vm.ToValue(normalized)
|
|
}
|
|
|
|
// calculateStringSimilarity calculates similarity between two strings (0-1)
|
|
func calculateStringSimilarity(s1, s2 string) float64 {
|
|
if len(s1) == 0 && len(s2) == 0 {
|
|
return 1.0
|
|
}
|
|
if len(s1) == 0 || len(s2) == 0 {
|
|
return 0.0
|
|
}
|
|
|
|
// Use Levenshtein distance
|
|
distance := levenshteinDistance(s1, s2)
|
|
maxLen := len(s1)
|
|
if len(s2) > maxLen {
|
|
maxLen = len(s2)
|
|
}
|
|
|
|
return 1.0 - float64(distance)/float64(maxLen)
|
|
}
|
|
|
|
// levenshteinDistance calculates the Levenshtein distance between two strings
|
|
func levenshteinDistance(s1, s2 string) int {
|
|
if len(s1) == 0 {
|
|
return len(s2)
|
|
}
|
|
if len(s2) == 0 {
|
|
return len(s1)
|
|
}
|
|
|
|
// Create matrix
|
|
matrix := make([][]int, len(s1)+1)
|
|
for i := range matrix {
|
|
matrix[i] = make([]int, len(s2)+1)
|
|
matrix[i][0] = i
|
|
}
|
|
for j := range matrix[0] {
|
|
matrix[0][j] = j
|
|
}
|
|
|
|
// Fill matrix
|
|
for i := 1; i <= len(s1); i++ {
|
|
for j := 1; j <= len(s2); j++ {
|
|
cost := 1
|
|
if s1[i-1] == s2[j-1] {
|
|
cost = 0
|
|
}
|
|
matrix[i][j] = min(
|
|
matrix[i-1][j]+1, // deletion
|
|
matrix[i][j-1]+1, // insertion
|
|
matrix[i-1][j-1]+cost, // substitution
|
|
)
|
|
}
|
|
}
|
|
|
|
return matrix[len(s1)][len(s2)]
|
|
}
|
|
|
|
// normalizeStringForMatching normalizes a string for comparison
|
|
func normalizeStringForMatching(s string) string {
|
|
// Convert to lowercase
|
|
s = strings.ToLower(s)
|
|
|
|
// Remove common suffixes/prefixes
|
|
suffixes := []string{
|
|
" (remastered)", " (remaster)", " - remastered", " - remaster",
|
|
" (deluxe)", " (deluxe edition)", " - deluxe", " - deluxe edition",
|
|
" (explicit)", " (clean)", " [explicit]", " [clean]",
|
|
" (album version)", " (single version)", " (radio edit)",
|
|
" (feat.", " (ft.", " feat.", " ft.",
|
|
}
|
|
for _, suffix := range suffixes {
|
|
if idx := strings.Index(s, suffix); idx != -1 {
|
|
s = s[:idx]
|
|
}
|
|
}
|
|
|
|
// Remove special characters
|
|
var result strings.Builder
|
|
for _, r := range s {
|
|
if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == ' ' {
|
|
result.WriteRune(r)
|
|
}
|
|
}
|
|
|
|
// Collapse multiple spaces
|
|
s = strings.Join(strings.Fields(result.String()), " ")
|
|
|
|
return strings.TrimSpace(s)
|
|
}
|