]>
Commit | Line | Data |
---|---|---|
489423d6 ER |
1 | # -*- coding: utf-8 -*- |
2 | ||
3 | __revision__ = '$Id: $' | |
4 | ||
5 | # Copyright (c) 2015 | |
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 | ||
24 | from plugins.imp import ImportPlugin as IP | |
25 | import os | |
26 | import gutils | |
27 | import string | |
28 | from xml.dom import minidom, Node | |
29 | from lxml import etree | |
30 | import zipfile | |
31 | from shutil import rmtree | |
32 | from tempfile import mkdtemp | |
33 | ||
34 | import logging | |
35 | log = logging.getLogger("Griffith") | |
36 | ||
37 | """ | |
880cc34c ER |
38 | Supports importing videodb.xml exported as single file from Kodi/XBMC. |
39 | ||
40 | http://kodi.wiki/view/Import-export_library | |
41 | http://kodi.wiki/view/HOW-TO:Backup_the_video_library | |
42 | ||
489423d6 ER |
43 | See lib/plugins/imp/__init__.py for workflow how importer invokes methods from importer plugins |
44 | """ | |
45 | class ImportPlugin(IP): | |
46 | description = 'Kodi/XBMC MovieDB importer' | |
47 | author = 'Elan Ruusamäe' | |
48 | email = 'glen@pld-linux.org' | |
49 | version = '1.0' | |
50 | file_filters = 'videodb.xml' | |
51 | mime_types = None | |
52 | ||
53 | fileversion = None | |
54 | xml = None | |
55 | items = None | |
56 | itemindex = 0 | |
57 | ||
58 | # used by get_movie_details method | |
59 | # griffith field => kodi field | |
60 | # griffith field is actual SQL column in 'movies' table | |
61 | field_map = { | |
62 | 'title': 'title', | |
63 | 'o_title': 'originaltitle', | |
64 | 'year': 'year', | |
5411d3e0 | 65 | 'runtime': 'runtime', |
489423d6 ER |
66 | 'rating': 'rating', |
67 | 'plot': 'plot', | |
68 | 'director': 'director', | |
69 | 'studio': 'studio', | |
70 | 'country': 'country', | |
91cfb43d | 71 | 'classification': 'mpaa', |
489423d6 ER |
72 | # while the trailer field exists, it is not useful for griffith |
73 | # as it's something like: "plugin://plugin.video.youtube/?action=play_video&videoid=..." | |
74 | # however youtube urls can be probably fixed. | |
75 | 'trailer': 'trailer', | |
76 | } | |
77 | ||
91cfb43d ER |
78 | # rest of the stuff to insert into notes field |
79 | notes_map = { | |
80 | _('Id') : 'id', | |
81 | _('Play count'): 'playcount', | |
82 | _('Date added'): 'dateadded', | |
83 | _('Last played'): 'lastplayed', | |
84 | _('Collection'): 'set', | |
85 | _('Premiered'): 'premiered', | |
86 | _('Aired'): 'aired', | |
87 | } | |
88 | ||
489423d6 ER |
89 | def initialize(self): |
90 | if not IP.initialize(self): | |
91 | return False | |
92 | self.edit = False | |
93 | return True | |
94 | ||
95 | def set_source(self, name): | |
96 | IP.set_source(self, name) | |
97 | self.filename = name | |
98 | self.fileversion = self.read_fileversion() | |
99 | if self.fileversion == None: | |
100 | gutils.error(_('The format of the file is not supported.')) | |
101 | return False | |
102 | return True | |
103 | ||
104 | def clear(self): | |
105 | """clear plugin state before next source file""" | |
106 | IP.clear(self) | |
107 | if self.xml: | |
108 | self.xml = None | |
109 | self.fileversion = None | |
110 | self.items = None | |
111 | self.itemindex = 0 | |
112 | ||
113 | def destroy(self): | |
114 | """close all resources""" | |
115 | IP.destroy(self) | |
116 | ||
117 | def read_fileversion(self): | |
118 | version = None | |
119 | try: | |
120 | self.xml = etree.parse(self.filename) | |
121 | version = self.xml.xpath('/videodb/version')[0].text | |
122 | except Exception, e: | |
123 | log.error(str(e)) | |
124 | log.info('Found file version %s' % version) | |
125 | return version | |
126 | ||
127 | def count_movies(self): | |
128 | """Returns number of movies in file which is about to be imported""" | |
129 | if not self.xml: | |
130 | log.error('No XML object') | |
131 | return 0 | |
132 | ||
133 | count = 0 | |
134 | ||
135 | try: | |
136 | count = int(self.xml.xpath('count(/videodb/movie)')) | |
137 | except Exception, e: | |
138 | log.exception(e) | |
139 | ||
140 | log.info('%s movies for import' % count) | |
141 | return count | |
142 | ||
143 | def get_movie_details(self): | |
144 | """Returns dictionary with movie details""" | |
145 | if not self.xml: | |
146 | log.error('XML not opened') | |
147 | return None | |
148 | ||
149 | if not self.items: | |
150 | self.items = self.xml.xpath('/videodb/movie') | |
151 | self.itemindex = 0 | |
152 | ||
153 | # don't bother for empty db (shouldn't happen really) | |
154 | if not self.items or len(self.items) < 1: | |
155 | return None | |
156 | ||
157 | # no more items | |
158 | if self.itemindex >= len(self.items): | |
159 | return None | |
160 | ||
161 | item = self.items[self.itemindex] | |
162 | ||
163 | # fill details | |
164 | details = {} | |
165 | for k,v in self.field_map.items(): | |
166 | details[k] = item.findtext(v) | |
167 | ||
168 | # if playcount set, means it's seen | |
169 | details['seen'] = int(item.find('playcount').text) > 0 | |
170 | ||
171 | # genre can be multiple items, join by comma | |
172 | details['genre'] = ', '.join(n.text for n in item.findall('genre')) | |
173 | ||
5411d3e0 ER |
174 | # build text for 'cast' field |
175 | cast = [] | |
176 | for actor in item.findall('actor'): | |
177 | cast.append('%s as %s' % (actor.findtext('name'), actor.findtext('role'))) | |
178 | ||
179 | if cast: | |
180 | details['cast'] = "\n".join(cast) | |
181 | ||
91cfb43d ER |
182 | # put rest of information into notes field |
183 | notes = [] | |
184 | for k,v in self.notes_map.items(): | |
185 | v = item.findtext(v) | |
186 | if v: | |
187 | notes.append('%s: %s' % (k, v)) | |
188 | ||
189 | # credits can have multiple values, handle separately | |
190 | credits = ', '.join(n.text for n in item.findall('credits')) | |
191 | if credits: | |
192 | notes.append(_('Credits: %s') % credits) | |
193 | ||
194 | if notes: | |
195 | details['notes'] = "\n".join(notes) | |
196 | ||
78f08559 ER |
197 | # handle poster |
198 | # take first <thumb aspect="poster"> element | |
199 | posters = item.xpath('thumb[@aspect="poster"]') | |
200 | if posters: | |
201 | details['image'] = posters[0].get('preview') | |
202 | ||
489423d6 ER |
203 | # increment for next iteration |
204 | self.itemindex = self.itemindex + 1 | |
205 | ||
206 | return details |