157 lines
4.2 KiB
Go
157 lines
4.2 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/xml"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"regexp"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type DownloadRequest struct {
|
|
Link string `json:"link"`
|
|
}
|
|
type RssWatcher struct {
|
|
Feed *RssFeed
|
|
}
|
|
|
|
var videoRegex = regexp.MustCompile(`yt:video:(?<videoid>[^ ]+) (?:[^ ]+ ){2}(?<videotitle>.+?)https(?:[^ ]+ ){2}(?<date>[^ ]+)`)
|
|
var feeds []*RssFeed = []*RssFeed{
|
|
{Url: "https://www.youtube.com/feeds/videos.xml?channel_id=UCMwJJL5FJFuTRT55ksbQ4GQ", Id: "@AsmongoldClips"},
|
|
{Url: "https://www.youtube.com/feeds/videos.xml?channel_id=UC8nZUXCwCTffxthKLtOp6ng", Id: "@Splattercatgaming"},
|
|
{Url: "https://www.youtube.com/feeds/videos.xml?channel_id=UC2THf0jmDDeBujMzG1sD2-Q", Id: "@thesingleplayersquad"},
|
|
{Url: "https://www.youtube.com/feeds/videos.xml?channel_id=UCmtyQOKKmrMVaKuRXz02jbQ", Id: "@SebastianLague"},
|
|
{Url: "https://www.youtube.com/feeds/videos.xml?channel_id=UCywBfpGBYhsczNuyyh6Cf6w", Id: "@WorthABuyreviews"},
|
|
}
|
|
|
|
func (w *RssWatcher) Watch(videoUrls chan string) error {
|
|
ticker := time.NewTicker(1 * time.Minute)
|
|
defer ticker.Stop()
|
|
|
|
log.Printf("[%s]: Watcher started, checking every minute.", w.Feed.Id)
|
|
|
|
w.CheckFeed(videoUrls)
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
log.Printf("[%s]: Checking feed", w.Feed.Id)
|
|
err := w.CheckFeed(videoUrls)
|
|
if err != nil {
|
|
return fmt.Errorf("watcher %s failed to check feed: %w", w.Feed.Id, err)
|
|
}
|
|
log.Printf("[%s]: Successfully checked feed", w.Feed.Id)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (w *RssWatcher) CheckFeed(videoUrls chan string) error {
|
|
log.Printf("Checking feed URL: %s", w.Feed.Url)
|
|
resp, err := http.Get(w.Feed.Url)
|
|
if err != nil {
|
|
return fmt.Errorf("[%s]: failed to create request: %w", w.Feed.Id, err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
log.Printf("Received response with status code: %d", resp.StatusCode)
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return fmt.Errorf("[%s]: failed to read response body: %w", w.Feed.Id, err)
|
|
}
|
|
|
|
// os.WriteFile("cache.xml", body, 0644)
|
|
// body, err := os.ReadFile("cache.xml")
|
|
// if err != nil {
|
|
// return fmt.Errorf("[%s]: failed to read cache file: %w", w.Feed.Id, err)
|
|
// }
|
|
|
|
var feed Feed
|
|
err = xml.Unmarshal(body, &feed)
|
|
if err != nil {
|
|
return fmt.Errorf("[%s]: failed to unmarshal feed: %w", w.Feed.Id, err)
|
|
}
|
|
|
|
for _, entry := range feed.Entry {
|
|
uploaded, err := time.Parse(time.RFC3339, entry.Published)
|
|
if err != nil {
|
|
return fmt.Errorf("[%s]: failed to parse published date: %w", w.Feed.Id, err)
|
|
}
|
|
if uploaded.Before(w.Feed.LastSeen) {
|
|
log.Printf("[%s]: Skipping video titled %q because it was uploaded before %s", w.Feed.Id, entry.Title, w.Feed.LastSeen.Format(time.RFC3339))
|
|
continue
|
|
}
|
|
log.Printf("[%s]: Found new video titled %q with url %q", w.Feed.Id, entry.Title, entry.Link.Href)
|
|
videoUrls <- entry.Link.Href
|
|
}
|
|
err = w.Feed.WriteLastSeen(time.Now())
|
|
if err != nil {
|
|
return fmt.Errorf("[%s]: failed to write last seen: %w", w.Feed.Id, err)
|
|
}
|
|
w.Feed.LastSeen = time.Now()
|
|
|
|
return nil
|
|
}
|
|
|
|
var Error *log.Logger
|
|
var Warning *log.Logger
|
|
|
|
func init() {
|
|
log.SetFlags(log.Lmicroseconds | log.Lshortfile)
|
|
logFile, err := os.Create("ywatcher.log")
|
|
if err != nil {
|
|
log.Printf("Error creating log file: %v", err)
|
|
os.Exit(1)
|
|
}
|
|
logger := io.MultiWriter(os.Stdout, logFile)
|
|
log.SetOutput(logger)
|
|
|
|
Error = log.New(io.MultiWriter(logFile, os.Stderr, os.Stdout),
|
|
fmt.Sprintf("%sERROR:%s ", "\033[0;101m", "\033[0m"),
|
|
log.Lmicroseconds|log.Lshortfile)
|
|
Warning = log.New(io.MultiWriter(logFile, os.Stdout),
|
|
fmt.Sprintf("%sWarning:%s ", "\033[0;93m", "\033[0m"),
|
|
log.Lmicroseconds|log.Lshortfile)
|
|
}
|
|
|
|
func main() {
|
|
videoUrls := make(chan string, 12)
|
|
|
|
wg := &sync.WaitGroup{}
|
|
for _, feed := range feeds {
|
|
wg.Add(1)
|
|
go func(feed *RssFeed) {
|
|
err := feed.UpdateLastSeen()
|
|
if err != nil {
|
|
Error.Printf("failed to update lastseen for feed %s: %v", feed.Id, err)
|
|
panic(err)
|
|
}
|
|
|
|
defer wg.Done()
|
|
watcher := RssWatcher{
|
|
Feed: feed,
|
|
}
|
|
err = watcher.Watch(videoUrls)
|
|
if err != nil {
|
|
Error.Printf("watcher %s failed to watch feed: %v", feed.Id, err)
|
|
panic(err)
|
|
}
|
|
}(feed)
|
|
}
|
|
|
|
go func() {
|
|
for videoUrl := range videoUrls {
|
|
log.Printf("Got new video with url %q", videoUrl)
|
|
err := Download(videoUrl)
|
|
if err != nil {
|
|
Error.Printf("failed to download video: %v", err)
|
|
panic(err)
|
|
}
|
|
}
|
|
}()
|
|
|
|
wg.Wait()
|
|
}
|