-
Notifications
You must be signed in to change notification settings - Fork 10
Big finish environment variables for characters and series mapping #25
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
base: main
Are you sure you want to change the base?
Changes from all commits
e8ce3f1
2b2fa27
7697009
3f86c38
2241a4c
381d996
174998b
c792ec4
557c569
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -145,6 +145,8 @@ Under "Auth" use `abs` | |
| | Name | Type | Validation | Description | | ||
| |------|------|------------|-------------| | ||
| | `limit` | int | 1-5 | Maximum number of results to return (default: 3, max: 5) | | ||
| | 'characters' | boolean | true/false | allows characters to be returned as tags | | ||
| | 'seriesmapping' | boolean | true/false | series mapping to standard fileds | | ||
|
Comment on lines
+148
to
+149
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exactly like this then. These are for the config.json and are generated from the script, defined in package.json |
||
|
|
||
| ### Returned Fields | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,9 @@ services: | |
| - HARDCOVER_TOKEN=${HARDCOVER_TOKEN} | ||
| # See https://lazylibrarian.gitlab.io/config_importing/ | ||
| - GOODREADS_API_KEY=${GOODREADS_API_KEY} | ||
| # Big Finish Env Variables | ||
| - characters=false | ||
| - seriesmapping=false | ||
|
Comment on lines
+14
to
+16
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same |
||
| volumes: | ||
| - ./data:/app/data | ||
| # This container runs by default as 1000:1000 user. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| { | ||
| "Class": "Class (CL)", | ||
| "Counter-Measures": "Counter Measures (CM)", | ||
| "Cyberman": "Cyberman (CY)", | ||
| "Doctor Who - The Monthly Adventures": "D0. Dr Who - Main Range (MR)", | ||
| "Doctor Who - The First Doctor Adventures": "D1. The First Doctor Adventures (1DA)", | ||
| "Doctor Who - The Tenth Doctor Adventures": "D10. The Tenth Doctor Adventures (10DA)", | ||
| "Doctor Who - The Second Doctor Adventures": "D2. The Second Doctor Adventures (2DA)", | ||
| "Doctor Who - The Third Doctor Adventures": "D3. The Third Doctor Adventures (3DA)", | ||
| "Doctor Who - The Fourth Doctor Adventures": "D4. The Fourth Doctor Adventures (4DA)", | ||
| "Doctor Who - The Fifth Doctor Adventures": "D5. The Fifth Doctor Adventures (5DA)", | ||
| "Doctor Who - The Sixth Doctor Adventures": "D6. The Sixth Doctor Adventures (6DA)", | ||
| "Doctor Who - The Seventh Doctor Adventures": "D7. The Seventh Doctor Adventures (7DA)", | ||
| "Doctor Who - The Eighth Doctor Adventures": "D8. The Eighth Doctor Adventures (8DA)", | ||
| "Doctor Who - The Ninth Doctor Adventures": "D9. The Ninth Doctor Adventures (9DA)", | ||
| "Doctor Who: The First Doctor Adventures": "D1. The First Doctor Adventures (1DA)", | ||
| "Doctor Who: The Tenth Doctor Adventures": "D10. The Tenth Doctor Adventures (10DA)", | ||
| "Doctor Who: The Eleventh Doctor Adventures": "D11. The Eleventh Doctor Adventures (11DA)", | ||
| "Doctor Who: The Twelfth Doctor Adventures": "D12. The Twelfth Doctor Adventures (12DA)", | ||
| "Doctor Who: The Thirteenth Doctor Adventures": "D13. The Thirteenth Doctor Adventures (13DA)", | ||
| "Doctor Who: The Second Doctor Adventures": "D2. The Second Doctor Adventures (2DA)", | ||
| "Doctor Who: The Third Doctor Adventures": "D3. The Third Doctor Adventures (3DA)", | ||
| "Doctor Who: The Fourth Doctor Adventures": "D4. The Fourth Doctor Adventures (4DA)", | ||
| "Doctor Who: The Fifth Doctor Adventures": "D5. The Fifth Doctor Adventures (5DA)", | ||
| "Doctor Who: The Sixth Doctor Adventures": "D6. The Sixth Doctor Adventures (6DA)", | ||
| "Doctor Who: The Seventh Doctor Adventures": "D7. The Seventh Doctor Adventures (7DA)", | ||
| "Doctor Who: The Eighth Doctor Adventures": "D8. The Eighth Doctor Adventures (8DA)", | ||
| "Doctor Who: The Ninth Doctor Adventures": "D9. The Ninth Doctor Adventures (9DA)", | ||
| "Dalek Empire": "Dalek Empire (DE)", | ||
| "Dark Gallifrey": "Dark Gallifrey (DG)", | ||
| "Doctor Who - Destiny of the Doctor": "Destiny of the Doctor", | ||
| "Doom's Day": "Doom's Day (DD)", | ||
| "Bernice Summerfield": "F1. Bernice Summerfield (BS)", | ||
| "Bernice Summerfield - Books & Audiobooks": "F2. Bernice Summerfield Audiobooks (BSAB)", | ||
| "Bernice Summerfield: Books & Audiobooks": "F2. Bernice Summerfield Audiobooks (BSAB)", | ||
| "Doctor Who - The New Adventures of Bernice Summerfield": "F3. The New Adventures of Bernice Summerfield (NABS)", | ||
| "Doctor Who: The New Adventures of Bernice Summerfield": "F3. The New Adventures of Bernice Summerfield (NABS)", | ||
| "Gallifrey": "Gallifrey (GAL)", | ||
| "I, Davros": "I, DAVROS", | ||
| "Jago & Litefoot": "Jago & Litefoot (J&L)", | ||
| "Missy": "Missy (MIS)", | ||
| "Doctor Who - Once and Future": "Once and Future (O&F)", | ||
| "Doctor Who - Philip Hinchcliffe Presents": "Philip Hincliffe Presents (PHP)", | ||
| "Doctor Who - Short Trips Rarities": "Rarities & Subcriber Short Trips (SST)", | ||
| "Doctor Who: Once and Future": "Once and Future (O&F)", | ||
| "Doctor Who: Philip Hinchcliffe Presents": "Philip Hincliffe Presents (PHP)", | ||
| "Doctor Who: Short Trips Rarities": "Rarities & Subcriber Short Trips (SST)", | ||
| "Rose Tyler": "Rose Tyler The Dimension Cannon (RT)", | ||
| "Sarah Jane Smith": "Sarah Jane Smith (SJS)", | ||
| "Doctor Who - Short Trips": "Short Trips (ST)", | ||
| "The Worlds of Doctor Who - Special Releases": "Special Releases (SP)", | ||
| "Doctor Who: Short Trips": "Short Trips (ST)", | ||
| "The Worlds of Doctor Who: Special Releases": "Special Releases (SP)", | ||
| "Doctor Who: The Classic Series: Special Releases": "Special Releases (SP)", | ||
| "Torchwood - Monthly Range": "T0. Torchwood Main Range (TMR)", | ||
| "Torchwood - Special Releases": "T1. Torchwood - Specials (TWsp)", | ||
| "Torchwood: Monthly Range": "T0. Torchwood Main Range (TMR)", | ||
| "Torchwood: Special Releases": "T1. Torchwood - Specials (TWsp)", | ||
| "Torchwood One": "T2. Torchwood One (TW1)", | ||
| "Torchwood - The Story Continues": "T3. Torchwood - The Story Continues", | ||
| "Torchwood: The Story Continues": "T3. Torchwood - The Story Continues", | ||
| "Torchwood Soho": "T4. Torchwood Soho (TWS)", | ||
| "Doctor Who - The Audio Novels": "The Audio Novels", | ||
| "Doctor Who - The Companion Chronicles": "The Companion Chronicles (CC)", | ||
| "Doctor Who: The Audio Novels": "The Audio Novels", | ||
| "Doctor Who: The Companion Chronicles": "The Companion Chronicles (CC)", | ||
| "River Song": "The Diary of River Song (RS)", | ||
| "Doctor Who - The Doctor Chronicles": "The Doctor Chronicles (TDC)", | ||
| "Doctor Who - The Early Adventures": "The Early Adventures (EA)", | ||
| "Doctor Who: The Doctor Chronicles": "The Doctor Chronicles (TDC)", | ||
| "Doctor Who: The Early Adventures": "The Early Adventures (EA)", | ||
| "The Lives of Captain Jack": "The Lives of Captain Jack (LCJ)", | ||
| "Doctor Who - The Lost Stories": "The Lost Stories (LS)", | ||
| "Doctor Who: The Lost Stories": "The Lost Stories (LS)", | ||
| "The Paternoster Gang": "The Paternoster Gang (PAT)", | ||
| "The Robots": "The Robots (ROB)", | ||
| "Doctor Who - The Stageplays": "The Stageplays (STG)", | ||
| "Doctor Who - The War Doctor": "The War Doctor (WD)", | ||
| "Doctor Who: The Stageplays": "The Stageplays (STG)", | ||
| "Doctor Who: The War Doctor": "The War Doctor (WD)", | ||
| "The War Master": "The War Master (WM)", | ||
| "Doctor Who - Time Lord Victorious": "Time Lord Victorious (TLV)", | ||
| "Doctor Who: Time Lord Victorious": "Time Lord Victorious (TLV)", | ||
| "UNIT": "UNIT (UNIT)", | ||
| "UNIT - The New Series": "UNIT - The New Series (UNITNS)", | ||
| "UNIT: The New Series": "UNIT - The New Series (UNITNS)", | ||
| "Iris Wildthyme": "F4. Iris Wildthyme (IW)", | ||
| "Iris Wildthyme and Friends":"F5. Iris Wildthyme & Friends (IWF)", | ||
| "Graceless": "F6. Graceless", | ||
| "Doctor Who - Unbound": "Unbound (UN)", | ||
| "Vienna": "F7. Vienna", | ||
| "Charlotte Pollard": "F8. Charlotte Pollard", | ||
| "Doctor Who - The Fugitive Doctor": "The Fugitive Doctor Adventures (FDA)", | ||
| "Doctor Who: The Fugitive Doctor": "The Fugitive Doctor Adventures (FDA)", | ||
| "Call Me Master": "Call Me Master (CMM)", | ||
| "Susan's War": "Susan's War (SW)", | ||
| "V UK": "V - UK", | ||
| "Planet Krynoid": "Planet Krynoid (PG)", | ||
| "Doctor Who: Circuit Breaker": "Circuit Breaker (CB)" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,10 @@ const config: ProviderConfig = JSON.parse(fs.readFileSync(configPath, 'utf-8')) | |
| const BASE_URL = 'https://www.bigfinish.com' | ||
| const SEARCH_URL = `${BASE_URL}/api/search` | ||
|
|
||
| const isEnvEnabled = (value: string | undefined): boolean => value?.trim().toLowerCase() === 'true' | ||
| const ENABLE_SERIES_MAPPING = isEnvEnabled(process.env.seriesmapping) | ||
| const ENABLE_CHARACTERS = isEnvEnabled(process.env.characters) | ||
|
Comment on lines
+15
to
+17
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that would better fit as an parameter for the provider via the config so that each user can decide for themselfs |
||
|
|
||
| const SEARCH_HEADERS = { | ||
| 'User-Agent': | ||
| 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', | ||
|
|
@@ -30,6 +34,23 @@ const RSC_HEADERS = { | |
| rsc: '1' | ||
| } | ||
|
|
||
| const SERIES_MAPPING_PATH = path.join( | ||
| process.cwd(), | ||
| 'data', | ||
| 'series-mapping.json' | ||
| ) | ||
|
|
||
| function getSeriesMapping(): Record<string, string> { | ||
| try { | ||
| return JSON.parse( | ||
| fs.readFileSync(SERIES_MAPPING_PATH, 'utf8') | ||
| ) as Record<string, string> | ||
| } catch (error) { | ||
| console.error(`Failed to load ${SERIES_MAPPING_PATH}`, error) | ||
| return {} | ||
| } | ||
| } | ||
|
|
||
| interface BigFinishSearchResult { | ||
| id: number | ||
| release_slug: string | ||
|
|
@@ -60,6 +81,7 @@ interface ParsedBookData { | |
| duration: string | null | ||
| writtenBy: string | null | ||
| narratedBy: string | null | ||
| tags?: string[] | ||
| coverUrl: string | null | ||
| isbn: string | null | ||
| } | ||
|
|
@@ -157,7 +179,9 @@ export default class BigFinishProvider extends BaseProvider { | |
| if (!releaseData) return null | ||
|
|
||
| const titleParts = this.extractTitleParts(releaseData.title || hit.name) | ||
| const narrators = this.namesFrom(releaseData.cast?.filter((person) => person.label?.toLowerCase() === 'narrator')) | ||
| const narratorNames = this.namesFrom(releaseData.cast?.filter((person) => person.label?.toLowerCase() === 'narrator')) | ||
| const narrators = ENABLE_CHARACTERS ? this.formatNarrators(releaseData.cast) : narratorNames | ||
| const narratorTags = ENABLE_CHARACTERS ? this.extractNarratorTags(releaseData.cast) : [] | ||
| const authors = this.namesFrom(releaseData.written_by) | ||
| const technicalDetails = releaseData.production_credits?.technical_details as Record<string, unknown> | undefined | ||
| const duration = | ||
|
|
@@ -166,18 +190,20 @@ export default class BigFinishProvider extends BaseProvider { | |
| hit.duration | ||
| const isbn = technicalDetails?.digital_retail_isbn || technicalDetails?.physical_retail_isbn | ||
| const description = this.resolveRscText(rsc, releaseData.about?.summary) || hit.description || null | ||
| const about = ENABLE_CHARACTERS ? this.appendContributors(description, releaseData) : description | ||
|
|
||
| return { | ||
| schemaVersion: 3, | ||
| url, | ||
| title: releaseData.title || hit.name || null, | ||
| series: this.formatSeries(releaseData.range || titleParts.series), | ||
| series: ENABLE_SERIES_MAPPING ? this.formatSeries(releaseData.range || titleParts.series) : releaseData.range || titleParts.series, | ||
| seriesTag: releaseData.release_number ? String(releaseData.release_number) : titleParts.seriesTag, | ||
| releaseDate: releaseData.release_date || null, | ||
| about: this.appendContributors(description, releaseData), | ||
| about, | ||
| duration: duration ? String(duration) : null, | ||
| writtenBy: authors.join(', ') || null, | ||
| narratedBy: narrators.join(', ') || null, | ||
| tags: narratorTags.length > 0 ? narratorTags : undefined, | ||
| coverUrl: releaseData.image || hit.image || null, | ||
| isbn: typeof isbn === 'string' ? isbn : null | ||
| } | ||
|
|
@@ -235,6 +261,47 @@ export default class BigFinishProvider extends BaseProvider { | |
| ] | ||
| } | ||
|
|
||
| private formatNarrators(cast: NamedContributor[] | undefined): string[] { | ||
| const narrators = this.namesFrom(cast?.filter((person) => person.label?.toLowerCase() === 'narrator')) | ||
| const rolesByName = new Map<string, Set<string>>() | ||
|
|
||
| for (const person of cast || []) { | ||
| const name = person.name?.trim() | ||
| const label = person.label?.trim() | ||
| if (!name || !label || label.toLowerCase() === 'narrator') continue | ||
|
|
||
| for (const role of this.splitRoleLabels(label)) { | ||
| const roles = rolesByName.get(name) ?? new Set<string>() | ||
| roles.add(role) | ||
| rolesByName.set(name, roles) | ||
| } | ||
| } | ||
|
|
||
| return narrators.map((name) => { | ||
| const roles = Array.from(rolesByName.get(name) || []).sort() | ||
| return roles.length > 0 ? `${name} (${roles.join(', ')})` : name | ||
| }) | ||
| } | ||
|
|
||
| private extractNarratorTags(cast: NamedContributor[] | undefined): string[] { | ||
| const tags = new Set<string>() | ||
|
|
||
| for (const person of cast || []) { | ||
| const label = person.label?.trim() | ||
| if (!label || label.toLowerCase() === 'narrator') continue | ||
|
|
||
| for (const role of this.splitRoleLabels(label)) { | ||
| tags.add(role) | ||
| } | ||
| } | ||
|
|
||
| return [...tags].sort() | ||
| } | ||
|
|
||
| private splitRoleLabels(label: string): string[] { | ||
| return [...new Set(label.split('/').map((role) => role.trim()).filter(Boolean))] | ||
| } | ||
|
|
||
| private appendContributors(description: string | null, releaseData: BigFinishReleaseData): string | null { | ||
| const entries: string[] = [] | ||
| const add = (role: string, people: NamedContributor[] | undefined) => { | ||
|
|
@@ -259,7 +326,12 @@ export default class BigFinishProvider extends BaseProvider { | |
| } | ||
|
|
||
| private formatSeries(series: string | null | undefined): string | null { | ||
| return series ? series.replace(/\s*:\s*/g, ' - ') : null | ||
| if (!series) return null | ||
|
|
||
| const seriesMapping = getSeriesMapping() | ||
| const mappedSeries = seriesMapping[series.trim()] ?? series | ||
|
|
||
| return mappedSeries.replace(/\s*:\s*/g, ' - ') | ||
| } | ||
|
|
||
| private escapeHtml(value: string): string { | ||
|
|
@@ -309,8 +381,9 @@ export default class BigFinishProvider extends BaseProvider { | |
| description: data.about, | ||
| cover: data.coverUrl, | ||
| isbn: data.isbn, | ||
| tags: data.tags, | ||
| series, | ||
| language: 'en', | ||
| language: 'Eng', | ||
| publishedYear, | ||
| publisher: 'Big Finish', | ||
| duration | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be redundant then