Skip to content

Commit 1e30af3

Browse files
Add files via upload
1 parent 6df80a2 commit 1e30af3

File tree

2 files changed

+466
-0
lines changed

2 files changed

+466
-0
lines changed

Lirycs_extractor/Lyrics_extractor.py

Lines changed: 374 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,374 @@
1+
#!/usr/bin/env python
2+
import sys
3+
import re
4+
import urllib.request
5+
from youtube_transcript_api import YouTubeTranscriptApi
6+
7+
from PyQt5.QtCore import QThread, pyqtSignal, Qt
8+
from PyQt5.QtGui import QPixmap, QPalette, QColor
9+
from PyQt5.QtWidgets import (
10+
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
11+
QLabel, QLineEdit, QPushButton, QTextEdit, QMessageBox, QFileDialog,
12+
QComboBox, QCheckBox, QGroupBox, QFrame, QStyleFactory
13+
)
14+
15+
16+
def extract_video_id(url):
17+
"""
18+
Extracts the YouTube video ID from a given URL.
19+
Supports both standard and shortened URLs.
20+
"""
21+
pattern = r'(?:v=|\/)([0-9A-Za-z_-]{11}).*'
22+
match = re.search(pattern, url)
23+
if match:
24+
return match.group(1)
25+
return None
26+
27+
28+
def get_transcript(video_id, language='en'):
29+
"""
30+
Retrieves the transcript for the given YouTube video ID.
31+
Returns the concatenated transcript text.
32+
"""
33+
transcript_list = YouTubeTranscriptApi.get_transcript(video_id, languages=[language])
34+
transcript_text = " ".join([entry['text'] for entry in transcript_list])
35+
return transcript_text
36+
37+
38+
# Worker thread to fetch transcript without freezing the UI
39+
class TranscriptFetcher(QThread):
40+
transcript_fetched = pyqtSignal(str)
41+
error_occurred = pyqtSignal(str)
42+
43+
def __init__(self, video_id, language='en'):
44+
super().__init__()
45+
self.video_id = video_id
46+
self.language = language
47+
48+
def run(self):
49+
try:
50+
transcript = get_transcript(self.video_id, self.language)
51+
self.transcript_fetched.emit(transcript)
52+
except Exception as e:
53+
self.error_occurred.emit(str(e))
54+
55+
56+
class MainWindow(QMainWindow):
57+
def __init__(self):
58+
super().__init__()
59+
self.setWindowTitle("YouTube Transcript Fetcher")
60+
self.setGeometry(100, 100, 900, 600)
61+
self.transcript = ""
62+
self.setup_ui()
63+
64+
def setup_ui(self):
65+
# Set a main widget and apply a vertical layout
66+
self.main_widget = QWidget()
67+
self.setCentralWidget(self.main_widget)
68+
self.main_layout = QVBoxLayout(self.main_widget)
69+
self.main_widget.setLayout(self.main_layout)
70+
71+
# Title Section: A banner frame for the header
72+
self.banner_frame = QFrame()
73+
self.banner_frame.setObjectName("bannerFrame")
74+
self.banner_layout = QHBoxLayout()
75+
self.banner_frame.setLayout(self.banner_layout)
76+
77+
self.header_label = QLabel("YouTube Transcript Fetcher")
78+
self.header_label.setObjectName("headerLabel")
79+
self.header_label.setAlignment(Qt.AlignCenter)
80+
81+
self.banner_layout.addWidget(self.header_label)
82+
self.main_layout.addWidget(self.banner_frame)
83+
84+
# Description below the header
85+
self.description_label = QLabel("Easily fetch and save YouTube transcripts with a single click.")
86+
self.description_label.setAlignment(Qt.AlignCenter)
87+
self.main_layout.addWidget(self.description_label)
88+
89+
# Group box for URL input
90+
self.url_group = QGroupBox("Video URL")
91+
self.url_group_layout = QHBoxLayout()
92+
self.url_group.setLayout(self.url_group_layout)
93+
94+
self.url_input = QLineEdit()
95+
self.url_input.setPlaceholderText("Enter YouTube URL here...")
96+
self.fetch_button = QPushButton("Fetch Transcript")
97+
self.fetch_button.clicked.connect(self.fetch_transcript)
98+
99+
self.url_group_layout.addWidget(self.url_input)
100+
self.url_group_layout.addWidget(self.fetch_button)
101+
self.main_layout.addWidget(self.url_group)
102+
103+
# Group box for Video Thumbnail
104+
self.thumbnail_group = QGroupBox("Video Preview")
105+
self.thumbnail_layout = QVBoxLayout()
106+
self.thumbnail_group.setLayout(self.thumbnail_layout)
107+
108+
self.thumbnail_label = QLabel()
109+
self.thumbnail_label.setAlignment(Qt.AlignCenter)
110+
self.thumbnail_layout.addWidget(self.thumbnail_label)
111+
self.main_layout.addWidget(self.thumbnail_group)
112+
113+
# Group box for Transcript display
114+
self.transcript_group = QGroupBox("Transcript")
115+
self.transcript_group_layout = QVBoxLayout()
116+
self.transcript_group.setLayout(self.transcript_group_layout)
117+
118+
self.transcript_display = QTextEdit()
119+
self.transcript_display.setReadOnly(True)
120+
self.transcript_group_layout.addWidget(self.transcript_display)
121+
self.main_layout.addWidget(self.transcript_group)
122+
123+
# Group box for Actions (Save + Status)
124+
self.actions_group = QGroupBox("Actions")
125+
self.actions_layout = QHBoxLayout()
126+
self.actions_group.setLayout(self.actions_layout)
127+
128+
self.save_button = QPushButton("Save Transcript")
129+
self.save_button.clicked.connect(self.save_transcript)
130+
self.status_label = QLabel("Ready")
131+
132+
self.actions_layout.addWidget(self.save_button)
133+
self.actions_layout.addWidget(self.status_label)
134+
self.main_layout.addWidget(self.actions_group)
135+
136+
# Group box for Settings (Language + Theme)
137+
self.settings_group = QGroupBox("Settings")
138+
self.settings_layout = QHBoxLayout()
139+
self.settings_group.setLayout(self.settings_layout)
140+
141+
self.language_label = QLabel("Language:")
142+
self.language_combo = QComboBox()
143+
self.language_combo.addItems(["en", "es", "fr", "de", "it"]) # Example languages
144+
145+
self.theme_toggle = QCheckBox("Dark Mode")
146+
self.theme_toggle.stateChanged.connect(self.toggle_theme)
147+
148+
self.settings_layout.addWidget(self.language_label)
149+
self.settings_layout.addWidget(self.language_combo)
150+
self.settings_layout.addStretch(1)
151+
self.settings_layout.addWidget(self.theme_toggle)
152+
self.main_layout.addWidget(self.settings_group)
153+
154+
# Apply a custom style sheet for a more modern look
155+
self.apply_style_sheet()
156+
157+
def apply_style_sheet(self):
158+
"""
159+
Applies a style sheet to give the UI a more modern, consistent look.
160+
"""
161+
self.setStyleSheet("""
162+
/* Overall Window Style */
163+
QMainWindow {
164+
background-color: #f7f7f7;
165+
}
166+
167+
/* Banner Frame */
168+
#bannerFrame {
169+
background-color: #1976D2; /* A modern blue color */
170+
padding: 12px;
171+
}
172+
/* Header Label in Banner */
173+
#headerLabel {
174+
color: white;
175+
font-size: 22px;
176+
font-weight: 600;
177+
letter-spacing: 0.5px;
178+
}
179+
180+
/* Group Boxes */
181+
QGroupBox {
182+
font: 14px 'Arial';
183+
font-weight: bold;
184+
margin-top: 10px;
185+
border: 1px solid #ccc;
186+
border-radius: 8px;
187+
padding: 10px;
188+
}
189+
QGroupBox::title {
190+
subcontrol-origin: margin;
191+
subcontrol-position: top left;
192+
padding: 2px 5px;
193+
}
194+
195+
/* Labels */
196+
QLabel {
197+
font: 13px 'Arial';
198+
color: #333;
199+
}
200+
201+
/* Line Edit */
202+
QLineEdit {
203+
font: 13px 'Arial';
204+
border-radius: 5px;
205+
padding: 6px;
206+
border: 1px solid #bbb;
207+
background-color: #fff;
208+
}
209+
210+
/* Text Edit */
211+
QTextEdit {
212+
font: 13px 'Arial';
213+
border-radius: 5px;
214+
border: 1px solid #bbb;
215+
background-color: #fff;
216+
}
217+
218+
/* Push Buttons */
219+
QPushButton {
220+
font: 13px 'Arial';
221+
border-radius: 5px;
222+
padding: 6px 14px;
223+
background-color: #2196F3;
224+
color: white;
225+
border: none;
226+
}
227+
QPushButton:hover {
228+
background-color: #1976D2;
229+
}
230+
QPushButton:disabled {
231+
background-color: #9e9e9e;
232+
color: #f0f0f0;
233+
}
234+
235+
/* Check Box */
236+
QCheckBox {
237+
font: 13px 'Arial';
238+
color: #333;
239+
}
240+
241+
/* Combo Box */
242+
QComboBox {
243+
font: 13px 'Arial';
244+
border-radius: 5px;
245+
padding: 4px;
246+
border: 1px solid #bbb;
247+
background-color: #fff;
248+
}
249+
""")
250+
251+
def fetch_transcript(self):
252+
url = self.url_input.text().strip()
253+
if not url:
254+
QMessageBox.warning(self, "Input Error", "Please enter a YouTube URL.")
255+
return
256+
257+
video_id = extract_video_id(url) or url
258+
self.status_label.setText(f"Fetching transcript for video ID: {video_id}...")
259+
self.fetch_button.setEnabled(False)
260+
self.transcript_display.clear()
261+
self.thumbnail_label.clear()
262+
263+
# Load video thumbnail if available
264+
thumbnail_url = f"https://img.youtube.com/vi/{video_id}/0.jpg"
265+
try:
266+
data = urllib.request.urlopen(thumbnail_url).read()
267+
pixmap = QPixmap()
268+
pixmap.loadFromData(data)
269+
self.thumbnail_label.setPixmap(pixmap.scaled(320, 180, Qt.KeepAspectRatio))
270+
except Exception:
271+
# Thumbnail is optional; ignore errors if not available
272+
pass
273+
274+
language = self.language_combo.currentText()
275+
# Start background thread to fetch transcript
276+
self.worker = TranscriptFetcher(video_id, language)
277+
self.worker.transcript_fetched.connect(self.on_transcript_fetched)
278+
self.worker.error_occurred.connect(self.on_error)
279+
self.worker.start()
280+
281+
def on_transcript_fetched(self, transcript):
282+
self.transcript = transcript
283+
self.transcript_display.setPlainText(transcript)
284+
word_count = len(transcript.split())
285+
self.status_label.setText(f"Transcript retrieved. Word count: {word_count}")
286+
self.fetch_button.setEnabled(True)
287+
288+
def on_error(self, error_message):
289+
QMessageBox.critical(self, "Error Fetching Transcript", error_message)
290+
self.status_label.setText("Error fetching transcript.")
291+
self.fetch_button.setEnabled(True)
292+
293+
def save_transcript(self):
294+
if not self.transcript:
295+
QMessageBox.warning(self, "No Transcript", "There is no transcript to save.")
296+
return
297+
options = QFileDialog.Options()
298+
filename, _ = QFileDialog.getSaveFileName(
299+
self, "Save Transcript", "", "Text Files (*.txt);;All Files (*)", options=options
300+
)
301+
if filename:
302+
try:
303+
with open(filename, "w", encoding="utf-8") as f:
304+
f.write(self.transcript)
305+
self.status_label.setText(f"Transcript saved to {filename}")
306+
except Exception as e:
307+
QMessageBox.critical(self, "Save Error", str(e))
308+
self.status_label.setText("Error saving transcript.")
309+
310+
def toggle_theme(self, state):
311+
"""
312+
Switches between light and dark themes by applying custom palettes.
313+
"""
314+
if state == Qt.Checked:
315+
# Define dark palette
316+
dark_palette = QPalette()
317+
dark_palette.setColor(QPalette.Window, QColor(53, 53, 53))
318+
dark_palette.setColor(QPalette.WindowText, Qt.white)
319+
dark_palette.setColor(QPalette.Base, QColor(25, 25, 25))
320+
dark_palette.setColor(QPalette.AlternateBase, QColor(53, 53, 53))
321+
dark_palette.setColor(QPalette.ToolTipBase, Qt.white)
322+
dark_palette.setColor(QPalette.ToolTipText, Qt.white)
323+
dark_palette.setColor(QPalette.Text, Qt.white)
324+
dark_palette.setColor(QPalette.Button, QColor(53, 53, 53))
325+
dark_palette.setColor(QPalette.ButtonText, Qt.white)
326+
dark_palette.setColor(QPalette.BrightText, Qt.red)
327+
dark_palette.setColor(QPalette.Highlight, QColor(142, 45, 197))
328+
dark_palette.setColor(QPalette.HighlightedText, Qt.black)
329+
330+
QApplication.instance().setPalette(dark_palette)
331+
else:
332+
# Define light palette
333+
light_palette = QPalette()
334+
light_palette.setColor(QPalette.Window, Qt.white)
335+
light_palette.setColor(QPalette.WindowText, Qt.black)
336+
light_palette.setColor(QPalette.Base, Qt.white)
337+
light_palette.setColor(QPalette.AlternateBase, QColor(240, 240, 240))
338+
light_palette.setColor(QPalette.ToolTipBase, Qt.white)
339+
light_palette.setColor(QPalette.ToolTipText, Qt.black)
340+
light_palette.setColor(QPalette.Text, Qt.black)
341+
light_palette.setColor(QPalette.Button, QColor(240, 240, 240))
342+
light_palette.setColor(QPalette.ButtonText, Qt.black)
343+
light_palette.setColor(QPalette.BrightText, Qt.red)
344+
light_palette.setColor(QPalette.Highlight, QColor(0, 120, 215))
345+
light_palette.setColor(QPalette.HighlightedText, Qt.white)
346+
347+
QApplication.instance().setPalette(light_palette)
348+
349+
350+
if __name__ == "__main__":
351+
app = QApplication(sys.argv)
352+
# Use the modern Fusion style
353+
app.setStyle(QStyleFactory.create("Fusion"))
354+
355+
# Apply a light palette by default
356+
light_palette = QPalette()
357+
light_palette.setColor(QPalette.Window, Qt.white)
358+
light_palette.setColor(QPalette.WindowText, Qt.black)
359+
light_palette.setColor(QPalette.Base, Qt.white)
360+
light_palette.setColor(QPalette.AlternateBase, QColor(240, 240, 240))
361+
light_palette.setColor(QPalette.ToolTipBase, Qt.white)
362+
light_palette.setColor(QPalette.ToolTipText, Qt.black)
363+
light_palette.setColor(QPalette.Text, Qt.black)
364+
light_palette.setColor(QPalette.Button, QColor(240, 240, 240))
365+
light_palette.setColor(QPalette.ButtonText, Qt.black)
366+
light_palette.setColor(QPalette.BrightText, Qt.red)
367+
light_palette.setColor(QPalette.Highlight, QColor(0, 120, 215))
368+
light_palette.setColor(QPalette.HighlightedText, Qt.white)
369+
370+
app.setPalette(light_palette)
371+
372+
window = MainWindow()
373+
window.show()
374+
sys.exit(app.exec_())

0 commit comments

Comments
 (0)