Skip to content

Commit

Permalink
initial book view
Browse files Browse the repository at this point in the history
  • Loading branch information
ConcernedHobbit committed Feb 22, 2023
1 parent 21bd85d commit bbacaae
Show file tree
Hide file tree
Showing 8 changed files with 528 additions and 9 deletions.
236 changes: 236 additions & 0 deletions books/tekis-2017.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
{
"title": "TKO-älyn laulukirja (2017)",
"organisation": "TKO-äly ry.",
"credits": {
"Laulukirjavastaavat": [
"Julius Uusinarkaus",
"Maximilian Remming",
"Paul Saikko"
],
"Lauluja keräsi": [
"Jonne Huotari",
"Juho Kostet",
"Felix Lindholm",
"Henri Malkki",
"Jussi Maristo",
"Eetu Mattila",
"Mikko Rinta-Homi",
"Ville-Veikko Saari"
],
"Kategoriasivut": ["Hugo Holmqvist", "Mauri Karlin", "Topi Talvitie"],
"Oikoluku, läpilaulu": [
"Eetu Mattila",
"Valentin Abramenkov",
"Ada Hyvärinen",
"Sami Lindqvist",
"Markus Riskumäki",
"Nico Roos",
"Sakari Väkevä",
"Tuomas Tynkkynen",
"Martti Rannanjärvi"
],
"Suurkiitokset": ["make", "Google Sheets", "Python", "LaTeX"],
"Kirjojen nouto": ["Kimmo Kulmala", "Miia Rämö"]
},
"songs": [
{ "number": "0", "title": "Sandstorm" },
{ "number": "1", "title": "Finlandia" },
{ "number": "2", "title": "Maamme" },
{ "number": "3", "title": "De Brevitae Vitae" },
{ "number": "4", "title": "Aqua Vera" },
{ "number": "5", "title": "Brännvin, vatten" },
{ "number": "6", "title": "Eino Leinon juomalaulu" },
{ "number": "7", "title": "Eurovision juomalaulu" },
{ "number": "8", "title": "Hauki ja viina" },
{ "number": "9", "title": "Hei vahtimestari" },
{ "number": "10", "title": "Helan går" },
{ "number": "11", "title": "Hell and gore" },
{ "number": "12", "title": "Henkilökuntaa" },
{ "number": "13", "title": "Hypermedialaulu" },
{ "number": "14", "title": "Internationalen" },
{ "number": "15", "title": "Intian kuu" },
{ "number": "16", "title": "Järjestöasiantuntijan runtulaulu" },
{ "number": "17", "title": "Jos (e)ukkosi kieltää sua juomasta" },
{ "number": "18", "title": "Karhuviinaa" },
{ "number": "19", "title": "Ko-Ko-Ko-Koskenkorvaa" },
{ "number": "20", "title": "Lapin Kulta kaanon" },
{ "number": "21", "title": "Leukojen välliin" },
{ "number": "22", "title": "Livet är härligt" },
{ "number": "23", "title": "Lärvit" },
{ "number": "24", "title": "Mielihaluja" },
{ "number": "25", "title": "Minnet" },
{ "number": "26", "title": "No onkos tullut kesä" },
{ "number": "27", "title": "Varpunen jouluaamuna" },
{ "number": "28", "title": "Joulu on nyt" },
{ "number": "29", "title": "Joulu on taas" },
{ "number": "30", "title": "Muurari rabbi" },
{ "number": "31", "title": "Olof Palme -snapvisan" },
{ "number": "32", "title": "Paavi ja Sulttaani" },
{ "number": "33", "title": "Pienet käärmeet" },
{ "number": "34", "title": "Puheenjohtajan juomalaulu" },
{ "number": "35", "title": "Ryyppää vaan" },
{ "number": "36", "title": "Salatequila" },
{ "number": "37", "title": "Sörnai gusha" },
{ "number": "38", "title": "Tänään otetaan" },
{ "number": "39", "title": "Unkarin viini" },
{ "number": "40", "title": "Demokratian kriisi" },
{ "number": "41", "title": "Vesipoikien marssi" },
{ "number": "42", "title": "Vi ska dricka hela natten" },
{ "number": "43", "title": "Vitutuslaulu" },
{ "number": "44", "title": "Felix" },
{ "number": "45", "title": "Finlandssvensk snapsvisa" },
{ "number": "46", "title": "Hullu kirvesmies" },
{ "number": "47", "title": "Jesussoldat" },
{ "number": "49", "title": "Klusterilainen" },
{ "number": "50", "title": "Nyt ryypätään" },
{ "number": "51", "title": "Oispa kaljaa" },
{ "number": "52", "title": "Raittiuslaulu" },
{ "number": "53", "title": "Rati riti ralla" },
{ "number": "54", "title": "Sähkö-ATK laulu" },
{ "number": "55", "title": "Sisään" },
{ "number": "56", "title": "Snapsien kaveri" },
{ "number": "57", "title": "Suhteellisen rivakka turkulainen juomalaulu" },
{ "number": "58", "title": "Turkulainen juomalaulu" },
{ "number": "59", "title": "Vaakuna, Herba ja Koskenkorva" },
{ "number": "60", "title": "Valomerkin jälkeen" },
{ "number": "61", "title": "Vem i helvete?" },
{ "number": "62", "title": "Yliopistoinnovaatio" },
{ "number": "63", "title": "Dokataan" },
{ "number": "64", "title": "Euforia" },
{ "number": "65", "title": "F-U-K-S-I" },
{ "number": "66", "title": "Gurulassa" },
{ "number": "67", "title": "Huutolaatta" },
{ "number": "68", "title": "Internationaali" },
{ "number": "69", "title": "Jallu" },
{ "number": "70", "title": "Kaks kisälliä" },
{ "number": "71", "title": "Kalliolle Kumpulamme" },
{ "number": "72", "title": "Klurre kädelläsi" },
{ "number": "73", "title": "Koff" },
{ "number": "74", "title": "Koodaillaan" },
{ "number": "75", "title": "Melkki kaatuu" },
{ "number": "76", "title": "Sen nimi oli Jallu" },
{ "number": "77", "title": "Lasi täyttyy oluesta" },
{ "number": "78", "title": "Laulu Uudelle" },
{ "number": "79", "title": "Leikkaamaan" },
{ "number": "80", "title": "Leppätalo" },
{ "number": "81", "title": "Lonkéro" },
{ "number": "82", "title": "Luukkaisen poika" },
{
"number": "?",
"title": "test song",
"content": "test song please ignore"
},
{ "number": "84", "title": "Mandariinimies" },
{ "number": "85", "title": "Meksikon pikapano" },
{ "number": "86", "title": "Minun juomani kossu on" },
{ "number": "87", "title": "Muinaisten valssi" },
{ "number": "88", "title": "Murheellisten bittien maa" },
{ "number": "89", "title": "Nisti ja nööri" },
{ "number": "90", "title": "Pienet konjakit" },
{ "number": "91", "title": "Rakastuin mä klusteriin" },
{ "number": "92", "title": "Rakenteisiin asti" },
{ "number": "93", "title": "Sauvakirkon polttaja" },
{ "number": "94", "title": "Skrillexin mökkitie" },
{ "number": "95", "title": "Sitsipeto on irti" },
{ "number": "96", "title": "Skumppaa punkun kaa" },
{ "number": "97", "title": "Urhometso" },
{ "number": "98", "title": "YTHS" },
{ "number": "99", "title": "Viidestoista vuosi" },
{
"number": "100",
"title": "Alfred J. Kwak (Intro)",
"slug": "alfred-j-kwak"
},
{
"number": "101",
"title": "Alfred J. Kwak (Outro)",
"slug": "alfred-j-kwak"
},
{ "number": "102", "title": "Ankronikka" },
{ "number": "103", "title": "Dodo-sorsa" },
{ "number": "104", "title": "Kun mä kuolen" },
{ "number": "105", "title": "Käpylä" },
{ "number": "106", "title": "Leipuri hiiva" },
{ "number": "107", "title": "Matin syntymäpäivälaulu" },
{ "number": "108", "title": "Matkalaulu" },
{ "number": "109", "title": "Minä soitan harmonikkaa" },
{ "number": "110", "title": "Muumit teema" },
{ "number": "111", "title": "Onomatopoeettinen alkoholiliike" },
{ "number": "112", "title": "Paljon jallua" },
{ "number": "113", "title": "Peppi Pitkätossu" },
{ "number": "114", "title": "Pikkukakkosen posti" },
{ "number": "115", "title": "Pokémon teema" },
{ "number": "116", "title": "Pokémon Theme" },
{ "number": "117", "title": "Pumppulaulu" },
{ "number": "118", "title": "Teletappien juomalaulu" },
{ "number": "119", "title": "Tietoliikennelaulu" },
{ "number": "120", "title": "Viinamaa" },
{ "number": "121", "title": "Kalmarevisan" },
{ "number": "122", "title": "Koska meitä käsketään" },
{ "number": "123", "title": "Lapsuuden toverille" },
{ "number": "124", "title": "Nämä lääkkeet" },
{ "number": "125", "title": "Niin minä neitonen sinulle laulan" },
{ "number": "126", "title": "Nocturne" },
{ "number": "127", "title": "Oman kullan silmät" },
{ "number": "128", "title": "Punschen kommer" },
{ "number": "129", "title": "Romanssi" },
{ "number": "130", "title": "Rosvo Roope" },
{ "number": "131", "title": "Ryyppy saapuu" },
{ "number": "132", "title": "Vit vecka" },
{ "number": "133", "title": "Vodka vodka" },
{ "number": "134", "title": "Lenin-setä asuu Venäjällä" },
{ "number": "135", "title": "Neuvostoliiton hymni" },
{ "number": "136", "title": "Oodi sitoutumattomuudelle" },
{ "number": "137", "title": "Dirlandaa" },
{ "number": "138", "title": "Eesti (On My Mind)" },
{ "number": "139", "title": "Ei tippa tapa" },
{ "number": "140", "title": "Ievan Polkka" },
{ "number": "141", "title": "Jallutyttö" },
{ "number": "142", "title": "Jallutähden alla" },
{ "number": "143", "title": "Jaloviinaa" },
{ "number": "144", "title": "Joutsenlaulu" },
{ "number": "145", "title": "Juodaan viinaa" },
{ "number": "146", "title": "Kotiviini" },
{ "number": "147", "title": "Maija" },
{ "number": "148", "title": "Mökkitie" },
{ "number": "149", "title": "Pohjois-Karjala" },
{ "number": "150", "title": "Sinä lähdit pois" },
{ "number": "151", "title": "Soome" },
{ "number": "152", "title": "Taistelu Jaska - Theme" },
{ "number": "153", "title": "TIETO - Value for Life" },
{ "number": "154", "title": "Tähtipölyä" },
{ "number": "155", "title": "Unelmia ja toimistohommia" },
{ "number": "156", "title": "Volga" },
{ "number": "157", "title": "A Finnish Drinking Song" },
{ "number": "158", "title": "Ace of Spades" },
{ "number": "159", "title": "All Star" },
{ "number": "160", "title": "All the Drinks" },
{ "number": "161", "title": "Always Look on the BRight Side of Life" },
{ "number": "162", "title": "Barbie Girl" },
{ "number": "163", "title": "Bohemian Rhapsody" },
{ "number": "164", "title": "Call Me Maybe" },
{ "number": "165", "title": "Drink Your Beer" },
{ "number": "166", "title": "Drunken Sailor" },
{ "number": "167", "title": "Eye of the Tiger" },
{ "number": "168", "title": "Far Over the Misty Mountains Cold" },
{ "number": "169", "title": "Fast Food Song" },
{ "number": "170", "title": "HEYYEYAAEYAAAEYA" },
{ "number": "171", "title": "My Favourite DRinks" },
{ "number": "172", "title": "Never Gonna Give You Up" },
{ "number": "173", "title": "Numa Numa" },
{ "number": "174", "title": "Still Alive" },
{ "number": "175", "title": "Syphilis" },
{ "number": "176", "title": "Take on Me" },
{ "number": "177", "title": "The ABC song" },
{
"number": "178",
"title": "We Are Number One But Everytime One Is Said Everyone Takes a Drink"
},
{ "number": "179", "title": "Wonderwall" },
{ "number": "180", "title": "Yogi Bear" },
{ "number": "181", "title": "Luonnontieteilijän uni" },
{ "number": "n-1", "title": "Oodi Kumpulalle" },
{ "number": "n", "title": "Ken on fuksi" },
{ "number": "", "title": "Teinilaulu" }
]
}
13 changes: 13 additions & 0 deletions components/BackButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useRouter } from "next/router";
import { useCallback } from "react";
import { Link } from "./Link";

export const BackButton = () => {
const router = useRouter();
const goBack = useCallback(() => router.back(), [router]);
return (
<Link variant="primary" href="/" onClick={goBack}>
Back
</Link>
);
};
37 changes: 37 additions & 0 deletions lib/books.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { Book } from "@/types/book";

import { promises } from "fs";
import path from "path";

const directory = path.join(process.cwd(), "books");

const getFilenames = async () => promises.readdir(directory);

// FIXME: Validate JSON structure (parsed data is `unknown`)
export const getBooks = async (): Promise<Book[]> => {
const filenames = await getFilenames();
return await Promise.all(filenames.map(readBook));
};

export const getBook = async (name: string): Promise<Book | null> => {
const filenames = await getFilenames();
const filename = `${name}.json`;
if (!filenames.includes(filename)) {
return null;
}
return readBook(filename);
};

const readBook = async (filename: string): Promise<Book> => {
const filePath = path.join(directory, filename);
const contents = await promises.readFile(filePath, "utf-8");
return {
...JSON.parse(contents),
name: path.basename(filename, ".json"),
};
};

export const getBookNames = async () => {
const filenames = await getFilenames();
return filenames.map((filename) => path.basename(filename, ".json"));
};
18 changes: 14 additions & 4 deletions lib/getTelegramUrl.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import { Book } from "@/types/book";
import type { Song } from "@/types/song";
import slugify from "./slugify";

const BYTE_LIMIT = 4096;
const TELEGRAM_BASE = "https://t.me/share/url";
const FALLBACK_BASE_URL = "https://laulum.me/";

const getTelegramURL = (songUrl: string, text: string): string => {
export const getTelegramURL = (songUrl: string, text?: string): string => {
const url = new URL(TELEGRAM_BASE);
url.searchParams.set("url", songUrl);
url.searchParams.set("text", text);
if (text) url.searchParams.set("text", text);
return url.toString();
};

/**
* A function to generate a Telegram share link for a song.
* Reads the base URL from environment variable BASE_URL.
* @param song
* @param baseURL Base URL for the link, e.g. "https://laulum.me" (default)
*/
export const getTelegramLink = (song: Song, baseURL?: string): string => {
export const getSongLink = (song: Song, baseURL?: string): string => {
const songURL = new URL(
`/songs/${slugify(song.title)}`,
baseURL ?? FALLBACK_BASE_URL
Expand All @@ -40,3 +40,13 @@ export const getTelegramLink = (song: Song, baseURL?: string): string => {
// If the body is too long (exceeds BYTE_LIMIT), send only necessary information
return getTelegramURL(songURL, header);
};

/**
* A function to generate a Telegram share link for a book.
* @param book
* @param baseUrl Base URL for the link, e.g. "https://laulum.me" (default)
*/
export const getBookLink = (book: Book, baseURL?: string): string => {
const bookURL = new URL(`/books/${book.name}`, baseURL ?? FALLBACK_BASE_URL);
return getTelegramURL(bookURL.toString());
};
5 changes: 5 additions & 0 deletions lib/songs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,8 @@ export const getSong = async (querySlug: string) => {

return readSong(match.filename);
};

export const songExists = async (querySlug: string) => {
const mapping = await getSlugs(querySlug);
return mapping.some(({ slug }) => querySlug === slug);
};
Loading

0 comments on commit bbacaae

Please sign in to comment.