diff --git a/.env b/.env index 2ce8fdff..db12cf75 100644 --- a/.env +++ b/.env @@ -1,6 +1,3 @@ -ACCESS_KEY_WASABI= -SECRET_KEY_WASABI= -BUCKET_WASABI= API_KEY_PLAUSIBLE= API_KEY_TRAKT= API_KEY_WEBMENTIONS_CORYD_DEV= diff --git a/src/_data/json/artist-genres.json b/src/_data/json/artist-genres.json deleted file mode 100644 index 0e80aaa7..00000000 --- a/src/_data/json/artist-genres.json +++ /dev/null @@ -1,915 +0,0 @@ -{ - "Punk": [ - "+44", - "wire", - "sex-pistols", - "rkl", - "refused", - "propagandhi", - "the-pogues", - "nomeansno", - "nofx", - "misfits", - "minutemen", - "minor-threat", - "joyce-manor", - "jello-biafra-with-nomeansno", - "j-church", - "hüsker-dü", - "green-day", - "fugazi", - "fake-names", - "descendents", - "deep-turtle", - "death-from-above-1979", - "dead-kennedys", - "cock-sparrer", - "coachwhips", - "cloud-nothings", - "the-clash", - "box-car-racer", - "blink-182", - "black-flag", - "bad-religion", - "bad-brains", - "afi" - ], - "Black Metal": [ - "zhrine", - "yellow-eyes", - "wormlust", - "woods-of-ypres", - "woods-of-desolation", - "wolves-in-the-throne-room", - "white-ward", - "weakling", - "vital-spirit", - "ved-buens-ende...", - "vaura", - "unanimated", - "ultha", - "ultar", - "trna", - "tómarúm", - "tod-huetet-uebel", - "thou,-mizmor-&-emma-ruth-rundle", - "thantifaxath", - "tardigrada", - "svartidauði", - "sunken", - "stormkeep", - "skáphe", - "sinmara", - "the-silver", - "show-me-a-dinosaur", - "sacramentum", - "rebirth-of-nefast", - "pillorian", - "panopticon", - "outre", - "ossuaire", - "örmagna", - "olhava", - "odraza", - "oathbreaker", - "numenorean", - "nordicwinter", - "noltem", - "moonsorrow", - "møl", - "mizmor-&-thou", - "mizmor-&-andrew-black", - "mizmor", - "misþyrming", - "mgła", - "mare-cognitum", - "mannveira", - "lunar-aurora", - "lantlôs", - "kriegsmaschine", - "krallice", - "kostnatění", - "karg", - "hræ", - "helfró", - "harakiri-for-the-sky", - "grey-waters", - "gravenchalice", - "furia", - "fen", - "falls-of-rauros", - "enslaved", - "emperor", - "embrace-of-thorns", - "drudkh", - "drought", - "deathspell-omega", - "deafheaven", - "the-clearing-path", - "celeste", - "carpe-noctem", - "bosse-de-nage", - "blut-aus-nord", - "behemoth", - "an-autumn-for-crippled-children", - "au-dessus", - "ash-borer", - "arcturus", - "aosoth", - "aoratos", - "andavald", - "amesoeurs", - "altar-of-plagues", - "akhlys", - "agalloch", - "afsky" - ], - "Death Metal": [ - "zealotry", - "xysma", - "xoth", - "xenomorph", - "wretched-fate", - "worthless", - "worm", - "wombbath", - "witherscape", - "witch-vomit", - "weeping-sores", - "warp-chamber", - "vrenth", - "vorum", - "voimaton", - "voidceremony", - "void-terror", - "void-rot", - "vircolac", - "vertebra-atlantis", - "venomous-skeleton", - "vengeful", - "venenum", - "vasaeleth", - "vader", - "vacuous", - "vacivus", - "unleashed", - "universally-estranged", - "undersave", - "undergang", - "under-the-church", - "undeath", - "unbounded-terror", - "unaussprechlichen-kulten", - "unanimated", - "ulthar", - "úlfúð", - "ulcerate", - "turris-eburnea", - "tumba-de-carne", - "tribulation", - "trenchrot", - "transgressor", - "toughness", - "totten-korps", - "torchure", - "tomb-mold", - "timeghoul", - "tiamat", - "therion", - "thergothon", - "thanatos", - "temisto", - "teleport", - "teitanblood", - "teeth", - "taphos", - "sxuperion", - "sweven", - "swallowed", - "suppression", - "superstition", - "sunless", - "sulphurous", - "sulphur-aeon", - "suffocation", - "suffering-hour", - "stench", - "stargazer", - "spectral-voice", - "spawn-of-possession", - "spasme", - "sororicide", - "sorcery", - "sonne-adam", - "solothus", - "snorlax", - "sněť", - "slugathor", - "skullcrush", - "skeletal-remains", - "skeletal", - "sinister", - "siderean", - "shub-niggurath", - "shards-of-humanity", - "setentia", - "sermon-of-flames", - "seputus", - "sepulcher", - "sentient-horror", - "sentenced", - "sedimentum", - "seance", - "scythian", - "scorched", - "sarmat", - "runemagick", - "rune", - "rottrevore", - "rotted", - "rothadás", - "ritual-necromancy", - "ripping-corpse", - "ripped-to-shreds", - "resurgency", - "reptilian", - "replicant", - "reeking-aura", - "ravenous-death", - "rapture", - "question", - "qrixkuor", - "pyrrhon", - "pyrexia", - "pyre", - "putrescine", - "purtenance", - "pungent-stench", - "psycroptic", - "possessed", - "portal", - "poisonous", - "plague-bearer", - "phrenelith", - "phobophilic", - "pestilength", - "perilaxe-occlusion", - "pan.thy.monium", - "outre-tombe", - "outer-heaven", - "ossuarium", - "orthodoxy", - "oksennus", - "of-feather-and-bone", - "obscure-infinity", - "obscura", - "obscene", - "obliveon", - "obliteration", - "obituary", - "nucleus", - "nothingness", - "noisem", - "nocturnus", - "nithing", - "nihilist", - "nex-carnis", - "neuraxis", - "nekrovault", - "nekrofilth", - "negativa", - "necrovorous", - "necrovore", - "necrovation", - "necrot", - "necros-christos", - "necrophobic", - "necrophiliac", - "napalm-death", - "moss-upon-the-skull", - "mortuous", - "mortuary", - "mortiferum", - "mortem", - "mortal-incarnation", - "morta-skuld", - "morpheus-descends", - "morgue", - "mordicus", - "morbus-chron", - "morbid-angel", - "monstrosity", - "molested", - "mitochondrion", - "mithridatum", - "mithras", - "miscreance", - "miasma", - "merciless", - "megaslaughter", - "mefitis", - "master", - "massacre", - "martyr", - "malignant-altar", - "malevolent-creation", - "lykathea-aflame", - "lurid-panacea", - "lunar-chamber", - "luciferion", - "lost-harvest", - "last-sacrament", - "lantern", - "krypts", - "kronos", - "kralizec", - "krabathor", - "killing-addiction", - "khthoniik-cerviiks", - "kever", - "karmacipher", - "jumpin'-jesus", - "jordablod", - "intellect-devourer", - "innumerable-forms", - "infester", - "infernal-coil", - "inculter", - "incantation", - "inanna", - "imprecation", - "imperial-triumphant", - "impaler", - "immortalis", - "immolation", - "ignivomous", - "hypocrisy", - "hyperdontia", - "howls-of-ebb", - "howls-of-ebb", - "hour-of-penance", - "horror-god", - "hooded-menace", - "hissing", - "hideous-divinity", - "heaving-earth", - "haunter", - "hail-of-bullets", - "gutvoid", - "gutter-instinct", - "grenadier", - "graveyard", - "graveside", - "grave-ritual", - "grave-miasma", - "grave-infestation", - "grave", - "gosudar", - "gorguts", - "gorephilia", - "gorefest", - "golgothan-remains", - "gold-spire", - "god-macabre", - "glacial-tomb", - "gigan", - "ghoulgotha", - "ghastly", - "gateway", - "garroted", - "galvanizer", - "funeral-leech", - "funebre", - "funebrarum", - "fuming-mouth", - "frozen-soul", - "flourishing", - "fleshgod-apocalypse", - "fleshcrawl", - "fetid", - "faceless-burial", - "exlimitir", - "execration", - "excruciate", - "evilyn", - "eucharist", - "eternal-dirge", - "epitaph", - "entombed", - "engulfed", - "encoffination", - "encenathrakh", - "emptiness", - "electrocution", - "egregore", - "edge-of-sanity", - "dream-unending", - "dragged-into-sunlight", - "dormant-ordeal", - "dominus-xul", - "domains", - "dismember", - "disma", - "diskord", - "disincarnate", - "disharmonic-orchestra", - "disembarkation", - "disciples-of-mockery", - "disastrous-murmur", - "dig-that-body-up,-it's-alive!", - "devoid-of-thought", - "deteriorate", - "desultory", - "desolation-realm", - "desecresy", - "depravity", - "demilich", - "demigod", - "demented-ted", - "deiquisitor", - "deicide", - "degial", - "defeated-sanity", - "defacement", - "deceased", - "decaying-purity", - "death", - "dead-congregation", - "darkthrone", - "cryptworm", - "cryptopsy", - "cryptic-shift", - "cryptborn", - "crypt-of-kerberos", - "cruciamentum", - "cosmovore", - "cosmic-putrefaction", - "cosmic-atrophy", - "coscradh", - "corpsessed", - "convulsing", - "convulse", - "contaminated", - "concrete-winds", - "comecon", - "coffincraft", - "coffin-texts", - "coffin-curse", - "cianide", - "chthe'ilist", - "the-chasm", - "chapel-of-disease", - "chaotian", - "chainsword", - "cerebral-rot", - "centinex", - "cenotaph", - "cemetery-filth", - "cemetary", - "carnal-tomb", - "carnage", - "carcinoma", - "carcass", - "carbonized", - "cancer", - "cambion", - "cadaveric-fumes", - "cadaver", - "burial-invocation", - "brutality", - "broken-hope", - "borgia", - "bolt-thrower", - "bog-body", - "bloodsoaked-necrovoid", - "bloodbath", - "blood-spore", - "blood-incantation", - "black-curse", - "benediction", - "behemoth", - "bedsore", - "baphomet", - "backyard-mortuary", - "azath", - "autopsy", - "autophagy", - "authorize", - "augury", - "atrocity", - "atheist", - "atemporal", - "ataraxy", - "astriferous", - "asphyx", - "appalling-spawn", - "anatomia", - "amputory", - "amorphis", - "altered-dead", - "altars-ablaze", - "altars", - "altarage", - "agony", - "afflicted", - "aeviterne", - "adversarial", - "adramelech", - "ad-vitam-infernal", - "ad-nauseam", - "acrostichon", - "acephalix", - "accidental-suicide", - "abhorrence" - ], - "Classic Rock": [ - "yes", - "traveling-wilburys", - "tom-petty-and-the-heartbreakers", - "tom-petty", - "rush", - "pink-floyd", - "king-crimson", - "grave", - "bruce-springsteen" - ], - "Alternative": ["year-of-the-rabbit", "failure", "the-cranberries", "bob-mould"], - "Sludge Metal": [ - "yautja", - "yashira", - "thou-&-the-body", - "thou-&-mizmor", - "thou-&-emma-ruth-rundle", - "thou", - "sumac", - "primitive-man", - "neurosis", - "kowloon-walled-city", - "keiji-haino-&-sumac", - "inter-arma", - "full-of-hell-&-primitive-man", - "chat-pile", - "acid-bath" - ], - "Indie": [ - "the-xx", - "will-sheff", - "sufjan-stevens", - "the-stone-roses", - "the-postal-service", - "p.-wolf-and-avi", - "okkervil-river", - "neutral-milk-hotel", - "the-japanese-house", - "iron-&-wine", - "the-head-and-the-heart", - "golden-shoulders", - "frightened-rabbit", - "bright-eyes", - "bon-iver", - "blind-pilot", - "big-red-machine", - "better-oblivion-community-center", - "all-time-quarterback" - ], - "Indie Rock": [ - "the-wytches", - "wavves-&-cloud-nothings", - "the-war-on-drugs", - "thom-yorke", - "teenage-fanclub", - "rogue-wave", - "the-replacements", - "radiohead", - "radioactivity", - "pixies", - "pedro-the-lion", - "pedro-the-lion", - "pavement", - "okkervil-river", - "nova-mob", - "no", - "the-national", - "matt-berninger", - "marked-men", - "the-kills", - "jay-farrar-and-benjamin-gibbard", - "japandroids", - "interpol", - "hop-along", - "the-halo-benders", - "frances-quinlan", - "el-vy", - "death-cab-for-cutie", - "dredg", - "doug-martsch", - "the-dismemberment-plan", - "dinosaur-jr.", - "death-from-above-1979", - "death-cab-for-cutie", - "death-cab-for-cutie", - "damien-jurado", - "cloud-nothings", - "built-to-spill", - "built-to-spill", - "boygenius", - "atoms-for-peace", - "arcade-fire" - ], - "Emo": [ - "worst-party-ever", - "the-wonder-years", - "turnover", - "title-fight", - "tigers-jaw", - "taking-back-sunday", - "state-lines", - "spanish-love-songs", - "sincere-engineer", - "seahaven", - "rites-of-spring", - "prince-daddy-&-the-hyena", - "pity-sex", - "pinegrove", - "oso-oso", - "the-newfound-interest-in-connecticut", - "my-chemical-romance", - "michael-cera-palin", - "macseal", - "knuckle-puck", - "joyce-manor", - "jimmy-eat-world", - "jawbreaker", - "the-hotelier", - "hot-mulligan", - "home-is-where", - "have-mercy", - "the-get-up-kids", - "free-throw", - "fiddlehead", - "fall-out-boy", - "drive-like-jehu", - "dogleg", - "charmer", - "can't-swim", - "the-brave-little-abacus", - "brand-new", - "basement", - "balance-and-composure", - "the-ataris", - "anxious", - "american-football", - "alkaline-trio" - ], - "Grindcore": [ - "wormrot", - "terrorizer", - "rotten-sound", - "regional-justice-center", - "pig-destroyer", - "napalm-death", - "nails", - "knelt-rote", - "full-of-hell-&-merzbow", - "full-of-hell", - "fawn-limbs", - "dephosphorus", - "dead-in-the-dirt", - "cloud-rat", - "cianide", - "caustic-wound", - "carcass", - "the-body-&-full-of-hell" - ], - "Shoegaze": [ - "whirr", - "the-verve", - "trauma-ray", - "teenage-wrist", - "swervedriver", - "soul-blind", - "slowdive", - "slow-crush", - "ringo-deathstarr", - "ride", - "pale-saints", - "nothing", - "narrow-head", - "my-bloody-valentine", - "m83", - "just-mustard", - "ison", - "hum", - "greet-death", - "gleemer", - "glare", - "glare", - "drop-nineteens", - "deafcult", - "corps-fleur", - "cloakroom", - "chapterhouse", - "catherine-wheel", - "black-wing", - "beach-house", - "autolux", - "asobi-seksu", - "all-under-heaven", - "all-natural-lemon-&-lime-flavors", - "alcest", - "airiel" - ], - "Rock": [ - "webbed-wing", - "violent-femmes", - "superheaven", - "superbloom", - "sonic-youth", - "the-smashing-pumpkins", - "sebadoh", - "oasis", - "nirvana", - "kim-gordon-&-j-mascis", - "katatonia", - "jimmy-eat-world", - "foo-fighters", - "counting-crows", - "audioslave" - ], - "Progressive Black Metal": [ - "waste-of-space-orchestra", - "oranssi-pazuzu", - "negură-bunget", - "ivar-bjørnson-&-einar-selvik", - "ihsahn", - "enslaved" - ], - "Progressive Metal": [ - "virus", - "unexpect", - "ulver", - "sunn-o)))-&-ulver", - "sculptured", - "opeth", - "maudlin-of-the-well", - "mastodon", - "disillusion", - "dan-swanö", - "cynic", - "cult-of-luna", - "anathema" - ], - "Progressive Rock": [ - "unicorn", - "steven-wilson", - "riverside", - "porcupine-tree", - "nightingale", - "the-mars-volta", - "lunatic-soul" - ], - "Post Rock": [ - "unconditional-arms", - "this-will-destroy-you", - "sigur-rós", - "red-sparowes", - "of-the-vine", - "mono", - "mogwai", - "long-distance-calling-&-leech", - "long-distance-calling", - "if-these-trees-could-talk", - "godspeed-you!-black-emperor", - "god-is-an-astronaut", - "explosions-in-the-sky", - "caspian" - ], - "Hip-Hop": [ - "the-uncluded", - "run-the-jewels", - "pusha-t", - "mckinley-dixon", - "malibu-ken", - "kendrick-lamar", - "injury-reserve", - "hail-mary-mallon", - "el-p", - "death-grips", - "chance-the-rapper", - "aesop-rock" - ], - "Indie Pop": ["two-door-cinema-club", "the-smiths"], - "Soundtrack": ["trent-reznor-&-atticus-ross", "mogwai"], - "Country": [ - "trampled-by-turtles", - "trampled-by-turtles", - "sunday-valley", - "sturgill-simpson", - "johnny-cash", - "hiss-golden-messenger" - ], - "Post Hardcore": [ - "touché-amoré", - "thrice", - "self-defense-family", - "seahaven", - "rival-schools", - "quicksand", - "pitchfork", - "modern-color", - "militarie-gun", - "la-dispute", - "jawbox", - "hot-snakes", - "hell-is-for-heroes", - "gospel", - "fleshwater", - "envy", - "drug-church", - "drive-like-jehu", - "birds-in-row", - "big-garden", - "at-the-drive-in" - ], - "Singer/Songwriter": [ - "tom-waits", - "sun-kil-moon", - "soccer-mommy", - "sharon-van-etten", - "ryan-adams", - "phoebe-bridgers", - "kermit-obert", - "j-mascis", - "giles-corey", - "elliott-smith", - "dan-campbell", - "damien-jurado", - "city-and-colour", - "bob-dylan", - "ben-gibbard" - ], - "Grind": ["terrorizer"], - "Pop": ["taylor-swift"], - "Post Punk": [ - "talking-heads", - "soft-kill", - "shiner", - "savages", - "revenge-sf", - "the-psychedelic-furs", - "protomartyr", - "peter-&-the-test-tube-babies", - "new-order", - "modern-english", - "mission-of-burma", - "joy-division", - "the-jesus-and-mary-chain", - "idles", - "have-a-nice-life", - "hands-of-anne-boleyn", - "echo-&-the-bunnymen", - "damaged-bug", - "the-cure", - "cold-showers", - "the-chameleons", - "centaur" - ], - "Jazz": ["sword-&-sandals", "miles-davis", "mahavishnu-orchestra", "bohren-&-der-club-of-gore"], - "Screamo": ["state-faults", "senza", "ostraca", "loma-prieta", "frail-body"], - "Ambient": ["stars-of-the-lid", "eluvium", "the-dead-texan"], - "Post Metal": [ - "spotlights", - "russian-circles", - "mouth-of-the-architect", - "mono", - "lantlôs", - "jesu", - "isis", - "holy-fawn", - "envy", - "cult-of-luna" - ], - "Noise Rock": ["sonic-youth", "a-place-to-bury-strangers", "pink-&-brown", "metz", "the-drums"], - "Folk": ["simon-&-garfunkel"], - "Thrash Metal": ["sadus", "kreator"], - "Hardcore": ["refused"], - "Metalcore": [ - "portrayal-of-guilt", - "loathe", - "hacksaw-to-the-throat", - "glassing", - "fuming-mouth", - "converge-&-chelsea-wolfe", - "converge", - "chat-pile", - "cave-in" - ], - "Psychedelic Rock": ["osees", "john-dwyer", "bent-arcana"], - "Industrial": ["nine-inch-nails"], - "Synthpop": ["m83"], - "Electronic": ["lcd-soundsystem", "jesu-/-sun-kil-moon", "jamie-xx", "depeche-mode"], - "Doom Metal": ["katatonia"], - "Industrial Metal": ["godflesh"], - "Alternative Metal": ["deftones"], - "Melodic Death Metal": ["dark-tranquillity", "carcass", "at-the-gates", "arsis"] -} diff --git a/src/_data/json/mocks/music.json b/src/_data/json/mocks/music.json deleted file mode 100644 index 0ccb0334..00000000 --- a/src/_data/json/mocks/music.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "i.rXXXdmUa6Nme-1689970612847": { - "name": "Sacrificial Blood Oath In The Temple Of K'zadu", - "artist": "Gateway", - "album": "Galgendood", - "art": "https://store-033.blobstore.apple.com/sq-mq-us-033-000002/18/f1/a3/18f1a37a-8c9a-169a-5458-464aea20ce05/image?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20230721T202228Z&X-Amz-SignedHeaders=host&X-Amz-Expires=86400&X-Amz-Credential=MKIAU0HKO2RBEAT0UMZS%2F20230721%2Fstore-033%2Fs3%2Faws4_request&X-Amz-Signature=85790600221880597074559ed3674564f17ca3df6634d6fa15496baf7aca5d56", - "url": "https://rateyourmusic.com/search?searchtype=l&searchterm=Galgendood%20Gateway", - "id": "i.rXXXdmUa6Nme", - "playTime": 1689970612847, - "duration": 338808 - } -} diff --git a/src/_data/music.js b/src/_data/music.js index 495723d5..47cbc955 100644 --- a/src/_data/music.js +++ b/src/_data/music.js @@ -1,124 +1,8 @@ -const { S3Client, GetObjectCommand, PutObjectCommand } = require('@aws-sdk/client-s3') -const _ = require('lodash') -const artistGenres = require('./json/artist-genres.json') -const mockedMusic = require('./json/mocks/music.json') -const { getReadableData } = require('../utils/aws') -const { getKeyByValue } = require('../utils/arrays') -const { aliasArtist, sanitizeMedia } = require('../utils/media') +const { AssetCache } = require('@11ty/eleventy-fetch') +const { aliasArtist, sanitizeMedia, sortByPlays } = require('../utils/media') const { titleCase } = require('../utils/grammar') -const getTracksOneHour = (tracks) => { - const TIMER_CEILING = 3600000 // 1 hour - const tracksOneHour = [] - let trackIndex = 0 - let trackTimer = 0 - - while (trackTimer < TIMER_CEILING) { - if (!tracks[trackIndex]) return tracksOneHour - trackTimer = trackTimer + parseInt(tracks[trackIndex].duration) - tracksOneHour.push(tracks[trackIndex]) - trackIndex++ - } - - return tracksOneHour -} - -const diffTracks = (cache, tracks) => { - const trackCompareSet = Object.values(tracks) - const cacheCompareSet = _.orderBy(Object.values(cache), ['time'], ['desc']) - const diffedTracks = {} - const cacheCompareOneHour = getTracksOneHour(cacheCompareSet) - const comparedTracks = _.differenceWith(trackCompareSet, cacheCompareOneHour, (a, b) => - _.isEqual(a.id, b.id) - ) - - for (let i = 0; i < comparedTracks.length; i++) - diffedTracks[`${comparedTracks[i]?.id}-${comparedTracks[i].playTime}`] = comparedTracks[i] - - return diffedTracks -} - -const formatTracks = (tracks) => { - let formattedTracks = {} - let time = new Date().getTime() - - Object.values(tracks).forEach((track) => { - const artistFormatted = titleCase(aliasArtist(track.attributes['artistName'])) - const albumFormatted = titleCase(sanitizeMedia(track.attributes['albumName'])) - const trackFormatted = sanitizeMedia(track.attributes['name']) - formattedTracks[`${track.id}-${time}`] = { - name: trackFormatted, - artist: artistFormatted, - album: albumFormatted, - art: track.attributes.artwork.url.replace('{w}', '300').replace('{h}', '300'), - url: - track['relationships'] && track['relationships'].albums.data.length > 0 - ? `https://song.link/${track['relationships'].albums.data.pop().attributes.url}` - : `https://rateyourmusic.com/search?searchtype=l&searchterm=${encodeURI( - albumFormatted - )}%20${encodeURI(artistFormatted)}`, - id: track.id, - playTime: time - parseInt(track.attributes['durationInMillis']), - duration: parseInt(track.attributes['durationInMillis']), - } - }) - return formattedTracks -} - -const deriveCharts = (tracks) => { - const charts = { - artists: {}, - albums: {}, - } - const tracksForLastWeek = Object.values(tracks).filter((track) => { - const currentDate = new Date() - const currentDateTime = new Date().getTime() - const lastWeek = new Date(currentDate.setDate(currentDate.getDate() - 7)) - const lastWeekDateTime = lastWeek.getTime() - const trackDateTime = new Date(track.playTime).getTime() - return trackDateTime <= currentDateTime && trackDateTime > lastWeekDateTime - }) - - tracksForLastWeek.forEach((track) => { - if (!charts.artists[track.artist]) { - charts.artists[track.artist] = { - artist: track.artist, - genre: getKeyByValue(artistGenres, track.artist.replace(/\s+/g, '-').toLowerCase()), - url: `https://rateyourmusic.com/search?searchterm=${encodeURI(track.artist)}`, - plays: 1, - } - } else { - charts.artists[track.artist].plays++ - } - - if (!charts.albums[track.album]) { - charts.albums[track.album] = { - name: track.album, - artist: track.artist, - art: track.art, - url: track.url, - plays: 1, - } - } else { - charts.albums[track.album].plays++ - } - }) - - return charts -} - module.exports = async function () { - const client = new S3Client({ - credentials: { - accessKeyId: process.env.ACCESS_KEY_WASABI, - secretAccessKey: process.env.SECRET_KEY_WASABI, - }, - endpoint: { - url: 'https://s3.us-west-1.wasabisys.com', - }, - region: 'us-west-1', - }) - const WASABI_BUCKET = process.env.BUCKET_WASABI const APPLE_BEARER = process.env.API_BEARER_APPLE_MUSIC const APPLE_MUSIC_TOKEN = process.env.API_TOKEN_APPLE_MUSIC const APPLE_TOKEN_RESPONSE = await fetch(process.env.APPLE_RENEW_TOKEN_URL, { @@ -133,15 +17,21 @@ module.exports = async function () { .then((data) => data.json()) .catch() const APPLE_TOKEN = APPLE_TOKEN_RESPONSE['music-token'] + const asset = new AssetCache('recent_tracks_data') const PAGE_SIZE = 30 - const TIMER_CEILING = 3600000 // 1 hour - let charts + const PAGES = 10 + const charts = { + artists: {}, + albums: {}, + tracks: {}, + } let CURRENT_PAGE = 0 - let trackTimer = 0 let res = [] - let cachedTracks = mockedMusic + let hasNextPage = true - while (trackTimer < TIMER_CEILING) { + if (asset.isCacheValid('1h')) return await asset.getCachedValue() + + while (CURRENT_PAGE < PAGES && hasNextPage) { const URL = `https://api.music.apple.com/v1/me/recent/played/tracks?limit=${PAGE_SIZE}&offset=${ PAGE_SIZE * CURRENT_PAGE }&include[songs]=albums&extend=artistUrl` @@ -154,45 +44,54 @@ module.exports = async function () { }) .then((data) => data.json()) .catch() - - tracks.data.forEach((track) => { - trackTimer = trackTimer + parseInt(track.attributes['durationInMillis']) - if (trackTimer >= TIMER_CEILING) return - res.push(track) - }) + if (!tracks.next) hasNextPage = false + if (tracks.data.length) res = [...res, ...tracks.data] CURRENT_PAGE++ } + res.forEach((track) => { + const formattedArtist = titleCase(aliasArtist(track.attributes['artistName'])) + const formattedAlbum = titleCase(sanitizeMedia(track.attributes['albumName'])) + const formattedTrack = sanitizeMedia(track.attributes['name']) - if (process.env.ELEVENTY_PRODUCTION === 'true') { - const cachedTracksOutput = await client.send( - new GetObjectCommand({ - Bucket: WASABI_BUCKET, - Key: 'music.json', - }) - ) - const cachedTracksData = getReadableData(cachedTracksOutput.Body) - cachedTracks = await cachedTracksData.then((tracks) => JSON.parse(tracks)).catch() - } + if (!charts.artists[formattedArtist]) { + charts.artists[formattedArtist] = { + artist: formattedArtist, + url: `https://rateyourmusic.com/search?searchterm=${encodeURI(formattedArtist)}`, + plays: 1, + } + } else { + charts.artists[formattedArtist].plays++ + } - const diffedTracks = diffTracks(cachedTracks, formatTracks(res)) - const updatedCache = { - ...cachedTracks, - ...diffedTracks, - } - - charts = deriveCharts(updatedCache) - charts.artists = _.orderBy(Object.values(charts.artists), ['plays'], ['desc']).splice(0, 8) - charts.albums = _.orderBy(Object.values(charts.albums), ['plays'], ['desc']).splice(0, 8) - - if (!_.isEmpty(diffedTracks) && process.env.ELEVENTY_PRODUCTION === 'true') { - await client.send( - new PutObjectCommand({ - Bucket: WASABI_BUCKET, - Key: 'music.json', - Body: JSON.stringify(updatedCache), - }) - ) - } + if (!charts.albums[formattedAlbum]) { + charts.albums[formattedAlbum] = { + name: formattedAlbum, + artist: formattedArtist, + art: track.attributes.artwork.url.replace('{w}', '300').replace('{h}', '300'), + url: track['relationships'] + ? `https://song.link/${track['relationships'].albums.data.pop().attributes.url}` + : `https://rateyourmusic.com/search?searchtype=l&searchterm=${encodeURI( + formattedAlbum + )}%20${encodeURI(formattedArtist)}`, + plays: 1, + } + } else { + charts.albums[formattedAlbum].plays++ + } + if (!charts.tracks[formattedTrack]) { + charts.tracks[formattedTrack] = { + name: formattedTrack, + artist: formattedArtist, + plays: 1, + } + } else { + charts.tracks[formattedTrack].plays++ + } + }) + charts.artists = sortByPlays(charts.artists).splice(0, 8) + charts.albums = sortByPlays(charts.albums).splice(0, 8) + charts.tracks = sortByPlays(charts.tracks).splice(0, 5) + await asset.save(charts, 'json') return charts } diff --git a/src/_includes/partials/now/artists.liquid b/src/_includes/partials/now/artists.liquid index 0446930c..442f673a 100644 --- a/src/_includes/partials/now/artists.liquid +++ b/src/_includes/partials/now/artists.liquid @@ -11,7 +11,7 @@
{{ artist.artist }}
- {{ artist.genre }} + {{ artist.plays }} plays
{%- capture artistImg %}{{ artist.artist | artist }}{% endcapture -%} diff --git a/src/utils/arrays.js b/src/utils/arrays.js deleted file mode 100644 index dad074c3..00000000 --- a/src/utils/arrays.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - getKeyByValue: (object, value) => Object.keys(object).find((key) => object[key].includes(value)), -} diff --git a/src/utils/media.js b/src/utils/media.js index 8fed9b45..afa487d7 100644 --- a/src/utils/media.js +++ b/src/utils/media.js @@ -29,4 +29,5 @@ module.exports = { /-\s*(?:single|ep)\s*|(\[|\()(Deluxe Edition|Special Edition|Remastered|Full Dynamic Range Edition|Anniversary Edition)(\]|\))/gi return media.replace(denyList, '').trim() }, + sortByPlays: (array) => Object.values(array).sort((a, b) => b.plays - a.plays), }