Spaces:
Sleeping
Sleeping
File size: 10,275 Bytes
f7d2a44 0c936fa b35af45 00487e6 3ff48b9 2d7377d f7d2a44 03dfca2 3ac04fc 4102ee9 7e5e159 f7d2a44 ddf47e6 03dfca2 96196b4 c3f878d f7d2a44 0c936fa 897cf53 2d7377d 0c936fa 2d7377d f7d2a44 0c936fa 897cf53 2d7377d 0c936fa 00487e6 0c936fa 11f9fd2 2d7377d 766ff27 7e5e159 0c936fa 897cf53 0c936fa 2d7377d 897cf53 0c936fa 2d7377d 897cf53 0c936fa 897cf53 0c936fa 897cf53 0c936fa 00487e6 3a43614 0c936fa 897cf53 0c936fa 2d7377d 0c936fa 2d7377d 0c936fa f7d2a44 03dfca2 b35af45 766ff27 b35af45 03dfca2 20da126 6ae9b17 20da126 766ff27 20da126 03dfca2 0c936fa 00487e6 0c936fa 897cf53 03dfca2 00487e6 c3f878d 21cce09 897cf53 31447cd 897cf53 0c936fa 897cf53 ce6901f 21cce09 2d7377d 21cce09 897cf53 3e825f9 897cf53 2d7377d ce6901f 2d7377d 21cce09 2d7377d 50dc821 11f9fd2 2d7377d 11f9fd2 897cf53 2d7377d 897cf53 2d7377d b35af45 11f9fd2 2d7377d 11f9fd2 2d7377d 11f9fd2 2d7377d 11f9fd2 2d7377d 11f9fd2 2d7377d 11f9fd2 897cf53 11f9fd2 a124874 f4f04e7 11f9fd2 897cf53 11f9fd2 f4f04e7 11f9fd2 f4f04e7 b35af45 11f9fd2 f4f04e7 11f9fd2 f4f04e7 897cf53 11f9fd2 897cf53 11f9fd2 1a8ceff 897cf53 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
import gradio as gr
import pandas as pd
from Functions.video_player_functions import youtube_link_to_id, get_video_link_by_pointer, get_youtube_player_html, change_video_completion_status
from Functions.caption_editor_functions import request_captions_by_video_id, save_captions_to_db
from Resources.css import css
from Resources.js import yt_init_js
from Resources.localization import get_string
next_video_pointer = 0
user = "anonymous_user"
n_videos = 21
placeholder_link = "https://www.youtube.com/watch?v=wTQjwG2-ePA"
def get_username(profile: gr.OAuthProfile):
global user
if profile is None:
user = "anonymous_user"
else:
user = profile.username
return profile
def on_row_select(df, evt: gr.SelectData):
"""Handle row selection in DataFrame"""
if evt.index is not None and len(evt.index) > 0:
row_idx = evt.index[0]
if row_idx < len(df):
row = df.iloc[row_idx]
return (
gr.update(visible=True), # editing_panel
gr.update(value=float(row['Start'])), # start_time
gr.update(value=str(row['Text'])), # text_input
gr.update(value=float(row['End'])), # end_time
gr.update(value=row_idx), # selected_row_idx
gr.update(value=get_string("update_entry_button")) # save_entry_button
)
return gr.update(visible=False), "", "", "", -1, get_string("save_entry_button")
def show_add_entry_form():
"""Show editing panel for adding new entry"""
return (
gr.update(visible=True), # editing_panel
gr.update(value=0.0), # start_time
gr.update(value=""), # text_input
gr.update(value=0.0), # end_time
gr.update(value=-1), # selected_row_idx (-1 means new entry)
gr.update(value=get_string("add_entry_button_form")) # save_entry_button
)
def save_entry(df, start_time, text, end_time, selected_row_idx, video_id):
"""Save or update a caption entry"""
if user == "anonymous_user":
return df, gr.update(visible=True), gr.Warning(get_string("please_sign_in"))
if next_video_pointer == -1:
return df, gr.update(visible=True), gr.Warning(get_string("all_videos_transcribed"))
try:
start_time = float(start_time)
end_time = float(end_time)
if start_time >= end_time:
return df, gr.update(visible=True), gr.Warning(get_string("start_less_than_end"))
if not text.strip():
return df, gr.update(visible=True), gr.Warning(get_string("text_cannot_be_empty"))
df_copy = df.copy()
if selected_row_idx == -1: # Adding new entry
new_row = pd.DataFrame({
'Start': [start_time],
'Text': [text.strip()],
'End': [end_time]
})
df_copy = pd.concat([df_copy, new_row], ignore_index=True)
# Sort by start time
df_copy = df_copy.sort_values('Start').reset_index(drop=True)
else: # Updating existing entry
if 0 <= selected_row_idx < len(df_copy):
df_copy.iloc[selected_row_idx] = [start_time, text.strip(), end_time]
# Sort by start time
df_copy = df_copy.sort_values('Start').reset_index(drop=True)
# Update in database
save_result = save_captions_to_db(df_copy, video_id, user)
return (
df_copy,
gr.update(visible=False), # Hide panel on success
gr.Info(f"{save_result}")
)
except ValueError as e:
return df, gr.update(visible=True), gr.Warning(f"{get_string('invalid_time_format')} {str(e)}")
except Exception as e:
return df, gr.update(visible=True), gr.Error(f"{get_string('error')} {str(e)}")
def cancel_edit():
"""Cancel editing and hide the form"""
return gr.update(visible=False)
def change_completion_status(completion_status):
global next_video_pointer
change_video_completion_status(completion_status, (next_video_pointer + n_videos - 1) % n_videos)
def get_next_components():
global next_video_pointer
if next_video_pointer != -1:
next_video_link = get_video_link_by_pointer(next_video_pointer)
next_video_pointer = (next_video_pointer + 1) % n_videos
for i in range(n_videos + 1):
if next_video_link is not None:
break
next_video_link = get_video_link_by_pointer(next_video_pointer)
next_video_pointer = (next_video_pointer + 1) % n_videos
if next_video_link is None:
next_video_link = placeholder_link
next_video_pointer = -1
try:
next_video_id = youtube_link_to_id(next_video_link)
next_captions = request_captions_by_video_id(next_video_id)
return next_captions, next_video_id
except (ValueError, Exception) as e:
empty_captions = pd.DataFrame(columns=["Start", "Text", "End"])
return empty_captions, "error"
(start_captions, start_video_id) = get_next_components()
with gr.Blocks(css=css, head=yt_init_js, fill_width=True) as main_page:
user_state = gr.State("anonymous_user")
pointer_state = gr.State(0)
current_video_id = gr.Textbox(value="", visible=False, interactive=False)
selected_row_idx = gr.Number(value=-1, visible=False)
main_page.load(get_username, outputs=user_state)
with gr.Row(variant="panel"):
with gr.Column(scale=4):
gr.Markdown(f"## {get_string('app_title')}")
with gr.Column(scale=4):
@gr.render(inputs=user_state)
def check_login(logged_in_user):
if logged_in_user == "anonymous_user":
gr.Markdown(get_string("please_log_in"), rtl=True)
else:
gr.Markdown(f"{get_string('logged_in_as')} {logged_in_user.username}", rtl=True)
with gr.Column(scale=1, min_width=50):
gr.LoginButton(value=get_string("log_in_button"), logout_value=get_string("log_in_button"))
with gr.Row():
with gr.Column(scale=2, min_width=600):
video_embed = gr.HTML(value=get_youtube_player_html())
next_video_button = gr.Button(get_string("next_button"), key="next_video")
with gr.Column(scale=1, min_width=200):
caption_editor = gr.DataFrame(
interactive=False,
elem_id="tbl",
datatype=["number", "str", "number"],
col_count=(3, "fixed"),
column_widths=["20%", "60%", "20%"],
headers=[get_string("header_start"), get_string("header_text"), get_string("header_end")],
wrap=True
)
add_entry_button = gr.Button(get_string("add_entry_button"), variant="secondary")
editing_complete_checkbox = gr.Checkbox(label=get_string("editing_complete_checkbox"))
with gr.Row():
with gr.Group(visible=False) as editing_panel:
gr.Markdown(f"### {get_string('edit_caption_title')}")
with gr.Row(equal_height=False):
with gr.Column():
start_time_input = gr.Textbox(label=get_string("start_time_label"), value="0.000", interactive=False)
insert_start_time_button = gr.Button(get_string("insert_current_time"))
with gr.Column():
text_input = gr.Textbox(label=get_string("caption_text_label"), placeholder=get_string("caption_text_placeholder"))
with gr.Column():
end_time_input = gr.Textbox(label=get_string("end_time_label"), value="0.000", interactive=False)
insert_end_time_button = gr.Button(get_string("insert_current_time"))
with gr.Row(equal_height=False):
save_entry_button = gr.Button(get_string("save_entry_button"), variant="primary")
cancel_button = gr.Button(get_string("cancel_button"), variant="secondary")
save_result = gr.Markdown()
main_page.load(
fn=get_next_components,
outputs=[caption_editor, current_video_id],
js="""() => {
const checkPlayer = setInterval(() => {
if (window.ytPlayer && window.ytPlayer.cueVideoById) {
clearInterval(checkPlayer);
// cue video dynamically once captions arrive
}
}, 100);
}"""
)
next_video_button.click(
fn=get_next_components,
outputs=[caption_editor, current_video_id]
)
next_video_button.click(
fn=lambda: False,
outputs=editing_complete_checkbox
)
current_video_id.change(
fn=None,
inputs=current_video_id,
outputs=None,
js="""(videoId) => {
if (window.ytPlayer && window.ytPlayer.cueVideoById) {
console.log('[Video Load] cueVideoById', videoId);
window.ytPlayer.cueVideoById(videoId);
}
}"""
)
caption_editor.select(
fn=on_row_select,
inputs=[caption_editor],
outputs=[editing_panel, start_time_input, text_input, end_time_input, selected_row_idx, save_entry_button]
)
editing_complete_checkbox.input(
fn=change_completion_status,
inputs=editing_complete_checkbox
)
add_entry_button.click(
fn=show_add_entry_form,
outputs=[editing_panel, start_time_input, text_input, end_time_input, selected_row_idx, save_entry_button]
)
save_entry_button.click(
fn=save_entry,
inputs=[caption_editor, start_time_input, text_input, end_time_input,
selected_row_idx, current_video_id],
outputs=[caption_editor, editing_panel, save_result]
)
insert_start_time_button.click(
fn=None, inputs=None, outputs=start_time_input,
js="() => window.ytPlayer ? +window.ytPlayer.getCurrentTime().toFixed(3) : 0"
)
insert_end_time_button.click(
fn=None, inputs=None, outputs=end_time_input,
js="() => window.ytPlayer ? +window.ytPlayer.getCurrentTime().toFixed(3) : 0"
)
cancel_button.click(fn=cancel_edit, outputs=[editing_panel])
main_page.launch(share=True) |