From be4d18b6f61bc9ff3993e1033441e803517249a6 Mon Sep 17 00:00:00 2001 From: thorwebdev Date: Tue, 28 Oct 2025 16:23:33 -0700 Subject: [PATCH 1/4] feat: render chinese characters via cloudinary. --- badge/apps/badge/__init__.py | 73 ++++++++++++++++++++++++++++++++---- badge/secrets.py | 7 +++- 2 files changed, 72 insertions(+), 8 deletions(-) diff --git a/badge/apps/badge/__init__.py b/badge/apps/badge/__init__.py index 41215ab..86f5560 100755 --- a/badge/apps/badge/__init__.py +++ b/badge/apps/badge/__init__.py @@ -28,6 +28,7 @@ WIFI_PASSWORD = None WIFI_SSID = None +CLOUDINARY_CLOUD_NAME = "demo" wlan = None connected = False @@ -39,7 +40,7 @@ def message(text): def get_connection_details(user): - global WIFI_PASSWORD, WIFI_SSID + global WIFI_PASSWORD, WIFI_SSID, CLOUDINARY_CLOUD_NAME if WIFI_SSID is not None and user.handle is not None: return True @@ -47,6 +48,11 @@ def get_connection_details(user): try: sys.path.insert(0, "/") from secrets import WIFI_PASSWORD, WIFI_SSID, GITHUB_USERNAME + try: + from secrets import CLOUDINARY_CLOUD_NAME as cloud_name + CLOUDINARY_CLOUD_NAME = cloud_name + except ImportError: + pass # Use default "demo" sys.path.pop(0) except ImportError: WIFI_PASSWORD = None @@ -171,6 +177,20 @@ def get_user_data(user, force_update=False): user.handle = r["login"] user.followers = r["followers"] user.repos = r["public_repos"] + + # Check if name contains Chinese characters and fetch rendered image + if user.name and has_chinese_characters(user.name): + message(f"Rendering Chinese name: {user.name}") + chinese_url = generate_chinese_text_url(user.name) + if chinese_url: + try: + yield from async_fetch_to_disk(chinese_url, "/chinese_name.png", force_update) + user.chinese_name_img = Image.load("/chinese_name.png") + message("Chinese name image loaded") + except Exception as e: + message(f"Failed to load Chinese name: {e}") + user.chinese_name_img = None + del r gc.collect() @@ -201,6 +221,34 @@ def fake_number(): return random.randint(10000, 99999) +def has_chinese_characters(text): + """Check if text contains Chinese characters""" + if not text: + return False + return any('\u4e00' <= char <= '\u9fff' for char in text) + + +def generate_chinese_text_url(text, width=150, height=20, font_size=14): + """Generate Cloudinary URL for rendering Chinese text as image""" + try: + from urllib.parse import quote + text_encoded = quote(text) + + # Using Cloudinary's text overlay feature with a CJK-compatible font + # Note: This uses Arial Unicode MS which supports Chinese + # Cloud name is loaded from secrets.py (defaults to "demo") + + url = f"https://res.cloudinary.com/{CLOUDINARY_CLOUD_NAME}/image/upload/" + url += f"w_{width},h_{height},c_fit,b_rgb:000000/" + url += f"l_text:Arial%20Unicode%20MS_{font_size}:{text_encoded},co_rgb:ebf5ff/" + url += "blank.png" + + return url + except Exception as e: + print(f"Error generating Cloudinary URL: {e}") + return None + + def placeholder_if_none(text): if text: return text @@ -234,6 +282,7 @@ def update(self, force_update=False): self.contribution_data = None self.repos = None self.avatar = None + self.chinese_name_img = None self._task = None self._force_update = force_update @@ -278,6 +327,10 @@ def draw(self, connected): handle = "fetching user data..." if not self._task: self._task = get_user_data(self, self._force_update) + elif self.name and has_chinese_characters(self.name) and not self.chinese_name_img: + handle = "rendering chinese..." + if not self._task: + self._task = get_user_data(self, self._force_update) elif not self.contribs: handle = "fetching contribs..." if not self._task: @@ -302,12 +355,18 @@ def draw(self, connected): screen.brush = white screen.text(handle, 80 - (w / 2), 2) - # draw name - screen.font = small_font - screen.brush = phosphor - name = placeholder_if_none(self.name) - w, _ = screen.measure_text(name) - screen.text(name, 80 - (w / 2), 16) + # draw name (use image if Chinese characters, otherwise use text) + if self.chinese_name_img: + # Display Chinese name as image, centered + img_w = self.chinese_name_img.width + screen.blit(self.chinese_name_img, 80 - (img_w / 2), 16) + else: + # Display name as text for non-Chinese names + screen.font = small_font + screen.brush = phosphor + name = placeholder_if_none(self.name) + w, _ = screen.measure_text(name) + screen.text(name, 80 - (w / 2), 16) # draw statistics self.draw_stat("followers", self.followers, 88, 33) diff --git a/badge/secrets.py b/badge/secrets.py index a372b06..3a18569 100755 --- a/badge/secrets.py +++ b/badge/secrets.py @@ -5,4 +5,9 @@ WIFI_PASSWORD = "h4ck4w4y" # Update with your GitHub username -GITHUB_USERNAME = "" \ No newline at end of file +GITHUB_USERNAME = "" + +# Optional: Update with your Cloudinary cloud name for Chinese character support +# Sign up at https://cloudinary.com (free tier available) +# Leave as "demo" to use the public demo account +CLOUDINARY_CLOUD_NAME = "demo" \ No newline at end of file From 9ccc3e30d1eea4e92da07203861cf4a0ab2ab69a Mon Sep 17 00:00:00 2001 From: thorwebdev Date: Tue, 28 Oct 2025 16:38:59 -0700 Subject: [PATCH 2/4] migrate to shields.io --- badge/apps/badge/__init__.py | 29 +++++++++++------------------ badge/secrets.py | 7 +------ 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/badge/apps/badge/__init__.py b/badge/apps/badge/__init__.py index 86f5560..faccba2 100755 --- a/badge/apps/badge/__init__.py +++ b/badge/apps/badge/__init__.py @@ -28,7 +28,6 @@ WIFI_PASSWORD = None WIFI_SSID = None -CLOUDINARY_CLOUD_NAME = "demo" wlan = None connected = False @@ -40,7 +39,7 @@ def message(text): def get_connection_details(user): - global WIFI_PASSWORD, WIFI_SSID, CLOUDINARY_CLOUD_NAME + global WIFI_PASSWORD, WIFI_SSID if WIFI_SSID is not None and user.handle is not None: return True @@ -48,11 +47,6 @@ def get_connection_details(user): try: sys.path.insert(0, "/") from secrets import WIFI_PASSWORD, WIFI_SSID, GITHUB_USERNAME - try: - from secrets import CLOUDINARY_CLOUD_NAME as cloud_name - CLOUDINARY_CLOUD_NAME = cloud_name - except ImportError: - pass # Use default "demo" sys.path.pop(0) except ImportError: WIFI_PASSWORD = None @@ -180,7 +174,7 @@ def get_user_data(user, force_update=False): # Check if name contains Chinese characters and fetch rendered image if user.name and has_chinese_characters(user.name): - message(f"Rendering Chinese name: {user.name}") + message(f"Fetching Chinese name via shields.io: {user.name}") chinese_url = generate_chinese_text_url(user.name) if chinese_url: try: @@ -229,23 +223,22 @@ def has_chinese_characters(text): def generate_chinese_text_url(text, width=150, height=20, font_size=14): - """Generate Cloudinary URL for rendering Chinese text as image""" + """Generate shields.io URL for rendering Chinese text as image""" try: from urllib.parse import quote text_encoded = quote(text) - # Using Cloudinary's text overlay feature with a CJK-compatible font - # Note: This uses Arial Unicode MS which supports Chinese - # Cloud name is loaded from secrets.py (defaults to "demo") + # Using shields.io static badge API + # Automatically uses white text on black background for good contrast + # No account or API key needed - completely free - url = f"https://res.cloudinary.com/{CLOUDINARY_CLOUD_NAME}/image/upload/" - url += f"w_{width},h_{height},c_fit,b_rgb:000000/" - url += f"l_text:Arial%20Unicode%20MS_{font_size}:{text_encoded},co_rgb:ebf5ff/" - url += "blank.png" + url = f"https://img.shields.io/static/v1?" + url += f"label=&message={text_encoded}" + url += f"&color=000000&labelColor=000000&style=flat-square" return url except Exception as e: - print(f"Error generating Cloudinary URL: {e}") + print(f"Error generating shields.io URL: {e}") return None @@ -328,7 +321,7 @@ def draw(self, connected): if not self._task: self._task = get_user_data(self, self._force_update) elif self.name and has_chinese_characters(self.name) and not self.chinese_name_img: - handle = "rendering chinese..." + handle = "fetching chinese..." if not self._task: self._task = get_user_data(self, self._force_update) elif not self.contribs: diff --git a/badge/secrets.py b/badge/secrets.py index 3a18569..a372b06 100755 --- a/badge/secrets.py +++ b/badge/secrets.py @@ -5,9 +5,4 @@ WIFI_PASSWORD = "h4ck4w4y" # Update with your GitHub username -GITHUB_USERNAME = "" - -# Optional: Update with your Cloudinary cloud name for Chinese character support -# Sign up at https://cloudinary.com (free tier available) -# Leave as "demo" to use the public demo account -CLOUDINARY_CLOUD_NAME = "demo" \ No newline at end of file +GITHUB_USERNAME = "" \ No newline at end of file From bfe23600d9e7da0bc4b86912c9aa4bea487be794 Mon Sep 17 00:00:00 2001 From: thorwebdev Date: Tue, 28 Oct 2025 16:53:07 -0700 Subject: [PATCH 3/4] chore: use png. --- badge/apps/badge/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/badge/apps/badge/__init__.py b/badge/apps/badge/__init__.py index faccba2..aa7905f 100755 --- a/badge/apps/badge/__init__.py +++ b/badge/apps/badge/__init__.py @@ -231,8 +231,9 @@ def generate_chinese_text_url(text, width=150, height=20, font_size=14): # Using shields.io static badge API # Automatically uses white text on black background for good contrast # No account or API key needed - completely free + # Add .png extension to force PNG format instead of SVG - url = f"https://img.shields.io/static/v1?" + url = f"https://img.shields.io/static/v1.png?" url += f"label=&message={text_encoded}" url += f"&color=000000&labelColor=000000&style=flat-square" From b419301d2b288b6b70ba594aa6cd5eeac0f174c2 Mon Sep 17 00:00:00 2001 From: thorwebdev Date: Tue, 28 Oct 2025 16:59:01 -0700 Subject: [PATCH 4/4] try fix. --- badge/apps/badge/__init__.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/badge/apps/badge/__init__.py b/badge/apps/badge/__init__.py index aa7905f..d47fb4e 100755 --- a/badge/apps/badge/__init__.py +++ b/badge/apps/badge/__init__.py @@ -178,12 +178,15 @@ def get_user_data(user, force_update=False): chinese_url = generate_chinese_text_url(user.name) if chinese_url: try: + message(f"URL: {chinese_url}") yield from async_fetch_to_disk(chinese_url, "/chinese_name.png", force_update) user.chinese_name_img = Image.load("/chinese_name.png") - message("Chinese name image loaded") + message("Chinese name image loaded successfully") except Exception as e: message(f"Failed to load Chinese name: {e}") - user.chinese_name_img = None + # Set to False (not None) to indicate we tried and failed + # This prevents infinite retry loop + user.chinese_name_img = False del r gc.collect() @@ -228,12 +231,12 @@ def generate_chinese_text_url(text, width=150, height=20, font_size=14): from urllib.parse import quote text_encoded = quote(text) - # Using shields.io static badge API + # Using shields.io raster service for PNG format + # Direct URL to avoid redirect issues with MicroPython's urlopen # Automatically uses white text on black background for good contrast # No account or API key needed - completely free - # Add .png extension to force PNG format instead of SVG - url = f"https://img.shields.io/static/v1.png?" + url = f"https://raster.shields.io/static/v1.png?" url += f"label=&message={text_encoded}" url += f"&color=000000&labelColor=000000&style=flat-square" @@ -321,7 +324,9 @@ def draw(self, connected): handle = "fetching user data..." if not self._task: self._task = get_user_data(self, self._force_update) - elif self.name and has_chinese_characters(self.name) and not self.chinese_name_img: + elif self.name and has_chinese_characters(self.name) and self.chinese_name_img is None: + # Only show "fetching chinese..." if we haven't tried yet (None) + # If it's False, we already tried and failed, so skip handle = "fetching chinese..." if not self._task: self._task = get_user_data(self, self._force_update) @@ -350,12 +355,12 @@ def draw(self, connected): screen.text(handle, 80 - (w / 2), 2) # draw name (use image if Chinese characters, otherwise use text) - if self.chinese_name_img: + if self.chinese_name_img and self.chinese_name_img is not False: # Display Chinese name as image, centered img_w = self.chinese_name_img.width screen.blit(self.chinese_name_img, 80 - (img_w / 2), 16) else: - # Display name as text for non-Chinese names + # Display name as text for non-Chinese names or if image fetch failed screen.font = small_font screen.brush = phosphor name = placeholder_if_none(self.name)