fix: resolve bugs found in pre-release review

This commit is contained in:
Mathis Maquenne
2026-08-05 21:33:08 +02:00
parent 5bb1448650
commit a695fb4de3
17 changed files with 258 additions and 60 deletions
+1 -1
View File
@@ -93,7 +93,7 @@ func findTrackURL(ctx context.Context, httpClient *http.Client, artist, title, d
const toleranceSec = 2
foundDuration := minutes*60 + seconds
if foundDuration <= wantDuration-toleranceSec || foundDuration >= wantDuration+toleranceSec {
if foundDuration < wantDuration-toleranceSec || foundDuration > wantDuration+toleranceSec {
return true
}
+1
View File
@@ -81,6 +81,7 @@ func (d *Downloader) prepareResource(ctx context.Context, id string, opts Option
if err := fsutil.EnsureDir(outputDir); err != nil {
return nil, "", fmt.Errorf("failed to create output directory: %w", err)
}
sweepPartFiles(outputDir)
return resource, outputDir, nil
}
+8 -2
View File
@@ -4,7 +4,10 @@ import (
"context"
"fmt"
"net/http"
"net/url"
"strings"
"unicode"
"unicode/utf8"
"github.com/PuerkitoBio/goquery"
)
@@ -31,7 +34,9 @@ func toLower(ss []string) []string {
}
func fetchGenre(ctx context.Context, httpClient *http.Client, artist, title string) (string, error) {
reqURL := fmt.Sprintf("https://www.last.fm/music/%s/%s/+tags", artist, title)
// Escape the path segments: names containing '/', '?', or '#' would
// otherwise change the URL structure and fetch the wrong page.
reqURL := fmt.Sprintf("https://www.last.fm/music/%s/%s/+tags", url.PathEscape(artist), url.PathEscape(title))
doc, err := fetchGenrePage(ctx, httpClient, reqURL)
if err != nil {
@@ -116,7 +121,8 @@ func formatTags(tags []string) string {
}
words := strings.Fields(tag)
for i, w := range words {
words[i] = strings.ToUpper(w[:1]) + strings.ToLower(w[1:])
r, size := utf8.DecodeRuneInString(w)
words[i] = string(unicode.ToUpper(r)) + strings.ToLower(w[size:])
}
formatted = append(formatted, strings.Join(words, " "))
}
+35 -3
View File
@@ -5,20 +5,41 @@ import (
"errors"
"io"
"os"
"path/filepath"
"github.com/mathismqn/godeez/internal/deezer"
)
const chunkSize = 2048
const (
chunkSize = 2048
partPattern = ".godeez-*.part"
)
func sweepPartFiles(dir string) {
matches, err := filepath.Glob(filepath.Join(dir, partPattern))
if err != nil {
return
}
for _, match := range matches {
os.Remove(match)
}
}
func (d *Downloader) streamToFile(ctx context.Context, stream io.ReadCloser, outputPath string, key []byte) error {
defer stream.Close()
file, err := os.Create(outputPath)
file, err := os.CreateTemp(filepath.Dir(outputPath), partPattern)
if err != nil {
return err
}
defer file.Close()
tmpPath := file.Name()
done := false
defer func() {
if !done {
file.Close()
os.Remove(tmpPath)
}
}()
buffer := make([]byte, chunkSize)
for chunk := 0; ; chunk++ {
@@ -60,5 +81,16 @@ func (d *Downloader) streamToFile(ctx context.Context, stream io.ReadCloser, out
}
}
if err := file.Sync(); err != nil {
return err
}
if err := file.Close(); err != nil {
return err
}
if err := os.Rename(tmpPath, outputPath); err != nil {
return err
}
done = true
return nil
}
+27 -6
View File
@@ -34,20 +34,19 @@ func (d *Downloader) downloadTrack(ctx context.Context, resource deezer.Resource
metadataChan <- fetchMetadata(d.deezerClient.Session.HTTPClient, ctx, track, opts)
}()
stream, err := d.deezerClient.MediaStream(ctx, media)
dlCtx, cancel := context.WithTimeout(ctx, opts.Timeout)
defer cancel()
stream, err := d.deezerClient.MediaStream(dlCtx, media)
if err != nil {
return downloadResult{err: fmt.Errorf("failed to get media stream: %w", err)}
}
dlCtx, cancel := context.WithTimeout(ctx, opts.Timeout)
defer cancel()
fileName := track.Filename(d.kind, mediaFormat)
outputPath := filepath.Join(outputDir, fileName)
outputPath := d.uniqueOutputPath(track.ID, filepath.Join(outputDir, fileName))
key := deezer.BlowfishKey(track.ID)
if err := d.streamToFile(dlCtx, stream, outputPath, key); err != nil {
fsutil.Remove(outputPath)
return downloadResult{err: fmt.Errorf("failed to stream to file: %w", err)}
}
@@ -63,12 +62,34 @@ func (d *Downloader) downloadTrack(ctx context.Context, resource deezer.Resource
}
metadata := <-metadataChan
if err := ctx.Err(); err != nil {
fsutil.Remove(outputPath)
return downloadResult{err: err}
}
warnings = append(warnings, metadata.warnings...)
warnings = append(warnings, d.finalizeDownload(resource, track, outputPath, mediaFormat, metadata.genre, cover, metadata.bpmKey)...)
return downloadResult{warnings: warnings}
}
func (d *Downloader) uniqueOutputPath(trackID, path string) string {
owned := ""
if info, err := d.store.DownloadInfo(trackID); err == nil {
owned = info.Path
}
ext := filepath.Ext(path)
stem := strings.TrimSuffix(path, ext)
candidate := path
for i := 2; candidate != owned && fsutil.Exists(candidate); i++ {
candidate = fmt.Sprintf("%s (%d)%s", stem, i, ext)
}
return candidate
}
func (d *Downloader) finalizeDownload(resource deezer.Resource, track *deezer.Track, outputPath, mediaFormat, genre string, cover []byte, bpmKey bpmKey) []string {
var warnings []string