Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

main.go 3.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. package main
  2. import (
  3. "bufio"
  4. "encoding/json"
  5. "flag"
  6. "fmt"
  7. "log"
  8. "net/http"
  9. "net/url"
  10. "os"
  11. "path"
  12. "strings"
  13. "github.com/grafov/m3u8"
  14. )
  15. const baseScheme = "http"
  16. const MASTER = "master"
  17. const PLAYLIST = "playlist"
  18. type m3u8Json struct {
  19. URL string
  20. BaseUrl string
  21. Type string
  22. Manifest interface{}
  23. }
  24. // by default m3u8 package creates a 1024 length slice. We need to update the
  25. // len of the slice so we don't get null when doing json marshal
  26. func nilIndex(input []*m3u8.MediaSegment) int {
  27. output := -1
  28. for i := range input {
  29. if input[i] == nil {
  30. return i
  31. }
  32. }
  33. return output
  34. }
  35. func main() {
  36. isUrl := false
  37. var data *bufio.Reader
  38. var outputJson m3u8Json
  39. recursive := flag.Bool("r", false, "Recursive download master and playlists")
  40. abs := flag.Bool("a", false, "make URI absolute")
  41. baseUrl := flag.String("b", "", "Base URL")
  42. flag.Parse()
  43. inputs := flag.Args()
  44. if len(inputs) != 1 {
  45. log.Fatal("Only 1 input is allowed")
  46. }
  47. input := inputs[0]
  48. u, err := url.Parse(input)
  49. if err != nil {
  50. log.Fatal("not a valid URL")
  51. }
  52. if strings.Contains(u.Scheme, baseScheme) {
  53. isUrl = true
  54. }
  55. if !isUrl {
  56. var f *os.File
  57. var err error
  58. if input == "-" {
  59. data = bufio.NewReader(os.Stdin)
  60. } else {
  61. f, err = os.Open(input)
  62. if err != nil {
  63. log.Fatal(err)
  64. }
  65. data = bufio.NewReader(f)
  66. }
  67. } else {
  68. if *baseUrl == "" {
  69. *baseUrl = fmt.Sprintf("%v://%v%v", u.Scheme, u.Host, path.Dir(u.EscapedPath()))
  70. }
  71. r, err := http.Get(u.String())
  72. if err != nil {
  73. log.Fatal(err)
  74. }
  75. outputJson.URL = u.String()
  76. outputJson.BaseUrl = *baseUrl
  77. data = bufio.NewReader(r.Body)
  78. }
  79. p, listType, err := m3u8.DecodeFrom(data, true)
  80. if err != nil {
  81. log.Fatal(err)
  82. }
  83. switch listType {
  84. case m3u8.MEDIA:
  85. outputJson.Type = PLAYLIST
  86. playlist := p.(*m3u8.MediaPlaylist)
  87. playlist.Segments = playlist.Segments[:nilIndex(playlist.Segments)]
  88. for _, entry := range playlist.Segments {
  89. u, err := url.Parse(entry.URI)
  90. if err != nil {
  91. log.Fatal("not a valid URL")
  92. }
  93. if *abs && !u.IsAbs() && *baseUrl != "" {
  94. entry.URI = *baseUrl + "/" + entry.URI
  95. }
  96. }
  97. outputJson.Manifest = playlist
  98. case m3u8.MASTER:
  99. outputJson.Type = MASTER
  100. master := p.(*m3u8.MasterPlaylist)
  101. for _, entry := range master.Variants {
  102. var chunckUrl string
  103. u, err := url.Parse(entry.URI)
  104. if err != nil {
  105. log.Fatal("not a valid URL")
  106. }
  107. if !u.IsAbs() {
  108. chunckUrl = *baseUrl + "/" + entry.URI
  109. if *abs && *baseUrl != "" {
  110. entry.URI = chunckUrl
  111. }
  112. } else {
  113. chunckUrl = entry.URI
  114. }
  115. if isUrl && *recursive {
  116. clist, err := http.Get(chunckUrl)
  117. if err != nil {
  118. log.Fatal(err)
  119. }
  120. cdata := bufio.NewReader(clist.Body)
  121. p, _, _ := m3u8.DecodeFrom(cdata, true)
  122. playlist := p.(*m3u8.MediaPlaylist)
  123. playlist.Segments = playlist.Segments[:nilIndex(playlist.Segments)]
  124. for _, entry := range playlist.Segments {
  125. u, err := url.Parse(entry.URI)
  126. if err != nil {
  127. log.Fatal("not a valid URL")
  128. }
  129. if *abs && !u.IsAbs() {
  130. entry.URI = *baseUrl + "/" + entry.URI
  131. }
  132. }
  133. entry.Chunklist = playlist
  134. }
  135. }
  136. outputJson.Manifest = master
  137. }
  138. output, err := json.MarshalIndent(outputJson, "", " ")
  139. if err != nil {
  140. log.Fatal(err)
  141. }
  142. fmt.Printf("%s", output)
  143. }