sync-image-gen 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ GOOGLE_API_KEY=your_gemini_api_key_here
2
+ WATCH_DIRECTORY=./images
3
+ OUTPUT_DIRECTORY=./processed
4
+ GEMINI_PROMPT="將圖片改為動漫風格"
5
+ GEMINI_MODEL="gemini-3-pro-image-preview"
@@ -0,0 +1,4 @@
1
+ .venv/
2
+ .env
3
+ .DS_Store
4
+ **/*.pyc
@@ -0,0 +1 @@
1
+ 3.10
@@ -0,0 +1,95 @@
1
+ Metadata-Version: 2.4
2
+ Name: sync-image-gen
3
+ Version: 0.1.0
4
+ Summary: Sync directory monitor with Nano Banana Pro (Gemini) image processing and full-screen display
5
+ Project-URL: Homepage, https://github.com/derekhsu/sync-image-gen
6
+ Author: Derek Hsu
7
+ License: MIT
8
+ Keywords: directory-monitor,gemini,image-processing,nano-banana,watchdog
9
+ Requires-Python: >=3.10
10
+ Requires-Dist: google-genai>=0.1.0
11
+ Requires-Dist: pillow>=10.0.0
12
+ Requires-Dist: python-dotenv>=1.0.0
13
+ Requires-Dist: watchdog>=4.0.0
14
+ Description-Content-Type: text/markdown
15
+
16
+ # Sync Image Gen (Nano Banana Pro)
17
+
18
+ 這是一個基於 Python 的自動化工具,專為目錄監視與 AI 圖片風格轉換而設計。它會監視特定目錄中的新圖片,透過 Google 最新一代的 **Nano Banana Pro (Gemini 3 Pro Image)** 模型進行風格化處理,並將處理後的結果全螢幕展示。
19
+
20
+ ## 🌟 核心功能
21
+
22
+ - **自動目錄監控**:使用 `watchdog` 實時監控新產生的圖片。
23
+ - **Nano Banana Pro 整合**:串接最新 Gemini 3 Pro 影像模型,支援精確的風格轉換(如動漫、賽博龐克等)。
24
+ - **跨平台全螢幕展示**:支援 macOS、Ubuntu Desktop 與 Windows,處理完成後自動彈出全螢幕視窗。
25
+ - **無干擾模式**:啟動後視窗預設隱藏,僅在有新圖片時彈出;按 `ESC` 隱藏視窗但不結束程式。
26
+ - **高度可配置**:透過環境變數或啟動參數靈活調整路徑、Prompt 與模型版本。
27
+
28
+ ## 🚀 快速開始
29
+
30
+ ### 1. 安裝環境
31
+ 本專案建議使用 [uv](https://github.com/astral-sh/uv) 進行管理:
32
+
33
+ ```bash
34
+ # 安裝依賴並建立虛擬環境
35
+ uv sync
36
+ ```
37
+
38
+ **Ubuntu 使用者注意:**
39
+ Linux 系統需額外安裝 tkinter 支持:
40
+ ```bash
41
+ sudo apt-get install python3-tk
42
+ ```
43
+
44
+ ### 2. 配置環境變數
45
+ 複製 `.env.example` 並更名為 `.env`,填入您的 API Key:
46
+
47
+ ```bash
48
+ cp .env.example .env
49
+ ```
50
+
51
+ `.env` 內容說明:
52
+ - `GOOGLE_API_KEY`: 您的 Google Gemini API Key。
53
+ - `GEMINI_PROMPT`: 轉換圖片的指令(例如:「將圖片轉換為動漫風格」)。
54
+ - `GEMINI_MODEL`: 模型名稱(預設為 `gemini-3-pro-image-preview`)。
55
+ - `WATCH_DIRECTORY`: 監視路徑。
56
+ - `OUTPUT_DIRECTORY`: 處理後圖片儲存路徑。
57
+
58
+ ### 3. 執行程式
59
+
60
+ **開發模式執行:**
61
+ ```bash
62
+ uv run sync-image-gen
63
+ ```
64
+
65
+ **測試模式(僅複製不呼叫 API):**
66
+ ```bash
67
+ uv run sync-image-gen -t
68
+ ```
69
+
70
+ **使用 uvx 執行:**
71
+ ```bash
72
+ uvx --from . sync-image-gen
73
+ ```
74
+
75
+ ## 🛠️ 命令列參數 (CLI Arguments)
76
+
77
+ 啟動參數的優先權高於 `.env` 設定。
78
+
79
+ | 參數 | 說明 | 預設值 |
80
+ | :--- | :--- | :--- |
81
+ | `-h, --help` | 顯示說明訊息 | - |
82
+ | `--watch-dir` | 指定監視目錄 | `./images` |
83
+ | `--output-dir` | 指定處理後圖片儲存目錄 | `./processed` |
84
+ | `-t, --test` | 啟用測試模式(不呼叫 Gemini API) | `False` |
85
+
86
+ ## ⌨️ 快捷鍵
87
+
88
+ - `ESC`: 隱藏全螢幕視窗(程式繼續在背景監視)。
89
+ - `Ctrl + C` (在終端機): 徹底停止程式與監視任務。
90
+
91
+ ## 📋 系統要求
92
+
93
+ - Python 3.10+
94
+ - 穩定且具備 Imagen 功能的 Google Gemini API Key。
95
+ - 網路連線(用於呼叫 Gemini API)。
@@ -0,0 +1,80 @@
1
+ # Sync Image Gen (Nano Banana Pro)
2
+
3
+ 這是一個基於 Python 的自動化工具,專為目錄監視與 AI 圖片風格轉換而設計。它會監視特定目錄中的新圖片,透過 Google 最新一代的 **Nano Banana Pro (Gemini 3 Pro Image)** 模型進行風格化處理,並將處理後的結果全螢幕展示。
4
+
5
+ ## 🌟 核心功能
6
+
7
+ - **自動目錄監控**:使用 `watchdog` 實時監控新產生的圖片。
8
+ - **Nano Banana Pro 整合**:串接最新 Gemini 3 Pro 影像模型,支援精確的風格轉換(如動漫、賽博龐克等)。
9
+ - **跨平台全螢幕展示**:支援 macOS、Ubuntu Desktop 與 Windows,處理完成後自動彈出全螢幕視窗。
10
+ - **無干擾模式**:啟動後視窗預設隱藏,僅在有新圖片時彈出;按 `ESC` 隱藏視窗但不結束程式。
11
+ - **高度可配置**:透過環境變數或啟動參數靈活調整路徑、Prompt 與模型版本。
12
+
13
+ ## 🚀 快速開始
14
+
15
+ ### 1. 安裝環境
16
+ 本專案建議使用 [uv](https://github.com/astral-sh/uv) 進行管理:
17
+
18
+ ```bash
19
+ # 安裝依賴並建立虛擬環境
20
+ uv sync
21
+ ```
22
+
23
+ **Ubuntu 使用者注意:**
24
+ Linux 系統需額外安裝 tkinter 支持:
25
+ ```bash
26
+ sudo apt-get install python3-tk
27
+ ```
28
+
29
+ ### 2. 配置環境變數
30
+ 複製 `.env.example` 並更名為 `.env`,填入您的 API Key:
31
+
32
+ ```bash
33
+ cp .env.example .env
34
+ ```
35
+
36
+ `.env` 內容說明:
37
+ - `GOOGLE_API_KEY`: 您的 Google Gemini API Key。
38
+ - `GEMINI_PROMPT`: 轉換圖片的指令(例如:「將圖片轉換為動漫風格」)。
39
+ - `GEMINI_MODEL`: 模型名稱(預設為 `gemini-3-pro-image-preview`)。
40
+ - `WATCH_DIRECTORY`: 監視路徑。
41
+ - `OUTPUT_DIRECTORY`: 處理後圖片儲存路徑。
42
+
43
+ ### 3. 執行程式
44
+
45
+ **開發模式執行:**
46
+ ```bash
47
+ uv run sync-image-gen
48
+ ```
49
+
50
+ **測試模式(僅複製不呼叫 API):**
51
+ ```bash
52
+ uv run sync-image-gen -t
53
+ ```
54
+
55
+ **使用 uvx 執行:**
56
+ ```bash
57
+ uvx --from . sync-image-gen
58
+ ```
59
+
60
+ ## 🛠️ 命令列參數 (CLI Arguments)
61
+
62
+ 啟動參數的優先權高於 `.env` 設定。
63
+
64
+ | 參數 | 說明 | 預設值 |
65
+ | :--- | :--- | :--- |
66
+ | `-h, --help` | 顯示說明訊息 | - |
67
+ | `--watch-dir` | 指定監視目錄 | `./images` |
68
+ | `--output-dir` | 指定處理後圖片儲存目錄 | `./processed` |
69
+ | `-t, --test` | 啟用測試模式(不呼叫 Gemini API) | `False` |
70
+
71
+ ## ⌨️ 快捷鍵
72
+
73
+ - `ESC`: 隱藏全螢幕視窗(程式繼續在背景監視)。
74
+ - `Ctrl + C` (在終端機): 徹底停止程式與監視任務。
75
+
76
+ ## 📋 系統要求
77
+
78
+ - Python 3.10+
79
+ - 穩定且具備 Imagen 功能的 Google Gemini API Key。
80
+ - 網路連線(用於呼叫 Gemini API)。
@@ -0,0 +1,2 @@
1
+ [tools]
2
+ python = "3.13"
@@ -0,0 +1,27 @@
1
+ [project]
2
+ name = "sync-image-gen"
3
+ version = "0.1.0"
4
+ description = "Sync directory monitor with Nano Banana Pro (Gemini) image processing and full-screen display"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Derek Hsu" }
8
+ ]
9
+ license = { text = "MIT" }
10
+ keywords = ["gemini", "nano-banana", "image-processing", "directory-monitor", "watchdog"]
11
+ requires-python = ">=3.10"
12
+ dependencies = [
13
+ "watchdog>=4.0.0",
14
+ "google-genai>=0.1.0",
15
+ "pillow>=10.0.0",
16
+ "python-dotenv>=1.0.0",
17
+ ]
18
+
19
+ [project.urls]
20
+ Homepage = "https://github.com/derekhsu/sync-image-gen"
21
+
22
+ [project.scripts]
23
+ sync-image-gen = "sync_image_gen:main"
24
+
25
+ [build-system]
26
+ requires = ["hatchling"]
27
+ build-backend = "hatchling.build"
@@ -0,0 +1,198 @@
1
+ import os
2
+ import time
3
+ import threading
4
+ import tkinter as tk
5
+ import shutil
6
+ import argparse
7
+ from pathlib import Path
8
+ from PIL import Image, ImageTk
9
+ from watchdog.observers import Observer
10
+ from watchdog.events import FileSystemEventHandler
11
+ from dotenv import load_dotenv
12
+ from google import genai
13
+ from google.genai import types
14
+
15
+ load_dotenv()
16
+
17
+ class ImageHandler(FileSystemEventHandler):
18
+ def __init__(self, callback):
19
+ self.callback = callback
20
+
21
+ def on_created(self, event):
22
+ if not event.is_directory and event.src_path.lower().endswith(('.png', '.jpg', '.jpeg')):
23
+ print(f"發現新圖片: {event.src_path}")
24
+ self.callback(event.src_path)
25
+
26
+ class App:
27
+ def __init__(self, test_mode=False):
28
+ self.root = tk.Tk()
29
+ self.root.title("Sync Image Viewer")
30
+ self.root.configure(background='black')
31
+ self.test_mode = test_mode
32
+
33
+ # 初始狀態隱藏視窗
34
+ self.root.withdraw()
35
+ self.root.protocol("WM_DELETE_WINDOW", self.hide_window)
36
+ self.root.bind("<Escape>", lambda e: self.hide_window())
37
+
38
+ self.label = tk.Label(self.root, background='black')
39
+ self.label.pack(expand=True, fill='both')
40
+
41
+ self.current_photo = None
42
+
43
+ # 初始化 Gemini Client
44
+ api_key = os.getenv("GOOGLE_API_KEY")
45
+ if api_key:
46
+ self.client = genai.Client(api_key=api_key)
47
+ else:
48
+ self.client = None
49
+ if not test_mode:
50
+ print("警告: 找不到 GOOGLE_API_KEY,將無法進行圖片轉換。")
51
+
52
+ def hide_window(self):
53
+ self.root.attributes("-fullscreen", False)
54
+ self.root.withdraw()
55
+ print("視窗已隱藏,監控中...")
56
+
57
+ def display_image(self, image_path):
58
+ try:
59
+ img = Image.open(image_path)
60
+ self.root.deiconify()
61
+ self.root.attributes("-fullscreen", True)
62
+ self.root.attributes("-topmost", True)
63
+ self.root.lift()
64
+ self.root.focus_force()
65
+
66
+ screen_width = self.root.winfo_screenwidth()
67
+ screen_height = self.root.winfo_screenheight()
68
+ img.thumbnail((screen_width, screen_height), Image.Resampling.LANCZOS)
69
+
70
+ self.current_photo = ImageTk.PhotoImage(img)
71
+ self.label.config(image=self.current_photo)
72
+ self.root.after(1000, lambda: self.root.attributes("-topmost", False))
73
+ print(f"已顯示圖片: {image_path}")
74
+ except Exception as e:
75
+ print(f"顯示圖片失敗: {e}")
76
+
77
+ def call_gemini_api(self, image_path, target_path):
78
+ """呼叫 Gemini API 進行圖片轉換"""
79
+ # 強制重新讀取 .env 以取得最新 Prompt
80
+ load_dotenv(override=True)
81
+
82
+ if not self.client:
83
+ print("錯誤: Gemini Client 未初始化 (缺少 API Key)")
84
+ return False
85
+
86
+ # 預設使用 Nano Banana Pro (Gemini 3 Pro Image)
87
+ model_name = os.getenv("GEMINI_MODEL", "gemini-3-pro-image-preview")
88
+ prompt = os.getenv("GEMINI_PROMPT", "Transform this image with a creative style.")
89
+
90
+ print(f"正在呼叫模型: {model_name}")
91
+ print(f"執行 Prompt: {prompt}")
92
+
93
+ try:
94
+ # 加入重試機制讀取圖片,防止檔案還在寫入中
95
+ raw_img = None
96
+ for i in range(5):
97
+ try:
98
+ raw_img = Image.open(image_path)
99
+ raw_img.load() # 嘗試載入以確保檔案完整
100
+ break
101
+ except Exception:
102
+ time.sleep(0.5)
103
+
104
+ if not raw_img:
105
+ print(f"錯誤: 無法讀取圖片檔案 {image_path}")
106
+ return False
107
+
108
+ # 使用 Gemini 進行風格轉換
109
+ response = self.client.models.generate_content(
110
+ model=model_name,
111
+ contents=[
112
+ prompt,
113
+ raw_img
114
+ ],
115
+ config=types.GenerateContentConfig(
116
+ response_modalities=["IMAGE"],
117
+ )
118
+ )
119
+
120
+ # 尋找輸出中的圖片部分
121
+ generated_img = None
122
+ if response.parts:
123
+ for part in response.parts:
124
+ if part.inline_data:
125
+ generated_img = part.as_image()
126
+ break
127
+
128
+ if generated_img:
129
+ generated_img.save(target_path)
130
+ print(f"Gemini 處理完成: {target_path}")
131
+ return True
132
+ else:
133
+ print(f"Gemini 未回傳圖片數據。")
134
+ if response.text:
135
+ print(f"回傳文字內容: {response.text}")
136
+ return False
137
+
138
+ except Exception as e:
139
+ print(f"Gemini API 呼叫失敗: {e}")
140
+ return False
141
+
142
+ def process_and_show(self, image_path):
143
+ output_dir = os.environ.get("OUTPUT_DIRECTORY", "./processed")
144
+ Path(output_dir).mkdir(parents=True, exist_ok=True)
145
+ target_path = Path(output_dir) / Path(image_path).name
146
+
147
+ # 確保檔案已經寫入完成
148
+ time.sleep(0.5)
149
+
150
+ if self.test_mode:
151
+ print(f"[測試模式] 複製圖片中: {image_path}")
152
+ time.sleep(0.5)
153
+ shutil.copy2(image_path, target_path)
154
+ self.root.after(0, self.display_image, str(target_path))
155
+ else:
156
+ def run_task():
157
+ success = self.call_gemini_api(image_path, target_path)
158
+ if not success:
159
+ print("改用原始圖片顯示...")
160
+ shutil.copy2(image_path, target_path)
161
+ self.root.after(0, self.display_image, str(target_path))
162
+
163
+ threading.Thread(target=run_task, daemon=True).start()
164
+
165
+ def run(self):
166
+ watch_dir = os.environ.get("WATCH_DIRECTORY", "./images")
167
+ Path(watch_dir).mkdir(parents=True, exist_ok=True)
168
+
169
+ event_handler = ImageHandler(self.process_and_show)
170
+ observer = Observer()
171
+ observer.schedule(event_handler, watch_dir, recursive=False)
172
+ observer.start()
173
+
174
+ print(f"正在監視目錄: {watch_dir}")
175
+ try:
176
+ self.root.mainloop()
177
+ finally:
178
+ observer.stop()
179
+ observer.join()
180
+
181
+ def main():
182
+ parser = argparse.ArgumentParser(
183
+ description="Sync Image Gen: 監視目錄、Gemini 圖片處理並全螢幕展示",
184
+ formatter_class=argparse.RawTextHelpFormatter
185
+ )
186
+ parser.add_argument("--watch-dir", type=str, default=os.getenv("WATCH_DIRECTORY", "./images"))
187
+ parser.add_argument("--output-dir", type=str, default=os.getenv("OUTPUT_DIRECTORY", "./processed"))
188
+ parser.add_argument("-t", "--test", action="store_true")
189
+
190
+ args = parser.parse_args()
191
+ os.environ["WATCH_DIRECTORY"] = args.watch_dir
192
+ os.environ["OUTPUT_DIRECTORY"] = args.output_dir
193
+
194
+ app = App(test_mode=args.test)
195
+ app.run()
196
+
197
+ if __name__ == "__main__":
198
+ main()