DEV Community

Cover image for Sadghuru quotes from my puppeteer api
Caner Demirci
Caner Demirci

Posted on

1 1

Sadghuru quotes from my puppeteer api

I have made a simple web app that finds and shows and enabling you to print a random Sadghuru quote from goodreads.com with Node.js, Express, Puppeteer and Vue.js. App includes some basic CSS animations also print-friendly. I don't know very well Sadghuru but I see he is a wise person. I love to read his quotes.

Here is my server (Index.js) (Packages: dotenv, cors, express, puppeteer)

require('dotenv').config();

// in .env file (PORT=5000)
const PORT = process.env.PORT || 3000;

const express = require('express');
const cors = require('cors');
const puppeteer = require('puppeteer');

const app = express();

app.use(cors());
app.use(express.json());

app.get('/', async (req, res) => {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();

    const rndPage = Math.floor(Math.random() * 26) + 1;
    await page.goto(`https://www.goodreads.com/author/quotes/30378.Sadhguru?page=${rndPage}`);

    const quotes = await page.$$('.quoteText');
    const rndQuote = Math.floor(Math.random() * (quotes.length - 1)) + 1;
    const quote = await quotes.find((el, index) => index === rndQuote).evaluate(el => el.innerText);

    await browser.close();

    return res.json({ quote });
});

app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
Enter fullscreen mode Exit fullscreen mode

const browser = await puppeteer.launch();
Try this launch({ headless: false }); and browser will show.

There are 26 pages for Sadghuru quotes. page parameter can be random 1 to 26. goodreads.com/author/quotes/30378.Sadhguru?..{rndPage}

// Get all quote div elements.
const quotes = await page.$$('.quoteText');
// Random number 1-26
const rndQuote = Math.floor(Math.random() * (quotes.length - 1)) + 1;
// Find a random quote from all quotes in the random page then grab text in the div.
const quote = await quotes.find((el, index) => index === rndQuote).evaluate(el => el.innerText);
Enter fullscreen mode Exit fullscreen mode

Then we close puppeteer browser. (await browser.close()) Then we send the quote as json. (res.json({quote}))

And Frontend

App.vue

<template>
  <div id="app">
    <h1 class="header-text">Sadghuru Quotes</h1>
    <QuoteCard :quote="quote" :loadingAnim="loading" @onclick="findQuote()" />
    <button @click="printQuote()" id="printBtn"><span class="material-icons">print</span></button>
  </div>
</template>

<script>
import axios from 'axios';
import QuoteCard from './components/QuoteCard.vue';

export default {
  name: 'App',
  components: {
    QuoteCard
  },
  data() {
    return {
      quote: null,
      loading: true,
    }
  },
  mounted() {
    this.fetchQuote();
  },
  methods: {
    async fetchQuote() {
      this.loading = true;
      const result = await axios.get('http://localhost:5000/');
      this.quote = result.data.quote;
      this.loading = false;
    },
    findQuote: function() {
      if (!this.loading) this.fetchQuote();
    },
    printQuote() {
      window.print();
    }
  }
}
</script>

<style>
.header-text {
  text-align: center;
  margin: 2rem 0 2rem 0;
}

#printBtn {
  position: fixed;
  right: 2rem;
  bottom: 2rem;
  outline: none;
  border: 0;
  padding: 1rem;
  border-radius: 50%;
  cursor: pointer;
}

#printBtn:hover {
  animation-name: printbtnanim;
  animation-duration: 1s;
  animation-iteration-count: infinite;
}

#printBtn span {
  font-size: 48px;
}

@keyframes printbtnanim {
  0% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.2);
  }
  100% {
    transform: scale(1);
  }
}

@media print {
  html, body { color: #000; }

  #printBtn {
    display: none;
  }

  .header-text {
    display: none;
  }
}
</style>
Enter fullscreen mode Exit fullscreen mode

QuoteCard.vue

<template>
    <div class="quotecard" :class="{'quotecard-anim': loadingAnim, 'fadein-anim': !loadingAnim }" @click="onClick()">
      <p>{{loadingAnim ? 'Loading...' : quote}}</p>
    </div>
</template>

<script>
export default {
    name: 'QuoteCard',
    props: {
        quote: String,
        loadingAnim: Boolean
    },
    methods: {
        onClick() {
            this.$emit('onclick');
        }
    }
}
</script>

<style scoped>
.quotecard {
  cursor: pointer;
  width: 70vw;
  max-height: 50vh;
  overflow: auto;
  padding: 2rem;
  margin: 4rem auto 0 auto;
  border-radius: 10px;
  box-shadow: 0 0 55px 1px rgba(0, 250, 250, 0.3);
  transition: all .5s;
  text-align: center;
  font-size: 1.4rem;
}

.quotecard:hover {
  box-shadow: 0 0 55px 1px rgba(0, 250, 250, 0.8);
}

.quotecard-anim {
  animation-name: quotecardanim;
  animation-duration: 1s;
  animation-iteration-count: infinite;
}

.fadein-anim {
    animation-name: fadeinanim;
    animation-duration: 2s;
}

@keyframes quotecardanim {
  0% {
    box-shadow: 0 0 55px 1px rgba(0, 250, 250, .1);
  }
  50% {
    box-shadow: 0 0 55px 1px rgba(0, 250, 250, 1);
  }
  100% {
    box-shadow: 0 0 55px 1px rgba(0, 250, 250, .1);
  }
}

@keyframes fadeinanim {
    from { opacity: 0; }
    to { opacity: 1; }
}

::-webkit-scrollbar{
width: 4px;
height: 4px;
}
::-webkit-scrollbar-thumb{
background: #ADFFFF;
border-radius: 15px;
}
::-webkit-scrollbar-thumb:hover{
background: #D4D4D4;
}
::-webkit-scrollbar-track{
background: #F0F0F0;
border-radius: 0px;
box-shadow: inset 0px 0px 0px 0px #F0F0F0;
}

@media print {
    .quotecard {
        box-shadow: none;
        width: 100%;
        max-height: none;
        padding: 1cm;
        margin: 0;
        border: 3px dashed #000;
        border-radius: 0;
    }

    .quotecard::after {
      content: 'Sad Ghuru';
      display: block;
      margin-top: 1cm;
      text-align: right;
      font-weight: bold;
    }
}
</style>
Enter fullscreen mode Exit fullscreen mode

style.css

@import url('https://fonts.googleapis.com/css2?family=Architects+Daughter&family=Italianno&display=swap');

* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

html, body {
    font-family: 'Architects Daughter', cursive;
    background: rgb(41, 41, 41) linear-gradient(rgb(41, 41, 41), rgb(0, 0, 0)) no-repeat top left;
    background-size: cover;
    color: #fff;
    height: 100%;
    width: 100%;
    overflow: hidden;
}

h1 {
    font-family: 'Architects Daughter', cursive;
}
Enter fullscreen mode Exit fullscreen mode

index.html

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <link rel="stylesheet" href="style.css">
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons"
      rel="stylesheet">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Top comments (0)

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay