mirror of
https://github.com/TheBinaryNinja/tvapp2.git
synced 2026-06-04 15:15:41 -04:00
feat: new health api check every 10 minutes with toast notification to user
This commit is contained in:
@@ -8,6 +8,9 @@
|
|||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/css/bootstrap.min.css" integrity="sha384-SgOJa3DmI69IUzQ2PVdRZhwQ+dy64/BUtbMJw1MZ8t5HZApcHrRKUc4W0kG879m7" crossorigin="anonymous">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/css/bootstrap.min.css" integrity="sha384-SgOJa3DmI69IUzQ2PVdRZhwQ+dy64/BUtbMJw1MZ8t5HZApcHrRKUc4W0kG879m7" crossorigin="anonymous">
|
||||||
<link rel="stylesheet" href="css/tvapp2.min.css">
|
<link rel="stylesheet" href="css/tvapp2.min.css">
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
|
<script src='https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/js/bootstrap.bundle.min.js' integrity='sha384-k6d4wzSIapyDyv1kpU366/PK5hCdSbCRGRCMv+eplOQJWyd1fbcAu9OCUj5zNLiq' crossorigin='anonymous'></script>
|
||||||
|
<script src=' https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js '></script>
|
||||||
|
<script src='js/tvapp2.min.js'></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
@@ -18,7 +21,7 @@
|
|||||||
<a class="header-name" href="<%= appUrlGithub %>">TVApp2 for Docker</a>
|
<a class="header-name" href="<%= appUrlGithub %>">TVApp2 for Docker</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="navbar-social">
|
<div class="navbar-social">
|
||||||
<a href="javascript:toggleRestart();"><i class="restart fa-solid fa-rotate" style="--fa-primary-color: rgb(255, 255, 255); --fa-secondary-color: rgb(255, 255, 255);" aria-hidden="true"></i></a>
|
<a href="javascript:doResync();"><i class="restart fa-solid fa-rotate" style="--fa-primary-color: rgb(255, 255, 255); --fa-secondary-color: rgb(255, 255, 255);" aria-hidden="true"></i></a>
|
||||||
<a href="<%= appUrlDocs %>"><i class="logo fa-duotone fa-solid fa-book-open-cover" style="--fa-primary-color: rgb(255, 255, 255); --fa-secondary-color: rgb(255, 255, 255);" aria-hidden="true"></i></a>
|
<a href="<%= appUrlDocs %>"><i class="logo fa-duotone fa-solid fa-book-open-cover" style="--fa-primary-color: rgb(255, 255, 255); --fa-secondary-color: rgb(255, 255, 255);" aria-hidden="true"></i></a>
|
||||||
<a href="<%= appUrlGithub %>"><i class="logo fa-logos fa-github" style="--fa-primary-color: rgb(255, 255, 255); --fa-secondary-color: rgb(255, 255, 255);" aria-hidden="true"></i></a>
|
<a href="<%= appUrlGithub %>"><i class="logo fa-logos fa-github" style="--fa-primary-color: rgb(255, 255, 255); --fa-secondary-color: rgb(255, 255, 255);" aria-hidden="true"></i></a>
|
||||||
<a href="<%= appUrlDiscord %>"><i class="logo fa-logos fa-discord" style="--fa-primary-color: rgb(255, 255, 255); --fa-secondary-color: rgb(255, 255, 255);" aria-hidden="true"></i></a>
|
<a href="<%= appUrlDiscord %>"><i class="logo fa-logos fa-discord" style="--fa-primary-color: rgb(255, 255, 255); --fa-secondary-color: rgb(255, 255, 255);" aria-hidden="true"></i></a>
|
||||||
@@ -127,64 +130,108 @@
|
|||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<!-- Toast Notifications -->
|
||||||
|
<!-- <button type="button" class="btn btn-primary" id="btnTestToasts">Show toast</button> -->
|
||||||
|
<div style="z-index: 9999;" class="toast position-fixed bottom-0 end-0 p-8 m-3" id="tvapp2Toast" role="alert" aria-live="assertive" aria-atomic="true" data-bs-autohide="true" data-bs-delay="4000">
|
||||||
|
<div class="toast-body">
|
||||||
|
<div class="d-flex gap-4">
|
||||||
|
<span><i class="fa-solid fa-circle-check fa-lg icon-success"></i></span>
|
||||||
|
<div class="d-flex flex-column flex-grow-1 gap-2">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<span id="toast-title" class="fw-semibold">Toast Title</span>
|
||||||
|
<button type="button" class="btn-close btn-close-sm ms-auto" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<span id="toast-message">Dismiss in 6 seconds</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Modal -->
|
<!-- Modal -->
|
||||||
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
<div class="modal fade" id="modalTvapp2" tabindex="-1" data-bs-backdrop="static" aria-labelledby="modalTvapp2Label" aria-hidden="true">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="exampleModalLabel">TVApp2</h5>
|
<h5 class="modal-title" id="modalTvapp2Label">Modal title</h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
Do you ever feel like a plastic bag.... drifting through the wind?
|
...
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Welcome to costco</button>
|
<button type="button" id="btn-secondary" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||||
<button type="button" class="btn btn-primary">I'll be back</button>
|
<button type="button" id="btn-primary" class="btn btn-primary">Save changes</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
/*
|
||||||
|
this is test code. enable the "Show Toast" button and then uncomment this code.
|
||||||
|
|
||||||
|
document.getElementById("btnTestToasts").onclick = function()
|
||||||
|
{
|
||||||
|
var toastElList = [].slice.call(document.querySelectorAll('.toast'))
|
||||||
|
var toastList = toastElList.map(function(toastEl)
|
||||||
|
{
|
||||||
|
return new bootstrap.Toast(toastEl)
|
||||||
|
});
|
||||||
|
|
||||||
|
toastList.forEach(toast => toast.show());
|
||||||
|
console.log(toastList);
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
const urlBase = window.location.origin;
|
const urlBase = window.location.origin;
|
||||||
const urlM3U = urlBase + "/playlist";
|
const urlM3U = urlBase + '/playlist';
|
||||||
const urlXML = urlBase + "/epg";
|
const urlXML = urlBase + '/epg';
|
||||||
const urlGZP = urlBase + "/gzip";
|
const urlGZP = urlBase + '/gzip';
|
||||||
|
|
||||||
document.getElementById("m3u-name").textContent = "<%= fileM3U %>";
|
document.getElementById('m3u-name').textContent = '<%= fileM3U %>';
|
||||||
document.getElementById("m3u-name").href = urlM3U;
|
document.getElementById('m3u-name').href = urlM3U;
|
||||||
document.getElementById("m3u-link").textContent = urlM3U;
|
document.getElementById('m3u-link').textContent = urlM3U;
|
||||||
document.getElementById("m3u-link").href = urlM3U;
|
document.getElementById('m3u-link').href = urlM3U;
|
||||||
document.getElementById("m3u-size").textContent = "<%= sizeM3U %>";
|
document.getElementById('m3u-size').textContent = '<%= sizeM3U %>';
|
||||||
document.getElementById("m3u-date").textContent = "<%= dateM3U %>";
|
document.getElementById('m3u-date').textContent = '<%= dateM3U %>';
|
||||||
|
|
||||||
document.getElementById("xml-name").textContent = "<%= fileXML %>";
|
document.getElementById('xml-name').textContent = '<%= fileXML %>';
|
||||||
document.getElementById("xml-name").href = urlXML;
|
document.getElementById('xml-name').href = urlXML;
|
||||||
document.getElementById("xml-link").textContent = urlXML;
|
document.getElementById('xml-link').textContent = urlXML;
|
||||||
document.getElementById("xml-link").href = urlXML;
|
document.getElementById('xml-link').href = urlXML;
|
||||||
document.getElementById("xml-size").textContent = "<%= sizeXML %>";
|
document.getElementById('xml-size').textContent = '<%= sizeXML %>';
|
||||||
document.getElementById("xml-date").textContent = "<%= dateXML %>";
|
document.getElementById('xml-date').textContent = '<%= dateXML %>';
|
||||||
|
|
||||||
document.getElementById("gzp-name").textContent = "<%= fileGZP %>";
|
document.getElementById('gzp-name').textContent = '<%= fileGZP %>';
|
||||||
document.getElementById("gzp-name").href = urlGZP;
|
document.getElementById('gzp-name').href = urlGZP;
|
||||||
document.getElementById("gzp-link").textContent = urlGZP;
|
document.getElementById('gzp-link').textContent = urlGZP;
|
||||||
document.getElementById("gzp-link").href = urlGZP;
|
document.getElementById('gzp-link').href = urlGZP;
|
||||||
document.getElementById("gzp-size").textContent = "<%= sizeGZP %>";
|
document.getElementById('gzp-size').textContent = '<%= sizeGZP %>';
|
||||||
document.getElementById("gzp-date").textContent = "<%= dateGZP %>";
|
document.getElementById('gzp-date').textContent = '<%= dateGZP %>';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
/*
|
||||||
|
Action > DOM Status
|
||||||
|
*/
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentReady", function() {
|
||||||
|
$("#tvapp2Toast").toast();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
/* $('#tvapp2Toast').toast("show"); */
|
||||||
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Notify > Localhost
|
Notify > Localhost
|
||||||
*/
|
*/
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function()
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
{
|
|
||||||
const host = window.location.hostname;
|
const host = window.location.hostname;
|
||||||
const port = window.location.port || (window.location.protocol === "https:" ? "443" : "80");
|
const port = window.location.port || (window.location.protocol === 'https:' ? '443' : '80');
|
||||||
if (host === "localhost" || host === "127.0.0.1")
|
if (host === 'localhost' || host === '127.0.0.1')
|
||||||
{
|
{
|
||||||
const msg = "<p><span class='warning'>Warning</span> If you are accessing this page via 127.0.0.1 or localhost, proxying will not work on other devices.Please load \
|
const msg = "<p><span class='warning'>Warning</span> If you are accessing this page via 127.0.0.1 or localhost, proxying will not work on other devices.Please load \
|
||||||
this page using your computer's IP address (e.g., 192.168.x.x) and port in order to access the playlist from other devices on your network.</p> \
|
this page using your computer's IP address (e.g., 192.168.x.x) and port in order to access the playlist from other devices on your network.</p> \
|
||||||
@@ -192,10 +239,10 @@
|
|||||||
<p> Learn how to locate your IP address on <a href='https://youtube.com/watch?v=UAhDHXN2c6E' target = '_blank' > Windows</a> or \
|
<p> Learn how to locate your IP address on <a href='https://youtube.com/watch?v=UAhDHXN2c6E' target = '_blank' > Windows</a> or \
|
||||||
<a href='https://youtube.com/watch?v=gaIYP4TZfHI' target = '_blank' > Linux</a>.</p>";
|
<a href='https://youtube.com/watch?v=gaIYP4TZfHI' target = '_blank' > Linux</a>.</p>";
|
||||||
|
|
||||||
document.getElementById("ntfy-localhost").innerHTML = msg;
|
document.getElementById('ntfy-localhost').innerHTML = msg;
|
||||||
document.getElementById("ntfy-localhost").style.display = "block";
|
document.getElementById('ntfy-localhost').style.display = 'block';
|
||||||
} else {
|
} else {
|
||||||
document.getElementById("ntfy-localhost").style.display = "none";
|
document.getElementById('ntfy-localhost').style.display = 'none';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -203,79 +250,148 @@
|
|||||||
Notify > Firewall
|
Notify > Firewall
|
||||||
*/
|
*/
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function()
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
{
|
const port = window.location.port || (window.location.protocol === 'https:' ? '443' : '80');
|
||||||
const port = window.location.port || (window.location.protocol === "https:" ? "443" : "80");
|
|
||||||
const msg = "<p><span class='notice'>Notice</span> Port <strong> " + port + " </strong> must be open and allowed through your <a href='https://youtu.be/zOZWlTplrcA?si=nGXrHKU4sAQsy18e&t=18 target='_blank'>Windows</a> \
|
const msg = "<p><span class='notice'>Notice</span> Port <strong> " + port + " </strong> must be open and allowed through your <a href='https://youtu.be/zOZWlTplrcA?si=nGXrHKU4sAQsy18e&t=18 target='_blank'>Windows</a> \
|
||||||
or <a href='https://youtu.be/7c_V_3nWWbA?si=Hkd_II9myn-AkNnS&t=12' target='_blank'>Linux</a> OS firewall settings \
|
or <a href='https://youtu.be/7c_V_3nWWbA?si=Hkd_II9myn-AkNnS&t=12' target='_blank'>Linux</a> OS firewall settings \
|
||||||
This action enables devices such as Firestick or Android to connect to the server and request the playlist through the proxy.</p>";
|
This action enables devices such as Firestick or Android to connect to the server and request the playlist through the proxy.</p>";
|
||||||
|
|
||||||
document.getElementById("ntfy-firewall").innerHTML = msg;
|
document.getElementById('ntfy-firewall').innerHTML = msg;
|
||||||
document.getElementById("ntfy-firewall").style.display = "block";
|
document.getElementById('ntfy-firewall').style.display = 'block';
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Notify > Restart / Resync
|
Notify > Restart / Resync
|
||||||
*/
|
*/
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function()
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
{
|
const port = window.location.port || (window.location.protocol === 'https:' ? '443' : '80');
|
||||||
const port = window.location.port || (window.location.protocol === "https:" ? "443" : "80");
|
|
||||||
const msg = "<p><span class='success'>Success</span> Your IPTV m3u channels and xml guide data has been successfully re-synced. \
|
const msg = "<p><span class='success'>Success</span> Your IPTV m3u channels and xml guide data has been successfully re-synced. \
|
||||||
Please refresh this window to see new data</p>";
|
Please refresh this window to see new data</p>";
|
||||||
|
|
||||||
document.getElementById("ntfy-restart").innerHTML = msg;
|
document.getElementById('ntfy-restart').innerHTML = msg;
|
||||||
document.getElementById("ntfy-restart").style.display = "none";
|
document.getElementById('ntfy-restart').style.display = 'none';
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Activate Resync
|
Action > Healthcheck
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function toggleRestart()
|
function doHealthCheck()
|
||||||
|
{
|
||||||
|
const toastTypeClass = [];
|
||||||
|
toastTypeClass[ 'DEFAULT' ] = 'text-bg-primary';
|
||||||
|
toastTypeClass[ 'UNHEALTHY' ] = 'text-bg-warning';
|
||||||
|
toastTypeClass[ 'HEALTHY' ]= 'text-bg-success';
|
||||||
|
toastTypeClass[ 'ERROR' ]= 'text-bg-danger';
|
||||||
|
|
||||||
|
$.ajax(
|
||||||
|
{
|
||||||
|
url: 'api/health',
|
||||||
|
type: 'GET',
|
||||||
|
data: {
|
||||||
|
internal: 1
|
||||||
|
},
|
||||||
|
beforeSend: function( data )
|
||||||
|
{
|
||||||
|
console.log('Sending health check ...')
|
||||||
|
},
|
||||||
|
success: function( data )
|
||||||
|
{
|
||||||
|
const status = data.message;
|
||||||
|
const code = data.code;
|
||||||
|
if ( status )
|
||||||
|
{
|
||||||
|
const toastClass = toastTypeClass[status.toUpperCase()];
|
||||||
|
const toastElm = document.getElementById('tvapp2Toast');
|
||||||
|
toastElm.classList.add(toastClass);
|
||||||
|
|
||||||
|
$('.toast #toast-title').html(`<%= appName %> is ${ status }`);
|
||||||
|
$('.toast #toast-message').html(`Health check returned ${ status } (${ code })`);
|
||||||
|
$('#tvapp2Toast').toast('show');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function( data )
|
||||||
|
{
|
||||||
|
const toastClass = toastTypeClass['ERROR'];
|
||||||
|
const toastElm = document.getElementById('tvapp2Toast');
|
||||||
|
toastElm.classList.add(toastClass);
|
||||||
|
|
||||||
|
$('.toast #toast-title').html(`Could not connect to health check api`);
|
||||||
|
$('.toast #toast-message').html(`Failed to communicate with health check api. Try restarting the docker container to restore connection.`);
|
||||||
|
$('#tvapp2Toast').toast('show');
|
||||||
|
}
|
||||||
|
}).always(function()
|
||||||
|
{
|
||||||
|
const healthTime = '<%= healthTimer %>';
|
||||||
|
setTimeout(function()
|
||||||
|
{
|
||||||
|
doHealthCheck();
|
||||||
|
}, parseInt(healthTime));
|
||||||
|
}).responseText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Action > Healthcheck > Initialize
|
||||||
|
*/
|
||||||
|
|
||||||
|
setTimeout(function(){ doHealthCheck(); }, 10000);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Action > Do Resync
|
||||||
|
*/
|
||||||
|
|
||||||
|
function doResync()
|
||||||
{
|
{
|
||||||
$.ajax(
|
$.ajax(
|
||||||
{
|
{
|
||||||
url: 'restart',
|
url: 'restart',
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
data: {
|
data: {
|
||||||
x: 1
|
internal: 1
|
||||||
},
|
},
|
||||||
beforeSend: function( data )
|
beforeSend: function( data )
|
||||||
{
|
{
|
||||||
const dimmer = document.createElement('div');
|
const dimmer = document.createElement('div');
|
||||||
dimmer.setAttribute("id", "dimmer");
|
dimmer.setAttribute('id', 'dimmer');
|
||||||
dimmer.style.visibility = "visible";
|
dimmer.style.visibility = 'visible';
|
||||||
dimmer.classList.add("dimmer-in");
|
dimmer.classList.add('dimmer-in');
|
||||||
document.getElementsByTagName('body')[0].appendChild(dimmer);
|
document.getElementsByTagName('body')[0].appendChild(dimmer);
|
||||||
document.getElementById("ntfy-firewall").style.display = "none";
|
document.getElementById('ntfy-firewall').style.display = 'none';
|
||||||
document.getElementById("ntfy-localhost").style.display = "none";
|
document.getElementById('ntfy-localhost').style.display = 'none';
|
||||||
document.getElementById("ntfy-restart").style.display = "none";
|
document.getElementById('ntfy-restart').style.display = 'none';
|
||||||
|
|
||||||
const iconResync = document.getElementsByClassName('fa-rotate');
|
const iconResync = document.getElementsByClassName('fa-rotate');
|
||||||
iconResync[0].classList.remove("restart");
|
iconResync[0].classList.remove('restart');
|
||||||
iconResync[0].classList.add("spin");
|
iconResync[0].classList.add('spin');
|
||||||
|
|
||||||
|
$('.modal-content .modal-body').html('<small>The M3U and EPG data will now be re-downloaded and synced with your TVApp2 container. Afterward, this page will be refreshed automatically.</small><br /><br /><small>Please wait...</small>')
|
||||||
|
$('.modal-content .modal-title').html('Resyncing Data')
|
||||||
|
$('#modalTvapp2').modal('show');
|
||||||
|
|
||||||
|
const modalBtnPrimary = document.querySelector('#btn-primary');
|
||||||
|
modalBtnPrimary.style.display = 'none';
|
||||||
|
modalBtnPrimary.style.visibility= 'hidden';
|
||||||
|
|
||||||
},
|
},
|
||||||
success: function( data )
|
success: function( data )
|
||||||
{
|
{
|
||||||
setTimeout(() =>
|
setTimeout(() =>
|
||||||
{
|
{
|
||||||
document.getElementById("ntfy-restart").style.display = "block"
|
document.getElementById('ntfy-restart').style.display = 'block'
|
||||||
const dimmer = document.getElementById("dimmer");
|
const dimmer = document.getElementById('dimmer');
|
||||||
dimmer.classList.remove("dimmer-in");
|
dimmer.classList.remove('dimmer-in');
|
||||||
dimmer.classList.add("dimmer-out");
|
dimmer.classList.add('dimmer-out');
|
||||||
dimmer.remove();
|
dimmer.remove();
|
||||||
const iconResync = document.getElementsByClassName('fa-rotate');
|
const iconResync = document.getElementsByClassName('fa-rotate');
|
||||||
iconResync[0].classList.remove("spin");
|
iconResync[0].classList.remove('spin');
|
||||||
iconResync[0].classList.add("restart");
|
iconResync[0].classList.add('restart');
|
||||||
setTimeout(location.reload.bind(location), 1000);
|
|
||||||
|
setTimeout(location.reload.bind(location), 5000);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/js/bootstrap.bundle.min.js" integrity="sha384-k6d4wzSIapyDyv1kpU366/PK5hCdSbCRGRCMv+eplOQJWyd1fbcAu9OCUj5zNLiq" crossorigin="anonymous"></script>
|
|
||||||
<script src=" https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js "></script>
|
|
||||||
<script src="js/tvapp2.min.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user