- 
                Notifications
    You must be signed in to change notification settings 
- Fork 17
feat: Soundcloud Support #156
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
12bbba7
              353a0bb
              01641e1
              187d4d1
              99d450d
              d9c97e7
              a4701ca
              bdc0f04
              943a02b
              c8ae12d
              f339b38
              df02363
              1ac64fa
              4dd484d
              bf0554b
              f1684f4
              33e8047
              6aa35c7
              a6c1ddc
              5b604aa
              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 | 
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| export const snapshot = {}; | ||
|  | ||
| snapshot[`SoundCloud provider > release lookup > track release with downloads enabled 1`] = ` | ||
| { | ||
| artists: [ | ||
| { | ||
| creditedName: "League of Legends", | ||
| externalIds: [ | ||
| { | ||
| id: "leagueoflegends", | ||
| provider: "soundcloud", | ||
| type: "artist", | ||
| }, | ||
| ], | ||
| name: "League of Legends", | ||
| }, | ||
| ], | ||
| availableIn: undefined, | ||
| externalLinks: [ | ||
| { | ||
| types: [ | ||
| "free streaming", | ||
| "free download", | ||
| ], | ||
| url: "https://soundcloud.com/leagueoflegends/piercing-light-mako-remix", | ||
| }, | ||
| ], | ||
| images: [ | ||
| { | ||
| provider: "SoundCloud", | ||
| thumbUrl: "https://i1.sndcdn.com/artworks-000142912000-f05col-t300x300.jpg", | ||
| types: [ | ||
| "front", | ||
| ], | ||
| url: "https://i1.sndcdn.com/artworks-000142912000-f05col-t500x500.jpg", | ||
| }, | ||
| ], | ||
| info: { | ||
| messages: [], | ||
| providers: [ | ||
| { | ||
| apiUrl: "https://api.soundcloud.com/resolve?url=https%3A%2F%2Fsoundcloud.com%2Fleagueoflegends%2Fpiercing-light-mako-remix", | ||
| id: "leagueoflegends/track/piercing-light-mako-remix", | ||
| internalName: "soundcloud", | ||
| lookup: { | ||
| method: "id", | ||
| value: "leagueoflegends/track/piercing-light-mako-remix", | ||
| }, | ||
| name: "SoundCloud", | ||
| url: "https://soundcloud.com/leagueoflegends/piercing-light-mako-remix", | ||
| }, | ||
| ], | ||
| }, | ||
| labels: undefined, | ||
| media: [ | ||
| { | ||
| format: "Digital Media", | ||
| tracklist: [ | ||
| { | ||
| artists: [ | ||
| { | ||
| creditedName: "League of Legends", | ||
| externalIds: [ | ||
| { | ||
| id: "leagueoflegends", | ||
| provider: "soundcloud", | ||
| type: "artist", | ||
| }, | ||
| ], | ||
| name: "League of Legends", | ||
| }, | ||
| ], | ||
| availableIn: undefined, | ||
| isrc: undefined, | ||
| length: 291422, | ||
| number: 1, | ||
| recording: { | ||
| externalIds: [ | ||
| { | ||
| id: "leagueoflegends/piercing-light-mako-remix", | ||
| provider: "soundcloud", | ||
| type: "track", | ||
| }, | ||
| ], | ||
| }, | ||
| title: "Piercing Light (Mako Remix)", | ||
| }, | ||
| ], | ||
| }, | ||
| ], | ||
| packaging: "None", | ||
| releaseDate: { | ||
| day: 12, | ||
| 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. The website says 14 January 2016 when I hover the "9 years ago". Even if there is a timezone offset, it can't be two days? 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. As it turns out, SoundCloud entities have 4 "release" dates... the most reliable being the dedicated release fields, as those are manually entered by the artist (usually pretty rarely, similar to ISRC and UPC data). The date that appears on the website is the  | ||
| month: 1, | ||
| year: 2016, | ||
| }, | ||
| title: "Piercing Light (Mako Remix)", | ||
| types: [ | ||
| "Single", | ||
| ], | ||
| } | ||
| `; | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,206 @@ | ||
| // Classes from https://github.com/Moebytes/soundcloud.ts | ||
| // MIT License | ||
| export type SoundcloudImageFormats = | ||
| | 't500x500' | ||
| | 'crop' | ||
| | 't300x300' | ||
| | 'large' | ||
| | 't67x67' | ||
| | 'badge' | ||
| | 'small' | ||
| | 'tiny' | ||
| | 'mini'; | ||
|  | ||
| export type SoundcloudLicense = | ||
| | 'no-rights-reserved' | ||
| | 'all-rights-reserved' | ||
| | 'cc-by' | ||
| | 'cc-by-nc' | ||
| | 'cc-by-nd' | ||
| | 'cc-by-sa' | ||
| | 'cc-by-nc-nd' | ||
| | 'cc-by-nc-sa'; | ||
|  | ||
| export type SoundcloudTrackType = | ||
| | 'original' | ||
| | 'remix' | ||
| | 'live' | ||
| | 'recording' | ||
| | 'spoken' | ||
| | 'podcast' | ||
| | 'demo' | ||
| | 'in progress' | ||
| | 'stem' | ||
| | 'loop' | ||
| | 'sound effect' | ||
| | 'sample' | ||
| | 'other'; | ||
|  | ||
| export interface SoundcloudTrack { | ||
| artwork_url: string; | ||
| comment_count: number; | ||
| commentable: boolean; | ||
| created_at: string; | ||
| description: string; | ||
| display_date: string; | ||
| download_count: number; | ||
| downloadable: boolean; | ||
| duration: number; | ||
| embeddable_by: 'all' | 'me' | 'none'; | ||
| full_duration: number; | ||
| genre: string; | ||
| has_downloads_left: boolean; | ||
| id: number; | ||
| kind: string; | ||
| label_name: string; | ||
| last_modified: string; | ||
| license: SoundcloudLicense; | ||
| likes_count: number; | ||
| monetization_model: string; | ||
| permalink: string; | ||
| permalink_url: string; | ||
| playback_count: number; | ||
| policy: string; | ||
| public: boolean; | ||
| purchase_title: string; | ||
| purchase_url: string; | ||
| reposts_count: number; | ||
| secret_token: string; | ||
| sharing: 'private' | 'public'; | ||
| state: 'processing' | 'failed' | 'finished'; | ||
| streamable: boolean; | ||
| tag_list: string; | ||
| title: string; | ||
| uri: string; | ||
| urn: string; | ||
| user: SoundcloudUser; | ||
| user_id: number; | ||
| visuals: string; | ||
| waveform_url: string; | ||
| release: string | null; | ||
| key_signature: string | null; | ||
| isrc: string | null; | ||
| bpm: number | null; | ||
| release_year: number | null; | ||
| release_month: number | null; | ||
| release_day: number | null; | ||
| stream_url: string; | ||
| download_url: string | null; | ||
| available_country_codes: string[] | null; | ||
| secret_uri: string | null; | ||
| user_favorite: boolean | null; | ||
| user_playback_count: number | null; | ||
| favoritings_count: number; | ||
| access: string; | ||
| metadata_artist: string; | ||
| } | ||
|  | ||
| export interface SoundcloudPlaylist { | ||
| duration: number; | ||
| permalink_url: string; | ||
| reposts_count: number; | ||
| genre: string | null; | ||
| permalink: string; | ||
| purchase_url: string | null; | ||
| description: string | null; | ||
| uri: string; | ||
| urn: string; | ||
| label_name: string | null; | ||
| tag_list: string; | ||
| set_type: string; | ||
| public: boolean; | ||
| track_count: number; | ||
| user_id: number; | ||
| last_modified: string; | ||
| license: SoundcloudLicense; | ||
| tracks: SoundcloudTrack[]; | ||
| id: number; | ||
| display_date: string; | ||
| sharing: 'public' | 'private'; | ||
| secret_token: string | null; | ||
| created_at: string; | ||
| likes_count: number; | ||
| kind: string; | ||
| title: string; | ||
| purchase_title: string | null; | ||
| managed_by_feeds: boolean; | ||
| artwork_url: string | null; | ||
| is_album: boolean; | ||
| user: SoundcloudUser; | ||
| published_at: string | null; | ||
| embeddable_by: 'all' | 'me' | 'none'; | ||
| release_year: number | null; | ||
| release_month: number | null; | ||
| release_day: number | null; | ||
| type: string | null; | ||
| playlist_type: string | null; | ||
| } | ||
|  | ||
| export interface SoundcloudUser { | ||
| avatar_url: string; | ||
| city: string; | ||
| comments_count: number; | ||
| country_code: number | null; | ||
| created_at: string; | ||
| creator_subscriptions: SoundcloudCreatorSubscription[]; | ||
| creator_subscription: SoundcloudCreatorSubscription; | ||
| description: string; | ||
| followers_count: number; | ||
| followings_count: number; | ||
| first_name: string; | ||
| full_name: string; | ||
| groups_count: number; | ||
| id: number; | ||
| kind: string; | ||
| last_modified: string; | ||
| last_name: string; | ||
| likes_count: number; | ||
| playlist_likes_count: number; | ||
| permalink: string; | ||
| permalink_url: string; | ||
| playlist_count: number; | ||
| reposts_count: number | null; | ||
| track_count: number; | ||
| uri: string; | ||
| urn: string; | ||
| username: string; | ||
| verified: boolean; | ||
| visuals: { | ||
| urn: string; | ||
| enabled: boolean; | ||
| visuals: SoundcloudVisual[]; | ||
| tracking: null; | ||
| }; | ||
| } | ||
|  | ||
| export interface SoundcloudVisual { | ||
| urn: string; | ||
| entry_time: number; | ||
| visual_url: string; | ||
| } | ||
|  | ||
| export interface SoundcloudCreatorSubscription { | ||
| product: { | ||
| id: string; | ||
| }; | ||
| } | ||
|  | ||
| //Custom Classes | ||
|  | ||
| export type ApiError = { | ||
| code: number; | ||
| message: string; | ||
| link: string; | ||
| status: string; | ||
| errors: string[]; | ||
| error: string | null; | ||
| }; | ||
|  | ||
| export type RawReponse = { | ||
| /** Raw Data from the soundcloud API */ | ||
| apiResponse: SoundcloudTrack | SoundcloudPlaylist; | ||
| /** The type of release, either 'track' or 'playlist' */ | ||
| type: 'track' | 'playlist'; | ||
| /** Url of the release */ | ||
| href: string; | ||
| }; | 
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| import { describeProvider, makeProviderOptions } from '@/providers/test_spec.ts'; | ||
| import { stubProviderLookups } from '@/providers/test_stubs.ts'; | ||
| import { assert } from 'std/assert/assert.ts'; | ||
| import { afterAll, describe } from '@std/testing/bdd'; | ||
| import { assertSnapshot } from '@std/testing/snapshot'; | ||
|  | ||
| import SoundCloudProvider from './mod.ts'; | ||
|  | ||
| describe('SoundCloud provider', () => { | ||
| const sc = new SoundCloudProvider(makeProviderOptions()); | ||
| const lookupStub = stubProviderLookups(sc); | ||
|  | ||
| describeProvider(sc, { | ||
| urls: [{ | ||
| description: 'album page', | ||
| url: new URL('https://soundcloud.com/ivycomb/sets/crimsongalaxies'), | ||
| id: { type: 'playlist', id: 'ivycomb/crimsongalaxies' }, | ||
| isCanonical: true, | ||
| }, { | ||
| description: 'standalone track page', | ||
| url: new URL('https://soundcloud.com/lonealphamusic/magazines'), | ||
| id: { type: 'track', id: 'lonealphamusic/magazines' }, | ||
| serializedId: 'lonealphamusic/track/magazines', | ||
| isCanonical: true, | ||
| }, { | ||
| description: 'artist page', | ||
| url: new URL('https://soundcloud.com/vocalokat'), | ||
| id: { type: 'artist', id: 'vocalokat' }, | ||
| isCanonical: true, | ||
| }], | ||
| releaseLookup: [{ | ||
| description: 'track release with downloads enabled', | ||
| release: 'leagueoflegends/track/piercing-light-mako-remix', | ||
| assert: async (release, ctx) => { | ||
| await assertSnapshot(ctx, release); | ||
| const isFree = release.externalLinks.some((link) => link.types?.includes('free download')); | ||
| assert(isFree, 'Release should be downloadable for free'); | ||
| }, | ||
| }], | ||
| }); | ||
|  | ||
| afterAll(() => { | ||
| lookupStub.restore(); | ||
| }); | ||
| }); | 
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.
Do the URLs ever have a www subdomain? The provider implementation doesn't accept it at least.