import { createClient } from "@supabase/supabase-js"; import slugify from "slugify"; const sanitizeMediaString = (str) => { const sanitizedString = str .normalize("NFD") .replace(/[\u0300-\u036f\u2010\-\.\?\(\)\[\]\{\}]/g, "") .replace(/\.{3}/g, ""); return slugify(sanitizedString, { replacement: "-", remove: /[#,&,+()$~%.'":*?<>{}]/g, lower: true, }); }; const sendEmail = async (subject, text, authHeader, maxRetries = 3) => { const emailData = new URLSearchParams({ from: "coryd.dev ", to: "hi@coryd.dev", subject: subject, text: text, }).toString(); let attempt = 0; let success = false; while (attempt < maxRetries && !success) { attempt++; try { const response = await fetch("https://api.forwardemail.net/v1/emails", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", Authorization: authHeader, }, body: emailData, }); if (!response.ok) { const responseText = await response.text(); console.error( `Attempt ${attempt}: Email API response error:`, response.status, responseText ); throw new Error(`Failed to send email: ${responseText}`); } console.log("Email sent successfully on attempt", attempt); success = true; } catch (error) { console.error(`Attempt ${attempt}: Error sending email:`, error.message); if (attempt < maxRetries) { console.log( `Retrying email send (attempt ${attempt + 1}/${maxRetries})...` ); } else { console.error("All attempts to send email failed."); } } } return success; }; export default { async fetch(request, env) { const supabaseUrl = env.SUPABASE_URL; const supabaseKey = env.SUPABASE_KEY; const FORWARDEMAIL_API_KEY = env.FORWARDEMAIL_API_KEY; const ACCOUNT_ID_PLEX = env.ACCOUNT_ID_PLEX; const supabase = createClient(supabaseUrl, supabaseKey); const authHeader = "Basic " + btoa(`${FORWARDEMAIL_API_KEY}:`); const url = new URL(request.url); const params = url.searchParams; const id = params.get("id"); if (!id) return new Response(JSON.stringify({ status: "Bad request" }), { headers: { "Content-Type": "application/json" }, }); if (id !== ACCOUNT_ID_PLEX) return new Response(JSON.stringify({ status: "Forbidden" }), { headers: { "Content-Type": "application/json" }, }); const contentType = request.headers.get("Content-Type") || ""; if (!contentType.includes("multipart/form-data")) return new Response( JSON.stringify({ status: "Bad request", message: "Invalid Content-Type. Expected multipart/form-data.", }), { headers: { "Content-Type": "application/json" } } ); try { const data = await request.formData(); const payload = JSON.parse(data.get("payload")); if (payload?.event === "media.scrobble") { const artistName = payload["Metadata"]["grandparentTitle"]; const albumName = payload["Metadata"]["parentTitle"]; const trackName = payload["Metadata"]["title"]; const listenedAt = Math.floor(Date.now() / 1000); const artistKey = sanitizeMediaString(artistName); const albumKey = `${artistKey}-${sanitizeMediaString(albumName)}`; let { data: artistData, error: artistError } = await supabase .from("artists") .select("*") .ilike("name_string", artistName) .single(); if (artistError && artistError.code === "PGRST116") { const { error: insertArtistError } = await supabase .from("artists") .insert([ { mbid: null, art: "4cef75db-831f-4f5d-9333-79eaa5bb55ee", name: artistName, slug: "/music", tentative: true, total_plays: 0, }, ]); if (insertArtistError) { console.error( "Error inserting artist: ", insertArtistError.message ); return new Response( JSON.stringify({ status: "error", message: insertArtistError.message, }), { headers: { "Content-Type": "application/json" } } ); } await sendEmail( "New tentative artist record", `A new tentative artist record was inserted:\n\nArtist: ${artistName}\nKey: ${artistKey}`, authHeader ); ({ data: artistData, error: artistError } = await supabase .from("artists") .select("*") .ilike("name_string", artistName) .single()); } if (artistError) { console.error("Error fetching artist:", artistError.message); return new Response( JSON.stringify({ status: "error", message: artistError.message }), { headers: { "Content-Type": "application/json" } } ); } let { data: albumData, error: albumError } = await supabase .from("albums") .select("*") .ilike("key", albumKey) .single(); if (albumError && albumError.code === "PGRST116") { const { error: insertAlbumError } = await supabase .from("albums") .insert([ { mbid: null, art: "4cef75db-831f-4f5d-9333-79eaa5bb55ee", key: albumKey, name: albumName, tentative: true, total_plays: 0, artist: artistData.id, }, ]); if (insertAlbumError) { console.error("Error inserting album:", insertAlbumError.message); return new Response( JSON.stringify({ status: "error", message: insertAlbumError.message, }), { headers: { "Content-Type": "application/json" } } ); } await sendEmail( "New tentative album record", `A new tentative album record was inserted:\n\nAlbum: ${albumName}\nKey: ${albumKey}\nArtist: ${artistName}`, authHeader ); ({ data: albumData, error: albumError } = await supabase .from("albums") .select("*") .ilike("key", albumKey) .single()); } if (albumError) { console.error("Error fetching album:", albumError.message); return new Response( JSON.stringify({ status: "error", message: albumError.message }), { headers: { "Content-Type": "application/json" } } ); } const { error: listenError } = await supabase.from("listens").insert([ { artist_name: artistData["name_string"] || artistName, album_name: albumData["name"] || albumName, track_name: trackName, listened_at: listenedAt, album_key: albumKey, }, ]); if (listenError) { console.error("Error inserting listen:", listenError.message); return new Response( JSON.stringify({ status: "error", message: listenError.message }), { headers: { "Content-Type": "application/json" } } ); } console.log("Listen record inserted successfully"); } return new Response(JSON.stringify({ status: "success" }), { headers: { "Content-Type": "application/json" }, }); } catch (e) { console.error("Error processing request:", e.message); return new Response( JSON.stringify({ status: "error", message: e.message }), { headers: { "Content-Type": "application/json" } } ); } }, };