Skip to content

Commit

Permalink
Migrated from deprecated registerFileProtocol function
Browse files Browse the repository at this point in the history
  • Loading branch information
Sandakan committed Jan 14, 2025
1 parent 6491939 commit 2a12148
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 22 deletions.
28 changes: 22 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@
"electron-store": "^10.0.0",
"electron-updater": "^6.2.1",
"i18next": "^24.2.0",
"mime": "^4.0.6",
"music-metadata": "^10.0.1",
"node-id3": "^0.2.6",
"node-vibrant": "^4.0.0",
Expand Down
99 changes: 88 additions & 11 deletions src/main/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import path, { join } from 'path';
import fs from 'fs';
import os from 'os';
import {
app,
Expand All @@ -22,6 +23,7 @@ import {
// session
} from 'electron';
import debug from 'electron-debug';
import mime from 'mime/lite';
// import { pathToFileURL } from 'url';

// import * as Sentry from '@sentry/electron';
Expand Down Expand Up @@ -55,6 +57,7 @@ import { is } from '@electron-toolkit/utils';
import noraAppIcon from '../../resources/logo_light_mode.png?asset';
import logger from './logger';
import roundTo from '../common/roundTo';
import { isAnErrorWithCode } from './utils/isAnErrorWithCode';

// / / / / / / / CONSTANTS / / / / / / / / /
const DEFAULT_APP_PROTOCOL = 'nora';
Expand Down Expand Up @@ -261,7 +264,8 @@ app
// })
// }
// })
protocol.registerFileProtocol('nora', registerFileProtocol);
// protocol.registerFileProtocol('nora', registerFileProtocol);
protocol.handle('nora', handleFileProtocol);

tray = new Tray(appIcon);
const trayContextMenu = Menu.buildFromTemplate([
Expand Down Expand Up @@ -439,20 +443,93 @@ function addEventsToCache(dataType: DataUpdateEventTypes, data = [] as string[],
return dataEventsCache.push(obj);
}

function registerFileProtocol(request: { url: string }, callback: (arg: string) => void) {
const urlWithQueries = decodeURI(request.url).replace(
/nora:[/\\]{1,2}localfiles[/\\]{1,2}/gm,
''
);
// function registerFileProtocol(request: { url: string }, callback: (arg: string) => void) {
// const urlWithQueries = decodeURI(request.url).replace(
// /nora:[/\\]{1,2}localfiles[/\\]{1,2}/gm,
// ''
// );

// try {
// const [url] = urlWithQueries.split('?');
// return callback(url);
// } catch (error) {
// logger.error(`Failed to locate a resource in the system.`, { urlWithQueries, error });
// return callback('404');
// }
// }

const handleFileProtocol = async (request: GlobalRequest): Promise<GlobalResponse> => {
try {
const [url] = urlWithQueries.split('?');
return callback(url);
const urlWithQueries = decodeURI(request.url).replace(
/nora:[/\\]{1,2}localfiles[/\\]{1,2}/gm,
''
);
const [filePath] = urlWithQueries.split('?');

logger.verbose('Serving file from nora://', { filePath });

if (!fs.existsSync(filePath)) {
logger.error(`File not found: ${filePath}`);
return new Response('File not found', { status: 404 });
}

const fileStat = fs.statSync(filePath);
const range = request.headers.get('range');
let start = 0,
end = fileStat.size - 1;

if (range) {
const match = range.match(/bytes=(\d*)-(\d*)/);
if (match) {
start = match[1] ? parseInt(match[1], 10) : start;
end = match[2] ? parseInt(match[2], 10) : end;
}
}

const chunkSize = end - start + 1;
logger.verbose(`Serving range: ${start}-${end}/${fileStat.size}`);

const mimeType = mime.getType(filePath) || 'application/octet-stream';

// Create a readable stream with proper event handling
const stream = new ReadableStream({
start(controller) {
const fileStream = fs.createReadStream(filePath, { start, end });

fileStream.on('data', (chunk) => {
try {
controller.enqueue(chunk);
} catch (error) {
if (isAnErrorWithCode(error) && error.code !== 'ERR_INVALID_STATE')
logger.warn('Attempted to enqueue after stream closed:', { error });
}
});

fileStream.on('end', () => {
if (controller.desiredSize) {
controller.close();
}
});

fileStream.on('error', (err) => {
logger.error('Stream error:', err);
controller.error(err);
});
}
});

const headers = new Headers();
headers.set('Content-Type', mimeType);
headers.set('Content-Range', `bytes ${start}-${end}/${fileStat.size}`);
headers.set('Accept-Ranges', 'bytes');
headers.set('Content-Length', chunkSize.toString());

return new Response(stream, { headers });
} catch (error) {
logger.error(`Failed to locate a resource in the system.`, { urlWithQueries, error });
return callback('404');
logger.error('Error handling media protocol:', { error });
return new Response('Internal Server Error', { status: 500 });
}
}
};

export const setCurrentSongPath = (songPath: string) => {
currentSongPath = songPath;
Expand Down
6 changes: 1 addition & 5 deletions src/main/other/artworks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,16 +136,12 @@ export const removeSongArtworkFromUnknownSource = async (artworkPath: string) =>
}
};

const isError = (error: unknown): error is Error => {
return error instanceof Error;
};

const createTempFolder = async (folderPath: string) => {
try {
await fs.stat(folderPath);
return true;
} catch (error) {
if (isError(error) && 'code' in error && error.code === 'ENOENT') {
if (isAnErrorWithCode(error) && error.code === 'ENOENT') {
await fs.mkdir(folderPath);
return true;
}
Expand Down

0 comments on commit 2a12148

Please sign in to comment.