first working version!

This commit is contained in:
ari melody 2026-01-28 10:48:14 +00:00
parent 1bba7ef03d
commit 84de96df31
Signed by: ari
GPG key ID: CF99829C92678188
9 changed files with 689 additions and 80 deletions

231
youtube/youtube.go Normal file
View file

@ -0,0 +1,231 @@
package youtube
import (
"bytes"
"context"
"encoding/json"
"fmt"
"log"
"os"
"path"
"strings"
"text/template"
"time"
"arimelody.space/live-vod-uploader/scanner"
"golang.org/x/oauth2"
"google.golang.org/api/option"
"google.golang.org/api/youtube/v3"
)
var DEFAULT_TAGS = []string{
"ari melody",
"ari melody LIVE",
"livestream",
"vtuber",
"twitch",
"gaming",
"let's play",
"full VOD",
"VOD",
"stream",
"archive",
}
const (
CATEGORY_GAMING = "20"
CATEGORY_ENTERTAINMENT = "24"
)
type TitleType int
const (
TITLE_GAME TitleType = iota
TITLE_OTHER
)
type (
Title struct {
Name string
Type TitleType
Url string
}
Video struct {
Title *Title
Part int
Date time.Time
Tags []string
Filename string
}
)
func BuildVideo(metadata *scanner.Metadata) (*Video, error) {
var titleType TitleType
switch metadata.Category.Type {
case "gaming":
titleType = TITLE_GAME
default:
titleType = TITLE_OTHER
}
videoDate, err := time.Parse("2006-01-02", metadata.Date)
if err != nil {
return nil, fmt.Errorf("failed to parse date from metadata: %v", err)
}
return &Video{
Title: &Title{
Name: metadata.Category.Name,
Type: titleType,
Url: metadata.Category.Url,
},
Part: metadata.Part,
Date: videoDate,
Tags: DEFAULT_TAGS,
Filename: path.Join(
metadata.FootageDir,
fmt.Sprintf(
"%s-fullvod.mkv",
videoDate.Format("2006-01-02"),
)),
}, nil
}
type (
MetaTitle struct {
Name string
Type string
Url string
}
Metadata struct {
Date string
Title *MetaTitle
Part int
}
)
var titleTemplate *template.Template = template.Must(
template.ParseFiles("template/title.txt"),
)
func BuildTitle(video *Video) (string, error) {
var titleType string
switch video.Title.Type {
case TITLE_GAME:
titleType = "game"
case TITLE_OTHER:
fallthrough
default:
titleType = "other"
}
out := &bytes.Buffer{}
titleTemplate.Execute(out, Metadata{
Date: strings.ToLower(video.Date.Format("02 Jan 2006")),
Title: &MetaTitle{
Name: video.Title.Name,
Type: titleType,
Url: video.Title.Url,
},
Part: video.Part,
})
return strings.TrimSpace(out.String()), nil
}
var descriptionTemplate *template.Template = template.Must(
template.ParseFiles("template/description.txt"),
)
func BuildDescription(video *Video) (string, error) {
var titleType string
switch video.Title.Type {
case TITLE_GAME:
titleType = "game"
case TITLE_OTHER:
fallthrough
default:
titleType = "other"
}
out := &bytes.Buffer{}
descriptionTemplate.Execute(out, Metadata{
Date: strings.ToLower(video.Date.Format("02 Jan 2006")),
Title: &MetaTitle{
Name: video.Title.Name,
Type: titleType,
Url: video.Title.Url,
},
Part: video.Part,
})
return out.String(), nil
}
func UploadVideo(ctx context.Context, token *oauth2.Token, video *Video) error {
title, err := BuildTitle(video)
if err != nil {
return fmt.Errorf("failed to build title: %v", err)
}
description, err := BuildDescription(video)
if err != nil {
return fmt.Errorf("failed to build description: %v", err)
}
service, err := youtube.NewService(
ctx,
option.WithScopes(youtube.YoutubeUploadScope),
option.WithTokenSource(oauth2.StaticTokenSource(token)),
)
if err != nil {
log.Fatalf("Failed to create youtube service: %v\n", err)
return err
}
videoService := youtube.NewVideosService(service)
var categoryId string
switch video.Title.Type {
case TITLE_GAME:
categoryId = CATEGORY_GAMING
default:
categoryId = CATEGORY_ENTERTAINMENT
}
call := videoService.Insert([]string{
"snippet", "status",
}, &youtube.Video{
Snippet: &youtube.VideoSnippet{
Title: title,
Description: description,
Tags: append(DEFAULT_TAGS, video.Tags...),
CategoryId: categoryId, // gaming
},
Status: &youtube.VideoStatus{
PrivacyStatus: "private",
},
}).NotifySubscribers(false)
file, err := os.Open(video.Filename)
if err != nil {
log.Fatalf("Failed to open file: %v\n", err)
return err
}
call.Media(file)
log.Println("Uploading video...")
res, err := call.Do()
if err != nil {
log.Fatalf("Failed to upload video: %v\n", err)
return err
}
data, err := json.MarshalIndent(res, "", " ")
if err != nil {
log.Fatalf("Failed to marshal video data json: %v\n", err)
return err
}
fmt.Println(string(data))
return nil
}