From 9984bb804a33c052b035b215522b71958100c6c6 Mon Sep 17 00:00:00 2001 From: bivashy Date: Thu, 8 Jan 2026 03:57:00 +0500 Subject: [PATCH] Implement "import history" in import page --- .../uploadentry/PlaylistUploadEntry.vue | 52 ++---- .../import/uploadentry/SingleUploadEntry.vue | 11 +- .../import/uploadentry/UploadEntry.vue | 27 ++++ .../playlists/select/PlaylistsNotFound.vue | 6 + app/composeables/api/axios-instance.ts | 5 +- app/composeables/api/event-source.ts | 94 +++++++++++ .../api/models/baseTrackProgress.ts | 16 ++ .../api/models/baseTrackProgressType.ts | 16 ++ app/composeables/api/models/index.ts | 15 +- .../api/models/playlistProgress.ts | 10 ++ .../api/models/playlistProgressAllOf.ts | 14 ++ .../api/models/playlistProgressAllOfStatus.ts | 15 ++ .../api/models/playlistReadDTO.ts | 14 -- .../api/models/singleTrackProgress.ts | 10 ++ ...eateDTO.ts => singleTrackProgressAllOf.ts} | 5 +- .../api/models/streamProgress200Item.ts | 10 ++ app/composeables/api/models/trackResponse.ts | 1 + ...AfterRequest.ts => youtubeTrackRequest.ts} | 5 +- .../playlist-controller.ts | 19 +-- .../progress-sse-controller.ts | 99 ++++++++++++ .../api/s-3-controller/s-3-controller.ts | 19 +-- .../api/track-controller/track-controller.ts | 153 ++++++++++++++++-- app/pages/import.vue | 90 ++++++++--- bun.lock | 5 + orval.config.js | 6 +- package.json | 1 + 26 files changed, 591 insertions(+), 127 deletions(-) create mode 100644 app/components/internal/import/uploadentry/UploadEntry.vue create mode 100644 app/composeables/api/event-source.ts create mode 100644 app/composeables/api/models/baseTrackProgress.ts create mode 100644 app/composeables/api/models/baseTrackProgressType.ts create mode 100644 app/composeables/api/models/playlistProgress.ts create mode 100644 app/composeables/api/models/playlistProgressAllOf.ts create mode 100644 app/composeables/api/models/playlistProgressAllOfStatus.ts delete mode 100644 app/composeables/api/models/playlistReadDTO.ts create mode 100644 app/composeables/api/models/singleTrackProgress.ts rename app/composeables/api/models/{playlistCreateDTO.ts => singleTrackProgressAllOf.ts} (68%) create mode 100644 app/composeables/api/models/streamProgress200Item.ts rename app/composeables/api/models/{trackReorderAfterRequest.ts => youtubeTrackRequest.ts} (56%) create mode 100644 app/composeables/api/progress-sse-controller/progress-sse-controller.ts diff --git a/app/components/internal/import/uploadentry/PlaylistUploadEntry.vue b/app/components/internal/import/uploadentry/PlaylistUploadEntry.vue index d58ad80..0d3249c 100644 --- a/app/components/internal/import/uploadentry/PlaylistUploadEntry.vue +++ b/app/components/internal/import/uploadentry/PlaylistUploadEntry.vue @@ -15,10 +15,6 @@

{{ trackCount }} track(s)

- -

- {{ type }} -

@@ -56,7 +52,7 @@

- + Playlist Upload Details @@ -84,25 +80,19 @@

{{ trackCount }} tracks

-
- -

{{ type }}

-
-
+

yt-dlp Output

-
-
{{
-                                playlistProgressData.ytdlnStdout }}
+
{{ ytdlnStdout }}
@@ -130,7 +120,6 @@ import { CassetteTape, Copy, - Dot, EllipsisVertical, Eye, FileQuestionMark, @@ -139,22 +128,15 @@ import { Trash2 } from 'lucide-vue-next'; import { ref } from 'vue'; +import type { PlaylistProgressAllOfStatus } from '~/composeables/api/models'; interface Props { title: string trackCount?: number - type?: string + ytdlnStdout: string + status: PlaylistProgressAllOfStatus progress?: number error?: string - playlistProgressData?: { - playlistId: number - trackSourceId: number - userId: number - timestamp: number - ytdlnStdout: string - overallProgress: number - status: 'LOADING' | 'FINISHED' - } } const props = withDefaults(defineProps(), { @@ -167,36 +149,34 @@ const emit = defineEmits<{ const isDialogOpen = ref(false); -const hasLoaded = props.trackCount && props.type; +const hasLoaded = props.trackCount; const hasProgress = props.progress !== undefined && props.progress > 0; const hasError = props.error; const openDialog = () => { isDialogOpen.value = true; - console.log(isDialogOpen.value) }; +const toggleDialog = (value: boolean) => { + isDialogOpen.value = value; +} + const getStatusColor = () => { if (hasError) return 'bg-destructive'; - if (props.playlistProgressData?.status === 'FINISHED') return 'bg-green-500'; - if (props.playlistProgressData?.status === 'LOADING') return 'bg-blue-500'; + if (props.status === 'FINISHED') return 'bg-green-500'; + if (props.status === 'LOADING') return 'bg-blue-500'; if (hasProgress) return 'bg-amber-500'; return 'bg-gray-500'; }; const getStatusText = () => { if (hasError) return 'Error'; - if (props.playlistProgressData?.status === 'FINISHED') return 'Completed'; - if (props.playlistProgressData?.status === 'LOADING') return 'Loading'; + if (props.status === 'FINISHED') return 'Completed'; + if (props.status === 'LOADING') return 'Loading'; if (hasProgress) return 'In Progress'; return 'Pending'; }; -const formatTimestamp = (timestamp?: number) => { - if (!timestamp) return 'N/A'; - return new Date(timestamp).toLocaleString(); -}; - const copyToClipboard = async (text: string) => { try { await navigator.clipboard.writeText(text); diff --git a/app/components/internal/import/uploadentry/SingleUploadEntry.vue b/app/components/internal/import/uploadentry/SingleUploadEntry.vue index f5b388c..3b22bca 100644 --- a/app/components/internal/import/uploadentry/SingleUploadEntry.vue +++ b/app/components/internal/import/uploadentry/SingleUploadEntry.vue @@ -19,10 +19,6 @@

{{ format }}

- -

- {{ type }} -

@@ -85,10 +81,6 @@

{{ size }}

-
- -

{{ type }}

-
@@ -194,7 +186,6 @@ interface Props { title: string size?: string format?: string - type?: string progress?: number error?: string trackProgressData?: { @@ -219,7 +210,7 @@ const emit = defineEmits<{ const isDialogOpen = ref(false); -const hasLoaded = props.size && props.format && props.type; +const hasLoaded = props.size && props.format; const hasProgress = props.progress !== undefined && props.progress > 0; const hasError = props.error; diff --git a/app/components/internal/import/uploadentry/UploadEntry.vue b/app/components/internal/import/uploadentry/UploadEntry.vue new file mode 100644 index 0000000..cc988d4 --- /dev/null +++ b/app/components/internal/import/uploadentry/UploadEntry.vue @@ -0,0 +1,27 @@ + + + diff --git a/app/components/internal/playlists/select/PlaylistsNotFound.vue b/app/components/internal/playlists/select/PlaylistsNotFound.vue index ba79856..09b9c03 100644 --- a/app/components/internal/playlists/select/PlaylistsNotFound.vue +++ b/app/components/internal/playlists/select/PlaylistsNotFound.vue @@ -1,6 +1,12 @@ diff --git a/app/composeables/api/axios-instance.ts b/app/composeables/api/axios-instance.ts index 798c85a..d5c5779 100644 --- a/app/composeables/api/axios-instance.ts +++ b/app/composeables/api/axios-instance.ts @@ -1,7 +1,7 @@ import Axios, { type AxiosRequestConfig, type AxiosResponse } from 'axios'; export const AXIOS_INSTANCE = Axios.create(); -export const axiosInstance = ( +const axiosInstance = ( config: AxiosRequestConfig, options?: AxiosRequestConfig, ): Promise> => { @@ -13,7 +13,7 @@ export const axiosInstance = ( ...options, baseURL: baseURL, auth: { - username: 'user', + username: 'user1', password: 'password', } }, @@ -23,3 +23,4 @@ export const axiosInstance = ( return promise; }; +export default axiosInstance; diff --git a/app/composeables/api/event-source.ts b/app/composeables/api/event-source.ts new file mode 100644 index 0000000..e9e5553 --- /dev/null +++ b/app/composeables/api/event-source.ts @@ -0,0 +1,94 @@ +import type { AxiosRequestConfig } from "axios"; +import { ErrorEvent, EventSource } from 'eventsource' + +type Unarray = T extends Array ? U : T; + +export interface EventSourceListener { + handle: (callback: (data: T) => void) => EventSourceListener; + close: () => void; + onError: (callback: (error: ErrorEvent) => void) => EventSourceListener; + onOpen: (callback: (event: Event) => void) => EventSourceListener; +} + +const eventSource = ( + config: AxiosRequestConfig, + options?: AxiosRequestConfig, +): Promise>> => { + return new Promise((resolve, reject) => { + try { + const baseURL = useRuntimeConfig().public.apiBaseUrl; + const endpoint = config.url; + const eventSource = new EventSource(baseURL + endpoint, { + fetch: (input, init) => fetch(input, { + ...init, + headers: { + ...init.headers, + Authorization: 'Basic ' + btoa('user1' + ":" + 'password'), + }, + }) + }) + + let messageCallback: ((data: Unarray) => void) | null = null; + let errorCallback: ((error: ErrorEvent) => void) | null = null; + let openCallback: ((event: Event) => void) | null = null; + + eventSource.addEventListener("message", (event: MessageEvent) => { + if (messageCallback) { + try { + let data = JSON.parse(event.data) as Unarray; + messageCallback(data); + } catch (error) { + console.error('Error parsing EventSource data:', error); + } + } + }); + + eventSource.addEventListener('error', (event) => { + if (errorCallback) { + errorCallback(event); + } + }); + + eventSource.addEventListener('open', (event) => { + if (openCallback) { + openCallback(event); + } + }); + + const listener: EventSourceListener> = { + handle: (callback: (data: Unarray) => void) => { + messageCallback = callback; + return listener; + }, + + onError: (callback: (error: ErrorEvent) => void) => { + errorCallback = callback; + return listener; + }, + + onOpen: (callback: (event: Event) => void) => { + openCallback = callback; + return listener; + }, + + close: () => { + eventSource.close(); + messageCallback = null; + errorCallback = null; + openCallback = null; + } + }; + + resolve(listener); + setTimeout(() => { + if (eventSource.readyState === EventSource.CLOSED && !errorCallback) { + reject(new Error('EventSource connection failed')); + } + }, 5000); + } catch (error) { + reject(error); + } + }); +} + +export default eventSource; diff --git a/app/composeables/api/models/baseTrackProgress.ts b/app/composeables/api/models/baseTrackProgress.ts new file mode 100644 index 0000000..7fcb5fb --- /dev/null +++ b/app/composeables/api/models/baseTrackProgress.ts @@ -0,0 +1,16 @@ +/** + * Generated by orval v7.16.0 🍺 + * Do not edit manually. + * OpenAPI definition + * OpenAPI spec version: v0 + */ +import type { BaseTrackProgressType } from './baseTrackProgressType'; + +export interface BaseTrackProgress { + id?: string; + playlistId?: number; + trackSourceId?: number; + userId?: number; + timestamp?: number; + type?: BaseTrackProgressType; +} diff --git a/app/composeables/api/models/baseTrackProgressType.ts b/app/composeables/api/models/baseTrackProgressType.ts new file mode 100644 index 0000000..c2e09ed --- /dev/null +++ b/app/composeables/api/models/baseTrackProgressType.ts @@ -0,0 +1,16 @@ +/** + * Generated by orval v7.16.0 🍺 + * Do not edit manually. + * OpenAPI definition + * OpenAPI spec version: v0 + */ + +export type BaseTrackProgressType = typeof BaseTrackProgressType[keyof typeof BaseTrackProgressType]; + + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const BaseTrackProgressType = { + PLAYLIST: 'PLAYLIST', + TRACK: 'TRACK', + EXTERNAL_TRACK: 'EXTERNAL_TRACK', +} as const; diff --git a/app/composeables/api/models/index.ts b/app/composeables/api/models/index.ts index 7108d50..8a40eeb 100644 --- a/app/composeables/api/models/index.ts +++ b/app/composeables/api/models/index.ts @@ -6,14 +6,19 @@ */ export * from './addLocalTrackRequest'; -export * from './playlistCreateDTO'; +export * from './baseTrackProgress'; +export * from './baseTrackProgressType'; export * from './playlistCreateRequest'; -export * from './playlistReadDTO'; +export * from './playlistProgress'; +export * from './playlistProgressAllOf'; +export * from './playlistProgressAllOfStatus'; export * from './playlistReadResponse'; export * from './playlistTrackResponse'; export * from './readParams'; +export * from './singleTrackProgress'; +export * from './singleTrackProgressAllOf'; +export * from './streamProgress200Item'; export * from './trackBulkReorderRequest'; -export * from './trackReoderAfterRequest'; -export * from './trackReorderAfterRequest'; export * from './trackResponse'; -export * from './uploadBody'; \ No newline at end of file +export * from './uploadBody'; +export * from './youtubeTrackRequest'; \ No newline at end of file diff --git a/app/composeables/api/models/playlistProgress.ts b/app/composeables/api/models/playlistProgress.ts new file mode 100644 index 0000000..30e2c49 --- /dev/null +++ b/app/composeables/api/models/playlistProgress.ts @@ -0,0 +1,10 @@ +/** + * Generated by orval v7.16.0 🍺 + * Do not edit manually. + * OpenAPI definition + * OpenAPI spec version: v0 + */ +import type { BaseTrackProgress } from './baseTrackProgress'; +import type { PlaylistProgressAllOf } from './playlistProgressAllOf'; + +export type PlaylistProgress = BaseTrackProgress & PlaylistProgressAllOf; diff --git a/app/composeables/api/models/playlistProgressAllOf.ts b/app/composeables/api/models/playlistProgressAllOf.ts new file mode 100644 index 0000000..23fc795 --- /dev/null +++ b/app/composeables/api/models/playlistProgressAllOf.ts @@ -0,0 +1,14 @@ +/** + * Generated by orval v7.16.0 🍺 + * Do not edit manually. + * OpenAPI definition + * OpenAPI spec version: v0 + */ +import type { PlaylistProgressAllOfStatus } from './playlistProgressAllOfStatus'; + +export type PlaylistProgressAllOf = { + ytdlnStdout?: string; + overallProgress?: number; + trackCount?: number; + status?: PlaylistProgressAllOfStatus; +}; diff --git a/app/composeables/api/models/playlistProgressAllOfStatus.ts b/app/composeables/api/models/playlistProgressAllOfStatus.ts new file mode 100644 index 0000000..5c4486e --- /dev/null +++ b/app/composeables/api/models/playlistProgressAllOfStatus.ts @@ -0,0 +1,15 @@ +/** + * Generated by orval v7.16.0 🍺 + * Do not edit manually. + * OpenAPI definition + * OpenAPI spec version: v0 + */ + +export type PlaylistProgressAllOfStatus = typeof PlaylistProgressAllOfStatus[keyof typeof PlaylistProgressAllOfStatus]; + + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const PlaylistProgressAllOfStatus = { + LOADING: 'LOADING', + FINISHED: 'FINISHED', +} as const; diff --git a/app/composeables/api/models/playlistReadDTO.ts b/app/composeables/api/models/playlistReadDTO.ts deleted file mode 100644 index bbec970..0000000 --- a/app/composeables/api/models/playlistReadDTO.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Generated by orval v7.16.0 🍺 - * Do not edit manually. - * OpenAPI definition - * OpenAPI spec version: v0 - */ - -export interface PlaylistReadDTO { - id?: number; - ownerId?: number; - title?: string; - createdAt?: string; - updatedAt?: string; -} diff --git a/app/composeables/api/models/singleTrackProgress.ts b/app/composeables/api/models/singleTrackProgress.ts new file mode 100644 index 0000000..86047a2 --- /dev/null +++ b/app/composeables/api/models/singleTrackProgress.ts @@ -0,0 +1,10 @@ +/** + * Generated by orval v7.16.0 🍺 + * Do not edit manually. + * OpenAPI definition + * OpenAPI spec version: v0 + */ +import type { BaseTrackProgress } from './baseTrackProgress'; +import type { SingleTrackProgressAllOf } from './singleTrackProgressAllOf'; + +export type SingleTrackProgress = BaseTrackProgress & SingleTrackProgressAllOf; diff --git a/app/composeables/api/models/playlistCreateDTO.ts b/app/composeables/api/models/singleTrackProgressAllOf.ts similarity index 68% rename from app/composeables/api/models/playlistCreateDTO.ts rename to app/composeables/api/models/singleTrackProgressAllOf.ts index 4a3ae60..df7b7e7 100644 --- a/app/composeables/api/models/playlistCreateDTO.ts +++ b/app/composeables/api/models/singleTrackProgressAllOf.ts @@ -5,6 +5,7 @@ * OpenAPI spec version: v0 */ -export interface PlaylistCreateDTO { +export type SingleTrackProgressAllOf = { title?: string; -} + format?: string; +}; diff --git a/app/composeables/api/models/streamProgress200Item.ts b/app/composeables/api/models/streamProgress200Item.ts new file mode 100644 index 0000000..6da7095 --- /dev/null +++ b/app/composeables/api/models/streamProgress200Item.ts @@ -0,0 +1,10 @@ +/** + * Generated by orval v7.16.0 🍺 + * Do not edit manually. + * OpenAPI definition + * OpenAPI spec version: v0 + */ +import type { PlaylistProgress } from './playlistProgress'; +import type { SingleTrackProgress } from './singleTrackProgress'; + +export type StreamProgress200Item = PlaylistProgress | SingleTrackProgress; diff --git a/app/composeables/api/models/trackResponse.ts b/app/composeables/api/models/trackResponse.ts index b338b0a..5ea6394 100644 --- a/app/composeables/api/models/trackResponse.ts +++ b/app/composeables/api/models/trackResponse.ts @@ -10,6 +10,7 @@ export interface TrackResponse { title?: string; artist?: string; audioPath?: string; + fileFormat?: string; durationSeconds?: number; fileName?: string; } diff --git a/app/composeables/api/models/trackReorderAfterRequest.ts b/app/composeables/api/models/youtubeTrackRequest.ts similarity index 56% rename from app/composeables/api/models/trackReorderAfterRequest.ts rename to app/composeables/api/models/youtubeTrackRequest.ts index 0594e41..3dd6a6c 100644 --- a/app/composeables/api/models/trackReorderAfterRequest.ts +++ b/app/composeables/api/models/youtubeTrackRequest.ts @@ -5,7 +5,6 @@ * OpenAPI spec version: v0 */ -export interface TrackReorderAfterRequest { - moveTrackId?: number; - targetTrackId?: number; +export interface YoutubeTrackRequest { + youtubeUrl?: string; } diff --git a/app/composeables/api/playlist-controller/playlist-controller.ts b/app/composeables/api/playlist-controller/playlist-controller.ts index e1fbea4..2f26746 100644 --- a/app/composeables/api/playlist-controller/playlist-controller.ts +++ b/app/composeables/api/playlist-controller/playlist-controller.ts @@ -32,7 +32,8 @@ import type { PlaylistReadResponse } from '.././models'; -import { axiosInstance } from '.././axios-instance'; +import createPlaylistMutator from '.././axios-instance'; +import playlistsMutator from '.././axios-instance'; type SecondParameter unknown> = Parameters[1]; @@ -41,11 +42,11 @@ type SecondParameter unknown> = Parameters[1]; export const createPlaylist = ( playlistCreateRequest: MaybeRef, - options?: SecondParameter,signal?: AbortSignal + options?: SecondParameter,signal?: AbortSignal ) => { playlistCreateRequest = unref(playlistCreateRequest); - return axiosInstance( + return createPlaylistMutator( {url: `/playlist`, method: 'POST', headers: {'Content-Type': 'application/json', }, data: playlistCreateRequest, signal @@ -56,7 +57,7 @@ export const createPlaylist = ( export const getCreatePlaylistMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{data: PlaylistCreateRequest}, TContext>, request?: SecondParameter} + TContext = unknown>(options?: { mutation?:UseMutationOptions>, TError,{data: PlaylistCreateRequest}, TContext>, request?: SecondParameter} ): UseMutationOptions>, TError,{data: PlaylistCreateRequest}, TContext> => { const mutationKey = ['createPlaylist']; @@ -85,7 +86,7 @@ const {mutation: mutationOptions, request: requestOptions} = options ? export type CreatePlaylistMutationError = unknown export const useCreatePlaylist = (options?: { mutation?:UseMutationOptions>, TError,{data: PlaylistCreateRequest}, TContext>, request?: SecondParameter} + TContext = unknown>(options?: { mutation?:UseMutationOptions>, TError,{data: PlaylistCreateRequest}, TContext>, request?: SecondParameter} , queryClient?: QueryClient): UseMutationReturnType< Awaited>, TError, @@ -99,11 +100,11 @@ const {mutation: mutationOptions, request: requestOptions} = options ? } export const playlists = ( - options?: SecondParameter,signal?: AbortSignal + options?: SecondParameter,signal?: AbortSignal ) => { - return axiosInstance( + return playlistsMutator( {url: `/playlists`, method: 'GET', signal }, options); @@ -119,7 +120,7 @@ export const getPlaylistsQueryKey = () => { } -export const getPlaylistsQueryOptions = >, TError = unknown>( options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +export const getPlaylistsQueryOptions = >, TError = unknown>( options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} ) => { const {query: queryOptions, request: requestOptions} = options ?? {}; @@ -143,7 +144,7 @@ export type PlaylistsQueryError = unknown export function usePlaylists>, TError = unknown>( - options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} + options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} , queryClient?: QueryClient ): UseQueryReturnType & { queryKey: DataTag } { diff --git a/app/composeables/api/progress-sse-controller/progress-sse-controller.ts b/app/composeables/api/progress-sse-controller/progress-sse-controller.ts new file mode 100644 index 0000000..719cd0d --- /dev/null +++ b/app/composeables/api/progress-sse-controller/progress-sse-controller.ts @@ -0,0 +1,99 @@ +/** + * Generated by orval v7.16.0 🍺 + * Do not edit manually. + * OpenAPI definition + * OpenAPI spec version: v0 + */ +import { + useQuery +} from '@tanstack/vue-query'; +import type { + DataTag, + QueryClient, + QueryFunction, + QueryKey, + UseQueryOptions, + UseQueryReturnType +} from '@tanstack/vue-query'; + +import { + computed, + unref +} from 'vue'; +import type { + MaybeRef +} from 'vue'; + +import type { + StreamProgress200Item +} from '.././models'; + +import streamProgressMutator from '.././event-source'; + + +type SecondParameter unknown> = Parameters[1]; + + + +export const streamProgress = ( + playlistId: MaybeRef, + options?: SecondParameter,signal?: AbortSignal +) => { + playlistId = unref(playlistId); + + return streamProgressMutator( + {url: `/importing/stream/${playlistId}`, method: 'GET', signal + }, + options); + } + + + + +export const getStreamProgressQueryKey = (playlistId?: MaybeRef,) => { + return [ + 'importing','stream',playlistId + ] as const; + } + + +export const getStreamProgressQueryOptions = >, TError = unknown>(playlistId: MaybeRef, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +) => { + +const {query: queryOptions, request: requestOptions} = options ?? {}; + + const queryKey = getStreamProgressQueryKey(playlistId); + + + + const queryFn: QueryFunction>> = ({ signal }) => streamProgress(playlistId, requestOptions, signal); + + + + + + return { queryKey, queryFn, enabled: computed(() => !!(unref(playlistId))), ...queryOptions} as UseQueryOptions>, TError, TData> +} + +export type StreamProgressQueryResult = NonNullable>> +export type StreamProgressQueryError = unknown + + + +export function useStreamProgress>, TError = unknown>( + playlistId: MaybeRef, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} + , queryClient?: QueryClient + ): UseQueryReturnType & { queryKey: DataTag } { + + const queryOptions = getStreamProgressQueryOptions(playlistId,options) + + const query = useQuery(queryOptions, queryClient) as UseQueryReturnType & { queryKey: DataTag }; + + query.queryKey = unref(queryOptions).queryKey as DataTag; + + return query; +} + + + + diff --git a/app/composeables/api/s-3-controller/s-3-controller.ts b/app/composeables/api/s-3-controller/s-3-controller.ts index f5b0255..b43f8fb 100644 --- a/app/composeables/api/s-3-controller/s-3-controller.ts +++ b/app/composeables/api/s-3-controller/s-3-controller.ts @@ -32,7 +32,8 @@ import type { UploadBody } from '.././models'; -import { axiosInstance } from '.././axios-instance'; +import uploadMutator from '.././axios-instance'; +import readMutator from '.././axios-instance'; type SecondParameter unknown> = Parameters[1]; @@ -41,13 +42,13 @@ type SecondParameter unknown> = Parameters[1]; export const upload = ( uploadBody: MaybeRef, - options?: SecondParameter,signal?: AbortSignal + options?: SecondParameter,signal?: AbortSignal ) => { uploadBody = unref(uploadBody); const formData = new FormData(); formData.append(`document`, uploadBody.document) - return axiosInstance( + return uploadMutator( {url: `/upload`, method: 'POST', headers: {'Content-Type': 'multipart/form-data', }, data: formData, signal @@ -58,7 +59,7 @@ formData.append(`document`, uploadBody.document) export const getUploadMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{data: UploadBody}, TContext>, request?: SecondParameter} + TContext = unknown>(options?: { mutation?:UseMutationOptions>, TError,{data: UploadBody}, TContext>, request?: SecondParameter} ): UseMutationOptions>, TError,{data: UploadBody}, TContext> => { const mutationKey = ['upload']; @@ -87,7 +88,7 @@ const {mutation: mutationOptions, request: requestOptions} = options ? export type UploadMutationError = unknown export const useUpload = (options?: { mutation?:UseMutationOptions>, TError,{data: UploadBody}, TContext>, request?: SecondParameter} + TContext = unknown>(options?: { mutation?:UseMutationOptions>, TError,{data: UploadBody}, TContext>, request?: SecondParameter} , queryClient?: QueryClient): UseMutationReturnType< Awaited>, TError, @@ -101,11 +102,11 @@ const {mutation: mutationOptions, request: requestOptions} = options ? } export const read = ( params: MaybeRef, - options?: SecondParameter,signal?: AbortSignal + options?: SecondParameter,signal?: AbortSignal ) => { params = unref(params); - return axiosInstance( + return readMutator( {url: `/read`, method: 'GET', params: unref(params), signal }, @@ -122,7 +123,7 @@ export const getReadQueryKey = (params?: MaybeRef,) => { } -export const getReadQueryOptions = >, TError = unknown>(params: MaybeRef, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +export const getReadQueryOptions = >, TError = unknown>(params: MaybeRef, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} ) => { const {query: queryOptions, request: requestOptions} = options ?? {}; @@ -146,7 +147,7 @@ export type ReadQueryError = unknown export function useRead>, TError = unknown>( - params: MaybeRef, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} + params: MaybeRef, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} , queryClient?: QueryClient ): UseQueryReturnType & { queryKey: DataTag } { diff --git a/app/composeables/api/track-controller/track-controller.ts b/app/composeables/api/track-controller/track-controller.ts index cad82d6..c2808b1 100644 --- a/app/composeables/api/track-controller/track-controller.ts +++ b/app/composeables/api/track-controller/track-controller.ts @@ -32,27 +32,150 @@ import type { AddLocalTrackRequest, PlaylistTrackResponse, TrackBulkReorderRequest, - TrackResponse + TrackResponse, + YoutubeTrackRequest } from '.././models'; -import { axiosInstance } from '.././axios-instance'; +import addYoutubeTrackMutator from '.././axios-instance'; +import addYoutubeTrack1Mutator from '.././axios-instance'; +import addLocalTrackMutator from '.././axios-instance'; +import bulkReorderMutator from '.././axios-instance'; +import getPlaylistTracksMutator from '.././axios-instance'; type SecondParameter unknown> = Parameters[1]; -export const addLocalTrack = ( +export const addYoutubeTrack = ( + playlistId: MaybeRef, + youtubeTrackRequest: MaybeRef, + options?: SecondParameter,signal?: AbortSignal +) => { + playlistId = unref(playlistId); +youtubeTrackRequest = unref(youtubeTrackRequest); + + return addYoutubeTrackMutator( + {url: `/playlist/${playlistId}/track/youtube`, method: 'POST', + headers: {'Content-Type': 'application/json', }, + data: youtubeTrackRequest, signal + }, + options); + } + + + +export const getAddYoutubeTrackMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{playlistId: number;data: YoutubeTrackRequest}, TContext>, request?: SecondParameter} +): UseMutationOptions>, TError,{playlistId: number;data: YoutubeTrackRequest}, TContext> => { + +const mutationKey = ['addYoutubeTrack']; +const {mutation: mutationOptions, request: requestOptions} = options ? + options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? + options + : {...options, mutation: {...options.mutation, mutationKey}} + : {mutation: { mutationKey, }, request: undefined}; + + + + + const mutationFn: MutationFunction>, {playlistId: number;data: YoutubeTrackRequest}> = (props) => { + const {playlistId,data} = props ?? {}; + + return addYoutubeTrack(playlistId,data,requestOptions) + } + + + + + return { mutationFn, ...mutationOptions }} + + export type AddYoutubeTrackMutationResult = NonNullable>> + export type AddYoutubeTrackMutationBody = YoutubeTrackRequest + export type AddYoutubeTrackMutationError = unknown + + export const useAddYoutubeTrack = (options?: { mutation?:UseMutationOptions>, TError,{playlistId: number;data: YoutubeTrackRequest}, TContext>, request?: SecondParameter} + , queryClient?: QueryClient): UseMutationReturnType< + Awaited>, + TError, + {playlistId: number;data: YoutubeTrackRequest}, + TContext + > => { + + const mutationOptions = getAddYoutubeTrackMutationOptions(options); + + return useMutation(mutationOptions, queryClient); + } + export const addYoutubeTrack1 = ( + playlistId: MaybeRef, + sourceId: MaybeRef, + options?: SecondParameter,signal?: AbortSignal +) => { + playlistId = unref(playlistId); +sourceId = unref(sourceId); + + return addYoutubeTrack1Mutator( + {url: `/playlist/${playlistId}/track/youtube/refresh/${sourceId}`, method: 'POST', signal + }, + options); + } + + + +export const getAddYoutubeTrack1MutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{playlistId: number;sourceId: number}, TContext>, request?: SecondParameter} +): UseMutationOptions>, TError,{playlistId: number;sourceId: number}, TContext> => { + +const mutationKey = ['addYoutubeTrack1']; +const {mutation: mutationOptions, request: requestOptions} = options ? + options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey ? + options + : {...options, mutation: {...options.mutation, mutationKey}} + : {mutation: { mutationKey, }, request: undefined}; + + + + + const mutationFn: MutationFunction>, {playlistId: number;sourceId: number}> = (props) => { + const {playlistId,sourceId} = props ?? {}; + + return addYoutubeTrack1(playlistId,sourceId,requestOptions) + } + + + + + return { mutationFn, ...mutationOptions }} + + export type AddYoutubeTrack1MutationResult = NonNullable>> + + export type AddYoutubeTrack1MutationError = unknown + + export const useAddYoutubeTrack1 = (options?: { mutation?:UseMutationOptions>, TError,{playlistId: number;sourceId: number}, TContext>, request?: SecondParameter} + , queryClient?: QueryClient): UseMutationReturnType< + Awaited>, + TError, + {playlistId: number;sourceId: number}, + TContext + > => { + + const mutationOptions = getAddYoutubeTrack1MutationOptions(options); + + return useMutation(mutationOptions, queryClient); + } + export const addLocalTrack = ( playlistId: MaybeRef, addLocalTrackRequest: MaybeRef, - options?: SecondParameter,signal?: AbortSignal + options?: SecondParameter,signal?: AbortSignal ) => { playlistId = unref(playlistId); addLocalTrackRequest = unref(addLocalTrackRequest); const formData = new FormData(); formData.append(`source`, addLocalTrackRequest.source) - return axiosInstance( + return addLocalTrackMutator( {url: `/playlist/${playlistId}/track/local`, method: 'POST', headers: {'Content-Type': 'multipart/form-data', }, data: formData, signal @@ -63,7 +186,7 @@ formData.append(`source`, addLocalTrackRequest.source) export const getAddLocalTrackMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{playlistId: number;data: AddLocalTrackRequest}, TContext>, request?: SecondParameter} + TContext = unknown>(options?: { mutation?:UseMutationOptions>, TError,{playlistId: number;data: AddLocalTrackRequest}, TContext>, request?: SecondParameter} ): UseMutationOptions>, TError,{playlistId: number;data: AddLocalTrackRequest}, TContext> => { const mutationKey = ['addLocalTrack']; @@ -92,7 +215,7 @@ const {mutation: mutationOptions, request: requestOptions} = options ? export type AddLocalTrackMutationError = unknown export const useAddLocalTrack = (options?: { mutation?:UseMutationOptions>, TError,{playlistId: number;data: AddLocalTrackRequest}, TContext>, request?: SecondParameter} + TContext = unknown>(options?: { mutation?:UseMutationOptions>, TError,{playlistId: number;data: AddLocalTrackRequest}, TContext>, request?: SecondParameter} , queryClient?: QueryClient): UseMutationReturnType< Awaited>, TError, @@ -107,12 +230,12 @@ const {mutation: mutationOptions, request: requestOptions} = options ? export const bulkReorder = ( playlistId: MaybeRef, trackBulkReorderRequest: MaybeRef, - options?: SecondParameter,signal?: AbortSignal + options?: SecondParameter,signal?: AbortSignal ) => { playlistId = unref(playlistId); trackBulkReorderRequest = unref(trackBulkReorderRequest); - return axiosInstance( + return bulkReorderMutator( {url: `/playlist/${playlistId}/bulk-reorder`, method: 'POST', headers: {'Content-Type': 'application/json', }, data: trackBulkReorderRequest, signal @@ -123,7 +246,7 @@ trackBulkReorderRequest = unref(trackBulkReorderRequest); export const getBulkReorderMutationOptions = (options?: { mutation?:UseMutationOptions>, TError,{playlistId: number;data: TrackBulkReorderRequest}, TContext>, request?: SecondParameter} + TContext = unknown>(options?: { mutation?:UseMutationOptions>, TError,{playlistId: number;data: TrackBulkReorderRequest}, TContext>, request?: SecondParameter} ): UseMutationOptions>, TError,{playlistId: number;data: TrackBulkReorderRequest}, TContext> => { const mutationKey = ['bulkReorder']; @@ -152,7 +275,7 @@ const {mutation: mutationOptions, request: requestOptions} = options ? export type BulkReorderMutationError = unknown export const useBulkReorder = (options?: { mutation?:UseMutationOptions>, TError,{playlistId: number;data: TrackBulkReorderRequest}, TContext>, request?: SecondParameter} + TContext = unknown>(options?: { mutation?:UseMutationOptions>, TError,{playlistId: number;data: TrackBulkReorderRequest}, TContext>, request?: SecondParameter} , queryClient?: QueryClient): UseMutationReturnType< Awaited>, TError, @@ -166,11 +289,11 @@ const {mutation: mutationOptions, request: requestOptions} = options ? } export const getPlaylistTracks = ( playlistId: MaybeRef, - options?: SecondParameter,signal?: AbortSignal + options?: SecondParameter,signal?: AbortSignal ) => { playlistId = unref(playlistId); - return axiosInstance( + return getPlaylistTracksMutator( {url: `/playlist/${playlistId}/tracks`, method: 'GET', signal }, options); @@ -186,7 +309,7 @@ export const getGetPlaylistTracksQueryKey = (playlistId?: MaybeRef,) => } -export const getGetPlaylistTracksQueryOptions = >, TError = unknown>(playlistId: MaybeRef, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} +export const getGetPlaylistTracksQueryOptions = >, TError = unknown>(playlistId: MaybeRef, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} ) => { const {query: queryOptions, request: requestOptions} = options ?? {}; @@ -210,7 +333,7 @@ export type GetPlaylistTracksQueryError = unknown export function useGetPlaylistTracks>, TError = unknown>( - playlistId: MaybeRef, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} + playlistId: MaybeRef, options?: { query?:Partial>, TError, TData>>, request?: SecondParameter} , queryClient?: QueryClient ): UseQueryReturnType & { queryKey: DataTag } { diff --git a/app/pages/import.vue b/app/pages/import.vue index 259ee0d..78a1dbc 100644 --- a/app/pages/import.vue +++ b/app/pages/import.vue @@ -1,9 +1,62 @@