// // rhimportd // // The Radio Helsinki Rivendell Import Daemon // // // Copyright (C) 2015 Christian Pointner // // This file is part of rhimportd. // // rhimportd is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // any later version. // // rhimportd is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with rhimportd. If not, see . // package rhimport import ( "bufio" "bytes" "fmt" "github.com/golang-basic/go-curl" "mime/multipart" "net/http" "os" "path" ) var ( bool2str = map[bool]string{false: "0", true: "1"} ) type ImportContext struct { *Config *RdDb UserName string Password string Trusted bool ShowId int GroupName string Cart int Cut int Channels int NormalizationLevel int AutotrimLevel int UseMetaData bool SourceUri string SourceFile string DeleteSourceFile bool DeleteSourceDir bool ProgressCallBack func(step int, step_name string, progress float64, userdata interface{}) ProgressCallBackData interface{} } func NewImportContext(conf *Config, rddb *RdDb, user string, group string) *ImportContext { ctx := new(ImportContext) ctx.Config = conf ctx.RdDb = rddb ctx.UserName = user ctx.Password = "" ctx.Trusted = false ctx.ShowId = 0 ctx.GroupName = group ctx.Cart = 0 ctx.Cut = 0 ctx.Channels = 2 ctx.NormalizationLevel = -1200 ctx.AutotrimLevel = 0 ctx.UseMetaData = false ctx.SourceFile = "" ctx.DeleteSourceFile = false ctx.DeleteSourceDir = false ctx.ProgressCallBack = nil return ctx } func (ctx *ImportContext) getPassword(cached bool) (err error) { req := getPasswordRequest{} req.username = ctx.UserName req.cached = cached req.response = make(chan getPasswordResult) ctx.RdDb.getPasswordChan <- req res := <-req.response if res.err != nil { err = res.err return } ctx.Password = res.password return } func (ctx *ImportContext) getShowInfo() (err error) { req := getShowInfoRequest{} req.showid = ctx.ShowId req.response = make(chan getShowInfoResult) ctx.RdDb.getShowInfoChan <- req res := <-req.response if res.err != nil { err = res.err return } rhdl.Printf("Title of show %d is '%s'", ctx.ShowId, res.title) return } type ImportResult struct { ResponseCode int ErrorString string Cart int Cut int } func (self *ImportResult) fromRDWebResult(rdres *RDWebResult) { self.ResponseCode = rdres.ResponseCode self.ErrorString = rdres.ErrorString if rdres.AudioConvertError != 0 { self.ErrorString += fmt.Sprint(", Audio Convert Error: %d", rdres.AudioConvertError) } } func add_cart(ctx *ImportContext, result *ImportResult) (err error) { var b bytes.Buffer w := multipart.NewWriter(&b) if err = w.WriteField("COMMAND", "12"); err != nil { return } if err = w.WriteField("LOGIN_NAME", ctx.UserName); err != nil { return } if err = w.WriteField("PASSWORD", ctx.Password); err != nil { return } if err = w.WriteField("GRPOUP_NAME", ctx.GroupName); err != nil { return } if err = w.WriteField("TYPE", "audio"); err != nil { return } if ctx.Cart != 0 { if err = w.WriteField("CART_NUMBER", fmt.Sprintf("%d", ctx.Cart)); err != nil { return } } w.Close() var res *http.Response if res, err = send_post_request(ctx.Config.RDXportEndpoint, &b, w.FormDataContentType()); err != nil { return } defer res.Body.Close() if res.StatusCode != http.StatusOK { var rdres *RDWebResult if rdres, err = NewRDWebResultFromXML(res.Body); err != nil { return } result.fromRDWebResult(rdres) return } var cartadd *RDCartAdd if cartadd, err = NewRDCartAddFromXML(res.Body); err != nil { return } result.ResponseCode = res.StatusCode result.ErrorString = "OK" result.Cart = cartadd.Carts[0].Number ctx.Cart = result.Cart return } func add_cut(ctx *ImportContext, result *ImportResult) (err error) { var b bytes.Buffer w := multipart.NewWriter(&b) if err = w.WriteField("COMMAND", "10"); err != nil { return } if err = w.WriteField("LOGIN_NAME", ctx.UserName); err != nil { return } if err = w.WriteField("PASSWORD", ctx.Password); err != nil { return } if err = w.WriteField("CART_NUMBER", fmt.Sprintf("%d", ctx.Cart)); err != nil { return } w.Close() var res *http.Response if res, err = send_post_request(ctx.Config.RDXportEndpoint, &b, w.FormDataContentType()); err != nil { return } defer res.Body.Close() if res.StatusCode != http.StatusOK { var rdres *RDWebResult if rdres, err = NewRDWebResultFromXML(res.Body); err != nil { return } result.fromRDWebResult(rdres) return } var cutadd *RDCutAdd if cutadd, err = NewRDCutAddFromXML(res.Body); err != nil { return } result.ResponseCode = res.StatusCode result.ErrorString = "OK" result.Cut = cutadd.Cuts[0].Number ctx.Cut = cutadd.Cuts[0].Number return } func remove_cart(ctx *ImportContext, result *ImportResult) (err error) { var b bytes.Buffer w := multipart.NewWriter(&b) if err = w.WriteField("COMMAND", "13"); err != nil { return } if err = w.WriteField("LOGIN_NAME", ctx.UserName); err != nil { return } if err = w.WriteField("PASSWORD", ctx.Password); err != nil { return } if err = w.WriteField("CART_NUMBER", fmt.Sprintf("%d", ctx.Cart)); err != nil { return } w.Close() var res *http.Response if res, err = send_post_request(ctx.Config.RDXportEndpoint, &b, w.FormDataContentType()); err != nil { return } defer res.Body.Close() var rdres *RDWebResult if rdres, err = NewRDWebResultFromXML(res.Body); err != nil { return } result.fromRDWebResult(rdres) return } func send_post_request(url string, b *bytes.Buffer, contenttype string) (res *http.Response, err error) { var req *http.Request if req, err = http.NewRequest("POST", url, b); err != nil { return } if contenttype != "" { req.Header.Set("Content-Type", contenttype) } client := &http.Client{} if res, err = client.Do(req); err != nil { return } return } func import_audio_create_request(ctx *ImportContext, easy *curl.CURL) (form *curl.Form, err error) { form = curl.NewForm() if err = form.Add("COMMAND", "2"); err != nil { return } if err = form.Add("LOGIN_NAME", ctx.UserName); err != nil { return } if err = form.Add("PASSWORD", ctx.Password); err != nil { return } if err = form.Add("CART_NUMBER", fmt.Sprintf("%d", ctx.Cart)); err != nil { return } if err = form.Add("CUT_NUMBER", fmt.Sprintf("%d", ctx.Cut)); err != nil { return } if err = form.Add("CHANNELS", fmt.Sprintf("%d", ctx.Channels)); err != nil { return } if err = form.Add("NORMALIZATION_LEVEL", fmt.Sprintf("%d", ctx.NormalizationLevel)); err != nil { return } if err = form.Add("AUTOTRIM_LEVEL", fmt.Sprintf("%d", ctx.AutotrimLevel)); err != nil { return } if err = form.Add("USE_METADATA", bool2str[ctx.UseMetaData]); err != nil { return } if err = form.AddFile("FILENAME", ctx.SourceFile); err != nil { return } return } func import_audio(ctx *ImportContext, result *ImportResult) (err error) { easy := curl.EasyInit() if easy != nil { defer easy.Cleanup() easy.Setopt(curl.OPT_URL, ctx.Config.RDXportEndpoint) easy.Setopt(curl.OPT_POST, true) var form *curl.Form if form, err = import_audio_create_request(ctx, easy); err != nil { return } easy.Setopt(curl.OPT_HTTPPOST, form) easy.Setopt(curl.OPT_HTTPHEADER, []string{"Expect:"}) var resbody bytes.Buffer easy.Setopt(curl.OPT_WRITEFUNCTION, func(ptr []byte, userdata interface{}) bool { b := userdata.(*bytes.Buffer) b.Write(ptr) return true }) easy.Setopt(curl.OPT_WRITEDATA, &resbody) easy.Setopt(curl.OPT_NOPROGRESS, false) easy.Setopt(curl.OPT_PROGRESSFUNCTION, func(dltotal, dlnow, ultotal, ulnow float64, userdata interface{}) bool { ctx := userdata.(*ImportContext) if ctx.ProgressCallBack != nil { ctx.ProgressCallBack(2, "importing", ulnow/ultotal, ctx.ProgressCallBackData) } return true }) easy.Setopt(curl.OPT_PROGRESSDATA, ctx) if err = easy.Perform(); err != nil { return } // var status_code interface{} // if status_code, err = easy.Getinfo(curl.INFO_RESPONSE_CODE); err != nil { // return // } var rdres *RDWebResult if rdres, err = NewRDWebResultFromXML(bufio.NewReader(&resbody)); err != nil { return } result.fromRDWebResult(rdres) return } else { err = fmt.Errorf("Error initializing libcurl") } return } func cleanup_files(ctx *ImportContext) { if ctx.DeleteSourceFile { rhdl.Printf("importer: removing file: %s", ctx.SourceFile) if err := os.Remove(ctx.SourceFile); err != nil { rhl.Printf("importer: error removing source file: %s", err) return } if ctx.DeleteSourceDir { dir := path.Dir(ctx.SourceFile) rhdl.Printf("importer: also removing directory: %s", dir) if err := os.Remove(dir); err != nil { rhl.Printf("importer: error removing source directory: %s", err) } } } return } func ImportFile(ctx *ImportContext) (res *ImportResult, err error) { rhl.Println("ImportFile called for", ctx.SourceFile) defer cleanup_files(ctx) if ctx.ProgressCallBack != nil { ctx.ProgressCallBack(2, "importing", 0.0, ctx.ProgressCallBackData) } if ctx.Trusted { if err = ctx.getPassword(true); err != nil { return } } res = &ImportResult{} if err = import_audio(ctx, res); err != nil { return } if ctx.ProgressCallBack != nil { ctx.ProgressCallBack(2, "importing", 1.0, ctx.ProgressCallBackData) } rhdl.Printf("ImportResult: %+v\n", res) if res.ResponseCode == http.StatusOK { rhl.Println("ImportFile succesfully imported", ctx.SourceFile) } else { rhl.Println("ImportFile import of", ctx.SourceFile, "was unsuccesful") } return }