import os import io import socket import json import requests import xbmc import xbmcgui from PIL import Image from pathlib import Path from requests.adapters import HTTPAdapter, Retry from . import config from . import utils from . import weather # DNS cache old_getaddrinfo = socket.getaddrinfo def new_getaddrinfo(*args): try: return config.dnscache[args] except KeyError: r = old_getaddrinfo(*args) config.dnscache[args] = r return r socket.getaddrinfo = new_getaddrinfo # Requests r = Retry(total=2, backoff_factor=1, status_forcelist=[500, 502, 503, 504]) s = requests.Session() s.headers.update(config.addon_ua) s.mount('https://', HTTPAdapter(max_retries=r)) # Network def network(): if config.neterr > 10: return False else: return True # Get url def geturl(url, head=False): # Network timeout if network(): timeout = 6 else: timeout = 1 # Download try: if head: utils.log(f'Checking: {url}', 3) r = s.head(url, timeout=timeout) else: utils.log(f'Download: {url}', 3) r = s.get(url, timeout=timeout) except Exception as e: utils.log(f'Download: {url} ({e})', 3) config.dnscache = {} config.neterr += 1 return None else: config.neterr = 0 if r.ok: utils.log(f'Download: {url} ({r.status_code})', 3) return r.content else: utils.log(f'Download: {url} ({r.status_code})', 2) config.dnscache = {} return None # Get data def getdata(type, loc, map=None): # URL if type == 'weather': url = config.map_api.get(type).format(map[0], map[1]) elif type == 'airquality': url = config.map_api.get(type).format(map[0], map[1]) elif type == 'sun': url = config.map_api.get(type).format(map[0], map[1], map[2]) elif type == 'moon': url = config.map_api.get(type).format(map[0], map[1], map[2]) # Weather file = f'{config.addon_cache}/{loc}/{type}.json' data = geturl(url) if data: with open(Path(file), 'wb') as f: f.write(data) # Get Map ( 0:loc, 1:type, 2:count, 3:z, 4:x, 5:y, 6:xtile 7:ytile, 8:path, 9:time, 10-13 bbox ) def getmap(map, head=False): if map[1] == 'osm': url = config.map_api.get(map[1]).format(map[3], map[4], map[5]) elif map[1] == 'rvradar': url = config.map_api.get(map[1]).format(map[8], map[3], map[4], map[5]) elif map[1] == 'rvsatellite': url = config.map_api.get(map[1]).format(map[8], map[3], map[4], map[5]) elif map[1] == 'gctemp': url = config.map_api.get(map[1]).format(map[10], map[11], map[12], map[13]) elif map[1] == 'gcwind': url = config.map_api.get(map[1]).format(map[10], map[11], map[12], map[13]) # HEAD if head: data = geturl(url, head=True) return data # GET data = geturl(url) if data: config.mapcache[map[1]][map[2]] = data else: with open(Path(f'{config.addon_path}/resources/tile.png'), 'rb') as f: tile = f.read() config.mapcache[map[1]][map[2]] = tile # Map merge def mapmerge(map): image = Image.new("RGBA", (756, 756), None) for item in map: try: tile = Image.open(io.BytesIO(config.mapcache[item[1]][item[2]])) except: tile = Image.open(f'{config.addon_path}/resources/tile.png') else: image.paste( tile, (item[6], item[7])) if map[0][1] == 'osm': image.save(f'{config.addon_cache}/{map[0][0]}/{map[0][1]}.png') else: image.save(f'{config.addon_cache}/{map[0][0]}/{map[0][1]}_{map[0][9]}.png') # Get rvdata def getrvindex(type): try: data = json.loads(geturl(config.map_api.get('rvindex'))) map = config.map_layers.get(type) time = data[map[0]][map[1]][-1]['time'] path = data[map[0]][map[1]][-1]['path'] except: return None, None else: return time, path # Get location (GeoIP) def getloc(locid): utils.log(f'Geolocation ...') utils.setsetting('geoip', 'true') # Get location try: data = json.loads(geturl(config.map_api.get('geoip'))) city = data['city'] region = data.get('region_name') country = data.get('country_code') # Search data = json.loads(geturl(config.map_api.get('search').format(city))) location = data['results'][0] for item in data['results']: if country and region: if country in location['country_code'] and region in location['admin1']: location = item break if country: if country in location['country_code']: location = item break except Exception as e: utils.log(f'Geolocation: Unknown ({e})') else: utils.log(f'Geolocation: {location["name"]}, {location["admin1"]}, {location["country_code"]} [{location["latitude"]}, {location["longitude"]}]') utils.setsetting(f'loc{locid}', f'{location["name"]}, {location["admin1"]}, {location["country_code"]}') utils.setsetting(f'loc{locid}lat', str(location["latitude"])) utils.setsetting(f'loc{locid}lon', str(location["longitude"])) utils.setsetting(f'loc{locid}tz', str(location["timezone"])) # Clear location def clearloc(locid, last=False): if last: utils.setsetting(f'loc{locid}data', '321318000') utils.setsetting(f'loc{locid}map', '321318000') utils.setsetting(f'loc{locid}rv', '321318000') utils.setsetting(f'loc{locid}gc', '321318000') else: utils.setsetting(f'loc{locid}', '') utils.setsetting(f'loc{locid}user', '') utils.setsetting(f'loc{locid}alert', 'true') utils.setsetting(f'loc{locid}utz', 'false') utils.setsetting(f'loc{locid}tz', '') utils.setsetting(f'loc{locid}lat', '0') utils.setsetting(f'loc{locid}lon', '0') # Set location def setloc (locid): utils.log(f'Search dialog ...') dialog = xbmcgui.Dialog() input = utils.setting(f'loc{locid}') keyboard = xbmc.Keyboard(input, utils.loc(14024), False) keyboard.doModal() if keyboard.isConfirmed(): search = keyboard.getText() # No changes if search == input: utils.log(f'[LOC{locid}] No changes') # Remove location elif search == '': check = utils.setting(f'loc{int(locid)+1}') if not check: utils.log(f'[LOC{locid}] Removed') clearloc(locid) clearloc(locid, True) # Search location else: try: locs = [] url = config.map_api.get('search').format(search) data = json.loads(geturl(url))['results'] except: utils.log('[LOC{locid}] No results') dialog.ok('Open-meteo', utils.loc(284)) else: for item in data: li = xbmcgui.ListItem(f'{item.get("name")}, {item.get("admin1")}, {item.get("country_code")} (Lat: {item.get("latitude")}, Lon: {item.get("longitude")})') locs.append(li) select = dialog.select(utils.loc(396), locs, useDetails=True) if select != -1: # Cleanup cache dir dir = f'{config.addon_cache}/{locid}' files = sorted(list(Path(dir).glob('*'))) for file in files: os.remove(file) # Set location utils.log(f'Location {locid}: {data[select].get("name")}, {data[select].get("admin1")}, {data[select].get("country_code")} {data[select].get("latitude")} {data[select].get("longitude")}') utils.setsetting(f'loc{locid}', f'{data[select].get("name")}, {data[select].get("admin1")}, {data[select].get("country_code")}') utils.setsetting(f'loc{locid}lat', data[select]["latitude"]) utils.setsetting(f'loc{locid}lon', data[select]["longitude"]) utils.setsetting(f'loc{locid}tz', data[select]["timezone"]) # Wait for settings dialog while xbmcgui.getCurrentWindowDialogId() == 10140: utils.log(f'Waiting for settings dialog ...') utils.monitor.waitForAbort(1) if utils.monitor.abortRequested(): return # Cleanup lastupdate clearloc(locid, True) # Refresh if int(utils.settingrpc("weather.currentlocation")) == int(locid): weather.Main(str(locid), mode='download') weather.Main(str(locid), mode='update') else: weather.Main(str(locid), mode='download') weather.Main(str(locid), mode='updatelocs')