12 Commits
0.2.0 ... 1.0.2

Author SHA1 Message Date
ahjephson
78e62f31d0 Merge branch 'hotfix/1.0.2' 2025-03-22 13:52:33 +00:00
ahjephson
e23842fcb0 Fix invalid exception being caught. 2025-03-22 13:51:44 +00:00
ahjephson
4098f8f5a9 Merge branch 'hotfix/1.0.1' 2025-02-10 08:57:01 +00:00
ahjephson
12f81c5978 Fix issue with TorrentActions treating actions as all downloaded. 2025-02-10 08:55:46 +00:00
ahjephson
717738d720 Update readme.md 2025-02-07 13:24:25 +00:00
ahjephson
885c34c8cf Update readme.md 2025-02-07 13:10:15 +00:00
ahjephson
ef3c68a6aa Merge tag '1.0.0' into develop
1.0.0
2025-02-07 13:02:16 +00:00
ahjephson
a29e64fc1b Merge branch 'release/1.0.0' 2025-02-07 13:01:36 +00:00
ahjephson
e55955c75e Fix small screen issues 2025-02-07 11:49:37 +00:00
ahjephson
aa80396862 Update dotnet.yml 2025-02-07 09:53:01 +00:00
ahjephson
30ced3293c Update dotnet.yml 2025-02-07 09:52:18 +00:00
ahjephson
c54f73a517 Merge tag '0.2.0' into develop
0.2.0
2025-02-07 09:49:18 +00:00
13 changed files with 198 additions and 111 deletions

View File

@@ -26,7 +26,7 @@ jobs:
- name: Install GitVersion
uses: gittools/actions/gitversion/setup@v3.0.0
with:
versionSpec: '6.x'
versionSpec: '6.0.0'
- name: Determine Version
id: gitversion

View File

@@ -2,6 +2,7 @@
<MudMenuItem Icon="@Icons.Material.Filled.DriveFileRenameOutline" OnClick="RenameFileContextMenu">Rename</MudMenuItem>
</ContextMenu>
<div style="overflow-x: auto; white-space: nowrap; width: 100%;">
<MudToolBar Gutters="false" Dense="true">
<MudIconButton Icon="@Icons.Material.Filled.DriveFileRenameOutline" OnClick="RenameFileToolbar" title="Rename" />
<MudDivider Vertical="true" />
@@ -22,6 +23,7 @@
<MudSpacer />
<MudTextField T="string" Value="SearchText" ValueChanged="SearchTextChanged" Immediate="true" DebounceInterval="500" Placeholder="Filter file list" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"></MudTextField>
</MudToolBar>
</div>
<DynamicTable
@ref="Table"

View File

@@ -441,7 +441,7 @@ namespace Lantean.QBTMud.Components
thereAreFirstLastPiecePrio = true;
}
if (torrent.Progress > 0.999999) // not downloaded
if (torrent.Progress < 0.999999) // not downloaded
{
allAreDownloaded = false;
}

View File

@@ -37,7 +37,7 @@ namespace Lantean.QBTMud.Helpers
{
time = TimeSpan.FromSeconds(seconds.Value);
}
catch (OverflowException)
catch
{
return "∞";
}

View File

@@ -36,23 +36,23 @@
</CascadingValue>
</CascadingValue>
</CascadingValue>
<MudAppBar Bottom="true" Fixed="true" Elevation="0" Dense="true" Style="background-color: var(--mud-palette-dark-lighten);">
<MudAppBar Bottom="true" Fixed="true" Elevation="0" Dense="true" Style="background-color: var(--mud-palette-dark-lighten); z-index: 900">
@if (MainData?.LostConnection == true)
{
<MudText Class="mx-2 mb-1" Color="Color.Error">qBittorrent client is not reachable</MudText>
<MudText Class="mx-2 mb-1 d-none d-sm-flex" Color="Color.Error">qBittorrent client is not reachable</MudText>
}
<MudSpacer />
<MudText Class="mx-2 mb-1">@DisplayHelpers.Size(MainData?.ServerState.FreeSpaceOnDisk, "Free space: ")</MudText>
<MudDivider Vertical="true" />
<MudText Class="mx-2 mb-1">DHT @(MainData?.ServerState.DHTNodes ?? 0) nodes</MudText>
<MudDivider Vertical="true" />
<MudText Class="mx-2 mb-1 d-none d-sm-flex">@DisplayHelpers.Size(MainData?.ServerState.FreeSpaceOnDisk, "Free space: ")</MudText>
<MudDivider Vertical="true" Class="d-none d-sm-flex" />
<MudText Class="mx-2 mb-1 d-none d-sm-flex">DHT @(MainData?.ServerState.DHTNodes ?? 0) nodes</MudText>
<MudDivider Vertical="true" Class="d-none d-sm-flex" />
@{
var (icon, colour) = GetConnectionIcon(MainData?.ServerState.ConnectionStatus);
}
<MudIcon Class="mx-1 mb-1" Icon="@icon" Color="@colour" Title="MainData?.ServerState.ConnectionStatus" />
<MudDivider Vertical="true" />
<MudDivider Vertical="true" Class="" />
<MudIcon Class="mx-1 mb-1" Icon="@Icons.Material.Outlined.Speed" Color="@((MainData?.ServerState.UseAltSpeedLimits ?? false) ? Color.Error : Color.Success)" />
<MudDivider Vertical="true" />
<MudDivider Vertical="true" Class="" />
<MudIcon Class="ml-1 mb-1" Icon="@Icons.Material.Filled.KeyboardDoubleArrowDown" Color="Color.Success" />
<MudText Class="mr-1 mb-1">
@DisplayHelpers.Size(MainData?.ServerState.DownloadInfoSpeed, null, "/s")

View File

@@ -23,16 +23,15 @@
<MudSwitch T="bool" Label="Dark Mode" LabelPlacement="Placement.End" Value="IsDarkMode" ValueChanged="DarkModeChanged" Class="pl-3" />
<Menu @ref="Menu" />
</MudAppBar>
@if (IsDebug)
{
<MudDrawer Open="ErrorDrawerOpen" ClipMode="DrawerClipMode.Docked" Elevation="2" Anchor="Anchor.Right">
<ErrorDisplay ErrorBoundary="ErrorBoundary" />
</MudDrawer>
}
<MudDrawer @bind-Open="ErrorDrawerOpen" ClipMode="DrawerClipMode.Docked" Elevation="2" Anchor="Anchor.Right">
<ErrorDisplay ErrorBoundary="ErrorBoundary" />
</MudDrawer>
<CascadingValue Value="Theme">
<CascadingValue Value="IsDarkMode" Name="IsDarkMode">
<CascadingValue Value="Menu">
@Body
<CascadingValue Value="DrawerOpen" Name="DrawerOpen">
@Body
</CascadingValue>
</CascadingValue>
</CascadingValue>
</CascadingValue>

View File

@@ -44,12 +44,6 @@ namespace Lantean.QBTMud.Layout
protected MudTheme Theme { get; set; }
#if DEBUG
private bool IsDebug { get; } = true;
#else
private bool IsDebug { get; } = false;
#endif
public MainLayout()
{
Theme = new MudTheme();
@@ -96,9 +90,9 @@ namespace Lantean.QBTMud.Layout
return Task.CompletedTask;
}
public Task NotifyBrowserViewportChangeAsync(BrowserViewportEventArgs browserViewportEventArgs)
public async Task NotifyBrowserViewportChangeAsync(BrowserViewportEventArgs browserViewportEventArgs)
{
if (browserViewportEventArgs.Breakpoint == Breakpoint.Sm && DrawerOpen)
if (browserViewportEventArgs.Breakpoint <= Breakpoint.Sm)
{
DrawerOpen = false;
}
@@ -107,7 +101,17 @@ namespace Lantean.QBTMud.Layout
DrawerOpen = true;
}
return Task.CompletedTask;
if (ErrorBoundary?.Errors.Count > 0)
{
ErrorDrawerOpen = true;
}
else
{
await Task.Delay(250);
ErrorDrawerOpen = false;
}
await InvokeAsync(StateHasChanged);
}
protected void ToggleErrorDrawer()

View File

@@ -12,89 +12,96 @@
<MudTabs Elevation="2" ApplyEffectsToContainer="true">
<MudTabPanel Text="About">
<div class="d-flex gap-4">
<MudImage Src="images/mascot.png" Alt="Mascot" Class="ma-6" Fluid ObjectFit="ObjectFit.None" ObjectPosition="ObjectPosition.LeftTop" Height="162" Width="94" />
<MudGrid Class="mx-0 mt-0 mb-3">
<MudItem xs="12">
<div class="d-flex gap-3">
<MudImage Src="images/qbittorrent32.png" Fluid ObjectFit="ObjectFit.None" Alt="QBT" Height="32" Width="32" /><MudText Typo="Typo.h6">qBittorrent @QBittorrentVersion</MudText>
<MudContainer MaxWidth="MaxWidth.ExtraExtraLarge" Class="mt-3">
<MudGrid Class="mt-0 mb-4">
<MudItem xs="12" sm="3" md="2" lg="2" xl="1" Class="d-flex justify-center">
<MudImage Src="images/mascot.png" Alt="Mascot" Class="ma-6"
Fluid ObjectFit="ObjectFit.None" ObjectPosition="ObjectPosition.LeftTop"
Height="162" Width="94" />
</MudItem>
<MudItem xs="12" sm="9" md="10" lg="10" xl="11">
<div class="d-flex flex-column gap-2">
<div class="d-flex gap-3 align-items-center">
<MudImage Src="images/qbittorrent32.png" Fluid ObjectFit="ObjectFit.None"
Alt="QBT" Height="32" Width="32" />
<MudText Typo="Typo.h6">qBittorrent @QBittorrentVersion</MudText>
</div>
<MudText Typo="Typo.body1">
An advanced BitTorrent client programmed in C++, based on Qt toolkit and libtorrent-rasterbar.
</MudText>
<MudText Typo="Typo.body1">Copyright © 2006-2024 The qBittorrent project</MudText>
<div class="d-flex flex-wrap">
<MudText Typo="Typo.body1" Class="fw-bold">Home Page: </MudText>
<MudLink Href="https://www.qbittorrent.org" Target="_blank" Class="ms-2">
qbittorrent.org
</MudLink>
</div>
<div class="d-flex flex-wrap">
<MudText Typo="Typo.body1" Class="fw-bold">Bug Tracker: </MudText>
<MudLink Href="https://bugs.qbittorrent.org" Target="_blank" Class="ms-2">
bugs.qbittorrent.org
</MudLink>
</div>
<div class="d-flex flex-wrap">
<MudText Typo="Typo.body1" Class="fw-bold">Forum: </MudText>
<MudLink Href="https://forum.qbittorrent.org" Target="_blank" Class="ms-2">
forum.qbittorrent.org
</MudLink>
</div>
</div>
</MudItem>
<MudItem xs="12">
<MudText Typo="Typo.body1">An advanced BitTorrent client programmed in C++, based on Qt toolkit and libtorrent-rasterbar.</MudText>
</MudItem>
<MudItem xs="12">
<MudText Typo="Typo.body1">Copyright © 2006-2024 The qBittorrent project</MudText>
</MudItem>
<MudItem xs="2">
<MudText Typo="Typo.body1">Home Page</MudText>
</MudItem>
<MudItem xs="10">
<MudLink Href="https://www.qbittorrent.org" Target="https://www.qbittorrent.org">https://www.qbittorrent.org</MudLink>
</MudItem>
<MudItem xs="2">
<MudText Typo="Typo.body1">Bug Tracker</MudText>
</MudItem>
<MudItem xs="10">
<MudLink Href="https://bugs.qbittorrent.org" Target="https://bugs.qbittorrent.org">https://bugs.qbittorrent.org</MudLink>
</MudItem>
<MudItem xs="2">
<MudText Typo="Typo.body1">Forum</MudText>
</MudItem>
<MudItem xs="10">
<MudLink Href="https://forum.qbittorrent.org" Target="https://forum.qbittorrent.org">https://forum.qbittorrent.org</MudLink>
</MudItem>
</MudGrid>
</div>
</MudContainer>
</MudTabPanel>
<MudTabPanel Text="Authors">
<MudContainer MaxWidth="MaxWidth.ExtraExtraLarge" Class="mt-3">
<MudText Typo="Typo.body1" Class="py-1">Current maintainer</MudText>
<MudText Typo="Typo.h5" Class="py-1">Current maintainer</MudText>
<MudGrid Class="mt-0 mb-4">
<MudItem xs="2">
<MudText Typo="Typo.body1">Name</MudText>
<MudItem xs="12" md="2">
<MudText Typo="Typo.h6">Name</MudText>
</MudItem>
<MudItem xs="10">
<MudItem xs="12" md="10">
<MudText Typo="Typo.body1">Sledgehammer999</MudText>
</MudItem>
<MudItem xs="2">
<MudText Typo="Typo.body1">Nationality</MudText>
<MudItem xs="12" md="2">
<MudText Typo="Typo.h6">Nationality</MudText>
</MudItem>
<MudItem xs="10">
<MudItem xs="12" md="10">
<MudText Typo="Typo.body1">Greece</MudText>
</MudItem>
<MudItem xs="2">
<MudText Typo="Typo.body1">E-mail</MudText>
<MudItem xs="12" md="2">
<MudText Typo="Typo.h6">E-mail</MudText>
</MudItem>
<MudItem xs="10">
<MudItem xs="12" md="10">
<MudLink Href="mailto:sledgehammer999@qbittorrent.org">sledgehammer999@qbittorrent.org</MudLink>
</MudItem>
</MudGrid>
<MudText Typo="Typo.body1" Class="py-1">Original author</MudText>
<MudText Typo="Typo.h5" Class="py-1">Original author</MudText>
<MudGrid Class="mt-0 mb-4">
<MudItem xs="2">
<MudText Typo="Typo.body1">Name</MudText>
<MudItem xs="12" md="2">
<MudText Typo="Typo.h6">Name</MudText>
</MudItem>
<MudItem xs="10">
<MudItem xs="12" md="10">
<MudText Typo="Typo.body1">Christophe Dumez</MudText>
</MudItem>
<MudItem xs="2">
<MudText Typo="Typo.body1">Nationality</MudText>
<MudItem xs="12" md="2">
<MudText Typo="Typo.h6">Nationality</MudText>
</MudItem>
<MudItem xs="10">
<MudItem xs="12" md="10">
<MudText Typo="Typo.body1">France</MudText>
</MudItem>
<MudItem xs="2">
<MudText Typo="Typo.body1">E-mail</MudText>
<MudItem xs="12" md="2">
<MudText Typo="Typo.h6">E-mail</MudText>
</MudItem>
<MudItem xs="10">
<MudItem xs="12" md="10">
<MudLink Href="mailto:chris@qbittorrent.org">chris@qbittorrent.org</MudLink>
</MudItem>
</MudGrid>
@@ -118,7 +125,7 @@
(the list might not be up to date)
</MudText>
<MudList T="string" ReadOnly>
<MudListItem Icon="@Icons.Material.Filled.Circle" IconColor="Color.Info"><u>Arabic:</u> SDERAWI (abz8868@msn.com), sn51234 (nesseyan@gmail.com) and Ibrahim Saed ibraheem_alex(Transifex)</MudListItem>
<MudListItem Icon="@Icons.Material.Filled.Circle"><u>Arabic:</u> SDERAWI (abz8868@msn.com), sn51234 (nesseyan@gmail.com) and Ibrahim Saed ibraheem_alex(Transifex)</MudListItem>
<MudListItem Icon="@Icons.Material.Filled.Circle"><u>Armenian:</u> Hrant Ohanyan (hrantohanyan@mail.am)</MudListItem>
<MudListItem Icon="@Icons.Material.Filled.Circle"><u>Basque:</u> Xabier Aramendi (azpidatziak@gmail.com)</MudListItem>
<MudListItem Icon="@Icons.Material.Filled.Circle"><u>Belarusian:</u> Mihas Varantsou (meequz@gmail.com)</MudListItem>
@@ -1058,38 +1065,38 @@
<MudText Typo="Typo.body1" Class="py-1">qBittorrent was built with the following libraries:</MudText>
<MudGrid Class="mt-1 mb-4">
<MudItem xs="2">
<MudItem xs="3">
<MudText Typo="Typo.body1">Qt</MudText>
</MudItem>
<MudItem xs="10">
<MudItem xs="9">
<MudText Typo="Typo.body1">@QtVersion</MudText>
</MudItem>
<MudItem xs="2">
<MudItem xs="3">
<MudText Typo="Typo.body1">Libtorrent</MudText>
</MudItem>
<MudItem xs="10">
<MudItem xs="9">
<MudText Typo="Typo.body1">@LibtorrentVersion</MudText>
</MudItem>
<MudItem xs="2">
<MudItem xs="3">
<MudText Typo="Typo.body1">Boost</MudText>
</MudItem>
<MudItem xs="10">
<MudItem xs="9">
<MudText Typo="Typo.body1">@BoostVersion</MudText>
</MudItem>
<MudItem xs="2">
<MudItem xs="3">
<MudText Typo="Typo.body1">OpenSSL</MudText>
</MudItem>
<MudItem xs="10">
<MudItem xs="9">
<MudText Typo="Typo.body1">@OpensslVersion</MudText>
</MudItem>
<MudItem xs="2">
<MudItem xs="3">
<MudText Typo="Typo.body1">zlib</MudText>
</MudItem>
<MudItem xs="10">
<MudItem xs="9">
<MudText Typo="Typo.body1">@ZlibVersion</MudText>
</MudItem>
</MudGrid>

View File

@@ -1,6 +1,7 @@
@page "/details/{hash}"
@layout DetailsLayout
<div style="overflow-x: auto; white-space: nowrap; width: 100%;">
<MudToolBar Gutters="false" Dense="true">
@if (!DrawerOpen)
{
@@ -14,6 +15,7 @@
<MudDivider Vertical="true" />
<MudText Class="pl-5 no-wrap">@Name</MudText>
</MudToolBar>
</div>
@if (ShowTabs)
{

View File

@@ -18,7 +18,7 @@
<MudItem xs="12" md="4">
<MudTextField T="string" Label="Criteria" @bind-Value="Model.SearchText" Variant="Variant.Outlined" />
</MudItem>
<MudItem xs="2" md="3">
<MudItem xs="12" md="3">
<MudSelect T="string" Label="Categories" @bind-Value="Model.SelectedCategory" Variant="Variant.Outlined">
@foreach (var (value, name) in Categories)
{
@@ -30,17 +30,21 @@
}
</MudSelect>
</MudItem>
<MudItem xs="2" md="3">
<MudItem xs="12" md="3">
<MudSelect T="string" Label="Plugins" @bind-Value="Model.SelectedPlugin" Variant="Variant.Outlined">
<MudSelectItem Value="@("all")">All</MudSelectItem>
<MudDivider />
@if (Plugins.Count > 0)
{
<MudDivider />
}
@foreach (var (value, name) in Plugins)
{
<MudSelectItem Value="value">@name</MudSelectItem>
}
</MudSelect>
</MudItem>
<MudItem xs="2" md="2">
<MudItem xs="12" md="2">
<MudButton ButtonType="ButtonType.Submit" FullWidth="true" Color="Color.Primary" EndIcon="@Icons.Material.Filled.Search" Variant="Variant.Filled" Class="mt-6">@(_searchId is null ? "Search" : "Stop")</MudButton>
</MudItem>

View File

@@ -7,6 +7,7 @@
<TorrentActions RenderType="RenderType.MenuItems" Hashes="GetContextMenuTargetHashes()" PrimaryHash="@(ContextMenuItem?.Hash)" Torrents="MainData.Torrents" Preferences="Preferences" />
</ContextMenu>
<div style="overflow-x: auto; white-space: nowrap; width: 100%;">
<MudToolBar Gutters="false" Dense="true">
<MudIconButton Icon="@Icons.Material.Outlined.AddLink" OnClick="AddTorrentLink" title="Add torrent link" />
<MudIconButton Icon="@Icons.Material.Outlined.AddCircle" OnClick="AddTorrentFile" title="Add torrent file" />
@@ -18,6 +19,7 @@
<MudSpacer />
<MudTextField Value="SearchText" TextChanged="SearchTextChanged" Immediate="true" DebounceInterval="1000" Placeholder="Filter torrent list" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"></MudTextField>
</MudToolBar>
</div>
<MudContainer MaxWidth="MaxWidth.ExtraExtraLarge" Class="ma-0 pa-0">
<DynamicTable

View File

@@ -155,7 +155,7 @@ code {
}
.torrent-list .mud-table-container {
height: calc(100vh - 149px);
height: calc(100vh - 160px);
}
.file-list .mud-table-container {
@@ -251,4 +251,8 @@ td .folder-button {
width: 25px;
max-width: 25px;
padding: 0 8px !important;
}
.mud-popover .mud-divider:last-child {
display: none;
}

View File

@@ -1,14 +1,77 @@
# qbt-mud
# qbtmud
## To-Do
qbtmud is a drop-in replacement for qBittorrent's default WebUI, implementing all of its functionality with a modern and user-friendly interface.
- Rename multiple files dialog
- ~~RSS feeds and dialogs~~
- ~~About~~
- ~~Context menu for files list/trackers list/peers list~~
- ~~Tag management page~~
- ~~Category management page~~
- ~~Update all tables to use DynamicTable~~
- ~~Log~~
- ~~Blocks~~
- ~~Search~~
## Features
qbtmud replicates all core features of the qBittorrent WebUI, including:
- **Torrent Management** Add, remove, and control torrents.
- **Tracker Control** View and manage trackers.
- **Peer Management** Monitor and manage peers connected to torrents.
- **File Prioritization** Select and prioritize specific files within a torrent.
- **Speed Limits** Set global and per-torrent speed limits.
- **RSS Integration** Subscribe to RSS feeds for automated torrent downloads.
- **Search Functionality** Integrated torrent search.
- **Sequential Downloading** Download files in order for media streaming.
- **Super Seeding Mode** Efficiently distribute torrents as an initial seeder.
- **IP Filtering** Improve security by filtering specific IP addresses.
- **IPv6 Support** Full support for IPv6 networks.
- **Bandwidth Scheduler** Schedule bandwidth limits.
- **WebUI Access** Remotely manage torrents through the WebUI.
For a detailed explanation of these features, refer to the [qBittorrent Options Guide](https://github.com/qbittorrent/qBittorrent/wiki/Explanation-of-Options-in-qBittorrent).
---
## Installation
To install qbtmud without building from source:
### 1. Download the Latest Release
- Go to the [qbtmud Releases](https://github.com/lantean-code/qbtmud/releases) page.
- Download the latest release archive for your operating system.
### 2. Extract the Archive
- Extract the contents of the downloaded archive to a directory of your choice.
### 3. Configure qBittorrent to Use qbtmud
- Open qBittorrent and navigate to `Tools` > `Options` > `Web UI`.
- Enable the option **"Use alternative WebUI"**.
- Set the **"Root Folder"** to the directory where you extracted qbtmud.
- Click **OK** to save the settings.
### 4. Access qbtmud
- Open your web browser and go to `http://localhost:8080` (or the port configured in qBittorrent).
For more detailed instructions, refer to the [Alternate WebUI Usage Guide](https://github.com/qbittorrent/qBittorrent/wiki/Alternate-WebUI-usage).
---
## Building from Source
To build qbtmud from source, you need to have the **.NET 9.0 SDK** installed on your system.
### 1. Clone the Repository
```sh
git clone https://github.com/lantean-code/qbtmud.git
cd qbtmud
```
### 2. Restore Dependencies
```sh
dotnet restore
```
### 3. Build the Application
```sh
dotnet build --configuration Release
```
### 4. Configure qBittorrent to Use qbtmud
Follow the same steps as in the **Installation** section to set qbtmud as your WebUI.
### 5. Run qbtmud
Navigate to the directory containing the built files and run the application using the appropriate command for your OS.
By following these steps, you can set up qbtmud to manage your qBittorrent server with an improved web interface, offering better functionality and usability.