video_translator / templates /tiktok_player.html
sergey.agapov
initial commit
12b350f
raw
history blame
13.1 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Multilanguage Player</title>
<script src="https://cdn.dashjs.org/latest/dash.all.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/webvtt-parser@2.1.2/dist/parser.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
margin: 0;
background-color: #ffffff;
}
.top-div {
width: 100%;
height: 100px;
background-color: #3C8DF9;
margin-bottom: 20px;
}
.container {
display: flex;
justify-content: space-between;
width: 90%;
max-width: 1200px;
}
.left-panel {
width: 30%;
padding: 20px;
box-sizing: border-box;
}
.right-panel {
width: 65%;
padding: 20px;
box-sizing: border-box;
}
.frame {
width: 402px;
height: 720px;
border: 2px solid #ccc;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 10px;
}
video {
max-width: 100%;
max-height: 100%;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
select, input, button {
margin: 10px 0;
padding: 5px;
width: 100%;
}
#result {
margin-top: 10px;
font-weight: bold;
}
</style>
</head>
<body>
<div class="top-div"><img src="{{ url_for('static', filename='images/logo.png') }}" alt="Bytedance" height="100px"></div>
<div class="container">
<div class="left-panel">
<h2>FLV Stream Url</h2>
<form id="flvForm">
<input type="text" id="flvInput" placeholder="http://example.com/stream.flv" required>
<button type="submit">Play Stream</button>
</form>
<div id="result"></div>
<form id="terminateStreamForm">
<button type="submit">Stop Stream</button>
</form>
<h3>Language Selection</h3>
<select id="captionSelect">
<option value="original">Original</option>
<option value="es">Spanish</option>
<option value="ru">Russian</option>
<option value="en">English</option>
<option value="zh">Chinese</option>
</select>
<h3>Model Selection</h3>
<select id="models">
<option value="base">Base</option>
<option value="small">Small</option>
<option value="medium">Medium</option>
<option value="large">Large</option>
<option value="large-v2">Large-V2</option>
</select>
</div>
<div class="right-panel">
<div class="frame">
<div id="waitingMessage">Waiting for the stream...</div>
<video id="videoPlayer" controls style="display: none;"></video>
</div>
</div>
</div>
<script>
(function () {
var url = "{{ url_for('serve_file', filename='manifest.mpd') }}";
var player = dashjs.MediaPlayer().create();
var video = document.querySelector("#videoPlayer");
var waitingMessage = document.querySelector("#waitingMessage");
var captionSelect = document.querySelector("#captionSelect");
var currentLanguage = "original";
var refreshInterval = 10000;
var desiredDelay = 45;
var checkInterval = 5000; // Check every 5 seconds
function initializePlayer() {
// Reset the player if it's already initialized
if (player.isReady()) {
player.reset();
}
console.log("Initializing the player with %s", url)
player.updateSettings({
streaming: {
delay: {
liveDelay: 50,
},
buffer: {
bufferToKeep: 40,
bufferTimeAtTopQuality: 50,
bufferTimeAtTopQualityLongForm: 50,
initialBufferLevel: 50,
},
}
});
fetch(url, {method: 'GET', cache: "no-store"})
.then(response => {
if (response.ok) {
console.log("Response: ", response.text())
} else {
console.log("Response not ok: ", response.body)
}
})
.catch(() => {
console.log("Error")
});
player.initialize(video, url, false);
// player.attachView(video);
player.setMute(true)
player.enableForcedTextStreaming(true);
player.on(dashjs.MediaPlayer.events.STREAM_INITIALIZED, onStreamInitialized);
player.on(dashjs.MediaPlayer.events.ERROR, onPlayerError);
}
function checkStreamAvailability() {
fetch(url, {method: 'GET', cache: "no-store"})
.then(response => {
if (response.ok) {
console.log("Stream is ready")
//manifest is there, wait a sec for init segments
setTimeout(initializePlayer, 15000)
} else {
setTimeout(checkStreamAvailability, checkInterval);
}
})
.catch(() => {
setTimeout(checkStreamAvailability, checkInterval);
});
}
function onStreamInitialized() {
console.log("Stream initialized, setting up captions");
setupCaptions();
setInterval(refreshCaptions, refreshInterval);
waitForInitialData();
player.play()
}
function onPlayerError(e) {
console.log("Player error:", e);
let errorCode = e.code || e.error?.code || e.error?.error?.code;
console.log("Extracted error code:", errorCode);
if (errorCode === 25) {
console.log("Rescheduling...")
waitingMessage.style.display = "block";
video.style.display = "none";
checkStreamAvailability();
}
console.log("None...")
//waitingMessage.style.display = "block";
//video.style.display = "none";
//checkStreamAvailability();
}
function waitForInitialData() {
console.log("Waiting for initial data");
if (player.getBufferLength() > 0 && video.readyState >= 2) {
console.log("Initial data buffered, starting playback");
waitingMessage.style.display = "none";
video.style.display = "block";
//player.play();
} else {
setTimeout(waitForInitialData, 100);
}
}
function parseVTT(vttContent) {
const lines = vttContent.trim().split('\n');
let cues = [];
let cue = {};
for (let i = 0; i < lines.length; i++) {
if (lines[i].includes('-->')) {
const [start, end] = lines[i].split('-->').map(timeString => {
const [hours, minutes, seconds] = timeString.trim().split(':');
return parseFloat(hours) * 3600 + parseFloat(minutes) * 60 + parseFloat(seconds);
});
cue = {start, end, text: ''};
} else if (lines[i].trim() !== '' && cue.start !== undefined) {
cue.text += lines[i] + '\n';
} else if (lines[i].trim() === '' && cue.text) {
cues.push(cue);
cue = {};
}
}
if (cue.text) {
cues.push(cue);
}
return cues;
}
function loadCaptions(lang) {
console.log("Loading captions for language: " + lang);
var baseUrl = "{{ url_for('serve_file', filename='') }}"; // This will give the base URL for the 'serve_file' endpoint.
var fileName = "captions_" + lang + ".vtt";
var captionUrl = baseUrl + fileName;
for (var i = 0; i < video.textTracks.length; i++) {
video.textTracks[i].mode = 'disabled';
}
var track = Array.from(video.textTracks).find(t => t.language === lang);
if (!track) {
track = video.addTextTrack("captions", lang, lang);
}
track.mode = 'showing';
updateTrackCues(track, captionUrl);
currentLanguage = lang;
console.log("Captions loaded for language: " + lang);
}
function updateTrackCues(track, url) {
fetch(url)
.then(response => response.text())
.then(vttContent => {
const cues = parseVTT(vttContent);
while (track.cues.length > 0) {
track.removeCue(track.cues[0]);
}
cues.forEach(cue => {
const vttCue = new VTTCue(cue.start, cue.end, cue.text.trim());
track.addCue(vttCue);
});
})
.catch(error => console.error('Error updating captions:', error));
}
function refreshCaptions() {
if (currentLanguage) {
var track = Array.from(video.textTracks).find(t => t.language === currentLanguage);
if (track) {
var baseUrl = "{{ url_for('serve_file', filename='') }}"; // This will give the base URL for the 'serve_file' endpoint.
var fileName = "captions_" + currentLanguage + ".vtt";
var captionUrl = baseUrl + fileName;
updateTrackCues(track, captionUrl);
}
}
}
function setupCaptions() {
var tracks = player.getTracksFor('text');
console.log("Available text tracks:", tracks);
if (tracks.length > 0) {
captionSelect.innerHTML = '';
tracks.forEach(function (track) {
var option = document.createElement('option');
option.value = track.lang;
option.text = track.lang;
captionSelect.appendChild(option);
});
loadCaptions(tracks[0].lang);
} else {
loadCaptions(currentLanguage);
}
}
captionSelect.addEventListener("change", function () {
loadCaptions(this.value);
});
// FLV Stream Checker
$('#flvForm').submit(function(e) {
$('#waitingMessage').text("Checking the url...");
e.preventDefault();
$.ajax({
url: '/terminate',
method: 'POST',
data: null,
});
if (player.isReady()) {
player.pause()
}
$.ajax({
url: '/check_flv',
method: 'POST',
data: { url: $('#flvInput').val(), model: $('#models').val() },
success: function(response) {
$('#waitingMessage').text(response.message);
if (response.status === 'success') {
waitingMessage.style.display = "block";
video.style.display = "none";
//initializePlayer();
//checkStreamAvailability();
}
},
error: function() {
$('#result').text('An error occurred');
}
});
});
// FLV Stream Checker
$('#terminateStreamForm').submit(function(e) {
e.preventDefault();
$.ajax({
url: '/terminate',
method: 'POST',
data: null,
});
if (player.isReady()) {
player.pause()
}
});
// Start checking for stream availability
//initializePlayer();
checkStreamAvailability();
// Log current live delay every 5 seconds
// setInterval(() => {
// var currentLiveDelay = player.duration() - player.time();
// console.log("Current live delay:", currentLiveDelay);
//}, 5000);
})();
</script>
</body>
</html>