New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tweeting w/ Media using postMediaChunked fails with error 324: Unsupported raw media category #439

Closed
SpreadShotStudios opened this Issue Jun 6, 2018 · 11 comments

Comments

Projects
None yet
4 participants
@SpreadShotStudios

SpreadShotStudios commented Jun 6, 2018

My Firebase web project has been working for several months now. But on Sunday June 3, 2018, my application stopped sending tweets with media (images) attached. Before this, it was working for months. I have not changed the failing code before the 3rd and I have even reverted to code that worked before but the app still fails.

SDK versions:
I am using the most up to date versions of Firebase tools (3.18.6), and cloud functions (1.0.3). Along with twit (2.2.10) a javascript library for twitter api.
Do note my project was also working on older versions of the above including pre v1.0 cloud functions. Also note, I am still able to send regular text tweets just not ones with any media (image,gif,mp4).

How to reproduce:
For simplicity, I will link to the code in a tutorial which I originally used when starting the project.

Setup a twitter account and retrieve all the necessary tokens as outlined in the tutorial. Then simply call the cloud function below and it will attempt to tweet the NASA image of the day.

The cloud function is able to upload the picture to the twitter server and the response I get is expected:

{ media_id: 1004461244487643100,
  media_id_string: '1004461244487643136',
  media_key: '5_1004461244487643136',
  size: 92917,
  expires_after_secs: 86400,
  image: { image_type: 'image/jpeg', w: 960, h: 1318 } }

However, once it attempts to post the tweet with the media attached, I receive an error code 324:

'Unsupported raw media category'

which doesn't exist in Twitter's docs: https://developer.twitter.com/en/docs/basics/response-codes.html

Now, Code 324 does exist but in Twitter's docs there is a different description:

"The validation of media ids failed"

Which I have yet to receive. So my media id is valid, so something else is wrong. No where on the internet can I find someone with this exact error.

Link to tutorial code:
https://medium.freecodecamp.org/how-to-build-and-deploy-a-multifunctional-twitter-bot-49e941bb3092

Javascript code that reproduces the issue:
**index.js

'use strict';
const functions = require('firebase-functions');
const request = require('request');
const path = require('path');
const os = require('os');
const fs = require('fs');
const tmpDir = os.tmpdir(); // Ref to the temporary dir on worker machine

const Twit = require('twit');

const T = new Twit({
	consumer_key: 'your twitter key'
	,consumer_secret: 'your twitter secret'
	,access_token: 'your twitter token'
	,access_token_secret: 'your twitter token secret'
});

exports.http_testMediaTweet = functions.https.onRequest((req, res) => {

	function getPhoto() {
		const parameters = {
			url: 'https://api.nasa.gov/planetary/apod',
			qs: {
				api_key: 'DEMO_KEY'
			},
			encoding: 'binary'
		};
		request.get(parameters, (err, response, body) => {
			if (err) {console.log('err: ' + err)}
			body = JSON.parse(body);
			var f = path.join(tmpDir, 'nasa.jpg');
			saveFile(body, f);
		});
	}

	function saveFile(body, fileName) {
		const file = fs.createWriteStream(fileName);
		request(body).pipe(file).on('close', err => {
		  if (err) {
			console.log(err)
		  } else {
			console.log('Media saved! '+body.title)
			const descriptionText = body.title
			uploadMedia(descriptionText, fileName);
		  }
		})
	}

	function uploadMedia(descriptionText, fileName) {
		const filePath = path.join(__dirname, `../${fileName}`)
		console.log(`uploadMedia: file PATH ${fileName}`)
		T.postMediaChunked({
		  file_path: fileName
		}, (err, data, respone) => {
		  if (err) {
			console.log(err)
		  } else {
			console.log(data)
			const params = {
			  status: descriptionText,
			  media_ids: data.media_id_string
			}
			postStatus(params);
		  }
		})
	}

	function postStatus(params) {
		T.post('statuses/update', params, (err, data, respone) => {
		  if (err) {
			console.log(err)
			res.status(500).send('Error: ' + err);
		  } else {
			console.log('Status posted!')
			res.status(200).send('success');
		  }
		})
	}

	// Do thing
	getPhoto();

});

Hopefully I am just doing something wrong, thanks. I'm posting this here and hedging my bets, I've also posted this issue on Twitter and Firebase's forums.

@SpreadShotStudios

This comment has been minimized.

SpreadShotStudios commented Jun 7, 2018

Also, I ran the provided test "rest_chunked_upload.js" using mocha and it failed 0/2. So either this function is no longer working or its just me? Other tests run fine like twit.js
twit-fail

@JasonMHasperhoven

This comment has been minimized.

JasonMHasperhoven commented Jun 8, 2018

@SpreadShotStudios I ran into the same issue and fixed it. I shall submit a PR shortly.

Basically what you can do for now is copy twit from node_modules inside your project and import this instead. To make it work edit the lib/file_uploader inside like so:

var FileUploader = function(params, twit) {
  assert(params);
  assert(
    params.file_path,
    'Must specify `file_path` to upload a file. Got: ' + params.file_path + '.',
  );
  var self = this;
  self._file_path = params.file_path;
  self._media_category = params.media_category; // add this
  self._twit = twit;
  self._isUploading = false;
  self._isFileStreamEnded = false;
};

FileUploader.prototype._finalizeMedia = function(media_id, cb) {
  var self = this;
  self._twit.post(
    'media/upload',
    {
      command: 'FINALIZE',
      media_id: media_id,
      media_category: self._media_category, // add this
    },
    cb,
  );
};

FileUploader.prototype._appendMedia = function(
  media_id_string,
  chunk_part,
  segment_index,
  cb,
) {
  var self = this;
  self._twit.post(
    'media/upload',
    {
      command: 'APPEND',
      media_id: media_id_string.toString(),
      media_category: self._media_category, // add this
      segment_index: segment_index,
      media: chunk_part,
    },
    cb,
  );
};

FileUploader.prototype._initMedia = function(cb) {
  var self = this;
  var mediaType = mime.lookup(self._file_path);
  var mediaFileSizeBytes = fs.statSync(self._file_path).size;

  // Check the file size - it should not go over 15MB for video.
  // See https://dev.twitter.com/rest/reference/post/media/upload-chunked
  if (mediaFileSizeBytes < MAX_FILE_SIZE_BYTES) {
    self._twit.post(
      'media/upload',
      {
        command: 'INIT',
        media_type: mediaType,
        media_category: self._media_category, // add this
        total_bytes: mediaFileSizeBytes,
      },
      cb,
    );
  } else {
    var errMsg = util.format(
      'This file is too large. Max size is %dB. Got: %dB.',
      MAX_FILE_SIZE_BYTES,
      mediaFileSizeBytes,
    );
    cb(new Error(errMsg));
  }
};

Then you can add your media category as a param:

  const mediaCategoryMap = {
    'image/jpeg': 'tweet_image',
    'image/png': 'tweet_image',
    'image/webp': 'tweet_image',
    'image/gif': 'tweet_gif',
  };

T.postMediaChunked({
  file_path: file.path,
  media_category: mediaCategoryMap[file.type] || 'tweet_image',
}, (err, data) => {
  ...
});
@SpreadShotStudios

This comment has been minimized.

SpreadShotStudios commented Jun 8, 2018

@JasonMHasperhoven Brilliant! This is such good news for me, thanks so much. I will try what you said shortly but I just wanted to understand why this bug happened in the first place?

I posted this error to Twitter dev forum and they swore to me nothing changed on their back-end.

So how does something like this suddenly break last weekend? Twitter said adding media_category has always been mandatory when uploading media but that's bull. This module has been working correctly for several months (since the beginning of my project anyways).

Anyway, kudos 🍻 for responding to this so quickly I thought I was dead in the water. This issue is cropping up in every other js twitter library I've been trying since this happened.

@SpreadShotStudios

This comment has been minimized.

SpreadShotStudios commented Jun 8, 2018

Also don't forget video category. Not sure what media type amplify_video pertains to but I know its a thing.


	const mediaCategoryMap = {
		'image/jpeg': 'tweet_image',
		'image/png': 'tweet_image',
		'image/webp': 'tweet_image',
		'image/gif': 'tweet_gif',
		'video/mp4': 'tweet_video'
		//'???': 'amplify_video'
	};
@SpreadShotStudios

This comment has been minimized.

SpreadShotStudios commented Jun 8, 2018

Cool, got it working now. Thanks bro I owe you a 6 pack 🍺🍺🍺🍺🍺🍺

Just a note, I noticed in your pasted code you left out the apostrophes for the params, should look like:

  if (mediaFileSizeBytes < MAX_FILE_SIZE_BYTES) {
    self._twit.post('media/upload', {
      'command': 'INIT',
      'media_type': mediaType,
      'media_category': self._media_category, // add this
      'total_bytes': mediaFileSizeBytes
    }, cb);
  }
@SpreadShotStudios

This comment has been minimized.

SpreadShotStudios commented Jun 8, 2018

Another note, I couldn't upload mp4 video until I removed

|| 'tweet_image'

from

		T.postMediaChunked({
			file_path: fileName,
			media_category: mediaCategoryMap[mediaType] || 'tweet_image'
		}
@JasonMHasperhoven

This comment has been minimized.

JasonMHasperhoven commented Jun 8, 2018

Glad that I could be of help :)

@JasonMHasperhoven

This comment has been minimized.

JasonMHasperhoven commented Jun 8, 2018

Just a note, I noticed in your pasted code you left out the apostrophes for the params, should look like:

The apostrophes don't do anything in this case. I guess they got removed automatically because I use eslint and prettier.

@SpreadShotStudios

This comment has been minimized.

SpreadShotStudios commented Jun 8, 2018

Ah ok didn't know that.

@BooDoo

This comment has been minimized.

Contributor

BooDoo commented Jun 13, 2018

Minimum change is simply replacing dm_image, dm_gif and dm_video with their tweet_... equivalent in the existing FileUploader.prototype._initMedia function.

I'll submit a PR with this minimum change tonight. If someone wants to over-engineer something else and submit their own, of course feel free.

@ttezel

This comment has been minimized.

Owner

ttezel commented Jun 30, 2018

This shoudl be fixed by #442. @SpreadShotStudios feel free to reopen if this error still occurs for you after updating to twit v2.2.11!

@ttezel ttezel closed this Jun 30, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment