|
1 |
| -# SPDX-FileCopyrightText: 2022 DJDevon3 for Adafruit Industries |
| 1 | +# SPDX-FileCopyrightText: 2024 DJDevon3 |
2 | 2 | # SPDX-License-Identifier: MIT
|
3 |
| -# Coded for Circuit Python 8.0 |
4 |
| -"""DJDevon3 Adafruit Feather ESP32-S2 YouTube_API_Example""" |
5 |
| -import gc |
6 |
| -import json |
| 3 | +# Coded for Circuit Python 8.2.x |
| 4 | +"""YouTube API Subscriber Count Example""" |
| 5 | +# pylint: disable=import-error |
| 6 | + |
7 | 7 | import os
|
8 |
| -import ssl |
9 | 8 | import time
|
10 | 9 |
|
11 |
| -import socketpool |
| 10 | +import adafruit_connection_manager |
12 | 11 | import wifi
|
13 | 12 |
|
14 | 13 | import adafruit_requests
|
15 | 14 |
|
16 |
| -# Ensure these are uncommented and in secrets.py or .env |
17 |
| -# "YT_username": "Your YouTube Username", |
18 |
| -# "YT_token" : "Your long API developer token", |
19 |
| - |
20 |
| -# Initialize WiFi Pool (There can be only 1 pool & top of script) |
21 |
| -pool = socketpool.SocketPool(wifi.radio) |
| 15 | +# Initalize Wifi, Socket Pool, Request Session |
| 16 | +pool = adafruit_connection_manager.get_radio_socketpool(wifi.radio) |
| 17 | +ssl_context = adafruit_connection_manager.get_radio_ssl_context(wifi.radio) |
| 18 | +requests = adafruit_requests.Session(pool, ssl_context) |
22 | 19 |
|
23 |
| -# Time between API refreshes |
| 20 | +# API Polling Rate |
24 | 21 | # 900 = 15 mins, 1800 = 30 mins, 3600 = 1 hour
|
25 |
| -sleep_time = 900 |
| 22 | +SLEEP_TIME = 900 |
| 23 | + |
| 24 | +# Set debug to True for full JSON response. |
| 25 | +# WARNING: Will show credentials |
| 26 | +DEBUG = False |
| 27 | + |
| 28 | +# Ensure these are uncommented and in settings.toml |
| 29 | +# YOUTUBE_USERNAME = "Your YouTube Username", |
| 30 | +# YOUTUBE_TOKEN = "Your long API developer token", |
26 | 31 |
|
27 | 32 | # Get WiFi details, ensure these are setup in settings.toml
|
28 | 33 | ssid = os.getenv("CIRCUITPY_WIFI_SSID")
|
29 | 34 | password = os.getenv("CIRCUITPY_WIFI_PASSWORD")
|
30 |
| -yt_username = os.getenv("YT_username") |
31 |
| -yt_token = os.getenv("YT_token") |
32 |
| - |
33 |
| - |
34 |
| -if sleep_time < 60: |
35 |
| - sleep_time_conversion = "seconds" |
36 |
| - sleep_int = sleep_time |
37 |
| -elif 60 <= sleep_time < 3600: |
38 |
| - sleep_int = sleep_time / 60 |
39 |
| - sleep_time_conversion = "minutes" |
40 |
| -elif 3600 <= sleep_time < 86400: |
41 |
| - sleep_int = sleep_time / 60 / 60 |
42 |
| - sleep_time_conversion = "hours" |
43 |
| -else: |
44 |
| - sleep_int = sleep_time / 60 / 60 / 24 |
45 |
| - sleep_time_conversion = "days" |
| 35 | +# Requires YouTube/Google API key |
| 36 | +# https://console.cloud.google.com/apis/dashboard |
| 37 | +YT_USERNAME = os.getenv("YOUTUBE_USERNAME") |
| 38 | +YT_TOKEN = os.getenv("YOUTUBE_TOKEN") |
| 39 | + |
| 40 | + |
| 41 | +def time_calc(input_time): |
| 42 | + """Converts seconds to minutes/hours/days""" |
| 43 | + if input_time < 60: |
| 44 | + return f"{input_time:.0f} seconds" |
| 45 | + if input_time < 3600: |
| 46 | + return f"{input_time / 60:.0f} minutes" |
| 47 | + if input_time < 86400: |
| 48 | + return f"{input_time / 60 / 60:.0f} hours" |
| 49 | + return f"{input_time / 60 / 60 / 24:.1f} days" |
| 50 | + |
46 | 51 |
|
47 | 52 | # https://youtube.googleapis.com/youtube/v3/channels?part=statistics&forUsername=[YOUR_USERNAME]&key=[YOUR_API_KEY]
|
48 |
| -YT_SOURCE = ( |
49 |
| - "https://youtube.googleapis.com/youtube/v3/channels?" |
50 |
| - + "part=statistics" |
51 |
| - + "&forUsername=" |
52 |
| - + yt_username |
| 53 | +YOUTUBE_SOURCE = ( |
| 54 | + "https://youtube.googleapis.com/youtube/v3/channels?part=statistics&forUsername=" |
| 55 | + + str(YT_USERNAME) |
53 | 56 | + "&key="
|
54 |
| - + yt_token |
| 57 | + + str(YT_TOKEN) |
55 | 58 | )
|
56 | 59 |
|
57 |
| -# Connect to Wi-Fi |
58 |
| -print("\n===============================") |
59 |
| -print("Connecting to WiFi...") |
60 |
| -requests = adafruit_requests.Session(pool, ssl.create_default_context()) |
61 |
| -while not wifi.radio.ipv4_address: |
62 |
| - try: |
63 |
| - wifi.radio.connect(ssid, password) |
64 |
| - except ConnectionError as e: |
65 |
| - print("Connection Error:", e) |
66 |
| - print("Retrying in 10 seconds") |
67 |
| - time.sleep(10) |
68 |
| - gc.collect() |
69 |
| -print("Connected!\n") |
70 |
| - |
71 | 60 | while True:
|
| 61 | + # Connect to Wi-Fi |
| 62 | + print("\nConnecting to WiFi...") |
| 63 | + while not wifi.radio.ipv4_address: |
| 64 | + try: |
| 65 | + wifi.radio.connect(ssid, password) |
| 66 | + except ConnectionError as e: |
| 67 | + print("❌ Connection Error:", e) |
| 68 | + print("Retrying in 10 seconds") |
| 69 | + print("✅ Wifi!") |
72 | 70 | try:
|
73 |
| - print("Attempting to GET YouTube Stats!") # ---------------------------------- |
74 |
| - debug_request = False # Set true to see full request |
75 |
| - if debug_request: |
76 |
| - print("Full API GET URL: ", YT_SOURCE) |
77 |
| - print("===============================") |
| 71 | + print(" | Attempting to GET YouTube JSON...") |
78 | 72 | try:
|
79 |
| - response = requests.get(YT_SOURCE).json() |
| 73 | + youtube_response = requests.get(url=YOUTUBE_SOURCE) |
| 74 | + youtube_json = youtube_response.json() |
80 | 75 | except ConnectionError as e:
|
81 | 76 | print("Connection Error:", e)
|
82 | 77 | print("Retrying in 10 seconds")
|
| 78 | + print(" | ✅ YouTube JSON!") |
| 79 | + |
| 80 | + if DEBUG: |
| 81 | + print(f" | Full API GET URL: {YOUTUBE_SOURCE}") |
| 82 | + print(f" | Full API Dump: {youtube_json}") |
83 | 83 |
|
84 |
| - # Print Full JSON to Serial |
85 |
| - debug_response = False # Set true to see full response |
86 |
| - if debug_response: |
87 |
| - dump_object = json.dumps(response) |
88 |
| - print("JSON Dump: ", dump_object) |
| 84 | + # Key:Value RESPONSES |
| 85 | + if "pageInfo" in youtube_json: |
| 86 | + totalResults = youtube_json["pageInfo"]["totalResults"] |
| 87 | + print(f" | | Matching Results: {totalResults}") |
89 | 88 |
|
90 |
| - # Print to Serial |
91 |
| - yt_debug_keys = True # Set to True to print Serial data |
92 |
| - if yt_debug_keys: |
93 |
| - print("Matching Results: ", response["pageInfo"]["totalResults"]) |
| 89 | + if "items" in youtube_json: |
| 90 | + YT_request_kind = youtube_json["items"][0]["kind"] |
| 91 | + print(f" | | Request Kind: {YT_request_kind}") |
94 | 92 |
|
95 |
| - YT_request_kind = response["items"][0]["kind"] |
96 |
| - print("Request Kind: ", YT_request_kind) |
| 93 | + YT_channel_id = youtube_json["items"][0]["id"] |
| 94 | + print(f" | | Channel ID: {YT_channel_id}") |
97 | 95 |
|
98 |
| - YT_response_kind = response["kind"] |
99 |
| - print("Response Kind: ", YT_response_kind) |
| 96 | + YT_videoCount = youtube_json["items"][0]["statistics"]["videoCount"] |
| 97 | + print(f" | | Videos: {YT_videoCount}") |
100 | 98 |
|
101 |
| - YT_channel_id = response["items"][0]["id"] |
102 |
| - print("Channel ID: ", YT_channel_id) |
| 99 | + YT_viewCount = youtube_json["items"][0]["statistics"]["viewCount"] |
| 100 | + print(f" | | Views: {YT_viewCount}") |
103 | 101 |
|
104 |
| - YT_videoCount = response["items"][0]["statistics"]["videoCount"] |
105 |
| - print("Videos: ", YT_videoCount) |
| 102 | + YT_subsCount = youtube_json["items"][0]["statistics"]["subscriberCount"] |
| 103 | + print(f" | | Subscribers: {YT_subsCount}") |
106 | 104 |
|
107 |
| - YT_viewCount = response["items"][0]["statistics"]["viewCount"] |
108 |
| - print("Views: ", YT_viewCount) |
| 105 | + if "kind" in youtube_json: |
| 106 | + YT_response_kind = youtube_json["kind"] |
| 107 | + print(f" | | Response Kind: {YT_response_kind}") |
109 | 108 |
|
110 |
| - YT_subsCount = response["items"][0]["statistics"]["subscriberCount"] |
111 |
| - print("Subscribers: ", YT_subsCount) |
112 |
| - print("Monotonic: ", time.monotonic()) |
| 109 | + youtube_response.close() |
| 110 | + print("✂️ Disconnected from YouTube API") |
113 | 111 |
|
114 | 112 | print("\nFinished!")
|
115 |
| - print("Next Update in %s %s" % (int(sleep_int), sleep_time_conversion)) |
| 113 | + print(f"Board Uptime: {time_calc(time.monotonic())}") |
| 114 | + print(f"Next Update: {time_calc(SLEEP_TIME)}") |
116 | 115 | print("===============================")
|
117 |
| - gc.collect() |
118 | 116 |
|
119 | 117 | except (ValueError, RuntimeError) as e:
|
120 |
| - print("Failed to get data, retrying\n", e) |
| 118 | + print(f"Failed to get data, retrying\n {e}") |
121 | 119 | time.sleep(60)
|
122 |
| - continue |
123 |
| - time.sleep(sleep_time) |
| 120 | + break |
| 121 | + time.sleep(SLEEP_TIME) |
0 commit comments