ytsync/local/local.go

236 lines
6 KiB
Go
Raw Normal View History

2021-09-27 16:11:07 +02:00
package local
import (
2021-10-14 20:03:57 +02:00
"errors"
"os"
"regexp"
2021-10-14 20:03:57 +02:00
"strings"
2021-11-04 21:28:28 +01:00
"time"
2021-09-27 16:11:07 +02:00
2021-10-14 20:03:57 +02:00
log "github.com/sirupsen/logrus"
2021-09-27 16:11:07 +02:00
"github.com/spf13/cobra"
"github.com/abadojack/whatlanggo"
2021-10-14 20:03:57 +02:00
2021-11-04 21:28:28 +01:00
"github.com/lbryio/lbry.go/v2/extras/util"
"github.com/lbryio/ytsync/v5/namer"
"github.com/lbryio/ytsync/v5/tags_manager"
2021-09-27 16:11:07 +02:00
)
2021-10-14 20:03:57 +02:00
type SyncContext struct {
2021-11-05 16:09:59 +01:00
DryRun bool
2021-10-14 20:03:57 +02:00
TempDir string
LbrynetAddr string
ChannelID string
PublishBid float64
2021-11-04 21:28:28 +01:00
YouTubeSourceConfig *YouTubeSourceConfig
2021-10-14 20:03:57 +02:00
}
func (c *SyncContext) Validate() error {
if c.TempDir == "" {
return errors.New("No TempDir provided")
}
if c.LbrynetAddr == "" {
return errors.New("No Lbrynet address provided")
}
if c.ChannelID == "" {
return errors.New("No channel ID provided")
}
if c.PublishBid <= 0.0 {
return errors.New("Publish bid is not greater than zero")
}
2021-10-14 20:03:57 +02:00
return nil
}
2021-11-04 21:28:28 +01:00
type YouTubeSourceConfig struct {
YouTubeAPIKey string
}
2021-10-14 20:03:57 +02:00
var syncContext SyncContext
2021-09-27 16:11:07 +02:00
func AddCommand(rootCmd *cobra.Command) {
cmd := &cobra.Command{
Use: "local",
Short: "run a personal ytsync",
Run: localCmd,
2021-10-14 20:03:57 +02:00
Args: cobra.ExactArgs(1),
2021-09-27 16:11:07 +02:00
}
2021-11-05 16:09:59 +01:00
cmd.Flags().BoolVar(&syncContext.DryRun, "dry-run", false, "Display information about the stream publishing, but do not publish the stream")
2021-10-14 20:03:57 +02:00
cmd.Flags().StringVar(&syncContext.TempDir, "temp-dir", getEnvDefault("TEMP_DIR", ""), "directory to use for temporary files")
cmd.Flags().Float64Var(&syncContext.PublishBid, "publish-bid", 0.01, "Bid amount for the stream claim")
2021-10-14 20:03:57 +02:00
cmd.Flags().StringVar(&syncContext.LbrynetAddr, "lbrynet-address", getEnvDefault("LBRYNET_ADDRESS", ""), "JSONRPC address of the local LBRYNet daemon")
cmd.Flags().StringVar(&syncContext.ChannelID, "channel-id", "", "LBRY channel ID to publish to")
2021-11-04 21:28:28 +01:00
// For now, assume source is always YouTube
syncContext.YouTubeSourceConfig = &YouTubeSourceConfig{}
cmd.Flags().StringVar(&syncContext.YouTubeSourceConfig.YouTubeAPIKey, "youtube-api-key", getEnvDefault("YOUTUBE_API_KEY", ""), "YouTube API Key")
2021-09-27 16:11:07 +02:00
rootCmd.AddCommand(cmd)
2021-10-14 20:03:57 +02:00
}
2021-09-27 16:11:07 +02:00
2021-10-14 20:03:57 +02:00
func getEnvDefault(key, defaultValue string) string {
if value, ok := os.LookupEnv(key); ok {
return value
}
return defaultValue
2021-09-27 16:11:07 +02:00
}
func localCmd(cmd *cobra.Command, args []string) {
2021-10-14 20:03:57 +02:00
err := syncContext.Validate()
if err != nil {
log.Error(err)
return
}
videoID := args[0]
2021-11-04 21:28:28 +01:00
log.Debugf("Running sync for video ID %s", videoID)
2021-10-14 20:03:57 +02:00
var publisher VideoPublisher
publisher, err = NewLocalSDKPublisher(syncContext.LbrynetAddr, syncContext.ChannelID, syncContext.PublishBid)
2021-10-14 20:03:57 +02:00
if err != nil {
log.Errorf("Error setting up publisher: %v", err)
2021-10-14 20:03:57 +02:00
return
}
var videoSource VideoSource
2021-11-04 21:28:28 +01:00
if syncContext.YouTubeSourceConfig != nil {
videoSource, err = NewYtdlVideoSource(syncContext.TempDir, syncContext.YouTubeSourceConfig)
if err != nil {
log.Errorf("Error setting up video source: %v", err)
return
}
2021-10-14 20:03:57 +02:00
}
sourceVideo, err := videoSource.GetVideo(videoID)
2021-10-14 20:03:57 +02:00
if err != nil {
log.Errorf("Error getting source video: %v", err)
return
}
processedVideo, err := processVideoForPublishing(*sourceVideo, syncContext.ChannelID)
if err != nil {
log.Errorf("Error processing source video for publishing: %v", err)
return
}
2021-11-05 16:09:59 +01:00
if syncContext.DryRun {
log.Infoln("This is a dry run. Nothing will be published.")
log.Infof("The local file %s would be published to channel ID %s as %s.", processedVideo.FullLocalPath, syncContext.ChannelID, processedVideo.ClaimName)
log.Debugf("Object to be published: %v", processedVideo)
2021-11-05 16:09:59 +01:00
} else {
done, err := publisher.Publish(*processedVideo)
if err != nil {
log.Errorf("Error publishing video: %v", err)
return
}
err = <-done
if err != nil {
log.Errorf("Error while wating for stream to reflect: %v", err)
}
2021-10-14 20:03:57 +02:00
}
log.Info("Done")
}
type SourceVideo struct {
ID string
Title *string
Description *string
SourceURL string
Languages []string
Tags []string
ReleaseTime *int64
ThumbnailURL *string
FullLocalPath string
}
type PublishableVideo struct {
ID string
ClaimName string
Title string
Description string
SourceURL string
Languages []string
Tags []string
ReleaseTime int64
ThumbnailURL string
FullLocalPath string
}
func processVideoForPublishing(source SourceVideo, channelID string) (*PublishableVideo, error) {
tags, err := tags_manager.SanitizeTags(source.Tags, channelID)
if err != nil {
log.Errorf("Error sanitizing tags: %v", err)
return nil, err
}
descriptionSample := ""
if source.Description != nil {
urlsRegex := regexp.MustCompile(`(?m) ?(f|ht)(tp)(s?)(://)(.*)[.|/](.*)`)
descriptionSample = urlsRegex.ReplaceAllString(*source.Description, "")
}
info := whatlanggo.Detect(descriptionSample)
title := ""
if source.Title != nil {
title = *source.Title
}
info2 := whatlanggo.Detect(title)
var languages []string = nil
if info.IsReliable() && info.Lang.Iso6391() != "" {
language := info.Lang.Iso6391()
languages = []string{language}
} else if info2.IsReliable() && info2.Lang.Iso6391() != "" {
language := info2.Lang.Iso6391()
languages = []string{language}
}
claimName := namer.NewNamer().GetNextName(title)
2021-11-04 21:28:28 +01:00
thumbnailURL := source.ThumbnailURL
if thumbnailURL == nil {
thumbnailURL = util.PtrToString("")
}
releaseTime := source.ReleaseTime
if releaseTime == nil {
releaseTime = util.PtrToInt64(time.Now().Unix())
}
processed := PublishableVideo {
ClaimName: claimName,
Title: title,
Description: getAbbrevDescription(source),
Languages: languages,
Tags: tags,
2021-11-04 21:28:28 +01:00
ReleaseTime: *releaseTime,
ThumbnailURL: *thumbnailURL,
FullLocalPath: source.FullLocalPath,
}
log.Debugf("Video prepared for publication: %v", processed)
return &processed, nil
}
2021-11-04 21:28:28 +01:00
func getAbbrevDescription(v SourceVideo) string {
if v.Description == nil {
return v.SourceURL
}
maxLength := 2800
description := strings.TrimSpace(*v.Description)
additionalDescription := "\n" + v.SourceURL
if len(description) > maxLength {
description = description[:maxLength]
}
return description + "\n..." + additionalDescription
}
type VideoSource interface {
GetVideo(id string) (*SourceVideo, error)
}
type VideoPublisher interface {
Publish(video PublishableVideo) (chan error, error)
}