Convert socketStore and stateStore to ts
This commit is contained in:
parent
1580388b96
commit
84f623f2e4
6 changed files with 305 additions and 206 deletions
|
|
@ -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
162
ui/stores/SocketStore.ts
Normal 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 };
|
||||||
|
});
|
||||||
|
|
@ -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
59
ui/stores/StateStore.ts
Normal 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
8
ui/types/sockets.d.ts
vendored
Normal 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
76
ui/types/store.d.ts
vendored
Normal 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
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue