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