#65991 Тормоза и ошибки на WebOS

This commit is contained in:
Алексей Манаев
2025-08-05 12:34:55 +00:00
parent 95d7de93d5
commit a596981765
33 changed files with 1096 additions and 272 deletions
+9
View File
@@ -0,0 +1,9 @@
{
# email для уведомлений Let's Encrypt
email am@just-work.org
}
# Хост и прокси
mule.jwrk.org {
reverse_proxy server:8080
}
+4 -4
View File
@@ -1,7 +1,7 @@
aoc_app.zip__DEL__Xi+Vnza2bfBYCpTzhMX8HO7sSyo5JfBi17aPIXWLhlgawq3P3ec6FxooSW51jvGyPKSVHAVFXNrp
NXzDtaWCXw==
config.xml__DEL__nsWBkoKSk7r0pu0ubb37gDanmRdIYcujiTiy0EErZP7ExRQajQE00K+sneKa8eSSOxH0OOecX9Ip
X++l2ykBJA==
config.xml__DEL__PRuz95KE2QodgYrVJWPvkcWMK+Y6ykfDlRjDrXdQw2vlrXdd/T/W++TTRz1pJ0LTem6M0LjxTrSS
20iJYH+sLA==
css/style.css__DEL__/QJ+/nq2xebaAiLLB7s3kvL1fT/61LBickart4MZi3T2I1+AyTrBYD61Nk91tjKcvcN6RGiA5O63
5o7u57rIRQ==
icon.png__DEL__BTGBG483ma/3HgWnreWF2xbdzr4F0gTRWjwxoSV+7kKBWt4UJTuv5WE4DhKdU4in2kwn/55O/qPD
@@ -12,5 +12,5 @@ index.html__DEL__CYjkR+ZNwp7L/if9LsT+uFjGHChwaswBoSaFu+BgC+SlB3r0YGND4cN20rlg1At
oDBqLNXYGw==
js/main.js__DEL__Gfmwxx8WB6EHvN7d/Hs+7I1Blt+5u6JI6J+BljCGddvh7brF4mywWKg8iTnDvdA8Xb35l+178i0D
hwV/qPVCxw==
author-signature.xml__DEL__M/eFnHFi2g/GYv0/D2hsHUZDLs9J1DZjCANuMtBFImSPGBr8F6JadxBVou43O8wyeEacadF9ZDG9
5prLXzM+gg==
author-signature.xml__DEL__d49WGJ3MjEY0kD9IHGog/wEPNo2zS1s5pHer6i1FM2dZalIHsRuRsAVGlVxlXM5pFSCkp9kxWf5L
1J59A2Tfgw==
@@ -9,8 +9,8 @@ NXzDtaWCXw==</DigestValue>
</Reference>
<Reference URI="config.xml">
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
<DigestValue>nsWBkoKSk7r0pu0ubb37gDanmRdIYcujiTiy0EErZP7ExRQajQE00K+sneKa8eSSOxH0OOecX9Ip
X++l2ykBJA==</DigestValue>
<DigestValue>PRuz95KE2QodgYrVJWPvkcWMK+Y6ykfDlRjDrXdQw2vlrXdd/T/W++TTRz1pJ0LTem6M0LjxTrSS
20iJYH+sLA==</DigestValue>
</Reference>
<Reference URI="css%2Fstyle.css">
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
@@ -47,49 +47,42 @@ QY10fG1EWQ==</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>
FSRsPfbPzOeJ6kYa8qwARh3fRZyttY2IZAUTNEcC+Jx6LuZxu8kZkK059HyxSfBAdPTq7r3dECv+
Jxay4KsWTXUHSFxQLgpMkFhI4wfTiN8h6hYwHqhn9sxpUnnk8YDkUVULCgMPcjq4jUeV6K/+eDwy
BshnFQQIYiYfVPUX/tXBDevovg/x+q3j9TX/7W24Fpkq9vw/ti+nbyk7KF8Uvt6cgLA2qqoslrAr
6YOymNO40uvBoKkpcMgdoRdXgkRrQZo9vDjbhfxZua2Yc+74lTXSmXVaVYmiAFGhWGUu0AJpZB+V
nbDbicP65tQwJEAKeUTBiIm7A9SGjxX6B+5Vvw==
KF5KNSA49WfpUYrUi3vmhB3JnTjqXObPGju0bzEWw3ztUJKO/uk9JG+yRbzSW+476lnPBzMGBbRo
e+q+r39TvmCAQkhL1h/1KpZbpgjvRtY6ZWspOkFXzEVjXiRtZPhGy4IJl8DgxQqDwSToyZYkKgPZ
5HUu17mxR2xIGA9DgeM=
</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>
MIIDaTCCAlGgAwIBAgIBAjANBgkqhkiG9w0BAQ0FADCBijELMAkGA1UEBhMCS1IxFDASBgNVBAgT
C1NvdXRoIEtvcmVhMQ4wDAYDVQQHEwVTdXdvbjEmMCQGA1UEChMdU2Ftc3VuZyBFbGVjdHJvbmlj
cyBDby4sIEx0ZC4xCzAJBgNVBAsTAlZEMSAwHgYDVQQDExdTYW1zdW5nIEF1dGhvciBDQSBDbGFz
czAeFw0yNTA3MDMxNTE3MDNaFw0yNjA3MDMxNTE3MDNaMEkxCTAHBgNVBAYTADEJMAcGA1UECBMA
MQkwBwYDVQQHEwAxCTAHBgNVBAoTADEJMAcGA1UECxMAMRAwDgYDVQQDEwd0di1jZXJ0MIIBIjAN
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjOugberkYHutm3WeIqlkcB56Juyf0D7tOOztFSWE
t0CP7i5RYzOXGDQ8ZtmCXQHYBxQB+1eNmpAwEHOkrw5R3qqBdDuBw3Hud1qJJgknrgNJHsUF1pg9
LxkLsvRsu6cQSIoeCh9k1LU4ytquY4ZDKn+1IABxWipMp995CoqK8kOzgLQ6Mhva8BksE7vA+t+3
KH6/d8wVSwA1vMbcnbsC+J81BrSfpHWtn38XSGPZw6D6ZKL/RHOIu6/20Ft+OXCLtu2G0M9Wg7MK
AQsB3AMRn81EF4cCEENkuw/KiNkiZCMJHXaJvbs0NKBx/tDV+PAtKcbnXGBJF4zCl/XbPIkzfwID
AQABoxowGDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIHgDANBgkqhkiG9w0BAQ0FAAOCAQEAO2yLTZ8O
Am0ad/zReLaK+b5L1438FRjYR5TVRAv/QVF7Kad1oFMDJRc9sCB6JwHyjxgTkyJInaYVojhQwazP
D7b6bj2EryUHBWCExjTFaoQ7hxZEu01E4kHtnK1OOqICgCbI3GLtH2CJXTG8QQljqKzWUIritP3o
Llp7ccuVZ2m4bWiRe8sKNfr/QJ9mAQU5BnRspcnltSTa4WhapJ/+WKc5tk7i3tl9BkMRweMA9qn/
hOeC41f8mGweeiVLxZqxNOPcgCxEQWm50x9IFo8Dxpab0m3W1u8CyHwDteVJjjhQXg2tIgzj6S3u
ev9IuF52clygqML7oLvSKq0KC0eNUA==
MIIClTCCAX2gAwIBAgIGAZhmXwGaMA0GCSqGSIb3DQEBDQUAMFYxGjAYBgNVBAoMEVRpemVuIEFz
c29jaWF0aW9uMRowGAYDVQQLDBFUaXplbiBBc3NvY2lhdGlvbjEcMBoGA1UEAwwTVGl6ZW4gRGV2
ZWxvcGVycyBDQTAeFw0yNTA4MDExNjAyMzlaFw0yNzAxMDEwMDAwMDBaMBExDzANBgNVBAMMBmVy
dDEyMzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAiwW+5wQuJiS2rNeF+b3yM5eF/GsrsJs+
iXgXdvtvU0XVEumjQsBvwqcnskHo3Wk1GgnsJx9YRdjG4MqP1JtwoSDZE6Ly8ENplH5NvJmsoTgM
i8Fx0NW7nT2NqdpKgF8ymcgxeiHPQNgPimh4gsXakga9NJhuGh1ZsJpcZcvZuyUCAwEAAaMyMDAw
DAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwDQYJKoZIhvcN
AQENBQADggEBAIH8WSGEJjLWGaVE64sQRwJ5JSc56yXWBJeLMyECOZ6hXPC/vA3xtswT3MBI3gj5
65h77/0QjBaDGWzI5PKEdYwRN7XQhQl5QvaFbv1HfqqciBwB8rpBaQu4YZNvWzMBvu1Yw9jiJOmP
Dr6hDRa/pGbd7dNUt+hr0PqubhJY43eD05PbVsUFpI3PJnQutg7bB1tj2zpUrQBHtZSozEZgSywW
Zatw8RB8uKxf/Z5cLCXK70liCwQ9kT7foYSeUGxl9sI2m1abmEVCGAVl0VT9Or+DwW1uGJEiGjIg
o/Wv4PfxEgpGUsn7dB6nHXbXPALwKRa68VFno06t0gabQWfdjlI=
</X509Certificate>
<X509Certificate>
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
</X509Certificate>
</X509Data>
</KeyInfo>
+32 -44
View File
@@ -9,13 +9,13 @@ NXzDtaWCXw==</DigestValue>
</Reference>
<Reference URI="author-signature.xml">
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
<DigestValue>M/eFnHFi2g/GYv0/D2hsHUZDLs9J1DZjCANuMtBFImSPGBr8F6JadxBVou43O8wyeEacadF9ZDG9
5prLXzM+gg==</DigestValue>
<DigestValue>d49WGJ3MjEY0kD9IHGog/wEPNo2zS1s5pHer6i1FM2dZalIHsRuRsAVGlVxlXM5pFSCkp9kxWf5L
1J59A2Tfgw==</DigestValue>
</Reference>
<Reference URI="config.xml">
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
<DigestValue>nsWBkoKSk7r0pu0ubb37gDanmRdIYcujiTiy0EErZP7ExRQajQE00K+sneKa8eSSOxH0OOecX9Ip
X++l2ykBJA==</DigestValue>
<DigestValue>PRuz95KE2QodgYrVJWPvkcWMK+Y6ykfDlRjDrXdQw2vlrXdd/T/W++TTRz1pJ0LTem6M0LjxTrSS
20iJYH+sLA==</DigestValue>
</Reference>
<Reference URI="css%2Fstyle.css">
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></DigestMethod>
@@ -52,52 +52,40 @@ GyvKX9Snig==</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>
h/6uo1RBmf2PcDwFShF1rJktgYolWJ+6oAfNxWnQaOAR4aITvD5BLGIXwA9JHLQXTtByBP2q8ODd
QUBGPXZLz67cHdzuWVHNgcb8FSuiHZzYXsCRKmgwj5BN24PHe41bZ/TSQejJTsPgTZ5R3jiOMTzP
gY67phZ7QCWj515gYvvWA4Y7Y888HbJpGFx8oStRRPl0hTLkr4dcOOMpX5XTABI2KTqNWDReiTUy
ulOE6MsdrtHEip6BynRvSgQJOGf9+iWjFm65LlHGt4G9WcVErPfhVNxXjgcxspL9ovo+vCZDSOP8
E0f5jOAM6pxBSIilM1tUg4jlcywzoNe2pSj4KQ==
YiUEi2AHQ7GtltquG6Y6TdXKQA/CtYYWtYnsO1CEpbq5oX0gQRN6RI2jrNoktuHyzZkL+jZOs7wL
dghXMoY89v4PSce418R+sQ0VHmgSfMGVdJm9vE9pKXyb6upCi+5dfNRrcUSqY7u5U6YZmcczJmnR
lStlx/B4VIzVSDs3vhE=
</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>
MIID4zCCAsugAwIBAgIBZDANBgkqhkiG9w0BAQ0FADCBjzELMAkGA1UEBhMCS1IxFDASBgNVBAgM
C1NvdXRoIEtvcmVhMQ4wDAYDVQQHDAVTdXdvbjEmMCQGA1UECgwdU2Ftc3VuZyBFbGVjdHJvbmlj
cyBDby4sIEx0ZC4xCzAJBgNVBAsMAlZEMSUwIwYDVQQDDBxWRCBERVZFTE9QRVIgUHVibGljIENB
IENsYXNzMB4XDTI1MDcwMzE1MzExNFoXDTI2MDcwMzE1MzExNFowcTERMA8GA1UEAwwIVGl6ZW5T
REsxCTAHBgNVBAsMADEJMAcGA1UECgwAMQkwBwYDVQQHDAAxCTAHBgNVBAgMADEJMAcGA1UEBhMA
MSUwIwYJKoZIhvcNAQkBFhZqc2phcmRpbmVpcm9AZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAwxqNIiL0PS/WtelG8kXZf0pehwiIodi+QOdNedi32clrSHYoGJr8yaDA
pqx9g2LRWDJM1Av9iBNz89n4GZX3HUqPSbdKmV/k0jO7OoHOXPVDbus7uIlbG/9y4kZtXx/OTKSa
XX5nThSbknwP/qQcsA+H/MSuj0zgE+40scLd72kH3NU0xAg40tdB6J7EemodOkxxQPAWXD42nNAu
KLn25sALITwpuQDBXrqPcF3496jXMStGKHYxCpObkv+4rSd8P0s5hVhX5+j1Xr8FfjCgabGK575a
zIzc+orZIBvDbLut6kyLWhbbzeog0YIuh53f3+YlPE1KAPm4X2nWpBxLFwIDAQABo2cwZTBjBgNV
HREEXDBahhRVUk46dGl6ZW46cGFja2FnZWlkPYYgVVJOOnRpemVuOmRldmljZWlkPVhUQ2pZalpY
WkJaVkuGIFVSTjp0aXplbjpkZXZpY2VpZD1YVENKWUpaWFpCWlZLMA0GCSqGSIb3DQEBDQUAA4IB
AQAJ13iGnABSVF0carYNY6DjVaazKfMpg9B30CqHz8+2IpYLhaZuN4sW5cYpA5cIb6gjbRdyCGrc
ly5YSkhd61pjeV/Lclw9+Sp0YQQ6RkYSJqLQKrRKwo3XluEGe2Yhrj3uliDAESzdtf0fVqWx+NHI
5lSsFwS1wILJ6ovPYllWzC1VYrukf6z+yZLaFdBHYAygFRzNrQjDVnsO7kGW264uULN/csop3TcO
xcPb3yi7dWbZjgKpZnNurpx8C3UEtQ4CbPL6HtvDemeEP94qDLLsUyZHwt4QaxJae8Yygs6o3L6j
ja2S8auhtN2yclvvIl1/hblxvX4+vmwNQ2bc9p6E
MIICmzCCAgQCCQDXI7WLdVZwiTANBgkqhkiG9w0BAQUFADCBjzELMAkGA1UEBhMCS1IxDjAMBgNV
BAgMBVN1d29uMQ4wDAYDVQQHDAVTdXdvbjEWMBQGA1UECgwNVGl6ZW4gVGVzdCBDQTEiMCAGA1UE
CwwZVGl6ZW4gRGlzdHJpYnV0b3IgVGVzdCBDQTEkMCIGA1UEAwwbVGl6ZW4gUHVibGljIERpc3Ry
aWJ1dG9yIENBMB4XDTEyMTAyOTEzMDMwNFoXDTIyMTAyNzEzMDMwNFowgZMxCzAJBgNVBAYTAktS
MQ4wDAYDVQQIDAVTdXdvbjEOMAwGA1UEBwwFU3V3b24xFjAUBgNVBAoMDVRpemVuIFRlc3QgQ0Ex
IjAgBgNVBAsMGVRpemVuIERpc3RyaWJ1dG9yIFRlc3QgQ0ExKDAmBgNVBAMMH1RpemVuIFB1Ymxp
YyBEaXN0cmlidXRvciBTaWduZXIwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALtMvlc5hENK
90ZdA+y66+Sy0enD1gpZDBh5T9RP0oRsptJv5jjNTseQbQi0SZOdOXb6J7iQdlBCtR343RpIEz8H
mrBy7mSY7mgwoU4EPpp4CTSUeAuKcmvrNOngTp5Hv7Ngf02TTHOLK3hZLpGayaDviyNZB5PdqQdB
hokKjzAzAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAvGp1gxxAIlFfhJH1efjb9BJK/rtRkbYn9+Ez
GEbEULg1svsgnyWisFimI3uFvgI/swzr1eKVY3Sc8MQ3+Fdy3EkbDZ2+WAubhcEkorTWjzWz2fL1
vKaYjeIsuEX6TVRUugHWudPzcEuQRLQf8ibZWjbQdBmpeQYBMg5x+xKLCJc=
</X509Certificate>
<X509Certificate>
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
VhfnkHwPltmrpYVe
</X509Certificate>
</X509Data>
</KeyInfo>
+7 -4
View File
@@ -2,12 +2,15 @@
<widget xmlns:tizen="http://tizen.org/ns/widgets" xmlns="http://www.w3.org/ns/widgets" id="http://yourdomain/BasicUI" version="1.0.0" viewmodes="maximized">
<access origin="http://188.246.226.196:8080" subdomains="false"></access>
<access origin="*" subdomains="false"></access>
<tizen:application id="ckiRCUgzke.BasicUI" package="ckiRCUgzke" required_version="8.0"/>
<tizen:application id="ckiRCUgzke.BasicUI" package="ckiRCUgzke" required_version="5.0"/>
<content src="index.html"/>
<icon src="icon.png"/>
<name>BasicUI</name>
<tizen:privilege name="http://tizen.org/privilege/download"/>
<tizen:privilege name="http://tizen.org/privilege/mediacontroller.client"/>
<tizen:privilege name="http://tizen.org/privilege/volume.set"/>
<tizen:privilege name="http://developer.samsung.com/privilege/drminfo"/>
<tizen:privilege name="http://developer.samsung.com/privilege/drmplay"/>
<tizen:privilege name="http://developer.samsung.com/privilege/network.public"/>
<tizen:privilege name="http://tizen.org/privilege/tv.inputdevice"/>
<tizen:privilege name="http://tizen.org/privilege/application.launch"/>
<tizen:profile name="tv-samsung"/>
<tizen:setting screen-orientation="portrait" context-menu="enable" background-support="disable" encryption="disable" install-location="auto" hwkey-event="enable"/>
</widget>
+19 -21
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html style="width: 100vw; height: 100vh; margin: 0; padding: 0; background: #000; overflow: hidden;">
<head>
<meta charset="utf-8" />
@@ -8,22 +8,28 @@
<title>Tizen Mobile Web Basic Application</title>
<link rel="stylesheet" type="text/css" href="css/style.css" />
<script type="text/javascript" src="$WEBAPIS/webapis/webapis.js"></script>
<script language="JavaScript" src="http://188.246.226.196:8080/distribution/vokaPlayer.global.js"></script>
<script language="JavaScript" src="https://mule.jwrk.org/distribution/vokaPlayer.global.js"></script>
</head>
<body>
<div id="main" class="page">
<div class="contents">
<span id="content-text">Basic</span>
<div id="my-video"/>
</div>
</div>
<body style="width: 100vw; height: 100vh; margin: 0; padding: 0; background: #000; overflow: hidden;">
<div id="my-video"></div>
</body>
<script language="JavaScript">
console.log(webapis)
console.log(webapis.avplay)
window.addEventListener('load', () => {
tizen.tvinputdevice.registerKey('MediaPlayPause');
tizen.tvinputdevice.registerKey('MediaRewind');
tizen.tvinputdevice.registerKey('MediaFastForward');
tizen.tvinputdevice.registerKey('MediaPlay');
tizen.tvinputdevice.registerKey('MediaPause');
tizen.tvinputdevice.registerKey('MediaStop');
tizen.tvinputdevice.registerKey('MediaTrackPrevious');
tizen.tvinputdevice.registerKey('MediaTrackNext');
tizen.tvinputdevice.registerKey('ArrowRight');
tizen.tvinputdevice.registerKey('ArrowLeft');
});
var player = spbtvplayer('my-video', {
log: true,
features: {
api: true,
drm: false,
@@ -36,7 +42,7 @@
movieId: null,
episodeId: null,
newsId: null,
apiHost: 'http://188.246.226.196:8080/',
apiHost: 'https://mule.jwrk.org',
},
uiConfig: {
initAsLive: true
@@ -62,7 +68,7 @@
},
});
player.afterInitialize(() => {
console.log("Inited 1")
console.log("Initialized")
})
player.addEventListener('play', onPlay, window)
@@ -81,7 +87,6 @@
function oncontrolsHide() { console.log("on oncontrolsHide") }
//
function controlBarHide() {
player.setControlbarVisibility(false)
}
@@ -104,11 +109,6 @@
}
</script>
<button onclick="controlBarShow()" type="button">SHOW CONTROL BAR</button>
<button onclick="controlBarHide()" type="button">HIDE CONTROL BAR</button>
<button onclick="changeQualityAuto()" type="button">AUTO QUALITY</button>
<button onclick="changeQualityLowest()" type="button">LOWEST QUALITY</button>
<button onclick="changeQualityBest()" type="button">BEST QUALITY</button>
<script>
/*
@@ -119,8 +119,6 @@ if (webapis.avplay) {
webapis.avplay.play();
}
*/
</script>
</html>
+73
View File
@@ -0,0 +1,73 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Tizen AVPlay Minimal (Official Object)</title>
<style>
html, body {
margin: 0; padding: 0;
width: 100vw; height: 100vh;
background: #000;
overflow: hidden;
}
#av-object {
position: absolute;
left: 60px;
top: 60px;
width: 800px;
height: 450px;
border: 6px solid lime;
z-index: 1000;
background: #111;
}
</style>
</head>
<body>
<div id="my-video">
<object id="av-object" type="application/avplayer"></object>
</div>
<script>
function run () {
// Координаты object элемента
var obj = document.getElementById('av-object');
var rect = obj.getBoundingClientRect();
console.log("setDisplayRect:", rect.left, rect.top, rect.width, rect.height);
// Официальный тестовый поток
var url = "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/gear1/prog_index.m3u8";
webapis.avplay.open(url);
webapis.avplay.setListener({
onbufferingstart: function() {
console.log("AVPlay: buffering start");
},
onbufferingprogress: function(percent) {
console.log("AVPlay: buffering progress", percent);
},
onbufferingcomplete: function() {
console.log("AVPlay: buffering complete");
// Важно! setDisplayRect по координатам object-а
webapis.avplay.setDisplayRect(
Math.round(rect.left),
Math.round(rect.top),
Math.round(rect.width),
Math.round(rect.height)
);
webapis.avplay.play();
},
onrenderingstart: function () {
console.log("AVPlay: video rendering started!");
},
oncurrentplaytime: function (t) {
// console.log("Current time:", t);
},
onerror: function (err) {
console.error("AVPlay error:", err);
}
});
webapis.avplay.prepareAsync();
};
run();
</script>
</body>
</html>
+1
View File
@@ -0,0 +1 @@
8e961301-1a56-4a69-ad97-a82e65bd4a3e
+4 -2
View File
@@ -1,9 +1,11 @@
<!DOCTYPE html>
<html>
<head> </head>
<head>
<script type="text/javascript" src="webOSTV.js"></script>
</head>
<body>
<script>
window.location.href = "http://localhost:8080/?apiHost=localhost:8080";
window.location.href = "https://mule.jwrk.org/index.html?apiHost=https://mule.jwrk.org";
</script>
</body>
</html>
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+181
View File
@@ -0,0 +1,181 @@
<!DOCTYPE html>
<html lang="en" style="width: 100%; height: 100%">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"></script>
<script language="JavaScript" src="./distribution/vokaPlayer.global.js"></script>
</head>
<body style="width: 100%; height: 100%">
<h1>Hello world</h1>
<div id="my-video" style="width: 100%; height: 100%"></div>
<button onclick="controlBarShow()" type="button">SHOW CONTROL BAR</button>
<button onclick="controlBarHide()" type="button">HIDE CONTROL BAR</button>
<button onclick="changeQualityAuto()" type="button">AUTO QUALITY</button>
<button onclick="changeQualityLowest()" type="button">LOWEST QUALITY</button>
<button onclick="changeQualityBest()" type="button">BEST QUALITY</button>
<button onclick="setEngAudioTrack()" type="button">ENGLISH AUDIO</button>
<button onclick="setDubAudioTrack()" type="button">DUBBING AUDIO</button>
<button onclick="disableSubs()" type="button">DISABLE SUBS</button>
<button onclick="setDeSubs()" type="button">DEUTCH SUBS</button>
<button onclick="setEnSubs()" type="button">ENGLISH SUBS</button>
<h2>Logs</h2>
<div id="logs" class="logs"></div>
<script language="JavaScript">
var player = spbtvplayer('my-video', {
log: true,
features: {
api: true,
drm: false,
metrics: true
},
apiConfig: {
channelId: 'dash-playready-vod',
urlGetParams: 'minheight=400',
clientId: null,
movieId: null,
episodeId: null,
newsId: null,
},
uiConfig: {
initAsLive: true
},
globalOpts: {
uiLanguage: 'ru'
},
streamOpts: {
autoplay: true
},
controls: {
externalSubtitles: {
url: "https://raw.githubusercontent.com/videojs/video.js/c7298d40a4632a6e9dfcd5a2f5cc3bbe92a78744/docs/examples/elephantsdream/captions.ru.vtt",
lang: 'ru'
},
zoomButton: {
isVisible: true,
enable: true,
},
editing: {
enable: true,
},
},
});
player.afterInitialize(() => {
console.log("afterInitialize")
player.setControlbarVisibility(false)
})
player.addEventListener('play', onPlay, window)
player.addEventListener('pause', onPause, window)
player.addEventListener('canplay', onCanPlay, window)
player.addEventListener('controlbarShow', onControlsShow, window)
player.addEventListener('controlbarHide', oncontrolsHide, window)
function onPlay() { console.log("on PLAY") }
function onPause() { console.log("on PAUSE") }
function onCanPlay() { console.log("on CANPLAY") }
function onControlsShow() { console.log("on onControlsShow") }
function oncontrolsHide() { console.log("on oncontrolsHide") }
//
function controlBarHide() {
player.setControlbarVisibility(false)
}
function controlBarShow() {
player.setControlbarVisibility(true)
}
function changeQualityAuto() {
player.setSelectedVideoQuality(-1)
}
function changeQualityLowest() {
player.setSelectedVideoQuality(0)
}
function changeQualityBest() {
var quality = player.getVideoQualityList()
player.setSelectedVideoQuality(quality.length - 1)
}
function setDubAudioTrack() {
player.setCurrentAudioTrack(1)
}
function setEngAudioTrack() {
player.setCurrentAudioTrack(0)
}
function disableSubs() {
player.setCurrentSubtitlesTrack(-1)
}
function setDeSubs() {
player.setCurrentSubtitlesTrack(0)
}
function setEnSubs() {
player.setCurrentSubtitlesTrack(1)
}
const fireEvents = [
'play',
'pause',
'canplay',
'ended',
// 'timeupdate',
'error',
'volumechange',
'controlbarShow',
'controlbarHide',
'qualityChange',
'sourceAttached',
'zoomButtonChange',
'trackChange',
'bufferLengthUpdate',
'zoomModeChange',
'toolboxStartSel',
'toolboxEndSel',
'toolboxProcessSel',
'destroyed',
'bufferingUpdate',
'timeshiftUpdate',
]
function currentTime() {
const d = new Date()
const h = `${d.getHours()}`.padStart(2, '0')
const m = `${d.getMinutes()}`.padStart(2, '0')
const s = `${d.getSeconds()}`.padStart(2, '0')
return h + ":" + m + ":" + s
}
function addLog (message) {
const element = document.createElement('div')
element.classList.add('log-item')
element.innerHTML = '<b class="current-time">' + currentTime() + '</b> ' + message;
document.querySelector("#logs").prepend(element)
}
fireEvents.forEach(event => {
player.addEventListener(event, (payload) => {
addLog (`<i>${event}</i> : ${JSON.stringify(payload)}`)
})
})
</script>
<style>
.logs {
display: flex;
flex-direction: column;
height: 600px;
border: 1px solid gray;
overflow: auto;
}
</style>
</body>
</html>
+182
View File
@@ -0,0 +1,182 @@
<!DOCTYPE html>
<html lang="en" style="width: 100%; height: 100%">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"></script>
<script language="JavaScript" src="./distribution/vokaPlayer.global.js"></script>
</head>
<body style="width: 100%; height: 100%">
<h1>Hello world</h1>
<div id="my-video" style="width: 100%; height: 100%"></div>
<button onclick="controlBarShow()" type="button">SHOW CONTROL BAR</button>
<button onclick="controlBarHide()" type="button">HIDE CONTROL BAR</button>
<button onclick="changeQualityAuto()" type="button">AUTO QUALITY</button>
<button onclick="changeQualityLowest()" type="button">LOWEST QUALITY</button>
<button onclick="changeQualityBest()" type="button">BEST QUALITY</button>
<button onclick="setEngAudioTrack()" type="button">ENGLISH AUDIO</button>
<button onclick="setDubAudioTrack()" type="button">DUBBING AUDIO</button>
<button onclick="disableSubs()" type="button">DISABLE SUBS</button>
<button onclick="setDeSubs()" type="button">DEUTCH SUBS</button>
<button onclick="setEnSubs()" type="button">ENGLISH SUBS</button>
<h2>Logs</h2>
<div id="logs" class="logs"></div>
<script language="JavaScript">
var player = spbtvplayer('my-video', {
log: true,
features: {
api: true,
drm: false,
metrics: true
},
apiConfig: {
channelId: 'dash-widevine-vod',
urlGetParams: 'minheight=400',
clientId: null,
movieId: null,
episodeId: null,
newsId: null,
// apiHost: 'localhost:8080',
},
uiConfig: {
initAsLive: true
},
globalOpts: {
uiLanguage: 'ru'
},
streamOpts: {
autoplay: true
},
controls: {
externalSubtitles: {
url: "https://raw.githubusercontent.com/videojs/video.js/c7298d40a4632a6e9dfcd5a2f5cc3bbe92a78744/docs/examples/elephantsdream/captions.ru.vtt",
lang: 'ru'
},
zoomButton: {
isVisible: true,
enable: true,
},
editing: {
enable: true,
},
},
});
player.afterInitialize(() => {
console.log("afterInitialize")
player.setControlbarVisibility(false)
})
player.addEventListener('play', onPlay, window)
player.addEventListener('pause', onPause, window)
player.addEventListener('canplay', onCanPlay, window)
player.addEventListener('controlbarShow', onControlsShow, window)
player.addEventListener('controlbarHide', oncontrolsHide, window)
function onPlay() { console.log("on PLAY") }
function onPause() { console.log("on PAUSE") }
function onCanPlay() { console.log("on CANPLAY") }
function onControlsShow() { console.log("on onControlsShow") }
function oncontrolsHide() { console.log("on oncontrolsHide") }
//
function controlBarHide() {
player.setControlbarVisibility(false)
}
function controlBarShow() {
player.setControlbarVisibility(true)
}
function changeQualityAuto() {
player.setSelectedVideoQuality(-1)
}
function changeQualityLowest() {
player.setSelectedVideoQuality(0)
}
function changeQualityBest() {
var quality = player.getVideoQualityList()
player.setSelectedVideoQuality(quality.length - 1)
}
function setDubAudioTrack() {
player.setCurrentAudioTrack(1)
}
function setEngAudioTrack() {
player.setCurrentAudioTrack(0)
}
function disableSubs() {
player.setCurrentSubtitlesTrack(-1)
}
function setDeSubs() {
player.setCurrentSubtitlesTrack(0)
}
function setEnSubs() {
player.setCurrentSubtitlesTrack(1)
}
const fireEvents = [
'play',
'pause',
'canplay',
'ended',
// 'timeupdate',
'error',
'volumechange',
'controlbarShow',
'controlbarHide',
'qualityChange',
'sourceAttached',
'zoomButtonChange',
'trackChange',
'bufferLengthUpdate',
'zoomModeChange',
'toolboxStartSel',
'toolboxEndSel',
'toolboxProcessSel',
'destroyed',
'bufferingUpdate',
'timeshiftUpdate',
]
function currentTime() {
const d = new Date()
const h = `${d.getHours()}`.padStart(2, '0')
const m = `${d.getMinutes()}`.padStart(2, '0')
const s = `${d.getSeconds()}`.padStart(2, '0')
return h + ":" + m + ":" + s
}
function addLog (message) {
const element = document.createElement('div')
element.classList.add('log-item')
element.innerHTML = '<b class="current-time">' + currentTime() + '</b> ' + message;
document.querySelector("#logs").prepend(element)
}
fireEvents.forEach(event => {
player.addEventListener(event, (payload) => {
addLog (`<i>${event}</i> : ${JSON.stringify(payload)}`)
})
})
</script>
<style>
.logs {
display: flex;
flex-direction: column;
height: 600px;
border: 1px solid gray;
overflow: auto;
}
</style>
</body>
</html>
+3 -3
View File
@@ -1,14 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" style="width: 100%; height: 100%">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"></script>
<script language="JavaScript" src="./distribution/vokaPlayer.global.js"></script>
</head>
<body>
<body style="width: 100%; height: 100%">
<h1>Hello world</h1>
<div id="my-video"></div>
<div id="my-video" style="width: 100%; height: 100%"></div>
<button onclick="controlBarShow()" type="button">SHOW CONTROL BAR</button>
<button onclick="controlBarHide()" type="button">HIDE CONTROL BAR</button>
<button onclick="changeQualityAuto()" type="button">AUTO QUALITY</button>
-1
View File
@@ -29,7 +29,6 @@
movieId: null,
episodeId: null,
newsId: null,
apiHost: 'localhost:8080',
},
uiConfig: {
initAsLive: true
+1 -2
View File
@@ -9,7 +9,7 @@
<body>
<h1>Hello world</h1>
<p>
Демка показывает возможность воспроизведения hls widevine vod
Демка показывает возможность воспроизведения hls fairplay vod
</p>
<p>
Исходник https://hlsjs.video-dev.org/demo/?src=https%3A%2F%2Fstorage.googleapis.com%2Fshaka-demo-assets%2Fangel-one-widevine-hls%2Fhls.m3u8&demoConfig=eyJlbmFibGVTdHJlYW1pbmciOnRydWUsImF1dG9SZWNvdmVyRXJyb3IiOnRydWUsInN0b3BPblN0YWxsIjpmYWxzZSwiZHVtcGZNUDQiOmZhbHNlLCJsZXZlbENhcHBpbmciOi0xLCJsaW1pdE1ldHJpY3MiOi0xfQ==
@@ -29,7 +29,6 @@
movieId: null,
episodeId: null,
newsId: null,
apiHost: 'localhost:8080',
},
uiConfig: {
initAsLive: true
-1
View File
@@ -29,7 +29,6 @@
movieId: null,
episodeId: null,
newsId: null,
apiHost: 'localhost:8080',
},
uiConfig: {
initAsLive: true
+182
View File
@@ -0,0 +1,182 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"></script>
<script language="JavaScript" src="./distribution/vokaPlayer.global.js"></script>
</head>
<body>
<h1>Hello world</h1>
<div id="my-video"></div>
<button onclick="controlBarShow()" type="button">SHOW CONTROL BAR</button>
<button onclick="controlBarHide()" type="button">HIDE CONTROL BAR</button>
<button onclick="changeQualityAuto()" type="button">AUTO QUALITY</button>
<button onclick="changeQualityLowest()" type="button">LOWEST QUALITY</button>
<button onclick="changeQualityBest()" type="button">BEST QUALITY</button>
<button onclick="setEngAudioTrack()" type="button">ENGLISH AUDIO</button>
<button onclick="setDubAudioTrack()" type="button">DUBBING AUDIO</button>
<button onclick="disableSubs()" type="button">DISABLE SUBS</button>
<button onclick="setDeSubs()" type="button">DEUTCH SUBS</button>
<button onclick="setEnSubs()" type="button">ENGLISH SUBS</button>
<h2>Logs</h2>
<div id="logs" class="logs"></div>
<script language="JavaScript">
var player = spbtvplayer('my-video', {
log: true,
features: {
api: true,
drm: false,
metrics: true
},
apiConfig: {
channelId: 'hls-998f5396-c9dd-4a1e-82c7-0aec531fc015',
urlGetParams: 'minheight=400',
clientId: null,
movieId: null,
episodeId: null,
newsId: null,
// apiHost: 'localhost:8080',
},
uiConfig: {
initAsLive: true
},
globalOpts: {
uiLanguage: 'ru'
},
streamOpts: {
autoplay: true
},
controls: {
externalSubtitles: {
url: "https://raw.githubusercontent.com/videojs/video.js/c7298d40a4632a6e9dfcd5a2f5cc3bbe92a78744/docs/examples/elephantsdream/captions.ru.vtt",
lang: 'ru'
},
zoomButton: {
isVisible: true,
enable: true,
},
editing: {
enable: true,
},
},
});
player.afterInitialize(() => {
console.log("afterInitialize")
player.setControlbarVisibility(false)
})
player.addEventListener('play', onPlay, window)
player.addEventListener('pause', onPause, window)
player.addEventListener('canplay', onCanPlay, window)
player.addEventListener('controlbarShow', onControlsShow, window)
player.addEventListener('controlbarHide', oncontrolsHide, window)
function onPlay() { console.log("on PLAY") }
function onPause() { console.log("on PAUSE") }
function onCanPlay() { console.log("on CANPLAY") }
function onControlsShow() { console.log("on onControlsShow") }
function oncontrolsHide() { console.log("on oncontrolsHide") }
//
function controlBarHide() {
player.setControlbarVisibility(false)
}
function controlBarShow() {
player.setControlbarVisibility(true)
}
function changeQualityAuto() {
player.setSelectedVideoQuality(-1)
}
function changeQualityLowest() {
player.setSelectedVideoQuality(0)
}
function changeQualityBest() {
var quality = player.getVideoQualityList()
player.setSelectedVideoQuality(quality.length - 1)
}
function setDubAudioTrack() {
player.setCurrentAudioTrack(1)
}
function setEngAudioTrack() {
player.setCurrentAudioTrack(0)
}
function disableSubs() {
player.setCurrentSubtitlesTrack(-1)
}
function setDeSubs() {
player.setCurrentSubtitlesTrack(0)
}
function setEnSubs() {
player.setCurrentSubtitlesTrack(1)
}
const fireEvents = [
'play',
'pause',
'canplay',
'ended',
// 'timeupdate',
'error',
'volumechange',
'controlbarShow',
'controlbarHide',
'qualityChange',
'sourceAttached',
'zoomButtonChange',
'trackChange',
'bufferLengthUpdate',
'zoomModeChange',
'toolboxStartSel',
'toolboxEndSel',
'toolboxProcessSel',
'destroyed',
'bufferingUpdate',
'timeshiftUpdate',
]
function currentTime() {
const d = new Date()
const h = `${d.getHours()}`.padStart(2, '0')
const m = `${d.getMinutes()}`.padStart(2, '0')
const s = `${d.getSeconds()}`.padStart(2, '0')
return h + ":" + m + ":" + s
}
function addLog (message) {
const element = document.createElement('div')
element.classList.add('log-item')
element.innerHTML = '<b class="current-time">' + currentTime() + '</b> ' + message;
document.querySelector("#logs").prepend(element)
}
fireEvents.forEach(event => {
player.addEventListener(event, (payload) => {
addLog (`<i>${event}</i> : ${JSON.stringify(payload)}`)
})
})
</script>
<style>
.logs {
display: flex;
flex-direction: column;
height: 600px;
border: 1px solid gray;
overflow: auto;
}
</style>
</body>
</html>
@@ -0,0 +1,21 @@
{
"data": {
"url": "https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd",
"drm": {
"type": "playready",
"license_server": "https://drm-playready-licensing.axtest.net/AcquireLicense"
},
"adv1": "https://cdn.theoplayer.com/demos/ads/vmap/single-pre-mid-post-no-skip.xml",
"subtitles": "https://raw.githubusercontent.com/videojs/video.js/c7298d40a4632a6e9dfcd5a2f5cc3bbe92a78744/docs/examples/elephantsdream/captions.ru.vtt",
"analytics_v2": {
"url": "https://juraldinio.com/analytics/",
"interval": 5000,
"additional_parameters": {
"application_id": "42",
"user_id": "12321",
"resource_type": "video",
"watch_session_id": "100500"
}
}
}
}
@@ -1,6 +1,6 @@
{
"data": {
"url": "https://streaming-iptv.voka.tv/eyJvc19uYW1lIjoibWFjIG9zIiwidXNlcl91aWQiOiIzMDE4ZmM2Yi02YmY4LTQ4OGQtOGRhMi02YjZmNWFmMjFjNDQiLCJkYXRhY2VudGVyIjoiaW50IiwiZG9tYWluX25hbWUiOiJzdHJlYW1pbmctaXB0di52b2thLnR2IiwiZHJtIjoid2lkZXZpbmUiLCJleHBpcmF0aW9uX2RhdGUiOiIyMDI1LTA3LTMxVDIwOjUxOjEzWiIsImlwX2FkZHJlc3MiOiIxODUuMTM1LjE1MC40MCIsInByb2plY3QiOiJ2b2thX3Byb2R1Y3Rpb24iLCJwcm90b2NvbCI6ImRhc2giLCJzZXNzaW9uX2lkIjoiM2MzYTkxMjMtY2VkZi00ODdmLWI4MzItZjhkYjNmNWZkMmY0Iiwic3RyZWFtX25hbWUiOiI1ODMiLCJzdHJlYW1fcGF0aCI6Ii9pcF92NSJ9/MCwCFAaa_NjyUSswHJxTYp3hATrVF-cFAhQFUugUrPqnUOYfpnkl5RuhIAcuSA%3D%3D/ip_v5/583.mpd?b_app_channel_id=7e8cedbe-5f18-4649-8468-dd5af597a670&b_app_id=voka_production&b_device_platform=mac%20os&b_device_uid=1d866e05-b0e5-e943-f49c-295ac85c20d7&b_stream_sid=3c3a9123-cedf-487f-b832-f8db3f5fd2f4&b_strmr_channel_id=583&stream_dvr_window=10800000000 ",
"url": "https://streaming-iptv.voka.tv/eyJvc19uYW1lIjoibWFjIG9zIiwidXNlcl91aWQiOiIzMDE4ZmM2Yi02YmY4LTQ4OGQtOGRhMi02YjZmNWFmMjFjNDQiLCJkYXRhY2VudGVyIjoiaW50IiwiZG9tYWluX25hbWUiOiJzdHJlYW1pbmctaXB0di52b2thLnR2IiwiZHJtIjoic3BidHZjYXMiLCJleHBpcmF0aW9uX2RhdGUiOiIyMDI1LTA4LTAzVDE3OjQxOjU0WiIsImlwX2FkZHJlc3MiOiI4OS4xMTAuMTIyLjE3NSIsInByb2plY3QiOiJ2b2thX3Byb2R1Y3Rpb24iLCJwcm90b2NvbCI6ImRhc2giLCJzZXNzaW9uX2lkIjoiNzkzNmJmY2EtZTA1ZS00NjJjLWE1MTQtZTU3NTk0MmY2ZDc2Iiwic3RyZWFtX25hbWUiOiI1ODMiLCJzdHJlYW1fcGF0aCI6Ii9pcF92NSJ9/MCwCFHXIZystCP_74eXig9GDBoGggAxjAhRGzvQFAovCN4VwluftKYV6tBmWFQ%3D%3D/ip_v5/583.mpd?b_app_channel_id=7e8cedbe-5f18-4649-8468-dd5af597a670&b_app_id=voka_production&b_device_platform=mac%20os&b_device_uid=1d866e05-b0e5-e943-f49c-295ac85c20d7&b_stream_sid=7936bfca-e05e-462c-a514-e575942f6d76&b_strmr_channel_id=583&stream_dvr_window=10800000000",
"drm": {
"type": "widevine",
"license_server": "https://drmproxy.voka.tv"
@@ -0,0 +1,22 @@
{
"data": {
"url-1": "https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd",
"url": "https://media.axprod.net/TestVectors/Dash/protected_dash_1080p_h264_singlekey/manifest.mpd",
"drm": {
"type": "widevine",
"license_server": "https://drm-widevine-licensing.axtest.net/AcquireLicense"
},
"adv1": "https://cdn.theoplayer.com/demos/ads/vmap/single-pre-mid-post-no-skip.xml",
"subtitles": "https://raw.githubusercontent.com/videojs/video.js/c7298d40a4632a6e9dfcd5a2f5cc3bbe92a78744/docs/examples/elephantsdream/captions.ru.vtt",
"analytics_v2": {
"url": "https://juraldinio.com/analytics/",
"interval": 5000,
"additional_parameters": {
"application_id": "42",
"user_id": "12321",
"resource_type": "video",
"watch_session_id": "100500"
}
}
}
}
@@ -1,8 +1,26 @@
{
"data": {
"url2": "http://mirrors.standaloneinstaller.com/video-sample/jellyfish-25-mbps-hd-hevc.mp4",
"urlq": "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8",
"url": "https://cdn.bitmovin.com/content/assets/sintel/hls/playlist.m3u8",
"url-simple": "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/gear1/prog_index.m3u8",
"url": "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8",
"url-dash": "https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps.mpd",
"url-blocked": "https://cdn.bitmovin.com/content/assets/sintel/hls/playlist.m3u8",
"url-hls-videwine-vod": "https://storage.googleapis.com/shaka-demo-assets/angel-one-widevine-hls/hls.m3u8",
"drm-hls-videwine-vod": {
"type": "widevine",
"license_server": "https://cwip-shaka-proxy.appspot.com/no_auth"
},
"dash-widevine": "mark",
"url-dash-widevine": "https://media.axprod.net/TestVectors/Dash/protected_dash_1080p_h264_singlekey/manifest.mpd",
"drm-dash-widevine": {
"type": "widevine",
"license_server": "https://drm-widevine-licensing.axtest.net/AcquireLicense"
},
"drm-dash-playready": {
"type": "playready",
"license_server": "https://drm-playready-licensing.axprod.net/AcquireLicense"
},
"url-hls-live": "https://dai.google.com/linear/hls/event/rtcMlf4RTvOEkaudeany5w/master.m3u8?iu=/4128/CBS.NY.OTT",
"adv1": "https://cdn.theoplayer.com/demos/ads/vmap/single-pre-mid-post-no-skip.xml",
"subtitles": "https://raw.githubusercontent.com/videojs/video.js/c7298d40a4632a6e9dfcd5a2f5cc3bbe92a78744/docs/examples/elephantsdream/captions.ru.vtt",
"analytics_v2": {
+45
View File
@@ -0,0 +1,45 @@
version: '3.8'
services:
server:
image: docker.io/library/voka-player
container_name: voka-player
environment:
- USER_UID=1000
- USER_GID=1000
restart: always
volumes:
- $PWD:/usr/voka
- node_modules:/usr/voka/node_modules
networks:
- web
labels:
- "traefik.enable=false" # просто чтобы избежать конфликтов
caddy:
image: caddy:2
container_name: caddy
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
networks:
- web
volumes:
node_modules:
driver: local
driver_opts:
type: none
o: bind
device: $PWD/node_modules
caddy_data: {}
caddy_config: {}
networks:
web:
driver: bridge
+1
View File
@@ -1,5 +1,6 @@
.voka-poster {
display: inline-block;
z-index: -1000;
vertical-align: middle;
background-repeat: no-repeat;
background-position: 50% 50%;
+11 -23
View File
@@ -12,7 +12,7 @@ import {
MediaPlayerErrorEvent,
QualityChangeRenderedEvent, Representation,
StreamInitializedEvent,
} from 'dashjs'
} from "dashjs";
import {
ComponentErrorInfo,
VokaError,
@@ -591,21 +591,11 @@ export default class VokaDash {
view: any
): MediaPlayerClass {
const mediaPlayer = MediaPlayer().create()
// For whatever reason, we need to call setTextDefaultEnabled(false) to get
// VTT captions to show, even though we're doing virtually the same thing
// in setup-text-tracks.js
// @TODO разобраться, почему нет такого метода
// mediaPlayer.setTextDefaultEnabled(false)
// mediaPlayer.extend('HTTPLoader', HTTPLoader, true)
// Must run controller before these two lines or else there is no
// element to bind to.
mediaPlayer.initialize()
mediaPlayer.setProtectionData(keySystemOptions)
this.attachListeners(mediaPlayer)
//Хотя что мы вообще собираемся посылать?
// Apply all dash options that are set
if (options) {
Object.keys(options).forEach((key) => {
@@ -632,17 +622,7 @@ export default class VokaDash {
})
}
mediaPlayer.attachView(view)
// Dash.js autoplays by default, video.js will handle autoplay
mediaPlayer.setAutoPlay(false)
// Setup text tracks
//setupTextTracks.call(null, this.player, tech, options)
// Attach the source with any protection data
mediaPlayer.setProtectionData(keySystemOptions)
mediaPlayer.attachSource(manifestSource)
mediaPlayer.initialize(view, manifestSource, false)
this.player.trigger(VokaEvent.MasterDashPlaylistLoad, manifestSource)
@@ -702,6 +682,14 @@ export default class VokaDash {
this.setBufferingComplete,
this
)
mediaPlayer.on(
'public_keySystemSelected',
e => console.log("Key system selected: ", e),
)
mediaPlayer.on(
'public_keyAdded',
e => console.log("Key added:", e),
)
}
private updateBufferingMode(value: boolean) {
+88 -63
View File
@@ -1,3 +1,9 @@
import {
IVokaSource,
TizenSourceParams,
TizenSourceProtection,
WebOSSourceProtection,
} from "@/internal/player/native/VokaSourceHandler";
import videojs from 'video.js'
import Player from 'video.js/dist/types/player'
import chromecastPlugin from '@silvermine/videojs-chromecast/'
@@ -251,80 +257,93 @@ namespace VokaCorePlayer {
throw new Error('Empty content URL')
}
let playableContent = null
const drmTypes = {
[DRMType.WIDEVINE]: 'com.widevine.alpha',
[DRMType.PLAYREADY]: 'com.microsoft.playready',
[DRMType.FAIRPLAY]: 'com.apple.fps',
}
let playableContent: IVokaSource = null
const drm = content.drmConfig
switch(content.type) {
case VokaContentType.HLS:
playableContent = {
sourceType: "application/vnd.apple.mpegurl",
}
} as IVokaSource
if (!drm) break
switch (drm.type) {
case DRMType.WIDEVINE:
playableContent["drmSystems"] = {
'com.widevine.alpha': {
licenseUrl: drm.certificateUrl,
}
const drmTypeString = drmTypes[drm.type]
if (!drmTypeString) break
if (drm.type === DRMType.FAIRPLAY) {
// hls.js
playableContent.drmSystems = {
[drmTypeString]: {
certificateUrl: drm.certificateUrl,
licenseUrl: drm.licenseUrl
}
break
case DRMType.PLAYREADY:
playableContent["drmSystems"] = {
'com.microsoft.playready': {
licenseUrl: drm.certificateUrl,
}
}
} else {
// hls.js
playableContent.drmSystems = {
[drmTypeString]: {
licenseUrl: drm.certificateUrl
}
break
// Подержка FP в hls.js
case DRMType.FAIRPLAY:
playableContent["drmSystems"] = {
'com.apple.fps': {
certificateUrl: drm.certificateUrl,
licenseUrl: drm.licenseUrl,
}
}
// WebOS native
playableContent.webOSProtection = {
licenseServer: drm.certificateUrl
} as WebOSSourceProtection
// Tizen native
playableContent.tizenParams = {
protection: {
type: drm.type,
licenseServer: drm.certificateUrl
}
break
}
break
case VokaContentType.MP4:
playableContent = {
sourceType: "video/mp4",
} as TizenSourceParams
}
break
case VokaContentType.DASH:
case VokaContentType.WIDEVINE:
case VokaContentType.PLAYREADY:
playableContent = {
sourceType: "application/dash+xml",
}
break
case VokaContentType.WIDEVINE:
if (drm != null && drm.type == DRMType.WIDEVINE) {
playableContent = {
sourceType: "application/dash+xml",
keySystemOptions: [{
name: "com.widevine.alpha",
options: {
serverURL: drm.certificateUrl,
httpRequestHeaders: drm.headers,
priority: 0,
}
}]
}
}
break
case VokaContentType.PLAYREADY:
if (drm != null && drm.type == DRMType.PLAYREADY) {
playableContent = {
sourceType: "application/dash+xml",
keySystemOptions: [{
name: "com.microsoft.playready",
options: {
serverURL: drm.certificateUrl,
httpRequestHeaders: drm.headers,
priority: 0,
}
}]
}
}
break
} as IVokaSource;
if (!drm) break;
const allowedDrmTypes = [DRMType.WIDEVINE, DRMType.PLAYREADY];
if (!allowedDrmTypes.includes(drm.type)) break;
// dash.js
playableContent.keySystemOptions = [{
name: drmTypes[drm.type],
options: {
serverURL: drm.certificateUrl,
// Доп. заголовки, например, авторизация запсроса к серверу лицензий
// httpRequestHeaders: {
// "X-AxDRM-Message": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.ewogICJ2ZXJzaW9uIjogMSwKICAiY29tX2tleV9pZCI6ICI2OWU1NDA4OC1lOWUwLTQ1MzAtOGMxYS0xZWI2ZGNkMGQxNGUiLAogICJtZXNzYWdlIjogewogICAgInR5cGUiOiAiZW50aXRsZW1lbnRfbWVzc2FnZSIsCiAgICAidmVyc2lvbiI6IDIsCiAgICAibGljZW5zZSI6IHsKICAgICAgImFsbG93X3BlcnNpc3RlbmNlIjogdHJ1ZQogICAgfSwKICAgICJjb250ZW50X2tleXNfc291cmNlIjogewogICAgICAiaW5saW5lIjogWwogICAgICAgIHsKICAgICAgICAgICJpZCI6ICI0MDYwYTg2NS04ODc4LTQyNjctOWNiZi05MWFlNWJhZTFlNzIiLAogICAgICAgICAgImVuY3J5cHRlZF9rZXkiOiAid3QzRW51dVI1UkFybjZBRGYxNkNCQT09IiwKICAgICAgICAgICJ1c2FnZV9wb2xpY3kiOiAiUG9saWN5IEEiCiAgICAgICAgfQogICAgICBdCiAgICB9LAogICAgImNvbnRlbnRfa2V5X3VzYWdlX3BvbGljaWVzIjogWwogICAgICB7CiAgICAgICAgIm5hbWUiOiAiUG9saWN5IEEiLAogICAgICAgICJwbGF5cmVhZHkiOiB7CiAgICAgICAgICAibWluX2RldmljZV9zZWN1cml0eV9sZXZlbCI6IDE1MCwKICAgICAgICAgICJwbGF5X2VuYWJsZXJzIjogWwogICAgICAgICAgICAiNzg2NjI3RDgtQzJBNi00NEJFLThGODgtMDhBRTI1NUIwMUE3IgogICAgICAgICAgXQogICAgICAgIH0KICAgICAgfQogICAgXQogIH0KfQ.l8PnZznspJ6lnNmfAE9UQV532Ypzt1JXQkvrk8gFSRw"
// },
httpRequestHeaders: drm.headers,
priority: 0,
},
},
];
// WebOS native
playableContent.webOSProtection = {
licenseServer: drm.certificateUrl,
} as WebOSSourceProtection;
// Tizen native
playableContent.tizenParams = {
protection: {
type: drm.type,
licenseServer: drm.certificateUrl,
// Доп. заголовки, например, авторизация запроса к серверу лицензий
// В виде строки, разделитель заголовков - \n
// httpHeader:
// "X-AxDRM-Message:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.ewogICJ2ZXJzaW9uIjogMSwKICAiY29tX2tleV9pZCI6ICI2OWU1NDA4OC1lOWUwLTQ1MzAtOGMxYS0xZWI2ZGNkMGQxNGUiLAogICJtZXNzYWdlIjogewogICAgInR5cGUiOiAiZW50aXRsZW1lbnRfbWVzc2FnZSIsCiAgICAidmVyc2lvbiI6IDIsCiAgICAibGljZW5zZSI6IHsKICAgICAgImFsbG93X3BlcnNpc3RlbmNlIjogdHJ1ZQogICAgfSwKICAgICJjb250ZW50X2tleXNfc291cmNlIjogewogICAgICAiaW5saW5lIjogWwogICAgICAgIHsKICAgICAgICAgICJpZCI6ICI0MDYwYTg2NS04ODc4LTQyNjctOWNiZi05MWFlNWJhZTFlNzIiLAogICAgICAgICAgImVuY3J5cHRlZF9rZXkiOiAid3QzRW51dVI1UkFybjZBRGYxNkNCQT09IiwKICAgICAgICAgICJ1c2FnZV9wb2xpY3kiOiAiUG9saWN5IEEiCiAgICAgICAgfQogICAgICBdCiAgICB9LAogICAgImNvbnRlbnRfa2V5X3VzYWdlX3BvbGljaWVzIjogWwogICAgICB7CiAgICAgICAgIm5hbWUiOiAiUG9saWN5IEEiLAogICAgICAgICJwbGF5cmVhZHkiOiB7CiAgICAgICAgICAibWluX2RldmljZV9zZWN1cml0eV9sZXZlbCI6IDE1MCwKICAgICAgICAgICJwbGF5X2VuYWJsZXJzIjogWwogICAgICAgICAgICAiNzg2NjI3RDgtQzJBNi00NEJFLThGODgtMDhBRTI1NUIwMUE3IgogICAgICAgICAgXQogICAgICAgIH0KICAgICAgfQogICAgXQogIH0KfQ.l8PnZznspJ6lnNmfAE9UQV532Ypzt1JXQkvrk8gFSRw"
},
} as TizenSourceParams;
break;
// Нативный FP (Safari & iOS)
case VokaContentType.FAIRPLAY:
if (drm != null && drm.type == DRMType.FAIRPLAY) {
@@ -335,14 +354,20 @@ namespace VokaCorePlayer {
certificateUrl: drm.certificateUrl,
licenseUrl: drm.licenseUrl,
},
}
} as IVokaSource
}
break
case VokaContentType.AES:
playableContent = {
sourceType: "application/vnd.apple.mpegurl",
}
} as IVokaSource
break
case VokaContentType.MP4:
playableContent = {
sourceType: "video/mp4",
} as IVokaSource
break
}
if (playableContent != null) {
@@ -410,7 +435,7 @@ namespace VokaCorePlayer {
// Create main container for player.
const playerContainer = Dom.createEl(
'object',
'div',
{ id: `${DivID}` },
{}
)
@@ -23,6 +23,8 @@ interface WebOSSourceProtection {
interface TizenSourceProtection {
type: string
licenseServer: string
// ex: "X-AxDRM-Message:" + DRMToken
httpHeader: string
}
interface TizenSourceParams {
protection: TizenSourceProtection | null
@@ -41,8 +43,9 @@ interface IVokaSource {
sourceType: string
src: string
startSeconds: number | null
// Dash WideVine only
// dash.js DRM options (widevine, playready only)
keySystemOptions: [{ [key: string]: any }] | null
// FirePlay для Apple native
protection: AppleSourceProtection | null
webOSProtection: WebOSSourceProtection | null
tizenParams: TizenSourceParams | null
@@ -25,6 +25,8 @@ namespace AVPlayHelper {
}
export interface IAVPlayer {
get lastError(): any
get avState(): State
get state(): State
get eventsEmitter(): EventBus
get currentTime(): number
@@ -61,6 +63,7 @@ namespace AVPlayHelper {
class AVPlayer implements IAVPlayer {
private readonly avPlayer: any
private _lastError: any
private _state: State
private _bus: EventBus
private _currentTime: number
@@ -74,51 +77,64 @@ namespace AVPlayHelper {
this._currentTime = 0
this.isPrebufferPlaying = false
console.log("✨ avplay initialized " + this.avPlayer.getVersion())
console.log("AVplay initialized " + this.avPlayer.getVersion())
}
private addListener(avPlayer: any) {
if (AVPlayer.isAllowedState(this.state, State.idle) == null) { return }
avPlayer.setListener({
oncurrentplaytime: (currentTime) => {
console.log("[AVPlayHelper] oncurrentplaytime : ", currentTime)
this._currentTime = currentTime
this._bus.publish(
Events.time({time: this.currentTime, player: this})
)
},
onevent: (eventType, eventData) => { console.log("event type: " + eventType + ", data: " + eventData) },
onevent: (eventType, eventData) => {
console.log("[AVPlayHelper] event type: " + eventType + ", data: " + eventData)
},
onbufferingstart: () => {
// console.log("Buffering start.")
console.log("[AVPlayHelper] Buffering start.")
this.isPrebufferPlaying = this.state == State.playing
},
onbufferingprogress: (percent) => { console.log("Buffering progress data : " + percent) },
onbufferingprogress: (percent) => {
console.log("[AVPlayHelper] Buffering progress data : " + percent)
},
onbufferingcomplete: () => {
this.switchState(State.ready)
if (this.isPrebufferPlaying) {
this.isPrebufferPlaying = false
this.avPlayer.play()
}
console.log("Buffering complete.")
console.log("[AVPlayHelper] Buffering complete. avState, avPlayer ", this.avState, this.avPlayer)
},
onrenderingstart: () => {
console.log('[AVPlayHelper] onRenderingStart');
},
onstreamcompleted: () => {
console.log("Stream Completed")
console.log("[AVPlayHelper] Stream Completed")
this.isPrebufferPlaying = false
this.stop(false)
},
onsubtitlechange: (duration, text, data3, data4) => {
console.log("subtitleText: " + text)
console.log("[AVPlayHelper] subtitleText: " + text)
},
onerror: (eventType) => {
console.log("event type error : " + eventType)
this._lastError = eventType
console.log("[AVPlayHelper] event type error : " + eventType)
},
ondrmevent: (drmEvent, drmData) => {
console.log("DRM callback: " + drmEvent + ", data: " + drmData)
console.log("[AVPlayHelper] DRM callback: ", drmEvent, drmData)
},
onerrormsg: (err, msg) => console.error("[AVPlayHelper] onerrormsg:", err, msg),
})
}
private switchState(state: State) {
const arrived = AVPlayer.isAllowedState(this._state, state)
if (arrived == null) {
console.log("Not allowed transition from " + this._state + " to " + state)
console.log("[AVPlayHelper] switchState Not allowed transition from " + this._state + " to " + state)
return
}
@@ -126,6 +142,7 @@ namespace AVPlayHelper {
this._bus.publish(
Events.state({state: arrived, player: this})
)
console.log("[AVPlayHelper] Transition from " + this._state + " to " + state)
}
// https://developer.samsung.com/media/2893/avplay_18082017fw.png
@@ -147,13 +164,13 @@ namespace AVPlayHelper {
if ([State.playing, State.paused].includes(arrive)) return arrive
break
}
console.log("🌚 " + depart + " -> " + arrive)
console.log("[AVPlayHelper] isAllowedState Not allowed state transition: " + depart + " -> " + arrive)
return null
}
private requiredState(state: State): boolean {
if (this.state != state) {
console.error("🐣 Incorrect state: " + this.state)
console.error("[AVPlayHelper] requiredState incorrect state: ", this.state, state)
return false
}
return true
@@ -161,7 +178,7 @@ namespace AVPlayHelper {
private requireAllowedStates(states: State[]): boolean {
if (!states.includes(this.state)) {
console.error("🐣 Incorrect state: " + this.state)
console.error("[AVPlayHelper] requireAllowedStates incorrect state: ", this.state, states, )
return false
}
return true
@@ -169,7 +186,17 @@ namespace AVPlayHelper {
// MARK: - IAVPlay
get state(): State { return this._state }
get lastError(): any { return this._lastError }
get avState(): State {
console.log("[AVPlayHelper] avState: ", this.avPlayer.getState())
return this.avPlayer.getState()
}
get state(): State {
// console.log("[AVPlayHelper] state: _state, avState ", this._state, this.avState)
return this._state
}
get eventsEmitter(): EventBus { return this._bus }
get currentTime(): number { return this._currentTime / 1000 }
@@ -179,14 +206,17 @@ namespace AVPlayHelper {
get isPlaying(): boolean { return this.state == State.playing }
open(url: string) {
if (AVPlayer.isAllowedState(this.state, State.idle) == null) { return }
if (AVPlayer.isAllowedState(this.state, State.idle) == null) {
return
}
console.log('[AVPlayHelper] open', url)
this.avPlayer.open(url)
this.switchState(State.idle)
this.addListener(this.avPlayer)
}
prepare(): Promise<void> {
console.log('[AVPlayHelper] prepare');
if (!this.requiredState(State.idle)) {
return Promise.reject('incorrect state')
}
@@ -195,21 +225,30 @@ namespace AVPlayHelper {
this.avPlayer.prepareAsync(
() => {
this.switchState(State.ready)
console.log('[AVPlayHelper] prepareAsync completed: _state, avState', this._state, this.avState);
resolve()
},
(error) => { reject('prepareAsync failed: ' + error) }
(error) => {
console.log('[AVPlayHelper] prepareFailed: ', error, this.avState);
console.log('[AVPlayHelper] prepareFailed stack: ', error.stack);
reject('prepare failed: ' + error)
}
)
}
)
}
play() {
if (AVPlayer.isAllowedState(this.state, State.playing) == null) { return }
console.log('[AVPlayHelper] play');
if (AVPlayer.isAllowedState(this.state, State.playing) == null) {
console.log('[AVPlayHelper] play invalid transition', this.state, State.playing);
return
}
try {
this.avPlayer.play()
this.switchState(State.playing)
} catch (error) {
console.error(error)
console.error("[AVPlayHelper] play error: ", error)
}
}
@@ -219,7 +258,7 @@ namespace AVPlayHelper {
this.avPlayer.pause()
this.switchState(State.paused)
} catch (error) {
console.error(error)
console.error("[AVPlayHelper] pause error: ",error)
}
}
@@ -228,7 +267,7 @@ namespace AVPlayHelper {
try {
this.avPlayer.seekTo(seconds * 1000)
} catch (e) {
console.log(e)
console.error("[AVPlayHelper] seekTo error: ", e)
}
}
@@ -248,16 +287,22 @@ namespace AVPlayHelper {
drmOptions(properties: any) {
if (!this.requiredState(State.idle)) { return }
this.avPlayer.setDrm(
'PLAYREADY',
'SetProperties',
JSON.stringify(properties)
)
console.log("[drmOptions] setDrm properties:", properties)
try {
this.avPlayer.setDrm(
'PLAYREADY',
'SetProperties',
JSON.stringify(properties)
)
} catch (e) {
console.log("[drmOptions] setDrm error", e)
}
}
setDisplayRect(x: number, y: number, width: number, heigth: number) {
setDisplayRect(x: number, y: number, width: number, height: number) {
if (!this.requiredState(State.idle)) { return }
this.avPlayer.setDisplayRect(0, 0, 1920, 1080)
console.log('[AVPlayHelper] setDisplayRect', x, y, width, height);
this.avPlayer.setDisplayRect(x, y, width, height)
}
setDisplayMethod(method: string) {
@@ -1,3 +1,4 @@
import { DeviceInfo, Stream } from "@/internal/player/native/tizen/models/PlayerLoadParameters";
import videojs from 'video.js'
import VokaTizenSourceHandler from '../sourcehandler/VokaTizenSourceHandler'
import AVPlayHelper from './AVPlayHelper'
@@ -24,7 +25,7 @@ class VokaTizenTech extends Tech {
private startTime: number | null
private switchToThisTime: number | null
constructor(player, options, ready) {
constructor(options, ready) {
const opt = options || {}
opt.techId = VokaTizenTech.TECH_NAME
super(opt, ready)
@@ -48,6 +49,10 @@ class VokaTizenTech extends Tech {
this.attachListeners(this.avPlayer)
if (options.source) {
this.setSource(options.source);
}
this.triggerReady() // from Component
}
@@ -76,6 +81,10 @@ class VokaTizenTech extends Tech {
case AVPlayHelper.State.paused:
this.trigger('pause')
break
case AVPlayHelper.State.ready:
this.trigger('durationchange');
this.trigger('canplay')
break
}
}
)
@@ -88,9 +97,10 @@ class VokaTizenTech extends Tech {
const el = document.createElement(this.elementTagName)
el.id = playerId
el.setAttribute('type', 'application/avplayer')
el.setAttribute('id', 'av-player')
el.style.width = '100%'
el.style.height = '100%'
el.setAttribute('className', 'vjs-tech tizen-avplay-tech')
el.style.width = '100vw'
el.style.height = '100vh'
this.el_ = el
return el
}
@@ -115,7 +125,6 @@ class VokaTizenTech extends Tech {
reset() {
this.screenSaver.enable()
this.startTime = null
this.selectedAudioTrack = null
this.params = null
@@ -136,8 +145,8 @@ class VokaTizenTech extends Tech {
loop() { return false }
setLoop(value) { }
seeking() { return false }
seekable() { return false }
seeking() { return true }
seekable() { return true }
preload() { }
setPreload(preload) { }
@@ -149,13 +158,20 @@ class VokaTizenTech extends Tech {
const src = typeof source.src === 'undefined' ? '' : source.src
const sourceType = typeof source.sourceType === 'undefined' ? '' : source.sourceType
let parameters = source.tizenParams
console.log("[VokaTizenTech.setSrc] source ", source)
if (parameters == null) {
parameters = {} as TizenSourceParams
parameters = {} as TizenSourceParams
}
if (typeof parameters.isUHDSupported !== "boolean") {
parameters.isUHDSupported = true
parameters.platform = {}
}
if (!parameters.platform) {
parameters.platform = {} as DeviceInfo
}
if (!parameters?.stream) {
parameters.stream = {
url: src,
}
url: src
} as Stream
}
this.is4KSupported = parameters.isUHDSupported
@@ -170,7 +186,7 @@ class VokaTizenTech extends Tech {
if (!this.is4KSupported) {
if (supportedBandWidthCount === 0) {
//onError(event, PlayerAccessError.CannotPlayStream);
console.log("[VokaTizenTech] No supported BandWidth", playListsAttributes)
return
}
}
@@ -183,20 +199,12 @@ class VokaTizenTech extends Tech {
.split('.')[0], 10)
const playerUrl = parameters.stream.url
// if (getProtocolByStreamURL(playerUrl) === Protocol.HLS) {
// playerUrl += '|COMPONENT=HLS';
// }
const protection = parameters.protection
const hasDRM = protection != null && protection.type == 'playready'
console.log("[VokaTizenTech] setSrc: protection, hasDRM", protection, hasDRM)
this.avPlayer.open(playerUrl)
// if (seekTo !== undefined && !isNaN(seekTo)) {
// webapis.avplay.seekTo(seekTo * 1000);
//
// switchToThisTime = null;
// }
/*
https://developer.samsung.com/smarttv/develop/guides/multimedia/media-playback/using-avplay.html#drm-contents-playback-sequence
*/
@@ -205,10 +213,19 @@ class VokaTizenTech extends Tech {
DeleteLicenseAfterUse: true,
LicenseServer: protection.licenseServer,
}
if (protection.httpHeader) {
properties.HttpHeader = protection.httpHeader
}
this.avPlayer.drmOptions(properties)
}
this.avPlayer.setDisplayRect(0, 0, 1920, 1080)
const rect = this.el_.getBoundingClientRect()
this.avPlayer.setDisplayRect(
Math.round(rect.left),
Math.round(rect.top),
Math.round(rect.width),
Math.round(rect.height)
)
this.avPlayer.setDisplayMethod('PLAYER_DISPLAY_MODE_AUTO_ASPECT_RATIO')
if (this.is4KSupported) {
@@ -277,7 +294,7 @@ class VokaTizenTech extends Tech {
this.avPlayer.seekTo(this.switchToThisTime)
this.switchToThisTime = null
} else if (parameters.percentsWatched) {
this.avPlayer.seekTo(parameters.percentsWatched * this.getDuration() || 0)
this.avPlayer.seekTo(parameters.percentsWatched * this.duration() || 0)
}
})
}
@@ -286,18 +303,25 @@ class VokaTizenTech extends Tech {
}
duration() {
console.log("[VokaTizenTech] duration() started")
const parameters = this.params
if (parameters == null) { return }
if (parameters == null) {
console.log("[VokaTizenTech] duration is null")
return
}
try {
if (parameters.isLiveStream) {
const [start, end] = this.avPlayer.getStreamingProperty('GET_LIVE_DURATION').split('|')
const duration = this.avPlayer.duration
console.log("[VokaTizenTech] duration(): start, end", start, end)
return (
duration > 0 ? duration : parseInt(end, 10) - parseInt(start, 10) || 0
)
}
console.log("[VokaTizenTech] duration ", this.avPlayer.duration)
return this.avPlayer.duration
} catch (e) {
console.error("[VokaTizenTech] Cant get duration", e)
return 0
}
}
@@ -93,7 +93,10 @@ export default class VokaWebOSSourceHandler extends VokaSourceHandler {
private createDRMPlayer(source: IVokaSource, tech: VokaWebOSTech): WebOSPlayerNativeDRM {
const protection = source.webOSProtection
if (protection == null) { return }
if (protection == null) {
console.log('[createDRMPlayer] cant get source.webOSProtection: source', source)
return
}
const config = {
licenseServer: protection.licenseServer,
@@ -13,7 +13,7 @@ class VokaWebOSTech extends Tech {
private lastCurrentTime: number | null
private playPromise: null
constructor(player, options, ready) {
constructor(options, ready) {
const opt = options || {}
opt.techId = VokaWebOSTech.TECH_NAME
super(opt, ready)
@@ -21,6 +21,10 @@ class VokaWebOSTech extends Tech {
this.playPromise = null
this.lastCurrentTime = null
if (options.source) {
this.setSource(options.source);
}
this.triggerReady()
}
@@ -122,10 +126,10 @@ class VokaWebOSTech extends Tech {
setLoop(value) { }
seeking() {
return false
return true
}
seekable() {
return false
return true
}
preload() { }
@@ -157,7 +161,7 @@ class VokaWebOSTech extends Tech {
currentSrc() { return '' }
duration() {
return this.el().duration || 0
return this.el().duration || 0
}
setCurrentTime(seconds) {
@@ -205,7 +209,7 @@ class VokaWebOSTech extends Tech {
}
paused() {
return false
return this.el().paused
}
ended() {
+13 -2
View File
@@ -18,7 +18,10 @@ namespace VokaMagicRemotePlugin {
PAUSE: 19,
NEXT: 417,
PREV: 412
PREV: 412,
TRACK_NEXT: 10233,
TRACK_PREV: 10232
}
export class Plugin extends VideoPlugin {
@@ -53,10 +56,12 @@ namespace VokaMagicRemotePlugin {
break
case KEY_CODES.LEFT:
case KEY_CODES.PREV:
case KEY_CODES.TRACK_PREV:
this.operationStepBackward()
break
case KEY_CODES.RIGHT:
case KEY_CODES.NEXT:
case KEY_CODES.TRACK_NEXT:
this.operationStepForward()
break
}
@@ -104,13 +109,18 @@ namespace VokaMagicRemotePlugin {
}
private operationStepForward() {
const skipTime = 5//this.options.skip?.forward
console.log("[operationStepForward] started")
const skipTime = 5 //this.options.skip?.forward
if (isNaN(this.player.duration()) || !skipTime) {
return
}
const currentVideoTime = this.player.currentTime()
console.log("[operationStepForward] currentTime = ", currentVideoTime)
const liveTracker = this.player.liveTracker
console.log("[operationStepForward] liveTracker, duration", liveTracker.isLive(), liveTracker.seekableEnd(), this.player.duration())
const duration =
liveTracker && liveTracker.isLive()
? liveTracker.seekableEnd()
@@ -123,6 +133,7 @@ namespace VokaMagicRemotePlugin {
newTime = duration
}
console.log("[operationStepForward] newTime = ", newTime)
this.player.currentTime(newTime)
}
}
+4 -1
View File
@@ -173,7 +173,10 @@ export class VokaPlayerImpl implements IVokaPlayer {
// Логика для сайта voka - если нечего играть, плеер скрываем;
player.load(iContent)
.then(() => player.enable())
.catch((error) => player.disable())
.catch((error) => {
console.error("Error content loading, disable player", error)
player.disable()
})
}
private async initialize(player: VokaCorePlayer.CorePlayer, features: SupportedCodecs.ISelectProtocolResult): Promise<boolean> {