Initial commit.
This commit is contained in:
commit
81bc6488f5
6 changed files with 868 additions and 0 deletions
9
.gitlab-ci.yml
Normal file
9
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
stages:
|
||||||
|
- deploy
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
stage: deploy
|
||||||
|
script:
|
||||||
|
- sh deploy.sh
|
||||||
|
only:
|
||||||
|
- main
|
27
PeerTubeConfig.json
Normal file
27
PeerTubeConfig.json
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"name": "PeerTube",
|
||||||
|
"description": "A plugin that adds PeerTube as a source",
|
||||||
|
"author": "FUTO",
|
||||||
|
"authorUrl": "https://futo.org",
|
||||||
|
|
||||||
|
"sourceUrl": "https://plugins.grayjay.app/PeerTube/PeerTubeConfig.json",
|
||||||
|
"repositoryUrl": "https://futo.org",
|
||||||
|
"scriptUrl": "./PeerTubeScript.js",
|
||||||
|
"version": 13,
|
||||||
|
|
||||||
|
"iconUrl": "./peertube.png",
|
||||||
|
"id": "1c291164-294c-4c2d-800d-7bc6d31d0019",
|
||||||
|
|
||||||
|
"scriptSignature": "",
|
||||||
|
"scriptPublicKey": "",
|
||||||
|
"packages": ["Http"],
|
||||||
|
|
||||||
|
"allowEval": false,
|
||||||
|
"allowUrls": [
|
||||||
|
"everywhere"
|
||||||
|
],
|
||||||
|
|
||||||
|
"constants": {
|
||||||
|
"baseUrl": "https://peertube.futo.org"
|
||||||
|
}
|
||||||
|
}
|
335
PeerTubeScript.js
Normal file
335
PeerTubeScript.js
Normal file
|
@ -0,0 +1,335 @@
|
||||||
|
const PLATFORM = "PeerTube";
|
||||||
|
|
||||||
|
var config = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a query
|
||||||
|
* @param {{[key: string]: any}} params Query params
|
||||||
|
* @returns {String} Query string
|
||||||
|
*/
|
||||||
|
function buildQuery(params) {
|
||||||
|
let query = "";
|
||||||
|
let first = true;
|
||||||
|
for (const [key, value] of Object.entries(params)) {
|
||||||
|
if (value) {
|
||||||
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
query += "&";
|
||||||
|
}
|
||||||
|
|
||||||
|
query += `${key}=${value}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (query && query.length > 0) ? `?${query}` : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getChannelPager(path, params, page) {
|
||||||
|
log(`getChannelPager page=${page}`, params)
|
||||||
|
|
||||||
|
const count = 20;
|
||||||
|
const start = (page ?? 0) * count;
|
||||||
|
params = { ... params, start, count }
|
||||||
|
|
||||||
|
const url = `${config.constants.baseUrl}${path}`;
|
||||||
|
const urlWithParams = `${url}${buildQuery(params)}`;
|
||||||
|
log("GET " + urlWithParams);
|
||||||
|
const res = http.GET(urlWithParams, {});
|
||||||
|
|
||||||
|
if (res.code != 200) {
|
||||||
|
log("Failed to get channels", res);
|
||||||
|
return new ChannelPager([], false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const obj = JSON.parse(res.body);
|
||||||
|
|
||||||
|
return new PeerTubeChannelPager(obj.data.map(v => {
|
||||||
|
return new PlatformAuthorLink(new PlatformID(PLATFORM, v.name, config.id), v.displayName, v.url, v.avatar ? `${config.constants.baseUrl}${v.avatar.path}` : "");
|
||||||
|
|
||||||
|
}), obj.total > (start + count), path, params, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVideoPager(path, params, page) {
|
||||||
|
log(`getVideoPager page=${page}`, params)
|
||||||
|
|
||||||
|
const count = 20;
|
||||||
|
const start = (page ?? 0) * count;
|
||||||
|
params = { ... params, start, count }
|
||||||
|
|
||||||
|
const url = `${config.constants.baseUrl}${path}`;
|
||||||
|
const urlWithParams = `${url}${buildQuery(params)}`;
|
||||||
|
log("GET " + urlWithParams);
|
||||||
|
const res = http.GET(urlWithParams, {});
|
||||||
|
|
||||||
|
if (res.code != 200) {
|
||||||
|
log("Failed to get videos", res);
|
||||||
|
return new VideoPager([], false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const obj = JSON.parse(res.body);
|
||||||
|
|
||||||
|
return new PeerTubeVideoPager(obj.data.map(v => {
|
||||||
|
return new PlatformVideo({
|
||||||
|
id: new PlatformID(PLATFORM, v.uuid, config.id),
|
||||||
|
name: v.name ?? "",
|
||||||
|
thumbnails: new Thumbnails([new Thumbnail(`${config.constants.baseUrl}${v.thumbnailPath}`, 0)]),
|
||||||
|
author: new PlatformAuthorLink(new PlatformID(PLATFORM, v.channel.name, config.id),
|
||||||
|
v.channel.displayName,
|
||||||
|
v.channel.url,
|
||||||
|
v.channel.avatar ? `${config.constants.baseUrl}${v.channel.avatar.path}` : ""),
|
||||||
|
datetime: Math.round((new Date(v.publishedAt)).getTime() / 1000),
|
||||||
|
duration: v.duration,
|
||||||
|
viewCount: v.views,
|
||||||
|
url: v.url,
|
||||||
|
isLive: v.isLive
|
||||||
|
});
|
||||||
|
|
||||||
|
}), obj.total > (start + count), path, params, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCommentPager(path, params, page) {
|
||||||
|
log(`getCommentPager page=${page}`, params)
|
||||||
|
|
||||||
|
const count = 20;
|
||||||
|
const start = (page ?? 0) * count;
|
||||||
|
params = { ... params, start, count }
|
||||||
|
|
||||||
|
const url = `${config.constants.baseUrl}${path}`;
|
||||||
|
const urlWithParams = `${url}${buildQuery(params)}`;
|
||||||
|
log("GET " + urlWithParams);
|
||||||
|
const res = http.GET(urlWithParams, {});
|
||||||
|
|
||||||
|
if (res.code != 200) {
|
||||||
|
log("Failed to get comments", res);
|
||||||
|
return new CommentPager([], false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const obj = JSON.parse(res.body);
|
||||||
|
|
||||||
|
return new PeerTubeCommentPager(obj.data.map(v => {
|
||||||
|
return new Comment({
|
||||||
|
contextUrl: url,
|
||||||
|
author: new PlatformAuthorLink(new PlatformID(PLATFORM, v.account.name, config.id), v.account.displayName, `${config.constants.baseUrl}/api/v1/video-channels/${v.account.name}`, ""),
|
||||||
|
message: v.text,
|
||||||
|
rating: new RatingLikes(0),
|
||||||
|
date: Math.round((new Date(v.createdAt)).getTime() / 1000),
|
||||||
|
replyCount: v.totalReplies,
|
||||||
|
context: { id: v.id }
|
||||||
|
});
|
||||||
|
}), obj.total > (start + count), path, params, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
source.enable = function (conf) {
|
||||||
|
config = conf ?? {};
|
||||||
|
};
|
||||||
|
|
||||||
|
source.getHome = function () {
|
||||||
|
return getVideoPager('/api/v1/videos', {
|
||||||
|
sort: "best"
|
||||||
|
}, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
source.searchSuggestions = function(query) {
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
source.getSearchCapabilities = () => {
|
||||||
|
return {
|
||||||
|
types: [Type.Feed.Mixed, Type.Feed.Streams, Type.Feed.Videos],
|
||||||
|
sorts: [Type.Order.Chronological, "publishedAt"]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
source.search = function (query, type, order, filters) {
|
||||||
|
let sort = order;
|
||||||
|
if (sort === Type.Order.Chronological) {
|
||||||
|
sort = "-publishedAt";
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
search: query,
|
||||||
|
sort
|
||||||
|
};
|
||||||
|
|
||||||
|
if (type == Type.Feed.Streams) {
|
||||||
|
params.isLive = true;
|
||||||
|
} else if (type == Type.Feed.Videos) {
|
||||||
|
params.isLive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getVideoPager('/api/v1/search/videos', params, 0);
|
||||||
|
};
|
||||||
|
source.searchChannels = function (query) {
|
||||||
|
return getChannelPager('/api/v1/search/video-channels', {
|
||||||
|
search: query
|
||||||
|
}, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
source.isChannelUrl = function(url) {
|
||||||
|
return url.startsWith(`${config.constants.baseUrl}/video-channels/`);
|
||||||
|
};
|
||||||
|
source.getChannel = function (url) {
|
||||||
|
const tokens = url.split('/');
|
||||||
|
const handle = tokens[tokens.length - 1];
|
||||||
|
const urlWithParams = `${config.constants.baseUrl}/api/v1/video-channels/${handle}`;
|
||||||
|
log("GET " + urlWithParams);
|
||||||
|
const res = http.GET(urlWithParams, {});
|
||||||
|
|
||||||
|
if (res.code != 200) {
|
||||||
|
log("Failed to get channel", res);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const obj = JSON.parse(res.body);
|
||||||
|
|
||||||
|
return new PlatformChannel({
|
||||||
|
id: new PlatformID(PLATFORM, obj.name, config.id),
|
||||||
|
name: obj.displayName,
|
||||||
|
thumbnail: obj.avatar ? `${config.constants.baseUrl}${obj.avatar.path}` : "",
|
||||||
|
banner: null,
|
||||||
|
subscribers: obj.followersCount,
|
||||||
|
description: obj.description ?? "",
|
||||||
|
url: obj.url,
|
||||||
|
links: {}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
source.getChannelCapabilities = () => {
|
||||||
|
return {
|
||||||
|
types: [Type.Feed.Mixed, Type.Feed.Streams, Type.Feed.Videos],
|
||||||
|
sorts: [Type.Order.Chronological, "publishedAt"]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
source.getChannelContents = function (url, type, order, filters) {
|
||||||
|
let sort = order;
|
||||||
|
if (sort === Type.Order.Chronological) {
|
||||||
|
sort = "-publishedAt";
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
sort
|
||||||
|
};
|
||||||
|
|
||||||
|
if (type == Type.Feed.Streams) {
|
||||||
|
params.isLive = true;
|
||||||
|
} else if (type == Type.Feed.Videos) {
|
||||||
|
params.isLive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokens = url.split('/');
|
||||||
|
const handle = tokens[tokens.length - 1];
|
||||||
|
return getVideoPager(`/api/v1/video-channels/${handle}/videos`, params, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
source.isContentDetailsUrl = function(url) {
|
||||||
|
return url.startsWith(`${config.constants.baseUrl}/videos/watch/`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const supportedResolutions = {
|
||||||
|
'1080p': { width: 1920, height: 1080 },
|
||||||
|
'720p': { width: 1280, height: 720 },
|
||||||
|
'480p': { width: 854, height: 480 },
|
||||||
|
'360p': { width: 640, height: 360 },
|
||||||
|
'144p': { width: 256, height: 144 }
|
||||||
|
};
|
||||||
|
|
||||||
|
source.getContentDetails = function (url) {
|
||||||
|
const tokens = url.split('/');
|
||||||
|
const handle = tokens[tokens.length - 1];
|
||||||
|
const urlWithParams = `${config.constants.baseUrl}/api/v1/videos/${handle}`;
|
||||||
|
log("GET " + urlWithParams);
|
||||||
|
const res = http.GET(urlWithParams, {});
|
||||||
|
|
||||||
|
if (res.code != 200) {
|
||||||
|
log("Failed to get video detail", res);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const obj = JSON.parse(res.body);
|
||||||
|
const sources = [];
|
||||||
|
|
||||||
|
for (const streamingPlaylist of obj.streamingPlaylists) {
|
||||||
|
sources.push(new HLSSource({
|
||||||
|
name: "HLS",
|
||||||
|
url: streamingPlaylist.playlistUrl,
|
||||||
|
duration: obj.duration ?? 0,
|
||||||
|
priority: true
|
||||||
|
}));
|
||||||
|
|
||||||
|
for (const file of streamingPlaylist.files) {
|
||||||
|
let supportedResolution;
|
||||||
|
if (file.resolution.width && file.resolution.height) {
|
||||||
|
supportedResolution = { width: file.resolution.width, height: file.resolution.height };
|
||||||
|
} else {
|
||||||
|
supportedResolution = supportedResolutions[file.resolution.label];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!supportedResolution) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sources.push(new VideoUrlSource({
|
||||||
|
name: file.resolution.label,
|
||||||
|
url: file.fileDownloadUrl,
|
||||||
|
width: supportedResolution.width,
|
||||||
|
height: supportedResolution.height,
|
||||||
|
duration: obj.duration,
|
||||||
|
container: "video/mp4"
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PlatformVideoDetails({
|
||||||
|
id: new PlatformID(PLATFORM, obj.uuid, config.id),
|
||||||
|
name: obj.name,
|
||||||
|
thumbnails: new Thumbnails([new Thumbnail(`${config.constants.baseUrl}${obj.thumbnailPath}`, 0)]),
|
||||||
|
author: new PlatformAuthorLink(new PlatformID(PLATFORM, obj.channel.name, config.id),
|
||||||
|
obj.channel.displayName,
|
||||||
|
obj.channel.url,
|
||||||
|
obj.channel.avatar ? `${config.constants.baseUrl}${obj.channel.avatar.path}` : ""),
|
||||||
|
datetime: Math.round((new Date(obj.publishedAt)).getTime() / 1000),
|
||||||
|
duration: obj.duration,
|
||||||
|
viewCount: obj.views,
|
||||||
|
url: obj.url,
|
||||||
|
isLive: obj.isLive,
|
||||||
|
description: obj.description,
|
||||||
|
video: new VideoSourceDescriptor(sources)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
source.getComments = function (url) {
|
||||||
|
const tokens = url.split('/');
|
||||||
|
const handle = tokens[tokens.length - 1];
|
||||||
|
return getCommentPager(`/api/v1/videos/${handle}/comment-threads`, {}, 0);
|
||||||
|
}
|
||||||
|
source.getSubComments = function(comment) {
|
||||||
|
return getCommentPager(`/api/v1/videos/${comment.context.id}/comment-threads`, {}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
class PeerTubeVideoPager extends VideoPager {
|
||||||
|
constructor(results, hasMore, path, params, page) {
|
||||||
|
super(results, hasMore, { path, params, page });
|
||||||
|
}
|
||||||
|
|
||||||
|
nextPage() {
|
||||||
|
return getVideoPager(this.context.path, this.context.params, (this.context.page ?? 0) + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PeerTubeChannelPager extends ChannelPager {
|
||||||
|
constructor(results, hasMore, path, params, page) {
|
||||||
|
super(results, hasMore, { path, params, page });
|
||||||
|
}
|
||||||
|
|
||||||
|
nextPage() {
|
||||||
|
return getChannelPager(this.context.path, this.context.params, (this.context.page ?? 0) + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PeerTubeCommentPager extends CommentPager {
|
||||||
|
constructor(results, hasMore, path, params, page) {
|
||||||
|
super(results, hasMore, { path, params, page });
|
||||||
|
}
|
||||||
|
|
||||||
|
nextPage() {
|
||||||
|
return getCommentPager(this.context.path, this.context.params, (this.context.page ?? 0) + 1);
|
||||||
|
}
|
||||||
|
}
|
474
autocomplete.js
Normal file
474
autocomplete.js
Normal file
|
@ -0,0 +1,474 @@
|
||||||
|
//Reference Scriptfile
|
||||||
|
//Intended exclusively for auto-complete in your IDE, not for execution
|
||||||
|
var IS_TESTING = false;
|
||||||
|
|
||||||
|
let Type = {
|
||||||
|
Source: {
|
||||||
|
Dash: "DASH",
|
||||||
|
HLS: "HLS",
|
||||||
|
STATIC: "Static"
|
||||||
|
},
|
||||||
|
Feed: {
|
||||||
|
Videos: "VIDEOS",
|
||||||
|
Streams: "STREAMS",
|
||||||
|
Mixed: "MIXED",
|
||||||
|
Live: "LIVE"
|
||||||
|
},
|
||||||
|
Order: {
|
||||||
|
Chronological: "CHRONOLOGICAL"
|
||||||
|
},
|
||||||
|
Date: {
|
||||||
|
LastHour: "LAST_HOUR",
|
||||||
|
Today: "TODAY",
|
||||||
|
LastWeek: "LAST_WEEK",
|
||||||
|
LastMonth: "LAST_MONTH",
|
||||||
|
LastYear: "LAST_YEAR"
|
||||||
|
},
|
||||||
|
Duration: {
|
||||||
|
Short: "SHORT",
|
||||||
|
Medium: "MEDIUM",
|
||||||
|
Long: "LONG"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let Language = {
|
||||||
|
UNKNOWN: "Unknown",
|
||||||
|
ARABIC: "Arabic",
|
||||||
|
SPANISH: "Spanish",
|
||||||
|
FRENCH: "French",
|
||||||
|
HINDI: "Hindi",
|
||||||
|
INDONESIAN: "Indonesian",
|
||||||
|
KOREAN: "Korean",
|
||||||
|
PORTBRAZIL: "Portuguese Brazilian",
|
||||||
|
RUSSIAN: "Russian",
|
||||||
|
THAI: "Thai",
|
||||||
|
TURKISH: "Turkish",
|
||||||
|
VIETNAMESE: "Vietnamese",
|
||||||
|
ENGLISH: "English"
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScriptException extends Error {
|
||||||
|
constructor(type, msg) {
|
||||||
|
if(arguments.length == 1) {
|
||||||
|
super(arguments[0]);
|
||||||
|
this.plugin_type = "ScriptException";
|
||||||
|
this.message = arguments[0];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super(msg);
|
||||||
|
this.plugin_type = type ?? ""; //string
|
||||||
|
this.msg = msg ?? ""; //string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class TimeoutException extends ScriptException {
|
||||||
|
constructor(msg) {
|
||||||
|
super(msg);
|
||||||
|
this.plugin_type = "ScriptTimeoutException";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Thumbnails {
|
||||||
|
constructor(thumbnails) {
|
||||||
|
this.sources = thumbnails ?? []; // Thumbnail[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class Thumbnail {
|
||||||
|
constructor(url, quality) {
|
||||||
|
this.url = url ?? ""; //string
|
||||||
|
this.quality = quality ?? 0; //integer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PlatformID {
|
||||||
|
constructor(platform, id, pluginId) {
|
||||||
|
this.platform = platform ?? ""; //string
|
||||||
|
this.pluginId = pluginId; //string
|
||||||
|
this.value = id; //string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ResultCapabilities {
|
||||||
|
constructor(types, sorts, filters) {
|
||||||
|
this.types = types ?? [];
|
||||||
|
this.sorts = sorts ?? [];
|
||||||
|
this.filters = filters ?? [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class FilterGroup {
|
||||||
|
constructor(name, filters, isMultiSelect, id) {
|
||||||
|
if(!name) throw new ScriptException("No name for filter group");
|
||||||
|
if(!filters) throw new ScriptException("No filter provided");
|
||||||
|
|
||||||
|
this.name = name
|
||||||
|
this.filters = filters
|
||||||
|
this.isMultiSelect = isMultiSelect;
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class FilterCapability {
|
||||||
|
constructor(name, value, id) {
|
||||||
|
if(!name) throw new ScriptException("No name for filter");
|
||||||
|
if(!value) throw new ScriptException("No filter value");
|
||||||
|
|
||||||
|
this.name = name;
|
||||||
|
this.value = value;
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PlatformAuthorLink {
|
||||||
|
constructor(id, name, url, thumbnail) {
|
||||||
|
this.id = id ?? PlatformID(); //PlatformID
|
||||||
|
this.name = name ?? ""; //string
|
||||||
|
this.url = url ?? ""; //string
|
||||||
|
this.thumbnail = thumbnail; //string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class PlatformVideo {
|
||||||
|
constructor(obj) {
|
||||||
|
obj = obj ?? {};
|
||||||
|
this.plugin_type = "PlatformVideo";
|
||||||
|
this.id = obj.id ?? PlatformID(); //PlatformID
|
||||||
|
this.name = obj.name ?? ""; //string
|
||||||
|
this.thumbnails = obj.thumbnails ?? Thumbnails([]); //Thumbnail[]
|
||||||
|
this.author = obj.author ?? PlatformAuthorLink(); //PlatformAuthorLink
|
||||||
|
this.datetime = obj.uploadDate ?? 0; //OffsetDateTime (Long)
|
||||||
|
this.url = obj.url ?? ""; //String
|
||||||
|
|
||||||
|
this.duration = obj.duration ?? -1; //Long
|
||||||
|
this.viewCount = obj.viewCount ?? -1; //Long
|
||||||
|
|
||||||
|
this.isLive = obj.isLive ?? false; //Boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class PlatformVideoDetails extends PlatformVideo {
|
||||||
|
constructor(obj) {
|
||||||
|
super(obj);
|
||||||
|
obj = obj ?? {};
|
||||||
|
this.plugin_type = "PlatformVideoDetails";
|
||||||
|
|
||||||
|
this.description = obj.description ?? "";//String
|
||||||
|
this.video = obj.video ?? {}; //VideoSourceDescriptor
|
||||||
|
this.dash = obj.dash ?? null; //DashSource
|
||||||
|
this.hls = obj.hls ?? null; //HLSSource
|
||||||
|
this.live = obj.live ?? null; //VideoSource
|
||||||
|
|
||||||
|
this.rating = obj.rating ?? null; //IRating
|
||||||
|
this.subtitles = obj.subtitles ?? [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Sources
|
||||||
|
class VideoSourceDescriptor {
|
||||||
|
constructor(obj) {
|
||||||
|
obj = obj ?? {};
|
||||||
|
this.plugin_type = "MuxVideoSourceDescriptor";
|
||||||
|
this.isUnMuxed = false;
|
||||||
|
|
||||||
|
if(obj.constructor === Array)
|
||||||
|
this.videoSources = obj;
|
||||||
|
else
|
||||||
|
this.videoSources = obj.videoSources ?? [];
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class UnMuxVideoSourceDescriptor {
|
||||||
|
constructor(videoSourcesOrObj, audioSources) {
|
||||||
|
videoSourcesOrObj = videoSourcesOrObj ?? {};
|
||||||
|
this.plugin_type = "UnMuxVideoSourceDescriptor";
|
||||||
|
this.isUnMuxed = true;
|
||||||
|
|
||||||
|
if(videoSourcesOrObj.constructor === Array) {
|
||||||
|
this.videoSources = videoSourcesOrObj;
|
||||||
|
this.audioSources = audioSources;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.videoSources = videoSourcesOrObj.videoSources ?? [];
|
||||||
|
this.audioSources = videoSourcesOrObj.audioSources ?? [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class VideoUrlSource {
|
||||||
|
constructor(obj) {
|
||||||
|
obj = obj ?? {};
|
||||||
|
this.plugin_type = "VideoUrlSource";
|
||||||
|
this.width = obj.width ?? 0;
|
||||||
|
this.height = obj.height ?? 0;
|
||||||
|
this.container = obj.container ?? "";
|
||||||
|
this.codec = obj.codec ?? "";
|
||||||
|
this.name = obj.name ?? "";
|
||||||
|
this.bitrate = obj.bitrate ?? 0;
|
||||||
|
this.duration = obj.duration ?? 0;
|
||||||
|
this.url = obj.url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class VideoUrlRangeSource extends VideoUrlSource {
|
||||||
|
constructor(obj) {
|
||||||
|
super(obj);
|
||||||
|
this.plugin_type = "VideoUrlRangeSource";
|
||||||
|
|
||||||
|
this.itagId = obj.itagId ?? null;
|
||||||
|
this.initStart = obj.initStart ?? null;
|
||||||
|
this.initEnd = obj.initEnd ?? null;
|
||||||
|
this.indexStart = obj.indexStart ?? null;
|
||||||
|
this.indexEnd = obj.indexEnd ?? null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class AudioUrlSource {
|
||||||
|
constructor(obj) {
|
||||||
|
obj = obj ?? {};
|
||||||
|
this.plugin_type = "AudioUrlSource";
|
||||||
|
this.name = obj.name ?? "";
|
||||||
|
this.bitrate = obj.bitrate ?? 0;
|
||||||
|
this.container = obj.container ?? "";
|
||||||
|
this.codec = obj.codec ?? "";
|
||||||
|
this.duration = obj.duration ?? 0;
|
||||||
|
this.url = obj.url;
|
||||||
|
this.language = obj.language ?? Language.UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class AudioUrlRangeSource extends AudioUrlSource {
|
||||||
|
constructor(obj) {
|
||||||
|
super(obj);
|
||||||
|
this.plugin_type = "AudioUrlRangeSource";
|
||||||
|
|
||||||
|
this.itagId = obj.itagId ?? null;
|
||||||
|
this.initStart = obj.initStart ?? null;
|
||||||
|
this.initEnd = obj.initEnd ?? null;
|
||||||
|
this.indexStart = obj.indexStart ?? null;
|
||||||
|
this.indexEnd = obj.indexEnd ?? null;
|
||||||
|
this.audioChannels = obj.audioChannels ?? 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class HLSSource {
|
||||||
|
constructor(obj) {
|
||||||
|
obj = obj ?? {};
|
||||||
|
this.plugin_type = "HLSSource";
|
||||||
|
this.name = obj.name ?? "HLS";
|
||||||
|
this.duration = obj.duration ?? 0;
|
||||||
|
this.url = obj.url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class DashSource {
|
||||||
|
constructor(obj) {
|
||||||
|
obj = obj ?? {};
|
||||||
|
this.plugin_type = "DashSource";
|
||||||
|
this.name = obj.name ?? "Dash";
|
||||||
|
this.duration = obj.duration ?? 0;
|
||||||
|
this.url = obj.url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Channel
|
||||||
|
class PlatformChannel {
|
||||||
|
constructor(obj) {
|
||||||
|
obj = obj ?? {};
|
||||||
|
this.plugin_type = "PlatformChannel";
|
||||||
|
this.id = obj.id ?? ""; //string
|
||||||
|
this.name = obj.name ?? ""; //string
|
||||||
|
this.thumbnail = obj.thumbnail; //string
|
||||||
|
this.banner = obj.banner; //string
|
||||||
|
this.subscribers = obj.subscribers ?? 0; //integer
|
||||||
|
this.description = obj.description; //string
|
||||||
|
this.url = obj.url ?? ""; //string
|
||||||
|
this.links = obj.links ?? { } //Map<string,string>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Ratings
|
||||||
|
class RatingLikes {
|
||||||
|
constructor(likes) {
|
||||||
|
this.type = 1;
|
||||||
|
this.likes = likes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class RatingLikesDislikes {
|
||||||
|
constructor(likes,dislikes) {
|
||||||
|
this.type = 2;
|
||||||
|
this.likes = likes;
|
||||||
|
this.dislikes = dislikes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class RatingScaler {
|
||||||
|
constructor(value) {
|
||||||
|
this.type = 3;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Comment {
|
||||||
|
constructor(obj) {
|
||||||
|
this.plugin_type = "Comment";
|
||||||
|
this.contextUrl = obj.contextUrl ?? "";
|
||||||
|
this.author = obj.author ?? new PlatformAuthorLink(null, "", "", null);
|
||||||
|
this.message = obj.message ?? "";
|
||||||
|
this.rating = obj.rating ?? new RatingLikes(0);
|
||||||
|
this.date = obj.date ?? 0;
|
||||||
|
this.replyCount = obj.replyCount ?? 0;
|
||||||
|
this.context = obj.context ?? {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Pagers
|
||||||
|
class VideoPager {
|
||||||
|
constructor(results, hasMore, context) {
|
||||||
|
this.plugin_type = "VideoPager";
|
||||||
|
this.results = results ?? [];
|
||||||
|
this.hasMore = hasMore ?? false;
|
||||||
|
this.context = context ?? {};
|
||||||
|
}
|
||||||
|
|
||||||
|
hasMorePagers() { return this.hasMore; }
|
||||||
|
nextPage() { return new Pager([], false, this.context) }
|
||||||
|
}
|
||||||
|
class ChannelPager {
|
||||||
|
constructor(results, hasMore, context) {
|
||||||
|
this.plugin_type = "ChannelPager";
|
||||||
|
this.results = results ?? [];
|
||||||
|
this.hasMore = hasMore ?? false;
|
||||||
|
this.context = context ?? {};
|
||||||
|
}
|
||||||
|
|
||||||
|
hasMorePagers() { return this.hasMore; }
|
||||||
|
nextPage() { return new Pager([], false, this.context) }
|
||||||
|
}
|
||||||
|
class CommentPager {
|
||||||
|
constructor(results, hasMore, context) {
|
||||||
|
this.plugin_type = "CommentPager";
|
||||||
|
this.results = results ?? [];
|
||||||
|
this.hasMore = hasMore ?? false;
|
||||||
|
this.context = context ?? {};
|
||||||
|
}
|
||||||
|
|
||||||
|
hasMorePagers() { return this.hasMore; }
|
||||||
|
nextPage() { return new Pager([], false, this.context) }
|
||||||
|
}
|
||||||
|
|
||||||
|
function throwException(type, message) {
|
||||||
|
throw new Error("V8EXCEPTION:" + type + "-" + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
//To override by plugin
|
||||||
|
const source = {
|
||||||
|
getHome() { return new Pager([], false, {}); },
|
||||||
|
|
||||||
|
enable(config){ },
|
||||||
|
disable() {},
|
||||||
|
|
||||||
|
searchSuggestions(query){ return []; },
|
||||||
|
getSearchCapabilities(){ return { types: [], sorts: [] }; },
|
||||||
|
search(query){ return new Pager([], false, {}); }, //TODO
|
||||||
|
|
||||||
|
|
||||||
|
isChannelUrl(url){ return false; },
|
||||||
|
getChannel(url){ return null; },
|
||||||
|
getChannelCapabilities(){ return { types: [], sorts: [] }; },
|
||||||
|
getChannelVideos(url) { return new Pager([], false, {}); },
|
||||||
|
|
||||||
|
isVideoDetailsUrl(url){ return false; },
|
||||||
|
getVideoDetails(url){ }, //TODO
|
||||||
|
|
||||||
|
//getComments(url){ return new Pager([], false, {}); }, //TODO
|
||||||
|
//getSubComments(comment){ return new Pager([], false, {}); }, //TODO
|
||||||
|
|
||||||
|
//getSubscriptionsUser(){ return []; },
|
||||||
|
//getPlaylistsUser(){ return []; }
|
||||||
|
};
|
||||||
|
|
||||||
|
function parseSettings(settings) {
|
||||||
|
if(!settings)
|
||||||
|
return {};
|
||||||
|
let newSettings = {};
|
||||||
|
for(let key in settings) {
|
||||||
|
if(typeof settings[key] == "string")
|
||||||
|
newSettings[key] = JSON.parse(settings[key]);
|
||||||
|
else
|
||||||
|
newSettings[key] = settings[key];
|
||||||
|
}
|
||||||
|
return newSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
function log(str) {
|
||||||
|
if(str) {
|
||||||
|
if(typeof str == "string")
|
||||||
|
bridge.log(str);
|
||||||
|
else
|
||||||
|
bridge.log(JSON.stringify(str, null, 4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Package Bridge (variable: bridge)
|
||||||
|
let bridge = {
|
||||||
|
/**
|
||||||
|
* @return {Boolean}
|
||||||
|
**/
|
||||||
|
isLoggedIn: function() {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {String} str
|
||||||
|
* @return {Unit}
|
||||||
|
**/
|
||||||
|
log: function(str) {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {String} str
|
||||||
|
* @return {Unit}
|
||||||
|
**/
|
||||||
|
throwTest: function(str) {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {String} str
|
||||||
|
* @return {Unit}
|
||||||
|
**/
|
||||||
|
toast: function(str) {},
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Package Http (variable: http)
|
||||||
|
let http = {
|
||||||
|
/**
|
||||||
|
* @param {String} url
|
||||||
|
* @param {Map} headers
|
||||||
|
* @param {Boolean} useAuth
|
||||||
|
* @return {BridgeHttpResponse}
|
||||||
|
**/
|
||||||
|
GET: function(url, headers, useAuth) {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {String} url
|
||||||
|
* @param {String} body
|
||||||
|
* @param {Map} headers
|
||||||
|
* @param {Boolean} useAuth
|
||||||
|
* @return {BridgeHttpResponse}
|
||||||
|
**/
|
||||||
|
POST: function(url, body, headers, useAuth) {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {BatchBuilder}
|
||||||
|
**/
|
||||||
|
batch: function() {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {String} method
|
||||||
|
* @param {String} url
|
||||||
|
* @param {Map} headers
|
||||||
|
* @param {Boolean} useAuth
|
||||||
|
* @return {BridgeHttpResponse}
|
||||||
|
**/
|
||||||
|
request: function(method, url, headers, useAuth) {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {String} method
|
||||||
|
* @param {String} url
|
||||||
|
* @param {String} body
|
||||||
|
* @param {Map} headers
|
||||||
|
* @param {Boolean} useAuth
|
||||||
|
* @return {BridgeHttpResponse}
|
||||||
|
**/
|
||||||
|
requestWithBody: function(method, url, body, headers, useAuth) {},
|
||||||
|
|
||||||
|
}
|
23
deploy.sh
Normal file
23
deploy.sh
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#!/bin/sh
|
||||||
|
DOCUMENT_ROOT=/var/www/sources
|
||||||
|
|
||||||
|
# Take site offline
|
||||||
|
echo "Taking site offline..."
|
||||||
|
touch $DOCUMENT_ROOT/maintenance.file
|
||||||
|
|
||||||
|
# Swap over the content
|
||||||
|
echo "Deploying content..."
|
||||||
|
cp peertube.png $DOCUMENT_ROOT/
|
||||||
|
cp PeerTubeConfig.json $DOCUMENT_ROOT/
|
||||||
|
cp PeerTubeScript.js $DOCUMENT_ROOT/
|
||||||
|
|
||||||
|
# Notify Cloudflare to wipe the CDN cache
|
||||||
|
echo "Purging Cloudflare cache..."
|
||||||
|
curl -X POST "https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/purge_cache" \
|
||||||
|
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
--data '{"purge_everything":true}'
|
||||||
|
|
||||||
|
# Take site back online
|
||||||
|
echo "Bringing site back online..."
|
||||||
|
rm $DOCUMENT_ROOT/maintenance.file
|
BIN
peertube.png
Normal file
BIN
peertube.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 77 KiB |
Loading…
Reference in a new issue