Skip to content

Commit

Permalink
feat(collections): Added exclusions to the collection detail screen
Browse files Browse the repository at this point in the history
* chore: Add backend code for rule statistics. Also add an API endpoint to test a single media item

* refactor: Add application to rule statistics output name

* chore: Add UI modal

* chore: UI - button margin

* refactor: Fix broken seasons / episodes after rule comparator refactor

* refactor: Add a global statistic result indicating if the item matched the complete rule

* refactor: add info header

* refactor: Added validation for media type & improved info messages

* refactor: button caps

* refactor: import/export : fetch identifier from id instead of array location

* refactor: Responsive monaco editor height

* fix(collection): Clicking on the card will now navigate to the details page

* feat(collection): Expanded collection detail screen, it now shows exclusions

* chore: Improve exclusion code

* chore: Center the test media button

* refactor: Editing your rules will not recreate the rulgeroup & collection anymore
  • Loading branch information
jorenn92 authored Jan 17, 2024
1 parent 0753088 commit 76d29ef
Show file tree
Hide file tree
Showing 20 changed files with 843 additions and 180 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class ExclusionsAddTypeField1705399208460 implements MigrationInterface {
name = 'ExclusionsAddTypeField1705399208460';

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
'ALTER TABLE exclusion ADD COLUMN "type" INTEGER CHECK("type" IS NULL OR "type" IN (1, 2, 3, 4)) DEFAULT NULL',
);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE exclusion DROP "type"`);
}
}
17 changes: 17 additions & 0 deletions server/src/modules/collections/collections.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,21 @@ export class CollectionsController {
size: size,
});
}

@Get('/exclusions/:id/content/:page')
getExclusions(
@Param('id') id: number,
@Param('page', new ParseIntPipe()) page: number,
@Query('size') amount: number,
) {
const size = amount ? amount : 25;
const offset = (page - 1) * size;
return this.collectionService.getCollectionExclusionsWithPlexDataAndhPaging(
id,
{
offset: offset,
size: size,
},
);
}
}
8 changes: 7 additions & 1 deletion server/src/modules/collections/collections.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,17 @@ import { OverseerrApiModule } from '../api/overseerr-api/overseerr-api.module';
import { ServarrApiModule } from '../api/servarr-api/servarr-api.module';
import { RuleGroup } from '../rules/entities/rule-group.entities';
import { TasksModule } from '../tasks/tasks.module';
import { Exclusion } from '../rules/entities/exclusion.entities';

@Module({
imports: [
PlexApiModule,
TypeOrmModule.forFeature([Collection, CollectionMedia, RuleGroup]),
TypeOrmModule.forFeature([
Collection,
CollectionMedia,
RuleGroup,
Exclusion,
]),
OverseerrApiModule,
TmdbApiModule,
ServarrApiModule,
Expand Down
101 changes: 91 additions & 10 deletions server/src/modules/collections/collections.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
} from './interfaces/collection-media.interface';
import { ICollection } from './interfaces/collection.interface';
import { EPlexDataType } from '../api/plex-api/enums/plex-data-type-enum';
import { Exclusion } from '../rules/entities/exclusion.entities';

interface addCollectionDbResponse {
id: number;
Expand All @@ -41,6 +42,8 @@ export class CollectionsService {
private readonly CollectionMediaRepo: Repository<CollectionMedia>,
@InjectRepository(RuleGroup)
private readonly ruleGroupRepo: Repository<RuleGroup>,
@InjectRepository(Exclusion)
private readonly exclusionRepo: Repository<Exclusion>,
private readonly connection: Connection,
private readonly plexApi: PlexApiService,
private readonly tmdbApi: TmdbApiService,
Expand Down Expand Up @@ -120,14 +123,68 @@ export class CollectionsService {
}
}

public async getCollectionExclusionsWithPlexDataAndhPaging(
id: number,
{ offset = 0, size = 25 }: { offset?: number; size?: number } = {},
): Promise<{ totalSize: number; items: Exclusion[] }> {
try {
const rulegroup = await this.ruleGroupRepo.findOne({
where: {
collectionId: id,
},
});

const groupId = rulegroup.id;

const queryBuilder = this.exclusionRepo.createQueryBuilder('exclusion');

queryBuilder
.where(`exclusion.ruleGroupId = ${groupId}`)
.orWhere(`exclusion.ruleGroupId is null`)
.andWhere(`exclusion.type = ${rulegroup.dataType}`)
.orderBy('id', 'DESC')
.skip(offset)
.take(size);

const itemCount = await queryBuilder.getCount();
let { entities } = await queryBuilder.getRawAndEntities();

entities = await Promise.all(
entities.map(async (el) => {
el.plexData = await this.plexApi.getMetadata(el.plexId.toString());
if (el.plexData?.grandparentRatingKey) {
el.plexData.parentData = await this.plexApi.getMetadata(
el.plexData.grandparentRatingKey.toString(),
);
} else if (el.plexData?.parentRatingKey) {
el.plexData.parentData = await this.plexApi.getMetadata(
el.plexData.parentRatingKey.toString(),
);
}
return el;
}),
);

return {
totalSize: itemCount,
items: entities ?? [],
};
} catch (err) {
this.logger.warn(
'An error occurred while performing collection actions: ' + err,
);
return undefined;
}
}

async getCollections(libraryId?: number, typeId?: 1 | 2 | 3 | 4) {
try {
const collections = await this.collectionRepo.find(
libraryId
? { where: { libraryId: libraryId } }
: typeId
? { where: { type: typeId } }
: undefined,
? { where: { type: typeId } }
: undefined,
);

return await Promise.all(
Expand Down Expand Up @@ -260,7 +317,9 @@ export class CollectionsService {
const dbCollection = await this.collectionRepo.findOne({
where: { id: +collection.id },
});

let plexColl: PlexCollection;

if (dbCollection?.plexId) {
const collectionObj: CreateUpdateCollection = {
libraryId: collection.libraryId.toString(),
Expand All @@ -269,14 +328,36 @@ export class CollectionsService {
collectionId: +dbCollection.plexId,
summary: collection?.description,
};
plexColl = await this.plexApi.updateCollection(collectionObj);
await this.plexApi.UpdateCollectionSettings({
libraryId: dbCollection.libraryId,
collectionId: dbCollection.plexId,
recommended: false,
ownHome: collection.visibleOnHome,
sharedHome: collection.visibleOnHome,
});

// is the type the same & is it an automatic collection, then update
if (
collection.type === dbCollection.type &&
!dbCollection.manualCollection &&
!collection.manualCollection
) {
plexColl = await this.plexApi.updateCollection(collectionObj);
await this.plexApi.UpdateCollectionSettings({
libraryId: dbCollection.libraryId,
collectionId: dbCollection.plexId,
recommended: false,
ownHome: collection.visibleOnHome,
sharedHome: collection.visibleOnHome,
});
} else {
// if the type changed, or the manual collection changed
if (
collection.manualCollection !== dbCollection.manualCollection ||
collection.type !== dbCollection.type ||
collection.manualCollectionName !==
dbCollection.manualCollectionName
) {
if (!dbCollection.manualCollection) {
// Don't remove the collections if it was a manual one
this.plexApi.deleteCollection(dbCollection.plexId.toString());
}
dbCollection.plexId = null;
}
}
}
const dbResp: ICollection = await this.collectionRepo.save({
...dbCollection,
Expand Down
6 changes: 6 additions & 0 deletions server/src/modules/rules/entities/exclusion.entities.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { PlexMetadata } from 'src/modules/api/plex-api/interfaces/media.interface';
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
Expand All @@ -13,4 +14,9 @@ export class Exclusion {

@Column({ nullable: true })
parent: number;

@Column({ nullable: true }) // nullable because old exclusions don't have the type. They'll be added by a maintenance task
type: 1 | 2 | 3 | 4 | undefined;

plexData: PlexMetadata; // this will be added programatically
}
5 changes: 2 additions & 3 deletions server/src/modules/rules/rules.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import { CommunityRule } from './dtos/communityRule.dto';
import { ExclusionAction, ExclusionContextDto } from './dtos/exclusion.dto';
import { RulesDto } from './dtos/rules.dto';
import { RuleExecutorService } from './rule-executor.service';
import { RuleExecutorService } from './tasks/rule-executor.service';
import { ReturnStatus, RulesService } from './rules.service';

@Controller('api/rules')
Expand Down Expand Up @@ -101,8 +101,7 @@ export class RulesController {
}
@Put()
async updateRule(@Body() body: RulesDto): Promise<ReturnStatus> {
this.rulesService.deleteRuleGroup(body.id);
return await this.rulesService.setRules(body);
return await this.rulesService.updateRules(body);
}
@Post()
async updateJob(@Body() body: { cron: string }): Promise<ReturnStatus> {
Expand Down
8 changes: 5 additions & 3 deletions server/src/modules/rules/rules.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { RulesController } from './rules.controller';
import { PlexApiModule } from '../api/plex-api/plex-api.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Rules } from './entities/rules.entities';
import { RuleExecutorService } from './rule-executor.service';
import { RuleExecutorService } from './tasks/rule-executor.service';
import { RuleGroup } from './entities/rule-group.entities';
import { PlexGetterService } from './getter/plex-getter.service';
import { ValueGetterService } from './getter/getter.service';
Expand All @@ -21,10 +21,11 @@ import { CollectionMedia } from '../collections/entities/collection_media.entiti
import { Exclusion } from './entities/exclusion.entities';
import { CommunityRuleKarma } from './entities/community-rule-karma.entities';
import { Settings } from '../settings/entities/settings.entities';
import { RuleMaintenanceService } from './rule-maintenance.service';
import { RuleMaintenanceService } from './tasks/rule-maintenance.service';
import { RuleYamlService } from './helpers/yaml.service';
import { RuleComparatorService } from './helpers/rule.comparator.service';
import { RuleConstanstService } from './constants/constants.service';
import { ExclusionTypeCorrectorService } from './tasks/exclusion-corrector.service';

@Module({
imports: [
Expand All @@ -48,14 +49,15 @@ import { RuleConstanstService } from './constants/constants.service';
RulesService,
RuleExecutorService,
RuleMaintenanceService,
ExclusionTypeCorrectorService,
PlexGetterService,
RadarrGetterService,
SonarrGetterService,
OverseerrGetterService,
ValueGetterService,
RuleYamlService,
RuleComparatorService,
RuleConstanstService
RuleConstanstService,
],
controllers: [RulesController],
})
Expand Down
Loading

0 comments on commit 76d29ef

Please sign in to comment.