Skip to content

Commit

Permalink
quality profile settings UI (working - need to add remove (wip)), add…
Browse files Browse the repository at this point in the history
… Observable return types, rename QualityProfile profile->quality
  • Loading branch information
lardbit committed Aug 12, 2024
1 parent 2acf44f commit e151826
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 48 deletions.
76 changes: 52 additions & 24 deletions src/frontend/src/app/api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export class ApiService {
API_URL_GENRES_MOVIE = '/api/genres/movie/';
API_URL_GENRES_TV = '/api/genres/tv/';
API_URL_MEDIA_CATEGORIES = '/api/media-categories/';
API_URL_QUALITIES = '/api/qualities/';
API_URL_QUALITY_PROFILES = '/api/quality-profile/';
API_URL_GIT_COMMIT = '/api/git-commit/';
API_URL_IMPORT_MEDIA_TV = '/api/import/media/tv/';
Expand All @@ -55,6 +56,7 @@ export class ApiService {
public userToken: string;
public users: any; // staff-only list of all users
public settings: any;
public qualities: string[] = [];
public qualityProfiles: any[] = [];
public mediaCategories: string[];
public watchTVSeasons: any[] = [];
Expand Down Expand Up @@ -180,6 +182,7 @@ export class ApiService {
}
})
),
this.fetchQualities(),
this.fetchQualityProfiles(),
this.fetchMediaCategories(),
]).pipe(
Expand Down Expand Up @@ -211,7 +214,7 @@ export class ApiService {
);
}

public fetchSettings() {
public fetchSettings(): Observable<any> {
return this.http.get(this.API_URL_SETTINGS, {headers: this._requestHeaders()}).pipe(
map((data: any) => {
if (data.length) {
Expand All @@ -224,7 +227,7 @@ export class ApiService {
);
}

public fetchMediaCategories() {
public fetchMediaCategories(): Observable<string[]> {
return this.http.get(this.API_URL_MEDIA_CATEGORIES, {headers: this._requestHeaders()}).pipe(
map((data: any) => {
if (data.mediaCategories) {
Expand All @@ -237,7 +240,20 @@ export class ApiService {
);
}

public fetchQualityProfiles() {
public fetchQualities(): Observable<string[]> {
return this.http.get(this.API_URL_QUALITIES, {headers: this._requestHeaders()}).pipe(
map((data: any) => {
if (data.length) {
this.qualities = data;
} else {
console.error('no qualities');
}
return this.qualities;
}),
);
}

public fetchQualityProfiles(): Observable<any[]> {
return this.http.get(this.API_URL_QUALITY_PROFILES, {headers: this._requestHeaders()}).pipe(
map((data: any) => {
if (data.length) {
Expand Down Expand Up @@ -303,7 +319,7 @@ export class ApiService {
);
}

public login(user: string, pass: string) {
public login(user: string, pass: string): Observable<any> {
const params = {
username: user,
password: pass,
Expand Down Expand Up @@ -334,15 +350,27 @@ export class ApiService {
);
}

public searchTorrents(query: string, mediaType: string) {
public updateQualityProfile(id: number, params: any): Observable<any> {
return this.http.patch(`${this.API_URL_QUALITY_PROFILES}${id}/`, params, {headers: this._requestHeaders()}).pipe(
map((data: any) => {
this.qualityProfiles.forEach((profile, index) => {
if (profile.id === id) {
this.qualityProfiles[index] = params;
}
})
}),
);
}

public searchTorrents(query: string, mediaType: string): Observable<any> {
return this.http.get(`${this.API_URL_SEARCH_TORRENTS}?q=${query}&media_type=${mediaType}`, {headers: this._requestHeaders()}).pipe(
map((data: any) => {
return data;
}),
);
}

public download(torrentResult: any, mediaType: string, tmdbMedia: any, params?: any) {
public download(torrentResult: any, mediaType: string, tmdbMedia: any, params?: any): Observable<any> {
// add extra params
Object.assign(params || {}, {
torrent: torrentResult,
Expand Down Expand Up @@ -377,7 +405,7 @@ export class ApiService {
);
}

public searchMedia(query: string, mediaType: string, page = 1) {
public searchMedia(query: string, mediaType: string, page = 1): Observable<any> {
let params = {
q: query,
media_type: mediaType,
Expand All @@ -392,7 +420,7 @@ export class ApiService {
);
}

public searchSimilarMedia(tmdbMediaId: string, mediaType: string) {
public searchSimilarMedia(tmdbMediaId: string, mediaType: string): Observable<any> {
let params = {
tmdb_media_id: tmdbMediaId,
media_type: mediaType,
Expand All @@ -407,7 +435,7 @@ export class ApiService {
);
}

public searchRecommendedMedia(tmdbMediaId: string, mediaType: string) {
public searchRecommendedMedia(tmdbMediaId: string, mediaType: string): Observable<any> {
let params = {
tmdb_media_id: tmdbMediaId,
media_type: mediaType,
Expand All @@ -422,7 +450,7 @@ export class ApiService {
);
}

public searchMediaDetail(mediaType: string, id: string) {
public searchMediaDetail(mediaType: string, id: string): Observable<any> {
const options = {headers: this._requestHeaders(), params: this._defaultParams()};
return this.http.get(`${this.API_URL_SEARCH_MEDIA}${mediaType}/${id}/`, options).pipe(
map((data: any) => {
Expand All @@ -431,7 +459,7 @@ export class ApiService {
);
}

public fetchMediaVideos(mediaType: string, id: string) {
public fetchMediaVideos(mediaType: string, id: string): Observable<any> {
const options = {headers: this._requestHeaders()};
return this.http.get(`${this.API_URL_SEARCH_MEDIA}${mediaType}/${id}/videos/`, options).pipe(
map((data: any) => {
Expand All @@ -440,7 +468,7 @@ export class ApiService {
);
}

public fetchWatchTVShows(params?: any) {
public fetchWatchTVShows(params?: any): Observable<any[]> {
params = params || {};
const httpParams = new HttpParams({fromObject: params});
return this.http.get(this.API_URL_WATCH_TV_SHOW, {params: httpParams, headers: this._requestHeaders()}).pipe(
Expand All @@ -451,7 +479,7 @@ export class ApiService {
);
}

public fetchWatchTVSeasons(params?: any) {
public fetchWatchTVSeasons(params?: any): Observable<any[]> {
params = params || {};
const httpParams = new HttpParams({fromObject: params});
return this.http.get(this.API_URL_WATCH_TV_SEASON, {params: httpParams, headers: this._requestHeaders()}).pipe(
Expand All @@ -467,7 +495,7 @@ export class ApiService {
);
}

public fetchWatchTVSeasonRequests(params?: any) {
public fetchWatchTVSeasonRequests(params?: any): Observable<any[]> {
params = params || {};
const httpParams = new HttpParams({fromObject: params});
return this.http.get(this.API_URL_WATCH_TV_SEASON_REQUEST, {params: httpParams, headers: this._requestHeaders()}).pipe(
Expand All @@ -483,7 +511,7 @@ export class ApiService {
);
}

public fetchWatchMovies(params?: any) {
public fetchWatchMovies(params?: any): Observable<any[]> {
params = params || {};
const httpParams = new HttpParams({fromObject: params});

Expand All @@ -500,7 +528,7 @@ export class ApiService {
);
}

public fetchWatchTVEpisodes(params: any) {
public fetchWatchTVEpisodes(params: any): Observable<any[]> {
const httpParams = new HttpParams({fromObject: params});
return this.http.get(this.API_URL_WATCH_TV_EPISODE, {headers: this._requestHeaders(), params: httpParams}).pipe(
map((records: any) => {
Expand All @@ -515,7 +543,7 @@ export class ApiService {
);
}

public fetchCurrentTorrents(params: any) {
public fetchCurrentTorrents(params: any): Observable<any[]> {
const httpParams = new HttpParams({fromObject: params});
return this.http.get(this.API_URL_CURRENT_TORRENTS, {headers: this._requestHeaders(), params: httpParams}).pipe(
map((data: any) => {
Expand Down Expand Up @@ -734,7 +762,7 @@ export class ApiService {
);
}

public verifySettings() {
public verifySettings(): Observable<any> {
return this.http.get(`${this.API_URL_SETTINGS}${this.settings.id}/verify/`, {headers: this._requestHeaders()}).pipe(
map((data: any) => {
return data;
Expand Down Expand Up @@ -801,15 +829,15 @@ export class ApiService {
return this._discoverMedia(this.SEARCH_MEDIA_TYPE_TV, params);
}

public fetchMovieGenres() {
public fetchMovieGenres(): Observable<any> {
return this._fetchGenres(this.SEARCH_MEDIA_TYPE_MOVIE);
}

public fetchTVGenres() {
public fetchTVGenres(): Observable<any> {
return this._fetchGenres(this.SEARCH_MEDIA_TYPE_TV);
}

public verifyJackettIndexers() {
public verifyJackettIndexers(): Observable<any> {
return this.http.get(`${this.API_URL_SETTINGS}${this.settings.id}/verify-jackett-indexers/`, {headers: this._requestHeaders()});
}

Expand Down Expand Up @@ -837,7 +865,7 @@ export class ApiService {
return this.http.get(url, {params: httpParams, headers: this._requestHeaders()});
}

public openSubtitlesAuth() {
public openSubtitlesAuth(): Observable<any> {
const url = this.API_URL_OPEN_SUBTITLES_AUTH;
return this.http.post(url, null, {headers: this._requestHeaders()});
}
Expand Down Expand Up @@ -961,13 +989,13 @@ export class ApiService {
this._updateStorage().subscribe();
}

protected _fetchGenres(mediaType: string) {
protected _fetchGenres(mediaType: string): Observable<any> {
const url = mediaType === this.SEARCH_MEDIA_TYPE_MOVIE ? this.API_URL_GENRES_MOVIE : this.API_URL_GENRES_TV;
const params = this._defaultParams();
return this.http.get(url, {headers: this._requestHeaders(), params: params});
}

protected _discoverMedia(mediaType: string, params: any) {
protected _discoverMedia(mediaType: string, params: any): Observable<any> {
params = Object.assign(params, this._defaultParams());
const httpParams = new HttpParams({fromObject: params});
const url = mediaType === this.SEARCH_MEDIA_TYPE_MOVIE ? this.API_URL_DISCOVER_MOVIES : this.API_URL_DISCOVER_TV;
Expand Down
13 changes: 7 additions & 6 deletions src/frontend/src/app/settings/quality-profiles.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ <h4 class="modal-title">Quality Profiles</h4>
<button type="button" class="btn-close" aria-label="Close" (click)="activeModal.dismiss('Cross click')"></button>
</div>
<div class="modal-body" [formGroup]="form">
<ngx-loading [show]="isLoading" [config]="{fullScreenBackdrop: true}"></ngx-loading>
<form class="was-validated" [formArrayName]="'profiles'">
<div class="card my-2" *ngFor="let profile of form.controls.profiles.controls; let i = index" [formGroupName]="i">
<div class="card-body">
Expand All @@ -15,10 +16,10 @@ <h4 class="modal-title">Quality Profiles</h4>
</div>
<!-- quality -->
<div class="mb-3">
<label [for]="i + '_profile'" class="form-label">Quality</label>
<select class="form-select" [id]="i + '_profile'" [formControlName]="'profile'" required>
<label [for]="i + '_quality'" class="form-label">Quality</label>
<select class="form-select" [id]="i + '_quality'" [formControlName]="'quality'" required>
<option></option>
<option *ngFor="let profile of apiService.qualityProfiles" [value]="profile.profile">{{ profile.profile }}</option>
<option *ngFor="let quality of apiService.qualities" [value]="quality">{{ quality }}</option>
</select>
</div>
<!-- min size -->
Expand All @@ -45,10 +46,10 @@ <h4 class="modal-title">Quality Profiles</h4>
Require 5.1 Surround Sound
</label>
</div>
<div *ngIf="{'valid': form.controls.profiles.controls[i].valid} as validity" class="text-end">
<button type="button" class="btn" [ngClass]="{'btn-outline-success': validity.valid, 'btn-outline-danger': !validity.valid}" (click)="save(form.controls.profiles.controls[i])" [disabled]="!validity.valid">Save</button>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-success" (click)="save()">Save</button>
</div>
31 changes: 17 additions & 14 deletions src/frontend/src/app/settings/quality-profiles.component.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import {Component, OnInit} from '@angular/core';
import {NgbActiveModal} from "@ng-bootstrap/ng-bootstrap";
import {ApiService} from "../api.service";
import {FormArray, FormBuilder, FormGroup, Validators} from "@angular/forms";
import {FormArray, FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
import {ToastrService} from 'ngx-toastr';

// TODO - add/remove profiles
// TODO - implement UI (html & angular) validation

@Component({
selector: 'app-quality-profiles',
templateUrl: './quality-profiles.component.html',
styleUrl: './quality-profiles.component.css'
})
export class QualityProfilesComponent implements OnInit {

public form: FormGroup<{ profiles: FormArray }>;
public isLoading = false;
public form: FormGroup<{ profiles: FormArray<FormGroup> }>;

constructor(
public apiService: ApiService,
Expand All @@ -29,24 +28,28 @@ export class QualityProfilesComponent implements OnInit {
profiles: this.fb.array(this.apiService.qualityProfiles.map(p => this.fb.group({
id: p.id,
name: this.fb.control(p.name, [Validators.required, Validators.minLength(2)]),
profile: this.fb.control(p.profile, [Validators.required]),
quality: this.fb.control(p.quality, [Validators.required]),
min_size_gb: this.fb.control(p.min_size_gb, [Validators.min(0)]),
max_size_gb: this.fb.control(p.max_size_gb, [Validators.min(0)]),
require_hdr: p.require_hdr,
require_five_point_one: p.require_five_point_one,
}))),
});
}

this.form.valueChanges.subscribe({
next: (data) => {
console.log(data);
public save(profileFormGroup: FormGroup) {
this.isLoading = true;
const data = profileFormGroup.value;
this.apiService.updateQualityProfile(data.id, data).subscribe({
next: () => {
this.toastr.success('Successfully updated quality profile');
this.isLoading = false;
},
error: (error) => {
console.error(error);
this.toastr.error('An unknown error occurred updating the quality profile');
this.isLoading = false;
}
})
}

public save() {
// TODO - implement save
this.toastr.error('SAVE TODO')
this.activeModal.close()
}
}
1 change: 1 addition & 0 deletions src/nefarious/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
path('import/media/<str:media_type>/', views.ImportMediaLibraryView.as_view()),
path('genres/<str:media_type>/', views.GenresView.as_view()),
path('media-categories/', views.MediaCategoriesView.as_view()),
path('qualities/', views.QualitiesView.as_view()),
path('auth/', views.ObtainAuthTokenView.as_view()), # authenticates user and returns token
path('git-commit/', views.GitCommitView.as_view()), # returns this app's git commit
path('open-subtitles/auth/', views.OpenSubtitlesAuthView.as_view()), # auths against open subtitles
Expand Down
7 changes: 7 additions & 0 deletions src/nefarious/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -570,3 +570,10 @@ def post(self, request):
assert 'message' in request.data, 'missing notification message'
return Response({'success': send_message(request.data['message'])})


@method_decorator(gzip_page, name='dispatch')
class QualitiesView(views.APIView):

def get(self, request):
return Response([p.name for p in PROFILES])

18 changes: 18 additions & 0 deletions src/nefarious/migrations/0090_auto_20240812_2209.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.0.2 on 2024-08-12 22:09

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('nefarious', '0089_qualityprofile_require_five_point_one'),
]

operations = [
migrations.RenameField(
model_name='qualityprofile',
old_name='profile',
new_name='quality',
),
]
Loading

0 comments on commit e151826

Please sign in to comment.