From 25a66e5ff609eeb03907030f16c408197b646ecc Mon Sep 17 00:00:00 2001 From: Andreas Frisch Date: Wed, 29 Oct 2008 16:57:49 +0000 Subject: [PATCH] allow creating .ISO files. allow burning .ISO images or preauthored dvd structures (e.g. from failed burning attempts) to DVD. allow choice between ISO9660 level 1, ISO9660 version 2 and UDF as file system for data DVDs. new options are available for selection within settings screen. burning dvd from image is available as option in menu. --- .../Plugins/Extensions/DVDBurn/DVDProject.py | 6 +- .../DVDBurn/DreamboxDVDtemplate.ddvdp.xml | 6 +- .../Plugins/Extensions/DVDBurn/Process.py | 141 +++++++++++++----- .../Extensions/DVDBurn/ProjectSettings.py | 43 ++++-- .../Plugins/Extensions/DVDBurn/TitleList.py | 15 +- 5 files changed, 154 insertions(+), 57 deletions(-) diff --git a/lib/python/Plugins/Extensions/DVDBurn/DVDProject.py b/lib/python/Plugins/Extensions/DVDBurn/DVDProject.py index 73a2de57..8d02cb22 100644 --- a/lib/python/Plugins/Extensions/DVDBurn/DVDProject.py +++ b/lib/python/Plugins/Extensions/DVDBurn/DVDProject.py @@ -1,4 +1,3 @@ - from Tools.Directories import fileExists from Components.config import config, ConfigSubsection, ConfigInteger, ConfigYesNo, ConfigText, ConfigSelection, getConfigListEntry, ConfigSequence @@ -33,6 +32,9 @@ class DVDProject: self.settings = ConfigSubsection() self.settings.name = ConfigText(fixed_size = False, visible_width = 40) self.settings.authormode = ConfigSelection(choices = [("menu_linked", _("Linked titles with a DVD menu")), ("just_linked", _("Direct playback of linked titles without menu")), ("menu_seperate", _("Seperate titles with a main menu")), ("data_ts", _("Dreambox format data DVD (HDTV compatible)"))]) + self.settings.output = ConfigSelection(choices = [("iso", _("Create DVD-ISO")), ("dvd", _("Burn DVD"))]) + self.settings.isopath = ConfigText(fixed_size = False, visible_width = 40) + self.settings.dataformat = ConfigSelection(choices = [("iso9660_1", ("ISO9660 Level 1")), ("iso9660_4", ("ISO9660 version 2")), ("udf", ("UDF"))]) self.settings.menubg = ConfigFilename() self.settings.menuaudio = ConfigFilename() self.settings.titleformat = ConfigText(fixed_size = False, visible_width = 40) @@ -45,7 +47,7 @@ class DVDProject: self.settings.space = ConfigPixelvals() self.settings.vmgm = ConfigFilename() self.settings.autochapter = ConfigInteger(default = 0, limits = (0, 99)) - self.filekeys = ["vmgm", "menubg", "menuaudio", "font_face"] + self.filekeys = ["vmgm", "menubg", "menuaudio", "font_face", "isopath"] def addService(self, service): import DVDTitle diff --git a/lib/python/Plugins/Extensions/DVDBurn/DreamboxDVDtemplate.ddvdp.xml b/lib/python/Plugins/Extensions/DVDBurn/DreamboxDVDtemplate.ddvdp.xml index c7fef58c..d9831e63 100644 --- a/lib/python/Plugins/Extensions/DVDBurn/DreamboxDVDtemplate.ddvdp.xml +++ b/lib/python/Plugins/Extensions/DVDBurn/DreamboxDVDtemplate.ddvdp.xml @@ -13,6 +13,10 @@ color_headline="[0, 0, 128]" font_face="/usr/share/fonts/nmsbd.ttf" font_size="[46, 24, 14]" - space="[120, 32, 56]" /> + space="[120, 32, 56]" + output="dvd" + isopath="/media/hdd/movie/" + dataformat="iso9660_4" + /> diff --git a/lib/python/Plugins/Extensions/DVDBurn/Process.py b/lib/python/Plugins/Extensions/DVDBurn/Process.py index f54fcb00..b8a3788e 100644 --- a/lib/python/Plugins/Extensions/DVDBurn/Process.py +++ b/lib/python/Plugins/Extensions/DVDBurn/Process.py @@ -210,9 +210,8 @@ class RemoveESFiles(Task): self.args += [self.demux_task.cutfile] class DVDAuthorTask(Task): - def __init__(self, job, diskspaceNeeded): + def __init__(self, job): Task.__init__(self, job, "Authoring DVD") - self.global_preconditions.append(DiskspacePrecondition(diskspaceNeeded)) self.weighting = 300 self.setTool("/usr/bin/dvdauthor") self.CWD = self.job.workspace @@ -257,26 +256,14 @@ class BurnTaskPostcondition(Condition): class BurnTask(Task): ERROR_NOTWRITEABLE, ERROR_LOAD, ERROR_SIZE, ERROR_WRITE_FAILED, ERROR_DVDROM, ERROR_ISOFS, ERROR_UNKNOWN = range(7) - def __init__(self, job, extra_args=[]): - Task.__init__(self, job, _("Burn to DVD...")) - + def __init__(self, job, extra_args=[], tool="/bin/growisofs"): + Task.__init__(self, job, job.name) self.weighting = 500 self.end = 120 # 100 for writing, 10 for buffer flush, 10 for closing disc self.postconditions.append(BurnTaskPostcondition()) - self.setTool("/bin/growisofs") - volName = self.getASCIIname(job.project.settings.name.getValue()) - self.args += [ "-dvd-compat", "-Z", "/dev/" + harddiskmanager.getCD(), "-V", volName, "-publisher", "Dreambox", "-use-the-force-luke=dummy" ] + self.setTool(tool) self.args += extra_args - - def getASCIIname(self, name): - ASCIIname = "" - for char in name.decode("utf-8").encode("ascii","replace"): - if ord(char) <= 0x20 or ( ord(char) >= 0x3a and ord(char) <= 0x40 ): - ASCIIname += '_' - else: - ASCIIname += char - return ASCIIname - + def prepare(self): self.error = None @@ -324,11 +311,34 @@ class RemoveDVDFolder(Task): self.args += ["-rf", self.job.workspace] self.weighting = 10 -class PreviewTask(Task): +class CheckDiskspaceTask(Task): def __init__(self, job): + Task.__init__(self, job, "Checking free space") + totalsize = 50*1024*1024 # require an extra safety 50 MB + maxsize = 0 + for title in job.project.titles: + titlesize = title.estimatedDiskspace + if titlesize > maxsize: maxsize = titlesize + totalsize += titlesize + diskSpaceNeeded = totalsize + maxsize + self.global_preconditions.append(DiskspacePrecondition(diskSpaceNeeded)) + self.weighting = 5 + + def run(self, callback, task_progress_changed): + failed_preconditions = self.checkPreconditions(True) + self.checkPreconditions(False) + if len(failed_preconditions): + callback(self, failed_preconditions) + return + self.callback = callback + self.task_progress_changed = task_progress_changed + Task.processFinished(self, 0) + +class PreviewTask(Task): + def __init__(self, job, path): Task.__init__(self, job, "Preview") self.postconditions.append(PreviewTaskPostcondition()) self.job = job + self.path = path self.weighting = 10 def run(self, callback, task_progress_changed): @@ -337,7 +347,8 @@ class PreviewTask(Task): if self.job.menupreview: self.previewProject() else: - self.job.project.session.openWithCallback(self.previewCB, MessageBox, _("Do you want to preview this DVD before burning?"), timeout = 60, default = False) + from Tools import Notifications + Notifications.AddNotificationWithCallback(self.previewCB, MessageBox, _("Do you want to preview this DVD before burning?"), timeout = 60, default = False) def abort(self): self.finish(aborted = True) @@ -352,17 +363,18 @@ class PreviewTask(Task): if self.job.menupreview: self.closedCB(True) else: - self.job.project.session.openWithCallback(self.closedCB, MessageBox, _("Do you want to burn this collection to DVD medium?") ) + from Tools import Notifications + Notifications.AddNotificationWithCallback(self.closedCB, MessageBox, _("Do you want to burn this collection to DVD medium?") ) def closedCB(self, answer): if answer == True: Task.processFinished(self, 0) else: Task.processFinished(self, 1) - + def previewProject(self): from Plugins.Extensions.DVDPlayer.plugin import DVDPlayer - self.job.project.session.openWithCallback(self.playerClosed, DVDPlayer, dvd_filelist= [ self.job.project.workspace + "/dvd/VIDEO_TS/" ]) + self.job.project.session.openWithCallback(self.playerClosed, DVDPlayer, dvd_filelist= [ self.path ]) class PreviewTaskPostcondition(Condition): def check(self, task): @@ -639,9 +651,18 @@ def CreateAuthoringXML(job): f.write(x) f.close() +def getISOfilename(isopath, volName): + from Tools.Directories import fileExists + i = 0 + filename = isopath+'/'+volName+".iso" + while fileExists(filename): + i = i+1 + filename = isopath+'/'+volName + str(i).zfill(3) + ".iso" + return filename + class DVDJob(Job): def __init__(self, project, menupreview=False): - Job.__init__(self, _("Burn DVD")) + Job.__init__(self, "DVDBurn Job") self.project = project from time import strftime from Tools.Directories import SCOPE_HDD, resolveFilename, createDir @@ -653,24 +674,17 @@ class DVDJob(Job): self.conduct() def conduct(self): + CheckDiskspaceTask(self) if self.project.settings.authormode.getValue().startswith("menu") or self.menupreview: Menus(self) CreateAuthoringXML(self) - totalsize = 50*1024*1024 # require an extra safety 50 MB - maxsize = 0 - for title in self.project.titles: - titlesize = title.estimatedDiskspace - if titlesize > maxsize: maxsize = titlesize - totalsize += titlesize - diskSpaceNeeded = totalsize + maxsize - - DVDAuthorTask(self, diskSpaceNeeded) + DVDAuthorTask(self) nr_titles = len(self.project.titles) if self.menupreview: - PreviewTask(self) + PreviewTask(self, self.workspace + "/dvd/VIDEO_TS/") else: for self.i in range(nr_titles): title = self.project.titles[self.i] @@ -681,8 +695,20 @@ class DVDJob(Job): MplexTask(self, outputfile=title_filename, demux_task=demux) RemoveESFiles(self, demux) WaitForResidentTasks(self) - PreviewTask(self) - BurnTask(self, ["-dvd-video", self.workspace + "/dvd"]) + PreviewTask(self, self.workspace + "/dvd/VIDEO_TS/") + output = self.project.settings.output.getValue() + volName = self.project.settings.name.getValue() + if output == "dvd": + self.name = _("Burn DVD") + tool = "/bin/growisofs" + burnargs = [ "-Z", "/dev/" + harddiskmanager.getCD(), "-dvd-compat", "-use-the-force-luke=dummy" ] + elif output == "iso": + self.name = _("Create DVD-ISO") + tool = "/usr/bin/mkisofs" + isopathfile = getISOfilename(self.project.settings.isopath.getValue(), volName) + burnargs = [ "-o", isopathfile ] + burnargs += [ "-dvd-video", "-publisher", "Dreambox", "-V", volName, self.workspace + "/dvd" ] + BurnTask(self, burnargs, tool) RemoveDVDFolder(self) class DVDdataJob(Job): @@ -698,16 +724,49 @@ class DVDdataJob(Job): self.conduct() def conduct(self): - diskSpaceNeeded = 50*1024*1024 # require an extra safety 50 MB - for title in self.project.titles: - diskSpaceNeeded += title.filesize + if self.project.settings.output.getValue() == "iso": + CheckDiskspaceTask(self) nr_titles = len(self.project.titles) - for self.i in range(nr_titles): title = self.project.titles[self.i] filename = title.inputfile.rstrip("/").rsplit("/",1)[1] link_name = self.workspace + filename LinkTS(self, title.inputfile, link_name) CopyMeta(self, title.inputfile) - BurnTask(self, ["-iso-level", "4", "-follow-links", self.workspace]) + + output = self.project.settings.output.getValue() + volName = self.project.settings.name.getValue() + tool = "/bin/growisofs" + if output == "dvd": + self.name = _("Burn DVD") + burnargs = [ "-Z", "/dev/" + harddiskmanager.getCD(), "-dvd-compat", "-use-the-force-luke=dummy" ] + elif output == "iso": + tool = "/usr/bin/mkisofs" + self.name = _("Create DVD-ISO") + isopathfile = getISOfilename(self.project.settings.isopath.getValue(), volName) + burnargs = [ "-o", isopathfile ] + if self.project.settings.dataformat.getValue() == "iso9660_1": + burnargs += ["-iso-level", "1" ] + elif self.project.settings.dataformat.getValue() == "iso9660_4": + burnargs += ["-iso-level", "4", "-allow-limited-size" ] + elif self.project.settings.dataformat.getValue() == "udf": + burnargs += ["-udf", "-allow-limited-size" ] + burnargs += [ "-publisher", "Dreambox", "-V", volName, "-follow-links", self.workspace ] + BurnTask(self, burnargs, tool) RemoveDVDFolder(self) + +class DVDisoJob(Job): + def __init__(self, project, imagepath): + Job.__init__(self, _("Burn DVD")) + self.project = project + self.menupreview = False + if imagepath.endswith(".iso"): + PreviewTask(self, imagepath) + burnargs = [ "-Z", "/dev/" + harddiskmanager.getCD() + '='+imagepath, "-dvd-compat", "-use-the-force-luke=dummy" ] + else: + PreviewTask(self, imagepath + "/VIDEO_TS/") + volName = self.project.settings.name.getValue() + burnargs = [ "-Z", "/dev/" + harddiskmanager.getCD(), "-dvd-compat", "-use-the-force-luke=dummy" ] + burnargs += [ "-dvd-video", "-publisher", "Dreambox", "-V", volName, imagepath ] + tool = "/bin/growisofs" + BurnTask(self, burnargs, tool) diff --git a/lib/python/Plugins/Extensions/DVDBurn/ProjectSettings.py b/lib/python/Plugins/Extensions/DVDBurn/ProjectSettings.py index 311c7785..7491e046 100644 --- a/lib/python/Plugins/Extensions/DVDBurn/ProjectSettings.py +++ b/lib/python/Plugins/Extensions/DVDBurn/ProjectSettings.py @@ -9,7 +9,7 @@ from Components.Sources.StaticText import StaticText from Components.Sources.Progress import Progress from Components.FileList import FileList from enigma import eListboxPythonMultiContent, gFont, RT_HALIGN_LEFT -from Tools.Directories import resolveFilename, SCOPE_PLUGINS, SCOPE_FONTS +from Tools.Directories import fileExists, resolveFilename, SCOPE_PLUGINS, SCOPE_FONTS, SCOPE_HDD from Components.config import config, getConfigListEntry from Components.ConfigList import ConfigListScreen @@ -39,6 +39,11 @@ class FileBrowser(Screen, HelpableScreen): elif self.scope == "font_face": currDir = self.getDir(settings.font_face, resolveFilename(SCOPE_FONTS)) pattern = "(?i)^.*\.(ttf)" + elif self.scope == "isopath": + currDir = settings.isopath.getValue() + elif self.scope == "image": + currDir = resolveFilename(SCOPE_HDD) + pattern = "(?i)^.*\.(iso)" self.filelist = FileList(currDir, matchingPattern=pattern) self["filelist"] = self.filelist @@ -59,11 +64,17 @@ class FileBrowser(Screen, HelpableScreen): def ok(self): if self.filelist.canDescent(): self.filelist.descent() + if self.scope == "image": + path = self["filelist"].getCurrentDirectory() or "" + if fileExists(path+"VIDEO_TS"): + self.close(path,self.scope) else: ret = self["filelist"].getCurrentDirectory() + '/' + self["filelist"].getFilename() self.close(ret,self.scope) def exit(self): + if self.scope == "isopath": + self.close(self["filelist"].getCurrentDirectory(),self.scope) self.close(None,False) class ProjectSettings(Screen,ConfigListScreen): @@ -95,7 +106,7 @@ class ProjectSettings(Screen,ConfigListScreen): self.settings = project.settings ConfigListScreen.__init__(self, []) - self.initConfigList(self.settings.authormode.getValue()) + self.initConfigList() self.keydict = {} for key, val in self.settings.dict().iteritems(): @@ -112,14 +123,19 @@ class ProjectSettings(Screen,ConfigListScreen): }, -2) def changedConfigList(self): - if self.keydict[self["config"].getCurrent()[1]] == "authormode": - self.initConfigList(self.settings.authormode.getValue()) - - def initConfigList(self, authormode=""): - print "initConfigList(%s)" % authormode + key = self.keydict[self["config"].getCurrent()[1]] + if key == "authormode" or key == "output": + self.initConfigList() + + def initConfigList(self): + authormode = self.settings.authormode.getValue() + output = self.settings.output.getValue() self.list = [] self.list.append(getConfigListEntry(_("Collection name"), self.settings.name)) self.list.append(getConfigListEntry(_("Authoring mode"), self.settings.authormode)) + self.list.append(getConfigListEntry(("Output"), self.settings.output)) + if output == "iso": + self.list.append(getConfigListEntry(_("ISO path"), self.settings.isopath)) if authormode.startswith("menu"): self.list.append(getConfigListEntry(_("Menu")+' '+_("background image"), self.settings.menubg)) self.list.append(getConfigListEntry(_("Menu")+' '+_("Title"), self.settings.titleformat)) @@ -134,17 +150,22 @@ class ProjectSettings(Screen,ConfigListScreen): if authormode != "data_ts": self.list.append(getConfigListEntry(_("VMGM (intro trailer)"), self.settings.vmgm)) self.list.append(getConfigListEntry(_("Auto chapter split every ? minutes (0=never)"), self.settings.autochapter)) + else: + self.list.append(getConfigListEntry(("DVD data format"), self.settings.dataformat)) + self["config"].setList(self.list) def keyLeft(self): ConfigListScreen.keyLeft(self) - if self.keydict[self["config"].getCurrent()[1]] == "authormode": - self.initConfigList(self.settings.authormode.getValue()) + key = self.keydict[self["config"].getCurrent()[1]] + if key == "authormode" or key == "output": + self.initConfigList() def keyRight(self): ConfigListScreen.keyRight(self) - if self.keydict[self["config"].getCurrent()[1]] == "authormode": - self.initConfigList(self.settings.authormode.getValue()) + key = self.keydict[self["config"].getCurrent()[1]] + if key == "authormode" or key == "output": + self.initConfigList() def exit(self): self.applySettings() diff --git a/lib/python/Plugins/Extensions/DVDBurn/TitleList.py b/lib/python/Plugins/Extensions/DVDBurn/TitleList.py index 2c2d16c8..600411ce 100644 --- a/lib/python/Plugins/Extensions/DVDBurn/TitleList.py +++ b/lib/python/Plugins/Extensions/DVDBurn/TitleList.py @@ -10,7 +10,6 @@ from Components.ActionMap import HelpableActionMap, ActionMap from Components.Sources.List import List from Components.Sources.StaticText import StaticText from Components.Sources.Progress import Progress -from Components.FileList import FileList from Components.Label import Label from enigma import eListboxPythonMultiContent, gFont, RT_HALIGN_LEFT from Tools.Directories import resolveFilename, SCOPE_PLUGINS @@ -78,13 +77,17 @@ class TitleList(Screen, HelpableScreen): def showMenu(self): menu = [] - menu.append((_("Burn DVD"), "burn")); + if self.project.settings.output.getValue() == "dvd": + menu.append((_("Burn DVD"), "burn")); + elif self.project.settings.output.getValue() == "iso": + menu.append((_("Create DVD-ISO"), "burn")); menu.append((_("Preview menu"), "previewMenu")); menu.append((_("DVD media toolbox"), "toolbox")); menu.append((_("Collection settings"), "settings")); menu.append((_("Add a new title"), "addtitle")); menu.append((_("Remove title"), "removetitle")); menu.append((_("Edit chapters of current title"), "edittitle")); + menu.append((_("Burn existing image to DVD"), "burniso")); menu.append((_("Exit"), "exit")); self.session.openWithCallback(self.menuCallback, ChoiceBox, title="", list=menu) @@ -105,6 +108,8 @@ class TitleList(Screen, HelpableScreen): self.previewMenu() elif choice[1] == "burn": self.burnProject() + elif choice[1] == "burniso": + self.session.openWithCallback(self.burnISO, ProjectSettings.FileBrowser, "image", self.project.settings) elif choice[1] == "exit": self.leave() @@ -201,6 +206,12 @@ class TitleList(Screen, HelpableScreen): job_manager.in_background = False self.session.openWithCallback(self.JobViewCB, JobView, job) + def burnISO(self, path, scope): + job = Process.DVDisoJob(self.project, path) + job_manager.AddJob(job) + job_manager.in_background = False + self.session.openWithCallback(self.JobViewCB, JobView, job) + def JobViewCB(self, in_background): job_manager.in_background = in_background -- 2.30.2