offlineState.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. package core
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "github.com/grafov/m3u8"
  7. "github.com/owncast/owncast/services/config"
  8. "github.com/owncast/owncast/static"
  9. "github.com/owncast/owncast/utils"
  10. log "github.com/sirupsen/logrus"
  11. )
  12. func appendOfflineToVariantPlaylist(index int, playlistFilePath string) {
  13. existingPlaylistContents, err := os.ReadFile(playlistFilePath) // nolint: gosec
  14. if err != nil {
  15. log.Debugln("unable to read existing playlist file", err)
  16. return
  17. }
  18. c := config.Get()
  19. tmpFileName := fmt.Sprintf("tmp-stream-%d.m3u8", index)
  20. atomicWriteTmpPlaylistFile, err := os.CreateTemp(c.TempDir, tmpFileName)
  21. if err != nil {
  22. log.Errorln("error creating tmp playlist file to write to", playlistFilePath, err)
  23. return
  24. }
  25. // Write the existing playlist contents
  26. if _, err := atomicWriteTmpPlaylistFile.Write(existingPlaylistContents); err != nil {
  27. log.Debugln("error writing existing playlist contents to tmp playlist file", err)
  28. return
  29. }
  30. // Manually append the offline clip to the end of the media playlist.
  31. _, _ = atomicWriteTmpPlaylistFile.WriteString("#EXT-X-DISCONTINUITY\n")
  32. // If "offline" content gets changed then change the duration below
  33. _, _ = atomicWriteTmpPlaylistFile.WriteString("#EXTINF:8.000000,\n")
  34. _, _ = atomicWriteTmpPlaylistFile.WriteString("offline.ts\n")
  35. _, _ = atomicWriteTmpPlaylistFile.WriteString("#EXT-X-ENDLIST\n")
  36. if err := atomicWriteTmpPlaylistFile.Close(); err != nil {
  37. log.Errorln(err)
  38. }
  39. if err := utils.Move(atomicWriteTmpPlaylistFile.Name(), playlistFilePath); err != nil {
  40. log.Errorln("error moving temp playlist to overwrite existing one", err)
  41. }
  42. }
  43. func makeVariantIndexOffline(index int, offlineFilePath string, offlineFilename string) {
  44. c := config.Get()
  45. playlistFilePath := fmt.Sprintf(filepath.Join(c.HLSStoragePath, "%d/stream.m3u8"), index)
  46. segmentFilePath := fmt.Sprintf(filepath.Join(c.HLSStoragePath, "%d/%s"), index, offlineFilename)
  47. if err := utils.Copy(offlineFilePath, segmentFilePath); err != nil {
  48. log.Warnln(err)
  49. }
  50. if _, err := _storage.Save(segmentFilePath, 0); err != nil {
  51. log.Warnln(err)
  52. }
  53. if utils.DoesFileExists(playlistFilePath) {
  54. appendOfflineToVariantPlaylist(index, playlistFilePath)
  55. } else {
  56. createEmptyOfflinePlaylist(playlistFilePath, offlineFilename)
  57. }
  58. if _, err := _storage.Save(playlistFilePath, 0); err != nil {
  59. log.Warnln(err)
  60. }
  61. }
  62. func createEmptyOfflinePlaylist(playlistFilePath string, offlineFilename string) {
  63. p, err := m3u8.NewMediaPlaylist(1, 1)
  64. if err != nil {
  65. log.Errorln(err)
  66. }
  67. // If "offline" content gets changed then change the duration below
  68. if err := p.Append(offlineFilename, 8.0, ""); err != nil {
  69. log.Errorln(err)
  70. }
  71. p.Close()
  72. f, err := os.Create(playlistFilePath) //nolint:gosec
  73. if err != nil {
  74. log.Errorln(err)
  75. }
  76. defer f.Close()
  77. if _, err := f.Write(p.Encode().Bytes()); err != nil {
  78. log.Errorln(err)
  79. }
  80. }
  81. func saveOfflineClipToDisk(offlineFilename string) (string, error) {
  82. offlineFileData := static.GetOfflineSegment()
  83. c := config.Get()
  84. offlineTmpFile, err := os.CreateTemp(c.TempDir, offlineFilename)
  85. if err != nil {
  86. log.Errorln("unable to create temp file for offline video segment", err)
  87. }
  88. if _, err = offlineTmpFile.Write(offlineFileData); err != nil {
  89. return "", fmt.Errorf("unable to write offline segment to disk: %s", err)
  90. }
  91. offlineFilePath := offlineTmpFile.Name()
  92. return offlineFilePath, nil
  93. }