mirror of
https://git.sr.ht/~joren/streamrip-go
synced 2026-06-17 15:05:39 +02:00
fix lastfm extraction regression and honor no-db
This commit is contained in:
@@ -552,6 +552,7 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
defer func() { _ = mainApp.Close() }()
|
defer func() { _ = mainApp.Close() }()
|
||||||
|
mainApp.IgnoreDB = gopts.noDB
|
||||||
|
|
||||||
title, tracks, err := fetchLastFMPlaylist(ctx, cfg.Session.Downloads.VerifySSL, opts.PlaylistURL)
|
title, tracks, err := fetchLastFMPlaylist(ctx, cfg.Session.Downloads.VerifySSL, opts.PlaylistURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1423,6 +1424,7 @@ type resolvedLastFMTrack struct {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
lastFMTitleTagsRe = regexp.MustCompile(`<a\b[^>]*\btitle=(?:"([^"]+)"|'([^']+)')`)
|
lastFMTitleTagsRe = regexp.MustCompile(`<a\b[^>]*\btitle=(?:"([^"]+)"|'([^']+)')`)
|
||||||
|
lastFMDataTrackArtistRe = regexp.MustCompile(`data-track-name=(?:"([^"]+)"|'([^']+)')[^>]*data-artist-name=(?:"([^"]+)"|'([^']+)')`)
|
||||||
lastFMTotalTracksRe = regexp.MustCompile(`data-playlisting-entry-count="(\d+)"`)
|
lastFMTotalTracksRe = regexp.MustCompile(`data-playlisting-entry-count="(\d+)"`)
|
||||||
lastFMPlaylistTitleRe = regexp.MustCompile(`<h1[^>]*class="[^"]*playlisting-playlist-header-title[^"]*"[^>]*>([^<]+)</h1>`)
|
lastFMPlaylistTitleRe = regexp.MustCompile(`<h1[^>]*class="[^"]*playlisting-playlist-header-title[^"]*"[^>]*>([^<]+)</h1>`)
|
||||||
lastFMMirrorTitleRe = regexp.MustCompile(`^Title:\s*(.+?)\s+\|`)
|
lastFMMirrorTitleRe = regexp.MustCompile(`^Title:\s*(.+?)\s+\|`)
|
||||||
@@ -1828,11 +1830,30 @@ func extractLastFMPlaylistInfo(page string) (string, int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func extractLastFMTitleArtistPairs(page string) []lastFMTrack {
|
func extractLastFMTitleArtistPairs(page string) []lastFMTrack {
|
||||||
|
dataPairs := lastFMDataTrackArtistRe.FindAllStringSubmatch(page, -1)
|
||||||
|
if len(dataPairs) > 0 {
|
||||||
|
out := make([]lastFMTrack, 0, len(dataPairs))
|
||||||
|
for _, m := range dataPairs {
|
||||||
|
title := html.UnescapeString(strings.TrimSpace(firstNonEmpty(m[1], m[2])))
|
||||||
|
artist := html.UnescapeString(strings.TrimSpace(firstNonEmpty(m[3], m[4])))
|
||||||
|
if title == "" || artist == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out = append(out, lastFMTrack{Title: title, Artist: artist})
|
||||||
|
}
|
||||||
|
if len(out) > 0 {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
titles := lastFMTitleTagsRe.FindAllStringSubmatch(page, -1)
|
titles := lastFMTitleTagsRe.FindAllStringSubmatch(page, -1)
|
||||||
out := make([]lastFMTrack, 0, len(titles)/2)
|
out := make([]lastFMTrack, 0, len(titles)/2)
|
||||||
for i := 0; i+1 < len(titles); i += 2 {
|
for i := 0; i+1 < len(titles); i += 2 {
|
||||||
titleRaw := strings.TrimSpace(firstNonEmpty(titles[i][1], titles[i][2]))
|
titleRaw := strings.TrimSpace(firstNonEmpty(titles[i][1], titles[i][2]))
|
||||||
artistRaw := strings.TrimSpace(firstNonEmpty(titles[i+1][1], titles[i+1][2]))
|
artistRaw := strings.TrimSpace(firstNonEmpty(titles[i+1][1], titles[i+1][2]))
|
||||||
|
if strings.EqualFold(titleRaw, "Play on YouTube") || strings.EqualFold(artistRaw, "Play on YouTube") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
title := html.UnescapeString(titleRaw)
|
title := html.UnescapeString(titleRaw)
|
||||||
artist := html.UnescapeString(artistRaw)
|
artist := html.UnescapeString(artistRaw)
|
||||||
if title == "" || artist == "" {
|
if title == "" || artist == "" {
|
||||||
|
|||||||
@@ -171,6 +171,25 @@ func TestExtractLastFMTitleArtistPairsSingleQuotes(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestExtractLastFMTitleArtistPairsSkipsPlayOnYouTubeNoise(t *testing.T) {
|
||||||
|
html := `<a href="https://www.youtube.com/watch?v=1" data-track-name="Won't Forget You" data-artist-name="Shouse" title="Play on YouTube">Play track</a>
|
||||||
|
<a href="/music/Shouse/_/Won%27t+Forget+You" title="Won't Forget You"></a>
|
||||||
|
<a href="/music/Shouse" title="Shouse"></a>
|
||||||
|
<a href="https://www.youtube.com/watch?v=2" data-track-name="EYES" data-artist-name="The Blaze" title="Play on YouTube">Play track</a>
|
||||||
|
<a href="/music/The+Blaze/_/EYES" title="EYES"></a>
|
||||||
|
<a href="/music/The+Blaze" title="The Blaze"></a>`
|
||||||
|
pairs := extractLastFMTitleArtistPairs(html)
|
||||||
|
if len(pairs) != 2 {
|
||||||
|
t.Fatalf("pairs len = %d, want 2", len(pairs))
|
||||||
|
}
|
||||||
|
if pairs[0].Title != "Won't Forget You" || pairs[0].Artist != "Shouse" {
|
||||||
|
t.Fatalf("unexpected first pair: %+v", pairs[0])
|
||||||
|
}
|
||||||
|
if pairs[1].Title != "EYES" || pairs[1].Artist != "The Blaze" {
|
||||||
|
t.Fatalf("unexpected second pair: %+v", pairs[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestParseGlobalArgsNoDBBeforeCommand(t *testing.T) {
|
func TestParseGlobalArgsNoDBBeforeCommand(t *testing.T) {
|
||||||
opts, err := parseGlobalArgs([]string{"-ndb", "url", "https://play.qobuz.com/album/0004228000522"})
|
opts, err := parseGlobalArgs([]string{"-ndb", "url", "https://play.qobuz.com/album/0004228000522"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/vbauerster/mpb/v8"
|
"github.com/vbauerster/mpb/v8"
|
||||||
"github.com/vbauerster/mpb/v8/decor"
|
"github.com/vbauerster/mpb/v8/decor"
|
||||||
@@ -90,9 +91,10 @@ func (d *Downloader) FileDeezerEncrypted(ctx context.Context, sourceURL, outputP
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
var bar *mpb.Bar
|
var bar *mpb.Bar
|
||||||
if d.ProgressEnabled() && resp.ContentLength > 0 {
|
if d.ProgressEnabled() {
|
||||||
d.barStarted.Store(1)
|
d.barStarted.Store(1)
|
||||||
desc := shortenName(filepath.Base(outputPath), 54)
|
desc := shortenName(filepath.Base(outputPath), 54)
|
||||||
|
if resp.ContentLength > 0 {
|
||||||
bar = d.progress.AddBar(
|
bar = d.progress.AddBar(
|
||||||
resp.ContentLength,
|
resp.ContentLength,
|
||||||
mpb.PrependDecorators(
|
mpb.PrependDecorators(
|
||||||
@@ -108,6 +110,21 @@ func (d *Downloader) FileDeezerEncrypted(ctx context.Context, sourceURL, outputP
|
|||||||
),
|
),
|
||||||
mpb.BarRemoveOnComplete(),
|
mpb.BarRemoveOnComplete(),
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
bar = d.progress.AddSpinner(
|
||||||
|
0,
|
||||||
|
mpb.PrependDecorators(
|
||||||
|
decor.Name(desc+" ", decor.WC{W: 56, C: decor.DSyncWidth | decor.DindentRight}),
|
||||||
|
),
|
||||||
|
mpb.AppendDecorators(
|
||||||
|
decor.CurrentKibiByte("% .1f", decor.WCSyncWidthR),
|
||||||
|
decor.Name(" | ", decor.WCSyncWidth),
|
||||||
|
decor.Elapsed(decor.ET_STYLE_GO, decor.WCSyncWidthR),
|
||||||
|
),
|
||||||
|
mpb.BarRemoveOnComplete(),
|
||||||
|
)
|
||||||
|
defer bar.SetTotal(-1, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
block, err := blowfish.NewCipher(deriveDeezerBlowfishKey(trackID))
|
block, err := blowfish.NewCipher(deriveDeezerBlowfishKey(trackID))
|
||||||
@@ -193,10 +210,12 @@ func (d *Downloader) file(ctx context.Context, sourceURL, outputPath string, all
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if d.ProgressEnabled() && allowProgress && resp.ContentLength > 0 {
|
if d.ProgressEnabled() && allowProgress {
|
||||||
d.barStarted.Store(1)
|
d.barStarted.Store(1)
|
||||||
desc := shortenName(filepath.Base(outputPath), 54)
|
desc := shortenName(filepath.Base(outputPath), 54)
|
||||||
bar := d.progress.AddBar(
|
var bar *mpb.Bar
|
||||||
|
if resp.ContentLength > 0 {
|
||||||
|
bar = d.progress.AddBar(
|
||||||
resp.ContentLength,
|
resp.ContentLength,
|
||||||
mpb.PrependDecorators(
|
mpb.PrependDecorators(
|
||||||
decor.Name(desc+" ", decor.WC{W: 56, C: decor.DSyncWidth | decor.DindentRight}),
|
decor.Name(desc+" ", decor.WC{W: 56, C: decor.DSyncWidth | decor.DindentRight}),
|
||||||
@@ -211,6 +230,21 @@ func (d *Downloader) file(ctx context.Context, sourceURL, outputPath string, all
|
|||||||
),
|
),
|
||||||
mpb.BarRemoveOnComplete(),
|
mpb.BarRemoveOnComplete(),
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
bar = d.progress.AddSpinner(
|
||||||
|
0,
|
||||||
|
mpb.PrependDecorators(
|
||||||
|
decor.Name(desc+" ", decor.WC{W: 56, C: decor.DSyncWidth | decor.DindentRight}),
|
||||||
|
),
|
||||||
|
mpb.AppendDecorators(
|
||||||
|
decor.CurrentKibiByte("% .1f", decor.WCSyncWidthR),
|
||||||
|
decor.Name(" | ", decor.WCSyncWidth),
|
||||||
|
decor.Elapsed(decor.ET_STYLE_GO, decor.WCSyncWidthR),
|
||||||
|
),
|
||||||
|
mpb.BarRemoveOnComplete(),
|
||||||
|
)
|
||||||
|
defer bar.SetTotal(-1, true)
|
||||||
|
}
|
||||||
buf := make([]byte, 256*1024)
|
buf := make([]byte, 256*1024)
|
||||||
totalWritten := int64(0)
|
totalWritten := int64(0)
|
||||||
for {
|
for {
|
||||||
@@ -286,6 +320,41 @@ func shortenName(name string, max int) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Downloader) streamManifestWithFFmpeg(ctx context.Context, sourceURL, outputPath string, includeVideo bool) error {
|
func (d *Downloader) streamManifestWithFFmpeg(ctx context.Context, sourceURL, outputPath string, includeVideo bool) error {
|
||||||
|
stopSpinner := func() {}
|
||||||
|
if d.ProgressEnabled() {
|
||||||
|
d.barStarted.Store(1)
|
||||||
|
desc := shortenName(filepath.Base(outputPath), 54)
|
||||||
|
spin := d.progress.AddSpinner(
|
||||||
|
0,
|
||||||
|
mpb.PrependDecorators(
|
||||||
|
decor.Name(desc+" ", decor.WC{W: 56, C: decor.DSyncWidth | decor.DindentRight}),
|
||||||
|
),
|
||||||
|
mpb.AppendDecorators(
|
||||||
|
decor.Elapsed(decor.ET_STYLE_GO, decor.WCSyncWidthR),
|
||||||
|
decor.Name(" | ffmpeg stream", decor.WCSyncWidth),
|
||||||
|
),
|
||||||
|
mpb.BarRemoveOnComplete(),
|
||||||
|
)
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
ticker := time.NewTicker(120 * time.Millisecond)
|
||||||
|
defer ticker.Stop()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
spin.IncrBy(1)
|
||||||
|
case <-done:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
stopSpinner = func() {
|
||||||
|
close(done)
|
||||||
|
spin.SetTotal(-1, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer stopSpinner()
|
||||||
|
|
||||||
if _, err := exec.LookPath("ffmpeg"); err != nil {
|
if _, err := exec.LookPath("ffmpeg"); err != nil {
|
||||||
return fmt.Errorf("ffmpeg not found for manifest stream: %w", err)
|
return fmt.Errorf("ffmpeg not found for manifest stream: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user