mirror of
https://github.com/Monibuca/plugin-record.git
synced 2025-09-27 13:02:09 +08:00
179 lines
4.2 KiB
Go
179 lines
4.2 KiB
Go
package record
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
|
|
. "github.com/Monibuca/engine/v3"
|
|
. "github.com/Monibuca/utils/v3"
|
|
)
|
|
|
|
var config struct {
|
|
Path string
|
|
AutoRecord bool
|
|
AppendFlv bool
|
|
}
|
|
var recordings sync.Map
|
|
|
|
type FlvFileInfo struct {
|
|
Path string
|
|
Size int64
|
|
Duration uint32
|
|
}
|
|
|
|
type FileWr interface {
|
|
io.Reader
|
|
io.Writer
|
|
io.Seeker
|
|
io.Closer
|
|
}
|
|
|
|
var ExtraConfig struct {
|
|
CreateFileFn func(filename string) (FileWr, error)
|
|
AutoRecordFilter func(stream string) bool
|
|
}
|
|
|
|
func init() {
|
|
pc := PluginConfig{
|
|
Name: "Record",
|
|
Config: &config,
|
|
HotConfig: map[string]func(interface{}){
|
|
"AutoRecord": func(v interface{}) {
|
|
config.AutoRecord = v.(bool)
|
|
},
|
|
"AppendFlv": func(v interface{}) {
|
|
config.AppendFlv = v.(bool)
|
|
},
|
|
},
|
|
}
|
|
pc.Install(run)
|
|
}
|
|
func run() {
|
|
go AddHook(HOOK_PUBLISH, onPublish)
|
|
os.MkdirAll(config.Path, 0755)
|
|
http.HandleFunc("/vod/", VodHandler)
|
|
http.HandleFunc("/api/record/flv/list", func(w http.ResponseWriter, r *http.Request) {
|
|
CORS(w, r)
|
|
if files, err := tree(config.Path, 0); err == nil {
|
|
var bytes []byte
|
|
if bytes, err = json.Marshal(files); err == nil {
|
|
w.Write(bytes)
|
|
} else {
|
|
w.Write([]byte("{\"err\":\"" + err.Error() + "\"}"))
|
|
}
|
|
} else {
|
|
w.Write([]byte("{\"err\":\"" + err.Error() + "\"}"))
|
|
}
|
|
})
|
|
http.HandleFunc("/api/record/flv", func(w http.ResponseWriter, r *http.Request) {
|
|
CORS(w, r)
|
|
if streamPath := r.URL.Query().Get("streamPath"); streamPath != "" {
|
|
if err := SaveFlv(streamPath, r.URL.Query().Get("append") == "true"); err != nil {
|
|
w.Write([]byte(err.Error()))
|
|
} else {
|
|
w.Write([]byte("success"))
|
|
}
|
|
} else {
|
|
w.Write([]byte("no streamPath"))
|
|
}
|
|
})
|
|
|
|
http.HandleFunc("/api/record/flv/stop", func(w http.ResponseWriter, r *http.Request) {
|
|
CORS(w, r)
|
|
if streamPath := r.URL.Query().Get("streamPath"); streamPath != "" {
|
|
filePath := filepath.Join(config.Path, streamPath+".flv")
|
|
if stream, ok := recordings.Load(filePath); ok {
|
|
output := stream.(*Subscriber)
|
|
output.Close()
|
|
w.Write([]byte("success"))
|
|
} else {
|
|
w.Write([]byte("no query stream"))
|
|
}
|
|
} else {
|
|
w.Write([]byte("no such stream"))
|
|
}
|
|
})
|
|
http.HandleFunc("/api/record/flv/play", func(w http.ResponseWriter, r *http.Request) {
|
|
CORS(w, r)
|
|
if streamPath := r.URL.Query().Get("streamPath"); streamPath != "" {
|
|
if err := PublishFlvFile(streamPath); err != nil {
|
|
w.Write([]byte(err.Error()))
|
|
} else {
|
|
w.Write([]byte("success"))
|
|
}
|
|
} else {
|
|
w.Write([]byte("no streamPath"))
|
|
}
|
|
})
|
|
http.HandleFunc("/api/record/flv/delete", func(w http.ResponseWriter, r *http.Request) {
|
|
CORS(w, r)
|
|
if streamPath := r.URL.Query().Get("streamPath"); streamPath != "" {
|
|
filePath := filepath.Join(config.Path, streamPath+".flv")
|
|
if Exist(filePath) {
|
|
if err := os.Remove(filePath); err != nil {
|
|
w.Write([]byte(err.Error()))
|
|
} else {
|
|
w.Write([]byte("success"))
|
|
}
|
|
} else {
|
|
w.Write([]byte("no such file"))
|
|
}
|
|
} else {
|
|
w.Write([]byte("no streamPath"))
|
|
}
|
|
})
|
|
}
|
|
|
|
func onPublish(p *Stream) {
|
|
if config.AutoRecord || (ExtraConfig.AutoRecordFilter != nil && ExtraConfig.AutoRecordFilter(p.StreamPath)) {
|
|
SaveFlv(p.StreamPath, config.AppendFlv)
|
|
}
|
|
}
|
|
|
|
func tree(dstPath string, level int) (files []*FlvFileInfo, err error) {
|
|
var dstF *os.File
|
|
dstF, err = os.Open(dstPath)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer dstF.Close()
|
|
fileInfo, err := dstF.Stat()
|
|
if err != nil {
|
|
return
|
|
}
|
|
if !fileInfo.IsDir() { //如果dstF是文件
|
|
if path.Ext(fileInfo.Name()) == ".flv" {
|
|
p := strings.TrimPrefix(dstPath, config.Path)
|
|
p = strings.ReplaceAll(p, "\\", "/")
|
|
files = append(files, &FlvFileInfo{
|
|
Path: strings.TrimPrefix(p, "/"),
|
|
Size: fileInfo.Size(),
|
|
Duration: getDuration(dstF),
|
|
})
|
|
}
|
|
return
|
|
} else { //如果dstF是文件夹
|
|
var dir []os.FileInfo
|
|
dir, err = dstF.Readdir(0) //获取文件夹下各个文件或文件夹的fileInfo
|
|
if err != nil {
|
|
return
|
|
}
|
|
for _, fileInfo = range dir {
|
|
var _files []*FlvFileInfo
|
|
_files, err = tree(filepath.Join(dstPath, fileInfo.Name()), level+1)
|
|
if err != nil {
|
|
return
|
|
}
|
|
files = append(files, _files...)
|
|
}
|
|
return
|
|
}
|
|
|
|
}
|