I'm completely new to Javascript and NodeJs and functional programming in general. The code below scrapes a torrent-website containing movies, gets info about the movie from the OMDb API and lets a user choose a movie to watch by streaming the torrent. Any feedback appreciated.var scraperhelper = require('./scraperhelper.js')var cheerio = require('cheerio')var spawn = require('child_process').spawnvar fs = require('fs')var async = require('async')var omdb = require('omdb')var config = require('./config')var FileCookieStore = require('tough-cookie-filestore')var request = require('request')var WebTorrent = require('webtorrent')/*Initialize cookie file which store the login info*/try {  fs.openSync(config.cookiePath, 'r')} catch (e) {  if (e.code === 'ENOENT') {        // File not found, so make one    fs.writeFileSync(config.cookiePath, '', { flags: 'wx' }, function (err) {      if (err) {        console.log(err)        throw err      }    })  } else {    console.log(e)    throw e  }}var j = request.jar(new FileCookieStore(config.cookiePath))request = request.defaults({ jar: j })/*Requires login*/exports.fetch = function (url, callback) {  scrapeTorrents(url, function (err, results) {    if (err) {      console.log(err)      callback(err, null)    } else {      callback(null,            scraperhelper.sortMoviesByImdbRating(      scraperhelper.sortReleasesBySeeders(results)))    }  })}exports.login = function (callback) {  request.post({    uri: config.urls.login,    headers: { 'content-type': 'application/x-www-form-urlencoded' },    body: require('querystring').stringify(config.credentials)  }, function (err, res, body) {    if (err) {      console.log('Login error: ' + err)      callback(err, null)    } else {      console.log('Login successful')      callback(null, body)    }  })}function scrapeTorrents (url, callback) {  request(url, function (err, res, body) {    if (err) {      console.log('Error scraping ' + url + ': ' + err)      callback(err, null)      return    }    var $ = cheerio.load(body)    var results = []    var asyncTasks = []    $('span.title').each(function (i, element) {      var name = $(this).text()      var detailsUrl = $(this).children().eq(0).attr('href')      var torrentUrl = $(this).parent().next().children().eq(0).attr('href')      var size = $(this).parent().next().next().next().text()      var seeders = $(this).parent().next().next().next().next().next().text()      var leechers = $(this).parent().next().next().next().next().next().next().text()      var info = scraperhelper.extractInfoFromName(name)      var searchTerm = {terms: info.title, year: info.year, specialChars: true}      asyncTasks.push(function (callback) {        getOmdbInfo(searchTerm, function (err, res) {          var omdbInfo          var isCollection // Movies not found in OMDb gets collected into one item          if (err) {            isCollection = true            info.rlsDetails = name            omdbInfo = {title: 'Not found in IMDb', year: '', runtime: '', actors: '', plot: '', imdb: {id: '', rating: 10, votes: 0}, poster: '../static/folder2.jpg'}          } else {            omdbInfo = res            isCollection = false            omdbInfo.genres = omdbInfo.genres.map(function (s) { return ' ' + s })            omdbInfo.genres[0] = omdbInfo.genres[0].substring(1)          }          var movieInfo = {            isCollection: isCollection,            title: omdbInfo.title,            year: omdbInfo.year,            release: [{              rlsDetails: info.rlsDetails,              detailsUrl: detailsUrl,              torrentUrl: torrentUrl,              size: size,              seeders: seeders,              leechers: leechers            }],            runtime: omdbInfo.runtime,            genres: omdbInfo.genres,            actors: omdbInfo.actors,            plot: omdbInfo.plot,            imdbId: omdbInfo.imdb.id,            imdbRating: omdbInfo.imdb.rating,            imdbVotes: omdbInfo.imdb.votes,            imgUrl: omdbInfo.poster          }          var added = false          for (var j = 0; j < results.length; j++) {            if (results[j].imdbId === movieInfo.imdbId) {              results[j].release.push(movieInfo.release[0])              added = true              break            }          }          if (!added) {            results.push(movieInfo)          }          callback()        })      })    })    async.parallel(asyncTasks, function () {      callback(null, results)    })  })}function getOmdbInfo (show, callback) {  show.type = 'movie'  omdb.search(show, function (err, movies) {    if (err) {      callback(err, null)    }    if (movies.length === 0) {      if (show.specialChars === true) {        // Replace special charactes with whitespace and retry OMDb search        show.terms = show.terms.replace(/[^a-z0-9\s]/gi, '')        show.terms = show.terms.replace(/\s+/g, ' ')        show.specialChars = false        getOmdbInfo(show, function (err, res) {          callback(err, res)        })      } else {        console.log('No OMDb search results for: ' + show.terms + ' ' + show.year)        callback('No search results for: ' + show.terms + ' ' + show.year, null)      }    } else {      omdb.get(movies[0].imdb, true, function (err, movie) {        if (err) {          callback(err, null)        }        if (!movie) {          console.log('No OMDb get result for: ' + show.terms + ' ' + show.year + ' ' + movies[0].imdb)          callback('Movie not found!', null)        } else {          callback(null, movie)        }      })    }  })}exports.play = function (torrentUrl) {  downloadTorrent(torrentUrl, function (err, path) {    if (err) {      console.log(err)    } else {      launchWebtorrent(path)    }  })}function downloadTorrent (url, callback) {  var dir = config.torrentDir  if (!fs.existsSync(dir)) {    fs.mkdirSync(dir)  }  var filename = url.split('/')  var path = dir + filename[filename.length - 1]  var uri = 'http://example.org' + url  request({uri: uri})    .on('error', function (err) {      console.log('Download torrent err: ' + err + ' for ' + url)      callback(err, null)      return    })        .pipe(fs.createWriteStream(path))    .on('close', function () {      callback(null, path)    }  )}function launchWebtorrent (torrentFilePath) {  var client = new WebTorrent()  client.add(torrentFilePath, function ontorrent (torrent) {    var server = torrent.createServer()    server.listen(config.port)    var biggestIdx = 0    for (var i = 1 ; i < torrent.files.length; i++) {      if (torrent.files[i].length > torrent.files[biggestIdx].length) {        biggestIdx = i      }    }    var url = 'http://localhost:' + config.port + '/' + biggestIdx    console.log(torrent.files[biggestIdx].name + ' available at ' + url)    launchVideoPlayer(url)        /*    @TODO: need to close and destroy when finished wathing movie        server.close()        client.destroy()        */        /*    @TODO feature: show download progress        torrent.on('download', function(chunkSize){          console.log('chunk size: ' + chunkSize);          console.log('total downloaded: ' + torrent.downloaded);          console.log('download speed: ' + torrent.downloadSpeed());          console.log('progress: ' + torrent.progress);          console.log('======');          })        */  })}function launchVideoPlayer (url) {  /*  @TODO feature: remember playing position and resume there  /start ms     Start playing at ms (= milliseconds)  /startpos hh:mm:ss    Start playing at position hh:mm:ss  */  var child = spawn(config.players.mpchc, [url, '/play'])  child.on('error', function (err) {    console.log('Failed to start child process: ' + err)  })}function launchPeerflix (torrentFilePath) {  var args = '--mpchc'  var child = spawn('cmd', ['/c', 'peerflix', torrentFilePath, args])  child.on('error', function (err) {    console.log('Failed to start child process: ' + err)  })}scraperhelper.js:var ptn = require('parse-torrent-name')/*Extract title, year and the details of the release from a string.Example: The Man From U.N.C.L.E 2015 1080p BDRip-Haxx0r -->{Title: The Man From U.N.C.L.E, Year: 2015, rlsDetails: 1080p BDRip-Haxx0r }*/exports.extractInfoFromName = function (name) {  var parsed = ptn(name)  var rlsDetails = name.replace(parsed.title, '')  rlsDetails = rlsDetails.replace(parsed.year, '')  if (typeof parsed.year === 'undefined') {    parsed.year = ''  }  return ({    title: parsed.title,    year: parsed.year.toString(),    rlsDetails: rlsDetails  })}/*Sort the releases of each movie by the number of seeders in descendingorder.*/exports.sortReleasesBySeeders = function (movies) {  for (var i = 0 ; i < movies.length; i++) {    movies[i].release = movies[i].release.sort(function (b, a) {      return parseFloat(a.seeders) - parseFloat(b.seeders)    })  }  return movies}/*Sort the movies the IMDb rating in descendingorder.*/exports.sortMoviesByImdbRating = function (movies) {  return movies.sort(function (b, a) {    return parseFloat(a.imdbRating) - parseFloat(b.imdbRating)  })}
Movie torrent-site web scraper with IMDb info and streaming
javascript;beginner;node.js;web scraping