64VERSION=
"0.2.20200122-1"
69debug_keeptempfiles =
False
77debug_secondrunthrough =
False
80defaultEncodingProfile =
"SP"
87addCutlistChapters =
False
101import xml.dom.minidom
103from PIL
import ImageDraw
104from PIL
import ImageFont
105from PIL
import ImageColor
109from fcntl
import ioctl
120 CDS_DRIVE_NOT_READY = 3
124 CDROM_DRIVE_STATUS = 0x5326
125 CDROM_LOCKDOOR = 0x5329
127from shutil
import copy
130from MythTV
import datetime
131from MythTV.altdict
import OrdDict
144dvdPALHalfD1=
"352x576"
145dvdNTSCHalfD1=
"352x480"
146dvdPALD1=
"%sx%s" % (dvdPAL[0],dvdPAL[1])
147dvdNTSCD1=
"%sx%s" % (dvdNTSC[0],dvdNTSC[1])
156aspectRatioThreshold = 1.4
174alwaysRunMythtranscode =
False
175copyremoteFiles =
False
178clearArchiveTable =
True
183mainmenuAspectRatio =
"16:9"
187chaptermenuAspectRatio =
"Video"
190chapterLength = 5 * 60;
197progressfile = open(
"/dev/null",
'w', encoding=
'utf-8')
200dvddrivepath =
"/dev/dvd"
225MVID = MythTV.MythVideo(db=DB)
228configHostname = DB.gethostname()
234 oldlocale = os.environ[
"LC_ALL"]
237os.putenv(
"LC_ALL",
"en_US.UTF-8")
249 sys.stdout.write(
"Using simple_fix_rtl\n")
250 fix_rtl = simple_fix_rtl
252 sys.stdout.write(
"Using pyfribidi.log2vis\n")
253 fix_rtl = pyfribidi.log2vis
259 def __init__(self, name=None, fontFile=None, size=19, color="white", effect="normal", shadowColor="black", shadowSize=1):
270 if self.
font is None:
276 if self.
font is None:
284 image = Image.new(
"RGBA", (textwidth + (self.
shadowSize * 2), textheight), (0,0,0,0))
285 draw = ImageDraw.ImageDraw(image)
287 if self.
effect ==
"shadow":
289 draw.text((0,0), text, font=self.
font, fill=color)
290 elif self.
effect ==
"outline":
297 draw.text((0,0), text, font=self.
font, fill=color)
299 bbox = image.getbbox()
300 image = image.crop(bbox)
307 """Simple place to channel all text output through"""
309 if sys.version_info == 2:
310 sys.stdout.write((text +
"\n").encode(
"utf-8",
"replace"))
312 sys.stdout.write(text +
"\n")
315 if progress ==
True and progresslog !=
"":
316 progressfile.write(time.strftime(
"%Y-%m-%d %H:%M:%S ") + text +
"\n")
323 """Display an error message and exit app"""
325 write(
"ERROR: " + msg)
326 write(
"See mythburn.log for more information.")
330 saveSetting(
"MythArchiveLastRunEnd", time.strftime(
"%Y-%m-%d %H:%M:%S "))
337 """Display a warning message"""
339 write(
"WARNING: " + msg)
347 """Return the input string with single quotes escaped."""
348 return str.replace(
"'",
"'\"'\"'")
354 """This is the folder where all temporary files will be created."""
361 """return the number of CPUs"""
363 cpustat = open(
"/proc/cpuinfo",
'r', encoding=
'utf-8')
364 cpudata = cpustat.readlines()
369 tokens = line.split()
371 if tokens[0] ==
"processor":
377 write(
"Found %d CPUs" % cpucount)
385 """This is the folder where all encoder profile files are located."""
386 return os.path.join(sharepath,
"mytharchive",
"encoder_profiles")
392 """Returns true/false if a given file or path exists."""
393 return os.path.exists( file )
399 arg = arg.replace(
'"',
'\\"')
400 arg = arg.replace(
'`',
'\\`')
407 """Returns the text contents from a given XML element."""
408 if node.childNodes.length>0:
409 return node.childNodes[0].data
417 """Find a theme file - first look in the specified theme directory then look in the
418 shared music and image directories
"""
419 if os.path.exists(os.path.join(sharepath,
"mytharchive",
"themes", theme, file)):
420 return os.path.join(sharepath,
"mytharchive",
"themes", theme, file)
422 if os.path.exists(os.path.join(sharepath,
"mytharchive",
"images", file)):
423 return os.path.join(sharepath,
"mytharchive",
"images", file)
425 if os.path.exists(os.path.join(sharepath,
"mytharchive",
"intro", file)):
426 return os.path.join(sharepath,
"mytharchive",
"intro", file)
428 if os.path.exists(os.path.join(sharepath,
"mytharchive",
"music", file)):
429 return os.path.join(sharepath,
"mytharchive",
"music", file)
431 fatalError(
"Cannot find theme file '%s' in theme '%s'" % (file, theme))
437 return os.path.join(sharepath,
"fonts", fontname)
443 return os.path.join(
getTempPath(),
"%s" % itemnumber)
451 write(
"Looking for: " + file)
458 if videomode==
"ntsc":
459 return videoresolution==(720,480)
or videoresolution==(704,480)
or videoresolution==(352,480)
or videoresolution==(352,240)
461 return videoresolution==(720,576)
or videoresolution==(704,576)
or videoresolution==(352,576)
or videoresolution==(352,288)
467 """Does what it says on the tin!."""
468 for root, dirs, deletefiles
in os.walk(folder, topdown=
False):
469 for name
in deletefiles:
470 os.remove(os.path.join(root, name))
476 for root, dirs, files
in os.walk(folder, topdown=
False):
478 os.remove(os.path.join(root, name))
480 if os.path.islink(os.path.join(root, name)):
481 os.remove(os.path.join(root, name))
483 os.rmdir(os.path.join(root, name))
489 """Checks to see if the user has cancelled this run"""
490 if os.path.exists(os.path.join(logpath,
"mythburncancel.lck")):
491 os.remove(os.path.join(logpath,
"mythburncancel.lck"))
493 write(
"Job has been cancelled at users request")
504 result = os.system(command.encode(
'utf-8'))
506 if os.WIFEXITED(result):
507 result = os.WEXITSTATUS(result)
515 """Convert a time in seconds to a frame position"""
517 framespersecond=frameratePAL
519 framespersecond=framerateNTSC
521 frames=int(seconds * framespersecond)
527def encodeMenu(background, tempvideo, music, musiclength, tempmovie, xmlfile, finaloutput, aspectratio):
529 framespersecond=frameratePAL
531 framespersecond=framerateNTSC
533 totalframes=int(musiclength * framespersecond)
535 command =
quoteCmdArg(path_jpeg2yuv[0]) +
" -n %s -v0 -I p -f %s -j %s | %s -b 5000 -a %s -v 1 -f 8 -o %s" \
539 fatalError(
"Failed while running jpeg2yuv - %s" % command)
544 fatalError(
"Failed while running mplex - %s" % command)
550 fatalError(
"Failed while running spumux - %s" % command)
552 os.rename(tempmovie, finaloutput)
554 if os.path.exists(tempvideo):
556 if os.path.exists(tempmovie):
564 """Returns the XML node for the given encoding profile"""
569 if videomode ==
"ntsc":
570 filename = os.path.expanduser(
"~/.mythtv/MythArchive/ffmpeg_dvd_ntsc.xml")
572 filename = os.path.expanduser(
"~/.mythtv/MythArchive/ffmpeg_dvd_pal.xml")
574 if not os.path.exists(filename):
576 if videomode ==
"ntsc":
581 write(
"Using encoder profiles from %s" % filename)
583 DOM = xml.dom.minidom.parse(filename)
586 if DOM.documentElement.tagName !=
"encoderprofiles":
587 fatalError(
"Profile xml file doesn't look right (%s)" % filename)
589 profiles = DOM.getElementsByTagName(
"profile")
590 for node
in profiles:
591 if getText(node.getElementsByTagName(
"name")[0]) == profile:
592 write(
"Encoding profile (%s) found" % profile)
595 write(
'WARNING: Encoding profile "' + profile +
'" not found.')
596 write(
'Using default profile "' + defaultEncodingProfile +
'".')
597 for node
in profiles:
598 if getText(node.getElementsByTagName(
"name")[0]) == defaultEncodingProfile:
599 write(
"Encoding profile (%s) found" % defaultEncodingProfile)
602 fatalError(
'Neither encoding profile "' + profile +
'" nor default enocding profile "' + defaultEncodingProfile +
'" found. Giving up.')
609 """Loads the XML file from disk for a specific theme"""
612 themeDOM = xml.dom.minidom.parse(
getThemeFile(theme,
"theme.xml") )
614 if themeDOM.documentElement.tagName !=
"mythburntheme":
615 fatalError(
"Theme xml file doesn't look right (%s)" % theme)
622 """Returns the length of a video file (in seconds)"""
625 infoDOM = xml.dom.minidom.parse(os.path.join(
getItemTempPath(index),
'streaminfo.xml'))
628 if infoDOM.documentElement.tagName !=
"file":
630 file = infoDOM.getElementsByTagName(
"file")[0]
631 if file.attributes[
"cutduration"].value !=
'N/A':
632 duration = int(file.attributes[
"cutduration"].value)
643 """Returns the audio bitrate and no of channels for a file from its streaminfo.xml"""
646 infoDOM = xml.dom.minidom.parse(os.path.join(folder,
'streaminfo.xml'))
649 if infoDOM.documentElement.tagName !=
"file":
650 fatalError(
"Stream info file doesn't look right (%s)" % os.path.join(folder,
'streaminfo.xml'))
651 audio = infoDOM.getElementsByTagName(
"file")[0].getElementsByTagName(
"streams")[0].getElementsByTagName(
"audio")[0]
653 samplerate = audio.attributes[
"samplerate"].value
654 channels = audio.attributes[
"channels"].value
656 return (samplerate, channels)
663 """Returns the video resolution, fps and aspect ratio for the video file from the streaminfo.xml file"""
666 infoDOM = xml.dom.minidom.parse(os.path.join(folder,
'streaminfo.xml'))
669 if infoDOM.documentElement.tagName !=
"file":
671 video = infoDOM.getElementsByTagName(
"file")[0].getElementsByTagName(
"streams")[0].getElementsByTagName(
"video")[0]
673 if video.attributes[
"aspectratio"].value !=
'N/A':
674 aspect_ratio = video.attributes[
"aspectratio"].value
676 aspect_ratio =
"1.77778"
678 videores = video.attributes[
"width"].value +
'x' + video.attributes[
"height"].value
679 fps = video.attributes[
"fps"].value
687 if float(fr) != float(fps):
688 write(
"WARNING: frames rates do not match")
689 write(
"The frame rate for %s should be %s but the stream info file "
690 "report a fps of %s" % (videomode, fr, fps))
693 return (videores, fps, aspect_ratio)
699 """Returns the aspect ratio of the video file (1.333, 1.778, etc)"""
702 infoDOM = xml.dom.minidom.parse(os.path.join(
getItemTempPath(index),
'streaminfo.xml'))
705 if infoDOM.documentElement.tagName !=
"file":
707 video = infoDOM.getElementsByTagName(
"file")[0].getElementsByTagName(
"streams")[0].getElementsByTagName(
"video")[0]
708 if video.attributes[
"aspectratio"].value !=
'N/A':
709 aspect_ratio = float(video.attributes[
"aspectratio"].value)
711 aspect_ratio = 1.77778;
712 write(
"aspect ratio is: %s" % aspect_ratio)
719 """Returns the sync offset between the video and first audio stream"""
723 infoDOM = xml.dom.minidom.parse(os.path.join(
getItemTempPath(index),
'streaminfo.xml'))
726 if infoDOM.documentElement.tagName !=
"file":
729 video = infoDOM.getElementsByTagName(
"file")[0].getElementsByTagName(
"streams")[0].getElementsByTagName(
"video")[0]
730 video_start = float(video.attributes[
"start_time"].value)
732 audio = infoDOM.getElementsByTagName(
"file")[0].getElementsByTagName(
"streams")[0].getElementsByTagName(
"audio")[0]
733 audio_start = float(audio.attributes[
"start_time"].value)
738 sync_offset = int((video_start - audio_start) * 1000)
749 minutes = int(duration / 60)
750 seconds = duration % 60
751 hours = int(minutes / 60)
754 return '%02d:%02d:%02d' % (hours, minutes, seconds)
760 sec = int(frame / fps)
761 frame = frame - int(sec * fps)
767 return '%02d:%02d:%02d' % (hour, mins, sec)
773 parts = formatedtime.split(
':')
781 return sec + (mins * 60) + (hour * 60 * 60)
788 """Returns numofchapters chapter marks even spaced through a certain time period"""
791 infoDOM = xml.dom.minidom.parse(os.path.join(
getItemTempPath(itemnum),
"info.xml"))
792 thumblistNode = infoDOM.getElementsByTagName(
"thumblist")
793 if thumblistNode.length > 0:
794 thumblist =
getText(thumblistNode[0])
795 write(
"Using user defined thumb images - %s" % thumblist)
799 segment=int(lengthofvideo / numofchapters)
801 write(
"Video length is %s seconds. Each chapter will be %s seconds" % (lengthofvideo,segment))
808 while count<=numofchapters:
809 chapters.append(time.strftime(
"%H:%M:%S",time.gmtime(starttime)))
812 if thumboffset < segment:
813 thumbList.append(str(thumboffset))
815 thumbList.append(str(starttime))
817 thumbList.append(str(starttime))
822 chapters =
','.join(chapters)
823 thumbList =
','.join(thumbList)
825 if getthumbnails==
True:
835 """Returns chapter marks at cut list ends,
836 or evenly spaced chapters
'segment' seconds through the file
"""
839 if addCutlistChapters ==
True:
843 infoDOM = xml.dom.minidom.parse(os.path.join(
getItemTempPath(itemnum),
"info.xml"))
844 chapterlistNode = infoDOM.getElementsByTagName(
"chapterlist")
845 if chapterlistNode.length > 0:
846 chapterlist =
getText(chapterlistNode[0])
847 write(
"Using commercial end marks - %s" % chapterlist)
850 if lengthofvideo < segment:
853 numofchapters = lengthofvideo // segment + 1;
854 chapters =
"00:00:00"
857 while count <= numofchapters:
859 chapters +=
"," + time.strftime(
"%H:%M:%S", time.gmtime(starttime))
862 write(
"Fixed length chapters: %s" % chapters)
870 """Reads settings from MythTV database"""
872 write(
"Obtaining MythTV settings from MySQL database for hostname " + configHostname)
875 sqlstatement=
"""SELECT value, data FROM settings WHERE value IN(
879 OR (hostname=%s AND value IN(
883 'MythArchiveVideoFormat',
884 'MythArchiveTempDir',
885 'MythArchiveMplexCmd',
886 'MythArchiveDvdauthorCmd',
887 'MythArchiveMkisofsCmd',
888 'MythArchiveM2VRequantiserCmd',
889 'MythArchiveMpg123Cmd',
890 'MythArchiveProjectXCmd',
891 'MythArchiveDVDLocation',
892 'MythArchiveGrowisofsCmd',
893 'MythArchiveJpeg2yuvCmd',
894 'MythArchiveSpumuxCmd',
895 'MythArchiveMpeg2encCmd',
896 'MythArchiveCopyRemoteFiles',
897 'MythArchiveAlwaysUseMythTranscode',
898 'MythArchiveUseProjectX',
899 'MythArchiveAddSubtitles',
900 'MythArchiveUseFIFO',
901 'MythArchiveMainMenuAR',
902 'MythArchiveChapterMenuAR',
903 'MythArchiveDateFormat',
904 'MythArchiveTimeFormat',
905 'MythArchiveClearArchiveTable',
906 'MythArchiveDriveSpeed',
913 cursor.execute(sqlstatement, (configHostname,))
915 result = cursor.fetchall()
918 for i
in range(len(result)):
919 cfg[result[i][0]] = result[i][1]
922 if not "MythArchiveTempDir" in cfg:
923 fatalError(
"Can't find the setting for the temp directory. \nHave you run setup in the frontend?")
930 host = DB.gethostname()
931 DB.settings[host][name] = data
937 ''' Remove all archive items from the archiveitems DB table'''
939 write(
"Removing all archive items from the archiveitems DB table")
941 cursor.execute(
"DELETE FROM archiveitems")
953 if options.length == 0:
954 fatalError(
"Trying to read the options from the job file but none found?")
957 doburn = options.attributes[
"doburn"].value !=
'0'
958 docreateiso = options.attributes[
"createiso"].value !=
'0'
959 erasedvdrw = options.attributes[
"erasedvdrw"].value !=
'0'
960 mediatype = int(options.attributes[
"mediatype"].value)
961 savefilename = options.attributes[
"savefilename"].value
963 write(
"Options - mediatype = %d, doburn = %d, createiso = %d, erasedvdrw = %d" \
964 % (mediatype, doburn, docreateiso, erasedvdrw))
965 write(
" savefilename = '%s'" % savefilename)
970def expandItemText(infoDOM, text, itemnumber, pagenumber, keynumber,chapternumber, chapterlist ):
971 """Replaces keywords in a string with variables from the XML and filesystem"""
972 text=text.replace(
"%page",
"%s" % pagenumber)
975 if getText( infoDOM.getElementsByTagName(
"coverfile")[0]) ==
"":
976 text=text.replace(
"%thumbnail", os.path.join(
getItemTempPath(itemnumber),
"title.jpg"))
978 text=text.replace(
"%thumbnail",
getText( infoDOM.getElementsByTagName(
"coverfile")[0]) )
980 text=text.replace(
"%itemnumber",
"%s" % itemnumber )
981 text=text.replace(
"%keynumber",
"%s" % keynumber )
983 text=text.replace(
"%title",
getText( infoDOM.getElementsByTagName(
"title")[0]) )
984 text=text.replace(
"%subtitle",
getText( infoDOM.getElementsByTagName(
"subtitle")[0]) )
985 text=text.replace(
"%description",
getText( infoDOM.getElementsByTagName(
"description")[0]) )
986 text=text.replace(
"%type",
getText( infoDOM.getElementsByTagName(
"type")[0]) )
988 text=text.replace(
"%recordingdate",
getText( infoDOM.getElementsByTagName(
"recordingdate")[0]) )
989 text=text.replace(
"%recordingtime",
getText( infoDOM.getElementsByTagName(
"recordingtime")[0]) )
993 text=text.replace(
"%myfolder",
getThemeFile(themeName,
""))
996 text=text.replace(
"%chapternumber",
"%s" % chapternumber )
997 text=text.replace(
"%chaptertime",
"%s" % chapterlist[chapternumber - 1] )
998 text=text.replace(
"%chapterthumbnail", os.path.join(
getItemTempPath(itemnumber),
"chapter-%s.jpg" % chapternumber))
1006 """ Returns a value taken from attribute in node scaled for the current video mode"""
1008 if videomode ==
"pal" or attribute ==
"x" or attribute ==
"w":
1009 return int(node.attributes[attribute].value)
1011 return int(float(node.attributes[attribute].value) / 1.2)
1017 """Based on http://mail.python.org/pipermail/image-sig/2004-December/003064.html"""
1025 words = text.split()
1031 thistext = lines[line]
1033 innerFinished =
False
1034 while not innerFinished:
1038 if drawer.textsize(
' '.join(thistext),font.getFont())[0] > containerWidth:
1042 if str(thistext).
find(
' ') != -1:
1043 newline.insert(0,thistext.pop(-1))
1046 innerFinished =
True
1048 innerFinished =
True
1049 if len(newline) > 0:
1050 lines.append(newline)
1056 tmp.append(
fix_rtl(
' '.join(i) ) )
1064 if node.hasAttribute(
"bgcolor"):
1065 bgcolor = node.attributes[
"bgcolor"].value
1070 r,g,b = ImageColor.getrgb(bgcolor)
1072 if node.hasAttribute(
"bgalpha"):
1073 a = int(node.attributes[
"bgalpha"].value)
1077 image.paste((r, g, b, a), (x, y, x + w, y + h))
1083def paintButton(draw, bgimage, bgimagemask, node, infoDOM, itemnum, page,
1084 itemsonthispage, chapternumber, chapterlist):
1086 imagefilename =
getThemeFile(themeName, node.attributes[
"filename"].value)
1088 fatalError(
"Cannot find image for menu button (%s)." % imagefilename)
1089 maskimagefilename =
getThemeFile(themeName, node.attributes[
"mask"].value)
1091 fatalError(
"Cannot find mask image for menu button (%s)." % maskimagefilename)
1093 picture = Image.open(imagefilename,
"r").resize(
1095 picture = picture.convert(
"RGBA")
1101 textnode = node.getElementsByTagName(
"textnormal")
1102 if textnode.length > 0:
1103 textnode = textnode[0]
1104 text =
expandItemText(infoDOM,textnode.attributes[
"value"].value,
1105 itemnum, page, itemsonthispage,
1106 chapternumber,chapterlist)
1109 paintText(draw, bgimage, text, textnode)
1113 write(
"Added button image %s" % imagefilename)
1115 picture = Image.open(maskimagefilename,
"r").resize(
1117 picture = picture.convert(
"RGBA")
1123 textnode = node.getElementsByTagName(
"textselected")
1124 if textnode.length > 0:
1125 textnode = textnode[0]
1126 text =
expandItemText(infoDOM, textnode.attributes[
"value"].value,
1127 itemnum, page, itemsonthispage,
1128 chapternumber, chapterlist)
1129 textImage = Image.new(
"RGBA",picture.size)
1130 textDraw = ImageDraw.Draw(textImage)
1133 paintText(textDraw, textImage, text, textnode,
"white",
1140 (width, height) = textImage.size
1141 for y
in range(height):
1142 for x
in range(width):
1143 if textImage.getpixel((x,y)) < (100, 100, 100, 255):
1144 textImage.putpixel((x,y), (0, 0, 0, 0))
1146 textImage.putpixel((x,y), (255, 255, 255, 255))
1148 if textnode.hasAttribute(
"colour"):
1149 color = textnode.attributes[
"colour"].value
1150 elif textnode.hasAttribute(
"color"):
1151 color = textnode.attributes[
"color"].value
1155 bgimagemask.paste(color,
1160 del text, textImage, textDraw
1167 x = None, y = None, width = None, height = None):
1168 """Takes a piece of text and draws it onto an image inside a bounding box."""
1177 font = themeFonts[node.attributes[
"font"].value]
1180 if node.hasAttribute(
"colour"):
1181 color = node.attributes[
"colour"].value
1182 elif node.hasAttribute(
"color"):
1183 color = node.attributes[
"color"].value
1187 if node.hasAttribute(
"halign"):
1188 halign = node.attributes[
"halign"].value
1189 elif node.hasAttribute(
"align"):
1190 halign = node.attributes[
"align"].value
1194 if node.hasAttribute(
"valign"):
1195 valign = node.attributes[
"valign"].value
1199 if node.hasAttribute(
"vindent"):
1200 vindent = int(node.attributes[
"vindent"].value)
1204 if node.hasAttribute(
"hindent"):
1205 hindent = int(node.attributes[
"hindent"].value)
1209 lines =
intelliDraw(draw, text, font, width - (hindent * 2))
1213 textImage = font.drawText(lines[0])
1214 h = int(textImage.size[1] * 1.1)
1217 if (j * h) < (height - (vindent * 2) - h):
1218 textImage = font.drawText(i, color)
1219 write(
"Wrapped text = " + i )
1221 if halign ==
"left":
1223 elif halign ==
"center" or halign ==
"centre":
1224 xoffset = (width // 2) - (textImage.size[0] // 2)
1225 elif halign ==
"right":
1226 xoffset = width - textImage.size[0] - hindent
1232 elif valign ==
"center" or halign ==
"centre":
1233 yoffset = (height // 2) - (textImage.size[1] // 2)
1234 elif valign ==
"bottom":
1235 yoffset = height - textImage.size[1] - vindent
1239 image.paste(textImage, (x + xoffset,y + yoffset + j * h), textImage)
1241 write(
"Wrapped text = " + i )
1248def paintImage(filename, maskfilename, imageDom, destimage, stretch=True):
1249 """Paste the image specified in the filename into the specified image"""
1252 write(
"Image file (%s) does not exist" % filename)
1255 picture = Image.open(filename,
"r")
1260 (imgw, imgh) = picture.size
1261 write(
"Image (%s, %s) into space of (%s, %s) at (%s, %s)" % (imgw, imgh, w, h, xpos, ypos),
False)
1264 if imageDom.hasAttribute(
"stretch"):
1265 if imageDom.attributes[
"stretch"].value ==
"True":
1274 if float(w)/imgw < float(h)/imgh:
1278 if imageDom.hasAttribute(
"valign"):
1279 valign = imageDom.attributes[
"valign"].value
1283 if valign ==
"bottom":
1285 if valign ==
"center":
1286 ypos += (h - imgh)//2
1291 if imageDom.hasAttribute(
"halign"):
1292 halign = imageDom.attributes[
"halign"].value
1296 if halign ==
"right":
1298 if halign ==
"center":
1299 xpos += (w - imgw)//2
1301 write(
"Image resized to (%s, %s) at (%s, %s)" % (imgw, imgh, xpos, ypos),
False)
1302 picture = picture.resize((imgw, imgh))
1303 picture = picture.convert(
"RGBA")
1306 maskpicture = Image.open(maskfilename,
"r").resize((imgw, imgh))
1307 maskpicture = maskpicture.convert(
"RGBA")
1309 maskpicture = picture
1311 destimage.paste(picture, (xpos, ypos), maskpicture)
1316 write (
"Added image %s" % filename)
1328 if getText(node.attributes[
"static"]) ==
"False":
1330 boundarybox =
getScaledAttribute(node,
"x"), boundarybox[1], boundarybox[2], boundarybox[3]
1333 boundarybox = boundarybox[0],
getScaledAttribute(node,
"y"), boundarybox[2], boundarybox[3]
1340 boundarybox = boundarybox[0], boundarybox[1], boundarybox[2], \
1352 nodelistfonts = themeDOM.getElementsByTagName(
"font")
1355 for node
in nodelistfonts:
1358 if node.hasAttribute(
"name"):
1359 name = node.attributes[
"name"].value
1361 name = str(fontnumber)
1365 if node.hasAttribute(
"color"):
1366 color = node.attributes[
"color"].value
1370 if node.hasAttribute(
"effect"):
1371 effect = node.attributes[
"effect"].value
1375 if node.hasAttribute(
"shadowsize"):
1376 shadowsize = int(node.attributes[
"shadowsize"].value)
1380 if node.hasAttribute(
"shadowcolor"):
1381 shadowcolor = node.attributes[
"shadowcolor"].value
1383 shadowcolor =
"black"
1386 fontsize, color, effect, shadowcolor, shadowsize)
1395 outputfile = os.path.join(folder,
"info.xml")
1396 impl = xml.dom.minidom.getDOMImplementation()
1397 infoDOM = impl.createDocument(
None,
"fileinfo",
None)
1398 top_element = infoDOM.documentElement
1400 data = OrdDict(((
'chanid',
''),
1401 (
'type',
''), (
'filename',
''),
1402 (
'title',
''), (
'recordingdate',
''),
1403 (
'recordingtime',
''), (
'subtitle',
''),
1404 (
'description',
''), (
'rating',
''),
1405 (
'coverfile',
''), (
'cutlist',
'')))
1408 details = file.getElementsByTagName(
"details")
1409 if details.length > 0:
1410 data.type = file.attributes[
"type"].value
1411 data.filename = file.attributes[
"filename"].value
1412 data.title = details[0].attributes[
"title"].value
1413 data.recordingdate = details[0].attributes[
"startdate"].value
1414 data.recordingtime = details[0].attributes[
"starttime"].value
1415 data.subtitle = details[0].attributes[
"subtitle"].value
1416 data.description =
getText(details[0])
1419 if file.attributes[
"type"].value==
"recording":
1420 filename = file.attributes[
"filename"].value
1422 rec = next(DB.searchRecorded(basename=os.path.basename(filename)))
1423 except StopIteration:
1424 fatalError(
"Failed to get recording details from the DB for %s" % filename)
1426 data.chanid = rec.chanid
1427 data.recordingtime = rec.starttime.isoformat()
1428 data.recordingdate = rec.starttime.isoformat()
1430 cutlist = rec.markup.getcutlist()
1432 data.hascutlist =
'yes'
1433 if file.attributes[
"usecutlist"].value ==
"0" and addCutlistChapters ==
True:
1434 chapterlist = [
'00:00:00']
1438 data.chapterlist =
','.join(chapterlist)
1440 data.hascutlist =
'no'
1442 elif file.attributes[
"type"].value==
"recording":
1443 filename = file.attributes[
"filename"].value
1445 rec = next(DB.searchRecorded(basename=os.path.basename(filename)))
1446 except StopIteration:
1447 fatalError(
"Failed to get recording details from the DB for %s" % filename)
1449 write(
" " + rec.title)
1450 data.type = file.attributes[
"type"].value
1451 data.filename = filename
1452 data.title = rec.title
1453 data.recordingdate = rec.progstart.strftime(dateformat)
1454 data.recordingtime = rec.progstart.strftime(timeformat)
1455 data.subtitle = rec.subtitle
1456 data.description = rec.description
1457 data.rating = str(rec.stars)
1458 data.chanid = rec.chanid
1459 data.starttime = rec.starttime.utcisoformat()
1461 cutlist = rec.markup.getcutlist()
1463 data.hascutlist =
'yes'
1464 if file.attributes[
"usecutlist"].value ==
"0" and addCutlistChapters ==
True:
1465 chapterlist = [
'00:00:00']
1469 data.chapterlist =
','.join(chapterlist)
1471 data.hascutlist =
'no'
1473 elif file.attributes[
"type"].value==
"video":
1474 filename = file.attributes[
"filename"].value
1476 vid = next(MVID.searchVideos(file=filename))
1477 except StopIteration:
1478 vid = Video.fromFilename(filename)
1480 data.type = file.attributes[
"type"].value
1481 data.filename = filename
1482 data.title = vid.title
1484 if vid.year != 1895:
1485 data.recordingdate =
unicode(vid.year)
1487 data.subtitle = vid.subtitle
1489 if (vid.plot
is not None)
and (vid.plot !=
'None'):
1490 data.description = vid.plot
1492 data.rating = str(vid.userrating)
1495 data.coverfile = vid.coverfile
1497 elif file.attributes[
"type"].value==
"file":
1498 data.type = file.attributes[
"type"].value
1499 data.filename = file.attributes[
"filename"].value
1500 data.title = file.attributes[
"filename"].value
1503 thumbs = file.getElementsByTagName(
"thumbimages")
1504 if thumbs.length > 0:
1506 thumbs = file.getElementsByTagName(
"thumb")
1510 for thumb
in thumbs:
1511 caption = thumb.attributes[
"caption"].value
1512 frame = thumb.attributes[
"frame"].value
1513 filename = thumb.attributes[
"filename"].value
1514 if caption !=
"Title":
1515 thumblist.append(
frameToTime(int(frame), float(fps)))
1518 copy(filename, folder)
1520 data.thumblist =
','.join(thumblist)
1522 for k,v
in list(data.items()):
1523 write(
"Node = %s, Data = %s" % (k, v))
1524 node = infoDOM.createElement(k)
1531 node.appendChild(infoDOM.createTextNode(
unicode(v)))
1532 top_element.appendChild(node)
1534 WriteXMLToFile (infoDOM, outputfile)
1542 f=open(filename,
'w')
1544 if sys.hexversion >= 0x03000000:
1545 f.write(myDOM.toprettyxml(indent=
" ", encoding=
"UTF-8").decode())
1546 elif sys.hexversion >= 0x020703F0:
1547 f.write(myDOM.toprettyxml(indent=
" ", encoding=
"UTF-8"))
1549 f.write(myDOM.toxml(encoding=
"UTF-8"))
1558 """Pre-process a single video/recording file."""
1560 write(
"Pre-processing %s %d: '%s'" % (file.attributes[
"type"].value, count, file.attributes[
"filename"].value))
1568 if file.attributes[
"type"].value ==
"recording":
1569 mediafile = file.attributes[
"filename"].value
1570 elif file.attributes[
"type"].value ==
"video":
1571 mediafile = os.path.join(videopath, file.attributes[
"filename"].value)
1572 elif file.attributes[
"type"].value ==
"file":
1573 mediafile = file.attributes[
"filename"].value
1575 fatalError(
"Unknown type of video file it must be 'recording', 'video' or 'file'.")
1577 if file.hasAttribute(
"localfilename"):
1578 mediafile = file.attributes[
"localfilename"].value
1581 fatalError(
"Source file does not exist: " + mediafile)
1584 copy(os.path.join(folder,
"streaminfo.xml"), os.path.join(folder,
"streaminfo_orig.xml"))
1588 videosize =
getVideoSize(os.path.join(folder,
"streaminfo.xml"))
1590 write(
"Video resolution is %s by %s" % (videosize[0], videosize[1]))
1595def encodeAudio(format, sourcefile, destinationfile, deletesourceafterencode):
1596 write(
"Encoding audio to "+format)
1598 cmd =
"mythffmpeg -v 0 -y "
1601 cmd +=
"-threads %d " % cpuCount
1607 fatalError(
"Failed while running mythffmpeg to re-encode the audio to ac3\n"
1608 "Command was %s" % cmd)
1610 fatalError(
"Unknown encodeAudio format " + format)
1612 if deletesourceafterencode==
True:
1613 os.remove(sourcefile)
1620 """multiplex one video and one or two audio streams together"""
1622 write("Multiplexing MPEG stream to %s" % destination)
1628 if useSyncOffset ==
True:
1629 write(
"Adding sync offset of %dms" % syncOffset)
1631 write(
"Using sync offset is disabled - it would be %dms" % syncOffset)
1635 os.remove(destination)
1639 audio1 = audio1 +
".ac3"
1641 audio1 = audio1 +
".mp2"
1646 audio2 = audio2 +
".ac3"
1648 audio2 = audio2 +
".mp2"
1652 if os.path.exists(os.path.dirname(destination) +
"/stream.d/spumux.xml"):
1655 localUseFIFO=useFIFO
1657 if localUseFIFO==
True:
1658 os.mkfifo(destination)
1666 write(
"Available streams - video and one audio stream")
1667 write(
"running %s -M -f 8 -v 0 --sync-offset %sms -o %s %s %s" %(path_mplex[0], syncOffset, destination, video, audio1))
1668 result=os.spawnlp(mode, path_mplex[0], path_mplex[1],
1672 '--sync-offset',
'%sms' % syncOffset,
1677 write(
"Available streams - video and two audio streams")
1678 result=os.spawnlp(mode, path_mplex[0], path_mplex[1],
1682 '--sync-offset',
'%sms' % syncOffset,
1688 if localUseFIFO ==
True:
1689 write(
"Multiplex started PID=%s" % result)
1693 fatalError(
"mplex failed with result %d" % result)
1696 if os.path.exists(os.path.dirname(destination) +
"/stream.d/spumux.xml"):
1697 write(
"Checking integrity of subtitle pngs")
1698 command =
quoteCmdArg(os.path.join(scriptpath,
"testsubtitlepngs.sh")) +
" " +
quoteCmdArg(os.path.dirname(destination) +
"/stream.d/spumux.xml")
1701 fatalError(
"Failed while running testsubtitlepngs.sh - %s" % command)
1703 write(
"Running spumux to add subtitles")
1708 "Command was - %s.\n"
1709 "Look in the full log to see why it failed" % command)
1710 os.remove(os.path.splitext(destination)[0] +
"-sub.mpg")
1712 os.rename(os.path.splitext(destination)[0] +
"-sub.mpg", destination)
1721 """create a stream.xml file for filename"""
1723 command =
"mytharchivehelper -q -q --getfileinfo --infile %s --outfile %s --method %d" % (
quoteCmdArg(filename),
quoteCmdArg(xmlFilename), lenMethod)
1729 fatalError(
"Failed while running mytharchivehelper to get stream information.\n"
1730 "Result: %d, Command was %s" % (result, command))
1733 infoDOM = xml.dom.minidom.parse(xmlFilename)
1734 write(xmlFilename +
":-\n" + infoDOM.toprettyxml(
" ",
""),
False)
1740 """Get video width and height from stream.xml file"""
1743 infoDOM = xml.dom.minidom.parse(xmlFilename)
1746 if infoDOM.documentElement.tagName !=
"file":
1747 fatalError(
"This info file doesn't look right (%s)." % xmlFilename)
1748 nodes = infoDOM.getElementsByTagName(
"video")
1749 if nodes.length == 0:
1750 fatalError(
"Didn't find any video elements in stream info file. (%s)" % xmlFilename)
1752 if nodes.length > 1:
1753 write(
"Found more than one video element in stream info file.!!!")
1755 width = int(node.attributes[
"width"].value)
1756 height = int(node.attributes[
"height"].value)
1758 return (width, height)
1764 """Use mythtranscode to cut commercials and/or clean up an mpeg2 file"""
1767 rec = next(DB.searchRecorded(chanid=chanid, starttime=starttime))
1768 cutlist = rec.markup.getcutlist()
1769 except StopIteration:
1773 if usecutlist
and len(cutlist):
1776 cutlist_s +=
' %d-%d ' % cut
1778 write(
"Using cutlist: %s" % cutlist_s)
1780 if (localfile !=
""):
1781 if usecutlist ==
True:
1782 command =
"mythtranscode --mpeg2 --honorcutlist %s --infile %s --outfile %s" % (cutlist_s,
quoteCmdArg(localfile),
quoteCmdArg(destination))
1784 command =
"mythtranscode --mpeg2 --infile %s --outfile %s" % (
quoteCmdArg(localfile),
quoteCmdArg(destination))
1786 if usecutlist ==
True:
1787 command =
"mythtranscode --mpeg2 --honorcutlist --chanid %s --starttime %s --outfile %s" % (chanid, starttime,
quoteCmdArg(destination))
1789 command =
"mythtranscode --mpeg2 --chanid %s --starttime %s --outfile %s" % (chanid, starttime,
quoteCmdArg(destination))
1794 write(
"Failed while running mythtranscode to cut commercials and/or clean up an mpeg2 file.\n"
1795 "Result: %d, Command was %s" % (result, command))
1805 """generate cutlist_x.txt for ProjectX"""
1807 rec = next(DB.searchRecorded(chanid=chanid, starttime=starttime))
1808 starttime = rec.starttime.utcisoformat()
1809 cutlist = rec.markup.getcutlist()
1812 with open(os.path.join(folder,
"cutlist_x.txt"),
'w', encoding=
'utf-8')
as cutlist_f:
1813 cutlist_f.write(
"CollectionPanel.CutMode=2\n")
1821 cutlist_f.write(
'0\n%d\n' % cut[0])
1822 cutlist_f.write(
'%d\n' % cut[1])
1823 elif i == len(cutlist) - 1:
1824 cutlist_f.write(
'%d\n' % cut[0])
1825 if cut[1] != 9999999:
1826 cutlist_f.write(
'%d\n9999999\n' % cut[1])
1828 cutlist_f.write(
'%d\n%d\n' % cut)
1833 write(
"No cutlist in the DB for chanid %s, starttime %s" % chanid, starttime)
1840 """Use Project-X to cut commercials and demux an mpeg2 file"""
1844 write(
"Failed to generate Project-X cutlist.")
1847 if os.path.exists(file) !=
True:
1848 write(
"Error: input file doesn't exist on local filesystem")
1852 if usecutlist ==
True:
1853 command +=
" -cut %s" %
quoteCmdArg(os.path.join(folder,
"cutlist_x.txt"))
1858 write(
"Failed while running Project-X to cut commercials and/or demux an mpeg2 file.\n"
1859 "Result: %d, Command was %s" % (result, command))
1868 videoID_hex =
"0x%x" % video[VIDEO_ID]
1869 if audio1[AUDIO_ID] != -1:
1870 audio1ID_hex =
"0x%x" % audio1[AUDIO_ID]
1873 if audio2[AUDIO_ID] != -1:
1874 audio2ID_hex =
"0x%x" % audio2[AUDIO_ID]
1877 if addSubtitles
and subtitles[SUBTITLE_ID] != -1:
1878 subtitlesID_hex =
"0x%x" % subtitles[SUBTITLE_ID]
1880 subtitlesID_hex =
""
1883 files = os.listdir(folder)
1885 if file[0:9] ==
"stream{0x":
1888 if PID == videoID_hex
or SubID == videoID_hex:
1889 os.rename(os.path.join(folder, file), os.path.join(folder,
"stream.mv2"))
1890 elif PID == audio1ID_hex
or SubID == audio1ID_hex:
1891 os.rename(os.path.join(folder, file), os.path.join(folder,
"stream0." + file[-3:]))
1892 elif PID == audio2ID_hex
or SubID == audio2ID_hex:
1893 os.rename(os.path.join(folder, file), os.path.join(folder,
"stream1." + file[-3:]))
1894 elif PID == subtitlesID_hex
or SubID == subtitlesID_hex:
1895 if file[-3:] ==
"sup":
1896 os.rename(os.path.join(folder, file), os.path.join(folder,
"stream.sup"))
1898 os.rename(os.path.join(folder, file), os.path.join(folder,
"stream.sup.IFO"))
1903 files = os.listdir(folder)
1905 if file[0:9] ==
"stream{0x":
1906 if not os.path.exists(os.path.join(folder,
"stream.mv2"))
and file[-3:] ==
"m2v":
1907 os.rename(os.path.join(folder, file), os.path.join(folder,
"stream.mv2"))
1908 elif not (os.path.exists(os.path.join(folder,
"stream0.ac3"))
or os.path.exists(os.path.join(folder,
"stream0.mp2")))
and file[-3:] ==
"ac3":
1909 os.rename(os.path.join(folder, file), os.path.join(folder,
"stream0.ac3"))
1910 elif not (os.path.exists(os.path.join(folder,
"stream0.ac3"))
or os.path.exists(os.path.join(folder,
"stream0.mp2")))
and file[-3:] ==
"mp2":
1911 os.rename(os.path.join(folder, file), os.path.join(folder,
"stream0.mp2"))
1912 elif not (os.path.exists(os.path.join(folder,
"stream1.ac3"))
or os.path.exists(os.path.join(folder,
"stream1.mp2")))
and file[-3:] ==
"ac3":
1913 os.rename(os.path.join(folder, file), os.path.join(folder,
"stream1.ac3"))
1914 elif not (os.path.exists(os.path.join(folder,
"stream1.ac3"))
or os.path.exists(os.path.join(folder,
"stream1.mp2")))
and file[-3:] ==
"mp2":
1915 os.rename(os.path.join(folder, file), os.path.join(folder,
"stream1.mp2"))
1916 elif not os.path.exists(os.path.join(folder,
"stream.sup"))
and file[-3:] ==
"sup":
1917 os.rename(os.path.join(folder, file), os.path.join(folder,
"stream.sup"))
1918 elif not os.path.exists(os.path.join(folder,
"stream.sup.IFO"))
and file[-3:] ==
"IFO":
1919 os.rename(os.path.join(folder, file), os.path.join(folder,
"stream.sup.IFO"))
1925 if (os.path.exists(os.path.join(folder,
"stream.sup"))
and
1926 os.path.exists(os.path.join(folder,
"stream.sup.IFO"))):
1927 write(
"Found DVB subtitles converting to DVD subtitles")
1928 command =
"mytharchivehelper -q -q --sup2dast "
1929 command +=
" --infile %s --ifofile %s --delay 0" % (
quoteCmdArg(os.path.join(folder,
"stream.sup")),
quoteCmdArg(os.path.join(folder,
"stream.sup.IFO")))
1934 write(
"Failed while running mytharchivehelper to convert DVB subtitles to DVD subtitles.\n"
1935 "Result: %d, Command was %s" % (result, command))
1947 h = int(time[0:2]) * 3600 * 90000
1948 m = int(time[3:5]) * 60 * 90000
1949 s = int(time[6:8]) * 90000
1950 ms = int(time[9:11]) * 90
1952 return h + m + s + ms
1960 subDOM = xml.dom.minidom.parse(spumuxFile)
1963 if subDOM.documentElement.tagName !=
"subpictures":
1964 fatalError(
"This does not look like a spumux.xml file (%s)" % spumuxFile)
1966 streamNodes = subDOM.getElementsByTagName(
"stream")
1967 if streamNodes.length == 0:
1968 write(
"Didn't find any stream elements in file.!!!")
1970 streamNode = streamNodes[0]
1972 nodes = subDOM.getElementsByTagName(
"spu")
1973 if nodes.length == 0:
1974 write(
"Didn't find any spu elements in file.!!!")
1981 start =
ts2pts(node.attributes[
"start"].value)
1982 end =
ts2pts(node.attributes[
"end"].value)
1983 image = node.attributes[
"image"].value
1987 if start <= lastEnd:
1991 write(
"removing subtitle: %s to %s - (%d - %d (%d))" % (node.attributes[
"start"].value, node.attributes[
"end"].value, start, end, lastEnd),
False)
1992 streamNode.removeChild(node)
2004 write(
"Extracting thumbnail image from %s at position %s" % (source, seconds))
2005 write(
"Destination file %s" % destination)
2009 if videomode==
"pal":
2014 command =
"mytharchivehelper -q -q --createthumbnail --infile %s --thumblist '%s' --outfile %s" % (
quoteCmdArg(source), seconds,
quoteCmdArg(destination))
2017 fatalError(
"Failed while running mytharchivehelper to get thumbnails.\n"
2018 "Result: %d, Command was %s" % (result, command))
2020 myimage=Image.open(destination,
"r")
2022 if myimage.format !=
"JPEG":
2023 write(
"Something went wrong with thumbnail capture - " + myimage.format)
2034 write(
"Extracting thumbnail images from: %s - at %s" % (source, thumbList))
2035 write(
"Destination file %s" % destination)
2037 command =
"mytharchivehelper -q -q --createthumbnail --infile %s --thumblist '%s' --outfile %s" % (
quoteCmdArg(source), thumbList,
quoteCmdArg(destination))
2041 fatalError(
"Failed while running mytharchivehelper to get thumbnails.\n"
2042 "Result: %d, Command was %s" % (result, command))
2048 """Encodes an unknown video source file eg. AVI to MPEG2 video and AC3 audio, use mythffmpeg"""
2052 passes = int(
getText(profileNode.getElementsByTagName(
"passes")[0]))
2054 command =
"mythffmpeg"
2057 command +=
" -threads %d" % cpuCount
2059 parameters = profileNode.getElementsByTagName(
"parameter")
2061 for param
in parameters:
2062 name = param.attributes[
"name"].value
2063 value = param.attributes[
"value"].value
2066 if value ==
"%inputfile":
2068 if value ==
"%outputfile":
2070 if value ==
"%aspect":
2074 if audio1[AUDIO_CODEC] ==
"AC3":
2075 if name ==
"-acodec":
2077 if name ==
"-ar" or name ==
"-b:a" or name ==
"-ac":
2082 command +=
" " + name
2085 command +=
" " + value
2089 if audio2[AUDIO_ID] != -1:
2090 for param
in parameters:
2091 name = param.attributes[
"name"].value
2092 value = param.attributes[
"value"].value
2095 if audio1[AUDIO_CODEC] ==
"AC3":
2096 if name ==
"-acodec":
2098 if name ==
"-ar" or name ==
"-b:a" or name ==
"-ac":
2102 if name ==
"-acodec" or name ==
"-ar" or name ==
"-b:a" or name ==
"-ac":
2103 command +=
" " + name +
" " + value
2106 command +=
" -map 0:%d -map 0:%d " % (video[VIDEO_INDEX], audio1[AUDIO_INDEX])
2107 if audio2[AUDIO_ID] != -1:
2108 command +=
"-map 0:%d" % (audio2[AUDIO_INDEX])
2114 fatalError(
"Failed while running mythffmpeg to re-encode video.\n"
2115 "Command was %s" % command)
2120 pass1 = command.replace(
"%passno",
"1")
2121 pass1 = pass1.replace(
"%passlogfile",
quoteCmdArg(passLog))
2122 write(
"Pass 1 - " + pass1)
2126 fatalError(
"Failed while running mythffmpeg (Pass 1) to re-encode video.\n"
2127 "Command was %s" % command)
2129 if os.path.exists(destvideofile):
2130 os.remove(destvideofile)
2132 pass2 = command.replace(
"%passno",
"2")
2133 pass2 = pass2.replace(
"%passlogfile", passLog)
2134 write(
"Pass 2 - " + pass2)
2138 fatalError(
"Failed while running mythffmpeg (Pass 2) to re-encode video.\n"
2139 "Command was %s" % command)
2144 """Encodes a nuv video source file to MPEG2 video and AC3 audio, using mythtranscode & mythffmpeg"""
2148 fatalError(
"Something is wrong! Found one or more stale fifo's from mythtranscode\n"
2149 "Delete the fifos in '%s' and start again" % folder)
2152 parameters = profileNode.getElementsByTagName(
"parameter")
2155 outvideobitrate =
"5000k"
2156 if videomode ==
"ntsc":
2157 outvideores =
"720x480"
2159 outvideores =
"720x576"
2161 outaudiochannels = 2
2162 outaudiobitrate = 384
2163 outaudiosamplerate = 48000
2164 outaudiocodec =
"ac3"
2171 for param
in parameters:
2172 name = param.attributes[
"name"].value
2173 value = param.attributes[
"value"].value
2176 if name ==
"-acodec":
2177 outaudiocodec = value
2179 outaudiochannels = value
2181 outaudiobitrate = value
2183 outaudiosamplerate = value
2185 outvideobitrate = value
2188 if name ==
"-deinterlace":
2190 if name ==
"-filter:v":
2196 if name ==
"-qdiff":
2200 utcstarttime = datetime.duck(starttime).utcisoformat()
2201 if (usecutlist ==
True):
2202 PID=os.spawnlp(os.P_NOWAIT,
"mythtranscode",
"mythtranscode",
2205 '--starttime', utcstarttime,
2207 '--fifodir', folder)
2208 write(
"mythtranscode started (using cut list) PID = %s" % PID)
2210 PID=os.spawnlp(os.P_NOWAIT,
"mythtranscode",
"mythtranscode",
2213 '--starttime', utcstarttime,
2214 '--fifodir', folder)
2216 write(
"mythtranscode started PID = %s" % PID)
2217 elif mediafile != -1:
2218 PID=os.spawnlp(os.P_NOWAIT,
"mythtranscode",
"mythtranscode",
2220 '--infile', mediafile,
2221 '--fifodir', folder)
2222 write(
"mythtranscode started (using file) PID = %s" % PID)
2224 fatalError(
"no video source passed to encodeNuvToMPEG2.\n")
2230 command =
"mythffmpeg -y "
2233 command +=
"-threads %d " % cpuCount
2235 command +=
"-f s16le -ar %s -ac %s -i %s " % (samplerate, channels,
quoteCmdArg(os.path.join(folder,
"audout")))
2236 command +=
"-f rawvideo -pix_fmt yuv420p -s %s -aspect %s -r %s " % (videores, aspectratio, fps)
2237 command +=
"-i %s " %
quoteCmdArg(os.path.join(folder,
"vidout"))
2238 command +=
"-aspect %s -r %s " % (aspectratio, fps)
2239 if (deinterlace == 1):
2240 command +=
"-deinterlace "
2241 command +=
"%s" % filter
2242 command +=
"-s %s -b %s -vcodec mpeg2video " % (outvideores, outvideobitrate)
2243 command +=
"-qmin %s -qmax %s -qdiff %s " % (qmin, qmax, qdiff)
2244 command +=
"-ab %s -ar %s -acodec %s " % (outaudiobitrate, outaudiosamplerate, outaudiocodec)
2245 command +=
"-f dvd %s" %
quoteCmdArg(destvideofile)
2249 while (tries
and not(
doesFileExist(os.path.join(folder,
"audout"))
and
2252 write(
"Waiting for mythtranscode to create the fifos")
2256 fatalError(
"Waited too long for mythtranscode to create the fifos - giving up!!")
2258 write(
"Running mythffmpeg")
2261 os.kill(PID, signal.SIGKILL)
2262 fatalError(
"Failed while running mythffmpeg to re-encode video.\n"
2263 "Command was %s" % command)
2269 write(
"Starting dvdauthor")
2271 result=os.spawnlp(os.P_WAIT, path_dvdauthor[0],path_dvdauthor[1],
'-x',os.path.join(
getTempPath(),
'dvdauthor.xml'))
2273 fatalError(
"Failed while running dvdauthor. Result: %d" % result)
2274 write(
"Finished dvdauthor")
2280 write(
"Creating ISO image")
2282 command =
quoteCmdArg(path_mkisofs[0]) +
' -dvd-video '
2291 "Command was %s" % command)
2293 write(
"Finished creating ISO image")
2300 write(
"Burning ISO image to %s" % dvddrivepath)
2305 f = os.open(dvddrivepath, os.O_RDONLY | os.O_NONBLOCK)
2306 status = ioctl(f,CDROM.CDROM_DRIVE_STATUS, 0)
2309 def displayneededdisktype():
2310 if mediatype == DVD_SL:
2311 write(
"Please insert an empty single-layer disc (DVD+R or DVD-R).")
2312 if mediatype == DVD_DL:
2313 write(
"Please insert an empty double-layer disc (DVD+R DL or DVD-R DL).")
2314 if mediatype == DVD_RW:
2315 write(
"Please insert a rewritable disc (DVD+RW or DVD-RW).")
2316 def drive(action, value=0):
2320 if action == CDROM.CDROMEJECT:
2322 while drivestatus() != CDROM.CDS_TRAY_OPEN
and counter < 15:
2323 counter = counter + 1
2324 drive(CDROM.CDROM_LOCKDOOR, 0)
2326 f = os.open(dvddrivepath, os.O_RDONLY | os.O_NONBLOCK)
2328 ioctl(f,action, value)
2330 write(
"Sending command '0x%x' to drive failed" %action,
False)
2334 write(
'"eject" is probably not installed.',
False)
2337 if drivestatus() == CDROM.CDS_TRAY_OPEN:
2341 write(
"Failed to eject the disc! Probably drive is blocked by another program.")
2344 f = os.open(dvddrivepath, os.O_RDONLY | os.O_NONBLOCK)
2346 ioctl(f,action, value)
2349 write(
"Sending command '0x%x' to drive failed" %action,
False)
2355 while drivestatus() == CDROM.CDS_DRIVE_NOT_READY:
2357 write(
"Waiting for drive")
2363 write(
"Try a hard-reset of the device")
2364 drive(CDROM.CDROMRESET)
2381 if drivestatus() == CDROM.CDS_DISC_OK
or drivestatus() == CDROM.CDS_NO_INFO:
2388 command =
quoteCmdArg(path_growisofs[0]) +
" -input-charset=UTF-8 -dvd-compat"
2390 command +=
" -speed=%d" % drivespeed
2391 if mediatype == DVD_RW
and erasedvdrw ==
True:
2392 command +=
" -use-the-force-luke"
2395 write(
"Running growisofs to burn DVD")
2402 f = os.open(dvddrivepath, os.O_RDONLY | os.O_NONBLOCK)
2405 while busy
and tries < 10:
2408 ioctl(f, CDROM.CDROM_LOCKDOOR, 0)
2411 write(
"Drive is still busy")
2418 write(
"You probably inserted a medium of wrong type.")
2419 elif (result == 156):
2421 write(
"You probably inserted a non-empty, corrupt or too small medium.")
2422 elif (result == 144):
2424 write(
"You inserted a non-empty medium.")
2427 write(
"ERROR: Failed while running growisofs.")
2428 write(
"Result %d, Command was: %s" % (result, command))
2429 write(
"Please check mythburn.log for further information")
2432 write(
"Going to try it again until canceled by user:")
2435 displayneededdisktype()
2438 drive(CDROM.CDROMEJECT)
2441 elif drivestatus() == CDROM.CDS_TRAY_OPEN:
2442 displayneededdisktype()
2443 write(
"Waiting for tray to close.")
2445 while drivestatus() == CDROM.CDS_TRAY_OPEN:
2448 elif drivestatus() == CDROM.CDS_NO_DISC:
2449 drive(CDROM.CDROMEJECT)
2450 displayneededdisktype()
2452 write(
"Finished burning ISO image")
2461 command =
"mythreplex --demux --fix_sync -t TS -o %s " %
quoteCmdArg(folder +
"/stream")
2462 command +=
"-v %d " % (video[VIDEO_ID])
2464 if audio1[AUDIO_ID] != -1:
2465 if audio1[AUDIO_CODEC] ==
'MP2':
2466 command +=
"-a %d " % (audio1[AUDIO_ID])
2467 elif audio1[AUDIO_CODEC] ==
'AC3':
2468 command +=
"-c %d " % (audio1[AUDIO_ID])
2469 elif audio1[AUDIO_CODEC] ==
'EAC3':
2470 command +=
"-c %d " % (audio1[AUDIO_ID])
2472 if audio2[AUDIO_ID] != -1:
2473 if audio2[AUDIO_CODEC] ==
'MP2':
2474 command +=
"-a %d " % (audio2[AUDIO_ID])
2475 elif audio2[AUDIO_CODEC] ==
'AC3':
2476 command +=
"-c %d " % (audio2[AUDIO_ID])
2477 elif audio2[AUDIO_CODEC] ==
'EAC3':
2478 command +=
"-c %d " % (audio2[AUDIO_ID])
2481 command =
"mythreplex --demux --fix_sync -o %s " %
quoteCmdArg(folder +
"/stream")
2482 command +=
"-v %d " % (video[VIDEO_ID] & 255)
2484 if audio1[AUDIO_ID] != -1:
2485 if audio1[AUDIO_CODEC] ==
'MP2':
2486 command +=
"-a %d " % (audio1[AUDIO_ID] & 255)
2487 elif audio1[AUDIO_CODEC] ==
'AC3':
2488 command +=
"-c %d " % (audio1[AUDIO_ID] & 255)
2489 elif audio1[AUDIO_CODEC] ==
'EAC3':
2490 command +=
"-c %d " % (audio1[AUDIO_ID] & 255)
2493 if audio2[AUDIO_ID] != -1:
2494 if audio2[AUDIO_CODEC] ==
'MP2':
2495 command +=
"-a %d " % (audio2[AUDIO_ID] & 255)
2496 elif audio2[AUDIO_CODEC] ==
'AC3':
2497 command +=
"-c %d " % (audio2[AUDIO_ID] & 255)
2498 elif audio2[AUDIO_CODEC] ==
'EAC3':
2499 command +=
"-c %d " % (audio2[AUDIO_ID] & 255)
2502 command += mediafile
2503 write(
"Running: " + command)
2508 fatalError(
"Failed while running mythreplex. Command was %s" % command)
2515 M2Vsize0 = os.path.getsize(source)
2516 write(
"Initial M2Vsize is %.2f Mb , target is %.2f Mb" % ( (float(M2Vsize0)/mega), (float(M2Vsize0)/(factor*mega)) ))
2519 command +=
" %.5f " % factor
2520 command +=
" %s " % M2Vsize0
2524 write(
"Running: " + command)
2527 fatalError(
"Failed while running M2VRequantiser. Command was %s" % command)
2529 M2Vsize1 = os.path.getsize(destination)
2531 write(
"M2Vsize after requant is %.2f Mb " % (float(M2Vsize1)/mega))
2532 fac1=float(M2Vsize0) / float(M2Vsize1)
2533 write(
"Factor demanded %.5f, achieved %.5f, ratio %.5f " % ( factor, fac1, fac1/factor))
2539 """ Returns the sizes of all video, audio and menu files"""
2550 file=os.path.join(folder,
"stream.mv2")
2552 totalvideosize+=os.path.getsize(file)
2556 totalaudiosize+=os.path.getsize(os.path.join(folder,
"stream0.ac3"))
2558 totalaudiosize+=os.path.getsize(os.path.join(folder,
"stream0.mp2"))
2562 totalaudiosize+=os.path.getsize(os.path.join(folder,
"stream1.ac3"))
2564 totalaudiosize+=os.path.getsize(os.path.join(folder,
"stream1.mp2"))
2568 totalmenusize+=os.path.getsize(os.path.join(
getTempPath(),
"chaptermenu-%s.mpg" % filecount))
2572 totalmenusize+=os.path.getsize(os.path.join(
getTempPath(),
"details-%s.mpg" % filecount))
2576 totalmenusize+=os.path.getsize(os.path.join(
getTempPath(),
"menu-%s.mpg" % filecount))
2579 return totalvideosize,totalaudiosize,totalmenusize
2591 file=os.path.join(folder,
"stream.mv2")
2592 progvsize=os.path.getsize(file)
2593 progvbitrate=progvsize/progduration
2594 if progvbitrate>rate :
2595 tvsize+=progduration*rate
2612 allfiles=totalvideosize+totalaudiosize+totalmenusize
2615 write(
"Total video %.2f Mb, audio %.2f Mb, menus %.2f Mb." % (totalvideosize/mega,totalaudiosize/mega,totalmenusize/mega))
2618 mv2space=((dvdrsize*mega-totalmenusize)/fudge_pack)-totalaudiosize
2621 fatalError(
"Audio and menu files are too big. No room for video. Giving up!")
2623 if totalvideosize>mv2space:
2624 write(
"Video files are %.1f Mb too big. Need to shrink." % ((totalvideosize - mv2space)/mega) )
2626 if path_M2VRequantiser[0] ==
"":
2627 fatalError(
"M2VRequantiser is not available to resize the files. Giving up!")
2635 file=os.path.join(folder,
"stream.mv2")
2636 vsize+=os.path.getsize(file)
2652 vrHi=3.0*float(vsize)/duration
2663 if (testsize<mv2space):
2666 write(
"vrate %.3f kb/s, testsize %.4f , mv2space %.4f Mb " % ((vrate)/1000.0, (testsize)/mega, (mv2space)/mega) )
2671 file=os.path.join(folder,
"stream.mv2")
2672 progvsize=os.path.getsize(file)
2674 progvbitrate=progvsize/progduration
2675 write(
"File %s, size %.2f Mb, rate %.2f, limit %.2f kb/s " %( filecount, float(progvsize)/mega, progvbitrate/1000.0, vrate/1000.0 ))
2676 if progvbitrate>vrate :
2677 scalefactor=1.0+(fudge_requant*float(progvbitrate-vrate)/float(vrate))
2678 if scalefactor>3.0 :
2679 write(
"Large shrink factor. You may not like the result! ")
2684 write(
"Unpackaged total %.2f Mb. About %.0f Mb will be unused." % ((allfiles/mega),(mv2space-totalvideosize)/mega))
2690 """Creates the xml file for dvdauthor to use the MythBurn menus."""
2693 menunode=themeDOM.getElementsByTagName(
"menu")
2694 if menunode.length!=1:
2695 fatalError(
"Cannot find the menu element in the theme file")
2696 menunode=menunode[0]
2698 menuitems=menunode.getElementsByTagName(
"item")
2700 itemsperpage = menuitems.length
2701 write(
"Menu items per page %s" % itemsperpage)
2702 autoplaymenu = 2 + ((numberofitems + itemsperpage - 1)//itemsperpage)
2706 submenunode=themeDOM.getElementsByTagName(
"submenu")
2707 if submenunode.length!=1:
2708 fatalError(
"Cannot find the submenu element in the theme file")
2710 submenunode=submenunode[0]
2712 chapteritems=submenunode.getElementsByTagName(
"chapter")
2714 chapters = chapteritems.length
2715 write(
"Chapters per recording %s" % chapters)
2726 write(
"Creating DVD XML file for dvd author")
2728 dvddom = xml.dom.minidom.parseString(
2738 dvdauthor_element=dvddom.documentElement
2739 menus_element = dvdauthor_element.childNodes[1].childNodes[1]
2741 dvdauthor_element.insertBefore( dvddom.createComment("""
2745 g2=title number selected on current menu page (see g4)
2746 g3=1
if intro movie has played
2747 g4=last menu page on display
2748 g5=next title to autoplay (0
or >
2749 """), dvdauthor_element.firstChild )
2750 dvdauthor_element.insertBefore(dvddom.createComment("dvdauthor XML file created by MythBurn script"), dvdauthor_element.firstChild )
2752 menus_element.appendChild( dvddom.createComment(
"Title menu used to hold intro movie") )
2754 dvdauthor_element.setAttribute(
"dest",os.path.join(
getTempPath(),
"dvd"))
2756 video = dvddom.createElement(
"video")
2757 video.setAttribute(
"format",videomode)
2760 if mainmenuAspectRatio ==
"4:3":
2761 video.setAttribute(
"aspect",
"4:3")
2763 video.setAttribute(
"aspect",
"16:9")
2764 video.setAttribute(
"widescreen",
"nopanscan")
2766 menus_element.appendChild(video)
2768 pgc=menus_element.childNodes[1]
2772 pre = dvddom.createElement(
"pre")
2773 pgc.appendChild(pre)
2777 node = themeDOM.getElementsByTagName(
"intro")[0]
2778 introFile = node.attributes[
"filename"].value
2781 vob = dvddom.createElement(
"vob")
2782 vob.setAttribute(
"file",os.path.join(
getThemeFile(themeName, videomode +
'_' + introFile)))
2783 pgc.appendChild(vob)
2788 post = dvddom.createElement(
"post")
2789 post .appendChild(dvddom.createTextNode(
"{g3=1;g2=1;jump menu 2;}"))
2790 pgc.appendChild(post)
2794 post = dvddom.createElement(
"post")
2795 post .appendChild(dvddom.createTextNode(
"{g3=1;g2=1;jump menu 2;}"))
2796 pgc.appendChild(post)
2799 while itemnum <= numberofitems:
2800 write(
"Menu page %s" % page)
2803 menupgc = dvddom.createElement(
"pgc")
2804 menus_element.appendChild(menupgc)
2806 menupgc.appendChild( dvddom.createComment(
"Menu Page %s" % page) )
2810 pre = dvddom.createElement(
"pre")
2811 pre.appendChild(dvddom.createTextNode(
"{button=g2*1024;g4=%s;}" % page))
2812 menupgc.appendChild(pre)
2814 vob = dvddom.createElement(
"vob")
2815 vob.setAttribute(
"file",os.path.join(
getTempPath(),
"menu-%s.mpg" % page))
2816 menupgc.appendChild(vob)
2819 post = dvddom.createElement(
"post")
2820 post.appendChild(dvddom.createTextNode(
"jump cell 1;"))
2821 menupgc.appendChild(post)
2830 while itemnum <= numberofitems
and itemsonthispage < itemsperpage:
2831 menuitem=menuitems[ itemsonthispage ]
2836 infoDOM = xml.dom.minidom.parse( os.path.join(
getItemTempPath(itemnum),
"info.xml") )
2838 if infoDOM.documentElement.tagName !=
"fileinfo":
2844 button=dvddom.createElement(
"button")
2845 button.setAttribute(
"name",
"%s" % itemnum)
2846 button.appendChild(dvddom.createTextNode(
"{g2=" +
"%s" % itemsonthispage +
"; g5=0; jump title %s;}" % itemnum))
2847 menupgc.appendChild(button)
2851 titleset = dvddom.createElement(
"titleset")
2852 dvdauthor_element.appendChild(titleset)
2855 comment =
getText(infoDOM.getElementsByTagName(
"title")[0]).replace(
'--',
'-')
2856 titleset.appendChild( dvddom.createComment(comment))
2858 menus= dvddom.createElement(
"menus")
2859 titleset.appendChild(menus)
2861 video = dvddom.createElement(
"video")
2862 video.setAttribute(
"format",videomode)
2865 if chaptermenuAspectRatio ==
"4:3":
2866 video.setAttribute(
"aspect",
"4:3")
2867 elif chaptermenuAspectRatio ==
"16:9":
2868 video.setAttribute(
"aspect",
"16:9")
2869 video.setAttribute(
"widescreen",
"nopanscan")
2873 video.setAttribute(
"aspect",
"16:9")
2874 video.setAttribute(
"widescreen",
"nopanscan")
2876 video.setAttribute(
"aspect",
"4:3")
2878 menus.appendChild(video)
2881 mymenupgc = dvddom.createElement(
"pgc")
2882 menus.appendChild(mymenupgc)
2884 pre = dvddom.createElement(
"pre")
2885 mymenupgc.appendChild(pre)
2887 pre.appendChild(dvddom.createTextNode(
"{button=s7 - 1 * 1024;}"))
2889 pre.appendChild(dvddom.createTextNode(
"{button=s7 * 1024;}"))
2891 vob = dvddom.createElement(
"vob")
2892 vob.setAttribute(
"file",os.path.join(
getTempPath(),
"chaptermenu-%s.mpg" % itemnum))
2893 mymenupgc.appendChild(vob)
2896 post = dvddom.createElement(
"post")
2897 post.appendChild(dvddom.createTextNode(
"jump cell 1;"))
2898 mymenupgc.appendChild(post)
2905 chapterlist = thumblist.split(
",")
2906 if chapterlist[0] !=
'00:00:00':
2909 while x <= chapters:
2911 button = dvddom.createElement(
"button")
2912 button.setAttribute(
"name",
"%s" % x)
2914 button.appendChild(dvddom.createTextNode(
"jump title %s chapter %s;" % (1, firstChapter + x + 1)))
2916 button.appendChild(dvddom.createTextNode(
"jump title %s chapter %s;" % (1, firstChapter + x)))
2918 mymenupgc.appendChild(button)
2923 submenunode = themeDOM.getElementsByTagName(
"submenu")
2924 submenunode = submenunode[0]
2925 titlemenunodes = submenunode.getElementsByTagName(
"titlemenu")
2926 if titlemenunodes.length > 0:
2927 button = dvddom.createElement(
"button")
2928 button.setAttribute(
"name",
"titlemenu")
2929 button.appendChild(dvddom.createTextNode(
"{jump vmgm menu;}"))
2930 mymenupgc.appendChild(button)
2933 titles = dvddom.createElement(
"titles")
2934 titleset.appendChild(titles)
2937 title_video = dvddom.createElement(
"video")
2938 title_video.setAttribute(
"format",videomode)
2941 title_video.setAttribute(
"aspect",
"16:9")
2942 title_video.setAttribute(
"widescreen",
"nopanscan")
2944 title_video.setAttribute(
"aspect",
"4:3")
2946 titles.appendChild(title_video)
2950 title_audio = dvddom.createElement(
"audio")
2951 title_audio.setAttribute(
"format",
"mp2")
2953 title_audio = dvddom.createElement(
"audio")
2954 title_audio.setAttribute(
"format",
"ac3")
2956 titles.appendChild(title_audio)
2958 pgc = dvddom.createElement(
"pgc")
2959 titles.appendChild(pgc)
2963 vob = dvddom.createElement(
"vob")
2964 vob.setAttribute(
"file",os.path.join(
getTempPath(),
"details-%s.mpg" % itemnum))
2965 pgc.appendChild(vob)
2967 vob = dvddom.createElement(
"vob")
2970 chapterlist = thumblist.split(
",")
2971 if chapterlist[0] !=
'00:00:00':
2972 thumblist =
'00:00:00,' + thumblist
2973 vob.setAttribute(
"chapters", thumblist)
2975 vob.setAttribute(
"chapters",
2980 vob.setAttribute(
"file",os.path.join(
getItemTempPath(itemnum),
"final.vob"))
2981 pgc.appendChild(vob)
2983 post = dvddom.createElement(
"post")
2984 post.appendChild(dvddom.createTextNode(
"if (g5 eq %s) call vmgm menu %s; call vmgm menu %s;" % (itemnum + 1, autoplaymenu, page + 1)))
2985 pgc.appendChild(post)
2997 for node
in menuitem.childNodes:
2999 if node.nodeName==
"previous":
3001 button=dvddom.createElement(
"button")
3002 button.setAttribute(
"name",
"previous")
3003 button.appendChild(dvddom.createTextNode(
"{g2=1;jump menu %s;}" % page ))
3004 endbuttons.append(button)
3007 elif node.nodeName==
"next":
3008 if itemnum < numberofitems:
3009 button=dvddom.createElement(
"button")
3010 button.setAttribute(
"name",
"next")
3011 button.appendChild(dvddom.createTextNode(
"{g2=1;jump menu %s;}" % (page + 2)))
3012 endbuttons.append(button)
3014 elif node.nodeName==
"playall":
3015 button=dvddom.createElement(
"button")
3016 button.setAttribute(
"name",
"playall")
3017 button.appendChild(dvddom.createTextNode(
"{g5=1; jump menu %s;}" % autoplaymenu))
3018 endbuttons.append(button)
3026 for button
in endbuttons:
3027 menupgc.appendChild(button)
3030 menupgc = dvddom.createElement(
"pgc")
3031 menus_element.appendChild(menupgc)
3032 menupgc.setAttribute(
"pause",
"inf")
3033 menupgc.appendChild( dvddom.createComment(
"Autoplay hack") )
3036 while (itemnum > 1):
3038 dvdcode +=
"if (g5 eq %s) {g5 = %s; jump title %s;} " % (itemnum, itemnum + 1, itemnum)
3039 dvdcode +=
"g5 = 0; jump menu 1;"
3041 pre = dvddom.createElement(
"pre")
3042 pre.appendChild(dvddom.createTextNode(dvdcode))
3043 menupgc.appendChild(pre)
3048 dvdcode=
"if (g3 eq 1) {"
3051 dvdcode+=
"if (g4 eq %s) " % page
3052 dvdcode+=
"jump menu %s;" % (page + 1)
3056 vmgm_pre_node.appendChild(dvddom.createTextNode(dvdcode))
3060 WriteXMLToFile (dvddom,os.path.join(
getTempPath(),
"dvdauthor.xml"))
3069 """Creates the xml file for dvdauthor to use the MythBurn menus."""
3075 write(
"Creating DVD XML file for dvd author (No Main Menu)")
3083 """Creates the xml file for dvdauthor containing no menus."""
3089 write(
"Creating DVD XML file for dvd author (No Menus)")
3091 dvddom = xml.dom.minidom.parseString(
3096 <pgc entry=
"title" pause=
"0">
3102 dvdauthor_element = dvddom.documentElement
3103 menus = dvdauthor_element.childNodes[1].childNodes[1]
3104 menu_pgc = menus.childNodes[1]
3106 dvdauthor_element.insertBefore(dvddom.createComment("dvdauthor XML file created by MythBurn script"), dvdauthor_element.firstChild )
3107 dvdauthor_element.setAttribute(
"dest",os.path.join(
getTempPath(),
"dvd"))
3111 video = dvddom.createElement(
"video")
3112 video.setAttribute(
"format", videomode)
3115 if mainmenuAspectRatio ==
"4:3":
3116 video.setAttribute(
"aspect",
"4:3")
3118 video.setAttribute(
"aspect",
"16:9")
3119 video.setAttribute(
"widescreen",
"nopanscan")
3120 menus.appendChild(video)
3122 pre = dvddom.createElement(
"pre")
3123 pre.appendChild(dvddom.createTextNode(
"if (g2==1) jump menu 2;"))
3124 menu_pgc.appendChild(pre)
3126 node = themeDOM.getElementsByTagName(
"intro")[0]
3127 introFile = node.attributes[
"filename"].value
3129 vob = dvddom.createElement(
"vob")
3130 vob.setAttribute(
"file",
getThemeFile(themeName, videomode +
'_' + introFile))
3131 menu_pgc.appendChild(vob)
3133 post = dvddom.createElement(
"post")
3134 post.appendChild(dvddom.createTextNode(
"g2=1; jump menu 2;"))
3135 menu_pgc.appendChild(post)
3141 pre = dvddom.createElement(
"pre")
3142 pre.appendChild(dvddom.createTextNode(
"g2=1;jump menu 2;"))
3143 menu_pgc.appendChild(pre)
3145 vob = dvddom.createElement(
"vob")
3146 vob.setAttribute(
"file",
getThemeFile(themeName, videomode +
'_' +
"blank.mpg"))
3147 menu_pgc.appendChild(vob)
3154 menu_pgc = dvddom.createElement(
"pgc")
3156 preText =
"if (g1==0) g1=1;"
3157 for i
in range(numberofitems):
3158 preText +=
"if (g1==%d) jump titleset %d menu;" % (i + 1, i + 1)
3160 pre = dvddom.createElement(
"pre")
3161 pre.appendChild(dvddom.createTextNode(preText))
3162 menu_pgc.appendChild(pre)
3164 vob = dvddom.createElement(
"vob")
3165 vob.setAttribute(
"file",
getThemeFile(themeName, videomode +
'_' +
"blank.mpg"))
3166 menu_pgc.appendChild(vob)
3167 menus.appendChild(menu_pgc)
3171 while itemNum <= numberofitems:
3172 write(
"Adding item %s" % itemNum)
3174 titleset = dvddom.createElement(
"titleset")
3175 dvdauthor_element.appendChild(titleset)
3178 menu = dvddom.createElement(
"menus")
3179 menupgc = dvddom.createElement(
"pgc")
3180 menu.appendChild(menupgc)
3181 titleset.appendChild(menu)
3185 vob = dvddom.createElement(
"vob")
3186 vob.setAttribute(
"file", os.path.join(
getTempPath(),
"details-%s.mpg" % itemNum))
3187 menupgc.appendChild(vob)
3189 post = dvddom.createElement(
"post")
3190 post.appendChild(dvddom.createTextNode(
"jump title 1;"))
3191 menupgc.appendChild(post)
3195 pre = dvddom.createElement(
"pre")
3196 pre.appendChild(dvddom.createTextNode(
"jump title 1;"))
3197 menupgc.appendChild(pre)
3200 vob = dvddom.createElement(
"vob")
3201 vob.setAttribute(
"file",
getThemeFile(themeName, videomode +
'_' +
"blank.mpg"))
3202 menupgc.appendChild(vob)
3204 titles = dvddom.createElement(
"titles")
3207 title_video = dvddom.createElement(
"video")
3208 title_video.setAttribute(
"format", videomode)
3212 title_video.setAttribute(
"aspect",
"16:9")
3213 title_video.setAttribute(
"widescreen",
"nopanscan")
3215 title_video.setAttribute(
"aspect",
"4:3")
3217 titles.appendChild(title_video)
3219 pgc = dvddom.createElement(
"pgc")
3221 vob = dvddom.createElement(
"vob")
3222 vob.setAttribute(
"file", os.path.join(
getItemTempPath(itemNum),
"final.vob"))
3226 pgc.appendChild(vob)
3231 post = dvddom.createElement(
"post")
3232 if itemNum == numberofitems:
3233 post.appendChild(dvddom.createTextNode(
"exit;"))
3235 post.appendChild(dvddom.createTextNode(
"g1=%d;call vmgm menu 2;" % (itemNum + 1)))
3237 pgc.appendChild(post)
3239 titles.appendChild(pgc)
3240 titleset.appendChild(titles)
3251 WriteXMLToFile (dvddom,os.path.join(
getTempPath(),
"dvdauthor.xml"))
3261 if os.path.exists(previewfolder):
3263 os.rmdir (previewfolder)
3264 os.makedirs(previewfolder)
3265 return previewfolder
3271 """generate thumbnails for a preview in a menu"""
3280 for node
in menuitem.childNodes:
3281 if node.nodeName==
"graphic":
3282 if node.attributes[
"filename"].value ==
"%movie":
3285 outputfile = os.path.join(previewfolder,
"preview-i%d-t%%1-f%%2.jpg" % itemonthispage)
3290 command =
"mytharchivehelper -q -q --createthumbnail --infile %s --thumblist '%s' --outfile %s --framecount %d" % (
quoteCmdArg(inputfile), starttime,
quoteCmdArg(outputfile), frames)
3293 write(
"mytharchivehelper failed with code %d. Command = %s" % (result, command) )
3299 if node.hasAttribute(
"mask"):
3300 imagemaskfilename =
getThemeFile(themeName, node.attributes[
"mask"].value)
3301 if node.attributes[
"mask"].value !=
"" and doesFileExist(imagemaskfilename):
3302 maskpicture = Image.open(imagemaskfilename,
"r").resize((width, height))
3303 maskpicture = maskpicture.convert(
"RGBA")
3305 return (positionx, positiony, width, height, maskpicture)
3311 bgimagemask, drawmask, highlightcolor, spumuxdom, spunode,
3312 numberofitems, chapternumber, chapterlist):
3313 """Draws text and graphics onto a dvd menu, called by
3314 createMenu and createChapterMenu
"""
3317 infoDOM = xml.dom.minidom.parse(os.path.join(
getItemTempPath(itemnum),
"info.xml"))
3320 if infoDOM.documentElement.tagName !=
"fileinfo":
3321 fatalError(
"The info.xml file (%s) doesn't look right" %
3326 boundarybox = 9999,9999,0,0
3327 wantHighlightBox =
True
3330 for node
in menuitem.childNodes:
3333 if node.nodeName==
"graphic":
3340 if node.attributes[
"filename"].value ==
"%movie":
3345 node.attributes[
"filename"].value,
3346 itemnum, page, itemsonthispage,
3347 chapternumber, chapterlist)
3350 if imagefilename == node.attributes[
"filename"].value:
3352 node.attributes[
"filename"].value)
3356 if node.hasAttribute(
"mask")
and node.attributes[
"mask"].value !=
"":
3357 maskfilename =
getThemeFile(themeName, node.attributes[
"mask"].value)
3361 if (node.attributes[
"filename"].value ==
"%thumbnail"
3362 and getText(infoDOM.getElementsByTagName(
"coverfile")[0]) !=
""):
3367 if paintImage(imagefilename, maskfilename, node, bgimage, stretch):
3370 write(
"Image file does not exist '%s'" % imagefilename)
3372 elif node.nodeName ==
"text":
3379 itemnum, page, itemsonthispage,
3380 chapternumber, chapterlist)
3388 elif node.nodeName==
"previous":
3395 paintButton(draw, bgimage, bgimagemask, node, infoDOM,
3396 itemnum, page, itemsonthispage, chapternumber,
3399 button = spumuxdom.createElement(
"button")
3400 button.setAttribute(
"name",
"previous")
3407 spunode.appendChild(button)
3409 write(
"Added previous page button")
3412 elif node.nodeName ==
"next":
3413 if itemnum < numberofitems:
3419 paintButton(draw, bgimage, bgimagemask, node, infoDOM,
3420 itemnum, page, itemsonthispage, chapternumber,
3423 button = spumuxdom.createElement(
"button")
3424 button.setAttribute(
"name",
"next")
3431 spunode.appendChild(button)
3433 write(
"Added next page button")
3435 elif node.nodeName==
"playall":
3441 paintButton(draw, bgimage, bgimagemask, node, infoDOM, itemnum, page,
3442 itemsonthispage, chapternumber, chapterlist)
3444 button = spumuxdom.createElement(
"button")
3445 button.setAttribute(
"name",
"playall")
3452 spunode.appendChild(button)
3454 write(
"Added playall button")
3456 elif node.nodeName ==
"titlemenu":
3457 if itemnum < numberofitems:
3463 paintButton(draw, bgimage, bgimagemask, node, infoDOM,
3464 itemnum, page, itemsonthispage, chapternumber,
3467 button = spumuxdom.createElement(
"button")
3468 button.setAttribute(
"name",
"titlemenu")
3475 spunode.appendChild(button)
3477 write(
"Added titlemenu button")
3479 elif node.nodeName==
"button":
3485 wantHighlightBox =
False
3487 paintButton(draw, bgimage, bgimagemask, node, infoDOM, itemnum, page,
3488 itemsonthispage, chapternumber, chapterlist)
3493 elif node.nodeName==
"#text" or node.nodeName==
"#comment":
3497 write(
"Dont know how to process %s" % node.nodeName)
3499 if drawmask
is None:
3503 if wantHighlightBox ==
True:
3505 boundarybox=boundarybox[0]-1,boundarybox[1]-1,boundarybox[2]+1,boundarybox[3]+1
3506 drawmask.rectangle(boundarybox,outline=highlightcolor)
3509 boundarybox=boundarybox[0]-1,boundarybox[1]-1,boundarybox[2]+1,boundarybox[3]+1
3510 drawmask.rectangle(boundarybox,outline=highlightcolor)
3512 node = spumuxdom.createElement(
"button")
3515 node.setAttribute(
"name",
"%s" % chapternumber)
3517 node.setAttribute(
"name",
"%s" % itemnum)
3518 node.setAttribute(
"x0",
"%d" % int(boundarybox[0]))
3519 node.setAttribute(
"y0",
"%d" % int(boundarybox[1]))
3520 node.setAttribute(
"x1",
"%d" % int(boundarybox[2] + 1))
3521 node.setAttribute(
"y1",
"%d" % int(boundarybox[3] + 1))
3522 spunode.appendChild(node)
3528 """Creates all the necessary menu images and files for the MythBurn menus."""
3531 menunode=themeDOM.getElementsByTagName(
"menu")
3532 if menunode.length!=1:
3533 fatalError(
"Cannot find menu element in theme file")
3534 menunode=menunode[0]
3536 menuitems=menunode.getElementsByTagName(
"item")
3538 itemsperpage = menuitems.length
3539 write(
"Menu items per page %s" % itemsperpage)
3542 backgroundfilename = menunode.attributes[
"background"].value
3543 if backgroundfilename==
"":
3544 fatalError(
"Background image is not set in theme file")
3546 backgroundfilename =
getThemeFile(themeName,backgroundfilename)
3547 write(
"Background image file is %s" % backgroundfilename)
3549 fatalError(
"Background image not found (%s)" % backgroundfilename)
3552 highlightcolor =
"red"
3553 if menunode.hasAttribute(
"highlightcolor"):
3554 highlightcolor = menunode.attributes[
"highlightcolor"].value
3557 menumusic =
"menumusic.ac3"
3558 if menunode.hasAttribute(
"music"):
3559 menumusic = menunode.attributes[
"music"].value
3563 if menunode.hasAttribute(
"length"):
3564 menulength = int(menunode.attributes[
"length"].value)
3566 write(
"Music is %s, length is %s seconds" % (menumusic, menulength))
3574 write(
"Creating DVD menus")
3576 while itemnum <= numberofitems:
3577 write(
"Menu page %s" % page)
3582 write(
"Creating Preview Video")
3583 previewitem = itemnum
3593 while previewitem <= numberofitems
and itemsonthispage < itemsperpage:
3594 menuitem=menuitems[ itemsonthispage ]
3601 px, py, pw, ph, maskimage =
generateVideoPreview(previewitem, itemsonthispage, menuitem, 0, menulength, previewfolder)
3606 previewmask.append(maskimage)
3613 savedpreviewitem = itemnum
3624 overlayimage=Image.new(
"RGBA",screensize)
3625 draw=ImageDraw.Draw(overlayimage)
3628 bgimagemask=Image.new(
"RGBA",overlayimage.size)
3629 drawmask=ImageDraw.Draw(bgimagemask)
3631 spumuxdom = xml.dom.minidom.parseString(
'<subpictures><stream><spu force="yes" start="00:00:00.0" highlight="" select="" ></spu></stream></subpictures>')
3632 spunode = spumuxdom.documentElement.firstChild.firstChild
3635 while itemnum <= numberofitems
and itemsonthispage < itemsperpage:
3636 menuitem=menuitems[ itemsonthispage ]
3641 itemnum, menuitem, overlayimage,
3642 draw, bgimagemask, drawmask, highlightcolor,
3643 spumuxdom, spunode, numberofitems, 0,
"")
3649 bgimage=Image.open(backgroundfilename,
"r").resize(screensize)
3650 bgimage.paste(overlayimage, (0,0), overlayimage)
3653 rgb_bgimage=bgimage.convert(
'RGB')
3654 rgb_bgimage.save(os.path.join(
getTempPath(),
"background-%s.jpg" % page),
"JPEG", quality=99)
3656 bgimagemask.save(os.path.join(
getTempPath(),
"backgroundmask-%s.png" % page),
"PNG",quality=99,optimize=0,dpi=screendpi)
3669 if haspreview ==
True:
3670 write(
"Generating the preview images" )
3672 while framenum < numframes:
3673 previewitem = savedpreviewitem
3675 while previewitem <= numberofitems
and itemsonthispage < itemsperpage:
3677 if previewx[itemsonthispage-1] != 9999:
3679 previewfile =
"preview-i%d-t1-f%d.jpg" % (itemsonthispage, framenum)
3680 imagefile = os.path.join(previewpath, previewfile)
3683 picture = Image.open(imagefile,
"r").resize((previeww[itemsonthispage-1], previewh[itemsonthispage-1]))
3684 picture = picture.convert(
"RGBA")
3685 imagemaskfile = os.path.join(previewpath,
"mask-i%d.png" % itemsonthispage)
3686 if previewmask[itemsonthispage-1]
is not None:
3687 bgimage.paste(picture, (previewx[itemsonthispage-1], previewy[itemsonthispage-1]), previewmask[itemsonthispage-1])
3689 bgimage.paste(picture, (previewx[itemsonthispage-1], previewy[itemsonthispage-1]))
3693 rgb_bgimage=bgimage.convert(
'RGB')
3694 rgb_bgimage.save(os.path.join(
getTempPath(),
"background-%s-f%06d.jpg" % (page, framenum)),
"JPEG",quality=99)
3698 spumuxdom.documentElement.firstChild.firstChild.setAttribute(
"select",os.path.join(
getTempPath(),
"backgroundmask-%s.png" % page))
3699 spumuxdom.documentElement.firstChild.firstChild.setAttribute(
"highlight",os.path.join(
getTempPath(),
"backgroundmask-%s.png" % page))
3711 WriteXMLToFile (spumuxdom,os.path.join(
getTempPath(),
"spumux-%s.xml" % page))
3713 if mainmenuAspectRatio ==
"4:3":
3718 write(
"Encoding Menu Page %s using aspect ratio '%s'" % (page, mainmenuAspectRatio))
3719 if haspreview ==
True:
3725 os.path.join(
getTempPath(),
"spumux-%s.xml" % page),
3734 os.path.join(
getTempPath(),
"spumux-%s.xml" % page),
3745 """Creates all the necessary menu images and files for the MythBurn menus."""
3748 menunode=themeDOM.getElementsByTagName(
"submenu")
3749 if menunode.length!=1:
3750 fatalError(
"Cannot find submenu element in theme file")
3751 menunode=menunode[0]
3753 menuitems=menunode.getElementsByTagName(
"chapter")
3755 itemsperpage = menuitems.length
3756 write(
"Chapter items per page %s " % itemsperpage)
3759 backgroundfilename = menunode.attributes[
"background"].value
3760 if backgroundfilename==
"":
3761 fatalError(
"Background image is not set in theme file")
3762 backgroundfilename =
getThemeFile(themeName,backgroundfilename)
3763 write(
"Background image file is %s" % backgroundfilename)
3765 fatalError(
"Background image not found (%s)" % backgroundfilename)
3768 highlightcolor =
"red"
3769 if menunode.hasAttribute(
"highlightcolor"):
3770 highlightcolor = menunode.attributes[
"highlightcolor"].value
3773 menumusic =
"menumusic.ac3"
3774 if menunode.hasAttribute(
"music"):
3775 menumusic = menunode.attributes[
"music"].value
3779 if menunode.hasAttribute(
"length"):
3780 menulength = int(menunode.attributes[
"length"].value)
3782 write(
"Music is %s, length is %s seconds" % (menumusic, menulength))
3787 write(
"Creating DVD sub-menus")
3789 while page <= numberofitems:
3790 write(
"Sub-menu %s " % page)
3798 overlayimage=Image.new(
"RGBA",screensize, (0,0,0,0))
3799 draw=ImageDraw.Draw(overlayimage)
3802 bgimagemask=Image.new(
"RGBA",overlayimage.size, (0,0,0,0))
3803 drawmask=ImageDraw.Draw(bgimagemask)
3805 spumuxdom = xml.dom.minidom.parseString(
'<subpictures><stream><spu force="yes" start="00:00:00.0" highlight="" select="" ></spu></stream></subpictures>')
3806 spunode = spumuxdom.documentElement.firstChild.firstChild
3810 chapterlist=chapterlist.split(
",")
3829 while previewchapter < itemsperpage:
3830 menuitem=menuitems[ previewchapter ]
3835 px, py, pw, ph, maskimage =
generateVideoPreview(page, previewchapter, menuitem, previewtime, menulength, previewfolder)
3840 previewmask.append(maskimage)
3849 while chapter < itemsperpage:
3850 menuitem=menuitems[ chapter ]
3855 bgimagemask, drawmask, highlightcolor,
3857 999, chapter, chapterlist)
3860 bgimage=Image.open(backgroundfilename,
"r").resize(screensize)
3861 bgimage.paste(overlayimage, (0,0), overlayimage)
3862 rgb_bgimage=bgimage.convert(
'RGB')
3863 rgb_bgimage.save(os.path.join(
getTempPath(),
"chaptermenu-%s.jpg" % page),
"JPEG", quality=99)
3866 bgimagemask.save(os.path.join(
getTempPath(),
"chaptermenumask-%s.png" % page),
"PNG",quality=90,optimize=0)
3868 if haspreview ==
True:
3873 write(
"Generating the preview images" )
3875 while framenum < numframes:
3877 while previewchapter < itemsperpage:
3878 if previewx[previewchapter] != 9999:
3880 previewfile =
"preview-i%d-t1-f%d.jpg" % (previewchapter, framenum)
3881 imagefile = os.path.join(previewpath, previewfile)
3884 picture = Image.open(imagefile,
"r").resize((previeww[previewchapter], previewh[previewchapter]))
3885 picture = picture.convert(
"RGBA")
3886 imagemaskfile = os.path.join(previewpath,
"mask-i%d.png" % previewchapter)
3887 if previewmask[previewchapter]
is not None:
3888 bgimage.paste(picture, (previewx[previewchapter], previewy[previewchapter]), previewmask[previewchapter])
3890 bgimage.paste(picture, (previewx[previewchapter], previewy[previewchapter]))
3893 rgb_bgimage=bgimage.convert(
'RGB')
3894 rgb_bgimage.save(os.path.join(
getTempPath(),
"chaptermenu-%s-f%06d.jpg" % (page, framenum)),
"JPEG",quality=99)
3898 spumuxdom.documentElement.firstChild.firstChild.setAttribute(
"select",os.path.join(
getTempPath(),
"chaptermenumask-%s.png" % page))
3899 spumuxdom.documentElement.firstChild.firstChild.setAttribute(
"highlight",os.path.join(
getTempPath(),
"chaptermenumask-%s.png" % page))
3912 WriteXMLToFile (spumuxdom,os.path.join(
getTempPath(),
"chapterspumux-%s.xml" % page))
3914 if chaptermenuAspectRatio ==
"4:3":
3916 elif chaptermenuAspectRatio ==
"16:9":
3924 write(
"Encoding Chapter Menu Page %s using aspect ratio '%s'" % (page, chaptermenuAspectRatio))
3926 if haspreview ==
True:
3932 os.path.join(
getTempPath(),
"chapterspumux-%s.xml" % page),
3933 os.path.join(
getTempPath(),
"chaptermenu-%s.mpg" % page),
3941 os.path.join(
getTempPath(),
"chapterspumux-%s.xml" % page),
3942 os.path.join(
getTempPath(),
"chaptermenu-%s.mpg" % page),
3952 """Creates all the necessary images and files for the details page."""
3954 write(
"Creating details pages")
3957 detailnode=themeDOM.getElementsByTagName(
"detailspage")
3958 if detailnode.length!=1:
3959 fatalError(
"Cannot find detailspage element in theme file")
3960 detailnode=detailnode[0]
3963 backgroundfilename = detailnode.attributes[
"background"].value
3964 if backgroundfilename==
"":
3965 fatalError(
"Background image is not set in theme file")
3966 backgroundfilename =
getThemeFile(themeName,backgroundfilename)
3967 write(
"Background image file is %s" % backgroundfilename)
3969 fatalError(
"Background image not found (%s)" % backgroundfilename)
3972 menumusic =
"menumusic.ac3"
3973 if detailnode.hasAttribute(
"music"):
3974 menumusic = detailnode.attributes[
"music"].value
3978 if detailnode.hasAttribute(
"length"):
3979 menulength = int(detailnode.attributes[
"length"].value)
3981 write(
"Music is %s, length is %s seconds" % (menumusic, menulength))
3986 while itemnum <= numberofitems:
3987 write(
"Creating details page for %s" % itemnum)
3994 previewx, previewy, previeww, previewh, previewmask =
generateVideoPreview(itemnum, 1, detailnode, 0, menulength, previewfolder)
3995 if previewx != 9999:
4004 overlayimage=Image.new(
"RGBA",screensize, (0,0,0,0))
4005 draw=ImageDraw.Draw(overlayimage)
4007 spumuxdom = xml.dom.minidom.parseString(
'<subpictures><stream><spu force="yes" start="00:00:00.0" highlight="" select="" ></spu></stream></subpictures>')
4008 spunode = spumuxdom.documentElement.firstChild.firstChild
4010 drawThemeItem(0, 0, itemnum, detailnode, overlayimage, draw,
None,
None,
4011 "", spumuxdom, spunode, numberofitems, 0,
"")
4014 bgimage=Image.open(backgroundfilename,
"r").resize(screensize)
4015 bgimage.paste(overlayimage, (0,0), overlayimage)
4016 rgb_bgimage=bgimage.convert(
'RGB')
4017 rgb_bgimage.save(os.path.join(
getTempPath(),
"details-%s.jpg" % itemnum),
"JPEG", quality=99)
4020 if haspreview ==
True:
4024 write(
"Generating the detail preview images" )
4026 while framenum < numframes:
4027 if previewx != 9999:
4029 previewfile =
"preview-i%d-t1-f%d.jpg" % (1, framenum)
4030 imagefile = os.path.join(previewpath, previewfile)
4033 picture = Image.open(imagefile,
"r").resize((previeww, previewh))
4034 picture = picture.convert(
"RGBA")
4035 imagemaskfile = os.path.join(previewpath,
"mask-i%d.png" % 1)
4036 if previewmask
is not None:
4037 bgimage.paste(picture, (previewx, previewy), previewmask)
4039 bgimage.paste(picture, (previewx, previewy))
4041 rgb_bgimage=bgimage.convert(
'RGB')
4042 rgb_bgimage.save(os.path.join(
getTempPath(),
"details-%s-f%06d.jpg" % (itemnum, framenum)),
"JPEG",quality=99)
4057 WriteXMLToFile (spumuxdom,os.path.join(
getTempPath(),
"detailsspumux-%s.xml" % itemnum))
4059 write(
"Encoding Details Page %s" % itemnum)
4060 if haspreview ==
True:
4067 os.path.join(
getTempPath(),
"details-%s.mpg" % itemnum),
4076 os.path.join(
getTempPath(),
"details-%s.mpg" % itemnum),
4086 fh = open(file,
'rb')
4089 return Magic==
"RIFF"
4095 """encode audio to ac3 for better compression and compatability with NTSC players"""
4098 if not encodetoac3
and doesFileExist(os.path.join(folder,
'stream0.mp2')):
4100 write(
"Audio track 1 is in mp2 format - NOT re-encoding to ac3")
4101 elif doesFileExist(os.path.join(folder,
'stream0.mp2'))==
True:
4102 write(
"Audio track 1 is in mp2 format - re-encoding to ac3")
4103 encodeAudio(
"ac3",os.path.join(folder,
'stream0.mp2'), os.path.join(folder,
'stream0.ac3'),
True)
4104 elif doesFileExist(os.path.join(folder,
'stream0.mpa'))==
True:
4105 write(
"Audio track 1 is in mpa format - re-encoding to ac3")
4106 encodeAudio(
"ac3",os.path.join(folder,
'stream0.mpa'), os.path.join(folder,
'stream0.ac3'),
True)
4107 elif doesFileExist(os.path.join(folder,
'stream0.ac3'))==
True:
4108 write(
"Audio is already in ac3 format")
4110 fatalError(
"Track 1 - Unknown audio format or de-multiplex failed!")
4113 if not encodetoac3
and doesFileExist(os.path.join(folder,
'stream1.mp2')):
4115 write(
"Audio track 2 is in mp2 format - NOT re-encoding to ac3")
4116 elif doesFileExist(os.path.join(folder,
'stream1.mp2'))==
True:
4117 write(
"Audio track 2 is in mp2 format - re-encoding to ac3")
4118 encodeAudio(
"ac3",os.path.join(folder,
'stream1.mp2'), os.path.join(folder,
'stream1.ac3'),
True)
4119 elif doesFileExist(os.path.join(folder,
'stream1.mpa'))==
True:
4120 write(
"Audio track 2 is in mpa format - re-encoding to ac3")
4121 encodeAudio(
"ac3",os.path.join(folder,
'stream1.mpa'), os.path.join(folder,
'stream1.ac3'),
True)
4122 elif doesFileExist(os.path.join(folder,
'stream1.ac3'))==
True:
4123 write(
"Audio is already in ac3 format")
4139 """Choose the streams we want from the source file"""
4141 video = (-1,
'N/A', -1)
4142 audio1 = (-1,
'N/A', -1,
'N/A')
4143 audio2 = (-1,
'N/A', -1,
'N/A')
4146 infoDOM = xml.dom.minidom.parse(os.path.join(folder,
'streaminfo.xml'))
4148 if infoDOM.documentElement.tagName !=
"file":
4149 fatalError(
"This does not look like a stream info file (%s)" % os.path.join(folder,
'streaminfo.xml'))
4153 nodes = infoDOM.getElementsByTagName(
"video")
4154 if nodes.length == 0:
4155 write(
"Didn't find any video elements in stream info file.!!!")
4158 if nodes.length > 1:
4159 write(
"Found more than one video element in stream info file.!!!")
4161 video = (int(node.attributes[
"ffmpegindex"].value), node.attributes[
"codec"].value, int(node.attributes[
"id"].value))
4170 write(
"Preferred audio languages %s and %s" % (preferredlang1, preferredlang2))
4172 nodes = infoDOM.getElementsByTagName(
"audio")
4174 if nodes.length == 0:
4175 write(
"Didn't find any audio elements in stream info file.!!!")
4182 index = int(node.attributes[
"ffmpegindex"].value)
4183 lang = node.attributes[
"language"].value
4184 format = node.attributes[
"codec"].value.upper()
4185 pid = int(node.attributes[
"id"].value)
4186 if lang == preferredlang1
and format ==
"AC3":
4188 if pid < audio1[AUDIO_ID]:
4189 audio1 = (index, format, pid, lang)
4191 audio1 = (index, format, pid, lang)
4197 index = int(node.attributes[
"ffmpegindex"].value)
4198 lang = node.attributes[
"language"].value
4199 format = node.attributes[
"codec"].value.upper()
4200 pid = int(node.attributes[
"id"].value)
4201 if lang == preferredlang1
and format ==
"MP2":
4203 if pid < audio1[AUDIO_ID]:
4204 audio1 = (index, format, pid, lang)
4206 audio1 = (index, format, pid, lang)
4212 index = int(node.attributes[
"ffmpegindex"].value)
4213 format = node.attributes[
"codec"].value.upper()
4214 pid = int(node.attributes[
"id"].value)
4216 audio1 = (index, format, pid, lang)
4219 if format ==
"AC3" and audio1[AUDIO_CODEC] ==
"MP2":
4220 audio1 = (index, format, pid, lang)
4222 if pid < audio1[AUDIO_ID]:
4223 audio1 = (index, format, pid, lang)
4226 if preferredlang1 != preferredlang2
and nodes.length > 1:
4230 index = int(node.attributes[
"ffmpegindex"].value)
4231 lang = node.attributes[
"language"].value
4232 format = node.attributes[
"codec"].value.upper()
4233 pid = int(node.attributes[
"id"].value)
4234 if lang == preferredlang2
and format ==
"AC3":
4236 if pid < audio2[AUDIO_ID]:
4237 audio2 = (index, format, pid, lang)
4239 audio2 = (index, format, pid, lang)
4245 index = int(node.attributes[
"ffmpegindex"].value)
4246 lang = node.attributes[
"language"].value
4247 format = node.attributes[
"codec"].value.upper()
4248 pid = int(node.attributes[
"id"].value)
4249 if lang == preferredlang2
and format ==
"MP2":
4251 if pid < audio2[AUDIO_ID]:
4252 audio2 = (index, format, pid, lang)
4254 audio2 = (index, format, pid, lang)
4260 index = int(node.attributes[
"ffmpegindex"].value)
4261 format = node.attributes[
"codec"].value.upper()
4262 pid = int(node.attributes[
"id"].value)
4265 if pid != audio1[AUDIO_ID]:
4266 audio2 = (index, format, pid, lang)
4269 if format ==
"AC3" and audio2[AUDIO_CODEC] ==
"MP2" and pid != audio1[AUDIO_ID]:
4270 audio2 = (index, format, pid, lang)
4272 if pid < audio2[AUDIO_ID]
and pid != audio1[AUDIO_ID]:
4273 audio2 = (index, format, pid, lang)
4275 write(
"Video id: 0x%x, Audio1: [%d] 0x%x (%s, %s), Audio2: [%d] - 0x%x (%s, %s)" % \
4276 (video[VIDEO_ID], audio1[AUDIO_INDEX], audio1[AUDIO_ID], audio1[AUDIO_CODEC], audio1[AUDIO_LANG], \
4277 audio2[AUDIO_INDEX], audio2[AUDIO_ID], audio2[AUDIO_CODEC], audio2[AUDIO_LANG]))
4279 return (video, audio1, audio2)
4291 """Choose the subtitle stream we want from the source file"""
4293 subtitle = (-1,
'N/A', -1,
'N/A')
4296 infoDOM = xml.dom.minidom.parse(os.path.join(folder,
'streaminfo.xml'))
4298 if infoDOM.documentElement.tagName !=
"file":
4299 fatalError(
"This does not look like a stream info file (%s)" % os.path.join(folder,
'streaminfo.xml'))
4303 nodes = infoDOM.getElementsByTagName(
"subtitle")
4304 if nodes.length == 0:
4305 write(
"Didn't find any subtitle elements in stream info file.")
4308 write(
"Preferred languages %s and %s" % (preferredlang1, preferredlang2))
4313 index = int(node.attributes[
"ffmpegindex"].value)
4314 lang = node.attributes[
"language"].value
4315 format = node.attributes[
"codec"].value.upper()
4316 pid = int(node.attributes[
"id"].value)
4317 if not found
and lang == preferredlang1
and format ==
"dvbsub":
4318 subtitle = (index, format, pid, lang)
4324 index = int(node.attributes[
"ffmpegindex"].value)
4325 lang = node.attributes[
"language"].value
4326 format = node.attributes[
"codec"].value.upper()
4327 pid = int(node.attributes[
"id"].value)
4328 if not found
and lang == preferredlang2
and format ==
"dvbsub":
4329 subtitle = (index, format, pid, lang)
4335 index = int(node.attributes[
"ffmpegindex"].value)
4336 format = node.attributes[
"codec"].value.upper()
4337 pid = int(node.attributes[
"id"].value)
4339 subtitle = (index, format, pid, lang)
4342 write(
"Subtitle id: 0x%x" % (subtitle[SUBTITLE_ID]))
4350 """figure out what aspect ratio we want from the source file"""
4356 infoDOM = xml.dom.minidom.parse(os.path.join(folder,
'streaminfo.xml'))
4358 if infoDOM.documentElement.tagName !=
"file":
4359 fatalError(
"This does not look like a stream info file (%s)" % os.path.join(folder,
'streaminfo.xml'))
4363 nodes = infoDOM.getElementsByTagName(
"video")
4364 if nodes.length == 0:
4365 write(
"Didn't find any video elements in stream info file.!!!")
4368 if nodes.length > 1:
4369 write(
"Found more than one video element in stream info file.!!!")
4372 ar = float(node.attributes[
"aspectratio"].value)
4373 if ar > float(4.0/3.0 - 0.01)
and ar < float(4.0/3.0 + 0.01):
4375 write(
"Aspect ratio is 4:3")
4376 elif ar > float(16.0/9.0 - 0.01)
and ar < float(16.0/9.0 + 0.01):
4377 aspectratio =
"16:9"
4378 write(
"Aspect ratio is 16:9")
4380 write(
"Unknown aspect ratio %f - Using 16:9" % ar)
4381 aspectratio =
"16:9"
4383 aspectratio =
"16:9"
4391 """Get the video codec from the streaminfo.xml for the file"""
4394 infoDOM = xml.dom.minidom.parse(os.path.join(folder,
'streaminfo.xml'))
4396 if infoDOM.documentElement.tagName !=
"file":
4397 fatalError(
"This does not look like a stream info file (%s)" % os.path.join(folder,
'streaminfo.xml'))
4399 nodes = infoDOM.getElementsByTagName(
"video")
4400 if nodes.length == 0:
4401 write(
"Didn't find any video elements in stream info file!!!")
4404 if nodes.length > 1:
4405 write(
"Found more than one video element in stream info file!!!")
4407 return node.attributes[
"codec"].value
4413 """Get the overall file type from the streaminfo.xml for the file"""
4416 infoDOM = xml.dom.minidom.parse(os.path.join(folder,
'streaminfo.xml'))
4418 if infoDOM.documentElement.tagName !=
"file":
4419 fatalError(
"This does not look like a stream info file (%s)" % os.path.join(folder,
'streaminfo.xml'))
4421 nodes = infoDOM.getElementsByTagName(
"file")
4422 if nodes.length == 0:
4423 write(
"Didn't find any file elements in stream info file!!!")
4426 if nodes.length > 1:
4427 write(
"Found more than one file element in stream info file!!!")
4430 return node.attributes[
"type"].value
4440 streamList =
"0x%x" % video[VIDEO_ID]
4442 if audio1[AUDIO_ID] != -1:
4443 streamList +=
",0x%x" % audio1[AUDIO_ID]
4445 if audio2[AUDIO_ID] != -1:
4446 streamList +=
",0x%x" % audio2[AUDIO_ID]
4451 if subtitles[SUBTITLE_ID] != -1:
4452 streamList +=
",0x%x" % subtitles[SUBTITLE_ID]
4461 """return true if the file is dvd compliant"""
4463 if not getVideoCodec(folder).lower().startswith(
"mpeg2video"):
4470 videosize =
getVideoSize(os.path.join(folder,
"streaminfo.xml"))
4473 if file.hasAttribute(
"encodingprofile"):
4474 if file.attributes[
"encodingprofile"].value !=
"NONE":
4475 write(
"File will be re-encoded using profile %s" % file.attributes[
"encodingprofile"].value)
4480 if file.hasAttribute(
"encodingprofile"):
4481 if file.attributes[
"encodingprofile"].value ==
"NONE":
4482 write(
"WARNING: File does not have a DVD compliant resolution but "
4483 "you have selected not to re-encode the file")
4495 """Process a single video/recording file ready for burning."""
4507 """Process a single video/recording file ready for burning."""
4509 write(
"*************************************************************")
4510 write(
"Processing %s %d: '%s'" % (file.attributes[
"type"].value, count, file.attributes[
"filename"].value))
4511 write(
"*************************************************************")
4521 if file.hasAttribute(
"localfilename"):
4522 mediafile=file.attributes[
"localfilename"].value
4523 elif file.attributes[
"type"].value==
"recording":
4524 mediafile = file.attributes[
"filename"].value
4525 elif file.attributes[
"type"].value==
"video":
4526 mediafile=os.path.join(videopath, file.attributes[
"filename"].value)
4527 elif file.attributes[
"type"].value==
"file":
4528 mediafile=file.attributes[
"filename"].value
4530 fatalError(
"Unknown type of video file it must be 'recording', 'video' or 'file'.")
4533 infoDOM = xml.dom.minidom.parse( os.path.join(folder,
"info.xml") )
4535 if infoDOM.documentElement.tagName !=
"fileinfo":
4536 fatalError(
"The info.xml file (%s) doesn't look right" % os.path.join(folder,
"info.xml"))
4540 if file.attributes[
"type"].value ==
"recording":
4544 if (
getVideoCodec(folder)).lower().startswith(
"mpeg2video"):
4545 if file.attributes[
"usecutlist"].value ==
"1" and getText(infoDOM.getElementsByTagName(
"hascutlist")[0]) ==
"yes":
4547 if file.hasAttribute(
"localfilename"):
4548 localfile = file.attributes[
"localfilename"].value
4551 write(
"File has a cut list - running mythtranscode to remove unwanted segments")
4552 chanid =
getText(infoDOM.getElementsByTagName(
"chanid")[0])
4553 starttime =
getText(infoDOM.getElementsByTagName(
"starttime")[0])
4554 if runMythtranscode(chanid, starttime, os.path.join(folder,
'newfile.mpg'),
True, localfile):
4555 mediafile = os.path.join(folder,
'newfile.mpg')
4557 write(
"Failed to run mythtranscode to remove unwanted segments")
4561 if (alwaysRunMythtranscode ==
True or
4564 if file.hasAttribute(
"localfilename"):
4565 localfile = file.attributes[
"localfilename"].value
4568 write(
"Running mythtranscode --mpeg2 to fix any errors")
4569 chanid =
getText(infoDOM.getElementsByTagName(
"chanid")[0])
4570 starttime =
getText(infoDOM.getElementsByTagName(
"starttime")[0])
4571 if runMythtranscode(chanid, starttime, os.path.join(folder,
'newfile.mpg'),
False, localfile):
4572 mediafile = os.path.join(folder,
'newfile.mpg')
4574 write(
"Failed to run mythtranscode to fix any errors")
4581 if (alwaysRunMythtranscode ==
True and
4584 if file.hasAttribute(
"localfilename"):
4585 localfile = file.attributes[
"localfilename"].value
4587 localfile = file.attributes[
"filename"].value
4588 write(
"Running mythtranscode --mpeg2 to fix any errors")
4591 if runMythtranscode(chanid, starttime, os.path.join(folder,
'newfile.mpg'),
False, localfile):
4592 mediafile = os.path.join(folder,
'newfile.mpg')
4594 write(
"Failed to run mythtranscode to fix any errors")
4612 write(
"Re-encoding audio and video from nuv file")
4615 if file.hasAttribute(
"encodingprofile"):
4616 profile = file.attributes[
"encodingprofile"].value
4618 profile = defaultEncodingProfile
4620 if file.hasAttribute(
"localfilename"):
4621 mediafile = file.attributes[
"localfilename"].value
4625 elif file.attributes[
"type"].value ==
"recording":
4627 chanid =
getText(infoDOM.getElementsByTagName(
"chanid")[0])
4628 starttime =
getText(infoDOM.getElementsByTagName(
"starttime")[0])
4629 usecutlist = (file.attributes[
"usecutlist"].value ==
"1" and
4630 getText(infoDOM.getElementsByTagName(
"hascutlist")[0]) ==
"yes")
4636 encodeNuvToMPEG2(chanid, starttime, mediafile, os.path.join(folder,
"newfile2.mpg"), folder,
4637 profile, usecutlist)
4638 mediafile = os.path.join(folder,
'newfile2.mpg')
4650 write(
"Re-encoding audio and video")
4653 if file.hasAttribute(
"localfilename"):
4654 mediafile = file.attributes[
"localfilename"].value
4657 if file.hasAttribute(
"encodingprofile"):
4658 profile = file.attributes[
"encodingprofile"].value
4660 profile = defaultEncodingProfile
4664 audio1, audio2, aspectratio, profile)
4665 mediafile = os.path.join(folder,
'newfile2.mpg')
4669 if debug_keeptempfiles==
False:
4670 if os.path.exists(os.path.join(folder,
"newfile.mpg")):
4671 os.remove(os.path.join(folder,
'newfile.mpg'))
4682 write(
"Splitting MPEG stream into audio and video parts")
4686 if debug_keeptempfiles==
False:
4687 if os.path.exists(os.path.join(folder,
"newfile.mpg")):
4688 os.remove(os.path.join(folder,
'newfile.mpg'))
4689 if os.path.exists(os.path.join(folder,
"newfile2.mpg")):
4690 os.remove(os.path.join(folder,
'newfile2.mpg'))
4697 titleImage = os.path.join(folder,
"title.jpg")
4698 if not os.path.exists(titleImage):
4700 if file.attributes[
"type"].value ==
"recording":
4701 previewImage = file.attributes[
"filename"].value +
".png"
4702 if usebookmark ==
True and os.path.exists(previewImage):
4703 copy(previewImage, titleImage)
4709 write(
"*************************************************************")
4710 write(
"Finished processing '%s'" % file.attributes[
"filename"].value)
4711 write(
"*************************************************************")
4719 """Process a single video/recording file ready for burning."""
4721 write(
"*************************************************************")
4722 write(
"Processing %s %d: '%s'" % (file.attributes[
"type"].value, count, file.attributes[
"filename"].value))
4723 write(
"*************************************************************")
4733 if file.hasAttribute(
"localfilename"):
4734 mediafile=file.attributes[
"localfilename"].value
4735 elif file.attributes[
"type"].value==
"recording":
4736 mediafile = file.attributes[
"filename"].value
4737 elif file.attributes[
"type"].value==
"video":
4738 mediafile=os.path.join(videopath, file.attributes[
"filename"].value)
4739 elif file.attributes[
"type"].value==
"file":
4740 mediafile=file.attributes[
"filename"].value
4742 fatalError(
"Unknown type of video file it must be 'recording', 'video' or 'file'.")
4745 infoDOM = xml.dom.minidom.parse( os.path.join(folder,
"info.xml") )
4747 if infoDOM.documentElement.tagName !=
"fileinfo":
4748 fatalError(
"The info.xml file (%s) doesn't look right" % os.path.join(folder,
"info.xml"))
4766 write(
"Re-encoding audio and video from nuv file")
4769 if file.hasAttribute(
"encodingprofile"):
4770 profile = file.attributes[
"encodingprofile"].value
4772 profile = defaultEncodingProfile
4774 if file.hasAttribute(
"localfilename"):
4775 mediafile = file.attributes[
"localfilename"].value
4779 elif file.attributes[
"type"].value ==
"recording":
4781 chanid =
getText(infoDOM.getElementsByTagName(
"chanid")[0])
4782 starttime =
getText(infoDOM.getElementsByTagName(
"starttime")[0])
4783 usecutlist = (file.attributes[
"usecutlist"].value ==
"1" and
4784 getText(infoDOM.getElementsByTagName(
"hascutlist")[0]) ==
"yes")
4790 encodeNuvToMPEG2(chanid, starttime, mediafile, os.path.join(folder,
"newfile2.mpg"), folder,
4791 profile, usecutlist)
4792 mediafile = os.path.join(folder,
'newfile2.mpg')
4804 write(
"Re-encoding audio and video")
4807 if file.hasAttribute(
"localfilename"):
4808 mediafile = file.attributes[
"localfilename"].value
4811 if file.hasAttribute(
"encodingprofile"):
4812 profile = file.attributes[
"encodingprofile"].value
4814 profile = defaultEncodingProfile
4818 audio1, audio2, aspectratio, profile)
4819 mediafile = os.path.join(folder,
'newfile2.mpg')
4822 if os.path.exists(os.path.join(folder,
"newfile1.mpg")):
4823 os.remove(os.path.join(folder,
'newfile1.mpg'))
4839 if file.attributes[
"type"].value ==
"recording":
4840 if file.attributes[
"usecutlist"].value ==
"1" and getText(infoDOM.getElementsByTagName(
"hascutlist")[0]) ==
"yes":
4841 chanid =
getText(infoDOM.getElementsByTagName(
"chanid")[0])
4842 starttime =
getText(infoDOM.getElementsByTagName(
"starttime")[0])
4843 write(
"File has a cut list - running Project-X to remove unwanted segments")
4844 if not runProjectX(chanid, starttime, folder,
True, mediafile):
4845 fatalError(
"Failed to run Project-X to remove unwanted segments and demux")
4848 chanid =
getText(infoDOM.getElementsByTagName(
"chanid")[0])
4849 starttime =
getText(infoDOM.getElementsByTagName(
"starttime")[0])
4850 write(
"Using Project-X to demux file")
4851 if not runProjectX(chanid, starttime, folder,
False, mediafile):
4852 fatalError(
"Failed to run Project-X to demux file")
4857 write(
"Running Project-X to demux file")
4858 if not runProjectX(chanid, starttime, folder,
False, mediafile):
4859 fatalError(
"Failed to run Project-X to demux file")
4866 titleImage = os.path.join(folder,
"title.jpg")
4867 if not os.path.exists(titleImage):
4869 if file.attributes[
"type"].value ==
"recording":
4870 previewImage = file.attributes[
"filename"].value +
".png"
4871 if usebookmark ==
True and os.path.exists(previewImage):
4872 copy(previewImage, titleImage)
4878 write(
"*************************************************************")
4879 write(
"Finished processing file '%s'" % file.attributes[
"filename"].value)
4880 write(
"*************************************************************")
4886 '''go through the list of files looking for files on remote filesytems
4887 and copy them to a local file
for quicker processing
'''
4888 localTmpPath = os.path.join(tmpPath, "localcopy")
4890 tmpfile = node.attributes[
"filename"].value
4891 filename = os.path.basename(tmpfile)
4895 if res == 2
and copyremoteFiles==
True:
4897 write(
"Copying file from " + tmpfile)
4898 write(
"to " + os.path.join(localTmpPath, filename))
4902 copy(tmpfile, os.path.join(localTmpPath, filename))
4905 node.setAttribute(
"localfilename", os.path.join(localTmpPath, filename))
4908 write(
"Copying file from " + tmpfile)
4909 localfile = os.path.join(localTmpPath, filename)
4910 write(
"to " + localfile)
4917 node.setAttribute(
"localfilename", localfile)
4924 """Starts processing a MythBurn job, expects XML nodes to be passed as input."""
4925 global wantIntro, wantMainMenu, wantChapterMenu, wantDetailsPage
4926 global themeDOM, themeName, themeFonts
4929 media=job.getElementsByTagName(
"media")
4933 themeName=job.attributes[
"theme"].value
4937 fatalError(
"Failed to validate theme (%s)" % themeName)
4945 nodes=themeDOM.getElementsByTagName(
"intro")
4946 wantIntro = (nodes.length > 0)
4948 nodes=themeDOM.getElementsByTagName(
"menu")
4949 wantMainMenu = (nodes.length > 0)
4951 nodes=themeDOM.getElementsByTagName(
"submenu")
4952 wantChapterMenu = (nodes.length > 0)
4954 nodes=themeDOM.getElementsByTagName(
"detailspage")
4955 wantDetailsPage = (nodes.length > 0)
4957 write(
"wantIntro: %d, wantMainMenu: %d, wantChapterMenu: %d, wantDetailsPage: %d" \
4958 % (wantIntro, wantMainMenu, wantChapterMenu, wantDetailsPage))
4960 if videomode==
"ntsc":
4963 elif videomode==
"pal":
4967 fatalError(
"Unknown videomode is set (%s)" % videomode)
4969 write(
"Final DVD Video format will be " + videomode)
4973 files=media[0].getElementsByTagName(
"file")
4975 if files.length > 0:
4976 write(
"There are %s file(s) to process" % files.length)
4978 if debug_secondrunthrough==
False:
4981 localCopyFolder=os.path.join(
getTempPath(),
"localcopy")
4982 os.makedirs(localCopyFolder)
4994 if debug_secondrunthrough==
False:
4999 if debug_secondrunthrough==
False:
5025 if not wantMainMenu
and not wantChapterMenu:
5027 elif not wantMainMenu:
5033 if mediatype == DVD_DL:
5048 os.path.join(folder,
'stream0'),
5049 os.path.join(folder,
'stream1'),
5050 os.path.join(folder,
'final.vob'),
5057 if debug_keeptempfiles==
False:
5062 if os.path.exists(os.path.join(folder,
"stream.mv2")):
5063 os.remove(os.path.join(folder,
'stream.mv2'))
5064 if os.path.exists(os.path.join(folder,
"stream0.mp2")):
5065 os.remove(os.path.join(folder,
'stream0.mp2'))
5066 if os.path.exists(os.path.join(folder,
"stream1.mp2")):
5067 os.remove(os.path.join(folder,
'stream1.mp2'))
5068 if os.path.exists(os.path.join(folder,
"stream0.ac3")):
5069 os.remove(os.path.join(folder,
'stream0.ac3'))
5070 if os.path.exists(os.path.join(folder,
"stream1.ac3")):
5071 os.remove(os.path.join(folder,
'stream1.ac3'))
5075 infoDOM = xml.dom.minidom.parse( os.path.join(
getItemTempPath(1),
"info.xml") )
5077 if infoDOM.documentElement.tagName !=
"fileinfo":
5078 fatalError(
"The info.xml file (%s) doesn't look right" % os.path.join(folder,
"info.xml"))
5081 title = title.encode(
'ascii',
'replace').decode(
'ascii',
'replace')
5082 title = title.strip()
5087 while (index < len(title))
and (index < 32):
5088 if title[index].isalnum
and title[index] !=
' ':
5089 title_new += title[index]
5094 title = title_new.upper()
5100 if docreateiso ==
True or mediatype == FILE:
5104 if doburn ==
True and mediatype != FILE:
5108 if mediatype == FILE
and savefilename !=
"":
5109 write(
"Moving ISO image to: %s" % savefilename)
5111 os.rename(os.path.join(
getTempPath(),
'mythburn.iso'), savefilename)
5113 f1 = open(os.path.join(
getTempPath(),
'mythburn.iso'),
'rb')
5114 f2 = open(savefilename,
'wb')
5115 data = f1.read(1024 * 1024)
5118 data = f1.read(1024 * 1024)
5121 os.unlink(os.path.join(
getTempPath(),
'mythburn.iso'))
5123 write(
"Nothing to do! (files)")
5125 write(
"Nothing to do! (media)")
5133 -h/--help (Show this usage)
5134 -j/--jobfile file (use file as the job file)
5135 -l/--progresslog file (log file to output progress messages)
5139#############################################################
5140# The main starting point for mythburn.py
5143 global sharepath, scriptpath, cpuCount, videopath, gallerypath, musicpath
5144 global videomode, temppath, logpath, dvddrivepath, dbVersion, preferredlang1
5145 global preferredlang2, useFIFO, encodetoac3, alwaysRunMythtranscode
5146 global copyremoteFiles, mainmenuAspectRatio, chaptermenuAspectRatio, dateformat
5147 global timeformat, clearArchiveTable, nicelevel, drivespeed, path_mplex
5148 global path_dvdauthor, path_mkisofs, path_growisofs, path_M2VRequantiser, addSubtitles
5149 global path_jpeg2yuv, path_spumux, path_mpeg2enc, path_projectx, useprojectx, progresslog
5150 global progressfile, jobfile
5152 write(
"mythburn.py (%s) starting up..." % VERSION)
5155 if not hasattr(sys,
"hexversion")
or sys.hexversion < 0x20305F0:
5156 sys.stderr.write(
"Sorry, your Python is too old. Please upgrade at least to 2.3.5\n")
5160 scriptpath = os.path.dirname(sys.argv[0])
5161 scriptpath = os.path.abspath(scriptpath)
5162 write(
"script path:" + scriptpath)
5165 sharepath = os.path.split(scriptpath)[0]
5166 sharepath = os.path.split(sharepath)[0]
5167 write(
"myth share path:" + sharepath)
5171 opts, args = getopt.getopt(sys.argv[1:],
"j:hl:", [
"jobfile=",
"help",
"progresslog="])
5172 except getopt.GetoptError:
5178 if o
in (
"-h",
"--help"):
5181 if o
in (
"-j",
"--jobfile"):
5183 write(
"passed job file: " + a)
5184 if o
in (
"-l",
"--progresslog"):
5185 progresslog = str(a)
5186 write(
"passed progress log file: " + a)
5189 if progresslog !=
"":
5190 if os.path.exists(progresslog):
5191 os.remove(progresslog)
5192 progressfile = open(progresslog,
'w', encoding=
'utf-8')
5193 write(
"mythburn.py (%s) starting up..." % VERSION)
5198 saveSetting(
"MythArchiveLastRunStart", time.strftime(
"%Y-%m-%d %H:%M:%S "))
5200 saveSetting(
"MythArchiveLastRunStatus",
"Running")
5207 if not os.environ[
'PATH'].endswith(
':'):
5208 os.environ[
'PATH'] +=
":"
5209 os.environ[
'PATH'] +=
"/bin:/sbin:/usr/local/bin:/usr/bin:/opt/bin:" + installPrefix +
"/bin:"
5213 videopath = defaultsettings.get(
"VideoStartupDir",
None)
5214 gallerypath = defaultsettings.get(
"GalleryDir",
None)
5215 musicpath = defaultsettings.get(
"MusicLocation",
None)
5216 videomode = defaultsettings[
"MythArchiveVideoFormat"].lower()
5217 temppath = os.path.join(defaultsettings[
"MythArchiveTempDir"],
"work")
5218 logpath = os.path.join(defaultsettings[
"MythArchiveTempDir"],
"logs")
5219 write(
"temppath: " + temppath)
5220 write(
"logpath: " + logpath)
5221 dvddrivepath = defaultsettings[
"MythArchiveDVDLocation"]
5222 dbVersion = defaultsettings[
"DBSchemaVer"]
5223 preferredlang1 = defaultsettings[
"ISO639Language0"]
5224 preferredlang2 = defaultsettings[
"ISO639Language1"]
5225 useFIFO = (defaultsettings[
"MythArchiveUseFIFO"] ==
'1')
5226 alwaysRunMythtranscode = (defaultsettings[
"MythArchiveAlwaysUseMythTranscode"] ==
'1')
5227 copyremoteFiles = (defaultsettings[
"MythArchiveCopyRemoteFiles"] ==
'1')
5228 mainmenuAspectRatio = defaultsettings[
"MythArchiveMainMenuAR"]
5229 chaptermenuAspectRatio = defaultsettings[
"MythArchiveChapterMenuAR"]
5230 dateformat = defaultsettings.get(
"MythArchiveDateFormat",
"%a %d %b %Y")
5231 timeformat = defaultsettings.get(
"MythArchiveTimeFormat",
"%I:%M %p")
5232 drivespeed = int(defaultsettings.get(
"MythArchiveDriveSpeed",
"0"))
5233 if "MythArchiveClearArchiveTable" in defaultsettings:
5234 clearArchiveTable = (defaultsettings[
"MythArchiveClearArchiveTable"] ==
'1')
5235 nicelevel = defaultsettings.get(
"JobQueueCPU",
"0")
5238 path_mplex = [defaultsettings[
"MythArchiveMplexCmd"], os.path.split(defaultsettings[
"MythArchiveMplexCmd"])[1]]
5239 path_dvdauthor = [defaultsettings[
"MythArchiveDvdauthorCmd"], os.path.split(defaultsettings[
"MythArchiveDvdauthorCmd"])[1]]
5240 path_mkisofs = [defaultsettings[
"MythArchiveMkisofsCmd"], os.path.split(defaultsettings[
"MythArchiveMkisofsCmd"])[1]]
5241 path_growisofs = [defaultsettings[
"MythArchiveGrowisofsCmd"], os.path.split(defaultsettings[
"MythArchiveGrowisofsCmd"])[1]]
5242 path_M2VRequantiser = [defaultsettings[
"MythArchiveM2VRequantiserCmd"], os.path.split(defaultsettings[
"MythArchiveM2VRequantiserCmd"])[1]]
5243 path_jpeg2yuv = [defaultsettings[
"MythArchiveJpeg2yuvCmd"], os.path.split(defaultsettings[
"MythArchiveJpeg2yuvCmd"])[1]]
5244 path_spumux = [defaultsettings[
"MythArchiveSpumuxCmd"], os.path.split(defaultsettings[
"MythArchiveSpumuxCmd"])[1]]
5245 path_mpeg2enc = [defaultsettings[
"MythArchiveMpeg2encCmd"], os.path.split(defaultsettings[
"MythArchiveMpeg2encCmd"])[1]]
5247 path_projectx = [defaultsettings[
"MythArchiveProjectXCmd"], os.path.split(defaultsettings[
"MythArchiveProjectXCmd"])[1]]
5248 useprojectx = (defaultsettings[
"MythArchiveUseProjectX"] ==
'1')
5249 addSubtitles = (defaultsettings[
"MythArchiveAddSubtitles"] ==
'1')
5252 if path_projectx[0] ==
"":
5255 if nicelevel ==
'1':
5257 elif nicelevel ==
'2':
5262 nicelevel = os.nice(nicelevel)
5263 write(
"Setting process priority to %s" % nicelevel)
5268 write(
"Cannot change ionice level")
5270 write(
"Setting ionice level to idle")
5271 p = psutil.Process(os.getpid())
5272 p.ionice(psutil.IOPRIO_CLASS_IDLE)
5279 lckpath = os.path.join(logpath,
"mythburn.lck")
5281 fd = os.open(lckpath, os.O_WRONLY | os.O_CREAT | os.O_EXCL)
5283 os.write(fd, b
"%d\n" % os.getpid())
5288 except OSError
as e:
5289 if e.errno == errno.EEXIST:
5290 write(
"Lock file exists -- already running???")
5298 jobDOM = xml.dom.minidom.parse(jobfile)
5301 if jobDOM.documentElement.tagName !=
"mythburn":
5306 jobs=jobDOM.getElementsByTagName(
"job")
5309 write(
"Processing Mythburn job number %s." % jobcount)
5312 options = job.getElementsByTagName(
"options")
5313 if options.length > 0:
5321 if clearArchiveTable ==
True:
5324 saveSetting(
"MythArchiveLastRunStatus",
"Success")
5325 saveSetting(
"MythArchiveLastRunEnd", time.strftime(
"%Y-%m-%d %H:%M:%S "))
5326 write(
"Finished processing jobs!!!")
5332 os.system(
"chmod -R a+rw-x+X %s" % defaultsettings[
"MythArchiveTempDir"])
5337 traceback.print_exc(file=sys.stdout)
5338 if progresslog !=
"":
5339 traceback.print_exc(file=progressfile)
5342 saveSetting(
"MythArchiveLastRunEnd", time.strftime(
"%Y-%m-%d %H:%M:%S "))
5344if __name__ ==
"__main__":
5347os.putenv(
"LC_ALL", oldlocale)
static uint32_t getsize(int fd)
class to hold a font definition
def __init__(self, name=None, fontFile=None, size=19, color="white", effect="normal", shadowColor="black", shadowSize=1)
def drawText(self, text, color=None)
static pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
MBASE_PUBLIC long long copy(QFile &dst, QFile &src, uint block_size=0)
Copies src file to dst file.
def createVideoChapters(itemnum, numofchapters, lengthofvideo, getthumbnails)
Creates a set of chapter points evenly spread thoughout a file Optionally grabs the thumbnails from t...
def createChapterMenu(screensize, screendpi, numberofitems)
creates a chapter menu for a file on a DVD
def quoteString(str)
Return the input string with single quotes escaped.
def isMediaAVIFile(file)
checks if a file is an avi file
def total_mv2_brl(files, rate)
returns total size of bitrate-limited m2v files
def encodeNuvToMPEG2(chanid, starttime, mediafile, destvideofile, folder, profile, usecutlist)
Re-encodes a nuv file to mpeg2 optionally removing commercials.
def doProcessFileProjectX(file, folder, count)
process a single file ready for burning using projectX to cut and demux
def runM2VRequantiser(source, destination, factor)
Run M2VRequantiser.
def calcSyncOffset(index)
Calculates the sync offset between the video and first audio stream.
def doProcessFile(file, folder, count)
process a single file ready for burning using mythtranscode/mythreplex to cut and demux
def getLengthOfVideo(index)
Gets the duration of a video file from its stream info file.
def getStreamInformation(filename, xmlFilename, lenMethod)
Creates a stream xml file for a video file.
def getFileInformation(file, folder)
Creates an info xml file from details in the job file or from the DB.
def WriteXMLToFile(myDOM, filename)
Write an xml file to disc.
def getEncodingProfilePath()
Get the directory where all encoder profile files are located.
def ts2pts(time)
convert time stamp to pts
def getItemTempPath(itemnumber)
Creates a file path where the temp files for a video file can be created.
def getVideoCodec(folder)
gets video stream codec from the stream info xml file
def preProcessFile(file, folder, count)
Pre-process a single video/recording file.
def getStreamList(folder)
get the list of required stream ids for a file
def getFormatedLengthOfVideo(index)
Gets the length of a video file and returns it as a string.
def BurnDVDISO(title)
Burns the contents of a directory to create a DVD.
def createDVDAuthorXMLNoMenus(screensize, numberofitems)
Creates the DVDAuthor xml file used to create an Autoplay DVD.
def multiplexMPEGStream(video, audio1, audio2, destination, syncOffset)
Recombines a video and one or two audio streams back together adding in the NAV packets required to c...
def write(text, progress=True)
def calculateFileSizes(files)
Calculates the total size of all the video, audio and menu files.
def runCommand(command)
Runs an external command checking to see if the user has cancelled the DVD creation process.
def selectAspectRatio(folder)
gets the video aspect ratio from the stream info xml file
def getAspectRatioOfVideo(index)
Gets the aspect ratio of a video file from its stream info file.
def selectSubtitleStream(folder)
def deMultiplexMPEG2File(folder, mediafile, video, audio1, audio2)
Splits a file into the separate audio and video streams using mythreplex.
def processJob(job)
processes one job
def deleteAllFilesInFolder(folder)
Removes all the files from a directory.
def isFileOkayForDVD(file, folder)
check if file is DVD compliant
def checkCancelFlag()
Check to see if the user has cancelled the DVD creation process.
def encodeMenu(background, tempvideo, music, musiclength, tempmovie, xmlfile, finaloutput, aspectratio)
Creates a short mpeg file from a jpeg image and an ac3 sound track.
def loadFonts(themeDOM)
Load the font defintions from a DVD theme file.
def copyRemote(files, tmpPath)
copy files on remote filesystems to the local filesystem
def getVideoSize(xmlFilename)
Gets the video width and height from a file's stream xml file.
def findEncodingProfile(profile)
Return an xml node from a re-encoding profile xml file for a given profile name.
def CreateDVDISO(title)
Creates an ISO image from the contents of a directory.
def quoteCmdArg(arg)
Escape quotes in a command line argument.
def createEmptyPreviewFolder(videoitem)
Creates the directory to hold the preview images for an animated menu.
def getVideoParams(folder)
Gets the video resolution, frames per second and aspect ratio of a video file from its stream info fi...
def getScaledAttribute(node, attribute)
Scale a theme position/size depending on the current video mode.
def extractVideoFrame(source, destination, seconds)
Grabs a sequence of consecutive frames from a file.
def getFontPathName(fontname)
Returns the path where we can find our fonts.
def performMPEG2Shrink(files, dvdrsize)
Uses requantiser if available to shrink the video streams so they will fit on a DVD.
def createDVDAuthorXML(screensize, numberofitems)
Creates the DVDAuthor xml file used to create a standard DVD with menus.
def secondsToFrames(seconds)
Convert a time in seconds to a frame number.
def generateProjectXCutlist(chanid, starttime, folder)
Create a projectX cut list for a recording.
def processFile(file, folder, count)
process a single file ready for burning using either mythtranscode/mythreplex or ProjectX as the cutt...
def doesFileExist(file)
Returns true/false if a given file or path exists.
def createMenu(screensize, screendpi, numberofitems)
creates the main menu for a DVD
def getOptions(options)
Load the options from the options node passed in the job file.
def paintButton(draw, bgimage, bgimagemask, node, infoDOM, itemnum, page, itemsonthispage, chapternumber, chapterlist)
Paints a button onto an image.
def encodeAudio(format, sourcefile, destinationfile, deletesourceafterencode)
Re-encodes an audio stream to ac3.
def runMythtranscode(chanid, starttime, destination, usecutlist, localfile)
Run a file though the lossless encoder optionally removing commercials.
def processAudio(folder)
checks to see if an audio stream need to be converted to ac3
def paintImage(filename, maskfilename, imageDom, destimage, stretch=True)
Paint an image on the background image.
def intelliDraw(drawer, text, font, containerWidth)
Splits some text into lines so it will fit into a given container.
def paintText(draw, image, text, node, color=None, x=None, y=None, width=None, height=None)
Paint some theme text on to an image.
def isResolutionOkayForDVD(videoresolution)
Returns True if the given resolution is a DVD compliant one.
def drawThemeItem(page, itemsonthispage, itemnum, menuitem, bgimage, draw, bgimagemask, drawmask, highlightcolor, spumuxdom, spunode, numberofitems, chapternumber, chapterlist)
Draws text and graphics onto a dvd menu.
def createDVDAuthorXMLNoMainMenu(screensize, numberofitems)
Creates the DVDAuthor xml file used to create a DVD with no main menu.
def saveSetting(name, data)
Save a setting to the settings table in the DB.
def runDVDAuthor()
Runs DVDAuthor to create a DVD file structure.
def getText(node)
Returns the text contents from a given XML element.
def frameToTime(frame, fps)
Convert a frame number to a time string.
def encodeVideoToMPEG2(source, destvideofile, video, audio1, audio2, aspectratio, profile)
Re-encodes a file to mpeg2.
def runProjectX(chanid, starttime, folder, usecutlist, file)
Use Project-X to cut commercials and/or demux an mpeg2 file.
def createDetailsPage(screensize, screendpi, numberofitems)
creates the details page for a file on a DVD
def selectStreams(folder)
def extractVideoFrames(source, destination, thumbList)
Grabs a list of single frames from a file.
def checkBoundaryBox(boundarybox, node)
Check if boundary box need adjusting.
def clearArchiveItems()
Remove all archive items from the archiveitems DB table.
def getCPUCount()
Try to work out how many cpus we have available.
def createVideoChaptersFixedLength(itemnum, segment, lengthofvideo)
Creates some fixed length chapter marks.
def validateTheme(theme)
Returns True if the theme.xml file can be found for the given theme.
def deleteEverythingInFolder(folder)
Romoves all the objects from a directory.
def paintBackground(image, node)
Paints a background rectangle onto an image.
def getDefaultParametersFromMythTVDB()
Reads a load of settings from DB.
def getTempPath()
Directory where all temporary files will be created.
def getFileType(folder)
gets file container type from the stream info xml file
def expandItemText(infoDOM, text, itemnumber, pagenumber, keynumber, chapternumber, chapterlist)
Substitutes some text from a theme file with the required values.
def main()
The main starting point for mythburn.py.
def getThemeFile(theme, file)
Try to find a theme file.
def fatalError(msg)
Display an error message and exit.
def checkSubtitles(spumuxFile)
check the given spumux.xml file for consistancy
def timeStringToSeconds(formatedtime)
Convert a time string of format 00:00:00 to number of seconds.
def getThemeConfigurationXML(theme)
Load the theme.xml file for a DVD theme.
def generateVideoPreview(videoitem, itemonthispage, menuitem, starttime, menulength, previewfolder)
Generates the thumbnail images used to create animated menus.
def getAudioParams(folder)
Gets the audio sample rate and number of channels of a video file from its stream info file.