Open janvda opened 4 years ago
interesting and good case. If I add a setNewToken
to the photos object, you could call it after refresh? would that work?
Sorry for the delay I had another issue that blocked me.
Your proposal would be great.
I would call the method rather setNewAccessToken
to make clear that it is the access token and not the refresh token that must be passed as argument.
I had the same problem. Google tokens automatically expire in ~1hr, so one must reload it in advance, to avoid any HTTP - Unauthorized
errors. Here is how I did it:
/*
This doesn't need to be a global var, but it makes things much easier if
you plan to use it in other parts of the code.
*/
var photos;
main();
async function main() {
/*
authorizeGAPI() follows ideal procedure with reading a credentials file,
initializing google.auth.OAuth2 with those credentials, and calling
.setCredentials() on it using a local token file. It also returns a JSON
object representing the token (exact contents of the token file)
*/
let [OA2Client, token] = await authorizeGAPI();
// We only need the access token for this
photos = new Photos(token.access_token);
/*
This is the important part; it will automatically refresh the token and
Photos object every 45 minutes (2,700,000ms).
*/
setInterval(() => refreshGAPIToken(OA2Client), 2700000);
}
async function refreshGAPIToken(OA2Client) {
console.log("Refreshing GAPI token...");
// You probably already know about this procedure as you said.
let token = await OA2Client.refreshAccessToken().catch((err) => {
console.log(`!! Error refreshing GAPI token: ${err}`);
});
/*
We need to await the whole JSON data to complete for some reason.
Also the "token" returned is the token JSON plus some stuff we don't need.
*/
token = await token.credentials;
// Delete the old Photos instance just to be sure
delete photos;
// Initialize it just like at the start.
photos = new Photos(token.access_token);
// Verify that the token has been updated properly
if (photos.transport.authToken != token.access_token) {
console.log("!! Error refreshing GAPI token: Unable to update GPhotos token.");
/*
For the sake of simplicity, I'm leaving this empty. You can implement
your own retry mechanics.
*/
}
// Write the token to a file for later use.
await fs.promises.writeFile("./token.json", JSON.stringify(token))
.catch(err => {
m.exitErr(`!! Error saving token file, you must fix this first: ${err}`);
});
return;
}
In summary: Yes, you do need to initialize a new Photos()
object, after making sure you delete the old one.
I have had the code running an evening, overnight, and a morning, (~16hrs) and it has uploaded without fail.
hmmm maybe we should just build in a way into the photos client to do the refresh with the refresh token so that this is handled for the users.
I agree, this 'replacing the photos object' thing might lead to memory leaks and is a little bit janky. There should be a way to replace the token like @janvda suggested.
Any changes on this matter in the meantime?
Update on my implementation: I've been running it for two months without user intervention and can report no memory leaks. Its memory usage has stayed at a constant 177K during that whole time. For now I am confident in saying that this is a reliable way to go about this while such a feature is not yet implemented.
@skybldev I have support for this here: https://github.com/roopakv/google-photos/pull/47
Curious what you think
I am just wondering how to automatically handle expired access tokens.
I gradually understand how I can use google.auth.OAuth2() so that it will automatically request a new access token (using the refresh token) when it becomes expired but it is not clear how I should integrate this with this library ?
Should I call again
new Photos(...)
but this time with the new access token ?kr Jan.