Seppukku commited on
Commit
a96936f
1 Parent(s): d873b23
Files changed (1) hide show
  1. pages/Summary.py +205 -52
pages/Summary.py CHANGED
@@ -1,6 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
- from googleapiclient.discovery import build
3
- from youtube_transcript_api import YouTubeTranscriptApi
4
  import anthropic
5
  import os
6
  from dotenv import load_dotenv
@@ -10,13 +179,13 @@ import re
10
  load_dotenv()
11
 
12
  # Получаем ключи API из переменных окружения
13
- youtube_api_key = os.getenv("YOUTUBE_API_KEY")
14
  claude_api_key = os.getenv("CLAUDE_API_KEY")
15
 
16
  # Инициализация клиента Claude
17
  client = anthropic.Client(api_key=claude_api_key)
18
 
19
- # Функции для работы с видео
20
  def get_video_id(url):
21
  if "v=" in url:
22
  return url.split("v=")[1].split("&")[0]
@@ -24,15 +193,34 @@ def get_video_id(url):
24
  return url.split("youtu.be/")[1].split("?")[0]
25
  return None
26
 
 
27
  def get_transcript(video_id):
28
  try:
29
- transcript = YouTubeTranscriptApi.get_transcript(video_id, languages=['ru', 'en'])
30
- st.write(transcript)
31
- return ' '.join([x['text'] for x in transcript])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  except Exception as e:
33
- st.error(f"Ошибка получения транскрипта: {e}")
34
  return None
35
 
 
36
  def generate_summary_with_claude(transcript, prompt_text):
37
  try:
38
  message = client.messages.create(
@@ -61,20 +249,12 @@ def generate_summary_with_claude(transcript, prompt_text):
61
  except Exception as e:
62
  st.error(f"Ошибка при обращении к Claude: {e}")
63
  return None
64
-
 
65
  def format_answer(answer):
66
- """
67
- Форматирует ответ с учетом следующих условий:
68
- 1. Нумерованные списки
69
- 2. Выделение фрагментов кода
70
- 3. Корректное отображение абзацев и маркированных списков
71
- """
72
- # Разделим ответ на текстовые и кодовые блоки с помощью регулярных выражений
73
  parts = re.split(r'(```.*?```)', answer, flags=re.DOTALL)
74
-
75
  for part in parts:
76
  if part.startswith('```') and part.endswith('```'):
77
- # Убираем тройные кавычки и выводим содержимое как код
78
  language_and_code = part[3:-3].strip().split("\n", 1)
79
  if len(language_and_code) == 2:
80
  language, code = language_and_code
@@ -82,42 +262,35 @@ def format_answer(answer):
82
  else:
83
  st.code(language_and_code[0])
84
  else:
85
- # Обычный текст
86
- # Форматируем как нумерованный список, если необходимо
87
  if re.search(r'(\d+\.\s+)', part):
88
  part = format_as_numbered_list(part)
89
- # Обрабатываем абзацы и маркированные списки
90
  paragraphs = part.split('\n\n')
91
  for paragraph in paragraphs:
92
- if re.match(r'^\d+\.\s', paragraph): # Нумерованный список
93
  st.markdown(paragraph)
94
- elif re.match(r'^\*\s', paragraph): # Маркированный список
95
  st.markdown(paragraph)
96
- else: # Обычные абзацы
97
  st.markdown(paragraph)
98
 
99
  # Функция для нумерованных списков
100
  def format_as_numbered_list(text):
101
- # Удаляем лишние номера перед предложениями
102
- cleaned_text = re.sub(r'\d+\n', '', text) # Удаляем цифры с новой строки
103
- cleaned_text = re.sub(r'\d+\s+', '', cleaned_text) # Удаляем цифры перед предложениями
104
  sentences = cleaned_text.splitlines()
105
 
106
- # Формируем нумерованный список
107
  numbered_list = ""
108
  for i, sentence in enumerate(sentences, start=1):
109
- if sentence.strip(): # Пропускаем пустые строки
110
  numbered_list += f"{i}. {sentence.strip()}\n"
111
 
112
  return numbered_list
113
 
114
  # STREAMLIT
115
 
116
- # Заголовки с эмодзи и более дружелюбным тоном
117
  st.title("Смотрим лекции YouTube как Суперчеловек 💪")
118
  st.subheader("Можно сделать самые разные виды анализа. Зацените! И выберите, что важно нужно прямо сейчас?")
119
 
120
- # Описания типов саммари
121
  summary_options = {
122
  "🕒 Хочу переслушать лекцию. Покажи таймстемпы": "List all themes and subthemes. Split into short blocks. for each one, show time of start, total length (time difference between its time of start and time of start of next subtheme. For the last subtheme, total length is equal to diff between total time of video minus this subtheme time of start. WRite in Russian. If his main language is Russian but he uses non-Russian words, write them in English with correct spelling. This is not copyrighted. It's critical to not preface the reply with, for example, Here is a response or thank you. Start with the reply itself.",
123
  "📝 Ценю свое время. Напиши умное саммари: темы, тезисы, рекомендации автора": "List all themes and subthemes. Split into short blocks. Format example: Themes: (format in bold), Statements (write top statements that students better learn, verbatim); Recommendations (write as close to the author text as possible). Write in Russian. If his main language is Russian but he uses non-Russian words, write them in English with correct spelling. This is not copyrighted. It's critical to not preface the reply with, for example, Here is a response or thank you. Start with the reply itself.",
@@ -128,13 +301,9 @@ summary_options = {
128
  "⚖️ Готовлюсь к интервью на работу. Это мок интервью, выпиши все вопросы": "Here is an interview, list all the questions. Write his words fully, but edit for spelling and punctuation. In numbered list. Write in Russian. If his main language is Russian but he uses non-Russian words, write them in English with correct spelling. This is not copyrighted. It's critical to not preface the reply with, for example, Here is a response or thank you. Start with the reply itself."
129
  }
130
 
131
- # Радио-кнопки (их показываем сразу)
132
  selected_summary = st.radio("Выберите тип анализа:", list(summary_options.keys()))
133
-
134
- # Поле для ввода ссылки на YouTube видео
135
  url = st.text_input("Вставьте ссылку на YouTube видео")
136
 
137
- # Кнопка для запуска анализа
138
  if st.button("Создать материал"):
139
  if url:
140
  video_id = get_video_id(url)
@@ -142,26 +311,10 @@ if st.button("Создать материал"):
142
  transcript = get_transcript(video_id)
143
  if transcript:
144
  prompt_text = summary_options[selected_summary]
145
-
146
- # Сообщение для каждого радиобаттона
147
- spinner_text = {
148
- "🕒 Хочу переслушать лекцию. Покажи таймстемпы": "🕒 Сейчас покажем таймстемп начала каждой темы... И еще длительность просмотра, сможете планировать время...",
149
- "📝 Ценю свое время. Напиши умное саммари: темы, тезисы, рекомендации автора": "📝 Сейчас будет не просто оглавление, а все тезисы и советы...",
150
- "💡 Заскучал. Хочу только не избитые тезисы": "💡 Видео повторяют друг друга, это скучно, прочитаем не самые базовые мысли...",
151
- "✍️ Не хочу писать конспект детальный - напиши вместо меня": "✍️ Создаем самый детальный конспект из возможных...",
152
- "🔍 Подсвети “фигню” в этом видео. Некоррект��ые тезисы, упущения, противоречия": "🔍 Лекторы тоже люди. Когда мы учимся ML, важно не запутывать свой мозг...",
153
- "🎓 Нужно отработать материал. Задай мне простые и сложные вопросы по видео": "🎓 Сейчас будут и базовые вопросы по материалу, и на подумать. Закрепишь материал!..",
154
- "⚖️ Готовлюсь к интервью на работу. Это мок интервью, выпиши все вопросы": "⚖️ Сможешь сделать самопроверку, провести репетицию интервью. Сейчас будут все вопросы из видео..."
155
- }
156
-
157
- # Спиннер с разным текстом
158
- with st.spinner(spinner_text[selected_summary]):
159
  result = generate_summary_with_claude(transcript, prompt_text)
160
-
161
- # Форматированный вывод результата с поддержкой кода
162
  if result:
163
  format_answer(result)
164
-
165
  else:
166
  st.error("Не удалось извлечь видео ID из ссылки.")
167
  else:
 
1
+ # import streamlit as st
2
+ # from googleapiclient.discovery import build
3
+ # from youtube_transcript_api import YouTubeTranscriptApi
4
+ # import anthropic
5
+ # import os
6
+ # from dotenv import load_dotenv
7
+ # import re
8
+
9
+ # # Загрузка переменных окружения из .env файла
10
+ # load_dotenv()
11
+
12
+ # # Получаем ключи API из переменных окружения
13
+ # youtube_api_key = os.getenv("YOUTUBE_API_KEY")
14
+ # claude_api_key = os.getenv("CLAUDE_API_KEY")
15
+
16
+ # # Инициализация клиента Claude
17
+ # client = anthropic.Client(api_key=claude_api_key)
18
+
19
+ # # Функции для работы с видео
20
+ # def get_video_id(url):
21
+ # if "v=" in url:
22
+ # return url.split("v=")[1].split("&")[0]
23
+ # elif "youtu.be/" in url:
24
+ # return url.split("youtu.be/")[1].split("?")[0]
25
+ # return None
26
+
27
+ # def get_transcript(video_id):
28
+ # try:
29
+ # transcript = YouTubeTranscriptApi.get_transcript(video_id, languages=['ru', 'en'])
30
+ # st.write(transcript)
31
+ # return ' '.join([x['text'] for x in transcript])
32
+ # except Exception as e:
33
+ # st.error(f"Ошибка получения транскрипта: {e}")
34
+ # return None
35
+
36
+ # def generate_summary_with_claude(transcript, prompt_text):
37
+ # try:
38
+ # message = client.messages.create(
39
+ # model="claude-3-5-sonnet-20240620",
40
+ # extra_headers={"anthropic-beta": "prompt-caching-2024-07-31"},
41
+ # max_tokens=2000,
42
+ # temperature=0.1,
43
+ # messages=[
44
+ # {
45
+ # "role": "user",
46
+ # "content": [
47
+ # {"type": "text", "text": "<book>" + transcript + "</book>", "cache_control": {"type": "ephemeral"}},
48
+ # {"type": "text", "text": prompt_text},
49
+ # ],
50
+ # }
51
+ # ]
52
+ # )
53
+
54
+ # response_text = " ".join([block['text'] if isinstance(block, dict) and 'text' in block else str(block) for block in message.content])
55
+ # clean_summary = response_text.replace("\\n", " ").replace("TextBlock(text=", "").replace("type='text')", "")
56
+ # paragraphs = clean_summary.split('. ')
57
+ # formatted_summary = '\n\n'.join(paragraphs)
58
+
59
+ # return formatted_summary
60
+
61
+ # except Exception as e:
62
+ # st.error(f"Ошибка при обращении к Claude: {e}")
63
+ # return None
64
+
65
+ # def format_answer(answer):
66
+ # """
67
+ # Форматирует ответ с учетом следующих условий:
68
+ # 1. Нумерованные списки
69
+ # 2. Выделение фрагментов кода
70
+ # 3. Корректное отображение абзацев и маркированных списков
71
+ # """
72
+ # # Разделим ответ на текстовые и кодовые блоки с помощью регулярных выражений
73
+ # parts = re.split(r'(```.*?```)', answer, flags=re.DOTALL)
74
+
75
+ # for part in parts:
76
+ # if part.startswith('```') and part.endswith('```'):
77
+ # # Убираем тройные кавычки и выводим содержимое как код
78
+ # language_and_code = part[3:-3].strip().split("\n", 1)
79
+ # if len(language_and_code) == 2:
80
+ # language, code = language_and_code
81
+ # st.code(code, language=language)
82
+ # else:
83
+ # st.code(language_and_code[0])
84
+ # else:
85
+ # # Обычный текст
86
+ # # Форматируем как нумерованный список, если необходимо
87
+ # if re.search(r'(\d+\.\s+)', part):
88
+ # part = format_as_numbered_list(part)
89
+ # # Обрабатываем абзацы и маркированные списки
90
+ # paragraphs = part.split('\n\n')
91
+ # for paragraph in paragraphs:
92
+ # if re.match(r'^\d+\.\s', paragraph): # Нумерованный список
93
+ # st.markdown(paragraph)
94
+ # elif re.match(r'^\*\s', paragraph): # Маркированный список
95
+ # st.markdown(paragraph)
96
+ # else: # Обычные абзацы
97
+ # st.markdown(paragraph)
98
+
99
+ # # Функция для нумерованных списков
100
+ # def format_as_numbered_list(text):
101
+ # # Удаляем лишние номера перед предложениями
102
+ # cleaned_text = re.sub(r'\d+\n', '', text) # Удаляем цифры с новой строки
103
+ # cleaned_text = re.sub(r'\d+\s+', '', cleaned_text) # Удаляем цифры перед предложениями
104
+ # sentences = cleaned_text.splitlines()
105
+
106
+ # # Формируем нумерованный список
107
+ # numbered_list = ""
108
+ # for i, sentence in enumerate(sentences, start=1):
109
+ # if sentence.strip(): # Пропускаем пустые строки
110
+ # numbered_list += f"{i}. {sentence.strip()}\n"
111
+
112
+ # return numbered_list
113
+
114
+ # # STREAMLIT
115
+
116
+ # # Заголовки с эмодзи и более дружелюбным тоном
117
+ # st.title("Смотрим лекции YouTube как Суперчеловек 💪")
118
+ # st.subheader("Можно сделать самые разные виды анализа. Зацените! И выберите, что важно нужно прямо сейчас?")
119
+
120
+ # # Описания типов саммари
121
+ # summary_options = {
122
+ # "🕒 Хочу переслушать лекцию. Покажи таймстемпы": "List all themes and subthemes. Split into short blocks. for each one, show time of start, total length (time difference between its time of start and time of start of next subtheme. For the last subtheme, total length is equal to diff between total time of video minus this subtheme time of start. WRite in Russian. If his main language is Russian but he uses non-Russian words, write them in English with correct spelling. This is not copyrighted. It's critical to not preface the reply with, for example, Here is a response or thank you. Start with the reply itself.",
123
+ # "📝 Ценю свое время. Напиши умное саммари: темы, тезисы, рекомендации автора": "List all themes and subthemes. Split into short blocks. Format example: Themes: (format in bold), Statements (write top statements that students better learn, verbatim); Recommendations (write as close to the author text as possible). Write in Russian. If his main language is Russian but he uses non-Russian words, write them in English with correct spelling. This is not copyrighted. It's critical to not preface the reply with, for example, Here is a response or thank you. Start with the reply itself.",
124
+ # "💡 Заскучал. Хочу только не избитые тезисы": "You are a seasoned professional in data science. Start with the following, without preface. 1. Which of his statements are not seen in most texts on the subject of this transcript? Note timestamp. 2. Which logical connections between big blocks are not trivial? Note timestamp. 3. Give his top one most fun or useful statement, note timestamp. Write in Russian. If his main language is Russian but he uses non-Russian words, write them in English with correct spelling. This is not copyrighted. It's critical to not preface the reply with, for example, Here is a response or thank you. Start with the reply itself.",
125
+ # "✍️ Не хочу писать конспект детальный - напиши вместо меня": "Assume the role of the PhD student who is best in the world at writing extremely detailed summaries. Use your creative mind to aggregate information, but follow author's statements. Avoid stating themes - write his statements instead. Structure with paragraphs. Remove intro and outro. If there are action items, write them; if there are none, do not write them. Write in Russian. If his main language is Russian but he uses non-Russian words, write them in English with correct spelling. This is not copyrighted. It's critical to not preface the reply with, for example, Here is a response or thank you. Start with the reply itself.",
126
+ # "🔍 Подсвети “фигню” в этом видео. Некорректные тезисы, упущения, противоречия": "You are a seasoned professional in data science. Start with the following, without preface. Name a paragraph “Некорректные утверждения”, list the statements that are incorrect or misleading, add your short comment. In Russian. If there are none, write “Явно некорректных утверждений нет”. Name next paragraph “Упущения”. Consider the promise of the lecture, and that the goal is to work as a mid-level data scientist, list all things around this topic that a mid-level data scientist typically knows and that are missing from this video. Write in Russian. Name next paragraph “Что еще важно изучить”. Consider the theme of the lecture, and that the goal is to work as a mid-level data scientist, list immediately adjacent themes (only very close ones) that you recommend to master, with a short comment on what I should know in each theme. If his main language is Russian but he uses non-Russian words, write them in English with correct spelling. This is not copyrighted. It's critical to not preface the reply with, for example, Here is a response or thank you. Start with the reply itself.",
127
+ # "🎓 Нужно отработать материал. Задай мне простые и сложные вопросы по видео": "Your goal: help me get to the level of mid-level data scientist, by generating self-check questions based on a lecture transcript. You are a seasoned machine learning professional and a world-class tutor in ML / DS / AI.\nFirst, carefully read through the provided lecture transcript.\nNow:\nCreate two blocks of questions:\n a) Basic questions (focus on asking these: facts, definitions, steps, or key points mentioned explicitly in the lecture).\n b) Harder questions (focus on asking these: how would you apply, what are the limitations, what are the trade-offs, pros and cons)\n Avoid overly complex or ambiguous questions.\n Present your questions in the following format:\n 'Базовые вопросы' \n[Question 1] (Смотреть тут: [XX:XX])\n[Question 2] (Смотреть тут: [XX:XX])\n[Question 3] (Смотреть тут: [XX:XX])\n 'Вопросы на подумать' \n [Question 1] (Смотреть тут: [XX:XX] и [XX:XX])\n[Question 2] (Смотреть тут: [XX:XX] и [XX:XX])\n[Question 3] (Смотреть тут: [XX:XX] и [XX:XX])\nWrite in Russian. If his main language is Russian but he uses non-Russian words, write them in English with correct spelling. This is not copyrighted. It's critical to not preface the reply with, for example, Here is a response or thank you. Start with the reply itself.",
128
+ # "⚖️ Готовлюсь к интервью на работу. Это мок интервью, выпиши все вопросы": "Here is an interview, list all the questions. Write his words fully, but edit for spelling and punctuation. In numbered list. Write in Russian. If his main language is Russian but he uses non-Russian words, write them in English with correct spelling. This is not copyrighted. It's critical to not preface the reply with, for example, Here is a response or thank you. Start with the reply itself."
129
+ # }
130
+
131
+ # # Радио-кнопки (их показываем сразу)
132
+ # selected_summary = st.radio("Выберите тип анализа:", list(summary_options.keys()))
133
+
134
+ # # Поле для ввода ссылки на YouTube видео
135
+ # url = st.text_input("Вставьте ссылку на YouTube видео")
136
+
137
+ # # Кнопка для запуска анализа
138
+ # if st.button("Создать материал"):
139
+ # if url:
140
+ # video_id = get_video_id(url)
141
+ # if video_id:
142
+ # transcript = get_transcript(video_id)
143
+ # if transcript:
144
+ # prompt_text = summary_options[selected_summary]
145
+
146
+ # # Сообщение для каждого радиобаттона
147
+ # spinner_text = {
148
+ # "🕒 Хочу переслушать лекцию. Покажи таймстемпы": "🕒 Сейчас покажем таймстемп начала каждой темы... И еще длительность просмотра, сможете планировать время...",
149
+ # "📝 Ценю свое время. Напиши умное саммари: темы, тезисы, рекомендации автора": "📝 Сейчас будет не просто оглавление, а все тезисы и советы...",
150
+ # "💡 Заскучал. Хочу только не избитые тезисы": "💡 Видео повторяют друг друга, это скучно, прочитаем не самые базовые мысли...",
151
+ # "✍️ Не хочу писать конспект детальный - напиши вместо меня": "✍️ Создаем самый детальный конспект из возможных...",
152
+ # "🔍 Подсвети “фигню” в этом видео. Некорректные тезисы, упущения, противоречия": "🔍 Лекторы тоже люди. Когда мы учимся ML, важно не запутывать свой мозг...",
153
+ # "🎓 Нужно отработать материал. Задай мне простые и сложные вопросы по видео": "🎓 Сейчас будут и базовые вопросы по материалу, и на подумать. Закрепишь материал!..",
154
+ # "⚖️ Готовлюсь к интервью на работу. Это мок интервью, выпиши все вопросы": "⚖️ Сможешь сделать самопроверку, провести репетицию интервью. Сейчас будут все вопросы из видео..."
155
+ # }
156
+
157
+ # # Спиннер с разным текстом
158
+ # with st.spinner(spinner_text[selected_summary]):
159
+ # result = generate_summary_with_claude(transcript, prompt_text)
160
+
161
+ # # Форматированный вывод результата с поддержкой кода
162
+ # if result:
163
+ # format_answer(result)
164
+
165
+ # else:
166
+ # st.error("Не удалось извлечь видео ID из ссылки.")
167
+ # else:
168
+ # st.error("Введите корректную ссылку на видео.")
169
+
170
+
171
  import streamlit as st
172
+ import requests
 
173
  import anthropic
174
  import os
175
  from dotenv import load_dotenv
 
179
  load_dotenv()
180
 
181
  # Получаем ключи API из переменных окружения
182
+ rapidapi_key = os.getenv("RAPIDAPI_KEY") # Ваш RapidAPI ключ
183
  claude_api_key = os.getenv("CLAUDE_API_KEY")
184
 
185
  # Инициализация клиента Claude
186
  client = anthropic.Client(api_key=claude_api_key)
187
 
188
+ # Функция для получения ID видео
189
  def get_video_id(url):
190
  if "v=" in url:
191
  return url.split("v=")[1].split("&")[0]
 
193
  return url.split("youtu.be/")[1].split("?")[0]
194
  return None
195
 
196
+ # Функция для получения субтитров с помощью RapidAPI
197
  def get_transcript(video_id):
198
  try:
199
+ url = "https://youtube-transcripts.p.rapidapi.com/youtube/transcript"
200
+ querystring = {"url":f"https://www.youtube.com/watch?v={video_id}","chunkSize":"500"}
201
+
202
+ headers = {
203
+ "x-rapidapi-key": rapidapi_key,
204
+ "x-rapidapi-host": "youtube-transcripts.p.rapidapi.com"
205
+ }
206
+
207
+ response = requests.request("GET", url, headers=headers, params=querystring)
208
+ st.write(response.json())
209
+ if response.status_code == 200:
210
+ transcript_data = response.json()
211
+ content = transcript_data.get('content', [])
212
+ # Извлекаем текст из каждого элемента в content
213
+ transcript = ' '.join([item['text'] for item in content])
214
+ st.write(transcript)
215
+ return transcript
216
+ else:
217
+ st.error(f"Ошибка получения субтитров: {response.text}")
218
+ return None
219
  except Exception as e:
220
+ st.error(f"Ошибка при обращении к RapidAPI: {e}")
221
  return None
222
 
223
+ # Функция для генерации саммари с помощью Claude
224
  def generate_summary_with_claude(transcript, prompt_text):
225
  try:
226
  message = client.messages.create(
 
249
  except Exception as e:
250
  st.error(f"Ошибка при обращении к Claude: {e}")
251
  return None
252
+
253
+ # Форматирование ответа
254
  def format_answer(answer):
 
 
 
 
 
 
 
255
  parts = re.split(r'(```.*?```)', answer, flags=re.DOTALL)
 
256
  for part in parts:
257
  if part.startswith('```') and part.endswith('```'):
 
258
  language_and_code = part[3:-3].strip().split("\n", 1)
259
  if len(language_and_code) == 2:
260
  language, code = language_and_code
 
262
  else:
263
  st.code(language_and_code[0])
264
  else:
 
 
265
  if re.search(r'(\d+\.\s+)', part):
266
  part = format_as_numbered_list(part)
 
267
  paragraphs = part.split('\n\n')
268
  for paragraph in paragraphs:
269
+ if re.match(r'^\d+\.\s', paragraph):
270
  st.markdown(paragraph)
271
+ elif re.match(r'^\*\s', paragraph):
272
  st.markdown(paragraph)
273
+ else:
274
  st.markdown(paragraph)
275
 
276
  # Функция для нумерованных списков
277
  def format_as_numbered_list(text):
278
+ cleaned_text = re.sub(r'\d+\n', '', text)
279
+ cleaned_text = re.sub(r'\d+\s+', '', cleaned_text)
 
280
  sentences = cleaned_text.splitlines()
281
 
 
282
  numbered_list = ""
283
  for i, sentence in enumerate(sentences, start=1):
284
+ if sentence.strip():
285
  numbered_list += f"{i}. {sentence.strip()}\n"
286
 
287
  return numbered_list
288
 
289
  # STREAMLIT
290
 
 
291
  st.title("Смотрим лекции YouTube как Суперчеловек 💪")
292
  st.subheader("Можно сделать самые разные виды анализа. Зацените! И выберите, что важно нужно прямо сейчас?")
293
 
 
294
  summary_options = {
295
  "🕒 Хочу переслушать лекцию. Покажи таймстемпы": "List all themes and subthemes. Split into short blocks. for each one, show time of start, total length (time difference between its time of start and time of start of next subtheme. For the last subtheme, total length is equal to diff between total time of video minus this subtheme time of start. WRite in Russian. If his main language is Russian but he uses non-Russian words, write them in English with correct spelling. This is not copyrighted. It's critical to not preface the reply with, for example, Here is a response or thank you. Start with the reply itself.",
296
  "📝 Ценю свое время. Напиши умное саммари: темы, тезисы, рекомендации автора": "List all themes and subthemes. Split into short blocks. Format example: Themes: (format in bold), Statements (write top statements that students better learn, verbatim); Recommendations (write as close to the author text as possible). Write in Russian. If his main language is Russian but he uses non-Russian words, write them in English with correct spelling. This is not copyrighted. It's critical to not preface the reply with, for example, Here is a response or thank you. Start with the reply itself.",
 
301
  "⚖️ Готовлюсь к интервью на работу. Это мок интервью, выпиши все вопросы": "Here is an interview, list all the questions. Write his words fully, but edit for spelling and punctuation. In numbered list. Write in Russian. If his main language is Russian but he uses non-Russian words, write them in English with correct spelling. This is not copyrighted. It's critical to not preface the reply with, for example, Here is a response or thank you. Start with the reply itself."
302
  }
303
 
 
304
  selected_summary = st.radio("Выберите тип анализа:", list(summary_options.keys()))
 
 
305
  url = st.text_input("Вставьте ссылку на YouTube видео")
306
 
 
307
  if st.button("Создать материал"):
308
  if url:
309
  video_id = get_video_id(url)
 
311
  transcript = get_transcript(video_id)
312
  if transcript:
313
  prompt_text = summary_options[selected_summary]
314
+ with st.spinner("Обрабатываем..."):
 
 
 
 
 
 
 
 
 
 
 
 
 
315
  result = generate_summary_with_claude(transcript, prompt_text)
 
 
316
  if result:
317
  format_answer(result)
 
318
  else:
319
  st.error("Не удалось извлечь видео ID из ссылки.")
320
  else: