]> git.pld-linux.org Git - packages/griffith.git/blob - Kodi.py
3aa56f19f3a7d20d47c65f132f77fdf91b2c1b8b
[packages/griffith.git] / Kodi.py
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 from lxml import etree
28 import tempfile
29 from urllib2 import urlopen, URLError, HTTPError
30
31 import logging
32 log = logging.getLogger("Griffith")
33
34 """
35 Supports importing videodb.xml exported as single file from Kodi/XBMC.
36
37 http://kodi.wiki/view/Import-export_library
38 http://kodi.wiki/view/HOW-TO:Backup_the_video_library
39
40 See lib/plugins/imp/__init__.py for workflow how importer invokes methods from importer plugins
41 """
42 class ImportPlugin(IP):
43     description  = 'Kodi/XBMC MovieDB importer'
44     author       = 'Elan Ruusamäe'
45     email        = 'glen@pld-linux.org'
46     version      = '1.0'
47     file_filters = 'videodb.xml'
48     mime_types   = None
49
50     fileversion  = None
51     xml          = None
52     items        = None
53     itemindex    = 0
54     poster_file  = None
55
56     # used by get_movie_details method
57     # griffith field => kodi field
58     # griffith field is actual SQL column in 'movies' table
59     field_map = {
60         'title': 'title',
61         'o_title': 'originaltitle',
62         'year': 'year',
63         'runtime': 'runtime',
64         'rating': 'rating',
65         'plot': 'plot',
66         'director': 'director',
67         'studio': 'studio',
68         'country': 'country',
69         'classification': 'mpaa',
70         # while the trailer field exists, it is not useful for griffith
71         # as it's something like: "plugin://plugin.video.youtube/?action=play_video&videoid=..."
72         # however youtube urls can be probably fixed.
73         'trailer': 'trailer',
74     }
75
76     # rest of the stuff to insert into notes field
77     notes_map = {
78         _('Id') : 'id',
79         _('Play count'): 'playcount',
80         _('Date added'): 'dateadded',
81         _('Last played'): 'lastplayed',
82         _('Collection'): 'set',
83         _('Premiered'): 'premiered',
84         _('Aired'): 'aired',
85     }
86
87     def initialize(self):
88         if not IP.initialize(self):
89             return False
90         self.edit = False
91         return True
92
93     def set_source(self, name):
94         IP.set_source(self, name)
95         self.filename = name
96         self.fileversion = self.read_fileversion()
97         if self.fileversion == None:
98             gutils.error(_('The format of the file is not supported.'))
99             return False
100         return True
101
102     def clear(self):
103         """clear plugin state before next source file"""
104         IP.clear(self)
105         if self.xml:
106             self.xml = None
107             self.fileversion = None
108             self.items = None
109             self.itemindex = 0
110             if self.poster_file:
111                 os.unlink(self.poster_file)
112                 self.poster_file = None
113
114     def destroy(self):
115         """close all resources"""
116         IP.destroy(self)
117
118     def read_fileversion(self):
119         version = None
120         try:
121             self.xml = etree.parse(self.filename)
122             version = self.xml.xpath('/videodb/version')[0].text
123         except Exception, e:
124             log.error(str(e))
125         log.info('Found file version %s' % version)
126         return version
127
128     def count_movies(self):
129         """Returns number of movies in file which is about to be imported"""
130         if not self.xml:
131             log.error('No XML object')
132             return 0
133
134         count = 0
135
136         try:
137             count = int(self.xml.xpath('count(/videodb/movie)'))
138         except Exception, e:
139             log.exception(e)
140
141         log.info('%s movies for import' % count)
142         return count
143
144     def get_movie_details(self):
145         """Returns dictionary with movie details"""
146         if not self.xml:
147             log.error('XML not opened')
148             return None
149
150         if not self.items:
151             self.items = self.xml.xpath('/videodb/movie')
152             self.itemindex = 0
153
154         # don't bother for empty db (shouldn't happen really)
155         if not self.items or len(self.items) < 1:
156             return None
157
158         # no more items
159         if self.itemindex >= len(self.items):
160             return None
161
162         item = self.items[self.itemindex]
163
164         # fill details
165         details = {}
166         for k,v in self.field_map.items():
167             details[k] = item.findtext(v)
168
169         # if playcount set, means it's seen
170         details['seen'] = int(item.find('playcount').text) > 0
171
172         # genre can be multiple items, join by comma
173         details['genre'] = ', '.join(n.text for n in item.findall('genre'))
174
175         # build text for 'cast' field
176         cast = []
177         for actor in item.findall('actor'):
178             cast.append('%s as %s' % (actor.findtext('name'), actor.findtext('role')))
179
180         if cast:
181             details['cast'] = "\n".join(cast)
182
183         # put rest of information into notes field
184         notes = []
185         for k,v in self.notes_map.items():
186             v = item.findtext(v)
187             if v:
188                 notes.append('%s: %s' % (k, v))
189
190         # credits can have multiple values, handle separately
191         credits = ', '.join(n.text for n in item.findall('credits'))
192         if credits:
193             notes.append(_('Credits: %s') % credits)
194
195         if notes:
196             details['notes'] = "\n".join(notes)
197
198         # handle poster
199         # take first <thumb aspect="poster"> element
200         posters = item.xpath('thumb[@aspect="poster"]')
201         if posters:
202             self.poster_file = self.grab_url(posters[0].get('preview'), prefix = 'poster_', suffix = '.jpg')
203             details['image'] = self.poster_file
204
205         # increment for next iteration
206         self.itemindex = self.itemindex + 1
207
208         return details
209
210     # grab url, return temp filename with remote file contents
211     # XXX could not figure out how to use griffith own downloader with ui interaction, etc
212     # XXX: grabbing urls while processing import xml blocks the ui
213     def grab_url(self, url, prefix = None, suffix=None):
214         log.debug("Downloading: %s" % url)
215         (fd, local_file) = tempfile.mkstemp(suffix=suffix, prefix=prefix)
216         try:
217             f = urlopen(url)
218             os.write(fd, f.read())
219             os.close(fd)
220
221         except HTTPError, e:
222             log.error("HTTP Error: %s: %s" % (e.code, url))
223             return None
224         except URLError, e:
225             log.error("URL Error: %s: %s" % (e.reason, url))
226             return None
227         else:
228             return local_file
229
230         # we get here with an exception, cleanup and return None
231         os.unlink(local_file)
232         return None
This page took 0.054569 seconds and 2 git commands to generate.