mirror of
https://github.com/DanielnetoDotCom/YouPHPTube
synced 2025-10-06 03:50:04 +02:00
Offline player test
This commit is contained in:
parent
44ed5e2f0c
commit
53dfc89fd2
9 changed files with 234 additions and 66 deletions
|
@ -1,5 +1,5 @@
|
|||
# version 4.0
|
||||
<filesMatch "\.(php|sh|log|lock|sql|bat|bin|cmd|com|cpl|exe|gadget|inf1|ins|inx|isu|job|jse|lnk|msc|msi|msp|mst|paf|pif|ps1|reg|rgs|scr|sct|shb|shs|u3p|vb|vbe|vbs|vbscript|ws|wsf|wsh|bak|config|inc|bkp)$">
|
||||
<filesMatch "\.(php[a-z0-9]?|phtml|sh|log|lock|sql|bat|bin|cmd|com|cpl|exe|gadget|inf1|ins|inx|isu|job|jse|lnk|msc|msi|msp|mst|paf|pif|ps1|reg|rgs|scr|sct|shb|shs|u3p|vb|vbe|vbs|vbscript|ws|wsf|wsh|bak|config|inc|bkp)$">
|
||||
<IfModule !authz_core_module>
|
||||
Order Allow,Deny
|
||||
Deny from all
|
||||
|
|
|
@ -4564,6 +4564,17 @@ if (!class_exists('Video')) {
|
|||
$this->rrating = $rrating;
|
||||
}
|
||||
|
||||
public static function getVideoTypeFromId($videos_id) {
|
||||
if(empty($videos_id)){
|
||||
return false;
|
||||
}
|
||||
$video = Video::getVideoLight($videos_id);
|
||||
if(empty($video['filename'])){
|
||||
return false;
|
||||
}
|
||||
return self::getVideoType($video['filename']);
|
||||
}
|
||||
|
||||
public static function getVideoType($filename) {
|
||||
global $_getVideoType;
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@ class PlayerSkins extends PluginAbstract {
|
|||
$obj->showLogoAdjustLeft = "-74px";
|
||||
$obj->showLogoAdjustTop = "-22px;";
|
||||
$obj->disableEmbedTopInfo = false;
|
||||
$obj->disableOfflineVideos = false;
|
||||
$obj->contextMenuDisableEmbedOnly = false;
|
||||
$obj->contextMenuLoop = true;
|
||||
$obj->contextMenuCopyVideoURL = true;
|
||||
|
@ -62,7 +63,6 @@ class PlayerSkins extends PluginAbstract {
|
|||
$obj->contextMenuShare = true;
|
||||
$obj->playerFullHeight = false;
|
||||
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
|
@ -264,13 +264,15 @@ class PlayerSkins extends PluginAbstract {
|
|||
if ($obj->showShareAutoplay && isVideoPlayerHasProgressBar() && empty($obj->forceAlwaysAutoplay) && empty($_REQUEST['hideAutoplaySwitch'])) {
|
||||
$css .= "<link href=\"" . getURL('plugin/PlayerSkins/autoplayButton.css') . "\" rel=\"stylesheet\" type=\"text/css\"/>";
|
||||
}
|
||||
if (self::showOfflineVideo()) {
|
||||
$css .= "<link href=\"" . getURL('plugin/PlayerSkins/offlineButton.css') . "\" rel=\"stylesheet\" type=\"text/css\"/>";
|
||||
}
|
||||
}
|
||||
|
||||
$url = urlencode(getSelfURI());
|
||||
$oembed = '<link href="' . getCDN() . 'oembed/?format=json&url=' . $url . '" rel="alternate" type="application/json+oembed" />';
|
||||
$oembed .= '<link href="' . getCDN() . 'oembed/?format=xml&url=' . $url . '" rel="alternate" type="application/xml+oembed" />';
|
||||
|
||||
|
||||
return $js . $css . $oembed;
|
||||
}
|
||||
|
||||
|
@ -303,7 +305,7 @@ class PlayerSkins extends PluginAbstract {
|
|||
|
||||
if ($obj->showShareAutoplay && isVideoPlayerHasProgressBar() && empty($obj->forceAlwaysAutoplay) && empty($_REQUEST['hideAutoplaySwitch'])) {
|
||||
PlayerSkins::getStartPlayerJS(file_get_contents("{$global['systemRootPath']}plugin/PlayerSkins/autoplayButton.js"));
|
||||
}else{
|
||||
} else {
|
||||
if ($obj->showShareAutoplay) {
|
||||
$js .= "<!-- PlayerSkins showShareAutoplay -->";
|
||||
}
|
||||
|
@ -317,6 +319,9 @@ class PlayerSkins extends PluginAbstract {
|
|||
$js .= "<!-- PlayerSkins empty(\$_REQUEST['hideAutoplaySwitch']) -->";
|
||||
}
|
||||
}
|
||||
if (self::showOfflineVideo()) {
|
||||
PlayerSkins::getStartPlayerJS(file_get_contents("{$global['systemRootPath']}plugin/PlayerSkins/offlineButton.js"));
|
||||
}
|
||||
}
|
||||
if (isAudio()) {
|
||||
$videos_id = getVideos_id();
|
||||
|
@ -335,10 +340,12 @@ class PlayerSkins extends PluginAbstract {
|
|||
include $global['systemRootPath'] . 'plugin/PlayerSkins/mediaSession.php';
|
||||
PlayerSkins::addOnPlayerReady('if(typeof updateMediaSessionMetadata === "function"){updateMediaSessionMetadata();}');
|
||||
|
||||
if(isVideo()){
|
||||
$js .= "<script>const offlineVideoDbName = 'videos_offlineDb_".User::getId()."';</script>";
|
||||
$js .= "<script src=\"" . getURL('node_modules/dexie/dist/dexie.min.js') . "\"></script>";
|
||||
$js .= "<script src=\"" . getURL('plugin/PlayerSkins/offlineVideo.js') . "\"></script>";
|
||||
if (isVideo()) {
|
||||
$js .= "<script src=\"" . getURL('node_modules/dexie/dist/dexie.min.js') . "\"></script>";
|
||||
if (self::showOfflineVideo()) {
|
||||
$js .= "<script>const offlineVideoDbName = 'videos_offlineDb_" . User::getId() . "';</script>";
|
||||
$js .= "<script src=\"" . getURL('plugin/PlayerSkins/offlineVideo.js') . "\"></script>";
|
||||
}
|
||||
}
|
||||
|
||||
return $js;
|
||||
|
@ -491,17 +498,17 @@ class PlayerSkins extends PluginAbstract {
|
|||
player.ready(function () {";
|
||||
|
||||
// this is here because for some reason videos on the storage only works if it loads dinamically on android devices only
|
||||
if(isMobile()){
|
||||
if (isMobile()) {
|
||||
$js .= "player.src(player.currentSources());";
|
||||
}
|
||||
if(empty($_REQUEST['mute'])){
|
||||
if (empty($_REQUEST['mute'])) {
|
||||
$play = "playerPlayIfAutoPlay({$currentTime});";
|
||||
|
||||
$js .= "
|
||||
player.persistvolume({
|
||||
namespace: 'AVideo'
|
||||
});";
|
||||
}else{
|
||||
} else {
|
||||
$play = "player.volume(0);player.muted(true);playerPlayMutedIfAutoPlay({$currentTime});";
|
||||
}
|
||||
|
||||
|
@ -682,4 +689,25 @@ class PlayerSkins extends PluginAbstract {
|
|||
return array($tags);
|
||||
}
|
||||
|
||||
public static function showOfflineVideo() {
|
||||
global $global;
|
||||
$obj = AVideoPlugin::getDataObject('PlayerSkins');
|
||||
if (!empty($obj->disableOfflineVideos)) {
|
||||
return false;
|
||||
}
|
||||
if (empty($global['developer_mode'])) {
|
||||
return false;
|
||||
}
|
||||
$videos_id = getVideos_id();
|
||||
|
||||
if (empty($videos_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$types = Video::getVideoTypeFromId($videos_id);
|
||||
|
||||
return !empty($types->mp4);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
27
plugin/PlayerSkins/offlineButton.css
Normal file
27
plugin/PlayerSkins/offlineButton.css
Normal file
|
@ -0,0 +1,27 @@
|
|||
.offline-button{
|
||||
outline: none;
|
||||
}
|
||||
.offline-button:before {
|
||||
display: inline-block;
|
||||
font-style: normal;
|
||||
font-variant: normal;
|
||||
text-rendering: auto;
|
||||
content: "\f358";
|
||||
font-size: 1.2em;
|
||||
line-height: 1.0em;
|
||||
font-family: "Font Awesome\ 5 Free";
|
||||
font-weight: 400;
|
||||
}
|
||||
.offline-button.hasOfflineVideo:before {
|
||||
content: "\f058";
|
||||
font-weight: 400;
|
||||
}
|
||||
.offline-button.loading:before {
|
||||
content: "\f110";
|
||||
font-weight: 900;
|
||||
}
|
||||
.playingOfflineVideo .offline-button.hasOfflineVideo:before {
|
||||
content: "\f13a";
|
||||
font-weight: 900;
|
||||
color: yellow;
|
||||
}
|
25
plugin/PlayerSkins/offlineButton.js
Normal file
25
plugin/PlayerSkins/offlineButton.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
var Button = videojs.getComponent('Button');
|
||||
|
||||
var offlineButton = videojs.extend(Button, {
|
||||
//constructor: function(player, options) {
|
||||
constructor: function () {
|
||||
Button.apply(this, arguments);
|
||||
this.addClass('offline-button');
|
||||
this.controlText("offline");
|
||||
},
|
||||
handleClick: function () {
|
||||
console.log('offlineButton clicked');
|
||||
openDownloadOfflineVideoPage();
|
||||
}
|
||||
});
|
||||
|
||||
videojs.registerComponent('offlineButton', offlineButton);
|
||||
player.getChild('controlBar').addChild('offlineButton', {}, getPlayerButtonIndex('fullscreenToggle') - 1);
|
||||
|
||||
player.on('resolutionchange', function (event) {
|
||||
if(isOfflineSourceSelectedToPlay()){
|
||||
setOfflineButton('playingOffline', false);
|
||||
}else{
|
||||
setOfflineButton('readyToPlayOffline', false);
|
||||
}
|
||||
});
|
|
@ -11,7 +11,6 @@ async function downloadOfflineVideo(source) {
|
|||
getOfflineVideo(mediaId, resolution).then(function (video) {
|
||||
if (video) {
|
||||
console.log('downloadOfflineVideo', video);
|
||||
//createSourceElement(video.fileBlob, video.video_type, video.resolution);
|
||||
resolve(video);
|
||||
} else {
|
||||
// Fetch the videos from the network
|
||||
|
@ -23,25 +22,32 @@ async function downloadOfflineVideo(source) {
|
|||
});
|
||||
}
|
||||
|
||||
function replaceVideoSourcesPerOfflineVersion(){
|
||||
replaceVideoSourcesPerOfflineVersionIfExists(mediaId);
|
||||
}
|
||||
|
||||
async function replaceVideoSourcesPerOfflineVersionIfExists(videos_id) {
|
||||
videos_id = parseInt(videos_id);
|
||||
videoJSRecreateSources(false);
|
||||
$('source.offline-video').remove();
|
||||
getOfflineVideo(videos_id).then(function (collection) {
|
||||
collection.toArray().then(function (offlineVideoSources) {
|
||||
var firstSource = null;
|
||||
if (offlineVideoSources.length) {
|
||||
console.log('something in the array', offlineVideoSources, offlineVideoSources.length);
|
||||
var sources = [];
|
||||
var firstSource = null;
|
||||
for (var item in offlineVideoSources) {
|
||||
if(typeof offlineVideoSources[item] === 'object'){
|
||||
if (typeof offlineVideoSources[item] === 'object') {
|
||||
var video = offlineVideoSources[item];
|
||||
const videoURL = URL.createObjectURL(video.fileBlob);
|
||||
var source = {
|
||||
src: videoURL,
|
||||
type: video.video_type,
|
||||
res: video.resolution,
|
||||
label: video.resolution+ 'p <span class="label label-warning" style="padding: 0 2px; font-size: .8em; display: inline;">(OFFLINE)</span>',
|
||||
class: 'offline-video',
|
||||
label: video.resolution + 'p <span class="label label-warning" style="padding: 0 2px; font-size: .8em; display: inline;">(OFFLINE)</span>',
|
||||
};
|
||||
if(!firstSource){
|
||||
if (!firstSource) {
|
||||
firstSource = source;
|
||||
}
|
||||
sources.push(source);
|
||||
|
@ -50,11 +56,10 @@ async function replaceVideoSourcesPerOfflineVersionIfExists(videos_id) {
|
|||
}
|
||||
console.log('Adding sources ', firstSource, sources);
|
||||
player.src(sources);
|
||||
videoJSRecreateSources(firstSource);
|
||||
Promise.resolve(offlineVideoSources);
|
||||
} else {
|
||||
console.log('empty array', offlineVideoSources, offlineVideoSources.length);
|
||||
}
|
||||
videoJSRecreateSources(firstSource);
|
||||
offlineVideoButtonCheck();
|
||||
Promise.resolve(offlineVideoSources);
|
||||
}).catch(function (e) {
|
||||
console.log("Error: " + (e.stack || e));
|
||||
});
|
||||
|
@ -64,29 +69,10 @@ async function replaceVideoSourcesPerOfflineVersionIfExists(videos_id) {
|
|||
}
|
||||
|
||||
async function getOfflineVideo(videos_id) {
|
||||
videos_id = parseInt(videos_id);
|
||||
return await offlineDbRequest.offline_videos.where('videos_id').equals(videos_id);
|
||||
}
|
||||
|
||||
function getOfflineSources(videos_id) {
|
||||
var first = false;
|
||||
var video480 = false;
|
||||
$("#mainVideo source").each(function (index) {
|
||||
if (empty(first)) {
|
||||
first = $(this);
|
||||
}
|
||||
var resolution = $(this).attr("res");
|
||||
if (resolution == 480) {
|
||||
video480 = $(this);
|
||||
}
|
||||
});
|
||||
if (!empty(video480)) {
|
||||
console.log('getOneOfflineVideoSource 480p video found', video480);
|
||||
return video480;
|
||||
}
|
||||
console.log('getOneOfflineVideoSource first video found', first);
|
||||
return first;
|
||||
}
|
||||
|
||||
function getOneOfflineVideoSource() {
|
||||
var first = false;
|
||||
var video480 = false;
|
||||
|
@ -107,14 +93,6 @@ function getOneOfflineVideoSource() {
|
|||
return first;
|
||||
}
|
||||
|
||||
async function downloadOneOfflineVideo() {
|
||||
var source = getOneOfflineVideoSource();
|
||||
if (!empty(source)) {
|
||||
return await downloadOfflineVideo(source);
|
||||
}
|
||||
reject(false);
|
||||
}
|
||||
|
||||
function changeProgressBarOfflineVideo(progressBarSelector, value) {
|
||||
value = value.toFixed(2);
|
||||
$(progressBarSelector).find('.progress-bar')
|
||||
|
@ -155,7 +133,6 @@ async function fetchVideoFromNetwork(src, type, resolution, progressBarSelector)
|
|||
}
|
||||
|
||||
let fileBlob = new Blob(chunks);
|
||||
//createSourceElement(fileBlob, type, resolution);
|
||||
return await storeOfflineVideo(src, fileBlob, type, contentLength, mediaId, resolution);
|
||||
}
|
||||
|
||||
|
@ -187,17 +164,109 @@ function deleteOfflineVideo(videos_id, resolution) {
|
|||
|
||||
function createSourceElement(source) {
|
||||
var sourceElement = $('<source />', source);
|
||||
if(!empty(source.class)){
|
||||
$(sourceElement).addClass(source.class);
|
||||
}
|
||||
console.log('displayVideo', source);
|
||||
$("video#mainVideo, #mainVideo_html5_api").append(sourceElement);
|
||||
}
|
||||
|
||||
function openDownloadOfflineVideoPage() {
|
||||
if (empty(mediaId)) {
|
||||
return false;
|
||||
}
|
||||
var url = webSiteRootURL + 'plugin/PlayerSkins/offlineVideo.php';
|
||||
url = addQueryStringParameter(url, 'videos_id', mediaId);
|
||||
url = addQueryStringParameter(url, 'socketResourceId', socketResourceId);
|
||||
avideoModalIframeSmall(url);
|
||||
return true;
|
||||
}
|
||||
var offlineVideoButtonCheckTimeout;
|
||||
function offlineVideoButtonCheck() {
|
||||
getOfflineVideo(mediaId).then(function (collection) {
|
||||
collection.toArray().then(function (offlineVideoSources) {
|
||||
console.log("offlineVideoButtonCheck offlineVideoSources.length: ", offlineVideoSources.length);
|
||||
if (offlineVideoSources.length) {
|
||||
if(isOfflineSourceSelectedToPlay()){
|
||||
setOfflineButton('playingOffline', false);
|
||||
}else{
|
||||
setOfflineButton('readyToPlayOffline', false);
|
||||
}
|
||||
} else {
|
||||
setOfflineButton('download', false);
|
||||
}
|
||||
clearTimeout(offlineVideoButtonCheckTimeout);
|
||||
offlineVideoButtonCheckTimeout = setTimeout(function () {
|
||||
offlineVideoButtonCheck();
|
||||
}, 5000);
|
||||
}).catch(function (e) {
|
||||
console.log("Error offlineVideoButtonCheck 1: ", e);
|
||||
});
|
||||
}).catch(function (e) {
|
||||
console.log("Error offlineVideoButtonCheck 2: ", e);
|
||||
});
|
||||
}
|
||||
|
||||
function isOfflineSourceSelectedToPlay() {
|
||||
var currSource = player.currentSrc();
|
||||
console.log("isOfflineSourceSelectedToPlay: ", currSource);
|
||||
if (currSource.match(/^blob:http/i)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function setOfflineButton(type, showLoading) {
|
||||
if (showLoading) {
|
||||
offlineVideoLoading(true);
|
||||
}
|
||||
switch (type) {
|
||||
case 'download':
|
||||
avideoTooltip(".offline-button", "Download");
|
||||
$('.offline-button').removeClass('hasOfflineVideo');
|
||||
$('body').removeClass('playingOfflineVideo');
|
||||
offlineVideoButtonCheck();
|
||||
break;
|
||||
case 'readyToPlayOffline':
|
||||
avideoTooltip(".offline-button", "Ready to play offline");
|
||||
$('body').removeClass('playingOfflineVideo');
|
||||
$('.offline-button').addClass('hasOfflineVideo');
|
||||
break;
|
||||
case 'playingOffline':
|
||||
avideoTooltip(".offline-button", "Playing offline");
|
||||
$('body').addClass('playingOfflineVideo');
|
||||
$('.offline-button').addClass('hasOfflineVideo');
|
||||
break;
|
||||
}
|
||||
if (showLoading) {
|
||||
offlineVideoLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
function offlineVideoLoading(active) {
|
||||
if (active) {
|
||||
$('.offline-button').addClass('loading');
|
||||
$('.offline-button').addClass('fa-pulse');
|
||||
} else {
|
||||
$('.offline-button').removeClass('loading');
|
||||
$('.offline-button').removeClass('fa-pulse');
|
||||
}
|
||||
}
|
||||
|
||||
function socketUpdateOfflineVideoSource(resourceId){
|
||||
if(avideoSocketIsActive()){
|
||||
sendSocketMessageToResourceId({}, 'replaceVideoSourcesPerOfflineVersion', resourceId)
|
||||
}
|
||||
}
|
||||
|
||||
var offlineDbRequest = new Dexie(offlineVideoDbName);
|
||||
offlineDbRequest.version(1).stores({
|
||||
offline_videos: "videos_id_resolution,*videos_id"
|
||||
});
|
||||
|
||||
$(document).ready(function () {
|
||||
if(!empty(mediaId) && $("#mainVideo").length){
|
||||
if (!empty(mediaId) && $("#mainVideo").length) {
|
||||
replaceVideoSourcesPerOfflineVersionIfExists(mediaId);
|
||||
}
|
||||
});
|
|
@ -32,7 +32,7 @@ if (empty($mainResolution) && !empty($sourcesResolutions)) {
|
|||
$mainResolution = $sourcesResolutions[0];
|
||||
}
|
||||
|
||||
function createOfflineDownloadPanel($option, $class = 'col-sm-6') {
|
||||
function createOfflineDownloadPanel($option, $class = 'col-xs-6') {
|
||||
global $videos_id;
|
||||
?>
|
||||
<div class="<?php echo $class; ?>">
|
||||
|
@ -107,7 +107,7 @@ function createOfflineDownloadPanel($option, $class = 'col-sm-6') {
|
|||
<div class="tab-content">
|
||||
<div id="offlineVideo" class="tab-pane fade in active">
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="col-xs-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading clearfix">
|
||||
<?php
|
||||
|
@ -123,7 +123,7 @@ function createOfflineDownloadPanel($option, $class = 'col-sm-6') {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="col-xs-6">
|
||||
<?php
|
||||
createOfflineDownloadPanel($mainResolution, '');
|
||||
?>
|
||||
|
@ -179,6 +179,7 @@ function createOfflineDownloadPanel($option, $class = 'col-sm-6') {
|
|||
return await fetchVideoFromNetwork(src, 'video/mp4', resolution, progressBarSelector).then(function (video) {
|
||||
console.log("_downloadOfflineVideo: ", video);
|
||||
listAllOfflineVideo();
|
||||
socketUpdateOfflineVideoSource(<?php echo json_encode($_REQUEST['socketResourceId']); ?>);
|
||||
}).catch(function (e) {
|
||||
console.log("_downloadOfflineVideo Error: ", e);
|
||||
});
|
||||
|
@ -187,6 +188,7 @@ function createOfflineDownloadPanel($option, $class = 'col-sm-6') {
|
|||
return deleteOfflineVideo(<?php echo $videos_id; ?>, resolution).then((video) => {
|
||||
console.log('_deleteOfflineVideo', video);
|
||||
listAllOfflineVideo();
|
||||
socketUpdateOfflineVideoSource(<?php echo json_encode($_REQUEST['socketResourceId']); ?>);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -2816,8 +2816,13 @@ function fixAdSize() {
|
|||
}
|
||||
}
|
||||
|
||||
var videoJSRecreateSourcesTimeout;
|
||||
function videoJSRecreateSources(defaultSource){
|
||||
if(empty(player) || empty(player.options_) || empty(player.updateSrc)){
|
||||
clearTimeout(videoJSRecreateSourcesTimeout);
|
||||
videoJSRecreateSourcesTimeout = setTimeout(function(){
|
||||
videoJSRecreateSources(defaultSource);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
player.options_.sources = [];
|
||||
|
|
|
@ -370,7 +370,8 @@ TimeLogEnd($timeLogNameMY, __LINE__, $TimeLogLimitMY);
|
|||
<br>
|
||||
<br>
|
||||
<div class="alert alert-warning">
|
||||
<span class="glyphicon glyphicon-facetime-video"></span> <strong><?php echo __("Attention"); ?>!</strong> <?php echo empty($advancedCustom->videoNotFoundText->value) ? __("We have not found any videos or audios to show") : $advancedCustom->videoNotFoundText->value; ?>.
|
||||
<span class="glyphicon glyphicon-facetime-video"></span>
|
||||
<strong><?php echo __("Attention"); ?>!</strong> <?php echo empty($advancedCustom->videoNotFoundText->value) ? __("We have not found any videos or audios to show") : $advancedCustom->videoNotFoundText->value; ?>.
|
||||
</div>
|
||||
<?php
|
||||
} ?>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue