All checks were successful
Deploy production / deploy (push) Successful in 2m37s
- music schema: artists/albums/tracks (+album art), plays with source + dedupe, now_playing, spotify_auth, import_state; plays_deduped view hides Last.fm scrobbles Spotify also has - in-app Spotify poller (60s, leased) started from hooks init; no new role or env needed - /api/spotify/connect + callback (owner only), /trackers/music public stats - scripts: music-poll, music-import-lastfm (resumable), music-import-history - deploy/music-collector: optional host timer with its own role
150 lines
3.9 KiB
TypeScript
150 lines
3.9 KiB
TypeScript
// Real projects (tools and experiments). Personal data trackers live in `trackers`.
|
|
export const projects = [
|
|
{
|
|
name: 'vash esports',
|
|
subtitle: 'the best way to play osu!',
|
|
href: 'https://esports.vash.software',
|
|
external: true
|
|
},
|
|
{
|
|
name: 'osu! map finder',
|
|
subtitle: 'find osu! maps for Spotify songs/albums/playlists',
|
|
href: '/projects/spotify-beatmap-finder',
|
|
external: false
|
|
},
|
|
{
|
|
name: 'derby lens (warning: AI slop)',
|
|
subtitle: 'estimate odds for horse betting',
|
|
href: '/projects/derby',
|
|
external: false
|
|
},
|
|
{
|
|
name: 'world opinions (warning: AI slop)',
|
|
subtitle: 'ideologies, values and biases across countries',
|
|
href: '/projects/world-opinions',
|
|
external: false
|
|
},
|
|
{
|
|
name: 'language learner (warning: AI slop)',
|
|
subtitle: 'Dutch, Korean and English, from zero to fluent',
|
|
href: '/projects/language-learner',
|
|
external: false
|
|
}
|
|
] as const;
|
|
|
|
// Trackers, in display order. Names are short single nouns. Most keep their existing
|
|
// /projects/... URLs (no moves, so no redirects needed). `private` trackers are shown in the
|
|
// header, home page and /trackers only to the logged-in owner (layout data `isOwner`), never
|
|
// in the sitemap; their routes 404 for everyone else.
|
|
const allTrackers = [
|
|
{
|
|
name: 'tasks',
|
|
subtitle: 'private: to-dos, goals and my TickTick history',
|
|
href: '/trackers/tasks',
|
|
external: false,
|
|
private: true
|
|
},
|
|
{
|
|
name: 'money',
|
|
subtitle: 'a manual ledger for accounts, bills and utilities',
|
|
href: '/trackers/money',
|
|
external: false,
|
|
private: false
|
|
},
|
|
{
|
|
name: 'books',
|
|
subtitle: 'my library: what I am reading, have read and want to read',
|
|
href: '/trackers/books',
|
|
external: false,
|
|
private: false
|
|
},
|
|
{
|
|
name: 'cars',
|
|
subtitle: 'a car pokedex: every brand and model, with Dutch and US data',
|
|
href: '/trackers/cars',
|
|
external: false,
|
|
private: false
|
|
},
|
|
{
|
|
name: 'music',
|
|
subtitle: 'everything I listen to: top artists, tracks, albums and streaks',
|
|
href: '/trackers/music',
|
|
external: false,
|
|
private: false
|
|
},
|
|
{
|
|
name: 'health',
|
|
subtitle: 'private: body, workouts, sleep, meds and vaccinations',
|
|
href: '/trackers/health',
|
|
external: false,
|
|
private: true
|
|
},
|
|
{
|
|
name: 'groceries',
|
|
subtitle: 'private: supermarket products, favorites and prices per store',
|
|
href: '/trackers/groceries',
|
|
external: false,
|
|
private: true
|
|
},
|
|
{
|
|
name: 'houses',
|
|
subtitle: 'private: rentals around Gouda, applications and alerts',
|
|
href: '/trackers/houses',
|
|
external: false,
|
|
private: true
|
|
},
|
|
{
|
|
name: 'energy',
|
|
subtitle: 'Dutch electricity mix, utilization and gas storage',
|
|
href: '/projects/energy',
|
|
external: false,
|
|
private: false
|
|
},
|
|
{
|
|
name: 'games',
|
|
subtitle: 'every pokemon in every game, and call of duty camo mastery',
|
|
href: '/projects/game-tracker',
|
|
external: false,
|
|
private: false
|
|
},
|
|
|
|
{
|
|
name: 'skins',
|
|
subtitle: 'Counter-Strike skin market prices across time',
|
|
href: '/projects/cs-prices',
|
|
external: false,
|
|
private: false
|
|
},
|
|
{
|
|
name: 'pets',
|
|
subtitle: 'animaldex: add neighborhood pets to the world map',
|
|
href: '/projects/animaldex',
|
|
external: false,
|
|
private: false
|
|
},
|
|
{
|
|
name: 'lists',
|
|
subtitle: 'books, games, places and other things worth keeping',
|
|
href: '/lists',
|
|
external: false,
|
|
private: false
|
|
}
|
|
] as const;
|
|
|
|
type Tracker = { name: string; subtitle: string; href: string; external: boolean };
|
|
const strip = ({ name, subtitle, href, external }: Tracker): Tracker => ({
|
|
name,
|
|
subtitle,
|
|
href,
|
|
external
|
|
});
|
|
/** Public trackers (sitemap, logged-out navigation). */
|
|
export const trackers: readonly Tracker[] = allTrackers.filter((t) => !t.private).map(strip);
|
|
/** Owner-only trackers. */
|
|
export const privateTrackers: readonly Tracker[] = allTrackers.filter((t) => t.private).map(strip);
|
|
const ownerTrackers: readonly Tracker[] = allTrackers.map(strip);
|
|
|
|
export function visibleTrackers(isOwner: boolean): readonly Tracker[] {
|
|
return isOwner ? ownerTrackers : trackers;
|
|
}
|