Added fuzzy finder to search, made sync check as middleware, .id file is never used
Build / build (push) Successful in 45s
Build / build (push) Successful in 45s
This commit is contained in:
@@ -1,117 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"log"
|
||||
"music-server/internal/backend"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var soundtracks_added []string
|
||||
|
||||
func FindSoundtrackWebHandler(w http.ResponseWriter, r *http.Request) {
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
http.Error(w, "Bad Request", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
search_term := r.FormValue("search_term")
|
||||
|
||||
search(search_term)
|
||||
|
||||
component := FoundSoundtracks(soundtracks_added)
|
||||
err = component.Render(r.Context(), w)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
log.Fatalf("Error rendering in FindSoundtrackWebHandler: %e", err)
|
||||
}
|
||||
}
|
||||
|
||||
func search(searchText string) {
|
||||
soundtracks_added = nil
|
||||
soundtracks := backend.GetAllSoundtracks()
|
||||
for _, soundtrack := range soundtracks {
|
||||
if is_match_exact(searchText, soundtrack) {
|
||||
add_soundtrack(soundtrack)
|
||||
}
|
||||
}
|
||||
for _, soundtrack := range soundtracks {
|
||||
if is_match_contains(clean_term(searchText), clean_term(soundtrack)) {
|
||||
add_soundtrack(soundtrack)
|
||||
}
|
||||
}
|
||||
for _, soundtrack := range soundtracks {
|
||||
if is_match_regex(clean_term(searchText), clean_term(soundtrack)) {
|
||||
add_soundtrack(soundtrack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func is_match_exact(search_term string, soundtrack_name string) bool {
|
||||
search_term = strings.ToLower(search_term)
|
||||
soundtrack_name = strings.ToLower(soundtrack_name)
|
||||
|
||||
if search_term == "" {
|
||||
return true
|
||||
} else if strings.Contains(soundtrack_name, search_term) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func is_match_contains(search_term string, soundtrack_name string) bool {
|
||||
if search_term == "" {
|
||||
return true
|
||||
} else if strings.Contains(soundtrack_name, search_term) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func is_match_regex(search_term string, soundtrack_name string) bool {
|
||||
if search_term == "" {
|
||||
return true
|
||||
} else if compile_regex(search_term).MatchString(soundtrack_name) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func add_soundtrack(soundtrack string) {
|
||||
if !check_if_soundtrack_exists(soundtrack) {
|
||||
soundtracks_added = append(soundtracks_added, soundtrack)
|
||||
}
|
||||
}
|
||||
|
||||
func check_if_soundtrack_exists(soundtrackName string) bool {
|
||||
soundtrack_exists := false
|
||||
for _, child := range soundtracks_added {
|
||||
if child == soundtrackName {
|
||||
soundtrack_exists = true
|
||||
}
|
||||
}
|
||||
return soundtrack_exists
|
||||
}
|
||||
|
||||
func compile_regex(search_term string) *regexp.Regexp {
|
||||
regText := ".*"
|
||||
for _, letter := range search_term {
|
||||
regText += string(letter) + ".*"
|
||||
}
|
||||
r, _ := regexp.Compile(regText)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func clean_term(term string) string {
|
||||
term = strings.ReplaceAll(term, " ", "")
|
||||
term = strings.ReplaceAll(term, "é", "e")
|
||||
term = strings.ReplaceAll(term, "+", "plus")
|
||||
term = strings.ReplaceAll(term, "&", "and")
|
||||
term = strings.ReplaceAll(term, "'n", "and")
|
||||
return strings.ToLower(term)
|
||||
}
|
||||
@@ -0,0 +1,403 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"log"
|
||||
"music-server/internal/backend"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var soundtracks_added []string
|
||||
|
||||
// Precomputed data for optimization
|
||||
type SoundtrackData struct {
|
||||
Original string
|
||||
Cleaned string
|
||||
Abbreviation string
|
||||
}
|
||||
|
||||
var (
|
||||
precomputedSoundtracks []SoundtrackData
|
||||
precomputedOnce sync.Once
|
||||
regexCache = make(map[string]*regexp.Regexp)
|
||||
regexCacheMutex sync.Mutex
|
||||
)
|
||||
|
||||
func FindSoundtrackWebHandler(w http.ResponseWriter, r *http.Request) {
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
http.Error(w, "Bad Request", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
search_term := r.FormValue("search_term")
|
||||
|
||||
search(search_term)
|
||||
|
||||
component := FoundSoundtracks(soundtracks_added)
|
||||
err = component.Render(r.Context(), w)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
log.Fatalf("Error rendering in FindSoundtrackWebHandler: %e", err)
|
||||
}
|
||||
}
|
||||
|
||||
func search(searchText string) {
|
||||
soundtracks_added = nil
|
||||
|
||||
// Precompute cleaned search term once
|
||||
cleanedSearchTerm := clean_term(searchText)
|
||||
|
||||
// Use precomputed data for efficiency
|
||||
soundtrackData := getPrecomputedSoundtracks()
|
||||
seen := make(map[string]bool) // O(1) duplicate checking
|
||||
|
||||
for _, data := range soundtrackData {
|
||||
// Check exact match (case-insensitive contains)
|
||||
if is_match_exact(searchText, data.Original) && !seen[data.Original] {
|
||||
soundtracks_added = append(soundtracks_added, data.Original)
|
||||
seen[data.Original] = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, data := range soundtrackData {
|
||||
// Check contains match with cleaned terms
|
||||
if !seen[data.Original] && is_match_contains(cleanedSearchTerm, data.Cleaned) {
|
||||
soundtracks_added = append(soundtracks_added, data.Original)
|
||||
seen[data.Original] = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, data := range soundtrackData {
|
||||
// Check regex match with cleaned terms
|
||||
if !seen[data.Original] && is_match_regex_cached(cleanedSearchTerm, data.Cleaned) {
|
||||
soundtracks_added = append(soundtracks_added, data.Original)
|
||||
seen[data.Original] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func is_match_exact(search_term string, soundtrack_name string) bool {
|
||||
search_term = strings.ToLower(search_term)
|
||||
soundtrack_name = strings.ToLower(soundtrack_name)
|
||||
|
||||
if search_term == "" {
|
||||
return true
|
||||
} else if strings.Contains(soundtrack_name, search_term) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func is_match_contains(search_term string, soundtrack_name string) bool {
|
||||
if search_term == "" {
|
||||
return true
|
||||
} else if strings.Contains(soundtrack_name, search_term) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func is_match_regex(search_term string, soundtrack_name string) bool {
|
||||
if search_term == "" {
|
||||
return true
|
||||
} else if compile_regex(search_term).MatchString(soundtrack_name) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func compile_regex(search_term string) *regexp.Regexp {
|
||||
regText := ".*"
|
||||
for _, letter := range search_term {
|
||||
regText += string(letter) + ".*"
|
||||
}
|
||||
r, _ := regexp.Compile(regText)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// is_match_regex_cached uses cached compiled regex patterns for better performance
|
||||
func is_match_regex_cached(search_term string, soundtrack_name string) bool {
|
||||
if search_term == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check cache first
|
||||
regexCacheMutex.Lock()
|
||||
re, exists := regexCache[search_term]
|
||||
if !exists {
|
||||
re = compile_regex(search_term)
|
||||
regexCache[search_term] = re
|
||||
}
|
||||
regexCacheMutex.Unlock()
|
||||
|
||||
return re.MatchString(soundtrack_name)
|
||||
}
|
||||
|
||||
func clean_term(term string) string {
|
||||
term = strings.ReplaceAll(term, " ", "")
|
||||
term = strings.ReplaceAll(term, "é", "e")
|
||||
term = strings.ReplaceAll(term, "+", "plus")
|
||||
term = strings.ReplaceAll(term, "&", "and")
|
||||
term = strings.ReplaceAll(term, "'n", "and")
|
||||
return strings.ToLower(term)
|
||||
}
|
||||
|
||||
// toLower converts a string to lowercase.
|
||||
func toLower(s string) string {
|
||||
return strings.Map(unicode.ToLower, s)
|
||||
}
|
||||
|
||||
// levenshteinWithThreshold calculates Levenshtein distance with early termination and space optimization.
|
||||
// Uses O(min(n,m)) space instead of O(n*m) and stops early if threshold is exceeded.
|
||||
func levenshteinWithThreshold(s, t string, threshold int) int {
|
||||
if threshold < 0 {
|
||||
threshold = 2 // default threshold
|
||||
}
|
||||
|
||||
m, n := len(s), len(t)
|
||||
|
||||
// Quick checks for early termination
|
||||
if m == 0 {
|
||||
return n
|
||||
}
|
||||
if n == 0 {
|
||||
return m
|
||||
}
|
||||
if abs(m-n) > threshold {
|
||||
return threshold + 1 // Can't be within threshold
|
||||
}
|
||||
|
||||
// Use the shorter string for the row to minimize space
|
||||
if m < n {
|
||||
s, t = t, s
|
||||
m, n = n, m
|
||||
}
|
||||
|
||||
// Space optimization: only store two rows
|
||||
prevRow := make([]int, n+1)
|
||||
currRow := make([]int, n+1)
|
||||
|
||||
// Initialize first row
|
||||
for j := 0; j <= n; j++ {
|
||||
prevRow[j] = j
|
||||
}
|
||||
|
||||
for i := 1; i <= m; i++ {
|
||||
currRow[0] = i
|
||||
minInRow := currRow[0]
|
||||
|
||||
for j := 1; j <= n; j++ {
|
||||
if s[i-1] == t[j-1] {
|
||||
currRow[j] = prevRow[j-1]
|
||||
} else {
|
||||
currRow[j] = min3(prevRow[j], currRow[j-1], prevRow[j-1]) + 1
|
||||
}
|
||||
if currRow[j] < minInRow {
|
||||
minInRow = currRow[j]
|
||||
}
|
||||
}
|
||||
|
||||
// Early termination: if minimum in current row exceeds threshold
|
||||
if minInRow > threshold {
|
||||
return threshold + 1
|
||||
}
|
||||
|
||||
// Swap rows for next iteration
|
||||
prevRow, currRow = currRow, prevRow
|
||||
}
|
||||
|
||||
return prevRow[n]
|
||||
}
|
||||
|
||||
// Helper functions for optimized Levenshtein
|
||||
func abs(x int) int {
|
||||
if x < 0 {
|
||||
return -x
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func min3(a, b, c int) int {
|
||||
if a < b {
|
||||
if a < c {
|
||||
return a
|
||||
}
|
||||
return c
|
||||
}
|
||||
if b < c {
|
||||
return b
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// precomputeSoundtrackData initializes cleaned terms and abbreviations for all soundtracks
|
||||
func precomputeSoundtrackData() {
|
||||
precomputedOnce.Do(func() {
|
||||
soundtracks := backend.GetAllSoundtracks()
|
||||
precomputedSoundtracks = make([]SoundtrackData, len(soundtracks))
|
||||
|
||||
for i, soundtrack := range soundtracks {
|
||||
cleaned := clean_term(soundtrack)
|
||||
precomputedSoundtracks[i] = SoundtrackData{
|
||||
Original: soundtrack,
|
||||
Cleaned: cleaned,
|
||||
Abbreviation: extractAbbreviation(soundtrack),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// getPrecomputedSoundtracks ensures precomputed data is available and returns it
|
||||
func getPrecomputedSoundtracks() []SoundtrackData {
|
||||
precomputeSoundtrackData()
|
||||
return precomputedSoundtracks
|
||||
}
|
||||
|
||||
// levenshtein maintains backward compatibility by calling the optimized version
|
||||
func levenshtein(s, t string) int {
|
||||
return levenshteinWithThreshold(s, t, -1) // -1 uses default threshold
|
||||
}
|
||||
|
||||
// extractAbbreviation extracts the first letters of each word in a string.
|
||||
func extractAbbreviation(s string) string {
|
||||
words := strings.Fields(s)
|
||||
abbr := ""
|
||||
for _, word := range words {
|
||||
if len(word) > 0 {
|
||||
abbr += string(word[0])
|
||||
}
|
||||
}
|
||||
return toLower(abbr)
|
||||
}
|
||||
|
||||
// fuzzyFindSubstring checks if the query fuzzy-matches any substring of the item.
|
||||
func fuzzyFindSubstring(query, item string, threshold int) (bool, int) {
|
||||
query = toLower(query)
|
||||
item = toLower(item)
|
||||
queryLen := len(query)
|
||||
itemLen := len(item)
|
||||
if queryLen > itemLen {
|
||||
return false, 0
|
||||
}
|
||||
|
||||
// Check for exact substring match first (weight: 100)
|
||||
if strings.Contains(item, query) {
|
||||
return true, 100
|
||||
}
|
||||
|
||||
// Check for fuzzy substring match using optimized Levenshtein
|
||||
for i := 0; i <= itemLen-queryLen; i++ {
|
||||
substring := item[i : i+queryLen]
|
||||
distance := levenshteinWithThreshold(query, substring, threshold)
|
||||
if distance <= threshold {
|
||||
// Weight: higher for matches at the start of the string
|
||||
weight := 50 - i // Higher weight for earlier matches
|
||||
return true, weight
|
||||
}
|
||||
}
|
||||
return false, 0
|
||||
}
|
||||
|
||||
// fuzzyFind checks if the query matches the item (substring or abbreviation).
|
||||
func fuzzyFind(query string, item string, threshold int) (bool, int) {
|
||||
query = toLower(query)
|
||||
item = toLower(item)
|
||||
|
||||
// Check for substring fuzzy match
|
||||
if matched, weight := fuzzyFindSubstring(query, item, threshold); matched {
|
||||
return true, weight
|
||||
}
|
||||
|
||||
// Check for abbreviation match using optimized Levenshtein
|
||||
abbr := extractAbbreviation(item)
|
||||
distance := levenshteinWithThreshold(query, abbr, threshold)
|
||||
if distance <= threshold {
|
||||
// Weight: higher for exact abbreviation matches
|
||||
weight := 100 - distance*10 // Higher weight for exact matches
|
||||
return true, weight
|
||||
}
|
||||
|
||||
return false, 0
|
||||
}
|
||||
|
||||
// getAdaptiveThreshold returns a threshold based on query length
|
||||
func getAdaptiveThreshold(query string) int {
|
||||
threshold := 2 // Base threshold for minor typos
|
||||
queryLen := len(query)
|
||||
if queryLen > 6 {
|
||||
// For longer queries, allow more tolerance
|
||||
threshold = queryLen / 3
|
||||
if threshold < 2 {
|
||||
threshold = 2
|
||||
} else if threshold > 4 {
|
||||
threshold = 4 // Cap at 4 for very long queries
|
||||
}
|
||||
}
|
||||
return threshold
|
||||
}
|
||||
|
||||
// fuzzySearch performs fuzzy search on soundtracks using the cleaned terms for consistency
|
||||
func fuzzySearch(searchText string) []string {
|
||||
query := clean_term(searchText)
|
||||
threshold := getAdaptiveThreshold(query)
|
||||
|
||||
// Use precomputed data for efficiency
|
||||
soundtrackData := getPrecomputedSoundtracks()
|
||||
|
||||
type match struct {
|
||||
item string
|
||||
weight int
|
||||
}
|
||||
var matches []match
|
||||
seen := make(map[string]bool) // O(1) duplicate checking
|
||||
|
||||
for _, data := range soundtrackData {
|
||||
if matched, weight := fuzzyFind(query, data.Cleaned, threshold); matched {
|
||||
if !seen[data.Original] {
|
||||
matches = append(matches, match{data.Original, weight})
|
||||
seen[data.Original] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort matches by weight (descending)
|
||||
sort.Slice(matches, func(i, j int) bool {
|
||||
return matches[i].weight > matches[j].weight
|
||||
})
|
||||
|
||||
// Extract just the soundtrack names in order
|
||||
result := make([]string, len(matches))
|
||||
for i, m := range matches {
|
||||
result[i] = m.item
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// FindSoundtrackFuzzyWebHandler handles fuzzy search requests
|
||||
func FindSoundtrackFuzzyWebHandler(w http.ResponseWriter, r *http.Request) {
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
http.Error(w, "Bad Request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
search_term := r.FormValue("search_term")
|
||||
|
||||
results := fuzzySearch(search_term)
|
||||
|
||||
component := FoundSoundtracks(results)
|
||||
err = component.Render(r.Context(), w)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
log.Fatalf("Error rendering in FindSoundtrackFuzzyWebHandler: %e", err)
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,45 @@
|
||||
package web
|
||||
|
||||
templ HelloForm() {
|
||||
templ SearchForm() {
|
||||
@Base() {
|
||||
<button id="dark-mode-toggle">🌙</button>
|
||||
<div id="search-container">
|
||||
<input id="search_term" name="search_term" type="text" hx-post="/find" hx-trigger="keyup changed delay:0.25s" hx-target="#soundtracks-container"/>
|
||||
<input id="search_term" name="search_term" type="text" hx-post="/findfuzzy" hx-trigger="keyup changed delay:0.25s" hx-target="#soundtracks-container"/>
|
||||
<div class="radio-group" style="display: inline-block; margin-left: 10px;">
|
||||
<label><input type="radio" name="search_type" value="fuzzy" checked> Fuzzy</label>
|
||||
<label><input type="radio" name="search_type" value="normal"> Normal</label>
|
||||
</div>
|
||||
<button type="button" id="clear" name="clear">Clear</button>
|
||||
</div>
|
||||
<div id="soundtracks-container"></div>
|
||||
<script>
|
||||
document.addEventListener('readystatechange', () => {
|
||||
// Get current search type from radio buttons
|
||||
function getSearchType() {
|
||||
return document.querySelector('input[name="search_type"]:checked').value;
|
||||
}
|
||||
|
||||
// Get endpoint based on search type
|
||||
function getSearchEndpoint() {
|
||||
return getSearchType() === 'fuzzy' ? '/findfuzzy' : '/find';
|
||||
}
|
||||
|
||||
// Update search input endpoint
|
||||
function updateSearchEndpoint() {
|
||||
const endpoint = getSearchEndpoint();
|
||||
document.getElementById('search_term').setAttribute('hx-post', endpoint);
|
||||
}
|
||||
|
||||
document.addEventListener('readystatechange', () => {
|
||||
if (document.readyState == 'complete') {
|
||||
htmx.ajax('POST', '/find', '#soundtracks-container');
|
||||
// Initialize with fuzzy search (default)
|
||||
htmlx.ajax('POST', '/findfuzzy', '#soundtracks-container');
|
||||
document.getElementById("search_term").focus();
|
||||
|
||||
// Add event listeners for radio buttons
|
||||
document.querySelectorAll('input[name="search_type"]').forEach(radio => {
|
||||
radio.addEventListener('change', updateSearchEndpoint);
|
||||
});
|
||||
|
||||
// Initialize dark mode from localStorage (default to dark)
|
||||
const savedTheme = localStorage.getItem('theme') || 'dark';
|
||||
if (savedTheme === 'dark') {
|
||||
@@ -38,7 +64,8 @@ templ HelloForm() {
|
||||
|
||||
document.getElementById("clear").addEventListener("click", function (event) {
|
||||
document.getElementById("search_term").value = "";
|
||||
htmx.ajax('POST', '/find', '#soundtracks-container');
|
||||
// Use current search type endpoint
|
||||
htmlx.ajax('POST', getSearchEndpoint(), '#soundtracks-container');
|
||||
document.getElementById("search_term").focus();
|
||||
});
|
||||
</script>
|
||||
@@ -51,4 +78,4 @@ templ FoundSoundtracks(soundtracks []string) {
|
||||
<p class="soundtrack-text">{ soundtrack }</p>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
-54
@@ -175,17 +175,17 @@ func SyncResult() SyncResponse {
|
||||
}
|
||||
}
|
||||
|
||||
func SyncSoundtracksNewFull() {
|
||||
syncSoundtracksNew(true)
|
||||
func SyncSoundtracksFull() {
|
||||
syncSoundtracks(true)
|
||||
Reset()
|
||||
}
|
||||
|
||||
func SyncSoundtracksNewOnlyChanges() {
|
||||
syncSoundtracksNew(false)
|
||||
func SyncSoundtracksOnlyChanges() {
|
||||
syncSoundtracks(false)
|
||||
Reset()
|
||||
}
|
||||
|
||||
func syncSoundtracksNew(full bool) {
|
||||
func syncSoundtracks(full bool) {
|
||||
musicPath := os.Getenv("MUSIC_PATH")
|
||||
fmt.Printf("dir: %s\n", musicPath)
|
||||
logging.GetLogger().Debug("Folder to sync", zap.String("MUSIC_PATH", musicPath))
|
||||
@@ -233,11 +233,11 @@ func syncSoundtracksNew(full bool) {
|
||||
for _, dir := range directories {
|
||||
pool.Submit(func() {
|
||||
defer syncWg.Done()
|
||||
syncSoundtrackNew(dir, foldersToSkip, musicPath, full)
|
||||
syncSoundtrack(dir, foldersToSkip, musicPath, full)
|
||||
})
|
||||
}
|
||||
syncWg.Wait()
|
||||
checkBrokenSongsNew()
|
||||
checkBrokenSongs()
|
||||
|
||||
soundtracksAfterSync, err = repo.FindAllSoundtracks(BackendCtx())
|
||||
handleError("FindAllSoundtracks After", err, "")
|
||||
@@ -250,7 +250,7 @@ func syncSoundtracksNew(full bool) {
|
||||
Syncing = false
|
||||
}
|
||||
|
||||
func checkBrokenSongsNew() {
|
||||
func checkBrokenSongs() {
|
||||
allSongs, err := repo.FetchAllSongs(BackendCtx())
|
||||
handleError("FetchAllSongs", err, "")
|
||||
var brokenWg sync.WaitGroup
|
||||
@@ -261,7 +261,7 @@ func checkBrokenSongsNew() {
|
||||
for _, song := range allSongs {
|
||||
poolBroken.Submit(func() {
|
||||
defer brokenWg.Done()
|
||||
checkBrokenSongNew(song)
|
||||
checkBrokenSong(song)
|
||||
})
|
||||
}
|
||||
brokenWg.Wait()
|
||||
@@ -271,7 +271,7 @@ func checkBrokenSongsNew() {
|
||||
}
|
||||
}
|
||||
|
||||
func checkBrokenSongNew(song repository.Song) {
|
||||
func checkBrokenSong(song repository.Song) {
|
||||
//Check if file exists and open
|
||||
openFile, err := os.Open(song.Path)
|
||||
if err != nil {
|
||||
@@ -286,7 +286,7 @@ func checkBrokenSongNew(song repository.Song) {
|
||||
}
|
||||
}
|
||||
|
||||
func syncSoundtrackNew(file os.DirEntry, foldersToSkip []string, baseDir string, full bool) {
|
||||
func syncSoundtrack(file os.DirEntry, foldersToSkip []string, baseDir string, full bool) {
|
||||
if file.IsDir() && !contains(foldersToSkip, file.Name()) {
|
||||
logging.GetLogger().Debug("Syncing soundtrack", zap.String("soundtrack", file.Name()))
|
||||
soundtrackDir := baseDir + file.Name() + "/"
|
||||
@@ -328,46 +328,14 @@ func syncSoundtrackNew(file os.DirEntry, foldersToSkip []string, baseDir string,
|
||||
}
|
||||
switch status {
|
||||
case NewSoundtrack:
|
||||
if id != -1 {
|
||||
for _, entry := range entries {
|
||||
fileInfo, err := entry.Info()
|
||||
if err != nil {
|
||||
logging.GetLogger().Error("Failed to get file info", zap.String("error", err.Error()))
|
||||
continue
|
||||
}
|
||||
id = getIdFromFileNew(fileInfo)
|
||||
if id != -1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
err = repo.InsertSoundtrackWithExistingId(BackendCtx(), repository.InsertSoundtrackWithExistingIdParams{ID: id, SoundtrackName: file.Name(), Path: soundtrackDir, Hash: dirHash})
|
||||
handleError("InsertSoundtrackWithExistingId", err, "")
|
||||
if err != nil {
|
||||
logging.GetLogger().Debug("Soundtrack already exists, removing old ID file",
|
||||
zap.Int32("id", id),
|
||||
zap.String("soundtrack_dir", soundtrackDir))
|
||||
fileName := soundtrackDir + "/." + strconv.Itoa(int(id)) + ".id"
|
||||
logging.GetLogger().Debug("Removing ID file", zap.String("filename", fileName))
|
||||
|
||||
err := os.Remove(fileName)
|
||||
if err != nil {
|
||||
logging.GetLogger().Error("Failed to remove ID file", zap.String("filename", fileName), zap.String("error", err.Error()))
|
||||
}
|
||||
|
||||
newDirHash := getHashForDir(soundtrackDir)
|
||||
|
||||
id = insertSoundtrackNew(file.Name(), soundtrackDir, newDirHash)
|
||||
}
|
||||
} else {
|
||||
id = insertSoundtrackNew(file.Name(), soundtrackDir, dirHash)
|
||||
}
|
||||
id = insertSoundtrack(file.Name(), soundtrackDir, dirHash)
|
||||
logging.GetLogger().Debug("New soundtrack detected",
|
||||
zap.Int32("id", id),
|
||||
zap.String("soundtrack", file.Name()),
|
||||
zap.String("hash", dirHash),
|
||||
zap.String("status", status.String()))
|
||||
soundtracksAdded = append(soundtracksAdded, file.Name())
|
||||
newCheckSongs(entries, soundtrackDir, id)
|
||||
checkSongs(entries, soundtrackDir, id)
|
||||
case SoundtrackChanged:
|
||||
logging.GetLogger().Debug("Soundtrack changed",
|
||||
zap.Int32("id", id),
|
||||
@@ -377,7 +345,7 @@ func syncSoundtrackNew(file os.DirEntry, foldersToSkip []string, baseDir string,
|
||||
err = repo.UpdateSoundtrackHash(BackendCtx(), repository.UpdateSoundtrackHashParams{Hash: dirHash, ID: id})
|
||||
handleError("UpdateSoundtrackHash", err, "")
|
||||
soundtracksChangedContent = append(soundtracksChangedContent, file.Name())
|
||||
newCheckSongs(entries, soundtrackDir, id)
|
||||
checkSongs(entries, soundtrackDir, id)
|
||||
case TitleChanged:
|
||||
logging.GetLogger().Debug("Soundtrack title changed",
|
||||
zap.Int32("id", id),
|
||||
@@ -387,7 +355,7 @@ func syncSoundtrackNew(file os.DirEntry, foldersToSkip []string, baseDir string,
|
||||
zap.String("status", status.String()))
|
||||
err = repo.UpdateSoundtrackName(BackendCtx(), repository.UpdateSoundtrackNameParams{Name: file.Name(), Path: soundtrackDir, ID: id})
|
||||
handleError("UpdateSoundtrackName", err, "")
|
||||
newCheckSongs(entries, soundtrackDir, id)
|
||||
checkSongs(entries, soundtrackDir, id)
|
||||
if soundtracksChangedTitle == nil {
|
||||
soundtracksChangedTitle = make(map[string]string)
|
||||
}
|
||||
@@ -405,7 +373,7 @@ func syncSoundtrackNew(file os.DirEntry, foldersToSkip []string, baseDir string,
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
newCheckSongs(entries, soundtrackDir, id)
|
||||
checkSongs(entries, soundtrackDir, id)
|
||||
soundtracksReAdded = append(soundtracksReAdded, file.Name())
|
||||
logging.GetLogger().Debug("Soundtrack added again",
|
||||
zap.Int32("id", id),
|
||||
@@ -430,7 +398,7 @@ func syncSoundtrackNew(file os.DirEntry, foldersToSkip []string, baseDir string,
|
||||
zap.Int("percent", int((foldersSynced/numberOfFoldersToSync)*100)))
|
||||
}
|
||||
|
||||
func insertSoundtrackNew(name string, path string, hash string) int32 {
|
||||
func insertSoundtrack(name string, path string, hash string) int32 {
|
||||
var duplicateError = errors.New("ERROR: duplicate key value violates unique")
|
||||
id, err := repo.InsertSoundtrack(BackendCtx(), repository.InsertSoundtrackParams{SoundtrackName: name, Path: path, Hash: hash})
|
||||
handleError("InsertSoundtrack", err, "")
|
||||
@@ -440,14 +408,14 @@ func insertSoundtrackNew(name string, path string, hash string) int32 {
|
||||
logging.GetLogger().Debug("Resetting soundtrack ID sequence")
|
||||
_, err = repo.ResetSoundtrackIdSeq(BackendCtx())
|
||||
handleError("ResetSoundtrackIdSeq", err, "")
|
||||
id = insertSoundtrackNew(name, path, hash)
|
||||
id = insertSoundtrack(name, path, hash)
|
||||
}
|
||||
}
|
||||
return id
|
||||
|
||||
}
|
||||
|
||||
func newCheckSongs(entries []os.DirEntry, soundtrackDir string, id int32) int32 {
|
||||
func checkSongs(entries []os.DirEntry, soundtrackDir string, id int32) int32 {
|
||||
//hasher := md5.New()
|
||||
var numberOfSongs int32
|
||||
numberOfFiles := len(entries)
|
||||
@@ -457,7 +425,7 @@ func newCheckSongs(entries []os.DirEntry, soundtrackDir string, id int32) int32
|
||||
for _, entry := range entries {
|
||||
poolSong.Submit(func() {
|
||||
defer songWg.Done()
|
||||
if newCheckSong(entry, soundtrackDir, id) {
|
||||
if checkSong(entry, soundtrackDir, id) {
|
||||
numberOfSongs++
|
||||
}
|
||||
})
|
||||
@@ -466,7 +434,7 @@ func newCheckSongs(entries []os.DirEntry, soundtrackDir string, id int32) int32
|
||||
return numberOfSongs
|
||||
}
|
||||
|
||||
func newCheckSong(entry os.DirEntry, soundtrackDir string, id int32) bool {
|
||||
func checkSong(entry os.DirEntry, soundtrackDir string, id int32) bool {
|
||||
fileInfo, err := entry.Info()
|
||||
if err != nil {
|
||||
logging.GetLogger().Error("Failed to get file info", zap.String("filename", entry.Name()), zap.String("error", err.Error()))
|
||||
@@ -570,7 +538,7 @@ func getHashForFile(path string) string {
|
||||
return hex.EncodeToString(hasher.Sum(nil))
|
||||
}
|
||||
|
||||
func getIdFromFileNew(file os.FileInfo) int32 {
|
||||
func getIdFromFile(file os.FileInfo) int32 {
|
||||
name := file.Name()
|
||||
if !file.IsDir() && strings.HasSuffix(name, ".id") {
|
||||
name = strings.Replace(name, ".id", "", 1)
|
||||
|
||||
@@ -9,10 +9,10 @@ import (
|
||||
|
||||
func TestContains(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
slice []string
|
||||
search string
|
||||
expected bool
|
||||
name string
|
||||
slice []string
|
||||
search string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "element exists",
|
||||
@@ -155,9 +155,9 @@ func TestGetIdFromFileNew(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := getIdFromFileNew(tt.fileInfo)
|
||||
result := getIdFromFile(tt.fileInfo)
|
||||
if result != tt.expected {
|
||||
t.Errorf("getIdFromFileNew() = %v, want %v", result, tt.expected)
|
||||
t.Errorf("getIdFromFile() = %v, want %v", result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -172,10 +172,10 @@ type mockFileInfoForSong struct {
|
||||
|
||||
func (m *mockFileInfoForSong) Name() string { return m.name }
|
||||
func (m *mockFileInfoForSong) Size() int64 { return m.size }
|
||||
func (m *mockFileInfoForSong) Mode() os.FileMode { return 0 }
|
||||
func (m *mockFileInfoForSong) ModTime() time.Time { return time.Time{} }
|
||||
func (m *mockFileInfoForSong) IsDir() bool { return m.isDir }
|
||||
func (m *mockFileInfoForSong) Sys() interface{} { return nil }
|
||||
func (m *mockFileInfoForSong) Mode() os.FileMode { return 0 }
|
||||
func (m *mockFileInfoForSong) ModTime() time.Time { return time.Time{} }
|
||||
func (m *mockFileInfoForSong) IsDir() bool { return m.isDir }
|
||||
func (m *mockFileInfoForSong) Sys() interface{} { return nil }
|
||||
|
||||
type mockFileInfoForCover struct {
|
||||
name string
|
||||
@@ -185,10 +185,10 @@ type mockFileInfoForCover struct {
|
||||
|
||||
func (m *mockFileInfoForCover) Name() string { return m.name }
|
||||
func (m *mockFileInfoForCover) Size() int64 { return m.size }
|
||||
func (m *mockFileInfoForCover) Mode() os.FileMode { return 0 }
|
||||
func (m *mockFileInfoForCover) ModTime() time.Time { return time.Time{} }
|
||||
func (m *mockFileInfoForCover) IsDir() bool { return m.isDir }
|
||||
func (m *mockFileInfoForCover) Sys() interface{} { return nil }
|
||||
func (m *mockFileInfoForCover) Mode() os.FileMode { return 0 }
|
||||
func (m *mockFileInfoForCover) ModTime() time.Time { return time.Time{} }
|
||||
func (m *mockFileInfoForCover) IsDir() bool { return m.isDir }
|
||||
func (m *mockFileInfoForCover) Sys() interface{} { return nil }
|
||||
|
||||
type mockFileInfoForId struct {
|
||||
name string
|
||||
@@ -198,7 +198,7 @@ type mockFileInfoForId struct {
|
||||
|
||||
func (m *mockFileInfoForId) Name() string { return m.name }
|
||||
func (m *mockFileInfoForId) Size() int64 { return m.size }
|
||||
func (m *mockFileInfoForId) Mode() os.FileMode { return 0 }
|
||||
func (m *mockFileInfoForId) ModTime() time.Time { return time.Time{} }
|
||||
func (m *mockFileInfoForId) IsDir() bool { return m.isDir }
|
||||
func (m *mockFileInfoForId) Sys() interface{} { return nil }
|
||||
func (m *mockFileInfoForId) Mode() os.FileMode { return 0 }
|
||||
func (m *mockFileInfoForId) ModTime() time.Time { return time.Time{} }
|
||||
func (m *mockFileInfoForId) IsDir() bool { return m.isDir }
|
||||
func (m *mockFileInfoForId) Sys() interface{} { return nil }
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"music-server/internal/backend"
|
||||
"music-server/internal/logging"
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
)
|
||||
|
||||
// SyncCheckMiddleware blocks requests when syncing is in progress.
|
||||
// It returns HTTP 423 Locked with a standard message.
|
||||
// This middleware should be applied to handlers that cannot execute during sync.
|
||||
func SyncCheckMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
logging.GetLogger().Info("Syncing is in progress - request blocked")
|
||||
return c.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
@@ -31,10 +31,6 @@ func NewMusicHandler() *MusicHandler {
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music [get]
|
||||
func (m *MusicHandler) GetSong(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
song := ctx.QueryParam("song")
|
||||
if song == "" {
|
||||
return ctx.String(http.StatusBadRequest, "song can't be empty")
|
||||
@@ -58,10 +54,6 @@ func (m *MusicHandler) GetSong(ctx *echo.Context) error {
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music/soundTest [get]
|
||||
func (m *MusicHandler) GetSoundCheckSong(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
songPath := backend.GetSoundCheckSong()
|
||||
file, err := os.Open(songPath)
|
||||
if err != nil {
|
||||
@@ -80,10 +72,6 @@ func (m *MusicHandler) GetSoundCheckSong(ctx *echo.Context) error {
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music/reset [get]
|
||||
func (m *MusicHandler) ResetMusic(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
backend.Reset()
|
||||
return ctx.NoContent(http.StatusOK)
|
||||
}
|
||||
@@ -98,10 +86,6 @@ func (m *MusicHandler) ResetMusic(ctx *echo.Context) error {
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music/rand [get]
|
||||
func (m *MusicHandler) GetRandomSong(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
songPath := backend.GetRandomSong()
|
||||
file, err := os.Open(songPath)
|
||||
if err != nil {
|
||||
@@ -121,10 +105,6 @@ func (m *MusicHandler) GetRandomSong(ctx *echo.Context) error {
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music/rand/low [get]
|
||||
func (m *MusicHandler) GetRandomSongLowChance(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
songPath := backend.GetRandomSongLowChance()
|
||||
file, err := os.Open(songPath)
|
||||
if err != nil {
|
||||
@@ -144,10 +124,6 @@ func (m *MusicHandler) GetRandomSongLowChance(ctx *echo.Context) error {
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music/rand/classic [get]
|
||||
func (m *MusicHandler) GetRandomSongClassic(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
songPath := backend.GetRandomSongClassic()
|
||||
file, err := os.Open(songPath)
|
||||
if err != nil {
|
||||
@@ -193,10 +169,6 @@ func (m *MusicHandler) GetPlayedSongs(ctx *echo.Context) error {
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music/next [get]
|
||||
func (m *MusicHandler) GetNextSong(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
songPath := backend.GetNextSong()
|
||||
file, err := os.Open(songPath)
|
||||
if err != nil {
|
||||
@@ -216,10 +188,6 @@ func (m *MusicHandler) GetNextSong(ctx *echo.Context) error {
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music/previous [get]
|
||||
func (m *MusicHandler) GetPreviousSong(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
songPath := backend.GetPreviousSong()
|
||||
file, err := os.Open(songPath)
|
||||
if err != nil {
|
||||
@@ -239,10 +207,6 @@ func (m *MusicHandler) GetPreviousSong(ctx *echo.Context) error {
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music/all/order [get]
|
||||
func (m *MusicHandler) GetAllSoundtracks(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
soundtrackList := backend.GetAllSoundtracks()
|
||||
return ctx.JSON(http.StatusOK, soundtrackList)
|
||||
}
|
||||
@@ -257,10 +221,6 @@ func (m *MusicHandler) GetAllSoundtracks(ctx *echo.Context) error {
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music/all/random [get]
|
||||
func (m *MusicHandler) GetAllSoundtracksRandom(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
soundtrackList := backend.GetAllSoundtracksRandom()
|
||||
return ctx.JSON(http.StatusOK, soundtrackList)
|
||||
}
|
||||
@@ -277,10 +237,6 @@ func (m *MusicHandler) GetAllSoundtracksRandom(ctx *echo.Context) error {
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music/played [put]
|
||||
func (m *MusicHandler) PutPlayed(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
song, err := strconv.Atoi(ctx.QueryParam("song"))
|
||||
if err != nil {
|
||||
return ctx.JSON(http.StatusBadRequest, err.Error())
|
||||
@@ -299,10 +255,6 @@ func (m *MusicHandler) PutPlayed(ctx *echo.Context) error {
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music/addQue [get]
|
||||
func (m *MusicHandler) AddLatestToQue(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
backend.AddLatestToQue()
|
||||
return ctx.NoContent(http.StatusOK)
|
||||
}
|
||||
@@ -316,10 +268,6 @@ func (m *MusicHandler) AddLatestToQue(ctx *echo.Context) error {
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /music/addPlayed [get]
|
||||
func (m *MusicHandler) AddLatestPlayed(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
logging.GetLogger().Info("Syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
backend.AddLatestPlayed()
|
||||
return ctx.NoContent(http.StatusOK)
|
||||
}
|
||||
|
||||
+22
-21
@@ -50,8 +50,9 @@ func (s *Server) RegisterRoutes() http.Handler {
|
||||
fileServer := http.FileServer(http.FS(web.Assets))
|
||||
e.GET("/assets/*", echo.WrapHandler(fileServer))
|
||||
|
||||
e.GET("/search", echo.WrapHandler(templ.Handler(web.HelloForm())))
|
||||
e.GET("/search", echo.WrapHandler(templ.Handler(web.SearchForm())))
|
||||
e.POST("/find", echo.WrapHandler(http.HandlerFunc(web.FindSoundtrackWebHandler)))
|
||||
e.POST("/findfuzzy", echo.WrapHandler(http.HandlerFunc(web.FindSoundtrackFuzzyWebHandler)))
|
||||
|
||||
e.Static("/", "/frontend")
|
||||
|
||||
@@ -82,32 +83,32 @@ func (s *Server) RegisterRoutes() http.Handler {
|
||||
|
||||
sync := NewSyncHandler()
|
||||
syncGroup := e.Group("/sync")
|
||||
syncGroup.GET("", deprecatedMiddleware(sync.SyncSoundtracksNewOnlyChanges))
|
||||
syncGroup.GET("", deprecatedMiddleware(middleware.SyncCheckMiddleware(sync.SyncSoundtracksNewOnlyChanges)))
|
||||
syncGroup.GET("/progress", deprecatedMiddleware(sync.SyncProgress))
|
||||
syncGroup.GET("/new", deprecatedMiddleware(sync.SyncSoundtracksNewOnlyChanges))
|
||||
syncGroup.GET("/full", deprecatedMiddleware(sync.SyncSoundtracksNewFull))
|
||||
syncGroup.GET("/new/full", deprecatedMiddleware(sync.SyncSoundtracksNewFull))
|
||||
syncGroup.GET("/quick", deprecatedMiddleware(sync.SyncSoundtracksNewOnlyChanges))
|
||||
syncGroup.GET("/reset", deprecatedMiddleware(sync.ResetDB))
|
||||
syncGroup.GET("/new", deprecatedMiddleware(middleware.SyncCheckMiddleware(sync.SyncSoundtracksNewOnlyChanges)))
|
||||
syncGroup.GET("/full", deprecatedMiddleware(middleware.SyncCheckMiddleware(sync.SyncSoundtracksNewFull)))
|
||||
syncGroup.GET("/new/full", deprecatedMiddleware(middleware.SyncCheckMiddleware(sync.SyncSoundtracksNewFull)))
|
||||
syncGroup.GET("/quick", deprecatedMiddleware(middleware.SyncCheckMiddleware(sync.SyncSoundtracksNewOnlyChanges)))
|
||||
syncGroup.GET("/reset", deprecatedMiddleware(middleware.SyncCheckMiddleware(sync.ResetDB)))
|
||||
|
||||
music := NewMusicHandler()
|
||||
musicGroup := e.Group("/music")
|
||||
musicGroup.GET("", deprecatedMiddleware(music.GetSong))
|
||||
musicGroup.GET("/soundTest", deprecatedMiddleware(music.GetSoundCheckSong))
|
||||
musicGroup.GET("/reset", deprecatedMiddleware(music.ResetMusic))
|
||||
musicGroup.GET("/rand", deprecatedMiddleware(music.GetRandomSong))
|
||||
musicGroup.GET("/rand/low", deprecatedMiddleware(music.GetRandomSongLowChance))
|
||||
musicGroup.GET("/rand/classic", deprecatedMiddleware(music.GetRandomSongClassic))
|
||||
musicGroup.GET("", deprecatedMiddleware(middleware.SyncCheckMiddleware(music.GetSong)))
|
||||
musicGroup.GET("/soundTest", deprecatedMiddleware(middleware.SyncCheckMiddleware(music.GetSoundCheckSong)))
|
||||
musicGroup.GET("/reset", deprecatedMiddleware(middleware.SyncCheckMiddleware(music.ResetMusic)))
|
||||
musicGroup.GET("/rand", deprecatedMiddleware(middleware.SyncCheckMiddleware(music.GetRandomSong)))
|
||||
musicGroup.GET("/rand/low", deprecatedMiddleware(middleware.SyncCheckMiddleware(music.GetRandomSongLowChance)))
|
||||
musicGroup.GET("/rand/classic", deprecatedMiddleware(middleware.SyncCheckMiddleware(music.GetRandomSongClassic)))
|
||||
musicGroup.GET("/info", deprecatedMiddleware(music.GetSongInfo))
|
||||
musicGroup.GET("/list", deprecatedMiddleware(music.GetPlayedSongs))
|
||||
musicGroup.GET("/next", deprecatedMiddleware(music.GetNextSong))
|
||||
musicGroup.GET("/previous", deprecatedMiddleware(music.GetPreviousSong))
|
||||
musicGroup.GET("/all", deprecatedMiddleware(music.GetAllSoundtracksRandom))
|
||||
musicGroup.GET("/all/order", deprecatedMiddleware(music.GetAllSoundtracks))
|
||||
musicGroup.GET("/all/random", deprecatedMiddleware(music.GetAllSoundtracksRandom))
|
||||
musicGroup.PUT("/played", deprecatedMiddleware(music.PutPlayed))
|
||||
musicGroup.GET("/addQue", deprecatedMiddleware(music.AddLatestToQue))
|
||||
musicGroup.GET("/addPlayed", deprecatedMiddleware(music.AddLatestPlayed))
|
||||
musicGroup.GET("/next", deprecatedMiddleware(middleware.SyncCheckMiddleware(music.GetNextSong)))
|
||||
musicGroup.GET("/previous", deprecatedMiddleware(middleware.SyncCheckMiddleware(music.GetPreviousSong)))
|
||||
musicGroup.GET("/all", deprecatedMiddleware(middleware.SyncCheckMiddleware(music.GetAllSoundtracksRandom)))
|
||||
musicGroup.GET("/all/order", deprecatedMiddleware(middleware.SyncCheckMiddleware(music.GetAllSoundtracks)))
|
||||
musicGroup.GET("/all/random", deprecatedMiddleware(middleware.SyncCheckMiddleware(music.GetAllSoundtracksRandom)))
|
||||
musicGroup.PUT("/played", deprecatedMiddleware(middleware.SyncCheckMiddleware(music.PutPlayed)))
|
||||
musicGroup.GET("/addQue", deprecatedMiddleware(middleware.SyncCheckMiddleware(music.AddLatestToQue)))
|
||||
musicGroup.GET("/addPlayed", deprecatedMiddleware(middleware.SyncCheckMiddleware(music.AddLatestPlayed)))
|
||||
|
||||
// ============================================
|
||||
// API v1 Routes with Token Authentication
|
||||
|
||||
@@ -44,13 +44,9 @@ func (s *SyncHandler) SyncProgress(ctx *echo.Context) error {
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /sync [get]
|
||||
func (s *SyncHandler) SyncSoundtracksNewOnlyChanges(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
logging.GetLogger().Warn("Syncing is already in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
logging.GetLogger().Info("Starting sync with only changes")
|
||||
backend.Syncing = true
|
||||
go backend.SyncSoundtracksNewOnlyChanges()
|
||||
go backend.SyncSoundtracksOnlyChanges()
|
||||
return ctx.JSON(http.StatusOK, "Start syncing soundtracks")
|
||||
}
|
||||
|
||||
@@ -64,13 +60,9 @@ func (s *SyncHandler) SyncSoundtracksNewOnlyChanges(ctx *echo.Context) error {
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /sync/full [get]
|
||||
func (s *SyncHandler) SyncSoundtracksNewFull(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
logging.GetLogger().Warn("Syncing is already in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
logging.GetLogger().Info("Starting full sync")
|
||||
backend.Syncing = true
|
||||
go backend.SyncSoundtracksNewFull()
|
||||
go backend.SyncSoundtracksFull()
|
||||
return ctx.JSON(http.StatusOK, "Start syncing soundtracks full")
|
||||
}
|
||||
|
||||
@@ -84,10 +76,6 @@ func (s *SyncHandler) SyncSoundtracksNewFull(ctx *echo.Context) error {
|
||||
// @Failure 423 {string} string "Syncing is in progress"
|
||||
// @Router /sync/reset [get]
|
||||
func (s *SyncHandler) ResetDB(ctx *echo.Context) error {
|
||||
if backend.Syncing {
|
||||
logging.GetLogger().Warn("Cannot reset - syncing is in progress")
|
||||
return ctx.JSON(http.StatusLocked, "Syncing is in progress")
|
||||
}
|
||||
logging.GetLogger().Info("Resetting soundtracks database")
|
||||
backend.ResetDB()
|
||||
return ctx.JSON(http.StatusOK, "Soundtracks and songs are deleted from the database")
|
||||
|
||||
Reference in New Issue
Block a user