212 lines
5.2 KiB
Go
212 lines
5.2 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"log"
|
|
"msw-open-music/pkg/database"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func (api *API) CheckGetFileStream(w http.ResponseWriter, r *http.Request) error {
|
|
var err error
|
|
q := r.URL.Query()
|
|
ids := q["id"]
|
|
if len(ids) == 0 {
|
|
err = errors.New(`parameter "id" can't be empty`)
|
|
api.HandleError(w, r, err)
|
|
return err
|
|
}
|
|
_, err = strconv.Atoi(ids[0])
|
|
if err != nil {
|
|
err = errors.New(`parameter "id" should be an integer`)
|
|
api.HandleError(w, r, err)
|
|
return err
|
|
}
|
|
configs := q["config"]
|
|
if len(configs) == 0 {
|
|
err = errors.New(`parameter "config" can't be empty`)
|
|
api.HandleError(w, r, err)
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// /get_file_stream?id=1&config=ffmpeg_config_name
|
|
func (api *API) HandleGetFileStream(w http.ResponseWriter, r *http.Request) {
|
|
err := api.CheckGetFileStream(w, r)
|
|
if err != nil {
|
|
return
|
|
}
|
|
q := r.URL.Query()
|
|
ids := q["id"]
|
|
id, err := strconv.Atoi(ids[0])
|
|
configs := q["config"]
|
|
configName := configs[0]
|
|
file, err := api.Db.GetFile(int64(id))
|
|
if err != nil {
|
|
api.HandleError(w, r, err)
|
|
return
|
|
}
|
|
|
|
path, err := file.Path()
|
|
if err != nil {
|
|
api.HandleError(w, r, err)
|
|
return
|
|
}
|
|
|
|
log.Println("[api] Stream file", path, configName)
|
|
|
|
ffmpegConfig, ok := api.GetFfmpegConfig(configName)
|
|
if !ok {
|
|
api.HandleErrorStringCode(w, r, `ffmpeg config not found`, 404)
|
|
return
|
|
}
|
|
|
|
// set headers for filename
|
|
filename := file.Filename + "." + ffmpegConfig.Name + "." + ffmpegConfig.Format
|
|
filename = url.PathEscape(filename)
|
|
// replace invalid characters
|
|
w.Header().Set("Content-Disposition", "inline; filename*=UTF-8''"+filename)
|
|
|
|
args := strings.Split(ffmpegConfig.Args, " ")
|
|
startArgs := []string{"-threads", strconv.FormatInt(api.APIConfig.FfmpegThreads, 10), "-i", path}
|
|
endArgs := []string{"-f", ffmpegConfig.Format, "-"}
|
|
ffmpegArgs := append(startArgs, args...)
|
|
ffmpegArgs = append(ffmpegArgs, endArgs...)
|
|
cmd := exec.Command("ffmpeg", ffmpegArgs...)
|
|
cmd.Stdout = w
|
|
// cmd.Stderr = os.Stderr
|
|
err = cmd.Run()
|
|
if err != nil {
|
|
api.HandleError(w, r, err)
|
|
return
|
|
}
|
|
}
|
|
|
|
type PrepareFileStreamDirectRequest struct {
|
|
ID int64 `json:"id"`
|
|
ConfigName string `json:"config_name"`
|
|
}
|
|
|
|
type PrepareFileStreamDirectResponse struct {
|
|
File *database.File `json:"file"`
|
|
}
|
|
|
|
// /prepare_file_stream_direct?id=1&config=ffmpeg_config_name
|
|
func (api *API) HandlePrepareFileStreamDirect(w http.ResponseWriter, r *http.Request) {
|
|
prepareFileStreamDirectRequst := &PrepareFileStreamDirectRequest{
|
|
ID: -1,
|
|
}
|
|
err := json.NewDecoder(r.Body).Decode(prepareFileStreamDirectRequst)
|
|
if err != nil {
|
|
api.HandleError(w, r, err)
|
|
return
|
|
}
|
|
|
|
// check empty
|
|
if prepareFileStreamDirectRequst.ID < 0 {
|
|
api.HandleErrorString(w, r, `"id" can't be none or negative`)
|
|
return
|
|
}
|
|
if prepareFileStreamDirectRequst.ConfigName == "" {
|
|
api.HandleErrorString(w, r, `"config_name" can't be empty`)
|
|
return
|
|
}
|
|
|
|
file, err := api.Db.GetFile(prepareFileStreamDirectRequst.ID)
|
|
if err != nil {
|
|
api.HandleError(w, r, err)
|
|
return
|
|
}
|
|
srcPath, err := file.Path()
|
|
if err != nil {
|
|
api.HandleError(w, r, err)
|
|
return
|
|
}
|
|
|
|
log.Println("[api] Prepare stream direct file", srcPath, prepareFileStreamDirectRequst.ConfigName)
|
|
ffmpegConfig, ok := api.GetFfmpegConfig(prepareFileStreamDirectRequst.ConfigName)
|
|
if !ok {
|
|
api.HandleErrorStringCode(w, r, `ffmpeg config not found`, 404)
|
|
return
|
|
}
|
|
objPath := api.Tmpfs.GetObjFilePath(prepareFileStreamDirectRequst.ID, ffmpegConfig)
|
|
|
|
// check obj file exists
|
|
exists := api.Tmpfs.Exits(objPath)
|
|
if !exists {
|
|
// lock the object
|
|
api.Tmpfs.Lock(objPath)
|
|
|
|
args := strings.Split(ffmpegConfig.Args, " ")
|
|
startArgs := []string{"-threads", strconv.FormatInt(api.APIConfig.FfmpegThreads, 10), "-i", srcPath}
|
|
endArgs := []string{"-y", objPath}
|
|
ffmpegArgs := append(startArgs, args...)
|
|
ffmpegArgs = append(ffmpegArgs, endArgs...)
|
|
cmd := exec.Command("ffmpeg", ffmpegArgs...)
|
|
err = cmd.Run()
|
|
if err != nil {
|
|
api.HandleError(w, r, err)
|
|
return
|
|
}
|
|
|
|
api.Tmpfs.Record(objPath)
|
|
api.Tmpfs.Unlock(objPath)
|
|
|
|
}
|
|
|
|
fileInfo, err := os.Stat(objPath)
|
|
if err != nil {
|
|
api.HandleError(w, r, err)
|
|
return
|
|
}
|
|
|
|
file.Filesize = fileInfo.Size()
|
|
|
|
prepareFileStreamDirectResponse := &PrepareFileStreamDirectResponse{
|
|
File: file,
|
|
}
|
|
json.NewEncoder(w).Encode(prepareFileStreamDirectResponse)
|
|
}
|
|
|
|
// /get_file_stream_direct?id=1&config=ffmpeg_config_name
|
|
// return converted file with http.ServeFile method
|
|
func (api *API) HandleGetFileStreamDirect(w http.ResponseWriter, r *http.Request) {
|
|
err := api.CheckGetFileStream(w, r)
|
|
if err != nil {
|
|
return
|
|
}
|
|
q := r.URL.Query()
|
|
ids := q["id"]
|
|
id, err := strconv.Atoi(ids[0])
|
|
configs := q["config"]
|
|
configName := configs[0]
|
|
|
|
ffmpegConfig, ok := api.GetFfmpegConfig(configName)
|
|
if !ok {
|
|
api.HandleErrorStringCode(w, r, `ffmpeg config not found`, 404)
|
|
return
|
|
}
|
|
|
|
path := api.Tmpfs.GetObjFilePath(int64(id), ffmpegConfig)
|
|
if api.Tmpfs.Exits(path) {
|
|
api.Tmpfs.Record(path)
|
|
}
|
|
|
|
// set headers for filename
|
|
filename := ids[0] + "." + ffmpegConfig.Name + "." + ffmpegConfig.Format
|
|
filename = url.PathEscape(filename)
|
|
// replace invalid characters
|
|
w.Header().Set("Content-Disposition", "inline; filename*=UTF-8''"+filename)
|
|
|
|
log.Println("[api] Get direct cached file", path)
|
|
|
|
http.ServeFile(w, r, path)
|
|
}
|