omnipost / youtube
Omnipost YouTube: the channel's videos through the Data API (a key), and Shorts uploaded through OAuth.
Requires
- php: >=8.2
- glitchr/omnipost: ^1.0@dev
- symfony/http-client: ^6.4|^7.0|^8.0
Requires (Dev)
- phpunit/phpunit: ^11.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-03 16:12:11 UTC
README
YouTube for glitchr/omnipost: the channel and its videos read with an API key, videos and Shorts uploaded through OAuth - the YouTube Data API v3.
omnipost: providers: youtube: factory: youtube options: api_key: '%env(default::YOUTUBE_API_KEY)%' # reading: the channel, its videos channel_id: '%env(default::YOUTUBE_CHANNEL_ID)%' # UC...; without it, the channel the OAuth token belongs to client_id: '%env(default::YOUTUBE_CLIENT_ID)%' # the OAuth client: uploading, deleting client_secret: '%env(default::YOUTUBE_CLIENT_SECRET)%' refresh_token: '%env(default::YOUTUBE_REFRESH_TOKEN)%' # what exchange() gave privacy: private # private, unlisted, public category_id: ~ # "10" is Music
No option is required to build the provider: capabilities() and authorizationUrl() need no
credential, and a call that needs one and finds none throws InvalidConfigException.
Reading, with a key
account(): the channel (its handle, title, picture, subscribers unless hidden, videos count).
feed($pageToken, $limit): the channel's uploads playlist, newest first, then the videos for
their durations (ISO 8601, PT1M30S) and views. The API does not tell a Short nor the video's
ratio: a video of three minutes at most is given as REEL, a longer one as VIDEO. permalink is the
watch page, url the embed player, thumbnailUrl the largest thumbnail.
Uploading, with OAuth
$publication = $youtube->publish($post->for(Platform::YOUTUBE)); // PROCESSING, the video's id, its watch URL $youtube->status($publication->id); // PUBLISHED once processed; FAILED with the reason $youtube->delete($publication->id);
publish() checks the post (a title is required, 100 characters at most; the caption - with its
hashtags - is the description, 5 000 at most; a REEL lasts three minutes at most), then uploads in
two calls (a resumable upload): the metadata, then the bytes - from Media::$path, or brought
from Media::$url to a temporary file when there is no local one. A Short is a video like any
other: vertical (or square) and three minutes at most, YouTube files it as a Short itself.
A post scheduled (withScheduledAt()) goes up private with its publishAt, and YouTube publishes
it then (PENDING until that time). The access token is drawn from the refresh token and kept for
its hour.
Uploads are private until the project is audited. The videos uploaded by an API project that
has not passed Google's compliance audit are locked private, whatever privacy says: the artist
makes them public in YouTube Studio. That is why privacy defaults to private; the audit (a form,
the site's terms and privacy policy, a few weeks) lifts it.
What it takes
- A Google Cloud project with the YouTube Data API v3 enabled.
- To read: an API key (APIs & Services → Credentials) and the channel's id (
UC..., in YouTube Studio → Settings → Channel → Advanced, or the channel's URL). - To upload: an OAuth client (type Web application, the site's redirect URI declared), the
OAuth consent screen with the scopes
youtube.uploadandyoutube.readonly, and the artist's account among its test users while the app is in Testing - a refresh token of an app in Testing dies after 7 days: publish the consent screen ("In production") for one that lasts. ThenauthorizationUrl()(offline access, consent asked again: that is what gives a refresh token) andexchange(); the refresh token is therefresh_tokenoption. A revoked one is anUnauthorizedException: the channel is connected again. - Quota: 10 000 units a day per project by default. A read costs 1 unit; an upload is the expensive call - about 100 units now, so some 100 uploads a day (check Google's quota calculator: it cost 1 600 until recently). More is asked for in the Cloud console.
License: LGPL-3.0-or-later.