riffy-soundcloud 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ramkrishna-js
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # riffy-soundcloud
2
+
3
+ > [!IMPORTANT]
4
+ > This is an **unofficial** plugin and is not officially maintained or created by the [Riffy](https://riffy.js.org/) team.
5
+
6
+ A robust and easy-to-use plugin for [Riffy](https://github.com/riffy-team/riffy) that enables seamless SoundCloud track and playlist resolution using the V2 API. Perfect for Discord music bots looking to expand their audio sources beyond Spotify and YouTube.
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ npm install riffy-soundcloud
12
+ ```
13
+
14
+ ## Usage
15
+
16
+ ```javascript
17
+ const { Riffy } = require("riffy");
18
+ const { SoundCloud } = require("riffy-soundcloud");
19
+
20
+ const riffy = new Riffy(client, nodes, {
21
+ plugins: [
22
+ new SoundCloud({
23
+ clientId: "YOUR_SOUNDCLOUD_CLIENT_ID" // Required
24
+ })
25
+ ]
26
+ });
27
+ ```
28
+
29
+ ## Features
30
+
31
+ - Resolves SoundCloud Tracks
32
+ - Resolves SoundCloud Playlists
33
+ - Supports `soundcloud.com` and `on.soundcloud.com` URLs
34
+
35
+ ## Support
36
+
37
+ For support, you can:
38
+ - Create an [Issue](https://github.com/ramkrishna-js/riffy-soundcloud/issues)
39
+ - DM me on Discord: `ramkrishna0`
40
+
41
+ ## License
42
+
43
+ This project is licensed under the [MIT License](LICENSE).
package/dist/plugin.js ADDED
@@ -0,0 +1,129 @@
1
+ const { fetch } = require("undici");
2
+ const { Plugin, Track } = require("riffy");
3
+
4
+ const REGEX = /^https?:\/\/(?:www\.|m\.|on\.)?soundcloud\.com\/.+/;
5
+
6
+ class SoundCloud extends Plugin {
7
+ constructor(options) {
8
+ super();
9
+ this.baseURL = 'https://api-v2.soundcloud.com';
10
+ this.options = options;
11
+
12
+ if (!this.options?.clientId) {
13
+ throw new Error('SoundCloud Plugin: clientId is required.');
14
+ }
15
+ }
16
+
17
+ async load(riffy) {
18
+ this.riffy = riffy;
19
+ this._resolve = riffy.resolve.bind(riffy);
20
+ riffy.resolve = this.resolve.bind(this);
21
+ }
22
+
23
+ check(query) {
24
+ return REGEX.test(query);
25
+ }
26
+
27
+ async resolve({ query, requester }) {
28
+ let trackLoaded = "",
29
+ playlistLoaded = "",
30
+ loadFailed = "";
31
+
32
+ const node = this.riffy.leastUsedNodes[0];
33
+
34
+ if (node.restVersion === "v4") {
35
+ trackLoaded = "track";
36
+ playlistLoaded = "playlist";
37
+ loadFailed = "error";
38
+ } else {
39
+ trackLoaded = "TRACK_LOADED";
40
+ playlistLoaded = "PLAYLIST_LOADED";
41
+ loadFailed = "LOAD_FAILED";
42
+ }
43
+
44
+ const finalQuery = query.query || query;
45
+ if (!this.check(finalQuery)) return this._resolve({ query, requester });
46
+
47
+ try {
48
+ const data = await this.fetchData(finalQuery);
49
+ if (!data) throw new Error("No data found");
50
+
51
+ if (data.kind === "track") {
52
+ const track = await this.buildUnresolved(data, requester);
53
+ return this.buildResponse(trackLoaded, [track], null, null);
54
+ } else if (data.kind === "playlist") {
55
+ // Filter out tracks that might not be playable or full objects if necessary
56
+ // But usually resolve returns enough info
57
+ const tracks = await Promise.all(data.tracks.map(async t => {
58
+ // Sometimes playlist tracks are incomplete?
59
+ if (!t.title) return null;
60
+ return await this.buildUnresolved(t, requester);
61
+ }));
62
+
63
+ const validTracks = tracks.filter(t => t !== null);
64
+ return this.buildResponse(playlistLoaded, validTracks, data.title, null);
65
+ } else {
66
+ return this.buildResponse(loadFailed, null, null, "Unsupported SoundCloud URL type");
67
+ }
68
+
69
+ } catch (e) {
70
+ return this.buildResponse(loadFailed, null, null, e.message || null);
71
+ }
72
+ }
73
+
74
+ async fetchData(url) {
75
+ // Handle short links by following redirects if necessary, but API resolve usually handles them.
76
+ const res = await fetch(`${this.baseURL}/resolve?url=${encodeURIComponent(url)}&client_id=${this.options.clientId}`);
77
+ if (!res.ok) {
78
+ // Try to read body for error
79
+ throw new Error(`SoundCloud API Error: ${res.status} ${res.statusText}`);
80
+ }
81
+ return await res.json();
82
+ }
83
+
84
+ async buildUnresolved(track, requester) {
85
+ if (!track) throw new ReferenceError('The SoundCloud track object was not provided');
86
+
87
+ const node = this.riffy.leastUsedNodes[0];
88
+
89
+ const artwork = track.artwork_url ? track.artwork_url.replace('-large', '-t500x500') : null;
90
+
91
+ return new Track(
92
+ {
93
+ track: "",
94
+ info: {
95
+ identifier: String(track.id),
96
+ isSeekable: true,
97
+ author: track.user?.username || "Unknown",
98
+ length: track.duration,
99
+ isStream: false,
100
+ sourceName: "soundcloud",
101
+ title: track.title,
102
+ uri: track.permalink_url,
103
+ thumbnail: artwork,
104
+ position: 0,
105
+ },
106
+ },
107
+ requester,
108
+ node
109
+ );
110
+ }
111
+
112
+ async buildResponse(loadType, tracks, name, error) {
113
+ return Object.assign(
114
+ {
115
+ loadType,
116
+ tracks,
117
+ playlistInfo: name ? {
118
+ name
119
+ } : null,
120
+ exception: error ? {
121
+ message: error,
122
+ severity: 'COMMON',
123
+ } : null
124
+ }
125
+ )
126
+ }
127
+ }
128
+
129
+ module.exports = { SoundCloud };
package/index.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export declare class SoundCloud {
2
+ constructor(options: {
3
+ clientId: string;
4
+ });
5
+ }
package/index.js ADDED
@@ -0,0 +1,3 @@
1
+ const { SoundCloud } = require("./dist/plugin");
2
+
3
+ module.exports = { SoundCloud };
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "riffy-soundcloud",
3
+ "description": "A robust SoundCloud plugin for Riffy that enables seamless track and playlist resolution.",
4
+ "version": "1.0.0",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "dependencies": {
10
+ "riffy": "^1.0.3",
11
+ "undici": "^5.0.0"
12
+ },
13
+ "keywords": [
14
+ "riffy",
15
+ "soundcloud",
16
+ "music",
17
+ "bot",
18
+ "discord",
19
+ "plugin",
20
+ "music-bot",
21
+ "lavalink"
22
+ ],
23
+ "author": "ramkrishna-js",
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/ramkrishna-js/riffy-soundcloud.git"
28
+ },
29
+ "bugs": {
30
+ "url": "https://github.com/ramkrishna-js/riffy-soundcloud/issues"
31
+ },
32
+ "homepage": "https://github.com/ramkrishna-js/riffy-soundcloud#readme"
33
+ }
34
+