[
  {
    "path": ".gitignore",
    "content": "venv/\noutput/\nmodel/\nconfig.json"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2023 ADT109119\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# WhisperGUI\n\n![pic](https://user-images.githubusercontent.com/106337749/221340883-4b437d03-97fc-42ee-821e-dd04096323fe.png)\n\n> 此專案是我用2個小時簡單製作的WhisperGUI+快速安裝包，可以讓我們在使用Whisper時快速操作，無須打指令，以及讓懶得手動裝一堆東西的人可以快速的使用(Python以及FFmpeg還是要自己裝)。\n\n> 2023.03.30更新 日前有人回報無法抓到GPU的問題，目前推測可能跟CUDA Toolkit有關，若未安裝CUDA Toolkit的人，可嘗試先安裝Nvidia CUDA Toolkit。若已安裝卻還是無法顯示GPU，可嘗試調整Bat檔中的第22行，將安裝的Torch CUDA版本改為與電腦的CUDA Toolkit版本相同。\n\n> 2023.08.12更新 目前我有測試打包這個專案成執行檔，不用跑setup.bat創虛擬環境，直接下載下來解壓縮，執行裡面的**main.exe**就可以執行了，大家可以到releases裡下載下來試試看。(打包版經測試無須手動安裝CUDA Toolkit)\n\n[OpenAI Whisper](https://github.com/openai/whisper)\n\n## 專案用途\n\n此專案的作用\n\n在於方便大家可以快速設定好的Whisper執行環境\n\n以及讓多數使用者\n\n可以僅需透過此GUI介面操作使用\n\n而無須打指令\n\n\n## 功能\n\n目前支援操作以下幾種Whisper的功能\n1. 選擇多個音檔\n1. 選擇輸出位置\n1. 選擇使用模型\n1. 選擇辨識語言\n1. 選擇使用裝置(CPU、指定顯卡)\n1. 將字幕翻為英文\n\n\n## 畫面\n\n> 操作示意 選擇檔案>選擇跑模型的裝置>執行完成\n\n![選模型](https://user-images.githubusercontent.com/106337749/218459288-0fd24ee4-4ed6-49c9-a3f4-1fd97976a89d.png)\n![選裝置](https://user-images.githubusercontent.com/106337749/218459323-faaf2d8d-0a68-4bfc-a6e3-62e45b94ad0f.png)\n![執行完成](https://user-images.githubusercontent.com/106337749/218460468-a801fe68-0f01-479d-a4bd-4f04eea1af41.png)\n\n## 安裝\n\n> 請先自行安裝Python 3.7以上版本，以及FFmpeg\n\n以下將會引導你如何在你的電腦上執行此專案。\n\n### 取得專案\n\n```bash\ngit clone git@github.com/ADT109119/WhisperGUI.git\n```\n\n**或是直接在GitHub頁面點Download ZIP**\n\n### 確認電腦已有Pyhton以及FFmpeg\n\n```bash\npython --version\nffmpeg -version\n```\n\n### 執行setup.bat\n\n請直接執行資料夾中的setup.bat，等待虛擬環境完成設置\n\n### 執行專案\n\n請直接執行資料夾中的run.bat，若無報錯，將可以看到GUI介面\n\n## 資料夾說明\n\n- model - 模型存放處\n- output - 預設輸出資料夾\n- venv - 虛擬環境資料夾\n...\n\n## 專案技術\n\n- Python\n- tkinter\n- ttkbootstrap\n\n## 聯絡作者\n\n你可以透過以下方式與我聯絡\n\n- [Email: 2.jerry32262686@gmail.com](mailto:2.jerry32262686@gmail.com)\n...\n\n## License\nThis project is under the MIT License. See [LICENSE](https://github.com/ADT109119/WhisperGUI/blob/main/LICENSE) for further details.\n"
  },
  {
    "path": "main.py",
    "content": "import os\r\nimport easygui\r\nimport tkinter as tk\r\nfrom tkinter import filedialog\r\nimport tkinter.ttk\r\nfrom tkinter.constants import *\r\nfrom tkinter import messagebox\r\nimport subprocess\r\nimport json\r\n\r\nimport torch\r\nimport time\r\nimport urllib.request\r\n\r\nimport ttkbootstrap as ttk\r\n# from ttkbootstrap.constants import *\r\n\r\n#easygui.fileopenbox()\r\n\r\nimport webbrowser\r\n\r\nimport threading\r\n\r\ndef callback(url):\r\n    webbrowser.open_new(url)\r\n\r\n\r\ndef selectPhotoFolder():\r\n    outputDir = easygui.diropenbox(\"字幕存放資料夾\")\r\n    output_dir.delete(0, 'end')\r\n    output_dir.insert(0, outputDir)\r\n    config[\"outputDir\"] = outputDir\r\n    saveConfig(\"outputDir\", outputDir)\r\n    # with open('config.json', 'w', encoding='utf8') as f:\r\n    #     json.dump(config, f)\r\n\r\n\r\ndef selectAudioFile():\r\n    paths = filedialog.askopenfilenames()\r\n    for path in paths:\r\n        displayAudioFilePath.insert(END, path)\r\n    # File = easygui.fileopenbox(\"選擇音檔檔案\")\r\n    # displayAudioFilePath.delete(0, 'end')\r\n    # displayAudioFilePath.insert(0, File)\r\n    \r\ndef detectAvailableDevice():\r\n    if torch.cuda.is_available():\r\n        for i in range(torch.cuda.device_count()):\r\n            devices.append(torch.cuda.get_device_name(i))\r\n        \r\n    usingDevice['value'] = devices\r\n    return\r\n\r\ndef deviceChange(index, value, op):\r\n    #print(\"%s %s %s\"%(index, value, op))\r\n    dev = devices.index(usingDevice.get())\r\n    config[\"usingDevice\"] = dev\r\n    saveConfig(\"usingDevice\", dev)\r\n    # with open('config.json', 'w', encoding='utf8') as f:\r\n    #     json.dump(config, f)\r\n\r\n    if dev > 0:\r\n        print(\"cuda:%s\"%(dev-1))\r\n    else:\r\n        print(\"cpu\")\r\n\r\ndef deviceDecode():\r\n    dev = devices.index(usingDevice.get())\r\n    if dev > 0:\r\n        return(\"cuda:%s\"%(dev-1))\r\n    else:\r\n        return(\"cpu\")\r\n\r\ndef languageChange(index, value, op):\r\n    transcribeLanguage = languages.index(usingLanguage.get())\r\n    config[\"transcribeLanguage\"] = transcribeLanguage\r\n    saveConfig(\"transcribeLanguage\", transcribeLanguage)\r\n    # with open('config.json', 'w', encoding='utf8') as f:\r\n    #     json.dump(config, f)\r\n\r\ndef modelChange(index, value, op):\r\n    model = models.index(usingModel.get())\r\n    config[\"usingModel\"] = model\r\n    saveConfig(\"usingModel\", model)\r\n    # with open('config.json', 'w', encoding='utf8') as f:\r\n    #     json.dump(config, f)\r\n\r\ndef versionCheck(ori): # fetch app version\r\n    try:\r\n        url = 'https://raw.githubusercontent.com/ADT109119/WhisperGUI/main/version.txt'\r\n        response = urllib.request.urlopen(url)\r\n        fetchVersion = response.read().decode('utf-8')\r\n        if fetchVersion != \"ver 1.9.1\":\r\n            checkVisitGithub = messagebox.askquestion(title=\"有新版本\", message=\"目前最新版本為%s\\n請問您是否想前往GitHub下載最新版本\"%(fetchVersion))\r\n            if checkVisitGithub == 'yes':\r\n                callback(\"https://github.com/ADT109119/WhisperGUI\")\r\n        elif ori == 1:\r\n            messagebox.showinfo(title=\"訊息\", message=\"目前版本為最新\")\r\n\r\n    except:\r\n        print(\"無法獲取版本資訊\")\r\n\r\ndef saveConfig(key, value):\r\n    global config\r\n    config[key] = value\r\n    with open('config.json', 'w', encoding='utf8') as f:\r\n        json.dump(config, f)\r\n\r\ndef start_process():\r\n    t = threading.Thread(target=process)\r\n    t.start()\r\n\r\ndef process():\r\n    # File = displayAudioFilePath.get(0)\r\n    if displayAudioFilePath.size() == 0:\r\n        messagebox.showerror(title=\"錯誤\", message='未選擇音檔')\r\n        return 0\r\n\r\n    start = time.time()\r\n\r\n    baseCommandStr = \"venv\\\\Scripts\\\\whisper\"\r\n    \r\n    for i in range(displayAudioFilePath.size()):\r\n        path = displayAudioFilePath.get(i)\r\n        print(path)\r\n        commandStr = baseCommandStr + ' \"%s\"'%path\r\n\r\n        languageInput = usingLanguage.get()\r\n\r\n        if languageInput != \"自動偵測\":\r\n            commandStr = commandStr + \" --language %s \"%languageInput\r\n\r\n        deviceInput = deviceDecode()\r\n        commandStr = commandStr + \" --device %s --fp16 False\"%deviceInput\r\n\r\n        commandStr = commandStr + \" --model %s \"%usingModel.get()\r\n\r\n        commandStr = commandStr + \" -f srt\"\r\n\r\n        output_dirInput = output_dir.get()\r\n\r\n        if outputToTheSamePathAsInputVar.get()==\"1\":\r\n            output_dirInput = \"/\".join(path.split(\"/\")[:-1])\r\n            # print(output_dirInput)\r\n\r\n        if output_dirInput != \"\":\r\n            commandStr = commandStr + ' --output_dir \"%s\" '%output_dirInput\r\n\r\n        commandStr = commandStr + \" --model_dir %s \"%(\"model\")\r\n\r\n        if translateToEnglishVar.get() == '1':\r\n            commandStr = commandStr + \" --task %s \"%(\"translate\")\r\n\r\n\r\n        if initial_prompt.get() != \"\":\r\n            commandStr = commandStr + ' --initial_prompt \"%s\" '%initial_prompt.get()\r\n\r\n        # print(os.system(\"echo %s\"%commandStr))\r\n        # print(commandStr)\r\n        processButton[\"state\"] = \"disable\"\r\n        \r\n        out = subprocess.Popen(commandStr)\r\n        (out, err) = out.communicate()\r\n    \r\n\r\n    end = time.time()\r\n\r\n    messagebox.showinfo(title=\"訊息\", message=\"處理完成\\n花費時間%.2f秒\"%(end-start))\r\n    processButton[\"state\"] = \"normal\"\r\n    saveConfig(\"prompt\", initial_prompt.get())\r\n    #outputPreviewVar.set(out)\r\n    #print(out)\r\n\r\n# cinfig\r\nconfig = {\r\n    \"outputDir\": os.getcwd() + \"\\\\output\",\r\n    \"usingModel\": 2,\r\n    \"usingDevice\": 0,\r\n    \"transcribeLanguage\": 0,\r\n    \"autoCheckVersion\": True,\r\n    \"prompt\": \"\"\r\n}\r\n\r\n\r\nif not os.path.exists(\".\\\\config.json\"):\r\n    with open('config.json', 'w', encoding='utf8') as f:\r\n        json.dump(config, f)\r\nelse:\r\n    with open('config.json', 'r', encoding='utf8') as f:\r\n        config = json.load(f)\r\n\r\nif config[\"autoCheckVersion\"] == True:\r\n    versionCheck(0)\r\n\r\n\r\nheightFix_1 = 70\r\n\r\nwindow = tk.Tk()\r\nwindow.title('WhisperGUI By The Walking Fish')\r\nwindow.geometry('580x330')\r\nwindow.resizable(False, False)\r\n\r\nlabel1 = tk.Label(text='選擇音檔')\r\nlabel1.place(x=0, y=10)\r\ndisplayAudioFilePath = tk.Listbox(width=60, height=5)\r\ndisplayAudioFilePath.place(x=80, y=10)\r\nselectAudioFileButton = ttk.Button(text='＋添加', command=selectAudioFile)\r\nselectAudioFileButton.place(x=510, y=20)\r\nselectAudioFileButton = ttk.Button(text='－刪除', bootstyle='danger', command=lambda x=displayAudioFilePath: x.delete(\"active\"))\r\nselectAudioFileButton.place(x=510, y=50)\r\n\r\n\r\n\r\nlabel2 = tk.Label(text='字幕存放資料夾')\r\nlabel2.place(x=0, y=30+heightFix_1)\r\noutput_dir = tk.Entry(width=55)\r\noutput_dir.place(x=120, y=30+heightFix_1)\r\noutput_dir.insert(0, config[\"outputDir\"])\r\nselectPhotoPathButton = tk.Button(text='....', command=selectPhotoFolder)\r\nselectPhotoPathButton.place(x=500, y=30+heightFix_1)\r\n\r\noutputToTheSamePathAsInputVar = tk.StringVar()\r\noutputToTheSamePathAsInput = tk.Checkbutton(text=\"檔案輸出到與個別輸入檔案相同位置\", variable=outputToTheSamePathAsInputVar, onvalue=\"1\", offvalue=\"0\")\r\noutputToTheSamePathAsInput.deselect()\r\noutputToTheSamePathAsInput.place(x=300, y=60+heightFix_1)\r\n\r\nlabel_usingModel = tk.Label(text='使用模型')\r\nlabel_usingModel.place(x=0, y=60+heightFix_1)\r\nvar = tk.StringVar()\r\nvar.trace(\"w\", modelChange)\r\nusingModel = tkinter.ttk.Combobox(window, textvariable=var)\r\nmodels = ['tiny', 'base', 'small', 'medium', 'large', 'large-v1', 'large-v2', 'large-v3', 'turbo']\r\nusingModel['value'] = models\r\nusingModel.current(config[\"usingModel\"])\r\nusingModel.place(x=60, y=60+heightFix_1)\r\n\r\nlabel_usingDevice = tk.Label(text='使用裝置')\r\nlabel_usingDevice.place(x=0, y=90+heightFix_1)\r\ndeviceVar = tk.StringVar()\r\ndeviceVar.trace(\"w\", deviceChange)\r\nusingDevice = tkinter.ttk.Combobox(window, textvariable=deviceVar)\r\ndevices = ['cpu']\r\nusingDevice['value'] = devices\r\ndetectAvailableDevice()\r\nusingDevice.current(config[\"usingDevice\"])\r\nusingDevice.place(x=60, y=90+heightFix_1)\r\n\r\nlabel_language = tk.Label(text='辨識語言')\r\nlabel_language.place(x=0, y=120+heightFix_1)\r\nlanguageVar = tk.StringVar()\r\nlanguageVar.trace_add(\"write\", languageChange)\r\nusingLanguage = tkinter.ttk.Combobox(window, textvariable=languageVar)\r\nlanguages = ['自動偵測', \"Afrikaans\",\"Albanian\",\"Amharic\",\"Arabic\",\"Armenian\",\"Assamese\",\"Azerbaijani\",\"Bashkir\",\"Basque\",\"Belarusian\",\"Bengali\",\"Bosnian\",\"Breton\",\"Bulgarian\",\"Burmese\",\"Castilian\",\"Catalan\",\"Chinese\",\"Croatian\",\"Czech\",\"Danish\",\"Dutch\",\"English\",\"Estonian\",\"Faroese\",\"Finnish\",\"Flemish\",\"French\",\"Galician\",\"Georgian\",\"German\",\"Greek\",\"Gujarati\",\"Haitian\",\"Haitian Creole\",\"Hausa\",\"Hawaiian\",\"Hebrew\",\"Hindi\",\"Hungarian\",\"Icelandic\",\"Indonesian\",\"Italian\",\"Japanese\",\"Javanese\",\"Kannada\",\"Kazakh\",\"Khmer\",\"Korean\",\"Lao\",\"Latin\",\"Latvian\",\"Letzeburgesch\",\"Lingala\",\"Lithuanian\",\"Luxembourgish\",\"Macedonian\",\"Malagasy\",\"Malay\",\"Malayalam\",\"Maltese\",\"Maori\",\"Marathi\",\"Moldavian\",\"Moldovan\",\"Mongolian\",\"Myanmar\",\"Nepali\",\"Norwegian\",\"Nynorsk\",\"Occitan\",\"Panjabi\",\"Pashto\",\"Persian\",\"Polish\",\"Portuguese\",\"Punjabi\",\"Pushto\",\"Romanian\",\"Russian\",\"Sanskrit\",\"Serbian\",\"Shona\",\"Sindhi\",\"Sinhala\",\"Sinhalese\",\"Slovak\",\"Slovenian\",\"Somali\",\"Spanish\",\"Sundanese\",\"Swahili\",\"Swedish\",\"Tagalog\",\"Tajik\",\"Tamil\",\"Tatar\",\"Telugu\",\"Thai\",\"Tibetan\",\"Turkish\",\"Turkmen\",\"Ukrainian\",\"Urdu\",\"Uzbek\",\"Valencian\",\"Vietnamese\",\"Welsh\",\"Yiddish\",\"Yoruba\"]\r\nusingLanguage['value'] = languages\r\nusingLanguage.current(config[\"transcribeLanguage\"])\r\nusingLanguage.place(x=60, y=120+heightFix_1)\r\n\r\ntranslateToEnglishVar = tk.StringVar()\r\ntranslateToEnglish = tk.Checkbutton(text=\"將輸出字幕翻譯為英文\", variable=translateToEnglishVar, onvalue=\"1\", offvalue=\"0\")\r\ntranslateToEnglish.deselect()\r\ntranslateToEnglish.place(x=0, y=150+heightFix_1)\r\n\r\ninitial_prompt_label = tk.Label(text='內容提示詞')\r\ninitial_prompt_label.place(x=0, y=180+heightFix_1)\r\ninitial_prompt_var = tk.StringVar(value=config.get(\"prompt\"))\r\ninitial_prompt = tk.Entry(width=55, textvariable=initial_prompt_var)\r\ninitial_prompt.pack()\r\ninitial_prompt.place(x=80, y=180+heightFix_1)\r\n\r\nlabel_copyright = tk.Label(text='MIT License')\r\nlabel_copyright.place(x=0, y=210+heightFix_1)\r\nlabel_author = tk.Label(text='製作: The Walking Fish')\r\nlabel_author.bind(\"<Button-1>\", lambda e: callback(\"https://www.youtube.com/@the_walking_fish\"))\r\nlabel_author.place(x=0, y=230+heightFix_1)\r\n\r\nprocessButton = tk.Button(text='執行', width=20, command=start_process)\r\nprocessButton.place(anchor='center', x=290, y=240+heightFix_1)\r\n\r\n# menu\r\nmenu = tk.Menu(window)\r\nsettingMenu = tk.Menu(menu)\r\nautoCheckVar = tk.BooleanVar()\r\nautoCheckVar.trace_add(\"write\", lambda index, value, op: saveConfig(\"autoCheckVersion\", autoCheckVar.get()))\r\nautoCheckVar.set(config[\"autoCheckVersion\"])\r\nsettingMenu.add_checkbutton(label=\"自動檢查版本\", variable=autoCheckVar)\r\nsettingMenu.add_separator()\r\nsettingMenu.add_command(label=\"檢查版本\", command=lambda: versionCheck(1))\r\nmenu.add_cascade(label=\"setting\", menu=settingMenu)\r\nwindow.config(menu=menu)\r\n\r\nwindow.mainloop()\r\n"
  },
  {
    "path": "requirements.txt",
    "content": "easygui\r\nttkbootstrap\r\nopenai-whisper\nrequests"
  },
  {
    "path": "run.bat",
    "content": "venv\\Scripts\\python main.py\r\n"
  },
  {
    "path": "setup.bat",
    "content": "@echo off\r\ngoto :DOES_PYTHON_EXIST\r\n\r\n\r\n:DOES_PYTHON_EXIST\r\npython -V | find /v \"Python\" >NUL 2>NUL && (goto :PYTHON_DOES_NOT_EXIST)\r\npython -V | find \"Python\"    >NUL 2>NUL && (goto :PYTHON_DOES_EXIST)\r\ngoto :EOF\r\n\r\n:PYTHON_DOES_NOT_EXIST\r\necho Python is not installed on your system.\r\necho Now opeing the download URL.\r\nstart \"\" \"https://www.microsoft.com/store/productId/9PJPW5LDXLZ5\"\r\nPAUSE\r\ngoto :EOF\r\n\r\n:PYTHON_DOES_EXIST\r\n:: This will retrieve Python 3.8.0 for example.\r\nfor /f \"delims=\" %%V in ('python -V') do @set ver=%%V\r\necho Congrats, %ver% is installed...\r\npython -m venv .\\venv\r\nvenv\\Scripts\\pip install torch==1.13.1 torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu116\r\nvenv\\Scripts\\pip install -U -r requirements.txt\r\n\r\nPAUSE\r\ngoto :EOF\r\n\r\n\r\n"
  },
  {
    "path": "update.bat",
    "content": "venv\\Scripts\\pip install -U openai-whisper"
  },
  {
    "path": "version.txt",
    "content": "ver 1.9.1"
  },
  {
    "path": "使用說明.txt",
    "content": "1.先打開setup.bat，此檔案會自動創建虛擬環境，以及安裝所需要的各種函式庫\r\n2.打開run.bat，開始使用\r\n3.在輸出資料夾找到檔案"
  }
]