I have a nodejs route where I am trying to download a url as mp3 using npm-youtube-dl. I have a download directory that I watch with chokidar for files being added and when a file is added I save the link to the file and after the download finishes I call a function that's supposed to respond with the download URL using res.download. When the sendURL function is called the url that I can clearly see has been saved before is undefined when I console.log it... Any idea what i'm doing wrong here/how I can fix this? i'm guessing it's a js variable scope issue?
my code:
var express = require('express');
var router = express.Router();
var yt = require('youtube-dl');
var fs = require('fs');
var path = require('path');
var chokidar = require('chokidar');
var downloadPath = '';
var watcher = chokidar.watch('./downloads', {
ignored: '/^[^.]+$|\.(?!(part)$)([^.]+$)/',
persistent: true
});
watcher.on('add', function(path) {
console.log('added: ', path);
this.downloadPath = path;
console.log('saved', this.downloadPath);
});
/*
router.get('/', function(req, res, next) {
next();
});
*/
router.get('/', function(req, res) {
var url = 'https://soundcloud.com/astral-flowers-music/bella-flor';
var options = ['-x', '--audio-format', 'mp3', '-o', './downloads/%(title)s.%(ext)s'];
var video = yt.exec(url, options, {}, function exec(err, output) {
if (err) { throw err; }
console.log(output.join('\n'));
sendUrl();
});
function sendUrl() {
console.log(this.downloadPath);
//res.download(this.downloadPath);
}
});
module.exports = router;
You're misusing this. If you want to use the downloadPath variable in your functions, remove the this. from in front of them. this.downloadPath looks for a property called downloadPath on an object referenced by this, which is different from your module-global variable.
More: How does the "this" keyword work?
Even with that, you're relying on your add callback having been called before any client requests your / route, and you're returning the last value assigned to downloadPath by that add callback. I don't know enough about what you're doing to know whether that's correct, but the lack of coordination seems problematic.
Related
I have the following URL:
http://localhost:3000/?url=test
In my routes/index.js I'm great the url parameter and trying to console.log:
var express = require('express');
var router = express.Router();
var url_param;
router.get('/:url', function (req, res) {
var url_param = req.params.url;
});
var url;
var url = url_param
console.log(url);
However it doesn't log anything. In my terminal I get it performing the GET function correctly:
GET /?url=test 304 4.169 ms - -
Am I missing something?
Thanks!
(I am guessing that this will work.) Try to write you console.log inside your function. Like
router.get('/:url', function (req, res) {
var url_param = req.params.url;
console.log(/* your code */ );
});
Here's how to store the value and use it somewhere else:
var express = require('express');
var router = express.Router();
var url;
// http://localhost:3000/url
router.get('/url', function(req, res) {
res.send("the stored url is " + url);
});
// http://localhost:3000/?url=x
router.get('/:url', function(req, res) {
url = req.params.url;
console.log(url);
res.send("url stored");
});
move the console.log() inside the route.get function.
Even though you have to move the console.log(); already inside your router function you declared a different variable var url_param by so doing they don't have same reference.
Why wouldn't it work outside the route.get function?
The moment you run 'node thisfile.js' everything on the script will be processed, however router.get function will be waiting to receive an event which will only be triggered once the url is visited.
Thus without the router.get function receiving an event url_param remains undefined. So to get the url param you need to visit the url it matches.
var express = require('express');
var router = express.Router();
var url_param;
router.get('/:url', function (req, res) {
url_param = req.params.url;
console.log(url_param);//TO BE HONEST This will work
});
console.log(url_param);//TO BE HONEST THIS WOULDNT WORK
For my api I want to require a separate file to set a cookie (using cookie-parser). However res or req are not passed to the required file...
index.js
app.get('/api/user/:username', function(req, res) {
urlUsername = req.params.username;
require('./set/cookie')
});
set/cookie.js
res.cookie('login_session', urlUsername) // returns 'res' not defined
As you can see to partially overcome this problem I set urlUsername which works. But surely there has to be another way :) ?
Thanks
you need to modify your code like this
======= set/cookie.js ==========
module.exports = function(res) { // accept res parameter
res.cookie('login_session', urlUsername)
};
=========== index.js ===========
app.get('/api/user/:username', function(req, res) {
urlUsername = req.params.username;
require('./set/cookie')(res); // pass res to module
});
you need to use "module.exports" in the module you created as require returns an object with the same name.
You need to modify your cookie.js file so that it creates an object that has the ability to cache, and that you can pass 'res' object to, so that it is available in scope
For instance, your cookie.js should look like the following:
module.exports.cookie = function(res){
return {
cache: function(){
/*your cookie code*/
return res.cookie('login_session', urlUsername);
}
};
}
This returns an object that has a cache method, and that has the response object passed to it. You would then invoke this by calling:
app.get('/api/user/:username', function(req, res) {
urlUsername = req.params.username;
var cookie = require('./set/cookie').cookie(res);
cookie.cache();
});
I have made a very casual commenting system, and now I want to add replies. So, when someone posts a reply on someone else's comment, that user must be notified that someone replied to their comment. In order to do that, when the replier clicks the reply button an AJAX post request is made to the server, the server then needs to get the id of the first commenter and send them a response text using socket.io (socket.io is not required to be used if there is another way to send the reply text with another module or express itself). This is my code so far:
app.post('/reply', function(req, res){
var commenterId = req.body.userId; // this is the id of the original commenter, not the replier
User.findOne({'_id':commenterId}, function(err, user){
user.send({'replied': req.user._id}); // this is what I need to do, and
//I don't know if this specific code works and that's why I'm asking if there is a way to do it with socket.io,
// io.to(socketId).emit('reply', 'some reply text here...'); // but if I do this I don't know how to get the socketId!
//Is there even a way to do this? Maybe with another module,
//or some express function I don't know about? And if it is done with express how would
//the client side code, look like? Thank you!
});
res.end();
});
//app.js file
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io').listen(server);
var routes = require('./routes/routes')(io);
app.use('/', routes);
//router file
var express = require('express');
var router = express.Router();
var _socket = null;
//list of socket users.once they logout delete the socket by
//delete users[_socket.userid];
var users = {};
var returnRouter = function(io) {
io.sockets.on('connection', function (socket) {
//now _Socket is available inside routes
_socket = socket;
});
router.post('/login', function(req, res) {
//authentication logic
User.findOne({'email': req.body.email}, function (err, user) {
//userid must be unique
_socket.userId= user.userId
//set session variable to store id of the user
req.session.userId = user.userId;
//now every user has a socket associated with their id
users[_socket.userId] = _socket;
});
});
router.post('/reply', function (req, res) {
var commenterId = req.body.userId;
User.findOne({'_id': commenterId}, function (err, user) {
// you can get the id of the logged in user that is the creator
//of the original post from req.session.userId
//if you have implemented session store
//the commenter user object is obtained from findOne method
users[req.session.userId].emit('notification', {
notification: user.username+' commented on your post'
}});
});
res.end();
});
return router;
};
module.exports = returnRouter;
I'm trying to use a class that I created called user.js. The code for this user is:
function User(){};
User.prototype.addUser = function(){
//Do stuff
return 0;
};
module.exports = User;
I'm including it in my index.js route file, which looks like this:
var config = require('../lib/config');
var db = require('../lib/db');
var User = require('../lib/user');
var express = require('express');
var router = express.Router();
var bodyParser = require('body-parser');
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
/* GET create user. */
router.get('/newuser', function(req, res, next) {
*****var newuser = User.addUser();*****
res.render('index', {
user: newuser
});
});
module.exports = router;
However, when I visit localhost/newuser, I get the following error: TypeError: undefined is not a function. This error is being thrown in index.js on the line I marked with 5 asterisks above.
You are defining a constructor named User() and exporting it. But, then when you require('../lib/user'), you get the constructor function, but you never construct a new object with it so you don't actually have an object of type User, you just have the constructor and thus there is no method addUser() on the constructor.
Instead of this:
var User = require('../lib/user');
you can call the constructor function like this:
var u = require('../lib/user');
var User = new u();
Or, if you never need to make another one, you can do it all in one line:
var User = new (require('../lib/user'))();
Another way would be to change your User.js class to return something on these lines...
function User(){};
User.prototype.addUser = function(){
//Do stuff
return 0;
};
module.exports = new User();
I have a simple example:
server = http.createServer(function(req, res) {
uploads = {}
if (req.url == '/check') {
res.writeHead(200, {'content-type': 'text/plain'});
res.write(JSON.stringify(uploads));
res.end();
}
if (req.url == '/upload') {
var form = new formidable.IncomingForm();
form.on('fileBegin', function(field, file) {
var tracker = {file: file, progress: [], ended: false};
uploads[file.filename] = tracker;
file.on('progress', function(bytesReceived) {
tracker.progress.push(bytesReceived);
})
});
};
});
Why "check" return empty uploads ? Should I use global variable in this case ?
Your problem has nothing to do with global variables - when a new variable is created in JavaScript without being prefixed by var, it is dynamically scoped AFAIK, which means it is global. Anyway, I would feel it better if your variable was indeed declared outside the function for not only being global, but for looking global too:
var uploads = {}
server = http.createServer(function(req, res) {
if (req.url == '/check') {
// ...
Now, let us see your real problem: you are using formidable in a very strange way. I, for one, have never used the on() method of formidable.IncomingForm. I would recommend you to use the parse() method, which provides you with a key-value object where the keys are the name of the <input type="file"> field and the value is the upload file data, such as the path of the file in the server:
if (req.url == '/upload') {
var form = new formidable.IncomingForm();
form.parse(req, function (err, fields, files) {
for (var filename in files) {
var file = files[filename];
var tracker = {filename: filename, progress:[], ended:false};
fs.readFile(file.path, function(err, filecontent) {
tracker.progress.push(filecontent);
tracker.ended = true;
});
uploads[filename] = tracker;
}
res.writeHead(200, {'content-type': 'text/html'});
res.write("<A href='/'>ok</a>");
res.end();
});
IncomingForm.parse() will process all the files before call the callback. You will have all the files available, and the path to them. Of course, in this example our tracker.progress will contain only one bunch of data - which will be the complete file content.
Also, note that the form you will use to upload the file should have the enctype='multipart/form-data' attribute:
<form action='/upload' method='POST' enctype='multipart/form-data'>
That is it, hope this helps. Note that I am assuming that you need to solve a problem; if you want to test the IncomingForm.on() method, however, this answer will not be much useful. I hope I have made the correct guess :)
upload is being set to an empty object({}) on every request, its always going to be {}.
Note that the function is called on all requests
Global variable has a special meaning in node.js since all modules has a local scope.
The solution would be to declare it outside of the function.
Like this.
var uploads = {};
var server = http.createServer(function(req, res) {
....
I hope this helps