Seppukku commited on
Commit
d873b23
1 Parent(s): c43fd6d

return to start

Browse files
Files changed (3) hide show
  1. pages/Summary.py +45 -75
  2. requirements.txt +1 -0
  3. youtube.com_cookies.txt +2 -11
pages/Summary.py CHANGED
@@ -1,24 +1,21 @@
1
  import streamlit as st
2
- import os
3
  from youtube_transcript_api import YouTubeTranscriptApi
4
  import anthropic
 
5
  from dotenv import load_dotenv
6
  import re
7
- import json
8
- import yt_dlp
9
 
10
  # Загрузка переменных окружения из .env файла
11
  load_dotenv()
12
 
13
  # Получаем ключи API из переменных окружения
 
14
  claude_api_key = os.getenv("CLAUDE_API_KEY")
15
 
16
  # Инициализация клиента Claude
17
  client = anthropic.Client(api_key=claude_api_key)
18
 
19
- # Путь к файлу с куками (обязательно добавьте ваш файл с куками в репозиторий или папку, где размещаете приложение)
20
- cookies_file = 'youtube.com_cookies.txt'
21
-
22
  # Функции для работы с видео
23
  def get_video_id(url):
24
  if "v=" in url:
@@ -29,60 +26,13 @@ def get_video_id(url):
29
 
30
  def get_transcript(video_id):
31
  try:
32
- # Попытка получить субтитры через youtube_transcript_api (проверка русского и английского языков)
33
  transcript = YouTubeTranscriptApi.get_transcript(video_id, languages=['ru', 'en'])
 
34
  return ' '.join([x['text'] for x in transcript])
35
  except Exception as e:
36
- st.warning(f"Не удалось получить транскрипт через YouTube Transcript API: {e}")
37
- st.info("Пробуем получить автоматические субтитры через yt-dlp...")
38
-
39
- # Если не получилось через YouTube Transcript API, пробуем через yt-dlp с куками
40
- try:
41
- result = get_transcript_via_ytdlp(video_id)
42
- if result:
43
- return result
44
- except Exception as e:
45
- st.error(f"Ошибка получения субтитров через yt-dlp: {e}")
46
-
47
  return None
48
 
49
- def get_transcript_via_ytdlp(video_id):
50
- try:
51
- # Настройка параметров для загрузки субтитров через yt-dlp с использованием файла куков
52
- ydl_opts = {
53
- 'writesubtitles': True,
54
- 'writeautomaticsub': True, # Загружаем автоматические субтитры
55
- 'subtitlesformat': 'json', # Формат субтитров
56
- 'skip_download': True, # Не загружаем видео
57
- 'subtitleslangs': ['ru', 'en'], # Поддержка русского и английского языков
58
- 'outtmpl': f'{video_id}.%(ext)s',
59
- 'cookies': cookies_file # Используем файл куков для обхода ограничения
60
- }
61
-
62
- # Использование yt-dlp API для загрузки субтитров
63
- with yt_dlp.YoutubeDL(ydl_opts) as ydl:
64
- info_dict = ydl.extract_info(f"https://www.youtube.com/watch?v={video_id}", download=False)
65
- subtitles = info_dict.get('subtitles', {})
66
- automatic_captions = info_dict.get('automatic_captions', {})
67
-
68
- # Определяем основной язык видео
69
- language = 'en' if 'en' in automatic_captions else 'ru'
70
-
71
- # Проверяем наличие автоматических субтитров
72
- if language in automatic_captions:
73
- caption_url = automatic_captions[language][0]['url']
74
- ydl.download([caption_url])
75
-
76
- with open(f"{video_id}.{language}.vtt.json", 'r', encoding='utf-8') as file:
77
- data = json.load(file)
78
- transcript = ' '.join([item['text'] for item in data['events']])
79
- return transcript
80
- else:
81
- raise RuntimeError("Автоматические субтитры не найдены.")
82
-
83
- except Exception as e:
84
- raise RuntimeError(f"Ошибка при загрузке субтитров через yt-dlp: {e}")
85
-
86
  def generate_summary_with_claude(transcript, prompt_text):
87
  try:
88
  message = client.messages.create(
@@ -111,20 +61,32 @@ def generate_summary_with_claude(transcript, prompt_text):
111
  except Exception as e:
112
  st.error(f"Ошибка при обращении к Claude: {e}")
113
  return None
114
-
115
  def format_answer(answer):
116
- # Форматирует ответ с учетом следующих условий: нумерованные списки, выделение кода
 
 
 
 
 
 
117
  parts = re.split(r'(```.*?```)', answer, flags=re.DOTALL)
118
 
119
  for part in parts:
120
  if part.startswith('```') and part.endswith('```'):
 
121
  language_and_code = part[3:-3].strip().split("\n", 1)
122
- if len(language_and_code == 2):
123
  language, code = language_and_code
124
  st.code(code, language=language)
125
  else:
126
  st.code(language_and_code[0])
127
  else:
 
 
 
 
 
128
  paragraphs = part.split('\n\n')
129
  for paragraph in paragraphs:
130
  if re.match(r'^\d+\.\s', paragraph): # Нумерованный список
@@ -134,24 +96,28 @@ def format_answer(answer):
134
  else: # Обычные абзацы
135
  st.markdown(paragraph)
136
 
 
137
  def format_as_numbered_list(text):
138
- cleaned_text = re.sub(r'\d+\n', '', text)
139
- cleaned_text = re.sub(r'\d+\s+', '', cleaned_text)
 
140
  sentences = cleaned_text.splitlines()
141
 
 
142
  numbered_list = ""
143
  for i, sentence in enumerate(sentences, start=1):
144
- if sentence.strip():
145
  numbered_list += f"{i}. {sentence.strip()}\n"
146
 
147
  return numbered_list
148
 
149
  # STREAMLIT
150
 
 
151
  st.title("Смотрим лекции YouTube как Суперчеловек 💪")
152
  st.subheader("Можно сделать самые разные виды анализа. Зацените! И выберите, что важно нужно прямо сейчас?")
153
 
154
-
155
  summary_options = {
156
  "🕒 Хочу переслушать лекцию. Покажи таймстемпы": "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.",
157
  "📝 Ценю свое время. Напиши умное саммари: темы, тезисы, рекомендации автора": "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.",
@@ -162,37 +128,41 @@ summary_options = {
162
  "⚖️ Готовлюсь к интервью на работу. Это мок интервью, выпиши все вопросы": "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."
163
  }
164
 
 
165
  selected_summary = st.radio("Выберите тип анализа:", list(summary_options.keys()))
 
 
166
  url = st.text_input("Вставьте ссылку на YouTube видео")
167
 
 
168
  if st.button("Создать материал"):
169
  if url:
170
  video_id = get_video_id(url)
171
  if video_id:
172
  transcript = get_transcript(video_id)
173
-
174
  if transcript:
175
  prompt_text = summary_options[selected_summary]
176
 
177
- # Спиннер с разным текстом
178
  spinner_text = {
179
- "🕒 Хочу переслушать лекцию. Покажи таймстемпы": "🕒 Сейчас покажем таймстемп начала каждой темы...",
180
- "📝 Ценю свое время. Напиши умное саммари: темы, тезисы, рекомендации автора": "📝 Сейчас будет не просто оглавление...",
181
- "💡 Заскучал. Хочу только не избитые тезисы": "💡 Читаем не самые базовые мысли...",
182
- "✍️ Не хочу писать конспект детальный - напиши вместо меня": "✍️ Создаем самый детальный конспект...",
183
- "🔍 Подсвети “фигню” в этом видео. Некорректные тезисы, упущения, противоречия": "🔍 Лекторы тоже люди...",
184
- "🎓 Нужно отработать материал. Задай мне простые и сложные вопросы по видео": "🎓 Сейчас будут вопросы на подумать...",
185
- "⚖️ Готовлюсь к интервью на работу. Это мок интервью, выпиши все вопросы": "⚖️ Проведем репетицию интервью..."
186
- }
187
 
 
188
  with st.spinner(spinner_text[selected_summary]):
189
  result = generate_summary_with_claude(transcript, prompt_text)
190
 
 
191
  if result:
192
  format_answer(result)
193
- else:
194
- st.error("Субтитры не найдены.")
195
  else:
196
- st.error("Не удалось извлечь ID видео.")
197
  else:
198
  st.error("Введите корректную ссылку на видео.")
 
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:
 
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
  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): # Нумерованный список
 
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
  "⚖️ Готовлюсь к интервью на работу. Это мок интервью, выпиши все вопросы": "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("Введите корректную ссылку на видео.")
requirements.txt CHANGED
@@ -13,3 +13,4 @@ google-api-python-client
13
  faiss-cpu
14
  Pillow
15
  yt-dlp
 
 
13
  faiss-cpu
14
  Pillow
15
  yt-dlp
16
+ youtube-dl
youtube.com_cookies.txt CHANGED
@@ -1,20 +1,11 @@
1
  # Netscape HTTP Cookie File
2
  # This file is generated by yt-dlp. Do not edit.
3
 
4
- .youtube.com TRUE / FALSE 1760100965 APISID QaK8K92wONkT790k/AFJ44oiJuhh0N8HGC
5
- .youtube.com TRUE / FALSE 1760100965 HSID Am1bUTzMqr6dQjvwN
6
- .youtube.com TRUE / TRUE 1753254130 LOGIN_INFO AFmmF2swRgIhALsGUWXFjv7PV7X08BGhW9HDvV9eV4PjA3e6q1hfvptgAiEAmjJwUfL34w1dm62QnvvtSSy-jSIopYYAvw1MW298j_0:QUQ3MjNmeE5vY2pnMzFmc2gwa3diYlpqalZFT3VESW5NNWQ5SkllQVFUVXZkWjlILU82V0VaMEdENVFDY0hPcUtQaXVZZnVhWDVBU3M5MFZxdEEwbGRNUVVWYXdFLXBTTVFKZlNxZDZqOVRQQTlZVGdWYUwwMTZ6Mk1qZzlkTHhMbUN1OWl5elBTVE16dEdQN1JtdlRDdUNBTjJwMjFxbk5R
7
  .youtube.com TRUE / FALSE 0 PREF f6=40000000&tz=UTC&f7=100&f5=30000&hl=en
8
- .youtube.com TRUE / TRUE 1760100965 SAPISID UQDxhyn9YE0r587X/AnJ3T3p1fidEWDgTK
9
- .youtube.com TRUE / FALSE 1760100965 SID g.a000nQglg9HyyDJBOYrrQuRmZuwioF7xk3b4Vl3EORsMagTBvV1re-wzqCJwjnUuqYipkoGd5QACgYKAXYSARQSFQHGX2MiP-CUBKAPFiQt-h7LjXrTzBoVAUF8yKrVVyiu2BMR-9zHG1DFhN9b0076
10
- .youtube.com TRUE / FALSE 1757514563 SIDCC AKEyXzVUVcF6LNDWwO6Lqb9pzg0OCWMxjR9zoyHzuxm9VxBc_oIwnzkEuZVYxMLmRB0H8YIAIBw
11
- .youtube.com TRUE / TRUE 1760100965 SSID A_wPouNPwQxl2DxVp
12
  .youtube.com TRUE / TRUE 1741530562 VISITOR_INFO1_LIVE 81Ny6Vdr0Bs
13
  .youtube.com TRUE / TRUE 1741530562 VISITOR_PRIVACY_METADATA CgJSVRIEGgAgYA%3D%3D
14
- .youtube.com TRUE / TRUE 0 YSC wNr111SRVFQ
15
- .youtube.com TRUE / TRUE 1760100965 __Secure-1PAPISID UQDxhyn9YE0r587X/AnJ3T3p1fidEWDgTK
16
- .youtube.com TRUE / TRUE 1760100965 __Secure-1PSID g.a000nQglg9HyyDJBOYrrQuRmZuwioF7xk3b4Vl3EORsMagTBvV1rtIV-2rZ8xhKZfZ7GAp3L_AACgYKARMSARQSFQHGX2Miu-blEbM7-Pc_awrJ6D2zUxoVAUF8yKrzk1DtNBwh4XVW-KzwEjUR0076
17
- .youtube.com TRUE / TRUE 1757514563 __Secure-1PSIDCC AKEyXzUkzTzerZGDdGFg-J2sfmdcHCQI5f3s9278uuKnpeixKtsOxzrZ3MVhzjFk7zXUPqTU_g
18
  .youtube.com TRUE / TRUE 1757513604 __Secure-1PSIDTS sidts-CjIBUFGohwxHu0iEulDbRE_9uf6vRel820hsr4MioM70Jl_idW0I4PiLDS4LLDJMyq7YXBAA
19
  .youtube.com TRUE / TRUE 1760100965 __Secure-3PAPISID UQDxhyn9YE0r587X/AnJ3T3p1fidEWDgTK
20
  .youtube.com TRUE / TRUE 1760100965 __Secure-3PSID g.a000nQglg9HyyDJBOYrrQuRmZuwioF7xk3b4Vl3EORsMagTBvV1r6tl3OIQ8dSt5bnhATTQ0WAACgYKAbkSARQSFQHGX2MilBvwCcpK8Zemzvu3FP4jMhoVAUF8yKrzu9dbX1bJUYS1ed44oBv80076
 
1
  # Netscape HTTP Cookie File
2
  # This file is generated by yt-dlp. Do not edit.
3
 
4
+ .youtube.com TRUE / TRUE 1725985189 GPS 1
 
 
5
  .youtube.com TRUE / FALSE 0 PREF f6=40000000&tz=UTC&f7=100&f5=30000&hl=en
6
+ .youtube.com TRUE / TRUE 1725988325 SAPISID UQDxhyn9YE0r587X/AnJ3T3p1fidEWDgTK
 
 
 
7
  .youtube.com TRUE / TRUE 1741530562 VISITOR_INFO1_LIVE 81Ny6Vdr0Bs
8
  .youtube.com TRUE / TRUE 1741530562 VISITOR_PRIVACY_METADATA CgJSVRIEGgAgYA%3D%3D
 
 
 
 
9
  .youtube.com TRUE / TRUE 1757513604 __Secure-1PSIDTS sidts-CjIBUFGohwxHu0iEulDbRE_9uf6vRel820hsr4MioM70Jl_idW0I4PiLDS4LLDJMyq7YXBAA
10
  .youtube.com TRUE / TRUE 1760100965 __Secure-3PAPISID UQDxhyn9YE0r587X/AnJ3T3p1fidEWDgTK
11
  .youtube.com TRUE / TRUE 1760100965 __Secure-3PSID g.a000nQglg9HyyDJBOYrrQuRmZuwioF7xk3b4Vl3EORsMagTBvV1r6tl3OIQ8dSt5bnhATTQ0WAACgYKAbkSARQSFQHGX2MilBvwCcpK8Zemzvu3FP4jMhoVAUF8yKrzu9dbX1bJUYS1ed44oBv80076