| 1 | #!/usr/bin/env python
|
|---|
| 2 | #
|
|---|
| 3 | # Copyright 2007 by Peter Schachte
|
|---|
| 4 | #
|
|---|
| 5 | # This script is free software; you can redistribute it and/or
|
|---|
| 6 | # modify it under the terms of the GNU General Public
|
|---|
| 7 | # License as published by the Free Software Foundation; either
|
|---|
| 8 | # version 2 of the License, or (at your option) any later version.
|
|---|
| 9 | #
|
|---|
| 10 | # This script is distributed in the hope that it will be useful,
|
|---|
| 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|---|
| 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|---|
| 13 | # General Public License for more details.
|
|---|
| 14 | #
|
|---|
| 15 | # You should have received a copy of the GNU General Public
|
|---|
| 16 | # License along with this library; if not, write to the
|
|---|
| 17 | # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|---|
| 18 | # Boston, MA 02111-1307, USA.
|
|---|
| 19 |
|
|---|
| 20 |
|
|---|
| 21 | ################################################################
|
|---|
| 22 | # Generate nice printed labels for standard DVD boxes holding up to 4
|
|---|
| 23 | # mytharchive native archive format DVDs. This relies on cdlabelgen
|
|---|
| 24 | # (http://www.aczoom.com/tools/cdinsert/) to do the actual postscript
|
|---|
| 25 | # formatting. It examines the DVD(s), reading the relevant information
|
|---|
| 26 | # from the xml file stored on the disc itself to construct a suitable
|
|---|
| 27 | # label listing the videos included on each disc in the box.
|
|---|
| 28 | #
|
|---|
| 29 | # Currently, this script supports only native format archives, and only
|
|---|
| 30 | # mythvideo files. I'd like to support DVD format archives as well,
|
|---|
| 31 | # but they do not contain the necessary information. This script
|
|---|
| 32 | # should be able to support recordings, but I have not implemented this
|
|---|
| 33 | # yet.
|
|---|
| 34 | #
|
|---|
| 35 | # This code supports naming all discs with a common prefix (by
|
|---|
| 36 | # default "MythArchive Disc "). As each disc is inserted, you will
|
|---|
| 37 | # be prompted for its number; the full name of the disc is the prefix
|
|---|
| 38 | # followed by the entered text. The prefix can be set to anything on
|
|---|
| 39 | # the command line, including the empty string, and any text can be
|
|---|
| 40 | # entered for the disc "number."
|
|---|
| 41 | ################################################################
|
|---|
| 42 |
|
|---|
| 43 |
|
|---|
| 44 | import sys, os, glob
|
|---|
| 45 | from optparse import OptionParser
|
|---|
| 46 | from elementtree.ElementTree import ElementTree
|
|---|
| 47 |
|
|---|
| 48 | ################################################################
|
|---|
| 49 | # Command line parsing and help text
|
|---|
| 50 |
|
|---|
| 51 | def parse_command_line():
|
|---|
| 52 | """Handle parsing of command line."""
|
|---|
| 53 | parser = OptionParser(version="0.1")
|
|---|
| 54 | parser.add_option("-o", "--outfile", metavar="FILE", default="-",
|
|---|
| 55 | help="write postscript output to FILE [default: stdout]")
|
|---|
| 56 | parser.add_option("-q", "--quiet", action="store_false",
|
|---|
| 57 | dest="verbose", default=True,
|
|---|
| 58 | help="don't print status messages to stderr")
|
|---|
| 59 | parser.add_option("-m", "--mountpoint", metavar="DIR",
|
|---|
| 60 | help="mount DVD on DIR [default /media/cdrom]",
|
|---|
| 61 | default="/media/cdrom")
|
|---|
| 62 | parser.add_option("-p", "--prefix", metavar="TEXT",
|
|---|
| 63 | help="common prefix for discs " +
|
|---|
| 64 | "[default 'MythArchive Disc ']",
|
|---|
| 65 | default="MythArchive Disc ")
|
|---|
| 66 | (options,args) = parser.parse_args()
|
|---|
| 67 | return options
|
|---|
| 68 |
|
|---|
| 69 |
|
|---|
| 70 | ################################################################
|
|---|
| 71 | # Main code
|
|---|
| 72 |
|
|---|
| 73 | def main():
|
|---|
| 74 | options = parse_command_line()
|
|---|
| 75 | discs = []
|
|---|
| 76 | while True:
|
|---|
| 77 | added = get_titles(options)
|
|---|
| 78 | if added is None: break
|
|---|
| 79 | discs.append(added)
|
|---|
| 80 |
|
|---|
| 81 | if discs:
|
|---|
| 82 | generate_cover(discs, options)
|
|---|
| 83 | else:
|
|---|
| 84 | sys.stderr.write("No titles found; no label generated.\n")
|
|---|
| 85 | sys.exit(1)
|
|---|
| 86 |
|
|---|
| 87 |
|
|---|
| 88 | ################################################################
|
|---|
| 89 | # Read one DVD to see what's on it
|
|---|
| 90 |
|
|---|
| 91 | def get_titles(options):
|
|---|
| 92 | """Ask user to insert one DVD, and return list of items it contains."""
|
|---|
| 93 | while True:
|
|---|
| 94 | sys.stderr.write("Insert DVD to include in DVD label now, " +
|
|---|
| 95 | "then enter the MythArchive\n")
|
|---|
| 96 | sys.stderr.write("disc number, or just hit enter if there are " +
|
|---|
| 97 | "no more DVDs to include\n")
|
|---|
| 98 | sys.stderr.write("Disc number, or enter to quit: ")
|
|---|
| 99 | discnum = sys.stdin.readline().strip()
|
|---|
| 100 | if discnum == "": return None
|
|---|
| 101 | if 0 != os.system("mount -r " + options.mountpoint):
|
|---|
| 102 | sys.stderr.write("Error: couldn't mount DVD on " +
|
|---|
| 103 | options.mountpoint + "\n")
|
|---|
| 104 | continue
|
|---|
| 105 | items = []
|
|---|
| 106 | for dirname in os.listdir(options.mountpoint):
|
|---|
| 107 | if options.verbose:
|
|---|
| 108 | sys.stderr.write(" " + dirname + "\n")
|
|---|
| 109 | for filename in glob.glob(options.mountpoint + "/" +
|
|---|
| 110 | dirname + "/*.xml"):
|
|---|
| 111 | # there should only be one xml file in each directory
|
|---|
| 112 | items.append(ElementTree(file=filename).getroot())
|
|---|
| 113 |
|
|---|
| 114 | os.system("eject " + options.mountpoint)
|
|---|
| 115 | return (discnum, items)
|
|---|
| 116 |
|
|---|
| 117 | ################################################################
|
|---|
| 118 | # Finally generate the cover Postscript file
|
|---|
| 119 |
|
|---|
| 120 | def generate_cover(discs, options):
|
|---|
| 121 | """Generate the DVD cover postscript output for the specified titles."""
|
|---|
| 122 | lines = []
|
|---|
| 123 | if len(discs) > 1:
|
|---|
| 124 | names = [name for (name,content) in discs]
|
|---|
| 125 | title = options.prefix + ", ".join(names[0:-1]) + " and " + names[-1]
|
|---|
| 126 | for (name,content) in discs:
|
|---|
| 127 | lines.append("")
|
|---|
| 128 | lines.append("{#BI} ----- " +
|
|---|
| 129 | options.prefix + name + " -----")
|
|---|
| 130 | lines.append("")
|
|---|
| 131 | lines += text_for_one_disc(content, options)
|
|---|
| 132 | lines.append("")
|
|---|
| 133 | else:
|
|---|
| 134 | title = options.prefix + discs[0][0]
|
|---|
| 135 | lines += text_for_one_disc(discs[0][1], options)
|
|---|
| 136 |
|
|---|
| 137 |
|
|---|
| 138 | if options.verbose:
|
|---|
| 139 | sys.stderr.write(
|
|---|
| 140 | ("cdlabelgen --create-dvd-outside -c '%s' " +
|
|---|
| 141 | "-w -o '%s' -v 40 -i '%s'\n") % \
|
|---|
| 142 | (quote(title), quote(options.outfile), quote("%".join(lines))))
|
|---|
| 143 | sys.exit(os.system(
|
|---|
| 144 | "cdlabelgen --create-dvd-outside -c '%s' -w -o '%s' -v 40 -i '%s'" % \
|
|---|
| 145 | (quote(title), quote(options.outfile), quote("%".join(lines)))))
|
|---|
| 146 |
|
|---|
| 147 |
|
|---|
| 148 | ################################################################
|
|---|
| 149 | # Generate the output for one disc
|
|---|
| 150 |
|
|---|
| 151 | def text_for_one_disc(content, options):
|
|---|
| 152 | lines = []
|
|---|
| 153 | for item in content:
|
|---|
| 154 | meta = item.find("videometadata")
|
|---|
| 155 | if item.get("type") == "video" and meta:
|
|---|
| 156 | title = meta.findtext("title")
|
|---|
| 157 | if title:
|
|---|
| 158 | text = "{#B}" + title
|
|---|
| 159 | year = meta.findtext("year")
|
|---|
| 160 | if year: text += " (" + year + ")"
|
|---|
| 161 | lines.append(text)
|
|---|
| 162 |
|
|---|
| 163 | line = []
|
|---|
| 164 | gens = item.find("genres")
|
|---|
| 165 | if gens:
|
|---|
| 166 | line.append(", ".join([g.get("genre") for g in gens]))
|
|---|
| 167 | length = meta.findtext("length")
|
|---|
| 168 | if length and length != "0":
|
|---|
| 169 | line.append("Running time: %s minutes" % length)
|
|---|
| 170 | if line: lines.append("; ".join(line))
|
|---|
| 171 |
|
|---|
| 172 | line = []
|
|---|
| 173 | rating = meta.findtext("rating")
|
|---|
| 174 | if rating: line.append(rating)
|
|---|
| 175 | imdb = meta.findtext("userrating")
|
|---|
| 176 | if imdb: line.append("IMDB rating: %s / 10" % imdb)
|
|---|
| 177 | if line: lines.append("; ".join(line))
|
|---|
| 178 | lines.append("")
|
|---|
| 179 | return lines
|
|---|
| 180 |
|
|---|
| 181 | ################################################################
|
|---|
| 182 | # Protect single quote characters from the shell
|
|---|
| 183 |
|
|---|
| 184 | def quote(str):
|
|---|
| 185 | """Return the input string with single quotes escaped."""
|
|---|
| 186 | return str.replace("'", "'\"'\"'")
|
|---|
| 187 |
|
|---|
| 188 | ################################################################
|
|---|
| 189 |
|
|---|
| 190 | if __name__ == "__main__":
|
|---|
| 191 | main()
|
|---|