youngtsai commited on
Commit
c64f183
·
1 Parent(s): 6615b87

function changeImage(direction, count, galleryIndex) {

Browse files
Files changed (1) hide show
  1. app.py +210 -3
app.py CHANGED
@@ -437,7 +437,6 @@ def generate_transcription(video_id):
437
 
438
  return transcription
439
 
440
-
441
  def process_transcript_and_screenshots(video_id):
442
  print("====process_transcript_and_screenshots====")
443
 
@@ -611,6 +610,9 @@ def process_youtube_link(password, link):
611
  formatted_transcript_json = json.dumps(formatted_transcript, ensure_ascii=False, indent=2)
612
  summary_json = get_video_id_summary(video_id, formatted_simple_transcript, source)
613
  summary = summary_json["summary"]
 
 
 
614
  html_content = format_transcript_to_html(formatted_transcript)
615
  simple_html_content = format_simple_transcript_to_html(formatted_simple_transcript)
616
  first_image = formatted_transcript[0]['screenshot_path']
@@ -632,6 +634,7 @@ def process_youtube_link(password, link):
632
  questions[2] if len(questions) > 2 else "", \
633
  formatted_transcript_json, \
634
  summary, \
 
635
  mind_map, \
636
  mind_map_html, \
637
  html_content, \
@@ -1057,6 +1060,169 @@ def change_questions(password, df_string):
1057
  print("=====get_questions=====")
1058
  return q1, q2, q3
1059
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1060
  # ---- LLM CRUD ----
1061
  def enable_edit_mode():
1062
  return gr.update(interactive=True)
@@ -1545,6 +1711,43 @@ HEAD = """
1545
  });
1546
  }
1547
  </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1548
  """
1549
 
1550
  with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, secondary_hue=gr.themes.colors.amber, text_size = gr.themes.sizes.text_lg), head=HEAD) as demo:
@@ -1615,7 +1818,10 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
1615
  summary_delete_button = gr.Button("刪除", size="sm", variant="primary")
1616
  summary_create_button = gr.Button("建立", size="sm", variant="primary")
1617
  with gr.Row():
1618
- df_summarise = gr.Textbox(container=True, show_copy_button=True, lines=40, show_label=False)
 
 
 
1619
  with gr.Tab("教學備課"):
1620
  with gr.Row():
1621
  content_subject = gr.Dropdown(label="選擇主題", choices=["數學", "自然", "國文", "英文", "社會","物理", "化學", "生物", "地理", "歷史", "公民"], value="", visible=False)
@@ -1796,7 +2002,8 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
1796
  btn_2,
1797
  btn_3,
1798
  df_string_output,
1799
- df_summarise,
 
1800
  mind_map,
1801
  mind_map_html,
1802
  transcript_html,
 
437
 
438
  return transcription
439
 
 
440
  def process_transcript_and_screenshots(video_id):
441
  print("====process_transcript_and_screenshots====")
442
 
 
610
  formatted_transcript_json = json.dumps(formatted_transcript, ensure_ascii=False, indent=2)
611
  summary_json = get_video_id_summary(video_id, formatted_simple_transcript, source)
612
  summary = summary_json["summary"]
613
+ key_moments_json = get_key_moments(video_id, formatted_simple_transcript, formatted_transcript, source)
614
+ key_moments = key_moments_json["key_moments"]
615
+ key_moments_html = get_key_moments_html(key_moments)
616
  html_content = format_transcript_to_html(formatted_transcript)
617
  simple_html_content = format_simple_transcript_to_html(formatted_simple_transcript)
618
  first_image = formatted_transcript[0]['screenshot_path']
 
634
  questions[2] if len(questions) > 2 else "", \
635
  formatted_transcript_json, \
636
  summary, \
637
+ key_moments_html, \
638
  mind_map, \
639
  mind_map_html, \
640
  html_content, \
 
1060
  print("=====get_questions=====")
1061
  return q1, q2, q3
1062
 
1063
+ # 「關鍵時刻」另外獨立成一個 tab,時間戳記和文字的下方附上對應的截圖,重點摘要的「關鍵時刻」加上截圖資訊
1064
+ def get_key_moments(video_id, formatted_simple_transcript, formatted_transcript, source):
1065
+ if source == "gcs":
1066
+ print("===get_key_moments on gcs===")
1067
+ gcs_client = GCS_CLIENT
1068
+ bucket_name = 'video_ai_assistant'
1069
+ file_name = f'{video_id}_key_moments.json'
1070
+ blob_name = f"{video_id}/{file_name}"
1071
+ # 检查檔案是否存在
1072
+ is_key_moments_exists = GCS_SERVICE.check_file_exists(bucket_name, blob_name)
1073
+ if not is_key_moments_exists:
1074
+ key_moments = generate_key_moments(formatted_simple_transcript, formatted_transcript)
1075
+ key_moments_json = {"key_moments": key_moments}
1076
+ key_moments_text = json.dumps(key_moments_json, ensure_ascii=False, indent=2)
1077
+ upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, key_moments_text)
1078
+ print("key_moments已上傳到GCS")
1079
+ else:
1080
+ # key_moments已存在,下载内容
1081
+ print("key_moments已存在于GCS中")
1082
+ key_moments_text = download_blob_to_string(gcs_client, bucket_name, blob_name)
1083
+ key_moments_json = json.loads(key_moments_text)
1084
+
1085
+ elif source == "drive":
1086
+ print("===get_key_moments on drive===")
1087
+ service = init_drive_service()
1088
+ parent_folder_id = '1GgI4YVs0KckwStVQkLa1NZ8IpaEMurkL'
1089
+ folder_id = create_folder_if_not_exists(service, video_id, parent_folder_id)
1090
+ file_name = f'{video_id}_key_moments.json'
1091
+
1092
+ # 检查檔案是否存在
1093
+ exists, file_id = check_file_exists(service, folder_id, file_name)
1094
+ if not exists:
1095
+ key_moments = generate_key_moments(formatted_simple_transcript, formatted_transcript)
1096
+ key_moments_json = {"key_moments": key_moments}
1097
+ key_moments_text = json.dumps(key_moments_json, ensure_ascii=False, indent=2)
1098
+ upload_content_directly(service, file_name, folder_id, key_moments_text)
1099
+ print("key_moments已上傳到Google Drive")
1100
+ else:
1101
+ # key_moments已存在,下载内容
1102
+ print("key_moments已存在于Google Drive中")
1103
+ key_moments_text = download_file_as_string(service, file_id)
1104
+ key_moments_json = json.loads(key_moments_text)
1105
+
1106
+ return key_moments_json
1107
+
1108
+ def generate_key_moments(formatted_simple_transcript, formatted_transcript):
1109
+ # 使用 OpenAI 生成基于上传数据的问题
1110
+ sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
1111
+ user_content = f"""
1112
+ 請根據 {formatted_simple_transcript} 文本,提取出重點摘要,並給出對應的時間軸
1113
+ 重點摘要的「關鍵時刻」加上截圖資訊
1114
+ 1. 小範圍切出不同段落的相對應時間軸的重點摘要,
1115
+ 2. 每一小段最多不超過 1/5 的總內容(例如五分鐘的影片就一段不超過一分鐘,10分鐘就一段最多兩分鐘)
1116
+ 3. 注意不要遺漏任何一段時間軸的內容 從零秒開始
1117
+ 4. 如果頭尾的情節不是重點,就併入到附近的段落,特別是打招呼或是介紹人物就是不重要的情節
1118
+ 以這種方式分析整個文本,從零秒開始分析,直到結束。這很重要
1119
+
1120
+ 並用 JSON 格式返回 key_moments:[{{
1121
+ "start": "00:00",
1122
+ "end": "00:00",
1123
+ "text": "逐字稿的重點摘要",
1124
+ "transcript": "逐字稿的集合(要有合理的標點符號)",
1125
+ "images": 截圖的連結們 list
1126
+ }}]
1127
+ """
1128
+ messages = [
1129
+ {"role": "system", "content": sys_content},
1130
+ {"role": "user", "content": user_content}
1131
+ ]
1132
+ response_format = { "type": "json_object" }
1133
+
1134
+ request_payload = {
1135
+ "model": "gpt-4-1106-preview",
1136
+ "messages": messages,
1137
+ "max_tokens": 4000,
1138
+ "response_format": response_format
1139
+ }
1140
+
1141
+ response = OPEN_AI_CLIENT.chat.completions.create(**request_payload)
1142
+ key_moments = json.loads(response.choices[0].message.content)["key_moments"]
1143
+ print("=====key_moments=====")
1144
+ print(key_moments)
1145
+ print("=====key_moments=====")
1146
+ image_links = {entry['start_time']: entry['screenshot_path'] for entry in formatted_transcript}
1147
+ for moment in key_moments:
1148
+ start_time = moment['start']
1149
+ end_time = moment['end']
1150
+ moment_images = [image_links[time] for time in image_links if start_time <= time <= end_time]
1151
+ moment['images'] = moment_images
1152
+
1153
+ return key_moments
1154
+
1155
+ def get_key_moments_html(key_moments):
1156
+ """
1157
+ Generates HTML for key moments with a left-side gallery and right-side text.
1158
+ """
1159
+ css = """
1160
+ <style>
1161
+ /* Existing CSS from your sample ... */
1162
+
1163
+ .gallery-container {
1164
+ display: flex;
1165
+ align-items: center;
1166
+ margin-bottom: 20px;
1167
+ }
1168
+ .image-container {
1169
+ position: relative;
1170
+ max-height: 350px;
1171
+ flex: 1;
1172
+ }
1173
+ .image-container img {
1174
+ max-height: 350px;
1175
+ display: block;
1176
+ margin: 0 auto; /* Center the image */
1177
+ }
1178
+ .text-content {
1179
+ flex: 2;
1180
+ margin-left: 20px;
1181
+ }
1182
+ .arrow {
1183
+ cursor: pointer;
1184
+ user-select: none;
1185
+ position: absolute;
1186
+ top: 50%;
1187
+ transform: translateY(-50%);
1188
+ background-color: rgba(255, 255, 255, 0.8);
1189
+ border: none;
1190
+ padding: 10px;
1191
+ font-size: 24px;
1192
+ z-index: 10;
1193
+ }
1194
+ .arrow-prev { left: 0; }
1195
+ .arrow-next { right: 0; }
1196
+ </style>
1197
+ """
1198
+
1199
+ key_moments_html = "" + css
1200
+
1201
+ for i, moment in enumerate(key_moments):
1202
+ start_time = moment['start']
1203
+ end_time = moment['end']
1204
+ text = moment['text']
1205
+ transcript = moment['transcript']
1206
+ images = moment['images']
1207
+ image_elements = "".join([f'<img src="{img}" alt="Image {idx}" class="slide-image slide-image-{i}-{idx}" style="display: {"" if idx == 0 else "none"};" />' for idx, img in enumerate(images)])
1208
+
1209
+ key_moments_html += f"""
1210
+ <div class="gallery-container">
1211
+ <div class="image-container">
1212
+ <button class="arrow arrow-prev" onclick="changeImage(-1, {len(images)}, {i})">&#10094;</button>
1213
+ {image_elements}
1214
+ <button class="arrow arrow-next" onclick="changeImage(1, {len(images)}, {i})">&#10095;</button>
1215
+ </div>
1216
+ <div class="text-content">
1217
+ <h3>{start_time} - {end_time}</h3>
1218
+ <p><strong>摘要:</strong> {text}</p>
1219
+ <p><strong>逐字稿:</strong> {transcript}</p>
1220
+ </div>
1221
+ </div>
1222
+ """
1223
+
1224
+ return key_moments_html
1225
+
1226
  # ---- LLM CRUD ----
1227
  def enable_edit_mode():
1228
  return gr.update(interactive=True)
 
1711
  });
1712
  }
1713
  </script>
1714
+
1715
+ <script>
1716
+ function changeImage(direction, count, galleryIndex) {
1717
+ // Find the current visible image by iterating over possible indices
1718
+ var currentImage = null;
1719
+ var currentIndex = -1;
1720
+ for (var i = 0; i < count; i++) {
1721
+ var img = document.querySelector('.slide-image-' + galleryIndex + '-' + i);
1722
+ if (img && img.style.display !== 'none') {
1723
+ currentImage = img;
1724
+ currentIndex = i;
1725
+ break;
1726
+ }
1727
+ }
1728
+
1729
+ // If no current image is visible, show the first one and return
1730
+ if (currentImage === null) {
1731
+ document.querySelector('.slide-image-' + galleryIndex + '-0').style.display = 'block';
1732
+ console.error('No current image found for galleryIndex ' + galleryIndex + ', defaulting to first image.');
1733
+ return;
1734
+ }
1735
+
1736
+ // Hide the current image
1737
+ currentImage.style.display = 'none';
1738
+
1739
+ // Calculate the index of the next image to show
1740
+ var newIndex = (currentIndex + direction + count) % count;
1741
+
1742
+ // Select the next image and show it
1743
+ var nextImage = document.querySelector('.slide-image-' + galleryIndex + '-' + newIndex);
1744
+ if (nextImage) {
1745
+ nextImage.style.display = 'block';
1746
+ } else {
1747
+ console.error('No image found for galleryIndex ' + galleryIndex + ' and newIndex ' + newIndex);
1748
+ }
1749
+ }
1750
+ </script>
1751
  """
1752
 
1753
  with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, secondary_hue=gr.themes.colors.amber, text_size = gr.themes.sizes.text_lg), head=HEAD) as demo:
 
1818
  summary_delete_button = gr.Button("刪除", size="sm", variant="primary")
1819
  summary_create_button = gr.Button("建立", size="sm", variant="primary")
1820
  with gr.Row():
1821
+ df_summarise = gr.Textbox(container=True, show_copy_button=True, lines=40, show_label=False)
1822
+ with gr.Tab("關鍵時刻"):
1823
+ with gr.Row():
1824
+ key_moments_html = gr.HTML(value="")
1825
  with gr.Tab("教學備課"):
1826
  with gr.Row():
1827
  content_subject = gr.Dropdown(label="選擇主題", choices=["數學", "自然", "國文", "英文", "社會","物理", "化學", "生物", "地理", "歷史", "公民"], value="", visible=False)
 
2002
  btn_2,
2003
  btn_3,
2004
  df_string_output,
2005
+ df_summarise,
2006
+ key_moments_html,
2007
  mind_map,
2008
  mind_map_html,
2009
  transcript_html,