Fix bug of incomplete waveform when playing new track twice in quick succession

This commit is contained in:
Drew Weymouth
2025-08-04 08:17:33 -07:00
parent 96b47feb76
commit 6c4435314c
4 changed files with 99 additions and 29 deletions
+49 -7
View File
@@ -26,8 +26,10 @@ type AudioCache struct {
}
type cacheEntry struct {
done bool
cancel context.CancelFunc
done bool
refCount int
pendingDeletion bool
cancel context.CancelFunc
}
// AudioCacheRequest represents a request to prefetch and cache an audio file.
@@ -62,19 +64,52 @@ func (a *AudioCache) PathForCachedFile(id string) string {
return ""
}
// IsFullyDownloaded returns true if the file for the given id is fully downloaded.
func (a *AudioCache) IsFullyDownloaded(id string) bool {
return a.PathForCachedFile(id) != ""
}
// PathForCachedFile returns the local filesystem path for a cached track,
// including one that is in the process of downloading.
// If it is not cached or downloading, it returns an empty string.
func (a *AudioCache) PathForCachedOrDownloadingFile(id string) string {
return a.pathForCachedOrDownloadingFile(id, false)
}
// ObtainReferenceToFile returns the local filesystem path for a cached track,
// including one that is in the process of downloading, and obtains a refernce
// to it such that it will not be deleted until ReleaseReferenceToFile is called.
// If it is not cached or downloading, it returns an empty string.
func (a *AudioCache) ObtainReferenceToFile(id string) string {
return a.pathForCachedOrDownloadingFile(id, true)
}
func (a *AudioCache) pathForCachedOrDownloadingFile(id string, obtainReference bool) string {
a.mutex.Lock()
defer a.mutex.Unlock()
if _, ok := a.entries[id]; ok {
if entry, ok := a.entries[id]; ok {
if obtainReference {
entry.refCount++
}
return a.pathForID(id)
}
return ""
}
func (a *AudioCache) ReleaseReferenceToFile(id string) {
a.mutex.Lock()
defer a.mutex.Unlock()
if e, ok := a.entries[id]; ok {
e.refCount--
if e.refCount == 0 && e.pendingDeletion {
a.deleteEntry(id, e)
}
}
}
// CacheFile begins downloading a file (if not already downloading) and stores it
// to the cache directory under its ID as filename. The download is asynchronous.
func (a *AudioCache) CacheFile(id, dlURL string) {
@@ -119,10 +154,11 @@ func (a *AudioCache) CacheOnly(keep string, fetch []AudioCacheRequest) {
if id != keep && !slices.ContainsFunc(fetch, func(a AudioCacheRequest) bool {
return a.ID == id
}) {
_ = e
e.cancel()
_ = os.Remove(a.pathForID(id))
delete(a.entries, id)
if e.refCount == 0 {
a.deleteEntry(id, e)
} else {
e.pendingDeletion = true
}
}
}
@@ -156,3 +192,9 @@ func (a *AudioCache) Shutdown() {
func (a *AudioCache) pathForID(id string) string {
return filepath.Join(a.baseCacheDir, id)
}
func (a *AudioCache) deleteEntry(id string, e *cacheEntry) {
e.cancel()
_ = os.Remove(a.pathForID(id))
delete(a.entries, id)
}