diff --git a/src/main/index.ts b/src/main/index.ts index 2b406cc..0c16779 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -39,6 +39,8 @@ export * as configStore from './configStore.js'; export { isLinux, isWindows, isMac, platform } from './util.js'; +export { pathToFileURL } from 'node:url'; + // https://www.i18next.com/overview/typescript#argument-of-type-defaulttfuncreturn-is-not-assignable-to-parameter-of-type-xyz // todo This should not be necessary anymore since v23.0.0 diff --git a/src/main/pathToFileURL.test.ts b/src/main/pathToFileURL.test.ts new file mode 100644 index 0000000..e35f152 --- /dev/null +++ b/src/main/pathToFileURL.test.ts @@ -0,0 +1,49 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import { test, expect, describe } from 'vitest'; + +import { pathToFileURL } from 'node:url'; + + +if (process.platform === 'win32') { + describe('file uri windows only', () => { + test('converts path to file url', () => { + expect(pathToFileURL('C:\\Users\\sindresorhus\\dev\\te^st.jpg').href).toEqual('file:///C:/Users/sindresorhus/dev/te^st.jpg'); + }); + }); +} else { + describe('file uri non-windows', () => { + // https://github.com/mifi/lossless-cut/issues/1941 + test('file with backslash', () => { + expect(pathToFileURL('/has/back\\slash').href).toEqual('file:///has/back%5Cslash'); + }); + }); +} + +// taken from https://github.com/sindresorhus/file-url +describe('file uri both platforms', () => { + test('converts path to file url', () => { + expect(pathToFileURL('/test.jpg').href).toMatch(/file:\/{3}test\.jpg/); + + expect(pathToFileURL('/Users/sindresorhus/dev/te^st.jpg').href).toEqual('file:///Users/sindresorhus/dev/te^st.jpg'); + }); + + test('escapes more special characters in path', () => { + expect(pathToFileURL('/a^?!@#$%&\'";<>').href).toEqual('file:///a^%3F!@%23$%25&\'%22;%3C%3E'); + }); + + test('escapes whitespace characters in path', () => { + expect(pathToFileURL('/file with\r\nnewline').href).toEqual('file:///file%20with%0D%0Anewline'); + }); + + test('relative path', () => { + expect(pathToFileURL('relative/test.jpg').href).toMatch(/^file:\/{3}.*\/relative\/test\.jpg$/); + }); + + test('slash', () => { + expect(pathToFileURL('/').href).toEqual('file:///'); + }); + + test('empty', () => { + expect(pathToFileURL('').href).toMatch(/^file:\/{3}.*$/); + }); +}); diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index ce88b1b..5a470d5 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -70,7 +70,7 @@ import { isStoreBuild, dragPreventer, havePermissionToReadFile, resolvePathIfNeeded, getPathReadAccessError, html5ifiedPrefix, html5dummySuffix, findExistingHtml5FriendlyFile, deleteFiles, isOutOfSpaceError, isExecaFailure, readFileSize, readFileSizes, checkFileSizes, setDocumentTitle, getOutFileExtension, getSuffixedFileName, mustDisallowVob, readVideoTs, readDirRecursively, getImportProjectType, - calcShouldShowWaveform, calcShouldShowKeyframes, mediaSourceQualities, isWindows, + calcShouldShowWaveform, calcShouldShowKeyframes, mediaSourceQualities, } from './util'; import { toast, errorToast } from './swal'; import { formatDuration } from './util/duration'; @@ -89,14 +89,13 @@ import isDev from './isDev'; import { ChromiumHTMLVideoElement, EdlFileType, FfmpegCommandLog, FormatTimecode, PlaybackMode, SegmentColorIndex, SegmentTags, SegmentToExport, StateSegment, Thumbnail, TunerType } from './types'; import { CaptureFormat, KeyboardAction, Html5ifyMode } from '../../../types'; import { FFprobeChapter, FFprobeFormat, FFprobeStream } from '../../../ffprobe'; -import filePathToUrl from './util/fileUri'; const electron = window.require('electron'); const { exists } = window.require('fs-extra'); const { lstat } = window.require('fs/promises'); const { parse: parsePath, join: pathJoin, basename, dirname } = window.require('path'); -const { focusWindow, hasDisabledNetworking, quitApp } = window.require('@electron/remote').require('./index.js'); +const { focusWindow, hasDisabledNetworking, quitApp, pathToFileURL } = window.require('@electron/remote').require('./index.js'); const videoStyle: CSSProperties = { width: '100%', height: '100%', objectFit: 'contain' }; @@ -449,7 +448,7 @@ function App() { const effectiveFilePath = previewFilePath || filePath; const fileUri = useMemo(() => { if (!effectiveFilePath) return ''; // Setting video src="" prevents memory leak in chromium - const uri = filePathToUrl(effectiveFilePath, isWindows); + const uri = pathToFileURL(effectiveFilePath).href; // https://github.com/mifi/lossless-cut/issues/1674 if (cacheBuster !== 0) { const qs = new URLSearchParams(); diff --git a/src/renderer/src/util/fileUri.test.ts b/src/renderer/src/util/fileUri.test.ts deleted file mode 100644 index 5f98a58..0000000 --- a/src/renderer/src/util/fileUri.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { test, expect, describe } from 'vitest'; - -import fileUriRaw from './fileUri.js'; - - -describe('file uri windows only', () => { - test('converts path to file url', () => { - expect(fileUriRaw('C:\\Users\\sindresorhus\\dev\\te^st.jpg', true)).toEqual('file:///C:/Users/sindresorhus/dev/te%5Est.jpg'); - }); -}); - -describe('file uri non-windows', () => { - // https://github.com/mifi/lossless-cut/issues/1941 - test('file with backslash', () => { - expect(fileUriRaw('/has/back\\slash', false)).toEqual('file:///has/back%5Cslash'); - }); -}); - -// taken from https://github.com/sindresorhus/file-url -describe.each([{ isWindows: false }, { isWindows: true }])('file uri both platforms isWindows=$isWindows', ({ isWindows }) => { - const fileUri = (path) => fileUriRaw(path, isWindows); - - test('converts path to file url', () => { - expect(fileUri('/test.jpg')).toMatch(/file:\/{3}test\.jpg/); - - expect(fileUri('/Users/sindresorhus/dev/te^st.jpg')).toEqual('file:///Users/sindresorhus/dev/te%5Est.jpg'); - }); - - test('escapes more special characters in path', () => { - expect(fileUri('/a?!@#$%^&\'";<>')).toEqual('file:///a%3F!@%23$%25%5E&\'%22;%3C%3E'); - }); - - test('escapes whitespace characters in path', () => { - expect(fileUri('/file with\r\nnewline')).toEqual('file:///file%20with%0D%0Anewline'); - }); - - test('relative path', () => { - expect(fileUri('relative/test.jpg')).toEqual('file:///relative/test.jpg'); - }); - - test('empty', () => { - expect(fileUri('')).toEqual('file:///'); - }); - - test('slash', () => { - expect(fileUri('/')).toEqual('file:///'); - }); -}); diff --git a/src/renderer/src/util/fileUri.ts b/src/renderer/src/util/fileUri.ts deleted file mode 100644 index df955ee..0000000 --- a/src/renderer/src/util/fileUri.ts +++ /dev/null @@ -1,17 +0,0 @@ -export default function fileUri(filePath: string, isWindows: boolean) { - let pathName = filePath; - - if (isWindows) { - pathName = pathName.replaceAll('\\', '/'); - } - - // Windows drive letter must be prefixed with a slash. - // also relative paths will be converted to absolute - if (pathName[0] !== '/') { - pathName = `/${pathName}`; - } - - // Escape required characters for path components. - // See: https://tools.ietf.org/html/rfc3986#section-3.3 - return encodeURI(`file://${pathName}`).replaceAll(/[#?]/g, encodeURIComponent); -}