Renamed module from devede to devedeng
Now it uses the 'locale' folder to create the locales list names.
This commit is contained in:
parent
cfb9238a73
commit
bc6fa87b3c
43 changed files with 241 additions and 245 deletions
19
src/devedeng/__init__.py
Normal file
19
src/devedeng/__init__.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
38
src/devedeng/about.py
Normal file
38
src/devedeng/about.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
from gi.repository import Gtk
|
||||
import os
|
||||
import devedeng.configuration_data
|
||||
|
||||
class about_window:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(self.config.gettext_domain)
|
||||
|
||||
builder.add_from_file(os.path.join(self.config.glade,"wabout.ui"))
|
||||
builder.connect_signals(self)
|
||||
w_window = builder.get_object("about_devedeng")
|
||||
w_window.set_version(self.config.version)
|
||||
|
||||
w_window.show_all()
|
||||
w_window.run()
|
||||
w_window.destroy()
|
||||
86
src/devedeng/add_files.py
Normal file
86
src/devedeng/add_files.py
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
from gi.repository import Gtk
|
||||
import os
|
||||
import devedeng.configuration_data
|
||||
|
||||
class add_files:
|
||||
|
||||
last_path = None
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
def run(self):
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(self.config.gettext_domain)
|
||||
|
||||
builder.add_from_file(os.path.join(self.config.glade,"wadd_files.ui"))
|
||||
builder.connect_signals(self)
|
||||
wadd_files = builder.get_object("add_files")
|
||||
self.wfile_chooser = builder.get_object("filechooserwidget1")
|
||||
if (add_files.last_path != None):
|
||||
self.wfile_chooser.set_current_folder(add_files.last_path)
|
||||
self.wbutton_accept = builder.get_object("button_accept")
|
||||
|
||||
file_filter_videos=Gtk.FileFilter()
|
||||
file_filter_videos.set_name(_("Video files"))
|
||||
file_filter_videos.add_mime_type("video/*")
|
||||
file_filter_videos.add_pattern("*.rmvb")
|
||||
|
||||
file_filter_all=Gtk.FileFilter()
|
||||
file_filter_all.set_name(_("All files"))
|
||||
file_filter_all.add_pattern("*")
|
||||
|
||||
self.wfile_chooser.add_filter(file_filter_videos)
|
||||
self.wfile_chooser.add_filter(file_filter_all)
|
||||
|
||||
wadd_files.show_all()
|
||||
|
||||
retval = wadd_files.run()
|
||||
self.files = None
|
||||
|
||||
if (retval == 2):
|
||||
self.files = self.get_files()
|
||||
|
||||
add_files.last_path = self.wfile_chooser.get_current_folder()
|
||||
wadd_files.destroy()
|
||||
|
||||
if (retval == 2):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_files(self):
|
||||
files = self.wfile_chooser.get_filenames()
|
||||
files_out = []
|
||||
for element in files:
|
||||
if (os.path.isdir(element)):
|
||||
continue
|
||||
files_out.append(element)
|
||||
return files_out
|
||||
|
||||
def on_filechooserwidget1_selection_changed(self,b):
|
||||
|
||||
files = self.get_files()
|
||||
if (len(files) == 0):
|
||||
self.wbutton_accept.set_sensitive(False)
|
||||
else:
|
||||
self.wbutton_accept.set_sensitive(True)
|
||||
46
src/devedeng/ask.py
Normal file
46
src/devedeng/ask.py
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
from gi.repository import Gtk
|
||||
import os
|
||||
import devedeng.configuration_data
|
||||
|
||||
class ask_window:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
def run(self,text,title):
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(self.config.gettext_domain)
|
||||
|
||||
builder.add_from_file(os.path.join(self.config.glade,"wask.ui"))
|
||||
builder.connect_signals(self)
|
||||
wask_window = builder.get_object("dialog_ask")
|
||||
wask_window.set_title(title)
|
||||
wask_text = builder.get_object("label_ask")
|
||||
wask_text.set_markup(text)
|
||||
|
||||
wask_window.show_all()
|
||||
retval = wask_window.run()
|
||||
wask_window.destroy()
|
||||
if (retval == 1):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
120
src/devedeng/ask_subtitles.py
Normal file
120
src/devedeng/ask_subtitles.py
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
from gi.repository import Gtk
|
||||
import os
|
||||
import devedeng.configuration_data
|
||||
import devedeng.add_files
|
||||
|
||||
class ask_subtitles:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
def run(self):
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(self.config.gettext_domain)
|
||||
|
||||
builder.add_from_file(os.path.join(self.config.glade,"wask_subtitles.ui"))
|
||||
builder.connect_signals(self)
|
||||
wask_window = builder.get_object("ask_subtitles")
|
||||
self.wfilename = builder.get_object("subtitle_file")
|
||||
self.waccept = builder.get_object("accept")
|
||||
wlist_encodings = builder.get_object("list_encodings")
|
||||
wlist_languages = builder.get_object("list_languages")
|
||||
wencoding = builder.get_object("encoding_l")
|
||||
wlanguage = builder.get_object("language_l")
|
||||
|
||||
if (devedeng.add_files.add_files.last_path != None):
|
||||
self.wfilename.set_current_folder(devedeng.add_files.add_files.last_path)
|
||||
|
||||
lang_selection = 0
|
||||
enc_selection = 0
|
||||
|
||||
self.language = None
|
||||
self.encoding = None
|
||||
self.put_upper = False
|
||||
self.filename = None
|
||||
|
||||
counter = 0
|
||||
encodings = open(os.path.join(self.config.other_path,"codepages.lst"))
|
||||
for element in encodings:
|
||||
element = element.strip()
|
||||
if (element == self.config.sub_codepage):
|
||||
enc_selection = counter
|
||||
wlist_encodings.append([element])
|
||||
counter += 1
|
||||
encodings.close()
|
||||
|
||||
counter = 0
|
||||
languages = open(os.path.join(self.config.other_path,"languages.lst"))
|
||||
for element in languages:
|
||||
element = element.strip()
|
||||
if (element == self.config.sub_language):
|
||||
lang_selection = counter
|
||||
wlist_languages.append([element])
|
||||
counter += 1
|
||||
languages.close()
|
||||
|
||||
wencoding.set_active(enc_selection)
|
||||
wlanguage.set_active(lang_selection)
|
||||
|
||||
file_filter_subt=Gtk.FileFilter()
|
||||
file_filter_subt.set_name(_("Subtitle files"))
|
||||
|
||||
file_filter_subt.add_pattern("*.sub")
|
||||
file_filter_subt.add_pattern("*.srt")
|
||||
file_filter_subt.add_pattern("*.ssa")
|
||||
file_filter_subt.add_pattern("*.smi")
|
||||
file_filter_subt.add_pattern("*.rt")
|
||||
file_filter_subt.add_pattern("*.txt")
|
||||
file_filter_subt.add_pattern("*.aqt")
|
||||
|
||||
file_filter_all=Gtk.FileFilter()
|
||||
file_filter_all.set_name(_("All files"))
|
||||
file_filter_all.add_pattern("*")
|
||||
|
||||
self.wfilename.add_filter(file_filter_subt)
|
||||
self.wfilename.add_filter(file_filter_all)
|
||||
|
||||
wask_window.show_all()
|
||||
self.on_subtitle_file_set(None)
|
||||
retval = wask_window.run()
|
||||
if (retval == 2): # accept
|
||||
self.put_upper = builder.get_object("put_subtitles_upper").get_active()
|
||||
self.config.sub_codepage = wencoding.get_active_id()
|
||||
self.config.sub_language = wlanguage.get_active_id()
|
||||
self.encoding = wencoding.get_active_id()
|
||||
self.language = wlanguage.get_active_id()[:2]
|
||||
self.filename = self.wfilename.get_filename()
|
||||
|
||||
wask_window.destroy()
|
||||
if (retval == 2):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def on_subtitle_file_set(self,b):
|
||||
|
||||
f = self.wfilename.get_filename()
|
||||
if (f == None) or (f == ""):
|
||||
self.waccept.set_sensitive(False)
|
||||
else:
|
||||
self.waccept.set_sensitive(True)
|
||||
427
src/devedeng/avconv.py
Normal file
427
src/devedeng/avconv.py
Normal file
|
|
@ -0,0 +1,427 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
import subprocess
|
||||
import os
|
||||
import devedeng.configuration_data
|
||||
import devedeng.executor
|
||||
import devedeng.mux_dvd_menu
|
||||
|
||||
class avconv(devedeng.executor.executor):
|
||||
|
||||
supports_analize = False
|
||||
supports_play = False
|
||||
supports_convert = True
|
||||
supports_menu = True
|
||||
supports_mkiso = False
|
||||
supports_burn = False
|
||||
display_name = "AVCONV"
|
||||
disc_types = []
|
||||
|
||||
@staticmethod
|
||||
def check_is_installed():
|
||||
try:
|
||||
handle = subprocess.Popen(["avconv","-codecs"], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
|
||||
(stdout, stderr) = handle.communicate()
|
||||
if 0==handle.wait():
|
||||
mp2 = False
|
||||
mp3 = False
|
||||
ac3 = False
|
||||
mpeg1 = False
|
||||
mpeg2 = False
|
||||
divx = False
|
||||
h264 = False
|
||||
for line in stdout.decode("latin-1").split("\n"):
|
||||
parts = line.strip().split(" ")
|
||||
if len(parts) < 2:
|
||||
continue
|
||||
if len(parts[0]) != 6:
|
||||
continue
|
||||
capabilities = parts[0]
|
||||
codec = parts[1]
|
||||
|
||||
if capabilities[1] != 'E':
|
||||
continue
|
||||
|
||||
if (codec == "mpeg1video"):
|
||||
mpeg1 = True
|
||||
continue
|
||||
if (codec == "mpeg2video"):
|
||||
mpeg2 = True
|
||||
continue
|
||||
if (codec == "mp2"):
|
||||
mp2 = True
|
||||
continue
|
||||
if (codec == "mp3"):
|
||||
mp3 = True
|
||||
continue
|
||||
if (codec == "ac3"):
|
||||
ac3 = True
|
||||
continue
|
||||
if (codec == "h264") or (codec == "H264"):
|
||||
h264 = True
|
||||
continue
|
||||
if (codec == "mpeg4"):
|
||||
divx = True
|
||||
continue
|
||||
|
||||
if (mpeg1 and mp2):
|
||||
devedeng.avconv.avconv.disc_types.append("vcd")
|
||||
if (mpeg2 and mp2):
|
||||
devedeng.avconv.avconv.disc_types.append("svcd")
|
||||
devedeng.avconv.avconv.disc_types.append("cvd")
|
||||
if (mpeg2 and mp2 and ac3):
|
||||
devedeng.avconv.avconv.disc_types.append("dvd")
|
||||
if (divx and mp3):
|
||||
devedeng.avconv.avconv.disc_types.append("divx")
|
||||
if (h264 and mp3):
|
||||
devedeng.avconv.avconv.disc_types.append("mkv")
|
||||
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def __init__(self):
|
||||
|
||||
devedeng.executor.executor.__init__(self)
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
def convert_file(self,file_project,output_file,video_length,pass2 = False):
|
||||
|
||||
if file_project.two_pass_encoding:
|
||||
if pass2:
|
||||
self.text = _("Converting %(X)s (pass 2)") % {"X" : file_project.title_name}
|
||||
else:
|
||||
self.text = _("Converting %(X)s (pass 1)") % {"X" : file_project.title_name}
|
||||
# Prepare the converting process for the second pass
|
||||
tmp = devedeng.avconv.avconv()
|
||||
tmp.convert_file(file_project, output_file, video_length, True)
|
||||
# it deppends of this process
|
||||
tmp.add_dependency(self)
|
||||
# add it as a child process of this one
|
||||
self.add_child_process(tmp)
|
||||
else:
|
||||
self.text = _("Converting %(X)s") % {"X" : file_project.title_name}
|
||||
|
||||
if (pass2 == False) and (file_project.two_pass_encoding == True):
|
||||
# this is the first pass in a 2-pass codification
|
||||
second_pass = False
|
||||
else:
|
||||
# second_pass is TRUE in the second pass of a 2-pass codification, and also when not doing 2-pass codification
|
||||
# It is used to remove unnecessary steps during the first pass, but that are needed on the second pass, or when not using 2-pass codification
|
||||
second_pass = True
|
||||
|
||||
if (video_length == 0):
|
||||
self.final_length = file_project.original_length
|
||||
else:
|
||||
self.final_length = video_length
|
||||
self.command_var=[]
|
||||
self.command_var.append("avconv")
|
||||
self.command_var.append("-i")
|
||||
self.command_var.append(file_project.file_name)
|
||||
|
||||
if (file_project.volume!=100) and second_pass:
|
||||
self.command_var.append("-vol")
|
||||
self.command_var.append(str((256*file_project.volume)/100))
|
||||
|
||||
if (file_project.audio_delay != 0.0) and (file_project.copy_sound==False) and (file_project.no_reencode_audio_video==False) and second_pass:
|
||||
self.command_var.append("-itsoffset")
|
||||
self.command_var.append(str(file_project.audio_delay))
|
||||
|
||||
self.command_var.append("-i")
|
||||
self.command_var.append(file_project.file_name)
|
||||
self.command_var.append("-map")
|
||||
self.command_var.append("1:"+str(file_project.video_list[0]))
|
||||
if (not file_project.copy_sound) and (not file_project.no_reencode_audio_video):
|
||||
for l in file_project.audio_list:
|
||||
self.command_var.append("-map")
|
||||
self.command_var.append("0:"+str(l))
|
||||
|
||||
if (file_project.no_reencode_audio_video==False) and second_pass:
|
||||
cmd_line=""
|
||||
|
||||
if file_project.deinterlace=="deinterlace_yadif":
|
||||
cmd_line+="yadif"
|
||||
|
||||
vflip=False
|
||||
hflip=False
|
||||
|
||||
if (file_project.rotation=="rotation_90"):
|
||||
if (cmd_line!=""):
|
||||
cmd_line+=",fifo,"
|
||||
cmd_line+="transpose=1"
|
||||
elif (file_project.rotation=="rotation_270"):
|
||||
if (cmd_line!=""):
|
||||
cmd_line+=",fifo,"
|
||||
cmd_line+="transpose=2"
|
||||
elif (file_project.rotation=="rotation_180"):
|
||||
vflip=True
|
||||
hflip=True
|
||||
|
||||
if (file_project.mirror_vertical):
|
||||
vflip=not vflip
|
||||
if (file_project.mirror_horizontal):
|
||||
hflip=not hflip
|
||||
|
||||
if (vflip):
|
||||
if (cmd_line!=""):
|
||||
cmd_line+=",fifo,"
|
||||
cmd_line+="vflip"
|
||||
if (hflip):
|
||||
if (cmd_line!=""):
|
||||
cmd_line+=",fifo,"
|
||||
cmd_line+="hflip"
|
||||
|
||||
if (file_project.width_midle != file_project.original_width) or (file_project.height_midle != file_project.original_height):
|
||||
if (cmd_line!=""):
|
||||
cmd_line+=",fifo,"
|
||||
x = int((file_project.width_midle - file_project.original_width) /2)
|
||||
y = int((file_project.height_midle - file_project.original_height) /2)
|
||||
if (x > 0) or (y > 0):
|
||||
cmd_line+="pad="+str(file_project.width_midle)+":"+str(file_project.height_midle)+":"+str(x)+":"+str(y)+":0x000000"
|
||||
else:
|
||||
cmd_line+="crop="+str(file_project.width_midle)+":"+str(file_project.height_midle)+":"+str(x)+":"+str(y)
|
||||
|
||||
if (file_project.width_final != file_project.width_midle) or (file_project.height_final != file_project.height_midle):
|
||||
if (cmd_line!=""):
|
||||
cmd_line+=",fifo,"
|
||||
cmd_line+="scale="+str(file_project.width_final)+":"+str(file_project.height_final)
|
||||
|
||||
if cmd_line!="":
|
||||
self.command_var.append("-vf")
|
||||
self.command_var.append(cmd_line)
|
||||
|
||||
|
||||
self.command_var.append("-y")
|
||||
|
||||
vcd=False
|
||||
|
||||
if (self.config.disc_type == "divx"):
|
||||
self.command_var.append("-vcodec")
|
||||
self.command_var.append("mpeg4")
|
||||
self.command_var.append("-acodec")
|
||||
self.command_var.append("libmp3lame")
|
||||
self.command_var.append("-f")
|
||||
self.command_var.append("avi")
|
||||
elif (self.config.disc_type == "mkv"):
|
||||
self.command_var.append("-vcodec")
|
||||
self.command_var.append("h264")
|
||||
self.command_var.append("-acodec")
|
||||
self.command_var.append("libmp3lame")
|
||||
self.command_var.append("-f")
|
||||
self.command_var.append("matroska")
|
||||
else:
|
||||
self.command_var.append("-target")
|
||||
if (self.config.disc_type=="dvd"):
|
||||
if not file_project.format_pal:
|
||||
self.command_var.append("ntsc-dvd")
|
||||
elif (file_project.original_fps==24):
|
||||
self.command_var.append("film-dvd")
|
||||
else:
|
||||
self.command_var.append("pal-dvd")
|
||||
if (not file_project.copy_sound):
|
||||
if file_project.sound5_1:
|
||||
self.command_var.append("-acodec")
|
||||
self.command_var.append("ac3")
|
||||
elif (self.config.disc_type=="vcd"):
|
||||
vcd=True
|
||||
if not file_project.format_pal:
|
||||
self.command_var.append("ntsc-vcd")
|
||||
else:
|
||||
self.command_var.append("pal-vcd")
|
||||
elif (self.config.disc_type=="svcd"):
|
||||
if not file_project.format_pal:
|
||||
self.command_var.append("ntsc-svcd")
|
||||
else:
|
||||
self.command_var.append("pal-svcd")
|
||||
elif (self.config.disc_type=="cvd"):
|
||||
if not file_project.format_pal:
|
||||
self.command_var.append("ntsc-svcd")
|
||||
else:
|
||||
self.command_var.append("pal-svcd")
|
||||
|
||||
if (not file_project.no_reencode_audio_video):
|
||||
self.command_var.append("-sn") # no subtitles
|
||||
|
||||
if file_project.copy_sound or file_project.no_reencode_audio_video:
|
||||
self.command_var.append("-acodec")
|
||||
self.command_var.append("copy")
|
||||
|
||||
if file_project.no_reencode_audio_video:
|
||||
self.command_var.append("-vcodec")
|
||||
self.command_var.append("copy")
|
||||
|
||||
if (vcd==False):
|
||||
if not file_project.format_pal:
|
||||
if (file_project.original_fps==24) and ((self.config.disc_type=="dvd")):
|
||||
keyintv=15
|
||||
else:
|
||||
keyintv=18
|
||||
else:
|
||||
keyintv=15
|
||||
|
||||
if not file_project.gop12:
|
||||
self.command_var.append("-g")
|
||||
self.command_var.append(str(keyintv))
|
||||
|
||||
if (self.config.disc_type=="divx") or (self.config.disc_type=="mkv"):
|
||||
self.command_var.append("-g")
|
||||
self.command_var.append("300")
|
||||
elif file_project.gop12 and (file_project.no_reencode_audio_video==False):
|
||||
self.command_var.append("-g")
|
||||
self.command_var.append("12")
|
||||
|
||||
self.command_var.append("-bf")
|
||||
self.command_var.append("2")
|
||||
self.command_var.append("-strict")
|
||||
self.command_var.append("1")
|
||||
|
||||
if video_length != 0:
|
||||
self.command_var.append("-t")
|
||||
self.command_var.append(str(video_length))
|
||||
|
||||
self.command_var.append("-ac")
|
||||
if (file_project.sound5_1) and ((self.config.disc_type=="dvd") or (self.config.disc_type=="divx") or (self.config.disc_type=="mkv")):
|
||||
self.command_var.append("6")
|
||||
else:
|
||||
self.command_var.append("2")
|
||||
|
||||
self.command_var.append("-aspect")
|
||||
self.command_var.append(str(file_project.aspect_ratio_final))
|
||||
|
||||
if self.config.disc_type=="divx":
|
||||
self.command_var.append("-vtag")
|
||||
self.command_var.append("DX50")
|
||||
|
||||
if (file_project.deinterlace == "deinterlace_ffmpeg") and (file_project.no_reencode_audio_video==False) and second_pass:
|
||||
self.command_var.append("-deinterlace")
|
||||
|
||||
if (file_project.no_reencode_audio_video==False) and (vcd==False) and second_pass:
|
||||
self.command_var.append("-s")
|
||||
self.command_var.append(str(file_project.width_final)+"x"+str(file_project.height_final))
|
||||
|
||||
if second_pass:
|
||||
self.command_var.append("-trellis")
|
||||
self.command_var.append("1")
|
||||
self.command_var.append("-mbd")
|
||||
self.command_var.append("2")
|
||||
else:
|
||||
self.command_var.append("-trellis")
|
||||
self.command_var.append("0")
|
||||
self.command_var.append("-mbd")
|
||||
self.command_var.append("0")
|
||||
|
||||
if (vcd == False) and (file_project.no_reencode_audio_video == False):
|
||||
self.command_var.append("-b:a")
|
||||
self.command_var.append(str(file_project.audio_rate_final)+"k")
|
||||
|
||||
self.command_var.append("-b:v")
|
||||
self.command_var.append(str(file_project.video_rate_final)+"k")
|
||||
|
||||
if file_project.two_pass_encoding == True:
|
||||
self.command_var.append("-passlogfile")
|
||||
self.command_var.append(output_file)
|
||||
self.command_var.append("-pass")
|
||||
if pass2:
|
||||
self.command_var.append("2")
|
||||
else:
|
||||
self.command_var.append("1")
|
||||
|
||||
self.command_var.append(output_file)
|
||||
|
||||
|
||||
def create_menu_mpeg(self,n_page,background_music,sound_length,pal,video_rate, audio_rate,output_path, use_mp2):
|
||||
|
||||
self.n_page = n_page
|
||||
self.final_length = float(sound_length)
|
||||
self.text = _("Creating menu %(X)d") % {"X": self.n_page}
|
||||
|
||||
self.command_var=[]
|
||||
self.command_var.append("avconv")
|
||||
|
||||
self.command_var.append("-loop")
|
||||
self.command_var.append("1")
|
||||
|
||||
self.command_var.append("-f")
|
||||
self.command_var.append("image2")
|
||||
self.command_var.append("-i")
|
||||
self.command_var.append(os.path.join(output_path,"menu_"+str(n_page)+"_bg.png"))
|
||||
self.command_var.append("-i")
|
||||
self.command_var.append(background_music)
|
||||
|
||||
self.command_var.append("-y")
|
||||
self.command_var.append("-target")
|
||||
if pal:
|
||||
self.command_var.append("pal-dvd")
|
||||
else:
|
||||
self.command_var.append("ntsc-dvd")
|
||||
self.command_var.append("-acodec")
|
||||
if (use_mp2):
|
||||
self.command_var.append("mp2")
|
||||
else:
|
||||
self.command_var.append("ac3")
|
||||
self.command_var.append("-s")
|
||||
if pal:
|
||||
self.command_var.append("720x576")
|
||||
else:
|
||||
self.command_var.append("720x480")
|
||||
self.command_var.append("-g")
|
||||
self.command_var.append("12")
|
||||
self.command_var.append("-b:v")
|
||||
self.command_var.append(str(video_rate)+"k")
|
||||
self.command_var.append("-b:a")
|
||||
self.command_var.append(str(audio_rate)+"k")
|
||||
self.command_var.append("-aspect")
|
||||
self.command_var.append("4:3")
|
||||
|
||||
self.command_var.append("-t")
|
||||
self.command_var.append(str(1+sound_length))
|
||||
|
||||
movie_path = os.path.join(output_path,"menu_"+str(n_page)+".mpg")
|
||||
self.command_var.append(movie_path)
|
||||
|
||||
muxer = devedeng.mux_dvd_menu.mux_dvd_menu()
|
||||
final_path = muxer.create_mpg(n_page,output_path,movie_path)
|
||||
# the muxer process depends of the converter process
|
||||
muxer.add_dependency(self)
|
||||
self.add_child_process(muxer)
|
||||
|
||||
return (final_path)
|
||||
|
||||
def process_stdout(self,data):
|
||||
|
||||
return
|
||||
|
||||
def process_stderr(self,data):
|
||||
|
||||
pos = data[0].find("time=")
|
||||
if (pos != -1):
|
||||
pos+=5
|
||||
pos2 = data[0].find(" ",pos)
|
||||
if (pos2 != -1):
|
||||
parts = data[0][pos:pos2].split(":")
|
||||
t = 0.0
|
||||
for e in parts:
|
||||
t *= 60.0
|
||||
t += float(e)
|
||||
t /= self.final_length
|
||||
self.progress_bar[1].set_fraction(t)
|
||||
self.progress_bar[1].set_text("%.1f%%" % (100.0 * t))
|
||||
147
src/devedeng/avprobe.py
Normal file
147
src/devedeng/avprobe.py
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
import subprocess
|
||||
import devedeng.configuration_data
|
||||
import devedeng.executor
|
||||
import os
|
||||
import json
|
||||
|
||||
class avprobe(devedeng.executor.executor):
|
||||
|
||||
supports_analize = True
|
||||
supports_play = False
|
||||
supports_convert = False
|
||||
supports_menu = False
|
||||
supports_mkiso = False
|
||||
supports_burn = False
|
||||
display_name = "AVPROBE"
|
||||
|
||||
@staticmethod
|
||||
def check_is_installed():
|
||||
try:
|
||||
handle = subprocess.Popen(["avprobe","-h"], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
|
||||
(stdout, stderr) = handle.communicate()
|
||||
if 0==handle.wait():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def __init__(self):
|
||||
|
||||
devedeng.executor.executor.__init__(self)
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
def process_stdout(self,data):
|
||||
return
|
||||
|
||||
def process_stderr(self,data):
|
||||
return
|
||||
|
||||
def get_film_data(self, file_name):
|
||||
""" processes a file, refered by the FILE_MOVIE movie object, and fills its
|
||||
main data (resolution, FPS, length...) """
|
||||
|
||||
self.audio_list=[]
|
||||
self.audio_streams = 0
|
||||
self.video_list=[]
|
||||
self.video_streams = 0
|
||||
self.original_width = 0
|
||||
self.original_height = 0
|
||||
self.original_length = -1
|
||||
self.original_videorate = 0
|
||||
self.original_audiorate = 0
|
||||
self.original_audiorate_uncompressed = 0
|
||||
self.original_fps = 0
|
||||
self.original_aspect_ratio = 0
|
||||
|
||||
self.original_file_size = os.path.getsize(file_name)
|
||||
|
||||
command_line = ["avprobe",file_name,"-of","json","-show_streams", "-loglevel", "quiet"]
|
||||
|
||||
(stdout, stderr) = self.launch_process(command_line, False)
|
||||
try:
|
||||
stdout2 = stdout.decode("utf-8")
|
||||
except:
|
||||
stdout2 = stdout.decode("latin1")
|
||||
|
||||
try:
|
||||
video_data = json.loads(stdout2)
|
||||
except:
|
||||
return True # There was an error reading the JSON data
|
||||
|
||||
if not("streams" in video_data):
|
||||
return True # There are no streams!!!!!
|
||||
|
||||
for element in video_data["streams"]:
|
||||
|
||||
if (self.original_length == -1) and ("duration" in element):
|
||||
try:
|
||||
self.original_length = int(float(element["duration"]))
|
||||
except:
|
||||
self.original_length = -1
|
||||
|
||||
if (element["codec_type"]=="video"):
|
||||
self.video_streams += 1
|
||||
self.video_list.append(element["index"])
|
||||
if (self.video_streams == 1):
|
||||
self.original_width = int(float(element["width"]))
|
||||
self.original_height = int(float(element["height"]))
|
||||
if ("bit_rate" in element):
|
||||
self.original_videorate = int(float(element["bit_rate"]))/1000
|
||||
self.original_fps = self.get_division(element["avg_frame_rate"])
|
||||
if ("display_aspect_ratio" in element):
|
||||
self.original_aspect_ratio = self.get_division(element["display_aspect_ratio"])
|
||||
|
||||
elif (element["codec_type"]=="audio"):
|
||||
self.audio_streams += 1
|
||||
self.audio_list.append(element["index"])
|
||||
if (self.audio_streams == 1):
|
||||
if ("bit_rate" in element):
|
||||
self.original_audiorate = int(float(element["bit_rate"]))/1000
|
||||
self.original_audiorate_uncompressed = int(float(element["sample_rate"]))
|
||||
|
||||
self.original_size = str(self.original_width)+"x"+str(self.original_height)
|
||||
if (self.original_aspect_ratio == None) or (self.original_aspect_ratio <= 1.0):
|
||||
if (self.original_height != 0):
|
||||
self.original_aspect_ratio = (float(self.original_width))/(float(self.original_height))
|
||||
|
||||
if (self.original_aspect_ratio != None):
|
||||
self.original_aspect_ratio = (float(int(self.original_aspect_ratio*1000.0)))/1000.0
|
||||
|
||||
if (len(self.video_list) == 0):
|
||||
return True # the file is not a video file; maybe an audio-only file or another thing
|
||||
|
||||
if self.original_length == -1: # if it was unable to detect the duration, try to use the human readable format
|
||||
command_line = ["avprobe",file_name]
|
||||
(stdout, stderr) = self.launch_process(command_line, False)
|
||||
try:
|
||||
stdout2 = stdout.decode("utf-8") + "\n" + stderr.decode("utf-8")
|
||||
except:
|
||||
stdout2 = stdout.decode("latin1") + "\n" + stderr.decode("latin1")
|
||||
|
||||
for line in stdout2.split("\n"):
|
||||
line = line.strip()
|
||||
if line.startswith("Duration: "):
|
||||
self.original_length = self.get_time(line[10:])
|
||||
break
|
||||
|
||||
return False # no error
|
||||
57
src/devedeng/brasero.py
Normal file
57
src/devedeng/brasero.py
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
import subprocess
|
||||
import devedeng.configuration_data
|
||||
import devedeng.executor
|
||||
|
||||
class brasero(devedeng.executor.executor):
|
||||
|
||||
supports_analize = False
|
||||
supports_play = False
|
||||
supports_convert = False
|
||||
supports_menu = False
|
||||
supports_mkiso = False
|
||||
supports_burn = True
|
||||
display_name = "BRASERO"
|
||||
|
||||
@staticmethod
|
||||
def check_is_installed():
|
||||
try:
|
||||
handle = subprocess.Popen(["brasero","-h"], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
|
||||
(stdout, stderr) = handle.communicate()
|
||||
if 0==handle.wait():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def __init__(self):
|
||||
|
||||
devedeng.executor.executor.__init__(self)
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
def burn(self,file_name):
|
||||
|
||||
self.command_var = ["brasero", file_name]
|
||||
|
||||
def process_stdout(self,data):
|
||||
return
|
||||
|
||||
def process_stderr(self,data):
|
||||
return
|
||||
216
src/devedeng/choose_disc_type.py
Normal file
216
src/devedeng/choose_disc_type.py
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
from gi.repository import Gtk,GObject
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import devedeng.configuration_data
|
||||
|
||||
class choose_disc_type(GObject.GObject):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(self.config.gettext_domain)
|
||||
|
||||
builder.add_from_file(os.path.join(self.config.glade,"wselect_disk.ui"))
|
||||
builder.connect_signals(self)
|
||||
self.wask_window = builder.get_object("wselect_disk")
|
||||
|
||||
self.cv = devedeng.converter.converter.get_converter()
|
||||
dvd = True
|
||||
vcd = True
|
||||
cvd = True
|
||||
svcd = True
|
||||
divx = True
|
||||
mkv = True
|
||||
analizers, players, converters, menuers, burners, mkiso = self.cv.get_needed_programs()
|
||||
|
||||
if (analizers != None) or (converters != None):
|
||||
dvd = False
|
||||
vcd = False
|
||||
cvd = False
|
||||
svcd = False
|
||||
divx = False
|
||||
mkv = False
|
||||
|
||||
if menuers != None:
|
||||
dvd = False
|
||||
|
||||
if mkiso != None:
|
||||
dvd = False
|
||||
|
||||
if self.check_program(["dvdauthor","--help"]) == False:
|
||||
dvd = False
|
||||
if self.check_program(["vcdimager","--help"]) == False:
|
||||
vcd = False
|
||||
svcd = False
|
||||
cvd = False
|
||||
if self.check_program(["spumux" , "--help"]) == False:
|
||||
dvd = False
|
||||
vcd = False
|
||||
svcd = False
|
||||
cvd = False
|
||||
|
||||
if self.cv.discs.count("dvd") == 0:
|
||||
dvd = False
|
||||
if self.cv.discs.count("vcd") == 0:
|
||||
vcd = False
|
||||
if self.cv.discs.count("svcd") == 0:
|
||||
svcd = False
|
||||
if self.cv.discs.count("cvd") == 0:
|
||||
cvd = False
|
||||
if self.cv.discs.count("divx") == 0:
|
||||
divx = False
|
||||
if self.cv.discs.count("mkv") == 0:
|
||||
mkv = False
|
||||
|
||||
builder.get_object("button_dvd").set_sensitive(dvd)
|
||||
builder.get_object("button_vcd").set_sensitive(vcd)
|
||||
builder.get_object("button_svcd").set_sensitive(svcd)
|
||||
builder.get_object("button_cvd").set_sensitive(cvd)
|
||||
builder.get_object("button_divx").set_sensitive(divx)
|
||||
builder.get_object("button_mkv").set_sensitive(mkv)
|
||||
|
||||
self.wask_window.show_all()
|
||||
|
||||
def check_program(self,command_line):
|
||||
try:
|
||||
handle = subprocess.Popen(command_line, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
|
||||
(stdout, stderr) = handle.communicate()
|
||||
retval = handle.wait()
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def set_type(self,disc_type):
|
||||
|
||||
self.config.set_disc_type(disc_type)
|
||||
self.wask_window.hide()
|
||||
self.wask_window.destroy()
|
||||
self.wask_window = None
|
||||
|
||||
def on_button_dvd_clicked(self,b):
|
||||
|
||||
self.set_type("dvd")
|
||||
|
||||
def on_button_vcd_clicked(self,b):
|
||||
|
||||
self.set_type("vcd")
|
||||
|
||||
def on_button_svcd_clicked(self,b):
|
||||
|
||||
self.set_type("svcd")
|
||||
|
||||
def on_button_cvd_clicked(self,b):
|
||||
|
||||
self.set_type("cvd")
|
||||
|
||||
def on_button_divx_clicked(self,b):
|
||||
|
||||
self.set_type("divx")
|
||||
|
||||
def on_button_mkv_clicked(self,b):
|
||||
|
||||
self.set_type("mkv")
|
||||
|
||||
def on_programs_needed_clicked(self,b):
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(self.config.gettext_domain)
|
||||
builder.add_from_file(os.path.join(self.config.glade,"wneeded.ui"))
|
||||
window = builder.get_object("needed")
|
||||
textbuf = builder.get_object("textbuffer")
|
||||
|
||||
window.show_all()
|
||||
|
||||
analizers, players, menuers, converters, burners, mkiso = self.cv.get_supported_programs()
|
||||
analizers_i, players_i, menuers_i, converters_i, burners_i, mkiso_i = self.cv.get_available_programs()
|
||||
|
||||
text = ""
|
||||
for e in analizers:
|
||||
if analizers_i.count(e.display_name) == 0:
|
||||
text += _("\t%(program_name)s (not installed)\n") % {"program_name": e.display_name}
|
||||
else:
|
||||
text += _("\t%(program_name)s (installed)\n") % {"program_name": e.display_name}
|
||||
text1 = _("Movie identifiers (install at least one of these):\n\n%(program_list)s\n") % {"program_list" : text}
|
||||
|
||||
text = ""
|
||||
for e in players:
|
||||
if players_i.count(e.display_name) == 0:
|
||||
text += _("\t%(program_name)s (not installed)\n") % {"program_name": e.display_name}
|
||||
else:
|
||||
text += _("\t%(program_name)s (installed)\n") % {"program_name": e.display_name}
|
||||
text2 = _("Movie players (install at least one of these):\n\n%(program_list)s\n") % {"program_list" : text}
|
||||
|
||||
text = ""
|
||||
for e in converters:
|
||||
sup = ""
|
||||
for s in e.disc_types:
|
||||
if sup != "":
|
||||
sup += ", "
|
||||
sup += s
|
||||
if converters_i.count(e.display_name) == 0:
|
||||
text += _("\t%(program_name)s (not installed)\n") % {"program_name": e.display_name + " ("+sup+")"}
|
||||
else:
|
||||
text += _("\t%(program_name)s (installed)\n") % {"program_name": e.display_name + " ("+sup+")"}
|
||||
text3 = _("Movie Converters (install at least one of these):\n\n%(program_list)s\n") % {"program_list" : text}
|
||||
|
||||
text = ""
|
||||
for e in burners:
|
||||
if burners_i.count(e.display_name) == 0:
|
||||
text += _("\t%(program_name)s (not installed)\n") % {"program_name": e.display_name}
|
||||
else:
|
||||
text += _("\t%(program_name)s (installed)\n") % {"program_name": e.display_name}
|
||||
text4 = _("CD/DVD burners (install at least one of these):\n\n%(program_list)s\n") % {"program_list" : text}
|
||||
|
||||
text = ""
|
||||
for e in mkiso:
|
||||
if mkiso_i.count(e.display_name) == 0:
|
||||
text += _("\t%(program_name)s (not installed)\n") % {"program_name": e.display_name}
|
||||
else:
|
||||
text += _("\t%(program_name)s (installed)\n") % {"program_name": e.display_name}
|
||||
text5 = _("ISO creators (install at least one of these):\n\n%(program_list)s\n") % {"program_list" : text}
|
||||
|
||||
text = ""
|
||||
if self.check_program(["dvdauthor","--help"]) == False:
|
||||
text += _("\t%(program_name)s (not installed)\n") % {"program_name": "DVDAUTHOR (dvd)"}
|
||||
else:
|
||||
text += _("\t%(program_name)s (installed)\n") % {"program_name": "DVDAUTHOR (dvd)"}
|
||||
if self.check_program(["vcdimager","--help"]) == False:
|
||||
text += _("\t%(program_name)s (not installed)\n") % {"program_name": "VCDIMAGER (vcd, svcd, cvd)"}
|
||||
else:
|
||||
text += _("\t%(program_name)s (installed)\n") % {"program_name": "VCDIMAGER (vcd, svcd, cvd)"}
|
||||
if self.check_program(["spumux" , "--help"]) == False:
|
||||
text += _("\t%(program_name)s (not installed)\n") % {"program_name": "SPUMUX (dvd, vcd, svcd, cvd)"}
|
||||
else:
|
||||
text += _("\t%(program_name)s (installed)\n") % {"program_name": "SPUMUX (dvd, vcd, svcd, cvd)"}
|
||||
|
||||
text6 = _("Other programs:\n\n%(program_list)s\n") % {"program_list" : text}
|
||||
|
||||
final_text = text1+text2+text3+text4+text5+text6
|
||||
textbuf.insert_at_cursor(final_text,len(final_text))
|
||||
window.run()
|
||||
window.destroy()
|
||||
|
||||
|
||||
def on_wselect_disk_destroy_event(self,w,b):
|
||||
|
||||
Gtk.main_quit()
|
||||
236
src/devedeng/configuration_data.py
Normal file
236
src/devedeng/configuration_data.py
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
from gi.repository import GObject
|
||||
import os
|
||||
|
||||
class configuration(GObject.GObject):
|
||||
|
||||
current_configuration = None
|
||||
__gsignals__ = {'disc_type': (GObject.SIGNAL_RUN_FIRST, None,(str,))}
|
||||
|
||||
@staticmethod
|
||||
def get_config():
|
||||
if configuration.current_configuration == None:
|
||||
configuration.current_configuration = configuration()
|
||||
if (configuration.current_configuration.fill_config()):
|
||||
configuration.current_configuration = None
|
||||
return configuration.current_configuration
|
||||
|
||||
def __init__(self):
|
||||
GObject.GObject.__init__(self)
|
||||
self.version = "0.1 Beta 10"
|
||||
|
||||
def fill_config(self):
|
||||
|
||||
self.cores = 0
|
||||
proc_file = open("/proc/cpuinfo","r")
|
||||
for line in proc_file:
|
||||
if (line.startswith("processor")):
|
||||
self.cores += 1
|
||||
|
||||
is_local = None
|
||||
self.log = ""
|
||||
self.disc_type = None
|
||||
|
||||
try:
|
||||
os.stat("/usr/share/devede_ng/wselect_disk.ui")
|
||||
is_local = False
|
||||
except:
|
||||
pass
|
||||
|
||||
if is_local == None:
|
||||
try:
|
||||
os.stat("/usr/local/share/devede_ng/wselect_disk.ui")
|
||||
is_local = True
|
||||
except:
|
||||
pass
|
||||
|
||||
if is_local == None:
|
||||
return True
|
||||
else:
|
||||
if (is_local):
|
||||
# locales must be always at /usr/share/locale because Gtk.Builder always search there
|
||||
self.share_locale="/usr/share/locale"
|
||||
self.glade="/usr/local/share/devede_ng"
|
||||
self.font_path="/usr/local/share/devede_ng"
|
||||
self.pic_path="/usr/local/share/devede_ng"
|
||||
self.other_path="/usr/local/share/devede_ng"
|
||||
self.help_path="/usr/local/share/doc/devede_ng"
|
||||
else:
|
||||
self.share_locale="/usr/share/locale"
|
||||
self.glade="/usr/share/devede_ng"
|
||||
self.font_path="/usr/share/devede_ng"
|
||||
self.pic_path="/usr/share/devede_ng"
|
||||
self.other_path="/usr/share/devede_ng"
|
||||
self.help_path="/usr/share/doc/devede_ng"
|
||||
|
||||
self.gettext_domain = "devede_ng"
|
||||
|
||||
self.PAL = True
|
||||
self.tmp_folder = "/var/tmp"
|
||||
self.multicore = self.cores
|
||||
self.final_folder = os.environ.get("HOME")
|
||||
self.sub_language = None
|
||||
self.sub_codepage = None
|
||||
self.film_analizer = None
|
||||
self.film_player = None
|
||||
self.film_converter = None
|
||||
self.menu_converter = None
|
||||
self.subtitles_font_size = 28
|
||||
self.sub_language = None
|
||||
self.sub_codepage = None
|
||||
self.burner = None
|
||||
self.mkiso = None
|
||||
self.subt_fill_color = (1,1,1,1)
|
||||
self.subt_outline_color = (0,0,0,1)
|
||||
self.subt_outline_thickness = 0.0
|
||||
|
||||
config_path = os.path.join(os.environ.get("HOME"),".devedeng")
|
||||
try:
|
||||
config_data = open(config_path,"r")
|
||||
for linea in config_data:
|
||||
linea = linea.strip()
|
||||
if linea == "":
|
||||
continue
|
||||
if linea[0] == "#":
|
||||
continue
|
||||
if linea[:13]=="video_format:":
|
||||
if linea[13:].strip() =="pal":
|
||||
self.PAL=True
|
||||
elif linea[13:].strip()=="ntsc":
|
||||
self.PAL=False
|
||||
continue
|
||||
if linea[:12]=="temp_folder:":
|
||||
self.tmp_folder=linea[12:].strip()
|
||||
continue
|
||||
if linea[:10]=="multicore:":
|
||||
self.multicore = int(linea[10:].strip())
|
||||
continue
|
||||
if linea[:13]=="final_folder:":
|
||||
self.final_folder=linea[13:].strip()
|
||||
continue
|
||||
if linea[:13]=="sub_language:":
|
||||
self.sub_language=linea[13:].strip()
|
||||
continue
|
||||
if linea[:13]=="sub_codepage:":
|
||||
self.sub_codepage=linea[13:].strip()
|
||||
continue
|
||||
if linea[:14]=="film_analizer:":
|
||||
self.film_analizer = linea[14:].strip()
|
||||
continue
|
||||
if linea[:12]=="film_player:":
|
||||
self.film_player = linea[12:].strip()
|
||||
continue
|
||||
if linea[:15]=="film_converter:":
|
||||
self.film_converter = linea[15:].strip()
|
||||
continue
|
||||
if linea[:15]=="menu_converter:":
|
||||
self.menu_converter = linea[15:].strip()
|
||||
continue
|
||||
if linea[:7]=="burner:":
|
||||
self.burner = linea[7:].strip()
|
||||
continue
|
||||
if linea[:6]=="mkiso:":
|
||||
self.mkiso = linea[6:].strip()
|
||||
continue
|
||||
if linea[:19]=="subtitle_font_size:":
|
||||
self.subtitles_font_size = int(linea[19:].strip())
|
||||
continue
|
||||
if linea[:20] == "subtitle_fill_color:":
|
||||
c = linea[20:].strip().split(",")
|
||||
self.subt_fill_color = (float(c[0]), float(c[1]), float(c[2]), 1.0)
|
||||
if linea[:23] == "subtitle_outline_color:":
|
||||
c = linea[23:].strip().split(",")
|
||||
self.subt_outline_color = (float(c[0]), float(c[1]), float(c[2]), 1.0)
|
||||
if linea[:27] == "subtitle_outilne_thickness:":
|
||||
self.subt_outline_thickness = float(linea[27:].strip())
|
||||
config_data.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def set_disc_type(self,disc_type):
|
||||
|
||||
self.disc_type = disc_type
|
||||
self.emit('disc_type',disc_type)
|
||||
|
||||
|
||||
def save_config(self):
|
||||
|
||||
config_path = os.path.join(os.environ.get("HOME"),".devedeng")
|
||||
try:
|
||||
config_data = open(config_path,"w")
|
||||
config_data.write("video_format:")
|
||||
if (self.PAL):
|
||||
config_data.write("pal\n")
|
||||
else:
|
||||
config_data.write("ntsc\n")
|
||||
if (self.tmp_folder != None):
|
||||
config_data.write("temp_folder:"+str(self.tmp_folder)+"\n")
|
||||
config_data.write("multicore:"+str(self.multicore)+"\n")
|
||||
if (self.final_folder != None):
|
||||
config_data.write("final_folder:"+str(self.final_folder)+"\n")
|
||||
if (self.sub_language != None):
|
||||
config_data.write("sub_language:"+str(self.sub_language)+"\n")
|
||||
if (self.sub_codepage != None):
|
||||
config_data.write("sub_codepage:"+str(self.sub_codepage)+"\n")
|
||||
if (self.film_analizer != None):
|
||||
config_data.write("film_analizer:"+str(self.film_analizer)+"\n")
|
||||
if (self.film_player != None):
|
||||
config_data.write("film_player:"+str(self.film_player)+"\n")
|
||||
if (self.film_converter != None):
|
||||
config_data.write("film_converter:"+str(self.film_converter)+"\n")
|
||||
if (self.menu_converter != None):
|
||||
config_data.write("menu_converter:"+str(self.menu_converter)+"\n")
|
||||
if self.burner != None:
|
||||
config_data.write("burner:"+str(self.burner)+"\n")
|
||||
if self.mkiso != None:
|
||||
config_data.write("mkiso:"+str(self.mkiso)+"\n")
|
||||
if (self.sub_codepage != None):
|
||||
config_data.write("sub_codepage:"+str(self.sub_codepage)+"\n")
|
||||
if (self.sub_language != None):
|
||||
config_data.write("sub_language:"+str(self.sub_language)+"\n")
|
||||
config_data.write("subtitle_font_size:"+str(self.subtitles_font_size)+"\n")
|
||||
config_data.write("subtitle_fill_color:"+str(self.subt_fill_color[0])+","+str(self.subt_fill_color[1])+","+str(self.subt_fill_color[2])+"\n")
|
||||
config_data.write("subtitle_outline_color:"+str(self.subt_outline_color[0])+","+str(self.subt_outline_color[1])+","+str(self.subt_outline_color[2])+"\n")
|
||||
config_data.write("subtitle_outilne_thickness:"+str(self.subt_outline_thickness))
|
||||
|
||||
config_data.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def append_log(self,data,cr = True):
|
||||
|
||||
self.log+=data
|
||||
if (cr):
|
||||
self.log += "\n"
|
||||
|
||||
|
||||
def clear_log(self):
|
||||
|
||||
self.log = ""
|
||||
|
||||
|
||||
def get_log(self):
|
||||
|
||||
return self.log
|
||||
255
src/devedeng/converter.py
Normal file
255
src/devedeng/converter.py
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
import devedeng.configuration_data
|
||||
import devedeng.mplayer
|
||||
import devedeng.avconv
|
||||
import devedeng.avprobe
|
||||
import devedeng.ffmpeg
|
||||
import devedeng.ffprobe
|
||||
import devedeng.vlc
|
||||
import devedeng.brasero
|
||||
import devedeng.k3b
|
||||
import devedeng.mkisofs
|
||||
import devedeng.genisoimage
|
||||
import devedeng.mpv
|
||||
|
||||
class converter:
|
||||
|
||||
current_converter = None
|
||||
|
||||
@staticmethod
|
||||
def get_converter():
|
||||
if converter.current_converter == None:
|
||||
converter.current_converter = converter()
|
||||
return converter.current_converter
|
||||
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
# List of classes with conversion capabilities, in order of preference
|
||||
self.c = [devedeng.vlc.vlc, devedeng.mpv.mpv, devedeng.mplayer.mplayer, devedeng.ffmpeg.ffmpeg, devedeng.ffprobe.ffprobe, devedeng.avconv.avconv, devedeng.avprobe.avprobe,
|
||||
devedeng.brasero.brasero, devedeng.k3b.k3b, devedeng.mkisofs.mkisofs, devedeng.genisoimage.genisoimage]
|
||||
|
||||
self.analizers = {}
|
||||
self.default_analizer = None
|
||||
self.players = {}
|
||||
self.default_player = None
|
||||
self.converters = {}
|
||||
self.default_converter = None
|
||||
self.menuers = {}
|
||||
self.default_menuer = None
|
||||
self.mkiso = {}
|
||||
self.default_mkiso = None
|
||||
self.burners = {}
|
||||
self.default_burner = None
|
||||
self.discs = []
|
||||
|
||||
for element in self.c:
|
||||
if (element.check_is_installed() == False):
|
||||
continue
|
||||
name = element.display_name
|
||||
if (element.supports_analize):
|
||||
self.analizers[name] = element
|
||||
if (self.default_analizer == None):
|
||||
self.default_analizer = element
|
||||
if (element.supports_play):
|
||||
self.players[name] = element
|
||||
if (self.default_player == None):
|
||||
self.default_player = element
|
||||
if (element.supports_convert):
|
||||
self.converters[name] = element
|
||||
if (self.default_converter == None):
|
||||
self.default_converter = element
|
||||
for types in element.disc_types:
|
||||
if self.discs.count(types) == 0:
|
||||
self.discs.append(types)
|
||||
if (element.supports_menu):
|
||||
self.menuers[name] = element
|
||||
if (self.default_menuer == None):
|
||||
self.default_menuer = element
|
||||
if (element.supports_mkiso):
|
||||
self.mkiso[name] = element
|
||||
if (self.default_mkiso == None):
|
||||
self.default_mkiso = element
|
||||
if (element.supports_burn):
|
||||
self.burners[name] = element
|
||||
if (self.default_burner == None):
|
||||
self.default_burner = element
|
||||
|
||||
|
||||
def get_supported_programs(self):
|
||||
|
||||
analizers = []
|
||||
players = []
|
||||
converters = []
|
||||
menuers = []
|
||||
mkiso = []
|
||||
burners = []
|
||||
|
||||
for element in self.c:
|
||||
if (element.supports_analize):
|
||||
analizers.append(element)
|
||||
if (element.supports_play):
|
||||
players.append(element)
|
||||
if (element.supports_convert):
|
||||
converters.append(element)
|
||||
if (element.supports_menu):
|
||||
menuers.append(element)
|
||||
if (element.supports_mkiso):
|
||||
mkiso.append(element)
|
||||
if (element.supports_burn):
|
||||
burners.append(element)
|
||||
|
||||
return (analizers, players, menuers, converters, burners, mkiso)
|
||||
|
||||
|
||||
def get_available_programs(self):
|
||||
|
||||
players = []
|
||||
menuers = []
|
||||
converters = []
|
||||
analizers = []
|
||||
burners = []
|
||||
mkiso = []
|
||||
|
||||
for e in self.analizers:
|
||||
analizers.append(e)
|
||||
for e in self.players:
|
||||
players.append(e)
|
||||
for e in self.menuers:
|
||||
menuers.append(e)
|
||||
for e in self.converters:
|
||||
converters.append(e)
|
||||
for e in self.burners:
|
||||
burners.append(e)
|
||||
for e in self.mkiso:
|
||||
mkiso.append(e)
|
||||
|
||||
return (analizers, players, menuers, converters, burners, mkiso)
|
||||
|
||||
|
||||
def get_needed_programs(self):
|
||||
""" returns a tupla with six lists. When a list is NONE, there are installed in the system
|
||||
programs that covers the needs for that group; when not, it contains the programs valid
|
||||
to cover the needs for that group.
|
||||
The groups are, in this order: ANALIZERS, PLAYERS, CONVERTERS, MENUERS, BURNERS, MKISO
|
||||
(menuers are the programs that creates the mpeg files for menus) """
|
||||
|
||||
if (self.default_analizer != None):
|
||||
analizers = None
|
||||
else:
|
||||
analizers = []
|
||||
if (self.default_player != None):
|
||||
players = None
|
||||
else:
|
||||
players = []
|
||||
if (self.default_converter != None):
|
||||
converters = None
|
||||
else:
|
||||
converters = []
|
||||
if (self.default_menuer != None):
|
||||
menuers = None
|
||||
else:
|
||||
menuers = []
|
||||
if (self.default_burner != None):
|
||||
burners = None
|
||||
else:
|
||||
burners = []
|
||||
if (self.default_mkiso != None):
|
||||
mkiso = None
|
||||
else:
|
||||
mkiso = []
|
||||
|
||||
for element in self.c:
|
||||
e = element()
|
||||
name = e.display_name
|
||||
if (e.supports_analize) and (analizers != None):
|
||||
analizers.append(name)
|
||||
if (e.supports_play) and (players != None):
|
||||
players.append(name)
|
||||
if (e.supports_convert) and (converters != None):
|
||||
converters.append(name)
|
||||
if (e.supports_menu) and (menuers != None):
|
||||
menuers.append(name)
|
||||
if (e.supports_burn) and (burners != None):
|
||||
burners.append(name)
|
||||
if (e.supports_mkiso) and (mkiso != None):
|
||||
mkiso.append(name)
|
||||
|
||||
return ( analizers, players, converters, menuers, burners, mkiso )
|
||||
|
||||
def get_film_player(self):
|
||||
""" returns a class for the desired film player, or the most priviledged if the desired is not installed """
|
||||
|
||||
if (self.config.film_player == None) or (self.config.film_player not in self.players):
|
||||
return self.default_player
|
||||
else:
|
||||
return self.players[self.config.film_player]
|
||||
|
||||
def get_film_analizer(self):
|
||||
""" returns a class for the desired film analizer, or the most priviledged if the desired is not installed """
|
||||
|
||||
if (self.config.film_analizer == None) or (self.config.film_analizer not in self.analizers):
|
||||
return self.default_analizer
|
||||
else:
|
||||
return self.analizers[self.config.film_analizer]
|
||||
|
||||
def get_menu_converter(self):
|
||||
""" returns a class for the desired menu converter, or the most priviledged if the desired is not installed """
|
||||
|
||||
if (self.config.menu_converter == None) or (self.config.menu_converter not in self.menuers):
|
||||
return self.default_menuer
|
||||
else:
|
||||
return self.menuers[self.config.menu_converter]
|
||||
|
||||
def get_disc_converter(self):
|
||||
""" returns a class for the desired disc converter, or the most priviledged if the desired is not installed """
|
||||
|
||||
# if there is a film converter chosen by the user, and it is installed in the system
|
||||
if (self.config.film_converter != None) and (self.config.film_converter in self.converters):
|
||||
# and that converter supports the current disc type
|
||||
if self.converters[self.config.film_converter].disc_types.count(self.config.disc_type) != 0:
|
||||
# return that converter
|
||||
return self.converters[self.config.film_converter]
|
||||
# if not, return the first available converter that supports the current disc type
|
||||
for converter in self.converters:
|
||||
if self.converters[converter].disc_types.count(self.config.disc_type) != 0:
|
||||
return self.converters[converter]
|
||||
return None
|
||||
|
||||
def get_disc_converter_by_name(self,name):
|
||||
if name in self.converters:
|
||||
return self.converters[name]
|
||||
return None
|
||||
|
||||
def get_burner(self):
|
||||
""" returns a class for the desired burner, or the most priviledged if the desired is not installed """
|
||||
|
||||
if (self.config.burner == None) or (self.config.burner not in self.burners):
|
||||
return self.default_burner
|
||||
else:
|
||||
return self.burners[self.config.burner]
|
||||
|
||||
def get_mkiso(self):
|
||||
""" returns a class for the desired mkiso, or the most priviledged if the desired is not installed """
|
||||
|
||||
if (self.config.mkiso == None) or (self.config.mkiso not in self.mkiso):
|
||||
return self.default_mkiso
|
||||
else:
|
||||
return self.mkiso[self.config.mkiso]
|
||||
69
src/devedeng/create_disk_window.py
Normal file
69
src/devedeng/create_disk_window.py
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
from gi.repository import Gtk
|
||||
import os
|
||||
import devedeng.configuration_data
|
||||
|
||||
class create_disk_window:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
def run(self):
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(self.config.gettext_domain)
|
||||
|
||||
builder.add_from_file(os.path.join(self.config.glade,"wcreate.ui"))
|
||||
builder.connect_signals(self)
|
||||
wcreate_window = builder.get_object("dialog_create")
|
||||
self.wpath = builder.get_object("path")
|
||||
self.wname = builder.get_object("name")
|
||||
wshutdown = builder.get_object("shutdown")
|
||||
self.waccept = builder.get_object("accept")
|
||||
|
||||
self.wname.set_text("movie")
|
||||
self.wpath.set_current_folder(self.config.final_folder)
|
||||
|
||||
wcreate_window.show_all()
|
||||
self.on_iface_changed(None)
|
||||
retval = wcreate_window.run()
|
||||
self.name = self.wname.get_text()
|
||||
self.path = os.path.join(self.wpath.get_filename(),self.name)
|
||||
self.shutdown = wshutdown.get_active()
|
||||
|
||||
if (retval == 1):
|
||||
self.config.final_folder = self.wpath.get_filename()
|
||||
self.config.save_config()
|
||||
|
||||
wcreate_window.destroy()
|
||||
if (retval == 1):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def on_iface_changed(self,b):
|
||||
|
||||
path = self.wpath.get_filename()
|
||||
name = self.wname.get_text()
|
||||
|
||||
if ((path == None) or (path == "") or (name == None) or (name == "")):
|
||||
self.waccept.set_sensitive(False)
|
||||
else:
|
||||
self.waccept.set_sensitive(True)
|
||||
623
src/devedeng/dvd_menu.py
Normal file
623
src/devedeng/dvd_menu.py
Normal file
|
|
@ -0,0 +1,623 @@
|
|||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
import cairo
|
||||
from gi.repository import Gtk,Gdk,GdkPixbuf
|
||||
import os
|
||||
import devedeng.configuration_data
|
||||
import devedeng.interface_manager
|
||||
import devedeng.message
|
||||
import devedeng.mux_dvd_menu
|
||||
|
||||
class dvd_menu(devedeng.interface_manager.interface_manager):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.entry_vertical_margin = 2.0
|
||||
self.entry_separation = 2.0
|
||||
|
||||
devedeng.interface_manager.interface_manager.__init__(self)
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
self.default_background = os.path.join(self.config.pic_path,"backgrounds","default_bg.png")
|
||||
self.default_sound = os.path.join(self.config.pic_path,"silence.ogg")
|
||||
|
||||
self.add_colorbutton("title_color", (0,0,0,1), self.update_preview)
|
||||
self.add_colorbutton("title_shadow", (0,0,0,0), self.update_preview)
|
||||
self.add_colorbutton("unselected_color", (1,1,1,1), self.update_preview)
|
||||
self.add_colorbutton("shadow_color", (0,0,0,0), self.update_preview)
|
||||
self.add_colorbutton("selected_color", (0,1,1,1), self.update_preview)
|
||||
self.add_colorbutton("background_color", (0,0,0,0.75), self.update_preview)
|
||||
|
||||
self.add_text("title_text", None, self.update_preview)
|
||||
|
||||
self.add_group("position_horizontal", ["left", "center", "right"], "center", self.update_preview)
|
||||
self.add_group("at_startup", ["menu_show_at_startup", "play_first_title_at_startup"], "menu_show_at_startup")
|
||||
|
||||
self.add_integer_adjustment("sound_length", 30)
|
||||
|
||||
self.add_dualtoggle("audio_mp2", "audio_ac3", True)
|
||||
|
||||
self.add_float_adjustment("margin_left", 10.0, self.update_preview)
|
||||
self.add_float_adjustment("margin_top", 12.5, self.update_preview)
|
||||
self.add_float_adjustment("margin_right", 10.0, self.update_preview)
|
||||
self.add_float_adjustment("margin_bottom", 12.5, self.update_preview)
|
||||
self.add_float_adjustment("title_horizontal", 0.0, self.update_preview)
|
||||
self.add_float_adjustment("title_vertical", 10.0, self.update_preview)
|
||||
|
||||
self.add_fontbutton("title_font", "Sans 28", self.update_preview)
|
||||
self.add_fontbutton("entry_font", "Sans 28", self.update_preview)
|
||||
|
||||
self.add_filebutton("background_picture", self.default_background, self.update_preview)
|
||||
self.add_filebutton("background_music", self.default_sound, self.update_music)
|
||||
|
||||
self.cached_menu_font = None
|
||||
self.cached_menu_size = 0
|
||||
self.video_rate = 2500
|
||||
self.audio_rate = 224
|
||||
|
||||
|
||||
def get_estimated_size(self):
|
||||
|
||||
estimated_size = ((self.video_rate + self.audio_rate) * self.sound_length) / 8
|
||||
return estimated_size
|
||||
|
||||
|
||||
def update_music(self,b=None):
|
||||
|
||||
self.store_ui(self.builder)
|
||||
cv = devedeng.converter.converter.get_converter()
|
||||
film_analizer = (cv.get_film_analizer())()
|
||||
(video, audio, length) = film_analizer.analize_film_data(self.background_music,True)
|
||||
if (video != 0):
|
||||
devedeng.message.message_window(_("The selected file is a video, not an audio file"),_("Error"))
|
||||
self.on_no_sound_clicked(None)
|
||||
elif (audio == 0):
|
||||
devedeng.message.message_window(_("The selected file is not an audio file"),_("Error"))
|
||||
self.on_no_sound_clicked(None)
|
||||
else:
|
||||
self.sound_length = length
|
||||
|
||||
|
||||
def update_preview(self,b=None):
|
||||
|
||||
self.store_ui(self.builder)
|
||||
self.sf = None # force to repaint the menu
|
||||
self.wdrawing_area.queue_draw()
|
||||
|
||||
|
||||
def on_default_background_clicked(self,b):
|
||||
|
||||
self.background_picture = self.default_background
|
||||
self.update_ui(self.builder)
|
||||
self.update_preview()
|
||||
|
||||
|
||||
def on_no_sound_clicked(self,b):
|
||||
|
||||
self.background_music = self.default_sound
|
||||
self.sound_length = 30
|
||||
self.update_ui(self.builder)
|
||||
|
||||
|
||||
def show_configuration(self, file_list):
|
||||
|
||||
self.builder = Gtk.Builder()
|
||||
self.builder.set_translation_domain(self.config.gettext_domain)
|
||||
self.file_list = file_list
|
||||
|
||||
self.refresh_static_data()
|
||||
|
||||
self.builder.add_from_file(os.path.join(self.config.glade,"wmenu.ui"))
|
||||
self.builder.connect_signals(self)
|
||||
|
||||
self.wframe_preview = self.builder.get_object("frame_preview")
|
||||
self.wframe_preview_controls = self.builder.get_object("frame_preview_controls")
|
||||
self.wmenu = self.builder.get_object("menu")
|
||||
self.wpreview_menu = self.builder.get_object("preview_menu")
|
||||
self.wdrawing_area = self.builder.get_object("drawingarea_preview")
|
||||
self.wcurrent_page = self.builder.get_object("current_page")
|
||||
self.wshow_as_selected = self.builder.get_object("show_as_selected")
|
||||
self.wshow_as_selected.connect("toggled",self.update_preview)
|
||||
|
||||
self.wbackground_picture = self.builder.get_object("background_picture")
|
||||
self.wbackground_music = self.builder.get_object("background_music")
|
||||
|
||||
file_filter_pictures=Gtk.FileFilter()
|
||||
file_filter_pictures.set_name(_("Picture files"))
|
||||
|
||||
file_filter_pictures.add_mime_type("image/*")
|
||||
|
||||
file_filter_all=Gtk.FileFilter()
|
||||
file_filter_all.set_name(_("All files"))
|
||||
file_filter_all.add_pattern("*")
|
||||
|
||||
self.wbackground_picture.add_filter(file_filter_pictures)
|
||||
self.wbackground_picture.add_filter(file_filter_all)
|
||||
|
||||
file_filter_music=Gtk.FileFilter()
|
||||
file_filter_music.set_name(_("Sound files"))
|
||||
|
||||
file_filter_music.add_mime_type("audio/*")
|
||||
|
||||
file_filter_all=Gtk.FileFilter()
|
||||
file_filter_all.set_name(_("All files"))
|
||||
file_filter_all.add_pattern("*")
|
||||
|
||||
self.wbackground_music.add_filter(file_filter_music)
|
||||
self.wbackground_music.add_filter(file_filter_all)
|
||||
|
||||
self.wmenu.show_all()
|
||||
|
||||
self.pages = 0
|
||||
self.update_ui(self.builder)
|
||||
self.save_ui()
|
||||
|
||||
|
||||
def on_accept_clicked(self,b):
|
||||
|
||||
self.store_ui(self.builder)
|
||||
self.wmenu.destroy()
|
||||
|
||||
|
||||
def on_cancel_clicked(self,b):
|
||||
|
||||
self.restore_ui()
|
||||
self.wmenu.destroy()
|
||||
|
||||
|
||||
def get_font_params(self,font_name):
|
||||
|
||||
font_elements=font_name.split(" ")
|
||||
|
||||
if (len(font_elements))<2:
|
||||
fontname="Sans"
|
||||
fontstyle=cairo.FONT_WEIGHT_NORMAL
|
||||
fontslant=cairo.FONT_SLANT_NORMAL
|
||||
fontsize=12
|
||||
else:
|
||||
fontname=""
|
||||
fontstyle=cairo.FONT_WEIGHT_NORMAL
|
||||
fontslant=cairo.FONT_SLANT_NORMAL
|
||||
for counter2 in range(len(font_elements)-1):
|
||||
if font_elements[counter2]=="Bold":
|
||||
fontstyle=cairo.FONT_WEIGHT_BOLD
|
||||
elif font_elements[counter2]=="Italic":
|
||||
fontslant=cairo.FONT_SLANT_ITALIC
|
||||
else:
|
||||
fontname+=" "+font_elements[counter2]
|
||||
if fontname!="":
|
||||
fontname=fontname[1:]
|
||||
else:
|
||||
fontname="Sans"
|
||||
|
||||
try:
|
||||
fontsize=float(font_elements[-1])
|
||||
except:
|
||||
fontsize=12
|
||||
|
||||
return fontname,fontstyle,fontslant,fontsize
|
||||
|
||||
|
||||
def paint_arrow(self,xl,xr,y,arrow_type,left):
|
||||
|
||||
if arrow_type == "menu_entry":
|
||||
fgcolor = self.unselected_color
|
||||
elif arrow_type == "menu_entry_selected":
|
||||
fgcolor = self.selected_color
|
||||
elif arrow_type == "menu_entry_activated":
|
||||
fgcolor = ( 1.0 - self.selected_color[0], 1.0 - self.selected_color[1], 1.0 - self.selected_color[2], self.selected_color[3])
|
||||
else:
|
||||
return
|
||||
|
||||
fo=cairo.FontOptions()
|
||||
fo.set_antialias(cairo.ANTIALIAS_NONE)
|
||||
self.cr.set_font_options(fo)
|
||||
self.cr.set_antialias(cairo.ANTIALIAS_NONE)
|
||||
|
||||
x = (xl + xr) / 2.0
|
||||
h = (self.cached_menu_size / 2.0) - 1.0
|
||||
self.cr.set_source_rgba(fgcolor[0],fgcolor[1],fgcolor[2],fgcolor[3])
|
||||
self.cr.move_to(x,y-h)
|
||||
if (left):
|
||||
self.cr.line_to(x-self.cached_menu_size+2,y)
|
||||
else:
|
||||
self.cr.line_to(x+self.cached_menu_size-2,y)
|
||||
self.cr.line_to(x,y+h)
|
||||
self.cr.line_to(x,y-h)
|
||||
self.cr.fill()
|
||||
|
||||
fo=cairo.FontOptions()
|
||||
fo.set_antialias(cairo.ANTIALIAS_DEFAULT)
|
||||
self.cr.set_font_options(fo)
|
||||
self.cr.set_antialias(cairo.ANTIALIAS_DEFAULT)
|
||||
|
||||
|
||||
def write_text(self,text,text_type,xl,xr,y,alignment):
|
||||
""" Renders a line of text, in the rectangle delimited by xl,y-h;xr,y+h, with the specified alignment """
|
||||
|
||||
if text == None:
|
||||
return
|
||||
|
||||
if text_type == "title":
|
||||
fgcolor = self.title_color
|
||||
bgcolor = self.title_shadow
|
||||
hard_borders = False
|
||||
font = self.title_font
|
||||
elif text_type == "menu_entry":
|
||||
fgcolor = self.unselected_color
|
||||
bgcolor = self.shadow_color
|
||||
hard_borders = True
|
||||
font = self.entry_font
|
||||
elif text_type == "menu_entry_selected":
|
||||
fgcolor = self.selected_color
|
||||
bgcolor = None
|
||||
hard_borders = True
|
||||
font = self.entry_font
|
||||
elif text_type == "menu_entry_activated":
|
||||
fgcolor = ( 1.0 - self.selected_color[0], 1.0 - self.selected_color[1], 1.0 - self.selected_color[2], self.selected_color[3])
|
||||
bgcolor = None
|
||||
hard_borders = True
|
||||
font = self.entry_font
|
||||
else:
|
||||
return
|
||||
|
||||
if hard_borders:
|
||||
fo=cairo.FontOptions()
|
||||
fo.set_antialias(cairo.ANTIALIAS_NONE)
|
||||
self.cr.set_font_options(fo)
|
||||
self.cr.set_antialias(cairo.ANTIALIAS_NONE)
|
||||
|
||||
(fontname, fontstyle, fontslant, fontsize) = self.get_font_params(font)
|
||||
self.cr.select_font_face(fontname,fontslant, fontstyle)
|
||||
self.cr.set_font_size(fontsize)
|
||||
extents = self.cr.text_extents(text)
|
||||
|
||||
if alignment == "left":
|
||||
x = xl
|
||||
elif alignment == "right":
|
||||
x = xr - extents[2]
|
||||
elif alignment == "center":
|
||||
x = ((xl + xr) / 2.0) - (extents[2] / 2.0)
|
||||
|
||||
if (bgcolor != None):
|
||||
self.cr.move_to(x+extents[0]+2,y-(extents[3]/2.0)-extents[1]+2)
|
||||
self.cr.set_source_rgba(bgcolor[0],bgcolor[1],bgcolor[2],bgcolor[3])
|
||||
self.cr.show_text(text)
|
||||
|
||||
self.cr.move_to(x+extents[0],y-(extents[3]/2.0)-extents[1])
|
||||
self.cr.set_source_rgba(fgcolor[0],fgcolor[1],fgcolor[2],fgcolor[3])
|
||||
self.cr.show_text(text)
|
||||
|
||||
if hard_borders:
|
||||
fo=cairo.FontOptions()
|
||||
fo.set_antialias(cairo.ANTIALIAS_DEFAULT)
|
||||
self.cr.set_font_options(fo)
|
||||
self.cr.set_antialias(cairo.ANTIALIAS_DEFAULT)
|
||||
|
||||
|
||||
def refresh_static_data(self):
|
||||
""" This method sets data that can't be changed from the dvd settings window.
|
||||
It must be called before painting a menu """
|
||||
|
||||
if self.config.PAL:
|
||||
self.y=576.0
|
||||
else:
|
||||
self.y=480.0
|
||||
|
||||
self.title_list = []
|
||||
counter = 0
|
||||
for element in self.file_list:
|
||||
if element.show_in_menu:
|
||||
self.title_list.append( (element, counter) )
|
||||
counter += 1
|
||||
|
||||
self.sf = None
|
||||
self.current_shown_page = 0
|
||||
self.wcurrent_page = None
|
||||
|
||||
|
||||
def paint_menu(self,paint_background, paint_selected, paint_activated, page_number):
|
||||
|
||||
coordinates = []
|
||||
|
||||
if self.sf == None:
|
||||
self.sf=cairo.ImageSurface(cairo.FORMAT_ARGB32,720,int(self.y))
|
||||
self.cr=cairo.Context(self.sf)
|
||||
|
||||
if self.cached_menu_font != self.entry_font:
|
||||
# memorize the font sizes
|
||||
(fontname, fontstyle, fontslant, fontsize) = self.get_font_params(self.entry_font)
|
||||
self.cr.select_font_face(fontname,fontslant, fontstyle)
|
||||
self.cr.set_font_size(fontsize)
|
||||
extents = self.cr.font_extents()
|
||||
self.cached_menu_size = extents[2]
|
||||
self.cached_menu_font = self.entry_font
|
||||
|
||||
if paint_background:
|
||||
extra_pixbuf = GdkPixbuf.Pixbuf.new_from_file(self.background_picture)
|
||||
extra_x = float(extra_pixbuf.get_width())
|
||||
extra_y = float(extra_pixbuf.get_height())
|
||||
|
||||
self.cr.save()
|
||||
self.cr.scale(720.0/extra_x,self.y/extra_y)
|
||||
|
||||
surface = Gdk.cairo_surface_create_from_pixbuf(extra_pixbuf,1,None)
|
||||
self.cr.set_source_surface(surface,0,0)
|
||||
self.cr.paint()
|
||||
self.cr.restore()
|
||||
hmargin = self.title_horizontal * 720.0 / 100.0
|
||||
self.write_text(self.title_text, "title", 0 + hmargin, 720 + hmargin, self.title_vertical * self.y / 100.0, "center")
|
||||
# else:
|
||||
# self.cr.set_source_rgb(1.0,1.0,1.0)
|
||||
# self.cr.paint()
|
||||
|
||||
top_margin_p = self.y * self.margin_top / 100.0
|
||||
bottom_margin_p = self.y * self.margin_bottom / 100.0
|
||||
left_margin_p = 720.0 * self.margin_left / 100.0
|
||||
right_margin_p = 720.0 * self.margin_right / 100.0
|
||||
|
||||
entry_height = self.cached_menu_size + self.entry_vertical_margin * 2.0 + self.entry_separation
|
||||
entries_per_page = int((self.y - top_margin_p - bottom_margin_p) / entry_height)
|
||||
n_entries = len(self.title_list)
|
||||
if (n_entries > entries_per_page):
|
||||
paint_arrows = True
|
||||
entries_per_page -= 1
|
||||
else:
|
||||
paint_arrows = False
|
||||
|
||||
self.pages = int(n_entries / entries_per_page)
|
||||
if (n_entries == 0) or ((n_entries % entries_per_page) != 0):
|
||||
self.pages += 1
|
||||
if (page_number >= self.pages) and (page_number > 0):
|
||||
page_number -= 1
|
||||
|
||||
if self.wcurrent_page != None:
|
||||
self.wcurrent_page.set_text(_("Page %(X)d of %(Y)d") % {"X":page_number+1 , "Y":self.pages})
|
||||
xl = left_margin_p
|
||||
xr = 720.0 - right_margin_p
|
||||
y = top_margin_p + entry_height/2.0
|
||||
height = (self.cached_menu_size + self.entry_vertical_margin) / 2.0
|
||||
for entry in self.title_list[page_number*entries_per_page:(page_number+1)*entries_per_page]:
|
||||
coordinates.append([xl, y-height, xr, y+height, "entry"])
|
||||
text = entry[0].title_name
|
||||
if paint_background:
|
||||
self.paint_base(xl, xr, y, 0)
|
||||
self.write_text(text, "menu_entry", xl, xr, y, self.position_horizontal)
|
||||
if paint_selected:
|
||||
self.write_text(text, "menu_entry_selected", xl, xr, y, self.position_horizontal)
|
||||
if paint_activated:
|
||||
self.write_text(text, "menu_entry_activated", xl, xr, y, self.position_horizontal)
|
||||
y += entry_height
|
||||
|
||||
if paint_arrows:
|
||||
if page_number == 0:
|
||||
coordinates.append([xl, y-height, xr, y+height, "right"])
|
||||
if paint_background:
|
||||
self.paint_base(xl, xr, y, 0)
|
||||
self.paint_arrow(xl, xr, y, "menu_entry", False)
|
||||
if paint_selected:
|
||||
self.paint_arrow(xl, xr, y, "menu_entry_selected", False)
|
||||
if paint_activated:
|
||||
self.paint_arrow(xl, xr, y, "menu_entry_activated", False)
|
||||
elif page_number == (self.pages - 1):
|
||||
coordinates.append([xl, y-height, xr, y+height, "left"])
|
||||
if paint_background:
|
||||
self.paint_base(xl, xr, y, 0)
|
||||
self.paint_arrow(xl, xr, y, "menu_entry", True)
|
||||
if paint_selected:
|
||||
self.paint_arrow(xl, xr, y, "menu_entry_selected", True)
|
||||
if paint_activated:
|
||||
self.paint_arrow(xl, xr, y, "menu_entry_activated", True)
|
||||
else:
|
||||
med = (xl + xr) / 2.0
|
||||
coordinates.append([xl, y-height, med, y+height, "left"])
|
||||
coordinates.append([med, y-height, xr, y+height, "right"])
|
||||
if paint_background:
|
||||
self.paint_base(xl, xr, y, 1)
|
||||
self.paint_base(xl, xr, y, 2)
|
||||
self.paint_arrow(med, xr, y, "menu_entry", False)
|
||||
self.paint_arrow(xl, med, y, "menu_entry", True)
|
||||
if paint_selected:
|
||||
self.paint_arrow(med, xr, y, "menu_entry_selected", False)
|
||||
self.paint_arrow(xl, med, y, "menu_entry_selected", True)
|
||||
if paint_activated:
|
||||
self.paint_arrow(med, xr, y, "menu_entry_activated", False)
|
||||
self.paint_arrow(xl, med, y, "menu_entry_activated", True)
|
||||
return coordinates
|
||||
|
||||
|
||||
def paint_base(self,xl, xr, y, type_b):
|
||||
|
||||
height = self.cached_menu_size + self.entry_vertical_margin
|
||||
radius = (height) / 2.0
|
||||
y -= radius
|
||||
|
||||
if (type_b == 1): # half button, at left
|
||||
xr = (xl+xr) / 2.0
|
||||
xr -= radius
|
||||
elif (type_b == 2): # half button, at right
|
||||
xl = (xl+xr) / 2.0
|
||||
xl += radius
|
||||
|
||||
self.cr.set_source_rgba(self.background_color[0],self.background_color[1],self.background_color[2],self.background_color[3])
|
||||
self.cr.move_to(xl,y)
|
||||
self.cr.line_to(xr,y)
|
||||
self.cr.curve_to(xr+radius,y,xr+radius,y+height,xr,y+height)
|
||||
self.cr.line_to(xl,y+height)
|
||||
self.cr.curve_to(xl-radius,y+height,xl-radius,y,xl,y)
|
||||
self.cr.fill()
|
||||
|
||||
|
||||
def on_next_page_clicked(self,b):
|
||||
|
||||
self.current_shown_page += 1
|
||||
if (self.current_shown_page == self.pages):
|
||||
self.current_shown_page -= 1
|
||||
self.update_preview()
|
||||
|
||||
|
||||
def on_previous_page_clicked(self,b):
|
||||
|
||||
self.current_shown_page -= 1
|
||||
if (self.current_shown_page < 0):
|
||||
self.current_shown_page = 0
|
||||
self.update_preview()
|
||||
|
||||
|
||||
def on_drawingarea_preview_draw(self,widget,cr):
|
||||
|
||||
""" Callback to repaint the menu preview window when it
|
||||
sends the EXPOSE event """
|
||||
|
||||
if (self.sf == None):
|
||||
self.paint_menu(True, self.wshow_as_selected.get_active(), False, self.current_shown_page)
|
||||
if (self.current_shown_page >= self.pages):
|
||||
self.current_shown_page = self.pages
|
||||
|
||||
cr.set_source_surface(self.sf)
|
||||
cr.paint()
|
||||
|
||||
|
||||
def create_menu_stream(self,path,n_page,coordinates):
|
||||
|
||||
""" Creates the menu XML file """
|
||||
|
||||
file_name = os.path.join(path,"menu_"+str(n_page)+".xml")
|
||||
entry_data = {}
|
||||
entry_data["chapters"] = []
|
||||
entry_data["right"] = None
|
||||
entry_data["left"] = None
|
||||
|
||||
xml_file=open(file_name,"w")
|
||||
xml_file.write('<subpictures>\n\t<stream>\n\t\t<spu force="yes" start="00:00:00.00"')# transparent="000000"')
|
||||
xml_file.write(' image="'+os.path.join(path,"menu_"+str(n_page)+'_unselected_bg.png"'))
|
||||
xml_file.write(' highlight="'+os.path.join(path,"menu_"+str(n_page)+'_selected_bg.png"'))
|
||||
xml_file.write(' select="'+os.path.join(path,"menu_"+str(n_page)+'_active_bg.png"'))
|
||||
xml_file.write(' >\n')
|
||||
n_elements = 0
|
||||
has_next = False
|
||||
has_previous = False
|
||||
|
||||
for e in coordinates:
|
||||
if (e[4] == "entry"):
|
||||
n_elements += 1
|
||||
continue
|
||||
if (e[4] == "right"):
|
||||
has_next = True
|
||||
continue
|
||||
if (e[4] == "left"):
|
||||
has_previous = True
|
||||
continue
|
||||
|
||||
counter = 0
|
||||
for element in coordinates:
|
||||
xl = int(element[0])
|
||||
yt = int(element[1])
|
||||
xr = int(element[2])
|
||||
yb = int(element[3])
|
||||
if (xl % 2) == 1:
|
||||
xl += 1
|
||||
if (yt % 2) == 1:
|
||||
yt += 1
|
||||
if (xr % 2) == 1:
|
||||
xr -= 1
|
||||
if (yb % 2) == 1:
|
||||
yb -= 1
|
||||
|
||||
if (element[4] == "left"):
|
||||
entry_data["left"] = "boton"+str(n_page)+"x"+str(counter)
|
||||
elif (element[4] == "right"):
|
||||
entry_data["right"] = "boton"+str(n_page)+"x"+str(counter)
|
||||
else:
|
||||
entry_data["chapters"].append("boton"+str(n_page)+"x"+str(counter))
|
||||
|
||||
xml_file.write('\t\t\t<button name="boton'+str(n_page)+"x"+str(counter))
|
||||
xml_file.write('" x0="'+str(xl)+'" y0="'+str(yt)+'" x1="'+str(xr)+'" y1="'+str(yb)+'"')
|
||||
if counter > 0:
|
||||
xml_file.write(' up="boton'+str(n_page)+"x")
|
||||
if counter >= n_elements:
|
||||
xml_file.write(str(n_elements - 1))
|
||||
else:
|
||||
xml_file.write(str(counter-1))
|
||||
xml_file.write('"')
|
||||
|
||||
if (counter < (n_elements - 1)) or ((counter < n_elements) and (has_next or has_previous)):
|
||||
xml_file.write(' down="boton'+str(n_page)+"x")
|
||||
xml_file.write(str(counter+1))
|
||||
xml_file.write('"')
|
||||
if (element[4] == "left") and (has_next):
|
||||
xml_file.write(' right="boton'+str(n_page)+'x'+str(counter+1)+'"')
|
||||
if (element[4] == "right") and (has_previous):
|
||||
xml_file.write(' left="boton'+str(n_page)+'x'+str(counter-1)+'"')
|
||||
xml_file.write(' > </button>\n')
|
||||
counter += 1
|
||||
|
||||
xml_file.write("</spu>\n</stream>\n</subpictures>\n")
|
||||
xml_file.close()
|
||||
|
||||
return entry_data
|
||||
|
||||
|
||||
def create_dvd_menus(self, file_list, base_path):
|
||||
|
||||
self.file_list = file_list
|
||||
self.refresh_static_data()
|
||||
cv = devedeng.converter.converter.get_converter()
|
||||
menu_folder = os.path.join(base_path,"menu")
|
||||
try:
|
||||
os.makedirs(menu_folder)
|
||||
except:
|
||||
pass
|
||||
n_page = 0
|
||||
self.pages = 1
|
||||
processes = []
|
||||
menu_entries = []
|
||||
menu_converter = cv.get_menu_converter()
|
||||
while n_page < self.pages:
|
||||
self.sf = None
|
||||
coordinates = self.paint_menu(True, False, False, n_page)
|
||||
self.sf.write_to_png(os.path.join(menu_folder,"menu_"+str(n_page)+"_bg.png"))
|
||||
self.sf = None
|
||||
self.paint_menu(False, False, False, n_page)
|
||||
self.sf.write_to_png(os.path.join(menu_folder,"menu_"+str(n_page)+"_unselected_bg.png"))
|
||||
self.sf = None
|
||||
self.paint_menu(False, True, False, n_page)
|
||||
self.sf.write_to_png(os.path.join(menu_folder,"menu_"+str(n_page)+"_selected_bg.png"))
|
||||
self.sf = None
|
||||
self.paint_menu(False, False, True, n_page)
|
||||
self.sf.write_to_png(os.path.join(menu_folder,"menu_"+str(n_page)+"_active_bg.png"))
|
||||
entry_data = self.create_menu_stream(menu_folder, n_page, coordinates)
|
||||
converter = menu_converter()
|
||||
final_path = converter.create_menu_mpeg(n_page,self.background_music,self.sound_length,self.config.PAL,self.video_rate,self.audio_rate,menu_folder, self.audio_mp2)
|
||||
entry_data["filename"] = final_path
|
||||
menu_entries.append(entry_data)
|
||||
# add this process without dependencies
|
||||
processes.append(converter)
|
||||
n_page += 1
|
||||
|
||||
return processes,menu_entries
|
||||
|
||||
|
||||
def store_menu(self):
|
||||
|
||||
return self.serialize()
|
||||
|
||||
|
||||
def restore_menu(self,data):
|
||||
|
||||
self.unserialize(data)
|
||||
456
src/devedeng/dvdauthor_converter.py
Normal file
456
src/devedeng/dvdauthor_converter.py
Normal file
|
|
@ -0,0 +1,456 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
import os
|
||||
import devedeng.configuration_data
|
||||
import devedeng.executor
|
||||
|
||||
class dvdauthor_converter(devedeng.executor.executor):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
devedeng.executor.executor.__init__(self)
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
|
||||
def create_dvd_project (self, path, name, file_movies, menu_entries, start_with_menu):
|
||||
|
||||
movie_path = os.path.join(path,"dvd_tree")
|
||||
try:
|
||||
os.makedirs(movie_path)
|
||||
except:
|
||||
pass
|
||||
|
||||
xml_file = self.create_dvdauthor_xml(path, file_movies, menu_entries, start_with_menu)
|
||||
|
||||
self.command_var=[]
|
||||
self.command_var.append("dvdauthor")
|
||||
self.command_var.append("-o")
|
||||
self.command_var.append(movie_path)
|
||||
self.command_var.append("-x")
|
||||
self.command_var.append(xml_file)
|
||||
self.use_pulse_mode = True
|
||||
self.text = _("Creating DVD structure")
|
||||
|
||||
|
||||
def process_stdout(self,data):
|
||||
return
|
||||
|
||||
|
||||
def process_stderr(self,data):
|
||||
if (data != None) and (data[0] != ""):
|
||||
self.progress_bar[1].set_text(data[0])
|
||||
return
|
||||
|
||||
|
||||
def create_dvdauthor_xml(self,movie_folder, file_movies, menu_entries, start_with_menu):
|
||||
|
||||
xmlpath = os.path.join(movie_folder,"xml_data")
|
||||
xml_file_path = os.path.join(xmlpath,"dvdauthor.xml")
|
||||
datapath = os.path.join(movie_folder,"dvd_tree")
|
||||
try:
|
||||
os.makedirs(xmlpath)
|
||||
except:
|
||||
pass
|
||||
|
||||
if (len(file_movies) == 1) and (menu_entries == None):
|
||||
onlyone = True
|
||||
else:
|
||||
onlyone = False
|
||||
|
||||
if (menu_entries == None):
|
||||
elements_per_menu = 1000
|
||||
else:
|
||||
elements_per_menu = len(menu_entries[0]["chapters"])
|
||||
|
||||
xml_file=open(xml_file_path,"w")
|
||||
xml_file.write('<dvdauthor dest="'+datapath+'">\n')
|
||||
|
||||
if onlyone:
|
||||
xml_file.write('\t<vmgm />\n')
|
||||
else:
|
||||
xml_file.write('\t<vmgm>\n')
|
||||
|
||||
# MENU
|
||||
|
||||
# in the FPC we do a jump to the first menu in the first titleset if we wanted MENU
|
||||
# or we jump to the second titleset if we didn't want MENU at startup
|
||||
|
||||
xml_file.write('\t\t<fpc>\n')
|
||||
xml_file.write('\t\t\tg0=100;\n')
|
||||
if (menu_entries != None) and (start_with_menu):
|
||||
xml_file.write('\t\t\tg1=0;\n')
|
||||
else:
|
||||
xml_file.write('\t\t\tg1=100;\n')
|
||||
xml_file.write('\t\t\tg2=1024;\n')
|
||||
xml_file.write('\t\t\tjump menu 1;\n')
|
||||
xml_file.write('\t\t</fpc>\n')
|
||||
|
||||
|
||||
# in the VMGM menu we create a code to jump to the title specified in G0
|
||||
# but if the title is 100, we jump to the menus. There we show the menu number
|
||||
# contained in G1
|
||||
|
||||
xml_file.write("\t\t<menus>\n")
|
||||
|
||||
xml_file.write('\t\t\t<video format="')
|
||||
if self.config.PAL:
|
||||
xml_file.write("pal")
|
||||
else:
|
||||
xml_file.write("ntsc")
|
||||
xml_file.write('" aspect="4:3"> </video>\n')
|
||||
|
||||
xml_file.write('\t\t\t<pgc>\n')
|
||||
xml_file.write('\t\t\t\t<pre>\n')
|
||||
|
||||
counter=1
|
||||
for element in file_movies:
|
||||
xml_file.write('\t\t\t\t\tif (g0 eq '+str(counter)+') {\n')
|
||||
xml_file.write('\t\t\t\t\t\tjump titleset '+str(1+counter)+' menu;\n')
|
||||
xml_file.write('\t\t\t\t\t}\n')
|
||||
counter+=1
|
||||
xml_file.write('\t\t\t\t\tif (g0 eq 100) {\n')
|
||||
xml_file.write('\t\t\t\t\t\tg2=1024;\n')
|
||||
xml_file.write('\t\t\t\t\t\tjump titleset 1 menu;\n')
|
||||
xml_file.write('\t\t\t\t\t}\n')
|
||||
xml_file.write('\t\t\t\t</pre>\n')
|
||||
# fake video (one black picture with one second of sound) to ensure 100% compatibility
|
||||
xml_file.write('\t\t\t\t<vob file="')
|
||||
|
||||
if self.config.PAL:
|
||||
xml_file.write(self.expand_xml(str(os.path.join(self.config.other_path,"base_pal.mpg"))))
|
||||
else:
|
||||
xml_file.write(self.expand_xml(str(os.path.join(self.config.other_path,"base_ntsc.mpg"))))
|
||||
|
||||
xml_file.write('"></vob>\n')
|
||||
xml_file.write('\t\t\t</pgc>\n')
|
||||
xml_file.write('\t\t</menus>\n')
|
||||
xml_file.write("\t</vmgm>\n")
|
||||
|
||||
xml_file.write("\n")
|
||||
|
||||
|
||||
# the first titleset contains all the menus. G1 allows us to jump to the desired menu
|
||||
|
||||
xml_file.write('\t<titleset>\n')
|
||||
xml_file.write('\t\t<menus>\n')
|
||||
xml_file.write('\t\t\t<video format="')
|
||||
if self.config.PAL:
|
||||
xml_file.write("pal")
|
||||
else:
|
||||
xml_file.write("ntsc")
|
||||
xml_file.write('" aspect="4:3"> </video>\n')
|
||||
|
||||
first_entry = True
|
||||
menu_number = 0
|
||||
|
||||
counter = 1
|
||||
title_list = []
|
||||
for element in file_movies:
|
||||
if element.show_in_menu:
|
||||
title_list.append(counter)
|
||||
counter += 1
|
||||
|
||||
if (menu_entries != None):
|
||||
nmenues = len(menu_entries)
|
||||
button_counter = 0
|
||||
for menu_page in menu_entries:
|
||||
xml_file.write('\t\t\t<pgc>\n')
|
||||
xml_file.write('\t\t\t\t<pre>\n')
|
||||
# first we recover the currently selected button
|
||||
xml_file.write('\t\t\t\t\ts8=g2;\n')
|
||||
|
||||
if first_entry: # here we add some code to jump to each menu
|
||||
for menu2 in range(nmenues-1):
|
||||
xml_file.write('\t\t\t\t\tif (g1 eq '+str(menu2+1)+') {\n')
|
||||
xml_file.write('\t\t\t\t\t\tjump menu '+str(menu2+2)+';\n')
|
||||
xml_file.write('\t\t\t\t\t}\n')
|
||||
|
||||
# this code is to fix a bug in some players
|
||||
xml_file.write('\t\t\t\t\tif (g1 eq 100) {\n')
|
||||
xml_file.write('\t\t\t\t\t\tjump title 1;\n') #menu '+str(self.nmenues+1)+';\n')
|
||||
xml_file.write('\t\t\t\t\t}\n')
|
||||
first_entry = False
|
||||
|
||||
xml_file.write('\t\t\t\t</pre>\n')
|
||||
xml_file.write('\t\t\t\t<vob file="')
|
||||
|
||||
xml_file.write(self.expand_xml(menu_page["filename"]))
|
||||
xml_file.write('"></vob>\n')
|
||||
|
||||
for nbutton in menu_page["chapters"]:
|
||||
xml_file.write('\t\t\t\t<button name="'+nbutton+'"> g0='+str(title_list[button_counter])+'; jump vmgm menu; </button>\n')
|
||||
button_counter+=1
|
||||
|
||||
if (menu_page["left"] != None):
|
||||
xml_file.write('\t\t\t\t<button name="'+menu_page["left"]+'"> g1=')
|
||||
xml_file.write(str(menu_number-1))
|
||||
xml_file.write('; g2=1024; jump menu ')
|
||||
xml_file.write(str(menu_number))
|
||||
xml_file.write('; </button>\n')
|
||||
|
||||
if (menu_page["right"] != None):
|
||||
xml_file.write('\t\t\t\t<button name="'+menu_page["right"]+'"> g1=')
|
||||
xml_file.write(str(menu_number+1))
|
||||
xml_file.write('; g2=1024; jump menu ')
|
||||
xml_file.write(str(menu_number+2))
|
||||
xml_file.write('; </button>\n')
|
||||
|
||||
xml_file.write('\t\t\t\t<post>\n')
|
||||
xml_file.write('\t\t\t\t\tg2=s8;\n')
|
||||
xml_file.write('\t\t\t\t\tg1='+str(menu_number)+';\n')
|
||||
xml_file.write('\t\t\t\t\tjump menu '+str(menu_number+1)+';\n')
|
||||
xml_file.write('\t\t\t\t</post>\n')
|
||||
xml_file.write('\t\t\t</pgc>\n')
|
||||
menu_number += 1
|
||||
|
||||
xml_file.write('\t\t</menus>\n')
|
||||
else:
|
||||
xml_file.write('\t\t\t<pgc>\n')
|
||||
xml_file.write('\t\t\t\t<pre>\n')
|
||||
# first we recover the currently selected button
|
||||
xml_file.write('\t\t\t\t\ts8=g2;\n')
|
||||
|
||||
# this code is to fix a bug in some players
|
||||
xml_file.write('\t\t\t\t\tif (g1 eq 100) {\n')
|
||||
xml_file.write('\t\t\t\t\t\tjump title 1;\n') #menu '+str(self.nmenues+1)+';\n')
|
||||
xml_file.write('\t\t\t\t\t}\n')
|
||||
|
||||
xml_file.write('\t\t\t\t</pre>\n')
|
||||
xml_file.write('\t\t\t\t<vob file="')
|
||||
if self.config.PAL:
|
||||
xml_file.write(self.expand_xml(str(os.path.join(self.config.other_path,"base_pal.mpg"))))
|
||||
else:
|
||||
xml_file.write(self.expand_xml(str(os.path.join(self.config.other_path,"base_ntsc.mpg"))))
|
||||
xml_file.write('"></vob>\n')
|
||||
|
||||
xml_file.write('\t\t\t\t<post>\n')
|
||||
xml_file.write('\t\t\t\t\tg2=s8;\n')
|
||||
xml_file.write('\t\t\t\t\tg1=0;\n')
|
||||
xml_file.write('\t\t\t\t\tjump menu 1;\n')
|
||||
xml_file.write('\t\t\t\t</post>\n')
|
||||
xml_file.write('\t\t\t</pgc>\n')
|
||||
|
||||
xml_file.write('\t\t</menus>\n')
|
||||
|
||||
xml_file.write('\t\t<titles>\n')
|
||||
xml_file.write('\t\t\t<video format="')
|
||||
if self.config.PAL:
|
||||
xml_file.write("pal")
|
||||
else:
|
||||
xml_file.write("ntsc")
|
||||
xml_file.write('" aspect="4:3"> </video>\n')
|
||||
xml_file.write('\t\t\t<pgc>\n')
|
||||
xml_file.write('\t\t\t\t<vob file="')
|
||||
if self.config.PAL:
|
||||
xml_file.write(self.expand_xml(str(os.path.join(self.config.other_path,"base_pal.mpg"))))
|
||||
else:
|
||||
xml_file.write(self.expand_xml(str(os.path.join(self.config.other_path,"base_ntsc.mpg"))))
|
||||
xml_file.write('"></vob>\n')
|
||||
xml_file.write('\t\t\t\t<post>\n')
|
||||
xml_file.write('\t\t\t\t\tg0=1;\n')
|
||||
xml_file.write('\t\t\t\t\tg1=0;\n')
|
||||
xml_file.write('\t\t\t\t\tg2=1024;\n')
|
||||
xml_file.write('\t\t\t\t\tcall vmgm menu entry title;\n')
|
||||
xml_file.write('\t\t\t\t</post>\n')
|
||||
xml_file.write('\t\t\t</pgc>\n')
|
||||
xml_file.write('\t\t</titles>\n')
|
||||
xml_file.write("\t</titleset>\n")
|
||||
|
||||
xml_file.write("\n")
|
||||
|
||||
|
||||
# Now, create the titleset for each video
|
||||
|
||||
total_t=len(file_movies)
|
||||
titleset=1
|
||||
titles=0
|
||||
counter=0
|
||||
for element in file_movies:
|
||||
files=0
|
||||
action=element.actions
|
||||
|
||||
xml_file.write("\n")
|
||||
|
||||
if element.is_mpeg_ps:
|
||||
|
||||
# if it's already an MPEG-2 compliant file, we use the original values
|
||||
if element.original_fps == 25:
|
||||
pal_ntsc="pal"
|
||||
ispal=True
|
||||
else:
|
||||
pal_ntsc="ntsc"
|
||||
ispal=False
|
||||
|
||||
if element.original_aspect_ratio > 1.6:
|
||||
faspect='16:9'
|
||||
fwide=True
|
||||
else:
|
||||
faspect='4:3'
|
||||
fwide=False
|
||||
|
||||
else:
|
||||
# but if we are converting it, we use the desired values
|
||||
if element.format_pal:
|
||||
pal_ntsc="pal"
|
||||
ispal=True
|
||||
else:
|
||||
pal_ntsc="ntsc"
|
||||
ispal=False
|
||||
|
||||
if element.aspect_ratio_final > 1.6:
|
||||
faspect='16:9'
|
||||
fwide=True
|
||||
else:
|
||||
faspect='4:3'
|
||||
fwide=False
|
||||
|
||||
xml_file.write("\t<titleset>\n")
|
||||
|
||||
if not onlyone:
|
||||
xml_file.write("\t\t<menus>\n")
|
||||
xml_file.write('\t\t\t<video format="'+pal_ntsc+'" aspect="'+faspect+'"')
|
||||
if fwide:
|
||||
xml_file.write(' widescreen="nopanscan"')
|
||||
xml_file.write('> </video>\n')
|
||||
|
||||
xml_file.write("\t\t\t<pgc>\n")
|
||||
xml_file.write("\t\t\t\t<pre>\n")
|
||||
xml_file.write('\t\t\t\t\tif (g0 eq 100) {\n')
|
||||
xml_file.write('\t\t\t\t\t\tjump vmgm menu entry title;\n')
|
||||
xml_file.write('\t\t\t\t\t}\n')
|
||||
xml_file.write('\t\t\t\t\tg0=100;\n')
|
||||
xml_file.write('\t\t\t\t\tg1='+str(int(titles/elements_per_menu))+';\n')
|
||||
xml_file.write('\t\t\t\t\tjump title 1;\n')
|
||||
xml_file.write('\t\t\t\t</pre>\n')
|
||||
# fake video to ensure compatibility
|
||||
xml_file.write('\t\t\t\t<vob file="')
|
||||
if ispal:
|
||||
xml_file.write(self.expand_xml(str(os.path.join(self.config.other_path,"base_pal"))))
|
||||
else:
|
||||
xml_file.write(self.expand_xml(str(os.path.join(self.config.other_path,"base_ntsc"))))
|
||||
if fwide:
|
||||
xml_file.write("_wide")
|
||||
xml_file.write('.mpg"></vob>\n')
|
||||
xml_file.write("\t\t\t</pgc>\n")
|
||||
xml_file.write("\t\t</menus>\n")
|
||||
|
||||
xml_file.write("\t\t<titles>\n")
|
||||
xml_file.write('\t\t\t<video format="'+pal_ntsc+'" aspect="'+faspect+'"')
|
||||
if fwide:
|
||||
xml_file.write(' widescreen="nopanscan"')
|
||||
xml_file.write('> </video>\n')
|
||||
|
||||
# subtitles part
|
||||
# for element3 in element2["sub_list"]:
|
||||
# xml_file.write('\t\t\t<subpicture lang="'+self.expand_xml(str(element3["sub_language"][:2].lower()))+'" />\n')
|
||||
xml_file.write('\t\t\t<pgc>\n')
|
||||
|
||||
# if (element2["force_subs"]) and (len(element2["sub_list"])!=0):
|
||||
# xml_file.write('\t\t\t\t<pre>\n')
|
||||
# xml_file.write('\t\t\t\t\tsubtitle=64;\n')
|
||||
# xml_file.write('\t\t\t\t</pre>\n')
|
||||
|
||||
xml_file.write('\t\t\t\t<vob file="'+self.expand_xml(element.converted_filename)+'" ')
|
||||
xml_file.write('chapters="0')
|
||||
if (element.original_length > 5):
|
||||
if (element.divide_in_chapters): # add chapters
|
||||
toadd = element.chapter_size
|
||||
seconds = toadd*60
|
||||
while seconds < (element.original_length - 4):
|
||||
thetime = self.return_time(seconds,False)
|
||||
xml_file.write(","+thetime)
|
||||
seconds += (toadd*60)
|
||||
xml_file.write(',' + self.return_time((element.original_length -2 ),False))
|
||||
xml_file.write('" />\n')
|
||||
|
||||
if not onlyone:
|
||||
xml_file.write('\t\t\t\t<post>\n')
|
||||
files+=1
|
||||
xml_file.write('\t\t\t\t\tg1='+str(int(titles/elements_per_menu))+';\n')
|
||||
if (action=="action_stop"):
|
||||
xml_file.write('\t\t\t\t\tg0=100;\n')
|
||||
xml_file.write('\t\t\t\t\tcall vmgm menu entry title;\n')
|
||||
else:
|
||||
xml_file.write('\t\t\t\t\tg0=')
|
||||
if action=="action_play_previous":
|
||||
if titles == 0:
|
||||
prev_t = total_t - 1
|
||||
else:
|
||||
prev_t = titles - 1
|
||||
xml_file.write(str(prev_t + 1))
|
||||
elif action=="action_play_again":
|
||||
xml_file.write(str(titles + 1))
|
||||
elif action=="action_play_next":
|
||||
if titles==total_t-1:
|
||||
next_t=0
|
||||
else:
|
||||
next_t=titles + 1
|
||||
xml_file.write(str(next_t + 1))
|
||||
elif action=="action_play_last":
|
||||
xml_file.write(str(total_t))
|
||||
else:
|
||||
xml_file.write('1') # first
|
||||
|
||||
xml_file.write(';\n')
|
||||
xml_file.write('\t\t\t\t\tcall vmgm menu entry title;\n')
|
||||
xml_file.write('\t\t\t\t</post>\n')
|
||||
xml_file.write("\t\t\t</pgc>\n")
|
||||
xml_file.write("\t\t</titles>\n")
|
||||
xml_file.write("\t</titleset>\n")
|
||||
counter+=1
|
||||
titles+=1
|
||||
|
||||
xml_file.write("</dvdauthor>")
|
||||
|
||||
xml_file.close()
|
||||
|
||||
return xml_file_path
|
||||
|
||||
|
||||
def return_time(self,seconds,empty):
|
||||
|
||||
""" cuts a time in seconds into seconds, minutes and hours """
|
||||
|
||||
seconds2=int(seconds)
|
||||
|
||||
hours=str(int(seconds2/3600))
|
||||
if empty:
|
||||
if len(hours)==1:
|
||||
hours="0"+hours
|
||||
else:
|
||||
if hours=="0":
|
||||
hours=""
|
||||
if hours!="":
|
||||
hours+=":"
|
||||
|
||||
minutes=str(int((seconds2/60)%60))
|
||||
if empty or (hours!=""):
|
||||
if len(minutes)==1:
|
||||
minutes="0"+minutes
|
||||
elif (minutes=="0") and (hours==""):
|
||||
minutes=""
|
||||
if minutes!="":
|
||||
minutes+=":"
|
||||
|
||||
secs=str(int(seconds2%60))
|
||||
if (len(secs)==1) and (minutes!=""):
|
||||
secs="0"+secs
|
||||
|
||||
return hours+minutes+secs
|
||||
75
src/devedeng/end_job.py
Normal file
75
src/devedeng/end_job.py
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
from gi.repository import Gtk,Gdk
|
||||
import os
|
||||
import devedeng.configuration_data
|
||||
|
||||
class end_window:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
def run(self,time_used, do_burn):
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(self.config.gettext_domain)
|
||||
|
||||
builder.add_from_file(os.path.join(self.config.glade,"wdone.ui"))
|
||||
builder.connect_signals(self)
|
||||
werror_window = builder.get_object("done")
|
||||
wburn = builder.get_object("button_burn")
|
||||
wdebug_buffer = builder.get_object("debug_buffer")
|
||||
wdebug_buffer.insert_at_cursor(self.config.get_log())
|
||||
wtime = builder.get_object("label_time")
|
||||
hours = int(time_used / 3600)
|
||||
minutes = int((time_used / 60) % 60)
|
||||
seconds = int(time_used % 60)
|
||||
time_used_str = ""
|
||||
if hours < 10:
|
||||
time_used_str += "0"
|
||||
time_used_str += str(hours)+":"
|
||||
if minutes < 10:
|
||||
time_used_str += "0"
|
||||
time_used_str += str(minutes)+":"
|
||||
if seconds < 10:
|
||||
time_used_str += "0"
|
||||
time_used_str += str(seconds)
|
||||
wtime.set_text(time_used_str)
|
||||
|
||||
werror_window.show_all()
|
||||
|
||||
if do_burn:
|
||||
wburn.show()
|
||||
else:
|
||||
wburn.hide()
|
||||
|
||||
retval = werror_window.run()
|
||||
werror_window.destroy()
|
||||
if retval == 1:
|
||||
return True # burn disc image
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def on_copy_clicked(self,b):
|
||||
|
||||
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
||||
data =self.config.get_log()
|
||||
clipboard.set_text(data,len(data))
|
||||
return
|
||||
47
src/devedeng/error.py
Normal file
47
src/devedeng/error.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
from gi.repository import Gtk,Gdk
|
||||
import os
|
||||
import devedeng.configuration_data
|
||||
|
||||
class error_window:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(self.config.gettext_domain)
|
||||
|
||||
builder.add_from_file(os.path.join(self.config.glade,"werror.ui"))
|
||||
builder.connect_signals(self)
|
||||
werror_window = builder.get_object("dialog_error")
|
||||
wdebug_buffer = builder.get_object("debug_buffer")
|
||||
wdebug_buffer.insert_at_cursor(self.config.get_log())
|
||||
|
||||
werror_window.show_all()
|
||||
werror_window.run()
|
||||
werror_window.destroy()
|
||||
|
||||
|
||||
def on_copy_clicked(self,b):
|
||||
|
||||
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
||||
data =self.config.get_log()
|
||||
clipboard.set_text(data,len(data))
|
||||
return
|
||||
390
src/devedeng/executor.py
Normal file
390
src/devedeng/executor.py
Normal file
|
|
@ -0,0 +1,390 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
from gi.repository import GLib,GObject
|
||||
import subprocess
|
||||
import os
|
||||
import signal
|
||||
import devedeng.configuration_data
|
||||
|
||||
class executor(GObject.GObject):
|
||||
""" This class encapsulates everything needed for launching processes """
|
||||
|
||||
__gsignals__ = {'ended': (GObject.SIGNAL_RUN_FIRST, None,(int,))}
|
||||
|
||||
def __init__(self):
|
||||
|
||||
GObject.GObject.__init__(self)
|
||||
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
self.channel_stdin = None
|
||||
self.channel_stdout = None
|
||||
self.channel_stderr = None
|
||||
self.text = ""
|
||||
self.stdout_data = ""
|
||||
self.stderr_data = ""
|
||||
self.stdin_file = None
|
||||
self.stdout_file = None
|
||||
|
||||
self.dependencies = None
|
||||
self.childs = []
|
||||
self.progress_bar = None
|
||||
self.killed = False
|
||||
self.pulse_mode = False
|
||||
self.use_pulse_mode = False
|
||||
self.pulse_text = None
|
||||
self.handle = None
|
||||
|
||||
|
||||
def add_dependency(self, dep):
|
||||
|
||||
self.add_dependency2(dep)
|
||||
|
||||
for child in dep.childs:
|
||||
self.add_dependency2(child)
|
||||
|
||||
|
||||
def add_dependency2(self, dep):
|
||||
|
||||
if (self.dependencies == None):
|
||||
self.dependencies = []
|
||||
|
||||
if (self.dependencies.count(dep) == 0):
|
||||
self.dependencies.append(dep)
|
||||
|
||||
# the childs have the same dependencies than the parent process because, from outside, it is viewed as a single process
|
||||
for child in self.childs:
|
||||
child.add_dependency(dep)
|
||||
|
||||
|
||||
def remove_dependency(self,process):
|
||||
# dependencies are removed only in the parent because the running class have all the processes, parents and childs, and calls
|
||||
# this method on all of them
|
||||
if (self.dependencies != None):
|
||||
tmp2 = []
|
||||
for dep in self.dependencies:
|
||||
if dep != process:
|
||||
tmp2.append(dep)
|
||||
if (len(tmp2) != 0):
|
||||
self.dependencies = tmp2
|
||||
else:
|
||||
self.dependencies = None
|
||||
|
||||
|
||||
def add_child_process(self,child):
|
||||
|
||||
# the childs have the same dependencies than the parent process because, from outside, it is viewed as a single process
|
||||
if self.dependencies != None:
|
||||
for dep in self.dependencies:
|
||||
child.add_dependency(dep)
|
||||
|
||||
if (self.childs.count(child) == 0):
|
||||
self.childs.append(child)
|
||||
|
||||
|
||||
def run(self, progress_bar):
|
||||
|
||||
self.progress_bar = progress_bar
|
||||
self.progress_bar[0].set_label(self.text)
|
||||
self.progress_bar[1].set_fraction(0.0)
|
||||
self.progress_bar[0].show_all()
|
||||
# call, if it exists, the pre-function
|
||||
try:
|
||||
self.pre_function()
|
||||
except:
|
||||
pass
|
||||
self.launch_process(self.command_var)
|
||||
if self.use_pulse_mode != self.pulse_mode:
|
||||
self.set_pulse_mode(self.use_pulse_mode)
|
||||
|
||||
|
||||
def remove_ansi(self,line):
|
||||
|
||||
output=""
|
||||
while True:
|
||||
pos=line.find("\033[") # try with double-byte ESC
|
||||
jump=2
|
||||
if pos==-1:
|
||||
pos=line.find("\233") # if not, try with single-byte ESC
|
||||
jump=1
|
||||
if pos==-1: # no ANSI characters; we ended
|
||||
output+=line
|
||||
break
|
||||
|
||||
output+=line[:pos]
|
||||
line=line[pos+jump:]
|
||||
|
||||
while True:
|
||||
if len(line)==0:
|
||||
break
|
||||
if (ord(line[0])<64) or (ord(line[0])>126):
|
||||
line=line[1:]
|
||||
else:
|
||||
line=line[1:]
|
||||
break
|
||||
return output
|
||||
|
||||
|
||||
def launch_process(self,command,redirect_output = True):
|
||||
|
||||
if command != None:
|
||||
self.launch_command = "\n\nLaunching:"
|
||||
for e in command:
|
||||
self.launch_command += (" "+e)
|
||||
self.launch_command += "\n"
|
||||
|
||||
self.config.append_log(self.text)
|
||||
if command != None:
|
||||
self.config.append_log(self.launch_command)
|
||||
|
||||
try:
|
||||
if (self.stdin_file != None):
|
||||
self.handle = subprocess.Popen(command,stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
|
||||
self.channel_stdin = GLib.IOChannel(self.handle.stdin.fileno())
|
||||
self.channel_stdin.add_watch(GLib.IO_OUT | GLib.IO_HUP, self.read_stdin_from_file)
|
||||
self.file_in = open(self.stdin_file,"rb")
|
||||
else:
|
||||
self.handle = subprocess.Popen(command,stdout = subprocess.PIPE, stderr = subprocess.PIPE)
|
||||
except Exception as error_launch:
|
||||
self.handle = None
|
||||
self.stderr_data += str(error_launch)
|
||||
self.wait_end()
|
||||
return
|
||||
|
||||
if (redirect_output):
|
||||
self.stdout_buf = ""
|
||||
self.stderr_buf = ""
|
||||
self.channel_stdout = GLib.IOChannel(self.handle.stdout.fileno())
|
||||
self.channel_stderr = GLib.IOChannel(self.handle.stderr.fileno())
|
||||
if (self.stdout_file != None):
|
||||
self.channel_stdout.add_watch(GLib.IO_IN | GLib.IO_HUP,self.read_stdout_to_file)
|
||||
self.file_out = open(self.stdout_file,"wb")
|
||||
else:
|
||||
self.channel_stdout.add_watch(GLib.IO_IN | GLib.IO_HUP,self.read_stdout)
|
||||
self.channel_stderr.add_watch(GLib.IO_IN | GLib.IO_HUP,self.read_stderr)
|
||||
else:
|
||||
(stdout_r, stderr_r) = self.handle.communicate()
|
||||
self.handle = None
|
||||
self.config.append_log(self.launch_command)
|
||||
try:
|
||||
self.config.append_log(stdout_r.decode("utf-8"))
|
||||
except:
|
||||
self.config.append_log(stdout_r.decode("latin-1"))
|
||||
try:
|
||||
self.config.append_log(stderr_r.decode("utf-8"))
|
||||
except:
|
||||
self.config.append_log(stderr_r.decode("latin-1"))
|
||||
return (stdout_r, stderr_r)
|
||||
|
||||
|
||||
def set_pulse_mode(self,pulse_mode):
|
||||
|
||||
if pulse_mode == self.pulse_mode:
|
||||
return
|
||||
|
||||
self.pulse_mode = pulse_mode
|
||||
|
||||
if pulse_mode:
|
||||
self.timer_pulse = GLib.timeout_add(250, self.run_pulse)
|
||||
else:
|
||||
GLib.source_remove(self.timer_pulse)
|
||||
|
||||
|
||||
def run_pulse(self,v = None):
|
||||
|
||||
if self.progress_bar == None:
|
||||
return
|
||||
|
||||
if self.pulse_text != None:
|
||||
self.progress_bar[1].set_text(self.pulse_text)
|
||||
self.progress_bar[1].pulse()
|
||||
return True
|
||||
|
||||
|
||||
def read_stdout_to_file(self,source,condition):
|
||||
|
||||
if (condition != GLib.IO_IN):
|
||||
self.channel_stdout = None
|
||||
if ((self.channel_stderr == None) and (self.channel_stdin == None)):
|
||||
self.wait_end()
|
||||
return False
|
||||
else:
|
||||
line_data = self.handle.stdout.read1(4096)
|
||||
self.file_out.write(line_data)
|
||||
return True
|
||||
|
||||
|
||||
def read_stdin_from_file(self,source,condition):
|
||||
|
||||
line_data = self.file_in.read1(4096)
|
||||
if (len(line_data) == 0):
|
||||
self.channel_stdin = None
|
||||
self.handle.stdin.close()
|
||||
if ((self.channel_stderr == None) and (self.channel_stdout == None)):
|
||||
self.wait_end()
|
||||
return False
|
||||
else:
|
||||
self.handle.stdin.write(line_data)
|
||||
return True
|
||||
|
||||
|
||||
def read_stdout(self,source,condition):
|
||||
|
||||
if (condition != GLib.IO_IN):
|
||||
self.channel_stdout = None
|
||||
if ((self.channel_stderr == None) and (self.channel_stdin == None)):
|
||||
self.wait_end()
|
||||
return False
|
||||
else:
|
||||
read_data = self.handle.stdout.read1(4096)
|
||||
try:
|
||||
line_data = self.stdout_buf+(read_data.decode("utf-8"))
|
||||
except:
|
||||
line_data = self.stdout_buf+(read_data.decode("latin-1"))
|
||||
self.stdout_data += line_data
|
||||
data = (line_data).replace("\r","\n").split("\n")
|
||||
if (len(data) == 1):
|
||||
final_data = []
|
||||
self.stdout_buf = data[0]
|
||||
else:
|
||||
final_data = data[:-1]
|
||||
self.stdout_buf = data[-1]
|
||||
if (len(final_data) != 0):
|
||||
self.process_stdout(final_data)
|
||||
return True
|
||||
|
||||
|
||||
def read_stderr(self,source,condition):
|
||||
|
||||
if (condition != GLib.IO_IN):
|
||||
self.channel_stderr = None
|
||||
if (((self.channel_stdout == None) or (self.stdout_file != None)) and ((self.channel_stdin == None) or (self.stdin_file != None))):
|
||||
self.wait_end()
|
||||
return False
|
||||
else:
|
||||
read_data = self.handle.stderr.read1(4096)
|
||||
try:
|
||||
line_data = self.stderr_buf+(read_data.decode("utf-8"))
|
||||
except:
|
||||
line_data = self.stderr_buf+(read_data.decode("latin-1"))
|
||||
self.stderr_data += line_data
|
||||
data = (line_data).replace("\r","\n").split("\n")
|
||||
if (len(data) == 1):
|
||||
final_data = []
|
||||
self.stderr_buf = data[0]
|
||||
else:
|
||||
final_data = data[:-1]
|
||||
self.stderr_buf = data[-1]
|
||||
if (len(final_data) != 0):
|
||||
self.process_stderr(final_data)
|
||||
return True
|
||||
|
||||
|
||||
def cancel(self):
|
||||
|
||||
""" Called to kill this process. """
|
||||
|
||||
if self.handle==None:
|
||||
return
|
||||
self.killed = True
|
||||
os.kill(self.handle.pid,signal.SIGKILL)
|
||||
|
||||
|
||||
def wait_end(self):
|
||||
|
||||
if self.handle != None:
|
||||
retval = self.handle.wait()
|
||||
self.handle = None
|
||||
else:
|
||||
retval = -1
|
||||
|
||||
self.set_pulse_mode(False)
|
||||
|
||||
# call, if it exists, the post-function
|
||||
try:
|
||||
self.post_function(retval,self.killed)
|
||||
except:
|
||||
pass
|
||||
|
||||
if self.killed:
|
||||
retval = 0
|
||||
else:
|
||||
self.config.append_log(self.stdout_data)
|
||||
self.config.append_log(self.stderr_data)
|
||||
|
||||
self.emit("ended",retval)
|
||||
|
||||
|
||||
def expand_xml(self,text):
|
||||
|
||||
text=text.replace('&','&')
|
||||
text=text.replace('<','<')
|
||||
text=text.replace('>','>')
|
||||
text=text.replace('"','"')
|
||||
text=text.replace("'",''')
|
||||
return text
|
||||
|
||||
|
||||
def get_division(self,data):
|
||||
|
||||
pos = data.find("/")
|
||||
pos2 = data.find(":")
|
||||
|
||||
if (pos == -1):
|
||||
pos = pos2
|
||||
|
||||
if (pos == -1):
|
||||
try:
|
||||
return float(data)
|
||||
except:
|
||||
return 0
|
||||
else:
|
||||
try:
|
||||
data1 = float(data[:pos])
|
||||
data2 = float(data[pos+1:])
|
||||
except:
|
||||
return 0
|
||||
if (data2 == 0):
|
||||
return 0
|
||||
else:
|
||||
return (float(int((data1 / data2)*1000.0)))/1000.0
|
||||
|
||||
|
||||
def get_time(self,line):
|
||||
|
||||
time_string = ""
|
||||
|
||||
for letter in line.strip():
|
||||
if (letter == ':') or (letter == '.') or (letter.isdigit()):
|
||||
time_string += letter
|
||||
else:
|
||||
break
|
||||
|
||||
elements = time_string.split(":")
|
||||
|
||||
if (len(elements) == 0):
|
||||
return -1
|
||||
|
||||
value = 0
|
||||
for element in elements:
|
||||
if (element == ''):
|
||||
continue
|
||||
value *= 60
|
||||
value += int(0.5 + float(element))
|
||||
return value
|
||||
427
src/devedeng/ffmpeg.py
Normal file
427
src/devedeng/ffmpeg.py
Normal file
|
|
@ -0,0 +1,427 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
import subprocess
|
||||
import os
|
||||
import devedeng.configuration_data
|
||||
import devedeng.executor
|
||||
import devedeng.mux_dvd_menu
|
||||
|
||||
class ffmpeg(devedeng.executor.executor):
|
||||
|
||||
supports_analize = False
|
||||
supports_play = False
|
||||
supports_convert = True
|
||||
supports_menu = True
|
||||
supports_mkiso = False
|
||||
supports_burn = False
|
||||
display_name = "FFMPEG"
|
||||
disc_types = []
|
||||
|
||||
@staticmethod
|
||||
def check_is_installed():
|
||||
try:
|
||||
handle = subprocess.Popen(["ffmpeg","-codecs"], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
|
||||
(stdout, stderr) = handle.communicate()
|
||||
if 0==handle.wait():
|
||||
mp2 = False
|
||||
mp3 = False
|
||||
ac3 = False
|
||||
mpeg1 = False
|
||||
mpeg2 = False
|
||||
divx = False
|
||||
h264 = False
|
||||
for line in stdout.decode("latin-1").split("\n"):
|
||||
parts = line.strip().split(" ")
|
||||
if len(parts) < 2:
|
||||
continue
|
||||
if len(parts[0]) != 6:
|
||||
continue
|
||||
capabilities = parts[0]
|
||||
codec = parts[1]
|
||||
|
||||
if capabilities[1] != 'E':
|
||||
continue
|
||||
|
||||
if (codec == "mpeg1video"):
|
||||
mpeg1 = True
|
||||
continue
|
||||
if (codec == "mpeg2video"):
|
||||
mpeg2 = True
|
||||
continue
|
||||
if (codec == "mp2"):
|
||||
mp2 = True
|
||||
continue
|
||||
if (codec == "mp3"):
|
||||
mp3 = True
|
||||
continue
|
||||
if (codec == "ac3"):
|
||||
ac3 = True
|
||||
continue
|
||||
if (codec == "h264") or (codec == "H264"):
|
||||
h264 = True
|
||||
continue
|
||||
if (codec == "mpeg4"):
|
||||
divx = True
|
||||
continue
|
||||
|
||||
if (mpeg1 and mp2):
|
||||
devedeng.ffmpeg.ffmpeg.disc_types.append("vcd")
|
||||
if (mpeg2 and mp2):
|
||||
devedeng.ffmpeg.ffmpeg.disc_types.append("svcd")
|
||||
devedeng.ffmpeg.ffmpeg.disc_types.append("cvd")
|
||||
if (mpeg2 and mp2 and ac3):
|
||||
devedeng.ffmpeg.ffmpeg.disc_types.append("dvd")
|
||||
if (divx and mp3):
|
||||
devedeng.ffmpeg.ffmpeg.disc_types.append("divx")
|
||||
if (h264 and mp3):
|
||||
devedeng.ffmpeg.ffmpeg.disc_types.append("mkv")
|
||||
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def __init__(self):
|
||||
|
||||
devedeng.executor.executor.__init__(self)
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
def convert_file(self,file_project,output_file,video_length,pass2 = False):
|
||||
|
||||
if file_project.two_pass_encoding:
|
||||
if pass2:
|
||||
self.text = _("Converting %(X)s (pass 2)") % {"X" : file_project.title_name}
|
||||
else:
|
||||
self.text = _("Converting %(X)s (pass 1)") % {"X" : file_project.title_name}
|
||||
# Prepare the converting process for the second pass
|
||||
tmp = devedeng.ffmpeg.ffmpeg()
|
||||
tmp.convert_file(file_project, output_file, video_length, True)
|
||||
# it deppends of this process
|
||||
tmp.add_dependency(self)
|
||||
# add it as a child process of this one
|
||||
self.add_child_process(tmp)
|
||||
else:
|
||||
self.text = _("Converting %(X)s") % {"X" : file_project.title_name}
|
||||
|
||||
if (pass2 == False) and (file_project.two_pass_encoding == True):
|
||||
# this is the first pass in a 2-pass codification
|
||||
second_pass = False
|
||||
else:
|
||||
# second_pass is TRUE in the second pass of a 2-pass codification, and also when not doing 2-pass codification
|
||||
# It is used to remove unnecessary steps during the first pass, but that are needed on the second pass, or when not using 2-pass codification
|
||||
second_pass = True
|
||||
|
||||
if (video_length == 0):
|
||||
self.final_length = file_project.original_length
|
||||
else:
|
||||
self.final_length = video_length
|
||||
self.command_var=[]
|
||||
self.command_var.append("ffmpeg")
|
||||
self.command_var.append("-i")
|
||||
self.command_var.append(file_project.file_name)
|
||||
|
||||
if (file_project.volume!=100) and second_pass:
|
||||
self.command_var.append("-vol")
|
||||
self.command_var.append(str((256*file_project.volume)/100))
|
||||
|
||||
if (file_project.audio_delay != 0.0) and (file_project.copy_sound==False) and (file_project.no_reencode_audio_video==False) and second_pass:
|
||||
self.command_var.append("-itsoffset")
|
||||
self.command_var.append(str(file_project.audio_delay))
|
||||
|
||||
self.command_var.append("-i")
|
||||
self.command_var.append(file_project.file_name)
|
||||
self.command_var.append("-map")
|
||||
self.command_var.append("1:"+str(file_project.video_list[0]))
|
||||
if (not file_project.copy_sound) and (not file_project.no_reencode_audio_video):
|
||||
for l in file_project.audio_list:
|
||||
self.command_var.append("-map")
|
||||
self.command_var.append("0:"+str(l))
|
||||
|
||||
if (file_project.no_reencode_audio_video==False) and second_pass:
|
||||
cmd_line=""
|
||||
|
||||
if file_project.deinterlace=="deinterlace_yadif":
|
||||
cmd_line+="yadif"
|
||||
|
||||
vflip=False
|
||||
hflip=False
|
||||
|
||||
if (file_project.rotation=="rotation_90"):
|
||||
if (cmd_line!=""):
|
||||
cmd_line+=",fifo,"
|
||||
cmd_line+="transpose=1"
|
||||
elif (file_project.rotation=="rotation_270"):
|
||||
if (cmd_line!=""):
|
||||
cmd_line+=",fifo,"
|
||||
cmd_line+="transpose=2"
|
||||
elif (file_project.rotation=="rotation_180"):
|
||||
vflip=True
|
||||
hflip=True
|
||||
|
||||
if (file_project.mirror_vertical):
|
||||
vflip=not vflip
|
||||
if (file_project.mirror_horizontal):
|
||||
hflip=not hflip
|
||||
|
||||
if (vflip):
|
||||
if (cmd_line!=""):
|
||||
cmd_line+=",fifo,"
|
||||
cmd_line+="vflip"
|
||||
if (hflip):
|
||||
if (cmd_line!=""):
|
||||
cmd_line+=",fifo,"
|
||||
cmd_line+="hflip"
|
||||
|
||||
if (file_project.width_midle != file_project.original_width) or (file_project.height_midle != file_project.original_height):
|
||||
if (cmd_line!=""):
|
||||
cmd_line+=",fifo,"
|
||||
x = int((file_project.width_midle - file_project.original_width) /2)
|
||||
y = int((file_project.height_midle - file_project.original_height) /2)
|
||||
if (x > 0) or (y > 0):
|
||||
cmd_line+="pad="+str(file_project.width_midle)+":"+str(file_project.height_midle)+":"+str(x)+":"+str(y)+":0x000000"
|
||||
else:
|
||||
cmd_line+="crop="+str(file_project.width_midle)+":"+str(file_project.height_midle)+":"+str(x)+":"+str(y)
|
||||
|
||||
if (file_project.width_final != file_project.width_midle) or (file_project.height_final != file_project.height_midle):
|
||||
if (cmd_line!=""):
|
||||
cmd_line+=",fifo,"
|
||||
cmd_line+="scale="+str(file_project.width_final)+":"+str(file_project.height_final)
|
||||
|
||||
if cmd_line!="":
|
||||
self.command_var.append("-vf")
|
||||
self.command_var.append(cmd_line)
|
||||
|
||||
|
||||
self.command_var.append("-y")
|
||||
|
||||
vcd=False
|
||||
|
||||
if (self.config.disc_type == "divx"):
|
||||
self.command_var.append("-vcodec")
|
||||
self.command_var.append("mpeg4")
|
||||
self.command_var.append("-acodec")
|
||||
self.command_var.append("libmp3lame")
|
||||
self.command_var.append("-f")
|
||||
self.command_var.append("avi")
|
||||
elif (self.config.disc_type == "mkv"):
|
||||
self.command_var.append("-vcodec")
|
||||
self.command_var.append("h264")
|
||||
self.command_var.append("-acodec")
|
||||
self.command_var.append("libmp3lame")
|
||||
self.command_var.append("-f")
|
||||
self.command_var.append("matroska")
|
||||
else:
|
||||
self.command_var.append("-target")
|
||||
if (self.config.disc_type=="dvd"):
|
||||
if not file_project.format_pal:
|
||||
self.command_var.append("ntsc-dvd")
|
||||
elif (file_project.original_fps==24):
|
||||
self.command_var.append("film-dvd")
|
||||
else:
|
||||
self.command_var.append("pal-dvd")
|
||||
if (not file_project.copy_sound):
|
||||
if file_project.sound5_1:
|
||||
self.command_var.append("-acodec")
|
||||
self.command_var.append("ac3")
|
||||
elif (self.config.disc_type=="vcd"):
|
||||
vcd=True
|
||||
if not file_project.format_pal:
|
||||
self.command_var.append("ntsc-vcd")
|
||||
else:
|
||||
self.command_var.append("pal-vcd")
|
||||
elif (self.config.disc_type=="svcd"):
|
||||
if not file_project.format_pal:
|
||||
self.command_var.append("ntsc-svcd")
|
||||
else:
|
||||
self.command_var.append("pal-svcd")
|
||||
elif (self.config.disc_type=="cvd"):
|
||||
if not file_project.format_pal:
|
||||
self.command_var.append("ntsc-svcd")
|
||||
else:
|
||||
self.command_var.append("pal-svcd")
|
||||
|
||||
if (not file_project.no_reencode_audio_video):
|
||||
self.command_var.append("-sn") # no subtitles
|
||||
|
||||
if file_project.copy_sound or file_project.no_reencode_audio_video:
|
||||
self.command_var.append("-acodec")
|
||||
self.command_var.append("copy")
|
||||
|
||||
if file_project.no_reencode_audio_video:
|
||||
self.command_var.append("-vcodec")
|
||||
self.command_var.append("copy")
|
||||
|
||||
if (vcd==False):
|
||||
if not file_project.format_pal:
|
||||
if (file_project.original_fps==24) and ((self.config.disc_type=="dvd")):
|
||||
keyintv=15
|
||||
else:
|
||||
keyintv=18
|
||||
else:
|
||||
keyintv=15
|
||||
|
||||
if not file_project.gop12:
|
||||
self.command_var.append("-g")
|
||||
self.command_var.append(str(keyintv))
|
||||
|
||||
if (self.config.disc_type=="divx") or (self.config.disc_type=="mkv"):
|
||||
self.command_var.append("-g")
|
||||
self.command_var.append("300")
|
||||
elif file_project.gop12 and (file_project.no_reencode_audio_video==False):
|
||||
self.command_var.append("-g")
|
||||
self.command_var.append("12")
|
||||
|
||||
self.command_var.append("-bf")
|
||||
self.command_var.append("2")
|
||||
self.command_var.append("-strict")
|
||||
self.command_var.append("1")
|
||||
|
||||
if video_length != 0:
|
||||
self.command_var.append("-t")
|
||||
self.command_var.append(str(video_length))
|
||||
|
||||
self.command_var.append("-ac")
|
||||
if (file_project.sound5_1) and ((self.config.disc_type=="dvd") or (self.config.disc_type=="divx") or (self.config.disc_type=="mkv")):
|
||||
self.command_var.append("6")
|
||||
else:
|
||||
self.command_var.append("2")
|
||||
|
||||
self.command_var.append("-aspect")
|
||||
self.command_var.append(str(file_project.aspect_ratio_final))
|
||||
|
||||
if self.config.disc_type=="divx":
|
||||
self.command_var.append("-vtag")
|
||||
self.command_var.append("DX50")
|
||||
|
||||
if (file_project.deinterlace == "deinterlace_ffmpeg") and (file_project.no_reencode_audio_video==False) and second_pass:
|
||||
self.command_var.append("-deinterlace")
|
||||
|
||||
if (file_project.no_reencode_audio_video==False) and (vcd==False) and second_pass:
|
||||
self.command_var.append("-s")
|
||||
self.command_var.append(str(file_project.width_final)+"x"+str(file_project.height_final))
|
||||
|
||||
if second_pass:
|
||||
self.command_var.append("-trellis")
|
||||
self.command_var.append("1")
|
||||
self.command_var.append("-mbd")
|
||||
self.command_var.append("2")
|
||||
else:
|
||||
self.command_var.append("-trellis")
|
||||
self.command_var.append("0")
|
||||
self.command_var.append("-mbd")
|
||||
self.command_var.append("0")
|
||||
|
||||
if (vcd == False) and (file_project.no_reencode_audio_video == False):
|
||||
self.command_var.append("-b:a")
|
||||
self.command_var.append(str(file_project.audio_rate_final)+"k")
|
||||
|
||||
self.command_var.append("-b:v")
|
||||
self.command_var.append(str(file_project.video_rate_final)+"k")
|
||||
|
||||
if file_project.two_pass_encoding == True:
|
||||
self.command_var.append("-passlogfile")
|
||||
self.command_var.append(output_file)
|
||||
self.command_var.append("-pass")
|
||||
if pass2:
|
||||
self.command_var.append("2")
|
||||
else:
|
||||
self.command_var.append("1")
|
||||
|
||||
self.command_var.append(output_file)
|
||||
|
||||
|
||||
def create_menu_mpeg(self,n_page,background_music,sound_length,pal,video_rate, audio_rate,output_path, use_mp2):
|
||||
|
||||
self.n_page = n_page
|
||||
self.final_length = float(sound_length)
|
||||
self.text = _("Creating menu %(X)d") % {"X": self.n_page}
|
||||
|
||||
self.command_var=[]
|
||||
self.command_var.append("ffmpeg")
|
||||
|
||||
self.command_var.append("-loop")
|
||||
self.command_var.append("1")
|
||||
|
||||
self.command_var.append("-f")
|
||||
self.command_var.append("image2")
|
||||
self.command_var.append("-i")
|
||||
self.command_var.append(os.path.join(output_path,"menu_"+str(n_page)+"_bg.png"))
|
||||
self.command_var.append("-i")
|
||||
self.command_var.append(background_music)
|
||||
|
||||
self.command_var.append("-y")
|
||||
self.command_var.append("-target")
|
||||
if pal:
|
||||
self.command_var.append("pal-dvd")
|
||||
else:
|
||||
self.command_var.append("ntsc-dvd")
|
||||
self.command_var.append("-acodec")
|
||||
if use_mp2:
|
||||
self.command_var.append("mp2")
|
||||
else:
|
||||
self.command_var.append("ac3")
|
||||
self.command_var.append("-s")
|
||||
if pal:
|
||||
self.command_var.append("720x576")
|
||||
else:
|
||||
self.command_var.append("720x480")
|
||||
self.command_var.append("-g")
|
||||
self.command_var.append("12")
|
||||
self.command_var.append("-b:v")
|
||||
self.command_var.append(str(video_rate)+"k")
|
||||
self.command_var.append("-b:a")
|
||||
self.command_var.append(str(audio_rate)+"k")
|
||||
self.command_var.append("-aspect")
|
||||
self.command_var.append("4:3")
|
||||
|
||||
self.command_var.append("-t")
|
||||
self.command_var.append(str(1+sound_length))
|
||||
|
||||
movie_path = os.path.join(output_path,"menu_"+str(n_page)+".mpg")
|
||||
self.command_var.append(movie_path)
|
||||
|
||||
muxer = devedeng.mux_dvd_menu.mux_dvd_menu()
|
||||
final_path = muxer.create_mpg(n_page,output_path,movie_path)
|
||||
# the muxer process depends of the converter process
|
||||
muxer.add_dependency(self)
|
||||
self.add_child_process(muxer)
|
||||
|
||||
return (final_path)
|
||||
|
||||
def process_stdout(self,data):
|
||||
|
||||
return
|
||||
|
||||
def process_stderr(self,data):
|
||||
|
||||
pos = data[0].find("time=")
|
||||
if (pos != -1):
|
||||
pos+=5
|
||||
pos2 = data[0].find(" ",pos)
|
||||
if (pos2 != -1):
|
||||
parts = data[0][pos:pos2].split(":")
|
||||
t = 0.0
|
||||
for e in parts:
|
||||
t *= 60.0
|
||||
t += float(e)
|
||||
t /= self.final_length
|
||||
self.progress_bar[1].set_fraction(t)
|
||||
self.progress_bar[1].set_text("%.1f%%" % (100.0 * t))
|
||||
147
src/devedeng/ffprobe.py
Normal file
147
src/devedeng/ffprobe.py
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
import subprocess
|
||||
import devedeng.configuration_data
|
||||
import devedeng.executor
|
||||
import os
|
||||
import json
|
||||
|
||||
class ffprobe(devedeng.executor.executor):
|
||||
|
||||
supports_analize = True
|
||||
supports_play = False
|
||||
supports_convert = False
|
||||
supports_menu = False
|
||||
supports_mkiso = False
|
||||
supports_burn = False
|
||||
display_name = "FFPROBE"
|
||||
|
||||
@staticmethod
|
||||
def check_is_installed():
|
||||
try:
|
||||
handle = subprocess.Popen(["ffprobe","-h"], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
|
||||
(stdout, stderr) = handle.communicate()
|
||||
if 0==handle.wait():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def __init__(self):
|
||||
|
||||
devedeng.executor.executor.__init__(self)
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
def process_stdout(self,data):
|
||||
return
|
||||
|
||||
def process_stderr(self,data):
|
||||
return
|
||||
|
||||
def get_film_data(self, file_name):
|
||||
""" processes a file, refered by the FILE_MOVIE movie object, and fills its
|
||||
main data (resolution, FPS, length...) """
|
||||
|
||||
self.audio_list=[]
|
||||
self.audio_streams = 0
|
||||
self.video_list=[]
|
||||
self.video_streams = 0
|
||||
self.original_width = 0
|
||||
self.original_height = 0
|
||||
self.original_length = -1
|
||||
self.original_videorate = 0
|
||||
self.original_audiorate = 0
|
||||
self.original_audiorate_uncompressed = 0
|
||||
self.original_fps = 0
|
||||
self.original_aspect_ratio = 0
|
||||
|
||||
self.original_file_size = os.path.getsize(file_name)
|
||||
|
||||
command_line = ["ffprobe",file_name,"-of","json","-show_streams", "-loglevel", "quiet"]
|
||||
|
||||
(stdout, stderr) = self.launch_process(command_line, False)
|
||||
try:
|
||||
stdout2 = stdout.decode("utf-8")
|
||||
except:
|
||||
stdout2 = stdout.decode("latin1")
|
||||
|
||||
try:
|
||||
video_data = json.loads(stdout2)
|
||||
except:
|
||||
return True # There was an error reading the JSON data
|
||||
|
||||
if not("streams" in video_data):
|
||||
return True # There are no streams!!!!!
|
||||
|
||||
for element in video_data["streams"]:
|
||||
|
||||
if (self.original_length == -1) and ("duration" in element):
|
||||
try:
|
||||
self.original_length = int(float(element["duration"]))
|
||||
except:
|
||||
self.original_length = -1
|
||||
|
||||
if (element["codec_type"]=="video"):
|
||||
self.video_streams += 1
|
||||
self.video_list.append(element["index"])
|
||||
if (self.video_streams == 1):
|
||||
self.original_width = int(float(element["width"]))
|
||||
self.original_height = int(float(element["height"]))
|
||||
if ("bit_rate" in element):
|
||||
self.original_videorate = int(float(element["bit_rate"]))/1000
|
||||
self.original_fps = self.get_division(element["avg_frame_rate"])
|
||||
if ("display_aspect_ratio" in element):
|
||||
self.original_aspect_ratio = self.get_division(element["display_aspect_ratio"])
|
||||
|
||||
elif (element["codec_type"]=="audio"):
|
||||
self.audio_streams += 1
|
||||
self.audio_list.append(element["index"])
|
||||
if (self.audio_streams == 1):
|
||||
if ("bit_rate" in element):
|
||||
self.original_audiorate = int(float(element["bit_rate"]))/1000
|
||||
self.original_audiorate_uncompressed = int(float(element["sample_rate"]))
|
||||
|
||||
self.original_size = str(self.original_width)+"x"+str(self.original_height)
|
||||
if (self.original_aspect_ratio == None) or (self.original_aspect_ratio <= 1.0):
|
||||
if (self.original_height != 0):
|
||||
self.original_aspect_ratio = (float(self.original_width))/(float(self.original_height))
|
||||
|
||||
if (self.original_aspect_ratio != None):
|
||||
self.original_aspect_ratio = (float(int(self.original_aspect_ratio*1000.0)))/1000.0
|
||||
|
||||
if (len(self.video_list) == 0):
|
||||
return True # the file is not a video file; maybe an audio-only file or another thing
|
||||
|
||||
if self.original_length == -1: # if it was unable to detect the duration, try to use the human readable format
|
||||
command_line = ["ffprobe",file_name]
|
||||
(stdout, stderr) = self.launch_process(command_line, False)
|
||||
try:
|
||||
stdout2 = stdout.decode("utf-8") + "\n" + stderr.decode("utf-8")
|
||||
except:
|
||||
stdout2 = stdout.decode("latin1") + "\n" + stderr.decode("latin1")
|
||||
|
||||
for line in stdout2.split("\n"):
|
||||
line = line.strip()
|
||||
if line.startswith("Duration: "):
|
||||
self.original_length = self.get_time(line[10:])
|
||||
break
|
||||
|
||||
return False # no error
|
||||
54
src/devedeng/file_copy.py
Normal file
54
src/devedeng/file_copy.py
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
import os
|
||||
import devedeng.configuration_data
|
||||
import devedeng.executor
|
||||
|
||||
class file_copy(devedeng.executor.executor):
|
||||
|
||||
def __init__(self,input_path, output_path):
|
||||
|
||||
devedeng.executor.executor.__init__(self)
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
self.text = _("Copying file %(X)s") % {"X": os.path.basename(input_path)}
|
||||
|
||||
self.command_var=[]
|
||||
self.command_var.append("copy_files_verbose.py")
|
||||
self.command_var.append(input_path)
|
||||
self.command_var.append(output_path)
|
||||
|
||||
|
||||
def process_stdout(self,data):
|
||||
|
||||
if (data == None) or (len(data) == 0):
|
||||
return
|
||||
if (data[0].startswith("Copied ")):
|
||||
pos = data[0].find("%")
|
||||
if (pos == -1):
|
||||
return
|
||||
p = float(data[0][7:pos])
|
||||
self.progress_bar[1].set_fraction(p/ 100.0)
|
||||
self.progress_bar[1].set_text("%.1f%%" % (p))
|
||||
return
|
||||
|
||||
def process_stderr(self,data):
|
||||
|
||||
return
|
||||
733
src/devedeng/file_movie.py
Normal file
733
src/devedeng/file_movie.py
Normal file
|
|
@ -0,0 +1,733 @@
|
|||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
from gi.repository import Gtk,GObject
|
||||
import os
|
||||
|
||||
import devedeng.configuration_data
|
||||
import devedeng.interface_manager
|
||||
import devedeng.converter
|
||||
import devedeng.ask_subtitles
|
||||
import devedeng.preview
|
||||
import devedeng.file_copy
|
||||
import devedeng.subtitles_mux
|
||||
|
||||
class file_movie(devedeng.interface_manager.interface_manager):
|
||||
|
||||
__gsignals__ = {'title_changed': (GObject.SIGNAL_RUN_FIRST, None,(str,))}
|
||||
|
||||
def __init__(self,file_name, list_files = None):
|
||||
|
||||
self.list_files = list_files
|
||||
|
||||
devedeng.interface_manager.interface_manager.__init__(self)
|
||||
|
||||
self.wfile_properties = None
|
||||
self.builder = None
|
||||
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
self.set_type(None, self.config.disc_type)
|
||||
self.config.connect('disc_type',self.set_type)
|
||||
|
||||
if list_files == None:
|
||||
self.add_text("file_name", file_name)
|
||||
self.add_text("title_name", os.path.splitext(os.path.basename(file_name))[0])
|
||||
self.add_label("original_size",None)
|
||||
self.add_label("original_length",None)
|
||||
self.add_label("original_videorate",None)
|
||||
self.add_label("original_audiorate",None)
|
||||
self.add_label("original_aspect_ratio",None)
|
||||
self.add_label("original_fps",None)
|
||||
self.add_toggle("show_in_menu", True)
|
||||
else:
|
||||
self.original_aspect_ratio = 1.777 # dummy value
|
||||
|
||||
self.add_dualtoggle("format_pal","format_ntsc",self.config.PAL)
|
||||
self.add_toggle("video_rate_automatic", True)
|
||||
self.add_toggle("audio_rate_automatic", True)
|
||||
self.add_toggle("divide_in_chapters", True)
|
||||
self.add_toggle("force_subtitles", False)
|
||||
self.add_toggle("mirror_horizontal", False)
|
||||
self.add_toggle("mirror_vertical", False)
|
||||
self.add_toggle("two_pass_encoding", False)
|
||||
self.add_toggle("sound5_1", False)
|
||||
self.add_toggle("copy_sound", False)
|
||||
self.add_toggle("is_mpeg_ps", False)
|
||||
self.add_toggle("no_reencode_audio_video", False)
|
||||
if (self.disc_type == "divx") or (self.disc_type == "mkv"):
|
||||
self.add_toggle("gop12", False)
|
||||
else:
|
||||
self.add_toggle("gop12", True)
|
||||
|
||||
self.add_group("final_size_pal", ["size_auto", "size_1920x1080", "size_1280x720", "size_720x576", "size_704x576", "size_480x576","size_352x576", "size_352x288"], "size_auto")
|
||||
self.add_group("final_size_ntsc", ["size_auto_ntsc", "size_1920x1080_ntsc", "size_1280x720_ntsc", "size_720x480_ntsc", "size_704x480_ntsc", "size_480x480_ntsc","size_352x480_ntsc", "size_352x240_ntsc"], "size_auto_ntsc")
|
||||
self.add_group("aspect_ratio", ["aspect_auto", "aspect_classic", "aspect_wide"], "aspect_auto")
|
||||
self.add_group("scaling", ["add_black_bars", "scale_picture" ,"cut_picture"], "add_black_bars")
|
||||
self.add_group("rotation",["rotation_0","rotation_90","rotation_180","rotation_270"], "rotation_0")
|
||||
self.add_group("deinterlace", ["deinterlace_none", "deinterlace_ffmpeg", "deinterlace_yadif"], "deinterlace_none")
|
||||
self.add_group("actions", ["action_stop","action_play_first","action_play_previous","action_play_again","action_play_next","action_play_last"], "action_stop")
|
||||
|
||||
self.add_integer_adjustment("volume", 100)
|
||||
if (self.disc_type == "dvd"):
|
||||
self.add_integer_adjustment("video_rate", 5000)
|
||||
elif (self.disc_type == "vcd"):
|
||||
self.add_integer_adjustment("video_rate", 1152)
|
||||
else:
|
||||
self.add_integer_adjustment("video_rate", 2000)
|
||||
|
||||
self.add_integer_adjustment("audio_rate", 224)
|
||||
self.add_integer_adjustment("subt_font_size", 28)
|
||||
self.add_float_adjustment("audio_delay", 0.0)
|
||||
self.add_integer_adjustment("chapter_size", 5)
|
||||
|
||||
self.add_colorbutton("subt_fill_color", self.config.subt_fill_color)
|
||||
self.add_colorbutton("subt_outline_color", self.config.subt_outline_color)
|
||||
self.add_float_adjustment("subt_thickness", self.config.subt_outline_thickness)
|
||||
|
||||
if list_files == None:
|
||||
self.add_list("subtitles_list")
|
||||
else:
|
||||
self.add_list("files_to_set")
|
||||
for e in list_files:
|
||||
self.files_to_set.append([e.title_name, e])
|
||||
|
||||
self.add_show_hide("format_pal", ["size_pal"], ["size_ntsc"])
|
||||
|
||||
self.add_enable_disable("divide_in_chapters", ["chapter_size_spinbutton"], [])
|
||||
self.add_enable_disable("video_rate_automatic", [], ["video_spinbutton"])
|
||||
self.add_enable_disable("audio_rate_automatic", [], ["audio_spinbutton"])
|
||||
self.add_enable_disable("sound5_1", ["copy_sound"], [])
|
||||
|
||||
if (self.disc_type == "dvd"):
|
||||
self.add_enable_disable("aspect_wide", [], ["size_704x576", "size_480x576","size_352x576", "size_352x288","size_704x480_ntsc", "size_480x480_ntsc","size_352x480_ntsc", "size_352x240_ntsc"])
|
||||
|
||||
self.add_enable_disable("copy_sound", [], ["audio_delay_spinbutton","audio_rate_automatic","audio_spinbutton","spinbutton_volume","scale_volume","reset_volume"])
|
||||
|
||||
common_elements = ["gop12","video_rate_automatic","video_spinbutton","audio_rate_automatic","audio_spinbutton","format_pal","format_ntsc","spinbutton_volume","scale_volume","reset_volume",
|
||||
"size_auto", "size_1920x1080", "size_1280x720", "size_720x576", "size_704x576", "size_480x576","size_352x576", "size_352x288",
|
||||
"size_auto_ntsc", "size_1920x1080_ntsc", "size_1280x720_ntsc", "size_720x480_ntsc", "size_704x480_ntsc", "size_480x480_ntsc","size_352x480_ntsc", "size_352x240_ntsc",
|
||||
"aspect_auto","aspect_classic","aspect_wide","mirror_horizontal","mirror_vertical","add_black_bars","scale_picture","cut_picture",
|
||||
"rotation_0","rotation_90","rotation_180","rotation_270","two_pass_encoding","deinterlace_none","deinterlace_ffmpeg","deinterlace_yadif",
|
||||
"audio_delay_spinbutton","sound5_1","copy_sound"]
|
||||
|
||||
is_mpeg_ps_list = common_elements[:]
|
||||
is_mpeg_ps_list.append("no_reencode_audio_video")
|
||||
is_mpeg_ps_list.append("font_size_spinbutton")
|
||||
is_mpeg_ps_list.append("force_subtitles")
|
||||
is_mpeg_ps_list.append("add_subtitles")
|
||||
is_mpeg_ps_list.append("del_subtitles")
|
||||
no_reencode_audio_video_list = common_elements[:]
|
||||
no_reencode_audio_video_list.append("is_mpeg_ps")
|
||||
|
||||
self.add_enable_disable("is_mpeg_ps", [], is_mpeg_ps_list)
|
||||
self.add_enable_disable("no_reencode_audio_video", [], no_reencode_audio_video_list)
|
||||
|
||||
if list_files == None:
|
||||
cv = devedeng.converter.converter.get_converter()
|
||||
film_analizer = (cv.get_film_analizer())()
|
||||
if (film_analizer.get_film_data(self.file_name)):
|
||||
self.error = True
|
||||
else:
|
||||
self.error = False
|
||||
self.audio_list = film_analizer.audio_list
|
||||
self.video_list = film_analizer.video_list
|
||||
self.audio_streams = film_analizer.audio_streams
|
||||
self.video_streams = film_analizer.video_streams
|
||||
self.original_width = film_analizer.original_width
|
||||
self.original_height = film_analizer.original_height
|
||||
self.original_length = film_analizer.original_length
|
||||
self.original_size = film_analizer.original_size
|
||||
self.original_aspect_ratio = film_analizer.original_aspect_ratio
|
||||
self.original_videorate = film_analizer.original_videorate
|
||||
self.original_audiorate = film_analizer.original_audiorate
|
||||
|
||||
self.original_audiorate_uncompressed = film_analizer.original_audiorate_uncompressed
|
||||
self.original_fps = film_analizer.original_fps
|
||||
self.original_file_size = film_analizer.original_file_size
|
||||
|
||||
if self.original_audiorate <= 0:
|
||||
# just a guess, but usually correct
|
||||
self.original_audiorate = 224
|
||||
if self.original_videorate <= 0:
|
||||
# presume that there are only video and audio streams
|
||||
self.original_videorate = ((8 * self.original_file_size) / self.original_length) - (self.original_audiorate * self.audio_streams)
|
||||
|
||||
self.width_midle = -1
|
||||
self.height_midle = -1
|
||||
self.width_final = -1
|
||||
self.height_final = -1
|
||||
self.video_rate_auto = self.video_rate
|
||||
self.audio_rate_auto = self.audio_rate
|
||||
self.video_rate_final = self.video_rate
|
||||
self.audio_rate_final = self.audio_rate
|
||||
self.aspect_ratio_final = None
|
||||
self.converted_filename = None
|
||||
|
||||
|
||||
def set_title(self,new_title):
|
||||
self.title_name = new_title
|
||||
self.emit('title_changed',self.title_name)
|
||||
|
||||
|
||||
def get_duration(self):
|
||||
return self.original_length
|
||||
|
||||
|
||||
def get_estimated_size(self):
|
||||
""" Returns the estimated final file size, in kBytes, based on the final audio and video rate, and the subtitles """
|
||||
|
||||
self.set_final_rates()
|
||||
self.set_final_size_aspect()
|
||||
|
||||
if self.is_mpeg_ps:
|
||||
estimated_size = self.original_file_size / 1000
|
||||
else:
|
||||
# let's asume 8kbps for each subtitle
|
||||
sub_rate = 8 * len(self.subtitles_list)
|
||||
estimated_size = ((self.video_rate_final + (self.audio_rate_final * self.audio_streams) + sub_rate) * self.original_length) / 8
|
||||
|
||||
return estimated_size
|
||||
|
||||
|
||||
def get_size_data(self):
|
||||
|
||||
estimated_size = self.get_estimated_size()
|
||||
if self.is_mpeg_ps or self.no_reencode_audio_video:
|
||||
videorate_fixed_size = True
|
||||
else:
|
||||
videorate_fixed_size = False
|
||||
|
||||
# let's asume 8kbps for each subtitle
|
||||
sub_rate = 8 * len(self.subtitles_list)
|
||||
|
||||
return estimated_size, videorate_fixed_size, self.audio_rate_final * self.audio_streams, sub_rate, self.width_final, self.height_final, self.original_length
|
||||
|
||||
|
||||
def set_auto_video_audio_rate(self, new_video_rate, new_audio_rate):
|
||||
|
||||
self.video_rate_auto = int(new_video_rate)
|
||||
self.audio_rate_auto = int(new_audio_rate)
|
||||
|
||||
|
||||
def get_max_resolution(self,rx,ry,aspect):
|
||||
|
||||
tmpx = ry*aspect
|
||||
tmpy = rx/aspect
|
||||
if (tmpx > rx):
|
||||
return tmpx,ry
|
||||
else:
|
||||
return rx,tmpy
|
||||
|
||||
|
||||
def set_final_rates(self):
|
||||
|
||||
if (self.disc_type == "divx") or (self.disc_type == "mkv"):
|
||||
self.audio_rate_auto = 192
|
||||
elif (self.disc_type == "vcd") or (self.disc_type == "svcd") or (self.disc_type == "cvd"):
|
||||
self.audio_rate_auto = 224
|
||||
else: # dvd
|
||||
if self.sound5_1:
|
||||
self.audio_rate_auto = 384
|
||||
else:
|
||||
self.audio_rate_auto = 224
|
||||
|
||||
if self.is_mpeg_ps or self.no_reencode_audio_video:
|
||||
self.video_rate_final = self.original_videorate
|
||||
self.audio_rate_final = self.original_audiorate
|
||||
else:
|
||||
if self.video_rate_automatic:
|
||||
self.video_rate_final = self.video_rate_auto
|
||||
else:
|
||||
self.video_rate_final = self.video_rate
|
||||
|
||||
if self.copy_sound:
|
||||
self.audio_rate_final = self.original_audiorate
|
||||
else:
|
||||
if self.audio_rate_automatic:
|
||||
self.audio_rate_final = self.audio_rate_auto
|
||||
else:
|
||||
self.audio_rate_final = self.audio_rate
|
||||
|
||||
|
||||
def set_final_size_aspect(self):
|
||||
|
||||
if self.is_mpeg_ps:
|
||||
self.width_midle = self.original_width
|
||||
self.width_final = self.original_width
|
||||
self.height_midle = self.original_height
|
||||
self.height_final = self.original_height
|
||||
self.aspect_ratio_final = self.original_aspect_ratio
|
||||
return
|
||||
|
||||
if self.format_pal:
|
||||
final_size = self.final_size_pal
|
||||
else:
|
||||
final_size = self.final_size_ntsc[:-5] # remove the "_ntsc" from the string
|
||||
|
||||
# for divx or matroska, if the size and the aspect ratio is automatic, just don't change them
|
||||
if ((self.disc_type == "divx") or (self.disc_type == "mkv")) and (final_size == "size_auto") and (self.aspect_ratio == "aspect_auto"):
|
||||
self.width_midle = self.original_width
|
||||
self.width_final = self.original_width
|
||||
self.height_midle = self.original_height
|
||||
self.height_final = self.original_height
|
||||
self.aspect_ratio_final = self.original_aspect_ratio
|
||||
return
|
||||
|
||||
# The steps are:
|
||||
# - Decide the final aspect ratio
|
||||
# - Calculate the midle size: the original video will be cut to this size, or black bars will be added
|
||||
# - Calculate the final size: the midle video will be scaled to this size
|
||||
|
||||
aspect_wide = False
|
||||
# first, decide the final aspect ratio
|
||||
if (self.disc_type == "vcd") or (self.disc_type == "svcd") or (self.disc_type == "cvd"):
|
||||
self.aspect_ratio_final = 4.0/3.0
|
||||
else:
|
||||
if (self.aspect_ratio == "aspect_auto"):
|
||||
if (self.disc_type != "dvd"):
|
||||
self.aspect_ratio_final = self.original_aspect_ratio
|
||||
else:
|
||||
if self.original_aspect_ratio >= 1.7:
|
||||
self.aspect_ratio_final = 16.0/9.0
|
||||
aspect_wide = True
|
||||
else:
|
||||
self.aspect_ratio_final = 4.0/3.0
|
||||
elif (self.aspect_ratio == "aspect_classic"):
|
||||
self.aspect_ratio_final = 4.0/3.0
|
||||
else:
|
||||
self.aspect_ratio_final = 16.0/9.0
|
||||
aspect_wide = True
|
||||
|
||||
# now, the final resolution
|
||||
if self.disc_type == "vcd":
|
||||
self.width_final = 352
|
||||
if (self.format_pal):
|
||||
self.height_final = 288
|
||||
else:
|
||||
self.height_final = 240
|
||||
else:
|
||||
if final_size == "size_auto":
|
||||
if self.disc_type == "svcd":
|
||||
self.width_final =480
|
||||
if (self.format_pal):
|
||||
self.height_final = 576
|
||||
else:
|
||||
self.height_final = 480
|
||||
elif self.disc_type == "cvd":
|
||||
self.width_final =352
|
||||
if (self.format_pal):
|
||||
self.height_final = 576
|
||||
else:
|
||||
self.height_final = 480
|
||||
elif self.disc_type == "dvd":
|
||||
if aspect_wide:
|
||||
self.width_final = 720
|
||||
if (self.format_pal):
|
||||
self.height_final = 576
|
||||
else:
|
||||
self.height_final = 480
|
||||
else:
|
||||
tx, ty = self.get_max_resolution(self.original_width,self.original_height,self.original_aspect_ratio)
|
||||
if (self.format_pal):
|
||||
th = 576
|
||||
th2 = 288
|
||||
else:
|
||||
th = 480
|
||||
th2 = 240
|
||||
if ( tx <= 352 ) and (ty <= th2):
|
||||
self.width_final = 352
|
||||
self.height_final = th2
|
||||
elif (tx <= 352) and (ty <= th):
|
||||
self.width_final = 352
|
||||
self.height_final = th
|
||||
elif (tx <= 704) and (ty <= th):
|
||||
self.width_final = 704
|
||||
self.height_final = th
|
||||
else:
|
||||
self.width_final = 720
|
||||
self.height_final = th
|
||||
else:
|
||||
self.width_final , self.height_final = self.get_max_resolution(self.original_width,self.original_height,self.original_aspect_ratio)
|
||||
else:
|
||||
values = final_size[5:].split("x")
|
||||
self.width_final = int(values[0])
|
||||
self.height_final = int(values[1])
|
||||
|
||||
self.width_final = int(self.width_final)
|
||||
self.height_final = int(self.height_final)
|
||||
|
||||
# finally, calculate the midle size
|
||||
|
||||
if (self.rotation == "rotation_90") or (self.rotation == "rotation_270"):
|
||||
midle_aspect_ratio = 1.0 / self.original_aspect_ratio
|
||||
else:
|
||||
midle_aspect_ratio = self.original_aspect_ratio
|
||||
|
||||
if self.scaling == "scale_picture":
|
||||
self.width_midle = int(self.original_width)
|
||||
self.height_midle = int(self.original_height)
|
||||
elif self.scaling == "add_black_bars":
|
||||
if midle_aspect_ratio > self.aspect_ratio_final: # add horizontal black bars, at top and bottom
|
||||
self.width_midle = int(self.original_width)
|
||||
self.height_midle = int(self.original_height * midle_aspect_ratio / self.aspect_ratio_final)
|
||||
else: # add vertical black bars, at left and right
|
||||
self.width_midle = int(self.original_width * self.aspect_ratio_final / midle_aspect_ratio)
|
||||
self.height_midle = int(self.original_height)
|
||||
else: # cut picture
|
||||
if midle_aspect_ratio > self.aspect_ratio_final:
|
||||
self.width_midle = int(self.original_width * self.aspect_ratio_final / midle_aspect_ratio)
|
||||
self.height_midle = int(self.original_height)
|
||||
else:
|
||||
self.width_midle = int(self.original_width)
|
||||
self.height_midle = int(self.original_height * midle_aspect_ratio / self.aspect_ratio_final)
|
||||
|
||||
|
||||
def set_type(self,obj = None,disc_type = None):
|
||||
|
||||
if (disc_type != None):
|
||||
self.disc_type = disc_type
|
||||
|
||||
|
||||
def delete_file(self):
|
||||
|
||||
return
|
||||
|
||||
|
||||
def properties(self):
|
||||
|
||||
if (self.wfile_properties != None):
|
||||
self.wfile_properties.present()
|
||||
return
|
||||
|
||||
self.builder = Gtk.Builder()
|
||||
self.builder.set_translation_domain(self.config.gettext_domain)
|
||||
|
||||
self.builder.add_from_file(os.path.join(self.config.glade,"wfile_properties.ui"))
|
||||
self.builder.connect_signals(self)
|
||||
self.wfile_properties = self.builder.get_object("file_properties")
|
||||
self.wfile_properties.show_all()
|
||||
|
||||
self.wframe_title = self.builder.get_object("frame_title")
|
||||
self.wframe_fileinfo = self.builder.get_object("frame_fileinfo")
|
||||
self.wframe_multiproperties = self.builder.get_object("frame_multiproperties")
|
||||
self.wtreeview_multiproperties = self.builder.get_object("treeview_multiproperties")
|
||||
self.wbutton_preview = self.builder.get_object("button_preview")
|
||||
|
||||
self.wshow_in_menu = self.builder.get_object("show_in_menu")
|
||||
|
||||
self.wnotebook = self.builder.get_object("notebook")
|
||||
|
||||
# elements in page GENERAL
|
||||
self.wformat_pal = self.builder.get_object("format_pal")
|
||||
self.wformat_ntsc = self.builder.get_object("format_ntsc")
|
||||
self.wframe_video_rate = self.builder.get_object("frame_video_rate")
|
||||
self.wframe_audio_rate = self.builder.get_object("frame_audio_rate")
|
||||
self.waudio_rate = self.builder.get_object("audio_rate")
|
||||
self.wframe_division_chapters = self.builder.get_object("frame_division_chapters")
|
||||
|
||||
if (self.disc_type == "dvd") or (self.disc_type == "divx") or (self.disc_type == "mkv"):
|
||||
self.waudio_rate.set_upper(448.0)
|
||||
else:
|
||||
self.waudio_rate.set_upper(384.0)
|
||||
|
||||
# elements in page SUBTITLES
|
||||
self.wsubtitles_list = self.builder.get_object("subtitles_list")
|
||||
self.wtreview_subtitles = self.builder.get_object("treeview_subtitles")
|
||||
self.wscrolledwindow_subtitles = self.builder.get_object("scrolledwindow_subtitles")
|
||||
self.wadd_subtitles = self.builder.get_object("add_subtitles")
|
||||
self.wdel_subtitles = self.builder.get_object("del_subtitles")
|
||||
|
||||
selection = self.wtreview_subtitles.get_selection()
|
||||
selection.set_mode(Gtk.SelectionMode.BROWSE)
|
||||
|
||||
# elements in page VIDEO OPTIONS
|
||||
self.wsize_1920x1080 = self.builder.get_object("size_1920x1080")
|
||||
self.wsize_1280x720 = self.builder.get_object("size_1280x720")
|
||||
self.wsize_1920x1080_ntsc = self.builder.get_object("size_1920x1080_ntsc")
|
||||
self.wsize_1280x720_ntsc = self.builder.get_object("size_1280x720_ntsc")
|
||||
self.wframe_final_size = self.builder.get_object("frame_final_size")
|
||||
self.wframe_aspect_ratio = self.builder.get_object("frame_aspect_ratio")
|
||||
self.waspect_classic = self.builder.get_object("aspect_classic")
|
||||
self.waspect_wide = self.builder.get_object("aspect_wide")
|
||||
self.wadd_black_bars_pic = self.builder.get_object("add_black_bars_pic")
|
||||
self.wscale_picture_pic = self.builder.get_object("scale_picture_pic")
|
||||
self.wcut_picture_pic = self.builder.get_object("cut_picture_pic")
|
||||
|
||||
# elements in page AUDIO
|
||||
self.wsound5_1 = self.builder.get_object("sound5_1")
|
||||
self.wcopy_sound = self.builder.get_object("copy_sound")
|
||||
|
||||
# Adjust the interface UI to the kind of disc
|
||||
|
||||
if (self.disc_type == 'dvd'):
|
||||
self.wsize_1920x1080.hide()
|
||||
self.wsize_1280x720.hide()
|
||||
self.wsize_1920x1080_ntsc.hide()
|
||||
self.wsize_1280x720_ntsc.hide()
|
||||
elif (self.disc_type == 'vcd'):
|
||||
self.wshow_in_menu.hide()
|
||||
self.wframe_video_rate.hide()
|
||||
self.wframe_audio_rate.hide()
|
||||
self.wframe_division_chapters.hide()
|
||||
self.wframe_final_size.hide()
|
||||
self.wframe_aspect_ratio.hide()
|
||||
self.wsound5_1.hide()
|
||||
self.wcopy_sound.hide()
|
||||
self.wnotebook.remove_page(5)
|
||||
elif (self.disc_type == 'svcd'):
|
||||
self.wsize_1920x1080.hide()
|
||||
self.wsize_1280x720.hide()
|
||||
self.wsize_1920x1080_ntsc.hide()
|
||||
self.wsize_1280x720_ntsc.hide()
|
||||
self.wshow_in_menu.hide()
|
||||
self.wframe_division_chapters.hide()
|
||||
self.wframe_aspect_ratio.hide()
|
||||
self.wsound5_1.hide()
|
||||
self.wcopy_sound.hide()
|
||||
self.wnotebook.remove_page(5)
|
||||
elif (self.disc_type == 'cvd'):
|
||||
self.wsize_1920x1080.hide()
|
||||
self.wsize_1280x720.hide()
|
||||
self.wsize_1920x1080_ntsc.hide()
|
||||
self.wsize_1280x720_ntsc.hide()
|
||||
self.wshow_in_menu.hide()
|
||||
self.wframe_division_chapters.hide()
|
||||
self.wframe_aspect_ratio.hide()
|
||||
self.wsound5_1.hide()
|
||||
self.wcopy_sound.hide()
|
||||
self.wnotebook.remove_page(5)
|
||||
elif (self.disc_type == 'divx'):
|
||||
self.wshow_in_menu.hide()
|
||||
self.wframe_division_chapters.hide()
|
||||
self.wsound5_1.hide()
|
||||
self.wcopy_sound.hide()
|
||||
self.wnotebook.remove_page(5)
|
||||
self.wnotebook.remove_page(1)
|
||||
elif (self.disc_type == 'mkv'):
|
||||
self.wshow_in_menu.hide()
|
||||
self.wframe_division_chapters.hide()
|
||||
self.wnotebook.remove_page(5)
|
||||
|
||||
if self.list_files == None:
|
||||
self.wframe_title.show()
|
||||
self.wframe_fileinfo.show()
|
||||
self.wframe_multiproperties.hide()
|
||||
else:
|
||||
self.wframe_title.hide()
|
||||
self.wframe_fileinfo.hide()
|
||||
self.wframe_multiproperties.show()
|
||||
self.wscrolledwindow_subtitles.hide()
|
||||
self.wbutton_preview.hide()
|
||||
self.wadd_subtitles.hide()
|
||||
self.wdel_subtitles.hide()
|
||||
sel = self.wtreeview_multiproperties.get_selection()
|
||||
sel.set_mode(Gtk.SelectionMode.MULTIPLE)
|
||||
self.wtreeview_multiproperties.set_rubber_banding(True)
|
||||
|
||||
|
||||
self.save_ui()
|
||||
self.update_ui(self.builder)
|
||||
self.on_aspect_classic_toggled(None)
|
||||
self.on_treeview_subtitles_cursor_changed(None)
|
||||
|
||||
|
||||
def on_aspect_classic_toggled(self,b):
|
||||
|
||||
status1 = self.waspect_classic.get_active()
|
||||
status2 = self.waspect_wide.get_active()
|
||||
if (status1):
|
||||
final_aspect = 4.0/3.0
|
||||
elif (status2):
|
||||
final_aspect = 16.0/9.0
|
||||
else:
|
||||
if self.original_aspect_ratio >= 1.7:
|
||||
final_aspect = 16.0/9.0
|
||||
else:
|
||||
final_aspect = 4.0/3.0
|
||||
if (final_aspect < self.original_aspect_ratio):
|
||||
self.wadd_black_bars_pic.set_from_file(os.path.join(self.config.pic_path,"to_classic_blackbars.png"))
|
||||
self.wcut_picture_pic.set_from_file(os.path.join(self.config.pic_path,"to_classic_cut.png"))
|
||||
self.wscale_picture_pic.set_from_file(os.path.join(self.config.pic_path,"to_classic_scale.png"))
|
||||
else:
|
||||
self.wadd_black_bars_pic.set_from_file(os.path.join(self.config.pic_path,"to_wide_blackbars.png"))
|
||||
self.wcut_picture_pic.set_from_file(os.path.join(self.config.pic_path,"to_wide_cut.png"))
|
||||
self.wscale_picture_pic.set_from_file(os.path.join(self.config.pic_path,"to_wide_scale.png"))
|
||||
|
||||
|
||||
def on_button_accept_clicked(self,b):
|
||||
|
||||
self.store_ui(self.builder)
|
||||
self.config.subt_fill_color = self.subt_fill_color
|
||||
self.config.subt_outline_color = self.subt_outline_color
|
||||
self.config.subt_outline_thickness = self.subt_thickness
|
||||
if self.list_files == None:
|
||||
# editing file properties
|
||||
self.set_final_rates()
|
||||
self.set_final_size_aspect()
|
||||
self.emit('title_changed',self.title_name)
|
||||
else:
|
||||
# editing properties for a group of files
|
||||
data = self.store_file()
|
||||
sel = self.wtreeview_multiproperties.get_selection()
|
||||
model, pathlist = sel.get_selected_rows()
|
||||
for file_path in pathlist:
|
||||
obj = model[file_path][1]
|
||||
obj.restore_file(data)
|
||||
|
||||
self.wfile_properties.destroy()
|
||||
self.wfile_properties = None
|
||||
self.builder = None
|
||||
|
||||
|
||||
def on_button_cancel_clicked(self,b):
|
||||
|
||||
if self.list_files == None:
|
||||
self.restore_ui()
|
||||
|
||||
self.wfile_properties.destroy()
|
||||
self.wfile_properties = None
|
||||
self.builder = None
|
||||
|
||||
|
||||
def on_add_subtitles_clicked(self,b):
|
||||
|
||||
subt = devedeng.ask_subtitles.ask_subtitles()
|
||||
if (subt.run()):
|
||||
self.wsubtitles_list.append([subt.filename, subt.encoding, subt.language, subt.put_upper])
|
||||
|
||||
|
||||
def get_selected_subtitle(self):
|
||||
|
||||
selection = self.wtreview_subtitles.get_selection()
|
||||
model, treeiter = selection.get_selected()
|
||||
|
||||
if treeiter != None:
|
||||
return ( (model, treeiter) )
|
||||
else:
|
||||
return ( (None, None) )
|
||||
|
||||
|
||||
def on_del_subtitles_clicked(self,b):
|
||||
|
||||
model, treeiter = self.get_selected_subtitle()
|
||||
if (model != None):
|
||||
model.remove(treeiter)
|
||||
|
||||
|
||||
def on_treeview_subtitles_cursor_changed(self,b):
|
||||
|
||||
model, treeiter = self.get_selected_subtitle()
|
||||
if (model == None):
|
||||
self.wdel_subtitles.set_sensitive(False)
|
||||
else:
|
||||
self.wdel_subtitles.set_sensitive(True)
|
||||
|
||||
|
||||
def do_conversion(self, output_path, duration = 0):
|
||||
|
||||
self.converted_filename = output_path
|
||||
if self.is_mpeg_ps:
|
||||
converter = devedeng.file_copy.file_copy(self.file_name,output_path)
|
||||
else:
|
||||
self.set_final_size_aspect()
|
||||
self.set_final_rates()
|
||||
|
||||
cv = devedeng.converter.converter.get_converter()
|
||||
disc_converter = cv.get_disc_converter()
|
||||
converter = disc_converter()
|
||||
converter.convert_file(self,output_path,duration)
|
||||
|
||||
if len(self.subtitles_list) != 0:
|
||||
last_process = converter
|
||||
#if duration == 0:
|
||||
# it seems that SPUMUX always fills the entire subtitles
|
||||
duration2 = self.original_length
|
||||
#else:
|
||||
# duration2 = duration
|
||||
stream_id = 0
|
||||
for subt in self.subtitles_list:
|
||||
subt_file = subt[0]
|
||||
subt_codepage = subt[1]
|
||||
subt_lang = subt[2]
|
||||
subt_upper = subt[3]
|
||||
if self.aspect_ratio_final >= 1.7:
|
||||
final_aspect = "16:9"
|
||||
else:
|
||||
final_aspect = "4:3"
|
||||
subt_mux = devedeng.subtitles_mux.subtitles_mux()
|
||||
subt_mux.multiplex_subtitles( output_path, subt_file, subt_codepage, subt_lang, subt_upper,
|
||||
self.subt_font_size,self.format_pal,self.force_subtitles,
|
||||
final_aspect, duration2, stream_id,
|
||||
self.subt_fill_color, self.subt_outline_color, self.subt_thickness)
|
||||
subt_mux.add_dependency(last_process)
|
||||
converter.add_child_process(subt_mux)
|
||||
last_process = subt_mux
|
||||
stream_id += 1
|
||||
|
||||
return converter
|
||||
|
||||
|
||||
def on_button_preview_clicked(self,b):
|
||||
self.store_ui(self.builder)
|
||||
self.do_preview()
|
||||
|
||||
|
||||
def do_preview(self):
|
||||
|
||||
wpreview = devedeng.preview.preview_window()
|
||||
if (wpreview.run() == False):
|
||||
return
|
||||
|
||||
run_window = devedeng.runner.runner()
|
||||
p = self.do_conversion(os.path.join(self.config.tmp_folder,"movie_preview.mpg"),wpreview.lvalue)
|
||||
run_window.add_process(p)
|
||||
run_window.connect("done",self.preview_done)
|
||||
run_window.run()
|
||||
|
||||
|
||||
def preview_done(self,o,retval):
|
||||
|
||||
if (retval == 0):
|
||||
cv = devedeng.converter.converter.get_converter()
|
||||
disc_player = (cv.get_film_player())()
|
||||
disc_player.play_film(os.path.join(self.config.tmp_folder,"movie_preview.mpg"))
|
||||
|
||||
|
||||
def store_file(self):
|
||||
|
||||
data = self.serialize()
|
||||
if "files_to_set" in data:
|
||||
del data["files_to_set"]
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def restore_file(self,data):
|
||||
|
||||
self.unserialize(data)
|
||||
|
||||
|
||||
def on_select_all_clicked(self,b):
|
||||
|
||||
sel = self.wtreeview_multiproperties.get_selection()
|
||||
sel.select_all()
|
||||
|
||||
|
||||
def on_unselect_all_clicked(self,b):
|
||||
|
||||
sel = self.wtreeview_multiproperties.get_selection()
|
||||
sel.unselect_all()
|
||||
83
src/devedeng/genisoimage.py
Normal file
83
src/devedeng/genisoimage.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
import os
|
||||
import devedeng.configuration_data
|
||||
import subprocess
|
||||
import devedeng.executor
|
||||
|
||||
class genisoimage(devedeng.executor.executor):
|
||||
|
||||
supports_analize = False
|
||||
supports_play = False
|
||||
supports_convert = False
|
||||
supports_menu = False
|
||||
supports_mkiso = True
|
||||
supports_burn = False
|
||||
display_name = "GENISOIMAGE"
|
||||
|
||||
@staticmethod
|
||||
def check_is_installed():
|
||||
try:
|
||||
handle = subprocess.Popen(["genisoimage","--help"], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
|
||||
(stdout, stderr) = handle.communicate()
|
||||
if 0==handle.wait():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def __init__(self):
|
||||
|
||||
devedeng.executor.executor.__init__(self)
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
def create_iso (self, path, name):
|
||||
|
||||
filesystem_path = os.path.join(path,"dvd_tree")
|
||||
final_path = os.path.join(path,name+".iso")
|
||||
|
||||
self.command_var=[]
|
||||
self.command_var.append("genisoimage")
|
||||
self.command_var.append("-dvd-video")
|
||||
self.command_var.append("-V")
|
||||
self.command_var.append("DVDVIDEO")
|
||||
self.command_var.append("-v")
|
||||
self.command_var.append("-udf")
|
||||
self.command_var.append("-o")
|
||||
self.command_var.append(final_path)
|
||||
self.command_var.append(filesystem_path)
|
||||
self.text = _("Creating ISO image")
|
||||
|
||||
|
||||
def process_stdout(self,data):
|
||||
return
|
||||
|
||||
def process_stderr(self,data):
|
||||
|
||||
if (data[0].find("% done") == -1):
|
||||
return
|
||||
|
||||
l = data[0].split("%")
|
||||
p = float(l[0])
|
||||
self.progress_bar[1].set_fraction(p/100.0)
|
||||
self.progress_bar[1].set_text("%.1f%%" % (p))
|
||||
|
||||
return
|
||||
576
src/devedeng/interface_manager.py
Normal file
576
src/devedeng/interface_manager.py
Normal file
|
|
@ -0,0 +1,576 @@
|
|||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
from gi.repository import GObject,Gdk
|
||||
|
||||
class interface_manager(GObject.GObject):
|
||||
""" This class allows to automatically generate variables for a GLADE interface,
|
||||
set the widgets in the interface to their values, and copy the current values
|
||||
in the widgets to the variables """
|
||||
|
||||
def __init__(self):
|
||||
|
||||
GObject.GObject.__init__(self)
|
||||
self.interface_groups = {}
|
||||
self.interface_toggles = []
|
||||
self.interface_dualtoggles = []
|
||||
self.interface_labels = []
|
||||
self.interface_text = []
|
||||
self.interface_show_hide = []
|
||||
self.interface_enable_disable = []
|
||||
self.interface_float_adjustments = []
|
||||
self.interface_integer_adjustments = []
|
||||
self.interface_lists = []
|
||||
self.interface_colorbuttons = []
|
||||
self.interface_fontbuttons = []
|
||||
self.interface_filebuttons = []
|
||||
self.interface_comboboxes = []
|
||||
|
||||
def add_group(self,group_name,radiobutton_list,default_value,callback = None):
|
||||
""" Adds a group of radiobuttons and creates an internal variable with
|
||||
the name group_name, setting it to default_value. The
|
||||
value for the variable will be the name of the active
|
||||
radiobutton """
|
||||
|
||||
if (default_value != None):
|
||||
exec('self.'+group_name+' = "'+str(default_value)+'"')
|
||||
else:
|
||||
exec('self.'+group_name+' = None')
|
||||
self.interface_groups[group_name] = ( radiobutton_list, callback )
|
||||
|
||||
def add_toggle(self,toggle_name,default_value,callback = None):
|
||||
""" Adds an internal variable with the name toggle_name, linked to a widget
|
||||
element with the same name (must be or inherint from Gtk.ToogleButton).
|
||||
The default value can be True of False """
|
||||
|
||||
exec('self.'+toggle_name+' = '+str(default_value))
|
||||
self.interface_toggles.append( (toggle_name, callback) )
|
||||
|
||||
def add_dualtoggle(self,toggle_name,toggle2,default_value,callback = None):
|
||||
""" Adds an internal variable with the name toggle_name, linked to widget
|
||||
elements with names toggle_nane and toggle2 (must be or inherint from Gtk.ToogleButton).
|
||||
The default value can be True of False, with True being toggle_name active, and False
|
||||
being toggle2 active """
|
||||
|
||||
exec('self.'+toggle_name+' = '+str(default_value))
|
||||
self.interface_dualtoggles.append( (toggle_name, toggle2, callback) )
|
||||
|
||||
def add_text(self,text_name,default_value,callback = None):
|
||||
""" Adds an internal variable with the name text_name, linked to an
|
||||
element with the same name (must be a Gtk.TextEntry or a Gtk.Label).
|
||||
The default value can be a text or None """
|
||||
|
||||
if (default_value != None):
|
||||
exec('self.'+text_name+' = "'+str(default_value)+'"')
|
||||
else:
|
||||
exec('self.'+text_name+' = None')
|
||||
self.interface_text.append( (text_name, callback) )
|
||||
|
||||
def add_label(self,text_name,default_value):
|
||||
""" Adds an internal variable with the name text_name, linked to an
|
||||
element with the same name (must be a Gtk.TextEntry or a Gtk.Label).
|
||||
The default value can be a text or None. This element is copied to the UI,
|
||||
but is never updated from the UI if the user changes it """
|
||||
|
||||
exec('self.'+text_name+' = default_value')
|
||||
self.interface_labels.append(text_name)
|
||||
|
||||
def add_integer_adjustment(self,adjustment_name,default_value,callback = None):
|
||||
""" Adds an internal variable with the name text_name, linked to an
|
||||
element with the same name (must be a Gtk.Adjustment).
|
||||
The default value must be an integer """
|
||||
|
||||
exec('self.'+adjustment_name+' = '+str(default_value))
|
||||
self.interface_integer_adjustments.append( (adjustment_name, callback) )
|
||||
|
||||
def add_float_adjustment(self,adjustment_name,default_value,callback = None):
|
||||
""" Adds an internal variable with the name text_name, linked to an
|
||||
element with the same name (must be a Gtk.Adjustment).
|
||||
The default value must be an float """
|
||||
|
||||
exec('self.'+adjustment_name+' = '+str(default_value))
|
||||
self.interface_float_adjustments.append( (adjustment_name, callback))
|
||||
|
||||
def add_list(self,liststore_name,callback = None):
|
||||
""" Adds an internal variable with the name liststore_name, linked to
|
||||
an element with the same name (must be a Gtk.ListStore). """
|
||||
|
||||
exec('self.'+liststore_name+' = []')
|
||||
self.interface_lists.append( (liststore_name, callback ))
|
||||
|
||||
def add_colorbutton(self,colorbutton_name, default_value,callback = None):
|
||||
""" Adds an internal variable with the name colorbutton_name, linked to an
|
||||
element with the same name (must be a Gtk.ColorButton).
|
||||
The default value must be a set with RGBA values """
|
||||
|
||||
exec('self.'+colorbutton_name+' = default_value')
|
||||
self.interface_colorbuttons.append( (colorbutton_name, callback ))
|
||||
|
||||
def add_fontbutton(self,fontbutton_name, default_value, callback = None):
|
||||
""" Adds an internal variable with the name fontbutton_name, linked to an
|
||||
element with the same name (must be a Gtk.FontButton).
|
||||
The default value must be a string with the font values """
|
||||
|
||||
exec('self.'+fontbutton_name+' = default_value')
|
||||
self.interface_fontbuttons.append( (fontbutton_name, callback ))
|
||||
|
||||
def add_filebutton(self,filebutton_name, default_value, callback = None):
|
||||
""" Adds an internal variable with the name filebutton_name, linked to an
|
||||
element with the same name (must be a Gtk.FileButton).
|
||||
The default value must be a string with the font values """
|
||||
|
||||
exec('self.'+filebutton_name+' = default_value')
|
||||
self.interface_filebuttons.append( (filebutton_name, callback ) )
|
||||
|
||||
def add_combobox(self,combobox_name,values,default_value,callback = None):
|
||||
""" Adds an internal variable with the name combobox_name, linked to an
|
||||
element with the same name (must be a Gtk.Combobox).
|
||||
The default value must be an integer with the entry selected """
|
||||
|
||||
exec('self.'+combobox_name+' = default_value')
|
||||
self.interface_comboboxes.append ( (combobox_name, values, callback) )
|
||||
|
||||
def add_show_hide(self,element_name,to_show,to_hide):
|
||||
""" Adds an element that can be active or inactive, and two lists of elements.
|
||||
The first one contains elements that will be visible when the element is
|
||||
active, and invisible when it is inactive, and the second one contains
|
||||
elements that will be visible when the element is inactive, and
|
||||
invisible when the element is active """
|
||||
|
||||
self.interface_show_hide.append([element_name, to_show, to_hide])
|
||||
|
||||
def add_enable_disable(self,element_name,to_enable,to_disable):
|
||||
""" Adds an element that can be active or inactive, and two lists of elements.
|
||||
The first one contains elements that will be enabled when the element is
|
||||
active, and disabled when it is inactive, and the second one contains
|
||||
elements that will be enabled when the element is inactive, and
|
||||
disabled when the element is active """
|
||||
|
||||
self.interface_enable_disable.append([element_name, to_enable, to_disable])
|
||||
|
||||
def update_ui(self,builder):
|
||||
""" Sets the value of the widgets in base of the internal variables """
|
||||
|
||||
for key in self.interface_groups:
|
||||
obj = eval('self.'+key)
|
||||
builder.get_object(obj).set_active(True)
|
||||
callback = self.interface_groups[key][1]
|
||||
if (callback != None):
|
||||
for element in self.interface_groups[key][0]:
|
||||
obj = builder.get_object(element)
|
||||
obj.connect("toggled",callback)
|
||||
|
||||
for element in self.interface_toggles:
|
||||
value = eval('self.'+element[0])
|
||||
obj = builder.get_object(element[0])
|
||||
obj.set_active(value)
|
||||
callback = element[1]
|
||||
if (callback != None):
|
||||
obj.connect("toggled",callback)
|
||||
|
||||
for element in self.interface_dualtoggles:
|
||||
value = eval('self.'+element[0])
|
||||
obj = builder.get_object(element[0])
|
||||
obj2 = builder.get_object(element[1])
|
||||
if value:
|
||||
obj.set_active(True)
|
||||
else:
|
||||
obj2.set_active(True)
|
||||
callback = element[2]
|
||||
if (callback != None):
|
||||
obj.connect("toggled",callback)
|
||||
|
||||
for element in self.interface_text:
|
||||
value = eval('self.'+element[0])
|
||||
obj = builder.get_object(element[0])
|
||||
if (value != None):
|
||||
obj.set_text(value)
|
||||
else:
|
||||
obj.set_text("")
|
||||
callback = element[1]
|
||||
if (callback != None):
|
||||
obj.connect("changed",callback)
|
||||
|
||||
for element in self.interface_labels:
|
||||
value = eval('self.'+element)
|
||||
obj = builder.get_object(element)
|
||||
if obj != None:
|
||||
if (value != None):
|
||||
obj.set_text(str(value))
|
||||
else:
|
||||
obj.set_text("")
|
||||
|
||||
for element in self.interface_integer_adjustments:
|
||||
obj = builder.get_object(element[0])
|
||||
if obj != None:
|
||||
value = eval('self.'+element[0])
|
||||
obj.set_value(float(value))
|
||||
callback = element[1]
|
||||
if (callback != None):
|
||||
obj.connect("value_changed",callback)
|
||||
|
||||
for element in self.interface_float_adjustments:
|
||||
obj = builder.get_object(element[0])
|
||||
if obj != None:
|
||||
value = eval('self.'+element[0])
|
||||
obj.set_value(value)
|
||||
callback = element[1]
|
||||
if (callback != None):
|
||||
obj.connect("value_changed",callback)
|
||||
|
||||
for element in self.interface_lists:
|
||||
obj = eval('self.'+element[0])
|
||||
the_liststore = builder.get_object(element[0])
|
||||
the_liststore.clear()
|
||||
for item in obj:
|
||||
the_liststore.append(item)
|
||||
callback = element[1]
|
||||
if (callback != None):
|
||||
the_liststore.connect("row_changed",callback)
|
||||
the_liststore.connect("row_deleted",callback)
|
||||
the_liststore.connect("row_inserted",callback)
|
||||
the_liststore.connect("row_reordered",callback)
|
||||
|
||||
for element in self.interface_colorbuttons:
|
||||
value = eval('self.'+element[0])
|
||||
obj = builder.get_object(element[0])
|
||||
objcolor = Gdk.Color(int(value[0]*65535.0),int(value[1]*65535.0),int(value[2]*65535.0))
|
||||
obj.set_color(objcolor)
|
||||
obj.set_alpha(int(value[3]*65535.0))
|
||||
callback = element[1]
|
||||
if (callback != None):
|
||||
obj.connect("color_set",callback)
|
||||
|
||||
for element in self.interface_fontbuttons:
|
||||
value = eval('self.'+element[0])
|
||||
obj = builder.get_object(element[0])
|
||||
if (value != None):
|
||||
obj.set_font(value)
|
||||
callback = element[1]
|
||||
if (callback != None):
|
||||
obj.connect("font_set",callback)
|
||||
|
||||
for element in self.interface_filebuttons:
|
||||
value = eval('self.'+element[0])
|
||||
obj = builder.get_object(element[0])
|
||||
if (value != None):
|
||||
obj.set_filename(value)
|
||||
callback = element[1]
|
||||
if (callback != None):
|
||||
obj.connect("file_set",callback)
|
||||
|
||||
for element in self.interface_comboboxes:
|
||||
obj = eval('self.'+element[0])
|
||||
the_combo = builder.get_object(element[0])
|
||||
the_list = the_combo.get_model()
|
||||
the_list.clear()
|
||||
counter = 0
|
||||
dv = 0
|
||||
for item in element[1]:
|
||||
the_list.append([item])
|
||||
if (item == obj):
|
||||
dv = counter
|
||||
counter += 1
|
||||
the_combo.set_active(dv)
|
||||
callback = element[2]
|
||||
if (callback != None):
|
||||
the_combo.connect("changed",callback)
|
||||
|
||||
self.interface_show_hide_obj = {}
|
||||
for element in self.interface_show_hide:
|
||||
obj = builder.get_object(element[0])
|
||||
to_show = []
|
||||
for e2 in element[1]:
|
||||
to_show.append(builder.get_object(e2))
|
||||
to_hide = []
|
||||
for e3 in element[2]:
|
||||
to_hide.append(builder.get_object(e3))
|
||||
self.interface_show_hide_obj[obj] = [to_show, to_hide]
|
||||
obj.connect('toggled',self.toggled_element)
|
||||
self.toggled_element(obj)
|
||||
|
||||
self.interface_enable_disable_obj = {}
|
||||
for element in self.interface_enable_disable:
|
||||
obj = builder.get_object(element[0])
|
||||
to_enable = []
|
||||
for e2 in element[1]:
|
||||
to_enable.append(builder.get_object(e2))
|
||||
to_disable = []
|
||||
for e3 in element[2]:
|
||||
to_disable.append(builder.get_object(e3))
|
||||
self.interface_enable_disable_obj[obj] = [to_enable, to_disable]
|
||||
obj.connect('toggled',self.toggled_element2)
|
||||
self.toggled_element2(obj)
|
||||
|
||||
|
||||
def toggled_element(self,element):
|
||||
""" Wenever an element with 'hide' or 'show' needs is toggled, this callback is called """
|
||||
|
||||
# First, show all items for each possible element
|
||||
for key in self.interface_show_hide_obj:
|
||||
to_show = self.interface_show_hide_obj[key][0]
|
||||
to_hide = self.interface_show_hide_obj[key][1]
|
||||
|
||||
active = key.get_active()
|
||||
|
||||
for item in to_show:
|
||||
if active:
|
||||
item.show()
|
||||
|
||||
for item in to_hide:
|
||||
if not active:
|
||||
item.show()
|
||||
|
||||
# And now, hide all items that must be hiden
|
||||
# This is done this way because this allows to have an item being hiden by
|
||||
# one widget, and being shown by another: in that case, it will be hiden always
|
||||
for key in self.interface_show_hide_obj:
|
||||
to_show = self.interface_show_hide_obj[key][0]
|
||||
to_hide = self.interface_show_hide_obj[key][1]
|
||||
|
||||
active = key.get_active()
|
||||
|
||||
for item in to_show:
|
||||
if not active:
|
||||
item.hide()
|
||||
|
||||
for item in to_hide:
|
||||
if active:
|
||||
item.hide()
|
||||
|
||||
|
||||
def toggled_element2(self,element):
|
||||
""" Wenever an element with 'enable' or 'disable' needs is toggled, this callback is called """
|
||||
|
||||
# First enable all items that must be enabled
|
||||
for key in self.interface_enable_disable_obj:
|
||||
to_enable = self.interface_enable_disable_obj[key][0]
|
||||
to_disable = self.interface_enable_disable_obj[key][1]
|
||||
|
||||
active = key.get_active()
|
||||
if (active):
|
||||
for item in to_enable:
|
||||
item.set_sensitive(True)
|
||||
else:
|
||||
for item in to_disable:
|
||||
item.set_sensitive(True)
|
||||
|
||||
# And now, disable all items that must be disabled
|
||||
# This is done this way because this allows to have an item being disabled by
|
||||
# one widget, and being enabled by another: in that case, it will be disabled always
|
||||
for key in self.interface_enable_disable_obj:
|
||||
to_enable = self.interface_enable_disable_obj[key][0]
|
||||
to_disable = self.interface_enable_disable_obj[key][1]
|
||||
|
||||
active = key.get_active()
|
||||
if (not active):
|
||||
for item in to_enable:
|
||||
item.set_sensitive(False)
|
||||
else:
|
||||
for item in to_disable:
|
||||
item.set_sensitive(False)
|
||||
|
||||
|
||||
def store_ui(self,builder):
|
||||
""" Takes the values of the widgets and stores them in the internal variables """
|
||||
|
||||
for key in self.interface_groups:
|
||||
for element in self.interface_groups[key][0]:
|
||||
obj = builder.get_object(element)
|
||||
if obj.get_active():
|
||||
exec('self.'+key+' = "'+element+'"')
|
||||
break
|
||||
|
||||
for element in self.interface_toggles:
|
||||
obj = builder.get_object(element[0])
|
||||
if obj.get_active():
|
||||
exec('self.'+element[0]+' = True')
|
||||
else:
|
||||
exec('self.'+element[0]+' = False')
|
||||
|
||||
for element in self.interface_dualtoggles:
|
||||
obj = builder.get_object(element[0])
|
||||
if obj.get_active():
|
||||
exec('self.'+element[0]+' = True')
|
||||
else:
|
||||
exec('self.'+element[0]+' = False')
|
||||
|
||||
for element in self.interface_text:
|
||||
obj = builder.get_object(element[0])
|
||||
exec('self.'+element[0]+' = obj.get_text()')
|
||||
|
||||
for element in self.interface_integer_adjustments:
|
||||
obj = builder.get_object(element[0])
|
||||
if obj != None:
|
||||
exec('self.'+element[0]+' = int(obj.get_value())')
|
||||
|
||||
for element in self.interface_float_adjustments:
|
||||
obj = builder.get_object(element[0])
|
||||
if obj != None:
|
||||
exec('self.'+element[0]+' = obj.get_value()')
|
||||
|
||||
for element in self.interface_colorbuttons:
|
||||
obj = builder.get_object(element[0])
|
||||
objcolor = obj.get_color()
|
||||
alpha = obj.get_alpha()
|
||||
exec('self.'+element[0]+' = ((float(objcolor.red))/65535.0, (float(objcolor.green))/65535.0, (float(objcolor.blue))/65535.0, (float(alpha))/65535.0)')
|
||||
|
||||
for element in self.interface_fontbuttons:
|
||||
obj = builder.get_object(element[0])
|
||||
exec('self.'+element[0]+' = obj.get_font()')
|
||||
|
||||
for element in self.interface_filebuttons:
|
||||
obj = builder.get_object(element[0])
|
||||
exec('self.'+element[0]+' = obj.get_filename()')
|
||||
|
||||
for element in self.interface_lists:
|
||||
exec('self.'+element[0]+' = []')
|
||||
the_liststore = builder.get_object(element[0])
|
||||
ncolumns = the_liststore.get_n_columns()
|
||||
for row in the_liststore:
|
||||
final_row = []
|
||||
for c in range(0,ncolumns):
|
||||
final_row.append(row.model[row.iter][c])
|
||||
exec('self.'+element[0]+'.append(final_row)')
|
||||
|
||||
for element in self.interface_comboboxes:
|
||||
obj = builder.get_object(element[0])
|
||||
exec('self.'+element[0]+' = element[1][obj.get_active()]')
|
||||
|
||||
|
||||
def save_ui(self):
|
||||
""" Makes a copy of all the UI variables """
|
||||
|
||||
for element in self.interface_groups:
|
||||
exec('self.'+element+'_backup = self.'+element)
|
||||
for element in self.interface_toggles:
|
||||
exec('self.'+element[0]+'_backup = self.'+element[0])
|
||||
for element in self.interface_dualtoggles:
|
||||
exec('self.'+element[0]+'_backup = self.'+element[0])
|
||||
for element in self.interface_text:
|
||||
exec('self.'+element[0]+'_backup = self.'+element[0])
|
||||
for element in self.interface_integer_adjustments:
|
||||
exec('self.'+element[0]+'_backup = self.'+element[0])
|
||||
for element in self.interface_float_adjustments:
|
||||
exec('self.'+element[0]+'_backup = self.'+element[0])
|
||||
for element in self.interface_colorbuttons:
|
||||
exec('self.'+element[0]+'_backup = self.'+element[0])
|
||||
for element in self.interface_fontbuttons:
|
||||
exec('self.'+element[0]+'_backup = self.'+element[0])
|
||||
for element in self.interface_filebuttons:
|
||||
exec('self.'+element[0]+'_backup = self.'+element[0])
|
||||
for element in self.interface_lists:
|
||||
exec('self.'+element[0]+'_backup = self.'+element[0])
|
||||
for element in self.interface_comboboxes:
|
||||
exec('self.'+element[0]+'_backup = self.'+element[0])
|
||||
|
||||
def restore_ui(self):
|
||||
""" Restores a copy of all the UI variables """
|
||||
|
||||
for element in self.interface_groups:
|
||||
exec('self.'+element+' = self.'+element+'_backup')
|
||||
for element in self.interface_toggles:
|
||||
exec('self.'+element[0]+' = self.'+element[0]+'_backup')
|
||||
for element in self.interface_dualtoggles:
|
||||
exec('self.'+element[0]+' = self.'+element[0]+'_backup')
|
||||
for element in self.interface_text:
|
||||
exec('self.'+element[0]+' = self.'+element[0]+'_backup')
|
||||
for element in self.interface_integer_adjustments:
|
||||
exec('self.'+element[0]+' = self.'+element[0]+'_backup')
|
||||
for element in self.interface_float_adjustments:
|
||||
exec('self.'+element[0]+' = self.'+element[0]+'_backup')
|
||||
for element in self.interface_colorbuttons:
|
||||
exec('self.'+element[0]+' = self.'+element[0]+'_backup')
|
||||
for element in self.interface_fontbuttons:
|
||||
exec('self.'+element[0]+' = self.'+element[0]+'_backup')
|
||||
for element in self.interface_filebuttons:
|
||||
exec('self.'+element[0]+' = self.'+element[0]+'_backup')
|
||||
for element in self.interface_lists:
|
||||
exec('self.'+element[0]+' = self.'+element[0]+'_backup')
|
||||
for element in self.interface_comboboxes:
|
||||
exec('self.'+element[0]+' = self.'+element[0]+'_backup')
|
||||
|
||||
|
||||
def serialize(self):
|
||||
""" Returns a dictionary with both the variables of the interface and its values,
|
||||
which can be restored with unserialize
|
||||
"""
|
||||
|
||||
output = {}
|
||||
for element in self.interface_groups:
|
||||
output[element] = eval('self.'+element)
|
||||
for element in self.interface_toggles:
|
||||
output[element[0]] = eval('self.'+element[0])
|
||||
for element in self.interface_dualtoggles:
|
||||
output[element[0]] = eval('self.'+element[0])
|
||||
for element in self.interface_text:
|
||||
output[element[0]] = eval('self.'+element[0])
|
||||
for element in self.interface_integer_adjustments:
|
||||
output[element[0]] = eval('self.'+element[0])
|
||||
for element in self.interface_float_adjustments:
|
||||
output[element[0]] = eval('self.'+element[0])
|
||||
for element in self.interface_colorbuttons:
|
||||
output[element[0]] = eval('self.'+element[0])
|
||||
for element in self.interface_fontbuttons:
|
||||
output[element[0]] = eval('self.'+element[0])
|
||||
for element in self.interface_filebuttons:
|
||||
output[element[0]] = eval('self.'+element[0])
|
||||
for element in self.interface_lists:
|
||||
output[element[0]] = eval('self.'+element[0])
|
||||
for element in self.interface_comboboxes:
|
||||
output[element[0]] = eval('self.'+element[0])
|
||||
return output
|
||||
|
||||
|
||||
def unserialize(self,data_list):
|
||||
""" Takes a dictionary with the variables of the interface and its values,
|
||||
and restores them into their variables
|
||||
"""
|
||||
|
||||
for element in self.interface_groups:
|
||||
if element in data_list:
|
||||
exec('self.'+element+' = data_list["'+element+'"]')
|
||||
for element in self.interface_toggles:
|
||||
if element[0] in data_list:
|
||||
exec('self.'+element[0]+' = data_list["'+element[0]+'"]')
|
||||
for element in self.interface_dualtoggles:
|
||||
if element[0] in data_list:
|
||||
exec('self.'+element[0]+' = data_list["'+element[0]+'"]')
|
||||
for element in self.interface_text:
|
||||
if element[0] in data_list:
|
||||
exec('self.'+element[0]+' = data_list["'+element[0]+'"]')
|
||||
for element in self.interface_integer_adjustments:
|
||||
if element[0] in data_list:
|
||||
exec('self.'+element[0]+' = data_list["'+element[0]+'"]')
|
||||
for element in self.interface_float_adjustments:
|
||||
if element[0] in data_list:
|
||||
exec('self.'+element[0]+' = data_list["'+element[0]+'"]')
|
||||
for element in self.interface_colorbuttons:
|
||||
if element[0] in data_list:
|
||||
exec('self.'+element[0]+' = data_list["'+element[0]+'"]')
|
||||
for element in self.interface_fontbuttons:
|
||||
if element[0] in data_list:
|
||||
exec('self.'+element[0]+' = data_list["'+element[0]+'"]')
|
||||
for element in self.interface_filebuttons:
|
||||
if element[0] in data_list:
|
||||
exec('self.'+element[0]+' = data_list["'+element[0]+'"]')
|
||||
for element in self.interface_lists:
|
||||
if element[0] in data_list:
|
||||
exec('self.'+element[0]+' = data_list["'+element[0]+'"]')
|
||||
for element in self.interface_comboboxes:
|
||||
if element[0] in data_list:
|
||||
exec('self.'+element[0]+' = data_list["'+element[0]+'"]')
|
||||
57
src/devedeng/k3b.py
Normal file
57
src/devedeng/k3b.py
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
import subprocess
|
||||
import devedeng.configuration_data
|
||||
import devedeng.executor
|
||||
|
||||
class k3b(devedeng.executor.executor):
|
||||
|
||||
supports_analize = False
|
||||
supports_play = False
|
||||
supports_convert = False
|
||||
supports_menu = False
|
||||
supports_mkiso = False
|
||||
supports_burn = True
|
||||
display_name = "K3B"
|
||||
|
||||
@staticmethod
|
||||
def check_is_installed():
|
||||
try:
|
||||
handle = subprocess.Popen(["k3b","--help"], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
|
||||
(stdout, stderr) = handle.communicate()
|
||||
if 0==handle.wait():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def __init__(self):
|
||||
|
||||
devedeng.executor.executor.__init__(self)
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
def burn(self,file_name):
|
||||
|
||||
self.command_var = ["k3b", file_name]
|
||||
|
||||
def process_stdout(self,data):
|
||||
return
|
||||
|
||||
def process_stderr(self,data):
|
||||
return
|
||||
48
src/devedeng/message.py
Normal file
48
src/devedeng/message.py
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
from gi.repository import Gtk
|
||||
import os
|
||||
import devedeng.configuration_data
|
||||
|
||||
class message_window:
|
||||
|
||||
def __init__(self,text,title,list_data = None):
|
||||
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(self.config.gettext_domain)
|
||||
|
||||
builder.add_from_file(os.path.join(self.config.glade,"wmessage.ui"))
|
||||
builder.connect_signals(self)
|
||||
wmessage_window = builder.get_object("dialog_message")
|
||||
wmessage_window.set_title(title)
|
||||
wmessage_text = builder.get_object("label_message")
|
||||
wmessage_text.set_markup(text)
|
||||
wmessage_list = builder.get_object("list_message")
|
||||
wmessage_liststore = builder.get_object("liststore_elements")
|
||||
wmessage_window.show_all()
|
||||
|
||||
if (list_data == None):
|
||||
wmessage_list.hide()
|
||||
else:
|
||||
for element in list_data:
|
||||
wmessage_liststore.append([element])
|
||||
|
||||
wmessage_window.run()
|
||||
wmessage_window.destroy()
|
||||
83
src/devedeng/mkisofs.py
Normal file
83
src/devedeng/mkisofs.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
import os
|
||||
import devedeng.configuration_data
|
||||
import subprocess
|
||||
import devedeng.executor
|
||||
|
||||
class mkisofs(devedeng.executor.executor):
|
||||
|
||||
supports_analize = False
|
||||
supports_play = False
|
||||
supports_convert = False
|
||||
supports_menu = False
|
||||
supports_mkiso = True
|
||||
supports_burn = False
|
||||
display_name = "MKISOFS"
|
||||
|
||||
@staticmethod
|
||||
def check_is_installed():
|
||||
try:
|
||||
handle = subprocess.Popen(["mkisofs","--help"], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
|
||||
(stdout, stderr) = handle.communicate()
|
||||
if 0==handle.wait():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def __init__(self):
|
||||
|
||||
devedeng.executor.executor.__init__(self)
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
def create_iso (self, path, name):
|
||||
|
||||
filesystem_path = os.path.join(path,"dvd_tree")
|
||||
final_path = os.path.join(path,name+".iso")
|
||||
|
||||
self.command_var=[]
|
||||
self.command_var.append("mkisofs")
|
||||
self.command_var.append("-dvd-video")
|
||||
self.command_var.append("-V")
|
||||
self.command_var.append("DVDVIDEO")
|
||||
self.command_var.append("-v")
|
||||
self.command_var.append("-udf")
|
||||
self.command_var.append("-o")
|
||||
self.command_var.append(final_path)
|
||||
self.command_var.append(filesystem_path)
|
||||
self.text = _("Creating ISO image")
|
||||
|
||||
|
||||
def process_stdout(self,data):
|
||||
return
|
||||
|
||||
def process_stderr(self,data):
|
||||
|
||||
if (data[0].find("% done") == -1):
|
||||
return
|
||||
|
||||
l = data[0].split("%")
|
||||
p = float(l[0])
|
||||
self.progress_bar[1].set_fraction(p/100.0)
|
||||
self.progress_bar[1].set_text("%.1f%%" % (p))
|
||||
|
||||
return
|
||||
173
src/devedeng/mplayer.py
Normal file
173
src/devedeng/mplayer.py
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
import subprocess
|
||||
import devedeng.configuration_data
|
||||
import devedeng.executor
|
||||
import os
|
||||
|
||||
class mplayer(devedeng.executor.executor):
|
||||
|
||||
supports_analize = True
|
||||
supports_play = True
|
||||
supports_convert = False
|
||||
supports_menu = False
|
||||
supports_mkiso = False
|
||||
supports_burn = False
|
||||
display_name = "MPLAYER"
|
||||
|
||||
@staticmethod
|
||||
def check_is_installed():
|
||||
try:
|
||||
handle = subprocess.Popen(["mplayer","-v"], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
|
||||
(stdout, stderr) = handle.communicate()
|
||||
if 0==handle.wait():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def __init__(self):
|
||||
|
||||
devedeng.executor.executor.__init__(self)
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
def play_film(self,file_name):
|
||||
|
||||
command_line = ["mplayer", file_name]
|
||||
self.launch_process(command_line)
|
||||
|
||||
def process_stdout(self,data):
|
||||
return
|
||||
|
||||
def process_stderr(self,data):
|
||||
return
|
||||
|
||||
def get_film_data(self, file_name):
|
||||
""" processes a file, refered by the FILE_MOVIE movie object, and fills its
|
||||
main data (resolution, FPS, length...) """
|
||||
|
||||
self.original_file_size = os.path.getsize(file_name)
|
||||
(video, audio, length) = self.analize_film_data(file_name, True)
|
||||
|
||||
if (video != 0):
|
||||
self.analize_film_data(file_name, False)
|
||||
return False # no error
|
||||
else:
|
||||
return True # the file is not a video file; maybe an audio-only file, or another thing
|
||||
|
||||
def analize_film_data(self,file_name,check_audio):
|
||||
|
||||
if (check_audio):
|
||||
frames = "0"
|
||||
else:
|
||||
frames = "5"
|
||||
|
||||
command_line = ["mplayer","-loop","1","-identify", "-vo", "null", "-ao", "null", "-frames", frames , file_name]
|
||||
|
||||
(stdout, stderr) = self.launch_process(command_line, False)
|
||||
|
||||
stream_number = 0
|
||||
minimum_audio=-1
|
||||
self.audio_list=[]
|
||||
self.audio_streams = 0
|
||||
minimum_video=-1
|
||||
self.video_list=[]
|
||||
self.video_streams = 0
|
||||
self.original_width = 0
|
||||
self.original_height = 0
|
||||
self.original_length = 0
|
||||
self.original_videorate = 0
|
||||
self.original_audiorate = 0
|
||||
self.original_audiorate_uncompressed = 0
|
||||
self.original_fps = 0
|
||||
self.original_aspect_ratio = 0
|
||||
|
||||
try:
|
||||
stdout2 = stdout.decode("utf-8")
|
||||
except:
|
||||
stdout2 = stdout.decode("latin1")
|
||||
|
||||
for line in stdout2.split("\n"):
|
||||
line=self.remove_ansi(line)
|
||||
if line == "":
|
||||
continue
|
||||
position=line.find("ID_")
|
||||
if position==-1:
|
||||
continue
|
||||
line=line[position:]
|
||||
|
||||
pos2 = line.find("=")
|
||||
if (pos2 != -1):
|
||||
command = line[0:pos2]
|
||||
parameter = line[pos2+1:]
|
||||
else:
|
||||
continue
|
||||
|
||||
if command=="ID_VIDEO_BITRATE":
|
||||
self.original_videorate=int(int(parameter)/1000)
|
||||
if command=="ID_VIDEO_WIDTH":
|
||||
self.original_width=int(parameter)
|
||||
if command=="ID_VIDEO_HEIGHT":
|
||||
self.original_height=int(parameter)
|
||||
if command=="ID_VIDEO_ASPECT":
|
||||
self.original_aspect_ratio=float(parameter)
|
||||
if command=="ID_VIDEO_FPS":
|
||||
self.original_fps=float(parameter)
|
||||
if command=="ID_AUDIO_BITRATE":
|
||||
self.original_audiorate=int(int(parameter)/1000)
|
||||
if command=="ID_AUDIO_RATE":
|
||||
self.original_audiorate_uncompressed=int(parameter)
|
||||
if command=="ID_LENGTH":
|
||||
self.original_length=int(float(parameter))
|
||||
|
||||
if command=="ID_VIDEO_ID":
|
||||
self.video_streams+=1
|
||||
video_track=int(parameter)
|
||||
if (minimum_video == -1) or (minimum_video>video_track):
|
||||
minimum_video=video_track
|
||||
self.video_list.append(stream_number)
|
||||
if command=="ID_AUDIO_ID":
|
||||
self.audio_streams+=1
|
||||
audio_track=int(parameter)
|
||||
if (minimum_audio == -1) or (minimum_audio>audio_track):
|
||||
minimum_audio=audio_track
|
||||
self.audio_list.append(stream_number)
|
||||
|
||||
# increment the stream_number counter in this case to also count
|
||||
# subtitles and other stream types
|
||||
if (command[-3:] == "_ID"):
|
||||
stream_number += 1
|
||||
|
||||
if (check_audio):
|
||||
return (self.video_streams, self.audio_streams, self.original_length)
|
||||
else:
|
||||
self.original_size = str(self.original_width)+"x"+str(self.original_height)
|
||||
if (self.original_aspect_ratio == None) or (self.original_aspect_ratio <= 1.0):
|
||||
if (self.original_height != 0):
|
||||
self.original_aspect_ratio = (float(self.original_width))/(float(self.original_height))
|
||||
|
||||
if (self.original_aspect_ratio != None):
|
||||
self.original_aspect_ratio = (float(int(self.original_aspect_ratio*1000.0)))/1000.0
|
||||
|
||||
if (self.video_streams == 0):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
60
src/devedeng/mpv.py
Normal file
60
src/devedeng/mpv.py
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
import subprocess
|
||||
import devedeng.configuration_data
|
||||
import devedeng.executor
|
||||
|
||||
class mpv(devedeng.executor.executor):
|
||||
|
||||
supports_analize = False
|
||||
supports_play = True
|
||||
supports_convert = False
|
||||
supports_menu = False
|
||||
supports_mkiso = False
|
||||
supports_burn = False
|
||||
display_name = "MPV"
|
||||
|
||||
@staticmethod
|
||||
def check_is_installed():
|
||||
try:
|
||||
handle = subprocess.Popen(["mpv","-v"], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
|
||||
(stdout, stderr) = handle.communicate()
|
||||
if 0==handle.wait():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def __init__(self):
|
||||
|
||||
devedeng.executor.executor.__init__(self)
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
def play_film(self,file_name):
|
||||
|
||||
command_line = ["mpv", file_name]
|
||||
self.launch_process(command_line)
|
||||
|
||||
def process_stdout(self,data):
|
||||
return
|
||||
|
||||
def process_stderr(self,data):
|
||||
return
|
||||
48
src/devedeng/mux_dvd_menu.py
Normal file
48
src/devedeng/mux_dvd_menu.py
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
import os
|
||||
import devedeng.configuration_data
|
||||
import devedeng.executor
|
||||
|
||||
class mux_dvd_menu(devedeng.executor.executor):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
devedeng.executor.executor.__init__(self)
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
def create_mpg(self,n_page,output_path,movie_path):
|
||||
|
||||
self.n_page = n_page
|
||||
self.text = _("Mixing menu %(X)d") % {"X": self.n_page}
|
||||
|
||||
final_path = os.path.join(output_path,"menu_"+str(n_page)+"B.mpg")
|
||||
|
||||
self.command_var=[]
|
||||
self.command_var.append("spumux")
|
||||
self.command_var.append(os.path.join(output_path,"menu_"+str(n_page)+".xml"))
|
||||
self.stdin_file = movie_path
|
||||
self.stdout_file = final_path
|
||||
|
||||
return final_path
|
||||
|
||||
def process_stderr(self,data):
|
||||
|
||||
return
|
||||
62
src/devedeng/opensave.py
Normal file
62
src/devedeng/opensave.py
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
from gi.repository import Gtk
|
||||
import os
|
||||
import devedeng.configuration_data
|
||||
|
||||
class opensave_window:
|
||||
|
||||
def __init__(self, save):
|
||||
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
self.save = save
|
||||
|
||||
|
||||
def run(self,current_file = None):
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(self.config.gettext_domain)
|
||||
|
||||
if self.save:
|
||||
builder.add_from_file(os.path.join(self.config.glade,"wsave_project.ui"))
|
||||
else:
|
||||
builder.add_from_file(os.path.join(self.config.glade,"wopen_project.ui"))
|
||||
builder.connect_signals(self)
|
||||
w_window = builder.get_object("data_project")
|
||||
if current_file != None:
|
||||
w_window.set_filename(current_file)
|
||||
|
||||
file_filter_projects=Gtk.FileFilter()
|
||||
file_filter_projects.set_name(_("DevedeNG projects"))
|
||||
file_filter_projects.add_pattern("*.devedeng")
|
||||
|
||||
file_filter_all=Gtk.FileFilter()
|
||||
file_filter_all.set_name(_("All files"))
|
||||
file_filter_all.add_pattern("*")
|
||||
|
||||
w_window.add_filter(file_filter_projects)
|
||||
w_window.add_filter(file_filter_all)
|
||||
|
||||
w_window.show_all()
|
||||
retval = w_window.run()
|
||||
self.final_filename = w_window.get_filename()
|
||||
w_window.destroy()
|
||||
if (retval == 1):
|
||||
return self.final_filename
|
||||
else:
|
||||
return None
|
||||
46
src/devedeng/preview.py
Normal file
46
src/devedeng/preview.py
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
from gi.repository import Gtk
|
||||
import os
|
||||
import devedeng.configuration_data
|
||||
|
||||
class preview_window:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
def run(self):
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(self.config.gettext_domain)
|
||||
|
||||
builder.add_from_file(os.path.join(self.config.glade,"wpreview.ui"))
|
||||
builder.connect_signals(self)
|
||||
wpreview_window = builder.get_object("dialog_preview")
|
||||
wlength = builder.get_object("length")
|
||||
|
||||
wpreview_window.show_all()
|
||||
wlength.set_value(60.0)
|
||||
retval = wpreview_window.run()
|
||||
self.lvalue = int(wlength.get_value())
|
||||
wpreview_window.destroy()
|
||||
if (retval == 1):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
717
src/devedeng/project.py
Normal file
717
src/devedeng/project.py
Normal file
|
|
@ -0,0 +1,717 @@
|
|||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
from gi.repository import Gtk, Gdk
|
||||
import os
|
||||
import time
|
||||
import shutil
|
||||
import urllib.parse
|
||||
import pickle
|
||||
import math
|
||||
|
||||
import devedeng.file_movie
|
||||
import devedeng.ask
|
||||
import devedeng.add_files
|
||||
import devedeng.message
|
||||
import devedeng.dvd_menu
|
||||
import devedeng.create_disk_window
|
||||
import devedeng.runner
|
||||
import devedeng.settings
|
||||
import devedeng.dvdauthor_converter
|
||||
import devedeng.mkisofs
|
||||
import devedeng.end_job
|
||||
import devedeng.vcdimager_converter
|
||||
import devedeng.shutdown
|
||||
import devedeng.about
|
||||
import devedeng.opensave
|
||||
|
||||
class devede_project:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
self.disc_type = self.config.disc_type
|
||||
self.menu = devedeng.dvd_menu.dvd_menu()
|
||||
|
||||
self.current_title = None
|
||||
self.project_file = None
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(self.config.gettext_domain)
|
||||
|
||||
builder.add_from_file(os.path.join(self.config.glade,"wmain.ui"))
|
||||
builder.connect_signals(self)
|
||||
|
||||
# Interface widgets
|
||||
self.wmain_window = builder.get_object("wmain_window")
|
||||
self.wdisc_size = builder.get_object("disc_size")
|
||||
self.wliststore_files = builder.get_object("liststore_files")
|
||||
self.wfiles = builder.get_object("files")
|
||||
self.wdisc_fill_level = builder.get_object("disc_fill_level")
|
||||
self.wuse_pal = builder.get_object("use_pal")
|
||||
self.wuse_ntsc = builder.get_object("use_ntsc")
|
||||
self.wframe_titles = builder.get_object("frame_titles")
|
||||
|
||||
self.wframe_menu = builder.get_object("frame_menu")
|
||||
self.wcreate_menu = builder.get_object("create_menu")
|
||||
self.wmenu_options = builder.get_object("menu_options")
|
||||
|
||||
|
||||
self.wadd_file = builder.get_object("add_file")
|
||||
self.wdelete_file = builder.get_object("delete_file")
|
||||
self.wup_file = builder.get_object("up_file")
|
||||
self.wdown_file = builder.get_object("down_file")
|
||||
self.wproperties_file = builder.get_object("properties_file")
|
||||
self.wpreview_file = builder.get_object("preview_file")
|
||||
|
||||
self.wcreate_disc = builder.get_object("create_disc")
|
||||
|
||||
selection = self.wfiles.get_selection()
|
||||
selection.set_mode(Gtk.SelectionMode.BROWSE)
|
||||
|
||||
if (self.config.PAL):
|
||||
self.wuse_pal.set_active(True)
|
||||
self.pal = True
|
||||
else:
|
||||
self.wuse_ntsc.set_active(True)
|
||||
self.pal = False
|
||||
|
||||
self.wcreate_menu.set_active(True)
|
||||
|
||||
self.config.connect('disc_type',self.set_type)
|
||||
if (self.disc_type != None):
|
||||
self.set_type(None, self.disc_type)
|
||||
|
||||
self.wmain_window.drag_dest_set(Gtk.DestDefaults.ALL,[],Gdk.DragAction.COPY)
|
||||
targets = Gtk.TargetList.new([])
|
||||
targets.add(Gdk.Atom.intern("text/uri-list", False),0,0)
|
||||
self.wmain_window.drag_dest_set_target_list(targets)
|
||||
|
||||
|
||||
def set_type(self,obj=None,disc_type=None):
|
||||
""" this method sets the disk type to the specified one and adapts the interface
|
||||
in the main window to it. Also leaves everything ready to start creating a
|
||||
new disc """
|
||||
|
||||
if (disc_type != None):
|
||||
self.disc_type = disc_type
|
||||
self.wmain_window.show_all()
|
||||
self.set_interface_status(None)
|
||||
|
||||
# Set the default disc size
|
||||
if (self.disc_type == "dvd") or (self.disc_type == "mkv"):
|
||||
self.wdisc_size.set_active(1) # 4.7 GB DVD
|
||||
else:
|
||||
self.wdisc_size.set_active(3) # 700 MB CD
|
||||
|
||||
if (self.disc_type == "dvd"):
|
||||
self.wframe_menu.show_all()
|
||||
self.wcreate_menu.set_active(True)
|
||||
else:
|
||||
self.wframe_menu.hide()
|
||||
self.wcreate_menu.set_active(False)
|
||||
|
||||
|
||||
def get_current_file(self):
|
||||
|
||||
""" returns the currently selected file """
|
||||
|
||||
selection = self.wfiles.get_selection()
|
||||
model, treeiter = selection.get_selected()
|
||||
|
||||
if treeiter != None:
|
||||
element = model[treeiter][0]
|
||||
position = 0
|
||||
for row in self.wfiles.get_model():
|
||||
item = row.model[row.iter][0]
|
||||
if element == item:
|
||||
break
|
||||
position += 1
|
||||
return ( (element, position, model, treeiter) )
|
||||
else:
|
||||
return ( (None, -1, None, None) )
|
||||
|
||||
|
||||
def get_all_files(self):
|
||||
|
||||
retval = []
|
||||
|
||||
for row in self.wfiles.get_model():
|
||||
retval.append(row.model[row.iter][0])
|
||||
return retval
|
||||
|
||||
|
||||
def set_interface_status(self,b):
|
||||
|
||||
self.wadd_file.set_sensitive(True)
|
||||
self.wdelete_file.set_sensitive(True)
|
||||
self.wup_file.set_sensitive(True)
|
||||
self.wdown_file.set_sensitive(True)
|
||||
self.wproperties_file.set_sensitive(True)
|
||||
self.wpreview_file.set_sensitive(True)
|
||||
|
||||
status = self.wcreate_menu.get_active()
|
||||
self.wmenu_options.set_sensitive(status)
|
||||
|
||||
self.pal = self.wuse_pal.get_active()
|
||||
|
||||
(element, position, model, treeiter) = self.get_current_file()
|
||||
if (element == None):
|
||||
self.wdelete_file.set_sensitive(False)
|
||||
self.wup_file.set_sensitive(False)
|
||||
self.wdown_file.set_sensitive(False)
|
||||
self.wproperties_file.set_sensitive(False)
|
||||
self.wpreview_file.set_sensitive(False)
|
||||
else:
|
||||
nfiles = len(self.wfiles.get_model())
|
||||
if (nfiles < 1):
|
||||
self.wdelete_file.set_sensitive(False)
|
||||
if (position == 0):
|
||||
self.wup_file.set_sensitive(False)
|
||||
if (position == (nfiles-1)):
|
||||
self.wdown_file.set_sensitive(False)
|
||||
|
||||
|
||||
def on_cellrenderertext3_edited(self, widget, path, text):
|
||||
self.wliststore_files[path][0].set_title(text)
|
||||
|
||||
|
||||
def on_use_pal_toggled(self,b):
|
||||
|
||||
self.config.PAL = self.wuse_pal.get_active()
|
||||
self.set_interface_status(None)
|
||||
|
||||
|
||||
def on_wmain_window_delete_event(self,b,e=None):
|
||||
|
||||
ask = devedeng.ask.ask_window()
|
||||
if (ask.run(_("Abort the current DVD and exit?"),_("Exit DeVeDe"))):
|
||||
Gtk.main_quit()
|
||||
return True
|
||||
|
||||
def title_changed(self,obj,new_title):
|
||||
|
||||
for item in self.wliststore_files:
|
||||
element = item.model[item.iter][0]
|
||||
if element == obj:
|
||||
item.model.set_value(item.iter,1,new_title)
|
||||
self.refresh_disc_usage()
|
||||
|
||||
|
||||
def on_add_file_clicked(self,b):
|
||||
|
||||
ask_files = devedeng.add_files.add_files()
|
||||
if (ask_files.run()):
|
||||
self.add_several_files(ask_files.files)
|
||||
|
||||
|
||||
def duration_to_string(self,duration):
|
||||
|
||||
seconds = math.floor(duration%60)
|
||||
minutes = math.floor((duration/60)%60)
|
||||
hours = math.floor(duration/3600)
|
||||
|
||||
output = str(seconds)+"s"
|
||||
if ((hours != 0) or (minutes != 0)):
|
||||
output = str(minutes)+"m "+output
|
||||
if (hours != 0):
|
||||
output = str(hours)+"h "+output
|
||||
return output
|
||||
|
||||
|
||||
def add_several_files(self,file_list):
|
||||
error_list = []
|
||||
for efile in file_list:
|
||||
if efile.startswith("file://"):
|
||||
efile = urllib.parse.unquote(efile[7:])
|
||||
new_file = devedeng.file_movie.file_movie(efile)
|
||||
if (new_file.error):
|
||||
error_list.append(os.path.basename(efile))
|
||||
else:
|
||||
new_file.connect('title_changed',self.title_changed)
|
||||
self.wliststore_files.append([new_file, new_file.title_name,True,self.duration_to_string(new_file.get_duration())])
|
||||
if (len(error_list)!=0):
|
||||
devedeng.message.message_window(_("The following files could not be added:"),_("Error while adding files"),error_list)
|
||||
self.set_interface_status(None)
|
||||
self.refresh_disc_usage()
|
||||
|
||||
|
||||
def on_delete_file_clicked(self,b):
|
||||
|
||||
(element, position, model, treeiter) = self.get_current_file()
|
||||
if (element == None):
|
||||
return
|
||||
|
||||
ask_w = devedeng.ask.ask_window()
|
||||
if (ask_w.run(_("The file <b>%(X)s</b> <i>(%(Y)s)</i> will be removed.") % {"X":element.title_name, "Y":element.file_name},_("Delete file"))):
|
||||
element.delete_file()
|
||||
model.remove(treeiter)
|
||||
self.set_interface_status(None)
|
||||
self.refresh_disc_usage()
|
||||
|
||||
|
||||
def on_up_file_clicked(self,b):
|
||||
|
||||
(element, position, model, treeiter) = self.get_current_file()
|
||||
if (element == None) or (position == 0):
|
||||
return
|
||||
|
||||
last_element = self.wfiles.get_model()[position-1]
|
||||
self.wfiles.get_model().swap(last_element.iter,treeiter)
|
||||
self.set_interface_status(None)
|
||||
|
||||
|
||||
def on_down_file_clicked(self,b):
|
||||
|
||||
(element, position, model, treeiter) = self.get_current_file()
|
||||
if (element == None) or (position == (len(self.wfiles.get_model())-1)):
|
||||
return
|
||||
|
||||
last_element = self.wfiles.get_model()[position+1]
|
||||
self.wfiles.get_model().swap(last_element.iter,treeiter)
|
||||
self.set_interface_status(None)
|
||||
|
||||
|
||||
def on_properties_file_clicked(self,b):
|
||||
(element, position, model, treeiter) = self.get_current_file()
|
||||
if (element == None):
|
||||
return
|
||||
element.properties()
|
||||
|
||||
|
||||
def on_create_menu_toggled(self,b):
|
||||
self.set_interface_status(None)
|
||||
self.refresh_disc_usage()
|
||||
|
||||
|
||||
def on_adjust_disc_usage_clicked(self,b):
|
||||
|
||||
total_resolution = 0
|
||||
fixed_size = 0
|
||||
to_adjust = []
|
||||
for f in self.get_all_files():
|
||||
estimated_size, videorate_fixed_size, audio_rate, sub_rate, width, height, time_length = f.get_size_data()
|
||||
if videorate_fixed_size:
|
||||
fixed_size += estimated_size
|
||||
else:
|
||||
fixed_size += ((audio_rate + sub_rate) * time_length)
|
||||
# 76800 = 320x240, which is the smallest resolution
|
||||
surface = ((float(width) * float(height)) / 76800.0) * float(time_length)
|
||||
to_adjust.append( (f, surface, time_length, audio_rate, height) )
|
||||
total_resolution += surface
|
||||
|
||||
if (self.disc_type == "dvd") and (self.wcreate_menu.get_active()):
|
||||
fixed_size += self.menu.get_estimated_size() * 8.0
|
||||
|
||||
if (self.disc_type == "dvd"):
|
||||
max_bps_base = {0: 9000}
|
||||
min_bps = 300
|
||||
max_total = 10000
|
||||
elif (self.disc_type == "svcd") or (self.disc_type == "cvd"):
|
||||
max_bps_base = {0: 2700}
|
||||
min_bps = 200
|
||||
max_total = 2700
|
||||
elif (self.disc_type == "divx"):
|
||||
max_bps_base = {0: 4854, 720: 9708, 1080: 20000}
|
||||
min_bps = 300
|
||||
max_total = -1
|
||||
elif (self.disc_type == "mkv"):
|
||||
max_bps_base = {0: 192, 240: 2000, 480: 14000, 720: 50000, 1080: 240000}
|
||||
min_bps = 192
|
||||
max_total = -1
|
||||
else:
|
||||
max_bps_base = {0: 9000}
|
||||
min_bps = 300
|
||||
max_total = -1
|
||||
|
||||
if (total_resolution != 0):
|
||||
remaining_disc_size = ((8000.0 * self.get_dvd_size()[0]) - fixed_size) # in kbits
|
||||
for l in to_adjust:
|
||||
f = l[0]
|
||||
surface = l[1]
|
||||
length = l[2]
|
||||
audio_rate = l[3]
|
||||
height = l[4]
|
||||
|
||||
max_bps = max_bps_base[0]
|
||||
resy = 0
|
||||
for bitrates in max_bps_base:
|
||||
if (height >= bitrates) and (resy < bitrates):
|
||||
resy = bitrates
|
||||
max_bps = max_bps_base[bitrates]
|
||||
|
||||
video_rate = (remaining_disc_size * surface) / ( length * total_resolution)
|
||||
if max_total != -1:
|
||||
max2 = max_total - audio_rate
|
||||
if (max2 > max_bps):
|
||||
max2 = max_bps
|
||||
else:
|
||||
max2 = max_bps
|
||||
if (video_rate > max2):
|
||||
video_rate = max2
|
||||
if (video_rate < min_bps):
|
||||
video_rate = min_bps
|
||||
f.set_auto_video_audio_rate(video_rate, audio_rate)
|
||||
|
||||
self.refresh_disc_usage()
|
||||
|
||||
|
||||
def refresh_disc_usage(self):
|
||||
|
||||
used = 0.0
|
||||
|
||||
for f in self.get_all_files():
|
||||
estimated_size = f.get_estimated_size()
|
||||
used += float(estimated_size)
|
||||
|
||||
if self.wcreate_menu.get_active():
|
||||
used += float(self.menu.get_estimated_size())
|
||||
|
||||
used /= 1000.0
|
||||
|
||||
disc_size,minvrate,maxvrate = self.get_dvd_size()
|
||||
|
||||
if used > disc_size:
|
||||
self.wdisc_fill_level.set_fraction(1.0)
|
||||
addv=1
|
||||
else:
|
||||
self.wdisc_fill_level.set_fraction(used / disc_size)
|
||||
addv=0
|
||||
|
||||
self.wdisc_fill_level.set_text(str(addv+int((used / disc_size)*100))+"%")
|
||||
self.wdisc_fill_level.set_show_text(True)
|
||||
|
||||
|
||||
def on_menu_options_clicked(self,b):
|
||||
|
||||
self.menu.show_configuration(self.get_all_files())
|
||||
|
||||
|
||||
def get_dvd_size(self):
|
||||
|
||||
""" Returns the size for the currently selected disk type, and the minimum and maximum
|
||||
videorate for the current video disk """
|
||||
|
||||
active = self.wdisc_size.get_active()
|
||||
|
||||
# here we choose the size in Mbytes for the media
|
||||
if 5==active:
|
||||
size=170.0
|
||||
elif 4==active:
|
||||
size=700.0
|
||||
elif 3==active:
|
||||
size=750.0
|
||||
elif 2==active:
|
||||
size=1100.0
|
||||
elif 1==active:
|
||||
size=4200.0
|
||||
else:
|
||||
size=8000.0
|
||||
|
||||
if self.disc_type=="vcd":
|
||||
minvrate=1152
|
||||
maxvrate=1152
|
||||
elif (self.disc_type == "svcd") or (self.disc_type == "cvd"):
|
||||
minvrate=400
|
||||
maxvrate=2300
|
||||
elif (self.disc_type == "dvd"):
|
||||
minvrate=400
|
||||
maxvrate=8500
|
||||
elif (self.disc_type == "divx") or (self.disc_type == "mkv"):
|
||||
minvrate=300
|
||||
maxvrate=6000
|
||||
else:
|
||||
minvrate = 0
|
||||
maxvrate = 8000
|
||||
|
||||
size *= 0.90 # a safe margin of 10% to ensure that it never will be bigger
|
||||
# (it's important to have in mind the space needed by disk structures like
|
||||
# directories, file entries, and so on)
|
||||
|
||||
return size,minvrate,maxvrate
|
||||
|
||||
|
||||
def on_disc_size_changed(self,c):
|
||||
|
||||
self.refresh_disc_usage()
|
||||
|
||||
|
||||
def on_create_disc_clicked(self,b):
|
||||
|
||||
if self.disc_type == "dvd":
|
||||
max_files = 62
|
||||
else:
|
||||
max_files = -1
|
||||
|
||||
file_movies = self.get_all_files()
|
||||
t = len(file_movies)
|
||||
if (max_files != -1) and (t > max_files):
|
||||
devedeng.message.message_window(_("The limit for this format is %(l)d files, but your project has %(h)d.") % {"l": max_files, "h" : t}, _("Too many files in the project"))
|
||||
return
|
||||
|
||||
data = devedeng.create_disk_window.create_disk_window()
|
||||
if (not data.run()):
|
||||
return
|
||||
|
||||
if os.path.exists(data.path):
|
||||
ask_w = devedeng.ask.ask_window()
|
||||
retval = ask_w.run(_("The selected folder already exists. To create the project, Devede must delete it.\nIf you continue, the folder\n\n <b>%s</b>\n\n and all its contents <b>will be deleted</b>. Continue?") % data.path,_("Delete folder"))
|
||||
if retval:
|
||||
# delete only the bare minimun needed
|
||||
shutil.rmtree(os.path.join(data.path,"dvd_tree"),True)
|
||||
shutil.rmtree(os.path.join(data.path,"menu"),True)
|
||||
shutil.rmtree(os.path.join(data.path,"movies"),True)
|
||||
shutil.rmtree(os.path.join(data.path,"xml_data"),True)
|
||||
if self.config.disc_type == "dvd":
|
||||
try:
|
||||
os.unlink(os.path.join(data.path,data.name+".iso"))
|
||||
except:
|
||||
pass
|
||||
if (self.config.disc_type == "vcd") or (self.config.disc_type == "svcd") or (self.config.disc_type == "cvd"):
|
||||
try:
|
||||
os.unlink(os.path.join(data.path,data.name+".bin"))
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
os.unlink(os.path.join(data.path,data.name+".cue"))
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
return
|
||||
|
||||
self.shutdown = data.shutdown
|
||||
|
||||
run_window = devedeng.runner.runner()
|
||||
|
||||
final_dependencies = []
|
||||
|
||||
if (self.disc_type == "dvd") and (self.wcreate_menu.get_active()):
|
||||
processes,menu_entries = self.menu.create_dvd_menus(file_movies, data.path)
|
||||
for p in processes:
|
||||
run_window.add_process(p)
|
||||
final_dependencies.append(p)
|
||||
else:
|
||||
menu_entries = None
|
||||
|
||||
movie_folder = os.path.join(data.path,"movies")
|
||||
try:
|
||||
os.makedirs(movie_folder)
|
||||
except:
|
||||
pass
|
||||
counter = 0
|
||||
for movie in file_movies:
|
||||
p = movie.do_conversion(os.path.join(movie_folder,"movie_"+str(counter)+".mpg"))
|
||||
run_window.add_process(p)
|
||||
final_dependencies.append(p)
|
||||
counter += 1
|
||||
|
||||
if (self.disc_type == "dvd"):
|
||||
if (self.menu.at_startup == "menu_show_at_startup"):
|
||||
start_with_menu = True
|
||||
else:
|
||||
start_with_menu = False
|
||||
dvdauthor = devedeng.dvdauthor_converter.dvdauthor_converter()
|
||||
dvdauthor.create_dvd_project(data.path, data.name, file_movies, menu_entries, start_with_menu)
|
||||
# dvdauthor must wait until all the files have been converted
|
||||
for element in final_dependencies:
|
||||
dvdauthor.add_dependency(element)
|
||||
run_window.add_process(dvdauthor)
|
||||
|
||||
cv = devedeng.converter.converter.get_converter()
|
||||
isocreator = cv.get_mkiso()()
|
||||
isocreator.create_iso(data.path, data.name)
|
||||
isocreator.add_dependency(dvdauthor)
|
||||
run_window.add_process(isocreator)
|
||||
self.disc_image_name = os.path.join(data.path,data.name+".iso")
|
||||
elif (self.disc_type == "vcd") or (self.disc_type == "svcd") or (self.disc_type == "cvd"):
|
||||
vcdcreator = devedeng.vcdimager_converter.vcdimager_converter()
|
||||
vcdcreator.create_cd_project(data.path, data.name, file_movies)
|
||||
for element in final_dependencies:
|
||||
vcdcreator.add_dependency(element)
|
||||
run_window.add_process(vcdcreator)
|
||||
self.disc_image_name = os.path.join(data.path,data.name+".cue")
|
||||
else:
|
||||
self.disc_image_name = None
|
||||
|
||||
run_window.connect("done",self.disc_done)
|
||||
self.wmain_window.hide()
|
||||
self.time_start = time.time()
|
||||
run_window.run()
|
||||
|
||||
|
||||
def disc_done(self,object,value):
|
||||
|
||||
if self.shutdown:
|
||||
Gtk.main_quit()
|
||||
devedeng.shutdown.shutdown()
|
||||
|
||||
if value == 0:
|
||||
ended = devedeng.end_job.end_window()
|
||||
if self.disc_image_name == None:
|
||||
do_burn = False
|
||||
else:
|
||||
do_burn = True
|
||||
if (ended.run(time.time() - self.time_start, do_burn)):
|
||||
cv = devedeng.converter.converter.get_converter()
|
||||
burner = cv.get_burner()()
|
||||
burner.burn(self.disc_image_name)
|
||||
run_window = devedeng.runner.runner(False)
|
||||
run_window.add_process(burner)
|
||||
run_window.connect("done", self.disc_done2)
|
||||
run_window.run()
|
||||
return
|
||||
|
||||
self.wmain_window.show()
|
||||
|
||||
|
||||
def disc_done2(self,object, value):
|
||||
|
||||
self.wmain_window.show()
|
||||
|
||||
|
||||
def on_preview_file_clicked(self,b):
|
||||
|
||||
(element, position, model, treeiter) = self.get_current_file()
|
||||
if (element == None):
|
||||
return
|
||||
element.do_preview()
|
||||
|
||||
|
||||
def on_settings_activate(self,b):
|
||||
|
||||
w = devedeng.settings.settings_window()
|
||||
|
||||
|
||||
def on_about_activate(self,b):
|
||||
|
||||
w = devedeng.about.about_window()
|
||||
|
||||
|
||||
def on_new_activate(self,b):
|
||||
|
||||
w = devedeng.ask.ask_window()
|
||||
if w.run(_("Close current project and start a fresh one?"), _("New project")):
|
||||
self.wliststore_files.clear()
|
||||
self.project_file = None
|
||||
self.wmain_window.hide()
|
||||
devedeng.choose_disc_type.choose_disc_type()
|
||||
|
||||
|
||||
def on_wmain_window_drag_motion(self,wid, context, x, y, time):
|
||||
Gdk.drag_status(context, Gdk.DragAction.COPY, time)
|
||||
return True
|
||||
|
||||
|
||||
def on_wmain_window_drag_drop(self, wid, context, x, y, time):
|
||||
# Used with windows drag and drop
|
||||
return True
|
||||
|
||||
|
||||
def on_wmain_window_drag_data_received(self, widget, drag_context, x,y, data, info, time):
|
||||
uris = data.get_uris()
|
||||
self.add_several_files(uris)
|
||||
Gtk.drag_finish (drag_context, True, Gdk.DragAction.COPY, time);
|
||||
|
||||
|
||||
def on_save_activate(self,b):
|
||||
if self.project_file != None:
|
||||
self.save_current_project()
|
||||
else:
|
||||
self.on_save_as_activate(None)
|
||||
|
||||
|
||||
def on_save_as_activate(self,b):
|
||||
|
||||
while True:
|
||||
w = devedeng.opensave.opensave_window(True)
|
||||
retval = w.run(self.project_file)
|
||||
if retval == None:
|
||||
return
|
||||
if not retval.endswith(".devedeng"):
|
||||
retval += ".devedeng"
|
||||
if os.path.isfile(retval):
|
||||
w = devedeng.ask.ask_window()
|
||||
if not w.run(_("The file already exists. Overwrite it?"), _("The file already exists")):
|
||||
continue
|
||||
self.project_file = retval
|
||||
self.save_current_project()
|
||||
return
|
||||
|
||||
|
||||
def on_load_activate(self,b):
|
||||
w = devedeng.opensave.opensave_window(False)
|
||||
retval = w.run()
|
||||
if retval != None:
|
||||
self.load_project(retval)
|
||||
|
||||
|
||||
def save_current_project(self):
|
||||
|
||||
project = {}
|
||||
|
||||
project["PAL"] = self.wuse_pal.get_active()
|
||||
project["create_menu"] = self.wcreate_menu.get_active()
|
||||
project["disc_type"] = self.config.disc_type
|
||||
project["disc_size"] = self.wdisc_size.get_active()
|
||||
project["files"] = []
|
||||
f = self.get_all_files()
|
||||
for i in f:
|
||||
project["files"].append(i.store_file())
|
||||
if self.disc_type == "dvd":
|
||||
project["menu"] = self.menu.store_menu()
|
||||
|
||||
with open(self.project_file, 'wb') as f:
|
||||
pickle.dump(project, f, 3)
|
||||
|
||||
|
||||
def load_project(self,project_file):
|
||||
|
||||
self.wliststore_files.clear()
|
||||
self.project_file = project_file
|
||||
with open(project_file, 'rb') as f:
|
||||
project = pickle.load(f)
|
||||
if "disc_type" in project:
|
||||
self.config.set_disc_type(project["disc_type"])
|
||||
if "PAL" in project:
|
||||
self.wuse_pal.set_active(project["PAL"])
|
||||
if "create_menu" in project:
|
||||
self.wcreate_menu.set_active(project["create_menu"])
|
||||
if "disc_size" in project:
|
||||
self.wdisc_size.set_active(project["disc_size"])
|
||||
if "menu" in project:
|
||||
self.menu.restore_menu(project["menu"])
|
||||
if "files" in project:
|
||||
error_list = []
|
||||
for efile in project["files"]:
|
||||
new_file = devedeng.file_movie.file_movie(efile["file_name"])
|
||||
if (new_file.error):
|
||||
error_list.append(os.path.basename(efile["file_name"]))
|
||||
else:
|
||||
new_file.restore_file(efile)
|
||||
new_file.connect('title_changed',self.title_changed)
|
||||
self.wliststore_files.append([new_file, new_file.title_name,True,self.duration_to_string(new_file.get_duration())])
|
||||
if (len(error_list)!=0):
|
||||
devedeng.message.message_window(_("The following files in the project could not be added again:"),_("Error while adding files"),error_list)
|
||||
self.set_interface_status(None)
|
||||
self.refresh_disc_usage()
|
||||
|
||||
|
||||
def on_multiproperties_activate(self,b):
|
||||
|
||||
e = devedeng.file_movie.file_movie(None,self.get_all_files())
|
||||
e.properties()
|
||||
161
src/devedeng/runner.py
Normal file
161
src/devedeng/runner.py
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
from gi.repository import Gtk,GObject
|
||||
import os
|
||||
import devedeng.configuration_data
|
||||
import devedeng.error
|
||||
import devedeng.ask
|
||||
|
||||
class runner(GObject.GObject):
|
||||
|
||||
__gsignals__ = {'done': (GObject.SIGNAL_RUN_FIRST, None,(int,))}
|
||||
|
||||
def __init__(self, show_window = True):
|
||||
|
||||
GObject.GObject.__init__(self)
|
||||
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
if (self.config.multicore > 0):
|
||||
if self.config.cores < self.config.multicore:
|
||||
self.cores = self.config.cores
|
||||
else:
|
||||
self.cores = self.config.multicore
|
||||
else:
|
||||
self.cores = self.config.cores - self.config.multicore
|
||||
if (self.cores <= 0):
|
||||
self.cores = 1
|
||||
|
||||
self.proc_list = []
|
||||
self.running = 0
|
||||
|
||||
self.builder = Gtk.Builder()
|
||||
self.builder.set_translation_domain(self.config.gettext_domain)
|
||||
|
||||
self.builder.add_from_file(os.path.join(self.config.glade,"wprogress.ui"))
|
||||
self.builder.connect_signals(self)
|
||||
self.wprogress = self.builder.get_object("progress")
|
||||
if show_window:
|
||||
self.wprogress.show_all()
|
||||
self.wtotal = self.builder.get_object("progress_total")
|
||||
|
||||
progress_frame = self.builder.get_object("progress_frame")
|
||||
box = Gtk.Box(Gtk.Orientation.VERTICAL, 0)
|
||||
progress_frame.add(box)
|
||||
box.show()
|
||||
self.progress_bars = []
|
||||
self.used_progress_bars = []
|
||||
for c in range(0,self.cores):
|
||||
f = Gtk.Frame()
|
||||
p = Gtk.ProgressBar()
|
||||
p.set_orientation(Gtk.Orientation.HORIZONTAL)
|
||||
p.set_show_text(True)
|
||||
f.add(p)
|
||||
# A frame, a progress bar, and the process running in that bar
|
||||
self.progress_bars.append([f, p, None])
|
||||
box.pack_start(f,True,True,0)
|
||||
box.set_orientation(Gtk.Orientation.VERTICAL)
|
||||
self.total_processes = 0
|
||||
self.error = False
|
||||
|
||||
|
||||
def add_process(self,process):
|
||||
|
||||
if (self.proc_list.count(process) == 0):
|
||||
self.proc_list.append(process)
|
||||
|
||||
for p in process.childs:
|
||||
self.add_process(p)
|
||||
|
||||
self.total_processes = len(self.proc_list)
|
||||
|
||||
|
||||
def on_cancel_clicked(self,b):
|
||||
|
||||
ask_w = devedeng.ask.ask_window()
|
||||
retval = ask_w.run(_("Cancel the current job?"),_("Cancel the current job?"))
|
||||
if retval:
|
||||
self.error = True
|
||||
for element in self.proc_list:
|
||||
element.cancel()
|
||||
self.wprogress.destroy()
|
||||
self.emit("done",1) # there was an error
|
||||
return
|
||||
|
||||
def run(self, clear_log = True):
|
||||
|
||||
if clear_log:
|
||||
self.config.clear_log()
|
||||
|
||||
for element in self.proc_list:
|
||||
# each element has three items:
|
||||
# * the process object
|
||||
# * the list of dependencies, or None if there are no more dependencies
|
||||
# * the progress bar being used by this process
|
||||
if (element.dependencies == None) and (element.progress_bar == None):
|
||||
element.connect("ended",self.process_ended)
|
||||
element.run(self.progress_bars[0])
|
||||
element.progress_bar = self.progress_bars[0]
|
||||
self.used_progress_bars.append(self.progress_bars[0])
|
||||
if (len(self.progress_bars) > 1):
|
||||
self.progress_bars = self.progress_bars[1:]
|
||||
else:
|
||||
self.progress_bars = []
|
||||
break
|
||||
self.wtotal.set_text(str(self.total_processes - len(self.proc_list))+"/"+str(self.total_processes))
|
||||
self.wtotal.set_fraction((float(self.total_processes - len(self.proc_list)))/(float(self.total_processes)))
|
||||
|
||||
|
||||
def process_ended(self,process, retval):
|
||||
|
||||
if self.error:
|
||||
return
|
||||
|
||||
if retval != 0:
|
||||
self.error = True
|
||||
for element in self.proc_list:
|
||||
element.cancel()
|
||||
self.wprogress.destroy()
|
||||
devedeng.error.error_window()
|
||||
self.emit("done",1) # there was an error
|
||||
return
|
||||
|
||||
# move the progress bar used by this process to the list of available progress bars
|
||||
tmp = []
|
||||
for e in self.used_progress_bars:
|
||||
if (process.progress_bar == e):
|
||||
self.progress_bars.append(e)
|
||||
e[0].hide()
|
||||
else:
|
||||
tmp.append(e)
|
||||
self.used_progress_bars = tmp
|
||||
|
||||
# remove this process from the list of processes, and remove it from the dependencies in other processes
|
||||
tmp = []
|
||||
for e in self.proc_list:
|
||||
if (e != process):
|
||||
tmp.append(e)
|
||||
e.remove_dependency(process)
|
||||
self.proc_list = tmp
|
||||
|
||||
# launch a new process
|
||||
if (len(self.proc_list) != 0):
|
||||
self.run(False)
|
||||
else:
|
||||
self.wprogress.destroy()
|
||||
self.emit("done",0) # no error
|
||||
121
src/devedeng/settings.py
Normal file
121
src/devedeng/settings.py
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
from gi.repository import Gtk
|
||||
import os
|
||||
import devedeng.configuration_data
|
||||
import devedeng.interface_manager
|
||||
import gettext
|
||||
import devedeng.converter
|
||||
|
||||
class settings_window(devedeng.interface_manager.interface_manager):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
devedeng.interface_manager.interface_manager.__init__(self)
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
if (self.config.multicore > 0):
|
||||
if self.config.cores < self.config.multicore:
|
||||
cores = self.config.cores
|
||||
else:
|
||||
cores = self.config.multicore
|
||||
else:
|
||||
if self.config.cores <= -self.config.multicore:
|
||||
cores = -self.config.cores+1
|
||||
else:
|
||||
cores = self.config.multicore
|
||||
|
||||
self.core_elements = {}
|
||||
list_core_elements = []
|
||||
default_value = _("Use all cores")
|
||||
counter = 1
|
||||
for c in range(self.config.cores-1,-self.config.cores, -1):
|
||||
if c > 0:
|
||||
translated_string = gettext.ngettext("Use %(X)d core","Use %(X)d cores",c) % {"X":c}
|
||||
value = c
|
||||
if c == cores:
|
||||
default_value = translated_string
|
||||
counter += 1
|
||||
elif c < 0:
|
||||
translated_string = gettext.ngettext("Use all except %(X)d core","Use all except %(X)d cores", -c) % {"X": -c}
|
||||
value = c
|
||||
if c == cores:
|
||||
default_value = translated_string
|
||||
counter += 1
|
||||
else:
|
||||
translated_string = _("Use all cores")
|
||||
value = c
|
||||
|
||||
self.core_elements[translated_string] = value
|
||||
list_core_elements.append(translated_string)
|
||||
|
||||
self.add_combobox("multicore",list_core_elements,default_value)
|
||||
self.add_filebutton("tempo_path", self.config.tmp_folder)
|
||||
|
||||
c = devedeng.converter.converter.get_converter()
|
||||
(analizers, players, menuers, converters, burners, mkiso) = c.get_available_programs()
|
||||
|
||||
self.add_combobox("analizer", analizers,self.config.film_analizer)
|
||||
self.add_combobox("player", players,self.config.film_player)
|
||||
self.add_combobox("converter", converters,self.config.film_converter,self.set_data_converter)
|
||||
self.add_combobox("menuer", menuers,self.config.menu_converter)
|
||||
self.add_combobox("mkiso", mkiso, self.config.mkiso)
|
||||
self.add_combobox("burner", burners, self.config.burner)
|
||||
|
||||
self.builder = Gtk.Builder()
|
||||
self.builder.set_translation_domain(self.config.gettext_domain)
|
||||
|
||||
self.builder.add_from_file(os.path.join(self.config.glade,"wsettings.ui"))
|
||||
self.builder.connect_signals(self)
|
||||
wsettings_window = self.builder.get_object("settings")
|
||||
self.wconverter = self.builder.get_object("converter")
|
||||
self.wtypes = self.builder.get_object("disc_types_supported")
|
||||
|
||||
wsettings_window.show_all()
|
||||
self.update_ui(self.builder)
|
||||
self.set_data_converter(None)
|
||||
|
||||
retval = wsettings_window.run()
|
||||
self.store_ui(self.builder)
|
||||
wsettings_window.destroy()
|
||||
|
||||
if retval == 1:
|
||||
self.config.multicore = self.core_elements[self.multicore]
|
||||
self.config.tmp_folder = self.tempo_path
|
||||
self.config.film_analizer = self.analizer
|
||||
self.config.film_player = self.player
|
||||
self.config.film_converter = self.converter
|
||||
self.config.menu_converter = self.menuer
|
||||
self.config.burner = self.burner
|
||||
self.config.mkiso = self.mkiso
|
||||
self.config.save_config()
|
||||
|
||||
def set_data_converter(self,b):
|
||||
|
||||
self.store_ui(self.builder)
|
||||
cv = devedeng.converter.converter.get_converter()
|
||||
cv2 = cv.get_disc_converter_by_name(self.converter)
|
||||
data = ""
|
||||
for t in cv2.disc_types:
|
||||
if data != "":
|
||||
data += ", "
|
||||
data += t
|
||||
if data != "":
|
||||
self.wtypes.set_text(data)
|
||||
else:
|
||||
self.wtypes.set_text(_("No discs supported"))
|
||||
52
src/devedeng/shutdown.py
Normal file
52
src/devedeng/shutdown.py
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
from gi.repository import Gio, GLib
|
||||
|
||||
class shutdown:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
# First, try with logind
|
||||
try:
|
||||
bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
|
||||
bus.call_sync("org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager",
|
||||
"PowerOff", GLib.Variant_boolean('(bb)', ( False , False) ), None, Gio.DBusCallFlags.NONE, -1, None)
|
||||
except:
|
||||
failure=True
|
||||
|
||||
if (failure):
|
||||
failure=False
|
||||
|
||||
# If it fails, try with ConsoleKit
|
||||
try:
|
||||
bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
|
||||
bus.call_sync("org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit.Manager",
|
||||
"Stop", None, None, Gio.DBusCallFlags.NONE, -1, None)
|
||||
except:
|
||||
failure=True
|
||||
|
||||
if (failure):
|
||||
failure=False
|
||||
|
||||
# If it fails, try with HAL
|
||||
try:
|
||||
bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
|
||||
bus.call_sync("org.freedesktop.Hal", "/org/freedesktop/Hal/devices/computer","org.freedesktop.Hal.Device.SystemPowerManagement",
|
||||
"Shutdown", None, None, Gio.DBusCallFlags.NONE, -1, None)
|
||||
except:
|
||||
failure=True
|
||||
117
src/devedeng/subtitles_mux.py
Normal file
117
src/devedeng/subtitles_mux.py
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import devedeng.configuration_data
|
||||
import devedeng.executor
|
||||
|
||||
class subtitles_mux(devedeng.executor.executor):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
devedeng.executor.executor.__init__(self)
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
def multiplex_subtitles(self, file_path, subtitles_path, subt_codepage, subt_lang,
|
||||
subt_upper, font_size, pal, force_subtitles, aspect, duration, stream_id, fill_color, outline_color, outline_thick):
|
||||
|
||||
if len(fill_color) == 4:
|
||||
fill_color = fill_color[:3]
|
||||
if len(outline_color) == 4:
|
||||
outline_color = outline_color[:3]
|
||||
|
||||
self.subt_path = file_path
|
||||
self.duration = duration
|
||||
self.text = _("Adding %(L)s subtitles to %(X)s") % {"X": os.path.basename(file_path), "L": subt_lang}
|
||||
|
||||
out_xml = open(file_path+".xml","w")
|
||||
out_xml.write('<subpictures format="')
|
||||
if pal:
|
||||
out_xml.write('PAL')
|
||||
else:
|
||||
out_xml.write('NTSC')
|
||||
out_xml.write('">\n')
|
||||
out_xml.write('\t<stream>\n')
|
||||
out_xml.write('\t\t<textsub filename="')
|
||||
out_xml.write(self.expand_xml(subtitles_path))
|
||||
out_xml.write('" characterset="')
|
||||
out_xml.write(self.expand_xml(subt_codepage))
|
||||
out_xml.write('" fontsize="')
|
||||
out_xml.write(str(font_size))
|
||||
if subt_upper:
|
||||
out_xml.write('" bottom-margin="50')
|
||||
|
||||
out_xml.write('" fill-color="#%02X%02X%02X"' % tuple([fill_color[i] * 255 for i in range(len(fill_color))]))
|
||||
out_xml.write(' outline-color="#%02X%02X%02X"' % tuple([outline_color[i] * 255 for i in range(len(outline_color))]))
|
||||
out_xml.write(' outline-thickness="%d"' % outline_thick)
|
||||
out_xml.write(' font="arial" horizontal-alignment="center" vertical-alignment="bottom" aspect="')
|
||||
out_xml.write(str(aspect))
|
||||
out_xml.write('" force="')
|
||||
if force_subtitles:
|
||||
out_xml.write('yes')
|
||||
else:
|
||||
out_xml.write('no')
|
||||
out_xml.write('" />\n')
|
||||
out_xml.write('\t</stream>\n')
|
||||
out_xml.write('</subpictures>')
|
||||
out_xml.close()
|
||||
|
||||
self.command_var=[]
|
||||
self.command_var.append("spumux")
|
||||
mode = self.config.disc_type
|
||||
if mode == "vcd":
|
||||
mode = "svcd"
|
||||
self.command_var.append("-m")
|
||||
self.command_var.append(mode)
|
||||
self.command_var.append("-s")
|
||||
self.command_var.append(str(stream_id))
|
||||
self.command_var.append(file_path+".xml")
|
||||
self.stdin_file = file_path+".tmp"
|
||||
self.stdout_file = file_path
|
||||
|
||||
|
||||
def pre_function(self):
|
||||
|
||||
final_path = self.subt_path+".tmp"
|
||||
if os.path.exists(final_path):
|
||||
os.remove(final_path)
|
||||
os.rename(self.subt_path, final_path)
|
||||
|
||||
|
||||
def process_stderr(self,data):
|
||||
|
||||
if (data == None) or (len(data) == 0):
|
||||
return
|
||||
|
||||
if self.duration == 0:
|
||||
return
|
||||
|
||||
if data[0].startswith("STAT: "):
|
||||
time_pos = data[0][6:].split(":")
|
||||
current_time = 0
|
||||
for t in time_pos:
|
||||
current_time *= 60
|
||||
current_time += float(t)
|
||||
t = current_time / self.duration
|
||||
self.progress_bar[1].set_fraction(t)
|
||||
self.progress_bar[1].set_text("%.1f%%" % (100.0 * t))
|
||||
|
||||
return
|
||||
96
src/devedeng/title.py
Normal file
96
src/devedeng/title.py
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
from gi.repository import Gtk,GObject
|
||||
import os
|
||||
|
||||
class title(GObject.GObject):
|
||||
|
||||
counter = 0
|
||||
|
||||
def __init__(self,config,file_treeview,original_liststore,title_name = None):
|
||||
|
||||
GObject.GObject.__init__(self)
|
||||
self.config = config
|
||||
self.file_treeview = file_treeview
|
||||
if (title_name == None):
|
||||
title.counter += 1
|
||||
self.title_name = _("Title %(X)d") % {"X":title.counter}
|
||||
else:
|
||||
self.title_name = title_name
|
||||
self.post_action = "stop"
|
||||
columns = []
|
||||
for iterator in range(0, original_liststore.get_n_columns()):
|
||||
columns.append(original_liststore.get_column_type(iterator))
|
||||
self.files = Gtk.ListStore()
|
||||
self.files.set_column_types(columns)
|
||||
|
||||
def set_type(self,disc_type):
|
||||
|
||||
self.disc_type = disc_type
|
||||
|
||||
def properties(self):
|
||||
|
||||
builder = Gtk.Builder()
|
||||
builder.set_translation_domain(self.config.gettext_domain)
|
||||
|
||||
builder.add_from_file(os.path.join(self.config.glade,"wtitle_properties.ui"))
|
||||
builder.connect_signals(self)
|
||||
|
||||
# Interface widgets
|
||||
wtitle_properties = builder.get_object("title_properties")
|
||||
wtitle = builder.get_object("entry_title")
|
||||
wplay_first = builder.get_object("play_first")
|
||||
wplay_prev = builder.get_object("play_previous")
|
||||
wplay_again = builder.get_object("play_again")
|
||||
wplay_next = builder.get_object("play_next")
|
||||
wplay_last = builder.get_object("play_last")
|
||||
|
||||
wtitle.set_text(self.title_name)
|
||||
builder.get_object(self.post_action).set_active(True)
|
||||
|
||||
wtitle_properties.show_all()
|
||||
retval = wtitle_properties.run()
|
||||
if (retval == 2):
|
||||
self.title_name = wtitle.get_text()
|
||||
if (wplay_first.get_active()):
|
||||
self.post_action = "play_first"
|
||||
elif (wplay_prev.get_active()):
|
||||
self.post_action = "play_previous"
|
||||
elif (wplay_again.get_active()):
|
||||
self.post_action = "play_again"
|
||||
elif (wplay_next.get_active()):
|
||||
self.post_action = "play_next"
|
||||
elif (wplay_last.get_active()):
|
||||
self.post_action = "play_last"
|
||||
else:
|
||||
self.post_action = "stop"
|
||||
|
||||
wtitle_properties.destroy()
|
||||
|
||||
|
||||
def delete_title(self):
|
||||
|
||||
print("Deleted title "+self.title_name)
|
||||
|
||||
def refresh(self):
|
||||
|
||||
self.file_treeview.set_model(self.files)
|
||||
|
||||
def add_file(self,new_file):
|
||||
|
||||
self.files.append([new_file.file_name, new_file])
|
||||
55
src/devedeng/vcdimager_converter.py
Normal file
55
src/devedeng/vcdimager_converter.py
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
import os
|
||||
import devedeng.configuration_data
|
||||
import devedeng.executor
|
||||
|
||||
class vcdimager_converter(devedeng.executor.executor):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
devedeng.executor.executor.__init__(self)
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
def create_cd_project (self, path, name, file_movies):
|
||||
|
||||
self.command_var=[]
|
||||
self.command_var.append("vcdimager")
|
||||
self.command_var.append("-c")
|
||||
self.command_var.append(os.path.join(path,name+".cue"))
|
||||
self.command_var.append("-b")
|
||||
self.command_var.append(os.path.join(path,name+".bin"))
|
||||
self.command_var.append("-t")
|
||||
if self.config.disc_type == "vcd":
|
||||
self.command_var.append("vcd2")
|
||||
else:
|
||||
self.command_var.append("svcd")
|
||||
for element in file_movies:
|
||||
self.command_var.append(element.converted_filename)
|
||||
self.text = _("Creating CD image")
|
||||
|
||||
|
||||
def process_stdout(self,data):
|
||||
print("Stdout: "+str(data))
|
||||
return
|
||||
|
||||
def process_stderr(self,data):
|
||||
print("Stderr: "+str(data))
|
||||
return
|
||||
58
src/devedeng/vlc.py
Normal file
58
src/devedeng/vlc.py
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# Copyright 2014 (C) Raster Software Vigo (Sergio Costas)
|
||||
#
|
||||
# This file is part of DeVeDe-NG
|
||||
#
|
||||
# DeVeDe-NG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# DeVeDe-NG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
import subprocess
|
||||
import devedeng.configuration_data
|
||||
import devedeng.executor
|
||||
|
||||
class vlc(devedeng.executor.executor):
|
||||
|
||||
supports_analize = False
|
||||
supports_play = True
|
||||
supports_convert = False
|
||||
supports_menu = False
|
||||
supports_mkiso = False
|
||||
supports_burn = False
|
||||
display_name = "VLC"
|
||||
|
||||
@staticmethod
|
||||
def check_is_installed():
|
||||
try:
|
||||
handle = subprocess.Popen(["vlc","-h"], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
|
||||
(stdout, stderr) = handle.communicate()
|
||||
if 0==handle.wait():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def __init__(self):
|
||||
|
||||
devedeng.executor.executor.__init__(self)
|
||||
self.config = devedeng.configuration_data.configuration.get_config()
|
||||
|
||||
def play_film(self,file_name):
|
||||
|
||||
command_line = ["vlc", "--play-and-exit",file_name]
|
||||
self.launch_process(command_line)
|
||||
|
||||
def process_stdout(self,data):
|
||||
return
|
||||
|
||||
def process_stderr(self,data):
|
||||
return
|
||||
Loading…
Add table
Add a link
Reference in a new issue