{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pip install faster-whisper srt torch ctranslate2 numpy --extra-index-url https://download.pytorch.org/whl/cu118"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!sudo apt-get update && sudo apt-get install -y ffmpeg"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 在 Notebook 单元格中运行\n",
    "!pip install ipywidgets\n",
    "!jupyter nbextension enable --py widgetsnbextension"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "============================================================\n",
      "依赖检查\n",
      "============================================================\n",
      "✅ faster-whisper 1.2.1\n",
      "✅ srt\n",
      "✅ torch 2.10.0+cu128\n",
      "⚠️ CUDA 不可用，将使用 CPU\n",
      "✅ ctranslate2 4.8.1\n",
      "⚠️ ctranslate2 未检测到 CUDA\n",
      "✅ numpy 2.3.5\n",
      "============================================================\n",
      "FFmpeg\n",
      "============================================================\n",
      "ffmpeg version 6.1.1-3ubuntu5 Copyright (c) 2000-2023 the FFmpeg developers\n",
      "依赖检查完成\n",
      "\n"
     ]
    }
   ],
   "source": [
    "import subprocess\n",
    "from faster_whisper import WhisperModel\n",
    "import srt\n",
    "from datetime import timedelta\n",
    "\n",
    "# ========== 1. 检查 ==========\n",
    "print(\"=\"*60)\n",
    "print(\"依赖检查\")\n",
    "print(\"=\"*60)\n",
    "\n",
    "try:\n",
    "    import faster_whisper\n",
    "    print(f\"✅ faster-whisper {faster_whisper.__version__}\")\n",
    "except ImportError:\n",
    "    print(\"❌ faster-whisper 未安装\"); exit(1)\n",
    "\n",
    "try:\n",
    "    import srt\n",
    "    print(\"✅ srt\")\n",
    "except ImportError:\n",
    "    print(\"❌ srt 未安装\"); exit(1)\n",
    "\n",
    "try:\n",
    "    import torch\n",
    "    print(f\"✅ torch {torch.__version__}\")\n",
    "    if torch.cuda.is_available():\n",
    "        print(f\"✅ CUDA 可用: {torch.cuda.get_device_name(0)}\")\n",
    "    else:\n",
    "        print(\"⚠️ CUDA 不可用，将使用 CPU\")\n",
    "except ImportError:\n",
    "    print(\"❌ torch 未安装\"); exit(1)\n",
    "\n",
    "try:\n",
    "    import ctranslate2\n",
    "    print(f\"✅ ctranslate2 {ctranslate2.__version__}\")\n",
    "    if ctranslate2.get_cuda_device_count() == 0:\n",
    "        print(\"⚠️ ctranslate2 未检测到 CUDA\")\n",
    "except ImportError:\n",
    "    print(\"❌ ctranslate2 未安装\"); exit(1)\n",
    "\n",
    "try:\n",
    "    import numpy as np\n",
    "    print(f\"✅ numpy {np.__version__}\")\n",
    "except ImportError:\n",
    "    print(\"❌ numpy 未安装\"); exit(1)\n",
    "\n",
    "print(\"=\"*60)\n",
    "print(\"FFmpeg\")\n",
    "print(\"=\"*60)\n",
    "\n",
    "print(subprocess.check_output([\"ffmpeg\", \"-version\"]).decode().splitlines()[0])\n",
    "\n",
    "print(\"依赖检查完成\\n\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "音频文件： /workspace/1_1_250213_(Vocals)_(Vocals)_0.wav\n"
     ]
    }
   ],
   "source": [
    "# ========== 2. 指定本地音频文件路径 ==========\n",
    "video_path = r\"/workspace/1_0.wav\"   # 改成你的实际文件路径\n",
    "print(\"音频文件：\", video_path)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "模型加载完成\n"
     ]
    }
   ],
   "source": [
    "model = WhisperModel(\n",
    "    \"tiny\",\n",
    "    device=\"cpu\",\n",
    "    compute_type=\"int8\"   # 或 \"float32\"（更准确但慢）\n",
    ")\n",
    "print(\"模型加载完成\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "识别完成，共 4 段\n"
     ]
    }
   ],
   "source": [
    "# ========== 4. 转写 ==========\n",
    "segments, info = model.transcribe(\n",
    "    video_path,\n",
    "    language=\"ko\",\n",
    "    beam_size=5,\n",
    "    vad_filter=True\n",
    ")\n",
    "segments = list(segments)\n",
    "print(f\"识别完成，共 {len(segments)} 段\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "TXT 已生成\n",
      "\n",
      "============================================================\n",
      "识别结果（纯文本）\n",
      "============================================================\n",
      "한 메스로 컴퓨터 드리겠습니다.\n",
      "사진으로 또 컴퓨터가만큼\n",
      "사진에 있는 곡들로 이렇게 무대를 하게 될 예정인데\n",
      "많이 기대해주시고 또 오랜만에\n"
     ]
    }
   ],
   "source": [
    "# ========== 5. 导出 TXT ==========\n",
    "with open(\"output.txt\", \"w\", encoding=\"utf-8\") as f:\n",
    "    for seg in segments:\n",
    "        f.write(seg.text.strip() + \"\\n\")\n",
    "print(\"TXT 已生成\")\n",
    "print(\"\\n\" + \"=\"*60)\n",
    "print(\"识别结果（纯文本）\")\n",
    "print(\"=\"*60)\n",
    "if segments:\n",
    "    for seg in segments:\n",
    "        print(seg.text.strip())\n",
    "else:\n",
    "    print(\"⚠️ 未识别到任何语音内容\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "SRT 已生成\n",
      "\n",
      "============================================================\n",
      "识别结果（SRT 字幕格式）\n",
      "============================================================\n",
      "1\n",
      "00:00:00,620 --> 00:00:02,780\n",
      "한 메스로 컴퓨터 드리겠습니다.\n",
      "\n",
      "2\n",
      "00:00:02,780 --> 00:00:04,500\n",
      "사진으로 또 컴퓨터가만큼\n",
      "\n",
      "3\n",
      "00:00:04,500 --> 00:00:07,780\n",
      "사진에 있는 곡들로 이렇게 무대를 하게 될 예정인데\n",
      "\n",
      "4\n",
      "00:00:07,780 --> 00:00:09,780\n",
      "많이 기대해주시고 또 오랜만에\n",
      "\n",
      "\n",
      "全部完成！文件保存在当前目录：output.txt 和 output.srt\n"
     ]
    }
   ],
   "source": [
    "# ========== 6. 导出 SRT ==========\n",
    "subs = []\n",
    "for i, seg in enumerate(segments):\n",
    "    subs.append(\n",
    "        srt.Subtitle(\n",
    "            index=i + 1,\n",
    "            start=timedelta(seconds=seg.start),\n",
    "            end=timedelta(seconds=seg.end),\n",
    "            content=seg.text.strip()\n",
    "        )\n",
    "    )\n",
    "with open(\"output.srt\", \"w\", encoding=\"utf-8\") as f:\n",
    "    f.write(srt.compose(subs))\n",
    "print(\"SRT 已生成\")\n",
    "\n",
    "print(\"\\n\" + \"=\"*60)\n",
    "print(\"识别结果（SRT 字幕格式）\")\n",
    "print(\"=\"*60)\n",
    "if segments:\n",
    "    subs = []\n",
    "    for i, seg in enumerate(segments):\n",
    "        start = timedelta(seconds=seg.start)\n",
    "        end = timedelta(seconds=seg.end)\n",
    "        # 格式化时间为 SRT 标准格式（HH:MM:SS,mmm）\n",
    "        def fmt(td):\n",
    "            total_sec = td.total_seconds()\n",
    "            hours = int(total_sec // 3600)\n",
    "            minutes = int((total_sec % 3600) // 60)\n",
    "            seconds = int(total_sec % 60)\n",
    "            millis = int((total_sec % 1) * 1000)\n",
    "            return f\"{hours:02d}:{minutes:02d}:{seconds:02d},{millis:03d}\"\n",
    "        \n",
    "        subs.append(\n",
    "            srt.Subtitle(\n",
    "                index=i + 1,\n",
    "                start=timedelta(seconds=seg.start),\n",
    "                end=timedelta(seconds=seg.end),\n",
    "                content=seg.text.strip()\n",
    "            )\n",
    "        )\n",
    "    srt_text = srt.compose(subs)\n",
    "    print(srt_text)\n",
    "else:\n",
    "    print(\"⚠️ 未识别到任何语音内容\")\n",
    "\n",
    "\n",
    "print(\"全部完成！文件保存在当前目录：output.txt 和 output.srt\")"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
