Skip to content

Commit

Permalink
Merge pull request #3136 from sircharlo/chokidar
Browse files Browse the repository at this point in the history
feat: support custom path for extra media
  • Loading branch information
sircharlo authored Nov 11, 2024
2 parents 59e0fbe + 9f1feab commit 8c8fa9a
Show file tree
Hide file tree
Showing 22 changed files with 593 additions and 69 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ In addition, M³ itself has been translated into several languages by many volun
<!-- prettier-ignore-start -->
<!-- CROWDIN-TRANSLATIONS-PROGRESS-ACTION-START -->


#### Available

<table><tr><td align="center" valign="top"><img width="30px" height="30px" title="Chinese Simplified" alt="Chinese Simplified" src="https://raw.githubusercontent.com/benjaminjonard/crowdin-translations-progress-action/1.0/flags/zh-CN.png"></div><div align="center" valign="top">97%</td><td align="center" valign="top"><img width="30px" height="30px" title="Dutch" alt="Dutch" src="https://raw.githubusercontent.com/benjaminjonard/crowdin-translations-progress-action/1.0/flags/nl.png"></div><div align="center" valign="top">97%</td><td align="center" valign="top"><img width="30px" height="30px" title="Portuguese" alt="Portuguese" src="https://raw.githubusercontent.com/benjaminjonard/crowdin-translations-progress-action/1.0/flags/pt-PT.png"></div><div align="center" valign="top">97%</td><td align="center" valign="top"><img width="30px" height="30px" title="Slovenian" alt="Slovenian" src="https://raw.githubusercontent.com/benjaminjonard/crowdin-translations-progress-action/1.0/flags/sl.png"></div><div align="center" valign="top">97%</td><td align="center" valign="top"><img width="30px" height="30px" title="Swahili" alt="Swahili" src="https://raw.githubusercontent.com/benjaminjonard/crowdin-translations-progress-action/1.0/flags/sw.png"></div><div align="center" valign="top">97%</td></tr><tr><td align="center" valign="top"><img width="30px" height="30px" title="Swedish" alt="Swedish" src="https://raw.githubusercontent.com/benjaminjonard/crowdin-translations-progress-action/1.0/flags/sv-SE.png"></div><div align="center" valign="top">97%</td><td align="center" valign="top"><img width="30px" height="30px" title="French" alt="French" src="https://raw.githubusercontent.com/benjaminjonard/crowdin-translations-progress-action/1.0/flags/fr.png"></div><div align="center" valign="top">94%</td><td align="center" valign="top"><img width="30px" height="30px" title="Portuguese, Brazilian" alt="Portuguese, Brazilian" src="https://raw.githubusercontent.com/benjaminjonard/crowdin-translations-progress-action/1.0/flags/pt-BR.png"></div><div align="center" valign="top">92%</td><td align="center" valign="top"><img width="30px" height="30px" title="Ukrainian" alt="Ukrainian" src="https://raw.githubusercontent.com/benjaminjonard/crowdin-translations-progress-action/1.0/flags/uk.png"></div><div align="center" valign="top">87%</td><td align="center" valign="top"><img width="30px" height="30px" title="Russian" alt="Russian" src="https://raw.githubusercontent.com/benjaminjonard/crowdin-translations-progress-action/1.0/flags/ru.png"></div><div align="center" valign="top">78%</td></tr><tr><td align="center" valign="top"><img width="30px" height="30px" title="Hungarian" alt="Hungarian" src="https://raw.githubusercontent.com/benjaminjonard/crowdin-translations-progress-action/1.0/flags/hu.png"></div><div align="center" valign="top">71%</td><td align="center" valign="top"><img width="30px" height="30px" title="Estonian" alt="Estonian" src="https://raw.githubusercontent.com/benjaminjonard/crowdin-translations-progress-action/1.0/flags/et.png"></div><div align="center" valign="top">59%</td></table>
Expand Down
1 change: 1 addition & 0 deletions build/icons/folder-multiple-image.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,11 @@
"@sentry/vue": "^8.37.1",
"@vueuse/core": "^11.2.0",
"@vueuse/router": "^11.2.0",
"adm-zip": "^0.5.16",
"better-sqlite3": "^11.5.0",
"cheerio": "^1.0.0",
"chokidar": "^4.0.1",
"countries-and-timezones": "^3.7.2",
"decompress": "^4.2.1",
"dompurify": "^3.2.0",
"electron-dl-manager": "^3.2.1",
"electron-updater": "^6.3.9",
Expand Down Expand Up @@ -75,8 +76,8 @@
"@rollup/plugin-inject": "^5.0.5",
"@sentry/esbuild-plugin": "^2.22.6",
"@sentry/vite-plugin": "^2.22.6",
"@types/adm-zip": "^0.5.6",
"@types/better-sqlite3": "^7.6.11",
"@types/decompress": "^4",
"@types/dompurify": "^3.0.5",
"@types/fs-extra": "^11.0.4",
"@types/heic-convert": "^2.1.0",
Expand Down
3 changes: 2 additions & 1 deletion quasar.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,9 @@ module.exports = configure(function (ctx) {
'@numairawan/video-duration',
'@sentry/electron',
'@sentry/vue',
'adm-zip',
'better-sqlite3',
'chokidar',
'decompress',
'countries-and-timezones',
'electron-dl-manager',
'electron-updater',
Expand Down
6 changes: 5 additions & 1 deletion src-electron/electron-preload.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ElectronApi } from 'src/types';

import decompress from 'decompress';
import { contextBridge, webUtils } from 'electron/renderer';
import fs from 'fs-extra';
import path from 'upath';
Expand All @@ -11,7 +12,6 @@ import {
getNrOfPdfPages,
} from './preload/converters';
import {
decompress,
fileUrlToPath,
getVideoDuration,
isFileUrl,
Expand Down Expand Up @@ -62,8 +62,10 @@ const electronApi: ElectronApi = {
onDownloadStarted: (cb) => listen('downloadStarted', cb),
onLog: (cb) => listen('log', cb),
onShortcut: (cb) => listen('shortcut', cb),
onWatchFolderUpdate: (cb) => listen('watchFolderUpdate', cb),
openExternal: (w) => send('openExternal', w),
openFileDialog: (s, f) => invoke('openFileDialog', s, f),
openFolderDialog: () => invoke('openFolderDialog'),
openWebsiteWindow,
parseMediaFile,
path,
Expand All @@ -76,6 +78,8 @@ const electronApi: ElectronApi = {
setUrlVariables: (v) => send('setUrlVariables', v),
toggleMediaWindow: (s) => send('toggleMediaWindow', s),
unregisterShortcut: (s) => send('unregisterShortcut', s),
unwatchFolders: () => send('unwatchFolders'),
watchFolder: (p) => send('watchFolder', p),
zoomWebsiteWindow,
};

Expand Down
67 changes: 65 additions & 2 deletions src-electron/main/fs.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
import type { FileDialogFilter, FileItem } from 'src/types';

import { watch as filesystemWatch, type FSWatcher } from 'chokidar';
import { dialog } from 'electron';
import { type Dirent, exists, readdir, stat } from 'fs-extra';
import { type Dirent, exists, readdir, stat, type Stats } from 'fs-extra';
import {
IMG_EXTENSIONS,
JWPUB_EXTENSIONS,
PDF_EXTENSIONS,
} from 'src/constants/fs';
import { join } from 'upath';
import { basename, dirname, join, toUnix } from 'upath';

import { errorCatcher } from '../utils';
import { sendToWindow } from './window/window-base';
import { mainWindow } from './window/window-main';

export async function openFolderDialog() {
if (!mainWindow) return;
return dialog.showOpenDialog(mainWindow, {
properties: ['openDirectory'],
});
}

export async function openFileDialog(
single: boolean,
filter: FileDialogFilter,
Expand Down Expand Up @@ -52,6 +61,60 @@ export async function openFileDialog(
});
}

const watchers = new Set<FSWatcher>();
const datePattern = /^\d{4}-\d{2}-\d{2}$/; // YYYY-MM-DD

export async function unwatchFolders() {
for (const watcher of watchers) {
try {
if (!watcher?.closed) await watcher?.close();
watchers.delete(watcher);
} catch (error) {
errorCatcher(error);
}
}
}

export async function watchFolder(folderPath: string) {
watchers.add(
filesystemWatch(folderPath, {
atomic: false,
ignored: (fp: string, stats?: Stats) => {
try {
if (toUnix(folderPath) === fp) return false; // Don't ignore the root folder itself
if (!stats) return false; // Don't ignore anything if no stats are available
const dirPath = toUnix(stats.isDirectory() ? fp : dirname(fp)); // If this isn't a directory, get the parent directory
const dirOfNote = basename(dirPath); // Get the name of the directory
return !datePattern.test(dirOfNote); // Ignore files in a directory whose name doesn't match YYYY-MM-DD
} catch (error) {
errorCatcher(error);
return true;
}
},
ignorePermissionErrors: true,
}).on('all', (event, changedPath, stats) => {
try {
// console.log(event, changedPath);
if (!changedPath || (!stats && !event.includes('unlink'))) return; // Don't do anything if no stats are available or if no path is available
const dirPath = toUnix(
stats?.isDirectory() || event === 'unlinkDir'
? changedPath
: dirname(changedPath),
); // If this isn't a directory, get the parent directory
const dirOfNote = basename(dirPath); // Get the name of the directory
sendToWindow(mainWindow, 'watchFolderUpdate', {
changedPath,
day: dirOfNote,
event,
});
} catch (error) {
errorCatcher(error);
return true;
}
}),
);
}

export async function readDirectory(
dir: string,
withSizes?: boolean,
Expand Down
15 changes: 14 additions & 1 deletion src-electron/main/ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ import {
isSelf,
} from './../utils';
import { downloadFile, isDownloadErrorExpected } from './downloads';
import { openFileDialog, readDirectory } from './fs';
import {
openFileDialog,
openFolderDialog,
readDirectory,
unwatchFolders,
watchFolder,
} from './fs';
import { getAllScreens } from './screen';
import { setUrlVariables } from './session';
import { registerShortcut, unregisterShortcut } from './shortcuts';
Expand Down Expand Up @@ -146,6 +152,11 @@ handleIpcSend('openExternal', (_e, website: ExternalWebsite) => {
if (url) shell.openExternal(url);
});

handleIpcSend('unwatchFolders', () => unwatchFolders());
handleIpcSend('watchFolder', (_e, folderPath: string) =>
watchFolder(folderPath),
);

// IPC invoke/handle

function handleIpcInvoke<T = unknown>(
Expand Down Expand Up @@ -207,6 +218,8 @@ handleIpcInvoke(
openFileDialog(single, filter),
);

handleIpcInvoke('openFolderDialog', async () => openFolderDialog());

handleIpcInvoke('downloadErrorIsExpected', async () =>
isDownloadErrorExpected(),
);
16 changes: 0 additions & 16 deletions src-electron/preload/fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,6 @@ import url from 'url';

import { errorCatcher } from '../utils';

export const decompress = async (inputZip: string, outputFolder: string) => {
const { default: AdmZip } = await import('adm-zip');

const zip = new AdmZip(inputZip);
return new Promise<void>((resolve, reject) => {
zip.extractAllToAsync(outputFolder, true, true, (error) => {
if (error) {
errorCatcher(error);
reject(error);
} else {
resolve();
}
});
});
};

export const getVideoDuration = async (
filePath: string,
): Promise<VideoDuration> => {
Expand Down
2 changes: 2 additions & 0 deletions src/components/form-inputs/BaseInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
:rules="item.rules"
:setting-id="settingId"
/>
<FolderInput v-else-if="item.type === 'path'" v-model="model" />
<SliderInput
v-else-if="item.type === 'slider'"
v-model="model"
Expand Down Expand Up @@ -50,6 +51,7 @@
import type { SettingsItem, SettingsValues } from 'src/types';
import DateInput from 'src/components/form-inputs/DateInput.vue';
import FolderInput from 'src/components/form-inputs/FolderInput.vue';
import SelectInput from 'src/components/form-inputs/SelectInput.vue';
import ShortcutInput from 'src/components/form-inputs/ShortcutInput.vue';
import SliderInput from 'src/components/form-inputs/SliderInput.vue';
Expand Down
46 changes: 46 additions & 0 deletions src/components/form-inputs/FolderInput.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<template>
<q-btn
:no-caps="!!model"
color="primary"
hide-bottom-space
icon="mmm-folder-multiple-image"
outline
style="max-width: 240px"
@click="showFolderPicker"
><div class="ellipsis q-ml-sm">
{{ displayFolderName || t('choose-a-folder') }}
</div>
<q-tooltip v-if="!!model" :delay="1000">{{ model }}</q-tooltip>
</q-btn>
</template>

<script setup lang="ts">
import { errorCatcher } from 'src/helpers/error-catcher';
import { computed } from 'vue';
import { useI18n } from 'vue-i18n';
const { openFolderDialog, path } = window.electronApi;
const displayFolderName = computed(() => {
return model.value ? path.basename(model.value) : '';
});
const { t } = useI18n();
const model = defineModel<null | string>({ required: true });
const showFolderPicker = async () => {
try {
const result = await openFolderDialog().catch((error) => {
errorCatcher(error);
});
if (!result || !result.filePaths || result.canceled) {
model.value = null;
} else if (result.filePaths.length > 0) {
model.value = result.filePaths[0];
}
} catch (error) {
errorCatcher(error);
model.value = null;
}
};
</script>
14 changes: 14 additions & 0 deletions src/constants/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,18 @@ export const settingsDefinitions: SettingsItems = {
subgroup: 'cache',
type: 'toggle',
},
enableFolderWatcher: {
depends: 'enableMediaDisplayButton',
group: 'mediaRetrievalPlayback',
subgroup: 'folderWatcher',
type: 'toggle',
},
folderToWatch: {
depends: ['enableFolderWatcher', 'enableMediaDisplayButton'],
group: 'mediaRetrievalPlayback',
subgroup: 'folderWatcher',
type: 'path',
},

// Integrations
obsEnable: {
Expand Down Expand Up @@ -300,13 +312,15 @@ export const defaultSettings: SettingsValues = {
darkMode: 'auto',
disableMediaFetching: false,
enableExtraCache: false,
enableFolderWatcher: false,
enableKeyboardShortcuts: false,
enableMediaDisplayButton: false,
enableMusicButton: true,
enableSubtitles: false,
excludeFootnotes: false,
excludeTh: true,
firstDayOfWeek: 0,
folderToWatch: '',
hideMediaLogo: false,
includePrinted: true,
lang: 'E',
Expand Down
20 changes: 17 additions & 3 deletions src/helpers/fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,11 @@ const getThumbnailFromMetadata = async (mediaPath: string) => {
return '';
}
} catch (error) {
errorCatcher(error, {
contexts: { fn: { mediaPath, name: 'getThumbnailFromMetadata' } },
});
if (!mediaPath?.toLowerCase().endsWith('.mov')) {
errorCatcher(error, {
contexts: { fn: { mediaPath, name: 'getThumbnailFromMetadata' } },
});
}
return '';
}
};
Expand Down Expand Up @@ -354,6 +356,17 @@ const enableUpdates = async () => {
}
};

const watchExternalFolder = async (folder?: string) => {
try {
const currentState = useCurrentStateStore();
currentState.watchFolderMedia = {};
window.electronApi.unwatchFolders();
if (folder) window.electronApi.watchFolder(folder);
} catch (error) {
errorCatcher(error);
}
};

export {
disableUpdates,
enableUpdates,
Expand All @@ -371,4 +384,5 @@ export {
getThumbnailUrl,
removeEmptyDirs,
updatesDisabled,
watchExternalFolder,
};
Loading

0 comments on commit 8c8fa9a

Please sign in to comment.