Как оставить запрос открытым для использования метода write () после долгого времени - PullRequest
2 голосов
/ 25 июня 2019

Мне нужно держать соединение открытым, поэтому после завершения музыки я пишу новые данные. Проблема в том, что, как и я, поток просто останавливается после первой песни. Как сохранить соединение открытым и воспроизвести следующие песни?

const fs = require('fs');
const express = require('express');
const app = express();
const server = require('http').createServer(app)
const getMP3Duration = require('get-mp3-duration')

let sounds = ['61880.mp3', '62026.mp3', '62041.mp3', '62090.mp3', '62257.mp3', '60763.mp3']

app.get('/current', async (req, res) => {
    let readStream = fs.createReadStream('sounds/61068.mp3')
    let duration = await getMP3Duration(fs.readFileSync('sounds/61068.mp3'))

    let pipe = readStream.pipe(res, {end: false})

    async function put(){
        let file_path = 'sounds/'+sounds[Math.random() * sounds.length-1]

        duration = await getMP3Duration(fs.readFileSync(file_path))

        readStream = fs.createReadStream(file_path)

        readStream.on('data', chunk => {
            console.log(chunk)
            pipe.write(chunk)
        })

        console.log('Current Sound: ', file_path)

        setTimeout(put, duration)
    }

    setTimeout(put, duration)
})

server.listen(3005, async function () {
    console.log('Server is running on port 3005...')
});

Ответы [ 3 ]

0 голосов
/ 03 июля 2019

Экспресс работает, возвращая один ответ на один запрос. Как только запрос был отправлен, необходимо сгенерировать новый запрос для запуска нового ответа.

Однако в вашем случае вы хотите продолжать генерировать новые ответы из одного запроса.

Для решения вашей проблемы можно использовать два подхода:

  1. Измените способ создания своего ответа, чтобы удовлетворить ваш вариант использования.
  2. использовать систему мгновенной связи (websocket). Самое лучшее и простое, что приходит мне в голову, это socket.io

Адаптируемый экспресс

Решение здесь состоит в том, чтобы следовать этой процедуре:

  1. Запрос на конечную точку /current поступает
  2. Подготовлена ​​звуковая последовательность
  3. Возвращается поток всей последовательности

Итак, ваш обработчик будет выглядеть так:

const fs = require('fs');
const express = require('express');
const app = express();
const server = require('http').createServer(app);
// Import the PassThrough class to concatenate the streams
const { PassThrough } = require('stream');
// The array of sounds now contain all the sounds
const sounds = ['61068.mp3','61880.mp3', '62026.mp3', '62041.mp3', '62090.mp3', '62257.mp3', '60763.mp3'];


// function which concatenate an array of streams
const concatStreams = streamArray => {
  let pass = new PassThrough();
  let waiting = streamArray.length;
  streamArray.forEach(soundStream => {
    pass = soundStream.pipe(pass, {end: false});
    soundStream.once('end', () => --waiting === 0 && pass.emit('end'));
  });
  return pass;
};

// function which returns a shuffled array
const shuffle = (array) => {
  const a = [...array]; // shallow copy of the array
  for (let i = a.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [a[i], a[j]] = [a[j], a[i]];
  }
  return a;
};


server.get('/current', (req, res) => {
  // Start by shuffling the array
  const shuffledSounds = shuffle(sounds);

  // Create a readable stream for each sound
  const streams = shuffledSounds.map(sound => fs.createReadStream(`sounds/${sound}`));

  // Concatenate all the streams into a single stream
  const readStream = concatStreams(streams);

  // This will wait until we know the readable stream is actually valid before piping
  readStream.on('open', function () {
    // This just pipes the read stream to the response object (which goes to the client)
    // the response is automatically ended when the stream emits the "end" event
    readStream.pipe(res);
  });
});

Обратите внимание, что функция больше не требует ключевого слова async. Процесс все еще асинхронный, но кодирование основано на излучателе, а не на обещании.

Если вы хотите зациклить звуки, вы можете создать дополнительные шаги перетасовки / преобразования в поток / конкатенацию.

Я не включил альтернативу socketio для простоты.

0 голосов
/ 04 июля 2019

Окончательное решение после нескольких правок :

Я подозреваю, что ваша основная проблема связана с генератором случайных элементов массива. Вам нужно обернуть то, что у вас есть, с помощью Math.floor, чтобы округлить, чтобы получить целое число:

sounds[Math.floor(Math.random() * sounds.length)]

Кроме того, Readstream.pipe возвращает пункт назначения, поэтому то, что вы делаете, имеет смысл. Тем не менее, вы можете получить неожиданные результаты при вызове on('data') на вашем читаемом после того, как вы уже с него по трубопроводу. node.js streams docs упоминает об этом. Я проверил ваш код на моей локальной машине, и это, похоже, не проблема, но, возможно, имеет смысл изменить это, чтобы у вас не было проблем в будущем.

Выберите один стиль API

Интерфейс API для чтения потоков развивался в нескольких версиях Node.js и предоставляет несколько методов использования потоковых данных. Как правило, разработчики должны выбирать один из методов потребления данных и никогда не должны использовать несколько методов для получения данных из одного потока. В частности, использование комбинации итераторов on ('data'), on ('readable'), pipe () или async может привести к неинтуитивному поведению.

Вместо того, чтобы звонить on('data') и res.write, я бы просто снова направил трубку из readStream в res. Кроме того, если вы действительно не хотите получить длительность, я бы вытащил эту библиотеку и просто использовал событие readStream.end, чтобы сделать дополнительные вызовы к put(). Это работает, потому что вы передаете опцию false при конвейере, которая отключает стандартную функциональность события end в потоке записи и оставляет его открытым. Тем не менее, он по-прежнему испускается, поэтому вы можете использовать его в качестве маркера, чтобы узнать, когда читаемый закончил конвейер. Вот переработанный код:

const fs = require('fs');
const express = require('express');
const app = express();
const server = require('http').createServer(app)
//const getMP3Duration = require('get-mp3-duration') no longer needed

let sounds = ['61880.mp3', '62026.mp3', '62041.mp3', '62090.mp3', '62257.mp3', '60763.mp3']

app.get('/current', async (req, res) => {
    let readStream = fs.createReadStream('sounds/61068.mp3')
    let duration = await getMP3Duration(fs.readFileSync('sounds/61068.mp3'))

    let pipe = readStream.pipe(res, {end: false})

    function put(){
        let file_path = 'sounds/'+sounds[Math.floor(Math.random() * sounds.length)]

        readStream = fs.createReadStream(file_path)

        // you may also be able to do readStream.pipe(res, {end: false})
        readStream.pipe(pipe, {end: false})

        console.log('Current Sound: ', file_path)

        readStream.on('end', () => {
            put()
        });
    }

    readStream.on('end', () => {
        put()
    });
})

server.listen(3005, async function () {
    console.log('Server is running on port 3005...')
});
0 голосов
/ 25 июня 2019

Вы должны использовать библиотеку или посмотреть на исходный код и посмотреть, что они делают.Хорошим является: https://github.com/obastemur/mediaserver

СОВЕТ:
Всегда начинайте свое исследование с изучения других проектов .. (Когда это возможно или когда вы не изобретаете колесо;)) вы не первый, ктосделать это или решить эту проблему:)

быстрый поиск по фразе "nodejs stream mp3 github" дал мне несколько указаний .. Удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...