mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-08-16 01:55:51 -07:00
fix(overlay): support native Wayland file drag-and-drop (#199)
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
type: fixed
|
||||||
|
area: overlay
|
||||||
|
|
||||||
|
- Fixed native Wayland drag-and-drop from file managers such as Thunar so subtitle and video files dropped on the visible overlay are resolved and forwarded to mpv.
|
||||||
@@ -30,6 +30,16 @@ test('collectDroppedVideoPaths keeps supported dropped file paths in order', ()
|
|||||||
assert.deepEqual(result, ['/videos/ep02.mkv', '/videos/ep03.MP4']);
|
assert.deepEqual(result, ['/videos/ep02.mkv', '/videos/ep03.MP4']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('collectDroppedVideoPaths accepts paths resolved from standard Web File objects', () => {
|
||||||
|
const transfer = makeTransfer({
|
||||||
|
files: [{ name: 'ep02.mkv' }, { name: 'notes.txt' }],
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = collectDroppedVideoPaths(transfer, ['/videos/ep02.mkv', '/videos/notes.txt']);
|
||||||
|
|
||||||
|
assert.deepEqual(result, ['/videos/ep02.mkv']);
|
||||||
|
});
|
||||||
|
|
||||||
test('collectDroppedVideoPaths parses text/uri-list entries and de-duplicates', () => {
|
test('collectDroppedVideoPaths parses text/uri-list entries and de-duplicates', () => {
|
||||||
const transfer = makeTransfer({
|
const transfer = makeTransfer({
|
||||||
getData: (format: string) =>
|
getData: (format: string) =>
|
||||||
@@ -53,6 +63,20 @@ test('collectDroppedSubtitlePaths keeps supported dropped subtitle paths in orde
|
|||||||
assert.deepEqual(result, ['/subs/ep02.ass', '/subs/ep03.SRT']);
|
assert.deepEqual(result, ['/subs/ep02.ass', '/subs/ep03.SRT']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('collectDroppedSubtitlePaths accepts paths resolved from standard Web File objects', () => {
|
||||||
|
const transfer = makeTransfer({
|
||||||
|
files: [{ name: 'ep02.ass' }, { name: 'readme.txt' }, { name: 'ep03.SRT' }],
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = collectDroppedSubtitlePaths(transfer, [
|
||||||
|
'/subs/ep02.ass',
|
||||||
|
'/subs/readme.txt',
|
||||||
|
'/subs/ep03.SRT',
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert.deepEqual(result, ['/subs/ep02.ass', '/subs/ep03.SRT']);
|
||||||
|
});
|
||||||
|
|
||||||
test('collectDroppedSubtitlePaths parses text/uri-list entries and de-duplicates', () => {
|
test('collectDroppedSubtitlePaths parses text/uri-list entries and de-duplicates', () => {
|
||||||
const transfer = makeTransfer({
|
const transfer = makeTransfer({
|
||||||
getData: (format: string) =>
|
getData: (format: string) =>
|
||||||
|
|||||||
@@ -93,18 +93,21 @@ export function parseClipboardVideoPath(text: string): string | null {
|
|||||||
|
|
||||||
export function collectDroppedVideoPaths(
|
export function collectDroppedVideoPaths(
|
||||||
dataTransfer: DropDataTransferLike | null | undefined,
|
dataTransfer: DropDataTransferLike | null | undefined,
|
||||||
|
resolvedFilePaths: ArrayLike<string> = [],
|
||||||
): string[] {
|
): string[] {
|
||||||
return collectDroppedPaths(dataTransfer, isSupportedVideoPath);
|
return collectDroppedPaths(dataTransfer, resolvedFilePaths, isSupportedVideoPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function collectDroppedSubtitlePaths(
|
export function collectDroppedSubtitlePaths(
|
||||||
dataTransfer: DropDataTransferLike | null | undefined,
|
dataTransfer: DropDataTransferLike | null | undefined,
|
||||||
|
resolvedFilePaths: ArrayLike<string> = [],
|
||||||
): string[] {
|
): string[] {
|
||||||
return collectDroppedPaths(dataTransfer, isSupportedSubtitlePath);
|
return collectDroppedPaths(dataTransfer, resolvedFilePaths, isSupportedSubtitlePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
function collectDroppedPaths(
|
function collectDroppedPaths(
|
||||||
dataTransfer: DropDataTransferLike | null | undefined,
|
dataTransfer: DropDataTransferLike | null | undefined,
|
||||||
|
resolvedFilePaths: ArrayLike<string>,
|
||||||
isSupportedPath: (pathValue: string) => boolean,
|
isSupportedPath: (pathValue: string) => boolean,
|
||||||
): string[] {
|
): string[] {
|
||||||
if (!dataTransfer) return [];
|
if (!dataTransfer) return [];
|
||||||
@@ -124,6 +127,7 @@ function collectDroppedPaths(
|
|||||||
for (let i = 0; i < dataTransfer.files.length; i += 1) {
|
for (let i = 0; i < dataTransfer.files.length; i += 1) {
|
||||||
const file = dataTransfer.files[i] as { path?: string } | undefined;
|
const file = dataTransfer.files[i] as { path?: string } | undefined;
|
||||||
addPath(file?.path);
|
addPath(file?.path);
|
||||||
|
addPath(resolvedFilePaths[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -16,7 +16,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron';
|
import { contextBridge, ipcRenderer, IpcRendererEvent, webUtils } from 'electron';
|
||||||
import { resolveOverlayLayerFromArgv } from './preload-args';
|
import { resolveOverlayLayerFromArgv } from './preload-args';
|
||||||
import type {
|
import type {
|
||||||
SubtitleData,
|
SubtitleData,
|
||||||
@@ -248,6 +248,7 @@ const onSecondarySubtitleModeEvent = createLatestValueIpcListenerWithPayload<Sec
|
|||||||
|
|
||||||
const electronAPI: ElectronAPI = {
|
const electronAPI: ElectronAPI = {
|
||||||
getOverlayLayer: () => overlayLayer,
|
getOverlayLayer: () => overlayLayer,
|
||||||
|
getPathForFile: (file: File) => webUtils.getPathForFile(file),
|
||||||
onSubtitle: (callback: (data: SubtitleData) => void) => {
|
onSubtitle: (callback: (data: SubtitleData) => void) => {
|
||||||
onSubtitleSetEvent(callback);
|
onSubtitleSetEvent(callback);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -899,8 +899,11 @@ function setupDragDropToMpvQueue(): void {
|
|||||||
if (!event.dataTransfer) return;
|
if (!event.dataTransfer) return;
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const droppedVideoPaths = collectDroppedVideoPaths(event.dataTransfer);
|
const resolvedFilePaths = Array.from(event.dataTransfer.files, (file) =>
|
||||||
const droppedSubtitlePaths = collectDroppedSubtitlePaths(event.dataTransfer);
|
window.electronAPI.getPathForFile(file),
|
||||||
|
);
|
||||||
|
const droppedVideoPaths = collectDroppedVideoPaths(event.dataTransfer, resolvedFilePaths);
|
||||||
|
const droppedSubtitlePaths = collectDroppedSubtitlePaths(event.dataTransfer, resolvedFilePaths);
|
||||||
const appendDroppedVideos = event.shiftKey;
|
const appendDroppedVideos = event.shiftKey;
|
||||||
const loadCommands = buildMpvLoadfileCommands(droppedVideoPaths, appendDroppedVideos);
|
const loadCommands = buildMpvLoadfileCommands(droppedVideoPaths, appendDroppedVideos);
|
||||||
const subtitleCommands = buildMpvSubtitleAddCommands(droppedSubtitlePaths);
|
const subtitleCommands = buildMpvSubtitleAddCommands(droppedSubtitlePaths);
|
||||||
|
|||||||
@@ -425,6 +425,7 @@ export interface SessionNumericSelectionStartPayload {
|
|||||||
|
|
||||||
export interface ElectronAPI {
|
export interface ElectronAPI {
|
||||||
getOverlayLayer: () => 'visible' | 'modal' | null;
|
getOverlayLayer: () => 'visible' | 'modal' | null;
|
||||||
|
getPathForFile: (file: File) => string;
|
||||||
onSubtitle: (callback: (data: SubtitleData) => void) => void;
|
onSubtitle: (callback: (data: SubtitleData) => void) => void;
|
||||||
onOverlayPointerRecoveryRequested: (callback: () => void) => void;
|
onOverlayPointerRecoveryRequested: (callback: () => void) => void;
|
||||||
onOverlayNotification: (callback: (payload: OverlayNotificationEventPayload) => void) => void;
|
onOverlayNotification: (callback: (payload: OverlayNotificationEventPayload) => void) => void;
|
||||||
|
|||||||
Reference in New Issue
Block a user