Skip to content

Commit 3f79e33

Browse files
committed
feat: populate track relationship types for Bandcamp
- stream for free, download for free and purchase for download are populated correctly; - pay what you want is not populated, because that requires some redesign (to query the individual tracks from bandcamp and check their pricing options); more info here #95.
1 parent ba3209e commit 3f79e33

File tree

5 files changed

+63
-4
lines changed

5 files changed

+63
-4
lines changed

harmonizer/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ export interface EntityId {
3030
export interface ExternalEntityId extends EntityId {
3131
/** Internal name of the provider. */
3232
provider: string;
33+
/** Optional link types for this external ID. */
34+
linkTypes?: LinkType[];
3335
}
3436

3537
/** Entity which may have external IDs which can be resolved to its MBID. */

providers/Bandcamp/__snapshots__/mod.test.ts.snap

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ suika.love",
7474
externalIds: [
7575
{
7676
id: "thedarkthursday/suomi-lovely",
77+
linkTypes: [
78+
"free streaming",
79+
"paid download",
80+
],
7781
provider: "bandcamp",
7882
type: "track",
7983
},
@@ -89,6 +93,10 @@ suika.love",
8993
externalIds: [
9094
{
9195
id: "thedarkthursday/bittersweet-desires-to-overdose-on-my-prescriptions",
96+
linkTypes: [
97+
"free streaming",
98+
"paid download",
99+
],
92100
provider: "bandcamp",
93101
type: "track",
94102
},
@@ -104,6 +112,10 @@ suika.love",
104112
externalIds: [
105113
{
106114
id: "thedarkthursday/set-fire-to-the-space-snow",
115+
linkTypes: [
116+
"free streaming",
117+
"paid download",
118+
],
107119
provider: "bandcamp",
108120
type: "track",
109121
},

providers/Bandcamp/json_types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,8 @@ export interface TrackInfo {
189189
/** Indicates whether the track can be streamed (can also be `1` for unreleased tracks). */
190190
streaming: 1; // = boolean `1 | null`?
191191
is_downloadable: boolean | null;
192-
has_free_download: null;
192+
/** Indicates whether the track is available for free download only (not for purchase). */
193+
has_free_download: boolean | null;
193194
free_album_download: boolean;
194195
/** Duration in seconds (floating point, `0.0` for unreleased tracks). */
195196
duration: number;

providers/Bandcamp/mod.ts

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,47 @@ export default class BandcampProvider extends MetadataProvider {
109109
}
110110
}
111111

112-
override getLinkTypesForEntity(): LinkType[] {
112+
override getLinkTypesForEntity(entity: EntityId): LinkType[] {
113+
if (entity.type === 'track') {
114+
// For tracks, we need to determine the link types based on the track data
115+
// This will be handled by the release lookup when creating the track
116+
return [];
117+
}
113118
// MB has special handling for Bandcamp artist URLs
114119
return ['discography page'];
115120
}
116121

122+
/** Determines the link types for a track based on its data. */
123+
getTrackLinkTypes(track: TrackInfo | PlayerTrack): LinkType[] {
124+
const linkTypes: LinkType[] = [];
125+
126+
if ('track_streaming' in track) {
127+
const playerTrack = track as PlayerTrack;
128+
if (playerTrack.track_streaming) {
129+
linkTypes.push('free streaming');
130+
}
131+
132+
return linkTypes;
133+
}
134+
135+
const trackInfo = track as TrackInfo;
136+
137+
if (trackInfo.streaming === 1) {
138+
linkTypes.push('free streaming');
139+
}
140+
141+
if (trackInfo.is_downloadable === true) {
142+
if (trackInfo.has_free_download) {
143+
linkTypes.push('free download');
144+
} else {
145+
linkTypes.push('paid download');
146+
// TODO: And potentially a free download
147+
}
148+
}
149+
150+
return linkTypes;
151+
}
152+
117153
extractEmbeddedJson<Data>(webUrl: URL, maxTimestamp?: number): Promise<CacheEntry<Data>> {
118154
return this.fetchJSON<Data>(webUrl, {
119155
policy: { maxTimestamp },
@@ -349,13 +385,21 @@ export class BandcampReleaseLookup extends ReleaseLookup<BandcampProvider, Relea
349385
trackNumber = rawTrack.tracknum + 1;
350386
}
351387

388+
const externalIds = title_link ? this.provider.makeExternalIdsFromUrl(title_link, this.rawReleaseUrl) : [];
389+
if (externalIds.length > 0) {
390+
const linkTypes = this.provider.getTrackLinkTypes(rawTrack);
391+
if (linkTypes.length > 0) {
392+
externalIds[0].linkTypes = linkTypes;
393+
}
394+
}
395+
352396
return {
353397
number: trackNumber,
354398
title,
355399
artists: artist ? [this.makeArtistCreditName(artist)] : undefined,
356400
length: rawTrack.duration * 1000,
357401
recording: {
358-
externalIds: title_link ? this.provider.makeExternalIdsFromUrl(title_link, this.rawReleaseUrl) : [],
402+
externalIds,
359403
},
360404
};
361405
}

server/components/LinkWithMusicBrainz.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export function LinkWithMusicBrainz({ entity, entityType, sourceEntityUrl, entit
3838
const provider = providers.findByName(externalId.provider)!;
3939
return {
4040
url: provider.constructUrl(externalId).href,
41-
types: provider.getLinkTypesForEntity(externalId),
41+
types: externalId.linkTypes ?? provider.getLinkTypesForEntity(externalId),
4242
};
4343
}).filter((link) => !existingLinks.has(link.url));
4444

0 commit comments

Comments
 (0)