Skip to content

Instantly share code, notes, and snippets.

@sovetski
Created April 5, 2026 15:21
Show Gist options
  • Select an option

  • Save sovetski/f7ae3ece35902782db7c2e063872fc32 to your computer and use it in GitHub Desktop.

Select an option

Save sovetski/f7ae3ece35902782db7c2e063872fc32 to your computer and use it in GitHub Desktop.
YouTube auto thumbnail and title update depending on the current views
import os
import time
import pickle
import datetime
from PIL import Image, ImageDraw, ImageFont
from googleapiclient.discovery import build
from googleapiclient.http import MediaFileUpload
# Source and destination video IDs
SOURCE_VIDEO_ID = "XXX"
DEST_VIDEO_ID = "XXX"
# Thumbnail text position and info
X_COORD = 730
Y_COORD = 250
FONT_SIZE = 120
WAIT_MINUTES = 8 # Title updated every...
last_thumbnail_text = ""
def get_authenticated_service():
now = datetime.datetime.now()
hour = now.hour
# 12h/12h rotation
if 8 <= hour < 20:
token_file = "token_2.pickle"
label = "PROJECT #2 (DAY)"
else:
token_file = "token.pickle"
label = "PROJECT #1 (NIGHT)"
if not os.path.exists(token_file):
raise Exception(f"{token_file} missing!")
with open(token_file, "rb") as token:
creds = pickle.load(token)
if creds and creds.expired and creds.refresh_token:
from google.auth.transport.requests import Request
creds.refresh(Request())
return build("youtube", "v3", credentials=creds)
def get_thumbnail_logic(views):
"""Handles thumbnails: fixed message under 1000 views, then steps of 100 views"""
if views < 90:
return "This video will get\nless than 100 views"
elif views < 190:
return "This video will get\nless than 200 views"
elif views < 290:
return "This video will get\nless than 300 views"
elif views < 390:
return "This video will get\nless than 400 views"
elif views < 490:
return "This video will get\nless than 500 views"
elif views < 590:
return "This video will get\nless than 600 views"
elif views < 690:
return "This video will get\nless than 700 views"
elif views < 790:
return "This video will get\nless than 800 views"
elif views < 890:
return "This video will get\nless than 900 views"
elif views < 1000:
return "This video will get\nless than 1,000 views"
elif views < 10000:
# Between 1k and 10k: keep decimal
value_k = (views // 100) / 10.0
str_k = str(value_k)
return f"This video will get\n{str_k}k views"
elif views < 1000000:
# Between 10k and 999k: no decimal
value_k = views // 1000
return f"This video will get\n{value_k}k views"
else:
# Round down for millions
value_m = (views // 100000) / 10.0
str_m = str(value_m)
return f"This video will get\n{str_m}M views"
def create_thumbnail(text):
try:
img = Image.open("background.jpg")
except:
img = Image.new('RGB', (1280, 720), color=(220, 220, 220))
draw = ImageDraw.Draw(img)
try:
font = ImageFont.truetype("arialbd.ttf", FONT_SIZE)
except:
font = ImageFont.load_default()
draw.text((X_COORD, Y_COORD), text, font=font, fill="black", align="center", spacing=20)
img.save("current_thumbnail.jpg")
return "current_thumbnail.jpg"
def update_loop():
global last_thumbnail_text
while True:
try:
youtube = get_authenticated_service()
print(f"\n--- CYCLE ({datetime.datetime.now().strftime('%H:%M:%S')}) ---")
# 1. Read
source_data = youtube.videos().list(part="statistics", id=SOURCE_VIDEO_ID).execute()
views = int(source_data["items"][0]["statistics"]["viewCount"])
# 2. TITLE
formatted_views = f"{views:,}"
new_title = f"It will get {formatted_views} views to be precise"
dest_data = youtube.videos().list(part="snippet", id=DEST_VIDEO_ID).execute()
category_id = dest_data["items"][0]["snippet"]["categoryId"]
youtube.videos().update(
part="snippet",
body={"id": DEST_VIDEO_ID, "snippet": {"title": new_title, "categoryId": category_id}}
).execute()
print(f"TITLE: {new_title}")
# 3. THUMBNAIL BY STEP
thumbnail_text = get_thumbnail_logic(views)
if thumbnail_text != last_thumbnail_text:
print(f"New step: {thumbnail_text}")
thumb_path = create_thumbnail(thumbnail_text)
youtube.thumbnails().set(
videoId=DEST_VIDEO_ID,
media_body=MediaFileUpload(thumb_path)
).execute()
last_thumbnail_text = thumbnail_text
print("THUMBNAIL updated.")
else:
print("Step unchanged. Thumbnail kept.")
except Exception as e:
print(f"Error: {e}")
time.sleep(60)
continue
time.sleep(WAIT_MINUTES * 60)
if __name__ == "__main__":
update_loop()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment