fuxxy serch + rebuild script

This commit is contained in:
Olive Vaughn 2026-08-26 00:34:45 -04:00
parent 6625e5927e
commit 27ce5a4a92
3 changed files with 73 additions and 5 deletions

14
deploy.sh Executable file
View file

@ -0,0 +1,14 @@
#!/bin/bash
set -e
REMOTE="root@minifridge"
REMOTE_DIR="/mnt/user/appdata/sheepflix"
echo "syncing to $REMOTE:$REMOTE_DIR..."
rsync -avz --exclude '.git' --exclude 'testdata' --exclude 'sheepflix' \
./ "$REMOTE:$REMOTE_DIR/"
echo "rebuilding on remote..."
ssh "$REMOTE" "cd $REMOTE_DIR && docker compose up -d --build"
echo "done."

View file

@ -134,17 +134,67 @@ func (l *Library) Get(id string) (*MediaItem, string, bool) {
return item, item.fullPath, true return item, item.fullPath, true
} }
// Search returns items whose name contains the query (case-insensitive). // Search returns items matching the query, ranked by fuzzy match score.
func (l *Library) Search(query string) []MediaItem { func (l *Library) Search(query string) []MediaItem {
q := strings.ToLower(query) q := strings.ToLower(query)
l.mu.RLock() l.mu.RLock()
defer l.mu.RUnlock() defer l.mu.RUnlock()
var results []MediaItem type scored struct {
item MediaItem
score int
}
var results []scored
for _, item := range l.items { for _, item := range l.items {
if strings.Contains(strings.ToLower(item.Name), q) { name := strings.ToLower(item.Name)
results = append(results, item) if s := fuzzyScore(name, q); s > 0 {
results = append(results, scored{item, s})
} }
} }
return results sort.Slice(results, func(i, j int) bool {
return results[i].score > results[j].score
})
items := make([]MediaItem, len(results))
for i, r := range results {
items[i] = r.item
}
return items
}
// fuzzyScore returns a match score for how well target matches the query.
// Returns 0 if no match. Higher is better.
// Exact substring match gets a big bonus. Otherwise uses subsequence matching
// with bonuses for consecutive chars and word-boundary matches.
func fuzzyScore(target, query string) int {
// exact substring = best possible match
if strings.Contains(target, query) {
return 1000 + len(query)*10
}
// subsequence match with scoring
qi := 0
score := 0
consecutive := 0
for ti := 0; ti < len(target) && qi < len(query); ti++ {
if target[ti] == query[qi] {
score += 10
consecutive++
if consecutive > 1 {
score += consecutive * 5 // bonus for runs
}
// word boundary bonus
if ti == 0 || target[ti-1] == ' ' || target[ti-1] == '.' || target[ti-1] == '_' || target[ti-1] == '-' {
score += 20
}
qi++
} else {
consecutive = 0
}
}
if qi < len(query) {
return 0 // didn't match all query chars
}
return score
} }

View file

@ -45,6 +45,10 @@ func Scan(rootDir string) ([]MediaFile, error) {
return nil return nil
} }
if strings.HasPrefix(d.Name(), "._") {
return nil
}
ext := strings.ToLower(filepath.Ext(d.Name())) ext := strings.ToLower(filepath.Ext(d.Name()))
if !videoExtensions[ext] { if !videoExtensions[ext] {
return nil return nil