Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a8603361c0 | |||
| dbda9a3897 | |||
| a987e438c4 | |||
| a150d34c6c |
@@ -0,0 +1,50 @@
|
||||
name: Release Tagging
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "v0.2" ]
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # Important for git describe --tags
|
||||
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.88.0
|
||||
components: cargo
|
||||
|
||||
- name: Extract Version from Cargo.toml
|
||||
id: version
|
||||
run: |
|
||||
VERSION=$(grep '^version = "' Cargo.toml | sed -E 's/version = "([^"]+)"/\1/')
|
||||
PROJECT_COMMIT_HASH=$(git rev-parse HEAD | cut -c 1-10)
|
||||
BRANCH_REF="${GITHUB_REF}"
|
||||
BRANCH_NAME=$(echo "$BRANCH_REF" | cut -d '/' -f 3)
|
||||
PROJECT_TAG_RELEASE="v$VERSION-$BRANCH_NAME-$PROJECT_COMMIT_HASH"
|
||||
echo "::set-output name=project_tag_release::$PROJECT_TAG_RELEASE-961"
|
||||
echo "Version: $VERSION"
|
||||
echo "Hash: $PROJECT_COMMIT_HASH"
|
||||
echo "Branch: $BRANCH_NAME"
|
||||
echo "Tag Release: $PROJECT_TAG_RELEASE"
|
||||
|
||||
- name: Print version
|
||||
id: print_version
|
||||
run: |
|
||||
echo "Printing version"
|
||||
echo "Version: ${{ steps.version.outputs.project_tag_release }}"
|
||||
|
||||
- name: Create GitHub Release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ steps.version.outputs.project_tag_release }}
|
||||
release_name: Release ${{ steps.version.outputs.project_tag_release }}
|
||||
body: |
|
||||
Release of version ${{ steps.version.outputs.project_tag_release }}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"dev": {
|
||||
"migrations": "./migrations"
|
||||
},
|
||||
"test": {
|
||||
"migrations": "./test_migrations"
|
||||
}
|
||||
}
|
||||
Generated
+3
-3
@@ -752,7 +752,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "icarus"
|
||||
version = "0.1.93"
|
||||
version = "0.1.95"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"common-multipart-rfc7578",
|
||||
@@ -793,8 +793,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "icarus_models"
|
||||
version = "0.4.5"
|
||||
source = "git+ssh://git@git.kundeng.us/phoenix/icarus_models.git?tag=v0.4.5-devel-655d05dabb-111#655d05dabbdadb9b28940564a1eb82470aa4f166"
|
||||
version = "0.5.1"
|
||||
source = "git+ssh://git@git.kundeng.us/phoenix/icarus_models.git?tag=v0.5.1-devel-1c5de9dc26-111#1c5de9dc26099ac5f21e1dc7a1f7d0dbb892b34b"
|
||||
dependencies = [
|
||||
"rand 0.9.1",
|
||||
"serde",
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "icarus"
|
||||
version = "0.1.93"
|
||||
version = "0.1.95"
|
||||
edition = "2024"
|
||||
rust-version = "1.88"
|
||||
|
||||
@@ -18,7 +18,7 @@ uuid = { version = "1.17.0", features = ["v4", "serde"] }
|
||||
sqlx = { version = "0.8.6", features = ["postgres", "runtime-tokio-native-tls", "time", "uuid"] }
|
||||
time = { version = "0.3.41", features = ["formatting", "macros", "parsing", "serde"] }
|
||||
icarus_meta = { git = "ssh://git@git.kundeng.us/phoenix/icarus_meta.git", tag = "v0.3.0-devel-f4b71de969-680" }
|
||||
icarus_models = { git = "ssh://git@git.kundeng.us/phoenix/icarus_models.git", tag = "v0.4.5-devel-655d05dabb-111" }
|
||||
icarus_models = { git = "ssh://git@git.kundeng.us/phoenix/icarus_models.git", tag = "v0.5.1-devel-1c5de9dc26-111" }
|
||||
icarus_envy = { git = "ssh://git@git.kundeng.us/phoenix/icarus_envy.git", tag = "v0.3.0-devel-d73fba9899-006" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -45,6 +45,13 @@ pub mod request {
|
||||
pub coverart_queue_id: uuid::Uuid,
|
||||
}
|
||||
}
|
||||
|
||||
pub mod get_coverart {
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct Params {
|
||||
pub id: Option<uuid::Uuid>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod response {
|
||||
@@ -99,6 +106,14 @@ pub mod response {
|
||||
pub data: Vec<uuid::Uuid>,
|
||||
}
|
||||
}
|
||||
|
||||
pub mod get_coverart {
|
||||
#[derive(Debug, Default, serde::Deserialize, serde::Serialize)]
|
||||
pub struct Response {
|
||||
pub message: String,
|
||||
pub data: Vec<icarus_models::coverart::CoverArt>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod db {
|
||||
@@ -321,6 +336,46 @@ pub mod cov_db {
|
||||
Err(_err) => Err(sqlx::Error::RowNotFound),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_coverart(
|
||||
pool: &sqlx::PgPool,
|
||||
id: &uuid::Uuid,
|
||||
) -> Result<icarus_models::coverart::CoverArt, sqlx::Error> {
|
||||
let result = sqlx::query(
|
||||
r#"
|
||||
SELECT id, title, path, song_id FROM "coverart" WHERE id = $1;
|
||||
"#,
|
||||
)
|
||||
.bind(id)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
eprintln!("Error querying data: {e:?}");
|
||||
});
|
||||
|
||||
match result {
|
||||
Ok(row) => Ok(icarus_models::coverart::CoverArt {
|
||||
id: row
|
||||
.try_get("id")
|
||||
.map_err(|_e| sqlx::Error::RowNotFound)
|
||||
.unwrap(),
|
||||
title: row
|
||||
.try_get("title")
|
||||
.map_err(|_e| sqlx::Error::RowNotFound)
|
||||
.unwrap(),
|
||||
path: row
|
||||
.try_get("path")
|
||||
.map_err(|_e| sqlx::Error::RowNotFound)
|
||||
.unwrap(),
|
||||
data: Vec::new(),
|
||||
song_id: row
|
||||
.try_get("song_id")
|
||||
.map_err(|_e| sqlx::Error::RowNotFound)
|
||||
.unwrap(),
|
||||
}),
|
||||
Err(_) => Err(sqlx::Error::RowNotFound),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod endpoint {
|
||||
@@ -560,4 +615,32 @@ pub mod endpoint {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_coverart(
|
||||
axum::Extension(pool): axum::Extension<sqlx::PgPool>,
|
||||
axum::extract::Query(params): axum::extract::Query<super::request::get_coverart::Params>,
|
||||
) -> (
|
||||
axum::http::StatusCode,
|
||||
axum::Json<super::response::get_coverart::Response>,
|
||||
) {
|
||||
let mut response = super::response::get_coverart::Response::default();
|
||||
|
||||
match params.id {
|
||||
Some(id) => match super::cov_db::get_coverart(&pool, &id).await {
|
||||
Ok(coverart) => {
|
||||
response.data.push(coverart);
|
||||
response.message = String::from(super::super::response::SUCCESSFUL);
|
||||
(axum::http::StatusCode::OK, axum::Json(response))
|
||||
}
|
||||
Err(err) => {
|
||||
response.message = err.to_string();
|
||||
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
|
||||
}
|
||||
},
|
||||
None => {
|
||||
response.message = String::from("Invalid parameters");
|
||||
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@ pub mod endpoints {
|
||||
pub const QUEUECOVERARTDATAWIPE: &str = "/api/v2/coverart/queue/data/wipe";
|
||||
|
||||
pub const CREATESONG: &str = "/api/v2/song";
|
||||
pub const GETSONGS: &str = "/api/v2/song";
|
||||
pub const CREATECOVERART: &str = "/api/v2/coverart";
|
||||
pub const GETCOVERART: &str = "/api/v2/coverart";
|
||||
}
|
||||
|
||||
pub mod response {
|
||||
|
||||
@@ -92,6 +92,13 @@ pub mod request {
|
||||
pub user_id: uuid::Uuid,
|
||||
}
|
||||
}
|
||||
|
||||
pub mod get_songs {
|
||||
#[derive(Debug, Default, serde::Deserialize, serde::Serialize)]
|
||||
pub struct Params {
|
||||
pub id: Option<uuid::Uuid>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod response {
|
||||
@@ -158,6 +165,14 @@ pub mod response {
|
||||
pub data: Vec<uuid::Uuid>,
|
||||
}
|
||||
}
|
||||
|
||||
pub mod get_songs {
|
||||
#[derive(Debug, Default, serde::Deserialize, serde::Serialize)]
|
||||
pub struct Response {
|
||||
pub message: String,
|
||||
pub data: Vec<icarus_models::song::Song>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Might make a distinction between year and date in a song's tag at some point
|
||||
@@ -968,4 +983,32 @@ pub mod endpoint {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_songs(
|
||||
axum::Extension(pool): axum::Extension<sqlx::PgPool>,
|
||||
axum::extract::Query(params): axum::extract::Query<super::request::get_songs::Params>,
|
||||
) -> (
|
||||
axum::http::StatusCode,
|
||||
Json<super::response::get_songs::Response>,
|
||||
) {
|
||||
let mut response = super::response::get_songs::Response::default();
|
||||
|
||||
match params.id {
|
||||
Some(id) => match super::song_db::get_song(&pool, &id).await {
|
||||
Ok(song) => {
|
||||
response.message = String::from(super::super::response::SUCCESSFUL);
|
||||
response.data.push(song);
|
||||
(axum::http::StatusCode::OK, axum::Json(response))
|
||||
}
|
||||
Err(err) => {
|
||||
response.message = err.to_string();
|
||||
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
|
||||
}
|
||||
},
|
||||
None => {
|
||||
response.message = String::from("Invalid parameters");
|
||||
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+178
-26
@@ -115,12 +115,21 @@ pub mod init {
|
||||
crate::callers::endpoints::CREATECOVERART,
|
||||
post(crate::callers::coverart::endpoint::create_coverart),
|
||||
)
|
||||
.route(
|
||||
crate::callers::endpoints::GETSONGS,
|
||||
get(crate::callers::song::endpoint::get_songs),
|
||||
)
|
||||
.route(
|
||||
crate::callers::endpoints::GETCOVERART,
|
||||
get(crate::callers::coverart::endpoint::get_coverart),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn app() -> axum::Router {
|
||||
let pool = crate::db::create_pool()
|
||||
.await
|
||||
.expect("Failed to create pool");
|
||||
// TODO: Look into handling this. Seems redundant to run migrations multiple times
|
||||
crate::db::migrations(&pool).await;
|
||||
|
||||
routes()
|
||||
@@ -227,6 +236,15 @@ mod tests {
|
||||
Err("Error parsing".into())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn migrations(pool: &sqlx::PgPool) {
|
||||
// Run migrations using the sqlx::migrate! macro
|
||||
// Assumes your test migrations are in a ./test_migrations folder relative to Cargo.toml
|
||||
sqlx::migrate!("./test_migrations")
|
||||
.run(pool)
|
||||
.await
|
||||
.expect("Failed to run migrations");
|
||||
}
|
||||
}
|
||||
|
||||
mod init {
|
||||
@@ -247,7 +265,7 @@ mod tests {
|
||||
) -> Result<axum::response::Response, std::convert::Infallible> {
|
||||
// Create multipart form
|
||||
let mut form = MultipartForm::default();
|
||||
let _ = form.add_file("flac", "tests/Machine_gun/track01.flac");
|
||||
let _ = form.add_file("flac", "tests/I/track01.flac");
|
||||
|
||||
// Create request
|
||||
let content_type = form.content_type();
|
||||
@@ -331,7 +349,7 @@ mod tests {
|
||||
app: &axum::Router,
|
||||
) -> Result<axum::response::Response, std::convert::Infallible> {
|
||||
let mut form = MultipartForm::default();
|
||||
let _ = form.add_file("jpg", "tests/Machine_gun/160809_machinegun.jpg");
|
||||
let _ = form.add_file("jpg", "tests/I/Coverart.jpg");
|
||||
|
||||
// Create request
|
||||
let content_type = form.content_type();
|
||||
@@ -407,22 +425,7 @@ mod tests {
|
||||
song_queue_id: &uuid::Uuid,
|
||||
user_id: &uuid::Uuid,
|
||||
) -> Result<axum::response::Response, std::convert::Infallible> {
|
||||
let payload = serde_json::json!({
|
||||
"title": "Power of Soul",
|
||||
"artist": "Jimmi Hendrix",
|
||||
"album_artist": "Jimmi Hendrix",
|
||||
"album": "Machine Gun",
|
||||
"genre": "Psychadelic Rock",
|
||||
"date": "2016-01-01",
|
||||
"track": 1,
|
||||
"disc": 1,
|
||||
"track_count": 11,
|
||||
"disc_count": 1,
|
||||
"duration": 330,
|
||||
"audio_type": "flac",
|
||||
"user_id": user_id,
|
||||
"song_queue_id": song_queue_id
|
||||
});
|
||||
let payload = payload_data::create_song(song_queue_id, user_id).await;
|
||||
|
||||
let req = axum::http::Request::builder()
|
||||
.method(axum::http::Method::POST)
|
||||
@@ -653,17 +656,39 @@ mod tests {
|
||||
serde_json::json!(
|
||||
{
|
||||
"song_queue_id": song_queue_id,
|
||||
"album" : "Machine Gun: The FillMore East First Show",
|
||||
"album_artist" : "Jimi Hendrix",
|
||||
"artist" : "Jimi Hendrix",
|
||||
"album" : "I",
|
||||
"album_artist" : "Kuoth",
|
||||
"artist" : "Kuoth",
|
||||
"disc" : 1,
|
||||
"disc_count" : 1,
|
||||
"duration" : 330,
|
||||
"genre" : "Psychadelic Rock",
|
||||
"title" : "Power of Soul",
|
||||
"duration" : 139,
|
||||
"genre" : "Alternative Hip-Hop",
|
||||
"title" : "Hypocrite Like The Rest",
|
||||
"track" : 1,
|
||||
"track_count" : 11,
|
||||
"year" : 2016
|
||||
"track_count" : 9,
|
||||
"year" : 2020
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn create_song(
|
||||
song_queue_id: &uuid::Uuid,
|
||||
user_id: &uuid::Uuid,
|
||||
) -> serde_json::Value {
|
||||
serde_json::json!({
|
||||
"title" : "Hypocrite Like The Rest",
|
||||
"artist" : "Kuoth",
|
||||
"album_artist": "Kuoth",
|
||||
"album": "I",
|
||||
"genre" : "Alternative Hip-Hop",
|
||||
"date": "2020-01-01",
|
||||
"track": 1,
|
||||
"disc": 1,
|
||||
"track_count": 9,
|
||||
"disc_count": 1,
|
||||
"duration": 139,
|
||||
"audio_type": "flac",
|
||||
"user_id": user_id,
|
||||
"song_queue_id": song_queue_id
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1792,4 +1817,131 @@ mod tests {
|
||||
|
||||
let _ = db_mgr::drop_database(&tm_pool, &db_name).await;
|
||||
}
|
||||
|
||||
pub mod after_song_queue {
|
||||
use tower::ServiceExt;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_songs() {
|
||||
let tm_pool = super::db_mgr::get_pool().await.unwrap();
|
||||
let db_name = super::db_mgr::generate_db_name().await;
|
||||
|
||||
match super::db_mgr::create_database(&tm_pool, &db_name).await {
|
||||
Ok(_) => {
|
||||
println!("Success");
|
||||
}
|
||||
Err(err) => {
|
||||
assert!(false, "Error: {:?}", err);
|
||||
}
|
||||
}
|
||||
|
||||
let pool = super::db_mgr::connect_to_db(&db_name).await.unwrap();
|
||||
super::db_mgr::migrations(&pool).await;
|
||||
|
||||
let app = super::init::app(pool).await;
|
||||
|
||||
let mut id = uuid::Uuid::nil();
|
||||
match uuid::Uuid::parse_str("44cf7940-34ff-489f-9124-d0ec90a55af9") {
|
||||
Ok(val) => {
|
||||
id = val;
|
||||
}
|
||||
Err(err) => {
|
||||
assert!(false, "Error: {err:?}");
|
||||
}
|
||||
};
|
||||
|
||||
let uri = format!("{}?id={id}", crate::callers::endpoints::GETSONGS);
|
||||
|
||||
match app
|
||||
.clone()
|
||||
.oneshot(
|
||||
axum::http::Request::builder()
|
||||
.method(axum::http::Method::GET)
|
||||
.uri(uri)
|
||||
.header(axum::http::header::CONTENT_TYPE, "application/json")
|
||||
.body(axum::body::Body::empty())
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(response) => {
|
||||
let resp = super::get_resp_data::<
|
||||
crate::callers::song::response::get_songs::Response,
|
||||
>(response)
|
||||
.await;
|
||||
assert_eq!(false, resp.data.is_empty(), "Should not be empty");
|
||||
|
||||
let song = resp.data[0].clone();
|
||||
assert_eq!(id, song.id, "Id does not match {song:?}");
|
||||
}
|
||||
Err(err) => {
|
||||
assert!(false, "Error: {err:?}");
|
||||
}
|
||||
}
|
||||
|
||||
let _ = super::db_mgr::drop_database(&tm_pool, &db_name).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_coverart() {
|
||||
let tm_pool = super::db_mgr::get_pool().await.unwrap();
|
||||
let db_name = super::db_mgr::generate_db_name().await;
|
||||
|
||||
match super::db_mgr::create_database(&tm_pool, &db_name).await {
|
||||
Ok(_) => {
|
||||
println!("Success");
|
||||
}
|
||||
Err(err) => {
|
||||
assert!(false, "Error: {:?}", err);
|
||||
}
|
||||
}
|
||||
|
||||
let pool = super::db_mgr::connect_to_db(&db_name).await.unwrap();
|
||||
super::db_mgr::migrations(&pool).await;
|
||||
|
||||
let app = super::init::app(pool).await;
|
||||
|
||||
let mut id = uuid::Uuid::nil();
|
||||
|
||||
match uuid::Uuid::parse_str("996122cd-5ae9-4013-9934-60768d3006ed") {
|
||||
Ok(val) => {
|
||||
id = val;
|
||||
}
|
||||
Err(err) => {
|
||||
assert!(false, "Error: {err:?}");
|
||||
}
|
||||
};
|
||||
|
||||
let uri = format!("{}?id={id}", crate::callers::endpoints::GETCOVERART);
|
||||
|
||||
match app
|
||||
.clone()
|
||||
.oneshot(
|
||||
axum::http::Request::builder()
|
||||
.method(axum::http::Method::GET)
|
||||
.uri(uri)
|
||||
.header(axum::http::header::CONTENT_TYPE, "application/json")
|
||||
.body(axum::body::Body::empty())
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(response) => {
|
||||
let resp = super::get_resp_data::<
|
||||
crate::callers::coverart::response::get_coverart::Response,
|
||||
>(response)
|
||||
.await;
|
||||
assert_eq!(false, resp.data.is_empty(), "Should not be empty");
|
||||
|
||||
let coverart = resp.data[0].clone();
|
||||
assert_eq!(id, coverart.id, "Id does not match {coverart:?}");
|
||||
}
|
||||
Err(err) => {
|
||||
assert!(false, "Error: {err:?}");
|
||||
}
|
||||
}
|
||||
|
||||
let _ = super::db_mgr::drop_database(&tm_pool, &db_name).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
-- Add migration script here
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
|
||||
-- Table to store queued songs to process
|
||||
CREATE TABLE IF NOT EXISTS "songQueue" (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
filename TEXT NOT NULL,
|
||||
status TEXT CHECK (status IN ('pending', 'ready', 'processing', 'done')),
|
||||
data BYTEA NULL,
|
||||
user_id UUID NULL
|
||||
);
|
||||
|
||||
-- Table to store queued metadata
|
||||
CREATE TABLE IF NOT EXISTS "metadataQueue" (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
metadata jsonb NOT NULL,
|
||||
created_at timestamptz DEFAULT now(),
|
||||
song_queue_id UUID NOT NULL
|
||||
);
|
||||
|
||||
-- Table to store queued coverart
|
||||
CREATE TABLE IF NOT EXISTS "coverartQueue" (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
data BYTEA NULL,
|
||||
song_queue_id UUID NULL
|
||||
);
|
||||
|
||||
-- Create an index for better query performance
|
||||
CREATE INDEX metadata_queue_data_metadata ON "metadataQueue" USING gin (metadata);
|
||||
|
||||
-- Table to store a song's info
|
||||
CREATE TABLE IF NOT EXISTS "song" (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
title TEXT NOT NULL,
|
||||
artist TEXT NOT NULL,
|
||||
album_artist TEXT NOT NULL,
|
||||
album TEXT NOT NULL,
|
||||
genre TEXT NOT NULL,
|
||||
year INT NOT NULL,
|
||||
track INT NOT NULL,
|
||||
disc INT NOT NULL,
|
||||
track_count INT NOT NULL,
|
||||
disc_count INT NOT NULL,
|
||||
duration INT NOT NULL,
|
||||
audio_type TEXT NOT NULL,
|
||||
date_created timestamptz DEFAULT now(),
|
||||
filename TEXT NOT NULL,
|
||||
directory TEXT NOT NULL,
|
||||
user_id UUID NULL
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "coverart" (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
title TEXT NOT NULL,
|
||||
path TEXT NOT NULL,
|
||||
song_id UUID NOT NULL
|
||||
);
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Add migration script here
|
||||
INSERT INTO "song" (id, title, artist, album_artist, album, genre, year, track, disc, track_count, disc_count, duration, audio_type, date_created, filename, directory, user_id) VALUES('44cf7940-34ff-489f-9124-d0ec90a55af9', 'Hypocrite Like The Rest', 'Kuoth', 'Kuoth', 'I', 'Alternative Hip-Hop', 2020, 1, 1, 9, 1, 139, 'flac', '2020-01-01 13:00:00-05', 'track01.flac', 'tests/I', '47491f9b-725a-4ba4-b9a5-711e1be46670');
|
||||
INSERT INTO "coverart" VALUES('996122cd-5ae9-4013-9934-60768d3006ed', 'I', 'tests/I/Coverart.jpg', '44cf7940-34ff-489f-9124-d0ec90a55af9');
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.7 MiB |
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"album": "I",
|
||||
"album_artist": "Kuoth",
|
||||
"disc_count": 1,
|
||||
"genre": "Alternative Hip-Hop",
|
||||
"year": 2020,
|
||||
"track_count": 9,
|
||||
"tracks": [
|
||||
{
|
||||
"artist": "Kuoth",
|
||||
"disc": 1,
|
||||
"title": "Hypocrite Like the Rest",
|
||||
"duration": 139,
|
||||
"track": 1
|
||||
},
|
||||
{
|
||||
"artist": "Kuoth",
|
||||
"disc": 1,
|
||||
"title": "It's Too Late",
|
||||
"duration": 116,
|
||||
"track": 2
|
||||
},
|
||||
{
|
||||
"artist": "Kuoth",
|
||||
"disc": 1,
|
||||
"title": "Hell Might be Nice",
|
||||
"duration": 229,
|
||||
"track": 3
|
||||
},
|
||||
{
|
||||
"artist": "Kuoth",
|
||||
"disc": 1,
|
||||
"title": "Dark Side of Progress",
|
||||
"duration": 187,
|
||||
"track": 4
|
||||
},
|
||||
{
|
||||
"artist": "Kuoth",
|
||||
"disc": 1,
|
||||
"title": "Distractions",
|
||||
"duration": 141,
|
||||
"track": 5
|
||||
},
|
||||
{
|
||||
"artist": "Kuoth",
|
||||
"disc": 1,
|
||||
"title": "But You Won't Listen",
|
||||
"duration": 184,
|
||||
"track": 6
|
||||
},
|
||||
{
|
||||
"artist": "Kuoth",
|
||||
"disc": 1,
|
||||
"title": "Guidance",
|
||||
"duration": 121,
|
||||
"track": 7
|
||||
},
|
||||
{
|
||||
"artist": "Kuoth",
|
||||
"disc": 1,
|
||||
"title": "M",
|
||||
"duration": 98,
|
||||
"track": 8
|
||||
},
|
||||
{
|
||||
"artist": "Kuoth",
|
||||
"disc": 1,
|
||||
"title": "Social Acceptance",
|
||||
"duration": 169,
|
||||
"track": 9
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 271 KiB |
Reference in New Issue
Block a user