diff --git a/LICENSE b/LICENSE index d0ce461..2bbeabd 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2023 Vladislav Zenkevich +Copyright (c) 2024 Vladislav Zenkevich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.MD b/README.MD index 7e9277b..e6256f2 100644 --- a/README.MD +++ b/README.MD @@ -11,8 +11,8 @@ This module uses web scraping and engineering techniques to interface with Bing' ### 🔑 Key Features - 🖼️ **Generate images** by providing a text prompt -- 📸 **Get image URLs** - up to 4 generated images -- 🔐 **Authentication** via saved Bing cookies +- 📸 **Get image URLs** up to 4 generated images +- 🔐 **Authentication** via saved Bing cookies or auto-fetched from browsers - ⚠️ **Custom exceptions** for common issues ## 💻 Usage @@ -22,12 +22,28 @@ Import and instantiate the `BingArt` class with a valid `_U` cookie value: ```python from bingart import BingArt -bing = BingArt(auth_cookie_U='...') +bing_art = BingArt(auth_cookie_U='...') -# in some cases, `KievRPSSecAuth` cookie might be needed as well -bing = BingArt(auth_cookie_U='...', auth_cookie_KievRPSSecAuth='...') +try: + results = bing_art.generate_images('sunset') + print(results) +finally: + bing_art.close_session() ``` +### Sometimes an extra cookie called `KievRPSSecAuth` is required for it to work properly + +```python +bing_art = BingArt(auth_cookie_U='...', auth_cookie_KievRPSSecAuth='...') +``` + +### Also, you can try the auto cookie search feature + +```python +bing_art = BingArt(auto=True) +``` + + Call `generate_images()` with your query text: ```python diff --git a/bingart/bingart.py b/bingart/bingart.py index c606552..b4150a8 100644 --- a/bingart/bingart.py +++ b/bingart/bingart.py @@ -1,6 +1,8 @@ import requests import re import time +import psutil +import browser_cookie_3x as bc from urllib.parse import urlencode class AuthCookieError(Exception): @@ -10,66 +12,117 @@ class PromptRejectedError(Exception): pass class BingArt: - def __init__(self, auth_cookie_U, auth_cookie_KievRPSSecAuth=''): - self.auth_cookie_U = auth_cookie_U - self.auth_cookie_KievRPSSecAuth = auth_cookie_KievRPSSecAuth - - def generate_images(self, query): - encoded_query = urlencode({'q': query}) - - cookie_str = f'_U={self.auth_cookie_U}; KievRPSSecAuth={self.auth_cookie_KievRPSSecAuth}' - if self.auth_cookie_KievRPSSecAuth == '': - cookie_str = f'_U={self.auth_cookie_U}' - - headers = { - 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1474.0', - 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', - 'Referer': 'https://www.bing.com/images/create', - 'Accept-Language': 'en-US;q=0.6,en;q=0.5', + browser_procs = { + bc.chrome: 'chrome.exe', + bc.yandex: 'browser.exe', + bc.firefox: 'firefox.exe', + bc.edge: 'msedge.exe', + bc.opera: 'launcher.exe', + bc.opera_gx: 'launcher.exe' + } + + def __init__(self, auth_cookie_U, auth_cookie_KievRPSSecAuth=None, auto=False): + self.session = requests.Session() + self.base_url = 'https://www.bing.com/images/create' + + if auto: + self.auth_cookie_U, self.auth_cookie_KievRPSSecAuth = self.get_auth_cookies() + else: + self.auth_cookie_U = auth_cookie_U + self.auth_cookie_KievRPSSecAuth = auth_cookie_KievRPSSecAuth + + self.headers = self._prepare_headers() + + def kill_proc(self, proc): + for process in psutil.process_iter(['name']): + if process.info['name'] == proc: + try: + process.kill() + except: + pass + + def scan_cookies(self, cookies): + auth_cookie_U = auth_cookie_KievRPSSecAuth = None + for cookie in cookies: + if cookie.domain == '.bing.com': + if cookie.name == '_U': + auth_cookie_U = cookie.value + elif cookie.name == 'KievRPSSecAuth': + auth_cookie_KievRPSSecAuth = cookie.value + return auth_cookie_U, auth_cookie_KievRPSSecAuth + + def get_auth_cookies(self, after_check=False): + for browser in self.browser_procs: + try: + cookies = browser() + auth_cookie_U, auth_cookie_KievRPSSecAuth = self.scan_cookies(cookies) + if auth_cookie_U: + return auth_cookie_U, auth_cookie_KievRPSSecAuth + except PermissionError as e: + if after_check: + self.kill_proc(self.browser_procs[browser]) + cookies = browser() + auth_cookie_U, auth_cookie_KievRPSSecAuth = self.scan_cookies(cookies) + if auth_cookie_U: + return auth_cookie_U, auth_cookie_KievRPSSecAuth + except Exception as e: + pass + if not after_check: + return self.get_auth_cookies(True) + raise AuthCookieError('Failed to fetch authentication cookies automatically.') + + def _prepare_headers(self): + cookie_str = '' + if self.auth_cookie_U: + cookie_str += f'_U={self.auth_cookie_U};' + if self.auth_cookie_KievRPSSecAuth: + cookie_str += f' KievRPSSecAuth={self.auth_cookie_KievRPSSecAuth};' + + return { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', + 'Referer': self.base_url, + 'Accept-Language': 'en-US,en;q=0.9', 'Cookie': cookie_str } - data = { - 'q': query, - 'qs': 'ds' - } - - with requests.Session() as session: - session.headers.update(headers) - - r = session.get('https://www.bing.com/images/create') - - try: - coins = int(r.text.split('bal" aria-label="')[1].split(' ')[0]) - except IndexError: - raise AuthCookieError('Auth cookie failed!') - - url = f'https://www.bing.com/images/create?{encoded_query}&rt=' + def _get_balance(self): + response = self.session.get(self.base_url) + try: + coins = int(re.search('bal" aria-label="(\d+) ', response.text).group(1)) + except AttributeError: + raise AuthCookieError('Auth cookie failed!') + return coins + + def _fetch_images(self, encoded_query, ID, IG): + images = [] + while True: + response = self.session.get(f'{self.base_url}/async/results/{ID}?{encoded_query}&IG={IG}&IID=images.as'.replace('&nfy=1', '')) + if 'text/css' in response.text: + src_urls = re.findall(r'src="([^"]+)"', response.text) + for src_url in src_urls: + if '?' in src_url: + clean_url = src_url.split('?')[0] + '?pid=ImgGn' + images.append({'url': clean_url}) + return images + time.sleep(5) + + def generate_images(self, query): + encoded_query = urlencode({'q': query}) + self.session.headers.update(self.headers) + coins = self._get_balance() rt = '4' if coins > 0 else '3' - url += rt - url += '&FORM=GENCRE' - - with requests.Session() as session: - session.headers = headers - r = session.post(url, data=data) - - try: - ID = r.text.split(';id=')[1].split('"')[0] - except IndexError: - raise PromptRejectedError('Error! Your prompt has been rejected for ethical reasons.') - - IG = r.text.split('IG:"')[1].split('"')[0] + creation_url = f'{self.base_url}?{encoded_query}&rt={rt}&FORM=GENCRE' - while True: - r = session.get(f'https://www.bing.com/images/create/async/results/{ID}?{encoded_query}&IG={IG}&IID=images.as'.replace('&nfy=1', '')) - if 'text/css' in r.text: - break - time.sleep(5) + response = self.session.post(creation_url, data={'q': query}) + try: + ID = re.search(';id=([^"]+)"', response.text).group(1) + IG = re.search('IG:"([^"]+)"', response.text).group(1) + except AttributeError: + raise PromptRejectedError('Error! Your prompt has been rejected for ethical reasons.') - src_urls = re.findall(r'src="([^"]+)"', r.text) - src_urls = [url for url in src_urls if '?' in url] + images = self._fetch_images(encoded_query, ID, IG) + return {'images': images, 'prompt': query} - for i, src_url in enumerate(src_urls): - new_url = src_url.replace(src_url.split('?')[1], 'pid=ImgGn') - src_urls[i] = new_url - return {'images': [{'url': src_url} for src_url in src_urls], 'prompt': query} \ No newline at end of file + def close_session(self): + self.session.close() \ No newline at end of file diff --git a/setup.py b/setup.py index 78f513f..06570ef 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name='bingart', - version='1.0.2', + version='1.0.3', author='Maehdakvan', author_email='visitanimation@google.com', description='bingart is an unofficial API wrapper for Bing Image Creator (based on DALL-E 3).', @@ -22,6 +22,6 @@ ], packages=find_packages(), include_package_data = True, - install_requires = ['requests'], + install_requires = ['requests', 'browser-cookie-3x', 'psutil'], python_requires='>=3.6' ) \ No newline at end of file