package converter import ( "errors" "fmt" "os" "path/filepath" "strings" "sub-cli/internal/format/lrc" "sub-cli/internal/format/srt" "sub-cli/internal/model" ) // ErrUnsupportedFormat is returned when trying to convert to/from an unsupported format var ErrUnsupportedFormat = errors.New("unsupported format") // Convert converts a file from one format to another func Convert(sourceFile, targetFile string) error { sourceFmt := strings.TrimPrefix(filepath.Ext(sourceFile), ".") targetFmt := strings.TrimPrefix(filepath.Ext(targetFile), ".") switch sourceFmt { case "lrc": return convertFromLRC(sourceFile, targetFile, targetFmt) case "srt": return convertFromSRT(sourceFile, targetFile, targetFmt) default: return fmt.Errorf("%w: %s", ErrUnsupportedFormat, sourceFmt) } } // convertFromLRC converts an LRC file to another format func convertFromLRC(sourceFile, targetFile, targetFmt string) error { sourceLyrics, err := lrc.Parse(sourceFile) if err != nil { return fmt.Errorf("error parsing source LRC file: %w", err) } switch targetFmt { case "txt": return lrcToTxt(sourceLyrics, targetFile) case "srt": return lrcToSRT(sourceLyrics, targetFile) case "lrc": return lrc.Generate(sourceLyrics, targetFile) default: return fmt.Errorf("%w: %s", ErrUnsupportedFormat, targetFmt) } } // convertFromSRT converts an SRT file to another format func convertFromSRT(sourceFile, targetFile, targetFmt string) error { entries, err := srt.Parse(sourceFile) if err != nil { return fmt.Errorf("error parsing source SRT file: %w", err) } switch targetFmt { case "txt": return srtToTxt(entries, targetFile) case "lrc": lyrics := srt.ConvertToLyrics(entries) return lrc.Generate(lyrics, targetFile) case "srt": return srt.Generate(entries, targetFile) default: return fmt.Errorf("%w: %s", ErrUnsupportedFormat, targetFmt) } } // lrcToTxt converts LRC lyrics to a plain text file func lrcToTxt(lyrics model.Lyrics, targetFile string) error { file, err := os.Create(targetFile) if err != nil { return fmt.Errorf("error creating target file: %w", err) } defer file.Close() for _, content := range lyrics.Content { if _, err := fmt.Fprintln(file, content); err != nil { return err } } return nil } // lrcToSRT converts LRC lyrics to an SRT file func lrcToSRT(lyrics model.Lyrics, targetFile string) error { var entries []model.SRTEntry for i, content := range lyrics.Content { if i >= len(lyrics.Timeline) { break } startTime := lyrics.Timeline[i] endTime := startTime // If there's a next timeline entry, use it for end time // Otherwise add a few seconds to the start time if i+1 < len(lyrics.Timeline) { endTime = lyrics.Timeline[i+1] } else { endTime.Seconds += 3 } entry := model.SRTEntry{ Number: i + 1, StartTime: startTime, EndTime: endTime, Content: content, } entries = append(entries, entry) } return srt.Generate(entries, targetFile) } // srtToTxt converts SRT entries to a plain text file func srtToTxt(entries []model.SRTEntry, targetFile string) error { file, err := os.Create(targetFile) if err != nil { return fmt.Errorf("error creating target file: %w", err) } defer file.Close() for _, entry := range entries { if _, err := fmt.Fprintln(file, entry.Content); err != nil { return err } } return nil }