Add mp3 support + command line option to limit tracks + general redesign for more modularity
This commit is contained in:
		
							
								
								
									
										103
									
								
								vorbis/vorbis.go
									
									
									
									
									
								
							
							
						
						
									
										103
									
								
								vorbis/vorbis.go
									
									
									
									
									
								
							@@ -4,93 +4,78 @@ import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	ErrNoHeaderSegment               = errors.New("no header segment")
 | 
			
		||||
	ErrNoMetadata                    = errors.New("no metadata found")
 | 
			
		||||
	ErrCallReadRestAfterReadMetadata = errors.New("please call vorbis.Decoder.ReadRest() after having called vorbis.Decoder.ReadMetadata()")
 | 
			
		||||
	ErrReadMetadataCalledTwice       = errors.New("cannot call vorbis.Decoder.ReadMetadata() twice on the same file")
 | 
			
		||||
	ErrNoHeaderSegment = errors.New("vorbis: no header segment")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Decoder struct {
 | 
			
		||||
	r           io.Reader
 | 
			
		||||
type Extractor struct {
 | 
			
		||||
	hasMetadata bool
 | 
			
		||||
	metadata    *VorbisComment // Used for filename.
 | 
			
		||||
	checksum    uint32         // Used for an alternate filename when there's no metadata.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewDecoder(r io.Reader) *Decoder {
 | 
			
		||||
	return &Decoder{
 | 
			
		||||
		r: r,
 | 
			
		||||
	}
 | 
			
		||||
func NewExtractor() (*Extractor, error) {
 | 
			
		||||
	return new(Extractor), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *Decoder) readPage() (page OggPage, hdr VorbisHeader, err error) {
 | 
			
		||||
func (d *Extractor) ReadBlock(reader io.Reader, w io.Writer) (isFirst bool, err error) {
 | 
			
		||||
	// Everything we read here is part of the music data so we can just use a
 | 
			
		||||
	// tee reader.
 | 
			
		||||
	r := io.TeeReader(reader, w)
 | 
			
		||||
 | 
			
		||||
	// Decode page.
 | 
			
		||||
	page, err = OggDecode(d.r)
 | 
			
		||||
	page, err := OggDecode(r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return page, hdr, err
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We need to be able to access `page.Segments[0]`.
 | 
			
		||||
	if len(page.Segments) == 0 {
 | 
			
		||||
		return page, hdr, ErrNoHeaderSegment
 | 
			
		||||
		return false, ErrNoHeaderSegment
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Decode Vorbis header, stored in `page.Segments[0]`.
 | 
			
		||||
	hdr, err = VorbisHeaderDecode(bytes.NewBuffer(page.Segments[0]))
 | 
			
		||||
	hdr, err := VorbisHeaderDecode(bytes.NewBuffer(page.Segments[0]))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return page, hdr, err
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return page, hdr, nil
 | 
			
		||||
	// Extract potential metadata.
 | 
			
		||||
	if hdr.PackType == PackTypeComment {
 | 
			
		||||
		d.hasMetadata = true
 | 
			
		||||
		d.metadata = hdr.Comment
 | 
			
		||||
		d.checksum = page.Header.Checksum
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Return true for isFirst if this block is the beginning of a new file.
 | 
			
		||||
	return (page.Header.HeaderType & FHeaderTypeBOS) > 0, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Reads the Ogg/Vorbis file until it finds its metadata. Leaves the reader
 | 
			
		||||
// right after the end of the metadata. `crc32Sum` gives the crc32 checksum
 | 
			
		||||
// of the page containing the metadata. It is equivalent to the page checksum
 | 
			
		||||
// used in the Ogg container. Since the page contains more than just metadata,
 | 
			
		||||
// the checksum can usually be used as a unique identifier.
 | 
			
		||||
func (d *Decoder) ReadMetadata() (metadata *VorbisComment, crc32Sum uint32, err error) {
 | 
			
		||||
	if d.hasMetadata {
 | 
			
		||||
		return nil, 0, ErrReadMetadataCalledTwice
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		page, hdr, err := d.readPage()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, 0, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (page.Header.HeaderType & FHeaderTypeEOS) > 0 {
 | 
			
		||||
			// End of stream
 | 
			
		||||
			return nil, 0, ErrNoMetadata
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if hdr.PackType == PackTypeComment {
 | 
			
		||||
			d.hasMetadata = true
 | 
			
		||||
			return hdr.Comment, page.Header.Checksum, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Must to be called after `ReadMetadata()`. Reads the rest of the Ogg/Vorbis
 | 
			
		||||
// file, leaving the reader right after the end of the Ogg/Vorbis file.
 | 
			
		||||
func (d *Decoder) ReadRest() error {
 | 
			
		||||
func (d *Extractor) TryGetFilename() (filename string, hasFilename bool) {
 | 
			
		||||
	if !d.hasMetadata {
 | 
			
		||||
		return ErrCallReadRestAfterReadMetadata
 | 
			
		||||
		return "", false
 | 
			
		||||
	}
 | 
			
		||||
	d.hasMetadata = false
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		page, _, err := d.readPage()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (page.Header.HeaderType & FHeaderTypeEOS) > 0 {
 | 
			
		||||
			// End of stream
 | 
			
		||||
			break
 | 
			
		||||
	// Use relevant metadata to create a filename.
 | 
			
		||||
	var base string // Filename without extension.
 | 
			
		||||
	artist, artistOk := d.metadata.FieldByName("Artist")
 | 
			
		||||
	title, titleOk := d.metadata.FieldByName("Title")
 | 
			
		||||
	if artistOk || titleOk {
 | 
			
		||||
		if !artistOk {
 | 
			
		||||
			artist = "Unknown"
 | 
			
		||||
		} else if !titleOk {
 | 
			
		||||
			title = "Unknown"
 | 
			
		||||
		}
 | 
			
		||||
		base = artist + " -- " + title
 | 
			
		||||
	} else {
 | 
			
		||||
		base = "Unknown_" + strconv.FormatInt(int64(d.checksum), 10)
 | 
			
		||||
	}
 | 
			
		||||
	base = strings.ReplaceAll(base, "/", "_") // Replace invalid characters.
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
	return base + ".ogg", true
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user