Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 84 additions & 2 deletions convert/convertBooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,13 +248,15 @@ export async function convertBooks(
const catalogEntries: Promise<any>[] = [];
/**quizzes by book collection*/
const quizzes: any = {};
/**songbooks by book collection*/
const songbooks: any = {};
/**htmlBooks by book collection*/
const htmlBooks: any = {};
/**array of files to be written*/
const files: any[] = [];

// copy book-related folder resources
['quiz', 'songs'].forEach((folder) => {
['quiz'].forEach((folder) => {
const folderSrcDir = path.join(dataDir, folder);
const folderDstDir = path.join('static', folder);
if (fs.existsSync(folderSrcDir)) {
Expand Down Expand Up @@ -302,12 +304,12 @@ export async function convertBooks(
}
//add empty array of quizzes for book collection
quizzes[context.docSet] = [];
songbooks[context.docSet] = [];
htmlBooks[context.docSet] = [];
for (const book of collection.books) {
let bookConverted = false;
switch (book.type) {
case 'story':
case 'songs':
case 'audio-only':
case 'bloom-player':
case 'undefined':
Expand All @@ -327,6 +329,22 @@ export async function convertBooks(
});
displayBookId(context.bcId, book.id);
break;
case 'songs':
bookConverted = true;
songbooks[context.docSet].push({ id: book.id, name: book.name });
files.push({
path: path.join(
'static',
'collections',
context.bcId,
'songs',
book.id + '.json'
),
content: JSON.stringify(convertSongIndex(context, book), null, 2)
});
convertScriptureBook(pk, context, book, bcGlossary, docs, inputFiles);
displayBookId(context.bcId, book.id);
break;
default:
bookConverted = true;
if (book.format === 'html') {
Expand Down Expand Up @@ -372,6 +390,14 @@ export async function convertBooks(
fs.mkdirSync(qPath, { recursive: true });
}
}
//add songbooks path if necessary
if (songbooks[context.docSet].length > 0) {
const sPath = path.join('static', 'collections', context.bcId, 'songs');
if (!fs.existsSync(sPath)) {
if (verbose) console.log('creating: ' + sPath);
fs.mkdirSync(sPath, { recursive: true });
}
}
}
//write catalog entries
const entries = await Promise.all(catalogEntries);
Expand Down Expand Up @@ -462,6 +488,18 @@ export type Quiz = {
passScore?: number; //\pm
};

export type SongIndex = {
id: string;
byNumber: {
id: number;
title: string;
}[];
byTitle: {
id: number;
title: string;
}[];
};

function convertHtmlBook(context: ConvertBookContext, book: BookConfig, files: any[]) {
const srcFile = path.join(context.dataDir, 'books', context.bcId, book.file);
const dstFile = path.join('static', 'collections', context.bcId, book.file);
Expand All @@ -474,6 +512,50 @@ function convertHtmlBook(context: ConvertBookContext, book: BookConfig, files: a
});
}

function convertSongIndex(context: ConvertBookContext, book: BookConfig): SongIndex {
if (context.verbose) {
console.log('Converting SongBook:', book.id);
}
const idxByNumber = fs.readFileSync(
path.join(context.dataDir, 'songs', `${context.bcId}-${book.id}-songs-by-number.txt`),
'utf8'
);
const idxByTitle = fs.readFileSync(
path.join(context.dataDir, 'songs', `${context.bcId}-${book.id}-songs-by-title.txt`),
'utf8'
);

const songIndex: SongIndex = {
id: book.id,
byNumber: [],
byTitle: []
};

idxByNumber
.trim()
.split('\n')
.forEach((line) => {
const parts = line.split('\t');

songIndex.byNumber.push({
id: Number(parts[0]),
title: parts[1]
});
});

idxByTitle
.trim()
.split('\n')
.forEach((line) => {
const parts = line.split('\t');
songIndex.byTitle.push({
id: Number(parts[0]),
title: parts[1]
});
});

return songIndex;
}
function convertQuizBook(context: ConvertBookContext, book: BookConfig): Quiz {
if (context.verbose) {
console.log('Converting QuizBook:', book.id);
Expand Down
32 changes: 32 additions & 0 deletions src/lib/components/SongCard.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<script lang="ts">
import { base } from '$app/paths';
import { s, convertStyle } from '$lib/data/stores';

export let id: number;
export let title: string;

function onSongClick() {
console.log(`click: ${id}, ${title}`);
}
</script>

<div>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div style="text-decoration:none;" on:click={onSongClick}>
<div class="flex space-x-4 h-16 border-b-2">
<div
class="flex-none w-16 flex items-center justify-end"
style={convertStyle($s['ui.song.number'])}
>
{id}
</div>
<div
class="flex-1 flex items-center justify-start"
style={convertStyle($s['ui.song.title'])}
>
{title}
</div>
</div>
</div>
</div>
27 changes: 27 additions & 0 deletions src/routes/songs/[collection]/[id]/+page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { base } from '$app/paths';
import config from '$lib/data/config';

/** @type {import('./$types').PageLoad} */
export async function load({ params, fetch }) {
const id = params.id;
const collection = params.collection;
console.log(`Songs: collection=${collection} id=${id}`);

let songs;
try {
const response = await fetch(`${base}/collections/${collection}/songs/${id}.json`);
if (!response.ok) {
throw new Error('Failed to fetch songs JSON file');
}

songs = await response.json();
} catch (error) {
console.error('Error fetching songs JSON file', error);
}

return {
collection,
book: id,
songs
};
}
68 changes: 68 additions & 0 deletions src/routes/songs/[collection]/[id]/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<script>
import { page } from '$app/stores';
import Navbar from '$lib/components/Navbar.svelte';
import SongCard from '$lib/components/SongCard.svelte';
import { s, t, convertStyle } from '$lib/data/stores';

const { collection, book, songs } = $page.data;

let selectedTab = 'number';
</script>

<div class="grid grid-rows-[auto,1fr]" style="height:100vh;height:100dvh;">
<div class="navbar">
<Navbar>
<!-- <div slot="left-buttons" /> -->
<!-- <label for="sidebar" slot="center">
<div class="btn btn-ghost normal-case text-xl">{$t['Menu_Plans']}</div>
</label> -->
<!-- <div slot="right-buttons" class="flex items-center"> -->
</Navbar>
</div>

<div class="overflow-y-auto mx-auto max-w-screen-md w-full">
<div
role="tablist"
class="dy-tabs dy-tabs-bordered"
style={convertStyle($s['ui.selector.tabs'])}
>
<input
type="radio"
name="songs"
role="tab"
class="dy-tab {selectedTab === 'number' ? 'dy-tab-active' : ''}"
on:click={() => (selectedTab = 'number')}
aria-label={$t['Song_List_By_Number']}
style={convertStyle($s['ui.selector.tabs'])}
/>
<input
type="radio"
name="songs"
role="tab"
class="dy-tab {selectedTab === 'title' ? 'dy-tab-active' : ''}"
on:click={() => (selectedTab = 'title')}
aria-label={$t['Song_List_By_Title']}
style={convertStyle($s['ui.selector.tabs'])}
/>
</div>

<div id="container" class="song-list">
{#if selectedTab === 'number'}
{#each songs.byNumber as song}
<SongCard id={song.id} title={song.title} />
{/each}
{:else if selectedTab === 'title'}
{#each songs.byTitle as song}
<SongCard id={song.id} title={song.title} />
{/each}
{/if}
</div>
</div>
</div>

<style lang="postcss">
.dy-tabs-bordered > .dy-tab-active {
@apply border-b-[6px];
@apply !border-white;
}
</style>
Loading