Fix download client type being sent as number instead of string (#245)

This commit is contained in:
Flaminel
2025-07-27 14:23:48 +03:00
committed by GitHub
parent 72855bc030
commit de06d1c2d3
7 changed files with 64 additions and 86 deletions

View File

@@ -2,7 +2,7 @@
public enum DownloadClientTypeName
{
QBittorrent,
qBittorrent,
Deluge,
Transmission,
}

View File

@@ -46,7 +46,7 @@ public sealed class DownloadServiceFactory
return downloadClientConfig.TypeName switch
{
DownloadClientTypeName.QBittorrent => CreateQBitService(downloadClientConfig),
DownloadClientTypeName.qBittorrent => CreateQBitService(downloadClientConfig),
DownloadClientTypeName.Deluge => CreateDelugeService(downloadClientConfig),
DownloadClientTypeName.Transmission => CreateTransmissionService(downloadClientConfig),
_ => throw new NotSupportedException($"Download client type {downloadClientConfig.TypeName} is not supported")

View File

@@ -84,7 +84,7 @@ export class DocumentationService {
'download-client': {
'enabled': 'enable-download-client',
'name': 'client-name',
'type': 'client-type',
'typeName': 'client-type',
'host': 'client-host',
'urlBase': 'url-base-path',
'username': 'username',

View File

@@ -147,21 +147,21 @@
<div class="field">
<label for="client-type">
<i class="pi pi-info-circle field-info-icon"
(click)="openFieldDocs('type')"
(click)="openFieldDocs('typeName')"
pTooltip="Click for documentation"></i>
Client Type *
</label>
<p-select
id="client-type"
formControlName="type"
[options]="clientTypeOptions"
formControlName="typeName"
[options]="typeNameOptions"
optionLabel="label"
optionValue="value"
placeholder="Select client type"
appendTo="body"
class="w-full"
></p-select>
<small *ngIf="hasError(clientForm, 'type', 'required')" class="p-error">Client type is required</small>
<small *ngIf="hasError(clientForm, 'typeName', 'required')" class="p-error">Client type is required</small>
</div>
<ng-container>

View File

@@ -5,7 +5,7 @@ import { Subject, takeUntil } from "rxjs";
import { DownloadClientConfigStore } from "./download-client-config.store";
import { CanComponentDeactivate } from "../../core/guards";
import { ClientConfig, DownloadClientConfig, CreateDownloadClientDto } from "../../shared/models/download-client-config.model";
import { DownloadClientType } from "../../shared/models/enums";
import { DownloadClientType, DownloadClientTypeName } from "../../shared/models/enums";
import { DocumentationService } from "../../core/services/documentation.service";
// PrimeNG Components
@@ -56,11 +56,7 @@ export class DownloadClientSettingsComponent implements OnDestroy, CanComponentD
editingClient: ClientConfig | null = null;
// Download client type options
clientTypeOptions = [
{ label: "qBittorrent", value: DownloadClientType.QBittorrent },
{ label: "Deluge", value: DownloadClientType.Deluge },
{ label: "Transmission", value: DownloadClientType.Transmission },
];
typeNameOptions: { label: string, value: DownloadClientTypeName }[] = [];
// Clean up subscriptions
private destroy$ = new Subject<void>();
@@ -89,7 +85,7 @@ export class DownloadClientSettingsComponent implements OnDestroy, CanComponentD
// Initialize client form for modal
this.clientForm = this.formBuilder.group({
name: ['', Validators.required],
type: [null, Validators.required],
typeName: [null, Validators.required],
host: ['', [Validators.required, this.uriValidator.bind(this)]],
username: [''],
password: [''],
@@ -97,11 +93,19 @@ export class DownloadClientSettingsComponent implements OnDestroy, CanComponentD
enabled: [true]
});
// Initialize type name options
for (const key of Object.keys(DownloadClientTypeName)) {
this.typeNameOptions.push({
label: key,
value: DownloadClientTypeName[key as keyof typeof DownloadClientTypeName]
});
}
// Load Download Client config data
this.downloadClientStore.loadConfig();
// Setup client type change handler
this.clientForm.get('type')?.valueChanges
this.clientForm.get('typeName')?.valueChanges
.pipe(takeUntil(this.destroy$))
.subscribe(() => {
this.onClientTypeChange();
@@ -184,14 +188,9 @@ export class DownloadClientSettingsComponent implements OnDestroy, CanComponentD
this.modalMode = 'edit';
this.editingClient = client;
// Map backend type to frontend type
const frontendType = client.typeName
? this.mapClientTypeFromBackend(client.typeName)
: client.type;
this.clientForm.patchValue({
name: client.name,
type: frontendType,
typeName: client.typeName,
host: client.host,
username: client.username,
password: client.password,
@@ -222,28 +221,27 @@ export class DownloadClientSettingsComponent implements OnDestroy, CanComponentD
}
const formValue = this.clientForm.value;
const mappedType = this.mapClientTypeForBackend(formValue.type);
const clientData: CreateDownloadClientDto = {
name: formValue.name,
typeName: mappedType.typeName,
type: mappedType.type,
host: formValue.host,
username: formValue.username,
password: formValue.password,
urlBase: formValue.urlBase,
enabled: formValue.enabled
};
if (this.modalMode === 'add') {
const clientData: CreateDownloadClientDto = {
name: formValue.name,
type: this.mapTypeNameToType(formValue.typeName),
typeName: formValue.typeName,
host: formValue.host,
username: formValue.username,
password: formValue.password,
urlBase: formValue.urlBase,
enabled: formValue.enabled
};
this.downloadClientStore.createClient(clientData);
} else if (this.editingClient) {
// For updates, create a proper ClientConfig object
const clientConfig: ClientConfig = {
id: this.editingClient.id!,
id: this.editingClient.id,
name: formValue.name,
type: formValue.type, // Keep the frontend enum type
typeName: mappedType.typeName,
type: this.mapTypeNameToType(formValue.typeName),
typeName: formValue.typeName,
host: formValue.host,
username: formValue.username,
password: formValue.password,
@@ -325,42 +323,24 @@ export class DownloadClientSettingsComponent implements OnDestroy, CanComponentD
}
/**
* Map frontend client type to backend TypeName and Type
* Map typeName to type category
*/
private mapClientTypeForBackend(frontendType: DownloadClientType): { typeName: string, type: string } {
switch (frontendType) {
case DownloadClientType.QBittorrent:
return { typeName: 'qBittorrent', type: 'Torrent' };
case DownloadClientType.Deluge:
return { typeName: 'Deluge', type: 'Torrent' };
case DownloadClientType.Transmission:
return { typeName: 'Transmission', type: 'Torrent' };
private mapTypeNameToType(typeName: DownloadClientTypeName): DownloadClientType {
switch (typeName) {
case DownloadClientTypeName.qBittorrent:
case DownloadClientTypeName.Deluge:
case DownloadClientTypeName.Transmission:
return DownloadClientType.Torrent;
default:
return { typeName: 'QBittorrent', type: 'Torrent' };
throw new Error(`Unknown client type name: ${typeName}`);
}
}
/**
* Map backend TypeName to frontend client type
*/
private mapClientTypeFromBackend(backendTypeName: string): DownloadClientType {
switch (backendTypeName) {
case 'QBittorrent':
return DownloadClientType.QBittorrent;
case 'Deluge':
return DownloadClientType.Deluge;
case 'Transmission':
return DownloadClientType.Transmission;
default:
return DownloadClientType.QBittorrent;
}
}
/**
* Handle client type changes to update validation
*/
onClientTypeChange(): void {
const clientType = this.clientForm.get('type')?.value;
const clientTypeName = this.clientForm.get('typeName')?.value;
const hostControl = this.clientForm.get('host');
const usernameControl = this.clientForm.get('username');
const urlBaseControl = this.clientForm.get('urlBase');
@@ -373,13 +353,13 @@ export class DownloadClientSettingsComponent implements OnDestroy, CanComponentD
]);
// Clear username value and remove validation for Deluge
if (clientType === DownloadClientType.Deluge) {
if (clientTypeName === DownloadClientTypeName.Deluge) {
usernameControl.setValue('');
usernameControl.clearValidators();
}
// Set default URL base for Transmission
if (clientType === DownloadClientType.Transmission) {
if (clientTypeName === DownloadClientTypeName.Transmission) {
urlBaseControl.setValue('transmission');
}
@@ -392,19 +372,15 @@ export class DownloadClientSettingsComponent implements OnDestroy, CanComponentD
* Check if username field should be shown (hidden for Deluge)
*/
shouldShowUsernameField(): boolean {
const clientType = this.clientForm.get('type')?.value;
return clientType !== DownloadClientType.Deluge;
const clientTypeName = this.clientForm.get('typeName')?.value;
return clientTypeName !== DownloadClientTypeName.Deluge;
}
/**
* Get client type label for display
*/
getClientTypeLabel(client: ClientConfig): string {
const frontendType = client.typeName
? this.mapClientTypeFromBackend(client.typeName)
: client.type;
const option = this.clientTypeOptions.find(opt => opt.value === frontendType);
const option = this.typeNameOptions.find(opt => opt.value === client.typeName);
return option?.label || 'Unknown';
}

View File

@@ -1,4 +1,4 @@
import { DownloadClientType } from './enums';
import { DownloadClientType, DownloadClientTypeName } from './enums';
/**
* Represents a download client configuration object
@@ -37,7 +37,7 @@ export interface ClientConfig {
/**
* Type name of download client (backend enum)
*/
typeName?: string;
typeName: DownloadClientTypeName;
/**
* Host address for the download client
@@ -73,16 +73,16 @@ export interface CreateDownloadClientDto {
* Friendly name for this client
*/
name: string;
/**
* Type of download client (backend enum)
*/
type: DownloadClientType;
/**
* Type name of download client (backend enum)
*/
typeName: string;
/**
* Type of download client (backend enum)
*/
type: string;
typeName: DownloadClientTypeName;
/**
* Host address for the download client

View File

@@ -1,8 +1,10 @@
/**
* Download client type enum matching backend DownloadClientType
*/
export enum DownloadClientType {
QBittorrent = 0,
Deluge = 1,
Transmission = 2,
Torrent = "Torrent",
Usenet = "Usenet",
}
export enum DownloadClientTypeName {
qBittorrent = "qBittorrent",
Deluge = "Deluge",
Transmission = "Transmission",
}