Skip to content

ActivityPub

Each PeerTube instance is able to fetch video from other compatible servers it follows, in a process known as “federation”. Federation is implemented using the ActivityPub protocol, in order to leverage existing tools and be compatible with other services such as Mastodon, Pleroma and many more.

Federation in PeerTube is twofold: videos metadata are shared as activities for inter-server communication in what amounts to sharing parts of one's database, and user interaction via comments which are compatible with the kind of activity textual platforms like Mastodon use.

Supported Activities

Supported Objects

Follow

Follow is an activity standardized in the ActivityPub specification (see Follow Activity). The Follow activity is used to subscribe to the activities of another actor (a server subscribing to another server's videos, a user subscribing to another user's videos).

Supported on
  • Actor (peertube actor, account actor or channel actor)

Accept

Supported on
  • Follow

Reject

Reject is an activity standardized in the ActivityPub specification (see Reject Activity).

Supported on
  • Follow

Undo

Undo is an activity standardized in the ActivityPub specification (see Undo Activity). The Undo activity is used to undo a previous activity.

Supported on

Like

Like is an activity standardized in the ActivityPub specification (see Like Activity).

Supported on

Dislike

Dislike is an activity standardized in the ActivityStream specification (see Dislike Activity).

Supported on

Update

Update is an activity standardized in the ActivityPub specification (see Update Activity). The Update activity is used when updating an already existing object.

Supported on

Create

Create is an activity standardized in the ActivityPub specification (see Create Activity). The Create activity is used when posting a new object. This has the side effect that the object embedded within the Activity (in the object property) is created.

Supported on

Example

json
{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://w3id.org/security/v1",
    {}
  ],
  "to": ["https://peertube2.cpy.re/accounts/root/activity"],
  "type": "Create",
  "actor": "https://peertube2.cpy.re/accounts/root",
  "object": {}
}

Delete

Delete is an activity standardized in the ActivityPub specification (see Delete Activity).

Supported on

Announce

Announce is an activity standardized in the ActivityPub specification (see Announce Activity).

Supported on

Example

json
{
  "type": "Announce",
  "id": "https://peertube2.cpy.re/videos/watch/997111d4-e8d8-4f45-99d3-857905785d05/announces/1",
  "actor": "https://peertube2.cpy.re/accounts/root",
  "object": "https://peertube2.cpy.re/videos/watch/997111d4-e8d8-4f45-99d3-857905785d05",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public",
    "https://peertube2.cpy.re/accounts/root/followers",
    "https://peertube2.cpy.re/video-channels/root_channel/followers"
  ],
  "cc": []
}

Flag

Flag is an activity standardized in the ActivityStream specification (see Flag Activity).

Supported on

View

View is an activity standardized in the ActivityStream specification (see View Activity).

A PeerTube instance sends a View activity every time a user watched a video so the origin server can increment video views counter.

If the View activity includes an expires attribute, it means a user is currently watching a video. This kind of event is sent periodically until the user stops watching the video. The same View action can be sent multiple times using a different expires attribute, meaning the user is still watching the video.

Supported on

Supported Objects

Video

INFO

This object extends the ActivityPub specification, and therefore some properties are not part of it.

Structure

The model structure definition lies in packages/models/src/activitypub/objects/video-object.ts.

ts
import { LiveVideoLatencyModeType, VideoStateType } from '../../videos/index.js'
import {
  ActivityIconObject,
  ActivityIdentifierObject,
  ActivityPubAttributedTo,
  ActivityTagObject,
  ActivityUrlObject
} from './common-objects.js'
import { VideoChapterObject } from './video-chapters-object.js'

export interface VideoObject {
  type: 'Video'
  id: string
  name: string
  duration: string
  uuid: string
  tag: ActivityTagObject[]
  category: ActivityIdentifierObject
  licence: ActivityIdentifierObject
  language: ActivityIdentifierObject
  subtitleLanguage: ActivityIdentifierObject[]

  views: number

  sensitive: boolean

  isLiveBroadcast: boolean
  liveSaveReplay: boolean
  permanentLive: boolean
  latencyMode: LiveVideoLatencyModeType

  commentsEnabled: boolean
  downloadEnabled: boolean
  waitTranscoding: boolean
  state: VideoStateType

  published: string
  originallyPublishedAt: string
  updated: string
  uploadDate: string

  mediaType: 'text/markdown'
  content: string

  support: string

  icon: ActivityIconObject[]

  url: ActivityUrlObject[]

  likes: string
  dislikes: string
  shares: string
  comments: string
  hasParts: string | VideoChapterObject[]

  attributedTo: ActivityPubAttributedTo[]

  preview?: ActivityPubStoryboard[]

  to?: string[]
  cc?: string[]

  // For export
  attachment?: {
    type: 'Video'
    url: string
    mediaType: string
    height: number
    size: number
    fps: number
  }[]
}

export interface ActivityPubStoryboard {
  type: 'Image'
  rel: [ 'storyboard' ]
  url: {
    href: string
    mediaType: string
    width: number
    height: number
    tileWidth: number
    tileHeight: number
    tileDuration: string
  }[]
}

Example

A Video object could be complex depending on transcoding settings. Here is an example with some comments:

JSON
{
  "type": "Video",
  "id": "https://peertube2.cpy.re/videos/watch/...",
  "name": "HLS test 1",
  "duration": "PT730S",
  "uuid": "969bf103-7818-43b5-94a0-...",
  "tag": [
    {
      "type": "Hashtag",
      "name": "tagexample"
    }
  ],
  "views": 35,
  "sensitive": false,
  "waitTranscoding": true,
  "isLiveBroadcast": false,
  // If this is a live, tell if the user will save a replay or not
  "liveSaveReplay": null,
  // If this is a live, tell if this is a permanent live or not
  "permanentLive": null,
  // See the REST API documentation: https://docs.joinpeertube.org/api-rest-reference.html#operation/getVideo
  "state": 1,
  "commentsEnabled": true,
  "downloadEnabled": true,
  "published": "2019-04-17T08:55:11.871Z",
  "originallyPublishedAt": null,
  "updated": "2020-12-15T11:01:02.477Z",
  "mediaType": "text/markdown",
  "content": "The story of programming prodigy and information activist Aaron Swartz, who took his own life at the age of 26.",
  // How to support the uploaded/content creator
  "support": "Pay me a coffee when you see me",
  "subtitleLanguage": [
    {
      "identifier": "ca",
      "name": "Catalan",
      "url": "https://peertube2.cpy.re/lazy-static/video-captions/...-ca.vtt"
    }
  ],
  "category": {
    "identifier": "2", // Internal PeerTube ID
    "name": "Films"
  },
  "licence": {
    "identifier": "5", // Internal PeerTube ID
    "name": "Attribution - Non Commercial - Share Alike"
  },
  // Optional
  "language": {
    "identifier": "en",
    "name": "English"
  },
  "icon": [
    {
      "type": "Image",
      "url": "https://peertube2.cpy.re/static/thumbnails/....jpg",
      "mediaType": "image/jpeg",
      "width": 200,
      "height": 110
    },
    {
      "type": "Image",
      "url": "https://peertube2.cpy.re/lazy-static/previews/....jpg",
      "mediaType": "image/jpeg",
      "width": 560,
      "height": 315
    }
  ],
  "url": [
    // Webpage
    {
      "type": "Link",
      "mediaType": "text/html",
      "href": "https://peertube2.cpy.re/videos/watch/969bf103-7818-43b5-94a0-de159e13de50"
    },

    // Raw URL to web video compatible mp4 file (only if Web Video transcoding is enabled)
    {
      "type": "Link",
      "mediaType": "video/mp4",
      "href": "https://peertube2.cpy.re/static/webseed/969bf103-7818-43b5-94a0-de159e13de50-536.mp4",
      "height": 536,
      "size": 135239407,
      "fps": 24
    },
    // URL to fetch video file metadata (only if Web Video transcoding is enabled)
    {
      "type": "Link",
      "rel": [
        "metadata",
        "video/mp4"
      ],
      "mediaType": "application/json",
      "href": "https://peertube2.cpy.re/api/v1/videos/969bf103-7818-43b5-94a0-de159e13de50/metadata/1642209",
      "height": 536,
      "fps": 24
    },
    // Torrent file URL (only if Web Video transcoding is enabled)
    {
      "type": "Link",
      "mediaType": "application/x-bittorrent",
      "href": "https://peertube2.cpy.re/static/torrents/969bf103-7818-43b5-94a0-de159e13de50-536.torrent",
      "height": 536
    },
    // Magnet URI (only if Web Video transcoding is enabled)
    {
      "type": "Link",
      "mediaType": "application/x-bittorrent;x-scheme-handler/magnet",
      "href": "magnet:?xs=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Ftorrents%2F969bf103-7818-43b5-94a0-de159e13de50-536.torrent&xt=urn:btih:673aaa764ad4ba61aa5b50306a5fd77fdbd4e78e&dn=HLS+test+1&tr=wss%3A%2F%2Fpeertube2.cpy.re%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube2.cpy.re%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Fwebseed%2F969bf103-7818-43b5-94a0-de159e13de50-536.mp4",
      "height": 536
    },

    // HLS playlist URL (only if HLS transcoding is enabled)
    {
      "type": "Link",
      "mediaType": "application/x-mpegURL",
      "href": "https://peertube2.cpy.re/static/streaming-playlists/hls/969bf103-7818-43b5-94a0-de159e13de50/master.m3u8",
      "tag": [
        // Infohashes for p2p-media-loader of every resolution
        {
          "type": "Infohash",
          "name": "d7844378e5a6b9af2d45267c0e413688e7839918"
        },
        // URL to a JSON that contains the playlist's segments sha sum
        {
          "type": "Link",
          "name": "sha256",
          "mediaType": "application/json",
          "href": "https://peertube2.cpy.re/static/streaming-playlists/hls/969bf103-7818-43b5-94a0-de159e13de50/segments-sha256.json"
        },
        // Raw URL to the fragmented mp4 file used by the HLS playlist
        {
          "type": "Link",
          "mediaType": "video/mp4",
          "href": "https://peertube2.cpy.re/static/streaming-playlists/hls/969bf103-7818-43b5-94a0-de159e13de50/969bf103-7818-43b5-94a0-de159e13de50-536-fragmented.mp4",
          "height": 536,
          "size": 135108145,
          "fps": 24
        },
        // URL to fetch video file metadata
        {
          "type": "Link",
          "rel": [
            "metadata",
            "video/mp4"
          ],
          "mediaType": "application/json",
          "href": "https://peertube2.cpy.re/api/v1/videos/969bf103-7818-43b5-94a0-de159e13de50/metadata/3649370",
          "height": 536,
          "fps": 24
        },
        // Fragmented mp4 file torrent URL
        {
          "type": "Link",
          "mediaType": "application/x-bittorrent",
          "href": "https://peertube2.cpy.re/static/torrents/969bf103-7818-43b5-94a0-de159e13de50-536-hls.torrent",
          "height": 536
        },
        // Fragmented mp4 file magnet URI
        {
          "type": "Link",
          "mediaType": "application/x-bittorrent;x-scheme-handler/magnet",
          "href": "magnet:?xs=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Ftorrents%2F969bf103-7818-43b5-94a0-de159e13de50-536-hls.torrent&xt=urn:btih:e629524f99aa27bc2b42d05b22ac318cc22cfcf7&dn=HLS+test+1&tr=wss%3A%2F%2Fpeertube2.cpy.re%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube2.cpy.re%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Fstreaming-playlists%2Fhls%2F969bf103-7818-43b5-94a0-de159e13de50%2F969bf103-7818-43b5-94a0-de159e13de50-536-fragmented.mp4",
          "height": 536
        }
      ]
    }
  ],
  "likes": "https://peertube2.cpy.re/videos/watch/969bf103-7818-43b5-94a0-de159e13de50/likes",
  "dislikes": "https://peertube2.cpy.re/videos/watch/969bf103-7818-43b5-94a0-de159e13de50/dislikes",
  "shares": "https://peertube2.cpy.re/videos/watch/969bf103-7818-43b5-94a0-de159e13de50/announces",
  "comments": "https://peertube2.cpy.re/videos/watch/969bf103-7818-43b5-94a0-de159e13de50/comments",
  "attributedTo": [
    // The account
    {
      "type": "Person",
      "id": "https://peertube2.cpy.re/accounts/root"
    },
    // The channel
    {
      "type": "Group",
      "id": "https://peertube2.cpy.re/video-channels/a75cbdf4-acf2-45c0-a491-e9b28939f8db"
    }
  ],
  "to": [],
  "cc": [],
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://w3id.org/security/v1",
    ...
  ]

Playlist

INFO

This object is not standard to the ActivityPub specification.

The model structure definition lies in packages/models/src/activitypub/objects/playlist-object.ts.

ts
import { ActivityIconObject, ActivityPubAttributedTo } from './common-objects.js'

export interface PlaylistObject {
  id: string
  type: 'Playlist'

  name: string

  content: string
  mediaType: 'text/markdown'

  uuid: string

  totalItems: number
  attributedTo: ActivityPubAttributedTo[]

  icon?: ActivityIconObject

  published: string
  updated: string

  orderedItems?: string[]

  partOf?: string
  next?: string
  first?: string

  to?: string[]
}

CacheFile

INFO

This object is not standard to the ActivityPub specification.

The object is used to represent a cached file. It is usually sent by third-party servers to the origin server hosting a video file (a resolution from a Video), to notify it that they have put up a copy of that file. The origin server should then add the server emitting the CacheFile to the list of WebSeeds for that file.

Structure

The model structure definition lies in packages/models/src/activitypub/objects/cache-file-object.ts.

ts
import { ActivityVideoUrlObject, ActivityPlaylistUrlObject } from './common-objects.js'

export interface CacheFileObject {
  id: string
  type: 'CacheFile'
  object: string
  expires: string
  url: ActivityVideoUrlObject | ActivityPlaylistUrlObject
}

Note

A Note is usually a comment made to a video. Since most ActivityPub textual platforms use the Note object for their messages, most of them can interact in the same way with PeerTube videos, making them able to comment PeerTube videos directly!

Structure

The model structure definition lies in packages/models/src/activitypub/objects/video-comment-object.ts.

ts
import { ActivityPubAttributedTo, ActivityTagObject } from './common-objects.js'

export interface VideoCommentObject {
  type: 'Note'
  id: string

  content: string
  mediaType: 'text/markdown'

  inReplyTo: string
  published: string
  updated: string
  url: string
  attributedTo: ActivityPubAttributedTo
  tag: ActivityTagObject[]
}

Playlist

INFO

This object is not standard to the ActivityPub specification.

The object is used to represent a video playlist. It extends OrderedCollectionPage and items are PlaylistElement.

Structure

The model structure definition lies in packages/models/src/activitypub/objects/playlist-object.ts.

ts
import { ActivityIconObject, ActivityPubAttributedTo } from './common-objects.js'

export interface PlaylistObject {
  id: string
  type: 'Playlist'

  name: string

  content: string
  mediaType: 'text/markdown'

  uuid: string

  totalItems: number
  attributedTo: ActivityPubAttributedTo[]

  icon?: ActivityIconObject

  published: string
  updated: string

  orderedItems?: string[]

  partOf?: string
  next?: string
  first?: string

  to?: string[]
}

Playlist Element

INFO

This object is not standard to the ActivityPub specification.

The object is used to represent a video playlist element inside the Playlist object.

Structure

The model structure definition lies in packages/models/src/activitypub/objects/playlist-element-object.ts.

ts
export interface PlaylistElementObject {
  id: string
  type: 'PlaylistElement'

  url: string
  position: number

  startTimestamp?: number
  stopTimestamp?: number
}

Watch Action PeerTube >= 4.2

INFO

This object is not standard to the ActivityPub specification. It comes from Schema.org

The object is used to represent a viewer that watched a video. Allows origin server to aggregate viewer statistics.

Structure

The model structure definition lies in packages/models/src/activitypub/objects/watch-action-object.ts.

ts
export interface WatchActionObject {
  id: string
  type: 'WatchAction'

  startTime: string
  endTime: string

  location?: {
    addressCountry: string
    addressRegion: string
  }

  uuid: string
  object: string
  actionStatus: 'CompletedActionStatus'

  duration: string

  watchSections: {
    startTimestamp: number
    endTimestamp: number
  }[]
}