peertube

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jul 9, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

README

peertube

A small, focused Go client for uploading videos to a PeerTube instance, plus a matching CLI. Built against the PeerTube 8.1 OpenAPI spec (openapi.json).

It deliberately implements only what's needed to publish a video and manage the channel it lives in:

  • OAuth2 password-grant login, token refresh, and 2FA/OTP (/api/v1/users/token).
  • Legacy single-request upload (POST /api/v1/videos/upload).
  • Resumable chunked upload (POST/PUT /api/v1/videos/upload-resumable), following the node-uploadx protocol PeerTube uses.
  • Channel management: list, create, and set avatar/banner images.
go get github.com/ernado/peertube

Library

package main

import (
	"context"
	"log"
	"os"

	"github.com/ernado/peertube"
)

func main() {
	ctx := context.Background()

	c, err := peertube.NewClient("https://peertube.example.org")
	if err != nil {
		log.Fatal(err)
	}
	if _, err := c.Login(ctx, "alice", "secret"); err != nil {
		log.Fatal(err)
	}

	f, err := os.Open("video.mp4")
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	info, _ := f.Stat()

	// Resumable upload: survives transient failures, good for large files.
	res, err := c.UploadResumable(ctx, peertube.UploadParams{
		Name:      "My video",
		ChannelID: 3,
		Privacy:   peertube.PrivacyPublic,
		Tags:      []string{"go", "peertube"},
	}, "video.mp4", f, info.Size())
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("uploaded: uuid=%s", res.UUID)
}

For small files there is also Upload (single multipart request):

res, err := c.Upload(ctx, params, "video.mp4", f)
Channels
// Discover channels (useful to find a ChannelID for uploads).
channels, err := c.MyChannels(ctx)

// Create a channel.
ch, err := c.CreateChannel(ctx, peertube.CreateChannelParams{
	Name:        "my_channel", // immutable handle
	DisplayName: "My Channel",
})

// Set its avatar / banner (PNG or JPEG).
avatar, _ := os.Open("avatar.png")
defer avatar.Close()
_, err = c.SetChannelAvatar(ctx, ch.Name, "avatar.png", avatar)
Testability

The client talks to any Doer (Do(*http.Request) (*http.Response, error)), which *http.Client satisfies. Inject a stub or an httptest.Server to test without touching the network:

c, _ := peertube.NewClient("https://x", peertube.WithHTTPClient(myDoer))

Non-2xx responses are returned as *peertube.APIError carrying the HTTP status and PeerTube error code (e.g. quota_reached, invalid_grant):

var apiErr *peertube.APIError
if errors.As(err, &apiErr) && apiErr.Code == "quota_reached" {
	// ...
}

CLI

The CLI is built with cobra and shows an upload progress bar via schollz/progressbar.

go install github.com/ernado/peertube/cmd/peertube@latest

# Save credentials once (verified against the instance, stored in the config).
# Prompts for username/password if not passed via flags or environment.
peertube login --url https://peertube.example.org

# Then commands work without repeating credentials.
peertube channel list
peertube upload --file video.mp4 --name "My video" --tags go,peertube

Or pass everything inline (no login required):

peertube upload \
  --url https://peertube.example.org \
  --username alice --password secret \
  --file video.mp4 --name "My video"

Commands:

Command Purpose
peertube upload Upload a video.
peertube login Verify and persist credentials; prompts for missing username/password; --default sets the default instance.
peertube channel list List the authenticated user's channels.
peertube channel create Create a video channel (--name, --display-name, optional --avatar/--banner).
peertube channel set-avatar / set-banner Upload an avatar/banner image (--channel, --file).

Credential resolution, highest precedence first:

  1. --url / --username / --password flags.
  2. PEERTUBE_USER / PEERTUBE_PASSWORD environment variables.
  3. Saved config (login) — os.UserConfigDir()/peertube/config.json, written with 0600 since it holds the password. The default instance supplies --url when omitted.
  4. login additionally prompts on the terminal for any username/password still missing (password input is hidden).

Other notes:

  • Channel auto-discovery: omit --channel-id and the CLI picks your channel automatically when the account has exactly one; with several it lists them and asks you to choose.
  • Uploads are resumable by default (with a progress bar); pass --legacy for a single request.

Run peertube --help or peertube <command> --help for all flags.

License

See LICENSE.

Documentation

Overview

Package peertube is a small, focused client for uploading videos to a PeerTube instance (https://joinpeertube.org).

It implements exactly what is needed to authenticate and publish a video:

  • OAuth2 login (password grant) against /api/v1/users/token.
  • Legacy single-request upload (POST /api/v1/videos/upload).
  • Resumable chunked upload (POST/PUT /api/v1/videos/upload-resumable), following the node-uploadx protocol used by PeerTube.

The client is transport-agnostic: it talks to anything implementing Doer (which *http.Client satisfies), so it is trivial to unit test against an httptest.Server or an in-memory mock.

Typical usage:

c, err := peertube.NewClient("https://peertube.example.org")
if err != nil {
	return err
}
if _, err := c.Login(ctx, "user", "secret"); err != nil {
	return err
}
f, err := os.Open("video.mp4")
if err != nil {
	return err
}
defer f.Close()
res, err := c.Upload(ctx, peertube.UploadParams{
	Name:      "My video",
	ChannelID: 3,
	Privacy:   peertube.PrivacyPublic,
}, "video.mp4", f)
if err != nil {
	return err
}
fmt.Println("uploaded", res.UUID)

Index

Constants

View Source
const DefaultChunkSize = 5 << 20

DefaultChunkSize is the chunk size used by UploadResumable when none is set. It must be a multiple of 1024 (a node-uploadx requirement for non-final chunks); 5 MiB is a good default balance of overhead and retry cost.

Variables

This section is empty.

Functions

This section is empty.

Types

type APIError

type APIError struct {
	// Status is the HTTP status code.
	Status int
	// Code is the machine-readable error code when present
	// (e.g. "quota_reached", "invalid_grant", "max_file_size_reached").
	Code string
	// Detail is a human-readable message.
	Detail string
	// Body is the raw response body (truncated), for diagnostics.
	Body string
}

APIError is returned when the PeerTube API responds with a non-2xx status.

PeerTube reports errors either as a plain body or as an RFC 7807 problem-details document; the fields below are populated best-effort.

func (*APIError) Error

func (e *APIError) Error() string

Error implements error.

type ActorImage

type ActorImage struct {
	// FileURL is the image URL (PeerTube >= 7.1).
	FileURL string `json:"fileUrl"`
	// Path is the legacy image path (deprecated in favor of FileURL).
	Path   string `json:"path"`
	Width  int    `json:"width"`
	Height int    `json:"height"`
}

ActorImage describes an uploaded avatar or banner image.

type Channel

type Channel struct {
	// ID is the numeric channel id used as UploadParams.ChannelID.
	ID int `json:"id"`
	// Name is the channel handle (e.g. "my_channel").
	Name string `json:"name"`
	// DisplayName is the human-friendly channel name.
	DisplayName string `json:"displayName"`
}

Channel is a video channel owned by the authenticated user.

type Client

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

Client is a PeerTube API client scoped to a single instance.

A Client is safe for concurrent use as long as the access token is not mutated concurrently (Login and SetToken are not synchronized).

func NewClient

func NewClient(baseURL string, opts ...Option) (*Client, error)

NewClient returns a Client for the PeerTube instance at baseURL (e.g. "https://peertube.example.org"). The /api/v1 prefix is added automatically, so it must not be part of baseURL.

func (*Client) CreateChannel

func (c *Client) CreateChannel(ctx context.Context, p CreateChannelParams) (Channel, error)

CreateChannel creates a video channel owned by the authenticated user (POST /api/v1/video-channels) and returns it with the server-assigned ID.

func (*Client) Login

func (c *Client) Login(ctx context.Context, username, password string, opts ...LoginOptions) (Token, error)

Login performs the OAuth2 password grant and, on success, stores the access token on the client so subsequent uploads are authenticated. The obtained token is also returned so callers may persist the refresh token.

func (*Client) MyChannels

func (c *Client) MyChannels(ctx context.Context) ([]Channel, error)

MyChannels returns the video channels owned by the authenticated user (GET /api/v1/users/me). It is useful to discover a ChannelID for uploads.

func (*Client) OAuthClient

func (c *Client) OAuthClient(ctx context.Context) (OAuthClient, error)

OAuthClient fetches the instance's local OAuth client id/secret (GET /oauth-clients/local). Login calls this automatically; it is exported for callers who cache credentials.

func (*Client) Refresh

func (c *Client) Refresh(ctx context.Context, client OAuthClient, refreshToken string) (Token, error)

Refresh exchanges a refresh token for a new token pair and stores the new access token on the client.

func (*Client) SetChannelAvatar

func (c *Client) SetChannelAvatar(ctx context.Context, handle, filename string, image io.Reader) ([]ActorImage, error)

SetChannelAvatar uploads an avatar image (PNG or JPEG) for the channel with the given handle (POST /video-channels/{handle}/avatar/pick). It returns the generated avatar variants.

func (*Client) SetChannelBanner

func (c *Client) SetChannelBanner(ctx context.Context, handle, filename string, image io.Reader) ([]ActorImage, error)

SetChannelBanner uploads a banner image (PNG or JPEG) for the channel with the given handle (POST /video-channels/{handle}/banner/pick). It returns the generated banner variants.

func (*Client) SetToken

func (c *Client) SetToken(token string)

SetToken sets the OAuth2 access token used to authenticate requests.

func (*Client) Token

func (c *Client) Token() string

Token returns the access token currently in use (empty if not authenticated).

func (*Client) Upload

func (c *Client) Upload(ctx context.Context, params UploadParams, filename string, video io.Reader) (*UploadedVideo, error)

Upload publishes a video in a single multipart request (POST /api/v1/videos/upload).

It is the simplest path and is well suited to small/medium files. For large files or unreliable networks prefer Client.UploadResumable, which can resume after an interruption.

filename is the original file name (used to derive the content type on the server); video streams the file contents. The body is streamed, not buffered, so memory use stays constant regardless of file size.

func (*Client) UploadResumable

func (c *Client) UploadResumable(
	ctx context.Context,
	params UploadParams,
	filename string,
	video io.Reader,
	size int64,
	opts ...ResumableOptions,
) (*UploadedVideo, error)

UploadResumable publishes a video using PeerTube's resumable (chunked) protocol. Unlike Client.Upload it needs the total size up front and sends the file in chunks, which lets large uploads survive transient failures.

The total size must be known and video is read sequentially. If a chunk fails, the server-side session is canceled (best-effort) so it does not linger.

type CommentsPolicy

type CommentsPolicy int

CommentsPolicy controls who may comment on a video (VideoCommentsPolicySet).

const (
	CommentsEnabled         CommentsPolicy = 1
	CommentsDisabled        CommentsPolicy = 2
	CommentsRequireApproval CommentsPolicy = 3
)

Comment policies.

type CreateChannelParams

type CreateChannelParams struct {
	// Name is the immutable channel handle (1..50 chars, [a-zA-Z0-9-_.:]).
	// Required.
	Name string
	// DisplayName is the human-friendly name. Required.
	DisplayName string
	// Description and Support are optional free text.
	Description string
	Support     string
}

CreateChannelParams describes a new video channel.

type Doer

type Doer interface {
	Do(req *http.Request) (*http.Response, error)
}

Doer executes HTTP requests. *http.Client implements it.

Depending on it (instead of a concrete *http.Client) keeps the client testable: tests can supply a stub that returns canned responses without touching the network.

type LoginOptions

type LoginOptions struct {
	// OTP is the two-factor code, required when the account has 2FA enabled.
	OTP string
	// Client, when set, is used instead of fetching /oauth-clients/local.
	Client *OAuthClient
}

LoginOptions tunes the login request.

type OAuthClient

type OAuthClient struct {
	ClientID     string `json:"client_id"`
	ClientSecret string `json:"client_secret"`
}

OAuthClient holds the local OAuth client credentials required before login.

type Option

type Option func(*Client)

Option configures a Client.

func WithHTTPClient

func WithHTTPClient(d Doer) Option

WithHTTPClient sets the underlying HTTP transport. Use it to inject timeouts, custom transports, or a mock in tests. Defaults to http.DefaultClient.

func WithToken

func WithToken(token string) Option

WithToken presets the OAuth2 access token, skipping the need to Login (for example when reusing a previously obtained token).

type Privacy

type Privacy int

Privacy is the visibility of a video (VideoPrivacySet in the API).

const (
	PrivacyPublic            Privacy = 1
	PrivacyUnlisted          Privacy = 2
	PrivacyPrivate           Privacy = 3
	PrivacyInternal          Privacy = 4
	PrivacyPasswordProtected Privacy = 5
)

Video privacy levels.

type ResumableOptions

type ResumableOptions struct {
	// ChunkSize is the size of each PUT chunk in bytes. It must be a positive
	// multiple of 1024. Zero uses DefaultChunkSize.
	ChunkSize int64
	// ContentType is the video MIME type (e.g. "video/mp4"). When empty it is
	// inferred from the filename extension, defaulting to
	// "application/octet-stream".
	ContentType string
}

ResumableOptions tunes a resumable upload.

type Token

type Token struct {
	TokenType             string `json:"token_type"`
	AccessToken           string `json:"access_token"`
	RefreshToken          string `json:"refresh_token"`
	ExpiresIn             int    `json:"expires_in"`
	RefreshTokenExpiresIn int    `json:"refresh_token_expires_in"`
}

Token is an OAuth2 token pair returned by Login.

type UploadParams

type UploadParams struct {
	// Name is the video title (3..120 chars). Required.
	Name string
	// ChannelID is the channel that will own the video. Required.
	ChannelID int

	// Privacy is the visibility. Zero means "let the server decide".
	Privacy Privacy
	// Category, Licence identifiers as defined by the instance (0 = unset).
	Category int
	Licence  int
	// Language is an ISO 639 code (e.g. "en"); empty = unset.
	Language string

	Description string
	Support     string
	// Tags: up to 5, each 2..30 chars.
	Tags []string

	// Pointer booleans distinguish "unset" from an explicit false.
	NSFW                  *bool
	WaitTranscoding       *bool
	GenerateTranscription *bool
	DownloadEnabled       *bool

	CommentsPolicy CommentsPolicy
	// OriginallyPublishedAt is an RFC 3339 timestamp; empty = unset.
	OriginallyPublishedAt string
}

UploadParams holds the metadata common to both the legacy and resumable uploads. Only Name and ChannelID are required; zero-valued optional fields are omitted from the request so the instance defaults apply.

type UploadedVideo

type UploadedVideo struct {
	ID        int    `json:"id"`
	UUID      string `json:"uuid"`
	ShortUUID string `json:"shortUUID"`
}

UploadedVideo identifies a successfully uploaded video (VideoUploadResponse).

Directories

Path Synopsis
cmd
peertube command
Command peertube is a minimal CLI to log in to a PeerTube instance and upload videos, built on the github.com/ernado/peertube library.
Command peertube is a minimal CLI to log in to a PeerTube instance and upload videos, built on the github.com/ernado/peertube library.

Jump to

Keyboard shortcuts

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