From 196d2d4e320088bbcdbc20a21fbfa680c97170ad Mon Sep 17 00:00:00 2001 From: arabcoders Date: Tue, 9 Jun 2026 18:30:39 +0300 Subject: [PATCH] feat: add share button --- README.md | 25 ++++++++++--------- ui/app/components/NewDownload.vue | 2 ++ ui/app/composables/useWebShare.ts | 38 +++++++++++++++++++++++++++++ ui/app/pages/history.vue | 40 +++++++++++++++++++++++++++++++ ui/app/utils/index.ts | 3 +-- 5 files changed, 95 insertions(+), 13 deletions(-) create mode 100644 ui/app/composables/useWebShare.ts diff --git a/README.md b/README.md index e42217bd..f73f6386 100644 --- a/README.md +++ b/README.md @@ -50,17 +50,22 @@ Please read the [FAQ](FAQ.md) for more information. ## Run using docker command ```bash -mkdir -p ./{config,downloads/files,downloads/tmp} && docker run -itd --rm --user "${UID}:${UID}" --name ytptube \ +mkdir -p ./{config,downloads/{files,tmp}} && docker run -itd --rm --user "${UID}:${UID}" --name ytptube \ -e YTP_TEMP_PATH=/downloads/tmp -e YTP_DOWNLOAD_PATH=/downloads/files \ -p 8081:8081 -v ./config:/config:rw -v ./downloads:/downloads:rw \ ghcr.io/arabcoders/ytptube:latest ``` -Then you can access the WebUI at `http://localhost:8081`. +## Run using podman -> [!NOTE] -> If you are using `podman` instead of `docker`, you can use the same command, but you need to change the user to `0:0` -> it will appears to be running as root, but it will run as the user who started the container. +```bash +mkdir -p ./{config,downloads/{files,tmp}} && podman run -itd --rm --userns=keep-id --name ytptube \ +-e YTP_TEMP_PATH=/downloads/tmp -e YTP_DOWNLOAD_PATH=/downloads/files \ +-p 8081:8081 -v ./config:/config:rw -v ./downloads:/downloads:rw \ +arabcoders/ytptube:latest +``` + +Then you can access the WebUI at `http://localhost:8081`. ## Using compose file @@ -70,6 +75,8 @@ The following is an example of a `compose.yaml` file that can be used to run YTP services: ytptube: user: "${UID:-1000}:${UID:-1000}" # change this to your user id and group id. + # comment out the above line and uncomment the below line if you are using podman-compose. + #userns_mode: keep-id image: ghcr.io/arabcoders/ytptube:latest container_name: ytptube restart: unless-stopped @@ -84,18 +91,14 @@ services: ``` > [!IMPORTANT] -> Make sure to change the `user` line to match your user id and group id. +> Make sure to change the `user` line to match your user id and group id in docker setups, or use `userns_mode: keep-id` in podman setups. ```bash -mkdir -p ./{config,downloads/files,downloads/tmp} && docker compose -f compose.yaml up -d +mkdir -p ./{config,downloads/{files,tmp}} && docker compose -f compose.yaml up -d ``` Then you can access the WebUI at `http://localhost:8081`. -> [!NOTE] -> you can use podman-compose instead of docker-compose, as it supports the same syntax. However, you should change the -> user to `0:0` it will appears to be running as root, but it will run as the user who started the container. - ## Unraid For `Unraid` users You can install the `Community Applications` plugin, and search for **ytptube** it comes diff --git a/ui/app/components/NewDownload.vue b/ui/app/components/NewDownload.vue index d1d8f54d..6c9253f1 100644 --- a/ui/app/components/NewDownload.vue +++ b/ui/app/components/NewDownload.vue @@ -37,6 +37,7 @@ base: 'min-h-[7.25rem] bg-elevated/60 ring-default focus-visible:ring-primary', }" @keydown="handleKeyDown" + autofocus /> diff --git a/ui/app/composables/useWebShare.ts b/ui/app/composables/useWebShare.ts new file mode 100644 index 00000000..741d8bd2 --- /dev/null +++ b/ui/app/composables/useWebShare.ts @@ -0,0 +1,38 @@ +import { useDialog } from './useDialog'; +import { useYtpConfig } from './useYtpConfig'; +import { makeDownload } from '~/utils'; +import type { StoreItem } from '~/types/store'; + +export const useWebShare = () => { + const canShare = (): boolean => + typeof navigator !== 'undefined' && typeof navigator.share === 'function'; + + const shareUrl = async (download: StoreItem): Promise => { + if (!canShare()) { + useNotification().error('Web Share API is not supported in this browser.'); + return; + } + + try { + const title = download.title || download.filename || 'Download'; + await navigator.share({ + title: title, + text: download.description || title, + url: makeDownload(useYtpConfig(), download), + }); + } catch (err: any) { + if (err?.name === 'AbortError') { + return; + } + + console.error('Share failed:', err); + + await useDialog().alertDialog({ + title: 'Share Failed', + message: `Share failed: ${err?.message || 'unknown error'}`, + }); + } + }; + + return { canShare, shareUrl }; +}; diff --git a/ui/app/pages/history.vue b/ui/app/pages/history.vue index 37d05ef1..112e5327 100644 --- a/ui/app/pages/history.vue +++ b/ui/app/pages/history.vue @@ -28,6 +28,16 @@
+ + Add + + + + + + Share + + >( 'pending-download-form-item', () => ({}), @@ -885,6 +917,8 @@ watch(video_item, (value) => { document.querySelector('body')?.setAttribute('style', `opacity: ${value ? 1 : bg_opacity.value}`); }); +const canShareUrl = computed(() => canShare()); + watch(embed_url, (value) => { if (!bg_enable.value) { return; @@ -933,6 +967,12 @@ const changeDisplay = (): void => { display_style.value = display_style.value === 'grid' ? 'list' : 'grid'; }; +const addNewDownload = async (): Promise => { + config.showForm = true; + await nextTick(); + await navigateTo('/'); +}; + const toNewDownload = async (item: item_request | Partial): Promise => { if (!item) { return; diff --git a/ui/app/utils/index.ts b/ui/app/utils/index.ts index 49655989..da333abf 100644 --- a/ui/app/utils/index.ts +++ b/ui/app/utils/index.ts @@ -476,8 +476,7 @@ const makeDownload = ( } if (item.folder) { - item.folder = item.folder.replace(/#/g, '%23'); - baseDir += item.folder + '/'; + baseDir += item.folder.replace(/#/g, '%23') + '/'; } if (!item.filename) {