1. Project Overview & Quickstart (iv-org/LICENSE)
LICENSE
Open-source repository iv-org/LICENSE
Repository Details
- Repository: iv-org/LICENSE
- Primary Language: Code
Note: High-volume repository documentation is actively indexed and synchronized by YakaAI.
2. Official Technical Reference & Guides (iv-org/docs)
File: README.md
The Invidious documentation
Running the documentation locally for development purposes
Run those commands in the repository's folder.
Local `mkdocs-material` installation
# You might want to create a virtualenv first
pip install mkdocs-material
mkdocs-material serveWith docker
docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material:latestFile: docs/api/authenticated-endpoints.md
API - Authenticated endpoints
All endpoints under namespace /api/v1/auth require authentication.
Authentication can be in one of two forms:
- A
Cookie: <SID>header (for logged in users) - An
Authorization: Bearer <TOKEN>(recommended)
A new token can be generated from /authorize_token with the given parameters:
scopes: Comma-separated list of scopes
callback_url: URL to redirect to with generated token
expire: Int64 how long a given token should be valid (in seconds)Each scope has the following format:
METHOD1;METHOD2...:ENDPOINT(*)?Where METHOD can be one of GET, POST, PUT, DELETE, PATCH.
An ENDPOINT can be any of the documented endpoints below.
Examples:
POST:playlists*: authorizesPOSTmethods to any endpoint under/api/v1/auth/playlists(/api/v1/auth/playlists,/api/v1/playlists/:id/videos, etc.):playlists/*: authorizes any method to endpoints under/api/v1/auth/playlists/(/api/v1/auth/playlists/:id,/api/v1/playlists/:id/videos, etc.)GET:playlists/IVPAAAAAAA: authorizesGETonly to playlistIVPAAAAAAA.:preferences: authorizes any method to/api/v1/auth/preferencesGET;POST:preferences: authorizesGETorPOSTto/api/v1/auth/preferences
When a callback_url is specified, after a user has authorized a token with the desired scopes, a GET request will be made to the callback_url with the token URL-escaped and appended as token=TOKEN.
GET `/api/v1/auth/feed`
Get subscription feed for the authenticated user.
Parameters:
max_results: Int32
page: Int32Schema:
{
"notifications": [
{
"type": "shortVideo",
"title": String,
"videoId": String,
"videoThumbnails": [
{
"quality": String,
"url": String,
"width": Int64,
"height": Int64
}
],
"lengthSeconds": Int64,
"author": String,
"authorId": String,
"authorUrl": String,
"published": Int64,
"publishedText": String,
"viewCount": Int64
}
],
"videos": [
{
"type": "shortVideo",
"title": String,
"videoId": String,
"videoThumbnails": [
{
"quality": String,
"url": String,
"width": Int64,
"height": Int64
}
],
"lengthSeconds": Int64,
"author": String,
"authorId": String,
"authorUrl": String,
"published": Int64,
"publishedText": String,
"viewCount": Int64
}
]
}GET `/api/v1/auth/notifications`
Parameters:
topics: Array(String) (comma separated: e.g. "UCID1,UCID2) limit of 1000 topics
since: Int64, timestampProvides an EventSource for receiving changes from each topic in topics. Currently the only supported topic-type is ucid, which will return an updated video object whenever the given channel uploads a video.
Important to note is that an event will also be sent when a channel changes an already uploaded video, for example changing description or title.
Each event is a JSON object with the same schema as /api/v1/videos.
A debug topic can also provided which will return a (psuedo-)randomly selected video every minute.
since will return all videos uploaded since TIMESTAMP, with a limit of the 15 most recent videos from each topic.
More details in #469.
POST `/api/v1/auth/notifications`
Same as above GET endpoint, however topics is moved into post body as Content-Type: application/x-www-form-urlencoded.
GET `/api/v1/auth/playlists`
Get list of playlists for the given user.
Schema:
[
{
"type": "invidiousPlaylist",
"title": String,
"playlistId": String,
"author": String,
"authorId": null,
"authorUrl": null,
"authorThumbnails": [],
"description": String,
"descriptionHtml": String,
"videoCount": Int32,
"viewCount": 0,
"updated": Int64,
"isListed": Boolean,
"videos": [
{
"title": String,
"videoId": String,
"author": String,
"authorId": String,
"authorUrl": String,
"videoThumbnails": [
{
"quality": String,
"url": String,
"width": Int32,
"height": Int32
}
],
"index": Int32,
"indexId": String,
"lengthSeconds": Int32
}
]
}
]POST `/api/v1/auth/playlists`
Content-Type: application/json
Create new playlist.
Example request body:
{
"title": String,
"privacy": "private"
}privacy can be any of: public, unlisted, private
If successful, returns 201, a link to the created resource as a Location header, and the following response:
{
"title": String,
"playlistId": String
}GET `/api/v1/auth/playlists/:id`
Returns same result as unauthenticated /api/v1/playlists/:id.
Important to note is that if the requested playlist is marked as private, it will return an error if the request is not authenticated as the playlist's author.
PATCH `/api/v1/auth/playlists/:id`
Content-Type: application/json
Modify a playlist's description, title, description, or privacy.
Example request body:
{
"title": String,
"description": String,
"privacy": "private"
}privacy can be any of: public, unlisted, private
Will return 204 on success.
DELETE `/api/v1/auth/playlists/:id`
Delete a given playlist :id.
Will return 204 on success.
POST `/api/v1/auth/playlists/:id/videos`
Content-Type: application/json
Add a video to the given playlist :id.
Example request body:
{
"videoId": String
}Returns a 201 on success with the following schema:
{
"title": String,
"videoId": String,
"author": String,
"authorId": String,
"authorUrl": String,
"videoThumbnails": [
{
"quality": String,
"url": String,
"width": Int32,
"height": Int32
}
]
}DELETE `/api/v1/auth/playlists/:id/videos/:index`
Delete a video from the given playlist :id with indexId :index.
Will return 204 on success.
GET `/api/v1/auth/preferences`
Get preferences for authenticated user.
Schema:
{
"annotations": false,
"annotations_subscribed": false,
"autoplay": false,
"captions": [
"",
"",
""
],
"comments": [
"youtube",
""
],
"continue": false,
"continue_autoplay": true,
"dark_mode": "light",
"latest_only": false,
"listen": false,
"local": false,
"locale": "en-US",
"max_results": 40,
"notifications_only": false,
"player_style": "invidious",
"quality": "hd720",
"default_home": "Popular",
"feed_menu": [
"Trending",
"Playlists"
],
"related_videos": true,
"sort": "published",
"speed": 1.0,
"thin_mode": false,
"unseen_only": false,
"video_loop": false,
"volume": 100
}POST `/api/v1/auth/preferences`
Content-Type: application/json
Patch user preferences.
Example body:
{
"speed": 2.0,
"volume": 10
}GET `/api/v1/auth/subscriptions`
Get user's subscriptions.
Schema:
[
{
"author": String,
"authorId": String
}
]POST `/api/v1/auth/subscriptions/:ucid`
Content-Type: application/json
Add a given ucid to a user's subscriptions.
Will return 204 on success.
DELETE `/api/v1/auth/subscriptions/:ucid`
Removes a given ucid from a user's subscriptions.
Will return 204 on success.
GET `/api/v1/auth/tokens`
Get a list of tokens for the authenticated user.
Schema:
[
{
"session": String,
"issued": Int64
}
]POST `/api/v1/auth/tokens/register`
Content-Type: application/json
Create a new token for the authenticated user.
Example request body:
{
"scopes": Array(String), // Each scope has same format as each scope in `/authorize_token`
"callbackUrl": String?,
"expire": Int64
}Returns a 200 on success with the newly created token as the response body.
Example response:
{
"session":"v1:YUwKEL1XwHQzp7-AAAAAAAAAAAAAAAAAA=",
"scopes":["GET:notifications"],
"signature":"jNYdAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAXGb__2Gv-w="
}POST `/api/v1/auth/tokens/unregister`
Content-Type: application/json
Revoke a token for the authenticated user.
Example request:
{
"session": "v1:YUwKEL1XwHQzp7-AAAAAAAAAAAAAAAAAA="
}Returns 204 on success.
GET `/api/v1/auth/history`
Get the history of videos played by the user.
Parameters:
max_results: Int32
page: Int32Schema:
[
// video IDs
]POST `/api/v1/auth/history/:id`
Set a video as watched.
Returns 204 on success.
DELETE `/api/v1/auth/history/:id`
Delete a video from the user watched history.
Returns 204 on success.
File: docs/api/channels_endpoint.md
API - Channels endpoint
Please refer to the Common object types page for more
details on the various JSON objects used below.
GET `/api/v1/channels/:id`
Response:
{
"author": String,
"authorId": String,
"authorUrl": String,
"authorVerified": Boolean,
"authorBanners": [
// One or more ImageObject
],
"authorThumbnails": [
// One or more ImageObject
],
"subCount": Number, // Integer
"totalViews": Number, // Integer
"joined": Number, // Unix timestamp
"autoGenerated": Boolean,
"isFamilyFriendly": Boolean,
"description": String,
"descriptionHtml": String,
"allowedRegions": String[],
"tabs": String[],
"pronouns": String?
"latestVideos": [
// One or more VideoObject
],
"relatedChannels": [
// One or more ChannelObject
]
}GET `/api/v1/channels/:id/channels`
URL parameters:
continuation: A continuation token to get the next chunk of items. The token is provided each time this API is requested.
Response:
{
"relatedChannels": [
// One or more ChannelObject
],
"continuation": String
}GET `/api/v1/channels/:id/latest`
This is the same as requesting /api/v1/channels/:id/videos without URL parameters.
GET `/api/v1/channels/:id/playlists`
URL parameters:
continuation: A continuation token to get the next chunk of items. The token is provided each time this API is requested.sort_by: Sort order filter. Accepted values:oldest,newest,last. Defaults tolast.
Response:
{
"playlists": [
// One or more PlaylistOject
],
"continuation": continuation
}GET `/api/v1/channels/:id/podcasts`
URL parameters:
continuation: A continuation token to get the next chunk of items. The token is provided each time this API is requested.
Response:
{
"playlists": [
// One or more PlaylistOject
],
"continuation": continuation
}GET `/api/v1/channels/:id/releases`
URL parameters:
continuation: A continuation token to get the next chunk of items. The token is provided each time this API is requested.
Response:
{
"playlists": [
// One or more PlaylistOject
],
"continuation": continuation
}GET `/api/v1/channels/:id/shorts`
URL parameters:
continuation: A continuation token to get the next chunk of items. The token is provided each time this API is requested.sort_by: Sort order filter. Accepted values:newest,popularoroldest
Response:
See: GET /api/v1/channels/:id/videos
GET `/api/v1/channels/:id/streams`
URL parameters:
continuation: A continuation token to get the next chunk of items. The token is provided each time this API is requested.sort_by: Sort order filter. Accepted values:newest,popularoroldest
Response:
See: GET /api/v1/channels/:id/videos
GET `/api/v1/channels/:id/videos`
URL parameters:
continuation: A continuation token to get the next chunk of items. The token is provided each time this API is requested.sort_by: Sort order filter. Accepted values:newest,popularoroldest(Broken as of 10/2022). Default tonewest.
Response:
{
"videos": [
// One or more VideoObject
],
"continuation": String
}GET `/api/v1/channels/:id/community`
Please refer to the Community Post Attachment types below for more details on the attachment JSON objects used below.
Url parameters
continuation: A continuation token to get the next chunk of items.
Response:
{
"authorId": String,
"comments": {
"attachment": ImageAttachment | MultiImageAttachment | VideoAttachment | PollAttachment | PlaylistAttachment
"author": String,
"authorIsChannelOwner": Boolean
"authorId": String,
"authorThumbnails": ImageObject[],
"authorUrl": String,
"commentId": String,
"content": String,
"contentHtml": String,
"isEdited": Boolean,
"likeCount": Number,
"published": Number,
"publishedText": String,
"replyCount": Number
}[]
}Community Post Attachment Types
VideoAttachment
See VideoObject common type
PlaylistAttachment
See PlaylistObject common type
ImageAttachment
{
"type": "image",
"imageThumbnails": ImageObject[]
}MultiImageAttachment
{
"type": "multiImage",
"images": ImageObject[][]
}PollAttachment
{
"type": "poll",
"totalVotes": Number,
"choices": {
"text": String,
"image?": ImageObject[]
}[]
}Unknown
This usually means that parsing support for the attachment type has not yet been implemented.
{
"type": "unknown",
"error": String
}GET `/api/v1/channels/:ucid/search`
Url parameters
q: The query to searchpage: The page number of the search (Int32)
Response:
[
VideoObject,
PlaylistObject
];GET `/api/v1/post/:id`
Url parameters
ucid: It's recommended to provide the channel's id for fetching the post.
Response:
Same as/api/v1/channels/:id/communitybut only returns one post in the comments array
GET `/api/v1/post/:id/comments`
Url parameters
ucid: You need to provide the channel's id for fetching the post's comments.sort_by: "top", "new" (default: top)
Response:
Same as/api/v1/channels/:id/commentsbut has a postId instead of a videoId
File: docs/api/common_types.md
API - Common object types
ImageObject
{
"url": String,
"width": Number, // Integer
"height": Number // Integer
}ThumbnailObject
{
"quality": String,
"url": String,
"width": Number, // Integer
"height": Number // Integer
}VideoObject
{
"type": "video", // Constant
"title": String,
"videoId": String,
"author": String,
"authorId": String,
"authorUrl": String,
"authorVerified": Boolean,
"videoThumbnails": [
// One or more ThumbnailObject
],
"description": String,
"descriptionHtml": String,
"viewCount": Number, // Integer
"viewCountText": String,
"lengthSeconds": Number, // Integer
"published": Number, // Unix timestamp
"publishedText": String,
// Only available on premiered videos
"premiereTimestamp": Number, // Unix timestamp
"liveNow": Boolean,
"premium": Boolean,
"isUpcoming": Boolean,
"isNew" Boolean,
"is4k": Boolean,
"is8k": Boolean,
"isVr180": Boolean,
"isVr360": Boolean,
"is3d": Boolean,
"hasCaptions": Boolean
}ChannelObject
{
"type": "channel", // Constant
"author": String,
"authorId": String,
"authorUrl": String,
"authorVerified": Boolean,
"authorThumbnails": [
// One or more ThumbnailObject
],
"autoGenerated": Boolean,
"subCount": Number, // Integer
"videoCount": Number, // Integer
"description": String,
"descriptionHtml": String,
}PlaylistObject
{
"type": "playlist", // Constant
"title": String,
"playlistId": String,
"playlistThumbnail": String,
"author": String,
"authorId": String,
"authorUrl": String,
"authorVerified": Boolean,
"videoCount": Number, // Integer
"videos": [
{
"title": String,
"videoId": String,
"lengthSeconds": Number, // Integer
"videoThumbnails": [
// One or more ThumbnailObject
]
}
]
}File: docs/apache2.md
Apache2 reverse proxy setup
- A very basic config, secured with Let's Encrypt. Any log is disabled by default. Do not forget to replace
ServerNamewith your domain.
<VirtualHost *:443>
ServerName invidious.domain.tld
ServerAdmin admin@localhost
ProxyPreserveHost On
ProxyRequests off
ProxyPass / http://127.0.0.1:3000/ nocanon
ProxyPassReverse / http://127.0.0.1:3000/
AllowEncodedSlashes on
# ErrorLog /var/log/apache2/invidious.domain.tld/error.log
CustomLog /dev/null combined
RewriteEngine on
SSLCertificateFile /etc/letsencrypt/live/invidious.domain.tld/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/invidious.domain.tld/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/invidious.domain.tld/chain.pem
</VirtualHost>- Another config example without HTTPS, but with Apache Basic Auth HTTP login.
The user will connect to Apache on port 3333 and will be asked to log in. If authentification is successful, Apache will redirect the user to Invidious' page.
To make the VirtualHost config below actually work, you should as well:- Create a .htpasswd file and add required username/login combos to it, if not already existing.
- Open port 3333 (or any other free port) adding
Listen 3333to Apacheports.conf(Debian/etc/apache2/ports.conf) - If you run Invidious with default parameters, you may need to replace default host binding (0.0.0.0) with localhost (127.0.0.1) instead. That way, Invidious won't be publicly available on port 3000 anymore, but only accessible via the reverse proxy on port 3333. So if you run Invidious via a systemd service, you would edit the service file (e.g.
/etc/systemd/system/invidious.service) and modify the ExecStart line to include the -b switch as followsExecStart=/home/invidious/invidious/invidious -b 127.0.0.1 -o invidious.logand then reload the daemon withsystemctl daemon-reloadso that changes are taken into account. - A convenient way to open such protected Invidious page without having to log in manually everytime is to access use a URL with the following format: http://username:password@domain:3333
<VirtualHost *:3333>
ServerName invidious.domain.tld #add your own domain name (or localhost if you have none)
ServerAdmin admin@localhost
<Location />
Deny from all # Forbid access to all by default...
#Allow from 127.0.0.1 #...Except from specific IPs (which will not need to authenticate)...
AuthUserFile /etc/apache2/.htpasswd #path to .htpasswd file
AuthName "Restricted Area" # name displayed in the promptbox
AuthType Basic # http://httpd.apache.org/docs/current/howto/auth.html
Satisfy Any
Require valid-user # ...and except from authenticated users included in the .htpasswd file
</Location>
ProxyPass / http://127.0.0.1:3000/ nocanon
ProxyPassReverse / http://127.0.0.1:3000/
ProxyPreserveHost On
ProxyRequests Off
AllowEncodedSlashes On
#ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog /dev/null combined
</VirtualHost>File: docs/api.md
API
Language
All endpoints that return a JSON body support &hl=LANGUAGE for translating fields into the desired language. A list of languages are provided in List of URL parameters.
GET `/api/v1/stats`
Schema:
{
"version": String,
"software": {
"name": "invidious",
"version": String,
"branch": String
},
"openRegistrations": Bool,
"usage": {
"users": {
"total": Int32,
"activeHalfyear": Int32,
"activeMonth": Int32
}
},
"metadata": {
"updatedAt": Int64,
"lastChannelRefreshedAt": Int64
},
"playback": {
"totalRequests": Int32?
"successfulRequests": Int32?
"ratio": Float32?
}
}GET `/api/v1/videos/:id`
Schema:
/* Detailed source-code truncated for AI context efficiency. */Parameters:
region: ISO 3166 country code (default: "US")GET `/api/v1/annotations/:id`
Parameters:
source: "archive", "youtube" (default: "archive")Returns annotation XML from YouTube's /annotations_invideo endpoint. Alternatively it provides access to legacy annotation data using this collection on archive.org.
GET `/api/v1/comments/:id`
Schema:
{
"commentCount": Int32?,
"videoId": String,
"comments": [
{
"author": String,
"authorThumbnail": String,
"authorId": String,
"authorUrl": String,
"isEdited": Boolean,
"isPinned": Boolean,
"isSponsor": Boolean?,
"sponsorIconUrl": String?,
"content": String,
"contentHtml": String,
"published": Int64,
"publishedText": String,
"likeCount": Int32,
"commentId": String,
"authorIsChannelOwner": Bool,
"creatorHeart": {
"creatorThumbnail": String,
"creatorName": String
}?,
"replies": {
"replyCount": Int32,
"continuation": String
}?
}
],
"continuation": String?
}Parameters:
sort_by: "top", "new" (default: top)
source: "youtube", "reddit" (default: youtube)
continuation: StringGET `/api/v1/captions/:id`
Schema:
{
"captions": [
{
"label": String,
"languageCode": String,
"url": String
}
]
}Parameters:
label: String
lang: String
tlang: String
region: ISO 3166 country code (default: "US")A request with label will return the selected captions in WebVTT format.
Captions can also be selected with an ISO lang, e.g. &lang=en, tlang will auto-translate from English into the requested language (if English captions are available).
GET `/api/v1/trending`
Schema:
[
{
"title": String,
"videoId": String,
"videoThumbnails": [
{
"quality": String,
"url": String,
"width": Int32,
"height": Int32
}
],
"lengthSeconds": Int32,
"viewCount": Int64,
"author": String,
"authorId": String,
"authorUrl": String,
"published": Int64,
"publishedText": String,
"description": String,
"descriptionHtml": String,
"liveNow": Bool,
"paid": Bool,
"premium": Bool
}
]Parameters:
type: "music", "gaming", "movies", "default"
region: ISO 3166 country code (default: "US")GET `/api/v1/popular`
Schema:
[
{
"type": "shortVideo",
"title": String,
"videoId": String,
"videoThumbnails": [
{
"quality": String,
"url": String,
"width": Int32,
"height": Int32
}
],
"lengthSeconds": Int32,
"viewCount": Int64,
"author": String,
"authorId": String,
"authorUrl": String,
"published": Int64,
"publishedText": String
}
]GET `/api/v1/search/suggestions`
Schema:
{
"query": String,
"suggestions": Array(String)
}Parameters:
q: StringGET `/api/v1/search`
Schema:
[
{
type: "video",
title: String,
videoId: String,
author: String,
authorId: String,
authorUrl: String,
videoThumbnails: [
{
quality: String,
url: String,
width: Int32,
height: Int32
}
],
descriptionHtml: String,
viewCount: Int64,
published: Int64,
publishedText: String,
lengthSeconds: Int32,
liveNow: Bool,
paid: Bool,
premium: Bool
},
{
type: "playlist",
title: String,
playlistId: String,
playlistThumbnail: String,
author: String,
authorId: String,
authorUrl: String,
authorVerified: Boolean,
videoCount: Int32,
videos: [
{
title: String,
videoId: String,
lengthSeconds: Int32,
videoThumbnails: [
{
quality: String,
url: String,
width: Int32,
height: Int32
}
]
}
]
},
{
type: "channel",
author: String,
authorId: String,
authorUrl: String,
authorThumbnails: [
{
url: String,
width: Int32,
height: Int32
}
],
autoGenerated: Bool
subCount: Int32,
videoCount: Int32,
descriptionHtml: String
},
{
type: "hashtag"
title: String,
url: String,
channelCount: Int32,
videoCount: Int32
}
];Parameters:
q: String
page: Int32
sort: "relevance", "views"
date: "hour", "today", "week", "month", "year"
duration: "short", "long", "medium"
type: "video", "playlist", "channel", "movie", "show", "all", (default: all)
features: "hd", "subtitles", "creative_commons", "3d", "live", "purchased", "4k", "360", "location", "hdr", "vr180" (comma separated: e.g. "&features=hd,subtitles,3d,live")
region: ISO 3166 country code (default: "US")GET `/api/v1/playlists/:plid`
Schema:
{
"title": String,
"playlistId": String,
"author": String,
"authorId": String,
"authorThumbnails": [
{
"url": String,
"width": String,
"height": String
}
],
"description": String,
"descriptionHtml": String,
"videoCount": Int32,
"viewCount": Int64,
"viewCountText": String,
"updated": Int64,
"videos": [
{
"title": String,
"videoId": String,
"author": String,
"authorId": String,
"authorUrl": String,
"videoThumbnails": [
{
"quality": String,
"url": String,
"width": Int32,
"height": Int32
}
],
"index": Int32,
"lengthSeconds": Int32
}
]
}Parameters:
page: Int32GET `/api/v1/mixes/:rdid`
Schema:
{
title: String,
mixId: String,
videos: [
{
title: String,
videoId: String,
author: String,
authorId: String,
authorUrl: String,
videoThumbnails: [
{
quality: String,
url: String,
width: Int32,
height: Int32
}
],
index: Int32,
lengthSeconds: Int32
}
]
}GET `/api/v1/hashtag/:tag`
Schema:
{
results: VideoObject[],
}Parameters:
page: Int32GET `/api/v1/resolveurl`
Schema:
{
browseId?: String,
ucid?: String,
videoId?: String,
playlistId?: String,
startTimeSeconds?: String,
postId?: String,
params?: String,
pageType: string
}Parameters:
url: URLGET `/api/v1/clips`
Schema:
{
startTime: Float64, // in seconds
endTime: Float64, // in seconds
clipTitle: String,
video: VideoObject
}Parameters:
id: stringFile: docs/applications.md
Applications
Lists of third-party projects that use or support Invidious.
Applications
| Name | Description | Links |
|---|---|---|
| FreeTube | A desktop libre software YouTube app for privacy. | |
| Yattee | Alternative YouTube frontend for iPhone, iPad, Mac and Apple TV. | App Store |
| CloudTube | A JavaScript-rich alternate online YouTube player. | |
| HoloPlay | Progressive Web App for music connecting on Invidious API's with search, playlists and favorites. | |
| WatchTube | Powerful YouTube client for Apple Watch. | App Store |
| TubiTui | A lightweight, libre, TUI-based YouTube client. For desktop. | |
| Ytfzf | A posix script to find and watch youtube videos from the terminal. (Without API). | |
| Playlet | A Roku TV YouTube app that uses Invidious as a backend. | Roku Store |
| GTK+ Pipe Viewer | YouTube client for Linux. | |
| PlasmaTube | YouTube client for Linux. | KDE Applications |
| yt2alt | A cli tool to import your profile from Youtube to an alternative platform. | |
| Materialious | Modern material design for Invidious. | |
| IInvidious | An Invidious client for the Apple II. | Repository |
Browser Extensions
| Name | Description | Links |
|---|---|---|
| View on Invidious | View page on Invidious (bookmarklet). | |
| Redirector | Extension for Firefox and Chromium that can manually be configured to process Invidious Redirects. For details, see the setup page to use it with Invidious. | Source / Firefox / Chrome |
| Invidious Copy URL | Adds context menu options on Invidious to copy shortened YouTube URL at current time or not (Requires using developer mode in Chrome or a developer version of Firefox). | |
| Privacy Redirect | Redirects YouTube to Invidious, Twitter to Nitter, and Instagram to Bibliogram. | Source / Firefox / Chrome |
| SponsorBlock | A crowd-sourced extension to skip sponsorships. Support Invidious instances if enabled in the options. | Source / Firefox / Chrome |
| Inviduration | Firefox extension that shows total duration of playlists on Invidious. | Source / Firefox |
| Alter | Firefox extension that redirects YouTube, Twitter and Reddit to Invidious, Nitter and Teddit respectively. | Source / Firefox |
Userscripts for Invidious.
| Name | Description |
|---|---|
| Invidious Redirect | Redirects YouTube URLs to a deprecated instance, but can be edited to redirect to a different instance. |
| YouTube to Invidious | Scans current page for YouTube embeds and replace with invidio.us. (Can be edited to use any preferred instance) |
| No-Youtube | Userscript to replace YouTube links to invidio.us. Can be edited to redirect to a preferred instance. |
| FYTE | Replace all YouTube embeds on a page with the video's thumbnail. Click on the thumbnail to play the video. Invidious is supported in the Options panel (redirects to invidio.us by default, can be edited). |
| Privacy Redirector | Userscript to automatically redirect to different private frontends, including Invidious. Can be configured to redirect to specific instances of the user's choice. |
| Simple Sponsor Skipper | Userscript based on SponsorBlock that is pre-configured to work with multiple Invidious instances, but can be easily altered to use any instance desired by the user. |
| Invidious Preferences Userscript | Userscript for enforcing preferences across multiple instances, through URL Parameters, and without using browser cookies. Works in tandem with Privacy Redirector and Simple Sponsor Skipper. See here for more information about URL Parameters. |
Extensions that Integrate Invidious into other Apps
| Name | Description | Links |
|---|---|---|
| iPhone Redirector Shortcut | Automatically open YouTube videos in Invidious (iPhone shortcut). | |
| FreshRSS Extension | A FreshRSS extension to directly embed videos from Invidious channel feeds. | |
| UntrackMe | Android app to rewrite YouTube links to Invidious. Can optionally play videos in the app as well. | |
| Kodi add-on | Watch YouTube videos in the Kodi media center, using the Invidious API. Privacy-friendly alternative to the YouTube API add-on. | |
| Koutube | Watch YouTube videos on Discord without loading the YouTube embed. Quickly replace any YouTube links using the sed syntax (s/y/k) or by swapping the y with a k. | Source / Website |
Utilities
| Name | Description |
|---|---|
| Invidious-Updater (And Installer) | Automatic install and update script for Invidious. |
File: docs/caddy.md
Caddy reverse proxy setup
This is a very basic config, assuming that you're using Caddy to manage SSL certificates for you.
Any log is disabled by default. Do not forget to replace server_name with your domain.
https://<server_name> {
reverse_proxy localhost:3000
log {
output discard
}
}File: docs/captcha-bug.md
CAPTCHA bug on Debian and Ubuntu
Warning: If you use ImageMagick on Ubuntu for other services like Mastodon this guide may break them, please use [this guide](https://linuxconfig.org/how-to-install-imagemagick-7-on-ubuntu-18-04-linux) instead.
There is some issue on Debian 9 and Ubuntu 18.04 and later. It appears that the clock (the CAPTCHA) has no hands but you can see them outside the clock. You need to compile ImageMagick yourself with librsvg to solve this issue.
Thanks Tmiland for showing us a solution at #299
For lazy people, a little hack is to disable CAPTCHA or use text one.
You can check if your version of ImageMagick is affected with convert -list format.
It should show the following if your installed version is okay.
SVG SVG rw+ Scalable Vector Graphics (RSVG 2.40.13)
SVGZ SVG rw+ Compressed Scalable Vector Graphics (RSVG 2.40.13)If this is not the case, your version is not compiled with librsvg, then you get the following.
SVG SVG rw+ Scalable Vector Graphics (XML 2.9.4)
SVGZ SVG rw+ Compressed Scalable Vector Graphics (XML 2.9.4)Follow the steps to fix this issue:
$ sudo apt purge imagemagick
$ cd /tmp
# check for new releases: https://github.com/ImageMagick/ImageMagick6/releases
$ wget https://github.com/ImageMagick/ImageMagick6/archive/6.9.11-19.tar.gz
$ tar -xvf 6.9.10-24.tar.gz
$ cd ImageMagick6-6.9.10-24
$ ./configure --with-rsvg
$ make
$ sudo make installSet the correct path: $ sudo ln -s /usr/local/bin/convert /usr/bin/convert
If you get an error here that this file already exists, please execute $ sudo apt autoremove
Now convert -list format reports
SVG rw+ Scalable Vector Graphics (RSVG 2.40.16)
SVGZ rw+ Compressed Scalable Vector Graphics (RSVG 2.40.16)Restart Invidious, just to be sure:
$ sudo systemctl restart invidious.serviceFile: docs/community-installation-guide.md
Community Installation Guide
??? warning "Before following the community installation guide"
All the platforms listed in the community installation guide are not supported officially by the Invidious developers.
This means:
- The Invidious developers can't help you to solve issues with your platform. Ask the community on [Matrix](https://matrix.to/#/#invidious:matrix.org) or [IRC](https://web.libera.chat/?channel=#invidious) before creating GitHub issues. But if you do fix an issue please create a PR for updating the community installation guide.
- The guide for your platform may be outdated because things have changed since the creation of the guide.If your platform is not listed but you would like to contribute to this guide for adding it, please do here. We rely on the community to help us.
After installation take a look at the Post-install steps.
Podman (rootless container)
Guide contributor(s): @sigulete
Podman is usually pre-installed in Fedora, CentOS, RHEL and derivatives. But if this is not the case, the instruction below will install all necessary packages.
RHEL based and RHEL-like systems
sudo dnf install podmanDownload the configuration files from Invidious' repository
Note: Currently the repository has to be cloned, this is because the init-invidious-db.sh file and the config/sql directory have to be mounted to the postgres container (See the volumes section in the postgres' container). This "problem" will be solved in the future.
`` Absolute path in your home directory where Invidious will be downloaded (e.i. /home/johnsmith/.inv)
cd
git clone https://github.com/iv-org/invidious.gitCreate Pod - videos
podman pod create --name videos -p 3000:3000Create Container - postgres
podman create --rm \
--pod videos \
--name postgres \
--label "io.containers.autoupdate=registry" \
--health-cmd='pg_isready -U $POSTGRES_USER -d $POSTGRES_DB' \
-v postgresdata:/var/lib/postgresql/data \
-v /invidious/config/sql:/config/sql:z \
-v /invidious/docker/init-invidious-db.sh:/docker-entrypoint-initdb.d/init-invidious-db.sh:z \
-e POSTGRES_DB=invidious \
-e POSTGRES_USER=kemal \
-e POSTGRES_PASSWORD=kemal \
docker.io/library/postgres:14Create Container - invidious
Copy /invidious/config/config.example.yml to /config.yml and update parameters as required.
podman create --rm \
--pod videos \
--name invidious \
--label "io.containers.autoupdate=registry" \
--health-cmd="wget -nv --tries=1 --spider http://127.0.0.1:3000/api/v1/stats || exit 1" \
--health-interval=30s \
--health-timeout=5s \
--health-retries=2 \
-v /config.yml:/invidious/config/config.yml:z,U \
quay.io/invidious/invidious:latestCreate systemd services to manage the Pod
Podman can generate systemd services to handle the life cycle of pods and containers.
The instructions below will create 3 service units, and they will be placed in the correct location ready to be used.
cd ~
cp $(podman generate systemd --new --files --name videos) .config/systemd/userStart Pod
Despite the existance of 3 services, only the one related to the Pod must be used. The life cycle for the 2 containers implementing postgres and invidious will be handled by the pod.
systemctl --user daemon-reload
systemctl --user enable --now pod-videos.serviceAnd similarly, the instruction below will re-start the service:
systemctl --user restart pod-videos.serviceIf this service runs on a server, it will stop as soon as you logout, because it is running in user space.
To ensure it is persistent and remains active after logging out, you will need to enable user lingering.
loginctl enable-lingerUpdating to the latest release
podman auto-update
podman image prune -fPodman via systemd
Guide contributor(s): @redbeardymcgee
This method employs rootless containers through podman whose lifecycles are managed by systemd and is suitable for systems which come with Podman version 5.x or higher. Ensure that SELinux is in enforcing mode for maximum security. Do not run any of the following commands or scripts as root.
Define containers
Add the quadlet definitions for Invidious, the database, and the companion containers to $HOME/.config/containers/systemd/invidious.
# $HOME/.config/containers/systemd/invidious/invidious.container
[Unit]
Description=Invidious
Requires=invidious-db.service
After=invidious-db.service
Requires=invidious-companion.service
After=invidious-companion.service
[Service]
Restart=on-failure
TimeoutStartSec=900
[Install]
WantedBy=multi-user.target
[Container]
Image=quay.io/invidious/invidious:latest
ContainerName=invidious
AutoUpdate=registry
Network=invidious.network
HostName=invidious
Volume=./config.yml:/invidious/config/config.yml:Z# $HOME/.config/containers/systemd/invidious/invidious-db
[Unit]
Description=Invidious postgres
[Service]
Restart=on-failure
TimeoutStartSec=900
[Install]
WantedBy=multi-user.target
[Container]
Image=docker.io/library/postgres:14
ContainerName=invidious-db
AutoUpdate=registry
Network=invidious.network
HostName=invidious-db
Volume=invidious-db:/var/lib/postgresql/data:Z
Environment=POSTGRES_DB=invidious
Environment=POSTGRES_USER=kemal
Environment=POSTGRES_PASSWORD=kemal
# NOTE: Alternatively, set password as a podman secret
# `printf 'my-postgres-password' | podman secret create --replace invidious-db-pw -`
# Secret=invidious-db-pw,type=env,target=POSTGRES_PASSWORD# $HOME/.config/containers/systemd/invidious/invidious-companion
[Unit]
Description=Invidious companion
[Service]
Restart=on-failure
TimeoutStartSec=900
[Install]
WantedBy=multi-user.target
[Container]
Image=quay.io/invidious/invidious-companion:latest
ContainerName=invidious-companion
AutoUpdate=registry
Network=invidious.network
HostName=invidious-companion
Volume=invidious-companion-cache:/var/tmp/youtubei.js:rw,Z
# WARNING: The container will fail to start without this env var
# NOTE: The podman secret is preferred, but you may set the env var directly like this
# Environment=SERVER_SECRET_KEY=my-secret-key
# If you set the env var above, comment this out
# `pwgen 16 1 | podman secret create --replace invidious-db-pw -`
Secret=invidious-companion-secret-key,type=env,target=SERVER_SECRET_KEYDefine the storage volumes
The database requires a data volume to persist the database. The companion uses a cache volume.
# $HOME/.config/containers/systemd/invidious/invidious-db.volume
[Volume]
VolumeName=invidious-db# $HOME/.config/containers/systemd/invidious/invidious-companion.volume
[Volume]
VolumeName=invidious-companion-cacheModify `config.yml` for your evironment
Copy the example config from HERE.
curl -o "$HOME"/.config/containers/systemd/invidious/config.yml https://raw.githubusercontent.com/iv-org/invidious/refs/heads/master/config/config.example.yml
Edit the configuration according to your environment. The example is very well commented. Notable fields include invidious_companion and invidious_companion_key to ensure that the companion container is connectable. If you changed the $POSTGRES_PASSWORD, then it should be configured to match in the db field. The field hmac_key is mandatory.
!!! warning
The Invidious container may fail to start or operate as expected if the `config.yml` is not correctly configured.Confirm the container services are generated
Systemd units are generated on-the-fly during daemon-reload command, but before that let's check syntax with quadlet generator. Note, you need Podman version 5.0 or higher, older versions will not work:
QUADLET_UNIT_DIRS="$HOME/.config/containers/systemd/invidious" /lib/systemd/user-generators/podman-user-generator -user -dryrun
Reload systemd daemon. Keep in mind you need to do this command every time you change a unit file.
systemctl --user daemon-reload
Prepare the database
The database container requires an initial migration. This should be handled by the field check_tables in config.yml if set to true. The following steps will manually initialize the database in case there is an issue.
# Start the database container
systemctl --user start invidious-db
# Enter the container, install curl, initialize the database, uninstall curl
podman exec invidious-db \
sh -c '
apt-get update
apt-get install --assume-yes --no-install-recommends curl
for initdb in channels videos channel_videos users session_ids nonces annotations playlists playlist_videos
do
curl -s https://raw.githubusercontent.com/iv-org/invidious/refs/heads/master/config/sql/$initdb.sql | psql postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@invidious-db/$POSTGRES_DB
done
apt-get --assume-yes purge curl
'Create a timer to restart Invidious regularly
Invidious recommends restarting frequently in the post-install configuration documentation. A systemd timer is an effective method of achieving this. Add the file to $HOME/.config/systemd/user/invidious.timer and activate it with systemctl --user enable --now invidious.timer.
# $HOME/.config/systemd/user/invidious.timer
[Unit]
Description=Restart Invidious every hour
[Install]
WantedBy=timers.target
[Timer]
OnUnitActiveSec=60minutesStart the application
And the whole application can be now started:
systemctl --user start invidious
Keep in mind that generated units cannot be enabled using systemctl --user enable. The Invidious containers will be started automatically.
Proxmox VMs and LXCs
Guide contributor(s): @SimplyProgrammer
This method provides a simple and straightforward guide with scripts to setup Invidious inside a Proxmox Linux VM or LXC container.
It utilizes podman to create Invidious containers, Caddy 2 for self-signed https reverse proxy and Linux service for automatic startup.
Prerequirements
Debian 12+ VM or an unprivileged Debian 12+ LXC container with:
- Name "invidious" (or something similar)
- 4GB+ of RAM
- 2+ x86_64 cores, 4 is ideal (ARM is also possible but
compose.ymlhas to be modified accordingly). - 20+GB of storage
- Static IP or static DHCP lease on your router.
Networking
Further, you will need a DNS domain, paid or local one to point to this IP of your LXC/VM with Invidious.
It would be great if your router would allow/do this by default, and automatically assign something like invidious.lan or even better, invidious.home.arpa to your LXC/VM. For example, OpenWrt does this by default. For other routers, extra config might be needed.
If you have your own domain, reversed-proxy etc., setup might differ for you...
Installation
Run this script (with sudo privileges or as root):
mkdir -p ~/invidious-podman && cd "$_" && git clone "https://github.com/SimplyProgrammer/Invidious-podman-LXC-VM.git" . && chmod 755 setup-invidious.sh && ./setup-invidious.shWhen it asks for the Invidious git repo, for 90% of you just press enter (unless you have your own forked one).
The most important part, when it asks for the DNS domain, specify aforementioned invidious.lan, invidious.home.arpa or whatever you have. But make sure the domain is reachable and working in advance (nslookup etc.).
If everything was done properly, now you should have a working LXC/VM with Invidious service and should be able to open it at https://<your-invidious-domain>/.
It should automatically start with your VM/LXC, no extra work needed.
More info and the whole solution is available for review at https://github.com/SimplyProgrammer/Invidious-podman-LXC-VM.
MacOS
Looking for contributors to this operating system. Please submit a new doc for MacOS at https://github.com/iv-org/documentation/blob/master/docs/community-installation-guide.md
BSD (FreeBSD - OpenBSD)
People have successfully ran Invidious on FreeBSD/OpenBSD as discussed in https://github.com/iv-org/invidious/issues/2388
We are looking for contributors to write a proper guide for installing Invidious on FreeBSD/OpenBSD. If you can, please submit a new doc for FreeBSD/OpenBSD at https://github.com/iv-org/documentation/blob/master/docs/community-installation-guide.md