mirror of
https://github.com/neosubhamoy/neodlp.git
synced 2025-12-19 23:39:33 +05:30
(feat): added global video/audio file format selection settings and other minor improvements
This commit is contained in:
21
src/App.tsx
21
src/App.tsx
@@ -57,6 +57,8 @@ export default function App({ children }: { children: React.ReactNode }) {
|
|||||||
const PREFER_VIDEO_OVER_PLAYLIST = useSettingsPageStatesStore(state => state.settings.prefer_video_over_playlist);
|
const PREFER_VIDEO_OVER_PLAYLIST = useSettingsPageStatesStore(state => state.settings.prefer_video_over_playlist);
|
||||||
const USE_PROXY = useSettingsPageStatesStore(state => state.settings.use_proxy);
|
const USE_PROXY = useSettingsPageStatesStore(state => state.settings.use_proxy);
|
||||||
const PROXY_URL = useSettingsPageStatesStore(state => state.settings.proxy_url);
|
const PROXY_URL = useSettingsPageStatesStore(state => state.settings.proxy_url);
|
||||||
|
const VIDEO_FORMAT = useSettingsPageStatesStore(state => state.settings.video_format);
|
||||||
|
const AUDIO_FORMAT = useSettingsPageStatesStore(state => state.settings.audio_format);
|
||||||
|
|
||||||
const appWindow = getCurrentWebviewWindow()
|
const appWindow = getCurrentWebviewWindow()
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -144,6 +146,13 @@ export default function App({ children }: { children: React.ReactNode }) {
|
|||||||
console.log('Video Metadata:', videoMetadata);
|
console.log('Video Metadata:', videoMetadata);
|
||||||
videoMetadata = isPlaylist ? videoMetadata.entries[0] : videoMetadata;
|
videoMetadata = isPlaylist ? videoMetadata.entries[0] : videoMetadata;
|
||||||
|
|
||||||
|
const fileType = determineFileType(videoMetadata.vcodec, videoMetadata.acodec);
|
||||||
|
|
||||||
|
if (fileType !== 'unknown' && (VIDEO_FORMAT !== 'auto' || AUDIO_FORMAT !== 'auto')) {
|
||||||
|
if (VIDEO_FORMAT !== 'auto' && (fileType === 'video+audio' || fileType === 'video')) videoMetadata.ext = VIDEO_FORMAT;
|
||||||
|
if (AUDIO_FORMAT !== 'auto' && fileType === 'audio') videoMetadata.ext = AUDIO_FORMAT;
|
||||||
|
}
|
||||||
|
|
||||||
const videoId = resumeState?.video_id || generateVideoId(videoMetadata.id, videoMetadata.webpage_url_domain);
|
const videoId = resumeState?.video_id || generateVideoId(videoMetadata.id, videoMetadata.webpage_url_domain);
|
||||||
const playlistId = isPlaylist ? (resumeState?.playlist_id || generateVideoId(videoMetadata.playlist_id, videoMetadata.webpage_url_domain)) : null;
|
const playlistId = isPlaylist ? (resumeState?.playlist_id || generateVideoId(videoMetadata.playlist_id, videoMetadata.webpage_url_domain)) : null;
|
||||||
const downloadId = resumeState?.download_id || generateDownloadId(videoMetadata.id, videoMetadata.webpage_url_domain);
|
const downloadId = resumeState?.download_id || generateDownloadId(videoMetadata.id, videoMetadata.webpage_url_domain);
|
||||||
@@ -173,6 +182,18 @@ export default function App({ children }: { children: React.ReactNode }) {
|
|||||||
args.push('--playlist-items', playlistIndex);
|
args.push('--playlist-items', playlistIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fileType !== 'unknown' && (VIDEO_FORMAT !== 'auto' || AUDIO_FORMAT !== 'auto')) {
|
||||||
|
if (VIDEO_FORMAT !== 'auto' && fileType === 'video+audio') {
|
||||||
|
args.push('--merge-output-format', VIDEO_FORMAT);
|
||||||
|
}
|
||||||
|
if (VIDEO_FORMAT !== 'auto' && fileType === 'video') {
|
||||||
|
args.push('--remux-video', VIDEO_FORMAT);
|
||||||
|
}
|
||||||
|
if (AUDIO_FORMAT !== 'auto' && fileType === 'audio') {
|
||||||
|
args.push('--extract-audio', '--audio-format', AUDIO_FORMAT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (resumeState) {
|
if (resumeState) {
|
||||||
args.push('--continue');
|
args.push('--continue');
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { Input } from "@/components/ui/input";
|
|||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { useToast } from "@/hooks/use-toast";
|
import { useToast } from "@/hooks/use-toast";
|
||||||
import { useAppContext } from "@/providers/appContextProvider";
|
import { useAppContext } from "@/providers/appContextProvider";
|
||||||
import { useCurrentVideoMetadataStore, useDownloaderPageStatesStore } from "@/services/store";
|
import { useCurrentVideoMetadataStore, useDownloaderPageStatesStore, useSettingsPageStatesStore } from "@/services/store";
|
||||||
import { determineFileType, fileFormatFilter, formatBitrate, formatDurationString, formatFileSize, formatReleaseDate, formatYtStyleCount, isObjEmpty, sortByBitrate } from "@/utils";
|
import { determineFileType, fileFormatFilter, formatBitrate, formatDurationString, formatFileSize, formatReleaseDate, formatYtStyleCount, isObjEmpty, sortByBitrate } from "@/utils";
|
||||||
import { Calendar, Clock, DownloadCloud, Eye, Info, Loader2, Music, ThumbsUp, Video, File, ListVideo, PackageSearch } from "lucide-react";
|
import { Calendar, Clock, DownloadCloud, Eye, Info, Loader2, Music, ThumbsUp, Video, File, ListVideo, PackageSearch } from "lucide-react";
|
||||||
import { FormatSelectionGroup, FormatSelectionGroupItem } from "@/components/custom/formatSelectionGroup";
|
import { FormatSelectionGroup, FormatSelectionGroupItem } from "@/components/custom/formatSelectionGroup";
|
||||||
@@ -51,6 +51,9 @@ export default function DownloaderPage() {
|
|||||||
const setSelectedSubtitles = useDownloaderPageStatesStore((state) => state.setSelectedSubtitles);
|
const setSelectedSubtitles = useDownloaderPageStatesStore((state) => state.setSelectedSubtitles);
|
||||||
const setSelectedPlaylistVideoIndex = useDownloaderPageStatesStore((state) => state.setSelectedPlaylistVideoIndex);
|
const setSelectedPlaylistVideoIndex = useDownloaderPageStatesStore((state) => state.setSelectedPlaylistVideoIndex);
|
||||||
|
|
||||||
|
const videoFormat = useSettingsPageStatesStore(state => state.settings.video_format);
|
||||||
|
const audioFormat = useSettingsPageStatesStore(state => state.settings.audio_format);
|
||||||
|
|
||||||
const audioOnlyFormats = videoMetadata?._type === 'video' ? sortByBitrate(videoMetadata?.formats.filter(fileFormatFilter('audio'))) : videoMetadata?._type === 'playlist' ? sortByBitrate(videoMetadata?.entries[Number(selectedPlaylistVideoIndex) - 1].formats.filter(fileFormatFilter('audio'))) : [];
|
const audioOnlyFormats = videoMetadata?._type === 'video' ? sortByBitrate(videoMetadata?.formats.filter(fileFormatFilter('audio'))) : videoMetadata?._type === 'playlist' ? sortByBitrate(videoMetadata?.entries[Number(selectedPlaylistVideoIndex) - 1].formats.filter(fileFormatFilter('audio'))) : [];
|
||||||
const videoOnlyFormats = videoMetadata?._type === 'video' ? sortByBitrate(videoMetadata?.formats.filter(fileFormatFilter('video'))) : videoMetadata?._type === 'playlist' ? sortByBitrate(videoMetadata?.entries[Number(selectedPlaylistVideoIndex) - 1].formats.filter(fileFormatFilter('video'))) : [];
|
const videoOnlyFormats = videoMetadata?._type === 'video' ? sortByBitrate(videoMetadata?.formats.filter(fileFormatFilter('video'))) : videoMetadata?._type === 'playlist' ? sortByBitrate(videoMetadata?.entries[Number(selectedPlaylistVideoIndex) - 1].formats.filter(fileFormatFilter('video'))) : [];
|
||||||
const combinedFormats = videoMetadata?._type === 'video' ? sortByBitrate(videoMetadata?.formats.filter(fileFormatFilter('video+audio'))) : videoMetadata?._type === 'playlist' ? sortByBitrate(videoMetadata?.entries[Number(selectedPlaylistVideoIndex) - 1].formats.filter(fileFormatFilter('video+audio'))) : [];
|
const combinedFormats = videoMetadata?._type === 'video' ? sortByBitrate(videoMetadata?.formats.filter(fileFormatFilter('video+audio'))) : videoMetadata?._type === 'playlist' ? sortByBitrate(videoMetadata?.entries[Number(selectedPlaylistVideoIndex) - 1].formats.filter(fileFormatFilter('video+audio'))) : [];
|
||||||
@@ -105,6 +108,17 @@ export default function DownloaderPage() {
|
|||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const bottomBarRef = useRef<HTMLDivElement>(null);
|
const bottomBarRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
let selectedFormatExtensionMsg = 'Auto - unknown';
|
||||||
|
if (selectedFormat?.ext) {
|
||||||
|
if ((selectedFormatFileType === 'video+audio' || selectedFormatFileType === 'video') && videoFormat !== 'auto') {
|
||||||
|
selectedFormatExtensionMsg = `Forced - ${videoFormat.toUpperCase()}`;
|
||||||
|
} else if (selectedFormatFileType === 'audio' && audioFormat !== 'auto') {
|
||||||
|
selectedFormatExtensionMsg = `Forced - ${audioFormat.toUpperCase()}`;
|
||||||
|
} else {
|
||||||
|
selectedFormatExtensionMsg = `Auto - ${selectedFormat.ext.toUpperCase()}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const searchForm = useForm<z.infer<typeof searchFormSchema>>({
|
const searchForm = useForm<z.infer<typeof searchFormSchema>>({
|
||||||
resolver: zodResolver(searchFormSchema),
|
resolver: zodResolver(searchFormSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
@@ -596,7 +610,7 @@ export default function DownloaderPage() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<span className="text-sm text-nowrap max-w-[30rem] xl:max-w-[50rem] overflow-hidden text-ellipsis">{videoMetadata._type === 'video' ? videoMetadata.title : videoMetadata._type === 'playlist' ? videoMetadata.entries[Number(selectedPlaylistVideoIndex) - 1].title : 'Unknown' }</span>
|
<span className="text-sm text-nowrap max-w-[30rem] xl:max-w-[50rem] overflow-hidden text-ellipsis">{videoMetadata._type === 'video' ? videoMetadata.title : videoMetadata._type === 'playlist' ? videoMetadata.entries[Number(selectedPlaylistVideoIndex) - 1].title : 'Unknown' }</span>
|
||||||
<span className="text-xs text-muted-foreground">{selectedFormat?.ext ? selectedFormat.ext.toUpperCase() : 'unknown'} ({selectedFormat?.resolution ? selectedFormat.resolution : 'unknown'}) {selectedFormat?.dynamic_range && selectedFormat.dynamic_range !== 'SDR' ? selectedFormat.dynamic_range : null } {selectedSubtitles.length > 0 ? `• ESUB` : null} • {selectedFormat?.filesize_approx ? formatFileSize(selectedFormat?.filesize_approx) : 'unknown filesize'}</span>
|
<span className="text-xs text-muted-foreground">{selectedFormatExtensionMsg} ({selectedFormat?.resolution ? selectedFormat.resolution : 'unknown'}) {selectedFormat?.dynamic_range && selectedFormat.dynamic_range !== 'SDR' ? selectedFormat.dynamic_range : null } {selectedSubtitles.length > 0 ? `• ESUB` : null} • {selectedFormat?.filesize_approx ? formatFileSize(selectedFormat?.filesize_approx) : 'unknown filesize'}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { Switch } from "@/components/ui/switch";
|
|||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { useToast } from "@/hooks/use-toast";
|
import { useToast } from "@/hooks/use-toast";
|
||||||
import { ArrowDownToLine, ArrowRight, EthernetPort, ExternalLink, Folder, FolderOpen, Loader2, LucideIcon, Monitor, Moon, Radio, RotateCcw, RotateCw, Sun, Terminal, WandSparkles, Wifi, Wrench } from "lucide-react";
|
import { ArrowDownToLine, ArrowRight, EthernetPort, ExternalLink, FileVideo, Folder, FolderOpen, Loader2, LucideIcon, Monitor, Moon, Radio, RotateCcw, RotateCw, Sun, Terminal, WandSparkles, Wifi, Wrench } from "lucide-react";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useTheme } from "@/providers/themeProvider";
|
import { useTheme } from "@/providers/themeProvider";
|
||||||
@@ -23,6 +23,7 @@ import { Form, FormControl, FormField, FormItem, FormMessage } from "@/component
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog";
|
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog";
|
||||||
import { SlidingButton } from "@/components/custom/slidingButton";
|
import { SlidingButton } from "@/components/custom/slidingButton";
|
||||||
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||||
|
|
||||||
const websocketPortSchema = z.object({
|
const websocketPortSchema = z.object({
|
||||||
port: z.coerce.number({
|
port: z.coerce.number({
|
||||||
@@ -44,7 +45,11 @@ export default function SettingsPage() {
|
|||||||
const { setTheme } = useTheme();
|
const { setTheme } = useTheme();
|
||||||
|
|
||||||
const activeTab = useSettingsPageStatesStore(state => state.activeTab);
|
const activeTab = useSettingsPageStatesStore(state => state.activeTab);
|
||||||
|
const activeSubAppTab = useSettingsPageStatesStore(state => state.activeSubAppTab);
|
||||||
|
const activeSubExtTab = useSettingsPageStatesStore(state => state.activeSubExtTab);
|
||||||
const setActiveTab = useSettingsPageStatesStore(state => state.setActiveTab);
|
const setActiveTab = useSettingsPageStatesStore(state => state.setActiveTab);
|
||||||
|
const setActiveSubAppTab = useSettingsPageStatesStore(state => state.setActiveSubAppTab);
|
||||||
|
const setActiveSubExtTab = useSettingsPageStatesStore(state => state.setActiveSubExtTab);
|
||||||
|
|
||||||
const isUsingDefaultSettings = useSettingsPageStatesStore(state => state.isUsingDefaultSettings);
|
const isUsingDefaultSettings = useSettingsPageStatesStore(state => state.isUsingDefaultSettings);
|
||||||
const ytDlpVersion = useSettingsPageStatesStore(state => state.ytDlpVersion);
|
const ytDlpVersion = useSettingsPageStatesStore(state => state.ytDlpVersion);
|
||||||
@@ -57,6 +62,8 @@ export default function SettingsPage() {
|
|||||||
const preferVideoOverPlaylist = useSettingsPageStatesStore(state => state.settings.prefer_video_over_playlist);
|
const preferVideoOverPlaylist = useSettingsPageStatesStore(state => state.settings.prefer_video_over_playlist);
|
||||||
const useProxy = useSettingsPageStatesStore(state => state.settings.use_proxy);
|
const useProxy = useSettingsPageStatesStore(state => state.settings.use_proxy);
|
||||||
const proxyUrl = useSettingsPageStatesStore(state => state.settings.proxy_url);
|
const proxyUrl = useSettingsPageStatesStore(state => state.settings.proxy_url);
|
||||||
|
const videoFormat = useSettingsPageStatesStore(state => state.settings.video_format);
|
||||||
|
const audioFormat = useSettingsPageStatesStore(state => state.settings.audio_format);
|
||||||
const websocketPort = useSettingsPageStatesStore(state => state.settings.websocket_port);
|
const websocketPort = useSettingsPageStatesStore(state => state.settings.websocket_port);
|
||||||
const isChangingWebSocketPort = useSettingsPageStatesStore(state => state.isChangingWebSocketPort);
|
const isChangingWebSocketPort = useSettingsPageStatesStore(state => state.isChangingWebSocketPort);
|
||||||
const setIsChangingWebSocketPort = useSettingsPageStatesStore(state => state.setIsChangingWebSocketPort);
|
const setIsChangingWebSocketPort = useSettingsPageStatesStore(state => state.setIsChangingWebSocketPort);
|
||||||
@@ -269,9 +276,10 @@ export default function SettingsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<Tabs
|
<Tabs
|
||||||
orientation="vertical"
|
|
||||||
defaultValue="general"
|
|
||||||
className="w-full flex flex-row items-start gap-4 mt-10"
|
className="w-full flex flex-row items-start gap-4 mt-10"
|
||||||
|
orientation="vertical"
|
||||||
|
value={activeSubAppTab}
|
||||||
|
onValueChange={setActiveSubAppTab}
|
||||||
>
|
>
|
||||||
<TabsList className="shrink-0 grid grid-cols-1 gap-1 p-0 bg-background min-w-45">
|
<TabsList className="shrink-0 grid grid-cols-1 gap-1 p-0 bg-background min-w-45">
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
@@ -289,6 +297,11 @@ export default function SettingsPage() {
|
|||||||
value="folders"
|
value="folders"
|
||||||
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground justify-start px-3 py-1.5 gap-2"
|
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground justify-start px-3 py-1.5 gap-2"
|
||||||
><Folder className="size-4" /> Folders</TabsTrigger>
|
><Folder className="size-4" /> Folders</TabsTrigger>
|
||||||
|
<TabsTrigger
|
||||||
|
key="formats"
|
||||||
|
value="formats"
|
||||||
|
className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground justify-start px-3 py-1.5 gap-2"
|
||||||
|
><FileVideo className="size-4" /> Formats</TabsTrigger>
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
key="network"
|
key="network"
|
||||||
value="network"
|
value="network"
|
||||||
@@ -296,7 +309,7 @@ export default function SettingsPage() {
|
|||||||
><Wifi className="size-4" /> Network</TabsTrigger>
|
><Wifi className="size-4" /> Network</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<div className="min-h-full flex flex-col max-w-[55%] w-full border-l border-border pl-4">
|
<div className="min-h-full flex flex-col max-w-[55%] w-full border-l border-border pl-4">
|
||||||
<TabsContent key="general" value="general" className="flex flex-col gap-4 min-h-[150px]">
|
<TabsContent key="general" value="general" className="flex flex-col gap-4 min-h-[190px]">
|
||||||
<div className="max-parallel-downloads">
|
<div className="max-parallel-downloads">
|
||||||
<h3 className="font-semibold">Max Parallel Downloads</h3>
|
<h3 className="font-semibold">Max Parallel Downloads</h3>
|
||||||
<p className="text-xs text-muted-foreground mb-3">Set maximum number of allowed parallel downloads</p>
|
<p className="text-xs text-muted-foreground mb-3">Set maximum number of allowed parallel downloads</p>
|
||||||
@@ -320,7 +333,7 @@ export default function SettingsPage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent key="appearance" value="appearance" className="flex flex-col gap-4 min-h-[150px]">
|
<TabsContent key="appearance" value="appearance" className="flex flex-col gap-4 min-h-[190px]">
|
||||||
<div className="app-theme">
|
<div className="app-theme">
|
||||||
<h3 className="font-semibold">Theme</h3>
|
<h3 className="font-semibold">Theme</h3>
|
||||||
<p className="text-xs text-muted-foreground mb-3">Choose app interface theme</p>
|
<p className="text-xs text-muted-foreground mb-3">Choose app interface theme</p>
|
||||||
@@ -343,7 +356,7 @@ export default function SettingsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent key="folders" value="folders" className="flex flex-col gap-4 min-h-[150px]">
|
<TabsContent key="folders" value="folders" className="flex flex-col gap-4 min-h-[190px]">
|
||||||
<div className="download-dir">
|
<div className="download-dir">
|
||||||
<h3 className="font-semibold">Download Folder</h3>
|
<h3 className="font-semibold">Download Folder</h3>
|
||||||
<p className="text-xs text-muted-foreground mb-3">Set default download folder (directory)</p>
|
<p className="text-xs text-muted-foreground mb-3">Set default download folder (directory)</p>
|
||||||
@@ -376,7 +389,63 @@ export default function SettingsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent key="network" value="network" className="flex flex-col gap-4 min-h-[150px]">
|
<TabsContent key="formats" value="formats" className="flex flex-col gap-4 min-h-[190px]">
|
||||||
|
<div className="video-format">
|
||||||
|
<h3 className="font-semibold">Video Format</h3>
|
||||||
|
<p className="text-xs text-muted-foreground mb-3">Choose in which format the final video file will be saved</p>
|
||||||
|
<RadioGroup
|
||||||
|
orientation="horizontal"
|
||||||
|
className="flex items-center gap-4"
|
||||||
|
value={videoFormat}
|
||||||
|
onValueChange={(value) => saveSettingsKey('video_format', value)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<RadioGroupItem value="auto" id="v-auto" />
|
||||||
|
<Label htmlFor="v-auto">Auto (Default)</Label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<RadioGroupItem value="mp4" id="v-mp4" />
|
||||||
|
<Label htmlFor="v-mp4">MP4</Label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<RadioGroupItem value="webm" id="v-webm" />
|
||||||
|
<Label htmlFor="v-webm">WEBM</Label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<RadioGroupItem value="mkv" id="v-mkv" />
|
||||||
|
<Label htmlFor="v-mkv">MKV</Label>
|
||||||
|
</div>
|
||||||
|
</RadioGroup>
|
||||||
|
</div>
|
||||||
|
<div className="audio-format">
|
||||||
|
<h3 className="font-semibold">Audio Format</h3>
|
||||||
|
<p className="text-xs text-muted-foreground mb-3">Choose in which format the final audio file will be saved</p>
|
||||||
|
<RadioGroup
|
||||||
|
orientation="horizontal"
|
||||||
|
className="flex items-center gap-4"
|
||||||
|
value={audioFormat}
|
||||||
|
onValueChange={(value) => saveSettingsKey('audio_format', value)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<RadioGroupItem value="auto" id="a-auto" />
|
||||||
|
<Label htmlFor="a-auto">Auto (Default)</Label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<RadioGroupItem value="m4a" id="a-m4a" />
|
||||||
|
<Label htmlFor="a-m4a">M4A</Label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<RadioGroupItem value="opus" id="a-opus" />
|
||||||
|
<Label htmlFor="a-opus">OPUS</Label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<RadioGroupItem value="mp3" id="a-mp3" />
|
||||||
|
<Label htmlFor="a-mp3">MP3</Label>
|
||||||
|
</div>
|
||||||
|
</RadioGroup>
|
||||||
|
</div>
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent key="network" value="network" className="flex flex-col gap-4 min-h-[190px]">
|
||||||
<div className="proxy">
|
<div className="proxy">
|
||||||
<h3 className="font-semibold">Proxy</h3>
|
<h3 className="font-semibold">Proxy</h3>
|
||||||
<p className="text-xs text-muted-foreground mb-3">Use proxy for downloads, Unblocks blocked sites in your region (Download speed may affect, Some sites may not work)</p>
|
<p className="text-xs text-muted-foreground mb-3">Use proxy for downloads, Unblocks blocked sites in your region (Download speed may affect, Some sites may not work)</p>
|
||||||
@@ -474,9 +543,10 @@ export default function SettingsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<Tabs
|
<Tabs
|
||||||
orientation="vertical"
|
|
||||||
defaultValue="install"
|
|
||||||
className="w-full flex flex-row items-start gap-4 mt-10"
|
className="w-full flex flex-row items-start gap-4 mt-10"
|
||||||
|
orientation="vertical"
|
||||||
|
value={activeSubExtTab}
|
||||||
|
onValueChange={setActiveSubExtTab}
|
||||||
>
|
>
|
||||||
<TabsList className="shrink-0 grid grid-cols-1 gap-1 p-0 bg-background min-w-45">
|
<TabsList className="shrink-0 grid grid-cols-1 gap-1 p-0 bg-background min-w-45">
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
|
|||||||
@@ -94,6 +94,8 @@ export const useDownloadActionStatesStore = create<DownloadActionStatesStore>((s
|
|||||||
|
|
||||||
export const useSettingsPageStatesStore = create<SettingsPageStatesStore>((set) => ({
|
export const useSettingsPageStatesStore = create<SettingsPageStatesStore>((set) => ({
|
||||||
activeTab: 'app',
|
activeTab: 'app',
|
||||||
|
activeSubAppTab: 'general',
|
||||||
|
activeSubExtTab: 'install',
|
||||||
appVersion: null,
|
appVersion: null,
|
||||||
isFetchingAppVersion: false,
|
isFetchingAppVersion: false,
|
||||||
ytDlpVersion: null,
|
ytDlpVersion: null,
|
||||||
@@ -108,6 +110,8 @@ export const useSettingsPageStatesStore = create<SettingsPageStatesStore>((set)
|
|||||||
max_parallel_downloads: 2,
|
max_parallel_downloads: 2,
|
||||||
use_proxy: false,
|
use_proxy: false,
|
||||||
proxy_url: '',
|
proxy_url: '',
|
||||||
|
video_format: 'auto',
|
||||||
|
audio_format: 'auto',
|
||||||
websocket_port: 53511
|
websocket_port: 53511
|
||||||
},
|
},
|
||||||
isUsingDefaultSettings: true,
|
isUsingDefaultSettings: true,
|
||||||
@@ -118,6 +122,8 @@ export const useSettingsPageStatesStore = create<SettingsPageStatesStore>((set)
|
|||||||
isUpdatingApp: false,
|
isUpdatingApp: false,
|
||||||
appUpdateDownloadProgress: 0,
|
appUpdateDownloadProgress: 0,
|
||||||
setActiveTab: (tab) => set(() => ({ activeTab: tab })),
|
setActiveTab: (tab) => set(() => ({ activeTab: tab })),
|
||||||
|
setActiveSubAppTab: (tab) => set(() => ({ activeSubAppTab: tab })),
|
||||||
|
setActiveSubExtTab: (tab) => set(() => ({ activeSubExtTab: tab })),
|
||||||
setAppVersion: (version) => set(() => ({ appVersion: version })),
|
setAppVersion: (version) => set(() => ({ appVersion: version })),
|
||||||
setIsFetchingAppVersion: (isFetching) => set(() => ({ isFetchingAppVersion: isFetching })),
|
setIsFetchingAppVersion: (isFetching) => set(() => ({ isFetchingAppVersion: isFetching })),
|
||||||
setYtDlpVersion: (version) => set(() => ({ ytDlpVersion: version })),
|
setYtDlpVersion: (version) => set(() => ({ ytDlpVersion: version })),
|
||||||
@@ -140,6 +146,8 @@ export const useSettingsPageStatesStore = create<SettingsPageStatesStore>((set)
|
|||||||
max_parallel_downloads: 2,
|
max_parallel_downloads: 2,
|
||||||
use_proxy: false,
|
use_proxy: false,
|
||||||
proxy_url: '',
|
proxy_url: '',
|
||||||
|
video_format: 'auto',
|
||||||
|
audio_format: 'auto',
|
||||||
websocket_port: 53511
|
websocket_port: 53511
|
||||||
},
|
},
|
||||||
isUsingDefaultSettings: true
|
isUsingDefaultSettings: true
|
||||||
|
|||||||
@@ -12,5 +12,8 @@ export interface Settings {
|
|||||||
prefer_video_over_playlist: boolean;
|
prefer_video_over_playlist: boolean;
|
||||||
use_proxy: boolean;
|
use_proxy: boolean;
|
||||||
proxy_url: string;
|
proxy_url: string;
|
||||||
|
video_format: string;
|
||||||
|
audio_format: string;
|
||||||
|
// extension settings
|
||||||
websocket_port: number;
|
websocket_port: number;
|
||||||
}
|
}
|
||||||
@@ -58,6 +58,8 @@ export interface DownloadActionStatesStore {
|
|||||||
|
|
||||||
export interface SettingsPageStatesStore {
|
export interface SettingsPageStatesStore {
|
||||||
activeTab: string;
|
activeTab: string;
|
||||||
|
activeSubAppTab: string;
|
||||||
|
activeSubExtTab: string;
|
||||||
appVersion: string | null;
|
appVersion: string | null;
|
||||||
isFetchingAppVersion: boolean;
|
isFetchingAppVersion: boolean;
|
||||||
ytDlpVersion: string | null;
|
ytDlpVersion: string | null;
|
||||||
@@ -72,6 +74,8 @@ export interface SettingsPageStatesStore {
|
|||||||
isUpdatingApp: boolean;
|
isUpdatingApp: boolean;
|
||||||
appUpdateDownloadProgress: number;
|
appUpdateDownloadProgress: number;
|
||||||
setActiveTab: (tab: string) => void;
|
setActiveTab: (tab: string) => void;
|
||||||
|
setActiveSubAppTab: (tab: string) => void;
|
||||||
|
setActiveSubExtTab: (tab: string) => void;
|
||||||
setAppVersion: (version: string | null) => void;
|
setAppVersion: (version: string | null) => void;
|
||||||
setIsFetchingAppVersion: (isFetching: boolean) => void;
|
setIsFetchingAppVersion: (isFetching: boolean) => void;
|
||||||
setYtDlpVersion: (version: string | null) => void;
|
setYtDlpVersion: (version: string | null) => void;
|
||||||
|
|||||||
Reference in New Issue
Block a user