Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support CHAP frames #62

Merged
merged 24 commits into from Nov 23, 2021
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
52 changes: 23 additions & 29 deletions chapter_frame.go
Expand Up @@ -24,7 +24,7 @@ type ChapterFrame struct {
StartOffset uint32
EndOffset uint32
Title *TextFrame
SubTitle *TextFrame
Description *TextFrame
}

func (cf ChapterFrame) Size() int {
Expand All @@ -36,10 +36,10 @@ func (cf ChapterFrame) Size() int {
frameHeaderSize + // Title frame header size
cf.Title.Size()
}
if cf.SubTitle != nil {
if cf.Description != nil {
size = size +
frameHeaderSize + // SubTitle frame header size
cf.SubTitle.Size()
frameHeaderSize + // Description frame header size
cf.Description.Size()
}
return size
}
Expand All @@ -63,14 +63,15 @@ func (cf ChapterFrame) WriteTo(w io.Writer) (n int64, err error) {
writeFrame(bw, "TIT2", *cf.Title, true)
}

if cf.SubTitle != nil {
writeFrame(bw, "TIT3", cf.SubTitle, true)
if cf.Description != nil {
writeFrame(bw, "TIT3", *cf.Description, true)
}
})
}

func parseChapterFrame(br *bufReader) (Framer, error) {
func parseChapterFrame(br *bufReader, version byte) (Framer, error) {
ElementID := br.ReadText(EncodingISO)
n10v marked this conversation as resolved.
Show resolved Hide resolved
var synchSafe bool
var startTime uint32
var startOffset uint32
var endTime uint32
Expand All @@ -90,47 +91,40 @@ func parseChapterFrame(br *bufReader) (Framer, error) {
}

var title TextFrame
var subTitle TextFrame
var description TextFrame

// borrowed from parse.go
buf := getByteSlice(32 * 1024)
defer putByteSlice(buf)
if version == 4 {
synchSafe = true
} else {
synchSafe = false
}
n10v marked this conversation as resolved.
Show resolved Hide resolved
for {
// no way to determine whether this should be true or not
// this is likely should be fixed
header, err := parseFrameHeader(buf, br, true)
header, err := parseFrameHeader(buf, br, synchSafe)
if err == io.EOF || err == errBlankFrame || err == ErrInvalidSizeFormat {
break
}
if err != nil {
return nil, err
}
id, bodySize := header.ID, header.BodySize
if id == "TIT2" {
if id == "TIT2" || id == "TIT3" {
bodyRd := getLimitedReader(br, bodySize)
br2 := newBufReader(bodyRd)
frame, err := parseTextFrame(br2)
br := newBufReader(bodyRd)
frame, err := parseTextFrame(br)
if err != nil {
putLimitedReader(bodyRd)
return nil, err
}
title = frame.(TextFrame)

putLimitedReader(bodyRd)
break
}
if id == "TIT3" {
bodyRd := getLimitedReader(br, bodySize)
br3 := newBufReader(bodyRd)
frame, err := parseTextFrame(br3)
if err != nil {
putLimitedReader(bodyRd)
return nil, err
if id == "TIT2" {
title = frame.(TextFrame)
} else if id == "TIT3" {
description = frame.(TextFrame)
}
subTitle = frame.(TextFrame)

putLimitedReader(bodyRd)
break
}
n10v marked this conversation as resolved.
Show resolved Hide resolved
}

Expand All @@ -143,7 +137,7 @@ func parseChapterFrame(br *bufReader) (Framer, error) {
StartOffset: startOffset,
EndOffset: endOffset,
Title: &title,
SubTitle: &subTitle,
Description: &description,
}
return cf, nil
}
272 changes: 151 additions & 121 deletions chapter_frame_test.go
Expand Up @@ -29,129 +29,159 @@ func prepareTestFile() (*os.File, error) {
}

func TestAddChapterFrame(t *testing.T) {
tmpFile, err := prepareTestFile()
if err != nil {
t.Error(err)
}
defer os.Remove(tmpFile.Name())

tag, err := Open(tmpFile.Name(), Options{Parse: true})
if tag == nil || err != nil {
log.Fatal("Error while opening mp3 file: ", err)
}

chap := ChapterFrame{
ElementID: "chap0",
StartTime: 0,
EndTime: time.Duration(1000 * nanosInMillis),
StartOffset: 0,
EndOffset: 0,
}
tag.AddChapterFrame(chap)

if err := tag.Save(); err != nil {
t.Error(err)
}
tag.Close()

tag, err = Open(tmpFile.Name(), Options{Parse: true})
if tag == nil || err != nil {
log.Fatal("Error while opening mp3 file: ", err)
}
frame := tag.GetLastFrame("CHAP").(ChapterFrame)
if frame.ElementID != "chap0" {
t.Error(err)
}
if frame.StartTime != 0 {
t.Errorf("expected: %d, but got %s", 0, frame.StartTime)
}
if frame.EndTime.Seconds()*1000 != 1000 {
t.Errorf("expected: %d, but got %d", 1000, int(frame.EndTime.Seconds()*1000))
}
}

func TestAddChapterFrameWithTitle(t *testing.T) {
tmpFile, err := prepareTestFile()
if err != nil {
t.Error(err)
}
defer os.Remove(tmpFile.Name())

tag, err := Open(tmpFile.Name(), Options{Parse: true})
if tag == nil || err != nil {
log.Fatal("Error while opening mp3 file: ", err)
}

chap := ChapterFrame{
ElementID: "chap0",
StartTime: 0,
EndTime: 0,
StartOffset: 0,
EndOffset: 0,
Title: &TextFrame{
Encoding: EncodingUTF8,
Text: "chapter 0",
type fields struct {
ElementID string
StartTime time.Duration
EndTime time.Duration
StartOffset uint32
EndOffset uint32
Title *TextFrame
Description *TextFrame
}
tests := []struct {
name string
fields fields
wantElementId string
n10v marked this conversation as resolved.
Show resolved Hide resolved
wantTitle string
wantDescription string
}{
{
name: "element id only",
fields: fields{
ElementID: "chap0",
StartTime: 0,
EndTime: time.Duration(1000 * nanosInMillis),
StartOffset: 0,
EndOffset: 0,
},
wantElementId: "chap0",
wantTitle: "",
wantDescription: "",
},
}
tag.AddChapterFrame(chap)

if err := tag.Save(); err != nil {
t.Error(err)
}
tag.Close()

tag, err = Open(tmpFile.Name(), Options{Parse: true})
if tag == nil || err != nil {
log.Fatal("Error while opening mp3 file: ", err)
}
frame := tag.GetLastFrame("CHAP").(ChapterFrame)
if frame.ElementID != "chap0" {
t.Error(err)
}
if frame.Title.Text != "chapter 0" {
t.Errorf("expected: %s, but got %s", "chapter 0", frame.Title)
}
}

func TestAddChapterFrameWithSubTitle(t *testing.T) {
tmpFile, err := prepareTestFile()
if err != nil {
t.Error(err)
}
defer os.Remove(tmpFile.Name())

tag, err := Open(tmpFile.Name(), Options{Parse: true})
if tag == nil || err != nil {
log.Fatal("Error while opening mp3 file: ", err)
}

chap := ChapterFrame{
ElementID: "chap0",
StartTime: 0,
EndTime: 0,
StartOffset: 0,
EndOffset: 0,
SubTitle: &TextFrame{
Encoding: EncodingUTF8,
Text: "chapter 0",
{
name: "with title",
fields: fields{
ElementID: "chap0",
StartTime: 0,
EndTime: time.Duration(1000 * nanosInMillis),
StartOffset: 0,
EndOffset: 0,
Title: &TextFrame{
Encoding: EncodingUTF8,
Text: "chapter 0",
},
},
wantElementId: "chap0",
wantTitle: "chapter 0",
wantDescription: "",
},
{
name: "with description",
fields: fields{
ElementID: "chap0",
StartTime: 0,
EndTime: time.Duration(1000 * nanosInMillis),
StartOffset: 0,
EndOffset: 0,
Description: &TextFrame{
Encoding: EncodingUTF8,
Text: "chapter 0",
},
},
wantElementId: "chap0",
wantTitle: "",
wantDescription: "chapter 0",
},
{
name: "with title and description",
fields: fields{
ElementID: "chap0",
StartTime: 0,
EndTime: time.Duration(1000 * nanosInMillis),
StartOffset: 0,
EndOffset: 0,
Title: &TextFrame{
Encoding: EncodingUTF8,
Text: "chapter 0 title",
},
Description: &TextFrame{
Encoding: EncodingUTF8,
Text: "chapter 0 description",
},
},
wantElementId: "chap0",
wantTitle: "chapter 0 title",
wantDescription: "chapter 0 description",
},
{
name: "non-zero time and offset",
fields: fields{
ElementID: "chap0",
StartTime: time.Duration(1000 * nanosInMillis),
EndTime: time.Duration(1000 * nanosInMillis),
StartOffset: 10,
EndOffset: 10,
},
wantElementId: "chap0",
wantTitle: "",
wantDescription: "",
},
}
tag.AddChapterFrame(chap)

if err := tag.Save(); err != nil {
t.Error(err)
}
tag.Close()

tag, err = Open(tmpFile.Name(), Options{Parse: true})
if tag == nil || err != nil {
log.Fatal("Error while opening mp3 file: ", err)
}
frame := tag.GetLastFrame("CHAP").(ChapterFrame)
if frame.ElementID != "chap0" {
t.Error(err)
}
if frame.SubTitle.Text != "chapter 0" {
t.Errorf("expected: %s, but got %s", "chapter 0", frame.Title)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tmpFile, err := prepareTestFile()
if err != nil {
t.Error(err)
}
defer os.Remove(tmpFile.Name())

tag, err := Open(tmpFile.Name(), Options{Parse: true})
if tag == nil || err != nil {
log.Fatal("Error while opening mp3 file: ", err)
}

cf := ChapterFrame{
ElementID: tt.fields.ElementID,
StartTime: tt.fields.StartTime,
EndTime: tt.fields.EndTime,
StartOffset: tt.fields.StartOffset,
EndOffset: tt.fields.EndOffset,
Title: tt.fields.Title,
Description: tt.fields.Description,
}
tag.AddChapterFrame(cf)

if err := tag.Save(); err != nil {
t.Error(err)
}
tag.Close()

tag, err = Open(tmpFile.Name(), Options{Parse: true})
if tag == nil || err != nil {
log.Fatal("Error while opening mp3 file: ", err)
}
frame := tag.GetLastFrame("CHAP").(ChapterFrame)
if frame.ElementID != tt.wantElementId {
t.Errorf("expected: %s, but got %s", tt.wantElementId, frame.ElementID)
n10v marked this conversation as resolved.
Show resolved Hide resolved
}
if frame.Title.Text != tt.wantTitle {
n10v marked this conversation as resolved.
Show resolved Hide resolved
t.Errorf("expected: %s, but got %s", tt.wantTitle, frame.Title)
takaishi marked this conversation as resolved.
Show resolved Hide resolved
}
if frame.Description.Text != tt.wantDescription {
n10v marked this conversation as resolved.
Show resolved Hide resolved
t.Errorf("expected: %s, but got %s", tt.wantDescription, frame.Description.Text)
takaishi marked this conversation as resolved.
Show resolved Hide resolved
}
if frame.StartTime != tt.fields.StartTime {
t.Errorf("expected: %s, but got %s", tt.fields.StartTime, frame.StartTime)
takaishi marked this conversation as resolved.
Show resolved Hide resolved
}
if frame.EndTime != tt.fields.EndTime {
t.Errorf("expected: %s, but got %s", tt.fields.EndTime, frame.EndTime)
takaishi marked this conversation as resolved.
Show resolved Hide resolved
}
if frame.StartOffset != tt.fields.StartOffset {
t.Errorf("expected: %d, but got %d", tt.fields.StartOffset, frame.StartOffset)
takaishi marked this conversation as resolved.
Show resolved Hide resolved
}
if frame.EndOffset != tt.fields.EndOffset {
t.Errorf("expected: %d, but got %d", tt.fields.EndOffset, frame.EndOffset)
takaishi marked this conversation as resolved.
Show resolved Hide resolved
}
})
}
}
n10v marked this conversation as resolved.
Show resolved Hide resolved