From 0239e509defee7b31cc876aed54e0d912c2f19c8 Mon Sep 17 00:00:00 2001 From: ArabCoders Date: Sat, 14 Dec 2024 19:27:13 +0300 Subject: [PATCH] sweeping changes to internals --- .vscode/launch.json | 27 +---- app/Download.py | 10 +- app/DownloadQueue.py | 11 +- app/ItemDTO.py | 1 + app/Utils.py | 4 +- app/main.py | 25 ++-- ui/assets/css/style.css | 6 + ui/components/History.vue | 107 ++++++++++-------- .../{LazyLoader.vue => LateLoader.vue} | 0 .../add.vue => components/NewDownload.vue} | 33 +----- ui/components/Queue.vue | 61 ++-------- ui/composables/useToast.js | 2 + ui/layouts/default.vue | 19 +--- ui/nuxt.config.ts | 8 +- ui/package.json | 5 +- ui/pages/index.vue | 3 + ui/plugins/toast.js | 9 ++ ui/stores/SocketStore.js | 21 ++-- ui/yarn.lock | 45 ++++---- 19 files changed, 183 insertions(+), 214 deletions(-) rename ui/components/{LazyLoader.vue => LateLoader.vue} (100%) rename ui/{pages/add.vue => components/NewDownload.vue} (91%) create mode 100644 ui/composables/useToast.js create mode 100644 ui/plugins/toast.js diff --git a/.vscode/launch.json b/.vscode/launch.json index 866a9b9b..5c60f268 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,18 +5,18 @@ "version": "0.2.0", "configurations": [ { - "name": "Node: App.Vue", + "name": "Node: Nuxt3", "request": "launch", "runtimeArgs": [ "run", - "serve", + "dev", "--", "--port", - "3000" + "8082" ], - "runtimeExecutable": "npm", + "runtimeExecutable": "yarn", "type": "node", - "cwd": "${workspaceFolder}/frontend", + "cwd": "${workspaceFolder}/ui", "env": { "VUE_APP_BASE_URL": "http://localhost:8081", } @@ -73,23 +73,6 @@ } ], "justMyCode": true - }, - { - "name": "Node: NuxtJS", - "request": "launch", - "runtimeArgs": [ - "run", - "dev", - "--", - "--port", - "8082" - ], - "runtimeExecutable": "yarn", - "type": "node", - "cwd": "${workspaceFolder}/ui", - "env": { - "VUE_APP_BASE_URL": "http://localhost:8081", - } } ] } diff --git a/app/Download.py b/app/Download.py index 2ecf9d28..a63a43af 100644 --- a/app/Download.py +++ b/app/Download.py @@ -201,7 +201,7 @@ class Download: self.proc.start() self.info.status = 'preparing' - asyncio.create_task(self.notifier.updated(self.info), name=f"notifier-{self.id}") + asyncio.create_task(self.notifier.updated(dl=self.info), name=f"notifier-{self.id}") asyncio.create_task(self.progress_update(), name=f"update-{self.id}") return await asyncio.get_running_loop().run_in_executor(None, self.proc.join) @@ -329,7 +329,7 @@ class Download: LOG.debug(f'Status Update: {self.info._id=} {status=}') if isinstance(status, str): - asyncio.create_task(self.notifier.updated(self.info), name=f"notifier-u-{self.id}") + asyncio.create_task(self.notifier.updated(dl=self.info), name=f"notifier-u-{self.id}") continue self.tmpfilename = status.get('tmpfilename') @@ -349,7 +349,9 @@ class Download: if self.info.status == 'error' and 'error' in status: self.info.error = status.get('error') - asyncio.create_task(self.notifier.error(self.info, self.info.error), name=f"notifier-e-{self.id}") + asyncio.create_task( + self.notifier.error(dl=self.info, message=self.info.error), + name=f"notifier-e-{self.id}") if 'downloaded_bytes' in status: total = status.get('total_bytes') or status.get('total_bytes_estimate') @@ -368,4 +370,4 @@ class Download: self.info.file_size = None pass - asyncio.create_task(self.notifier.updated(self.info), name=f"notifier-u-{self.id}") + asyncio.create_task(self.notifier.updated(dl=self.info), name=f"notifier-u-{self.id}") diff --git a/app/DownloadQueue.py b/app/DownloadQueue.py index 9a5c968b..6ed916dc 100644 --- a/app/DownloadQueue.py +++ b/app/DownloadQueue.py @@ -12,7 +12,6 @@ from ItemDTO import ItemDTO from DataStore import DataStore from Utils import Notifier, ObjectSerializer, calcDownloadPath, ExtractInfo, isDownloaded, mergeConfig from AsyncPool import AsyncPool -from concurrent.futures import ThreadPoolExecutor LOG = logging.getLogger('DownloadQueue') TYPE_DONE: str = 'done' @@ -295,9 +294,9 @@ class DownloadQueue: await item.close() LOG.debug(f'Deleting from queue {itemMessage}') self.queue.delete(id) - asyncio.create_task(self.notifier.canceled(id), name=f'notifier-c-{id}') + asyncio.create_task(self.notifier.canceled(id=id, dl=item), name=f'notifier-c-{id}') self.done.put(item) - asyncio.create_task(self.notifier.completed(item), name=f'notifier-d-{id}') + asyncio.create_task(self.notifier.completed(dl=item), name=f'notifier-d-{id}') LOG.info(f'Deleted from queue {itemMessage}') status[id] = 'ok' @@ -319,7 +318,7 @@ class DownloadQueue: itemMessage = f"{id=} {item.info.id=} {item.info.title=}" LOG.debug(f'Deleting completed download {itemMessage}') self.done.delete(id) - asyncio.create_task(self.notifier.cleared(id), name=f'notifier-c-{id}') + asyncio.create_task(self.notifier.cleared(id, dl=item), name=f'notifier-c-{id}') LOG.info(f'Deleted completed download {itemMessage}') status[id] = 'ok' @@ -418,12 +417,12 @@ class DownloadQueue: self.queue.delete(key=id) if entry.is_canceled() is True: - asyncio.create_task(self.notifier.canceled(id), name=f'notifier-c-{id}') + asyncio.create_task(self.notifier.canceled(id, dl=entry.info), name=f'notifier-c-{id}') entry.info.status = 'canceled' entry.info.error = 'Canceled by user.' self.done.put(value=entry) - asyncio.create_task(self.notifier.completed(entry.info), name=f'notifier-d-{id}') + asyncio.create_task(self.notifier.completed(dl=entry.info), name=f'notifier-d-{id}') self.event.set() diff --git a/app/ItemDTO.py b/app/ItemDTO.py index ae567dda..5b07fbb6 100644 --- a/app/ItemDTO.py +++ b/app/ItemDTO.py @@ -15,6 +15,7 @@ class ItemDTO: url: str quality: str format: str + preset: str = "default" folder: str download_dir: str = None temp_dir: str = None diff --git a/app/Utils.py b/app/Utils.py index 866e6965..8846af7e 100644 --- a/app/Utils.py +++ b/app/Utils.py @@ -333,10 +333,10 @@ class Notifier: async def completed(self, dl: dict): await self.emit('completed', dl) - async def canceled(self, id: str): + async def canceled(self, id: str, dl: dict = None): await self.emit('canceled', id) - async def cleared(self, id: str): + async def cleared(self, id: str, dl: dict = None): await self.emit('cleared', id) async def error(self, dl: dict, message: str): diff --git a/app/main.py b/app/main.py index aa82ff2f..25a54c1e 100644 --- a/app/main.py +++ b/app/main.py @@ -717,19 +717,28 @@ class Main: await self.sio.emit('status', self.serializer.encode(status)) @self.sio.event - async def cancel_items(sid, post: dict): - ids = post.get('ids') - identifier: str = post.get('identifier') - - if not ids: + async def item_cancel(sid, id: str): + if not id: await self.notifier.warning('Invalid request.') return status: dict[str, str] = {} - status = await self.queue.cancel(ids) - status.update({'identifier': identifier}) + status = await self.queue.cancel([id]) + status.update({'identifier': id}) - await self.sio.emit('cancel_items', self.serializer.encode(status)) + await self.sio.emit('item_cancel', self.serializer.encode(status)) + + @self.sio.event + async def item_delete(sid, id: str): + if not id: + await self.notifier.warning('Invalid request.') + return + + status: dict[str, str] = {} + status = await self.queue.clear([id]) + status.update({'identifier': id}) + + await self.sio.emit('item_delete', self.serializer.encode(status)) @self.sio.event() async def archive_item(sid, data): diff --git a/ui/assets/css/style.css b/ui/assets/css/style.css index 4492540d..e655e006 100644 --- a/ui/assets/css/style.css +++ b/ui/assets/css/style.css @@ -46,6 +46,12 @@ hr { white-space: nowrap; } +.user-hint { + user-select: none; + cursor: help; + border-bottom: 1px dotted; +} + @media (prefers-color-scheme: dark) { * { unicode-bidi: plaintext; diff --git a/ui/components/History.vue b/ui/components/History.vue index b335b489..01b0f7c9 100644 --- a/ui/components/History.vue +++ b/ui/components/History.vue @@ -4,7 +4,7 @@ - History ({{ getTotal }}) + History ({{ stateStore.count('history') }}) @@ -24,7 +24,7 @@
- +
@@ -127,7 +127,8 @@
- + {{ moment(item.datetime).fromNow() }}
@@ -150,33 +151,26 @@
- +
-

+

@@ -219,15 +213,14 @@ diff --git a/ui/composables/useToast.js b/ui/composables/useToast.js new file mode 100644 index 00000000..4738a4ca --- /dev/null +++ b/ui/composables/useToast.js @@ -0,0 +1,2 @@ +import { useToast } from "vue-toastification"; +export default () => useToast() diff --git a/ui/layouts/default.vue b/ui/layouts/default.vue index a5a58ded..d1f51e7a 100644 --- a/ui/layouts/default.vue +++ b/ui/layouts/default.vue @@ -21,12 +21,13 @@

+
@@ -151,17 +153,4 @@ watch(selectedTheme, value => { }) const reloadPage = () => window.location.reload() -const toggleForm = () => { - console.log('emitting show_form'); - bus.emit('show_form', { foo: 'bar' }) - dEvent('show_form', { foo: 'bar' }) -} - - diff --git a/ui/nuxt.config.ts b/ui/nuxt.config.ts index a5761247..45c3e6b3 100644 --- a/ui/nuxt.config.ts +++ b/ui/nuxt.config.ts @@ -8,7 +8,9 @@ export default defineNuxtConfig({ port: 8082, host: "0.0.0.0", }, - + css: [ + 'vue-toastification/dist/index.css' + ], runtimeConfig: { public: { domain: '/', @@ -16,7 +18,9 @@ export default defineNuxtConfig({ version: '2.0.0', } }, - + build: { + transpile: ['vue-toastification'], + }, app: { buildAssetsDir: "assets", head: { diff --git a/ui/package.json b/ui/package.json index 12bb5bed..a986811f 100644 --- a/ui/package.json +++ b/ui/package.json @@ -23,9 +23,10 @@ "nuxt3-notifications": "^1.2.0", "pinia": "^2.1.7", "plyr": "^3.7.8", + "socket.io-client": "^4.7.2", "vue": "^3.4.21", "vue-router": "^4.3.0", - "socket.io-client": "^4.7.2" - }, + "vue-toastification": "^2.0.0-rc.5" + }, "devDependencies": {} } diff --git a/ui/pages/index.vue b/ui/pages/index.vue index bb14d8f3..bffad7dc 100644 --- a/ui/pages/index.vue +++ b/ui/pages/index.vue @@ -7,4 +7,7 @@ diff --git a/ui/plugins/toast.js b/ui/plugins/toast.js new file mode 100644 index 00000000..d6536428 --- /dev/null +++ b/ui/plugins/toast.js @@ -0,0 +1,9 @@ +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 +})) diff --git a/ui/stores/SocketStore.js b/ui/stores/SocketStore.js index def06ba4..17039fe0 100644 --- a/ui/stores/SocketStore.js +++ b/ui/stores/SocketStore.js @@ -1,10 +1,11 @@ import { io } from "socket.io-client"; -import { notification, ag } from "~/utils/index" +import { ag } from "~/utils/index" export const useSocketStore = defineStore('socket', () => { const runtimeConfig = useRuntimeConfig() const config = useConfigStore() const stateStore = useStateStore() + const toast = useToast() const socket = ref(null); const isConnected = ref(false); @@ -31,31 +32,34 @@ export const useSocketStore = defineStore('socket', () => { socket.value.on('added', stream => { const item = JSON.parse(stream); stateStore.add('queue', item._id, item); - notification('success', `Item queued successfully: ${ag(stateStore.get('queue', item._id, {}), 'title')}`); + toast.success(`Item queued successfully: ${ag(stateStore.get('queue', item._id, {}), 'title')}`); }); socket.value.on('error', stream => { const [item, error] = JSON.parse(stream); - notification('error', `${item?.id}: Error: ${error}`); + toast.error(`${item?.id}: Error: ${error}`); }); socket.value.on('completed', stream => { const item = JSON.parse(stream); + + console.log(item) if (true === stateStore.has('queue', item._id)) { - stateStore.move('queue', 'history', item._id); - return + stateStore.remove('queue', item._id); } - stateStore.add('history', item); + + stateStore.add('history', item._id, item); }); socket.value.on('canceled', stream => { const id = JSON.parse(stream); if (true !== stateStore.has('queue', id)) { + console.log(stream) return } - notification('info', `Download canceled: ${ag(stateStore.get('queue', id, {}), 'title')}`); + toast.info(`Download canceled: ${ag(stateStore.get('queue', id, {}), 'title')}`); if (true === stateStore.has('queue', id)) { stateStore.remove('queue', id); @@ -64,9 +68,12 @@ export const useSocketStore = defineStore('socket', () => { socket.value.on('cleared', stream => { const id = JSON.parse(stream); + if (true !== stateStore.has('history', id)) { + console.log(stream) return } + stateStore.remove('history', id); }); diff --git a/ui/yarn.lock b/ui/yarn.lock index c512472d..c3223955 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -1683,13 +1683,13 @@ braces@^3.0.3, braces@~3.0.2: fill-range "^7.1.1" browserslist@^4.0.0, browserslist@^4.23.3, browserslist@^4.24.0: - version "4.24.2" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580" - integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg== + version "4.24.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.3.tgz#5fc2725ca8fb3c1432e13dac278c7cc103e026d2" + integrity sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA== dependencies: - caniuse-lite "^1.0.30001669" - electron-to-chromium "^1.5.41" - node-releases "^2.0.18" + caniuse-lite "^1.0.30001688" + electron-to-chromium "^1.5.73" + node-releases "^2.0.19" update-browserslist-db "^1.1.1" buffer-crc32@^1.0.0: @@ -1750,7 +1750,7 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001669: +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001688: version "1.0.30001688" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001688.tgz#f9d3ede749f083ce0db4c13db9d828adaf2e8d0a" integrity sha512-Nmqpru91cuABu/DTCXbM2NSRHzM2uVHfPnhJ/1zEAJx/ILBRVmz3pzH4N7DZqbdG0gWClsCC05Oj0mJ/1AWMbA== @@ -2248,7 +2248,7 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -electron-to-chromium@^1.5.41: +electron-to-chromium@^1.5.73: version "1.5.73" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.73.tgz#f32956ce40947fa3c8606726a96cd8fb5bb5f720" integrity sha512-8wGNxG9tAG5KhGd3eeA0o6ixhiNdgr0DcHWm85XPCphwZgD1lIEoi6t3VERayWao7SF7AAZTw6oARGJeVjH8Kg== @@ -2864,10 +2864,10 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-core-module@^2.13.0: - version "2.15.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37" - integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== +is-core-module@^2.16.0: + version "2.16.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.0.tgz#6c01ffdd5e33c49c1d2abfa93334a85cb56bd81c" + integrity sha512-urTSINYfAYgcbLb0yDQ6egFm6h3Mo1DcF9EkyXSRjjzdHbsulg01qhwWuXdOoUBuTkbQ80KDboXa0vFJ+BDH+g== dependencies: hasown "^2.0.2" @@ -3468,7 +3468,7 @@ node-gyp-build@^4.2.2: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.4.tgz#8a70ee85464ae52327772a90d66c6077a900cfc8" integrity sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ== -node-releases@^2.0.18: +node-releases@^2.0.19: version "2.0.19" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== @@ -4182,11 +4182,11 @@ resolve-from@^5.0.0: integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== resolve@^1.22.1: - version "1.22.8" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" - integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + version "1.22.9" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.9.tgz#6da76e4cdc57181fa4471231400e8851d0a924f3" + integrity sha512-QxrmX1DzraFIi9PxdG5VkRfRwIgjwyud+z/iBwfRRrVmHc+P9Q7u2lSSpQ6bjr2gy5lrqIiU9vb6iAeGf2400A== dependencies: - is-core-module "^2.13.0" + is-core-module "^2.16.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -4640,9 +4640,9 @@ terser@^5.17.4: source-map-support "~0.5.20" text-decoder@^1.1.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/text-decoder/-/text-decoder-1.2.2.tgz#63dd2401c43895cecb292e2407db184b50ad60ac" - integrity sha512-/MDslo7ZyWTA2vnk1j7XoDVfXsGk3tp+zFEJHJGm0UjIlQifonVFwlVbQDFh8KJzTBnT8ie115TYqir6bclddA== + version "1.2.3" + resolved "https://registry.yarnpkg.com/text-decoder/-/text-decoder-1.2.3.tgz#b19da364d981b2326d5f43099c310cc80d770c65" + integrity sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA== dependencies: b4a "^1.6.4" @@ -5054,6 +5054,11 @@ vue-router@^4.3.0, vue-router@^4.4.5: dependencies: "@vue/devtools-api" "^6.6.4" +vue-toastification@^2.0.0-rc.5: + version "2.0.0-rc.5" + resolved "https://registry.yarnpkg.com/vue-toastification/-/vue-toastification-2.0.0-rc.5.tgz#92798604d806ae473cfb76ed776fae294280f8f8" + integrity sha512-q73e5jy6gucEO/U+P48hqX+/qyXDozAGmaGgLFm5tXX4wJBcVsnGp4e/iJqlm9xzHETYOilUuwOUje2Qg1JdwA== + vue@^3.4.21, vue@^3.5.13: version "3.5.13" resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.13.tgz#9f760a1a982b09c0c04a867903fc339c9f29ec0a"