|
|
|
|
|
|
|
|
|
|
|
import os, sys, time, re
|
|
from datetime import datetime as dt
|
|
from itertools import chain, filterfalse
|
|
|
|
from urllib.request import urlopen
|
|
from urllib.request import Request
|
|
import urllib.error
|
|
|
|
import numpy as np
|
|
from PIL import Image
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def check_site_redirected(starturl):
|
|
|
|
res = urllib.request.urlopen(starturl)
|
|
finalurl = res.geturl()
|
|
return finalurl
|
|
|
|
def get_links(start_html, c_regex):
|
|
|
|
html_links = []
|
|
|
|
try:
|
|
|
|
req = Request(start_html)
|
|
req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 AOL/9.8 AOLBuild/4346.2019.US Safari/537.36')
|
|
start_file = urlopen(req)
|
|
|
|
except urllib.error.HTTPError as e:
|
|
print('Download Failed for',start_html)
|
|
print('Status', e.code,' - Reason', e.reason,' - Url', e.url)
|
|
return html_links
|
|
|
|
start_text = str(start_file.read(),encoding="utf-8",errors="ignore")
|
|
start_file.close()
|
|
|
|
|
|
|
|
|
|
if start_text != "":
|
|
html_links = c_regex.findall(start_text)
|
|
|
|
return html_links
|
|
|
|
|
|
|
|
def pipe_inspector(link_input):
|
|
print("'",link_input,"',")
|
|
yield link_input
|
|
|
|
|
|
|
|
def link_from_list(link_list):
|
|
for link_input in link_list:
|
|
yield link_input
|
|
|
|
def get_test_links(link_input, c_regex):
|
|
html_links = c_regex.findall(str(link_input))
|
|
return html_links
|
|
|
|
def link_text_replace_filter(file_link):
|
|
return [file_link.replace("vids","highres")]
|
|
|
|
def link_prefix_adder(file_link):
|
|
return [prefix_adder+file_link]
|
|
|
|
|
|
def file_name_filter(file_link,c_regex):
|
|
file_name = file_link[file_link.rfind("/")+1:]
|
|
file_name = file_name[:-4]
|
|
|
|
link_filter = c_regex.findall(str(file_name))
|
|
if link_filter == []:
|
|
return ""
|
|
else:
|
|
return [file_link]
|
|
|
|
def file_name_generator(file_link):
|
|
|
|
str_template = "%2d"
|
|
file_list = []
|
|
|
|
for c in range(1,10+1):
|
|
file_name = str_template % (c)
|
|
file_list.append(file_name)
|
|
|
|
return file_list
|
|
|
|
|
|
|
|
|
|
class link_prefix():
|
|
|
|
def __init__(self, file_prefix):
|
|
self.file_prefix = file_prefix
|
|
return
|
|
|
|
def add(self, file_link):
|
|
|
|
return [self.file_prefix + file_link]
|
|
|
|
class link_two_string():
|
|
|
|
def __init__(self, string_format, func1=None, func2=None):
|
|
self.string_format = string_format
|
|
self.func1 = func1
|
|
self.func2 = func2
|
|
return
|
|
|
|
def add(self, file_link):
|
|
|
|
if (self.func1 == None and self.func2 != None):
|
|
return [self.string_format % (file_link,self.func2(file_link))]
|
|
elif (self.func1!=None and self.func2==None):
|
|
return [self.string_format % (self.func1(file_link),file_link)]
|
|
elif (self.func1!=None and self.func2!=None):
|
|
return [self.string_format % (self.func1(file_link),self.func2(file_link))]
|
|
else:
|
|
return [self.string_format % (file_link,file_link)]
|
|
|
|
class link_generator():
|
|
|
|
def __init__(self, file_ext,header,start,end):
|
|
self.file_ext = file_ext
|
|
self.header = header
|
|
self.start = start
|
|
self.end = end
|
|
self.filelist = []
|
|
return
|
|
|
|
def add(self, file_link):
|
|
self.filelist = []
|
|
for c in range(self.start,self.end+1):
|
|
|
|
filename = file_link + self.header % (c) + self.file_ext
|
|
self.filelist.append(filename)
|
|
return self.filelist
|
|
|
|
|
|
|
|
def html_cal_iter(html_str,start_year,end_year,end_month):
|
|
for tgt_year in range(start_year, end_year+1):
|
|
for tgt_month in range(1,12+1):
|
|
if tgt_year < end_year or (tgt_year == end_year and tgt_month <= end_month):
|
|
yield html_str % (tgt_year,tgt_month)
|
|
|
|
def html_num_iter(html_str,start_page,end_page):
|
|
for tgt_page in range(start_page, end_page+1):
|
|
yield html_str % (tgt_page)
|
|
|
|
def html_list_iter(iter_list):
|
|
for tgt_page in iter_list:
|
|
yield tgt_page
|
|
|
|
def html_alpha_iter(html_str,start_letter=0,end_letter=26,use_upper=True):
|
|
|
|
alpha = "abcdefghijklmnopqrstuvwxyz"
|
|
alpha = alpha[start_letter:end_letter]
|
|
|
|
if use_upper:
|
|
alpha = alpha.upper()
|
|
|
|
for tgt_page in alpha:
|
|
yield html_str % (tgt_page)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class cDownloader:
|
|
|
|
def __init__(self, file_namer, file_extension, file_prefix="", file_start_counter=0, pause=0.2, dead_link_kill=3, snap_page=False):
|
|
|
|
|
|
|
|
self.file_namer = file_namer
|
|
self.file_ext = file_extension
|
|
self.prefix = file_prefix
|
|
|
|
self.file_count = file_start_counter
|
|
self.pause = pause
|
|
|
|
self.dead_link_kill = dead_link_kill
|
|
self.dead_links = 0
|
|
self.last_dead_link = ""
|
|
self.snap_page = snap_page
|
|
|
|
if self.snap_page:
|
|
|
|
options = webdriver.ChromeOptions()
|
|
options.add_argument("headless")
|
|
options.add_argument("window-size=2500,1200")
|
|
|
|
self.browser = webdriver.Chrome(chrome_options=options)
|
|
|
|
def __del__(self):
|
|
|
|
if self.snap_page:
|
|
self.browser.quit()
|
|
|
|
def download(self, file_link):
|
|
|
|
if file_link is None:
|
|
pass
|
|
|
|
else:
|
|
|
|
self.file_count += 1
|
|
add_prefix = ""
|
|
|
|
if self.file_namer == "FILENAME":
|
|
|
|
trim_file_link = file_link[:file_link.rfind(self.file_ext)+len(self.file_ext)]
|
|
file_name = self.prefix + add_prefix + trim_file_link[trim_file_link.rfind("/")+1:]
|
|
|
|
else:
|
|
file_name = (self.prefix + add_prefix + "%06d" + self.file_ext) % self.file_count
|
|
|
|
|
|
|
|
if (self.dead_links >= self.dead_link_kill and self.last_dead_link == file_link[:file_link.rfind("/")]):
|
|
|
|
|
|
pass
|
|
|
|
elif os.path.exists(file_name):
|
|
print("WARNING: File exists ",file_name," skipping")
|
|
pass
|
|
|
|
|
|
|
|
|
|
if self.snap_page:
|
|
|
|
self.browser.get(file_link)
|
|
self.browser.save_screenshot(file_name+".png")
|
|
time.sleep(self.pause)
|
|
|
|
else:
|
|
|
|
try:
|
|
req = Request(file_link)
|
|
req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 AOL/9.8 AOLBuild/4346.2019.US Safari/537.36')
|
|
remotefile = urlopen(req)
|
|
|
|
localfile = open(file_name, 'wb')
|
|
localfile.write(remotefile.read())
|
|
localfile.close()
|
|
remotefile.close()
|
|
|
|
|
|
|
|
|
|
self.dead_links = 0
|
|
self.last_dead_link = ""
|
|
|
|
except urllib.error.HTTPError as e:
|
|
|
|
print('ERROR: ',e.code,' REASON: ',e.reason, " => ",file_link)
|
|
|
|
self.dead_links += 1
|
|
self.last_dead_link = file_link[:file_link.rfind("/")]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class cDataPipe:
|
|
|
|
def __init__(self):
|
|
|
|
self.nLayers = 0
|
|
self.DataPipes = []
|
|
|
|
def new_pipe(self, data_pump):
|
|
self.DataPipes.append([data_input for data_input in data_pump])
|
|
|
|
def end_pipe(self, data_downloader):
|
|
self.nLayers += 1
|
|
self.DataPipes.append([data_downloader(data_input) for data_input in self.DataPipes[self.nLayers-1]])
|
|
|
|
def add_pipe(self, data_processor, c_regex=""):
|
|
|
|
self.nLayers += 1
|
|
|
|
if c_regex == "":
|
|
self.DataPipes.append(self.unique_items(self.flatten([data_processor(data_input) for data_input in self.DataPipes[self.nLayers-1]])))
|
|
else:
|
|
self.DataPipes.append(self.unique_items(self.flatten([data_processor(data_input,c_regex) for data_input in self.DataPipes[self.nLayers-1]])))
|
|
|
|
def output(self):
|
|
return list(self.DataPipes[self.nLayers])
|
|
|
|
def flow(self):
|
|
yield self.DataPipes[self.nLayers]
|
|
|
|
def flatten(self, list_of_lists):
|
|
|
|
flat_list = []
|
|
|
|
try:
|
|
flat_list = list(chain.from_iterable(list_of_lists))
|
|
except:
|
|
print("ERROR - Non-iterable in flatten chain")
|
|
return []
|
|
|
|
return flat_list
|
|
|
|
def unique_items(self, iterable):
|
|
|
|
seen = set()
|
|
|
|
for element in filterfalse(seen.__contains__,iterable):
|
|
seen.add(element)
|
|
yield element
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
'''
|
|
|
|
Floyd-Steinberg Dithering Example
|
|
https://scipython.com/blog/floyd-steinberg-dithering/
|
|
|
|
Also see:
|
|
|
|
https://tannerhelland.com/2012/12/28/dithering-eleven-algorithms-source-code.html
|
|
https://en.wikipedia.org/wiki/Ordered_dithering
|
|
https://archive.ph/71e9G
|
|
|
|
Adjusted for ePaper colours - which are 7
|
|
|
|
Supported Colours
|
|
|
|
Brown R - 255 G - 128 B - 0
|
|
Green R - 0 G - 255 B - 0
|
|
White R - 255 G - 255 B - 255
|
|
Black R - 0 G - 0 B - 0
|
|
Red R - 255 G -0 B - 0
|
|
Blue R - 0 G - 0 B - 255
|
|
Yellow R - 255 G 255 B - 0
|
|
|
|
Not Supported (but would be normal 2 channel colours)
|
|
|
|
Purple R - 255 G 0 B - 255
|
|
Turq R - 0 G 255 B - 255
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
colour_map = [np.array([0,0,0]),
|
|
np.array([255,0,0]),
|
|
np.array([0,255,0]),
|
|
np.array([0,0,255]),
|
|
np.array([255,255,0]),
|
|
np.array([255,128,0]),
|
|
np.array([255,255,255]),
|
|
]
|
|
|
|
NUMBER_COLOURS = len(colour_map)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_new_val(old_val, nc):
|
|
"""
|
|
Get the "closest" colour to old_val in the range [0,1] per channel divided
|
|
into nc values.
|
|
|
|
"""
|
|
|
|
return np.round(old_val * (nc - 1)) / (nc - 1)
|
|
|
|
'''
|
|
|
|
Floyd-Steinberg algo dithers the image into a palette with fixed colours per channel
|
|
|
|
F = 1/16 | _ * 7 |
|
|
| 3 5 1 |
|
|
|
|
Stucki
|
|
| * 8 4 |
|
|
= 1/42 | 2 4 8 4 2 |
|
|
| 1 2 4 2 1 |
|
|
|
|
where * is the current pixel
|
|
|
|
'''
|
|
|
|
def fs_dither(img, height=448, width=600):
|
|
|
|
arr = np.array(img, dtype=int)
|
|
|
|
for ir in range(height-1):
|
|
|
|
for ic in range(width-1):
|
|
|
|
matches = colour_map - np.tile(arr[ir, ic],(NUMBER_COLOURS,1))
|
|
dist_match = np.apply_along_axis(np.linalg.norm,1,matches)
|
|
best_match = np.argmin(dist_match)
|
|
best_colour = colour_map[best_match]
|
|
diff_best = arr[ir,ic] - best_colour
|
|
dist_best = np.linalg.norm(diff_best)
|
|
|
|
|
|
|
|
arr[ir,ic] = best_colour
|
|
|
|
|
|
|
|
if dist_best > 88:
|
|
|
|
for factor,ir_d,ic_d in [(7,0,1),(3,1,-1),(5,1,0),(1,1,1)]:
|
|
|
|
arr[ir+ir_d, ic+ic_d][0] = min(arr[ir+ir_d, ic+ic_d][0] + round(diff_best[0] * factor/16,0),255)
|
|
arr[ir+ir_d, ic+ic_d][1] = min(arr[ir+ir_d, ic+ic_d][1] + round(diff_best[1] * factor/16,0),255)
|
|
arr[ir+ir_d, ic+ic_d][2] = min(arr[ir+ir_d, ic+ic_d][2] + round(diff_best[2] * factor/16,0),255)
|
|
|
|
|
|
return Image.fromarray(np.uint8(arr))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def load_image(input_image):
|
|
|
|
new_image = Image.new("RGB", (600, 448), "white")
|
|
|
|
jpeg_image = Image.open(input_image)
|
|
|
|
aspect_ratio = jpeg_image.width / jpeg_image.height
|
|
new_height = 448
|
|
new_width = int(aspect_ratio * new_height)
|
|
rescaled_image = jpeg_image.resize((new_width, new_height))
|
|
|
|
new_image.paste(rescaled_image, (0, 0))
|
|
|
|
return new_image
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
'''
|
|
|
|
The display dimensions of the ePaper device is
|
|
|
|
width - 600
|
|
height - 448
|
|
|
|
We need to adjust according
|
|
|
|
TODO - rotate image based on best fit
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sys.exit()
|
|
|
|
|
|
|
|
|
|
|
|
start_time = dt.now()
|
|
print("Running",start_time)
|
|
|
|
input_files = [file.name for file in os.scandir() if file.name[-3:]=="jpg" and not os.path.exists(file.name[:-3]+"bmp")]
|
|
|
|
for input_image in input_files:
|
|
|
|
print("Processing:",input_image)
|
|
|
|
img = load_image(input_image)
|
|
dim = fs_dither(img)
|
|
|
|
if dim is not None:
|
|
dim.save(input_image[:-3]+"bmp")
|
|
else:
|
|
print("ERROR: No image returned for",input_image)
|
|
|
|
|
|
print("Finished",dt.now()-start_time)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
'''
|
|
|
|
from PIL import Image
|
|
|
|
# Open the larger calendar image
|
|
calendar = Image.open("calendar.bmp")
|
|
|
|
# Open the smaller weather image
|
|
weather = Image.open("weather.bmp")
|
|
|
|
# Get the dimensions of the calendar image
|
|
calendar_width, calendar_height = calendar.size
|
|
|
|
# Get the dimensions of the weather image
|
|
weather_width, weather_height = weather.size
|
|
|
|
# Calculate the position to paste the weather image
|
|
position = (calendar_width - weather_width, calendar_height - weather_height)
|
|
|
|
# Paste the weather image onto the calendar image at the calculated position
|
|
calendar.paste(weather, position)
|
|
|
|
# Save the modified calendar image
|
|
calendar.save("calendar_with_weather.bmp")
|
|
|
|
# Optionally, show the result
|
|
calendar.show()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# test calendar code
|
|
|
|
from datetime import datetime
|
|
|
|
# Get the current date
|
|
current_date = datetime.now()
|
|
|
|
# Extract the current year and month as integers
|
|
current_year = current_date.year
|
|
current_month = current_date.month
|
|
|
|
# Print the year and month
|
|
print("Year:", current_year)
|
|
print("Month:", current_month)
|
|
|
|
current_date = datetime.now()
|
|
|
|
print(current_date.strftime("%A"))
|
|
|
|
print(current_date.strftime("%b %d, %Y"))
|
|
|
|
import calendar
|
|
|
|
yy = 2025
|
|
mm = 1
|
|
|
|
cal = calendar.month(yy, mm)
|
|
cal_lines = cal.split(chr(10))
|
|
|
|
for cal_line in cal_lines[1:]:
|
|
print(cal_line)
|
|
|
|
#print(cal)
|
|
#print(cal_lines)
|
|
|
|
#for char in cal:
|
|
# print(ord(char))
|
|
|
|
|
|
|
|
|
|
# Downloader
|
|
|
|
##pipe = cDataPipe()
|
|
##pipe.new_pipe(html_list_iter([r"https://creator.nightcafe.studio/"]))
|
|
##pipe.add_pipe(get_links,re.compile(r"(https:\/\/images\.nightcafe\.studio\/(?!\/assets)[A-z0-9\/\.\-]*jpg)"))
|
|
##pipe.add_pipe(pipe_inspector)
|
|
###pipe.end_pipe(cDownloader(file_namer="COUNTER",file_extension=".jpg").download)
|
|
##pipe.flow()
|
|
##
|
|
##sys.exit()
|
|
|
|
|
|
|
|
|
|
|
|
# width, height = img.size
|
|
#print(img.__dict__) #, width, height)
|
|
#new_width = 600
|
|
#new_height = int(height * new_width / width)
|
|
#img = img.resize((new_width, new_height), Image.ANTIALIAS)
|
|
|
|
def fs_dither_old(img, nc=2):
|
|
|
|
arr = np.array(img, dtype=float) / 255
|
|
|
|
for ir in range(new_height):
|
|
|
|
for ic in range(new_width):
|
|
|
|
# NB need to copy here for RGB arrays otherwise err will be (0,0,0)!
|
|
|
|
old_val = arr[ir, ic].copy()
|
|
new_val = get_new_val(old_val, nc)
|
|
arr[ir, ic] = new_val
|
|
err = old_val - new_val
|
|
|
|
# In this simple example, we will just ignore the border pixels.
|
|
|
|
if ic < new_width - 1:
|
|
arr[ir, ic+1] += (err * 7/16)
|
|
if ir < new_height - 1:
|
|
if ic > 0:
|
|
arr[ir+1, ic-1] += (err * 3/16)
|
|
arr[ir+1, ic] += (err * 5/16)
|
|
if ic < new_width - 1:
|
|
arr[ir+1, ic+1] += (err / 16)
|
|
|
|
carr = np.array(arr/np.max(arr, axis=(0,1)) * 255, dtype=np.uint8)
|
|
|
|
return Image.fromarray(carr)
|
|
|
|
|
|
|
|
'''
|
|
|