62 lines
1.7 KiB
Go
62 lines
1.7 KiB
Go
|
package sniff
|
||
|
|
||
|
import "bytes"
|
||
|
|
||
|
type Sniffer interface {
|
||
|
Sniff(header []byte) bool
|
||
|
}
|
||
|
|
||
|
var audioExtensions = map[string]Sniffer{
|
||
|
// ref: https://mimesniff.spec.whatwg.org
|
||
|
".mp3": prefixSniffer("ID3"),
|
||
|
".ogg": prefixSniffer("OggS"),
|
||
|
".wav": prefixSniffer("RIFF"),
|
||
|
|
||
|
// ref: https://www.loc.gov/preservation/digital/formats/fdd/fdd000027.shtml
|
||
|
".wma": prefixSniffer{
|
||
|
0x30, 0x26, 0xb2, 0x75, 0x8e, 0x66, 0xcf, 0x11,
|
||
|
0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c,
|
||
|
},
|
||
|
|
||
|
// ref: https://www.garykessler.net/library/file_sigs.html
|
||
|
".m4a": mpeg4Sniffer{}, // MPEG-4 container, m4a treat as audio
|
||
|
".aac": prefixSniffer{0xFF, 0xF1}, // MPEG-4 AAC-LC
|
||
|
|
||
|
".flac": prefixSniffer("fLaC"), // ref: https://xiph.org/flac/format.html
|
||
|
".dff": prefixSniffer("FRM8"), // DSDIFF, ref: https://www.sonicstudio.com/pdf/dsd/DSDIFF_1.5_Spec.pdf
|
||
|
|
||
|
}
|
||
|
|
||
|
// AudioExtension sniffs the known audio types, and returns the file extension.
|
||
|
// header is recommended to at least 16 bytes.
|
||
|
func AudioExtension(header []byte) (string, bool) {
|
||
|
for ext, sniffer := range audioExtensions {
|
||
|
if sniffer.Sniff(header) {
|
||
|
return ext, true
|
||
|
}
|
||
|
}
|
||
|
return "", false
|
||
|
}
|
||
|
|
||
|
// AudioExtensionWithFallback is equivalent to AudioExtension, but returns fallback
|
||
|
// most likely to use .mp3 as fallback, because mp3 files may not have ID3v2 tag.
|
||
|
func AudioExtensionWithFallback(header []byte, fallback string) string {
|
||
|
ext, ok := AudioExtension(header)
|
||
|
if !ok {
|
||
|
return fallback
|
||
|
}
|
||
|
return ext
|
||
|
}
|
||
|
|
||
|
type prefixSniffer []byte
|
||
|
|
||
|
func (s prefixSniffer) Sniff(header []byte) bool {
|
||
|
return bytes.HasPrefix(header, s)
|
||
|
}
|
||
|
|
||
|
type mpeg4Sniffer struct{}
|
||
|
|
||
|
func (mpeg4Sniffer) Sniff(header []byte) bool {
|
||
|
return len(header) >= 8 && bytes.Equal([]byte("ftyp"), header[4:8])
|
||
|
}
|