govidious

package module
v0.0.0-...-6bad929 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 12, 2023 License: GPL-3.0 Imports: 6 Imported by: 1

README

Description

This is a Go module (ie. "library") that provides a wrapper over the Invidious REST API v1.

WARNING: WHILE THIS BANNER IS PRESENT, THIS REPOSITORY DOES NOT IMPLEMENT THE WHOLE INVIDIOUS API AND YOU PROBABLY WANT TO WAIT BEFORE START USING IT.

It lets you access the Invidious API directly from Go to search for videos, get their URL, etc... like this:

import "git.sr.ht/~greenfoo/govidious"           // <--- Tell Go you are going to use this module
...
g := govidious.New(INVIDIOUS_SERVER, nil)        // <--- Specify the URL of an Invidious server
queryResults, _ := g.Search("Rick Asley")        // <--- Query the Invidious server

for _, videoEntry := range queryResults.Videos { // <--- Process the results
    fmt.Println(videoEntry.Title)
}

If you don't know what an "Invidious server" is, jump to the latest section of this README, called "About Invidious". Then come back and keep reading from here.

Documentation

For a more detailed package/API description, check the module embedded documentation, which you can obtain in two ways:

  1. You can print it to stdout by running this command:

    $ go doc -all
    
  2. ...Or you can serve it in HTML form in "http://localhost:6060" by running this other command:

    $ godoc
    

There is also a "example_test.go" file that shows how some of the APIs are meant to be used.

License

This is GPLv3 software. Check file "LICENSE" inside this same folder.

Hacking

If you want to contribute to this repository, please make sure you run all the tests before sending your patch, like this:

$ go test -server <URL of Indivious server>

...where <URL of Indivious server> is the IP/URL of an Indivious server that has been configured to expose the REST API.

Note that, depending on several items that will be discussed next, not all tests are required to end successfully. The rule is that the same number of tests must end successfully before and after applying your patch.

Things you have to be aware of:

  • Problems with public servers:
    • Not all public servers are configured to expose the REST API.
    • Public servers configured to expose the REST API might not expose all the expected APIs (for example, the "Stats" endpoint is usually disabled)
    • Public servers tend to be rate limited, thus running the whole test suit at once might fail. In that case, try running each individual test function by adding the -run <function_name> option.
  • Because of all the previous items, it is recommended to use your own (private) Invidious server instead of a public one (see section "Running your own Invidious instance" at the end of this README to find out how to do it).
  • The YouTube HTML code (which Invidious parses) changes very frequently. This means any of the Invidious REST endpoints might break for a few days (until Invidious developers fix it) at any point in time.
  • When one API fails, add the -verbose option to better understand what the problem might be.

About Invidious

What is Invidious?

"Invidious", in case you don't already know, is an alternative front-end to YouTube. In other words: you can watch YouTube videos by either going directly to "www.youtube.com" or to any of the existing "Invidious" instances deployed all over the world (see here)

Invidious is an open source project (hosted here), so you can (and, in fact, are encouraged) to run it on your own server.

Why would I want to access an Invidious server instead of the original YouTube server?

Several reasons:

  1. Avoid the (direct) Google tracking that takes place every time you visit a YouTube page.

    Note: On the most simple configuration, Invidious simply removes the Javascript tracking and gives you a direct link to the (YouTube) hosted video, thus Google will still be able to know which videos are being served to your IP. However, it is also true that Invidious can be configured to proxy the videos itself, so that you never interact with Google/YouTube servers, which is great... but remember this needs to be explicitely configured.

  2. No need to sign in to watch age restricted videos.

  3. Depending on where the Invidious instance is located, you will also be able to watch geo-restricted videos.

  4. Invidious servers can be configured to expose a public REST API, which is something that requires an account in the case of YouTube servers. This makes it possible to create tools that communicate with the Invidious server to "navigate" the YouTube database (search for videos, check their comments, etc...)

You can see what others think about Invidious here: https://news.ycombinator.com/item?id=21535562

Running your own Invidious instance

Why is it "better" to run my own instance instead of using a public one? Several reasons:

  1. Avoid centralization. The more instances there are, the harder they are to censor.

  2. Privacy. Maybe you don't (and, in fact, you shouldn't!) trust public instances (they might, for example, log your queries).

This doesn't mean it is "bad" to use public instances. It only means that it is "better" to run your own.

Note, however, that the IP that your instance uses might become "blocked" by YouTube if it is abused (for example, if you publish the IP of your instance and many people start using it).

In order to run your own instance follow these instructions

From all the alternatives described in the previous link, using docker is the easiest one:

git clone https://github.com/iv-org/invidious.git
cd invidious
docker-compose up  

...and the instance will be ready on http://localhost:3000 (for either direclty accessing it from your webbrowser or using it as the REST API URL)

Notice that before running "docker-compose up" you might want to edit file "docker-compose.yml", in particular section "INVIDIOUS_CONFIG" where you can add/change any of the configuration parameters described here.

Documentation

Overview

Package govidious implements a wrapper over the Invidious REST API v1, which is described in these documents:

For each of the endpoints listed in those links, this module provides an API function which:

  1. Takes as many input arguments as needed depending on the endpoint. For example, the "/api/v1/stat" endpoint is represented by the "Stat()" API function which takes no arguments, while the "/api/v1/search" endpoint is represented by the "Search()" API function which takes many arguments (search query, date, duration, ...).

  2. Returns a structure that matches the JSON response associated to each endpoint.

In order to use this package first create a new "InvidiousV1" instance using the "New()" function:

import "git.sr.ht/~greenfoo/govidious"
g := govidious.New("invidious.myserver.com", nil)

...then, simply call, on that object, the desired API function(s):

g.Search(...)
g.Top(...)
g.Videos(...)

For example, to get the URL of the most watched video featuring "kittens playing golf", you would do this:

response, _ := g.Search("kittens playing golf", 0, "view_count", "", "", "video", "", "")
videoId := response.Videos[0].VideoId
youtubeUrl := "https://www.youtube.com/watch?v="+videoId
invidiousUrl := INVIDIOUS_SERVER + "/watch?v="+videoId

Notice that both URLs ("youtubeUrl" and "invidiousUrl") can be used on a web browser to actually watch the video. The main difference is that:

  • Using "youtubeUrl" establishes a direct connection with YouTube servers.

  • Using "invidiousUrl" might or might not establish a direct connection with YouTube servers, depending on how the Invidious server is configured. If you don't want to leave any trace of YouTube servers, make sure you use "InvidiousUrl" on an Invidious server configured to proxy video files.

Another thing you can do with this URL ("youtubeUrl" or "invidiousUrl") is to use the "mpv" player to start watching the video directly, without needing a web browser. Under the hood, "mpv" uses "youtube-dl" (another program) to parse the web page, find the URL of the video file (which might change depending on the options you call "mpv" with, such as the desired quality) and download it.

Well... the good news is that you don't need "mpv" nor "youtube-dl" to do all of this as the Invidious API can also be used to find out the URL of the video file once you have the "videoId".

First, you obtain all the associated data of the desired video:

response, _ = g.Videos(videoId, "")

...then you inspect the returned structure to find the URL of the video with the desired quality level:

for _, x := range response.AdaptiveFormats {
    if x.Resolution == ... {
        return x.Url
    }
}
for _, x := range response.FormatStreams {
    if x.Resolution == ... {
        return x.Url
    }
}

There are many other things you can do with this package, such as listing all the videos from a given channel, retrieving the subtitles associated to a video, etc... Check the documentation of each function (and the name of the fields of the structures they return) for more details.

Example (Basic)
package main

import (
	"fmt"

	"git.sr.ht/~greenfoo/govidious"
)

const INVIDIOUS_SERVER = "https://invidious.snopyta.org"

func main() {

	// This is how you print the title of all Rick Asley videos:

	g := govidious.New(INVIDIOUS_SERVER, nil)
	queryResults, _ := g.Search("Rick Asley", 0, "view_count", "", "", "video", "", "")

	for _, videoEntry := range queryResults.Videos {
		fmt.Println(videoEntry.Title)
	}
}
Output:

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrInvidiousClientError           = errors.New("HTTP client returned an error")
	ErrInvidiousServerError           = errors.New("HTTP server returned an error")
	ErrInvidiousServerInvalidResponse = errors.New("HTTP server returned invalid data")
)

Errors that this API might return

Functions

This section is empty.

Types

type AnnotationsResponse

type AnnotationsResponse struct {
	XmlData string
}

AnnotationsResponse is the structure returned when calling "Annotations()". Notice how this structure is different from the rest in that it contains a big blob of XML data (instead of a more structured tree of fields) that you will need to parse yourself.

type CaptionsResponse

type CaptionsResponse struct {
	Captions []struct {
		Label        string `json:"label"`
		LanguageCode string `json:"languageCode"`
		Url          string `json:"url"`
	} `json:"captions"`
}

CaptionsResponse is the structure returned when calling "Captions()"

type ChannelsCommentsResponse

type ChannelsCommentsResponse struct {
	AuthorId string `json:"authorId"`

	Comments []struct {
		Author string `json:"author"`

		AuthorThumbnails []struct {
			Url    string `json:"url"`
			Width  int32  `json:"width"`
			Height int32  `json:"height"`
		} `json:"authorThumbnails"`

		AuthorId             string `json:"authorId"`
		AuthorUrl            string `json:"authorUrl"`
		IsEdited             bool   `json:"isEdited"`
		Content              string `json:"content"`
		ContentHtml          string `json:"contentHtml"`
		Published            int64  `json:"published"`
		PublishedText        string `json:"publishedText"`
		LikeCount            int32  `json:"likeCount"`
		CommentId            string `json:"commentId"`
		AuthorIsChannelOwner bool   `json:"authorIsChannelOwner"`

		CreatorHeart struct {
			CreatorThumbnail string `json:"creatorThumbnail"`
			CreatorName      string `json:"CreatorName"`
		} `json:"creatorHeart"`

		Replies struct {
			ReplyCount   int32  `json:"replyCount"`
			Continuation string `json:"Continuation"`
		} `json:"replies"`

		Attachment struct{} `json:"attachment"`
	} `json:"comments"`

	Continuation string `json:"Continuation"`
}

ChannelsCommentsResponse is the structure returned when calling "ChannelsComments()"

TODO: Support for different types of attachements

type ChannelsLatestResponse

type ChannelsLatestResponse ChannelsVideosResponse

ChannelsLatestResponse is the structure returned when calling "ChannelsLatest()"

type ChannelsPlaylistsResponse

type ChannelsPlaylistsResponse struct {
	Playlists []struct {
		Title      string `json:"title"`
		PlaylistId string `json:"playlistId"`
		Author     string `json:"author"`
		AuthorId   string `json:"authorId"`
		AuthorUrl  string `json:"authorUrl"`
		VideoCount int32  `json:"videoCount"`

		Videos []struct {
			Title         string `json:"title"`
			VideoId       string `json:"videoId"`
			LengthSeconds int32  `json:"lengthSeconds"`

			VideoThumbnails []struct {
				Quality string `json:"quality"`
				Url     string `json:"url"`
				Width   int32  `json:"width"`
				Height  int32  `json:"height"`
			} `json:"videoThumbnails"`
		} `json:"videos"`
	} `json:"playlists"`

	Continuation string `json:"continuation"`
}

ChannelsPlaylistsResponse is the structure returned when calling "ChannelsPlaylists()"

type ChannelsResponse

type ChannelsResponse struct {
	Author    string `json:"author"`
	AuthorId  string `json:"authorId"`
	AuthorUrl string `json:"authorUrl"`

	AuthorThumbnails []struct {
		Url    string `json:"url"`
		Width  int32  `json:"width"`
		Height int32  `json:"height"`
	} `json:"authorThumbnails"`

	SubCount   int32 `json:"subCount"`
	TotalViews int64 `json:"totalViews"`
	Joined     int64 `json:"joined"`

	Paid             bool     `json:"paid"`
	AutoGenerated    bool     `json:"autoGenerated"`
	IsFamilyFriendly bool     `json:"insFamilyFriendly"`
	Description      string   `json:"description"`
	DescriptionHtml  string   `json:"descriptionHtml"`
	AllowedRegions   []string `json:"string"`

	LatestVideos []struct {
		Title     string `json:"title"`
		VideoId   string `json:"videoId"`
		Author    string `json:"author"`
		AuthorId  string `json:"authorId"`
		AuthorUrl string `json:"authorUrl"`

		VideoThumbnails []struct {
			Quality string `json:"quality"`
			Url     string `json:"url"`
			Width   int32  `json:"width"`
			Height  int32  `json:"height"`
		} `json:"videoThumbnails"`

		Description     string `json:"description"`
		DescriptionHtml string `json:"descriptionHtml"`
		ViewCount       int64  `json:"viewCount"`
		Published       int64  `json:"published"`
		PublishedText   string `json:"publishedText"`
		LengthSeconds   int32  `json:"lengthSeconds"`
		Paid            bool   `json:"paid"`
		Premium         bool   `json:"premium"`
	} `json:"latestVideos"`

	RelatedChannels []struct {
		Author    string `json:"author"`
		AuthorId  string `json:"authorId"`
		AuthorUrl string `json:"authorUrl"`

		AuthorThumbnails struct {
			Url    string `json:"url"`
			Width  int32  `json:"width"`
			Height int32  `json:"height"`
		} `json:"authorThumbnails"`
	}
}

ChannelsResponse is the structure returned when calling "Channels()"

type ChannelsSearchResponse

type ChannelsSearchResponse struct {
	Videos    []SearchResponseTypeVideo
	Playlists []SearchResponseTypePlaylist
	Channels  []SearchResponseTypeChannel
}

ChannelsSearchResponse is the structure returned when calling "ChannelsSearch()"

Notice how this structure does *not* match the original JSON structure defined in the Inviduous REST API document.

The original JSON structure is a list of dictionaries of different types (some elements of the list represent a video, others a playlist and others a channel).

The SearchResponse structure, instead, is made up of three homogeneous lists, one for each type of entry.

type ChannelsVideosResponse

type ChannelsVideosResponse struct {
	Videos       []VideosResponse `json:"videos"`
	Continuation string           `json:"continuation"`
}

ChannelsVideosResponse is the structure returned when calling "ChannelsVideos()"

type CommentsResponse

type CommentsResponse struct {
	CommentCount int32  `json:"commentCount"`
	VideoId      string `json:"videoId"`

	Comments []struct {
		Author string `json:"author"`

		AuthorThumbnails []struct {
			Url    string `json:"url"`
			Width  int32  `json:"width"`
			Height int32  `json:"height"`
		} `json:"authorThumbnails"`

		AuthorId             string `json:"authorId"`
		AuthorUrl            string `json:"authorUrl"`
		IsEdited             bool   `json:"isEdited"`
		Content              string `json:"content"`
		ContentHtml          string `json:"contentHtml"`
		Published            int64  `json:"published"`
		PublishedText        string `json:"publishedText"`
		LikeCount            int32  `json:"likeCount"`
		CommentId            string `json:"commentId"`
		AuthorIsChannelOwner bool   `json:"authorIsChannelOwner"`

		CreatorHeart struct {
			CreatorThumbnail string `json:"creatorThumbnail"`
			CreatorName      string `json:"CreatorName"`
		} `json:"creatorHeart"`

		Replies struct {
			ReplyCount   int32  `json:"replyCount"`
			Continuation string `json:"Continuation"`
		} `json:"replies"`
	} `json:"comments"`

	Continuation string `json:"Continuation"`
}

CommentsResponse is the structure returned when calling "Comments()"

type InsightsResponse

type InsightsResponse struct {
	ViewCount              int64    `json:"viewCount"`
	TimeWatchedText        string   `json:"timeWatchedText"`
	SubscriptionsDriven    int32    `json:"subscriptionsDriven"`
	Shares                 int32    `json:"shares"`
	AvgViewDurationSeconds int32    `json:"avgViewDurationSeconds"`
	GraphData              struct{} `json:"graphData"`
}

InsightsResponse is the structure returned when calling "Insights()"

type InvidiousV1

type InvidiousV1 struct {
	// contains filtered or unexported fields
}

InvidiousV1 is the structure returned when calling "New()". You later use it to call the rest of API functions

func New

func New(serverUrl string, lg *log.Logger) *InvidiousV1

New creates an Invidious object, which is later used to call the rest of API functions. This is always the first function of this API you have to call.

The first argument ("serverUrl") is the URL of the Invidious server, which will typically be your own server. However, you can also use one of the many public instances, some of which are listed here: https://github.com/iv-org/documentation/blob/master/Invidious-Instances.md

Note that the server *must* be configured to expose the REST API (and *not* all the server from the previous list do that).

The second argument ("lg") is a "log" object that this module will use to print log messages. If you set this to "nil", log messages will not be printed.

Example #1 (no logging):

i := New("invidious.myservername.com", nil)
i.Search(...)
i.Videos(...)

Example #2 (logging to stderr):

i := New("invidious.myservername.com", log.New(os.Stderr, "GOVIDIOUS", log.Ltime | log.Lshortfile))
i.Search(...)
i.Videos(...)

func (*InvidiousV1) Annotations

func (i *InvidiousV1) Annotations(videoId string, source string) (AnnotationsResponse, error)

Annotations returns an XML blob with all the annotations the video contains ("annotations" are pieces of text intended to appear on top of the video in the form of a pop up to bring attention to something).

The first argument ("videoId") is the same one "Videos()" takes.

The second argument ("source") can be either "archive" or "youtube", and it tells where to retrieve the annotations from (either from "https://archive.org/details/youtubeannotations" or from YouTube itself). It looks like YouTube removed annotations some time ago (2018?): you can no longer create them and they have been removed from all past videos (!!). Fortunately, a copy was saved to "archive.org". In other words: you want to always use "archive" as the second argument to this function.

This function does not parse the returned XML blob. Fortunately it is not that hard to understand so you can do it yourself.

func (*InvidiousV1) CaptionByLabel

func (i *InvidiousV1) CaptionByLabel(videoId, region, label string) (string, error)

CaptionByLabel returns the close captions of a video in webVTT format.

The first argument ("videoId") is the same one "Videos()" takes.

The second argument ("region") is the same one "Videos()" takes.

The third argument ("label") is a string describing the language of the close captions that you want to retrieve. In order to find out what the valid values for "label" are for a particular video, call first "Captions()", which returns an array where each entry is tagged with a particular "label" such as, for example, "Spanish".

The returned text contains all the captions in the requested language in webVTT format (see https://en.wikipedia.org/wiki/WebVTT)

func (*InvidiousV1) CaptionByLanguageCode

func (i *InvidiousV1) CaptionByLanguageCode(videoId, region, lang string, translationAllowed bool) (string, error)

CaptionByLanguageCode returns the close captions of a video in webVTT format.

The first argument ("videoId") is the same one "Videos()" takes.

The second argument ("region") is the same one "Videos()" takes.

The third argument ("lang") is a two lowercase characters ISO 639-1 language code (you can check them here, on the column called "639-1": https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes). The returned text will be in that language (if available) or...

When the fourth argument ("translationAllowed") is set to "false", this function will fail when the video does not contain close captions in the requested language. In order to obtain the list of available languages for a particular video, call first "Captions()", which returns an array where each entry is tagged with a particualr "languageCode" such as, for example, "es" When set to "true", this function will ask the server to try to auto-generate a translation in the requested language before failing (which can still happen for whatever reason).

The returned text contains all the captions in the requested language in webVTT format (see https://en.wikipedia.org/wiki/WebVTT)

func (*InvidiousV1) Captions

func (i *InvidiousV1) Captions(videoId, region string) (CaptionsResponse, error)

Captions returns the list of languages for which close captions for the specified video are available.

The first argument ("videoId") is the same one "Videos()" takes.

The second argument ("region") is the same one "Videos()" takes.

The returned "CaptionsResponse" structure will contain an array ("captions") -which might be empty- with one entry for each language-specific close caption available. From all the returned entries you are expected to select one, take note of either its "label" (ex: "Spanish") or its "languageCode" (ex: "es") and then call either "CaptionByLabel()" or "CaptionByLanguageCode()" to, finally, obtain the close captions.

For example:

r, err := g.Captions("Q8Sq9r50gc0", "US")
...(make sure "Spanish"/"es" is returned inside "r")...
t, err := g.CaptionByLabel("Q8Sq9r50gc0", "US", "Spanish")
...or...
t, err := g.CaptionByLanguageCode("Q8Sq9r50gc0", "US", "es", false)

func (*InvidiousV1) Channels

func (i *InvidiousV1) Channels(authorId string) (ChannelsResponse, error)

Channels returns, despite of its name, information about *one* particular channel such as a text description, the author name, list of latest videos on the channel (*not* the list of *all* videos, for that you need to call "ChannelsVideos()"), ...

The first argument ("authorId") is a string used to identify the owner of the channel. It can be obtained from several other API calls such as "Search()", "Popular()", "Top()", ... Note that the "authorId" string obtained in the response to those other API functions looks like random (ex: "UCSMOQeBJ2RAnuFungnQOxLg" for the "Blender" channel). HOWEVER it looks like you can also use, instead, another human readable identifier (such as "BlenderFoundation") to obtain the same result. I haven't been able to find where to obtain this alternative human redable identifier, though... I'll update this comment once I do.

func (*InvidiousV1) ChannelsComments

func (i *InvidiousV1) ChannelsComments(authorId, continuation string) (ChannelsCommentsResponse, error)

ChannelsComments returns the list of entries published by a channel author on the channel's "community" section.

The first argument ("authorId") is the same one "Channels()" takes.

The second argument ("continuation") is the same one "Comments()" takes.

func (*InvidiousV1) ChannelsLatest

func (i *InvidiousV1) ChannelsLatest(authorId string) (ChannelsLatestResponse, error)

ChannelsLatest returns a list of the latest videos from a channel. The returned list is the same as the one you would get by calling "ChannelsVideos(..., 1, "newer")" except it contains less entries.

The first argument ("authorId") is the same one "Channels()" takes.

func (*InvidiousV1) ChannelsPlaylists

func (i *InvidiousV1) ChannelsPlaylists(authorId string, sortBy, continuation string) (ChannelsPlaylistsResponse, error)

ChannelsPlaylists returns a list of the playlists associated to a channel.

The first argument ("authorId") is the same one "Channels()" takes.

The second argument ("sort_by") can be set to one of these: "newest" (meaning "first the playlist most recently created"), "oldest" (which means the opposite) or "last" (which means "first the playlist where a new video was most recenty added). You can also leave it empty (""), which means "last".

The third argument ("continuation") is the same one "Comments()" takes.

func (*InvidiousV1) ChannelsSearch

func (i *InvidiousV1) ChannelsSearch(videoId string) (ChannelsSearchResponse, error)

func (*InvidiousV1) ChannelsVideos

func (i *InvidiousV1) ChannelsVideos(authorId string, sortBy string) (ChannelsVideosResponse, error)

ChannelsVideos returns a list with all the videos from a channel.

The first argument ("authorId") is the same one "Channels()" takes.

The third argument ("sort_by") is the same one "Channels()" takes.

func (*InvidiousV1) Comments

func (i *InvidiousV1) Comments(videoId, sortBy, source, continuation string) (CommentsResponse, error)

Comments returns all the comments associated to a video.

The first argument ("videoId") is the same one "Videos()" takes.

The second argument ("sortBy") can be either "top" or "new" to sort the comments by most voted of most recently posted respectively.

The third argument ("source") can be either "youtube" or "reddit" to obtain the comments from YoutTube itself or from "http://reddit.com/r/FellowKids" respectively.

TODO: When using "reddit", the returned json object from the Indivious
server does not match the (expected) structure defined by the
"CommentsResponse" type. Because of this, when setting "source" to
"reddit", this functions returns an empty object.
This is something that will be fixed in the future (maybe creating one
function for each "source" or maybe making this function return one extra
structure that will be the one filled in the "reddit" case).

The fourth argument ("continuation") is used to retrieve a given "page" of comments. This is how it works: The first time, call this functions with "continuation" set to an empty string and you will receive a "CommentsResponse" object with a "continuation" field. If you then want to retrive more comments from that same video, you need to call this function once again, with the same arguments, except for "continuation", which must now be set to that value obtained in the previous response... and so on until the returned object contains an empty "continuation" field.

func (*InvidiousV1) IdToUrl

func (i *InvidiousV1) IdToUrl(videoId string) (string, error)

func (*InvidiousV1) Insights

func (i *InvidiousV1) Insights(videoId string) (InsightsResponse, error)

Insights returns analytics information about one particular video. Unfortunately YouTube has removed publicly-available analytics and this function will always return an error (ErrInvidiousServerInvalidResponse)

The first argument ("videoId") is the same one "Videos()" takes.

func (*InvidiousV1) Mixes

func (i *InvidiousV1) Mixes(videoId string) (MixesResponse, error)

func (*InvidiousV1) Playlists

func (i *InvidiousV1) Playlists(playlistId string) (PlaylistsResponse, error)

func (*InvidiousV1) Popular

func (i *InvidiousV1) Popular() (PopularResponse, error)

Trending returns a list of "popular" videos.

It is not clear what the difference betwen "trending", "top" and "popular" videos is (that is up to YouTube to decide).

func (*InvidiousV1) Search

func (i *InvidiousV1) Search(query string, page int32, sort_by, date, duration, category, features, region string) (SearchResponse, error)

Search returns a list of videos, playlists and/or channels that match the provided arguments.

In particular, the returned "SearchResponse" type contains three lists (one for videos, one for playlists and one for channels). Any (or even all) of them can be empty.

The first argument ("query") is the piece of text you want to search for (ex: "typing monkey")

The second argument ("page") is the page of results that you want to get. You typically want to start with "1" and keep increasing "2", "3", ... There doesn't seem to be a practical limit for this argument.

The third argument ("sort_by") can be set to one of these: "relevance", "rating", "upload_date" or "view_count". You can also leave it empty (""), which seems to have a similar effect to "relevance" (but results are not exactly the same!)

The fourth argument ("date") can be set to "hour", "today", "week", "month" or "year" to limit the returned results to only entries published in the last hour, today, last week, last month or last year respectively. You can also leave it empty (""), which means "I want results from all times".

The fifth argument ("duration") can be set to "short" or "long". You can also leave it empty (""), which means "any length". "Short" videos seem to be shorter than 5 minutes, while "long" videos seems to be longer than 20 minutes, but don't quote me on that :)

The sixth argument ("category") can be set to "video", "playlist", "channel" or "all". If you leave it empty ("") it has the same effect as "video". When you use something different from "all", the returned "SearchResponse" structure will contain two empty lists (ex: if you set "category" to "video", then the returned "playlist" and "channel" lists will be empty).

The seventh argument ("features") can be set to a comma separated list (no spaces) of one or more of the following elements: "hd", "subtitles", "creative_commons", "3d", "live", "purchased", "4k", "360", "location", "hdr". It can also be left empty (""), which means "no special features required".

The eighth argument ("region") is the same one "Videos()" takes.

func (*InvidiousV1) SearchSuggestions

func (i *InvidiousV1) SearchSuggestions(videoId string) (SearchSuggestionsResponse, error)

func (*InvidiousV1) Stats

func (i *InvidiousV1) Stats() (StatsResponse, error)

Stats returns information about the Invidious server you are connected to, such as the server version, number of users, etc...

func (*InvidiousV1) Top

func (i *InvidiousV1) Top() (TopResponse, error)

Trending returns a list of "top" videos.

It is not clear what the difference betwen "trending", "top" and "popular" videos is (that is up to YouTube to decide).

*DEPRECATION WARNING*: The "top" feed was removed from Invidious implementation in late 2020, thus you will probably receive an error when calling this function. See [here](https://github.com/iv-org/invidious/pull/1592)

func (*InvidiousV1) Trending

func (i *InvidiousV1) Trending(category, region string) (TrendingResponse, error)

Trending returns a list of currently "trending" videos.

It is not clear what the difference betwen "trending", "top" and "popular" videos is (that is up to YouTube to decide).

The first argument ("category") can be one of "music", "gaming", "news" or "movies". It can also be an empty string (""), which means you want the list of trending videos "overall" (without considering their category).

The second argument ("region") is the same one "Videos()" takes.

func (*InvidiousV1) Videos

func (i *InvidiousV1) Videos(videoId, region string) (VideosResponse, error)

Videos returns, despite of its name, information about *one* particular video such as a text description, the author name, publication date, etc...

The first argument ("videoId") is a string used to identify one particular video from all those available in Invidious. It that can be obtained from several other API calls such as "Search()", "Popular()", "Top()", ...

The second argument ("region") is a two uppercase characters ISO 3166 country code (you can check them here, on the column called "Alpha-2 code": https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes). The contents of the response will be localized to the provided country code when available. If you leave this argument empty it will be set to "US".

type MixesResponse

type MixesResponse struct {
	Title string `json:"title"`
	MixId string `json:"mixId"`

	Videos []struct {
		Title     string `json:"title"`
		VideoId   string `json:"videoId"`
		Author    string `json:"author"`
		AuthorId  string `json:"authorId"`
		AuthorUrl string `json:"authorUrl"`

		VideoThumbnails []struct {
			Quality string `json:"quality"`
			Url     string `json:"url"`
			Width   int32  `json:"width"`
			Height  int32  `json:"height"`
		} `json:"videoThumbnails"`

		Index         string `json:"index"`
		LengthSeconds int32  `json:"lengthSeconds"`
	} `json:"videos"`
}

MixesResponse is the structure returned when calling "Mixes()"

type PlaylistsResponse

type PlaylistsResponse struct {
	Title      string `json:"title"`
	PlaylistId string `json:"playlistId"`

	Author   string `json:"author"`
	AuthorId string `json:"authorId"`

	AuthorThumbnails struct {
		Url    string `json:"url"`
		Width  int32  `json:"width"`
		Height int32  `json:"height"`
	} `json:"authorThumbnails"`

	Description     string `json:"description"`
	DescriptionHtml string `json:"descriptionHtml"`

	VideoCount int32 `json:"videoCount"`
	ViewCount  int64 `json:"viewCount"`

	Updated int64 `json:"updated"`

	Videos []struct {
		Title     string `json:"title"`
		VideoId   string `json:"videoId"`
		Author    string `json:"author"`
		AuthorId  string `json:"authorId"`
		AuthorUrl string `json:"authorUrl"`

		VideoThumbnails []struct {
			Quality string `json:"quality"`
			Url     string `json:"url"`
			Width   int32  `json:"width"`
			Height  int32  `json:"height"`
		} `json:"videoThumbnails"`

		Index         string `json:"index"`
		LengthSeconds int32  `json:"lengthSeconds"`
	} `json:"videos"`
}

PlaylistsResponse is the structure returned when calling "Playlists()"

type PopularResponse

type PopularResponse []struct {
	Type    string `json:"type"`
	Title   string `json:"title"`
	VideoId string `json:"videoId"`

	VideoThumbnails []struct {
		Quality string `json:"quality"`
		Url     string `json:"url"`
		Width   int32  `json:"width"`
		Height  int32  `json:"height"`
	} `json:"videoThumbnails"`

	LengthSeconds int32 `json:"lengthSeconds"`
	ViewCount     int64 `json:"viewCount"`

	Author    string `json:"author"`
	AuthorId  string `json:"authorId"`
	AuthorUrl string `json:"authorUrl"`

	Published     int64  `json:"published"`
	PublishedText string `json:"publishedText"`
}

PopularResponse is the structure returned when calling "Popular()"

type SearchResponse

type SearchResponse struct {
	Videos    []SearchResponseTypeVideo
	Playlists []SearchResponseTypePlaylist
	Channels  []SearchResponseTypeChannel
}

SearchResponse is the structure returned when calling "Search()".

Notice how this structure does *not* match the original JSON structure defined in the Inviduous REST API document.

The original JSON structure is a list of dictionaries of different types (some elements of the list represent a video, others a playlist and others a channel).

The SearchResponse structure, instead, is made up of three homogeneous lists, one for each type of entry.

type SearchResponseTypeChannel

type SearchResponseTypeChannel struct {
	Type      string `json:"type"`
	Author    string `json:"author"`
	AuthorId  string `json:"authorId"`
	AuthorUrl string `json:"authorUrl"`

	AuthorThumbnails []struct {
		Url    string `json:"url"`
		Width  int32  `json:"width"`
		Height int32  `json:"height"`
	}

	SubCount        int32  `json:"subCount"`
	VideoCount      int32  `json:"videoCount"`
	Description     string `json:"description"`
	DescriptionHtml string `json:"descriptionHtml"`
}

SearchResponseTypeChannel is a list containing playlist entries. It is embedded in the SearchResponse and ChannelsSearchResponse structures returned when calling "Search()" and "ChannelsSearch()" respectively.

type SearchResponseTypePlaylist

type SearchResponseTypePlaylist struct {
	Type       string `json:"type"`
	Title      string `json:"title"`
	PlaylistId string `json:"playlistId"`
	Author     string `json:"author"`
	AuthorId   string `json:"authorId"`
	AuthorUrl  string `json:"authorUrl"`
	VideoCount int32  `json:"videoCount"`

	Videos []struct {
		Title           string `json:"title"`
		VideoId         string `json:"videoId"`
		LengthSeconds   int32  `json:"lengthSeconds"`
		VideoThumbnails []struct {
			Quality string `json:"quality"`
			Url     string `json:"url"`
			Width   int32  `json:"width"`
			Height  int32  `json:"height"`
		} `json:"videoThumbnails"`
	} `json:"videos"`
}

SearchResponseTypePlaylist is a list containing playlist entries. It is embedded in the SearchResponse and ChannelsSearchResponse structures returned when calling "Search()" and "ChannelsSearch()" respectively.

type SearchResponseTypeVideo

type SearchResponseTypeVideo struct {
	Type      string `json:"video"`
	Title     string `json:"title"`
	VideoId   string `json:"videoId"`
	Author    string `json:"author"`
	AuthorId  string `json:"authorId"`
	AuthorUrl string `json:"authorUrl"`

	VideoThumbnails []struct {
		Quality string `json:"quality"`
		Url     string `json:"url"`
		Width   int32  `json:"width"`
		Height  int32  `json:"height"`
	} `json:"videoThumbnails"`

	Description     string `json:"description"`
	DescriptionHtml string `json:"descriptionHtml"`
	ViewCount       int64  `json:"viewCount"`
	Published       int64  `json:"published"`
	PublishedText   string `json:"publishedText"`
	LengthSeconds   int32  `json:"lengthSeconds"`
	LiveNow         bool   `json:"liveNow"`
	Paid            bool   `json:"paid"`
	Premium         bool   `json:"premium"`
}

SearchResponseTypeVideo is a list containing video entries. It is embedded in the SearchResponse and ChannelsSearchResponse structures returned when calling "Search()" and "ChannelsSearch()" respectively.

type SearchSuggestionsResponse

type SearchSuggestionsResponse struct {
	Query       string   `json:"query"`
	Suggestions []string `json:"suggestions"`
}

SearchSuggestionsResponse is the structure returned when calling "SearchSuggestions()"

type StatsResponse

type StatsResponse struct {
	Version string `json:"version"`

	Software struct {
		Name    string `json:"name"`
		Version string `json:"version"`
		Branch  string `json:"branch"`
	} `json:"software"`

	OpenRegistrations bool `json:"openRegistrations"`

	Usage struct {
		Users struct {
			Total          int32 `json:"total"`
			ActiveHalfYear int32 `json:"activeHalfYear"`
			ActiveMonth    int32 `json:"activeMonth"`
		} `json:"users"`
	} `json:"usage"`

	Metadata struct {
		UpdatedAt              int64 `json:"updatedAt"`
		LastChannelRefreshedAt int64 `json:"lastChannelRefreshedAt"`
	} `json:"metadata"`
}

StatsResponse is the structure returned when calling "Stats()"

type TopResponse

type TopResponse []struct {
	Title   string `json:"title"`
	VideoId string `json:"videoId"`

	VideoThumbnails struct {
		Quality string `json:"quality"`
		Url     string `json:"url"`
		Width   int32  `json:"width"`
		Height  int32  `json:"height"`
	} `json:"videoThumbnails"`

	LengthSeconds int32 `json:"lengthSeconds"`
	ViewCount     int64 `json:"viewCount"`

	Author    string `json:"author"`
	AuthorId  string `json:"authorId"`
	AuthorUrl string `json:"authorUrl"`

	Published       int64  `json:"published"`
	PublishedText   string `json:"publishedText"`
	Description     string `json:"description"`
	DescriptionHtml string `json:"descriptionHtml"`
}

TopResponse is the structure returned when calling "Top()"

type TrendingResponse

type TrendingResponse []struct {
	Title   string `json:"title"`
	VideoId string `json:"videoId"`

	VideoThumbnails []struct {
		Quality string `json:"quality"`
		Url     string `json:"url"`
		Width   int32  `json:"width"`
		Height  int32  `json:"height"`
	} `json:"videoThumbnails"`

	LengthSeconds int32 `json:"lengthSeconds"`
	ViewCount     int64 `json:"viewCount"`

	Author    string `json:"author"`
	AuthorId  string `json:"authorId"`
	AuthorUrl string `json:"authorUrl"`

	Published       int64  `json:"published"`
	PublishedText   string `json:"publishedText"`
	Description     string `json:"description"`
	DescriptionHtml string `json:"descriptionHtml"`

	LiveNow bool `json:"liveNow"`
	Paid    bool `json:"paid"`
	Premium bool `json:"premium"`
}

TrendingResponse is the structure returned when calling "Trending()"

type VideosResponse

type VideosResponse struct {
	Title   string `json:"title"`
	VideoId string `json:"videoId"`

	VideoThumbnails []struct {
		Quality string `json:"quality"`
		Url     string `json:"url"`
		Width   int32  `json:"width"`
		Height  int32  `json:"height"`
	} `json:"videoThumbnails"`

	Description     string `json:"description"`
	DescriptionHtml string `json:"descriptionHtml"`
	Published       int64  `json:"published"`
	PublishedText   string `json:"publishedText"`

	Keywords     []string `json:"keywords"`
	ViewCount    int64    `json:"viewCount"`
	LikeCount    int32    `json:"likeCount"`
	DislikeCount int32    `json:"dislikeCount"`

	Paid             bool     `json:"paid"`
	Premium          bool     `json:"premium"`
	IsFamilyFriendly bool     `json:"isFamilyFriendly"`
	AllowedRegions   []string `json:"allowedRegions"`
	Genre            string   `json:"genre"`
	GenreUrl         string   `json:"genreUrl"`

	Author    string `json:"author"`
	AuthorId  string `json:"authorId"`
	AuthorUrl string `json:"authorUrl"`

	AuthorThumbnails []struct {
		Url    string `json:"url"`
		Width  int32  `json:"width"`
		Height int32  `json:"height"`
	} `json:"authorThumbnails"`

	SubCountText      string  `json:"subCountText"`
	LengthSeconds     int32   `json:"lengthSeconds"`
	AllowRatings      bool    `json:"allowRatings"`
	Rating            float32 `json:"rating"`
	IsListed          bool    `json:"isListed"`
	LiveNow           bool    `json:"liveNow"`
	IsUpcoming        bool    `json:"isUpcoming"`
	PremiereTimestamp int64   `json:"premiereTimestamp"`

	HlsUrl string `json:"hlsUrl"`

	AdaptiveFormats []struct {
		Index          string `json:"index"`
		Bitrate        string `json:"bitrate"`
		Init           string `json:"init"`
		Url            string `json:"url"`
		Itag           string `json:"itag"`
		Type           string `json:"type"`
		Clen           string `json:"clen"`
		Lmt            string `json:"lmt"`
		ProjectionType string `json:"projectionType"`
		Container      string `json:"container"`
		Encoding       string `json:"encoding"`
		QualityLabel   string `json:"qualityLabel"`
		Resolution     string `json:"resolution"`
	} `json:"adaptiveFormats"`

	FormatStreams []struct {
		Url          string `json:"url"`
		Itag         string `json:"itag"`
		Type         string `json:"type"`
		Quality      string `json:"quality"`
		Container    string `json:"container"`
		Encoding     string `json:"encoding"`
		QualityLabel string `json:"qualityLabel"`
		Resolution   string `json:"resolution"`
		Size         string `json:"size"`
	} `json:"formatStreams"`

	Captions []struct {
		Label        string `json:"label"`
		LanguageCode string `json:"languageCode"`
		Url          string `json:"url"`
	} `json:"captions"`

	RecommendedVideos []struct {
		VideoId string `json:"videoId"`
		Title   string `json:"title"`

		VideoThumbnails []struct {
			Quality string `json:"quality"`
			Url     string `json:"url"`
			Width   int32  `json:"width"`
			Height  int32  `json:"height"`
		} `json:"videoThumbnails"`

		Author        string `json:"author"`
		LengthSeconds int32  `json:"lengthSeconds"`
		ViewCountText string `json:"viewCountText"`
	}
}

VideosResponse is the structure returned when calling "Videos()"

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL