75 lines
1.9 KiB
Go
75 lines
1.9 KiB
Go
package feedparser
|
|
|
|
import (
|
|
"encoding/xml"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
|
|
"gitea.theedgeofrage.com/TheEdgeOfRage/ytrssil-api/lib/log"
|
|
"github.com/paulrosania/go-charset/charset"
|
|
)
|
|
|
|
var (
|
|
ErrInvalidChannelID = errors.New("invalid channel ID")
|
|
ErrParseFailed = errors.New("failed to parse feed")
|
|
)
|
|
|
|
var urlFormat = "https://www.youtube.com/feeds/videos.xml?channel_id=%s"
|
|
|
|
// Video struct for each video in the feed
|
|
type Video struct {
|
|
ID string `xml:"id"`
|
|
Title string `xml:"title"`
|
|
Published Date `xml:"published"`
|
|
}
|
|
|
|
// Channel struct for RSS
|
|
type Channel struct {
|
|
Name string `xml:"title"`
|
|
Videos []Video `xml:"entry"`
|
|
}
|
|
|
|
func read(l log.Logger, url string) (io.ReadCloser, error) {
|
|
req, err := http.NewRequest("GET", url, nil)
|
|
if err != nil {
|
|
l.Log("level", "ERROR", "function", "feedparser.read", "call", "http.NewRequest", "error", err)
|
|
return nil, err
|
|
}
|
|
|
|
response, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
l.Log("level", "ERROR", "function", "feedparser.read", "call", "http.Do", "error", err)
|
|
return nil, err
|
|
}
|
|
|
|
if response.StatusCode == http.StatusNotFound {
|
|
return nil, ErrInvalidChannelID
|
|
} else if response.StatusCode != http.StatusOK {
|
|
return nil, fmt.Errorf("failed to get feed with status %d", response.StatusCode)
|
|
}
|
|
|
|
return response.Body, nil
|
|
}
|
|
|
|
// Parse parses a YouTube channel XML feed from a channel ID
|
|
func Parse(l log.Logger, channelID string) (*Channel, error) {
|
|
url := fmt.Sprintf(urlFormat, channelID)
|
|
reader, err := read(l, url)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer reader.Close()
|
|
xmlDecoder := xml.NewDecoder(reader)
|
|
xmlDecoder.CharsetReader = charset.NewReader
|
|
|
|
var channel Channel
|
|
if err := xmlDecoder.Decode(&channel); err != nil {
|
|
l.Log("level", "ERROR", "function", "feedparser.read", "call", "xml.Decode", "error", err)
|
|
return nil, fmt.Errorf("%w: %s", ErrParseFailed, err.Error())
|
|
}
|
|
return &channel, nil
|
|
}
|