md2yt/main.go
2025-08-07 23:59:01 +01:00

130 lines
3.2 KiB
Go

package main
import (
"bufio"
"context"
"fmt"
"log"
"os"
"regexp"
"strings"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/api/option"
"google.golang.org/api/youtube/v3"
)
func getClient(config *oauth2.Config) *oauth2.Token {
tokFile := "token.json"
f, err := os.Open(tokFile)
if err == nil {
defer f.Close()
var tok oauth2.Token
fmt.Fscan(f, &tok.AccessToken, &tok.TokenType, &tok.RefreshToken, &tok.Expiry)
return &tok
}
authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
fmt.Printf("Go to the following link in your browser then type the authorization code: \n%v\n", authURL)
var authCode string
fmt.Scan(&authCode)
tok, err := config.Exchange(context.Background(), authCode)
if err != nil {
log.Fatalf("Unable to retrieve token from web: %v", err)
}
f, _ = os.Create(tokFile)
defer f.Close()
fmt.Fprint(f, tok.AccessToken, " ", tok.TokenType, " ", tok.RefreshToken, " ", tok.Expiry)
return tok
}
func main() {
b, err := os.ReadFile("credentials.json")
if err != nil {
log.Fatalf("Unable to read client secret file: %v", err)
}
config, err := google.ConfigFromJSON(b, youtube.YoutubeScope)
if err != nil {
log.Fatalf("Unable to parse client secret file to config: %v", err)
}
tok := getClient(config)
client := config.Client(context.Background(), tok)
service, err := youtube.NewService(context.Background(), option.WithHTTPClient(client))
if err != nil {
log.Fatalf("Unable to create YouTube client: %v", err)
}
file, err := os.Open("playlist.md")
if err != nil {
log.Fatalf("Unable to open playlist file: %v", err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
linkRegex := regexp.MustCompile(`https:\/\/music\.youtube\.com\/watch\?v=([a-zA-Z0-9_-]+)`)
playlists := make(map[string][]string)
var currentPlaylist string
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "## ") {
currentPlaylist = strings.TrimPrefix(line, "## ")
playlists[currentPlaylist] = []string{}
}
if matches := linkRegex.FindStringSubmatch(line); matches != nil && currentPlaylist != "" {
playlists[currentPlaylist] = append(playlists[currentPlaylist], matches[1])
}
}
if len(playlists) == 0 {
log.Fatal("No playlists found in file")
}
for title, videoIDs := range playlists {
if len(videoIDs) == 0 {
continue
}
playlist := &youtube.Playlist{
Snippet: &youtube.PlaylistSnippet{
Title: title,
Description: "Created automatically from Markdown",
},
Status: &youtube.PlaylistStatus{
PrivacyStatus: "private",
},
}
newPlaylist, err := service.Playlists.Insert([]string{"snippet", "status"}, playlist).Do()
if err != nil {
log.Printf("Unable to create playlist '%s': %v", title, err)
continue
}
for _, id := range videoIDs {
_, err := service.PlaylistItems.Insert([]string{"snippet"}, &youtube.PlaylistItem{
Snippet: &youtube.PlaylistItemSnippet{
PlaylistId: newPlaylist.Id,
ResourceId: &youtube.ResourceId{
Kind: "youtube#video",
VideoId: id,
},
},
}).Do()
if err != nil {
log.Printf("Unable to add video %s to playlist '%s': %v", id, title, err)
}
}
fmt.Printf("Playlist '%s' created with %d videos\n", title, len(videoIDs))
}
}