Skip to content

Commit

Permalink
v1.0.3
Browse files Browse the repository at this point in the history
  • Loading branch information
DedInc committed Jan 26, 2024
1 parent a697614 commit 5119cc9
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 64 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -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
Expand Down
26 changes: 21 additions & 5 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
165 changes: 109 additions & 56 deletions bingart/bingart.py
Original file line number Diff line number Diff line change
@@ -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):
Expand All @@ -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}
def close_session(self):
self.session.close()
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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).',
Expand All @@ -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'
)

0 comments on commit 5119cc9

Please sign in to comment.