You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

150 lines
6.6 KiB
HTML

{% extends "base.html" %}
{% from "_pagination.html" import pager %}
{% block title %}📊 Video Storage Analytics{% endblock %}
{% block content %}
<h1>📊 Video Storage Analytics</h1>
<div class="controls">
<form method="get" action="{{ url_for('web.dashboard') }}" style="display:inline-flex;gap:8px;align-items:center;">
<label>Timeframe:</label>
<select name="timeframe" onchange="this.form.submit()">
<option value="all" {{ 'selected' if timeframe=='all' else '' }}>All time</option>
<option value="week" {{ 'selected' if timeframe=='week' else '' }}>This week</option>
<option value="month" {{ 'selected' if timeframe=='month' else '' }}>Last 30 days</option>
<option value="year" {{ 'selected' if timeframe=='year' else '' }}>Last 365 days</option>
<option value="custom"{{ 'selected' if timeframe=='custom' else '' }}>Custom…</option>
</select>
<input type="date" name="start" value="{{ start_date or '' }}" placeholder="Start" />
<input type="date" name="end" value="{{ end_date or '' }}" placeholder="End" />
<input type="hidden" name="q" value="{{ query or '' }}">
<input type="hidden" name="sort" value="{{ sort }}">
<input type="hidden" name="dir" value="{{ dir }}">
<button type="submit">Apply</button>
</form>
<form method="get" action="{{ url_for('web.dashboard') }}" style="display:inline-block;">
<input type="text" name="q" placeholder="Search users..." value="{{ query or '' }}">
<button type="submit">Search</button>
</form>
</div>
<div class="table-wrap">
<table id="analytics-table">
<thead>
<tr>
{% set next_user_dir = 'asc' if sort != 'user' or dir == 'desc' else 'desc' %}
{% set next_platform_dir = 'asc' if sort != 'platform' or dir == 'desc' else 'desc' %}
{% set next_total_dir = 'asc' if sort != 'total_size' or dir == 'desc' else 'desc' %}
{% set next_count_dir = 'asc' if sort != 'video_count' or dir == 'desc' else 'desc' %}
{% set next_avg_dir = 'asc' if sort != 'avg_size' or dir == 'desc' else 'desc' %}
{% set next_last_dir = 'asc' if sort != 'last_online' or dir == 'desc' else 'desc' %}
<th><a href="{{ url_for('web.dashboard', q=query, page=1, sort='user', dir=next_user_dir) }}">User{% if sort=='user' %} {{ '▲' if dir=='asc' else '▼' }}{% endif %}</a></th>
<th><a href="{{ url_for('web.dashboard', q=query, page=1, sort='platform', dir=next_platform_dir) }}">Platform{% if sort=='platform' %} {{ '▲' if dir=='asc' else '▼' }}{% endif %}</a></th>
<th><a href="{{ url_for('web.dashboard', q=query, page=1, sort='total_size', dir=next_total_dir) }}">Total Storage (GB){% if sort=='total_size' %} {{ '▲' if dir=='asc' else '▼' }}{% endif %}</a></th>
<th><a href="{{ url_for('web.dashboard', q=query, page=1, sort='video_count', dir=next_count_dir) }}">Video Count{% if sort=='video_count' %} {{ '▲' if dir=='asc' else '▼' }}{% endif %}</a></th>
<th><a href="{{ url_for('web.dashboard', q=query, page=1, sort='avg_size', dir=next_avg_dir) }}">Avg Size per Video (GB){% if sort=='avg_size' %} {{ '▲' if dir=='asc' else '▼' }}{% endif %}</a></th>
<th><a href="{{ url_for('web.dashboard', q=query, page=1, sort='last_online', dir=next_last_dir) }}">Last Online{% if sort=='last_online' %} {{ '▲' if dir=='asc' else '▼' }}{% endif %}</a></th>
</tr>
</thead>
<tbody>
{% for key, stats in storage_usage %}
{% set user, platform = key.split("::") %}
<tr data-username="{{ user|lower }}">
<td>
<!-- ⭐ favorite star on the left -->
<button class="fav-btn user-fav"
data-username="{{ user }}"
aria-pressed="{{ 'true' if user in favorite_users else 'false' }}">
</button>
<a href="/user/{{ user }}">{{ user }}</a>
{% set uname = user.lower() %}
{% if uname in online_set %}
<span class="status-dot dot-online" title="Online"></span>
{% elif uname in recording_offline_set %}
<span class="status-dot dot-record" title="Recording (offline)"></span>
{% else %}
<span class="status-dot dot-offline" title="Offline"></span>
{% endif %}
</td>
<td><a href="https://{{ platform }}.com/{{ user }}">{{ platform }}</a></td>
<td>{{ "%.2f"|format(stats.total_size) }}</td>
<td>{{ stats.video_count }}</td>
<td>{{ "%.2f"|format(avg_sizes[key]) }}</td>
<td>
{% if stats.last_online %}
{{ stats.last_online.strftime('%Y-%m-%d') }}
{% else %}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{{ pager('web.dashboard', page, total_pages, q=query, sort=sort, dir=dir, timeframe=timeframe, start=start_str, end=end_str) }}
{% endblock %}
{% block scripts %}
<script>
async function refreshStatusDots() {
try {
const resp = await fetch("/api/get_recording/");
if (!resp.ok) return;
const streamers = await resp.json();
const online = new Set(streamers.filter(s => s.is_online).map(s => (s.username||"").toLowerCase()));
const recOff = new Set(streamers.filter(s => !s.is_online).map(s => (s.username||"").toLowerCase()));
document.querySelectorAll('#analytics-table tbody tr').forEach(tr => {
const uname = (tr.dataset.username || "").toLowerCase();
const dot = tr.querySelector('.status-dot'); if (!dot) return;
dot.classList.remove('dot-online','dot-record','dot-offline');
if (online.has(uname)) { dot.classList.add('dot-online'); dot.title='Online'; }
else if (recOff.has(uname)) { dot.classList.add('dot-record'); dot.title='Recording (offline)'; }
else { dot.classList.add('dot-offline'); dot.title='Offline'; }
});
} catch(e) { console.error('status refresh failed:', e); }
}
refreshStatusDots(); setInterval(refreshStatusDots, 20000);
</script>
<script>
document.addEventListener("click", async (e) => {
if (e.target.classList.contains("fav-btn")) {
const btn = e.target;
const username = btn.dataset.username;
const videoId = btn.dataset.videoId;
const isFav = btn.getAttribute("aria-pressed") === "true";
const newState = !isFav;
btn.setAttribute("aria-pressed", newState);
try {
if (username) {
await fetch("/api/favorite_user", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username, favorite: newState })
});
} else if (videoId) {
await fetch(`/api/fav/toggle/${videoId}`, { method: "POST" });
}
} catch (err) {
console.error("Favorite toggle failed:", err);
}
}
});
</script>
{% endblock %}