Skip to content

Instantly share code, notes, and snippets.

@myersjustinc
Created July 11, 2021 05:11
Show Gist options
  • Save myersjustinc/9a14a41be014104e2414258a53069d53 to your computer and use it in GitHub Desktop.
Save myersjustinc/9a14a41be014104e2414258a53069d53 to your computer and use it in GitHub Desktop.
Rename Twemoji files to be human-readable
#!/usr/bin/env python
from operator import itemgetter
import os
import shutil
import sys
import requests
EMOJI_DATA_URL = (
'https://raw.githubusercontent.com/iamcal/emoji-data/master/'
'emoji_pretty.json')
def get_emoji_data():
r = requests.get(EMOJI_DATA_URL)
return r.json()
def build_skin_tones_lookup(emoji_data):
skin_tones = tuple(filter(
lambda x: 'FITZPATRICK' in x['name'], emoji_data))
return dict(map(itemgetter('unified', 'short_name'), skin_tones))
def build_filename(emoji_meta, skin_tones=None):
if skin_tones is None:
return os.path.join(emoji_meta['category'], (
f'{str(emoji_meta["sheet_x"]).rjust(2, "0")}-'
f'{str(emoji_meta["sheet_y"]).rjust(2, "0")}--'
f'{emoji_meta["short_name"]}--{emoji_meta["image"]}'))
return os.path.join(emoji_meta['category'], (
f'{str(emoji_meta["sheet_x"]).rjust(2, "0")}-'
f'{str(emoji_meta["sheet_y"]).rjust(2, "0")}--'
f'{emoji_meta["short_name"]}--{"--".join(skin_tones)}--'
f'{emoji_meta["image"]}'))
def move_image(source_filename, target_filename, image_root):
if source_filename.startswith('00'):
source_filename = source_filename.strip('0').replace('-fe0f', '')
source_path = os.path.join(image_root, source_filename)
target_path = os.path.join(image_root, target_filename)
category_dir = os.path.dirname(target_path)
os.makedirs(category_dir, exist_ok=True)
try:
shutil.move(source_path, target_path)
print(f'{source_filename} -> {target_filename}')
except FileNotFoundError:
if not source_filename.endswith('-fe0f.png'):
sys.stderr.write(f'Not found: {source_filename}\n')
return
source_filename = source_filename.replace('-fe0f.png', '.png')
source_path = os.path.join(image_root, source_filename)
try:
shutil.move(source_path, target_path)
print(f'{source_filename} -> {target_filename}')
except FileNotFoundError:
sys.stderr.write(f'Not found: {source_filename}\n')
def handle_character(emoji_meta, image_root, skin_tones_lookup):
move_image(emoji_meta['image'], build_filename(emoji_meta), image_root)
if 'skin_variations' not in emoji_meta:
return
for tones_raw, variation_meta in emoji_meta['skin_variations'].items():
if not variation_meta['has_img_twitter']:
continue
skin_tones = tones_raw.split('-')
skin_tone_names = tuple(map(skin_tones_lookup.get, skin_tones))
variation_combined = emoji_meta.copy()
variation_combined.update(variation_meta)
move_image(
variation_combined['image'],
build_filename(variation_combined, skin_tone_names),
image_root)
def handle_regional_indicators(image_root):
for letter_index, codepoint in enumerate(range(0x1F1E6, 0x1F1FF + 1)):
codepoint_hex = hex(codepoint)[2:]
letter_value = chr(ord('a') + letter_index)
source_filename = f'{codepoint_hex}.png'
target_filename = os.path.join('Regional Indicators', (
f'{str(letter_index + 1).rjust(2, "0")}--'
f'regional_indicator_{letter_value}--{source_filename}'))
move_image(source_filename, target_filename, image_root)
def main(image_root):
emoji_data = get_emoji_data()
skin_tones_lookup = build_skin_tones_lookup(emoji_data)
twemoji_data = tuple(filter(itemgetter('has_img_twitter'), emoji_data))
for emoji_meta in twemoji_data:
handle_character(emoji_meta, image_root, skin_tones_lookup)
handle_regional_indicators(image_root)
if __name__ == '__main__':
try:
main(sys.argv[1])
except IndexError:
sys.stderr.write(f'Usage: {sys.argv[0]} image_root\n')
sys.exit(1)
@myersjustinc
Copy link
Author

Licensed ISC:

Copyright 2021 Justin Myers

Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment