├── LICENSE ├── README.md ├── ffmpegGUI.py ├── icon.png ├── image.png ├── image2.png └── logo.png /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Brad heffernan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ffmpeg python Gtk GUI 2 | 3 | ![screenshot](https://github.com/BradHeff/ffmpeg-GUI/blob/master/image.png) 4 | 5 | ![screenshot](https://github.com/BradHeff/ffmpeg-GUI/blob/master/image2.png) 6 | 7 | This is a GUI i made to encode video files with ease, to use this just do the following. 8 | 9 | NOTE: 10 | ---- 11 | 12 | Tested solely on URxvt. options for other terminals have been taken from online man pages and not tested. all execution commands use the option _-e_. 13 | 14 | Any feedback is appreciated. 15 | 16 | Installation: 17 | --- 18 | 19 | 1. open terminal and clone repository 20 | 21 | ```bash 22 | 23 | > git clone https://github.com/BradHeff/ffmpeg-GUI.git && cd ffmpeg-GUI 24 | 25 | ``` 26 | 27 | 2. then run the ffmpegGUI.py 28 | 29 | ```bash 30 | 31 | > python3 ffmpegGUI.py 32 | 33 | ``` 34 | 35 | 36 | Alternative Installation 37 | ------ 38 | 39 | 1. make the script executable 40 | 41 | ```bash 42 | 43 | > chmod +x ffmpegGUI.py 44 | 45 | ``` 46 | 47 | 48 | 49 | 2. now you can run the script from within its directory with the following 50 | 51 | ```bash 52 | 53 | > ./ffmpegGUI.py 54 | 55 | ``` 56 | 57 | 58 | Optional: 59 | --- 60 | 61 | symlink it to your **_/usr/bin_** or **_/usr/local/bin_** 62 | 63 | 1. symlink the script 64 | 65 | * replace **_USER_** with your username 66 | 67 | * replace **_DIR_** with the directory were you cloned your ffmpeg-GUI repository 68 | 69 | ```bash 70 | 71 | > sudo ln -s /home/_USER_/_DIR_/ffmpeg-GUI/ffmpegGUI.py /usr/bin/ffmpegGUI 72 | 73 | ``` 74 | 75 | 2. now you can execute the script with the following from rofi, dmenu or anywere in terminal. 76 | 77 | ```bash 78 | 79 | > ffmpegGUI 80 | 81 | ``` 82 | 83 | -------------------------------------------------------------------------------- /ffmpegGUI.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import subprocess 5 | import gi 6 | gi.require_version('Gtk', '3.0') 7 | from gi.repository import GLib, Gtk, GObject 8 | import threading 9 | import time 10 | import signal 11 | 12 | 13 | aspectRatio = ["16:9", "1.85", "2.40"] 14 | 15 | res16_9 = ["1920x1080", "1280x720", "720x406"] 16 | res1_85 = ["1920x1038", "1280x692", "720x388"] 17 | res2_40 = ["1920x801", "1280x534", "720x300"] 18 | 19 | vcodecs = ["x264", "x264_lossless", "x265", "x265_lossless", "matroska"] 20 | 21 | x265_profiles = ["ultrafast", "superfast", "veryfast", "faster", "fast", "medium", "slow", "slower", "veryslow", "placebo"] 22 | x264_profiles = ["high", "main", "baseline"] 23 | 24 | high = ["4.2", "4.1", "4.0"] 25 | main = ["4.0", "3.1"] 26 | baseline = ["3.1", "3.0"] 27 | 28 | MP4_Acodecs = ["aac", "mp3", "ac3"] 29 | MKV_Acodecs = ["aac", "mp3", "ac3", "vorbis", "opus"] 30 | 31 | bit = ["8bit", "10bit", "12bit"] 32 | 33 | SYSTERMS = ["urxvt", "xfce4-terminal", "gnome-terminal", "xterm", "terminator", "tilda", 34 | "lxterminal", "konsole", "st"] 35 | 36 | error = 0; 37 | 38 | base_dir = os.path.dirname(os.path.realpath(__file__)) 39 | 40 | class ffmpegGUI(Gtk.Window): 41 | 42 | def __init__(self): 43 | super(ffmpegGUI, self).__init__() 44 | Gtk.Window.__init__(self, title="HEFFS FFMPEG GUI") 45 | self.set_resizable(False) 46 | self.set_border_width(10) 47 | self.set_icon_from_file(os.path.join(base_dir, 'icon.png')) 48 | self.set_position(Gtk.WindowPosition.CENTER) 49 | 50 | #=========================================== 51 | # Main Container 52 | #=========================================== 53 | 54 | self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10) 55 | self.add(self.vbox) 56 | 57 | #=========================================== 58 | # HEADER Section 59 | #=========================================== 60 | self.listviewHDR = Gtk.ListBox() 61 | self.listviewHDR.set_selection_mode(Gtk.SelectionMode.NONE) 62 | self.vbox.pack_start(self.listviewHDR, True, True, 0) 63 | 64 | #ListRow 1 65 | self.listRowHDR = Gtk.ListBoxRow() 66 | self.hboxHDR = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) 67 | self.listRowHDR.add(self.hboxHDR) 68 | 69 | #ListRow 1 Elements 70 | self.image = Gtk.Image() 71 | self.image.set_from_file(os.path.join(base_dir, 'logo.png')) 72 | self.image_area = Gtk.Box() 73 | self.image_area.add(self.image) 74 | self.image_area.show_all() 75 | 76 | self.hboxHDR.pack_start(self.image_area, True, True, 0) 77 | self.listviewHDR.add(self.listRowHDR) 78 | 79 | #=========================================== 80 | # 1st Section 81 | #=========================================== 82 | self.listview1 = Gtk.ListBox() 83 | self.listview1.set_selection_mode(Gtk.SelectionMode.NONE) 84 | self.vbox.pack_start(self.listview1, True, True, 0) 85 | 86 | #ListRow 1 87 | self.listRow1 = Gtk.ListBoxRow() 88 | self.hbox1 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) 89 | self.listRow1.add(self.hbox1) 90 | 91 | #ListRow 1 Elements 92 | self.label1 = Gtk.Label(xalign=0) 93 | self.label1.set_text("Auto Close") 94 | self.switch = Gtk.Switch() 95 | self.switch.set_active(True) 96 | 97 | self.hbox1.pack_start(self.label1, True, True, 0) 98 | self.hbox1.pack_start(self.switch, False, False, 0) 99 | self.listview1.add(self.listRow1) 100 | 101 | #=========================================== 102 | # TERM Section 103 | #=========================================== 104 | self.listviewTERM = Gtk.ListBox() 105 | self.listviewTERM.set_selection_mode(Gtk.SelectionMode.NONE) 106 | self.vbox.pack_start(self.listviewTERM, True, True, 0) 107 | 108 | #ListRow 1 109 | self.listRowTERM = Gtk.ListBoxRow() 110 | self.hbox1TERM = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) 111 | self.listRowTERM.add(self.hbox1TERM) 112 | 113 | #ListRow 1 Elements 114 | self.labelTERM = Gtk.Label(xalign=0) 115 | self.labelTERM.set_text("Terminal") 116 | self.comboTERM = Gtk.ComboBoxText() 117 | self.comboTERM.set_size_request(170, 0) 118 | for TERM in SYSTERMS: 119 | self.comboTERM.append_text(TERM) 120 | self.comboTERM.set_active(0) 121 | 122 | self.hbox1TERM.pack_start(self.labelTERM, True, True, 0) 123 | self.hbox1TERM.pack_start(self.comboTERM, False, False, 0) 124 | self.listviewTERM.add(self.listRowTERM) 125 | 126 | #=========================================== 127 | # 2nd Section 128 | #=========================================== 129 | self.listview2 = Gtk.ListBox() 130 | self.listview2.set_selection_mode(Gtk.SelectionMode.NONE) 131 | self.vbox.pack_start(self.listview2, True, True, 0) 132 | 133 | #================== 134 | # Row 1 135 | #================== 136 | self.listRow1 = Gtk.ListBoxRow() 137 | self.hbox1 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) 138 | self.listRow1.add(self.hbox1) 139 | 140 | # ListRow 1 Elements 141 | self.label2 = Gtk.Label(xalign=0) 142 | self.label2.set_text("In File") 143 | self.textbox1 = Gtk.Entry() 144 | self.button1 = Gtk.Button(label="...") 145 | self.button1.connect("clicked", self.on_button_open_clicked) 146 | 147 | self.hbox1.pack_start(self.label2, True, True, 0) 148 | self.hbox1.pack_start(self.textbox1, False, False, 0) 149 | self.hbox1.pack_start(self.button1, False, False, 0) 150 | self.listview2.add(self.listRow1) 151 | 152 | #================== 153 | # Row 2 154 | #================== 155 | self.listRow2 = Gtk.ListBoxRow() 156 | self.hbox1 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) 157 | self.listRow2.add(self.hbox1) 158 | 159 | # ListRow 2 Elements 160 | self.label3 = Gtk.Label(xalign=0) 161 | self.label3.set_text("Out File") 162 | self.textbox2 = Gtk.Entry() 163 | self.button2 = Gtk.Button(label="...") 164 | self.button2.connect("clicked", self.on_button_save_clicked) 165 | 166 | self.hbox1.pack_start(self.label3, True, True, 0) 167 | self.hbox1.pack_start(self.textbox2, False, False, 0) 168 | self.hbox1.pack_start(self.button2, False, False, 0) 169 | self.listview2.add(self.listRow2) 170 | 171 | 172 | #=========================================== 173 | # 3rd Section 174 | #=========================================== 175 | self.listview3 = Gtk.ListBox() 176 | self.listview3.set_selection_mode(Gtk.SelectionMode.NONE) 177 | self.vbox.pack_start(self.listview3, True, True, 0) 178 | 179 | #================== 180 | # Row 1 181 | #================== 182 | self.listRow3 = Gtk.ListBoxRow() 183 | self.hbox1 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) 184 | self.listRow3.add(self.hbox1) 185 | 186 | # ListRow 1 Elements 187 | self.label4 = Gtk.Label(xalign=0) 188 | self.label4.set_text("Aspect Ratio") 189 | self.comboAspect = Gtk.ComboBoxText() 190 | self.comboAspect.set_size_request(170, 0) 191 | for aspect in aspectRatio: 192 | self.comboAspect.append_text(aspect) 193 | self.comboAspect.set_active(0) 194 | 195 | self.hbox1.pack_start(self.label4, True, True, 0) 196 | self.hbox1.pack_start(self.comboAspect, False, False, 0) 197 | self.listview3.add(self.listRow3) 198 | 199 | #================== 200 | # Row 2 201 | #================== 202 | self.listRow4 = Gtk.ListBoxRow() 203 | self.hbox1 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) 204 | self.listRow4.add(self.hbox1) 205 | 206 | # ListRow 1 Elements 207 | self.label5 = Gtk.Label(xalign=0) 208 | self.label5.set_text("Resolution") 209 | self.comboRes = Gtk.ComboBoxText() 210 | self.comboRes.set_size_request(170,0) 211 | for res in res16_9: 212 | self.comboRes.append_text(res) 213 | self.comboRes.set_active(0) 214 | 215 | self.hbox1.pack_start(self.label5, True, True, 0) 216 | self.hbox1.pack_start(self.comboRes, False, False, 0) 217 | self.listview3.add(self.listRow4) 218 | 219 | self.comboAspect.connect("changed", self.on_aspect_changed) 220 | 221 | #=========================================== 222 | # 4th Section 223 | #=========================================== 224 | self.listview_profile = Gtk.ListBox() 225 | self.listview_profile.set_selection_mode(Gtk.SelectionMode.NONE) 226 | self.vbox.pack_start(self.listview_profile, True, True, 0) 227 | 228 | #================== 229 | # Row 1 230 | #================== 231 | self.listRowC = Gtk.ListBoxRow() 232 | self.hboxC = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) 233 | self.listRowC.add(self.hboxC) 234 | 235 | # ListRow 1 Elements 236 | self.labelC = Gtk.Label(xalign=0) 237 | self.labelC.set_text("Codec") 238 | self.comboCodec = Gtk.ComboBoxText() 239 | self.comboCodec.set_size_request(170, 0) 240 | for codec in vcodecs: 241 | self.comboCodec.append_text(codec) 242 | self.comboCodec.set_active(0) 243 | 244 | self.hboxC.pack_start(self.labelC, True, True, 0) 245 | self.hboxC.pack_start(self.comboCodec, False, False, 0) 246 | self.listview_profile.add(self.listRowC) 247 | 248 | self.comboCodec.connect("changed", self.on_codec_change) 249 | 250 | #================== 251 | # Row 2 252 | #================== 253 | self.listRow8 = Gtk.ListBoxRow() 254 | self.hbox1 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) 255 | self.listRow8.add(self.hbox1) 256 | 257 | # ListRow 1 Elements 258 | self.label9 = Gtk.Label(xalign=0) 259 | self.label9.set_text("x264 Profile") 260 | self.comboProfile = Gtk.ComboBoxText() 261 | self.comboProfile.set_size_request(170, 0) 262 | for profile in x264_profiles: 263 | self.comboProfile.append_text(profile) 264 | self.comboProfile.set_active(0) 265 | 266 | self.hbox1.pack_start(self.label9, True, True, 0) 267 | self.hbox1.pack_start(self.comboProfile, False, False, 0) 268 | self.listview_profile.add(self.listRow8) 269 | 270 | #================== 271 | # Row 3 272 | #================== 273 | self.listRow9 = Gtk.ListBoxRow() 274 | self.hbox1 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) 275 | self.listRow9.add(self.hbox1) 276 | 277 | # ListRow 1 Elements 278 | self.label10 = Gtk.Label(xalign=0) 279 | self.label10.set_text("Profile Level") 280 | self.comboLevel = Gtk.ComboBoxText() 281 | self.comboLevel.set_size_request(170, 0) 282 | for level in high: 283 | self.comboLevel.append_text(level) 284 | self.comboLevel.set_active(0) 285 | 286 | self.hbox1.pack_start(self.label10, True, True, 0) 287 | self.hbox1.pack_start(self.comboLevel, False, False, 0) 288 | self.listview_profile.add(self.listRow9) 289 | 290 | self.comboProfile.connect("changed", self.on_profile_change) 291 | 292 | #=========================================== 293 | # 5th Section 294 | #=========================================== 295 | self.listview4 = Gtk.ListBox() 296 | self.listview4.set_selection_mode(Gtk.SelectionMode.NONE) 297 | self.vbox.pack_start(self.listview4, True, True, 0) 298 | 299 | #================== 300 | # Row 1 301 | #================== 302 | self.listRow5 = Gtk.ListBoxRow() 303 | self.hbox1 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) 304 | self.listRow5.add(self.hbox1) 305 | 306 | # ListRow 1 Elements 307 | self.label6 = Gtk.Label(xalign=0) 308 | self.label6.set_text("Audio Format") 309 | self.comboAudio = Gtk.ComboBoxText() 310 | self.comboAudio.set_size_request(170, 0) 311 | for audio in MP4_Acodecs: 312 | self.comboAudio.append_text(audio) 313 | self.comboAudio.set_active(0) 314 | 315 | self.hbox1.pack_start(self.label6, True, True, 0) 316 | self.hbox1.pack_start(self.comboAudio, False, False, 0) 317 | self.listview4.add(self.listRow5) 318 | 319 | #=========================================== 320 | # 6th Section 321 | #=========================================== 322 | self.listview5 = Gtk.ListBox() 323 | self.listview5.set_selection_mode(Gtk.SelectionMode.NONE) 324 | self.vbox.pack_start(self.listview5, True, True, 0) 325 | 326 | #================== 327 | # Row 1 328 | #================== 329 | self.listRow6 = Gtk.ListBoxRow() 330 | self.hbox1 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) 331 | self.listRow6.add(self.hbox1) 332 | 333 | # ListRow 1 Elements 334 | self.label7 = Gtk.Label(xalign=0) 335 | self.label7.set_text("Video Bitrate") 336 | self.textbox3 = Gtk.Entry() 337 | self.textbox3.set_size_request(80, 0) 338 | self.textbox3.set_text("1800") 339 | 340 | self.hbox1.pack_start(self.label7, True, True, 0) 341 | self.hbox1.pack_start(self.textbox3, False, False, 0) 342 | self.listview5.add(self.listRow6) 343 | 344 | #================== 345 | # Row 2 346 | #================== 347 | self.listRow7 = Gtk.ListBoxRow() 348 | self.hbox1 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) 349 | self.listRow7.add(self.hbox1) 350 | 351 | # ListRow 1 Elements 352 | self.label8 = Gtk.Label(xalign=0) 353 | self.label8.set_text("Audio Bitrate (kbps)") 354 | self.textbox4 = Gtk.Entry() 355 | self.textbox4.set_size_request(80, 0) 356 | self.textbox4.set_text("128") 357 | 358 | self.hbox1.pack_start(self.label8, True, True, 0) 359 | self.hbox1.pack_start(self.textbox4, False, False, 0) 360 | self.listview5.add(self.listRow7) 361 | 362 | self.comboAudio.connect("changed", self.on_audio_changed) 363 | 364 | #=========================================== 365 | # 7th Section 366 | #=========================================== 367 | self.listviewBTM = Gtk.ListBox() 368 | self.listviewBTM.set_selection_mode(Gtk.SelectionMode.NONE) 369 | self.vbox.pack_start(self.listviewBTM, True, True, 0) 370 | 371 | #================== 372 | # Row 1 373 | #================== 374 | self.listRowBTM = Gtk.ListBoxRow() 375 | self.hbox1 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) 376 | self.listRowBTM.add(self.hbox1) 377 | 378 | # ListRow 1 Elements 379 | self.buttonBTM = Gtk.Button(label="RUN") 380 | self.buttonBTM.set_size_request(100, 0) 381 | self.buttonBTM.connect("clicked", self.on_buttonBTM_changed) 382 | 383 | self.hbox1.pack_end(self.buttonBTM, True, True, 0) 384 | self.listviewBTM.add(self.listRowBTM) 385 | 386 | 387 | def on_button_save_clicked(self, widget): 388 | dialog = Gtk.FileChooserDialog( 389 | title="Please choose a file", 390 | action=Gtk.FileChooserAction.SAVE, 391 | ) 392 | 393 | dialog.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, 394 | "Save", Gtk.ResponseType.OK) 395 | 396 | response = dialog.run() 397 | 398 | if response == Gtk.ResponseType.OK: 399 | filename = dialog.get_filename() 400 | if self.comboCodec.get_active_text() == "matroska": 401 | if ".mkv" in filename: 402 | self.textbox2.set_text(filename) 403 | else: 404 | self.textbox2.set_text(filename + ".mkv") 405 | else: 406 | if ".mp4" in filename: 407 | self.textbox2.set_text(filename) 408 | else: 409 | self.textbox2.set_text(filename + ".mp4") 410 | 411 | dialog.destroy() 412 | elif response == Gtk.ResponseType.CANCEL: 413 | # print("Cancel clicked") 414 | dialog.destroy() 415 | pass 416 | 417 | def on_button_open_clicked(self, widget): 418 | 419 | dialog = Gtk.FileChooserDialog( 420 | title="Please choose a file", 421 | action=Gtk.FileChooserAction.OPEN, 422 | ) 423 | 424 | dialog.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, 425 | "Open", Gtk.ResponseType.OK) 426 | 427 | filter = Gtk.FileFilter() 428 | filter.set_name("VIDEO Files") 429 | filter.add_mime_type("video/x-msvideo") 430 | filter.add_mime_type("video/msvideo") 431 | filter.add_mime_type("video/avi") 432 | filter.add_mime_type("video/x-matroska") 433 | filter.add_mime_type("video/mpeg") 434 | filter.add_mime_type("video/mp4") 435 | dialog.set_filter(filter) 436 | 437 | response = dialog.run() 438 | 439 | if response == Gtk.ResponseType.OK: 440 | #print("Open clicked") 441 | self.textbox1.set_text(dialog.get_filename()) 442 | dialog.destroy() 443 | elif response == Gtk.ResponseType.CANCEL: 444 | #print("Cancel clicked") 445 | dialog.destroy() 446 | pass 447 | 448 | 449 | def on_codec_change(self,combo): 450 | text = combo.get_active_text() 451 | 452 | self.comboProfile.get_model().clear() 453 | 454 | if text == "x264": 455 | for profs in x264_profiles: 456 | self.comboProfile.append_text(profs) 457 | 458 | self.comboAudio.get_model().clear() 459 | for codes in MP4_Acodecs: 460 | self.comboAudio.append_text(codes) 461 | elif text == "x264_lossless": 462 | for profs in x265_profiles: 463 | self.comboProfile.append_text(profs) 464 | 465 | self.comboAudio.get_model().clear() 466 | for codes in MP4_Acodecs: 467 | self.comboAudio.append_text(codes) 468 | elif text == "x265" or text == "x265_lossless": 469 | for profs in x265_profiles: 470 | self.comboProfile.append_text(profs) 471 | 472 | self.comboAudio.get_model().clear() 473 | for codes in MP4_Acodecs: 474 | self.comboAudio.append_text(codes) 475 | 476 | else: 477 | self.comboProfile.append_text('---') 478 | 479 | self.comboAudio.get_model().clear() 480 | for codes in MKV_Acodecs: 481 | self.comboAudio.append_text(codes) 482 | 483 | self.comboProfile.set_active(0); 484 | self.comboAudio.set_active(0); 485 | 486 | 487 | 488 | def on_audio_changed(self, combo): 489 | text = combo.get_active_text() 490 | if text == "aac": 491 | self.textbox4.set_text("128") 492 | elif text == "mp3": 493 | self.textbox4.set_text("192") 494 | elif text == "vorbis": 495 | self.textbox4.set_text("128"); 496 | elif text == "opus": 497 | self.textbox4.set_text("64"); 498 | else: 499 | self.textbox4.set_text("160"); 500 | 501 | 502 | def on_aspect_changed(self, combo): 503 | text = combo.get_active_text() 504 | if text == "16:9": 505 | self.comboRes.get_model().clear() 506 | for res in res16_9: 507 | self.comboRes.append_text(res) 508 | self.comboRes.set_active(0) 509 | elif text == "1.85": 510 | self.comboRes.get_model().clear() 511 | for res in res1_85: 512 | self.comboRes.append_text(res) 513 | self.comboRes.set_active(0) 514 | elif text == "2.40": 515 | self.comboRes.get_model().clear() 516 | for res in res2_40: 517 | self.comboRes.append_text(res) 518 | self.comboRes.set_active(0) 519 | 520 | def on_profile_change(self, combo): 521 | text = combo.get_active_text() 522 | 523 | self.comboLevel.get_model().clear() 524 | 525 | if text == "high": 526 | for level in high: 527 | self.comboLevel.append_text(level) 528 | elif text == "main": 529 | for level in main: 530 | self.comboLevel.append_text(level) 531 | elif text == "baseline": 532 | for level in baseline: 533 | self.comboLevel.append_text(level) 534 | elif text == "ultrafast" or text == "superfast" or text == "veryfast" or text == "faster" or text == "fast" \ 535 | or text == "medium" or text == "slow" or text == "slower" or text == "veryslow" or text == "placebo": 536 | for level in bit: 537 | self.comboLevel.append_text(level) 538 | else: 539 | self.comboLevel.append_text("---") 540 | 541 | self.comboLevel.set_active(0) 542 | 543 | 544 | def on_buttonBTM_changed(self,widget): 545 | if not self.checkBoxes(): 546 | md = Gtk.MessageDialog(parent=self, flags=0, message_type=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.OK, text="Infile or Outfile missing") 547 | md.format_secondary_text("Both fields need to be filled for the encoder to work") 548 | md.run() 549 | md.destroy() 550 | return False 551 | 552 | error = 0; 553 | TERMINAL = self.comboTERM.get_active_text() 554 | 555 | G_SIZE="120x23" 556 | GEOMETRY = "--geometry=" + G_SIZE 557 | TITLE = "-T" 558 | 559 | if TERMINAL == "urxvt" or TERMINAL == "xterm": 560 | GEOMETRY = "-geometry" 561 | TITLE = "-title" 562 | elif TERMINAL == "lxterminal": 563 | TITLE = "-t" 564 | elif TERMINAL == "konsole": 565 | TITLE = "--nofork" 566 | elif TERMINAL == "st": 567 | GEOMETRY = "-g" 568 | 569 | 570 | 571 | if self.comboAudio.get_active_text() == "mp3": 572 | aCodec = "libmp3lame" 573 | aArg = "-q:a" 574 | aVal = "2" 575 | elif self.comboAudio.get_active_text() == "aac": 576 | aCodec = "aac" 577 | aArg = "-q:a" 578 | aVal = "3" 579 | elif self.comboAudio.get_active_text() == "vorbis": 580 | aCodec = "libvorbis" 581 | aArg = "-q:a" 582 | aVal = "4" 583 | elif self.comboAudio.get_active_text() == "opus": 584 | aCodec = "libopus" 585 | aArg = "-q:a" 586 | aVal = "2" 587 | else: 588 | aCodec = "ac3" 589 | aArg = "-q:a" 590 | aVal = "2" 591 | 592 | if self.comboCodec.get_active_text() == "x264" or self.comboCodec.get_active_text() == "x264_lossless": 593 | bits = "-pix_fmt" 594 | if self.comboLevel.get_active_text() == "8bit": 595 | bits_val = "yuv420p" 596 | elif self.comboLevel.get_active_text() == "10bit": 597 | bits_val = "yuv420p10le" 598 | elif self.comboLevel.get_active_text() == "12bit": 599 | bits_val = "yuv420p12le" 600 | else: 601 | bits = "-level" 602 | bits_val = self.comboLevel.get_active_text() 603 | 604 | if self.comboCodec.get_active_text() == "x264_lossless": 605 | INPUT = "-preset" 606 | CRF = "-crf" 607 | CRF_VAL = "0" 608 | else: 609 | INPUT = "-profile:v" 610 | CRF = "-crf" 611 | CRF_VAL = "30" 612 | 613 | COMMAND = [TERMINAL, TITLE, "bash", GEOMETRY, G_SIZE, "-e", "ffmpeg", "-i", self.textbox1.get_text(), "-metadata", "title=" + os.path.basename(self.textbox2.get_text()), "-vf", "scale=" + self.comboRes.get_active_text(), 614 | "-aspect", self.comboAspect.get_active_text(), "-acodec", aCodec, aArg, aVal, "-ar", "48000", "-b:a", self.textbox4.get_text() + "k", "-vcodec", "mpeg4", "-b:v", 615 | self.textbox3.get_text() + "K", "-c:v", "libx264", CRF, CRF_VAL, INPUT, self.comboProfile.get_active_text(), bits, bits_val, "-hide_banner", self.textbox2.get_text()] 616 | 617 | 618 | elif self.comboCodec.get_active_text() == "x265" or self.comboCodec.get_active_text() == "x265_lossless": 619 | bits = "-pix_fmt" 620 | bits_val = "yuv420p" 621 | 622 | if self.comboLevel.get_active_text() == "8bit": 623 | bits_val = "yuv420p" 624 | elif self.comboLevel.get_active_text() == "10bit": 625 | bits_val = "yuv420p10le" 626 | elif self.comboLevel.get_active_text() == "12bit": 627 | bits_val = "yuv420p12le" 628 | 629 | if self.comboCodec.get_active_text() == "x265_lossless": 630 | CRF = "crf=0" 631 | else: 632 | CRF = "crf=30" 633 | 634 | COMMAND = [TERMINAL, TITLE, "bash", GEOMETRY, G_SIZE, "-e", "ffmpeg", "-i", self.textbox1.get_text(), "-metadata", "title=" + os.path.basename(self.textbox2.get_text()), "-vf", "scale=" + self.comboRes.get_active_text(), 635 | "-aspect", self.comboAspect.get_active_text(), "-acodec", aCodec, aArg, aVal, "-ar", "48000", "-b:a", self.textbox4.get_text() + "k", "-b:v", self.textbox3.get_text() + "K", "-c:v", "libx265", "-x265-params", 636 | CRF, bits, bits_val, "-preset", self.comboProfile.get_active_text(), "-hide_banner", self.textbox2.get_text()] 637 | 638 | 639 | else: 640 | COMMAND = [TERMINAL, TITLE, "bash", GEOMETRY, G_SIZE, "-e", "ffmpeg", "-i", self.textbox1.get_text(), "-metadata", "title=" + os.path.basename(self.textbox2.get_text()), "-f", "matroska", "-vf", 641 | "scale=" + self.comboRes.get_active_text(), "-aspect", self.comboAspect.get_active_text(), "-acodec", aCodec, aArg, aVal, "-ar", "48000", "-b:a", self.textbox4.get_text() + "k", 642 | "-vcodec", "vp8", "-b:v", self.textbox3.get_text() + "K", "-hide_banner", self.textbox2.get_text()] 643 | 644 | 645 | self.set_sensitive(False) 646 | 647 | if not os.path.isfile(self.textbox1.get_text()): 648 | self.callError(0) 649 | return False 650 | 651 | t = threading.Thread(target=self.RunEncode, args=(COMMAND,)) 652 | t.daemon = True 653 | t.start() 654 | 655 | 656 | def callError(self, errorCode): 657 | if errorCode == 0: 658 | message = "Seems ffmpegGUI can not find the file you wish to encode.\n Please check your file exists." 659 | else: 660 | message = "Seems the terminal selected is incorrect or the command input is not properly coded for your terminals excecution option." 661 | 662 | error = 1; 663 | md = Gtk.MessageDialog(parent=self, flags=0, message_type=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.OK, text="An Error has occured") 664 | md.format_secondary_text(message) 665 | md.run() 666 | md.destroy() 667 | self.set_sensitive(True) 668 | 669 | def checkEncoder(self,CODE): 670 | if CODE == 0 and not error == 1: 671 | print("ENCODING COMPLETE!") 672 | if self.switch.get_active(): 673 | Gtk.main_quit(0) 674 | else: 675 | self.set_sensitive(True) 676 | return False 677 | 678 | def RunEncode(self, command): 679 | try: 680 | p = subprocess.Popen(command, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 681 | res = p.communicate() 682 | 683 | while True: 684 | GLib.idle_add(self.checkEncoder, p.returncode) 685 | time.sleep(0.2) 686 | if p.returncode == 0: 687 | break 688 | except: 689 | GLib.idle_add(self.callError, 1) 690 | print("ENCODING ERROR!") 691 | 692 | 693 | 694 | def checkBoxes(self): 695 | if self.textbox1.get_text() == "": 696 | return False 697 | elif self.textbox2.get_text() == "": 698 | return False 699 | else: 700 | return True 701 | 702 | 703 | def signal_handler(sig, frame): 704 | print('\nYou pressed Ctrl+C!\nHEFFS ffmpeg GUI is Closing.') 705 | Gtk.main_quit(0) 706 | 707 | if __name__ == '__main__': 708 | #GObject.threads_init() 709 | signal.signal(signal.SIGINT, signal_handler) 710 | window = ffmpegGUI() 711 | window.connect("delete-event", Gtk.main_quit) 712 | window.show_all() 713 | Gtk.main() -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BradHeff/ffmpeg-GUI/a4f9c68fc221be1d914b7dd908b4e560d87188bd/icon.png -------------------------------------------------------------------------------- /image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BradHeff/ffmpeg-GUI/a4f9c68fc221be1d914b7dd908b4e560d87188bd/image.png -------------------------------------------------------------------------------- /image2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BradHeff/ffmpeg-GUI/a4f9c68fc221be1d914b7dd908b4e560d87188bd/image2.png -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BradHeff/ffmpeg-GUI/a4f9c68fc221be1d914b7dd908b4e560d87188bd/logo.png --------------------------------------------------------------------------------