How I Made a Django Blog Audio Versions of My Blog Articles (Auto-Gen)

Creating a blog application not only helps us to practice our skills as a Django developer but it is also a way to share our programming knowledge. There are many people out there in different stages of their programming journey who will benefit from your wealth of experience.

By blogging about your programming knowledge, you are helping both yourself and those who read your content. The step starts with creating and using a blog application proudly designed by you using any framework of your choice. This serves as living proof of your expertise in the programming niche you are blogging.

This is what we have done so far in this tutorial series. If you want to learn how we get to the stage we are right now, you do well to start from the beginning1 of this tutorial series.

In this tutorial, we are going to add more features to our blog application to make it look more robust. Specifically, we are going to:

  1. Create a dark mode toggler;
  2. Add reading time on each page;
  3. Add an option to listen to an audio version of our content;

This helps to improve user experience. We have a lot of coding to do. Don’t just sit down there! Follow along with me and get your hands dirty with this hands-on practical project.

Adding reading time on each page

Believe it or not, time is a very precious and scarce commodity. There is a saying that time is money. Hence, you don’t joke with people’s time. When readers visit your blog, they would like to know how long it will take them to read your content. When they see you always display the reading time, they will likely come back to your website.

How can we add a reading time to our blog application? You can either use JavaScript or Python. It’s a matter of defining how many words per minute (WPM). Studies have shown that the average reading speed of an adult is 238 WPM. Thus, to check the reading time of 500-word text, divide it by 238. The result will be in minutes.

500 / 238 = 2.10

2.10 * 60 = 126 seconds or 2 mins 6 seconds

This is exactly the formula we will use except that we will go by 250WPM which is mostly used.

Open the Django shell let’s demonstrate how to do so with Python.

We split the text separated by space, and get its length. Then, we divide it by 250.

Go to the Post model in your models.py file. Create a method that will do the same thing we did above.

class Post(models.Model):
    …

    def read_time(self):
        return round(len(self.body.split(' ')) / 250)

The body field is where the blog content is written. It will split each word separated by space and get the length which is then divided by 250. The result is rounded up to the nearest minute. Wherever this method is called on the application, the reading time is displayed.

To display the reading time, go to the blog_detail template. Check where you will see the following and add the method to it.

<p class=" text-muted">{{ post.author }} | {{ post.created_on }} | Reading Time: {{ post.read_time }} Mins</p>

 Now, all your blog pages will have the reading time displayed. As simple as that.

Toggling Dark Mode

I like surfing the net with dark mode toggled. I wonder why some webmasters can’t implement such a simple feature on their web pages. This helps to reduce eye strain. Let’s get straight ahead to add this feature using JavaScript. First of all, add this to base.html just after the link to the homepage:

<button id="mode" onclick="toggler()">Night Mode</button>

Once the button is clicked the toggler() function will be executed. On the bottom after the body tag, add a link to a JavaScript file.

<script src="{% static 'js/index.js' %}"></script>

Note that the JavaScript function to be implemented will not work if the script comes before the button element. Next, add this to the styles.css file:

  #mode {
            float: right;
        }

        body.dark-mode {
           background: black;
           color: white;
        }

        body.dark-mode header {
           background: black;
           color: white;
        }


       body.dark-mode .container .card p {
           background: black;
           border-color: black;
           color: black;
       }

      body.dark-mode .container .card {
           background: black;
           border-color: black;
     }

     body.dark-mode .container .card .card-body {
           color: white;
      }

Here, we create a dark-mode CSS class name that will be called when the dark mode is toggled. We use the CSS float property to move the button to the right. We overwrite the Bootstrap to make sure that the white border is removed.

Next, create a js folder and a script.js file inside the folder. The js folder should be inside the static folder.

toggler = () => {
    let element = document.getElementById('mode');
    let body = document.body;
    if (element.innerHTML === 'Night Mode') {
       element.innerHTML = 'Light Mode';
       body.classList.toggle('dark-mode');
       
    } else {
        element.innerHTML = 'Night Mode';
        body.classList.toggle('dark-mode');
    }
}

If the text in the button is strictly ‘Night Mode’, it will change the text to ‘Light Mode, and toggle the night mode by calling the dark-mode CSS class name.

Adding an audio version of the content

To add audio to our blog app, we will use Google’s text-to-speech library. We will modify the blog_detail view function to generate an audio file and pass it to a template.

from gtts import gTTS
from django.core.files.storage import default_storage
from django.core.files.base import ContentFile
from django.conf import settings
import os

def blog_detail(request, slug):
    post = get_object_or_404(Post, slug=slug)
    comments = post.comments.filter(active=True)
    new_comments = None
    if request.method == 'POST':
        comment_form = CommentForm(data=request.POST)
        if comment_form.is_valid():
            # create object but don't save to database yet
            new_comment = comment_form.save(commit=False)
            # assign the current post to the comment
            new_comment.post = post
            # save the comment to the database
            new_comment.save()
    else:
        comment_form = CommentForm()

    # Convert the text content of the blog post to speech using gTTS
    tts = gTTS(text=post.body, lang='en')
    audio_file_name = f"{post.slug}.mp3"
    audio_folder_path = os.path.join(settings.MEDIA_ROOT, 'audio')
    os.makedirs(audio_folder_path, exist_ok=True)
    audio_file_path = os.path.join(audio_folder_path, audio_file_name)
    tts.save(audio_file_path)

    # Save the audio file to the default storage (e.g. local disk or cloud storage)
    with open(audio_file_path, 'rb') as f:
        default_storage.save(f"audio/{audio_file_name}", ContentFile(f.read()))

    # Pass the URL of the audio file to the template
    context = {
        'post': post,
        'comments': comments,
        'new_comments': new_comments,
        'comment_form': comment_form, 
           'audio_file_url': default_storage.url(f"audio/{audio_file_name}")
    }

    return render(request, 'blog_detail.html', context)

This code creates an audio folder within your media directory using the os.makedirs() function. The generated MP3 file is saved in this folder and its URL is passed to the template through the context dictionary.

In your template, you can use the audio_file_url variable to create an <audio> element that plays the generated audio file:

<audio controls>
  <source src="{{ audio_file_url }}" type="audio/mpeg">
  Your browser does not support the audio element.
</audio>

This code creates an <audio> element with a <source> element that specifies the URL of the audio file. The controls attribute adds playback controls to the audio player.

You can see it from the screenshot. Once you hit the play button, it will play the blog content. This audio will be visible on every blog page. Unlike the previous screenshot, I haven’t yet formatted this page. If you want to learn how I showed you how to format your blog page, check out this article.

Conclusion

No doubt, this tutorial has been an interesting one. We added three features to our Django blog application to improve user experience. Our blog is fully ready to be used to share our programming knowledge. Or is it?

Speaking about sharing, there are no share buttons to share to social media sites. There are no like buttons too. Moreover, I decided that there’s no need to integrate user authentication. There are many projects on that if you want to learn more.

What about migrating to a different database and deploying to a production server? So, you can see that our Django blog is not yet ready until it is ready.