Use generated download link element instead of iframe for downloading

This commit is contained in:
2025-03-27 12:52:45 +01:00
parent 61ed63d458
commit c8d22f534a
10 changed files with 92 additions and 72 deletions

View File

@@ -4,15 +4,16 @@ import Modal from "../util/Modal.svelte"
export let file = { export let file = {
id: "", id: "",
name: "",
availability: "", availability: "",
download_href: "", download_href: "",
} }
export let list = { export let list = {
id: "", id: "",
title: "",
download_href: "", download_href: "",
} }
let download_frame
let load_captcha_script = false let load_captcha_script = false
let download_captcha_window let download_captcha_window
let captcha_type = "" // rate_limit or malware let captcha_type = "" // rate_limit or malware
@@ -24,13 +25,13 @@ let error_code = ""
let error_message = "" let error_message = ""
export const download_file = () => { export const download_file = () => {
if (!window.viewer_data.captcha_key) { if (!window.viewer_data.captcha_key) {
console.debug("Server doesn't support captcha, starting download") console.debug("Server doesn't support captcha, starting download", file.download_href)
download_frame.src = file.download_href download(file.download_href, file.name)
return return
} }
if (file.availability === "") { if (file.availability === "") {
console.debug("File is available, starting download") console.debug("File is available, starting download", file.download_href)
download_frame.src = file.download_href download(file.download_href, file.name)
return return
} }
if (!file.availability.endsWith("_captcha_required")) { if (!file.availability.endsWith("_captcha_required")) {
@@ -47,7 +48,8 @@ export const download_file = () => {
// we trigger the download using the captcha token Google provided us with // we trigger the download using the captcha token Google provided us with
let captcha_complete_callback = token => { let captcha_complete_callback = token => {
// Download the file using the recaptcha token // Download the file using the recaptcha token
download_frame.src = file.download_href + "&recaptcha_response=" + token console.debug("Captcha validation successful, starting download", file.download_href)
download(file.download_href + "&recaptcha_response=" + token, file.name)
download_captcha_window.hide() download_captcha_window.hide()
} }
@@ -91,9 +93,17 @@ export const download_file = () => {
} }
export const download_list = () => { export const download_list = () => {
if (list.id !== "") { if (list.id !== "") {
download_frame.src = list.download_href download(list.download_href, list.title+".zip")
} }
} }
const download = (href, file_name) => {
let a = document.createElement("a")
a.href = href
a.download = file_name
a.click()
a.remove()
}
</script> </script>
<svelte:head> <svelte:head>
@@ -102,7 +112,6 @@ export const download_list = () => {
{/if} {/if}
</svelte:head> </svelte:head>
<iframe class="download_frame" bind:this={download_frame} title="File download frame"></iframe>
<Modal bind:this={download_captcha_window} title={captcha_window_title} width="500px"> <Modal bind:this={download_captcha_window} title={captcha_window_title} width="500px">
{#if captcha_type === "rate_limit"} {#if captcha_type === "rate_limit"}
<p class="indent"> <p class="indent">
@@ -143,12 +152,6 @@ export const download_list = () => {
</Modal> </Modal>
<style> <style>
.download_frame {
position: absolute;
display: none;
width: 1px;
height: 1px;
}
.captcha_container { .captcha_container {
text-align: center; text-align: center;
} }

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 "../lib/StatsSocket.js" import { set_file, stats } from "../lib/StatsSocket.mjs"
export let file = { export let file = {
id: "", id: "",

View File

@@ -1,22 +1,24 @@
<script> <script>
import { formatDataVolume } from "../util/Formatting.svelte"; import { formatDataVolume } from "../util/Formatting.svelte";
import { stats } from "../lib/StatsSocket.js" import { stats } from "../lib/StatsSocket.mjs"
let percent = 0 let percent = 0
let title = "" let title = ""
$: { $: {
if ($stats.limits.transfer_limit === 0) { if ($stats.limits_init === true) {
percent = 0 // Avoid division by 0 if ($stats.limits.transfer_limit === 0) {
} else if ($stats.limits.transfer_limit_used / $stats.limits.transfer_limit > 1) { percent = 0 // Avoid division by 0
percent = 100 } else if ($stats.limits.transfer_limit_used / $stats.limits.transfer_limit > 1) {
} else { percent = 100
percent = ($stats.limits.transfer_limit_used / $stats.limits.transfer_limit) * 100 } else {
} percent = ($stats.limits.transfer_limit_used / $stats.limits.transfer_limit) * 100
}
title = "Download limit used: " + title = "Download limit used: " +
formatDataVolume($stats.limits.transfer_limit_used, 3) + formatDataVolume($stats.limits.transfer_limit_used, 3) +
" of " + " of " +
formatDataVolume($stats.limits.transfer_limit, 3); formatDataVolume($stats.limits.transfer_limit, 3);
}
} }
</script> </script>

View File

@@ -2,7 +2,7 @@
import { formatDataVolume } from "../../util/Formatting.svelte"; import { formatDataVolume } from "../../util/Formatting.svelte";
import TextBlock from "../../layout/TextBlock.svelte" import TextBlock from "../../layout/TextBlock.svelte"
import ProgressBar from "../../util/ProgressBar.svelte"; import ProgressBar from "../../util/ProgressBar.svelte";
import { stats } from "../../lib/StatsSocket.js" import { stats } from "../../lib/StatsSocket.mjs"
export let file = { export let file = {
size: 0, size: 0,

View File

@@ -11,7 +11,7 @@ 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 { stats } from "../../lib/StatsSocket.js" import { stats } from "../../lib/StatsSocket.mjs"
import Zip from "./Zip.svelte"; import Zip from "./Zip.svelte";
import SlowDown from "../../layout/SlowDown.svelte"; import SlowDown from "../../layout/SlowDown.svelte";
import TextBlock from "../../layout/TextBlock.svelte"; import TextBlock from "../../layout/TextBlock.svelte";

View File

@@ -1,7 +1,7 @@
<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 "../../lib/StatsSocket.js" import { stats } from "../../lib/StatsSocket.mjs"
import IconBlock from "../../layout/IconBlock.svelte"; import IconBlock from "../../layout/IconBlock.svelte";
import TextBlock from "../../layout/TextBlock.svelte" import TextBlock from "../../layout/TextBlock.svelte"
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()

View File

@@ -12,7 +12,7 @@ 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 TransferLimit from '../file_viewer/TransferLimit.svelte';
import { stats } from "../lib/StatsSocket.js" import { stats } from "../lib/StatsSocket.mjs"
import { css_from_path } from './edit_window/Branding'; import { css_from_path } from './edit_window/Branding';
import AffiliatePrompt from '../user_home/AffiliatePrompt.svelte'; import AffiliatePrompt from '../user_home/AffiliatePrompt.svelte';
@@ -20,7 +20,6 @@ let file_viewer
let file_preview let file_preview
let toolbar let toolbar
let upload_widget let upload_widget
let download_frame
let details_visible = false let details_visible = false
let edit_window let edit_window
let edit_visible = false let edit_visible = false
@@ -128,11 +127,18 @@ const keydown = e => {
}; };
const download = () => { const download = () => {
let a = document.createElement("a")
if (nav.base.type === "file") { if (nav.base.type === "file") {
download_frame.src = fs_path_url(nav.base.path) + "?attach" a.href = fs_path_url(nav.base.path) + "?attach"
a.download = nav.base.name
} else if (nav.base.type === "dir") { } else if (nav.base.type === "dir") {
download_frame.src = fs_path_url(nav.base.path) + "?bulk_download" a.href = fs_path_url(nav.base.path) + "?bulk_download"
a.download = nav.base.name+".zip"
} }
a.click()
a.remove()
} }
</script> </script>
@@ -184,13 +190,6 @@ const download = () => {
</div> </div>
{/if} {/if}
<!-- This frame will load the download URL when a download button is pressed -->
<iframe
bind:this={download_frame}
title="Frame for downloading files"
style="display: none; width: 1px; height: 1px;">
</iframe>
<DetailsWindow nav={nav} bind:visible={details_visible} /> <DetailsWindow nav={nav} bind:visible={details_visible} />
<EditWindow nav={nav} bind:this={edit_window} bind:visible={edit_visible} /> <EditWindow nav={nav} bind:this={edit_window} bind:visible={edit_visible} />

View File

@@ -12,7 +12,7 @@ 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 "../../lib/StatsSocket.js" import { stats } from "../../lib/StatsSocket.mjs"
import SlowDown from "../../layout/SlowDown.svelte"; import SlowDown from "../../layout/SlowDown.svelte";
export let nav export let nav

View File

@@ -1,7 +1,7 @@
<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 "../lib/StatsSocket.js" import { stats } from "../lib/StatsSocket.mjs"
import TextBlock from "./TextBlock.svelte" import TextBlock from "./TextBlock.svelte"
import IconBlock from "./IconBlock.svelte"; import IconBlock from "./IconBlock.svelte";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()

View File

@@ -1,25 +1,39 @@
import { readable } from "svelte/store"; import { readable } from "svelte/store";
let results = { type SocketResults = {
connected: false, connected: boolean,
file_stats_init: false, file_stats_init: boolean,
file_stats: { file_stats: FileStats
views: 0, limits_init: boolean,
downloads: 0, limits: Limits,
bandwidth: 0,
bandwidth_paid: 0,
},
limits_init: false,
limits: {
server_overload: false,
speed_limit: 0,
download_limit: 0,
download_limit_used: 0,
transfer_limit: 0,
transfer_limit_used: 0,
},
} }
type FileStats = {
views: number,
downloads: number,
bandwidth: number,
bandwidth_paid: number,
}
type Limits = {
server_overload: boolean,
speed_limit: number,
download_limit: number,
download_limit_used: number,
transfer_limit: number,
transfer_limit_used: number,
}
type SocketCommand = {
type: string,
data?: Object,
}
let results: SocketResults = {
file_stats: {} as FileStats,
limits: {} as Limits,
} as SocketResults
export const stats = readable( export const stats = readable(
results, results,
(set) => { (set) => {
@@ -28,24 +42,24 @@ export const stats = readable(
}, },
); );
let socket = null let socket: WebSocket = null
const start_sock = (set_func) => { const start_sock = (set_func: (value: SocketResults) => void) => {
if (socket !== null) { if (socket !== null) {
return return
} }
console.log("initializing stats socket") console.log("Initializing stats socket")
socket = new WebSocket(location.origin.replace(/^http/, 'ws') + "/api/file_stats") socket = new WebSocket(location.origin.replace(/^http/, 'ws') + "/api/file_stats")
socket.onopen = () => { socket.onopen = () => {
results.connected = true results.connected = true
set_func(results) // set_func(results)
// Subscribe to the rate limit feed. This will also process any queued // Subscribe to the rate limit feed. This will also process any queued
// commands built up while the socket was down // commands built up while the socket was down
send_cmd({ type: "limits" }) send_cmd({ type: "limits" })
} }
socket.onmessage = msg => { socket.onmessage = (msg: MessageEvent) => {
let j = JSON.parse(msg.data) let j = JSON.parse(msg.data)
console.debug("WS update", j) console.debug("WS update", j)
@@ -61,18 +75,19 @@ const start_sock = (set_func) => {
console.error("Unknown ws message type", j.type, "data", msg.data) console.error("Unknown ws message type", j.type, "data", msg.data)
} }
} }
socket.onerror = err => { socket.onerror = (err: Event) => {
console.error("socket error", err) console.error("Stats socket error", err)
stop_sock(set_func) stop_sock(set_func)
window.setTimeout(() => start_sock(set_func), 2000) window.setTimeout(() => start_sock(set_func), 2000)
} }
socket.onclose = () => { socket.onclose = (e: CloseEvent) => {
console.debug("Stats socket close", e)
stop_sock(set_func) stop_sock(set_func)
window.setTimeout(() => start_sock(set_func), 2000) window.setTimeout(() => start_sock(set_func), 2000)
} }
} }
const stop_sock = (set_func) => { const stop_sock = (set_func: (value: SocketResults) => void) => {
if (socket === null) { if (socket === null) {
return return
} }
@@ -92,7 +107,8 @@ const stop_sock = (set_func) => {
set_func(results) set_func(results)
} }
export const set_file = file_id => {
export const set_file = (file_id: string) => {
send_cmd({ send_cmd({
type: "file_stats", type: "file_stats",
data: { file_id: file_id }, data: { file_id: file_id },
@@ -100,7 +116,7 @@ export const set_file = file_id => {
} }
let queued_commands = [] let queued_commands = []
const send_cmd = cmd => { const send_cmd = (cmd: SocketCommand) => {
if (socket !== null && socket.readyState === WebSocket.OPEN) { if (socket !== null && socket.readyState === WebSocket.OPEN) {
// First empty the queue // First empty the queue