package subsonic import ( "git.noroutes.net/NoRoutes/vibetunes/internal/database" ) func init() { registerExtension("songLyrics", 1, 2) register("getLyricsBySongId", handleGetLyricsBySongID) } // handleGetLyricsBySongID implements the songLyrics extension. It never // fakes cue-level timing: cueLine data is only emitted for entries that // were actually parsed from an Enhanced-LRC-style source with word/ // syllable cues, and only when the client asked for enhanced=true. func handleGetLyricsBySongID(rc *Context) *apiError { id, aerr := rc.Params.Required("id") if aerr != nil { return aerr } enhanced := rc.Params.Bool("enhanced", false) db := rc.Server.DB if _, err := database.GetSong(db, id); err != nil { return errNotFound("Song") } rows, err := database.GetSongStructuredLyrics(db, id) if err != nil { return newError(ErrGeneric, err.Error()) } list := &LyricsList{} for _, row := range rows { kind := "main" // every entry VibeSonic currently stores/ingests is a primary lyric layer, never a translation or pronunciation. if !enhanced && kind != "main" { continue } sl := StructuredLyrics{ Lang: row.Language, Synced: row.Synced, Line: make([]Line, 0, len(row.Lines)), } if enhanced { sl.Kind = kind } hasCues := false for _, l := range row.Lines { line := Line{Value: l.Text} if row.Synced && l.StartMs != nil { start := *l.StartMs line.Start = &start } sl.Line = append(sl.Line, line) if len(l.Words) > 0 { hasCues = true } } if enhanced && row.Synced && hasCues { sl.CueLine = make([]CueLine, 0, len(row.Lines)) for i, l := range row.Lines { if len(l.Words) == 0 { continue } cl := CueLine{Index: i, Value: l.Text} if l.StartMs != nil { cl.Start = *l.StartMs } cl.Cue = make([]Cue, 0, len(l.Words)) for _, w := range l.Words { cue := Cue{Start: w.StartMs, ByteStart: w.ByteStart, ByteEnd: w.ByteEnd, Value: w.Text} if w.EndMs != nil { end := *w.EndMs cue.End = &end } cl.Cue = append(cl.Cue, cue) } if last := l.Words[len(l.Words)-1]; last.EndMs != nil { cl.End = *last.EndMs } sl.CueLine = append(sl.CueLine, cl) } } list.StructuredLyrics = append(list.StructuredLyrics, sl) } rc.Response = newResponse() rc.Response.LyricsList = list return nil }