update
This commit is contained in:
BIN
assets/banner.png
Normal file
BIN
assets/banner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 103 KiB |
BIN
assets/chart-analytics-dark.png
Normal file
BIN
assets/chart-analytics-dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 127 KiB |
BIN
assets/chart-analytics-light.png
Normal file
BIN
assets/chart-analytics-light.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 123 KiB |
209
assets/charts.html
Normal file
209
assets/charts.html
Normal file
@ -0,0 +1,209 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Twitch-Channel-Points-Miner-v2</title>
|
||||
<link rel="icon" type="image/png" sizes="32x32"
|
||||
href="https://static.twitchcdn.net/assets/favicon-32-e29e246c157142c94346.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16"
|
||||
href="https://static.twitchcdn.net/assets/favicon-16-52e571ffea063af7a7f4.png">
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/apexcharts@3.42.0"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js"></script>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css" rel="stylesheet" />
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.1/css/bulma.css" rel="stylesheet" />
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" rel="stylesheet"
|
||||
integrity="sha512-1ycn6IcaQQ40/MKBW2W4Rhis/DbILU74C1vSrLJxCq57o941Ym01SwNsOMqvEBFlcgUa6xLiPY/NS5R+E6ztJQ=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||
|
||||
<link href="{{url_for('static', filename='dark-theme.css')}}" rel="stylesheet" />
|
||||
<link href="{{url_for('static', filename='style.css')}}" rel="stylesheet" />
|
||||
|
||||
</head>
|
||||
|
||||
<body style="height: 100vh;">
|
||||
<div class="container has-text-centered" style="height: 100vh;">
|
||||
<div class="columns" id="header">
|
||||
<div class="column has-text-right">
|
||||
<a href="https://github.com/rdavydov/Twitch-Channel-Points-Miner-v2" target="_blank"><img
|
||||
style="margin-top: -20px;" width="600px" src="{{url_for('static', filename='banner.png')}}"
|
||||
alt="banner"></a>
|
||||
</div>
|
||||
<div class="column is-4 has-text-left" style="margin-top: 35px;">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="https://github.com/Tkd-Alex" target="_blank">Tkd-Alex</a></td>
|
||||
<td><a href="https://github.com/rdavydov" target="_blank">rdavydov</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/Tkd-Alex/Twitch-Channel-Points-Miner-v2/blob/master/LICENSE">
|
||||
<img alt="License"
|
||||
src="https://img.shields.io/github/license/Tkd-Alex/Twitch-Channel-Points-Miner-v2" /></a>
|
||||
</td>
|
||||
<td><a href="https://github.com/rdavydov/Twitch-Channel-Points-Miner-v2/blob/master/LICENSE">
|
||||
<img alt="License"
|
||||
src="https://img.shields.io/github/license/rdavydov/Twitch-Channel-Points-Miner-v2" /></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://www.python.org/downloads/release/python-360/">
|
||||
<img alt="Python3"
|
||||
src="https://img.shields.io/badge/built%20for-Python≥3.6-red.svg?style=flat"></a>
|
||||
</td>
|
||||
<td><a href="https://www.python.org/downloads/release/python-360/">
|
||||
<img alt="Python3"
|
||||
src="https://img.shields.io/badge/built%20for-Python≥3.6-red.svg?style=flat"></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/Tkd-Alex/Twitch-Channel-Points-Miner-v2/pulls">
|
||||
<img alt="PRsWelcome"
|
||||
src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat" /></a>
|
||||
</td>
|
||||
<td><a href="https://github.com/rdavydov/Twitch-Channel-Points-Miner-v2/pulls">
|
||||
<img alt="PRsWelcome"
|
||||
src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat" /></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/Tkd-Alex/Twitch-Channel-Points-Miner-v2/stargazers">
|
||||
<img alt="GitHub Repo stars"
|
||||
src="https://img.shields.io/github/stars/Tkd-Alex/Twitch-Channel-Points-Miner-v2" /></a>
|
||||
</td>
|
||||
<td><a href="https://github.com/rdavydov/Twitch-Channel-Points-Miner-v2/stargazers">
|
||||
<img alt="GitHub Repo stars"
|
||||
src="https://img.shields.io/github/stars/rdavydov/Twitch-Channel-Points-Miner-v2" /></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a
|
||||
href="https://github.com/Tkd-Alex/Twitch-Channel-Points-Miner-v2/issues?q=is%3Aissue+is%3Aclosed">
|
||||
<img alt="GitHub closed issues"
|
||||
src="https://img.shields.io/github/issues-closed/Tkd-Alex/Twitch-Channel-Points-Miner-v2"></a>
|
||||
</td>
|
||||
<td><a
|
||||
href="https://github.com/rdavydov/Twitch-Channel-Points-Miner-v2/issues?q=is%3Aissue+is%3Aclosed">
|
||||
<img alt="GitHub closed issues"
|
||||
src="https://img.shields.io/github/issues-closed/rdavydov/Twitch-Channel-Points-Miner-v2"></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/Tkd-Alex/Twitch-Channel-Points-Miner-v2">
|
||||
<img alt="GitHub last commit"
|
||||
src="https://img.shields.io/github/last-commit/Tkd-Alex/Twitch-Channel-Points-Miner-v2" /></a>
|
||||
</td>
|
||||
<td><a href="https://github.com/rdavydov/Twitch-Channel-Points-Miner-v2">
|
||||
<img alt="GitHub last commit"
|
||||
src="https://img.shields.io/github/last-commit/rdavydov/Twitch-Channel-Points-Miner-v2" /></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns">
|
||||
<div class="column is-narrow has-text-center">
|
||||
<div class="dropdown">
|
||||
<div class="dropdown-trigger">
|
||||
<button class="button" aria-haspopup="true" aria-controls="dropdown-menu">
|
||||
<span id="sorting-by"></span>
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-angle-down" aria-hidden="true"></i>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="dropdown-menu" id="dropdown-menu" role="menu">
|
||||
<div class="dropdown-content">
|
||||
<a href="#" class="dropdown-item" onClick="changeSortBy(this)">
|
||||
Name ascending
|
||||
</a>
|
||||
<a href="#" class="dropdown-item" onClick="changeSortBy(this)">
|
||||
Name descending
|
||||
</a>
|
||||
<a href="#" class="dropdown-item" onClick="changeSortBy(this)">
|
||||
Points ascending
|
||||
</a>
|
||||
<a href="#" class="dropdown-item" onClick="changeSortBy(this)">
|
||||
Points descending
|
||||
</a>
|
||||
<a href="#" class="dropdown-item" onClick="changeSortBy(this)">
|
||||
Last activity ascending
|
||||
</a>
|
||||
<a href="#" class="dropdown-item" onClick="changeSortBy(this)">
|
||||
Last activity descending
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-2 has-text-left">
|
||||
<input class="input" type="date" id="startDate">
|
||||
</div>
|
||||
<div class="column is-2 has-text-left">
|
||||
<input class="input" type="date" id="endDate">
|
||||
</div>
|
||||
<div class="column has-text-right" style="margin: auto">
|
||||
<button id="auto-update-log">⏸️</button>
|
||||
<label class="checkbox checkbox-label">
|
||||
Log
|
||||
<input type="checkbox" id="log">
|
||||
</label>
|
||||
<label class="checkbox checkbox-label">
|
||||
Annotations
|
||||
<input type="checkbox" checked="true" id="annotations">
|
||||
</label>
|
||||
<label class="checkbox checkbox-label">
|
||||
Dark mode
|
||||
<input type="checkbox" checked="true" id="dark-mode">
|
||||
</label>
|
||||
<label class="checkbox checkbox-label">
|
||||
Header
|
||||
<input type="checkbox" checked="true" id="toggle-header">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column is-narrow">
|
||||
<div class="tabs">
|
||||
<ul id="streamers-list"></ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="box" id="chart" style="padding: 0.30rem;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column is-12">
|
||||
<div class="box" id="log-box" style="padding: 0.30rem; display: none;">
|
||||
<pre id="log-content">In your run.py file set save=True in logger_settings to save logs to a file. </pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script type="text/javascript">
|
||||
// n.b. Seems that we are unable to call Flask variable in JS code imported from JS file.
|
||||
// That's why "link[href='{{url_for('static', filename='dark-theme.css')}}']", {{ refresh }}, {{ daysAgo }} are here.
|
||||
|
||||
function toggleDarkMode() {
|
||||
var darkMode = $('#dark-mode').prop("checked")
|
||||
$("link[href='{{url_for('static', filename='dark-theme.css')}}']").prop("disabled", !darkMode);
|
||||
chart.updateOptions({
|
||||
colors: darkMode === true ? ["#f9826c"] : ['#008ffb'],
|
||||
chart: {
|
||||
foreColor: darkMode === true ? "#fff" : '#373d3f'
|
||||
},
|
||||
tooltip: {
|
||||
theme: darkMode === true ? "dark" : "light"
|
||||
}
|
||||
})
|
||||
}
|
||||
var refresh = parseInt("{{ refresh }}");
|
||||
var daysAgo = parseInt("{{ daysAgo }}");
|
||||
</script>
|
||||
<script type="text/javascript" src="{{url_for('static', filename='script.js')}}"></script>
|
||||
|
||||
</html>
|
||||
39
assets/dark-theme.css
Normal file
39
assets/dark-theme.css
Normal file
@ -0,0 +1,39 @@
|
||||
body, .dropdown *, .input {
|
||||
background: #343E59;
|
||||
color: #fff;
|
||||
}
|
||||
a {
|
||||
color: #fff;
|
||||
}
|
||||
a:hover {
|
||||
color: #f9826c;
|
||||
}
|
||||
.box {
|
||||
background-color: #2B2D3E;
|
||||
}
|
||||
.tabs a {
|
||||
border-bottom-color: #dbdbdb;
|
||||
color: #fff;
|
||||
border-bottom-style: none;
|
||||
}
|
||||
.tabs li.is-active a {
|
||||
border-bottom-color: #f9826c;
|
||||
color: #fff;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
.tabs a:hover {
|
||||
border-bottom-color: #dbdbdb;
|
||||
color: #dbdbdb;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
.tabs ul{
|
||||
margin-bottom: 5px;
|
||||
border-bottom-style: none;
|
||||
}
|
||||
.checkbox:hover{
|
||||
color: #f9826c;
|
||||
}
|
||||
#log-content {
|
||||
color: #fff;
|
||||
background-color: #2B2D3E;
|
||||
}
|
||||
BIN
assets/prediction.png
Normal file
BIN
assets/prediction.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
389
assets/script.js
Normal file
389
assets/script.js
Normal file
@ -0,0 +1,389 @@
|
||||
// https://apexcharts.com/javascript-chart-demos/line-charts/zoomable-timeseries/
|
||||
var options = {
|
||||
series: [],
|
||||
chart: {
|
||||
type: 'area',
|
||||
stacked: false,
|
||||
height: 490,
|
||||
zoom: {
|
||||
type: 'x',
|
||||
enabled: true,
|
||||
autoScaleYaxis: true
|
||||
},
|
||||
// background: '#2B2D3E',
|
||||
foreColor: '#fff'
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
},
|
||||
stroke: {
|
||||
curve: 'smooth',
|
||||
},
|
||||
markers: {
|
||||
size: 0,
|
||||
},
|
||||
title: {
|
||||
text: 'Channel points (dates are displayed in UTC)',
|
||||
align: 'left'
|
||||
},
|
||||
colors: ["#f9826c"],
|
||||
fill: {
|
||||
type: 'gradient',
|
||||
gradient: {
|
||||
shadeIntensity: 1,
|
||||
inverseColors: false,
|
||||
opacityFrom: 0.5,
|
||||
opacityTo: 0,
|
||||
stops: [0, 90, 100]
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
title: {
|
||||
text: 'Channel points'
|
||||
},
|
||||
},
|
||||
xaxis: {
|
||||
type: 'datetime',
|
||||
labels: {
|
||||
datetimeUTC: false
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
theme: 'dark',
|
||||
shared: false,
|
||||
x: {
|
||||
show: true,
|
||||
format: 'HH:mm:ss dd MMM',
|
||||
},
|
||||
custom: ({
|
||||
series,
|
||||
seriesIndex,
|
||||
dataPointIndex,
|
||||
w
|
||||
}) => {
|
||||
return (`<div class="apexcharts-active">
|
||||
<div class="apexcharts-tooltip-title">${w.globals.seriesNames[seriesIndex]}</div>
|
||||
<div class="apexcharts-tooltip-series-group apexcharts-active" style="order: 1; display: flex; padding-bottom: 0px !important;">
|
||||
<div class="apexcharts-tooltip-text">
|
||||
<div class="apexcharts-tooltip-y-group">
|
||||
<span class="apexcharts-tooltip-text-label"><b>Points</b>: ${series[seriesIndex][dataPointIndex]}</span><br>
|
||||
<span class="apexcharts-tooltip-text-label"><b>Reason</b>: ${w.globals.seriesZ[seriesIndex][dataPointIndex] ? w.globals.seriesZ[seriesIndex][dataPointIndex] : ''}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`)
|
||||
}
|
||||
},
|
||||
noData: {
|
||||
text: 'Loading...'
|
||||
}
|
||||
};
|
||||
|
||||
var chart = new ApexCharts(document.querySelector("#chart"), options);
|
||||
var currentStreamer = null;
|
||||
var annotations = [];
|
||||
|
||||
var streamersList = [];
|
||||
var sortBy = "Name ascending";
|
||||
var sortField = 'name';
|
||||
|
||||
var startDate = new Date();
|
||||
startDate.setDate(startDate.getDate() - daysAgo);
|
||||
var endDate = new Date();
|
||||
|
||||
$(document).ready(function () {
|
||||
// Variable to keep track of whether log checkbox is checked
|
||||
var isLogCheckboxChecked = $('#log').prop('checked');
|
||||
|
||||
// Variable to keep track of whether auto-update log is active
|
||||
var autoUpdateLog = true;
|
||||
|
||||
// Variable to keep track of the last received log index
|
||||
var lastReceivedLogIndex = 0;
|
||||
|
||||
$('#auto-update-log').click(() => {
|
||||
autoUpdateLog = !autoUpdateLog;
|
||||
$('#auto-update-log').text(autoUpdateLog ? '⏸️' : '▶️');
|
||||
|
||||
if (autoUpdateLog) {
|
||||
getLog();
|
||||
}
|
||||
});
|
||||
|
||||
// Function to get the full log content
|
||||
function getLog() {
|
||||
if (isLogCheckboxChecked) {
|
||||
$.get(`/log?lastIndex=${lastReceivedLogIndex}`, function (data) {
|
||||
// Process and display the new log entries received
|
||||
$("#log-content").append(data);
|
||||
// Scroll to the bottom of the log content
|
||||
$("#log-content").scrollTop($("#log-content")[0].scrollHeight);
|
||||
|
||||
// Update the last received log index
|
||||
lastReceivedLogIndex += data.length;
|
||||
|
||||
if (autoUpdateLog) {
|
||||
// Call getLog() again after a certain interval (e.g., 1 second)
|
||||
setTimeout(getLog, 1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve the saved header visibility preference from localStorage
|
||||
var headerVisibility = localStorage.getItem('headerVisibility');
|
||||
|
||||
// Set the initial header visibility based on the saved preference or default to 'visible'
|
||||
if (headerVisibility === 'hidden') {
|
||||
$('#toggle-header').prop('checked', false);
|
||||
$('#header').hide();
|
||||
} else {
|
||||
$('#toggle-header').prop('checked', true);
|
||||
$('#header').show();
|
||||
}
|
||||
|
||||
// Handle the toggle header change event
|
||||
$('#toggle-header').change(function () {
|
||||
if (this.checked) {
|
||||
$('#header').show();
|
||||
// Save the header visibility preference as 'visible' in localStorage
|
||||
localStorage.setItem('headerVisibility', 'visible');
|
||||
} else {
|
||||
$('#header').hide();
|
||||
// Save the header visibility preference as 'hidden' in localStorage
|
||||
localStorage.setItem('headerVisibility', 'hidden');
|
||||
}
|
||||
});
|
||||
|
||||
chart.render();
|
||||
|
||||
if (!localStorage.getItem("annotations")) localStorage.setItem("annotations", true);
|
||||
if (!localStorage.getItem("dark-mode")) localStorage.setItem("dark-mode", true);
|
||||
if (!localStorage.getItem("sort-by")) localStorage.setItem("sort-by", "Name ascending");
|
||||
|
||||
// Restore settings from localStorage on page load
|
||||
$('#annotations').prop("checked", localStorage.getItem("annotations") === "true");
|
||||
$('#dark-mode').prop("checked", localStorage.getItem("dark-mode") === "true");
|
||||
|
||||
// Handle the annotation toggle click event
|
||||
$('#annotations').click(() => {
|
||||
var isChecked = $('#annotations').prop("checked");
|
||||
localStorage.setItem("annotations", isChecked);
|
||||
updateAnnotations();
|
||||
});
|
||||
|
||||
// Handle the dark mode toggle click event
|
||||
$('#dark-mode').click(() => {
|
||||
var isChecked = $('#dark-mode').prop("checked");
|
||||
localStorage.setItem("dark-mode", isChecked);
|
||||
toggleDarkMode();
|
||||
});
|
||||
|
||||
$('#startDate').val(formatDate(startDate));
|
||||
$('#endDate').val(formatDate(endDate));
|
||||
|
||||
sortBy = localStorage.getItem("sort-by");
|
||||
if (sortBy.includes("Points")) sortField = 'points';
|
||||
else if (sortBy.includes("Last activity")) sortField = 'last_activity';
|
||||
else sortField = 'name';
|
||||
$('#sorting-by').text(sortBy);
|
||||
getStreamers();
|
||||
|
||||
updateAnnotations();
|
||||
toggleDarkMode();
|
||||
|
||||
// Retrieve log checkbox state from localStorage and update UI accordingly
|
||||
var logCheckboxState = localStorage.getItem('logCheckboxState');
|
||||
$('#log').prop('checked', logCheckboxState === 'true');
|
||||
if (logCheckboxState === 'true') {
|
||||
isLogCheckboxChecked = true;
|
||||
$('#auto-update-log').show();
|
||||
$('#log-box').show();
|
||||
// Start continuously updating the log content
|
||||
getLog();
|
||||
}
|
||||
|
||||
// Handle the log checkbox change event
|
||||
$('#log').change(function () {
|
||||
isLogCheckboxChecked = $(this).prop('checked');
|
||||
localStorage.setItem('logCheckboxState', isLogCheckboxChecked);
|
||||
|
||||
if (isLogCheckboxChecked) {
|
||||
$('#log-box').show();
|
||||
$('#auto-update-log').show();
|
||||
getLog();
|
||||
$('html, body').scrollTop($(document).height());
|
||||
} else {
|
||||
$('#log-box').hide();
|
||||
$('#auto-update-log').hide();
|
||||
// Clear log content when checkbox is unchecked
|
||||
// $("#log-content").text('');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function formatDate(date) {
|
||||
var d = new Date(date),
|
||||
month = '' + (d.getMonth() + 1),
|
||||
day = '' + d.getDate(),
|
||||
year = d.getFullYear();
|
||||
|
||||
if (month.length < 2) month = '0' + month;
|
||||
if (day.length < 2) day = '0' + day;
|
||||
|
||||
return [year, month, day].join('-');
|
||||
}
|
||||
|
||||
function changeStreamer(streamer, index) {
|
||||
$("li").removeClass("is-active")
|
||||
$("li").eq(index - 1).addClass('is-active');
|
||||
currentStreamer = streamer;
|
||||
|
||||
// Update the chart title with the current streamer's name
|
||||
options.title.text = `${streamer.replace(".json", "")}'s channel points (dates are displayed in UTC)`;
|
||||
chart.updateOptions(options);
|
||||
|
||||
// Save the selected streamer in localStorage
|
||||
localStorage.setItem("selectedStreamer", currentStreamer);
|
||||
|
||||
getStreamerData(streamer);
|
||||
}
|
||||
|
||||
function getStreamerData(streamer) {
|
||||
if (currentStreamer == streamer) {
|
||||
$.getJSON(`./json/${streamer}`, {
|
||||
startDate: formatDate(startDate),
|
||||
endDate: formatDate(endDate)
|
||||
}, function (response) {
|
||||
chart.updateSeries([{
|
||||
name: streamer.replace(".json", ""),
|
||||
data: response["series"]
|
||||
}], true)
|
||||
clearAnnotations();
|
||||
annotations = response["annotations"];
|
||||
updateAnnotations();
|
||||
setTimeout(function () {
|
||||
getStreamerData(streamer);
|
||||
}, 300000); // 5 minutes
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getAllStreamersData() {
|
||||
$.getJSON(`./json_all`, function (response) {
|
||||
for (var i in response) {
|
||||
chart.appendSeries({
|
||||
name: response[i]["name"].replace(".json", ""),
|
||||
data: response[i]["data"]["series"]
|
||||
}, true)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getStreamers() {
|
||||
$.getJSON('streamers', function (response) {
|
||||
streamersList = response;
|
||||
sortStreamers();
|
||||
|
||||
// Restore the selected streamer from localStorage on page load
|
||||
var selectedStreamer = localStorage.getItem("selectedStreamer");
|
||||
|
||||
if (selectedStreamer) {
|
||||
currentStreamer = selectedStreamer;
|
||||
} else {
|
||||
// If no selected streamer is found, default to the first streamer in the list
|
||||
currentStreamer = streamersList.length > 0 ? streamersList[0].name : null;
|
||||
}
|
||||
|
||||
// Ensure the selected streamer is still active and scrolled into view
|
||||
renderStreamers();
|
||||
});
|
||||
}
|
||||
|
||||
function renderStreamers() {
|
||||
$("#streamers-list").empty();
|
||||
var promised = new Promise((resolve, reject) => {
|
||||
streamersList.forEach((streamer, index, array) => {
|
||||
displayname = streamer.name.replace(".json", "");
|
||||
if (sortField == 'points') displayname = "<font size='-2'>" + streamer['points'] + "</font> " + displayname;
|
||||
else if (sortField == 'last_activity') displayname = "<font size='-2'>" + formatDate(streamer['last_activity']) + "</font> " + displayname;
|
||||
var isActive = currentStreamer === streamer.name;
|
||||
if (!isActive && localStorage.getItem("selectedStreamer") === null && index === 0) {
|
||||
isActive = true;
|
||||
currentStreamer = streamer.name;
|
||||
}
|
||||
var activeClass = isActive ? 'is-active' : '';
|
||||
var listItem = `<li id="streamer-${streamer.name}" class="${activeClass}"><a onClick="changeStreamer('${streamer.name}', ${index + 1}); return false;">${displayname}</a></li>`;
|
||||
$("#streamers-list").append(listItem);
|
||||
if (isActive) {
|
||||
// Scroll the selected streamer into view
|
||||
document.getElementById(`streamer-${streamer.name}`).scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'center'
|
||||
});
|
||||
}
|
||||
if (index === array.length - 1) resolve();
|
||||
});
|
||||
});
|
||||
promised.then(() => {
|
||||
changeStreamer(currentStreamer, streamersList.findIndex(streamer => streamer.name === currentStreamer) + 1);
|
||||
});
|
||||
}
|
||||
|
||||
function sortStreamers() {
|
||||
streamersList = streamersList.sort((a, b) => {
|
||||
return (a[sortField] > b[sortField] ? 1 : -1) * (sortBy.includes("ascending") ? 1 : -1);
|
||||
});
|
||||
}
|
||||
|
||||
function changeSortBy(option) {
|
||||
sortBy = option.innerText.trim();
|
||||
if (sortBy.includes("Points")) sortField = 'points'
|
||||
else if (sortBy.includes("Last activity")) sortField = 'last_activity'
|
||||
else sortField = 'name';
|
||||
sortStreamers();
|
||||
renderStreamers();
|
||||
$('#sorting-by').text(sortBy);
|
||||
localStorage.setItem("sort-by", sortBy);
|
||||
}
|
||||
|
||||
function updateAnnotations() {
|
||||
if ($('#annotations').prop("checked") === true) {
|
||||
clearAnnotations()
|
||||
if (annotations && annotations.length > 0)
|
||||
annotations.forEach((annotation, index) => {
|
||||
annotations[index]['id'] = `id-${index}`
|
||||
chart.addXaxisAnnotation(annotation, true)
|
||||
})
|
||||
} else clearAnnotations()
|
||||
}
|
||||
|
||||
function clearAnnotations() {
|
||||
if (annotations && annotations.length > 0)
|
||||
annotations.forEach((annotation, index) => {
|
||||
chart.removeAnnotation(annotation['id'])
|
||||
})
|
||||
chart.clearAnnotations();
|
||||
}
|
||||
|
||||
// Toggle
|
||||
$('#annotations').click(() => {
|
||||
updateAnnotations();
|
||||
});
|
||||
$('#dark-mode').click(() => {
|
||||
toggleDarkMode();
|
||||
});
|
||||
|
||||
$('.dropdown').click(() => {
|
||||
$('.dropdown').toggleClass('is-active');
|
||||
});
|
||||
|
||||
// Input date
|
||||
$('#startDate').change(() => {
|
||||
startDate = new Date($('#startDate').val());
|
||||
getStreamerData(currentStreamer);
|
||||
});
|
||||
$('#endDate').change(() => {
|
||||
endDate = new Date($('#endDate').val());
|
||||
getStreamerData(currentStreamer);
|
||||
});
|
||||
70
assets/style.css
Normal file
70
assets/style.css
Normal file
@ -0,0 +1,70 @@
|
||||
html,
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.tabs ul {
|
||||
-webkit-flex-direction: column;
|
||||
flex-direction: column;
|
||||
flex-grow: 0px;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
font-size: 20px;
|
||||
padding-left: 15px;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #d6dee1;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #d6dee1;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #d6dee1;
|
||||
border-radius: 20px;
|
||||
border: 6px solid transparent;
|
||||
background-clip: content-box;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #a8bbbf;
|
||||
}
|
||||
|
||||
#log-content {
|
||||
text-align: left;
|
||||
white-space: pre-wrap;
|
||||
max-height: 400px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#auto-update-log {
|
||||
display: none;
|
||||
background-color: transparent;
|
||||
font-size: 20px;
|
||||
padding: 0;
|
||||
border-radius: 5px;
|
||||
}
|
||||
Reference in New Issue
Block a user