custom-media: import spec and fix a minor missed validation

This commit is contained in:
Calvin Montgomery 2017-08-08 20:46:10 -07:00
parent 04c9d48779
commit 92f0a956b9
3 changed files with 181 additions and 2 deletions

160
docs/custom-media.md Normal file
View File

@ -0,0 +1,160 @@
CyTube Custom Content Metadata
==============================
Last Updated: 2017-08-08
Status: Experimental
## Purpose ##
CyTube currently supports adding custom audio/video content by allowing the user
to supply a direct URL to an audio/video file. The server uses `ffprobe` to
probe the file for various metadata, including the codec/container format and
the duration. This approach has a few disadvantages over the officially
supported media providers, namely:
* Since it accepts a single file, it is not possible to provide multiple
source URLs with varying formats or bitrates to allow viewers to select the
best source for their computer.
- It also means it is not possible to provide text tracks for subtitles or
closed captioning, or to provide image URLs for thumbnails/previews.
* Probing the file with `ffprobe` is slow, especially if the content is hosted
in a far away network location, which at best is inconvenient and at worst
results in timeouts and inability to add the content.
* Parsing the `ffprobe` output is inexact, and may sometimes result in
detecting the wrong format, or failing to detect the title.
This document specifies a new supported media provider which allows users to
provide a JSON manifest specifying the metadata for custom content in a way that
avoids the above issues and is more flexible for extension.
## Manifest Format ##
To add custom content, the user provides a JSON object with the following keys:
* `title`: A nonempty string specifying the title of the content. For legacy
reasons, CyTube currently truncates this to 100 UTF-8 characters.
* `duration`: A non-negative, finite number specifying the duration, in
seconds, of the content. This is what the server will use for timing
purposes. Decimals are allowed, but CyTube's timer truncates the value as
an integer number of seconds, so including fractional seconds lends no
advantage.
* `live`: An optional boolean (default: `false`) indicating whether the
content is live or pre-recorded. For live content, the `duration` is
ignored, and the server won't advance the playlist automatically.
* `thumbnail`: An optional string specifying a URL for a thumbnail image of
the content. CyTube currently does not support displaying thumbnails in the
playlist, but this functionality may be offered in the future.
* `sources`: A nonempty list of playable sources for the content. The format
is described below.
* `textTracks`: An optional list of text tracks for subtitles or closed
captioning. The format is described below.
### Source Format ###
Each source entry is a JSON object with the following keys:
* `url`: A valid URL that browsers can use to retrieve the content. The URL
must resolve to a publicly-routed IP address, and must the `https:` scheme.
* `contentType`: A string representing the MIME type of the content at `url`.
A list of acceptable MIME types is provided below.
* `quality`: A number representing the quality level of the source. The
supported quality levels are `240`, `360`, `480`, `540`, `720`, `1080`,
`1440`, and `2160`. This may be extended in the future.
* `bitrate`: An optional number indicating the bitrate (in Kbps) of the
content. It must be a positive, finite number if provided. The bitrate is
not currently used by CyTube, but may be used by extensions or custom
scripts to determine whether this source is feasible to play on the viewer's
internet connection.
#### Acceptable MIME Types ####
The following MIME types are accepted for the `contentType` field:
* `video/mp4`
* `video/webm`
* `video/ogg`
* `application/x-mpegURL` (HLS streams)
- HLS is only supported for livestreams. Metadata with HLS sources
but without `live: true` will be rejected.
* ~~`rtmp/flv`~~
- In light of Adobe phasing out support for Flash, and many browsers
already dropping support, RTMP is not supported by this feature.
RTMP streams are only supported through the existing `rt:` media
type.
* `audio/aac`
* `audio/ogg`
* `audio/mpeg`
Other audio or video formats, such as AVI, MKV, and FLAC, are not supported due
to lack of common support across browsers for playing these formats. For more
information, refer to
[MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats#Browser_compatibility).
### Text Track Format ###
Each text track entry is a JSON object with the following keys:
* `url`: A valid URL that browsers can use to retrieve the track. The URL
must resolve to a publicly-routed IP address, and must the `https:` scheme.
* `contentType`: A string representing the MIME type of the track at `url`.
The only currently supported MIME type is
[`text/vtt`](https://developer.mozilla.org/en-US/docs/Web/API/WebVTT_API).
* `name`: A name for the text track. This is displayed in the menu for the
viewer to select a text track.
**Important note regarding text tracks and CORS:**
By default, browsers block requests for WebVTT tracks hosted on different
domains than the current page. In order for text tracks to work cross-origin,
the `Access-Control-Allow-Origin` header needs to be set by the remote server
when serving the VTT file. See
[MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin)
for more information about setting this header.
## Example ##
{
"title": "Test Video",
"duration": 10,
"live": false,
"thumbnail": "https://example.com/thumb.jpg",
"sources": [
{
"url": "https://example.com/video.mp4",
"contentType": "video/mp4",
"quality": 1080,
"bitrate": 5000
}
],
"textTracks": [
{
"url": "https://example.com/subtitles.vtt",
"contentType": "text/vtt",
"name": "English Subtitles"
}
]
}
## Permissions ##
The permission node to allow users to add custom content is the same as the
permission node for the existing raw file support. Custom content is considered
as an extension of the existing feature.
## Unsupported/Undefined Behavior ##
The behavior under any the following circumstances is not defined by this
specification, and any technical support in these cases is voided. This list is
non-exhaustive.
* Source URLs or text track URLs are hosted on a third-party website that does
not have knowledge of its content being played on CyTube.
* The webserver hosting the source or text track URLs serves a different MIME
type than the one specified in the manifest.
* The webserver hosting the source or text track URLs serves a file that does
not match the MIME type specified in the `Content-Type` HTTP header returned
to the browser.
* The manifest includes source URLs or text track URLs with expiration times,
session IDs, etc. in the URL querystring.

View File

@ -29,6 +29,10 @@ const SOURCE_CONTENT_TYPES = new Set([
'video/webm'
]);
const LIVE_ONLY_CONTENT_TYPES = new Set([
'application/x-mpegURL'
]);
export function lookup(url, opts) {
if (!opts) opts = {};
if (!opts.hasOwnProperty('timeout')) opts.timeout = 10000;
@ -158,11 +162,11 @@ export function validate(data) {
validateURL(data.thumbnail);
}
validateSources(data.sources);
validateSources(data.sources, data);
validateTextTracks(data.textTracks);
}
function validateSources(sources) {
function validateSources(sources, data) {
if (!Array.isArray(sources))
throw new ValidationError('sources must be a list');
if (sources.length === 0)
@ -178,6 +182,11 @@ function validateSources(sources) {
`unacceptable source contentType "${source.contentType}"`
);
if (LIVE_ONLY_CONTENT_TYPES.has(source.contentType) && !data.live)
throw new ValidationError(
`contentType "${source.contentType}" requires live: true`
);
if (!SOURCE_QUALITIES.has(source.quality))
throw new ValidationError(`unacceptable source quality "${source.quality}"`);

View File

@ -89,6 +89,16 @@ describe('custom-media', () => {
assert.throws(() => validate(invalid), /URL protocol must be HTTPS/);
});
it('rejects non-live HLS', () => {
invalid.live = false;
invalid.sources[0].contentType = 'application/x-mpegURL';
assert.throws(
() => validate(invalid),
/contentType "application\/x-mpegURL" requires live: true/
);
});
});
describe('#validateSources', () => {