Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .bruno/bruno.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
"node_modules",
".git"
]
}
}
10 changes: 5 additions & 5 deletions .docs/_cron.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# CRON jobs

You have to allow two extensions: `pg_cron`, `pg_net` for a database to make this
work.
See [Supabase Documentation](https://supabase.com/docs/guides/cron).
You have to allow two extensions: `pg_cron`, `pg_net` for a database to make
this work. See [Supabase Documentation](https://supabase.com/docs/guides/cron).

You have to run the following commands in SQL Editor of your Supabase project.

## Generate cron tab

1. Fill out root `.env` file with variables
2. run tests: `deno run tests`
3. cron install queries will be generated into the file `cron_jobs.sql` in the root
3. cron install queries will be generated into the file `cron_jobs.sql` in the
root
4. visit supabase dashboard and run these queries in SQL editor

## GUI
Expand All @@ -21,7 +21,7 @@ There is actually a UI where you can manage your jobs very easily.
https://supabase.com/dashboard/project/***/integrations/cron/jobs
```

*Replace your project ID in the URL*.
_Replace your project ID in the URL_.

## Remove existing job

Expand Down
15 changes: 9 additions & 6 deletions .docs/_edge_functions.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
# Edge Functions

Basically the same as AWS Lambda, but much better integrated in the whole system. Check [Supabase Documentation](https://supabase.com/docs/guides/functions).
Basically the same as AWS Lambda, but much better integrated in the whole
system. Check
[Supabase Documentation](https://supabase.com/docs/guides/functions).

Code is located in: `./supabase/functions/*`.

Each function can run a very short time (up to 2s) then it's terminated. So it's good to write small scripts with pagination. Database can give you great tooling for that.

Unexpected errors are handled by Sentry. That works good. What should be tweaked is notification about terminated function. It can be done probably by event pushing to Sentry as well.


Each function can run a very short time (up to 2s) then it's terminated. So it's
good to write small scripts with pagination. Database can give you great tooling
for that.

Unexpected errors are handled by Sentry. That works good. What should be tweaked
is notification about terminated function. It can be done probably by event
pushing to Sentry as well.
2 changes: 1 addition & 1 deletion .docs/_localhost.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ supabase functions deploy --project-ref xxx

## Dependencies

run `deno outdated`
run `deno outdated`
4 changes: 2 additions & 2 deletions .docs/_podman.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Podman

If you are switching between Docker Desktop and Podman, you probably got some errors.
Here is my troubleshooting:
If you are switching between Docker Desktop and Podman, you probably got some
errors. Here is my troubleshooting:

```bash
docker context ls
Expand Down
12 changes: 6 additions & 6 deletions .github/action/deno-outdated/action.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
name: Deno Outdated Check
description: 'Check for outdated Deno dependencies and display them in PR comments'
description: "Check for outdated Deno dependencies and display them in PR comments"

inputs:
directory:
description: 'Directory where to run the deno outdated check'
description: "Directory where to run the deno outdated check"
required: true
title:
description: 'Message title'
required: true
description: "Message title"
required: true

outputs:
result:
description: 'Result of the outdated check'
description: "Result of the outdated check"
value: ${{ steps.outdated.outputs.result }}

runs:
Expand All @@ -29,7 +29,7 @@ runs:
cat deno.json
bash ${{ github.workspace }}/.github/action/deno-outdated/deno-outdated.sh

- name: 'Outdated packages - post'
- name: "Outdated packages - post"
uses: marocchino/sticky-pull-request-comment@v2
with:
recreate: true
Expand Down
14 changes: 7 additions & 7 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
version: 2
updates:
- package-ecosystem: 'github-actions'
directory: '/'
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: 'monthly'
interval: "monthly"
labels:
- 'dependencies'
- 'github-actions'
- "dependencies"
- "github-actions"
commit-message:
prefix: 'ci'
include: 'scope'
prefix: "ci"
include: "scope"
2 changes: 1 addition & 1 deletion .github/workflows/deploy-supabase.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
with:
version: 2.13.3

- name: 'Supabase - Remote Connect'
- name: "Supabase - Remote Connect"
env:
PROJECT_ID: ${{ secrets.SUPABASE_PROJECT_ID }}
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deploy-web.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
cd src/web
deno run compile
deno task build

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/pr-supabase.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ env:

jobs:
qa:
name: 'QA'
name: "QA"
environment: test
runs-on: ubuntu-latest
steps:
Expand All @@ -38,16 +38,16 @@ jobs:
- run: deno lint supabase/*
- run: deno task tests --reload

- name: 'Generate cron jobs'
- name: "Generate cron jobs"
uses: actions/upload-artifact@v4
with:
name: cron_jobs.sql
path: cron_jobs.sql
retention-days: 1

- name: 'Check Outdated Packages'
- name: "Check Outdated Packages"
uses: ./.github/action/deno-outdated
id: outdated_supabase
with:
directory: ./supabase/functions
title: 'Deno Outdated - Supabase'
title: "Deno Outdated - Supabase"
13 changes: 6 additions & 7 deletions .github/workflows/pr-web.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ permissions:

jobs:
qa:
name: 'QA'
name: "QA"
environment: test
runs-on: ubuntu-latest
defaults:
Expand All @@ -36,17 +36,16 @@ jobs:
- run: deno fmt --check
- run: deno lint

- run: deno run compile
- name: 'Build the app'
run: deno run build
- name: "Build the app"
run: deno task build

- name: 'Upload artifact'
- name: "Upload artifact"
uses: actions/upload-pages-artifact@v3
with:
path: ./src/web/dist

- name: 'Check Outdated packages'
- name: "Check Outdated packages"
uses: ./.github/action/deno-outdated
with:
directory: ./src/web
title: 'Deno Outdated - Web'
title: "Deno Outdated - Web"
28 changes: 17 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,36 @@

[![Better Stack Badge](https://uptime.betterstack.com/status-badges/v3/monitor/1skah.svg)](https://landsman.betteruptime.com)

The purpose of this project is to store data about the music I listen to in my own database and to query it using SQL.
I also want to have fun while learning more about [Supabase](https://supabase.com) native features.
The purpose of this project is to store data about the music I listen to in my
own database and to query it using SQL. I also want to have fun while learning
more about [Supabase](https://supabase.com) native features.

Currently, I am downloading all historical data from my Last.fm profile
using its API to PostgresSQL via [Edge Functions](.docs/_edge_functions.md) and the [built-in cron extension](.docs/_cron.md),
which run every 5 minutes and download 500 items.
Currently, I am downloading all historical data from my Last.fm profile using
its API to PostgresSQL via [Edge Functions](.docs/_edge_functions.md) and the
[built-in cron extension](.docs/_cron.md), which run every 5 minutes and
download 500 items.

## Roadmap

- [x] download and sync listened tracks from Last.fm every 5 minutes
- [ ] fetch tags from last.fm to downloaded tracks: https://www.last.fm/api/show/track.getTags
- [x] download and sync listened tracks from Last.fm every 5 minutes
- [ ] fetch tags from last.fm to downloaded tracks:
https://www.last.fm/api/show/track.getTags
- [ ] connect to spotify api, find track links to play them
- [ ] fetch detailed info about artists from last.fm or spotify
- [ ] fetch detailed info about albums from last.fm or spotify

## Tools

- Read [documentation](.docs/README.md) about localhost setup and cloud configuration
- Read [documentation](.docs/README.md) about localhost setup and cloud
configuration
- [IntelliJ IDEA](https://www.jetbrains.com/idea/) as a code editor
- [Docker](https://docs.docker.com/get-started/get-docker/) for a localhost studio
- [Docker](https://docs.docker.com/get-started/get-docker/) for a localhost
studio
- [Deno](https://deno.com)
- [Bruno](https://www.usebruno.com) for easy debugging
- [Supabase](https://supabase.com) account
- [Last.fm](https://www.last.fm/home) account [connected to Spotify](https://www.last.fm/about/trackmymusic), [API key](https://www.last.fm/api/authentication)
- [Last.fm](https://www.last.fm/home) account
[connected to Spotify](https://www.last.fm/about/trackmymusic),
[API key](https://www.last.fm/api/authentication)
- [Sentry](https://sentry.io/) for error notifications
- [BetterStack] for uptime monitoring

83 changes: 61 additions & 22 deletions src/web/src/data/tracks-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,66 @@ export type ListenedTracks = Omit<Listened, "hooman_id"> & {

export async function getLastListenedTracks(
signal: AbortSignal,
page: number = 0,
): Promise<ListenedTracks[]> {
const { data, error } = await supabase
.from("listened")
.select<string, ListenedTracks>(`
id,
artist_name,
track_name,
album_lastfm_id,
album_name,
created_at,
listened_at,
lastfm_id,
hooman:hooman_id (
id,
lastfm_user
)
`)
.order("listened_at", { ascending: false })
.limit(50)
.abortSignal(signal);

if (error) throw error;
return data ?? [];
const limit = 50;
const offset = page * limit;

console.log("Fetching tracks with page:", page, "offset:", offset);

try {
// First, check if we can connect to Supabase at all
const healthCheck = await supabase.from("listened").select("count()", {
count: "exact",
});
console.log("Supabase health check:", healthCheck);

// Now perform the actual query
const { data, error } = await supabase
.from("listened")
.select<string, ListenedTracks>(`
id,
artist_name,
track_name,
album_lastfm_id,
album_name,
created_at,
listened_at,
lastfm_id,
hooman:hooman_id (
id,
lastfm_user
)
`)
.order("listened_at", { ascending: false })
.range(offset, offset + limit - 1)
.abortSignal(signal);

console.log("Supabase response:", {
dataReceived: !!data,
dataLength: data?.length || 0,
error: error ? error.message : null,
firstItem: data && data.length > 0
? {
id: data[0].id,
artist: data[0].artist_name,
track: data[0].track_name,
}
: null,
});

if (error) {
console.error("Supabase error details:", error);
throw error;
}

if (!data || data.length === 0) {
console.warn("No data returned from Supabase query");
}

return data ?? [];
} catch (error) {
console.error("Error fetching tracks:", error);
throw error;
}
}
Loading