Monday, 31 January 2022

How to Create Dynamic Open Graph Images with Google Sheets

An open graph image (OG image) is the image that is displayed when any of your website links are shared on Facebook, LinkedIn or Twitter. You can provide the public URL of the image in the meta tags of your website and social media website will automatically pick up from there.

<head>
  <title>Digital Inspiration</title>
  <meta property="og:image" content="https://www.labnol.org/og/default.png" />
</head>

Open Graph Images with Puppeteer

Github internally use Google’s Puppeteer library to generate dynamic Open Graph images. The images are generated on the fly by feeding custom HTML into Puppeteer which then generates a screenshot. You can view a sample OG image generated by Github in this tweet.

Vercel, the company behind Next.js, also uses Puppeteer for their open graph image generator. Headless chromium is used to render an HTML page, a screenshot of the page is captured and the file is cached for improved performance.

Create Open Graph Images without Puppeteer

Puppeteer is a wonderful library (I use it internally for screnshot.guru) but it does require some technical know-how to deploy Puppeteer as a cloud function. There’s also cost involved in deploying Puppeteer to the cloud since you’ve to pay for ever request made to the service.

Generate Open Graph Images

If you are looking for no-code, no-cost, no-puppeteer solution, you can use Google Sheets to generate Open Graph images. That’s what I use to generate dynamic and unique images for every page of my website. You can see a sample OG image in this tweet.

The idea is inspired by Document Studio. You create an image design in Google Slides, replace the placeholder text in the template with the title of your webpage, then generate a screenshot image of the presentation and save it in your Google Drive.

To get started, make a copy of this Google Sheet in your Google Drive. Replace the titles in Column A with the titles of your pages and clear the Image URL column. Click the Play button, authorize the script and you’ll notice that the spreadsheet is immediately updated with the image URLs for each page.

Add more page titles in the Google Sheet, hit the Play button again and the spreadsheet will be updated with image URLs of only the new pages. That’s it.

Open Graph Images

Test your Open Graph Images

After you’ve added the Open Graph meta tags to your website, you can test your Open Graph images using the tool below.

  1. cards-dev.twitter.com/validator - Paste the URL of your website in the URL field and click the Validate button to see if Twitter is able to render the image provided in your Open Graph meta tags. You may also use this validator tool to clear the OG Image from Twitter’s cache for any page.

  2. developers.facebook.com/tools/debug/ - Paste the URL of your website in the URL field and click the Debug button to see if Facebook is able to render the image provided in your Open Graph meta tags.

  3. linkedin.com/post-inspector/ - LinkedIn’s Post Inspector tool can help you determine how your web page will appear when shared on the LinkedIn platform. You can also request LinkedIn to re-scrape the page if the associated OG Image has changed.

How Open Graph Image Generator Works?

Inside the Google Sheet, go to the Extensions menu and choose Apps Script to view the source code that is used to generate the Open Graph images. You can also create graphics in Canva using any of the available templates and then import Canva designs in Google Slides.

The app is written in Google Apps Script. It reads the post titles from Google Sheets, generates a copy of the presentation for every row in the sheet, generates a screenshot of the slide and adds it to your Google Drive.

const FOLDER = 'Open Graph Images';
const TEMPLATE_ID = '1QZ4mR6B36XEVyzJf-s8vq88SDnSRPiRDchJ71VM-cfU';

const APP = {
  /* Create a folder in Google Drive for storing open graph images */
  getFolder() {
    if (typeof this.folder === 'undefined') {
      const folders = DriveApp.getFoldersByName(FOLDER);
      this.folder = folders.hasNext() ? folders.next() : DriveApp.createFolder(FOLDER);
    }
    return this.folder;
  },

  /* Download the Slide thumbnail URL and save it to Google Drive */
  getImageUrl(contentUrl, title) {
    const blob = UrlFetchApp.fetch(contentUrl).getBlob();
    const file = this.folder.createFile(blob);
    file.setName(title);
    return file.getUrl();
  },

  /* Make a temporary copy of the Google Slides template  */
  getTemplate(title) {
    const slideTemplate = DriveApp.getFileById(TEMPLATE_ID);
    const slideCopy = slideTemplate.makeCopy(title, this.getFolder());
    return slideCopy.getId();
  },

  /* Get the thumbnail URL of the Google Slides template */
  getThumbnailUrl(presentationId) {
    const { slides: [{ objectId }] = {} } = Slides.Presentations.get(presentationId, {
      fields: 'slides/objectId',
    });

    const data = Slides.Presentations.Pages.getThumbnail(presentationId, objectId);
    return data.contentUrl;
  },

  /* Replace the placeholder text with the title of the web page */
  createImage(title) {
    const presentationId = this.getTemplate(title);
    Slides.Presentations.batchUpdate(
      {
        requests: [
          {
            replaceAllText: {
              containsText: { matchCase: false, text: '' },
              replaceText: title,
            },
          },
        ],
      },
      presentationId
    );
    const contentUrl = this.getThumbnailUrl(presentationId);
    const imageUrl = this.getImageUrl(contentUrl, title);
    /* Trash the presentation copy after the image is downloaded */
    DriveApp.getFileById(presentationId).setTrashed(true);
    return imageUrl;
  },

  /* Show job progress to the user */
  toast(title) {
    SpreadsheetApp.getActiveSpreadsheet().toast('✔️ ' + title);
  },

  run() {
    const sheet = SpreadsheetApp.getActiveSheet();
    sheet
      .getDataRange()
      .getDisplayValues()
      .forEach(([title, url], index) => {
        /* Only process rows that have a title */
        if (title && !/^http/.test(url) && index > 0) {
          const imageUrl = this.createImage(title);
          sheet.getRange(index + 1, 2).setValue(imageUrl);
          this.toast(title);
        }
      });
  },
};


from Digital Inspiration https://ift.tt/NAjct3vFC

Sunday, 30 January 2022

How to Hide Empty AdSense Slots When No Ads are not Available

If you are using Google AdSense to monetize your website, you may have noticed that there are instances when there are no ads showing up on one or more pages.

This is likely because Google AdSense is not able to find any advertisers that are probably willing to bid on your page at that time. Or may be you have blocked specific categories of advertisers from bidding on your page.

When Ads Are Available

Here’s a page with a Google AdSense ad unit placed somewhere in the middle of the page.

Google AdSense Ads

When Ads Are Unavailable

And here’s the same page with the same Google AdSense ad unit but with the ad unit replaced with a blank space as the ad inventory is not available.

Google AdSense Ads Unfilled

Behind the Scenes

When any Google AdSense ad unit on your website sends an ad request to Google’s servers, the AdSense server either responds with an ad or it sets the status of the ad unit to unfilled meaning that there are no ads to serve at that time.

If you would not like to see any whitespace in your website because of unavailability of ads, you can easily hide the unfilled ad units with CSS.

Open your website template and add the following CSS to the <head> section of your HTML:

<style>
  ins.adsbygoogle[data-ad-status='unfilled'] {
    display: none !important;
  }
</style>

Now if there are any empty ad slots on your website, they will collapse and will not be displayed. You may also consider replacing the unfilled AdSense ad unit with a fallback image that internally links to one of your own pages.



from Digital Inspiration https://ift.tt/oiexsWwFr

Create Google Cloud Function to Generate Images in Google Cloud Storage

This example shows how you can use Google Cloud Function to generate open graph images from Google Slides template in Google Drive. You may open any page on this website and look for the og:image meta tag in the head to see the generated image that is different for each image.

When the cloud function is invoked, the input text is provided in the query string and this replaces the placeholder in the template to generate the customized image. The generated image is stored in Google Cloud storage and the file’s public URL is returned.

Create a Service Account

Go to console.cloud.google.com and create a new Google Cloud project. While the project is selected, go to APIs & Services > Credentials > Create credentials and select Service Account.

Give your service account a name and grant the Project > Owner role to the service account.

Create Service Account

Your service account will have an email address like <project-id>-<service-account-name>@<project-id>.iam.gserviceaccount.com.

Related: Use Service Accounts with Apps Script

Create a Service Account Key

In the Google Cloud Console, click the email address for the service account that you have created in the preview step. Click Keys > Add Key > Create new key. A JSON file will be downloaded to your computer. Make sure you add this file to the .gitignore file as it contains the private key and should not be committed to the repository.

You can also pass the authentication credentials to the cloud function by setting the environment variable GOOGLE_APPLICATION_CREDENTIALS to the path of the JSON file.

export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account.json"

Enable the Google Cloud APIs

Go to APIs & Services > Library and enable the Google Slides API and the Google Drive API for your cloud project.

Enable Google Cloud APIs

Create a Google Slides Template

Go to Google Slides and create a new presentation containing a single slide. Add a text box containing the text and ensure that the Resize shape to fit text option is checked since we may have a long title as well.

Google Slides Template

Add the service account email as an editor to the Google Slides presentation.

Create a Google Drive Folder

Create a new folder in your Google Drive and share it with the service account email. This folder will be used to store the slide templates that are used to generate the open graph images.

Make a note of the ID of the folder and the Slides template created in the previous step.

Create a Cloud Storage Bucket

Switch to Google Cloud Storage and create a new bucket for storing the generated images. Please note that billing must be enabled in your Google Cloud project for using this feature.

Write the Google Cloud Function

Initialize a new project in your local disk with the npm init command and add the code to the index.js file. We create our own signed JWT from the service account’s private key and then exchange the JWT for an Access Token to authenticate the Google APIs.

const fetch = require('node-fetch');
const { google } = require('googleapis');
const { client_email, private_key } = require('./creds.json');
const { Storage } = require('@google-cloud/storage');

const { client_email, private_key } = require('./creds.json');
const jwtClient = new google.auth.JWT(client_email, null, private_key, [
  'https://www.googleapis.com/auth/drive',
  'https://www.googleapis.com/auth/presentations',
]);

const slides = google.slides({ version: 'v1', auth: jwtClient });
const drive = google.drive({ version: 'v3', auth: jwtClient });

const CLOUD_STORAGE_BUCKET = 'BUCKET_NAME_GOES_HERE';
const FOLDER_ID = 'DRIVE_FOLDER_ID_GOES_HERE';
const PRESENTATION_ID = 'PRESENTATION_ID_GOES_HERE';

const createOgImage = async (fileName, replaceText) => {
  const { data: { id: presentationId } = {} } = await drive.files.copy({
    fileId: PRESENTATION_ID,
    fields: 'id',
    requestBody: { name: fileName, parents: [FOLDER_ID] },
  });

  await slides.presentations.batchUpdate({
    presentationId,
    requestBody: {
      requests: [
        {
          replaceAllText: {
            replaceText,
            containsText: { matchCase: false, text: '' },
          },
        },
      ],
    },
  });

  const { data = {} } = await slides.presentations.get({
    presentationId,
    fields: 'slides/objectId',
  });

  const { data: { contentUrl } = {} } = await slides.presentations.pages.getThumbnail({
    presentationId,
    pageObjectId: data.slides[0].objectId,
  });

  const response = await fetch(contentUrl);
  const arrayBuffer = await response.arrayBuffer();
  const buffer = Buffer.from(arrayBuffer);
  await drive.files.delete({ fileId: presentationId });
  return buffer;
};

const generateImagesAPI = async (req, res) => {
  const storage = new Storage();
  const bucket = storage.bucket(CLOUD_STORAGE_BUCKET);
  const text = req.query.text;
  const fileName = `${text.replace(/\s/g, '-').toLowerCase()}.png`;
  const file = bucket.file(fileName);
  const [fileExists] = await file.exists();
  if (fileExists === false) {
    const buffer = await createOgImage(fileName, text);
    await file.save(buffer, {
      resumable: false,
      contentType: 'image/png',
      public: true,
    });
    await file.makePublic();
  }
  const fileLink = `${storage.apiEndpoint}/${CLOUD_STORAGE_BUCKET}/${fileName}`;
  res.set('Cache-Control', 'public, max-age=86400, s-maxage=86400');
  return res.redirect(fileLink);
};

module.exports = generateImagesAPI;

Deploy the cloud function

If you are using Firebase, you can deploy the function using the firebase deploy --only functions command.

After the function is deployed, go to the Google Cloud Console > Cloud Function and edit your function. Expand the Runtime, build, connections and security section and reduce the memory allocation from 256MB to 128MB. You may also reduce the timeout to sometime like 30s since it is not a very resource intensive function.

Memory Google Cloud Function



from Digital Inspiration https://ift.tt/aNWl4osdT

Saturday, 29 January 2022

How to Import Canva Designs into Google Slides

Canva is everyone’s favorite tool for creating social media graphics, YouTube video thumbnails, Instagram stories logos, and even regular presentations. The app is jam-packed with beautiful, ready-to-use templates that make it easy to even non-designers to create unique and impressive designs.

Google Slides is probably the most popular app for creating presentations but their built-in template library is limited and haven’t seen any update in a long time.

Google Slides Template Library

Cavan Presentation Templates

Compare the Slides library to Canva and you’ll find thousands of presentation templates that will fit the needs of any project, be it in education, business, or personal.

Canva Presentation Templates

Import Canva Templates into Google Slides

If you prefer using Canva for creating presentations slides and Google Slides for collaborative editing, the good news is that you can get the best of both worlds. You can easily import Canva designs into Google Slides and then edit them in Google Slides.

Here’s how you convert any Canva designs into Google Slides.

  1. Open any presentation or graphic created inside Canva.

  2. Click the 3-dot horizontal menu on the right side of the screen and choose the Microsoft PowerPoint format under the Share section.

Download Canva Graphic as PowerPoint

The PowerPoint export format is available for Canva presentations as well as for any other graphic created inside Canva. If the PowerPoint icon is not visible, you can click the See all link to get a list of all the available formats.

  1. Now that you have the PowerPoint file on the desktop, open your Google Drive and click the New > File Upload button bot to import the .pptx file into Google Drive.

  2. Once the file is uploaded in Google Drive, and choose File > Save as Google Slides to convert the PowerPoint file into Google Slides.

You will now be abled to edit the presentation in Google Slides. The import may not be perfect, but because individual elements of the presentation are imported separately, it is easy to make changes.

Also, you should ensure that if any custom fonts are used in the Canva presentation, you have the same enabled inside your Google Slides as well. It is recommended that you either use standard common fonts or choose any of the Google Fonts since they are available both in Google Slides and Canva.

Save PowerPoint as Google Slides

Import Canva Designs as Images

If you would like your Canva design to appear as is in Google Slides, you can consider importing them as images in Google Slides. The designs will be displayed exactly as they are in Canva but you’ll lose the ability to edit them in Google Slides.

  1. Open any Canva graphic, click the Download button, and choose PNG Image as the download format.
  2. Switch to Google Slides, go to the Slides menu and choose Change Background.
  3. Choose the image exported from Canva and click OK to apply the background.

Do make sure that your Slides dimensions are the same as the Canva graphic dimensions else the image may appear stretched.

Change Background Image in Google Slides



from Digital Inspiration https://bit.ly/3oaOeop

Friday, 28 January 2022

Manage Shared Drives in Google Drive with Google Apps Script

These code samples show how you can use Google Apps Script to manage and search through the content of shared drives in Google Drive using the Drive API.

To get started, click the + icon in the Services section to add the Drive API search to your Google project. Google Apps Script currently supports Drive API v2 though the latest version is v3.

Google Drive API Service

Once the Drive API service is enabled, you can use the Drive API to search through the content of shared drives.

Create a Shared Drive

function createSharedDrive() {
  const driveName = 'Digital Inspiration';
  const sharedDrive = Drive.Drives.insert({ name: driveName }, Utilities.getUuid());
  console.log('Shared Drive created', sharedDrive.id);
}

Share a Shared Drive with a User

function shareSharedDriveWithUser({ driveId, role, email }) {
  // role can be writer, reader, organizaer or commenter
  const response = Drive.Permissions.insert(
    {
      role: role,
      type: 'user',
      value: email,
    },
    driveId,
    {
      supportsAllDrives: true,
      sendNotificationEmails: true,
      fields: 'emailAddress,role',
    }
  );
  console.log('Shared Drive shared with %s', response.emailAddress);
}

Please note that you can only share Shared Drive with Google accounts. The API will not throw an exception if you try share a Shared drive with a non-Google account.

List all Shared Drives

Print a list of all Shared Drives that are accessible to the current user.

function listSharedDrives() {
  let pageToken = null;
  const response = [];

  do {
    const { items = [], nextPageToken = null } = Drive.Drives.list({
      pageToken,
      maxResults: 50,
      orderBy: 'name',
      fields: 'nextPageToken, items(id, name)',
    });
    items.forEach((item) => response.push(item));
    pageToken = nextPageToken;
  } while (pageToken !== null);

  console.log(response);
}

List Files in a Shared Drive

In the next example, we’ll print a list of all files contained in a specific Shared Drive identified by its drive ID that we retrieved in the previous example.

function listFilesInSharedDrive(teamDriveId) {
  let pageToken = null;
  const response = [];

  do {
    const { items = [], nextPageToken = null } = Drive.Files.list({
      pageToken,
      maxResults: 50,
      supportsAllDrives: true,
      includeItemsFromAllDrives: true,
      q: `'${teamDriveId}' in parents and trashed = false and mimeType != 'application/vnd.google-apps.folder'`,
      fields: 'nextPageToken, items(id,title,mimeType)',
    });
    items.forEach((item) => response.push(item));
    pageToken = nextPageToken;
  } while (pageToken !== null);

  console.log(response);
}

Move Files in Shared Drives

Files contained in a specific Shared Drive can be moved to another Shared Drive or to another folder in the same Shared Drive depending on permissions.

function moveFilesBetweenSharedDrives({ parentFolderId, destinationFolderId, fileId }) {
  const data = Drive.Files.update({}, fileId, null, {
    addParents: destinationFolderId,
    removeParents: parentFolderId,
    supportsAllDrives: true,
    fields: 'title,embedLink',
  });
  console.log('File Moved', data.title, data.embedLink);
}

The getCanMoveItemOutOfDrive() method can be used to determine whether the current user can move this item outside of this drive by changing its parent.

Copy Files in Shared Drives

The next snippet illustrates how you can copy files from one Shared Drive to another or between folders of the same Drive. The destinationFolerId is the ID of the folder where the file will be copied to.

function copyFilesInSharedDrives({ title, destinationFolderId, fileId }) {
  const data = Drive.Files.copy({ parents: [{ id: destinationFolderId }], title }, fileId, {
    supportsAllDrives: true,
    fields: 'title,embedLink',
  });
  console.log('File Copied', data.title, data.embedLink);
}


from Digital Inspiration https://ift.tt/32DZNwM