PodcastToM3U/main.go

106 lines
2.4 KiB
Go

package main
import (
"bytes"
"errors"
"fmt"
"math"
"regexp"
"strconv"
"strings"
"time"
"github.com/mmcdole/gofeed"
)
const durationPattern = `^([0-9]+:)?([0-9]+:)?([0-9]+(\.[0-9]+)?)$`
var durationRegexp = regexp.MustCompile(durationPattern)
func main() {
playlist, err := feed2m3u("https://feeds.soundcloud.com/users/soundcloud:users:520257816/sounds.rss")
if err != nil {
panic(err)
}
fmt.Println(playlist)
}
func feed2m3u(feedURL string) (string, error) {
parser := gofeed.NewParser()
feed, err := parser.ParseURL(feedURL)
if err != nil {
return "", err
}
defaultAuthor := "No Author"
if feed.Author != nil && feed.Author.Name != "" {
defaultAuthor = feed.Author.Name
}
defaultTitle := "No Title"
if feed.Title != "" {
defaultTitle = feed.Title
}
playlistBuffer := bytes.NewBuffer(nil)
fmt.Fprintln(playlistBuffer, "#EXTM3U")
for _, item := range feed.Items {
duration := -1
if item.ITunesExt != nil && item.ITunesExt.Duration != "" {
duration, err = normalizeDuration(item.ITunesExt.Duration)
if err != nil {
return "", err
}
}
title := defaultTitle
if item.Title != "" {
title = item.Title
}
author := defaultAuthor
if item.Author != nil && item.Author.Name != "" {
author = item.Author.Name
}
for _, enclosure := range item.Enclosures {
if strings.HasPrefix(strings.ToLower(enclosure.Type), "audio/") {
fmt.Fprintf(playlistBuffer, "\n#EXTINF:%d, %s - %s\n%s\n", duration, title, author, enclosure.URL)
break
}
}
}
return playlistBuffer.String(), nil
}
func normalizeDuration(durationString string) (int, error) {
durationString = strings.ReplaceAll(durationString, " ", "")
ok := durationRegexp.MatchString(durationString)
if ok {
durationComponents := strings.Split(durationString, ":")
var total float64 = 0
for i, value := range durationComponents {
intValue, err := strconv.ParseFloat(value, 64)
if err != nil {
return 0, err
}
total += intValue * math.Pow(60, float64(len(durationComponents)-i-1))
}
return int(total), nil
}
durationString = strings.ReplaceAll(durationString, "min", "m")
durationString = strings.ReplaceAll(durationString, "sec", "s")
if duration, err := time.ParseDuration(durationString); err == nil {
return int(duration.Seconds()), nil
}
return 0, errors.New("NormalizeDuration: duration didn't match the duration pattern")
}