Spaces:
Sleeping
Sleeping
Commit
·
be8708a
1
Parent(s):
a3a9b2c
init prod test
Browse files- .idea/.gitignore +3 -0
- .idea/Siloshot-Generator.iml +8 -0
- .idea/inspectionProfiles/Project_Default.xml +20 -0
- .idea/inspectionProfiles/profiles_settings.xml +6 -0
- .idea/misc.xml +7 -0
- .idea/modules.xml +8 -0
- .idea/vcs.xml +6 -0
- Dockerfile +43 -0
- app.py +110 -0
- requirements.txt +14 -0
.idea/.gitignore
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
# Default ignored files
|
2 |
+
/shelf/
|
3 |
+
/workspace.xml
|
.idea/Siloshot-Generator.iml
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<module type="PYTHON_MODULE" version="4">
|
3 |
+
<component name="NewModuleRootManager">
|
4 |
+
<content url="file://$MODULE_DIR$" />
|
5 |
+
<orderEntry type="inheritedJdk" />
|
6 |
+
<orderEntry type="sourceFolder" forTests="false" />
|
7 |
+
</component>
|
8 |
+
</module>
|
.idea/inspectionProfiles/Project_Default.xml
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<component name="InspectionProjectProfileManager">
|
2 |
+
<profile version="1.0">
|
3 |
+
<option name="myName" value="Project Default" />
|
4 |
+
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
5 |
+
<option name="ignoredPackages">
|
6 |
+
<value>
|
7 |
+
<list size="7">
|
8 |
+
<item index="0" class="java.lang.String" itemvalue="huggingface_hub" />
|
9 |
+
<item index="1" class="java.lang.String" itemvalue="glob2" />
|
10 |
+
<item index="2" class="java.lang.String" itemvalue="playwright" />
|
11 |
+
<item index="3" class="java.lang.String" itemvalue="uvicorn" />
|
12 |
+
<item index="4" class="java.lang.String" itemvalue="fastapi" />
|
13 |
+
<item index="5" class="java.lang.String" itemvalue="pydantic" />
|
14 |
+
<item index="6" class="java.lang.String" itemvalue="matplotlib" />
|
15 |
+
</list>
|
16 |
+
</value>
|
17 |
+
</option>
|
18 |
+
</inspection_tool>
|
19 |
+
</profile>
|
20 |
+
</component>
|
.idea/inspectionProfiles/profiles_settings.xml
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<component name="InspectionProjectProfileManager">
|
2 |
+
<settings>
|
3 |
+
<option name="USE_PROJECT_PROFILE" value="false" />
|
4 |
+
<version value="1.0" />
|
5 |
+
</settings>
|
6 |
+
</component>
|
.idea/misc.xml
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<project version="4">
|
3 |
+
<component name="Black">
|
4 |
+
<option name="sdkName" value="Python 3.12" />
|
5 |
+
</component>
|
6 |
+
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12" project-jdk-type="Python SDK" />
|
7 |
+
</project>
|
.idea/modules.xml
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<project version="4">
|
3 |
+
<component name="ProjectModuleManager">
|
4 |
+
<modules>
|
5 |
+
<module fileurl="file://$PROJECT_DIR$/.idea/Siloshot-Generator.iml" filepath="$PROJECT_DIR$/.idea/Siloshot-Generator.iml" />
|
6 |
+
</modules>
|
7 |
+
</component>
|
8 |
+
</project>
|
.idea/vcs.xml
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<project version="4">
|
3 |
+
<component name="VcsDirectoryMappings">
|
4 |
+
<mapping directory="" vcs="Git" />
|
5 |
+
</component>
|
6 |
+
</project>
|
Dockerfile
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.9
|
2 |
+
|
3 |
+
# Install necessary dependencies
|
4 |
+
RUN apt-get update && apt-get install -y \
|
5 |
+
libnss3 \
|
6 |
+
libnspr4 \
|
7 |
+
libatk1.0-0 \
|
8 |
+
libatk-bridge2.0-0 \
|
9 |
+
libcups2 \
|
10 |
+
libatspi2.0-0 \
|
11 |
+
libxcomposite1 \
|
12 |
+
libxdamage1 \
|
13 |
+
libxrandr2 \
|
14 |
+
libgbm-dev \
|
15 |
+
libgtk-3-0 \
|
16 |
+
xdg-utils \
|
17 |
+
&& rm -rf /var/lib/apt/lists/*
|
18 |
+
|
19 |
+
# Install Python dependencies
|
20 |
+
RUN pip install --upgrade pip
|
21 |
+
COPY requirements.txt .
|
22 |
+
RUN pip install -r requirements.txt
|
23 |
+
|
24 |
+
# Install Playwright and download the required browsers
|
25 |
+
RUN playwright install --with-deps
|
26 |
+
|
27 |
+
# Create and switch to a non-root user
|
28 |
+
RUN useradd -m -u 1000 user
|
29 |
+
USER user
|
30 |
+
ENV PATH="/home/user/.local/bin:$PATH"
|
31 |
+
|
32 |
+
# Ensure Playwright browsers are installed for the non-root user
|
33 |
+
RUN playwright install
|
34 |
+
RUN pip install python-multipart
|
35 |
+
|
36 |
+
# Set working directory
|
37 |
+
WORKDIR /app
|
38 |
+
|
39 |
+
# Copy application code
|
40 |
+
COPY --chown=user . /app
|
41 |
+
|
42 |
+
# Run the application
|
43 |
+
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
|
app.py
ADDED
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import re
|
3 |
+
import io
|
4 |
+
import zipfile
|
5 |
+
import tempfile
|
6 |
+
import requests
|
7 |
+
from fastapi import FastAPI, HTTPException, Form
|
8 |
+
from fastapi.responses import StreamingResponse
|
9 |
+
from playwright.async_api import async_playwright
|
10 |
+
from bs4 import BeautifulSoup
|
11 |
+
|
12 |
+
app = FastAPI()
|
13 |
+
|
14 |
+
@app.get("/")
|
15 |
+
async def read_root():
|
16 |
+
return {"message": "Hello, World!"}
|
17 |
+
|
18 |
+
|
19 |
+
async def login(page, username, password):
|
20 |
+
await page.goto("https://portal.intiaro.com/login?configuratorVersion=2.5")
|
21 |
+
await page.fill('input[name="userName"]', username)
|
22 |
+
await page.fill('input[name="password"]', password)
|
23 |
+
await page.click('button[type=submit]')
|
24 |
+
await page.wait_for_timeout(2000) # Wait for navigation after login
|
25 |
+
|
26 |
+
|
27 |
+
async def siloshot_making(page):
|
28 |
+
await page.wait_for_timeout(5000) # Wait for the page to fully load
|
29 |
+
html = await page.content()
|
30 |
+
soup = BeautifulSoup(html, 'html.parser')
|
31 |
+
slider = soup.find('img', class_='slider-image')
|
32 |
+
img = slider.get('src')
|
33 |
+
return img
|
34 |
+
|
35 |
+
|
36 |
+
def generate_urls(img, start_angle=0, end_angle=360, step=10):
|
37 |
+
angle_pattern = re.compile(r'(angle/)(\d+)')
|
38 |
+
match = angle_pattern.search(img)
|
39 |
+
if match:
|
40 |
+
base_url = img[:match.start(2)] # Base URL up to the angle value
|
41 |
+
|
42 |
+
urls = []
|
43 |
+
for angle in range(start_angle, end_angle + 1, step):
|
44 |
+
new_url = re.sub(r'angle/\d+', f'angle/{angle}', img)
|
45 |
+
urls.append(new_url)
|
46 |
+
return urls
|
47 |
+
|
48 |
+
|
49 |
+
async def download_images(img_urls, product_name):
|
50 |
+
angle_pattern = re.compile(r'angle/(\d+)')
|
51 |
+
count_img_downloaded = 0
|
52 |
+
image_paths = []
|
53 |
+
|
54 |
+
with tempfile.TemporaryDirectory() as tmpdirname:
|
55 |
+
for img_url in img_urls:
|
56 |
+
try:
|
57 |
+
img_response = requests.get(img_url)
|
58 |
+
img_response.raise_for_status()
|
59 |
+
angle_match = angle_pattern.search(img_url)
|
60 |
+
if angle_match:
|
61 |
+
angle_value = angle_match.group(1)
|
62 |
+
img_name = f"{product_name}_{angle_value}.png"
|
63 |
+
else:
|
64 |
+
img_name = "product_unknown.jpg"
|
65 |
+
img_path = os.path.join(tmpdirname, img_name)
|
66 |
+
image_paths.append(img_path)
|
67 |
+
with open(img_path, 'wb') as file:
|
68 |
+
file.write(img_response.content)
|
69 |
+
count_img_downloaded += 1
|
70 |
+
except Exception as e:
|
71 |
+
print(f'Failed to download {img_url}: {e}')
|
72 |
+
|
73 |
+
zip_buffer = io.BytesIO()
|
74 |
+
with zipfile.ZipFile(zip_buffer, 'w') as zipf:
|
75 |
+
for img_path in image_paths:
|
76 |
+
zipf.write(img_path, os.path.basename(img_path))
|
77 |
+
|
78 |
+
zip_buffer.seek(0)
|
79 |
+
return zip_buffer
|
80 |
+
|
81 |
+
|
82 |
+
async def process_images(username, password, product_url, product_name):
|
83 |
+
async with async_playwright() as playwright:
|
84 |
+
browser = await playwright.chromium.launch(headless=True)
|
85 |
+
context = await browser.new_context()
|
86 |
+
page = await context.new_page()
|
87 |
+
await login(page, username, password)
|
88 |
+
await page.goto(product_url)
|
89 |
+
img = await siloshot_making(page)
|
90 |
+
img_urls = generate_urls(img, start_angle=0, end_angle=360, step=10)
|
91 |
+
zip_buffer = await download_images(img_urls, product_name)
|
92 |
+
await browser.close()
|
93 |
+
return zip_buffer
|
94 |
+
|
95 |
+
|
96 |
+
@app.post("/generate-images/")
|
97 |
+
async def generate_images(username: str = Form(...), password: str = Form(...), product_url: str = Form(...),
|
98 |
+
product_name: str = Form(...)):
|
99 |
+
if not (username and password and product_url and product_name):
|
100 |
+
raise HTTPException(status_code=400, detail="All input fields are required.")
|
101 |
+
|
102 |
+
zip_buffer = await process_images(username, password, product_url, product_name)
|
103 |
+
return StreamingResponse(zip_buffer, media_type="application/zip",
|
104 |
+
headers={"Content-Disposition": f"attachment; filename={product_name}_images.zip"})
|
105 |
+
|
106 |
+
|
107 |
+
if __name__ == "__main__":
|
108 |
+
import uvicorn
|
109 |
+
|
110 |
+
uvicorn.run(app, host="0.0.0.0", port=7860)
|
requirements.txt
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
aiofiles==23.2.1
|
2 |
+
beautifulsoup4==4.12.3
|
3 |
+
fastapi==0.95.2
|
4 |
+
packaging==24.1
|
5 |
+
pandas==2.2.2
|
6 |
+
playwright==1.39.0
|
7 |
+
pydantic==1.8.2
|
8 |
+
python-multipart==0.0.9
|
9 |
+
requests==2.32.3
|
10 |
+
urllib3==2.2.2
|
11 |
+
uvicorn==0.22.0
|
12 |
+
watchfiles==0.22.0
|
13 |
+
websockets==12.0
|
14 |
+
glob2==0.7
|