System snapshots, #build
Changed: - Added: Dedublicated snapshots, read docs/Settings#snapshots first - Added: Actions for better Python and JS linting - Changed: Clean up JS - Changed: Use patched ffmpeg builds for arm64 - API: Added endpoints to interact with snapshots - Fixed: mobile layout for channel filter dopdown on downloadspull/357/head
commit
d69460bf98
@ -0,0 +1,17 @@
|
|||||||
|
'use strict';
|
||||||
|
module.exports = {
|
||||||
|
extends: ['eslint:recommended', 'eslint-config-prettier'],
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
strict: ['error', 'global'],
|
||||||
|
'no-unused-vars': ['error', { vars: 'local' }],
|
||||||
|
eqeqeq: ['error', 'always', { null: 'ignore' }],
|
||||||
|
curly: ['error', 'multi-line'],
|
||||||
|
'no-var': 'error',
|
||||||
|
},
|
||||||
|
};
|
@ -0,0 +1,16 @@
|
|||||||
|
name: lint_js
|
||||||
|
|
||||||
|
on: [pull_request, push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
name: lint_js
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: '16'
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm run lint
|
||||||
|
- run: npm run format -- --check
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"lint": "eslint 'tubearchivist/static/**/*.js'",
|
||||||
|
"format": "prettier --write 'tubearchivist/static/**/*.js'"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"eslint": "^8.26.0",
|
||||||
|
"prettier": "^2.7.1",
|
||||||
|
"eslint-config-prettier": "^8.5.0"
|
||||||
|
},
|
||||||
|
"prettier": {
|
||||||
|
"singleQuote": true,
|
||||||
|
"arrowParens": "avoid",
|
||||||
|
"printWidth": 100
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,254 @@
|
|||||||
|
"""
|
||||||
|
functionality:
|
||||||
|
- handle snapshots in ES
|
||||||
|
"""
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from os import environ
|
||||||
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
|
from home.src.es.connect import ElasticWrap
|
||||||
|
from home.src.es.index_setup import get_mapping
|
||||||
|
|
||||||
|
|
||||||
|
class ElasticSnapshot:
|
||||||
|
"""interact with snapshots on ES"""
|
||||||
|
|
||||||
|
REPO = "ta_snapshot"
|
||||||
|
REPO_SETTINGS = {
|
||||||
|
"compress": "true",
|
||||||
|
"chunk_size": "1g",
|
||||||
|
"location": "/usr/share/elasticsearch/data/snapshot",
|
||||||
|
}
|
||||||
|
POLICY = "ta_daily"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.all_indices = self._get_all_indices()
|
||||||
|
|
||||||
|
def _get_all_indices(self):
|
||||||
|
"""return all indices names managed by TA"""
|
||||||
|
mapping = get_mapping()
|
||||||
|
all_indices = [f"ta_{i['index_name']}" for i in mapping]
|
||||||
|
|
||||||
|
return all_indices
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
"""setup the snapshot in ES, create or update if needed"""
|
||||||
|
print("snapshot: run setup")
|
||||||
|
repo_exists = self._check_repo_exists()
|
||||||
|
if not repo_exists:
|
||||||
|
self.create_repo()
|
||||||
|
|
||||||
|
policy_exists = self._check_policy_exists()
|
||||||
|
if not policy_exists:
|
||||||
|
self.create_policy()
|
||||||
|
|
||||||
|
is_outdated = self._needs_startup_snapshot()
|
||||||
|
if is_outdated:
|
||||||
|
_ = self.take_snapshot_now()
|
||||||
|
|
||||||
|
def _check_repo_exists(self):
|
||||||
|
"""check if expected repo already exists"""
|
||||||
|
path = f"_snapshot/{self.REPO}"
|
||||||
|
response, statuscode = ElasticWrap(path).get()
|
||||||
|
if statuscode == 200:
|
||||||
|
print(f"snapshot: repo {self.REPO} already created")
|
||||||
|
matching = response[self.REPO]["settings"] == self.REPO_SETTINGS
|
||||||
|
if not matching:
|
||||||
|
print(f"snapshot: update repo settings {self.REPO_SETTINGS}")
|
||||||
|
|
||||||
|
return matching
|
||||||
|
|
||||||
|
print(f"snapshot: setup repo {self.REPO} config {self.REPO_SETTINGS}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def create_repo(self):
|
||||||
|
"""create filesystem repo"""
|
||||||
|
path = f"_snapshot/{self.REPO}"
|
||||||
|
data = {
|
||||||
|
"type": "fs",
|
||||||
|
"settings": self.REPO_SETTINGS,
|
||||||
|
}
|
||||||
|
response, statuscode = ElasticWrap(path).post(data=data)
|
||||||
|
if statuscode == 200:
|
||||||
|
print(f"snapshot: repo setup correctly: {response}")
|
||||||
|
|
||||||
|
def _check_policy_exists(self):
|
||||||
|
"""check if snapshot policy is set correctly"""
|
||||||
|
policy = self._get_policy()
|
||||||
|
expected_policy = self._build_policy_data()
|
||||||
|
if not policy:
|
||||||
|
print(f"snapshot: create policy {self.POLICY} {expected_policy}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if policy["policy"] != expected_policy:
|
||||||
|
print(f"snapshot: update policy settings {expected_policy}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print("snapshot: policy is set.")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _get_policy(self):
|
||||||
|
"""get policy from es"""
|
||||||
|
path = f"_slm/policy/{self.POLICY}"
|
||||||
|
response, statuscode = ElasticWrap(path).get()
|
||||||
|
if statuscode != 200:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return response[self.POLICY]
|
||||||
|
|
||||||
|
def create_policy(self):
|
||||||
|
"""create snapshot lifetime policy"""
|
||||||
|
path = f"_slm/policy/{self.POLICY}"
|
||||||
|
data = self._build_policy_data()
|
||||||
|
response, statuscode = ElasticWrap(path).put(data)
|
||||||
|
if statuscode == 200:
|
||||||
|
print(f"snapshot: policy setup correctly: {response}")
|
||||||
|
|
||||||
|
def _build_policy_data(self):
|
||||||
|
"""build policy dict from config"""
|
||||||
|
return {
|
||||||
|
"schedule": "0 30 1 * * ?",
|
||||||
|
"name": f"<{self.POLICY}_>",
|
||||||
|
"repository": self.REPO,
|
||||||
|
"config": {
|
||||||
|
"indices": self.all_indices,
|
||||||
|
"include_global_state": True,
|
||||||
|
},
|
||||||
|
"retention": {
|
||||||
|
"expire_after": "30d",
|
||||||
|
"min_count": 5,
|
||||||
|
"max_count": 50,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def _needs_startup_snapshot(self):
|
||||||
|
"""check if last snapshot is expired"""
|
||||||
|
snap_dicts = self._get_all_snapshots()
|
||||||
|
if not snap_dicts:
|
||||||
|
print("snapshot: create initial snapshot")
|
||||||
|
return True
|
||||||
|
|
||||||
|
last_stamp = snap_dicts[0]["end_stamp"]
|
||||||
|
now = int(datetime.now().strftime("%s"))
|
||||||
|
outdated = (now - last_stamp) / 60 / 60 > 24
|
||||||
|
if outdated:
|
||||||
|
print("snapshot: is outdated, create new now")
|
||||||
|
|
||||||
|
print("snapshot: last snapshot is up-to-date")
|
||||||
|
return outdated
|
||||||
|
|
||||||
|
def take_snapshot_now(self):
|
||||||
|
"""execute daily snapshot now"""
|
||||||
|
path = f"_slm/policy/{self.POLICY}/_execute"
|
||||||
|
response, statuscode = ElasticWrap(path).post()
|
||||||
|
if statuscode == 200:
|
||||||
|
print(f"snapshot: executing now: {response}")
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
def get_snapshot_stats(self):
|
||||||
|
"""get snapshot info for frontend"""
|
||||||
|
snapshot_info = self._build_policy_details()
|
||||||
|
if snapshot_info:
|
||||||
|
snapshot_info.update({"snapshots": self._get_all_snapshots()})
|
||||||
|
|
||||||
|
return snapshot_info
|
||||||
|
|
||||||
|
def get_single_snapshot(self, snapshot_id):
|
||||||
|
"""get single snapshot metadata"""
|
||||||
|
path = f"_snapshot/{self.REPO}/{snapshot_id}"
|
||||||
|
response, statuscode = ElasticWrap(path).get()
|
||||||
|
if statuscode == 404:
|
||||||
|
print(f"snapshots: not found: {snapshot_id}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
snapshot = response["snapshots"][0]
|
||||||
|
return self._parse_single_snapshot(snapshot)
|
||||||
|
|
||||||
|
def _get_all_snapshots(self):
|
||||||
|
"""get a list of all registered snapshots"""
|
||||||
|
path = f"_snapshot/{self.REPO}/*?sort=start_time&order=desc"
|
||||||
|
response, statuscode = ElasticWrap(path).get()
|
||||||
|
if statuscode == 404:
|
||||||
|
print("snapshots: not configured")
|
||||||
|
return False
|
||||||
|
|
||||||
|
all_snapshots = response["snapshots"]
|
||||||
|
if not all_snapshots:
|
||||||
|
print("snapshots: no snapshots found")
|
||||||
|
return False
|
||||||
|
|
||||||
|
snap_dicts = []
|
||||||
|
for snapshot in all_snapshots:
|
||||||
|
snap_dict = self._parse_single_snapshot(snapshot)
|
||||||
|
snap_dicts.append(snap_dict)
|
||||||
|
|
||||||
|
return snap_dicts
|
||||||
|
|
||||||
|
def _parse_single_snapshot(self, snapshot):
|
||||||
|
"""extract relevant metadata from single snapshot"""
|
||||||
|
snap_dict = {
|
||||||
|
"id": snapshot["snapshot"],
|
||||||
|
"state": snapshot["state"],
|
||||||
|
"es_version": snapshot["version"],
|
||||||
|
"start_date": self._date_converter(snapshot["start_time"]),
|
||||||
|
"end_date": self._date_converter(snapshot["end_time"]),
|
||||||
|
"end_stamp": snapshot["end_time_in_millis"] // 1000,
|
||||||
|
"duration_s": snapshot["duration_in_millis"] // 1000,
|
||||||
|
}
|
||||||
|
return snap_dict
|
||||||
|
|
||||||
|
def _build_policy_details(self):
|
||||||
|
"""get additional policy details"""
|
||||||
|
policy = self._get_policy()
|
||||||
|
if not policy:
|
||||||
|
return False
|
||||||
|
|
||||||
|
next_exec = policy["next_execution_millis"] // 1000
|
||||||
|
next_exec_date = datetime.fromtimestamp(next_exec)
|
||||||
|
next_exec_str = next_exec_date.strftime("%Y-%m-%d %H:%M")
|
||||||
|
expire_after = policy["policy"]["retention"]["expire_after"]
|
||||||
|
policy_metadata = {
|
||||||
|
"next_exec": next_exec,
|
||||||
|
"next_exec_str": next_exec_str,
|
||||||
|
"expire_after": expire_after,
|
||||||
|
}
|
||||||
|
return policy_metadata
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _date_converter(date_utc):
|
||||||
|
"""convert datetime string"""
|
||||||
|
expected_format = "%Y-%m-%dT%H:%M:%S.%fZ"
|
||||||
|
date = datetime.strptime(date_utc, expected_format)
|
||||||
|
local_datetime = date.replace(tzinfo=ZoneInfo("localtime"))
|
||||||
|
converted = local_datetime.astimezone(ZoneInfo(environ.get("TZ")))
|
||||||
|
converted_str = converted.strftime("%Y-%m-%d %H:%M")
|
||||||
|
|
||||||
|
return converted_str
|
||||||
|
|
||||||
|
def restore_all(self, snapshot_name):
|
||||||
|
"""restore snapshot by name"""
|
||||||
|
for index in self.all_indices:
|
||||||
|
_, _ = ElasticWrap(index).delete()
|
||||||
|
|
||||||
|
path = f"_snapshot/{self.REPO}/{snapshot_name}/_restore"
|
||||||
|
data = {"indices": "*"}
|
||||||
|
response, statuscode = ElasticWrap(path).post(data=data)
|
||||||
|
if statuscode == 200:
|
||||||
|
print(f"snapshot: executing now: {response}")
|
||||||
|
return response
|
||||||
|
|
||||||
|
print(f"snapshot: failed to restore, {statuscode} {response}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def delete_single_snapshot(self, snapshot_id):
|
||||||
|
"""delete single snapshot from index"""
|
||||||
|
path = f"_snapshot/{self.REPO}/{snapshot_id}"
|
||||||
|
response, statuscode = ElasticWrap(path).delete()
|
||||||
|
if statuscode == 200:
|
||||||
|
print(f"snapshot: deleting {snapshot_id} {response}")
|
||||||
|
return response
|
||||||
|
|
||||||
|
print(f"snapshot: failed to delete, {statuscode} {response}")
|
||||||
|
return False
|
@ -1,148 +1,157 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/* global cast chrome getVideoPlayerVideoId postVideoProgress setProgressBar getVideoPlayer getVideoPlayerWatchStatus watchedThreshold isWatched getVideoData getURL getVideoPlayerCurrentTime */
|
||||||
|
|
||||||
function initializeCastApi() {
|
function initializeCastApi() {
|
||||||
cast.framework.CastContext.getInstance().setOptions({
|
cast.framework.CastContext.getInstance().setOptions({
|
||||||
receiverApplicationId: chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID, // Use built in receiver app on cast device, see https://developers.google.com/cast/docs/styled_receiver if you want to be able to add a theme, splash screen or watermark. Has a $5 one time fee.
|
receiverApplicationId: chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID, // Use built in receiver app on cast device, see https://developers.google.com/cast/docs/styled_receiver if you want to be able to add a theme, splash screen or watermark. Has a $5 one time fee.
|
||||||
autoJoinPolicy: chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED
|
autoJoinPolicy: chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED,
|
||||||
});
|
});
|
||||||
|
|
||||||
var player = new cast.framework.RemotePlayer();
|
let player = new cast.framework.RemotePlayer();
|
||||||
var playerController = new cast.framework.RemotePlayerController(player);
|
let playerController = new cast.framework.RemotePlayerController(player);
|
||||||
|
|
||||||
// Add event listerner to check if a connection to a cast device is initiated
|
// Add event listerner to check if a connection to a cast device is initiated
|
||||||
playerController.addEventListener(
|
playerController.addEventListener(
|
||||||
cast.framework.RemotePlayerEventType.IS_CONNECTED_CHANGED, function() {
|
cast.framework.RemotePlayerEventType.IS_CONNECTED_CHANGED,
|
||||||
castConnectionChange(player)
|
function () {
|
||||||
}
|
castConnectionChange(player);
|
||||||
);
|
}
|
||||||
playerController.addEventListener(
|
);
|
||||||
cast.framework.RemotePlayerEventType.CURRENT_TIME_CHANGED, function() {
|
playerController.addEventListener(
|
||||||
castVideoProgress(player)
|
cast.framework.RemotePlayerEventType.CURRENT_TIME_CHANGED,
|
||||||
}
|
function () {
|
||||||
);
|
castVideoProgress(player);
|
||||||
playerController.addEventListener(
|
}
|
||||||
cast.framework.RemotePlayerEventType.IS_PAUSED_CHANGED, function() {
|
);
|
||||||
castVideoPaused(player)
|
playerController.addEventListener(
|
||||||
}
|
cast.framework.RemotePlayerEventType.IS_PAUSED_CHANGED,
|
||||||
);
|
function () {
|
||||||
|
castVideoPaused(player);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function castConnectionChange(player) {
|
function castConnectionChange(player) {
|
||||||
// If cast connection is initialized start cast
|
// If cast connection is initialized start cast
|
||||||
if (player.isConnected) {
|
if (player.isConnected) {
|
||||||
// console.log("Cast Connected.");
|
// console.log("Cast Connected.");
|
||||||
castStart();
|
castStart();
|
||||||
} else if (!player.isConnected) {
|
} else if (!player.isConnected) {
|
||||||
// console.log("Cast Disconnected.");
|
// console.log("Cast Disconnected.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function castVideoProgress(player) {
|
function castVideoProgress(player) {
|
||||||
var videoId = getVideoPlayerVideoId();
|
let videoId = getVideoPlayerVideoId();
|
||||||
if (player.mediaInfo.contentId.includes(videoId)) {
|
if (player.mediaInfo.contentId.includes(videoId)) {
|
||||||
var currentTime = player.currentTime;
|
let currentTime = player.currentTime;
|
||||||
var duration = player.duration;
|
let duration = player.duration;
|
||||||
if ((currentTime % 10) <= 1.0 && currentTime != 0 && duration != 0) { // Check progress every 10 seconds or else progress is checked a few times a second
|
if (currentTime % 10 <= 1.0 && currentTime !== 0 && duration !== 0) {
|
||||||
postVideoProgress(videoId, currentTime);
|
// Check progress every 10 seconds or else progress is checked a few times a second
|
||||||
setProgressBar(videoId, currentTime, duration);
|
postVideoProgress(videoId, currentTime);
|
||||||
if (!getVideoPlayerWatchStatus()) { // Check if video is already marked as watched
|
setProgressBar(videoId, currentTime, duration);
|
||||||
if (watchedThreshold(currentTime, duration)) {
|
if (!getVideoPlayerWatchStatus()) {
|
||||||
isWatched(videoId);
|
// Check if video is already marked as watched
|
||||||
}
|
if (watchedThreshold(currentTime, duration)) {
|
||||||
}
|
isWatched(videoId);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function castVideoPaused(player) {
|
function castVideoPaused(player) {
|
||||||
var videoId = getVideoPlayerVideoId();
|
let videoId = getVideoPlayerVideoId();
|
||||||
var currentTime = player.currentTime;
|
let currentTime = player.currentTime;
|
||||||
var duration = player.duration;
|
let duration = player.duration;
|
||||||
if (player.mediaInfo != null) {
|
if (player.mediaInfo != null) {
|
||||||
if (player.mediaInfo.contentId.includes(videoId)) {
|
if (player.mediaInfo.contentId.includes(videoId)) {
|
||||||
if (currentTime != 0 && duration != 0) {
|
if (currentTime !== 0 && duration !== 0) {
|
||||||
postVideoProgress(videoId, currentTime);
|
postVideoProgress(videoId, currentTime);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function castStart() {
|
function castStart() {
|
||||||
var castSession = cast.framework.CastContext.getInstance().getCurrentSession();
|
let castSession = cast.framework.CastContext.getInstance().getCurrentSession();
|
||||||
// Check if there is already media playing on the cast target to prevent recasting on page reload or switching to another video page
|
// Check if there is already media playing on the cast target to prevent recasting on page reload or switching to another video page
|
||||||
if (!castSession.getMediaSession()) {
|
if (!castSession.getMediaSession()) {
|
||||||
var videoId = getVideoPlayerVideoId();
|
let videoId = getVideoPlayerVideoId();
|
||||||
var videoData = getVideoData(videoId);
|
let videoData = getVideoData(videoId);
|
||||||
var contentId = getURL() + videoData.data.media_url;
|
let contentId = getURL() + videoData.data.media_url;
|
||||||
var contentTitle = videoData.data.title;
|
let contentTitle = videoData.data.title;
|
||||||
var contentImage = getURL() + videoData.data.vid_thumb_url;
|
let contentImage = getURL() + videoData.data.vid_thumb_url;
|
||||||
|
|
||||||
contentType = 'video/mp4'; // Set content type, only videos right now so it is hard coded
|
let contentType = 'video/mp4'; // Set content type, only videos right now so it is hard coded
|
||||||
contentCurrentTime = getVideoPlayerCurrentTime(); // Get video's current position
|
let contentCurrentTime = getVideoPlayerCurrentTime(); // Get video's current position
|
||||||
contentActiveSubtitle = [];
|
let contentActiveSubtitle = [];
|
||||||
// Check if a subtitle is turned on.
|
// Check if a subtitle is turned on.
|
||||||
for (var i = 0; i < getVideoPlayer().textTracks.length; i++) {
|
for (let i = 0; i < getVideoPlayer().textTracks.length; i++) {
|
||||||
if (getVideoPlayer().textTracks[i].mode == "showing") {
|
if (getVideoPlayer().textTracks[i].mode === 'showing') {
|
||||||
contentActiveSubtitle =[i + 1];
|
contentActiveSubtitle = [i + 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
contentSubtitles = [];
|
let contentSubtitles = [];
|
||||||
var videoSubtitles = videoData.data.subtitles; // Array of subtitles
|
let videoSubtitles = videoData.data.subtitles; // Array of subtitles
|
||||||
if (typeof(videoSubtitles) != 'undefined' && videoData.config.downloads.subtitle) {
|
if (typeof videoSubtitles !== 'undefined' && videoData.config.downloads.subtitle) {
|
||||||
for (var i = 0; i < videoSubtitles.length; i++) {
|
for (let i = 0; i < videoSubtitles.length; i++) {
|
||||||
subtitle = new chrome.cast.media.Track(i, chrome.cast.media.TrackType.TEXT);
|
let subtitle = new chrome.cast.media.Track(i, chrome.cast.media.TrackType.TEXT);
|
||||||
subtitle.trackContentId = videoSubtitles[i].media_url;
|
subtitle.trackContentId = videoSubtitles[i].media_url;
|
||||||
subtitle.trackContentType = 'text/vtt';
|
subtitle.trackContentType = 'text/vtt';
|
||||||
subtitle.subtype = chrome.cast.media.TextTrackType.SUBTITLES;
|
subtitle.subtype = chrome.cast.media.TextTrackType.SUBTITLES;
|
||||||
subtitle.name = videoSubtitles[i].name;
|
subtitle.name = videoSubtitles[i].name;
|
||||||
subtitle.language = videoSubtitles[i].lang;
|
subtitle.language = videoSubtitles[i].lang;
|
||||||
subtitle.customData = null;
|
subtitle.customData = null;
|
||||||
contentSubtitles.push(subtitle);
|
contentSubtitles.push(subtitle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaInfo = new chrome.cast.media.MediaInfo(contentId, contentType); // Create MediaInfo var that contains url and content type
|
let mediaInfo = new chrome.cast.media.MediaInfo(contentId, contentType); // Create MediaInfo var that contains url and content type
|
||||||
// mediaInfo.streamType = chrome.cast.media.StreamType.BUFFERED; // Set type of stream, BUFFERED, LIVE, OTHER
|
// mediaInfo.streamType = chrome.cast.media.StreamType.BUFFERED; // Set type of stream, BUFFERED, LIVE, OTHER
|
||||||
mediaInfo.metadata = new chrome.cast.media.GenericMediaMetadata(); // Create metadata var and add it to MediaInfo
|
mediaInfo.metadata = new chrome.cast.media.GenericMediaMetadata(); // Create metadata var and add it to MediaInfo
|
||||||
mediaInfo.metadata.title = contentTitle.replace("&", "&"); // Set the video title
|
mediaInfo.metadata.title = contentTitle.replace('&', '&'); // Set the video title
|
||||||
mediaInfo.metadata.images = [new chrome.cast.Image(contentImage)]; // Set the video thumbnail
|
mediaInfo.metadata.images = [new chrome.cast.Image(contentImage)]; // Set the video thumbnail
|
||||||
// mediaInfo.textTrackStyle = new chrome.cast.media.TextTrackStyle();
|
// mediaInfo.textTrackStyle = new chrome.cast.media.TextTrackStyle();
|
||||||
mediaInfo.tracks = contentSubtitles;
|
mediaInfo.tracks = contentSubtitles;
|
||||||
|
|
||||||
var request = new chrome.cast.media.LoadRequest(mediaInfo); // Create request with the previously set MediaInfo.
|
let request = new chrome.cast.media.LoadRequest(mediaInfo); // Create request with the previously set MediaInfo.
|
||||||
// request.queueData = new chrome.cast.media.QueueData(); // See https://developers.google.com/cast/docs/reference/web_sender/chrome.cast.media.QueueData for playlist support.
|
// request.queueData = new chrome.cast.media.QueueData(); // See https://developers.google.com/cast/docs/reference/web_sender/chrome.cast.media.QueueData for playlist support.
|
||||||
request.currentTime = shiftCurrentTime(contentCurrentTime); // Set video start position based on the browser video position
|
request.currentTime = shiftCurrentTime(contentCurrentTime); // Set video start position based on the browser video position
|
||||||
request.activeTrackIds = contentActiveSubtitle; // Set active subtitle based on video player
|
request.activeTrackIds = contentActiveSubtitle; // Set active subtitle based on video player
|
||||||
// request.autoplay = false; // Set content to auto play, true by default
|
// request.autoplay = false; // Set content to auto play, true by default
|
||||||
castSession.loadMedia(request).then(
|
castSession.loadMedia(request).then(
|
||||||
function() {
|
function () {
|
||||||
castSuccessful();
|
castSuccessful();
|
||||||
},
|
},
|
||||||
function() {
|
function (error) {
|
||||||
castFailed(errorCode);
|
castFailed(error.code);
|
||||||
}
|
}
|
||||||
); // Send request to cast device
|
); // Send request to cast device
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function shiftCurrentTime(contentCurrentTime) { // Shift media back 3 seconds to prevent missing some of the content
|
function shiftCurrentTime(contentCurrentTime) {
|
||||||
if (contentCurrentTime > 5) {
|
// Shift media back 3 seconds to prevent missing some of the content
|
||||||
return(contentCurrentTime - 3);
|
if (contentCurrentTime > 5) {
|
||||||
} else {
|
return contentCurrentTime - 3;
|
||||||
return(0);
|
} else {
|
||||||
}
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function castSuccessful() {
|
function castSuccessful() {
|
||||||
// console.log('Cast Successful.');
|
// console.log('Cast Successful.');
|
||||||
getVideoPlayer().pause(); // Pause browser video on successful cast
|
getVideoPlayer().pause(); // Pause browser video on successful cast
|
||||||
}
|
}
|
||||||
|
|
||||||
function castFailed(errorCode) {
|
function castFailed(errorCode) {
|
||||||
console.log('Error code: ' + errorCode);
|
console.log('Error code: ' + errorCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
window['__onGCastApiAvailable'] = function(isAvailable) {
|
window['__onGCastApiAvailable'] = function (isAvailable) {
|
||||||
if (isAvailable) {
|
if (isAvailable) {
|
||||||
initializeCastApi();
|
initializeCastApi();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue