PaulinaAa commited on
Commit
72f2543
·
verified ·
1 Parent(s): b5409e4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +143 -96
app.py CHANGED
@@ -1,9 +1,11 @@
1
  import gradio as gr
2
- import requests # Dodajemy od razu, bo będzie potrzebny
3
- from PIL import Image # Dodajemy od razu
4
- import io # Dodajemy od razu
5
- import os # Do odczytu sekretu
6
- import traceback # Do lepszego śledzenia błędów
 
 
7
  from gradio_client import Client, file
8
  import uuid
9
  import shutil
@@ -12,194 +14,236 @@ print("--- Plik app.py - Start ładowania ---")
12
 
13
  # --- Konfiguracja ---
14
  # Odczytanie sekretu (API Token) ustawionego w Space
15
- # PAMIĘTAJ, ABY DODAĆ TEN SEKRET W USTAWIENIACH SPACE'A!
16
- # Settings -> Repository secrets -> New secret
17
- # Name: HUGGINGFACE_API_TOKEN
18
- # Value: <twój skopiowany token API>
19
  API_TOKEN = os.getenv("HUGGINGFACE_API_TOKEN")
20
 
21
  # Sprawdzenie, czy token został załadowany
22
  if not API_TOKEN:
23
- print("!!! OSTRZEŻENIE: Nie znaleziono sekretu HUGGINGFACE_API_TOKEN !!!")
24
- # Można tu rzucić błąd, ale na razie tylko ostrzegamy
25
- # raise ValueError("Nie ustawiono sekretu HUGGINGFACE_API_TOKEN w ustawieniach Space!")
26
-
27
- # Endpoint modelu InstantID na Hugging Face Inference API
28
- API_URL = "https://api-inference.huggingface.co/models/InstantX/InstantID"
29
 
30
  # Profesjonalny prompt, który chcemy uzyskać
31
- # Możesz go później dostosować
32
  LINKEDIN_PROMPT = (
33
  "linkedin professional profile photo, corporate headshot, high quality, realistic photograph, "
34
- "man/woman [choose one or remove] wearing a dark business suit or elegant blouse, plain white background, "
35
  "soft studio lighting, sharp focus, looking at camera, slight smile, natural skin texture"
36
  )
37
 
38
- TARGET_SPACE_URL = "https://huggingface.co/spaces/InstantX/InstantID"
39
- # Uwaga: InstantID może nie być idealny w zmianie ubrań, skupia się na twarzy.
40
- # Prompt pomaga nadać ogólny styl.
 
 
 
 
41
 
42
  # --- Logika aplikacji ---
43
  def generate_photo(input_selfie, current_prompt):
 
44
  print("\n--- Funkcja generate_photo (gradio_client) została wywołana ---")
45
 
46
  # 1. Walidacja danych wejściowych
47
  if input_selfie is None:
48
- print("Błąd: Nie wgrano zdjęcia wejściowego.")
49
- raise gr.Error("Proszę najpierw wgrać swoje selfie!")
50
  if not current_prompt:
51
- print("Błąd: Prompt jest pusty.")
52
- raise gr.Error("Prompt nie może być pusty!")
53
- # Sprawdźmy też token, bo może być potrzebny do połączenia z klientem
54
  if not API_TOKEN:
55
- print("OSTRZEŻENIE: Brak API Tokena. Połączenie z publicznym Space może działać, ale może być ograniczone.")
56
- # Nie rzucamy błędu, spróbujemy połączyć się anonimowo
57
 
58
  print(f"Otrzymano obrazek typu: {type(input_selfie)}, Rozmiar: {input_selfie.size}")
59
  print(f"Otrzymano prompt (początek): {current_prompt[:100]}...")
60
 
61
  # 2. Przygotowanie pliku tymczasowego dla obrazka wejściowego
62
  temp_dir = f"temp_input_{uuid.uuid4()}" # Unikalny folder tymczasowy
63
- os.makedirs(temp_dir, exist_ok=True)
64
- input_image_path = os.path.join(temp_dir, "input_selfie.jpg")
65
  try:
 
 
 
 
66
  # Upewnijmy się, że obraz jest w RGB przed zapisem jako JPEG
 
67
  rgb_image = input_selfie
68
  if input_selfie.mode == 'RGBA':
69
  print("Konwertuję obraz RGBA na RGB z białym tłem przed zapisem...")
 
70
  rgb_image = Image.new("RGB", input_selfie.size, (255, 255, 255))
 
71
  rgb_image.paste(input_selfie, mask=input_selfie.split()[3])
72
 
 
73
  rgb_image.save(input_image_path, format="JPEG")
74
  print(f"Obrazek wejściowy zapisany tymczasowo w: {input_image_path}")
 
75
  except Exception as e:
76
  print(f"BŁĄD podczas zapisywania obrazu tymczasowego: {e}")
77
- traceback.print_exc()
78
- # Spróbuj posprzątać folder tymczasowy
79
- if os.path.exists(temp_dir):
80
- shutil.rmtree(temp_dir)
 
 
 
 
 
81
  raise gr.Error(f"Problem z przygotowaniem obrazu do wysłania: {e}")
82
 
83
  # 3. Wywołanie zdalnego API Gradio za pomocą gradio_client
84
- output_image = None
 
85
  try:
 
86
  print(f"Łączenie z docelowym Space Gradio: {TARGET_SPACE_ID}")
87
- # Inicjalizujemy klienta, przekazując token (może pomóc z limitami)
88
  client = Client(TARGET_SPACE_ID, hf_token=API_TOKEN)
89
- # Możesz spróbować bez tokena, jeśli są problemy: client = Client(TARGET_SPACE_URL)
90
- TARGET_SPACE_ID = "InstantX/InstantID"
91
-
92
- print("Połączono. Próbuję wywołać funkcję predict...")
93
 
94
- # WAŻNE: Musimy zgadnąć lub sprawdzić (np. przez client.view_api() lub patrząc na UI/API zdalnego Space'a),
95
- # jakich dokładnie argumentów i w jakiej kolejności oczekuje funkcja 'predict' w zdalnym Space.
96
- # Poniżej jest ZGADYWANA struktura bazująca na typowym demo InstantID. Może wymagać dostosowania!
97
- # Argumenty: (Twarz, Poza, Prompt, NegPrompt, Styl, Skala CN, Skala IP)
98
- # Używamy `file()` do przekazania ścieżki pliku obrazka.
 
 
 
 
 
 
 
 
 
 
99
 
100
- negative_prompt = "ugly, deformed, noisy, blurry, low contrast, text, signature, watermark, duplicate, multiple people"
101
- style_name = "Realistic" # Lub "(No style)", "Comic book" etc. - sprawdź opcje w docelowym Space!
 
 
 
 
 
 
102
 
 
103
  result = client.predict(
104
- file(input_image_path), # Argument 1: Obraz twarzy (jako plik)
105
- None, # Argument 2: Obraz pozy (opcjonalny, dajemy None)
106
- current_prompt, # Argument 3: Prompt
107
- negative_prompt, # Argument 4: Negative prompt
108
- style_name, # Argument 5: Nazwa stylu
109
- 0.8, # Argument 6: Skala ControlNet (zgadnięta wartość domyślna)
110
- 0.8, # Argument 7: Skala IdentityNet (IP Adapter) (zgadnięta wartość domyślna)
111
- api_name="/generate_image" # Zwykle nazywa się '/predict', ale InstantX używa '/generate_image'
112
- # Sprawdź API docelowego space jeśli to nie działa! Może być "/predict"
113
- # Można sprawdzić programowo: client.view_api(all_endpoints=True) i wydrukować w logach
114
  )
115
 
116
- # `predict` zwraca wynik zgodny z wyjściami zdalnego API.
117
- # Dla InstantID zwykle jest to lista obrazów (nawet jeśli jest jeden).
118
- # Często zwraca ścieżkę do pliku (lub listę ścieżek) zapisanego tymczasowo przez gradio_client.
119
  print(f"Otrzymano wynik od klienta: {type(result)}")
120
- print(f"Wynik (fragment): {str(result)[:500]}") # Wydrukuj fragment wyniku
 
121
 
122
- # Sprawdźmy, czy wynik to lista i czy zawiera ścieżkę do pliku
123
  if isinstance(result, list) and len(result) > 0 and isinstance(result[0], str) and os.path.exists(result[0]):
124
- output_file_path = result[0]
125
- print(f"Wygląda na to, że otrzymano ścieżkę do obrazka: {output_file_path}")
126
- # Wczytaj obrazek wynikowy z tej ścieżki
127
  output_image = Image.open(output_file_path)
128
- print(f"Obrazek wynikowy załadowany. Rozmiar: {output_image.size}")
129
- elif isinstance(result, str) and os.path.exists(result): # Czasem zwraca tylko jedną ścieżkę
 
130
  output_file_path = result
131
- print(f"Wygląda na to, że otrzymano ścieżkę do obrazka: {output_file_path}")
132
  output_image = Image.open(output_file_path)
133
- print(f"Obrazek wynikowy załadowany. Rozmiar: {output_image.size}")
134
  else:
135
- # Jeśli wynik nie jest oczekiwaną ścieżką, zgłoś błąd
136
  print(f"BŁĄD: Otrzymano nieoczekiwany format wyniku od gradio_client: {type(result)}")
137
  raise gr.Error(f"Nie udało się przetworzyć wyniku ze zdalnego API. Otrzymano: {str(result)[:200]}")
138
 
139
  except Exception as e:
 
140
  print(f"BŁĄD podczas komunikacji z klientem Gradio lub przetwarzania wyniku: {e}")
141
- traceback.print_exc()
142
- raise gr.Error(f"Problem podczas generowania przez zdalny serwis InstantID: {e}")
 
 
 
 
 
 
 
 
 
143
 
144
  finally:
145
- # 4. Sprzątanie - usuń folder tymczasowy z plikiem wejściowym
146
- if os.path.exists(temp_dir):
147
  try:
148
  shutil.rmtree(temp_dir)
149
  print(f"Folder tymczasowy {temp_dir} usunięty.")
150
  except Exception as e_clean:
 
151
  print(f"OSTRZEŻENIE: Nie udało się usunąć folderu tymczasowego {temp_dir}: {e_clean}")
152
 
153
- # 5. Zwróć wynik (załadowany obrazek PIL)
154
  if output_image:
 
155
  return output_image
156
  else:
157
- # To nie powinno się zdarzyć, jeśli nie było błędu wcześniej, ale dla pewności:
158
- print("BŁĄD: Brak obrazka wynikowego po zakończeniu funkcji.")
159
- raise gr.Error("Nie udało się uzyskać obrazka wynikowego.")
 
160
 
161
  # --- Budowa Interfejsu Gradio ---
162
  print("--- Definiowanie interfejsu Gradio ---")
 
163
  with gr.Blocks(css="footer {display: none !important}") as demo: # Ukrywamy domyślny footer Gradio
 
164
  gr.Markdown(
165
  """
166
  # Generator Profesjonalnych Zdjęć Profilowych
167
- Wgraj swoje selfie, a my postaramy się stworzyć profesjonalne zdjęcie w stylu LinkedIn!
168
- **Ważne:** Najlepsze efekty uzyskasz wgrywając wyraźne zdjęcie twarzy, patrzącej w miarę prosto, dobrze oświetlonej.
 
 
 
169
  """
170
  )
171
 
 
172
  with gr.Row():
 
173
  with gr.Column(scale=1):
 
174
  input_image = gr.Image(
175
  label="1. Wgraj swoje selfie (JPG/PNG)",
176
  type="pil" # Chcemy obiekt PIL w funkcji Pythona
177
  )
178
- prompt_input = gr.Textbox( # Dodajemy pole na prompt, z domyślną wartością
179
- label="Opcjonalnie: Dostosuj opis zdjęcia (prompt)",
180
- value=LINKEDIN_PROMPT,
181
- lines=4
 
182
  )
183
- generate_button = gr.Button("✨ Generuj Zdjęcie Biznesowe ✨", variant="primary")
 
184
 
 
185
  with gr.Column(scale=1):
 
186
  output_image = gr.Image(
187
  label="Oto Twoje wygenerowane zdjęcie:",
188
  type="pil" # Oczekujemy obiektu PIL jako wynik
189
  )
190
- # Można dodać informację o stanie
191
- status_info = gr.Textbox(label="Status", interactive=False)
192
 
193
  # --- Podłączenie akcji do przycisku ---
194
- # Kiedy przycisk zostanie kliknięty, wywołaj funkcję 'generate_photo'
195
- # Przekaż zawartość 'input_image' jako argument do funkcji
196
- # Wynik funkcji umieść w komponencie 'output_image'
197
  generate_button.click(
198
- fn=generate_photo,
199
- inputs=[input_image, prompt_input], # Lista komponentów wejściowych
200
- outputs=[output_image] # Lista komponentów wyjściowych (na razie jeden)
201
- # inputs=[input_image, prompt_input], # Jeśli chcemy przekazać też prompt
202
- # outputs=[output_image, status_info] # Jeśli chcemy aktualizować też status
203
  )
204
 
205
  print("--- Interfejs Gradio zdefiniowany ---")
@@ -207,5 +251,8 @@ print("--- Interfejs Gradio zdefiniowany ---")
207
  # --- Uruchomienie aplikacji ---
208
  if __name__ == "__main__":
209
  print("--- Uruchamianie demo.launch() ---")
210
- demo.launch() # Uruchamia aplikację Gradio
211
- print("--- demo.launch() zakończone (?) ---") # Ten print może się nie pojawić od razu w logach
 
 
 
 
1
  import gradio as gr
2
+ # requests jest nadal w requirements.txt, ale nie jest już bezpośrednio używany do głównego wywołania API
3
+ import requests
4
+ from PIL import Image
5
+ import io
6
+ import os
7
+ import traceback
8
+ # Poprawny import dla gradio_client
9
  from gradio_client import Client, file
10
  import uuid
11
  import shutil
 
14
 
15
  # --- Konfiguracja ---
16
  # Odczytanie sekretu (API Token) ustawionego w Space
 
 
 
 
17
  API_TOKEN = os.getenv("HUGGINGFACE_API_TOKEN")
18
 
19
  # Sprawdzenie, czy token został załadowany
20
  if not API_TOKEN:
21
+ # Nadal ostrzegamy, ale klient może działać anonimowo
22
+ print("!!! OSTRZEŻENIE: Nie znaleziono sekretu HUGGINGFACE_API_TOKEN. Klient Gradio spróbuje połączyć się anonimowo (mogą obowiązywać limity). !!!")
23
+ else:
24
+ print("--- Sekret HUGGINGFACE_API_TOKEN załadowany. ---")
 
 
25
 
26
  # Profesjonalny prompt, który chcemy uzyskać
 
27
  LINKEDIN_PROMPT = (
28
  "linkedin professional profile photo, corporate headshot, high quality, realistic photograph, "
29
+ "person wearing a dark business suit or elegant blouse, plain white background, " # Usunięto potencjalnie problematyczne tagi
30
  "soft studio lighting, sharp focus, looking at camera, slight smile, natural skin texture"
31
  )
32
 
33
+ # Adres publicznego Space'a z InstantID
34
+ # Używamy identyfikatora Space'a dla gradio_client
35
+ TARGET_SPACE_ID = "InstantX/InstantID"
36
+
37
+ # Kontrolny print, aby sprawdzić, czy zmienna jest ładowana na starcie
38
+ # Powinien pojawić się w logach kontenera po restarcie Space'a
39
+ print(f"--- Konfiguracja załadowana. Cel dla gradio_client: {TARGET_SPACE_ID} ---")
40
 
41
  # --- Logika aplikacji ---
42
  def generate_photo(input_selfie, current_prompt):
43
+ # Ta funkcja jest teraz wywoływana przez Gradio po kliknięciu przycisku
44
  print("\n--- Funkcja generate_photo (gradio_client) została wywołana ---")
45
 
46
  # 1. Walidacja danych wejściowych
47
  if input_selfie is None:
48
+ print("BŁĄD: Nie wgrano zdjęcia wejściowego.")
49
+ raise gr.Error("Proszę najpierw wgrać swoje selfie!") # Pokaż błąd w interfejsie
50
  if not current_prompt:
51
+ print("BŁĄD: Prompt jest pusty.")
52
+ raise gr.Error("Prompt (opis zdjęcia) nie może być pusty!")
53
+ # Informacja o tokenie (nie jest już krytyczny dla działania, ale może wpływać na limity)
54
  if not API_TOKEN:
55
+ print("INFO: Brak API Tokena. Połączenie z publicznym Space jako anonimowy użytkownik.")
 
56
 
57
  print(f"Otrzymano obrazek typu: {type(input_selfie)}, Rozmiar: {input_selfie.size}")
58
  print(f"Otrzymano prompt (początek): {current_prompt[:100]}...")
59
 
60
  # 2. Przygotowanie pliku tymczasowego dla obrazka wejściowego
61
  temp_dir = f"temp_input_{uuid.uuid4()}" # Unikalny folder tymczasowy
62
+ input_image_path = None # Inicjalizacja na wypadek błędu przed przypisaniem
 
63
  try:
64
+ os.makedirs(temp_dir, exist_ok=True)
65
+ # Pełna ścieżka do pliku tymczasowego
66
+ input_image_path = os.path.join(temp_dir, "input_selfie.jpg")
67
+
68
  # Upewnijmy się, że obraz jest w RGB przed zapisem jako JPEG
69
+ # To ważne, bo format JPEG nie obsługuje przezroczystości (kanału alfa)
70
  rgb_image = input_selfie
71
  if input_selfie.mode == 'RGBA':
72
  print("Konwertuję obraz RGBA na RGB z białym tłem przed zapisem...")
73
+ # Stwórz nowy obraz RGB wypełniony białym kolorem
74
  rgb_image = Image.new("RGB", input_selfie.size, (255, 255, 255))
75
+ # Wklej oryginalny obraz używając jego kanału alfa jako maski
76
  rgb_image.paste(input_selfie, mask=input_selfie.split()[3])
77
 
78
+ # Zapisz przetworzony obraz jako JPEG
79
  rgb_image.save(input_image_path, format="JPEG")
80
  print(f"Obrazek wejściowy zapisany tymczasowo w: {input_image_path}")
81
+
82
  except Exception as e:
83
  print(f"BŁĄD podczas zapisywania obrazu tymczasowego: {e}")
84
+ traceback.print_exc() # Pokaż pełny błąd w logach
85
+ # Spróbuj posprzątać folder tymczasowy nawet jeśli zapis się nie udał
86
+ if temp_dir and os.path.exists(temp_dir):
87
+ try:
88
+ shutil.rmtree(temp_dir)
89
+ print(f"Folder tymczasowy {temp_dir} usunięty po błędzie zapisu.")
90
+ except Exception as e_clean:
91
+ print(f"OSTRZEŻENIE: Nie udało się usunąć folderu tymczasowego {temp_dir} po błędzie zapisu: {e_clean}")
92
+ # Pokaż błąd użytkownikowi
93
  raise gr.Error(f"Problem z przygotowaniem obrazu do wysłania: {e}")
94
 
95
  # 3. Wywołanie zdalnego API Gradio za pomocą gradio_client
96
+ output_image = None # Zmienna na wynikowy obrazek
97
+ client = None # Zmienna na obiekt klienta
98
  try:
99
+ # Używamy TARGET_SPACE_ID zdefiniowanej globalnie
100
  print(f"Łączenie z docelowym Space Gradio: {TARGET_SPACE_ID}")
101
+ # Inicjalizujemy klienta, przekazując ID Space'a i token (jeśli jest)
102
  client = Client(TARGET_SPACE_ID, hf_token=API_TOKEN)
 
 
 
 
103
 
104
+ print("Połączono. Próbuję wywołać funkcję na zdalnym Space...")
105
+
106
+ # --- Konfiguracja parametrów dla InstantX/InstantID ---
107
+ # Te wartości mogą wymagać dostosowania w zależności od dokładnej
108
+ # konfiguracji zdalnego Space'a i pożądanych efektów.
109
+ negative_prompt = "ugly, deformed, noisy, blurry, low contrast, text, signature, watermark, duplicate, multiple people, cartoon, drawing, illustration, sketch"
110
+ # Popularne style w demo InstantID: Realistic, (No style), Comic book, Disney, Pixar
111
+ style_name = "Realistic"
112
+ # Skale ControlNet i IP-Adapter (siła tożsamości) - typowe wartości to 0.6-1.0
113
+ cn_scale = 0.8
114
+ ip_scale = 0.8
115
+ # Nazwa endpointu API w zdalnym Space'u (często '/predict' lub specyficzna jak '/generate_image')
116
+ # Dla InstantX/InstantID wydaje się, że to '/generate_image'
117
+ api_endpoint_name = "/generate_image"
118
+ # -------------------------------------------------------
119
 
120
+ print(f"Wywołuję endpoint '{api_endpoint_name}' z parametrami:")
121
+ print(f" Input image path: {input_image_path}")
122
+ # Nie drukujemy całego promptu, bo może być długi
123
+ print(f" Prompt (start): {current_prompt[:60]}...")
124
+ print(f" Negative Prompt (start): {negative_prompt[:60]}...")
125
+ print(f" Style: {style_name}")
126
+ print(f" ControlNet Scale: {cn_scale}")
127
+ print(f" IP-Adapter Scale: {ip_scale}")
128
 
129
+ # Wywołanie funkcji 'predict' na zdalnym kliencie
130
  result = client.predict(
131
+ file(input_image_path), # Obraz twarzy (jako obiekt pliku Gradio)
132
+ None, # Obraz pozy (opcjonalny, dajemy None)
133
+ current_prompt, # Prompt tekstowy
134
+ negative_prompt, # Negatywny prompt
135
+ style_name, # Nazwa stylu
136
+ cn_scale, # Skala ControlNet
137
+ ip_scale, # Skala IP-Adapter (siła tożsamości)
138
+ api_name=api_endpoint_name # Nazwa endpointu API
 
 
139
  )
140
 
141
+ # Przetwarzanie wyniku zwróconego przez klienta
 
 
142
  print(f"Otrzymano wynik od klienta: {type(result)}")
143
+ # Wydrukuj fragment wyniku, aby zobaczyć jego strukturę
144
+ print(f"Wynik (fragment): {str(result)[:500]}")
145
 
146
+ # Sprawdzamy, czy wynik jest listą ścieżek do plików (typowe dla Gradio)
147
  if isinstance(result, list) and len(result) > 0 and isinstance(result[0], str) and os.path.exists(result[0]):
148
+ output_file_path = result[0] # Bierzemy pierwszą ścieżkę z listy
149
+ print(f"Przetwarzam pierwszy obrazek wynikowy ze ścieżki: {output_file_path}")
150
+ # Wczytaj obrazek wynikowy z tej ścieżki za pomocą Pillow
151
  output_image = Image.open(output_file_path)
152
+ print(f"Obrazek wynikowy załadowany pomyślnie. Rozmiar: {output_image.size}")
153
+ # Sprawdzamy, czy wynik jest pojedynczą ścieżką do pliku
154
+ elif isinstance(result, str) and os.path.exists(result):
155
  output_file_path = result
156
+ print(f"Przetwarzam obrazek wynikowy ze ścieżki: {output_file_path}")
157
  output_image = Image.open(output_file_path)
158
+ print(f"Obrazek wynikowy załadowany pomyślnie. Rozmiar: {output_image.size}")
159
  else:
160
+ # Jeśli wynik nie jest ani listą ścieżek, ani pojedynczą ścieżką
161
  print(f"BŁĄD: Otrzymano nieoczekiwany format wyniku od gradio_client: {type(result)}")
162
  raise gr.Error(f"Nie udało się przetworzyć wyniku ze zdalnego API. Otrzymano: {str(result)[:200]}")
163
 
164
  except Exception as e:
165
+ # Obsługa wszelkich błędów podczas komunikacji z klientem lub przetwarzania wyniku
166
  print(f"BŁĄD podczas komunikacji z klientem Gradio lub przetwarzania wyniku: {e}")
167
+ traceback.print_exc() # Pokaż pełny błąd w logach
168
+ # Spróbuj dać użytkownikowi bardziej pomocny komunikat
169
+ error_message = f"Problem podczas generowania przez zdalny serwis InstantID: {e}"
170
+ if "Could not fetch config" in str(e):
171
+ error_message = f"Nie można połączyć się z konfiguracją zdalnego serwisu ({TARGET_SPACE_ID}). Może być chwilowo niedostępny, przeciążony lub wymagać logowania. Spróbuj ponownie później."
172
+ elif "timed out" in str(e).lower():
173
+ error_message = "Przekroczono limit czasu oczekiwania na odpowiedź ze zdalnego serwisu. Może być przeciążony. Spróbuj ponownie."
174
+ elif "queue full" in str(e).lower():
175
+ error_message = "Kolejka w zdalnym serwisie jest pełna. Spróbuj ponownie za chwilę."
176
+ # Pokaż błąd w interfejsie Gradio
177
+ raise gr.Error(error_message)
178
 
179
  finally:
180
+ # 4. Sprzątanie - ZAWSZE próbuj usunąć folder tymczasowy, nawet jeśli był błąd
181
+ if temp_dir and os.path.exists(temp_dir):
182
  try:
183
  shutil.rmtree(temp_dir)
184
  print(f"Folder tymczasowy {temp_dir} usunięty.")
185
  except Exception as e_clean:
186
+ # Tylko ostrzeżenie, jeśli sprzątanie się nie uda
187
  print(f"OSTRZEŻENIE: Nie udało się usunąć folderu tymczasowego {temp_dir}: {e_clean}")
188
 
189
+ # 5. Zwróć wynik (załadowany obrazek PIL), jeśli się udało
190
  if output_image:
191
+ print("Zwracam wygenerowany obrazek do interfejsu Gradio.")
192
  return output_image
193
  else:
194
+ # Ten kod nie powinien zostać osiągnięty, jeśli błędy poprawnie obsługiwane
195
+ print("BŁĄD KRYTYCZNY: Brak obrazka wynikowego po zakończeniu funkcji, a nie zgłoszono błędu.")
196
+ raise gr.Error("Nie udało się uzyskać obrazka wynikowego z nieznanego powodu.")
197
+
198
 
199
  # --- Budowa Interfejsu Gradio ---
200
  print("--- Definiowanie interfejsu Gradio ---")
201
+ # Tworzymy główny blok aplikacji Gradio
202
  with gr.Blocks(css="footer {display: none !important}") as demo: # Ukrywamy domyślny footer Gradio
203
+ # Tytuł i opis aplikacji wyświetlany użytkownikowi
204
  gr.Markdown(
205
  """
206
  # Generator Profesjonalnych Zdjęć Profilowych
207
+ Wgraj swoje selfie, a my (korzystając z modelu InstantID) postaramy się stworzyć profesjonalne zdjęcie w stylu LinkedIn!
208
+ **Wskazówki:**
209
+ * Użyj wyraźnego zdjęcia twarzy, patrzącej w miarę prosto, dobrze oświetlonej.
210
+ * Unikaj zdjęć grupowych, bardzo małych lub z mocno zasłoniętą twarzą.
211
+ * Generowanie może potrwać od 30 sekund do kilku minut, bądź cierpliwy!
212
  """
213
  )
214
 
215
+ # Układ interfejsu w dwóch kolumnach
216
  with gr.Row():
217
+ # Kolumna lewa (wejście)
218
  with gr.Column(scale=1):
219
+ # Komponent do wgrywania obrazka
220
  input_image = gr.Image(
221
  label="1. Wgraj swoje selfie (JPG/PNG)",
222
  type="pil" # Chcemy obiekt PIL w funkcji Pythona
223
  )
224
+ # Pole tekstowe do wpisania promptu (opisu zdjęcia)
225
+ prompt_input = gr.Textbox(
226
+ label="2. Opis pożądanego zdjęcia (prompt)",
227
+ value=LINKEDIN_PROMPT, # Ustawiamy domyślny prompt
228
+ lines=4 # Określamy wysokość pola
229
  )
230
+ # Przycisk uruchamiający generowanie
231
+ generate_button = gr.Button("✨ Generuj Zdjęcie Biznesowe ✨", variant="primary") # Wyróżniony przycisk
232
 
233
+ # Kolumna prawa (wyjście)
234
  with gr.Column(scale=1):
235
+ # Komponent do wyświetlania wyniku (obrazka)
236
  output_image = gr.Image(
237
  label="Oto Twoje wygenerowane zdjęcie:",
238
  type="pil" # Oczekujemy obiektu PIL jako wynik
239
  )
 
 
240
 
241
  # --- Podłączenie akcji do przycisku ---
242
+ # Definiujemy, co ma się stać po kliknięciu przycisku 'generate_button'
 
 
243
  generate_button.click(
244
+ fn=generate_photo, # Wywołaj funkcję 'generate_photo'
245
+ inputs=[input_image, prompt_input], # Przekaż zawartość 'input_image' i 'prompt_input' jako argumenty
246
+ outputs=[output_image] # Wynik funkcji umieść w komponencie 'output_image'
 
 
247
  )
248
 
249
  print("--- Interfejs Gradio zdefiniowany ---")
 
251
  # --- Uruchomienie aplikacji ---
252
  if __name__ == "__main__":
253
  print("--- Uruchamianie demo.launch() ---")
254
+ # Uruchamiamy aplikację Gradio
255
+ # share=False: nie generuj publicznego linku (zalecane dla bezpieczeństwa)
256
+ # debug=False: wyłącz tryb debugowania Gradio (zalecane dla produkcji)
257
+ demo.launch(share=False, debug=False)
258
+ print("--- Aplikacja Gradio zakończyła działanie (jeśli nie jest w trybie ciągłym serwera) ---")