// // tank // // Import and Playlist Daemon for autoradio project // // // Copyright (C) 2017-2019 Christian Pointner <equinox@helsinki.at> // // This file is part of tank. // // tank 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. // // tank 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 tank. If not, see <http://www.gnu.org/licenses/>. // package v1 import ( "context" "encoding/json" "net/http" "net/url" "time" "github.com/gin-gonic/gin" "gitlab.servus.at/autoradio/tank/importer" "gitlab.servus.at/autoradio/tank/store" ) func (api *API) ListFilesOfShow() gin.HandlerFunc { return func(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequest(c, showID); !authorized { return } // TODO: implement pagination files, err := api.store.ListFiles(showID) if err != nil { sendError(c, err) return } c.JSON(http.StatusOK, FilesListing{files}) } } func (api *API) CreateFileForShow() gin.HandlerFunc { return func(c *gin.Context) { showID := c.Param("show-id") authorized, sess := authorizeRequest(c, showID) if !authorized { return } request := &FileCreateRequest{} err := json.NewDecoder(c.Request.Body).Decode(request) if err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{Error: "error decoding request: " + err.Error()}) return } if request.SourceURI == "" { c.JSON(http.StatusBadRequest, ErrorResponse{Error: "source-uri is mandatory"}) return } srcURI, err := url.Parse(request.SourceURI) if err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{Error: "source-uri is invalid: " + err.Error()}) return } waitFor := c.Query("wait-for") switch waitFor { case "running": case "done": case "": default: c.JSON(http.StatusBadRequest, ErrorResponse{Error: "unable to wait for unknown state: " + waitFor}) return } file := &store.File{} file.Source.URI = request.SourceURI if file, err = api.store.CreateFile(showID, *file); err != nil { sendError(c, err) return } // TODO: from here on, in case of an error we should either delete the newly created // file or at least return the id of the created file so the UI can deal with it. refID := "" // TODO: get this from query paremeter job, err := api.importer.CreateJob(showID, file.ID, *srcURI, sess.Username, refID) if err != nil { // possible errors: // - importer.ErrSourceNotSupported -> delete file // - any error returned by store.GetFile(): // possible reasons for this: // * connection problem to the store database // * somebody deleted the file // - importer.ErrFileNotNew -> leave file sendError(c, err) return } if err = job.Start(context.Background(), 3*time.Hour); err != nil { // TODO: hardcoded value // possible errors: // - importer.ErrAlreadyCanceled -> ?? // - error while creating job workdir -> ?? // - importer.ErrTooManyJobs -> ?? sendError(c, err) return } if waitFor == "" { c.JSON(http.StatusCreated, file) return } switch waitFor { case "running": <-job.Running() case "done": <-job.Done() } if file, err = api.store.GetFile(showID, file.ID); err != nil { // possible reasons why this failed: // - connection problem to the store database // - somebody deleted the file sendError(c, err) return } c.JSON(http.StatusCreated, file) } } func (api *API) ReadFileOfShow() gin.HandlerFunc { return func(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequest(c, showID); !authorized { return } id, err := idFromString(c.Param("file-id")) if err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{Error: "invalid file-id: " + err.Error()}) return } file, err := api.store.GetFile(showID, id) if err != nil { sendError(c, err) return } c.JSON(http.StatusOK, file) } } func (api *API) PatchFileOfShow() gin.HandlerFunc { return func(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequest(c, showID); !authorized { return } id, err := idFromString(c.Param("file-id")) if err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{Error: "invalid file-id: " + err.Error()}) return } data := make(map[string]string) if err = json.NewDecoder(c.Request.Body).Decode(&data); err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{Error: "error decoding request: " + err.Error()}) return } file, err := api.store.UpdateFileMetadata(showID, id, data) if err != nil { sendError(c, err) return } c.JSON(http.StatusOK, file) } } func (api *API) DeleteFileOfShow() gin.HandlerFunc { return func(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequest(c, showID); !authorized { return } id, err := idFromString(c.Param("file-id")) if err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{Error: "invalid file-id: " + err.Error()}) return } if job, err := api.importer.GetJob(showID, id); err != importer.ErrNotFound { job.Cancel() } if err = api.store.DeleteFile(showID, id); err != nil { sendError(c, err) return } c.JSON(http.StatusNoContent, nil) } } func (api *API) ReadUsageOfFile() gin.HandlerFunc { return func(c *gin.Context) { showID := c.Param("show-id") if authorized, _ := authorizeRequest(c, showID); !authorized { return } id, err := idFromString(c.Param("file-id")) if err != nil { c.JSON(http.StatusBadRequest, ErrorResponse{Error: "invalid file-id: " + err.Error()}) return } result := FileUsageListing{} if result.Usage.Playlists, err = api.store.GetFileUsage(showID, id); err != nil { sendError(c, err) return } c.JSON(http.StatusOK, result) } }