Art-Gallery / app.py
aifeifei798's picture
Update app.py
3b7098a verified
# --- START OF MODIFIED FILE app.py ---
import gradio as gr
from atproto import Client, models
import os
import io
from PIL import Image
# --- 配置您的 Bluesky 账户信息 ---
BSKY_HANDLE = os.environ.get("BSKY_HANDLE")
BSKY_APP_PASSWORD = os.environ.get("BSKY_APP_PASSWORD")
# --- 新增:配置缓存目录 ---
IMAGE_CACHE_DIR = "image_cache"
# 确保缓存目录存在
os.makedirs(IMAGE_CACHE_DIR, exist_ok=True)
client = Client()
# --- 全局登录 ---
try:
print("正在尝试登录 Bluesky...")
if BSKY_HANDLE and BSKY_APP_PASSWORD:
client.login(BSKY_HANDLE, BSKY_APP_PASSWORD)
print(f"Bluesky 登录成功!用户: {client.me.handle}")
else:
print("未找到 Bluesky 环境变量,跳过全局登录。")
except Exception as e:
print(f"Bluesky 全局登录失败: {e}")
pass
def fetch_bsky_gallery(max_posts=51):
if not client.me:
print("客户端未登录,正在尝试重新登录...")
if not BSKY_HANDLE or not BSKY_APP_PASSWORD:
raise gr.Error("Bluesky 登录失败:请在 Hugging Face Spaces 的 Repository secrets 中设置 BSKY_HANDLE 和 BSKY_APP_PASSWORD。")
try:
client.login(BSKY_HANDLE, BSKY_APP_PASSWORD)
print(f"Bluesky 重新登录成功!用户: {client.me.handle}")
except Exception as e:
raise gr.Error(f"Bluesky 登录失败,请检查 Handle 或应用密码并重启程序。错误: {e}")
print(f"正在为用户 {client.me.handle} 获取帖子...")
try:
actor_did = client.me.did
profile_feed = client.get_author_feed(actor=actor_did, limit=max_posts)
print(f"从 API 获取了 {len(profile_feed.feed)} 条帖子,正在开始检查...")
gallery_items = []
for i, feed_view in enumerate(profile_feed.feed):
post_record = feed_view.post.record
# 核心判断逻辑
if post_record.embed and isinstance(post_record.embed, models.AppBskyEmbedImages.Main):
print(f"成功匹配到图片帖子 #{i+1}!正在处理...")
images = post_record.embed.images
post_text = getattr(post_record, 'text', '')
for j, image_data in enumerate(images):
try:
image_cid = image_data.image.ref.link
# --- 缓存逻辑开始 ---
# 1. 定义缓存文件的路径
cached_image_path = os.path.join(IMAGE_CACHE_DIR, f"{image_cid}.jpg")
# 2. 检查缓存是否存在
if os.path.exists(cached_image_path):
print(f" > 缓存命中!直接使用本地图片: {cached_image_path}")
else:
# 3. 如果缓存不存在,则下载并保存
print(f" > 缓存未命中。正在下载 CID: {image_cid}...")
params = models.ComAtprotoSyncGetBlob.Params(did=actor_did, cid=image_cid)
image_bytes = client.com.atproto.sync.get_blob(params)
if image_bytes:
# 使用 PIL 打开图片并转换为 RGB
img = Image.open(io.BytesIO(image_bytes))
if img.mode == 'RGBA':
img = img.convert('RGB')
# 保存到缓存目录
img.save(cached_image_path, format='JPEG')
print(f" > 图片已成功下载并缓存至: {cached_image_path}")
else:
print(f" > 下载失败,未获取到图片数据。")
continue # 跳过这张图片
# --- 缓存逻辑结束 ---
# 将缓存路径(或已存在的本地路径)添加到画廊列表
caption = post_text if j == 0 else ""
gallery_items.append((cached_image_path, caption))
except Exception as img_e:
print(f"处理单张图片时发生错误: {img_e}")
continue
else:
print(f"帖子 #{i+1} 不包含图片,已跳过。")
print(f"\n检查完成。成功获取并处理了 {len(gallery_items)} 张图片。")
if not gallery_items:
placeholder_image = "https://i.imgur.com/nA8n922.png"
return [(placeholder_image, f"在最近的 {max_posts} 条帖子中没有找到符合条件的图片。")]
return gallery_items
except Exception as e:
print(f"获取帖子时发生错误: {e}")
raise gr.Error(f"获取 Bluesky 帖子时出错: {e}")
with gr.Blocks() as demo:
gr.Markdown("""
### 🎨 AiFeiFei 的 Bluesky 数字艺术画廊
""")
with gr.Row():
gallery = gr.Gallery(
label="作品集",
columns=3,
height=600,
show_download_button=True,
object_fit="contain",
show_label=False
)
with gr.Row():
refresh_button = gr.Button("🔄 刷新画廊")
demo.load(fn=fetch_bsky_gallery, inputs=None, outputs=gallery)
refresh_button.click(fn=fetch_bsky_gallery, inputs=None, outputs=gallery)
if __name__ == "__main__":
demo.launch(debug=True)
# --- END OF MODIFIED FILE app.py ---