├── 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 | 
34 |
35 | ```
36 | python3 -m ClipDropSDXL --prompt 'landscape of a Japanese garden in autumn' --style photographic
37 | ```
38 | 
39 |
40 |
41 |
42 | **NEONPUNK**
43 |
44 | ```
45 | python3 -m ClipDropSDXL --prompt 'Man in hoodie walking away from camera' --style neonpunk
46 | ```
47 |
48 | 
49 |
50 | ```
51 | python3 -m ClipDropSDXL --prompt 'stunning sunset over a calm beach with palm trees.' --style neonpunk
52 | ```
53 | 
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()
--------------------------------------------------------------------------------