Change many lines

This commit is contained in:
Luca Bosin
2023-08-17 09:41:41 +02:00
parent 5e99dc4aa2
commit ba42d6958a
36 changed files with 1251 additions and 312 deletions

View File

@ -1,11 +0,0 @@
<script>
</script>
<section class="gallery">
<slot />
</section>
<style>
</style>

View File

@ -1,29 +0,0 @@
[
{
"slug": "sts",
"title": "Testgalerie",
"description": "Das ist eine Testgalerie",
"timestamp": "2023-08-04T00:00:00.000Z",
"place": "Berlin",
"tags": [
"Berlin",
"Test"
],
"license": "CC BY-NC-ND 4.0",
"items": "img.zip",
"itemsMeta": [
{
"item": "cover.jpg",
"title": "Cover",
"description": "Das ist das Cover",
"timestamp": "2023-08-04T00:00:00.000Z",
"place": "Berlin",
"tags": [
"Berlin",
"Cover"
],
"license": "CC BY-NC-ND 4.0"
}
]
}
]

View File

@ -0,0 +1,7 @@
<footer>
</footer>
<style>
</style>

View File

@ -0,0 +1,36 @@
<script>
import { strf } from '$lib/data/language.js';
function sortItems() {
items.sort((a, b) => {
if (a.item < b.item) return -1;
if (a.item > b.item) return 1;
return 0;
});
}
/** @type {Item[]} */
export let items = [];
export let base = '';
</script>
<section class="gallery">
<ul>
{#each items as item (item.item)}
<!-- <Photo src={`${uriBase}&item=${item.item}`} alt={$strf(item.title)} /> -->
<li>
<h3>{item.item}</h3>
<p>
<b>Title:</b> {$strf(item.title)}<br />
<b>Description:</b> {#if item.description}{$strf(item.description)}{:else}<i>no description</i>{/if}
</p>
<img src={`${base}&item=${item.item}`} alt={$strf(item.title)} />
</li>
{/each}
</ul>
</section>
<style>
</style>

View File

@ -0,0 +1,20 @@
<script>
import Icon from "./Icon.svelte";
export let title = 'Galerie';
export let description = '';
/** @type {string | undefined} */
export let back = undefined;
</script>
<header>
<section class="title">
{#if back}
<a href={back}>
<Icon name="arrow-left" />
</a>
{/if}
<h1>{title}</h1>
<p>{description}</p>
</section>
</header>

View File

@ -1,5 +1,6 @@
<script>
export let name = '';
name;
</script>

5
src/lib/data/album.js Normal file
View File

@ -0,0 +1,5 @@
import { writable } from "svelte/store";
const album = {error: "Not found"};
/** @type {import('svelte/store').Writable<ApiError | Album>} */
export default writable(album);

69
src/lib/data/language.js Normal file
View File

@ -0,0 +1,69 @@
import { writable, derived } from "svelte/store";
/** @type {import('svelte/store').Writable<TranslationKey>} */
export const language = writable('de');
/**
* @type {Record<TranslationKey, Record<String, String>>}
*/
const translations = {
de: {
'gallery': 'Galerie',
'album': 'Album',
'albums': 'Alben',
'photo': 'Foto',
'photos': 'Fotos',
'video': 'Video',
'videos': 'Videos',
'back': 'Zurück',
'small': 'Klein',
'medium': 'Mittel',
'large': 'Groß',
'open': 'Öffnen',
'download': 'Herunterladen',
'download-all': 'Alle herunterladen',
},
en: {
'gallery': 'Gallery',
'album': 'Album',
'albums': 'Albums',
'photo': 'Photo',
'photos': 'Photos',
'video': 'Video',
'videos': 'Videos',
'back': 'Back',
'small': 'Small',
'medium': 'Medium',
'large': 'Large',
'open': 'Open',
'download': 'Download',
'download-all': 'Download all',
}
};
export const str = derived(language, $language => {
/**
* @param {string} key
* @param {...any} args
*/
function translate(key, ...args) {
const str = translations[$language][key];
if (str === undefined) return key;
return str.replace(/\{(\d+)\}/g, (_, i) => args[i]);
}
return translate;
});
export const strf = derived(language, $language => {
/**
* @param {Translation | string} translations
* @param {...any} args
*/
function translate(translations, ...args) {
if (typeof translations === 'string') return translations;
const str = translations[$language];
if (str === undefined) return translations.de;
return str.replace(/\{(\w+)\}/g, (_, i) => args[i]);
}
return translate;
});

View File

@ -0,0 +1,73 @@
[
{
"type": "cc0",
"title": "CC0 1.0 Universal (CC0 1.0) Public Domain Dedication",
"text": {
"de": "Sie sind berechtigt, dieses Werk zu kopieren, verändern, verbreiten und aufzuführen, selbst für kommerzielle Zwecke, <b>ohne um weitere Erlaubnis bitten zu müssen</b>.",
"en": "You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission."
},
"url": "https://creativecommons.org/publicdomain/zero/1.0/"
},
{
"type": "cc-by",
"title": "Creative Commons Attribution 4.0 International (CC BY 4.0)",
"text": {
"de": "Sie sind berechtigt, dieses Werk zu kopieren, verändern, verbreiten und aufzuführen, selbst für kommerzielle Zwecke, <b>wenn Sie den Namen des Autors nennen</b>, die Lizenz erwähnen und ggf. angeben, ob Veränderungen vorgenommen wurden.",
"en": "You can copy, modify, distribute and perform the work, even for commercial purposes, <b>if you give appropriate credit</b>, mention the license and indicate if changes were made."
},
"url": "https://creativecommons.org/licenses/by/4.0/"
},
{
"type": "cc-by-sa",
"title": "Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)",
"text": {
"de": "Sie sind berechtigt, dieses Werk zu kopieren, verändern, verbreiten und aufzuführen, selbst für kommerzielle Zwecke, <b>wenn Sie den Namen des Autors nennen</b>, die Lizenz erwähnen und ggf. angeben, ob Veränderungen vorgenommen wurden. Wenn Sie das Werk verändern, dürfen Sie das veränderte Werk nur <b>unter derselben Lizenz</b> verbreiten.",
"en": "You can copy, modify, distribute and perform the work, even for commercial purposes, <b>if you give appropriate credit</b>, mention the license and indicate if changes were made. If you remix, transform, or build upon the material, you must distribute your contributions <b>under the same license</b> as the original."
},
"url": "https://creativecommons.org/licenses/by-sa/4.0/"
},
{
"type": "cc-by-nc",
"title": "Creative Commons Attribution-NonCommercial 4.0 International (CC BY-NC 4.0)",
"text": {
"de": "Sie sind berechtigt, dieses Werk zu kopieren, verändern, verbreiten und aufzuführen, <b>wenn Sie den Namen des Autors nennen</b>, die Lizenz erwähnen und ggf. angeben, ob Veränderungen vorgenommen wurden. Sie dürfen das Werk <b>nicht für kommerzielle Zwecke</b> nutzen.",
"en": "You can copy, modify, distribute and perform the work, <b>if you give appropriate credit</b>, mention the license and indicate if changes were made. You may use the material only for <b>non commercial purposes</b>."
},
"url": "https://creativecommons.org/licenses/by-nc/4.0/"
},
{
"type": "cc-by-nc-sa",
"title": "Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)",
"text": {
"de": "Sie sind berechtigt, dieses Werk zu kopieren, verändern, verbreiten und aufzuführen, <b>wenn Sie den Namen des Autors nennen</b>, die Lizenz erwähnen und ggf. angeben, ob Veränderungen vorgenommen wurden. Sie dürfen das Werk <b>nicht für kommerzielle Zwecke</b> nutzen. Wenn Sie das Werk verändern, dürfen Sie das veränderte Werk nur <b>unter derselben Lizenz</b> verbreiten.",
"en": "You can copy, modify, distribute and perform the work, <b>if you give appropriate credit</b>, mention the license and indicate if changes were made. You may use the material only for <b>non commercial purposes</b>. If you remix, transform, or build upon the material, you must distribute your contributions <b>under the same license</b> as the original."
},
"url": "https://creativecommons.org/licenses/by-nc-sa/4.0/"
},
{
"type": "cc-by-nd",
"title": "Creative Commons Attribution-NoDerivatives 4.0 International (CC BY-ND 4.0)",
"text": {
"de": "Sie sind berechtigt, dieses Werk zu kopieren, verbreiten und aufzuführen, selbst für kommerzielle Zwecke, <b>wenn Sie den Namen des Autors nennen</b> und die Lizenz erwähnen. Sie dürfen das Werk <b>nicht verändern</b>.",
"en": "You can copy, distribute and perform the work, even for commercial purposes, <b>if you give appropriate credit</b> and mention the license. You may <b>not alter</b> the work in any way."
},
"url": "https://creativecommons.org/licenses/by-nd/4.0/"
},
{
"type": "cc-by-nc-nd",
"title": "Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)",
"text": {
"de": "Sie sind berechtigt, dieses Werk zu kopieren, verbreiten und aufzuführen, <b>wenn Sie den Namen des Autors nennen</b> und die Lizenz erwähnen. Sie dürfen das Werk <b>nicht verändern</b> und <b>nicht für kommerzielle Zwecke</b> nutzen.",
"en": "You can copy, distribute and perform the work, <b>if you give appropriate credit</b> and mention the license. You may <b>not alter</b> the work in any way and may use the material only for <b>non commercial purposes</b>."
},
"url": "https://creativecommons.org/licenses/by-nc-nd/4.0/"
},
{
"type": "all-rights-reserved",
"title": "All rights reserved",
"text": {
"de": "Alle Rechte vorbehalten. Dieses Werk darf ohne Erlaubnis des Autors <b>nicht kopiert, verändert, verbreitet oder aufgeführt</b> werden.",
"en": "All rights reserved. This work <b>may not be copied, modified, distributed or performed</b> without the permission of the author."
}
}
]

43
src/lib/types.d.ts vendored Normal file
View File

@ -0,0 +1,43 @@
type TranslationKey = 'de' | 'en';
type Translation = Record<TranslationKey, string>;
type LicenseType = 'cc0' | 'cc-by' | 'cc-by-sa' | 'cc-by-nc' | 'cc-by-nc-sa' | 'cc-by-nd' | 'cc-by-nc-nd' | 'all-rights-reserved';
type License = {
type: LicenseType;
title: Translation | string;
text: Translation | string;
url?: string;
}
type Metadata = {
title: Translation | string;
description?: Translation | string;
authors?: string[] | string;
place?: string;
tags?: string[];
license?: License;
}
type AlbumMetadata = Metadata & {
date: string; // ISO 8601, e.g. 2020-12-24, used for sorting
cover?: string;
};
type ItemMetadata = Metadata & {
item: string;
timestamp?: string; // ISO 8601, e.g. 2020-12-24T12:00:00Z
};
type Item = ItemMetadata;
type Album = AlbumMetadata & {
slug: string;
uriTimestamp?: string;
items: Item[];
};
type ApiError = {
error: string;
};

View File

@ -2,5 +2,30 @@
import { page } from '$app/stores';
</script>
<h1>{$page.status}</h1>
<p>{$page.error?.message}</p>
<div class="container">
<h1>{$page.status} {$page.error?.message}</h1>
<a href="https://bosin.ch/">back to bosin.ch</a>
</div>
<style>
*{
margin: 0px 0px 0px 0px;
padding: 0px 0px 0px 0px;
border: none;
outline: none;
}
:global(body){
background-color: #22313F;
}
.container {
font-family: Roboto, open-sans, Arial;
position: fixed;
top: 50%;
left: 50%;
color: #FFF;
transform: translate(-50%, -50%);
}
a {
color: #FFF;
}
</style>

View File

@ -0,0 +1,4 @@
<script>
</script>

View File

@ -1,16 +1,26 @@
import { error } from '@sveltejs/kit';
import albums from '$lib/albums.json';
import StreamZip from 'node-stream-zip';
/** @type {import('./$types').PageLoad} */
export function load({ params }) {
const album = albums.find((album) => album.slug === params.slug);
if (album) {
return {
title: album.title,
content: album.description,
image: '/s/lga/?i=LFB04128-Enhanced-NR.jpg'
};
}
export async function load({ params }) {
const { slug, timestamp } = params;
const cslug = slug.replace(/[^\w-]/gi, '');
const ctimestamp = timestamp?.replace(/[^\w-]/gi, '');
throw error(404, 'Not found');
const zipFile = `./zip/${cslug}${ctimestamp ? '-' + ctimestamp :''}.zip`;
let entries = null;
try {
const zip = new StreamZip.async({ file: zipFile });
entries = await zip.entries();
await zip.close();
} catch (err) {
console.error(err);
throw error(404, 'Not found');
}
return {
slug: params.slug,
timestamp: params.timestamp,
entries
};
}

View File

@ -1,8 +1,14 @@
<script>
import { str, strf } from '$lib/data/language.js';
import Header from '$lib/components/Header.svelte';
import Gallery from '$lib/components/Gallery.svelte';
/** @type {import('./$types').PageData} */
export let data;
const uriBase = `/s/apitest.php?slug=${data.slug}` + (data.timestamp ? `&timestamp=${data.timestamp}` : '');
</script>
<h1>{data.title}</h1>
<div>{@html data.content}</div>
<img src={data.image} alt={data.title} />
<Header title={$strf(data.slug)}/>
<Gallery items={$album.items} base={uriBase} />

View File

@ -1,26 +0,0 @@
import { error } from '@sveltejs/kit';
/** @type {import('./$types').RequestHandler} */
export function GET({ url }) {
const noAttachment = url.searchParams.has('r');
const min = Number(url.searchParams.get('min') ?? '0');
const max = Number(url.searchParams.get('max') ?? '1');
const d = max - min;
if (isNaN(d) || d < 0) {
throw error(400, 'min and max must be numbers, and min must be less than max');
}
const random = min + Math.random() * d;
return new Response(String(random), {
headers: {
'Content-Type': 'text/plain',
'Content-Disposition': noAttachment ? 'inline' : 'attachment'
}
});
}

View File

@ -0,0 +1,20 @@
import { error } from '@sveltejs/kit';
import StreamZip from 'node-stream-zip';
/** @type {import('./$types').PageLoad} */
export async function load({ params }) {
let entries = null;
try {
const zip = new StreamZip.async({ file: `./zip/${params.slug}.zip` });
entries = await zip.entries();
await zip.close();
} catch (err) {
console.error(err);
}
error(404, 'Not found');
return {
slug: params.slug,
timestamp: params.timestamp,
entries
};
}

View File

@ -0,0 +1,13 @@
<script>
/** @type {import('./$types').PageData} */
export let data;
/** @type {import('node-stream-zip').ZipEntry[]} */
$: entries = data.entries !== null ? (Array.isArray(data.entries) ? data.entries : Object.values(data.entries)) : [];
</script>
<h2>Zip Entries</h2>
{#each entries as entry}
{entry.name}
{:else}
No entries
{/each}

View File

@ -0,0 +1,24 @@
import StreamZip from 'node-stream-zip';
import sharp from 'sharp';
/** @type {import('./$types').RequestHandler} */
export async function GET({ params }) {
let entryData = null;
try {
const zip = new StreamZip.async({ file: `./zip/${params.slug}.zip` });
const entries = Object.values(await zip.entries()).filter(entry => entry.name.endsWith('.jpg'));
const entry = entries[Math.floor(Math.random() * entries.length)];
const content = await zip.entryData(entry.name);
await zip.close();
entryData = await sharp(content).resize(400).avif({ quality: 70, effort: 0 }).toBuffer()
} catch (err) {
console.error(err);
}
return new Response(entryData, {
headers: {
'Content-Type': 'image/avif',
'Content-Disposition': 'inline'
}
});
}