lettercast commited on
Commit
eb572c5
0 Parent(s):

initial commit

Browse files

update app

update app

update app

Files changed (4) hide show
  1. .gitattributes +35 -0
  2. .gitignore +1 -0
  3. README.md +12 -0
  4. app.py +171 -0
.gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ .venv
README.md ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Lettercast.ai API
3
+ emoji: 🎙️
4
+ colorFrom: gray
5
+ colorTo: blue
6
+ sdk: gradio
7
+ sdk_version: 4.44.0
8
+ app_file: app.py
9
+ pinned: false
10
+ ---
11
+
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import traceback
2
+ import gradio as gr
3
+ import requests
4
+ import time
5
+ import os
6
+
7
+ # Read from environment variables with default values if not set
8
+ LETTERCAST_API_CAST_TEMPLATE = (
9
+ f'{os.environ.get("LETTERCAST_API_CAST")}/feeds/{{feed_id}}/casts'
10
+ )
11
+ TTS_BACKEND = "openai-tts-1-hd" # Default TTS backend
12
+ WAIT_TIME_S = 30
13
+
14
+
15
+ def create_cast(token, feed_id, url, file, detail_level, create_video, hosts):
16
+ if not url and file is None:
17
+ return {"error": "Please provide a URL or upload a file."}, None, None
18
+ if not token or not feed_id:
19
+ return {"error": "Please provide both Token and Feed ID."}, None, None
20
+ headers = {"Authorization": f"Bearer {token}"}
21
+ data = {
22
+ "hosts": hosts,
23
+ "detail_level": detail_level,
24
+ "create_video": str(create_video).lower(),
25
+ "tts_backend": TTS_BACKEND,
26
+ }
27
+ if url:
28
+ data["url"] = url
29
+ endpoint_url = LETTERCAST_API_CAST_TEMPLATE.format(feed_id=feed_id)
30
+
31
+ if file is not None:
32
+ with open(file, "rb") as f:
33
+ files = {"file": (os.path.basename(file), f, "application/pdf")}
34
+ response = requests.post(
35
+ endpoint_url, headers=headers, data=data, files=files
36
+ )
37
+ else:
38
+ response = requests.post(endpoint_url, headers=headers, data=data)
39
+
40
+ if response.status_code == 200:
41
+ resp_data = response.json()
42
+ audio_file_url = resp_data.get("audio_file_url")
43
+ video_file_url = resp_data.get("video_file_url")
44
+ return resp_data, audio_file_url, video_file_url
45
+ else:
46
+ error_message = f"Error {response.status_code}: {response.text}"
47
+ return {"error": error_message}, None, None
48
+
49
+
50
+ def poll_until_ready(
51
+ audio_file_url, video_file_url, create_video, progress=gr.Progress()
52
+ ):
53
+ max_retries = 60 # Max wait time of 5 minutes (assuming 5s per retry)
54
+ wait_time = WAIT_TIME_S # seconds
55
+ audio_ready = False
56
+ video_ready = False
57
+ for i in range(max_retries):
58
+ progress(i / max_retries)
59
+ if audio_file_url and not audio_ready:
60
+ try:
61
+ response = requests.get(audio_file_url)
62
+ if response.status_code == 200:
63
+ audio_ready = True
64
+ except Exception:
65
+ traceback.print_exc() # Ignore exceptions and retry
66
+ if create_video and video_file_url and not video_ready:
67
+ try:
68
+ response = requests.get(video_file_url)
69
+ if response.status_code == 200:
70
+ video_ready = True
71
+ except Exception:
72
+ traceback.print_exc()
73
+ if audio_ready and (not create_video or video_ready):
74
+ status_text = "Media is ready."
75
+ audio_player_html = (
76
+ create_audio_player(audio_file_url) if audio_ready else ""
77
+ )
78
+ video_player_html = (
79
+ create_video_player(video_file_url) if video_ready else ""
80
+ )
81
+ progress(1.0)
82
+ return status_text, audio_player_html, video_player_html
83
+ else:
84
+ time.sleep(wait_time)
85
+ return "Media not available yet.", "", ""
86
+
87
+
88
+ def create_audio_player(audio_url):
89
+ if audio_url:
90
+ return f"""
91
+ <audio controls controlsList="nodownload">
92
+ <source src="{audio_url}" type="audio/mpeg">
93
+ Your browser does not support the audio element.
94
+ </audio>
95
+ """
96
+ else:
97
+ return ""
98
+
99
+
100
+ def create_video_player(video_url):
101
+ if video_url:
102
+ return f"""
103
+ <video controls controlsList="nodownload" width="100%">
104
+ <source src="{video_url}" type="video/mp4">
105
+ Your browser does not support the video tag.
106
+ </video>
107
+ """
108
+ else:
109
+ return ""
110
+
111
+
112
+ with gr.Blocks() as demo:
113
+ gr.Markdown("## Test the Lettercast.ai API (beta)")
114
+ # gr.Markdown(
115
+ # "To get your access token visit [developer.lettercast.ai](https://developer.lettercast.ai)"
116
+ # )
117
+ with gr.Row():
118
+ token = gr.Textbox(label="Token", placeholder="Enter your token")
119
+ feed_id = gr.Textbox(label="Feed ID", placeholder="Enter your feed ID")
120
+ with gr.Row():
121
+ url = gr.Textbox(label="URL", placeholder="Enter a URL")
122
+ file = gr.File(label="Upload a PDF file", type="filepath")
123
+ with gr.Row():
124
+ detail_level = gr.Dropdown(
125
+ choices=["summary", "full"], label="Detail Level", value="summary"
126
+ )
127
+ hosts = gr.Dropdown(
128
+ choices=["single_host", "two_hosts"], label="Hosts", value="single_host"
129
+ )
130
+ create_video = gr.Checkbox(label="Create Video (PDFs)", value=False)
131
+
132
+ # Button for creating pod and showing the JSON response
133
+ create_pod_btn = gr.Button("Create Pod")
134
+ output = gr.JSON(label="Response")
135
+ audio_pod_data = gr.State()
136
+ video_pod_data = gr.State()
137
+
138
+ # Button for starting the polling process
139
+ start_polling_btn = gr.Button("Start Polling")
140
+ status = gr.Textbox(label="Status")
141
+ audio_player = gr.HTML(label="Generated Audio")
142
+ video_player = gr.HTML(label="Generated Video")
143
+
144
+ # Handle pod creation
145
+ create_pod_btn.click(
146
+ fn=create_cast,
147
+ inputs=[
148
+ token,
149
+ feed_id,
150
+ url,
151
+ file,
152
+ detail_level,
153
+ create_video,
154
+ hosts,
155
+ ],
156
+ outputs=[output, audio_pod_data, video_pod_data],
157
+ show_progress="minimal",
158
+ concurrency_limit=1,
159
+ )
160
+
161
+ # Handle polling for media readiness
162
+ start_polling_btn.click(
163
+ fn=lambda audio_pod_data, video_pod_data, create_video: poll_until_ready(
164
+ audio_pod_data, video_pod_data, create_video
165
+ ),
166
+ inputs=[audio_pod_data, video_pod_data, create_video],
167
+ outputs=[status, audio_player, video_player],
168
+ show_api=False,
169
+ )
170
+
171
+ demo.launch()