Add attributes for demonstation purposes
This commit is contained in:
30
.vscode/tasks.json
vendored
Normal file
30
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||||
|
// for the documentation about the tasks.json format
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "Build & Deploy",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "npm run build && rsync -arzP --progress --delete-after --force --update --inplace --times ./build ./package.json ./package-lock.json root@f.bosin.ch:/home/sveltekit"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Restart Server",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "ssh f.bosin.ch systemctl restart bosin-files"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Full Deploy",
|
||||||
|
"dependsOn": [
|
||||||
|
"Build & Deploy"
|
||||||
|
],
|
||||||
|
"type": "shell",
|
||||||
|
"command": "ssh f.bosin.ch systemctl restart bosin-files",
|
||||||
|
"problemMatcher": [],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
39
README.md
39
README.md
@ -1,38 +1,5 @@
|
|||||||
# create-svelte
|
# f.bosin.ch/g
|
||||||
|
|
||||||
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).
|
Die neue Fotogalerie von Luca Bosin.
|
||||||
|
|
||||||
## Creating a project
|
Jetzt testen, auf [f.bosin.ch/g/test](https://f.bosin.ch/g/test).
|
||||||
|
|
||||||
If you're seeing this, you've probably already done this step. Congrats!
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# create a new project in the current directory
|
|
||||||
npm create svelte@latest
|
|
||||||
|
|
||||||
# create a new project in my-app
|
|
||||||
npm create svelte@latest my-app
|
|
||||||
```
|
|
||||||
|
|
||||||
## Developing
|
|
||||||
|
|
||||||
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run dev
|
|
||||||
|
|
||||||
# or start the server and open the app in a new browser tab
|
|
||||||
npm run dev -- --open
|
|
||||||
```
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
To create a production version of your app:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
You can preview the production build with `npm run preview`.
|
|
||||||
|
|
||||||
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import { getFileName } from '$lib/util/links';
|
||||||
import { strf } from '$lib/data/language.js';
|
import { strf } from '$lib/data/language.js';
|
||||||
|
|
||||||
function sortItems() {
|
function sortItems() {
|
||||||
@ -22,10 +23,12 @@
|
|||||||
<li>
|
<li>
|
||||||
<h3>{item.item}</h3>
|
<h3>{item.item}</h3>
|
||||||
<p>
|
<p>
|
||||||
<b>Title:</b> {$strf(item.title)}<br />
|
<b>Title:</b> {#if item.title}{$strf(item.title)}{:else}<i>no title</i>{/if}<br />
|
||||||
<b>Description:</b> {#if item.description}{$strf(item.description)}{:else}<i>no description</i>{/if}
|
<b>Description:</b> {#if item.description}{$strf(item.description)}{:else}<i>no description</i>{/if}<br />
|
||||||
|
<b>Links:</b> <a href={`${base}${item.item}`}>Open</a> • <a href={`${base}${item.item}/download`}>Download</a><br />
|
||||||
|
<b>Thumbnails:</b> <a href={`${base}${item.item}/t/s`}>Small</a> • <a href={`${base}${item.item}/t/m`}>Medium</a> • <a href={`${base}${item.item}/t/l`}>Large</a> • <a href={`${base}${item.item}/t/full`}>Full</a>
|
||||||
</p>
|
</p>
|
||||||
<img src={`${base}${item.item}/t`} alt={$strf(item.title) || item.item} />
|
<img src={`${base}${item.item}/t`} alt={(item.title ? $strf(item.title) : null) || (item.description ? $strf(item.description) : null) || getFileName(item.item)} />
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
3
src/lib/types.d.ts
vendored
3
src/lib/types.d.ts
vendored
@ -12,7 +12,6 @@ type License = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Metadata = {
|
type Metadata = {
|
||||||
title: Translation | string;
|
|
||||||
description?: Translation | string;
|
description?: Translation | string;
|
||||||
authors?: string[] | string;
|
authors?: string[] | string;
|
||||||
place?: string;
|
place?: string;
|
||||||
@ -21,11 +20,13 @@ type Metadata = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AlbumMetadata = Metadata & {
|
type AlbumMetadata = Metadata & {
|
||||||
|
title: Translation | string;
|
||||||
date: string; // ISO 8601, e.g. 2020-12-24, used for sorting
|
date: string; // ISO 8601, e.g. 2020-12-24, used for sorting
|
||||||
cover?: string;
|
cover?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ItemMetadata = Metadata & {
|
type ItemMetadata = Metadata & {
|
||||||
|
title?: Translation | string;
|
||||||
item: string;
|
item: string;
|
||||||
timestamp?: string; // ISO 8601, e.g. 2020-12-24T12:00:00Z
|
timestamp?: string; // ISO 8601, e.g. 2020-12-24T12:00:00Z
|
||||||
};
|
};
|
||||||
|
|||||||
9
src/routes/+layout.svelte
Normal file
9
src/routes/+layout.svelte
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<slot />
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:global(*) {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,14 +1,12 @@
|
|||||||
import { getMetadataOpen } from '$lib/util/album';
|
import { getMetadata } from '$lib/util/album';
|
||||||
import { getAlbumUri, getZipName } from '$lib/util/links';
|
import { getAlbumUri, getZipName } from '$lib/util/links';
|
||||||
|
|
||||||
/** @type {import('./$types').PageLoad} */
|
/** @type {import('./$types').PageLoad} */
|
||||||
export async function load({ params }) {
|
export async function load({ params }) {
|
||||||
const {zip, album} = await getMetadataOpen(getZipName(params));
|
const album = await getMetadata(getZipName(params));
|
||||||
const entries = await zip.entries();
|
|
||||||
const base = getAlbumUri(params);
|
const base = getAlbumUri(params);
|
||||||
await zip.close();
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
album, entries, base
|
album, base
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,4 +11,10 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Header title={$strf(data.album.title)}/>
|
<Header title={$strf(data.album.title)}/>
|
||||||
|
<p>
|
||||||
|
<b>Description:</b> {#if data.album.description}{$strf(data.album.description)}{:else}<i>no description</i>{/if}<br />
|
||||||
|
<b>Authors: </b> {#if data.album.authors}{Array.isArray(data.album.authors) ? data.album.authors.join(', ') : data.album.authors}{:else}<i>unknown author</i>{/if}<br />
|
||||||
|
<b>Sorting Date:</b> {data.album.date}<br />
|
||||||
|
<b>License:</b> {data.album.license}<br />
|
||||||
|
</p>
|
||||||
<Gallery items={data.album.items} base={`${data.base}/i/`} />
|
<Gallery items={data.album.items} base={`${data.base}/i/`} />
|
||||||
|
|||||||
@ -1,29 +0,0 @@
|
|||||||
import { error } from '@sveltejs/kit';
|
|
||||||
import StreamZip from 'node-stream-zip';
|
|
||||||
|
|
||||||
/** @type {import('./$types').LayoutLoad} */
|
|
||||||
export async function load({ params }) {
|
|
||||||
const { slug, timestamp } = params;
|
|
||||||
const cslug = slug.replace(/[^\w-]/gi, '');
|
|
||||||
const ctimestamp = timestamp?.replace(/[^\w-]/gi, '');
|
|
||||||
|
|
||||||
const file = `./zip/${cslug}${ctimestamp ? '-' + ctimestamp :''}.zip`;
|
|
||||||
let entries = null;
|
|
||||||
try {
|
|
||||||
const zip = new StreamZip.async({ file });
|
|
||||||
entries = await zip.entries();
|
|
||||||
await zip.close();
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
throw error(404, 'Not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`REQ: ${cslug}/${ctimestamp} @ ${file} with ${entries.length} entries:\n ${JSON.stringify(entries)}`);
|
|
||||||
|
|
||||||
return {
|
|
||||||
file,
|
|
||||||
cslug,
|
|
||||||
ctimestamp,
|
|
||||||
entries
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,8 +1,14 @@
|
|||||||
import { getFileName } from '$lib/util/links.js';
|
import { getMetadata } from '$lib/util/album';
|
||||||
|
import { getFileName, getFilePath, getZipName } from '$lib/util/links.js';
|
||||||
|
|
||||||
/** @type {import('./$types').PageLoad} */
|
/** @type {import('./$types').PageLoad} */
|
||||||
export async function load({ params }) {
|
export async function load({ params }) {
|
||||||
|
const album = await getMetadata(getZipName(params));
|
||||||
|
const filePath = getFilePath(params.item);
|
||||||
|
const item = album.items.find(item => item.item === filePath);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
item,
|
||||||
filename: getFileName(params.item)
|
filename: getFileName(params.item)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,19 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import { strf } from '$lib/data/language';
|
||||||
|
|
||||||
/** @type {import('./$types').PageData} */
|
/** @type {import('./$types').PageData} */
|
||||||
export let data;
|
export let data;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<img src="./{data.filename}/t/full" />
|
{#if data.item}
|
||||||
|
<img class="image" src="{data.filename}/t/full" alt={(data.item.title ? $strf(data.item.title) : null) || (data.item.description ? $strf(data.item.description) : null) || data.filename} />
|
||||||
|
{:else}
|
||||||
|
<p>Item not found.</p>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.image {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
import { getFile } from '$lib/util/album';
|
import { getFile } from '$lib/util/album';
|
||||||
import { getFilePath, getZipName } from '$lib/util/links';
|
import { getFileName, getFilePath, getZipName } from '$lib/util/links';
|
||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
|
|
||||||
/** @type {import('./$types').RequestHandler} */
|
/** @type {import('./$types').RequestHandler} */
|
||||||
export async function GET({ params }) {
|
export async function GET({ params }) {
|
||||||
let thumbnail = null;
|
let thumbnail = null;
|
||||||
console.log(`Getting thumbnail for ${params}`);
|
console.log(`Getting thumbnail for ${params}`);
|
||||||
|
let width = 400;
|
||||||
try {
|
try {
|
||||||
const content = await getFile(getZipName(params), getFilePath(params.item));
|
const content = await getFile(getZipName(params), getFilePath(params.item));
|
||||||
let width = 400;
|
|
||||||
if (params.width) {
|
if (params.width) {
|
||||||
if (params.width === 'full') {
|
if (params.width === 'full') {
|
||||||
thumbnail = await sharp(content).webp({ quality: 90 }).toBuffer();
|
thumbnail = await sharp(content).webp({ quality: 90 }).toBuffer();
|
||||||
@ -29,7 +29,7 @@ export async function GET({ params }) {
|
|||||||
return new Response(thumbnail, {
|
return new Response(thumbnail, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'image/webp',
|
'Content-Type': 'image/webp',
|
||||||
'Content-Disposition': 'inline'
|
'Content-Disposition': `inline; filename="${getFileName(params.item)}-thumb-${params.width === 'full' ? 'full' : width}.webp"`,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,20 +0,0 @@
|
|||||||
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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
<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}
|
|
||||||
Reference in New Issue
Block a user