21

I am working for a charity which is promoting sign language, and they want to post a video to their FB page every day. There's a large (and growing) number of videos, so they want to schedule the uploads programmatically. I don't really mind what programming language I end up doing this in, but I've tried the following and not got very far:

  • Perl using WWW::Facebook::API (old REST API)

    my $res = $client->video->upload(
     title => $name,
     description => $description,
     data => scalar(read_file("videos/split/$name.mp4"))
    );
    

    Authentication is OK, and this correctly posts a facebook.video.upload method to https://api-video.facebook.com/restserver.php. Unfortunately, this returns "Method unknown". I presume this is to do with the REST API being deprecated.

  • Facebook::Graph in Perl or fb_graph gem in Ruby. (OAuth API)

    I can't even authenticate. Both of these are geared towards web rather than desktop applications of OAuth, but I think I ought to be able to do:

    my $fb = Facebook::Graph->new(
     app_id => "xxx",
     secret => "yyy",
     postback => "https://www.facebook.com/connect/login_success.html"
    );
    print $fb->authorize->extend_permissions(qw(publish_stream read_stream))->uri_as_string;
    

    Go to that URL in my browser, capture the code parameter returned, and then

    my $r = $fb->request_access_token($code);
    

    Unfortunately:

    Could not fetch access token: Bad Request at /Library/Perl/5.16/Facebook/Graph/AccessToken/Response.pm line 26
    
  • Similarly in Ruby, using fb_graph,

    fb_auth = FbGraph::Auth.new(APP_ID, APP_SECRET)
    
    client = fb_auth.client
    client.redirect_uri = "https://www.facebook.com/connect/login_success.html"
    puts client.authorization_uri(
      :scope => [:publish_stream, :read_stream]
    )
    

    Gives me a URL which returns a code, but running

    client.authorization_code = <code>
    FbGraph.debug!
    access_token = client.access_token!
    

    returns

    {
      "error": {
        "message": "Missing client_id parameter.",
        "type":    "OAuthException",
        "code":    101
      }
    }
    

    Update: When I change the access_token! call to access_token!("foobar") to force Rack::OAuth2::Client to put the identifier and secret into the request body, I get the following error instead:

    {
      "error": {
        "message": "The request is invalid because the app is configured as a desktop app",
         "type":   "OAuthException",
         "code":   1
      }
    }
    

How am I supposed to authenticate a desktop/command line app to Facebook using OAuth?

amon
  • 57,091
  • 2
  • 89
  • 149
Simon Cozens
  • 755
  • 4
  • 16
  • the error is {"error":{"message":"Invalid verification code format.","type":"OAuthException","code":100}} – Pradeep Feb 27 '14 at 14:23
  • Is your App in development mode? It appears that the App must be made public in order to upload videos. In the App details section (under the link https://developers.facebook.com/x/apps//app-details/ where `` is your App ID.), the videos section of a newly created App says *Your app is currently in development mode. Apps must be made public to upload videos.* – Moh Mar 02 '14 at 20:52

4 Answers4

8

So, I finally got it working, without setting up a web server and doing a callback. The trick, counter-intuitively, was to turn off the "Desktop application" setting and not to request offline_access.

FaceBook::Graph's support for posting videos doesn't seem to work at the moment, so I ended up doing it in Ruby.

fb_auth = FbGraph::Auth.new(APP_ID, APP_SECRET)

client = fb_auth.client
client.redirect_uri = "https://www.facebook.com/connect/login_success.html"

if ARGV.length == 0
  puts "Go to this URL"
  puts client.authorization_uri(:scope => [:publish_stream, :read_stream] )
  puts "Then run me again with the code"
  exit
end

if ARGV.length == 1
  client.authorization_code = ARGV[0]
  access_token = client.access_token! :client_auth_body
  File.open("authtoken.txt", "w") { |io| io.write(access_token)  }
  exit
end

file, title, description = ARGV

access_token = File.read("authtoken.txt")
fb_auth.exchange_token! access_token
File.open("authtoken.txt", "w") { |io| io.write(fb_auth.access_token)  }

me = FbGraph::Page.new(PAGE_ID, :access_token => access_token)
me.video!(
    :source => File.new(file),
    :title => title,
    :description => description
)
Simon Cozens
  • 755
  • 4
  • 16
  • Well, offline_access is deprecated since a long time. And, if I understand the Ruby code correctly, you have to manually call the given URL and paste the then produced Access Token for the second run. This is not really gaining an Access Token from the command line. You could also just use the Graph API Explorer to get the Page Access Token liek this: https://developers.facebook.com/tools/explorer?method=GET&path=me%2Faccounts But I'm glad you found a solution which works for you. – Tobi Mar 05 '14 at 08:16
6

Problem is in your case that for OAuth you'll need some endpoint URL which is publicly reachable over the Internet for Facebook servers, which can be a no-go for normal client PCs, or a desktop application which is capable of WebViews (and I assume, command line isn't).

Facebook states at https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#login that you can build a desktop client login flow, but only via so-called WebViews. Therefore, you'd need to call the OAuth endpoint like this:

https://www.facebook.com/dialog/oauth?client_id={YOUR_APP_ID}&redirect_uri=https://www.facebook.com/connect/login_success.html&response_type=token&scope={YOUR_PERMISSION_LIST}

You then have to inspect the resulting redirected WebView URL as quoted:

When using a desktop app and logging in, Facebook redirects people to the redirect_uri mentioned above and places an access token along with some other metadata (such as token expiry time) in the URI fragment:

https://www.facebook.com/connect/login_success.html#access_token=ACCESS_TOKEN... 

Your app needs to detect this redirect and then read the access token out of the URI using the mechanisms provided by the OS and development framework you are using.

If you want to do this in "hacking mode", I'd recommend to do the following.:

  • As you want to post to a Page, get a Page Access Token and store it locally. This can be done by using the Graph Explorer at the

https://developers.facebook.com/tools/explorer?method=GET&path=me%2Faccounts

endpoint. Remember to give "manage_pages" and "publish_actions" permissions.

curl -v -0 --form title={YOUR_TITLE} --form description={YOUR_DESCRIPTION} --form source=@{YOUR_FULL_FILE_PATH} https://graph-video.facebook.com/{YOUR_PAGE_ID}/videos?access_token={YOUR_ACCESS_TOKEN}

References:

https://developers.facebook.com/docs/graph-api/reference/page/videos/#publish https://developers.facebook.com/docs/reference/api/video/

Tobi
  • 31,405
  • 8
  • 58
  • 90
  • AIUI, Page Access Tokens obtained using the Graph Explorer expire after an hour; this is not suitable for a job which runs every day. – Simon Cozens Mar 06 '14 at 02:35
  • Well, you could either call /me/accounts each time before you want to execute your page operations, or follow the steps outlined at http://stackoverflow.com/questions/12168452/long-lasting-fb-access-token-for-server-to-pull-fb-page-info to get a long-lived (60 days) page access token – Tobi Mar 06 '14 at 07:38
1

From the facebook video API reference:

An individual Video in the Graph API.

To read a Video, issue an HTTP GET request to /VIDEO_ID with the user_videos permission. This will return videos that the user has uploaded or has been tagged in.

Video POST requests should use graph-video.facebook.com.

So you should be posting to graph-video.facebook.com if you are to upload video.

You also need extended permissions from the user or profile you'll be uploading to, in this case you need video_upload this is going to be requested once only, when the user currently logged in is asked for such permission for the app.

And your endpoint should be:

https://graph-video.facebook.com/me/videos

If you always want to post to a specific user than you'll have to change the endpoint part from /me to the User ID or page ID.

Here's a sample (in PHP):

$app_id = "YOUR_APP_ID";
$app_secret = "YOUR_APP_SECRET"; 
$my_url = "YOUR_POST_LOGIN_URL"; 
$video_title = "YOUR_VIDEO_TITLE";
$video_desc = "YOUR_VIDEO_DESCRIPTION";

$code = $_REQUEST["code"];

if(empty($code)) {
   $dialog_url = "http://www.facebook.com/dialog/oauth?client_id=" 
     . $app_id . "&redirect_uri=" . urlencode($my_url) 
     . "&scope=publish_stream";
    echo("<script>top.location.href='" . $dialog_url . "'</script>");
}

$token_url = "https://graph.facebook.com/oauth/access_token?client_id="
    . $app_id . "&redirect_uri=" . urlencode($my_url) 
    . "&client_secret=" . $app_secret 
    . "&code=" . $code;
$access_token = file_get_contents($token_url);

$post_url = "https://graph-video.facebook.com/me/videos?"
    . "title=" . $video_title. "&description=" . $video_desc 
    . "&". $access_token;

echo '<form enctype="multipart/form-data" action=" '.$post_url.' "  
     method="POST">';
echo 'Please choose a file:';
echo '<input name="file" type="file">';
echo '<input type="submit" value="Upload" />';
echo '</form>';

Although I'm concerned about the upload speed if the videos are too big, but I'm guessing your customer has already sorted that out (compress/optimize/short videos etc.)

I've made you a demo here. Go to my website (I own that domain) and try to upload a video. I tried with this one which is a relatively small 4Mb file. Be sure that this script will only try to upload a video, nothing more (to the FB profile you are currently logged in, that is) but, if you are still concerned, copy my snippet, upload it to your own server (with PHP support of course) and create a test app where the site url is that domain and be sure to specify in the $my_url variable your endpoint which is basically the full path to your script receiving responses from facebook:

http://yourdomain.com/testfb.php 

If you still want to do it on a desktop app then you have to go to developer.facebook.com on your app settings:

Settings > Advanced

And look for the first option:

enter image description here

And enable that switch so that facebook allows you to POST from a desktop or native app instead of a web server.

Note: I'm not an expert on Ruby, but the above working PHP code should be pretty obvious and easy to port to it.

Gustavo Rubio
  • 10,209
  • 8
  • 39
  • 57
  • If you re-read the question, you'll notice that this is primarily about obtaining an auth token for a non-web application using Perl or Ruby: “*How am I supposed to authenticate a desktop/command line app to Facebook using OAuth?*” – This means your PHP code is not only irrelevant because of the language choice, but also because it incorrectly assumes a web interface. There are still some good parts in your answer (the “native or desktop app” switch), but I'm not sure whether that warrants more than a comment. – amon Mar 01 '14 at 23:15
  • 2
    @amon quoting: "I don't really mind what programming language I end up doing this in, but I've tried the following and not got very far..." Granted that the question revolves around a desktop app, nonetheless, I wanted to demo the main issue here which is posting VIDEO, if you re-read the question. Maybe changing the title to "Get access token from facebook in perl/ruby for a desktop application" would be more appropiate. – Gustavo Rubio Mar 02 '14 at 01:46
0

as far as I recall, what you want isn't really possible without some kind of endpoint that can receive a callback from facebook.

If you can finagle an oauth token, from say the Graph API Explorer, then it becomes pretty trivial to use a gem like koala to upload your video.

here's the salient bit:

@graph = Koala::Facebook::API.new(access_token)
@graph.put_video(path_to_my_video)

I've made you a sample project here: fb-upload-example

mikfreedman
  • 191
  • 1
  • 3