forked from j62/ctbrec
1
0
Fork 0

Add sorting for the recordings table

This commit is contained in:
0xboobface 2019-07-21 15:08:12 +02:00
parent d6beaac5f9
commit d4b7545fae
1 changed files with 165 additions and 191 deletions

View File

@ -2,17 +2,6 @@
<html lang="en">
<head>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-129283922-1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'UA-129283922-1');
</script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
@ -79,35 +68,15 @@
aria-controls="profile" aria-selected="false">Recordings</a></li>
<li class="nav-item mx-0 mx-lg-1"><a id="configuration-tab" data-toggle="tab" class="nav-link py-3 px-0 px-lg-3 rounded" href="#configuration" role="tab"
aria-controls="profile" aria-selected="false">Settings</a></li>
<!--
<li class="nav-item mx-0 mx-lg-1">
<a class="nav-link py-3 px-0 px-lg-3 rounded js-scroll-trigger" href="#code">Source Code</a>
</li>
<li class="nav-item mx-0 mx-lg-1">
<a href="#donate" class="js-scroll-trigger" style="float:right; margin-left: 10px">
<img src="/static/button-red.png" alt="Buy a coffee" style="width:150px; padding-top: 7px"/>
</a>
</li>
-->
</ul>
</div>
</div>
</nav>
<!-- Header -
<header class="masthead bg-primary text-white text-center">
<div class="container">
<h1 class="text-uppercase mb-0">CTB Recorder</h1>
<hr class="star-light">
<h2 class="font-weight-light mb-0">A free recording software for different camsites.<br/>Currently supported: BongaCams, Cam4, CamSoda, Chaturbate, FC2Live, LiveJasmin, MyFreeCams, Streamate</h2>
</div>
</header>
-->
<div id="alert-container"></div>
<div class="tab-content" id="myTabContent">
<section id="models" class="tab-pane fade show active" role="tabpanel" aria-labelledby="models-tab">
<section id="models" class="tab-pane fade" role="tabpanel" aria-labelledby="models-tab">
<div class="container">
<div class="row">
<div class="col-lg-10 mx-auto">
@ -138,7 +107,7 @@
</div>
</div>
</section>
<section id="recordings" class="tab-pane fade" role="tabpanel" aria-labelledby="recordings-tab">
<section id="recordings" class="tab-pane fade active show" role="tabpanel" aria-labelledby="recordings-tab">
<div class="container">
<div class="row">
<div class="col-lg-10 mx-auto">
@ -147,11 +116,11 @@
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>Model</th>
<th>Date</th>
<th>Status</th>
<th>Progress</th>
<th>Size</th>
<th><a href="#" data-bind="orderable: {collection: 'recordings', field: 'model.name'}">Model</a></th>
<th><a href="#" data-bind="orderable: {collection: 'recordings', field: 'startDate'}">Date</a></th>
<th><a href="#" data-bind="orderable: {collection: 'recordings', field: 'ko_status'}">Status</a></th>
<th><a href="#" data-bind="orderable: {collection: 'recordings', field: 'progress'}">Progress</a></th>
<th><a href="#" data-bind="orderable: {collection: 'recordings', field: 'sizeInByte'}">Size</a></th>
<th></th>
<th></th>
<th></th>
@ -160,10 +129,10 @@
<tbody data-bind="foreach: recordings">
<tr>
<td><a data-bind="attr: { href: model.url, title: model.name }, text: model.name"></a></td>
<td data-bind="text: date"></td>
<td data-bind="text: status"></td>
<td data-bind="text: progressString"></td>
<td data-bind="text: size"></td>
<td data-bind="text: ko_date"></td>
<td data-bind="text: ko_status"></td>
<td data-bind="text: ko_progressString"></td>
<td data-bind="text: ko_size"></td>
<td>
<button class="btn btn-secondary fa fa-play" title="Play recording" data-bind="click: play"></button>
</td>
@ -182,60 +151,16 @@
</div>
</section>
<section id="configuration" class="tab-pane fade" role="tabpanel" aria-labelledby="configuration-tab">
<h1>Coming soon</h1>
<div class="container">
<div class="row">
<div class="col-lg-10 mx-auto">
<h3>Not implemented, yet!</h3>
</div>
</div>
</div>
</section>
</div>
<!-- Footer -->
<!--
<footer class="footer text-center">
<div class="container">
<div class="row">
<div class="col-md-4 mb-5 mb-lg-0">
<h4 class="text-uppercase mb-4">Location</h4>
<p class="lead mb-0">2215 John Daniel Drive
<br>Clark, MO 65243</p>
</div>
<div class="col-md-4 mb-5 mb-lg-0">
<h4 class="text-uppercase mb-4">Around the Web</h4>
<ul class="list-inline mb-0">
<li class="list-inline-item">
<a class="btn btn-outline-light btn-social text-center rounded-circle" href="#">
<i class="fa fa-fw fa-facebook"></i>
</a>
</li>
<li class="list-inline-item">
<a class="btn btn-outline-light btn-social text-center rounded-circle" href="#">
<i class="fa fa-fw fa-google-plus"></i>
</a>
</li>
<li class="list-inline-item">
<a class="btn btn-outline-light btn-social text-center rounded-circle" href="#">
<i class="fa fa-fw fa-twitter"></i>
</a>
</li>
<li class="list-inline-item">
<a class="btn btn-outline-light btn-social text-center rounded-circle" href="#">
<i class="fa fa-fw fa-linkedin"></i>
</a>
</li>
<li class="list-inline-item">
<a class="btn btn-outline-light btn-social text-center rounded-circle" href="#">
<i class="fa fa-fw fa-dribbble"></i>
</a>
</li>
</ul>
</div>
<div class="col-md-4">
<h4 class="text-uppercase mb-4">About Freelancer</h4>
<p class="lead mb-0">Freelance is a free to use, open source Bootstrap theme created by
<a href="http://startbootstrap.com">Start Bootstrap</a>.</p>
</div>
</div>
</div>
</footer>
-->
<!-- Bootstrap core JavaScript -->
<script src="/static/vendor/jquery/jquery.min.js"></script>
<script src="/static/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
@ -280,21 +205,21 @@
dataType : 'json',
async : true,
timeout : 60000,
data : '{"action": "resume", "model": ' + JSON.stringify(model) + '}',
success : function(data) {
if (data.status === 'success') {
$.notify('Recording of ' + model.name + ' resumed', 'info');
} else {
$.notify('Resuming recording of model ' + model.name + ' failed', 'error');
}
},
error : function(jqXHR, textStatus, errorThrown) {
console.log(textStatus, errorThrown);
data : '{"action": "resume", "model": ' + JSON.stringify(model) + '}'
})
.done(function(data) {
if (data.status === 'success') {
$.notify('Recording of ' + model.name + ' resumed', 'info');
} else {
$.notify('Resuming recording of model ' + model.name + ' failed', 'error');
}
})
.fail(function(jqXHR, textStatus, errorThrown) {
console.log(textStatus, errorThrown);
$.notify('Resuming recording of model ' + model.name + ' failed', 'error');
});
} catch (e) {
console.log(e);
console.log('Unexpected error', e);
}
},
@ -306,21 +231,21 @@
dataType : 'json',
async : true,
timeout : 60000,
data : '{"action": "suspend", "model": ' + JSON.stringify(model) + '}',
success : function(data) {
if (data.status === 'success') {
$.notify('Recording of ' + model.name + ' suspended', 'info');
} else {
$.notify('Suspending recording of model ' + model.name + ' failed', 'error');
}
},
error : function(jqXHR, textStatus, errorThrown) {
console.log(textStatus, errorThrown);
data : '{"action": "suspend", "model": ' + JSON.stringify(model) + '}'
})
.done(function(data) {
if (data.status === 'success') {
$.notify('Recording of ' + model.name + ' suspended', 'info');
} else {
$.notify('Suspending recording of model ' + model.name + ' failed', 'error');
}
})
.fail(function(jqXHR, textStatus, errorThrown) {
console.log(textStatus, errorThrown);
$.notify('Suspending recording of model ' + model.name + ' failed', 'error');
});
} catch (e) {
console.log(e);
console.log('Unexpected error', e);
}
},
@ -332,27 +257,27 @@
dataType : 'json',
async : true,
timeout : 60000,
data : '{"action": "stop", "model": ' + JSON.stringify(model) + '}',
success : function(data) {
if (data.status === 'success') {
$.notify('Removed ' + model.name, 'info');
observableModelsArray.remove(model);
} else {
$.notify('Removing model ' + model.name + ' failed', 'error');
}
},
error : function(jqXHR, textStatus, errorThrown) {
console.log(textStatus, errorThrown);
data : '{"action": "stop", "model": ' + JSON.stringify(model) + '}'
})
.done(function(data) {
if (data.status === 'success') {
$.notify('Removed ' + model.name, 'info');
observableModelsArray.remove(model);
} else {
$.notify('Removing model ' + model.name + ' failed', 'error');
}
})
.fail(function(jqXHR, textStatus, errorThrown) {
console.log(textStatus, errorThrown);
$.notify('Removing model ' + model.name + ' failed', 'error');
});
} catch (e) {
console.log(e);
console.log('Unexpected error', e);
}
},
deleteRecording: function(recording) {
let name = recording.model.name + ' ' + recording.date;
let name = recording.model.name + ' ' + recording.ko_date();
try {
$.ajax({
type : 'POST',
@ -360,22 +285,22 @@
dataType : 'json',
async : true,
timeout : 60000,
data : '{"action": "delete", "recording": ' + JSON.stringify(recording) + '}',
success : function(data) {
if (data.status === 'success') {
$.notify('Removed recording ' + name, 'info');
observableRecordingsArray.remove(recording);
} else {
$.notify('Removing recording ' + name + ' failed', 'error');
}
},
error : function(jqXHR, textStatus, errorThrown) {
console.log(textStatus, errorThrown);
data : '{"action": "delete", "recording": ' + JSON.stringify(recording) + '}'
})
.done(function(data) {
if (data.status === 'success') {
$.notify('Removed recording ' + name, 'info');
observableRecordingsArray.remove(recording);
} else {
$.notify('Removing recording ' + name + ' failed', 'error');
}
})
.fail(function(jqXHR, textStatus, errorThrown) {
console.log(textStatus, errorThrown);
$.notify('Removing recording ' + name + ' failed', 'error');
});
} catch (e) {
console.log(e);
console.log('Unexpected error', e);
}
}
};
@ -442,21 +367,21 @@
dataType : 'json',
async : true,
timeout : 60000,
data : '{"action": "listOnline"}',
success : function(data) {
if (data.status === 'success') {
onlineModels = data.models;
} else {
console.log('request failed', data);
}
updateModels();
},
error : function(jqXHR, textStatus, errorThrown) {
console.log(textStatus, errorThrown);
data : '{"action": "listOnline"}'
})
.done(function(data, textStatus, jqXHR) {
if (data.status === 'success') {
onlineModels = data.models;
} else {
console.log('request failed', data);
}
updateModels();
})
.fail(function(jqXHR, textStatus, errorThrown) {
console.log(jqXHR, textStatus, errorThrown);
});
} catch (e) {
console.log(e);
console.log('Unexpected error', e);
}
setTimeout(updateOnlineModels, 3000);
}
@ -481,7 +406,6 @@
for (let idx in observableModelsArray()) {
let model = observableModelsArray()[idx];
if(!isModelInArray(models, model)) {
console.log('remove', model);
observableModelsArray.remove(model);
}
}
@ -552,20 +476,85 @@
dataType : 'json',
async : true,
timeout : 60000,
data : '{"action": "list"}',
success : function(data) {
if (data.status === 'success') {
syncModels(data.models);
} else {
console.log('request failed', data);
}
},
error : function(jqXHR, textStatus, errorThrown) {
console.log(textStatus, errorThrown);
data : '{"action": "list"}'
})
.done(function(data) {
if (data.status === 'success') {
syncModels(data.models);
} else {
console.log('request failed', data);
}
})
.fail(function(jqXHR, textStatus, errorThrown) {
console.log(textStatus, errorThrown);
});
} catch (e) {
console.log(e);
console.log('Unexpected error', e);
}
}
function isRecordingInArray(array, recording) {
for (let idx in array) {
let r = array[idx];
if(r.path === recording.path) {
return true;
}
}
return false;
}
/**
* Synchronizes recordings from the server with the displayed
* knockout recordings table
*/
function syncRecordings(recordings) {
// remove recordings from the observable array, which are not in the
// updated list
for (let idx in observableRecordingsArray()) {
let recording = observableRecordingsArray()[idx];
if(!isRecordingInArray(recordings, recording)) {
observableRecordingsArray.remove(recording);
}
}
// add recordings to the observable array, which are new in the
// updated list
for (let idx in recordings) {
let recording = recordings[idx];
if (!isRecordingInArray(observableRecordingsArray(), recording)) {
recording.ko_date = ko.computed(function() {
return new Date(recording.startDate).toLocaleString('default', {
'day': '2-digit',
'month': '2-digit',
'year': 'numeric',
'hour': '2-digit',
'minute': '2-digit',
'second': '2-digit'
})
});
recording.ko_progressString = ko.observable(recording.progress === -1 ? '' : recording.progress);
recording.ko_size = ko.observable(calculateSize(recording.sizeInByte));
recording.ko_status = ko.observable(recording.status);
recording.playlist = '/hls' + recording.path + '/playlist.m3u8';
observableRecordingsArray.push(recording);
}
}
// update existing recordings
for (let i in recordings) {
let recording = recordings[i];
for (let j in observableRecordingsArray()) {
let r = observableRecordingsArray()[j];
if(recording.path === r.path) {
r.progress = recording.progress;
r.sizeInByte = recording.sizeInByte;
r.status = recording.status;
r.startDate = recording.startDate;
r.ko_progressString(recording.progress === -1 ? '' : (recording.progress + '%'));
r.ko_size(calculateSize(recording.sizeInByte));
r.ko_status(recording.status);
}
}
}
}
@ -577,37 +566,22 @@
dataType : 'json',
async : true,
timeout : 60000,
data : '{"action": "recordings"}',
success : function(data) {
if (data.status === 'success') {
observableRecordingsArray.removeAll();
for ( let idx in data.recordings) {
let recording = data.recordings[idx];
recording.date = new Date(recording.startDate).toLocaleString('default', {
'day': '2-digit',
'month': '2-digit',
'year': 'numeric',
'hour': '2-digit',
'minute': '2-digit',
'second': '2-digit'
});
recording.progressString = recording.progress === -1 ? '' : recording.progress;
recording.size = calculateSize(recording.sizeInByte);
recording.playlist = '/hls' + recording.path + '/playlist.m3u8';
observableRecordingsArray.push(recording);
}
} else {
console.log('request failed', data);
}
},
error : function(jqXHR, textStatus, errorThrown) {
console.log(textStatus, errorThrown);
data : '{"action": "recordings"}'
})
.done(function(data) {
if (data.status === 'success') {
syncRecordings(data.recordings);
} else {
console.log('request failed', data);
}
})
.fail(function(jqXHR, textStatus, errorThrown) {
console.log(textStatus, errorThrown);
});
} catch (e) {
console.log(e);
console.log('Unexpected error', e);
}
setTimeout(updateRecordings, 60000);
setTimeout(updateRecordings, 3000);
}
updateOnlineModels();