#9 Major routes refactor and expansion, #15 Added API for statistics, Added search cache backend and functionality tests, Backend sync improvements
Build / build (push) Successful in 56s

This commit is contained in:
Sansan committed 2026-09-26 16:48:40 +02:00
1 parent e86c0f8e04
commit a8a2602f50
34 files changed
+1819 -1294

No files matched your search

+78 -15
View File
@@ -16,6 +16,7 @@ import (
"sync"
"time"
"github.com/google/uuid"
"github.com/panjf2000/ants/v2"
"github.com/MShekow/directory-checksum/directory_checksum"
@@ -178,11 +179,13 @@ func SyncResult() SyncResponse {
func SyncSoundtracksFull() {
syncSoundtracks(true)
Reset()
ReloadSearchCache()
}
func SyncSoundtracksOnlyChanges() {
syncSoundtracks(false)
Reset()
ReloadSearchCache()
}
func syncSoundtracks(full bool) {
@@ -295,6 +298,7 @@ func syncSoundtrack(file os.DirEntry, foldersToSkip []string, baseDir string, fu
var status SoundtrackStatus = NewSoundtrack
var oldSoundtrack repository.Soundtrack
var id int32 = -1
var soundtrackUuid uuid.UUID
//fmt.Printf("Soundtracks before: %d\n", len(soundtracksBeforeSync))
@@ -304,16 +308,19 @@ func syncSoundtrack(file os.DirEntry, foldersToSkip []string, baseDir string, fu
if oldSoundtrack.SoundtrackName == file.Name() && oldSoundtrack.Hash == dirHash {
status = NotChanged
id = oldSoundtrack.ID
soundtrackUuid = oldSoundtrack.Uuid
//fmt.Printf("Soundtrack not changed\n")
break
} else if oldSoundtrack.SoundtrackName == file.Name() && oldSoundtrack.Hash != dirHash {
status = SoundtrackChanged
id = oldSoundtrack.ID
soundtrackUuid = oldSoundtrack.Uuid
//fmt.Printf("Soundtrack changed\n")
break
} else if oldSoundtrack.SoundtrackName != file.Name() && oldSoundtrack.Hash == dirHash {
status = TitleChanged
id = oldSoundtrack.ID
soundtrackUuid = oldSoundtrack.Uuid
//fmt.Printf("SoundtrackName changed\n")
break
}
@@ -328,34 +335,37 @@ func syncSoundtrack(file os.DirEntry, foldersToSkip []string, baseDir string, fu
}
switch status {
case NewSoundtrack:
id = insertSoundtrack(file.Name(), soundtrackDir, dirHash)
id, soundtrackUuid = insertSoundtrack(file.Name(), soundtrackDir, dirHash)
logging.GetLogger().Debug("New soundtrack detected",
zap.Int32("id", id),
zap.String("uuid", soundtrackUuid.String()),
zap.String("soundtrack", file.Name()),
zap.String("hash", dirHash),
zap.String("status", status.String()))
soundtracksAdded = append(soundtracksAdded, file.Name())
checkSongs(entries, soundtrackDir, id)
checkSongs(entries, soundtrackDir, id, soundtrackUuid)
case SoundtrackChanged:
logging.GetLogger().Debug("Soundtrack changed",
zap.Int32("id", id),
zap.String("uuid", soundtrackUuid.String()),
zap.String("soundtrack", file.Name()),
zap.String("hash", dirHash),
zap.String("status", status.String()))
err = repo.UpdateSoundtrackHash(BackendCtx(), repository.UpdateSoundtrackHashParams{Hash: dirHash, ID: id})
handleError("UpdateSoundtrackHash", err, "")
soundtracksChangedContent = append(soundtracksChangedContent, file.Name())
checkSongs(entries, soundtrackDir, id)
checkSongs(entries, soundtrackDir, id, soundtrackUuid)
case TitleChanged:
logging.GetLogger().Debug("Soundtrack title changed",
zap.Int32("id", id),
zap.String("uuid", soundtrackUuid.String()),
zap.String("oldName", oldSoundtrack.SoundtrackName),
zap.String("newName", file.Name()),
zap.String("hash", dirHash),
zap.String("status", status.String()))
err = repo.UpdateSoundtrackName(BackendCtx(), repository.UpdateSoundtrackNameParams{Name: file.Name(), Path: soundtrackDir, ID: id})
handleError("UpdateSoundtrackName", err, "")
checkSongs(entries, soundtrackDir, id)
checkSongs(entries, soundtrackDir, id, soundtrackUuid)
if soundtracksChangedTitle == nil {
soundtracksChangedTitle = make(map[string]string)
}
@@ -373,10 +383,11 @@ func syncSoundtrack(file os.DirEntry, foldersToSkip []string, baseDir string, fu
}
}
if !found {
checkSongs(entries, soundtrackDir, id)
checkSongs(entries, soundtrackDir, id, soundtrackUuid)
soundtracksReAdded = append(soundtracksReAdded, file.Name())
logging.GetLogger().Debug("Soundtrack added again",
zap.Int32("id", id),
zap.String("uuid", soundtrackUuid.String()),
zap.String("newName", file.Name()),
zap.String("hash", dirHash),
zap.String("status", status.String()))
@@ -390,6 +401,15 @@ func syncSoundtrack(file os.DirEntry, foldersToSkip []string, baseDir string, fu
zap.String("status", status.String()))
err = repo.RemoveSoundtrackDeletionDate(BackendCtx(), id)
handleError("RemoveSoundtrackDeletionDate", err, "")
// Remove .id files and update hash if any were removed
if removeIdFilesInSoundtrack(soundtrackDir) {
newHash := getHashForDir(soundtrackDir)
if newHash != dirHash {
err = repo.UpdateSoundtrackHash(BackendCtx(), repository.UpdateSoundtrackHashParams{Hash: newHash, ID: id})
handleError("UpdateSoundtrackHash for .id cleanup", err, "")
}
}
}
foldersSynced++
logging.GetLogger().Debug("Sync progress",
@@ -398,24 +418,31 @@ func syncSoundtrack(file os.DirEntry, foldersToSkip []string, baseDir string, fu
zap.Int("percent", int((foldersSynced/numberOfFoldersToSync)*100)))
}
func insertSoundtrack(name string, path string, hash string) int32 {
func insertSoundtrack(name string, path string, hash string) (int32, uuid.UUID) {
var duplicateError = errors.New("ERROR: duplicate key value violates unique")
id, err := repo.InsertSoundtrack(BackendCtx(), repository.InsertSoundtrackParams{SoundtrackName: name, Path: path, Hash: hash})
soundtrackUuid := uuid.New()
result, err := repo.InsertSoundtrack(BackendCtx(), repository.InsertSoundtrackParams{
SoundtrackName: name,
Path: path,
Hash: hash,
Uuid: soundtrackUuid,
})
handleError("InsertSoundtrack", err, "")
if err != nil {
logging.GetLogger().Warn("ID collision detected, resetting sequence")
logging.GetLogger().Warn("ID collision or UUID duplicate detected, resetting sequence")
if strings.HasPrefix(err.Error(), duplicateError.Error()) {
logging.GetLogger().Debug("Resetting soundtrack ID sequence")
_, err = repo.ResetSoundtrackIdSeq(BackendCtx())
handleError("ResetSoundtrackIdSeq", err, "")
id = insertSoundtrack(name, path, hash)
id, uuid := insertSoundtrack(name, path, hash)
return id, uuid
}
}
return id
return result.ID, result.Uuid
}
func checkSongs(entries []os.DirEntry, soundtrackDir string, id int32) int32 {
func checkSongs(entries []os.DirEntry, soundtrackDir string, id int32, soundtrackUuid uuid.UUID) int32 {
//hasher := md5.New()
var numberOfSongs int32
numberOfFiles := len(entries)
@@ -425,7 +452,7 @@ func checkSongs(entries []os.DirEntry, soundtrackDir string, id int32) int32 {
for _, entry := range entries {
poolSong.Submit(func() {
defer songWg.Done()
if checkSong(entry, soundtrackDir, id) {
if checkSong(entry, soundtrackDir, id, soundtrackUuid) {
numberOfSongs++
}
})
@@ -434,7 +461,7 @@ func checkSongs(entries []os.DirEntry, soundtrackDir string, id int32) int32 {
return numberOfSongs
}
func checkSong(entry os.DirEntry, soundtrackDir string, id int32) bool {
func checkSong(entry os.DirEntry, soundtrackDir string, id int32, soundtrackUuid uuid.UUID) 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()))
@@ -487,8 +514,17 @@ func checkSong(entry os.DirEntry, soundtrackDir string, id int32) bool {
err = repo.AddHashToSong(BackendCtx(), repository.AddHashToSongParams{Hash: songHash, SoundtrackID: id, Path: path})
handleError("AddHashToSong", err, fmt.Sprintf("SoundtrackID: %d | Path: %s | SongName: %s | SongHash: %s", id, path, entry.Name(), songHash))
} else {
err = repo.AddSong(BackendCtx(), repository.AddSongParams{SoundtrackID: id, SongName: songName, Path: path, FileName: &fileName, Hash: songHash})
handleError("AddSong", err, fmt.Sprintf("SoundtrackID: %d | Path: %s | SongName: %s | SongHash: %s", id, path, entry.Name(), songHash))
songUuid := uuid.New()
err = repo.AddSong(BackendCtx(), repository.AddSongParams{
SoundtrackID: id,
SongName: songName,
Path: path,
FileName: &fileName,
Hash: songHash,
Uuid: songUuid,
SoundtrackUuid: soundtrackUuid,
})
handleError("AddSong", err, fmt.Sprintf("SoundtrackID: %d | UUID: %s | Path: %s | SongName: %s | SongHash: %s", id, songUuid.String(), path, entry.Name(), songHash))
}
}
@@ -558,6 +594,33 @@ func isCoverImage(entry fs.FileInfo) bool {
(strings.HasSuffix(entry.Name(), ".jpg") || strings.HasSuffix(entry.Name(), ".png"))
}
func removeIdFilesInSoundtrack(soundtrackDir string) bool {
entries, err := os.ReadDir(soundtrackDir)
if err != nil {
logging.GetLogger().Error("Failed to read soundtrack directory for .id file cleanup",
zap.String("path", soundtrackDir),
zap.String("error", err.Error()))
return false
}
anyRemoved := false
for _, entry := range entries {
if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".id") {
filePath := soundtrackDir + entry.Name()
err := os.Remove(filePath)
if err != nil {
logging.GetLogger().Error("Failed to remove .id file",
zap.String("path", filePath),
zap.String("error", err.Error()))
} else {
logging.GetLogger().Info("Removed .id file", zap.String("path", filePath))
anyRemoved = true
}
}
}
return anyRemoved
}
func contains(s []string, searchTerm string) bool {
i := sort.SearchStrings(s, searchTerm)
return i < len(s) && s[i] == searchTerm