728x90
반응형
aa,mp3 라는 파일에 여러 음악(노래)가 있는 경우, 각각의 노래로 분리
aa.txt 파일에 다음과 같이 '/'로 분리되어 있어야 합니다.
시작시간 / 노래제목(파일명) / 음악가 정보
00:00:00/Song name/음악가
00:04:14/Song name/음악가
그럼 txt 파일의 정보로 mp3 파일을 분리합니다.
from pydub import AudioSegment
from mutagen.id3 import ID3, TIT2, TPE1, TPE2 # ID3 태그를 위한 클래스 임포트
from mutagen.mp3 import MP3
import os
def split_mp3_by_txt(mp3_filepath, txt_filepath, output_directory="output_splits"):
"""
텍스트 파일에 지정된 시작 시간, 제목, 음악가 정보에 따라 MP3 파일을 분리하고,
각 파일에 제목과 참여 음악가 ID3 태그를 추가합니다.
각 곡의 종료 시간은 다음 곡의 시작 시간 직전으로 자동 계산됩니다.
마지막 곡의 종료 시간은 원본 MP3 파일의 총 길이입니다.
Args:
mp3_filepath (str): 입력 MP3 파일의 경로.
txt_filepath (str): 분리 정보가 포함된 텍스트 파일의 경로 (형식: 시작시간/제목/음악가).
output_directory (str): 분리된 MP3 파일이 저장될 디렉토리.
기본값은 "output_splits"입니다.
"""
if not os.path.exists(mp3_filepath):
print(f"오류: MP3 파일 '{mp3_filepath}'을(를) 찾을 수 없습니다.")
return
if not os.path.exists(txt_filepath):
print(f"오류: 텍스트 파일 '{txt_filepath}'을(를) 찾을 수 없습니다.")
return
os.makedirs(output_directory, exist_ok=True)
try:
audio = AudioSegment.from_mp3(mp3_filepath)
total_audio_length_ms = len(audio) # MP3 파일의 총 길이 (밀리초)
except Exception as e:
print(f"MP3 파일 로드 중 오류 발생: {e}")
return
# 시간을 밀리초로 변환하는 헬퍼 함수
def time_to_milliseconds(time_str):
time_parts = list(map(int, time_str.split(':')))
if len(time_parts) == 2: # MM:SS
minutes, seconds = time_parts
return (minutes * 60 + seconds) * 1000
elif len(time_parts) == 3: # HH:MM:SS
hours, minutes, seconds = time_parts
return (hours * 3600 + minutes * 60 + seconds) * 1000
else:
raise ValueError("지원되지 않는 시간 형식입니다. MM:SS 또는 HH:MM:SS를 사용하세요.")
# 밀리초를 HH:MM:SS 또는 MM:SS 형식으로 변환 (로그 출력용)
def ms_to_hms(ms):
seconds = ms // 1000
minutes = seconds // 60
hours = minutes // 60
seconds %= 60
minutes %= 60
if hours > 0:
return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
else:
return f"{minutes:02d}:{seconds:02d}"
segments_info = [] # (start_ms, title, artist) 저장
with open(txt_filepath, 'r', encoding='utf-8') as f:
lines = f.readlines()
print(f"'{os.path.basename(mp3_filepath)}' 파일을 '{os.path.basename(txt_filepath)}'에 따라 분리합니다...")
for i, line in enumerate(lines):
line = line.strip()
if not line:
continue
# '시작시간/제목/음악가' 형식으로 분리
parts = line.split('/', 2) # 최대 2번만 분리하여 음악가 이름에 '/'가 있어도 오류 방지
if len(parts) < 3:
print(f"경고: {i+1}번째 줄 '{line}'이(가) 형식이 잘못되어 건너뜁니다. 형식: 시작시간/제목/음악가")
continue
try:
start_time_str = parts[0].strip()
title = parts[1].strip()
artist = parts[2].strip()
start_ms = time_to_milliseconds(start_time_str)
if start_ms >= total_audio_length_ms:
print(f"경고: {i+1}번째 줄 ('{line}')을(를) 건너뜠습니다. 시작 시간이 오디오 길이보다 깁니다.")
continue
segments_info.append((start_ms, title, artist))
except ValueError as ve:
print(f"오류: {i+1}번째 줄 '{line}'에서 시간 또는 정보 구문 분석 중 오류 발생 - {ve}")
except Exception as e:
print(f"오류: {i+1}번째 줄 '{line}'에서 예상치 못한 오류 발생 - {e}")
# 시작 시간을 기준으로 정렬 (혹시 텍스트 파일 순서가 뒤죽박죽일 경우를 대비)
segments_info.sort(key=lambda x: x[0])
# 각 세그먼트 추출 및 태그 추가
for i, (start_ms, title, artist) in enumerate(segments_info):
# 다음 세그먼트의 시작 시간을 현재 세그먼트의 끝 시간으로 설정
# 마지막 세그먼트의 경우 오디오의 끝까지
if i + 1 < len(segments_info):
end_ms = segments_info[i+1][0]
else:
end_ms = total_audio_length_ms
# 안전 장치: 시작 시간이 종료 시간보다 크거나 같을 경우 (논리적 오류 방지)
if start_ms >= end_ms:
print(f"경고: 세그먼트 '{title}' ({ms_to_hms(start_ms)})의 시작 시간이 종료 시간 ({ms_to_hms(end_ms)})보다 크거나 같아 건너뜠습니다.")
continue
# 파일명은 제목으로 사용 (Windows/Linux/macOS 파일명 규칙에 맞게 특수문자 제거 또는 치환)
# 예: '/', ':', '*', '?', '"', '<', '>', '|'
invalid_chars = r'[\/:*?"<>|]'
safe_filename = "".join(c if c.isalnum() or c in [' ', '-', '_', '.', ','] else '_' for c in title) # 더 유연하게 허용
# re 모듈을 사용하는 것이 더 좋지만, 여기서는 간단한 예시로 대체합니다.
# import re
# safe_filename = re.sub(invalid_chars, '_', title)
output_filename = safe_filename + ".mp3"
output_filepath = os.path.join(output_directory, output_filename)
try:
segment = audio[start_ms:end_ms]
segment.export(output_filepath, format="mp3")
print(f" 내보내기 완료: {output_filepath} (시작: {ms_to_hms(start_ms)}, 종료: {ms_to_hms(end_ms)})")
# ID3 태그 추가
try:
mp3_file = MP3(output_filepath)
if not mp3_file.tags:
mp3_file.add_tags()
# TIT2: 제목 (Title)
mp3_file.tags.add(TIT2(encoding=3, text=[title]))
# TPE1: 아티스트 (Artist) - '참여 음악가'로 사용
mp3_file.tags.add(TPE1(encoding=3, text=[artist]))
# TPE2: 앨범 아티스트 (Album Artist) - 필요하다면 추가 가능
# mp3_file.tags.add(TPE2(encoding=3, text=["Massimo Faraò Trio"])) # 예시
mp3_file.save()
print(f" 태그 추가 완료: '{title}' (아티스트: '{artist}')")
except Exception as tag_e:
print(f"경고: '{output_filepath}' 파일에 ID3 태그 추가 중 오류 발생: {tag_e}")
except Exception as e:
print(f"오류: '{output_filename}' 파일 내보내기 중 오류 발생 - {e}")
print("\nMP3 분리 및 태그 추가 완료!")
if __name__ == "__main__":
# --- 설정 ---
# 실제 파일 경로와 텍스트 파일명으로 반드시 변경하세요!
input_mp3_file = "E:/temp/Sound Test/aa.mp3"
input_txt_file = "E:/temp/Sound Test/aa.txt"
output_folder = "E:/temp/Sound Test/aa_dir" # 분리된 파일이 저장될 새 폴더를 지정하는 것이 좋습니다.
# --- 분리 함수 실행 ---
split_mp3_by_txt(input_mp3_file, input_txt_file, output_folder)
print(f"\n'{output_folder}' 디렉토리에서 분리되고 태그가 추가된 MP3 파일을 확인하세요.")
print("MP3 파일 경로와 텍스트 파일 내용을 실제 사용 환경에 맞게 변경하는 것을 잊지 마세요.")
728x90
반응형
'프로그램' 카테고리의 다른 글
[파이썬] 1,875배 빨라진다! 조건에 맞는 데이터 계산(Numba GPU 사용) (2) | 2025.06.08 |
---|---|
[파이썬] 680배 빨라진다! 조건에 맞는 데이터 계산(병렬처리 하지 않고 Numba만 사용) (0) | 2025.06.03 |
[파이썬] 150배 빨라진다! 조건에 맞는 데이터 계산(병렬처리와 Numba사용) (0) | 2025.06.03 |
[파이썬] 조건에 맞는 데이터 계산(다중 for문) (0) | 2025.06.03 |
[A.I] OpenManus(chatGPT) 사용하기 (2) | 2025.04.13 |
댓글