Add Google Photos into your notes, either locally stored or a remote thumbnail to save space. Works on mobile too :)

New plugin released :partying_face:

This is now a plugin! Check out the new thread here:


Original post

This is a quick and easy way to embed your Google Photos into your notes. You can add any photo in 3 different ways:

  1. As an locally-saved thumbnail, with a link to the remote fullsize image. The thumbnail sizes are fully configurable. The image will be saved into the same folder as the current note.
  2. As a remotely hosted thumbnail, with a link to the remote fullsize image. This way you can add lots of visual photos to your notes without taking up any vault space at all.
  3. As a text link to the fullsize photo.

This works on both desktop and mobile.

The remote thumbnail images created by the script are permanent as far as I know. I have images created this way in 2016 which are still functional, so you shouldn’t end up with broken thumbnails in your vault.

You could also use this as a reference implementation for any other remote image hosting service.

How to use it

  1. Click the Share button on any Google Photo, and click Create link.

  2. Copy the link, launch the template from Templater and paste in the share link:

  1. Choose the type of embed that you want:

  1. Done!

How to set it up

  1. Install Templater from the Community Plugins.

  2. Create a new Templater template file.

  3. Copy and paste this code into the new file. Make sure to paste with Ctrl + Shift + V, so that you don’t paste with any formatting. The resulting code should look identical to this. If it’s double-spaced, you’ve pasted it wrong.

<%*
// You can create any number of resolution options that you want to select from
const resolutions = [
  {
    name: 'Thumbnail', // A friendly name
    res: [350, 250]    // Width by height. The image will maintain aspect ratio
  },
  {
    name: 'Large',
    res: [1280, 720]
  }
]

// Prompt for the Google Photos share link
const link = await tp.system.prompt('Enter the Google Photos share link')
const types = {
  link: 'As a link',
  remote: 'As an image (remote)',
  local: 'As an image (saved locally)'
}

if (link && link.startsWith('https://photos.app.goo.gl/')) {
  const type = await tp.system.suggester(Object.values(types), Object.keys(types))
  if (type === 'link') {
    return `[Link](${link})`
  } else if (type) {
    // Prompt for the download size
    let size
    if (resolutions.length === 1) {
      size = resolutions[0].res
    } else {
      size = await tp.system.suggester(resolutions.map(x => x.name), resolutions.map(x => x.res))
    }
    if (!size) return '' // no resolution was selected

    // Fetch the actual photo image URL from the provided Google Photos link
    try {
      const raw = await requestUrl({ url: link })
      const html = raw.text
      // Note: this line is written as a single-quoted RegExp because Templater has difficulty processing the regex otherwise
      const image = html.match(new RegExp('<img[^>]*?src="(https:[^"]+?)=w\\d+-h\\d+-no"[^>]*?alt=""[^>]*?>', 's'))[1]
      // Generate the download link for the thumbnail image file
      const thumbnail = `${image}=w${size[0]}-h${size[1]}-no`
      if (type === 'remote') {
        // For a remote image, use the Markdown syntax to embed the remote thumbnail file
        // and point the link to the remote fullsize image
        return `[![](${thumbnail})](${link})\n`
      } else if (type === 'local') {
        // For a local image, download the thumbnail image and save to the current note's folder
        const folder = tp.file.path(true).replace(/[^/]+$/, '')
        const filename = moment().format('YYYY-MM-DD') + '_google-photo_' + moment().format('HHmmss') + '.jpg'
        const imageData = await requestUrl({ url: thumbnail })
        await app.vault.adapter.writeBinary(folder + filename, imageData.arrayBuffer)
        // Return the link with local thumbnail pointing to remote fullsize image
        return `[![](${filename})](${link})\n`
      }
    } catch (e) {
      // do nothing
    }
  }
}
return ''
%>
10 Likes

Thanks for making this! But having some trouble getting it to work with my gphoto links.

When I launch the template, enter the URL, type, and resolution, I get nothing out of it. Added some console.log debugs and the try catch error is: Error: TypeError: Cannot read properties of null (reading '1'). A little more digging and it looks like the RegExp on the html is returning null.

This is one of the links I’m attempting to use: https://photos.app.goo.gl/aYQ6bcapMFP9NUd97
I know little about regular expressions or digging through html so this is about as deep as I can go right now, any ideas? Thanks!

It works perfectly for me with your provided photo link, and I see this:

To isolate the error from anything else which might be happening inside your vault, I’ve created a demo vault which you can test with.

Please download this vault and let me know if you still experience the same issue when working inside this vault:

Download demo vault

(The demo vault was created by copy/pasting the code above without any changes.)

If it still fails, can you give me some more details about what OS you’re running / what version of Obsidian?

Thanks for putting that together. Yes the issue is persisting, and when I put back in some debug prints to print the catch exception, it looks like the same error as before.

My Obsidian version is 1.0.3 and OS is macOS Montery 12.0.1.

Thanks - might be something different in MacOS. We’ll troubleshoot this over DM.