Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic Quality Profiles #212

Merged
merged 30 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0ee6fb1
new quality profile table/model, migration to populate initial entries
lardbit Aug 1, 2022
f62d87b
remove stale migrations to merge with master
lardbit Jul 26, 2024
6013a23
merge master
lardbit Jul 26, 2024
914211c
handle migration conflicts
lardbit Jul 26, 2024
5e59167
copy old quality profile values into new, rename old vs new quality p…
lardbit Jul 29, 2024
ec2f74c
saving profiles works, remove old profile view
lardbit Aug 1, 2024
f227d7e
save movie/tv with custom quality profile
lardbit Aug 1, 2024
8628b34
save tv with custom quality profile, rename QualityProfile.quality ->…
lardbit Aug 1, 2024
a2bc7d3
enforce quality profile min/max size in processor
lardbit Aug 2, 2024
371ee88
enforce HDR quality profile setting in processor
lardbit Aug 2, 2024
fd2b354
fix migration to handle empty settings
lardbit Aug 2, 2024
a02a1ca
pre-populate default tv/movie quality profile in init script
lardbit Aug 2, 2024
45e0a1f
rename hdr field
lardbit Aug 2, 2024
11e814b
enforce "5.1" surround sound matching in quality profile
lardbit Aug 4, 2024
2acf44f
quality profile settings UI (WIP)
lardbit Aug 8, 2024
e151826
quality profile settings UI (working - need to add remove (wip)), add…
lardbit Aug 12, 2024
d1865bc
ability to delete quality profiles (as long as they're not system-wid…
lardbit Aug 15, 2024
9e75ba3
update model's on_delete to be appropriate
lardbit Aug 16, 2024
9f4beb4
remove "allow null" from NefariousSettings quality profiles
lardbit Aug 16, 2024
dc5f964
create quality profiles
lardbit Aug 16, 2024
3dbcf52
rm old TODO comments
lardbit Aug 16, 2024
7a7cf2c
rm unused param
lardbit Aug 16, 2024
7efd419
use FormRecord vs FormGroup (for dynamic fields), update UI for user …
lardbit Aug 16, 2024
c9d1802
use *ngFor "trackby" to fix a select issue on items repopulation
lardbit Aug 17, 2024
f10bfb1
rm todos
lardbit Aug 17, 2024
ba83c65
fix size requirements (bytes vs kb), specify gb in UI
lardbit Aug 17, 2024
9bcf4dc
swap QualityProfile.profile->quality in init
lardbit Aug 17, 2024
94199df
sort wanted/watching by date vs name
lardbit Aug 17, 2024
6223d6b
fix removing a new/unsaved profile, add duplicate profile error handling
lardbit Aug 19, 2024
1c5c3ce
fix init to assign default profiles
lardbit Aug 20, 2024
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
3 changes: 0 additions & 3 deletions docs/CHANGELOG.md

This file was deleted.

2 changes: 2 additions & 0 deletions src/frontend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ testem.log
# System Files
.DS_Store
Thumbs.db

.angular
122 changes: 88 additions & 34 deletions src/frontend/src/app/api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ 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_QUALITY_PROFILES = '/api/quality-profiles/';
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/';
API_URL_IMPORT_MEDIA_MOVIE = '/api/import/media/movie/';
Expand All @@ -55,7 +56,8 @@ export class ApiService {
public userToken: string;
public users: any; // staff-only list of all users
public settings: any;
public qualityProfiles: string[];
public qualities: string[] = [];
public qualityProfiles: any[] = [];
public mediaCategories: string[];
public watchTVSeasons: any[] = [];
public watchTVSeasonRequests: 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,11 +240,24 @@ 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.profiles) {
this.qualityProfiles = data.profiles;
if (data.length) {
this.qualityProfiles = data;
} else {
console.error('no quality profiles');
}
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 All @@ -313,8 +329,8 @@ export class ApiService {
console.log('token auth', data);
this.userToken = data.token;
this.localStorage.set(this.STORAGE_KEY_API_TOKEN, this.userToken).subscribe(
(wasSet) => {
console.log('local storage set', wasSet);
() => {
console.log('local storage set');
},
(error) => {
console.error('local storage error', error);
Expand All @@ -334,15 +350,53 @@ 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 deleteQualityProfile(id: number): Observable<any> {
return this.http.delete(`${this.API_URL_QUALITY_PROFILES}${id}/`, {headers: this._requestHeaders()}).pipe(
map((data: any) => {
// remove this quality profile
this.qualityProfiles = this.qualityProfiles.filter(profile => profile.id !== id);
// unset quality profile from Movie/TVShow/TVSeasonRequest media records
[this.watchMovies, this.watchTVShows, this.watchTVSeasonRequests].forEach((watchMediaList) => {
watchMediaList.forEach((watchMedia) => {
if (watchMedia.quality_profile === id) {
watchMedia.quality_profile = null;
}
})
})
}),
);
}

public createQualityProfile(data: any): Observable<any> {
return this.http.post(this.API_URL_QUALITY_PROFILES, data, {headers: this._requestHeaders()}).pipe(
map((data: any) => {
// append this new quality profile
this.qualityProfiles.push(data);
}),
);
}

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 +431,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 +446,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 +461,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 +476,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 +485,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 +494,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 +505,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 +521,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 +537,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 +554,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 +569,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 All @@ -526,14 +580,14 @@ export class ApiService {

public watchTVShow(
showId: number, name: string, posterImageUrl: string,
releaseDate: string, autoWatchNewSeasons?: boolean, qualityProfile?: string) {
releaseDate: string, autoWatchNewSeasons?: boolean, qualityProfile?: number) {
const params = {
tmdb_show_id: showId,
name: name,
poster_image_url: posterImageUrl,
release_date: releaseDate || null,
auto_watch: !!autoWatchNewSeasons,
quality_profile_custom: qualityProfile,
quality_profile: qualityProfile,
};
return this.http.post(this.API_URL_WATCH_TV_SHOW, params, {headers: this._requestHeaders()}).pipe(
map((data: any) => {
Expand Down Expand Up @@ -653,12 +707,12 @@ export class ApiService {
);
}

public watchMovie(movieId: number, name: string, posterImageUrl: string, releaseDate: string, qualityProfileCustom?: string) {
public watchMovie(movieId: number, name: string, posterImageUrl: string, releaseDate: string, qualityProfile?: number) {
const params = {
tmdb_movie_id: movieId,
name: name,
poster_image_url: posterImageUrl,
quality_profile_custom: qualityProfileCustom,
quality_profile: qualityProfile,
release_date: releaseDate || null,
};

Expand Down Expand Up @@ -734,7 +788,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 +855,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 +891,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 +1015,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
Loading
Loading