Merge pull request #350 from arabcoders/dev
Fixed in browser match_str to closely match yt-dlp match_str
This commit is contained in:
commit
fd75d3cb8f
74 changed files with 235 additions and 231 deletions
|
|
@ -207,8 +207,6 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
import { decode } from '~/utils/importer'
|
|
||||||
import { match_str } from '~/utils/ytdlp'
|
|
||||||
import type { ConditionItem, ImportedConditionItem } from '~/types/conditions'
|
import type { ConditionItem, ImportedConditionItem } from '~/types/conditions'
|
||||||
|
|
||||||
const emitter = defineEmits<{
|
const emitter = defineEmits<{
|
||||||
|
|
@ -292,7 +290,7 @@ const run_test = async (): Promise<void> => {
|
||||||
}
|
}
|
||||||
|
|
||||||
test_data.value.in_progress = true
|
test_data.value.in_progress = true
|
||||||
test_data.value.data.status = null
|
test_data.value.data.status = false
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await request('/api/conditions/test', {
|
const response = await request('/api/conditions/test', {
|
||||||
|
|
@ -363,7 +361,7 @@ const show_data = (): string => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const logic_test = computed(() => {
|
const logic_test = computed(() => {
|
||||||
if (!test_data.value.data || !test_data.value.data.data) {
|
if (Object.keys(test_data.value.data?.data ?? {}).length < 1) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -20,8 +20,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
|
||||||
|
|
||||||
const emitter = defineEmits(['open_state'])
|
const emitter = defineEmits(['open_state'])
|
||||||
defineProps({
|
defineProps({
|
||||||
label: {
|
label: {
|
||||||
|
|
@ -20,19 +20,16 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
const props = defineProps<{
|
||||||
import { request } from '~/utils/index'
|
|
||||||
|
|
||||||
const props = defineProps < {
|
|
||||||
image?: string
|
image?: string
|
||||||
title?: string
|
title?: string
|
||||||
loader?: () => Promise < void>
|
loader?: () => Promise<void>
|
||||||
privacy ?: boolean
|
privacy?: boolean
|
||||||
} > ()
|
}>()
|
||||||
|
|
||||||
const cache = useSessionCache()
|
const cache = useSessionCache()
|
||||||
const toast = useNotification()
|
const toast = useNotification()
|
||||||
const url = ref < string | null > (null)
|
const url = ref<string | null>(null)
|
||||||
const error = ref(false)
|
const error = ref(false)
|
||||||
const isPreloading = ref(false)
|
const isPreloading = ref(false)
|
||||||
|
|
||||||
|
|
@ -38,9 +38,6 @@ code {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
|
||||||
import { request } from '~/utils/index'
|
|
||||||
|
|
||||||
const toast = useNotification()
|
const toast = useNotification()
|
||||||
const emitter = defineEmits<{ (e: 'closeModel'): void }>()
|
const emitter = defineEmits<{ (e: 'closeModel'): void }>()
|
||||||
|
|
||||||
|
|
@ -461,12 +461,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { NuxtLink } from '#components'
|
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import { getEmbedable, isEmbedable } from '~/utils/embedable'
|
|
||||||
import { formatBytes, makeDownload, uri } from '~/utils/index'
|
|
||||||
import Dropdown from './Dropdown.vue'
|
|
||||||
|
|
||||||
const emitter = defineEmits(['getInfo', 'add_new', 'getItemInfo', 'clear_search'])
|
const emitter = defineEmits(['getInfo', 'add_new', 'getItemInfo', 'clear_search'])
|
||||||
|
|
||||||
|
|
@ -19,8 +19,6 @@ img {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { request } from '~/utils/index'
|
|
||||||
|
|
||||||
const toast = useNotification()
|
const toast = useNotification()
|
||||||
const emitter = defineEmits(['closeModel'])
|
const emitter = defineEmits(['closeModel'])
|
||||||
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, nextTick, onBeforeUnmount } from 'vue'
|
|
||||||
import { useIntersectionObserver } from '@vueuse/core'
|
import { useIntersectionObserver } from '@vueuse/core'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|
@ -232,7 +232,6 @@
|
||||||
import 'assets/css/bulma-switch.css'
|
import 'assets/css/bulma-switch.css'
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
import type { item_request } from '~/types/item'
|
import type { item_request } from '~/types/item'
|
||||||
import { getSeparatorsName, separators } from '~/utils/index'
|
|
||||||
|
|
||||||
const props = defineProps<{ item?: Partial<item_request> }>()
|
const props = defineProps<{ item?: Partial<item_request> }>()
|
||||||
const emitter = defineEmits<{
|
const emitter = defineEmits<{
|
||||||
|
|
@ -269,7 +269,6 @@
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
import { decode } from '~/utils/importer'
|
|
||||||
|
|
||||||
const emitter = defineEmits(['cancel', 'submit']);
|
const emitter = defineEmits(['cancel', 'submit']);
|
||||||
const toast = useNotification();
|
const toast = useNotification();
|
||||||
|
|
@ -232,7 +232,6 @@
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
import { decode } from '~/utils/importer'
|
|
||||||
|
|
||||||
const emitter = defineEmits(['cancel', 'submit']);
|
const emitter = defineEmits(['cancel', 'submit']);
|
||||||
|
|
||||||
|
|
@ -315,8 +315,6 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
import { ucFirst } from '~/utils/index'
|
|
||||||
import { isEmbedable, getEmbedable } from '~/utils/embedable'
|
|
||||||
|
|
||||||
const emitter = defineEmits(['getInfo', 'clear_search', 'getItemInfo'])
|
const emitter = defineEmits(['getInfo', 'clear_search', 'getItemInfo'])
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|
@ -142,7 +142,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
import { POSITION } from 'vue-toastification'
|
import { POSITION } from 'vue-toastification'
|
||||||
import { separators } from '~/utils/index'
|
|
||||||
|
|
||||||
defineProps<{ isLoading: boolean }>()
|
defineProps<{ isLoading: boolean }>()
|
||||||
|
|
||||||
|
|
@ -292,7 +292,6 @@
|
||||||
import 'assets/css/bulma-switch.css'
|
import 'assets/css/bulma-switch.css'
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
import { CronExpressionParser } from 'cron-parser'
|
import { CronExpressionParser } from 'cron-parser'
|
||||||
import { decode } from '~/utils/importer'
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
reference: {
|
reference: {
|
||||||
|
|
@ -28,7 +28,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
import Hls from 'hls.js'
|
import Hls from 'hls.js'
|
||||||
import { makeDownload } from '~/utils/index'
|
|
||||||
|
|
||||||
type video_track_element = {
|
type video_track_element = {
|
||||||
file: string,
|
file: string,
|
||||||
|
|
@ -161,6 +161,9 @@ const applyPreferredColorScheme = (scheme: string) => {
|
||||||
|
|
||||||
for (let s = 0; s < document.styleSheets.length; s++) {
|
for (let s = 0; s < document.styleSheets.length; s++) {
|
||||||
const styleSheet = document.styleSheets[s]
|
const styleSheet = document.styleSheets[s]
|
||||||
|
if (!styleSheet?.cssRules) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
let rules: CSSRuleList
|
let rules: CSSRuleList
|
||||||
try {
|
try {
|
||||||
|
|
@ -193,7 +193,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { request } from '~/utils/index'
|
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
|
|
||||||
|
|
@ -115,8 +115,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { request } from '~/utils/index'
|
|
||||||
import { encode } from '~/utils/importer'
|
|
||||||
import type { ConditionItem, ImportedConditionItem } from '~/types/conditions'
|
import type { ConditionItem, ImportedConditionItem } from '~/types/conditions'
|
||||||
|
|
||||||
const toast = useNotification()
|
const toast = useNotification()
|
||||||
|
|
@ -85,7 +85,6 @@ import type { item_request } from '~/types/item'
|
||||||
const config = useConfigStore()
|
const config = useConfigStore()
|
||||||
const stateStore = useStateStore()
|
const stateStore = useStateStore()
|
||||||
const socket = useSocketStore()
|
const socket = useSocketStore()
|
||||||
const box = useConfirm()
|
|
||||||
const bg_enable = useStorage<boolean>('random_bg', true)
|
const bg_enable = useStorage<boolean>('random_bg', true)
|
||||||
const bg_opacity = useStorage<number>('random_bg_opacity', 0.95)
|
const bg_opacity = useStorage<number>('random_bg_opacity', 0.95)
|
||||||
const display_style = useStorage<string>('display_style', 'cards')
|
const display_style = useStorage<string>('display_style', 'cards')
|
||||||
|
|
@ -111,8 +111,6 @@ code {
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import { request } from '~/utils/index'
|
|
||||||
import { ref, onMounted, nextTick } from 'vue'
|
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
import type { log_line } from '~/types/logs'
|
import type { log_line } from '~/types/logs'
|
||||||
|
|
||||||
|
|
@ -140,9 +140,6 @@ div.is-centered {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { request } from '~/utils/index'
|
|
||||||
import { encode } from '~/utils/importer'
|
|
||||||
|
|
||||||
const toast = useNotification()
|
const toast = useNotification()
|
||||||
const config = useConfigStore()
|
const config = useConfigStore()
|
||||||
const socket = useSocketStore()
|
const socket = useSocketStore()
|
||||||
|
|
@ -212,8 +212,6 @@ div.is-centered {
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
import { request } from '~/utils/index'
|
|
||||||
import { encode } from '~/utils/importer'
|
|
||||||
|
|
||||||
const toast = useNotification()
|
const toast = useNotification()
|
||||||
const config = useConfigStore()
|
const config = useConfigStore()
|
||||||
|
|
@ -328,8 +328,6 @@ div.is-centered {
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
import { CronExpressionParser } from 'cron-parser'
|
import { CronExpressionParser } from 'cron-parser'
|
||||||
import { request, sleep } from '~/utils/index'
|
|
||||||
import { encode } from '~/utils/importer'
|
|
||||||
import type { task_item, exported_task, error_response } from '~/types/tasks'
|
import type { task_item, exported_task, error_response } from '~/types/tasks'
|
||||||
|
|
||||||
const box = useConfirm()
|
const box = useConfirm()
|
||||||
|
|
@ -377,14 +375,14 @@ watch(query, () => {
|
||||||
selectedElms.value = []
|
selectedElms.value = []
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(masterSelectAll, (value) => {
|
watch(masterSelectAll, value => {
|
||||||
|
if (!value) {
|
||||||
|
selectedElms.value = []
|
||||||
|
return
|
||||||
|
}
|
||||||
for (const key in filteredTasks.value) {
|
for (const key in filteredTasks.value) {
|
||||||
const element = filteredTasks.value[key]
|
const element = filteredTasks.value[key] as task_item
|
||||||
if (value) {
|
selectedElms.value.push(element.id)
|
||||||
selectedElms.value.push(element.id)
|
|
||||||
} else {
|
|
||||||
selectedElms.value = []
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
38
ui/app/plugins/autoscroll.ts
Normal file
38
ui/app/plugins/autoscroll.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
let observer: MutationObserver;
|
||||||
|
const AUTO_SCROLL_EVENTS = ['scroll', 'mousewheel', 'touchmove'] as const;
|
||||||
|
const events_handlers: Array<(ev: Event) => void> = [];
|
||||||
|
|
||||||
|
const on_mounted = (el: HTMLElement) => {
|
||||||
|
let scrolledToBottom = true;
|
||||||
|
|
||||||
|
AUTO_SCROLL_EVENTS.forEach((ev, index) => {
|
||||||
|
const handler = () => {
|
||||||
|
scrolledToBottom = (el.scrollHeight - el.scrollTop) - el.clientHeight <= 80;
|
||||||
|
};
|
||||||
|
events_handlers[index] = handler;
|
||||||
|
el.addEventListener(ev as string, handler, { passive: true } as AddEventListenerOptions);
|
||||||
|
});
|
||||||
|
|
||||||
|
observer = new MutationObserver(() => {
|
||||||
|
if (false === scrolledToBottom) return;
|
||||||
|
el.scrollTop = el.scrollHeight;
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(el, { childList: true, subtree: true });
|
||||||
|
}
|
||||||
|
const on_unmounted = (el: HTMLElement) => {
|
||||||
|
AUTO_SCROLL_EVENTS.forEach((ev, index) => {
|
||||||
|
const handler = events_handlers[index];
|
||||||
|
if (handler) {
|
||||||
|
el.removeEventListener(ev as string, handler, { passive: true } as EventListenerOptions);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
observer.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineNuxtPlugin(nuxtApp => {
|
||||||
|
nuxtApp.vueApp.directive('autoscroll', {
|
||||||
|
mounted: on_mounted,
|
||||||
|
unmounted: on_unmounted,
|
||||||
|
});
|
||||||
|
});
|
||||||
54
ui/app/plugins/rtime.ts
Normal file
54
ui/app/plugins/rtime.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
|
type RTimeElement = HTMLElement & { _next_timer?: number }
|
||||||
|
|
||||||
|
const parseInterval = (arg: string | undefined): number => {
|
||||||
|
if (!arg) {
|
||||||
|
return 60 * 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
const match = arg.match(/^(\d+)([smhd])$/)
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
return 60 * 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
const [, numStr, unit] = match
|
||||||
|
const num = parseInt(String(numStr), 10)
|
||||||
|
|
||||||
|
switch (unit) {
|
||||||
|
case 'd': return num * 24 * 3600 * 1000
|
||||||
|
case 'h': return num * 3600 * 1000
|
||||||
|
case 'm': return num * 60 * 1000
|
||||||
|
case 's': return num * 1000
|
||||||
|
default: return 60 * 1000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineNuxtPlugin(nuxtApp => {
|
||||||
|
nuxtApp.vueApp.directive('rtime', {
|
||||||
|
mounted(el: RTimeElement, binding) {
|
||||||
|
const intervalMs = parseInterval(binding.arg)
|
||||||
|
const update = () => el.textContent = moment(binding.value).fromNow()
|
||||||
|
|
||||||
|
update()
|
||||||
|
el._next_timer = window.setInterval(update, intervalMs)
|
||||||
|
},
|
||||||
|
updated(el: RTimeElement, binding) {
|
||||||
|
if (binding.oldValue !== binding.value) {
|
||||||
|
if (null != el._next_timer) clearInterval(el._next_timer)
|
||||||
|
|
||||||
|
const intervalMs = parseInterval(binding.arg)
|
||||||
|
const update = () => el.textContent = moment(binding.value).fromNow()
|
||||||
|
|
||||||
|
update()
|
||||||
|
el._next_timer = window.setInterval(update, intervalMs)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeUnmount(el: RTimeElement) {
|
||||||
|
if (null != el._next_timer) clearInterval(el._next_timer)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {}
|
||||||
|
})
|
||||||
12
ui/app/plugins/toast.ts
Normal file
12
ui/app/plugins/toast.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { defineNuxtPlugin } from '#app'
|
||||||
|
import Toast, { type PluginOptions } from 'vue-toastification'
|
||||||
|
import 'vue-toastification/dist/index.css'
|
||||||
|
|
||||||
|
export default defineNuxtPlugin(nuxtApp => {
|
||||||
|
nuxtApp.vueApp.use(Toast, {
|
||||||
|
transition: "Vue-Toastification__bounce",
|
||||||
|
//position: "bottom-right",
|
||||||
|
maxToasts: 5,
|
||||||
|
newestOnTop: true,
|
||||||
|
} as PluginOptions)
|
||||||
|
})
|
||||||
|
|
@ -26,6 +26,7 @@ export const useConfigStore = defineStore('config', () => {
|
||||||
app_commit_sha: '',
|
app_commit_sha: '',
|
||||||
app_build_date: '',
|
app_build_date: '',
|
||||||
app_branch: '',
|
app_branch: '',
|
||||||
|
started: 0,
|
||||||
},
|
},
|
||||||
presets: [
|
presets: [
|
||||||
{
|
{
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { io } from "socket.io-client";
|
import { io } from "socket.io-client";
|
||||||
import { ag } from "~/utils/index"
|
|
||||||
import type { Socket as IOSocket, SocketOptions } from "socket.io-client"
|
import type { Socket as IOSocket, SocketOptions } from "socket.io-client"
|
||||||
import type { ManagerOptions } from "socket.io-client";
|
import type { ManagerOptions } from "socket.io-client";
|
||||||
|
import type { StoreItem } from "~/types/store";
|
||||||
|
|
||||||
export const useSocketStore = defineStore('socket', () => {
|
export const useSocketStore = defineStore('socket', () => {
|
||||||
const runtimeConfig = useRuntimeConfig()
|
const runtimeConfig = useRuntimeConfig()
|
||||||
|
|
@ -65,7 +65,7 @@ export const useSocketStore = defineStore('socket', () => {
|
||||||
on('item_added', stream => {
|
on('item_added', stream => {
|
||||||
const json = JSON.parse(stream);
|
const json = JSON.parse(stream);
|
||||||
stateStore.add('queue', json.data._id, json.data);
|
stateStore.add('queue', json.data._id, json.data);
|
||||||
toast.success(`Item queued: ${ag(stateStore.get('queue', json.data._id, {}), 'title')}`);
|
toast.success(`Item queued: ${ag(stateStore.get('queue', json.data._id, {} as StoreItem), 'title')}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
on(['log_info', 'log_success', 'log_warning', 'log_error'], (event: string, stream: string) => {
|
on(['log_info', 'log_success', 'log_warning', 'log_error'], (event: string, stream: string) => {
|
||||||
|
|
@ -111,7 +111,7 @@ export const useSocketStore = defineStore('socket', () => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
toast.warning(`Download cancelled: ${ag(stateStore.get('queue', id, {}), 'title')}`);
|
toast.warning(`Download cancelled: ${ag(stateStore.get('queue', id, {} as StoreItem), 'title')}`);
|
||||||
|
|
||||||
if (true === stateStore.has('queue', id)) {
|
if (true === stateStore.has('queue', id)) {
|
||||||
stateStore.remove('queue', id);
|
stateStore.remove('queue', id);
|
||||||
|
|
@ -1,24 +1,22 @@
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { reactive, toRefs } from 'vue'
|
|
||||||
import type { StoreItem } from '~/types/store'
|
import type { StoreItem } from '~/types/store'
|
||||||
|
|
||||||
type StateType = 'queue' | 'history'
|
type StateType = 'queue' | 'history'
|
||||||
type KeyType = string
|
type KeyType = string
|
||||||
type ValueType = StoreItem
|
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
queue: Record<KeyType, ValueType>
|
queue: Record<KeyType, StoreItem>
|
||||||
history: Record<KeyType, ValueType>
|
history: Record<KeyType, StoreItem>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useStateStore = defineStore('state', () => {
|
export const useStateStore = defineStore('state', () => {
|
||||||
const state = reactive<State>({ queue: {}, history: {} })
|
const state = reactive<State>({ queue: {}, history: {} })
|
||||||
|
|
||||||
const add = (type: StateType, key: KeyType, value: ValueType): void => {
|
const add = (type: StateType, key: KeyType, value: StoreItem): void => {
|
||||||
state[type][key] = value
|
state[type][key] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
const update = (type: StateType, key: KeyType, value: ValueType): void => {
|
const update = (type: StateType, key: KeyType, value: StoreItem): void => {
|
||||||
state[type][key] = value
|
state[type][key] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -28,7 +26,7 @@ export const useStateStore = defineStore('state', () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const get = (type: StateType, key: KeyType, defaultValue: ValueType | null | object = null): ValueType | null => {
|
const get = (type: StateType, key: KeyType, defaultValue: StoreItem | null = null): StoreItem | null => {
|
||||||
return state[type][key] || defaultValue
|
return state[type][key] || defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,7 +38,7 @@ export const useStateStore = defineStore('state', () => {
|
||||||
state[type] = {}
|
state[type] = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const addAll = (type: StateType, data: Record<KeyType, ValueType>): void => {
|
const addAll = (type: StateType, data: Record<KeyType, StoreItem>): void => {
|
||||||
state[type] = data
|
state[type] = data
|
||||||
}
|
}
|
||||||
|
|
||||||
0
ui/types/item.d.ts → ui/app/types/item.d.ts
vendored
0
ui/types/item.d.ts → ui/app/types/item.d.ts
vendored
|
|
@ -584,16 +584,72 @@ const formatTime = (seconds: number): string => {
|
||||||
*/
|
*/
|
||||||
const sleep = (seconds: number): Promise<void> => new Promise(resolve => setTimeout(resolve, seconds * 1000))
|
const sleep = (seconds: number): Promise<void> => new Promise(resolve => setTimeout(resolve, seconds * 1000))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits for the test function to return a truthy value.
|
||||||
|
*
|
||||||
|
* @param test - The function to test
|
||||||
|
* @param timeout_ms - The maximum time to wait in milliseconds.
|
||||||
|
* @param frequency - The frequency to check the test function in milliseconds.
|
||||||
|
*
|
||||||
|
* @returns The result of the test function.
|
||||||
|
*/
|
||||||
|
const awaiter = async (test: Function, timeout_ms: number = 20 * 1000, frequency: number = 200) => {
|
||||||
|
if (typeof (test) != "function") {
|
||||||
|
throw new Error("test should be a function in awaiter(test, [timeout_ms], [frequency])")
|
||||||
|
}
|
||||||
|
|
||||||
|
const isNotTruthy = (val: any) => val === undefined || val === false || val === null || val.length === 0;
|
||||||
|
const endTime: number = Date.now() + timeout_ms;
|
||||||
|
|
||||||
|
let result = test();
|
||||||
|
|
||||||
|
while (isNotTruthy(result)) {
|
||||||
|
if (Date.now() > endTime) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
await sleep(frequency);
|
||||||
|
result = test();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a JavaScript object into a URL-safe base64 string.
|
||||||
|
*
|
||||||
|
* @param obj - The object to encode.
|
||||||
|
* @returns A URL-safe base64-encoded string.
|
||||||
|
*/
|
||||||
|
const encode = (obj: Record<string, any>): string => {
|
||||||
|
const jsonStr = JSON.stringify(obj);
|
||||||
|
const utf8Bytes = new TextEncoder().encode(jsonStr);
|
||||||
|
const binary = String.fromCharCode(...utf8Bytes);
|
||||||
|
const base64 = btoa(binary);
|
||||||
|
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a URL-safe base64 string into an object.
|
||||||
|
*
|
||||||
|
* @param str - The URL-safe base64-encoded string.
|
||||||
|
* @returns The decoded JavaScript object.
|
||||||
|
*/
|
||||||
|
const decode = (str: string): object => {
|
||||||
|
const base64 = str
|
||||||
|
.replace(/-/g, '+')
|
||||||
|
.replace(/_/g, '/')
|
||||||
|
.padEnd(str.length + (4 - str.length % 4) % 4, '=');
|
||||||
|
|
||||||
|
const binary = atob(base64);
|
||||||
|
const bytes = Uint8Array.from(binary, c => c.charCodeAt(0));
|
||||||
|
const jsonStr = new TextDecoder().decode(bytes);
|
||||||
|
return JSON.parse(jsonStr);
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
convertCliOptions, separators, getSeparatorsName, iTrim, eTrim, sTrim, ucFirst,
|
separators, convertCliOptions, getSeparatorsName, iTrim, eTrim, sTrim, ucFirst,
|
||||||
getValue, ag, ag_set, awaitElement, r, copyText, dEvent, makePagination, encodePath,
|
getValue, ag, ag_set, awaitElement, r, copyText, dEvent, makePagination, encodePath,
|
||||||
request, removeANSIColors, dec2hex, makeId, basename, dirname, getQueryParams,
|
request, removeANSIColors, dec2hex, makeId, basename, dirname, getQueryParams,
|
||||||
makeDownload,
|
makeDownload, formatBytes, has_data, toggleClass, cleanObject, uri, formatTime,
|
||||||
formatBytes,
|
sleep, awaiter, encode, decode
|
||||||
has_data,
|
|
||||||
toggleClass,
|
|
||||||
cleanObject,
|
|
||||||
uri,
|
|
||||||
formatTime,
|
|
||||||
sleep
|
|
||||||
}
|
}
|
||||||
|
|
@ -5,9 +5,28 @@ function match_str(filterStr: string, dct: Record<string, any>, incomplete: bool
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return filterStr
|
if (/\s*&\s*$/.test(filterStr)) {
|
||||||
.split(/(?<!\\)&/)
|
return false;
|
||||||
.every(filterPart => _match_one(filterPart.replace(/\\&/g, '&'), dct, incomplete));
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const filterParts = filterStr
|
||||||
|
.split(/(?:(^|[^\\]))&/g)
|
||||||
|
.reduce((parts, part, index, array) => {
|
||||||
|
if (index % 2 === 0) {
|
||||||
|
parts.push((part + (array[index + 1] || '')).replace(/\\&/g, '&'));
|
||||||
|
}
|
||||||
|
return parts;
|
||||||
|
}, [] as string[]);
|
||||||
|
|
||||||
|
if (filterParts.some(fp => !fp.trim())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return filterParts.every(filterPart => _match_one(filterPart, dct, incomplete));
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function lookup_unit_table(unitTable: Record<string, number>, s: string, strict = false): number | null {
|
function lookup_unit_table(unitTable: Record<string, number>, s: string, strict = false): number | null {
|
||||||
|
|
@ -101,7 +120,14 @@ function _match_one(filterPart: string, dct: Record<string, any>, incomplete: bo
|
||||||
const opFn = negation ? (a: any, b: any) => !unnegatedOp(a, b) : unnegatedOp;
|
const opFn = negation ? (a: any, b: any) => !unnegatedOp(a, b) : unnegatedOp;
|
||||||
|
|
||||||
let value = quoted ?? plain ?? '';
|
let value = quoted ?? plain ?? '';
|
||||||
if (quote) value = value.replace(new RegExp(`\\\\${quote}`, 'g'), quote);
|
|
||||||
|
if (quote) {
|
||||||
|
value = value.replace(new RegExp(`\\\\${quote}`, 'g'), quote);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(key! in dct)) {
|
||||||
|
return isIncomplete(key!) || Boolean(noneInclusive);
|
||||||
|
}
|
||||||
|
|
||||||
const actual = dct[key!];
|
const actual = dct[key!];
|
||||||
let numeric: number | null = null;
|
let numeric: number | null = null;
|
||||||
|
|
@ -117,10 +143,6 @@ function _match_one(filterPart: string, dct: Record<string, any>, incomplete: bo
|
||||||
throw new Error(`Operator ${op} only supports string values!`);
|
throw new Error(`Operator ${op} only supports string values!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actual == null) {
|
|
||||||
return isIncomplete(key!) || Boolean(noneInclusive);
|
|
||||||
}
|
|
||||||
|
|
||||||
return opFn(actual, numeric !== null ? numeric : value);
|
return opFn(actual, numeric !== null ? numeric : value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,12 +155,12 @@ function _match_one(filterPart: string, dct: Record<string, any>, incomplete: bo
|
||||||
const mu = filterPart.trim().match(unaryRe);
|
const mu = filterPart.trim().match(unaryRe);
|
||||||
if (mu?.groups) {
|
if (mu?.groups) {
|
||||||
const { op, key } = mu.groups;
|
const { op, key } = mu.groups;
|
||||||
const actual = dct[key!];
|
|
||||||
|
|
||||||
if (actual === undefined && isIncomplete(key!)) {
|
if (!(key! in dct)) {
|
||||||
return true;
|
return isIncomplete(key!);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const actual = dct[key!];
|
||||||
const unaryOp = UNARY_OPERATORS[op!];
|
const unaryOp = UNARY_OPERATORS[op!];
|
||||||
|
|
||||||
if (!unaryOp) {
|
if (!unaryOp) {
|
||||||
|
|
@ -70,7 +70,11 @@ export default defineNuxtConfig({
|
||||||
},
|
},
|
||||||
...extraNitro,
|
...extraNitro,
|
||||||
},
|
},
|
||||||
|
vite: {
|
||||||
|
server: {
|
||||||
|
allowedHosts: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
telemetry: false,
|
telemetry: false,
|
||||||
compatibilityDate: "2024-07-13",
|
compatibilityDate: "2024-07-13",
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
let observer;
|
|
||||||
const AUTO_SCROLL_EVENTS = ['scroll', 'mousewheel', 'touchmove'];
|
|
||||||
let events_handlers = [];
|
|
||||||
|
|
||||||
export default defineNuxtPlugin((nuxtApp) => {
|
|
||||||
nuxtApp.vueApp.directive('autoscroll', {
|
|
||||||
mounted: async el => {
|
|
||||||
let scrolledToBottom = true;
|
|
||||||
|
|
||||||
AUTO_SCROLL_EVENTS.forEach((ev, index) => {
|
|
||||||
events_handlers[index] = () => {
|
|
||||||
scrolledToBottom = (el.scrollHeight - el.scrollTop) - el.clientHeight <= 80;
|
|
||||||
}
|
|
||||||
el.addEventListener(ev, events_handlers[index], { passive: true });
|
|
||||||
});
|
|
||||||
|
|
||||||
observer = new MutationObserver(_ => {
|
|
||||||
if (false === scrolledToBottom) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
el.scrollTop = el.scrollHeight;
|
|
||||||
});
|
|
||||||
|
|
||||||
observer.observe(el, { childList: true, subtree: true });
|
|
||||||
},
|
|
||||||
unmounted: el => {
|
|
||||||
AUTO_SCROLL_EVENTS.forEach((ev, index) => {
|
|
||||||
el.removeEventListener(ev, events_handlers[index], { passive: true });
|
|
||||||
});
|
|
||||||
observer.disconnect();
|
|
||||||
},
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
import { defineNuxtPlugin } from '#app'
|
|
||||||
import moment from 'moment'
|
|
||||||
|
|
||||||
const parseInterval = arg => {
|
|
||||||
const match = arg.match(/^(\d+)([smhd])$/)
|
|
||||||
if (!match) {
|
|
||||||
return 60 * 1000
|
|
||||||
}
|
|
||||||
|
|
||||||
const [, numStr, unit] = match
|
|
||||||
const num = parseInt(numStr, 10)
|
|
||||||
|
|
||||||
switch (unit) {
|
|
||||||
case 'd': return num * 24 * 3600 * 1000
|
|
||||||
case 'h': return num * 3600 * 1000
|
|
||||||
case 'm': return num * 60 * 1000
|
|
||||||
case 's': return num * 1000
|
|
||||||
default: return 60 * 1000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default defineNuxtPlugin(nuxtApp => nuxtApp.vueApp.directive('rtime', {
|
|
||||||
mounted(el, binding) {
|
|
||||||
const intervalMs = binding.arg ? parseInterval(binding.arg) : 60 * 1000
|
|
||||||
const update = () => el.textContent = moment(binding.value).fromNow()
|
|
||||||
|
|
||||||
update()
|
|
||||||
el._next_timer = window.setInterval(update, intervalMs)
|
|
||||||
},
|
|
||||||
updated(el, binding) {
|
|
||||||
if (binding.oldValue !== binding.value) {
|
|
||||||
if (null != el._next_timer) {
|
|
||||||
clearInterval(el._next_timer)
|
|
||||||
}
|
|
||||||
|
|
||||||
const intervalMs = binding.arg ? parseInterval(binding.arg) : 60 * 1000
|
|
||||||
|
|
||||||
const update = () => el.textContent = moment(binding.value).fromNow()
|
|
||||||
update()
|
|
||||||
|
|
||||||
el._next_timer = window.setInterval(update, intervalMs)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
beforeUnmount(el) {
|
|
||||||
if (null != el._next_timer) {
|
|
||||||
clearInterval(el._next_timer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
import Toast from 'vue-toastification'
|
|
||||||
import 'vue-toastification/dist/index.css'
|
|
||||||
|
|
||||||
export default defineNuxtPlugin(nuxtApp => nuxtApp.vueApp.use(Toast, {
|
|
||||||
transition: "Vue-Toastification__bounce",
|
|
||||||
//position: "bottom-right",
|
|
||||||
maxToasts: 5,
|
|
||||||
newestOnTop: true
|
|
||||||
}))
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"allowImportingTsExtensions": false,
|
"allowImportingTsExtensions": false,
|
||||||
"types": [
|
"types": [
|
||||||
"./types/globals"
|
"./app/types/globals"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
|
||||||
|
|
||||||
// for non arrays, length is undefined, so != 0
|
|
||||||
const isNotTruthy = (val: any) => val === undefined || val === false || val === null || val.length === 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Waits for the test function to return a truthy value.
|
|
||||||
*
|
|
||||||
* @param test - The function to test
|
|
||||||
* @param timeout_ms - The maximum time to wait in milliseconds.
|
|
||||||
* @param frequency - The frequency to check the test function in milliseconds.
|
|
||||||
*
|
|
||||||
* @returns The result of the test function.
|
|
||||||
*/
|
|
||||||
export default async function awaiter(test: Function, timeout_ms: number = 20 * 1000, frequency: number = 200) {
|
|
||||||
if (typeof (test) != "function") {
|
|
||||||
throw new Error("test should be a function in awaiter(test, [timeout_ms], [frequency])")
|
|
||||||
}
|
|
||||||
|
|
||||||
const endTime: number = Date.now() + timeout_ms;
|
|
||||||
|
|
||||||
let result = test();
|
|
||||||
|
|
||||||
while (isNotTruthy(result)) {
|
|
||||||
if (Date.now() > endTime) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
await sleep(frequency);
|
|
||||||
result = test();
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
const encode = (obj: Record<string, any>): string => {
|
|
||||||
const jsonStr = JSON.stringify(obj);
|
|
||||||
const utf8Bytes = new TextEncoder().encode(jsonStr);
|
|
||||||
const binary = String.fromCharCode(...utf8Bytes);
|
|
||||||
const base64 = btoa(binary);
|
|
||||||
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
const decode = (str: string): object => {
|
|
||||||
const base64 = str
|
|
||||||
.replace(/-/g, '+')
|
|
||||||
.replace(/_/g, '/')
|
|
||||||
.padEnd(str.length + (4 - str.length % 4) % 4, '=');
|
|
||||||
|
|
||||||
const binary = atob(base64);
|
|
||||||
const bytes = Uint8Array.from(binary, c => c.charCodeAt(0));
|
|
||||||
const jsonStr = new TextDecoder().decode(bytes);
|
|
||||||
return JSON.parse(jsonStr);
|
|
||||||
}
|
|
||||||
|
|
||||||
export { encode, decode }
|
|
||||||
Loading…
Reference in a new issue