├── index.js ├── README.md ├── lib ├── feed.js ├── entry.js └── client.js └── LICENSE /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/client'); 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | node-gdata 2 | ========== 3 | 4 | A Google Data API client for node.js. Only the latest release (version 3) 5 | of the GData protocol supported. 6 | 7 | Requirements 8 | ------------ 9 | 10 | * [node-oauth](https://github.com/ciaranj/node-oauth) 11 | 12 | Example (Picasa Web Albums) 13 | --------------------------- 14 | 15 | var GDClient = require('node-gdata').GDClient; 16 | var PICASA_ALBUMS_URL = 'https://picasaweb.google.com/data/feed/api/user/default'; 17 | 18 | var google = new GDClient('consumer key', 'consumer secret'); 19 | 20 | // call getAccessToken() to obtain, or setAccessToken() if you have one 21 | google.setAccessToken('token', 'token secret'); 22 | 23 | google.get(PICASA_ALBUMS_URL, function(err, feed) { 24 | feed.getEntries().forEach(function(entry) { 25 | console.log('album: ' + entry.getTitle()); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /lib/feed.js: -------------------------------------------------------------------------------- 1 | /** 2 | * feed.js 3 | * A GData Feed 4 | * 5 | * @author Amir Malik 6 | */ 7 | 8 | var Entry = require('./entry').Entry; 9 | 10 | function Feed(feed) { 11 | this.feed = feed['feed']; 12 | this.entries = []; 13 | 14 | try { 15 | this.last_update = new Date(Date.parse(this.feed['updated']['$t'])); 16 | } catch(e) { 17 | throw new Error('feed must contain "updated" key'); 18 | } 19 | 20 | if(this.feed['entry']) { 21 | for(var i = 0; i < this.feed['entry'].length; i++) { 22 | this.entries.push(new Entry(this.feed['entry'][i])); 23 | } 24 | } 25 | } 26 | 27 | Feed.prototype.getUpdateDate = function getUpdateDate() { 28 | return this.last_update; 29 | }; 30 | 31 | Feed.prototype.getEntries = function getEntries(i) { 32 | if(i) 33 | return this.entries[i]; 34 | else 35 | return this.entries; 36 | }; 37 | 38 | Feed.prototype.count = function count() { 39 | return this.entries.length; 40 | }; 41 | 42 | exports.Feed = Feed; 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011 Amir Malik 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /lib/entry.js: -------------------------------------------------------------------------------- 1 | /** 2 | * entry.js 3 | * A GData Entry 4 | * 5 | * @author Amir Malik 6 | */ 7 | 8 | function Entry(entry) { 9 | this.entry = entry; 10 | 11 | if(entry) 12 | this._init(); 13 | } 14 | 15 | Entry.prototype._init = function _init() { 16 | var self = this; 17 | var selfId; 18 | var subFeedId; 19 | var parentId; 20 | 21 | // type of entry 22 | this.entry['category'].forEach(function(category) { 23 | if('http://schemas.google.com/g/2005#kind' == category['scheme']) { 24 | self.kind_uri = category['term']; 25 | } 26 | }); 27 | 28 | // parent folder 29 | this.entry['link'].forEach(function(link) { 30 | switch(link['rel']) { 31 | case 'self': 32 | selfId = link['href']; 33 | break; 34 | 35 | case 'http://schemas.google.com/g/2005#feed': 36 | subFeedId = link['href']; 37 | break; 38 | 39 | case 'http://schemas.google.com/docs/2007#parent': 40 | parentId = link['href']; 41 | break; 42 | } 43 | }); 44 | 45 | if(subFeedId) 46 | this.feed_url = subFeedId; 47 | }; 48 | 49 | Entry.prototype.getEtag = function getEtag() { 50 | return this.entry['gd$etag']; 51 | }; 52 | 53 | Entry.prototype.getTitle = function getTitle() { 54 | return this.entry['title']['$t']; 55 | }; 56 | 57 | Entry.prototype.getPublishedDate = function getPublishedDate() { 58 | return new Date(Date.parse(this.entry['published']['$t'])); 59 | }; 60 | 61 | Entry.prototype.getUpdateDate = function getUpdateDate() { 62 | return new Date(Date.parse(this.entry['updated']['$t'])); 63 | }; 64 | 65 | Entry.prototype.getId = function getId() { 66 | return this.entry['id']['$t']; 67 | }; 68 | 69 | Entry.prototype.getFeedURL = function getFeedURL() { 70 | return this.feed_url; 71 | }; 72 | 73 | Entry.prototype.getResourceId = function getResourceId() { 74 | return this.entry['gd$resourceId']['$t']; 75 | }; 76 | 77 | Entry.prototype.getKind = function getKind() { 78 | return this.kind_uri; 79 | }; 80 | 81 | exports.Entry = Entry; 82 | -------------------------------------------------------------------------------- /lib/client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * client.js 3 | * Google Data API client 4 | * 5 | * @author Amir Malik 6 | */ 7 | 8 | var querystring = require('querystring'), 9 | OAuth = require('oauth').OAuth; 10 | 11 | var Feed = require('./feed').Feed; 12 | 13 | var GDClient = function(consumer_key, consumer_secret) { 14 | this.consumer_key = consumer_key; 15 | this.consumer_secret = consumer_secret; 16 | this.access_token = undefined; 17 | this.access_token_secret = undefined; 18 | this.oauth = new OAuth('https://www.google.com/accounts/OAuthGetRequestToken', 'https://www.google.com/accounts/OAuthGetAccessToken', consumer_key, consumer_secret, '1.0', null, 'HMAC-SHA1'); 19 | this.oauth._headers['GData-Version'] = '3.0'; // HACK 20 | }; 21 | 22 | GDClient.prototype.getAccessToken = function(email, password, cb) { 23 | if(!email || !password) 24 | return cb(new Error('getAccessToken(email, password, cb) expected')); 25 | 26 | cb(new Error('TODO')); 27 | }; 28 | 29 | GDClient.prototype.setAccessToken = function setAccessToken(token, token_secret) { 30 | this.access_token = token; 31 | this.access_token_secret = token_secret; 32 | }; 33 | 34 | GDClient.prototype.get = function get(url, optargs, cb) { 35 | if(typeof optargs == 'function') cb = optargs, optargs = {}; 36 | 37 | // always request JSON output instead of XML 38 | // TODO: try adding header: Accept: application/json to force JSON 39 | if(url.lastIndexOf('alt=json') == -1) { 40 | if(url.lastIndexOf('?') > -1) { 41 | url += '&alt=json'; 42 | } else { 43 | url += '?alt=json'; 44 | } 45 | } 46 | 47 | this.oauth.get(url, 48 | optargs.token || this.access_token, 49 | optargs.secret || this.access_token_secret, 50 | function(err, data, res) { 51 | if(err) { 52 | cb(err); 53 | } else { 54 | try { 55 | cb(null, new Feed(JSON.parse(data))); 56 | } catch(e) { 57 | cb(e); 58 | } 59 | } 60 | }); 61 | }; 62 | 63 | exports.GDClient = GDClient; 64 | --------------------------------------------------------------------------------