Compare commits
11 Commits
v1.0.0
...
feat/subsc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1893b5d954 | ||
|
|
0de2098755 | ||
|
|
448a15fce4 | ||
|
|
5af393ef84 | ||
|
|
fe59f1d2bf | ||
|
|
c2b2c35da6 | ||
|
|
41e4a4993c | ||
|
|
0bd27a96c3 | ||
|
|
d36313fe97 | ||
|
|
9040224319 | ||
|
|
56351941ee |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vidbee",
|
||||
"version": "1.0.0",
|
||||
"version": "0.3.5",
|
||||
"description": "A modern Electron application for downloading videos and audios",
|
||||
"main": "./out/main/index.js",
|
||||
"author": "VidBee",
|
||||
|
||||
@@ -448,4 +448,4 @@
|
||||
"internal": {
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,42 +27,16 @@ import {
|
||||
} from '../../store/downloads'
|
||||
import { settingsAtom } from '../../store/settings'
|
||||
|
||||
const normalizeSavedFileName = (fileName?: string): string | undefined => {
|
||||
if (!fileName) {
|
||||
return undefined
|
||||
}
|
||||
const trimmed = fileName.trim()
|
||||
if (!trimmed) {
|
||||
return undefined
|
||||
}
|
||||
return trimmed.replace(/\.f\d+(?=\.[^.]+$)/i, '')
|
||||
}
|
||||
|
||||
const generateFilePathCandidates = (
|
||||
downloadPath: string,
|
||||
title: string,
|
||||
format: string,
|
||||
savedFileName?: string
|
||||
): string[] => {
|
||||
const candidateFileNames = savedFileName
|
||||
? [savedFileName]
|
||||
: [`${title} via VidBee.${format}`, `${title}.${format}`]
|
||||
const normalizedDownloadPath = downloadPath.replace(/\\/g, '/')
|
||||
const safeTitle = title.trim() || 'Unknown'
|
||||
|
||||
const savedNameCandidates: string[] = []
|
||||
const trimmedSavedFileName = savedFileName?.trim()
|
||||
if (trimmedSavedFileName) {
|
||||
const normalized = normalizeSavedFileName(trimmedSavedFileName)
|
||||
if (normalized) {
|
||||
savedNameCandidates.push(normalized)
|
||||
}
|
||||
if (!normalized || normalized !== trimmedSavedFileName) {
|
||||
savedNameCandidates.push(trimmedSavedFileName)
|
||||
}
|
||||
}
|
||||
|
||||
const candidateFileNames =
|
||||
savedNameCandidates.length > 0
|
||||
? savedNameCandidates
|
||||
: [`${safeTitle} via VidBee.${format}`, `${safeTitle}.${format}`]
|
||||
return Array.from(
|
||||
new Set(candidateFileNames.map((fileName) => `${normalizedDownloadPath}/${fileName}`))
|
||||
)
|
||||
@@ -82,10 +56,10 @@ const tryFileOperation = async (
|
||||
}
|
||||
|
||||
const getSavedFileExtension = (fileName?: string): string | undefined => {
|
||||
const normalized = normalizeSavedFileName(fileName)
|
||||
if (!normalized) {
|
||||
if (!fileName) {
|
||||
return undefined
|
||||
}
|
||||
const normalized = fileName.trim()
|
||||
if (!normalized.includes('.')) {
|
||||
return undefined
|
||||
}
|
||||
@@ -208,7 +182,6 @@ export function DownloadItem({ download }: DownloadItemProps) {
|
||||
? actionsContainerBaseClass
|
||||
: `${actionsContainerBaseClass} sm:opacity-0 sm:group-hover:opacity-100`
|
||||
const resolvedExtension = resolveDownloadExtension(download)
|
||||
const normalizedSavedFileName = normalizeSavedFileName(download.savedFileName)
|
||||
|
||||
// Track if the file exists
|
||||
const [fileExists, setFileExists] = useState(false)
|
||||
@@ -479,10 +452,10 @@ export function DownloadItem({ download }: DownloadItemProps) {
|
||||
})
|
||||
}
|
||||
|
||||
if (normalizedSavedFileName || download.savedFileName) {
|
||||
if (download.savedFileName) {
|
||||
metadataDetails.push({
|
||||
label: t('download.metadata.savedFile'),
|
||||
value: normalizedSavedFileName ?? download.savedFileName
|
||||
value: download.savedFileName
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Badge } from '@renderer/components/ui/badge'
|
||||
import { Button } from '@renderer/components/ui/button'
|
||||
import {
|
||||
Dialog,
|
||||
@@ -13,7 +14,7 @@ import { Switch } from '@renderer/components/ui/switch'
|
||||
import { ipcServices } from '@renderer/lib/ipc'
|
||||
import { settingsAtom } from '@renderer/store/settings'
|
||||
import { resolveFeedAtom } from '@renderer/store/subscriptions'
|
||||
import type { SubscriptionRule } from '@shared/types'
|
||||
import type { SubscriptionResolvedFeed, SubscriptionRule } from '@shared/types'
|
||||
import { useAtom, useSetAtom } from 'jotai'
|
||||
import { ChevronRight } from 'lucide-react'
|
||||
import { useEffect, useId, useRef, useState } from 'react'
|
||||
@@ -66,6 +67,7 @@ export function SubscriptionFormDialog({
|
||||
const [namingTemplate, setNamingTemplate] = useState('')
|
||||
|
||||
// Feed detection state
|
||||
const [detectedFeed, setDetectedFeed] = useState<SubscriptionResolvedFeed | null>(null)
|
||||
const [detectingFeed, setDetectingFeed] = useState(false)
|
||||
|
||||
const detectTimeout = useRef<NodeJS.Timeout | null>(null)
|
||||
@@ -94,6 +96,7 @@ export function SubscriptionFormDialog({
|
||||
setDownloadDirectory(settings.downloadPath)
|
||||
setNamingTemplate(settings.subscriptionFilenameTemplate)
|
||||
}
|
||||
setDetectedFeed(null)
|
||||
}, [
|
||||
open,
|
||||
mode,
|
||||
@@ -134,11 +137,13 @@ export function SubscriptionFormDialog({
|
||||
// Feed detection logic
|
||||
useEffect(() => {
|
||||
if (!url.trim()) {
|
||||
setDetectedFeed(null)
|
||||
return
|
||||
}
|
||||
|
||||
// In edit mode, don't detect if URL hasn't changed
|
||||
if (mode === 'edit' && subscription && url.trim() === subscription.feedUrl) {
|
||||
setDetectedFeed(null)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -149,9 +154,11 @@ export function SubscriptionFormDialog({
|
||||
detectTimeout.current = setTimeout(async () => {
|
||||
setDetectingFeed(true)
|
||||
try {
|
||||
await resolveFeed(url.trim())
|
||||
const result = await resolveFeed(url.trim())
|
||||
setDetectedFeed(result)
|
||||
} catch (error) {
|
||||
console.error('Failed to resolve feed:', error)
|
||||
setDetectedFeed(null)
|
||||
} finally {
|
||||
setDetectingFeed(false)
|
||||
}
|
||||
@@ -245,6 +252,14 @@ export function SubscriptionFormDialog({
|
||||
placeholder={t('subscriptions.placeholders.url')}
|
||||
onChange={(event) => setUrl(event.target.value)}
|
||||
/>
|
||||
{detectedFeed && (
|
||||
<Badge variant="outline" className="w-fit text-xs">
|
||||
{t('subscriptions.detectedFeed', {
|
||||
platform: detectedFeed.platform,
|
||||
feed: detectedFeed.feedUrl
|
||||
})}
|
||||
</Badge>
|
||||
)}
|
||||
{detectingFeed && (
|
||||
<p className="text-xs text-muted-foreground">{t('subscriptions.detecting')}</p>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user