format with black
This commit is contained in:
		@@ -1,48 +1,122 @@
 | 
			
		||||
palArr=[[[0,0,0],[255,255,255]],
 | 
			
		||||
[[0,0,0],[255,255,255],[127,0,0]],
 | 
			
		||||
[[0,0,0],[255,255,255],[127,127,127]],
 | 
			
		||||
[[0,0,0],[255,255,255],[127,127,127],[127,0,0]],
 | 
			
		||||
[[0,0,0],[255,255,255]],
 | 
			
		||||
[[0,0,0],[255,255,255],[220,180,0]],
 | 
			
		||||
[[0,0,0]],
 | 
			
		||||
[[0,0,0],[255,255,255],[0,255,0],[0,0,255],[255,0,0],[255,255,0],[255,128,0]]];
 | 
			
		||||
palArr = [
 | 
			
		||||
    [[0, 0, 0], [255, 255, 255]],
 | 
			
		||||
    [[0, 0, 0], [255, 255, 255], [127, 0, 0]],
 | 
			
		||||
    [[0, 0, 0], [255, 255, 255], [127, 127, 127]],
 | 
			
		||||
    [[0, 0, 0], [255, 255, 255], [127, 127, 127], [127, 0, 0]],
 | 
			
		||||
    [[0, 0, 0], [255, 255, 255]],
 | 
			
		||||
    [[0, 0, 0], [255, 255, 255], [220, 180, 0]],
 | 
			
		||||
    [[0, 0, 0]],
 | 
			
		||||
    [
 | 
			
		||||
        [0, 0, 0],
 | 
			
		||||
        [255, 255, 255],
 | 
			
		||||
        [0, 255, 0],
 | 
			
		||||
        [0, 0, 255],
 | 
			
		||||
        [255, 0, 0],
 | 
			
		||||
        [255, 255, 0],
 | 
			
		||||
        [255, 128, 0],
 | 
			
		||||
    ],
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
epdArr=[[200,200,0],[200,200,3],[152,152,5],
 | 
			
		||||
[122,250,0],[104,212,1],[104,212,5],[104,212,0],
 | 
			
		||||
[176,264,0],[176,264,1],
 | 
			
		||||
[128,296,0],[128,296,1],[128,296,5],[128,296,0],
 | 
			
		||||
[400,300,0],[400,300,1],[400,300,5],
 | 
			
		||||
[600,448,0],[600,448,1],[600,448,5],
 | 
			
		||||
[640,384,0],[640,384,1],[640,384,5],
 | 
			
		||||
[800,480,0],[800,480,1],[880,528,1],
 | 
			
		||||
[600,448,7],[880,528,0],[280,480,0],
 | 
			
		||||
[152,296,0],[648,480,1],[128,296,1],
 | 
			
		||||
[200,200,1],[104,214,1],[128,296,0],
 | 
			
		||||
[400,300,1],[152,296,1],[648,480,0],
 | 
			
		||||
[640,400,7],[176,264,1],[122,250,0],
 | 
			
		||||
[122,250,1],[240,360,0],[176,264,0],
 | 
			
		||||
[122,250,0],[400,300,0],[960,680,0],
 | 
			
		||||
[800,480,0],[128,296,1],[960,680,1]];
 | 
			
		||||
epdArr = [
 | 
			
		||||
    [200, 200, 0],
 | 
			
		||||
    [200, 200, 3],
 | 
			
		||||
    [152, 152, 5],
 | 
			
		||||
    [122, 250, 0],
 | 
			
		||||
    [104, 212, 1],
 | 
			
		||||
    [104, 212, 5],
 | 
			
		||||
    [104, 212, 0],
 | 
			
		||||
    [176, 264, 0],
 | 
			
		||||
    [176, 264, 1],
 | 
			
		||||
    [128, 296, 0],
 | 
			
		||||
    [128, 296, 1],
 | 
			
		||||
    [128, 296, 5],
 | 
			
		||||
    [128, 296, 0],
 | 
			
		||||
    [400, 300, 0],
 | 
			
		||||
    [400, 300, 1],
 | 
			
		||||
    [400, 300, 5],
 | 
			
		||||
    [600, 448, 0],
 | 
			
		||||
    [600, 448, 1],
 | 
			
		||||
    [600, 448, 5],
 | 
			
		||||
    [640, 384, 0],
 | 
			
		||||
    [640, 384, 1],
 | 
			
		||||
    [640, 384, 5],
 | 
			
		||||
    [800, 480, 0],
 | 
			
		||||
    [800, 480, 1],
 | 
			
		||||
    [880, 528, 1],
 | 
			
		||||
    [600, 448, 7],
 | 
			
		||||
    [880, 528, 0],
 | 
			
		||||
    [280, 480, 0],
 | 
			
		||||
    [152, 296, 0],
 | 
			
		||||
    [648, 480, 1],
 | 
			
		||||
    [128, 296, 1],
 | 
			
		||||
    [200, 200, 1],
 | 
			
		||||
    [104, 214, 1],
 | 
			
		||||
    [128, 296, 0],
 | 
			
		||||
    [400, 300, 1],
 | 
			
		||||
    [152, 296, 1],
 | 
			
		||||
    [648, 480, 0],
 | 
			
		||||
    [640, 400, 7],
 | 
			
		||||
    [176, 264, 1],
 | 
			
		||||
    [122, 250, 0],
 | 
			
		||||
    [122, 250, 1],
 | 
			
		||||
    [240, 360, 0],
 | 
			
		||||
    [176, 264, 0],
 | 
			
		||||
    [122, 250, 0],
 | 
			
		||||
    [400, 300, 0],
 | 
			
		||||
    [960, 680, 0],
 | 
			
		||||
    [800, 480, 0],
 | 
			
		||||
    [128, 296, 1],
 | 
			
		||||
    [960, 680, 1],
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
EPD_TYPES = {
 | 
			
		||||
    '1.54': 0, '1.54b': 1, '1.54c': 2,
 | 
			
		||||
    '2.13': 3, '2.13b': 4, '2.13c': 5, '2.13d': 6,
 | 
			
		||||
    '2.7': 7, '2.7b': 8,
 | 
			
		||||
    '2.9': 9, '2.9b': 10, '2.9c': 11, '2.9d': 12,
 | 
			
		||||
    '4.2': 13, '4.2b': 14, '4.2c': 15,
 | 
			
		||||
    '5.83': 16, '5.83b': 17, '5.83c': 18,
 | 
			
		||||
    '7.5': 19, '7.5b': 20, '7.5c': 21,
 | 
			
		||||
    '7.5 V2': 22, '7.5b V2': 23,
 | 
			
		||||
    '7.5b HD': 24, '5.65f': 25,
 | 
			
		||||
    '7.5 HD': 26, '3.7': 27, '2.66': 28,
 | 
			
		||||
    '5.83b V2': 29, '2.9b V3': 30,
 | 
			
		||||
    '1.54b V2': 31, '2.13b V3': 32,
 | 
			
		||||
    '2.9 V2': 33, '4.2b V2': 34,
 | 
			
		||||
    '2.66b': 35, '5.83 V2': 36,
 | 
			
		||||
    '4.01 f': 37, '2.7b V2': 38,
 | 
			
		||||
    '2.13 V3': 39, '2.13 B V4': 40,
 | 
			
		||||
    '3.52': 41, '2.7 V2': 42,
 | 
			
		||||
    '2.13 V4': 43, '4.2 V2': 44,
 | 
			
		||||
    '13.3k': 45, '4.26': 46,
 | 
			
		||||
    '2.9bV4': 47, '13.3b': 48
 | 
			
		||||
    "1.54": 0,
 | 
			
		||||
    "1.54b": 1,
 | 
			
		||||
    "1.54c": 2,
 | 
			
		||||
    "2.13": 3,
 | 
			
		||||
    "2.13b": 4,
 | 
			
		||||
    "2.13c": 5,
 | 
			
		||||
    "2.13d": 6,
 | 
			
		||||
    "2.7": 7,
 | 
			
		||||
    "2.7b": 8,
 | 
			
		||||
    "2.9": 9,
 | 
			
		||||
    "2.9b": 10,
 | 
			
		||||
    "2.9c": 11,
 | 
			
		||||
    "2.9d": 12,
 | 
			
		||||
    "4.2": 13,
 | 
			
		||||
    "4.2b": 14,
 | 
			
		||||
    "4.2c": 15,
 | 
			
		||||
    "5.83": 16,
 | 
			
		||||
    "5.83b": 17,
 | 
			
		||||
    "5.83c": 18,
 | 
			
		||||
    "7.5": 19,
 | 
			
		||||
    "7.5b": 20,
 | 
			
		||||
    "7.5c": 21,
 | 
			
		||||
    "7.5 V2": 22,
 | 
			
		||||
    "7.5b V2": 23,
 | 
			
		||||
    "7.5b HD": 24,
 | 
			
		||||
    "5.65f": 25,
 | 
			
		||||
    "7.5 HD": 26,
 | 
			
		||||
    "3.7": 27,
 | 
			
		||||
    "2.66": 28,
 | 
			
		||||
    "5.83b V2": 29,
 | 
			
		||||
    "2.9b V3": 30,
 | 
			
		||||
    "1.54b V2": 31,
 | 
			
		||||
    "2.13b V3": 32,
 | 
			
		||||
    "2.9 V2": 33,
 | 
			
		||||
    "4.2b V2": 34,
 | 
			
		||||
    "2.66b": 35,
 | 
			
		||||
    "5.83 V2": 36,
 | 
			
		||||
    "4.01 f": 37,
 | 
			
		||||
    "2.7b V2": 38,
 | 
			
		||||
    "2.13 V3": 39,
 | 
			
		||||
    "2.13 B V4": 40,
 | 
			
		||||
    "3.52": 41,
 | 
			
		||||
    "2.7 V2": 42,
 | 
			
		||||
    "2.13 V4": 43,
 | 
			
		||||
    "4.2 V2": 44,
 | 
			
		||||
    "13.3k": 45,
 | 
			
		||||
    "4.26": 46,
 | 
			
		||||
    "2.9bV4": 47,
 | 
			
		||||
    "13.3b": 48,
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,64 +6,70 @@ import sys
 | 
			
		||||
def byteToStr(v):
 | 
			
		||||
    return chr((v & 0xF) + 97) + chr(((v >> 4) & 0xF) + 97)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def wordToStr(v):
 | 
			
		||||
    return byteToStr(v & 0xFF) + byteToStr((v >> 8) & 0xFF)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Reimplementation of u_data from scriptD.js
 | 
			
		||||
def _u_data(pixel_indices, c, px_ind_start):
 | 
			
		||||
    rqMsg = ''
 | 
			
		||||
    rqMsg = ""
 | 
			
		||||
    pxInd = px_ind_start
 | 
			
		||||
    max_rq_len = 1000 # Max length of rqMsg
 | 
			
		||||
    max_rq_len = 1000  # Max length of rqMsg
 | 
			
		||||
 | 
			
		||||
    if c == -1: # 16-bit values
 | 
			
		||||
    if c == -1:  # 16-bit values
 | 
			
		||||
        while pxInd < len(pixel_indices) and len(rqMsg) < max_rq_len:
 | 
			
		||||
            v = 0
 | 
			
		||||
            for i in range(0, 16, 2): # This means 8 iterations, packing 8 pixels
 | 
			
		||||
            for i in range(0, 16, 2):  # This means 8 iterations, packing 8 pixels
 | 
			
		||||
                if pxInd < len(pixel_indices):
 | 
			
		||||
                    v |= (pixel_indices[pxInd] << i) # This is likely wrong, should be 4 bits per pixel
 | 
			
		||||
                    v |= (
 | 
			
		||||
                        pixel_indices[pxInd] << i
 | 
			
		||||
                    )  # This is likely wrong, should be 4 bits per pixel
 | 
			
		||||
                pxInd += 1
 | 
			
		||||
            rqMsg += wordToStr(v)
 | 
			
		||||
    elif c == -2: # 7-color, 4-bit values
 | 
			
		||||
    elif c == -2:  # 7-color, 4-bit values
 | 
			
		||||
        while pxInd < len(pixel_indices) and len(rqMsg) < max_rq_len:
 | 
			
		||||
            v = 0
 | 
			
		||||
            for i in range(0, 16, 4): # 4 iterations, packing 4 pixels
 | 
			
		||||
            for i in range(0, 16, 4):  # 4 iterations, packing 4 pixels
 | 
			
		||||
                if pxInd < len(pixel_indices):
 | 
			
		||||
                    v |= (pixel_indices[pxInd] << i)
 | 
			
		||||
                    v |= pixel_indices[pxInd] << i
 | 
			
		||||
                pxInd += 1
 | 
			
		||||
            rqMsg += wordToStr(v)
 | 
			
		||||
    else: # Monochrome, 1-bit values
 | 
			
		||||
    else:  # Monochrome, 1-bit values
 | 
			
		||||
        while pxInd < len(pixel_indices) and len(rqMsg) < max_rq_len:
 | 
			
		||||
            v = 0
 | 
			
		||||
            for i in range(8): # Packs 8 pixels into 1 byte
 | 
			
		||||
            for i in range(8):  # Packs 8 pixels into 1 byte
 | 
			
		||||
                if pxInd < len(pixel_indices) and pixel_indices[pxInd] != c:
 | 
			
		||||
                    v |= (128 >> i)
 | 
			
		||||
                    v |= 128 >> i
 | 
			
		||||
                pxInd += 1
 | 
			
		||||
            rqMsg += byteToStr(v)
 | 
			
		||||
    return rqMsg, pxInd
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Reimplementation of u_line from scriptD.js
 | 
			
		||||
def _u_line(pixel_indices, c, px_ind_start):
 | 
			
		||||
    rqMsg = ''
 | 
			
		||||
    rqMsg = ""
 | 
			
		||||
    pxInd = px_ind_start
 | 
			
		||||
    max_rq_len = 1000 # Max length of rqMsg
 | 
			
		||||
    max_rq_len = 1000  # Max length of rqMsg
 | 
			
		||||
 | 
			
		||||
    while len(rqMsg) < max_rq_len:
 | 
			
		||||
        x = 0
 | 
			
		||||
        while x < 122: # Processes 122 pixels at a time
 | 
			
		||||
        while x < 122:  # Processes 122 pixels at a time
 | 
			
		||||
            v = 0
 | 
			
		||||
            for i in range(8):
 | 
			
		||||
                if pxInd < len(pixel_indices) and x < 122:
 | 
			
		||||
                    if pixel_indices[pxInd] != c:
 | 
			
		||||
                        v |= (128 >> i)
 | 
			
		||||
                        v |= 128 >> i
 | 
			
		||||
                    pxInd += 1
 | 
			
		||||
                    x += 1
 | 
			
		||||
                else:
 | 
			
		||||
                    break # Break if no more pixels or 122 pixels processed
 | 
			
		||||
                    break  # Break if no more pixels or 122 pixels processed
 | 
			
		||||
            rqMsg += byteToStr(v)
 | 
			
		||||
            if x >= 122: # If 122 pixels processed, break inner loop
 | 
			
		||||
            if x >= 122:  # If 122 pixels processed, break inner loop
 | 
			
		||||
                break
 | 
			
		||||
    return rqMsg, pxInd
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def upload_image(img, ip_addr, epd_ind, epdArr, palArr, getNear):
 | 
			
		||||
    print(f"[DEBUG] Starting upload_image for EPD index {epd_ind} to {ip_addr}")
 | 
			
		||||
    url_prefix = f"http://{ip_addr}/"
 | 
			
		||||
@@ -72,7 +78,7 @@ def upload_image(img, ip_addr, epd_ind, epdArr, palArr, getNear):
 | 
			
		||||
    pal_ind = epdArr[epd_ind][2]
 | 
			
		||||
    curPal = palArr[pal_ind]
 | 
			
		||||
    pixels = list(img.getdata())
 | 
			
		||||
    pixel_indices = [getNear(r,g,b, curPal) for r,g,b in pixels]
 | 
			
		||||
    pixel_indices = [getNear(r, g, b, curPal) for r, g, b in pixels]
 | 
			
		||||
 | 
			
		||||
    pxInd = 0
 | 
			
		||||
    stInd = 0
 | 
			
		||||
@@ -82,44 +88,46 @@ def upload_image(img, ip_addr, epd_ind, epdArr, palArr, getNear):
 | 
			
		||||
        full_url = url_prefix + cmd
 | 
			
		||||
        try:
 | 
			
		||||
            response = requests.post(full_url)
 | 
			
		||||
            response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
 | 
			
		||||
            response.raise_for_status()  # Raise HTTPError for bad responses (4xx or 5xx)
 | 
			
		||||
            if next_state:
 | 
			
		||||
                stInd += 1
 | 
			
		||||
            return 0
 | 
			
		||||
        except requests.exceptions.RequestException as e:
 | 
			
		||||
            print(f"[ERROR] Error sending command {cmd}: {e}")
 | 
			
		||||
            raise # Re-raise the exception to stop execution
 | 
			
		||||
            raise  # Re-raise the exception to stop execution
 | 
			
		||||
 | 
			
		||||
    def u_next():
 | 
			
		||||
        nonlocal pxInd
 | 
			
		||||
        pxInd = 0
 | 
			
		||||
        return u_send('NEXT_', True)
 | 
			
		||||
        return u_send("NEXT_", True)
 | 
			
		||||
 | 
			
		||||
    def u_done():
 | 
			
		||||
        sys.stdout.write('\n') # Ensure progress bar finishes on a new line
 | 
			
		||||
        sys.stdout.write("\n")  # Ensure progress bar finishes on a new line
 | 
			
		||||
        print("Image upload complete!")
 | 
			
		||||
        return u_send('SHOW_', True)
 | 
			
		||||
        return u_send("SHOW_", True)
 | 
			
		||||
 | 
			
		||||
    def u_show(a_len, k1, k2, rqMsg_val):
 | 
			
		||||
        nonlocal pxInd
 | 
			
		||||
        total_pixels = len(pixel_indices)
 | 
			
		||||
        current_progress_pixels = pxInd
 | 
			
		||||
 | 
			
		||||
        overall_percentage = (current_progress_pixels / total_pixels) * 100 if total_pixels > 0 else 0
 | 
			
		||||
        overall_percentage = (
 | 
			
		||||
            (current_progress_pixels / total_pixels) * 100 if total_pixels > 0 else 0
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        bar_length = 50
 | 
			
		||||
        filled_length = int(bar_length * overall_percentage // 100)
 | 
			
		||||
        bar = '#' * filled_length + '-' * (bar_length - filled_length)
 | 
			
		||||
        sys.stdout.write(f'\rProgress: |{bar}| {overall_percentage:.2f}%')
 | 
			
		||||
        bar = "#" * filled_length + "-" * (bar_length - filled_length)
 | 
			
		||||
        sys.stdout.write(f"\rProgress: |{bar}| {overall_percentage:.2f}%")
 | 
			
		||||
        sys.stdout.flush()
 | 
			
		||||
 | 
			
		||||
        return u_send(rqMsg_val + wordToStr(len(rqMsg_val)) + 'LOAD_', pxInd >= a_len)
 | 
			
		||||
        return u_send(rqMsg_val + wordToStr(len(rqMsg_val)) + "LOAD_", pxInd >= a_len)
 | 
			
		||||
 | 
			
		||||
    # Main upload logic based on scriptD.js
 | 
			
		||||
    try:
 | 
			
		||||
        # Initial EPD command
 | 
			
		||||
        epd_cmd_char = chr(epd_ind + 97) if epd_ind < 26 else chr(epd_ind - 26 + 65)
 | 
			
		||||
        u_send(f'EPD{epd_cmd_char}_')
 | 
			
		||||
        u_send(f"EPD{epd_cmd_char}_")
 | 
			
		||||
 | 
			
		||||
        # Conditional logic based on epd_ind, mimicking scriptD.js
 | 
			
		||||
        if epd_ind in [3, 39, 43]:
 | 
			
		||||
@@ -185,8 +193,12 @@ def upload_image(img, ip_addr, epd_ind, epdArr, palArr, getNear):
 | 
			
		||||
                    # Special handling for 7.5b V2 (epd_ind 23) red channel
 | 
			
		||||
                    if epd_ind == 23:
 | 
			
		||||
                        # Encode only red pixels (palette index 2)
 | 
			
		||||
                        red_pixel_indices = [0 if p == 2 else 2 for p in pixel_indices] # Invert red channel: 0 for red, 2 for non-red
 | 
			
		||||
                        rqMsg, pxInd = _u_data(red_pixel_indices, 0, pxInd) # Use monochrome encoding for this layer
 | 
			
		||||
                        red_pixel_indices = [
 | 
			
		||||
                            0 if p == 2 else 2 for p in pixel_indices
 | 
			
		||||
                        ]  # Invert red channel: 0 for red, 2 for non-red
 | 
			
		||||
                        rqMsg, pxInd = _u_data(
 | 
			
		||||
                            red_pixel_indices, 0, pxInd
 | 
			
		||||
                        )  # Use monochrome encoding for this layer
 | 
			
		||||
                    else:
 | 
			
		||||
                        rqMsg, pxInd = _u_data(pixel_indices, 3, pxInd)
 | 
			
		||||
                    u_show(len(pixel_indices), 50, 50, rqMsg)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,27 +1,31 @@
 | 
			
		||||
from PIL import Image
 | 
			
		||||
 | 
			
		||||
def getErr(r,g,b,stdCol):
 | 
			
		||||
    r-=stdCol[0]
 | 
			
		||||
    g-=stdCol[1]
 | 
			
		||||
    b-=stdCol[2]
 | 
			
		||||
    return r*r + g*g + b*b
 | 
			
		||||
 | 
			
		||||
def getNear(r,g,b, curPal):
 | 
			
		||||
    ind=0
 | 
			
		||||
    err=getErr(r,g,b,curPal[0])
 | 
			
		||||
    for i in range(1,len(curPal)):
 | 
			
		||||
        cur=getErr(r,g,b,curPal[i])
 | 
			
		||||
        if (cur<err):
 | 
			
		||||
            err=cur
 | 
			
		||||
            ind=i
 | 
			
		||||
def getErr(r, g, b, stdCol):
 | 
			
		||||
    r -= stdCol[0]
 | 
			
		||||
    g -= stdCol[1]
 | 
			
		||||
    b -= stdCol[2]
 | 
			
		||||
    return r * r + g * g + b * b
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def getNear(r, g, b, curPal):
 | 
			
		||||
    ind = 0
 | 
			
		||||
    err = getErr(r, g, b, curPal[0])
 | 
			
		||||
    for i in range(1, len(curPal)):
 | 
			
		||||
        cur = getErr(r, g, b, curPal[i])
 | 
			
		||||
        if cur < err:
 | 
			
		||||
            err = cur
 | 
			
		||||
            ind = i
 | 
			
		||||
    return ind
 | 
			
		||||
 | 
			
		||||
def addVal(c,r,g,b,k):
 | 
			
		||||
    return[c[0]+(r*k)/32,c[1]+(g*k)/32,c[2]+(b*k)/32]
 | 
			
		||||
 | 
			
		||||
def addVal(c, r, g, b, k):
 | 
			
		||||
    return [c[0] + (r * k) / 32, c[1] + (g * k) / 32, c[2] + (b * k) / 32]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def procImg(img, dW, dH, dX, dY, palInd, isLvl, isRed, palArr):
 | 
			
		||||
    if isRed and ((palInd & 1) == 0):
 | 
			
		||||
        raise ValueError('This white-black display')
 | 
			
		||||
        raise ValueError("This white-black display")
 | 
			
		||||
 | 
			
		||||
    if not isRed:
 | 
			
		||||
        palInd = palInd & 0xFE
 | 
			
		||||
@@ -31,7 +35,7 @@ def procImg(img, dW, dH, dX, dY, palInd, isLvl, isRed, palArr):
 | 
			
		||||
    sW = img.width
 | 
			
		||||
    sH = img.height
 | 
			
		||||
 | 
			
		||||
    pDst = Image.new('RGB', (dW, dH))
 | 
			
		||||
    pDst = Image.new("RGB", (dW, dH))
 | 
			
		||||
    pixels = pDst.load()
 | 
			
		||||
 | 
			
		||||
    if isLvl:
 | 
			
		||||
@@ -45,7 +49,9 @@ def procImg(img, dW, dH, dX, dY, palInd, isLvl, isRed, palArr):
 | 
			
		||||
                    color_index = getNear(r, g, b, curPal)
 | 
			
		||||
                    pixels[i, j] = tuple(curPal[color_index])
 | 
			
		||||
                else:
 | 
			
		||||
                    pixels[i, j] = tuple(curPal[(i + j) % 2]) # checkerboard for out of bounds
 | 
			
		||||
                    pixels[i, j] = tuple(
 | 
			
		||||
                        curPal[(i + j) % 2]
 | 
			
		||||
                    )  # checkerboard for out of bounds
 | 
			
		||||
    else:
 | 
			
		||||
        # Dithering
 | 
			
		||||
        errArr = [[[0, 0, 0] for _ in range(dW)] for _ in range(2)]
 | 
			
		||||
@@ -54,9 +60,9 @@ def procImg(img, dW, dH, dX, dY, palInd, isLvl, isRed, palArr):
 | 
			
		||||
 | 
			
		||||
        for j in range(dH):
 | 
			
		||||
            y = dY + j
 | 
			
		||||
            aInd, bInd = bInd, aInd # swap buffers
 | 
			
		||||
            aInd, bInd = bInd, aInd  # swap buffers
 | 
			
		||||
            for i in range(dW):
 | 
			
		||||
                errArr[bInd][i] = [0,0,0]
 | 
			
		||||
                errArr[bInd][i] = [0, 0, 0]
 | 
			
		||||
 | 
			
		||||
            for i in range(dW):
 | 
			
		||||
                x = dX + i
 | 
			
		||||
@@ -77,20 +83,38 @@ def procImg(img, dW, dH, dX, dY, palInd, isLvl, isRed, palArr):
 | 
			
		||||
 | 
			
		||||
                    # Propagate error (Floyd-Steinberg-like)
 | 
			
		||||
                    if i == 0:
 | 
			
		||||
                        errArr[bInd][i]     = addVal(errArr[bInd][i],     r_err, g_err, b_err, 7.0)
 | 
			
		||||
                        errArr[bInd][i] = addVal(
 | 
			
		||||
                            errArr[bInd][i], r_err, g_err, b_err, 7.0
 | 
			
		||||
                        )
 | 
			
		||||
                        if dW > 1:
 | 
			
		||||
                            errArr[bInd][i+1]   = addVal(errArr[bInd][i+1],   r_err, g_err, b_err, 2.0)
 | 
			
		||||
                            errArr[aInd][i+1]   = addVal(errArr[aInd][i+1],   r_err, g_err, b_err, 7.0)
 | 
			
		||||
                            errArr[bInd][i + 1] = addVal(
 | 
			
		||||
                                errArr[bInd][i + 1], r_err, g_err, b_err, 2.0
 | 
			
		||||
                            )
 | 
			
		||||
                            errArr[aInd][i + 1] = addVal(
 | 
			
		||||
                                errArr[aInd][i + 1], r_err, g_err, b_err, 7.0
 | 
			
		||||
                            )
 | 
			
		||||
                    elif i == dW - 1:
 | 
			
		||||
                        errArr[bInd][i-1]   = addVal(errArr[bInd][i-1],   r_err, g_err, b_err, 7.0)
 | 
			
		||||
                        errArr[bInd][i]     = addVal(errArr[bInd][i],     r_err, g_err, b_err, 9.0)
 | 
			
		||||
                        errArr[bInd][i - 1] = addVal(
 | 
			
		||||
                            errArr[bInd][i - 1], r_err, g_err, b_err, 7.0
 | 
			
		||||
                        )
 | 
			
		||||
                        errArr[bInd][i] = addVal(
 | 
			
		||||
                            errArr[bInd][i], r_err, g_err, b_err, 9.0
 | 
			
		||||
                        )
 | 
			
		||||
                    else:
 | 
			
		||||
                        errArr[bInd][i-1]   = addVal(errArr[bInd][i-1],   r_err, g_err, b_err, 3.0)
 | 
			
		||||
                        errArr[bInd][i]     = addVal(errArr[bInd][i],     r_err, g_err, b_err, 5.0)
 | 
			
		||||
                        errArr[bInd][i+1]   = addVal(errArr[bInd][i+1],   r_err, g_err, b_err, 1.0)
 | 
			
		||||
                        errArr[aInd][i+1]   = addVal(errArr[aInd][i+1],   r_err, g_err, b_err, 7.0)
 | 
			
		||||
                        errArr[bInd][i - 1] = addVal(
 | 
			
		||||
                            errArr[bInd][i - 1], r_err, g_err, b_err, 3.0
 | 
			
		||||
                        )
 | 
			
		||||
                        errArr[bInd][i] = addVal(
 | 
			
		||||
                            errArr[bInd][i], r_err, g_err, b_err, 5.0
 | 
			
		||||
                        )
 | 
			
		||||
                        errArr[bInd][i + 1] = addVal(
 | 
			
		||||
                            errArr[bInd][i + 1], r_err, g_err, b_err, 1.0
 | 
			
		||||
                        )
 | 
			
		||||
                        errArr[aInd][i + 1] = addVal(
 | 
			
		||||
                            errArr[aInd][i + 1], r_err, g_err, b_err, 7.0
 | 
			
		||||
                        )
 | 
			
		||||
 | 
			
		||||
                else:
 | 
			
		||||
                    pixels[i, j] = tuple(curPal[(i + j) % 2]) # checkerboard
 | 
			
		||||
                    pixels[i, j] = tuple(curPal[(i + j) % 2])  # checkerboard
 | 
			
		||||
 | 
			
		||||
    return pDst
 | 
			
		||||
 
 | 
			
		||||
@@ -11,3 +11,6 @@ dependencies = [
 | 
			
		||||
    "Pillow==11.3.0",
 | 
			
		||||
    "python-dotenv==1.1.1",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[project.optional-dependencies]
 | 
			
		||||
dev = ["black==24.4.2"]
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,6 @@ from epd.image_processor import getErr, getNear, addVal, procImg
 | 
			
		||||
from epd.epd_uploader import upload_image
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# get truly random post
 | 
			
		||||
try:
 | 
			
		||||
    szuru_url = os.environ["SZURU_URL"]
 | 
			
		||||
@@ -32,11 +31,13 @@ post = None
 | 
			
		||||
while post is None:
 | 
			
		||||
    random_id = random.randint(0, highest_post.id_)
 | 
			
		||||
    temp_post = booru.getPost(random_id)
 | 
			
		||||
    if temp_post and temp_post.mime.startswith('image/') and temp_post.content:
 | 
			
		||||
    if temp_post and temp_post.mime.startswith("image/") and temp_post.content:
 | 
			
		||||
        post = temp_post
 | 
			
		||||
        print(f"[DEBUG] Found image post with ID: {post.id_}, MIME: {post.mime}")
 | 
			
		||||
    else:
 | 
			
		||||
        print(f"[DEBUG] Skipping post ID: {random_id} (MIME: {temp_post.mime if temp_post else 'None'}, Content: {bool(temp_post.content) if temp_post else 'None'}) - not a valid image.")
 | 
			
		||||
        print(
 | 
			
		||||
            f"[DEBUG] Skipping post ID: {random_id} (MIME: {temp_post.mime if temp_post else 'None'}, Content: {bool(temp_post.content) if temp_post else 'None'}) - not a valid image."
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
# download image
 | 
			
		||||
image_downloaded = False
 | 
			
		||||
@@ -45,19 +46,21 @@ while not image_downloaded:
 | 
			
		||||
        r = requests.get(post.content, stream=True)
 | 
			
		||||
        print(f"[DEBUG] Downloading image from {post.content}")
 | 
			
		||||
        r.raise_for_status()
 | 
			
		||||
        with open("image.jpg", 'wb') as f:
 | 
			
		||||
        with open("image.jpg", "wb") as f:
 | 
			
		||||
            for chunk in r.iter_content(chunk_size=8192):
 | 
			
		||||
                f.write(chunk)
 | 
			
		||||
        print("[DEBUG] Image downloaded and saved as image.jpg")
 | 
			
		||||
 | 
			
		||||
        # Attempt to open the image to verify
 | 
			
		||||
        img = Image.open("image.jpg")
 | 
			
		||||
        img.verify() # Verify that it is an image
 | 
			
		||||
        img.verify()  # Verify that it is an image
 | 
			
		||||
        img.close()
 | 
			
		||||
        image_downloaded = True
 | 
			
		||||
        print("[DEBUG] Image successfully verified.")
 | 
			
		||||
    except (PIL.UnidentifiedImageError, requests.exceptions.RequestException) as e:
 | 
			
		||||
        print(f"[ERROR] Failed to process downloaded file (Error: {e}). Retrying with a new post...")
 | 
			
		||||
        print(
 | 
			
		||||
            f"[ERROR] Failed to process downloaded file (Error: {e}). Retrying with a new post..."
 | 
			
		||||
        )
 | 
			
		||||
        # Clean up the invalid file
 | 
			
		||||
        if os.path.exists("image.jpg"):
 | 
			
		||||
            os.remove("image.jpg")
 | 
			
		||||
@@ -66,13 +69,17 @@ while not image_downloaded:
 | 
			
		||||
        while post is None:
 | 
			
		||||
            random_id = random.randint(0, highest_post.id_)
 | 
			
		||||
            temp_post = booru.getPost(random_id)
 | 
			
		||||
            if temp_post and temp_post.mime.startswith('image/') and temp_post.content:
 | 
			
		||||
            if temp_post and temp_post.mime.startswith("image/") and temp_post.content:
 | 
			
		||||
                post = temp_post
 | 
			
		||||
                print(f"[DEBUG] Found new image post with ID: {post.id_}, MIME: {post.mime}")
 | 
			
		||||
                print(
 | 
			
		||||
                    f"[DEBUG] Found new image post with ID: {post.id_}, MIME: {post.mime}"
 | 
			
		||||
                )
 | 
			
		||||
            else:
 | 
			
		||||
                print(f"[DEBUG] Skipping post ID: {random_id} (MIME: {temp_post.mime if temp_post else 'None'}, Content: {bool(temp_post.content) if temp_post else 'None'}) - not a valid image.")
 | 
			
		||||
                print(
 | 
			
		||||
                    f"[DEBUG] Skipping post ID: {random_id} (MIME: {temp_post.mime if temp_post else 'None'}, Content: {bool(temp_post.content) if temp_post else 'None'}) - not a valid image."
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
# Process and upload
 | 
			
		||||
    # Process and upload
 | 
			
		||||
    img = Image.open("image.jpg")
 | 
			
		||||
 | 
			
		||||
# Get configuration from environment variables
 | 
			
		||||
@@ -84,7 +91,9 @@ except KeyError as e:
 | 
			
		||||
    raise ValueError(f"Missing required environment variable: {e}")
 | 
			
		||||
 | 
			
		||||
if epd_type_str not in EPD_TYPES:
 | 
			
		||||
    raise ValueError(f"Invalid EPD_TYPE: {epd_type_str}. Must be one of {list(EPD_TYPES.keys())}")
 | 
			
		||||
    raise ValueError(
 | 
			
		||||
        f"Invalid EPD_TYPE: {epd_type_str}. Must be one of {list(EPD_TYPES.keys())}"
 | 
			
		||||
    )
 | 
			
		||||
epd_ind = EPD_TYPES[epd_type_str]
 | 
			
		||||
 | 
			
		||||
is_red = False
 | 
			
		||||
@@ -93,12 +102,16 @@ if dithering_mode == "color":
 | 
			
		||||
    # EPDs that are explicitly monochrome in scriptD.js despite palArr:
 | 
			
		||||
    monochrome_only_epds = [0, 3, 6, 7, 9, 12, 16, 19, 22, 26, 27, 28, 39, 40, 43]
 | 
			
		||||
    if epd_ind in monochrome_only_epds:
 | 
			
		||||
        print(f"[WARNING] Dithering mode is set to 'color', but EPD type '{epd_type_str}' (Index {epd_ind}) is treated as monochrome by the device's protocol. Color will not appear.")
 | 
			
		||||
        is_red = False # Force monochrome for this EPD type
 | 
			
		||||
        print(
 | 
			
		||||
            f"[WARNING] Dithering mode is set to 'color', but EPD type '{epd_type_str}' (Index {epd_ind}) is treated as monochrome by the device's protocol. Color will not appear."
 | 
			
		||||
        )
 | 
			
		||||
        is_red = False  # Force monochrome for this EPD type
 | 
			
		||||
    else:
 | 
			
		||||
        is_red = True
 | 
			
		||||
elif dithering_mode != "mono":
 | 
			
		||||
    raise ValueError(f"Invalid DITHERING_MODE: {dithering_mode}. Must be 'mono' or 'color'.")
 | 
			
		||||
    raise ValueError(
 | 
			
		||||
        f"Invalid DITHERING_MODE: {dithering_mode}. Must be 'mono' or 'color'."
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
# Get display dimensions and palette from epdArr
 | 
			
		||||
dW, dH, pal_ind = epdArr[epd_ind]
 | 
			
		||||
@@ -122,9 +135,11 @@ print(f"[DEBUG] EPD Type String: {epd_type_str}")
 | 
			
		||||
print(f"[DEBUG] EPD Index: {epd_ind}")
 | 
			
		||||
print(f"[DEBUG] Palette Index: {pal_ind}")
 | 
			
		||||
# Process the final image with dithering
 | 
			
		||||
processed_img = procImg(img, dW, dH, 0, 0, pal_ind, isLvl=False, isRed=is_red, palArr=palArr)
 | 
			
		||||
processed_img = procImg(
 | 
			
		||||
    img, dW, dH, 0, 0, pal_ind, isLvl=False, isRed=is_red, palArr=palArr
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
processed_img.save("processed.png") # Save for debugging
 | 
			
		||||
processed_img.save("processed.png")  # Save for debugging
 | 
			
		||||
print("[DEBUG] Imaged processed and saved as processed.png")
 | 
			
		||||
 | 
			
		||||
upload_image(processed_img, ip_addr, epd_ind, epdArr, palArr, getNear)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user