├── package.json ├── README.md └── main.js /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "soundcloud_v1.3", 3 | "productName": "soundcloud_v1.3", 4 | "description": "SoundCloud avec fonctionnalités avancées", 5 | "keywords": [], 6 | "main": "./main.js", 7 | "version": "1.3.0", 8 | "author": "Arizaki", 9 | "scripts": { 10 | "start": "electron .", 11 | "build": "electron-builder", 12 | "pack": "electron-builder --dir", 13 | "dist": "electron-builder" 14 | }, 15 | "dependencies": { 16 | "discord-rpc": "4.0.1", 17 | "electron-squirrel-startup": "^1.0.1", 18 | "fs": "0.0.1-security", 19 | "node-html-parser": "^7.0.1", 20 | "path": "0.12.7" 21 | }, 22 | "devDependencies": { 23 | "electron": "23.2.0", 24 | "electron-builder": "^26.0.12" 25 | }, 26 | "build": { 27 | "appId": "com.arizaki.soundcloud", 28 | "productName": "SoundCloud v1.3", 29 | "copyright": "Copyright © 2025 Arizaki", 30 | "icon": "soundcloud.ico", 31 | "win": { 32 | "icon": "soundcloud.ico", 33 | "target": ["nsis"], 34 | "artifactName": "SoundCloud-v1.3-setup-${version}.${ext}" 35 | }, 36 | "nsis": { 37 | "oneClick": false, 38 | "allowToChangeInstallationDirectory": true, 39 | "installerIcon": "soundcloud.ico", 40 | "uninstallerIcon": "soundcloud.ico", 41 | "installerHeaderIcon": "soundcloud.ico", 42 | "createDesktopShortcut": true, 43 | "createStartMenuShortcut": true 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # 🎵 SoundCloud-cracked 4 | 5 | Bannière de l'application 6 | 7 | [![Version](https://img.shields.io/badge/version-1.3-blue.svg?style=for-the-badge)](https://github.com/ArizakiDev/SoundCloud-cracked/releases) 8 | [![License](https://img.shields.io/badge/license-MIT-green.svg?style=for-the-badge)](LICENSE) 9 | [![Made by Arizaki](https://img.shields.io/badge/Made%20by-Arizaki-purple.svg?style=for-the-badge)](https://github.com/ArizakiDev) 10 | [![Downloads](https://img.shields.io/badge/downloads-1K%2B-orange.svg?style=for-the-badge)](https://github.com/ArizakiDev/SoundCloud-cracked/releases) 11 | [![Stars](https://img.shields.io/github/stars/ArizakiDev/SoundCloud-cracked?style=for-the-badge)](https://github.com/ArizakiDev/SoundCloud-cracked/stargazers) 12 | 13 | *Une version améliorée de SoundCloud avec retrait des publicités et intégration Discord RPC* 14 | 15 | [🇫🇷 Français](#français) | [🇬🇧 English](#english) | [📥 Télécharger](#installation) | [⭐ Star](https://github.com/ArizakiDev/SoundCloud-cracked) 16 | 17 |
18 | 19 | --- 20 | 21 |
22 | 23 | # 🇫🇷 Français 24 | 25 | ## ✨ Fonctionnalités 26 | 27 | ### 🚫 Suppression des Publicités 28 | - Navigation fluide sans interruptions publicitaires 29 | - Expérience d'écoute pure et sans distractions 30 | - Blocage intelligent des publicités 31 | 32 | ### 🎮 Discord Rich Presence 33 | - Partagez votre activité musicale en temps réel 34 | - Montrez à vos amis ce que vous écoutez sur Discord 35 | - Statut personnalisé avec pochette d'album 36 | 37 | ### 🎯 Autres Fonctionnalités 38 | - 🚀 Performance optimisée 39 | - 💻 Interface épurée 40 | - 🔄 Mises à jour régulières 41 | 42 | ## 📊 Statistiques 43 | ``` 44 | 📥 +1000 Téléchargements 45 | ⭐ +11 Stars 46 | 🔄 Mis à jour régulièrement 47 | ``` 48 | 49 | ## 📝 Journal des modifications 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 65 | 66 | 67 | 68 | 69 | 73 | 74 | 75 | 76 | 77 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 |
VersionÉtatModifications
1.3 61 | • Ajout: Minuterie
62 | • Amélioration: Discord rpc
63 | • D'autres ajouts..
64 |
1.2.1⚠️ 70 | • Ajout: Version Android
71 | • à corriger: Page de connexion
72 |
1.2 78 | • Fix: Retrait des pubs
79 | • Fix: RPC Discord
80 | • Amélioration des performances 81 |
1.1📦• Ajout: Suppression des publicités
1.0🎉• Version initiale
94 | 95 | ## 📥 Installation 96 | 97 | ### Méthode 1: Téléchargement direct 98 | 1. Cliquez sur [ce lien](https://github.com/ArizakiDev/SoundCloud-cracked/releases/download/1.2/SoundCloud.Crack.exe) 99 | 2. Lancez l'installation 100 | 3. Profitez de l'application ! 🎉 101 | 102 | ### Méthode 2: Via les releases 103 | 1. Rendez-vous dans la section [Releases](https://github.com/ArizakiDev/SoundCloud-cracked/releases) 104 | 2. Téléchargez la dernière version disponible 105 | 3. Suivez les instructions d'installation 106 | 107 |
108 | 109 | --- 110 | 111 |
112 | 113 | # 🇬🇧 English 114 | 115 | ## ✨ Features 116 | 117 | ### 🚫 Ad Removal 118 | - Smooth navigation without ad interruptions 119 | - Pure and distraction-free listening experience 120 | - Smart ad blocking system 121 | 122 | ### 🎮 Discord Rich Presence 123 | - Share your musical activity in real time 124 | - Show your friends what you're listening to on Discord 125 | - Custom status with album artwork 126 | 127 | ### 🎯 Other Features 128 | - 🚀 Optimized performance 129 | - 💻 Clean interface 130 | - 🔄 Regular updates 131 | 132 | ## 📊 Statistics 133 | ``` 134 | 📥 +1000 Downloads 135 | ⭐ +11 Stars 136 | 🔄 Updated regularly 137 | ``` 138 | 139 | ## 📝 Changelog 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 155 | 156 | 157 | 158 | 159 | 162 | 163 | 164 | 165 | 166 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 |
VersionStatusChanges
1.3 151 | • add: sleep timer
152 | • upgrade: discord rpc
153 | • More add
154 |
1.2.1⚠️ 160 | • add: Android Version
161 |
1.2 167 | • Fix: Ads removal
168 | • Fix: Discord RPC
169 | • Performance improvements 170 |
1.1📦• Added: Ads removal
1.0🎉• Initial release
183 | 184 | ## 📥 Installation 185 | 186 | ### Method 1: Direct download 187 | 1. Click on [this link](https://github.com/ArizakiDev/SoundCloud-cracked/releases/download/1.0/soundcloud.By.Arizaki.exe) 188 | 2. Run the installation 189 | 3. Enjoy the app! 🎉 190 | 191 | ### Method 2: Via releases 192 | 1. Go to the [Releases](https://github.com/ArizakiDev/SoundCloud-cracked/releases) section 193 | 2. Download the latest version 194 | 3. Follow the installation instructions 195 | 196 |
197 | 198 | --- 199 | 200 |
201 | 202 | ## 🌟 Nous soutenir | Support Us 203 | 204 | [![Star on GitHub](https://img.shields.io/github/stars/ArizakiDev/SoundCloud-cracked?style=social)](https://github.com/ArizakiDev/SoundCloud-cracked/stargazers) 205 | [![Follow on GitHub](https://img.shields.io/github/followers/ArizakiDev?style=social)](https://github.com/ArizakiDev) 206 | 207 | ## ⚠️ Avertissement | Warning 208 | ```diff 209 | - Ce projet est destiné uniquement à des fins éducatives et de développement. 210 | - This project is intended for educational and development purposes only. 211 | ``` 212 | 213 | ## 📜 Licence | License 214 | Ce projet est sous licence MIT. 215 | This project is under the MIT license. 216 | 217 |
218 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SoundCloud v1.3 - Client optimisé 3 | * Développé par Arizaki 4 | * Optimisé par Arizaki 5 | * Copyright © 2025 Arizaki 6 | * 7 | * Application permettant d'écouter de la musique depuis SoundCloud 8 | * avec des fonctionnalités avancées comme Discord RPC et blocage de publicités. 9 | */ 10 | 11 | const { app, BrowserWindow, Menu, shell, dialog, ipcMain } = require('electron'); 12 | const DiscordRPC = require('discord-rpc'); 13 | const fs = require('fs'); 14 | const path = require('path'); 15 | const https = require('https'); 16 | const http = require('http'); 17 | const clientId = '1090770350251458592'; 18 | let rpc = null; 19 | let rpcConnected = false; 20 | let rpcReconnectAttempt = 0; 21 | const MAX_RECONNECT_ATTEMPTS = 5; 22 | 23 | let mainWindow; 24 | let currentTrack = null; 25 | let checkForRPC = null; 26 | let sleepTimerTimeout = null; 27 | let lastRpcUpdate = 0; 28 | let cachedRpcData = null; 29 | let rpcUpdateInterval = 10000; 30 | let artworkCache = new Map(); 31 | let logEnabled = false; 32 | const iconPath = path.join(__dirname, 'soundcloud.ico'); 33 | 34 | // Gestion de la langue 35 | let currentLanguage = 'fr'; 36 | 37 | // Définitions des textes multilingues 38 | const locales = { 39 | fr: { 40 | app_title: 'SoundCloud - by Arizaki v1.3', 41 | menu: { 42 | home: 'Accueil', 43 | sleep_timer: 'Minuteur de sommeil', 44 | official_site: 'Site officiel', 45 | github: 'Github', 46 | about: 'À propos', 47 | refresh: 'Rafraîchir', 48 | quit: 'Quitter', 49 | devtools: 'Activer/Désactiver DevTools', 50 | language: 'Langue', 51 | language_fr: 'Français', 52 | language_en: 'Anglais', 53 | language_auto: 'Automatique' 54 | }, 55 | about_dialog: { 56 | title: 'À propos de SoundCloud v1.3', 57 | message: 'SoundCloud v1.3 - Client optimisé', 58 | detail: 'Développé par Arizaki\nOptimisé par Arizaki\nCopyright © 2025 Arizaki\n\nApplication permettant d\'écouter de la musique depuis SoundCloud avec des fonctionnalités avancées.' 59 | }, 60 | sleep_timer: { 61 | title: 'Minuteur de sommeil', 62 | message: 'Arrêter la lecture après:', 63 | custom_title: 'Durée personnalisée', 64 | custom_message: 'Entrez la durée en minutes:', 65 | activated_title: 'Minuteur activé', 66 | activated_message: 'La lecture s\'arrêtera dans {minutes} minutes', 67 | stopped_title: 'Minuteur de sommeil', 68 | stopped_message: 'La lecture a été mise en pause par le minuteur' 69 | }, 70 | options: { 71 | min15: '15 minutes', 72 | min30: '30 minutes', 73 | min45: '45 minutes', 74 | hour1: '1 heure', 75 | hour1_30: '1h30', 76 | hour2: '2 heures', 77 | custom: 'Personnalisé...', 78 | disable: 'Désactiver', 79 | cancel: 'Annuler' 80 | } 81 | }, 82 | en: { 83 | app_title: 'SoundCloud - Player v1.3', 84 | menu: { 85 | home: 'Home', 86 | sleep_timer: 'Sleep Timer', 87 | official_site: 'Official Website', 88 | github: 'Github', 89 | about: 'About', 90 | refresh: 'Refresh', 91 | quit: 'Quit', 92 | devtools: 'Toggle DevTools', 93 | language: 'Language', 94 | language_fr: 'French', 95 | language_en: 'English', 96 | language_auto: 'Automatic' 97 | }, 98 | about_dialog: { 99 | title: 'About SoundCloud v1.3', 100 | message: 'SoundCloud v1.3 - Optimized Client', 101 | detail: 'Developed by Arizaki\nOptimized by Arizaki\nCopyright © 2025 Arizaki\n\nApplication allowing you to listen to music from SoundCloud with advanced features.' 102 | }, 103 | sleep_timer: { 104 | title: 'Sleep Timer', 105 | message: 'Stop playback after:', 106 | custom_title: 'Custom Duration', 107 | custom_message: 'Enter duration in minutes:', 108 | activated_title: 'Timer Activated', 109 | activated_message: 'Playback will stop in {minutes} minutes', 110 | stopped_title: 'Sleep Timer', 111 | stopped_message: 'Playback has been paused by the timer' 112 | }, 113 | options: { 114 | min15: '15 minutes', 115 | min30: '30 minutes', 116 | min45: '45 minutes', 117 | hour1: '1 hour', 118 | hour1_30: '1h30', 119 | hour2: '2 hours', 120 | custom: 'Custom...', 121 | disable: 'Disable', 122 | cancel: 'Cancel' 123 | } 124 | } 125 | }; 126 | 127 | // Fonction pour déterminer la langue du système 128 | function detectSystemLanguage() { 129 | const osLocale = app.getLocale() || ''; 130 | // Si la langue du système commence par 'fr', on utilise le français 131 | if (osLocale.startsWith('fr')) { 132 | return 'fr'; 133 | } 134 | // Sinon on utilise l'anglais par défaut 135 | return 'en'; 136 | } 137 | 138 | // Fonction pour changer la langue 139 | function changeLanguage(lang) { 140 | if (lang === 'auto') { 141 | currentLanguage = detectSystemLanguage(); 142 | } else { 143 | currentLanguage = lang; 144 | } 145 | 146 | // Mettre à jour le menu 147 | createMenu(); 148 | 149 | // Mettre à jour le titre de l'application 150 | if (mainWindow) { 151 | mainWindow.setTitle(locales[currentLanguage].app_title); 152 | } 153 | } 154 | 155 | function connectDiscordRPC() { 156 | try { 157 | if (rpc) { 158 | try { 159 | rpc.destroy(); 160 | rpc = null; 161 | } catch (e) { 162 | if (logEnabled) console.log('Erreur lors de la destruction de la connexion RPC précédente:', e); 163 | } 164 | } 165 | 166 | if (logEnabled) console.log('Tentative de connexion à Discord RPC...'); 167 | 168 | DiscordRPC.register(clientId); 169 | 170 | rpc = new DiscordRPC.Client({ transport: 'ipc' }); 171 | 172 | rpc.on('ready', () => { 173 | if (logEnabled) console.log('Discord RPC connecté avec succès'); 174 | rpcConnected = true; 175 | rpcReconnectAttempt = 0; 176 | 177 | try { 178 | setDefaultActivity(); 179 | } catch (err) { 180 | if (logEnabled) console.error('Erreur lors de la définition de l\'activité initiale:', err); 181 | } 182 | }); 183 | 184 | rpc.login({ clientId }).catch(error => { 185 | if (logEnabled) console.error('Échec de la connexion à Discord RPC:', error); 186 | rpcConnected = false; 187 | 188 | if (rpcReconnectAttempt < MAX_RECONNECT_ATTEMPTS) { 189 | rpcReconnectAttempt++; 190 | if (logEnabled) console.log(`Tentative de reconnexion (${rpcReconnectAttempt}/${MAX_RECONNECT_ATTEMPTS})...`); 191 | setTimeout(connectDiscordRPC, 20000); 192 | } 193 | }); 194 | } catch (error) { 195 | if (logEnabled) console.error('Erreur lors de l\'initialisation de Discord RPC:', error); 196 | 197 | if (rpcReconnectAttempt < MAX_RECONNECT_ATTEMPTS) { 198 | rpcReconnectAttempt++; 199 | if (logEnabled) console.log(`Tentative de reconnexion (${rpcReconnectAttempt}/${MAX_RECONNECT_ATTEMPTS})...`); 200 | setTimeout(connectDiscordRPC, 20000); 201 | } 202 | } 203 | } 204 | 205 | function setDefaultActivity() { 206 | if (!rpcConnected || !rpc) return; 207 | 208 | try { 209 | const texts = locales[currentLanguage]; 210 | const isEnglish = currentLanguage === 'en'; 211 | 212 | const activity = { 213 | details: isEnglish ? 'listening on Soundcloud' : 'écoute sur Soundcloud', 214 | state: isEnglish ? 'Waiting...' : 'En attente...', 215 | largeImageKey: 'soundcloud-logo', 216 | largeImageText: 'by Arizaki', 217 | buttons: [ 218 | { 219 | label: isEnglish ? "Listen on SoundCloud" : "Écouter sur SoundCloud", 220 | url: "https://soundcloud.com/" 221 | } 222 | ], 223 | instance: false 224 | }; 225 | 226 | rpc.setActivity(activity).catch(err => { 227 | if (logEnabled) console.warn('Échec de la définition de l\'activité par défaut:', err); 228 | }); 229 | 230 | if (logEnabled) console.log('Activité par défaut définie'); 231 | } catch (error) { 232 | if (logEnabled) console.warn('Erreur lors de la définition de l\'activité par défaut:', error); 233 | } 234 | } 235 | 236 | function shortenString(str, customLength) { 237 | if (!str) return ''; 238 | 239 | const maxLength = customLength || 100; 240 | 241 | if (str.length > maxLength) { 242 | return str.substring(0, maxLength) + '...'; 243 | } 244 | 245 | return str; 246 | } 247 | 248 | async function getOptimizedArtworkUrl(url) { 249 | if (!url) return 'soundcloud-logo'; 250 | 251 | if (artworkCache.has(url)) { 252 | return artworkCache.get(url); 253 | } 254 | 255 | const optimizedUrl = url.replace(/\d+x\d+/, "200x200"); 256 | 257 | if (artworkCache.size > 50) { 258 | const firstKey = artworkCache.keys().next().value; 259 | artworkCache.delete(firstKey); 260 | } 261 | 262 | artworkCache.set(url, optimizedUrl); 263 | return optimizedUrl; 264 | } 265 | 266 | async function injectCustomFeatures() { 267 | try { 268 | if (!mainWindow) return; 269 | 270 | const customCSS = ` 271 | .sc-sleep-timer-btn { 272 | background: none; 273 | border: none; 274 | font-size: 16px; 275 | cursor: pointer; 276 | margin-left: 10px; 277 | vertical-align: middle; 278 | } 279 | `; 280 | 281 | await mainWindow.webContents.insertCSS(customCSS); 282 | 283 | const injectionResult = await mainWindow.webContents.executeJavaScript(` 284 | if (window.scFeaturesInjected) { 285 | return "Déjà injecté"; 286 | } 287 | 288 | window.scFeaturesInjected = true; 289 | 290 | function addSleepTimerButton() { 291 | const playerExtra = document.querySelector('.playControls__elements'); 292 | if (playerExtra && !playerExtra.querySelector('.sc-sleep-timer-btn')) { 293 | const timerBtn = document.createElement('button'); 294 | timerBtn.className = 'sc-sleep-timer-btn'; 295 | timerBtn.innerHTML = '⏱️'; 296 | timerBtn.title = 'Minuteur de sommeil'; 297 | 298 | timerBtn.onclick = function() { 299 | window.postMessage({ 300 | type: 'show-sleep-timer' 301 | }, '*'); 302 | }; 303 | 304 | playerExtra.appendChild(timerBtn); 305 | } 306 | } 307 | 308 | const observer = new MutationObserver((mutations) => { 309 | for (const mutation of mutations) { 310 | if (mutation.addedNodes.length) { 311 | if (document.querySelector('.playControls__elements') && 312 | !document.querySelector('.sc-sleep-timer-btn')) { 313 | addSleepTimerButton(); 314 | break; 315 | } 316 | } 317 | } 318 | }); 319 | 320 | observer.observe(document.body, { 321 | childList: true, 322 | subtree: true, 323 | attributes: false, 324 | characterData: false 325 | }); 326 | 327 | addSleepTimerButton(); 328 | 329 | let lastTrackInfo = null; 330 | let lastUpdateTime = 0; 331 | 332 | function checkCurrentTrack() { 333 | try { 334 | const now = Date.now(); 335 | if (now - lastUpdateTime < 1000) return; 336 | lastUpdateTime = now; 337 | 338 | const playControls = document.querySelector('.playControls'); 339 | if (!playControls) return; 340 | 341 | const isPlaying = playControls.classList.contains('m-playing'); 342 | 343 | const titleElement = document.querySelector('.playbackSoundBadge__titleLink'); 344 | const artistElement = document.querySelector('.playbackSoundBadge__lightLink'); 345 | 346 | if (!titleElement || !artistElement) return; 347 | 348 | const title = titleElement.title || titleElement.textContent.trim(); 349 | const artist = artistElement.title || artistElement.textContent.trim(); 350 | 351 | if (lastTrackInfo && 352 | lastTrackInfo.title === title && 353 | lastTrackInfo.artist === artist && 354 | lastTrackInfo.isPlaying === isPlaying) { 355 | return; 356 | } 357 | 358 | const artworkElement = document.querySelector('.playbackSoundBadge__avatar'); 359 | const artworkUrl = artworkElement ? 360 | (artworkElement.querySelector('span')?.style.backgroundImage.replace(/^url\\(['"](.+?)['"]\\)$/, '$1') || 361 | artworkElement.querySelector('img')?.src || '') : ''; 362 | 363 | const trackUrl = titleElement ? titleElement.href : ''; 364 | const artistUrl = artistElement ? artistElement.href : ''; 365 | 366 | const timeElement = document.querySelector('.playbackTimeline__timePassed span:last-child'); 367 | const durationElement = document.querySelector('.playbackTimeline__duration span:last-child'); 368 | 369 | const currentTime = timeElement ? timeElement.textContent : '0:00'; 370 | const duration = durationElement ? durationElement.textContent : '0:00'; 371 | 372 | if (title) { 373 | const trackInfo = { 374 | title, 375 | artist, 376 | artworkUrl, 377 | trackUrl, 378 | artistUrl, 379 | currentTime, 380 | duration, 381 | isPlaying 382 | }; 383 | 384 | lastTrackInfo = { 385 | title, 386 | artist, 387 | isPlaying 388 | }; 389 | 390 | window.postMessage({ 391 | type: 'track-update', 392 | trackInfo: trackInfo 393 | }, '*'); 394 | } 395 | } catch (error) { 396 | console.error('Erreur lors de la vérification de la piste actuelle:', error); 397 | } 398 | } 399 | 400 | setInterval(checkCurrentTrack, 2000); 401 | 402 | checkCurrentTrack(); 403 | 404 | window.addEventListener('message', event => { 405 | const { type } = event.data; 406 | 407 | if (type === 'show-sleep-timer') { 408 | window.postMessage({ 409 | type: 'show-sleep-timer' 410 | }, '*'); 411 | } 412 | }); 413 | 414 | return "Fonctionnalités injectées avec succès"; 415 | `); 416 | 417 | if (logEnabled) console.log('Résultat de l\'injection:', injectionResult); 418 | 419 | if (logEnabled) { 420 | mainWindow.webContents.on('console-message', (event, level, message) => { 421 | if (message.includes('error') || message.includes('Error')) { 422 | console.log('Console du renderer:', message); 423 | } 424 | }); 425 | } 426 | 427 | mainWindow.webContents.executeJavaScript(` 428 | window.addEventListener('message', event => { 429 | const { type, trackInfo } = event.data; 430 | 431 | if (type === 'track-update' && trackInfo) { 432 | const { ipcRenderer } = require('electron'); 433 | ipcRenderer.send('track-update', trackInfo); 434 | } else if (type === 'show-sleep-timer') { 435 | const { ipcRenderer } = require('electron'); 436 | ipcRenderer.send('show-sleep-timer'); 437 | } 438 | }); 439 | `); 440 | 441 | return true; 442 | } catch (error) { 443 | if (logEnabled) console.error('Erreur lors de l\'injection des fonctionnalités personnalisées:', error); 444 | return false; 445 | } 446 | } 447 | 448 | function showSleepTimer() { 449 | if (!mainWindow) return; 450 | 451 | const texts = locales[currentLanguage]; 452 | 453 | if (sleepTimerTimeout) { 454 | clearTimeout(sleepTimerTimeout); 455 | sleepTimerTimeout = null; 456 | } 457 | 458 | const options = [ 459 | { minutes: 15, label: texts.options.min15 }, 460 | { minutes: 30, label: texts.options.min30 }, 461 | { minutes: 45, label: texts.options.min45 }, 462 | { minutes: 60, label: texts.options.hour1 }, 463 | { minutes: 90, label: texts.options.hour1_30 }, 464 | { minutes: 120, label: texts.options.hour2 }, 465 | { minutes: -1, label: texts.options.custom }, 466 | { minutes: 0, label: texts.options.disable } 467 | ]; 468 | 469 | dialog.showMessageBox(mainWindow, { 470 | type: 'question', 471 | title: texts.sleep_timer.title, 472 | message: texts.sleep_timer.message, 473 | buttons: options.map(opt => opt.label), 474 | cancelId: options.length - 1 475 | }).then(async result => { 476 | const selectedOption = options[result.response]; 477 | 478 | if (selectedOption.minutes === -1) { 479 | const customResult = await dialog.showMessageBox(mainWindow, { 480 | type: 'question', 481 | title: texts.sleep_timer.custom_title, 482 | message: texts.sleep_timer.custom_message, 483 | buttons: [texts.options.cancel, '5', '10', '20', '40', '50', '75', '100', '150', '180'], 484 | cancelId: 0 485 | }); 486 | 487 | if (customResult.response === 0) { 488 | return; 489 | } 490 | 491 | const customMinutes = parseInt(customResult.buttons[customResult.response]); 492 | setTimerForMinutes(customMinutes); 493 | } else if (selectedOption.minutes > 0) { 494 | setTimerForMinutes(selectedOption.minutes); 495 | } 496 | }).catch(err => { 497 | console.error('Erreur lors de l\'affichage du minuteur:', err); 498 | }); 499 | 500 | function setTimerForMinutes(minutes) { 501 | const timeInMs = minutes * 60 * 1000; 502 | 503 | dialog.showMessageBox({ 504 | type: 'info', 505 | title: texts.sleep_timer.activated_title, 506 | message: texts.sleep_timer.activated_message.replace('{minutes}', minutes) 507 | }); 508 | 509 | sleepTimerTimeout = setTimeout(() => { 510 | mainWindow.webContents.executeJavaScript(` 511 | const playButton = document.querySelector('.playControls__play'); 512 | if (playButton && playButton.classList.contains('playing')) { 513 | playButton.click(); 514 | } 515 | `).catch(() => {}); 516 | 517 | dialog.showMessageBox({ 518 | type: 'info', 519 | title: texts.sleep_timer.stopped_title, 520 | message: texts.sleep_timer.stopped_message 521 | }); 522 | 523 | sleepTimerTimeout = null; 524 | }, timeInMs); 525 | } 526 | } 527 | 528 | async function updateDiscordRPC() { 529 | if (!rpcConnected || !rpc || !mainWindow) return; 530 | 531 | try { 532 | const now = Date.now(); 533 | if (now - lastRpcUpdate < rpcUpdateInterval) { 534 | return; 535 | } 536 | lastRpcUpdate = now; 537 | 538 | const playerInfo = await mainWindow.webContents.executeJavaScript(` 539 | (function() { 540 | try { 541 | const playButton = document.querySelector('.playControls__play'); 542 | const isPlaying = playButton && playButton.classList.contains('playing'); 543 | 544 | const titleEl = document.querySelector('.playbackSoundBadge__titleLink'); 545 | const artistEl = document.querySelector('.playbackSoundBadge__lightLink'); 546 | const artworkEl = document.querySelector('.playbackSoundBadge__avatar .image__lightOutline span'); 547 | 548 | let trackInfo = null; 549 | 550 | if (titleEl && artistEl) { 551 | trackInfo = { 552 | title: titleEl.innerText || 'Piste inconnue', 553 | artist: artistEl.innerText || 'Artiste inconnu', 554 | url: titleEl.href || 'https://soundcloud.com/', 555 | artwork: '' 556 | }; 557 | 558 | if (artworkEl && artworkEl.style && artworkEl.style.backgroundImage) { 559 | trackInfo.artwork = artworkEl.style.backgroundImage.replace('url("', '').replace('")', ''); 560 | } 561 | } 562 | 563 | return { isPlaying, trackInfo }; 564 | } catch (e) { 565 | return { isPlaying: false, trackInfo: null }; 566 | } 567 | })(); 568 | `).catch(err => { 569 | return { isPlaying: false, trackInfo: null }; 570 | }); 571 | 572 | const hasTrackChanged = playerInfo.trackInfo && 573 | (!cachedRpcData || 574 | cachedRpcData.title !== playerInfo.trackInfo.title || 575 | cachedRpcData.artist !== playerInfo.trackInfo.artist || 576 | cachedRpcData.isPlaying !== playerInfo.isPlaying); 577 | 578 | if (!hasTrackChanged && cachedRpcData) { 579 | return; 580 | } 581 | 582 | if (playerInfo.trackInfo) { 583 | currentTrack = playerInfo.trackInfo; 584 | } 585 | 586 | if (playerInfo.trackInfo) { 587 | cachedRpcData = { 588 | title: playerInfo.trackInfo.title, 589 | artist: playerInfo.trackInfo.artist, 590 | isPlaying: playerInfo.isPlaying 591 | }; 592 | } 593 | 594 | const isEnglish = currentLanguage === 'en'; 595 | 596 | if (playerInfo.isPlaying && currentTrack) { 597 | try { 598 | const artworkUrl = await getOptimizedArtworkUrl(currentTrack.artwork); 599 | 600 | const safeTitle = shortenString(currentTrack.title || '', 80); 601 | const safeArtist = shortenString(currentTrack.artist || '', 30); 602 | 603 | const totalDetails = safeTitle.length + safeArtist.length; 604 | let finalTitle = safeTitle; 605 | let finalArtist = safeArtist; 606 | 607 | if (totalDetails > 200) { 608 | finalTitle = shortenString(currentTrack.title || '', 80); 609 | finalArtist = shortenString(currentTrack.artist || '', 30); 610 | } 611 | 612 | await rpc.setActivity({ 613 | details: finalTitle, 614 | state: isEnglish ? `By ${finalArtist}` : `Par ${finalArtist}`, 615 | largeImageKey: artworkUrl, 616 | largeImageText: 'By Arizaki', 617 | buttons: [ 618 | { 619 | label: isEnglish ? "Listen on SoundCloud" : "Écouter sur SoundCloud", 620 | url: currentTrack.url || "https://soundcloud.com/" 621 | } 622 | ], 623 | instance: false 624 | }).catch(error => { 625 | if (logEnabled) console.warn('Erreur RPC, tentative avec des paramètres encore plus simples'); 626 | 627 | rpc.setActivity({ 628 | details: isEnglish ? "Listening to SoundCloud" : "Écoute SoundCloud", 629 | state: isEnglish ? "Music playing..." : "Musique en cours...", 630 | largeImageKey: 'soundcloud-logo', 631 | instance: false 632 | }).catch(e => { 633 | if (logEnabled) console.error('Échec complet de la mise à jour RPC'); 634 | }); 635 | }); 636 | } catch (error) { 637 | if (logEnabled) console.warn('Erreur lors de la mise à jour de Discord RPC (lecture)'); 638 | if (error && error.message && error.message.includes('connection closed')) { 639 | rpcConnected = false; 640 | if (rpcReconnectAttempt < MAX_RECONNECT_ATTEMPTS) { 641 | rpcReconnectAttempt++; 642 | if (logEnabled) console.log(`Erreur RPC détectée. Tentative de reconnexion (${rpcReconnectAttempt}/${MAX_RECONNECT_ATTEMPTS})...`); 643 | setTimeout(connectDiscordRPC, 15000); 644 | } 645 | } 646 | } 647 | } else if (rpcConnected && rpc) { 648 | try { 649 | await rpc.setActivity({ 650 | details: isEnglish ? 'listening on Soundcloud' : 'écoute sur Soundcloud', 651 | state: isEnglish ? 'Paused' : 'En pause', 652 | largeImageKey: 'idling', 653 | largeImageText: 'by Arizaki', 654 | instance: false 655 | }); 656 | } catch (error) { 657 | if (logEnabled) console.warn('Erreur lors de la mise à jour de Discord RPC (pause)'); 658 | if (error && error.message && error.message.includes('connection closed')) { 659 | rpcConnected = false; 660 | if (rpcReconnectAttempt < MAX_RECONNECT_ATTEMPTS) { 661 | rpcReconnectAttempt++; 662 | if (logEnabled) console.log(`Erreur RPC détectée. Tentative de reconnexion (${rpcReconnectAttempt}/${MAX_RECONNECT_ATTEMPTS})...`); 663 | setTimeout(connectDiscordRPC, 15000); 664 | } 665 | } 666 | } 667 | } 668 | } catch (error) { 669 | if (logEnabled) console.error('Erreur dans updateDiscordRPC'); 670 | } 671 | } 672 | 673 | async function createWindow() { 674 | // Détection automatique de la langue au démarrage 675 | currentLanguage = detectSystemLanguage(); 676 | const texts = locales[currentLanguage]; 677 | 678 | mainWindow = new BrowserWindow({ 679 | width: 1280, 680 | height: 720, 681 | icon: path.join(__dirname, 'soundcloud.ico'), 682 | webPreferences: { 683 | nodeIntegration: true, 684 | contextIsolation: false, 685 | enableRemoteModule: true, 686 | backgroundThrottling: true, 687 | offscreen: false 688 | } 689 | }); 690 | 691 | mainWindow.maximize(); 692 | mainWindow.loadURL('https://soundcloud.com/'); 693 | 694 | mainWindow.webContents.session.webRequest.onBeforeRequest({ urls: ["*://*/*"] }, (details, callback) => { 695 | const adPatterns = [ 696 | /.*ads.*/, 697 | /.*advert.*/, 698 | /.*doubleclick.net.*/, 699 | /.*googlesyndication.com.*/, 700 | /.*adservice.google.*/, 701 | /.*pagead.*/, 702 | /.*ad-delivery.*/, 703 | /.*exponential.com.*/, 704 | /.*amazon-adsystem.com.*/, 705 | /.*adnxs.com.*/, 706 | /.*taboola.com.*/, 707 | /.*outbrain.com.*/, 708 | /.*sponsor.*/, 709 | /.*adserver.*/, 710 | /.*banner.*/, 711 | /.*banners.*/, 712 | /.*promotions.*/ 713 | ]; 714 | 715 | const url = details.url.toLowerCase(); 716 | const shouldBlock = adPatterns.some(pattern => pattern.test(url)); 717 | 718 | callback({ cancel: shouldBlock }); 719 | }); 720 | 721 | mainWindow.webContents.on('did-finish-load', async () => { 722 | mainWindow.setTitle(texts.app_title); 723 | 724 | await injectCustomFeatures(); 725 | 726 | setInterval(updateDiscordRPC, rpcUpdateInterval); 727 | }); 728 | 729 | mainWindow.webContents.on('did-navigate', () => { 730 | if (global.gc) global.gc(); 731 | }); 732 | 733 | mainWindow.on('closed', function () { 734 | if (checkForRPC) clearInterval(checkForRPC); 735 | mainWindow = null; 736 | }); 737 | 738 | createMenu(); 739 | } 740 | 741 | function createMenu() { 742 | const texts = locales[currentLanguage]; 743 | 744 | const SoundCloud = [ 745 | { 746 | label: 'SoundCloud v1.3', 747 | submenu: [ 748 | { 749 | label: texts.menu.home, 750 | click: () => { 751 | mainWindow.loadURL('https://soundcloud.com/'); 752 | } 753 | }, 754 | { 755 | label: texts.menu.sleep_timer, 756 | click: () => { 757 | showSleepTimer(); 758 | } 759 | }, 760 | { 761 | label: texts.menu.official_site, 762 | click: () => { 763 | shell.openExternal('https://soundcloud-crack.web.app/'); 764 | } 765 | }, 766 | { 767 | label: texts.menu.github, 768 | click: () => { 769 | shell.openExternal('https://github.com/ArizakiDev/SoundCloud-cracked'); 770 | } 771 | }, 772 | { 773 | label: texts.menu.about, 774 | click: () => { 775 | dialog.showMessageBox(mainWindow, { 776 | type: 'info', 777 | title: texts.about_dialog.title, 778 | message: texts.about_dialog.message, 779 | detail: texts.about_dialog.detail, 780 | buttons: ['OK'] 781 | }); 782 | } 783 | }, 784 | { 785 | label: texts.menu.language, 786 | submenu: [ 787 | { 788 | label: texts.menu.language_auto, 789 | type: 'radio', 790 | checked: currentLanguage === detectSystemLanguage(), 791 | click: () => { 792 | changeLanguage('auto'); 793 | } 794 | }, 795 | { 796 | label: texts.menu.language_fr, 797 | type: 'radio', 798 | checked: currentLanguage === 'fr', 799 | click: () => { 800 | changeLanguage('fr'); 801 | } 802 | }, 803 | { 804 | label: texts.menu.language_en, 805 | type: 'radio', 806 | checked: currentLanguage === 'en', 807 | click: () => { 808 | changeLanguage('en'); 809 | } 810 | } 811 | ] 812 | }, 813 | { 814 | label: texts.menu.refresh, 815 | role: 'reload' 816 | }, 817 | { 818 | label: texts.menu.quit, 819 | click: () => { 820 | app.quit(); 821 | } 822 | } 823 | ] 824 | }, 825 | { 826 | label: texts.menu.devtools, 827 | submenu: [ 828 | { label: texts.menu.devtools, role: 'toggleDevTools' } 829 | ] 830 | } 831 | ]; 832 | 833 | const menu = Menu.buildFromTemplate(SoundCloud); 834 | Menu.setApplicationMenu(menu); 835 | } 836 | 837 | ipcMain.on('track-update', (event, trackInfo) => { 838 | if (trackInfo && trackInfo.title) { 839 | if (!currentTrack || currentTrack.title !== trackInfo.title || currentTrack.artist !== trackInfo.artist) { 840 | currentTrack = trackInfo; 841 | 842 | lastRpcUpdate = 0; 843 | updateDiscordRPC(); 844 | } 845 | } 846 | }); 847 | 848 | ipcMain.on('show-sleep-timer', () => { 849 | showSleepTimer(); 850 | }); 851 | 852 | if (require('electron-squirrel-startup')) app.quit(); 853 | 854 | app.on('ready', () => { 855 | createWindow(); 856 | setTimeout(connectDiscordRPC, 3000); 857 | }); 858 | 859 | app.on('window-all-closed', function () { 860 | if (rpc) { 861 | try { 862 | artworkCache.clear(); 863 | rpc.destroy(); 864 | rpc = null; 865 | } catch (e) { 866 | console.log('Error destroying RPC on app close:', e); 867 | } 868 | } 869 | 870 | if (process.platform !== 'darwin') { 871 | app.quit(); 872 | } 873 | }); 874 | 875 | app.on('activate', function () { 876 | if (mainWindow === null) { 877 | createWindow(); 878 | } 879 | 880 | if (!rpcConnected) { 881 | connectDiscordRPC(); 882 | } 883 | }); 884 | 885 | if (process.env.NODE_ENV === 'development') { 886 | console.log('Running in development mode'); 887 | } else { 888 | console.log('Running in production mode'); 889 | } 890 | 891 | module.exports = { 892 | appIcon: iconPath, 893 | buildOptions: { 894 | appId: 'com.arizaki.soundcloud', 895 | productName: 'SoundCloud v1.3', 896 | copyright: 'Copyright © 2025 Arizaki', 897 | win: { 898 | icon: iconPath, 899 | target: ['nsis'], 900 | artifactName: 'SoundCloud-v1.3-setup-${version}.${ext}' 901 | }, 902 | nsis: { 903 | oneClick: false, 904 | allowToChangeInstallationDirectory: true, 905 | installerIcon: iconPath, 906 | uninstallerIcon: iconPath, 907 | installerHeaderIcon: iconPath, 908 | createDesktopShortcut: true, 909 | createStartMenuShortcut: true 910 | }, 911 | extraMetadata: { 912 | author: { 913 | name: "Arizaki", 914 | email: "support@nexara-hosting.xyz" 915 | }, 916 | contributors: [ 917 | { 918 | name: "Arizaki", 919 | role: "dev & optimisations" 920 | } 921 | ], 922 | description: "Application SoundCloud optimisée avec fonctionnalités Discord RPC et blocage de publicités.", 923 | supportedLanguages: ["fr", "en"] 924 | }, 925 | extraResources: [ 926 | { 927 | from: "locales", 928 | to: "locales" 929 | } 930 | ] 931 | } 932 | }; 933 | --------------------------------------------------------------------------------