mirror of
https://github.com/Monibuca/plugin-record.git
synced 2025-10-30 19:36:18 +08:00
适配3.0
This commit is contained in:
49
flv.go
49
flv.go
@@ -6,19 +6,19 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
. "github.com/Monibuca/engine/v2"
|
. "github.com/Monibuca/engine/v3"
|
||||||
"github.com/Monibuca/engine/v2/avformat"
|
. "github.com/Monibuca/utils/v3"
|
||||||
"github.com/Monibuca/engine/v2/util"
|
"github.com/Monibuca/utils/v3/codec"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getDuration(file *os.File) uint32 {
|
func getDuration(file *os.File) uint32 {
|
||||||
_, err := file.Seek(-4, io.SeekEnd)
|
_, err := file.Seek(-4, io.SeekEnd)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
var tagSize uint32
|
var tagSize uint32
|
||||||
if tagSize, err = util.ReadByteToUint32(file, true); err == nil {
|
if tagSize, err = ReadByteToUint32(file, true); err == nil {
|
||||||
_, err = file.Seek(-int64(tagSize)-4, io.SeekEnd)
|
_, err = file.Seek(-int64(tagSize)-4, io.SeekEnd)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
_, timestamp, _, err := avformat.ReadFLVTag(file)
|
_, timestamp, _, err := codec.ReadFLVTag(file)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return timestamp
|
return timestamp
|
||||||
}
|
}
|
||||||
@@ -35,28 +35,43 @@ func SaveFlv(streamPath string, append bool) error {
|
|||||||
flag = flag | os.O_TRUNC | os.O_WRONLY
|
flag = flag | os.O_TRUNC | os.O_WRONLY
|
||||||
}
|
}
|
||||||
filePath := filepath.Join(config.Path, streamPath+".flv")
|
filePath := filepath.Join(config.Path, streamPath+".flv")
|
||||||
os.MkdirAll(path.Dir(filePath), 0775)
|
os.MkdirAll(path.Dir(filePath), 0755)
|
||||||
file, err := os.OpenFile(filePath, flag, 0775)
|
file, err := os.OpenFile(filePath, flag, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p := Subscriber{OnData: func(packet *avformat.SendPacket) error {
|
// return avformat.WriteFLVTag(file, packet)
|
||||||
return avformat.WriteFLVTag(file, packet)
|
p := Subscriber{
|
||||||
}}
|
ID: filePath,
|
||||||
p.ID = filePath
|
Type: "FlvRecord",
|
||||||
p.Type = "FlvRecord"
|
}
|
||||||
|
|
||||||
if append {
|
if append {
|
||||||
p.OffsetTime = getDuration(file)
|
p.OffsetTime = getDuration(file)
|
||||||
file.Seek(0, io.SeekEnd)
|
file.Seek(0, io.SeekEnd)
|
||||||
} else {
|
} else {
|
||||||
_, err = file.Write(avformat.FLVHeader)
|
_, err = file.Write(codec.FLVHeader)
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
recordings.Store(filePath, &p)
|
recordings.Store(filePath, &p)
|
||||||
go func() {
|
if err := p.Subscribe(streamPath); err == nil {
|
||||||
p.Subscribe(streamPath)
|
at, vt := p.OriginAudioTrack, p.OriginVideoTrack
|
||||||
file.Close()
|
var aac byte
|
||||||
}()
|
if at.SoundFormat == 10 {
|
||||||
|
aac = at.RtmpTag[0]
|
||||||
|
}
|
||||||
|
p.OnAudio = func(audio AudioPack) {
|
||||||
|
codec.WriteFLVTag(file, codec.FLV_TAG_TYPE_AUDIO, audio.Timestamp, audio.ToRTMPTag(aac))
|
||||||
|
}
|
||||||
|
p.OnVideo = func(video VideoPack) {
|
||||||
|
codec.WriteFLVTag(file, codec.FLV_TAG_TYPE_VIDEO, video.Timestamp, video.ToRTMPTag())
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
p.Play(at, vt)
|
||||||
|
file.Close()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
file.Close()
|
file.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
8
go.mod
8
go.mod
@@ -1,5 +1,9 @@
|
|||||||
module github.com/Monibuca/plugin-record
|
module github.com/Monibuca/plugin-record/v3
|
||||||
|
|
||||||
go 1.13
|
go 1.13
|
||||||
|
|
||||||
require github.com/Monibuca/engine/v2 v2.0.0
|
require (
|
||||||
|
github.com/Monibuca/engine/v3 v3.0.0-alpha6
|
||||||
|
github.com/Monibuca/utils/v3 v3.0.0-alpha5
|
||||||
|
github.com/pion/rtp v1.6.2 // indirect
|
||||||
|
)
|
||||||
|
|||||||
49
go.sum
49
go.sum
@@ -1,47 +1,28 @@
|
|||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Monibuca/engine v1.0.3 h1:/xwiEpqgQDAzjDb6zAwqFSZP8y0MOOVXv5aUx1os5nM=
|
github.com/Monibuca/engine/v3 v3.0.0-alpha6 h1:USZa+JDVJd13rb9wmX06ZxjRCrqYTAPe5wVD+0IODEE=
|
||||||
github.com/Monibuca/engine v1.0.3/go.mod h1:NjqVgtXuRSOyk3+NWgCuDf2p7TsBisjYxoEwA9uCZ38=
|
github.com/Monibuca/engine/v3 v3.0.0-alpha6/go.mod h1:V0/kfen6K5O/RLXHPsZj4DF/LboDZ0OqfeCfn35bWMo=
|
||||||
github.com/Monibuca/engine v1.1.1 h1:wk5qBJCssCOpeA7jyGU/oInVmFnfc9r+eT+sbjOIZfw=
|
github.com/Monibuca/utils/v3 v3.0.0-alpha4/go.mod h1:3xYmhQbgAZBHLyIMteUCd1va+1z/xnd72B585mCaT3c=
|
||||||
github.com/Monibuca/engine v1.1.1/go.mod h1:NjqVgtXuRSOyk3+NWgCuDf2p7TsBisjYxoEwA9uCZ38=
|
github.com/Monibuca/utils/v3 v3.0.0-alpha5 h1:IOyW/KJSRdRg+TPcgwkHLBynqfNQOV6p3iP7LgXEMFc=
|
||||||
github.com/Monibuca/engine v1.2.1 h1:TJmC6eZA1lR1MScWgempZLiEZD4T6aY/nn/rlQ9UdK8=
|
github.com/Monibuca/utils/v3 v3.0.0-alpha5/go.mod h1:3xYmhQbgAZBHLyIMteUCd1va+1z/xnd72B585mCaT3c=
|
||||||
github.com/Monibuca/engine v1.2.1/go.mod h1:WbDkXENLjcPjyjCR1Mix1GA+uAlwORkv/+8aMVrDX2g=
|
|
||||||
github.com/Monibuca/engine v1.2.2 h1:hNjsrZpOmui8lYhgCJ5ltJU8g/k0Rrdysx2tHNGGnbI=
|
|
||||||
github.com/Monibuca/engine/v2 v2.0.0-alpha2 h1:45yazqnnxEEcfHcOJGuIr1xtnBzQT6cPvhlymeZrDmA=
|
|
||||||
github.com/Monibuca/engine/v2 v2.0.0-alpha2/go.mod h1:34EYjjV15G6myuHOKaJkO7y5tJ1Arq/NfC9Weacr2mc=
|
|
||||||
github.com/Monibuca/engine/v2 v2.0.0-alpha3 h1:G1Mbq3Q0hbZuA0evrByf8qqRx/rk3lbvJa7Qp55tOsw=
|
|
||||||
github.com/Monibuca/engine/v2 v2.0.0-alpha3/go.mod h1:34EYjjV15G6myuHOKaJkO7y5tJ1Arq/NfC9Weacr2mc=
|
|
||||||
github.com/Monibuca/engine/v2 v2.0.0 h1:8FjaScrtN8QdbcxO9zZYABMC0Re3I1O1T4p94zAXYb0=
|
|
||||||
github.com/Monibuca/engine/v2 v2.0.0/go.mod h1:34EYjjV15G6myuHOKaJkO7y5tJ1Arq/NfC9Weacr2mc=
|
|
||||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
|
|
||||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
|
||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/funny/slab v0.0.0-20180511031532-b1fad5e5d478 h1:Db9StoJ6RZN3YttC0Pm0I4Y5izITRYch3RMbT59BYN0=
|
github.com/funny/slab v0.0.0-20180511031532-b1fad5e5d478 h1:Db9StoJ6RZN3YttC0Pm0I4Y5izITRYch3RMbT59BYN0=
|
||||||
github.com/funny/slab v0.0.0-20180511031532-b1fad5e5d478/go.mod h1:0j1+svBH8ABEIPdUP0AIg4qedsybnXGJBakCEw8cfoo=
|
github.com/funny/slab v0.0.0-20180511031532-b1fad5e5d478/go.mod h1:0j1+svBH8ABEIPdUP0AIg4qedsybnXGJBakCEw8cfoo=
|
||||||
github.com/funny/utest v0.0.0-20161029064919-43870a374500 h1:Z0r1CZnoIWFB/Uiwh1BU5FYmuFe6L5NPi6XWQEmsTRg=
|
github.com/funny/utest v0.0.0-20161029064919-43870a374500 h1:Z0r1CZnoIWFB/Uiwh1BU5FYmuFe6L5NPi6XWQEmsTRg=
|
||||||
github.com/funny/utest v0.0.0-20161029064919-43870a374500/go.mod h1:mUn39tBov9jKnTWV1RlOYoNzxdBFHiSzXWdY1FoNGGg=
|
github.com/funny/utest v0.0.0-20161029064919-43870a374500/go.mod h1:mUn39tBov9jKnTWV1RlOYoNzxdBFHiSzXWdY1FoNGGg=
|
||||||
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
|
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||||
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 h1:bqDmpDG49ZRnB5PcgP0RXtQvnMSgIF14M7CBd2shtXs=
|
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||||
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
|
|
||||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
|
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
||||||
|
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
||||||
|
github.com/pion/rtp v1.6.2 h1:iGBerLX6JiDjB9NXuaPzHyxHFG9JsIEdgwTC0lp5n/U=
|
||||||
|
github.com/pion/rtp v1.6.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
github.com/shirou/gopsutil v2.20.1+incompatible h1:oIq9Cq4i84Hk8uQAUOG3eNdI/29hBawGrD5YRl6JRDY=
|
|
||||||
github.com/shirou/gopsutil v2.20.1+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
|
|||||||
31
main.go
31
main.go
@@ -9,8 +9,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
. "github.com/Monibuca/engine/v2"
|
. "github.com/Monibuca/engine/v3"
|
||||||
. "github.com/Monibuca/engine/v2/util"
|
. "github.com/Monibuca/utils/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var config struct {
|
var config struct {
|
||||||
@@ -29,7 +29,6 @@ type FlvFileInfo struct {
|
|||||||
func init() {
|
func init() {
|
||||||
InstallPlugin(&PluginConfig{
|
InstallPlugin(&PluginConfig{
|
||||||
Name: "Record",
|
Name: "Record",
|
||||||
Type: PLUGIN_SUBSCRIBER,
|
|
||||||
Config: &config,
|
Config: &config,
|
||||||
Run: run,
|
Run: run,
|
||||||
HotConfig: map[string]func(interface{}){
|
HotConfig: map[string]func(interface{}){
|
||||||
@@ -43,10 +42,10 @@ func init() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
func run() {
|
func run() {
|
||||||
OnSubscribeHooks.AddHook(onSubscribe)
|
go AddHook(HOOK_SUBSCRIBE, onSubscribe)
|
||||||
OnPublishHooks.AddHook(onPublish)
|
go AddHook(HOOK_PUBLISH, onPublish)
|
||||||
os.MkdirAll(config.Path, 0755)
|
os.MkdirAll(config.Path, 0755)
|
||||||
http.HandleFunc("/record/flv/list", func(writer http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/api/record/flv/list", func(writer http.ResponseWriter, r *http.Request) {
|
||||||
if files, err := tree(config.Path, 0); err == nil {
|
if files, err := tree(config.Path, 0); err == nil {
|
||||||
var bytes []byte
|
var bytes []byte
|
||||||
if bytes, err = json.Marshal(files); err == nil {
|
if bytes, err = json.Marshal(files); err == nil {
|
||||||
@@ -58,7 +57,7 @@ func run() {
|
|||||||
writer.Write([]byte("{\"err\":\"" + err.Error() + "\"}"))
|
writer.Write([]byte("{\"err\":\"" + err.Error() + "\"}"))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
http.HandleFunc("/record/flv", func(writer http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/api/record/flv", func(writer http.ResponseWriter, r *http.Request) {
|
||||||
if streamPath := r.URL.Query().Get("streamPath"); streamPath != "" {
|
if streamPath := r.URL.Query().Get("streamPath"); streamPath != "" {
|
||||||
if err := SaveFlv(streamPath, r.URL.Query().Get("append") == "true"); err != nil {
|
if err := SaveFlv(streamPath, r.URL.Query().Get("append") == "true"); err != nil {
|
||||||
writer.Write([]byte(err.Error()))
|
writer.Write([]byte(err.Error()))
|
||||||
@@ -70,8 +69,8 @@ func run() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
http.HandleFunc("/record/flv/stop", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/api/record/flv/stop", func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
CORS(w, r)
|
||||||
if streamPath := r.URL.Query().Get("streamPath"); streamPath != "" {
|
if streamPath := r.URL.Query().Get("streamPath"); streamPath != "" {
|
||||||
filePath := filepath.Join(config.Path, streamPath+".flv")
|
filePath := filepath.Join(config.Path, streamPath+".flv")
|
||||||
if stream, ok := recordings.Load(filePath); ok {
|
if stream, ok := recordings.Load(filePath); ok {
|
||||||
@@ -85,7 +84,7 @@ func run() {
|
|||||||
w.Write([]byte("no such stream"))
|
w.Write([]byte("no such stream"))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
http.HandleFunc("/record/flv/play", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/api/record/flv/play", func(w http.ResponseWriter, r *http.Request) {
|
||||||
if streamPath := r.URL.Query().Get("streamPath"); streamPath != "" {
|
if streamPath := r.URL.Query().Get("streamPath"); streamPath != "" {
|
||||||
if err := PublishFlvFile(streamPath); err != nil {
|
if err := PublishFlvFile(streamPath); err != nil {
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
@@ -96,7 +95,7 @@ func run() {
|
|||||||
w.Write([]byte("no streamPath"))
|
w.Write([]byte("no streamPath"))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
http.HandleFunc("/record/flv/delete", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/api/record/flv/delete", func(w http.ResponseWriter, r *http.Request) {
|
||||||
if streamPath := r.URL.Query().Get("streamPath"); streamPath != "" {
|
if streamPath := r.URL.Query().Get("streamPath"); streamPath != "" {
|
||||||
filePath := filepath.Join(config.Path, streamPath+".flv")
|
filePath := filepath.Join(config.Path, streamPath+".flv")
|
||||||
if Exist(filePath) {
|
if Exist(filePath) {
|
||||||
@@ -113,17 +112,19 @@ func run() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
func onSubscribe(s *Subscriber) {
|
func onSubscribe(v interface{}) {
|
||||||
|
s:=v.(*Subscriber)
|
||||||
if config.AutoPublish {
|
if config.AutoPublish {
|
||||||
filePath := filepath.Join(config.Path, s.StreamPath+".flv")
|
filePath := filepath.Join(config.Path, s.StreamPath+".flv")
|
||||||
if s.Publisher == nil && Exist(filePath) {
|
if s.Publisher == nil && Exist(filePath) {
|
||||||
go PublishFlvFile(s.StreamPath)
|
PublishFlvFile(s.StreamPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func onPublish(p *Stream) {
|
func onPublish(v interface{}) {
|
||||||
|
p := v.(*Stream)
|
||||||
if config.AutoRecord {
|
if config.AutoRecord {
|
||||||
go SaveFlv(p.StreamPath, false)
|
SaveFlv(p.StreamPath, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
23
publisher.go
23
publisher.go
@@ -7,8 +7,9 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
. "github.com/Monibuca/engine/v2"
|
. "github.com/Monibuca/engine/v3"
|
||||||
"github.com/Monibuca/engine/v2/avformat"
|
. "github.com/Monibuca/utils/v3"
|
||||||
|
"github.com/Monibuca/utils/v3/codec"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FlvFile struct {
|
type FlvFile struct {
|
||||||
@@ -19,25 +20,27 @@ func PublishFlvFile(streamPath string) error {
|
|||||||
flvPath := filepath.Join(config.Path, streamPath+".flv")
|
flvPath := filepath.Join(config.Path, streamPath+".flv")
|
||||||
os.MkdirAll(filepath.Dir(flvPath), 0755)
|
os.MkdirAll(filepath.Dir(flvPath), 0755)
|
||||||
if file, err := os.Open(flvPath); err == nil {
|
if file, err := os.Open(flvPath); err == nil {
|
||||||
stream := FlvFile{}
|
var stream FlvFile
|
||||||
if stream.Publish(streamPath) {
|
if stream.Publish(streamPath) {
|
||||||
stream.Type = "FlvFile"
|
stream.Type = "FlvFile"
|
||||||
defer stream.Close()
|
defer stream.Close()
|
||||||
stream.UseTimestamp = true
|
file.Seek(int64(len(codec.FLVHeader)), io.SeekStart)
|
||||||
file.Seek(int64(len(avformat.FLVHeader)), io.SeekStart)
|
|
||||||
var lastTime uint32
|
var lastTime uint32
|
||||||
|
at := NewAudioTrack()
|
||||||
|
vt := NewVideoTrack()
|
||||||
|
stream.SetOriginAT(at)
|
||||||
for {
|
for {
|
||||||
if t, timestamp, payload, err := avformat.ReadFLVTag(file); err == nil {
|
if t, timestamp, payload, err := codec.ReadFLVTag(file); err == nil {
|
||||||
switch t {
|
switch t {
|
||||||
case avformat.FLV_TAG_TYPE_AUDIO:
|
case codec.FLV_TAG_TYPE_AUDIO:
|
||||||
stream.PushAudio(timestamp, payload)
|
at.Push(timestamp, payload)
|
||||||
case avformat.FLV_TAG_TYPE_VIDEO:
|
case codec.FLV_TAG_TYPE_VIDEO:
|
||||||
if timestamp != 0 {
|
if timestamp != 0 {
|
||||||
if lastTime == 0 {
|
if lastTime == 0 {
|
||||||
lastTime = timestamp
|
lastTime = timestamp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stream.PushVideo(timestamp, payload)
|
vt.Push(VideoPack{Timestamp: timestamp, CompositionTime: BigEndian.Uint24(payload[2:5]), Payload: payload[5:]})
|
||||||
time.Sleep(time.Duration(timestamp-lastTime) * time.Millisecond)
|
time.Sleep(time.Duration(timestamp-lastTime) * time.Millisecond)
|
||||||
lastTime = timestamp
|
lastTime = timestamp
|
||||||
}
|
}
|
||||||
|
|||||||
19
ui/dist/demo.html
vendored
19
ui/dist/demo.html
vendored
@@ -1,19 +0,0 @@
|
|||||||
<meta charset="utf-8">
|
|
||||||
<title>plugin-record demo</title>
|
|
||||||
<script src="https://unpkg.com/vue"></script>
|
|
||||||
<script src="./plugin-record.umd.js"></script>
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="./plugin-record.css">
|
|
||||||
|
|
||||||
|
|
||||||
<div id="app">
|
|
||||||
<demo></demo>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
new Vue({
|
|
||||||
components: {
|
|
||||||
demo: plugin-record
|
|
||||||
}
|
|
||||||
}).$mount('#app')
|
|
||||||
</script>
|
|
||||||
524
ui/dist/plugin-record.common.js
vendored
524
ui/dist/plugin-record.common.js
vendored
@@ -1,524 +0,0 @@
|
|||||||
module.exports =
|
|
||||||
/******/ (function(modules) { // webpackBootstrap
|
|
||||||
/******/ // The module cache
|
|
||||||
/******/ var installedModules = {};
|
|
||||||
/******/
|
|
||||||
/******/ // The require function
|
|
||||||
/******/ function __webpack_require__(moduleId) {
|
|
||||||
/******/
|
|
||||||
/******/ // Check if module is in cache
|
|
||||||
/******/ if(installedModules[moduleId]) {
|
|
||||||
/******/ return installedModules[moduleId].exports;
|
|
||||||
/******/ }
|
|
||||||
/******/ // Create a new module (and put it into the cache)
|
|
||||||
/******/ var module = installedModules[moduleId] = {
|
|
||||||
/******/ i: moduleId,
|
|
||||||
/******/ l: false,
|
|
||||||
/******/ exports: {}
|
|
||||||
/******/ };
|
|
||||||
/******/
|
|
||||||
/******/ // Execute the module function
|
|
||||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
|
||||||
/******/
|
|
||||||
/******/ // Flag the module as loaded
|
|
||||||
/******/ module.l = true;
|
|
||||||
/******/
|
|
||||||
/******/ // Return the exports of the module
|
|
||||||
/******/ return module.exports;
|
|
||||||
/******/ }
|
|
||||||
/******/
|
|
||||||
/******/
|
|
||||||
/******/ // expose the modules object (__webpack_modules__)
|
|
||||||
/******/ __webpack_require__.m = modules;
|
|
||||||
/******/
|
|
||||||
/******/ // expose the module cache
|
|
||||||
/******/ __webpack_require__.c = installedModules;
|
|
||||||
/******/
|
|
||||||
/******/ // define getter function for harmony exports
|
|
||||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
|
||||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
|
||||||
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
|
|
||||||
/******/ }
|
|
||||||
/******/ };
|
|
||||||
/******/
|
|
||||||
/******/ // define __esModule on exports
|
|
||||||
/******/ __webpack_require__.r = function(exports) {
|
|
||||||
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
|
||||||
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
||||||
/******/ }
|
|
||||||
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
|
||||||
/******/ };
|
|
||||||
/******/
|
|
||||||
/******/ // create a fake namespace object
|
|
||||||
/******/ // mode & 1: value is a module id, require it
|
|
||||||
/******/ // mode & 2: merge all properties of value into the ns
|
|
||||||
/******/ // mode & 4: return value when already ns object
|
|
||||||
/******/ // mode & 8|1: behave like require
|
|
||||||
/******/ __webpack_require__.t = function(value, mode) {
|
|
||||||
/******/ if(mode & 1) value = __webpack_require__(value);
|
|
||||||
/******/ if(mode & 8) return value;
|
|
||||||
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
|
|
||||||
/******/ var ns = Object.create(null);
|
|
||||||
/******/ __webpack_require__.r(ns);
|
|
||||||
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
|
|
||||||
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
|
|
||||||
/******/ return ns;
|
|
||||||
/******/ };
|
|
||||||
/******/
|
|
||||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
|
||||||
/******/ __webpack_require__.n = function(module) {
|
|
||||||
/******/ var getter = module && module.__esModule ?
|
|
||||||
/******/ function getDefault() { return module['default']; } :
|
|
||||||
/******/ function getModuleExports() { return module; };
|
|
||||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
|
||||||
/******/ return getter;
|
|
||||||
/******/ };
|
|
||||||
/******/
|
|
||||||
/******/ // Object.prototype.hasOwnProperty.call
|
|
||||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
|
||||||
/******/
|
|
||||||
/******/ // __webpack_public_path__
|
|
||||||
/******/ __webpack_require__.p = "";
|
|
||||||
/******/
|
|
||||||
/******/
|
|
||||||
/******/ // Load entry module and return exports
|
|
||||||
/******/ return __webpack_require__(__webpack_require__.s = "fb15");
|
|
||||||
/******/ })
|
|
||||||
/************************************************************************/
|
|
||||||
/******/ ({
|
|
||||||
|
|
||||||
/***/ "5941":
|
|
||||||
/***/ (function(module, exports, __webpack_require__) {
|
|
||||||
|
|
||||||
// extracted by mini-css-extract-plugin
|
|
||||||
|
|
||||||
/***/ }),
|
|
||||||
|
|
||||||
/***/ "f6fd":
|
|
||||||
/***/ (function(module, exports) {
|
|
||||||
|
|
||||||
// document.currentScript polyfill by Adam Miller
|
|
||||||
|
|
||||||
// MIT license
|
|
||||||
|
|
||||||
(function(document){
|
|
||||||
var currentScript = "currentScript",
|
|
||||||
scripts = document.getElementsByTagName('script'); // Live NodeList collection
|
|
||||||
|
|
||||||
// If browser needs currentScript polyfill, add get currentScript() to the document object
|
|
||||||
if (!(currentScript in document)) {
|
|
||||||
Object.defineProperty(document, currentScript, {
|
|
||||||
get: function(){
|
|
||||||
|
|
||||||
// IE 6-10 supports script readyState
|
|
||||||
// IE 10+ support stack trace
|
|
||||||
try { throw new Error(); }
|
|
||||||
catch (err) {
|
|
||||||
|
|
||||||
// Find the second match for the "at" string to get file src url from stack.
|
|
||||||
// Specifically works with the format of stack traces in IE.
|
|
||||||
var i, res = ((/.*at [^\(]*\((.*):.+:.+\)$/ig).exec(err.stack) || [false])[1];
|
|
||||||
|
|
||||||
// For all scripts on the page, if src matches or if ready state is interactive, return the script tag
|
|
||||||
for(i in scripts){
|
|
||||||
if(scripts[i].src == res || scripts[i].readyState == "interactive"){
|
|
||||||
return scripts[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no match, return null
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})(document);
|
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
|
||||||
|
|
||||||
/***/ "f87e":
|
|
||||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
/* harmony import */ var _node_modules_mini_css_extract_plugin_dist_loader_js_ref_6_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_App_vue_vue_type_style_index_0_id_015b8bdf_scoped_true_lang_css___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("5941");
|
|
||||||
/* harmony import */ var _node_modules_mini_css_extract_plugin_dist_loader_js_ref_6_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_App_vue_vue_type_style_index_0_id_015b8bdf_scoped_true_lang_css___WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_mini_css_extract_plugin_dist_loader_js_ref_6_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_App_vue_vue_type_style_index_0_id_015b8bdf_scoped_true_lang_css___WEBPACK_IMPORTED_MODULE_0__);
|
|
||||||
/* unused harmony reexport * */
|
|
||||||
/* unused harmony default export */ var _unused_webpack_default_export = (_node_modules_mini_css_extract_plugin_dist_loader_js_ref_6_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_App_vue_vue_type_style_index_0_id_015b8bdf_scoped_true_lang_css___WEBPACK_IMPORTED_MODULE_0___default.a);
|
|
||||||
|
|
||||||
/***/ }),
|
|
||||||
|
|
||||||
/***/ "fb15":
|
|
||||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
// ESM COMPAT FLAG
|
|
||||||
__webpack_require__.r(__webpack_exports__);
|
|
||||||
|
|
||||||
// CONCATENATED MODULE: ./node_modules/@vue/cli-service/lib/commands/build/setPublicPath.js
|
|
||||||
// This file is imported into lib/wc client bundles.
|
|
||||||
|
|
||||||
if (typeof window !== 'undefined') {
|
|
||||||
if (true) {
|
|
||||||
__webpack_require__("f6fd")
|
|
||||||
}
|
|
||||||
|
|
||||||
var i
|
|
||||||
if ((i = window.document.currentScript) && (i = i.src.match(/(.+\/)[^/]+\.js(\?.*)?$/))) {
|
|
||||||
__webpack_require__.p = i[1] // eslint-disable-line
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Indicate to webpack that this file can be concatenated
|
|
||||||
/* harmony default export */ var setPublicPath = (null);
|
|
||||||
|
|
||||||
// CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"5b04a8fd-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=template&id=015b8bdf&scoped=true&
|
|
||||||
var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[(_vm.$parent.titleTabActive==1)?_c('Records',{ref:"recordsPanel"}):_c('stream-table',{scopedSlots:_vm._u([{key:"default",fn:function(ref){
|
|
||||||
var stream = ref.row;
|
|
||||||
return [(_vm.isRecording(stream))?_c('m-button',{attrs:{"blink":""},on:{"click":function($event){return _vm.stopRecord(stream)}}},[_vm._v("正在录制")]):_c('m-button',{on:{"click":function($event){return _vm.record(stream)}}},[_vm._v("录制")])]}}])})],1)}
|
|
||||||
var staticRenderFns = []
|
|
||||||
|
|
||||||
|
|
||||||
// CONCATENATED MODULE: ./src/App.vue?vue&type=template&id=015b8bdf&scoped=true&
|
|
||||||
|
|
||||||
// CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"5b04a8fd-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/Records.vue?vue&type=template&id=675e088d&
|
|
||||||
var Recordsvue_type_template_id_675e088d_render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('mu-data-table',{attrs:{"data":_vm.data,"columns":_vm.columns},scopedSlots:_vm._u([{key:"default",fn:function(ref){
|
|
||||||
var row = ref.row;
|
|
||||||
return [_c('td',[_vm._v(_vm._s(row.Path))]),_c('td',[_vm._v(_vm._s(_vm.unitFormat(row.Size)))]),_c('td',[_vm._v(_vm._s(_vm.toDurationStr(row.Duration)))]),_c('td',[_c('mu-button',{attrs:{"icon":"","small":""},on:{"click":function($event){return _vm.play(row)}}},[_c('mu-icon',{attrs:{"value":"play_arrow"}})],1),_c('mu-button',{attrs:{"icon":"","small":""},on:{"click":function($event){return _vm.deleteFlv(row)}}},[_c('mu-icon',{attrs:{"value":"delete_forever"}})],1)],1)]}}])})}
|
|
||||||
var Recordsvue_type_template_id_675e088d_staticRenderFns = []
|
|
||||||
|
|
||||||
|
|
||||||
// CONCATENATED MODULE: ./src/components/Records.vue?vue&type=template&id=675e088d&
|
|
||||||
|
|
||||||
// CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/Records.vue?vue&type=script&lang=js&
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
/* harmony default export */ var Recordsvue_type_script_lang_js_ = ({
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
data: [],
|
|
||||||
columns:[
|
|
||||||
{
|
|
||||||
title:"文件路径"
|
|
||||||
},{
|
|
||||||
title:"大小"
|
|
||||||
},{
|
|
||||||
title:"时长"
|
|
||||||
},{title:"操作"}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
play(item) {
|
|
||||||
this.ajax.get(
|
|
||||||
"/record/flv/play",
|
|
||||||
{ streamPath: item.Path.replace(".flv", "") },
|
|
||||||
x => {
|
|
||||||
if (x == "success") {
|
|
||||||
this.ajax.getJSON("/record/flv/list", x => this.data = x);
|
|
||||||
this.$toast.success("开始发布");
|
|
||||||
} else {
|
|
||||||
this.$toast.error(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
deleteFlv(item) {
|
|
||||||
this.$confirm("是否删除Flv文件", "提示").then(result => {
|
|
||||||
if (result) {
|
|
||||||
return this.ajax.get(
|
|
||||||
"/record/flv/delete",
|
|
||||||
{ streamPath: item.Path.replace(".flv", "") },
|
|
||||||
x => {
|
|
||||||
if (x == "success") {
|
|
||||||
this.$toast.success("删除成功");
|
|
||||||
} else {
|
|
||||||
this.$toast.error(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
toDurationStr(value) {
|
|
||||||
if (value > 1000) {
|
|
||||||
let s = value / 1000;
|
|
||||||
if (s > 60) {
|
|
||||||
s = s | 0;
|
|
||||||
let min = (s / 60) >> 0;
|
|
||||||
if (min > 60) {
|
|
||||||
let hour = (min / 60) >> 0;
|
|
||||||
return hour + "hour" + (min % 60) + "min";
|
|
||||||
} else {
|
|
||||||
return min + "min" + (s % 60) + "s";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return s.toFixed(3) + "s";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return value + "ms";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.ajax.getJSON("/record/flv/list", x => this.data = x);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// CONCATENATED MODULE: ./src/components/Records.vue?vue&type=script&lang=js&
|
|
||||||
/* harmony default export */ var components_Recordsvue_type_script_lang_js_ = (Recordsvue_type_script_lang_js_);
|
|
||||||
// CONCATENATED MODULE: ./node_modules/vue-loader/lib/runtime/componentNormalizer.js
|
|
||||||
/* globals __VUE_SSR_CONTEXT__ */
|
|
||||||
|
|
||||||
// IMPORTANT: Do NOT use ES2015 features in this file (except for modules).
|
|
||||||
// This module is a runtime utility for cleaner component module output and will
|
|
||||||
// be included in the final webpack user bundle.
|
|
||||||
|
|
||||||
function normalizeComponent (
|
|
||||||
scriptExports,
|
|
||||||
render,
|
|
||||||
staticRenderFns,
|
|
||||||
functionalTemplate,
|
|
||||||
injectStyles,
|
|
||||||
scopeId,
|
|
||||||
moduleIdentifier, /* server only */
|
|
||||||
shadowMode /* vue-cli only */
|
|
||||||
) {
|
|
||||||
// Vue.extend constructor export interop
|
|
||||||
var options = typeof scriptExports === 'function'
|
|
||||||
? scriptExports.options
|
|
||||||
: scriptExports
|
|
||||||
|
|
||||||
// render functions
|
|
||||||
if (render) {
|
|
||||||
options.render = render
|
|
||||||
options.staticRenderFns = staticRenderFns
|
|
||||||
options._compiled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// functional template
|
|
||||||
if (functionalTemplate) {
|
|
||||||
options.functional = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// scopedId
|
|
||||||
if (scopeId) {
|
|
||||||
options._scopeId = 'data-v-' + scopeId
|
|
||||||
}
|
|
||||||
|
|
||||||
var hook
|
|
||||||
if (moduleIdentifier) { // server build
|
|
||||||
hook = function (context) {
|
|
||||||
// 2.3 injection
|
|
||||||
context =
|
|
||||||
context || // cached call
|
|
||||||
(this.$vnode && this.$vnode.ssrContext) || // stateful
|
|
||||||
(this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional
|
|
||||||
// 2.2 with runInNewContext: true
|
|
||||||
if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
|
|
||||||
context = __VUE_SSR_CONTEXT__
|
|
||||||
}
|
|
||||||
// inject component styles
|
|
||||||
if (injectStyles) {
|
|
||||||
injectStyles.call(this, context)
|
|
||||||
}
|
|
||||||
// register component module identifier for async chunk inferrence
|
|
||||||
if (context && context._registeredComponents) {
|
|
||||||
context._registeredComponents.add(moduleIdentifier)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// used by ssr in case component is cached and beforeCreate
|
|
||||||
// never gets called
|
|
||||||
options._ssrRegister = hook
|
|
||||||
} else if (injectStyles) {
|
|
||||||
hook = shadowMode
|
|
||||||
? function () { injectStyles.call(this, this.$root.$options.shadowRoot) }
|
|
||||||
: injectStyles
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hook) {
|
|
||||||
if (options.functional) {
|
|
||||||
// for template-only hot-reload because in that case the render fn doesn't
|
|
||||||
// go through the normalizer
|
|
||||||
options._injectStyles = hook
|
|
||||||
// register for functional component in vue file
|
|
||||||
var originalRender = options.render
|
|
||||||
options.render = function renderWithStyleInjection (h, context) {
|
|
||||||
hook.call(context)
|
|
||||||
return originalRender(h, context)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// inject component registration as beforeCreate hook
|
|
||||||
var existing = options.beforeCreate
|
|
||||||
options.beforeCreate = existing
|
|
||||||
? [].concat(existing, hook)
|
|
||||||
: [hook]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
exports: scriptExports,
|
|
||||||
options: options
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CONCATENATED MODULE: ./src/components/Records.vue
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* normalize component */
|
|
||||||
|
|
||||||
var component = normalizeComponent(
|
|
||||||
components_Recordsvue_type_script_lang_js_,
|
|
||||||
Recordsvue_type_template_id_675e088d_render,
|
|
||||||
Recordsvue_type_template_id_675e088d_staticRenderFns,
|
|
||||||
false,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
/* harmony default export */ var Records = (component.exports);
|
|
||||||
// CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=script&lang=js&
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
/* harmony default export */ var Appvue_type_script_lang_js_ = ({
|
|
||||||
components: {
|
|
||||||
Records: Records
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
record(item) {
|
|
||||||
let append = false;
|
|
||||||
this.$confirm(
|
|
||||||
h =>
|
|
||||||
h("mu-switch", {
|
|
||||||
props: {
|
|
||||||
label: "追加模式"
|
|
||||||
},
|
|
||||||
on: {
|
|
||||||
change(value) {
|
|
||||||
append = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
"是否开始录制"
|
|
||||||
).then(result => {
|
|
||||||
if (result) {
|
|
||||||
this.ajax.get(
|
|
||||||
"/record/flv?append=" + append,
|
|
||||||
{ streamPath: item.StreamPath },
|
|
||||||
x => {
|
|
||||||
if (x == "success") {
|
|
||||||
this.$toast.success(
|
|
||||||
"开始录制" + (append ? "(追加模式)" : "")
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
this.$toast.error(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
stopRecord(item) {
|
|
||||||
this.$confirm("是否停止录制", "提示").then(result => {
|
|
||||||
if(result)
|
|
||||||
{
|
|
||||||
this.ajax.get(
|
|
||||||
"/record/flv/stop",
|
|
||||||
{ streamPath: item.StreamPath },
|
|
||||||
x => {
|
|
||||||
if (x == "success") {
|
|
||||||
this.$toast.success("停止录制");
|
|
||||||
} else {
|
|
||||||
this.$toast.error(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
isRecording(item) {
|
|
||||||
return (
|
|
||||||
item.SubscriberInfo &&
|
|
||||||
item.SubscriberInfo.find(x => x.Type == "FlvRecord")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.$parent.titleTabs = ["StreamList", "录制的视频"];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// CONCATENATED MODULE: ./src/App.vue?vue&type=script&lang=js&
|
|
||||||
/* harmony default export */ var src_Appvue_type_script_lang_js_ = (Appvue_type_script_lang_js_);
|
|
||||||
// EXTERNAL MODULE: ./src/App.vue?vue&type=style&index=0&id=015b8bdf&scoped=true&lang=css&
|
|
||||||
var Appvue_type_style_index_0_id_015b8bdf_scoped_true_lang_css_ = __webpack_require__("f87e");
|
|
||||||
|
|
||||||
// CONCATENATED MODULE: ./src/App.vue
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* normalize component */
|
|
||||||
|
|
||||||
var App_component = normalizeComponent(
|
|
||||||
src_Appvue_type_script_lang_js_,
|
|
||||||
render,
|
|
||||||
staticRenderFns,
|
|
||||||
false,
|
|
||||||
null,
|
|
||||||
"015b8bdf",
|
|
||||||
null
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
/* harmony default export */ var App = (App_component.exports);
|
|
||||||
// CONCATENATED MODULE: ./node_modules/@vue/cli-service/lib/commands/build/entry-lib.js
|
|
||||||
|
|
||||||
|
|
||||||
/* harmony default export */ var entry_lib = __webpack_exports__["default"] = (App);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***/ })
|
|
||||||
|
|
||||||
/******/ })["default"];
|
|
||||||
//# sourceMappingURL=plugin-record.common.js.map
|
|
||||||
1
ui/dist/plugin-record.common.js.map
vendored
1
ui/dist/plugin-record.common.js.map
vendored
File diff suppressed because one or more lines are too long
1
ui/dist/plugin-record.css
vendored
1
ui/dist/plugin-record.css
vendored
@@ -1 +0,0 @@
|
|||||||
@-webkit-keyframes recording-data-v-015b8bdf{0%{opacity:.2}50%{opacity:1}to{opacity:.2}}@keyframes recording-data-v-015b8bdf{0%{opacity:.2}50%{opacity:1}to{opacity:.2}}.recording[data-v-015b8bdf]{-webkit-animation:recording-data-v-015b8bdf 1s infinite;animation:recording-data-v-015b8bdf 1s infinite}.layout[data-v-015b8bdf]{padding-bottom:30px;display:flex;flex-wrap:wrap}.room[data-v-015b8bdf]{width:250px;margin:10px;text-align:left}.empty[data-v-015b8bdf]{color:#ffc107;width:100%;min-height:500px;display:flex;justify-content:center;align-items:center}.status[data-v-015b8bdf]{position:fixed;display:flex;left:5px;bottom:10px}.status>div[data-v-015b8bdf]{margin:0 5px}
|
|
||||||
534
ui/dist/plugin-record.umd.js
vendored
534
ui/dist/plugin-record.umd.js
vendored
@@ -1,534 +0,0 @@
|
|||||||
(function webpackUniversalModuleDefinition(root, factory) {
|
|
||||||
if(typeof exports === 'object' && typeof module === 'object')
|
|
||||||
module.exports = factory();
|
|
||||||
else if(typeof define === 'function' && define.amd)
|
|
||||||
define([], factory);
|
|
||||||
else if(typeof exports === 'object')
|
|
||||||
exports["plugin-record"] = factory();
|
|
||||||
else
|
|
||||||
root["plugin-record"] = factory();
|
|
||||||
})((typeof self !== 'undefined' ? self : this), function() {
|
|
||||||
return /******/ (function(modules) { // webpackBootstrap
|
|
||||||
/******/ // The module cache
|
|
||||||
/******/ var installedModules = {};
|
|
||||||
/******/
|
|
||||||
/******/ // The require function
|
|
||||||
/******/ function __webpack_require__(moduleId) {
|
|
||||||
/******/
|
|
||||||
/******/ // Check if module is in cache
|
|
||||||
/******/ if(installedModules[moduleId]) {
|
|
||||||
/******/ return installedModules[moduleId].exports;
|
|
||||||
/******/ }
|
|
||||||
/******/ // Create a new module (and put it into the cache)
|
|
||||||
/******/ var module = installedModules[moduleId] = {
|
|
||||||
/******/ i: moduleId,
|
|
||||||
/******/ l: false,
|
|
||||||
/******/ exports: {}
|
|
||||||
/******/ };
|
|
||||||
/******/
|
|
||||||
/******/ // Execute the module function
|
|
||||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
|
||||||
/******/
|
|
||||||
/******/ // Flag the module as loaded
|
|
||||||
/******/ module.l = true;
|
|
||||||
/******/
|
|
||||||
/******/ // Return the exports of the module
|
|
||||||
/******/ return module.exports;
|
|
||||||
/******/ }
|
|
||||||
/******/
|
|
||||||
/******/
|
|
||||||
/******/ // expose the modules object (__webpack_modules__)
|
|
||||||
/******/ __webpack_require__.m = modules;
|
|
||||||
/******/
|
|
||||||
/******/ // expose the module cache
|
|
||||||
/******/ __webpack_require__.c = installedModules;
|
|
||||||
/******/
|
|
||||||
/******/ // define getter function for harmony exports
|
|
||||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
|
||||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
|
||||||
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
|
|
||||||
/******/ }
|
|
||||||
/******/ };
|
|
||||||
/******/
|
|
||||||
/******/ // define __esModule on exports
|
|
||||||
/******/ __webpack_require__.r = function(exports) {
|
|
||||||
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
|
||||||
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
||||||
/******/ }
|
|
||||||
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
|
||||||
/******/ };
|
|
||||||
/******/
|
|
||||||
/******/ // create a fake namespace object
|
|
||||||
/******/ // mode & 1: value is a module id, require it
|
|
||||||
/******/ // mode & 2: merge all properties of value into the ns
|
|
||||||
/******/ // mode & 4: return value when already ns object
|
|
||||||
/******/ // mode & 8|1: behave like require
|
|
||||||
/******/ __webpack_require__.t = function(value, mode) {
|
|
||||||
/******/ if(mode & 1) value = __webpack_require__(value);
|
|
||||||
/******/ if(mode & 8) return value;
|
|
||||||
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
|
|
||||||
/******/ var ns = Object.create(null);
|
|
||||||
/******/ __webpack_require__.r(ns);
|
|
||||||
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
|
|
||||||
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
|
|
||||||
/******/ return ns;
|
|
||||||
/******/ };
|
|
||||||
/******/
|
|
||||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
|
||||||
/******/ __webpack_require__.n = function(module) {
|
|
||||||
/******/ var getter = module && module.__esModule ?
|
|
||||||
/******/ function getDefault() { return module['default']; } :
|
|
||||||
/******/ function getModuleExports() { return module; };
|
|
||||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
|
||||||
/******/ return getter;
|
|
||||||
/******/ };
|
|
||||||
/******/
|
|
||||||
/******/ // Object.prototype.hasOwnProperty.call
|
|
||||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
|
||||||
/******/
|
|
||||||
/******/ // __webpack_public_path__
|
|
||||||
/******/ __webpack_require__.p = "";
|
|
||||||
/******/
|
|
||||||
/******/
|
|
||||||
/******/ // Load entry module and return exports
|
|
||||||
/******/ return __webpack_require__(__webpack_require__.s = "fb15");
|
|
||||||
/******/ })
|
|
||||||
/************************************************************************/
|
|
||||||
/******/ ({
|
|
||||||
|
|
||||||
/***/ "5941":
|
|
||||||
/***/ (function(module, exports, __webpack_require__) {
|
|
||||||
|
|
||||||
// extracted by mini-css-extract-plugin
|
|
||||||
|
|
||||||
/***/ }),
|
|
||||||
|
|
||||||
/***/ "f6fd":
|
|
||||||
/***/ (function(module, exports) {
|
|
||||||
|
|
||||||
// document.currentScript polyfill by Adam Miller
|
|
||||||
|
|
||||||
// MIT license
|
|
||||||
|
|
||||||
(function(document){
|
|
||||||
var currentScript = "currentScript",
|
|
||||||
scripts = document.getElementsByTagName('script'); // Live NodeList collection
|
|
||||||
|
|
||||||
// If browser needs currentScript polyfill, add get currentScript() to the document object
|
|
||||||
if (!(currentScript in document)) {
|
|
||||||
Object.defineProperty(document, currentScript, {
|
|
||||||
get: function(){
|
|
||||||
|
|
||||||
// IE 6-10 supports script readyState
|
|
||||||
// IE 10+ support stack trace
|
|
||||||
try { throw new Error(); }
|
|
||||||
catch (err) {
|
|
||||||
|
|
||||||
// Find the second match for the "at" string to get file src url from stack.
|
|
||||||
// Specifically works with the format of stack traces in IE.
|
|
||||||
var i, res = ((/.*at [^\(]*\((.*):.+:.+\)$/ig).exec(err.stack) || [false])[1];
|
|
||||||
|
|
||||||
// For all scripts on the page, if src matches or if ready state is interactive, return the script tag
|
|
||||||
for(i in scripts){
|
|
||||||
if(scripts[i].src == res || scripts[i].readyState == "interactive"){
|
|
||||||
return scripts[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no match, return null
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})(document);
|
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
|
||||||
|
|
||||||
/***/ "f87e":
|
|
||||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
/* harmony import */ var _node_modules_mini_css_extract_plugin_dist_loader_js_ref_6_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_App_vue_vue_type_style_index_0_id_015b8bdf_scoped_true_lang_css___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("5941");
|
|
||||||
/* harmony import */ var _node_modules_mini_css_extract_plugin_dist_loader_js_ref_6_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_App_vue_vue_type_style_index_0_id_015b8bdf_scoped_true_lang_css___WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_mini_css_extract_plugin_dist_loader_js_ref_6_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_App_vue_vue_type_style_index_0_id_015b8bdf_scoped_true_lang_css___WEBPACK_IMPORTED_MODULE_0__);
|
|
||||||
/* unused harmony reexport * */
|
|
||||||
/* unused harmony default export */ var _unused_webpack_default_export = (_node_modules_mini_css_extract_plugin_dist_loader_js_ref_6_oneOf_1_0_node_modules_css_loader_dist_cjs_js_ref_6_oneOf_1_1_node_modules_vue_loader_lib_loaders_stylePostLoader_js_node_modules_postcss_loader_src_index_js_ref_6_oneOf_1_2_node_modules_cache_loader_dist_cjs_js_ref_0_0_node_modules_vue_loader_lib_index_js_vue_loader_options_App_vue_vue_type_style_index_0_id_015b8bdf_scoped_true_lang_css___WEBPACK_IMPORTED_MODULE_0___default.a);
|
|
||||||
|
|
||||||
/***/ }),
|
|
||||||
|
|
||||||
/***/ "fb15":
|
|
||||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
// ESM COMPAT FLAG
|
|
||||||
__webpack_require__.r(__webpack_exports__);
|
|
||||||
|
|
||||||
// CONCATENATED MODULE: ./node_modules/@vue/cli-service/lib/commands/build/setPublicPath.js
|
|
||||||
// This file is imported into lib/wc client bundles.
|
|
||||||
|
|
||||||
if (typeof window !== 'undefined') {
|
|
||||||
if (true) {
|
|
||||||
__webpack_require__("f6fd")
|
|
||||||
}
|
|
||||||
|
|
||||||
var i
|
|
||||||
if ((i = window.document.currentScript) && (i = i.src.match(/(.+\/)[^/]+\.js(\?.*)?$/))) {
|
|
||||||
__webpack_require__.p = i[1] // eslint-disable-line
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Indicate to webpack that this file can be concatenated
|
|
||||||
/* harmony default export */ var setPublicPath = (null);
|
|
||||||
|
|
||||||
// CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"5b04a8fd-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=template&id=015b8bdf&scoped=true&
|
|
||||||
var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[(_vm.$parent.titleTabActive==1)?_c('Records',{ref:"recordsPanel"}):_c('stream-table',{scopedSlots:_vm._u([{key:"default",fn:function(ref){
|
|
||||||
var stream = ref.row;
|
|
||||||
return [(_vm.isRecording(stream))?_c('m-button',{attrs:{"blink":""},on:{"click":function($event){return _vm.stopRecord(stream)}}},[_vm._v("正在录制")]):_c('m-button',{on:{"click":function($event){return _vm.record(stream)}}},[_vm._v("录制")])]}}])})],1)}
|
|
||||||
var staticRenderFns = []
|
|
||||||
|
|
||||||
|
|
||||||
// CONCATENATED MODULE: ./src/App.vue?vue&type=template&id=015b8bdf&scoped=true&
|
|
||||||
|
|
||||||
// CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"5b04a8fd-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/Records.vue?vue&type=template&id=675e088d&
|
|
||||||
var Recordsvue_type_template_id_675e088d_render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('mu-data-table',{attrs:{"data":_vm.data,"columns":_vm.columns},scopedSlots:_vm._u([{key:"default",fn:function(ref){
|
|
||||||
var row = ref.row;
|
|
||||||
return [_c('td',[_vm._v(_vm._s(row.Path))]),_c('td',[_vm._v(_vm._s(_vm.unitFormat(row.Size)))]),_c('td',[_vm._v(_vm._s(_vm.toDurationStr(row.Duration)))]),_c('td',[_c('mu-button',{attrs:{"icon":"","small":""},on:{"click":function($event){return _vm.play(row)}}},[_c('mu-icon',{attrs:{"value":"play_arrow"}})],1),_c('mu-button',{attrs:{"icon":"","small":""},on:{"click":function($event){return _vm.deleteFlv(row)}}},[_c('mu-icon',{attrs:{"value":"delete_forever"}})],1)],1)]}}])})}
|
|
||||||
var Recordsvue_type_template_id_675e088d_staticRenderFns = []
|
|
||||||
|
|
||||||
|
|
||||||
// CONCATENATED MODULE: ./src/components/Records.vue?vue&type=template&id=675e088d&
|
|
||||||
|
|
||||||
// CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/Records.vue?vue&type=script&lang=js&
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
/* harmony default export */ var Recordsvue_type_script_lang_js_ = ({
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
data: [],
|
|
||||||
columns:[
|
|
||||||
{
|
|
||||||
title:"文件路径"
|
|
||||||
},{
|
|
||||||
title:"大小"
|
|
||||||
},{
|
|
||||||
title:"时长"
|
|
||||||
},{title:"操作"}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
play(item) {
|
|
||||||
this.ajax.get(
|
|
||||||
"/record/flv/play",
|
|
||||||
{ streamPath: item.Path.replace(".flv", "") },
|
|
||||||
x => {
|
|
||||||
if (x == "success") {
|
|
||||||
this.ajax.getJSON("/record/flv/list", x => this.data = x);
|
|
||||||
this.$toast.success("开始发布");
|
|
||||||
} else {
|
|
||||||
this.$toast.error(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
deleteFlv(item) {
|
|
||||||
this.$confirm("是否删除Flv文件", "提示").then(result => {
|
|
||||||
if (result) {
|
|
||||||
return this.ajax.get(
|
|
||||||
"/record/flv/delete",
|
|
||||||
{ streamPath: item.Path.replace(".flv", "") },
|
|
||||||
x => {
|
|
||||||
if (x == "success") {
|
|
||||||
this.$toast.success("删除成功");
|
|
||||||
} else {
|
|
||||||
this.$toast.error(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
toDurationStr(value) {
|
|
||||||
if (value > 1000) {
|
|
||||||
let s = value / 1000;
|
|
||||||
if (s > 60) {
|
|
||||||
s = s | 0;
|
|
||||||
let min = (s / 60) >> 0;
|
|
||||||
if (min > 60) {
|
|
||||||
let hour = (min / 60) >> 0;
|
|
||||||
return hour + "hour" + (min % 60) + "min";
|
|
||||||
} else {
|
|
||||||
return min + "min" + (s % 60) + "s";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return s.toFixed(3) + "s";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return value + "ms";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.ajax.getJSON("/record/flv/list", x => this.data = x);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// CONCATENATED MODULE: ./src/components/Records.vue?vue&type=script&lang=js&
|
|
||||||
/* harmony default export */ var components_Recordsvue_type_script_lang_js_ = (Recordsvue_type_script_lang_js_);
|
|
||||||
// CONCATENATED MODULE: ./node_modules/vue-loader/lib/runtime/componentNormalizer.js
|
|
||||||
/* globals __VUE_SSR_CONTEXT__ */
|
|
||||||
|
|
||||||
// IMPORTANT: Do NOT use ES2015 features in this file (except for modules).
|
|
||||||
// This module is a runtime utility for cleaner component module output and will
|
|
||||||
// be included in the final webpack user bundle.
|
|
||||||
|
|
||||||
function normalizeComponent (
|
|
||||||
scriptExports,
|
|
||||||
render,
|
|
||||||
staticRenderFns,
|
|
||||||
functionalTemplate,
|
|
||||||
injectStyles,
|
|
||||||
scopeId,
|
|
||||||
moduleIdentifier, /* server only */
|
|
||||||
shadowMode /* vue-cli only */
|
|
||||||
) {
|
|
||||||
// Vue.extend constructor export interop
|
|
||||||
var options = typeof scriptExports === 'function'
|
|
||||||
? scriptExports.options
|
|
||||||
: scriptExports
|
|
||||||
|
|
||||||
// render functions
|
|
||||||
if (render) {
|
|
||||||
options.render = render
|
|
||||||
options.staticRenderFns = staticRenderFns
|
|
||||||
options._compiled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// functional template
|
|
||||||
if (functionalTemplate) {
|
|
||||||
options.functional = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// scopedId
|
|
||||||
if (scopeId) {
|
|
||||||
options._scopeId = 'data-v-' + scopeId
|
|
||||||
}
|
|
||||||
|
|
||||||
var hook
|
|
||||||
if (moduleIdentifier) { // server build
|
|
||||||
hook = function (context) {
|
|
||||||
// 2.3 injection
|
|
||||||
context =
|
|
||||||
context || // cached call
|
|
||||||
(this.$vnode && this.$vnode.ssrContext) || // stateful
|
|
||||||
(this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional
|
|
||||||
// 2.2 with runInNewContext: true
|
|
||||||
if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
|
|
||||||
context = __VUE_SSR_CONTEXT__
|
|
||||||
}
|
|
||||||
// inject component styles
|
|
||||||
if (injectStyles) {
|
|
||||||
injectStyles.call(this, context)
|
|
||||||
}
|
|
||||||
// register component module identifier for async chunk inferrence
|
|
||||||
if (context && context._registeredComponents) {
|
|
||||||
context._registeredComponents.add(moduleIdentifier)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// used by ssr in case component is cached and beforeCreate
|
|
||||||
// never gets called
|
|
||||||
options._ssrRegister = hook
|
|
||||||
} else if (injectStyles) {
|
|
||||||
hook = shadowMode
|
|
||||||
? function () { injectStyles.call(this, this.$root.$options.shadowRoot) }
|
|
||||||
: injectStyles
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hook) {
|
|
||||||
if (options.functional) {
|
|
||||||
// for template-only hot-reload because in that case the render fn doesn't
|
|
||||||
// go through the normalizer
|
|
||||||
options._injectStyles = hook
|
|
||||||
// register for functional component in vue file
|
|
||||||
var originalRender = options.render
|
|
||||||
options.render = function renderWithStyleInjection (h, context) {
|
|
||||||
hook.call(context)
|
|
||||||
return originalRender(h, context)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// inject component registration as beforeCreate hook
|
|
||||||
var existing = options.beforeCreate
|
|
||||||
options.beforeCreate = existing
|
|
||||||
? [].concat(existing, hook)
|
|
||||||
: [hook]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
exports: scriptExports,
|
|
||||||
options: options
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CONCATENATED MODULE: ./src/components/Records.vue
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* normalize component */
|
|
||||||
|
|
||||||
var component = normalizeComponent(
|
|
||||||
components_Recordsvue_type_script_lang_js_,
|
|
||||||
Recordsvue_type_template_id_675e088d_render,
|
|
||||||
Recordsvue_type_template_id_675e088d_staticRenderFns,
|
|
||||||
false,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
/* harmony default export */ var Records = (component.exports);
|
|
||||||
// CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=script&lang=js&
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
/* harmony default export */ var Appvue_type_script_lang_js_ = ({
|
|
||||||
components: {
|
|
||||||
Records: Records
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
record(item) {
|
|
||||||
let append = false;
|
|
||||||
this.$confirm(
|
|
||||||
h =>
|
|
||||||
h("mu-switch", {
|
|
||||||
props: {
|
|
||||||
label: "追加模式"
|
|
||||||
},
|
|
||||||
on: {
|
|
||||||
change(value) {
|
|
||||||
append = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
"是否开始录制"
|
|
||||||
).then(result => {
|
|
||||||
if (result) {
|
|
||||||
this.ajax.get(
|
|
||||||
"/record/flv?append=" + append,
|
|
||||||
{ streamPath: item.StreamPath },
|
|
||||||
x => {
|
|
||||||
if (x == "success") {
|
|
||||||
this.$toast.success(
|
|
||||||
"开始录制" + (append ? "(追加模式)" : "")
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
this.$toast.error(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
stopRecord(item) {
|
|
||||||
this.$confirm("是否停止录制", "提示").then(result => {
|
|
||||||
if(result)
|
|
||||||
{
|
|
||||||
this.ajax.get(
|
|
||||||
"/record/flv/stop",
|
|
||||||
{ streamPath: item.StreamPath },
|
|
||||||
x => {
|
|
||||||
if (x == "success") {
|
|
||||||
this.$toast.success("停止录制");
|
|
||||||
} else {
|
|
||||||
this.$toast.error(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
isRecording(item) {
|
|
||||||
return (
|
|
||||||
item.SubscriberInfo &&
|
|
||||||
item.SubscriberInfo.find(x => x.Type == "FlvRecord")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.$parent.titleTabs = ["StreamList", "录制的视频"];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// CONCATENATED MODULE: ./src/App.vue?vue&type=script&lang=js&
|
|
||||||
/* harmony default export */ var src_Appvue_type_script_lang_js_ = (Appvue_type_script_lang_js_);
|
|
||||||
// EXTERNAL MODULE: ./src/App.vue?vue&type=style&index=0&id=015b8bdf&scoped=true&lang=css&
|
|
||||||
var Appvue_type_style_index_0_id_015b8bdf_scoped_true_lang_css_ = __webpack_require__("f87e");
|
|
||||||
|
|
||||||
// CONCATENATED MODULE: ./src/App.vue
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* normalize component */
|
|
||||||
|
|
||||||
var App_component = normalizeComponent(
|
|
||||||
src_Appvue_type_script_lang_js_,
|
|
||||||
render,
|
|
||||||
staticRenderFns,
|
|
||||||
false,
|
|
||||||
null,
|
|
||||||
"015b8bdf",
|
|
||||||
null
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
/* harmony default export */ var App = (App_component.exports);
|
|
||||||
// CONCATENATED MODULE: ./node_modules/@vue/cli-service/lib/commands/build/entry-lib.js
|
|
||||||
|
|
||||||
|
|
||||||
/* harmony default export */ var entry_lib = __webpack_exports__["default"] = (App);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***/ })
|
|
||||||
|
|
||||||
/******/ })["default"];
|
|
||||||
});
|
|
||||||
//# sourceMappingURL=plugin-record.umd.js.map
|
|
||||||
1
ui/dist/plugin-record.umd.js.map
vendored
1
ui/dist/plugin-record.umd.js.map
vendored
File diff suppressed because one or more lines are too long
2
ui/dist/plugin-record.umd.min.js
vendored
2
ui/dist/plugin-record.umd.min.js
vendored
File diff suppressed because one or more lines are too long
1
ui/dist/plugin-record.umd.min.js.map
vendored
1
ui/dist/plugin-record.umd.min.js.map
vendored
File diff suppressed because one or more lines are too long
9560
ui/package-lock.json
generated
9560
ui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "dashboard",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "dashboard of record plugin for monibuca",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"build": "vue-cli-service build --target lib --name plugin-record"
|
|
||||||
},
|
|
||||||
"author": "dexter",
|
|
||||||
"license": "ISC",
|
|
||||||
"devDependencies": {
|
|
||||||
"@vue/cli-service": "^4.2.3",
|
|
||||||
"vue-template-compiler": "^2.6.11"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
135
ui/src/App.vue
135
ui/src/App.vue
@@ -1,135 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<Records ref="recordsPanel" v-if="$parent.titleTabActive==1" />
|
|
||||||
<stream-table v-else>
|
|
||||||
<template v-slot="{row:stream}">
|
|
||||||
<m-button v-if="isRecording(stream)" @click="stopRecord(stream)" blink>正在录制</m-button>
|
|
||||||
<m-button v-else @click="record(stream)">录制</m-button>
|
|
||||||
</template>
|
|
||||||
</stream-table>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Records from "./components/Records";
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
Records
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
record(item) {
|
|
||||||
let append = false;
|
|
||||||
this.$confirm(
|
|
||||||
h =>
|
|
||||||
h("mu-switch", {
|
|
||||||
props: {
|
|
||||||
label: "追加模式"
|
|
||||||
},
|
|
||||||
on: {
|
|
||||||
change(value) {
|
|
||||||
append = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
"是否开始录制"
|
|
||||||
).then(result => {
|
|
||||||
if (result) {
|
|
||||||
this.ajax.get(
|
|
||||||
"/record/flv?append=" + append,
|
|
||||||
{ streamPath: item.StreamPath },
|
|
||||||
x => {
|
|
||||||
if (x == "success") {
|
|
||||||
this.$toast.success(
|
|
||||||
"开始录制" + (append ? "(追加模式)" : "")
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
this.$toast.error(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
stopRecord(item) {
|
|
||||||
this.$confirm("是否停止录制", "提示").then(result => {
|
|
||||||
if(result)
|
|
||||||
{
|
|
||||||
this.ajax.get(
|
|
||||||
"/record/flv/stop",
|
|
||||||
{ streamPath: item.StreamPath },
|
|
||||||
x => {
|
|
||||||
if (x == "success") {
|
|
||||||
this.$toast.success("停止录制");
|
|
||||||
} else {
|
|
||||||
this.$toast.error(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
isRecording(item) {
|
|
||||||
return (
|
|
||||||
item.SubscriberInfo &&
|
|
||||||
item.SubscriberInfo.find(x => x.Type == "FlvRecord")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.$parent.titleTabs = ["StreamList", "录制的视频"];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
@keyframes recording {
|
|
||||||
0% {
|
|
||||||
opacity: 0.2;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 0.2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.recording {
|
|
||||||
animation: recording 1s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.layout {
|
|
||||||
padding-bottom: 30px;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.room {
|
|
||||||
width: 250px;
|
|
||||||
margin: 10px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty {
|
|
||||||
color: #ffc107;
|
|
||||||
width: 100%;
|
|
||||||
min-height: 500px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status {
|
|
||||||
position: fixed;
|
|
||||||
display: flex;
|
|
||||||
left: 5px;
|
|
||||||
bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status > div {
|
|
||||||
margin: 0 5px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
<template>
|
|
||||||
<mu-data-table :data="data" :columns="columns">
|
|
||||||
<template #default="{row}">
|
|
||||||
<td>{{row.Path}}</td>
|
|
||||||
<td>{{unitFormat(row.Size)}}</td>
|
|
||||||
<td>{{toDurationStr(row.Duration)}}</td>
|
|
||||||
<td>
|
|
||||||
<mu-button @click="play(row)" icon small>
|
|
||||||
<mu-icon value="play_arrow" />
|
|
||||||
</mu-button>
|
|
||||||
<mu-button @click="deleteFlv(row)" icon small>
|
|
||||||
<mu-icon value="delete_forever" />
|
|
||||||
</mu-button>
|
|
||||||
</td>
|
|
||||||
</template>
|
|
||||||
</mu-data-table>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
data: [],
|
|
||||||
columns:[
|
|
||||||
{
|
|
||||||
title:"文件路径"
|
|
||||||
},{
|
|
||||||
title:"大小"
|
|
||||||
},{
|
|
||||||
title:"时长"
|
|
||||||
},{title:"操作"}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
play(item) {
|
|
||||||
this.ajax.get(
|
|
||||||
"/record/flv/play",
|
|
||||||
{ streamPath: item.Path.replace(".flv", "") },
|
|
||||||
x => {
|
|
||||||
if (x == "success") {
|
|
||||||
this.ajax.getJSON("/record/flv/list", x => this.data = x);
|
|
||||||
this.$toast.success("开始发布");
|
|
||||||
} else {
|
|
||||||
this.$toast.error(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
deleteFlv(item) {
|
|
||||||
this.$confirm("是否删除Flv文件", "提示").then(result => {
|
|
||||||
if (result) {
|
|
||||||
return this.ajax.get(
|
|
||||||
"/record/flv/delete",
|
|
||||||
{ streamPath: item.Path.replace(".flv", "") },
|
|
||||||
x => {
|
|
||||||
if (x == "success") {
|
|
||||||
this.$toast.success("删除成功");
|
|
||||||
} else {
|
|
||||||
this.$toast.error(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
toDurationStr(value) {
|
|
||||||
if (value > 1000) {
|
|
||||||
let s = value / 1000;
|
|
||||||
if (s > 60) {
|
|
||||||
s = s | 0;
|
|
||||||
let min = (s / 60) >> 0;
|
|
||||||
if (min > 60) {
|
|
||||||
let hour = (min / 60) >> 0;
|
|
||||||
return hour + "hour" + (min % 60) + "min";
|
|
||||||
} else {
|
|
||||||
return min + "min" + (s % 60) + "s";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return s.toFixed(3) + "s";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return value + "ms";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.ajax.getJSON("/record/flv/list", x => this.data = x);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user