add Fee support

lowercase all tags
refactor code
implement and test regexps for tags
This commit is contained in:
Niko Storni 2019-06-06 02:16:07 +02:00
parent cfe8ff0879
commit 76e653fb9b
7 changed files with 229 additions and 97 deletions

2
go.sum
View file

@ -18,6 +18,7 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o= github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzsP1G42dRafH3vf+al2vQIJU0YHX+1Tw87oco=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/aws/aws-sdk-go v1.17.3 h1:KBXxg7Jh0TxE5zmpNB2DwKmJeDUqh0O6jhy25TuYOmc= github.com/aws/aws-sdk-go v1.17.3 h1:KBXxg7Jh0TxE5zmpNB2DwKmJeDUqh0O6jhy25TuYOmc=
github.com/aws/aws-sdk-go v1.17.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.17.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
@ -59,6 +60,7 @@ github.com/go-ini/ini v1.42.0 h1:TWr1wGj35+UiWHlBA8er89seFXxzwFn11spilrrj+38=
github.com/go-ini/ini v1.42.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-ini/ini v1.42.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-ozzo/ozzo-validation v3.5.0+incompatible h1:sUy/in/P6askYr16XJgTKq/0SZhiWsdg4WZGaLsGQkM=
github.com/go-ozzo/ozzo-validation v3.5.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU= github.com/go-ozzo/ozzo-validation v3.5.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=

View file

@ -133,6 +133,7 @@ func (s *SyncManager) Start() error {
AwsS3Region: s.awsS3Region, AwsS3Region: s.awsS3Region,
AwsS3Bucket: s.awsS3Bucket, AwsS3Bucket: s.awsS3Bucket,
namer: namer.NewNamer(), namer: namer.NewNamer(),
Fee: channels[0].Fee,
} }
shouldInterruptLoop = true shouldInterruptLoop = true
} else { } else {
@ -167,6 +168,7 @@ func (s *SyncManager) Start() error {
AwsS3Region: s.awsS3Region, AwsS3Region: s.awsS3Region,
AwsS3Bucket: s.awsS3Bucket, AwsS3Bucket: s.awsS3Bucket,
namer: namer.NewNamer(), namer: namer.NewNamer(),
Fee: c.Fee,
}) })
} }
} }

View file

@ -74,18 +74,17 @@ type Sync struct {
AwsS3Secret string AwsS3Secret string
AwsS3Region string AwsS3Region string
AwsS3Bucket string AwsS3Bucket string
Fee *sdk.Fee
daemon *jsonrpc.Client daemon *jsonrpc.Client
claimAddress string claimAddress string
videoDirectory string videoDirectory string
syncedVideosMux *sync.RWMutex syncedVideosMux *sync.RWMutex
syncedVideos map[string]sdk.SyncedVideo syncedVideos map[string]sdk.SyncedVideo
grp *stop.Group grp *stop.Group
lbryChannelID string lbryChannelID string
namer *namer.Namer namer *namer.Namer
walletMux *sync.Mutex
walletMux *sync.Mutex queue chan video
queue chan video
} }
func (s *Sync) AppendSyncedVideo(videoID string, published bool, failureReason string, claimName string) { func (s *Sync) AppendSyncedVideo(videoID string, published bool, failureReason string, claimName string) {
@ -876,6 +875,7 @@ func (s *Sync) processVideo(v video) (err error) {
MaxVideoSize: s.Manager.maxVideoSize, MaxVideoSize: s.Manager.maxVideoSize,
Namer: s.namer, Namer: s.namer,
MaxVideoLength: s.Manager.maxVideoLength, MaxVideoLength: s.Manager.maxVideoLength,
Fee: s.Fee,
} }
summary, err := v.Sync(s.daemon, sp, &sv, isUpgradeSync) summary, err := v.Sync(s.daemon, sp, &sv, isUpgradeSync)

View file

@ -35,16 +35,17 @@ type SyncProperties struct {
YoutubeChannelID string YoutubeChannelID string
} }
type Fee struct {
Amount string `json:"amount"`
Address string `json:"address"`
Currency string `json:"currency"`
}
type YoutubeChannel struct { type YoutubeChannel struct {
ChannelId string `json:"channel_id"` ChannelId string `json:"channel_id"`
TotalVideos uint `json:"total_videos"` TotalVideos uint `json:"total_videos"`
DesiredChannelName string `json:"desired_channel_name"` DesiredChannelName string `json:"desired_channel_name"`
Fee *struct { Fee *Fee `json:"fee"`
Amount string `json:"amount"` ChannelClaimID string `json:"channel_claim_id"`
Address string `json:"address"`
Currency string `json:"currency"`
} `json:"fee"`
ChannelClaimID string `json:"channel_claim_id"`
} }
func (a *APIConfig) FetchChannels(status string, cp *SyncProperties) ([]YoutubeChannel, error) { func (a *APIConfig) FetchChannels(status string, cp *SyncProperties) ([]YoutubeChannel, error) {

View file

@ -24,6 +24,7 @@ import (
"github.com/ChannelMeter/iso8601duration" "github.com/ChannelMeter/iso8601duration"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/nikooo777/ytdl" "github.com/nikooo777/ytdl"
"github.com/shopspring/decimal"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"google.golang.org/api/youtube/v3" "google.golang.org/api/youtube/v3"
) )
@ -50,38 +51,38 @@ type YoutubeVideo struct {
const reflectorURL = "http://blobs.lbry.io/" const reflectorURL = "http://blobs.lbry.io/"
var youtubeCategories = map[string]string{ var youtubeCategories = map[string]string{
"1": "Film & Animation", "1": "film & animation",
"2": "Autos & Vehicles", "2": "autos & vehicles",
"10": "Music", "10": "music",
"15": "Pets & Animals", "15": "pets & animals",
"17": "Sports", "17": "sports",
"18": "Short Movies", "18": "short movies",
"19": "Travel & Events", "19": "travel & events",
"20": "Gaming", "20": "gaming",
"21": "Videoblogging", "21": "videoblogging",
"22": "People & Blogs", "22": "people & blogs",
"23": "Comedy", "23": "comedy",
"24": "Entertainment", "24": "entertainment",
"25": "News & Politics", "25": "news & politics",
"26": "Howto & Style", "26": "howto & style",
"27": "Education", "27": "education",
"28": "Science & Technology", "28": "science & technology",
"29": "Nonprofits & Activism", "29": "nonprofits & activism",
"30": "Movies", "30": "movies",
"31": "Anime/Animation", "31": "anime/animation",
"32": "Action/Adventure", "32": "action/adventure",
"33": "Classics", "33": "classics",
"34": "Comedy", "34": "comedy",
"35": "Documentary", "35": "documentary",
"36": "Drama", "36": "drama",
"37": "Family", "37": "family",
"38": "Foreign", "38": "foreign",
"39": "Horror", "39": "horror",
"40": "Sci-Fi/Fantasy", "40": "sci-fi/fantasy",
"41": "Thriller", "41": "thriller",
"42": "Shorts", "42": "shorts",
"43": "Shows", "43": "shows",
"44": "Trailers", "44": "trailers",
} }
func NewYoutubeVideo(directory string, videoData *youtube.Video, playlistPosition int64, awsConfig aws.Config) *YoutubeVideo { func NewYoutubeVideo(directory string, videoData *youtube.Video, playlistPosition int64, awsConfig aws.Config) *YoutubeVideo {
@ -319,19 +320,33 @@ func (v *YoutubeVideo) triggerThumbnailSave() (err error) {
return err return err
} }
func (v *YoutubeVideo) publish(daemon *jsonrpc.Client, claimAddress string, amount float64, namer *namer.Namer) (*SyncSummary, error) { func (v *YoutubeVideo) publish(daemon *jsonrpc.Client, params SyncParams) (*SyncSummary, error) {
languages, locations, tags := v.getMetadata() languages, locations, tags := v.getMetadata()
var fee *jsonrpc.Fee
if params.Fee != nil {
feeAmount, err := decimal.NewFromString(params.Fee.Amount)
if err != nil {
return nil, errors.Err(err)
}
fee = &jsonrpc.Fee{
FeeAddress: &params.Fee.Address,
FeeAmount: feeAmount,
FeeCurrency: jsonrpc.Currency(params.Fee.Currency),
}
}
options := jsonrpc.StreamCreateOptions{ options := jsonrpc.StreamCreateOptions{
ClaimCreateOptions: jsonrpc.ClaimCreateOptions{ ClaimCreateOptions: jsonrpc.ClaimCreateOptions{
Title: &v.title, Title: &v.title,
Description: util.PtrToString(v.getAbbrevDescription()), Description: util.PtrToString(v.getAbbrevDescription()),
ClaimAddress: &claimAddress, ClaimAddress: &params.ClaimAddress,
Languages: languages, Languages: languages,
ThumbnailURL: &v.thumbnailURL, ThumbnailURL: &v.thumbnailURL,
Tags: tags, Tags: tags,
Locations: locations, Locations: locations,
}, },
Fee: fee,
License: util.PtrToString("Copyrighted (contact publisher)"), License: util.PtrToString("Copyrighted (contact publisher)"),
ReleaseTime: util.PtrToInt64(v.publishedAt.Unix()), ReleaseTime: util.PtrToInt64(v.publishedAt.Unix()),
ChannelID: &v.lbryChannelID, ChannelID: &v.lbryChannelID,
@ -340,7 +355,7 @@ func (v *YoutubeVideo) publish(daemon *jsonrpc.Client, claimAddress string, amou
if err != nil { if err != nil {
return nil, err return nil, err
} }
return publishAndRetryExistingNames(daemon, v.title, downloadPath, amount, options, namer) return publishAndRetryExistingNames(daemon, v.title, downloadPath, params.Amount, options, params.Namer)
} }
func (v *YoutubeVideo) Size() *int64 { func (v *YoutubeVideo) Size() *int64 {
@ -354,6 +369,7 @@ type SyncParams struct {
MaxVideoSize int MaxVideoSize int
Namer *namer.Namer Namer *namer.Namer
MaxVideoLength float64 MaxVideoLength float64
Fee *sdk.Fee
} }
func (v *YoutubeVideo) Sync(daemon *jsonrpc.Client, params SyncParams, existingVideoData *sdk.SyncedVideo, reprocess bool) (*SyncSummary, error) { func (v *YoutubeVideo) Sync(daemon *jsonrpc.Client, params SyncParams, existingVideoData *sdk.SyncedVideo, reprocess bool) (*SyncSummary, error) {
@ -385,7 +401,7 @@ func (v *YoutubeVideo) downloadAndPublish(daemon *jsonrpc.Client, params SyncPar
} }
log.Debugln("Created thumbnail for " + v.id) log.Debugln("Created thumbnail for " + v.id)
summary, err := v.publish(daemon, params.ClaimAddress, params.Amount, params.Namer) summary, err := v.publish(daemon, params)
//delete the video in all cases (and ignore the error) //delete the video in all cases (and ignore the error)
_ = v.delete() _ = v.delete()
@ -455,21 +471,37 @@ func (v *YoutubeVideo) reprocess(daemon *jsonrpc.Client, params SyncParams, exis
return nil, errors.Err("the video must be republished as we can't get the right size") return nil, errors.Err("the video must be republished as we can't get the right size")
} }
} }
var fee *jsonrpc.Fee
if params.Fee != nil {
feeAmount, err := decimal.NewFromString(params.Fee.Amount)
if err != nil {
return nil, errors.Err(err)
}
fee = &jsonrpc.Fee{
FeeAddress: &params.Fee.Address,
FeeAmount: feeAmount,
FeeCurrency: jsonrpc.Currency(params.Fee.Currency),
}
}
streamCreateOptions := &jsonrpc.StreamCreateOptions{
ClaimCreateOptions: jsonrpc.ClaimCreateOptions{
Tags: tags,
ThumbnailURL: &thumbnailURL,
Languages: languages,
Locations: locations,
},
Author: util.PtrToString(""),
License: util.PtrToString("Copyrighted (contact publisher)"),
ChannelID: &v.lbryChannelID,
Height: util.PtrToUint(720),
Width: util.PtrToUint(1280),
Fee: fee,
}
if v.mocked { if v.mocked {
pr, err := daemon.StreamUpdate(existingVideoData.ClaimID, jsonrpc.StreamUpdateOptions{ pr, err := daemon.StreamUpdate(existingVideoData.ClaimID, jsonrpc.StreamUpdateOptions{
StreamCreateOptions: &jsonrpc.StreamCreateOptions{ StreamCreateOptions: streamCreateOptions,
ClaimCreateOptions: jsonrpc.ClaimCreateOptions{ FileSize: &videoSize,
Tags: tags,
ThumbnailURL: &thumbnailURL,
},
Author: util.PtrToString(""),
License: util.PtrToString("Copyrighted (contact publisher)"),
ChannelID: &v.lbryChannelID,
Height: util.PtrToUint(720),
Width: util.PtrToUint(1280),
},
FileSize: &videoSize,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -486,28 +518,16 @@ func (v *YoutubeVideo) reprocess(daemon *jsonrpc.Client, params SyncParams, exis
return nil, errors.Err(err) return nil, errors.Err(err)
} }
streamCreateOptions.ClaimCreateOptions.Title = &v.title
streamCreateOptions.ClaimCreateOptions.Description = util.PtrToString(v.getAbbrevDescription())
streamCreateOptions.Duration = util.PtrToUint64(uint64(math.Ceil(videoDuration.ToDuration().Seconds())))
streamCreateOptions.ReleaseTime = util.PtrToInt64(v.publishedAt.Unix())
pr, err := daemon.StreamUpdate(existingVideoData.ClaimID, jsonrpc.StreamUpdateOptions{ pr, err := daemon.StreamUpdate(existingVideoData.ClaimID, jsonrpc.StreamUpdateOptions{
ClearLanguages: util.PtrToBool(true), ClearLanguages: util.PtrToBool(true),
ClearLocations: util.PtrToBool(true), ClearLocations: util.PtrToBool(true),
ClearTags: util.PtrToBool(true), ClearTags: util.PtrToBool(true),
StreamCreateOptions: &jsonrpc.StreamCreateOptions{ StreamCreateOptions: streamCreateOptions,
ClaimCreateOptions: jsonrpc.ClaimCreateOptions{ FileSize: &videoSize,
Title: &v.title,
Description: util.PtrToString(v.getAbbrevDescription()),
Tags: tags,
Languages: languages,
Locations: locations,
ThumbnailURL: &thumbnailURL,
},
Author: util.PtrToString(""),
License: util.PtrToString("Copyrighted (contact publisher)"),
Height: util.PtrToUint(720),
Width: util.PtrToUint(1280),
ReleaseTime: util.PtrToInt64(v.publishedAt.Unix()),
Duration: util.PtrToUint64(uint64(math.Ceil(videoDuration.ToDuration().Seconds()))),
ChannelID: &v.lbryChannelID,
},
FileSize: &videoSize,
}) })
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -1,8 +1,11 @@
package tagsManager package tagsManager
import ( import (
"regexp"
"sort" "sort"
"strings" "strings"
log "github.com/sirupsen/logrus"
) )
const ( const (
@ -70,17 +73,23 @@ func SanitizeTags(tags []string, youtubeChannelID string) ([]string, error) {
func normalizeTag(t string) (string, error) { func normalizeTag(t string) (string, error) {
t = strings.ToLower(t) t = strings.ToLower(t)
multipleSpaces := regexp.MustCompile(`\s{2,}`)
leadingAndTrailingSpaces := regexp.MustCompile(`^\s+|\s$`)
hashTags := regexp.MustCompile(`(#\d+\s)|#+`)
inParenthesis := regexp.MustCompile(`\([^\)]+\)`)
weirdChars := regexp.MustCompile(`[^-\w'& +\/A-Za-zÀ-ÖØ-öø-ÿ]`)
startsOrEndsInWeirdChars := regexp.MustCompile(`^[^A-Za-zÀ-ÖØ-öø-ÿ0-9]+|[^A-Za-zÀ-ÖØ-öø-ÿ0-9]+$`)
t = hashTags.ReplaceAllString(t, "")
t = inParenthesis.ReplaceAllString(t, " ")
t = startsOrEndsInWeirdChars.ReplaceAllString(t, "")
t = multipleSpaces.ReplaceAllString(t, " ")
t = leadingAndTrailingSpaces.ReplaceAllString(t, "")
if weirdChars.MatchString(t) {
log.Debugf("tag '%s' has weird stuff in it, skipping\n", t)
return "", nil
}
return t, nil return t, nil
//r, err := regexp.Compile("/\\([^\\)]+\\)/g")
//if err != nil {
// return "", errors.Err(err)
//}
//r2, err := regexp.Compile("/[^\\w-'& \+]/g")
//if err != nil {
// return "", errors.Err(err)
//}
//t = r.ReplaceAllString(t, "")
//return r2.ReplaceAllString(t, ""), nil
} }
type tagsSanitizer struct { type tagsSanitizer struct {

View file

@ -1,6 +1,7 @@
package tagsManager package tagsManager
import ( import (
"fmt"
"testing" "testing"
) )
@ -44,3 +45,100 @@ outer:
} }
} }
func TestNormalizeTag(t *testing.T) {
tags := []string{
"blockchain",
"Switzerland",
"news ",
" science & Technology ",
"economics",
"experiments",
"this",
"in it",
"will build the (WOOPS)",
"~has",
"crypto",
"trump",
"wall",
"expensive",
"!currency",
" a lot of ",
"#",
"#whatever",
"#123",
"#123 Something else",
"#123aaa",
"!asdasd",
"CASA BLANCA",
"wwe 2k18 Elimination chamber!",
"pero'",
"però",
"è proprio",
"Ep 29",
"sctest29 Keddr",
"mortal kombat 11 shang tsung",
"!asdasd!",
}
normalizedTags := make([]string, 0, len(tags))
for _, tag := range tags {
got, err := normalizeTag(tag)
if err != nil {
t.Error(err)
return
}
if got != "" {
normalizedTags = append(normalizedTags, got)
}
fmt.Printf("Got tag: '%s'\n", got)
}
expected := []string{
"blockchain",
"switzerland",
"news",
"science & technology",
"economics",
"experiments",
"this",
"in it",
"will build the",
"has",
"crypto",
"trump",
"wall",
"expensive",
"currency",
"a lot of",
"whatever",
"123",
"something else",
"123aaa",
"asdasd",
"casa blanca",
"wwe 2k18 elimination chamber",
"pero",
"però",
"è proprio",
"ep 29",
"sctest29 keddr",
"mortal kombat 11 shang tsung",
"asdasd",
}
if !Equal(normalizedTags, expected) {
t.Error("result not as expected")
return
}
}
func Equal(a, b []string) bool {
if len(a) != len(b) {
fmt.Printf("expected length %d but got %d", len(b), len(a))
return false
}
for i, v := range a {
if v != b[i] {
fmt.Printf("expected %s but bot %s\n", b[i], v)
return false
}
}
return true
}