Compare commits
No commits in common. "044ccd668337123e07a09ffb387b2ceaccd3dbd8" and "fdf3fa59049ada1625bb7712ef47dfd46fef9cd7" have entirely different histories.
044ccd6683
...
fdf3fa5904
28 changed files with 631 additions and 779 deletions
13
Cargo.lock
generated
13
Cargo.lock
generated
|
@ -199,7 +199,6 @@ checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf"
|
|||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
"axum-macros",
|
||||
"bitflags 1.3.2",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
|
@ -241,18 +240,6 @@ dependencies = [
|
|||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-macros"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdca6a10ecad987bda04e95606ef85a5417dcaac1a78455242d72e031e2b6b62"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.69"
|
||||
|
|
|
@ -73,7 +73,7 @@ sqlx = { version = "0.7.0", default-features = false, features = [
|
|||
] }
|
||||
|
||||
# Web server
|
||||
axum = { version = "0.6.20", features = ["macros"] }
|
||||
axum = "0.6.20"
|
||||
headers = "0.3.9"
|
||||
http = "0.2.9"
|
||||
hyper = { version = "0.14.27", features = ["stream"] }
|
||||
|
|
|
@ -16,7 +16,7 @@ pub struct ApiError {
|
|||
}
|
||||
|
||||
/// API error type
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[cfg_attr(feature = "utoipa", derive(ToSchema))]
|
||||
pub enum ApiErrorKind {
|
||||
|
|
|
@ -20,12 +20,6 @@ pub enum ApiError {
|
|||
NotFound(Cow<'static, str>),
|
||||
#[error("{0}")]
|
||||
Other(Cow<'static, str>),
|
||||
#[error("{msg}")]
|
||||
Custom {
|
||||
msg: Cow<'static, str>,
|
||||
status: StatusCode,
|
||||
kind: ApiErrorKind,
|
||||
},
|
||||
}
|
||||
|
||||
impl ErrorStatus for ApiError {
|
||||
|
@ -38,7 +32,6 @@ impl ErrorStatus for ApiError {
|
|||
ApiError::Input(_) => StatusCode::BAD_REQUEST,
|
||||
ApiError::NotFound(_) => StatusCode::NOT_FOUND,
|
||||
ApiError::Other(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
ApiError::Custom { status, .. } => *status,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,7 +44,6 @@ impl ErrorStatus for ApiError {
|
|||
ApiError::Input(_) => ApiErrorKind::User,
|
||||
ApiError::NotFound(_) => ApiErrorKind::User,
|
||||
ApiError::Other(_) => ApiErrorKind::Other,
|
||||
ApiError::Custom { kind, .. } => *kind,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
//! These are custom axum extractors which return proper ApiErrors when they receive invalid data
|
||||
//!
|
||||
//! Source of example: https://github.com/tokio-rs/axum/blob/3ff45d9c96b5192af6b6ec26eb2a2bfcddd00d7d/examples/customize-extractor-error/src/derive_from_request.rs
|
||||
|
||||
use axum::{
|
||||
extract::FromRequest,
|
||||
extract::{
|
||||
rejection::{JsonRejection, PathRejection, QueryRejection},
|
||||
FromRequestParts,
|
||||
},
|
||||
response::IntoResponse,
|
||||
};
|
||||
use serde::Serialize;
|
||||
use tiraya_api_model::ApiErrorKind;
|
||||
|
||||
use crate::error::ApiError;
|
||||
|
||||
#[derive(FromRequestParts)]
|
||||
#[from_request(via(axum::extract::Path), rejection(ApiError))]
|
||||
pub struct Path<T>(pub T);
|
||||
|
||||
#[derive(FromRequestParts)]
|
||||
#[from_request(via(axum::extract::Query), rejection(ApiError))]
|
||||
pub struct Query<T>(pub T);
|
||||
|
||||
#[derive(FromRequest)]
|
||||
#[from_request(via(axum::extract::Json), rejection(ApiError))]
|
||||
pub struct Json<T>(pub T);
|
||||
|
||||
impl<T: Serialize> IntoResponse for Json<T> {
|
||||
fn into_response(self) -> axum::response::Response {
|
||||
let Self(value) = self;
|
||||
axum::Json(value).into_response()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PathRejection> for ApiError {
|
||||
fn from(rejection: PathRejection) -> Self {
|
||||
Self::Custom {
|
||||
msg: rejection.body_text().into(),
|
||||
status: rejection.status(),
|
||||
kind: ApiErrorKind::User,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<QueryRejection> for ApiError {
|
||||
fn from(rejection: QueryRejection) -> Self {
|
||||
Self::Custom {
|
||||
msg: rejection.body_text().into(),
|
||||
status: rejection.status(),
|
||||
kind: ApiErrorKind::User,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<JsonRejection> for ApiError {
|
||||
fn from(rejection: JsonRejection) -> Self {
|
||||
Self::Custom {
|
||||
msg: rejection.body_text().into(),
|
||||
status: rejection.status(),
|
||||
kind: ApiErrorKind::User,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
mod error;
|
||||
mod extract;
|
||||
mod routes;
|
||||
|
||||
use std::{net::SocketAddr, ops::Deref, str::FromStr, sync::Arc};
|
||||
|
@ -131,10 +130,7 @@ pub async fn serve() -> Result<(), anyhow::Error> {
|
|||
.route(
|
||||
"/user/:id/playlists",
|
||||
routing::get(routes::user::get_user_playlists),
|
||||
)
|
||||
.fallback(|| async {
|
||||
crate::error::ApiError::NotFound("API endpoint not found".into())
|
||||
}),
|
||||
),
|
||||
)
|
||||
// TMP: move to frontend server
|
||||
.merge(utoipa_rapidoc::RapiDoc::new("/api/openapi.json").path("/api-docs"))
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
use axum::extract::State;
|
||||
use axum::{
|
||||
extract::{Path, Query, State},
|
||||
Json,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use tiraya_api_model::{Album, TrackSlim};
|
||||
use tiraya_db::models::{self as tdb};
|
||||
use tiraya_extractor::parse_validate_tid;
|
||||
use tiraya_utils::EntityType;
|
||||
|
||||
use crate::{
|
||||
error::ApiError,
|
||||
extract::{Json, Path, Query},
|
||||
ApiState,
|
||||
};
|
||||
use crate::{error::ApiError, ApiState};
|
||||
|
||||
#[derive(Default, Deserialize)]
|
||||
#[serde(default)]
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
use axum::extract::State;
|
||||
use axum::{
|
||||
extract::{Path, Query, State},
|
||||
Json,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use tiraya_api_model::{AlbumSlim, Artist, ArtistSlim, PlaylistSlim, TrackSlim};
|
||||
use tiraya_db::models::{self as tdb};
|
||||
use tiraya_extractor::parse_validate_tid;
|
||||
use tiraya_utils::EntityType;
|
||||
|
||||
use crate::{
|
||||
error::ApiError,
|
||||
extract::{Json, Path, Query},
|
||||
ApiState,
|
||||
};
|
||||
use crate::{error::ApiError, ApiState};
|
||||
|
||||
/// Get artist
|
||||
///
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
use axum::extract::State;
|
||||
use axum::extract::{Path, Query, State};
|
||||
use hyper::{Body, Response};
|
||||
use serde::Deserialize;
|
||||
use tiraya_db::models::{self as tdb};
|
||||
use tiraya_extractor::parse_validate_tid;
|
||||
use tiraya_utils::{ImageKind, ImageSize};
|
||||
|
||||
use crate::{
|
||||
error::ApiError,
|
||||
extract::{Path, Query},
|
||||
ApiState,
|
||||
};
|
||||
use crate::{error::ApiError, ApiState};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct LocalImageQuery {
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use axum::extract::State;
|
||||
use axum::{
|
||||
extract::{Path, Query, State},
|
||||
Json,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use tiraya_api_model::{Playlist, UserSlim};
|
||||
use tiraya_db::models::{self as tdb};
|
||||
|
@ -8,11 +11,7 @@ use tiraya_extractor::parse_validate_tid;
|
|||
use tiraya_utils::EntityType;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
error::ApiError,
|
||||
extract::{Json, Path, Query},
|
||||
ApiState,
|
||||
};
|
||||
use crate::{error::ApiError, ApiState};
|
||||
|
||||
#[derive(Default, Deserialize)]
|
||||
#[serde(default)]
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
use axum::extract::State;
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
Json,
|
||||
};
|
||||
use tiraya_api_model::Track;
|
||||
use tiraya_extractor::parse_validate_tid;
|
||||
use tiraya_utils::EntityType;
|
||||
|
||||
use crate::{
|
||||
error::ApiError,
|
||||
extract::{Json, Path},
|
||||
ApiState,
|
||||
};
|
||||
use crate::{error::ApiError, ApiState};
|
||||
|
||||
/// Get track
|
||||
///
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
use axum::extract::State;
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
Json,
|
||||
};
|
||||
use tiraya_api_model::{PlaylistSlim, User};
|
||||
use tiraya_db::models::{self as tdb};
|
||||
use tiraya_extractor::parse_validate_tid;
|
||||
use tiraya_utils::EntityType;
|
||||
|
||||
use crate::{
|
||||
error::ApiError,
|
||||
extract::{Json, Path},
|
||||
ApiState,
|
||||
};
|
||||
use crate::{error::ApiError, ApiState};
|
||||
|
||||
/// Get user
|
||||
///
|
||||
|
|
45
crates/db/.sqlx/query-2c9bace9d693f5c7af4fa3ab77f021d399974387e2be0ca0107c8706a0a71d82.json
generated
Normal file
45
crates/db/.sqlx/query-2c9bace9d693f5c7af4fa3ab77f021d399974387e2be0ca0107c8706a0a71d82.json
generated
Normal file
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "insert into tracks (src_id, service, name, duration,\n album_id, album_pos, ul_artists, isrc, description, file_size, track_gain, primary_track)\nvalues ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)\non conflict (src_id, service) do update set\n name = excluded.name,\n duration = coalesce(excluded.duration, tracks.duration),\n album_id = excluded.album_id,\n album_pos = coalesce(excluded.album_pos, tracks.album_pos),\n ul_artists = coalesce(excluded.ul_artists, tracks.ul_artists),\n isrc = coalesce(excluded.isrc, tracks.isrc),\n description = coalesce(excluded.description, tracks.description),\n file_size = coalesce(excluded.file_size, tracks.file_size),\n track_gain = coalesce(excluded.track_gain, tracks.track_gain),\n primary_track = coalesce(excluded.primary_track, tracks.primary_track)\nreturning id",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
{
|
||||
"Custom": {
|
||||
"name": "music_service",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"ty",
|
||||
"yt",
|
||||
"sp",
|
||||
"mx"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Text",
|
||||
"Int4",
|
||||
"Int4",
|
||||
"Int2",
|
||||
"TextArray",
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Int8",
|
||||
"Float4",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "2c9bace9d693f5c7af4fa3ab77f021d399974387e2be0ca0107c8706a0a71d82"
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "select t.id, t.src_id, t.service as \"service: _\", t.name, t.duration, t.duration_ms,\n b.id as album_id, b.src_id as album_src_id, b.service as \"album_service: _\", b.name as album_name, b.release_date,\n b.album_type as \"album_type: _\", b.image_url, b.image_date,\n t.album_pos, t.ul_artists, t.isrc, t.description, t.file_size, t.track_gain, t.created_at, t.updated_at,\n t.primary_track, t.downloaded_at, t.last_streamed_at, t.n_streams,\n jsonb_agg(json_build_object('id', a.src_id, 'sv', a.service, 'n', a.name) order by art.seq)\n filter (where a.src_id is not null) as \"artists: _\"\nfrom tracks t\n left join artists_tracks art on art.track_id = t.id\n left join artists a on a.id = art.artist_id\n left join albums b on b.id = t.album_id\nwhere t.src_id=$1 and t.service=$2\ngroup by (t.id, b.id)",
|
||||
"query": "select t.id, t.src_id, t.service as \"service: _\", t.name, t.duration,\n b.id as album_id, b.src_id as album_src_id, b.service as \"album_service: _\", b.name as album_name, b.release_date,\n b.album_type as \"album_type: _\", b.image_url, b.image_date,\n t.album_pos, t.ul_artists, t.isrc, t.description, t.file_size, t.track_gain, t.created_at, t.updated_at,\n t.primary_track, t.downloaded_at, t.last_streamed_at, t.n_streams,\n jsonb_agg(json_build_object('id', a.src_id, 'sv', a.service, 'n', a.name) order by art.seq)\n filter (where a.src_id is not null) as \"artists: _\"\nfrom tracks t\n left join artists_tracks art on art.track_id = t.id\n left join artists a on a.id = art.artist_id\n left join albums b on b.id = t.album_id\nwhere t.src_id=$1 and t.service=$2\ngroup by (t.id, b.id)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
|
@ -42,21 +42,16 @@
|
|||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "duration_ms",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "album_id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"ordinal": 6,
|
||||
"name": "album_src_id",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 7,
|
||||
"name": "album_service: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
|
@ -73,17 +68,17 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 8,
|
||||
"name": "album_name",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 9,
|
||||
"name": "release_date",
|
||||
"type_info": "Date"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"ordinal": 10,
|
||||
"name": "album_type: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
|
@ -100,77 +95,77 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"ordinal": 11,
|
||||
"name": "image_url",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 12,
|
||||
"name": "image_date",
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"ordinal": 13,
|
||||
"name": "album_pos",
|
||||
"type_info": "Int2"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"ordinal": 14,
|
||||
"name": "ul_artists",
|
||||
"type_info": "TextArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"ordinal": 15,
|
||||
"name": "isrc",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"ordinal": 16,
|
||||
"name": "description",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"ordinal": 17,
|
||||
"name": "file_size",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 19,
|
||||
"ordinal": 18,
|
||||
"name": "track_gain",
|
||||
"type_info": "Float4"
|
||||
},
|
||||
{
|
||||
"ordinal": 20,
|
||||
"ordinal": 19,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"ordinal": 21,
|
||||
"ordinal": 20,
|
||||
"name": "updated_at",
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"ordinal": 22,
|
||||
"ordinal": 21,
|
||||
"name": "primary_track",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 23,
|
||||
"ordinal": 22,
|
||||
"name": "downloaded_at",
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"ordinal": 24,
|
||||
"ordinal": 23,
|
||||
"name": "last_streamed_at",
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"ordinal": 25,
|
||||
"ordinal": 24,
|
||||
"name": "n_streams",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 26,
|
||||
"ordinal": 25,
|
||||
"name": "artists: _",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
|
@ -203,7 +198,6 @@
|
|||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
|
@ -223,5 +217,5 @@
|
|||
null
|
||||
]
|
||||
},
|
||||
"hash": "5471630b1ffc7f91c3c3d03b231a695cbf56bd8375aee419b1ff26033b98eae7"
|
||||
"hash": "586725923e6284841ebeac8b2e9b74dc623f2eefe05adf9941ad64880317f360"
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "select t.id, t.src_id, t.service as \"service: _\", t.name, t.duration, t.duration_ms,\n b.id as album_id, b.src_id as album_src_id, b.service as \"album_service: _\", b.name as album_name, b.release_date,\n b.album_type as \"album_type: _\", b.image_url, b.image_date,\n t.album_pos, t.ul_artists, t.isrc, t.description, t.file_size, t.track_gain, t.created_at, t.updated_at,\n t.primary_track, t.downloaded_at, t.last_streamed_at, t.n_streams,\n jsonb_agg(json_build_object('id', a.src_id, 'sv', a.service, 'n', a.name) order by art.seq)\n filter (where a.src_id is not null) as \"artists: _\"\nfrom tracks t\n left join artists_tracks art on art.track_id = t.id\n left join artists a on a.id = art.artist_id\n left join albums b on b.id = t.album_id\n join track_aliases ta on ta.track_id=t.id\nwhere ta.src_id=$1 and ta.service=$2\ngroup by (t.id, b.id)",
|
||||
"query": "select t.id, t.src_id, t.service as \"service: _\", t.name, t.duration,\n b.id as album_id, b.src_id as album_src_id, b.service as \"album_service: _\", b.name as album_name, b.release_date,\n b.album_type as \"album_type: _\", b.image_url, b.image_date,\n t.album_pos, t.ul_artists, t.isrc, t.description, t.file_size, t.track_gain, t.created_at, t.updated_at,\n t.primary_track, t.downloaded_at, t.last_streamed_at, t.n_streams,\n jsonb_agg(json_build_object('id', a.src_id, 'sv', a.service, 'n', a.name) order by art.seq)\n filter (where a.src_id is not null) as \"artists: _\"\nfrom tracks t\n left join artists_tracks art on art.track_id = t.id\n left join artists a on a.id = art.artist_id\n left join albums b on b.id = t.album_id\n join track_aliases ta on ta.track_id=t.id\nwhere ta.src_id=$1 and ta.service=$2\ngroup by (t.id, b.id)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
|
@ -42,21 +42,16 @@
|
|||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "duration_ms",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "album_id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"ordinal": 6,
|
||||
"name": "album_src_id",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 7,
|
||||
"name": "album_service: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
|
@ -73,17 +68,17 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 8,
|
||||
"name": "album_name",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 9,
|
||||
"name": "release_date",
|
||||
"type_info": "Date"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"ordinal": 10,
|
||||
"name": "album_type: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
|
@ -100,77 +95,77 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"ordinal": 11,
|
||||
"name": "image_url",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 12,
|
||||
"name": "image_date",
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"ordinal": 13,
|
||||
"name": "album_pos",
|
||||
"type_info": "Int2"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"ordinal": 14,
|
||||
"name": "ul_artists",
|
||||
"type_info": "TextArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"ordinal": 15,
|
||||
"name": "isrc",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"ordinal": 16,
|
||||
"name": "description",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"ordinal": 17,
|
||||
"name": "file_size",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 19,
|
||||
"ordinal": 18,
|
||||
"name": "track_gain",
|
||||
"type_info": "Float4"
|
||||
},
|
||||
{
|
||||
"ordinal": 20,
|
||||
"ordinal": 19,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"ordinal": 21,
|
||||
"ordinal": 20,
|
||||
"name": "updated_at",
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"ordinal": 22,
|
||||
"ordinal": 21,
|
||||
"name": "primary_track",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 23,
|
||||
"ordinal": 22,
|
||||
"name": "downloaded_at",
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"ordinal": 24,
|
||||
"ordinal": 23,
|
||||
"name": "last_streamed_at",
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"ordinal": 25,
|
||||
"ordinal": 24,
|
||||
"name": "n_streams",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 26,
|
||||
"ordinal": 25,
|
||||
"name": "artists: _",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
|
@ -203,7 +198,6 @@
|
|||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
|
@ -223,5 +217,5 @@
|
|||
null
|
||||
]
|
||||
},
|
||||
"hash": "5178a0126bd22c1508c7dd630e38213052bf6f8623e59147c38f00d0acf38ff4"
|
||||
"hash": "6cb7208a08d2854472551fa8a771ea47ffc5bf510676b04fb487702ea08673ed"
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "select t.id, t.src_id, t.service as \"service: _\", t.name, t.duration, t.duration_ms,\n b.id as album_id, b.src_id as album_src_id, b.service as \"album_service: _\", b.name as album_name, b.release_date,\n b.album_type as \"album_type: _\", b.image_url, b.image_date,\n t.album_pos, t.ul_artists, t.isrc, t.description, t.file_size, t.track_gain, t.created_at, t.updated_at,\n t.primary_track, t.downloaded_at, t.last_streamed_at, t.n_streams,\n jsonb_agg(json_build_object('id', a.src_id, 'sv', a.service, 'n', a.name) order by art.seq)\n filter (where a.src_id is not null) as \"artists: _\"\nfrom tracks t\n left join artists_tracks art on art.track_id = t.id\n left join artists a on a.id = art.artist_id\n left join albums b on b.id = t.album_id\nwhere t.id=$1\ngroup by (t.id, b.id)",
|
||||
"query": "select t.id, t.src_id, t.service as \"service: _\", t.name, t.duration,\n b.id as album_id, b.src_id as album_src_id, b.service as \"album_service: _\", b.name as album_name, b.release_date,\n b.album_type as \"album_type: _\", b.image_url, b.image_date,\n t.album_pos, t.ul_artists, t.isrc, t.description, t.file_size, t.track_gain, t.created_at, t.updated_at,\n t.primary_track, t.downloaded_at, t.last_streamed_at, t.n_streams,\n jsonb_agg(json_build_object('id', a.src_id, 'sv', a.service, 'n', a.name) order by art.seq)\n filter (where a.src_id is not null) as \"artists: _\"\nfrom tracks t\n left join artists_tracks art on art.track_id = t.id\n left join artists a on a.id = art.artist_id\n left join albums b on b.id = t.album_id\nwhere t.id=$1\ngroup by (t.id, b.id)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
|
@ -42,21 +42,16 @@
|
|||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "duration_ms",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "album_id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"ordinal": 6,
|
||||
"name": "album_src_id",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 7,
|
||||
"name": "album_service: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
|
@ -73,17 +68,17 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 8,
|
||||
"name": "album_name",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 9,
|
||||
"name": "release_date",
|
||||
"type_info": "Date"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"ordinal": 10,
|
||||
"name": "album_type: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
|
@ -100,77 +95,77 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"ordinal": 11,
|
||||
"name": "image_url",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 12,
|
||||
"name": "image_date",
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"ordinal": 13,
|
||||
"name": "album_pos",
|
||||
"type_info": "Int2"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"ordinal": 14,
|
||||
"name": "ul_artists",
|
||||
"type_info": "TextArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"ordinal": 15,
|
||||
"name": "isrc",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"ordinal": 16,
|
||||
"name": "description",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"ordinal": 17,
|
||||
"name": "file_size",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 19,
|
||||
"ordinal": 18,
|
||||
"name": "track_gain",
|
||||
"type_info": "Float4"
|
||||
},
|
||||
{
|
||||
"ordinal": 20,
|
||||
"ordinal": 19,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"ordinal": 21,
|
||||
"ordinal": 20,
|
||||
"name": "updated_at",
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"ordinal": 22,
|
||||
"ordinal": 21,
|
||||
"name": "primary_track",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 23,
|
||||
"ordinal": 22,
|
||||
"name": "downloaded_at",
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"ordinal": 24,
|
||||
"ordinal": 23,
|
||||
"name": "last_streamed_at",
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"ordinal": 25,
|
||||
"ordinal": 24,
|
||||
"name": "n_streams",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 26,
|
||||
"ordinal": 25,
|
||||
"name": "artists: _",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
|
@ -190,7 +185,6 @@
|
|||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
|
@ -210,5 +204,5 @@
|
|||
null
|
||||
]
|
||||
},
|
||||
"hash": "c51949fcbd59f9343c883e993cd7bcacffe3e5f5dd32e7ba9075819cceccfb0b"
|
||||
"hash": "a5cdaf05833b1bad4b90b6960937795f508f338f088c3570e908f7c2df8c602b"
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "insert into tracks (src_id, service, name, duration, duration_ms,\n album_id, album_pos, ul_artists, isrc, description, file_size, track_gain, primary_track)\nvalues ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)\non conflict (src_id, service) do update set\n name = excluded.name,\n duration = case when tracks.duration_ms and not excluded.duration_ms\n then tracks.duration else coalesce(excluded.duration, tracks.duration) end,\n duration_ms = excluded.duration_ms or tracks.duration_ms,\n album_id = excluded.album_id,\n album_pos = coalesce(excluded.album_pos, tracks.album_pos),\n ul_artists = coalesce(excluded.ul_artists, tracks.ul_artists),\n isrc = coalesce(excluded.isrc, tracks.isrc),\n description = coalesce(excluded.description, tracks.description),\n file_size = coalesce(excluded.file_size, tracks.file_size),\n track_gain = coalesce(excluded.track_gain, tracks.track_gain),\n primary_track = coalesce(excluded.primary_track, tracks.primary_track)\nreturning id",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
{
|
||||
"Custom": {
|
||||
"name": "music_service",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"ty",
|
||||
"yt",
|
||||
"sp",
|
||||
"mx"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Text",
|
||||
"Int4",
|
||||
"Bool",
|
||||
"Int4",
|
||||
"Int2",
|
||||
"TextArray",
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Int8",
|
||||
"Float4",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "b0c1e18c19c896a15971562dd66dfe718cf51c7d15540015076ca1c65ea9a631"
|
||||
}
|
|
@ -30,7 +30,6 @@ CREATE TABLE
|
|||
service public.music_service NOT NULL,
|
||||
NAME TEXT NOT NULL,
|
||||
duration INTEGER,
|
||||
duration_ms bool NOT NULL DEFAULT FALSE,
|
||||
album_pos SMALLINT,
|
||||
album_id INTEGER NOT NULL,
|
||||
ul_artists TEXT[],
|
||||
|
@ -55,9 +54,7 @@ COMMENT ON COLUMN public.tracks.service IS E'Service providing the track';
|
|||
|
||||
COMMENT ON COLUMN public.tracks.name IS E'Track name';
|
||||
|
||||
COMMENT ON COLUMN public.tracks.duration IS E'Duration of the track in milliseconds';
|
||||
|
||||
COMMENT ON COLUMN public.tracks.duration_ms IS E'True if the duration is in millisecond precision';
|
||||
COMMENT ON COLUMN public.tracks.duration IS E'Duration of the track in seconds';
|
||||
|
||||
COMMENT ON COLUMN public.tracks.file_size IS E'File size in bytes';
|
||||
|
||||
|
@ -551,27 +548,22 @@ $$;
|
|||
|
||||
ALTER FUNCTION public.set_header_image_date () OWNER TO postgres;
|
||||
|
||||
CREATE TRIGGER artists_set_image_date BEFORE INSERT
|
||||
OR
|
||||
UPDATE OF image_url ON public.artists FOR EACH ROW
|
||||
CREATE TRIGGER artists_set_image_date BEFORE
|
||||
INSERT OR UPDATE OF image_url ON public.artists FOR EACH ROW
|
||||
EXECUTE PROCEDURE public.set_image_date ();
|
||||
|
||||
CREATE TRIGGER artists_set_header_image_date BEFORE INSERT
|
||||
OR
|
||||
UPDATE OF header_image_url ON public.artists FOR EACH ROW
|
||||
CREATE TRIGGER artists_set_header_image_date BEFORE
|
||||
INSERT OR UPDATE OF header_image_url ON public.artists FOR EACH ROW
|
||||
EXECUTE PROCEDURE public.set_header_image_date ();
|
||||
|
||||
CREATE TRIGGER albums_set_image_date BEFORE INSERT
|
||||
OR
|
||||
UPDATE OF image_url ON public.albums FOR EACH ROW
|
||||
CREATE TRIGGER albums_set_image_date BEFORE
|
||||
INSERT OR UPDATE OF image_url ON public.albums FOR EACH ROW
|
||||
EXECUTE PROCEDURE public.set_image_date ();
|
||||
|
||||
CREATE TRIGGER playlists_set_image_date BEFORE INSERT
|
||||
OR
|
||||
UPDATE OF image_url ON public.playlists FOR EACH ROW
|
||||
CREATE TRIGGER playlists_set_image_date BEFORE
|
||||
INSERT OR UPDATE OF image_url ON public.playlists FOR EACH ROW
|
||||
EXECUTE PROCEDURE public.set_image_date ();
|
||||
|
||||
CREATE TRIGGER users_set_image_date BEFORE INSERT
|
||||
OR
|
||||
UPDATE OF image_url ON public.users FOR EACH ROW
|
||||
CREATE TRIGGER users_set_image_date BEFORE
|
||||
INSERT OR UPDATE OF image_url ON public.users FOR EACH ROW
|
||||
EXECUTE PROCEDURE public.set_image_date ();
|
||||
|
|
|
@ -8,7 +8,6 @@ Track(
|
|||
service: yt,
|
||||
name: "empty",
|
||||
duration: None,
|
||||
duration_ms: false,
|
||||
artists: [],
|
||||
album_id: 1,
|
||||
album: AlbumTag(
|
||||
|
|
|
@ -7,8 +7,7 @@ Track(
|
|||
src_id: "g0iRiJ_ck48",
|
||||
service: yt,
|
||||
name: "Aulë und Yavanna",
|
||||
duration: Some(216481),
|
||||
duration_ms: true,
|
||||
duration: Some(216),
|
||||
artists: [
|
||||
ArtistTag(
|
||||
id: Some("yt:UC_MxOdawj_BStPs4CKBYD0Q"),
|
||||
|
|
|
@ -6,7 +6,7 @@ TrackSlim(
|
|||
src_id: "g0iRiJ_ck48",
|
||||
service: yt,
|
||||
name: "Aulë und Yavanna",
|
||||
duration: Some(216481),
|
||||
duration: Some(216),
|
||||
artists: [
|
||||
ArtistTag(
|
||||
id: Some("yt:UC_MxOdawj_BStPs4CKBYD0Q"),
|
||||
|
|
|
@ -19,7 +19,6 @@ pub struct Track {
|
|||
pub service: MusicService,
|
||||
pub name: String,
|
||||
pub duration: Option<i32>,
|
||||
pub duration_ms: bool,
|
||||
pub artists: Vec<ArtistTag>,
|
||||
pub album_id: i32,
|
||||
pub album: AlbumTag,
|
||||
|
@ -43,7 +42,6 @@ struct TrackRow {
|
|||
service: MusicService,
|
||||
name: String,
|
||||
duration: Option<i32>,
|
||||
duration_ms: bool,
|
||||
album_id: i32,
|
||||
album_src_id: String,
|
||||
album_service: MusicService,
|
||||
|
@ -74,7 +72,6 @@ pub struct TrackNew<'a> {
|
|||
pub service: MusicService,
|
||||
pub name: &'a str,
|
||||
pub duration: Option<i32>,
|
||||
pub duration_ms: bool,
|
||||
pub album_id: i32,
|
||||
pub album_pos: Option<i16>,
|
||||
pub ul_artists: Option<&'a [String]>,
|
||||
|
@ -90,7 +87,6 @@ pub struct TrackNew<'a> {
|
|||
pub struct TrackUpdate<'a> {
|
||||
pub name: Option<&'a str>,
|
||||
pub duration: Option<Option<i32>>,
|
||||
pub duration_ms: Option<bool>,
|
||||
pub album_id: Option<i32>,
|
||||
pub album_pos: Option<Option<i16>>,
|
||||
pub ul_artists: Option<&'a [String]>,
|
||||
|
@ -192,7 +188,7 @@ impl Track {
|
|||
Id::Db(id) => {
|
||||
sqlx::query_as!(
|
||||
TrackRow,
|
||||
r#"select t.id, t.src_id, t.service as "service: _", t.name, t.duration, t.duration_ms,
|
||||
r#"select t.id, t.src_id, t.service as "service: _", t.name, t.duration,
|
||||
b.id as album_id, b.src_id as album_src_id, b.service as "album_service: _", b.name as album_name, b.release_date,
|
||||
b.album_type as "album_type: _", b.image_url, b.image_date,
|
||||
t.album_pos, t.ul_artists, t.isrc, t.description, t.file_size, t.track_gain, t.created_at, t.updated_at,
|
||||
|
@ -213,7 +209,7 @@ group by (t.id, b.id)"#,
|
|||
Id::Src(src_id, srv) => {
|
||||
let res = sqlx::query_as!(
|
||||
TrackRow,
|
||||
r#"select t.id, t.src_id, t.service as "service: _", t.name, t.duration, t.duration_ms,
|
||||
r#"select t.id, t.src_id, t.service as "service: _", t.name, t.duration,
|
||||
b.id as album_id, b.src_id as album_src_id, b.service as "album_service: _", b.name as album_name, b.release_date,
|
||||
b.album_type as "album_type: _", b.image_url, b.image_date,
|
||||
t.album_pos, t.ul_artists, t.isrc, t.description, t.file_size, t.track_gain, t.created_at, t.updated_at,
|
||||
|
@ -238,7 +234,7 @@ group by (t.id, b.id)"#,
|
|||
None => {
|
||||
sqlx::query_as!(
|
||||
TrackRow,
|
||||
r#"select t.id, t.src_id, t.service as "service: _", t.name, t.duration, t.duration_ms,
|
||||
r#"select t.id, t.src_id, t.service as "service: _", t.name, t.duration,
|
||||
b.id as album_id, b.src_id as album_src_id, b.service as "album_service: _", b.name as album_name, b.release_date,
|
||||
b.album_type as "album_type: _", b.image_url, b.image_date,
|
||||
t.album_pos, t.ul_artists, t.isrc, t.description, t.file_size, t.track_gain, t.created_at, t.updated_at,
|
||||
|
@ -413,14 +409,12 @@ impl TrackNew<'_> {
|
|||
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
|
||||
{
|
||||
let res = sqlx::query!(
|
||||
r#"insert into tracks (src_id, service, name, duration, duration_ms,
|
||||
r#"insert into tracks (src_id, service, name, duration,
|
||||
album_id, album_pos, ul_artists, isrc, description, file_size, track_gain, primary_track)
|
||||
values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
|
||||
values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
||||
on conflict (src_id, service) do update set
|
||||
name = excluded.name,
|
||||
duration = case when tracks.duration_ms and not excluded.duration_ms
|
||||
then tracks.duration else coalesce(excluded.duration, tracks.duration) end,
|
||||
duration_ms = excluded.duration_ms or tracks.duration_ms,
|
||||
duration = coalesce(excluded.duration, tracks.duration),
|
||||
album_id = excluded.album_id,
|
||||
album_pos = coalesce(excluded.album_pos, tracks.album_pos),
|
||||
ul_artists = coalesce(excluded.ul_artists, tracks.ul_artists),
|
||||
|
@ -434,7 +428,6 @@ returning id"#,
|
|||
self.service as MusicService,
|
||||
self.name,
|
||||
self.duration,
|
||||
self.duration_ms,
|
||||
self.album_id,
|
||||
self.album_pos,
|
||||
self.ul_artists.as_deref(),
|
||||
|
@ -471,14 +464,6 @@ impl TrackUpdate<'_> {
|
|||
query.push_bind(duration);
|
||||
n += 1;
|
||||
}
|
||||
if let Some(duration_ms) = &self.duration_ms {
|
||||
if n != 0 {
|
||||
query.push(", ");
|
||||
}
|
||||
query.push("duration_ms=");
|
||||
query.push_bind(duration_ms);
|
||||
n += 1;
|
||||
}
|
||||
if let Some(album_id) = &self.album_id {
|
||||
if n != 0 {
|
||||
query.push(", ");
|
||||
|
@ -693,7 +678,6 @@ impl From<TrackRow> for Track {
|
|||
service: value.service,
|
||||
name: value.name,
|
||||
duration: value.duration,
|
||||
duration_ms: value.duration_ms,
|
||||
artists: util::map_artists(value.artists, value.ul_artists),
|
||||
album_id: value.album_id,
|
||||
album: AlbumTag {
|
||||
|
@ -747,7 +731,7 @@ impl From<Track> for tiraya_api_model::Track {
|
|||
Self {
|
||||
id: tiraya_api_model::TId::new(t.src_id, t.service.into()),
|
||||
name: t.name,
|
||||
duration: t.duration.map(|d| d / 1000),
|
||||
duration: t.duration,
|
||||
artists: t
|
||||
.artists
|
||||
.into_iter()
|
||||
|
@ -771,7 +755,7 @@ impl From<TrackSlim> for tiraya_api_model::TrackSlim {
|
|||
Self {
|
||||
id: tiraya_api_model::TId::new(t.src_id, t.service.into()),
|
||||
name: t.name,
|
||||
duration: t.duration.map(|d| d / 1000),
|
||||
duration: t.duration,
|
||||
artists: t
|
||||
.artists
|
||||
.into_iter()
|
||||
|
@ -801,8 +785,7 @@ mod tests {
|
|||
src_id: "g0iRiJ_ck48",
|
||||
service: MusicService::YouTube,
|
||||
name: "Aulë und Yavanna",
|
||||
duration: Some(216481),
|
||||
duration_ms: true,
|
||||
duration: Some(216),
|
||||
album_id: 1,
|
||||
album_pos: Some(1),
|
||||
ul_artists: Some(&ul_artists),
|
||||
|
@ -854,7 +837,6 @@ mod tests {
|
|||
let clear = TrackUpdate {
|
||||
name: Some("empty"),
|
||||
duration: Some(None),
|
||||
duration_ms: Some(false),
|
||||
album_id: None,
|
||||
album_pos: Some(None),
|
||||
ul_artists: Some(&[]),
|
||||
|
|
|
@ -850,7 +850,7 @@ impl YouTubeExtractor {
|
|||
src_id: &track.id,
|
||||
service: MusicService::YouTube,
|
||||
name: &track.name,
|
||||
duration: track.duration.and_then(|d| (d * 1000).try_into().ok()),
|
||||
duration: track.duration.and_then(|v| v.try_into().ok()),
|
||||
album_id,
|
||||
album_pos: track.track_nr.and_then(|v| v.try_into().ok()),
|
||||
ul_artists: ul_artists.as_deref(),
|
||||
|
@ -1346,8 +1346,7 @@ mod tests {
|
|||
src_id: "voLnMeuXQo4",
|
||||
service: yt,
|
||||
name: "PTT (Paint the Town)",
|
||||
duration: Some(202000),
|
||||
duration_ms: false,
|
||||
duration: Some(202),
|
||||
artists: [
|
||||
ArtistTag(
|
||||
id: Some("yt:UCa4ZqZPRjz9MYYnfpoh2few"),
|
||||
|
|
|
@ -7,7 +7,7 @@ expression: tracks
|
|||
src_id: "QapQgsYqR0o",
|
||||
service: yt,
|
||||
name: "747",
|
||||
duration: Some(144000),
|
||||
duration: Some(144),
|
||||
artists: [
|
||||
ArtistTag(
|
||||
id: Some("yt:UCFTcSVPYRWlDoHisR-ZKwgw"),
|
||||
|
@ -29,7 +29,7 @@ expression: tracks
|
|||
src_id: "gNd1pbc0suY",
|
||||
service: yt,
|
||||
name: "Süchtig",
|
||||
duration: Some(192000),
|
||||
duration: Some(192),
|
||||
artists: [
|
||||
ArtistTag(
|
||||
id: Some("yt:UCFTcSVPYRWlDoHisR-ZKwgw"),
|
||||
|
@ -51,7 +51,7 @@ expression: tracks
|
|||
src_id: "DIhEDU66xh8",
|
||||
service: yt,
|
||||
name: "Happy End (feat. Sido)",
|
||||
duration: Some(138000),
|
||||
duration: Some(138),
|
||||
artists: [
|
||||
ArtistTag(
|
||||
id: Some("yt:UCFTcSVPYRWlDoHisR-ZKwgw"),
|
||||
|
@ -73,7 +73,7 @@ expression: tracks
|
|||
src_id: "oQI0IT2cEfw",
|
||||
service: yt,
|
||||
name: "VIBE",
|
||||
duration: Some(157000),
|
||||
duration: Some(157),
|
||||
artists: [
|
||||
ArtistTag(
|
||||
id: Some("yt:UCFTcSVPYRWlDoHisR-ZKwgw"),
|
||||
|
@ -95,7 +95,7 @@ expression: tracks
|
|||
src_id: "C_pZsCHUqUU",
|
||||
service: yt,
|
||||
name: "Melatonin",
|
||||
duration: Some(140000),
|
||||
duration: Some(140),
|
||||
artists: [
|
||||
ArtistTag(
|
||||
id: Some("yt:UCFTcSVPYRWlDoHisR-ZKwgw"),
|
||||
|
@ -121,7 +121,7 @@ expression: tracks
|
|||
src_id: "pjCRr1zHpLs",
|
||||
service: yt,
|
||||
name: "Zehenspitzen",
|
||||
duration: Some(175000),
|
||||
duration: Some(175),
|
||||
artists: [
|
||||
ArtistTag(
|
||||
id: Some("yt:UCFTcSVPYRWlDoHisR-ZKwgw"),
|
||||
|
@ -143,7 +143,7 @@ expression: tracks
|
|||
src_id: "1xwJWeWdpHw",
|
||||
service: yt,
|
||||
name: "Summer Nights",
|
||||
duration: Some(178000),
|
||||
duration: Some(178),
|
||||
artists: [
|
||||
ArtistTag(
|
||||
id: Some("yt:UCFTcSVPYRWlDoHisR-ZKwgw"),
|
||||
|
@ -165,7 +165,7 @@ expression: tracks
|
|||
src_id: "2GY7M08AtM4",
|
||||
service: yt,
|
||||
name: "Schwarze Herzen",
|
||||
duration: Some(145000),
|
||||
duration: Some(145),
|
||||
artists: [
|
||||
ArtistTag(
|
||||
id: Some("yt:UCFTcSVPYRWlDoHisR-ZKwgw"),
|
||||
|
@ -191,7 +191,7 @@ expression: tracks
|
|||
src_id: "hw1bZ0unqgg",
|
||||
service: yt,
|
||||
name: "Stadtbezirk",
|
||||
duration: Some(170000),
|
||||
duration: Some(170),
|
||||
artists: [
|
||||
ArtistTag(
|
||||
id: Some("yt:UCFTcSVPYRWlDoHisR-ZKwgw"),
|
||||
|
@ -213,7 +213,7 @@ expression: tracks
|
|||
src_id: "c882l-0i1Ds",
|
||||
service: yt,
|
||||
name: "No Hard Feelings",
|
||||
duration: Some(159000),
|
||||
duration: Some(159),
|
||||
artists: [
|
||||
ArtistTag(
|
||||
id: Some("yt:UCFTcSVPYRWlDoHisR-ZKwgw"),
|
||||
|
@ -235,7 +235,7 @@ expression: tracks
|
|||
src_id: "5WRcgP_WDbY",
|
||||
service: yt,
|
||||
name: "Bitte Geh Nicht",
|
||||
duration: Some(132000),
|
||||
duration: Some(132),
|
||||
artists: [
|
||||
ArtistTag(
|
||||
id: Some("yt:UCFTcSVPYRWlDoHisR-ZKwgw"),
|
||||
|
@ -257,7 +257,7 @@ expression: tracks
|
|||
src_id: "bdXaTyLRhtQ",
|
||||
service: yt,
|
||||
name: "Als ob du mich liebst (feat. Vanessa Mai)",
|
||||
duration: Some(142000),
|
||||
duration: Some(142),
|
||||
artists: [
|
||||
ArtistTag(
|
||||
id: Some("yt:UCymtzNvoLbYPhnZbd2CcCZA"),
|
||||
|
@ -279,7 +279,7 @@ expression: tracks
|
|||
src_id: "u1xwYB0ViHQ",
|
||||
service: yt,
|
||||
name: "Aus & Vorbei",
|
||||
duration: Some(135000),
|
||||
duration: Some(135),
|
||||
artists: [
|
||||
ArtistTag(
|
||||
id: Some("yt:UCFTcSVPYRWlDoHisR-ZKwgw"),
|
||||
|
@ -301,7 +301,7 @@ expression: tracks
|
|||
src_id: "jI9nQeKGf4E",
|
||||
service: yt,
|
||||
name: "Unendlich",
|
||||
duration: Some(169000),
|
||||
duration: Some(169),
|
||||
artists: [
|
||||
ArtistTag(
|
||||
id: Some("yt:UCpuUi6e7YMwhSg8Q6erovXg"),
|
||||
|
|
|
@ -7,7 +7,7 @@ expression: album_tracks
|
|||
src_id: "BGcUVJXViqQ",
|
||||
service: yt,
|
||||
name: "고블린 Goblin",
|
||||
duration: Some(194000),
|
||||
duration: Some(194),
|
||||
artists: [
|
||||
ArtistTag(
|
||||
id: Some("yt:UCfwCE5VhPMGxNPFxtVv7lRw"),
|
||||
|
@ -29,7 +29,7 @@ expression: album_tracks
|
|||
src_id: "7_Bav4c7UGM",
|
||||
service: yt,
|
||||
name: "온더문 On The Moon",
|
||||
duration: Some(256000),
|
||||
duration: Some(256),
|
||||
artists: [
|
||||
ArtistTag(
|
||||
id: Some("yt:UCfwCE5VhPMGxNPFxtVv7lRw"),
|
||||
|
@ -51,7 +51,7 @@ expression: album_tracks
|
|||
src_id: "kzUZABVj5UQ",
|
||||
service: yt,
|
||||
name: "도로시 Dorothy",
|
||||
duration: Some(241000),
|
||||
duration: Some(241),
|
||||
artists: [
|
||||
ArtistTag(
|
||||
id: Some("yt:UCfwCE5VhPMGxNPFxtVv7lRw"),
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -7,8 +7,7 @@ Track(
|
|||
src_id: "BL-aIpCLWnU",
|
||||
service: yt,
|
||||
name: "Black Mamba",
|
||||
duration: Some(175000),
|
||||
duration_ms: false,
|
||||
duration: Some(175),
|
||||
artists: [
|
||||
ArtistTag(
|
||||
id: Some("yt:UCEdZAdnnKqbaHOlv8nM6OtA"),
|
||||
|
|
|
@ -8,7 +8,7 @@ expression: pl_entries
|
|||
src_id: "xwFRUfisow8",
|
||||
service: yt,
|
||||
name: "NXDE - (G)I-DLE Cover Español",
|
||||
duration: Some(180000),
|
||||
duration: Some(180),
|
||||
artists: [
|
||||
ArtistTag(
|
||||
id: Some("yt:UCTr0VOWDDZAR1ij4CjkD0VA"),
|
||||
|
@ -34,7 +34,7 @@ expression: pl_entries
|
|||
src_id: "9W6U3g2TecE",
|
||||
service: yt,
|
||||
name: "SHUT DOWN - BLACKPINK Cover Español",
|
||||
duration: Some(177000),
|
||||
duration: Some(177),
|
||||
artists: [
|
||||
ArtistTag(
|
||||
id: Some("yt:UCTr0VOWDDZAR1ij4CjkD0VA"),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue