Code Monkey home page Code Monkey logo

slsk-client's Introduction

Soulseek NodeJS client

Build Status JavaScript Style Guide GitHub stars

Before starting

You must already have a Soulseek account before using this module.

Implemented

  • File search
  • File download

Not implemented

This stuff is not implemented (yet?), but I wait your PR!

  • Chat
  • Sharing (+ Upnp opened port)

โš  Infos

You must choose file with slots: true, or you'll wait a long time before downloading it.

I advise you to sort files by speed and select the best one (OK, speed is sent by client and can be fake, but the big majority is real).

Getting started

const slsk = require('slsk-client')
slsk.connect({
  user: 'username',
  pass: 'password'
}, (err, client) => {
  client.search({
    req: 'random',
    timeout: 2000
  }, (err, res) => {
    if (err) return console.log(err)
    res = [
      {
        user: 'poulet',
        file: '@@poulet-files/random.mp3',
        size: 6437362,
        slots: true,
        bitrate: 320,
        speed: 1251293
      }
    ]
    client.download({
      file: res[0],
      path: __dirname + '/random.mp3'
    }, (err, data) => {
      //can res.send(data.buffer) if you use express
    })
  })
})

API

slsk

connect

argument
key required value default note
user true Your username
pass true Your password
host choose a different host for Slsk server server.slsknet.org
port choose a different port 2242
incomingPort Port used for incoming connection 2234
sharedFolders Folders to be shared []
callback

Return client (see just here โฌ‡)

client

search

argument
key required value default note
req true Sent to slsk server/peers to search file, use space to add keyword
timeout Slsk doesn't sent when search is finished. We ignore request after this time 4000
callback
key value note
user Peer name of slsk
file Full path of peer file
size Size of file
slots Available slots true if peer have enough slots to get file immediately
bitrate Bitrate of current file Can be undefined if not sent by client
speed Speed of peer Provided by peer, don't know what is it exactly

List of files

[
  {
    "user": "jambon",
    "file": "@@jambon-slsk/myfile.m4a",
    "slots": true,
    "speed": 32
  }
]
events

You can handle results with events

client.on('found') // any search result
client.on('found:${req}') // or only a specific request

download

Return buffered file, callback called when file is completely downloaded. (Stored in RAM)

argument
key required value default note
file true File sent when searched
path Complete path where file will be stored (if you want read it later) /tmp/slsk/{{originalName}}
callback
key value
buffer Complete buffer of file

downloadStream

WARNING: please report any issue with this function Return streamed file, wait for parts to be downloaded, can be used for HTTP 206 (partial content) for example

argument
key required value default note
file true File sent when searched
callback

Readable stream

Tests

Use env variables for tests

  • DEBUG=slsk:* to display debug messages
  • SLSK_USER=MyUsername
  • SLSK_PASS=MyPassword

Soulseek Protocol Documentation

ftp://ftp.tu-clausthal.de/pub/mirror/ftp.gwdg.de/gnu/ftp/savannah/files/mldonkey/docs/Soulseek/soulseek_protocol.html

https://nicotine-plus.org/doc/SLSKPROTOCOL.html

https://github.com/nicotine-plus/nicotine-plus

slsk-client's People

Contributors

dependabot[bot] avatar f-hj avatar ndr-brt avatar slook avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

slsk-client's Issues

TypeError: down.cb is not a function

/.../slsk-client/lib/index.js:395
            down.cb(new Error('Peer error'))
                 ^

TypeError: down.cb is not a function
    at msgs.forEach.msg (/.../slsk-client/lib/index.js:395:18)
    at Array.forEach (<anonymous>)
    at Messages.forEach (/.../slsk-client/lib/messages.js:41:19)
    at Socket.conn.on.data (/.../slsk-client/lib/index.js:270:10)
    at Socket.emit (events.js:180:13)
    at addChunk (_stream_readable.js:274:12)
    at readableAddChunk (_stream_readable.js:261:11)
    at Socket.Readable.push (_stream_readable.js:218:10)
    at TCP.onread (net.js:581:20)

Tests with no need of a real soulseek connection

I think that tests should be faster, because now, with my poor italian dsl, they take over a minute to run, and it becomes hard to refactoring.

So, I think we should have a suite of mocked server/peers tests that will run also when we run them on a firewalled network (like at work), and maybe keep just few soulseek-integration scenarios.

I'm working on a PR to let you see what I mean

Unhandled 'error' event

Hi, I'm trying your client, thanks for that!

I'm facing an annoying issue when the server refuses connection (due to closed port issues for example), the node process exit with this error:

events.js:183
      throw er; // Unhandled 'error' event
      ^

Error: connect ECONNREFUSED <ip_address>:2242
    at Object._errnoException (util.js:1022:11)
    at _exceptionWithHostPort (util.js:1044:20)
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1198:14)

would be better to handle that error?

How to implement "download folder"?

I'm wondering how "download folder" works?

I understand i can download a file but how to ask a user for the list of files in a given folder?

If it's not implemented yet is there any tips / clue on how to implement it?

thank you

use for hex function and stack variable

Hi, still looking at the code, I wonder what the hex function is for (since it's definition is an empty function.
Could be removed?

Also, another question about the stack object, is an object to keep session data, do you think it can be used also to keep currentLogin and rest variables?

Any idea when downloadStream() is going to be completed?

I think downloadStream() is the only major function missing from this package at the moment. I'm eager to build an app, but without a streaming option it's hard to build a user friendly experience.

So, any idea when this will be implemented?

Bitrate in results

Hello,
First of all, this is nice to see a client for the glorious Soulseek, thanks a lot for your work!

I was wondering if there was any way to fetch the bitrate of the mp3 files in the results, as in the official client. It would be useful to filter the results for example.

Thanks a lot

standardjs eslint

Hi, you talked about standardjs and I saw some eslint comments (into test specs).
Could be useful to include eslint into the package.json and the test process (no standard js syntax, no green bar! :) )?

downloadPeerFile() needs to handle error events

It seems downloadStream() cannot be handled properly in case of a connection error, because the function in download-peer-file.js doesn't listen for 'error' socket events.

Maybe it should listen for an error event, and then emit it on the stream returned by the downloadStream() function. Or is there another solution for this problem?

client.download "User not exist"

hi,

slsk.connect({ user: "username", pass: "password" }, (err, client) => {
    if (err) return console.log(err)
    let rawdata = fs.readFileSync("./music.json");
    let music = JSON.parse(rawdata.toString())
    console.log(music)
    if (music.length > 0) {
        console.log(music[0])
        client.download({
            file: music[0],
            path: `${__dirname}/${process.argv.slice(2).join(' - ')}`
        }, (err, data) => {
            if (err) return console.log(err)
            mm.parseFile(`${__dirname}/${process.arv.slice(2).join(' - ')}`)
            .then( metadata => {
                console.log(util.inspect(metadata, {showHidden: false, depth: null}));
            })
            .catch((err) => {
                console.error(err.message);
            });
        }) 
    }
})

i have a script search.js in nodejs that search for music and a script download.js that download music and a script python for user input and i call with subprocess search.js or download.js. all works fine except when i want to download a music i get 'user not exist' i search in your package and apparently peers == {} i don't understand the meaning so i open an issue hope u will guide me ...

Questions about the project and its future

Hi,

First of all: really nice project. I've gone through almost all of the soulseek related open source projects and this seems to have the biggest potential. I'm somewhat dissatisfied with the original (soulseekqt) client and am looking into creating an alternative tailored to my (and I think many other user's) needs. I'm not exactly sure but maybe an Electron or browser based GUI client or maybe a CLI. All of which would use your node js client as a "backend". So do you plan on maintaining this project in the future or should I just fork it instead. Are you interested in contributing to my project? Do you have detailed plans for the future so maybe I could contribute more easily?

Sorry if my message is a bit "all-over-the-place". I hope I was clear enough and thanks again for the awesome project.

Estimate download progress / queue position?

Hello,

I would like to understand how to estimate the progress of the download when downloading a file?

Also it would be interesting to know how to check if i'm currently in the QUEUE for downloading or if the download has already started?

Thanks a lot!

cc @hems

Here's how you can use this with async / await

Hi,
great project! Just leaving this here in case someone else wants to rewrite the example code with async / await.

const { promisify } = require('util');

const slsk = require('slsk-client');
const connect = promisify(slsk.connect);

const main = async () => {
  try {
    const client = await connect({
      user: 'your_username',
      pass: 'your_password'
    });
    const search = promisify(client.search);
    const download = promisify(client.download);
    console.log("searching...");
    const searchResults = await search.call(client, ({ req: 'random', timeout: 2000 }));
    console.log("search done!")
    console.log("downloading: ", searchResults[0]);
    await download({ file: searchResults[0], path: __dirname + '/random.mp3'});
    console.log("download done!");
  } catch (err) {
    console.log(err);
  }
}

main();

searchStream

Instead of waiting for a timeout, search results could appear in a stream / event bus.

Is this something that would be possible? I would love to put in the work, but I have no idea where to start, if someone can give me pointers and advise if this is possible or not, that would be very helpful.

Thanks

Search results as events

At the moment, you have to set a timeout to end the search query. But would it be possible to return the results as events? So, every time a file is found, an event is fired.

development branch

Hi, just a question.
It's correct to open PR on development branch, but then will be you to integrate development into master?

Can you define that workflow and explicit it in the documentation?

search returns zero results

hello dear people,
as the name suggests, i try to run a simple script to search for a song ('like a virgin' for example) and get zero results for my search.
i have a username and password.

here's the script:

const slsk = require('slsk-client');

const USERNAME = '**********'; 
const PASSWORD = '**********'; 

slsk.connect({
  user: USERNAME,
  pass: PASSWORD
}, (err, client) => {
  if (err) {
    console.error("Error connecting to Soulseek:", err);
    return;
  }
  
  const searchTerm = 'like a virgin';
  client.search({
    req: searchTerm,
    timeout: 2000
  }, (err, res) => {
    if (err) {
      console.error(`Error searching for "${searchTerm}":`, err);
      return;
    }
    if (res && res.length) {
      client.download({
        file: res[0],
        path: `./downloads/${searchTerm}.mp3`
      }, (err) => {
        if (err) {
          console.error(`Error downloading "${searchTerm}":`, err);
        } else {
          console.log(`Successfully downloaded "${searchTerm}"`);
        }
      });
    } else {
      console.log(`No results found for "${searchTerm}"`);
    }
  });
});

i get the output no results found and then the program simply hangs until i ctrl+c

I have access to my router's settings and can configure port forwarding, but I'm not entirely sure how to set it up specifically for this module. Could you provide some guidance on:

Does the slsk-client use a specific port for incoming connections that I should forward?
If so, which port is it, and should I set it for TCP, UDP, or both?

If the port is dynamic or if there's any other configuration I should be aware of to optimize search results, could you please guide me on that as well?

Do you think the issue can arise from something else but port forwarding?

I'd really appreciate any help or insights you can provide. I'm excited about using the module but am currently stuck due to this issue.

Thank you!

Sign up

Since in Soulseek you don't really need to confirm an account etc.

Would it be possible to programmatically sign up?

Can't disconnect gracefully

const slsk = require('slsk-client')

slsk.connect({
  user: 'user',
  pass: 'pass'
}, (err, client) => {
  client.search({
    req: 'random',
    timeout: 2000
  }, (err, res) => {
    if (err) return console.log(err)
    console.log(res)

    /* *** Trying to exit *** */
    client.destroy()
    slsk.disconnect()

  })
})

I cant exit the script without Ctrl-C. Am I doing something wrong here?

Callback must be a function

Got this error on a project that use slsk-client:

TypeError [ERR_INVALID_CALLBACK]: Callback must be a function
at maybeCallback (fs.js:129:9)
at Object.writeFile (fs.js:1156:14)
at Socket.conn.on (./node_modules/slsk-client/lib/download-peer-file.js:83:10)
at Socket.emit (events.js:182:13)
at TCP._handle.close (net.js:606:12)

I think it's just because the function writeFile ar row 83 (https://github.com/f-hj/slsk-client/blob/master/lib/download-peer-file.js#L83) is called without a callback

ERR_INDEX_OUT_OF_RANGE

Getting the error below sometimes when searching:

buffer.js:1015
    throw new errors.RangeError('ERR_INDEX_OUT_OF_RANGE');
    ^

RangeError [ERR_INDEX_OUT_OF_RANGE]: Index out of range
    at checkOffset (buffer.js:1015:11)
    at Buffer.readUInt32LE (buffer.js:1079:5)
    at Message.read32 (/.../slsk-client/lib/message.js:72:25)
    at Message.int32 (/.../slsk-client/lib/message.js:17:50)
    at msgs.forEach.msg (/.../slsk-client/lib/index.js:272:22)
    at Array.forEach (<anonymous>)
    at Messages.forEach (/.../slsk-client/lib/messages.js:41:19)
    at Socket.conn.on.data (/.../slsk-client/lib/index.js:270:10)
    at Socket.emit (events.js:180:13)
    at addChunk (_stream_readable.js:274:12)
    at readableAddChunk (_stream_readable.js:261:11)
    at Socket.Readable.push (_stream_readable.js:218:10)
    at TCP.onread (net.js:581:20)

Over time increasing CPU and RAM usage

Using slsk-client over time creates huge increase in CPU and RAM usage ๐Ÿ”ฅ. It got so bad that leaving my application running over night which is using this module had 100% CPU usage on one core and it would not come down. Thank god it was only using one core ๐Ÿ˜…

Took my time to debug this and found that peerSearchRequests array was collecting a lot of information. There seemed to be no other use for this array collection other then debugging (please correct me here). As a test i removed that part and issues CPU and RAM increase dropped drastically.

Here's a comparison where I'm only monitoring resource usage of a small application using slsk-client:
after

I'm creating a pull request in a moment and linking this issue with it.

In case this does not get merged anytime soon, feel free to use my fork (master branch) https://github.com/sortofdev/slsk-client which has a fix for this applied.

thank you!

thank you @f-hj and contributors! this module was very helpful in the creation of livelook, my "feature-complete" soulseek client module. i added the port forwarding, sharing and chatroom features missing from this one. it doesn't have unit testing yet so i can't recommend you use it, but i plan on adding them during the next month unless i could get some help. if not that's ok too!

Search result attributes

Is there any way to get attributes like bit depth and track duration like you see in the official Soulseek client? Since bitrate doesn't apply to flac and wav files this extra information would be helpful.

DeprecationWarning: Calling an asynchronous function without callback is deprecated.

Hi,

Been working with this library for a few weeks and it works great. Sadly there is a weird problem with the code. When running I get this exception on certain files. It eventually results into a "peer error".

(node:5580) [DEP0013] DeprecationWarning: Calling an asynchronous function without callback is deprecated.
UNCAUGHT EXCEPTION - keeping process alive: { Error: This socket has been ended by the other party
at Socket.writeAfterFIN [as write] (net.js:364:12)
at Timeout.setTimeout [as _onTimeout] (\slsk-client\lib\download-peer-file.js:25:14)
at ontimeout (timers.js:498:11)
at tryOnTimeout (timers.js:323:5)
at Timer.listOnTimeout (timers.js:290:5) code: 'EPIPE' }

I use this on a Windows machine with node version v8.11.4

An example item which fails

{ "user": "USER",
  "file": "l:\\music one\\absolute music - 320\\FILE.mp3",
  "size": 9051058,
  "slots": true,
  "bitrate": 320,
  "speed": 72159 }

mocha --recursive does not work with cov8

Hi, after the introduction of cov8 the npm test command does not run the tests into test/integration folder.

it seems like the --recursive parameter went ignored now.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.