Food4Thoth

🎧 Rstory Playlist & Downloader

A Phantom wallet–aware, Solana token-gated audio hub for FOOD4THOTH releases.
Holders with at least 1 RSTORY can view the special message; holders with 10,000 RSTORY can download gated tracks/videos.
The carousel opens on “Riviera of Bones.”


⚡ Highlights


🗂️ File / Folder Layout (suggested)

/RstoryPlayList/
  index.html              # main page (the big file you pasted)
  readme/
    index.md              # THIS README file (permalinked here)
  MidwayBlitz.png
  RivieraBones.png
  TaxStarve.png
  IMG_9573.JPG
  navigation.html         # loaded into #nav-container at runtime

You can rename the README to README.md, but for pretty site routing use readme/index.md with the permalink above.


🛠️ Dependencies (CDN)

All are already referenced via CDN in index.html.


🧠 Token Gate — Config Overview

In index.html (Phantom section):

// ---- CONFIG ----
const ANKR_HEX   = "<your_ankr_api_key_hex>";  // hex-encoded
const CHAIN      = "solana";                    // or "solana_devnet" / "solana_testnet"
const RSTORY_MINT = "RstyC4aoSD1JKPGTxnjB3PkWdRzV3yoePH1XiPMxBfz";
const RSTORY_DECIMALS = 6;

// Thresholds
const THRESHOLD_USER_UNITS = 10000; // download unlock ≥ 10,000 RSTORY
const VIEW_THRESHOLD_USER_UNITS = 1; // message unlock ≥ 1 RSTORY

Tip: Hex-encode your ANKR key (DevTools):

[...new TextEncoder().encode("YOUR_ANKR_KEY")]
  .map(b => b.toString(16).padStart(2,"0"))
  .join("");

Start on “Riviera of Bones”:

let currentSongIndex = 4;

var swiper = new Swiper(".swiper", {
  // ...
  initialSlide: 4,
  // ...
});

Slide ↔ Song mapping (adjust if you reorder slides):

const slideToSong = [0, 1, 2, 3, 4, 5, 6, 7, 8];

Keep them in sync:

function syncPlayerToSlide(i, { autoplay = false } = {}) {
  const next = slideToSong[i];
  if (next == null) return;
  currentSongIndex = next;
  updateSongInfo();
  if (autoplay) playSong(); else pauseSong();
}
swiper.on("slideChange", () => syncPlayerToSlide(swiper.realIndex));

🎵 Songs Array (public + gated examples)

const songs = [
  { title: "Rstory BackTrack", name: "FOOD4THOTH",
    source: "https://www.food4thoth.com/MusicLibraryVis/music/RstoryRainbowRap.m4a" },
  { title: "Time is Breaking", name: "INPROGRESSION",
    source: "https://www.food4thoth.com/MusicLibraryVis/InproSqSpace/Inprogression+-+Time+and+Energy+-+02+Time+is+Breaking.mp3" },
  { title: "Blip Blox 3D", name: "DeJahn",
    source: "https://www.food4thoth.com/MusicLibraryVis/music/BlipBlox3.wav" },

  // GATED (hex sources)
  { title: "Midway Blitz — industrial indictment", name: "FOOD4THOTH", gated: true,
    sourceHex: "68747470733a2f2f62726f6e7a652d6b696e642d7469636b2d3337302e6d7970696e6174612e636c6f75642f697066732f62616679626569676c6b6e636870327937656d6c6675757877696371776e7a65633668326663756a3376366d78617961617864673534676a336c75" },
  { title: "Riviera of Bones", name: "FOOD4THOTH", gated: true,
    sourceHex: "68747470733a2f2f62726f6e7a652d6b696e642d7469636b2d3337302e6d7970696e6174612e636c6f75642f697066732f62616679626569666833763535706770376d677378346a656e696466777335683370767a656665716668786135657663356b70727a62323634676d" },
  { title: "You Pay Tax Dollars to Starve Children", name: "FOOD4THOTH", gated: true,
    sourceHex: "68747470733a2f2f62726f6e7a652d6b696e642d7469636b2d3337302e6d7970696e6174612e636c6f75642f697066732f62616679626569627a6877326963716876646d7361736c326b66723669613279666b6d646b35696d79337470326d6e6a7472793368677261346d79" },

  { title: "Mescalito Amazing Story", name: "DeJahn",
    source: "https://www.food4thoth.com/MusicLibraryVis/music/MescalinosAmazingStory.wav" },
  { title: "Rabbit Hole", name: "INPROGRESSION",
    source: "https://www.food4thoth.com/MusicLibraryVis/InproSqSpace/Inprogression+-+Down+the+Rabbit+Hole+-+03+Rabbit+Hole%202.mp3" },
  { title: "Funkin Around", name: "DeJahn",
    source: "https://static1.squarespace.com/static/569ded85a128e6228959a613/t/56b0bbfd2eeb819ad6daaf05/1454423107728/ZOOM0003_ST001.mp3/original/ZOOM0003_ST001.mp3" }
];

For gated tracks, the player decodes sourceHex to a playable source (so listening is allowed),
but downloads remain locked until the 10k RSTORY threshold.


⬇️ Download Unlock (≥ 10,000 RSTORY)

Gate state flows in via the global event:

let gateOK = false;
window.addEventListener("rstory:gate", (e) => {
  gateOK = !!(e.detail?.hasDownload); // true only when ≥ 10k
  updateDownloadState();
});

Button guard:

function updateDownloadState() {
  const s = songs[currentSongIndex];
  const locked = s.gated && !gateOK;
  downloadBtn.disabled = locked;
  downloadBtn.title = locked ? "Hold 10k RSTORY to download" : "Download";
}

Force a file save when permitted:

downloadBtn.addEventListener("click", async () => {
  const s = songs[currentSongIndex];
  if (s.gated && !gateOK) return;
  const url = s.downloadHex ? fromHex(s.downloadHex)
            : s.source || (s.sourceHex ? fromHex(s.sourceHex) : "");
  if (!url) return;

  const resp = await fetch(url, { mode: "cors" });
  const blob = await resp.blob();
  const a = document.createElement("a");
  a.href = URL.createObjectURL(blob);
  a.download = `${(s.title||"track").replace(/[^\w\-]+/g,"_")}.${(blob.type?.split("/")[1]||"mp3")}`;
  document.body.appendChild(a); a.click(); a.remove();
});

🎬 Video Hydration (token-aware)

const VIDEO_HEX_BY_ID = new Map([
  ["midway-blitz", "68747470733a2f2f62726f6e7a652d6b696e642d7469636b2d3337302e6d7970696e6174612e636c6f75642f697066732f62616679626569676c6b6e636870327937656d6c6675757877696371776e7a65633668326663756a3376366d78617961617864673534676a336c75"],
  ["riviera",      "68747470733a2f2f62726f6e7a652d6b696e642d7469636b2d3337302e6d7970696e6174612e636c6f75642f697066732f62616679626569666833763535706770376d677378346a656e696466777335683370767a656665716668786135657663356b70727a62323634676d"],
  ["core-truth",   "68747470733a2f2f62726f6e7a652d6b696e642d7469636b2d3337302e6d7970696e6174612e636c6f75642f697066732f62616679626569627a6877326963716876646d7361736c326b66723669613279666b6d646b35696d79337470326d6e6a7472793368677261346d79"],
]);

function hydrateVideosAfterGate() {
  document.querySelectorAll(".song-card").forEach(card => {
    const id = card.getAttribute("data-song-id");
    const hex = VIDEO_HEX_BY_ID.get(id);
    const v = card.querySelector("video");
    if (!hex || !v || v.dataset.hydrated === "1") return;
    v.src = fromHex(hex) + "#t=0.1"; // tiny seek for thumbnail on some browsers
    v.dataset.hydrated = "1";
  });
}

window.addEventListener("rstory:gate", (e) => {
  if (e.detail?.hasAny) hydrateVideosAfterGate(); // ≥ 1 RSTORY
});

🔐 Message Gate + Rune Decode

window.addEventListener("rstory:gate", (e) => {
  const ok = !!(e.detail?.hasAny);
  decryptBtn.disabled = !ok;
});

Set AUTO_DECODE_ON_GATE = true if you want it to auto-reveal as soon as the holder is verified.


🧩 Navigation Loader

The page fetches ../navigation.html into #nav-container and sets up:

If your project doesn’t have navigation.html at that path, create it or remove the fetch block.


🧭 How to Add New Songs

  1. Add a slide in the Swiper with your cover image and any social links.
  2. Append an entry to songs[]. For public media, use source. For gated media, use gated: true with sourceHex:
    { title:"My New Track", name:"FOOD4THOTH", gated:true, sourceHex:"<hex url>" }
    
  3. Map the slide index to the songs[] index in slideToSong.
  4. (Optional) If you have a distinct download file/format, add downloadHex to that song.

Encoding to hex (DevTools):

const toHex = s =>
  [...new TextEncoder().encode(s)]
    .map(b => b.toString(16).padStart(2,"0"))
    .join("");

🧪 Quick Start / Local Test

If balance reads 0 but you hold tokens, check:


🧯 Troubleshooting


🔒 Security Notes


♿ Accessibility & UX


📈 SEO / Social


🌌 Philosophy and Vision

Food4Thoth is inspired by the principles of its namesake, Thoth:

The platform is a digital garden where ancient wisdom meets modern innovation.


✨ Why Visit Food4Thoth?

  1. Diverse Offerings: Content that caters to various interests, from art and mysticism to community activism.
  2. Interactive Tools: Explore engaging applications like calculators, games, and divination apps.
  3. Community Engagement: Opportunities for collaboration and connection through artistic and social projects.
  4. Inspiration: A space to spark curiosity, reflection, and joy.

🤝 Support and Contributions

Your contributions help support innovative projects like the Rainbow Glo-Calculato, community gardens, and esoteric tools, ensuring Food4Thoth continues to thrive.

Donation Options

Traditional Payments:

  1. https://paypal.me/artabillies
  2. https://venmo.com/u/DeJahnvu

Cryptocurrency:


💡 Wallets

  1. Coinbase Wallet:
    0x30D47A5815D94040291a819B8E39765AA09d44A8
  2. Metamask Wallet:
    0x30D47A5815D94040291a819B8E39765AA09d44A8
  3. VeWorld Wallet:
    0x020a79559990145e2f7d48c5771b233399b30bee
  4. Anchor Wallet:
    artabilly.gm

🤝 Contribution Guidelines

We welcome contributions to enhance this project:

# 1) Fork the repository, then clone your fork
git clone <your-fork-url>
cd <repo>

# 2) Create a feature branch
git checkout -b feature-name

# 3) Commit your changes
git commit -m "Add feature or fix"

# 4) Push your branch
git push origin feature-name

# 5) Open a Pull Request on GitHub/GitLab

🔗 Explore the Food4Thoth Hub


💌 Contact


🎉 Acknowledgments

This platform is part of the Food4Thoth Initiative, which blends creativity, technology, and community to make a positive impact.
Special thanks to our supporters for enabling these innovative projects and empowering meaningful change.

Your contributions make a difference. Thank you for your support!


⚡ Credits
Designed, coded, and curated by DeJahn under Artabillies & FOOD4THOTH.


📝 License

© 2025 Food4Thoth. All rights reserved.
Unauthorized redistribution, copying, or modification without explicit permission is prohibited.