Posting Twitter Status Updates (Tweets) With LinqToTwitter And .NET: Part 3, Media Tweets

So now we can successfully tweet. Twitter takes care of mentions, hashtags, links and embeddable media for us, too. That’s all great.

But what about our own media?

Photos and videos make a huge difference in terms of how people interact with a tweet; it stands to reason that visual elements also increase the number of eyeballs your tweets get, as well as how long those eyeballs remain engaged on your status and whether they click through any links in your tweet.

According to Twitter’s research, a photo or video link in a tweet doubled or tripled the likelihood of a musician’s / producer’s / record label’s status update being retweeted.

Fortunately, tweeting with media isn’t much more complicated than making a plain-text tweet. All you need to do is upload your media ahead of your tweet, then reference the media IDs when posting your status update.

And once again, LinqToTwitter has deeply abstracted that entire process for us, making it a snap … provided we’re tweeting images.

About Media In Twitter

Some important notes about the media you can upload to Twitter:

Update, 29 October 2015: LinqToTwitter now supports video uploads.

The process in LinqToTwitter for uploading and tweeting a video is exactly the same as for uploading and tweeting a single photo.

Your videos should be MP4 files using the H.264 codec and AAC audio; less than 512 MB in size and shorter than 30 seconds.

  • At this writing, LinqToTwitter does not support uploading video to Twitter.
  • You can include up to four images; or one video; or one animated GIF per tweet. For example, you can have a JPEG and a non-animated GIF in a tweet. Or one video. But not a JPEG, an animated GIF and a video; or a video and two JPEGs.
  • Images cannot be more than 5MB each.
  • LinqToTwitter will allow you to upload multiple images in parallel. This is good, bad and ugly.
    • Good, because it should take less time to upload all your pictures (assuming you have the sockets available to make multiple connections to the Twitter API, of course).
    • Bad, because that fact may make you lazy about saving your pictures to as small a file size as possible before uploading. Netiquette and common sense still rule here, make your media files as small as reasonable.
    • And ugly, thanks to the Twitter API, because you still need to wait for all your media to finish uploading before you can tweet your media.
It’s not immediately clear to me whether you are able to tweet with media that was previously uploaded to your account, or if you can tweet media that was uploaded by someone else’s account, using the media ids of those pictures / videos.

I can’t get it to work; but I may be doing it wrong.

Uploading Your Media

Building on the code we developed in Part 2 to store our Twitter credentials, and prepare a context so we can interact with the API …

using LinqToTwitter;

namespace linq2twitter_demo
{
    class Program
    {
        static void Main()
        {
            //we will come back to this in a moment
        }

        static async void SendTweet()
        {
            var auth = new SingleUserAuthorizer
            {
                CredentialStore = new SingleUserInMemoryCredentialStore
                {
                    ConsumerKey = "your consumer KEY from part 1",
                    ConsumerSecret = "your consumer SECRET from part 1",
                    AccessToken = "your access TOKEN from part 1",
                    AccessTokenSecret = "your ACCESS SECRET from part 1"
                }
            };

            var context = new TwitterContext(auth);
        }
    }
}

… we next need to upload our pictures and video for our upcoming tweet.

We do that with the UploadMediaAsync method of the TwitterContext class.

If we’re going to upload a single image, video or animated GIF, we do so with a single call to UploadMediaAsync:

var uploadedMedia = await context.UploadMediaAsync(File.ReadAllBytes(@"c:\path\to\image.jpg"));

And if we’re uploading multiple images, we do so with a List of Task<Media> objects:

var imageUploadTasks = 
	new List<Task<Media>> 
	{
		context.UploadMediaAsync(File.ReadAllBytes(@"c:\path\to\image1.jpg")),
		context.UploadMediaAsync(File.ReadAllBytes(@"c:\path\to\image2.png")),
		context.UploadMediaAsync(File.ReadAllBytes(@"c:\path\to\image3.jpg")),
		context.UploadMediaAsync(File.ReadAllBytes(@"c:\path\to\image4.gif"))
	};

await Task.WhenAll(imageUploadTasks);
As I’ve noted in previous entries, LinqToTwitter uses async methods to perform most Twitter API calls. I describe them in better detail in Part 4 of this tutorial.

For now, take it on faith that we are able to pass to the Twitter API a series of tasks to complete, have them take however long they take, and proceed with the rest of our code when they’re done. That’s what each block of code, above, does: Upload media to Twitter, and proceed with the program whenever those tasks are complete.

Aside On Non-Local Media Paths

This section talks about tweeting media that isn’t in your local filesystem. You can safely skip to the next section if you don’t need to know how to tweet media that isn’t on your computer.

Note that in the above code examples, I assume that the media you are uploading exists in your local filesystem.

However, all LinqToTwitter needs is a byte array of the media file in order to successfully upload it. So we can tweet from a Stream, from a file retrieved via a WebRequest, from the Bitmap class … in other words, if .NET can somehow address a media file, it can probably put it into a byte array; and if a media file is in a byte array, LinqToTwitter can upload it to Twitter.

Suppose, for example, what you want to do is tweet a photo that was uploaded to your website, which is hosted on a third-party server.

We can get that image into a byte array with two lines of C# code, using the WebClient class:

var client = new WebClient();
var imageBytes = client.DownloadData("http://www.example.com/path/to/image.jpg");
var uploadedMedia = await context.UploadMediaAsync(imageBytes);

And we can do the same from a Bitmap (or its parent class, Image):

var img = new Bitmap(); //var img = new Image();
var converter = new ImageConverter();
var imageBytes = (byte[])converter.ConvertTo(img, typeof(byte[]));
var uploadedMedia = await context.UploadMediaAsync(imageBytes);

So don’t feel as though your images have to be local files in order to tweet them. If you can get it into a byte array, you can use it.

Adding Your Media To Your Tweet

With our media uploaded, we’re now ready to post our tweet and make reference to those uploaded files (or file).

The way we make reference to those files (or file) in LinqToTwitter is via a List of Media IDs. (The media IDs we get back from Twitter will be long, unsigned integers.)

After LinqToTwitter uploads our pictures, it receives back from Twitter an ID number for each of those files. We send back to Twitter those same ID numbers when posting a tweet, and it knows that we intend to include those pictures in our tweet.

If we uploaded a single file, all we need to do is create a List containing the MediaID from the uploaded file:

var uploadedMedia = await context.UploadMediaAsync(File.ReadAllBytes(@"c:\path\to\image.jpg"));
var mediaIds = new List<ulong> { uploadedMedia.MediaID };

If you uploaded multiple files, we can use a LINQ expression to get the media IDs and convert them to a List:

var imageUploadTasks = 
	new List<Task<Media>> 
	{
		context.UploadMediaAsync(File.ReadAllBytes(@"c:\path\to\image1.jpg")),
		context.UploadMediaAsync(File.ReadAllBytes(@"c:\path\to\image2.png")),
		context.UploadMediaAsync(File.ReadAllBytes(@"c:\path\to\image3.jpg")),
		context.UploadMediaAsync(File.ReadAllBytes(@"c:\path\to\image4.gif"))
	};
await Task.WhenAll(imageUploadTasks);

var mediaIds =
	(from tsk in imageUploadTasks
	 select tsk.Result.MediaID)
	.ToList();
Yes, you read the preceding correctly: Even if you are only sending a single media ID to Twitter, along with your tweet text, you pass that media ID along as a List<ulong>.

That’s because the Twitter API wants an array of media IDs when calling a status update that includes media; and like all good API wrappers, LinqToTwitter is authored to have its implementation of each API method mirror the signature of the API method it is invoking.

The Completed Code

With our media ID(s) successfully put into a List, we’re ready to tweet. Here’s how it looks if I am sending only one image with my tweet:

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using LinqToTwitter;

namespace linq2twitter_demo
{
    class Program
    {
        static void Main()
        {
            Console.WriteLine("Program started.");

            try
            {
                Task.Run(() => SendTweetWithSinglePicture());
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            Console.WriteLine("Program completed.");
            Console.Read();
        }

        static async void SendTweetWithSinglePicture()
        {
            var auth = new SingleUserAuthorizer
            {
                CredentialStore = new SingleUserInMemoryCredentialStore
                {
                    ConsumerKey = "your consumer key",
                    ConsumerSecret = "your consumer secret",
                    AccessToken = "your access token",
                    AccessTokenSecret = "your access token secret"
                }
            };

            var context = new TwitterContext(auth);

            var uploadedMedia = await context.UploadMediaAsync(File.ReadAllBytes(@"c:\path\to\image.jpg"));
            var mediaIds = new List<ulong> { uploadedMedia.MediaID };

            await context.TweetAsync(
                "Hello World! I am testing @dougvdotcom's #LinqToTwitter demo, at " +
                "https://www.dougv.com/2015/08/posting-twitter-status-updates-tweets-with-linqtotwitter-and-net-part-3-media-tweets",
                mediaIds
            );
        }
    }
}

And here’s the code for tweeting up to four images (I’m sending three in this code):

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using LinqToTwitter;

namespace linq2twitter_demo
{
    class Program
    {
        static void Main()
        {
            Console.WriteLine("Program started.");

            try
            {
                Task.Run(() => SendTweetWithMultiplePictures());
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            Console.WriteLine("Program completed.");
            Console.Read();
        }

        static async void SendTweetWithMultiplePictures()
        {
            var auth = new SingleUserAuthorizer
            {
                CredentialStore = new SingleUserInMemoryCredentialStore
                {
                    ConsumerKey = "your consumer key",
                    ConsumerSecret = "your consumer secret",
                    AccessToken = "your access token",
                    AccessTokenSecret = "your access token secret"
                }
            };

            var context = new TwitterContext(auth);

            var imageUploadTasks =
                new List<Task<Media>>
                {
                    context.UploadMediaAsync(File.ReadAllBytes(@"c:\path\to\image1.jpg")),
                    context.UploadMediaAsync(File.ReadAllBytes(@"c:\path\to\image2.png")),
                    context.UploadMediaAsync(File.ReadAllBytes(@"c:\path\to\image3.jpg"))
                };
            await Task.WhenAll(imageUploadTasks);

            var mediaIds =
                (from tsk in imageUploadTasks
                 select tsk.Result.MediaID)
                .ToList();

            await context.TweetAsync(
                "Photos of Acadia National Park by Kim Seng https://www.flickr.com/photos/captainkimo/ #LinqToTwitter",
                mediaIds
            );
        }
    }
}

And that’s it for now. Part 4 discusses working with LinqToTwitter’s async methods in a synchronous program.

Github repo for this code: https://github.com/dougvdotcom/linqtotwitter_demo

I distribute code under the GNU GPL. See Copyright & Attribution for details.

All links in this post on delicious: https://delicious.com/dougvdotcom/posting-twitter-status-updates-tweets-with-linqtotwitter-and-net-part-3-media-tweets

7 Comments

  1. Can you help me with the image part of your code
     
    I am not a very accomplished C Sharp developer but this section of code seems to fail in 2015 Visual Studio with my file path.

    var uploadedMedia = await context.UploadMediaAsync(File.ReadAllBytes(@"c:\path\to\image.jpg")); 
    var mediaIds = new List { uploadedMedia.MediaID };
    
    await context.TweetAsync(
                    "Hello World! I am testing @dougvdotcom's #LinqToTwitter demo, at " +
                    "https://www.dougv.com/2015/08/posting-twitter-status-updates-tweets-with-linqtotwitter-and-net-part-3-media-tweets",
                    mediaIds
    

    I can send a text message, and embed links and all, I just cannot send pictures.

Leave a Reply

  • Check out the Commenting Guidelines before commenting, please!
  • Want to share code? Please put it into a GitHub Gist, CodePen or pastebin and link to that in your comment.
  • Just have a line or two of markup? Wrap them in an appropriate SyntaxHighlighter Evolved shortcode for your programming language, please!