Add free transfer limit to filesystem
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import { formatDataVolume, formatThousands } from "../util/Formatting.svelte"
|
||||
import { set_file, stats } from "./StatsSocket"
|
||||
import { set_file, stats } from "src/util/StatsSocket"
|
||||
|
||||
export let file = {
|
||||
id: "",
|
||||
|
@@ -1,115 +0,0 @@
|
||||
import { readable } from "svelte/store";
|
||||
|
||||
let results = {
|
||||
connected: false,
|
||||
file_stats_init: false,
|
||||
file_stats: {
|
||||
views: 0,
|
||||
downloads: 0,
|
||||
bandwidth: 0,
|
||||
bandwidth_paid: 0,
|
||||
},
|
||||
limits_init: false,
|
||||
limits: {
|
||||
download_limit: 0,
|
||||
download_limit_used: 0,
|
||||
transfer_limit: 0,
|
||||
transfer_limit_used: 0,
|
||||
},
|
||||
}
|
||||
|
||||
export const stats = readable(
|
||||
results,
|
||||
(set) => {
|
||||
start_sock(set)
|
||||
return () => stop_sock(set)
|
||||
},
|
||||
);
|
||||
|
||||
let socket = null
|
||||
const start_sock = (set_func) => {
|
||||
if (socket !== null) {
|
||||
return
|
||||
}
|
||||
|
||||
console.log("initializing stats socket")
|
||||
socket = new WebSocket(location.origin.replace(/^http/, 'ws') + "/api/file_stats")
|
||||
|
||||
socket.onopen = () => {
|
||||
results.connected = true
|
||||
set_func(results)
|
||||
|
||||
// Subscribe to the rate limit feed. This will also process any queued
|
||||
// commands built up while the socket was down
|
||||
send_cmd({ type: "limits" })
|
||||
}
|
||||
socket.onmessage = msg => {
|
||||
let j = JSON.parse(msg.data)
|
||||
console.debug("WS update", j)
|
||||
|
||||
if (j.type === "file_stats") {
|
||||
results.file_stats = j.file_stats
|
||||
results.file_stats_init = true
|
||||
set_func(results)
|
||||
} else if (j.type === "limits") {
|
||||
results.limits = j.limits
|
||||
results.limits_init = true
|
||||
set_func(results)
|
||||
} else {
|
||||
console.error("Unknown ws message type", j.type, "data", msg.data)
|
||||
}
|
||||
}
|
||||
socket.onerror = err => {
|
||||
console.error("socket error", err)
|
||||
stop_sock(set_func)
|
||||
window.setTimeout(() => start_sock(set_func), 2000)
|
||||
}
|
||||
socket.onclose = () => {
|
||||
stop_sock(set_func)
|
||||
window.setTimeout(() => start_sock(set_func), 2000)
|
||||
}
|
||||
}
|
||||
|
||||
const stop_sock = (set_func) => {
|
||||
if (socket === null) {
|
||||
return
|
||||
}
|
||||
|
||||
// Prevent error handlers from re-initializing the socket
|
||||
socket.onerror = null
|
||||
socket.onclose = null
|
||||
|
||||
// Close and delete the socket
|
||||
socket.close()
|
||||
socket = null
|
||||
|
||||
// Reset the state
|
||||
results.connected = false
|
||||
results.file_stats_init = false
|
||||
results.limits_init = false
|
||||
set_func(results)
|
||||
}
|
||||
|
||||
export const set_file = file_id => {
|
||||
send_cmd({
|
||||
type: "file_stats",
|
||||
data: { file_id: file_id },
|
||||
})
|
||||
}
|
||||
|
||||
let queued_commands = []
|
||||
const send_cmd = cmd => {
|
||||
if (socket !== null && socket.readyState === WebSocket.OPEN) {
|
||||
|
||||
// First empty the queue
|
||||
while (queued_commands.length !== 0) {
|
||||
socket.send(JSON.stringify(queued_commands.shift()))
|
||||
}
|
||||
|
||||
// Send the requested command
|
||||
socket.send(JSON.stringify(cmd))
|
||||
} else if (cmd !== null) {
|
||||
queued_commands.push(cmd)
|
||||
console.debug("Socket is closed, command", cmd, "added to queue")
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import { formatDataVolume } from "../util/Formatting.svelte";
|
||||
import { stats } from "./StatsSocket.js"
|
||||
import { stats } from "src/util/StatsSocket.js"
|
||||
|
||||
let percent = 0
|
||||
let title = ""
|
||||
|
@@ -1,8 +1,8 @@
|
||||
<script>
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import IconBlock from "./IconBlock.svelte"
|
||||
import TextBlock from "./TextBlock.svelte"
|
||||
import FileTitle from "./FileTitle.svelte";
|
||||
import IconBlock from "src/layout/IconBlock.svelte";
|
||||
import TextBlock from "src/layout/TextBlock.svelte"
|
||||
import FileTitle from "src/layout/FileTitle.svelte";
|
||||
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { createEventDispatcher, tick } from "svelte";
|
||||
import BandwidthUsage from "./BandwidthUsage.svelte";
|
||||
import FileTitle from "./FileTitle.svelte";
|
||||
import FileTitle from "src/layout/FileTitle.svelte";
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
export let is_list = false
|
||||
|
@@ -1,8 +1,8 @@
|
||||
<script>
|
||||
import { formatDataVolume } from "../../util/Formatting.svelte";
|
||||
import TextBlock from "./TextBlock.svelte";
|
||||
import TextBlock from "src/layout/TextBlock.svelte"
|
||||
import ProgressBar from "../../util/ProgressBar.svelte";
|
||||
import { stats } from "../StatsSocket"
|
||||
import { stats } from "src/util/StatsSocket.js"
|
||||
|
||||
export let file = {
|
||||
size: 0,
|
||||
|
@@ -1,8 +1,8 @@
|
||||
<script>
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import BandwidthUsage from "./BandwidthUsage.svelte";
|
||||
import IconBlock from "./IconBlock.svelte";
|
||||
import FileTitle from "./FileTitle.svelte";
|
||||
import IconBlock from "src/layout/IconBlock.svelte";
|
||||
import FileTitle from "src/layout/FileTitle.svelte";
|
||||
import { formatDataVolume } from "../../util/Formatting.svelte";
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
|
@@ -11,9 +11,9 @@ import Abuse from "./Abuse.svelte";
|
||||
import { file_type } from "../FileUtilities.svelte";
|
||||
import RateLimit from "./RateLimit.svelte";
|
||||
import Torrent from "./Torrent.svelte";
|
||||
import SpeedLimit from "./SpeedLimit.svelte";
|
||||
import { stats } from "../StatsSocket";
|
||||
import { stats } from "src/util/StatsSocket.js"
|
||||
import Zip from "./Zip.svelte";
|
||||
import SlowDown from "src/layout/SlowDown.svelte";
|
||||
|
||||
let viewer
|
||||
let viewer_type = "loading"
|
||||
@@ -66,7 +66,13 @@ export const seek = delta => {
|
||||
{:else if viewer_type === "abuse"}
|
||||
<Abuse bind:this={viewer} on:download></Abuse>
|
||||
{:else if !premium_download && $stats.limits.transfer_limit_used > $stats.limits.transfer_limit}
|
||||
<SpeedLimit file={current_file} on:download></SpeedLimit>
|
||||
<SlowDown
|
||||
on:download
|
||||
file_size={current_file.size}
|
||||
file_name={current_file.name}
|
||||
file_type={current_file.mime_type}
|
||||
icon_href={current_file.icon_href}
|
||||
/>
|
||||
{:else if viewer_type === "rate_limit"}
|
||||
<RateLimit bind:this={viewer} on:download></RateLimit>
|
||||
{:else if viewer_type === "image"}
|
||||
|
@@ -1,12 +0,0 @@
|
||||
<script>
|
||||
export let title = ""
|
||||
</script>
|
||||
|
||||
<h1>{title}</h1>
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
text-shadow: 1px 1px 2px #000000;
|
||||
line-break: anywhere;
|
||||
}
|
||||
</style>
|
@@ -1,44 +0,0 @@
|
||||
<script>
|
||||
export let icon_href = ""
|
||||
export let width = "750px"
|
||||
</script>
|
||||
|
||||
<div class="block" style="width: {width}; max-width: 100%">
|
||||
<img src={icon_href} alt="File icon" class="icon">
|
||||
<div class="description">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.block {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: 8px auto;
|
||||
}
|
||||
@media(max-width: 500px) {
|
||||
.block {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
flex: 0 0 auto;
|
||||
margin-right: 8px;
|
||||
border-radius: 8px;
|
||||
/* Prevent icon from being stretched if text content is too large */
|
||||
align-self: center;
|
||||
width: 128px;
|
||||
}
|
||||
.description {
|
||||
flex: 1 1 auto;
|
||||
display: inline-block;
|
||||
text-align: initial;
|
||||
padding-left: 8px;
|
||||
vertical-align: middle;
|
||||
overflow-wrap: anywhere;
|
||||
background-color: var(--shaded_background);
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
}
|
||||
</style>
|
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import { createEventDispatcher } from "svelte"
|
||||
import { swipe_nav } from "./SwipeNavigate";
|
||||
import { swipe_nav } from "src/util/SwipeNavigate.ts";
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
export const set_file = f => {
|
||||
|
@@ -1,9 +1,9 @@
|
||||
<script>
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { formatDataVolume } from "../../util/Formatting.svelte";
|
||||
import { stats } from "../StatsSocket";
|
||||
import IconBlock from "./IconBlock.svelte";
|
||||
import TextBlock from "./TextBlock.svelte";
|
||||
import { stats } from "src/util/StatsSocket.js"
|
||||
import IconBlock from "src/layout/IconBlock.svelte";
|
||||
import TextBlock from "src/layout/TextBlock.svelte"
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
export const set_file = f => file = f
|
||||
|
@@ -1,73 +0,0 @@
|
||||
<script>
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { formatDataVolume, formatDuration } from "../../util/Formatting.svelte";
|
||||
import { stats } from "../StatsSocket";
|
||||
import IconBlock from "./IconBlock.svelte";
|
||||
import TextBlock from "./TextBlock.svelte";
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
export let file = {
|
||||
name: "",
|
||||
mime_type: "",
|
||||
availability: "",
|
||||
size: 0,
|
||||
download_speed_limit: 0,
|
||||
}
|
||||
</script>
|
||||
|
||||
<TextBlock>
|
||||
<img src="/res/img/slow_down.webp" class="header_image" alt="Yea, I'm gonna need you to slow down a bit"/>
|
||||
<p>
|
||||
Pixeldrain's free tier is supported by my Patrons (be grateful). There's
|
||||
only so much that you can do with the budget they provide.
|
||||
{formatDataVolume($stats.limits.transfer_limit, 3)} per day is about
|
||||
the most I can give away for free while keeping it fair for everyone,
|
||||
and according to our records you have already downloaded
|
||||
{formatDataVolume($stats.limits.transfer_limit_used, 3)}.
|
||||
</p>
|
||||
<p>
|
||||
It's not that I want to withold this file from you, it's just that I
|
||||
don't want pixeldrain to fall into bankruptcy like so many of the
|
||||
websites that came before me. So if you really want this file you have a
|
||||
few options:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
Come back tomorrow when your free transfer limit resets
|
||||
</li>
|
||||
<li>
|
||||
Download the file at a rate of {file.download_speed_limit/(1<<10)}
|
||||
kiB/s. This will take at least
|
||||
{formatDuration((file.size/file.download_speed_limit)*1000)}
|
||||
</li>
|
||||
<li>
|
||||
<a href="/#pro" target="_blank" class="button button_highlight">
|
||||
<i class="icon">bolt</i> Upgrade your account
|
||||
</a>
|
||||
and earn my eternal gratitude
|
||||
{#if !window.user_authenticated}
|
||||
(you will need a <a href="/register">pixeldrain account</a> to
|
||||
receive the benefits)
|
||||
{/if}
|
||||
</li>
|
||||
</ul>
|
||||
</TextBlock>
|
||||
|
||||
<IconBlock icon_href={file.icon_href}>
|
||||
Name: {file.name}<br/>
|
||||
Type: {file.mime_type}<br/>
|
||||
<button on:click={() => {dispatch("download")}}>
|
||||
<i class="icon">download</i> Download
|
||||
</button>
|
||||
</IconBlock>
|
||||
|
||||
<TextBlock>
|
||||
Also, I believe you have my stapler. Please give it back.
|
||||
</TextBlock>
|
||||
|
||||
<style>
|
||||
.header_image {
|
||||
width: 100%;
|
||||
border-radius: 8px;
|
||||
}
|
||||
</style>
|
@@ -1,97 +0,0 @@
|
||||
// Dead zone before the swipe action gets detected
|
||||
const swipe_inital_offset = 25
|
||||
// Amount of pixels after which the navigation triggers
|
||||
const swipe_trigger_offset = 75
|
||||
|
||||
export const swipe_nav = (node: HTMLElement, props: { enabled: boolean, prev: boolean, next: boolean }) => {
|
||||
let start_x = 0
|
||||
let start_y = 0
|
||||
let render_offset = 0
|
||||
let enabled = props.enabled === undefined ? true : props.enabled
|
||||
let prev = props.prev === undefined ? true : props.prev
|
||||
let next = props.next === undefined ? true : props.next
|
||||
|
||||
const touchstart = (e: TouchEvent) => {
|
||||
start_x = e.touches[0].clientX
|
||||
start_y = e.touches[0].clientY
|
||||
render_offset = 0
|
||||
}
|
||||
|
||||
const touchmove = (e: TouchEvent) => {
|
||||
const offset_x = e.touches[0].clientX - start_x
|
||||
if (!enabled || (offset_x < 0 && !next) || (offset_x > 0 && !prev)) {
|
||||
return
|
||||
}
|
||||
|
||||
const abs_x = Math.abs(offset_x)
|
||||
const abs_y = Math.abs(e.touches[0].clientY - start_y)
|
||||
const neg = offset_x < 0 ? -1 : 1
|
||||
|
||||
// The cursor must have moved at least 50 pixels and three times as much
|
||||
// on the x axis than the y axis for it to count as a swipe
|
||||
if (abs_x > swipe_inital_offset && abs_y < abs_x / 3) {
|
||||
set_offset((abs_x - swipe_inital_offset) * neg, false)
|
||||
} else {
|
||||
set_offset(0, true)
|
||||
}
|
||||
}
|
||||
|
||||
const touchend = (e: TouchEvent) => {
|
||||
if (!enabled) {
|
||||
return
|
||||
}
|
||||
|
||||
if (render_offset > swipe_trigger_offset) {
|
||||
set_offset(1000, true)
|
||||
node.dispatchEvent(new CustomEvent("prev"))
|
||||
} else if (render_offset < -swipe_trigger_offset) {
|
||||
set_offset(-1000, true)
|
||||
node.dispatchEvent(new CustomEvent("next"))
|
||||
} else {
|
||||
set_offset(0, true)
|
||||
}
|
||||
}
|
||||
|
||||
const set_offset = (off: number, animate: boolean) => {
|
||||
render_offset = off
|
||||
|
||||
if (off === 0) {
|
||||
// Clear the transformation if the offset is zero
|
||||
node.style.transform = ""
|
||||
node.style.transition = ""
|
||||
} else {
|
||||
node.style.transform = "translateX(" + off + "px)"
|
||||
if (animate) {
|
||||
node.style.transition = "transform 400ms"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node.addEventListener("touchstart", touchstart)
|
||||
node.addEventListener("touchmove", touchmove)
|
||||
node.addEventListener("touchend", touchend)
|
||||
|
||||
// Get the child image so we can listen for the loaded event. When the
|
||||
// loaded event fires we clear the transformations so that the image appears
|
||||
// in the original position again
|
||||
for (let i = 0; i < node.childNodes.length; i++) {
|
||||
const child = node.childNodes.item(i)
|
||||
if (child instanceof HTMLImageElement) {
|
||||
child.addEventListener("load", () => set_offset(0, false))
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
update(props: { enabled: boolean, prev: boolean, next: boolean }) {
|
||||
enabled = props.enabled === undefined ? true : props.enabled
|
||||
prev = props.prev === undefined ? true : props.prev
|
||||
next = props.next === undefined ? true : props.next
|
||||
set_offset(0, false)
|
||||
},
|
||||
destroy() {
|
||||
node.removeEventListener("touchstart", touchstart)
|
||||
node.removeEventListener("touchmove", touchmove)
|
||||
node.removeEventListener("touchend", touchend)
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
<script>
|
||||
export let width = "750px"
|
||||
export let center = false
|
||||
</script>
|
||||
|
||||
<div class="block" class:center style="width: {width};">
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.block {
|
||||
display: block;
|
||||
text-align: initial;
|
||||
max-width: 99%;
|
||||
overflow-wrap: anywhere;
|
||||
margin: 8px auto;
|
||||
|
||||
background-color: var(--shaded_background);
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
@@ -1,12 +1,12 @@
|
||||
<script>
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import Magnet from "../../icons/Magnet.svelte";
|
||||
import { formatDate } from "../../util/Formatting.svelte"
|
||||
import IconBlock from "./IconBlock.svelte";
|
||||
import TextBlock from "./TextBlock.svelte";
|
||||
import Magnet from "src/icons/Magnet.svelte";
|
||||
import { formatDate } from "src/util/Formatting.svelte"
|
||||
import IconBlock from "src/layout/IconBlock.svelte";
|
||||
import TextBlock from "src/layout/TextBlock.svelte";
|
||||
import TorrentItem from "./TorrentItem.svelte"
|
||||
import FileTitle from "./FileTitle.svelte";
|
||||
import CopyButton from "../../layout/CopyButton.svelte";
|
||||
import FileTitle from "src/layout/FileTitle.svelte";
|
||||
import CopyButton from "src/layout/CopyButton.svelte";
|
||||
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { onMount, createEventDispatcher, tick } from "svelte";
|
||||
import BandwidthUsage from "./BandwidthUsage.svelte";
|
||||
import IconBlock from "./IconBlock.svelte";
|
||||
import IconBlock from "src/layout/IconBlock.svelte";
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
export let is_list = false
|
||||
|
@@ -1,11 +1,11 @@
|
||||
<script>
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { formatDataVolume, formatDate } from "../../util/Formatting.svelte"
|
||||
import IconBlock from "./IconBlock.svelte";
|
||||
import TextBlock from "./TextBlock.svelte";
|
||||
import IconBlock from "src/layout/IconBlock.svelte";
|
||||
import TextBlock from "src/layout/TextBlock.svelte"
|
||||
import ZipItem from "./ZipItem.svelte";
|
||||
import BandwidthUsage from "./BandwidthUsage.svelte";
|
||||
import FileTitle from "./FileTitle.svelte";
|
||||
import FileTitle from "src/layout/FileTitle.svelte";
|
||||
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
|
Reference in New Issue
Block a user