Spaces:
Sleeping
Sleeping
# app.py | |
import streamlit as st | |
from pydub import AudioSegment | |
from io import BytesIO | |
import tempfile | |
from datetime import timedelta | |
# ✅ ffmpeg はサーバにインストール済みなので、明示的なパス指定は不要! | |
# AudioSegment.converter や AudioSegment.ffprobe は書かなくてOK | |
# アプリ設定 | |
st.set_page_config(page_title="音声カッター", layout="centered") | |
st.title("✂️ 音声ファイルカッター(シンプル&見やすい)") | |
uploaded_file = st.file_uploader("音声ファイルを選んでください(mp3, wav, m4a)", type=["mp3", "wav", "m4a"]) | |
def format_time(seconds: float) -> str: | |
return str(timedelta(seconds=round(seconds))) | |
if uploaded_file: | |
st.audio(uploaded_file) | |
try: | |
# 一時ファイルに保存 | |
with tempfile.NamedTemporaryFile(delete=False, suffix=".tmp") as tmp: | |
tmp.write(uploaded_file.read()) | |
audio = AudioSegment.from_file(tmp.name) | |
duration_sec = len(audio) / 1000.0 | |
if duration_sec < 0.01: | |
st.warning("❗ 音声が短すぎて切り出せません。") | |
st.stop() | |
st.subheader("🎚️ 切り出したい範囲を選んでください") | |
start_sec, end_sec = st.slider( | |
f"範囲(全長 {format_time(duration_sec)})", | |
min_value=0.0, | |
max_value=duration_sec, | |
value=(0.0, min(1.0, duration_sec)), | |
step=0.01 | |
) | |
col1, col2 = st.columns(2) | |
col1.write(f"▶️ 開始時間: `{format_time(start_sec)}`") | |
col2.write(f"⏹️ 終了時間: `{format_time(end_sec)}`") | |
if st.button("🎬 切り出して保存"): | |
if start_sec >= end_sec: | |
st.warning("⚠️ 終了時間は開始時間より後にしてください。") | |
else: | |
start_ms = int(start_sec * 1000) | |
end_ms = int(end_sec * 1000) | |
cut_audio = audio[start_ms:end_ms] | |
buf = BytesIO() | |
cut_audio.export(buf, format="mp3") | |
buf.seek(0) | |
st.success("✅ 切り出し完了!") | |
st.audio(buf, format="audio/mp3") | |
st.download_button( | |
label="💾 ダウンロード(MP3)", | |
data=buf, | |
file_name="cut_audio.mp3", | |
mime="audio/mpeg" | |
) | |
except Exception as e: | |
st.error(f"❌ エラーが発生しました:\n\n{e}") | |