Implement most of the file viewer in svelte

This commit is contained in:
2021-10-26 22:15:01 +02:00
parent a88d83ae30
commit 2ae2993adf
25 changed files with 1700 additions and 136 deletions

View File

@@ -0,0 +1,29 @@
<script>
export let file = {
id: "",
name: "",
abuse_type: "",
abuse_reporter_name: "",
}
</script>
<div class="container">
<h1>Unavailable for legal reasons</h1>
<p>
This file has received an abuse report and was taken down.
</p>
<p>
Type of abuse: {file.abuse_type}. Reporter: {file.abuse_reporter_name}.
</p>
</div>
<style>
.container {
position: relative;
display: block;
height: 100%;
width: 100%;
text-align: center;
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,106 @@
<script>
import { onMount, createEventDispatcher, tick } from "svelte";
let dispatch = createEventDispatcher()
export let file = {
id: "",
name: "",
mime_type: "",
get_href: "",
}
$: loop = file.name.includes(".loop.")
let player
let playing = false
let audio_reload = false
let media_session = false
$: update_file(file.id)
const update_file = async () => {
if (media_session) {
navigator.mediaSession.metadata = new MediaMetadata({
title: file.name,
artist: "pixeldrain",
album: "unknown",
});
console.log("updating media session")
}
// When the component receives a new ID the video track does not automatically
// start playing the new video. So we use this little hack to make sure that the
// video is unloaded and loaded when the ID changes
audio_reload = true
await tick()
audio_reload = false
}
const toggle_play = () => playing ? player.pause() : player.play()
onMount(() => {
if ('mediaSession' in navigator) {
media_session = true
navigator.mediaSession.setActionHandler('play', () => player.play());
navigator.mediaSession.setActionHandler('pause', () => player.pause());
navigator.mediaSession.setActionHandler('stop', () => player.stop());
navigator.mediaSession.setActionHandler('previoustrack', () => dispatch("prev", {}));
navigator.mediaSession.setActionHandler('nexttrack', () => dispatch("next", {}));
update_file()
}
})
</script>
<div class="container">
<button on:click={() => dispatch("prev") }>
<i class="icon">skip_previous</i>
</button>
<button on:click={() => player.currentTime -= 10 }>
<i class="icon">replay_10</i>
</button>
<button on:click={toggle_play}>
{#if playing}
<i class="icon">pause</i>
{:else}
<i class="icon">play_arrow</i>
{/if}
</button>
<button on:click={() => player.currentTime += 10 }>
<i class="icon">forward_10</i>
</button>
<button on:click={() => dispatch("next") }>
<i class="icon">skip_next</i>
</button>
<br/><br/>
{#if !audio_reload}
<!-- svelte-ignore a11y-media-has-caption -->
<audio
bind:this={player}
class="player"
controls
playsinline
autoplay
loop={loop}
on:pause={() => playing = false }
on:play={() => playing = true }
on:ended={() => {dispatch("next", {})}}
>
<source src={file.get_href} type={file.mime_type} />
</audio>
{/if}
</div>
<style>
.container {
height: 100%;
width: 100%;
margin: 50px 0 0 0;
padding: 0;
overflow-y: auto;
text-align: center;
}
.player {
width: 90%;
}
</style>

View File

@@ -0,0 +1,47 @@
<script>
import { createEventDispatcher } from "svelte";
let dispatch = createEventDispatcher()
export let file = {
id: "",
name: "",
mime_type: "",
icon_href: "",
}
</script>
<div class="container">
<h1>You are viewing a file on pixeldrain</h1>
<img src={file.icon_href} alt="File icon" class="icon">
<div class="description">
Name: {file.name}<br/>
Type: {file.mime_type}<br/>
No preview is available for this file type. Download to view it locally.
<br/>
<button class="button_highlight" on:click={() => {dispatch("download")}}>
<i class="icon">save</i> Download
</button>
</div>
</div>
<style>
.icon {
display: inline-block;
vertical-align: middle;
}
.description {
display: inline-block;
text-align: left;
padding-left: 8px;
vertical-align: middle;
max-width: 600px;
}
.container {
position: relative;
display: block;
height: 100%;
width: 100%;
text-align: center;
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,91 @@
<script>
export let file = {
id: "",
name: "",
mime_type: "",
get_href: "",
}
let container
let zoom = false
let x, y = 0
let dragging = false
const mousedown = (e) => {
if (!dragging && e.which === 1 && zoom) {
x = e.pageX
y = e.pageY
dragging = true
e.preventDefault()
e.stopPropagation()
return false
}
}
const mousemove = (e) => {
if (dragging) {
container.scrollLeft = container.scrollLeft - (e.pageX - x)
container.scrollTop = container.scrollTop - (e.pageY - y)
x = e.pageX
y = e.pageY
e.preventDefault()
e.stopPropagation()
return false
}
}
const mouseup = (e) => {
if (dragging) {
dragging = false
e.preventDefault()
e.stopPropagation()
return false
}
}
</script>
<svelte:window on:mousemove={mousemove} on:mouseup={mouseup} />
<div bind:this={container} class="container" class:zoom>
<img
on:dblclick={() => {zoom = !zoom}}
on:doubletap={() => {zoom = !zoom}}
on:mousedown={mousedown}
class="image" class:zoom
src={file.get_href}
alt={file.name} />
</div>
<style>
.container {
position: relative;
display: block;
height: 100%;
width: 100%;
text-align: center;
overflow: hidden;
}
.container.zoom {
overflow: auto;
}
.image {
position: relative;
display: block;
margin: auto;
max-width: 100%;
max-height: 100%;
top: 50%;
cursor: pointer;
transform: translateY(-50%);
box-shadow: 1px 1px 5px var(--shadow_color);
}
.image.zoom {
max-width: none;
max-height: none;
top: 0;
cursor: move;
transform: none;
}
</style>

View File

@@ -0,0 +1,23 @@
<script>
export let file = {
get_href: "",
}
</script>
<iframe
class="container"
src={"/res/misc/pdf-viewer/web/viewer.html?file="+encodeURIComponent(file.get_href)}
title="PDF viewer">
</iframe>
<style>
.container {
position: relative;
display: block;
height: 100%;
width: 100%;
text-align: center;
overflow: hidden;
border: none;
}
</style>

View File

@@ -0,0 +1,126 @@
<script>
import { onMount } from "svelte";
export let file = {
id: "",
name: "",
mime_type: "",
size: 0,
get_href: "",
}
let container
let text_type = ""
$: update_file(file.id)
const update_file = async () => {
if (file.name.endsWith(".md") || file.name.endsWith(".markdown") || file.mime_type === "text/demo") {
markdown()
} else if (file.name.endsWith(".txt")) {
text()
} else {
code()
}
}
onMount(update_file)
let md_container
const markdown = () => {
text_type = "markdown"
fetch("/u/" + file.id + "/preview").then(resp => {
if (!resp.ok) { return Promise.reject(resp.status) }
return resp.text()
}).then(resp => {
md_container.innerHTML = resp
}).catch(err => {
md_container.innerText = "Error loading file: " + err
})
}
let text_pre
const text = () => {
text_type = "text"
if (file.size > 1 << 20) { // File larger than 1 MiB
text_pre.innerText = "File is too large to view online.\nPlease download and view it locally."
return
}
fetch(file.get_href).then(resp => {
if (!resp.ok) { return Promise.reject(resp.status) }
return resp.text()
}).then(resp => {
text_pre.innerText = resp
}).catch(err => {
text_pre.innerText = "Error loading file: " + err
})
}
let code_pre
let prettyprint = false
const code = () => {
text_type = "code"
if (file.size > 1 << 20) { // File larger than 1 MiB
code_pre.innerText = "File is too large to view online.\nPlease download and view it locally."
return
}
fetch(file.get_href).then(resp => {
if (!resp.ok) { return Promise.reject(resp.status) }
return resp.text()
}).then(resp => {
code_pre.innerText = resp
// Load prettyprint script
if (!prettyprint) {
let prettyprint = document.createElement("script")
prettyprint.src = "https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_prettify.js?skin=desert"
container.appendChild(prettyprint)
prettyprint = true
} else {
PR.prettyPrint()
}
}).catch(err => {
code_pre.innerText = "Error loading file: " + err
})
}
</script>
<div bind:this={container} class="container">
{#if text_type === "markdown"}
<div bind:this={md_container}>
Loading...
</div>
{:else if text_type === "text"}
<pre bind:this={text_pre}>
Loading...
</pre>
{:else if text_type === "code"}
<pre bind:this={code_pre} class="pre-container prettyprint linenums">
Loading...
</pre>
{/if}
</div>
<style>
.container {
background: #333 none;
position: relative;
text-align: left;
height: 100%;
width: 100%;
font-size: 0.9em;
line-height: 1.5em;
padding: 5px 5px 5px 20px;
box-sizing: border-box;
overflow-y: scroll;
overflow-x: hidden;
}
.container > pre {
white-space: pre-wrap;
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,106 @@
<script>
import { onMount, createEventDispatcher, tick } from "svelte";
let dispatch = createEventDispatcher()
export let file = {
id: "",
name: "",
mime_type: "",
get_href: "",
icon_href: "",
allow_video_player: true,
}
$: loop = file.name.includes(".loop.")
let player
let video_reload = false
let media_session = false
$: update_file(file.id)
const update_file = async () => {
if (media_session) {
navigator.mediaSession.metadata = new MediaMetadata({
title: file.name,
artist: "pixeldrain",
album: "unknown",
});
console.log("updating media session")
}
// When the component receives a new ID the video track does not automatically
// start playing the new video. So we use this little hack to make sure that the
// video is unloaded and loaded when the ID changes
video_reload = true
await tick()
video_reload = false
}
onMount(() => {
if ('mediaSession' in navigator) {
media_session = true
navigator.mediaSession.setActionHandler('play', () => player.play());
navigator.mediaSession.setActionHandler('pause', () => player.pause());
navigator.mediaSession.setActionHandler('stop', () => player.stop());
navigator.mediaSession.setActionHandler('previoustrack', () => dispatch("prev", {}));
navigator.mediaSession.setActionHandler('nexttrack', () => dispatch("next", {}));
update_file()
}
})
let download = () => { dispatch("download", {}) }
</script>
<div class="container">
{#if file.allow_video_player}
{#if !video_reload}
<!-- svelte-ignore a11y-media-has-caption -->
<video
bind:this={player}
controls
playsinline
autoplay
loop={loop}
class="center drop_shadow"
on:ended={() => {dispatch("next", {})}}
>
<source src={file.get_href} type={file.mime_type} />
</video>
{/if}
{:else}
<h1>This is a video file on pixeldrain</h1>
<img src={file.icon_href} alt="Video icon" style="display: inline-block; vertical-align: middle;">
<div style="display: inline-block; text-align: left; padding-left: 8px; vertical-align: middle; max-width: 600px;">
The online video player on pixeldrain has been disabled due to
repeated abuse. You can still watch videos online by upgrading to
Pro. Or download the video and watch it locally on your computer.
<br/>
<a href="https://www.patreon.com/join/pixeldrain/checkout?rid=5291427&cadence=12" class="button button_highlight">
<i class="icon">upgrade</i> Upgrade to Pro
</a>
<button on:click={download}>
<i class="icon">save</i> Download
</button>
</div>
{/if}
</div>
<style>
.container{
position: relative;
display: block;
height: 100%;
width: 100%;
text-align: center;
overflow: hidden;
}
.center {
position: relative;
display: block;
margin: auto;
max-width: 100%;
max-height: 100%;
top: 50%;
transform: translateY(-50%);
}
</style>