130 lines
3.2 KiB
Go
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))
|
|
}
|
|
}
|