Renamed module from devede to devedeng

Now it uses the 'locale' folder to create the locales list names.
This commit is contained in:
Sergio Costas 2015-01-29 00:34:47 +01:00
parent cfb9238a73
commit bc6fa87b3c
43 changed files with 241 additions and 245 deletions

19
src/devedeng/__init__.py Normal file
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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

View 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()

View 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
View 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]

View 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
View 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)

View 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
View 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
View 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
View 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('&','&amp;')
text=text.replace('<','&lt;')
text=text.replace('>','&gt;')
text=text.replace('"','&quot;')
text=text.replace("'",'&apos;')
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
View 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
View 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
View 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
View 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()

View 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

View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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])

View 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
View 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