Posting images on twitter with openframeworks

If you are using the system curl, then you need to use the system headers – in that case, you may need to exclude those headers on linux and include curl as a pkg-config pakage to get all the right settings.

i added this in config.make:

PROJECT_CFLAGS = $(shell pkg-config jsoncpp curl --cflags)
PROJECT_LDFLAGS=-Wl,-rpath=./libs $(shell pkg-config jsoncpp curl --libs)

i installed libcurl4-gnutls-dev with
sudo apt-get install libcurl4-gnutls-dev
package libcurl.pc in /usr/lib/x86_64-linux-gnu/pkgconfig/libcurl.pc

i get errors like this:

Compiling example_post for Release
Package curl was not found in the pkg-config search path.
Perhaps you should add the directory containing curl.pc' to the PKG_CONFIG_PATH environment variable No package 'curl' found Package curl was not found in the pkg-config search path. Perhaps you should add the directory containingcurl.pc’
to the PKG_CONFIG_PATH environment variable
No package ‘curl’ found
Compiling …/…/…/addons/ofxOAuth/src/ofxOAuth.cpp
g++ -c -Os -Wall -march=native -mtune=native -DOF_USING_GTK -DOF_USING_MPG123 -I./src -I…/…/…/addons/ofxXmlSettings/src -I…/…/…/addons/ofxXmlSettings/libs -I…/…/…/addons/ofxJSON/src -I…/…/…/addons/ofxJSON/libs -I…/…/…/addons/ofxOAuth/src -I…/…/…/addons/ofxOAuth/libs -I…/…/…/addons/ofxOAuth/libs/libcurl -I…/…/…/addons/ofxOAuth/libs/libcurl/lib -I…/…/…/addons/ofxOAuth/libs/libcurl/lib/osx -I…/…/…/addons/ofxOAuth/libs/libcurl/include -I…/…/…/addons/ofxOAuth/libs/libcurl/include/curl -I…/…/…/addons/ofxOAuth/libs/liboauth -I…/…/…/addons/ofxOAuth/libs/liboauth/lib -I…/…/…/addons/ofxOAuth/libs/liboauth/lib/linux64 -I…/…/…/addons/ofxOAuth/libs/liboauth/lib/osx -I…/…/…/addons/ofxOAuth/libs/liboauth/include -I…/…/…/addons/ofxOAuth/libs/libcurl/include -I…/…/…/addons/ofxOAuth/libs/libcurl/include/curl -I…/…/…/addons/ofxOAuth/libs/liboauth/include -I…/…/…/addons/ofxTwitter/src -pthread -D_REENTRANT -I/usr/local/include -I/usr/include/cairo -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/pixman-1 -I/usr/include/freetype2 -I/usr/include/gstreamer-0.10 -I/usr/include/libxml2 -I/usr/include/alsa -I/usr/include/libdrm -I/usr/include/GL -I/usr/include/gtk-2.0 -I/usr/lib/x86_64-linux-gnu/gtk-2.0/include -I/usr/include/atk-1.0 -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/pango-1.0 -I/usr/include/gio-unix-2.0/ -I…/…/…/libs/fmodex/include -I…/…/…/libs/glfw/include -I…/…/…/libs/glfw/include/GLFW -I…/…/…/libs/kiss/include -I…/…/…/libs/openssl/include -I…/…/…/libs/openssl/include/openssl -I…/…/…/libs/poco/include -I…/…/…/libs/portaudio/include -I…/…/…/libs/rtAudio/include -I…/…/…/libs/tess2/include -I…/…/…/libs/openFrameworks -I…/…/…/libs/openFrameworks/app -I…/…/…/libs/openFrameworks/graphics -I…/…/…/libs/openFrameworks/3d -I…/…/…/libs/openFrameworks/sound -I…/…/…/libs/openFrameworks/math -I…/…/…/libs/openFrameworks/types -I…/…/…/libs/openFrameworks/communication -I…/…/…/libs/openFrameworks/utils -I…/…/…/libs/openFrameworks/gl -I…/…/…/libs/openFrameworks/video -I…/…/…/libs/openFrameworks/events -MMD -MP -MF …/…/…/addons/obj/linux64/Release/ofxOAuth/src/ofxOAuth.d -MT …/…/…/addons/obj/linux64/Release/ofxOAuth/src/ofxOAuth.o -o …/…/…/addons/obj/linux64/Release/ofxOAuth/src/ofxOAuth.o -c …/…/…/addons/ofxOAuth/src/ofxOAuth.cpp
In file included from …/…/…/addons/ofxOAuth/src/ofxOAuth.h:36:0,
from …/…/…/addons/ofxOAuth/src/ofxOAuth.cpp:26:
…/…/…/addons/ofxOAuth/src/ofxOAuthVerifierCallbackServer.h: In member function ‘bool ofxOAuthAuthReqHandler::parseQuery(const string&, Poco::Net::NameValueCollection&)’:
…/…/…/addons/ofxOAuth/src/ofxOAuthVerifierCallbackServer.h:259:44: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
In file included from …/…/…/addons/ofxOAuth/libs/libcurl/include/curl/curl.h:35:0,
from …/…/…/addons/ofxOAuth/src/ofxOAuth.cpp:37:
…/…/…/addons/ofxOAuth/libs/libcurl/include/curl/curlrules.h: At global scope:
…/…/…/addons/ofxOAuth/libs/libcurl/include/curl/curlrules.h:143:41: error: size of array ‘curl_rule_01’ is negative
…/…/…/addons/ofxOAuth/src/ofxOAuth.cpp: In member function ‘std::map<std::basic_string, std::basic_string > ofxOAuth::obtainRequestToken()’:
…/…/…/addons/ofxOAuth/src/ofxOAuth.cpp:1277:40: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
…/…/…/addons/ofxOAuth/src/ofxOAuth.cpp: In member function ‘std::map<std::basic_string, std::basic_string > ofxOAuth::obtainAccessToken()’:
…/…/…/addons/ofxOAuth/src/ofxOAuth.cpp:1482:40: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
make[1]: *** […/…/…/addons/obj/linux64/Release/ofxOAuth/src/ofxOAuth.o] Errore 1
make: *** [Release] Errore 2
Process terminated with status 2 (0 minutes, 1 seconds)
1 errors, 3 warnings

Hi @bakercp I have updated ofxOauth twitter example to help you testing the new functions.

@kalwalt can you please send me the changes you’ve made in ofxTwitter to make it work on Linux? I´ll try to avoid those mistakes from now on.

@pelayo i did only this:

//
//  ofxTwitterSearch.h
//  twitterapp
//
//  Created by Pelayo on 13/12/13.
//
//

#pragma once

#include "ofMain.h"

struct ofxTwitterSearch {

    string query;
    // required
    //ofPoint geocode = ofPoint(0,0);
    ofPoint geocode;
    // x. latitude / y. longitude
    //bool bUseMiles = false;
    bool bUseMiles ;
    //int geocode_radius = 1;
    int geocode_radius ;
    string lang;
    // ISO 639-1 http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
    string locale;
    // only 'ja' is efective here
    string result_type;
    // valid values: mixed, recent, popular
    //int count = 15;
    int count ;
    // default 15 . maximun 100
    string until;
    // YYYY-MM-DD ej:2012-09-01
    //nt since_id = -1;
    //int max_id = -1;
    //bool include_entities = true;
    int since_id ;
    int max_id ;
    bool include_entities ;
    //string callback;

};

and this:

/*
 *  ofxTwitterTweet.h
 *
 *  Created by Douglas Edric Stanley on 10/12/10
 *  cc-by-sa 2010 www.abstractmachine.net. Some rights reserved.
 *  http://creativecommons.org/licenses/by-sa/3.0/
 *
 *  Adapted from code by Paul Shen
 *  cf. http://in.somniac.me/2010/01/openframeworks-http-requests-xml-parser/
 *
 *  10/12/2013 Adapted to Twitter API 1.1 by Pelayo MŽndez
 *
 */

#pragma once

#include "ofMain.h"

struct ofxTwitterTweetAuthor {

    string id_str;
    string uri;

    string name;
	string screen_name;
    string description;
    string location;
    string lang;
    string url;

    bool default_profile;
    bool default_profile_image;
    bool geo_enabled;

	string profile_image_url;
    ofImage profile_image;
    //bool profile_image_url_loaded = false;
    bool profile_image_url_loaded ;
    string profile_banner_url;
    ofImage profile_banner;
    //bool profile_banner_url_loaded = false;
    bool profile_banner_url_loaded ;
    string profile_background_image_url;
    string profile_background_color;
    bool profile_background_tile;
    bool profile_use_background_image;


};

struct ofxTwitterTweet {

	string id_str;
    string created_at;
	string language;
    string text;
    string geo;
    ofPoint coordinates;
    string source;
    int retweet_count;

    bool truncated;

	ofxTwitterTweetAuthor user;

    ofxTwitterTweet() { }
	ofxTwitterTweet(string defaultString)	: text(defaultString) { }

	void print() {

		string str;

        str +=  "\n";
        str +=  "\n--- Tweet ---";
		str += "\nid_str: " + id_str;
		str +=  "\ncreated_at: " + created_at;
        str +=  "\nlanguage: " + language;
		str +=  "\ntext: " + text;
        str +=  "\ngeo: " + geo;
        str +=  "\ncoordinates: " + ofToString(coordinates.x) + "/" + ofToString(coordinates.y);
        str +=  "\nsource: " + source;
        str +=  "\nretweet_count: " + ofToString(retweet_count);
        str +=  "\n--- User ---";
		str +=  "\nuser:name: " + user.name;
        str +=  "\nuser:screen_name: "+ user.screen_name;
		str +=  "\nuser:uri: " + user.uri;
		str +=  "\nuser:imageUri: " + user.profile_image_url;
        str +=  "\nuser:description: " + user.description;
        str +=  "\nuser:location: " + user.location;
        str +=  "\nuser:lang: " + user.lang;
        str +=  "\nuser:url:" + user.url;

		cout << str;

	}

//user.profile_image_url_loaded = false;
//user.profile_banner_url_loaded = false;

    bool isProfileImageLoaded() {
        if (user.profile_image.isAllocated() && user.profile_image_url_loaded) {
            return true;
        } else {
            return false;
        }
    }

    bool isBannerImageLoaded() {
        if (user.profile_banner.isAllocated() && user.profile_banner_url_loaded) {
            return true;
        } else {
            return false;
        }
    }

};

but i think that is not a good solution, possible other bugs will come with it. I don’t feel very comfortable with the structs. i don’t found any other quick solutions.

I did one step more; this flags works with json and curl packages installed ( you need to delete json headers in the ofxJSON addon or exclude it from source):

PROJECT_LDFLAGS=-Wl,-rpath=./libs  $(shell pkg-config curl  --libs)
PROJECT_LDFLAGS=+-Wl,-rpath=./libs  $(shell pkg-config jsoncpp  --libs)

and

PROJECT_CFLAGS = $(shell pkg-config curl  --cflags)
PROJECT_CFLAGS +=$(shell pkg-config  jsoncpp  --cflags)

but now i receive error in ofxTwitter.cpp updatueStatus(string,string) to this line:

   ../src/ofxTwitter.cpp|136|error: no matching function for call to ‘ofxOAuth::postfile_multipartdata(std::string&, std::basic_string<char>, const char [8], std::string&)’|
../../../addons/ofxTwitter/src/ofxTwitter.cpp:136:95: note: candidate is:
../../../addons/ofxOAuth/src/ofxOAuth.h:81:17: note: std::string ofxOAuth::postfile_multipartdata(const string&, const string&, const string&)
../../../addons/ofxOAuth/src/ofxOAuth.h:81:17: note:   candidate expects 3 arguments, 4 provided

i think i’m not testing the right branch, @pelayo please could you tell me what branches are you using (ofoAUth, ofsJson, ofxTwitter)?

Thanks @kalwalt I will change the way I assign default value to the variables in my code to avoid that issue.

I´m using the ‘master’ branch of my fork of ofxOauth at the moment to post to Twitter.

Solved! it works! a big WooooW! i have a tweet on my page with the buses.jpg!
But i needed to fix just some little things in ofxTwitter.cpp, due to some “ambiguituies” in the code:

// if(trends[i]["geo"] != NULL) {
            if(trends[i]["geo"] != Json::Value::null) {

and

 //if(author["profile_image_url"] != NULL && bLoadUserProfileImageOnMemory) {
            if(author["profile_image_url"] != Json::Value::null && bLoadUserProfileImageOnMemory) {

and

 //if(author["profile_banner_url"] != NULL && bLoadUserBannerImageOnMemory) {
            if(author["profile_banner_url"] != Json::Value::null && bLoadUserBannerImageOnMemory) {

the code commented was your version.

and also i do an error before with LD_FLAGS, this is right:

PROJECT_LDFLAGS=-Wl,-rpath=./libs  $(shell pkg-config curl  --libs)
PROJECT_LDFLAGS+= -Wl,-rpath=./libs  $(shell pkg-config jsoncpp  --libs)

i used your master branch of ofxoAUTH and ofxTwitter

I will send a PR in your github repo with these changes @pelayo!

Great! :slight_smile: It´s really nice to hear that you have it working.

Yes @kalwalt please send me a PR with the changes you have made to make ofxTwitter work on linux, so I can keep improving the addon from there.

yes! PR already sended.

1 Like

twitter api expects an image of each standard size to be loaded multipart media[] of specific sizes and data format per api return values… best to include each size explicitly or twimg will resize recompress and mangle input.

here:

https://dev.twitter.com/docs/api/1.1/get/help/configuration

https://dev.twitter.com/docs/api/1.1/post/statuses/update_with_media

My concern is the posting of image data (picture with frame including signature, generated/stamped by client) that will not be resized nor compressed by the server and posted to server unmodified, which should match the size and types indicated of certain max size.

twit:@gaobs

I may not follow your questions @gaobs but update_with_media currently only supports a single file be uploaded per POST (as given by the max_media_per_upload property of help/configuration) and the receiving server resizes the user-supplied image into each proxy it needs.

pushing the issue at twit:@kurrik and twitter legal as the accurate representation of data is a legal requirement by intl law, same problem with all other services…

Nope, I totally don’t follow. :confused:

Hi @gaobs, @pizthewiz

Size of the image is passed automatically with the update status request. Data format should be PNG, JPG or GIF.

What the addon lacks yet is calls to get the current max_media_per_upload and photo_size_limit values. current max_media_per_upload its not a problem since you’re sending one photo but the final size of the image should be check not to be bigger than ‘photo_size_limit’ to avoid compression on the server.

I will be updating the addon with a more finished version of the photo upload this week.

Hey great topic here! this addon also need some test on windows OS i hope to do it soon… @pelayo did you see my PR?

Thanks to @gaobs I understand a little bit better how Twitter handles the images once you send them. PNG is the way to go for better results as JPG is recompresed loosely and GIF is converted on PNG.

I have included that info in the source code and logs of ofxTwitter in case anyone want to make tests. I also included a call to ‘GET help/configuration’ on the initial setup, so you can have info about max_media_per_upload, photo_size_limit or supported photo_sizes before sending your media.

@pelayo
thanks for this post, it helped me resolve the issues with updateStatus() and postfile_multipartdata.
I’was following your ofxTwitter search example but nothing is returned in my app, and here is the log:

[notice ] ofxTwitter::authorize: Authorizing app…
[notice ] ofxTwitter::authorize: Config up to date.
==================THIS IS THE INSIDE OF THE THE FUNCTION
in here
URL TO CHECK https://api.twitter.com/1.1/search/tweets.json?q=ucsb
CURLOPT_CAINFO: …/…/…/data/cacert.pem
CURL RETURNED NOTHING

maybe I miss some steps? I wasn’t sure how to deal with the credential.xml and cacert.pem. So, I just copied the credential_old.xml from you example, renamed it as credential.xml, and replaced keys in the file. What is the right way to create credential.xml and cacert.pem under application data folder? is this what’s causing the log in my console?

Thanks.

Hi @awayOF

“Cacert.pem” should be in the data directory. The credentials.xml file will be created automatically once you have authorized your app the first time.

Have you created your app in https://dev.twitter.com/apps?

I think this can be the most tricky part. Maybe it´s not explained properly in the readme file in Github.