dthyresson-playmeasong.web.val.run
Readme

Play me a song

Search and if we find it, it'll open the song in Spotify!

Feeling Lucky? Play a random song.

Like a band? Play one of their tracks.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource npm:hono@3/jsx */
import { getSpotifyTrackUrl } from "https://esm.town/v/dthyresson/getSpotifyTrackUrl";
import { perform as refreshTracks, refreshTrackFromBlobKey } from "https://esm.town/v/dthyresson/processTrackBlobsJob";
import processTrackBlobsJob from "https://esm.town/v/dthyresson/processTrackBlobsJob";
import { sqlite } from "https://esm.town/v/std/sqlite";
import tailwindURL from "https://esm.town/v/stevekrouse/tailwindURL?v=2";
import { asc, desc, not, sql } from "npm:drizzle-orm";
import { drizzle } from "npm:drizzle-orm/libsql";
import { integer, sqliteTable, text } from "npm:drizzle-orm/sqlite-core";
import { Hono } from "npm:hono";
type Track = {
id: number | null;
artist: string;
title: string;
track_id: string | null;
icon_url: string | null;
url: string | null;
is_hidden: number | null;
};
const db = drizzle(sqlite as any);
const tracks = sqliteTable("tracks", {
id: integer("id").primaryKey(),
track_id: text("track_id").notNull(),
artist: text("artist"),
title: text("title"),
url: text("url"),
image_url: text("image_url"),
thumbnail_url: text("thumbnail_url"),
icon_url: text("icon_url"),
duration_ms: text("duration_ms"),
duration: text("duration"),
is_hidden: integer("is_hidden", { mode: "boolean" }),
});
const favoriteSongSearches = sqliteTable("favorite_song_searches", {
id: integer("id").primaryKey(),
track_id: text("track_id").notNull(),
artist: text("artist"),
title: text("title"),
is_hidden: integer("is_hidden", { mode: "boolean" }),
});
async function getSongCatalog() {
return await db.select().from(tracks).where(not(tracks.is_hidden)).orderBy(tracks.artist, tracks.title);
}
async function getRecentSearches() {
return await db.select().from(favoriteSongSearches).where(not(favoriteSongSearches.is_hidden)).orderBy(
desc(favoriteSongSearches.id),
);
}
async function getArtists() {
return await db.selectDistinct({ artist: tracks.artist }).from(tracks).where(not(tracks.is_hidden)).orderBy(
asc(tracks.artist),
);
}
const app = new Hono();
app.get("/refresh", (c) => {
refreshTracks();
return c.redirect("/");
});
app.get("/about", (c) => {
return c.redirect("https://www.val.town/v/dthyresson/playMeASong");
});
app.get("/", async (c) => {
let errorMessage = "";
const artist = c.req.query("artist") && c.req.query("artist").trim();
const title = c.req.query("title") && c.req.query("title").trim();
if (artist && title) {
try {
const spotifyInfo = await getSpotifyTrackUrl(artist, title, { format: "web" });
if (spotifyInfo) {
const { trackUrl, id, artists, name } = spotifyInfo;
if (trackUrl) {
const t = { artist: artists[0].name, title: name, track_id: id, is_hidden: 0 } as Track;
await db.insert(favoriteSongSearches)
.values(t)
.onConflictDoNothing();
const key = `spotify_track_id:${id}`;
await refreshTrackFromBlobKey(key);
return c.redirect(trackUrl);
}
}
errorMessage = `Could not get track for ${artist} and ${title}`;
} catch (e) {
errorMessage = `Could not get track. Might be a 429. ${e.message}`;
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
Comments
Nobody has commented on this val yet: be the first!
July 29, 2024