2021-11-27 07:53:27 +03:00
|
|
|
package core
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
"github.com/grafov/m3u8"
|
|
|
|
"github.com/owncast/owncast/config"
|
|
|
|
"github.com/owncast/owncast/static"
|
|
|
|
"github.com/owncast/owncast/utils"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
|
|
|
func appendOfflineToVariantPlaylist(index int, playlistFilePath string) {
|
2022-01-28 01:34:25 +03:00
|
|
|
existingPlaylistContents, err := os.ReadFile(playlistFilePath) // nolint: gosec
|
2021-11-27 07:53:27 +03:00
|
|
|
if err != nil {
|
2022-01-28 01:34:25 +03:00
|
|
|
log.Debugln("unable to read existing playlist file", err)
|
|
|
|
return
|
2021-11-27 07:53:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
tmpFileName := fmt.Sprintf("tmp-stream-%d.m3u8", index)
|
2022-03-08 03:30:40 +03:00
|
|
|
atomicWriteTmpPlaylistFile, err := os.CreateTemp(config.TempDir, tmpFileName)
|
2021-11-27 07:53:27 +03:00
|
|
|
if err != nil {
|
|
|
|
log.Errorln("error creating tmp playlist file to write to", playlistFilePath, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-01-28 01:34:25 +03:00
|
|
|
// Write the existing playlist contents
|
|
|
|
if _, err := atomicWriteTmpPlaylistFile.Write(existingPlaylistContents); err != nil {
|
|
|
|
log.Debugln("error writing existing playlist contents to tmp playlist file", err)
|
|
|
|
return
|
2021-11-27 07:53:27 +03:00
|
|
|
}
|
|
|
|
|
2022-01-28 01:34:25 +03:00
|
|
|
// Manually append the offline clip to the end of the media playlist.
|
2021-11-27 07:53:27 +03:00
|
|
|
_, _ = atomicWriteTmpPlaylistFile.WriteString("#EXT-X-DISCONTINUITY\n")
|
|
|
|
// If "offline" content gets changed then change the duration below
|
|
|
|
_, _ = atomicWriteTmpPlaylistFile.WriteString("#EXTINF:8.000000,\n")
|
|
|
|
_, _ = atomicWriteTmpPlaylistFile.WriteString("offline.ts\n")
|
|
|
|
_, _ = atomicWriteTmpPlaylistFile.WriteString("#EXT-X-ENDLIST\n")
|
|
|
|
|
|
|
|
if err := atomicWriteTmpPlaylistFile.Close(); err != nil {
|
|
|
|
log.Errorln(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := utils.Move(atomicWriteTmpPlaylistFile.Name(), playlistFilePath); err != nil {
|
|
|
|
log.Errorln("error moving temp playlist to overwrite existing one", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-21 07:13:59 +03:00
|
|
|
func makeVariantIndexOffline(streamId string, index int, offlineFilePath string, offlineFilename string) {
|
|
|
|
playlistFilePath := fmt.Sprintf(filepath.Join(config.HLSStoragePath, streamId, "%d/stream.m3u8"), index)
|
2023-09-08 03:48:47 +03:00
|
|
|
// segmentFilePath := fmt.Sprintf(filepath.Join(config.HLSStoragePath, streamId, "%d/%s"), index, offlineFilename)
|
2023-08-21 07:13:59 +03:00
|
|
|
segmentFileDestinationPath := fmt.Sprintf(filepath.Join("hls", streamId, "%d/%s"), index, offlineFilename)
|
2021-11-27 07:53:27 +03:00
|
|
|
|
2023-09-08 03:48:47 +03:00
|
|
|
if err := utils.Copy(offlineFilePath, offlineFilePath); err != nil {
|
2021-11-27 07:53:27 +03:00
|
|
|
log.Warnln(err)
|
|
|
|
}
|
|
|
|
|
2023-09-08 03:48:47 +03:00
|
|
|
if _, err := _storage.Save(offlineFilePath, segmentFileDestinationPath, 0); err != nil {
|
2021-11-27 07:53:27 +03:00
|
|
|
log.Warnln(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if utils.DoesFileExists(playlistFilePath) {
|
|
|
|
appendOfflineToVariantPlaylist(index, playlistFilePath)
|
|
|
|
} else {
|
|
|
|
createEmptyOfflinePlaylist(playlistFilePath, offlineFilename)
|
|
|
|
}
|
2023-08-10 02:19:09 +03:00
|
|
|
if _, err := _storage.Save(playlistFilePath, playlistFilePath, 0); err != nil {
|
2021-11-27 07:53:27 +03:00
|
|
|
log.Warnln(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func createEmptyOfflinePlaylist(playlistFilePath string, offlineFilename string) {
|
|
|
|
p, err := m3u8.NewMediaPlaylist(1, 1)
|
|
|
|
if err != nil {
|
|
|
|
log.Errorln(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// If "offline" content gets changed then change the duration below
|
|
|
|
if err := p.Append(offlineFilename, 8.0, ""); err != nil {
|
|
|
|
log.Errorln(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
p.Close()
|
2022-01-28 01:58:31 +03:00
|
|
|
f, err := os.Create(playlistFilePath) //nolint:gosec
|
2021-11-27 07:53:27 +03:00
|
|
|
if err != nil {
|
|
|
|
log.Errorln(err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
if _, err := f.Write(p.Encode().Bytes()); err != nil {
|
|
|
|
log.Errorln(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func saveOfflineClipToDisk(offlineFilename string) (string, error) {
|
|
|
|
offlineFileData := static.GetOfflineSegment()
|
2022-03-08 03:30:40 +03:00
|
|
|
offlineTmpFile, err := os.CreateTemp(config.TempDir, offlineFilename)
|
2021-11-27 07:53:27 +03:00
|
|
|
if err != nil {
|
|
|
|
log.Errorln("unable to create temp file for offline video segment", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err = offlineTmpFile.Write(offlineFileData); err != nil {
|
|
|
|
return "", fmt.Errorf("unable to write offline segment to disk: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
offlineFilePath := offlineTmpFile.Name()
|
|
|
|
|
|
|
|
return offlineFilePath, nil
|
|
|
|
}
|