diff --git a/src/frontend/components/edit/editors/SlideEditor.svelte b/src/frontend/components/edit/editors/SlideEditor.svelte
index 81562733..235b559f 100644
--- a/src/frontend/components/edit/editors/SlideEditor.svelte
+++ b/src/frontend/components/edit/editors/SlideEditor.svelte
@@ -2,7 +2,7 @@
import { onMount } from "svelte"
import { slide } from "svelte/transition"
import type { MediaStyle } from "../../../../types/Main"
- import { activeEdit, activePopup, activeShow, alertMessage, dictionary, driveData, focusMode, labelsDisabled, media, outputs, overlays, refreshEditSlide, showsCache, styles } from "../../../stores"
+ import { activeEdit, activePopup, activeShow, alertMessage, dictionary, driveData, focusMode, labelsDisabled, media, outputs, overlays, refreshEditSlide, showsCache, styles, textEditActive } from "../../../stores"
import { slideHasAction } from "../../actions/actions"
import MediaLoader from "../../drawer/media/MediaLoader.svelte"
import Icon from "../../helpers/Icon.svelte"
@@ -336,6 +336,15 @@
{#if !$labelsDisabled}
{/if}
+ {#if !$focusMode}
+
{:else if input.input === "media"}
-
{#each input.values as option}
diff --git a/src/frontend/components/edit/tools/Items.svelte b/src/frontend/components/edit/tools/Items.svelte
index de17fc06..83a16cd6 100644
--- a/src/frontend/components/edit/tools/Items.svelte
+++ b/src/frontend/components/edit/tools/Items.svelte
@@ -26,10 +26,8 @@
]
const specialItems: ItemRef[] = [
- { id: "list" },
// { id: "table" },
{ id: "camera" },
- { id: "variable" },
{ id: "slide_tracker", icon: "percentage" },
{ id: "events", icon: "calendar" },
{ id: "mirror" },
diff --git a/src/frontend/components/edit/values/boxes.ts b/src/frontend/components/edit/values/boxes.ts
index 69052154..2e1c28a0 100644
--- a/src/frontend/components/edit/values/boxes.ts
+++ b/src/frontend/components/edit/values/boxes.ts
@@ -111,6 +111,42 @@ export const boxes: Box = {
{ name: "background_color", id: "specialStyle.lineBg", input: "color", value: "", enableNoColor: true },
{ name: "background_opacity", id: "specialStyle.opacity", input: "number", value: 1, values: { step: 0.1, decimals: 1, max: 1, inputMultiplier: 10 } },
],
+ list: [
+ { name: "list", id: "list.enabled", input: "checkbox", value: false },
+ {
+ name: "style",
+ input: "dropdown",
+ id: "list.style",
+ value: "disc",
+ values: {
+ options: [
+ // common
+ { id: "disc", name: "$:list.disc:$" },
+ { id: "circle", name: "$:list.circle:$" },
+ { id: "square", name: "$:list.square:$" },
+ { id: "disclosure-closed", name: "$:list.disclosure-closed:$" },
+ { id: "disclosure-open", name: "$:list.disclosure-open:$" },
+ // numbers
+ { id: "decimal", name: "$:list.decimal:$" },
+ { id: "decimal-leading-zero", name: "$:list.decimal-leading-zero:$" },
+ // alpha
+ { id: "lower-alpha", name: "$:list.lower-alpha:$" }, // same as latin
+ { id: "upper-alpha", name: "$:list.upper-alpha:$" }, // same as latin
+ { id: "lower-roman", name: "$:list.lower-roman:$" },
+ { id: "upper-roman", name: "$:list.upper-roman:$" },
+ { id: "lower-greek", name: "$:list.lower-greek:$" },
+ // special
+ // {id: "bengali", name: "$:list.bengali:$" },
+ // {id: "cambodian", name: "$:list.cambodian:$" },
+ // {id: "devanagari", name: "$:list.devanagari:$" },
+ ],
+ },
+ // disabled: "list.interval", // WIP still disabled when set back to 0
+ hidden: true,
+ },
+ // { name: "one_at_a_time", id: "one_at_a_time", input: "checkbox", value: false },
+ // { name: "interval", id: "list.interval", input: "number", value: 0, hidden: true }, // slide timers can be user for this
+ ],
outline: [
{ name: "color", id: "style", key: "-webkit-text-stroke-color", input: "color", value: "#000000" },
{ name: "width", id: "style", key: "-webkit-text-stroke-width", input: "number", value: 0, values: { max: 100 }, extension: "px" },
diff --git a/src/frontend/components/edit/values/media.ts b/src/frontend/components/edit/values/media.ts
index 544d4347..b75f73c0 100644
--- a/src/frontend/components/edit/values/media.ts
+++ b/src/frontend/components/edit/values/media.ts
@@ -46,6 +46,13 @@ export const videoEdit = [
],
},
},
+ {
+ name: "media.volume",
+ id: "volume",
+ input: "number",
+ value: 100,
+ values: { max: 100 },
+ },
{
name: "inputs.start",
id: "fromTime",
diff --git a/src/frontend/components/helpers/audio.ts b/src/frontend/components/helpers/audio.ts
index bbec8dcd..aa06d5c0 100644
--- a/src/frontend/components/helpers/audio.ts
+++ b/src/frontend/components/helpers/audio.ts
@@ -1,7 +1,7 @@
import { get } from "svelte/store"
import { uid } from "uid"
import { MAIN, OUTPUT } from "../../../types/Channels"
-import { activePlaylist, audioChannels, audioPlaylists, gain, media, outLocked, playingAudio, playingVideos, special } from "../../stores"
+import { activePlaylist, audioChannels, audioPlaylists, gain, isFadingOut, media, outLocked, playingAudio, playingVideos, special } from "../../stores"
import { send } from "../../utils/request"
import { customActionActivation } from "../actions/actions"
import { stopMetronome } from "../drawer/audio/metronome"
@@ -13,7 +13,7 @@ import { checkNextAfterMedia } from "./showActions"
// WIP use get(special).audio_fade_duration ?? 1.5 to fade in when starting song ?? (currently just when fading out)
-export async function playAudio({ path, name = "", audio = null, stream = null }: any, pauseIfPlaying: boolean = true, startAt: number = 0, playMultiple: boolean = false, crossfade: number = 0) {
+export async function playAudio({ path, name = "", audio = null, stream = null }: any, pauseIfPlaying: boolean = true, startAt: number = 0, playMultiple: boolean = false, crossfade: number = 0, playlistCrossfade: boolean = false) {
let existing: any = get(playingAudio)[path]
if (existing) {
if (!pauseIfPlaying) {
@@ -36,7 +36,7 @@ export async function playAudio({ path, name = "", audio = null, stream = null }
let audioPlaying = Object.keys(get(playingAudio)).length
if (crossfade) crossfadeAudio(crossfade)
- else if (!playMultiple) clearAudio("", false)
+ else if (!playMultiple) clearAudio("", false, playlistCrossfade)
let encodedPath = encodeFilePath(path)
audio = audio || new Audio(encodedPath)
@@ -80,8 +80,11 @@ export async function playAudio({ path, name = "", audio = null, stream = null }
})
let localVolume: number = get(volume) * (get(media)[path]?.volume || 1)
- if (analyser.gainNode) analyser.gainNode.gain.value = localVolume * (get(gain) || 1)
- else audio.volume = localVolume
+ if (analyser.gainNode) {
+ analyser.gainNode.gain.value = localVolume * (get(gain) || 1)
+ if (get(special).preFaderVolumeMeter) audio.volume = 1
+ else audio.volume = localVolume
+ } else audio.volume = localVolume
let waitToPlay = 0
if (audioPlaying && crossfade) {
@@ -109,15 +112,20 @@ let currentlyCrossfading: string[] = []
// if no "path" is provided it will fade out/clear all audio
async function crossfadeAudio(crossfade: number = 0, path: string = "", waitToPlay: boolean = false) {
if (currentlyCrossfading[path]) return
- if (path) currentlyCrossfading.push(path)
// fade in
if (path) {
let playing = get(playingAudio)[path]?.audio
if (!playing) return
- setTimeout(() => fadeAudio(playing, waitToPlay ? crossfade * 0.4 : crossfade, true), waitToPlay ? crossfade * 0.6 * 1000 : 0)
- currentlyCrossfading.splice(currentlyCrossfading.indexOf(path), 1)
+ currentlyCrossfading.push(path)
+ setTimeout(
+ async () => {
+ await fadeAudio(playing, waitToPlay ? crossfade * 0.4 : crossfade, true)
+ currentlyCrossfading.splice(currentlyCrossfading.indexOf(path), 1)
+ },
+ waitToPlay ? crossfade * 0.6 * 1000 : 0
+ )
return
}
@@ -127,6 +135,7 @@ async function crossfadeAudio(crossfade: number = 0, path: string = "", waitToPl
})
async function fadeoutAudio(audio, path) {
+ currentlyCrossfading.push(path)
let faded = await fadeAudio(audio, crossfade)
if (faded) deleteAudio(path)
}
@@ -165,6 +174,9 @@ export function updateVolume(value: number | undefined | "local", changeGain: bo
if (a[id].analyser.gainNode) {
let gainedValue = localVolume * (get(gain) || 1)
a[id].analyser.gainNode.gain.value = gainedValue
+
+ if (get(special).preFaderVolumeMeter) a[id].audio.volume = 1
+ else a[id].audio.volume = localVolume
} else a[id].audio.volume = localVolume
})
@@ -252,7 +264,7 @@ export function playlistNext(previous: string = "", specificSong: string = "", c
})
// if (crossfade) isCrossfading = true
- playAudio({ path: nextSong }, false, 0, false, crossfade)
+ playAudio({ path: nextSong }, false, 0, false, crossfade, true)
function getSongs(): string[] {
if (previous && get(activePlaylist)?.songs) return get(activePlaylist).songs
@@ -467,7 +479,8 @@ function getPlayingAudio() {
get(playingAudio)[audioPath].audio.currentTime = 0
get(playingAudio)[audioPath].audio.play()
} else if (get(activePlaylist)?.active === audioPath) {
- let playlist = get(audioPlaylists)[audioPath] || {}
+ let playlistId = get(activePlaylist).id || ""
+ let playlist = get(audioPlaylists)[playlistId] || {}
playingAudio.update((a: any) => {
a[audioPath]?.audio?.pause()
@@ -509,7 +522,7 @@ function getPlayingVideos() {
videos.map((a) => {
// set volume (video in output window)
- let newVolume = get(volume)
+ let newVolume = get(volume) * ((get(media)[a.id]?.volume ?? 100) / 100)
if (a.analyser.gainNode) {
let gainedValue = newVolume * (get(gain) || 1)
a.analyser.gainNode.gain.value = gainedValue
@@ -567,9 +580,9 @@ function getHighestNumber(numbers: number[]): number {
return Math.max(...numbers)
}
-let clearing = false
+let clearing: string[] = []
let forceClear: boolean = false
-export function clearAudio(path: string = "", clearPlaylist: boolean = true, playlistCrossfade: boolean = false) {
+export function clearAudio(path: string = "", clearPlaylist: boolean = true, playlistCrossfade: boolean = false, commonClear: boolean = false) {
// turn off any playlist
if (clearPlaylist && (!path || get(activePlaylist)?.active === path)) activePlaylist.set(null)
@@ -578,14 +591,14 @@ export function clearAudio(path: string = "", clearPlaylist: boolean = true, pla
const clearTime = playlistCrossfade ? 0 : (get(special).audio_fade_duration ?? 1.5)
- if (clearing) {
+ if (clearing.includes(path)) {
+ if (!commonClear) return
// force stop audio files (bypass timeout if already active)
forceClear = true
setTimeout(() => (forceClear = false), 100)
return
}
if (!Object.keys(get(playingAudio)).length) return
- clearing = true
let newPlaying: any = get(playingAudio)
playingAudio.update((a) => {
@@ -595,6 +608,7 @@ export function clearAudio(path: string = "", clearPlaylist: boolean = true, pla
return a
async function clearAudio(path) {
+ clearing.push(path)
if (!a[path]?.audio) return deleteAudio(path)
let faded = await fadeAudio(a[path].audio, clearTime)
@@ -625,7 +639,7 @@ export function clearAudio(path: string = "", clearPlaylist: boolean = true, pla
setTimeout(() => {
playingAudio.set(newPlaying)
clearAudioStreams()
- clearing = false
+ clearing.splice(clearing.indexOf(path), 1)
}, 200)
}
}
@@ -684,7 +698,11 @@ async function fadeAudio(audio, duration = 1, increment: boolean = false): Promi
// WIP non linear easing
- let fadeId = uid()
+ if (!increment) {
+ isFadingOut.set(true)
+ }
+
+ let fadeId = (increment ? "in_" : "out_") + uid()
return new Promise((resolve) => {
currentlyFading[fadeId] = setInterval(() => {
if (forceClear) return finished()
@@ -709,6 +727,10 @@ async function fadeAudio(audio, duration = 1, increment: boolean = false): Promi
delete currentlyFading[fadeId]
clearTimeout(timedout)
setTimeout(() => resolve(true), 50)
+
+ if (!increment && !Object.keys(currentlyFading).filter((a) => a.includes("out")).length) {
+ isFadingOut.set(false)
+ }
}
})
}
diff --git a/src/frontend/components/helpers/historyHelpers.ts b/src/frontend/components/helpers/historyHelpers.ts
index 16941c91..4a6c79a4 100644
--- a/src/frontend/components/helpers/historyHelpers.ts
+++ b/src/frontend/components/helpers/historyHelpers.ts
@@ -7,6 +7,7 @@ import {
activeRename,
activeShow,
activeStage,
+ activeTagFilter,
audioPlaylists,
currentOutputSettings,
dictionary,
@@ -440,6 +441,9 @@ export const _updaters = {
activeRename.set("tag_" + id)
return data
},
+ deselect: () => {
+ activeTagFilter.set([])
+ }
},
tag_key: { store: globalTags },
diff --git a/src/frontend/components/helpers/media.ts b/src/frontend/components/helpers/media.ts
index ca2d2854..6994bd50 100644
--- a/src/frontend/components/helpers/media.ts
+++ b/src/frontend/components/helpers/media.ts
@@ -60,6 +60,8 @@ export function joinPath(path: string[]): string {
// fix for media files with special characters in file name not playing
export function encodeFilePath(path: string): string {
+ if (!path) return ""
+
// already encoded
if (path.match(/%\d+/g) || path.includes("http") || path.includes("data:")) return path
diff --git a/src/frontend/components/helpers/output.ts b/src/frontend/components/helpers/output.ts
index 38f96dfe..e622e052 100644
--- a/src/frontend/components/helpers/output.ts
+++ b/src/frontend/components/helpers/output.ts
@@ -41,6 +41,8 @@ import { fadeinAllPlayingAudio, fadeoutAllPlayingAudio } from "./audio"
import { getExtension, getFileName, removeExtension } from "./media"
import { replaceDynamicValues } from "./showActions"
import { _show } from "./shows"
+import { newToast } from "../../utils/common"
+import { getStyles } from "./style"
export function displayOutputs(e: any = {}, auto: boolean = false) {
// sort so display order can be changed! (needs app restart)
@@ -64,7 +66,7 @@ export function setOutput(key: string, data: any, toggle: boolean = false, outpu
let outs = outputId ? [outputId] : allOutputs
let inputData = clone(data)
- let firstOutputWithBackground = allOutputs.findIndex((id) => (get(styles)[get(outputs)[id]?.style || ""]?.layers || ["background"]).includes("background"))
+ let firstOutputWithBackground = allOutputs.findIndex((id) => !a[id]?.isKeyOutput && !a[id]?.stageOutput && (get(styles)[get(outputs)[id]?.style || ""]?.layers || ["background"]).includes("background"))
firstOutputWithBackground = Math.max(0, firstOutputWithBackground)
// reset slide cache (after update)
@@ -318,12 +320,12 @@ export function getOutputResolution(outputId: string, _updater = get(outputs)) {
return style || { width: 1920, height: 1080 }
}
-export function checkWindowCapture() {
- getActiveOutputs(get(outputs), false, true, true).forEach(shouldBeCaptured)
+export function checkWindowCapture(startup: boolean = false) {
+ getActiveOutputs(get(outputs), false, true, true).forEach((a) => shouldBeCaptured(a, startup))
}
// NDI | OutputShow | Stage CurrentOutput
-export function shouldBeCaptured(outputId: string) {
+export function shouldBeCaptured(outputId: string, startup: boolean = false) {
let output = get(outputs)[outputId]
let captures: any = {
ndi: !!output.ndi,
@@ -331,6 +333,9 @@ export function shouldBeCaptured(outputId: string) {
stage: stageHasOutput(outputId),
}
+ // alert user that screen recording starts
+ if (!startup && Object.values(captures).filter(Boolean).length) newToast("$toast.output_capture_enabled")
+
send(OUTPUT, ["CAPTURE"], { id: outputId, captures })
}
function stageHasOutput(outputId: string) {
@@ -522,7 +527,7 @@ export function getCurrentMediaTransition() {
// TEMPLATE
export function mergeWithTemplate(slideItems: Item[], templateItems: Item[], addOverflowTemplateItems: boolean = false, resetAutoSize: boolean = true) {
- if (!slideItems) return []
+ if (!slideItems?.length) return []
slideItems = clone(slideItems)
if (!templateItems.length) return slideItems
@@ -566,7 +571,16 @@ export function mergeWithTemplate(slideItems: Item[], templateItems: Item[], add
line.align = templateLine?.align || ""
line.text?.forEach((text: any, k: number) => {
let templateText = templateLine?.text[k] || templateLine?.text[0]
- if (!text.customType?.includes("disableTemplate")) text.style = templateText?.style || ""
+ if (!text.customType?.includes("disableTemplate")) {
+ let style = templateText?.style || ""
+
+ // add original text color
+ let textColor = getStyles(text.style)["color"] || "#FFFFFF"
+ // use template color if text is white (default)
+ if (textColor !== "#FFFFFF") style += `color: ${textColor};`
+
+ text.style = style
+ }
let firstChar = templateText?.value?.[0] || ""
@@ -926,3 +940,15 @@ export function getSlideFilter(slideData: any) {
return slideFilter
}
+
+export function getBlending() {
+ let blending = Object.values(get(outputs))[0]?.blending
+ if (!blending) return ""
+
+ if (!blending.left && !blending.right) return ""
+
+ const opacity = (blending.opacity ?? 50) / 100
+ const center = 50 + Number(blending.offset || 0)
+ if (blending.centered) return `-webkit-mask-image: linear-gradient(${blending.rotate ?? 90}deg, rgb(0, 0, 0) ${center - blending.left}%, rgba(0, 0, 0, ${opacity}) ${center}%, rgb(0, 0, 0) ${center + Number(blending.right)}%);`
+ return `-webkit-mask-image: linear-gradient(${blending.rotate ?? 90}deg, rgba(0, 0, 0, ${opacity}) 0%, rgb(0, 0, 0) ${blending.left}%, rgb(0, 0, 0) ${100 - blending.right}%, rgba(0, 0, 0, ${opacity}) 100%);`
+}
diff --git a/src/frontend/components/helpers/setShow.ts b/src/frontend/components/helpers/setShow.ts
index 9e2d7234..9155e82e 100644
--- a/src/frontend/components/helpers/setShow.ts
+++ b/src/frontend/components/helpers/setShow.ts
@@ -6,6 +6,7 @@ import { getShowCacheId, updateCachedShow } from "./show"
import { uid } from "uid"
import { destroy } from "../../utils/request"
import { fixShowIssues } from "../../converters/importHelpers"
+import type { ShowObj } from "../../classes/Show"
export function setShow(id: string, value: "delete" | Show): Show {
let previousValue: Show
@@ -27,6 +28,12 @@ export function setShow(id: string, value: "delete" | Show): Show {
if (!value.slides) value.slides = {}
if (!value.layouts) value.layouts = {}
if (!value.media) value.media = {}
+
+ // set metadata (CCLI) in quickAccess
+ if (value.meta.CCLI && !value.quickAccess?.metadata?.CCLI) {
+ if (!value.quickAccess?.metadata) value.quickAccess.metadata = {}
+ value.quickAccess.metadata.CCLI = value.meta.CCLI
+ }
}
}
@@ -174,3 +181,13 @@ export function saveTextCache(id: string, show: Show) {
tempCache = {}
}, 1000)
}
+
+export function setQuickAccessMetadata(show: ShowObj, key: string, value: string) {
+ if (!value) return show
+
+ if (!show.quickAccess) show.quickAccess = {}
+ if (!show.quickAccess.metadata) show.quickAccess.metadata = {}
+ show.quickAccess.metadata[key] = value
+
+ return show
+}
diff --git a/src/frontend/components/helpers/show.ts b/src/frontend/components/helpers/show.ts
index 4ce93f7e..c6de0590 100644
--- a/src/frontend/components/helpers/show.ts
+++ b/src/frontend/components/helpers/show.ts
@@ -14,7 +14,8 @@ export function checkName(name: string = "", showId: string = "") {
let number = 1
while (Object.entries(get(shows)).find(([id, a]: any) => (!showId || showId !== id) && a.name?.toLowerCase() === (number > 1 ? name.toLowerCase() + " " + number : name.toLowerCase()))) number++
- return number > 1 ? name + " " + number : name
+ // add number if existing name, and trim away spaces from the start/end
+ return (number > 1 ? name + " " + number : name).trim()
}
export function formatToFileName(name: string = "") {
@@ -142,6 +143,7 @@ export function updateShowsList(shows: Shows) {
} else {
// sort by name
sortedShows = sortByNameAndNumber(showsList)
+ if (sortType === "name_des") sortedShows = sortedShows.reverse()
}
let filteredShows: ShowList[] = removeValues(sortedShows, "private", true)
diff --git a/src/frontend/components/helpers/showActions.ts b/src/frontend/components/helpers/showActions.ts
index 92b0fd6c..488dacd8 100644
--- a/src/frontend/components/helpers/showActions.ts
+++ b/src/frontend/components/helpers/showActions.ts
@@ -32,6 +32,7 @@ import {
templates,
timers,
triggers,
+ variables,
videosData,
videosTime,
} from "./../../stores"
@@ -798,13 +799,13 @@ export function playNextGroup(globalGroupIds: string[], { showRef, outSlide, cur
}
// go to next slide if current output slide has nextAfterMedia action
-let nextActive = false
+let nextActive: string[] = []
export function checkNextAfterMedia(endedId: string, type: "media" | "audio" | "timer" = "media", outputId: string = "") {
- if (nextActive) return false
+ if (nextActive.includes(outputId)) return false
- nextActive = true
+ nextActive.push(outputId)
setTimeout(() => {
- nextActive = false
+ nextActive.splice(nextActive.indexOf(outputId), 1)
}, 600) // MAKE SURE NEXT SLIDE HAS TRANSITIONED
if (!outputId) outputId = getActiveOutputs(get(outputs), true, true, true)[0]
@@ -920,8 +921,9 @@ export const dynamicValueText = (id: string) => `{${id}}`
export function getDynamicIds() {
let mainValues = Object.keys(dynamicValues)
let metaValues = Object.keys(initializeMetadata({})).map((id) => `meta_` + id)
+ let variableValues = Object.values(get(variables)).map(({ name }) => `variable_` + getNameId(name))
- return [...mainValues, ...metaValues]
+ return [...mainValues, ...metaValues, ...variableValues]
}
export function replaceDynamicValues(text: string, { showId, layoutId, slideIndex, type, id }: any, _updater: number = 0) {
@@ -956,6 +958,18 @@ export function replaceDynamicValues(text: string, { showId, layoutId, slideInde
return show.meta[key] || ""
}
+ if (id.includes("variable_")) {
+ let nameId = id.slice(9)
+ let variable = Object.values(get(variables)).find((a) => getNameId(a.name) === nameId)
+ if (!variable) return ""
+
+ if (variable.type === "number") return Number(variable.number || 0)
+
+ if (variable.enabled === false) return ""
+ if (variable.text?.includes(id)) return variable.text || ""
+ return replaceDynamicValues(variable.text || "", { showId, layoutId, slideIndex, type, id })
+ }
+
let outputId: string = getActiveOutputs()[0]
if (id.includes("video_") && get(currentWindow) === "output") {
@@ -997,3 +1011,7 @@ const dynamicValues = {
video_duration: ({ videoDuration }) => joinTime(secondsToTime(videoDuration)),
video_countdown: ({ videoTime, videoDuration }) => joinTime(secondsToTime(videoDuration - videoTime)),
}
+
+function getNameId(name) {
+ return name.toLowerCase().trim().replaceAll(" ", "_")
+}
diff --git a/src/frontend/components/inputs/Color.svelte b/src/frontend/components/inputs/Color.svelte
index c93cddbf..d814d561 100644
--- a/src/frontend/components/inputs/Color.svelte
+++ b/src/frontend/components/inputs/Color.svelte
@@ -11,6 +11,7 @@
export let enableNoColor: boolean = false
export let showDisabled: boolean = false
export let custom: boolean = false
+ export let rightAlign: boolean = false
export let height: number = 0
export let width: number = 0
@@ -43,7 +44,7 @@
}
let colorElem
- let clipRight: boolean = false
+ let clipRight: boolean = rightAlign || false
$: if (colorElem) {
let pickerRect = colorElem.getBoundingClientRect()
let pickerRight = pickerRect.left + 200
diff --git a/src/frontend/components/inputs/ProjectButton.svelte b/src/frontend/components/inputs/ProjectButton.svelte
index a4f3d7a2..33bc0ace 100644
--- a/src/frontend/components/inputs/ProjectButton.svelte
+++ b/src/frontend/components/inputs/ProjectButton.svelte
@@ -1,6 +1,7 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/frontend/components/main/popups/ManageTags.svelte b/src/frontend/components/main/popups/ManageTags.svelte
new file mode 100644
index 00000000..b8dc9a63
--- /dev/null
+++ b/src/frontend/components/main/popups/ManageTags.svelte
@@ -0,0 +1,98 @@
+
+
+
+ {#if tags.length}
+ {#key tags}
+ {#each tags as tag}
+
+ updateKey(e, tag.id, "name")} autofocus={!tag.name} />
+ updateKey(e, tag.id, "color")} rightAlign />
+
+
+
+ {/each}
+ {/key}
+ {:else}
+
+
+
+ {/if}
+
+
+
+
+
+
+
+
+
diff --git a/src/frontend/components/main/popups/SlideShortcut.svelte b/src/frontend/components/main/popups/SlideShortcut.svelte
index ca4889e7..63cf6b0f 100644
--- a/src/frontend/components/main/popups/SlideShortcut.svelte
+++ b/src/frontend/components/main/popups/SlideShortcut.svelte
@@ -38,7 +38,7 @@
-
+
{#if currentShortcut}
diff --git a/src/frontend/components/main/popups/Trigger.svelte b/src/frontend/components/main/popups/Trigger.svelte
index e7c7669e..e076c69d 100644
--- a/src/frontend/components/main/popups/Trigger.svelte
+++ b/src/frontend/components/main/popups/Trigger.svelte
@@ -31,7 +31,7 @@
let sortedTriggers = sortByName(globalList)
function updateValue(e: any, key: string) {
- let value = e?.target?.value || e
+ let value = e?.target?.value ?? e
if (!value) return
currentTrigger[key] = value
diff --git a/src/frontend/components/main/popups/Unsaved.svelte b/src/frontend/components/main/popups/Unsaved.svelte
index 97c48373..1b91aadd 100644
--- a/src/frontend/components/main/popups/Unsaved.svelte
+++ b/src/frontend/components/main/popups/Unsaved.svelte
@@ -46,7 +46,7 @@
{:else}
diff --git a/src/frontend/components/output/Output.svelte b/src/frontend/components/output/Output.svelte
index 87c61caf..af229a7d 100644
--- a/src/frontend/components/output/Output.svelte
+++ b/src/frontend/components/output/Output.svelte
@@ -249,7 +249,7 @@
{#if $colorbars}
-
+
{/if}
diff --git a/src/frontend/components/output/VideoSlider.svelte b/src/frontend/components/output/VideoSlider.svelte
index 8a0c0413..7b22d3fa 100644
--- a/src/frontend/components/output/VideoSlider.svelte
+++ b/src/frontend/components/output/VideoSlider.svelte
@@ -9,6 +9,7 @@
export let activeOutputIds: string[] = []
export let unmutedId: string = ""
export let toOutput: boolean = false
+ export let big: boolean = false
export let disabled: boolean = false
export let changeValue: number = 0
@@ -103,7 +104,7 @@
}}
/>
-
+
{#if hover}
{time}
@@ -146,6 +147,9 @@
margin: 0 5px;
font-size: 0.8em;
}
+ .main.big {
+ font-size: 1em;
+ }
.slider {
flex: 1;
@@ -154,4 +158,7 @@
display: flex;
align-items: center;
}
+ .main.big .slider {
+ margin: 0 10px;
+ }
diff --git a/src/frontend/components/output/clear.ts b/src/frontend/components/output/clear.ts
index bb6b0926..2f20632a 100644
--- a/src/frontend/components/output/clear.ts
+++ b/src/frontend/components/output/clear.ts
@@ -41,7 +41,7 @@ export function clearAll(button: boolean = false) {
clearBackground()
clearSlide(true)
clearOverlays()
- clearAudio()
+ clearAudio("", true, false, true)
clearTimers()
}
diff --git a/src/frontend/components/output/layers/BackgroundMedia.svelte b/src/frontend/components/output/layers/BackgroundMedia.svelte
index f0673e8c..79cf1551 100644
--- a/src/frontend/components/output/layers/BackgroundMedia.svelte
+++ b/src/frontend/components/output/layers/BackgroundMedia.svelte
@@ -4,7 +4,7 @@
import { OUTPUT } from "../../../../types/Channels"
import type { MediaStyle } from "../../../../types/Main"
import type { OutBackground, Transition } from "../../../../types/Show"
- import { allOutputs, audioChannels, outputs, playingVideos, special, videosData, videosTime } from "../../../stores"
+ import { allOutputs, audioChannels, media, outputs, playingVideos, special, videosData, videosTime, volume } from "../../../stores"
import { destroy, receive, send } from "../../../utils/request"
import BmdStream from "../../drawer/live/BMDStream.svelte"
import NdiStream from "../../drawer/live/NDIStream.svelte"
@@ -163,7 +163,7 @@
// FADE OUT AUDIO
$: if (fadingOut && !videoData.muted) fadeoutVideo()
- $: if (!fadingOut && !videoData.muted && id) setVolume(1)
+ $: if (!fadingOut && !videoData.muted && id) setVolume($volume * (($media[id]?.volume ?? 100) / 100))
const speed = 0.01
const margin = 0.9 // video should fade to 0 before clearing
function fadeoutVideo() {
diff --git a/src/frontend/components/output/preview/ClearButtons.svelte b/src/frontend/components/output/preview/ClearButtons.svelte
index 52db13ac..6201ad70 100644
--- a/src/frontend/components/output/preview/ClearButtons.svelte
+++ b/src/frontend/components/output/preview/ClearButtons.svelte
@@ -1,5 +1,5 @@