diff --git a/public/lang/hu.json b/public/lang/hu.json
index 0250cb66..7592524a 100644
--- a/public/lang/hu.json
+++ b/public/lang/hu.json
@@ -256,10 +256,10 @@
},
"show_at": {
"never": "Egyetlen dián sem",
- "always": "Minden dia",
- "first": "Első dia",
- "last": "Utolsó dia",
- "first_last": "Első és utolsó dia"
+ "always": "Minden dián",
+ "first": "Első dián",
+ "last": "Utolsó dián",
+ "first_last": "Első és utolsó dián"
},
"themes": {
"default": "Alapértelmezett",
@@ -359,6 +359,7 @@
"change_name": "Név módosítása",
"choose_screen": "Képernyő kiválasztása",
"change_output_values": "Kimeneti értékek módosítása",
+ "choose_chord": "Akkord kiválasztása",
"set_time": "Idő beállítása",
"animate": "Animálás",
"next_timer": "Következő dia időzítője",
@@ -530,6 +531,7 @@
"svg_clipboard": "SVG importálása vágólapról",
"fullscreen_preview": "Teljes képernyős előnézet átváltása",
"toggle_output": "Kimeneti képernyő átváltása",
+ "toggle_panels": "Panel átváltása",
"change_tab": "Fül módosítása",
"change_drawer_tab": "Rajzoló fül módosítása",
"change_slide": "Dia módosítása",
@@ -540,8 +542,14 @@
"slide_actions": "Diaműveletek",
"item_actions": "Elemművelet",
"clear_history": "Előzmények törlése",
+ "chord_info": "Kérjük, kattintson bármelyik betűre akkord hozzáadásához.",
+ "chord_key": "Kulcs",
+ "chord_type": "Típus",
+ "chord_tension": "Színezőhang",
+ "chord_bass": "Basszus",
"set_key": "Billentyű beállítása",
"custom_key": "Egyéni érték beállítása",
+ "select_chord": "Akkord kijelölése",
"play_on_midi": "Aktiválás MIDI jelre",
"play_on_midi_tip": "A kiválasztott MIDI jel fogadásakor aktiválja ezt a speciális diát.",
"send_midi": "MIDI jel küldése",
@@ -593,6 +601,7 @@
"activate_video_ending": "Aktiválás videó befejezésekor",
"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"
},
"animate": {
@@ -694,6 +703,7 @@
"_title_underline": "Aláhúzott",
"_title_strikethrough": "Áthúzott",
"color": "Szín",
+ "accent_color": "Ékezet színe",
"background_color": "Háttérszín",
"background_opacity": "Háttérátlátszatlanság",
"background_image": "Háttérkép",
@@ -766,7 +776,8 @@
"start_days_from_today": "Kezdés napokban a mai naptól",
"just_one_day": "Csak egy nap",
"enable_start_date": "Kezdési dátum engedélyezése",
- "disable_navigation": "Navigációs billentyűk letiltása"
+ "disable_navigation": "Navigációs billentyűk letiltása",
+ "progress_bar": "Folyamatjelző"
},
"items": {
"text": "Szövegdoboz",
@@ -783,6 +794,7 @@
"timer": "Időzítő",
"variable": "Változó",
"web": "Weboldal",
+ "slide_tracker": "Folyamat",
"visualizer": "Vizualizáló",
"captions": "Feliratok",
"icon": "Ikon"
@@ -888,6 +900,7 @@
"next_slide_notes": "Következő dia jegyzetei",
"output": "Kimenet",
"current_output": "Aktuális kimenet",
+ "slide_tracker": "Folyamat",
"time": "Idő",
"system_clock": "Rendszeróra",
"video_time": "Videó idő",
@@ -921,7 +934,7 @@
"minutes": "perc",
"use24hClock": "24 órás időformátum használata",
"styles_hint": "Különböző stílusok létrehozása, amelyeket alkalmazni lehet a kimenetre a megjelenés módosítására.",
- "hide_output_hint": "Duplán kattintás a kimeneti ablakra a elrejtéséhez. Húzás lenyomva tartott Ctrl billentyűvel.",
+ "hide_output_hint": "Dupla kattintással a kimeneti ablakon az elrejtéséhez. Lenyomva tartott Ctrl billentyűvel a húzásához.",
"hide_menubar_hint": "Menüsor elrejtése a kioszk mód engedélyezesével, vagy „A menüsor automatikus elrejtése vagy megjelenítése” engedélyezésével a MacOS általános beállításaiban.",
"show_output_hint": "Megjelenítés kényszerítése a második monitoron a Ctrl/Cmd billentyű lenyomva tartásával a megjelenítés gombra kattintáskor.",
"move_output_hint": "Nem található a kijelző? Változtassa meg az elhelyezését, amíg az ablak meg nem jelenik a második képernyőn.",
@@ -958,6 +971,7 @@
"audio_fade_duration": "Halkítás időtartama",
"audio_crossfade": "Hang kereszthalkítása",
"max_auto_font_size": "Maximális automatikus betűméret",
+ "clear_style_background_on_text": "Stílusháttér törlése, amikor a dia aktív",
"resolution": "Felbontás",
"cropping": "Vágás",
"frame_rate": "Képkockasebesség",
@@ -1132,12 +1146,12 @@
"info": {
"created": "Létrehozás dátuma",
"modified": "Módosítva",
- "used": "Alkalmazott",
+ "used": "Alkalmazva",
"changed": "Módosított",
"extension": "Fájlkiterjesztés",
"size": "Méret",
- "slides": "Diák",
- "words": "Szavak",
+ "slides": "Dia",
+ "words": "Szó",
"template": "Sablon",
"category": "Kategória"
},
diff --git a/src/frontend/components/drawer/bible/Scripture.svelte b/src/frontend/components/drawer/bible/Scripture.svelte
index e76225d0..3544d2a3 100644
--- a/src/frontend/components/drawer/bible/Scripture.svelte
+++ b/src/frontend/components/drawer/bible/Scripture.svelte
@@ -857,8 +857,9 @@
{id}
draggable="true"
on:mouseup={(e) => selectVerse(e, id)}
- on:mousedown={() => {
- if (!activeVerses.includes(id)) activeVerses = [...activeVerses, id]
+ on:mousedown={(e) => {
+ if (e.ctrlKey || e.metaKey || e.shiftKey) return
+ if (!activeVerses.includes(id)) activeVerses = [id]
updateActiveVerses()
}}
on:dblclick={() => playOrClearScripture(true)}
diff --git a/src/frontend/components/helpers/audio.ts b/src/frontend/components/helpers/audio.ts
index c04b5cff..d61a0dbd 100644
--- a/src/frontend/components/helpers/audio.ts
+++ b/src/frontend/components/helpers/audio.ts
@@ -10,15 +10,7 @@ import { encodeFilePath } from "./media"
import { checkNextAfterMedia } from "./showActions"
import { customActionActivation } from "../actions/actions"
-// let currentlyStarting: string[] = []
export async function playAudio({ path, name = "", audio = null, stream = null }: any, pauseIfPlaying: boolean = true, startAt: number = 0, playMultiple: boolean = false, crossfade: number = 0) {
- // // prevent starting the same song rapidly
- // if (currentlyStarting.includes(path)) return
- // currentlyStarting.push(path)
- // setTimeout(() => {
- // currentlyStarting.splice(currentlyStarting.indexOf(path), 1)
- // }, 3000)
-
let existing: any = get(playingAudio)[path]
if (existing) {
if (!pauseIfPlaying) {
@@ -88,7 +80,10 @@ export async function playAudio({ path, name = "", audio = null, stream = null }
audio.addEventListener("canplay", () => {
setTimeout(() => {
- audio.play()
+ // audio might have been cleared
+ if (!get(playingAudio)[path]?.audio) return
+
+ get(playingAudio)[path].audio.play()
analyseAudio()
}, waitToPlay * 1000)
})
@@ -225,8 +220,6 @@ export function playlistNext(previous: string = "", specificSong: string = "", c
return
}
- console.log("PLAY NEXT", nextSong, crossfade)
-
// prevent playing the same song twice (while it's fading) to stop duplicate audio
if (Object.keys(playingAudio).includes(nextSong)) return
@@ -343,7 +336,11 @@ export function analyseAudio() {
}, audioUpdateInterval)
}
+let previousMerge = 0
function mergeAudio(allAudio) {
+ let timeSinceLast = Date.now() - previousMerge
+ if (timeSinceLast > 100 && timeSinceLast < 200) return // skip if overloaded
+
let allLefts: number[] = []
let allRights: number[] = []
@@ -362,6 +359,7 @@ function mergeAudio(allAudio) {
if (allLefts.length || allRights.length) merged = { left: getHighestNumber(allLefts), right: getHighestNumber(allRights) }
audioChannels.set(merged)
+ previousMerge = Date.now()
}
const extraMargin = 0.1 // s
@@ -399,11 +397,11 @@ function getPlayingAudio() {
let playlist = get(audioPlaylists)[audioPath] || {}
playingAudio.update((a: any) => {
+ a[audioPath]?.audio?.pause()
delete a[audioPath]
return a
})
- console.log("ENDED", playlist, playlist.loop !== false)
playlistNext(audioPath, "", 0, playlist.loop !== false)
return false
} else {
@@ -412,6 +410,7 @@ function getPlayingAudio() {
// a[audioPath].audio?.pause()
a[audioPath].paused = true
} else {
+ a[audioPath]?.audio?.pause()
delete a[audioPath]
}
diff --git a/src/frontend/components/helpers/show.ts b/src/frontend/components/helpers/show.ts
index c2bd73b8..225f9fb0 100644
--- a/src/frontend/components/helpers/show.ts
+++ b/src/frontend/components/helpers/show.ts
@@ -3,6 +3,7 @@ import type { Show, ShowList, Shows, Slide } from "../../../types/Show"
import { activeShow, cachedShowsData, dictionary, groupNumbers, groups, shows, showsCache, sorted, sortedShowsList, stageShows } from "../../stores"
import { clone, keysToID, removeValues, sortByNameAndNumber } from "./array"
import { GetLayout } from "./get"
+import { _show } from "./shows"
// check if name exists and add number
export function checkName(name: string = "", showId: string = "") {
@@ -60,6 +61,38 @@ export function getGlobalGroup(group: string, returnInputIfNull: boolean = false
return globalGroup || (returnInputIfNull ? groupId : "")
}
+// get group number (dynamic counter)
+export function getGroupName(show: Show, slideID: string, groupName: string | null, layoutIndex: number) {
+ let name = groupName
+ if (name === null) return name // child slide
+
+ if (!name.length) name = "—"
+ if (!get(groupNumbers)) return name
+
+ // sort by order when just one layout
+ let slides = keysToID(clone(show.slides || {}))
+ if (Object.keys(show.layouts || {}).length < 2) {
+ let layoutSlides = Object.values(show.layouts || {})[0]?.slides?.map(({ id }) => id) || []
+ slides = slides.sort((a, b) => layoutSlides.indexOf(a.id) - layoutSlides.indexOf(b.id))
+ }
+
+ // different slides with same name
+ let currentSlide = show.slides?.[slideID] || {}
+ let allSlidesWithSameGroup = slides.filter((a) => a.group === currentSlide.group)
+ let currentIndex = allSlidesWithSameGroup.findIndex((a) => a.id === slideID)
+ let currentGroupNumber = allSlidesWithSameGroup.length > 1 ? " " + (currentIndex + 1) : ""
+ name += currentGroupNumber
+
+ // same group - count
+ let layoutRef = _show().layouts("active").ref()[0]
+ let allGroupLayoutSlides = layoutRef.filter((a) => a.id === slideID)
+ let currentGroupLayoutIndex = allGroupLayoutSlides.findIndex((a) => a.layoutIndex === layoutIndex)
+ let currentLayoutNumber = allGroupLayoutSlides.length > 1 ? " (" + (currentGroupLayoutIndex + 1) + ")" : ""
+ name += currentLayoutNumber
+
+ return name
+}
+
// mirror & events
export function getListOfShows(removeCurrent: boolean = false) {
let list: any[] = Object.entries(get(shows)).map(([id, show]: any) => ({ id, name: show.name }))
@@ -142,16 +175,15 @@ export function updateCachedShow(id: string, show: Show) {
slidesUpdated: cachedShowsData[id]?.template?.slidesUpdated || false,
}
- // create groups
- let showSlides = keysToID(clone(show.slides || {}))
- let addedGroups: any = {}
-
// sort by order when just one layout
+ let showSlides = keysToID(clone(show.slides || {}))
if (Object.keys(show.layouts || {}).length < 2) {
let layoutSlides = Object.values(show.layouts || {})[0]?.slides?.map(({ id }) => id) || []
showSlides = showSlides.sort((a, b) => layoutSlides.indexOf(a.id) - layoutSlides.indexOf(b.id))
}
+ // create groups
+ let addedGroups: any = {}
let showGroups: any[] = showSlides.map(createGroups)
function createGroups(slide) {
// update if global group
@@ -173,13 +205,20 @@ export function updateCachedShow(id: string, show: Show) {
}
}
- if (!slide.group || !get(groupNumbers)) return { ...slide, id: slide.id }
+ if (slide.group === null || !get(groupNumbers)) return { ...slide, id: slide.id }
+ if (!slide.group) slide.group = "—"
// add numbers to different slides with same name
if (addedGroups[slide.group]) {
addedGroups[slide.group]++
slide.group += " " + addedGroups[slide.group]
- } else addedGroups[slide.group] = 1
+ } else {
+ addedGroups[slide.group] = 1
+
+ // find all groups with same name
+ let allSameGroups = showSlides.filter((a) => a.group !== null && (a.group || "—") === slide.group)
+ if (allSameGroups.length > 1) slide.group += " 1"
+ }
return { ...slide, id: slide.id }
}
diff --git a/src/frontend/components/main/popups/Import.svelte b/src/frontend/components/main/popups/Import.svelte
index adae8336..3c1428cd 100644
--- a/src/frontend/components/main/popups/Import.svelte
+++ b/src/frontend/components/main/popups/Import.svelte
@@ -28,13 +28,13 @@
name: "EasyWorship",
extensions: ["db"],
id: "easyworship",
- tutorial: "Import the SongsWords.db file from the Data folder
Optionally select Songs.db to import title/metadata",
+ tutorial: "Import the SongsWords.db file from the Data folder - Often located in the Documents folder
Optionally select Songs.db to import title/metadata",
},
{
name: "VideoPsalm",
extensions: ["json"],
id: "videopsalm",
- tutorial: "1. Find the .vpc or .json file(s)
2. If it's .vpc, add .zip to the end & extract
3. Import the .json file(s)",
+ tutorial: "1. Find the .vpc or .json file(s) - Often located in Documents\\VideoPsalm\\Songbooks
2. If it's .vpc, add .zip to the end & extract
3. Import the .json file(s)",
},
{ name: "OpenLP/OpenLyrics", extensions: ["xml", "sqlite"], id: "openlp" },
{ name: "OpenSong", extensions: [], id: "opensong" },
diff --git a/src/frontend/components/output/tools/Audio.svelte b/src/frontend/components/output/tools/Audio.svelte
index 6d741dae..dcf46a24 100644
--- a/src/frontend/components/output/tools/Audio.svelte
+++ b/src/frontend/components/output/tools/Audio.svelte
@@ -19,7 +19,9 @@
$: paused = playing.paused !== false
let duration = 0
- $: if (Object.keys($playingAudio).length === 1) getDuration()
+ $: justOneAudio = Object.keys($playingAudio).length === 1
+ $: if (justOneAudio && path) getDuration()
+ else duration = 0
async function getDuration() {
duration = 0
duration = await getAudioDuration(Object.keys($playingAudio)[0])
diff --git a/src/frontend/components/slide/Slide.svelte b/src/frontend/components/slide/Slide.svelte
index 12eb842f..a93a66af 100644
--- a/src/frontend/components/slide/Slide.svelte
+++ b/src/frontend/components/slide/Slide.svelte
@@ -3,37 +3,18 @@
import { MAIN } from "../../../types/Channels"
import type { MediaStyle } from "../../../types/Main"
import type { Media, Show, Slide, SlideData } from "../../../types/Show"
- import {
- activeShow,
- activeTimers,
- audioFolders,
- checkedFiles,
- dictionary,
- driveData,
- fullColors,
- groupNumbers,
- groups,
- media,
- mediaFolders,
- outputs,
- overlays,
- refreshListBoxes,
- refreshSlideThumbnails,
- showsCache,
- slidesOptions,
- styles,
- } from "../../stores"
+ import { activeShow, activeTimers, audioFolders, checkedFiles, dictionary, driveData, fullColors, groups, media, mediaFolders, outputs, overlays, refreshListBoxes, refreshSlideThumbnails, showsCache, slidesOptions, styles } from "../../stores"
import { wait } from "../../utils/common"
import { send } from "../../utils/request"
import { slideHasAction } from "../actions/actions"
import MediaLoader from "../drawer/media/MediaLoader.svelte"
import Editbox from "../edit/editbox/Editbox.svelte"
import { getItemText } from "../edit/scripts/textStyle"
- import { clone, keysToID } from "../helpers/array"
+ import { clone } from "../helpers/array"
import { getContrast } from "../helpers/color"
- import { GetLayoutRef } from "../helpers/get"
import { checkMedia, getFileName, getMediaStyle, getThumbnailPath, loadThumbnail, mediaSize, splitPath } from "../helpers/media"
import { getActiveOutputs, getResolution } from "../helpers/output"
+ import { getGroupName } from "../helpers/show"
import SelectElem from "../system/SelectElem.svelte"
import Actions from "./Actions.svelte"
import Icons from "./Icons.svelte"
@@ -173,44 +154,7 @@
// history({ id: "UPDATE", save: false, newData: { data: color, key: "slides", keys: [layoutSlide.id], subkey: "color" }, oldData: { id: $activeShow?.id }, location: { page: "show", id: "show_key" } })
}
- $: name = getGroupName(layoutSlide.id)
- // dynamic counter
- function getGroupName(slideID: string) {
- let name = group
- if (name === null || name === undefined) return name
-
- if (!name.length) name = "—"
- let added: any = {}
- if (!$groupNumbers) return name
-
- // different slides with same name
- let slides = keysToID(show.slides || [])
- // sort by order when just one layout
- if (Object.keys(show.layouts || {}).length < 2) {
- let layoutSlides = Object.values(show.layouts)[0]?.slides?.map(({ id }) => id) || []
- slides = slides.sort((a, b) => layoutSlides.indexOf(a.id) - layoutSlides.indexOf(b.id))
- }
- slides.forEach((slide: any) => {
- if (!slide) return
- if (added[slide.group]) {
- added[slide.group]++
- if (slide.id === slideID) name += " " + added[slide.group]
- } else added[slide.group] = 1
- })
-
- // same group count
- added = {}
- GetLayoutRef().forEach((a: any, i: number) => {
- if (a.type === "parent") {
- if (added[a.id]) {
- added[a.id]++
- if (i === index) name += " (" + added[a.id] + ")"
- } else added[a.id] = 1
- }
- })
-
- return name
- }
+ $: name = getGroupName(show, layoutSlide.id, group, index)
// quick edit
let html: string = ""