A decentralized music tracking and discovery platform built on AT Protocol 馃幍 rocksky.app
spotify atproto lastfm musicbrainz scrobbling listenbrainz
99
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 58 lines 1.5 kB view raw
1import _ from "lodash"; 2import * as R from "ramda"; 3 4export const dedupeTracksKeepLyrics = (tracks) => { 5 const trackMap = new Map(); 6 7 for (const track of tracks) { 8 const key = `${track.discNumber} - ${track.trackNumber}`; 9 10 if (!key) continue; 11 12 const existing = trackMap.get(key); 13 14 // If current has lyrics and either no existing or existing has no lyrics, replace it 15 if (!existing || (!existing.lyrics && track.lyrics)) { 16 trackMap.set(key, track); 17 } 18 } 19 20 return Array.from(trackMap.values()); 21}; 22 23type AnyObject = Record<string, any>; 24 25const isObject = (val: unknown): val is AnyObject => 26 typeof val === "object" && val !== null && !Array.isArray(val); 27 28export const deepCamelCaseKeys = <T>(obj: T): any => { 29 if (Array.isArray(obj)) { 30 return obj.map(deepCamelCaseKeys); 31 } else if (isObject(obj)) { 32 return R.pipe( 33 R.toPairs, 34 R.map( 35 ([key, value]) => 36 [_.camelCase(String(key)), deepCamelCaseKeys(value)] as [string, any], 37 ), 38 R.fromPairs, 39 )(obj as object); 40 } 41 return obj; 42}; 43 44export const deepSnakeCaseKeys = <T>(obj: T): any => { 45 if (Array.isArray(obj)) { 46 return obj.map(deepSnakeCaseKeys); 47 } else if (isObject(obj)) { 48 return R.pipe( 49 R.toPairs, 50 R.map( 51 ([key, value]) => 52 [_.snakeCase(String(key)), deepSnakeCaseKeys(value)] as [string, any], 53 ), 54 R.fromPairs, 55 )(obj as object); 56 } 57 return obj; 58};