Initial Commit

This commit is contained in:
kacarmichael 2025-07-17 01:14:45 -05:00
commit 001c761fe8
8 changed files with 184 additions and 0 deletions

8
.idea/.gitignore generated vendored Normal file
View 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
View 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
View 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
View 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>

1
go.mod Normal file
View File

@ -0,0 +1 @@
module m3u8-downloader

15
main.go Normal file
View 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
View 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
View 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
}