TooTallNate / node-spotify-web

Node.js implementation of the Spotify Web protocol
MIT License
697 stars 127 forks source link

Stopping track streams #38

Open basvdheijden opened 11 years ago

basvdheijden commented 11 years ago

How do I properly close a playing stream returned from Track.prototype.play?

I'm building a cli-based mp3 player and wanted to start a new song. therefore I have to close the currently playing song. Tried track.end(); but then it takes a lot of time before ending the stream sequence. Is this due to the nature of the PassThrough stream implementation?

mpg123 = child_process.spawn('mpg123', ['-']); track.play().pipe(mpg123.stdin);

setTimeout(function(){ track.end(); }, 5000); // Track ends after approx 15 secs.

basvdheijden commented 11 years ago

....And once I have 3 tracks open, I get the Rate limited error. this probably indicated that I did not properly 'close' the track?

I assume I have to close the source of the PassThrough stream?

adammw commented 11 years ago

For starters, you won't be able to do track.end() unless you have done track = track.play() (ie. you can end the playing stream but not the track object itself). I assume that's just a typo.

From what I can see is going on when I try to recreate this, calling .end() is asking the stream to stop (which takes a few seconds for the request and etc. to close), and mpg123 continues until it has flushed its buffer. The stream may actually be downloading faster than what you are playing at, so when you call end, mpg123 will continue playing until it has finished getting all the (buffered) data.

The sequence of events I get is something like:

calling stream.end() +0ms
stream finish +2s
stream end +1s
pipe finish +3s

To stop the music playing straight-away, it might be easier to use child_process to send a signal to the mpg123 application. Note however you will need to disconnect the streams first otherwise you will get exceptions trying to write to closed streams. I'm not too confident with streams nor with my explanation, so you may want to get a second opinion from someone else on the best way to go about it.

basvdheijden commented 11 years ago

Indeed, track.end() was a typo. It was: var stream = track.play(); stream.end();

Now because the stream returned by track.play() is an instance of the PassThrough Stream class, it only passes the data from stdin to stdout directly, without interfering with the stream contents.

My (perceived) problem is, that I can't (or at least, I don't know how to) close the underlying source of the PassThrough stream in a good manner.

I noticed when I call stream.end(), it closes after approx. 10 seconds. but when I open another stream like that, and keep doing that, I get a 'Rate limited' error after about 3 tries. So I think somehow the underlying source is still active?

LinusU commented 11 years ago

This worked for me:

var req = track.play();
var lame = new lame.Decoder();
var speaker = new Speaker();

req.pipe(lame).pipe(speaker);

setTimeout(function () {
  req.unpipe(lame);
  lame.unpipe(speaker);
  speaker.end();
, 5000);

But it seems I still have some Rate limited-problem so I will continue to investigate...

LinusU commented 11 years ago

Have a look at pull request #46

buschtoens commented 11 years ago

I was hoping to use this for my netbook, when I only have very poor mobile internet. I tried caching the next 3 songs, and immediately got rate limitted. Is it because I'm not properly closing streams?

Even when I enforce a 30s pause between two track downloads, I get an error.