1
1
mirror of https://github.com/neosubhamoy/neodlp.git synced 2026-02-04 11:52:23 +05:30

feat: added support for multiple audio stream selection in combine mode

This commit is contained in:
2026-01-19 20:36:50 +05:30
Verified
parent 4e5a4a1c73
commit 04590d892d
10 changed files with 261 additions and 87 deletions

View File

@@ -58,7 +58,7 @@ const FormatSelectionGroupItem = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
"relative w-full rounded-lg border-2 border-border bg-background px-3 py-2 shadow-sm transition-all", "relative w-full rounded-lg border-2 border-border bg-background px-3 py-2 shadow-sm transition-all",
"data-[state=checked]:border-primary data-[state=checked]:border-2 data-[state=checked]:bg-primary/10", "data-[state=checked]:border-primary data-[state=checked]:bg-primary/10",
"hover:bg-muted/70", "hover:bg-muted/70",
"disabled:cursor-not-allowed disabled:opacity-50", "disabled:cursor-not-allowed disabled:opacity-50",
className className

View File

@@ -0,0 +1,114 @@
import * as React from "react";
import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group";
import { type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
import { toggleVariants } from "@/components/ui/toggle";
import { VideoFormat } from "@/types/video";
import { determineFileType, formatBitrate, formatCodec, formatFileSize } from "@/utils";
import { Music, Video, File } from "lucide-react";
const FormatToggleGroupContext = React.createContext<
VariantProps<typeof toggleVariants> & { toggleType?: "single" | "multiple" }
>({
size: "default",
variant: "default",
toggleType: "multiple",
});
type FormatToggleGroupProps =
| (Omit<React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root>, "type"> &
VariantProps<typeof toggleVariants> & { type: "single", value?: string, onValueChange?: (value: string) => void })
| (Omit<React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root>, "type"> &
VariantProps<typeof toggleVariants> & { type: "multiple", value?: string[], onValueChange?: (value: string[]) => void });
export const FormatToggleGroup = React.forwardRef<
React.ComponentRef<typeof ToggleGroupPrimitive.Root>,
FormatToggleGroupProps
>(({ className, variant, size, children, type = "multiple", ...props }, ref) => {
if (type === "single") {
return (
<ToggleGroupPrimitive.Root
ref={ref}
type="single"
className={cn("flex flex-col gap-2", className)}
{...(props as any)}
>
<FormatToggleGroupContext.Provider value={{ variant, size, toggleType: "single" }}>
{children}
</FormatToggleGroupContext.Provider>
</ToggleGroupPrimitive.Root>
);
}
return (
<ToggleGroupPrimitive.Root
ref={ref}
type="multiple"
className={cn("flex flex-col gap-2", className)}
{...(props as any)}
>
<FormatToggleGroupContext.Provider value={{ variant, size, toggleType: "multiple" }}>
{children}
</FormatToggleGroupContext.Provider>
</ToggleGroupPrimitive.Root>
);
});
FormatToggleGroup.displayName = "FormatToggleGroup";
export const FormatToggleGroupItem = React.forwardRef<
React.ComponentRef<typeof ToggleGroupPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> &
VariantProps<typeof toggleVariants> & {
format: VideoFormat
}
>(({ className, children, variant, size, format, value, ...props }, ref) => {
const determineFileTypeIcon = (format: VideoFormat) => {
const fileFormat = determineFileType(/*format.video_ext, format.audio_ext,*/ format.vcodec, format.acodec)
switch (fileFormat) {
case 'video+audio':
return (
<span className="absolute flex items-center right-2 bottom-2">
<Video className="w-3 h-3 mr-2" />
<Music className="w-3 h-3" />
</span>
)
case 'video':
return (
<Video className="w-3 h-3 absolute right-2 bottom-2" />
)
case 'audio':
return (
<Music className="w-3 h-3 absolute right-2 bottom-2" />
)
default:
return (
<File className="w-3 h-3 absolute right-2 bottom-2" />
)
}
}
return (
<ToggleGroupPrimitive.Item
ref={ref}
className={cn(
"relative flex w-full p-2 rounded-lg border-2 border-border bg-background px-3 py-2 shadow-sm transition-all",
"hover:bg-muted/70 data-[state=on]:bg-primary/10",
"data-[state=on]:border-primary",
className
)}
value={value}
{...props}
>
<div className="flex flex-col items-start text-start gap-1">
<h5 className="text-sm">{format.format}</h5>
<p className="text-muted-foreground text-xs">{format.filesize_approx ? formatFileSize(format.filesize_approx) : 'unknown'} {format.tbr ? formatBitrate(format.tbr) : 'unknown'}</p>
<p className="text-muted-foreground text-xs">{format.ext ? format.ext.toUpperCase() : 'unknown'} {
((format.vcodec && format.vcodec !== 'none') || (format.acodec && format.acodec !== 'none')) && (
`(${format.vcodec && format.vcodec !== 'none' ? formatCodec(format.vcodec) : ''}${format.vcodec && format.vcodec !== 'none' && format.acodec && format.acodec !== 'none' ? ' ' : ''}${format.acodec && format.acodec !== 'none' ? formatCodec(format.acodec) : ''})`
)}</p>
{determineFileTypeIcon(format)}
</div>
</ToggleGroupPrimitive.Item>
);
});
FormatToggleGroupItem.displayName = "FormatToggleGroupItem";

View File

@@ -24,7 +24,7 @@ interface BottomBarProps {
selectedFormat: VideoFormat | undefined; selectedFormat: VideoFormat | undefined;
selectedFormatFileType: "video+audio" | "video" | "audio" | "unknown"; selectedFormatFileType: "video+audio" | "video" | "audio" | "unknown";
selectedVideoFormat: VideoFormat | undefined; selectedVideoFormat: VideoFormat | undefined;
selectedAudioFormat: VideoFormat | undefined; selectedAudioFormats: VideoFormat[] | undefined;
containerRef: React.RefObject<HTMLDivElement | null>; containerRef: React.RefObject<HTMLDivElement | null>;
} }
@@ -33,7 +33,7 @@ function DownloadConfigDialog({ selectedFormatFileType }: DownloadConfigDialogPr
const activeDownloadConfigurationTab = useDownloaderPageStatesStore((state) => state.activeDownloadConfigurationTab); const activeDownloadConfigurationTab = useDownloaderPageStatesStore((state) => state.activeDownloadConfigurationTab);
const selectedDownloadFormat = useDownloaderPageStatesStore((state) => state.selectedDownloadFormat); const selectedDownloadFormat = useDownloaderPageStatesStore((state) => state.selectedDownloadFormat);
const selectedCombinableVideoFormat = useDownloaderPageStatesStore((state) => state.selectedCombinableVideoFormat); const selectedCombinableVideoFormat = useDownloaderPageStatesStore((state) => state.selectedCombinableVideoFormat);
const selectedCombinableAudioFormat = useDownloaderPageStatesStore((state) => state.selectedCombinableAudioFormat); const selectedCombinableAudioFormats = useDownloaderPageStatesStore((state) => state.selectedCombinableAudioFormats);
const downloadConfiguration = useDownloaderPageStatesStore((state) => state.downloadConfiguration); const downloadConfiguration = useDownloaderPageStatesStore((state) => state.downloadConfiguration);
const setActiveDownloadConfigurationTab = useDownloaderPageStatesStore((state) => state.setActiveDownloadConfigurationTab); const setActiveDownloadConfigurationTab = useDownloaderPageStatesStore((state) => state.setActiveDownloadConfigurationTab);
const setDownloadConfigurationKey = useDownloaderPageStatesStore((state) => state.setDownloadConfigurationKey); const setDownloadConfigurationKey = useDownloaderPageStatesStore((state) => state.setDownloadConfigurationKey);
@@ -45,6 +45,8 @@ function DownloadConfigDialog({ selectedFormatFileType }: DownloadConfigDialogPr
const useCustomCommands = useSettingsPageStatesStore(state => state.settings.use_custom_commands); const useCustomCommands = useSettingsPageStatesStore(state => state.settings.use_custom_commands);
const customCommands = useSettingsPageStatesStore(state => state.settings.custom_commands); const customCommands = useSettingsPageStatesStore(state => state.settings.custom_commands);
const isCombineableAudioSelected = selectedCombinableAudioFormats && selectedCombinableAudioFormats.length > 0;
return ( return (
<Dialog> <Dialog>
<Tooltip> <Tooltip>
@@ -53,7 +55,7 @@ function DownloadConfigDialog({ selectedFormatFileType }: DownloadConfigDialogPr
<Button <Button
variant="outline" variant="outline"
size="icon" size="icon"
disabled={!selectedDownloadFormat || (activeDownloadModeTab === 'combine' && (!selectedCombinableVideoFormat || !selectedCombinableAudioFormat))} disabled={!selectedDownloadFormat || (activeDownloadModeTab === 'combine' && (!selectedCombinableVideoFormat || !isCombineableAudioSelected))}
> >
<Settings2 className="size-4" /> <Settings2 className="size-4" />
</Button> </Button>
@@ -272,14 +274,14 @@ function DownloadConfigDialog({ selectedFormatFileType }: DownloadConfigDialogPr
); );
} }
export function BottomBar({ videoMetadata, selectedFormat, selectedFormatFileType, selectedVideoFormat, selectedAudioFormat, containerRef }: BottomBarProps) { export function BottomBar({ videoMetadata, selectedFormat, selectedFormatFileType, selectedVideoFormat, selectedAudioFormats, containerRef }: BottomBarProps) {
const { startDownload } = useAppContext(); const { startDownload } = useAppContext();
console.log(selectedAudioFormats);
const activeDownloadModeTab = useDownloaderPageStatesStore((state) => state.activeDownloadModeTab); const activeDownloadModeTab = useDownloaderPageStatesStore((state) => state.activeDownloadModeTab);
const isStartingDownload = useDownloaderPageStatesStore((state) => state.isStartingDownload); const isStartingDownload = useDownloaderPageStatesStore((state) => state.isStartingDownload);
const selectedDownloadFormat = useDownloaderPageStatesStore((state) => state.selectedDownloadFormat); const selectedDownloadFormat = useDownloaderPageStatesStore((state) => state.selectedDownloadFormat);
const selectedCombinableVideoFormat = useDownloaderPageStatesStore((state) => state.selectedCombinableVideoFormat); const selectedCombinableVideoFormat = useDownloaderPageStatesStore((state) => state.selectedCombinableVideoFormat);
const selectedCombinableAudioFormat = useDownloaderPageStatesStore((state) => state.selectedCombinableAudioFormat); const selectedCombinableAudioFormats = useDownloaderPageStatesStore((state) => state.selectedCombinableAudioFormats);
const selectedSubtitles = useDownloaderPageStatesStore((state) => state.selectedSubtitles); const selectedSubtitles = useDownloaderPageStatesStore((state) => state.selectedSubtitles);
const selectedPlaylistVideos = useDownloaderPageStatesStore((state) => state.selectedPlaylistVideos); const selectedPlaylistVideos = useDownloaderPageStatesStore((state) => state.selectedPlaylistVideos);
const downloadConfiguration = useDownloaderPageStatesStore((state) => state.downloadConfiguration); const downloadConfiguration = useDownloaderPageStatesStore((state) => state.downloadConfiguration);
@@ -295,17 +297,21 @@ export function BottomBar({ videoMetadata, selectedFormat, selectedFormatFileTyp
const isPlaylist = videoMetadata._type === 'playlist'; const isPlaylist = videoMetadata._type === 'playlist';
const isMultiplePlaylistItems = isPlaylist && selectedPlaylistVideos.length > 1; const isMultiplePlaylistItems = isPlaylist && selectedPlaylistVideos.length > 1;
const isCombineableAudioSelected = selectedCombinableAudioFormats && selectedCombinableAudioFormats.length > 0 && selectedAudioFormats && selectedAudioFormats.length > 0;
const isMultipleCombineableAudioSelected = selectedCombinableAudioFormats && selectedCombinableAudioFormats.length > 1 && selectedAudioFormats && selectedAudioFormats.length > 1;
let selectedFormatExtensionMsg = 'Auto - unknown'; let selectedFormatExtensionMsg = 'Auto - unknown';
if (activeDownloadModeTab === 'combine') { if (activeDownloadModeTab === 'combine') {
if (downloadConfiguration.output_format && downloadConfiguration.output_format !== 'auto') { if (downloadConfiguration.output_format && downloadConfiguration.output_format !== 'auto') {
selectedFormatExtensionMsg = `Combined - ${downloadConfiguration.output_format.toUpperCase()}`; selectedFormatExtensionMsg = `Combined - ${downloadConfiguration.output_format.toUpperCase()}`;
} } else if (videoFormat !== 'auto') {
else if (videoFormat !== 'auto') {
selectedFormatExtensionMsg = `Combined - ${videoFormat.toUpperCase()}`; selectedFormatExtensionMsg = `Combined - ${videoFormat.toUpperCase()}`;
} } else if (isCombineableAudioSelected && selectedVideoFormat?.ext) {
else if (selectedAudioFormat?.ext && selectedVideoFormat?.ext) { if (isMultipleCombineableAudioSelected) {
selectedFormatExtensionMsg = `Combined - ${selectedVideoFormat.ext.toUpperCase()} + ${selectedAudioFormat.ext.toUpperCase()}`; selectedFormatExtensionMsg = `Combined - ${selectedVideoFormat.ext.toUpperCase()} + ${selectedAudioFormats.length} Audio`;
} else {
selectedFormatExtensionMsg = `Combined - ${selectedVideoFormat.ext.toUpperCase()} + ${selectedAudioFormats[0].ext.toUpperCase()}`;
}
} else { } else {
selectedFormatExtensionMsg = `Combined - unknown`; selectedFormatExtensionMsg = `Combined - unknown`;
} }
@@ -322,9 +328,23 @@ export function BottomBar({ videoMetadata, selectedFormat, selectedFormatFileTyp
} }
let selectedFormatResolutionMsg = 'unknown'; let selectedFormatResolutionMsg = 'unknown';
let totalTbr = 0;
if (activeDownloadModeTab === 'combine') { if (activeDownloadModeTab === 'combine') {
selectedFormatResolutionMsg = `${selectedVideoFormat?.resolution ?? 'unknown'} + ${selectedAudioFormat?.tbr ? formatBitrate(selectedAudioFormat.tbr) : 'unknown'}`; if (isCombineableAudioSelected) {
if (isMultipleCombineableAudioSelected) {
const totalAudioTbr = selectedAudioFormats.reduce((acc, format) => acc + (format.tbr ?? 0), 0);
totalTbr = (selectedVideoFormat?.tbr ?? 0) + totalAudioTbr;
selectedFormatResolutionMsg = `${selectedVideoFormat?.resolution ?? 'unknown'} + ${formatBitrate(totalAudioTbr)}`;
} else {
totalTbr = (selectedVideoFormat?.tbr ?? 0) + (selectedAudioFormats && selectedAudioFormats[0].tbr ? selectedAudioFormats[0].tbr : 0);
selectedFormatResolutionMsg = `${selectedVideoFormat?.resolution ?? 'unknown'} + ${selectedAudioFormats && selectedAudioFormats[0].tbr ? formatBitrate(selectedAudioFormats[0].tbr) : 'unknown'}`;
}
} else {
totalTbr = selectedVideoFormat?.tbr ?? 0;
selectedFormatResolutionMsg = `${selectedVideoFormat?.resolution ?? 'unknown'} + unknown`;
}
} else if (selectedFormat?.resolution) { } else if (selectedFormat?.resolution) {
totalTbr = selectedFormat.tbr ?? 0;
selectedFormatResolutionMsg = selectedFormat.resolution; selectedFormatResolutionMsg = selectedFormat.resolution;
} }
@@ -336,24 +356,37 @@ export function BottomBar({ videoMetadata, selectedFormat, selectedFormatFileTyp
} }
let selectedFormatFileSizeMsg = 'unknown filesize'; let selectedFormatFileSizeMsg = 'unknown filesize';
let totalFilesize = 0;
if (activeDownloadModeTab === 'combine') { if (activeDownloadModeTab === 'combine') {
selectedFormatFileSizeMsg = selectedVideoFormat?.filesize_approx && selectedAudioFormat?.filesize_approx ? formatFileSize(selectedVideoFormat.filesize_approx + selectedAudioFormat.filesize_approx) : 'unknown filesize'; if (isCombineableAudioSelected) {
if (isMultipleCombineableAudioSelected) {
totalFilesize = (selectedVideoFormat?.filesize_approx ?? 0) + selectedAudioFormats.reduce((acc, format) => acc + (format.filesize_approx ?? 0), 0);
selectedFormatFileSizeMsg = totalFilesize > 0 ? formatFileSize(totalFilesize) : 'unknown filesize';
} else {
totalFilesize = (selectedVideoFormat?.filesize_approx ?? 0) + (selectedAudioFormats && selectedAudioFormats[0].filesize_approx ? selectedAudioFormats[0].filesize_approx : 0);
selectedFormatFileSizeMsg = (selectedVideoFormat?.filesize_approx && selectedAudioFormats && selectedAudioFormats[0].filesize_approx) ? formatFileSize(selectedVideoFormat.filesize_approx + selectedAudioFormats[0].filesize_approx) : 'unknown filesize';
}
} else {
totalFilesize = selectedVideoFormat?.filesize_approx ?? 0;
selectedFormatFileSizeMsg = selectedVideoFormat?.filesize_approx ? formatFileSize(selectedVideoFormat.filesize_approx) : 'unknown filesize';
}
} else if (selectedFormat?.filesize_approx) { } else if (selectedFormat?.filesize_approx) {
totalFilesize = selectedFormat.filesize_approx;
selectedFormatFileSizeMsg = formatFileSize(selectedFormat.filesize_approx); selectedFormatFileSizeMsg = formatFileSize(selectedFormat.filesize_approx);
} }
let selectedFormatFinalMsg = ''; let selectedFormatFinalMsg = '';
if (activeDownloadModeTab === 'combine') { if (activeDownloadModeTab === 'combine') {
if (selectedCombinableVideoFormat && selectedCombinableAudioFormat) { if (selectedCombinableVideoFormat && selectedCombinableAudioFormats.length > 0) {
selectedFormatFinalMsg = `${selectedFormatExtensionMsg} (${selectedFormatResolutionMsg}) ${selectedFormatDynamicRangeMsg} ${selectedSubtitles.length > 0 ? `• ESUB` : ''} ${useSponsorblock || (downloadConfiguration.sponsorblock && downloadConfiguration.sponsorblock !== 'auto') ? `• SPBLOCK` : ''} • ${selectedFormatFileSizeMsg}`; selectedFormatFinalMsg = `${selectedFormatExtensionMsg} (${selectedFormatResolutionMsg}) ${selectedFormatDynamicRangeMsg} ${selectedSubtitles.length > 0 ? `• ESUB` : ''} ${useSponsorblock || (downloadConfiguration.sponsorblock && downloadConfiguration.sponsorblock !== 'auto') ? `• SPBLOCK` : ''} • ${selectedFormatFileSizeMsg}`;
} else { } else {
selectedFormatFinalMsg = `Choose a video and audio stream to combine`; selectedFormatFinalMsg = `Choose a video and audio streams to combine`;
} }
} else { } else {
if (selectedFormat) { if (selectedFormat) {
selectedFormatFinalMsg = `${selectedFormatExtensionMsg} (${selectedFormatResolutionMsg}) ${selectedFormatDynamicRangeMsg} ${selectedSubtitles.length > 0 ? `• ESUB` : ''} ${useSponsorblock || (downloadConfiguration.sponsorblock && downloadConfiguration.sponsorblock !== 'auto') ? `• SPBLOCK` : ''} • ${selectedFormatFileSizeMsg}`; selectedFormatFinalMsg = `${selectedFormatExtensionMsg} (${selectedFormatResolutionMsg}) ${selectedFormatDynamicRangeMsg} ${selectedSubtitles.length > 0 ? `• ESUB` : ''} ${useSponsorblock || (downloadConfiguration.sponsorblock && downloadConfiguration.sponsorblock !== 'auto') ? `• SPBLOCK` : ''} • ${selectedFormatFileSizeMsg}`;
} else { } else {
selectedFormatFinalMsg = `Choose a stream to download`; selectedFormatFinalMsg = `Select a stream to download`;
} }
} }
@@ -389,13 +422,16 @@ export function BottomBar({ videoMetadata, selectedFormat, selectedFormatFileTyp
<div className="flex justify-between items-center gap-2 fixed bottom-0 right-0 p-4 w-full bg-background rounded-t-lg border-t border-border z-20" ref={bottomBarRef}> <div className="flex justify-between items-center gap-2 fixed bottom-0 right-0 p-4 w-full bg-background rounded-t-lg border-t border-border z-20" ref={bottomBarRef}>
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<div className="flex justify-center items-center p-3 rounded-md border border-border"> <div className="flex justify-center items-center p-3 rounded-md border border-border">
{selectedFormatFileType && (selectedFormatFileType === 'video' || selectedFormatFileType === 'video+audio') && ( {activeDownloadModeTab === 'combine' && (
<Video className="w-4 h-4 stroke-primary" /> <Video className="w-4 h-4 stroke-primary" />
)} )}
{selectedFormatFileType && selectedFormatFileType === 'audio' && ( {activeDownloadModeTab !== 'combine' && selectedFormatFileType && (selectedFormatFileType === 'video' || selectedFormatFileType === 'video+audio') && (
<Video className="w-4 h-4 stroke-primary" />
)}
{activeDownloadModeTab !== 'combine' && selectedFormatFileType && selectedFormatFileType === 'audio' && (
<Music className="w-4 h-4 stroke-primary" /> <Music className="w-4 h-4 stroke-primary" />
)} )}
{(!selectedFormatFileType) || (selectedFormatFileType && selectedFormatFileType !== 'video' && selectedFormatFileType !== 'audio' && selectedFormatFileType !== 'video+audio') && ( {activeDownloadModeTab !== 'combine' && ((!selectedFormatFileType) || (selectedFormatFileType && selectedFormatFileType !== 'video' && selectedFormatFileType !== 'audio' && selectedFormatFileType !== 'video+audio')) && (
<File className="w-4 h-4 stroke-primary" /> <File className="w-4 h-4 stroke-primary" />
)} )}
</div> </div>
@@ -413,21 +449,28 @@ export function BottomBar({ videoMetadata, selectedFormat, selectedFormatFileTyp
if (videoMetadata._type === 'playlist') { if (videoMetadata._type === 'playlist') {
await startDownload({ await startDownload({
url: videoMetadata.original_url, url: videoMetadata.original_url,
selectedFormat: activeDownloadModeTab === 'combine' ? `${selectedCombinableVideoFormat}+${selectedCombinableAudioFormat}` : selectedDownloadFormat, selectedFormat: activeDownloadModeTab === 'combine' ? `${selectedCombinableVideoFormat}+${selectedCombinableAudioFormats.join('+')}` : selectedDownloadFormat,
downloadConfig: downloadConfiguration, downloadConfig: downloadConfiguration,
selectedSubtitles: selectedSubtitles.length > 0 ? selectedSubtitles.join(',') : null, selectedSubtitles: selectedSubtitles.length > 0 ? selectedSubtitles.join(',') : null,
playlistItems: selectedPlaylistVideos.sort((a, b) => Number(a) - Number(b)).join(','), playlistItems: selectedPlaylistVideos.sort((a, b) => Number(a) - Number(b)).join(','),
overrideOptions: isMultiplePlaylistItems ? { overrideOptions: isMultiplePlaylistItems ? {
filesize: activeDownloadModeTab === 'combine' ? (selectedVideoFormat?.filesize_approx && selectedAudioFormat?.filesize_approx ? selectedVideoFormat.filesize_approx + selectedAudioFormat.filesize_approx : undefined) : selectedFormat?.filesize_approx ? selectedFormat.filesize_approx : undefined, filesize: totalFilesize > 0 ? totalFilesize : undefined,
tbr: activeDownloadModeTab === 'combine' ? (selectedVideoFormat?.tbr && selectedAudioFormat?.tbr ? selectedVideoFormat.tbr + selectedAudioFormat.tbr : undefined) : selectedFormat?.tbr ? selectedFormat.tbr : undefined, tbr: totalTbr > 0 ? totalTbr : undefined,
} : isMultipleCombineableAudioSelected ? {
filesize: totalFilesize > 0 ? totalFilesize : undefined,
tbr: totalTbr > 0 ? totalTbr : undefined,
} : undefined } : undefined
}); });
} else if (videoMetadata._type === 'video') { } else if (videoMetadata._type === 'video') {
await startDownload({ await startDownload({
url: videoMetadata.webpage_url, url: videoMetadata.webpage_url,
selectedFormat: activeDownloadModeTab === 'combine' ? `${selectedCombinableVideoFormat}+${selectedCombinableAudioFormat}` : selectedDownloadFormat === 'best' ? videoMetadata.requested_downloads[0].format_id : selectedDownloadFormat, selectedFormat: activeDownloadModeTab === 'combine' ? `${selectedCombinableVideoFormat}+${selectedCombinableAudioFormats.join('+')}` : selectedDownloadFormat === 'best' ? videoMetadata.requested_downloads[0].format_id : selectedDownloadFormat,
downloadConfig: downloadConfiguration, downloadConfig: downloadConfiguration,
selectedSubtitles: selectedSubtitles.length > 0 ? selectedSubtitles.join(',') : null selectedSubtitles: selectedSubtitles.length > 0 ? selectedSubtitles.join(',') : null,
overrideOptions: isMultipleCombineableAudioSelected ? {
filesize: totalFilesize > 0 ? totalFilesize : undefined,
tbr: totalTbr > 0 ? totalTbr : undefined,
} : undefined
}); });
} }
// toast({ // toast({
@@ -443,7 +486,7 @@ export function BottomBar({ videoMetadata, selectedFormat, selectedFormatFileTyp
setIsStartingDownload(false); setIsStartingDownload(false);
} }
}} }}
disabled={isStartingDownload || !selectedDownloadFormat || (activeDownloadModeTab === 'combine' && (!selectedCombinableVideoFormat || !selectedCombinableAudioFormat)) || (useCustomCommands && !downloadConfiguration.custom_command)} disabled={isStartingDownload || !selectedDownloadFormat || (activeDownloadModeTab === 'combine' && (!selectedCombinableVideoFormat || !isCombineableAudioSelected)) || (useCustomCommands && !downloadConfiguration.custom_command)}
> >
{isStartingDownload ? ( {isStartingDownload ? (
<> <>

View File

@@ -9,6 +9,7 @@ import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/componen
import { PlaylistToggleGroup, PlaylistToggleGroupItem } from "@/components/custom/playlistToggleGroup"; import { PlaylistToggleGroup, PlaylistToggleGroupItem } from "@/components/custom/playlistToggleGroup";
import { getMergedBestFormat } from "@/utils"; import { getMergedBestFormat } from "@/utils";
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import { FormatToggleGroup, FormatToggleGroupItem } from "@/components/custom/formatToggleGroup";
interface PlaylistPreviewSelectionProps { interface PlaylistPreviewSelectionProps {
videoMetadata: RawVideoInfo; videoMetadata: RawVideoInfo;
@@ -42,7 +43,7 @@ function PlaylistPreviewSelection({ videoMetadata }: PlaylistPreviewSelectionPro
const selectedPlaylistVideos = useDownloaderPageStatesStore((state) => state.selectedPlaylistVideos); const selectedPlaylistVideos = useDownloaderPageStatesStore((state) => state.selectedPlaylistVideos);
const setSelectedDownloadFormat = useDownloaderPageStatesStore((state) => state.setSelectedDownloadFormat); const setSelectedDownloadFormat = useDownloaderPageStatesStore((state) => state.setSelectedDownloadFormat);
const setSelectedCombinableVideoFormat = useDownloaderPageStatesStore((state) => state.setSelectedCombinableVideoFormat); const setSelectedCombinableVideoFormat = useDownloaderPageStatesStore((state) => state.setSelectedCombinableVideoFormat);
const setSelectedCombinableAudioFormat = useDownloaderPageStatesStore((state) => state.setSelectedCombinableAudioFormat); const setSelectedCombinableAudioFormats = useDownloaderPageStatesStore((state) => state.setSelectedCombinableAudioFormats);
const setSelectedSubtitles = useDownloaderPageStatesStore((state) => state.setSelectedSubtitles); const setSelectedSubtitles = useDownloaderPageStatesStore((state) => state.setSelectedSubtitles);
const setSelectedPlaylistVideos = useDownloaderPageStatesStore((state) => state.setSelectedPlaylistVideos); const setSelectedPlaylistVideos = useDownloaderPageStatesStore((state) => state.setSelectedPlaylistVideos);
const resetDownloadConfiguration = useDownloaderPageStatesStore((state) => state.resetDownloadConfiguration); const resetDownloadConfiguration = useDownloaderPageStatesStore((state) => state.resetDownloadConfiguration);
@@ -70,7 +71,7 @@ function PlaylistPreviewSelection({ videoMetadata }: PlaylistPreviewSelectionPro
setSelectedDownloadFormat('best'); setSelectedDownloadFormat('best');
setSelectedSubtitles([]); setSelectedSubtitles([]);
setSelectedCombinableVideoFormat(''); setSelectedCombinableVideoFormat('');
setSelectedCombinableAudioFormat(''); setSelectedCombinableAudioFormats([]);
resetDownloadConfiguration(); resetDownloadConfiguration();
}} }}
disabled={totalVideos <= 1} disabled={totalVideos <= 1}
@@ -90,7 +91,7 @@ function PlaylistPreviewSelection({ videoMetadata }: PlaylistPreviewSelectionPro
setSelectedDownloadFormat('best'); setSelectedDownloadFormat('best');
setSelectedSubtitles([]); setSelectedSubtitles([]);
setSelectedCombinableVideoFormat(''); setSelectedCombinableVideoFormat('');
setSelectedCombinableAudioFormat(''); setSelectedCombinableAudioFormats([]);
resetDownloadConfiguration(); resetDownloadConfiguration();
} }
}} }}
@@ -237,10 +238,10 @@ function SelectivePlaylistDownload({ videoMetadata, audioOnlyFormats, videoOnlyF
function CombinedPlaylistDownload({ audioOnlyFormats, videoOnlyFormats, subtitleLanguages }: CombinedPlaylistDownloadProps) { function CombinedPlaylistDownload({ audioOnlyFormats, videoOnlyFormats, subtitleLanguages }: CombinedPlaylistDownloadProps) {
const selectedCombinableVideoFormat = useDownloaderPageStatesStore((state) => state.selectedCombinableVideoFormat); const selectedCombinableVideoFormat = useDownloaderPageStatesStore((state) => state.selectedCombinableVideoFormat);
const selectedCombinableAudioFormat = useDownloaderPageStatesStore((state) => state.selectedCombinableAudioFormat); const selectedCombinableAudioFormats = useDownloaderPageStatesStore((state) => state.selectedCombinableAudioFormats);
const selectedSubtitles = useDownloaderPageStatesStore((state) => state.selectedSubtitles); const selectedSubtitles = useDownloaderPageStatesStore((state) => state.selectedSubtitles);
const setSelectedCombinableVideoFormat = useDownloaderPageStatesStore((state) => state.setSelectedCombinableVideoFormat); const setSelectedCombinableVideoFormat = useDownloaderPageStatesStore((state) => state.setSelectedCombinableVideoFormat);
const setSelectedCombinableAudioFormat = useDownloaderPageStatesStore((state) => state.setSelectedCombinableAudioFormat); const setSelectedCombinableAudioFormats = useDownloaderPageStatesStore((state) => state.setSelectedCombinableAudioFormats);
const setSelectedSubtitles = useDownloaderPageStatesStore((state) => state.setSelectedSubtitles); const setSelectedSubtitles = useDownloaderPageStatesStore((state) => state.setSelectedSubtitles);
const resetDownloadConfiguration = useDownloaderPageStatesStore((state) => state.resetDownloadConfiguration); const resetDownloadConfiguration = useDownloaderPageStatesStore((state) => state.resetDownloadConfiguration);
@@ -276,29 +277,31 @@ function CombinedPlaylistDownload({ audioOnlyFormats, videoOnlyFormats, subtitle
</div> </div>
</ToggleGroup> </ToggleGroup>
)} )}
<FormatSelectionGroup <FormatToggleGroup
type="multiple"
variant="outline"
className="mb-2" className="mb-2"
value={selectedCombinableAudioFormat} value={selectedCombinableAudioFormats}
onValueChange={(value) => { onValueChange={(value: string[]) => {
setSelectedCombinableAudioFormat(value); setSelectedCombinableAudioFormats(value);
resetDownloadConfiguration(); resetDownloadConfiguration();
}} }}
> >
{videoOnlyFormats && videoOnlyFormats.length > 0 && audioOnlyFormats && audioOnlyFormats.length > 0 && ( {videoOnlyFormats && videoOnlyFormats.length > 0 && audioOnlyFormats && audioOnlyFormats.length > 0 && (
<> <>
<p className="text-xs">Audio</p> <p className="text-xs">Audio</p>
<div className="grid grid-cols-2 xl:grid-cols-3 gap-2"> <div className="grid grid-cols-2 xl:grid-cols-3 gap-2">
{audioOnlyFormats.map((format) => ( {audioOnlyFormats.map((format) => (
<FormatSelectionGroupItem <FormatToggleGroupItem
key={format.format_id} key={format.format_id}
value={format.format_id} value={format.format_id}
format={format} format={format}
/> />
))} ))}
</div> </div>
</> </>
)} )}
</FormatSelectionGroup> </FormatToggleGroup>
<FormatSelectionGroup <FormatSelectionGroup
value={selectedCombinableVideoFormat} value={selectedCombinableVideoFormat}
onValueChange={(value) => { onValueChange={(value) => {

View File

@@ -8,10 +8,10 @@ import { Calendar, Clock, DownloadCloud, Eye, Info, ThumbsUp, AlertCircleIcon }
import { FormatSelectionGroup, FormatSelectionGroupItem } from "@/components/custom/formatSelectionGroup"; import { FormatSelectionGroup, FormatSelectionGroupItem } from "@/components/custom/formatSelectionGroup";
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
import { RawVideoInfo, VideoFormat } from "@/types/video"; import { RawVideoInfo, VideoFormat } from "@/types/video";
// import { PlaylistToggleGroup, PlaylistToggleGroupItem } from "@/components/custom/playlistToggleGroup";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable"; import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable";
import { FormatToggleGroup, FormatToggleGroupItem } from "@/components/custom/formatToggleGroup";
interface VideoPreviewProps { interface VideoPreviewProps {
videoMetadata: RawVideoInfo; videoMetadata: RawVideoInfo;
@@ -215,10 +215,10 @@ function SelectiveVideoDownload({ videoMetadata, audioOnlyFormats, videoOnlyForm
function CombinedVideoDownload({ audioOnlyFormats, videoOnlyFormats, subtitleLanguages }: CombinedVideoDownloadProps) { function CombinedVideoDownload({ audioOnlyFormats, videoOnlyFormats, subtitleLanguages }: CombinedVideoDownloadProps) {
const selectedCombinableVideoFormat = useDownloaderPageStatesStore((state) => state.selectedCombinableVideoFormat); const selectedCombinableVideoFormat = useDownloaderPageStatesStore((state) => state.selectedCombinableVideoFormat);
const selectedCombinableAudioFormat = useDownloaderPageStatesStore((state) => state.selectedCombinableAudioFormat); const selectedCombinableAudioFormats = useDownloaderPageStatesStore((state) => state.selectedCombinableAudioFormats);
const selectedSubtitles = useDownloaderPageStatesStore((state) => state.selectedSubtitles); const selectedSubtitles = useDownloaderPageStatesStore((state) => state.selectedSubtitles);
const setSelectedCombinableVideoFormat = useDownloaderPageStatesStore((state) => state.setSelectedCombinableVideoFormat); const setSelectedCombinableVideoFormat = useDownloaderPageStatesStore((state) => state.setSelectedCombinableVideoFormat);
const setSelectedCombinableAudioFormat = useDownloaderPageStatesStore((state) => state.setSelectedCombinableAudioFormat); const setSelectedCombinableAudioFormats = useDownloaderPageStatesStore((state) => state.setSelectedCombinableAudioFormats);
const setSelectedSubtitles = useDownloaderPageStatesStore((state) => state.setSelectedSubtitles); const setSelectedSubtitles = useDownloaderPageStatesStore((state) => state.setSelectedSubtitles);
const resetDownloadConfiguration = useDownloaderPageStatesStore((state) => state.resetDownloadConfiguration); const resetDownloadConfiguration = useDownloaderPageStatesStore((state) => state.resetDownloadConfiguration);
@@ -254,29 +254,31 @@ function CombinedVideoDownload({ audioOnlyFormats, videoOnlyFormats, subtitleLan
</div> </div>
</ToggleGroup> </ToggleGroup>
)} )}
<FormatSelectionGroup <FormatToggleGroup
type="multiple"
variant="outline"
className="mb-2" className="mb-2"
value={selectedCombinableAudioFormat} value={selectedCombinableAudioFormats}
onValueChange={(value) => { onValueChange={(value: string[]) => {
setSelectedCombinableAudioFormat(value); setSelectedCombinableAudioFormats(value);
resetDownloadConfiguration(); resetDownloadConfiguration();
}} }}
> >
{videoOnlyFormats && videoOnlyFormats.length > 0 && audioOnlyFormats && audioOnlyFormats.length > 0 && ( {videoOnlyFormats && videoOnlyFormats.length > 0 && audioOnlyFormats && audioOnlyFormats.length > 0 && (
<> <>
<p className="text-xs">Audio</p> <p className="text-xs">Audio</p>
<div className="grid grid-cols-2 xl:grid-cols-3 gap-2"> <div className="grid grid-cols-2 xl:grid-cols-3 gap-2">
{audioOnlyFormats.map((format) => ( {audioOnlyFormats.map((format) => (
<FormatSelectionGroupItem <FormatToggleGroupItem
key={format.format_id} key={format.format_id}
value={format.format_id} value={format.format_id}
format={format} format={format}
/> />
))} ))}
</div> </div>
</> </>
)} )}
</FormatSelectionGroup> </FormatToggleGroup>
<FormatSelectionGroup <FormatSelectionGroup
value={selectedCombinableVideoFormat} value={selectedCombinableVideoFormat}
onValueChange={(value) => { onValueChange={(value) => {

View File

@@ -145,6 +145,7 @@ export function CompletedDownload({ state }: CompletedDownloadProps) {
const isPlaylist = state.playlist_id !== null && state.playlist_indices !== null; const isPlaylist = state.playlist_id !== null && state.playlist_indices !== null;
const isMultiplePlaylistItems = isPlaylist && state.playlist_indices && state.playlist_indices.includes(','); const isMultiplePlaylistItems = isPlaylist && state.playlist_indices && state.playlist_indices.includes(',');
const isMultipleAudioFormatSelected = state.format_id ? state.format_id.split('+').length > 2 : false;
return ( return (
<div className="p-4 border border-border rounded-lg flex gap-4" key={state.download_id}> <div className="p-4 border border-border rounded-lg flex gap-4" key={state.download_id}>
@@ -238,6 +239,9 @@ export function CompletedDownload({ state }: CompletedDownloadProps) {
{state.acodec && !isMultiplePlaylistItems && ( {state.acodec && !isMultiplePlaylistItems && (
<span className="border border-border py-1 px-2 rounded">{formatCodec(state.acodec)}</span> <span className="border border-border py-1 px-2 rounded">{formatCodec(state.acodec)}</span>
)} )}
{isMultipleAudioFormatSelected && (
<span className="border border-border py-1 px-2 rounded">MULTIAUDIO</span>
)}
{state.dynamic_range && state.dynamic_range !== 'SDR' && !isMultiplePlaylistItems && ( {state.dynamic_range && state.dynamic_range !== 'SDR' && !isMultiplePlaylistItems && (
<span className="border border-border py-1 px-2 rounded">{state.dynamic_range}</span> <span className="border border-border py-1 px-2 rounded">{state.dynamic_range}</span>
)} )}

View File

@@ -104,7 +104,11 @@ export default function useDownloader() {
const { url, formatId, playlistIndices, selectedSubtitles, resumeState, downloadConfig } = params; const { url, formatId, playlistIndices, selectedSubtitles, resumeState, downloadConfig } = params;
try { try {
const args = [url, '--dump-single-json', '--no-warnings']; const args = [url, '--dump-single-json', '--no-warnings'];
if (formatId) args.push('--format', formatId); if (formatId) {
const isMultipleAudioFormatSelected = formatId.split('+').length > 2;
args.push('--format', formatId);
if (isMultipleAudioFormatSelected) args.push('--audio-multistreams');
}
if (selectedSubtitles) { if (selectedSubtitles) {
const isAutoSub = selectedSubtitles.split(',').some(lang => lang.endsWith('-orig')); const isAutoSub = selectedSubtitles.split(',').some(lang => lang.endsWith('-orig'));
if (isAutoSub) args.push('--write-auto-sub'); if (isAutoSub) args.push('--write-auto-sub');
@@ -218,7 +222,7 @@ export default function useDownloader() {
const { url, selectedFormat, downloadConfig, selectedSubtitles, resumeState, playlistItems, overrideOptions } = params; const { url, selectedFormat, downloadConfig, selectedSubtitles, resumeState, playlistItems, overrideOptions } = params;
LOG.info('NEODLP', `Initiating yt-dlp download for URL: ${url}`); LOG.info('NEODLP', `Initiating yt-dlp download for URL: ${url}`);
console.log('Starting download:', { url, selectedFormat, downloadConfig, selectedSubtitles, resumeState, playlistItems }); console.log('Starting download:', { url, selectedFormat, downloadConfig, selectedSubtitles, resumeState, playlistItems, overrideOptions });
if (!ffmpegPath || !tempDownloadDirPath || !downloadDirPath) { if (!ffmpegPath || !tempDownloadDirPath || !downloadDirPath) {
console.error('FFmpeg or download paths not found'); console.error('FFmpeg or download paths not found');
return; return;
@@ -227,6 +231,7 @@ export default function useDownloader() {
const isPlaylist = (playlistItems && typeof playlistItems === 'string') || (resumeState?.playlist_id && resumeState?.playlist_indices) ? true : false; const isPlaylist = (playlistItems && typeof playlistItems === 'string') || (resumeState?.playlist_id && resumeState?.playlist_indices) ? true : false;
const playlistIndices = isPlaylist ? (resumeState?.playlist_indices || playlistItems) : null; const playlistIndices = isPlaylist ? (resumeState?.playlist_indices || playlistItems) : null;
const isMultiplePlaylistItems = isPlaylist && playlistIndices && typeof playlistIndices === 'string' && playlistIndices.includes(','); const isMultiplePlaylistItems = isPlaylist && playlistIndices && typeof playlistIndices === 'string' && playlistIndices.includes(',');
const isMultipleAudioFormatSelected = selectedFormat.split('+').length > 2;
let videoMetadata = await fetchVideoMetadata({ let videoMetadata = await fetchVideoMetadata({
url, url,
formatId: (!isPlaylist || (isPlaylist && selectedFormat !== 'best')) ? selectedFormat : undefined, formatId: (!isPlaylist || (isPlaylist && selectedFormat !== 'best')) ? selectedFormat : undefined,
@@ -245,7 +250,7 @@ export default function useDownloader() {
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); const fileType = isMultipleAudioFormatSelected ? 'video+audio' : determineFileType(videoMetadata.vcodec, videoMetadata.acodec);
if (fileType !== 'unknown' && (VIDEO_FORMAT !== 'auto' || AUDIO_FORMAT !== 'auto')) { if (fileType !== 'unknown' && (VIDEO_FORMAT !== 'auto' || AUDIO_FORMAT !== 'auto')) {
if (VIDEO_FORMAT !== 'auto' && (fileType === 'video+audio' || fileType === 'video')) videoMetadata.ext = VIDEO_FORMAT; if (VIDEO_FORMAT !== 'auto' && (fileType === 'video+audio' || fileType === 'video')) videoMetadata.ext = VIDEO_FORMAT;
@@ -313,6 +318,9 @@ export default function useDownloader() {
if (!isPlaylist || (isPlaylist && selectedFormat !== 'best')) { if (!isPlaylist || (isPlaylist && selectedFormat !== 'best')) {
args.push('--format', selectedFormat); args.push('--format', selectedFormat);
if (isMultipleAudioFormatSelected) {
args.push('--audio-multistreams');
}
} }
if (DEBUG_MODE && LOG_VERBOSE) { if (DEBUG_MODE && LOG_VERBOSE) {
@@ -529,7 +537,7 @@ export default function useDownloader() {
speed: currentProgress.speed || null, speed: currentProgress.speed || null,
eta: currentProgress.eta || null, eta: currentProgress.eta || null,
filepath: downloadFilePath, filepath: downloadFilePath,
filetype: determineFileType(videoMetadata.vcodec, videoMetadata.acodec) || null, filetype: fileType || null,
filesize: resumeState?.filesize || overrideOptions?.filesize || videoMetadata.filesize_approx || null, filesize: resumeState?.filesize || overrideOptions?.filesize || videoMetadata.filesize_approx || null,
output_format: outputFormat, output_format: outputFormat,
embed_metadata: embedMetadata, embed_metadata: embedMetadata,

View File

@@ -48,11 +48,11 @@ export default function DownloaderPage() {
const selectedDownloadFormat = useDownloaderPageStatesStore((state) => state.selectedDownloadFormat); const selectedDownloadFormat = useDownloaderPageStatesStore((state) => state.selectedDownloadFormat);
const selectedCombinableVideoFormat = useDownloaderPageStatesStore((state) => state.selectedCombinableVideoFormat); const selectedCombinableVideoFormat = useDownloaderPageStatesStore((state) => state.selectedCombinableVideoFormat);
const selectedCombinableAudioFormat = useDownloaderPageStatesStore((state) => state.selectedCombinableAudioFormat); const selectedCombinableAudioFormats = useDownloaderPageStatesStore((state) => state.selectedCombinableAudioFormats);
const selectedPlaylistVideos = useDownloaderPageStatesStore((state) => state.selectedPlaylistVideos); const selectedPlaylistVideos = useDownloaderPageStatesStore((state) => state.selectedPlaylistVideos);
const setSelectedDownloadFormat = useDownloaderPageStatesStore((state) => state.setSelectedDownloadFormat); const setSelectedDownloadFormat = useDownloaderPageStatesStore((state) => state.setSelectedDownloadFormat);
const setSelectedCombinableVideoFormat = useDownloaderPageStatesStore((state) => state.setSelectedCombinableVideoFormat); const setSelectedCombinableVideoFormat = useDownloaderPageStatesStore((state) => state.setSelectedCombinableVideoFormat);
const setSelectedCombinableAudioFormat = useDownloaderPageStatesStore((state) => state.setSelectedCombinableAudioFormat); const setSelectedCombinableAudioFormats = useDownloaderPageStatesStore((state) => state.setSelectedCombinableAudioFormats);
const setSelectedSubtitles = useDownloaderPageStatesStore((state) => state.setSelectedSubtitles); const setSelectedSubtitles = useDownloaderPageStatesStore((state) => state.setSelectedSubtitles);
const setSelectedPlaylistVideos = useDownloaderPageStatesStore((state) => state.setSelectedPlaylistVideos); const setSelectedPlaylistVideos = useDownloaderPageStatesStore((state) => state.setSelectedPlaylistVideos);
const resetDownloadConfiguration = useDownloaderPageStatesStore((state) => state.resetDownloadConfiguration); const resetDownloadConfiguration = useDownloaderPageStatesStore((state) => state.resetDownloadConfiguration);
@@ -126,14 +126,14 @@ export default function DownloaderPage() {
); );
} }
})(); })();
const selectedAudioFormat = (() => { const selectedAudioFormats = (() => {
if (videoMetadata?._type === 'video') { if (videoMetadata?._type === 'video') {
return allFilteredFormats.find( return allFilteredFormats.filter(
(format) => format.format_id === selectedCombinableAudioFormat (format) => selectedCombinableAudioFormats.includes(format.format_id)
); );
} else if (videoMetadata?._type === 'playlist') { } else if (videoMetadata?._type === 'playlist') {
return allFilteredFormats.find( return allFilteredFormats.filter(
(format) => format.format_id === selectedCombinableAudioFormat (format) => selectedCombinableAudioFormats.includes(format.format_id)
); );
} }
})(); })();
@@ -186,7 +186,7 @@ export default function DownloaderPage() {
setIsMetadataLoading(true); setIsMetadataLoading(true);
setSelectedDownloadFormat('best'); setSelectedDownloadFormat('best');
setSelectedCombinableVideoFormat(''); setSelectedCombinableVideoFormat('');
setSelectedCombinableAudioFormat(''); setSelectedCombinableAudioFormats([]);
setSelectedSubtitles([]); setSelectedSubtitles([]);
setSelectedPlaylistVideos(["1"]); setSelectedPlaylistVideos(["1"]);
resetDownloadConfiguration(); resetDownloadConfiguration();
@@ -391,7 +391,7 @@ export default function DownloaderPage() {
selectedFormat={selectedFormat} selectedFormat={selectedFormat}
selectedFormatFileType={selectedFormatFileType} selectedFormatFileType={selectedFormatFileType}
selectedVideoFormat={selectedVideoFormat} selectedVideoFormat={selectedVideoFormat}
selectedAudioFormat={selectedAudioFormat} selectedAudioFormats={selectedAudioFormats}
containerRef={containerRef} containerRef={containerRef}
/> />
)} )}

View File

@@ -51,7 +51,7 @@ export const useDownloaderPageStatesStore = create<DownloaderPageStatesStore>((s
isStartingDownload: false, isStartingDownload: false,
selectedDownloadFormat: 'best', selectedDownloadFormat: 'best',
selectedCombinableVideoFormat: '', selectedCombinableVideoFormat: '',
selectedCombinableAudioFormat: '', selectedCombinableAudioFormats: [],
selectedSubtitles: [], selectedSubtitles: [],
selectedPlaylistVideos: ["1"], selectedPlaylistVideos: ["1"],
downloadConfiguration: { downloadConfiguration: {
@@ -71,7 +71,7 @@ export const useDownloaderPageStatesStore = create<DownloaderPageStatesStore>((s
setIsStartingDownload: (isStarting) => set(() => ({ isStartingDownload: isStarting })), setIsStartingDownload: (isStarting) => set(() => ({ isStartingDownload: isStarting })),
setSelectedDownloadFormat: (format) => set(() => ({ selectedDownloadFormat: format })), setSelectedDownloadFormat: (format) => set(() => ({ selectedDownloadFormat: format })),
setSelectedCombinableVideoFormat: (format) => set(() => ({ selectedCombinableVideoFormat: format })), setSelectedCombinableVideoFormat: (format) => set(() => ({ selectedCombinableVideoFormat: format })),
setSelectedCombinableAudioFormat: (format) => set(() => ({ selectedCombinableAudioFormat: format })), setSelectedCombinableAudioFormats: (formats) => set(() => ({ selectedCombinableAudioFormats: formats })),
setSelectedSubtitles: (subtitles) => set(() => ({ selectedSubtitles: subtitles })), setSelectedSubtitles: (subtitles) => set(() => ({ selectedSubtitles: subtitles })),
setSelectedPlaylistVideos: (indices) => set(() => ({ selectedPlaylistVideos: indices })), setSelectedPlaylistVideos: (indices) => set(() => ({ selectedPlaylistVideos: indices })),
setDownloadConfigurationKey: (key, value) => set((state) => ({ setDownloadConfigurationKey: (key, value) => set((state) => ({
@@ -204,7 +204,7 @@ export const useSettingsPageStatesStore = create<SettingsPageStatesStore>((set)
force_internet_protocol: 'ipv4', force_internet_protocol: 'ipv4',
use_custom_commands: false, use_custom_commands: false,
custom_commands: [], custom_commands: [],
filename_template: '%(title)s_%(resolution|unknown)s', filename_template: '%(title|Unknown)s_%(resolution|unknown)s',
debug_mode: false, debug_mode: false,
log_verbose: true, log_verbose: true,
log_progress: false, log_progress: false,
@@ -275,7 +275,7 @@ export const useSettingsPageStatesStore = create<SettingsPageStatesStore>((set)
force_internet_protocol: 'ipv4', force_internet_protocol: 'ipv4',
use_custom_commands: false, use_custom_commands: false,
custom_commands: [], custom_commands: [],
filename_template: '%(title)s_%(resolution|unknown)s', filename_template: '%(title|Unknown)s_%(resolution|unknown)s',
debug_mode: false, debug_mode: false,
log_verbose: true, log_verbose: true,
log_progress: false, log_progress: false,

View File

@@ -41,7 +41,7 @@ export interface DownloaderPageStatesStore {
isStartingDownload: boolean; isStartingDownload: boolean;
selectedDownloadFormat: string; selectedDownloadFormat: string;
selectedCombinableVideoFormat: string; selectedCombinableVideoFormat: string;
selectedCombinableAudioFormat: string; selectedCombinableAudioFormats: string[];
selectedSubtitles: string[]; selectedSubtitles: string[];
selectedPlaylistVideos: string[]; selectedPlaylistVideos: string[];
downloadConfiguration: DownloadConfiguration; downloadConfiguration: DownloadConfiguration;
@@ -54,7 +54,7 @@ export interface DownloaderPageStatesStore {
setIsStartingDownload: (isStarting: boolean) => void; setIsStartingDownload: (isStarting: boolean) => void;
setSelectedDownloadFormat: (format: string) => void; setSelectedDownloadFormat: (format: string) => void;
setSelectedCombinableVideoFormat: (format: string) => void; setSelectedCombinableVideoFormat: (format: string) => void;
setSelectedCombinableAudioFormat: (format: string) => void; setSelectedCombinableAudioFormats: (formats: string[]) => void;
setSelectedSubtitles: (subtitles: string[]) => void; setSelectedSubtitles: (subtitles: string[]) => void;
setSelectedPlaylistVideos: (indices: string[]) => void; setSelectedPlaylistVideos: (indices: string[]) => void;
setDownloadConfigurationKey: (key: string, value: unknown) => void; setDownloadConfigurationKey: (key: string, value: unknown) => void;