Search code examples
facebook

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.


Solution

  • 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.