Refactor
Release / build (amd64, darwin, macos-latest) (push) Has been cancelled
Release / build (amd64, linux, ubuntu-latest) (push) Has been cancelled
Release / build (amd64, windows, windows-latest) (push) Has been cancelled
Release / build (arm64, darwin, macos-latest) (push) Has been cancelled
Release / build (arm64, linux, ubuntu-latest) (push) Has been cancelled
Release / release (push) Has been cancelled
Release / update-homebrew (push) Has been cancelled

This commit is contained in:
Bjarne Øverli
2026-03-23 21:39:55 +01:00
parent 4b17fab005
commit ccb1770fed
13 changed files with 48 additions and 48 deletions
+3 -3
View File
@@ -113,8 +113,8 @@ func (p *Provider) AddTrack(playlistName string, track playlist.Track) error {
return nil
}
// SavePlaylist overwrites the named playlist with the given tracks.
func (p *Provider) SavePlaylist(name string, tracks []playlist.Track) error {
// savePlaylist overwrites the named playlist with the given tracks.
func (p *Provider) savePlaylist(name string, tracks []playlist.Track) error {
if err := os.MkdirAll(p.dir, 0o755); err != nil {
return err
}
@@ -161,7 +161,7 @@ func (p *Provider) RemoveTrack(name string, index int) error {
if len(tracks) == 0 {
return p.DeletePlaylist(name)
}
return p.SavePlaylist(name, tracks)
return p.savePlaylist(name, tracks)
}
// writeTrack writes a single [[track]] TOML section to w.
+2 -2
View File
@@ -439,7 +439,7 @@ func (p *SpotifyProvider) NewStreamer(uri string) (beep.StreamSeekCloser, beep.F
}
}
streamer := NewSpotifyStreamer(stream)
streamer := newSpotifyStreamer(stream)
return streamer, streamer.Format(), streamer.Duration(), nil
}
@@ -447,7 +447,7 @@ func (p *SpotifyProvider) NewStreamer(uri string) (beep.StreamSeekCloser, beep.F
func (p *SpotifyProvider) webAPI(ctx context.Context, method, path string, query url.Values) (*http.Response, error) {
const maxRetries = 8
for attempt := range maxRetries {
resp, err := p.session.WebApi(ctx, method, path, query)
resp, err := p.session.webApi(ctx, method, path, query)
if err != nil {
return nil, err
}
+3 -3
View File
@@ -333,10 +333,10 @@ func (s *Session) NewStream(ctx context.Context, spotID librespot.SpotifyId, bit
return s.player.NewStream(ctx, http.DefaultClient, spotID, bitrate, 0)
}
// WebApi calls the Spotify Web API using the OAuth2 access token.
// webApi calls the Spotify Web API using the OAuth2 access token.
// This is the standard Web API token (not go-librespot's internal spclient token),
// which has proper rate limits for api.spotify.com endpoints.
func (s *Session) WebApi(ctx context.Context, method, path string, query url.Values) (*http.Response, error) {
func (s *Session) webApi(ctx context.Context, method, path string, query url.Values) (*http.Response, error) {
s.mu.Lock()
ts := s.tokenSource
s.mu.Unlock()
@@ -394,7 +394,7 @@ func (s *Session) Close() {
//
// The new session is established before tearing down the old one to avoid a
// window where s.sess/s.player are nil (which would crash concurrent callers
// like NewStream or WebApi).
// like NewStream or webApi).
func (s *Session) Reconnect(ctx context.Context) error {
// Capture clientID without holding the lock during the (potentially long)
// interactive OAuth2 flow.
+13 -13
View File
@@ -17,10 +17,10 @@ const (
spotifyChannels = 2
)
// SpotifyStreamer bridges a go-librespot AudioSource to beep.StreamSeekCloser.
// spotifyStreamer bridges a go-librespot AudioSource to beep.StreamSeekCloser.
// go-librespot outputs interleaved stereo float32 at 44100Hz; this converts
// to Beep's [][2]float64 sample format.
type SpotifyStreamer struct {
type spotifyStreamer struct {
source librespot.AudioSource
stream *librespotPlayer.Stream
buf []float32
@@ -28,13 +28,13 @@ type SpotifyStreamer struct {
err error
}
// NewSpotifyStreamer wraps a go-librespot Stream as a beep.StreamSeekCloser.
func NewSpotifyStreamer(stream *librespotPlayer.Stream) *SpotifyStreamer {
// newSpotifyStreamer wraps a go-librespot Stream as a beep.StreamSeekCloser.
func newSpotifyStreamer(stream *librespotPlayer.Stream) *spotifyStreamer {
var dur int64
if stream.Media != nil {
dur = int64(stream.Media.Duration())
}
return &SpotifyStreamer{
return &spotifyStreamer{
source: stream.Source,
stream: stream,
durationMs: dur,
@@ -43,7 +43,7 @@ func NewSpotifyStreamer(stream *librespotPlayer.Stream) *SpotifyStreamer {
// Stream reads interleaved float32 from the AudioSource and converts to
// [][2]float64 stereo pairs for Beep's audio pipeline.
func (s *SpotifyStreamer) Stream(samples [][2]float64) (n int, ok bool) {
func (s *spotifyStreamer) Stream(samples [][2]float64) (n int, ok bool) {
// Each stereo sample pair needs 2 float32 values (L, R).
needed := len(samples) * spotifyChannels
if len(s.buf) < needed {
@@ -72,20 +72,20 @@ func (s *SpotifyStreamer) Stream(samples [][2]float64) (n int, ok bool) {
return pairs, true
}
func (s *SpotifyStreamer) Err() error { return s.err }
func (s *spotifyStreamer) Err() error { return s.err }
// Len returns the total number of sample pairs (at 44100Hz stereo).
func (s *SpotifyStreamer) Len() int {
func (s *spotifyStreamer) Len() int {
return int(s.durationMs * spotifySampleRate / 1000)
}
// Position returns the current playback position in sample pairs.
func (s *SpotifyStreamer) Position() int {
func (s *spotifyStreamer) Position() int {
return int(s.source.PositionMs() * spotifySampleRate / 1000)
}
// Seek moves to sample position p (in sample pairs at 44100Hz).
func (s *SpotifyStreamer) Seek(p int) error {
func (s *spotifyStreamer) Seek(p int) error {
ms := int64(p) * 1000 / spotifySampleRate
return s.source.SetPositionMs(ms)
}
@@ -96,12 +96,12 @@ func (s *SpotifyStreamer) Seek(p int) error {
// reader and decryption pipeline will be released when the object is GC'd.
// This is a known limitation for skipped tracks until go-librespot exposes
// Close() on the AudioSource interface.
func (s *SpotifyStreamer) Close() error {
func (s *spotifyStreamer) Close() error {
return nil
}
// Format returns the Beep audio format for Spotify streams.
func (s *SpotifyStreamer) Format() beep.Format {
func (s *spotifyStreamer) Format() beep.Format {
return beep.Format{
SampleRate: beep.SampleRate(spotifySampleRate),
NumChannels: spotifyChannels,
@@ -110,6 +110,6 @@ func (s *SpotifyStreamer) Format() beep.Format {
}
// Duration returns the track duration.
func (s *SpotifyStreamer) Duration() time.Duration {
func (s *spotifyStreamer) Duration() time.Duration {
return time.Duration(s.durationMs) * time.Millisecond
}
+4 -4
View File
@@ -132,7 +132,7 @@ func fetchLRCLIB(query string) ([]Line, error) {
// Prefer synced lyrics.
for _, r := range results {
if r.SyncedLyrics != "" {
return ParseLRC(r.SyncedLyrics), nil
return parseLRC(r.SyncedLyrics), nil
}
}
@@ -198,11 +198,11 @@ func fetchNetEase(query string) ([]Line, error) {
return nil, ErrNotFound
}
return ParseLRC(lyricRes.Lrc.Lyric), nil
return parseLRC(lyricRes.Lrc.Lyric), nil
}
// ParseLRC converts standard LRC string blocks into a slice of timestamped Lines.
func ParseLRC(data string) []Line {
// parseLRC converts standard LRC string blocks into a slice of timestamped Lines.
func parseLRC(data string) []Line {
var lines []Line
for _, raw := range strings.Split(data, "\n") {
matches := lrcRegex.FindStringSubmatch(raw)
+2 -2
View File
@@ -7,8 +7,8 @@ import (
"github.com/gopxl/beep/v2"
)
// EQFreqs are the center frequencies for the 10-band parametric equalizer.
var EQFreqs = [10]float64{70, 180, 320, 600, 1000, 3000, 6000, 12000, 14000, 16000}
// eqFreqs are the center frequencies for the 10-band parametric equalizer.
var eqFreqs = [10]float64{70, 180, 320, 600, 1000, 3000, 6000, 12000, 14000, 16000}
// biquad implements a second-order IIR peaking equalizer per the Audio EQ Cookbook.
// Each filter reads its gain from a shared pointer, so EQ changes take
+3 -3
View File
@@ -40,7 +40,7 @@ type Player struct {
ctrl *beep.Ctrl
volume atomic.Uint64 // dB stored as Float64bits, range [-30, +6]
eqBands [10]atomic.Uint64 // dB stored as math.Float64bits
tap *Tap
tap *tap
playing atomic.Bool
paused atomic.Bool
mono atomic.Bool
@@ -164,11 +164,11 @@ func (p *Player) playPipeline(tp *trackPipeline) error {
var s beep.Streamer = p.gapless
for i := range 10 {
s = newBiquad(s, EQFreqs[i], 1.4, &p.eqBands[i], float64(p.sr))
s = newBiquad(s, eqFreqs[i], 1.4, &p.eqBands[i], float64(p.sr))
}
s = &volumeStreamer{s: s, vol: &p.volume, mono: &p.mono, cachedDB: math.NaN()}
p.tap = NewTap(s, 4096)
p.tap = newTap(s, 4096)
p.ctrl = &beep.Ctrl{Streamer: p.tap}
p.started = true
p.playing.Store(true)
+9 -9
View File
@@ -8,7 +8,7 @@ import (
"github.com/gopxl/beep/v2"
)
// Tap is a streamer wrapper that copies samples into a ring buffer
// tap is a streamer wrapper that copies samples into a ring buffer
// for real-time FFT visualization. It sits in the audio pipeline
// between the volume control and the speaker controller.
//
@@ -16,16 +16,16 @@ import (
// (sole writer) and the UI thread (infrequent reader at 50ms intervals)
// to operate without mutex contention. Minor sample tearing at the
// read boundary is invisible in FFT-based spectrum visualization.
type Tap struct {
type tap struct {
s beep.Streamer
buf []float64
pos atomic.Int64
size int
}
// NewTap wraps a streamer with a ring buffer of the given size.
func NewTap(s beep.Streamer, bufSize int) *Tap {
return &Tap{
// newTap wraps a streamer with a ring buffer of the given size.
func newTap(s beep.Streamer, bufSize int) *tap {
return &tap{
s: s,
buf: make([]float64, bufSize),
size: bufSize,
@@ -33,7 +33,7 @@ func NewTap(s beep.Streamer, bufSize int) *Tap {
}
// Stream passes audio through while capturing a mono mix into the ring buffer.
func (t *Tap) Stream(samples [][2]float64) (int, bool) {
func (t *tap) Stream(samples [][2]float64) (int, bool) {
n, ok := t.s.Stream(samples)
p := int(t.pos.Load())
for i := range n {
@@ -45,12 +45,12 @@ func (t *Tap) Stream(samples [][2]float64) (int, bool) {
}
// Err returns the underlying streamer's error.
func (t *Tap) Err() error {
func (t *tap) Err() error {
return t.s.Err()
}
// Samples returns the last n samples from the ring buffer in chronological order.
func (t *Tap) Samples(n int) []float64 {
func (t *tap) Samples(n int) []float64 {
if n > t.size {
n = t.size
}
@@ -65,7 +65,7 @@ func (t *Tap) Samples(n int) []float64 {
// SamplesInto copies the last len(dst) samples into dst, avoiding allocation.
// Returns the number of samples written.
func (t *Tap) SamplesInto(dst []float64) int {
func (t *tap) SamplesInto(dst []float64) int {
n := len(dst)
if n > t.size {
n = t.size
+1 -1
View File
@@ -205,7 +205,7 @@ func TrackFromPath(path string) Track {
if IsURL(path) {
return trackFromURL(path)
}
return ReadTags(path)
return readTags(path)
}
// trackFromURL creates a Track from an HTTP/HTTPS URL, extracting a clean
+2 -2
View File
@@ -8,10 +8,10 @@ import (
"github.com/dhowden/tag"
)
// ReadTags reads embedded metadata (ID3v2, Vorbis comments, MP4 atoms) from
// readTags reads embedded metadata (ID3v2, Vorbis comments, MP4 atoms) from
// a local audio file and returns a Track. Falls back to filename parsing if
// tag reading fails or the tags contain no title.
func ReadTags(path string) Track {
func readTags(path string) Track {
f, err := os.Open(path)
if err != nil {
return trackFromFilename(path)
+2 -2
View File
@@ -121,10 +121,10 @@ func entriesToTracks(entries []m3uEntry) []playlist.Track {
return tracks
}
// ResolveLocalM3U opens a local .m3u/.m3u8 file, parses it with EXTINF
// resolveLocalM3U opens a local .m3u/.m3u8 file, parses it with EXTINF
// metadata, and returns the resulting tracks. Relative paths in the M3U
// are resolved against the directory containing the M3U file.
func ResolveLocalM3U(path string) ([]playlist.Track, error) {
func resolveLocalM3U(path string) ([]playlist.Track, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
+2 -2
View File
@@ -139,9 +139,9 @@ func stripMirrorSuffix(s string) string {
return s
}
// ResolveLocalPLS opens a local .pls file, parses it, and returns the
// resolveLocalPLS opens a local .pls file, parses it, and returns the
// resulting tracks.
func ResolveLocalPLS(path string) ([]playlist.Track, error) {
func resolveLocalPLS(path string) ([]playlist.Track, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
+2 -2
View File
@@ -71,7 +71,7 @@ func Args(args []string) (Result, error) {
}
for _, path := range matches {
if playlist.IsLocalM3U(path) {
tracks, err := ResolveLocalM3U(path)
tracks, err := resolveLocalM3U(path)
if err != nil {
return r, fmt.Errorf("loading m3u %s: %w", path, err)
}
@@ -79,7 +79,7 @@ func Args(args []string) (Result, error) {
continue
}
if playlist.IsLocalPLS(path) {
tracks, err := ResolveLocalPLS(path)
tracks, err := resolveLocalPLS(path)
if err != nil {
return r, fmt.Errorf("loading pls %s: %w", path, err)
}