feat: added api conversation + api review
This commit is contained in:
32
.cursor/rules/api-conventions.mdc
Normal file
32
.cursor/rules/api-conventions.mdc
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
description: REST API запросы — создавать функции в furumiApi.ts (только furumi-node-player/client)
|
||||||
|
globs: furumi-node-player/client/**/*.{ts,tsx}
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
|
||||||
|
# REST API в furumi-node-player
|
||||||
|
|
||||||
|
**Область действия:** правило применяется только к проекту `furumi-node-player/client/`. В остальных частях репозитория можно использовать другой подход.
|
||||||
|
|
||||||
|
Чтобы выполнить REST API запрос, нужно создать функцию, которая принимает необходимые параметры и вызывает `furumiApi`. Все такие функции должны находиться в файле `furumi-node-player/client/src/furumiApi.ts`.
|
||||||
|
|
||||||
|
## Правила
|
||||||
|
|
||||||
|
1. **Не вызывать `furumiApi` напрямую** из компонентов или других модулей.
|
||||||
|
2. **Добавлять новую функцию** в `furumiApi.ts` для каждого эндпоинта.
|
||||||
|
3. Функция принимает нужные параметры и возвращает `Promise` с данными (или `null` при ошибке).
|
||||||
|
|
||||||
|
## Пример
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// furumiApi.ts — добавлять сюда
|
||||||
|
export async function getSomething(id: string) {
|
||||||
|
const res = await furumiApi.get(`/something/${id}`).catch(() => null)
|
||||||
|
return res?.data ?? null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// FurumiPlayer.tsx — использовать функцию, не furumiApi напрямую
|
||||||
|
const data = await getSomething(id)
|
||||||
|
```
|
||||||
@@ -1,6 +1,15 @@
|
|||||||
import { useEffect, useRef, useState, type MouseEvent as ReactMouseEvent } from 'react'
|
import { useEffect, useRef, useState, type MouseEvent as ReactMouseEvent } from 'react'
|
||||||
import './furumi-player.css'
|
import './furumi-player.css'
|
||||||
import { API_ROOT, furumiApi } from './furumiApi'
|
import {
|
||||||
|
API_ROOT,
|
||||||
|
getArtists,
|
||||||
|
getArtistAlbums,
|
||||||
|
getAlbumTracks,
|
||||||
|
getArtistTracks,
|
||||||
|
searchTracks,
|
||||||
|
getTrackInfo,
|
||||||
|
preloadStream,
|
||||||
|
} from './furumiApi'
|
||||||
import { SearchDropdown } from './components/SearchDropdown'
|
import { SearchDropdown } from './components/SearchDropdown'
|
||||||
import { Breadcrumbs } from './components/Breadcrumbs'
|
import { Breadcrumbs } from './components/Breadcrumbs'
|
||||||
import { LibraryList } from './components/LibraryList'
|
import { LibraryList } from './components/LibraryList'
|
||||||
@@ -107,7 +116,7 @@ export function FurumiPlayer() {
|
|||||||
setBreadcrumb([{ label: 'Artists', action: showArtists }])
|
setBreadcrumb([{ label: 'Artists', action: showArtists }])
|
||||||
setLibraryLoading(true)
|
setLibraryLoading(true)
|
||||||
setLibraryError(null)
|
setLibraryError(null)
|
||||||
const artists = (await furumiApi.get('/artists').catch(() => null))?.data ?? null
|
const artists = await getArtists()
|
||||||
if (!artists) {
|
if (!artists) {
|
||||||
setLibraryLoading(false)
|
setLibraryLoading(false)
|
||||||
setLibraryError('Error')
|
setLibraryError('Error')
|
||||||
@@ -133,7 +142,7 @@ export function FurumiPlayer() {
|
|||||||
])
|
])
|
||||||
setLibraryLoading(true)
|
setLibraryLoading(true)
|
||||||
setLibraryError(null)
|
setLibraryError(null)
|
||||||
const albums = (await furumiApi.get('/artists/' + artistSlug + '/albums').catch(() => null))?.data ?? null
|
const albums = await getArtistAlbums(artistSlug)
|
||||||
if (!albums) {
|
if (!albums) {
|
||||||
setLibraryLoading(false)
|
setLibraryLoading(false)
|
||||||
setLibraryError('Error')
|
setLibraryError('Error')
|
||||||
@@ -182,7 +191,7 @@ export function FurumiPlayer() {
|
|||||||
])
|
])
|
||||||
setLibraryLoading(true)
|
setLibraryLoading(true)
|
||||||
setLibraryError(null)
|
setLibraryError(null)
|
||||||
const tracks = (await furumiApi.get('/albums/' + albumSlug).catch(() => null))?.data ?? null
|
const tracks = await getAlbumTracks(albumSlug)
|
||||||
if (!tracks) {
|
if (!tracks) {
|
||||||
setLibraryLoading(false)
|
setLibraryLoading(false)
|
||||||
setLibraryError('Error')
|
setLibraryError('Error')
|
||||||
@@ -252,7 +261,7 @@ export function FurumiPlayer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function addAlbumToQueue(albumSlug: string, playFirst?: boolean) {
|
async function addAlbumToQueue(albumSlug: string, playFirst?: boolean) {
|
||||||
const tracks = (await furumiApi.get('/albums/' + albumSlug).catch(() => null))?.data ?? null
|
const tracks = await getAlbumTracks(albumSlug)
|
||||||
if (!tracks || !(tracks as any[]).length) return
|
if (!tracks || !(tracks as any[]).length) return
|
||||||
const list = tracks as any[]
|
const list = tracks as any[]
|
||||||
let firstIdx = queue.length
|
let firstIdx = queue.length
|
||||||
@@ -272,7 +281,7 @@ export function FurumiPlayer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function playAllArtistTracks(artistSlug: string) {
|
async function playAllArtistTracks(artistSlug: string) {
|
||||||
const tracks = (await furumiApi.get('/artists/' + artistSlug + '/tracks').catch(() => null))?.data ?? null
|
const tracks = await getArtistTracks(artistSlug)
|
||||||
if (!tracks || !(tracks as any[]).length) return
|
if (!tracks || !(tracks as any[]).length) return
|
||||||
const list = tracks as any[]
|
const list = tracks as any[]
|
||||||
clearQueue()
|
clearQueue()
|
||||||
@@ -487,7 +496,7 @@ export function FurumiPlayer() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
searchTimer = window.setTimeout(async () => {
|
searchTimer = window.setTimeout(async () => {
|
||||||
const results = (await furumiApi.get('/search?q=' + encodeURIComponent(q)).catch(() => null))?.data ?? null
|
const results = await searchTracks(q)
|
||||||
if (!results || !(results as any[]).length) {
|
if (!results || !(results as any[]).length) {
|
||||||
closeSearch()
|
closeSearch()
|
||||||
return
|
return
|
||||||
@@ -511,7 +520,7 @@ export function FurumiPlayer() {
|
|||||||
{ slug, title: '', artist: '', album_slug: null, duration: null },
|
{ slug, title: '', artist: '', album_slug: null, duration: null },
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
void furumiApi.get('/stream/' + slug).catch(() => null)
|
void preloadStream(slug)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
searchSelectRef.current = onSearchSelect
|
searchSelectRef.current = onSearchSelect
|
||||||
@@ -617,7 +626,7 @@ export function FurumiPlayer() {
|
|||||||
const url = new URL(window.location.href)
|
const url = new URL(window.location.href)
|
||||||
const urlSlug = url.searchParams.get('t')
|
const urlSlug = url.searchParams.get('t')
|
||||||
if (urlSlug) {
|
if (urlSlug) {
|
||||||
const info = (await furumiApi.get('/tracks/' + urlSlug).catch(() => null))?.data ?? null
|
const info = await getTrackInfo(urlSlug)
|
||||||
if (info) {
|
if (info) {
|
||||||
addTrackToQueue(
|
addTrackToQueue(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,3 +10,37 @@ export const furumiApi = axios.create({
|
|||||||
headers: apiKey ? { 'x-api-key': apiKey } : {},
|
headers: apiKey ? { 'x-api-key': apiKey } : {},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export async function getArtists() {
|
||||||
|
const res = await furumiApi.get('/artists').catch(() => null)
|
||||||
|
return res?.data ?? null
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getArtistAlbums(artistSlug: string) {
|
||||||
|
const res = await furumiApi.get(`/artists/${artistSlug}/albums`).catch(() => null)
|
||||||
|
return res?.data ?? null
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAlbumTracks(albumSlug: string) {
|
||||||
|
const res = await furumiApi.get(`/albums/${albumSlug}`).catch(() => null)
|
||||||
|
return res?.data ?? null
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getArtistTracks(artistSlug: string) {
|
||||||
|
const res = await furumiApi.get(`/artists/${artistSlug}/tracks`).catch(() => null)
|
||||||
|
return res?.data ?? null
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function searchTracks(query: string) {
|
||||||
|
const res = await furumiApi.get(`/search?q=${encodeURIComponent(query)}`).catch(() => null)
|
||||||
|
return res?.data ?? null
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getTrackInfo(trackSlug: string) {
|
||||||
|
const res = await furumiApi.get(`/tracks/${trackSlug}`).catch(() => null)
|
||||||
|
return res?.data ?? null
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function preloadStream(trackSlug: string) {
|
||||||
|
await furumiApi.get(`/stream/${trackSlug}`).catch(() => null)
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user