From 6301a520bf8015433cd7f94ca67e7b25d614eadb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kristoffer=20Vassb=C3=B8?=
Date: Fri, 9 Aug 2024 15:22:48 +0200
Subject: [PATCH] v1.2.4 (#732)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* ✨ Option to not split scripture verse reference
- Fixed freeze when renaming variable to nothing
- Fixed error when closing stage outputs just after creating
- Fixed stage output preview visual bug
- Fixed dropdown click visual bug
- Kiosk setting auto revert timer
- Unsplash API changes
* ✔ Fixed media ghost thumbnails not always loading
- EasyWorship import CCLI
- Improved screen recorder
* 🚩 Updated Norwegian
* 🔗 Updated links
- Updated languages
* 📺 Option to hide output from preview
- Improvements to scripture loading
* ✔ Special case fixes (like disabling kiosk mode)
* ✔ Missing background search will search deeper in nested folders
- Fixed media files with special characters not working
- Fixed slide not being able to play if background path did not exist
- Changed toggle panels from Q to T
- Changed app closing
* ✔ Version update
---
README.md | 2 +-
package.json | 2 +-
public/lang/en.json | 5 +
public/lang/hu.json | 32 +++++-
public/lang/it.json | 48 ++++++++-
public/lang/no.json | 32 +++++-
.../capture/helpers/CaptureLifecycle.ts | 6 +-
src/electron/data/export.ts | 2 +-
src/electron/index.ts | 21 ++--
.../output/helpers/OutputLifecycle.ts | 7 +-
src/electron/utils/files.ts | 98 ++++++++++++++-----
src/electron/utils/responses.ts | 7 ++
.../components/context/ContextItem.svelte | 6 +-
.../components/context/contextMenus.ts | 3 +-
src/frontend/components/context/menuClick.ts | 26 ++++-
.../components/drawer/audio/Audio.svelte | 12 +--
.../components/drawer/audio/AudioFile.svelte | 4 +-
.../components/drawer/bible/Scripture.svelte | 13 ++-
.../components/drawer/bible/scripture.ts | 14 +--
.../components/drawer/info/Info.svelte | 4 +-
.../components/drawer/info/MediaInfo.svelte | 14 +--
.../components/drawer/info/PlayerInfo.svelte | 11 +++
.../drawer/info/ScriptureInfo.svelte | 8 ++
.../components/drawer/live/LiveInfo.svelte | 6 +-
.../components/drawer/live/recorder.ts | 4 +
.../components/drawer/media/Image.svelte | 3 +-
.../components/drawer/media/Media.svelte | 4 +-
.../components/drawer/media/MediaCard.svelte | 9 +-
.../drawer/media/MediaLoader.svelte | 8 +-
.../components/drawer/media/unsplash.ts | 5 +-
.../components/drawer/pages/Variables.svelte | 4 +-
.../edit/editbox/EditboxOther.svelte | 4 +-
.../components/edit/tools/EditValues.svelte | 2 +-
src/frontend/components/helpers/audio.ts | 32 +++---
.../components/helpers/dropActions.ts | 7 +-
src/frontend/components/helpers/media.ts | 14 ++-
src/frontend/components/helpers/output.ts | 2 +-
.../components/helpers/showActions.ts | 18 +++-
.../components/inputs/Dropdown.svelte | 5 +-
.../components/inputs/FontDropdown.svelte | 5 +-
src/frontend/components/main/Toast.svelte | 4 +-
.../components/main/popups/About.svelte | 2 +-
.../components/main/popups/Shortcuts.svelte | 4 +-
.../components/main/popups/Variable.svelte | 4 +-
src/frontend/components/media/Image.svelte | 3 +-
src/frontend/components/media/Video.svelte | 3 +-
src/frontend/components/output/Output.svelte | 6 +-
src/frontend/components/output/clear.ts | 3 +-
.../output/preview/MultiOutputs.svelte | 2 +-
.../components/output/tools/Audio.svelte | 10 +-
.../output/tools/MediaControls.svelte | 5 +-
.../components/settings/tabs/Other.svelte | 17 ++--
.../components/settings/tabs/Outputs.svelte | 22 ++++-
src/frontend/components/show/Slides.svelte | 4 +-
.../components/show/tools/Media.svelte | 10 +-
src/frontend/components/slide/Slide.svelte | 13 ++-
src/frontend/components/slide/Textbox.svelte | 8 +-
src/frontend/components/stage/Stagebox.svelte | 2 +-
.../components/stage/tools/SlideStyle.svelte | 12 ++-
src/frontend/converters/easyworship.ts | 2 +-
src/frontend/stores.ts | 1 +
src/frontend/utils/listeners.ts | 5 +
src/frontend/utils/receivers.ts | 22 ++++-
src/frontend/utils/shortcuts.ts | 2 +-
src/types/Output.ts | 1 +
65 files changed, 490 insertions(+), 186 deletions(-)
diff --git a/README.md b/README.md
index 53b2a54c..7d9794ae 100644
--- a/README.md
+++ b/README.md
@@ -30,7 +30,7 @@ FreeShow exists because the creator found that other simular programs was either
## Support Us
-The only reason this program is free is because of the generous support from users. If you want to support us to keep this free, please head over to [ChurchApps](https://churchapps/partner) or [sponsor us on GitHub](/~https://github.com/sponsors/ChurchApps/). Thank you so much!
+The only reason this program is free is because of the generous support from users. If you want to support us to keep this free, please head over to [ChurchApps](https://churchapps.org/partner) or [sponsor us on GitHub](/~https://github.com/sponsors/ChurchApps/). Thank you so much!
## Join the Community
diff --git a/package.json b/package.json
index fc7bed4a..643d4eb5 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "freeshow",
- "version": "1.2.3",
+ "version": "1.2.4",
"private": true,
"main": "build/electron/index.js",
"description": "Show song lyrics and more for free!",
diff --git a/public/lang/en.json b/public/lang/en.json
index 4cdaa0f2..00b4de3e 100644
--- a/public/lang/en.json
+++ b/public/lang/en.json
@@ -410,6 +410,8 @@
"now": "now!",
"no_video_id": "No video ID",
"no_name": "No name",
+ "reverting_setting": "Reverting this change in {} seconds, enable again to make the change stay!",
+ "reverted": "Setting reverted! Only enable again if you did not have any problems.",
"media_replaced": "Missing media file replaced with match.",
"lyrics_undefined": "Could not find any lyrics!",
"lyrics_copied": "Lyrics copied from ",
@@ -571,6 +573,7 @@
"remove_layers": "Remove layers",
"start_recording": "Start recording",
"stop_recording": "Stop recording",
+ "export_recording": "Stop recording and export",
"index_select_project": "Select project by index",
"next_project_item": "Next project item",
"previous_project_item": "Previous project item",
@@ -683,6 +686,7 @@
"align_with_screen": "Align with screen",
"toggle_output": "Toggle output",
"move_to_front": "Move to front",
+ "hide_from_preview": "Hide from preview",
"lock_to_output": "Lock to output",
"place_under_slide": "Place under slide",
"toggle_clock": "Toggle clock",
@@ -1080,6 +1084,7 @@
"verses_on_individual_lines": "Verses on individual lines",
"version": "Show version",
"reference": "Show reference",
+ "split_reference": "Split reference",
"combine_with_text": "Combine with text",
"reference_at_bottom": "Move to bottom",
"red_jesus": "Jesus words in red",
diff --git a/public/lang/hu.json b/public/lang/hu.json
index 7592524a..17147091 100644
--- a/public/lang/hu.json
+++ b/public/lang/hu.json
@@ -165,6 +165,7 @@
},
"audio": {
"settings": "Hangbeállítások",
+ "playlist_settings": "Lejátszási lista beállítások",
"mute_when_video_plays": "Némítás videó lejátszása alatt",
"metronome": "Metronóm",
"toggle_metronome": "Metronóm átváltása",
@@ -252,7 +253,10 @@
"display_metadata": "Metaadatok megjelenítése",
"meta_template": "Metaadat sablon",
"text_divider": "Szövegelválasztó",
- "message_template": "Üzenet sablon"
+ "message_template": "Üzenet sablon",
+ "tags": "Címkék",
+ "new_tag": "Új címke",
+ "clear_tag_filter": "Címkeszűrő törlése"
},
"show_at": {
"never": "Egyetlen dián sem",
@@ -406,6 +410,8 @@
"now": "most!",
"no_video_id": "Nincs videóazonosító",
"no_name": "Nincs név",
+ "reverting_setting": "A módosítás visszavonása {} másodperc múlva. Engedélyezze újra, hogy a módosítás megmaradjon!",
+ "reverted": "Beállítás visszavonva! Csak akkor engedélyezze újra, ha nem volt semmilyen probléma.",
"media_replaced": "Hiányzó médiafájl helyettesítve a megfelelővel.",
"lyrics_undefined": "Nem található dalszöveg!",
"lyrics_copied": "Dalszöveg másolva erről:",
@@ -520,6 +526,7 @@
"format": "Formázás",
"find_replace": "Szöveg keresése és cseréje",
"cut_in_half": "Kettéválasztás",
+ "merge": "Összevonás",
"find": "Keresés",
"replace": "Csere",
"case_sensitive": "Kis- és nagybetű érzékeny",
@@ -547,6 +554,7 @@
"chord_type": "Típus",
"chord_tension": "Színezőhang",
"chord_bass": "Basszus",
+ "roman_keys": "Római kulcsok",
"set_key": "Billentyű beállítása",
"custom_key": "Egyéni érték beállítása",
"select_chord": "Akkord kijelölése",
@@ -565,6 +573,7 @@
"remove_layers": "Rétegek eltávolítása",
"start_recording": "Felvétel indítása",
"stop_recording": "Felvétel leállítása",
+ "export_recording": "Felvétel és exportálás leállítása ",
"index_select_project": "Projekt kijelölése index alapján",
"next_project_item": "Következő projektelem",
"previous_project_item": "Előző projektelem",
@@ -584,6 +593,8 @@
"start_playlist": "Lejátszási lista indítása",
"playlist_next": "Következő szám a lejátszási listában",
"start_metronome": "Metronóm indítása",
+ "name_start_timer": "Időzítő indítása név alapján",
+ "id_start_timer": "Időzítő indítása azonosító alapján",
"start_slide_timers": "Időzítők indítása az aktív dián",
"id_select_output_style": "Kimeneti stílus kijelölése azonosító alapján",
"change_output_style": "Kimeneti stílus módosítása",
@@ -602,7 +613,9 @@
"activate_timer_ending": "Aktiválás időzítő befejezésekor",
"activate_scripture_start": "Aktiválás szentírás indításakor",
"activate_slide_cleared": "Aktiválás dia törlésekor",
- "activate_show_created": "Aktiválás műsor létrehozásakor"
+ "activate_background_cleared": "Aktiválás háttér törlésekor",
+ "activate_show_created": "Aktiválás műsor létrehozásakor",
+ "activate_audio_playlist_ended": "Aktiválás hanglejátszási lista végén"
},
"animate": {
"change": "Módosítás",
@@ -626,6 +639,7 @@
"google_drive_api": "Google API szolgáltatási fiók kulcsa",
"select_key": "Kulcsfájl importálása",
"update_key": "Kulcsfájl frissítése",
+ "enable_custom_folder_id": "Egyedi mappaazonosító alkalmazása",
"main_folder": "Fő mappa kézi beállítása",
"media_folder": "Felhő médiamappa",
"reconnect": "Újracsatlakozás",
@@ -661,6 +675,7 @@
},
"context": {
"enabledTabs": "Fülek átváltása",
+ "filterByTags": "Szűrés címkékre",
"addToProject": "Hozzáadás a projekthez",
"newCategory": "Új kategória",
"changeIcon": "Ikon módosítása",
@@ -905,6 +920,7 @@
"system_clock": "Rendszeróra",
"video_time": "Videó idő",
"video_countdown": "Videó visszaszámláló",
+ "first_active_timer": "Első aktív időzítő",
"other": "Egyéb",
"message": "Üzenet",
"color": "Szín",
@@ -975,7 +991,12 @@
"resolution": "Felbontás",
"cropping": "Vágás",
"frame_rate": "Képkockasebesség",
+ "device": "Eszköz",
+ "display_mode": "Megjelenítése mód",
+ "pixel_format": "Pixel forma",
+ "alpha_key": "Alfakulcs",
"transparent": "Átlátszó",
+ "invisible_window": "Láthatatlan ablak",
"video_extensions": "Videó kiterjesztések",
"image_extensions": "Kép kiterjesztések",
"add": "Hozzáadás",
@@ -1062,6 +1083,7 @@
"verses_on_individual_lines": "Versszakok az egyes sorokban",
"version": "Verzió megjelenítése",
"reference": "Hivatkozás megjelenítése",
+ "split_reference": "Hivatkozás szétválasztása",
"combine_with_text": "Kombinálás a szöveggel",
"reference_at_bottom": "Mozgatás alulra",
"red_jesus": "Jézus szavai pirossal",
@@ -1153,7 +1175,11 @@
"slides": "Dia",
"words": "Szó",
"template": "Sablon",
- "category": "Kategória"
+ "category": "Kategória",
+ "photoUrl": "Fotó URL",
+ "likes": "Kedvelések",
+ "artist": "Művész",
+ "artistUrl": "Művész oldala"
},
"songbeamer_import": {
"options": "Beállítások",
diff --git a/public/lang/it.json b/public/lang/it.json
index 7570bfc9..12800146 100644
--- a/public/lang/it.json
+++ b/public/lang/it.json
@@ -165,6 +165,7 @@
},
"audio": {
"settings": "Impostazioni audio",
+ "playlist_settings": "Playlist settings",
"mute_when_video_plays": "Disattiva l'audio durante la riproduzione del video",
"metronome": "Metronomo",
"toggle_metronome": "Attiva metronomo",
@@ -252,7 +253,10 @@
"display_metadata": "Mostra i metadati",
"meta_template": "Modello di metadati",
"text_divider": "Separatore di testo",
- "message_template": "Modello di messaggio"
+ "message_template": "Modello di messaggio",
+ "tags": "Tags",
+ "new_tag": "New tag",
+ "clear_tag_filter": "Clear tag filter"
},
"show_at": {
"never": "Nessuna diapositiva",
@@ -359,6 +363,7 @@
"change_name": "Cambia nome attivo",
"choose_screen": "Scegli lo schermo",
"change_output_values": "Modifica i valori di output",
+ "choose_chord": "Scegli accordo",
"set_time": "Imposta orario",
"animate": "Animate",
"next_timer": "Timer diapositiva successiva",
@@ -405,6 +410,8 @@
"now": "ora!",
"no_video_id": "Nessun ID video",
"no_name": "Nessun nome",
+ "reverting_setting": "Reverting this change in {} seconds, enable again to make the change stay!",
+ "reverted": "Setting reverted! Only enable again if you did not have any problems.",
"media_replaced": "Missing media file replaced with match.",
"lyrics_undefined": "Impossibile trovare alcun testo!",
"lyrics_copied": "Testi copiati da",
@@ -519,6 +526,7 @@
"format": "Formato",
"find_replace": "Trova e sostituisci il testo",
"cut_in_half": "Dividere in due",
+ "merge": "Merge",
"find": "Trova",
"replace": "Sostituisci",
"case_sensitive": "Case sensitive",
@@ -530,6 +538,7 @@
"svg_clipboard": "Importa SVG dagli appunti",
"fullscreen_preview": "Attiva/disattiva l'anteprima a schermo intero",
"toggle_output": "Attiva/disattiva la schermata di output",
+ "toggle_panels": "Attiva pannelli",
"change_tab": "Cambia scheda",
"change_drawer_tab": "Cambia scheda cassetto",
"change_slide": "Cambia dispositiva",
@@ -540,8 +549,15 @@
"slide_actions": "Azioni diapositiva",
"item_actions": "Azioni degli elementi",
"clear_history": "Pulisci cronologia",
+ "chord_info": "Clicca su una lettera qualsiasi per aggiungere un accordo.",
+ "chord_key": "Chiave",
+ "chord_type": "Tipo",
+ "chord_tension": "Tensione",
+ "chord_bass": "Basso",
+ "roman_keys": "Roman keys",
"set_key": "Imposta chiave",
"custom_key": "Imposta valore personalizzato",
+ "select_chord": "Selezione questo accordo",
"play_on_midi": "Attiva sul segnale MIDI",
"play_on_midi_tip": "Attiva questa diapositiva specifica quando ricevi il segnale MIDI scelto",
"send_midi": "Invia segnale MIDI",
@@ -557,6 +573,7 @@
"remove_layers": "Rimuovi livelli",
"start_recording": "Inizia a registrare",
"stop_recording": "Ferma registrazione",
+ "export_recording": "Stop recording and export",
"index_select_project": "Seleziona progetto per indice",
"next_project_item": "Elemento successivo del progetto",
"previous_project_item": "Elemento del progetto precedente",
@@ -576,6 +593,8 @@
"start_playlist": "Avvia la playlist",
"playlist_next": "Traccia successiva nella playlist",
"start_metronome": "Avvia il metronomo",
+ "name_start_timer": "Start timer by name",
+ "id_start_timer": "Start timer by ID",
"start_slide_timers": "Avvia i timer sulla diapositiva attiva",
"id_select_output_style": "Seleziona lo stile di output in base all'ID",
"change_output_style": "Cambia stile di output",
@@ -593,7 +612,10 @@
"activate_video_ending": "Attiva quando il video sta finendo",
"activate_timer_ending": "Attiva quando il timer sta per scadere",
"activate_scripture_start": "Attiva quando viene avviata la scrittura",
- "activate_show_created": "Attiva quando viene creato lo spettacolo"
+ "activate_slide_cleared": "Si attiva quando la diapositiva viene cancellata",
+ "activate_background_cleared": "Activate when background is cleared",
+ "activate_show_created": "Attiva quando viene creato lo spettacolo",
+ "activate_audio_playlist_ended": "Activate when audio playlist has ended"
},
"animate": {
"change": "cambia",
@@ -617,6 +639,7 @@
"google_drive_api": "Chiave dell'account di servizio dell'API di Google",
"select_key": "Importa il file delle chiavi",
"update_key": "Aggiorna il file delle chiavi",
+ "enable_custom_folder_id": "Use custom folder ID",
"main_folder": "Imposta la cartella principale manualmente",
"media_folder": "Cartella multimediale cloud",
"reconnect": "Riconnetti",
@@ -652,6 +675,7 @@
},
"context": {
"enabledTabs": "Attiva/disattiva schede",
+ "filterByTags": "Filter by tags",
"addToProject": "Aggiungi al progetto",
"newCategory": "Nuova categoria",
"changeIcon": "Cambia icona",
@@ -694,6 +718,7 @@
"_title_underline": "Sottolineato",
"_title_strikethrough": "Barrato",
"color": "Colore",
+ "accent_color": "Colore di rifinitura",
"background_color": "Colore di sfondo",
"background_opacity": "Opacità dello sfondo",
"background_image": "Immagine di sfondo",
@@ -766,7 +791,8 @@
"start_days_from_today": "Inizia i giorni da oggi",
"just_one_day": "Solo un giorno",
"enable_start_date": "Abilita data di inizio",
- "disable_navigation": "Disabilita i controlli di navigazione"
+ "disable_navigation": "Disabilita i controlli di navigazione",
+ "progress_bar": "Barra di avanzamento"
},
"items": {
"text": "Casella di testo",
@@ -783,6 +809,7 @@
"timer": "Timer",
"variable": "Variable",
"web": "Sito web",
+ "slide_tracker": "Avanzamento",
"visualizer": "Visualizzatore",
"captions": "Didascalie",
"icon": "Icona"
@@ -888,10 +915,12 @@
"next_slide_notes": "Note sulla diapositiva successiva",
"output": "Output",
"current_output": "Output corrente",
+ "slide_tracker": "Avanzamento",
"time": "Tempo",
"system_clock": "Orologio di sistema",
"video_time": "Tempo del video",
"video_countdown": "Conto alla rovescia del video",
+ "first_active_timer": "First active timer",
"other": "Altro",
"message": "Messaggio",
"color": "Colore",
@@ -958,10 +987,16 @@
"audio_fade_duration": "Durata della dissolvenza audio",
"audio_crossfade": "Dissolvenza incrociata dell'audio",
"max_auto_font_size": "Dimensione massima del carattere automatico",
+ "clear_style_background_on_text": "Cancella lo stile dello sfondo quando la diapositiva è attiva",
"resolution": "Risoluzione",
"cropping": "Ritagliare",
"frame_rate": "Frame rate",
+ "device": "Device",
+ "display_mode": "Display mode",
+ "pixel_format": "Pixel format",
+ "alpha_key": "Alpha key",
"transparent": "Transparent",
+ "invisible_window": "Invisible window",
"video_extensions": "Estensioni video",
"image_extensions": "Estensioni immagine",
"add": "Aggiungi",
@@ -1048,6 +1083,7 @@
"verses_on_individual_lines": "Versetti su versi individuali",
"version": "Mostra versione",
"reference": "Mostra referenze",
+ "split_reference": "Split reference",
"combine_with_text": "Combina con testo",
"reference_at_bottom": "Sposta in basso",
"red_jesus": "Parole di Gesù in rosso",
@@ -1139,7 +1175,11 @@
"slides": "Diapositive",
"words": "Parole",
"template": "Modello",
- "category": "Categoria"
+ "category": "Categoria",
+ "photoUrl": "Photo URL",
+ "likes": "Likes",
+ "artist": "Artist",
+ "artistUrl": "Artist page"
},
"songbeamer_import": {
"options": "Opzioni",
diff --git a/public/lang/no.json b/public/lang/no.json
index c84c2c44..4ea9fac8 100644
--- a/public/lang/no.json
+++ b/public/lang/no.json
@@ -165,6 +165,7 @@
},
"audio": {
"settings": "Lydinnstillinger",
+ "playlist_settings": "Innstillinger for spilleliste",
"mute_when_video_plays": "Demp når video spilles",
"metronome": "Metronom",
"toggle_metronome": "Veksle metronom",
@@ -252,7 +253,10 @@
"display_metadata": "Vis metadata",
"meta_template": "Mal for metadata",
"text_divider": "Tekstskilletegn",
- "message_template": "Mal for melding"
+ "message_template": "Mal for melding",
+ "tags": "Etiketter",
+ "new_tag": "Ny etikett",
+ "clear_tag_filter": "Fjern etikettfilter"
},
"show_at": {
"never": "Ingen lysbilder",
@@ -406,6 +410,8 @@
"now": "nå!",
"no_video_id": "Ingen video-ID",
"no_name": "Ingen navn",
+ "reverting_setting": "Denne endringen vil bli reversert om {} sekunder, aktiver igjen for å bekrefte endringen!",
+ "reverted": "Innstilling tilbakestilt! Aktiver igjen på eget ansvar hvis du ikke hadde noen problemer.",
"media_replaced": "Manglende mediefil erstattet med match.",
"lyrics_undefined": "Kunne ikke finne noen sangtekster!",
"lyrics_copied": "Sangtekst kopiert fra ",
@@ -520,6 +526,7 @@
"format": "Formater",
"find_replace": "Finn og erstatt tekst",
"cut_in_half": "Del i to",
+ "merge": "Slå sammen",
"find": "Finn",
"replace": "Erstatt",
"case_sensitive": "Skill mellom små og store bokstaver",
@@ -547,6 +554,7 @@
"chord_type": "Type",
"chord_tension": "Septim",
"chord_bass": "Bass",
+ "roman_keys": "Romertall",
"set_key": "Sett toneart",
"custom_key": "Skriv inn verdi",
"select_chord": "Velg denne akkorden",
@@ -565,6 +573,7 @@
"remove_layers": "Fjern lag",
"start_recording": "Start opptak",
"stop_recording": "Stopp opptak",
+ "export_recording": "Stopp opptak og eksporter",
"index_select_project": "Velg prosjekt fra indeks",
"next_project_item": "Neste element i prosjekt",
"previous_project_item": "Forrige element i prosjekt",
@@ -584,6 +593,8 @@
"start_playlist": "Start spilleliste",
"playlist_next": "Neste låt i spilleliste",
"start_metronome": "Start metronom",
+ "name_start_timer": "Start tidtaker ved navn",
+ "id_start_timer": "Start tidtaker ved ID",
"start_slide_timers": "Start tidtakere på aktivt lysbilde",
"id_select_output_style": "Velg utgangsstil fra ID",
"change_output_style": "Endre stil for utgangsskjerm",
@@ -602,7 +613,9 @@
"activate_timer_ending": "Aktiver når tidtaker slutter",
"activate_scripture_start": "Aktiver når bibeltekst vises",
"activate_slide_cleared": "Aktiver når lysbilde fjernes",
- "activate_show_created": "Aktiver når show blir laget"
+ "activate_background_cleared": "Aktiver når bakgrunn fjernes",
+ "activate_show_created": "Aktiver når show blir laget",
+ "activate_audio_playlist_ended": "Aktiver når spilleliste er ferdig"
},
"animate": {
"change": "Endre",
@@ -626,6 +639,7 @@
"google_drive_api": "Google API tjenestekonto-nøkkel",
"select_key": "Importer nøkkelfil",
"update_key": "Oppdater nøkkelfil",
+ "enable_custom_folder_id": "Bruk egendefinert mappe-ID",
"main_folder": "Angi hovedmappe manuelt",
"media_folder": "Mediemappe i skyen",
"reconnect": "Koble til på nytt",
@@ -661,6 +675,7 @@
},
"context": {
"enabledTabs": "Veksle faner",
+ "filterByTags": "Filtrer etter etikett",
"addToProject": "Legg til i prosjekt",
"newCategory": "Ny kategori",
"changeIcon": "Endre ikon",
@@ -905,6 +920,7 @@
"system_clock": "Systemklokke",
"video_time": "Videotid",
"video_countdown": "Video nedtelling",
+ "first_active_timer": "Første aktive tidtaker",
"other": "Andre",
"message": "Melding",
"color": "Farge",
@@ -975,7 +991,12 @@
"resolution": "Oppløsning",
"cropping": "Beskjæring",
"frame_rate": "Bildefrekvens",
+ "device": "Enhet",
+ "display_mode": "Visningsmodus",
+ "pixel_format": "Pikselformat",
+ "alpha_key": "Alpha key",
"transparent": "Gjennomsiktig",
+ "invisible_window": "Usynlig vindu",
"video_extensions": "Videoutvidelse",
"image_extensions": "Bildeutvidelse",
"add": "Legg til",
@@ -1062,6 +1083,7 @@
"verses_on_individual_lines": "Vers på enkeltlinjer",
"version": "Vis versjon",
"reference": "Vis referanse",
+ "split_reference": "Del opp referanse",
"combine_with_text": "Kombiner med tekst",
"reference_at_bottom": "Flytt til bunn",
"red_jesus": "Jesus ord i rødt",
@@ -1153,7 +1175,11 @@
"slides": "Lysbilder",
"words": "Ord",
"template": "Mal",
- "category": "Kategori"
+ "category": "Kategori",
+ "photoUrl": "URL for bilde",
+ "likes": "Likes",
+ "artist": "Fotograf",
+ "artistUrl": "Skaperside"
},
"songbeamer_import": {
"options": "Alternativer",
diff --git a/src/electron/capture/helpers/CaptureLifecycle.ts b/src/electron/capture/helpers/CaptureLifecycle.ts
index 727ccd65..f969c975 100644
--- a/src/electron/capture/helpers/CaptureLifecycle.ts
+++ b/src/electron/capture/helpers/CaptureLifecycle.ts
@@ -6,7 +6,9 @@ import { CaptureTransmitter } from "./CaptureTransmitter"
export class CaptureLifecycle {
static startCapture(id: string, toggle: any = {}) {
const output = OutputHelper.getOutput(id)
- let window = output?.window
+ if (!output) return
+
+ let window = output.window
let windowIsRemoved = !window || window.isDestroyed()
if (windowIsRemoved) {
delete output.captureOptions
@@ -35,7 +37,7 @@ export class CaptureLifecycle {
captureFrame()
async function captureFrame() {
- if (!output.captureOptions || output.captureOptions.window.isDestroyed()) return
+ if (!output?.captureOptions?.window || output.captureOptions.window.isDestroyed()) return
let image = await output.captureOptions.window.webContents.capturePage()
processFrame(image)
diff --git a/src/electron/data/export.ts b/src/electron/data/export.ts
index 046f6423..ae070150 100644
--- a/src/electron/data/export.ts
+++ b/src/electron/data/export.ts
@@ -88,8 +88,8 @@ export function generatePDF(path: string) {
function exportMessage(message: string = "") {
toApp(MAIN, { channel: "ALERT", data: message })
+ exportWindow?.on("closed", () => (exportWindow = null))
exportWindow?.close()
- exportWindow = null
}
let exportWindow: any = null
diff --git a/src/electron/index.ts b/src/electron/index.ts
index c62dfb01..8f9e41ce 100644
--- a/src/electron/index.ts
+++ b/src/electron/index.ts
@@ -90,6 +90,9 @@ function mainWindowLoaded() {
if (config.get("maximized")) maximizeMain()
mainWindow?.show()
+ // this has to be called to actually remove the process!
+ // WIP seems like like loading window process is still up after everything else is closed!
+ loadingWindow?.removeAllListeners("close")
loadingWindow?.close()
if (RECORD_STARTUP_TIME) console.timeEnd("Full startup")
@@ -101,7 +104,7 @@ let loadingWindow: BrowserWindow | null = null
function createLoading() {
loadingWindow = new BrowserWindow(loadingOptions)
loadingWindow.loadFile("public/loading.html")
- loadingWindow.on("closed", () => (loadingWindow = null))
+ loadingWindow.once("closed", () => (loadingWindow = null))
}
// ----- MAIN WINDOW -----
@@ -115,13 +118,13 @@ function createMain() {
let screenBounds: Rectangle = screen.getPrimaryDisplay().bounds
let options: any = {
- width: !bounds.width || bounds.width === 800 ? screenBounds.width : bounds.width,
- height: !bounds.height || bounds.height === 600 ? screenBounds.height : bounds.height,
+ width: !bounds.width || bounds.width === 800 ? screenBounds.width || 800 : bounds.width,
+ height: !bounds.height || bounds.height === 600 ? screenBounds.height || 600 : bounds.height,
frame: !isProd || !isWindows,
autoHideMenuBar: isProd && isWindows,
}
- // should be centered to screen if x & y is not set
+ // should be centered to screen if x & y is not set (or bottom left on mac)
if (bounds.x) options.x = bounds.x
if (bounds.y) options.y = bounds.y
@@ -217,7 +220,7 @@ function setMainListeners() {
})
mainWindow.on("close", callClose)
- mainWindow.on("closed", exitApp)
+ mainWindow.once("closed", exitApp)
}
function callClose(e: any) {
@@ -230,7 +233,6 @@ function callClose(e: any) {
export async function exitApp() {
console.log("Closing app!")
- mainWindow = null
dialogClose = false
await OutputHelper.Lifecycle.closeAllOutputs()
@@ -244,8 +246,15 @@ export async function exitApp() {
if (!isProd) {
console.log("Dev mode active - Relaunching...")
app.relaunch()
+ } else {
+ // this has to be called to actually remove the process!
+ // https://stackoverflow.com/a/43520274
+ mainWindow?.removeAllListeners("close")
+ ipcMain.removeAllListeners()
}
+ mainWindow = null
+
try {
app.quit()
diff --git a/src/electron/output/helpers/OutputLifecycle.ts b/src/electron/output/helpers/OutputLifecycle.ts
index 147f20c1..b2592af6 100644
--- a/src/electron/output/helpers/OutputLifecycle.ts
+++ b/src/electron/output/helpers/OutputLifecycle.ts
@@ -108,15 +108,16 @@ export class OutputLifecycle {
return
}
- OutputHelper.getOutput(id).window.on("closed", () => {
+ OutputHelper.getOutput(id).window.once("closed", () => {
OutputHelper.deleteOutput(id)
if (reopen) this.createOutput(reopen)
})
try {
const output = OutputHelper.getOutput(id)
- output?.window?.destroy()
- //output?.previewWindow?.destroy()
+ // this has to be called to actually remove the process!
+ output?.window?.removeAllListeners("close")
+ output?.window?.close()
} catch (error) {
console.log(error)
}
diff --git a/src/electron/utils/files.ts b/src/electron/utils/files.ts
index 3c938552..7a987b05 100644
--- a/src/electron/utils/files.ts
+++ b/src/electron/utils/files.ts
@@ -7,13 +7,14 @@ import fs from "fs"
import { Stats } from "original-fs"
import path, { join, parse } from "path"
import { uid } from "uid"
-import { FILE_INFO, MAIN, OPEN_FOLDER, READ_FOLDER, SHOW, STORE } from "../../types/Channels"
+import { FILE_INFO, MAIN, OPEN_FOLDER, OUTPUT, READ_FOLDER, SHOW, STORE } from "../../types/Channels"
import { stores } from "../data/store"
import { createThumbnail } from "../data/thumbnails"
import { OPEN_FILE } from "./../../types/Channels"
import { mainWindow, toApp } from "./../index"
import { getAllShows, trimShow } from "./responses"
import { defaultSettings } from "../data/defaults"
+import { OutputHelper } from "../output/OutputHelper"
function actionComplete(err: Error | null, actionFailedMessage: string) {
if (err) console.error(actionFailedMessage + ":", err)
@@ -356,58 +357,78 @@ export function readExifData({ id }: any, e: any) {
}
// SEARCH FOR MEDIA FILE (in drawer media folders & their following folders)
+const NESTED_SEARCH = 8 // folder levels deep
export function locateMediaFile({ fileName, splittedPath, folders, ref }: any) {
let matches: string[] = []
- findMatches(true)
- if (matches.length < 1) findMatches()
- if (matches.length < 1) return
+ findMatches()
+ if (!matches.length) return
toApp(MAIN, { channel: "LOCATE_MEDIA_FILE", data: { path: matches[0], ref } })
/////
- function findMatches(searchWithFolder: boolean = false) {
+ function findMatches() {
for (const folderPath of folders) {
- if (matches.length > 1) return
-
- checkFolderForMatches(folderPath, searchWithFolder)
-
+ // if (matches.length > 1) return // this might be used if we want the user to choose if more than one match is found
if (matches.length) return
-
- let files = readFolder(folderPath)
- for (const name of files) {
- if (matches.length) return
-
- let p: string = path.join(folderPath, name)
- let fileStat = getFileStats(p)
- if (fileStat?.folder) checkFolderForMatches(p, searchWithFolder)
- }
+ searchInFolder(folderPath)
}
}
- function checkFolderForMatches(folderPath: string, searchWithFolder: boolean = false) {
+ function searchInFolder(folderPath: string, level: number = 1) {
+ if (level > NESTED_SEARCH || matches.length) return
+
+ let currentFolderFolders: string[] = []
let files = readFolder(folderPath)
+ for (const name of files) {
+ let currentFilePath: string = path.join(folderPath, name)
+ let fileStat = getFileStats(currentFilePath)
+
+ if (fileStat?.folder) {
+ // search all files in current folder before searching in any nested folders
+ currentFolderFolders.push(currentFilePath)
+ } else {
+ checkFileForMatch(name, folderPath)
+ if (matches.length) return
+ }
+ }
- let folderName = path.basename(folderPath)
- let searchName = fileName
- if (searchWithFolder && splittedPath?.length > 1) searchName = path.join(splittedPath[splittedPath.length - 2], fileName)
+ if (matches.length) return
- for (let name of files) {
- if (matches.length > 1) return
+ for (const folderName of currentFolderFolders) {
+ searchInFolder(folderName, level + 1)
+ if (matches.length) return
+ }
+ }
- let pathName = searchWithFolder ? path.join(folderName, name) : name
+ function checkFileForMatch(currentFileName: string, folderPath: string) {
+ // include original parent folder name in search first (to limit it a bit if two files with same name are in two different folders!)
+ if (splittedPath?.length > 1) {
+ let currentParentFolder = path.basename(folderPath)
+ let pathName = path.join(currentParentFolder, currentFileName)
+ let searchName = path.join(splittedPath[splittedPath.length - 2], fileName)
if (pathName === searchName) {
- let p: string = path.join(folderPath, name)
+ let p: string = path.join(folderPath, currentFileName)
matches.push(p)
}
}
+
+ if (matches.length) return
+
+ // check for file name exact match
+ if (currentFileName === fileName) {
+ let p: string = path.join(folderPath, currentFileName)
+ matches.push(p)
+ }
}
}
// LOAD SHOWS
export function loadShows({ showsPath }: any, returnShows: boolean = false) {
+ specialCaseFixer()
+
// list all shows in folder
let filesInFolder: string[] = readFolder(showsPath)
@@ -474,3 +495,28 @@ export function parseShow(jsonData: string) {
return show
}
+
+// some users might have got themselves in a situation they can't get out of
+// example: enables "kiosk" mode on mac might have resulted in a black screen, and they can't find the app data location to revert it!
+// how: Place any file in your Documents/FreeShow folder that has the FIXES key in it's name (e.g. DISABLE_KIOSK_MODE), when you now start your app the fix will be triggered!
+const FIXES: any = {
+ DISABLE_KIOSK_MODE: () => {
+ // wait to ensure output settings have loaded in the app!
+ setTimeout(() => {
+ toApp(OUTPUT, { channel: "UPDATE_OUTPUTS_DATA", data: { key: "kioskMode", value: false, autoSave: true } })
+ OutputHelper.getAllOutputs().forEach(([_id, output]) => output.window.setKiosk(false))
+ }, 1000)
+ },
+ OPEN_APPDATA_SETTINGS: () => {
+ // this will open the "settings.json" file located at the app data location (can also be used to find other setting files here)
+ openSystemFolder(stores.SETTINGS.path)
+ },
+}
+function specialCaseFixer() {
+ let defaultDataFolder = getDocumentsFolder(null, "")
+ let files: string[] = readFolder(defaultDataFolder)
+ files.forEach((fileName) => {
+ let matchFound = Object.keys(FIXES).find((key) => fileName.includes(key))
+ if (matchFound) FIXES[matchFound]()
+ })
+}
diff --git a/src/electron/utils/responses.ts b/src/electron/utils/responses.ts
index 2c686410..96305314 100644
--- a/src/electron/utils/responses.ts
+++ b/src/electron/utils/responses.ts
@@ -308,12 +308,19 @@ function getScreens(type: "window" | "screen" = "screen") {
}
// RECORDER
+// only open once per session
+let systemOpened: boolean = false
export function saveRecording(_: any, msg: any) {
let folder: string = getDataFolder(msg.path || "", dataFolderNames.recordings)
let p: string = path.join(folder, msg.name)
const buffer = Buffer.from(msg.blob)
writeFile(p, buffer)
+
+ if (!systemOpened) {
+ openSystemFolder(folder)
+ systemOpened = true
+ }
}
// ERROR LOGGER
diff --git a/src/frontend/components/context/ContextItem.svelte b/src/frontend/components/context/ContextItem.svelte
index 1719add2..1afd3512 100644
--- a/src/frontend/components/context/ContextItem.svelte
+++ b/src/frontend/components/context/ContextItem.svelte
@@ -1,5 +1,5 @@
-{#if subTab === "online"}
-
-{:else if subTab === "screens"}
+{#if subTab === "screens" || $activeRecording}
+{:else if subTab === "online"}
+
{:else}
diff --git a/src/frontend/components/drawer/info/PlayerInfo.svelte b/src/frontend/components/drawer/info/PlayerInfo.svelte
index 19d8f5b1..2354ce3f 100644
--- a/src/frontend/components/drawer/info/PlayerInfo.svelte
+++ b/src/frontend/components/drawer/info/PlayerInfo.svelte
@@ -46,6 +46,10 @@
{$photoApiCredits.downloadUrl}
-->
+
+
+ Photo by {$photoApiCredits.artist} from {$photoApiCredits.type}
+
{/if}
{/if}
@@ -83,4 +87,11 @@
margin: 20px 0;
background-color: var(--primary-lighter);
} */
+
+ .credits {
+ position: absolute;
+ bottom: 10px;
+ width: 100%;
+ text-align: center;
+ }
diff --git a/src/frontend/components/drawer/info/ScriptureInfo.svelte b/src/frontend/components/drawer/info/ScriptureInfo.svelte
index a989431f..01c93bb6 100644
--- a/src/frontend/components/drawer/info/ScriptureInfo.svelte
+++ b/src/frontend/components/drawer/info/ScriptureInfo.svelte
@@ -263,6 +263,14 @@
+ {#if $scriptureSettings.showVerse && sorted.length > 1}
+
+
+
+
+
+
+ {/if}
diff --git a/src/frontend/components/drawer/live/LiveInfo.svelte b/src/frontend/components/drawer/live/LiveInfo.svelte
index d09e1c4e..51448808 100644
--- a/src/frontend/components/drawer/live/LiveInfo.svelte
+++ b/src/frontend/components/drawer/live/LiveInfo.svelte
@@ -3,12 +3,12 @@
import Icon from "../../helpers/Icon.svelte"
import T from "../../helpers/T.svelte"
import Button from "../../inputs/Button.svelte"
- import { stopMediaRecorder, toggleMediaRecorder } from "./recorder"
+ import { mediaRecorderIsPaused, stopMediaRecorder, toggleMediaRecorder } from "./recorder"
let videoElem
$: if ($currentRecordingStream && videoElem) {
videoElem.srcObject = $currentRecordingStream
- paused = false
+ paused = mediaRecorderIsPaused() || false
}
let paused = false
@@ -39,7 +39,7 @@
{:else}
diff --git a/src/frontend/components/drawer/live/recorder.ts b/src/frontend/components/drawer/live/recorder.ts
index ea6d9c26..a4cd3b04 100644
--- a/src/frontend/components/drawer/live/recorder.ts
+++ b/src/frontend/components/drawer/live/recorder.ts
@@ -15,6 +15,10 @@ export function createMediaRecorder(stream) {
mediaRecorder.onstop = handleStop
}
+export function mediaRecorderIsPaused() {
+ return mediaRecorder?.state === "paused"
+}
+
export function toggleMediaRecorder() {
if (mediaRecorder.state === "paused") {
mediaRecorder.resume()
diff --git a/src/frontend/components/drawer/media/Image.svelte b/src/frontend/components/drawer/media/Image.svelte
index 2518f898..b511a11d 100644
--- a/src/frontend/components/drawer/media/Image.svelte
+++ b/src/frontend/components/drawer/media/Image.svelte
@@ -1,5 +1,6 @@
{#key retryCount}
-
+
{/key}