Compare commits
11 Commits
Author | SHA1 | Date |
---|---|---|
|
a39804142c | 5 days ago |
|
0212be3396 | 5 days ago |
|
f7da5c3f6f | 2 weeks ago |
|
7c2b5e0911 | 4 weeks ago |
|
55cab57cdc | 2 months ago |
|
cc709a87c7 | 2 months ago |
|
4a71482346 | 2 months ago |
|
27809fd87b | 2 months ago |
|
81f2c074d2 | 2 months ago |
|
e025d3bf8a | 2 months ago |
|
280d470409 | 7 months ago |
@ -0,0 +1,89 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAtgg0vsIc8bIMb9hAO1Lwq2TVNYMiD928WCVwq7Bj3B9LM8ci
|
||||
fN+mBxHz35SeC9JnQysbKAf8VVfVi7GVzwFq4VaRreoIl8DoMbBORb2iKxfLXKEH
|
||||
S283bbl17csAYHTTBqFsKnO3kKvYoDHRKFX0T0rzZibI0ACQiylW+ALE3fVS0hFI
|
||||
8fmoZnIAOyQ15RLp7KPO4bMRVdZaOqRUZeW0YzbvOBeGZKbx3bXiscL9RwfGSxlF
|
||||
MCLahaJsBrVDo4/IGDRCmSydK8k/41MHd86pGPzCixBx7cqtKO/6+cVeEuGxfJPf
|
||||
JVuxNq6vkRSt2HxCc6i2iagA2uL/WYdg3Pa/4wIDAQABAoIBAADFrCObAzBrRu46
|
||||
hps50NeJR/ZAJibXE/NzxTSVPPc0EseXcqgA8t1Y0CYEpV77d4CrcCQNVJ6wDrHX
|
||||
AQGtydxG17tbIMo0AUgkrVBSa5uvMCembzd8s0l93egyUkAWfsaqbKEJeJ/eer7D
|
||||
N1Xqd2zWro2iYHuxZOuSM1I+AMPIQsmYJ71w6/h9YpQh436Vd+zNQ5k/nWpLHihT
|
||||
VB2ECrJ36IbuiYo3UbSr9gQjyBSMkk/oUqO4jonkb6L7r0mqHXNeblycg99/m6i7
|
||||
O5c5DQKMhzqibwvNNf6uvWCcLKfF5Kqzzf9DKR3/pYOBQrVTA24l4UFsfTdEKUNS
|
||||
a8W3P8ECgYEA6CQOG15V9upc2nPzfFwgftGyomSMYH54PkSFdr2R4djyXkyil6Ik
|
||||
efK3E+lKr9YnzwcLw3csPmVt3lqSgixQUMcyXXrhCttfk/qzSJkI+UZPQE+SrNeW
|
||||
0c+blQOzVcfbNRu248iGFaRx+5qA6PMH4UZTgn7e6nXoPUgRp4ryI/MCgYEAyL24
|
||||
R7uMSuPQBRJFU84Lu+Rv4lkKdCYSLuQtMZly74m11iG6e+EHJQx0C3eexrC8LhOV
|
||||
Sm4xTlwVrYQ+IdW51bhAwwHcnzGUzpbESJSDK5ZTd/P5daz8yt8ZaGbUFxNEsxTr
|
||||
ElKPRcjJH5CRuyYr24DYg+CpMGdlF0N6Pcx5IFECgYAedlzDiqWNOUPmBsE02IIL
|
||||
IklmtfsVzoLI6QT6h/XUxTtI1JWhgE15EzijDEIYwOmIaUxJ4iGULos0Wn5PRrFj
|
||||
aEBbs/xECHWKXaOZKzvaOje8ILUGqWPJNI0eCNZHs2o4leJyEaZGwMWUVroD16B5
|
||||
F1luDmgCLGbFY+etLLaJsQKBgB40VbcNZDWcg59PuXi7pw5Vd/RB243QcKn3kUlG
|
||||
QoICYYbfulSLbmzHq+pRzGUvEJGKRstVOzwEJQrfvA2RQA4FVFFDRXP6nN5c1xno
|
||||
prf3PYXuAtoO9lZ8LTGFT2JNdufPPPOb0oz4gjKqqRLU0oKLp4hoVGzBEffnIkyM
|
||||
KKmRAoGBAIGXh4gvxzEQMgGzfKfNuxKCT9SEhsg7NU++Iey3qn4G4t+jIWOt2Gi7
|
||||
5+y49JWoGq6DL+2ZVVw6Cn6wd9tfzDKD5GhvIztK0z1+wqpFOL4M8bwqJDOKgsZ3
|
||||
PCPASbxPgMyNCjRhvxBuscCr+dRFYDUrirOK9EUPyO9EoNTPPN9a
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIGBTCCBO2gAwIBAgIQDNIYeWoFoT3jxF2+HmEbTDANBgkqhkiG9w0BAQsFADBu
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||
d3cuZGlnaWNlcnQuY29tMS0wKwYDVQQDEyRFbmNyeXB0aW9uIEV2ZXJ5d2hlcmUg
|
||||
RFYgVExTIENBIC0gRzIwHhcNMjMwOTI4MDAwMDAwWhcNMjQwOTI3MjM1OTU5WjAh
|
||||
MR8wHQYDVQQDExZkZWZhdWx0LnpsbWVkaWFraXQuY29tMIIBIjANBgkqhkiG9w0B
|
||||
AQEFAAOCAQ8AMIIBCgKCAQEAtgg0vsIc8bIMb9hAO1Lwq2TVNYMiD928WCVwq7Bj
|
||||
3B9LM8cifN+mBxHz35SeC9JnQysbKAf8VVfVi7GVzwFq4VaRreoIl8DoMbBORb2i
|
||||
KxfLXKEHS283bbl17csAYHTTBqFsKnO3kKvYoDHRKFX0T0rzZibI0ACQiylW+ALE
|
||||
3fVS0hFI8fmoZnIAOyQ15RLp7KPO4bMRVdZaOqRUZeW0YzbvOBeGZKbx3bXiscL9
|
||||
RwfGSxlFMCLahaJsBrVDo4/IGDRCmSydK8k/41MHd86pGPzCixBx7cqtKO/6+cVe
|
||||
EuGxfJPfJVuxNq6vkRSt2HxCc6i2iagA2uL/WYdg3Pa/4wIDAQABo4IC6jCCAuYw
|
||||
HwYDVR0jBBgwFoAUeN+RkF/u3qz2xXXr1UxVU+8kSrYwHQYDVR0OBBYEFHmEMVp9
|
||||
9EHIPWA2U1iLKogCosGFMCEGA1UdEQQaMBiCFmRlZmF1bHQuemxtZWRpYWtpdC5j
|
||||
b20wPgYDVR0gBDcwNTAzBgZngQwBAgEwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3
|
||||
dy5kaWdpY2VydC5jb20vQ1BTMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggr
|
||||
BgEFBQcDAQYIKwYBBQUHAwIwgYAGCCsGAQUFBwEBBHQwcjAkBggrBgEFBQcwAYYY
|
||||
aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEoGCCsGAQUFBzAChj5odHRwOi8vY2Fj
|
||||
ZXJ0cy5kaWdpY2VydC5jb20vRW5jcnlwdGlvbkV2ZXJ5d2hlcmVEVlRMU0NBLUcy
|
||||
LmNydDAMBgNVHRMBAf8EAjAAMIIBfwYKKwYBBAHWeQIEAgSCAW8EggFrAWkAdgDu
|
||||
zdBk1dsazsVct520zROiModGfLzs3sNRSFlGcR+1mwAAAYravqonAAAEAwBHMEUC
|
||||
IQDX+gqsd7I0yzjkhgp2YrccUlTx4wkFptFvmQxeChImRgIgJdgJa2Uamd790BCI
|
||||
/CZwSqmRlor5eU8exAixdcopYpcAdwBIsONr2qZHNA/lagL6nTDrHFIBy1bdLIHZ
|
||||
u7+rOdiEcwAAAYravqqCAAAEAwBIMEYCIQCP6rkKg2FlF92CyMbVMk3ESh/9gVaM
|
||||
tRsv5I//i5IVigIhAINHERhy7812wR47fwmvqWDjxyOB1ZodU7WA9D5L/1bVAHYA
|
||||
2ra/az+1tiKfm8K7XGvocJFxbLtRhIU0vaQ9MEjX+6sAAAGK2r6qQQAABAMARzBF
|
||||
AiAiz3bp/j4SlnVxKg1HZY+YdUboi+kaKf5G8X6aFLIqUgIhAPPCm5UN05p7Oqrc
|
||||
sP/wdHDB7O/2AbUksYSLhidmwfmhMA0GCSqGSIb3DQEBCwUAA4IBAQBmaG51jU1E
|
||||
MsgT1VzutQUXglEvJGVf54cA+0TSfjfnP1n9ALdKjGxHL3KBh4UkPx5zdE5//FUX
|
||||
dacua6BQEWSCmMtYL0CFieFnLGXh0mgkfvRaP6+3xe6TkJ4kuyJkMS9YMDpVl80F
|
||||
2GLlE09EsZ3Xk9+SCpmWOPLOCDFURbwpc5ht+acROfzYJQyCY0L8EGbyL5/q9oMn
|
||||
ugRGh4oyGvXgKvFIPzpZkaOmb0b63/uBc5JkiyQhuFdYaS2cLOwupXmCtIHL4Od6
|
||||
OU8/8smT8NEkD7d3lUijtc84q2TihW7ebT7RtOco49PDvFP/7w28QjxM8Ohv9/Gz
|
||||
Xyta8ICQVwmK
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEqjCCA5KgAwIBAgIQDeD/te5iy2EQn2CMnO1e0zANBgkqhkiG9w0BAQsFADBh
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
|
||||
MjAeFw0xNzExMjcxMjQ2NDBaFw0yNzExMjcxMjQ2NDBaMG4xCzAJBgNVBAYTAlVT
|
||||
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
|
||||
b20xLTArBgNVBAMTJEVuY3J5cHRpb24gRXZlcnl3aGVyZSBEViBUTFMgQ0EgLSBH
|
||||
MjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAO8Uf46i/nr7pkgTDqnE
|
||||
eSIfCFqvPnUq3aF1tMJ5hh9MnO6Lmt5UdHfBGwC9Si+XjK12cjZgxObsL6Rg1njv
|
||||
NhAMJ4JunN0JGGRJGSevbJsA3sc68nbPQzuKp5Jc8vpryp2mts38pSCXorPR+sch
|
||||
QisKA7OSQ1MjcFN0d7tbrceWFNbzgL2csJVQeogOBGSe/KZEIZw6gXLKeFe7mupn
|
||||
NYJROi2iC11+HuF79iAttMc32Cv6UOxixY/3ZV+LzpLnklFq98XORgwkIJL1HuvP
|
||||
ha8yvb+W6JislZJL+HLFtidoxmI7Qm3ZyIV66W533DsGFimFJkz3y0GeHWuSVMbI
|
||||
lfsCAwEAAaOCAU8wggFLMB0GA1UdDgQWBBR435GQX+7erPbFdevVTFVT7yRKtjAf
|
||||
BgNVHSMEGDAWgBROIlQgGJXm427mD/r6uRLtBhePOTAOBgNVHQ8BAf8EBAMCAYYw
|
||||
HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1UdEwEB/wQIMAYBAf8C
|
||||
AQAwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp
|
||||
Y2VydC5jb20wQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQu
|
||||
Y29tL0RpZ2lDZXJ0R2xvYmFsUm9vdEcyLmNybDBMBgNVHSAERTBDMDcGCWCGSAGG
|
||||
/WwBAjAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BT
|
||||
MAgGBmeBDAECATANBgkqhkiG9w0BAQsFAAOCAQEAoBs1eCLKakLtVRPFRjBIJ9LJ
|
||||
L0s8ZWum8U8/1TMVkQMBn+CPb5xnCD0GSA6L/V0ZFrMNqBirrr5B241OesECvxIi
|
||||
98bZ90h9+q/X5eMyOD35f8YTaEMpdnQCnawIwiHx06/0BfiTj+b/XQih+mqt3ZXe
|
||||
xNCJqKexdiB2IWGSKcgahPacWkk/BAQFisKIFYEqHzV974S3FAz/8LIfD58xnsEN
|
||||
GfzyIDkH3JrwYZ8caPTf6ZX9M1GrISN8HnWTtdNCH2xEajRa/h9ZBXjUyFKQrGk2
|
||||
n2hcLrfZSbynEC/pSw/ET7H5nWwckjmAJ1l9fcnbqkU/pf6uMQmnfl0JQjJNSg==
|
||||
-----END CERTIFICATE-----
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Binary file not shown.
After Width: | Height: | Size: 46 KiB |
@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head><title>欢迎使用ZLMediaKit</title></head>
|
||||
<body bgcolor="white">
|
||||
<center><h1>欢迎使用ZLMediaKit,请阅读<a href="https://github.com/ZLMediaKit/ZLMediaKit/wiki" title="wiki">wiki</a> 获取更多帮助!</h1></center>
|
||||
<hr>
|
||||
<center>ZLMediaKit</center>
|
||||
</body>
|
||||
</html>
|
Binary file not shown.
After Width: | Height: | Size: 665 B |
Binary file not shown.
After Width: | Height: | Size: 628 B |
@ -0,0 +1,16 @@
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
overflow: -moz-scrollbars-vertical;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: #fafafa;
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
<!-- HTML for static distribution bundle build -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Swagger UI</title>
|
||||
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" />
|
||||
<link rel="stylesheet" type="text/css" href="index.css" />
|
||||
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
|
||||
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
<script src="./swagger-ui-bundle.js" charset="UTF-8"> </script>
|
||||
<script src="./swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
|
||||
<script src="./swagger-initializer.js" charset="UTF-8"> </script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,79 @@
|
||||
<!doctype html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<title>Swagger UI: OAuth2 Redirect</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
function run () {
|
||||
var oauth2 = window.opener.swaggerUIRedirectOauth2;
|
||||
var sentState = oauth2.state;
|
||||
var redirectUrl = oauth2.redirectUrl;
|
||||
var isValid, qp, arr;
|
||||
|
||||
if (/code|token|error/.test(window.location.hash)) {
|
||||
qp = window.location.hash.substring(1).replace('?', '&');
|
||||
} else {
|
||||
qp = location.search.substring(1);
|
||||
}
|
||||
|
||||
arr = qp.split("&");
|
||||
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';});
|
||||
qp = qp ? JSON.parse('{' + arr.join() + '}',
|
||||
function (key, value) {
|
||||
return key === "" ? value : decodeURIComponent(value);
|
||||
}
|
||||
) : {};
|
||||
|
||||
isValid = qp.state === sentState;
|
||||
|
||||
if ((
|
||||
oauth2.auth.schema.get("flow") === "accessCode" ||
|
||||
oauth2.auth.schema.get("flow") === "authorizationCode" ||
|
||||
oauth2.auth.schema.get("flow") === "authorization_code"
|
||||
) && !oauth2.auth.code) {
|
||||
if (!isValid) {
|
||||
oauth2.errCb({
|
||||
authId: oauth2.auth.name,
|
||||
source: "auth",
|
||||
level: "warning",
|
||||
message: "Authorization may be unsafe, passed state was changed in server. The passed state wasn't returned from auth server."
|
||||
});
|
||||
}
|
||||
|
||||
if (qp.code) {
|
||||
delete oauth2.state;
|
||||
oauth2.auth.code = qp.code;
|
||||
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
|
||||
} else {
|
||||
let oauthErrorMsg;
|
||||
if (qp.error) {
|
||||
oauthErrorMsg = "["+qp.error+"]: " +
|
||||
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
|
||||
(qp.error_uri ? "More info: "+qp.error_uri : "");
|
||||
}
|
||||
|
||||
oauth2.errCb({
|
||||
authId: oauth2.auth.name,
|
||||
source: "auth",
|
||||
level: "error",
|
||||
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server."
|
||||
});
|
||||
}
|
||||
} else {
|
||||
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
|
||||
}
|
||||
window.close();
|
||||
}
|
||||
|
||||
if (document.readyState !== 'loading') {
|
||||
run();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
run();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,20 @@
|
||||
window.onload = function() {
|
||||
//<editor-fold desc="Changeable Configuration Block">
|
||||
|
||||
// the following lines will be replaced by docker/configurator, when it runs in a docker-container
|
||||
window.ui = SwaggerUIBundle({
|
||||
url: "/swagger/openapi.json",
|
||||
dom_id: '#swagger-ui',
|
||||
deepLinking: true,
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIStandalonePreset
|
||||
],
|
||||
plugins: [
|
||||
SwaggerUIBundle.plugins.DownloadUrl
|
||||
],
|
||||
layout: "StandaloneLayout"
|
||||
});
|
||||
|
||||
//</editor-fold>
|
||||
};
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 1002victor
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -0,0 +1,14 @@
|
||||
# zlm_webassist
|
||||
|
||||
|
||||
[](https://github.com/1002victor/zlm_webassist/blob/main/LICENSE)
|
||||

|
||||

|
||||

|
||||

|
||||
[](https://github.com/ZLMediaKit/ZLMediaKit)
|
||||
[](https://github.com/1002victor/zlm_webassist/pulls)
|
||||
|
||||
[简体中文](./README.md) | English
|
||||
|
||||
ZLMediakit's web management assistant
|
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
After Width: | Height: | Size: 134 KiB |
Binary file not shown.
After Width: | Height: | Size: 58 KiB |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,41 @@
|
||||
function findDifferentProperties(objA, objB, prefix = '') {
|
||||
const differentProps = {};
|
||||
|
||||
for (const key in objA) {
|
||||
if (objA.hasOwnProperty(key)) {
|
||||
const propPath = prefix ? `${prefix}.${key}` : key;
|
||||
if (typeof objA[key] === 'object' && typeof objB[key] === 'object') {
|
||||
const nestedDifferences = findDifferentProperties(objA[key], objB[key], propPath);
|
||||
if (Object.keys(nestedDifferences).length > 0) {
|
||||
differentProps[key] = nestedDifferences;
|
||||
}
|
||||
} else if (objA[key] !== objB[key]) {
|
||||
// differentProps[key] = {
|
||||
// oldValue: objA[key],
|
||||
// newValue: objB[key],
|
||||
// };
|
||||
differentProps[key] = objB[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return differentProps;
|
||||
}
|
||||
|
||||
function flattenObject(obj, parentKey = '') {
|
||||
const result = {};
|
||||
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
const newKey = parentKey ? `${parentKey}.${key}` : key;
|
||||
|
||||
if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
|
||||
const flattened = flattenObject(obj[key], newKey);
|
||||
Object.assign(result, flattened);
|
||||
} else {
|
||||
result[newKey] = obj[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -0,0 +1,450 @@
|
||||
<html>
|
||||
<meta charset="utf-8">
|
||||
<head>
|
||||
<title>ZLM RTC demo</title>
|
||||
<script src="./ZLMRTCClient.js"></script>
|
||||
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
|
||||
<script>
|
||||
// VConsole will be exported to `window.VConsole` by default.
|
||||
var vConsole = new window.VConsole();
|
||||
</script>
|
||||
<style>
|
||||
video {
|
||||
width: 40vw;
|
||||
max-height: 50vh;
|
||||
height: 22.5vw; /* 默认和宽:高为 16:9 */
|
||||
object-fit: contain;
|
||||
background-color: grey;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="text-align: center;">
|
||||
<div>
|
||||
<video id='video' controls autoplay>
|
||||
Your browser is too old which doesn't support HTML5 video.
|
||||
</video>
|
||||
|
||||
<video id='selfVideo' controls autoplay>
|
||||
Your browser is too old which doesn't support HTML5 video.
|
||||
</video>
|
||||
</div>
|
||||
|
||||
<div style="float: left; width:30%;">
|
||||
<span>已存在的流列表,点击自动播放:</span>
|
||||
<ol id="olstreamlist">
|
||||
<li>初始演示</li>
|
||||
<li>每秒自动刷新</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div style="float: right; width: 70%">
|
||||
<p>
|
||||
<label for="streamUrl">url:</label>
|
||||
<input type="text" style="co; width:70%" id='streamUrl' value="http://192.168.1.101/index/api/webrtc?app=live&stream=xiong&type=play">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="simulcast">simulcast:</label>
|
||||
<input type="checkbox" id='simulcast'>
|
||||
</p>
|
||||
<p>
|
||||
<label for="useCamera">useCamera:</label>
|
||||
<input type="checkbox" id='useCamera' checked="checked">
|
||||
</p>
|
||||
|
||||
|
||||
<p>
|
||||
<label for="audioEnable">audioEnable:</label>
|
||||
<input type="checkbox" id='audioEnable' checked="checked">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="videoEnable">videoEnable:</label>
|
||||
<input type="checkbox" id='videoEnable' checked="checked">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="method">method(play or push or echo):</label>
|
||||
<input type="radio" name="method" value="echo" >echo
|
||||
<input type="radio" name="method" value="push" >push
|
||||
<input type="radio" name="method" value="play" checked = true>play
|
||||
</p>
|
||||
<p>
|
||||
<label for="resolution">resolution:</label>
|
||||
<select id="resolution">
|
||||
</select>
|
||||
</p>
|
||||
<p>
|
||||
<label for="datachannel">datachannel:</label>
|
||||
<input id='datachannel' name="datachannel" type="checkbox" value="0">
|
||||
</p>
|
||||
<button onclick="start()">开始(start)</button>
|
||||
<button onclick="stop()">停止(stop)</button>
|
||||
|
||||
<p>
|
||||
<label for="msgsend">msgsend:</label>
|
||||
<input type="text" id='msgsend' value="hello word !">
|
||||
</p>
|
||||
<p>
|
||||
<label for="msgrecv">msgrecv:</label>
|
||||
<input type="text" id='msgrecv' disabled>
|
||||
</p>
|
||||
<button onclick="send()">发送(send by datachannel)</button>
|
||||
<button onclick="close()">关闭(close datachannel)</button>
|
||||
|
||||
<p>
|
||||
<label for="videoDevice">videodevice:</label>
|
||||
<select id="videoDevice">
|
||||
</select>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="audioDevice">audiodevice:</label>
|
||||
<select id="audioDevice">
|
||||
</select>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="switchDevice">switchDevice:</label>
|
||||
<input type="checkbox" id='switchDevice' checked="checked">
|
||||
</p>
|
||||
<button onclick="switchVideo()">切换视频(switch video)</button>
|
||||
<button onclick="switchAudio()">切换音频(switch audio)</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var player = null;
|
||||
var recvOnly = true;
|
||||
var resArr = [];
|
||||
|
||||
var ishttps = 'https:' === document.location.protocol;
|
||||
var isLocal = "file:" === document.location.protocol;
|
||||
|
||||
const searchParams = new URL(document.location.href).searchParams;
|
||||
let type = searchParams.get('type');
|
||||
if (!['echo','push','play'].includes(type)) {
|
||||
type = 'play';
|
||||
}
|
||||
recvOnly = type === 'play';
|
||||
const apiPath = `/index/api/webrtc?app=${searchParams.get('app') ?? 'live'}&stream=${searchParams.get('stream') ?? 'test'}&type=${type}`;
|
||||
|
||||
if(!ishttps && !isLocal){
|
||||
alert('本demo需要在https的网站访问, 如果你要推流的话(this demo must access in site of https if you want to push stream)');
|
||||
}
|
||||
|
||||
const apiHost = isLocal ? "http://127.0.0.1" : `${document.location.protocol}//${window.location.host}`;
|
||||
var url = apiHost + apiPath;
|
||||
|
||||
document.getElementById('streamUrl').value = url;
|
||||
document.getElementsByName("method").forEach((el,idx) => {
|
||||
el.checked = el.value === type;
|
||||
el.onclick = function(e) {
|
||||
const url = new URL(document.getElementById('streamUrl').value);
|
||||
url.searchParams.set("type",el.value);
|
||||
document.getElementById('streamUrl').value = url.toString();
|
||||
recvOnly = 'play' === el.value;
|
||||
};
|
||||
});
|
||||
|
||||
ZLMRTCClient.GetAllScanResolution().forEach((r,i) => {
|
||||
opt = document.createElement('option');
|
||||
opt.text = `${r.label}(${r.width}x${r.height})`;
|
||||
opt.value = r;
|
||||
if (1080*720 <= r.width * r.height && r.width * r.height <= 1280*720) {
|
||||
opt.selected = true;
|
||||
}
|
||||
document.getElementById("resolution").add(opt,null);
|
||||
});
|
||||
|
||||
ZLMRTCClient.GetAllMediaDevice().then(devices=>{
|
||||
devices.forEach(device=>{
|
||||
opt = document.createElement('option');
|
||||
opt.text = device.label + ":"+device.deviceId
|
||||
opt.value = JSON.stringify(device)
|
||||
if(device.kind == 'videoinput'){
|
||||
document.getElementById("videoDevice").add(opt,null)
|
||||
}else if(device.kind == 'audioinput'){
|
||||
document.getElementById("audioDevice").add(opt,null)
|
||||
}else if(device.kind == 'audiooutput'){
|
||||
// useless
|
||||
//console.error('not support device')
|
||||
}
|
||||
})
|
||||
}).catch(e=>{
|
||||
console.error(e);
|
||||
})
|
||||
|
||||
function start_play(){
|
||||
let elr = document.getElementById("resolution");
|
||||
let res = elr.options[elr.selectedIndex].text.match(/\d+/g);
|
||||
let h = parseInt(res.pop());
|
||||
let w = parseInt(res.pop());
|
||||
|
||||
const url = new URL(document.getElementById('streamUrl').value);
|
||||
const newUrl = new URL(window.location.href);
|
||||
let count = 0;
|
||||
if (url.searchParams.has('app')) {
|
||||
newUrl.searchParams.set('app', url.searchParams.get('app'));
|
||||
count++;
|
||||
}
|
||||
if (url.searchParams.has('stream')) {
|
||||
newUrl.searchParams.set('stream', url.searchParams.get('stream'));
|
||||
count++;
|
||||
}
|
||||
if (url.searchParams.has('type')) {
|
||||
newUrl.searchParams.set('type', url.searchParams.get('type'));
|
||||
count++;
|
||||
}
|
||||
if (count > 0) {
|
||||
window.history.pushState(null, null, newUrl);
|
||||
}
|
||||
|
||||
let elv = document.getElementById("videoDevice");
|
||||
let ela = document.getElementById("audioDevice");
|
||||
|
||||
let vdevid = ''
|
||||
let adevid = ''
|
||||
|
||||
if (!recvOnly) {
|
||||
if (elv.selectedIndex !== -1) {
|
||||
vdevid = JSON.parse(elv.options[elv.selectedIndex].value).deviceId
|
||||
}
|
||||
if (ela.selectedIndex !== -1) {
|
||||
adevid = JSON.parse(ela.options[ela.selectedIndex].value).deviceId
|
||||
}
|
||||
}
|
||||
|
||||
player = new ZLMRTCClient.Endpoint(
|
||||
{
|
||||
element: document.getElementById('video'),// video 标签
|
||||
debug: true,// 是否打印日志
|
||||
zlmsdpUrl:document.getElementById('streamUrl').value,//流地址
|
||||
simulcast:document.getElementById('simulcast').checked,
|
||||
useCamera:document.getElementById('useCamera').checked,
|
||||
audioEnable:document.getElementById('audioEnable').checked,
|
||||
videoEnable:document.getElementById('videoEnable').checked,
|
||||
recvOnly:recvOnly,
|
||||
resolution:{w,h},
|
||||
usedatachannel:document.getElementById('datachannel').checked,
|
||||
videoId:vdevid, // 不填选择默认的
|
||||
audioId:adevid, // 不填选择默认的
|
||||
}
|
||||
);
|
||||
|
||||
player.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR,function(e)
|
||||
{
|
||||
// ICE 协商出错
|
||||
console.log('ICE 协商出错');
|
||||
});
|
||||
|
||||
player.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS,function(e)
|
||||
{
|
||||
//获取到了远端流,可以播放
|
||||
console.log('播放成功',e.streams);
|
||||
});
|
||||
|
||||
player.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED,function(e)
|
||||
{
|
||||
// offer anwser 交换失败
|
||||
console.log('offer anwser 交换失败',e);
|
||||
stop();
|
||||
});
|
||||
|
||||
player.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM,function(s)
|
||||
{
|
||||
// 获取到了本地流
|
||||
document.getElementById('selfVideo').srcObject=s;
|
||||
document.getElementById('selfVideo').muted = true;
|
||||
//console.log('offer anwser 交换失败',e)
|
||||
});
|
||||
|
||||
player.on(ZLMRTCClient.Events.CAPTURE_STREAM_FAILED,function(s)
|
||||
{
|
||||
// 获取本地流失败
|
||||
console.log('获取本地流失败');
|
||||
});
|
||||
|
||||
player.on(ZLMRTCClient.Events.WEBRTC_ON_CONNECTION_STATE_CHANGE,function(state)
|
||||
{
|
||||
// RTC 状态变化 ,详情参考 https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/connectionState
|
||||
console.log('当前状态==>',state);
|
||||
});
|
||||
|
||||
player.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_OPEN,function(event)
|
||||
{
|
||||
console.log('rtc datachannel 打开 :',event);
|
||||
});
|
||||
|
||||
player.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_MSG,function(event)
|
||||
{
|
||||
console.log('rtc datachannel 消息 :',event.data);
|
||||
document.getElementById('msgrecv').value = event.data;
|
||||
});
|
||||
player.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_ERR,function(event)
|
||||
{
|
||||
console.log('rtc datachannel 错误 :',event);
|
||||
});
|
||||
player.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_CLOSE,function(event)
|
||||
{
|
||||
console.log('rtc datachannel 关闭 :',event);
|
||||
});
|
||||
}
|
||||
|
||||
function start()
|
||||
{
|
||||
stop();
|
||||
let elr = document.getElementById("resolution");
|
||||
let res = elr.options[elr.selectedIndex].text.match(/\d+/g);
|
||||
let h = parseInt(res.pop());
|
||||
let w = parseInt(res.pop());
|
||||
|
||||
if(document.getElementById('useCamera').checked && !recvOnly)
|
||||
{
|
||||
ZLMRTCClient.isSupportResolution(w,h).then(e=>{
|
||||
start_play();
|
||||
}).catch(e=>{
|
||||
alert("not support resolution");
|
||||
});
|
||||
}else{
|
||||
start_play();
|
||||
}
|
||||
}
|
||||
|
||||
function stop()
|
||||
{
|
||||
if(player)
|
||||
{
|
||||
player.close();
|
||||
player = null;
|
||||
var remote = document.getElementById('video');
|
||||
if(remote)
|
||||
{
|
||||
remote.srcObject = null;
|
||||
remote.load();
|
||||
}
|
||||
var local = document.getElementById('selfVideo');
|
||||
local.srcObject = null;
|
||||
local.load();
|
||||
}
|
||||
}
|
||||
|
||||
function send(){
|
||||
if(player){
|
||||
//send msg refernece https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel/send
|
||||
player.sendMsg(document.getElementById('msgsend').value);
|
||||
}
|
||||
}
|
||||
|
||||
function close(){
|
||||
if(player){
|
||||
player.closeDataChannel();
|
||||
}
|
||||
}
|
||||
|
||||
function on_click_to_play(app, stream) {
|
||||
console.log(`on_click_to_play: ${app}/${stream}`);
|
||||
var url = `${document.location.protocol}//${window.location.host}/index/api/webrtc?app=${app}&stream=${stream}&type=play`;
|
||||
document.getElementById('streamUrl').value = url;
|
||||
start();
|
||||
}
|
||||
|
||||
function clearStreamList() {
|
||||
let content = document.getElementById("olstreamlist");
|
||||
while (content.hasChildNodes()) {
|
||||
content.removeChild(content.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
function fillStreamList(json) {
|
||||
clearStreamList();
|
||||
if (json.code != 0 || !json.data) {
|
||||
return;
|
||||
}
|
||||
let ss = {};
|
||||
for (let o of json.data) {
|
||||
if (ss[o.app]) {
|
||||
ss[o.app].add(o.stream);
|
||||
} else {
|
||||
let set = new Set();
|
||||
set.add(o.stream);
|
||||
ss[o.app] = set;
|
||||
}
|
||||
}
|
||||
|
||||
for (let o in ss) {
|
||||
let app = o;
|
||||
for (let s of ss[o]) {
|
||||
if (s) {
|
||||
//console.log(app, s);
|
||||
let content = document.getElementById("olstreamlist");
|
||||
let child = `<li app="${app}" stream="${s}" onmouseover="this.style.color='blue';" onclick="on_click_to_play('${app}', '${s}')">${app}/${s}</li>`;
|
||||
content.insertAdjacentHTML("beforeend", child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getData(url) {
|
||||
const response = await fetch(url, {
|
||||
method: 'GET'
|
||||
});
|
||||
const data = await response.json();
|
||||
//console.log(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
function get_media_list() {
|
||||
let url = document.location.protocol+"//"+window.location.host+"/index/api/getMediaList?secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc";
|
||||
let json = getData(url);
|
||||
json.then((json)=> fillStreamList(json));
|
||||
}
|
||||
|
||||
setInterval(() => {
|
||||
// get_media_list();
|
||||
}, 5000);
|
||||
|
||||
function switchVideo(){
|
||||
if(player){
|
||||
// first arg bool false mean switch to screen video , second ignore
|
||||
// true mean switch to video , second is camera deviceid
|
||||
let elv = document.getElementById("videoDevice");
|
||||
let vdevid = JSON.parse(elv.options[elv.selectedIndex].value).deviceId
|
||||
player.switchVideo(document.getElementById('switchDevice').checked,vdevid).then(()=>{
|
||||
// switch video successful
|
||||
|
||||
}).catch(e=>{
|
||||
// switch video failed
|
||||
console.error(e);
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function switchAudio(){
|
||||
if(player){
|
||||
// first arg bool false mean switch to screen audio , second ignore
|
||||
// true mean switch to mic , second is mic deviceid
|
||||
let ela = document.getElementById("audioDevice");
|
||||
let adevid = JSON.parse(ela.options[ela.selectedIndex].value).deviceId
|
||||
player.switcAudio(document.getElementById('switchDevice').checked,adevid).then(()=>{
|
||||
// switch audio successful
|
||||
|
||||
}).catch(e=>{
|
||||
// switch audio failed
|
||||
console.error(e);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
<script>
|
||||
|
||||
</script>
|
||||
|
||||
</html>
|
Loading…
Reference in New Issue