mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-09-28 04:12:07 +02:00
feat: advanced filename templates, low-RAM device profiling, responsive artist UI, and project site
- Add advanced filename template placeholders: {track_raw}, {disc_raw}, {date},
formatted numbers {track:N}/{disc:N}, and date formatting {date:%Y-%m-%d}
with strftime-to-Go layout conversion and robust date parser
- Pass date/release_date metadata to filename builder in all providers
(Amazon, Qobuz, Tidal, YouTube, extensions) and Flutter download queue
- Detect ARM32-only / low-RAM Android devices at startup and reduce image
cache size and disable overscroll effects for smoother experience
- Make artist screen selection bar responsive: compact stacked layout on
narrow screens or large text scale; add quality picker before track download
- Add advanced tags toggle in download settings filename format editor
- Fix ICU plural syntax in DE/ES/PT/RU translations (one {}=1{...} -> one {...})
- Add filenameShowAdvancedTags l10n strings (EN, ID) and regenerate dart files
- Fix featured-artist regex: remove '&' from split separators
- Add Go filename template tests (filename_test.go)
- Add GitHub Pages workflow and static project site
This commit is contained in:
@@ -0,0 +1,484 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Downloads - SpotiFLAC Mobile</title>
|
||||
<meta name="description" content="Download the latest version of SpotiFLAC Mobile. Changelog and release history included.">
|
||||
<meta name="theme-color" content="#0a0a0a">
|
||||
<link rel="icon" href="icon.png" type="image/png">
|
||||
|
||||
<!-- Google Sans Flex -->
|
||||
<style>
|
||||
@font-face { font-family: 'Google Sans Flex'; font-style: normal; font-display: swap; font-weight: 400; src: url(https://cdn.jsdelivr.net/fontsource/fonts/google-sans-flex@latest/latin-400-normal.woff2) format('woff2'); }
|
||||
@font-face { font-family: 'Google Sans Flex'; font-style: normal; font-display: swap; font-weight: 500; src: url(https://cdn.jsdelivr.net/fontsource/fonts/google-sans-flex@latest/latin-500-normal.woff2) format('woff2'); }
|
||||
@font-face { font-family: 'Google Sans Flex'; font-style: normal; font-display: swap; font-weight: 600; src: url(https://cdn.jsdelivr.net/fontsource/fonts/google-sans-flex@latest/latin-600-normal.woff2) format('woff2'); }
|
||||
@font-face { font-family: 'Google Sans Flex'; font-style: normal; font-display: swap; font-weight: 700; src: url(https://cdn.jsdelivr.net/fontsource/fonts/google-sans-flex@latest/latin-700-normal.woff2) format('woff2'); }
|
||||
@font-face { font-family: 'Google Sans Flex'; font-style: normal; font-display: swap; font-weight: 800; src: url(https://cdn.jsdelivr.net/fontsource/fonts/google-sans-flex@latest/latin-800-normal.woff2) format('woff2'); }
|
||||
</style>
|
||||
|
||||
<style>
|
||||
/* ── M3 AMOLED surface ramp ── */
|
||||
:root {
|
||||
--green: #1DB954;
|
||||
--green-dim: #1aa34a;
|
||||
--bg: #0a0a0a;
|
||||
--bg-card: #1a1a1a;
|
||||
--bg-card-hover: #222222;
|
||||
--surface: #121212;
|
||||
--text: #e8e8e8;
|
||||
--text-dim: #999;
|
||||
--max-w: 900px;
|
||||
}
|
||||
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
html { scroll-behavior: smooth; }
|
||||
|
||||
body {
|
||||
font-family: 'Google Sans Flex', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
background: var(--bg); color: var(--text); line-height: 1.6;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
a { color: var(--green); text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
|
||||
/* ── NAV ── */
|
||||
nav {
|
||||
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
|
||||
background: rgba(18,18,18,.78);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
}
|
||||
.nav-inner {
|
||||
max-width: var(--max-w); margin: auto;
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 0 24px; height: 64px;
|
||||
}
|
||||
.nav-brand { display: flex; align-items: center; gap: 10px; font-weight: 700; font-size: 1.1rem; color: var(--text); }
|
||||
.nav-brand img { width: 32px; height: 32px; border-radius: 50%; }
|
||||
.nav-links { display: flex; gap: 24px; list-style: none; }
|
||||
.nav-links a { color: var(--text-dim); font-size: .9rem; transition: color .2s; }
|
||||
.nav-links a:hover { color: var(--text); text-decoration: none; }
|
||||
.nav-links a.active { color: var(--text); font-weight: 600; }
|
||||
.nav-links .nav-icon { display: flex; align-items: center; opacity: .6; transition: opacity .2s; margin-left: -12px; }
|
||||
.nav-links .nav-icon:hover { opacity: 1; }
|
||||
.nav-links .nav-icon svg { width: 24px; height: 24px; fill: currentColor; }
|
||||
.nav-links .nav-divider { width: 1px; height: 20px; background: rgba(255,255,255,.15); margin-left: -4px; }
|
||||
|
||||
/* ── PAGE HEADER ── */
|
||||
.page-header {
|
||||
padding: 100px 24px 40px; text-align: center;
|
||||
}
|
||||
.page-header h1 { font-size: 2rem; font-weight: 800; margin-bottom: 8px; }
|
||||
.page-header p { color: var(--text-dim); font-size: 1rem; }
|
||||
|
||||
/* ── LATEST HERO ── */
|
||||
.latest-hero {
|
||||
max-width: var(--max-w); margin: 0 auto; padding: 0 24px 40px;
|
||||
}
|
||||
.latest-card {
|
||||
background: var(--bg-card-hover); border-radius: 20px;
|
||||
padding: 32px; position: relative; overflow: hidden;
|
||||
}
|
||||
.latest-header {
|
||||
display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 20px;
|
||||
}
|
||||
.latest-tag { font-size: 1.6rem; font-weight: 800; }
|
||||
.latest-badge {
|
||||
font-size: .7rem; font-weight: 700; text-transform: uppercase;
|
||||
padding: 4px 12px; border-radius: 999px;
|
||||
background: var(--green); color: #000;
|
||||
}
|
||||
.latest-date { font-size: .85rem; color: var(--text-dim); margin-left: auto; }
|
||||
.latest-assets {
|
||||
display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 10px; margin-bottom: 24px;
|
||||
}
|
||||
.latest-asset {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 14px 18px; border-radius: 16px;
|
||||
background: rgba(29,185,84,.08);
|
||||
color: var(--text); transition: background .2s; text-decoration: none;
|
||||
}
|
||||
.latest-asset:hover { background: rgba(29,185,84,.15); text-decoration: none; }
|
||||
.latest-asset-icon { color: var(--green); flex-shrink: 0; }
|
||||
.latest-asset-info { min-width: 0; }
|
||||
.latest-asset-name { font-size: .85rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.latest-asset-meta { font-size: .75rem; color: var(--text-dim); }
|
||||
.latest-changelog-toggle {
|
||||
background: var(--bg-card); border: none; border-radius: 16px;
|
||||
color: var(--text-dim); padding: 10px 16px; font-size: .85rem;
|
||||
cursor: pointer; transition: background .2s; width: 100%;
|
||||
font-family: inherit;
|
||||
}
|
||||
.latest-changelog-toggle:hover { background: var(--surface); color: var(--text); }
|
||||
.latest-changelog {
|
||||
display: none; margin-top: 16px; padding-top: 16px;
|
||||
border-top: 1px solid rgba(255,255,255,.06);
|
||||
}
|
||||
.latest-changelog.show { display: block; }
|
||||
|
||||
/* ── OLDER RELEASES ── */
|
||||
.older-section {
|
||||
max-width: var(--max-w); margin: 0 auto; padding: 40px 24px 80px;
|
||||
}
|
||||
.older-title {
|
||||
font-size: 1.1rem; font-weight: 600; color: var(--text-dim);
|
||||
margin-bottom: 16px; padding-bottom: 12px;
|
||||
}
|
||||
|
||||
/* ── RELEASE CARDS ── */
|
||||
.release-card {
|
||||
background: var(--bg-card); border-radius: 16px;
|
||||
margin-bottom: 8px; transition: background .2s;
|
||||
}
|
||||
.release-card:hover { background: var(--bg-card-hover); }
|
||||
.release-summary {
|
||||
display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
|
||||
padding: 16px 20px; cursor: pointer; list-style: none;
|
||||
}
|
||||
.release-summary::-webkit-details-marker { display: none; }
|
||||
.release-tag { font-size: 1rem; font-weight: 700; }
|
||||
.release-badge {
|
||||
font-size: .65rem; font-weight: 700; text-transform: uppercase;
|
||||
padding: 2px 8px; border-radius: 999px;
|
||||
}
|
||||
.release-badge-pre { background: #f59e0b; color: #000; }
|
||||
.release-date { font-size: .8rem; color: var(--text-dim); margin-left: auto; }
|
||||
.release-expand { color: var(--text-dim); font-size: .8rem; transition: transform .2s; }
|
||||
details[open] .release-expand { transform: rotate(180deg); }
|
||||
.release-detail { padding: 0 20px 20px; }
|
||||
.release-assets { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }
|
||||
.release-asset {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 7px 14px; border-radius: 12px; font-size: .82rem; font-weight: 500;
|
||||
background: rgba(29,185,84,.08);
|
||||
color: var(--green); transition: background .2s; text-decoration: none;
|
||||
}
|
||||
.release-asset:hover { background: rgba(29,185,84,.15); text-decoration: none; }
|
||||
.release-asset-size { color: var(--text-dim); font-size: .72rem; }
|
||||
|
||||
/* ── CHANGELOG BODY ── */
|
||||
.release-body {
|
||||
font-size: .85rem; color: var(--text-dim); line-height: 1.7;
|
||||
max-height: 400px; overflow-y: auto;
|
||||
scrollbar-width: thin; scrollbar-color: #333 transparent;
|
||||
}
|
||||
.release-body h1, .release-body h2, .release-body h3 {
|
||||
color: var(--text); font-size: .95rem; margin: 16px 0 8px;
|
||||
}
|
||||
.release-body h1:first-child, .release-body h2:first-child, .release-body h3:first-child { margin-top: 0; }
|
||||
.release-body ul { padding-left: 20px; margin: 4px 0; }
|
||||
.release-body li { margin: 4px 0; }
|
||||
.release-body code { background: var(--bg-card-hover); padding: 2px 6px; border-radius: 4px; font-size: .8rem; }
|
||||
.release-body a { color: var(--green); }
|
||||
|
||||
/* ── FOOTER ── */
|
||||
footer {
|
||||
background: var(--surface);
|
||||
padding: 40px 24px; text-align: center;
|
||||
}
|
||||
.footer-inner { max-width: var(--max-w); margin: auto; }
|
||||
.footer-links { display: flex; gap: 24px; justify-content: center; flex-wrap: wrap; margin-bottom: 16px; }
|
||||
.footer-links a { color: var(--text-dim); font-size: .9rem; }
|
||||
.footer-links a:hover { color: var(--text); }
|
||||
.footer-copy { color: #555; font-size: .8rem; }
|
||||
|
||||
/* ── LOADING ── */
|
||||
.loading { text-align: center; color: var(--text-dim); padding: 60px 0; }
|
||||
.loading-spinner {
|
||||
width: 32px; height: 32px; margin: 0 auto 12px;
|
||||
border: 3px solid var(--surface); border-top-color: var(--green);
|
||||
border-radius: 50%; animation: spin .8s linear infinite;
|
||||
}
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
.all-releases-link {
|
||||
display: block; text-align: center; padding: 24px;
|
||||
color: var(--text-dim); font-size: .9rem;
|
||||
}
|
||||
|
||||
/* ── MOBILE MENU ── */
|
||||
.nav-burger {
|
||||
display: none; width: 40px; height: 40px; border-radius: 12px;
|
||||
background: none; border: none; cursor: pointer;
|
||||
align-items: center; justify-content: center; flex-shrink: 0;
|
||||
position: relative;
|
||||
}
|
||||
.nav-burger .bar {
|
||||
display: block; width: 20px; height: 2px; background: var(--text);
|
||||
border-radius: 2px; transition: transform .3s cubic-bezier(.4,0,.2,1), opacity .2s;
|
||||
position: absolute; left: 10px;
|
||||
}
|
||||
.nav-burger .bar:nth-child(1) { top: 12px; }
|
||||
.nav-burger .bar:nth-child(2) { top: 19px; }
|
||||
.nav-burger .bar:nth-child(3) { top: 26px; }
|
||||
.nav-burger.active .bar:nth-child(1) { top: 19px; transform: rotate(45deg); }
|
||||
.nav-burger.active .bar:nth-child(2) { opacity: 0; }
|
||||
.nav-burger.active .bar:nth-child(3) { top: 19px; transform: rotate(-45deg); }
|
||||
.mobile-overlay {
|
||||
position: fixed; top: 64px; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0,0,0,.5); z-index: 98;
|
||||
opacity: 0; pointer-events: none;
|
||||
transition: opacity .3s cubic-bezier(.4,0,.2,1);
|
||||
}
|
||||
.mobile-overlay.open { opacity: 1; pointer-events: auto; }
|
||||
.mobile-menu {
|
||||
position: fixed; top: 64px; left: 0; right: 0;
|
||||
background: rgba(18,18,18,.95); padding: 8px 16px 16px; z-index: 99;
|
||||
backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
|
||||
transform: translateY(-8px); opacity: 0; pointer-events: none;
|
||||
transition: transform .3s cubic-bezier(.4,0,.2,1), opacity .3s cubic-bezier(.4,0,.2,1);
|
||||
}
|
||||
.mobile-menu.open { transform: translateY(0); opacity: 1; pointer-events: auto; }
|
||||
.mobile-menu a {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
padding: 14px 16px; border-radius: 12px;
|
||||
color: var(--text-dim); font-size: .95rem; font-weight: 500;
|
||||
transition: background .2s; opacity: 0; transform: translateY(-6px);
|
||||
}
|
||||
.mobile-menu.open a {
|
||||
opacity: 1; transform: translateY(0);
|
||||
transition: background .2s, opacity .3s cubic-bezier(.4,0,.2,1), transform .3s cubic-bezier(.4,0,.2,1);
|
||||
}
|
||||
.mobile-menu.open a:nth-child(1) { transition-delay: .03s; }
|
||||
.mobile-menu.open a:nth-child(2) { transition-delay: .06s; }
|
||||
.mobile-menu.open a:nth-child(3) { transition-delay: .09s; }
|
||||
.mobile-menu.open a:nth-child(4) { transition-delay: .12s; }
|
||||
.mobile-menu.open a:nth-child(5) { transition-delay: .15s; }
|
||||
.mobile-menu a:hover { background: var(--bg-card); color: var(--text); text-decoration: none; }
|
||||
.mobile-menu a.active { color: var(--text); font-weight: 600; background: var(--bg-card); }
|
||||
.mobile-menu .mobile-divider {
|
||||
height: 1px; background: rgba(255,255,255,.06); margin: 4px 0;
|
||||
opacity: 0; transition: opacity .3s .15s;
|
||||
}
|
||||
.mobile-menu.open .mobile-divider { opacity: 1; }
|
||||
.mobile-menu .mobile-icons {
|
||||
display: flex; gap: 8px; padding: 8px 16px 0;
|
||||
opacity: 0; transform: translateY(-6px);
|
||||
transition: opacity .3s cubic-bezier(.4,0,.2,1) .18s, transform .3s cubic-bezier(.4,0,.2,1) .18s;
|
||||
}
|
||||
.mobile-menu.open .mobile-icons { opacity: 1; transform: translateY(0); }
|
||||
.mobile-menu .mobile-icons a {
|
||||
padding: 10px; border-radius: 12px; background: var(--bg-card);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
opacity: 1; transform: none;
|
||||
}
|
||||
.mobile-menu .mobile-icons a svg { width: 20px; height: 20px; fill: currentColor; }
|
||||
|
||||
/* ── MOBILE ── */
|
||||
@media (max-width: 640px) {
|
||||
.nav-links { display: none; }
|
||||
.nav-burger { display: flex; }
|
||||
.page-header { padding: 80px 16px 32px; }
|
||||
.latest-hero { padding: 0 16px 32px; }
|
||||
.latest-card { padding: 20px; }
|
||||
.latest-header { flex-direction: column; align-items: flex-start; gap: 6px; }
|
||||
.latest-date { margin-left: 0; }
|
||||
.latest-assets { grid-template-columns: 1fr; }
|
||||
.older-section { padding: 32px 16px 60px; }
|
||||
.release-summary { flex-direction: row; gap: 8px; }
|
||||
.release-date { margin-left: auto; }
|
||||
}
|
||||
|
||||
.icon-svg { width: 20px; height: 20px; fill: currentColor; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav>
|
||||
<div class="nav-inner">
|
||||
<a class="nav-brand" href="index.html">
|
||||
<img src="icon.png" alt="SpotiFLAC">
|
||||
SpotiFLAC
|
||||
</a>
|
||||
<ul class="nav-links">
|
||||
<li><a href="index.html#features">Features</a></li>
|
||||
<li><a href="downloads.html" class="active">Downloads</a></li>
|
||||
<li><a href="index.html#faq">FAQ</a></li>
|
||||
<li><a href="partners.html">Partners</a></li>
|
||||
<li><a href="https://zarz.moe/docs" target="_blank">Docs</a></li>
|
||||
<li class="nav-divider"></li>
|
||||
<li><a href="https://github.com/zarzet/SpotiFLAC-Mobile" target="_blank" class="nav-icon" aria-label="GitHub"><svg viewBox="0 0 24 24"><path d="M12 .3a12 12 0 0 0-3.8 23.38c.6.12.82-.26.82-.57L9 20.86c-3.34.72-4.04-1.61-4.04-1.61-.55-1.39-1.34-1.76-1.34-1.76-1.09-.74.08-.73.08-.73 1.2.09 1.84 1.24 1.84 1.24 1.07 1.84 2.81 1.3 3.5 1 .1-.78.42-1.31.76-1.61-2.67-.3-5.47-1.33-5.47-5.93 0-1.31.47-2.38 1.24-3.22-.13-.3-.54-1.52.12-3.18 0 0 1-.32 3.3 1.23a11.5 11.5 0 0 1 6.02 0c2.28-1.55 3.29-1.23 3.29-1.23.66 1.66.25 2.88.12 3.18.77.84 1.24 1.91 1.24 3.22 0 4.61-2.81 5.63-5.48 5.92.43.37.81 1.1.81 2.22l-.01 3.29c0 .31.21.69.82.57A12 12 0 0 0 12 .3"/></svg></a></li>
|
||||
<li><a href="https://t.me/spotiflac" target="_blank" class="nav-icon" aria-label="Telegram"><svg viewBox="0 0 24 24"><path d="M11.94 24c6.6 0 12-5.4 12-12s-5.4-12-12-12-12 5.4-12 12 5.4 12 12 12zm-3.2-8.69l-.37-3.04 8.52-5.18c.38-.23.73.09.45.35l-6.96 6.4-.29 2.97c-.04.35-.48.43-.64.12l-1.64-3.33-3.6-1.17c-.78-.24-.8-.78-.02-1.14l14.04-5.4c.65-.25 1.25.15 1.04.83l-2.39 11.28c-.18.81-.7 1.01-1.42.63l-3.92-2.89-1.89 1.82c-.21.2-.39.38-.65.38l.28-3.06z"/></svg></a></li>
|
||||
</ul>
|
||||
<button class="nav-burger" onclick="toggleMenu()" aria-label="Menu">
|
||||
<span class="bar"></span><span class="bar"></span><span class="bar"></span>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- MOBILE MENU -->
|
||||
<div class="mobile-overlay" id="mobileOverlay" onclick="toggleMenu()"></div>
|
||||
<div class="mobile-menu" id="mobileMenu">
|
||||
<a href="index.html#features">Features</a>
|
||||
<a href="downloads.html" class="active">Downloads</a>
|
||||
<a href="index.html#faq">FAQ</a>
|
||||
<a href="partners.html">Partners</a>
|
||||
<a href="https://zarz.moe/docs" target="_blank">Docs</a>
|
||||
<div class="mobile-divider"></div>
|
||||
<div class="mobile-icons">
|
||||
<a href="https://github.com/zarzet/SpotiFLAC-Mobile" target="_blank" aria-label="GitHub">
|
||||
<svg viewBox="0 0 24 24"><path d="M12 .3a12 12 0 00-3.8 23.4c.6.1.8-.3.8-.6v-2c-3.3.7-4-1.6-4-1.6-.5-1.4-1.3-1.8-1.3-1.8-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.8 1.3 3.5 1 .1-.8.4-1.3.7-1.6-2.7-.3-5.5-1.3-5.5-6 0-1.3.5-2.3 1.2-3.2-.1-.3-.5-1.5.1-3.2 0 0 1-.3 3.4 1.2a11.5 11.5 0 016 0c2.3-1.5 3.3-1.2 3.3-1.2.7 1.7.3 2.9.1 3.2.8.8 1.2 1.9 1.2 3.2 0 4.6-2.8 5.6-5.5 5.9.4.4.8 1.1.8 2.2v3.3c0 .3.2.7.8.6A12 12 0 0012 .3z"/></svg>
|
||||
</a>
|
||||
<a href="https://t.me/spotiflac" target="_blank" aria-label="Telegram">
|
||||
<svg viewBox="0 0 24 24"><path d="M11.94 24c6.6 0 12-5.4 12-12s-5.4-12-12-12-12 5.4-12 12 5.4 12 12 12zm-3.2-8.69l-.37-3.04 8.52-5.18c.38-.23.73.09.45.35l-6.96 6.4-.29 2.97c-.04.35-.48.43-.64.12l-1.64-3.33-3.6-1.17c-.78-.24-.8-.78-.02-1.14l14.04-5.4c.65-.25 1.25.15 1.04.83l-2.39 11.28c-.18.81-.7 1.01-1.42.63l-3.92-2.89-1.89 1.82c-.21.2-.39.38-.65.38l.28-3.06z"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-header">
|
||||
<h1>Downloads</h1>
|
||||
<p>Latest releases with changelog and direct download links.</p>
|
||||
</div>
|
||||
|
||||
<div class="latest-hero" id="latest-hero">
|
||||
<div class="loading">
|
||||
<div class="loading-spinner"></div>
|
||||
Loading latest release...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="older-section" id="older-section" style="display:none">
|
||||
<div class="older-title">Previous Releases</div>
|
||||
<div id="older-releases"></div>
|
||||
<a class="all-releases-link" href="https://github.com/zarzet/SpotiFLAC-Mobile/releases" target="_blank">
|
||||
View all releases on GitHub →
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<div class="footer-inner">
|
||||
<div class="footer-links">
|
||||
<a href="index.html">Home</a>
|
||||
<a href="https://zarz.moe/docs" target="_blank">Documentation</a>
|
||||
<a href="https://github.com/zarzet/SpotiFLAC-Mobile" target="_blank">GitHub</a>
|
||||
<a href="https://t.me/spotiflac" target="_blank">Telegram</a>
|
||||
<a href="https://ko-fi.com/zarzet" target="_blank">Support / Ko-fi</a>
|
||||
</div>
|
||||
<p class="footer-copy">SpotiFLAC is for educational and private use only. Not affiliated with any streaming service.</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
(async () => {
|
||||
const REPO = 'zarzet/SpotiFLAC-Mobile';
|
||||
const latestEl = document.getElementById('latest-hero');
|
||||
const olderEl = document.getElementById('older-releases');
|
||||
const olderSection = document.getElementById('older-section');
|
||||
|
||||
try {
|
||||
const res = await fetch(`https://api.github.com/repos/${REPO}/releases?per_page=10`);
|
||||
if (!res.ok) throw new Error(res.status);
|
||||
const releases = await res.json();
|
||||
if (!releases.length) { latestEl.innerHTML = '<p style="text-align:center;color:#999;padding:40px">No releases found.</p>'; return; }
|
||||
|
||||
// Latest release
|
||||
const latest = releases[0];
|
||||
const latestDate = fmtDate(latest.published_at);
|
||||
const latestBody = md(latest.body || '');
|
||||
const latestAssets = (latest.assets || []).filter(a => !a.name.endsWith('.sha256'));
|
||||
|
||||
latestEl.innerHTML = `
|
||||
<div class="latest-card">
|
||||
<div class="latest-header">
|
||||
<span class="latest-tag">${latest.tag_name}</span>
|
||||
<span class="latest-badge">${latest.prerelease ? 'Pre-release' : 'Latest Release'}</span>
|
||||
<span class="latest-date">${latestDate}</span>
|
||||
</div>
|
||||
<div class="latest-assets">
|
||||
${latestAssets.map(a => `
|
||||
<a class="latest-asset" href="${a.browser_download_url}">
|
||||
<svg class="latest-asset-icon" width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>
|
||||
</svg>
|
||||
<div class="latest-asset-info">
|
||||
<div class="latest-asset-name">${a.name}</div>
|
||||
<div class="latest-asset-meta">${fmtSize(a.size)} · ${fmtCount(a.download_count)} downloads</div>
|
||||
</div>
|
||||
</a>
|
||||
`).join('')}
|
||||
</div>
|
||||
${latestBody ? `
|
||||
<button class="latest-changelog-toggle" onclick="this.nextElementSibling.classList.toggle('show'); this.textContent = this.nextElementSibling.classList.contains('show') ? 'Hide changelog' : 'Show changelog'">
|
||||
Show changelog
|
||||
</button>
|
||||
<div class="latest-changelog">
|
||||
<div class="release-body">${latestBody}</div>
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Older releases
|
||||
const older = releases.slice(1);
|
||||
if (older.length) {
|
||||
olderSection.style.display = '';
|
||||
olderEl.innerHTML = older.map(r => {
|
||||
const date = fmtDate(r.published_at);
|
||||
const body = md(r.body || '');
|
||||
const assets = (r.assets || []).filter(a => !a.name.endsWith('.sha256'));
|
||||
|
||||
return `
|
||||
<details class="release-card">
|
||||
<summary class="release-summary">
|
||||
<span class="release-tag">${r.tag_name}</span>
|
||||
${r.prerelease ? '<span class="release-badge release-badge-pre">Pre-release</span>' : ''}
|
||||
<span class="release-date">${date}</span>
|
||||
<span class="release-expand">▼</span>
|
||||
</summary>
|
||||
<div class="release-detail">
|
||||
${assets.length ? `
|
||||
<div class="release-assets">
|
||||
${assets.map(a => `
|
||||
<a class="release-asset" href="${a.browser_download_url}" target="_blank">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/></svg>
|
||||
${a.name}
|
||||
<span class="release-asset-size">${fmtSize(a.size)}</span>
|
||||
</a>
|
||||
`).join('')}
|
||||
</div>
|
||||
` : ''}
|
||||
${body ? `<div class="release-body">${body}</div>` : ''}
|
||||
</div>
|
||||
</details>
|
||||
`;
|
||||
}).join('');
|
||||
}
|
||||
} catch (e) {
|
||||
latestEl.innerHTML = `<p style="text-align:center;color:#999;padding:40px">Failed to load releases. <a href="https://github.com/${REPO}/releases" target="_blank">View on GitHub</a></p>`;
|
||||
}
|
||||
|
||||
function fmtDate(d) { return new Date(d).toLocaleDateString('en-US', { year:'numeric', month:'short', day:'numeric' }); }
|
||||
function fmtSize(b) { return b < 1048576 ? (b/1024).toFixed(1)+' KB' : (b/1048576).toFixed(1)+' MB'; }
|
||||
function fmtCount(n) { return n >= 1000 ? (n/1000).toFixed(1)+'k' : n; }
|
||||
function md(s) {
|
||||
return s
|
||||
.replace(/^### (.+)$/gm, '<h3>$1</h3>')
|
||||
.replace(/^## (.+)$/gm, '<h2>$1</h2>')
|
||||
.replace(/^# (.+)$/gm, '<h1>$1</h1>')
|
||||
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
||||
.replace(/`(.+?)`/g, '<code>$1</code>')
|
||||
.replace(/\[(.+?)\]\((.+?)\)/g, '<a href="$2" target="_blank">$1</a>')
|
||||
.replace(/^[-*] (.+)$/gm, '<li>$1</li>')
|
||||
.replace(/(<li>.*<\/li>)/s, '<ul>$1</ul>')
|
||||
.replace(/\n{2,}/g, '<br>')
|
||||
.replace(/(^<ul>)|(<\/ul>$)/g, '')
|
||||
.replace(/(<li>[\s\S]*?<\/li>)(?=\s*<h|$|\s*<br>)/g, '<ul>$1</ul>');
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
<script>
|
||||
function toggleMenu() {
|
||||
document.getElementById('mobileMenu').classList.toggle('open');
|
||||
document.getElementById('mobileOverlay').classList.toggle('open');
|
||||
document.querySelector('.nav-burger').classList.toggle('active');
|
||||
}
|
||||
document.getElementById('mobileMenu').addEventListener('click', function(e) {
|
||||
if (e.target.closest('a')) toggleMenu();
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 539 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 811 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 104 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 122 KiB |
+465
@@ -0,0 +1,465 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>SpotiFLAC Mobile - Lossless Music Downloader</title>
|
||||
<meta name="description" content="Download music in true lossless FLAC from Tidal, Qobuz & Amazon Music. No account required. Available on Android & iOS.">
|
||||
<meta name="theme-color" content="#0a0a0a">
|
||||
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:title" content="SpotiFLAC Mobile">
|
||||
<meta property="og:description" content="Download music in true lossless FLAC from Tidal, Qobuz & Amazon Music. No account required.">
|
||||
<meta property="og:image" content="icon.png">
|
||||
<meta property="og:type" content="website">
|
||||
|
||||
<link rel="icon" href="icon.png" type="image/png">
|
||||
|
||||
<!-- Google Sans Flex -->
|
||||
<style>
|
||||
@font-face { font-family: 'Google Sans Flex'; font-style: normal; font-display: swap; font-weight: 400; src: url(https://cdn.jsdelivr.net/fontsource/fonts/google-sans-flex@latest/latin-400-normal.woff2) format('woff2'); }
|
||||
@font-face { font-family: 'Google Sans Flex'; font-style: normal; font-display: swap; font-weight: 500; src: url(https://cdn.jsdelivr.net/fontsource/fonts/google-sans-flex@latest/latin-500-normal.woff2) format('woff2'); }
|
||||
@font-face { font-family: 'Google Sans Flex'; font-style: normal; font-display: swap; font-weight: 600; src: url(https://cdn.jsdelivr.net/fontsource/fonts/google-sans-flex@latest/latin-600-normal.woff2) format('woff2'); }
|
||||
@font-face { font-family: 'Google Sans Flex'; font-style: normal; font-display: swap; font-weight: 700; src: url(https://cdn.jsdelivr.net/fontsource/fonts/google-sans-flex@latest/latin-700-normal.woff2) format('woff2'); }
|
||||
@font-face { font-family: 'Google Sans Flex'; font-style: normal; font-display: swap; font-weight: 800; src: url(https://cdn.jsdelivr.net/fontsource/fonts/google-sans-flex@latest/latin-800-normal.woff2) format('woff2'); }
|
||||
</style>
|
||||
|
||||
<style>
|
||||
/* ── M3 AMOLED surface ramp ── */
|
||||
:root {
|
||||
--green: #1DB954;
|
||||
--green-dim: #1aa34a;
|
||||
--bg: #0a0a0a; /* surfaceContainerLow */
|
||||
--bg-card: #1a1a1a; /* surfaceContainerHigh */
|
||||
--bg-card-hover: #222222; /* surfaceContainerHighest */
|
||||
--surface: #121212; /* surfaceContainer */
|
||||
--text: #e8e8e8; /* onSurface */
|
||||
--text-dim: #999; /* onSurfaceVariant */
|
||||
--max-w: 1100px;
|
||||
}
|
||||
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
html { scroll-behavior: smooth; }
|
||||
|
||||
body {
|
||||
font-family: 'Google Sans Flex', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
background: var(--bg); color: var(--text); line-height: 1.6;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
a { color: var(--green); text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
|
||||
/* ── NAV ── */
|
||||
nav {
|
||||
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
|
||||
background: rgba(18,18,18,.78);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
}
|
||||
.nav-inner {
|
||||
max-width: var(--max-w); margin: auto;
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 0 24px; height: 64px;
|
||||
}
|
||||
.nav-brand { display: flex; align-items: center; gap: 10px; font-weight: 700; font-size: 1.1rem; color: var(--text); }
|
||||
.nav-brand img { width: 32px; height: 32px; border-radius: 50%; }
|
||||
.nav-links { display: flex; gap: 24px; list-style: none; }
|
||||
.nav-links a { color: var(--text-dim); font-size: .9rem; transition: color .2s; }
|
||||
.nav-links a:hover { color: var(--text); text-decoration: none; }
|
||||
.nav-links a.active { color: var(--text); font-weight: 600; }
|
||||
.nav-links .nav-icon { display: flex; align-items: center; opacity: .6; transition: opacity .2s; margin-left: -12px; }
|
||||
.nav-links .nav-icon:hover { opacity: 1; }
|
||||
.nav-links .nav-icon svg { width: 24px; height: 24px; fill: currentColor; }
|
||||
.nav-links .nav-divider { width: 1px; height: 20px; background: rgba(255,255,255,.15); margin-left: -4px; }
|
||||
|
||||
/* ── HERO ── */
|
||||
.hero {
|
||||
min-height: 100vh;
|
||||
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
||||
text-align: center; padding: 100px 24px 60px;
|
||||
background: radial-gradient(ellipse at 50% 0%, rgba(29,185,84,.05) 0%, transparent 50%);
|
||||
}
|
||||
.hero h1 { font-size: clamp(2.2rem, 5vw, 3.5rem); font-weight: 800; letter-spacing: -1px; margin-bottom: 12px; }
|
||||
.hero h1 span { color: var(--green); }
|
||||
.hero p { font-size: 1.15rem; color: var(--text-dim); max-width: 520px; margin-bottom: 8px; }
|
||||
.hero-badges { display: flex; gap: 8px; justify-content: center; margin: 16px 0 32px; flex-wrap: wrap; }
|
||||
.badge {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 6px 16px; border-radius: 999px;
|
||||
font-size: .8rem; font-weight: 600;
|
||||
background: var(--surface); color: var(--text-dim);
|
||||
}
|
||||
.hero-actions { display: flex; gap: 12px; flex-wrap: wrap; justify-content: center; }
|
||||
.btn {
|
||||
display: inline-flex; align-items: center; gap: 8px;
|
||||
padding: 12px 28px; border-radius: 16px;
|
||||
font-size: .95rem; font-weight: 600;
|
||||
transition: background .2s; cursor: pointer; border: none;
|
||||
}
|
||||
.btn-primary { background: var(--green); color: #000; }
|
||||
.btn-primary:hover { background: var(--green-dim); text-decoration: none; }
|
||||
.btn-secondary { background: var(--bg-card); color: var(--text); }
|
||||
.btn-secondary:hover { background: var(--bg-card-hover); text-decoration: none; }
|
||||
|
||||
/* ── SECTIONS ── */
|
||||
section { padding: 80px 24px; }
|
||||
.section-inner { max-width: var(--max-w); margin: auto; }
|
||||
.section-title { font-size: 1.8rem; font-weight: 700; text-align: center; margin-bottom: 12px; }
|
||||
.section-sub { text-align: center; color: var(--text-dim); max-width: 560px; margin: 0 auto 48px; }
|
||||
|
||||
/* ── FEATURES ── */
|
||||
.features-grid {
|
||||
display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
.feature-card {
|
||||
background: var(--bg-card); border-radius: 20px;
|
||||
padding: 28px 24px; transition: background .2s;
|
||||
}
|
||||
.feature-card:hover { background: var(--bg-card-hover); }
|
||||
.feature-icon {
|
||||
width: 40px; height: 40px; border-radius: 12px;
|
||||
background: rgba(29,185,84,.12); color: var(--green);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
margin-bottom: 16px; font-size: 1.2rem;
|
||||
}
|
||||
.feature-card h3 { font-size: 1.05rem; margin-bottom: 6px; }
|
||||
.feature-card p { color: var(--text-dim); font-size: .9rem; }
|
||||
|
||||
/* ── FAQ ── */
|
||||
.faq-list { max-width: 700px; margin: auto; display: flex; flex-direction: column; gap: 8px; }
|
||||
.faq-item {
|
||||
background: var(--bg-card); border-radius: 16px;
|
||||
}
|
||||
.faq-item summary {
|
||||
cursor: pointer; font-weight: 600; font-size: 1rem;
|
||||
list-style: none; display: flex; justify-content: space-between; align-items: center;
|
||||
padding: 18px 20px;
|
||||
}
|
||||
.faq-item summary::-webkit-details-marker { display: none; }
|
||||
.faq-item summary::after { content: "+"; font-size: 1.4rem; color: var(--text-dim); transition: transform .2s; }
|
||||
.faq-item[open] summary::after { content: "\2212"; }
|
||||
.faq-item .faq-answer { padding: 0 20px 18px; color: var(--text-dim); font-size: .92rem; line-height: 1.7; }
|
||||
|
||||
/* ── FOOTER ── */
|
||||
footer {
|
||||
background: var(--surface);
|
||||
padding: 40px 24px; text-align: center;
|
||||
}
|
||||
.footer-inner { max-width: var(--max-w); margin: auto; }
|
||||
.footer-links { display: flex; gap: 24px; justify-content: center; flex-wrap: wrap; margin-bottom: 16px; }
|
||||
.footer-links a { color: var(--text-dim); font-size: .9rem; }
|
||||
.footer-links a:hover { color: var(--text); }
|
||||
.footer-copy { color: #555; font-size: .8rem; }
|
||||
|
||||
/* ── MOBILE MENU ── */
|
||||
.nav-burger {
|
||||
display: none; width: 40px; height: 40px; border-radius: 12px;
|
||||
background: none; border: none; cursor: pointer;
|
||||
align-items: center; justify-content: center; flex-shrink: 0;
|
||||
position: relative;
|
||||
}
|
||||
.nav-burger .bar {
|
||||
display: block; width: 20px; height: 2px; background: var(--text);
|
||||
border-radius: 2px; transition: transform .3s cubic-bezier(.4,0,.2,1), opacity .2s;
|
||||
position: absolute; left: 10px;
|
||||
}
|
||||
.nav-burger .bar:nth-child(1) { top: 12px; }
|
||||
.nav-burger .bar:nth-child(2) { top: 19px; }
|
||||
.nav-burger .bar:nth-child(3) { top: 26px; }
|
||||
.nav-burger.active .bar:nth-child(1) { top: 19px; transform: rotate(45deg); }
|
||||
.nav-burger.active .bar:nth-child(2) { opacity: 0; }
|
||||
.nav-burger.active .bar:nth-child(3) { top: 19px; transform: rotate(-45deg); }
|
||||
.mobile-overlay {
|
||||
position: fixed; top: 64px; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0,0,0,.5); z-index: 98;
|
||||
opacity: 0; pointer-events: none;
|
||||
transition: opacity .3s cubic-bezier(.4,0,.2,1);
|
||||
}
|
||||
.mobile-overlay.open { opacity: 1; pointer-events: auto; }
|
||||
.mobile-menu {
|
||||
position: fixed; top: 64px; left: 0; right: 0;
|
||||
background: rgba(18,18,18,.95); padding: 8px 16px 16px; z-index: 99;
|
||||
backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
|
||||
transform: translateY(-8px); opacity: 0; pointer-events: none;
|
||||
transition: transform .3s cubic-bezier(.4,0,.2,1), opacity .3s cubic-bezier(.4,0,.2,1);
|
||||
}
|
||||
.mobile-menu.open { transform: translateY(0); opacity: 1; pointer-events: auto; }
|
||||
.mobile-menu a {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
padding: 14px 16px; border-radius: 12px;
|
||||
color: var(--text-dim); font-size: .95rem; font-weight: 500;
|
||||
transition: background .2s; opacity: 0; transform: translateY(-6px);
|
||||
}
|
||||
.mobile-menu.open a {
|
||||
opacity: 1; transform: translateY(0);
|
||||
transition: background .2s, opacity .3s cubic-bezier(.4,0,.2,1), transform .3s cubic-bezier(.4,0,.2,1);
|
||||
}
|
||||
.mobile-menu.open a:nth-child(1) { transition-delay: .03s; }
|
||||
.mobile-menu.open a:nth-child(2) { transition-delay: .06s; }
|
||||
.mobile-menu.open a:nth-child(3) { transition-delay: .09s; }
|
||||
.mobile-menu.open a:nth-child(4) { transition-delay: .12s; }
|
||||
.mobile-menu.open a:nth-child(5) { transition-delay: .15s; }
|
||||
.mobile-menu a:hover { background: var(--bg-card); color: var(--text); text-decoration: none; }
|
||||
.mobile-menu a.active { color: var(--text); font-weight: 600; background: var(--bg-card); }
|
||||
.mobile-menu .mobile-divider {
|
||||
height: 1px; background: rgba(255,255,255,.06); margin: 4px 0;
|
||||
opacity: 0; transition: opacity .3s .15s;
|
||||
}
|
||||
.mobile-menu.open .mobile-divider { opacity: 1; }
|
||||
.mobile-menu .mobile-icons {
|
||||
display: flex; gap: 8px; padding: 8px 16px 0;
|
||||
opacity: 0; transform: translateY(-6px);
|
||||
transition: opacity .3s cubic-bezier(.4,0,.2,1) .18s, transform .3s cubic-bezier(.4,0,.2,1) .18s;
|
||||
}
|
||||
.mobile-menu.open .mobile-icons { opacity: 1; transform: translateY(0); }
|
||||
.mobile-menu .mobile-icons a {
|
||||
padding: 10px; border-radius: 12px; background: var(--bg-card);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
opacity: 1; transform: none;
|
||||
}
|
||||
.mobile-menu .mobile-icons a svg { width: 20px; height: 20px; fill: currentColor; }
|
||||
|
||||
/* ── MOBILE ── */
|
||||
@media (max-width: 640px) {
|
||||
.nav-links { display: none; }
|
||||
.nav-burger { display: flex; }
|
||||
.hero { padding: 80px 16px 40px; }
|
||||
section { padding: 60px 16px; }
|
||||
}
|
||||
|
||||
/* ── HERO MOCKUPS ── */
|
||||
.hero-mockups {
|
||||
display: flex; gap: 20px; justify-content: center; align-items: flex-end;
|
||||
margin-top: 48px; perspective: 800px;
|
||||
}
|
||||
.phone-frame {
|
||||
width: 180px; border-radius: 28px; overflow: hidden;
|
||||
border: 3px solid #333; background: #000;
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,.5);
|
||||
transition: transform .3s;
|
||||
}
|
||||
.phone-frame:hover { transform: translateY(-4px); }
|
||||
.phone-frame img { width: 100%; display: block; }
|
||||
.phone-frame.phone-center {
|
||||
width: 210px;
|
||||
border-color: var(--green);
|
||||
box-shadow: 0 24px 70px rgba(0,0,0,.6), 0 0 40px rgba(29,185,84,.1);
|
||||
}
|
||||
.phone-frame.phone-side { opacity: .7; }
|
||||
@media (max-width: 640px) {
|
||||
.hero-mockups { gap: 10px; margin-top: 32px; }
|
||||
.phone-frame { width: 120px; border-radius: 20px; border-width: 2px; }
|
||||
.phone-frame.phone-center { width: 150px; }
|
||||
}
|
||||
@media (max-width: 420px) {
|
||||
.phone-frame.phone-side { display: none; }
|
||||
.phone-frame.phone-center { width: 200px; }
|
||||
}
|
||||
|
||||
/* ── SVG ICONS ── */
|
||||
.icon-svg { width: 20px; height: 20px; fill: currentColor; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- NAV -->
|
||||
<nav>
|
||||
<div class="nav-inner">
|
||||
<a class="nav-brand" href="#">
|
||||
<img src="icon.png" alt="SpotiFLAC">
|
||||
SpotiFLAC
|
||||
</a>
|
||||
<ul class="nav-links">
|
||||
<li><a href="#features">Features</a></li>
|
||||
<li><a href="downloads.html">Downloads</a></li>
|
||||
<li><a href="#faq">FAQ</a></li>
|
||||
<li><a href="partners.html">Partners</a></li>
|
||||
<li><a href="https://zarz.moe/docs" target="_blank">Docs</a></li>
|
||||
<li class="nav-divider"></li>
|
||||
<li><a href="https://github.com/zarzet/SpotiFLAC-Mobile" target="_blank" class="nav-icon" aria-label="GitHub"><svg viewBox="0 0 24 24"><path d="M12 .3a12 12 0 0 0-3.8 23.38c.6.12.82-.26.82-.57L9 20.86c-3.34.72-4.04-1.61-4.04-1.61-.55-1.39-1.34-1.76-1.34-1.76-1.09-.74.08-.73.08-.73 1.2.09 1.84 1.24 1.84 1.24 1.07 1.84 2.81 1.3 3.5 1 .1-.78.42-1.31.76-1.61-2.67-.3-5.47-1.33-5.47-5.93 0-1.31.47-2.38 1.24-3.22-.13-.3-.54-1.52.12-3.18 0 0 1-.32 3.3 1.23a11.5 11.5 0 0 1 6.02 0c2.28-1.55 3.29-1.23 3.29-1.23.66 1.66.25 2.88.12 3.18.77.84 1.24 1.91 1.24 3.22 0 4.61-2.81 5.63-5.48 5.92.43.37.81 1.1.81 2.22l-.01 3.29c0 .31.21.69.82.57A12 12 0 0 0 12 .3"/></svg></a></li>
|
||||
<li><a href="https://t.me/spotiflac" target="_blank" class="nav-icon" aria-label="Telegram"><svg viewBox="0 0 24 24"><path d="M11.94 24c6.6 0 12-5.4 12-12s-5.4-12-12-12-12 5.4-12 12 5.4 12 12 12zm-3.2-8.69l-.37-3.04 8.52-5.18c.38-.23.73.09.45.35l-6.96 6.4-.29 2.97c-.04.35-.48.43-.64.12l-1.64-3.33-3.6-1.17c-.78-.24-.8-.78-.02-1.14l14.04-5.4c.65-.25 1.25.15 1.04.83l-2.39 11.28c-.18.81-.7 1.01-1.42.63l-3.92-2.89-1.89 1.82c-.21.2-.39.38-.65.38l.28-3.06z"/></svg></a></li>
|
||||
</ul>
|
||||
<button class="nav-burger" onclick="toggleMenu()" aria-label="Menu">
|
||||
<span class="bar"></span><span class="bar"></span><span class="bar"></span>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- MOBILE MENU -->
|
||||
<div class="mobile-overlay" id="mobileOverlay" onclick="toggleMenu()"></div>
|
||||
<div class="mobile-menu" id="mobileMenu">
|
||||
<a href="#features">Features</a>
|
||||
<a href="downloads.html">Downloads</a>
|
||||
<a href="#faq">FAQ</a>
|
||||
<a href="partners.html">Partners</a>
|
||||
<a href="https://zarz.moe/docs" target="_blank">Docs</a>
|
||||
<div class="mobile-divider"></div>
|
||||
<div class="mobile-icons">
|
||||
<a href="https://github.com/zarzet/SpotiFLAC-Mobile" target="_blank" aria-label="GitHub">
|
||||
<svg viewBox="0 0 24 24"><path d="M12 .3a12 12 0 00-3.8 23.4c.6.1.8-.3.8-.6v-2c-3.3.7-4-1.6-4-1.6-.5-1.4-1.3-1.8-1.3-1.8-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.8 1.3 3.5 1 .1-.8.4-1.3.7-1.6-2.7-.3-5.5-1.3-5.5-6 0-1.3.5-2.3 1.2-3.2-.1-.3-.5-1.5.1-3.2 0 0 1-.3 3.4 1.2a11.5 11.5 0 016 0c2.3-1.5 3.3-1.2 3.3-1.2.7 1.7.3 2.9.1 3.2.8.8 1.2 1.9 1.2 3.2 0 4.6-2.8 5.6-5.5 5.9.4.4.8 1.1.8 2.2v3.3c0 .3.2.7.8.6A12 12 0 0012 .3z"/></svg>
|
||||
</a>
|
||||
<a href="https://t.me/spotiflac" target="_blank" aria-label="Telegram">
|
||||
<svg viewBox="0 0 24 24"><path d="M11.94 24c6.6 0 12-5.4 12-12s-5.4-12-12-12-12 5.4-12 12 5.4 12 12 12zm-3.2-8.69l-.37-3.04 8.52-5.18c.38-.23.73.09.45.35l-6.96 6.4-.29 2.97c-.04.35-.48.43-.64.12l-1.64-3.33-3.6-1.17c-.78-.24-.8-.78-.02-1.14l14.04-5.4c.65-.25 1.25.15 1.04.83l-2.39 11.28c-.18.81-.7 1.01-1.42.63l-3.92-2.89-1.89 1.82c-.21.2-.39.38-.65.38l.28-3.06z"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- HERO -->
|
||||
<section class="hero">
|
||||
<h1>Spoti<span>FLAC</span> Mobile</h1>
|
||||
<p>Download music in true lossless FLAC from Tidal, Qobuz & Amazon Music — no account required.</p>
|
||||
<div class="hero-badges">
|
||||
<span class="badge">
|
||||
<svg class="icon-svg" viewBox="0 0 24 24"><path d="M6 18c0 .55.45 1 1 1h1v3.5c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5V19h2v3.5c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5V19h1c.55 0 1-.45 1-1V7H6v11zM3.5 7C2.67 7 2 7.67 2 8.5v7c0 .83.67 1.5 1.5 1.5S5 16.33 5 15.5v-7C5 7.67 4.33 7 3.5 7zm17 0c-.83 0-1.5.67-1.5 1.5v7c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5v-7c0-.83-.67-1.5-1.5-1.5zm-4.97-5.84l1.3-1.3c.2-.2.2-.51 0-.71-.2-.2-.51-.2-.71 0l-1.48 1.48A5.84 5.84 0 0012 0c-.96 0-1.86.23-2.66.63L7.85.15c-.2-.2-.51-.2-.71 0-.2.2-.2.51 0 .71l1.31 1.31A5.983 5.983 0 006 6h12c0-2.21-1.2-4.15-2.97-5.18-.25-.14-.4-.24-.5-.36v-.3zM10 4H9V3h1v1zm5 0h-1V3h1v1z"/></svg>
|
||||
Android 7.0+
|
||||
</span>
|
||||
<span class="badge">
|
||||
<svg class="icon-svg" viewBox="0 0 24 24"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>
|
||||
iOS 14.0+
|
||||
</span>
|
||||
<span class="badge">
|
||||
<svg class="icon-svg" viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></svg>
|
||||
Open Source
|
||||
</span>
|
||||
</div>
|
||||
<div class="hero-actions">
|
||||
<a class="btn btn-primary" href="downloads.html">
|
||||
<svg class="icon-svg" viewBox="0 0 24 24"><path fill="#000" d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/></svg>
|
||||
Download
|
||||
</a>
|
||||
<a class="btn btn-secondary" href="https://github.com/zarzet/SpotiFLAC-Mobile" target="_blank">
|
||||
<svg class="icon-svg" viewBox="0 0 24 24"><path fill="currentColor" d="M12 .3a12 12 0 00-3.8 23.4c.6.1.8-.3.8-.6v-2c-3.3.7-4-1.6-4-1.6-.5-1.4-1.3-1.8-1.3-1.8-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.8 1.3 3.5 1 .1-.8.4-1.3.7-1.6-2.7-.3-5.5-1.3-5.5-6 0-1.3.5-2.3 1.2-3.2-.1-.3-.5-1.5.1-3.2 0 0 1-.3 3.4 1.2a11.5 11.5 0 016 0c2.3-1.5 3.3-1.2 3.3-1.2.7 1.7.3 2.9.1 3.2.8.8 1.2 1.9 1.2 3.2 0 4.6-2.8 5.6-5.5 5.9.4.4.8 1.1.8 2.2v3.3c0 .3.2.7.8.6A12 12 0 0012 .3z"/></svg>
|
||||
GitHub
|
||||
</a>
|
||||
</div>
|
||||
<div class="hero-mockups">
|
||||
<div class="phone-frame phone-side"><img src="images/2.jpg" alt="Search" loading="lazy"></div>
|
||||
<div class="phone-frame phone-center"><img src="images/1.jpg" alt="Home screen" loading="lazy"></div>
|
||||
<div class="phone-frame phone-side"><img src="images/3.jpg" alt="History" loading="lazy"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- FEATURES -->
|
||||
<section id="features">
|
||||
<div class="section-inner">
|
||||
<h2 class="section-title">Features</h2>
|
||||
<p class="section-sub">Everything you need to build a high-quality music library on your phone.</p>
|
||||
<div class="features-grid">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<svg class="icon-svg" viewBox="0 0 24 24"><path fill="currentColor" d="M12 3v10.55c-.59-.34-1.27-.55-2-.55C7.79 13 6 14.79 6 17s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z"/></svg>
|
||||
</div>
|
||||
<h3>True Lossless FLAC</h3>
|
||||
<p>Download in up to 24-bit/192kHz quality. No transcoding, no quality loss. Pure studio-grade audio files.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<svg class="icon-svg" viewBox="0 0 24 24"><path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></svg>
|
||||
</div>
|
||||
<h3>Multiple Providers</h3>
|
||||
<p>Download from Tidal, Qobuz, Amazon Music, and more. Automatic fallback if a source is unavailable.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<svg class="icon-svg" viewBox="0 0 24 24"><path fill="currentColor" d="M20.5 11H19V7c0-1.1-.9-2-2-2h-4V3.5a2.5 2.5 0 00-5 0V5H4c-1.1 0-2 .9-2 2v3.8h1.5c1.49 0 2.7 1.21 2.7 2.7s-1.21 2.7-2.7 2.7H2V20c0 1.1.9 2 2 2h3.8v-1.5c0-1.49 1.21-2.7 2.7-2.7s2.7 1.21 2.7 2.7V22H17c1.1 0 2-.9 2-2v-4h1.5a2.5 2.5 0 000-5z"/></svg>
|
||||
</div>
|
||||
<h3>Extensions</h3>
|
||||
<p>Community-built extensions add new music sources and features. Install from the built-in Store with one tap.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<svg class="icon-svg" viewBox="0 0 24 24"><path fill="currentColor" d="M15.5 14h-.79l-.28-.27A6.47 6.47 0 0016 9.5 6.5 6.5 0 109.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
|
||||
</div>
|
||||
<h3>Search by Link or Name</h3>
|
||||
<p>Paste a Spotify, Tidal, Qobuz, or Deezer link. Or just search by song name — it handles the rest.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<svg class="icon-svg" viewBox="0 0 24 24"><path fill="currentColor" d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H8V4h12v12zm-6-1l-4-4.8h3V5h2v4.2h3L14 14z"/></svg>
|
||||
</div>
|
||||
<h3>Batch & Playlist Download</h3>
|
||||
<p>Download entire albums and playlists at once. Smart queue management with concurrent downloads.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<svg class="icon-svg" viewBox="0 0 24 24"><path fill="currentColor" d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h18v14zM5 15h14v3H5z"/></svg>
|
||||
</div>
|
||||
<h3>Rich Metadata</h3>
|
||||
<p>Full metadata embedding — album art, lyrics, genre, label, copyright, and more. All embedded in the FLAC file.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- FAQ -->
|
||||
<section id="faq">
|
||||
<div class="section-inner">
|
||||
<h2 class="section-title">FAQ</h2>
|
||||
<p class="section-sub">Common questions about SpotiFLAC Mobile.</p>
|
||||
<div class="faq-list">
|
||||
<details class="faq-item">
|
||||
<summary>Why is my download failing with "Song not found"?</summary>
|
||||
<div class="faq-answer">The track may not be available on Tidal, Qobuz, or Amazon Music. Try enabling more download services in Settings > Download > Provider Priority, or install additional extensions from the Store.</div>
|
||||
</details>
|
||||
<details class="faq-item">
|
||||
<summary>Why are some tracks downloading in lower quality?</summary>
|
||||
<div class="faq-answer">Quality depends on what's available from the streaming service. Tidal offers up to 24-bit/192kHz, Qobuz up to 24-bit/192kHz, and Amazon up to 24-bit/48kHz. The app automatically selects the best available quality.</div>
|
||||
</details>
|
||||
<details class="faq-item">
|
||||
<summary>Can I download entire playlists?</summary>
|
||||
<div class="faq-answer">Yes! Just paste the playlist URL in the search bar. The app will fetch all tracks and queue them for download.</div>
|
||||
</details>
|
||||
<details class="faq-item">
|
||||
<summary>Why do I need to grant storage permission?</summary>
|
||||
<div class="faq-answer">The app needs permission to save downloaded files to your device. On Android 13+, you may need to grant "All files access" in Settings > Apps > SpotiFLAC > Permissions.</div>
|
||||
</details>
|
||||
<details class="faq-item">
|
||||
<summary>Is this app safe?</summary>
|
||||
<div class="faq-answer">Yes, the app is fully open source. You can verify the code yourself on GitHub. Each release is scanned with VirusTotal.</div>
|
||||
</details>
|
||||
<details class="faq-item">
|
||||
<summary>Download not working in my country?</summary>
|
||||
<div class="faq-answer">Some countries have restricted access to certain streaming service APIs. If downloads are failing, try using a VPN to connect through a different region.</div>
|
||||
</details>
|
||||
<details class="faq-item">
|
||||
<summary>How do I create my own extension?</summary>
|
||||
<div class="faq-answer">Check out the <a href="https://zarz.moe/docs" target="_blank">Extension Development Guide</a> for complete documentation on building custom extensions.</div>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<footer>
|
||||
<div class="footer-inner">
|
||||
<div class="footer-links">
|
||||
<a href="https://github.com/zarzet/SpotiFLAC-Mobile/releases" target="_blank">Download</a>
|
||||
<a href="https://zarz.moe/docs" target="_blank">Documentation</a>
|
||||
<a href="https://github.com/zarzet/SpotiFLAC-Mobile" target="_blank">GitHub</a>
|
||||
<a href="https://github.com/afkarxyz/SpotiFLAC" target="_blank">Desktop Version</a>
|
||||
<a href="https://t.me/spotiflac" target="_blank">Telegram Channel</a>
|
||||
<a href="https://t.me/spotiflac_chat" target="_blank">Community</a>
|
||||
<a href="https://ko-fi.com/zarzet" target="_blank">Support / Ko-fi</a>
|
||||
<a href="https://crowdin.com/project/spotiflac-mobile" target="_blank">Help Translate</a>
|
||||
</div>
|
||||
<p class="footer-copy">SpotiFLAC is for educational and private use only. Not affiliated with any streaming service.</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
<script>
|
||||
function toggleMenu() {
|
||||
document.getElementById('mobileMenu').classList.toggle('open');
|
||||
document.getElementById('mobileOverlay').classList.toggle('open');
|
||||
document.querySelector('.nav-burger').classList.toggle('active');
|
||||
}
|
||||
document.getElementById('mobileMenu').addEventListener('click', function(e) {
|
||||
if (e.target.closest('a')) toggleMenu();
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,516 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Partners & Services - SpotiFLAC Mobile</title>
|
||||
<meta name="description" content="The APIs and services that power SpotiFLAC Mobile. Giving credit to the platforms that make lossless music downloads possible.">
|
||||
<meta name="theme-color" content="#0a0a0a">
|
||||
<link rel="icon" href="icon.png" type="image/png">
|
||||
|
||||
<!-- Google Sans Flex -->
|
||||
<style>
|
||||
@font-face { font-family: 'Google Sans Flex'; font-style: normal; font-display: swap; font-weight: 400; src: url(https://cdn.jsdelivr.net/fontsource/fonts/google-sans-flex@latest/latin-400-normal.woff2) format('woff2'); }
|
||||
@font-face { font-family: 'Google Sans Flex'; font-style: normal; font-display: swap; font-weight: 500; src: url(https://cdn.jsdelivr.net/fontsource/fonts/google-sans-flex@latest/latin-500-normal.woff2) format('woff2'); }
|
||||
@font-face { font-family: 'Google Sans Flex'; font-style: normal; font-display: swap; font-weight: 600; src: url(https://cdn.jsdelivr.net/fontsource/fonts/google-sans-flex@latest/latin-600-normal.woff2) format('woff2'); }
|
||||
@font-face { font-family: 'Google Sans Flex'; font-style: normal; font-display: swap; font-weight: 700; src: url(https://cdn.jsdelivr.net/fontsource/fonts/google-sans-flex@latest/latin-700-normal.woff2) format('woff2'); }
|
||||
@font-face { font-family: 'Google Sans Flex'; font-style: normal; font-display: swap; font-weight: 800; src: url(https://cdn.jsdelivr.net/fontsource/fonts/google-sans-flex@latest/latin-800-normal.woff2) format('woff2'); }
|
||||
</style>
|
||||
|
||||
<style>
|
||||
/* ── M3 AMOLED surface ramp ── */
|
||||
:root {
|
||||
--green: #1DB954;
|
||||
--green-dim: #1aa34a;
|
||||
--bg: #0a0a0a;
|
||||
--bg-card: #1a1a1a;
|
||||
--bg-card-hover: #222222;
|
||||
--surface: #121212;
|
||||
--text: #e8e8e8;
|
||||
--text-dim: #999;
|
||||
--max-w: 1100px;
|
||||
}
|
||||
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
html { scroll-behavior: smooth; }
|
||||
|
||||
body {
|
||||
font-family: 'Google Sans Flex', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
background: var(--bg); color: var(--text); line-height: 1.6;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
a { color: var(--green); text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
|
||||
/* ── NAV ── */
|
||||
nav {
|
||||
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
|
||||
background: rgba(18,18,18,.78);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
}
|
||||
.nav-inner {
|
||||
max-width: var(--max-w); margin: auto;
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 0 24px; height: 64px;
|
||||
}
|
||||
.nav-brand { display: flex; align-items: center; gap: 10px; font-weight: 700; font-size: 1.1rem; color: var(--text); }
|
||||
.nav-brand img { width: 32px; height: 32px; border-radius: 50%; }
|
||||
.nav-links { display: flex; gap: 24px; list-style: none; }
|
||||
.nav-links a { color: var(--text-dim); font-size: .9rem; transition: color .2s; }
|
||||
.nav-links a:hover { color: var(--text); text-decoration: none; }
|
||||
.nav-links a.active { color: var(--text); font-weight: 600; }
|
||||
.nav-links .nav-icon { display: flex; align-items: center; opacity: .6; transition: opacity .2s; margin-left: -12px; }
|
||||
.nav-links .nav-icon:hover { opacity: 1; }
|
||||
.nav-links .nav-icon svg { width: 24px; height: 24px; fill: currentColor; }
|
||||
.nav-links .nav-divider { width: 1px; height: 20px; background: rgba(255,255,255,.15); margin-left: -4px; }
|
||||
|
||||
/* ── PAGE HEADER ── */
|
||||
.page-header {
|
||||
padding: 100px 24px 40px; text-align: center;
|
||||
}
|
||||
.page-header h1 { font-size: 2rem; font-weight: 800; margin-bottom: 8px; }
|
||||
.page-header p { color: var(--text-dim); font-size: 1rem; max-width: 560px; margin: 0 auto; }
|
||||
|
||||
/* ── SECTIONS ── */
|
||||
section { padding: 40px 24px 60px; }
|
||||
.section-inner { max-width: var(--max-w); margin: auto; }
|
||||
.section-label {
|
||||
font-size: .85rem; font-weight: 600;
|
||||
color: var(--green); margin-bottom: 8px;
|
||||
}
|
||||
.section-title { font-size: 1.5rem; font-weight: 700; margin-bottom: 8px; }
|
||||
.section-sub { color: var(--text-dim); font-size: .95rem; margin-bottom: 32px; max-width: 600px; }
|
||||
|
||||
/* ── INFRA CARDS ── */
|
||||
.infra-grid {
|
||||
display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
.infra-card {
|
||||
background: var(--bg-card);
|
||||
border-radius: 20px;
|
||||
padding: 24px;
|
||||
display: flex; align-items: flex-start; gap: 16px;
|
||||
transition: background .2s;
|
||||
}
|
||||
.infra-card:hover { background: var(--bg-card-hover); }
|
||||
.infra-icon {
|
||||
width: 48px; height: 48px; border-radius: 12px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.infra-icon svg { width: 24px; height: 24px; fill: currentColor; }
|
||||
.infra-info { flex: 1; min-width: 0; }
|
||||
.infra-name { font-size: 1.05rem; font-weight: 700; margin-bottom: 4px; }
|
||||
.infra-desc { color: var(--text-dim); font-size: .88rem; line-height: 1.6; margin-bottom: 10px; }
|
||||
.infra-link {
|
||||
font-size: .82rem; font-weight: 600; color: var(--text-dim);
|
||||
display: inline-flex; align-items: center; gap: 5px;
|
||||
transition: color .2s;
|
||||
}
|
||||
.infra-link:hover { color: var(--text); text-decoration: none; }
|
||||
.infra-link svg { width: 13px; height: 13px; fill: currentColor; }
|
||||
|
||||
/* ── FOOTER ── */
|
||||
footer {
|
||||
background: var(--surface);
|
||||
padding: 40px 24px; text-align: center;
|
||||
}
|
||||
.footer-inner { max-width: var(--max-w); margin: auto; }
|
||||
.footer-links { display: flex; gap: 24px; justify-content: center; flex-wrap: wrap; margin-bottom: 16px; }
|
||||
.footer-links a { color: var(--text-dim); font-size: .9rem; }
|
||||
.footer-links a:hover { color: var(--text); }
|
||||
.footer-copy { color: #555; font-size: .8rem; }
|
||||
|
||||
/* ── DISCLAIMER ── */
|
||||
.disclaimer {
|
||||
max-width: var(--max-w); margin: 0 auto; padding: 0 24px 60px;
|
||||
text-align: center;
|
||||
}
|
||||
.disclaimer p {
|
||||
color: #555; font-size: .8rem; line-height: 1.6;
|
||||
max-width: 600px; margin: 0 auto;
|
||||
padding: 20px; border-radius: 16px;
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
/* ── MOBILE MENU ── */
|
||||
.nav-burger {
|
||||
display: none; width: 40px; height: 40px; border-radius: 12px;
|
||||
background: none; border: none; cursor: pointer;
|
||||
align-items: center; justify-content: center; flex-shrink: 0;
|
||||
position: relative;
|
||||
}
|
||||
.nav-burger .bar {
|
||||
display: block; width: 20px; height: 2px; background: var(--text);
|
||||
border-radius: 2px; transition: transform .3s cubic-bezier(.4,0,.2,1), opacity .2s;
|
||||
position: absolute; left: 10px;
|
||||
}
|
||||
.nav-burger .bar:nth-child(1) { top: 12px; }
|
||||
.nav-burger .bar:nth-child(2) { top: 19px; }
|
||||
.nav-burger .bar:nth-child(3) { top: 26px; }
|
||||
.nav-burger.active .bar:nth-child(1) { top: 19px; transform: rotate(45deg); }
|
||||
.nav-burger.active .bar:nth-child(2) { opacity: 0; }
|
||||
.nav-burger.active .bar:nth-child(3) { top: 19px; transform: rotate(-45deg); }
|
||||
.mobile-overlay {
|
||||
position: fixed; top: 64px; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0,0,0,.5); z-index: 98;
|
||||
opacity: 0; pointer-events: none;
|
||||
transition: opacity .3s cubic-bezier(.4,0,.2,1);
|
||||
}
|
||||
.mobile-overlay.open { opacity: 1; pointer-events: auto; }
|
||||
.mobile-menu {
|
||||
position: fixed; top: 64px; left: 0; right: 0;
|
||||
background: rgba(18,18,18,.95); padding: 8px 16px 16px; z-index: 99;
|
||||
backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
|
||||
transform: translateY(-8px); opacity: 0; pointer-events: none;
|
||||
transition: transform .3s cubic-bezier(.4,0,.2,1), opacity .3s cubic-bezier(.4,0,.2,1);
|
||||
}
|
||||
.mobile-menu.open { transform: translateY(0); opacity: 1; pointer-events: auto; }
|
||||
.mobile-menu a {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
padding: 14px 16px; border-radius: 12px;
|
||||
color: var(--text-dim); font-size: .95rem; font-weight: 500;
|
||||
transition: background .2s; opacity: 0; transform: translateY(-6px);
|
||||
}
|
||||
.mobile-menu.open a {
|
||||
opacity: 1; transform: translateY(0);
|
||||
transition: background .2s, opacity .3s cubic-bezier(.4,0,.2,1), transform .3s cubic-bezier(.4,0,.2,1);
|
||||
}
|
||||
.mobile-menu.open a:nth-child(1) { transition-delay: .03s; }
|
||||
.mobile-menu.open a:nth-child(2) { transition-delay: .06s; }
|
||||
.mobile-menu.open a:nth-child(3) { transition-delay: .09s; }
|
||||
.mobile-menu.open a:nth-child(4) { transition-delay: .12s; }
|
||||
.mobile-menu.open a:nth-child(5) { transition-delay: .15s; }
|
||||
.mobile-menu a:hover { background: var(--bg-card); color: var(--text); text-decoration: none; }
|
||||
.mobile-menu a.active { color: var(--text); font-weight: 600; background: var(--bg-card); }
|
||||
.mobile-menu .mobile-divider {
|
||||
height: 1px; background: rgba(255,255,255,.06); margin: 4px 0;
|
||||
opacity: 0; transition: opacity .3s .15s;
|
||||
}
|
||||
.mobile-menu.open .mobile-divider { opacity: 1; }
|
||||
.mobile-menu .mobile-icons {
|
||||
display: flex; gap: 8px; padding: 8px 16px 0;
|
||||
opacity: 0; transform: translateY(-6px);
|
||||
transition: opacity .3s cubic-bezier(.4,0,.2,1) .18s, transform .3s cubic-bezier(.4,0,.2,1) .18s;
|
||||
}
|
||||
.mobile-menu.open .mobile-icons { opacity: 1; transform: translateY(0); }
|
||||
.mobile-menu .mobile-icons a {
|
||||
padding: 10px; border-radius: 12px; background: var(--bg-card);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
opacity: 1; transform: none;
|
||||
}
|
||||
.mobile-menu .mobile-icons a svg { width: 20px; height: 20px; fill: currentColor; }
|
||||
|
||||
/* ── MOBILE ── */
|
||||
@media (max-width: 640px) {
|
||||
.nav-links { display: none; }
|
||||
.nav-burger { display: flex; }
|
||||
.page-header { padding: 80px 16px 32px; }
|
||||
section { padding: 32px 16px 48px; }
|
||||
.infra-grid { grid-template-columns: 1fr; }
|
||||
.disclaimer { padding: 0 16px 48px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav>
|
||||
<div class="nav-inner">
|
||||
<a class="nav-brand" href="index.html">
|
||||
<img src="icon.png" alt="SpotiFLAC">
|
||||
SpotiFLAC
|
||||
</a>
|
||||
<ul class="nav-links">
|
||||
<li><a href="index.html#features">Features</a></li>
|
||||
<li><a href="downloads.html">Downloads</a></li>
|
||||
<li><a href="index.html#faq">FAQ</a></li>
|
||||
<li><a href="partners.html" class="active">Partners</a></li>
|
||||
<li><a href="https://zarz.moe/docs" target="_blank">Docs</a></li>
|
||||
<li class="nav-divider"></li>
|
||||
<li><a href="https://github.com/zarzet/SpotiFLAC-Mobile" target="_blank" class="nav-icon" aria-label="GitHub"><svg viewBox="0 0 24 24"><path d="M12 .3a12 12 0 0 0-3.8 23.38c.6.12.82-.26.82-.57L9 20.86c-3.34.72-4.04-1.61-4.04-1.61-.55-1.39-1.34-1.76-1.34-1.76-1.09-.74.08-.73.08-.73 1.2.09 1.84 1.24 1.84 1.24 1.07 1.84 2.81 1.3 3.5 1 .1-.78.42-1.31.76-1.61-2.67-.3-5.47-1.33-5.47-5.93 0-1.31.47-2.38 1.24-3.22-.13-.3-.54-1.52.12-3.18 0 0 1-.32 3.3 1.23a11.5 11.5 0 0 1 6.02 0c2.28-1.55 3.29-1.23 3.29-1.23.66 1.66.25 2.88.12 3.18.77.84 1.24 1.91 1.24 3.22 0 4.61-2.81 5.63-5.48 5.92.43.37.81 1.1.81 2.22l-.01 3.29c0 .31.21.69.82.57A12 12 0 0 0 12 .3"/></svg></a></li>
|
||||
<li><a href="https://t.me/spotiflac" target="_blank" class="nav-icon" aria-label="Telegram"><svg viewBox="0 0 24 24"><path d="M11.94 24c6.6 0 12-5.4 12-12s-5.4-12-12-12-12 5.4-12 12 5.4 12 12 12zm-3.2-8.69l-.37-3.04 8.52-5.18c.38-.23.73.09.45.35l-6.96 6.4-.29 2.97c-.04.35-.48.43-.64.12l-1.64-3.33-3.6-1.17c-.78-.24-.8-.78-.02-1.14l14.04-5.4c.65-.25 1.25.15 1.04.83l-2.39 11.28c-.18.81-.7 1.01-1.42.63l-3.92-2.89-1.89 1.82c-.21.2-.39.38-.65.38l.28-3.06z"/></svg></a></li>
|
||||
</ul>
|
||||
<button class="nav-burger" onclick="toggleMenu()" aria-label="Menu">
|
||||
<span class="bar"></span><span class="bar"></span><span class="bar"></span>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- MOBILE MENU -->
|
||||
<div class="mobile-overlay" id="mobileOverlay" onclick="toggleMenu()"></div>
|
||||
<div class="mobile-menu" id="mobileMenu">
|
||||
<a href="index.html#features">Features</a>
|
||||
<a href="downloads.html">Downloads</a>
|
||||
<a href="index.html#faq">FAQ</a>
|
||||
<a href="partners.html" class="active">Partners</a>
|
||||
<a href="https://zarz.moe/docs" target="_blank">Docs</a>
|
||||
<div class="mobile-divider"></div>
|
||||
<div class="mobile-icons">
|
||||
<a href="https://github.com/zarzet/SpotiFLAC-Mobile" target="_blank" aria-label="GitHub">
|
||||
<svg viewBox="0 0 24 24"><path d="M12 .3a12 12 0 00-3.8 23.4c.6.1.8-.3.8-.6v-2c-3.3.7-4-1.6-4-1.6-.5-1.4-1.3-1.8-1.3-1.8-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.8 1.3 3.5 1 .1-.8.4-1.3.7-1.6-2.7-.3-5.5-1.3-5.5-6 0-1.3.5-2.3 1.2-3.2-.1-.3-.5-1.5.1-3.2 0 0 1-.3 3.4 1.2a11.5 11.5 0 016 0c2.3-1.5 3.3-1.2 3.3-1.2.7 1.7.3 2.9.1 3.2.8.8 1.2 1.9 1.2 3.2 0 4.6-2.8 5.6-5.5 5.9.4.4.8 1.1.8 2.2v3.3c0 .3.2.7.8.6A12 12 0 0012 .3z"/></svg>
|
||||
</a>
|
||||
<a href="https://t.me/spotiflac" target="_blank" aria-label="Telegram">
|
||||
<svg viewBox="0 0 24 24"><path d="M11.94 24c6.6 0 12-5.4 12-12s-5.4-12-12-12-12 5.4-12 12 5.4 12 12 12zm-3.2-8.69l-.37-3.04 8.52-5.18c.38-.23.73.09.45.35l-6.96 6.4-.29 2.97c-.04.35-.48.43-.64.12l-1.64-3.33-3.6-1.17c-.78-.24-.8-.78-.02-1.14l14.04-5.4c.65-.25 1.25.15 1.04.83l-2.39 11.28c-.18.81-.7 1.01-1.42.63l-3.92-2.89-1.89 1.82c-.21.2-.39.38-.65.38l.28-3.06z"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-header">
|
||||
<h1>Partners & Services</h1>
|
||||
<p>The behind-the-scenes APIs and tools that power SpotiFLAC Mobile. We appreciate every one of them.</p>
|
||||
</div>
|
||||
|
||||
<!-- INFRASTRUCTURE -->
|
||||
<section>
|
||||
<div class="section-inner">
|
||||
<div class="section-label">Infrastructure</div>
|
||||
<h2 class="section-title">APIs & Tools</h2>
|
||||
<p class="section-sub">The services that handle link resolution, lyrics, audio extraction, and more.</p>
|
||||
|
||||
<div class="infra-grid">
|
||||
|
||||
<!-- === TRACK LINKING === -->
|
||||
|
||||
<!-- Odesli / song.link (no GitHub — globe) -->
|
||||
<div class="infra-card">
|
||||
<div class="infra-icon" style="background: rgba(99,102,241,.1); color: #6366f1;">
|
||||
<svg viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></svg>
|
||||
</div>
|
||||
<div class="infra-info">
|
||||
<div class="infra-name">Odesli / song.link</div>
|
||||
<div class="infra-desc">Cross-platform link resolution. Translates any Spotify, Deezer, or streaming URL into matching Tidal, Qobuz, Amazon, and YouTube IDs — enabling SpotiFLAC to find the best lossless source for every track.</div>
|
||||
<a class="infra-link" href="https://odesli.co" target="_blank">
|
||||
odesli.co
|
||||
<svg viewBox="0 0 24 24"><path d="M14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- I Don't Have Spotify (GitHub) -->
|
||||
<div class="infra-card">
|
||||
<div class="infra-icon" style="background: rgba(255,255,255,.08); color: #e8e8e8;">
|
||||
<svg viewBox="0 0 24 24"><path d="M12 .3a12 12 0 00-3.8 23.4c.6.1.8-.3.8-.6v-2c-3.3.7-4-1.6-4-1.6-.5-1.4-1.3-1.8-1.3-1.8-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.8 1.3 3.5 1 .1-.8.4-1.3.7-1.6-2.7-.3-5.5-1.3-5.5-6 0-1.3.5-2.3 1.2-3.2-.1-.3-.5-1.5.1-3.2 0 0 1-.3 3.4 1.2a11.5 11.5 0 016 0c2.3-1.5 3.3-1.2 3.3-1.2.7 1.7.3 2.9.1 3.2.8.8 1.2 1.9 1.2 3.2 0 4.6-2.8 5.6-5.5 5.9.4.4.8 1.1.8 2.2v3.3c0 .3.2.7.8.6A12 12 0 0012 .3z"/></svg>
|
||||
</div>
|
||||
<div class="infra-info">
|
||||
<div class="infra-name">I Don't Have Spotify</div>
|
||||
<div class="infra-desc">Fallback link resolution service. When Odesli is rate-limited or unavailable, IDHS provides an alternative way to match Spotify links to Tidal, Qobuz, and other streaming platforms.</div>
|
||||
<a class="infra-link" href="https://github.com/sjdonado/idonthavespotify" target="_blank">
|
||||
sjdonado/idonthavespotify
|
||||
<svg viewBox="0 0 24 24"><path d="M14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- LRCLIB (GitHub) -->
|
||||
<div class="infra-card">
|
||||
<div class="infra-icon" style="background: rgba(255,255,255,.08); color: #e8e8e8;">
|
||||
<svg viewBox="0 0 24 24"><path d="M12 .3a12 12 0 00-3.8 23.4c.6.1.8-.3.8-.6v-2c-3.3.7-4-1.6-4-1.6-.5-1.4-1.3-1.8-1.3-1.8-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.8 1.3 3.5 1 .1-.8.4-1.3.7-1.6-2.7-.3-5.5-1.3-5.5-6 0-1.3.5-2.3 1.2-3.2-.1-.3-.5-1.5.1-3.2 0 0 1-.3 3.4 1.2a11.5 11.5 0 016 0c2.3-1.5 3.3-1.2 3.3-1.2.7 1.7.3 2.9.1 3.2.8.8 1.2 1.9 1.2 3.2 0 4.6-2.8 5.6-5.5 5.9.4.4.8 1.1.8 2.2v3.3c0 .3.2.7.8.6A12 12 0 0012 .3z"/></svg>
|
||||
</div>
|
||||
<div class="infra-info">
|
||||
<div class="infra-name">LRCLIB</div>
|
||||
<div class="infra-desc">Open synced lyrics database. Provides time-stamped lyrics that get embedded directly into downloaded FLAC files, so your music player can display lyrics in sync with the music.</div>
|
||||
<a class="infra-link" href="https://github.com/tranxuanthang/lrclib" target="_blank">
|
||||
tranxuanthang/lrclib
|
||||
<svg viewBox="0 0 24 24"><path d="M14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- === TIDAL STREAM APIs === -->
|
||||
|
||||
<!-- hifi-api / Binimum (GitHub) -->
|
||||
<div class="infra-card">
|
||||
<div class="infra-icon" style="background: rgba(255,255,255,.08); color: #e8e8e8;">
|
||||
<svg viewBox="0 0 24 24"><path d="M12 .3a12 12 0 00-3.8 23.4c.6.1.8-.3.8-.6v-2c-3.3.7-4-1.6-4-1.6-.5-1.4-1.3-1.8-1.3-1.8-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.8 1.3 3.5 1 .1-.8.4-1.3.7-1.6-2.7-.3-5.5-1.3-5.5-6 0-1.3.5-2.3 1.2-3.2-.1-.3-.5-1.5.1-3.2 0 0 1-.3 3.4 1.2a11.5 11.5 0 016 0c2.3-1.5 3.3-1.2 3.3-1.2.7 1.7.3 2.9.1 3.2.8.8 1.2 1.9 1.2 3.2 0 4.6-2.8 5.6-5.5 5.9.4.4.8 1.1.8 2.2v3.3c0 .3.2.7.8.6A12 12 0 0012 .3z"/></svg>
|
||||
</div>
|
||||
<div class="infra-info">
|
||||
<div class="infra-name">hifi-api / Binimum</div>
|
||||
<div class="infra-desc">Primary Tidal lossless stream API. Accepts a track ID and quality parameter, returns hi-res download URLs and DASH manifests. Also deployed at music.binimum.org.</div>
|
||||
<a class="infra-link" href="https://github.com/binimum/hifi-api" target="_blank">
|
||||
binimum/hifi-api
|
||||
<svg viewBox="0 0 24 24"><path d="M14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- QQDL (no GitHub — globe) -->
|
||||
<div class="infra-card">
|
||||
<div class="infra-icon" style="background: rgba(244,63,94,.1); color: #f43f5e;">
|
||||
<svg viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></svg>
|
||||
</div>
|
||||
<div class="infra-info">
|
||||
<div class="infra-name">QQDL</div>
|
||||
<div class="infra-desc">Redundant Tidal API mirror cluster. Operates five parallel endpoints (vogel, maus, hund, katze, wolf) for high-availability lossless track downloads across the API pool.</div>
|
||||
<a class="infra-link" href="https://qqdl.site" target="_blank">
|
||||
qqdl.site
|
||||
<svg viewBox="0 0 24 24"><path d="M14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Squid (no GitHub — globe) -->
|
||||
<div class="infra-card">
|
||||
<div class="infra-icon" style="background: rgba(6,182,212,.1); color: #06b6d4;">
|
||||
<svg viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></svg>
|
||||
</div>
|
||||
<div class="infra-info">
|
||||
<div class="infra-name">Squid</div>
|
||||
<div class="infra-desc">Dual-purpose download API serving both Tidal and Qobuz streams. Supports multi-region retrieval (US/FR fallback for Qobuz) to maximize track availability across catalogs.</div>
|
||||
<a class="infra-link" href="https://squid.wtf" target="_blank">
|
||||
squid.wtf
|
||||
<svg viewBox="0 0 24 24"><path d="M14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SpotiSaver (no GitHub — globe) -->
|
||||
<div class="infra-card">
|
||||
<div class="infra-icon" style="background: rgba(245,158,11,.1); color: #f59e0b;">
|
||||
<svg viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></svg>
|
||||
</div>
|
||||
<div class="infra-info">
|
||||
<div class="infra-name">SpotiSaver</div>
|
||||
<div class="infra-desc">Tidal hi-fi download endpoints. Hosts two parallel instances (hifi-one, hifi-two) that provide additional redundancy in the 10-API parallel race pool.</div>
|
||||
<a class="infra-link" href="https://spotisaver.net" target="_blank">
|
||||
spotisaver.net
|
||||
<svg viewBox="0 0 24 24"><path d="M14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- === QOBUZ STREAM APIs === -->
|
||||
|
||||
<!-- DabMusic (no GitHub — globe) -->
|
||||
<div class="infra-card">
|
||||
<div class="infra-icon" style="background: rgba(139,92,246,.1); color: #8b5cf6;">
|
||||
<svg viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></svg>
|
||||
</div>
|
||||
<div class="infra-info">
|
||||
<div class="infra-name">DabMusic</div>
|
||||
<div class="infra-desc">Primary Qobuz lossless stream API. Provides direct download URLs for FLAC audio at up to 24-bit/192kHz quality. Queried in parallel alongside squid.wtf for fastest response.</div>
|
||||
<a class="infra-link" href="https://dabmusic.xyz" target="_blank">
|
||||
dabmusic.xyz
|
||||
<svg viewBox="0 0 24 24"><path d="M14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Jumo DL (no GitHub — globe) -->
|
||||
<div class="infra-card">
|
||||
<div class="infra-icon" style="background: rgba(56,189,248,.1); color: #38bdf8;">
|
||||
<svg viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></svg>
|
||||
</div>
|
||||
<div class="infra-info">
|
||||
<div class="infra-name">Jumo DL</div>
|
||||
<div class="infra-desc">Qobuz final fallback. A Cloudflare Pages worker tried after all standard Qobuz APIs fail, with automatic quality downgrade cascade (hi-res → CD → MP3) to maximize success rate.</div>
|
||||
<a class="infra-link" href="https://jumo-dl.pages.dev" target="_blank">
|
||||
jumo-dl.pages.dev
|
||||
<svg viewBox="0 0 24 24"><path d="M14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- === AMAZON === -->
|
||||
|
||||
<!-- AfkarXYZ (GitHub) -->
|
||||
<div class="infra-card">
|
||||
<div class="infra-icon" style="background: rgba(255,255,255,.08); color: #e8e8e8;">
|
||||
<svg viewBox="0 0 24 24"><path d="M12 .3a12 12 0 00-3.8 23.4c.6.1.8-.3.8-.6v-2c-3.3.7-4-1.6-4-1.6-.5-1.4-1.3-1.8-1.3-1.8-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.8 1.3 3.5 1 .1-.8.4-1.3.7-1.6-2.7-.3-5.5-1.3-5.5-6 0-1.3.5-2.3 1.2-3.2-.1-.3-.5-1.5.1-3.2 0 0 1-.3 3.4 1.2a11.5 11.5 0 016 0c2.3-1.5 3.3-1.2 3.3-1.2.7 1.7.3 2.9.1 3.2.8.8 1.2 1.9 1.2 3.2 0 4.6-2.8 5.6-5.5 5.9.4.4.8 1.1.8 2.2v3.3c0 .3.2.7.8.6A12 12 0 0012 .3z"/></svg>
|
||||
</div>
|
||||
<div class="infra-info">
|
||||
<div class="infra-name">AfkarXYZ</div>
|
||||
<div class="infra-desc">Sole Amazon Music download API with stream decryption support. Also provides a SpotFetch-compatible Spotify metadata proxy used when direct API access is blocked.</div>
|
||||
<a class="infra-link" href="https://github.com/afkarxyz" target="_blank">
|
||||
afkarxyz
|
||||
<svg viewBox="0 0 24 24"><path d="M14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- === YOUTUBE AUDIO === -->
|
||||
|
||||
<!-- Cobalt (GitHub) -->
|
||||
<div class="infra-card">
|
||||
<div class="infra-icon" style="background: rgba(255,255,255,.08); color: #e8e8e8;">
|
||||
<svg viewBox="0 0 24 24"><path d="M12 .3a12 12 0 00-3.8 23.4c.6.1.8-.3.8-.6v-2c-3.3.7-4-1.6-4-1.6-.5-1.4-1.3-1.8-1.3-1.8-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.8 1.3 3.5 1 .1-.8.4-1.3.7-1.6-2.7-.3-5.5-1.3-5.5-6 0-1.3.5-2.3 1.2-3.2-.1-.3-.5-1.5.1-3.2 0 0 1-.3 3.4 1.2a11.5 11.5 0 016 0c2.3-1.5 3.3-1.2 3.3-1.2.7 1.7.3 2.9.1 3.2.8.8 1.2 1.9 1.2 3.2 0 4.6-2.8 5.6-5.5 5.9.4.4.8 1.1.8 2.2v3.3c0 .3.2.7.8.6A12 12 0 0012 .3z"/></svg>
|
||||
</div>
|
||||
<div class="infra-info">
|
||||
<div class="infra-name">Cobalt</div>
|
||||
<div class="infra-desc">Privacy-focused media extraction tool. The core engine behind YouTube Music downloads — accepts a video URL and returns a tunnel URL to the audio stream in opus or mp3 format.</div>
|
||||
<a class="infra-link" href="https://github.com/imputnet/cobalt" target="_blank">
|
||||
imputnet/cobalt
|
||||
<svg viewBox="0 0 24 24"><path d="M14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Qwkuns (no GitHub — globe) -->
|
||||
<div class="infra-card">
|
||||
<div class="infra-icon" style="background: rgba(16,185,129,.1); color: #10b981;">
|
||||
<svg viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></svg>
|
||||
</div>
|
||||
<div class="infra-info">
|
||||
<div class="infra-name">Qwkuns</div>
|
||||
<div class="infra-desc">Cobalt-compatible API for YouTube audio extraction. Serves as the fallback download engine when the primary SpotubeDL proxy is unavailable, using the standard Cobalt protocol.</div>
|
||||
<a class="infra-link" href="https://qwkuns.me" target="_blank">
|
||||
qwkuns.me
|
||||
<svg viewBox="0 0 24 24"><path d="M14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SpotubeDL (no GitHub — globe) -->
|
||||
<div class="infra-card">
|
||||
<div class="infra-icon" style="background: rgba(244,63,94,.1); color: #f43f5e;">
|
||||
<svg viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></svg>
|
||||
</div>
|
||||
<div class="infra-info">
|
||||
<div class="infra-name">SpotubeDL</div>
|
||||
<div class="infra-desc">Primary YouTube download proxy. Handles authentication to Cobalt download instances and serves as the first-choice engine for YouTube Music audio extraction.</div>
|
||||
<a class="infra-link" href="https://spotubedl.com" target="_blank">
|
||||
spotubedl.com
|
||||
<svg viewBox="0 0 24 24"><path d="M14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- DISCLAIMER -->
|
||||
<div class="disclaimer">
|
||||
<p>SpotiFLAC Mobile is not affiliated with, endorsed by, or connected to any of the services listed above. All trademarks and logos belong to their respective owners. This page is meant to acknowledge and appreciate the platforms that make this project possible.</p>
|
||||
</div>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<footer>
|
||||
<div class="footer-inner">
|
||||
<div class="footer-links">
|
||||
<a href="index.html">Home</a>
|
||||
<a href="downloads.html">Downloads</a>
|
||||
<a href="https://zarz.moe/docs" target="_blank">Documentation</a>
|
||||
<a href="https://github.com/zarzet/SpotiFLAC-Mobile" target="_blank">GitHub</a>
|
||||
<a href="https://t.me/spotiflac" target="_blank">Telegram</a>
|
||||
<a href="https://ko-fi.com/zarzet" target="_blank">Support / Ko-fi</a>
|
||||
</div>
|
||||
<p class="footer-copy">SpotiFLAC is for educational and private use only. Not affiliated with any streaming service.</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
function toggleMenu() {
|
||||
document.getElementById('mobileMenu').classList.toggle('open');
|
||||
document.getElementById('mobileOverlay').classList.toggle('open');
|
||||
document.querySelector('.nav-burger').classList.toggle('active');
|
||||
}
|
||||
document.getElementById('mobileMenu').addEventListener('click', function(e) {
|
||||
if (e.target.closest('a')) toggleMenu();
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user