chore: simpler charts
This commit is contained in:
parent
e8b8bb24b5
commit
eaa43c41af
7 changed files with 59 additions and 1092 deletions
3
.env
3
.env
|
@ -1,6 +1,3 @@
|
|||
ACCESS_KEY_WASABI=
|
||||
SECRET_KEY_WASABI=
|
||||
BUCKET_WASABI=
|
||||
API_KEY_PLAUSIBLE=
|
||||
API_KEY_TRAKT=
|
||||
API_KEY_WEBMENTIONS_CORYD_DEV=
|
||||
|
|
|
@ -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"]
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<div class="absolute left-1 bottom-2 drop-shadow-md">
|
||||
<div class="px-1 text-xs font-bold text-white line-clamp-2">{{ artist.artist }}</div>
|
||||
<div class="px-1 text-xs text-white">
|
||||
{{ artist.genre }}
|
||||
{{ artist.plays }} plays
|
||||
</div>
|
||||
</div>
|
||||
{%- capture artistImg %}{{ artist.artist | artist }}{% endcapture -%}
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
module.exports = {
|
||||
getKeyByValue: (object, value) => Object.keys(object).find((key) => object[key].includes(value)),
|
||||
}
|
|
@ -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),
|
||||
}
|
||||
|
|
Reference in a new issue