ytsync/local/local.go

224 lines
4.9 KiB
Go
Raw Normal View History

2021-09-27 16:11:07 +02:00
package local
import (
2021-10-14 20:03:57 +02:00
"encoding/json"
"errors"
2021-09-27 16:11:07 +02:00
"fmt"
2021-10-14 20:03:57 +02:00
"io/ioutil"
"time"
"os"
"os/exec"
"path"
"strings"
2021-09-27 16:11:07 +02:00
2021-10-14 20:03:57 +02:00
log "github.com/sirupsen/logrus"
2021-09-27 16:11:07 +02:00
"github.com/spf13/cobra"
2021-10-14 20:03:57 +02:00
"github.com/lbryio/ytsync/v5/downloader/ytdl"
"github.com/lbryio/lbry.go/v2/extras/jsonrpc"
2021-09-27 16:11:07 +02:00
)
2021-10-14 20:03:57 +02:00
type SyncContext struct {
TempDir string
LbrynetAddr string
}
func (c *SyncContext) Validate() error {
if c.TempDir == "" {
return errors.New("No TempDir provided")
}
if c.LbrynetAddr == "" {
return errors.New("No Lbrynet address provided")
}
return nil
}
var syncContext SyncContext
2021-09-27 16:11:07 +02:00
func AddCommand(rootCmd *cobra.Command) {
cmd := &cobra.Command{
Use: "local",
Short: "run a personal ytsync",
Run: localCmd,
2021-10-14 20:03:57 +02:00
Args: cobra.ExactArgs(1),
2021-09-27 16:11:07 +02:00
}
2021-10-14 20:03:57 +02:00
cmd.Flags().StringVar(&syncContext.TempDir, "temp-dir", getEnvDefault("TEMP_DIR", ""), "directory to use for temporary files")
cmd.Flags().StringVar(&syncContext.LbrynetAddr, "lbrynet-address", getEnvDefault("LBRYNET_ADDRESS", ""), "JSONRPC address of the local LBRYNet daemon")
2021-09-27 16:11:07 +02:00
rootCmd.AddCommand(cmd)
2021-10-14 20:03:57 +02:00
}
2021-09-27 16:11:07 +02:00
2021-10-14 20:03:57 +02:00
func getEnvDefault(key, defaultValue string) string {
if value, ok := os.LookupEnv(key); ok {
return value
}
return defaultValue
2021-09-27 16:11:07 +02:00
}
func localCmd(cmd *cobra.Command, args []string) {
2021-10-14 20:03:57 +02:00
err := syncContext.Validate()
if err != nil {
log.Error(err)
return
}
fmt.Println(syncContext.LbrynetAddr)
videoID := args[0]
fmt.Println(videoID)
lbrynet := jsonrpc.NewClient(syncContext.LbrynetAddr)
lbrynet.SetRPCTimeout(5 * time.Minute)
status, err := lbrynet.Status()
if err != nil {
log.Error(err)
return
}
fmt.Println(status.IsRunning)
fmt.Println(status.Wallet.Connected)
videoBasePath := path.Join(syncContext.TempDir, videoID)
_, videoMetadataPath, err := getVideoMetadata(videoBasePath, videoID)
if err != nil {
log.Errorf("Error getting video metadata: %v", err)
return
}
err = downloadVideo(videoBasePath, videoMetadataPath)
if err != nil {
log.Errorf("Error downloading video: %v", err)
}
log.Info("Done")
}
func getVideoMetadata(basePath, videoID string) (*ytdl.YtdlVideo, string, error) {
metadataPath := basePath + ".info.json"
_, err := os.Stat(metadataPath)
if err != nil && !os.IsNotExist(err) {
log.Errorf("Error determining if video metadata already exists: %v", err)
return nil, "", err
} else if err == nil {
log.Debugf("Video metadata file %s already exists. Attempting to load existing file.", metadataPath)
videoMetadata, err := loadVideoMetadata(metadataPath)
if err != nil {
log.Debugf("Error loading pre-existing video metadata: %v. Deleting file and attempting re-download.", err)
} else {
return videoMetadata, metadataPath, nil
}
}
if err := downloadVideoMetadata(basePath, videoID); err != nil {
return nil, "", err
}
videoMetadata, err := loadVideoMetadata(metadataPath)
return videoMetadata, metadataPath, err
}
func loadVideoMetadata(path string) (*ytdl.YtdlVideo, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
metadataBytes, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
}
var videoMetadata *ytdl.YtdlVideo
err = json.Unmarshal(metadataBytes, &videoMetadata)
if err != nil {
return nil, err
}
return videoMetadata, nil
}
func downloadVideoMetadata(basePath, videoID string) error {
ytdlArgs := []string{
"--skip-download",
"--write-info-json",
"--force-overwrites",
fmt.Sprintf("https://www.youtube.com/watch?v=%s", videoID),
"--cookies",
"cookies.txt",
"-o",
basePath,
}
ytdlCmd := exec.Command("yt-dlp", ytdlArgs...)
output, err := runCmd(ytdlCmd)
log.Debug(output)
return err
}
func downloadVideo(basePath, metadataPath string) error {
ytdlArgs := []string{
"--no-progress",
"-o",
basePath,
"--merge-output-format",
"mp4",
"--postprocessor-args",
"ffmpeg:-movflags faststart",
"--abort-on-unavailable-fragment",
"--fragment-retries",
"1",
"--cookies",
"cookies.txt",
"--extractor-args",
"youtube:player_client=android",
"--load-info-json",
metadataPath,
"-fbestvideo[ext=mp4][vcodec!*=av01][height<=720]+bestaudio[ext!=webm][format_id!=258][format_id!=251][format_id!=256][format_id!=327]",
}
ytdlCmd := exec.Command("yt-dlp", ytdlArgs...)
output, err := runCmd(ytdlCmd)
log.Debug(output)
return err
}
func runCmd(cmd *exec.Cmd) ([]string, error) {
log.Infof("running cmd: %s", strings.Join(cmd.Args, " "))
var err error
stderr, err := cmd.StderrPipe()
if err != nil {
return nil, err
}
stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}
err = cmd.Start()
if err != nil {
return nil, err
}
outLog, err := ioutil.ReadAll(stdout)
if err != nil {
return nil, err
}
errorLog, err := ioutil.ReadAll(stderr)
if err != nil {
return nil, err
}
done := make(chan error, 1)
go func() {
done <- cmd.Wait()
}()
select {
case err := <-done:
if err != nil {
log.Error(string(errorLog))
return nil, err
}
return strings.Split(strings.Replace(string(outLog), "\r\n", "\n", -1), "\n"), nil
}
2021-09-27 16:11:07 +02:00
}