@@ -14,6 +14,7 @@ import (
1414 "github.com/DatanoiseTV/tinyice/config"
1515 "github.com/dhowden/tag"
1616 "github.com/hajimehoshi/go-mp3"
17+ "github.com/mikkyang/id3-go"
1718 "github.com/sirupsen/logrus"
1819)
1920
@@ -48,6 +49,7 @@ type Streamer struct {
4849
4950 fileCancel context.CancelFunc
5051 titleCache map [string ]string
52+ titleFetchWg sync.WaitGroup
5153
5254 // Stats
5355 BytesStreamed int64
@@ -114,6 +116,12 @@ func (s *Streamer) ToggleInjectMetadata() {
114116 s .InjectMetadata = ! s .InjectMetadata
115117}
116118
119+ func (s * Streamer ) ClearQueue () {
120+ s .mu .Lock ()
121+ defer s .mu .Unlock ()
122+ s .Queue = []string {}
123+ }
124+
117125func (s * Streamer ) Stop () {
118126 s .mu .Lock ()
119127 defer s .mu .Unlock ()
@@ -149,8 +157,18 @@ func (s *Streamer) RemoveFromQueue(index int) {
149157 s .Queue = append (s .Queue [:index ], s .Queue [index + 1 :]... )
150158}
151159
152- type PlaylistItem struct {
153- Title string
160+ func (s * Streamer ) MoveQueueItem (from , to int ) {
161+ s .mu .Lock ()
162+ defer s .mu .Unlock ()
163+ if from < 0 || from >= len (s .Queue ) || to < 0 || to >= len (s .Queue ) {
164+ return
165+ }
166+ item := s .Queue [from ]
167+ s .Queue = append (s .Queue [:from ], s .Queue [from + 1 :]... )
168+ s .Queue = append (s .Queue [:to ], append ([]string {item }, s .Queue [to :]... )... )
169+ }
170+
171+ type PlaylistItem struct { Title string
154172 Path string
155173}
156174
@@ -172,31 +190,50 @@ func (s *Streamer) GetPlaylistInfo() []PlaylistItem {
172190
173191func (s * Streamer ) GetSongTitle (path string ) string {
174192 s .mu .RLock ()
175- defer s .mu .RUnlock ()
176193 if title , ok := s .titleCache [path ]; ok {
194+ s .mu .RUnlock ()
177195 return title
178196 }
197+ s .mu .RUnlock ()
198+
199+ // Trigger background fetch if not already in progress
200+ go s .fetchTitleAndCache (path )
201+
202+ // Fallback to filename if no title found (yet)
179203 return filepath .Base (path )
180204}
181205
182- func (s * Streamer ) fetchTitleAndCache (path string ) string {
183- title := filepath .Base (path )
184- f , err := os .Open (path )
185- if err == nil {
186- if m , err := tag .ReadFrom (f ); err == nil {
187- if m .Artist () != "" && m .Title () != "" {
188- title = fmt .Sprintf ("%s - %s" , m .Artist (), m .Title ())
189- } else if m .Title () != "" {
190- title = m .Title ()
206+ func (s * Streamer ) fetchTitleAndCache (path string ) {
207+ s .titleFetchWg .Add (1 )
208+ go func () {
209+ defer s .titleFetchWg .Done ()
210+
211+ s .mu .Lock ()
212+ if _ , ok := s .titleCache [path ]; ok {
213+ s .mu .Unlock ()
214+ return // Already fetched by another concurrent call
215+ }
216+ s .mu .Unlock ()
217+
218+ title := filepath .Base (path )
219+
220+ // Use id3-go for extraction
221+ if file , err := id3 .Open (path ); err == nil {
222+ defer file .Close ()
223+ artist := strings .TrimSpace (file .Artist ())
224+ song := strings .TrimSpace (file .Title ())
225+
226+ if artist != "" && song != "" {
227+ title = fmt .Sprintf ("%s - %s" , artist , song )
228+ } else if song != "" {
229+ title = song
191230 }
192231 }
193- f .Close ()
194- }
195232
196- s .mu .Lock ()
197- s .titleCache [path ] = title
198- s .mu .Unlock ()
199- return title
233+ s .mu .Lock ()
234+ s .titleCache [path ] = title
235+ s .mu .Unlock ()
236+ }()
200237}
201238
202239func (s * Streamer ) GetQueueInfo () []PlaylistItem {
0 commit comments