Coding soon

Coding soon

flaque

Coming soon

Download & share high quality music and more from any device or browser.

Flaque is a simple media downloader. Run your own instance at home, make it accessible from outside, share content with friends and family.

  • Download lossless files from Qobuz and Tidal
  • Unlock links and fast download via AllDebrid
  • Orders queue, no hurry, processed one by one
  • Email download link after order is processed
  • Delete email address once order is completed

No ads, no download manager, command lines, torrents, transfer, drive, etc...

One click drop, wait for delivery, play and share from any device.

Flaque web page#

Go to Flaque page, paste link and email.

Text inputsText inputs

Flaque bookmark#

Click.

Qobuz drop, also works with TidalQobuz drop, also works with Tidal
Alldebrid dropAlldebrid drop
Click, click, click.Click, click, click.

Flaque to go#

Share everywhere

iOs Qobuz dropiOs Qobuz drop
Android Qobuz dropAndroid Qobuz drop

Features#

Web player#

  • Play & share music from any browser
  • One "music box" per email (hashed)
  • Background audio
  • Native media controls
  • MP3@320 playback
  • Original file download
  • Offline playback [SOON]
  • Playlists [SOON]

Delivery#

  • Allow direct open files (flac, pdf, mp4, ...)
  • Or compress all orders and force download
  • Qobuz & Tidal albums zipped with cover & emailed
  • Single tracks are added to web player if enabled

Metadata#

  • Extract covers from audio files
  • Extract first page from PDF files (CBR & CBZ [SOON])
  • Parse MKV files and fetch posters from TMDB
  • Attach images to emails
  • Opengraph message previews
opengraph

Alldebrid download relay#

  • {FLAQUE_URL}/dbrd?url={LINK}
  • Waiting line page [SOON]
  • Start download
Lazy debridLazy debrid

Privacy#

  • No account
  • No cookies
  • No analytics
  • No centralized database
  • Not storing email addresses in database
  • Just keeping a hash for download limits
  • Also used by web player to store tracks
  • Collecting IP addresses but also hashed

Security#

Not really... just sharing ¯\(ツ)

Flaque setup#

🧪 FIRST RELEASE
🚀 NODEJS NO LTS
💥 UNSTABLE TEST
🪲 BUGS INCOMING

☣️ This version is running a Node.js 23.10 EXPERIMENTAL UNSAFE web server with no dependencies (built-in modules only), coded by a machine (and fixed by a human).

Flaque code was typed on a real keyboard, and needs a few npm packages to run smoothly.

Underlying server can be easily replaced.

This project was intended for personal use, tested with friends for almost a year now.
Running on a 2016 Intel NUC + Ubuntu server + 1Gbps internet access, uptime and performances are satisfying.
Downloaders get stuck from time to time, error handling is not fully implemented.

Flaque sources#

Download repository from Github

Required software#

Docker#

Flaque image is based on node:23.10-alpine.

Two volumes must be mounted to preserve data :

  • /app/prod : Flaque config files and drops directory

  • /root/.config : Qobuz & Tidal config files

  • Open terminal in Flaque directory

  • Create image
    docker compose --file ops/docker/docker-compose.yml build --force-rm --no-cache

  • Create container docker compose // volumes // ports

  • Connect Qobuz account (enter credentials, ignore all settings)
    docker exec -it {container_name} qobuz-dl

  • Connect Tidal account (enter credentials, follow instructions)
    docker exec -it {container_name} tidal-dl-ng login

Autoinstall#

⚠️ experimental

Following scripts will automatically download and install all required dependencies.

Windows

Self-contained portable installation Tested on Windows 11 Pro 24H2

  • Open terminal in Flaque directory
  • Run autoinstall script
    ops\scripts\install_win.bat
  • Connect Qobuz account (enter credentials, ignore all settings)
    run.bat qobuz-dl
  • Connect Tidal account (enter credentials, follow instructions)
    run.bat tidal-dl-ng login
  • Edit prod/server.conf.js and prod/flaque.conf.js
  • Start server
    run.bat npm start
  • Go to Flaque page {FLAQUE_URL}/flaque
  • If running locally : http://localhost:8080/flaque

Ubuntu

Tested on Ubuntu 24 desktop fresh install and Ubuntu 24 WSL2

  • Open terminal in Flaque directory
  • Make script executable
    chmod +x ./ops/scripts/install_ubuntu.sh
  • Run autoinstall script
    ./ops/scripts/install_ubuntu.sh
  • Open a new terminal
  • Connect Qobuz account (enter credentials, ignore all settings)
    qobuz-dl
  • Connect Tidal account (enter credentials, follow instructions)
    tidal-dl-ng login
  • Edit prod/server.conf.js and prod/flaque.conf.js
  • Start server
    npm start

HTTPS#

  • docker reverse
  • let's encrypt
  • win acme

Settings#

⚠️ Flaque is open by default. Accepts drops from any email address !

Flaque config
+
opts: { // global options

	url: "https://localhost", // flaque public url

	// download directory
	storage: "prod/drops", 

	// ⚠️ Must be enabled to allow drops from bookmarks, mobile shortcuts, HTTP GET
	// {FLAQUE_URL}/drop?lnk={url}&box={mail}&msg={message}
	fastdrop: true, 
	
	keeptime: 7200, // delete downloaded zip files after x seconds

	linked: [ // no zip, direct link delivery
		".flac", 
		".pdf", 
		".mkv", 
		".mp4"
	], 
	
	keepdata: true // keep history > 24h

}, 

bin: { // binaries

	"qobuz-dl": "qobuz-dl", 
	"tidal-dl-ng": "tidal-dl-ng", 
	"ffmpeg": "ffmpeg"

}, 

mail: { // mail conf

	smtp: "domain.tld", // smtp server
	port: 465, // smtp port
	from: "\"flaque\" <flaque@domain.tld>", // mail from
	user: "flaque@domain.tld", // mail account
	pass: "p4ssw0rd", // password
	msgsize: 512 // max mail message length

}, 

play: { // web player

	enabled: true, // enable web player

	tracks: 30, // max tracks per email address

	// not implemented, coming soon
	upload: false, // allow tracks upload

	// not implemented, coming soon
	upmail: [ // allow tracks upload mails
		// empty = anyone can upload tracks
	]

}, 

tmdb: { // movie posters

	// https://www.themoviedb.org/settings/api
	token: "TMDB_TOKEN"

}, 

limit: { // download restrictions

	skip: { // VIP bypass all limits

		mail: [ // email exact match

			"flaque@domain.tld"

		], 

		ip: [ // ip exact matches

		]

	}, 
	
	main: { // global limits

		day: 100, // max downloads / day
		hour: 10 // max downloads / hour

	}, 

	ip: { // ip limits

		day: 100, // max downloads / day
		hour: 20, // max downloads / hour

		list: [ // ban IP address or IP ranges

			// startsWith test

			// single IP
			// "127.127.127.127", 

			// IP range 127.127.xxx.xxx
			// "127.127", 

		]

	}, 

	mail: { // email limits

		day: 100, // max downloads / day
		hour: 20, // max downloads / hour

		list: [ // ban emails or domains

			// endsWith test

			// single email
			// "notyou@example.com", 

			// any email @example.com
			// "example.com", 

			// apple hide my email
			"privaterelay.appleid.com", 

			// or all icloud
			// "icloud.com"

		]

	}

}, 

www: { // downloaders

	qobuz: { // qobuz conf

		enabled: true, // enable downloader

		// audio quality
		// 5 = 320k, 6 = lossless 44/16, 7 = 96/24, 27 = 192/24
		quality: 6, 

		mail: { // email custom quality
			"flaque@domain.tld": 27
		}, 

		day: 100, // max downloads / day				
		hour: 10, // max downloads / hour

		type: {

			track: { // download tracks
				
				enabled: true 

			}, 
			album: { // download albums
				
				enabled: true 

			}

		}

	}, 

	tidal: { // tidal conf

		enabled: true, // enable downloader

		// audio quality
		// HIGH = 320k, LOSSLESS = 44/16, HI_RES = 96/24, HI_RES_LOSSLESS = 192/24
		quality: "LOSSLESS", 

		mail: { // email custom quality
			"flaque@domain.tld": "HI_RES_LOSSLESS"
		}, 

		delay: false, // random delay before download

		day: 100, // max downloads / day
		hour: 10, // max downloads / hour

		type: {

			track: { // download tracks
				
				enabled: true 

			}, 
			album: { // download albums
				
				enabled: true 

			}

		}

	}, 

	alldebrid: { // alldebrid conf

		enabled: true, // enable downloader

		relay: false, // enable alldebrid direct download relay, no mail required, ip check only

		// https://alldebrid.com/apikeys/
		app: "ALLDEBRID_APP_NAME", 
		key: "ALLDEBRID_API_KEY", 			

		day: 100, // max downloads / day
		hour: 10, // max downloads / hour

		size: 5000, // max ddl file size MB

		// not implemented, coming soon
		speed: 5, // download speed limit MB/s

		type: { // hosts conf

			"1fichier": {

				enabled: true, // enable 1fichier
				day: 100, // max downloads / day
				hour: 10, // max downloads / hour
				size: 2000 // max file size MB

			}, 

			"isra": { enabled: false }, 

			"katfile": {

				enabled: true, // enable katfile
				day: 100, // max downloads / day
				hour: 10, // max downloads / hour
				size: 75 // max file size MB

			}, 

			"mega": {enabled: true}, 
			"rapidgator": {enabled: true}, 
			"scribd": {enabled: true}, 

			"turbobit": {

				enabled: true, // enable turbobit
				day: 100, // max downloads / day
				hour: 10, // max downloads / hour
				size: 1000 // max file size MB

			}, 

			// ...

		}, 

		// not implemented, coming soon
		p2p: { // magnet & torrents

			// enabled: false, // enable p2p
			day: 100, // max downloads / day
			hour: 10, // max downloads / hour
			size: 75 // max file size MB

		}, 

		exts: [ // allowed file extensions

			// empty = no restrictions
			// "pdf", "epub", "cbz", "cbr", 
			// "zip", "rar", 
			// "mkv", "mp4"

		]

	}

}

Configuration#

Web drop#

  • Go to Flaque page
  • Paste link
  • Type email
  • Drop

Browser will keep email address in LocalStorage, next pasted links will be submitted automatically.

Bookmark drop#

  • Generate bookmarklet code below
  • Data not transfered to server
  • Right click bookmarks bar, Add page ...
  • Choose a fancy name, paste code in URL field
  • Go to Qobuz or Tidal track / album page
  • Click the button

One bookmark = one email address

bookmarklet html
-
<form>
	<input type="text" name="flaque" placeholder="Flaque URL" required/>
	<input type="email" name="email" placeholder="Email address" required/>
	<input type="text" name="msg" placeholder="Message (optional)"/>
	<input type="submit" value="Generate bookmark">
</form>
bookmarklet css
-
:root {

	--font: 'Courier New', Courier, monospace;

	--back: #FFFFFF;
	--btnback: #cacaca;
	--col: #333333;

}

@media (prefers-color-scheme: dark) {

	:root {
	
		--back: #0d1117;
		--btnback: #939393;
		--col: #c9d1d9;
	
	}

}

html, body {
	margin: 0;
	padding: 0;
	width: 100%;
	height: 100%;
}

input[type=submit], 
input[type=text], 
input[type=email] {
	box-sizing: border-box;
	-webkit-appearance: none;
	outline: none;
	background-color: var(--back);
	color: var(--col);
	border: solid 1px transparent;
	border-radius: 0;
	padding: 0;
	width: 100%;
	max-width: 380px;
	font-size: 1.2em;
	line-height: 1.2em;
	font-family: var(--font);
}

input[type=submit] {
	cursor: pointer;
	font-weight: normal;
}

input[type=text], 
input[type=email] {
	border-bottom: solid 1px var(--btnback);
}

form {
	width: 100%;
	height: 100%;
	display: flex;
	flex-direction: column;
	justify-content: space-evenly;
	align-items: center;
}
bookmarklet js
-
const cliqueFlaque = () => {

	const popMsg = (txt, col, out = 2500) => {

		console.log(txt);

		let wrap = document
		.createElement("div");

		wrap
		.setAttribute(
			"style", 
			[
				"position:fixed", 
				"top:20px", 
				"width:100%", 
				"z-index:5000", 
				"text-align:center"
			]
			.join(";") + ";"
		);
		
		let ack = document
		.createElement("span");

		ack
		.setAttribute(
			"style", 
			[
				"box-sizing:border-box", 
				"font-size:1.3em", 
				"border-radius:6px", 
				"padding:0.33em 0.66em", 
				"color:#FFFFFF", 
				"background-color:" + 
				[
					"rgba(228,45,68,0.7)", 
					"rgba(105,216,91,0.7)", 
					"rgba(232,174,11,0.7)"
				][col]
			]
			.join(";") + ";"
		);

		ack.innerHTML = txt;
		
		wrap
		.appendChild(ack);
		
		document.body
		.appendChild(wrap);

		setTimeout(
			() => 
				wrap
				.remove(), 
			out
		);

	};
	
	let box = "{box}" || prompt("email"), 
		
		msg = "{msg}", 

		drop = "{flaque}/drop?" + 
		"lnk=" + location.href + 
		"&box=" + box + 
		(
			msg 
			? "&msg=" + encodeURI(msg) 
			: ""
		);

	if(!box) 
		return popMsg(
			"mail please", 
			2
		);
		
	fetch(drop)
	.then(
		res => 
			res.ok
			&& res
			.json() 
			|| null
	)
	.then(
		dropped => {

			if(dropped) {

				if(dropped.box) {

					popMsg(
						"<a style=\"color:#FFFFFF;text-decoration:none;\" href=\"" + dropped.box + "\" target=\"_blank\">▶ click to open player</a>", 
						+dropped.ok, 
						5000
					);
					
				}
				else 
					popMsg(
						dropped.msg, 
						+dropped.ok
					);
				
			} 
			else {

				popMsg(
					"maybe", 
					2
				);
				
			}
			
		}
	);
	
};

const plouf = (flaque, box, msg) => 
	"javascript:(function(){" + 
		[
			["{flaque}", flaque], 
			["{box}", box], 
			["{msg}", msg], 
			// remove consecutive whitespaces
			[/\s+/g, " "], 
			// remove comments without breaking urls
			[/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*|^\s*\n$/gm, "$1"], 
			// remove spaces around operators
			[/\s*([{}[\]()=+\-/*:,;<>!|&?])\s*/g, "$1"], 
			// remove spaces around dot chain methods
			[/\s*\.\s*/g, "."]
		]
		.reduce(
			(clique, claque) => 
				clique
				.replace(...claque), 
			cliqueFlaque
			.toString()
		)
		.trim() 
	+ "})()";

window.addEventListener(
	"load", 
	() => 
		document.querySelector("form")
		.addEventListener(
			"submit", 
			evt => {
				evt.preventDefault();
				const dat = Object.fromEntries(new FormData(evt.target));
				console.log(plouf(dat["flaque"], dat["email"], dat["msg"]));
				return false;
			}
		)
);

Mobile drop#

Qobuz & Tidal

  • Install apps
  • Create free accounts
  • Choose track or album
  • Tap dots ⋮menu···
  • Share -> More
  • Run shortcut

Alldebrid

  • Open browser
  • Go to supported host link
  • Open share sheet
  • Run shortcut

Shortcuts can be duplicated, renamed, and edited

iOs#

  • Install Shortcuts
  • Get shortcut below
  • Run configuration
  • Set Flaque URL and email address

const iOsShortcutData = "data:application/octet-stream;base64,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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA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";

ios html
-
<div>Get iOs shortcut</div>
ios css
-
html, body {
	margin: 0;
	padding: 0;
	width: 100%;
	height: 100%;
}

body {
	width: 100%;
	height: 100%;
	display: flex;
	flex-direction: column;
	justify-content: space-evenly;
	align-items: center;
	background-color: #FFFFFF;
	color: #333333;
	font-family: 'Courier New', Courier, monospace;

}

@media (prefers-color-scheme: dark) {

	body {

		background-color: #0d1117;
		color: #c9d1d9;
		
	}

}

div {
	font-size: 1.5em;
	cursor: pointer;
}
ios load
-
document.querySelector("div")
.addEventListener(
	"click", 
	() => 
		forceDL(
			"FLAQUE_DROP.shortcut", 
			iOsShortcutData
		)
);
ios load
-
const forceDL = (nnm, dat) => {
	let a = document.createElement("a");
	document.body.appendChild(a);
	a.style.display = "none";
	a.href = dat;
	a.download = nnm;
	a.click();
	a.remove();
};

Android#

  • Install HTTP Shortcuts
  • Fill form below (not sent to server ;)
  • Set Flaque URL and email address
  • Download shortcut
  • Import in HTTP Shortcuts
android html
-
<form>
	<input type="text" name="flaque" placeholder="Flaque URL" required/>
	<input type="email" name="email" placeholder="Email address" required/>
	<input type="submit" value="Generate shortcut"/>
</form>
android css
-
input[type=submit] {
	font-size: 1.5em;
}
android js
-
const gen = () => 
	"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
	.replace(
		/[xy]/g,
		c => {
			const r = Math.random() * 16 | 0;
			return (c === "x" ? r : (r & 0x3 | 0x8)).toString(16);
		}
	);

const plouf = (flaque, box) => {

	const categoryId = gen();
	const shortcutId = gen();
	const variableId = gen();

	const shortcutJSON = JSON.stringify({
		categories: [
			{
				id: categoryId,
				name: "Shortcuts",
				shortcuts: [
					{
						categoryId: categoryId,
						codeOnFailure: "const fail \u003d JSON.parse(response.body);\nif(fail.msg) showToast(fail.msg);",
						codeOnSuccess: "const drop \u003d JSON.parse(response.body);\nif(drop.msg) showToast(drop.msg);\nif(drop.box) { if(confirm(\"open flaque player ?\")) { openUrl(drop.box); } }",
						description: "Good vibes",
						iconName: "flat_color_brightness",
						id: shortcutId,
						launcherShortcut: false,
						name: "FLAQUE DROP",
						responseHandling: {
							successOutput: "none"
						},
						url: flaque + "/drop?lnk\u003d{{" + variableId + "}}\u0026box\u003d" + box
					}
				]
			}
		],
		compatibilityVersion: 78,
		variables: [
			{
				flags: 1,
				id: variableId,
				key: "flaque_drop",
				urlEncode: true
			}
		],
		version: 1
	}, null, "\t");

	forceDL("FLAQUE_DROP.json", "data:application/json;base64," + btoa(shortcutJSON));

};

window.addEventListener(
	"load", 
	() => 
		document.querySelector("form")
		.addEventListener(
			"submit", 
			evt => {
				evt.preventDefault();
				const dat = Object.fromEntries(new FormData(evt.target));
				plouf(dat["flaque"], dat["email"]);
				return false;
			}
		)
);

URL drop#

Simple HTTP GET request
{FLAQUE_URL}/drop?lnk={MEDIA_LINK}&box={EMAIL_ADDRESS}

Optional email custom message
&msg=enjoy the silence when the music's over

Guides [SOON]#

  • Self hosting
  • Port redirect
  • Static IP
  • Domain name
  • Dynamic DNS
  • Let's Encrypt
  • Self-signed
  • Py managed env
  • pipx
  • Docker
  • Raspberry
  • ...

How it works#

  • Wait for order (link + email address)
  • Validate link, email, status, limits
  • Queue order, processed one by one
  • Download file
  • Process order
    • Parse metadata
    • Extract or fetch image
    • Create archive if needed
  • Move file to user directory
  • Email download link
  • Next order

Database#

flaque_drops : orders tracking

  • hash : drop hash
  • date : timestamp
  • www : portal, Qobuz / Tidal / Alldebrid
  • typ : media type, album / track / host
  • uid : keep download links, optional, can be disabled
  • stt : drop state (wait it, load it, parse it, zip it, send it, mail, delete it)
  • box : email address hash
  • ip : ip address hash

flaque_tracks : web player tracks

  • hash : drop hash
  • box : owner email hash
  • file : file name
  • artist : track artist
  • album : track album
  • title : track title
  • dur : track duration
  • ext : file type
  • size : file size
  • samp : sample rate
  • bits : bit depth
  • rate : bitrate
  • tags : custom tags [SOON]

flaque_files : downloaded files

  • hash : drop hash
  • box : owner email hash
  • date: timestamp
  • file : file name
  • ext : file type
  • size : file size

Storage#

Flaque drops directory structure

tmp : temporary storage
{email_hash} : user directory
	live : user music library
		{drop_hash}
			lossless flac
			320kbps mp3
			cover jpg
	file : user downloads
		{drop_hash}
			file || archive

Compile#

  • Open terminal in Flaque directory
  • Install necessary packages
    npm install google-closure-compiler sass
  • Optional : obfuscate JS code
    npm install javascript-obfuscator
  • Run compilation script
    npm run compile

Credits#

nodejs, nodemailer, archiver, python, qobuz-dl, tidal-dl-ng, alldebrid, music-metadata, tmdb, pdf.js, skr canvas, ffmpeg, fontawesome, closure compiler, sass, javascript-obfuscator

Disclaimer#

⚠️ No liability for any damages or issues
🚫 No responsibility for how this software is used
🎶 Good vibes

〜ヽ(⌐■_■)ノ♪♬