1# -*- coding: utf-8 -*- 2 3__revision__ = '$Id: $' 4 5# Copyright (c) 2011, 2012 6 7# This program is free software; you can redistribute it and/or modify 8# it under the terms of the GNU General Public License as published by 9# the Free Software Foundation; either version 2 of the License, or 10# (at your option) any later version. 11# 12# This program is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU Library General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License 18# along with this program; if not, write to the Free Software 19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 20 21# You may use and distribute this software under the terms of the 22# GNU General Public License, version 2 or later 23 24from plugins.imp import ImportPlugin as IP 25import os 26import gutils 27import string 28from xml.dom import minidom, Node 29import zipfile 30from shutil import rmtree 31from tempfile import mkdtemp 32 33import logging 34log = logging.getLogger("Griffith") 35 36 37class ImportPlugin(IP): 38 description = 'Tellico' 39 author = 'Elan Ruusamäe' 40 email = 'glen@delfi.ee' 41 version = '1.1' 42 file_filters = '*.[tT][cC]' 43 mime_types = None 44 45 fileversion = None 46 filedom = None 47 items = None 48 itemindex = 0 49 zipdir = None 50 51 def initialize(self): 52 if not IP.initialize(self): 53 return False 54 self.edit = False 55 return True 56 57 def set_source(self, name): 58 IP.set_source(self, name) 59 self.filename = name 60 self.fileversion = self.read_fileversion() 61 if self.fileversion == None: 62 gutils.error(_('The format of the file is not supported.')) 63 return False 64 return True 65 66 def count_movies(self): 67 """Returns number of movies in file which is about to be imported""" 68 count = 0 69 if self.filedom: 70 try: 71 collectionElement = self.filedom.getElementsByTagName('collection')[0] 72 for element in collectionElement.childNodes: 73 if element.nodeType == Node.ELEMENT_NODE and element.nodeName == 'entry': 74 count = count + 1 75 log.info('Tellico Import: %s movies for import' % count) 76 except: 77 log.exception('') 78 else: 79 log.error('Tellico Import: No filedom object.') 80 return count 81 82 def get_movie_details(self): 83 """Returns dictionary with movie details""" 84 if not self.filedom: 85 log.error('Tellico Import: filedom not open') 86 return None 87 88 if not self.items: 89 collectionElement = self.filedom.getElementsByTagName('collection')[0] 90 self.items = collectionElement.childNodes 91 self.itemindex = 0 92 if not self.items or len(self.items) < 1: 93 return None 94 if len(self.items) <= self.itemindex: 95 return None 96 item = self.items[self.itemindex] 97 while not (item.nodeType == Node.ELEMENT_NODE and item.nodeName == 'entry') and len(self.items) > self.itemindex + 1: 98 self.itemindex = self.itemindex + 1 99 item = self.items[self.itemindex] 100 if len(self.items) <= self.itemindex: 101 return None 102 if not len(item.childNodes): 103 return None 104 105 details = {} 106 try: 107 details['number'] = item.getAttribute('id') 108 for node in item.childNodes: 109 if node.nodeType == Node.ELEMENT_NODE and len(node.childNodes) > 0: 110 if node.nodeName == 'color': 111 value = node.childNodes[0].data.strip() 112 if value == 'Color': 113 details['color'] = 1 114 elif value == 'Black & White': 115 details['color'] = 2 116 elif node.nodeName == 'region': 117 details['region'] = node.childNodes[0].data.strip() 118 elif node.nodeName == 'running-time': 119 details['runtime'] = node.childNodes[0].data.strip() 120 try: 121 details['runtime'] = int(details['runtime']) 122 except: 123 details['runtime'] = 0 124 elif node.nodeName == 'year': 125 details['year'] = node.childNodes[0].data.strip() 126 elif node.nodeName == 'orig-title': 127 details['o_title'] = node.childNodes[0].data.strip() 128 elif node.nodeName == 'title': 129 details['title'] = node.childNodes[0].data.strip() 130 elif node.nodeName == 'directors': 131 details['director'] = self._joinXmlNodes(node.getElementsByTagName('director')) 132 elif node.nodeName == 'writers': 133 details['screenplay'] = self._joinXmlNodes(node.getElementsByTagName('writer')) 134 elif node.nodeName == 'o_site': 135 details['o_site'] = node.childNodes[0].data.strip() 136 elif node.nodeName == 'url': 137 details['site'] = node.childNodes[0].data.strip() 138 elif node.nodeName == 'nationalitys': 139 details['country'] = self._joinXmlNodes(node.getElementsByTagName('nationality')) 140 elif node.nodeName == 'genres': 141 details['genre'] = self._joinXmlNodes(node.getElementsByTagName('genre')) 142 elif node.nodeName == 'certification': 143 details['classification'] = node.childNodes[0].data.strip() 144 elif node.nodeName == 'casts': 145 details['cast'] = self._joinCastNodes(node.getElementsByTagName('cast'), separator = "\n") 146 elif node.nodeName == 'plot': 147 details['plot'] = node.childNodes[0].data.strip() 148 elif node.nodeName == 'comments': 149 details['notes'] = node.childNodes[0].data.strip() 150 elif node.nodeName == 'cover': 151 imgfile = node.childNodes[0].data.strip() 152 # use 'image' field, 'poster' field is buggy, bug #913283 153 # https://bugs.launchpad.net/griffith/+bug/913283 154 details['image'] = os.path.join(self.zipdir, 'images', imgfile) 155 except EOFError: 156 details = None 157 except Exception as e: 158 log.exception('') 159 details = None 160 161 self.itemindex = self.itemindex + 1 162 return details 163 164 def clear(self): 165 """clear plugin before next source file""" 166 IP.clear(self) 167 if self.filedom: 168 rmtree(self.zipdir) 169 self.zipdir = None 170 self.filedom.unlink() 171 self.filedom = None 172 self.fileversion = None 173 self.items = None 174 self.itemindex = 0 175 176 def destroy(self): 177 """close all resources""" 178 IP.destroy(self) 179 180 def read_fileversion(self): 181 version = None 182 try: 183 self.zipdir = self._extractZip(self.filename) 184 self.filedom = minidom.parse(os.path.join(self.zipdir, 'tellico.xml')) 185 rootElement = self.filedom.getElementsByTagName('tellico')[0] 186 version = rootElement.getAttribute('syntaxVersion') 187 except Exception as e: 188 log.error(str(e)) 189 if self.filedom: 190 self.filedom.unlink() 191 self.filedom = None 192 log.info('Tellico: Found file version %s' % version) 193 return version 194 195 def _extractZip(self, filename): 196 zf = zipfile.ZipFile(filename, 'r') 197 tmpdir = mkdtemp(prefix = 'griffit2tellico') 198 zf.extractall(path = tmpdir) 199 return tmpdir 200 201 def _joinXmlNodes(self, elements, separator = ', '): 202 ret = '' 203 for element in elements: 204 if len(element.childNodes) > 0: 205 ret = ret + element.childNodes[0].data.strip() + separator 206 207 # chop off last separator 208 if len(elements) > 0: 209 ret = ret[:-len(separator)] 210 return ret 211 212 # join nodes for cast 213 def _joinCastNodes(self, elements, separator = ', '): 214 """ 215 we assume that this format: 216 <cast> 217 <column>Actor/Actress</column> 218 <column>Role</column> 219 </cast> 220 221 ideally the columns likely came from header, which may differ: 222 <field flags="3" title="Cast" category="Cast" format="2" type="8" name="cast" description="..."> 223 <prop name="column1" >Actor/Actress</prop> 224 <prop name="column2" >Role</prop> 225 <prop name="columns" >2</prop> 226 </field> 227 """ 228 229 ret = '' 230 for element in elements: 231 actor = None 232 columns = element.getElementsByTagName('column') 233 actor = columns[0].childNodes[0].data.strip() 234 if len(columns) > 1 and len(columns[1].childNodes) > 0: 235 actor = actor + _(' as ') + columns[1].childNodes[0].data.strip() 236 237 if actor: 238 ret = ret + actor + separator 239 240 # chop off last separator 241 if len(elements) > 0: 242 ret = ret[:-len(separator)] 243 244 return ret 245