1
0
mirror of https://github.com/mxpv/podsync.git synced 2024-05-11 05:55:04 +00:00
mxpv-podsync/cmd/podsync/main.go

207 lines
4.6 KiB
Go
Raw Normal View History

2019-10-26 22:43:30 -07:00
package main
import (
"context"
"fmt"
"net/http"
2019-10-26 22:43:30 -07:00
"os"
"os/signal"
"syscall"
2019-10-29 15:07:59 -07:00
"time"
2019-10-26 22:43:30 -07:00
"github.com/jessevdk/go-flags"
"github.com/robfig/cron/v3"
2019-10-26 22:43:30 -07:00
log "github.com/sirupsen/logrus"
2019-10-29 13:30:51 -07:00
"golang.org/x/sync/errgroup"
2019-10-26 22:43:30 -07:00
"github.com/mxpv/podsync/pkg/config"
2020-02-02 16:32:11 -08:00
"github.com/mxpv/podsync/pkg/db"
"github.com/mxpv/podsync/pkg/fs"
2019-11-13 18:16:35 -08:00
"github.com/mxpv/podsync/pkg/ytdl"
2019-10-26 22:43:30 -07:00
)
type Opts struct {
ConfigPath string `long:"config" short:"c" default:"config.toml" env:"PODSYNC_CONFIG_PATH"`
Debug bool `long:"debug"`
2019-11-12 17:27:02 -08:00
NoBanner bool `long:"no-banner"`
2019-10-26 22:43:30 -07:00
}
2019-11-12 17:27:02 -08:00
const banner = `
_______ _______ ______ _______ _ _______
( ____ )( ___ )( __ \ ( ____ \|\ /|( ( /|( ____ \
| ( )|| ( ) || ( \ )| ( \/( \ / )| \ ( || ( \/
| (____)|| | | || | ) || (_____ \ (_) / | \ | || |
| _____)| | | || | | |(_____ ) \ / | (\ \) || |
| ( | | | || | ) | ) | ) ( | | \ || |
| ) | (___) || (__/ )/\____) | | | | ) \ || (____/\
|/ (_______)(______/ \_______) \_/ |/ )_)(_______/
`
2019-11-13 20:43:47 -08:00
var (
version = "dev"
commit = "none"
date = "unknown"
)
2019-10-26 22:43:30 -07:00
func main() {
2019-11-12 17:27:02 -08:00
log.SetFormatter(&log.TextFormatter{
TimestampFormat: time.RFC3339,
FullTimestamp: true,
})
2019-10-26 22:43:30 -07:00
stop := make(chan os.Signal, 1)
signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
2019-10-29 13:30:51 -07:00
group, ctx := errgroup.WithContext(ctx)
2019-10-26 22:43:30 -07:00
// Parse args
opts := Opts{}
_, err := flags.Parse(&opts)
if err != nil {
log.WithError(err).Fatal("failed to parse command line arguments")
}
if opts.Debug {
log.SetLevel(log.DebugLevel)
}
2019-11-12 17:27:02 -08:00
if !opts.NoBanner {
log.Info(banner)
}
2019-11-13 20:43:47 -08:00
log.WithFields(log.Fields{
"version": version,
"commit": commit,
"date": date,
}).Info("running podsync")
downloader, err := ytdl.New(ctx)
if err != nil {
log.WithError(err).Fatal("youtube-dl error")
}
2019-10-26 22:43:30 -07:00
// Load TOML file
log.Debugf("loading configuration %q", opts.ConfigPath)
cfg, err := config.LoadConfig(opts.ConfigPath)
2019-10-26 22:43:30 -07:00
if err != nil {
log.WithError(err).Fatal("failed to load configuration file")
}
2020-02-02 16:32:11 -08:00
database, err := db.NewBadger(&cfg.Database)
2019-12-01 15:19:08 -08:00
if err != nil {
log.WithError(err).Fatal("failed to open database")
}
storage, err := fs.NewLocal(cfg.Server.DataDir, cfg.Server.Hostname)
if err != nil {
log.WithError(err).Fatal("failed to open storage")
}
2019-10-29 15:07:59 -07:00
// Run updater thread
log.Debug("creating updater")
updater, err := NewUpdater(cfg, downloader, database, storage)
2019-10-29 15:07:59 -07:00
if err != nil {
log.WithError(err).Fatal("failed to create updater")
}
2020-03-07 17:04:05 -08:00
// Queue of feeds to update
updates := make(chan *config.Feed, 16)
defer close(updates)
//Create Cron
c := cron.New(cron.WithChain(cron.SkipIfStillRunning(nil)))
m := make(map[string]cron.EntryID)
2020-03-07 17:04:05 -08:00
// Run updates listener
2019-10-29 15:07:59 -07:00
group.Go(func() error {
2020-03-07 17:04:05 -08:00
for {
select {
case feed := <-updates:
if err := updater.Update(ctx, feed); err != nil {
log.WithError(err).Errorf("failed to update feed: %s", feed.URL)
} else {
2020-04-16 15:14:46 -07:00
log.Infof("next update of %s: %s", feed.ID, c.Entry(m[feed.ID]).Next)
2020-03-07 17:04:05 -08:00
}
case <-ctx.Done():
return ctx.Err()
}
}
})
// Run cron scheduler
group.Go(func() error {
var cronid cron.EntryID
for _, feed := range cfg.Feeds {
if feed.CronSchedule == "" {
feed.CronSchedule = fmt.Sprintf("@every %s", feed.UpdatePeriod.String())
}
_feed := feed
if cronid, err = c.AddFunc(_feed.CronSchedule, func() {
log.Debugf("adding %q to update queue", _feed.ID)
2020-04-09 17:10:32 +02:00
updates <- _feed
2020-03-07 17:04:05 -08:00
}); err != nil {
2020-04-09 17:10:32 +02:00
log.WithError(err).Fatalf("can't create cron task for feed: %s", _feed.ID)
2019-10-29 15:07:59 -07:00
}
m[_feed.ID] = cronid
log.Debugf("-> %s (update '%s')", _feed.ID, _feed.CronSchedule)
// Perform initial update after CLI restart
2020-04-09 17:10:32 +02:00
updates <- _feed
}
c.Start()
for {
<-ctx.Done()
2020-03-07 17:04:05 -08:00
log.Info("shutting down cron")
c.Stop()
return ctx.Err()
}
})
2019-10-29 15:07:59 -07:00
// Run web server
2019-10-29 14:38:29 -07:00
srv := NewServer(cfg)
2019-10-26 22:43:30 -07:00
2019-10-29 13:30:51 -07:00
group.Go(func() error {
2019-10-26 22:43:30 -07:00
log.Infof("running listener at %s", srv.Addr)
2019-10-29 13:30:51 -07:00
return srv.ListenAndServe()
})
group.Go(func() error {
// Shutdown web server
defer func() {
log.Info("shutting down web server")
if err := srv.Shutdown(ctx); err != nil {
log.WithError(err).Error("server shutdown failed")
}
}()
for {
select {
case <-ctx.Done():
return ctx.Err()
case <-stop:
cancel()
return nil
}
2019-10-26 22:43:30 -07:00
}
2019-10-29 13:30:51 -07:00
})
2019-10-26 22:43:30 -07:00
if err := group.Wait(); err != nil && (err != context.Canceled && err != http.ErrServerClosed) {
2019-10-29 13:30:51 -07:00
log.WithError(err).Error("wait error")
2019-10-26 22:43:30 -07:00
}
2020-02-02 16:32:11 -08:00
if err := database.Close(); err != nil {
2019-12-01 15:19:08 -08:00
log.WithError(err).Error("failed to close database")
}
2019-10-26 22:43:30 -07:00
log.Info("gracefully stopped")
}