Merge pull request #451 from arabcoders/dev

[FEAT] add custom download fields to simple mode.
This commit is contained in:
Abdulmohsen 2025-10-16 17:23:02 +03:00 committed by GitHub
commit 618ce4d387
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 101 additions and 9 deletions

1
.gitignore vendored
View file

@ -39,3 +39,4 @@ build
version.txt
test_impl.py
./eslint.config.js
.pytest_cache

View file

@ -99,8 +99,17 @@ For simple API documentation, you can refer to the [API documentation](API.md).
# Disclaimer
This project is not affiliated with yt-dlp, or any other service. It's a personal project that was created to
make downloading videos from the internet easier. It's not intended to be used for piracy or any other illegal activities.
This project is not affiliated with yt-dlp or any other service.
Its a personal project designed to make downloading videos from the internet more convenient. Its not intended for
piracy or any unlawful use.
This project was built primarily for my own needs and preferences. The UI might not be the most polished or visually
refined, but Im happy with it as it is. You can, however, create and load your own UI for complete customization. I
plan to refactor the UI/UX in the future using [Nuxt/ui](https://ui.nuxt.com/).
Contributions are welcome, but I may decline changes that dont align with my vision for the project. Unsolicited pull
requests may be ignored. For suggestions or feature requests, please open a discussion or join the Discord server.
# Social contact

View file

@ -370,3 +370,4 @@ div.is-centered {
padding: 0;
margin: 0 auto;
}

View file

@ -8,7 +8,7 @@
<div class="is-block" style="word-break: all;" v-if="props.title">
<span style="font-size: 120%;">{{ props.title }}</span>
</div>
<figure class="image is-3by1 is-hidden-mobile">
<figure :class="['image', thumbnail_ratio, 'is-hidden-mobile']">
<img @load="e => pImg(e)" :src="url" :alt="props.title" @error="clearCache"
:crossorigin="props.privacy ? 'anonymous' : 'use-credentials'"
:referrerpolicy="props.privacy ? 'no-referrer' : 'origin'" />
@ -20,6 +20,7 @@
</template>
<script setup lang="ts">
import { useStorage } from '@vueuse/core'
import { disableOpacity, enableOpacity } from '~/utils'
const props = defineProps<{
@ -31,6 +32,7 @@ const props = defineProps<{
const cache = useSessionCache()
const toast = useNotification()
const thumbnail_ratio = useStorage<'is-16by9' | 'is-3by1'>('thumbnail_ratio', 'is-3by1')
const url = ref<string | null>(null)
const error = ref(false)
const isPreloading = ref(false)

View file

@ -266,7 +266,7 @@
</div>
</header>
<div v-if="showThumbnails" class="card-image">
<figure class="image is-3by1">
<figure :class="['image', thumbnail_ratio]">
<span v-if="'finished' === item.status && item.filename" @click="playVideo(item)" class="play-overlay">
<div class="play-icon"></div>
<img @load="(e: Event) => pImg(e)" :src="getImage(item)" v-if="getImage(item)" />
@ -475,6 +475,7 @@ const direction = useStorage<'asc' | 'desc'>('sortCompleted', 'desc')
const display_style = useStorage<'grid' | 'list'>('display_style', 'grid')
const bg_enable = useStorage<boolean>('random_bg', true)
const bg_opacity = useStorage<number>('random_bg_opacity', 0.95)
const thumbnail_ratio = useStorage<'is-16by9' | 'is-3by1'>('thumbnail_ratio', 'is-3by1')
const selectedElms = ref<string[]>([])
const masterSelectAll = ref(false)

View file

@ -198,7 +198,7 @@
</div>
</header>
<div v-if="showThumbnails" class="card-image">
<figure class="image is-3by1">
<figure :class="['image', thumbnail_ratio]">
<span v-if="isEmbedable(item.url)" @click="embed_url = getEmbedable(item.url) as string"
class="play-overlay">
<div class="play-icon embed-icon"></div>
@ -336,6 +336,7 @@ const hideThumbnail = useStorage('hideThumbnailQueue', false)
const display_style = useStorage('display_style', 'grid')
const bg_enable = useStorage('random_bg', true)
const bg_opacity = useStorage('random_bg_opacity', 0.95)
const thumbnail_ratio = useStorage<'is-16by9' | 'is-3by1'>('thumbnail_ratio', 'is-3by1')
const selectedElms = ref<string[]>([])
const masterSelectAll = ref(false)

View file

@ -104,6 +104,21 @@
<p class="help is-bold"> Show videos thumbnail if available.</p>
</div>
<div class="field" v-if="show_thumbnail">
<label class="label is-unselectable">Thumbnail Aspect Ratio</label>
<div class="control">
<label for="ratio_16by9" class="radio">
<input id="ratio_16by9" type="radio" v-model="thumbnail_ratio" value="is-16by9">
<span>&nbsp;16:9</span>
</label>
<label for="ratio_3by1" class="radio">
<input id="ratio_3by1" type="radio" v-model="thumbnail_ratio" value="is-3by1">
<span>&nbsp;3:1</span>
</label>
</div>
<p class="help is-bold">Choose the aspect ratio for thumbnail display.</p>
</div>
<div class="field">
<label class="label" for="allow_toasts">Show notifications</label>
<div class="control">
@ -164,6 +179,7 @@ const allow_toasts = useStorage<boolean>('allow_toasts', true)
const toast_position = useStorage<POSITION>('toast_position', POSITION.TOP_RIGHT)
const toast_dismiss_on_click = useStorage<boolean>('toast_dismiss_on_click', true)
const show_thumbnail = useStorage<boolean>('show_thumbnail', true)
const thumbnail_ratio = useStorage<'is-16by9' | 'is-3by1'>('thumbnail_ratio', 'is-3by1')
const separator = useStorage<string>('url_separator', separators[0]?.value ?? ',')
const simpleMode = useStorage<boolean>('simple_mode', useConfigStore().app.simple_mode || false)
</script>

View file

@ -13,8 +13,9 @@
</label>
<div class="field has-addons">
<div class="control">
<button type="button" class="button is-info" @click="() => showPresets = !showPresets">
<span class="icon"><i class="fas" :class="showPresets ? 'fa-chevron-up' : 'fa-chevron-down'" /></span>
<button type="button" class="button is-info" @click="() => showExtras = !showExtras"
v-tooltip="showExtras ? 'Hide extra options' : 'Show extra options'">
<span class="icon"><i class="fas" :class="showExtras ? 'fa-chevron-up' : 'fa-chevron-down'" /></span>
</button>
</div>
<div class="control is-expanded">
@ -29,7 +30,7 @@
</button>
</div>
</div>
<div class="field has-addons" v-if="showPresets">
<div class="field has-addons" v-if="showExtras">
<div class="control">
<label class="button is-static">
<span class="icon"><i class="fas fa-sliders" /></span>
@ -55,6 +56,20 @@
</div>
</div>
<div class="columns is-multiline is-mobile" v-if="showExtras && configStore.dl_fields.length > 0">
<div class="column is-6-tablet is-12-mobile">
<DLInput id="force_download" type="bool" label="Force download"
v-model="dlFields['--no-download-archive']" icon="fa-solid fa-download"
:disabled="!socketStore.isConnected || addInProgress" description="Ignore archive and re-download." />
</div>
<div class="column is-6-tablet is-12-mobile" v-for="(fi, index) in sortedDLFields"
:key="fi.id || `dlf-${index}`">
<DLInput :id="fi?.id || `dlf-${index}`" :type="fi.kind" :description="fi.description" :label="fi.name"
:icon="fi.icon" v-model="dlFields[fi.field]" :field="fi.field"
:disabled="!socketStore.isConnected || addInProgress" />
</div>
</div>
</form>
</section>
</div>
@ -213,7 +228,8 @@ const videoItem = ref<StoreItem | null>(null)
const formUrl = ref<string>('')
const formPreset = ref<{ preset: string }>({ preset: app.value.default_preset || '' })
const addInProgress = ref<boolean>(false)
const showPresets = ref<boolean>(false)
const showExtras = ref<boolean>(false)
const dlFields = useStorage<Record<string, any>>('dl_fields', {})
const show_thumbnail = useStorage<boolean>('show_thumbnail', true)
const sortByNewest = (items: StoreItem[]): StoreItem[] => items.slice().sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0))
@ -247,9 +263,50 @@ const addDownload = async (): Promise<void> => {
return
}
let cli = ''
const dlFieldsExtra = ['--no-download-archive']
const is_valid = (dl_field: string): boolean => {
if (dlFieldsExtra.includes(dl_field)) {
return true
}
if (configStore.dl_fields && configStore.dl_fields.length > 0) {
return configStore.dl_fields.some(f => dl_field === f.field)
}
return false
}
if (dlFields.value && Object.keys(dlFields.value).length > 0) {
const joined = []
for (const [key, value] of Object.entries(dlFields.value)) {
if (false === is_valid(key)) {
continue
}
if ([undefined, null, '', false].includes(value as any)) {
continue
}
const keyRegex = new RegExp(`(^|\\s)${key}(\\s|$)`)
if (cli && keyRegex.test(cli)) {
continue
}
joined.push(true === value ? `${key}` : `${key} ${value}`)
}
if (joined.length > 0) {
cli = joined.join(' ')
}
}
const payload: item_request[] = [{
url,
preset: formPreset.value.preset || app.value.default_preset,
cli: cli || '',
auto_start: true,
}]
@ -274,6 +331,7 @@ const addDownload = async (): Promise<void> => {
formUrl.value = ''
formPreset.value.preset = app.value.default_preset || ''
dlFields.value = {}
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to add download.'
toast.error(message)
@ -544,6 +602,9 @@ const showMessage = (item: StoreItem) => {
return (item.msg?.length || 0) > 0
}
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
const sortedDLFields = computed(() => configStore.dl_fields.sort((a, b) => (a.order || 0) - (b.order || 0)))
const connectionStatusColor = computed(() => {
switch (socketStore.connectionStatus) {
case 'connected':