├── Makefile ├── README.md ├── requirements.txt ├── setup.py └── src └── ClipDropSDXL.py /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: docs 2 | init: 3 | python -m pip install --upgrade pip 4 | python -m pip install -r ./requirements.txt --upgrade 5 | python -m pip install build setuptools wheel flake8 --upgrade 6 | build: 7 | python -m build 8 | ci: 9 | python -m flake8 src --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 10 | python -m flake8 src --count --select=E9,F63,F7,F82 --show-source --statistics 11 | python setup.py install -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ClipDropSDXL 2 | 3 | Selenium Wrapper for ClipDrop: Unlocking High-Resolution Text-to-Image Creation with StableDiffusionXL (SDXL) 4 | ## Installation 5 | ``` 6 | python3 -m pip install --upgrade git+https://github.com/fredi-python/ClipDropSDXL.git 7 | ``` 8 | ## Usage 9 | ``` 10 | $ python3 -m ClipDropSDXL --help 11 | usage: ClipDropSDXL.py [-h] [--headless] [--style STYLE] --prompt PROMPT [--output-dir OUTPUT_DIR] [--browser BROWSER] 12 | 13 | options: 14 | -h, --help show this help message and exit 15 | --headless Run Browser in headless mode 16 | --style STYLE Style option, default: no style 17 | --prompt PROMPT Prompt to send to Clipdrop 18 | --output-dir OUTPUT_DIR 19 | Output Directory 20 | --browser BROWSER Browser to use (default: chrome) 21 | ``` 22 | ### Working with styles 23 | **Available styles:**
`anime`, `photographic`, `digital-art`, `comic-book`, `fantasy-art`, `analog-film`, `neonpunk`, `isometric`, `lowpoly`, `origami`, `line-art`, `cinematic`, `3d-model`, `pixel-art` 24 | 25 | ## Usage Examples 26 | 27 | **PHOTOGRAPHIC** 28 | 29 | ``` 30 | python3 -m ClipDropSDXL --prompt 'Car' --style photographic 31 | ``` 32 | 33 | ![170](https://github.com/fredi-python/ClipDropSDXL/assets/83492589/be95eb54-6608-42cd-82fb-1e12f57bbceb) 34 | 35 | ``` 36 | python3 -m ClipDropSDXL --prompt 'landscape of a Japanese garden in autumn' --style photographic 37 | ``` 38 | ![183](https://github.com/fredi-python/ClipDropSDXL/assets/83492589/3c833388-5b23-4194-9f0c-d3d46b53bf2f) 39 | 40 | 41 | 42 | **NEONPUNK** 43 | 44 | ``` 45 | python3 -m ClipDropSDXL --prompt 'Man in hoodie walking away from camera' --style neonpunk 46 | ``` 47 | 48 | ![140](https://github.com/fredi-python/ClipDropSDXL/assets/83492589/a21abb9f-101b-4151-b35a-47fcbb40afb7) 49 | 50 | ``` 51 | python3 -m ClipDropSDXL --prompt 'stunning sunset over a calm beach with palm trees.' --style neonpunk 52 | ``` 53 | ![118](https://github.com/fredi-python/ClipDropSDXL/assets/83492589/caa69965-ac40-4813-bbe2-abeb7b12dfb5) 54 | 55 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | selenium>=4.8.0 2 | argparse -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # setup.py 2 | from setuptools import setup, find_packages 3 | 4 | setup( 5 | name='ClipDropSDXL', 6 | version='0.0.1.4', 7 | packages=find_packages('src'), 8 | package_dir={'': 'src'}, 9 | entry_points={ 10 | 'console_scripts': [ 11 | 'ClipDropSDXL = ClipDropSDXL.main:main' 12 | ] 13 | }, 14 | install_requires=[ 15 | "selenium>=4.8.0", 16 | "argparse" 17 | ], 18 | author='Your Name', 19 | author_email='fredi@mt-oneblock.net', 20 | py_modules=["ClipDropSDXL"], 21 | description='Selenium Wrapper for ClipDrop: Unlocking High-Resolution Text-to-Image Creation with Stable Diffusion XL (SDXL)', 22 | url='https://github.com/fredi-python/ClipDropSDXL', 23 | ) 24 | -------------------------------------------------------------------------------- /src/ClipDropSDXL.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import base64 3 | import os 4 | 5 | from selenium import webdriver 6 | from selenium.webdriver.common.by import By 7 | from selenium.webdriver.support import expected_conditions as EC 8 | from selenium.webdriver.support.ui import WebDriverWait 9 | from selenium.webdriver.chrome.options import Options as ChromeOptions 10 | from selenium.webdriver.firefox.options import Options as FirefoxOptions 11 | 12 | def main(): 13 | parser = argparse.ArgumentParser() 14 | parser.add_argument('--headless', action='store_true', help='Run Browser in headless mode') 15 | parser.add_argument('--style', default='shrink-0', help='Style option') 16 | parser.add_argument('--prompt', help='Prompt to send to Clipdrop', required=True, type=str) 17 | parser.add_argument('--output-dir', help='Output Directory', type=str, default=os.getcwd()+"/outputs") 18 | parser.add_argument('--browser', default='firefox', help='Browser to use (default: firefox)') 19 | args = parser.parse_args() 20 | 21 | 22 | if args.browser.lower() in ['chrome', 'chromium']: 23 | options = ChromeOptions() 24 | elif args.browser.lower() == 'firefox': 25 | options = FirefoxOptions() 26 | else: 27 | raise ValueError("Invalid browser option. Supported options are 'chrome' and 'firefox'.") 28 | 29 | style = args.style 30 | styles = ["no-style", "anime", "photographic", "digital-art", "comic-book", "fantasy-art", "analog-film", "neonpunk", "isometric", "lowpoly", "origami", "line-art", "cinematic", "3d-model", "pixel-art"] 31 | if style not in styles: 32 | raise ValueError(f"Invalid Style option, follow list:\n{styles}") 33 | 34 | options.headless = args.headless 35 | 36 | if args.browser.lower() == 'chrome': 37 | driver = webdriver.Chrome(options=options) 38 | else: 39 | driver = webdriver.Firefox(options=options) 40 | 41 | 42 | driver.get("https://clipdrop.co/stable-diffusion") 43 | 44 | WebDriverWait(driver, 20).until( 45 | EC.element_to_be_clickable((By.CSS_SELECTOR, "button.termly-styles-module-root-f61419:nth-child(3)"))).click() 46 | 47 | input_prompt = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.NAME, "prompt"))) 48 | input_prompt.click() 49 | 50 | input_prompt.send_keys(args.prompt) 51 | 52 | WebDriverWait(driver, 1).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button.md\:flex-none:nth-child(1)"))).click() 53 | 54 | 55 | 56 | WebDriverWait(driver, 2).until(EC.element_to_be_clickable((By.XPATH, f"/html/body/div[1]/div/div[1]/div/main/div/div[2]/section/div/form/div[2]/div/div[2]/div[1]/div/div/div[{styles.index(style)+1}]/label"))).click() 57 | 58 | WebDriverWait(driver, 2).until( 59 | EC.presence_of_element_located((By.CSS_SELECTOR, "button.bg-primary-500:nth-child(2)"))).click() 60 | 61 | def get_file_content_chrome(driver, uri): 62 | result = driver.execute_async_script(""" 63 | var uri = arguments[0]; 64 | var callback = arguments[1]; 65 | var toBase64 = function(buffer){for(var r,n=new Uint8Array(buffer),t=n.length,a=new Uint8Array(4*Math.ceil(t/3)),i=new Uint8Array(64),o=0,c=0;64>c;++c)i[c]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charCodeAt(c);for(c=0;t-t%3>c;c+=3,o+=4)r=n[c]<<16|n[c+1]<<8|n[c+2],a[o]=i[r>>18],a[o+1]=i[r>>12&63],a[o+2]=i[r>>6&63],a[o+3]=i[63&r];return t%3===1?(r=n[t-1],a[o]=i[r>>2],a[o+1]=i[r<<4&63],a[o+2]=61,a[o+3]=61):t%3===2&&(r=(n[t-2]<<8)+n[t-1],a[o]=i[r>>10],a[o+1]=i[r>>4&63],a[o+2]=i[r<<2&63],a[o+3]=61),new TextDecoder("ascii").decode(a)}; 66 | var xhr = new XMLHttpRequest(); 67 | xhr.responseType = 'arraybuffer'; 68 | xhr.onload = function(){ callback(toBase64(xhr.response)) }; 69 | xhr.onerror = function(){ callback(xhr.status) }; 70 | xhr.open('GET', uri); 71 | xhr.send(); 72 | """, uri) 73 | if type(result) == int: 74 | raise Exception("Request failed with status %s" % result) 75 | return base64.b64decode(result) 76 | 77 | 78 | output_dir = args.output_dir 79 | os.makedirs(output_dir, exist_ok=True) 80 | 81 | image_elements = [WebDriverWait(driver, 20).until( 82 | EC.presence_of_element_located((By.CSS_SELECTOR, "div.rounded-md:nth-child(2) > img:nth-child(1)"))), 83 | WebDriverWait(driver, 20).until( 84 | EC.presence_of_element_located((By.CSS_SELECTOR, "div.rounded-md:nth-child(3) > img:nth-child(1)"))), 85 | WebDriverWait(driver, 20).until( 86 | EC.presence_of_element_located((By.CSS_SELECTOR, "div.rounded-md:nth-child(4) > img:nth-child(1)"))), 87 | WebDriverWait(driver, 20).until( 88 | EC.presence_of_element_located((By.CSS_SELECTOR, "div.rounded-md:nth-child(5) > img:nth-child(1)")))] 89 | 90 | for i, image_element in enumerate(image_elements): 91 | image_url = image_element.get_attribute('src') 92 | bytes = get_file_content_chrome(driver, image_url) 93 | file_name = f"{i}.jpg" 94 | file_path = os.path.join(output_dir, file_name) 95 | 96 | if os.path.exists(file_path): 97 | # Find the next available number 98 | count = 1 99 | while os.path.exists(os.path.join(output_dir, f"{i + count}.jpg")): 100 | count += 1 101 | file_name = f"{i + count}.jpg" 102 | file_path = os.path.join(output_dir, file_name) 103 | 104 | with open(file_path, 'wb') as file: 105 | file.write(bytes) 106 | 107 | driver.quit() 108 | 109 | if __name__ == "__main__": 110 | main() --------------------------------------------------------------------------------