diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml
index 4c1c5aa6..906df06f 100644
--- a/.github/workflows/playwright.yml
+++ b/.github/workflows/playwright.yml
@@ -1,9 +1,11 @@
name: Playwright Tests
on:
- push:
- branches: [dev]
- pull_request:
- branches: [dev]
+ # turning off temporarily as it does not work properly!
+ # Error: electron.launch: Process failed to launch!
+ # push:
+ # branches: [dev]
+ # pull_request:
+ # branches: [dev]
workflow_dispatch:
jobs:
@@ -15,6 +17,8 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 20
+ - name: Install libraries
+ run: sudo apt-get install libfontconfig1-dev
- name: Install dependencies
run: npm install
- name: Install Playwright Browsers
diff --git a/package-lock.json b/package-lock.json
index 60cdefe1..61cbdb50 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "freeshow",
- "version": "1.3.3-beta.1",
+ "version": "1.3.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "freeshow",
- "version": "1.3.3-beta.1",
+ "version": "1.3.3",
"hasInstallScript": true,
"license": "GPL-3.0",
"dependencies": {
diff --git a/package.json b/package.json
index 22d63a6f..4c9928d4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "freeshow",
- "version": "1.3.3-beta.2",
+ "version": "1.3.3",
"private": true,
"main": "build/electron/index.js",
"description": "Show song lyrics and more for free!",
diff --git a/public/import-logos/bible_json.webp b/public/import-logos/bible_json.webp
new file mode 100644
index 00000000..87f94968
Binary files /dev/null and b/public/import-logos/bible_json.webp differ
diff --git a/public/import-logos/json.webp b/public/import-logos/json.webp
new file mode 100644
index 00000000..cdbebb71
Binary files /dev/null and b/public/import-logos/json.webp differ
diff --git a/public/import-logos/xml.webp b/public/import-logos/xml.webp
new file mode 100644
index 00000000..25c06996
Binary files /dev/null and b/public/import-logos/xml.webp differ
diff --git a/public/import-logos/zefania.webp b/public/import-logos/zefania.webp
deleted file mode 100644
index d566dfe6..00000000
Binary files a/public/import-logos/zefania.webp and /dev/null differ
diff --git a/public/import-logos/zip.webp b/public/import-logos/zip.webp
new file mode 100644
index 00000000..5ff16407
Binary files /dev/null and b/public/import-logos/zip.webp differ
diff --git a/public/lang/en.json b/public/lang/en.json
index 715ff5ca..257c6a67 100644
--- a/public/lang/en.json
+++ b/public/lang/en.json
@@ -40,7 +40,8 @@
"bottom": "Bottom",
"left": "Left",
"centered": "Centered",
- "edge_blending_tip": "Blend together multiple outputs/projectors for a more seamless transition"
+ "edge_blending_tip": "Blend together multiple outputs/projectors for a more seamless transition",
+ "cropping_tip": "Fine adjust the window content position by cropping the sides"
},
"about": {
"check_updates": "Look for updates",
@@ -423,6 +424,7 @@
"history": "History",
"action": "Action",
"category_action": "Category action",
+ "user_data_overwrite": "Found existing data",
"connect": "Connect",
"cloud_update": "Syncing with cloud",
"cloud_method": "Data location",
@@ -565,6 +567,7 @@
"pdf_single_page": "Render as one document",
"convert_to_images": "Convert to images",
"converting": "Converting...",
+ "closing": "Closing app...",
"remove_template_from_show": "Remove template from show",
"reset_defaults": "Reset defaults",
"to_all": "Apply to all",
@@ -747,6 +750,9 @@
"all_shows": "All shows",
"all_projects": "All projects",
"project": "Project",
+ "option_type": "What would you like to export?",
+ "option_format": "What format would you like?",
+ "include_media": "Include media files",
"preview": "Preview",
"title": "Title",
"metadata": "Metadata",
@@ -821,6 +827,7 @@
"background_media": "Background media",
"overlay_content": "Add overlay content",
"different_first_template": "Custom template on first slide",
+ "max_lines_per_slide": "Max lines per slide",
"media_fit": "Media fit",
"one_letter": "One letter mode",
"sub_indexes": "Sub indexes",
@@ -1120,6 +1127,9 @@
"show_location": "Show location",
"data_location": "Data location",
"user_data_location": "Save user settings at 'Data location'",
+ "user_data_exists": "Found existing data at custom location, would you like to overwrite it?",
+ "user_data_yes": "Yes, keep current data",
+ "user_data_no": "No, import existing data",
"popup_before_close": "Enable close confirmation popup",
"disable_hardware_acceleration": "Disable hardware acceleration",
"restart_for_change": "You have to restart the program for the change to take effect!",
@@ -1199,7 +1209,7 @@
},
"scripture": {
"bibles": "Bibles from API.Bible",
- "custom": "Or import your own",
+ "local": "Import local file",
"max_verses": "Max verses per slide",
"verse_numbers": "Verse numbers",
"verses_on_individual_lines": "Verses on individual lines",
diff --git a/public/lang/hu.json b/public/lang/hu.json
index c53bd06f..76efcd1b 100644
--- a/public/lang/hu.json
+++ b/public/lang/hu.json
@@ -38,7 +38,10 @@
"top": "Felső",
"right": "Jobb",
"bottom": "Alsó",
- "left": "Bal"
+ "left": "Bal",
+ "centered": "Középre igazított",
+ "edge_blending_tip": "Több kimenet/projektor összekeverése a zökkenőmentesebb átmenetekért",
+ "cropping_tip": "Az ablak tartalmi pozíciójának finom módosítása a szélek vágásával"
},
"about": {
"check_updates": "Frissítések ellenőrzése",
@@ -173,7 +176,6 @@
"volume": "Hangerő",
"gain": "Erősítés",
"speed": "Sebesség",
- "show": "Megjelenítés",
"flip": "Tükrözés",
"flip_horizontally": "Vízszintes tükrözés",
"flip_vertically": "Függőleges tükrözés",
@@ -197,13 +199,13 @@
"mute_when_video_plays": "Némítás videó lejátszása alatt",
"allow_gaining": "Túlerősítés engedélyezése",
"allow_gaining_tip": "Hangerő beállításának engedélyezése 100% felett (torzítást okozhat)",
- "pre_fader_volume_meter": "Hangerő-szabályozó előszabolyozó",
+ "pre_fader_volume_meter": "Hangerő-szabályozó előszabályozó",
"mixer": "Keverő",
"metronome": "Metronóm",
"toggle_metronome": "Metronóm átváltása",
"tempo": "Tempó",
"bpm": "BPM",
- "beats": "Ütemek"
+ "beats": "Ütem"
},
"menu": {
"show": "Bemutató",
@@ -317,7 +319,7 @@
"primary-darkest": "Elsődleges legsötétebb",
"text": "Szöveg",
"textInvert": "Inverz szöveg",
- "secondary-text": "Másodlagos szöveg",
+ "secondary-text": "Kijelölések",
"secondary": "Másodlagos",
"secondary-opacity": "Másodlagos átlátszatlanság",
"hover": "Rámutatás",
@@ -414,7 +416,7 @@
"import": "Importálás",
"songbeamer_import": "Songbeamer importálása",
"export": "Exportálás",
- "importing": "Importálás folyamatban",
+ "importing": "Importálás…",
"import_scripture": "Szentírás importálása",
"player": "Lejátszó",
"edit_event": "Esemény szerkesztése",
@@ -422,6 +424,7 @@
"history": "Előzmények",
"action": "Művelet",
"category_action": "Kategória művelet",
+ "user_data_overwrite": "Meglévő adatok találhatóak",
"connect": "Csatlakozás",
"cloud_update": "Szinkronizálás a felhővel",
"cloud_method": "Adatok helye",
@@ -430,6 +433,7 @@
"manage_icons": "Ikonok kezelése",
"manage_colors": "Színek kezelése",
"choose_camera": "Kamera kiválasztása",
+ "manage_tags": "Címkék kezelése",
"initialize": "Üdvözöljük a FreeShow-ban",
"unsaved": "Biztosan ki szeretne léptni?",
"cancel": "Mége",
@@ -466,6 +470,7 @@
"deleted_cache": "Médiabélyegkép gyorsítótár törölve.",
"no_songswords_easyworship": "Hiányzik a SongsWords.db fájl.",
"delete_shows_empty": "Nincsenek törlendő bemutatók.",
+ "output_capture_enabled": "Képernyőkimenet felvétele engedélyezve. Ez teljesítményproblémákat okozhat, csak akkor használja, ha valóban szükséges!",
"midi_no_project": "Eseményindítót kaptunk a projekt módosítására, de nem található projekt az indexen:",
"midi_no_show": "Eseményindítót kaptunk a dia indítására, de nincs aktív bemutató.",
"midi_no_slide": "Eseményindítót kaptunk a dia indítására, de nem található dia az indexen:",
@@ -528,6 +533,7 @@
"view_private": "Magán bemutatók megjelenítése",
"import": "Importálás",
"export": "Exportálás",
+ "imported": "Importálva!",
"duplicate": "Duplikálás",
"delete": "Törlés",
"delete_slide": "Dia törlése",
@@ -558,8 +564,10 @@
"reset": "Visszaállítás",
"create_template": "Sablon létrehozása",
"project_template_tip": "Új projekt létrehozása e sablon alapján",
+ "pdf_single_page": "Megjelenítés egy dokumentumként",
"convert_to_images": "Konvertálás képpé",
"converting": "Konvertálás…",
+ "closing": "Alkalmazás bezárása…",
"remove_template_from_show": "Sablon eltávolítása a műsorból",
"reset_defaults": "Alapértelmezések visszaállítása",
"to_all": "Alkalmazás mindre",
@@ -734,6 +742,7 @@
"export": {
"export": "Exportálás",
"export_as": "{} exportálása, mint",
+ "exporting": "Exportálás…",
"exported": "Exportálva!",
"oneFile": "Egy fájl",
"selected_shows": "Kijelölt bemutatók",
@@ -741,6 +750,7 @@
"all_shows": "Összes bemutató",
"all_projects": "Összes projekt",
"project": "Projekt",
+ "include_media": "Médiafájl felvétele",
"preview": "Előnézet",
"title": "Cím",
"metadata": "Metaadatok",
@@ -772,6 +782,7 @@
"toggle_output": "Kimenet átváltása",
"move_to_front": "Előre mozgatás",
"hide_from_preview": "Előnézet elrejtése",
+ "enable_preview": "Előnézet engedélyezése",
"lock_to_output": "Rögzítés a kimenethez",
"place_under_slide": "Dia alá helyezés",
"toggle_clock": "Óra átváltása",
@@ -814,12 +825,14 @@
"background_media": "Háttérmédia",
"overlay_content": "Átfedéses tartalom hozzáadása",
"different_first_template": "Egyedi sablon az első dián",
+ "max_lines_per_slide": "Sorok maximális száma diánként",
"media_fit": "Média méretezése",
"one_letter": "Egyetlen betű mód",
"sub_indexes": "Alindex",
"size": "Méret",
"max_lines": "Maximum sor",
"invert_items": "Elemek megfordítása",
+ "list": "Lista",
"chords": "Akkordok",
"transpose": "Transzponálás",
"auto_size": "Automatikus méret",
@@ -843,6 +856,7 @@
"outline": "Körvonal",
"shadow": "Árnyék",
"shadow_inset": "Zsugorított árnyék",
+ "offset": "Eltolás",
"offsetX": "X eltolás",
"offsetY": "Y eltolás",
"blur": "Elmosás",
@@ -889,7 +903,6 @@
},
"items": {
"text": "Szövegdoboz",
- "list": "Lista",
"media": "Média",
"image": "Kép",
"camera": "Kamera",
@@ -1062,6 +1075,7 @@
"manual_input_hint": "Nem található a kijelző? Kattintson ide a helyzete kézi módosításához.",
"manual_drag_hint": "Az aktív kimeneti ablak fölé történő manuális húzáshoz tartsa lenyomva a Ctrl/Cmd billentyűt.",
"allow_main_screen": "Egyedi kimeneti pozíció és méret engedélyezése",
+ "edge_blending": "Sarkok keverése",
"identify_screens": "Képernyők azonosítása",
"new_output": "Új kimenet",
"normal": "Normál",
@@ -1111,11 +1125,15 @@
"show_location": "Bemutatók helye",
"data_location": "Adatok helye",
"user_data_location": "Felhasználói beállítások mentése az „Adatok helyére\"",
+ "user_data_exists": "Meglévő adatok találhatóak ezen az egyéni helyen, szeretné felülírni azokat?",
+ "user_data_yes": "Igen, jelenlegi adatok megőrzése",
+ "user_data_no": "Nem, meglévő adatok importálása",
"popup_before_close": "Zárást megerősítő felugró ablak engedélyezése",
"disable_hardware_acceleration": "Hardveres gyorsítás letiltása",
"restart_for_change": "Újra kell indítani a programot a módosítás érvénybe lépéséhez!",
"font": "Betűkészlet",
"font_family": "Betűkészlet család",
+ "font_style": "Betűkészlet stílusa",
"font_size": "Betűméret",
"border_radius": "Keret sugara",
"colors": "Színek",
@@ -1153,6 +1171,7 @@
"sort": {
"sort_by": "Sorbarendezés",
"name": "Név",
+ "name_des": "Név, csőkkenő",
"date": "Dátum",
"size": "Méret",
"type": "Típus",
diff --git a/public/lang/no.json b/public/lang/no.json
index 74624c68..08026432 100644
--- a/public/lang/no.json
+++ b/public/lang/no.json
@@ -38,7 +38,10 @@
"top": "Topp",
"right": "Høyre",
"bottom": "Bunn",
- "left": "Venstre"
+ "left": "Venstre",
+ "centered": "Sentrert",
+ "edge_blending_tip": "Bland sammen flere utganger/projektorer for en mer sømløs overgang",
+ "cropping_tip": "Finjuster plasseringen til innholdet i vinduet ved å beskjære sidene"
},
"about": {
"check_updates": "Se etter oppdateringer",
@@ -173,7 +176,6 @@
"volume": "Volum",
"gain": "Forsterkning",
"speed": "Hastighet",
- "show": "Vis",
"flip": "Vend",
"flip_horizontally": "Vend horisontalt",
"flip_vertically": "Vend vertikalt",
@@ -317,7 +319,7 @@
"primary-darkest": "Primær mørkest",
"text": "Tekst",
"textInvert": "Motsatt tekst",
- "secondary-text": "Sekundær tekst",
+ "secondary-text": "Markering",
"secondary": "Sekundær",
"secondary-opacity": "Sekundær gjennomsiktig",
"hover": "Sveve",
@@ -414,7 +416,7 @@
"import": "Importer",
"songbeamer_import": "Songbeamer importering",
"export": "Eksporter",
- "importing": "Importerer",
+ "importing": "Importerer...",
"import_scripture": "Importer en bibel",
"player": "Spiller",
"edit_event": "Rediger hendelse",
@@ -422,6 +424,7 @@
"history": "Historie",
"action": "Handling",
"category_action": "Handling for kategori",
+ "user_data_overwrite": "Fant eksisterende data",
"connect": "Koble til",
"cloud_update": "Synkroniserer med sky",
"cloud_method": "Dataplassering",
@@ -430,6 +433,7 @@
"manage_icons": "Endre ikoner",
"manage_colors": "Endre farger",
"choose_camera": "Velg kamera",
+ "manage_tags": "Administrer etiketter",
"initialize": "Velkommen til FreeShow",
"unsaved": "Er du sikkert på at du vil avslutte?",
"cancel": "Avbryt",
@@ -466,6 +470,7 @@
"deleted_cache": "Slettet hurtigbuffer med medie-miniatyrbilder.",
"no_songswords_easyworship": "Mangler SongsWords.db filen.",
"delete_shows_empty": "Ingen shows å slette.",
+ "output_capture_enabled": "Opptak av utgangsskjerm aktivert, dette kan forårsake ytelsesproblemer. Bruk kun ved behov!",
"midi_no_project": "Mottok utløser til å endre prosjekt, men ingen prosjekt funnet på indeks:",
"midi_no_show": "Mottok utløser til å starte lysbilde, men ingen show aktiv.",
"midi_no_slide": "Mottok utløser til å starte lysbilde, men ingen lysbilde funnet på indeks:",
@@ -528,6 +533,7 @@
"view_private": "Vis private",
"import": "Importer",
"export": "Eksporter",
+ "imported": "Importert!",
"duplicate": "Dupliser",
"delete": "Slett",
"delete_slide": "Slett lysbilde",
@@ -558,8 +564,10 @@
"reset": "Tilbakestill",
"create_template": "Opprett mal",
"project_template_tip": "Lag et nytt prosjekt fra denne malen",
+ "pdf_single_page": "Vis som ett dokument",
"convert_to_images": "Konverter til bilder",
"converting": "Konverterer...",
+ "closing": "Lukker app...",
"remove_template_from_show": "Fjern mal fra show",
"reset_defaults": "Tilbakestill standarder",
"to_all": "Legg til alle",
@@ -734,6 +742,7 @@
"export": {
"export": "Eksporter",
"export_as": "Eksporter {} som",
+ "exporting": "Eksporterer...",
"exported": "Eksportert!",
"oneFile": "En fil",
"selected_shows": "Valgte show",
@@ -741,6 +750,7 @@
"all_shows": "Alle show",
"all_projects": "Alle prosjekter",
"project": "Prosjekt",
+ "include_media": "Inkluder mediefiler",
"preview": "Forhåndsvisning",
"title": "Tittel",
"metadata": "Metadata",
@@ -772,6 +782,7 @@
"toggle_output": "Veksle utgangsskjerm",
"move_to_front": "Flytt fremst",
"hide_from_preview": "Skjul fra forhåndsvisning",
+ "enable_preview": "Aktiver forhåndsvisning",
"lock_to_output": "Lås til utgang",
"place_under_slide": "Plasser under lysbilde",
"toggle_clock": "Veksle klokke",
@@ -814,12 +825,14 @@
"background_media": "Bakgrunnsmedia",
"overlay_content": "Legg til innhold fra overlegg",
"different_first_template": "Egen mal på første lysbilde",
+ "max_lines_per_slide": "Maks linjer per lysbilde",
"media_fit": "Tilpass medier",
"one_letter": "Én bokstav-modus",
"sub_indexes": "Underindekser",
"size": "Størrelse",
"max_lines": "Maks linjer",
"invert_items": "Inverter elementer",
+ "list": "Liste",
"chords": "Akkorder",
"transpose": "Transponer",
"auto_size": "Auto-størrelse",
@@ -843,6 +856,7 @@
"outline": "Strek",
"shadow": "Skygge",
"shadow_inset": "Innfelt skygge",
+ "offset": "Forskyvning",
"offsetX": "Forskyvning X",
"offsetY": "Forskyvning Y",
"blur": "Uklarhet",
@@ -889,7 +903,6 @@
},
"items": {
"text": "Tekstboks",
- "list": "Liste",
"media": "Media",
"image": "Bilde",
"camera": "Kamera",
@@ -1062,6 +1075,7 @@
"manual_input_hint": "Finner ikke skjermen? Trykk her for å endre plasseringen manuelt.",
"manual_drag_hint": "Du kan også holde ctrl/cmd over en aktiv utgangsskjerm for å flytte den manuelt.",
"allow_main_screen": "Tillat egendefinert posisjon og størrelse på utgang",
+ "edge_blending": "Edge blending",
"identify_screens": "Identifiser skjermer",
"new_output": "Ny utgang",
"normal": "Normal",
@@ -1111,11 +1125,15 @@
"show_location": "Show-plassering",
"data_location": "Data-plassering",
"user_data_location": "Lagre bruker-innstillinger i 'Data-plassering'",
+ "user_data_exists": "Fant eksisterende data på egendefinert plassering, vil du overskrive det?",
+ "user_data_yes": "Ja, behold nåværende data",
+ "user_data_no": "Nei, importer eksisterende data",
"popup_before_close": "Aktiver popup for bekreftelse av lukking",
"disable_hardware_acceleration": "Deaktiver maskinvareakselerasjon",
"restart_for_change": "Du må starte programmet på nytt for at endringen skal tre i kraft!",
"font": "Skrift",
"font_family": "Skrifttype",
+ "font_style": "Skriftstil",
"font_size": "Skriftstørrelse",
"border_radius": "Hjørneradius",
"colors": "Farger",
@@ -1153,6 +1171,7 @@
"sort": {
"sort_by": "Sorter etter",
"name": "Navn",
+ "name_des": "Navn Synkende",
"date": "Dato",
"size": "Størrelse",
"type": "Type",
diff --git a/public/lang/zh_CN.json b/public/lang/zh_CN.json
index ba39dfb7..56cf43d8 100644
--- a/public/lang/zh_CN.json
+++ b/public/lang/zh_CN.json
@@ -38,7 +38,10 @@
"top": "上",
"right": "右",
"bottom": "下",
- "left": "左"
+ "left": "左",
+ "centered": "Centered",
+ "edge_blending_tip": "Blend together multiple outputs/projectors for a more seamless transition",
+ "cropping_tip": "Fine adjust the window content position by cropping the sides"
},
"about": {
"check_updates": "检查更新...",
@@ -163,6 +166,7 @@
"toggle_shuffle": "切换随机播放",
"next": "下一个",
"previous": "上一个",
+ "play_no_audio": "无声播放",
"play_no_filters": "无滤镜播放",
"favourite": "收藏",
"pause": "暂停",
@@ -172,7 +176,6 @@
"volume": "音量",
"gain": "增益",
"speed": "速度",
- "show": "节目",
"flip": "翻转",
"flip_horizontally": "水平翻转",
"flip_vertically": "垂直翻转",
@@ -194,7 +197,10 @@
"playlist_settings": "播放列表设置",
"custom_output": "自定义音频输出",
"mute_when_video_plays": "视频播放时静音",
+ "allow_gaining": "允许获取",
+ "allow_gaining_tip": "允许将音量设置超过100%(可能导致失真)",
"pre_fader_volume_meter": "推子前音量表",
+ "mixer": "调音台",
"metronome": "节拍器",
"toggle_metronome": "显示/隐藏节拍器",
"tempo": "节奏",
@@ -269,15 +275,17 @@
"ip": "无法获取你的设备IP,请转到计算机的 Wi-Fi 设置中查找本地 IPv4 地址。"
},
"meta": {
+ "number": "数字",
"title": "标题",
"artist": "艺术家",
"author": "作者",
"composer": "作曲家",
"publisher": "发行商",
"copyright": "版权",
- "CCLI": "授权协议(CCLI)",
+ "CCLI": "歌曲 ID(CCLI)",
"year": "年份",
"key": "调号",
+ "autofill": "自动填充",
"message": "消息",
"message_tip": "在全部幻灯片上显示内容",
"auto_media": "从媒体内容中获取元数据",
@@ -311,7 +319,7 @@
"primary-darkest": "主要最暗色",
"text": "文本",
"textInvert": "反转文字",
- "secondary-text": "辅助文本",
+ "secondary-text": "Selections",
"secondary": "辅助色",
"secondary-opacity": "辅助透明度",
"hover": "悬浮",
@@ -362,7 +370,8 @@
"music": "音乐",
"offers": "报价",
"notice": "通知",
- "visuals": "视觉"
+ "visuals": "视觉",
+ "action_tip": "每次展示该类别的节目时触发的操作。"
},
"groups": {
"current": "当前",
@@ -400,19 +409,22 @@
"change_output_values": "修改输出值",
"choose_chord": "选择和弦",
"set_time": "设置时间",
+ "slide_shortcut": "幻灯片快捷键",
"animate": "动画",
"translate": "本地化",
"next_timer": "下一张幻灯片计时器",
"import": "导入",
"songbeamer_import": "Songbeamer 导入",
"export": "导出",
- "importing": "正在导入",
+ "importing": "Importing...",
"import_scripture": "导入圣经",
"player": "播放器",
"edit_event": "编辑事件",
"about": "关于",
"history": "历史记录",
"action": "动作",
+ "category_action": "分类动作",
+ "user_data_overwrite": "Found existing data",
"connect": "连接",
"cloud_update": "与云端同步",
"cloud_method": "数据位置",
@@ -421,6 +433,7 @@
"manage_icons": "管理图标",
"manage_colors": "管理颜色",
"choose_camera": "选择相机",
+ "manage_tags": "Manage tags",
"initialize": "欢迎使用 FreeShow",
"unsaved": "你确定要退出吗?",
"cancel": "取消",
@@ -457,6 +470,7 @@
"deleted_cache": "已删除媒体缩略图缓存。",
"no_songswords_easyworship": "缺少 SongsWords.db 文件。",
"delete_shows_empty": "没有节目可删除。",
+ "output_capture_enabled": "Output screen capture enabled, this may cause performance issues. Only use if needed!",
"midi_no_project": "收到更改项目的触发信号,但未在指定索引处未找到项目:",
"midi_no_show": "接收到开始播放幻灯片的指令,但当前没有正在进行的节目。",
"midi_no_slide": "收到开始幻灯片的触发信号,但未在以下序号处找到幻灯片:",
@@ -519,6 +533,7 @@
"view_private": "节目私有",
"import": "导入",
"export": "导出",
+ "imported": "Imported!",
"duplicate": "创建副本",
"delete": "删除",
"delete_slide": "删除幻灯片",
@@ -547,8 +562,12 @@
"zoomIn": "放大",
"zoomOut": "缩小",
"reset": "重置",
+ "create_template": "创建模板",
+ "project_template_tip": "使用此模板创建新项目",
+ "pdf_single_page": "Render as one document",
"convert_to_images": "转换成图片",
"converting": "正在转换…",
+ "closing": "Closing app...",
"remove_template_from_show": "从节目中移除模板",
"reset_defaults": "重置默认设置",
"to_all": "全部应用",
@@ -608,6 +627,8 @@
"set_key": "设置按键",
"custom_key": "设置自定义值",
"select_chord": "选择这个和弦",
+ "play_with_shortcut": "使用快捷键激活",
+ "press_to_assign": "按任意字母键进行分配",
"play_on_midi": "在 MIDI 信号上激活",
"play_on_midi_tip": "接收到指定的 MIDI 信号时激活此特定幻灯片",
"send_midi": "发送 MIDI 信号",
@@ -624,6 +645,7 @@
"next_after_media": "媒体播放完成后下一个",
"remove_media": "移除媒体",
"remove_layers": "移除图层",
+ "toggle_checkbox_tip": "如果复选框未更改,动作将切换",
"start_recording": "开始录制",
"stop_recording": "停止录制",
"export_recording": "停止录制并导出",
@@ -632,6 +654,7 @@
"previous_project_item": "上一个项目元素",
"index_select_project_item": "按序号选择项目元素",
"name_select_show": "按名称选择节目",
+ "set_template_active": "在活动节目中设置模板",
"random_slide": "随机播放幻灯片",
"index_select_slide": "按序号选择幻灯片",
"name_select_slide": "按名称选择幻灯片",
@@ -671,7 +694,8 @@
"activate_scripture_start": "当经文开始时激活",
"activate_slide_cleared": "清除幻灯片时激活",
"activate_background_cleared": "背景清除时激活",
- "activate_show_created": "创建节目时启动",
+ "activate_show_created": "创建节目时激活",
+ "activate_show_opened": "节目打开时激活",
"activate_audio_playlist_ended": "音频播放列表结束时激活"
},
"recording": {
@@ -679,7 +703,9 @@
"tip": "记录并重放幻灯片的时间。与第一张幻灯片上的音轨同步。",
"layout_changed": "布局自上次录制后已更改!",
"audio_synced": "与音频同步!",
- "start": "开始幻灯片录制"
+ "start": "开始幻灯片录制",
+ "use_duration": "使用持续时间",
+ "use_duration_tip": "使用持续时间时间而非时间戳时间"
},
"animate": {
"change": "更改",
@@ -716,6 +742,7 @@
"export": {
"export": "导出",
"export_as": "将 {} 导出为",
+ "exporting": "Exporting...",
"exported": "已导出!",
"oneFile": "单文件",
"selected_shows": "已选的节目",
@@ -723,6 +750,7 @@
"all_shows": "全部节目",
"all_projects": "全部项目",
"project": "项目",
+ "include_media": "Include media files",
"preview": "预览",
"title": "标题",
"metadata": "元数据",
@@ -738,6 +766,7 @@
},
"context": {
"enabledTabs": "切换标签",
+ "setTag": "设置标签",
"filterByTags": "按标签过滤",
"addToProject": "添加到项目",
"add_to_show": "添加到节目",
@@ -753,6 +782,7 @@
"toggle_output": "切换输出",
"move_to_front": "移到最前",
"hide_from_preview": "在预览中隐藏",
+ "enable_preview": "Enable preview",
"lock_to_output": "锁定输出",
"place_under_slide": "置于幻灯片下方",
"toggle_clock": "切换时间",
@@ -795,10 +825,14 @@
"background_media": "背景媒体",
"overlay_content": "添加叠加内容",
"different_first_template": "在第一张幻灯片上使用自定义模板",
+ "max_lines_per_slide": "Max lines per slide",
"media_fit": "媒体适应",
+ "one_letter": "单字母模式",
+ "sub_indexes": "子索引",
"size": "大小",
"max_lines": "最大行数",
"invert_items": "反转元素",
+ "list": "List",
"chords": "合弦",
"transpose": "转置",
"auto_size": "自动调整大小",
@@ -822,6 +856,7 @@
"outline": "描边",
"shadow": "阴影",
"shadow_inset": "内阴影",
+ "offset": "Offset",
"offsetX": "X 轴偏移",
"offsetY": "Y 轴偏移",
"blur": "模糊",
@@ -868,7 +903,6 @@
},
"items": {
"text": "文本框",
- "list": "列表",
"media": "媒体",
"image": "图片",
"camera": "相机",
@@ -916,6 +950,7 @@
"to_event": "距离事件时间",
"counter": "倒计时",
"time": "时间",
+ "minutes": "分钟",
"seconds": "秒",
"from": "从",
"to": "到",
@@ -1033,19 +1068,20 @@
"use24hClock": "显示24小时制时间",
"styles_hint": "创建不同的样式,应用于输出以改变外观。",
"hide_output_hint": "双击输出窗口可隐藏。按住 Ctrl 键可拖动。",
- "hide_menubar_hint": "要隐藏菜单栏,请启用独占模式,或在 macOS 设置中启用“自动隐藏和显示菜单栏”。",
+ "hide_menubar_hint": "要隐藏菜单栏,请启用展台模式,或在 macOS 设置中启用“自动隐藏和显示菜单栏”。",
"show_output_hint": "按住 Ctrl/Cmd 并点击显示按钮,可以强制显示内容在此屏幕。",
"move_output_hint": "找不到显示器?调整窗口位置,直到它出现在第二屏幕上。",
"select_display": "点击你希望显示输出窗口的屏幕。",
"manual_input_hint": "找不到显示屏?点击这里手动改变位置。",
"manual_drag_hint": "可通过按住 Ctrl/Cmd 键并拖动激活的输出窗口来手动更改其位置。",
"allow_main_screen": "允许自定义输出位置和大小",
+ "edge_blending": "Edge blending",
"identify_screens": "识别屏幕",
"new_output": "新建输出",
"normal": "正常",
"enable_key_output": "启用 Alpha 密钥输出",
"always_on_top": "总在最前",
- "kiosk_mode": "独占模式",
+ "kiosk_mode": "展台模式",
"change_key_output_position": "更改关键信息输出位置",
"position": "位置",
"enabled": "启用",
@@ -1063,6 +1099,7 @@
"group_numbers": "分组编号",
"full_colors": "高对比度分组颜色",
"slide_number_keys": "使用数字键播放幻灯片",
+ "auto_shortcut_first_letter": "文本中首字母的自动快捷键",
"auto_output": "启动时激活输出屏幕",
"hide_cursor_in_output": "在输出中隐藏鼠标指针",
"clear_media_when_finished": "播放完成时清除媒体",
@@ -1088,11 +1125,15 @@
"show_location": "节目位置",
"data_location": "数据位置",
"user_data_location": "将个人设置存储在“数据位置”",
+ "user_data_exists": "Found existing data at custom location, would you like to overwrite it?",
+ "user_data_yes": "Yes, keep current data",
+ "user_data_no": "No, import existing data",
"popup_before_close": "关闭时显示确认弹窗",
"disable_hardware_acceleration": "禁用硬件加速",
"restart_for_change": "你必须重启程序才能使修改生效!",
"font": "字体",
"font_family": "字体",
+ "font_style": "Font style",
"font_size": "字体大小",
"border_radius": "边框圆角",
"colors": "颜色",
@@ -1124,11 +1165,13 @@
"auto": "自动",
"optimized": "已优化",
"reduced": "减少的",
- "full": "完整"
+ "full": "完整",
+ "section_trigger_action": "导航到演示文稿的某个部分时触发操作"
},
"sort": {
"sort_by": "排序",
"name": "名称",
+ "name_des": "Name Descending",
"date": "日期",
"size": "大小",
"type": "类型",
@@ -1172,6 +1215,7 @@
"reference": "演出偏好设置",
"split_reference": "拆分偏好设置",
"combine_with_text": "合并到文本",
+ "first_slide_reference": "在第一页幻灯片上引用",
"reference_at_bottom": "移到底部",
"red_jesus": "耶稣的话用红色显示",
"search": "在圣经中搜索"
diff --git a/src/electron/data/export.ts b/src/electron/data/export.ts
index b2b00160..d858683e 100644
--- a/src/electron/data/export.ts
+++ b/src/electron/data/export.ts
@@ -243,37 +243,46 @@ function exportAllShows(data: any) {
// ----- PROJECT -----
export function exportProject(data: any) {
- toApp(MAIN, {channel: "ALERT", data: "export.exporting"})
+ toApp(MAIN, { channel: "ALERT", data: "export.exporting" })
+
+ const files = data.file.files || []
+ if (!files.length) {
+ // export as plain JSON
+ writeFile(join(data.path, data.name), ".project", JSON.stringify(data.file), "utf-8", (err: any) => doneWritingFile(err, data.path))
+ return
+ }
// create archive
const zip = new AdmZip()
// copy files
- const files = data.file.files || []
files.forEach((path: string) => {
zip.addLocalFile(path)
- });
+ })
// add project file
zip.addFile("data.json", Buffer.from(JSON.stringify(data.file)))
-
- const outputPath = join(data.path, data.name + ".project")
- zip.writeZip(outputPath, (err: any) => doneWritingFile(err, data.path));
- // plain JSON
- // writeFile(join(data.path, data.name), ".project", JSON.stringify(data.file), "utf-8", (err: any) => doneWritingFile(err, data.path))
+ const outputPath = join(data.path, data.name)
+ let p = getUniquePath(outputPath, ".project")
+ zip.writeZip(p, (err: any) => doneWritingFile(err, data.path))
}
// ----- HELPERS -----
function writeFile(path: string, extension: string, data: any, options: any = undefined, callback: any) {
+ let p = getUniquePath(path, extension)
+ fs.writeFile(p, data, options, callback)
+}
+
+function getUniquePath(path: string, extension: string) {
let number = -1
- let tempPath: string = path
+ let p: string = path
do {
number++
- tempPath = path + (number ? "_" + number : "") + extension
- } while (doesPathExist(tempPath))
+ p = path + (number ? "_" + number : "") + extension
+ } while (doesPathExist(p))
- fs.writeFile(tempPath, data, options, callback)
+ return p
}
diff --git a/src/electron/data/store.ts b/src/electron/data/store.ts
index 255026cc..4b42ceed 100644
--- a/src/electron/data/store.ts
+++ b/src/electron/data/store.ts
@@ -9,6 +9,7 @@ import path from "path"
import { STORE } from "../../types/Channels"
import { dataFolderNames, deleteFile, doesPathExist, readFile } from "../utils/files"
import { defaultConfig, defaultSettings, defaultSyncedSettings } from "./defaults"
+import { forceCloseApp } from "../utils/responses"
const fileNames: { [key: string]: string } = {
error_log: "error_log",
@@ -169,7 +170,10 @@ function updateStoresPath(load: boolean = false) {
Object.keys(portableData).forEach((id) => createStoreAtNewLocation(id, load))
}
+let error: boolean = false
function createStoreAtNewLocation(id: string, load: boolean = false) {
+ if (error) return
+
let key = portableData[id].key
let tempData: any = {}
if (!load) {
@@ -181,7 +185,21 @@ function createStoreAtNewLocation(id: string, load: boolean = false) {
}
// set new stores to export
- stores[key] = new Store({ name: fileNames[id], defaults: portableData[id].defaults || {}, cwd: userDataPath! })
+ try {
+ stores[key] = new Store({ name: fileNames[id], defaults: portableData[id].defaults || {}, cwd: userDataPath! })
+ } catch (err) {
+ error = true
+ console.log("Can't create store at set location!", err)
+
+ // revert
+ let special = stores.SETTINGS.get("special")
+ special.customUserDataLocation = false
+ stores.SETTINGS.set("special", special)
+ stores.SETTINGS.set("dataPath", "")
+ stores.SETTINGS.set("showsPath", "")
+
+ forceCloseApp()
+ }
if (load || !Object.keys(tempData).length) return
diff --git a/src/electron/data/thumbnails.ts b/src/electron/data/thumbnails.ts
index 751ff905..f98789c7 100644
--- a/src/electron/data/thumbnails.ts
+++ b/src/electron/data/thumbnails.ts
@@ -1,11 +1,13 @@
-import { NativeImage, ResizeOptions, app, nativeImage } from "electron"
+import { BrowserWindow, NativeImage, ResizeOptions, app, nativeImage } from "electron"
import fs from "fs"
import path from "path"
-import { isProd, toApp } from ".."
-import { MAIN } from "../../types/Channels"
+import { isProd, loadWindowContent, toApp } from ".."
+import { MAIN, OUTPUT } from "../../types/Channels"
import { doesPathExist, doesPathExistAsync, makeDir } from "../utils/files"
import { waitUntilValueIsDefined } from "../utils/helpers"
import { imageExtensions, videoExtensions } from "./media"
+import { captureOptions } from "../utils/windowOptions"
+import { OutputHelper } from "../output/OutputHelper"
export function getThumbnail(data: any) {
let output = createThumbnail(data.input, data.size || 500)
@@ -188,3 +190,33 @@ function saveToDisk(savePath: string, image: NativeImage, nextOnFinished: boolea
if (nextOnFinished) generationFinished()
})
}
+
+///// CAPTURE SLIDE /////
+
+export function captureSlide(data: any) {
+ const OUTPUT_ID = "capture"
+ if (OutputHelper.getOutput(OUTPUT_ID)) return
+
+ let window = new BrowserWindow({ ...captureOptions, width: data.resolution?.width, height: data.resolution?.height })
+ loadWindowContent(window, "output")
+
+ OutputHelper.setOutput(OUTPUT_ID, { window })
+
+ window.on("ready-to-show", () => {
+ // send correct output data after load
+ setTimeout(() => {
+ window.webContents.send(OUTPUT, { channel: "OUTPUTS", data: data.output })
+ // WIP mute videos
+
+ // wait for content load
+ setTimeout(async () => {
+ const page = await window.capturePage()
+ const base64 = page.toDataURL({ scaleFactor: 1 })
+ toApp(MAIN, { channel: "CAPTURE_SLIDE", data: { listenerId: data.listenerId, base64 } })
+
+ window.destroy()
+ OutputHelper.deleteOutput(OUTPUT_ID)
+ }, 3000)
+ }, 1000)
+ })
+}
diff --git a/src/electron/index.ts b/src/electron/index.ts
index 733e3da8..1cee8645 100644
--- a/src/electron/index.ts
+++ b/src/electron/index.ts
@@ -158,21 +158,22 @@ function createMain() {
if (RECORD_STARTUP_TIME) console.timeEnd("Main window")
}
-export function loadWindowContent(window: BrowserWindow, isOutput: boolean = false) {
- if (!isOutput && RECORD_STARTUP_TIME) console.time("Main window content")
- if (!isOutput) console.log("Loading main window content")
+export function loadWindowContent(window: BrowserWindow, type: null | "output" = null) {
+ let mainOutput = type === null
+ if (mainOutput && RECORD_STARTUP_TIME) console.time("Main window content")
+ if (mainOutput) console.log("Loading main window content")
if (isProd) window.loadFile("public/index.html").catch(error)
else window.loadURL("http://localhost:3000").catch(error)
window.webContents.on("did-finish-load", () => {
- if (window === mainWindow) isOutput = false // make sure window is not output
- window.webContents.send(STARTUP, { channel: "TYPE", data: isOutput ? "output" : null })
- if (!isOutput) retryLoadingContent()
+ // if (window === mainWindow) type = null // make sure type is correct
+ window.webContents.send(STARTUP, { channel: "TYPE", data: type })
+ if (mainOutput) retryLoadingContent()
})
function error(err: any) {
console.error("Failed to load window:", JSON.stringify(err))
- if (isLoaded && !isOutput) app.quit()
+ if (isLoaded && mainOutput) app.quit()
}
}
@@ -251,15 +252,16 @@ export async function exitApp() {
stopMidi()
- 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()
- }
+ // relaunch does not work very well as it launched new processes
+ // 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
diff --git a/src/electron/output/helpers/OutputLifecycle.ts b/src/electron/output/helpers/OutputLifecycle.ts
index 18b16cb8..7d492e7d 100644
--- a/src/electron/output/helpers/OutputLifecycle.ts
+++ b/src/electron/output/helpers/OutputLifecycle.ts
@@ -89,7 +89,7 @@ export class OutputLifecycle {
})
// window.setVisibleOnAllWorkspaces(true)
- loadWindowContent(window, true)
+ loadWindowContent(window, "output")
this.setWindowListeners(window, { id, name })
// open devtools
diff --git a/src/electron/utils/responses.ts b/src/electron/utils/responses.ts
index 87bfe75e..17393fcd 100644
--- a/src/electron/utils/responses.ts
+++ b/src/electron/utils/responses.ts
@@ -6,14 +6,14 @@ import { app, BrowserWindow, desktopCapturer, DesktopCapturerSource, Display, sc
import { machineIdSync } from "node-machine-id"
import os from "os"
import path from "path"
-import { closeMain, isProd, mainWindow, maximizeMain, setGlobalMenu, toApp } from ".."
+import { closeMain, exitApp, isProd, mainWindow, maximizeMain, setGlobalMenu, toApp } from ".."
import { BIBLE, MAIN, SHOW } from "../../types/Channels"
import { restoreFiles } from "../data/backup"
import { downloadMedia } from "../data/downloadMedia"
import { importShow } from "../data/import"
import { convertPDFToImages } from "../data/pdfToImage"
import { config, error_log, stores } from "../data/store"
-import { getThumbnail, getThumbnailFolderPath, saveImage } from "../data/thumbnails"
+import { captureSlide, getThumbnail, getThumbnailFolderPath, saveImage } from "../data/thumbnails"
import { OutputHelper } from "../output/OutputHelper"
import { getPresentationApplications, presentationControl, startSlideshow } from "../output/ppt/presentation"
import { closeServers, startServers } from "../servers"
@@ -23,6 +23,7 @@ import {
bundleMediaFiles,
checkShowsFolder,
dataFolderNames,
+ doesPathExist,
getDataFolder,
getDocumentsFolder,
getFileInfo,
@@ -127,6 +128,7 @@ const mainResponses: any = {
MEDIA_CODEC: (data: any) => getMediaCodec(data),
DOWNLOAD_MEDIA: (data: any) => downloadMedia(data),
MEDIA_BASE64: (data: any) => storeMedia(data),
+ CAPTURE_SLIDE: (data: any) => captureSlide(data),
PDF_TO_IMAGE: (data: any) => convertPDFToImages(data),
ACCESS_CAMERA_PERMISSION: () => getPermission("camera"),
ACCESS_MICROPHONE_PERMISSION: () => getPermission("microphone"),
@@ -162,6 +164,19 @@ const mainResponses: any = {
// FILES
RESTORE: (data: any) => restoreFiles(data),
SYSTEM_OPEN: (data: any) => openSystemFolder(data),
+ DOES_PATH_EXIST: (data: any) => {
+ let p = data.path
+ if (p === "data_config") p = path.join(data.dataPath, dataFolderNames.userData)
+ return { ...data, exists: doesPathExist(p) }
+ },
+ UPDATE_DATA_PATH: () => {
+ // updateDataPath({ ...data, load: true })
+ let special = stores.SETTINGS.get("special")
+ special.customUserDataLocation = true
+ stores.SETTINGS.set("special", special)
+
+ forceCloseApp()
+ },
LOCATE_MEDIA_FILE: (data: any) => locateMediaFile(data),
GET_SIMULAR: (data: any) => getSimularPaths(data),
BUNDLE_MEDIA_FILES: (data: any) => bundleMediaFiles(data),
@@ -242,6 +257,12 @@ function getScreens(type: "window" | "screen" = "screen") {
}
}
+export function forceCloseApp() {
+ toApp(MAIN, { channel: "ALERT", data: "actions.closing" })
+ // let user read message and action finish
+ setTimeout(exitApp, 2000)
+}
+
// RECORDER
// only open once per session
let systemOpened: boolean = false
diff --git a/src/electron/utils/windowOptions.ts b/src/electron/utils/windowOptions.ts
index a590f33a..8635f376 100644
--- a/src/electron/utils/windowOptions.ts
+++ b/src/electron/utils/windowOptions.ts
@@ -97,14 +97,20 @@ export const exportOptions: BrowserWindowConstructorOptions = {
},
}
-// export const captureOptions: BrowserWindowConstructorOptions = {
-// show: false,
-// modal: true,
-// frame: false,
-// skipTaskbar: true,
-// webPreferences: {
-// webSecurity: isProd,
-// backgroundThrottling: false,
-// offscreen: true,
-// },
-// }
+export const captureOptions: BrowserWindowConstructorOptions = {
+ show: false,
+ backgroundColor: "#000000",
+ frame: false,
+ skipTaskbar: true,
+ webPreferences: {
+ preload: join(__dirname, "..", "preload"),
+ webSecurity: isProd,
+ nodeIntegration: !isProd,
+ contextIsolation: true,
+ allowRunningInsecureContent: false,
+ webviewTag: true,
+ backgroundThrottling: false,
+ autoplayPolicy: "no-user-gesture-required",
+ offscreen: true,
+ },
+}
diff --git a/src/frontend/MainLayout.svelte b/src/frontend/MainLayout.svelte
index 3e93acd1..b3b0826c 100644
--- a/src/frontend/MainLayout.svelte
+++ b/src/frontend/MainLayout.svelte
@@ -92,7 +92,7 @@
{:else if $activeEdit.type === "effect"}