Create Library module. Moved book controller to library controller. Added series addition to library while adding all known volumes in background. Fixed Google search context.

This commit is contained in:
Tom
2025-02-28 00:19:26 +00:00
parent 64ebdfd6f4
commit 969829da20
29 changed files with 1121 additions and 239 deletions

View File

@ -0,0 +1,93 @@
import { Processor, WorkerHost } from '@nestjs/bullmq';
import { Job } from 'bullmq';
import { PinoLogger } from 'nestjs-pino';
import { GoogleSearchContext } from 'src/providers/contexts/google.search.context';
import { BookSearchResultDto } from 'src/providers/dto/book-search-result.dto';
import { ProvidersService } from 'src/providers/providers.service';
import { CreateSeriesSubscriptionJobDto } from 'src/series/dto/create-series-subscription-job.dto';
import { LibraryService } from './library.service';
@Processor('library')
export class LibraryConsumer extends WorkerHost {
constructor(
private readonly library: LibraryService,
private readonly provider: ProvidersService,
private readonly logger: PinoLogger,
) {
super();
}
async process(job: Job, token?: string): Promise<any> {
console.log('job started:', job.name, job.data, job.id);
const series: CreateSeriesSubscriptionJobDto = job.data;
let context = this.provider.generateSearchContext(series.provider, series.title) as GoogleSearchContext;
//context.intitle = series.title;
context.maxResults = '40';
context.subject = 'Fiction';
// Search for the book(s) via the provider.
// Up until end of results or after 3 unhelpful pages of results.
let results = [];
let related = [];
let pageSearchedCount = 0;
let unhelpfulResultsCount = 0;
do {
pageSearchedCount += 1;
results = await this.provider.search(context);
const potential = results.filter(r => r.providerSeriesId == series.providerSeriesId || r.title == series.title);
if (potential.length > 0) {
related.push.apply(related, potential);
} else {
unhelpfulResultsCount += 1;
}
context = context.next();
job.updateProgress(pageSearchedCount * 5);
} while (results.length >= 40 && unhelpfulResultsCount < 3);
// Sort & de-duplicate the entries received.
const books = related.map(book => this.toScore(book, series))
.sort((a, b) => a.result.volume - b.result.volume || b.score - a.score)
.filter((_, index, arr) => index == 0 || arr[index - 1].result.volume != arr[index].result.volume);
job.updateProgress(25);
let counter = 0;
for (let book of books) {
try {
book.result.providerSeriesId = series.providerSeriesId;
await this.library.addBook(book.result);
} catch (err) {
this.logger.error({
class: LibraryConsumer.name,
method: this.process.name,
book: book.result,
score: book.score,
msg: 'Failed to add book in background.',
error: err,
});
} finally {
counter++;
job.updateProgress(25 + 75 * counter / books.length);
}
}
console.log('job completed:', job.name, job.data, job.id);
return null;
}
private toScore(book: BookSearchResultDto, series: CreateSeriesSubscriptionJobDto): ({ result: BookSearchResultDto, score: number }) {
if (!book) {
return {
result: null,
score: -1,
}
}
return {
result: book,
score: (!!book.providerSeriesId ? 50 : 0) + (book.title == series.title ? 25 : 0) + (book.url.startsWith('https://play.google.com/store/books/details?') ? 10 : 0),
}
}
}