tdranv
tdranv

Reputation: 1340

Facebook photo upload date timestamp

I've downloaded all my Facebook data and wish to upload some of the images that I've sent via Messenger to Google Photos. I wish to have them to have the correct metadata so they are uploaded under the correct day, not under today. Unfortunately, they have the date of download for Date created.

I tried parsing the title, but it doesn't seem to be a timestamp.

My question is: is there a way to create a script that adds the correct metadata to a photo downloaded from Facebook (via Download your information archive)? An example title is: 142666616_209126620919024_535058535265435125_n.jpg. This photo should have the date Jan 27, 2021, 10:53 AM.

Upvotes: 1

Views: 2737

Answers (1)

tdranv
tdranv

Reputation: 1340

After some digging I found a solution.

The archive that Facebook gives you has folders for each friend with the following structure:

\friend_name_a1b2c3
   \photos
      12345678_123456788996_123124421.jpg
   \gifs
   \audio
    messages_1.json

messages_1.json has all your messages with that friend and here is an example how a message looks like:

{
    "sender_name": "Your Name",
    "timestamp_ms": 1562647443588,
    "photos": [
      {
        "uri": "messages/inbox/friend_name_a1b2c3/photos/12345678_123456788996_123124421.jpg",
        "creation_timestamp": 1562647443
      }
    ],
    "type": "Generic",
    "is_unsent": false
},

So, using glob and utimes I came up with the following script:

var glob = require("glob")
var Promise = require('bluebird');
var fs = Promise.promisifyAll(require("fs"));
var { utimes } = require("utimes");

const readJSONFiles = async () => {
  const messagesFiles = glob.sync(`**/message_*.json`)
  const promises = [];
  messagesFiles.forEach(mFile => {
    promises.push(fs.readFileAsync(mFile, 'utf8'));
  })

  return Promise.all(promises);
}

readJSONFiles().then(result => {
  const map = {};
  result.forEach(data => {
    const messagesContents = JSON.parse(data);
    messagesContents.messages
      .filter(m => m.photos)
      .forEach(m => {
        m.photos.filter(p => {
          const splitted = p.uri.split("/")
          const messagePhotoFileName = splitted[splitted.length - 1];
          map[messagePhotoFileName] = m.timestamp_ms;
        })
      })
  })

  fs.writeFileSync("./map.json", JSON.stringify(map))
}).then(() => {
  fs.readFileAsync("./map.json", 'utf8').then(data => {
    const map = JSON.parse(data);
    glob("**/*.jpg", function (er, files) {
      files.forEach(file => {
        const [, , photo] = file.split("/");

        utimes(file, {
          btime: map[photo],
          atime: map[photo],
          mtime: map[photo]
        });
      })
    })
  })
});

It creates a map of file-name:date-taken then loops over all .jpg files and changes its metadata. It definitely is a little rough around the edges but gets the job done, after all.

Upvotes: 2

Related Questions