Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12,048 changes: 12,048 additions & 0 deletions assets/css/bootstrap.css

Large diffs are not rendered by default.

3 changes: 0 additions & 3 deletions assets/css/bulma.min.css

This file was deleted.

42 changes: 42 additions & 0 deletions assets/css/main.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
:root {
--bs-success: #00A46A;
--bs-green: #00A46A;
--black: #000;
--white: #fff;
}

.filters .filter-item {
padding: 15px 20px;
border-radius: 30px;
border: 1px solid var(--black);
transition: all 0.5s;
}

.filters .filter-item:hover {
background: var(--black);
color: var(--white);
transition: all 0.5s;
}

.filters .filter-item.filter-item--active {
background: var(--black);
color: var(--white);
}

a {
text-decoration: none;
color: var(--black);
}

.img-64 {
height: 64px;
width: auto;
}


/* Waiting for bootstrap SASS */

.btn-success {
border-color: var(--bs-success);
background-color: var(--bs-success);
}
7 changes: 7 additions & 0 deletions assets/javascript/bootstrap.min.js

Large diffs are not rendered by default.

24 changes: 23 additions & 1 deletion src/extractors/torrent_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,24 +51,33 @@ impl TorrentListViewRequest {
Self::SingleTorrent(target) => vec![list.get(target).unwrap()],
}
}

pub fn is_filter(&self, torrent_list_filter: TorrentListFilter) -> bool {
match self {
Self::ListFilter(filter) => filter == &torrent_list_filter,
_ => false,
}
}
}

/// A specific filter applied to the torrent list.
#[derive(Clone, Debug, Deserialize)]
#[derive(Clone, Debug, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum TorrentListFilter {
/// All torrents.
Everything,
Ongoing,
Stuck,
Unmanaged,
Ok,
}

impl TorrentListFilter {
pub fn filter_torrent_list(&self, list: TorrentList) -> Vec<Torrent> {
let mut list: Vec<Torrent> = match self {
Self::Everything => list.into_iter().collect(),
Self::Ongoing => list.into_iter().filter(is_torrent_ongoing).collect(),
Self::Ok => list.into_iter().filter(is_torrent_ok).collect(),
Self::Stuck => list.into_iter().filter(is_torrent_stuck).collect(),
Self::Unmanaged => list.into_iter().filter(is_torrent_unmanaged).collect(),
};
Expand All @@ -91,6 +100,8 @@ pub struct TorrentListCounter {
/// Torrents not known to TorrentManager.
// TODO: this is actually not implemented yet
pub unmanaged: u32,
/// Torrent successfully downloaded
pub ok: u32,
}

impl TorrentListCounter {
Expand All @@ -115,6 +126,11 @@ impl TorrentListCounter {
continue;
}

if is_torrent_ok(torrent) {
counter.ok += 1;
continue;
}

// Otherwise, the torrent is simply seeding,
// and we simply don't care.
}
Expand All @@ -129,6 +145,12 @@ pub fn is_torrent_unmanaged(_t: &Torrent) -> bool {
false
}

/// Check if torrent hasn't finished (progress < 100%)
/// and is less than 24h hours.
pub fn is_torrent_ok(t: &Torrent) -> bool {
t.progress == 100
}

/// Check if torrent hasn't finished (progress < 100%)
/// and is less than 24h hours.
pub fn is_torrent_ongoing(t: &Torrent) -> bool {
Expand Down
7 changes: 5 additions & 2 deletions src/routes/progress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use axum::extract::{Path, State};
use hightorrent_api::hightorrent::{SingleTarget, Torrent, TorrentContent};

use crate::extractors::torrent_list::{
TorrentListCounter, TorrentListView, TorrentListViewRequest,
TorrentListCounter, TorrentListFilter, TorrentListView, TorrentListViewRequest,
};
use crate::state::{AppState, AppStateContext, error::AppStateError};

Expand All @@ -15,6 +15,8 @@ pub struct TorrentListTemplate {
state: AppStateContext,
/// Specific context for torrent lists.
torrent_list: TorrentListContext,
// Filter object
filter: TorrentListViewRequest,
/// Logged-in user.
user: Option<String>,
}
Expand Down Expand Up @@ -43,7 +45,7 @@ pub async fn progress(
let TorrentListView {
counter,
filtered_list,
} = TorrentListView::apply_request(view_request, &app_state).await?;
} = TorrentListView::apply_request(view_request.clone(), &app_state).await?;

// If only one torrent is inspected, display the content files
let files = if filtered_list.len() == 1 {
Expand All @@ -59,6 +61,7 @@ pub async fn progress(

Ok(TorrentListTemplate {
state: app_state_context,
filter: view_request,
torrent_list: TorrentListContext {
counter,
files,
Expand Down
4 changes: 2 additions & 2 deletions src/state/free_space.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ pub enum FreeSpaceError {
/// Uses (vendored) uu_df from uutils under the hood.
pub struct FreeSpace {
/// Number of remaining GiB.
free_space_gib: u64,
pub free_space_gib: u64,
/// Number of total GiB.
total_space_gib: u64,
/// Percentage of remaining available space.
free_space_percent: u64,
pub free_space_percent: u64,
}

impl FreeSpace {
Expand Down
85 changes: 41 additions & 44 deletions templates/base.html
Original file line number Diff line number Diff line change
@@ -1,47 +1,44 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{% block title %}TorrentManager{% endblock %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/assets/css/bulma.min.css">
<link rel="stylesheet" href="/assets/css/fork-awesome.min.css">
<link rel="icon" type="image/x-icon" href="/assets/images/favicon.ico">
{% block extra_head %}{% endblock extra_head %}
</head>
<body>
{% include "menus/header.html" %}
<head>
<meta charset="utf-8">
<title>{% block title %}TorrentManager{% endblock %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/assets/css/bootstrap.css">
<link rel="stylesheet" href="/assets/css/main.css">

<section class="section" style="padding: 0;">
<div class="container">
{% for error in state.errors %}
<div class="notification is-danger">
<details>
<summary>{{ error }}</summary>
<ul class="ml-5">
{% for inner_error in error.inner_errors() %}
<li>→ {{ inner_error }}</li>
{% endfor %}
</ul>
</details>
</div>
{% endfor %}
</div>
{% block main %}{% endblock %}
</section>
<footer class="mb-6">
{% include "menus/footer.html" %}
{% block footer_extra %}
<div class="container is-centered has-text-centered">
{% if let Some(user) = user %}
<h3 style="font-weight: bolder;">Logged in as {{ user|safe }}</h3>
{% else %}
<h3 style="font-weight: bolder;">Not logged in.</h3>
{% endif %}
<h3 style="font-weight: bolder;">Free space: {{ state.free_space }}</h3>
<h3 style="font-weight: bolder;">Page generated in __GENERATION_TIME__</h3>
</div>
{% endblock %}
</footer>
</body>
<link rel="stylesheet" href="/assets/css/fork-awesome.min.css">
<link rel="icon" type="image/x-icon" href="/assets/images/favicon.ico">
{% block extra_head %}{% endblock extra_head %}
</head>

<body class="d-flex flex-column min-vh-100">
{% include "menus/header.html" %}

<section class="section" style="padding-top: 100px;">
<div class="container">
{% for error in state.errors %}
<div class="notification is-danger">
<details>
<summary>{{ error }}</summary>
<ul class="ml-5">
{% for inner_error in error.inner_errors() %}
<li>→ {{ inner_error }}</li>
{% endfor %}
</ul>
</details>
</div>
{% endfor %}
</div>

{% block main %}{% endblock %}
</section>

<footer class="text-center mt-auto pt-4">
{% block footer_extra %}
{% include "menus/footer.html" %}
{% endblock %}
</footer>

<script src="/assets/javascript/bootstrap.min.js" crossorigin="anonymous"></script>
</body>
</html>
66 changes: 44 additions & 22 deletions templates/index.html
Original file line number Diff line number Diff line change
@@ -1,28 +1,50 @@
{% extends "base.html" %}

{% block main %}
<h1 class="title" style="text-align: center;">TorrentManager</h1>
<form method="POST" action="/upload" enctype="multipart/form-data" accept-charset="utf-8">
<div class="box is-flex is-justify-content-center is-align-items-center" style="margin: 1rem auto; text-align: center; max-width: 512px; gap: 20px; border: solid 1px black;">
<h2>Category:</h2>
<div class="select is-centered">
<select name="collection">{#{% set post_tag = post.tag %} TODO #}
<option disabled {#{% if post_tag is not defined %}selected{% endif %}#}>Genre</option>
{% for tag in state.categories %}
<option {#{% if post_tag == tag %}selected {% endif %}#}value="{{ tag }}">{{ tag }}</option>
{% endfor %}
</select>
</div>
</div>
<br>
<div class="container">
<h1>Download files</h1>

{% if state.free_space.free_space_percent < 5 %}
<div class="alert alert-warning mt-4">
The disks are almost full : <span class="fw-bold">{{ state.free_space.free_space_gib }} GiB left</span>
</div>
{% endif %}

<div class="columns is-centered" style="gap: 10px; margin-bottom: 0;">
{% include "sources/magnet.html" %}
{% include "sources/torrent.html" %}
</div>
<form method="POST" action="/upload" enctype="multipart/form-data" accept-charset="utf-8">
<div class="card mt-4">
<div class="card-body">
<div class="form-group">
<label for="collection" class="form-label mb-0">Category</label>
<div id="collection" class="form-text mb-1">Choose the type of content you want to download.</div>
<select class="form-select" aria-label="Choose the type of content you want to download" name="collection">{#{% set post_tag = post.tag %} TODO #}
<option disabled {#{% if post_tag is not defined %}selected{% endif %}#}>Genre</option>
{% for tag in state.categories %}
<option {#{% if post_tag == tag %}selected {% endif %}#}value="{{ tag }}">{{ tag }}</option>
{% endfor %}
</select>
</div>
</div>
</div>

<div class="control" style="text-align: center;">
<input type="Submit" class="button is-success" value="Next">
</div>
</form>
<div class="row mt-4">
<div class="col-sm">
{% include "sources/magnet.html" %}
</div>

<div class="col-md-1 mt-3 mt-sm-0">
<div class="d-flex align-items-center h-100 justify-content-center">
<h2>OR</h2>
</div>
</div>

<div class="col-sm mt-3 mt-sm-0">
{% include "sources/torrent.html" %}
</div>
</div>

<div class="text-center mt-4">
<input type="Submit" class="btn btn-success btn-lg" value="Next">
</div>
</form>
</div>
{% endblock %}
25 changes: 12 additions & 13 deletions templates/menus/footer.html
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
<nav class="navbar fa-2x" role="navigation" aria-label="main navigation" style="max-width: 900px; margin: auto; justify-content: center; height: 6rem;">
<div class="navbar-menu" style="flex-grow: 0;">
<a class="navbar-item" href="/progress/everything">
<span class="icon-text">
<span class="icon"><i class="fa fa-spinner"></i></span><span>Progress</span>
</span>
</a>
<a class="navbar-item" href="/logs">
<span class="icon-text">
<span class="icon"><i class="fa fa-file-text-o"></i></span><span>Logs</span>
</span>
</a>
<div class="container text-center mt-4">
<div class="d-flex flex-column flex-sm-row gap-0 gap-sm-3 justify-content-between">
{% if let Some(user) = user %}
</p>Logged in as {{ user|safe }}</h3>
{% else %}
<p style="font-weight: bolder;">Not logged in.</p>
{% endif %}

<p>Free space: <span class="fw-bold">{{ state.free_space }}</span></p>
<p>Page generated in <span class="fw-bold">__GENERATION_TIME__</span></p>
</div>
</nav>
</div>

Loading