new feature: update metadata on uploaded videos
This commit is contained in:
parent
3053c19dd0
commit
c961aa3b6c
4 changed files with 196 additions and 128 deletions
|
|
@ -3,6 +3,7 @@ package youtube
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
|
@ -13,7 +14,6 @@ import (
|
|||
|
||||
"arimelody.space/vodular/scanner"
|
||||
"golang.org/x/oauth2"
|
||||
"google.golang.org/api/option"
|
||||
"google.golang.org/api/youtube/v3"
|
||||
)
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ type (
|
|||
Url string
|
||||
}
|
||||
|
||||
Video struct {
|
||||
VideoMetadata struct {
|
||||
Title string
|
||||
Category *Category
|
||||
Part int
|
||||
|
|
@ -45,13 +45,15 @@ type (
|
|||
SizeBytes int64
|
||||
}
|
||||
|
||||
Thumbnail struct {
|
||||
ThumbnailMetadata struct {
|
||||
Filepath string
|
||||
SizeBytes int64
|
||||
}
|
||||
)
|
||||
|
||||
func BuildVideo(metadata *scanner.Metadata) (*Video, error) {
|
||||
const VIDEO_URL_BASE string = "https://www.youtube.com/watch?v="
|
||||
|
||||
func BuildVideo(metadata *scanner.Metadata) (*VideoMetadata, error) {
|
||||
var category *Category = nil
|
||||
if metadata.Category != nil {
|
||||
category = &Category{
|
||||
|
|
@ -64,7 +66,7 @@ func BuildVideo(metadata *scanner.Metadata) (*Video, error) {
|
|||
if !ok { category.Type = CATEGORY_ENTERTAINMENT }
|
||||
}
|
||||
|
||||
return &Video{
|
||||
return &VideoMetadata{
|
||||
Title: metadata.Title,
|
||||
Category: category,
|
||||
Part: metadata.Part,
|
||||
|
|
@ -155,7 +157,7 @@ func FetchTemplates(templateDir string) (*Template, error) {
|
|||
if titleFile, err := os.ReadFile(titlePath); err == nil {
|
||||
tmpl.Title, err = titleTemplate.Parse(string(titleFile))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse title template: %v", err)
|
||||
return nil, fmt.Errorf("parse title template: %v", err)
|
||||
}
|
||||
} else {
|
||||
if !os.IsNotExist(err) { return nil, err }
|
||||
|
|
@ -176,7 +178,7 @@ func FetchTemplates(templateDir string) (*Template, error) {
|
|||
if descriptionFile, err := os.ReadFile(descriptionPath); err == nil {
|
||||
tmpl.Description, err = descriptionTemplate.Parse(string(descriptionFile))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse description template: %v", err)
|
||||
return nil, fmt.Errorf("parse description template: %v", err)
|
||||
}
|
||||
} else {
|
||||
if !os.IsNotExist(err) { return nil, err }
|
||||
|
|
@ -195,7 +197,7 @@ func FetchTemplates(templateDir string) (*Template, error) {
|
|||
return &tmpl, nil
|
||||
}
|
||||
|
||||
func BuildTemplate(video *Video, tmpl *template.Template) (string, error) {
|
||||
func BuildTemplate(video *VideoMetadata, tmpl *template.Template) (string, error) {
|
||||
out := &bytes.Buffer{}
|
||||
|
||||
var category *MetaCategory
|
||||
|
|
@ -220,26 +222,18 @@ func BuildTemplate(video *Video, tmpl *template.Template) (string, error) {
|
|||
func UploadVideo(
|
||||
ctx context.Context,
|
||||
tokenSource oauth2.TokenSource,
|
||||
video *Video,
|
||||
thumbnail *Thumbnail,
|
||||
service *youtube.Service,
|
||||
video *VideoMetadata,
|
||||
templates *Template,
|
||||
) (*youtube.Video, error) {
|
||||
if templates == nil { return nil, fmt.Errorf("templates cannot be nil") }
|
||||
if templates.Title == nil { return nil, fmt.Errorf("title template cannot be nil") }
|
||||
if templates.Description == nil { return nil, fmt.Errorf("description template cannot be nil") }
|
||||
|
||||
title, err := BuildTemplate(video, templates.Title)
|
||||
if err != nil { return nil, fmt.Errorf("failed to build title: %v", err) }
|
||||
if err != nil { return nil, fmt.Errorf("build title: %v", err) }
|
||||
description, err := BuildTemplate(video, templates.Description)
|
||||
if err != nil { return nil, fmt.Errorf("failed to build description: %v", err) }
|
||||
|
||||
service, err := youtube.NewService(
|
||||
ctx,
|
||||
option.WithScopes(youtube.YoutubeUploadScope),
|
||||
option.WithTokenSource(tokenSource),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create youtube service: %v\n", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// upload video
|
||||
if err != nil { return nil, fmt.Errorf("build description: %v", err) }
|
||||
|
||||
videoService := youtube.NewVideosService(service)
|
||||
|
||||
|
|
@ -263,6 +257,7 @@ func UploadVideo(
|
|||
PrivacyStatus: "private",
|
||||
},
|
||||
}).NotifySubscribers(false)
|
||||
videoInsertCall.Context(ctx)
|
||||
|
||||
videoFile, err := os.Open(video.Filepath)
|
||||
if err != nil {
|
||||
|
|
@ -285,30 +280,81 @@ func UploadVideo(
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if thumbnail != nil {
|
||||
// upload thumbnail
|
||||
|
||||
thumbnailService := youtube.NewThumbnailsService(service)
|
||||
thumbnailSetCall := thumbnailService.Set(ytVideo.Id)
|
||||
thumbnailFile, err := os.Open(thumbnail.Filepath)
|
||||
if err != nil {
|
||||
log.Printf("Failed to open thumbnail: %v\n", err)
|
||||
return ytVideo, err
|
||||
}
|
||||
thumbnailSetCall.Media(thumbnailFile)
|
||||
|
||||
log.Println("Uploading thumbnail...")
|
||||
|
||||
thumbnailSetCall.ProgressUpdater(func(current, total int64) {
|
||||
if total == 0 { total = thumbnail.SizeBytes }
|
||||
fmt.Printf("\t(%.2f%%)\n", float64(current) / float64(total) * 100)
|
||||
})
|
||||
// kinda don't care about the response here- so long as it works!
|
||||
_, err = thumbnailSetCall.Do()
|
||||
if err != nil {
|
||||
log.Printf("Failed to upload thumbnail: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
return ytVideo, err
|
||||
}
|
||||
|
||||
func UpdateMetadata(
|
||||
ctx context.Context,
|
||||
tokenSource oauth2.TokenSource,
|
||||
service *youtube.Service,
|
||||
metadata *VideoMetadata,
|
||||
videoId string,
|
||||
templates *Template,
|
||||
) error {
|
||||
if templates == nil { return fmt.Errorf("templates cannot be nil") }
|
||||
if templates.Title == nil { return fmt.Errorf("title template cannot be nil") }
|
||||
if templates.Description == nil { return fmt.Errorf("description template cannot be nil") }
|
||||
|
||||
metadataService := youtube.NewVideosService(service)
|
||||
videoFetchCall := metadataService.List([]string{"id", "snippet"})
|
||||
videoFetchCall.Context(ctx)
|
||||
videoFetchCall.Id(videoId)
|
||||
videoList, err := videoFetchCall.Do()
|
||||
if err != nil { return fmt.Errorf("fetch video: %v", err) }
|
||||
if len(videoList.Items) == 0 { return errors.New("video not found") }
|
||||
|
||||
var video *youtube.Video
|
||||
for _, v := range videoList.Items {
|
||||
if v.Id == videoId {
|
||||
video = v
|
||||
break
|
||||
}
|
||||
}
|
||||
if video == nil { return errors.New("Could not find video. Was it deleted?") }
|
||||
if video.Snippet == nil { return errors.New("video snippet is nil") }
|
||||
|
||||
video.Snippet.Title, err = BuildTemplate(metadata, templates.Title)
|
||||
if err != nil { return fmt.Errorf("build title: %v", err) }
|
||||
video.Snippet.Description, err = BuildTemplate(metadata, templates.Description)
|
||||
if err != nil { return fmt.Errorf("build description: %v", err) }
|
||||
video.Snippet.Tags = metadata.Tags
|
||||
video.Snippet.CategoryId = videoYtCategory[metadata.Category.Type]
|
||||
|
||||
metadataSetCall := metadataService.Update([]string{"snippet"}, video)
|
||||
metadataSetCall.Context(ctx)
|
||||
if video, err = metadataSetCall.Do(); err != nil {
|
||||
return fmt.Errorf("set metadata: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func UploadThumbnail(
|
||||
ctx context.Context,
|
||||
tokenSource oauth2.TokenSource,
|
||||
service *youtube.Service,
|
||||
videoId string,
|
||||
thumbnail *ThumbnailMetadata,
|
||||
) error {
|
||||
if thumbnail == nil {
|
||||
return fmt.Errorf("thumbnail cannot be nil")
|
||||
}
|
||||
|
||||
thumbnailService := youtube.NewThumbnailsService(service)
|
||||
thumbnailSetCall := thumbnailService.Set(videoId)
|
||||
thumbnailSetCall.Context(ctx)
|
||||
thumbnailFile, err := os.Open(thumbnail.Filepath)
|
||||
if err != nil { return fmt.Errorf("open thumbnail: %v", err) }
|
||||
thumbnailSetCall.Media(thumbnailFile)
|
||||
|
||||
thumbnailSetCall.ProgressUpdater(func(current, total int64) {
|
||||
if total == 0 { total = thumbnail.SizeBytes }
|
||||
fmt.Printf("\t(%.2f%%)\n", float64(current) / float64(total) * 100)
|
||||
})
|
||||
_, err = thumbnailSetCall.Do()
|
||||
if err != nil {
|
||||
log.Printf("Failed to upload thumbnail: %v\n", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue