new initial commit

Signed-off-by: Ramon Rüttimann <ramon@nine.ch>
master
Ramon Rüttimann 5 years ago
commit d38623c4fe

@ -0,0 +1,46 @@
kind: pipeline
name: default
steps:
- name: test
image: golang
environment:
GO111MODULE: on
commands:
- echo $GO111MODULE
- go test ./... -v -cover
- name: build
image: golang
environment:
GO111MODULE: on
commands:
- go build
- name: docker
image: plugins/docker
settings:
auto_tag: true
build_args_from_env:
- DRONE_COMMIT
- DRONE_TAG
username:
from_secret: username
password:
from_secret: password
repo: registry.ramonr.ch/mediaconverter
registry: registry.ramonr.ch
when:
event: tag
---
kind: secret
name: username
get:
path: registry-basicauth
name: user
---
kind: secret
name: password
get:
path: registry-basicauth
name: password

@ -0,0 +1,20 @@
FROM golang:alpine as builder
ARG DRONE_COMMIT="none"
ARG DRONE_TAG="none"
RUN apk --no-cache add git
WORKDIR /app
COPY . .
# ldflags to remove debugging tables
ENV DRONE_COMMIT=$DRONE_COMMIT
ENV DRONE_TAG=$DRONE_TAG
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s -X main.version=$DRONE_TAG -X main.commit=$DRONE_COMMIT" -o "mediaconverter" .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s -X main.version=$DRONE_TAG -X main.commit=$DRONE_COMMIT" -o "faker" ./faker/
FROM alfg/ffmpeg
USER 1000:1000
COPY --from=builder /app/mediaconverter /mediaconverter
COPY --from=builder /app/faker /mediaconverter
CMD ["/mediaconverter"]

@ -0,0 +1,195 @@
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")
}

@ -0,0 +1,34 @@
package converter
import "testing"
func TestExtractAlbumFolder(t *testing.T) {
tests := []struct {
m *message
albumFolder string
}{
{createMessage("/test/artistX", "/test/artistX/albumY/songX.flac"), "albumY"},
{createMessage("/test/artistX/", "/test/artistX/album Y/songX.flac"), "album Y"},
}
for _, tt := range tests {
actual := tt.m.extractAlbumFolder()
expected := tt.albumFolder
if actual != expected {
t.Errorf("expected %v, got %v", expected, actual)
}
}
}
func createMessage(artistPath, filePath string) *message {
return &message{
Artist: artist{
Path: artistPath,
},
TrackFiles: []trackFile{
{
Path: filePath,
},
},
}
}

@ -0,0 +1,52 @@
package main
import (
"bytes"
"flag"
"fmt"
"io/ioutil"
"net/http"
"git.ramonr.ch/ramon/mediaconverter/converter"
"k8s.io/klog"
)
// set at compile time
var (
version = ""
commit = ""
)
func main() {
klog.InitFlags(nil)
postAddr := flag.String("post-addr", "mediaconverter", "the address where to post the request")
directory := flag.String("dir", "", "the directory that contains the flac files")
artistName := flag.String("artist", "", "name of the artist")
flag.Parse()
fmt.Printf("Mediaconverter version %v, commit %v\n", version, commit)
msg, err := converter.CreateMessage(*artistName, *directory)
if err != nil {
klog.Fatalf("could not create message: %v", err)
}
klog.Infof("sending request")
req, err := http.NewRequest("POST", *postAddr, bytes.NewBuffer(msg))
if err != nil {
panic(err)
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
klog.Infof("received answer")
fmt.Println("response Status:", resp.Status)
fmt.Println("response Headers:", resp.Header)
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println("response Body:", string(body))
}

@ -0,0 +1,11 @@
module git.ramonr.ch/ramon/mediaconverter
go 1.13
require (
github.com/pkg/errors v0.8.1
github.com/xfrr/goffmpeg v0.0.0-20191108092855-c49093f2d9f7
k8s.io/klog v1.0.0
)
replace github.com/xfrr/goffmpeg => github.com/tommyknows/goffmpeg v0.0.0-20191109121430-95bbe3aa58b5

@ -0,0 +1,15 @@
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/tommyknows/goffmpeg v0.0.0-20191108092855-c49093f2d9f7 h1:19vePvbw3O7GvNDnipom3Fu8uLCN2XAZlpABr/Z+yFo=
github.com/tommyknows/goffmpeg v0.0.0-20191108092855-c49093f2d9f7/go.mod h1:W1AEwoY9XJ1POFmGvJ5y/sifvS6MnkGfwaCbV6nWKfo=
github.com/tommyknows/goffmpeg v0.0.0-20191109114406-40d09fee25ad h1:2ziJ/9WFv0zyZZvn2W7V/2SohYKw1M7K+9pHsGzW2bw=
github.com/tommyknows/goffmpeg v0.0.0-20191109114406-40d09fee25ad/go.mod h1:W1AEwoY9XJ1POFmGvJ5y/sifvS6MnkGfwaCbV6nWKfo=
github.com/tommyknows/goffmpeg v0.0.0-20191109120730-72121122651e h1:fuzOvyuRh3eGf+tgf7npiGQkg5Xn74DtlIMKizJpJNE=
github.com/tommyknows/goffmpeg v0.0.0-20191109120730-72121122651e/go.mod h1:W1AEwoY9XJ1POFmGvJ5y/sifvS6MnkGfwaCbV6nWKfo=
github.com/tommyknows/goffmpeg v0.0.0-20191109121430-95bbe3aa58b5 h1:UqjC/7jF5Sc9DhepBZyobGwNPiMNNtB2h+U6pHSLoEg=
github.com/tommyknows/goffmpeg v0.0.0-20191109121430-95bbe3aa58b5/go.mod h1:W1AEwoY9XJ1POFmGvJ5y/sifvS6MnkGfwaCbV6nWKfo=
github.com/xfrr/goffmpeg v0.0.0-20191108092855-c49093f2d9f7 h1:EMf+JgASgRmdgyXxsd5YqnDkmO6PI9cyp/yN7Z/wtk0=
github.com/xfrr/goffmpeg v0.0.0-20191108092855-c49093f2d9f7/go.mod h1:mL+qPvJWwu9An5a66+HyEJ7X7iEUSI09zX/Xl67XRXs=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=

@ -0,0 +1,41 @@
package main
import (
"flag"
"fmt"
"net/http"
"git.ramonr.ch/ramon/mediaconverter/converter"
"k8s.io/klog"
)
const iTunesDir = "/mnt/data/media/iTunes/iTunes Media/Automatically Add to Music.localized"
// set at compile time
var (
version = ""
commit = ""
)
func main() {
klog.InitFlags(nil)
listenAddr := flag.String("listen-addr", "127.0.0.1:8088", "the address where to listen on")
flag.Parse()
fmt.Printf("Mediaconverter version %v, commit %v\n", version, commit)
http.HandleFunc("/", conversionHandler())
fmt.Println("Starting to listen on", *listenAddr)
err := http.ListenAndServe(*listenAddr, nil)
fmt.Println(err)
}
func conversionHandler() http.HandlerFunc {
conv := converter.New(iTunesDir)
return func(w http.ResponseWriter, r *http.Request) {
if err := conv.Process(r.Body); err != nil {
klog.Errorf("could not convert: %v", err)
fmt.Fprintf(w, "could not convert: %v", err)
}
}
}
Loading…
Cancel
Save