From 34995e56a8b1a4723bc50500641478b00fe3db20 Mon Sep 17 00:00:00 2001 From: Chris Date: Tue, 7 Mar 2023 21:04:25 -0800 Subject: [PATCH 1/2] Refactor code to improve performance and readability. This commit replaces string concatenation with bytes.Buffer to improve efficiency in WriteTo and WriteSimpleTo. It also replaces fmt.Fprintf with strconv.AppendInt for writing integers and uses bufio.Scanner instead of bufio.NewReader for reading lines. --- go.mod | 5 ++ go.sum | 2 + m3u.go | 170 +++++++++++++++++++++++++++------------------------- m3u_test.go | 134 ++++++++++++++++++++--------------------- 4 files changed, 162 insertions(+), 149 deletions(-) create mode 100644 go.mod create mode 100644 go.sum diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..dae01b8 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module m3u + +go 1.19 + +require github.com/ushis/m3u v0.0.0-20150127162843-94396b784733 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..0129d6b --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/ushis/m3u v0.0.0-20150127162843-94396b784733 h1:m4zGEkIeft/gfUs469WS/gB6NT3RtkG8zQrOvCOzovE= +github.com/ushis/m3u v0.0.0-20150127162843-94396b784733/go.mod h1:/w56gU05vgM74JSy2/xFy6tUQ9vJBMiciHNvyIEU1UY= diff --git a/m3u.go b/m3u.go index 368bd7d..184fb40 100644 --- a/m3u.go +++ b/m3u.go @@ -20,18 +20,15 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -// This package implements a fault tolerant m3u parser and functions to write -// simple and extended m3u files. -// -// The spec can be found at http://www.scvi.net/pls.htm package m3u import ( - "bufio" - "fmt" - "io" - "strconv" - "strings" + "bufio" + "bytes" + "fmt" + "io" + "strconv" + "strings" ) // Represents a list of tracks. @@ -39,89 +36,98 @@ type Playlist []Track // Represents a single track. type Track struct { - Path string // path to the file - Title string // title of the track - Time int64 // duration of the track + Path string // path to the file + Title string // title of the track + Time int64 // duration of the track } // Parses simple and extended m3u files. Returns the playlist. func Parse(r io.Reader) (Playlist, error) { - br := bufio.NewReader(r) - pl := Playlist{} - - for { - line, err := br.ReadString('\n') - - if err != nil { - if err == io.EOF { - return pl, nil - } - return pl, err - } - line = line[:len(line)-1] - - if len(line) > 0 && line[0] != '#' { - pl = append(pl, Track{Path: line, Title: "", Time: -1}) - continue - } - - if len(line) > 8 && line[:8] == "#EXTINF:" { - i := strings.Index(line[8:], ",") - - if i < 0 { - return pl, fmt.Errorf("unexpected line: %q", line) - } - ftime, err := strconv.ParseFloat(line[8:i+8], 64) - - if err != nil { - return pl, err - } - time := int64(ftime) - path, err := br.ReadString('\n') - - if err != nil { - return pl, err - } - pl = append(pl, Track{Path: path[:len(path)-1], Title: line[i+9:], Time: time}) - } - } + scanner := bufio.NewScanner(r) + pl := Playlist{} + + for scanner.Scan() { + line := scanner.Text() + + if len(line) > 0 && line[0] != '#' { + pl = append(pl, Track{Path: line, Title: "", Time: -1}) + continue + } + + if len(line) > 8 && line[:8] == "#EXTINF:" { + i := strings.Index(line[8:], ",") + + if i < 0 { + return pl, fmt.Errorf("unexpected line: %q", line) + } + ftime, err := strconv.ParseFloat(line[8:i+8], 64) + + if err != nil { + return pl, err + } + time := int64(ftime) + scanner.Scan() + path := scanner.Text() + + if err := scanner.Err(); err != nil { + return pl, err + } + pl = append(pl, Track{Path: path, Title: line[i+9:], Time: time}) + } + } + if err := scanner.Err(); err != nil { + return pl, err + } + return pl, nil } // Writes the playlist to a writer in the extended m3u format. Returns the // number of written bytes. -func (pl Playlist) WriteTo(w io.Writer) (n int, err error) { - if n, err = fmt.Fprintln(w, "#EXTM3U"); err != nil { - return - } - - for _, t := range pl { - time := t.Time - - if time < 1 { - time = -1 - } - m, err := fmt.Fprintf(w, "#EXTINF:%d,%s\n%s\n", time, t.Title, t.Path) - - if err != nil { - return n, err - } - n += m - } - return +func (pl Playlist) WriteTo(w io.Writer) (int64, error) { + var buf bytes.Buffer + buf.WriteString("#EXTM3U\n") + + for _, t := range pl { + time := t.Time + + if time < 1 { + time = -1 + } + var b bytes.Buffer + strconv.AppendInt(b.Bytes(), time, 10) + buf.WriteString("#EXTINF:") + buf.Write(b.Bytes()) + buf.WriteString(",") + buf.WriteString(t.Title) + buf.WriteString("\n") + buf.WriteString(t.Path) + buf.WriteString("\n") + } + + // Write the buffer to the output. + n, err := buf.WriteTo(w) + if err != nil { + return 0, err + } + + return n, nil } // Writes the playlist to a writer in the simple m3u format. Returns the number // of written bytes. -func (pl Playlist) WriteSimpleTo(w io.Writer) (n int, err error) { - n = 0 - - for _, t := range pl { - m, err := fmt.Fprintln(w, t.Path) - - if err != nil { - return n, err - } - n += m - } - return +func (pl Playlist) WriteSimpleTo(w io.Writer) (int64, error) { + var buf bytes.Buffer + + for _, t := range pl { + buf.WriteString(t.Path) + buf.WriteString("\n") + } + + // Write the buffer to the output. + n, err := buf.WriteTo(w) + if err != nil { + return 0, err + } + + return n, nil } diff --git a/m3u_test.go b/m3u_test.go index 294f0cd..1c8726b 100644 --- a/m3u_test.go +++ b/m3u_test.go @@ -23,102 +23,102 @@ package m3u import ( - "bytes" - "io" - "os" - "testing" + "bytes" + "io" + "os" + "testing" ) var extended = Playlist{ - Track{ - Path: "Alternative\\everclear_SMFTA.mp3", - Title: "Everclear - So Much For The Afterglow", - Time: 233, - }, - Track{ - Path: "Comedy/Weird_Al_Everything_You_Know_Is_Wrong.mp3", - Title: "", - Time: 227, - }, - Track{ - Path: "Weird_Al_This_Is_The_Life.mp3", - Title: "Weird Al Yankovic - This is the Life", - Time: 187, - }, - Track{ - Path: "http://www.site.com/~user/gump.mp3", - Title: "Weird Al: Bad Hair Day - Gump", - Time: 129, - }, - Track{ - Path: "http://www.site.com:8000/listen.pls", - Title: "My Cool Stream", - Time: -1, - }, + Track{ + Path: "Alternative\\everclear_SMFTA.mp3", + Title: "Everclear - So Much For The Afterglow", + Time: 233, + }, + Track{ + Path: "Comedy/Weird_Al_Everything_You_Know_Is_Wrong.mp3", + Title: "", + Time: 227, + }, + Track{ + Path: "Weird_Al_This_Is_The_Life.mp3", + Title: "Weird Al Yankovic - This is the Life", + Time: 187, + }, + Track{ + Path: "http://www.site.com/~user/gump.mp3", + Title: "Weird Al: Bad Hair Day - Gump", + Time: 129, + }, + Track{ + Path: "http://www.site.com:8000/listen.pls", + Title: "My Cool Stream", + Time: -1, + }, } var simple = Playlist{ - Track{Time: -1, Title: "", Path: "Alternative\\everclear_SMFTA.mp3"}, - Track{Time: -1, Title: "", Path: "Comedy/Weird_Al_Everything_You_Know_Is_Wrong.mp3"}, - Track{Time: -1, Title: "", Path: "Weird_Al_This_Is_The_Life.mp3"}, - Track{Time: -1, Title: "", Path: "http://www.site.com/~user/gump.mp3"}, - Track{Time: -1, Title: "", Path: "http://www.site.com:8000/listen.pls"}, + Track{Time: -1, Title: "", Path: "Alternative\\everclear_SMFTA.mp3"}, + Track{Time: -1, Title: "", Path: "Comedy/Weird_Al_Everything_You_Know_Is_Wrong.mp3"}, + Track{Time: -1, Title: "", Path: "Weird_Al_This_Is_The_Life.mp3"}, + Track{Time: -1, Title: "", Path: "http://www.site.com/~user/gump.mp3"}, + Track{Time: -1, Title: "", Path: "http://www.site.com:8000/listen.pls"}, } func assertPlaylist(t *testing.T, a, b Playlist) { - if len(a) != len(b) { - t.Fatalf("Result: %v\nExpected: %v\n", a, b) - } - - for i, _ := range a { - if a[i].Path != b[i].Path || a[i].Title != b[i].Title || a[i].Time != b[i].Time { - t.Fatalf("\nResult: %v\nExpected: %v\n", a, b) - } - } + if len(a) != len(b) { + t.Fatalf("Result: %v\nExpected: %v\n", a, b) + } + + for i := range a { + if a[i].Path != b[i].Path || a[i].Title != b[i].Title || a[i].Time != b[i].Time { + t.Fatalf("\nResult: %v\nExpected: %v\n", a, b) + } + } } func parse(t *testing.T, path string) Playlist { - f, err := os.Open(path) + f, err := os.Open(path) - if err != nil { - t.Fatal(err) - } - defer f.Close() + if err != nil { + t.Fatal(err) + } + defer f.Close() - pl, err := Parse(f) + pl, err := Parse(f) - if err != nil { - t.Fatal(err) - } - return pl + if err != nil { + t.Fatal(err) + } + return pl } -func writeAndParse(t *testing.T, w func(io.Writer) (int, error)) Playlist { - var buf bytes.Buffer +func writeAndParse(t *testing.T, w func(io.Writer) (int64, error)) Playlist { + var buf bytes.Buffer - if _, err := w(&buf); err != nil { - t.Fatal(err) - } - pl, err := Parse(&buf) + if _, err := w(&buf); err != nil { + t.Fatal(err) + } + pl, err := Parse(&buf) - if err != nil { - t.Fatal(err) - } - return pl + if err != nil { + t.Fatal(err) + } + return pl } func TestParse(t *testing.T) { - assertPlaylist(t, parse(t, "testdata/extended.m3u"), extended) + assertPlaylist(t, parse(t, "testdata/extended.m3u"), extended) } func TestParseSimple(t *testing.T) { - assertPlaylist(t, parse(t, "testdata/simple.m3u"), simple) + assertPlaylist(t, parse(t, "testdata/simple.m3u"), simple) } func TestWriteTo(t *testing.T) { - assertPlaylist(t, writeAndParse(t, extended.WriteTo), extended) + assertPlaylist(t, writeAndParse(t, extended.WriteTo), extended) } func TestWriteSimpleTo(t *testing.T) { - assertPlaylist(t, writeAndParse(t, extended.WriteSimpleTo), simple) + assertPlaylist(t, writeAndParse(t, extended.WriteSimpleTo), simple) } From 705b28509fd3d27d5fda86e32457f888bc80abc0 Mon Sep 17 00:00:00 2001 From: Christopher Straight Date: Tue, 7 Mar 2023 21:14:39 -0800 Subject: [PATCH 2/2] Created LICENSE file --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c7e64f3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2013 Ushi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.