Skip to content

Commit

Permalink
youtube: Add alternative non-clickbait titles powered by DeArrow
Browse files Browse the repository at this point in the history
  • Loading branch information
Brawl345 committed Jan 3, 2024
1 parent 19b3c15 commit d98f405
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 2 deletions.
39 changes: 39 additions & 0 deletions plugin/youtube/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ type (
Blocked []string `json:"blocked"`
} `json:"regionRestriction"`
}

DeArrowResponse struct {
Titles []struct {
Title string `json:"title"`
Original bool `json:"original"`
Votes int `json:"votes"`
Locked bool `json:"locked"`
} `json:"titles"`
}
)

func (v *Video) BlockedInGermany() bool {
Expand Down Expand Up @@ -91,3 +100,33 @@ func (v *Video) WasLive() bool {
func (c *ContentDetails) ParseDuration() (*duration.Duration, error) {
return duration.Parse(c.Duration)
}

func (d *DeArrowResponse) GetBestTitle() string {
// From the API docs:
// "Data is returned ordered. You can use the first element. However, you should make sure the first element
// has either locked = true or votes >= 0. If not, it is considered untrusted and is only to be shown
// in the voting box until it has been confirmed by another user."

if len(d.Titles) == 0 {
return ""
}

if d.Titles[0].Locked && !d.Titles[0].Original {
return d.Titles[0].Title
}

// Will check for the highest number of votes instead of just taking the first one.
// API docs say that you should not use titles without votes but the browser extension does it anyway.
// So we will do it too!
maxVotes := -1
var title string
for _, t := range d.Titles {
t := t
if t.Votes > maxVotes && !t.Original {
maxVotes = t.Votes
title = t.Title
}
}

return title
}
51 changes: 50 additions & 1 deletion plugin/youtube/youtube.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,42 @@ func (p *Plugin) getVideoInfo(videoID string) (Video, error) {
return response.Items[0], nil
}

func deArrow(originalText string, video *Video) (string, error) {
// https://wiki.sponsor.ajay.app/w/API_Docs/DeArrow#GET_/api/branding
deArrowUrl := fmt.Sprintf("https://sponsor.ajay.app/api/branding/?videoID=%s", video.ID)
var deArrowResponse DeArrowResponse
var httpError *httpUtils.HttpError
err := httpUtils.GetRequest(
deArrowUrl,
&deArrowResponse,
)

if err != nil {
if errors.As(err, &httpError) {
if httpError.StatusCode == 500 { // API seems to throw 500 for every empty response
return "", nil
}
}
return "", err
}

alternativeTitle := deArrowResponse.GetBestTitle()
if alternativeTitle != "" {
modifiedText := strings.Replace(
originalText,
fmt.Sprintf("<b>%s</b>\n", utils.Escape(video.Snippet.Title)),
fmt.Sprintf("<b>%s</b>\n<i>Alternativer Titel: <b>%s</b>\n</i>",
utils.Escape(video.Snippet.Title),
utils.Escape(alternativeTitle),
),
1,
)
return modifiedText, nil
}

return "", nil
}

func constructText(video *Video) string {
var sb strings.Builder

Expand Down Expand Up @@ -276,7 +312,20 @@ func (p *Plugin) OnYouTubeLink(c plugin.GobotContext) error {

text := constructText(&video)

return c.Reply(text, utils.DefaultSendOptions)
msg, err := c.Bot().Reply(c.Message(), text, utils.DefaultSendOptions)
if err == nil {
modifiedText, err := deArrow(text, &video)
if err != nil {
log.Err(err).
Str("videoID", videoID).
Msg("Error while contacting DeArrow API")
return nil
}

_, err = c.Bot().Edit(msg, modifiedText, utils.DefaultSendOptions)
}

return err
}

func (p *Plugin) onYouTubeSearch(c plugin.GobotContext) error {
Expand Down
6 changes: 5 additions & 1 deletion plugin/youtube/youtube.http
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@ GET https://www.googleapis.com/youtube/v3/search
&part=snippet
&maxResults=1
&type=video
&fields=items/id/videoId
&fields=items/id/videoId

### DeArrow
GET https://sponsor.ajay.app/api/branding/
?videoID=7DKv5H5Frt0

0 comments on commit d98f405

Please sign in to comment.