mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-18 15:23:13 +00:00
feat(downloads): add dedicated Torrent category
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type DownloadCategory = "Musics" | "Movies" | "Compressed" | "Documents" | "Pictures" | "Applications" | "Other";
|
||||
export type DownloadCategory = "Musics" | "Movies" | "Compressed" | "Documents" | "Pictures" | "Applications" | "Torrents" | "Other";
|
||||
|
||||
@@ -888,7 +888,7 @@ export const AddDownloadsModal = () => {
|
||||
if (first.status !== 'ready' && first.status !== 'metadata-error') return;
|
||||
void resolveCategoryDestination(
|
||||
useSettingsStore.getState(),
|
||||
categoryForFileName(first.file)
|
||||
categoryForFileName(first.file, first.isTorrent === true)
|
||||
).then(location => {
|
||||
if (requestId === locationResolutionRequestRef.current) {
|
||||
setSaveLocation(location);
|
||||
@@ -940,8 +940,8 @@ export const AddDownloadsModal = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const categoryLocationForFile = (fileName: string) => {
|
||||
const category = categoryForFileName(fileName);
|
||||
const categoryLocationForFile = (fileName: string, isTorrent = false) => {
|
||||
const category = categoryForFileName(fileName, isTorrent);
|
||||
return resolveCategoryDestination(useSettingsStore.getState(), category);
|
||||
};
|
||||
|
||||
@@ -963,12 +963,13 @@ export const AddDownloadsModal = () => {
|
||||
fileName: string,
|
||||
finalLocation: string,
|
||||
useSharedDestination: boolean,
|
||||
destinationOverride?: string
|
||||
destinationOverride?: string,
|
||||
isTorrent = false
|
||||
): Promise<string> => {
|
||||
if (destinationOverride) return destinationOverride;
|
||||
const root = useSharedDestination
|
||||
? finalLocation
|
||||
: await categoryLocationForFile(fileName);
|
||||
: await categoryLocationForFile(fileName, isTorrent);
|
||||
return saveInDedicatedFolder
|
||||
? resolveSubfolderDestination(root, dedicatedFolderName)
|
||||
: root;
|
||||
@@ -1113,7 +1114,9 @@ export const AddDownloadsModal = () => {
|
||||
const suggestedLocation = await destinationForFile(
|
||||
item.file,
|
||||
finalLocation,
|
||||
isSaveLocationManual
|
||||
isSaveLocationManual,
|
||||
undefined,
|
||||
item.isTorrent === true
|
||||
);
|
||||
const selected = await open({
|
||||
directory: true,
|
||||
@@ -1160,7 +1163,8 @@ export const AddDownloadsModal = () => {
|
||||
finalFile,
|
||||
finalLocation,
|
||||
useSharedDestination,
|
||||
destinationOverrides[i]
|
||||
destinationOverrides[i],
|
||||
item.isTorrent === true
|
||||
);
|
||||
|
||||
const urlMatch = store.downloads.find(d =>
|
||||
@@ -1342,7 +1346,8 @@ export const AddDownloadsModal = () => {
|
||||
finalFile,
|
||||
finalLocation,
|
||||
useSharedDestination,
|
||||
destinationOverrides[idx]
|
||||
destinationOverrides[idx],
|
||||
item.isTorrent === true
|
||||
);
|
||||
|
||||
let count = 1;
|
||||
@@ -1358,7 +1363,8 @@ export const AddDownloadsModal = () => {
|
||||
candidateFile,
|
||||
finalLocation,
|
||||
useSharedDestination,
|
||||
destinationOverrides[candidateIndex]
|
||||
destinationOverrides[candidateIndex],
|
||||
candidate.isTorrent === true
|
||||
);
|
||||
batchTargets.push({ location: candidateLocation, fileName: candidateFile });
|
||||
}
|
||||
@@ -1419,7 +1425,8 @@ export const AddDownloadsModal = () => {
|
||||
finalFile,
|
||||
finalLocation,
|
||||
useSharedDestination,
|
||||
destinationOverrides[idx]
|
||||
destinationOverrides[idx],
|
||||
item.isTorrent === true
|
||||
);
|
||||
const store = useDownloadStore.getState();
|
||||
let existingItem = conflict?.existingDownloadId
|
||||
@@ -1531,7 +1538,7 @@ export const AddDownloadsModal = () => {
|
||||
? mediaFileNameForSelectedFormat(item.file, item)
|
||||
: canonicalizeDownloadFileName(item.file);
|
||||
let formatSelector = mediaFormatSelectorForRow(item);
|
||||
const category = categoryForFileName(finalFile);
|
||||
const category = categoryForFileName(finalFile, item.isTorrent === true);
|
||||
const added = await addDownload({
|
||||
id,
|
||||
url: item.downloadUrl,
|
||||
@@ -1559,7 +1566,8 @@ export const AddDownloadsModal = () => {
|
||||
finalFile,
|
||||
finalLocation,
|
||||
useSharedDestination,
|
||||
destinationOverrides[itemIndex]
|
||||
destinationOverrides[itemIndex],
|
||||
item.isTorrent === true
|
||||
)
|
||||
: undefined,
|
||||
isMedia: item.isMedia,
|
||||
|
||||
@@ -273,8 +273,8 @@ export const DownloadItem = React.memo<DownloadItemProps>(({
|
||||
</span>
|
||||
) : null}
|
||||
{download.isTorrent ? (
|
||||
<span className="download-quality-chip shrink-0" title={t($ => $.addDownloads.torrentFiles)}>
|
||||
{t($ => $.addDownloads.torrentFiles)}
|
||||
<span className="download-quality-chip shrink-0" title={t($ => $.addDownloads.torrent)}>
|
||||
{t($ => $.addDownloads.torrent)}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useToast } from '../contexts/ToastContext';
|
||||
import { useSettingsStore } from '../store/useSettingsStore';
|
||||
import { SidebarFilter } from './Sidebar';
|
||||
import {
|
||||
Play, Pause, Plus, FileText, Image as ImageIcon, Music, Film, Box, Archive, FileQuestion,
|
||||
Play, Pause, Plus, FileText, Image as ImageIcon, Music, Film, Box, Archive, FileQuestion, Magnet,
|
||||
ArrowDownCircle, ArrowUp, ArrowDown, Command, ChevronUp, ChevronDown, MoreHorizontal,
|
||||
AlignLeft, AlignCenter, AlignRight, GripVertical
|
||||
} from 'lucide-react';
|
||||
@@ -1850,6 +1850,7 @@ export const DownloadTable: React.FC<DownloadTableProps> = ({ filter, onSummaryC
|
||||
case 'Documents': return t($ => $.navigation.categories.documents);
|
||||
case 'Pictures': return t($ => $.navigation.categories.pictures);
|
||||
case 'Applications': return t($ => $.navigation.categories.applications);
|
||||
case 'Torrents': return t($ => $.navigation.categories.torrents);
|
||||
case 'Other': return t($ => $.navigation.categories.other);
|
||||
default: return filter;
|
||||
}
|
||||
@@ -2008,6 +2009,7 @@ export const DownloadTable: React.FC<DownloadTableProps> = ({ filter, onSummaryC
|
||||
case 'Applications': return <Box size={16} className="text-indigo-400" />;
|
||||
case 'Pictures': return <ImageIcon size={16} className="text-purple-400" />;
|
||||
case 'Compressed': return <Archive size={16} className="text-amber-600" />;
|
||||
case 'Torrents': return <Magnet size={16} className="text-violet-400" />;
|
||||
case 'Other': return <FileQuestion size={16} className="text-gray-400" />;
|
||||
default: return <FileQuestion size={16} className="text-gray-400" />;
|
||||
}
|
||||
|
||||
@@ -813,6 +813,7 @@ runEngineChecks(false);
|
||||
case 'Documents': return t($ => $.navigation.categories.documents);
|
||||
case 'Pictures': return t($ => $.navigation.categories.pictures);
|
||||
case 'Applications': return t($ => $.navigation.categories.applications);
|
||||
case 'Torrents': return t($ => $.navigation.categories.torrents);
|
||||
default: return t($ => $.navigation.categories.other);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState, useEffect, useLayoutEffect, useRef } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import {
|
||||
Inbox, Zap, CheckCircle2, CircleDashed,
|
||||
Film, Music, FileText, Box, Image as ImageIcon, Archive, FileQuestion,
|
||||
Film, Music, FileText, Box, Image as ImageIcon, Archive, FileQuestion, Magnet,
|
||||
List, CalendarClock, Gauge, Bug, Settings, Plus, Play, Pause, Edit2, Trash2, PanelLeft,
|
||||
ChevronDown,
|
||||
type LucideIcon
|
||||
@@ -403,6 +403,7 @@ export const Sidebar: React.FC<SidebarProps> = (props) => {
|
||||
<NavItem icon={FileText} label={t($ => $.navigation.categories.documents)} filter="Documents" />
|
||||
<NavItem icon={ImageIcon} label={t($ => $.navigation.categories.pictures)} filter="Pictures" />
|
||||
<NavItem icon={Box} label={t($ => $.navigation.categories.applications)} filter="Applications" />
|
||||
<NavItem icon={Magnet} label={t($ => $.navigation.categories.torrents)} filter="Torrents" />
|
||||
<NavItem icon={FileQuestion} label={t($ => $.navigation.categories.other)} filter="Other" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,6 +14,7 @@ const common = {
|
||||
documents: 'Documents',
|
||||
pictures: 'Pictures',
|
||||
applications: 'Applications',
|
||||
torrents: 'Torrents',
|
||||
other: 'Other',
|
||||
},
|
||||
folders: 'Folders',
|
||||
@@ -667,6 +668,7 @@ const common = {
|
||||
refreshMetadata: 'Refresh Metadata',
|
||||
files: 'Files',
|
||||
torrentFiles: 'Torrent files',
|
||||
torrent: 'Torrent',
|
||||
chooseTorrentFiles: 'Add .torrent files',
|
||||
torrentMetadataPending: 'Aria2 will resolve the magnet metadata when the transfer starts.',
|
||||
torrentSeeding: 'Torrent seeding',
|
||||
|
||||
@@ -14,6 +14,7 @@ const fa = {
|
||||
documents: 'اسناد',
|
||||
pictures: 'تصاویر',
|
||||
applications: 'برنامهها',
|
||||
torrents: 'تورنتها',
|
||||
other: 'سایر',
|
||||
},
|
||||
folders: 'پوشهها',
|
||||
@@ -667,6 +668,7 @@ const fa = {
|
||||
refreshMetadata: 'تازهسازی متادیتا',
|
||||
files: 'فایلها',
|
||||
torrentFiles: 'فایلهای تورنت',
|
||||
torrent: 'تورنت',
|
||||
chooseTorrentFiles: 'افزودن فایلهای .torrent',
|
||||
torrentMetadataPending: 'آریا۲ هنگام شروع انتقال، متادیتای مگنت را دریافت میکند.',
|
||||
torrentSeeding: 'اشتراکگذاری تورنت',
|
||||
|
||||
@@ -14,6 +14,7 @@ const he = {
|
||||
documents: 'מסמכים',
|
||||
pictures: 'תמונות',
|
||||
applications: 'יישומים',
|
||||
torrents: 'טורנטים',
|
||||
other: 'אחר',
|
||||
},
|
||||
folders: 'תיקיות',
|
||||
@@ -667,6 +668,7 @@ const he = {
|
||||
refreshMetadata: 'רענון מטא נתונים',
|
||||
files: 'קבצים',
|
||||
torrentFiles: 'קובצי טורנט',
|
||||
torrent: 'טורנט',
|
||||
chooseTorrentFiles: 'הוספת קובצי .torrent',
|
||||
torrentMetadataPending: 'Aria2 יאתר את נתוני המגנט כשההעברה תתחיל.',
|
||||
torrentSeeding: 'שיתוף טורנט',
|
||||
|
||||
@@ -14,6 +14,7 @@ const ru = {
|
||||
documents: 'Документы',
|
||||
pictures: 'Изображения',
|
||||
applications: 'Программы',
|
||||
torrents: 'Торренты',
|
||||
other: 'Другое',
|
||||
},
|
||||
folders: 'Папки',
|
||||
@@ -667,6 +668,7 @@ const ru = {
|
||||
refreshMetadata: 'Обновить метаданные',
|
||||
files: 'Файлы',
|
||||
torrentFiles: 'Торрент-файлы',
|
||||
torrent: 'Торрент',
|
||||
chooseTorrentFiles: 'Добавить файлы .torrent',
|
||||
torrentMetadataPending: 'Aria2 получит метаданные магнита при запуске передачи.',
|
||||
torrentSeeding: 'Раздача торрента',
|
||||
|
||||
@@ -14,6 +14,7 @@ const uk = {
|
||||
documents: 'Документи',
|
||||
pictures: 'Зображення',
|
||||
applications: 'Програми',
|
||||
torrents: 'Торенти',
|
||||
other: 'Інше',
|
||||
},
|
||||
folders: 'Папки',
|
||||
@@ -667,6 +668,7 @@ const uk = {
|
||||
refreshMetadata: 'Оновити метадані',
|
||||
files: 'Файли',
|
||||
torrentFiles: 'Торрент-файли',
|
||||
torrent: 'Торрент',
|
||||
chooseTorrentFiles: 'Додати файли .torrent',
|
||||
torrentMetadataPending: 'Aria2 отримає метадані магнітного посилання після початку передачі.',
|
||||
torrentSeeding: 'Роздача торрента',
|
||||
|
||||
@@ -14,6 +14,7 @@ const zhCN = {
|
||||
documents: '文档',
|
||||
pictures: '图片',
|
||||
applications: '应用程序',
|
||||
torrents: '种子',
|
||||
other: '其他',
|
||||
},
|
||||
folders: '文件夹',
|
||||
@@ -667,6 +668,7 @@ const zhCN = {
|
||||
refreshMetadata: '刷新元数据',
|
||||
files: '文件',
|
||||
torrentFiles: '种子文件',
|
||||
torrent: '种子',
|
||||
chooseTorrentFiles: '添加 .torrent 文件',
|
||||
torrentMetadataPending: '传输开始时,Aria2 将解析磁力链接元数据。',
|
||||
torrentSeeding: 'BT 做种',
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { UnlistenFn } from '@tauri-apps/api/event';
|
||||
import type { DownloadStatus } from '../bindings/DownloadStatus';
|
||||
import { listenEvent as listen } from '../ipc';
|
||||
import type { DownloadItem } from '../bindings/DownloadItem';
|
||||
import { categoryForFileName } from '../utils/downloads';
|
||||
import { categoryForDownload } from '../utils/downloads';
|
||||
import { useDownloadProgressStore } from './downloadProgressStore';
|
||||
|
||||
import {
|
||||
@@ -221,7 +221,11 @@ const startDownloadListeners = async () => {
|
||||
}
|
||||
if (payload.fileName && payload.fileName !== current.fileName) {
|
||||
updates.fileName = payload.fileName;
|
||||
updates.category = categoryForFileName(payload.fileName);
|
||||
updates.category = categoryForDownload(
|
||||
payload.fileName,
|
||||
current.isTorrent === true,
|
||||
current.category
|
||||
);
|
||||
}
|
||||
if (status !== 'downloading' && status !== 'verifying') {
|
||||
updates.speed = '-';
|
||||
|
||||
@@ -1607,6 +1607,23 @@ describe('useDownloadStore', () => {
|
||||
expect(ipc.invokeCommand).not.toHaveBeenCalledWith('enqueue_download', expect.anything());
|
||||
});
|
||||
|
||||
it('normalizes new Torrent rows before resolving their default destination', async () => {
|
||||
await useDownloadStore.getState().addDownload({
|
||||
id: 'torrent-default',
|
||||
url: 'magnet:?xt=urn:btih:default',
|
||||
fileName: 'metadata',
|
||||
category: 'Other',
|
||||
dateAdded: '',
|
||||
isTorrent: true
|
||||
}, { type: 'add-to-queue', queueId: 'queue-torrents' });
|
||||
|
||||
expect(useDownloadStore.getState().downloads[0]).toMatchObject({
|
||||
category: 'Torrents',
|
||||
destination: '/Users/test/Downloads/Torrents',
|
||||
status: 'staged'
|
||||
});
|
||||
});
|
||||
|
||||
it('inserts a newly staged queue item before paused rows', async () => {
|
||||
useDownloadStore.setState({
|
||||
downloads: [{
|
||||
|
||||
@@ -9,7 +9,7 @@ import type { ExtensionCookieScope } from '../bindings/ExtensionCookieScope';
|
||||
import type { Queue } from '../bindings/Queue';
|
||||
import { useSettingsStore } from './useSettingsStore';
|
||||
import { useDownloadProgressStore } from './downloadProgressStore';
|
||||
import { canonicalizeDownloadFileName, categoryForFileName, isActiveDownloadStatus, isTransferActiveStatus, isValidTorrentExcludeTrackerList, isValidTorrentTrackerList, MAX_TORRENT_STOP_TIMEOUT, normalizeSpeedLimitForBackend, normalizeTorrentEncryptionPolicy, normalizeTorrentFileAllocation, normalizeTorrentPrioritizePiece, normalizeTorrentTrackerInterval, normalizeTorrentTrackerTimeout, redactDownloadForPersistence, resolveDownloadConnections } from '../utils/downloads';
|
||||
import { canonicalizeDownloadFileName, categoryForDownload, categoryForFileName, isActiveDownloadStatus, isTransferActiveStatus, isValidTorrentExcludeTrackerList, isValidTorrentTrackerList, MAX_TORRENT_STOP_TIMEOUT, normalizeSpeedLimitForBackend, normalizeTorrentEncryptionPolicy, normalizeTorrentFileAllocation, normalizeTorrentPrioritizePiece, normalizeTorrentTrackerInterval, normalizeTorrentTrackerTimeout, redactDownloadForPersistence, resolveDownloadConnections } from '../utils/downloads';
|
||||
import {
|
||||
resolveCategoryDestination
|
||||
} from '../utils/downloadLocations';
|
||||
@@ -418,7 +418,11 @@ async function dispatchItemInternal(id: string, proxyOverride?: string | null):
|
||||
if (acceptedFilename !== item.fileName) {
|
||||
useDownloadStore.getState().updateDownload(id, {
|
||||
fileName: acceptedFilename,
|
||||
category: categoryForFileName(acceptedFilename)
|
||||
category: categoryForDownload(
|
||||
acceptedFilename,
|
||||
item.isTorrent === true,
|
||||
item.category
|
||||
)
|
||||
});
|
||||
}
|
||||
const order = await invoke('get_pending_order', { queueId: item.queueId || MAIN_QUEUE_ID });
|
||||
@@ -1614,7 +1618,8 @@ export const useDownloadStore = create<DownloadState>((set, get) => {
|
||||
const settings = useSettingsStore.getState();
|
||||
const normalizedItem = {
|
||||
...item,
|
||||
fileName: canonicalizeDownloadFileName(item.fileName)
|
||||
fileName: canonicalizeDownloadFileName(item.fileName),
|
||||
category: categoryForFileName(item.fileName, item.isTorrent === true)
|
||||
};
|
||||
const destPath = await effectiveDestinationForItem(normalizedItem, settings);
|
||||
const queueId = action.type === 'add-to-queue' ? action.queueId : MAIN_QUEUE_ID;
|
||||
@@ -2541,7 +2546,11 @@ export const useDownloadStore = create<DownloadState>((set, get) => {
|
||||
...(acceptedFilenames.has(download.id)
|
||||
? {
|
||||
fileName: acceptedFilenames.get(download.id),
|
||||
category: categoryForFileName(acceptedFilenames.get(download.id)!)
|
||||
category: categoryForDownload(
|
||||
acceptedFilenames.get(download.id)!,
|
||||
download.isTorrent === true,
|
||||
download.category
|
||||
)
|
||||
}
|
||||
: {}),
|
||||
hasBeenDispatched: true,
|
||||
|
||||
@@ -130,6 +130,24 @@ describe('download locations', () => {
|
||||
expect(await resolveCategoryDestination(automatic, 'Movies')).toBe('/Volumes/Media');
|
||||
});
|
||||
|
||||
it('defaults Torrent downloads to the Torrents folder and respects overrides', async () => {
|
||||
const settings = normalizeDownloadLocationSettings({
|
||||
baseDownloadFolder: '/Users/test/Downloads'
|
||||
});
|
||||
|
||||
expect(settings.categorySubfolders.Torrents).toBe('Torrents');
|
||||
expect(await resolveCategoryDestination(settings, 'Torrents'))
|
||||
.toBe('/Users/test/Downloads/Torrents');
|
||||
|
||||
settings.categoryDirectoryOverrides.Torrents = '/Volumes/Archive/Torrents';
|
||||
expect(await resolveCategoryDestination(settings, 'Torrents'))
|
||||
.toBe('/Volumes/Archive/Torrents');
|
||||
|
||||
settings.categorySubfoldersEnabled = false;
|
||||
expect(await resolveCategoryDestination(settings, 'Torrents'))
|
||||
.toBe('/Users/test/Downloads');
|
||||
});
|
||||
|
||||
it('defaults category subfolders on and sends every category to the base folder when disabled', async () => {
|
||||
const automatic = normalizeDownloadLocationSettings({
|
||||
baseDownloadFolder: '/Users/test/Downloads'
|
||||
|
||||
@@ -19,6 +19,7 @@ export const DOWNLOAD_CATEGORIES: DownloadCategory[] = [
|
||||
'Documents',
|
||||
'Pictures',
|
||||
'Applications',
|
||||
'Torrents',
|
||||
'Other'
|
||||
];
|
||||
|
||||
@@ -29,6 +30,7 @@ export const DEFAULT_CATEGORY_SUBFOLDERS: Record<DownloadCategory, string> = {
|
||||
Documents: 'Documents',
|
||||
Pictures: 'Pictures',
|
||||
Applications: 'Applications',
|
||||
Torrents: 'Torrents',
|
||||
Other: 'Other'
|
||||
};
|
||||
|
||||
@@ -245,6 +247,7 @@ export const normalizeDownloadLocationSettings = (
|
||||
Documents: 'Documents',
|
||||
Pictures: 'Images',
|
||||
Applications: 'Apps',
|
||||
Torrents: 'Torrents',
|
||||
Other: 'Other'
|
||||
};
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ import {
|
||||
downloadMediaKindsMatch,
|
||||
MAX_DOWNLOAD_FILENAME_BYTES,
|
||||
canonicalizeDownloadFileName,
|
||||
categoryForDownload,
|
||||
categoryForFileName,
|
||||
isValidTorrentExcludeTrackerList,
|
||||
isValidTorrentTrackerList,
|
||||
normalizeTorrentEncryptionPolicy,
|
||||
@@ -33,6 +35,17 @@ const item = (status: DownloadItem['status']): DownloadItem => ({
|
||||
totalIsEstimate: false
|
||||
});
|
||||
|
||||
describe('download category detection', () => {
|
||||
it('classifies torrent files and explicit Torrent rows separately from filename types', () => {
|
||||
expect(categoryForFileName('Example.torrent')).toBe('Torrents');
|
||||
expect(categoryForFileName('Example', true)).toBe('Torrents');
|
||||
expect(categoryForFileName('Example.mkv', true)).toBe('Torrents');
|
||||
expect(categoryForFileName('Example.mkv')).toBe('Movies');
|
||||
expect(categoryForDownload('Renamed', true, 'Other')).toBe('Other');
|
||||
expect(categoryForDownload('Renamed', true, 'Torrents')).toBe('Torrents');
|
||||
});
|
||||
});
|
||||
|
||||
describe('download persistence progress snapshots', () => {
|
||||
it('does not write active byte counters on every progress event', () => {
|
||||
const persisted = redactDownloadForPersistence(item('downloading'));
|
||||
|
||||
+14
-1
@@ -417,7 +417,11 @@ export const initMediaDomains = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
export const categoryForFileName = (fileName: string): DownloadCategory => {
|
||||
export const categoryForFileName = (
|
||||
fileName: string,
|
||||
isTorrent = false
|
||||
): DownloadCategory => {
|
||||
if (isTorrent || fileName.trim().toLowerCase().endsWith('.torrent')) return 'Torrents';
|
||||
const ext = fileName.split('.').pop()?.toLowerCase() || '';
|
||||
if (['mp4', 'mkv', 'avi', 'mov', 'wmv', 'flv', 'webm', 'm4v', 'mpeg', 'mpg', '3gp', 'ts', 'vob'].includes(ext)) return 'Movies';
|
||||
if (['mp3', 'wav', 'aac', 'flac', 'ogg', 'm4a', 'wma', 'alac', 'ape', 'mid', 'midi'].includes(ext)) return 'Musics';
|
||||
@@ -428,6 +432,15 @@ export const categoryForFileName = (fileName: string): DownloadCategory => {
|
||||
return 'Other';
|
||||
};
|
||||
|
||||
export const categoryForDownload = (
|
||||
fileName: string,
|
||||
isTorrent: boolean,
|
||||
existingCategory?: DownloadCategory
|
||||
): DownloadCategory => {
|
||||
if (isTorrent && existingCategory === 'Other') return existingCategory;
|
||||
return categoryForFileName(fileName, isTorrent);
|
||||
};
|
||||
|
||||
export const fileNameFromUrl = (rawUrl: string): string => {
|
||||
try {
|
||||
const url = new URL(rawUrl);
|
||||
|
||||
Reference in New Issue
Block a user