unlimited-storage

YouTube filesystem tool for uploading arbitrary data to the service
git clone git://git.laack.co/unlimited-storage.git
Log | Files | Refs | README

encode.py (3467B)


      1 import numpy as np
      2 import math
      3 import os
      4 import sys
      5 from PIL import Image
      6 import random
      7 import string
      8 
      9 def get_rnd_str(length):
     10     random_string = ''.join(random.choices(string.ascii_letters + string.digits, k=length))
     11     return(random_string)
     12 
     13 # idea:
     14 # encode text into XxX matrix for white/black bits
     15 # use image magick or smt
     16 # ffmpeg -> vid
     17 
     18 
     19 def byte_to_bits(byte):
     20     return [(byte >> i) & 1 for i in reversed(range(8))]
     21 
     22 
     23 # assume 1920x1080
     24 # how many bits?
     25 # 2073600 bits!
     26 # 259,200 bytes
     27 
     28 # this should be a deterministic size for ease of encoding/decoding
     29 
     30 # reserved header:
     31 # file name - 100 bytes - 800 bits / pixels
     32 # chunk number - 4 bytes - 32 pixels
     33 # bytes encoded - 4 bytes - 32 pixels
     34 
     35 # 108 * 8 = 864
     36 
     37 def make_header(file_name, chunk_number, bytes_encoded):
     38     file_name_bytes = file_name.encode("utf-8")
     39     if len(file_name_bytes) > 100:
     40         file_name_bytes = file_name_bytes[:100]
     41     else:
     42         file_name_bytes = file_name_bytes.ljust(100, b'\x00')
     43     
     44     chunk_num = np.int32(chunk_number).tobytes()
     45     num_bytes = np.int32(bytes_encoded).tobytes()
     46 
     47     header = file_name_bytes + chunk_num + num_bytes
     48     assert len(header) == 108
     49     return header
     50 
     51 # reserved header is 108 bytes
     52 
     53 # 259,200 - 108 = 259,092
     54 
     55 x = int(float(sys.argv[1]))
     56 y = int(float(sys.argv[2]))
     57 total_bits_available = x * y
     58 bits_for_data = total_bits_available - 864
     59 bytes_per_frame = bits_for_data // 8
     60 
     61 print("Max bytes per frame: " + str(bytes_per_frame))
     62 
     63 filename = sys.argv[3]
     64 destination = sys.argv[4]
     65 destination.removesuffix("/")
     66 
     67 if not os.path.exists(destination):
     68     os.mkdir(destination)
     69 
     70 print("x: " + str(x))
     71 print("y: " + str(y))
     72 print("filename: " + filename)
     73 
     74 file_size_bytes = os.path.getsize(filename)
     75 print("File size in bytes: " + str(file_size_bytes))
     76 
     77 
     78 chunks_required = math.ceil(file_size_bytes / bytes_per_frame)
     79 print("Chunks required: " + str(chunks_required))
     80 
     81 if chunks_required > 1:
     82     bytes_per_chunk = [bytes_per_frame] * (chunks_required - 1)
     83     bytes_per_chunk.append(file_size_bytes % bytes_per_frame)
     84 else:
     85     bytes_per_chunk = [file_size_bytes]
     86 
     87 assert sum(bytes_per_chunk) == file_size_bytes
     88 
     89 print("Bytes per chunk: " + str(bytes_per_chunk))
     90 
     91 
     92 with open(filename, "rb") as file:
     93     file_bytes = file.read()
     94 
     95 # ENSURE HEADER IS 108 BYTES
     96 # SIDE EFFECT FOR ARR
     97 # BYTES TO WRITE IS NUM
     98 # BYTES STR IS DATA
     99 def create_image(arr, header, byte_str, start_idx, bytes_to_write, x):
    100 
    101     written = 0
    102 
    103     offset = 0
    104     for byte in header:
    105         for bit in byte_to_bits(byte):
    106             x_idx = offset % x
    107             y_idx = offset // x
    108             arr[y_idx][x_idx] = 255 if bit else 0
    109             offset += 1
    110             written += 1
    111 
    112     assert offset == 108 * 8
    113 
    114     for i in range(bytes_to_write):
    115         byte = byte_str[start_idx + i]
    116         for bit in byte_to_bits(byte):
    117             x_idx = offset % x
    118             y_idx = offset // x
    119             arr[y_idx][x_idx] = 255 if bit else 0
    120             written += 1
    121             offset += 1
    122     return written
    123 
    124 
    125 index = 0
    126 chunk_num = 1
    127 for chunk_size in bytes_per_chunk:
    128     arr = np.zeros((y,x))
    129     written = create_image(arr, make_header(filename, chunk_num, chunk_size), file_bytes, index, chunk_size, x)
    130     img = Image.fromarray(arr)
    131     img = img.convert('L')
    132     fname = destination + "/" + str(get_rnd_str(16)) + ".png"
    133     print("Writing to: " + fname)
    134     img.save(fname)
    135     index += chunk_size
    136     chunk_num += 1
    137 
    138 assert index == file_size_bytes