implemented image cache, fixes #1
This commit is contained in:
parent
9cc6180845
commit
2ac21a4ca3
@ -23,11 +23,21 @@ signal cmd_invalid_argcount(cmd_name, sender_data, cmd_data, arg_ary)
|
|||||||
signal cmd_no_permission(cmd_name, sender_data, cmd_data, arg_ary)
|
signal cmd_no_permission(cmd_name, sender_data, cmd_data, arg_ary)
|
||||||
# Twitch's ping is about to be answered with a pong.
|
# Twitch's ping is about to be answered with a pong.
|
||||||
signal pong
|
signal pong
|
||||||
|
# Emote has been downloaded
|
||||||
|
signal emote_downloaded(emote_id)
|
||||||
|
# Badge has been downloaded
|
||||||
|
signal badge_downloaded(badge_name)
|
||||||
|
|
||||||
# Messages starting with one of these symbols are handled. '/' will be ignored, reserved by Twitch.
|
# Messages starting with one of these symbols are handled. '/' will be ignored, reserved by Twitch.
|
||||||
export(PoolStringArray) var command_prefixes : Array = ["!"]
|
export(Array, String) var command_prefixes : Array = ["!"]
|
||||||
# Time to wait after each sent chat message. Values below ~0.31 will lead to a disconnect after 100 messages.
|
# Time to wait after each sent chat message. Values below ~0.31 might lead to a disconnect after 100 messages.
|
||||||
export(float) var chat_timeout = 0.32
|
export(float) var chat_timeout = 0.32
|
||||||
|
export(bool) var get_images : bool = false
|
||||||
|
# If true, caches emotes/badges to disk, so that they don't have to be redownloaded on every restart.
|
||||||
|
# This however means that they might not be updated if they change until you clear the cache.
|
||||||
|
export(bool) var disk_cache : bool = false
|
||||||
|
# Disk Cache has to be enbaled for this to work
|
||||||
|
export(String, FILE) var disk_cache_path = "user://gift/cache"
|
||||||
|
|
||||||
var websocket : WebSocketClient = WebSocketClient.new()
|
var websocket : WebSocketClient = WebSocketClient.new()
|
||||||
var user_regex = RegEx.new()
|
var user_regex = RegEx.new()
|
||||||
@ -38,6 +48,7 @@ onready var chat_accu = chat_timeout
|
|||||||
# Mapping of channels to their channel info, like available badges.
|
# Mapping of channels to their channel info, like available badges.
|
||||||
var channels : Dictionary = {}
|
var channels : Dictionary = {}
|
||||||
var commands : Dictionary = {}
|
var commands : Dictionary = {}
|
||||||
|
var image_cache : ImageCache
|
||||||
|
|
||||||
# Required permission to execute the command
|
# Required permission to execute the command
|
||||||
enum PermissionFlag {
|
enum PermissionFlag {
|
||||||
@ -68,6 +79,9 @@ func _ready() -> void:
|
|||||||
websocket.connect("connection_closed", self, "connection_closed")
|
websocket.connect("connection_closed", self, "connection_closed")
|
||||||
websocket.connect("server_close_request", self, "sever_close_request")
|
websocket.connect("server_close_request", self, "sever_close_request")
|
||||||
websocket.connect("connection_error", self, "connection_error")
|
websocket.connect("connection_error", self, "connection_error")
|
||||||
|
if(get_images):
|
||||||
|
image_cache = ImageCache.new(disk_cache, disk_cache_path)
|
||||||
|
add_child(image_cache)
|
||||||
|
|
||||||
func connect_to_twitch() -> void:
|
func connect_to_twitch() -> void:
|
||||||
if(websocket.connect_to_url("wss://irc-ws.chat.twitch.tv:443") != OK):
|
if(websocket.connect_to_url("wss://irc-ws.chat.twitch.tv:443") != OK):
|
||||||
@ -180,7 +194,13 @@ func handle_message(message : String, tags : Dictionary) -> void:
|
|||||||
var sender_data : SenderData = SenderData.new(user_regex.search(msg[0]).get_string(), msg[2], tags)
|
var sender_data : SenderData = SenderData.new(user_regex.search(msg[0]).get_string(), msg[2], tags)
|
||||||
handle_command(sender_data, msg)
|
handle_command(sender_data, msg)
|
||||||
emit_signal("chat_message", sender_data, msg[3].right(1))
|
emit_signal("chat_message", sender_data, msg[3].right(1))
|
||||||
print("TAGS: " + str(tags))
|
if(get_images):
|
||||||
|
if(!image_cache.badge_map.has(tags["room-id"])):
|
||||||
|
image_cache.get_badge_mappings(tags["room-id"])
|
||||||
|
for emote in tags["emotes"].split("/", false):
|
||||||
|
image_cache.get_emote(emote.split(":")[0])
|
||||||
|
for badge in tags["badges"].split(",", false):
|
||||||
|
image_cache.get_badge(badge, tags["room-id"])
|
||||||
"WHISPER":
|
"WHISPER":
|
||||||
var sender_data : SenderData = SenderData.new(user_regex.search(msg[0]).get_string(), msg[2], tags)
|
var sender_data : SenderData = SenderData.new(user_regex.search(msg[0]).get_string(), msg[2], tags)
|
||||||
handle_command(sender_data, msg, true)
|
handle_command(sender_data, msg, true)
|
||||||
@ -195,6 +215,10 @@ func handle_command(sender_data : SenderData, msg : PoolStringArray, whisper : b
|
|||||||
var command : String = msg[3].right(2)
|
var command : String = msg[3].right(2)
|
||||||
var cmd_data : CommandData = commands.get(command)
|
var cmd_data : CommandData = commands.get(command)
|
||||||
if(cmd_data):
|
if(cmd_data):
|
||||||
|
if(whisper == true && cmd_data.where & WhereFlag.WHISPER != WhereFlag.WHISPER):
|
||||||
|
return
|
||||||
|
elif(whisper == false && cmd_data.where & WhereFlag.CHAT != WhereFlag.CHAT):
|
||||||
|
return
|
||||||
var args = "" if msg.size() < 5 else msg[4]
|
var args = "" if msg.size() < 5 else msg[4]
|
||||||
var arg_ary : PoolStringArray = PoolStringArray() if args == "" else args.split(" ")
|
var arg_ary : PoolStringArray = PoolStringArray() if args == "" else args.split(" ")
|
||||||
if(arg_ary.size() > cmd_data.max_args && cmd_data.max_args != -1 || arg_ary.size() < cmd_data.min_args):
|
if(arg_ary.size() > cmd_data.max_args && cmd_data.max_args != -1 || arg_ary.size() < cmd_data.min_args):
|
||||||
@ -264,9 +288,3 @@ func connection_error() -> void:
|
|||||||
|
|
||||||
func server_close_request(code : int, reason : String) -> void:
|
func server_close_request(code : int, reason : String) -> void:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
func _enter_tree() -> void:
|
|
||||||
pass
|
|
||||||
|
|
||||||
func _exit_tree() -> void:
|
|
||||||
pass
|
|
@ -3,5 +3,5 @@
|
|||||||
name="Godot IRC For Twitch"
|
name="Godot IRC For Twitch"
|
||||||
description="Godot websocket implementation for Twitch IRC."
|
description="Godot websocket implementation for Twitch IRC."
|
||||||
author="MennoMax"
|
author="MennoMax"
|
||||||
version="0.1.0"
|
version="0.1.1"
|
||||||
script="gift.gd"
|
script="gift.gd"
|
||||||
|
111
addons/gift/util/image_cache.gd
Normal file
111
addons/gift/util/image_cache.gd
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
extends Node
|
||||||
|
class_name ImageCache
|
||||||
|
|
||||||
|
signal badge_mapping_available
|
||||||
|
|
||||||
|
var cached_images : Dictionary = {"emotes": {}, "badges": {}}
|
||||||
|
var cache_mutex = Mutex.new()
|
||||||
|
var badge_map : Dictionary = {}
|
||||||
|
var badge_mutex = Mutex.new()
|
||||||
|
var dl_queue : PoolStringArray = []
|
||||||
|
var disk_cache : bool
|
||||||
|
var disk_cache_path : String
|
||||||
|
|
||||||
|
var file : File = File.new()
|
||||||
|
var dir : Directory = Directory.new()
|
||||||
|
|
||||||
|
func _init(do_disk_cache : bool, cache_path : String) -> void:
|
||||||
|
disk_cache = do_disk_cache
|
||||||
|
disk_cache_path = cache_path
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
if(disk_cache):
|
||||||
|
for cache_dir in cached_images.keys():
|
||||||
|
cached_images[cache_dir] = {}
|
||||||
|
dir.make_dir_recursive(disk_cache_path + "/" + cache_dir)
|
||||||
|
dir.open(disk_cache_path + "/" + cache_dir)
|
||||||
|
dir.list_dir_begin(true)
|
||||||
|
var current = dir.get_next()
|
||||||
|
while current != "":
|
||||||
|
if(!dir.current_is_dir()):
|
||||||
|
file.open(dir.get_current_dir() + "/" + current, File.READ)
|
||||||
|
var img : Image = Image.new()
|
||||||
|
img.load_png_from_buffer(file.get_buffer(file.get_len()))
|
||||||
|
file.close()
|
||||||
|
var img_texture : ImageTexture = ImageTexture.new()
|
||||||
|
img_texture.create_from_image(img, 0)
|
||||||
|
cache_mutex.lock()
|
||||||
|
cached_images[cache_dir][current.get_basename()] = img_texture
|
||||||
|
cache_mutex.unlock()
|
||||||
|
current = dir.get_next()
|
||||||
|
dir.open(disk_cache_path)
|
||||||
|
dir.list_dir_begin(true)
|
||||||
|
var current = dir.get_next()
|
||||||
|
while current != "":
|
||||||
|
if(!dir.current_is_dir()):
|
||||||
|
file.open(disk_cache_path + "/" + current, File.READ)
|
||||||
|
badge_map[current.get_basename()] = parse_json(file.get_as_text())["badge_sets"]
|
||||||
|
file.close()
|
||||||
|
current = dir.get_next()
|
||||||
|
get_badge_mappings()
|
||||||
|
yield(self, "badge_mapping_available")
|
||||||
|
|
||||||
|
func create_request(url : String, resource : String, res_type : String) -> void:
|
||||||
|
var http_request = HTTPRequest.new()
|
||||||
|
http_request.connect("request_completed", self, "downloaded", [http_request, resource, res_type], CONNECT_ONESHOT)
|
||||||
|
add_child(http_request)
|
||||||
|
http_request.download_file = disk_cache_path + "/" + res_type + "/" + resource + ".png"
|
||||||
|
http_request.request(url, [], false, HTTPClient.METHOD_GET)
|
||||||
|
|
||||||
|
# Gets badge mappings for the specified channel. Empty String will get the mappings for global badges instead.
|
||||||
|
func get_badge_mappings(channel_id : String = "") -> void:
|
||||||
|
var url : String
|
||||||
|
if(channel_id == ""):
|
||||||
|
channel_id = "_global"
|
||||||
|
url = "https://badges.twitch.tv/v1/badges/global/display"
|
||||||
|
else:
|
||||||
|
url = "https://badges.twitch.tv/v1/badges/channels/" + channel_id + "/display"
|
||||||
|
if(!badge_map.has(channel_id)):
|
||||||
|
var http_request = HTTPRequest.new()
|
||||||
|
add_child(http_request)
|
||||||
|
http_request.request(url, [], false, HTTPClient.METHOD_GET)
|
||||||
|
http_request.connect("request_completed", self, "badge_mapping_received", [http_request, channel_id], CONNECT_ONESHOT)
|
||||||
|
else:
|
||||||
|
emit_signal("badge_mapping_available")
|
||||||
|
|
||||||
|
func get_emote(id : String) -> ImageTexture:
|
||||||
|
cache_mutex.lock()
|
||||||
|
if(cached_images["emotes"].has(id)):
|
||||||
|
return cached_images["emotes"][id]
|
||||||
|
else:
|
||||||
|
create_request("http://static-cdn.jtvnw.net/emoticons/v1/" + id + "/1.0", id, "emotes")
|
||||||
|
cache_mutex.unlock()
|
||||||
|
return null
|
||||||
|
|
||||||
|
func get_badge(badge_name : String, channel_id : String = "") -> ImageTexture:
|
||||||
|
cache_mutex.lock()
|
||||||
|
var badge_data : PoolStringArray = badge_name.split("/")
|
||||||
|
if(cached_images["badges"].has(badge_data[0])):
|
||||||
|
return cached_images["badges"][badge_data[0]]
|
||||||
|
var channel : String
|
||||||
|
if(!badge_map[channel_id].has(badge_data[0])):
|
||||||
|
channel_id = "_global"
|
||||||
|
create_request(badge_map[channel_id][badge_data[0]]["versions"][badge_data[1]]["image_url_1x"], badge_data[0], "badges")
|
||||||
|
cache_mutex.unlock()
|
||||||
|
return null
|
||||||
|
|
||||||
|
func downloaded(result : int, response_code : int, headers : PoolStringArray, body : PoolByteArray, request : HTTPRequest, id : String, type : String) -> void:
|
||||||
|
if(type == "emotes"):
|
||||||
|
get_parent().emit_signal("emote_downloaded", id)
|
||||||
|
elif(type == "badges"):
|
||||||
|
get_parent().emit_signal("badge_downloaded", id)
|
||||||
|
request.queue_free()
|
||||||
|
|
||||||
|
func badge_mapping_received(result : int, response_copde : int, headers : PoolStringArray, body : PoolByteArray, request : HTTPRequest, id : String) -> void:
|
||||||
|
badge_map[id] = parse_json(body.get_string_from_utf8())["badge_sets"]
|
||||||
|
if(disk_cache):
|
||||||
|
file.open(disk_cache_path + "/" + id + ".json", File.WRITE)
|
||||||
|
file.store_buffer(body)
|
||||||
|
file.close()
|
||||||
|
emit_signal("badge_mapping_available")
|
||||||
|
request.queue_free()
|
Loading…
x
Reference in New Issue
Block a user