layout refactoring
This commit is contained in:
@@ -1,63 +1,16 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { SvelteToast } from '@zerodevx/svelte-toast';
|
import { SvelteToast } from '@zerodevx/svelte-toast';
|
||||||
import * as O from 'fp-ts/Option';
|
|
||||||
import { pipe } from 'fp-ts/lib/function';
|
|
||||||
import { onDestroy } from 'svelte';
|
|
||||||
import DownloadCard from './lib/DownloadCard.svelte';
|
|
||||||
import Footer from './lib/Footer.svelte';
|
import Footer from './lib/Footer.svelte';
|
||||||
|
import Home from './lib/Home.svelte';
|
||||||
import Navbar from './lib/Navbar.svelte';
|
import Navbar from './lib/Navbar.svelte';
|
||||||
import Spinner from './lib/Spinner.svelte';
|
|
||||||
import { downloads, rpcClient } from './lib/store';
|
|
||||||
import { datetimeCompareFunc, isRPCResponse } from './lib/utils';
|
|
||||||
|
|
||||||
const unsubscribe = rpcClient.subscribe(($client) => {
|
|
||||||
setInterval(() => $client.running(), 750);
|
|
||||||
|
|
||||||
$client.socket.onmessage = (ev: any) => {
|
|
||||||
const event = JSON.parse(ev.data);
|
|
||||||
// guards
|
|
||||||
if (!isRPCResponse(event)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!Array.isArray(event.result)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.result) {
|
|
||||||
return downloads.set(
|
|
||||||
O.of(
|
|
||||||
event.result
|
|
||||||
.filter((f) => !!f.info.url)
|
|
||||||
.sort((a, b) =>
|
|
||||||
datetimeCompareFunc(b.info.created_at, a.info.created_at),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
downloads.set(O.none);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
onDestroy(unsubscribe);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main
|
<main
|
||||||
class="bg-neutral-50 dark:bg-neutral-900 h-screen text-neutral-950 dark:text-neutral-50"
|
class="bg-neutral-50 dark:bg-neutral-900 h-screen text-neutral-950 dark:text-neutral-50"
|
||||||
>
|
>
|
||||||
<Navbar />
|
<Navbar />
|
||||||
{#if O.isNone($downloads)}
|
<Home />
|
||||||
<div class="h-[90vh] w-full flex justify-center items-center">
|
|
||||||
<Spinner />
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-2 p-8">
|
|
||||||
{#each pipe( $downloads, O.getOrElseW(() => []), ) as download}
|
|
||||||
<DownloadCard {download} />
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
<!-- <FloatingAction /> -->
|
|
||||||
<Footer />
|
<Footer />
|
||||||
<SvelteToast />
|
<SvelteToast />
|
||||||
|
<!-- <FloatingAction /> -->
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
border-t dark:border-t-neutral-700
|
border-t dark:border-t-neutral-700
|
||||||
shadow-lg
|
shadow-lg
|
||||||
rounded-t-xl"
|
rounded-t-xl"
|
||||||
style="height: {$height}px;"
|
style="min-height: {$height}px;"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="p-1 bg-neutral-200 dark:bg-neutral-700 rounded-lg border dark:border-neutral-700"
|
class="p-1 bg-neutral-200 dark:bg-neutral-700 rounded-lg border dark:border-neutral-700"
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
{#if $height > 100}
|
{#if $height > 100}
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<NewDownload />
|
<NewDownload />
|
||||||
<!-- <Settings /> -->
|
<Settings />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -3,48 +3,56 @@
|
|||||||
|
|
||||||
let group = '';
|
let group = '';
|
||||||
export let formats: DLFormat[];
|
export let formats: DLFormat[];
|
||||||
|
|
||||||
|
$: console.log(group);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex w-full flex-col items-center justify-center">
|
<div class="w-full mt-4">
|
||||||
<div class="w-full px-4 py-16">
|
<div class="mx-auto w-full">
|
||||||
<div class="mx-auto w-full max-w-md">
|
<fieldset class="grid grid-cols-7 gap-2">
|
||||||
<fieldset class="flex flex-col space-y-2">
|
|
||||||
{#each formats as format}
|
{#each formats as format}
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<input
|
<input
|
||||||
id="startup"
|
id="formats"
|
||||||
class="absolute opacity-0 w-0 h-0 peer"
|
class="absolute opacity-0 w-0 h-0 peer"
|
||||||
type="radio"
|
type="radio"
|
||||||
bind:group
|
bind:group
|
||||||
name="type"
|
name="type"
|
||||||
value="startup"
|
value="formats"
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
for="startup"
|
for="formats"
|
||||||
class="[&_p]:text-gray-900 [&_span]:text-gray-500 peer-checked:[&_p]:text-white peer-checked:[&_span]:text-sky-100 peer-focus:ring-2 peer-focus:ring-white peer-focus:ring-opacity-60 peer-focus:ring-offset-2 peer-focus:ring-offset-sky-300 bg-white relative flex cursor-pointer rounded-lg px-5 py-4 shadow-md focus:outline-none peer-checked:bg-sky-900/75 peer-checked:text-white"
|
class="
|
||||||
|
[&_p]:text-gray-900 [&_span]:text-gray-500
|
||||||
|
peer-checked:[&_p]:text-white peer-checked:[&_span]:text-orange-100
|
||||||
|
peer-focus:ring-2 peer-focus:ring-white
|
||||||
|
peer-focus:ring-opacity-60 peer-focus:ring-offset-2 peer-focus:ring-offset-orange-300
|
||||||
|
bg-white
|
||||||
|
relative flex
|
||||||
|
cursor-pointer
|
||||||
|
rounded-lg px-5 py-4
|
||||||
|
shadow-md
|
||||||
|
focus:outline-none
|
||||||
|
peer-checked:bg-orange-700/75
|
||||||
|
peer-checked:text-white"
|
||||||
>
|
>
|
||||||
<div class="flex w-full items-center justify-between">
|
<div class="flex w-full items-center justify-between">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="text-sm">
|
<div class="text-sm">
|
||||||
<p class="font-medium" id="headlessui-label-:R5mm:">
|
<p class="font-medium" id={format.format_id}>
|
||||||
{format.resolution}
|
{format.resolution}
|
||||||
</p>
|
</p>
|
||||||
<span class="inline" id="headlessui-description-:R9mm:">
|
<span class="inline" id={format.format_id}>
|
||||||
<span>{format.vcodec}</span>
|
<span>{format.vcodec}</span>
|
||||||
<span aria-hidden="true">·</span>
|
<span aria-hidden="true">·</span>
|
||||||
<span>{format.vcodec}</span></span
|
<span>{format.acodec}</span>
|
||||||
>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="shrink-0 text-white">
|
<div class="shrink-0 text-white">
|
||||||
<svg viewBox="0 0 24 24" fill="none" class="h-6 w-6">
|
<svg viewBox="0 0 24 24" fill="none" class="h-6 w-6">
|
||||||
<circle
|
<circle cx="12" cy="12" r="12" fill="#fff" opacity="0.2" />
|
||||||
cx="12"
|
<path
|
||||||
cy="12"
|
|
||||||
r="12"
|
|
||||||
fill="#fff"
|
|
||||||
opacity="0.2"
|
|
||||||
/><path
|
|
||||||
d="M7 13l3 3 7-7"
|
d="M7 13l3 3 7-7"
|
||||||
stroke="#fff"
|
stroke="#fff"
|
||||||
stroke-width="1.5"
|
stroke-width="1.5"
|
||||||
@@ -60,4 +68,3 @@
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|||||||
9
ui/src/lib/FullscreenSpinner.svelte
Normal file
9
ui/src/lib/FullscreenSpinner.svelte
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Spinner from './Spinner.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="top-0 left-0 absolute w-full h-full bg-neutral-950/20 flex items-center justify-center z-50"
|
||||||
|
>
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
52
ui/src/lib/Home.svelte
Normal file
52
ui/src/lib/Home.svelte
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import * as O from 'fp-ts/Option';
|
||||||
|
import { pipe } from 'fp-ts/lib/function';
|
||||||
|
import { onDestroy } from 'svelte';
|
||||||
|
import DownloadCard from './DownloadCard.svelte';
|
||||||
|
import Spinner from './Spinner.svelte';
|
||||||
|
import { downloads, rpcClient } from './store';
|
||||||
|
import { datetimeCompareFunc, isRPCResponse } from './utils';
|
||||||
|
|
||||||
|
const unsubscribe = rpcClient.subscribe(($client) => {
|
||||||
|
setInterval(() => $client.running(), 750);
|
||||||
|
|
||||||
|
$client.socket.onmessage = (ev: any) => {
|
||||||
|
const event = JSON.parse(ev.data);
|
||||||
|
// guards
|
||||||
|
if (!isRPCResponse(event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!Array.isArray(event.result)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.result) {
|
||||||
|
return downloads.set(
|
||||||
|
O.of(
|
||||||
|
event.result
|
||||||
|
.filter((f) => !!f.info.url)
|
||||||
|
.sort((a, b) =>
|
||||||
|
datetimeCompareFunc(b.info.created_at, a.info.created_at),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
downloads.set(O.none);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
onDestroy(unsubscribe);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if O.isNone($downloads)}
|
||||||
|
<div class="h-[90vh] w-full flex justify-center items-center">
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="grid grid-cols-1 xl:grid-cols-2 gap-2 p-8">
|
||||||
|
{#each pipe( $downloads, O.getOrElseW(() => []), ) as download}
|
||||||
|
<DownloadCard {download} />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ArrowDownUp, HardDrive, Network } from 'lucide-svelte';
|
import { ArrowDownUp, Github, HardDrive, Network } from 'lucide-svelte';
|
||||||
import { downloads, rpcClient, serverApiEndpoint } from './store';
|
import { downloads, rpcClient, serverApiEndpoint } from './store';
|
||||||
import { formatGiB, formatSpeedMiB } from './utils';
|
import { formatGiB, formatSpeedMiB } from './utils';
|
||||||
import * as O from 'fp-ts/Option';
|
import * as O from 'fp-ts/Option';
|
||||||
@@ -70,6 +70,13 @@
|
|||||||
{$serverApiEndpoint.split('//')[1]}
|
{$serverApiEndpoint.split('//')[1]}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<a
|
||||||
|
href="https://github.com/marcopeocchi/yt-dlp-web-ui"
|
||||||
|
class="flex items-center gap-1.5 p-1 text-neutral-900 bg-orange-200 rounded-lg"
|
||||||
|
>
|
||||||
|
<Github size={18} />
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
<div class="w-full px-8">
|
<div class="w-full px-8">
|
||||||
<div class="my-4 font-semibold text-xl">New download</div>
|
<div class="my-4 font-semibold text-xl">New download</div>
|
||||||
<div class="grid grid-cols-2 gap-2 w-full mb-2">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-2 w-full mb-2">
|
||||||
<TextField placeholder="https://..." label="URL" bind:value={url} />
|
<TextField placeholder="https://..." label="URL" bind:value={url} />
|
||||||
<TextField
|
<TextField
|
||||||
placeholder="arguments separated by space"
|
placeholder="arguments separated by space"
|
||||||
|
|||||||
@@ -7,7 +7,14 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<select
|
<select
|
||||||
class="p-2 bg-neutral-50 border rounded-lg appearance-none text-sm font-semibold"
|
class="
|
||||||
|
p-2
|
||||||
|
bg-neutral-50
|
||||||
|
border rounded-lg
|
||||||
|
appearance-none
|
||||||
|
text-sm font-semibold
|
||||||
|
focus:outline-orange-300
|
||||||
|
"
|
||||||
bind:value
|
bind:value
|
||||||
{disabled}
|
{disabled}
|
||||||
{placeholder}
|
{placeholder}
|
||||||
|
|||||||
@@ -1,13 +1,34 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import CookiesTextField from './CookiesTextField.svelte';
|
import { get } from 'svelte/store';
|
||||||
import { rpcHost, rpcPort } from './store';
|
import Button from './Button.svelte';
|
||||||
|
import TextField from './TextField.svelte';
|
||||||
|
import { rpcClient, rpcHost, rpcPort } from './store';
|
||||||
|
import FullscreenSpinner from './FullscreenSpinner.svelte';
|
||||||
|
|
||||||
|
let loading: Promise<any>;
|
||||||
|
|
||||||
|
const update = () => (loading = get(rpcClient).updateExecutable());
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div>
|
<div class="w-full px-8 mt-8">
|
||||||
<div class="font-semibold text-lg">Settings</div>
|
<div class="font-semibold text-lg mb-4">Settings</div>
|
||||||
|
|
||||||
<input type="text" bind:value={$rpcHost} />
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||||
<input type="text" bind:value={$rpcPort} />
|
<TextField
|
||||||
|
label="Server address"
|
||||||
<CookiesTextField />
|
bind:value={$rpcHost}
|
||||||
|
placeholder="localhost"
|
||||||
|
/>
|
||||||
|
<TextField label="Server port" bind:value={$rpcPort} placeholder="3033" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button class="mt-4" on:click={update}>Update yt-dlp</Button>
|
||||||
|
|
||||||
|
{#if loading}
|
||||||
|
{#await loading}
|
||||||
|
<FullscreenSpinner />
|
||||||
|
{/await}
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<!-- <CookiesTextField /> -->
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<label for=""> {label} </label>
|
<label for=""> {label} </label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class={`p-2 bg-neutral-50 border rounded-lg ${clazz}`}
|
class={`p-2 bg-neutral-50 border rounded-lg focus:outline-orange-300 ${clazz}`}
|
||||||
on:keyup
|
on:keyup
|
||||||
bind:value
|
bind:value
|
||||||
{placeholder}
|
{placeholder}
|
||||||
|
|||||||
Reference in New Issue
Block a user