Add free transfer limit to filesystem

This commit is contained in:
2024-09-05 17:28:31 +02:00
parent 04efcb6505
commit 51afa2615c
30 changed files with 125 additions and 64 deletions

View File

@@ -11,6 +11,7 @@
"behave-js": "^1.5.0", "behave-js": "^1.5.0",
"chart.js": "^4.2.0", "chart.js": "^4.2.0",
"pure-color": "^1.3.0", "pure-color": "^1.3.0",
"rollup-plugin-includepaths": "^0.2.4",
"svelte-preprocess": "^6.0.2", "svelte-preprocess": "^6.0.2",
"tslib": "^2.7.0" "tslib": "^2.7.0"
}, },
@@ -3460,6 +3461,12 @@
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
"node_modules/rollup-plugin-includepaths": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/rollup-plugin-includepaths/-/rollup-plugin-includepaths-0.2.4.tgz",
"integrity": "sha512-iZen+XKVExeCzk7jeSZPJKL7B67slZNr8GXSC5ROBXtDGXDBH8wdjMfdNW5hf9kPt+tHyIvWh3wlE9bPrZL24g==",
"license": "MIT"
},
"node_modules/rollup-plugin-livereload": { "node_modules/rollup-plugin-livereload": {
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://registry.npmjs.org/rollup-plugin-livereload/-/rollup-plugin-livereload-2.0.5.tgz", "resolved": "https://registry.npmjs.org/rollup-plugin-livereload/-/rollup-plugin-livereload-2.0.5.tgz",

View File

@@ -24,6 +24,7 @@
"behave-js": "^1.5.0", "behave-js": "^1.5.0",
"chart.js": "^4.2.0", "chart.js": "^4.2.0",
"pure-color": "^1.3.0", "pure-color": "^1.3.0",
"rollup-plugin-includepaths": "^0.2.4",
"svelte-preprocess": "^6.0.2", "svelte-preprocess": "^6.0.2",
"tslib": "^2.7.0" "tslib": "^2.7.0"
} }

View File

@@ -5,6 +5,7 @@ import livereload from 'rollup-plugin-livereload';
import terser from '@rollup/plugin-terser'; import terser from '@rollup/plugin-terser';
import babel from '@rollup/plugin-babel' import babel from '@rollup/plugin-babel'
import typescript from '@rollup/plugin-typescript' import typescript from '@rollup/plugin-typescript'
import includePaths from 'rollup-plugin-includepaths';
import { sveltePreprocess } from 'svelte-preprocess'; import { sveltePreprocess } from 'svelte-preprocess';
const production = !process.env.ROLLUP_WATCH; const production = !process.env.ROLLUP_WATCH;
@@ -39,7 +40,7 @@ export default [
}), }),
babel({ babel({
extensions: [".js", ".html", ".svelte"], extensions: [".js", ".ts", ".html", ".svelte"],
babelHelpers: "bundled", babelHelpers: "bundled",
}), }),
@@ -59,6 +60,9 @@ export default [
compilerOptions: { lib: ["es2015", "dom"] }, compilerOptions: { lib: ["es2015", "dom"] },
verbatimModuleSyntax: true, verbatimModuleSyntax: true,
}), }),
includePaths({
paths: ["."],
}),
// In dev mode, call `npm run start` once // In dev mode, call `npm run start` once
// the bundle has been generated // the bundle has been generated

View File

@@ -1,6 +1,6 @@
<script> <script>
import { formatDataVolume, formatThousands } from "../util/Formatting.svelte" import { formatDataVolume, formatThousands } from "../util/Formatting.svelte"
import { set_file, stats } from "./StatsSocket" import { set_file, stats } from "src/util/StatsSocket"
export let file = { export let file = {
id: "", id: "",

View File

@@ -1,6 +1,6 @@
<script> <script>
import { formatDataVolume } from "../util/Formatting.svelte"; import { formatDataVolume } from "../util/Formatting.svelte";
import { stats } from "./StatsSocket.js" import { stats } from "src/util/StatsSocket.js"
let percent = 0 let percent = 0
let title = "" let title = ""

View File

@@ -1,8 +1,8 @@
<script> <script>
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import IconBlock from "./IconBlock.svelte" import IconBlock from "src/layout/IconBlock.svelte";
import TextBlock from "./TextBlock.svelte" import TextBlock from "src/layout/TextBlock.svelte"
import FileTitle from "./FileTitle.svelte"; import FileTitle from "src/layout/FileTitle.svelte";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()

View File

@@ -1,7 +1,7 @@
<script> <script>
import { createEventDispatcher, tick } from "svelte"; import { createEventDispatcher, tick } from "svelte";
import BandwidthUsage from "./BandwidthUsage.svelte"; import BandwidthUsage from "./BandwidthUsage.svelte";
import FileTitle from "./FileTitle.svelte"; import FileTitle from "src/layout/FileTitle.svelte";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()
export let is_list = false export let is_list = false

View File

@@ -1,8 +1,8 @@
<script> <script>
import { formatDataVolume } from "../../util/Formatting.svelte"; 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 ProgressBar from "../../util/ProgressBar.svelte";
import { stats } from "../StatsSocket" import { stats } from "src/util/StatsSocket.js"
export let file = { export let file = {
size: 0, size: 0,

View File

@@ -1,8 +1,8 @@
<script> <script>
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import BandwidthUsage from "./BandwidthUsage.svelte"; import BandwidthUsage from "./BandwidthUsage.svelte";
import IconBlock from "./IconBlock.svelte"; import IconBlock from "src/layout/IconBlock.svelte";
import FileTitle from "./FileTitle.svelte"; import FileTitle from "src/layout/FileTitle.svelte";
import { formatDataVolume } from "../../util/Formatting.svelte"; import { formatDataVolume } from "../../util/Formatting.svelte";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()

View File

@@ -11,9 +11,9 @@ import Abuse from "./Abuse.svelte";
import { file_type } from "../FileUtilities.svelte"; import { file_type } from "../FileUtilities.svelte";
import RateLimit from "./RateLimit.svelte"; import RateLimit from "./RateLimit.svelte";
import Torrent from "./Torrent.svelte"; import Torrent from "./Torrent.svelte";
import SpeedLimit from "./SpeedLimit.svelte"; import { stats } from "src/util/StatsSocket.js"
import { stats } from "../StatsSocket";
import Zip from "./Zip.svelte"; import Zip from "./Zip.svelte";
import SlowDown from "src/layout/SlowDown.svelte";
let viewer let viewer
let viewer_type = "loading" let viewer_type = "loading"
@@ -66,7 +66,13 @@ export const seek = delta => {
{:else if viewer_type === "abuse"} {:else if viewer_type === "abuse"}
<Abuse bind:this={viewer} on:download></Abuse> <Abuse bind:this={viewer} on:download></Abuse>
{:else if !premium_download && $stats.limits.transfer_limit_used > $stats.limits.transfer_limit} {: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"} {:else if viewer_type === "rate_limit"}
<RateLimit bind:this={viewer} on:download></RateLimit> <RateLimit bind:this={viewer} on:download></RateLimit>
{:else if viewer_type === "image"} {:else if viewer_type === "image"}

View File

@@ -1,6 +1,6 @@
<script> <script>
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
import { swipe_nav } from "./SwipeNavigate"; import { swipe_nav } from "src/util/SwipeNavigate.ts";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()
export const set_file = f => { export const set_file = f => {

View File

@@ -1,9 +1,9 @@
<script> <script>
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import { formatDataVolume } from "../../util/Formatting.svelte"; import { formatDataVolume } from "../../util/Formatting.svelte";
import { stats } from "../StatsSocket"; import { stats } from "src/util/StatsSocket.js"
import IconBlock from "./IconBlock.svelte"; import IconBlock from "src/layout/IconBlock.svelte";
import TextBlock from "./TextBlock.svelte"; import TextBlock from "src/layout/TextBlock.svelte"
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()
export const set_file = f => file = f export const set_file = f => file = f

View File

@@ -1,12 +1,12 @@
<script> <script>
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import Magnet from "../../icons/Magnet.svelte"; import Magnet from "src/icons/Magnet.svelte";
import { formatDate } from "../../util/Formatting.svelte" import { formatDate } from "src/util/Formatting.svelte"
import IconBlock from "./IconBlock.svelte"; import IconBlock from "src/layout/IconBlock.svelte";
import TextBlock from "./TextBlock.svelte"; import TextBlock from "src/layout/TextBlock.svelte";
import TorrentItem from "./TorrentItem.svelte" import TorrentItem from "./TorrentItem.svelte"
import FileTitle from "./FileTitle.svelte"; import FileTitle from "src/layout/FileTitle.svelte";
import CopyButton from "../../layout/CopyButton.svelte"; import CopyButton from "src/layout/CopyButton.svelte";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()

View File

@@ -1,7 +1,7 @@
<script> <script>
import { onMount, createEventDispatcher, tick } from "svelte"; import { onMount, createEventDispatcher, tick } from "svelte";
import BandwidthUsage from "./BandwidthUsage.svelte"; import BandwidthUsage from "./BandwidthUsage.svelte";
import IconBlock from "./IconBlock.svelte"; import IconBlock from "src/layout/IconBlock.svelte";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()
export let is_list = false export let is_list = false

View File

@@ -1,11 +1,11 @@
<script> <script>
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import { formatDataVolume, formatDate } from "../../util/Formatting.svelte" import { formatDataVolume, formatDate } from "../../util/Formatting.svelte"
import IconBlock from "./IconBlock.svelte"; import IconBlock from "src/layout/IconBlock.svelte";
import TextBlock from "./TextBlock.svelte"; import TextBlock from "src/layout/TextBlock.svelte"
import ZipItem from "./ZipItem.svelte"; import ZipItem from "./ZipItem.svelte";
import BandwidthUsage from "./BandwidthUsage.svelte"; import BandwidthUsage from "./BandwidthUsage.svelte";
import FileTitle from "./FileTitle.svelte"; import FileTitle from "src/layout/FileTitle.svelte";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()

View File

@@ -1,5 +1,6 @@
import { fs_get_node, fs_encode_path, fs_split_path, type FSNode, type FSPath, type FSPermissions } from "./FilesystemAPI"; import { fs_get_node, fs_encode_path, fs_split_path } from "./FilesystemAPI";
import { type Writable } from "svelte/store" import type { FSNode, FSPath, FSPermissions, FSContext } from "./FilesystemAPI";
import type { Writable } from "svelte/store"
export class FSNavigator { export class FSNavigator {
// Parts of the raw API response // Parts of the raw API response
@@ -7,6 +8,7 @@ export class FSNavigator {
base_index: number = 0 base_index: number = 0
children: Array<FSNode> = [] children: Array<FSNode> = []
permissions: FSPermissions = <FSPermissions>{} permissions: FSPermissions = <FSPermissions>{}
context: FSContext = <FSContext>{}
// base equals path[base_index]. It's updated every time the path updates // base equals path[base_index]. It's updated every time the path updates
base: FSNode = <FSNode>{} base: FSNode = <FSNode>{}
@@ -128,6 +130,7 @@ export class FSNavigator {
this.base = node.path[node.base_index] this.base = node.path[node.base_index]
this.children = node.children this.children = node.children
this.permissions = node.permissions this.permissions = node.permissions
this.context = node.context
this.initialized = true this.initialized = true
console.debug("Opened node", node) console.debug("Opened node", node)

View File

@@ -13,6 +13,8 @@ import { branding_from_path } from './edit_window/Branding.js'
import Menu from './Menu.svelte'; import Menu from './Menu.svelte';
import { FSNavigator } from "./FSNavigator" import { FSNavigator } from "./FSNavigator"
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
import TransferLimit from '../file_viewer/TransferLimit.svelte';
import { stats } from "src/util/StatsSocket.js"
let file_viewer let file_viewer
let file_preview let file_preview
@@ -178,6 +180,21 @@ const search = async () => {
</div> </div>
</div> </div>
{#if $nav.context.premium_transfer === false}
<div class="download_limit">
{#if $stats.limits.transfer_limit_used > $stats.limits.transfer_limit}
<div class="highlight_yellow">
Your free download limit has been used up and your download
speed has been limited to 1 MiB/s. <a href="/#pro"
target="_blank">Upgrade to premium</a> to continue fast
downloading
</div>
{:else}
<TransferLimit/>
{/if}
</div>
{/if}
<!-- This frame will load the download URL when a download button is pressed --> <!-- This frame will load the download URL when a download button is pressed -->
<iframe <iframe
bind:this={download_frame} bind:this={download_frame}
@@ -244,6 +261,15 @@ const search = async () => {
overflow: hidden; overflow: hidden;
} }
/* Download limit gauge (row 3) */
.download_limit {
flex: 0 0 auto;
display: flex;
flex-direction: column;
text-align: center;
background-color: var(--shaded_background);
}
/* This max-width needs to be synced with the .toolbar max-width in /* This max-width needs to be synced with the .toolbar max-width in
Toolbar.svelte and the .label max-width in FileStats.svelte */ Toolbar.svelte and the .label max-width in FileStats.svelte */
@media (max-width: 800px) { @media (max-width: 800px) {

View File

@@ -11,6 +11,7 @@ export type FSPath = {
base_index: number, base_index: number,
children: Array<FSNode>, children: Array<FSNode>,
permissions: FSPermissions, permissions: FSPermissions,
context: FSContext,
} }
export type FSNode = { export type FSNode = {
@@ -42,6 +43,10 @@ export type FSPermissions = {
delete: boolean, delete: boolean,
} }
export type FSContext = {
premium_transfer: boolean,
}
// API parameters // API parameters
// ============== // ==============

View File

@@ -1,8 +1,8 @@
<script> <script>
import { onMount } from 'svelte' import { onMount } from 'svelte'
import { fs_path_url, fs_encode_path, fs_node_icon } from "./../FilesystemAPI" import { fs_path_url, fs_encode_path, fs_node_icon } from "src/filesystem/FilesystemAPI.ts"
import FileTitle from './../../file_viewer/viewers/FileTitle.svelte'; import FileTitle from "src/layout/FileTitle.svelte";
import TextBlock from './../../file_viewer/viewers/TextBlock.svelte'; import TextBlock from "src/layout/TextBlock.svelte"
export let nav export let nav
let player let player

View File

@@ -1,8 +1,8 @@
<script> <script>
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import IconBlock from "../../file_viewer/viewers/IconBlock.svelte"; import IconBlock from "src/layout/IconBlock.svelte";
import { fs_thumbnail_url } from "../FilesystemAPI"; import { fs_thumbnail_url } from "../FilesystemAPI";
import TextBlock from "../../file_viewer/viewers/TextBlock.svelte"; import TextBlock from "src/layout/TextBlock.svelte"
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()
export let nav export let nav

View File

@@ -1,7 +1,7 @@
<script> <script>
import { onMount, tick } from "svelte"; import { onMount, tick } from "svelte";
import Spinner from "../../util/Spinner.svelte"; import Spinner from "../../util/Spinner.svelte";
import { fs_node_type } from "./../FilesystemAPI"; import { fs_node_type, fs_thumbnail_url } from "./../FilesystemAPI";
import FileManager from "../filemanager/FileManager.svelte"; import FileManager from "../filemanager/FileManager.svelte";
import Audio from "./Audio.svelte"; import Audio from "./Audio.svelte";
import File from "./File.svelte"; import File from "./File.svelte";
@@ -12,6 +12,8 @@ import Video from "./Video.svelte";
import Torrent from "./Torrent.svelte"; import Torrent from "./Torrent.svelte";
import Zip from "./Zip.svelte"; import Zip from "./Zip.svelte";
import CustomBanner from "./CustomBanner.svelte"; import CustomBanner from "./CustomBanner.svelte";
import { stats } from "src/util/StatsSocket.js"
import SlowDown from "src/layout/SlowDown.svelte";
export let nav export let nav
export let edit_window export let edit_window
@@ -60,6 +62,14 @@ export const seek = delta => {
<FileManager nav={nav} edit_window={edit_window} on:upload_picker> <FileManager nav={nav} edit_window={edit_window} on:upload_picker>
<CustomBanner path={$nav.path}/> <CustomBanner path={$nav.path}/>
</FileManager> </FileManager>
{:else if $nav.context.premium_transfer === false && $stats.limits.transfer_limit_used > $stats.limits.transfer_limit}
<SlowDown
on:download
file_size={$nav.base.file_size}
file_name={$nav.base.name}
file_type={$nav.base.file_type}
icon_href={fs_thumbnail_url($nav.base.path, 256, 256)}
/>
{:else if viewer_type === "audio"} {:else if viewer_type === "audio"}
<Audio nav={nav} bind:this={viewer}> <Audio nav={nav} bind:this={viewer}>
<CustomBanner path={$nav.path}/> <CustomBanner path={$nav.path}/>

View File

@@ -1,6 +1,6 @@
<script> <script>
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import { swipe_nav } from "./../../file_viewer/viewers/SwipeNavigate"; import { swipe_nav } from "src/util/SwipeNavigate.ts";
import { fs_path_url } from "./../FilesystemAPI"; import { fs_path_url } from "./../FilesystemAPI";
let dispatch = createEventDispatcher(); let dispatch = createEventDispatcher();

View File

@@ -3,8 +3,8 @@ import { createEventDispatcher } from "svelte";
import Magnet from "../../icons/Magnet.svelte"; import Magnet from "../../icons/Magnet.svelte";
import { formatDate } from "../../util/Formatting.svelte" import { formatDate } from "../../util/Formatting.svelte"
import TorrentItem from "./TorrentItem.svelte" import TorrentItem from "./TorrentItem.svelte"
import IconBlock from "../../file_viewer/viewers/IconBlock.svelte"; import IconBlock from "src/layout/IconBlock.svelte";
import TextBlock from "../../file_viewer/viewers/TextBlock.svelte"; import TextBlock from "src/layout/TextBlock.svelte"
import { fs_node_icon, fs_path_url } from "../FilesystemAPI"; import { fs_node_icon, fs_path_url } from "../FilesystemAPI";
import CopyButton from "../../layout/CopyButton.svelte"; import CopyButton from "../../layout/CopyButton.svelte";

View File

@@ -2,8 +2,8 @@
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import { formatDataVolume, formatDate } from "../../util/Formatting.svelte" import { formatDataVolume, formatDate } from "../../util/Formatting.svelte"
import ZipItem from "../../file_viewer/viewers/ZipItem.svelte"; import ZipItem from "../../file_viewer/viewers/ZipItem.svelte";
import IconBlock from "../../file_viewer/viewers/IconBlock.svelte"; import IconBlock from "src/layout/IconBlock.svelte";
import TextBlock from "../../file_viewer/viewers/TextBlock.svelte"; import TextBlock from "src/layout/TextBlock.svelte"
import { fs_node_icon, fs_path_url } from "../FilesystemAPI"; import { fs_node_icon, fs_path_url } from "../FilesystemAPI";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()

View File

@@ -16,7 +16,7 @@ export let width = "750px"
flex-direction: row; flex-direction: row;
margin: 8px auto; margin: 8px auto;
} }
@media(max-width: 500px) { @media(max-width: 400px) {
.block { .block {
flex-direction: column; flex-direction: column;
} }

View File

@@ -1,28 +1,25 @@
<script> <script>
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import { formatDataVolume, formatDuration } from "../../util/Formatting.svelte"; import { formatDataVolume, formatDuration } from "../util/Formatting.svelte";
import { stats } from "../StatsSocket"; import { stats } from "src/util/StatsSocket.js"
import IconBlock from "./IconBlock.svelte"; import TextBlock from "src/layout/TextBlock.svelte"
import TextBlock from "./TextBlock.svelte"; import IconBlock from "src/layout/IconBlock.svelte";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()
export let file = { export let file_size = 0
name: "", export let file_name = ""
mime_type: "", export let file_type = ""
availability: "", export let icon_href = ""
size: 0,
download_speed_limit: 0,
}
</script> </script>
<TextBlock> <TextBlock>
<img src="/res/img/slow_down.webp" class="header_image" alt="Yea, I'm gonna need you to slow down a bit"/> <img src="/res/img/slow_down.webp" class="header_image" alt="Yea, I'm gonna need you to slow down a bit"/>
<p> <p>
Pixeldrain's free tier is supported by my Patrons (be grateful). There's Pixeldrain's free tier is supported by my Patrons. There's only so much
only so much that you can do with the budget they provide. that you can do with the budget they provide.
{formatDataVolume($stats.limits.transfer_limit, 3)} per day is about {formatDataVolume($stats.limits.transfer_limit, 3)} per day is about the
the most I can give away for free while keeping it fair for everyone, most I can give away for free while keeping it fair for everyone, and
and according to our records you have already downloaded according to our records you have already downloaded
{formatDataVolume($stats.limits.transfer_limit_used, 3)}. {formatDataVolume($stats.limits.transfer_limit_used, 3)}.
</p> </p>
<p> <p>
@@ -36,9 +33,8 @@ export let file = {
Come back tomorrow when your free transfer limit resets Come back tomorrow when your free transfer limit resets
</li> </li>
<li> <li>
Download the file at a rate of {file.download_speed_limit/(1<<10)} Download the file at a limited rate of 1 MiB/s. This will take at
kiB/s. This will take at least least {formatDuration((file_size/(1<<20))*1000, 0)}
{formatDuration((file.size/file.download_speed_limit)*1000)}
</li> </li>
<li> <li>
<a href="/#pro" target="_blank" class="button button_highlight"> <a href="/#pro" target="_blank" class="button button_highlight">
@@ -53,9 +49,12 @@ export let file = {
</ul> </ul>
</TextBlock> </TextBlock>
<IconBlock icon_href={file.icon_href}> <IconBlock icon_href={icon_href}>
Name: {file.name}<br/> <table>
Type: {file.mime_type}<br/> <tr><td colspan="2">{file_name}</td></tr>
<tr><td>Type</td><td>{file_type}</td></tr>
<tr><td>Size</td><td>{formatDataVolume(file_size, 3)}</td></tr>
</table>
<button on:click={() => {dispatch("download")}}> <button on:click={() => {dispatch("download")}}>
<i class="icon">download</i> Download <i class="icon">download</i> Download
</button> </button>