├── 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 |

6 |
7 | [](https://github.com/ArizakiDev/SoundCloud-cracked/releases)
8 | [](LICENSE)
9 | [](https://github.com/ArizakiDev)
10 | [](https://github.com/ArizakiDev/SoundCloud-cracked/releases)
11 | [](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 | | Version |
54 | État |
55 | Modifications |
56 |
57 |
58 | | 1.3 |
59 | ✅ |
60 |
61 | • Ajout: Minuterie
62 | • Amélioration: Discord rpc
63 | • D'autres ajouts..
64 | |
65 |
66 |
67 | | 1.2.1 |
68 | ⚠️ |
69 |
70 | • Ajout: Version Android
71 | • à corriger: Page de connexion
72 | |
73 |
74 |
75 | | 1.2 |
76 | ✅ |
77 |
78 | • Fix: Retrait des pubs
79 | • Fix: RPC Discord
80 | • Amélioration des performances
81 | |
82 |
83 |
84 | | 1.1 |
85 | 📦 |
86 | • Ajout: Suppression des publicités |
87 |
88 |
89 | | 1.0 |
90 | 🎉 |
91 | • Version initiale |
92 |
93 |
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 | | Version |
144 | Status |
145 | Changes |
146 |
147 |
148 | | 1.3 |
149 | ✅ |
150 |
151 | • add: sleep timer
152 | • upgrade: discord rpc
153 | • More add
154 | |
155 |
156 |
157 | | 1.2.1 |
158 | ⚠️ |
159 |
160 | • add: Android Version
161 | |
162 |
163 |
164 | | 1.2 |
165 | ✅ |
166 |
167 | • Fix: Ads removal
168 | • Fix: Discord RPC
169 | • Performance improvements
170 | |
171 |
172 |
173 | | 1.1 |
174 | 📦 |
175 | • Added: Ads removal |
176 |
177 |
178 | | 1.0 |
179 | 🎉 |
180 | • Initial release |
181 |
182 |
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 | [](https://github.com/ArizakiDev/SoundCloud-cracked/stargazers)
205 | [](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 |
--------------------------------------------------------------------------------