Я хочу скачивать видео одно за другим в серии.
То есть первый должен быть полностью загружен до запуска второго, второй должен быть полностью загружен до запуска третьего и так далее.
У меня есть следующая структура каталогов -
video-downloader
├── index.js
├── videos.js
├── package.json
{
"name": "video-downloader",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"download": "^7.1.0"
},
"scripts": {
"start": "node index"
}
}
const videos = [
{
url: 'https://video.com/lesson1.mp4',
name: 'Lesson 1',
},
{
url: 'https://video.com/lesson2.mp4',
name: 'Lesson 2',
},
.
.
.
{
url: 'https://video.com/lesson2.mp4',
name: 'Lesson 100',
}
]
const fs = require('fs')
const download = require('download')
const videos = require('./videos')
const OUTPUT_DIR = 'Downloads'
fs.mkdir(OUTPUT_DIR, () => {
main()
})
const main = () => {
videos.map((video, i) => {
console.info(`Downloaded file ${i + 1} of ${videos.length} (${video.name})`)
download(video.url).pipe(
fs.createWriteStream(`${OUTPUT_DIR}/${video.name}.mp4`),
)
})
}
Это загружает видео по частям параллельно. Все видео загружаются одновременно, но ни одно из них не завершается до начала другого.
Как скачать серийно?
Я знаю, что должен использовать что-то вроде http://caolan.github.io/async/, но для этого нужна подпись функции, и у меня есть videos
в виде массива, поэтому я не уверен, как это сделать.
Попробуйте асинхронное ожидание для этого. Сначала загрузите, а затем запишите в Sync.
const fs = require('fs');
const sh = require('shelljs');
const download = require('download');
const videos = require('./videos');
const OUTPUT_DIR = 'Downloads';
sh.mkdir('-p', OUTPUT_DIR);
videos.forEach(async (video, i) => {
console.info(`Downloading ${video.name}. Fil${i + 1}/${videos.length} - `);
const data = await download(video.url);
fs.writeFileSync(`${OUTPUT_DIR}/${video.name}.mp4`, data);
});
Это не работает, вы получите массив из 100 объектов промисов, а загрузка все еще находится в режиме parelel.
map не нужен, forEach будет работать, массив промисов нам не нужен. он будет загружать один контент в каждой итерации цикла. проверить порядок массива видео.
действительно это работает, но загружается в случайном порядке. даже с forEach. 1-е решение с использованием цикла for работает отлично :)
Вы можете использовать .reduce
с обещаниями для последовательного разрешения следующим образом:
const fs = require('fs')
const sh = require('shelljs')
const download = require('download')
const videos = require('./videos')
const OUTPUT_DIR = 'Downloads'
sh.mkdir('-p', OUTPUT_DIR)
videos = videos.reduce((acc, item) => {
return acc.then(() => {
return new Promise((resolve) => {
// Here you are using it as a Duplex Stream, not a promise,
// therefore, you must check when the stream emits the 'end' event
// so you can proceed further
let stream = download(video.url)
.pipe(fs.createWriteStream(`${OUTPUT_DIR}/${video.name}.mp4`));
stream.on('end', () => {
console.info(`stream done ${item}`);
resolve(item);
})
})
});
}, Promise.resolve());
// 'videos' is now a promise
videos.then((lastPromise) => {
// using reduce will return the last evaluated item(promise)
// but reaching the last one means the promises before that have been resolved
console.info('all files were downloaded');
})
я не пробовал это, так как первый сработал для меня, но есть голос и спасибо за ответ :)
Вы можете использовать ключевое слово await
в стандартных циклах for, и все будет обрабатываться по порядку и ждать каждой загрузки, прежде чем продолжить.
const fs = require('fs')
const download = require('download')
const videos = require('./videos')
const util = require('util')
const mkdirAsync = util.promisify(fs.mkdir)
const OUTPUT_DIR = 'Downloads'
const main = async () => {
await mkdirAsync(OUTPUT_DIR)
for (let i = 0; i < videos.length; i++) {
const video = videos[i]
const data = await download(video.url)
fs.writeFileSync(`${OUTPUT_DIR}/${video.name}.mp4`, data)
console.info(`Downloaded file ${i + 1} of ${videos.length} (${video.name})`)
}
}
main()
нп. Я только что видел, как отредактировал твой пост. возможно, вы захотите взглянуть на обещание вашей функции mkdir (и любой другой функции на основе обратного вызова), чтобы вы могли предотвратить спагетти-код. я отредактирую свой пост как таковой
какая разница? Я имею в виду, что теперь он работает отлично.
Функционально ничем не отличается. Я, вероятно, перегнул палку, предложив это, извините. Но, учитывая, что я уже предложил это... Вы используете две разные асинхронные методологии в одном фрагменте кода - обратный вызов и асинхронный/ожидающий. Если вы придерживаетесь одного, это снижает умственную нагрузку на размышления. Пример обещания fs.mkdir сам по себе был немного надуманным, но как только вы добавите больше асинхронных действий, будет намного проще рассуждать о том, является ли это только асинхронным/ожиданием или только обратными вызовами.
о нет, мне просто было интересно. не нужно сожалеть. Я понял. Я также склонен использовать любой из обоих, и мне больше нравится async/await без try-catch, и я склонен писать меньше LOC, поэтому я оставлю свой, пока он не доставит мне проблем. Тогда наверное попробую урс. Спасибо еще раз :)
спасибо, это работает, но загружается в случайном порядке. Я хочу, чтобы это было сделано в указанном порядке :)