Skip to main content
📜Heads up!
  • I wrote this post more than 2 years ago.
  • That's enough time for things to change.
  • Possibly, I may not endorse the content anymore.
Google Latest Articles Instead

counting...

The goal is to...

  1. Send notifications on Installation and Updates of a given Chrome Extension (with different content, of course)
  2. Open specific links when notifications are clicked.

Sending Notifications

var extensionPage =
'https://chosunghyun.com/youtube-comment-language-filter'
var updateLogPage =
'https://chosunghyun.com/youtube-comment-language-filter/updates'

chrome.runtime.onInstalled.addListener(function (object) {
if (object.reason === 'install') {
chrome.notifications.create(extensionPage, {
title: 'YCLF is now installed 😎',
message:
'Click here to learn more about the extension!',
iconUrl: './images/min-icon128.png',
type: 'basic',
})
} else if (object.reason === 'update') {
chrome.notifications.create(updateLogPage, {
title:
'YCLF updated to v' +
chrome.runtime.getManifest().version +
' 🚀',
message: "Click here to check out what's new!",
iconUrl: './images/min-icon128.png',
type: 'basic',
})
}
})

Also available on GitHub

  • Note that iconUrl should be the path from manifest.json to the image file, not from the background script.
  • You can use chrome.runtime.getManifest().version it to get the version of the extension.
  • If you want to send notifications from anywhere else than the background script, you must have a communication module between the notification sender and the background script to pass the notification details. Create a notification at background.js with that given detail. Sending notifications directly from content.js seems restricted. Check this post for more information.

Generally, you would need an event listener for each notification. However, there is a neat way to reduce duplicate codes.

chrome.notifications.onClicked.addListener(function (notificationId) {
chrome.tabs.create({ url: notificationId });
});

The trick is to store the link in notificationId field and attach an event listener to the notifications. This way, you can only use one event listener to open multiple types of links.

Additional Readings

Note: Added June 19, 2020

It doesn't seem that this is the ultimate answer. While the notification opens up the intended page when the user clicks the notification right after it pops up, the notification does not open up the page on click if the notification is sent to the notification center. This post will be updated if I find a better solution.

📜Heads up!
  • I wrote this post more than 2 years ago.
  • That's enough time for things to change.
  • Possibly, I may not endorse the content anymore.
Google Latest Articles Instead
📜Heads up!
  • I wrote this post more than 2 years ago.
  • That's enough time for things to change.
  • Possibly, I may not endorse the content anymore.
Google Latest Articles Instead

counting...

If your Ghost CMS blog using Handlebars theme shows published dates in relative time (like Published 11 months ago), you will find a handlebars code like this in your theme file.

<time datetime='{{date format='YYYY-MM-DD'}}'>
{{date published_at timeago='true'}}
</time>

Show exact date

The date published_at timeago="true" is responsible for the relative time. Change it to this.

<time datetime='{{date format='YYYY-MM-DD'}}'>
{{date published_at format='MMMM DD, YYYY'}}
</time>

This will give something like September 7, 2000.

Show exact time

You can use [moment.js](https://momentjs.com/) syntax for fine-tuning the details.

<!-- 2000 September 07 9:00:00 PM -->
<time datetime='{{date format='YYYY-MM-DD hh:mm:ss A'}}'>
{{date published_at format='YYYY MMMM DD hh:mm:ss A'}}
</time>

<!-- 2000 09 07 9:00 PM -->
<time datetime='{{date format='YYYY-MM-DD hh:mm A'}}'>
{{date published_at format='YYYY MM DD hh:mm A'}}
</time>

<!-- 2000 09 07 21:00 -->
<time datetime='{{date format='YYYY-MM-DD hh:mm'}}'>
{{date published_at format='YYYY MM DD hh:mm'}}
</time>

For months, use MM for short notations (like 09) and MMMM for more extended notations (like September.) The basic syntax is for hours, minutes, seconds, and AM/PM if you want to display time. For example, I am using the following.

<time datetime='{{date format='YYYY-MM-DD h:mm A'}}'>
{{date published_at format='YYYY/MM/DD h:mm A'}}
</time>

Further readings

📜Heads up!
  • I wrote this post more than 2 years ago.
  • That's enough time for things to change.
  • Possibly, I may not endorse the content anymore.
Google Latest Articles Instead
📜Heads up!
  • I wrote this post more than 2 years ago.
  • That's enough time for things to change.
  • Possibly, I may not endorse the content anymore.
Google Latest Articles Instead

counting...
import os
import math


def getFileSize(path, kilo=1024, readable=False, shortRead=False):
size = 0
sizeArr = []
units = ["B", "KB", "MB", "GB", "TB", "PB", "EB"]
if os.path.isdir(path):
for dirpath, dirnames, filenames in os.walk(path):
for i in filenames:
size += os.path.getsize(os.path.join(dirpath, i))
elif os.path.isfile(path):
size += os.path.getsize(path)
unit = math.floor(math.log(size, kilo))
for k in range(0, unit + 1):
sizeArr.append(
math.floor((size % kilo ** (k + 1)) / kilo ** k)
)

if readable:
sizeString = ""
if not shortRead:
for x in range(unit, -1, -1):
sizeString += str(sizeArr[x]) + units[x] + " "
return sizeString[:-1]
else:
return (
str(sizeArr[-1])
+ "."
+ str(math.floor(sizeArr[-2] / 1.024))
+ units[len(sizeArr) - 1]
)
else:
return sizeArr

Examples

Reference

  • C:\Users\anacl\OneDrive\Documents (Folder): 3.13GB (3,366,343,239 Bytes)
  • C:\Users\anacl\OneDrive\Pictures (Folder): 83.4MB (87,468,781 Bytes)
  • C:\Users\anacl\OneDrive\Pictures\screenshot.png (File): 139KB (143,262 Bytes)

Default

print(getFileSize("C:\\Users\\anacl\\OneDrive\\Documents"))
print(getFileSize("C:\\Users\\anacl\\OneDrive\\Pictures"))
print(
getFileSize(
"C:\\Users\\anacl\\OneDrive\\Pictures\\screenshot.png"
)
)

# Expected Output
# [583, 404, 138, 3]
# [749, 426, 83]
# [926, 139]

Each element in the returned list is the value of [B, KB, MB, GB, ...] of the file size.

Full Readable Output

print(
getFileSize(
"C:\\Users\\anacl\\OneDrive\\Documents", readable=True
)
)
print(
getFileSize("C:\\Users\\anacl\\OneDrive\\Pictures", readable=True)
)
print(
getFileSize(
"C:\\Users\\anacl\\OneDrive\\Pictures\\screenshot.png",
readable=True,
)
)
# Expected Output
# 3GB 138MB 404KB 583B
# 83MB 426KB 749B
# 139KB 926B

Short Readable Output

print(
getFileSize(
"C:\\Users\\anacl\\OneDrive\\Documents",
readable=True,
shortRead=True,
)
)
print(
getFileSize(
"C:\\Users\\anacl\\OneDrive\\Pictures",
readable=True,
shortRead=True,
)
)
print(
getFileSize(
"C:\\Users\\anacl\\OneDrive\\Pictures\\screenshot.png",
readable=True,
shortRead=True,
)
)
# Expected Output
# 3.134GB
# 83.416MB
# 139.904KB
📜Heads up!
  • I wrote this post more than 2 years ago.
  • That's enough time for things to change.
  • Possibly, I may not endorse the content anymore.
Google Latest Articles Instead
📜Heads up!
  • I wrote this post more than 2 years ago.
  • That's enough time for things to change.
  • Possibly, I may not endorse the content anymore.
Google Latest Articles Instead

counting...

While Chrome and Firefox are two very different browsers, Chrome Extension and Firefox Add-on are now more similar than ever. Therefore, it is possible to transplant a Chrome extension to a Firefox Add-on and publish it to the Mozilla store with minor changes. This post is how I transplanted my YouTube Comment Language Filter to Firefox.

Checking the Chrome incompatibilities

First of all, Firefox can run commands with chrome namespace, such as chrome.tabs.onUpdated. However, there are still a few codes that Firefox cannot run. Firefox offers a handy website to check the chrome incompatibilities.

  1. On your Chrome browser (or on any equivalent Chromium browsers,) visit chrome://extensions.
  2. Enable Developer Mode and Press Pack Extension.
  3. Select your extension directory and pack your extension. That will create a .crx file.
  4. Visit the Firefox Extension Test website and upload your .crx file.
  5. If it says there is no problem, then you are fine.

If there is any problem, I advise you to visit the MDN docs and see what code caused the problem. I didn't have any problem, so I cannot share any experience here.

Adding Firefox Manifest ID

Firefox also requires an ID inside the manifest.json file. It is like the following.

"browser_specific_settings": {
"gecko": {
"id": "addon@example.com",
"strict_min_version": "42.0"
}
},

As you can see, you can also add a strict_min_version here. See original MDN docs.

This was a minor hassle since Chrome could not recognize the above code block. So you need to keep two manifest.json, one with the above code block (for Firefox) and one without it (for Chrome). If I find a more straightforward way, I will add it here.

Uploading it to the Firefox Add-on Store

  1. Visit https://addons.mozilla.org/.
  2. Log in to your developer account (or create a developer account).
  3. Visit Firefox Submit a New Add-on page.
  4. Follow the guidelines on the screen.

One little tip: make sure you don't include any unnecessary files .DS_Store or anything like that. Using macOS's default Finder compressor will sometimes have these files. I recommend using Keka.

Update

  • It seems that you don't necessarily need a Firefox manifest ID. Therefore – submit the Chrome version, and 99% will work (If you didn't get any warning on the Firefox Extension Test website).
📜Heads up!
  • I wrote this post more than 2 years ago.
  • That's enough time for things to change.
  • Possibly, I may not endorse the content anymore.
Google Latest Articles Instead
📜Heads up!
  • I wrote this post more than 2 years ago.
  • That's enough time for things to change.
  • Possibly, I may not endorse the content anymore.
Google Latest Articles Instead

counting...

I recently found this:

This is just a thought. But it might be nice to have some sort
of easter egg message in here for the hard core Apple fans that
will stop the video.

01010011 01101111 00100000 01111001 01101111 01110101
00100000 01110100 01101111 01101111 01101011 00100000
01110100 01101000 01100101 00100000 01110100 01101001
01101101 01100101 00100000 01110100 01101111 00100000
01110100 01110010 01100001 01101110 01110011 01101100
01100001 01110100 01100101 00100000 01110100 01101000
01101001 01110011 00111111 00100000

01010111 01100101 00100000 01101100 01101111 01110110
01100101 00100000 01111001 01101111 01110101 00101110

So I made a short script.

egg = '''
01010011 01101111 00100000 01111001 01101111 01110101
00100000 01110100 01101111 01101111 01101011 00100000
01110100 01101000 01100101 00100000 01110100 01101001
01101101 01100101 00100000 01110100 01101111 00100000
01110100 01110010 01100001 01101110 01110011 01101100
01100001 01110100 01100101 00100000 01110100 01101000
01101001 01110011 00111111 00100000

01010111 01100101 00100000 01101100 01101111 01110110
01100101 00100000 01111001 01101111 01110101 00101110
'''.split()

for e in egg:
print(chr(int(e,2)), end="")
print()

It said...

So you took the time to translate this? We love you.

📜Heads up!
  • I wrote this post more than 2 years ago.
  • That's enough time for things to change.
  • Possibly, I may not endorse the content anymore.
Google Latest Articles Instead