반응형
반응형

[python] 유튜브 자막 가져오기 

 

 

""" 유튜브 자막 가져오기 
    pip install youtube-transcript-api
    
    -- 최신 버전으로 업데이트
    pip install --upgrade youtube-transcript-api

    https://www.youtube.com/watch?v=XyljmT8dGA4
    
    자막있는 동영상 : https://www.youtube.com/watch?v=zRz9q8dPjC4
"""


from youtube_transcript_api import YouTubeTranscriptApi
# youtube_transcript_api._errors 에서 TooManyRequests를 제외하고 임포트합니다.
# TooManyRequests는 더 이상 직접 임포트할 수 없는 것으로 보입니다.
from youtube_transcript_api._errors import NoTranscriptFound, TranscriptsDisabled, VideoUnavailable

def get_youtube_transcript(video_id, languages=['ko', 'en']):
    """
    주어진 YouTube 동영상 ID와 언어 목록에 대해 자막을 가져옵니다.
    자동 생성 자막과 공식 자막을 모두 시도합니다.
    """
    try:
        # 우선 공식 자막을 시도
        transcript_list = YouTubeTranscriptApi.list_transcripts(video_id)

        # 사용 가능한 언어 목록에서 요청한 언어 중 하나를 찾아 가져옵니다.
        chosen_transcript = None
        for lang_code in languages:
            for transcript in transcript_list:
                if transcript.language_code == lang_code:
                    chosen_transcript = transcript
                    break
            if chosen_transcript:
                break

        if chosen_transcript:
            print(f"[{video_id}] {chosen_transcript.language} ({chosen_transcript.language_code}) 자막을 가져옵니다.")
            # fetch()는 이제 FetchedTranscript 객체를 반환하며, 이는 이터러블합니다.
            transcript_segments = chosen_transcript.fetch()
            return transcript_segments
        else:
            raise NoTranscriptFound(
                f"No suitable official transcript found for video {video_id} in languages {languages}.",
                video_id
            )

    except NoTranscriptFound:
        print(f"[{video_id}] 공식 자막을 찾을 수 없습니다. 자동 생성 자막을 시도합니다.")
        try:
            for lang_code in languages:
                try:
                    # get_transcript() 역시 이터러블한 객체를 반환하는 것으로 가정합니다.
                    transcript_segments = YouTubeTranscriptApi.get_transcript(video_id, languages=[lang_code], preserve_formatting=True)
                    print(f"[{video_id}] {lang_code} 자동 생성 자막을 가져왔습니다.")
                    return transcript_segments
                except NoTranscriptFound:
                    continue # 다음 언어로 시도
            print(f"[{video_id}] 요청된 언어 ({languages})로 자동 생성 자막도 찾을 수 없습니다.")
            return None # 적합한 자막을 찾지 못함
        except TranscriptsDisabled:
            print(f"[{video_id}] 이 동영상은 자막이 비활성화되어 있습니다.")
            return None
        except VideoUnavailable:
            print(f"[{video_id}] 동영상을 사용할 수 없거나 비공개/삭제되었습니다.")
            return None
        except Exception as e: # TooManyRequests를 포함한 모든 예외를 잡습니다.
            # 이 부분에서 TooManyRequests 에러를 포함하여 일반적인 오류를 처리합니다.
            print(f"[{video_id}] 자막을 가져오는 중 예기치 않은 오류가 발생했습니다: {e}")
            return None

    except TranscriptsDisabled:
        print(f"[{video_id}] 이 동영상은 자막이 비활성화되어 있습니다.")
        return None
    except VideoUnavailable:
        print(f"[{video_id}] 동영상을 사용할 수 없거나 비공개/삭제되었습니다.")
        return None
    except Exception as e: # TooManyRequests를 포함한 모든 예외를 잡습니다.
        # 이 부분에서 TooManyRequests 에러를 포함하여 일반적인 오류를 처리합니다.
        print(f"[{video_id}] 자막을 가져오는 중 예기치 않은 오류가 발생했습니다: {e}")
        return None


if __name__ == "__main__":
    # 예시 동영상 ID (실제 존재하는 동영상 ID로 변경해야 합니다)
    # 제가 추천해 드렸던 URL에서 ID를 추출했습니다.
    video_id_with_subtitle = "XyljmT8dGA4" # "모바일 유튜브 자동번역 한글자막 보는 방법"
    video_id_auto_caption = "XyljmT8dGA4" # 짧은 영상 (자동 생성 자막 가능성)
    #video_id_invalid = "invalid_video_id_123"
    video_id_invalid = "XyljmT8dGA4"

    print("--- 예제 1: 자막이 있는 동영상 ---")
    transcript_data = get_youtube_transcript(video_id_with_subtitle, languages=['ko', 'en'])
    if transcript_data:
        # segment.start, segment.duration, segment.text와 같이 속성으로 접근합니다.
        for i, segment in enumerate(transcript_data[:5]): # 슬라이싱은 여전히 가능해야 합니다.
            print(f"[{segment.start:.2f}-{segment.start + segment.duration:.2f}] {segment.text}")
        print(f"... (총 {len(list(transcript_data))}개 세그먼트)") # len()을 위해 list로 변환

        # 전체 자막 텍스트만 추출하고 싶다면:
        full_text = " ".join([segment.text for segment in transcript_data])
        print("\n--- 전체 자막 텍스트 (예제 1) ---")
        print(full_text[:500] + "...")
    else:
        print("자막을 가져오지 못했습니다.")

    print("\n--- 예제 2: 자동 생성 자막을 시도할 수 있는 동영상 ---")
    transcript_data_auto = get_youtube_transcript(video_id_auto_caption, languages=['en', 'ko'])
    if transcript_data_auto:
        for i, segment in enumerate(transcript_data_auto[:5]):
            print(f"[{segment.start:.2f}-{segment.start + segment.duration:.2f}] {segment.text}")
        print(f"... (총 {len(list(transcript_data_auto))}개 세그먼트)")
    else:
        print("자막을 가져오지 못했습니다.")

    print("\n--- 예제 3: 존재하지 않는 동영상 ID ---")
    transcript_data_invalid = get_youtube_transcript(video_id_invalid)
    if transcript_data_invalid:
        print("자막을 가져왔습니다.")
    else:
        print("자막을 가져오지 못했습니다.")
반응형
반응형

 

입력받은 url을 다운로드 받고, MP3로 변환하고 싶으면 변환버튼.

다운 받은 파일 리스트 페이지 에서 파일 더블 클릭시 실행.

 

 

 

 

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QLineEdit, QScrollArea, QMessageBox
import yt_dlp
import threading
from moviepy.editor import AudioFileClip
 

class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        # 윈도우 설정
        self.setWindowTitle("YouTube Downloader & MP4 to MP3 Converter")
        self.setGeometry(300, 300, 600, 400)

        # 메인 레이아웃 설정
        self.main_layout = QVBoxLayout()

        # 버튼 레이아웃 (좌에서 우로 배치)
        button_layout = QHBoxLayout()

        # 버튼 1: YouTube URL 다운로드 버튼
        self.youtube_button = QPushButton("Download YouTube Video", self)
        self.youtube_button.clicked.connect(self.show_youtube_download_form)
        button_layout.addWidget(self.youtube_button)

        # 버튼 2: MP4 to MP3 변환 버튼
        self.convert_button = QPushButton("Convert MP4 to MP3", self)
        self.convert_button.clicked.connect(self.confirm_convert_form)  # 확인 창 함수 연결
        button_layout.addWidget(self.convert_button) 

        # 버튼 3: Downloads 폴더의 파일 리스트 보기 버튼
        self.show_files_button = QPushButton("Show Downloaded Files", self)
        self.show_files_button.clicked.connect(self.show_downloaded_files)
        button_layout.addWidget(self.show_files_button)

        # 버튼 레이아웃 추가
        self.main_layout.addLayout(button_layout)

        # YouTube URL 입력 필드 (초기에는 숨김)
        self.url_input = QLineEdit(self)
        self.url_input.setPlaceholderText("Enter YouTube URL here...")
        self.url_input.setVisible(False)  # 초기에는 숨김
        self.main_layout.addWidget(self.url_input)

        # 상태 및 결과 표시 레이블 (스크롤 가능)
        self.result_label = QLabel(self)
        self.result_label.setWordWrap(True)
        
        scroll_area = QScrollArea(self)
        scroll_area.setWidgetResizable(True)
        scroll_area.setWidget(self.result_label)
        self.main_layout.addWidget(scroll_area)

        # 메인 레이아웃 설정
        self.setLayout(self.main_layout)

    def show_youtube_download_form(self):
        # 입력 필드가 이미 있는지 확인 후 없을 때만 추가
        # 입력 필드를 보이도록 설정하고, 플레이스홀더 텍스트 설정
        self.url_input.setVisible(True)
        self.url_input.setPlaceholderText("Enter YouTube URL here...")
        self.url_input.clear()  # 이전 입력값 지우기
        self.result_label.setText("Enter a YouTube URL to download:") 

    def download_video(self):
        url = self.url_input.text().strip()
        if url:
            self.result_label.setText("Downloading... Please wait.")
            threading.Thread(target=self.youtube_download_process, args=(url,)).start()
        else:
            self.result_label.setText("Please enter a valid YouTube URL.")

    def youtube_download_process(self, url):
        ydl_opts = {
            'format': 'best',
            'outtmpl': './downloads/%(title)s.%(ext)s',
            'progress_hooks': [self.progress_hook]
        }
        with yt_dlp.YoutubeDL(ydl_opts) as ydl:
            ydl.download([url])

    def progress_hook(self, d):
        if d['status'] == 'downloading':
            percent = d['_percent_str']
            speed = d.get('speed', 'Unknown')
            eta = d.get('eta', 'Unknown')
            self.result_label.setText(f"Downloading: {percent} - Speed: {speed} - ETA: {eta}s")
        elif d['status'] == 'finished':
            self.result_label.setText(f"Download complete!")

    def confirm_convert_form(self):
        # 확인/취소 메시지 박스를 생성
        reply = QMessageBox.question(self, 'Convert MP4 to MP3',
                                     "Are you sure you want to convert all MP4 files in the folder to MP3?",
                                     QMessageBox.Yes | QMessageBox.No, QMessageBox.No)

        # 사용자가 Yes를 선택한 경우에만 변환 함수 실행
        if reply == QMessageBox.Yes:
            self.show_convert_form()

    def show_convert_form(self):
        # URL 입력창 숨김
        self.url_input.setVisible(False)
        
        # MP4 파일 선택 및 MP3로 변환 메시지 설정
        self.result_label.setText("Converting all MP4 files in the folder to MP3...")
        threading.Thread(target=self.convert_mp4_to_mp3).start()

    def convert_mp4_to_mp3(self):
        folder_path = './downloads'  # MP4 파일이 있는 폴더
         

        for filename in os.listdir(folder_path):
            if filename.endswith(".mp4"):
                mp4_path = os.path.join(folder_path, filename)
                mp3_path = os.path.join(folder_path, f"{os.path.splitext(filename)[0]}.mp3")
                
                if os.path.exists(mp3_path):
                    self.result_label.setText(self.result_label.text() + f"\nSkipping {filename}: MP3 already exists.")
                    continue

                audio_clip = AudioFileClip(mp4_path)
                audio_clip.write_audiofile(mp3_path)
                audio_clip.close()
                self.result_label.setText(self.result_label.text() + f"\nConverted: {filename} to MP3.")

        self.result_label.setText(self.result_label.text() + "\nAll MP4 files converted to MP3.")

    def show_downloaded_files(self):
        # URL 입력창 숨김
        self.url_input.setVisible(False)
        
        # ./downloads 폴더의 파일 리스트를 표시
        folder_path = './downloads'
        if not os.path.exists(folder_path):
            self.result_label.setText("No files found. The downloads folder does not exist.")
            return

        files = os.listdir(folder_path)
        if files:
            file_list = "\n".join(files)
            self.result_label.setText(f"Files in {folder_path}:\n{file_list}")
        else:
            self.result_label.setText("No files found in the downloads folder.")



# PyQt 애플리케이션 실행
if __name__ == "__main__":
    app = QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec_())
반응형
반응형

[python] youtube download 입력받은 url로 다운받기

 

yt-dlp는 YouTube 및 기타 비디오 플랫폼에서 동영상을 다운로드하는 강력한 명령줄 도구입니다. 

pytube보다 다양한 기능을 제공하며, Python 스크립트와 통합하여 사용할 수도 있습니다.

import yt_dlp

def main():
    # 다운로드할 YouTube 비디오의 URL
    #url = 'https://www.youtube.com/watch?v=vjcuQLjSUz4'
    
    
    # 사용자로부터 YouTube 비디오 URL 입력받기
    url = input("다운로드할 YouTube 비디오의 URL을 입력하세요: ")

    # 다운로드 옵션 설정
    ydl_opts = {
        'format': 'best',                   # 최고 화질 선택 (audio와 video 포함)
        'outtmpl': './downloads/%(title)s.%(ext)s',  # 다운로드 파일 경로 및 이름 템플릿
    }

    # yt-dlp를 사용하여 다운로드
    with yt_dlp.YoutubeDL(ydl_opts) as ydl:
        ydl.download([url])

    print("Video has been downloaded successfully!")


# 메인 함수 호출
if __name__ == "__main__":
    main()

 

반응형
반응형

YouTube 검색 및 탐색: 성공을 위한 팁 http://www.youtube.com/playlist?list=PLpjK416fmKwQK6_REczCaBQ1x1jyHvZAF

 

YouTube 검색 및 탐색: 성공을 위한 팁

 

www.youtube.com

 
 
 
3
4
5
6
반응형
반응형

https://www.youtube.com/watch?v=hPxnIix5ExI 

YouTube 검색 시스템의 작동 원리를 설명하는 시리즈에 오신 것을 환영합니다.

첫 번째 동영상에서는 '알고리즘'이 시청자를 따라가는 방법을 설명해드립니다.

다음 동영상 보기: https://youtu.be/gTrLniP5tSQ?list=PLp...

0:00 소개 0:11 알고리즘이란? 0:44 알고리즘 작동 원리

 

1분마다 400시간 분량의 동영상이 업로드되니 모두 살펴보기란 어려운 일입니다.

YouTube의 맞춤 동영상 시스템은 실시간 피드백 루프를 통해 시청자 개인마다의 다양한 관심사에 맞는 동영상을 찾습니다. 매일 800억 비트가 넘는 시청자 피드백으로부터 학습하며, 적절한 동영상을 적절한 시기에 적절한 시청자에게 소개하는 방법을 파악합니다. YouTube의 목표는 사람들이 마음에 드는 동영상을 더 많이 보게 하여 YouTube를 꾸준히 방문하도록 유도하는 것입니다. 크리에이터들은 '알고리즘은 어떤 동영상을 가장 선호하나요?'라고 묻곤 합니다. YouTube의 시스템은 크리에이터가 제작하는 동영상의 유형에 대해 아무런 의견이 없으며 특정 형식을 선호하지도 않습니다. 그보다는 다음과 같은 요소에 주목하여 적합한 시청자를 찾습니다.

• 시청자가 시청하는 콘텐츠

• 시청자가 시청하지 않는 콘텐츠

• 시청한 시간

• 좋아요 및 싫어요 표시

• ‘관심 없음’ 의견

알고리즘이 무엇을 선호하는지 고민하는 대신 시청자가 무엇을 좋아하는지에 관심을 집중하는 것이 좋습니다. 그렇게 해서 사람들이 동영상을 본다면 알고리즘도 결국 따라가기 때문입니다. 그렇다면 시청자는 어떤 동영상을 가장 재미있게 볼까요? 시청자는 내 채널을 얼마나 자주 시청하고 싶어 할까요?

YouTube 분석을 통해 이러한 질문에 답을 찾아 보세요.

 

채널을 취미로 운영하든 비즈니스 목적으로 운영하든 YouTube는 각 동영상이 잠재적인 시청자에게 도달할 기회를 주기 위해 노력합니다. 하지만 YouTube의 기능이 워낙 많다 보니 혼란스러울 수 있다는 것은 알고 있습니다. 시리즈를 계속 시청하면서 동영상이 표시되는 여섯 가지 주요 위치를 파악하고, 성공 확률을 높이는 방법을 알아보세요. 여섯 가지 위치를 순서 없이 나열하자면 검색, 추천 동영상, 홈, 인기, 구독, 알림입니다.

 

- 크리에이터 아카데미 강의를 통해 YouTube 활용법을 배우기: http://goo.gl/E9umlU

- 모든 강의 목록 보기: http://goo.gl/x2h1NG

- 단계별 지침 보기: http://goo.gl/fBzr7

반응형
반응형

 

https://www.youtube.com/watch?v=DnN9ypkUC3Y 

 

스트레이트 01월 16일 (일) 밤 8시20분 방송 

◇  김건희 씨는 왜? - 장인수

   - "슈퍼챗 많이 나올 것"
   - 김건희 씨는 왜?
   - "권력이란 게 무섭다"
   - 의혹과 해명
   - 김건희 씨 측의 반론

◇  배신당한 동학개미들 - 손병산

   - 쪼개기와 먹튀
   - 개미가 떠난 이유
반응형

+ Recent posts