strm-vbeta1-01 / index.html
privateuserh's picture
Add 3 files
e39aaec verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Music Stream Mine - Earn STRM Tokens</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
::-webkit-scrollbar {
width: 4px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
}
::-webkit-scrollbar-thumb {
background: #10b981;
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: #0d9f6e;
}
.music-card:hover {
transform: translateY(-2px);
}
.strm-bg { background-color: #10b981; }
* {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.explainer-animate {
animation: fadeIn 0.3s ease-out forwards;
}
.percentage-up {
color: #10b981;
}
.percentage-down {
color: #ef4444;
}
.percentage-neutral {
color: #6b7280;
}
.sentiment-positive {
background-color: rgba(16, 185, 129, 0.1);
border-left: 3px solid #10b981;
}
.sentiment-negative {
background-color: rgba(239, 68, 68, 0.1);
border-left: 3px solid #ef4444;
}
.sentiment-neutral {
background-color: rgba(156, 163, 175, 0.1);
border-left: 3px solid #9ca3af;
}
.sentiment-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
cursor: pointer;
}
.sentiment-slider.positive::-webkit-slider-thumb {
background: #10b981;
}
.sentiment-slider.negative::-webkit-slider-thumb {
background: #ef4444;
}
.sentiment-slider.neutral::-webkit-slider-thumb {
background: #6b7280;
}
/* Floating button styles */
.floating-btn {
position: fixed;
bottom: 80px;
right: 20px;
width: 50px;
height: 50px;
border-radius: 50%;
background-color: #10b981;
color: white;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
z-index: 40;
cursor: pointer;
transition: all 0.3s ease;
}
.floating-btn:hover {
transform: scale(1.1);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3);
}
/* Legend modal styles */
.legend-item {
display: flex;
align-items: center;
margin-bottom: 12px;
}
.legend-icon {
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 12px;
flex-shrink: 0;
}
.verified-badge {
display: inline-flex;
align-items: center;
background-color: #10b981;
color: white;
padding: 2px 6px;
border-radius: 10px;
font-size: 10px;
margin-left: 5px;
}
.mining-animation {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
/* Mining difficulty adjustment */
.mining-difficulty {
position: absolute;
bottom: 10px;
left: 10px;
font-size: 10px;
color: #6b7280;
}
</style>
</head>
<body class="bg-gray-50 font-sans text-gray-800">
<!-- App Container -->
<div class="max-w-md mx-auto bg-white min-h-screen flex flex-col">
<!-- Header -->
<header class="strm-bg p-4 shadow-sm">
<div class="flex items-center justify-between">
<h1 class="text-white text-xl font-bold">Music Stream Mine</h1>
<div class="flex items-center space-x-3">
<button class="text-white" id="explainerBtn">
<i class="fas fa-info-circle"></i>
</button>
<button class="text-white" id="strmBalanceBtn">
<span id="strmBalanceDisplay">0 STRM</span>
</button>
</div>
</div>
</header>
<!-- Main Content Area -->
<main class="flex-1 overflow-y-auto p-4" id="mainContent">
<!-- Home Tab Content -->
<div id="homeTab">
<div class="mb-6">
<h2 class="text-lg font-semibold mb-4">Music Streaming Pools</h2>
<div class="grid grid-cols-1 gap-4" id="streamingPoolsContainer">
<!-- Cards will be dynamically inserted here -->
</div>
</div>
<div class="mt-6">
<div class="flex items-center justify-between mb-4">
<h2 class="text-lg font-semibold">Your Staked Pools</h2>
<button id="stakeInfoBtn" class="text-xs text-strm-bg font-medium flex items-center">
<i class="fas fa-question-circle mr-1"></i> How mining works
</button>
</div>
<div class="space-y-3" id="stakedPoolsContainer">
<!-- Staked pools will appear here -->
</div>
</div>
</div>
<!-- Search Tab Content (hidden by default) -->
<div id="searchTab" class="hidden">
<div class="mb-4">
<div class="relative">
<input type="text" id="poolSearchInput" placeholder="Search music streaming pools..."
class="w-full py-3 px-4 rounded-lg bg-gray-100 focus:outline-none focus:ring-2 focus:ring-strm-bg">
<button id="searchPoolBtn" class="absolute right-3 top-3 text-gray-500 hover:text-strm-bg">
<i class="fas fa-search"></i>
</button>
</div>
</div>
<div class="grid grid-cols-1 gap-4" id="searchResultsContainer">
<!-- Search results will appear here -->
</div>
</div>
</main>
<!-- Bottom Navigation -->
<nav class="bg-white border-t border-gray-200 p-2">
<div class="flex justify-around">
<button class="tab-button active flex flex-col items-center text-strm-bg" data-tab="homeTab">
<i class="fas fa-home text-lg"></i>
<span class="text-xs mt-1">Home</span>
</button>
<button class="tab-button flex flex-col items-center text-gray-500" data-tab="searchTab">
<i class="fas fa-search text-lg"></i>
<span class="text-xs mt-1">Search</span>
</button>
<button class="flex flex-col items-center text-gray-500" id="addPoolBtn">
<i class="fas fa-plus-circle text-lg"></i>
<span class="text-xs mt-1">Add</span>
</button>
</div>
</nav>
<!-- Floating Mining Button -->
<div class="floating-btn mining-animation" id="miningBtn">
<i class="fas fa-digging"></i>
<div class="mining-difficulty">D: 1.0</div>
</div>
<!-- Add Pool Modal (hidden by default) -->
<div id="addPoolModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-lg w-full max-w-md mx-4">
<div class="p-4 border-b border-gray-200">
<h3 class="font-semibold text-lg">Add New Streaming Pool</h3>
</div>
<div class="p-4">
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Artist/Playlist</label>
<input type="text" id="newPoolName" placeholder="Artist or playlist name"
class="w-full py-2 px-4 rounded-lg bg-gray-100 focus:outline-none focus:ring-2 focus:ring-strm-bg">
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Music Category</label>
<div class="grid grid-cols-2 gap-2">
<button class="platform-btn bg-gray-200 text-gray-700 py-2 px-3 rounded text-sm" data-platform="pop">
<i class="fas fa-music mr-1 text-purple-500"></i> Pop
</button>
<button class="platform-btn bg-gray-200 text-gray-700 py-2 px-3 rounded text-sm" data-platform="rock">
<i class="fas fa-guitar mr-1 text-yellow-500"></i> Rock
</button>
<button class="platform-btn bg-gray-200 text-gray-700 py-2 px-3 rounded text-sm" data-platform="electronic">
<i class="fas fa-sliders-h mr-1 text-blue-500"></i> Electronic
</button>
<button class="platform-btn bg-gray-200 text-gray-700 py-2 px-3 rounded text-sm" data-platform="classical">
<i class="fas fa-viola mr-1 text-green-500"></i> Classical
</button>
</div>
</div>
</div>
<div class="p-4 border-t border-gray-200 flex justify-end space-x-3">
<button id="cancelAddPool" class="px-4 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700">
Cancel
</button>
<button id="savePool" class="px-4 py-2 strm-bg text-white rounded-md text-sm font-medium">
Create Pool
</button>
</div>
</div>
</div>
<!-- STRM Token Modal (hidden by default) -->
<div id="strmModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-lg w-full max-w-md mx-4">
<div class="p-4 border-b border-gray-200 flex justify-between items-center">
<h3 class="font-semibold text-lg">STRM Token</h3>
<span class="verified-badge">
<i class="fas fa-check-circle mr-1"></i> Verified
</span>
</div>
<div class="p-4">
<div class="flex items-center mb-4">
<div class="w-12 h-12 rounded-full strm-bg text-white flex items-center justify-center mr-3">
<i class="fas fa-music text-xl"></i>
</div>
<div>
<h4 class="font-medium">Bitcoin Cash CHIP Protocol</h4>
<p class="text-sm text-gray-500">Mining Pool: 5,000,000 STRM</p>
</div>
</div>
<div class="mb-4">
<p class="text-sm text-gray-700 mb-3">
STRM is a private cryptocurrency token on the Bitcoin Cash network using the CHIP protocol.
Mine STRM tokens by staking to music streaming pools. Total supply: 5,000,000 STRM.
</p>
<div class="space-y-2">
<a href="https://tokenexplorer.cash/?tokenId=8874186362dd0510af5e26c4321c2a335f7089cfc1a70fcd9e4efb63a49d2a76" target="_blank" class="flex items-center text-blue-500 text-sm">
<i class="fas fa-external-link-alt mr-2"></i> View on Token Explorer
</a>
<a href="https://app.cauldron.quest/swap/8874186362dd0510af5e26c4321c2a335f7089cfc1a70fcd9e4efb63a49d2a76" target="_blank" class="flex items-center text-blue-500 text-sm">
<i class="fas fa-exchange-alt mr-2"></i> Swap on Cauldron
</a>
<a href="https://blockchair.com/bitcoin-cash/transaction/8874186362dd0510af5e26c4321c2a335f7089cfc1a70fcd9e4efb63a49d2a76" target="_blank" class="flex items-center text-blue-500 text-sm">
<i class="fas fa-link mr-2"></i> View Transaction
</a>
</div>
</div>
<div class="bg-gray-100 rounded-lg p-3 mb-4">
<h4 class="font-medium text-sm mb-2">Your STRM Balance</h4>
<div class="flex justify-between items-center">
<span class="font-medium" id="strmBalance">0 STRM</span>
<button id="addStrmBtn" class="text-sm text-strm-bg font-medium">
<i class="fas fa-plus-circle mr-1"></i> Add STRM
</button>
</div>
</div>
<div class="bg-gray-100 rounded-lg p-3">
<h4 class="font-medium text-sm mb-2">Stake STRM to Pool</h4>
<div class="mb-2">
<select id="poolSelect" class="w-full p-2 rounded border border-gray-300 text-sm">
<option value="">-- Select a pool --</option>
</select>
</div>
<div class="mb-2">
<input type="number" id="stakeAmount" placeholder="STRM amount" class="w-full p-2 rounded border border-gray-300 text-sm">
</div>
<button id="stakeBtn" class="w-full py-2 strm-bg text-white rounded-lg text-sm font-medium">
Stake & Start Mining
</button>
</div>
</div>
<div class="p-4 border-t border-gray-200 flex justify-center">
<button id="closeStrmModal" class="px-4 py-2 strm-bg text-white rounded-md text-sm font-medium">
Close
</button>
</div>
</div>
</div>
<!-- Explainer Modal (hidden by default) -->
<div id="explainerModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-lg w-full max-w-md mx-4 explainer-animate">
<div class="p-4 border-b border-gray-200 flex justify-between items-center">
<h3 class="font-semibold text-lg">Music Stream Mining</h3>
<button id="closeExplainerModal" class="text-gray-500 hover:text-strm-bg">
<i class="fas fa-times"></i>
</button>
</div>
<div class="p-4 overflow-y-auto max-h-[70vh]">
<div class="mb-6">
<div class="flex items-start mb-4">
<div class="w-10 h-10 rounded-full strm-bg text-white flex items-center justify-center mr-3 flex-shrink-0">
<i class="fas fa-chart-pie"></i>
</div>
<div>
<h4 class="font-medium mb-1">Bitcoin Cash Mining Algorithm</h4>
<p class="text-sm text-gray-700">
Our mining algorithm is based on Bitcoin Cash's SHA-256 proof-of-work with difficulty adjustment.
The total supply is capped at 5,000,000 STRM tokens with halving every 4 years.
</p>
</div>
</div>
<div class="flex items-start mb-4">
<div class="w-10 h-10 rounded-full strm-bg text-white flex items-center justify-center mr-3 flex-shrink-0">
<i class="fas fa-percentage"></i>
</div>
<div>
<h4 class="font-medium mb-1">Mining Difficulty</h4>
<p class="text-sm text-gray-700">
Mining difficulty adjusts every 2016 blocks (approx. 2 weeks) to maintain a consistent block time.
Current difficulty is displayed on the mining button.
</p>
</div>
</div>
<div class="flex items-start mb-4">
<div class="w-10 h-10 rounded-full strm-bg text-white flex items-center justify-center mr-3 flex-shrink-0">
<i class="fas fa-tags"></i>
</div>
<div>
<h4 class="font-medium mb-1">Music Categories</h4>
<p class="text-sm text-gray-700">
We track four main music categories: Pop, Rock, Electronic, and Classical.
Each pool is classified into one of these categories for mining calculation.
</p>
</div>
</div>
<div class="flex items-start mb-4">
<div class="w-10 h-10 rounded-full strm-bg text-white flex items-center justify-center mr-3 flex-shrink-0">
<i class="fas fa-coins"></i>
</div>
<div>
<h4 class="font-medium mb-1">Mining Rewards</h4>
<p class="text-sm text-gray-700">
Current block reward is 6.25 STRM per block. The more you stake and the more streams
the pool gets, the higher your share of the rewards. Rewards are distributed daily.
</p>
</div>
</div>
<div class="bg-blue-50 border border-blue-100 rounded-lg p-3">
<div class="flex">
<div class="text-blue-500 mr-2">
<i class="fas fa-lightbulb"></i>
</div>
<p class="text-sm text-blue-800">
<strong>Pro Tip:</strong> Look for emerging artists with small but rapidly increasing streams.
These often provide the best mining opportunities with lower difficulty.
</p>
</div>
</div>
</div>
</div>
<div class="p-4 border-t border-gray-200 flex justify-center">
<button id="closeExplainerBtn" class="px-4 py-2 strm-bg text-white rounded-md text-sm font-medium">
Got It!
</button>
</div>
</div>
</div>
<!-- Mining Modal (hidden by default) -->
<div id="miningModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white rounded-lg w-full max-w-md mx-4">
<div class="p-4 border-b border-gray-200">
<h3 class="font-semibold text-lg">Mining Status</h3>
</div>
<div class="p-4">
<div class="flex flex-col items-center mb-4">
<div class="w-20 h-20 rounded-full strm-bg text-white flex items-center justify-center mb-4">
<i class="fas fa-digging text-3xl"></i>
</div>
<h4 class="font-medium mb-2" id="miningPoolName">Pop Hits 2023</h4>
<p class="text-sm text-gray-600 mb-4">Currently mining STRM tokens</p>
<div class="w-full bg-gray-200 rounded-full h-2.5 mb-4">
<div class="bg-strm-bg h-2.5 rounded-full" style="width: 45%"></div>
</div>
<div class="grid grid-cols-3 gap-4 w-full text-center mb-4">
<div>
<p class="text-sm text-gray-500">Staked</p>
<p class="font-medium" id="miningStaked">500 STRM</p>
</div>
<div>
<p class="text-sm text-gray-500">Earned</p>
<p class="font-medium" id="miningEarned">1.5 STRM</p>
</div>
<div>
<p class="text-sm text-gray-500">Difficulty</p>
<p class="font-medium" id="miningDifficulty">1.0</p>
</div>
</div>
<div class="w-full bg-gray-100 rounded-lg p-3 text-center">
<p class="text-xs text-gray-600 mb-1">Estimated Hashrate: <span id="miningHashrate">12.5 TH/s</span></p>
<p class="text-xs text-gray-600">Next Difficulty Adjustment: <span id="nextDifficulty">2016 blocks</span></p>
</div>
</div>
</div>
<div class="p-4 border-t border-gray-200 flex justify-end space-x-3">
<button id="stopMiningBtn" class="px-4 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700">
Stop Mining
</button>
<button id="claimMiningBtn" class="px-4 py-2 strm-bg text-white rounded-md text-sm font-medium">
Claim Rewards
</button>
</div>
</div>
</div>
<!-- Toast Notification (hidden by default) -->
<div id="toast" class="fixed bottom-20 left-1/2 transform -translate-x-1/2 bg-gray-800 text-white px-4 py-2 rounded-md shadow-lg hidden z-50">
<div class="flex items-center">
<span id="toastMessage">Pool created successfully</span>
</div>
</div>
</div>
<script>
// Music categories and sample data (removed hip hop)
const musicCategories = {
pop: {
name: "Pop",
icon: '<i class="fas fa-music text-purple-500"></i>',
color: 'bg-purple-500',
totalStreams: 25000000,
pools: [
{ name: "Pop Hits 2023", currentStreams: 6000000, previousStreams: 5500000, sentiment: 0 },
{ name: "Top 40 Global", currentStreams: 5000000, previousStreams: 4800000, sentiment: 0 },
{ name: "Taylor Swift Radio", currentStreams: 3000000, previousStreams: 2800000, sentiment: 0 },
{ name: "Weekend Party Mix", currentStreams: 2000000, previousStreams: 1900000, sentiment: 0 }
]
},
rock: {
name: "Rock",
icon: '<i class="fas fa-guitar text-yellow-500"></i>',
color: 'bg-yellow-500',
totalStreams: 12000000,
pools: [
{ name: "Classic Rock", currentStreams: 4000000, previousStreams: 4100000, sentiment: 0 },
{ name: "Alternative Rock", currentStreams: 3000000, previousStreams: 2900000, sentiment: 0 },
{ name: "Metal Essentials", currentStreams: 2000000, previousStreams: 2100000, sentiment: 0 },
{ name: "Indie Rock", currentStreams: 1500000, previousStreams: 1400000, sentiment: 0 }
]
},
electronic: {
name: "Electronic",
icon: '<i class="fas fa-sliders-h text-blue-500"></i>',
color: 'bg-blue-500',
totalStreams: 15000000,
pools: [
{ name: "EDM Bangers", currentStreams: 4000000, previousStreams: 3800000, sentiment: 0 },
{ name: "Deep House", currentStreams: 3000000, previousStreams: 2900000, sentiment: 0 },
{ name: "Techno Workout", currentStreams: 2500000, previousStreams: 2400000, sentiment: 0 },
{ name: "Drum & Bass", currentStreams: 2000000, previousStreams: 2100000, sentiment: 0 }
]
},
classical: {
name: "Classical",
icon: '<i class="fas fa-viola text-green-500"></i>',
color: 'bg-green-500',
totalStreams: 8000000,
pools: [
{ name: "Mozart Essentials", currentStreams: 2000000, previousStreams: 2100000, sentiment: 0 },
{ name: "Piano Relaxation", currentStreams: 1500000, previousStreams: 1400000, sentiment: 0 },
{ name: "Opera Highlights", currentStreams: 1200000, previousStreams: 1300000, sentiment: 0 },
{ name: "Film Scores", currentStreams: 1000000, previousStreams: 900000, sentiment: 0 }
]
}
};
// Calculate total streams across all categories
const totalAllStreams = Object.values(musicCategories).reduce((total, category) => total + category.totalStreams, 0);
// Bitcoin Cash mining parameters
const miningParams = {
totalSupply: 5000000, // 5 million STRM total supply
currentSupply: 0, // Start with 0 mined
blockReward: 6.25, // Initial block reward (halves every 4 years)
blocksUntilHalving: 210000, // Halving every ~4 years (BCH halving schedule)
difficulty: 1.0, // Initial difficulty
blocksUntilDifficultyAdjustment: 2016, // Adjust every 2016 blocks (BCH schedule)
blockTimeTarget: 600, // 10 minute target block time (same as BCH)
lastBlockTime: Date.now(), // Timestamp of last block
hashrate: 0 // Current network hashrate
};
// User data
let userData = {
strmBalance: 5000,
stakedPools: [
{
name: "Pop Hits 2023",
platform: "pop",
hasStrm: true,
stakedAmount: 1000,
sentiment: 0,
earned: 3.2,
mining: true,
hashrate: 12.5 // TH/s
},
{
name: "EDM Bangers",
platform: "electronic",
hasStrm: true,
stakedAmount: 500,
sentiment: 0,
earned: 1.5,
mining: true,
hashrate: 6.25 // TH/s
}
]
};
// Current pool being mined
let currentMiningPool = null;
let miningInterval = null;
// Function to calculate market share percentage with sentiment adjustment
function calculateMarketShare(poolName) {
// Find the pool in our categories
let poolData = null;
let category = null;
for (const [cat, data] of Object.entries(musicCategories)) {
const foundPool = data.pools.find(p => p.name === poolName);
if (foundPool) {
poolData = foundPool;
category = cat;
break;
}
}
// If pool not found in predefined data, check user staked pools
if (!poolData || !category) {
const stakedPool = userData.stakedPools.find(p => p.name === poolName);
if (stakedPool) {
category = stakedPool.platform;
// Create a zero entry with sentiment from staked pool
return {
percentage: 0,
change: 0,
category: category,
sentiment: stakedPool.sentiment || 0
};
}
// Default to pop for new pools
return {
percentage: 0,
change: 0,
category: "pop",
sentiment: 0
};
}
// Calculate percentage of category streams
const categoryPercentage = (poolData.currentStreams / musicCategories[category].totalStreams) * 100;
// Calculate percentage of all music streams
const globalPercentage = (poolData.currentStreams / totalAllStreams) * 100;
// Calculate raw percentage change from previous period
const rawChange = poolData.previousStreams > 0
? ((poolData.currentStreams - poolData.previousStreams) / poolData.previousStreams) * 100
: poolData.currentStreams > 0 ? 100 : 0;
// Apply sentiment adjustment (0-100 scale mapped to 0.5-1.5 multiplier)
const sentimentMultiplier = 1 + (poolData.sentiment / 200); // Maps -100 to 0.5, 0 to 1, 100 to 1.5
const adjustedChange = rawChange * sentimentMultiplier;
return {
percentage: globalPercentage.toFixed(2),
change: adjustedChange.toFixed(2),
rawChange: rawChange.toFixed(2),
category: category,
sentiment: poolData.sentiment || 0
};
}
// Function to get percentage change class
function getPercentageClass(change) {
const numChange = parseFloat(change);
if (numChange > 0) return 'percentage-up';
if (numChange < 0) return 'percentage-down';
return 'percentage-neutral';
}
// Function to get sentiment class
function getSentimentClass(sentiment) {
const numSentiment = parseInt(sentiment);
if (numSentiment > 20) return 'sentiment-positive';
if (numSentiment < -20) return 'sentiment-negative';
return 'sentiment-neutral';
}
// Function to get sentiment label
function getSentimentLabel(sentiment) {
const numSentiment = parseInt(sentiment);
if (numSentiment > 20) return 'Positive';
if (numSentiment < -20) return 'Negative';
return 'Neutral';
}
// Function to generate platform icon
function getPlatformIcon(platform) {
const icons = {
pop: '<i class="fas fa-music text-purple-500"></i>',
rock: '<i class="fas fa-guitar text-yellow-500"></i>',
electronic: '<i class="fas fa-sliders-h text-blue-500"></i>',
classical: '<i class="fas fa-viola text-green-500"></i>'
};
return icons[platform] || '<i class="fas fa-music"></i>';
}
// Function to render streaming pools
function renderStreamingPools() {
const container = document.getElementById('streamingPoolsContainer');
container.innerHTML = '';
// Get all pools from all categories
const allPools = [];
Object.values(musicCategories).forEach(category => {
category.pools.forEach(pool => {
allPools.push({
...pool,
category: category.name.toLowerCase()
});
});
});
// Sort by current streams (descending)
allPools.sort((a, b) => b.currentStreams - a.currentStreams);
// Take top 8 pools
const topPools = allPools.slice(0, 8);
topPools.forEach(item => {
const marketShare = calculateMarketShare(item.name);
const percentageClass = getPercentageClass(marketShare.change);
const sentimentClass = getSentimentClass(marketShare.sentiment);
const card = document.createElement('div');
card.className = `music-card bg-white rounded-lg shadow-sm p-4 h-40 flex flex-col justify-between transition cursor-pointer ${sentimentClass}`;
card.innerHTML = `
<div class="flex items-start justify-between mb-2">
<div>
<span class="inline-flex items-center bg-gray-100 text-gray-800 text-xs px-2 py-1 rounded-full">
${getPlatformIcon(item.category)}
<span class="ml-1">${item.name}</span>
</span>
</div>
<span class="inline-flex items-center bg-gray-100 text-gray-800 text-xs px-2 py-1 rounded-full">
<i class="fas fa-chart-line ${percentageClass} mr-1"></i>
${marketShare.percentage}%
<span class="ml-1 ${percentageClass}">(${marketShare.change > 0 ? '+' : ''}${marketShare.change}%)</span>
</span>
</div>
<div class="flex justify-between items-center mb-3">
<span class="text-xs text-gray-500">${item.currentStreams.toLocaleString()} streams</span>
<span class="text-xs px-2 py-1 rounded-full ${musicCategories[item.category].color} text-white">
${musicCategories[item.category].name}
</span>
</div>
<div class="flex justify-between items-center text-xs text-gray-500">
<span>
<span class="${getSentimentClass(marketShare.sentiment).replace('sentiment-', 'text-')}">
<i class="fas ${marketShare.sentiment > 20 ? 'fa-smile' : marketShare.sentiment < -20 ? 'fa-frown' : 'fa-meh'}"></i>
${getSentimentLabel(marketShare.sentiment)}
</span>
</span>
<button class="stake-pool-btn text-gray-400 hover:text-strm-bg" data-pool="${item.name}" data-platform="${item.category}">
<i class="fas fa-coins"></i> Stake
</button>
</div>
`;
container.appendChild(card);
// Add click event to open mining modal
card.addEventListener('click', function() {
openMiningModal(item.name, item.category);
});
});
// Add event listeners to stake buttons
document.querySelectorAll('.stake-pool-btn').forEach(btn => {
btn.addEventListener('click', function(e) {
e.stopPropagation();
const poolName = this.getAttribute('data-pool');
const platform = this.getAttribute('data-platform');
stakePool(poolName, platform);
});
});
}
// Function to render staked pools
function renderStakedPools() {
const container = document.getElementById('stakedPoolsContainer');
container.innerHTML = '';
// Update select dropdown
const poolSelect = document.getElementById('poolSelect');
poolSelect.innerHTML = '<option value="">-- Select a pool --</option>';
userData.stakedPools.forEach(pool => {
const marketShare = calculateMarketShare(pool.name);
const percentageClass = getPercentageClass(marketShare.change);
const sentimentClass = getSentimentClass(pool.sentiment);
const element = document.createElement('div');
element.className = `flex items-center p-3 rounded-lg bg-white shadow-sm cursor-pointer hover:bg-gray-50 ${sentimentClass}`;
element.innerHTML = `
<div class="flex-shrink-0 mr-3">
<div class="w-8 h-8 rounded-full flex items-center justify-center text-white ${musicCategories[pool.platform].color}">
${getPlatformIcon(pool.platform)}
</div>
</div>
<div class="flex-1">
<div class="flex items-center">
<h4 class="font-medium text-sm">${pool.name}</h4>
${pool.hasStrm ? '<span class="ml-1 text-green-500"><i class="fas fa-check-circle"></i></span>' : ''}
${pool.mining ? '<span class="ml-1 text-green-500"><i class="fas fa-digging"></i></span>' : ''}
</div>
<div class="flex items-center">
<p class="text-gray-500 text-xs mr-2">${musicCategories[pool.platform].name}</p>
<span class="text-xs ${percentageClass}">
${marketShare.percentage}% (${marketShare.change > 0 ? '+' : ''}${marketShare.change}%)
</span>
</div>
</div>
<div class="text-right">
<p class="text-xs font-medium">${pool.stakedAmount} STRM</p>
<p class="text-xs text-green-500">+${pool.earned} STRM</p>
</div>
`;
container.appendChild(element);
// Add to select dropdown
const option = document.createElement('option');
option.value = pool.name;
option.textContent = pool.name;
poolSelect.appendChild(option);
// Add click event to open mining modal
element.addEventListener('click', function() {
openMiningModal(pool.name, pool.platform);
});
});
// Update STRM balance display
document.getElementById('strmBalance').textContent = userData.strmBalance + ' STRM';
document.getElementById('strmBalanceDisplay').textContent = userData.strmBalance + ' STRM';
// Update mining difficulty display
document.querySelector('.mining-difficulty').textContent = `D: ${miningParams.difficulty.toFixed(1)}`;
}
// Function to open mining modal
function openMiningModal(poolName, platform) {
currentMiningPool = poolName;
// Find the pool data
let poolData = null;
for (const category of Object.values(musicCategories)) {
const foundPool = category.pools.find(p => p.name === poolName);
if (foundPool) {
poolData = foundPool;
break;
}
}
// If not found in categories, check staked pools
if (!poolData) {
const stakedPool = userData.stakedPools.find(p => p.name === poolName);
if (stakedPool) {
poolData = stakedPool;
}
}
// Set modal content
document.getElementById('miningPoolName').textContent = poolName;
document.getElementById('miningStaked').textContent = poolData?.stakedAmount + ' STRM';
document.getElementById('miningEarned').textContent = poolData?.earned + ' STRM';
document.getElementById('miningDifficulty').textContent = miningParams.difficulty.toFixed(1);
document.getElementById('miningHashrate').textContent = poolData?.hashrate ? poolData.hashrate + ' TH/s' : '0 TH/s';
document.getElementById('nextDifficulty').textContent = miningParams.blocksUntilDifficultyAdjustment + ' blocks';
// Show modal
document.getElementById('miningModal').classList.remove('hidden');
}
// Function to show search results
function showSearchResults(query) {
const container = document.getElementById('searchResultsContainer');
container.innerHTML = '';
if (!query) return;
// Simulate search with timeout
setTimeout(() => {
// Search across all categories
const results = [];
const queryLower = query.toLowerCase();
// First check exact matches
Object.values(musicCategories).forEach(category => {
category.pools.forEach(pool => {
if (pool.name.toLowerCase().includes(queryLower)) {
results.push({
...pool,
platform: category.name.toLowerCase()
});
}
});
});
// If no exact matches, check category names
if (results.length === 0) {
Object.entries(musicCategories).forEach(([key, category]) => {
if (category.name.toLowerCase().includes(queryLower)) {
results.push(...category.pools.slice(0, 3).map(p => ({
...p,
platform: key
})));
}
});
}
// If still no results, create a new "pop" pool
if (results.length === 0) {
const newPool = {
name: query.charAt(0).toUpperCase() + query.slice(1),
currentStreams: 10000, // Default small number for new pools
previousStreams: 0,
platform: "pop",
sentiment: 0
};
results.push(newPool);
// Add to pop category
musicCategories.pop.pools.push(newPool);
musicCategories.pop.totalStreams += newPool.currentStreams;
}
// Display results
results.forEach(item => {
const marketShare = calculateMarketShare(item.name);
const percentageClass = getPercentageClass(marketShare.change);
const sentimentClass = getSentimentClass(marketShare.sentiment);
const card = document.createElement('div');
card.className = `music-card bg-white rounded-lg shadow-sm p-4 h-40 flex flex-col justify-between transition cursor-pointer ${sentimentClass}`;
card.innerHTML = `
<div class="flex items-start justify-between mb-2">
<div>
<span class="inline-flex items-center bg-gray-100 text-gray-800 text-xs px-2 py-1 rounded-full">
${getPlatformIcon(item.platform)}
<span class="ml-1">${item.name}</span>
</span>
</div>
<span class="inline-flex items-center bg-gray-100 text-gray-800 text-xs px-2 py-1 rounded-full">
<i class="fas fa-chart-line ${percentageClass} mr-1"></i>
${marketShare.percentage}%
<span class="ml-1 ${percentageClass}">(${marketShare.change > 0 ? '+' : ''}${marketShare.change}%)</span>
</span>
</div>
<div class="flex justify-between items-center mb-3">
<span class="text-xs text-gray-500">${item.currentStreams.toLocaleString()} streams</span>
<span class="text-xs px-2 py-1 rounded-full ${musicCategories[item.platform].color} text-white">
${musicCategories[item.platform].name}
</span>
</div>
<div class="flex justify-between items-center text-xs text-gray-500">
<span>
<span class="${getSentimentClass(marketShare.sentiment).replace('sentiment-', 'text-')}">
<i class="fas ${marketShare.sentiment > 20 ? 'fa-smile' : marketShare.sentiment < -20 ? 'fa-frown' : 'fa-meh'}"></i>
${getSentimentLabel(marketShare.sentiment)}
</span>
</span>
<button class="stake-pool-btn text-gray-400 hover:text-strm-bg" data-pool="${item.name}" data-platform="${item.platform}">
<i class="fas fa-coins"></i> Stake
</button>
</div>
`;
container.appendChild(card);
// Add click event to open mining modal
card.addEventListener('click', function() {
openMiningModal(item.name, item.platform);
});
});
// Add event listeners to stake buttons
document.querySelectorAll('.stake-pool-btn').forEach(btn => {
btn.addEventListener('click', function(e) {
e.stopPropagation();
const poolName = this.getAttribute('data-pool');
const platform = this.getAttribute('data-platform');
stakePool(poolName, platform);
});
});
showToast(`Found ${results.length} results for ${query}`);
}, 1000);
}
// Function to stake to a pool
function stakePool(poolName, platform) {
// Check if already staked
if (userData.stakedPools.some(p => p.name === poolName)) {
showToast('You already have STRM staked to this pool');
return;
}
// Add to staked pools
userData.stakedPools.push({
name: poolName,
platform,
hasStrm: true,
stakedAmount: 0,
sentiment: 0,
earned: 0,
mining: false,
hashrate: 0
});
// Update UI
renderStakedPools();
showToast(`${poolName} added to your staked pools`);
// Open STRM modal to stake tokens
document.getElementById('strmModal').classList.remove('hidden');
document.getElementById('poolSelect').value = poolName;
}
// Function to stake STRM to a pool
function stakeStrm(poolName, amount) {
if (!poolName || !amount || amount <= 0) {
showToast('Please select a pool and enter a valid amount');
return;
}
if (amount > userData.strmBalance) {
showToast('Insufficient STRM balance');
return;
}
// Update staked amount
const pool = userData.stakedPools.find(p => p.name === poolName);
if (pool) {
pool.hasStrm = true;
pool.stakedAmount = (pool.stakedAmount || 0) + amount;
pool.mining = true;
pool.hashrate = Math.sqrt(pool.stakedAmount) * 0.5; // Simple hashrate calculation based on stake
userData.strmBalance -= amount;
// Start mining if not already running
if (!miningInterval) {
startMining();
}
}
// Update UI
renderStakedPools();
showToast(`Staked ${amount} STRM to ${poolName}`);
}
// Function to start mining
function startMining() {
if (miningInterval) return;
miningInterval = setInterval(() => {
// Calculate total network hashrate
miningParams.hashrate = userData.stakedPools.reduce((sum, pool) => {
return pool.mining ? sum + (pool.hashrate || 0) : sum;
}, 0);
// Mine blocks with difficulty adjustment
userData.stakedPools.forEach(pool => {
if (pool.mining && pool.stakedAmount > 0) {
// Calculate mining probability based on pool's share of total hashrate
const poolShare = miningParams.hashrate > 0 ? (pool.hashrate / miningParams.hashrate) : 0;
// Calculate block reward with difficulty adjustment
const blockReward = miningParams.blockReward * poolShare;
// Add reward to pool
pool.earned = (pool.earned || 0) + blockReward;
miningParams.currentSupply += blockReward;
// Decrease blocks until halving and difficulty adjustment
miningParams.blocksUntilHalving = Math.max(0, miningParams.blocksUntilHalving - 1);
miningParams.blocksUntilDifficultyAdjustment = Math.max(0, miningParams.blocksUntilDifficultyAdjustment - 1);
// Check for halving event
if (miningParams.blocksUntilHalving === 0) {
miningParams.blockReward /= 2;
miningParams.blocksUntilHalving = 210000;
showToast('Block reward halved! New reward: ' + miningParams.blockReward + ' STRM');
}
// Check for difficulty adjustment
if (miningParams.blocksUntilDifficultyAdjustment === 0) {
// Simple difficulty adjustment based on block time (target is 10 minutes)
const actualBlockTime = (Date.now() - miningParams.lastBlockTime) / 1000 / 60; // in minutes
const adjustmentFactor = Math.min(4, Math.max(0.25, miningParams.blockTimeTarget / actualBlockTime));
miningParams.difficulty *= adjustmentFactor;
miningParams.blocksUntilDifficultyAdjustment = 2016;
showToast(`Difficulty adjusted to ${miningParams.difficulty.toFixed(1)}`);
}
miningParams.lastBlockTime = Date.now();
}
});
// Update UI
renderStakedPools();
}, 10000); // Check every 10 seconds
}
// Function to stop mining
function stopMining() {
if (miningInterval) {
clearInterval(miningInterval);
miningInterval = null;
}
// Update all pools to not mining
userData.stakedPools.forEach(pool => {
pool.mining = false;
});
// Update UI
renderStakedPools();
showToast('Mining stopped');
}
// Function to add STRM tokens
function addStrm(amount) {
userData.strmBalance += amount;
renderStakedPools();
showToast(`Added ${amount} STRM to your balance`);
}
// Function to claim mining rewards
function claimRewards(poolName) {
const pool = userData.stakedPools.find(p => p.name === poolName);
if (pool && pool.earned > 0) {
userData.strmBalance += pool.earned;
const earned = pool.earned;
pool.earned = 0;
// Update UI
renderStakedPools();
showToast(`Claimed ${earned} STRM from ${poolName}`);
}
}
// Initialize the app
function initApp() {
renderStreamingPools();
renderStakedPools();
// Start mining automatically if user has staked pools
if (userData.stakedPools.some(p => p.mining)) {
startMining();
}
// Simulate changing stream data every 30 seconds
setInterval(() => {
// Randomly adjust some pool data
Object.values(musicCategories).forEach(category => {
category.pools.forEach(pool => {
// Base random change between -5% and +10%
let changePercent = (Math.random() * 15) - 5;
// Apply sentiment multiplier (0-100 scale mapped to 0.5-1.5 multiplier)
const sentimentMultiplier = 1 + (pool.sentiment / 200);
changePercent *= sentimentMultiplier;
const change = Math.round(pool.currentStreams * (changePercent / 100));
pool.previousStreams = pool.currentStreams;
pool.currentStreams = Math.max(1000, pool.currentStreams + change);
});
// Update category totals
category.totalStreams = category.pools.reduce((sum, pool) => sum + pool.currentStreams, 0);
});
// Update UI
renderStreamingPools();
renderStakedPools();
}, 30000);
}
// Tab switching functionality
document.querySelectorAll('.tab-button').forEach(button => {
button.addEventListener('click', function() {
// Remove active class from all buttons
document.querySelectorAll('.tab-button').forEach(btn => {
btn.classList.remove('active', 'text-strm-bg');
btn.classList.add('text-gray-500');
});
// Add active class to clicked button
this.classList.add('active', 'text-strm-bg');
this.classList.remove('text-gray-500');
// Hide all tabs
document.querySelectorAll('#mainContent > div').forEach(tab => {
tab.classList.add('hidden');
});
// Show selected tab
const tabId = this.getAttribute('data-tab');
document.getElementById(tabId).classList.remove('hidden');
// Scroll to top when switching tabs
document.getElementById('mainContent').scrollTo(0, 0);
// If switching to search tab, focus search input
if (tabId === 'searchTab') {
setTimeout(() => {
document.getElementById('poolSearchInput').focus();
}, 100);
}
});
});
// Search pool functionality
document.getElementById('searchPoolBtn').addEventListener('click', function() {
const query = document.getElementById('poolSearchInput').value.trim();
if (query) {
showSearchResults(query);
}
});
document.getElementById('poolSearchInput').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
const query = this.value.trim();
if (query) {
showSearchResults(query);
}
}
});
// Add pool button
document.getElementById('addPoolBtn').addEventListener('click', function() {
document.getElementById('addPoolModal').classList.remove('hidden');
});
// Cancel add pool
document.getElementById('cancelAddPool').addEventListener('click', function() {
document.getElementById('addPoolModal').classList.add('hidden');
});
// Save new pool
document.getElementById('savePool').addEventListener('click', function() {
const poolName = document.getElementById('newPoolName').value.trim();
const platform = document.querySelector('.platform-btn.bg-strm-bg')?.getAttribute('data-platform');
if (!poolName || !platform) {
showToast('Please enter a pool name and select a category');
return;
}
// Add to staked pools
userData.stakedPools.push({
name: poolName,
platform,
hasStrm: false,
stakedAmount: 0,
sentiment: 0,
earned: 0,
mining: false,
hashrate: 0
});
// Add to music category
musicCategories[platform].pools.push({
name: poolName,
currentStreams: 10000,
previousStreams: 0,
sentiment: 0
});
musicCategories[platform].totalStreams += 10000;
// Update UI
renderStreamingPools();
renderStakedPools();
document.getElementById('addPoolModal').classList.add('hidden');
document.getElementById('newPoolName').value = '';
showToast(`${poolName} created successfully`);
});
// Platform selection in add pool modal
document.querySelectorAll('.platform-btn').forEach(btn => {
btn.addEventListener('click', function() {
document.querySelectorAll('.platform-btn').forEach(b => {
b.classList.remove('bg-strm-bg', 'text-white');
b.classList.add('bg-gray-200', 'text-gray-700');
});
this.classList.remove('bg-gray-200', 'text-gray-700');
this.classList.add('bg-strm-bg', 'text-white');
});
});
// Stake STRM button
document.getElementById('stakeBtn').addEventListener('click', function() {
const poolName = document.getElementById('poolSelect').value;
const amount = parseFloat(document.getElementById('stakeAmount').value);
stakeStrm(poolName, amount);
});
// STRM balance button
document.getElementById('strmBalanceBtn').addEventListener('click', function() {
document.getElementById('strmModal').classList.remove('hidden');
});
// Close STRM modal
document.getElementById('closeStrmModal').addEventListener('click', function() {
document.getElementById('strmModal').classList.add('hidden');
});
// Add STRM button
document.getElementById('addStrmBtn').addEventListener('click', function() {
addStrm(1000);
});
// Explainer button
document.getElementById('explainerBtn').addEventListener('click', function() {
document.getElementById('explainerModal').classList.remove('hidden');
});
// Close explainer modal
document.getElementById('closeExplainerModal').addEventListener('click', function() {
document.getElementById('explainerModal').classList.add('hidden');
});
// Close explainer button
document.getElementById('closeExplainerBtn').addEventListener('click', function() {
document.getElementById('explainerModal').classList.add('hidden');
});
// Stake info button
document.getElementById('stakeInfoBtn').addEventListener('click', function() {
document.getElementById('explainerModal').classList.remove('hidden');
});
// Mining button
document.getElementById('miningBtn').addEventListener('click', function() {
if (userData.stakedPools.length > 0) {
openMiningModal(userData.stakedPools[0].name, userData.stakedPools[0].platform);
} else {
showToast('You have no pools staked for mining');
}
});
// Stop mining button
document.getElementById('stopMiningBtn').addEventListener('click', function() {
stopMining();
document.getElementById('miningModal').classList.add('hidden');
});
// Claim mining button
document.getElementById('claimMiningBtn').addEventListener('click', function() {
claimRewards(currentMiningPool);
document.getElementById('miningModal').classList.add('hidden');
});
// Toast notification function
function showToast(message) {
const toast = document.getElementById('toast');
const toastMessage = document.getElementById('toastMessage');
toastMessage.textContent = message;
toast.classList.remove('hidden');
setTimeout(() => {
toast.classList.add('hidden');
}, 3000);
}
// Initialize the app when DOM is loaded
document.addEventListener('DOMContentLoaded', initApp);
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=privateuserh/privstrm0-01pa" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>