mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-02 06:22:42 -08:00
131 lines
3.3 KiB
TypeScript
131 lines
3.3 KiB
TypeScript
export type DropFileLike = { path?: string } | { name: string };
|
|
|
|
export interface DropDataTransferLike {
|
|
files?: ArrayLike<DropFileLike>;
|
|
getData?: (format: string) => string;
|
|
}
|
|
|
|
const VIDEO_EXTENSIONS = new Set([
|
|
'.3gp',
|
|
'.avi',
|
|
'.flv',
|
|
'.m2ts',
|
|
'.m4v',
|
|
'.mkv',
|
|
'.mov',
|
|
'.mp4',
|
|
'.mpeg',
|
|
'.mpg',
|
|
'.mts',
|
|
'.ts',
|
|
'.webm',
|
|
'.wmv',
|
|
]);
|
|
|
|
function getPathExtension(pathValue: string): string {
|
|
const normalized = pathValue.split(/[?#]/, 1)[0] ?? '';
|
|
const dot = normalized.lastIndexOf('.');
|
|
return dot >= 0 ? normalized.slice(dot).toLowerCase() : '';
|
|
}
|
|
|
|
function isSupportedVideoPath(pathValue: string): boolean {
|
|
return VIDEO_EXTENSIONS.has(getPathExtension(pathValue));
|
|
}
|
|
|
|
function parseUriList(data: string): string[] {
|
|
if (!data.trim()) return [];
|
|
const out: string[] = [];
|
|
|
|
for (const line of data.split(/\r?\n/)) {
|
|
const trimmed = line.trim();
|
|
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
if (!trimmed.toLowerCase().startsWith('file://')) continue;
|
|
|
|
try {
|
|
const parsed = new URL(trimmed);
|
|
let filePath = decodeURIComponent(parsed.pathname);
|
|
if (/^\/[A-Za-z]:\//.test(filePath)) {
|
|
filePath = filePath.slice(1);
|
|
}
|
|
if (filePath && isSupportedVideoPath(filePath)) {
|
|
out.push(filePath);
|
|
}
|
|
} catch {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
export function parseClipboardVideoPath(text: string): string | null {
|
|
const trimmed = text.trim();
|
|
if (!trimmed) return null;
|
|
|
|
const unquoted =
|
|
(trimmed.startsWith('"') && trimmed.endsWith('"')) ||
|
|
(trimmed.startsWith("'") && trimmed.endsWith("'"))
|
|
? trimmed.slice(1, -1).trim()
|
|
: trimmed;
|
|
if (!unquoted) return null;
|
|
|
|
if (unquoted.toLowerCase().startsWith('file://')) {
|
|
try {
|
|
const parsed = new URL(unquoted);
|
|
let filePath = decodeURIComponent(parsed.pathname);
|
|
if (/^\/[A-Za-z]:\//.test(filePath)) {
|
|
filePath = filePath.slice(1);
|
|
}
|
|
return filePath && isSupportedVideoPath(filePath) ? filePath : null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return isSupportedVideoPath(unquoted) ? unquoted : null;
|
|
}
|
|
|
|
export function collectDroppedVideoPaths(dataTransfer: DropDataTransferLike | null | undefined): string[] {
|
|
if (!dataTransfer) return [];
|
|
|
|
const out: string[] = [];
|
|
const seen = new Set<string>();
|
|
|
|
const addPath = (candidate: string | null | undefined): void => {
|
|
if (!candidate) return;
|
|
const trimmed = candidate.trim();
|
|
if (!trimmed || !isSupportedVideoPath(trimmed) || seen.has(trimmed)) return;
|
|
seen.add(trimmed);
|
|
out.push(trimmed);
|
|
};
|
|
|
|
if (dataTransfer.files) {
|
|
for (let i = 0; i < dataTransfer.files.length; i += 1) {
|
|
const file = dataTransfer.files[i] as { path?: string } | undefined;
|
|
addPath(file?.path);
|
|
}
|
|
}
|
|
|
|
if (typeof dataTransfer.getData === 'function') {
|
|
for (const pathValue of parseUriList(dataTransfer.getData('text/uri-list'))) {
|
|
addPath(pathValue);
|
|
}
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
export function buildMpvLoadfileCommands(
|
|
paths: string[],
|
|
append: boolean,
|
|
): Array<(string | number)[]> {
|
|
if (append) {
|
|
return paths.map((pathValue) => ['loadfile', pathValue, 'append']);
|
|
}
|
|
return paths.map((pathValue, index) => [
|
|
'loadfile',
|
|
pathValue,
|
|
index === 0 ? 'replace' : 'append',
|
|
]);
|
|
}
|