Convert socketStore and stateStore to ts

This commit is contained in:
arabcoders 2025-07-18 19:49:36 +03:00
parent 1580388b96
commit 84f623f2e4
6 changed files with 305 additions and 206 deletions

View file

@ -1,163 +0,0 @@
import { io } from "socket.io-client";
import { ag } from "~/utils/index"
export const useSocketStore = defineStore('socket', () => {
const runtimeConfig = useRuntimeConfig()
const config = useConfigStore()
const stateStore = useStateStore()
const toast = useNotification()
const socket = ref(null)
const isConnected = ref(false)
const connect = () => {
let opts = {
transports: ['websocket', 'polling'],
withCredentials: true,
}
let url = runtimeConfig.public.wss
if ('development' !== runtimeConfig.public?.APP_ENV) {
url = window.origin;
opts.path = `${runtimeConfig.app.baseURL.replace(/\/$/, '')}/socket.io`;
}
socket.value = io(url, opts)
socket.value.on('connect', () => isConnected.value = true);
socket.value.on('disconnect', () => isConnected.value = false);
socket.value.on('initial_data', stream => {
const initialData = JSON.parse(stream)
config.setAll({
app: initialData['config'],
tasks: initialData['tasks'],
folders: initialData['folders'],
presets: initialData['presets'],
paused: Boolean(initialData['paused'])
})
stateStore.addAll('queue', initialData['queue'] ?? {})
stateStore.addAll('history', initialData['done'] ?? {})
})
socket.value.on('added', stream => {
const item = JSON.parse(stream);
stateStore.add('queue', item._id, item);
toast.success(`Item queued: ${ag(stateStore.get('queue', item._id, {}), 'title')}`);
});
socket.value.on('error', stream => {
const json = JSON.parse(stream);
toast.error(`${json.data?.id ?? json?.type}: ${json?.message}`, json.data || {});
});
socket.value.on('log_info', stream => {
const json = JSON.parse(stream);
toast.info(json?.message, json.data || {});
});
socket.value.on('log_success', stream => {
const json = JSON.parse(stream);
toast.success(json?.message, json.data || {});
});
socket.value.on('log_warning', stream => {
const json = JSON.parse(stream);
toast.warning(json?.message, json.data || {});
});
socket.value.on('log_error', stream => {
const json = JSON.parse(stream);
toast.error(json?.message, json.data || {});
});
socket.value.on('completed', stream => {
const item = JSON.parse(stream);
if (true === stateStore.has('queue', item._id)) {
stateStore.remove('queue', item._id);
}
stateStore.add('history', item._id, item);
});
socket.value.on('cancelled', stream => {
const item = JSON.parse(stream);
const id = item._id
if (true !== stateStore.has('queue', id)) {
return
}
toast.warning(`Download cancelled: ${ag(stateStore.get('queue', id, {}), 'title')}`);
if (true === stateStore.has('queue', id)) {
stateStore.remove('queue', id);
}
});
socket.value.on('cleared', stream => {
const item = JSON.parse(stream);
const id = item._id
if (true !== stateStore.has('history', id)) {
return
}
stateStore.remove('history', id);
});
socket.value.on("updated", stream => {
const data = JSON.parse(stream);
if (true === stateStore.has('history', data._id)) {
stateStore.update('history', data._id, data);
return;
}
let dl = stateStore.get('queue', data._id, {});
data.deleting = dl?.deleting;
stateStore.update('queue', data._id, data);
});
socket.value.on("update", stream => {
const data = JSON.parse(stream);
if (true === stateStore.has('history', data._id)) {
stateStore.update('history', data._id, data);
return;
}
});
socket.value.on('paused', data => {
const json = JSON.parse(data);
const pausedState = Boolean(json.paused);
config.update('paused', pausedState);
if (false === pausedState) {
toast.success('Download queue resumed.');
return;
}
toast.warning('Download queue paused.', {
timeout: 10000,
});
});
socket.value.on('presets_update', data => config.update('presets', JSON.parse(data)));
}
const on = (event, callback) => socket.value.on(event, callback);
const off = (event, callback) => socket.value.off(event, callback);
const emit = (event, data) => socket.value.emit(event, data);
if (false === isConnected.value) {
connect();
}
window.ws = socket.value;
return { connect, on, off, emit, socket, isConnected };
});

162
ui/stores/SocketStore.ts Normal file
View file

@ -0,0 +1,162 @@
import { io } from "socket.io-client";
import { ag } from "~/utils/index"
import type { Socket as IOSocket, SocketOptions } from "socket.io-client"
import type { ManagerOptions } from "socket.io-client";
export const useSocketStore = defineStore('socket', () => {
const runtimeConfig = useRuntimeConfig()
const config = useConfigStore()
const stateStore = useStateStore()
const toast = useNotification()
const socket = ref<IOSocket | null>(null)
const isConnected = ref<boolean>(false)
const connect = () => {
let opts = {
transports: ['websocket', 'polling'],
withCredentials: true,
} as Partial<ManagerOptions & SocketOptions>
let url = runtimeConfig.public.wss
if ('development' !== runtimeConfig.public?.APP_ENV) {
url = window.origin;
opts.path = `${runtimeConfig.app.baseURL.replace(/\/$/, '')}/socket.io`;
}
socket.value = io(url, opts)
socket.value.on('connect', () => isConnected.value = true);
socket.value.on('disconnect', () => isConnected.value = false);
socket.value.on('initial_data', stream => {
const json = JSON.parse(stream)
config.setAll({
app: json.data.config,
tasks: json.data.tasks,
folders: json.data.folders,
presets: json.data.presets,
paused: Boolean(json.data.paused)
})
stateStore.addAll('queue', json.data.queue || {})
stateStore.addAll('history', json.data.done || {})
})
socket.value.on('added', stream => {
const json = JSON.parse(stream);
stateStore.add('queue', json.data._id, json.data);
toast.success(`Item queued: ${ag(stateStore.get('queue', json.data._id, {}), 'title')}`);
});
socket.value.on('error', stream => {
const json = JSON.parse(stream);
toast.error(`${json.data?.id}: ${json?.message || json?.data?.message}`, json.data || {});
});
['log_info', 'log_success', 'log_warning', 'log_error'].forEach(event => socket.value?.on(event, stream => {
const json = JSON.parse(stream);
const message = json?.message || json?.data?.message;
const data = json.data?.data || json.data || {};
switch (event) {
case 'log_info':
toast.info(message, data);
break;
case 'log_success':
toast.success(message, data);
break;
case 'log_warning':
toast.warning(message, data);
break;
case 'log_error':
toast.error(message, data);
break;
}
}));
socket.value.on('completed', stream => {
const json = JSON.parse(stream);
if (true === stateStore.has('queue', json.data._id)) {
stateStore.remove('queue', json.data._id);
}
stateStore.add('history', json.data._id, json.data);
});
socket.value.on('cancelled', stream => {
const item = JSON.parse(stream);
const id = item.data._id
if (true !== stateStore.has('queue', id)) {
return
}
toast.warning(`Download cancelled: ${ag(stateStore.get('queue', id, {}), 'title')}`);
if (true === stateStore.has('queue', id)) {
stateStore.remove('queue', id);
}
});
socket.value.on('cleared', stream => {
const item = JSON.parse(stream);
const id = item.data._id
if (true !== stateStore.has('history', id)) {
return
}
stateStore.remove('history', id);
});
socket.value.on("updated", stream => {
const json = JSON.parse(stream);
const id = json.data._id;
if (true === stateStore.has('history', id)) {
stateStore.update('history', id, json.data);
return;
}
stateStore.update('queue', id, json.data);
});
socket.value.on("update", stream => {
const json = JSON.parse(stream);
if (true === stateStore.has('history', json.data._id)) {
stateStore.update('history', json.data._id, json.data);
return;
}
});
socket.value.on('paused', data => {
const json = JSON.parse(data);
const pausedState = Boolean(json.data.paused);
config.update('paused', pausedState);
if (false === pausedState) {
toast.success('Download queue resumed.');
return;
}
toast.warning('Download queue paused.', { timeout: 10000 });
});
socket.value.on('presets_update', (data: string) => config.update('presets', JSON.parse(data).data || []));
}
const emit = (event: string, data?: any): any => socket.value?.emit(event, data)
const on = (event: string, callback: (...args: any[]) => void): any => socket.value?.on(event, callback)
const off = (event: string, callback: (...args: any[]) => void): any => socket.value?.off(event, callback)
if (false === isConnected.value) {
connect();
}
window.ws = socket.value;
return { connect, on, off, emit, socket, isConnected };
});

View file

@ -1,43 +0,0 @@
export const useStateStore = defineStore('state', () => {
const state = reactive({
queue: {},
history: {}
});
const actions = {
add(type, key, value) {
state[type][key] = value;
},
update(type, key, value) {
state[type][key] = value;
},
remove(type, key) {
if (state[type][key]) {
delete state[type][key];
}
},
get(type, key, defaultValue = null) {
return state[type][key] || defaultValue;
},
has(type, key) {
return !!state[type][key];
},
clearAll(type) {
state[type] = {};
},
addAll(type, data) {
state[type] = data;
},
move(fromType, toType, key) {
if (state[fromType][key]) {
state[toType][key] = state[fromType][key];
delete state[fromType][key];
}
},
count(type) {
return Object.keys(state[type]).length;
}
}
return { ...toRefs(state), ...actions };
});

59
ui/stores/StateStore.ts Normal file
View file

@ -0,0 +1,59 @@
import { defineStore } from 'pinia'
import { reactive, toRefs } from 'vue'
import type { StoreItem } from '~/types/store'
type StateType = 'queue' | 'history'
type KeyType = string
type ValueType = StoreItem
interface State {
queue: Record<KeyType, ValueType>
history: Record<KeyType, ValueType>
}
export const useStateStore = defineStore('state', () => {
const state = reactive<State>({ queue: {}, history: {} })
const add = (type: StateType, key: KeyType, value: ValueType): void => {
state[type][key] = value
}
const update = (type: StateType, key: KeyType, value: ValueType): void => {
state[type][key] = value
}
const remove = (type: StateType, key: KeyType): void => {
if (state[type][key]) {
delete state[type][key]
}
}
const get = (type: StateType, key: KeyType, defaultValue: ValueType | null | object = null): ValueType | null => {
return state[type][key] || defaultValue
}
const has = (type: StateType, key: KeyType): boolean => {
return !!state[type][key]
}
const clearAll = (type: StateType): void => {
state[type] = {}
}
const addAll = (type: StateType, data: Record<KeyType, ValueType>): void => {
state[type] = data
}
const move = (fromType: StateType, toType: StateType, key: KeyType): void => {
if (state[fromType][key]) {
state[toType][key] = state[fromType][key]
delete state[fromType][key]
}
}
const count = (type: StateType): number => {
return Object.keys(state[type]).length
}
return { ...toRefs(state), add, update, remove, get, has, clearAll, addAll, move, count }
})

8
ui/types/sockets.d.ts vendored Normal file
View file

@ -0,0 +1,8 @@
export type Event = {
id: str
created_at: str
event: str
title: str | null = null
message: str | null = null
data: Any = { }
}

76
ui/types/store.d.ts vendored Normal file
View file

@ -0,0 +1,76 @@
export type ItemStatus = 'finished' | 'preparing' | 'error' | 'cancelled' | 'downloading' | 'postprocessing' | 'not_live' | null;
export type StoreItem = {
/** Unique identifier for the item */
_id: string
/** Error message if available */
error: string | null
/** Item source id */
id: string
/** Title of the item */
title: string
/** URL of the item */
url: string
/** Preset used for the item */
preset: string
/** Folder where the item is saved */
folder: string
/** Download directory */
download_dir: string
/** Temporary directory for the item */
temp_dir: string
/** Status of the item */
status: ItemStatus
/** If the item has cookies */
cookies: string
/** If the item has custom output_template */
template: string
/** If the item has custom output_template for chapters */
template_chapter: string
/** When the item was created */
timestamp: number
/** If the item is a live stream */
is_live: boolean
/** ISO 8601 formatted start time of the item */
datetime: string
/** Live stream start time if available */
live_in: string | null
/** File size of the item if available */
file_size: number | null
/** Custom yt-dlp command line arguments */
cli: string
/** If the item is auto-started */
auto_start: boolean
/** Options for the item */
options: Record<string, unknown>
/** Extras for the item */
extras: {
/** Which channel the item belongs to */
channel?: string
/** The video duration if available */
duration?: number | null
/** The video release date if available */
release_in?: string
/** The video thumbnail URL if available */
thumbnail?: string
/** The uploader of the item if available */
uploader?: string
}
/** The item temporary filename */
tmpfilename?: string | null
/** The item filename */
filename?: string | null
/** Actual file size of the item if available */
total_bytes?: number | null
/** Estimated total bytes for the item if available */
total_bytes_estimate?: number | null
/** Downloaded bytes of the item if available */
downloaded_bytes?: number | null
/** Message attached with the item if available */
msg?: string | null
/** Progress percentage of the item if available */
percent?: number | null
/** Speed of the item download if available */
speed?: number | null
/** Time remaining for the item download if available */
eta?: number | null
}