@@ -13,8 +13,7 @@ import { type CacheEntry, MetadataProvider, ReleaseLookup } from '@/providers/ba
1313import { DurationPrecision , FeatureQuality , FeatureQualityMap } from '@/providers/features.ts' ;
1414import { parseISODateTime , PartialDate } from '@/utils/date.ts' ;
1515import { ProviderError , ResponseError } from '@/utils/errors.ts' ;
16- // @deno -types="npm:@types/jsdom"
17- import { JSDOM } from 'jsdom' ;
16+ import { DOMParser , HTMLDocument } from '@b-fuze/deno-dom' ;
1817import { parseDuration } from '../../utils/time.ts' ;
1918
2019export default class OtotoyProvider extends MetadataProvider {
@@ -30,7 +29,7 @@ export default class OtotoyProvider extends MetadataProvider {
3029 pathname : '/_/default/a/:id' ,
3130 } ) ;
3231
33- readonly entityPathPattern = / \/ _ \/ d e f a u l t \/ (?: a | p ) \/ ( \d + ) $ / ;
32+ readonly entityPathPattern = / \/ _ \/ d e f a u l t \/ [ a p ] \/ ( \d + ) $ / ;
3433
3534 readonly labelUrlPattern = new URLPattern ( {
3635 hostname : this . supportedUrls . hostname ,
@@ -107,19 +106,22 @@ export default class OtotoyProvider extends MetadataProvider {
107106 }
108107
109108 scrapePackage ( html : string , webUrl : URL ) : PackagePage {
110- const { document } = ( new JSDOM ( html ) ) . window ;
109+ const doc = new DOMParser ( ) . parseFromString ( html , 'text/html' ) ;
110+ if ( ! doc ) {
111+ throw new ResponseError ( this . name , `Failed to parse HTML` , webUrl ) ;
112+ }
111113
112- const thumbUrl = this . parseAlbumArtwork ( document ) ;
114+ const thumbUrl = this . parseAlbumArtwork ( doc ) ;
113115 if ( ! thumbUrl ) {
114116 throw new ResponseError ( this . name , `Failed to extract album thumbnail` , webUrl ) ;
115117 }
116118
117- const albumMeta = this . parseAlbumMeta ( document ) ;
119+ const albumMeta = this . parseAlbumMeta ( doc ) ;
118120 if ( ! albumMeta ) {
119121 throw new ResponseError ( this . name , `Failed to extract album metadata` , webUrl ) ;
120122 }
121123
122- const trackList = this . parseTracklist ( document ) ;
124+ const trackList = this . parseTracklist ( doc ) ;
123125 if ( ! trackList ) {
124126 throw new ResponseError ( this . name , `Failed to extract tracklist` , webUrl ) ;
125127 }
@@ -142,11 +144,11 @@ export default class OtotoyProvider extends MetadataProvider {
142144 // </div>
143145 //
144146 // This is just the small thumbnail, the full size image comes from getArtwork()
145- parseAlbumArtwork ( doc : Document ) : string | undefined {
146- const imageElement = doc . querySelector < HTMLImageElement > ( 'div.album-artwork img.photo' ) ;
147+ parseAlbumArtwork ( doc : HTMLDocument ) : string | undefined {
148+ const imageElement = doc . querySelector ( 'div.album-artwork img.photo' ) ;
147149 if ( ! imageElement ) return undefined ;
148150
149- return imageElement . src ;
151+ return imageElement . getAttribute ( ' src' ) || undefined ;
150152 }
151153
152154 // The format is as follows:
@@ -169,16 +171,15 @@ export default class OtotoyProvider extends MetadataProvider {
169171 // </table>
170172 //
171173 // NOTE: `disc-row` is optional
172- parseTracklist ( doc : Document ) : Track [ ] | undefined {
173- const trackListRows = doc . querySelectorAll < HTMLTableRowElement > ( '#tracklist tbody tr' ) ;
174+ parseTracklist ( doc : HTMLDocument ) : Track [ ] | undefined {
175+ const trackListRows = doc . querySelectorAll ( '#tracklist tbody tr' ) ;
174176
175177 let currentDisc = null ;
176178 const tracks : Track [ ] = [ ] ;
177179
178180 for ( const trackRow of trackListRows ) {
179181 if ( trackRow . classList . contains ( 'disc-row' ) ) {
180- const discText = trackRow . textContent || '' ;
181- const match = discText . match ( / \d + / ) ;
182+ const match = trackRow . textContent . match ( / \d + / ) ;
182183
183184 if ( match ) {
184185 currentDisc = parseInt ( match [ 0 ] , 10 ) ;
@@ -190,17 +191,21 @@ export default class OtotoyProvider extends MetadataProvider {
190191 const trackNumberCell = trackRow . querySelector ( '.num' ) ;
191192 if ( ! trackNumberCell ) continue ;
192193
193- const trackNumber = trackNumberCell . textContent . trim ( ) ;
194+ const trackNumber = trackNumberCell . textContent ?. trim ( ) ?? '' ;
195+ if ( ! trackNumber ) return undefined ;
194196
195197 const titleSpan = trackRow . querySelector ( "td.item span[id^='title-']" ) ;
196198 if ( ! titleSpan ) return undefined ;
197199
198- const title = titleSpan . textContent . trim ( ) ;
200+ const title = titleSpan . textContent ?. trim ( ) ?? '' ;
201+ if ( ! title ) return undefined ;
199202
200203 const durationCell = trackRow . querySelectorAll ( 'td' ) [ 3 ] ;
201204 if ( ! durationCell ) continue ;
202205
203- const duration = durationCell . textContent . trim ( ) ;
206+ const duration = durationCell . textContent ?. trim ( ) ?? '' ;
207+ if ( ! duration ) return undefined ;
208+
204209 tracks . push ( {
205210 title : title ,
206211 discNumber : currentDisc ?? undefined ,
@@ -236,39 +241,43 @@ export default class OtotoyProvider extends MetadataProvider {
236241 // * The release can have an "original" release date, a platform release date, or both. "Release date" is the preferred date.
237242 // * In the case that only one date is present, sometimes "Original" is used, sometimes not. Whatever's available will
238243 // be used.
239- parseAlbumMeta ( doc : Document ) : AlbumMeta | undefined {
240- const albumMetadata = doc . querySelector < HTMLDivElement > ( 'div.album-meta-data' ) ;
244+ parseAlbumMeta ( doc : HTMLDocument ) : AlbumMeta | undefined {
245+ const albumMetadata = doc . querySelector ( 'div.album-meta-data' ) ;
241246 if ( ! albumMetadata ) return undefined ;
242247
243- const titleHeading = albumMetadata . querySelector < HTMLHeadingElement > ( 'h1.album-title' ) ;
248+ const titleHeading = albumMetadata . querySelector ( 'h1.album-title' ) ;
244249 if ( ! titleHeading ) return undefined ;
245250
246- const title = titleHeading . textContent ;
251+ const title = titleHeading . textContent ?. trim ( ) ;
252+ if ( ! title ) return undefined ;
247253
248- const artistSpans = albumMetadata . querySelectorAll < HTMLSpanElement > ( 'p.album-artist > span.album-artist' ) ;
254+ const artistSpans = Array . from ( albumMetadata . querySelectorAll ( 'p.album-artist > span.album-artist' ) ) ;
249255 if ( artistSpans . length === 0 ) return undefined ;
250256
251257 const artists : Artist [ ] = [ ] ;
252258 for ( const span of artistSpans ) {
253- const anchor = span . querySelector < HTMLAnchorElement > ( 'a' ) ;
259+ const anchor = span . querySelector ( 'a' ) ;
254260 if ( ! anchor ) return undefined ;
255261
256- const id = anchor . href . match ( this . entityPathPattern ) ?. [ 1 ] ;
262+ const id = anchor . getAttribute ( ' href' ) ? .match ( this . entityPathPattern ) ?. [ 1 ] ;
257263 if ( ! id ) return undefined ;
258264
265+ const name = span . textContent ?. trim ( ) ;
266+ if ( ! name ) return undefined ;
267+
259268 artists . push ( {
260- name : span . textContent ?. trim ( ) ,
269+ name,
261270 id,
262271 } ) ;
263272 }
264273
265- const details = albumMetadata . querySelector < HTMLDivElement > ( 'div.detail' ) ;
274+ const details = albumMetadata . querySelector ( 'div.detail' ) ;
266275 if ( ! details ) return undefined ;
267276
268277 let releaseDate : string | undefined ;
269278 let originalReleaseDate : string | undefined ;
270279
271- const releaseElements = details . querySelectorAll < HTMLParagraphElement > ( 'p.release-day' ) ;
280+ const releaseElements = details . querySelectorAll ( 'p.release-day' ) ;
272281
273282 releaseElements . forEach ( ( el ) => {
274283 const text = el . textContent ?. trim ( ) ;
@@ -290,21 +299,24 @@ export default class OtotoyProvider extends MetadataProvider {
290299 releaseDate,
291300 } ;
292301
293- const labelAnchor = details . querySelector < HTMLAnchorElement > ( 'p.label-name > a' ) ;
302+ const labelAnchor = details . querySelector ( 'p.label-name > a' ) ;
294303 if ( ! labelAnchor ) return albumMeta ;
295304
296- const catalogIdParagraph = details . querySelector < HTMLParagraphElement > ( 'p.catalog-id' ) ;
305+ const catalogIdParagraph = details . querySelector ( 'p.catalog-id' ) ;
297306
298307 let catalogNumber = undefined ;
299308 if ( catalogIdParagraph ) {
300- catalogNumber = catalogIdParagraph . textContent . trim ( ) . match ( / ^ C a t a l o g n u m b e r : ( .* ?) $ / ) ?. [ 1 ] ;
309+ catalogNumber = catalogIdParagraph . textContent ? .trim ( ) . match ( / ^ C a t a l o g n u m b e r : ( .* ?) $ / ) ?. [ 1 ] ;
301310 }
302311
303- const labelId = labelAnchor . href . match ( this . labelPathPattern ) ?. [ 1 ] ;
312+ const labelId = labelAnchor . getAttribute ( ' href' ) ? .match ( this . labelPathPattern ) ?. [ 1 ] ;
304313 if ( ! labelId ) return undefined ;
305314
315+ const labelName = labelAnchor . textContent ?. trim ( ) ;
316+ if ( ! labelName ) return undefined ;
317+
306318 albumMeta . label = {
307- name : labelAnchor . textContent ,
319+ name : labelName ,
308320 id : labelId ,
309321 catalogNumber,
310322 } ;
0 commit comments