Initial Commit
This commit is contained in:
commit
001c761fe8
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
9
.idea/m3u8-downloader.iml
generated
Normal file
9
.idea/m3u8-downloader.iml
generated
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="Go" enabled="true" />
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/m3u8-downloader.iml" filepath="$PROJECT_DIR$/.idea/m3u8-downloader.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
15
main.go
Normal file
15
main.go
Normal file
@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"m3u8-downloader/media"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
master_url := "https://d17cyqyz9yhmep.cloudfront.net/streams/234945/playlist_1752291107574_1752292056713.m3u8"
|
||||
|
||||
streams := media.GetStreamMetadata(master_url)
|
||||
|
||||
fmt.Println(streams)
|
||||
}
|
||||
113
media/types.go
Normal file
113
media/types.go
Normal file
@ -0,0 +1,113 @@
|
||||
package media
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type StreamSet struct {
|
||||
PlaylistURL string
|
||||
VideoURLs []VideoURL
|
||||
AudioURLs []AudioURL
|
||||
}
|
||||
|
||||
func GetStreamMetadata(master_url string) *StreamSet {
|
||||
resp, err := http.Get(master_url)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != 200 {
|
||||
panic(resp.StatusCode)
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
content := string(data)
|
||||
lines := strings.Split(content, "\n")
|
||||
video_urls := []VideoURL{}
|
||||
audio_urls := []AudioURL{}
|
||||
for i, line := range lines {
|
||||
if strings.HasPrefix(line, "#EXT-X-STREAM-INF") {
|
||||
video := NewVideoURL(lines[i], lines[i+1])
|
||||
video_urls = append(video_urls, *video)
|
||||
} else if strings.HasPrefix(line, "#EXT-X-MEDIA") {
|
||||
audio := NewAudioURL(lines[i])
|
||||
audio_urls = append(audio_urls, *audio)
|
||||
}
|
||||
}
|
||||
|
||||
return &StreamSet{
|
||||
PlaylistURL: master_url,
|
||||
VideoURLs: video_urls,
|
||||
AudioURLs: audio_urls,
|
||||
}
|
||||
}
|
||||
|
||||
type VideoURL struct {
|
||||
URL string
|
||||
Bandwidth int
|
||||
Codecs string
|
||||
Resolution string
|
||||
FrameRate string
|
||||
Audio string
|
||||
}
|
||||
|
||||
func NewVideoURL(input string, url string) *VideoURL {
|
||||
if strings.HasPrefix(input, "#EXT-X-STREAM-INF") {
|
||||
reg := regexp.MustCompile(`([A-Z0-9-]+)=(".*?"|[^,]*)`)
|
||||
split := reg.FindAllStringSubmatch(input, -1)
|
||||
bandwidth := split[0][2]
|
||||
bandwidth_int, err := strconv.Atoi(bandwidth)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
video := VideoURL{
|
||||
URL: url,
|
||||
Bandwidth: bandwidth_int,
|
||||
Codecs: split[1][2],
|
||||
Resolution: split[2][2],
|
||||
FrameRate: split[3][2],
|
||||
Audio: split[4][2],
|
||||
}
|
||||
return &video
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type AudioURL struct {
|
||||
URL string
|
||||
MediaType string
|
||||
GroupId string
|
||||
Name string
|
||||
Default string
|
||||
Autoselect string
|
||||
}
|
||||
|
||||
func NewAudioURL(input string) *AudioURL {
|
||||
if strings.HasPrefix(input, "#EXT-X-MEDIA") {
|
||||
split := strings.Split(strings.Split(input, ":")[1], ",")
|
||||
url := ParseAttribute(split[5])
|
||||
mediaType := ParseAttribute(split[0])
|
||||
groupId := ParseAttribute(split[1])
|
||||
name := ParseAttribute(split[2])
|
||||
default_ := ParseAttribute(split[3])
|
||||
autoselect := ParseAttribute(split[4])
|
||||
audio := AudioURL{
|
||||
URL: url,
|
||||
MediaType: mediaType,
|
||||
GroupId: groupId,
|
||||
Name: name,
|
||||
Default: default_,
|
||||
Autoselect: autoselect,
|
||||
}
|
||||
return &audio
|
||||
}
|
||||
return nil
|
||||
}
|
||||
24
media/utils.go
Normal file
24
media/utils.go
Normal file
@ -0,0 +1,24 @@
|
||||
package media
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ParseAttribute(attribute string) string {
|
||||
split := strings.Split(attribute, "=")
|
||||
return split[1]
|
||||
}
|
||||
|
||||
func ResolutionToPixels(resolution string) int {
|
||||
split := strings.Split(resolution, "x")
|
||||
width, err := strconv.Atoi(split[0])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
height, err := strconv.Atoi(split[1])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return width * height
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user