mirror of
https://github.com/neosubhamoy/neodlp.git
synced 2026-02-05 08:42:21 +05:30
feat: added support for full-playlist/selective-batch downloading #9
This commit is contained in:
@@ -3,11 +3,12 @@ import { DownloadCloud, Info, ListVideo, AlertCircleIcon } from "lucide-react";
|
||||
import { FormatSelectionGroup, FormatSelectionGroupItem } from "@/components/custom/formatSelectionGroup";
|
||||
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
|
||||
import { RawVideoInfo, VideoFormat } from "@/types/video";
|
||||
// import { PlaylistToggleGroup, PlaylistToggleGroupItem } from "@/components/custom/playlistToggleGroup";
|
||||
import { PlaylistSelectionGroup, PlaylistSelectionGroupItem } from "@/components/custom/playlistSelectionGroup";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable";
|
||||
import { PlaylistToggleGroup, PlaylistToggleGroupItem } from "@/components/custom/playlistToggleGroup";
|
||||
import { getMergedBestFormat } from "@/utils";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
|
||||
interface PlaylistPreviewSelectionProps {
|
||||
videoMetadata: RawVideoInfo;
|
||||
@@ -38,28 +39,61 @@ interface PlaylistDownloaderProps {
|
||||
}
|
||||
|
||||
function PlaylistPreviewSelection({ videoMetadata }: PlaylistPreviewSelectionProps) {
|
||||
const selectedPlaylistVideoIndex = useDownloaderPageStatesStore((state) => state.selectedPlaylistVideoIndex);
|
||||
const selectedPlaylistVideos = useDownloaderPageStatesStore((state) => state.selectedPlaylistVideos);
|
||||
const setSelectedDownloadFormat = useDownloaderPageStatesStore((state) => state.setSelectedDownloadFormat);
|
||||
const setSelectedCombinableVideoFormat = useDownloaderPageStatesStore((state) => state.setSelectedCombinableVideoFormat);
|
||||
const setSelectedCombinableAudioFormat = useDownloaderPageStatesStore((state) => state.setSelectedCombinableAudioFormat);
|
||||
const setSelectedSubtitles = useDownloaderPageStatesStore((state) => state.setSelectedSubtitles);
|
||||
const setSelectedPlaylistVideoIndex = useDownloaderPageStatesStore((state) => state.setSelectedPlaylistVideoIndex);
|
||||
const setSelectedPlaylistVideos = useDownloaderPageStatesStore((state) => state.setSelectedPlaylistVideos);
|
||||
const resetDownloadConfiguration = useDownloaderPageStatesStore((state) => state.resetDownloadConfiguration);
|
||||
|
||||
const totalVideos = videoMetadata.entries.filter((entry) => entry).length;
|
||||
const allVideoIndices = videoMetadata.entries.filter((entry) => entry).map((entry) => entry.playlist_index.toString());
|
||||
|
||||
return (
|
||||
<div className="flex flex-col w-full pr-4">
|
||||
<h3 className="text-sm mb-4 mt-2 flex items-center gap-2">
|
||||
<ListVideo className="w-4 h-4" />
|
||||
<span>Playlist ({videoMetadata.entries[0].n_entries})</span>
|
||||
</h3>
|
||||
<div className="flex items-center justify-between mb-4 mt-2">
|
||||
<h3 className="text-sm flex items-center gap-2">
|
||||
<ListVideo className="w-4 h-4" />
|
||||
<span>Playlist ({videoMetadata.entries[0].n_entries})</span>
|
||||
</h3>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Switch
|
||||
id="select-all-videos"
|
||||
checked={selectedPlaylistVideos.length === totalVideos && totalVideos > 0}
|
||||
onCheckedChange={(checked) => {
|
||||
if (checked) {
|
||||
setSelectedPlaylistVideos(allVideoIndices);
|
||||
} else {
|
||||
setSelectedPlaylistVideos(["1"]);
|
||||
}
|
||||
setSelectedDownloadFormat('best');
|
||||
setSelectedSubtitles([]);
|
||||
setSelectedCombinableVideoFormat('');
|
||||
setSelectedCombinableAudioFormat('');
|
||||
resetDownloadConfiguration();
|
||||
}}
|
||||
disabled={totalVideos <= 1}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col overflow-y-scroll max-h-[50vh] xl:max-h-[60vh] no-scrollbar">
|
||||
<h2 className="mb-1">{videoMetadata.entries[0].playlist_title ? videoMetadata.entries[0].playlist_title : 'UNTITLED'}</h2>
|
||||
<p className="text-muted-foreground text-xs mb-4">{videoMetadata.entries[0].playlist_creator || videoMetadata.entries[0].playlist_channel || videoMetadata.entries[0].playlist_uploader || 'unknown'}</p>
|
||||
{/* <PlaylistToggleGroup
|
||||
<PlaylistToggleGroup
|
||||
className="mb-2"
|
||||
type="multiple"
|
||||
value={selectedVideos}
|
||||
onValueChange={setSelectedVideos}
|
||||
value={selectedPlaylistVideos}
|
||||
onValueChange={(value: string[]) => {
|
||||
if (value.length > 0) {
|
||||
setSelectedPlaylistVideos(value);
|
||||
setSelectedDownloadFormat('best');
|
||||
setSelectedSubtitles([]);
|
||||
setSelectedCombinableVideoFormat('');
|
||||
setSelectedCombinableAudioFormat('');
|
||||
resetDownloadConfiguration();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{videoMetadata.entries.map((entry) => entry ? (
|
||||
<PlaylistToggleGroupItem
|
||||
@@ -68,27 +102,7 @@ function PlaylistPreviewSelection({ videoMetadata }: PlaylistPreviewSelectionPro
|
||||
video={entry}
|
||||
/>
|
||||
) : null)}
|
||||
</PlaylistToggleGroup> */}
|
||||
<PlaylistSelectionGroup
|
||||
className="mb-2"
|
||||
value={selectedPlaylistVideoIndex}
|
||||
onValueChange={(value) => {
|
||||
setSelectedPlaylistVideoIndex(value);
|
||||
setSelectedDownloadFormat('best');
|
||||
setSelectedSubtitles([]);
|
||||
setSelectedCombinableVideoFormat('');
|
||||
setSelectedCombinableAudioFormat('');
|
||||
resetDownloadConfiguration();
|
||||
}}
|
||||
>
|
||||
{videoMetadata.entries.map((entry) => entry ? (
|
||||
<PlaylistSelectionGroupItem
|
||||
key={entry.playlist_index}
|
||||
value={entry.playlist_index.toString()}
|
||||
video={entry}
|
||||
/>
|
||||
) : null)}
|
||||
</PlaylistSelectionGroup>
|
||||
</PlaylistToggleGroup>
|
||||
<div className="flex items-center text-muted-foreground">
|
||||
<Info className="w-3 h-3 mr-2" />
|
||||
<span className="text-xs">Extracted from {videoMetadata.entries[0].extractor ? videoMetadata.entries[0].extractor.charAt(0).toUpperCase() + videoMetadata.entries[0].extractor.slice(1) : 'Unknown'}</span>
|
||||
@@ -102,7 +116,7 @@ function PlaylistPreviewSelection({ videoMetadata }: PlaylistPreviewSelectionPro
|
||||
function SelectivePlaylistDownload({ videoMetadata, audioOnlyFormats, videoOnlyFormats, combinedFormats, qualityPresetFormats, subtitleLanguages }: SelectivePlaylistDownloadProps) {
|
||||
const selectedDownloadFormat = useDownloaderPageStatesStore((state) => state.selectedDownloadFormat);
|
||||
const selectedSubtitles = useDownloaderPageStatesStore((state) => state.selectedSubtitles);
|
||||
const selectedPlaylistVideoIndex = useDownloaderPageStatesStore((state) => state.selectedPlaylistVideoIndex);
|
||||
const selectedPlaylistVideos = useDownloaderPageStatesStore((state) => state.selectedPlaylistVideos);
|
||||
const setSelectedDownloadFormat = useDownloaderPageStatesStore((state) => state.setSelectedDownloadFormat);
|
||||
const setSelectedSubtitles = useDownloaderPageStatesStore((state) => state.setSelectedSubtitles);
|
||||
const resetDownloadConfiguration = useDownloaderPageStatesStore((state) => state.resetDownloadConfiguration);
|
||||
@@ -120,16 +134,23 @@ function SelectivePlaylistDownload({ videoMetadata, audioOnlyFormats, videoOnlyF
|
||||
>
|
||||
<p className="text-xs">Subtitle Languages</p>
|
||||
<div className="flex gap-2 flex-wrap items-center">
|
||||
{subtitleLanguages.map((lang) => (
|
||||
<ToggleGroupItem
|
||||
className="text-xs text-nowrap border-2 data-[state=on]:border-2 data-[state=on]:border-primary data-[state=on]:bg-primary/10 hover:bg-muted/70"
|
||||
value={lang.code}
|
||||
size="sm"
|
||||
aria-label={lang.lang}
|
||||
key={lang.code}>
|
||||
{lang.lang}
|
||||
</ToggleGroupItem>
|
||||
))}
|
||||
{subtitleLanguages.map((lang) => {
|
||||
const hasAutoSubSelected = selectedSubtitles.some(code => code.endsWith('-orig'));
|
||||
const hasNormalSubSelected = selectedSubtitles.some(code => !code.endsWith('-orig'));
|
||||
const isDisabled = (hasAutoSubSelected && !lang.code.endsWith('-orig')) || (hasNormalSubSelected && lang.code.endsWith('-orig'));
|
||||
|
||||
return (
|
||||
<ToggleGroupItem
|
||||
className="text-xs text-nowrap border-2 data-[state=on]:border-2 data-[state=on]:border-primary data-[state=on]:bg-primary/10 hover:bg-muted/70"
|
||||
value={lang.code}
|
||||
size="sm"
|
||||
aria-label={lang.lang}
|
||||
key={lang.code}
|
||||
disabled={isDisabled}>
|
||||
{lang.lang}
|
||||
</ToggleGroupItem>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</ToggleGroup>
|
||||
)}
|
||||
@@ -149,7 +170,7 @@ function SelectivePlaylistDownload({ videoMetadata, audioOnlyFormats, videoOnlyF
|
||||
<FormatSelectionGroupItem
|
||||
key="best"
|
||||
value="best"
|
||||
format={videoMetadata.entries[Number(selectedPlaylistVideoIndex) - 1].requested_downloads[0]}
|
||||
format={getMergedBestFormat(videoMetadata.entries, selectedPlaylistVideos) as VideoFormat}
|
||||
/>
|
||||
</div>
|
||||
{qualityPresetFormats && qualityPresetFormats.length > 0 && (
|
||||
@@ -235,16 +256,23 @@ function CombinedPlaylistDownload({ audioOnlyFormats, videoOnlyFormats, subtitle
|
||||
>
|
||||
<p className="text-xs">Subtitle Languages</p>
|
||||
<div className="flex gap-2 flex-wrap items-center">
|
||||
{subtitleLanguages.map((lang) => (
|
||||
<ToggleGroupItem
|
||||
className="text-xs text-nowrap border-2 data-[state=on]:border-2 data-[state=on]:border-primary data-[state=on]:bg-primary/10 hover:bg-muted/70"
|
||||
value={lang.code}
|
||||
size="sm"
|
||||
aria-label={lang.lang}
|
||||
key={lang.code}>
|
||||
{lang.lang}
|
||||
</ToggleGroupItem>
|
||||
))}
|
||||
{subtitleLanguages.map((lang) => {
|
||||
const hasAutoSubSelected = selectedSubtitles.some(code => code.endsWith('-orig'));
|
||||
const hasNormalSubSelected = selectedSubtitles.some(code => !code.endsWith('-orig'));
|
||||
const isDisabled = (hasAutoSubSelected && !lang.code.endsWith('-orig')) || (hasNormalSubSelected && lang.code.endsWith('-orig'));
|
||||
|
||||
return (
|
||||
<ToggleGroupItem
|
||||
className="text-xs text-nowrap border-2 data-[state=on]:border-2 data-[state=on]:border-primary data-[state=on]:bg-primary/10 hover:bg-muted/70"
|
||||
value={lang.code}
|
||||
size="sm"
|
||||
aria-label={lang.lang}
|
||||
key={lang.code}
|
||||
disabled={isDisabled}>
|
||||
{lang.lang}
|
||||
</ToggleGroupItem>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</ToggleGroup>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user