Skip to content

Commit

Permalink
Add Songbeamer import (#669)
Browse files Browse the repository at this point in the history
* Make T component update on changed id

* Sort categories in create show before mapping them to Options

This way sortObject uses the translatet names in default categories for sorting

* Show chords with negative positions before the line in stageview

* Forward all data in startImport to importShow

Add encoding argument to readFile

* 📑 Import Songbeamer files
  • Loading branch information
ItsFlo authored Jul 18, 2024
1 parent 8b5d3e2 commit a3ce289
Show file tree
Hide file tree
Showing 15 changed files with 970 additions and 21 deletions.
Binary file added public/import-logos/songbeamer.webp
Binary file not shown.
18 changes: 17 additions & 1 deletion public/lang/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@
"animate": "Animate",
"next_timer": "Timer zur nächsten Folie",
"import": "Importieren",
"songbeamer_import": "Songbeamer Importieren",
"export": "Exportieren",
"importing": "Importiere",
"import_scripture": "Importiere Bibel",
Expand Down Expand Up @@ -1003,5 +1004,20 @@
"words": "Wörter",
"template": "Vorlage",
"category": "Kategorie"
},
"songbeamer_import": {
"options": "Optionen",
"encoding": "Codierung",
"older_versions": "Für Songbeamer Versionen älter als 6.02a",
"category": "Kategorie",
"utf8": "UTF-8",
"latin1": "Latin 1",
"translations": "Übersetzungen",
"translation_multiline": "Mehrzeilig",
"translation_textboxes": "Textboxen",
"translation_layouts": "Layouts",
"translation_description_multiline": "Fügt alle Sprachen in eine einzelne Textbox in abwechselnden Zeilen ein. (Wie in Songbeamer)",
"translation_description_textboxes": "Fügt jede Sprache in eine separate Textbox auf der Folie ein.",
"translation_description_layouts": "Erstellt für jede Sprache eigene Slides und ein eigenes Layout nur für diese Sprache."
}
}
}
16 changes: 16 additions & 0 deletions public/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@
"animate": "Animate",
"next_timer": "Next slide timer",
"import": "Import",
"songbeamer_import": "Songbeamer Import",
"export": "Export",
"importing": "Importing",
"import_scripture": "Import scripture",
Expand Down Expand Up @@ -1139,5 +1140,20 @@
"words": "Words",
"template": "Template",
"category": "Category"
},
"songbeamer_import": {
"options": "Options",
"encoding": "Encoding",
"older_versions": "For Songbeamer versions older than 6.02a",
"category": "Category",
"utf8": "UTF-8",
"latin1": "Latin 1",
"translations": "Translations",
"translation_multiline": "Multiline",
"translation_textboxes": "Textboxes",
"translation_layouts": "Layouts",
"translation_description_multiline": "Adds all languages in a single textbox in alternating lines. (Like in Songbeamer)",
"translation_description_textboxes": "Adds every language as a separate textbox to the slide.",
"translation_description_layouts": "Creates a unique set of slides and a layout for every language."
}
}
25 changes: 18 additions & 7 deletions src/electron/data/import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ const specialImports: any = {

return data
},
pdf: async (files: string[], dataPath: string) => {
pdf: async (files: string[], settings: { path: string }) => {
let data: any[] = []

// TODO: linux don't support pdf-poppler!
const pdf = require("pdf-poppler")

let opts: any = { format: "png", scale: 1920, out_prefix: "img", page: null }
let importPath = getDataFolder(dataPath, dataFolderNames.imports)
let importPath = getDataFolder(settings.path, dataFolderNames.imports)

await Promise.all(files.map(pdfToImages))

Expand Down Expand Up @@ -103,9 +103,20 @@ const specialImports: any = {

return data
},
songbeamer: async (files: string[], data: any) => {
let encoding = data.encoding.id
let fileContents = await Promise.all(files.map(file => readFile(file, encoding)))
return {
files: fileContents,
length: fileContents.length,
encoding,
category: data.category.id,
translationMethod: data.translation,
}
}
}

export async function importShow(id: any, files: string[] | null, dataPath: string) {
export async function importShow(id: any, files: string[] | null, importSettings: any) {
if (!files?.length) return

let importId = id
Expand All @@ -114,26 +125,26 @@ export async function importShow(id: any, files: string[] | null, dataPath: stri
if (id === "easyworship" || id === "softprojector" || sqliteFile) importId = "sqlite"

let data: any[] = []
if (specialImports[importId]) data = await specialImports[importId](files, dataPath)
if (specialImports[importId]) data = await specialImports[importId](files, importSettings)
else {
// TXT | FreeShow | ProPresenter | VidoePsalm | OpenLP | OpenSong | XML Bible | Lessons.church
data = await Promise.all(files.map(readFile))
data = await Promise.all(files.map(file => readFile(file)))
}

if (!data.length) return

toApp(IMPORT, { channel: id, data })
}

async function readFile(filePath: string) {
async function readFile(filePath: string, encoding: BufferEncoding | null = "utf8") {
let content: string = ""

let name: string = getFileName(filePath) || ""
let extension: string = path.extname(filePath).substring(1).toLowerCase()

try {
if (extension === "pro") content = await decodeProto(filePath)
else content = readFileSync(filePath, "utf8").toString()
else content = readFileSync(filePath, encoding).toString()
} catch (err) {
console.error("Error reading file:", err.stack)
}
Expand Down
2 changes: 1 addition & 1 deletion src/electron/utils/responses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export function startImport(_e: any, msg: Message) {
let needsFileAndNoFileSelected = msg.data.format.extensions && !files.length
if (needsFileAndNoFileSelected || isLinuxAndPfdImport) return

importShow(msg.channel, files || null, msg.data.path)
importShow(msg.channel, files || null, msg.data)
}

// BIBLE
Expand Down
9 changes: 4 additions & 5 deletions src/frontend/components/helpers/T.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@
export let id: string
export let index: number = 0
export let lowercase: boolean = false
let category: string, key: string
if (id?.includes(".")) {
category = id.slice(0, id.indexOf(".")).replace("$:", "")
key = id.slice(id.indexOf(".") + 1, id.length).replace(":$", "")
}
const hasPeriod = id?.includes(".")
const periodIndex = id?.indexOf(".")
$: category = hasPeriod ? id.slice(0, periodIndex).replace("$:", "") : ""
$: key = hasPeriod ? id.slice(periodIndex + 1, id.length).replace(":$", "") : ""
</script>

{#key language}
Expand Down
2 changes: 2 additions & 0 deletions src/frontend/components/main/Popup.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import SelectShow from "./popups/SelectShow.svelte"
import SetTime from "./popups/SetTime.svelte"
import Shortcuts from "./popups/Shortcuts.svelte"
import SongbeamerImport from "./popups/SongbeamerImport.svelte"
import Timer from "./popups/Timer.svelte"
import Transition from "./popups/Transition.svelte"
import Trigger from "./popups/Trigger.svelte"
Expand All @@ -53,6 +54,7 @@
const popups: any = {
initialize: Initialize,
import: Import,
songbeamer_import: SongbeamerImport,
export: Export,
show: CreateShow,
delete_show: DeleteShow,
Expand Down
11 changes: 7 additions & 4 deletions src/frontend/components/main/popups/CreateShow.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,13 @@
const cats: any = [
{ id: "", name: "" },
...sortObject(
Object.keys($categories).map((id) => ({ id, name: $categories[id].default ? `$:${$categories[id].name}:$` : $categories[id].name })),
"name"
),
...sortObject(Object.keys($categories).map((key: string) => ({
id: key,
...$categories[key],
})), "name").map((cat: any) => ({
id: cat.id,
name: cat.default? `$:${cat.name}:$` : cat.name,
})),
]
let selectedCategory: any = cats[0]
Expand Down
13 changes: 11 additions & 2 deletions src/frontend/components/main/popups/Import.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<script lang="ts">
import { tick } from "svelte"
import { IMPORT } from "../../../../types/Channels"
import { Popups } from "../../../../types/Main"
import { convertText } from "../../../converters/txt"
import { activePopup, alertMessage, dataPath } from "../../../stores"
import { send } from "../../../utils/request"
Expand All @@ -15,7 +17,7 @@
// { name: "Scripture", id: "scripture" }, // scripture drawer tab
]
const text_formats = [
const text_formats: { popup?: Popups, [key: string]: any }[] = [
{ name: "Clipboard", id: "clipboard" },
{ name: "Text file", extensions: ["txt"], id: "txt" },
{ name: "ChordPro", extensions: ["cho", "crd", "chopro", "chordpro", "chord", "pro", "txt", "onsong"], id: "chordpro" },
Expand All @@ -37,6 +39,7 @@
{ name: "OpenLP/OpenLyrics", extensions: ["xml", "sqlite"], id: "openlp" },
{ name: "OpenSong", extensions: [], id: "opensong" },
{ name: "SoftProjector", extensions: ["sps"], id: "softprojector" },
{ name: "Songbeamer", id: "songbeamer", popup: "songbeamer_import" },
]
const media_formats = [
Expand Down Expand Up @@ -89,7 +92,13 @@
<Button
style="width: 20%;flex-direction: column;min-height: 160px;"
on:click={() => {
if (format.extensions) {
if (format.popup) {
tick().then(() => {
if(format.popup) {
activePopup.set(format.popup)
}
})
} else if (format.extensions) {
send(IMPORT, [format.id], { path: $dataPath, format })
displayTutorial(format)
} else if (format.id === "clipboard") {
Expand Down
144 changes: 144 additions & 0 deletions src/frontend/components/main/popups/SongbeamerImport.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<script lang="ts">
import T from "../../helpers/T.svelte"
import CombinedInput from "../../inputs/CombinedInput.svelte"
import Dropdown from "../../inputs/Dropdown.svelte"
import Button from "../../inputs/Button.svelte"
import Icon from "../../helpers/Icon.svelte"
import { translate } from "../../../utils/language";
import { Option } from "../../../../types/Main"
import { TranslationMethod } from "../../../../types/Songbeamer"
import { sortObject } from "../../helpers/array"
import { activePopup, categories } from "../../../stores"
import { send } from "../../../utils/request"
import { IMPORT } from "../../../../types/Channels"
const encodingOptions = [
{
id: "utf8",
name: translate("songbeamer_import.utf8"),
},
{
id: "latin1",
name: translate("songbeamer_import.latin1"),
extra: translate("songbeamer_import.older_versions"),
},
]
let selectedEncoding: Option = encodingOptions[0]
const songbeamerCatId: string = "songbeamer"
let cats: any = [
...sortObject(Object.keys($categories).map((key: string) => ({
id: key,
...$categories[key],
})), "name").map((cat: any): Option => ({
id: cat.id,
name: (cat.default? `$:${cat.name}:$` : cat.name),
})),
]
let selectedCategory: Option = cats.find((el: Option) => el.id === songbeamerCatId)
if(!selectedCategory) {
selectedCategory = {
id: songbeamerCatId,
name: "Songbeamer",
}
cats.unshift(selectedCategory)
}
let selectedTranslationMethod = TranslationMethod.MultiLine
function importListener() {
send(IMPORT, ["songbeamer"], {
encoding: selectedEncoding,
category: selectedCategory,
translation: selectedTranslationMethod,
format: {
extensions: ["sng"],
},
})
$activePopup = null
}
</script>

<h4><T id="songbeamer_import.options"/></h4>

<CombinedInput textWidth={30}>
<p><T id="songbeamer_import.encoding" /></p>
<Dropdown value={selectedEncoding?.name} options={encodingOptions} on:click={evt => selectedEncoding = evt.detail}/>
</CombinedInput>

<CombinedInput textWidth={30}>
<p><T id="show.category" /></p>
<Dropdown options={cats} value={selectedCategory?.name} on:click={evt => selectedCategory = evt.detail} />
</CombinedInput>

<h4><T id="songbeamer_import.translations" /></h4>

<div class="translation-method">
{#each Object.values(TranslationMethod) as method}
<Button
center
dark
active={method === selectedTranslationMethod}
on:click={() => selectedTranslationMethod = method}
>
<T id="songbeamer_import.translation_{method}" />
</Button>
{/each}
</div>

<p class="translation-description">
<T id="songbeamer_import.translation_description_{selectedTranslationMethod}" />
</p>

<hr />

<div class="import-button">
<Button center dark on:click={importListener}>
<Icon id="import" size={1.2} right />
<T id="actions.import" />
</Button>
</div>

<style>
h4 {
color: var(--text);
margin: 20px 0;
text-align: center;
}
h4:first-child {
margin-top: 0;
}
h4:last-child {
margin-bottom: 0;
}
.translation-method {
display: flex;
flex-flow: wrap;
}
.translation-method :global(button) {
flex: 1;
border-bottom: 2px solid var(--primary-lighter);
}
.translation-method :global(button.active) {
border-bottom: 2px solid var(--secondary) !important;
}
hr {
border: none;
height: 2px;
margin: 20px 0;
background-color: var(--primary-lighter);
}
.translation-description {
margin: 10px 0;
white-space: normal;
}
.import-button > :global(button) {
width: 100%;
}
</style>
Loading

0 comments on commit a3ce289

Please sign in to comment.