Skip to content
This repository has been archived by the owner on Aug 29, 2023. It is now read-only.

Commit

Permalink
feat: Add Audio support (#62)
Browse files Browse the repository at this point in the history
* add AddAudio to epub

* add unit tests to epub

* add writeAudio

* add writeAudioTest

* add test audio file

* fix: AddAudio function parameter name

* change sample audio file to reduce size

* test: Use smaller test audio file

... and add NOTICE

---------

Co-authored-by: Barış Hasdemir <[email protected]>
Co-authored-by: bmaupin <[email protected]>
  • Loading branch information
3 people authored May 2, 2023
1 parent 74b49e5 commit 5d94be3
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 1 deletion.
5 changes: 5 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
The following files are from or contain content from external sources:

testdata/sample_audio.wav
Source: https://cgit.freedesktop.org/libreoffice/core/tree/extras/source/gallery/sounds/laser.wav
License: Mozilla Public License Version 2.0
23 changes: 22 additions & 1 deletion epub.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ Basic usage:
if err != nil {
// handle error
}
*/
package epub

Expand Down Expand Up @@ -78,6 +77,7 @@ const (
FontFolderName = "fonts"
ImageFolderName = "images"
VideoFolderName = "videos"
AudioFolderName = "audios"
)

const (
Expand Down Expand Up @@ -106,6 +106,7 @@ img {
videoFileFormat = "video%04d%s"
sectionFileFormat = "section%04d.xhtml"
urnUUIDPrefix = "urn:uuid:"
audioFileFormat = "audio%04d%s"
)

// Epub implements an EPUB file.
Expand All @@ -123,6 +124,8 @@ type Epub struct {
images map[string]string
// The key is the video filename, the value is the video source
videos map[string]string
// The key is the audio filename, the value is the audio source
audios map[string]string
// Language
lang string
// Description
Expand Down Expand Up @@ -164,6 +167,7 @@ func NewEpub(title string) *Epub {
e.fonts = make(map[string]string)
e.images = make(map[string]string)
e.videos = make(map[string]string)
e.audios = make(map[string]string)
e.pkg = newPackage()
e.toc = newToc()
// Set minimal required attributes
Expand Down Expand Up @@ -246,6 +250,23 @@ func (e *Epub) AddVideo(source string, videoFilename string) (string, error) {
return addMedia(e.Client, source, videoFilename, videoFileFormat, VideoFolderName, e.videos)
}

// AddAudio adds an audio to the EPUB and returns a relative path to the audio
// file that can be used in EPUB sections in the format:
// ../AudioFolderName/internalFilename
//
// The audio source should either be a URL, a path to a local file, or an embedded data URL; in any
// case, the audio file will be retrieved and stored in the EPUB.
//
// The internal filename will be used when storing the audio file in the EPUB
// and must be unique among all audio files. If the same filename is used more
// than once, FilenameAlreadyUsedError will be returned. The internal filename is
// optional; if no filename is provided, one will be generated.
func (e *Epub) AddAudio(source string, audioFilename string) (string, error) {
e.Lock()
defer e.Unlock()
return addMedia(e.Client, source, audioFilename, audioFileFormat, AudioFolderName, e.audios)
}

// AddSection adds a new section (chapter, etc) to the EPUB and returns a
// relative path to the section that can be used from another section (for
// links).
Expand Down
54 changes: 54 additions & 0 deletions epub_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ const (
testVideoFromFileFilename = "testfromfile.mp4"
testVideoFromFileSource = "testdata/sample_640x360.mp4"
testVideoFromURLSource = "https://filesamples.com/samples/video/mp4/sample_640x360.mp4"
testAudioFromFileFilename = "sample_audio.wav"
testAudioFromFileSource = "testdata/sample_audio.wav"
testAudioFromURLSource = "https://file-examples.com/storage/fe644084cb644d3709528c4/2017/11/file_example_WAV_1MG.wav"
testLangTemplate = `<dc:language>%s</dc:language>`
testDescTemplate = `<dc:description>%s</dc:description>`
testPpdTemplate = `page-progression-direction="%s"`
Expand Down Expand Up @@ -351,6 +354,56 @@ func TestAddVideo(t *testing.T) {
cleanup(testEpubFilename, tempDir)
}

func TestAddAudio(t *testing.T) {
e := NewEpub(testEpubTitle)
testAudioFromFilePath, err := e.AddAudio(testAudioFromFileSource, testAudioFromFileFilename)
if err != nil {
t.Errorf("Error adding audio: %s", err)
}
fmt.Println(testAudioFromFilePath)

testAudioFromURLPath, err := e.AddAudio(testAudioFromURLSource, "")
if err != nil {
t.Errorf("Error adding audio: %s", err)
}
fmt.Println(testAudioFromURLPath)

tempDir := writeAndExtractEpub(t, e, testEpubFilename)

// The audio path is relative to the XHTML folder
contents, err := storage.ReadFile(filesystem, filepath.Join(tempDir, contentFolderName, xhtmlFolderName, testAudioFromFilePath))
if err != nil {
t.Errorf("Unexpected error reading audio file from EPUB: %s", err)
}

testAudioContents, err := os.ReadFile(testAudioFromFileSource)
if err != nil {
t.Errorf("Unexpected error reading testdata audio file: %s", err)
}
if bytes.Compare(contents, testAudioContents) != 0 {
t.Errorf("Audio file contents don't match")
}

contents, err = storage.ReadFile(filesystem, filepath.Join(tempDir, contentFolderName, xhtmlFolderName, testAudioFromURLPath))
if err != nil {
t.Errorf("Unexpected error reading audio file from EPUB: %s", err)
}

resp, err := http.Get(testAudioFromURLSource)
if err != nil {
t.Errorf("Unexpected error response from test audio URL: %s", err)
}
testAudioContents, err = ioutil.ReadAll(resp.Body)
if err != nil {
t.Errorf("Unexpected error reading test audio file from URL: %s", err)
}
if bytes.Compare(contents, testAudioContents) != 0 {
t.Errorf("Audio file contents don't match")
}

cleanup(testEpubFilename, tempDir)
}

func TestAddSection(t *testing.T) {
e := NewEpub(testEpubTitle)
testSection1Path, err := e.AddSection(testSectionBody, testSectionTitle, testSectionFilename, "")
Expand Down Expand Up @@ -797,6 +850,7 @@ func testEpubValidity(t testing.TB) {
e.AddImage(testImageFromURLSource, "")
e.AddImage(testImageFromFileSource, testNumberFilenameStart)
e.AddVideo(testVideoFromURLSource, testVideoFromFileFilename)
e.AddAudio(testAudioFromURLSource, testAudioFromFileFilename)
e.SetAuthor(testEpubAuthor)
e.SetCover(testImagePath, "")
e.SetDescription(testEpubDescription)
Expand Down
Binary file added testdata/sample_audio.wav
Binary file not shown.
13 changes: 13 additions & 0 deletions write.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,13 @@ func (e *Epub) WriteTo(dst io.Writer) (int64, error) {
return 0, err
}

// Must be called after:
// createEpubFolders()
err = e.writeAudios(tempDir)
if err != nil {
return 0, err
}

// Must be called after:
// createEpubFolders()
e.writeSections(tempDir)
Expand All @@ -116,6 +123,7 @@ func (e *Epub) WriteTo(dst io.Writer) (int64, error) {
// writeCSSFiles()
// writeImages()
// writeVideos()
// writeAudios()
// writeSections()
// writeToc()
e.writePackageFile(tempDir)
Expand Down Expand Up @@ -335,6 +343,11 @@ func (e *Epub) writeVideos(rootEpubDir string) error {
return e.writeMedia(rootEpubDir, e.videos, VideoFolderName)
}

// Get audios from their source and save them in the temporary directory
func (e *Epub) writeAudios(rootEpubDir string) error {
return e.writeMedia(rootEpubDir, e.audios, AudioFolderName)
}

// Get media from their source and save them in the temporary directory
func (e *Epub) writeMedia(rootEpubDir string, mediaMap map[string]string, mediaFolderName string) error {
if len(mediaMap) > 0 {
Expand Down
4 changes: 4 additions & 0 deletions write_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ func TestWriteToErrors(t *testing.T) {
e := NewEpub(testEpubTitle)
testWriteToErrors(t, e, e.AddVideo, "sample_640x360.mp4")
})
t.Run("Audio", func(t *testing.T) {
e := NewEpub(testEpubTitle)
testWriteToErrors(t, e, e.AddAudio, "sample_audio.wav")
})
}

func testWriteToErrors(t *testing.T, e *Epub, adder func(string, string) (string, error), name string) {
Expand Down

0 comments on commit 5d94be3

Please sign in to comment.