feat: import artist metadata
This commit is contained in:
parent
1368b738ae
commit
96bff400e8
4 changed files with 235 additions and 12 deletions
41
package-lock.json
generated
41
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "coryd.dev",
|
||||
"version": "2.8.5",
|
||||
"version": "2.9.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "coryd.dev",
|
||||
"version": "2.8.5",
|
||||
"version": "2.9.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@11ty/eleventy-fetch": "4.0.1",
|
||||
|
@ -29,6 +29,7 @@
|
|||
"html-minifier-terser": "^7.2.0",
|
||||
"html-to-text": "^9.0.5",
|
||||
"http-proxy-middleware": "3.0.3",
|
||||
"i18n-iso-countries": "7.13.0",
|
||||
"ics": "^3.8.1",
|
||||
"linkedom": "0.18.5",
|
||||
"luxon": "^3.5.0",
|
||||
|
@ -36,7 +37,7 @@
|
|||
"markdown-it-anchor": "^9.2.0",
|
||||
"markdown-it-footnote": "^4.0.0",
|
||||
"markdown-it-prism": "^2.3.0",
|
||||
"postcss": "^8.4.47",
|
||||
"postcss": "^8.4.48",
|
||||
"postcss-import": "^16.1.0",
|
||||
"postcss-import-ext-glob": "^2.1.1",
|
||||
"rimraf": "^6.0.1",
|
||||
|
@ -1166,9 +1167,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001679",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001679.tgz",
|
||||
"integrity": "sha512-j2YqID/YwpLnKzCmBOS4tlZdWprXm3ZmQLBH9ZBXFOhoxLA46fwyBvx6toCBWBmnuwUY/qB3kEU6gFx8qgCroA==",
|
||||
"version": "1.0.30001680",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz",
|
||||
"integrity": "sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1667,6 +1668,13 @@
|
|||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/diacritics": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/diacritics/-/diacritics-1.3.0.tgz",
|
||||
"integrity": "sha512-wlwEkqcsaxvPJML+rDh/2iS824jbREk6DUMUKkEaSlxdYHeS43cClJtsWglvw2RfeXGm6ohKDqsXteJ5sP5enA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dom-serializer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
||||
|
@ -2700,6 +2708,19 @@
|
|||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/i18n-iso-countries": {
|
||||
"version": "7.13.0",
|
||||
"resolved": "https://registry.npmjs.org/i18n-iso-countries/-/i18n-iso-countries-7.13.0.tgz",
|
||||
"integrity": "sha512-pVh4CjdgAHZswI98hzG+1BItQlsQfR+yGDsjDISoWIV/jHDAvCmSyZ5vj2YWwAjfVZ8/BhBDqWcFvuGOyHe4vg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"diacritics": "1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
|
@ -3833,9 +3854,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.47",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
|
||||
"integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
|
||||
"version": "8.4.48",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.48.tgz",
|
||||
"integrity": "sha512-GCRK8F6+Dl7xYniR5a4FYbpBzU8XnZVeowqsQFYdcXuSbChgiks7qybSkbvnaeqv0G0B+dd9/jJgH8kkLDQeEA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
@ -3854,7 +3875,7 @@
|
|||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.7",
|
||||
"picocolors": "^1.1.0",
|
||||
"picocolors": "^1.1.1",
|
||||
"source-map-js": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "coryd.dev",
|
||||
"version": "2.8.5",
|
||||
"version": "2.9.0",
|
||||
"description": "The source for my personal site. Built using 11ty (and other tools).",
|
||||
"type": "module",
|
||||
"engines": {
|
||||
|
@ -46,6 +46,7 @@
|
|||
"html-minifier-terser": "^7.2.0",
|
||||
"html-to-text": "^9.0.5",
|
||||
"http-proxy-middleware": "3.0.3",
|
||||
"i18n-iso-countries": "7.13.0",
|
||||
"ics": "^3.8.1",
|
||||
"linkedom": "0.18.5",
|
||||
"luxon": "^3.5.0",
|
||||
|
@ -53,7 +54,7 @@
|
|||
"markdown-it-anchor": "^9.2.0",
|
||||
"markdown-it-footnote": "^4.0.0",
|
||||
"markdown-it-prism": "^2.3.0",
|
||||
"postcss": "^8.4.47",
|
||||
"postcss": "^8.4.48",
|
||||
"postcss-import": "^16.1.0",
|
||||
"postcss-import-ext-glob": "^2.1.1",
|
||||
"rimraf": "^6.0.1",
|
||||
|
|
186
workers/artist-import/index.js
Normal file
186
workers/artist-import/index.js
Normal file
|
@ -0,0 +1,186 @@
|
|||
import slugify from "slugify";
|
||||
import countries from "i18n-iso-countries";
|
||||
|
||||
countries.registerLocale(require("i18n-iso-countries/langs/en.json"));
|
||||
|
||||
function sanitizeMediaString(str) {
|
||||
const sanitizedString = str
|
||||
.normalize("NFD")
|
||||
.replace(/[\u0300-\u036f\u2010\-\.\?\(\)\[\]\{\}]/g, "")
|
||||
.replace(/\.{3}/g, "");
|
||||
return slugify(sanitizedString, {
|
||||
replacement: "-",
|
||||
remove: /[#,&,+()$~%.'\":*?<>{}]/g,
|
||||
lower: true,
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request, env) {
|
||||
const directusUrl = env["DIRECTUS_URL"];
|
||||
const directusToken = env["DIRECTUS_API_TOKEN"];
|
||||
const artistImportToken = env["ARTIST_IMPORT_TOKEN"];
|
||||
const artistFlowID = env["ARTIST_FLOW_ID"];
|
||||
const albumFlowID = env["ALBUM_FLOW_ID"];
|
||||
const placeholderImageId = "4cef75db-831f-4f5d-9333-79eaa5bb55ee";
|
||||
const requestUrl = new URL(request["url"]);
|
||||
const providedToken = requestUrl.searchParams.get("token");
|
||||
|
||||
if (!providedToken || providedToken !== artistImportToken) {
|
||||
return new Response("Unauthorized", { status: 401 });
|
||||
}
|
||||
|
||||
async function saveToDirectus(endpoint, payload) {
|
||||
const response = await fetch(`${directusUrl}/items/${endpoint}`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${directusToken}`,
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
const data = await response.json();
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
data["errors"]
|
||||
? data["errors"][0]["message"]
|
||||
: "Failed to save to Directus"
|
||||
);
|
||||
}
|
||||
return data["data"];
|
||||
}
|
||||
|
||||
async function findGenreIdByName(genreName) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${directusUrl}/items/genres?filter[name][_eq]=${encodeURIComponent(
|
||||
genreName.toLowerCase()
|
||||
)}`,
|
||||
{ headers: { Authorization: `Bearer ${directusToken}` } }
|
||||
);
|
||||
const data = await response.json();
|
||||
return data["data"].length > 0 ? data["data"][0]["id"] : null;
|
||||
} catch (error) {
|
||||
console.error("Error fetching genre ID:", error["message"]);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const artistId = requestUrl.searchParams.get("artist_id");
|
||||
if (!artistId)
|
||||
return new Response("artist_id parameter is required", { status: 400 });
|
||||
|
||||
let artistData;
|
||||
try {
|
||||
const artistResponse = await fetch(
|
||||
`${directusUrl}/flows/trigger/${artistFlowID}?artist_id=${artistId}&import_token=${artistImportToken}`,
|
||||
{ headers: { Authorization: `Bearer ${directusToken}` } }
|
||||
);
|
||||
artistData = await artistResponse.json();
|
||||
artistData =
|
||||
artistData["get_artist_data"]["data"]["MediaContainer"]["Metadata"][0];
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Error fetching artist data from Directus flow:",
|
||||
error["message"]
|
||||
);
|
||||
return new Response("Error fetching artist data", { status: 500 });
|
||||
}
|
||||
|
||||
const artistName = artistData["title"] || "";
|
||||
const artistKey = sanitizeMediaString(artistName);
|
||||
const countryName = artistData["Country"]
|
||||
? artistData["Country"][0]?.["tag"]
|
||||
: "";
|
||||
const countryIsoCode = countries.getAlpha2Code(countryName, "en") || "";
|
||||
const slug = `/music/artists/${artistKey}-${countryName.toLowerCase()}`;
|
||||
const description = artistData["summary"] || "";
|
||||
const mbid = artistData["Guid"]?.[0]?.["id"]?.replace("mbid://", "") || "";
|
||||
|
||||
const genreNames = artistData["Genre"]
|
||||
? artistData["Genre"].map((g) => g["tag"].toLowerCase())
|
||||
: [];
|
||||
let genreId = null;
|
||||
for (const genreName of genreNames) {
|
||||
genreId = await findGenreIdByName(genreName);
|
||||
if (genreId) break;
|
||||
}
|
||||
|
||||
const artistPayload = {
|
||||
name: artistName,
|
||||
name_string: artistName,
|
||||
slug: slug,
|
||||
description: description,
|
||||
mbid: mbid,
|
||||
tentative: true,
|
||||
genres: genreId,
|
||||
country: countryIsoCode,
|
||||
art: placeholderImageId,
|
||||
};
|
||||
|
||||
let insertedArtist;
|
||||
try {
|
||||
insertedArtist = await saveToDirectus("artists", artistPayload);
|
||||
} catch (error) {
|
||||
console.error("Error saving artist:", error["message"]);
|
||||
return new Response("Error saving artist", { status: 500 });
|
||||
}
|
||||
|
||||
let albumData;
|
||||
try {
|
||||
const albumResponse = await fetch(
|
||||
`${directusUrl}/flows/trigger/${albumFlowID}?artist_id=${artistId}&import_token=${artistImportToken}`,
|
||||
{ headers: { Authorization: `Bearer ${directusToken}` } }
|
||||
);
|
||||
albumData = await albumResponse.json();
|
||||
albumData =
|
||||
albumData["get_album_data"]["data"]["MediaContainer"]["Metadata"];
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Error fetching album data from Directus flow:",
|
||||
error["message"]
|
||||
);
|
||||
return new Response("Error fetching album data", { status: 500 });
|
||||
}
|
||||
|
||||
for (const album of albumData) {
|
||||
const albumName = album["title"] || "";
|
||||
const albumKey = `${artistKey}-${sanitizeMediaString(albumName)}`;
|
||||
const albumSlug = `/music/albums/${albumKey}`;
|
||||
const albumDescription = album["summary"] || "";
|
||||
const albumReleaseDate = album["originallyAvailableAt"] || "";
|
||||
const albumReleaseYear = albumReleaseDate
|
||||
? new Date(albumReleaseDate).getFullYear()
|
||||
: null;
|
||||
const albumGenres = album["Genre"]
|
||||
? album["Genre"].map((g) => g["tag"])
|
||||
: [];
|
||||
const albumMbid =
|
||||
album["Guid"]?.[0]?.["id"]?.replace("mbid://", "") || null;
|
||||
|
||||
const albumPayload = {
|
||||
name: albumName,
|
||||
key: albumKey,
|
||||
slug: albumSlug,
|
||||
mbid: albumMbid,
|
||||
description: albumDescription,
|
||||
release_year: albumReleaseYear,
|
||||
artist: insertedArtist["id"],
|
||||
artist_name: artistName,
|
||||
genres: albumGenres,
|
||||
art: placeholderImageId,
|
||||
tentative: true,
|
||||
};
|
||||
|
||||
try {
|
||||
await saveToDirectus("albums", albumPayload);
|
||||
} catch (error) {
|
||||
console.error("Error saving album:", error["message"]);
|
||||
}
|
||||
}
|
||||
|
||||
return new Response("Artist and albums synced successfully", {
|
||||
status: 200,
|
||||
});
|
||||
},
|
||||
};
|
15
workers/artist-import/wrangler.template.toml
Normal file
15
workers/artist-import/wrangler.template.toml
Normal file
|
@ -0,0 +1,15 @@
|
|||
name = "import-artist-worker"
|
||||
main = "./index.js"
|
||||
compatibility_date = "2023-01-01"
|
||||
|
||||
account_id = "${CF_ACCOUNT_ID}"
|
||||
workers_dev = true
|
||||
|
||||
[observability]
|
||||
enabled = true
|
||||
|
||||
[env.production]
|
||||
name = "import-artist-worker-production"
|
||||
routes = [
|
||||
{ pattern = "coryd.dev/api/import-artist*", zone_id = "${CF_ZONE_ID}" }
|
||||
]
|
Reference in a new issue