fuxxy serch + rebuild script
This commit is contained in:
parent
6625e5927e
commit
27ce5a4a92
3 changed files with 73 additions and 5 deletions
14
deploy.sh
Executable file
14
deploy.sh
Executable 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."
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue