2017-12-28 18:14:33 +01:00
|
|
|
package sources
|
|
|
|
|
|
|
|
import (
|
2019-05-03 05:11:52 +02:00
|
|
|
"fmt"
|
2019-05-28 18:33:42 +02:00
|
|
|
"io/ioutil"
|
2019-04-19 03:22:51 +02:00
|
|
|
"math"
|
2017-12-28 18:14:33 +01:00
|
|
|
"os"
|
2019-05-28 18:33:42 +02:00
|
|
|
"os/exec"
|
2017-12-28 18:14:33 +01:00
|
|
|
"regexp"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
2018-09-18 21:20:34 +02:00
|
|
|
"time"
|
2018-08-23 00:28:31 +02:00
|
|
|
|
2019-01-11 02:34:34 +01:00
|
|
|
"github.com/lbryio/lbry.go/extras/errors"
|
|
|
|
"github.com/lbryio/lbry.go/extras/jsonrpc"
|
2019-04-19 03:22:51 +02:00
|
|
|
"github.com/lbryio/lbry.go/extras/util"
|
2019-05-06 21:56:56 +02:00
|
|
|
|
2018-10-08 22:19:17 +02:00
|
|
|
"github.com/lbryio/ytsync/namer"
|
2019-05-06 21:56:56 +02:00
|
|
|
"github.com/lbryio/ytsync/sdk"
|
2019-05-28 18:33:42 +02:00
|
|
|
"github.com/lbryio/ytsync/tagsManager"
|
2019-05-06 21:56:56 +02:00
|
|
|
"github.com/lbryio/ytsync/thumbs"
|
2017-12-28 18:14:33 +01:00
|
|
|
|
2019-04-19 03:22:51 +02:00
|
|
|
"github.com/ChannelMeter/iso8601duration"
|
2019-05-03 05:11:52 +02:00
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
2019-02-22 19:33:00 +01:00
|
|
|
"github.com/nikooo777/ytdl"
|
2017-12-28 18:14:33 +01:00
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
"google.golang.org/api/youtube/v3"
|
|
|
|
)
|
|
|
|
|
|
|
|
type YoutubeVideo struct {
|
|
|
|
id string
|
|
|
|
channelTitle string
|
|
|
|
title string
|
|
|
|
description string
|
|
|
|
playlistPosition int64
|
2018-08-14 17:09:23 +02:00
|
|
|
size *int64
|
2018-09-18 23:28:25 +02:00
|
|
|
maxVideoSize int64
|
2019-01-03 19:55:27 +01:00
|
|
|
maxVideoLength float64
|
2017-12-28 18:14:33 +01:00
|
|
|
publishedAt time.Time
|
|
|
|
dir string
|
2019-04-19 03:22:51 +02:00
|
|
|
youtubeInfo *youtube.Video
|
|
|
|
tags []string
|
2019-05-03 05:11:52 +02:00
|
|
|
awsConfig aws.Config
|
2019-05-07 16:01:11 +02:00
|
|
|
thumbnailURL string
|
2019-05-07 21:15:43 +02:00
|
|
|
lbryChannelID string
|
2019-05-03 05:11:52 +02:00
|
|
|
}
|
|
|
|
|
2019-05-06 21:56:56 +02:00
|
|
|
const reflectorURL = "http://blobs.lbry.io/"
|
|
|
|
|
2019-05-03 05:11:52 +02:00
|
|
|
var youtubeCategories = map[string]string{
|
|
|
|
"1": "Film & Animation",
|
|
|
|
"2": "Autos & Vehicles",
|
|
|
|
"10": "Music",
|
|
|
|
"15": "Pets & Animals",
|
|
|
|
"17": "Sports",
|
|
|
|
"18": "Short Movies",
|
|
|
|
"19": "Travel & Events",
|
|
|
|
"20": "Gaming",
|
|
|
|
"21": "Videoblogging",
|
|
|
|
"22": "People & Blogs",
|
|
|
|
"23": "Comedy",
|
|
|
|
"24": "Entertainment",
|
|
|
|
"25": "News & Politics",
|
|
|
|
"26": "Howto & Style",
|
|
|
|
"27": "Education",
|
|
|
|
"28": "Science & Technology",
|
|
|
|
"29": "Nonprofits & Activism",
|
|
|
|
"30": "Movies",
|
|
|
|
"31": "Anime/Animation",
|
|
|
|
"32": "Action/Adventure",
|
|
|
|
"33": "Classics",
|
|
|
|
"34": "Comedy",
|
|
|
|
"35": "Documentary",
|
|
|
|
"36": "Drama",
|
|
|
|
"37": "Family",
|
|
|
|
"38": "Foreign",
|
|
|
|
"39": "Horror",
|
|
|
|
"40": "Sci-Fi/Fantasy",
|
|
|
|
"41": "Thriller",
|
|
|
|
"42": "Shorts",
|
|
|
|
"43": "Shows",
|
|
|
|
"44": "Trailers",
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
|
|
|
|
2019-05-03 05:11:52 +02:00
|
|
|
func NewYoutubeVideo(directory string, videoData *youtube.Video, playlistPosition int64, awsConfig aws.Config) *YoutubeVideo {
|
2019-04-19 03:22:51 +02:00
|
|
|
publishedAt, _ := time.Parse(time.RFC3339Nano, videoData.Snippet.PublishedAt) // ignore parse errors
|
2018-08-17 16:05:54 +02:00
|
|
|
return &YoutubeVideo{
|
2019-04-19 03:22:51 +02:00
|
|
|
id: videoData.Id,
|
|
|
|
title: videoData.Snippet.Title,
|
|
|
|
description: videoData.Snippet.Description,
|
|
|
|
channelTitle: videoData.Snippet.ChannelTitle,
|
|
|
|
playlistPosition: playlistPosition,
|
2017-12-28 18:14:33 +01:00
|
|
|
publishedAt: publishedAt,
|
|
|
|
dir: directory,
|
2019-04-19 03:22:51 +02:00
|
|
|
youtubeInfo: videoData,
|
2019-05-03 05:11:52 +02:00
|
|
|
awsConfig: awsConfig,
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-17 16:05:54 +02:00
|
|
|
func (v *YoutubeVideo) ID() string {
|
2017-12-28 18:14:33 +01:00
|
|
|
return v.id
|
|
|
|
}
|
|
|
|
|
2018-08-17 16:05:54 +02:00
|
|
|
func (v *YoutubeVideo) PlaylistPosition() int {
|
2018-04-25 20:56:26 +02:00
|
|
|
return int(v.playlistPosition)
|
|
|
|
}
|
|
|
|
|
2018-08-17 16:05:54 +02:00
|
|
|
func (v *YoutubeVideo) IDAndNum() string {
|
2017-12-28 18:14:33 +01:00
|
|
|
return v.ID() + " (" + strconv.Itoa(int(v.playlistPosition)) + " in channel)"
|
|
|
|
}
|
|
|
|
|
2018-08-17 16:05:54 +02:00
|
|
|
func (v *YoutubeVideo) PublishedAt() time.Time {
|
2017-12-28 18:14:33 +01:00
|
|
|
return v.publishedAt
|
|
|
|
}
|
|
|
|
|
2018-10-09 21:57:07 +02:00
|
|
|
func (v *YoutubeVideo) getFullPath() string {
|
2018-05-05 13:22:33 +02:00
|
|
|
maxLen := 30
|
2017-12-28 18:14:33 +01:00
|
|
|
reg := regexp.MustCompile(`[^a-zA-Z0-9]+`)
|
|
|
|
|
|
|
|
chunks := strings.Split(strings.ToLower(strings.Trim(reg.ReplaceAllString(v.title, "-"), "-")), "-")
|
|
|
|
|
|
|
|
name := chunks[0]
|
|
|
|
if len(name) > maxLen {
|
2018-08-10 14:41:21 +02:00
|
|
|
name = name[:maxLen]
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, chunk := range chunks[1:] {
|
|
|
|
tmpName := name + "-" + chunk
|
|
|
|
if len(tmpName) > maxLen {
|
|
|
|
if len(name) < 20 {
|
|
|
|
name = tmpName[:maxLen]
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
name = tmpName
|
|
|
|
}
|
2018-06-04 16:35:35 +02:00
|
|
|
if len(name) < 1 {
|
|
|
|
name = v.id
|
|
|
|
}
|
2018-07-24 02:01:35 +02:00
|
|
|
return v.videoDir() + "/" + name + ".mp4"
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
|
|
|
|
2018-08-17 16:05:54 +02:00
|
|
|
func (v *YoutubeVideo) getAbbrevDescription() string {
|
2017-12-28 18:14:33 +01:00
|
|
|
maxLines := 10
|
|
|
|
description := strings.TrimSpace(v.description)
|
|
|
|
if strings.Count(description, "\n") < maxLines {
|
|
|
|
return description
|
|
|
|
}
|
2019-05-07 21:15:43 +02:00
|
|
|
additionalDescription := "\nhttps://www.youtube.com/watch?v=" + v.id
|
|
|
|
khanAcademyClaimID := "5fc52291980268b82413ca4c0ace1b8d749f3ffb"
|
|
|
|
if v.lbryChannelID == khanAcademyClaimID {
|
|
|
|
additionalDescription = additionalDescription + "\nNote: All Khan Academy content is available for free at (www.khanacademy.org)"
|
|
|
|
}
|
2017-12-28 18:14:33 +01:00
|
|
|
return strings.Join(strings.Split(description, "\n")[:maxLines], "\n") + "\n..."
|
|
|
|
}
|
|
|
|
|
2019-05-28 18:33:42 +02:00
|
|
|
func (v *YoutubeVideo) fallbackDownload() error {
|
|
|
|
cmd := exec.Command("youtube-dl",
|
|
|
|
"--id "+v.ID(),
|
|
|
|
"--no-progress",
|
|
|
|
"-f \"bestvideo[ext=mp4,height<=1080,filesize<1000M]+bestaudio/best[ext=mp4,height<=1080,filesize<1000M]\"",
|
|
|
|
"-o "+strings.TrimRight(v.getFullPath(), ".mp4"))
|
|
|
|
log.Printf("Running command and waiting for it to finish...")
|
|
|
|
err := cmd.Run()
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Command finished with error: %v", err)
|
|
|
|
return errors.Err(err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-08-17 16:05:54 +02:00
|
|
|
func (v *YoutubeVideo) download() error {
|
2018-10-09 21:57:07 +02:00
|
|
|
videoPath := v.getFullPath()
|
2017-12-28 18:14:33 +01:00
|
|
|
|
2018-07-24 02:01:35 +02:00
|
|
|
err := os.Mkdir(v.videoDir(), 0750)
|
2018-06-09 01:14:55 +02:00
|
|
|
if err != nil && !strings.Contains(err.Error(), "file exists") {
|
|
|
|
return errors.Wrap(err, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = os.Stat(videoPath)
|
2017-12-28 18:14:33 +01:00
|
|
|
if err != nil && !os.IsNotExist(err) {
|
2019-01-11 20:15:17 +01:00
|
|
|
return errors.Err(err)
|
2017-12-28 18:14:33 +01:00
|
|
|
} else if err == nil {
|
|
|
|
log.Debugln(v.id + " already exists at " + videoPath)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-03-13 22:48:01 +01:00
|
|
|
videoUrl := "https://www.youtube.com/watch?v=" + v.id
|
|
|
|
videoInfo, err := ytdl.GetVideoInfo(videoUrl)
|
2017-12-28 18:14:33 +01:00
|
|
|
if err != nil {
|
2019-01-11 20:15:17 +01:00
|
|
|
return errors.Err(err)
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
2018-03-13 22:48:01 +01:00
|
|
|
|
2018-09-18 23:28:25 +02:00
|
|
|
codec := []string{"H.264"}
|
|
|
|
ext := []string{"mp4"}
|
|
|
|
|
|
|
|
//Filter requires a [] interface{}
|
|
|
|
codecFilter := make([]interface{}, len(codec))
|
|
|
|
for i, v := range codec {
|
|
|
|
codecFilter[i] = v
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
2018-03-13 22:48:01 +01:00
|
|
|
|
2018-09-18 23:28:25 +02:00
|
|
|
//Filter requires a [] interface{}
|
|
|
|
extFilter := make([]interface{}, len(ext))
|
|
|
|
for i, v := range ext {
|
|
|
|
extFilter[i] = v
|
|
|
|
}
|
|
|
|
|
|
|
|
formats := videoInfo.Formats.Filter(ytdl.FormatVideoEncodingKey, codecFilter).Filter(ytdl.FormatExtensionKey, extFilter)
|
|
|
|
if len(formats) == 0 {
|
|
|
|
return errors.Err("no compatible format available for this video")
|
|
|
|
}
|
|
|
|
maxRetryAttempts := 5
|
2019-01-03 19:55:27 +01:00
|
|
|
isLengthLimitSet := v.maxVideoLength > 0.01
|
|
|
|
if isLengthLimitSet && videoInfo.Duration.Hours() > v.maxVideoLength {
|
2018-10-11 23:21:05 +02:00
|
|
|
return errors.Err("video is too long to process")
|
|
|
|
}
|
|
|
|
|
2018-09-18 23:28:25 +02:00
|
|
|
for i := 0; i < len(formats) && i < maxRetryAttempts; i++ {
|
|
|
|
formatIndex := i
|
|
|
|
if i == maxRetryAttempts-1 {
|
|
|
|
formatIndex = len(formats) - 1
|
|
|
|
}
|
|
|
|
var downloadedFile *os.File
|
|
|
|
downloadedFile, err = os.Create(videoPath)
|
|
|
|
if err != nil {
|
2019-01-11 20:15:17 +01:00
|
|
|
return errors.Err(err)
|
2018-09-18 23:28:25 +02:00
|
|
|
}
|
|
|
|
err = videoInfo.Download(formats[formatIndex], downloadedFile)
|
2019-02-15 14:11:38 +01:00
|
|
|
_ = downloadedFile.Close()
|
2018-09-18 23:28:25 +02:00
|
|
|
if err != nil {
|
2018-10-03 02:51:42 +02:00
|
|
|
//delete the video and ignore the error
|
|
|
|
_ = v.delete()
|
2019-02-15 14:11:38 +01:00
|
|
|
return errors.Err(err.Error())
|
2018-09-18 23:28:25 +02:00
|
|
|
}
|
2018-10-09 21:57:07 +02:00
|
|
|
fi, err := os.Stat(v.getFullPath())
|
2018-09-18 23:28:25 +02:00
|
|
|
if err != nil {
|
2019-01-11 20:15:17 +01:00
|
|
|
return errors.Err(err)
|
2018-09-18 23:28:25 +02:00
|
|
|
}
|
|
|
|
videoSize := fi.Size()
|
|
|
|
v.size = &videoSize
|
|
|
|
|
2019-01-03 19:55:27 +01:00
|
|
|
isVideoSizeLimitSet := v.maxVideoSize > 0
|
|
|
|
if isVideoSizeLimitSet && videoSize > v.maxVideoSize {
|
2018-09-18 23:28:25 +02:00
|
|
|
//delete the video and ignore the error
|
|
|
|
_ = v.delete()
|
|
|
|
err = errors.Err("file is too big and there is no other format available")
|
|
|
|
} else {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2019-01-11 20:15:17 +01:00
|
|
|
return errors.Err(err)
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
|
|
|
|
2018-08-17 16:05:54 +02:00
|
|
|
func (v *YoutubeVideo) videoDir() string {
|
2018-07-24 02:01:35 +02:00
|
|
|
return v.dir + "/" + v.id
|
|
|
|
}
|
2019-05-28 18:33:42 +02:00
|
|
|
func (v *YoutubeVideo) getDownloadedPath() (string, error) {
|
|
|
|
files, err := ioutil.ReadDir(v.videoDir())
|
|
|
|
if err != nil {
|
|
|
|
err = errors.Prefix("list error", err)
|
|
|
|
log.Errorln(err)
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, f := range files {
|
|
|
|
if f.IsDir() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if strings.Contains(v.getFullPath(), f.Name()) {
|
|
|
|
return v.videoDir() + "/" + f.Name(), nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return "", errors.Err("could not find any downloaded videos")
|
2018-07-24 02:01:35 +02:00
|
|
|
|
2019-05-28 18:33:42 +02:00
|
|
|
}
|
2018-08-17 16:05:54 +02:00
|
|
|
func (v *YoutubeVideo) delete() error {
|
2019-05-28 18:33:42 +02:00
|
|
|
videoPath, err := v.getDownloadedPath()
|
|
|
|
if err != nil {
|
|
|
|
log.Errorln(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = os.Remove(videoPath)
|
|
|
|
log.Debugf("%s deleted from disk (%s)", v.id, videoPath)
|
|
|
|
|
2018-04-25 20:56:26 +02:00
|
|
|
if err != nil {
|
2019-05-28 18:33:42 +02:00
|
|
|
err = errors.Prefix("delete error", err)
|
|
|
|
log.Errorln(err)
|
2018-04-25 20:56:26 +02:00
|
|
|
return err
|
|
|
|
}
|
2019-05-28 18:33:42 +02:00
|
|
|
|
2018-04-25 20:56:26 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-05-07 16:01:11 +02:00
|
|
|
func (v *YoutubeVideo) triggerThumbnailSave() (err error) {
|
|
|
|
thumbnail := thumbs.GetBestThumbnail(v.youtubeInfo.Snippet.Thumbnails)
|
|
|
|
v.thumbnailURL, err = thumbs.MirrorThumbnail(thumbnail.Url, v.ID(), v.awsConfig)
|
|
|
|
return err
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
|
|
|
|
2019-05-07 21:15:43 +02:00
|
|
|
func (v *YoutubeVideo) publish(daemon *jsonrpc.Client, claimAddress string, amount float64, namer *namer.Namer) (*SyncSummary, error) {
|
|
|
|
languages, locations, tags := v.getMetadata()
|
2019-04-19 03:22:51 +02:00
|
|
|
|
|
|
|
options := jsonrpc.StreamCreateOptions{
|
|
|
|
ClaimCreateOptions: jsonrpc.ClaimCreateOptions{
|
|
|
|
Title: v.title,
|
2019-05-07 21:15:43 +02:00
|
|
|
Description: v.getAbbrevDescription(),
|
2019-04-19 03:22:51 +02:00
|
|
|
ClaimAddress: &claimAddress,
|
|
|
|
Languages: languages,
|
2019-05-07 16:01:11 +02:00
|
|
|
ThumbnailURL: &v.thumbnailURL,
|
2019-05-07 21:15:43 +02:00
|
|
|
Tags: tags,
|
|
|
|
Locations: locations,
|
2019-01-30 13:42:23 +01:00
|
|
|
},
|
2019-05-06 21:56:56 +02:00
|
|
|
Author: util.PtrToString(v.channelTitle),
|
|
|
|
License: util.PtrToString("Copyrighted (contact author)"),
|
|
|
|
ReleaseTime: util.PtrToInt64(v.publishedAt.Unix()),
|
2019-05-07 21:15:43 +02:00
|
|
|
ChannelID: &v.lbryChannelID,
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
2019-05-28 18:33:42 +02:00
|
|
|
downloadPath, err := v.getDownloadedPath()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return publishAndRetryExistingNames(daemon, v.title, downloadPath, amount, options, namer)
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
|
|
|
|
2018-08-17 16:05:54 +02:00
|
|
|
func (v *YoutubeVideo) Size() *int64 {
|
2018-08-14 17:09:23 +02:00
|
|
|
return v.size
|
|
|
|
}
|
|
|
|
|
2019-05-03 05:11:52 +02:00
|
|
|
type SyncParams struct {
|
|
|
|
ClaimAddress string
|
|
|
|
Amount float64
|
|
|
|
ChannelID string
|
|
|
|
MaxVideoSize int
|
|
|
|
Namer *namer.Namer
|
|
|
|
MaxVideoLength float64
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *YoutubeVideo) Sync(daemon *jsonrpc.Client, params SyncParams, existingVideoData *sdk.SyncedVideo, reprocess bool) (*SyncSummary, error) {
|
|
|
|
v.maxVideoSize = int64(params.MaxVideoSize) * 1024 * 1024
|
|
|
|
v.maxVideoLength = params.MaxVideoLength
|
2019-05-07 21:15:43 +02:00
|
|
|
v.lbryChannelID = params.ChannelID
|
2019-05-07 22:07:44 +02:00
|
|
|
if reprocess && existingVideoData != nil && existingVideoData.Published {
|
2019-05-06 21:56:56 +02:00
|
|
|
summary, err := v.reprocess(daemon, params, existingVideoData)
|
2019-05-03 05:11:52 +02:00
|
|
|
return summary, err
|
|
|
|
}
|
2019-05-06 21:56:56 +02:00
|
|
|
return v.downloadAndPublish(daemon, params)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *YoutubeVideo) downloadAndPublish(daemon *jsonrpc.Client, params SyncParams) (*SyncSummary, error) {
|
2018-02-13 18:47:05 +01:00
|
|
|
err := v.download()
|
2017-12-28 18:14:33 +01:00
|
|
|
if err != nil {
|
2019-05-28 18:33:42 +02:00
|
|
|
log.Errorf("standard downloader failed: %s. Trying fallback downloader\n", err.Error())
|
|
|
|
fallBackErr := v.fallbackDownload()
|
|
|
|
if fallBackErr != nil {
|
|
|
|
log.Errorf("fallback downloader failed: %s\n", err.Error())
|
|
|
|
return nil, errors.Prefix("download error", err) //return original error
|
|
|
|
}
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
|
|
|
log.Debugln("Downloaded " + v.id)
|
|
|
|
|
2018-02-13 18:47:05 +01:00
|
|
|
err = v.triggerThumbnailSave()
|
2017-12-28 18:14:33 +01:00
|
|
|
if err != nil {
|
2018-07-21 01:56:36 +02:00
|
|
|
return nil, errors.Prefix("thumbnail error", err)
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
|
|
|
log.Debugln("Created thumbnail for " + v.id)
|
|
|
|
|
2019-05-07 21:15:43 +02:00
|
|
|
summary, err := v.publish(daemon, params.ClaimAddress, params.Amount, params.Namer)
|
2018-06-06 23:47:28 +02:00
|
|
|
//delete the video in all cases (and ignore the error)
|
|
|
|
_ = v.delete()
|
2018-04-25 20:56:26 +02:00
|
|
|
|
2018-10-03 02:51:42 +02:00
|
|
|
return summary, errors.Prefix("publish error", err)
|
2017-12-28 18:14:33 +01:00
|
|
|
}
|
2019-05-03 05:11:52 +02:00
|
|
|
|
2019-05-07 21:15:43 +02:00
|
|
|
func (v *YoutubeVideo) getMetadata() (languages []string, locations []jsonrpc.Location, tags []string) {
|
|
|
|
languages = nil
|
|
|
|
if v.youtubeInfo.Snippet.DefaultLanguage != "" {
|
|
|
|
languages = []string{v.youtubeInfo.Snippet.DefaultLanguage}
|
|
|
|
}
|
|
|
|
|
|
|
|
locations = nil
|
2019-05-08 23:12:13 +02:00
|
|
|
if v.youtubeInfo.RecordingDetails != nil && v.youtubeInfo.RecordingDetails.Location != nil {
|
2019-05-07 21:15:43 +02:00
|
|
|
locations = []jsonrpc.Location{{
|
|
|
|
Latitude: util.PtrToString(fmt.Sprintf("%.7f", v.youtubeInfo.RecordingDetails.Location.Latitude)),
|
|
|
|
Longitude: util.PtrToString(fmt.Sprintf("%.7f", v.youtubeInfo.RecordingDetails.Location.Longitude)),
|
|
|
|
}}
|
|
|
|
}
|
|
|
|
tags = append([]string{youtubeCategories[v.youtubeInfo.Snippet.CategoryId]}, v.youtubeInfo.Snippet.Tags...)
|
2019-05-28 18:33:42 +02:00
|
|
|
tags = tagsManager.SanitizeTags(tags)
|
2019-05-07 21:15:43 +02:00
|
|
|
return languages, locations, tags
|
|
|
|
}
|
|
|
|
|
2019-05-06 21:56:56 +02:00
|
|
|
func (v *YoutubeVideo) reprocess(daemon *jsonrpc.Client, params SyncParams, existingVideoData *sdk.SyncedVideo) (*SyncSummary, error) {
|
2019-05-03 05:11:52 +02:00
|
|
|
c, err := daemon.ClaimSearch(nil, &existingVideoData.ClaimID, nil, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Err(err)
|
|
|
|
}
|
|
|
|
if len(c.Claims) == 0 {
|
|
|
|
return nil, errors.Err("cannot reprocess: no claim found for this video")
|
|
|
|
} else if len(c.Claims) > 1 {
|
|
|
|
return nil, errors.Err("cannot reprocess: too many claims. claimID: %s", existingVideoData.ClaimID)
|
|
|
|
}
|
|
|
|
|
|
|
|
currentClaim := c.Claims[0]
|
2019-05-07 21:15:43 +02:00
|
|
|
languages, locations, tags := v.getMetadata()
|
2019-05-03 05:11:52 +02:00
|
|
|
|
|
|
|
thumbnailURL := ""
|
|
|
|
if currentClaim.Value.GetThumbnail() == nil {
|
|
|
|
thumbnail := thumbs.GetBestThumbnail(v.youtubeInfo.Snippet.Thumbnails)
|
|
|
|
thumbnailURL, err = thumbs.MirrorThumbnail(thumbnail.Url, v.ID(), v.awsConfig)
|
|
|
|
} else {
|
|
|
|
thumbnailURL = thumbs.ThumbnailEndpoint + v.ID()
|
|
|
|
}
|
|
|
|
|
2019-05-06 21:56:56 +02:00
|
|
|
videoSize, err := currentClaim.GetStreamSizeByMagic()
|
|
|
|
if err != nil {
|
|
|
|
if existingVideoData.Size > 0 {
|
|
|
|
videoSize = uint64(existingVideoData.Size)
|
|
|
|
} else {
|
|
|
|
log.Infof("%s: the video must be republished as we can't get the right size", v.ID())
|
2019-05-07 16:01:11 +02:00
|
|
|
//return v.downloadAndPublish(daemon, params) //TODO: actually republish the video. NB: the current claim should be abandoned first
|
|
|
|
return nil, errors.Err("the video must be republished as we can't get the right size")
|
2019-05-06 21:56:56 +02:00
|
|
|
}
|
|
|
|
}
|
2019-05-07 21:15:43 +02:00
|
|
|
|
2019-05-03 05:11:52 +02:00
|
|
|
videoDuration, err := duration.FromString(v.youtubeInfo.ContentDetails.Duration)
|
2019-05-07 16:01:11 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Err(err)
|
|
|
|
}
|
2019-05-07 21:15:43 +02:00
|
|
|
|
2019-05-07 16:01:11 +02:00
|
|
|
pr, err := daemon.StreamUpdate(existingVideoData.ClaimID, jsonrpc.StreamUpdateOptions{
|
2019-05-03 05:11:52 +02:00
|
|
|
StreamCreateOptions: &jsonrpc.StreamCreateOptions{
|
|
|
|
ClaimCreateOptions: jsonrpc.ClaimCreateOptions{
|
|
|
|
Title: v.title,
|
|
|
|
Description: v.getAbbrevDescription(),
|
|
|
|
Tags: tags,
|
|
|
|
Languages: languages,
|
|
|
|
Locations: locations,
|
|
|
|
ThumbnailURL: &thumbnailURL,
|
|
|
|
},
|
2019-05-06 21:56:56 +02:00
|
|
|
Author: util.PtrToString(""),
|
|
|
|
License: util.PtrToString("Copyrighted (contact author)"),
|
|
|
|
ReleaseTime: util.PtrToInt64(v.publishedAt.Unix()),
|
|
|
|
Duration: util.PtrToUint64(uint64(math.Ceil(videoDuration.ToDuration().Seconds()))),
|
2019-05-07 21:15:43 +02:00
|
|
|
ChannelID: &v.lbryChannelID,
|
2019-05-03 05:11:52 +02:00
|
|
|
},
|
2019-05-08 23:12:13 +02:00
|
|
|
FileSize: &videoSize,
|
2019-05-03 05:11:52 +02:00
|
|
|
})
|
2019-05-07 16:01:11 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &SyncSummary{
|
2019-05-08 23:12:13 +02:00
|
|
|
ClaimID: pr.Outputs[0].ClaimID,
|
|
|
|
ClaimName: pr.Outputs[0].Name,
|
2019-05-07 16:01:11 +02:00
|
|
|
}, nil
|
2019-05-03 05:11:52 +02:00
|
|
|
}
|