Skip to content

Commit

Permalink
Merge branch 'feat/extend-collection-detail' into fix/merge-conflicts…
Browse files Browse the repository at this point in the history
…-16-01/2
  • Loading branch information
jorenn92 authored Jan 16, 2024
2 parents 0753088 + 5dc3874 commit 47f2f62
Show file tree
Hide file tree
Showing 10 changed files with 368 additions and 40 deletions.
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
60 changes: 58 additions & 2 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,67 @@ 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`)
.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
3 changes: 3 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,6 @@ export class Exclusion {

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

plexData: PlexMetadata; // this will be added programatically
}
138 changes: 138 additions & 0 deletions ui/src/components/Collection/CollectionDetail/Exclusions/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import { useEffect, useRef, useState } from 'react'
import OverviewContent, { IPlexMetadata } from '../../../Overview/Content'
import _ from 'lodash'
import { ICollection, ICollectionMedia } from '../..'
import GetApiHandler from '../../../../utils/ApiHandler'

interface ICollectionExclusions {
collection: ICollection
libraryId: number
}

const CollectionExcludions = (props: ICollectionExclusions) => {
const [data, setData] = useState<IPlexMetadata[]>([])
const [media, setMedia] = useState<ICollectionMedia[]>([])
// paging
const pageData = useRef<number>(0)
const fetchAmount = 25
const [totalSize, setTotalSize] = useState<number>(999)
const totalSizeRef = useRef<number>(999)
const dataRef = useRef<IPlexMetadata[]>([])
const mediaRef = useRef<ICollectionMedia[]>([])
const loadingRef = useRef<boolean>(true)
const loadingExtraRef = useRef<boolean>(false)
const [page, setPage] = useState(0)

useEffect(() => {
// Initial first fetch
setPage(1)
}, [])

const handleScroll = () => {
if (
window.innerHeight + document.documentElement.scrollTop >=
document.documentElement.scrollHeight * 0.9
) {
if (
!loadingRef.current &&
!loadingExtraRef.current &&
!(fetchAmount * (pageData.current - 1) >= totalSizeRef.current)
) {
setPage(pageData.current + 1)
}
}
}

useEffect(() => {
if (page !== 0) {
// Ignore initial page render
pageData.current = pageData.current + 1
fetchData()
}
}, [page])

useEffect(() => {
window.addEventListener('scroll', _.debounce(handleScroll.bind(this), 200))
return () => {
window.removeEventListener(
'scroll',
_.debounce(handleScroll.bind(this), 200),
)
}
}, [])

const fetchData = async () => {
if (!loadingRef.current) {
loadingExtraRef.current = true
}
// setLoading(true)
const resp: { totalSize: number; items: ICollectionMedia[] } =
await GetApiHandler(
`/collections/exclusions/${props.collection.id}/content/${pageData.current}?size=${fetchAmount}`,
)

setTotalSize(resp.totalSize)
// pageData.current = pageData.current + 1
setMedia([...mediaRef.current, ...resp.items])

setData([
...dataRef.current,
...resp.items.map((el) =>
el.plexData ? el.plexData : ({} as IPlexMetadata),
),
])
loadingRef.current = false
loadingExtraRef.current = false
}

useEffect(() => {
dataRef.current = data

// If page is not filled yet, fetch more
if (
!loadingRef.current &&
!loadingExtraRef.current &&
window.innerHeight + document.documentElement.scrollTop >=
document.documentElement.scrollHeight * 0.9 &&
!(fetchAmount * (pageData.current - 1) >= totalSizeRef.current)
) {
setPage(page + 1)
}
}, [data])

useEffect(() => {
mediaRef.current = media
}, [media])

useEffect(() => {
totalSizeRef.current = totalSize
}, [totalSize])

return (
<OverviewContent
dataFinished={true}
fetchData={() => {}}
loading={loadingRef.current}
data={data}
libraryId={props.libraryId}
collectionPage={true}
extrasLoading={
loadingExtraRef &&
!loadingRef.current &&
totalSize >= pageData.current * fetchAmount
}
onRemove={(id: string) =>
setTimeout(() => {
setData(dataRef.current.filter((el) => +el.ratingKey !== +id))
setMedia(mediaRef.current.filter((el) => +el.plexId !== +id))
}, 500)
}
collectionInfo={media.map((el) => {
props.collection.media = []
el.collection = props.collection
return el
})}
/>
)
}
export default CollectionExcludions
Loading

0 comments on commit 47f2f62

Please sign in to comment.