diff --git a/.vscode/i18n-ally-reviews.yml b/.vscode/i18n-ally-reviews.yml
new file mode 100644
index 00000000..b220e963
--- /dev/null
+++ b/.vscode/i18n-ally-reviews.yml
@@ -0,0 +1,14 @@
+# Review comments generated by i18n-ally. Please commit this file.
+
+reviews:
+ autoStartMusic:
+ locales:
+ en-US:
+ comments:
+ - user:
+ name: Olivier Savignac
+ email: sircharlo@gmail.com
+ id: CzFh4-s2naOGW4eTXNs-f
+ type: approve
+ comment: ''
+ time: '2024-06-15T18:19:27.506Z'
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 616c5644..9f379ce8 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -90,5 +90,8 @@
"&\n currentSettings.congregationName) ||\n $t('titles.profileSelection')\n }}\n ",
"&\n currentSettings ",
"Meeting Media Manager"
- ]
+ ],
+ "[xml]": {
+ "editor.defaultFormatter": "redhat.vscode-xml"
+ }
}
diff --git a/public/obs-icon.svg b/public/obs-icon.svg
new file mode 100644
index 00000000..fa106b97
--- /dev/null
+++ b/public/obs-icon.svg
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/src-electron/electron-main.ts b/src-electron/electron-main.ts
index bcac1d1d..f832092c 100644
--- a/src-electron/electron-main.ts
+++ b/src-electron/electron-main.ts
@@ -149,7 +149,9 @@ function createWindow() {
});
enable(mediaWindow.webContents);
}
- mainWindow.loadURL(process.env.APP_URL + '?page=congregation-selector');
+ mainWindow.loadURL(
+ process.env.APP_URL + '?page=initial-congregation-selector',
+ );
mediaWindow.loadURL(process.env.APP_URL + '?page=media-player');
}
diff --git a/src-electron/electron-preload.ts b/src-electron/electron-preload.ts
index eb7e4698..ab9bfeda 100644
--- a/src-electron/electron-preload.ts
+++ b/src-electron/electron-preload.ts
@@ -196,5 +196,16 @@ contextBridge.exposeInMainWorld('electronApi', {
});
},
path,
+ setMediaWindowPosition: (x: number, y: number) => {
+ const mediaWindow = getMediaWindow();
+ if (mediaWindow) {
+ mediaWindow.setPosition(x, y);
+ }
+ },
+ setautoStartAtLogin: (value: boolean) => {
+ app.setLoginItemSettings({
+ openAtLogin: value,
+ });
+ },
toggleMediaWindow,
});
diff --git a/src/components/media/MusicButton.vue b/src/components/media/MusicButton.vue
index 47ce1975..2ffbb647 100644
--- a/src/components/media/MusicButton.vue
+++ b/src/components/media/MusicButton.vue
@@ -72,7 +72,7 @@ import { storeToRefs } from 'pinia';
import { date } from 'quasar';
import { getFileUrl, getPublicationDirectoryContents } from 'src/helpers/fs';
import { formatTime } from 'src/helpers/mediaPlayback';
-import { computed, ref } from 'vue';
+import { computed, onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useCurrentStateStore } from '../../stores/current-state';
@@ -129,7 +129,8 @@ const getNextSongUrl = () => {
function playMusic() {
if (!musicPlayer.value || !musicPlayerSource.value) return;
- musicPlayer.value.volume = 1;
+ musicPlayer.value.volume =
+ (getSettingValue('musicVolume') as number) / 100 ?? 1;
musicPlayerSource.value.src = getNextSongUrl();
musicPlayer.value.load();
musicPlayer.value.play().then(() => {
@@ -158,16 +159,24 @@ function playMusic() {
};
}
+const meetingDay = ref(false);
+
+onMounted(() => {
+ meetingDay.value = !!selectedDateObject.value?.meeting;
+ if (currentSettings.value?.autoStartMusic && meetingDay.value) {
+ playMusic();
+ }
+});
+
/**
* Calculates the remaining time before the meeting starts based on the selected meeting day and start time settings.
*
* @return {string|null} The remaining time in hours and minutes, optionally formatted, or null if there is no meeting day selected.
*/
const remainingTimeBeforeMeetingStart = (formatted?: boolean) => {
- const meetingDay = selectedDateObject.value.meeting;
- if (meetingDay) {
+ if (meetingDay.value) {
const now = new Date();
- const weMeeting = meetingDay === 'we';
+ const weMeeting = selectedDateObject.value?.meeting === 'we';
const meetingStartTime = weMeeting
? (getSettingValue('weStartTime') as string)
: (getSettingValue('mwStartTime') as string);
@@ -192,10 +201,7 @@ const remainingTimeBeforeMeetingStart = (formatted?: boolean) => {
const musicRemainingTime = computed(() => {
if (!musicPlayer.value) return '..:..';
if (musicStopping.value) return ref(t('music.stopping')).value;
- if (
- selectedDateObject.value.meeting &&
- timeRemainingBeforeMusicStop.value > 0
- )
+ if (meetingDay.value && timeRemainingBeforeMusicStop.value > 0)
return formatTime(timeRemainingBeforeMusicStop.value);
return currentSongRemainingTime.value;
});
diff --git a/src/components/media/ObsStatus.vue b/src/components/media/ObsStatus.vue
index 9a15f868..678eaca4 100644
--- a/src/components/media/ObsStatus.vue
+++ b/src/components/media/ObsStatus.vue
@@ -6,36 +6,16 @@
rounded
v-if="currentSettings.obsEnable"
>
-
- {{ $t(obsMessage) }}
+
+ {{ $t(obsMessage) }}
',
},
- obsImageScene: {
+ obsPassword: {
+ actions: ['obsConnect'],
depends: 'obsEnable',
group: 'integrations',
- list: 'obsNonStageScenes',
- type: 'list',
+ type: 'text',
},
-
- obsMediaScene: {
+ obsPort: {
+ actions: ['obsConnect'],
+ depends: 'obsEnable',
+ group: 'integrations',
+ type: 'text',
+ },
+ obsCameraScene: {
depends: 'obsEnable',
group: 'integrations',
list: 'obsAllScenes',
type: 'list',
},
- obsPassword: {
- actions: ['obsConnect'],
+ obsMediaScene: {
depends: 'obsEnable',
group: 'integrations',
- type: 'text',
+ list: 'obsAllScenes',
+ type: 'list',
},
- obsPort: {
- actions: ['obsConnect'],
+ obsImageScene: {
depends: 'obsEnable',
group: 'integrations',
- type: 'text',
+ list: 'obsNonStageScenes',
+ type: 'list',
},
- preferredOutput: {
+
+ // Advanced
+
+ // todo: implement preferredOutput
+ // preferredOutput: {
+ // depends: 'enableMediaDisplayButton',
+ // group: 'advanced',
+ // list: 'screens',
+ // type: 'list',
+ // },
+ maxRes: {
depends: 'enableMediaDisplayButton',
- group: 'mediaRetrievalPlayback',
- list: 'screens',
+ group: 'advanced',
+ list: 'resolutions',
type: 'list',
},
- weDay: {
- group: 'congregationMeetings',
- list: 'days',
- rules: ['notEmpty'],
+ jwlCompanionMode: {
+ depends: 'enableMediaDisplayButton',
+ group: 'advanced',
+ type: 'toggle',
+ },
+ hideMediaLogo: {
+ depends: 'enableMediaDisplayButton',
+ group: 'advanced',
+ type: 'toggle',
+ },
+ includePrinted: {
+ depends: 'enableMediaDisplayButton',
+ group: 'advanced',
+ type: 'toggle',
+ },
+ // todo: test implementation of excludeFootnotes
+ excludeFootnotes: {
+ depends: 'enableMediaDisplayButton',
+ group: 'advanced',
+ type: 'toggle',
+ },
+ excludeTh: {
+ depends: 'enableMediaDisplayButton',
+ group: 'advanced',
+ type: 'toggle',
+ },
+ enableSubtitles: {
+ depends: 'enableMediaDisplayButton',
+ group: 'advanced',
+ type: 'toggle',
+ },
+ langSubtitles: {
+ depends: 'enableSubtitles',
+ group: 'advanced',
+ list: 'jwLanguages',
type: 'list',
},
- weStartTime: {
- group: 'congregationMeetings',
- options: ['meetingTime'],
- rules: ['notEmpty'],
- type: 'time',
+ musicVolume: {
+ depends: 'enableMusicButton',
+ group: 'advanced',
+ max: 100,
+ min: 0,
+ step: 1,
+ type: 'slider',
+ },
+ // todo: implement enableMusicFadeOut toggle
+ enableMusicFadeOut: {
+ depends: 'enableMusicButton',
+ group: 'advanced',
+ type: 'toggle',
},
- // Advanced
-
// enableMp4Conversion: {
// type: 'toggle',
// depends: 'advanced',
@@ -351,7 +356,7 @@ export const defaultSettings: SettingsValues = {
// autoPlayFirst: false,
// autoPlayFirstTime: 5,
// autoQuitWhenDone: false,
- autoRunAtBoot: false,
+ autoStartAtLogin: false,
autoStartMusic: true,
// autoStartSync: false,
coWeek: '',
@@ -394,7 +399,7 @@ export const defaultSettings: SettingsValues = {
obsMediaScene: '',
obsPassword: '',
obsPort: '',
- preferredOutput: '',
+ // preferredOutput: '',
// specialCong: false,
weDay: '',
weStartTime: '',
diff --git a/src/helpers/electron-api.ts b/src/helpers/electron-api.ts
index b9290303..cd99d5a1 100644
--- a/src/helpers/electron-api.ts
+++ b/src/helpers/electron-api.ts
@@ -16,6 +16,7 @@ export interface ElectronApi {
klawSync: typeof import('klaw-sync');
openFolderDialog: () => string[];
path: typeof import('path');
+ setautoStartAtLogin: (value: boolean) => void;
toggleMediaWindow: (action: string) => void;
}
diff --git a/src/helpers/jw-media.ts b/src/helpers/jw-media.ts
index 78f3755e..01bf80f8 100644
--- a/src/helpers/jw-media.ts
+++ b/src/helpers/jw-media.ts
@@ -28,6 +28,7 @@ import {
TableItem,
VideoMarker,
} from 'src/types/sqlite';
+import { useRoute } from 'vue-router';
import { useCurrentStateStore } from '../stores/current-state';
import { MAX_SONGS } from '../stores/jw';
@@ -132,9 +133,14 @@ const fetchMedia = async () => {
const { currentCongregation, lookupPeriod } = storeToRefs(
useCurrentStateStore(),
);
+ const route = useRoute();
const fetchErrors = {} as Record;
for (const day of lookupPeriod.value.filter((day) => day.meeting)) {
- if (!currentCongregation.value) break;
+ if (
+ !currentCongregation.value ||
+ !['/media-calendar', '/setup-wizard'].includes(route.fullPath)
+ )
+ break;
const dayDate = day.date;
day.loading = true;
let fetchResult = null;
diff --git a/src/helpers/migrations.ts b/src/helpers/migrations.ts
index 5ebab765..dd795c12 100644
--- a/src/helpers/migrations.ts
+++ b/src/helpers/migrations.ts
@@ -29,7 +29,7 @@ const parsePrefsFile = (path: string) => {
const buildNewPrefsObject = (oldPrefs: OldAppConfig) => {
const newPrefsObject: SettingsValues = {
- autoRunAtBoot: oldPrefs.app.autoRunAtBoot || false,
+ autoStartAtLogin: oldPrefs.app.autoRunAtBoot || false,
autoStartMusic: oldPrefs.meeting.autoStartMusic || true,
coWeek: oldPrefs.meeting.coWeek || '',
congregationName: oldPrefs.app.congregationName || '',
@@ -57,7 +57,7 @@ const buildNewPrefsObject = (oldPrefs: OldAppConfig) => {
obsMediaScene: oldPrefs.app.obs.mediaScene || '',
obsPassword: oldPrefs.app.obs.password || '',
obsPort: oldPrefs.app.obs.port?.toString() || '',
- preferredOutput: oldPrefs.media.preferredOutput || '',
+ // preferredOutput: oldPrefs.media.preferredOutput || '',
weDay: oldPrefs.meeting.weDay?.toString() || '',
weStartTime: oldPrefs.meeting.weStartTime?.toString() || '',
};
diff --git a/src/i18n/en-US/index.json b/src/i18n/en-US/index.json
index c6c56d13..3829b415 100644
--- a/src/i18n/en-US/index.json
+++ b/src/i18n/en-US/index.json
@@ -11,8 +11,8 @@
"are-you-using-this-app-at-a-kingdom-hall-if-so-well-configure-a-few-things-for-you-right-off-the-bat": "Are you using this app at a Kingdom Hall? If so, we'll configure a few things for you right off the bat.",
"areYouSure": "Are you sure?",
"automatic": "Automatic",
- "autoRunAtBoot": "Run app at system start-up",
- "autoStartMusic": "Play background music automatically before meetings",
+ "autoStartAtLogin": "Start M³ automatically after logging in",
+ "autoStartMusic": "Start background music before meetings automatically",
"back": "Back",
"betaUpdates": "Enable beta updates for testing purposes (Note: Do not enable this setting on the computer used to present media at the Kingdom Hall.)",
"cancel": "Cancel",
@@ -39,8 +39,8 @@
"does-your-kingdom-hall-use-a-program-called-obs-studio": "Does your Kingdom Hall use a program called OBS Studio?",
"doing-so-will-greatly-simplify-and-facilitate-sharing-media-during-hybrid-meetings": "Doing so will greatly simplify and facilitate sharing media during hybrid meetings.",
"drop-multimedia-files-here-to-add-them-to-the-list-for-this-day": "Drop multimedia files here to add them to the list for this day.",
- "enableMediaDisplayButton": "Present media on an external monitor or in a separate window",
- "enableMusicButton": "Enable button to play background music",
+ "enableMediaDisplayButton": "Enable media playback",
+ "enableMusicButton": "Enable background music playback",
"enter-the-port-and-password-configured-in-obs-studios-websocket-plugin": "Enter the port and password configured in OBS Studio's Websocket plugin.",
"entireFile": "Play from start to finish",
"error": "Error",
@@ -167,5 +167,7 @@
"please-wait-downloading-media-for-this-day": "Please wait, downloading media for this day...",
"noDateSelected": "No date is currently selected. Please select a date before continuing.",
"welcome-to-mmm": "Welcome to M³!",
- "successfully-migrated-from-the-previous-version": "Successfully migrated from the previous version"
+ "successfully-migrated-from-the-previous-version": "Successfully migrated from the previous version",
+ "depends-on": "Linked to:",
+ "setup-wizard": "Setup Wizard"
}
diff --git a/src/i18n/fr-CA/index.json b/src/i18n/fr-CA/index.json
index df778d4f..c0a9fd23 100644
--- a/src/i18n/fr-CA/index.json
+++ b/src/i18n/fr-CA/index.json
@@ -6,7 +6,7 @@
"areYouSure": "Êtes-vous sûr ?",
"automatic": "Automatique",
"autoPlayFirst": "Lecture automatique du premier média",
- "autoRunAtBoot": "Lancer l'appli automatiquement au démarrage de l'ordinateur",
+ "autoStartAtLogin": "Démarrez M³ automatiquement après l'ouverture de session",
"autoStartMusic": "Jouer automatiquement la musique de fond avant les réunions",
"autoStartSync": "Lancer la synchronisation automatiquement",
"back": "Retour",
@@ -41,8 +41,8 @@
"doing-so-will-greatly-simplify-and-facilitate-sharing-media-during-hybrid-meetings": "Cela simplifiera et facilitera grandement le partage de médias lors de réunions hybrides.",
"dontForgetToGetMedia": "N'oubliez pas de rafraîchir les dossiers multimédias sur l'ordinateur utilisé pour présenter les médias lors des réunions de l'assemblée locale !",
"downloadShuffleMusic": "Télécharger tous les cantiques pour la musique de fond",
- "enableMediaDisplayButton": "Présenter les médias sur un écran externe ou dans une fenêtre séparée",
- "enableMusicButton": "Activer le bouton pour jouer de la musique de fond",
+ "enableMediaDisplayButton": "Activer la fonction de lecture des médias",
+ "enableMusicButton": "Activer la fonction de musique de fond",
"enableObs": "Activer le mode de compatibilité avec OBS Studio (nécessite obs-websocket)",
"enablePp": "Activer les raccourcis clavier pendant la lecture des médias (à utiliser avec une télécommande USB, par exemple)",
"enableSubtitles": "Activer les sous-titres pour les vidéos",
@@ -328,5 +328,6 @@
"please-wait-downloading-media-for-this-day": "Veuillez patienter, téléchargement des médias pour cette journée en cours...",
"noDateSelected": "Aucune date n'est actuellement sélectionnée. Veuillez sélectionner une date avant de continuer.",
"welcome-to-mmm": "Bienvenue à M³ !",
- "successfully-migrated-from-the-previous-version": "Migré avec succès depuis la version précédente"
+ "successfully-migrated-from-the-previous-version": "Migré avec succès depuis la version précédente",
+ "depends-on": "Relié à :"
}
diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue
index 6fd31b7b..569a53b8 100644
--- a/src/layouts/MainLayout.vue
+++ b/src/layouts/MainLayout.vue
@@ -277,6 +277,7 @@ import { useAppSettingsStore } from '../stores/app-settings';
import { useCongregationSettingsStore } from '../stores/congregation-settings';
import { useCurrentStateStore } from '../stores/current-state';
import { useJwStore } from '../stores/jw';
+//electronapi
// Store and router initializations
@@ -320,7 +321,7 @@ jwStore.$subscribe((_, state) => {
// Ref and reactive initializations
const chooseSong = ref(false);
const mediaSortForDay = ref(true);
-const { toggleMediaWindow } = electronApi;
+const { setautoStartAtLogin, toggleMediaWindow } = electronApi;
const { locale, t } = useI18n({ useScope: 'global' });
const drawer = ref(true);
@@ -351,14 +352,15 @@ watch(route, (newVal) => {
);
});
-watch(currentSettings, (newSettings) => {
- console.log('currentSettings changed', newSettings);
- if (!newSettings) {
- if (route.fullPath !== '/congregation-selector') {
+const navigateToCongregationSelector = () => {
+ if (route.fullPath !== '/congregation-selector') {
router.push({ path: '/congregation-selector' });
- return;
}
- }
+};
+
+watch(currentSettings, (newSettings) => {
+ console.log('currentSettings changed', newSettings);
+ if (!newSettings) navigateToCongregationSelector();
});
watch(
@@ -403,6 +405,13 @@ watch(
},
);
+watch(
+ () => currentSettings.value?.autoStartAtLogin,
+ (newautoStartAtLogin) => {
+ setautoStartAtLogin(!!newautoStartAtLogin);
+ },
+);
+
const dateOptions = (lookupDate: string) => {
const dateArray: Date[] = lookupPeriod.value.map((day) => day.date);
// @ts-expect-error "A spread argument must either have a tuple type or be passed to a rest parameter."
@@ -455,5 +464,7 @@ if (!migrations.value.includes('firstRun')) {
onMounted(() => {
document.title = 'Meeting Media Manager';
+ console.log(currentSettings.value)
+ if (!currentSettings.value) navigateToCongregationSelector();
});
diff --git a/src/pages/CongregationSelectorPage.vue b/src/pages/CongregationSelectorPage.vue
index f7cee458..be71526f 100644
--- a/src/pages/CongregationSelectorPage.vue
+++ b/src/pages/CongregationSelectorPage.vue
@@ -98,7 +98,7 @@ function chooseCongregation(
}
const isHomePage = computed(() => {
- return route.path === '/';
+ return route.path === '/initial-congregation-selector';
});
function createNewCongregation() {
diff --git a/src/pages/SettingsPage.vue b/src/pages/SettingsPage.vue
index 4fcf4b54..3302cad3 100644
--- a/src/pages/SettingsPage.vue
+++ b/src/pages/SettingsPage.vue
@@ -38,16 +38,35 @@
-
+
{{ $t(settingId) }}
-
-
+
+
+
+ {{ $t('depends-on') }}
+ {{ $t(item.depends) }}
+
+
+
+
+
import('layouts/RouteHelper.vue'),
path: '/',
},
-
{
+ alias: ['/initial-congregation-selector'],
children: [
{
component: () => import('pages/CongregationSelectorPage.vue'),
@@ -17,7 +17,6 @@ const routes: RouteRecordRaw[] = [
meta: { title: 'titles.profileSelection' },
path: '/congregation-selector',
},
-
{
children: [
{ component: () => import('pages/MediaCalendarPage.vue'), path: '' },
@@ -26,7 +25,6 @@ const routes: RouteRecordRaw[] = [
meta: { title: 'titles.mediaCalendar' },
path: '/media-calendar',
},
-
{
children: [
{ component: () => import('pages/MediaPlayerPage.vue'), path: '' },
@@ -38,7 +36,7 @@ const routes: RouteRecordRaw[] = [
{
children: [{ component: () => import('pages/SetupWizard.vue'), path: '' }],
component: () => import('layouts/MainLayout.vue'),
- meta: { title: 'Setup Wizard' },
+ meta: { title: 'setup-wizard' },
path: '/setup-wizard',
},
{
diff --git a/src/types/settings.ts b/src/types/settings.ts
index d348726a..59d10f5a 100644
--- a/src/types/settings.ts
+++ b/src/types/settings.ts
@@ -3,7 +3,7 @@ export interface SettingsValues {
// autoPlayFirst: boolean;
// autoPlayFirstTime: number;
// autoQuitWhenDone: boolean;
- autoRunAtBoot: boolean;
+ autoStartAtLogin: boolean;
autoStartMusic: boolean;
// autoStartSync: boolean;
coWeek: string;
@@ -47,7 +47,7 @@ export interface SettingsValues {
obsMediaScene: string;
obsPassword: string;
obsPort: string;
- preferredOutput: string;
+ // preferredOutput: string;
// [key: string]: unknown;
weDay: string;
weStartTime: string;
@@ -57,6 +57,7 @@ export interface SettingsItem {
actions?: string[];
depends?: string;
group: string;
+ icon?: string;
list?: string;
max?: number;
min?: number;