Skip to content

Commit eee1547

Browse files
authored
feat: luma photon (#266)
* feat: luma photon * docs: update icons * chore: format code
1 parent 944c53c commit eee1547

File tree

8 files changed

+125
-5
lines changed

8 files changed

+125
-5
lines changed

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
<img src="assets/siliconcloud-text.svg" alt="SiliconFlow" width="100" height="60" />
2424
<img src="assets/wenxin-color.svg" alt="Baidu ERNIE" width="60" height="60" />
2525
<img src="assets/stability-brand-color.svg" alt="Stability AI" width="80" height="60" />
26+
<img src="assets/luma-color.svg" alt="Luma Photon" width="20" height="60" />
27+
<img src="assets/luma-text.svg" alt="Luma Photon" width="60" height="60" />
2628
</div>
2729

2830
## 1. Introduction
@@ -53,7 +55,7 @@
5355
- `Tencent Hunyuan`
5456
- `Baidu ERNIE irag-1.0`
5557
- `Stable Diffusion 3.5 large turbo`
56-
58+
- `Luma Photon`
5759
项目架构流程如下:
5860

5961
```mermaid
@@ -231,6 +233,12 @@ MLLM 模型主要用于自动切片后的切片标题生成,此功能默认关
231233
232234
请自行[注册账号](https://platform.stability.ai/account/keys)并申请 API Key,填写到 `bilive.toml` 文件中对应的 `STABILITY_API_KEY` 中。
233235

236+
##### 3.3.6 Luma Photon 模型
237+
238+
> 如需使用 Luma Photon 模型,请将 `IMAGE_GEN_MODEL` 参数设置为 `luma`
239+
240+
请自行[注册账号](https://lumalabs.ai/api/keys)并申请 API Key,填写到 `bilive.toml` 文件中对应的 `LUMA_API_KEY` 中。
241+
234242
#### 4. bilitool 登录
235243

236244
> 由于一般日志打印不出二维码效果(docker 的日志不确定是否能打印,等发布新image时再修改,docker 版本请先参考文档 [bilive](https://bilive.timerring.com),本 README 只针对源码部署),所以这步需要提前在机器上安装 [bilitool](https://github.com/timerring/bilitool):

assets/luma-color.svg

Lines changed: 1 addition & 0 deletions
Loading

assets/luma-text.svg

Lines changed: 1 addition & 0 deletions
Loading

bilive.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,11 @@ qwen_api_key = "" # Apply for your own Qwen API key at https://bailian.console.a
3737

3838
[cover]
3939
generate_cover = false # whether to generate cover
40-
image_gen_model = "minimax" # the image generation model, can be "minimax" or "siliconflow" or "tencent" or "baidu" or "stability"
40+
image_gen_model = "minimax" # the image generation model, can be "minimax" or "siliconflow" or "tencent" or "baidu" or "stability" or "luma"
4141
minimax_api_key = "" # Apply for your own Minimax API key at https://platform.minimaxi.com/user-center/basic-information/interface-key
4242
siliconflow_api_key = "" # Apply for your own SiliconFlow API key at https://cloud.siliconflow.cn/i/3Szr5BVg
4343
tencent_secret_id = "" # Apply for your own Tencent Cloud API key at https://console.cloud.tencent.com/cam/capi
4444
tencent_secret_key = "" # Apply for your own Tencent Cloud secret key as above
4545
baidu_api_key = "" # Apply for your own Baidu API key at https://console.bce.baidu.com/iam/key/list
46-
stability_api_key = "" # Apply for your own Stability API key at https://platform.stability.ai/account/keys
46+
stability_api_key = "" # Apply for your own Stability API key at https://platform.stability.ai/account/keys
47+
luma_api_key = "" # Apply for your own Luma API key at https://lumalabs.ai/api/keys

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ google-generativeai>=0.7.2 # don't change this part
1919
zhipuai
2020
openai
2121
toml
22-
groq
22+
groq
23+
lumaai

src/config.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,5 @@ def get_interface_config():
7979
TENCENT_SECRET_ID = config.get('cover', {}).get('tencent_secret_id')
8080
TENCENT_SECRET_KEY = config.get('cover', {}).get('tencent_secret_key')
8181
BAIDU_API_KEY = config.get('cover', {}).get('baidu_api_key')
82-
STABILITY_API_KEY = config.get('cover', {}).get('stability_api_key')
82+
STABILITY_API_KEY = config.get('cover', {}).get('stability_api_key')
83+
LUMA_API_KEY = config.get('cover', {}).get('luma_api_key')

src/cover/cover_generator.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ def wrapper(video_path):
7373
)
7474

7575
return stable_diffusion_generate_cover(cover_path)
76+
elif model_type == "luma":
77+
from .image_model_sdk.luma_sdk import luma_generate_cover
78+
79+
return luma_generate_cover(cover_path)
7680
else:
7781
upload_log.error(f"Unsupported model type: {model_type}")
7882
return None
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import requests
2+
import json
3+
import time
4+
import os
5+
import base64
6+
from PIL import Image
7+
from io import BytesIO
8+
from lumaai import LumaAI
9+
from src.upload.bilitool.bilitool.model.model import Model
10+
from src.config import LUMA_API_KEY
11+
12+
13+
def cover_up(img: str):
14+
"""Upload the cover image
15+
Parameters
16+
----------
17+
- img: img path or stream
18+
Returns
19+
-------
20+
- url: str
21+
the url of the cover image in bili server
22+
"""
23+
from PIL import Image
24+
from io import BytesIO
25+
26+
request = requests.Session()
27+
request.headers = {
28+
"user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/63.0.3239.108",
29+
"referer": "https://www.bilibili.com/",
30+
"connection": "keep-alive",
31+
}
32+
model = Model().get_config()
33+
request.cookies.set("SESSDATA", model["cookies"]["SESSDATA"])
34+
with Image.open(img) as im:
35+
# you should keep the image ratio 16:10
36+
xsize, ysize = im.size
37+
if xsize / ysize > 1.6:
38+
delta = xsize - ysize * 1.6
39+
region = im.crop((delta / 2, 0, xsize - delta / 2, ysize))
40+
else:
41+
delta = ysize - xsize * 10 / 16
42+
region = im.crop((0, delta / 2, xsize, ysize - delta / 2))
43+
buffered = BytesIO()
44+
region.save(buffered, format=im.format)
45+
r = request.post(
46+
url="https://member.bilibili.com/x/vu/web/cover/up",
47+
data={
48+
"cover": b"data:image/jpeg;base64,"
49+
+ (base64.b64encode(buffered.getvalue())),
50+
"csrf": model["cookies"]["bili_jct"],
51+
},
52+
timeout=30,
53+
)
54+
buffered.close()
55+
res = r.json()
56+
if res.get("data") is None:
57+
raise Exception(res)
58+
print(res["data"]["url"], flush=True)
59+
return res["data"]["url"]
60+
61+
62+
def luma_generate_cover(your_file_path):
63+
"""Generate cover for video using Luma Photon
64+
Args:
65+
your_file_path: str, path to the video file
66+
Returns:
67+
str: generated cover
68+
"""
69+
try:
70+
cover_url = cover_up(your_file_path)
71+
client = LumaAI(
72+
auth_token=LUMA_API_KEY,
73+
)
74+
generation = client.generations.image.create(
75+
prompt="This is a video screenshot, please generate a cover in the style of a manga",
76+
image_ref=[{"url": cover_url, "weight": 0.85}],
77+
)
78+
completed = False
79+
while not completed:
80+
generation = client.generations.get(id=generation.id)
81+
if generation.state == "completed":
82+
completed = True
83+
elif generation.state == "failed":
84+
raise RuntimeError(f"Generation failed: {generation.failure_reason}")
85+
print("Dreaming")
86+
time.sleep(2)
87+
88+
image_url = generation.assets.image
89+
90+
response = requests.get(image_url, stream=True)
91+
cover_name = time.strftime("%Y%m%d%H%M%S") + ".jpg"
92+
temp_cover_path = os.path.join(os.path.dirname(your_file_path), cover_name)
93+
with open(temp_cover_path, "wb") as file:
94+
file.write(response.content)
95+
os.remove(your_file_path)
96+
return temp_cover_path
97+
except Exception as e:
98+
print(e, flush=True)
99+
return None
100+
101+
102+
if __name__ == "__main__":
103+
print(luma_generate_cover(""))

0 commit comments

Comments
 (0)