2024-04-01 11:02:07 +00:00
|
|
|
// Package youtube contains an uploader to push VoDs to a Youtube
|
|
|
|
// channel
|
2024-03-27 14:20:45 +00:00
|
|
|
package youtube
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"path"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
"google.golang.org/api/option"
|
|
|
|
"google.golang.org/api/youtube/v3"
|
|
|
|
|
|
|
|
"git.luzifer.io/luzifer/publish-vod/pkg/uploader"
|
|
|
|
vaultoauth2 "git.luzifer.io/luzifer/publish-vod/pkg/vault-oauth2"
|
|
|
|
oauth4youtube "git.luzifer.io/luzifer/publish-vod/pkg/vault-oauth2/youtube"
|
|
|
|
"github.com/Luzifer/go_helpers/v2/fieldcollection"
|
|
|
|
)
|
|
|
|
|
|
|
|
type (
|
2024-04-01 11:02:07 +00:00
|
|
|
impl struct{}
|
2024-03-27 14:20:45 +00:00
|
|
|
)
|
|
|
|
|
2024-04-01 11:02:07 +00:00
|
|
|
var ptrStringEmpty = func(v string) *string { return &v }("")
|
2024-03-27 14:20:45 +00:00
|
|
|
|
2024-04-01 11:02:07 +00:00
|
|
|
// New creates a new Youtube Uploader
|
|
|
|
func New() uploader.Uploader { return impl{} }
|
2024-03-27 14:20:45 +00:00
|
|
|
|
2024-04-01 11:02:07 +00:00
|
|
|
func (impl) Prepare(ctx context.Context, opts uploader.Opts) error {
|
2024-03-27 17:26:13 +00:00
|
|
|
_, ts, err := oauth4youtube.GetOAuth2Client(ctx, opts.Config.MustString("vaultKey", nil))
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("getting oauth credentials: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
t, err := ts.Token()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("getting token to store: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := vaultoauth2.SaveTokenToVault(opts.Config.MustString("vaultKey", nil), t); err != nil {
|
|
|
|
return fmt.Errorf("storing token to vault: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-04-01 11:02:07 +00:00
|
|
|
func (impl) UploadFile(ctx context.Context, opts uploader.Opts) error {
|
2024-03-27 14:20:45 +00:00
|
|
|
client, ts, err := oauth4youtube.GetOAuth2Client(ctx, opts.Config.MustString("vaultKey", nil))
|
|
|
|
if err != nil {
|
2024-03-27 17:26:13 +00:00
|
|
|
return fmt.Errorf("getting oauth credentials: %w", err)
|
2024-03-27 14:20:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
t, err := ts.Token()
|
|
|
|
if err != nil {
|
2024-03-27 17:26:13 +00:00
|
|
|
logrus.WithError(err).Error("getting token to store back")
|
2024-03-27 14:20:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := vaultoauth2.SaveTokenToVault(opts.Config.MustString("vaultKey", nil), t); err != nil {
|
2024-03-27 17:26:13 +00:00
|
|
|
logrus.WithError(err).Error("storing token back to vault")
|
2024-03-27 14:20:45 +00:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
service, err := youtube.NewService(ctx, option.WithHTTPClient(client))
|
|
|
|
if err != nil {
|
2024-03-27 17:26:13 +00:00
|
|
|
return fmt.Errorf("creating Youtube client: %w", err)
|
2024-03-27 14:20:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
upload := &youtube.Video{
|
|
|
|
Snippet: &youtube.VideoSnippet{
|
|
|
|
CategoryId: "20", // Gaming
|
|
|
|
ChannelId: opts.Config.MustString("channel", nil),
|
|
|
|
DefaultAudioLanguage: "de",
|
|
|
|
DefaultLanguage: "de",
|
|
|
|
Description: opts.Config.MustString("description", ptrStringEmpty),
|
|
|
|
LiveBroadcastContent: "none",
|
|
|
|
Title: strings.ReplaceAll(path.Base(opts.Filename), path.Ext(opts.Filename), ""),
|
|
|
|
},
|
|
|
|
Status: &youtube.VideoStatus{
|
|
|
|
Embeddable: true,
|
|
|
|
License: "youtube",
|
|
|
|
MadeForKids: false,
|
|
|
|
PrivacyStatus: "private",
|
|
|
|
PublicStatsViewable: true,
|
|
|
|
},
|
|
|
|
RecordingDetails: &youtube.VideoRecordingDetails{
|
|
|
|
RecordingDate: time.Now().Format(time.RFC3339),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
progressReader := opts.ProgressBar.NewProxyReader(opts.Content)
|
|
|
|
|
|
|
|
resp, err := service.Videos.
|
|
|
|
Insert([]string{"snippet", "status", "recordingDetails"}, upload).
|
|
|
|
Media(progressReader).
|
|
|
|
Do()
|
|
|
|
if err != nil {
|
2024-03-27 17:26:13 +00:00
|
|
|
return fmt.Errorf("inserting video: %w", err)
|
2024-03-27 14:20:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
opts.FinalMessage("Video uploaded: https://youtu.be/%s", resp.Id)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-04-01 11:02:07 +00:00
|
|
|
func (impl) ValidateConfig(config *fieldcollection.FieldCollection) error {
|
2024-03-27 14:20:45 +00:00
|
|
|
for _, key := range []string{"channel", "vaultKey"} {
|
|
|
|
if v, err := config.String(key); err != nil || v == "" {
|
|
|
|
return fmt.Errorf("key %q must be non-empty string", key)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|