You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
196 lines
5.2 KiB
196 lines
5.2 KiB
package converter
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/xfrr/goffmpeg/transcoder"
|
|
"k8s.io/klog"
|
|
)
|
|
|
|
type Converter struct {
|
|
*message
|
|
// albumFolder is populated at runtime and contains the album name
|
|
albumFolder string
|
|
// the "automatically add to iTunes.localized" directory
|
|
iTunesDir string
|
|
}
|
|
|
|
// CreateMessage is basically the reverse process to the conversion.
|
|
// instead of getting the message and using it, this function can
|
|
// read a directory and create the message from it.
|
|
// Only populates what is necessary to do the conversion.
|
|
// artist must match the directory name.
|
|
func CreateMessage(artistName, albumFolder string) ([]byte, error) {
|
|
albumName := path.Base(albumFolder)
|
|
artistPath := path.Clean(strings.ReplaceAll(albumFolder, albumName, ""))
|
|
var trackFiles []trackFile
|
|
err := filepath.Walk(albumFolder, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return errors.Wrapf(err, "error visiting file")
|
|
}
|
|
if filepath.Ext(path) == ".flac" {
|
|
trackFiles = append(trackFiles, trackFile{Path: path})
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "error walking path")
|
|
}
|
|
sort.Slice(trackFiles, func(i, j int) bool {
|
|
return trackFiles[i].Path < trackFiles[j].Path
|
|
})
|
|
|
|
tracks := func() []track {
|
|
var tracks []track
|
|
for i, file := range trackFiles {
|
|
tracks = append(
|
|
tracks,
|
|
track{
|
|
Title: strings.ReplaceAll(path.Base(file.Path), ".flac", ""),
|
|
TrackNumber: strconv.Itoa(i + 1),
|
|
},
|
|
)
|
|
}
|
|
return tracks
|
|
}()
|
|
|
|
m := message{
|
|
Tracks: tracks,
|
|
TrackFiles: trackFiles,
|
|
EventType: "Download",
|
|
Artist: artist{
|
|
Name: artistName,
|
|
Path: artistPath,
|
|
},
|
|
}
|
|
return json.Marshal(m)
|
|
}
|
|
|
|
type message struct {
|
|
Tracks []track `json:"tracks"`
|
|
TrackFiles []trackFile `json:"trackFiles"`
|
|
IsUpgrade bool `json:"isUpgrade"`
|
|
EventType string `json:"eventType"`
|
|
Artist artist `json:"artist"`
|
|
}
|
|
|
|
type artist struct {
|
|
ID int `json:"id"`
|
|
Name string `json:"name"`
|
|
Path string `json:"path"`
|
|
MBID string `json:"mbID"`
|
|
}
|
|
|
|
type track struct {
|
|
ID int `json:"id"`
|
|
Title string `json:"title"`
|
|
TrackNumber string `json:"trackNumber"`
|
|
Quality string `json:"quality"`
|
|
QualityVersion int `json:"qualityVersion"`
|
|
}
|
|
|
|
func (t *track) mp3Name() string {
|
|
return fmt.Sprintf("%s - %s.mp3", t.TrackNumber, t.Title)
|
|
}
|
|
|
|
type trackFile struct {
|
|
ID int `json:"id"`
|
|
Path string `json:"path"`
|
|
Quality string `json:"quality"`
|
|
QualityVersion int `json:"qualityVersion"`
|
|
SceneName string `json:"sceneName"`
|
|
}
|
|
|
|
// New creates a new converter
|
|
func New(iTunesDir string) *Converter {
|
|
return &Converter{
|
|
iTunesDir: iTunesDir,
|
|
}
|
|
}
|
|
|
|
func (tf *trackFile) Convert(newFile string) error {
|
|
trans := new(transcoder.Transcoder)
|
|
if err := trans.Initialize(tf.Path, newFile); err != nil {
|
|
return err
|
|
}
|
|
trans.MediaFile().SetAudioVariableBitrate()
|
|
trans.MediaFile().SetAudioBitRate("0")
|
|
return <-trans.Run(false)
|
|
}
|
|
|
|
// convertFiles converts all files from the message into the
|
|
// iTunesDir and changes the permissions.
|
|
func (c *Converter) convertFiles() error {
|
|
for i, track := range c.Tracks {
|
|
destination := path.Join(c.iTunesDir, c.albumFolder, track.mp3Name())
|
|
if err := c.TrackFiles[i].Convert(destination); err != nil {
|
|
return errors.Wrapf(err, "could not convert %v", c.TrackFiles[i].Path)
|
|
}
|
|
|
|
if err := changePermission(path.Join(destination)); err != nil {
|
|
return errors.Wrapf(err, "could not change permissions on %v", destination)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *message) extractAlbumFolder() string {
|
|
artistDir := filepath.Clean(m.Artist.Path) + "/"
|
|
fileDir := filepath.Dir(m.TrackFiles[0].Path)
|
|
return strings.ReplaceAll(fileDir, artistDir, "")
|
|
}
|
|
|
|
// Process the message, converting the files, changing permissions and copying artwork
|
|
func (c *Converter) Process(msgSource io.Reader) error {
|
|
// read and parse the message
|
|
encMessage, err := ioutil.ReadAll(msgSource)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "could not read given source")
|
|
}
|
|
|
|
c.message = new(message)
|
|
if err := json.Unmarshal(encMessage, c.message); err != nil {
|
|
return errors.Wrapf(err, "could not unmarshal given source")
|
|
}
|
|
|
|
if c.message.EventType != "Download" {
|
|
klog.Infof("eventType is %v, nothing to do", c.message.EventType)
|
|
}
|
|
|
|
c.albumFolder = c.TrackFiles[0].SceneName
|
|
if c.albumFolder == "" {
|
|
c.albumFolder = c.extractAlbumFolder()
|
|
}
|
|
klog.Infof("got request to transcode %v", c.albumFolder)
|
|
|
|
// actual conversion work
|
|
if err := os.MkdirAll(path.Join(c.iTunesDir, c.albumFolder), os.ModePerm); err != nil {
|
|
return errors.Wrapf(err, "could not create iTunes Directory")
|
|
}
|
|
if err := os.Chmod(path.Join(c.iTunesDir, c.albumFolder), 0775); err != nil {
|
|
return errors.Wrapf(err, "could not change permissions on iTunes dir")
|
|
}
|
|
|
|
if err := c.convertFiles(); err != nil {
|
|
return errors.Wrapf(err, "could not convert files")
|
|
}
|
|
|
|
klog.Infof("Successfully converted %v", path.Join(c.Artist.Path, c.albumFolder))
|
|
return nil
|
|
}
|
|
|
|
func changePermission(newFile string) error {
|
|
err := os.Chmod(newFile, 0777)
|
|
return errors.Wrapf(err, "chmod failed")
|
|
}
|